import type {
Thenable,
ReactDebugInfo,
ReactComponentInfo,
ReactAsyncInfo,
} from 'shared/ReactTypes';
import type {LazyComponent} from 'react/src/ReactLazy';
import type {
ClientReference,
ClientReferenceMetadata,
SSRModuleMap,
StringDecoder,
ModuleLoading,
} from './ReactFlightClientConfig';
import type {
HintCode,
HintModel,
} from 'react-server/src/ReactFlightServerConfig';
import type {
CallServerCallback,
EncodeFormActionCallback,
} from './ReactFlightReplyClient';
import type {Postpone} from 'react/src/ReactPostpone';
import type {TemporaryReferenceSet} from './ReactFlightTemporaryReferences';
import {
enableBinaryFlight,
enablePostpone,
enableRefAsProp,
} from 'shared/ReactFeatureFlags';
import {
resolveClientReference,
preloadModule,
requireModule,
dispatchHint,
readPartialStringChunk,
readFinalStringChunk,
createStringDecoder,
prepareDestinationForModule,
printToConsole,
} from './ReactFlightClientConfig';
import {registerServerReference} from './ReactFlightReplyClient';
import {readTemporaryReference} from './ReactFlightTemporaryReferences';
import {
REACT_LAZY_TYPE,
REACT_ELEMENT_TYPE,
REACT_POSTPONE_TYPE,
} from 'shared/ReactSymbols';
export type {CallServerCallback, EncodeFormActionCallback};
type UninitializedModel = string;
export type JSONValue =
| number
| null
| boolean
| string
| {+[key: string]: JSONValue}
| $ReadOnlyArray<JSONValue>;
const ROW_ID = 0;
const ROW_TAG = 1;
const ROW_LENGTH = 2;
const ROW_CHUNK_BY_NEWLINE = 3;
const ROW_CHUNK_BY_LENGTH = 4;
type RowParserState = 0 | 1 | 2 | 3 | 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';
type PendingChunk<T> = {
status: 'pending',
value: null | Array<(T) => mixed>,
reason: null | Array<(mixed) => mixed>,
_response: Response,
_debugInfo?: null | ReactDebugInfo,
then(resolve: (T) => mixed, reject: (mixed) => mixed): void,
};
type BlockedChunk<T> = {
status: 'blocked',
value: null | Array<(T) => mixed>,
reason: null | Array<(mixed) => mixed>,
_response: Response,
_debugInfo?: null | ReactDebugInfo,
then(resolve: (T) => mixed, reject: (mixed) => mixed): void,
};
type CyclicChunk<T> = {
status: 'cyclic',
value: null | Array<(T) => mixed>,
reason: null | Array<(mixed) => mixed>,
_response: Response,
_debugInfo?: null | ReactDebugInfo,
then(resolve: (T) => mixed, reject: (mixed) => mixed): void,
};
type ResolvedModelChunk<T> = {
status: 'resolved_model',
value: UninitializedModel,
reason: null,
_response: Response,
_debugInfo?: null | ReactDebugInfo,
then(resolve: (T) => mixed, reject: (mixed) => mixed): void,
};
type ResolvedModuleChunk<T> = {
status: 'resolved_module',
value: ClientReference<T>,
reason: null,
_response: Response,
_debugInfo?: null | ReactDebugInfo,
then(resolve: (T) => mixed, reject: (mixed) => mixed): void,
};
type InitializedChunk<T> = {
status: 'fulfilled',
value: T,
reason: null,
_response: Response,
_debugInfo?: null | ReactDebugInfo,
then(resolve: (T) => mixed, reject: (mixed) => mixed): void,
};
type ErroredChunk<T> = {
status: 'rejected',
value: null,
reason: mixed,
_response: Response,
_debugInfo?: null | ReactDebugInfo,
then(resolve: (T) => mixed, reject: (mixed) => mixed): void,
};
type SomeChunk<T> =
| PendingChunk<T>
| BlockedChunk<T>
| CyclicChunk<T>
| ResolvedModelChunk<T>
| ResolvedModuleChunk<T>
| InitializedChunk<T>
| ErroredChunk<T>;
function Chunk(status: any, value: any, reason: any, response: Response) {
this.status = status;
this.value = value;
this.reason = reason;
this._response = response;
if (__DEV__) {
this._debugInfo = null;
}
}
Chunk.prototype = (Object.create(Promise.prototype): any);
Chunk.prototype.then = function <T>(
this: SomeChunk<T>,
resolve: (value: T) => mixed,
reject: (reason: mixed) => mixed,
) {
const chunk: SomeChunk<T> = this;
switch (chunk.status) {
case RESOLVED_MODEL:
initializeModelChunk(chunk);
break;
case RESOLVED_MODULE:
initializeModuleChunk(chunk);
break;
}
switch (chunk.status) {
case INITIALIZED:
resolve(chunk.value);
break;
case PENDING:
case BLOCKED:
case CYCLIC:
if (resolve) {
if (chunk.value === null) {
chunk.value = ([]: Array<(T) => mixed>);
}
chunk.value.push(resolve);
}
if (reject) {
if (chunk.reason === null) {
chunk.reason = ([]: Array<(mixed) => mixed>);
}
chunk.reason.push(reject);
}
break;
default:
reject(chunk.reason);
break;
}
};
export type Response = {
_bundlerConfig: SSRModuleMap,
_moduleLoading: ModuleLoading,
_callServer: CallServerCallback,
_encodeFormAction: void | EncodeFormActionCallback,
_nonce: ?string,
_chunks: Map<number, SomeChunk<any>>,
_fromJSON: (key: string, value: JSONValue) => any,
_stringDecoder: StringDecoder,
_rowState: RowParserState,
_rowID: number,
_rowTag: number,
_rowLength: number,
_buffer: Array<Uint8Array>,
_tempRefs: void | TemporaryReferenceSet,
};
function readChunk<T>(chunk: SomeChunk<T>): T {
switch (chunk.status) {
case RESOLVED_MODEL:
initializeModelChunk(chunk);
break;
case RESOLVED_MODULE:
initializeModuleChunk(chunk);
break;
}
switch (chunk.status) {
case INITIALIZED:
return chunk.value;
case PENDING:
case BLOCKED:
case CYCLIC:
throw ((chunk: any): Thenable<T>);
default:
throw chunk.reason;
}
}
export function getRoot<T>(response: Response): Thenable<T> {
const chunk = getChunk(response, 0);
return (chunk: any);
}
function createPendingChunk<T>(response: Response): PendingChunk<T> {
return new Chunk(PENDING, null, null, response);
}
function createBlockedChunk<T>(response: Response): BlockedChunk<T> {
return new Chunk(BLOCKED, null, null, response);
}
function createErrorChunk<T>(
response: Response,
error: Error | Postpone,
): ErroredChunk<T> {
return new Chunk(ERRORED, null, error, response);
}
function wakeChunk<T>(listeners: Array<(T) => mixed>, value: T): void {
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i];
listener(value);
}
}
function wakeChunkIfInitialized<T>(
chunk: SomeChunk<T>,
resolveListeners: Array<(T) => mixed>,
rejectListeners: null | Array<(mixed) => mixed>,
): void {
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<T>(chunk: SomeChunk<T>, error: mixed): void {
if (chunk.status !== PENDING && chunk.status !== BLOCKED) {
return;
}
const listeners = chunk.reason;
const erroredChunk: ErroredChunk<T> = (chunk: any);
erroredChunk.status = ERRORED;
erroredChunk.reason = error;
if (listeners !== null) {
wakeChunk(listeners, error);
}
}
function createResolvedModelChunk<T>(
response: Response,
value: UninitializedModel,
): ResolvedModelChunk<T> {
return new Chunk(RESOLVED_MODEL, value, null, response);
}
function createResolvedModuleChunk<T>(
response: Response,
value: ClientReference<T>,
): ResolvedModuleChunk<T> {
return new Chunk(RESOLVED_MODULE, value, null, response);
}
function createInitializedTextChunk(
response: Response,
value: string,
): InitializedChunk<string> {
return new Chunk(INITIALIZED, value, null, response);
}
function createInitializedBufferChunk(
response: Response,
value: $ArrayBufferView | ArrayBuffer,
): InitializedChunk<Uint8Array> {
return new Chunk(INITIALIZED, value, null, response);
}
function resolveModelChunk<T>(
chunk: SomeChunk<T>,
value: UninitializedModel,
): void {
if (chunk.status !== PENDING) {
return;
}
const resolveListeners = chunk.value;
const rejectListeners = chunk.reason;
const resolvedChunk: ResolvedModelChunk<T> = (chunk: any);
resolvedChunk.status = RESOLVED_MODEL;
resolvedChunk.value = value;
if (resolveListeners !== null) {
initializeModelChunk(resolvedChunk);
wakeChunkIfInitialized(chunk, resolveListeners, rejectListeners);
}
}
function resolveModuleChunk<T>(
chunk: SomeChunk<T>,
value: ClientReference<T>,
): void {
if (chunk.status !== PENDING && chunk.status !== BLOCKED) {
return;
}
const resolveListeners = chunk.value;
const rejectListeners = chunk.reason;
const resolvedChunk: ResolvedModuleChunk<T> = (chunk: any);
resolvedChunk.status = RESOLVED_MODULE;
resolvedChunk.value = value;
if (resolveListeners !== null) {
initializeModuleChunk(resolvedChunk);
wakeChunkIfInitialized(chunk, resolveListeners, rejectListeners);
}
}
let initializingChunk: ResolvedModelChunk<any> = (null: any);
let initializingChunkBlockedModel: null | {deps: number, value: any} = null;
function initializeModelChunk<T>(chunk: ResolvedModelChunk<T>): void {
const prevChunk = initializingChunk;
const prevBlocked = initializingChunkBlockedModel;
initializingChunk = chunk;
initializingChunkBlockedModel = null;
const resolvedModel = chunk.value;
const cyclicChunk: CyclicChunk<T> = (chunk: any);
cyclicChunk.status = CYCLIC;
cyclicChunk.value = null;
cyclicChunk.reason = null;
try {
const value: T = parseModel(chunk._response, resolvedModel);
if (
initializingChunkBlockedModel !== null &&
initializingChunkBlockedModel.deps > 0
) {
initializingChunkBlockedModel.value = value;
const blockedChunk: BlockedChunk<T> = (chunk: any);
blockedChunk.status = BLOCKED;
blockedChunk.value = null;
blockedChunk.reason = null;
} else {
const resolveListeners = cyclicChunk.value;
const initializedChunk: InitializedChunk<T> = (chunk: any);
initializedChunk.status = INITIALIZED;
initializedChunk.value = value;
if (resolveListeners !== null) {
wakeChunk(resolveListeners, value);
}
}
} catch (error) {
const erroredChunk: ErroredChunk<T> = (chunk: any);
erroredChunk.status = ERRORED;
erroredChunk.reason = error;
} finally {
initializingChunk = prevChunk;
initializingChunkBlockedModel = prevBlocked;
}
}
function initializeModuleChunk<T>(chunk: ResolvedModuleChunk<T>): void {
try {
const value: T = requireModule(chunk.value);
const initializedChunk: InitializedChunk<T> = (chunk: any);
initializedChunk.status = INITIALIZED;
initializedChunk.value = value;
} catch (error) {
const erroredChunk: ErroredChunk<T> = (chunk: any);
erroredChunk.status = ERRORED;
erroredChunk.reason = error;
}
}
export function reportGlobalError(response: Response, error: Error): void {
response._chunks.forEach(chunk => {
if (chunk.status === PENDING) {
triggerErrorOnChunk(chunk, error);
}
});
}
function nullRefGetter() {
if (__DEV__) {
return null;
}
}
function createElement(
type: mixed,
key: mixed,
props: mixed,
): React$Element<any> {
let element: any;
if (__DEV__ && enableRefAsProp) {
element = ({
$$typeof: REACT_ELEMENT_TYPE,
type,
key,
props,
_owner: null,
}: any);
Object.defineProperty(element, 'ref', {
enumerable: false,
get: nullRefGetter,
});
} else {
element = ({
$$typeof: REACT_ELEMENT_TYPE,
type,
key,
ref: null,
props,
_owner: null,
}: any);
}
if (__DEV__) {
element._store = ({}: {
validated?: boolean,
});
Object.defineProperty(element._store, 'validated', {
configurable: false,
enumerable: false,
writable: true,
value: true,
});
Object.defineProperty(element, '_debugInfo', {
configurable: false,
enumerable: false,
writable: true,
value: null,
});
}
return element;
}
function createLazyChunkWrapper<T>(
chunk: SomeChunk<T>,
): LazyComponent<T, SomeChunk<T>> {
const lazyType: LazyComponent<T, SomeChunk<T>> = {
$$typeof: REACT_LAZY_TYPE,
_payload: chunk,
_init: readChunk,
};
if (__DEV__) {
const chunkDebugInfo: ReactDebugInfo =
chunk._debugInfo || (chunk._debugInfo = []);
lazyType._debugInfo = chunkDebugInfo;
}
return lazyType;
}
function getChunk(response: Response, id: number): SomeChunk<any> {
const chunks = response._chunks;
let chunk = chunks.get(id);
if (!chunk) {
chunk = createPendingChunk(response);
chunks.set(id, chunk);
}
return chunk;
}
function createModelResolver<T>(
chunk: SomeChunk<T>,
parentObject: Object,
key: string,
cyclic: boolean,
): (value: any) => void {
let blocked;
if (initializingChunkBlockedModel) {
blocked = initializingChunkBlockedModel;
if (!cyclic) {
blocked.deps++;
}
} else {
blocked = initializingChunkBlockedModel = {
deps: cyclic ? 0 : 1,
value: (null: any),
};
}
return value => {
parentObject[key] = value;
blocked.deps--;
if (blocked.deps === 0) {
if (chunk.status !== BLOCKED) {
return;
}
const resolveListeners = chunk.value;
const initializedChunk: InitializedChunk<T> = (chunk: any);
initializedChunk.status = INITIALIZED;
initializedChunk.value = blocked.value;
if (resolveListeners !== null) {
wakeChunk(resolveListeners, blocked.value);
}
}
};
}
function createModelReject<T>(chunk: SomeChunk<T>): (error: mixed) => void {
return (error: mixed) => triggerErrorOnChunk(chunk, error);
}
function createServerReferenceProxy<A: Iterable<any>, T>(
response: Response,
metaData: {id: any, bound: null | Thenable<Array<any>>},
): (...A) => Promise<T> {
const callServer = response._callServer;
const proxy = function (): Promise<T> {
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));
}
return ((Promise.resolve(p): any): Promise<Array<any>>).then(
function (bound) {
return callServer(metaData.id, bound.concat(args));
},
);
};
registerServerReference(proxy, metaData, response._encodeFormAction);
return proxy;
}
function getOutlinedModel(response: Response, id: number): any {
const chunk = getChunk(response, id);
switch (chunk.status) {
case RESOLVED_MODEL:
initializeModelChunk(chunk);
break;
}
switch (chunk.status) {
case INITIALIZED: {
return chunk.value;
}
default:
throw chunk.reason;
}
}
function parseModelString(
response: Response,
parentObject: Object,
key: string,
value: string,
): any {
if (value[0] === '$') {
if (value === '$') {
return REACT_ELEMENT_TYPE;
}
switch (value[1]) {
case '$': {
return value.slice(1);
}
case 'L': {
const id = parseInt(value.slice(2), 16);
const chunk = getChunk(response, id);
return createLazyChunkWrapper(chunk);
}
case '@': {
if (value.length === 2) {
return new Promise(() => {});
}
const id = parseInt(value.slice(2), 16);
const chunk = getChunk(response, id);
return chunk;
}
case 'S': {
return Symbol.for(value.slice(2));
}
case 'F': {
const id = parseInt(value.slice(2), 16);
const metadata = getOutlinedModel(response, id);
return createServerReferenceProxy(response, metadata);
}
case 'T': {
const id = parseInt(value.slice(2), 16);
const temporaryReferences = response._tempRefs;
if (temporaryReferences == null) {
throw new Error(
'Missing a temporary reference set but the RSC response returned a temporary reference. ' +
'Pass a temporaryReference option with the set that was used with the reply.',
);
}
return readTemporaryReference(temporaryReferences, id);
}
case 'Q': {
const id = parseInt(value.slice(2), 16);
const data = getOutlinedModel(response, id);
return new Map(data);
}
case 'W': {
const id = parseInt(value.slice(2), 16);
const data = getOutlinedModel(response, id);
return new Set(data);
}
case 'I': {
return Infinity;
}
case '-': {
if (value === '$-0') {
return -0;
} else {
return -Infinity;
}
}
case 'N': {
return NaN;
}
case 'u': {
return undefined;
}
case 'D': {
return new Date(Date.parse(value.slice(2)));
}
case 'n': {
return BigInt(value.slice(2));
}
case 'E': {
if (__DEV__) {
try {
return (0, eval)(value.slice(2));
} catch (x) {
return function () {};
}
}
}
default: {
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;
}
switch (chunk.status) {
case INITIALIZED:
const chunkValue = chunk.value;
if (__DEV__ && chunk._debugInfo) {
if (
typeof chunkValue === 'object' &&
chunkValue !== null &&
(Array.isArray(chunkValue) ||
chunkValue.$$typeof === REACT_ELEMENT_TYPE) &&
!chunkValue._debugInfo
) {
Object.defineProperty(chunkValue, '_debugInfo', {
configurable: false,
enumerable: false,
writable: true,
value: chunk._debugInfo,
});
}
}
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: Response,
value: {+[key: string]: JSONValue} | $ReadOnlyArray<JSONValue>,
): any {
const tuple: [mixed, mixed, mixed, mixed] = (value: any);
if (tuple[0] === REACT_ELEMENT_TYPE) {
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.',
);
}
export function createResponse(
bundlerConfig: SSRModuleMap,
moduleLoading: ModuleLoading,
callServer: void | CallServerCallback,
encodeFormAction: void | EncodeFormActionCallback,
nonce: void | string,
temporaryReferences: void | TemporaryReferenceSet,
): Response {
const chunks: Map<number, SomeChunk<any>> = new Map();
const response: Response = {
_bundlerConfig: bundlerConfig,
_moduleLoading: moduleLoading,
_callServer: callServer !== undefined ? callServer : missingCall,
_encodeFormAction: encodeFormAction,
_nonce: nonce,
_chunks: chunks,
_stringDecoder: createStringDecoder(),
_fromJSON: (null: any),
_rowState: 0,
_rowID: 0,
_rowTag: 0,
_rowLength: 0,
_buffer: [],
_tempRefs: temporaryReferences,
};
response._fromJSON = createFromJSONCallback(response);
return response;
}
function resolveModel(
response: Response,
id: number,
model: UninitializedModel,
): void {
const chunks = response._chunks;
const chunk = chunks.get(id);
if (!chunk) {
chunks.set(id, createResolvedModelChunk(response, model));
} else {
resolveModelChunk(chunk, model);
}
}
function resolveText(response: Response, id: number, text: string): void {
const chunks = response._chunks;
chunks.set(id, createInitializedTextChunk(response, text));
}
function resolveBuffer(
response: Response,
id: number,
buffer: $ArrayBufferView | ArrayBuffer,
): void {
const chunks = response._chunks;
chunks.set(id, createInitializedBufferChunk(response, buffer));
}
function resolveModule(
response: Response,
id: number,
model: UninitializedModel,
): void {
const chunks = response._chunks;
const chunk = chunks.get(id);
const clientReferenceMetadata: ClientReferenceMetadata = parseModel(
response,
model,
);
const clientReference = resolveClientReference<$FlowFixMe>(
response._bundlerConfig,
clientReferenceMetadata,
);
prepareDestinationForModule(
response._moduleLoading,
response._nonce,
clientReferenceMetadata,
);
const promise = preloadModule(clientReference);
if (promise) {
let blockedChunk: BlockedChunk<any>;
if (!chunk) {
blockedChunk = createBlockedChunk(response);
chunks.set(id, blockedChunk);
} else {
blockedChunk = (chunk: any);
blockedChunk.status = BLOCKED;
}
promise.then(
() => resolveModuleChunk(blockedChunk, clientReference),
error => triggerErrorOnChunk(blockedChunk, error),
);
} else {
if (!chunk) {
chunks.set(id, createResolvedModuleChunk(response, clientReference));
} else {
resolveModuleChunk(chunk, clientReference);
}
}
}
type ErrorWithDigest = Error & {digest?: string};
function resolveErrorProd(
response: Response,
id: number,
digest: string,
): void {
if (__DEV__) {
throw new Error(
'resolveErrorProd should never be called in development mode. Use resolveErrorDev instead. This is a bug in React.',
);
}
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: any).digest = digest;
const errorWithDigest: ErrorWithDigest = (error: any);
const chunks = response._chunks;
const chunk = chunks.get(id);
if (!chunk) {
chunks.set(id, createErrorChunk(response, errorWithDigest));
} else {
triggerErrorOnChunk(chunk, errorWithDigest);
}
}
function resolveErrorDev(
response: Response,
id: number,
digest: string,
message: string,
stack: string,
): void {
if (!__DEV__) {
throw new Error(
'resolveErrorDev should never be called in production mode. Use resolveErrorProd instead. This is a bug in React.',
);
}
const error = new Error(
message ||
'An error occurred in the Server Components render but no message was provided',
);
error.stack = stack;
(error: any).digest = digest;
const errorWithDigest: ErrorWithDigest = (error: any);
const chunks = response._chunks;
const chunk = chunks.get(id);
if (!chunk) {
chunks.set(id, createErrorChunk(response, errorWithDigest));
} else {
triggerErrorOnChunk(chunk, errorWithDigest);
}
}
function resolvePostponeProd(response: Response, id: number): void {
if (__DEV__) {
throw new Error(
'resolvePostponeProd should never be called in development mode. Use resolvePostponeDev instead. This is a bug in React.',
);
}
const error = new Error(
'A Server Component was postponed. The reason is omitted in production' +
' builds to avoid leaking sensitive details.',
);
const postponeInstance: Postpone = (error: any);
postponeInstance.$$typeof = REACT_POSTPONE_TYPE;
postponeInstance.stack = 'Error: ' + error.message;
const chunks = response._chunks;
const chunk = chunks.get(id);
if (!chunk) {
chunks.set(id, createErrorChunk(response, postponeInstance));
} else {
triggerErrorOnChunk(chunk, postponeInstance);
}
}
function resolvePostponeDev(
response: Response,
id: number,
reason: string,
stack: string,
): void {
if (!__DEV__) {
throw new Error(
'resolvePostponeDev should never be called in production mode. Use resolvePostponeProd instead. This is a bug in React.',
);
}
const error = new Error(reason || '');
const postponeInstance: Postpone = (error: any);
postponeInstance.$$typeof = REACT_POSTPONE_TYPE;
postponeInstance.stack = stack;
const chunks = response._chunks;
const chunk = chunks.get(id);
if (!chunk) {
chunks.set(id, createErrorChunk(response, postponeInstance));
} else {
triggerErrorOnChunk(chunk, postponeInstance);
}
}
function resolveHint<Code: HintCode>(
response: Response,
code: Code,
model: UninitializedModel,
): void {
const hintModel: HintModel<Code> = parseModel(response, model);
dispatchHint(code, hintModel);
}
function resolveDebugInfo(
response: Response,
id: number,
debugInfo: ReactComponentInfo | ReactAsyncInfo,
): void {
if (!__DEV__) {
throw new Error(
'resolveDebugInfo should never be called in production mode. This is a bug in React.',
);
}
const chunk = getChunk(response, id);
const chunkDebugInfo: ReactDebugInfo =
chunk._debugInfo || (chunk._debugInfo = []);
chunkDebugInfo.push(debugInfo);
}
function resolveConsoleEntry(
response: Response,
value: UninitializedModel,
): void {
if (!__DEV__) {
throw new Error(
'resolveConsoleEntry should never be called in production mode. This is a bug in React.',
);
}
const payload: [string, string, string, mixed] = parseModel(response, value);
const methodName = payload[0];
const env = payload[2];
const args = payload.slice(3);
printToConsole(methodName, args, env);
}
function mergeBuffer(
buffer: Array<Uint8Array>,
lastChunk: Uint8Array,
): Uint8Array {
const l = buffer.length;
let byteLength = lastChunk.length;
for (let i = 0; i < l; i++) {
byteLength += buffer[i].byteLength;
}
const result = new Uint8Array(byteLength);
let offset = 0;
for (let i = 0; i < l; i++) {
const chunk = buffer[i];
result.set(chunk, offset);
offset += chunk.byteLength;
}
result.set(lastChunk, offset);
return result;
}
function resolveTypedArray(
response: Response,
id: number,
buffer: Array<Uint8Array>,
lastChunk: Uint8Array,
constructor: any,
bytesPerElement: number,
): void {
const chunk =
buffer.length === 0 && lastChunk.byteOffset % bytesPerElement === 0
? lastChunk
: mergeBuffer(buffer, lastChunk);
const view: $ArrayBufferView = new constructor(
chunk.buffer,
chunk.byteOffset,
chunk.byteLength / bytesPerElement,
);
resolveBuffer(response, id, view);
}
function processFullRow(
response: Response,
id: number,
tag: number,
buffer: Array<Uint8Array>,
chunk: Uint8Array,
): void {
if (enableBinaryFlight) {
switch (tag) {
case 65 :
resolveBuffer(response, id, mergeBuffer(buffer, chunk).buffer);
return;
case 67 :
resolveTypedArray(response, id, buffer, chunk, Int8Array, 1);
return;
case 99 :
resolveBuffer(
response,
id,
buffer.length === 0 ? chunk : mergeBuffer(buffer, chunk),
);
return;
case 85 :
resolveTypedArray(response, id, buffer, chunk, Uint8ClampedArray, 1);
return;
case 83 :
resolveTypedArray(response, id, buffer, chunk, Int16Array, 2);
return;
case 115 :
resolveTypedArray(response, id, buffer, chunk, Uint16Array, 2);
return;
case 76 :
resolveTypedArray(response, id, buffer, chunk, Int32Array, 4);
return;
case 108 :
resolveTypedArray(response, id, buffer, chunk, Uint32Array, 4);
return;
case 70 :
resolveTypedArray(response, id, buffer, chunk, Float32Array, 4);
return;
case 100 :
resolveTypedArray(response, id, buffer, chunk, Float64Array, 8);
return;
case 78 :
resolveTypedArray(response, id, buffer, chunk, BigInt64Array, 8);
return;
case 109 :
resolveTypedArray(response, id, buffer, chunk, BigUint64Array, 8);
return;
case 86 :
resolveTypedArray(response, id, buffer, chunk, DataView, 1);
return;
}
}
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 : {
resolveModule(response, id, row);
return;
}
case 72 : {
const code: HintCode = (row[0]: any);
resolveHint(response, code, row.slice(1));
return;
}
case 69 : {
const errorInfo = JSON.parse(row);
if (__DEV__) {
resolveErrorDev(
response,
id,
errorInfo.digest,
errorInfo.message,
errorInfo.stack,
);
} else {
resolveErrorProd(response, id, errorInfo.digest);
}
return;
}
case 84 : {
resolveText(response, id, row);
return;
}
case 68 : {
if (__DEV__) {
const debugInfo = JSON.parse(row);
resolveDebugInfo(response, id, debugInfo);
return;
}
}
case 87 : {
if (__DEV__) {
resolveConsoleEntry(response, row);
return;
}
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 : {
if (enablePostpone) {
if (__DEV__) {
const postponeInfo = JSON.parse(row);
resolvePostponeDev(
response,
id,
postponeInfo.reason,
postponeInfo.stack,
);
} else {
resolvePostponeProd(response, id);
}
return;
}
}
default: {
resolveModel(response, id, row);
return;
}
}
}
export function processBinaryChunk(
response: Response,
chunk: Uint8Array,
): void {
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 ) {
rowState = ROW_TAG;
} else {
rowID = (rowID << 4) | (byte > 96 ? byte - 87 : byte - 48);
}
continue;
}
case ROW_TAG: {
const resolvedRowTag = chunk[i];
if (
resolvedRowTag === 84 ||
(enableBinaryFlight &&
(resolvedRowTag === 65 ||
resolvedRowTag === 67 ||
resolvedRowTag === 99 ||
resolvedRowTag === 85 ||
resolvedRowTag === 83 ||
resolvedRowTag === 115 ||
resolvedRowTag === 76 ||
resolvedRowTag === 108 ||
resolvedRowTag === 70 ||
resolvedRowTag === 100 ||
resolvedRowTag === 78 ||
resolvedRowTag === 109 ||
resolvedRowTag === 86))
) {
rowTag = resolvedRowTag;
rowState = ROW_LENGTH;
i++;
} else if (resolvedRowTag > 64 && resolvedRowTag < 91 ) {
rowTag = resolvedRowTag;
rowState = ROW_CHUNK_BY_NEWLINE;
i++;
} else {
rowTag = 0;
rowState = ROW_CHUNK_BY_NEWLINE;
}
continue;
}
case ROW_LENGTH: {
const byte = chunk[i++];
if (byte === 44 ) {
rowState = ROW_CHUNK_BY_LENGTH;
} else {
rowLength = (rowLength << 4) | (byte > 96 ? byte - 87 : byte - 48);
}
continue;
}
case ROW_CHUNK_BY_NEWLINE: {
lastIdx = chunk.indexOf(10 , i);
break;
}
case ROW_CHUNK_BY_LENGTH: {
lastIdx = i + rowLength;
if (lastIdx > chunk.length) {
lastIdx = -1;
}
break;
}
}
const offset = chunk.byteOffset + i;
if (lastIdx > -1) {
const length = lastIdx - i;
const lastChunk = new Uint8Array(chunk.buffer, offset, length);
processFullRow(response, rowID, rowTag, buffer, lastChunk);
i = lastIdx;
if (rowState === ROW_CHUNK_BY_NEWLINE) {
i++;
}
rowState = ROW_ID;
rowTag = 0;
rowID = 0;
rowLength = 0;
buffer.length = 0;
} else {
const length = chunk.byteLength - i;
const remainingSlice = new Uint8Array(chunk.buffer, offset, length);
buffer.push(remainingSlice);
rowLength -= remainingSlice.byteLength;
break;
}
}
response._rowState = rowState;
response._rowID = rowID;
response._rowTag = rowTag;
response._rowLength = rowLength;
}
function parseModel<T>(response: Response, json: UninitializedModel): T {
return JSON.parse(json, response._fromJSON);
}
function createFromJSONCallback(response: Response) {
return function (key: string, value: JSONValue) {
if (typeof value === 'string') {
return parseModelString(response, this, key, value);
}
if (typeof value === 'object' && value !== null) {
return parseModelTuple(response, value);
}
return value;
};
}
export function close(response: Response): void {
reportGlobalError(response, new Error('Connection closed.'));
}