File size: 2,491 Bytes
677b1c7 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | import "./lib/error-capture";
import { consumeLastCapturedError } from "./lib/error-capture";
import { renderErrorPage } from "./lib/error-page";
type ServerEntry = {
fetch: (request: Request, env: unknown, ctx: unknown) => Promise<Response> | Response;
};
let serverEntryPromise: Promise<ServerEntry> | undefined;
async function getServerEntry(): Promise<ServerEntry> {
if (!serverEntryPromise) {
serverEntryPromise = import("@tanstack/react-start/server-entry").then(
(m) => ((m as { default?: ServerEntry }).default ?? (m as unknown as ServerEntry)),
);
}
return serverEntryPromise;
}
function brandedErrorResponse(): Response {
return new Response(renderErrorPage(), {
status: 500,
headers: { "content-type": "text/html; charset=utf-8" },
});
}
function isCatastrophicSsrErrorBody(body: string, responseStatus: number): boolean {
let payload: unknown;
try {
payload = JSON.parse(body);
} catch {
return false;
}
if (!payload || Array.isArray(payload) || typeof payload !== "object") {
return false;
}
const fields = payload as Record<string, unknown>;
const expectedKeys = new Set(["message", "status", "unhandled"]);
if (!Object.keys(fields).every((key) => expectedKeys.has(key))) {
return false;
}
return (
fields.unhandled === true &&
fields.message === "HTTPError" &&
(fields.status === undefined || fields.status === responseStatus)
);
}
// h3 swallows in-handler throws into a normal 500 Response with body
// {"unhandled":true,"message":"HTTPError"} — try/catch alone never fires for those.
async function normalizeCatastrophicSsrResponse(response: Response): Promise<Response> {
if (response.status < 500) return response;
const contentType = response.headers.get("content-type") ?? "";
if (!contentType.includes("application/json")) return response;
const body = await response.clone().text();
if (!isCatastrophicSsrErrorBody(body, response.status)) {
return response;
}
console.error(consumeLastCapturedError() ?? new Error(`h3 swallowed SSR error: ${body}`));
return brandedErrorResponse();
}
export default {
async fetch(request: Request, env: unknown, ctx: unknown) {
try {
const handler = await getServerEntry();
const response = await handler.fetch(request, env, ctx);
return await normalizeCatastrophicSsrResponse(response);
} catch (error) {
console.error(error);
return brandedErrorResponse();
}
},
};
|