import { JsonValue } from '$std/json/common.ts' export type JsonCompatible = JsonValue | { toJSON(): JsonValue } | unknown export function respondApi< Kind extends ApiPayload['kind'], Payload extends JsonCompatible, >( kind: Kind, payload?: Payload, status?: number, statusText?: string, ): Response { if (kind === 'error') { return Response.json({ kind: 'error', error: String(payload ?? ''), } as ApiPayload, { status: status ?? 500, statusText, }) } return Response.json({ kind: 'success', data: payload ?? null, } as ApiPayload) } export async function requestApi< Payload extends JsonCompatible | undefined, ApiResponse extends JsonCompatible, >( route: string, method: 'GET' | 'POST' | 'DELETE' | 'PATCH', payload?: Payload | null, ): Promise { const csrf = getCookie('_CSRF') ?? '' const base = new URL('/api/', location.origin) const endpoint = new URL( route.startsWith('/') ? `.${route}` : route, base.href, ) const response = await fetch(endpoint, { method, headers: { 'Content-Type': 'application/json; charset=utf-8', 'X-CSRF-TOKEN': csrf, }, body: payload ? JSON.stringify(payload) : null, }) const apiPayload = await response.json() as ApiPayload if (apiPayload.kind === 'error') { throw new Error(`api request error while getting "${endpoint.href}"`, { cause: apiPayload.error, }) } return apiPayload.data } export type ApiPayload = { kind: 'success' data: ApiResponse } | { kind: 'error' error: string } function getCookie(name: string): string | undefined { const cookiesEntries = document.cookie.split(';').map((cookie) => cookie.trim().split('=') ) const cookies = Object.fromEntries(cookiesEntries) return cookies[name] }