react-server-dom-webpack
Version:
React Server Components bindings for DOM using Webpack. This is intended to be integrated into meta-frameworks. It is not intended to be imported directly.
1,765 lines (1,438 loc) • 49.2 kB
JavaScript
/**
* @license React
* react-server-dom-webpack-client.node.unbundled.production.min.js
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
'use strict';
var util = require('util');
var ReactDOM = require('react-dom');
// -----------------------------------------------------------------------------
const enableBinaryFlight = false;
function createStringDecoder() {
return new util.TextDecoder();
}
const decoderOptions = {
stream: true
};
function readPartialStringChunk(decoder, buffer) {
return decoder.decode(buffer, decoderOptions);
}
function readFinalStringChunk(decoder, buffer) {
return decoder.decode(buffer);
}
// This is the parsed shape of the wire format which is why it is
// condensed to only the essentialy information
const ID = 0;
const CHUNKS = 1;
const NAME = 2; // export const ASYNC = 3;
// This logic is correct because currently only include the 4th tuple member
// when the module is async. If that changes we will need to actually assert
// the value is true. We don't index into the 4th slot because flow does not
// like the potential out of bounds access
function isAsyncImport(metadata) {
return metadata.length === 4;
}
// The reason this function needs to defined here in this file instead of just
// being exported directly from the WebpackDestination... file is because the
// ClientReferenceMetadata is opaque and we can't unwrap it there.
// This should get inlined and we could also just implement an unwrapping function
// though that risks it getting used in places it shouldn't be. This is unfortunate
// but currently it seems to be the best option we have.
function prepareDestinationForModule(moduleLoading, nonce, metadata) {
prepareDestinationWithChunks(moduleLoading, metadata[CHUNKS], nonce);
}
function resolveClientReference(bundlerConfig, metadata) {
const moduleExports = bundlerConfig[metadata[ID]];
let resolvedModuleData = moduleExports[metadata[NAME]];
let name;
if (resolvedModuleData) {
// The potentially aliased name.
name = resolvedModuleData.name;
} else {
// If we don't have this specific name, we might have the full module.
resolvedModuleData = moduleExports['*'];
if (!resolvedModuleData) {
throw new Error('Could not find the module "' + metadata[ID] + '" in the React SSR Manifest. ' + 'This is probably a bug in the React Server Components bundler.');
}
name = metadata[NAME];
}
return {
specifier: resolvedModuleData.specifier,
name: name,
async: isAsyncImport(metadata)
};
}
const asyncModuleCache = new Map();
function preloadModule(metadata) {
const existingPromise = asyncModuleCache.get(metadata.specifier);
if (existingPromise) {
if (existingPromise.status === 'fulfilled') {
return null;
}
return existingPromise;
} else {
// $FlowFixMe[unsupported-syntax]
let modulePromise = import(metadata.specifier);
if (metadata.async) {
// If the module is async, it must have been a CJS module.
// CJS modules are accessed through the default export in
// Node.js so we have to get the default export to get the
// full module exports.
modulePromise = modulePromise.then(function (value) {
return value.default;
});
}
modulePromise.then(value => {
const fulfilledThenable = modulePromise;
fulfilledThenable.status = 'fulfilled';
fulfilledThenable.value = value;
}, reason => {
const rejectedThenable = modulePromise;
rejectedThenable.status = 'rejected';
rejectedThenable.reason = reason;
});
asyncModuleCache.set(metadata.specifier, modulePromise);
return modulePromise;
}
}
function requireModule(metadata) {
let moduleExports; // We assume that preloadModule has been called before, which
// should have added something to the module cache.
const promise = asyncModuleCache.get(metadata.specifier);
if (promise.status === 'fulfilled') {
moduleExports = promise.value;
} else {
throw promise.reason;
}
if (metadata.name === '*') {
// This is a placeholder value that represents that the caller imported this
// as a CommonJS module as is.
return moduleExports;
}
if (metadata.name === '') {
// This is a placeholder value that represents that the caller accessed the
// default property of this if it was an ESM interop module.
return moduleExports.default;
}
return moduleExports[metadata.name];
}
function prepareDestinationWithChunks(moduleLoading, // Chunks are double-indexed [..., idx, filenamex, idy, filenamey, ...]
chunks, nonce) {
if (moduleLoading !== null) {
for (let i = 1; i < chunks.length; i += 2) {
preinitScriptForSSR(moduleLoading.prefix + chunks[i], nonce, moduleLoading.crossOrigin);
}
}
}
const ReactDOMSharedInternals = ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
function getCrossOriginString(input) {
if (typeof input === 'string') {
return input === 'use-credentials' ? input : '';
}
return undefined;
}
// This client file is in the shared folder because it applies to both SSR and browser contexts.
const ReactDOMCurrentDispatcher = ReactDOMSharedInternals.Dispatcher;
function dispatchHint(code, model) {
const dispatcher = ReactDOMCurrentDispatcher.current;
if (dispatcher) {
switch (code) {
case 'D':
{
const refined = refineModel(code, model);
const href = refined;
dispatcher.prefetchDNS(href);
return;
}
case 'C':
{
const refined = refineModel(code, model);
if (typeof refined === 'string') {
const href = refined;
dispatcher.preconnect(href);
} else {
const href = refined[0];
const crossOrigin = refined[1];
dispatcher.preconnect(href, crossOrigin);
}
return;
}
case 'L':
{
const refined = refineModel(code, model);
const href = refined[0];
const as = refined[1];
if (refined.length === 3) {
const options = refined[2];
dispatcher.preload(href, as, options);
} else {
dispatcher.preload(href, as);
}
return;
}
case 'm':
{
const refined = refineModel(code, model);
if (typeof refined === 'string') {
const href = refined;
dispatcher.preloadModule(href);
} else {
const href = refined[0];
const options = refined[1];
dispatcher.preloadModule(href, options);
}
return;
}
case 'S':
{
const refined = refineModel(code, model);
if (typeof refined === 'string') {
const href = refined;
dispatcher.preinitStyle(href);
} else {
const href = refined[0];
const precedence = refined[1] === 0 ? undefined : refined[1];
const options = refined.length === 3 ? refined[2] : undefined;
dispatcher.preinitStyle(href, precedence, options);
}
return;
}
case 'X':
{
const refined = refineModel(code, model);
if (typeof refined === 'string') {
const href = refined;
dispatcher.preinitScript(href);
} else {
const href = refined[0];
const options = refined[1];
dispatcher.preinitScript(href, options);
}
return;
}
case 'M':
{
const refined = refineModel(code, model);
if (typeof refined === 'string') {
const href = refined;
dispatcher.preinitModuleScript(href);
} else {
const href = refined[0];
const options = refined[1];
dispatcher.preinitModuleScript(href, options);
}
return;
}
}
}
} // Flow is having trouble refining the HintModels so we help it a bit.
// This should be compiled out in the production build.
function refineModel(code, model) {
return model;
}
function preinitScriptForSSR(href, nonce, crossOrigin) {
const dispatcher = ReactDOMCurrentDispatcher.current;
if (dispatcher) {
dispatcher.preinitScript(href, {
crossOrigin: getCrossOriginString(crossOrigin),
nonce
});
}
}
// ATTENTION
// When adding new symbols to this file,
// Please consider also adding to 'react-devtools-shared/src/backend/ReactSymbols'
// The Symbol used to tag the ReactElement-like types.
const REACT_ELEMENT_TYPE = Symbol.for('react.element');
const REACT_LAZY_TYPE = Symbol.for('react.lazy');
const MAYBE_ITERATOR_SYMBOL = Symbol.iterator;
const FAUX_ITERATOR_SYMBOL = '@@iterator';
function getIteratorFn(maybeIterable) {
if (maybeIterable === null || typeof maybeIterable !== 'object') {
return null;
}
const maybeIterator = MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL];
if (typeof maybeIterator === 'function') {
return maybeIterator;
}
return null;
}
const isArrayImpl = Array.isArray; // eslint-disable-next-line no-redeclare
function isArray(a) {
return isArrayImpl(a);
}
const getPrototypeOf = Object.getPrototypeOf;
const ObjectPrototype = Object.prototype;
const knownServerReferences = new WeakMap(); // Serializable values
// Thenable<ReactServerValue>
// function serializeByValueID(id: number): string {
// return '$' + id.toString(16);
// }
function serializePromiseID(id) {
return '$@' + id.toString(16);
}
function serializeServerReferenceID(id) {
return '$F' + id.toString(16);
}
function serializeSymbolReference(name) {
return '$S' + name;
}
function serializeFormDataReference(id) {
// Why K? F is "Function". D is "Date". What else?
return '$K' + id.toString(16);
}
function serializeNumber(number) {
if (Number.isFinite(number)) {
if (number === 0 && 1 / number === -Infinity) {
return '$-0';
} else {
return number;
}
} else {
if (number === Infinity) {
return '$Infinity';
} else if (number === -Infinity) {
return '$-Infinity';
} else {
return '$NaN';
}
}
}
function serializeUndefined() {
return '$undefined';
}
function serializeDateFromDateJSON(dateJSON) {
// JSON.stringify automatically calls Date.prototype.toJSON which calls toISOString.
// We need only tack on a $D prefix.
return '$D' + dateJSON;
}
function serializeBigInt(n) {
return '$n' + n.toString(10);
}
function serializeMapID(id) {
return '$Q' + id.toString(16);
}
function serializeSetID(id) {
return '$W' + id.toString(16);
}
function escapeStringValue(value) {
if (value[0] === '$') {
// We need to escape $ prefixed strings since we use those to encode
// references to IDs and as special symbol values.
return '$' + value;
} else {
return value;
}
}
function processReply(root, formFieldPrefix, resolve, reject) {
let nextPartId = 1;
let pendingParts = 0;
let formData = null;
function resolveToJSON(key, value) {
const parent = this; // Make sure that `parent[key]` wasn't JSONified before `value` was passed to us
if (value === null) {
return null;
}
if (typeof value === 'object') {
// $FlowFixMe[method-unbinding]
if (typeof value.then === 'function') {
// We assume that any object with a .then property is a "Thenable" type,
// or a Promise type. Either of which can be represented by a Promise.
if (formData === null) {
// Upgrade to use FormData to allow us to stream this value.
formData = new FormData();
}
pendingParts++;
const promiseId = nextPartId++;
const thenable = value;
thenable.then(partValue => {
const partJSON = JSON.stringify(partValue, resolveToJSON); // $FlowFixMe[incompatible-type] We know it's not null because we assigned it above.
const data = formData; // eslint-disable-next-line react-internal/safe-string-coercion
data.append(formFieldPrefix + promiseId, partJSON);
pendingParts--;
if (pendingParts === 0) {
resolve(data);
}
}, reason => {
// In the future we could consider serializing this as an error
// that throws on the server instead.
reject(reason);
});
return serializePromiseID(promiseId);
}
if (isArray(value)) {
// $FlowFixMe[incompatible-return]
return value;
} // TODO: Should we the Object.prototype.toString.call() to test for cross-realm objects?
if (value instanceof FormData) {
if (formData === null) {
// Upgrade to use FormData to allow us to use rich objects as its values.
formData = new FormData();
}
const data = formData;
const refId = nextPartId++; // Copy all the form fields with a prefix for this reference.
// These must come first in the form order because we assume that all the
// fields are available before this is referenced.
const prefix = formFieldPrefix + refId + '_'; // $FlowFixMe[prop-missing]: FormData has forEach.
value.forEach((originalValue, originalKey) => {
data.append(prefix + originalKey, originalValue);
});
return serializeFormDataReference(refId);
}
if (value instanceof Map) {
const partJSON = JSON.stringify(Array.from(value), resolveToJSON);
if (formData === null) {
formData = new FormData();
}
const mapId = nextPartId++;
formData.append(formFieldPrefix + mapId, partJSON);
return serializeMapID(mapId);
}
if (value instanceof Set) {
const partJSON = JSON.stringify(Array.from(value), resolveToJSON);
if (formData === null) {
formData = new FormData();
}
const setId = nextPartId++;
formData.append(formFieldPrefix + setId, partJSON);
return serializeSetID(setId);
}
const iteratorFn = getIteratorFn(value);
if (iteratorFn) {
return Array.from(value);
} // Verify that this is a simple plain object.
const proto = getPrototypeOf(value);
if (proto !== ObjectPrototype && (proto === null || getPrototypeOf(proto) !== null)) {
throw new Error('Only plain objects, and a few built-ins, can be passed to Server Actions. ' + 'Classes or null prototypes are not supported.');
}
return value;
}
if (typeof value === 'string') {
// TODO: Maybe too clever. If we support URL there's no similar trick.
if (value[value.length - 1] === 'Z') {
// Possibly a Date, whose toJSON automatically calls toISOString
// $FlowFixMe[incompatible-use]
const originalValue = parent[key];
if (originalValue instanceof Date) {
return serializeDateFromDateJSON(value);
}
}
return escapeStringValue(value);
}
if (typeof value === 'boolean') {
return value;
}
if (typeof value === 'number') {
return serializeNumber(value);
}
if (typeof value === 'undefined') {
return serializeUndefined();
}
if (typeof value === 'function') {
const metaData = knownServerReferences.get(value);
if (metaData !== undefined) {
const metaDataJSON = JSON.stringify(metaData, resolveToJSON);
if (formData === null) {
// Upgrade to use FormData to allow us to stream this value.
formData = new FormData();
} // The reference to this function came from the same client so we can pass it back.
const refId = nextPartId++; // eslint-disable-next-line react-internal/safe-string-coercion
formData.set(formFieldPrefix + refId, metaDataJSON);
return serializeServerReferenceID(refId);
}
throw new Error('Client Functions cannot be passed directly to Server Functions. ' + 'Only Functions passed from the Server can be passed back again.');
}
if (typeof value === 'symbol') {
// $FlowFixMe[incompatible-type] `description` might be undefined
const name = value.description;
if (Symbol.for(name) !== value) {
throw new Error('Only global symbols received from Symbol.for(...) can be passed to Server Functions. ' + ("The symbol Symbol.for(" + // $FlowFixMe[incompatible-type] `description` might be undefined
value.description + ") cannot be found among global symbols."));
}
return serializeSymbolReference(name);
}
if (typeof value === 'bigint') {
return serializeBigInt(value);
}
throw new Error("Type " + typeof value + " is not supported as an argument to a Server Function.");
} // $FlowFixMe[incompatible-type] it's not going to be undefined because we'll encode it.
const json = JSON.stringify(root, resolveToJSON);
if (formData === null) {
// If it's a simple data structure, we just use plain JSON.
resolve(json);
} else {
// Otherwise, we use FormData to let us stream in the result.
formData.set(formFieldPrefix + '0', json);
if (pendingParts === 0) {
// $FlowFixMe[incompatible-call] this has already been refined.
resolve(formData);
}
}
}
const boundCache = new WeakMap();
function encodeFormData(reference) {
let resolve, reject; // We need to have a handle on the thenable so that we can synchronously set
// its status from processReply, when it can complete synchronously.
const thenable = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
processReply(reference, '', body => {
if (typeof body === 'string') {
const data = new FormData();
data.append('0', body);
body = data;
}
const fulfilled = thenable;
fulfilled.status = 'fulfilled';
fulfilled.value = body;
resolve(body);
}, e => {
const rejected = thenable;
rejected.status = 'rejected';
rejected.reason = e;
reject(e);
});
return thenable;
}
function defaultEncodeFormAction(identifierPrefix) {
const reference = knownServerReferences.get(this);
if (!reference) {
throw new Error('Tried to encode a Server Action from a different instance than the encoder is from. ' + 'This is a bug in React.');
}
let data = null;
let name;
const boundPromise = reference.bound;
if (boundPromise !== null) {
let thenable = boundCache.get(reference);
if (!thenable) {
thenable = encodeFormData(reference);
boundCache.set(reference, thenable);
}
if (thenable.status === 'rejected') {
throw thenable.reason;
} else if (thenable.status !== 'fulfilled') {
throw thenable;
}
const encodedFormData = thenable.value; // This is hacky but we need the identifier prefix to be added to
// all fields but the suspense cache would break since we might get
// a new identifier each time. So we just append it at the end instead.
const prefixedData = new FormData(); // $FlowFixMe[prop-missing]
encodedFormData.forEach((value, key) => {
prefixedData.append('$ACTION_' + identifierPrefix + ':' + key, value);
});
data = prefixedData; // We encode the name of the prefix containing the data.
name = '$ACTION_REF_' + identifierPrefix;
} else {
// This is the simple case so we can just encode the ID.
name = '$ACTION_ID_' + reference.id;
}
return {
name: name,
method: 'POST',
encType: 'multipart/form-data',
data: data
};
}
function customEncodeFormAction(proxy, identifierPrefix, encodeFormAction) {
const reference = knownServerReferences.get(proxy);
if (!reference) {
throw new Error('Tried to encode a Server Action from a different instance than the encoder is from. ' + 'This is a bug in React.');
}
let boundPromise = reference.bound;
if (boundPromise === null) {
boundPromise = Promise.resolve([]);
}
return encodeFormAction(reference.id, boundPromise);
}
function isSignatureEqual(referenceId, numberOfBoundArgs) {
const reference = knownServerReferences.get(this);
if (!reference) {
throw new Error('Tried to encode a Server Action from a different instance than the encoder is from. ' + 'This is a bug in React.');
}
if (reference.id !== referenceId) {
// These are different functions.
return false;
} // Now check if the number of bound arguments is the same.
const boundPromise = reference.bound;
if (boundPromise === null) {
// No bound arguments.
return numberOfBoundArgs === 0;
} // Unwrap the bound arguments array by suspending, if necessary. As with
// encodeFormData, this means isSignatureEqual can only be called while React
// is rendering.
switch (boundPromise.status) {
case 'fulfilled':
{
const boundArgs = boundPromise.value;
return boundArgs.length === numberOfBoundArgs;
}
case 'pending':
{
throw boundPromise;
}
case 'rejected':
{
throw boundPromise.reason;
}
default:
{
if (typeof boundPromise.status === 'string') ; else {
const pendingThenable = boundPromise;
pendingThenable.status = 'pending';
pendingThenable.then(boundArgs => {
const fulfilledThenable = boundPromise;
fulfilledThenable.status = 'fulfilled';
fulfilledThenable.value = boundArgs;
}, error => {
const rejectedThenable = boundPromise;
rejectedThenable.status = 'rejected';
rejectedThenable.reason = error;
});
}
throw boundPromise;
}
}
}
function registerServerReference(proxy, reference, encodeFormAction) {
// Expose encoder for use by SSR, as well as a special bind that can be used to
// keep server capabilities.
{
// Only expose this in builds that would actually use it. Not needed on the client.
const $$FORM_ACTION = encodeFormAction === undefined ? defaultEncodeFormAction : function (identifierPrefix) {
return customEncodeFormAction(this, identifierPrefix, encodeFormAction);
};
Object.defineProperties(proxy, {
$$FORM_ACTION: {
value: $$FORM_ACTION
},
$$IS_SIGNATURE_EQUAL: {
value: isSignatureEqual
},
bind: {
value: bind
}
});
}
knownServerReferences.set(proxy, reference);
} // $FlowFixMe[method-unbinding]
const FunctionBind = Function.prototype.bind; // $FlowFixMe[method-unbinding]
const ArraySlice = Array.prototype.slice;
function bind() {
// $FlowFixMe[unsupported-syntax]
const newFn = FunctionBind.apply(this, arguments);
const reference = knownServerReferences.get(this);
if (reference) {
const args = ArraySlice.call(arguments, 1);
let boundPromise = null;
if (reference.bound !== null) {
boundPromise = Promise.resolve(reference.bound).then(boundArgs => boundArgs.concat(args));
} else {
boundPromise = Promise.resolve(args);
} // Expose encoder for use by SSR, as well as a special bind that can be used to
// keep server capabilities.
{
// Only expose this in builds that would actually use it. Not needed on the client.
Object.defineProperties(newFn, {
$$FORM_ACTION: {
value: this.$$FORM_ACTION
},
$$IS_SIGNATURE_EQUAL: {
value: isSignatureEqual
},
bind: {
value: bind
}
});
}
knownServerReferences.set(newFn, {
id: reference.id,
bound: boundPromise
});
}
return newFn;
}
function createServerReference$1(id, callServer, encodeFormAction) {
const proxy = function () {
// $FlowFixMe[method-unbinding]
const args = Array.prototype.slice.call(arguments);
return callServer(id, args);
};
registerServerReference(proxy, {
id,
bound: null
}, encodeFormAction);
return proxy;
}
const ROW_ID = 0;
const ROW_TAG = 1;
const ROW_LENGTH = 2;
const ROW_CHUNK_BY_NEWLINE = 3;
const ROW_CHUNK_BY_LENGTH = 4;
const PENDING = 'pending';
const BLOCKED = 'blocked';
const CYCLIC = 'cyclic';
const RESOLVED_MODEL = 'resolved_model';
const RESOLVED_MODULE = 'resolved_module';
const INITIALIZED = 'fulfilled';
const ERRORED = 'rejected'; // $FlowFixMe[missing-this-annot]
function Chunk(status, value, reason, response) {
this.status = status;
this.value = value;
this.reason = reason;
this._response = response;
} // We subclass Promise.prototype so that we get other methods like .catch
Chunk.prototype = Object.create(Promise.prototype); // TODO: This doesn't return a new Promise chain unlike the real .then
Chunk.prototype.then = function (resolve, reject) {
const chunk = this; // If we have resolved content, we try to initialize it first which
// might put us back into one of the other states.
switch (chunk.status) {
case RESOLVED_MODEL:
initializeModelChunk(chunk);
break;
case RESOLVED_MODULE:
initializeModuleChunk(chunk);
break;
} // The status might have changed after initialization.
switch (chunk.status) {
case INITIALIZED:
resolve(chunk.value);
break;
case PENDING:
case BLOCKED:
case CYCLIC:
if (resolve) {
if (chunk.value === null) {
chunk.value = [];
}
chunk.value.push(resolve);
}
if (reject) {
if (chunk.reason === null) {
chunk.reason = [];
}
chunk.reason.push(reject);
}
break;
default:
reject(chunk.reason);
break;
}
};
function readChunk(chunk) {
// If we have resolved content, we try to initialize it first which
// might put us back into one of the other states.
switch (chunk.status) {
case RESOLVED_MODEL:
initializeModelChunk(chunk);
break;
case RESOLVED_MODULE:
initializeModuleChunk(chunk);
break;
} // The status might have changed after initialization.
switch (chunk.status) {
case INITIALIZED:
return chunk.value;
case PENDING:
case BLOCKED:
case CYCLIC:
// eslint-disable-next-line no-throw-literal
throw chunk;
default:
throw chunk.reason;
}
}
function getRoot(response) {
const chunk = getChunk(response, 0);
return chunk;
}
function createPendingChunk(response) {
// $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors
return new Chunk(PENDING, null, null, response);
}
function createBlockedChunk(response) {
// $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors
return new Chunk(BLOCKED, null, null, response);
}
function createErrorChunk(response, error) {
// $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors
return new Chunk(ERRORED, null, error, response);
}
function wakeChunk(listeners, value) {
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i];
listener(value);
}
}
function wakeChunkIfInitialized(chunk, resolveListeners, rejectListeners) {
switch (chunk.status) {
case INITIALIZED:
wakeChunk(resolveListeners, chunk.value);
break;
case PENDING:
case BLOCKED:
case CYCLIC:
chunk.value = resolveListeners;
chunk.reason = rejectListeners;
break;
case ERRORED:
if (rejectListeners) {
wakeChunk(rejectListeners, chunk.reason);
}
break;
}
}
function triggerErrorOnChunk(chunk, error) {
if (chunk.status !== PENDING && chunk.status !== BLOCKED) {
// We already resolved. We didn't expect to see this.
return;
}
const listeners = chunk.reason;
const erroredChunk = chunk;
erroredChunk.status = ERRORED;
erroredChunk.reason = error;
if (listeners !== null) {
wakeChunk(listeners, error);
}
}
function createResolvedModelChunk(response, value) {
// $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors
return new Chunk(RESOLVED_MODEL, value, null, response);
}
function createResolvedModuleChunk(response, value) {
// $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors
return new Chunk(RESOLVED_MODULE, value, null, response);
}
function createInitializedTextChunk(response, value) {
// $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors
return new Chunk(INITIALIZED, value, null, response);
}
function resolveModelChunk(chunk, value) {
if (chunk.status !== PENDING) {
// We already resolved. We didn't expect to see this.
return;
}
const resolveListeners = chunk.value;
const rejectListeners = chunk.reason;
const resolvedChunk = chunk;
resolvedChunk.status = RESOLVED_MODEL;
resolvedChunk.value = value;
if (resolveListeners !== null) {
// This is unfortunate that we're reading this eagerly if
// we already have listeners attached since they might no
// longer be rendered or might not be the highest pri.
initializeModelChunk(resolvedChunk); // The status might have changed after initialization.
wakeChunkIfInitialized(chunk, resolveListeners, rejectListeners);
}
}
function resolveModuleChunk(chunk, value) {
if (chunk.status !== PENDING && chunk.status !== BLOCKED) {
// We already resolved. We didn't expect to see this.
return;
}
const resolveListeners = chunk.value;
const rejectListeners = chunk.reason;
const resolvedChunk = chunk;
resolvedChunk.status = RESOLVED_MODULE;
resolvedChunk.value = value;
if (resolveListeners !== null) {
initializeModuleChunk(resolvedChunk);
wakeChunkIfInitialized(chunk, resolveListeners, rejectListeners);
}
}
let initializingChunk = null;
let initializingChunkBlockedModel = null;
function initializeModelChunk(chunk) {
const prevChunk = initializingChunk;
const prevBlocked = initializingChunkBlockedModel;
initializingChunk = chunk;
initializingChunkBlockedModel = null;
const resolvedModel = chunk.value; // We go to the CYCLIC state until we've fully resolved this.
// We do this before parsing in case we try to initialize the same chunk
// while parsing the model. Such as in a cyclic reference.
const cyclicChunk = chunk;
cyclicChunk.status = CYCLIC;
cyclicChunk.value = null;
cyclicChunk.reason = null;
try {
const value = parseModel(chunk._response, resolvedModel);
if (initializingChunkBlockedModel !== null && initializingChunkBlockedModel.deps > 0) {
initializingChunkBlockedModel.value = value; // We discovered new dependencies on modules that are not yet resolved.
// We have to go the BLOCKED state until they're resolved.
const blockedChunk = chunk;
blockedChunk.status = BLOCKED;
blockedChunk.value = null;
blockedChunk.reason = null;
} else {
const resolveListeners = cyclicChunk.value;
const initializedChunk = chunk;
initializedChunk.status = INITIALIZED;
initializedChunk.value = value;
if (resolveListeners !== null) {
wakeChunk(resolveListeners, value);
}
}
} catch (error) {
const erroredChunk = chunk;
erroredChunk.status = ERRORED;
erroredChunk.reason = error;
} finally {
initializingChunk = prevChunk;
initializingChunkBlockedModel = prevBlocked;
}
}
function initializeModuleChunk(chunk) {
try {
const value = requireModule(chunk.value);
const initializedChunk = chunk;
initializedChunk.status = INITIALIZED;
initializedChunk.value = value;
} catch (error) {
const erroredChunk = chunk;
erroredChunk.status = ERRORED;
erroredChunk.reason = error;
}
} // Report that any missing chunks in the model is now going to throw this
// error upon read. Also notify any pending promises.
function reportGlobalError(response, error) {
response._chunks.forEach(chunk => {
// If this chunk was already resolved or errored, it won't
// trigger an error but if it wasn't then we need to
// because we won't be getting any new data to resolve it.
if (chunk.status === PENDING) {
triggerErrorOnChunk(chunk, error);
}
});
}
function createElement(type, key, props) {
const element = {
// This tag allows us to uniquely identify this as a React Element
$$typeof: REACT_ELEMENT_TYPE,
// Built-in properties that belong on the element
type: type,
key: key,
ref: null,
props: props,
// Record the component responsible for creating this element.
_owner: null
};
return element;
}
function createLazyChunkWrapper(chunk) {
const lazyType = {
$$typeof: REACT_LAZY_TYPE,
_payload: chunk,
_init: readChunk
};
return lazyType;
}
function getChunk(response, id) {
const chunks = response._chunks;
let chunk = chunks.get(id);
if (!chunk) {
chunk = createPendingChunk(response);
chunks.set(id, chunk);
}
return chunk;
}
function createModelResolver(chunk, parentObject, key, cyclic) {
let blocked;
if (initializingChunkBlockedModel) {
blocked = initializingChunkBlockedModel;
if (!cyclic) {
blocked.deps++;
}
} else {
blocked = initializingChunkBlockedModel = {
deps: cyclic ? 0 : 1,
value: null
};
}
return value => {
parentObject[key] = value;
blocked.deps--;
if (blocked.deps === 0) {
if (chunk.status !== BLOCKED) {
return;
}
const resolveListeners = chunk.value;
const initializedChunk = chunk;
initializedChunk.status = INITIALIZED;
initializedChunk.value = blocked.value;
if (resolveListeners !== null) {
wakeChunk(resolveListeners, blocked.value);
}
}
};
}
function createModelReject(chunk) {
return error => triggerErrorOnChunk(chunk, error);
}
function createServerReferenceProxy(response, metaData) {
const callServer = response._callServer;
const proxy = function () {
// $FlowFixMe[method-unbinding]
const args = Array.prototype.slice.call(arguments);
const p = metaData.bound;
if (!p) {
return callServer(metaData.id, args);
}
if (p.status === INITIALIZED) {
const bound = p.value;
return callServer(metaData.id, bound.concat(args));
} // Since this is a fake Promise whose .then doesn't chain, we have to wrap it.
// TODO: Remove the wrapper once that's fixed.
return Promise.resolve(p).then(function (bound) {
return callServer(metaData.id, bound.concat(args));
});
};
registerServerReference(proxy, metaData, response._encodeFormAction);
return proxy;
}
function getOutlinedModel(response, id) {
const chunk = getChunk(response, id);
switch (chunk.status) {
case RESOLVED_MODEL:
initializeModelChunk(chunk);
break;
} // The status might have changed after initialization.
switch (chunk.status) {
case INITIALIZED:
{
return chunk.value;
}
// We always encode it first in the stream so it won't be pending.
default:
throw chunk.reason;
}
}
function parseModelString(response, parentObject, key, value) {
if (value[0] === '$') {
if (value === '$') {
// A very common symbol.
return REACT_ELEMENT_TYPE;
}
switch (value[1]) {
case '$':
{
// This was an escaped string value.
return value.slice(1);
}
case 'L':
{
// Lazy node
const id = parseInt(value.slice(2), 16);
const chunk = getChunk(response, id); // We create a React.lazy wrapper around any lazy values.
// When passed into React, we'll know how to suspend on this.
return createLazyChunkWrapper(chunk);
}
case '@':
{
// Promise
const id = parseInt(value.slice(2), 16);
const chunk = getChunk(response, id);
return chunk;
}
case 'S':
{
// Symbol
return Symbol.for(value.slice(2));
}
case 'F':
{
// Server Reference
const id = parseInt(value.slice(2), 16);
const metadata = getOutlinedModel(response, id);
return createServerReferenceProxy(response, metadata);
}
case 'Q':
{
// Map
const id = parseInt(value.slice(2), 16);
const data = getOutlinedModel(response, id);
return new Map(data);
}
case 'W':
{
// Set
const id = parseInt(value.slice(2), 16);
const data = getOutlinedModel(response, id);
return new Set(data);
}
case 'I':
{
// $Infinity
return Infinity;
}
case '-':
{
// $-0 or $-Infinity
if (value === '$-0') {
return -0;
} else {
return -Infinity;
}
}
case 'N':
{
// $NaN
return NaN;
}
case 'u':
{
// matches "$undefined"
// Special encoding for `undefined` which can't be serialized as JSON otherwise.
return undefined;
}
case 'D':
{
// Date
return new Date(Date.parse(value.slice(2)));
}
case 'n':
{
// BigInt
return BigInt(value.slice(2));
}
default:
{
// We assume that anything else is a reference ID.
const id = parseInt(value.slice(1), 16);
const chunk = getChunk(response, id);
switch (chunk.status) {
case RESOLVED_MODEL:
initializeModelChunk(chunk);
break;
case RESOLVED_MODULE:
initializeModuleChunk(chunk);
break;
} // The status might have changed after initialization.
switch (chunk.status) {
case INITIALIZED:
const chunkValue = chunk.value;
return chunkValue;
case PENDING:
case BLOCKED:
case CYCLIC:
const parentChunk = initializingChunk;
chunk.then(createModelResolver(parentChunk, parentObject, key, chunk.status === CYCLIC), createModelReject(parentChunk));
return null;
default:
throw chunk.reason;
}
}
}
}
return value;
}
function parseModelTuple(response, value) {
const tuple = value;
if (tuple[0] === REACT_ELEMENT_TYPE) {
// TODO: Consider having React just directly accept these arrays as elements.
// Or even change the ReactElement type to be an array.
return createElement(tuple[1], tuple[2], tuple[3]);
}
return value;
}
function missingCall() {
throw new Error('Trying to call a function from "use server" but the callServer option ' + 'was not implemented in your router runtime.');
}
function createResponse(bundlerConfig, moduleLoading, callServer, encodeFormAction, nonce) {
const chunks = new Map();
const response = {
_bundlerConfig: bundlerConfig,
_moduleLoading: moduleLoading,
_callServer: callServer !== undefined ? callServer : missingCall,
_encodeFormAction: encodeFormAction,
_nonce: nonce,
_chunks: chunks,
_stringDecoder: createStringDecoder(),
_fromJSON: null,
_rowState: 0,
_rowID: 0,
_rowTag: 0,
_rowLength: 0,
_buffer: []
}; // Don't inline this call because it causes closure to outline the call above.
response._fromJSON = createFromJSONCallback(response);
return response;
}
function resolveModel(response, id, model) {
const chunks = response._chunks;
const chunk = chunks.get(id);
if (!chunk) {
chunks.set(id, createResolvedModelChunk(response, model));
} else {
resolveModelChunk(chunk, model);
}
}
function resolveText(response, id, text) {
const chunks = response._chunks; // We assume that we always reference large strings after they've been
// emitted.
chunks.set(id, createInitializedTextChunk(response, text));
}
function resolveModule(response, id, model) {
const chunks = response._chunks;
const chunk = chunks.get(id);
const clientReferenceMetadata = parseModel(response, model);
const clientReference = resolveClientReference(response._bundlerConfig, clientReferenceMetadata);
prepareDestinationForModule(response._moduleLoading, response._nonce, clientReferenceMetadata); // TODO: Add an option to encode modules that are lazy loaded.
// For now we preload all modules as early as possible since it's likely
// that we'll need them.
const promise = preloadModule(clientReference);
if (promise) {
let blockedChunk;
if (!chunk) {
// Technically, we should just treat promise as the chunk in this
// case. Because it'll just behave as any other promise.
blockedChunk = createBlockedChunk(response);
chunks.set(id, blockedChunk);
} else {
// This can't actually happen because we don't have any forward
// references to modules.
blockedChunk = chunk;
blockedChunk.status = BLOCKED;
}
promise.then(() => resolveModuleChunk(blockedChunk, clientReference), error => triggerErrorOnChunk(blockedChunk, error));
} else {
if (!chunk) {
chunks.set(id, createResolvedModuleChunk(response, clientReference));
} else {
// This can't actually happen because we don't have any forward
// references to modules.
resolveModuleChunk(chunk, clientReference);
}
}
}
function resolveErrorProd(response, id, digest) {
const error = new Error('An error occurred in the Server Components render. The specific message is omitted in production' + ' builds to avoid leaking sensitive details. A digest property is included on this error instance which' + ' may provide additional details about the nature of the error.');
error.stack = 'Error: ' + error.message;
error.digest = digest;
const errorWithDigest = error;
const chunks = response._chunks;
const chunk = chunks.get(id);
if (!chunk) {
chunks.set(id, createErrorChunk(response, errorWithDigest));
} else {
triggerErrorOnChunk(chunk, errorWithDigest);
}
}
function resolveHint(response, code, model) {
const hintModel = parseModel(response, model);
dispatchHint(code, hintModel);
}
function processFullRow(response, id, tag, buffer, chunk) {
const stringDecoder = response._stringDecoder;
let row = '';
for (let i = 0; i < buffer.length; i++) {
row += readPartialStringChunk(stringDecoder, buffer[i]);
}
row += readFinalStringChunk(stringDecoder, chunk);
switch (tag) {
case 73
/* "I" */
:
{
resolveModule(response, id, row);
return;
}
case 72
/* "H" */
:
{
const code = row[0];
resolveHint(response, code, row.slice(1));
return;
}
case 69
/* "E" */
:
{
const errorInfo = JSON.parse(row);
{
resolveErrorProd(response, id, errorInfo.digest);
}
return;
}
case 84
/* "T" */
:
{
resolveText(response, id, row);
return;
}
case 68
/* "D" */
:
{
throw new Error('Failed to read a RSC payload created by a development version of React ' + 'on the server while using a production version on the client. Always use ' + 'matching versions on the server and the client.');
}
case 80
/* "P" */
:
// Fallthrough
default:
/* """ "{" "[" "t" "f" "n" "0" - "9" */
{
// We assume anything else is JSON.
resolveModel(response, id, row);
return;
}
}
}
function processBinaryChunk(response, chunk) {
let i = 0;
let rowState = response._rowState;
let rowID = response._rowID;
let rowTag = response._rowTag;
let rowLength = response._rowLength;
const buffer = response._buffer;
const chunkLength = chunk.length;
while (i < chunkLength) {
let lastIdx = -1;
switch (rowState) {
case ROW_ID:
{
const byte = chunk[i++];
if (byte === 58
/* ":" */
) {
// Finished the rowID, next we'll parse the tag.
rowState = ROW_TAG;
} else {
rowID = rowID << 4 | (byte > 96 ? byte - 87 : byte - 48);
}
continue;
}
case ROW_TAG:
{
const resolvedRowTag = chunk[i];
if (resolvedRowTag === 84
/* "T" */
|| enableBinaryFlight
/* "V" */
) {
rowTag = resolvedRowTag;
rowState = ROW_LENGTH;
i++;
} else if (resolvedRowTag > 64 && resolvedRowTag < 91
/* "A"-"Z" */
) {
rowTag = resolvedRowTag;
rowState = ROW_CHUNK_BY_NEWLINE;
i++;
} else {
rowTag = 0;
rowState = ROW_CHUNK_BY_NEWLINE; // This was an unknown tag so it was probably part of the data.
}
continue;
}
case ROW_LENGTH:
{
const byte = chunk[i++];
if (byte === 44
/* "," */
) {
// Finished the rowLength, next we'll buffer up to that length.
rowState = ROW_CHUNK_BY_LENGTH;
} else {
rowLength = rowLength << 4 | (byte > 96 ? byte - 87 : byte - 48);
}
continue;
}
case ROW_CHUNK_BY_NEWLINE:
{
// We're looking for a newline
lastIdx = chunk.indexOf(10
/* "\n" */
, i);
break;
}
case ROW_CHUNK_BY_LENGTH:
{
// We're looking for the remaining byte length
lastIdx = i + rowLength;
if (lastIdx > chunk.length) {
lastIdx = -1;
}
break;
}
}
const offset = chunk.byteOffset + i;
if (lastIdx > -1) {
// We found the last chunk of the row
const length = lastIdx - i;
const lastChunk = new Uint8Array(chunk.buffer, offset, length);
processFullRow(response, rowID, rowTag, buffer, lastChunk); // Reset state machine for a new row
i = lastIdx;
if (rowState === ROW_CHUNK_BY_NEWLINE) {
// If we're trailing by a newline we need to skip it.
i++;
}
rowState = ROW_ID;
rowTag = 0;
rowID = 0;
rowLength = 0;
buffer.length = 0;
} else {
// The rest of this row is in a future chunk. We stash the rest of the
// current chunk until we can process the full row.
const length = chunk.byteLength - i;
const remainingSlice = new Uint8Array(chunk.buffer, offset, length);
buffer.push(remainingSlice); // Update how many bytes we're still waiting for. If we're looking for
// a newline, this doesn't hurt since we'll just ignore it.
rowLength -= remainingSlice.byteLength;
break;
}
}
response._rowState = rowState;
response._rowID = rowID;
response._rowTag = rowTag;
response._rowLength = rowLength;
}
function parseModel(response, json) {
return JSON.parse(json, response._fromJSON);
}
function createFromJSONCallback(response) {
// $FlowFixMe[missing-this-annot]
return function (key, value) {
if (typeof value === 'string') {
// We can't use .bind here because we need the "this" value.
return parseModelString(response, this, key, value);
}
if (typeof value === 'object' && value !== null) {
return parseModelTuple(response, value);
}
return value;
};
}
function close(response) {
// In case there are any remaining unresolved chunks, they won't
// be resolved now. So we need to issue an error to those.
// Ideally we should be able to early bail out if we kept a
// ref count of pending chunks.
reportGlobalError(response, new Error('Connection closed.'));
}
function noServerCall() {
throw new Error('Server Functions cannot be called during initial render. ' + 'This would create a fetch waterfall. Try to use a Server Component ' + 'to pass data to Client Components instead.');
}
function createServerReference(id, callServer) {
return createServerReference$1(id, noServerCall);
}
function createFromNodeStream(stream, ssrManifest, options) {
const response = createResponse(ssrManifest.moduleMap, ssrManifest.moduleLoading, noServerCall, options ? options.encodeFormAction : undefined, options && typeof options.nonce === 'string' ? options.nonce : undefined);
stream.on('data', chunk => {
processBinaryChunk(response, chunk);
});
stream.on('error', error => {
reportGlobalError(response, error);
});
stream.on('end', () => close(response));
return getRoot(response);
}
exports.createFromNodeStream = createFromNodeStream;
exports.createServerReference = createServerReference;