Repository Public
1export class ApiError extends Error { /** parsed JSON body of the error answer (risk refusals carry their code and reason there) */ body: unknown = undefined; constructor(public status: number, public code: string, message: string) { super(message); } }2let onUnauth: () => void = () => {};3export const setUnauthHandler = (f: () => void) => { onUnauth = f; };45export async function api<T = any>(method: string, path: string, body?: unknown, headers: Record<string, string> = {}): Promise<T> {6 const init: RequestInit = { method, credentials: 'same-origin', headers: { ...headers } };7 if (method !== 'GET') { (init.headers as Record<string, string>)['x-spw-csrf'] = '1'; (init.headers as Record<string, string>)['content-type'] = 'application/json'; init.body = JSON.stringify(body ?? {}); }8 let r: Response;9 try { r = await fetch('/api/v1' + path, init); } catch { throw new ApiError(0, 'offline', 'offline'); }10 const j = await r.json().catch(() => ({}));11 if (!r.ok) {12 if (r.status === 401 && j.error === 'unauthenticated') onUnauth();13 const err = new ApiError(r.status, j.error ?? 'error', j.message ?? 'error'); err.body = j;14 throw err;15 }16 return j as T;17}18export const get = <T = any>(p: string) => api<T>('GET', p);19export const post = <T = any>(p: string, b?: unknown, h?: Record<string, string>) => api<T>('POST', p, b, h);20