import { csrfHeader } from '../lib/csrf' import { ApiError, formatApiErrorDetail } from './errors' export type JsonRequestOptions = Omit & { body?: unknown } function apiUrl(path: string) { return path.startsWith('/api/') ? path : `/api/v1${path.startsWith('/') ? path : `/${path}`}` } async function apiFetch(path: string, options: RequestInit = {}) { const headers = new Headers(options.headers) const csrf = csrfHeader(options.method) if (csrf['x-csrf-token']) headers.set('x-csrf-token', csrf['x-csrf-token']) const response = await fetch(apiUrl(path), { credentials: 'include', ...options, headers }) if (!response.ok) { const contentType = response.headers.get('content-type') || '' const body = contentType.includes('json') ? await response.json().catch(() => ({})) : await response.text().catch(() => '') const detail = body && typeof body === 'object' && 'detail' in body ? body.detail : body const code = body && typeof body === 'object' && typeof body.code === 'string' ? body.code : detail && typeof detail === 'object' && !Array.isArray(detail) && typeof detail.code === 'string' ? detail.code : undefined throw new ApiError(formatApiErrorDetail(detail), response.status, code, detail) } return response } export async function requestJson(path: string, options: JsonRequestOptions = {}): Promise { const headers = new Headers(options.headers) const { body: ignoredBody, ...requestOptions } = options void ignoredBody const body = options.body === undefined ? undefined : JSON.stringify(options.body) if (body !== undefined) headers.set('content-type', 'application/json') const response = await apiFetch(path, { ...requestOptions, headers, body }) return response.status === 204 ? undefined as T : await response.json() as T } export async function requestBlob(path: string, options: RequestInit = {}): Promise { return (await apiFetch(path, options)).blob() } export async function uploadJson(path: string, file: File, options: Omit = {}): Promise { const form = new FormData() form.append('file', file) const response = await apiFetch(path, { ...options, method: options.method ?? 'POST', body: form }) return response.status === 204 ? undefined as T : await response.json() as T } export { ApiError } from './errors'