Initial push of Plunk Next

This commit is contained in:
Dries Augustyns
2025-12-01 09:56:56 +01:00
parent 07cea20262
commit ff1876d580
566 changed files with 89036 additions and 28423 deletions
+92
View File
@@ -0,0 +1,92 @@
import type {infer as ZodInfer, ZodSchema} from 'zod';
import {API_URI} from './constants';
interface Json {
[x: string]: string | number | boolean | Date | Json | JsonArray;
}
type JsonArray = (string | number | boolean | Date | Json | JsonArray)[];
interface TypedSchema extends ZodSchema {
_type: unknown;
}
interface ApiResponse {
message?: string;
[key: string]: unknown;
}
export class network {
public static async fetch<T, Schema extends TypedSchema | void = void>(
method: 'GET' | 'PUT' | 'POST' | 'DELETE' | 'PATCH',
path: string,
body?: Schema extends TypedSchema ? ZodInfer<Schema> : never,
): Promise<T> {
const url = path.startsWith('http') ? path : API_URI + path;
// Get active project ID from localStorage
const activeProjectId = typeof window !== 'undefined' ? localStorage.getItem('activeProjectId') : null;
const headers: Record<string, string> = {};
if (body) {
headers['Content-Type'] = 'application/json';
}
if (activeProjectId) {
headers['X-Project-Id'] = activeProjectId;
}
const response = await fetch(url, {
method,
body: body && JSON.stringify(body),
headers,
credentials: 'include',
});
// Handle 204 No Content responses (no body to parse)
if (response.status === 204) {
return {} as T;
}
const res = (await response.json()) as ApiResponse;
if (response.status >= 400) {
throw new Error(res.message ?? 'Something went wrong!');
}
return res as T;
}
/**
* Upload file using FormData (multipart/form-data)
* Used for file uploads where Content-Type must be set by browser
*/
public static async upload<T>(method: 'POST' | 'PUT' | 'PATCH', path: string, formData: FormData): Promise<T> {
const url = path.startsWith('http') ? path : API_URI + path;
// Get active project ID from localStorage
const activeProjectId = typeof window !== 'undefined' ? localStorage.getItem('activeProjectId') : null;
const headers: Record<string, string> = {};
// DO NOT set Content-Type - browser will set it automatically with boundary
if (activeProjectId) {
headers['X-Project-Id'] = activeProjectId;
}
const response = await fetch(url, {
method,
body: formData,
headers,
credentials: 'include',
});
const res = (await response.json()) as ApiResponse;
if (response.status >= 400) {
throw new Error(res.message ?? 'Something went wrong!');
}
return res as T;
}
}