1/**2 * Copyright (c) Meta Platforms, Inc. and affiliates.3 *4 * This source code is licensed under the MIT license found in the5 * LICENSE file in the root directory of this source tree.6 *7 * @flow8 */910import type {11 JSONValue,12 Thenable,13 ReactDebugInfo,14 ReactDebugInfoEntry,15 ReactComponentInfo,16 ReactAsyncInfo,17 ReactIOInfo,18 ReactStackTrace,19 ReactFunctionLocation,20 ReactErrorInfoDev,21} from 'shared/ReactTypes';22import type {LazyComponent} from 'react/src/ReactLazy';2324import type {25 ClientReference,26 ClientReferenceMetadata,27 ServerConsumerModuleMap,28 ServerManifest,29 StringDecoder,30 ModuleLoading,31} from './ReactFlightClientConfig';3233import type {34 HintCode,35 HintModel,36} from 'react-server/src/ReactFlightServerConfig';3738import type {39 CallServerCallback,40 EncodeFormActionCallback,41} from './ReactFlightReplyClient';4243import type {TemporaryReferenceSet} from './ReactFlightTemporaryReferences';4445import {46 enableProfilerTimer,47 enableComponentPerformanceTrack,48 enableAsyncDebugInfo,49 enableFlightWeakThenables,50} from 'shared/ReactFeatureFlags';5152import {53 resolveClientReference,54 resolveServerReference,55 preloadModule,56 requireModule,57 getModuleDebugInfo,58 dispatchHint,59 readPartialStringChunk,60 readFinalStringChunk,61 createStringDecoder,62 prepareDestinationForModule,63 bindToConsole,64 rendererVersion,65 rendererPackageName,66 checkEvalAvailabilityOnceDev,67} from './ReactFlightClientConfig';6869import {70 createBoundServerReference,71 registerBoundServerReference,72} from './ReactFlightReplyClient';7374import {readTemporaryReference} from './ReactFlightTemporaryReferences';7576import {77 markAllTracksInOrder,78 logComponentRender,79 logDedupedComponentRender,80 logComponentAborted,81 logComponentErrored,82 logIOInfo,83 logIOInfoErrored,84 logComponentAwait,85 logComponentAwaitAborted,86 logComponentAwaitErrored,87} from './ReactFlightPerformanceTrack';8889import {90 REACT_LAZY_TYPE,91 REACT_ELEMENT_TYPE,92 ASYNC_ITERATOR,93 REACT_FRAGMENT_TYPE,94} from 'shared/ReactSymbols';9596import getComponentNameFromType from 'shared/getComponentNameFromType';9798import {getOwnerStackByComponentInfoInDev} from 'shared/ReactComponentInfoStack';99100import hasOwnProperty from 'shared/hasOwnProperty';101102import getPrototypeOf from 'shared/getPrototypeOf';103104import {injectInternals} from './ReactFlightClientDevToolsHook';105106import {OMITTED_PROP_ERROR} from 'shared/ReactFlightPropertyAccess';107108import ReactVersion from 'shared/ReactVersion';109110import isArray from 'shared/isArray';111112import * as React from 'react';113114import type {SharedStateServer} from 'react/src/ReactSharedInternalsServer';115import type {SharedStateClient} from 'react/src/ReactSharedInternalsClient';116117// TODO: This is an unfortunate hack. We shouldn't feature detect the internals118// like this. It's just that for now we support the same build of the Flight119// client both in the RSC environment, in the SSR environments as well as the120// browser client. We should probably have a separate RSC build. This is DEV121// only though.122const ReactSharedInteralsServer: void | SharedStateServer = (React as any)123 .__SERVER_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;124const ReactSharedInternals: SharedStateServer | SharedStateClient =125 React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE ||126 ReactSharedInteralsServer;127128export type {CallServerCallback, EncodeFormActionCallback};129130interface FlightStreamController {131 enqueueValue(value: any): void;132 enqueueModel(json: UninitializedModel): void;133 close(json: UninitializedModel): void;134 error(error: Error): void;135}136137type UninitializedModel = string;138139type ProfilingResult = {140 track: number,141 endTime: number,142 component: null | ReactComponentInfo,143};144145const ROW_ID = 0;146const ROW_TAG = 1;147const ROW_LENGTH = 2;148const ROW_CHUNK_BY_NEWLINE = 3;149const ROW_CHUNK_BY_LENGTH = 4;150151type RowParserState = 0 | 1 | 2 | 3 | 4;152153const PENDING = 'pending';154// A weak Promise reference. Behaves like PENDING except that when the stream155// closes it transitions to HALTED instead of erroring, because the server156// may intentionally never emit it. Only used when enableFlightWeakThenables157// is on.158const PENDING_WEAK = 'pending_weak';159const BLOCKED = 'blocked';160const RESOLVED_MODEL = 'resolved_model';161const RESOLVED_MODULE = 'resolved_module';162const INITIALIZED = 'fulfilled';163const ERRORED = 'rejected';164// Means it never resolves, even when the connection closes. The shared165// terminal state of a weak chunk that didn't settle before close, of any166// pending chunk at close when partial streams are allowed, and of DEV-only167// debug halts.168const HALTED = 'halted';169170const __PROTO__ = '__proto__';171172const ObjectPrototype = Object.prototype;173const ArrayPrototype = Array.prototype;174175type PendingChunk<T> = {176 status: 'pending',177 value: null | Array<InitializationReference | (T => mixed)>,178 reason: null | Array<InitializationReference | (mixed => mixed)>,179 _children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only180 _debugChunk: null | SomeChunk<ReactDebugInfoEntry>, // DEV-only181 _debugInfo: ReactDebugInfo, // DEV-only182 then(resolve: (T) => mixed, reject?: (mixed) => mixed): void,183};184type PendingWeakChunk<T> = {185 status: 'pending_weak',186 value: null | Array<InitializationReference | (T => mixed)>,187 reason: null | Array<InitializationReference | (mixed => mixed)>,188 _children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only189 _debugChunk: null | SomeChunk<ReactDebugInfoEntry>, // DEV-only190 _debugInfo: ReactDebugInfo, // DEV-only191 then(resolve: (T) => mixed, reject?: (mixed) => mixed): void,192};193type BlockedChunk<T> = {194 status: 'blocked',195 value: null | Array<InitializationReference | (T => mixed)>,196 reason: null | Array<InitializationReference | (mixed => mixed)>,197 _children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only198 _debugChunk: null, // DEV-only199 _debugInfo: ReactDebugInfo, // DEV-only200 then(resolve: (T) => mixed, reject?: (mixed) => mixed): void,201};202type ResolvedModelChunk<T> = {203 status: 'resolved_model',204 value: UninitializedModel,205 reason: Response,206 _children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only207 _debugChunk: null | SomeChunk<ReactDebugInfoEntry>, // DEV-only208 _debugInfo: ReactDebugInfo, // DEV-only209 then(resolve: (T) => mixed, reject?: (mixed) => mixed): void,210};211type ResolvedModuleChunk<T> = {212 status: 'resolved_module',213 value: ClientReference<T>,214 reason: null,215 _children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only216 _debugChunk: null, // DEV-only217 _debugInfo: ReactDebugInfo, // DEV-only218 then(resolve: (T) => mixed, reject?: (mixed) => mixed): void,219};220type InitializedChunk<T> = {221 status: 'fulfilled',222 value: T,223 reason: null | FlightStreamController,224 _children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only225 _debugChunk: null, // DEV-only226 _debugInfo: ReactDebugInfo, // DEV-only227 then(resolve: (T) => mixed, reject?: (mixed) => mixed): void,228};229type InitializedStreamChunk<230 T: ReadableStream | $AsyncIterable<any, any, void>,231> = {232 status: 'fulfilled',233 value: T,234 reason: FlightStreamController,235 _children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only236 _debugChunk: null, // DEV-only237 _debugInfo: ReactDebugInfo, // DEV-only238 then(resolve: (ReadableStream) => mixed, reject?: (mixed) => mixed): void,239};240type ErroredChunk<T> = {241 status: 'rejected',242 value: null,243 reason: mixed,244 _children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only245 _debugChunk: null, // DEV-only246 _debugInfo: ReactDebugInfo, // DEV-only247 then(resolve: (T) => mixed, reject?: (mixed) => mixed): void,248};249type HaltedChunk<T> = {250 status: 'halted',251 value: null,252 reason: null,253 _children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only254 _debugChunk: null, // DEV-only255 _debugInfo: ReactDebugInfo, // DEV-only256 then(resolve: (T) => mixed, reject?: (mixed) => mixed): void,257};258type SomeChunk<T> =259 | PendingChunk<T>260 | PendingWeakChunk<T>261 | BlockedChunk<T>262 | ResolvedModelChunk<T>263 | ResolvedModuleChunk<T>264 | InitializedChunk<T>265 | ErroredChunk<T>266 | HaltedChunk<T>;267268// $FlowFixMe[missing-this-annot]269function ReactPromise(status: any, value: any, reason: any) {270 this.status = status;271 this.value = value;272 this.reason = reason;273 if (enableProfilerTimer && enableComponentPerformanceTrack) {274 this._children = [];275 }276 if (__DEV__) {277 this._debugChunk = null;278 this._debugInfo = [];279 }280}281// We subclass Promise.prototype so that we get other methods like .catch282ReactPromise.prototype = Object.create(Promise.prototype) as any;283// TODO: This doesn't return a new Promise chain unlike the real .then284function reactPromiseThen<T>(285 this: SomeChunk<T>,286 resolve: (value: T) => mixed,287 reject?: (reason: mixed) => mixed,288) {289 const chunk: SomeChunk<T> = this;290 // If we have resolved content, we try to initialize it first which291 // might put us back into one of the other states.292 switch (chunk.status) {293 case RESOLVED_MODEL:294 initializeModelChunk(chunk);295 break;296 case RESOLVED_MODULE:297 initializeModuleChunk(chunk);298 break;299 }300 if (__DEV__ && enableAsyncDebugInfo) {301 // Because only native Promises get picked up when we're awaiting we need to wrap302 // this in a native Promise in DEV. This means that these callbacks are no longer sync303 // but the lazy initialization is still sync and the .value can be inspected after,304 // allowing it to be read synchronously anyway.305 const resolveCallback = resolve;306 const rejectCallback = reject;307 const wrapperPromise: Promise<T> = new Promise((res, rej) => {308 resolve = value => {309 // $FlowFixMe[prop-missing]310 wrapperPromise._debugInfo = this._debugInfo;311 res(value);312 };313 reject = reason => {314 // $FlowFixMe[prop-missing]315 wrapperPromise._debugInfo = this._debugInfo;316 rej(reason);317 };318 });319 wrapperPromise.then(resolveCallback, rejectCallback);320 }321 // The status might have changed after initialization.322 switch (chunk.status) {323 case INITIALIZED:324 if (typeof resolve === 'function') {325 resolve(chunk.value);326 }327 break;328 case PENDING:329 case PENDING_WEAK:330 case BLOCKED:331 if (typeof resolve === 'function') {332 if (chunk.value === null) {333 chunk.value = [] as Array<InitializationReference | (T => mixed)>;334 }335 chunk.value.push(resolve);336 }337 if (typeof reject === 'function') {338 if (chunk.reason === null) {339 chunk.reason = [] as Array<340 InitializationReference | (mixed => mixed),341 >;342 }343 chunk.reason.push(reject);344 }345 break;346 case HALTED: {347 break;348 }349 default:350 if (typeof reject === 'function') {351 reject(chunk.reason);352 }353 break;354 }355}356// The shadowing `then` must be defined with `Object.defineProperty` instead of357// assignment. Assignment would throw when `Promise.prototype` is frozen (e.g.358// by SES lockdown) because assigning over an inherited non-writable property359// is rejected.360Object.defineProperty(ReactPromise.prototype, 'then', {361 writable: true,362 enumerable: true,363 configurable: true,364 value: reactPromiseThen,365});366367export type FindSourceMapURLCallback = (368 fileName: string,369 environmentName: string,370) => null | string;371372export type DebugChannelCallback = (message: string) => void;373374export type DebugChannel = {375 hasReadable: boolean,376 callback: DebugChannelCallback | null,377};378379type Response = {380 _bundlerConfig: ServerConsumerModuleMap,381 _serverReferenceConfig: null | ServerManifest,382 _moduleLoading: ModuleLoading,383 _callServer: CallServerCallback,384 _encodeFormAction: void | EncodeFormActionCallback,385 _nonce: ?string,386 _chunks: Map<number, SomeChunk<any>>,387 _stringDecoder: StringDecoder,388 _closed: boolean,389 _closedReason: mixed,390 _allowPartialStream: boolean,391 _tempRefs: void | TemporaryReferenceSet, // the set temporary references can be resolved from392 _timeOrigin: number, // Profiling-only393 _pendingInitialRender: null | TimeoutID, // Profiling-only,394 _pendingChunks: number, // DEV-only395 _weakResponse: WeakResponse, // DEV-only396 _debugRootOwner?: null | ReactComponentInfo, // DEV-only397 _debugRootStack?: null | Error, // DEV-only398 _debugRootTask?: null | ConsoleTask, // DEV-only399 _debugStartTime: number, // DEV-only400 _debugEndTime: null | number, // DEV-only401 _debugIOStarted: boolean, // DEV-only402 _debugFindSourceMapURL?: void | FindSourceMapURLCallback, // DEV-only403 _debugChannel?: void | DebugChannel, // DEV-only404 _blockedConsole?: null | SomeChunk<ConsoleEntry>, // DEV-only405 _replayConsole: boolean, // DEV-only406 _rootEnvironmentName: string, // DEV-only, the requested environment name.407};408409// This indirection exists only to clean up DebugChannel when all Lazy References are GC:ed.410// Therefore we only use the indirection in DEV.411type WeakResponse = {412 weak: WeakRef<Response>,413 response: null | Response, // This is null when there are no pending chunks.414};415416export type {WeakResponse as Response};417418function hasGCedResponse(weakResponse: WeakResponse): boolean {419 return __DEV__ && weakResponse.weak.deref() === undefined;420}421422function unwrapWeakResponse(weakResponse: WeakResponse): Response {423 if (__DEV__) {424 const response = weakResponse.weak.deref();425 if (response === undefined) {426 // eslint-disable-next-line react-internal/prod-error-codes427 throw new Error(428 'We did not expect to receive new data after GC:ing the response.',429 );430 }431 return response;432 } else {433 return weakResponse as any; // In prod we just use the real Response directly.434 }435}436437function getWeakResponse(response: Response): WeakResponse {438 if (__DEV__) {439 return response._weakResponse;440 } else {441 return response as any; // In prod we just use the real Response directly.442 }443}444445function closeDebugChannel(debugChannel: DebugChannel): void {446 if (debugChannel.callback) {447 debugChannel.callback('');448 }449}450451// If FinalizationRegistry doesn't exist, we cannot use the debugChannel.452const debugChannelRegistry =453 __DEV__ && typeof FinalizationRegistry === 'function'454 ? new FinalizationRegistry(closeDebugChannel)455 : null;456457function readChunk<T>(chunk: SomeChunk<T>): T {458 // If we have resolved content, we try to initialize it first which459 // might put us back into one of the other states.460 switch (chunk.status) {461 case RESOLVED_MODEL:462 initializeModelChunk(chunk);463 break;464 case RESOLVED_MODULE:465 initializeModuleChunk(chunk);466 break;467 }468 // The status might have changed after initialization.469 switch (chunk.status) {470 case INITIALIZED:471 return chunk.value;472 case PENDING:473 case PENDING_WEAK:474 case BLOCKED:475 case HALTED:476 // eslint-disable-next-line no-throw-literal477 throw chunk as any as Thenable<T>;478 default:479 throw chunk.reason;480 }481}482483export function getRoot<T>(weakResponse: WeakResponse): Thenable<T> {484 const response = unwrapWeakResponse(weakResponse);485 const chunk = getChunk(response, 0);486 return chunk as any;487}488489function createPendingChunk<T>(response: Response): PendingChunk<T> {490 if (__DEV__) {491 // Retain a strong reference to the Response while we wait for the result.492 if (response._pendingChunks++ === 0) {493 response._weakResponse.response = response;494 if (response._pendingInitialRender !== null) {495 clearTimeout(response._pendingInitialRender);496 response._pendingInitialRender = null;497 }498 }499 }500 // $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors501 return new ReactPromise(PENDING, null, null);502}503504function createPendingWeakChunk<T>(response: Response): PendingWeakChunk<T> {505 // Unlike a regular pending chunk, a weak chunk may never settle, so it506 // doesn't retain a strong reference to the Response while it waits.507 // $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors508 return new ReactPromise(PENDING_WEAK, null, null);509}510511function releasePendingChunk(response: Response, chunk: SomeChunk<any>): void {512 if (__DEV__ && chunk.status === PENDING) {513 if (--response._pendingChunks === 0) {514 // We're no longer waiting for any more chunks. We can release the strong reference515 // to the response. We'll regain it if we ask for any more data later on.516 response._weakResponse.response = null;517 // Wait a short period to see if any more chunks get asked for. E.g. by a React render.518 // These chunks might discover more pending chunks.519 // If we don't ask for more then we assume that those chunks weren't blocking initial520 // render and are excluded from the performance track.521 response._pendingInitialRender = setTimeout(522 flushInitialRenderPerformance.bind(null, response),523 100,524 );525 }526 }527}528529function createHaltedChunk<T>(response: Response): HaltedChunk<T> {530 // $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors531 return new ReactPromise(HALTED, null, null);532}533534// Transition a chunk to HALTED: it will never resolve, even when the535// connection closes. Clears any listeners to release their closures. Future536// .then() calls on HALTED chunks are no-ops.537function haltChunk<T>(response: Response, chunk: SomeChunk<T>): void {538 releasePendingChunk(response, chunk);539 const haltedChunk: HaltedChunk<T> = chunk as any;540 haltedChunk.status = HALTED;541 haltedChunk.value = null;542 haltedChunk.reason = null;543}544545function createBlockedChunk<T>(response: Response): BlockedChunk<T> {546 // $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors547 return new ReactPromise(BLOCKED, null, null);548}549550function createErrorChunk<T>(551 response: Response,552 error: mixed,553): ErroredChunk<T> {554 // $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors555 return new ReactPromise(ERRORED, null, error);556}557558function filterDebugInfo(559 response: Response,560 value: {_debugInfo: ReactDebugInfo, ...},561) {562 if (response._debugEndTime === null) {563 // No end time was defined, so we keep all debug info entries.564 return;565 }566567 // Remove any debug info entries after the defined end time. For async info568 // that means we're including anything that was awaited before the end time,569 // but it doesn't need to be resolved before the end time.570 const relativeEndTime =571 response._debugEndTime -572 // $FlowFixMe[prop-missing]573 performance.timeOrigin;574 const debugInfo = [];575 for (let i = 0; i < value._debugInfo.length; i++) {576 const info = value._debugInfo[i];577 if (typeof info.time === 'number' && info.time > relativeEndTime) {578 break;579 }580 debugInfo.push(info);581 }582 value._debugInfo = debugInfo;583}584585function pruneDebugInfoAfterError(586 response: Response,587 chunk: ErroredChunk<any>,588): void {589 if (response._debugEndTime === null) {590 return;591 }592593 const relativeEndTime =594 response._debugEndTime -595 // $FlowFixMe[prop-missing]596 performance.timeOrigin;597 const debugInfo = chunk._debugInfo;598 for (let i = 0; i < debugInfo.length; i++) {599 const info = debugInfo[i];600 if (typeof info.time === 'number' && info.time > relativeEndTime) {601 // This array may already be attached to the Lazy suspended in Fizz.602 debugInfo.length = i;603 return;604 }605 }606}607608function moveDebugInfoFromChunkToInnerValue<T>(609 chunk: InitializedChunk<T> | InitializedStreamChunk<any>,610 value: T,611): void {612 // Remove the debug info from the initialized chunk, and add it to the inner613 // value instead. This can be a React element, an array, or an uninitialized614 // Lazy.615 const resolvedValue = resolveLazy(value);616 if (617 typeof resolvedValue === 'object' &&618 resolvedValue !== null &&619 (isArray(resolvedValue) ||620 typeof resolvedValue[ASYNC_ITERATOR] === 'function' ||621 resolvedValue.$$typeof === REACT_ELEMENT_TYPE ||622 resolvedValue.$$typeof === REACT_LAZY_TYPE)623 ) {624 const debugInfo = chunk._debugInfo.splice(0);625 if (isArray(resolvedValue._debugInfo)) {626 // $FlowFixMe[method-unbinding]627 resolvedValue._debugInfo.unshift.apply(628 resolvedValue._debugInfo,629 debugInfo,630 );631 } else if (!Object.isFrozen(resolvedValue)) {632 Object.defineProperty(resolvedValue as any, '_debugInfo', {633 configurable: false,634 enumerable: false,635 writable: true,636 value: debugInfo,637 });638 }639 // TODO: If the resolved value is a frozen element (e.g. a client-created640 // element from a temporary reference, or a JSX element exported as a client641 // reference), server debug info is currently dropped because the element642 // can't be mutated. We should probably clone the element so each rendering643 // context gets its own mutable copy with the correct debug info.644 }645}646647function processChunkDebugInfo<T>(648 response: Response,649 chunk: InitializedChunk<T> | InitializedStreamChunk<any>,650 value: T,651): void {652 filterDebugInfo(response, chunk);653 moveDebugInfoFromChunkToInnerValue(chunk, value);654}655656function wakeChunk<T>(657 response: Response,658 listeners: Array<InitializationReference | (T => mixed)>,659 value: T,660 chunk: InitializedChunk<T>,661): void {662 for (let i = 0; i < listeners.length; i++) {663 const listener = listeners[i];664 if (typeof listener === 'function') {665 listener(value);666 } else {667 fulfillReference(response, listener, value, chunk);668 }669 }670671 if (__DEV__) {672 processChunkDebugInfo(response, chunk, value);673 }674}675676function rejectChunk(677 response: Response,678 listeners: Array<InitializationReference | (mixed => mixed)>,679 error: mixed,680): void {681 for (let i = 0; i < listeners.length; i++) {682 const listener = listeners[i];683 if (typeof listener === 'function') {684 listener(error);685 } else {686 rejectReference(response, listener.handler, error);687 }688 }689}690691function resolveBlockedCycle<T>(692 resolvedChunk: SomeChunk<T>,693 reference: InitializationReference,694): null | InitializationHandler {695 const referencedChunk = reference.handler.chunk;696 if (referencedChunk === null) {697 return null;698 }699 if (referencedChunk === resolvedChunk) {700 // We found the cycle. We can resolve the blocked cycle now.701 return reference.handler;702 }703 const resolveListeners = referencedChunk.value;704 if (resolveListeners !== null) {705 for (let i = 0; i < resolveListeners.length; i++) {706 const listener = resolveListeners[i];707 if (typeof listener !== 'function') {708 const foundHandler = resolveBlockedCycle(resolvedChunk, listener);709 if (foundHandler !== null) {710 return foundHandler;711 }712 }713 }714 }715 return null;716}717718function wakeChunkIfInitialized<T>(719 response: Response,720 chunk: SomeChunk<T>,721 resolveListeners: Array<InitializationReference | (T => mixed)>,722 rejectListeners: null | Array<InitializationReference | (mixed => mixed)>,723): void {724 switch (chunk.status) {725 case INITIALIZED:726 wakeChunk(response, resolveListeners, chunk.value, chunk);727 break;728 case BLOCKED:729 // It is possible that we're blocked on our own chunk if it's a cycle.730 // Before adding back the listeners to the chunk, let's check if it would731 // result in a cycle.732 for (let i = 0; i < resolveListeners.length; i++) {733 const listener = resolveListeners[i];734 if (typeof listener !== 'function') {735 const reference: InitializationReference = listener;736 const cyclicHandler = resolveBlockedCycle(chunk, reference);737 if (cyclicHandler !== null) {738 // This reference points back to this chunk. We can resolve the cycle by739 // using the value from that handler.740 fulfillReference(response, reference, cyclicHandler.value, chunk);741 resolveListeners.splice(i, 1);742 i--;743 if (rejectListeners !== null) {744 const rejectionIdx = rejectListeners.indexOf(reference);745 if (rejectionIdx !== -1) {746 rejectListeners.splice(rejectionIdx, 1);747 }748 }749 // The status might have changed after fulfilling the reference.750 switch ((chunk as SomeChunk<T>).status) {751 case INITIALIZED:752 const initializedChunk: InitializedChunk<T> = chunk as any;753 wakeChunk(754 response,755 resolveListeners,756 initializedChunk.value,757 initializedChunk,758 );759 return;760 case ERRORED:761 if (rejectListeners !== null) {762 rejectChunk(response, rejectListeners, chunk.reason);763 }764 return;765 }766 }767 }768 }769 // Fallthrough770 case PENDING:771 if (chunk.value) {772 for (let i = 0; i < resolveListeners.length; i++) {773 chunk.value.push(resolveListeners[i]);774 }775 } else {776 chunk.value = resolveListeners;777 }778779 if (chunk.reason) {780 if (rejectListeners) {781 for (let i = 0; i < rejectListeners.length; i++) {782 chunk.reason.push(rejectListeners[i]);783 }784 }785 } else {786 chunk.reason = rejectListeners;787 }788789 break;790 case ERRORED:791 if (rejectListeners) {792 rejectChunk(response, rejectListeners, chunk.reason);793 }794 break;795 }796}797798function triggerErrorOnChunk<T>(799 response: Response,800 chunk: SomeChunk<T>,801 error: mixed,802): void {803 if (804 chunk.status !== PENDING &&805 chunk.status !== PENDING_WEAK &&806 chunk.status !== BLOCKED807 ) {808 // If we get more data to an already resolved ID, we assume that it's809 // a stream chunk since any other row shouldn't have more than one entry.810 const streamChunk: InitializedStreamChunk<any> = chunk as any;811 const controller = streamChunk.reason;812 // $FlowFixMe[incompatible-type]: The error method should accept mixed.813 controller.error(error);814 return;815 }816 releasePendingChunk(response, chunk);817 const listeners = chunk.reason;818819 if (__DEV__ && (chunk.status === PENDING || chunk.status === PENDING_WEAK)) {820 // Lazily initialize any debug info and block the initializing chunk on any unresolved entries.821 if (chunk._debugChunk != null) {822 const prevHandler = initializingHandler;823 const prevChunk = initializingChunk;824 initializingHandler = null;825 const cyclicChunk: BlockedChunk<T> = chunk as any;826 cyclicChunk.status = BLOCKED;827 cyclicChunk.value = null;828 cyclicChunk.reason = null;829 if ((enableProfilerTimer && enableComponentPerformanceTrack) || __DEV__) {830 initializingChunk = cyclicChunk;831 }832 try {833 initializeDebugChunk(response, chunk);834 if (initializingHandler !== null) {835 if (initializingHandler.errored) {836 // Ignore error parsing debug info, we'll report the original error instead.837 } else if (initializingHandler.deps > 0) {838 // TODO: Block the resolution of the error until all the debug info has loaded.839 // We currently don't have a way to throw an error after all dependencies have840 // loaded because we currently treat errors as immediately cancelling the handler.841 }842 }843 } finally {844 initializingHandler = prevHandler;845 initializingChunk = prevChunk;846 }847 }848 }849850 const erroredChunk: ErroredChunk<T> = chunk as any;851 erroredChunk.status = ERRORED;852 erroredChunk.reason = error;853 if (__DEV__) {854 pruneDebugInfoAfterError(response, erroredChunk);855 }856 if (listeners !== null) {857 rejectChunk(response, listeners, error);858 }859}860861function createResolvedModelChunk<T>(862 response: Response,863 value: UninitializedModel,864): ResolvedModelChunk<T> {865 // $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors866 return new ReactPromise(RESOLVED_MODEL, value, response);867}868869function createResolvedModuleChunk<T>(870 response: Response,871 value: ClientReference<T>,872): ResolvedModuleChunk<T> {873 // $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors874 return new ReactPromise(RESOLVED_MODULE, value, null);875}876877function createInitializedTextChunk(878 response: Response,879 value: string,880): InitializedChunk<string> {881 // $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors882 return new ReactPromise(INITIALIZED, value, null);883}884885function createInitializedBufferChunk(886 response: Response,887 value: $ArrayBufferView | ArrayBuffer,888): InitializedChunk<Uint8Array> {889 // $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors890 return new ReactPromise(INITIALIZED, value, null);891}892893function createInitializedIteratorResultChunk<T>(894 response: Response,895 value: T,896 done: boolean,897): InitializedChunk<IteratorResult<T, T>> {898 // $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors899 return new ReactPromise(INITIALIZED, {done: done, value: value}, null);900}901902function createInitializedStreamChunk<903 T: ReadableStream | $AsyncIterable<any, any, void>,904>(905 response: Response,906 value: T,907 controller: FlightStreamController,908): InitializedChunk<T> {909 if (__DEV__) {910 // Retain a strong reference to the Response while we wait for chunks.911 if (response._pendingChunks++ === 0) {912 response._weakResponse.response = response;913 }914 }915 // We use the reason field to stash the controller since we already have that916 // field. It's a bit of a hack but efficient.917 // $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors918 return new ReactPromise(INITIALIZED, value, controller);919}920921function createResolvedIteratorResultChunk<T>(922 response: Response,923 value: UninitializedModel,924 done: boolean,925): ResolvedModelChunk<IteratorResult<T, T>> {926 // To reuse code as much code as possible we add the wrapper element as part of the JSON.927 const iteratorResultJSON =928 (done ? '{"done":true,"value":' : '{"done":false,"value":') + value + '}';929 // $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors930 return new ReactPromise(RESOLVED_MODEL, iteratorResultJSON, response);931}932933function resolveIteratorResultChunk<T>(934 response: Response,935 chunk: SomeChunk<IteratorResult<T, T>>,936 value: UninitializedModel,937 done: boolean,938): void {939 // To reuse code as much code as possible we add the wrapper element as part of the JSON.940 const iteratorResultJSON =941 (done ? '{"done":true,"value":' : '{"done":false,"value":') + value + '}';942 resolveModelChunk(response, chunk, iteratorResultJSON);943}944945function resolveModelChunk<T>(946 response: Response,947 chunk: SomeChunk<T>,948 value: UninitializedModel,949): void {950 if (chunk.status !== PENDING && chunk.status !== PENDING_WEAK) {951 // If we get more data to an already resolved ID, we assume that it's952 // a stream chunk since any other row shouldn't have more than one entry.953 const streamChunk: InitializedStreamChunk<any> = chunk as any;954 const controller = streamChunk.reason;955 controller.enqueueModel(value);956 return;957 }958 releasePendingChunk(response, chunk);959 const resolveListeners = chunk.value;960 const rejectListeners = chunk.reason;961 const resolvedChunk: ResolvedModelChunk<T> = chunk as any;962 resolvedChunk.status = RESOLVED_MODEL;963 resolvedChunk.value = value;964 resolvedChunk.reason = response;965 if (resolveListeners !== null) {966 // This is unfortunate that we're reading this eagerly if967 // we already have listeners attached since they might no968 // longer be rendered or might not be the highest pri.969 initializeModelChunk(resolvedChunk);970 // The status might have changed after initialization.971 wakeChunkIfInitialized(response, chunk, resolveListeners, rejectListeners);972 }973}974975function resolveModuleChunk<T>(976 response: Response,977 chunk: SomeChunk<T>,978 value: ClientReference<T>,979): void {980 if (981 chunk.status !== PENDING &&982 chunk.status !== PENDING_WEAK &&983 chunk.status !== BLOCKED984 ) {985 // We already resolved. We didn't expect to see this.986 return;987 }988 releasePendingChunk(response, chunk);989 const resolveListeners = chunk.value;990 const rejectListeners = chunk.reason;991 const resolvedChunk: ResolvedModuleChunk<T> = chunk as any;992 resolvedChunk.status = RESOLVED_MODULE;993 resolvedChunk.value = value;994 resolvedChunk.reason = null;995 if (__DEV__) {996 const debugInfo = getModuleDebugInfo(value);997 if (debugInfo !== null) {998 // Add to the live set if it was already initialized.999 // $FlowFixMe[method-unbinding]1000 resolvedChunk._debugInfo.push.apply(resolvedChunk._debugInfo, debugInfo);1001 }1002 }1003 if (resolveListeners !== null) {1004 initializeModuleChunk(resolvedChunk);1005 wakeChunkIfInitialized(response, chunk, resolveListeners, rejectListeners);1006 }1007}10081009type InitializationReference = {1010 handler: InitializationHandler,1011 parentObject: Object,1012 key: string,1013 map: (1014 response: Response,1015 model: any,1016 parentObject: Object,1017 key: string,1018 ) => any,1019 path: Array<string>,1020 isDebug?: boolean, // DEV-only1021};1022type InitializationHandler = {1023 parent: null | InitializationHandler,1024 chunk: null | BlockedChunk<any>,1025 value: any,1026 reason: any,1027 deps: number,1028 errored: boolean,1029};1030let initializingHandler: null | InitializationHandler = null;1031let initializingChunk: null | BlockedChunk<any> = null;1032let isInitializingDebugInfo: boolean = false;10331034function initializeDebugChunk(1035 response: Response,1036 chunk: ResolvedModelChunk<any> | PendingChunk<any> | PendingWeakChunk<any>,1037): void {1038 const debugChunk = chunk._debugChunk;1039 if (debugChunk !== null) {1040 const debugInfo = chunk._debugInfo;1041 const prevIsInitializingDebugInfo = isInitializingDebugInfo;1042 isInitializingDebugInfo = true;1043 try {1044 if (debugChunk.status === RESOLVED_MODEL) {1045 // Find the index of this debug info by walking the linked list.1046 let idx = debugInfo.length;1047 let c = debugChunk._debugChunk;1048 while (c !== null) {1049 if (c.status !== INITIALIZED) {1050 idx++;1051 }1052 c = c._debugChunk;1053 }1054 // Initializing the model for the first time.1055 initializeModelChunk(debugChunk);1056 const initializedChunk = debugChunk as any as SomeChunk<any>;1057 switch (initializedChunk.status) {1058 case INITIALIZED: {1059 debugInfo[idx] = initializeDebugInfo(1060 response,1061 initializedChunk.value,1062 );1063 break;1064 }1065 case BLOCKED:1066 case PENDING:1067 case PENDING_WEAK: {1068 waitForReference(1069 initializedChunk,1070 debugInfo,1071 '' + idx,1072 response,1073 initializeDebugInfo,1074 [''], // path1075 true,1076 );1077 break;1078 }1079 default:1080 throw initializedChunk.reason;1081 }1082 } else {1083 switch (debugChunk.status) {1084 case INITIALIZED: {1085 // Already done.1086 break;1087 }1088 case BLOCKED:1089 case PENDING:1090 case PENDING_WEAK: {1091 // Signal to the caller that we need to wait.1092 waitForReference(1093 debugChunk,1094 {}, // noop, since we'll have already added an entry to debug info1095 'debug', // noop, but we need it to not be empty string since that indicates the root object1096 response,1097 initializeDebugInfo,1098 [''], // path1099 true,1100 );1101 break;1102 }1103 default:1104 throw debugChunk.reason;1105 }1106 }1107 } catch (error) {1108 triggerErrorOnChunk(response, chunk, error);1109 } finally {1110 isInitializingDebugInfo = prevIsInitializingDebugInfo;1111 }1112 }1113}11141115function initializeModelChunk<T>(chunk: ResolvedModelChunk<T>): void {1116 const prevHandler = initializingHandler;1117 const prevChunk = initializingChunk;1118 initializingHandler = null;11191120 const resolvedModel = chunk.value;1121 const response = chunk.reason;11221123 // We go to the BLOCKED state until we've fully resolved this.1124 // We do this before parsing in case we try to initialize the same chunk1125 // while parsing the model. Such as in a cyclic reference.1126 const cyclicChunk: BlockedChunk<T> = chunk as any;1127 cyclicChunk.status = BLOCKED;1128 cyclicChunk.value = null;1129 cyclicChunk.reason = null;11301131 if ((enableProfilerTimer && enableComponentPerformanceTrack) || __DEV__) {1132 initializingChunk = cyclicChunk;1133 }11341135 if (__DEV__) {1136 // Initialize any debug info and block the initializing chunk on any1137 // unresolved entries.1138 initializeDebugChunk(response, chunk);1139 // TODO: The chunk might have transitioned to ERRORED now.1140 // Should we return early if that happens?1141 }11421143 try {1144 const value: T = parseModel(response, resolvedModel);1145 // Invoke any listeners added while resolving this model. I.e. cyclic1146 // references. This may or may not fully resolve the model depending on1147 // if they were blocked.1148 const resolveListeners = cyclicChunk.value;1149 if (resolveListeners !== null) {1150 cyclicChunk.value = null;1151 cyclicChunk.reason = null;1152 for (let i = 0; i < resolveListeners.length; i++) {1153 const listener = resolveListeners[i];1154 if (typeof listener === 'function') {1155 listener(value);1156 } else {1157 fulfillReference(response, listener, value, cyclicChunk);1158 }1159 }1160 }1161 if (initializingHandler !== null) {1162 if (initializingHandler.errored) {1163 throw initializingHandler.reason;1164 }1165 if (initializingHandler.deps > 0) {1166 // We discovered new dependencies on modules that are not yet resolved.1167 // We have to keep the BLOCKED state until they're resolved.1168 initializingHandler.value = value;1169 initializingHandler.chunk = cyclicChunk;1170 return;1171 }1172 }1173 const initializedChunk: InitializedChunk<T> = chunk as any;1174 initializedChunk.status = INITIALIZED;1175 initializedChunk.value = value;1176 initializedChunk.reason = null;11771178 if (__DEV__) {1179 processChunkDebugInfo(response, initializedChunk, value);1180 }1181 } catch (error) {1182 const erroredChunk: ErroredChunk<T> = chunk as any;1183 erroredChunk.status = ERRORED;1184 erroredChunk.reason = error;1185 } finally {1186 initializingHandler = prevHandler;1187 if ((enableProfilerTimer && enableComponentPerformanceTrack) || __DEV__) {1188 initializingChunk = prevChunk;1189 }1190 }1191}11921193function initializeModuleChunk<T>(chunk: ResolvedModuleChunk<T>): void {1194 try {1195 const value: T = requireModule(chunk.value);1196 const initializedChunk: InitializedChunk<T> = chunk as any;1197 initializedChunk.status = INITIALIZED;1198 initializedChunk.value = value;1199 initializedChunk.reason = null;1200 } catch (error) {1201 const erroredChunk: ErroredChunk<T> = chunk as any;1202 erroredChunk.status = ERRORED;1203 erroredChunk.reason = error;1204 }1205}12061207// Report that any missing chunks in the model is now going to throw this1208// error upon read. Also notify any pending promises.1209export function reportGlobalError(1210 weakResponse: WeakResponse,1211 error: Error,1212): void {1213 if (hasGCedResponse(weakResponse)) {1214 // Ignore close signal if we are not awaiting any more pending chunks.1215 return;1216 }1217 const response = unwrapWeakResponse(weakResponse);1218 response._closed = true;1219 response._closedReason = error;1220 response._chunks.forEach(chunk => {1221 // If this chunk was already resolved or errored, it won't1222 // trigger an error but if it wasn't then we need to1223 // because we won't be getting any new data to resolve it.1224 if (chunk.status === PENDING) {1225 triggerErrorOnChunk(response, chunk, error);1226 } else if (enableFlightWeakThenables && chunk.status === PENDING_WEAK) {1227 // A weak Promise reference may never be emitted by the server. It1228 // stays forever pending instead of erroring.1229 haltChunk(response, chunk);1230 } else if (chunk.status === INITIALIZED && chunk.reason !== null) {1231 chunk.reason.error(error);1232 }1233 });1234 if (__DEV__) {1235 const debugChannel = response._debugChannel;1236 if (debugChannel !== undefined) {1237 // If we don't have any more ways of reading data, we don't have to send1238 // any more neither. So we close the writable side.1239 closeDebugChannel(debugChannel);1240 response._debugChannel = undefined;1241 // Make sure the debug channel is not closed a second time when the1242 // Response gets GC:ed.1243 if (debugChannelRegistry !== null) {1244 debugChannelRegistry.unregister(response);1245 }1246 }1247 }1248}12491250function nullRefGetter() {1251 if (__DEV__) {1252 return null;1253 }1254}12551256function getIOInfoTaskName(ioInfo: ReactIOInfo): string {1257 return ioInfo.name || 'unknown';1258}12591260function getAsyncInfoTaskName(asyncInfo: ReactAsyncInfo): string {1261 return 'await ' + getIOInfoTaskName(asyncInfo.awaited);1262}12631264function getServerComponentTaskName(componentInfo: ReactComponentInfo): string {1265 return '<' + (componentInfo.name || '...') + '>';1266}12671268function getTaskName(type: mixed): string {1269 if (type === REACT_FRAGMENT_TYPE) {1270 return '<>';1271 }1272 if (typeof type === 'function') {1273 // This is a function so it must have been a Client Reference that resolved to1274 // a function. We use "use client" to indicate that this is the boundary into1275 // the client. There should only be one for any given owner chain.1276 return '"use client"';1277 }1278 if (1279 typeof type === 'object' &&1280 type !== null &&1281 type.$$typeof === REACT_LAZY_TYPE1282 ) {1283 if (type._payload instanceof ReactPromise) {1284 // This is a lazy node created by Flight, i.e. it wraps a chunk. It is1285 // probably a client reference. We use the "use client" string to indicate1286 // that this is the boundary into the client. There will only be one for1287 // any given owner chain.1288 return '"use client"';1289 }1290 // We don't want to eagerly initialize the initializer in DEV mode so we can't1291 // call it to extract the type so we don't know the type of this component.1292 return '<...>';1293 }1294 try {1295 const name = getComponentNameFromType(type);1296 return name ? '<' + name + '>' : '<...>';1297 } catch (x) {1298 return '<...>';1299 }1300}13011302function initializeElement(1303 response: Response,1304 element: any,1305 lazyNode: null | LazyComponent<1306 React$Element<any>,1307 SomeChunk<React$Element<any>>,1308 >,1309): void {1310 if (!__DEV__) {1311 return;1312 }1313 const stack = element._debugStack;1314 const owner = element._owner;1315 if (owner === null) {1316 element._owner = response._debugRootOwner;1317 }1318 let env = response._rootEnvironmentName;1319 if (owner !== null && owner.env != null) {1320 // Interestingly we don't actually have the environment name of where1321 // this JSX was created if it doesn't have an owner but if it does1322 // it must be the same environment as the owner. We could send it separately1323 // but it seems a bit unnecessary for this edge case.1324 env = owner.env;1325 }1326 let normalizedStackTrace: null | Error = null;1327 if (owner === null && response._debugRootStack != null) {1328 // We override the stack if we override the owner since the stack where the root JSX1329 // was created on the server isn't very useful but where the request was made is.1330 normalizedStackTrace = response._debugRootStack;1331 } else if (stack !== null) {1332 // We create a fake stack and then create an Error object inside of it.1333 // This means that the stack trace is now normalized into the native format1334 // of the browser and the stack frames will have been registered with1335 // source mapping information.1336 // This can unfortunately happen within a user space callstack which will1337 // remain on the stack.1338 normalizedStackTrace = createFakeJSXCallStackInDEV(response, stack, env);1339 }1340 element._debugStack = normalizedStackTrace;1341 let task: null | ConsoleTask = null;1342 if (supportsCreateTask && stack !== null) {1343 const createTaskFn = (console as any).createTask.bind(1344 console,1345 getTaskName(element.type),1346 );1347 const callStack = buildFakeCallStack(1348 response,1349 stack,1350 env,1351 false,1352 createTaskFn,1353 );1354 // This owner should ideally have already been initialized to avoid getting1355 // user stack frames on the stack.1356 const ownerTask =1357 owner === null ? null : initializeFakeTask(response, owner);1358 if (ownerTask === null) {1359 const rootTask = response._debugRootTask;1360 if (rootTask != null) {1361 task = rootTask.run(callStack);1362 } else {1363 task = callStack();1364 }1365 } else {1366 task = ownerTask.run(callStack);1367 }1368 }1369 element._debugTask = task;13701371 // This owner should ideally have already been initialized to avoid getting1372 // user stack frames on the stack.1373 if (owner !== null) {1374 initializeFakeStack(response, owner);1375 }13761377 if (lazyNode !== null) {1378 // If the lazy node is initialized, we move its debug info to the inner1379 // value.1380 if (lazyNode._payload.status === INITIALIZED && lazyNode._debugInfo) {1381 const debugInfo = lazyNode._debugInfo.splice(0);1382 if (element._debugInfo) {1383 // $FlowFixMe[method-unbinding]1384 element._debugInfo.unshift.apply(element._debugInfo, debugInfo);1385 } else {1386 Object.defineProperty(element, '_debugInfo', {1387 configurable: false,1388 enumerable: false,1389 writable: true,1390 value: debugInfo,1391 });1392 }1393 }1394 }13951396 // TODO: We should be freezing the element but currently, we might write into1397 // _debugInfo later. We could move it into _store which remains mutable.1398 Object.freeze(element.props);1399}14001401function createElement(1402 response: Response,1403 type: mixed,1404 key: mixed,1405 props: mixed,1406 owner: ?ReactComponentInfo, // DEV-only1407 stack: ?ReactStackTrace, // DEV-only1408 validated: 0 | 1 | 2, // DEV-only1409):1410 | React$Element<any>1411 | LazyComponent<React$Element<any>, SomeChunk<React$Element<any>>> {1412 let element: any;1413 if (__DEV__) {1414 // `ref` is non-enumerable in dev1415 element = {1416 $$typeof: REACT_ELEMENT_TYPE,1417 type,1418 key,1419 props,1420 _owner: owner === undefined ? null : owner,1421 } as any;1422 Object.defineProperty(element, 'ref', {1423 enumerable: false,1424 get: nullRefGetter,1425 });1426 } else {1427 element = {1428 // This tag allows us to uniquely identify this as a React Element1429 $$typeof: REACT_ELEMENT_TYPE,14301431 type,1432 key,1433 ref: null,1434 props,1435 } as any;1436 }14371438 if (__DEV__) {1439 // We don't really need to add any of these but keeping them for good measure.1440 // Unfortunately, _store is enumerable in jest matchers so for equality to1441 // work, I need to keep it or make _store non-enumerable in the other file.1442 element._store = {} as {1443 validated?: number,1444 };1445 Object.defineProperty(element._store, 'validated', {1446 configurable: false,1447 enumerable: false,1448 writable: true,1449 value: validated, // Whether the element has already been validated on the server.1450 });1451 // debugInfo contains Server Component debug information.1452 Object.defineProperty(element, '_debugInfo', {1453 configurable: false,1454 enumerable: false,1455 writable: true,1456 value: null,1457 });1458 Object.defineProperty(element, '_debugStack', {1459 configurable: false,1460 enumerable: false,1461 writable: true,1462 value: stack === undefined ? null : stack,1463 });1464 Object.defineProperty(element, '_debugTask', {1465 configurable: false,1466 enumerable: false,1467 writable: true,1468 value: null,1469 });1470 }14711472 if (initializingHandler !== null) {1473 const handler = initializingHandler;1474 // We pop the stack to the previous outer handler before leaving the Element.1475 // This is effectively the complete phase.1476 initializingHandler = handler.parent;1477 if (handler.errored) {1478 // Something errored inside this Element's props. We can turn this Element1479 // into a Lazy so that we can still render up until that Lazy is rendered.1480 const erroredChunk: ErroredChunk<React$Element<any>> = createErrorChunk(1481 response,1482 handler.reason,1483 );1484 if (__DEV__) {1485 initializeElement(response, element, null);1486 // Conceptually the error happened inside this Element but right before1487 // it was rendered. We don't have a client side component to render but1488 // we can add some DebugInfo to explain that this was conceptually a1489 // Server side error that errored inside this element. That way any stack1490 // traces will point to the nearest JSX that errored - e.g. during1491 // serialization.1492 const erroredComponent: ReactComponentInfo = {1493 name: getComponentNameFromType(element.type) || '',1494 owner: element._owner,1495 };1496 // $FlowFixMe[cannot-write]1497 erroredComponent.debugStack = element._debugStack;1498 if (supportsCreateTask) {1499 // $FlowFixMe[cannot-write]1500 erroredComponent.debugTask = element._debugTask;1501 }1502 erroredChunk._debugInfo = [erroredComponent];1503 }1504 return createLazyChunkWrapper(erroredChunk, validated);1505 }1506 if (handler.deps > 0) {1507 // We have blocked references inside this Element but we can turn this into1508 // a Lazy node referencing this Element to let everything around it proceed.1509 const blockedChunk: BlockedChunk<React$Element<any>> =1510 createBlockedChunk(response);1511 handler.value = element;1512 handler.chunk = blockedChunk;1513 const lazyNode = createLazyChunkWrapper(blockedChunk, validated);1514 if (__DEV__) {1515 // After we have initialized any blocked references, initialize stack etc.1516 const init = initializeElement.bind(null, response, element, lazyNode);1517 blockedChunk.then(init, init);1518 }1519 return lazyNode;1520 }1521 }1522 if (__DEV__) {1523 initializeElement(response, element, null);1524 }15251526 return element;1527}15281529function transferValidation(store: {validated: 0 | 1 | 2}, value: mixed): void {1530 if (store.validated && typeof value === 'object' && value !== null) {1531 // Only elements and lazy nodes carry key validation. Any other value, e.g.1532 // an array of children, needs to have its own items validated instead.1533 const $$typeof = (value as any).$$typeof;1534 if ($$typeof === REACT_ELEMENT_TYPE || $$typeof === REACT_LAZY_TYPE) {1535 const valueStore = (value as any)._store;1536 if (valueStore && !valueStore.validated) {1537 valueStore.validated = store.validated;1538 }1539 }1540 }1541}15421543function readChunkAndTransferValidation<T>(1544 store: {validated: 0 | 1 | 2},1545 payload: SomeChunk<T>,1546): T {1547 const value: T = readChunk(payload);1548 transferValidation(store, value);1549 return value;1550}15511552function createLazyChunkWrapper<T>(1553 chunk: SomeChunk<T>,1554 validated: 0 | 1 | 2, // DEV-only1555): LazyComponent<T, SomeChunk<T>> {1556 const lazyType: LazyComponent<T, SomeChunk<T>> = {1557 $$typeof: REACT_LAZY_TYPE,1558 _payload: chunk,1559 _init: readChunk,1560 };1561 if (__DEV__) {1562 // Forward the live array1563 lazyType._debugInfo = chunk._debugInfo;1564 // Initialize a store for key validation by the JSX runtime. It can only1565 // validate the lazy node itself, because the value it refers to might not1566 // exist yet at that point, e.g. if it's an outlined row that hasn't been1567 // initialized. So the validation is transferred to the value when the lazy1568 // node is unwrapped. If the value is another lazy node, unwrapping that one1569 // forwards the validation further.1570 const store = {validated: validated};1571 lazyType._store = store;1572 // $FlowFixMe[incompatible-type] `bind` loses the type argument.1573 lazyType._init = readChunkAndTransferValidation.bind(null, store);1574 }1575 return lazyType;1576}15771578function getChunk(response: Response, id: number): SomeChunk<any> {1579 const chunks = response._chunks;1580 let chunk = chunks.get(id);1581 if (!chunk) {1582 if (response._closed) {1583 if (response._allowPartialStream) {1584 // For partial streams, chunks accessed after close should be HALTED1585 // (never resolve).1586 chunk = createHaltedChunk(response);1587 } else {1588 // We have already errored the response and we're not going to get1589 // anything more streaming in so this will immediately error.1590 chunk = createErrorChunk(response, response._closedReason);1591 }1592 } else {1593 chunk = createPendingChunk(response);1594 }1595 chunks.set(id, chunk);1596 }1597 return chunk;1598}15991600// Like getChunk, but for weak Promise references. The server may never emit1601// the row for a weak reference, so an unresolved weak chunk halts (stays1602// forever pending) instead of erroring when the stream closes.1603function getWeakChunk(response: Response, id: number): SomeChunk<any> {1604 const chunks = response._chunks;1605 let chunk = chunks.get(id);1606 if (!chunk) {1607 if (response._closed) {1608 // The stream already closed without emitting this row, so it will1609 // never resolve.1610 chunk = createHaltedChunk(response);1611 } else {1612 chunk = createPendingWeakChunk(response);1613 }1614 chunks.set(id, chunk);1615 }1616 return chunk;1617}16181619function fulfillReference(1620 response: Response,1621 reference: InitializationReference,1622 value: any,1623 fulfilledChunk: SomeChunk<any>,1624): void {1625 const {handler, parentObject, key, map, path} = reference;16261627 try {1628 for (let i = 1; i < path.length; i++) {1629 while (1630 typeof value === 'object' &&1631 value !== null &&1632 value.$$typeof === REACT_LAZY_TYPE1633 ) {1634 // We never expect to see a Lazy node on this path because we encode those as1635 // separate models. This must mean that we have inserted an extra lazy node1636 // e.g. to replace a blocked element. We must instead look for it inside.1637 const referencedChunk: SomeChunk<any> = value._payload;1638 if (referencedChunk === handler.chunk) {1639 // This is a reference to the thing we're currently blocking. We can peak1640 // inside of it to get the value.1641 value = handler.value;1642 continue;1643 } else {1644 switch (referencedChunk.status) {1645 case RESOLVED_MODEL:1646 initializeModelChunk(referencedChunk);1647 break;1648 case RESOLVED_MODULE:1649 initializeModuleChunk(referencedChunk);1650 break;1651 }1652 switch (referencedChunk.status) {1653 case INITIALIZED: {1654 value = referencedChunk.value;1655 continue;1656 }1657 case BLOCKED: {1658 // It is possible that we're blocked on our own chunk if it's a cycle.1659 // Before adding the listener to the inner chunk, let's check if it would1660 // result in a cycle.1661 const cyclicHandler = resolveBlockedCycle(1662 referencedChunk,1663 reference,1664 );1665 if (cyclicHandler !== null) {1666 // This reference points back to this chunk. We can resolve the cycle by1667 // using the value from that handler.1668 value = cyclicHandler.value;1669 continue;1670 }1671 // Fallthrough1672 }1673 case PENDING:1674 case PENDING_WEAK: {1675 // If we're not yet initialized we need to skip what we've already drilled1676 // through and then wait for the next value to become available.1677 path.splice(0, i - 1);1678 // Add "listener" to our new chunk dependency.1679 if (referencedChunk.value === null) {1680 referencedChunk.value = [reference];1681 } else {1682 referencedChunk.value.push(reference);1683 }1684 if (referencedChunk.reason === null) {1685 referencedChunk.reason = [reference];1686 } else {1687 referencedChunk.reason.push(reference);1688 }1689 return;1690 }1691 case HALTED: {1692 // Do nothing. We couldn't fulfill.1693 // TODO: Mark downstreams as halted too.1694 return;1695 }1696 default: {1697 rejectReference(1698 response,1699 reference.handler,1700 referencedChunk.reason,1701 );1702 return;1703 }1704 }1705 }1706 }1707 const name = path[i];1708 if (1709 typeof value === 'object' &&1710 value !== null &&1711 hasOwnProperty.call(value, name)1712 ) {1713 value = value[name];1714 } else {1715 throw new Error('Invalid reference.');1716 }1717 }17181719 while (1720 typeof value === 'object' &&1721 value !== null &&1722 value.$$typeof === REACT_LAZY_TYPE1723 ) {1724 // If what we're referencing is a Lazy it must be because we inserted one as a virtual node1725 // while it was blocked by other data. If it's no longer blocked, we can unwrap it.1726 const referencedChunk: SomeChunk<any> = value._payload;1727 if (referencedChunk === handler.chunk) {1728 // This is a reference to the thing we're currently blocking. We can peak1729 // inside of it to get the value.1730 value = handler.value;1731 continue;1732 } else {1733 switch (referencedChunk.status) {1734 case RESOLVED_MODEL:1735 initializeModelChunk(referencedChunk);1736 break;1737 case RESOLVED_MODULE:1738 initializeModuleChunk(referencedChunk);1739 break;1740 }1741 switch (referencedChunk.status) {1742 case INITIALIZED: {1743 value = referencedChunk.value;1744 continue;1745 }1746 }1747 }1748 break;1749 }17501751 const mappedValue = map(response, value, parentObject, key);1752 if (key !== __PROTO__) {1753 parentObject[key] = mappedValue;1754 }17551756 // If this is the root object for a model reference, where `handler.value`1757 // is a stale `null`, the resolved value can be used directly.1758 if (key === '' && handler.value === null) {1759 handler.value = mappedValue;1760 }17611762 // If the parent object is an unparsed React element tuple, we also need to1763 // update the props and owner of the parsed element object (i.e.1764 // handler.value).1765 if (1766 parentObject[0] === REACT_ELEMENT_TYPE &&1767 typeof handler.value === 'object' &&1768 handler.value !== null &&1769 handler.value.$$typeof === REACT_ELEMENT_TYPE1770 ) {1771 const element: any = handler.value;1772 switch (key) {1773 case '3':1774 if (__DEV__) {1775 transferReferencedDebugInfo(handler.chunk, fulfilledChunk);1776 }1777 element.props = mappedValue;1778 break;1779 case '4':1780 // This path doesn't call transferReferencedDebugInfo because this reference is to a debug chunk.1781 if (__DEV__) {1782 element._owner = mappedValue;1783 }1784 break;1785 case '5':1786 // This path doesn't call transferReferencedDebugInfo because this reference is to a debug chunk.1787 if (__DEV__) {1788 element._debugStack = mappedValue;1789 }1790 break;1791 default:1792 if (__DEV__) {1793 transferReferencedDebugInfo(handler.chunk, fulfilledChunk);1794 }1795 break;1796 }1797 } else if (__DEV__ && !reference.isDebug) {1798 transferReferencedDebugInfo(handler.chunk, fulfilledChunk);1799 }1800 } catch (error) {1801 rejectReference(response, reference.handler, error);1802 return;1803 }18041805 handler.deps--;18061807 if (handler.deps === 0) {1808 const chunk = handler.chunk;1809 if (chunk === null || chunk.status !== BLOCKED) {1810 return;1811 }1812 const resolveListeners = chunk.value;1813 const initializedChunk: InitializedChunk<any> = chunk as any;1814 initializedChunk.status = INITIALIZED;1815 initializedChunk.value = handler.value;1816 initializedChunk.reason = handler.reason; // Used by streaming chunks1817 if (resolveListeners !== null) {1818 wakeChunk(response, resolveListeners, handler.value, initializedChunk);1819 } else {1820 if (__DEV__) {1821 processChunkDebugInfo(response, initializedChunk, handler.value);1822 }1823 }1824 }1825}18261827function rejectReference(1828 response: Response,1829 handler: InitializationHandler,1830 error: mixed,1831): void {1832 if (handler.errored) {1833 // We've already errored. We could instead build up an AggregateError1834 // but if there are multiple errors we just take the first one like1835 // Promise.all.1836 return;1837 }1838 const blockedValue = handler.value;1839 handler.errored = true;1840 handler.value = null;1841 handler.reason = error;1842 const chunk = handler.chunk;1843 if (chunk === null || chunk.status !== BLOCKED) {1844 return;1845 }18461847 if (__DEV__) {1848 if (1849 typeof blockedValue === 'object' &&1850 blockedValue !== null &&1851 blockedValue.$$typeof === REACT_ELEMENT_TYPE1852 ) {1853 const element = blockedValue;1854 // Conceptually the error happened inside this Element but right before1855 // it was rendered. We don't have a client side component to render but1856 // we can add some DebugInfo to explain that this was conceptually a1857 // Server side error that errored inside this element. That way any stack1858 // traces will point to the nearest JSX that errored - e.g. during1859 // serialization.1860 const erroredComponent: ReactComponentInfo = {1861 name: getComponentNameFromType(element.type) || '',1862 owner: element._owner,1863 };1864 // $FlowFixMe[cannot-write]1865 erroredComponent.debugStack = element._debugStack;1866 if (supportsCreateTask) {1867 // $FlowFixMe[cannot-write]1868 erroredComponent.debugTask = element._debugTask;1869 }1870 chunk._debugInfo.push(erroredComponent);1871 }1872 }18731874 triggerErrorOnChunk(response, chunk, error);1875}18761877function waitForReference<T>(1878 referencedChunk: PendingChunk<T> | PendingWeakChunk<T> | BlockedChunk<T>,1879 parentObject: Object,1880 key: string,1881 response: Response,1882 map: (response: Response, model: any, parentObject: Object, key: string) => T,1883 path: Array<string>,1884 isAwaitingDebugInfo: boolean, // DEV-only1885): T {1886 if (1887 __DEV__ &&1888 (response._debugChannel === undefined ||1889 !response._debugChannel.hasReadable)1890 ) {1891 if (1892 referencedChunk.status === PENDING &&1893 parentObject[0] === REACT_ELEMENT_TYPE &&1894 (key === '4' || key === '5')1895 ) {1896 // If the parent object is an unparsed React element tuple, and this is a reference1897 // to the owner or debug stack. Then we expect the chunk to have been emitted earlier1898 // in the stream. It might be blocked on other things but chunk should no longer be pending.1899 // If it's still pending that suggests that it was referencing an object in the debug1900 // channel, but no debug channel was wired up so it's missing. In this case we can just1901 // drop the debug info instead of halting the whole stream.1902 return null as any;1903 }1904 }19051906 let handler: InitializationHandler;1907 if (initializingHandler) {1908 handler = initializingHandler;1909 handler.deps++;1910 } else {1911 handler = initializingHandler = {1912 parent: null,1913 chunk: null,1914 value: null,1915 reason: null,1916 deps: 1,1917 errored: false,1918 };1919 }19201921 const reference: InitializationReference = {1922 handler,1923 parentObject,1924 key,1925 map,1926 path,1927 };1928 if (__DEV__) {1929 reference.isDebug = isAwaitingDebugInfo;1930 }19311932 // Add "listener".1933 if (referencedChunk.value === null) {1934 referencedChunk.value = [reference];1935 } else {1936 referencedChunk.value.push(reference);1937 }1938 if (referencedChunk.reason === null) {1939 referencedChunk.reason = [reference];1940 } else {1941 referencedChunk.reason.push(reference);1942 }19431944 // Return a place holder value for now.1945 return null as any;1946}19471948function loadServerReference<A: Iterable<any>, T>(1949 response: Response,1950 metaData: {1951 id: any,1952 bound: null | Thenable<Array<any>>,1953 name?: string, // DEV-only1954 env?: string, // DEV-only1955 location?: ReactFunctionLocation, // DEV-only1956 },1957 parentObject: Object,1958 key: string,1959): (...A) => Promise<T> {1960 if (!response._serverReferenceConfig) {1961 // In the normal case, we can't load this Server Reference in the current environment and1962 // we just return a proxy to it.1963 return createBoundServerReference(1964 metaData,1965 response._callServer,1966 response._encodeFormAction,1967 __DEV__ ? response._debugFindSourceMapURL : undefined,1968 );1969 }1970 // If we have a module mapping we can load the real version of this Server Reference.1971 const serverReference: ClientReference<T> =1972 resolveServerReference<$FlowFixMe>(1973 response._serverReferenceConfig,1974 metaData.id,1975 );19761977 let promise: null | Thenable<any> = preloadModule(serverReference);1978 if (!promise) {1979 if (!metaData.bound) {1980 const resolvedValue = requireModule(serverReference) as any;1981 registerBoundServerReference(1982 resolvedValue,1983 metaData.id,1984 metaData.bound,1985 response._encodeFormAction,1986 );1987 return resolvedValue;1988 } else {1989 promise = Promise.resolve(metaData.bound);1990 }1991 } else if (metaData.bound) {1992 promise = Promise.all([promise, metaData.bound]);1993 }19941995 let handler: InitializationHandler;1996 if (initializingHandler) {1997 handler = initializingHandler;1998 handler.deps++;1999 } else {2000 handler = initializingHandler = {
Findings
✓ No findings reported for this file.