const API_BASE_URL = import.meta.env.VITE_API_BASE_URL ?? ""; export function apiUrl(path: string): string { return `${API_BASE_URL}${path}`; } export class ApiHttpError extends Error { readonly code: string; readonly details?: unknown; constructor(message: string, code = "REQUEST_ERROR", details?: unknown) { super(message); this.name = "ApiHttpError"; this.code = code; this.details = details; } } async function parseResponse(response: Response): Promise { const payload = await response.json().catch(() => ({})); if (!response.ok) { const legacyError = typeof payload?.error === "object" && payload?.error !== null ? payload.error : null; const code = typeof payload?.error === "string" ? payload.error : legacyError?.code ?? "REQUEST_ERROR"; const message = payload?.message ?? legacyError?.message ?? `Request failed (${response.status})`; const details = payload?.details ?? legacyError?.details; throw new ApiHttpError(message, code, details); } return payload.data as T; } export async function apiGet(path: string): Promise { const response = await fetch(apiUrl(path)); return parseResponse(response); } export async function apiPost(path: string, body?: object): Promise { const response = await fetch(apiUrl(path), { method: "POST", headers: { "Content-Type": "application/json" }, body: body ? JSON.stringify(body) : undefined, }); return parseResponse(response); } export async function apiPatch(path: string, body?: object): Promise { const response = await fetch(apiUrl(path), { method: "PATCH", headers: { "Content-Type": "application/json" }, body: body ? JSON.stringify(body) : undefined, }); return parseResponse(response); } export async function apiDelete(path: string): Promise { const response = await fetch(apiUrl(path), { method: "DELETE", }); return parseResponse(response); } export async function apiMultipart(path: string, form: FormData): Promise { const response = await fetch(apiUrl(path), { method: "POST", body: form, }); return parseResponse(response); }