repositories
loading repo index
repositories
loading repo index
repository
loading code, commits, and activity
The Living OS cockpit
stars
latest
clone command
git clone gitlawb://did:key:z6Mku78K...XywC/living-os-cockp...git clone gitlawb://did:key:z6Mku78K.../living-os-cockp...59751530feat: surface worker supervisor health in live work5h ago| #1 | export class ApiError extends Error { |
| #2 | status: number; |
| #3 | url: string; |
| #4 | payload: unknown; |
| #5 | |
| #6 | constructor(message: string, status: number, url: string, payload?: unknown) { |
| #7 | super(message); |
| #8 | this.name = 'ApiError'; |
| #9 | this.status = status; |
| #10 | this.url = url; |
| #11 | this.payload = payload; |
| #12 | } |
| #13 | } |
| #14 | |
| #15 | export function isAuthError(error: unknown) { |
| #16 | return Boolean(error && typeof error === 'object' && 'status' in error && ((error as ApiError).status === 401 || (error as ApiError).status === 403)); |
| #17 | } |
| #18 | |
| #19 | export function sameOriginFetch(input: RequestInfo | URL, init: RequestInit = {}) { |
| #20 | return fetch(input, { |
| #21 | credentials: 'include', |
| #22 | cache: 'no-store', |
| #23 | ...init, |
| #24 | }); |
| #25 | } |
| #26 | |
| #27 | export async function readJsonOrThrow<T = any>(response: Response): Promise<T> { |
| #28 | const text = await response.text(); |
| #29 | const contentType = response.headers.get('content-type') ?? ''; |
| #30 | const url = response.url || 'API'; |
| #31 | |
| #32 | if (!text.trim()) { |
| #33 | if (response.ok) return null as T; |
| #34 | throw new ApiError(`Empty response from ${url} (${response.status})`, response.status, url); |
| #35 | } |
| #36 | |
| #37 | if (!contentType.includes('application/json')) { |
| #38 | throw new ApiError(`Non-JSON response from ${url} (${response.status}): ${text.slice(0, 180)}`, response.status, url, text); |
| #39 | } |
| #40 | |
| #41 | let parsed: unknown; |
| #42 | try { |
| #43 | parsed = JSON.parse(text); |
| #44 | } catch (error: any) { |
| #45 | throw new ApiError(`Invalid JSON from ${url} (${response.status}): ${error?.message ?? error}`, response.status, url, text); |
| #46 | } |
| #47 | |
| #48 | if (!response.ok) { |
| #49 | const payload = parsed && typeof parsed === 'object' ? parsed as Record<string, unknown> : {}; |
| #50 | const detail = String(payload.detail || payload.error || `API returned ${response.status}`); |
| #51 | throw new ApiError(detail, response.status, url, parsed); |
| #52 | } |
| #53 | |
| #54 | return parsed as T; |
| #55 | } |
| #56 | |
| #57 | export const swrFetcher = (url: string) => sameOriginFetch(url).then(readJsonOrThrow); |
| #58 |