"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); 0 && (module.exports = { chainStreams: null, continueDynamicHTMLResume: null, continueDynamicPrerender: null, continueFizzStream: null, continueStaticPrerender: null, createBufferedTransformStream: null, createDocumentClosingStream: null, createRootLayoutValidatorStream: null, renderToInitialFizzStream: null, streamFromBuffer: null, streamFromString: null, streamToBuffer: null, streamToString: null }); function _export(target, all) { for(var name in all)Object.defineProperty(target, name, { enumerable: true, get: all[name] }); } _export(exports, { chainStreams: function() { return chainStreams; }, continueDynamicHTMLResume: function() { return continueDynamicHTMLResume; }, continueDynamicPrerender: function() { return continueDynamicPrerender; }, continueFizzStream: function() { return continueFizzStream; }, continueStaticPrerender: function() { return continueStaticPrerender; }, createBufferedTransformStream: function() { return createBufferedTransformStream; }, createDocumentClosingStream: function() { return createDocumentClosingStream; }, createRootLayoutValidatorStream: function() { return createRootLayoutValidatorStream; }, renderToInitialFizzStream: function() { return renderToInitialFizzStream; }, streamFromBuffer: function() { return streamFromBuffer; }, streamFromString: function() { return streamFromString; }, streamToBuffer: function() { return streamToBuffer; }, streamToString: function() { return streamToString; } }); const _tracer = require("../lib/trace/tracer"); const _constants = require("../lib/trace/constants"); const _detachedpromise = require("../../lib/detached-promise"); const _scheduler = require("../../lib/scheduler"); const _encodedTags = require("./encodedTags"); const _uint8arrayhelpers = require("./uint8array-helpers"); const _constants1 = require("../../shared/lib/errors/constants"); function voidCatch() { // this catcher is designed to be used with pipeTo where we expect the underlying // pipe implementation to forward errors but we don't want the pipeTo promise to reject // and be unhandled } // We can share the same encoder instance everywhere // Notably we cannot do the same for TextDecoder because it is stateful // when handling streaming data const encoder = new TextEncoder(); function chainStreams(...streams) { // We could encode this invariant in the arguments but current uses of this function pass // use spread so it would be missed by if (streams.length === 0) { throw Object.defineProperty(new Error('Invariant: chainStreams requires at least one stream'), "__NEXT_ERROR_CODE", { value: "E437", enumerable: false, configurable: true }); } // If we only have 1 stream we fast path it by returning just this stream if (streams.length === 1) { return streams[0]; } const { readable, writable } = new TransformStream(); // We always initiate pipeTo immediately. We know we have at least 2 streams // so we need to avoid closing the writable when this one finishes. let promise = streams[0].pipeTo(writable, { preventClose: true }); let i = 1; for(; i < streams.length - 1; i++){ const nextStream = streams[i]; promise = promise.then(()=>nextStream.pipeTo(writable, { preventClose: true })); } // We can omit the length check because we halted before the last stream and there // is at least two streams so the lastStream here will always be defined const lastStream = streams[i]; promise = promise.then(()=>lastStream.pipeTo(writable)); // Catch any errors from the streams and ignore them, they will be handled // by whatever is consuming the readable stream. promise.catch(voidCatch); return readable; } function streamFromString(str) { return new ReadableStream({ start (controller) { controller.enqueue(encoder.encode(str)); controller.close(); } }); } function streamFromBuffer(chunk) { return new ReadableStream({ start (controller) { controller.enqueue(chunk); controller.close(); } }); } async function streamToBuffer(stream) { const reader = stream.getReader(); const chunks = []; while(true){ const { done, value } = await reader.read(); if (done) { break; } chunks.push(value); } return Buffer.concat(chunks); } async function streamToString(stream, signal) { const decoder = new TextDecoder('utf-8', { fatal: true }); let string = ''; for await (const chunk of stream){ if (signal == null ? void 0 : signal.aborted) { return string; } string += decoder.decode(chunk, { stream: true }); } string += decoder.decode(); return string; } function createBufferedTransformStream() { let bufferedChunks = []; let bufferByteLength = 0; let pending; const flush = (controller)=>{ // If we already have a pending flush, then return early. if (pending) return; const detached = new _detachedpromise.DetachedPromise(); pending = detached; (0, _scheduler.scheduleImmediate)(()=>{ try { const chunk = new Uint8Array(bufferByteLength); let copiedBytes = 0; for(let i = 0; i < bufferedChunks.length; i++){ const bufferedChunk = bufferedChunks[i]; chunk.set(bufferedChunk, copiedBytes); copiedBytes += bufferedChunk.byteLength; } // We just wrote all the buffered chunks so we need to reset the bufferedChunks array // and our bufferByteLength to prepare for the next round of buffered chunks bufferedChunks.length = 0; bufferByteLength = 0; controller.enqueue(chunk); } catch { // If an error occurs while enqueuing it can't be due to this // transformers fault. It's likely due to the controller being // errored due to the stream being cancelled. } finally{ pending = undefined; detached.resolve(); } }); }; return new TransformStream({ transform (chunk, controller) { // Combine the previous buffer with the new chunk. bufferedChunks.push(chunk); bufferByteLength += chunk.byteLength; // Flush the buffer to the controller. flush(controller); }, flush () { if (!pending) return; return pending.promise; } }); } function renderToInitialFizzStream({ ReactDOMServer, element, streamOptions }) { return (0, _tracer.getTracer)().trace(_constants.AppRenderSpan.renderToReadableStream, async ()=>ReactDOMServer.renderToReadableStream(element, streamOptions)); } function createHeadInsertionTransformStream(insert) { let inserted = false; // We need to track if this transform saw any bytes because if it didn't // we won't want to insert any server HTML at all let hasBytes = false; return new TransformStream({ async transform (chunk, controller) { hasBytes = true; const insertion = await insert(); if (inserted) { if (insertion) { const encodedInsertion = encoder.encode(insertion); controller.enqueue(encodedInsertion); } controller.enqueue(chunk); } else { // TODO (@Ethan-Arrowood): Replace the generic `indexOfUint8Array` method with something finely tuned for the subset of things actually being checked for. const index = (0, _uint8arrayhelpers.indexOfUint8Array)(chunk, _encodedTags.ENCODED_TAGS.CLOSED.HEAD); // In fully static rendering or non PPR rendering cases: // `/head>` will always be found in the chunk in first chunk rendering. if (index !== -1) { if (insertion) { const encodedInsertion = encoder.encode(insertion); // Get the total count of the bytes in the chunk and the insertion // e.g. // chunk =
// insertion = // output = [ ] const insertedHeadContent = new Uint8Array(chunk.length + encodedInsertion.length); // Append the first part of the chunk, before the head tag insertedHeadContent.set(chunk.slice(0, index)); // Append the server inserted content insertedHeadContent.set(encodedInsertion, index); // Append the rest of the chunk insertedHeadContent.set(chunk.slice(index), index + encodedInsertion.length); controller.enqueue(insertedHeadContent); } else { controller.enqueue(chunk); } inserted = true; } else { // This will happens in PPR rendering during next start, when the page is partially rendered. // When the page resumes, the head tag will be found in the middle of the chunk. // Where we just need to append the insertion and chunk to the current stream. // e.g. // PPR-static: ... [ resume content ] // PPR-resume: [ insertion ] [ rest content ] if (insertion) { controller.enqueue(encoder.encode(insertion)); } controller.enqueue(chunk); inserted = true; } } }, async flush (controller) { // Check before closing if there's anything remaining to insert. if (hasBytes) { const insertion = await insert(); if (insertion) { controller.enqueue(encoder.encode(insertion)); } } } }); } // Suffix after main body content - scripts before