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} from 'shared/ReactFeatureFlags';5051import {52 resolveClientReference,53 resolveServerReference,54 preloadModule,55 requireModule,56 getModuleDebugInfo,57 dispatchHint,58 readPartialStringChunk,59 readFinalStringChunk,60 createStringDecoder,61 prepareDestinationForModule,62 bindToConsole,63 rendererVersion,64 rendererPackageName,65 checkEvalAvailabilityOnceDev,66} from './ReactFlightClientConfig';6768import {69 createBoundServerReference,70 registerBoundServerReference,71} from './ReactFlightReplyClient';7273import {readTemporaryReference} from './ReactFlightTemporaryReferences';7475import {76 markAllTracksInOrder,77 logComponentRender,78 logDedupedComponentRender,79 logComponentAborted,80 logComponentErrored,81 logIOInfo,82 logIOInfoErrored,83 logComponentAwait,84 logComponentAwaitAborted,85 logComponentAwaitErrored,86} from './ReactFlightPerformanceTrack';8788import {89 REACT_LAZY_TYPE,90 REACT_ELEMENT_TYPE,91 ASYNC_ITERATOR,92 REACT_FRAGMENT_TYPE,93} from 'shared/ReactSymbols';9495import getComponentNameFromType from 'shared/getComponentNameFromType';9697import {getOwnerStackByComponentInfoInDev} from 'shared/ReactComponentInfoStack';9899import hasOwnProperty from 'shared/hasOwnProperty';100101import {injectInternals} from './ReactFlightClientDevToolsHook';102103import {OMITTED_PROP_ERROR} from 'shared/ReactFlightPropertyAccess';104105import ReactVersion from 'shared/ReactVersion';106107import isArray from 'shared/isArray';108109import * as React from 'react';110111import type {SharedStateServer} from 'react/src/ReactSharedInternalsServer';112import type {SharedStateClient} from 'react/src/ReactSharedInternalsClient';113114// TODO: This is an unfortunate hack. We shouldn't feature detect the internals115// like this. It's just that for now we support the same build of the Flight116// client both in the RSC environment, in the SSR environments as well as the117// browser client. We should probably have a separate RSC build. This is DEV118// only though.119const ReactSharedInteralsServer: void | SharedStateServer = (React as any)120 .__SERVER_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;121const ReactSharedInternals: SharedStateServer | SharedStateClient =122 React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE ||123 ReactSharedInteralsServer;124125export type {CallServerCallback, EncodeFormActionCallback};126127interface FlightStreamController {128 enqueueValue(value: any): void;129 enqueueModel(json: UninitializedModel): void;130 close(json: UninitializedModel): void;131 error(error: Error): void;132}133134type UninitializedModel = string;135136type ProfilingResult = {137 track: number,138 endTime: number,139 component: null | ReactComponentInfo,140};141142const ROW_ID = 0;143const ROW_TAG = 1;144const ROW_LENGTH = 2;145const ROW_CHUNK_BY_NEWLINE = 3;146const ROW_CHUNK_BY_LENGTH = 4;147148type RowParserState = 0 | 1 | 2 | 3 | 4;149150const PENDING = 'pending';151const BLOCKED = 'blocked';152const RESOLVED_MODEL = 'resolved_model';153const RESOLVED_MODULE = 'resolved_module';154const INITIALIZED = 'fulfilled';155const ERRORED = 'rejected';156const HALTED = 'halted'; // DEV-only. Means it never resolves even if connection closes.157158const __PROTO__ = '__proto__';159160type PendingChunk<T> = {161 status: 'pending',162 value: null | Array<InitializationReference | (T => mixed)>,163 reason: null | Array<InitializationReference | (mixed => mixed)>,164 _children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only165 _debugChunk: null | SomeChunk<ReactDebugInfoEntry>, // DEV-only166 _debugInfo: ReactDebugInfo, // DEV-only167 then(resolve: (T) => mixed, reject?: (mixed) => mixed): void,168};169type BlockedChunk<T> = {170 status: 'blocked',171 value: null | Array<InitializationReference | (T => mixed)>,172 reason: null | Array<InitializationReference | (mixed => mixed)>,173 _children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only174 _debugChunk: null, // DEV-only175 _debugInfo: ReactDebugInfo, // DEV-only176 then(resolve: (T) => mixed, reject?: (mixed) => mixed): void,177};178type ResolvedModelChunk<T> = {179 status: 'resolved_model',180 value: UninitializedModel,181 reason: Response,182 _children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only183 _debugChunk: null | SomeChunk<ReactDebugInfoEntry>, // DEV-only184 _debugInfo: ReactDebugInfo, // DEV-only185 then(resolve: (T) => mixed, reject?: (mixed) => mixed): void,186};187type ResolvedModuleChunk<T> = {188 status: 'resolved_module',189 value: ClientReference<T>,190 reason: null,191 _children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only192 _debugChunk: null, // DEV-only193 _debugInfo: ReactDebugInfo, // DEV-only194 then(resolve: (T) => mixed, reject?: (mixed) => mixed): void,195};196type InitializedChunk<T> = {197 status: 'fulfilled',198 value: T,199 reason: null | FlightStreamController,200 _children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only201 _debugChunk: null, // DEV-only202 _debugInfo: ReactDebugInfo, // DEV-only203 then(resolve: (T) => mixed, reject?: (mixed) => mixed): void,204};205type InitializedStreamChunk<206 T: ReadableStream | $AsyncIterable<any, any, void>,207> = {208 status: 'fulfilled',209 value: T,210 reason: FlightStreamController,211 _children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only212 _debugChunk: null, // DEV-only213 _debugInfo: ReactDebugInfo, // DEV-only214 then(resolve: (ReadableStream) => mixed, reject?: (mixed) => mixed): void,215};216type ErroredChunk<T> = {217 status: 'rejected',218 value: null,219 reason: mixed,220 _children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only221 _debugChunk: null, // DEV-only222 _debugInfo: ReactDebugInfo, // DEV-only223 then(resolve: (T) => mixed, reject?: (mixed) => mixed): void,224};225type HaltedChunk<T> = {226 status: 'halted',227 value: null,228 reason: null,229 _children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only230 _debugChunk: null, // DEV-only231 _debugInfo: ReactDebugInfo, // DEV-only232 then(resolve: (T) => mixed, reject?: (mixed) => mixed): void,233};234type SomeChunk<T> =235 | PendingChunk<T>236 | BlockedChunk<T>237 | ResolvedModelChunk<T>238 | ResolvedModuleChunk<T>239 | InitializedChunk<T>240 | ErroredChunk<T>241 | HaltedChunk<T>;242243// $FlowFixMe[missing-this-annot]244function ReactPromise(status: any, value: any, reason: any) {245 this.status = status;246 this.value = value;247 this.reason = reason;248 if (enableProfilerTimer && enableComponentPerformanceTrack) {249 this._children = [];250 }251 if (__DEV__) {252 this._debugChunk = null;253 this._debugInfo = [];254 }255}256// We subclass Promise.prototype so that we get other methods like .catch257ReactPromise.prototype = Object.create(Promise.prototype) as any;258// TODO: This doesn't return a new Promise chain unlike the real .then259ReactPromise.prototype.then = function <T>(260 this: SomeChunk<T>,261 resolve: (value: T) => mixed,262 reject?: (reason: mixed) => mixed,263) {264 const chunk: SomeChunk<T> = this;265 // If we have resolved content, we try to initialize it first which266 // might put us back into one of the other states.267 switch (chunk.status) {268 case RESOLVED_MODEL:269 initializeModelChunk(chunk);270 break;271 case RESOLVED_MODULE:272 initializeModuleChunk(chunk);273 break;274 }275 if (__DEV__ && enableAsyncDebugInfo) {276 // Because only native Promises get picked up when we're awaiting we need to wrap277 // this in a native Promise in DEV. This means that these callbacks are no longer sync278 // but the lazy initialization is still sync and the .value can be inspected after,279 // allowing it to be read synchronously anyway.280 const resolveCallback = resolve;281 const rejectCallback = reject;282 const wrapperPromise: Promise<T> = new Promise((res, rej) => {283 resolve = value => {284 // $FlowFixMe[prop-missing]285 wrapperPromise._debugInfo = this._debugInfo;286 res(value);287 };288 reject = reason => {289 // $FlowFixMe[prop-missing]290 wrapperPromise._debugInfo = this._debugInfo;291 rej(reason);292 };293 });294 wrapperPromise.then(resolveCallback, rejectCallback);295 }296 // The status might have changed after initialization.297 switch (chunk.status) {298 case INITIALIZED:299 if (typeof resolve === 'function') {300 resolve(chunk.value);301 }302 break;303 case PENDING:304 case BLOCKED:305 if (typeof resolve === 'function') {306 if (chunk.value === null) {307 chunk.value = [] as Array<InitializationReference | (T => mixed)>;308 }309 chunk.value.push(resolve);310 }311 if (typeof reject === 'function') {312 if (chunk.reason === null) {313 chunk.reason = [] as Array<314 InitializationReference | (mixed => mixed),315 >;316 }317 chunk.reason.push(reject);318 }319 break;320 case HALTED: {321 break;322 }323 default:324 if (typeof reject === 'function') {325 reject(chunk.reason);326 }327 break;328 }329};330331export type FindSourceMapURLCallback = (332 fileName: string,333 environmentName: string,334) => null | string;335336export type DebugChannelCallback = (message: string) => void;337338export type DebugChannel = {339 hasReadable: boolean,340 callback: DebugChannelCallback | null,341};342343type Response = {344 _bundlerConfig: ServerConsumerModuleMap,345 _serverReferenceConfig: null | ServerManifest,346 _moduleLoading: ModuleLoading,347 _callServer: CallServerCallback,348 _encodeFormAction: void | EncodeFormActionCallback,349 _nonce: ?string,350 _chunks: Map<number, SomeChunk<any>>,351 _stringDecoder: StringDecoder,352 _closed: boolean,353 _closedReason: mixed,354 _allowPartialStream: boolean,355 _tempRefs: void | TemporaryReferenceSet, // the set temporary references can be resolved from356 _timeOrigin: number, // Profiling-only357 _pendingInitialRender: null | TimeoutID, // Profiling-only,358 _pendingChunks: number, // DEV-only359 _weakResponse: WeakResponse, // DEV-only360 _debugRootOwner?: null | ReactComponentInfo, // DEV-only361 _debugRootStack?: null | Error, // DEV-only362 _debugRootTask?: null | ConsoleTask, // DEV-only363 _debugStartTime: number, // DEV-only364 _debugEndTime: null | number, // DEV-only365 _debugIOStarted: boolean, // DEV-only366 _debugFindSourceMapURL?: void | FindSourceMapURLCallback, // DEV-only367 _debugChannel?: void | DebugChannel, // DEV-only368 _blockedConsole?: null | SomeChunk<ConsoleEntry>, // DEV-only369 _replayConsole: boolean, // DEV-only370 _rootEnvironmentName: string, // DEV-only, the requested environment name.371};372373// This indirection exists only to clean up DebugChannel when all Lazy References are GC:ed.374// Therefore we only use the indirection in DEV.375type WeakResponse = {376 weak: WeakRef<Response>,377 response: null | Response, // This is null when there are no pending chunks.378};379380export type {WeakResponse as Response};381382function hasGCedResponse(weakResponse: WeakResponse): boolean {383 return __DEV__ && weakResponse.weak.deref() === undefined;384}385386function unwrapWeakResponse(weakResponse: WeakResponse): Response {387 if (__DEV__) {388 const response = weakResponse.weak.deref();389 if (response === undefined) {390 // eslint-disable-next-line react-internal/prod-error-codes391 throw new Error(392 'We did not expect to receive new data after GC:ing the response.',393 );394 }395 return response;396 } else {397 return weakResponse as any; // In prod we just use the real Response directly.398 }399}400401function getWeakResponse(response: Response): WeakResponse {402 if (__DEV__) {403 return response._weakResponse;404 } else {405 return response as any; // In prod we just use the real Response directly.406 }407}408409function closeDebugChannel(debugChannel: DebugChannel): void {410 if (debugChannel.callback) {411 debugChannel.callback('');412 }413}414415// If FinalizationRegistry doesn't exist, we cannot use the debugChannel.416const debugChannelRegistry =417 __DEV__ && typeof FinalizationRegistry === 'function'418 ? new FinalizationRegistry(closeDebugChannel)419 : null;420421function readChunk<T>(chunk: SomeChunk<T>): T {422 // If we have resolved content, we try to initialize it first which423 // might put us back into one of the other states.424 switch (chunk.status) {425 case RESOLVED_MODEL:426 initializeModelChunk(chunk);427 break;428 case RESOLVED_MODULE:429 initializeModuleChunk(chunk);430 break;431 }432 // The status might have changed after initialization.433 switch (chunk.status) {434 case INITIALIZED:435 return chunk.value;436 case PENDING:437 case BLOCKED:438 case HALTED:439 // eslint-disable-next-line no-throw-literal440 throw chunk as any as Thenable<T>;441 default:442 throw chunk.reason;443 }444}445446export function getRoot<T>(weakResponse: WeakResponse): Thenable<T> {447 const response = unwrapWeakResponse(weakResponse);448 const chunk = getChunk(response, 0);449 return chunk as any;450}451452function createPendingChunk<T>(response: Response): PendingChunk<T> {453 if (__DEV__) {454 // Retain a strong reference to the Response while we wait for the result.455 if (response._pendingChunks++ === 0) {456 response._weakResponse.response = response;457 if (response._pendingInitialRender !== null) {458 clearTimeout(response._pendingInitialRender);459 response._pendingInitialRender = null;460 }461 }462 }463 // $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors464 return new ReactPromise(PENDING, null, null);465}466467function releasePendingChunk(response: Response, chunk: SomeChunk<any>): void {468 if (__DEV__ && chunk.status === PENDING) {469 if (--response._pendingChunks === 0) {470 // We're no longer waiting for any more chunks. We can release the strong reference471 // to the response. We'll regain it if we ask for any more data later on.472 response._weakResponse.response = null;473 // Wait a short period to see if any more chunks get asked for. E.g. by a React render.474 // These chunks might discover more pending chunks.475 // If we don't ask for more then we assume that those chunks weren't blocking initial476 // render and are excluded from the performance track.477 response._pendingInitialRender = setTimeout(478 flushInitialRenderPerformance.bind(null, response),479 100,480 );481 }482 }483}484485function createBlockedChunk<T>(response: Response): BlockedChunk<T> {486 // $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors487 return new ReactPromise(BLOCKED, null, null);488}489490function createErrorChunk<T>(491 response: Response,492 error: mixed,493): ErroredChunk<T> {494 // $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors495 return new ReactPromise(ERRORED, null, error);496}497498function filterDebugInfo(499 response: Response,500 value: {_debugInfo: ReactDebugInfo, ...},501) {502 if (response._debugEndTime === null) {503 // No end time was defined, so we keep all debug info entries.504 return;505 }506507 // Remove any debug info entries after the defined end time. For async info508 // that means we're including anything that was awaited before the end time,509 // but it doesn't need to be resolved before the end time.510 const relativeEndTime =511 response._debugEndTime -512 // $FlowFixMe[prop-missing]513 performance.timeOrigin;514 const debugInfo = [];515 for (let i = 0; i < value._debugInfo.length; i++) {516 const info = value._debugInfo[i];517 if (typeof info.time === 'number' && info.time > relativeEndTime) {518 break;519 }520 debugInfo.push(info);521 }522 value._debugInfo = debugInfo;523}524525function pruneDebugInfoAfterError(526 response: Response,527 chunk: ErroredChunk<any>,528): void {529 if (response._debugEndTime === null) {530 return;531 }532533 const relativeEndTime =534 response._debugEndTime -535 // $FlowFixMe[prop-missing]536 performance.timeOrigin;537 const debugInfo = chunk._debugInfo;538 for (let i = 0; i < debugInfo.length; i++) {539 const info = debugInfo[i];540 if (typeof info.time === 'number' && info.time > relativeEndTime) {541 // This array may already be attached to the Lazy suspended in Fizz.542 debugInfo.length = i;543 return;544 }545 }546}547548function moveDebugInfoFromChunkToInnerValue<T>(549 chunk: InitializedChunk<T> | InitializedStreamChunk<any>,550 value: T,551): void {552 // Remove the debug info from the initialized chunk, and add it to the inner553 // value instead. This can be a React element, an array, or an uninitialized554 // Lazy.555 const resolvedValue = resolveLazy(value);556 if (557 typeof resolvedValue === 'object' &&558 resolvedValue !== null &&559 (isArray(resolvedValue) ||560 typeof resolvedValue[ASYNC_ITERATOR] === 'function' ||561 resolvedValue.$$typeof === REACT_ELEMENT_TYPE ||562 resolvedValue.$$typeof === REACT_LAZY_TYPE)563 ) {564 const debugInfo = chunk._debugInfo.splice(0);565 if (isArray(resolvedValue._debugInfo)) {566 // $FlowFixMe[method-unbinding]567 resolvedValue._debugInfo.unshift.apply(568 resolvedValue._debugInfo,569 debugInfo,570 );571 } else if (!Object.isFrozen(resolvedValue)) {572 Object.defineProperty(resolvedValue as any, '_debugInfo', {573 configurable: false,574 enumerable: false,575 writable: true,576 value: debugInfo,577 });578 }579 // TODO: If the resolved value is a frozen element (e.g. a client-created580 // element from a temporary reference, or a JSX element exported as a client581 // reference), server debug info is currently dropped because the element582 // can't be mutated. We should probably clone the element so each rendering583 // context gets its own mutable copy with the correct debug info.584 }585}586587function processChunkDebugInfo<T>(588 response: Response,589 chunk: InitializedChunk<T> | InitializedStreamChunk<any>,590 value: T,591): void {592 filterDebugInfo(response, chunk);593 moveDebugInfoFromChunkToInnerValue(chunk, value);594}595596function wakeChunk<T>(597 response: Response,598 listeners: Array<InitializationReference | (T => mixed)>,599 value: T,600 chunk: InitializedChunk<T>,601): void {602 for (let i = 0; i < listeners.length; i++) {603 const listener = listeners[i];604 if (typeof listener === 'function') {605 listener(value);606 } else {607 fulfillReference(response, listener, value, chunk);608 }609 }610611 if (__DEV__) {612 processChunkDebugInfo(response, chunk, value);613 }614}615616function rejectChunk(617 response: Response,618 listeners: Array<InitializationReference | (mixed => mixed)>,619 error: mixed,620): void {621 for (let i = 0; i < listeners.length; i++) {622 const listener = listeners[i];623 if (typeof listener === 'function') {624 listener(error);625 } else {626 rejectReference(response, listener.handler, error);627 }628 }629}630631function resolveBlockedCycle<T>(632 resolvedChunk: SomeChunk<T>,633 reference: InitializationReference,634): null | InitializationHandler {635 const referencedChunk = reference.handler.chunk;636 if (referencedChunk === null) {637 return null;638 }639 if (referencedChunk === resolvedChunk) {640 // We found the cycle. We can resolve the blocked cycle now.641 return reference.handler;642 }643 const resolveListeners = referencedChunk.value;644 if (resolveListeners !== null) {645 for (let i = 0; i < resolveListeners.length; i++) {646 const listener = resolveListeners[i];647 if (typeof listener !== 'function') {648 const foundHandler = resolveBlockedCycle(resolvedChunk, listener);649 if (foundHandler !== null) {650 return foundHandler;651 }652 }653 }654 }655 return null;656}657658function wakeChunkIfInitialized<T>(659 response: Response,660 chunk: SomeChunk<T>,661 resolveListeners: Array<InitializationReference | (T => mixed)>,662 rejectListeners: null | Array<InitializationReference | (mixed => mixed)>,663): void {664 switch (chunk.status) {665 case INITIALIZED:666 wakeChunk(response, resolveListeners, chunk.value, chunk);667 break;668 case BLOCKED:669 // It is possible that we're blocked on our own chunk if it's a cycle.670 // Before adding back the listeners to the chunk, let's check if it would671 // result in a cycle.672 for (let i = 0; i < resolveListeners.length; i++) {673 const listener = resolveListeners[i];674 if (typeof listener !== 'function') {675 const reference: InitializationReference = listener;676 const cyclicHandler = resolveBlockedCycle(chunk, reference);677 if (cyclicHandler !== null) {678 // This reference points back to this chunk. We can resolve the cycle by679 // using the value from that handler.680 fulfillReference(response, reference, cyclicHandler.value, chunk);681 resolveListeners.splice(i, 1);682 i--;683 if (rejectListeners !== null) {684 const rejectionIdx = rejectListeners.indexOf(reference);685 if (rejectionIdx !== -1) {686 rejectListeners.splice(rejectionIdx, 1);687 }688 }689 // The status might have changed after fulfilling the reference.690 switch ((chunk as SomeChunk<T>).status) {691 case INITIALIZED:692 const initializedChunk: InitializedChunk<T> = chunk as any;693 wakeChunk(694 response,695 resolveListeners,696 initializedChunk.value,697 initializedChunk,698 );699 return;700 case ERRORED:701 if (rejectListeners !== null) {702 rejectChunk(response, rejectListeners, chunk.reason);703 }704 return;705 }706 }707 }708 }709 // Fallthrough710 case PENDING:711 if (chunk.value) {712 for (let i = 0; i < resolveListeners.length; i++) {713 chunk.value.push(resolveListeners[i]);714 }715 } else {716 chunk.value = resolveListeners;717 }718719 if (chunk.reason) {720 if (rejectListeners) {721 for (let i = 0; i < rejectListeners.length; i++) {722 chunk.reason.push(rejectListeners[i]);723 }724 }725 } else {726 chunk.reason = rejectListeners;727 }728729 break;730 case ERRORED:731 if (rejectListeners) {732 rejectChunk(response, rejectListeners, chunk.reason);733 }734 break;735 }736}737738function triggerErrorOnChunk<T>(739 response: Response,740 chunk: SomeChunk<T>,741 error: mixed,742): void {743 if (chunk.status !== PENDING && chunk.status !== BLOCKED) {744 // If we get more data to an already resolved ID, we assume that it's745 // a stream chunk since any other row shouldn't have more than one entry.746 const streamChunk: InitializedStreamChunk<any> = chunk as any;747 const controller = streamChunk.reason;748 // $FlowFixMe[incompatible-type]: The error method should accept mixed.749 controller.error(error);750 return;751 }752 releasePendingChunk(response, chunk);753 const listeners = chunk.reason;754755 if (__DEV__ && chunk.status === PENDING) {756 // Lazily initialize any debug info and block the initializing chunk on any unresolved entries.757 if (chunk._debugChunk != null) {758 const prevHandler = initializingHandler;759 const prevChunk = initializingChunk;760 initializingHandler = null;761 const cyclicChunk: BlockedChunk<T> = chunk as any;762 cyclicChunk.status = BLOCKED;763 cyclicChunk.value = null;764 cyclicChunk.reason = null;765 if ((enableProfilerTimer && enableComponentPerformanceTrack) || __DEV__) {766 initializingChunk = cyclicChunk;767 }768 try {769 initializeDebugChunk(response, chunk);770 if (initializingHandler !== null) {771 if (initializingHandler.errored) {772 // Ignore error parsing debug info, we'll report the original error instead.773 } else if (initializingHandler.deps > 0) {774 // TODO: Block the resolution of the error until all the debug info has loaded.775 // We currently don't have a way to throw an error after all dependencies have776 // loaded because we currently treat errors as immediately cancelling the handler.777 }778 }779 } finally {780 initializingHandler = prevHandler;781 initializingChunk = prevChunk;782 }783 }784 }785786 const erroredChunk: ErroredChunk<T> = chunk as any;787 erroredChunk.status = ERRORED;788 erroredChunk.reason = error;789 if (__DEV__) {790 pruneDebugInfoAfterError(response, erroredChunk);791 }792 if (listeners !== null) {793 rejectChunk(response, listeners, error);794 }795}796797function createResolvedModelChunk<T>(798 response: Response,799 value: UninitializedModel,800): ResolvedModelChunk<T> {801 // $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors802 return new ReactPromise(RESOLVED_MODEL, value, response);803}804805function createResolvedModuleChunk<T>(806 response: Response,807 value: ClientReference<T>,808): ResolvedModuleChunk<T> {809 // $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors810 return new ReactPromise(RESOLVED_MODULE, value, null);811}812813function createInitializedTextChunk(814 response: Response,815 value: string,816): InitializedChunk<string> {817 // $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors818 return new ReactPromise(INITIALIZED, value, null);819}820821function createInitializedBufferChunk(822 response: Response,823 value: $ArrayBufferView | ArrayBuffer,824): InitializedChunk<Uint8Array> {825 // $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors826 return new ReactPromise(INITIALIZED, value, null);827}828829function createInitializedIteratorResultChunk<T>(830 response: Response,831 value: T,832 done: boolean,833): InitializedChunk<IteratorResult<T, T>> {834 // $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors835 return new ReactPromise(INITIALIZED, {done: done, value: value}, null);836}837838function createInitializedStreamChunk<839 T: ReadableStream | $AsyncIterable<any, any, void>,840>(841 response: Response,842 value: T,843 controller: FlightStreamController,844): InitializedChunk<T> {845 if (__DEV__) {846 // Retain a strong reference to the Response while we wait for chunks.847 if (response._pendingChunks++ === 0) {848 response._weakResponse.response = response;849 }850 }851 // We use the reason field to stash the controller since we already have that852 // field. It's a bit of a hack but efficient.853 // $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors854 return new ReactPromise(INITIALIZED, value, controller);855}856857function createResolvedIteratorResultChunk<T>(858 response: Response,859 value: UninitializedModel,860 done: boolean,861): ResolvedModelChunk<IteratorResult<T, T>> {862 // To reuse code as much code as possible we add the wrapper element as part of the JSON.863 const iteratorResultJSON =864 (done ? '{"done":true,"value":' : '{"done":false,"value":') + value + '}';865 // $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors866 return new ReactPromise(RESOLVED_MODEL, iteratorResultJSON, response);867}868869function resolveIteratorResultChunk<T>(870 response: Response,871 chunk: SomeChunk<IteratorResult<T, T>>,872 value: UninitializedModel,873 done: boolean,874): void {875 // To reuse code as much code as possible we add the wrapper element as part of the JSON.876 const iteratorResultJSON =877 (done ? '{"done":true,"value":' : '{"done":false,"value":') + value + '}';878 resolveModelChunk(response, chunk, iteratorResultJSON);879}880881function resolveModelChunk<T>(882 response: Response,883 chunk: SomeChunk<T>,884 value: UninitializedModel,885): void {886 if (chunk.status !== PENDING) {887 // If we get more data to an already resolved ID, we assume that it's888 // a stream chunk since any other row shouldn't have more than one entry.889 const streamChunk: InitializedStreamChunk<any> = chunk as any;890 const controller = streamChunk.reason;891 controller.enqueueModel(value);892 return;893 }894 releasePendingChunk(response, chunk);895 const resolveListeners = chunk.value;896 const rejectListeners = chunk.reason;897 const resolvedChunk: ResolvedModelChunk<T> = chunk as any;898 resolvedChunk.status = RESOLVED_MODEL;899 resolvedChunk.value = value;900 resolvedChunk.reason = response;901 if (resolveListeners !== null) {902 // This is unfortunate that we're reading this eagerly if903 // we already have listeners attached since they might no904 // longer be rendered or might not be the highest pri.905 initializeModelChunk(resolvedChunk);906 // The status might have changed after initialization.907 wakeChunkIfInitialized(response, chunk, resolveListeners, rejectListeners);908 }909}910911function resolveModuleChunk<T>(912 response: Response,913 chunk: SomeChunk<T>,914 value: ClientReference<T>,915): void {916 if (chunk.status !== PENDING && chunk.status !== BLOCKED) {917 // We already resolved. We didn't expect to see this.918 return;919 }920 releasePendingChunk(response, chunk);921 const resolveListeners = chunk.value;922 const rejectListeners = chunk.reason;923 const resolvedChunk: ResolvedModuleChunk<T> = chunk as any;924 resolvedChunk.status = RESOLVED_MODULE;925 resolvedChunk.value = value;926 resolvedChunk.reason = null;927 if (__DEV__) {928 const debugInfo = getModuleDebugInfo(value);929 if (debugInfo !== null) {930 // Add to the live set if it was already initialized.931 // $FlowFixMe[method-unbinding]932 resolvedChunk._debugInfo.push.apply(resolvedChunk._debugInfo, debugInfo);933 }934 }935 if (resolveListeners !== null) {936 initializeModuleChunk(resolvedChunk);937 wakeChunkIfInitialized(response, chunk, resolveListeners, rejectListeners);938 }939}940941type InitializationReference = {942 handler: InitializationHandler,943 parentObject: Object,944 key: string,945 map: (946 response: Response,947 model: any,948 parentObject: Object,949 key: string,950 ) => any,951 path: Array<string>,952 isDebug?: boolean, // DEV-only953};954type InitializationHandler = {955 parent: null | InitializationHandler,956 chunk: null | BlockedChunk<any>,957 value: any,958 reason: any,959 deps: number,960 errored: boolean,961};962let initializingHandler: null | InitializationHandler = null;963let initializingChunk: null | BlockedChunk<any> = null;964let isInitializingDebugInfo: boolean = false;965966function initializeDebugChunk(967 response: Response,968 chunk: ResolvedModelChunk<any> | PendingChunk<any>,969): void {970 const debugChunk = chunk._debugChunk;971 if (debugChunk !== null) {972 const debugInfo = chunk._debugInfo;973 const prevIsInitializingDebugInfo = isInitializingDebugInfo;974 isInitializingDebugInfo = true;975 try {976 if (debugChunk.status === RESOLVED_MODEL) {977 // Find the index of this debug info by walking the linked list.978 let idx = debugInfo.length;979 let c = debugChunk._debugChunk;980 while (c !== null) {981 if (c.status !== INITIALIZED) {982 idx++;983 }984 c = c._debugChunk;985 }986 // Initializing the model for the first time.987 initializeModelChunk(debugChunk);988 const initializedChunk = debugChunk as any as SomeChunk<any>;989 switch (initializedChunk.status) {990 case INITIALIZED: {991 debugInfo[idx] = initializeDebugInfo(992 response,993 initializedChunk.value,994 );995 break;996 }997 case BLOCKED:998 case PENDING: {999 waitForReference(1000 initializedChunk,1001 debugInfo,1002 '' + idx,1003 response,1004 initializeDebugInfo,1005 [''], // path1006 true,1007 );1008 break;1009 }1010 default:1011 throw initializedChunk.reason;1012 }1013 } else {1014 switch (debugChunk.status) {1015 case INITIALIZED: {1016 // Already done.1017 break;1018 }1019 case BLOCKED:1020 case PENDING: {1021 // Signal to the caller that we need to wait.1022 waitForReference(1023 debugChunk,1024 {}, // noop, since we'll have already added an entry to debug info1025 'debug', // noop, but we need it to not be empty string since that indicates the root object1026 response,1027 initializeDebugInfo,1028 [''], // path1029 true,1030 );1031 break;1032 }1033 default:1034 throw debugChunk.reason;1035 }1036 }1037 } catch (error) {1038 triggerErrorOnChunk(response, chunk, error);1039 } finally {1040 isInitializingDebugInfo = prevIsInitializingDebugInfo;1041 }1042 }1043}10441045function initializeModelChunk<T>(chunk: ResolvedModelChunk<T>): void {1046 const prevHandler = initializingHandler;1047 const prevChunk = initializingChunk;1048 initializingHandler = null;10491050 const resolvedModel = chunk.value;1051 const response = chunk.reason;10521053 // We go to the BLOCKED state until we've fully resolved this.1054 // We do this before parsing in case we try to initialize the same chunk1055 // while parsing the model. Such as in a cyclic reference.1056 const cyclicChunk: BlockedChunk<T> = chunk as any;1057 cyclicChunk.status = BLOCKED;1058 cyclicChunk.value = null;1059 cyclicChunk.reason = null;10601061 if ((enableProfilerTimer && enableComponentPerformanceTrack) || __DEV__) {1062 initializingChunk = cyclicChunk;1063 }10641065 if (__DEV__) {1066 // Initialize any debug info and block the initializing chunk on any1067 // unresolved entries.1068 initializeDebugChunk(response, chunk);1069 // TODO: The chunk might have transitioned to ERRORED now.1070 // Should we return early if that happens?1071 }10721073 try {1074 const value: T = parseModel(response, resolvedModel);1075 // Invoke any listeners added while resolving this model. I.e. cyclic1076 // references. This may or may not fully resolve the model depending on1077 // if they were blocked.1078 const resolveListeners = cyclicChunk.value;1079 if (resolveListeners !== null) {1080 cyclicChunk.value = null;1081 cyclicChunk.reason = null;1082 for (let i = 0; i < resolveListeners.length; i++) {1083 const listener = resolveListeners[i];1084 if (typeof listener === 'function') {1085 listener(value);1086 } else {1087 fulfillReference(response, listener, value, cyclicChunk);1088 }1089 }1090 }1091 if (initializingHandler !== null) {1092 if (initializingHandler.errored) {1093 throw initializingHandler.reason;1094 }1095 if (initializingHandler.deps > 0) {1096 // We discovered new dependencies on modules that are not yet resolved.1097 // We have to keep the BLOCKED state until they're resolved.1098 initializingHandler.value = value;1099 initializingHandler.chunk = cyclicChunk;1100 return;1101 }1102 }1103 const initializedChunk: InitializedChunk<T> = chunk as any;1104 initializedChunk.status = INITIALIZED;1105 initializedChunk.value = value;1106 initializedChunk.reason = null;11071108 if (__DEV__) {1109 processChunkDebugInfo(response, initializedChunk, value);1110 }1111 } catch (error) {1112 const erroredChunk: ErroredChunk<T> = chunk as any;1113 erroredChunk.status = ERRORED;1114 erroredChunk.reason = error;1115 } finally {1116 initializingHandler = prevHandler;1117 if ((enableProfilerTimer && enableComponentPerformanceTrack) || __DEV__) {1118 initializingChunk = prevChunk;1119 }1120 }1121}11221123function initializeModuleChunk<T>(chunk: ResolvedModuleChunk<T>): void {1124 try {1125 const value: T = requireModule(chunk.value);1126 const initializedChunk: InitializedChunk<T> = chunk as any;1127 initializedChunk.status = INITIALIZED;1128 initializedChunk.value = value;1129 initializedChunk.reason = null;1130 } catch (error) {1131 const erroredChunk: ErroredChunk<T> = chunk as any;1132 erroredChunk.status = ERRORED;1133 erroredChunk.reason = error;1134 }1135}11361137// Report that any missing chunks in the model is now going to throw this1138// error upon read. Also notify any pending promises.1139export function reportGlobalError(1140 weakResponse: WeakResponse,1141 error: Error,1142): void {1143 if (hasGCedResponse(weakResponse)) {1144 // Ignore close signal if we are not awaiting any more pending chunks.1145 return;1146 }1147 const response = unwrapWeakResponse(weakResponse);1148 response._closed = true;1149 response._closedReason = error;1150 response._chunks.forEach(chunk => {1151 // If this chunk was already resolved or errored, it won't1152 // trigger an error but if it wasn't then we need to1153 // because we won't be getting any new data to resolve it.1154 if (chunk.status === PENDING) {1155 triggerErrorOnChunk(response, chunk, error);1156 } else if (chunk.status === INITIALIZED && chunk.reason !== null) {1157 chunk.reason.error(error);1158 }1159 });1160 if (__DEV__) {1161 const debugChannel = response._debugChannel;1162 if (debugChannel !== undefined) {1163 // If we don't have any more ways of reading data, we don't have to send1164 // any more neither. So we close the writable side.1165 closeDebugChannel(debugChannel);1166 response._debugChannel = undefined;1167 // Make sure the debug channel is not closed a second time when the1168 // Response gets GC:ed.1169 if (debugChannelRegistry !== null) {1170 debugChannelRegistry.unregister(response);1171 }1172 }1173 }1174}11751176function nullRefGetter() {1177 if (__DEV__) {1178 return null;1179 }1180}11811182function getIOInfoTaskName(ioInfo: ReactIOInfo): string {1183 return ioInfo.name || 'unknown';1184}11851186function getAsyncInfoTaskName(asyncInfo: ReactAsyncInfo): string {1187 return 'await ' + getIOInfoTaskName(asyncInfo.awaited);1188}11891190function getServerComponentTaskName(componentInfo: ReactComponentInfo): string {1191 return '<' + (componentInfo.name || '...') + '>';1192}11931194function getTaskName(type: mixed): string {1195 if (type === REACT_FRAGMENT_TYPE) {1196 return '<>';1197 }1198 if (typeof type === 'function') {1199 // This is a function so it must have been a Client Reference that resolved to1200 // a function. We use "use client" to indicate that this is the boundary into1201 // the client. There should only be one for any given owner chain.1202 return '"use client"';1203 }1204 if (1205 typeof type === 'object' &&1206 type !== null &&1207 type.$$typeof === REACT_LAZY_TYPE1208 ) {1209 if (type._init === readChunk) {1210 // This is a lazy node created by Flight. It is probably a client reference.1211 // We use the "use client" string to indicate that this is the boundary into1212 // the client. There will only be one for any given owner chain.1213 return '"use client"';1214 }1215 // We don't want to eagerly initialize the initializer in DEV mode so we can't1216 // call it to extract the type so we don't know the type of this component.1217 return '<...>';1218 }1219 try {1220 const name = getComponentNameFromType(type);1221 return name ? '<' + name + '>' : '<...>';1222 } catch (x) {1223 return '<...>';1224 }1225}12261227function initializeElement(1228 response: Response,1229 element: any,1230 lazyNode: null | LazyComponent<1231 React$Element<any>,1232 SomeChunk<React$Element<any>>,1233 >,1234): void {1235 if (!__DEV__) {1236 return;1237 }1238 const stack = element._debugStack;1239 const owner = element._owner;1240 if (owner === null) {1241 element._owner = response._debugRootOwner;1242 }1243 let env = response._rootEnvironmentName;1244 if (owner !== null && owner.env != null) {1245 // Interestingly we don't actually have the environment name of where1246 // this JSX was created if it doesn't have an owner but if it does1247 // it must be the same environment as the owner. We could send it separately1248 // but it seems a bit unnecessary for this edge case.1249 env = owner.env;1250 }1251 let normalizedStackTrace: null | Error = null;1252 if (owner === null && response._debugRootStack != null) {1253 // We override the stack if we override the owner since the stack where the root JSX1254 // was created on the server isn't very useful but where the request was made is.1255 normalizedStackTrace = response._debugRootStack;1256 } else if (stack !== null) {1257 // We create a fake stack and then create an Error object inside of it.1258 // This means that the stack trace is now normalized into the native format1259 // of the browser and the stack frames will have been registered with1260 // source mapping information.1261 // This can unfortunately happen within a user space callstack which will1262 // remain on the stack.1263 normalizedStackTrace = createFakeJSXCallStackInDEV(response, stack, env);1264 }1265 element._debugStack = normalizedStackTrace;1266 let task: null | ConsoleTask = null;1267 if (supportsCreateTask && stack !== null) {1268 const createTaskFn = (console as any).createTask.bind(1269 console,1270 getTaskName(element.type),1271 );1272 const callStack = buildFakeCallStack(1273 response,1274 stack,1275 env,1276 false,1277 createTaskFn,1278 );1279 // This owner should ideally have already been initialized to avoid getting1280 // user stack frames on the stack.1281 const ownerTask =1282 owner === null ? null : initializeFakeTask(response, owner);1283 if (ownerTask === null) {1284 const rootTask = response._debugRootTask;1285 if (rootTask != null) {1286 task = rootTask.run(callStack);1287 } else {1288 task = callStack();1289 }1290 } else {1291 task = ownerTask.run(callStack);1292 }1293 }1294 element._debugTask = task;12951296 // This owner should ideally have already been initialized to avoid getting1297 // user stack frames on the stack.1298 if (owner !== null) {1299 initializeFakeStack(response, owner);1300 }13011302 if (lazyNode !== null) {1303 // In case the JSX runtime has validated the lazy type as a static child, we1304 // need to transfer this information to the element.1305 if (1306 lazyNode._store &&1307 lazyNode._store.validated &&1308 !element._store.validated1309 ) {1310 element._store.validated = lazyNode._store.validated;1311 }13121313 // If the lazy node is initialized, we move its debug info to the inner1314 // value.1315 if (lazyNode._payload.status === INITIALIZED && lazyNode._debugInfo) {1316 const debugInfo = lazyNode._debugInfo.splice(0);1317 if (element._debugInfo) {1318 // $FlowFixMe[method-unbinding]1319 element._debugInfo.unshift.apply(element._debugInfo, debugInfo);1320 } else {1321 Object.defineProperty(element, '_debugInfo', {1322 configurable: false,1323 enumerable: false,1324 writable: true,1325 value: debugInfo,1326 });1327 }1328 }1329 }13301331 // TODO: We should be freezing the element but currently, we might write into1332 // _debugInfo later. We could move it into _store which remains mutable.1333 Object.freeze(element.props);1334}13351336function createElement(1337 response: Response,1338 type: mixed,1339 key: mixed,1340 props: mixed,1341 owner: ?ReactComponentInfo, // DEV-only1342 stack: ?ReactStackTrace, // DEV-only1343 validated: 0 | 1 | 2, // DEV-only1344):1345 | React$Element<any>1346 | LazyComponent<React$Element<any>, SomeChunk<React$Element<any>>> {1347 let element: any;1348 if (__DEV__) {1349 // `ref` is non-enumerable in dev1350 element = {1351 $$typeof: REACT_ELEMENT_TYPE,1352 type,1353 key,1354 props,1355 _owner: owner === undefined ? null : owner,1356 } as any;1357 Object.defineProperty(element, 'ref', {1358 enumerable: false,1359 get: nullRefGetter,1360 });1361 } else {1362 element = {1363 // This tag allows us to uniquely identify this as a React Element1364 $$typeof: REACT_ELEMENT_TYPE,13651366 type,1367 key,1368 ref: null,1369 props,1370 } as any;1371 }13721373 if (__DEV__) {1374 // We don't really need to add any of these but keeping them for good measure.1375 // Unfortunately, _store is enumerable in jest matchers so for equality to1376 // work, I need to keep it or make _store non-enumerable in the other file.1377 element._store = {} as {1378 validated?: number,1379 };1380 Object.defineProperty(element._store, 'validated', {1381 configurable: false,1382 enumerable: false,1383 writable: true,1384 value: validated, // Whether the element has already been validated on the server.1385 });1386 // debugInfo contains Server Component debug information.1387 Object.defineProperty(element, '_debugInfo', {1388 configurable: false,1389 enumerable: false,1390 writable: true,1391 value: null,1392 });1393 Object.defineProperty(element, '_debugStack', {1394 configurable: false,1395 enumerable: false,1396 writable: true,1397 value: stack === undefined ? null : stack,1398 });1399 Object.defineProperty(element, '_debugTask', {1400 configurable: false,1401 enumerable: false,1402 writable: true,1403 value: null,1404 });1405 }14061407 if (initializingHandler !== null) {1408 const handler = initializingHandler;1409 // We pop the stack to the previous outer handler before leaving the Element.1410 // This is effectively the complete phase.1411 initializingHandler = handler.parent;1412 if (handler.errored) {1413 // Something errored inside this Element's props. We can turn this Element1414 // into a Lazy so that we can still render up until that Lazy is rendered.1415 const erroredChunk: ErroredChunk<React$Element<any>> = createErrorChunk(1416 response,1417 handler.reason,1418 );1419 if (__DEV__) {1420 initializeElement(response, element, null);1421 // Conceptually the error happened inside this Element but right before1422 // it was rendered. We don't have a client side component to render but1423 // we can add some DebugInfo to explain that this was conceptually a1424 // Server side error that errored inside this element. That way any stack1425 // traces will point to the nearest JSX that errored - e.g. during1426 // serialization.1427 const erroredComponent: ReactComponentInfo = {1428 name: getComponentNameFromType(element.type) || '',1429 owner: element._owner,1430 };1431 // $FlowFixMe[cannot-write]1432 erroredComponent.debugStack = element._debugStack;1433 if (supportsCreateTask) {1434 // $FlowFixMe[cannot-write]1435 erroredComponent.debugTask = element._debugTask;1436 }1437 erroredChunk._debugInfo = [erroredComponent];1438 }1439 return createLazyChunkWrapper(erroredChunk, validated);1440 }1441 if (handler.deps > 0) {1442 // We have blocked references inside this Element but we can turn this into1443 // a Lazy node referencing this Element to let everything around it proceed.1444 const blockedChunk: BlockedChunk<React$Element<any>> =1445 createBlockedChunk(response);1446 handler.value = element;1447 handler.chunk = blockedChunk;1448 const lazyNode = createLazyChunkWrapper(blockedChunk, validated);1449 if (__DEV__) {1450 // After we have initialized any blocked references, initialize stack etc.1451 const init = initializeElement.bind(null, response, element, lazyNode);1452 blockedChunk.then(init, init);1453 }1454 return lazyNode;1455 }1456 }1457 if (__DEV__) {1458 initializeElement(response, element, null);1459 }14601461 return element;1462}14631464function createLazyChunkWrapper<T>(1465 chunk: SomeChunk<T>,1466 validated: 0 | 1 | 2, // DEV-only1467): LazyComponent<T, SomeChunk<T>> {1468 const lazyType: LazyComponent<T, SomeChunk<T>> = {1469 $$typeof: REACT_LAZY_TYPE,1470 _payload: chunk,1471 _init: readChunk,1472 };1473 if (__DEV__) {1474 // Forward the live array1475 lazyType._debugInfo = chunk._debugInfo;1476 // Initialize a store for key validation by the JSX runtime.1477 lazyType._store = {validated: validated};1478 }1479 return lazyType;1480}14811482function getChunk(response: Response, id: number): SomeChunk<any> {1483 const chunks = response._chunks;1484 let chunk = chunks.get(id);1485 if (!chunk) {1486 if (response._closed) {1487 if (response._allowPartialStream) {1488 // For partial streams, chunks accessed after close should be HALTED1489 // (never resolve).1490 chunk = createPendingChunk(response);1491 const haltedChunk: HaltedChunk<any> = chunk as any;1492 haltedChunk.status = HALTED;1493 haltedChunk.value = null;1494 haltedChunk.reason = null;1495 } else {1496 // We have already errored the response and we're not going to get1497 // anything more streaming in so this will immediately error.1498 chunk = createErrorChunk(response, response._closedReason);1499 }1500 } else {1501 chunk = createPendingChunk(response);1502 }1503 chunks.set(id, chunk);1504 }1505 return chunk;1506}15071508function fulfillReference(1509 response: Response,1510 reference: InitializationReference,1511 value: any,1512 fulfilledChunk: SomeChunk<any>,1513): void {1514 const {handler, parentObject, key, map, path} = reference;15151516 try {1517 for (let i = 1; i < path.length; i++) {1518 while (1519 typeof value === 'object' &&1520 value !== null &&1521 value.$$typeof === REACT_LAZY_TYPE1522 ) {1523 // We never expect to see a Lazy node on this path because we encode those as1524 // separate models. This must mean that we have inserted an extra lazy node1525 // e.g. to replace a blocked element. We must instead look for it inside.1526 const referencedChunk: SomeChunk<any> = value._payload;1527 if (referencedChunk === handler.chunk) {1528 // This is a reference to the thing we're currently blocking. We can peak1529 // inside of it to get the value.1530 value = handler.value;1531 continue;1532 } else {1533 switch (referencedChunk.status) {1534 case RESOLVED_MODEL:1535 initializeModelChunk(referencedChunk);1536 break;1537 case RESOLVED_MODULE:1538 initializeModuleChunk(referencedChunk);1539 break;1540 }1541 switch (referencedChunk.status) {1542 case INITIALIZED: {1543 value = referencedChunk.value;1544 continue;1545 }1546 case BLOCKED: {1547 // It is possible that we're blocked on our own chunk if it's a cycle.1548 // Before adding the listener to the inner chunk, let's check if it would1549 // result in a cycle.1550 const cyclicHandler = resolveBlockedCycle(1551 referencedChunk,1552 reference,1553 );1554 if (cyclicHandler !== null) {1555 // This reference points back to this chunk. We can resolve the cycle by1556 // using the value from that handler.1557 value = cyclicHandler.value;1558 continue;1559 }1560 // Fallthrough1561 }1562 case PENDING: {1563 // If we're not yet initialized we need to skip what we've already drilled1564 // through and then wait for the next value to become available.1565 path.splice(0, i - 1);1566 // Add "listener" to our new chunk dependency.1567 if (referencedChunk.value === null) {1568 referencedChunk.value = [reference];1569 } else {1570 referencedChunk.value.push(reference);1571 }1572 if (referencedChunk.reason === null) {1573 referencedChunk.reason = [reference];1574 } else {1575 referencedChunk.reason.push(reference);1576 }1577 return;1578 }1579 case HALTED: {1580 // Do nothing. We couldn't fulfill.1581 // TODO: Mark downstreams as halted too.1582 return;1583 }1584 default: {1585 rejectReference(1586 response,1587 reference.handler,1588 referencedChunk.reason,1589 );1590 return;1591 }1592 }1593 }1594 }1595 const name = path[i];1596 if (1597 typeof value === 'object' &&1598 value !== null &&1599 hasOwnProperty.call(value, name)1600 ) {1601 value = value[name];1602 } else {1603 throw new Error('Invalid reference.');1604 }1605 }16061607 while (1608 typeof value === 'object' &&1609 value !== null &&1610 value.$$typeof === REACT_LAZY_TYPE1611 ) {1612 // If what we're referencing is a Lazy it must be because we inserted one as a virtual node1613 // while it was blocked by other data. If it's no longer blocked, we can unwrap it.1614 const referencedChunk: SomeChunk<any> = value._payload;1615 if (referencedChunk === handler.chunk) {1616 // This is a reference to the thing we're currently blocking. We can peak1617 // inside of it to get the value.1618 value = handler.value;1619 continue;1620 } else {1621 switch (referencedChunk.status) {1622 case RESOLVED_MODEL:1623 initializeModelChunk(referencedChunk);1624 break;1625 case RESOLVED_MODULE:1626 initializeModuleChunk(referencedChunk);1627 break;1628 }1629 switch (referencedChunk.status) {1630 case INITIALIZED: {1631 value = referencedChunk.value;1632 continue;1633 }1634 }1635 }1636 break;1637 }16381639 const mappedValue = map(response, value, parentObject, key);1640 if (key !== __PROTO__) {1641 parentObject[key] = mappedValue;1642 }16431644 // If this is the root object for a model reference, where `handler.value`1645 // is a stale `null`, the resolved value can be used directly.1646 if (key === '' && handler.value === null) {1647 handler.value = mappedValue;1648 }16491650 // If the parent object is an unparsed React element tuple, we also need to1651 // update the props and owner of the parsed element object (i.e.1652 // handler.value).1653 if (1654 parentObject[0] === REACT_ELEMENT_TYPE &&1655 typeof handler.value === 'object' &&1656 handler.value !== null &&1657 handler.value.$$typeof === REACT_ELEMENT_TYPE1658 ) {1659 const element: any = handler.value;1660 switch (key) {1661 case '3':1662 if (__DEV__) {1663 transferReferencedDebugInfo(handler.chunk, fulfilledChunk);1664 }1665 element.props = mappedValue;1666 break;1667 case '4':1668 // This path doesn't call transferReferencedDebugInfo because this reference is to a debug chunk.1669 if (__DEV__) {1670 element._owner = mappedValue;1671 }1672 break;1673 case '5':1674 // This path doesn't call transferReferencedDebugInfo because this reference is to a debug chunk.1675 if (__DEV__) {1676 element._debugStack = mappedValue;1677 }1678 break;1679 default:1680 if (__DEV__) {1681 transferReferencedDebugInfo(handler.chunk, fulfilledChunk);1682 }1683 break;1684 }1685 } else if (__DEV__ && !reference.isDebug) {1686 transferReferencedDebugInfo(handler.chunk, fulfilledChunk);1687 }1688 } catch (error) {1689 rejectReference(response, reference.handler, error);1690 return;1691 }16921693 handler.deps--;16941695 if (handler.deps === 0) {1696 const chunk = handler.chunk;1697 if (chunk === null || chunk.status !== BLOCKED) {1698 return;1699 }1700 const resolveListeners = chunk.value;1701 const initializedChunk: InitializedChunk<any> = chunk as any;1702 initializedChunk.status = INITIALIZED;1703 initializedChunk.value = handler.value;1704 initializedChunk.reason = handler.reason; // Used by streaming chunks1705 if (resolveListeners !== null) {1706 wakeChunk(response, resolveListeners, handler.value, initializedChunk);1707 } else {1708 if (__DEV__) {1709 processChunkDebugInfo(response, initializedChunk, handler.value);1710 }1711 }1712 }1713}17141715function rejectReference(1716 response: Response,1717 handler: InitializationHandler,1718 error: mixed,1719): void {1720 if (handler.errored) {1721 // We've already errored. We could instead build up an AggregateError1722 // but if there are multiple errors we just take the first one like1723 // Promise.all.1724 return;1725 }1726 const blockedValue = handler.value;1727 handler.errored = true;1728 handler.value = null;1729 handler.reason = error;1730 const chunk = handler.chunk;1731 if (chunk === null || chunk.status !== BLOCKED) {1732 return;1733 }17341735 if (__DEV__) {1736 if (1737 typeof blockedValue === 'object' &&1738 blockedValue !== null &&1739 blockedValue.$$typeof === REACT_ELEMENT_TYPE1740 ) {1741 const element = blockedValue;1742 // Conceptually the error happened inside this Element but right before1743 // it was rendered. We don't have a client side component to render but1744 // we can add some DebugInfo to explain that this was conceptually a1745 // Server side error that errored inside this element. That way any stack1746 // traces will point to the nearest JSX that errored - e.g. during1747 // serialization.1748 const erroredComponent: ReactComponentInfo = {1749 name: getComponentNameFromType(element.type) || '',1750 owner: element._owner,1751 };1752 // $FlowFixMe[cannot-write]1753 erroredComponent.debugStack = element._debugStack;1754 if (supportsCreateTask) {1755 // $FlowFixMe[cannot-write]1756 erroredComponent.debugTask = element._debugTask;1757 }1758 chunk._debugInfo.push(erroredComponent);1759 }1760 }17611762 triggerErrorOnChunk(response, chunk, error);1763}17641765function waitForReference<T>(1766 referencedChunk: PendingChunk<T> | BlockedChunk<T>,1767 parentObject: Object,1768 key: string,1769 response: Response,1770 map: (response: Response, model: any, parentObject: Object, key: string) => T,1771 path: Array<string>,1772 isAwaitingDebugInfo: boolean, // DEV-only1773): T {1774 if (1775 __DEV__ &&1776 (response._debugChannel === undefined ||1777 !response._debugChannel.hasReadable)1778 ) {1779 if (1780 referencedChunk.status === PENDING &&1781 parentObject[0] === REACT_ELEMENT_TYPE &&1782 (key === '4' || key === '5')1783 ) {1784 // If the parent object is an unparsed React element tuple, and this is a reference1785 // to the owner or debug stack. Then we expect the chunk to have been emitted earlier1786 // in the stream. It might be blocked on other things but chunk should no longer be pending.1787 // If it's still pending that suggests that it was referencing an object in the debug1788 // channel, but no debug channel was wired up so it's missing. In this case we can just1789 // drop the debug info instead of halting the whole stream.1790 return null as any;1791 }1792 }17931794 let handler: InitializationHandler;1795 if (initializingHandler) {1796 handler = initializingHandler;1797 handler.deps++;1798 } else {1799 handler = initializingHandler = {1800 parent: null,1801 chunk: null,1802 value: null,1803 reason: null,1804 deps: 1,1805 errored: false,1806 };1807 }18081809 const reference: InitializationReference = {1810 handler,1811 parentObject,1812 key,1813 map,1814 path,1815 };1816 if (__DEV__) {1817 reference.isDebug = isAwaitingDebugInfo;1818 }18191820 // Add "listener".1821 if (referencedChunk.value === null) {1822 referencedChunk.value = [reference];1823 } else {1824 referencedChunk.value.push(reference);1825 }1826 if (referencedChunk.reason === null) {1827 referencedChunk.reason = [reference];1828 } else {1829 referencedChunk.reason.push(reference);1830 }18311832 // Return a place holder value for now.1833 return null as any;1834}18351836function loadServerReference<A: Iterable<any>, T>(1837 response: Response,1838 metaData: {1839 id: any,1840 bound: null | Thenable<Array<any>>,1841 name?: string, // DEV-only1842 env?: string, // DEV-only1843 location?: ReactFunctionLocation, // DEV-only1844 },1845 parentObject: Object,1846 key: string,1847): (...A) => Promise<T> {1848 if (!response._serverReferenceConfig) {1849 // In the normal case, we can't load this Server Reference in the current environment and1850 // we just return a proxy to it.1851 return createBoundServerReference(1852 metaData,1853 response._callServer,1854 response._encodeFormAction,1855 __DEV__ ? response._debugFindSourceMapURL : undefined,1856 );1857 }1858 // If we have a module mapping we can load the real version of this Server Reference.1859 const serverReference: ClientReference<T> =1860 resolveServerReference<$FlowFixMe>(1861 response._serverReferenceConfig,1862 metaData.id,1863 );18641865 let promise: null | Thenable<any> = preloadModule(serverReference);1866 if (!promise) {1867 if (!metaData.bound) {1868 const resolvedValue = requireModule(serverReference) as any;1869 registerBoundServerReference(1870 resolvedValue,1871 metaData.id,1872 metaData.bound,1873 response._encodeFormAction,1874 );1875 return resolvedValue;1876 } else {1877 promise = Promise.resolve(metaData.bound);1878 }1879 } else if (metaData.bound) {1880 promise = Promise.all([promise, metaData.bound]);1881 }18821883 let handler: InitializationHandler;1884 if (initializingHandler) {1885 handler = initializingHandler;1886 handler.deps++;1887 } else {1888 handler = initializingHandler = {1889 parent: null,1890 chunk: null,1891 value: null,1892 reason: null,1893 deps: 1,1894 errored: false,1895 };1896 }18971898 function fulfill(): void {1899 let resolvedValue = requireModule(serverReference) as any;19001901 if (metaData.bound) {1902 // This promise is coming from us and should have initilialized by now.1903 const boundArgs: Array<any> = (metaData.bound as any).value.slice(0);1904 boundArgs.unshift(null); // this1905 resolvedValue = resolvedValue.bind.apply(resolvedValue, boundArgs);1906 }19071908 registerBoundServerReference(1909 resolvedValue,1910 metaData.id,1911 metaData.bound,1912 response._encodeFormAction,1913 );19141915 if (key !== __PROTO__) {1916 parentObject[key] = resolvedValue;1917 }19181919 // If this is the root object for a model reference, where `handler.value`1920 // is a stale `null`, the resolved value can be used directly.1921 if (key === '' && handler.value === null) {1922 handler.value = resolvedValue;1923 }19241925 // If the parent object is an unparsed React element tuple, we also need to1926 // update the props and owner of the parsed element object (i.e.1927 // handler.value).1928 if (1929 parentObject[0] === REACT_ELEMENT_TYPE &&1930 typeof handler.value === 'object' &&1931 handler.value !== null &&1932 handler.value.$$typeof === REACT_ELEMENT_TYPE1933 ) {1934 const element: any = handler.value;1935 switch (key) {1936 case '3':1937 element.props = resolvedValue;1938 break;1939 case '4':1940 if (__DEV__) {1941 element._owner = resolvedValue;1942 }1943 break;1944 }1945 }19461947 handler.deps--;19481949 if (handler.deps === 0) {1950 const chunk = handler.chunk;1951 if (chunk === null || chunk.status !== BLOCKED) {1952 return;1953 }1954 const resolveListeners = chunk.value;1955 const initializedChunk: InitializedChunk<T> = chunk as any;1956 initializedChunk.status = INITIALIZED;1957 initializedChunk.value = handler.value;1958 initializedChunk.reason = null;1959 if (resolveListeners !== null) {1960 wakeChunk(response, resolveListeners, handler.value, initializedChunk);1961 } else {1962 if (__DEV__) {1963 processChunkDebugInfo(response, initializedChunk, handler.value);1964 }1965 }1966 }1967 }19681969 function reject(error: mixed): void {1970 if (handler.errored) {1971 // We've already errored. We could instead build up an AggregateError1972 // but if there are multiple errors we just take the first one like1973 // Promise.all.1974 return;1975 }1976 const blockedValue = handler.value;1977 handler.errored = true;1978 handler.value = null;1979 handler.reason = error;1980 const chunk = handler.chunk;1981 if (chunk === null || chunk.status !== BLOCKED) {1982 return;1983 }19841985 if (__DEV__) {1986 if (1987 typeof blockedValue === 'object' &&1988 blockedValue !== null &&1989 blockedValue.$$typeof === REACT_ELEMENT_TYPE1990 ) {1991 const element = blockedValue;1992 // Conceptually the error happened inside this Element but right before1993 // it was rendered. We don't have a client side component to render but1994 // we can add some DebugInfo to explain that this was conceptually a1995 // Server side error that errored inside this element. That way any stack1996 // traces will point to the nearest JSX that errored - e.g. during1997 // serialization.1998 const erroredComponent: ReactComponentInfo = {1999 name: getComponentNameFromType(element.type) || '',2000 owner: element._owner,
Findings
✓ No findings reported for this file.