export class ApiClientError extends Error { constructor( message: string, public readonly status: number, public readonly payload?: unknown, ) { super(message) this.name = "ApiClientError" } } function trimBaseUrl(baseUrl: string): string { return baseUrl.replace(/\/$/, "") } export async function requestJson( baseUrl: string, path: string, init?: RequestInit, ): Promise { const hasBody = init?.body != null const res = await fetch(trimBaseUrl(baseUrl) + path, { ...init, headers: { ...(hasBody ? { "Content-Type": "application/json" } : {}), ...(init?.headers ?? {}), }, }) if (res.status === 204) return undefined as T const payload = await res.json().catch(() => undefined) if (!res.ok) { const msg = typeof payload === "object" && payload !== null && "error" in payload && typeof (payload as { error?: unknown }).error === "string" ? (payload as { error: string }).error : res.statusText throw new ApiClientError(msg, res.status, payload) } return payload as T }