import { getDeviceId } from "@/lib/deviceId"; export class ApiError extends Error { status: number; body: any; constructor(message: string, status: number, body?: any) { super(message); this.status = status; this.body = body; } } let refreshInFlight: Promise | null = null; async function tryRefresh(): Promise { if (!refreshInFlight) { refreshInFlight = fetch("/api/auth/refresh", { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ deviceId: getDeviceId() }), }) .then((r) => r.ok) .finally(() => { refreshInFlight = null; }); } return refreshInFlight; } export async function apiFetch( path: string, options: RequestInit = {}, _retried = false ): Promise { const res = await fetch(path, { ...options, credentials: "include", headers: { "Content-Type": "application/json", ...(options.headers ?? {}), }, }); if (res.status === 401 && !_retried && !path.startsWith("/api/auth/")) { const refreshed = await tryRefresh(); if (refreshed) { return apiFetch(path, options, true); } } const isJson = res.headers.get("content-type")?.includes("application/json"); const body = isJson ? await res.json().catch(() => ({})) : null; if (!res.ok) { throw new ApiError(body?.error ?? "Request failed", res.status, body); } return body as T; }