Skip to content
File

Blob: src/worker/routes/uiResponse.ts

typescript50 lines
1import { renderUiView } from "@/client/server/render";
2import type { Viewer } from "@/client/server/viewer";
3 
4const HTML_HEADERS = {
5 "Content-Type": "text/html; charset=utf-8",
6 "X-Page-Renderer": "react-ssr",
7};
8 
9type RenderUiDocumentOptions = {
10 status?: number;
11 cacheControl?: string;
12 failureBody: string;
13 failureStatus?: number;
14 headers?: HeadersInit;
15 // Optional signed-in viewer threaded into the SSR shell. Route handlers
16 // that want the signed-in header must call `loadViewer(c)` and pass the
17 // result here. The renderer itself never reads cookies or D1.
18 viewer?: Viewer | null;
19};
20 
21/**
22 * Renders a registered React SSR document with the headers used by route pages.
23 * Callers still choose the cache policy because owner listings are cacheable
24 * while repository views intentionally remain no-store.
25 */
26export async function renderUiDocumentResponse(
27 env: Env,
28 view: string,
29 data: Record<string, unknown>,
30 options: RenderUiDocumentOptions
31): Promise<Response> {
32 const body = await renderUiView(env, view, data, { viewer: options.viewer ?? null });
33 if (!body) {
34 return new Response(options.failureBody, { status: options.failureStatus ?? 500 });
35 }
36 
37 const headers = new Headers(options.headers);
38 for (const [name, value] of Object.entries(HTML_HEADERS)) {
39 if (!headers.has(name)) headers.set(name, value);
40 }
41 if (!headers.has("Cache-Control")) {
42 headers.set("Cache-Control", options.cacheControl ?? "no-store, no-cache, must-revalidate");
43 }
44 
45 return new Response(body, {
46 status: options.status ?? 200,
47 headers,
48 });
49}