import { chartViewResponseSchema, type ChartViewResponse } from "./chart-view-contract.ts"; import type { ChartViewLayer } from "./chart-view-engine.ts"; import { chartViewFailureResponse } from "./chart-view-failure.ts"; export async function fetchChartView(input: { layers?: readonly ChartViewLayer[]; signal?: AbortSignal; subjectId?: string; }): Promise<{ httpStatus: number; body: ChartViewResponse | null }> { const params = new URLSearchParams(); if (input.layers?.length) params.set("layers", input.layers.join(",")); if (input.subjectId) params.set("subject", input.subjectId); const serialized = params.toString(); const query = serialized ? `?${serialized}` : ""; const response = await fetch(`/api/chart-view${query}`, { credentials: "same-origin", headers: { Accept: "application/json" }, cache: "no-store", signal: input.signal, }); // Authentication is known from the headers; a missing body must not keep // a signed-out reader waiting for the JSON deadline. if (response.status === 401) return { httpStatus: 401, body: null }; let json: unknown = null; try { json = await response.json(); } catch (error) { // Aborting while reading the body is still a timeout, not malformed JSON. if (input.signal?.aborted) throw input.signal.reason; const errorName = error instanceof Error ? error.name : "Error"; console.warn("chart_view_client_bad_payload", { errorName, httpStatus: response.status }); return { httpStatus: response.status, body: chartViewFailureResponse("response_schema_failure") }; } const parsed = chartViewResponseSchema.safeParse(json); if (!parsed.success) { console.warn("chart_view_client_schema_failed", { httpStatus: response.status }); return { httpStatus: response.status, body: chartViewFailureResponse("response_schema_failure") }; } return { httpStatus: response.status, body: parsed.data }; }