// Minimal fetch wrapper for the LiveOps platform/admin HTTP API used by the seed // and by tests' admin-side assertions. Retries network errors and 5xx with backoff; // fails fast on 4xx. const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)); export class ApiError extends Error { constructor( public readonly status: number, public readonly path: string, public readonly body: string, ) { super(`HTTP ${status} on ${path}: ${body.slice(0, 400)}`); this.name = 'ApiError'; } } export interface AdminClient { setToken(token: string): void; readonly token: string | undefined; get(path: string, opts?: { auth?: boolean }): Promise; post(path: string, body?: unknown, opts?: { auth?: boolean }): Promise; put(path: string, body?: unknown, opts?: { auth?: boolean }): Promise; del(path: string, opts?: { auth?: boolean }): Promise; } export function createAdminClient(baseUrl: string): AdminClient { let token: string | undefined; async function request( method: string, path: string, body?: unknown, opts?: { auth?: boolean }, ): Promise { const url = `${baseUrl}${path}`; const headers: Record = { 'Content-Type': 'application/json' }; if (opts?.auth !== false && token) headers['Authorization'] = `Bearer ${token}`; let lastErr: unknown; for (let attempt = 0; attempt < 4; attempt++) { try { const res = await fetch(url, { method, headers, body: body === undefined ? undefined : JSON.stringify(body), }); const text = await res.text(); if (res.status >= 500) { lastErr = new ApiError(res.status, path, text); await sleep(300 * (attempt + 1)); continue; } if (!res.ok) throw new ApiError(res.status, path, text); return (text ? JSON.parse(text) : undefined) as T; } catch (err) { if (err instanceof ApiError) throw err; // 4xx — don't retry lastErr = err; // network error — retry await sleep(300 * (attempt + 1)); } } throw lastErr; } return { setToken(t: string) { token = t; }, get token() { return token; }, get: (path, opts) => request('GET', path, undefined, opts), post: (path, body, opts) => request('POST', path, body, opts), put: (path, body, opts) => request('PUT', path, body, opts), del: (path, opts) => request('DELETE', path, undefined, opts), }; }