export class ApiError extends Error { constructor( message: string, readonly status: number, ) { super(message); } } let sessionReady: Promise | null = null; async function ensureSession(): Promise { if (!sessionReady) { sessionReady = fetch("/api/session", { cache: "no-store" }).then((response) => { if (!response.ok) throw new Error("Could not initialize the private MatchDay session"); }); } try { await sessionReady; } catch (error) { sessionReady = null; throw error; } } function readableDetail(detail: unknown, fallback: string): string { if (typeof detail === "string" && detail.trim()) return detail; if (Array.isArray(detail)) { const messages = detail .map((item) => { if (!item || typeof item !== "object") return String(item || ""); const record = item as { msg?: unknown; loc?: unknown }; const message = typeof record.msg === "string" ? record.msg : "Invalid value"; const location = Array.isArray(record.loc) ? record.loc.filter((part) => part !== "body").join(".") : ""; return location ? `${location}: ${message}` : message; }) .filter(Boolean); if (messages.length) return messages.join("; "); } if (detail && typeof detail === "object" && "msg" in detail) { const message = (detail as { msg?: unknown }).msg; if (typeof message === "string") return message; } return fallback; } export async function api(path: string, init?: RequestInit): Promise { await ensureSession(); const response = await fetch(`/api/backend${path}`, { ...init, headers: { "content-type": "application/json", ...(init?.headers || {}), }, cache: "no-store", }); if (!response.ok) { const payload = await response.json().catch(() => ({})); throw new ApiError( readableDetail(payload.detail, `Request failed (${response.status})`), response.status, ); } if (response.status === 204) return undefined as T; return response.json() as Promise; }