export class AuthError extends Error { override name = 'AuthError' constructor(message: string) { super(message) } } export class APIError extends Error { override name = 'APIError' status: number code: string constructor(message: string, status: number, code: string) { super(message) this.status = status this.code = code } } export async function apiFetch(path: string, init?: RequestInit): Promise { const resp = await fetch(path, { ...init, headers: { Accept: 'application/json', ...init?.headers, }, }) if (resp.status === 401) { throw new AuthError('not authenticated') } if (!resp.ok) { const ct = resp.headers.get('Content-Type') ?? '' if (ct.includes('application/problem+json')) { const body = await resp.json() as { detail?: string; title?: string; status?: number; code?: string } throw new APIError(body.detail ?? body.title ?? 'error', body.status ?? resp.status, body.code ?? '') } if (resp.status >= 500) { throw new APIError('server error', resp.status, '') } throw new APIError(`request failed: ${resp.status}`, resp.status, '') } return resp.json() as Promise }