UNPKG

2.42 kBJavaScriptView Raw
1/**
2 * react-router v8.0.0
3 *
4 * Copyright (c) Remix Software Inc.
5 *
6 * This source code is licensed under the MIT license found in the
7 * LICENSE.md file in the root directory of this source tree.
8 *
9 * @license MIT
10 */
11//#region lib/rsc/html-stream/server.ts
12const encoder = new TextEncoder();
13const trailer = "</body></html>";
14function injectRSCPayload(rscStream) {
15 let decoder = new TextDecoder();
16 let resolveFlightDataPromise;
17 let flightDataPromise = new Promise((resolve) => resolveFlightDataPromise = resolve);
18 let startedRSC = false;
19 let buffered = [];
20 let timeout = null;
21 function flushBufferedChunks(controller) {
22 for (let chunk of buffered) {
23 let buf = decoder.decode(chunk, { stream: true });
24 if (buf.endsWith(trailer)) buf = buf.slice(0, -14);
25 controller.enqueue(encoder.encode(buf));
26 }
27 buffered.length = 0;
28 timeout = null;
29 }
30 return new TransformStream({
31 transform(chunk, controller) {
32 buffered.push(chunk);
33 if (timeout) return;
34 timeout = setTimeout(async () => {
35 flushBufferedChunks(controller);
36 if (!startedRSC) {
37 startedRSC = true;
38 writeRSCStream(rscStream, controller).catch((err) => controller.error(err)).then(resolveFlightDataPromise);
39 }
40 }, 0);
41 },
42 async flush(controller) {
43 await flightDataPromise;
44 if (timeout) {
45 clearTimeout(timeout);
46 flushBufferedChunks(controller);
47 }
48 controller.enqueue(encoder.encode("</body></html>"));
49 }
50 });
51}
52async function writeRSCStream(rscStream, controller) {
53 let decoder = new TextDecoder("utf-8", { fatal: true });
54 const reader = rscStream.getReader();
55 try {
56 let read;
57 while ((read = await reader.read()) && !read.done) {
58 const chunk = read.value;
59 try {
60 writeChunk(JSON.stringify(decoder.decode(chunk, { stream: true })), controller);
61 } catch (e) {
62 writeChunk(`Uint8Array.from(atob(${JSON.stringify(btoa(String.fromCodePoint(...chunk)))}), m => m.codePointAt(0))`, controller);
63 }
64 }
65 } finally {
66 reader.releaseLock();
67 }
68 let remaining = decoder.decode();
69 if (remaining.length) writeChunk(JSON.stringify(remaining), controller);
70}
71function writeChunk(chunk, controller) {
72 controller.enqueue(encoder.encode(`<script>${escapeScript(`(self.__FLIGHT_DATA||=[]).push(${chunk})`)}<\/script>`));
73}
74function escapeScript(script) {
75 return script.replace(/<!--/g, "<\\!--").replace(/<\/(script)/gi, "</\\$1");
76}
77//#endregion
78export { injectRSCPayload };