import * as Sentry from "@sentry/node"; import { PassThrough } from "node:stream"; import type { AppLoadContext, EntryContext } from "react-router"; import { createReadableStreamFromReadable } from "@react-router/node"; import { ServerRouter } from "react-router"; import { isbot } from "isbot"; import type { RenderToPipeableStreamOptions } from "react-dom/server"; import { renderToPipeableStream } from "react-dom/server"; import { initI18nServer, detectLanguage } from "@trails-cool/i18n"; const sentryEnvironment = process.env.CI ? "ci" : (process.env.NODE_ENV ?? "development"); Sentry.init({ dsn: "https://a32ffcc575d34be072e91b20f247eeee@o4509530546634752.ingest.de.sentry.io/4509530555547728", release: process.env.SENTRY_RELEASE, environment: sentryEnvironment, tracesSampleRate: 1.0, enabled: process.env.NODE_ENV === "production" && !process.env.CI, beforeSend(event) { // Drop 404s — they're expected (scanners, typos), not bugs const serialized = event.extra?.__serialized__ as Record | undefined; if (serialized?.status === 404) return null; return event; }, }); export const streamTimeout = 5_000; export default function handleRequest( request: Request, responseStatusCode: number, responseHeaders: Headers, routerContext: EntryContext, _loadContext: AppLoadContext, ) { initI18nServer(detectLanguage(request)); if (request.method.toUpperCase() === "HEAD") { return new Response(null, { status: responseStatusCode, headers: responseHeaders, }); } return new Promise((resolve, reject) => { let shellRendered = false; const userAgent = request.headers.get("user-agent"); const readyOption: keyof RenderToPipeableStreamOptions = (userAgent && isbot(userAgent)) || routerContext.isSpaMode ? "onAllReady" : "onShellReady"; let timeoutId: ReturnType | undefined = setTimeout( () => abort(), streamTimeout + 1000, ); const { pipe, abort } = renderToPipeableStream( , { [readyOption]() { shellRendered = true; const body = new PassThrough({ final(callback) { clearTimeout(timeoutId); timeoutId = undefined; callback(); }, }); const stream = createReadableStreamFromReadable(body); responseHeaders.set("Content-Type", "text/html"); pipe(body); resolve( new Response(stream, { headers: responseHeaders, status: responseStatusCode, }), ); }, onShellError(error: unknown) { reject(error); }, onError(error: unknown) { responseStatusCode = 500; if (shellRendered) { console.error(error); Sentry.captureException(error); } }, }, ); }); }