import { getConfig, getAuthHeader } from "./config.js"; export async function apiRequest( path: string, options: RequestInit = {}, ): Promise { const config = getConfig(); const headers: Record = { "Content-Type": "application/json", ...getAuthHeader(), ...(options.headers as Record), }; const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 30_000); const res = await fetch(`${config.apiUrl}${path}`, { ...options, headers, signal: controller.signal, }).finally(() => clearTimeout(timeout)); if (!res.ok) { const body = (await res.json().catch(() => null)) as { error?: { message?: string }; } | null; const msg = body?.error?.message || `API error: ${res.status}`; throw new Error(msg); } return res.json() as Promise; } /** * Like apiRequest but returns the raw response body as text — for endpoints * that serve non-JSON (e.g. the audit CSV export at /audit/export.csv). */ export async function apiRequestText( path: string, options: RequestInit = {}, ): Promise { const config = getConfig(); const headers: Record = { ...getAuthHeader(), ...(options.headers as Record), }; const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 30_000); const res = await fetch(`${config.apiUrl}${path}`, { ...options, headers, signal: controller.signal, }).finally(() => clearTimeout(timeout)); if (!res.ok) { throw new Error(`API error: ${res.status}`); } return res.text(); }