UNPKG

next

Version:

The React Framework

842 lines (841 loc) 35.2 kB
/** * Node.js stream operations for the rendering pipeline. * Loaded by stream-ops.ts when process.env.__NEXT_USE_NODE_STREAMS is true. * * AnyStream = AnyStreamType so the exported type surface matches stream-ops.web.ts, * allowing the switcher to assign either module without casts. * Rendering uses pipeable APIs; continue functions wrap the existing web * transforms via Readable.fromWeb() on their output. */ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); 0 && (module.exports = { chainStreams: null, continueDynamicHTMLResumeNode: null, continueDynamicHTMLResumeWeb: null, continueDynamicPrerender: null, continueFizzStream: null, continueStaticFallbackPrerender: null, continueStaticPrerender: null, createDocumentClosingStream: null, createNodeInlinedDataStream: null, createOnHeadersCallback: null, createPendingStream: null, createWebInlinedDataStream: null, getClientPrerender: null, getServerPrerender: null, pipeRuntimePrefetchTransform: null, processPrelude: null, renderToNodeFizzStream: null, renderToNodeFlightStream: null, renderToWebFizzStream: null, renderToWebFlightStream: null, resumeAndAbort: null, resumeToFizzStream: null, streamToBuffer: null, streamToString: null, teeStream: 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; }, continueDynamicHTMLResumeNode: function() { return continueDynamicHTMLResumeNode; }, continueDynamicHTMLResumeWeb: function() { return continueDynamicHTMLResumeWeb; }, continueDynamicPrerender: function() { return continueDynamicPrerender; }, continueFizzStream: function() { return continueFizzStream; }, continueStaticFallbackPrerender: function() { return continueStaticFallbackPrerender; }, continueStaticPrerender: function() { return continueStaticPrerender; }, createDocumentClosingStream: function() { return createDocumentClosingStream; }, createNodeInlinedDataStream: function() { return createNodeInlinedDataStream; }, createOnHeadersCallback: function() { return createOnHeadersCallback; }, createPendingStream: function() { return createPendingStream; }, createWebInlinedDataStream: function() { return createWebInlinedDataStream; }, getClientPrerender: function() { return getClientPrerender; }, getServerPrerender: function() { return getServerPrerender; }, pipeRuntimePrefetchTransform: function() { return pipeRuntimePrefetchTransform; }, processPrelude: function() { return processPrelude; }, renderToNodeFizzStream: function() { return renderToNodeFizzStream; }, renderToNodeFlightStream: function() { return renderToNodeFlightStream; }, renderToWebFizzStream: function() { return _streamopsweb.renderToWebFizzStream; }, renderToWebFlightStream: function() { return _streamopsweb.renderToWebFlightStream; }, resumeAndAbort: function() { return resumeAndAbort; }, resumeToFizzStream: function() { return resumeToFizzStream; }, streamToBuffer: function() { return streamToBuffer; }, streamToString: function() { return streamToString; }, teeStream: function() { return teeStream; } }); const _server = require("react-dom/server"); const _static = require("react-dom/static"); const _nodestream = require("node:stream"); const _nodebuffer = require("node:buffer"); const _nodewebstreamshelper = require("../stream-utils/node-web-streams-helper"); const _uint8arrayhelpers = require("../stream-utils/uint8array-helpers"); const _encodedtags = require("../stream-utils/encoded-tags"); const _nodebufferedtransformstream = require("../stream-utils/node-buffered-transform-stream"); const _constants = require("../../shared/lib/errors/constants"); const _htmlescape = require("../../shared/lib/htmlescape"); const _useflightresponse = require("./use-flight-response"); const _apprenderprerenderutils = require("./app-render-prerender-utils"); const _detachedpromise = require("../../lib/detached-promise"); const _tracer = require("../lib/trace/tracer"); const _constants1 = require("../lib/trace/constants"); const _scheduler = require("../../lib/scheduler"); const _streamopsweb = require("./stream-ops.web"); function nodeReadableToWebReadableStream(stream) { if (stream instanceof ReadableStream) { return stream; } // Readable.toWeb returns stream/web ReadableStream which is structurally // identical to the global ReadableStream<Uint8Array>. return _nodestream.Readable.toWeb(stream); } function webToReadable(stream) { if (stream instanceof _nodestream.Readable) { return stream; } return _nodestream.Readable.fromWeb(stream); } // --------------------------------------------------------------------------- // Flight data injection – Node.js Transform that passes HTML chunks through // while pulling from a separate data stream and interleaving its chunks. // --------------------------------------------------------------------------- function createFlightDataInjectionTransform(dataStream, delayDataUntilFirstHtmlChunk) { let htmlStreamFinished = false; let pull = null; let donePulling = false; function startOrContinuePulling(target) { if (!pull) { pull = startPulling(target); } return pull; } async function startPulling(target) { if (delayDataUntilFirstHtmlChunk) { // Buffer the inlined data stream until we've left the current Task so // it's inserted after flushing the shell. await (0, _scheduler.atLeastOneTask)(); } try { const iterator = dataStream[Symbol.asyncIterator](); while(true){ const { done, value } = await iterator.next(); if (done) { donePulling = true; return; } // Prioritize HTML over RSC data: the SSR render produces HTML from // the same RSC stream, so yield a task to let HTML flush first. if (!delayDataUntilFirstHtmlChunk && !htmlStreamFinished) { await (0, _scheduler.atLeastOneTask)(); } target.push(value); } } catch (err) { target.destroy(err); } } const nodeTransform = new _nodestream.Transform({ transform (chunk, _encoding, callback) { this.push(chunk); if (delayDataUntilFirstHtmlChunk) { startOrContinuePulling(this); } callback(); }, flush (callback) { htmlStreamFinished = true; if (donePulling) { callback(); return; } startOrContinuePulling(this).then(()=>callback(), (err)=>callback(err)); } }); if (!delayDataUntilFirstHtmlChunk) { startOrContinuePulling(nodeTransform); } return nodeTransform; } // --------------------------------------------------------------------------- // Head insertion – Node.js Transform that inserts server-generated HTML // (e.g. <script>, <style>) right before </head>, or prepends it if no // </head> tag is found (PPR resume case). // --------------------------------------------------------------------------- function createHeadInsertionTransform(insert) { let inserted = false; let hasBytes = false; return new _nodestream.Transform({ async transform (chunk, _encoding, callback) { hasBytes = true; try { const insertion = await insert(); if (inserted) { if (insertion) { this.push(Buffer.from(insertion)); } this.push(chunk); } else { const index = (0, _uint8arrayhelpers.indexOfUint8Array)(chunk, _encodedtags.ENCODED_TAGS.CLOSED.HEAD); if (index !== -1) { if (insertion) { const encodedInsertion = Buffer.from(insertion); const merged = Buffer.allocUnsafe(chunk.length + encodedInsertion.length); merged.set(chunk.slice(0, index)); merged.set(encodedInsertion, index); merged.set(chunk.slice(index), index + encodedInsertion.length); this.push(merged); } else { this.push(chunk); } inserted = true; } else { if (insertion) { this.push(Buffer.from(insertion)); } this.push(chunk); inserted = true; } } callback(); } catch (err) { callback(err); } }, async flush (callback) { try { if (hasBytes) { const insertion = await insert(); if (insertion) { this.push(Buffer.from(insertion)); } } callback(); } catch (err) { callback(err); } } }); } // --------------------------------------------------------------------------- // Metadata transform – Node.js Transform that finds the «nxt-icon» meta mark // and replaces it with a script tag (or removes it if inside <head>). // --------------------------------------------------------------------------- function createMetadataTransform(insert) { let chunkIndex = -1; let isMarkRemoved = false; return new _nodestream.Transform({ async transform (chunk, _encoding, callback) { let iconMarkIndex = -1; let closedHeadIndex = -1; chunkIndex++; if (isMarkRemoved) { this.push(chunk); callback(); return; } try { let iconMarkLength = 0; iconMarkIndex = (0, _uint8arrayhelpers.indexOfUint8Array)(chunk, _encodedtags.ENCODED_TAGS.META.ICON_MARK); if (iconMarkIndex === -1) { this.push(chunk); callback(); return; } iconMarkLength = _encodedtags.ENCODED_TAGS.META.ICON_MARK.length; if (chunk[iconMarkIndex + iconMarkLength] === 47) { iconMarkLength += 2; } else { iconMarkLength++; } if (chunkIndex === 0) { closedHeadIndex = (0, _uint8arrayhelpers.indexOfUint8Array)(chunk, _encodedtags.ENCODED_TAGS.CLOSED.HEAD); if (iconMarkIndex < closedHeadIndex) { const replaced = Buffer.allocUnsafe(chunk.length - iconMarkLength); replaced.set(chunk.subarray(0, iconMarkIndex)); replaced.set(chunk.subarray(iconMarkIndex + iconMarkLength), iconMarkIndex); chunk = replaced; } else { const insertion = await insert(); const encodedInsertion = Buffer.from(insertion); const insertionLength = encodedInsertion.length; const replaced = Buffer.allocUnsafe(chunk.length - iconMarkLength + insertionLength); replaced.set(chunk.subarray(0, iconMarkIndex)); replaced.set(encodedInsertion, iconMarkIndex); replaced.set(chunk.subarray(iconMarkIndex + iconMarkLength), iconMarkIndex + insertionLength); chunk = replaced; } isMarkRemoved = true; } else { const insertion = await insert(); const encodedInsertion = Buffer.from(insertion); const insertionLength = encodedInsertion.length; const replaced = Buffer.allocUnsafe(chunk.length - iconMarkLength + insertionLength); replaced.set(chunk.subarray(0, iconMarkIndex)); replaced.set(encodedInsertion, iconMarkIndex); replaced.set(chunk.subarray(iconMarkIndex + iconMarkLength), iconMarkIndex + insertionLength); chunk = replaced; isMarkRemoved = true; } this.push(chunk); callback(); } catch (err) { callback(err); } } }); } // --------------------------------------------------------------------------- // Deferred suffix – Node.js Transform that appends a suffix string after the // first HTML chunk, deferring via queueMicrotask so the chunk flushes first. // --------------------------------------------------------------------------- function createDeferredSuffixTransform(suffix) { let flushed = false; const encodedSuffix = Buffer.from(suffix); return new _nodestream.Transform({ transform (chunk, _encoding, callback) { this.push(chunk); if (!flushed) { flushed = true; queueMicrotask(()=>{ this.push(encodedSuffix); }); } callback(); }, flush (callback) { if (!flushed) { this.push(encodedSuffix); } callback(); } }); } // --------------------------------------------------------------------------- // Move suffix – Node.js Transform that strips </body></html> from its // original position and re-appends it at the very end of the stream, so any // content injected after the suffix still appears before the closing tags. // --------------------------------------------------------------------------- function createMoveSuffixTransform() { let foundSuffix = false; return new _nodestream.Transform({ transform (chunk, _encoding, callback) { if (foundSuffix) { this.push(chunk); callback(); return; } const index = (0, _uint8arrayhelpers.indexOfUint8Array)(chunk, _encodedtags.ENCODED_TAGS.CLOSED.BODY_AND_HTML); if (index > -1) { foundSuffix = true; if (chunk.length === _encodedtags.ENCODED_TAGS.CLOSED.BODY_AND_HTML.length) { callback(); return; } const before = chunk.slice(0, index); this.push(before); if (chunk.length > _encodedtags.ENCODED_TAGS.CLOSED.BODY_AND_HTML.length + index) { const after = chunk.slice(index + _encodedtags.ENCODED_TAGS.CLOSED.BODY_AND_HTML.length); this.push(after); } } else { this.push(chunk); } callback(); }, flush (callback) { this.push(_encodedtags.ENCODED_TAGS.CLOSED.BODY_AND_HTML); callback(); } }); } // --------------------------------------------------------------------------- // data-dpl-id insertion – Node.js Transform that inserts a `data-dpl-id` // attribute on the opening <html tag for deployment identification. // --------------------------------------------------------------------------- function createHtmlDataDplIdTransform(dplId) { let didTransform = false; return new _nodestream.Transform({ transform (chunk, _encoding, callback) { if (didTransform) { this.push(chunk); callback(); return; } const htmlTagIndex = (0, _uint8arrayhelpers.indexOfUint8Array)(chunk, _encodedtags.ENCODED_TAGS.OPENING.HTML); if (htmlTagIndex === -1) { this.push(chunk); callback(); return; } const insertionPoint = htmlTagIndex + _encodedtags.ENCODED_TAGS.OPENING.HTML.length; const encodedAttribute = Buffer.from(` data-dpl-id="${dplId}"`); const modified = Buffer.allocUnsafe(chunk.length + encodedAttribute.length); modified.set(chunk.subarray(0, insertionPoint)); modified.set(encodedAttribute, insertionPoint); modified.set(chunk.subarray(insertionPoint), insertionPoint + encodedAttribute.length); this.push(modified); didTransform = true; callback(); } }); } // --------------------------------------------------------------------------- // Root layout validator – Node.js Transform that checks whether <html> and // <body> tags are present in the streamed output. Dev-only; appends an // error template when tags are missing so the error overlay can display it. // --------------------------------------------------------------------------- function createRootLayoutValidatorTransform() { let foundHtml = false; let foundBody = false; return new _nodestream.Transform({ transform (chunk, _encoding, callback) { if (!foundHtml && (0, _uint8arrayhelpers.indexOfUint8Array)(chunk, _encodedtags.ENCODED_TAGS.OPENING.HTML) > -1) { foundHtml = true; } if (!foundBody && (0, _uint8arrayhelpers.indexOfUint8Array)(chunk, _encodedtags.ENCODED_TAGS.OPENING.BODY) > -1) { foundBody = true; } this.push(chunk); callback(); }, flush (callback) { const missingTags = []; if (!foundHtml) missingTags.push('html'); if (!foundBody) missingTags.push('body'); if (missingTags.length) { this.push(Buffer.from(`<html id="__next_error__"> <template data-next-error-message="Missing ${missingTags.map((c)=>`<${c}>`).join(missingTags.length > 1 ? ' and ' : '')} tags in the root layout.\nRead more at https://nextjs.org/docs/messages/missing-root-layout-tags" data-next-error-digest="${_constants.MISSING_ROOT_TAGS_ERROR}" data-next-error-stack="" ></template> `)); } callback(); } }); } function renderToNodeFlightStream(ComponentMod, payload, clientModules, opts) { if (!ComponentMod.renderToPipeableStream) { throw Object.defineProperty(new Error('renderToPipeableStream is not implemented'), "__NEXT_ERROR_CODE", { value: "E1153", enumerable: false, configurable: true }); } // `renderToPipeableStream` has no `signal` option (unlike the Web // `renderToReadableStream`), so pull `signal` out of the options and abort // the returned pipeable ourselves when it fires. We drop the listener when // the passthrough closes so a finished render's `pipeable` isn't retained by // the request signal, which can outlive it. const { signal, ...renderOptions } = opts ?? {}; const pt = new _nodestream.PassThrough(); const pipeable = ComponentMod.renderToPipeableStream(payload, clientModules, renderOptions); pipeable.pipe(pt); if (signal) { if (signal.aborted) { pipeable.abort(signal.reason); } else { const onAbort = ()=>pipeable.abort(signal.reason); signal.addEventListener('abort', onAbort, { once: true }); pt.on('close', ()=>signal.removeEventListener('abort', onAbort)); } } return pt; } async function renderToNodeFizzStream(element, streamOptions, options) { const pt = new _nodestream.PassThrough(); const shellReady = new _detachedpromise.DetachedPromise(); const allReady = new _detachedpromise.DetachedPromise(); const deferPipe = (options == null ? void 0 : options.waitForAllReady) === true; const pipeable = (0, _tracer.getTracer)().trace(_constants1.AppRenderSpan.renderToReadableStream, ()=>(0, _server.renderToPipeableStream)(element, { ...streamOptions, onHeaders: streamOptions == null ? void 0 : streamOptions.onHeaders, onShellReady () { var _streamOptions_onShellReady; streamOptions == null ? void 0 : (_streamOptions_onShellReady = streamOptions.onShellReady) == null ? void 0 : _streamOptions_onShellReady.call(streamOptions); shellReady.resolve(); }, onShellError (error) { var _streamOptions_onShellError; streamOptions == null ? void 0 : (_streamOptions_onShellError = streamOptions.onShellError) == null ? void 0 : _streamOptions_onShellError.call(streamOptions, error); shellReady.reject(error); }, onAllReady () { var _streamOptions_onAllReady; streamOptions == null ? void 0 : (_streamOptions_onAllReady = streamOptions.onAllReady) == null ? void 0 : _streamOptions_onAllReady.call(streamOptions); if (deferPipe) { pipeable.pipe(pt); } allReady.resolve(); }, onError: streamOptions == null ? void 0 : streamOptions.onError })); await (0, _tracer.getTracer)().trace(_constants1.AppRenderSpan.waitShellReady, ()=>shellReady.promise); if (!deferPipe) { await (0, _scheduler.waitAtLeastOneReactRenderTask)(); pipeable.pipe(pt); } return { stream: pt, allReady: allReady.promise, abort: (reason)=>pipeable.abort(reason) }; } async function resumeToFizzStream(element, postponedState, streamOptions, runInContext) { const run = runInContext ?? ((fn)=>fn()); const pt = new _nodestream.PassThrough(); const shellReady = new _detachedpromise.DetachedPromise(); const allReady = new _detachedpromise.DetachedPromise(); const pipeable = await run(()=>(0, _server.resumeToPipeableStream)(element, postponedState, { ...streamOptions, onShellReady () { var _streamOptions_onShellReady; streamOptions == null ? void 0 : (_streamOptions_onShellReady = streamOptions.onShellReady) == null ? void 0 : _streamOptions_onShellReady.call(streamOptions); shellReady.resolve(); }, onShellError (error) { var _streamOptions_onShellError; streamOptions == null ? void 0 : (_streamOptions_onShellError = streamOptions.onShellError) == null ? void 0 : _streamOptions_onShellError.call(streamOptions, error); shellReady.reject(error); }, onAllReady () { var _streamOptions_onAllReady; streamOptions == null ? void 0 : (_streamOptions_onAllReady = streamOptions.onAllReady) == null ? void 0 : _streamOptions_onAllReady.call(streamOptions); allReady.resolve(); } })); pipeable.pipe(pt); await shellReady.promise; return { stream: pt, allReady: allReady.promise, abort: (reason)=>pipeable.abort(reason) }; } async function resumeAndAbort(element, postponed, opts) { var _opts_signal; const pt = new _nodestream.PassThrough(); const pipeable = await (0, _server.resumeToPipeableStream)(element, postponed, opts); pipeable.pipe(pt); pipeable.abort(opts == null ? void 0 : (_opts_signal = opts.signal) == null ? void 0 : _opts_signal.reason); return pt; } async function continueFizzStream(renderStream, { suffix, inlinedDataStream, isStaticGeneration, allReady, deploymentId, getServerInsertedHTML, getServerInsertedMetadata, validateRootLayout }) { // Suffix itself might contain close tags at the end, so we need to split it. const suffixUnclosed = suffix ? suffix.split(_nodewebstreamshelper.CLOSE_TAG, 1)[0] : null; if (isStaticGeneration) { if (allReady) { await allReady; } } else { // Otherwise, we want to make sure Fizz is done with all microtasky work // before we start pulling the stream and cause a flush. await (0, _scheduler.waitAtLeastOneReactRenderTask)(); } // Pipe the render stream through Node.js Transforms: // 1. Buffer – coalesces chunks written in the same microtask into one Uint8Array // 2. Flight data injection – interleaves RSC data chunks with the HTML stream // 3. Head insertion – inserts server-generated HTML before </head> const buffered = (0, _nodebufferedtransformstream.createNodeBufferedTransformStream)(); webToReadable(renderStream).pipe(buffered); let source = buffered; if (deploymentId) { const dplId = createHtmlDataDplIdTransform(deploymentId); source.pipe(dplId); source = dplId; } // Metadata (icon mark replacement) const metadata = createMetadataTransform(getServerInsertedMetadata); source.pipe(metadata); source = metadata; // Insert suffix content if (suffixUnclosed != null && suffixUnclosed.length > 0) { const deferredSuffix = createDeferredSuffixTransform(suffixUnclosed); source.pipe(deferredSuffix); source = deferredSuffix; } // Flight data injection – interleaves RSC data chunks with the HTML stream if (inlinedDataStream) { const flightInjection = createFlightDataInjectionTransform(webToReadable(inlinedDataStream), true); source.pipe(flightInjection); source = flightInjection; } if (validateRootLayout) { const rootLayoutValidator = createRootLayoutValidatorTransform(); source.pipe(rootLayoutValidator); source = rootLayoutValidator; } // Close tags should always be deferred to the end const moveSuffix = createMoveSuffixTransform(); source.pipe(moveSuffix); source = moveSuffix; // Head insertion – inserts server-generated HTML before </head> const headInsertion = createHeadInsertionTransform(getServerInsertedHTML); source.pipe(headInsertion); source = headInsertion; return source; } async function continueStaticPrerender(prerenderStream, opts) { const webResult = await (0, _nodewebstreamshelper.continueStaticPrerender)(nodeReadableToWebReadableStream(prerenderStream), { ...opts, inlinedDataStream: nodeReadableToWebReadableStream(opts.inlinedDataStream) }); return webToReadable(webResult); } async function continueDynamicPrerender(prerenderStream, opts) { const webResult = await (0, _nodewebstreamshelper.continueDynamicPrerender)(nodeReadableToWebReadableStream(prerenderStream), opts); return webToReadable(webResult); } async function continueStaticFallbackPrerender(prerenderStream, opts) { const webResult = await (0, _nodewebstreamshelper.continueStaticFallbackPrerender)(nodeReadableToWebReadableStream(prerenderStream), { ...opts, inlinedDataStream: nodeReadableToWebReadableStream(opts.inlinedDataStream) }); return webToReadable(webResult); } async function continueDynamicHTMLResumeNode(renderStream, { delayDataUntilFirstHtmlChunk, inlinedDataStream, getServerInsertedHTML, getServerInsertedMetadata, deploymentId }) { await (0, _scheduler.waitAtLeastOneReactRenderTask)(); const buffered = (0, _nodebufferedtransformstream.createNodeBufferedTransformStream)(); webToReadable(renderStream).pipe(buffered); let source = buffered; if (deploymentId) { const dplId = createHtmlDataDplIdTransform(deploymentId); source.pipe(dplId); source = dplId; } const headInsertion = createHeadInsertionTransform(getServerInsertedHTML); source.pipe(headInsertion); source = headInsertion; const metadata = createMetadataTransform(getServerInsertedMetadata); source.pipe(metadata); source = metadata; const flightInjection = createFlightDataInjectionTransform(webToReadable(inlinedDataStream), delayDataUntilFirstHtmlChunk); source.pipe(flightInjection); source = flightInjection; const moveSuffix = createMoveSuffixTransform(); source.pipe(moveSuffix); source = moveSuffix; return source; } async function continueDynamicHTMLResumeWeb(renderStream, opts) { const webResult = await (0, _nodewebstreamshelper.continueDynamicHTMLResume)(nodeReadableToWebReadableStream(renderStream), { ...opts, inlinedDataStream: nodeReadableToWebReadableStream(opts.inlinedDataStream) }); return webToReadable(webResult); } function chainStreams(...streams) { if (streams.length === 0) { const pt = new _nodestream.PassThrough(); pt.end(); return pt; } if (streams.length === 1) { return streams[0]; } const out = new _nodestream.PassThrough(); let i = 0; function pipeNext() { if (i >= streams.length) { out.end(); return; } const current = webToReadable(streams[i++]); current.pipe(out, { end: false }); current.on('end', pipeNext); current.on('error', (err)=>out.destroy(err)); } pipeNext(); return out; } async function streamToBuffer(stream) { return (0, _nodewebstreamshelper.streamToBuffer)(nodeReadableToWebReadableStream(stream)); } async function streamToString(stream) { return (0, _nodewebstreamshelper.streamToString)(nodeReadableToWebReadableStream(stream)); } function createWebInlinedDataStream(source, nonce, formState) { const webSource = nodeReadableToWebReadableStream(source); const webResult = (0, _useflightresponse.createInlinedDataReadableStream)(webSource, nonce, formState); return webToReadable(webResult); } function createNodeInlinedDataStream(source, nonce, formState) { const startScriptTag = nonce ? `<script nonce="${(0, _htmlescape.htmlEscapeAttributeString)(nonce)}">` : '<script>'; const dataStream = webToReadable(source); const pt = new _nodestream.PassThrough(); // Write initial bootstrap instructions let scriptContents = `(self.__next_f=self.__next_f||[]).push(${(0, _htmlescape.htmlEscapeJsonString)(JSON.stringify([ INLINE_FLIGHT_PAYLOAD_BOOTSTRAP ]))})`; if (formState != null) { scriptContents += `;self.__next_f.push(${(0, _htmlescape.htmlEscapeJsonString)(JSON.stringify([ INLINE_FLIGHT_PAYLOAD_FORM_STATE, formState ]))})`; } pt.push(Buffer.from(`${startScriptTag}${scriptContents}</script>`)); // Pull from the flight data stream and wrap each chunk in a <script> tag pullFlightData(dataStream, pt, startScriptTag); return pt; } const INLINE_FLIGHT_PAYLOAD_BOOTSTRAP = 0; const INLINE_FLIGHT_PAYLOAD_DATA = 1; const INLINE_FLIGHT_PAYLOAD_FORM_STATE = 2; const INLINE_FLIGHT_PAYLOAD_BINARY = 3; async function pullFlightData(dataStream, output, startScriptTag) { function waitForReadableOrEnd() { if (dataStream.readableLength > 0 || dataStream.readableEnded) { return Promise.resolve(); } return new Promise((resolve, reject)=>{ function cleanup() { dataStream.removeListener('readable', onDone); dataStream.removeListener('end', onDone); dataStream.removeListener('error', onError); } function onDone() { cleanup(); resolve(); } function onError(err) { cleanup(); reject(err); } dataStream.on('readable', onDone); dataStream.on('end', onDone); dataStream.on('error', onError); }); } try { while(true){ const chunk = dataStream.read(); if (chunk !== null) { let htmlInlinedData; if ((0, _nodebuffer.isUtf8)(chunk)) { const decodedString = chunk.toString('utf-8'); htmlInlinedData = (0, _htmlescape.htmlEscapeJsonString)(JSON.stringify([ INLINE_FLIGHT_PAYLOAD_DATA, decodedString ])); } else { const base64 = Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength).toString('base64'); htmlInlinedData = (0, _htmlescape.htmlEscapeJsonString)(JSON.stringify([ INLINE_FLIGHT_PAYLOAD_BINARY, base64 ])); } output.push(Buffer.from(`${startScriptTag}self.__next_f.push(${htmlInlinedData})</script>`)); continue; } if (dataStream.readableEnded) { output.end(); return; } await waitForReadableOrEnd(); } } catch (err) { output.destroy(err); } } function createPendingStream() { return new _nodestream.PassThrough(); } function createDocumentClosingStream() { const webStream = (0, _nodewebstreamshelper.createDocumentClosingStream)(); return webToReadable(webStream); } function createOnHeadersCallback(appendHeader) { return (headers)=>{ headers.forEach((value, key)=>{ appendHeader(key, value); }); }; } function pipeRuntimePrefetchTransform(stream, sentinel, isPartial, staleTime) { const webStream = nodeReadableToWebReadableStream(stream); const transformed = webStream.pipeThrough((0, _nodewebstreamshelper.createRuntimePrefetchTransformStream)(sentinel, isPartial, staleTime)); return webToReadable(transformed); } async function processPrelude(unprocessedPrelude) { const [prelude, peek] = nodeReadableToWebReadableStream(unprocessedPrelude).tee(); const reader = peek.getReader(); const firstResult = await reader.read(); reader.cancel(); return { prelude: webToReadable(prelude), preludeIsEmpty: firstResult.done === true }; } function getServerPrerender(ComponentMod) { return ComponentMod.prerender; } const getClientPrerender = _static.prerender; function teeStream(stream) { const replayable = new _apprenderprerenderutils.ReplayableNodeStream(stream); return [ replayable.createReplayStream(), replayable.createReplayStream() ]; } //# sourceMappingURL=stream-ops.node.js.map