UNPKG

rwsdk

Version:

Build fast, server-driven webapps on Cloudflare with SSR, RSC, and realtime

85 lines (84 loc) 4.63 kB
/** * A utility to orchestrate and interleave two ReadableStreams (a document shell and an app shell) * based on a set of markers within their content. This is designed to solve a specific * race condition in streaming Server-Side Rendering (SSR) with Suspense. * * The logic is as follows: * 1. Stream the document until a start marker is found. * 2. Switch to the app stream and stream it until an end marker is found. This is the non-suspended shell. * 3. Switch back to the document stream and stream it until the closing body tag. This sends the client script. * 4. Switch back to the app stream and stream the remainder (the suspended content). * 5. Switch back to the document stream and stream the remainder (closing body and html tags). * * @param outerHtml The stream for the document shell (`<Document>`). * @param innerHtml The stream for the application's content. * @param startMarker The marker in the document to start injecting the app. * @param endMarker The marker in the app stream that signals the end of the initial, non-suspended render. */ /** * A utility that orchestrates and interleaves three ReadableStreams to produce a * single, valid HTML response stream. It uses two special markers: * * - `startMarker`: Placed in the `outerHtml` stream (the document shell) to * designate where the application's content should be injected. * - `endMarker`: Injected into the `innerHtml` stream's RSC payload to signal * the end of the initial, non-suspended render. This marker is needed for * non-blocking hydration, as it allows the stitching process to send the * client `<script>` tags before all suspended content has resolved. * * It manages three main stream readers: * * - `hoistedTagsReader`: Reads from the `hoistedTagsStream`, which contains only * the hoisted meta tags (e.g., `<title>`, `<meta>`). * - `outerReader`: Reads from the `outerHtml` stream, which is the server-rendered * document shell (containing `<html>`, `<head>`, etc.). * - `innerReader`: Reads from the `appBodyStream`, which contains the main * application content, stripped of its hoisted tags. * * The function proceeds through a multi-phase state machine, managed by the * `pump` function, to correctly interleave these streams. * * The state machine moves through the following phases: * * 1. `read-hoisted`: * - **Goal:** Buffer all hoisted tags from the `hoistedTagsStream`. * - **Action:** Reads from `hoistedTagsReader` and appends all content into * the `hoistedTagsBuffer`. Does not enqueue anything yet. * - **Transition:** Moves to `outer-head` when the stream is exhausted. * * 2. `outer-head`: * - **Goal:** Stream the document up to the closing `</head>` tag, inject the * hoisted tags, and then continue until the app `startMarker`. * - **Action:** Reads from `outerReader`. When it finds `</head>`, it enqueues * the content before it, then enqueues the `hoistedTagsBuffer`, and finally * enqueues the `</head>` tag itself. It then continues reading from * `outerReader` until it finds the `startMarker`. * - **Transition:** Moves to `inner-shell` after finding and discarding the * `startMarker`. * * 3. `inner-shell`: * - **Goal:** Stream the initial, non-suspended part of the application. * - **Action:** Switches to `innerReader`. It enqueues chunks until it finds * the `endMarker`. Any content after the marker is stored in * `innerSuspendedRemains`. * - **Transition:** Moves to `outer-tail` after finding the `endMarker`. * * 4. `outer-tail`: * - **Goal:** Stream the rest of the document's `<body>`, including client * `<script>` tags. * - **Action:** Switches back to `outerReader` and enqueues chunks until it * finds the `</body>` tag. * - **Transition:** Moves to `inner-suspended` after finding `</body>`. * * 5. `inner-suspended`: * - **Goal:** Stream any suspended content from the React app. * - **Action:** First enqueues any content from `innerSuspendedRemains`, then * continues reading from `innerReader` until the stream is exhausted. * - **Transition:** Moves to `outer-end` when the stream is exhausted. * * 6. `outer-end`: * - **Goal:** Finish the document. * - **Action:** Switches back to `outerReader` for the last time to send the * closing `</body>` and `</html>` tags. */ export declare function stitchDocumentAndAppStreams(outerHtml: ReadableStream<Uint8Array>, innerHtml: ReadableStream<Uint8Array>, startMarker: string, endMarker: string): ReadableStream<Uint8Array>;