const BASE = "/api"; export class ApiError extends Error { status: number; body: unknown; constructor(message: string, status: number, body: unknown) { super(message); this.name = "ApiError"; this.status = status; this.body = body; } } async function request(path: string, init?: RequestInit): Promise { const headers = new Headers(init?.headers ?? undefined); const body = init?.body; if (!(body instanceof FormData) && !headers.has("Content-Type")) { headers.set("Content-Type", "application/json"); } const res = await fetch(`${BASE}${path}`, { headers, credentials: "include", ...init, }); if (!res.ok) { const errorBody = await res.json().catch(() => null); throw new ApiError( (errorBody as { error?: string } | null)?.error ?? `Request failed: ${res.status}`, res.status, errorBody, ); } return res.json(); } export const api = { get: (path: string) => request(path), post: (path: string, body: unknown) => request(path, { method: "POST", body: JSON.stringify(body) }), postForm: (path: string, body: FormData) => request(path, { method: "POST", body }), put: (path: string, body: unknown) => request(path, { method: "PUT", body: JSON.stringify(body) }), patch: (path: string, body: unknown) => request(path, { method: "PATCH", body: JSON.stringify(body) }), delete: (path: string) => request(path, { method: "DELETE" }), };