import type { EntryContext } from '@remix-run/node'; import { RemixServer } from '@remix-run/react'; import { renderToReadableStream } from 'react-dom/server'; const ABORT_DELAY = 5000; export async function streamToText(stream: ReadableStream): Promise { let result = ''; const reader = stream.pipeThrough(new TextDecoderStream()).getReader(); while (true) { // eslint-disable-line no-constant-condition const { done, value } = await reader.read(); if (done) { break; } result += value; } return result; } type CachedResponse = { html: string; date: Date; } const cache = new Map(); export default async function handleRequest( request: Request, responseStatusCode: number, responseHeaders: Headers, remixContext: EntryContext ) { // check if we have a cached response in memory const cachedResponse = cache.get(request.url); if (cachedResponse) { // console.log('Serving from cache', request.url); // if we have a cached response, check if it's less than 5 seconds old const now = new Date(); const diff = now.getTime() - cachedResponse.date.getTime(); if (true || diff < 5000) { // if it's less than 5 seconds old, return the cached response responseHeaders.set('Content-Type', 'text/html'); return new Response(cachedResponse.html, { headers: responseHeaders, status: responseStatusCode, }); } } let didError = false; const chunks: Uint8Array[] = []; const body = await renderToReadableStream( , { onError: (error: unknown) => { didError = true; console.error(error); } } ); // tee the stream so we can cache it and send it to the client const [toReponse, toCache] = body.tee(); streamToText(toCache).then(html => { console.log('Caching', request.url); cache.set(request.url, { html: html.replace('Rendered Fresh', `Rendered from cache ${new Date().toISOString()}`), date: new Date(), }); }); const headers = new Headers(responseHeaders); headers.set("Content-Type", "text/html"); const response = new Response(toReponse, { headers, status: didError ? 500 : responseStatusCode, }); return response; }