1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @flow
    
  8.  */
    
  9. 
    
  10. import type {
    
  11.   Destination,
    
  12.   Chunk,
    
  13.   PrecomputedChunk,
    
  14. } from './ReactServerStreamConfig';
    
  15. import type {
    
  16.   ReactNodeList,
    
  17.   ReactContext,
    
  18.   ReactProviderType,
    
  19.   OffscreenMode,
    
  20.   Wakeable,
    
  21.   Thenable,
    
  22.   ReactFormState,
    
  23. } from 'shared/ReactTypes';
    
  24. import type {LazyComponent as LazyComponentType} from 'react/src/ReactLazy';
    
  25. import type {
    
  26.   RenderState,
    
  27.   ResumableState,
    
  28.   FormatContext,
    
  29.   BoundaryResources,
    
  30. } from './ReactFizzConfig';
    
  31. import type {ContextSnapshot} from './ReactFizzNewContext';
    
  32. import type {ComponentStackNode} from './ReactFizzComponentStack';
    
  33. import type {TreeContext} from './ReactFizzTreeContext';
    
  34. import type {ThenableState} from './ReactFizzThenable';
    
  35. 
    
  36. import {
    
  37.   scheduleWork,
    
  38.   beginWriting,
    
  39.   writeChunk,
    
  40.   writeChunkAndReturn,
    
  41.   completeWriting,
    
  42.   flushBuffered,
    
  43.   close,
    
  44.   closeWithError,
    
  45. } from './ReactServerStreamConfig';
    
  46. import {
    
  47.   writeCompletedRoot,
    
  48.   writePlaceholder,
    
  49.   writeStartCompletedSuspenseBoundary,
    
  50.   writeStartPendingSuspenseBoundary,
    
  51.   writeStartClientRenderedSuspenseBoundary,
    
  52.   writeEndCompletedSuspenseBoundary,
    
  53.   writeEndPendingSuspenseBoundary,
    
  54.   writeEndClientRenderedSuspenseBoundary,
    
  55.   writeStartSegment,
    
  56.   writeEndSegment,
    
  57.   writeClientRenderBoundaryInstruction,
    
  58.   writeCompletedBoundaryInstruction,
    
  59.   writeCompletedSegmentInstruction,
    
  60.   pushTextInstance,
    
  61.   pushStartInstance,
    
  62.   pushEndInstance,
    
  63.   pushStartCompletedSuspenseBoundary,
    
  64.   pushEndCompletedSuspenseBoundary,
    
  65.   pushSegmentFinale,
    
  66.   getChildFormatContext,
    
  67.   writeResourcesForBoundary,
    
  68.   writePreamble,
    
  69.   writeHoistables,
    
  70.   writePostamble,
    
  71.   hoistResources,
    
  72.   setCurrentlyRenderingBoundaryResourcesTarget,
    
  73.   createBoundaryResources,
    
  74.   prepareHostDispatcher,
    
  75.   supportsRequestStorage,
    
  76.   requestStorage,
    
  77.   pushFormStateMarkerIsMatching,
    
  78.   pushFormStateMarkerIsNotMatching,
    
  79.   resetResumableState,
    
  80. } from './ReactFizzConfig';
    
  81. import {
    
  82.   constructClassInstance,
    
  83.   mountClassInstance,
    
  84. } from './ReactFizzClassComponent';
    
  85. import {
    
  86.   getMaskedContext,
    
  87.   processChildContext,
    
  88.   emptyContextObject,
    
  89. } from './ReactFizzContext';
    
  90. import {
    
  91.   readContext,
    
  92.   rootContextSnapshot,
    
  93.   switchContext,
    
  94.   getActiveContext,
    
  95.   pushProvider,
    
  96.   popProvider,
    
  97. } from './ReactFizzNewContext';
    
  98. import {
    
  99.   prepareToUseHooks,
    
  100.   finishHooks,
    
  101.   checkDidRenderIdHook,
    
  102.   resetHooksState,
    
  103.   HooksDispatcher,
    
  104.   currentResumableState,
    
  105.   setCurrentResumableState,
    
  106.   getThenableStateAfterSuspending,
    
  107.   unwrapThenable,
    
  108.   getFormStateCount,
    
  109.   getFormStateMatchingIndex,
    
  110. } from './ReactFizzHooks';
    
  111. import {DefaultCacheDispatcher} from './ReactFizzCache';
    
  112. import {getStackByComponentStackNode} from './ReactFizzComponentStack';
    
  113. import {emptyTreeContext, pushTreeContext} from './ReactFizzTreeContext';
    
  114. 
    
  115. import {
    
  116.   getIteratorFn,
    
  117.   REACT_ELEMENT_TYPE,
    
  118.   REACT_PORTAL_TYPE,
    
  119.   REACT_LAZY_TYPE,
    
  120.   REACT_SUSPENSE_TYPE,
    
  121.   REACT_LEGACY_HIDDEN_TYPE,
    
  122.   REACT_DEBUG_TRACING_MODE_TYPE,
    
  123.   REACT_STRICT_MODE_TYPE,
    
  124.   REACT_PROFILER_TYPE,
    
  125.   REACT_SUSPENSE_LIST_TYPE,
    
  126.   REACT_FRAGMENT_TYPE,
    
  127.   REACT_FORWARD_REF_TYPE,
    
  128.   REACT_MEMO_TYPE,
    
  129.   REACT_PROVIDER_TYPE,
    
  130.   REACT_CONTEXT_TYPE,
    
  131.   REACT_SERVER_CONTEXT_TYPE,
    
  132.   REACT_SCOPE_TYPE,
    
  133.   REACT_OFFSCREEN_TYPE,
    
  134.   REACT_POSTPONE_TYPE,
    
  135. } from 'shared/ReactSymbols';
    
  136. import ReactSharedInternals from 'shared/ReactSharedInternals';
    
  137. import {
    
  138.   disableLegacyContext,
    
  139.   disableModulePatternComponents,
    
  140.   enableScopeAPI,
    
  141.   enableSuspenseAvoidThisFallbackFizz,
    
  142.   enableFloat,
    
  143.   enableCache,
    
  144.   enablePostpone,
    
  145. } from 'shared/ReactFeatureFlags';
    
  146. 
    
  147. import assign from 'shared/assign';
    
  148. import getComponentNameFromType from 'shared/getComponentNameFromType';
    
  149. import isArray from 'shared/isArray';
    
  150. import {SuspenseException, getSuspendedThenable} from './ReactFizzThenable';
    
  151. import type {Postpone} from 'react/src/ReactPostpone';
    
  152. 
    
  153. const ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
    
  154. const ReactCurrentCache = ReactSharedInternals.ReactCurrentCache;
    
  155. const ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
    
  156. 
    
  157. // Linked list representing the identity of a component given the component/tag name and key.
    
  158. // The name might be minified but we assume that it's going to be the same generated name. Typically
    
  159. // because it's just the same compiled output in practice.
    
  160. export type KeyNode = [
    
  161.   Root | KeyNode /* parent */,
    
  162.   string | null /* name */,
    
  163.   string | number /* key */,
    
  164. ];
    
  165. 
    
  166. type ResumeSlots =
    
  167.   | null // nothing to resume
    
  168.   | number // resume with segment ID at the root position
    
  169.   | {[index: number]: number}; // resume with segmentID at the index
    
  170. 
    
  171. type ReplaySuspenseBoundary = [
    
  172.   string | null /* name */,
    
  173.   string | number /* key */,
    
  174.   Array<ReplayNode> /* content keyed children */,
    
  175.   ResumeSlots /* content resumable slots */,
    
  176.   null | ReplayNode /* fallback content */,
    
  177.   number /* rootSegmentID */,
    
  178. ];
    
  179. 
    
  180. type ReplayNode =
    
  181.   | [
    
  182.       string | null /* name */,
    
  183.       string | number /* key */,
    
  184.       Array<ReplayNode> /* keyed children */,
    
  185.       ResumeSlots /* resumable slots */,
    
  186.     ]
    
  187.   | ReplaySuspenseBoundary;
    
  188. 
    
  189. type PostponedHoles = {
    
  190.   workingMap: Map<KeyNode, ReplayNode>,
    
  191.   rootNodes: Array<ReplayNode>,
    
  192.   rootSlots: ResumeSlots,
    
  193. };
    
  194. 
    
  195. type LegacyContext = {
    
  196.   [key: string]: any,
    
  197. };
    
  198. 
    
  199. const CLIENT_RENDERED = 4; // if it errors or infinitely suspends
    
  200. 
    
  201. type SuspenseBoundary = {
    
  202.   status: 0 | 1 | 4 | 5,
    
  203.   rootSegmentID: number,
    
  204.   errorDigest: ?string, // the error hash if it errors
    
  205.   errorMessage?: string, // the error string if it errors
    
  206.   errorComponentStack?: string, // the error component stack if it errors
    
  207.   parentFlushed: boolean,
    
  208.   pendingTasks: number, // when it reaches zero we can show this boundary's content
    
  209.   completedSegments: Array<Segment>, // completed but not yet flushed segments.
    
  210.   byteSize: number, // used to determine whether to inline children boundaries.
    
  211.   fallbackAbortableTasks: Set<Task>, // used to cancel task on the fallback if the boundary completes or gets canceled.
    
  212.   resources: BoundaryResources,
    
  213.   trackedContentKeyPath: null | KeyNode, // used to track the path for replay nodes
    
  214.   trackedFallbackNode: null | ReplayNode, // used to track the fallback for replay nodes
    
  215. };
    
  216. 
    
  217. type RenderTask = {
    
  218.   replay: null,
    
  219.   node: ReactNodeList,
    
  220.   childIndex: number,
    
  221.   ping: () => void,
    
  222.   blockedBoundary: Root | SuspenseBoundary,
    
  223.   blockedSegment: Segment, // the segment we'll write to
    
  224.   abortSet: Set<Task>, // the abortable set that this task belongs to
    
  225.   keyPath: Root | KeyNode, // the path of all parent keys currently rendering
    
  226.   formatContext: FormatContext, // the format's specific context (e.g. HTML/SVG/MathML)
    
  227.   legacyContext: LegacyContext, // the current legacy context that this task is executing in
    
  228.   context: ContextSnapshot, // the current new context that this task is executing in
    
  229.   treeContext: TreeContext, // the current tree context that this task is executing in
    
  230.   componentStack: null | ComponentStackNode, // DEV-only component stack
    
  231.   thenableState: null | ThenableState,
    
  232. };
    
  233. 
    
  234. type ReplaySet = {
    
  235.   nodes: Array<ReplayNode>, // the possible paths to follow down the replaying
    
  236.   slots: ResumeSlots, // slots to resume
    
  237.   pendingTasks: number, // tracks the number of tasks currently tracking this set of nodes
    
  238.   // if pending tasks reach zero but there are still nodes left, it means we couldn't find
    
  239.   // them all in the tree, so we need to abort and client render the boundary.
    
  240. };
    
  241. 
    
  242. type ReplayTask = {
    
  243.   replay: ReplaySet,
    
  244.   node: ReactNodeList,
    
  245.   childIndex: number,
    
  246.   ping: () => void,
    
  247.   blockedBoundary: Root | SuspenseBoundary,
    
  248.   blockedSegment: null, // we don't write to anything when we replay
    
  249.   abortSet: Set<Task>, // the abortable set that this task belongs to
    
  250.   keyPath: Root | KeyNode, // the path of all parent keys currently rendering
    
  251.   formatContext: FormatContext, // the format's specific context (e.g. HTML/SVG/MathML)
    
  252.   legacyContext: LegacyContext, // the current legacy context that this task is executing in
    
  253.   context: ContextSnapshot, // the current new context that this task is executing in
    
  254.   treeContext: TreeContext, // the current tree context that this task is executing in
    
  255.   componentStack: null | ComponentStackNode, // DEV-only component stack
    
  256.   thenableState: null | ThenableState,
    
  257. };
    
  258. 
    
  259. export type Task = RenderTask | ReplayTask;
    
  260. 
    
  261. const PENDING = 0;
    
  262. const COMPLETED = 1;
    
  263. const FLUSHED = 2;
    
  264. const ABORTED = 3;
    
  265. const ERRORED = 4;
    
  266. const POSTPONED = 5;
    
  267. 
    
  268. type Root = null;
    
  269. 
    
  270. type Segment = {
    
  271.   status: 0 | 1 | 2 | 3 | 4 | 5,
    
  272.   parentFlushed: boolean, // typically a segment will be flushed by its parent, except if its parent was already flushed
    
  273.   id: number, // starts as 0 and is lazily assigned if the parent flushes early
    
  274.   +index: number, // the index within the parent's chunks or 0 at the root
    
  275.   +chunks: Array<Chunk | PrecomputedChunk>,
    
  276.   +children: Array<Segment>,
    
  277.   // The context that this segment was created in.
    
  278.   parentFormatContext: FormatContext,
    
  279.   // If this segment represents a fallback, this is the content that will replace that fallback.
    
  280.   +boundary: null | SuspenseBoundary,
    
  281.   // used to discern when text separator boundaries are needed
    
  282.   lastPushedText: boolean,
    
  283.   textEmbedded: boolean,
    
  284. };
    
  285. 
    
  286. const OPEN = 0;
    
  287. const CLOSING = 1;
    
  288. const CLOSED = 2;
    
  289. 
    
  290. export opaque type Request = {
    
  291.   destination: null | Destination,
    
  292.   flushScheduled: boolean,
    
  293.   +resumableState: ResumableState,
    
  294.   +renderState: RenderState,
    
  295.   +rootFormatContext: FormatContext,
    
  296.   +progressiveChunkSize: number,
    
  297.   status: 0 | 1 | 2,
    
  298.   fatalError: mixed,
    
  299.   nextSegmentId: number,
    
  300.   allPendingTasks: number, // when it reaches zero, we can close the connection.
    
  301.   pendingRootTasks: number, // when this reaches zero, we've finished at least the root boundary.
    
  302.   completedRootSegment: null | Segment, // Completed but not yet flushed root segments.
    
  303.   abortableTasks: Set<Task>,
    
  304.   pingedTasks: Array<Task>, // High priority tasks that should be worked on first.
    
  305.   // Queues to flush in order of priority
    
  306.   clientRenderedBoundaries: Array<SuspenseBoundary>, // Errored or client rendered but not yet flushed.
    
  307.   completedBoundaries: Array<SuspenseBoundary>, // Completed but not yet fully flushed boundaries to show.
    
  308.   partialBoundaries: Array<SuspenseBoundary>, // Partially completed boundaries that can flush its segments early.
    
  309.   trackedPostpones: null | PostponedHoles, // Gets set to non-null while we want to track postponed holes. I.e. during a prerender.
    
  310.   // onError is called when an error happens anywhere in the tree. It might recover.
    
  311.   // The return string is used in production  primarily to avoid leaking internals, secondarily to save bytes.
    
  312.   // Returning null/undefined will cause a defualt error message in production
    
  313.   onError: (error: mixed) => ?string,
    
  314.   // onAllReady is called when all pending task is done but it may not have flushed yet.
    
  315.   // This is a good time to start writing if you want only HTML and no intermediate steps.
    
  316.   onAllReady: () => void,
    
  317.   // onShellReady is called when there is at least a root fallback ready to show.
    
  318.   // Typically you don't need this callback because it's best practice to always have a
    
  319.   // root fallback ready so there's no need to wait.
    
  320.   onShellReady: () => void,
    
  321.   // onShellError is called when the shell didn't complete. That means you probably want to
    
  322.   // emit a different response to the stream instead.
    
  323.   onShellError: (error: mixed) => void,
    
  324.   onFatalError: (error: mixed) => void,
    
  325.   // onPostpone is called when postpone() is called anywhere in the tree, which will defer
    
  326.   // rendering - e.g. to the client. This is considered intentional and not an error.
    
  327.   onPostpone: (reason: string) => void,
    
  328.   // Form state that was the result of an MPA submission, if it was provided.
    
  329.   formState: null | ReactFormState<any, any>,
    
  330. };
    
  331. 
    
  332. // This is a default heuristic for how to split up the HTML content into progressive
    
  333. // loading. Our goal is to be able to display additional new content about every 500ms.
    
  334. // Faster than that is unnecessary and should be throttled on the client. It also
    
  335. // adds unnecessary overhead to do more splits. We don't know if it's a higher or lower
    
  336. // end device but higher end suffer less from the overhead than lower end does from
    
  337. // not getting small enough pieces. We error on the side of low end.
    
  338. // We base this on low end 3G speeds which is about 500kbits per second. We assume
    
  339. // that there can be a reasonable drop off from max bandwidth which leaves you with
    
  340. // as little as 80%. We can receive half of that each 500ms - at best. In practice,
    
  341. // a little bandwidth is lost to processing and contention - e.g. CSS and images that
    
  342. // are downloaded along with the main content. So we estimate about half of that to be
    
  343. // the lower end throughput. In other words, we expect that you can at least show
    
  344. // about 12.5kb of content per 500ms. Not counting starting latency for the first
    
  345. // paint.
    
  346. // 500 * 1024 / 8 * .8 * 0.5 / 2
    
  347. const DEFAULT_PROGRESSIVE_CHUNK_SIZE = 12800;
    
  348. 
    
  349. function defaultErrorHandler(error: mixed) {
    
  350.   console['error'](error); // Don't transform to our wrapper
    
  351.   return null;
    
  352. }
    
  353. 
    
  354. function noop(): void {}
    
  355. 
    
  356. export function createRequest(
    
  357.   children: ReactNodeList,
    
  358.   resumableState: ResumableState,
    
  359.   renderState: RenderState,
    
  360.   rootFormatContext: FormatContext,
    
  361.   progressiveChunkSize: void | number,
    
  362.   onError: void | ((error: mixed) => ?string),
    
  363.   onAllReady: void | (() => void),
    
  364.   onShellReady: void | (() => void),
    
  365.   onShellError: void | ((error: mixed) => void),
    
  366.   onFatalError: void | ((error: mixed) => void),
    
  367.   onPostpone: void | ((reason: string) => void),
    
  368.   formState: void | null | ReactFormState<any, any>,
    
  369. ): Request {
    
  370.   prepareHostDispatcher();
    
  371.   const pingedTasks: Array<Task> = [];
    
  372.   const abortSet: Set<Task> = new Set();
    
  373.   const request: Request = {
    
  374.     destination: null,
    
  375.     flushScheduled: false,
    
  376.     resumableState,
    
  377.     renderState,
    
  378.     rootFormatContext,
    
  379.     progressiveChunkSize:
    
  380.       progressiveChunkSize === undefined
    
  381.         ? DEFAULT_PROGRESSIVE_CHUNK_SIZE
    
  382.         : progressiveChunkSize,
    
  383.     status: OPEN,
    
  384.     fatalError: null,
    
  385.     nextSegmentId: 0,
    
  386.     allPendingTasks: 0,
    
  387.     pendingRootTasks: 0,
    
  388.     completedRootSegment: null,
    
  389.     abortableTasks: abortSet,
    
  390.     pingedTasks: pingedTasks,
    
  391.     clientRenderedBoundaries: ([]: Array<SuspenseBoundary>),
    
  392.     completedBoundaries: ([]: Array<SuspenseBoundary>),
    
  393.     partialBoundaries: ([]: Array<SuspenseBoundary>),
    
  394.     trackedPostpones: null,
    
  395.     onError: onError === undefined ? defaultErrorHandler : onError,
    
  396.     onPostpone: onPostpone === undefined ? noop : onPostpone,
    
  397.     onAllReady: onAllReady === undefined ? noop : onAllReady,
    
  398.     onShellReady: onShellReady === undefined ? noop : onShellReady,
    
  399.     onShellError: onShellError === undefined ? noop : onShellError,
    
  400.     onFatalError: onFatalError === undefined ? noop : onFatalError,
    
  401.     formState: formState === undefined ? null : formState,
    
  402.   };
    
  403.   // This segment represents the root fallback.
    
  404.   const rootSegment = createPendingSegment(
    
  405.     request,
    
  406.     0,
    
  407.     null,
    
  408.     rootFormatContext,
    
  409.     // Root segments are never embedded in Text on either edge
    
  410.     false,
    
  411.     false,
    
  412.   );
    
  413.   // There is no parent so conceptually, we're unblocked to flush this segment.
    
  414.   rootSegment.parentFlushed = true;
    
  415.   const rootTask = createRenderTask(
    
  416.     request,
    
  417.     null,
    
  418.     children,
    
  419.     -1,
    
  420.     null,
    
  421.     rootSegment,
    
  422.     abortSet,
    
  423.     null,
    
  424.     rootFormatContext,
    
  425.     emptyContextObject,
    
  426.     rootContextSnapshot,
    
  427.     emptyTreeContext,
    
  428.   );
    
  429.   pingedTasks.push(rootTask);
    
  430.   return request;
    
  431. }
    
  432. 
    
  433. export function createPrerenderRequest(
    
  434.   children: ReactNodeList,
    
  435.   resumableState: ResumableState,
    
  436.   renderState: RenderState,
    
  437.   rootFormatContext: FormatContext,
    
  438.   progressiveChunkSize: void | number,
    
  439.   onError: void | ((error: mixed) => ?string),
    
  440.   onAllReady: void | (() => void),
    
  441.   onShellReady: void | (() => void),
    
  442.   onShellError: void | ((error: mixed) => void),
    
  443.   onFatalError: void | ((error: mixed) => void),
    
  444.   onPostpone: void | ((reason: string) => void),
    
  445. ): Request {
    
  446.   const request = createRequest(
    
  447.     children,
    
  448.     resumableState,
    
  449.     renderState,
    
  450.     rootFormatContext,
    
  451.     progressiveChunkSize,
    
  452.     onError,
    
  453.     onAllReady,
    
  454.     onShellReady,
    
  455.     onShellError,
    
  456.     onFatalError,
    
  457.     onPostpone,
    
  458.   );
    
  459.   // Start tracking postponed holes during this render.
    
  460.   request.trackedPostpones = {
    
  461.     workingMap: new Map(),
    
  462.     rootNodes: [],
    
  463.     rootSlots: null,
    
  464.   };
    
  465.   return request;
    
  466. }
    
  467. 
    
  468. export function resumeRequest(
    
  469.   children: ReactNodeList,
    
  470.   postponedState: PostponedState,
    
  471.   renderState: RenderState,
    
  472.   onError: void | ((error: mixed) => ?string),
    
  473.   onAllReady: void | (() => void),
    
  474.   onShellReady: void | (() => void),
    
  475.   onShellError: void | ((error: mixed) => void),
    
  476.   onFatalError: void | ((error: mixed) => void),
    
  477.   onPostpone: void | ((reason: string) => void),
    
  478. ): Request {
    
  479.   prepareHostDispatcher();
    
  480.   const pingedTasks: Array<Task> = [];
    
  481.   const abortSet: Set<Task> = new Set();
    
  482.   const request: Request = {
    
  483.     destination: null,
    
  484.     flushScheduled: false,
    
  485.     resumableState: postponedState.resumableState,
    
  486.     renderState,
    
  487.     rootFormatContext: postponedState.rootFormatContext,
    
  488.     progressiveChunkSize: postponedState.progressiveChunkSize,
    
  489.     status: OPEN,
    
  490.     fatalError: null,
    
  491.     nextSegmentId: postponedState.nextSegmentId,
    
  492.     allPendingTasks: 0,
    
  493.     pendingRootTasks: 0,
    
  494.     completedRootSegment: null,
    
  495.     abortableTasks: abortSet,
    
  496.     pingedTasks: pingedTasks,
    
  497.     clientRenderedBoundaries: ([]: Array<SuspenseBoundary>),
    
  498.     completedBoundaries: ([]: Array<SuspenseBoundary>),
    
  499.     partialBoundaries: ([]: Array<SuspenseBoundary>),
    
  500.     trackedPostpones: null,
    
  501.     onError: onError === undefined ? defaultErrorHandler : onError,
    
  502.     onPostpone: onPostpone === undefined ? noop : onPostpone,
    
  503.     onAllReady: onAllReady === undefined ? noop : onAllReady,
    
  504.     onShellReady: onShellReady === undefined ? noop : onShellReady,
    
  505.     onShellError: onShellError === undefined ? noop : onShellError,
    
  506.     onFatalError: onFatalError === undefined ? noop : onFatalError,
    
  507.     formState: null,
    
  508.   };
    
  509.   if (typeof postponedState.replaySlots === 'number') {
    
  510.     const resumedId = postponedState.replaySlots;
    
  511.     // We have a resume slot at the very root. This is effectively just a full rerender.
    
  512.     const rootSegment = createPendingSegment(
    
  513.       request,
    
  514.       0,
    
  515.       null,
    
  516.       postponedState.rootFormatContext,
    
  517.       // Root segments are never embedded in Text on either edge
    
  518.       false,
    
  519.       false,
    
  520.     );
    
  521.     rootSegment.id = resumedId;
    
  522.     // There is no parent so conceptually, we're unblocked to flush this segment.
    
  523.     rootSegment.parentFlushed = true;
    
  524.     const rootTask = createRenderTask(
    
  525.       request,
    
  526.       null,
    
  527.       children,
    
  528.       -1,
    
  529.       null,
    
  530.       rootSegment,
    
  531.       abortSet,
    
  532.       null,
    
  533.       postponedState.rootFormatContext,
    
  534.       emptyContextObject,
    
  535.       rootContextSnapshot,
    
  536.       emptyTreeContext,
    
  537.     );
    
  538.     pingedTasks.push(rootTask);
    
  539.     return request;
    
  540.   }
    
  541. 
    
  542.   const replay: ReplaySet = {
    
  543.     nodes: postponedState.replayNodes,
    
  544.     slots: postponedState.replaySlots,
    
  545.     pendingTasks: 0,
    
  546.   };
    
  547.   const rootTask = createReplayTask(
    
  548.     request,
    
  549.     null,
    
  550.     replay,
    
  551.     children,
    
  552.     -1,
    
  553.     null,
    
  554.     abortSet,
    
  555.     null,
    
  556.     postponedState.rootFormatContext,
    
  557.     emptyContextObject,
    
  558.     rootContextSnapshot,
    
  559.     emptyTreeContext,
    
  560.   );
    
  561.   pingedTasks.push(rootTask);
    
  562.   return request;
    
  563. }
    
  564. 
    
  565. let currentRequest: null | Request = null;
    
  566. 
    
  567. export function resolveRequest(): null | Request {
    
  568.   if (currentRequest) return currentRequest;
    
  569.   if (supportsRequestStorage) {
    
  570.     const store = requestStorage.getStore();
    
  571.     if (store) return store;
    
  572.   }
    
  573.   return null;
    
  574. }
    
  575. 
    
  576. function pingTask(request: Request, task: Task): void {
    
  577.   const pingedTasks = request.pingedTasks;
    
  578.   pingedTasks.push(task);
    
  579.   if (request.pingedTasks.length === 1) {
    
  580.     request.flushScheduled = request.destination !== null;
    
  581.     scheduleWork(() => performWork(request));
    
  582.   }
    
  583. }
    
  584. 
    
  585. function createSuspenseBoundary(
    
  586.   request: Request,
    
  587.   fallbackAbortableTasks: Set<Task>,
    
  588. ): SuspenseBoundary {
    
  589.   return {
    
  590.     status: PENDING,
    
  591.     rootSegmentID: -1,
    
  592.     parentFlushed: false,
    
  593.     pendingTasks: 0,
    
  594.     completedSegments: [],
    
  595.     byteSize: 0,
    
  596.     fallbackAbortableTasks,
    
  597.     errorDigest: null,
    
  598.     resources: createBoundaryResources(),
    
  599.     trackedContentKeyPath: null,
    
  600.     trackedFallbackNode: null,
    
  601.   };
    
  602. }
    
  603. 
    
  604. function createRenderTask(
    
  605.   request: Request,
    
  606.   thenableState: ThenableState | null,
    
  607.   node: ReactNodeList,
    
  608.   childIndex: number,
    
  609.   blockedBoundary: Root | SuspenseBoundary,
    
  610.   blockedSegment: Segment,
    
  611.   abortSet: Set<Task>,
    
  612.   keyPath: Root | KeyNode,
    
  613.   formatContext: FormatContext,
    
  614.   legacyContext: LegacyContext,
    
  615.   context: ContextSnapshot,
    
  616.   treeContext: TreeContext,
    
  617. ): RenderTask {
    
  618.   request.allPendingTasks++;
    
  619.   if (blockedBoundary === null) {
    
  620.     request.pendingRootTasks++;
    
  621.   } else {
    
  622.     blockedBoundary.pendingTasks++;
    
  623.   }
    
  624.   const task: RenderTask = ({
    
  625.     replay: null,
    
  626.     node,
    
  627.     childIndex,
    
  628.     ping: () => pingTask(request, task),
    
  629.     blockedBoundary,
    
  630.     blockedSegment,
    
  631.     abortSet,
    
  632.     keyPath,
    
  633.     formatContext,
    
  634.     legacyContext,
    
  635.     context,
    
  636.     treeContext,
    
  637.     thenableState,
    
  638.   }: any);
    
  639.   if (__DEV__) {
    
  640.     task.componentStack = null;
    
  641.   }
    
  642.   abortSet.add(task);
    
  643.   return task;
    
  644. }
    
  645. 
    
  646. function createReplayTask(
    
  647.   request: Request,
    
  648.   thenableState: ThenableState | null,
    
  649.   replay: ReplaySet,
    
  650.   node: ReactNodeList,
    
  651.   childIndex: number,
    
  652.   blockedBoundary: Root | SuspenseBoundary,
    
  653.   abortSet: Set<Task>,
    
  654.   keyPath: Root | KeyNode,
    
  655.   formatContext: FormatContext,
    
  656.   legacyContext: LegacyContext,
    
  657.   context: ContextSnapshot,
    
  658.   treeContext: TreeContext,
    
  659. ): ReplayTask {
    
  660.   request.allPendingTasks++;
    
  661.   if (blockedBoundary === null) {
    
  662.     request.pendingRootTasks++;
    
  663.   } else {
    
  664.     blockedBoundary.pendingTasks++;
    
  665.   }
    
  666.   replay.pendingTasks++;
    
  667.   const task: ReplayTask = ({
    
  668.     replay,
    
  669.     node,
    
  670.     childIndex,
    
  671.     ping: () => pingTask(request, task),
    
  672.     blockedBoundary,
    
  673.     blockedSegment: null,
    
  674.     abortSet,
    
  675.     keyPath,
    
  676.     formatContext,
    
  677.     legacyContext,
    
  678.     context,
    
  679.     treeContext,
    
  680.     thenableState,
    
  681.   }: any);
    
  682.   if (__DEV__) {
    
  683.     task.componentStack = null;
    
  684.   }
    
  685.   abortSet.add(task);
    
  686.   return task;
    
  687. }
    
  688. 
    
  689. function createPendingSegment(
    
  690.   request: Request,
    
  691.   index: number,
    
  692.   boundary: null | SuspenseBoundary,
    
  693.   parentFormatContext: FormatContext,
    
  694.   lastPushedText: boolean,
    
  695.   textEmbedded: boolean,
    
  696. ): Segment {
    
  697.   return {
    
  698.     status: PENDING,
    
  699.     id: -1, // lazily assigned later
    
  700.     index,
    
  701.     parentFlushed: false,
    
  702.     chunks: [],
    
  703.     children: [],
    
  704.     parentFormatContext,
    
  705.     boundary,
    
  706.     lastPushedText,
    
  707.     textEmbedded,
    
  708.   };
    
  709. }
    
  710. 
    
  711. // DEV-only global reference to the currently executing task
    
  712. let currentTaskInDEV: null | Task = null;
    
  713. function getCurrentStackInDEV(): string {
    
  714.   if (__DEV__) {
    
  715.     if (currentTaskInDEV === null || currentTaskInDEV.componentStack === null) {
    
  716.       return '';
    
  717.     }
    
  718.     return getStackByComponentStackNode(currentTaskInDEV.componentStack);
    
  719.   }
    
  720.   return '';
    
  721. }
    
  722. 
    
  723. function pushBuiltInComponentStackInDEV(task: Task, type: string): void {
    
  724.   if (__DEV__) {
    
  725.     task.componentStack = {
    
  726.       tag: 0,
    
  727.       parent: task.componentStack,
    
  728.       type,
    
  729.     };
    
  730.   }
    
  731. }
    
  732. function pushFunctionComponentStackInDEV(task: Task, type: Function): void {
    
  733.   if (__DEV__) {
    
  734.     task.componentStack = {
    
  735.       tag: 1,
    
  736.       parent: task.componentStack,
    
  737.       type,
    
  738.     };
    
  739.   }
    
  740. }
    
  741. function pushClassComponentStackInDEV(task: Task, type: Function): void {
    
  742.   if (__DEV__) {
    
  743.     task.componentStack = {
    
  744.       tag: 2,
    
  745.       parent: task.componentStack,
    
  746.       type,
    
  747.     };
    
  748.   }
    
  749. }
    
  750. function popComponentStackInDEV(task: Task): void {
    
  751.   if (__DEV__) {
    
  752.     if (task.componentStack === null) {
    
  753.       console.error(
    
  754.         'Unexpectedly popped too many stack frames. This is a bug in React.',
    
  755.       );
    
  756.     } else {
    
  757.       task.componentStack = task.componentStack.parent;
    
  758.     }
    
  759.   }
    
  760. }
    
  761. 
    
  762. // stash the component stack of an unwinding error until it is processed
    
  763. let lastBoundaryErrorComponentStackDev: ?string = null;
    
  764. 
    
  765. function captureBoundaryErrorDetailsDev(
    
  766.   boundary: SuspenseBoundary,
    
  767.   error: mixed,
    
  768. ) {
    
  769.   if (__DEV__) {
    
  770.     let errorMessage;
    
  771.     if (typeof error === 'string') {
    
  772.       errorMessage = error;
    
  773.     } else if (error && typeof error.message === 'string') {
    
  774.       errorMessage = error.message;
    
  775.     } else {
    
  776.       // eslint-disable-next-line react-internal/safe-string-coercion
    
  777.       errorMessage = String(error);
    
  778.     }
    
  779. 
    
  780.     const errorComponentStack =
    
  781.       lastBoundaryErrorComponentStackDev || getCurrentStackInDEV();
    
  782.     lastBoundaryErrorComponentStackDev = null;
    
  783. 
    
  784.     boundary.errorMessage = errorMessage;
    
  785.     boundary.errorComponentStack = errorComponentStack;
    
  786.   }
    
  787. }
    
  788. 
    
  789. function logPostpone(request: Request, reason: string): void {
    
  790.   // If this callback errors, we intentionally let that error bubble up to become a fatal error
    
  791.   // so that someone fixes the error reporting instead of hiding it.
    
  792.   request.onPostpone(reason);
    
  793. }
    
  794. 
    
  795. function logRecoverableError(request: Request, error: any): ?string {
    
  796.   // If this callback errors, we intentionally let that error bubble up to become a fatal error
    
  797.   // so that someone fixes the error reporting instead of hiding it.
    
  798.   const errorDigest = request.onError(error);
    
  799.   if (errorDigest != null && typeof errorDigest !== 'string') {
    
  800.     // eslint-disable-next-line react-internal/prod-error-codes
    
  801.     throw new Error(
    
  802.       `onError returned something with a type other than "string". onError should return a string and may return null or undefined but must not return anything else. It received something of type "${typeof errorDigest}" instead`,
    
  803.     );
    
  804.   }
    
  805.   return errorDigest;
    
  806. }
    
  807. 
    
  808. function fatalError(request: Request, error: mixed): void {
    
  809.   // This is called outside error handling code such as if the root errors outside
    
  810.   // a suspense boundary or if the root suspense boundary's fallback errors.
    
  811.   // It's also called if React itself or its host configs errors.
    
  812.   const onShellError = request.onShellError;
    
  813.   onShellError(error);
    
  814.   const onFatalError = request.onFatalError;
    
  815.   onFatalError(error);
    
  816.   if (request.destination !== null) {
    
  817.     request.status = CLOSED;
    
  818.     closeWithError(request.destination, error);
    
  819.   } else {
    
  820.     request.status = CLOSING;
    
  821.     request.fatalError = error;
    
  822.   }
    
  823. }
    
  824. 
    
  825. function renderSuspenseBoundary(
    
  826.   request: Request,
    
  827.   someTask: Task,
    
  828.   keyPath: KeyNode,
    
  829.   props: Object,
    
  830. ): void {
    
  831.   if (someTask.replay !== null) {
    
  832.     // If we're replaying through this pass, it means we're replaying through
    
  833.     // an already completed Suspense boundary. It's too late to do anything about it
    
  834.     // so we can just render through it.
    
  835.     const prevKeyPath = someTask.keyPath;
    
  836.     someTask.keyPath = keyPath;
    
  837.     const content: ReactNodeList = props.children;
    
  838.     try {
    
  839.       renderNode(request, someTask, content, -1);
    
  840.     } finally {
    
  841.       someTask.keyPath = prevKeyPath;
    
  842.     }
    
  843.     return;
    
  844.   }
    
  845.   // $FlowFixMe: Refined.
    
  846.   const task: RenderTask = someTask;
    
  847. 
    
  848.   pushBuiltInComponentStackInDEV(task, 'Suspense');
    
  849. 
    
  850.   const prevKeyPath = task.keyPath;
    
  851.   const parentBoundary = task.blockedBoundary;
    
  852.   const parentSegment = task.blockedSegment;
    
  853. 
    
  854.   // Each time we enter a suspense boundary, we split out into a new segment for
    
  855.   // the fallback so that we can later replace that segment with the content.
    
  856.   // This also lets us split out the main content even if it doesn't suspend,
    
  857.   // in case it ends up generating a large subtree of content.
    
  858.   const fallback: ReactNodeList = props.fallback;
    
  859.   const content: ReactNodeList = props.children;
    
  860. 
    
  861.   const fallbackAbortSet: Set<Task> = new Set();
    
  862.   const newBoundary = createSuspenseBoundary(request, fallbackAbortSet);
    
  863.   if (request.trackedPostpones !== null) {
    
  864.     newBoundary.trackedContentKeyPath = keyPath;
    
  865.   }
    
  866.   const insertionIndex = parentSegment.chunks.length;
    
  867.   // The children of the boundary segment is actually the fallback.
    
  868.   const boundarySegment = createPendingSegment(
    
  869.     request,
    
  870.     insertionIndex,
    
  871.     newBoundary,
    
  872.     task.formatContext,
    
  873.     // boundaries never require text embedding at their edges because comment nodes bound them
    
  874.     false,
    
  875.     false,
    
  876.   );
    
  877.   parentSegment.children.push(boundarySegment);
    
  878.   // The parentSegment has a child Segment at this index so we reset the lastPushedText marker on the parent
    
  879.   parentSegment.lastPushedText = false;
    
  880. 
    
  881.   // This segment is the actual child content. We can start rendering that immediately.
    
  882.   const contentRootSegment = createPendingSegment(
    
  883.     request,
    
  884.     0,
    
  885.     null,
    
  886.     task.formatContext,
    
  887.     // boundaries never require text embedding at their edges because comment nodes bound them
    
  888.     false,
    
  889.     false,
    
  890.   );
    
  891.   // We mark the root segment as having its parent flushed. It's not really flushed but there is
    
  892.   // no parent segment so there's nothing to wait on.
    
  893.   contentRootSegment.parentFlushed = true;
    
  894. 
    
  895.   // Currently this is running synchronously. We could instead schedule this to pingedTasks.
    
  896.   // I suspect that there might be some efficiency benefits from not creating the suspended task
    
  897.   // and instead just using the stack if possible.
    
  898.   // TODO: Call this directly instead of messing with saving and restoring contexts.
    
  899. 
    
  900.   // We can reuse the current context and task to render the content immediately without
    
  901.   // context switching. We just need to temporarily switch which boundary and which segment
    
  902.   // we're writing to. If something suspends, it'll spawn new suspended task with that context.
    
  903.   task.blockedBoundary = newBoundary;
    
  904.   task.blockedSegment = contentRootSegment;
    
  905.   if (enableFloat) {
    
  906.     setCurrentlyRenderingBoundaryResourcesTarget(
    
  907.       request.renderState,
    
  908.       newBoundary.resources,
    
  909.     );
    
  910.   }
    
  911.   task.keyPath = keyPath;
    
  912.   try {
    
  913.     // We use the safe form because we don't handle suspending here. Only error handling.
    
  914.     renderNode(request, task, content, -1);
    
  915.     pushSegmentFinale(
    
  916.       contentRootSegment.chunks,
    
  917.       request.renderState,
    
  918.       contentRootSegment.lastPushedText,
    
  919.       contentRootSegment.textEmbedded,
    
  920.     );
    
  921.     contentRootSegment.status = COMPLETED;
    
  922.     queueCompletedSegment(newBoundary, contentRootSegment);
    
  923.     if (newBoundary.pendingTasks === 0 && newBoundary.status === PENDING) {
    
  924.       newBoundary.status = COMPLETED;
    
  925.       // This must have been the last segment we were waiting on. This boundary is now complete.
    
  926.       // Therefore we won't need the fallback. We early return so that we don't have to create
    
  927.       // the fallback.
    
  928.       popComponentStackInDEV(task);
    
  929.       return;
    
  930.     }
    
  931.   } catch (error) {
    
  932.     contentRootSegment.status = ERRORED;
    
  933.     newBoundary.status = CLIENT_RENDERED;
    
  934.     let errorDigest;
    
  935.     if (
    
  936.       enablePostpone &&
    
  937.       typeof error === 'object' &&
    
  938.       error !== null &&
    
  939.       error.$$typeof === REACT_POSTPONE_TYPE
    
  940.     ) {
    
  941.       const postponeInstance: Postpone = (error: any);
    
  942.       logPostpone(request, postponeInstance.message);
    
  943.       // TODO: Figure out a better signal than a magic digest value.
    
  944.       errorDigest = 'POSTPONE';
    
  945.     } else {
    
  946.       errorDigest = logRecoverableError(request, error);
    
  947.     }
    
  948.     newBoundary.errorDigest = errorDigest;
    
  949.     if (__DEV__) {
    
  950.       captureBoundaryErrorDetailsDev(newBoundary, error);
    
  951.     }
    
  952. 
    
  953.     // We don't need to decrement any task numbers because we didn't spawn any new task.
    
  954.     // We don't need to schedule any task because we know the parent has written yet.
    
  955.     // We do need to fallthrough to create the fallback though.
    
  956.   } finally {
    
  957.     if (enableFloat) {
    
  958.       setCurrentlyRenderingBoundaryResourcesTarget(
    
  959.         request.renderState,
    
  960.         parentBoundary ? parentBoundary.resources : null,
    
  961.       );
    
  962.     }
    
  963.     task.blockedBoundary = parentBoundary;
    
  964.     task.blockedSegment = parentSegment;
    
  965.     task.keyPath = prevKeyPath;
    
  966.   }
    
  967. 
    
  968.   const fallbackKeyPath = [keyPath[0], 'Suspense Fallback', keyPath[2]];
    
  969.   const trackedPostpones = request.trackedPostpones;
    
  970.   if (trackedPostpones !== null) {
    
  971.     // We create a detached replay node to track any postpones inside the fallback.
    
  972.     const fallbackReplayNode: ReplayNode = [
    
  973.       fallbackKeyPath[1],
    
  974.       fallbackKeyPath[2],
    
  975.       ([]: Array<ReplayNode>),
    
  976.       null,
    
  977.     ];
    
  978.     trackedPostpones.workingMap.set(fallbackKeyPath, fallbackReplayNode);
    
  979.     if (newBoundary.status === POSTPONED) {
    
  980.       // This must exist now.
    
  981.       const boundaryReplayNode: ReplaySuspenseBoundary =
    
  982.         (trackedPostpones.workingMap.get(keyPath): any);
    
  983.       boundaryReplayNode[4] = fallbackReplayNode;
    
  984.     } else {
    
  985.       // We might not inject it into the postponed tree, unless the content actually
    
  986.       // postpones too. We need to keep track of it until that happpens.
    
  987.       newBoundary.trackedFallbackNode = fallbackReplayNode;
    
  988.     }
    
  989.   }
    
  990.   // We create suspended task for the fallback because we don't want to actually work
    
  991.   // on it yet in case we finish the main content, so we queue for later.
    
  992.   const suspendedFallbackTask = createRenderTask(
    
  993.     request,
    
  994.     null,
    
  995.     fallback,
    
  996.     -1,
    
  997.     parentBoundary,
    
  998.     boundarySegment,
    
  999.     fallbackAbortSet,
    
  1000.     fallbackKeyPath,
    
  1001.     task.formatContext,
    
  1002.     task.legacyContext,
    
  1003.     task.context,
    
  1004.     task.treeContext,
    
  1005.   );
    
  1006.   if (__DEV__) {
    
  1007.     suspendedFallbackTask.componentStack = task.componentStack;
    
  1008.   }
    
  1009.   // TODO: This should be queued at a separate lower priority queue so that we only work
    
  1010.   // on preparing fallbacks if we don't have any more main content to task on.
    
  1011.   request.pingedTasks.push(suspendedFallbackTask);
    
  1012. 
    
  1013.   popComponentStackInDEV(task);
    
  1014. }
    
  1015. 
    
  1016. function replaySuspenseBoundary(
    
  1017.   request: Request,
    
  1018.   task: ReplayTask,
    
  1019.   keyPath: KeyNode,
    
  1020.   props: Object,
    
  1021.   id: number,
    
  1022.   childNodes: Array<ReplayNode>,
    
  1023.   childSlots: ResumeSlots,
    
  1024.   fallbackNodes: Array<ReplayNode>,
    
  1025.   fallbackSlots: ResumeSlots,
    
  1026. ): void {
    
  1027.   pushBuiltInComponentStackInDEV(task, 'Suspense');
    
  1028. 
    
  1029.   const prevKeyPath = task.keyPath;
    
  1030.   const previousReplaySet: ReplaySet = task.replay;
    
  1031. 
    
  1032.   const parentBoundary = task.blockedBoundary;
    
  1033. 
    
  1034.   const content: ReactNodeList = props.children;
    
  1035.   const fallback: ReactNodeList = props.fallback;
    
  1036. 
    
  1037.   const fallbackAbortSet: Set<Task> = new Set();
    
  1038.   const resumedBoundary = createSuspenseBoundary(request, fallbackAbortSet);
    
  1039.   resumedBoundary.parentFlushed = true;
    
  1040.   // We restore the same id of this boundary as was used during prerender.
    
  1041.   resumedBoundary.rootSegmentID = id;
    
  1042. 
    
  1043.   // We can reuse the current context and task to render the content immediately without
    
  1044.   // context switching. We just need to temporarily switch which boundary and replay node
    
  1045.   // we're writing to. If something suspends, it'll spawn new suspended task with that context.
    
  1046.   task.blockedBoundary = resumedBoundary;
    
  1047.   task.replay = {nodes: childNodes, slots: childSlots, pendingTasks: 1};
    
  1048.   if (enableFloat) {
    
  1049.     setCurrentlyRenderingBoundaryResourcesTarget(
    
  1050.       request.renderState,
    
  1051.       resumedBoundary.resources,
    
  1052.     );
    
  1053.   }
    
  1054.   try {
    
  1055.     // We use the safe form because we don't handle suspending here. Only error handling.
    
  1056.     renderNode(request, task, content, -1);
    
  1057.     if (task.replay.pendingTasks === 1 && task.replay.nodes.length > 0) {
    
  1058.       throw new Error(
    
  1059.         "Couldn't find all resumable slots by key/index during replaying. " +
    
  1060.           "The tree doesn't match so React will fallback to client rendering.",
    
  1061.       );
    
  1062.     }
    
  1063.     task.replay.pendingTasks--;
    
  1064.     if (
    
  1065.       resumedBoundary.pendingTasks === 0 &&
    
  1066.       resumedBoundary.status === PENDING
    
  1067.     ) {
    
  1068.       resumedBoundary.status = COMPLETED;
    
  1069.       request.completedBoundaries.push(resumedBoundary);
    
  1070.       // This must have been the last segment we were waiting on. This boundary is now complete.
    
  1071.       // Therefore we won't need the fallback. We early return so that we don't have to create
    
  1072.       // the fallback.
    
  1073.       popComponentStackInDEV(task);
    
  1074.       return;
    
  1075.     }
    
  1076.   } catch (error) {
    
  1077.     resumedBoundary.status = CLIENT_RENDERED;
    
  1078.     let errorDigest;
    
  1079.     if (
    
  1080.       enablePostpone &&
    
  1081.       typeof error === 'object' &&
    
  1082.       error !== null &&
    
  1083.       error.$$typeof === REACT_POSTPONE_TYPE
    
  1084.     ) {
    
  1085.       const postponeInstance: Postpone = (error: any);
    
  1086.       logPostpone(request, postponeInstance.message);
    
  1087.       // TODO: Figure out a better signal than a magic digest value.
    
  1088.       errorDigest = 'POSTPONE';
    
  1089.     } else {
    
  1090.       errorDigest = logRecoverableError(request, error);
    
  1091.     }
    
  1092.     resumedBoundary.errorDigest = errorDigest;
    
  1093.     if (__DEV__) {
    
  1094.       captureBoundaryErrorDetailsDev(resumedBoundary, error);
    
  1095.     }
    
  1096. 
    
  1097.     task.replay.pendingTasks--;
    
  1098. 
    
  1099.     // The parent already flushed in the prerender so we need to schedule this to be emitted.
    
  1100.     request.clientRenderedBoundaries.push(resumedBoundary);
    
  1101. 
    
  1102.     // We don't need to decrement any task numbers because we didn't spawn any new task.
    
  1103.     // We don't need to schedule any task because we know the parent has written yet.
    
  1104.     // We do need to fallthrough to create the fallback though.
    
  1105.   } finally {
    
  1106.     if (enableFloat) {
    
  1107.       setCurrentlyRenderingBoundaryResourcesTarget(
    
  1108.         request.renderState,
    
  1109.         parentBoundary ? parentBoundary.resources : null,
    
  1110.       );
    
  1111.     }
    
  1112.     task.blockedBoundary = parentBoundary;
    
  1113.     task.replay = previousReplaySet;
    
  1114.     task.keyPath = prevKeyPath;
    
  1115.   }
    
  1116. 
    
  1117.   const fallbackKeyPath = [keyPath[0], 'Suspense Fallback', keyPath[2]];
    
  1118. 
    
  1119.   // We create suspended task for the fallback because we don't want to actually work
    
  1120.   // on it yet in case we finish the main content, so we queue for later.
    
  1121.   const fallbackReplay = {
    
  1122.     nodes: fallbackNodes,
    
  1123.     slots: fallbackSlots,
    
  1124.     pendingTasks: 0,
    
  1125.   };
    
  1126.   const suspendedFallbackTask = createReplayTask(
    
  1127.     request,
    
  1128.     null,
    
  1129.     fallbackReplay,
    
  1130.     fallback,
    
  1131.     -1,
    
  1132.     parentBoundary,
    
  1133.     fallbackAbortSet,
    
  1134.     fallbackKeyPath,
    
  1135.     task.formatContext,
    
  1136.     task.legacyContext,
    
  1137.     task.context,
    
  1138.     task.treeContext,
    
  1139.   );
    
  1140.   if (__DEV__) {
    
  1141.     suspendedFallbackTask.componentStack = task.componentStack;
    
  1142.   }
    
  1143.   // TODO: This should be queued at a separate lower priority queue so that we only work
    
  1144.   // on preparing fallbacks if we don't have any more main content to task on.
    
  1145.   request.pingedTasks.push(suspendedFallbackTask);
    
  1146. 
    
  1147.   popComponentStackInDEV(task);
    
  1148. }
    
  1149. 
    
  1150. function renderBackupSuspenseBoundary(
    
  1151.   request: Request,
    
  1152.   task: Task,
    
  1153.   keyPath: KeyNode,
    
  1154.   props: Object,
    
  1155. ) {
    
  1156.   pushBuiltInComponentStackInDEV(task, 'Suspense');
    
  1157. 
    
  1158.   const content = props.children;
    
  1159.   const segment = task.blockedSegment;
    
  1160.   const prevKeyPath = task.keyPath;
    
  1161.   task.keyPath = keyPath;
    
  1162.   if (segment === null) {
    
  1163.     // Replay
    
  1164.     renderNode(request, task, content, -1);
    
  1165.   } else {
    
  1166.     // Render
    
  1167.     pushStartCompletedSuspenseBoundary(segment.chunks);
    
  1168.     renderNode(request, task, content, -1);
    
  1169.     pushEndCompletedSuspenseBoundary(segment.chunks);
    
  1170.   }
    
  1171.   task.keyPath = prevKeyPath;
    
  1172. 
    
  1173.   popComponentStackInDEV(task);
    
  1174. }
    
  1175. 
    
  1176. function renderHostElement(
    
  1177.   request: Request,
    
  1178.   task: Task,
    
  1179.   keyPath: KeyNode,
    
  1180.   type: string,
    
  1181.   props: Object,
    
  1182. ): void {
    
  1183.   pushBuiltInComponentStackInDEV(task, type);
    
  1184.   const segment = task.blockedSegment;
    
  1185.   if (segment === null) {
    
  1186.     // Replay
    
  1187.     const children = props.children; // TODO: Make this a Config for replaying.
    
  1188.     const prevContext = task.formatContext;
    
  1189.     const prevKeyPath = task.keyPath;
    
  1190.     task.formatContext = getChildFormatContext(prevContext, type, props);
    
  1191.     task.keyPath = keyPath;
    
  1192. 
    
  1193.     // We use the non-destructive form because if something suspends, we still
    
  1194.     // need to pop back up and finish this subtree of HTML.
    
  1195.     renderNode(request, task, children, -1);
    
  1196. 
    
  1197.     // We expect that errors will fatal the whole task and that we don't need
    
  1198.     // the correct context. Therefore this is not in a finally.
    
  1199.     task.formatContext = prevContext;
    
  1200.     task.keyPath = prevKeyPath;
    
  1201.   } else {
    
  1202.     // Render
    
  1203.     const children = pushStartInstance(
    
  1204.       segment.chunks,
    
  1205.       type,
    
  1206.       props,
    
  1207.       request.resumableState,
    
  1208.       request.renderState,
    
  1209.       task.formatContext,
    
  1210.       segment.lastPushedText,
    
  1211.     );
    
  1212.     segment.lastPushedText = false;
    
  1213.     const prevContext = task.formatContext;
    
  1214.     const prevKeyPath = task.keyPath;
    
  1215.     task.formatContext = getChildFormatContext(prevContext, type, props);
    
  1216.     task.keyPath = keyPath;
    
  1217. 
    
  1218.     // We use the non-destructive form because if something suspends, we still
    
  1219.     // need to pop back up and finish this subtree of HTML.
    
  1220.     renderNode(request, task, children, -1);
    
  1221. 
    
  1222.     // We expect that errors will fatal the whole task and that we don't need
    
  1223.     // the correct context. Therefore this is not in a finally.
    
  1224.     task.formatContext = prevContext;
    
  1225.     task.keyPath = prevKeyPath;
    
  1226.     pushEndInstance(
    
  1227.       segment.chunks,
    
  1228.       type,
    
  1229.       props,
    
  1230.       request.resumableState,
    
  1231.       prevContext,
    
  1232.     );
    
  1233.     segment.lastPushedText = false;
    
  1234.   }
    
  1235.   popComponentStackInDEV(task);
    
  1236. }
    
  1237. 
    
  1238. function shouldConstruct(Component: any) {
    
  1239.   return Component.prototype && Component.prototype.isReactComponent;
    
  1240. }
    
  1241. 
    
  1242. function renderWithHooks<Props, SecondArg>(
    
  1243.   request: Request,
    
  1244.   task: Task,
    
  1245.   keyPath: KeyNode,
    
  1246.   prevThenableState: ThenableState | null,
    
  1247.   Component: (p: Props, arg: SecondArg) => any,
    
  1248.   props: Props,
    
  1249.   secondArg: SecondArg,
    
  1250. ): any {
    
  1251.   const componentIdentity = {};
    
  1252.   prepareToUseHooks(
    
  1253.     request,
    
  1254.     task,
    
  1255.     keyPath,
    
  1256.     componentIdentity,
    
  1257.     prevThenableState,
    
  1258.   );
    
  1259.   const result = Component(props, secondArg);
    
  1260.   return finishHooks(Component, props, result, secondArg);
    
  1261. }
    
  1262. 
    
  1263. function finishClassComponent(
    
  1264.   request: Request,
    
  1265.   task: Task,
    
  1266.   keyPath: KeyNode,
    
  1267.   instance: any,
    
  1268.   Component: any,
    
  1269.   props: any,
    
  1270. ): ReactNodeList {
    
  1271.   const nextChildren = instance.render();
    
  1272. 
    
  1273.   if (__DEV__) {
    
  1274.     if (instance.props !== props) {
    
  1275.       if (!didWarnAboutReassigningProps) {
    
  1276.         console.error(
    
  1277.           'It looks like %s is reassigning its own `this.props` while rendering. ' +
    
  1278.             'This is not supported and can lead to confusing bugs.',
    
  1279.           getComponentNameFromType(Component) || 'a component',
    
  1280.         );
    
  1281.       }
    
  1282.       didWarnAboutReassigningProps = true;
    
  1283.     }
    
  1284.   }
    
  1285. 
    
  1286.   if (!disableLegacyContext) {
    
  1287.     const childContextTypes = Component.childContextTypes;
    
  1288.     if (childContextTypes !== null && childContextTypes !== undefined) {
    
  1289.       const previousContext = task.legacyContext;
    
  1290.       const mergedContext = processChildContext(
    
  1291.         instance,
    
  1292.         Component,
    
  1293.         previousContext,
    
  1294.         childContextTypes,
    
  1295.       );
    
  1296.       task.legacyContext = mergedContext;
    
  1297.       renderNodeDestructive(request, task, null, nextChildren, -1);
    
  1298.       task.legacyContext = previousContext;
    
  1299.       return;
    
  1300.     }
    
  1301.   }
    
  1302. 
    
  1303.   const prevKeyPath = task.keyPath;
    
  1304.   task.keyPath = keyPath;
    
  1305.   renderNodeDestructive(request, task, null, nextChildren, -1);
    
  1306.   task.keyPath = prevKeyPath;
    
  1307. }
    
  1308. 
    
  1309. function renderClassComponent(
    
  1310.   request: Request,
    
  1311.   task: Task,
    
  1312.   keyPath: KeyNode,
    
  1313.   Component: any,
    
  1314.   props: any,
    
  1315. ): void {
    
  1316.   pushClassComponentStackInDEV(task, Component);
    
  1317.   const maskedContext = !disableLegacyContext
    
  1318.     ? getMaskedContext(Component, task.legacyContext)
    
  1319.     : undefined;
    
  1320.   const instance = constructClassInstance(Component, props, maskedContext);
    
  1321.   mountClassInstance(instance, Component, props, maskedContext);
    
  1322.   finishClassComponent(request, task, keyPath, instance, Component, props);
    
  1323.   popComponentStackInDEV(task);
    
  1324. }
    
  1325. 
    
  1326. const didWarnAboutBadClass: {[string]: boolean} = {};
    
  1327. const didWarnAboutModulePatternComponent: {[string]: boolean} = {};
    
  1328. const didWarnAboutContextTypeOnFunctionComponent: {[string]: boolean} = {};
    
  1329. const didWarnAboutGetDerivedStateOnFunctionComponent: {[string]: boolean} = {};
    
  1330. let didWarnAboutReassigningProps = false;
    
  1331. const didWarnAboutDefaultPropsOnFunctionComponent: {[string]: boolean} = {};
    
  1332. let didWarnAboutGenerators = false;
    
  1333. let didWarnAboutMaps = false;
    
  1334. let hasWarnedAboutUsingContextAsConsumer = false;
    
  1335. 
    
  1336. // This would typically be a function component but we still support module pattern
    
  1337. // components for some reason.
    
  1338. function renderIndeterminateComponent(
    
  1339.   request: Request,
    
  1340.   task: Task,
    
  1341.   keyPath: KeyNode,
    
  1342.   prevThenableState: ThenableState | null,
    
  1343.   Component: any,
    
  1344.   props: any,
    
  1345. ): void {
    
  1346.   let legacyContext;
    
  1347.   if (!disableLegacyContext) {
    
  1348.     legacyContext = getMaskedContext(Component, task.legacyContext);
    
  1349.   }
    
  1350.   pushFunctionComponentStackInDEV(task, Component);
    
  1351. 
    
  1352.   if (__DEV__) {
    
  1353.     if (
    
  1354.       Component.prototype &&
    
  1355.       typeof Component.prototype.render === 'function'
    
  1356.     ) {
    
  1357.       const componentName = getComponentNameFromType(Component) || 'Unknown';
    
  1358. 
    
  1359.       if (!didWarnAboutBadClass[componentName]) {
    
  1360.         console.error(
    
  1361.           "The <%s /> component appears to have a render method, but doesn't extend React.Component. " +
    
  1362.             'This is likely to cause errors. Change %s to extend React.Component instead.',
    
  1363.           componentName,
    
  1364.           componentName,
    
  1365.         );
    
  1366.         didWarnAboutBadClass[componentName] = true;
    
  1367.       }
    
  1368.     }
    
  1369.   }
    
  1370. 
    
  1371.   const value = renderWithHooks(
    
  1372.     request,
    
  1373.     task,
    
  1374.     keyPath,
    
  1375.     prevThenableState,
    
  1376.     Component,
    
  1377.     props,
    
  1378.     legacyContext,
    
  1379.   );
    
  1380.   const hasId = checkDidRenderIdHook();
    
  1381.   const formStateCount = getFormStateCount();
    
  1382.   const formStateMatchingIndex = getFormStateMatchingIndex();
    
  1383. 
    
  1384.   if (__DEV__) {
    
  1385.     // Support for module components is deprecated and is removed behind a flag.
    
  1386.     // Whether or not it would crash later, we want to show a good message in DEV first.
    
  1387.     if (
    
  1388.       typeof value === 'object' &&
    
  1389.       value !== null &&
    
  1390.       typeof value.render === 'function' &&
    
  1391.       value.$$typeof === undefined
    
  1392.     ) {
    
  1393.       const componentName = getComponentNameFromType(Component) || 'Unknown';
    
  1394.       if (!didWarnAboutModulePatternComponent[componentName]) {
    
  1395.         console.error(
    
  1396.           'The <%s /> component appears to be a function component that returns a class instance. ' +
    
  1397.             'Change %s to a class that extends React.Component instead. ' +
    
  1398.             "If you can't use a class try assigning the prototype on the function as a workaround. " +
    
  1399.             "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " +
    
  1400.             'cannot be called with `new` by React.',
    
  1401.           componentName,
    
  1402.           componentName,
    
  1403.           componentName,
    
  1404.         );
    
  1405.         didWarnAboutModulePatternComponent[componentName] = true;
    
  1406.       }
    
  1407.     }
    
  1408.   }
    
  1409. 
    
  1410.   if (
    
  1411.     // Run these checks in production only if the flag is off.
    
  1412.     // Eventually we'll delete this branch altogether.
    
  1413.     !disableModulePatternComponents &&
    
  1414.     typeof value === 'object' &&
    
  1415.     value !== null &&
    
  1416.     typeof value.render === 'function' &&
    
  1417.     value.$$typeof === undefined
    
  1418.   ) {
    
  1419.     if (__DEV__) {
    
  1420.       const componentName = getComponentNameFromType(Component) || 'Unknown';
    
  1421.       if (!didWarnAboutModulePatternComponent[componentName]) {
    
  1422.         console.error(
    
  1423.           'The <%s /> component appears to be a function component that returns a class instance. ' +
    
  1424.             'Change %s to a class that extends React.Component instead. ' +
    
  1425.             "If you can't use a class try assigning the prototype on the function as a workaround. " +
    
  1426.             "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " +
    
  1427.             'cannot be called with `new` by React.',
    
  1428.           componentName,
    
  1429.           componentName,
    
  1430.           componentName,
    
  1431.         );
    
  1432.         didWarnAboutModulePatternComponent[componentName] = true;
    
  1433.       }
    
  1434.     }
    
  1435. 
    
  1436.     mountClassInstance(value, Component, props, legacyContext);
    
  1437.     finishClassComponent(request, task, keyPath, value, Component, props);
    
  1438.   } else {
    
  1439.     // Proceed under the assumption that this is a function component
    
  1440.     if (__DEV__) {
    
  1441.       if (disableLegacyContext && Component.contextTypes) {
    
  1442.         console.error(
    
  1443.           '%s uses the legacy contextTypes API which is no longer supported. ' +
    
  1444.             'Use React.createContext() with React.useContext() instead.',
    
  1445.           getComponentNameFromType(Component) || 'Unknown',
    
  1446.         );
    
  1447.       }
    
  1448.     }
    
  1449.     if (__DEV__) {
    
  1450.       validateFunctionComponentInDev(Component);
    
  1451.     }
    
  1452.     finishFunctionComponent(
    
  1453.       request,
    
  1454.       task,
    
  1455.       keyPath,
    
  1456.       value,
    
  1457.       hasId,
    
  1458.       formStateCount,
    
  1459.       formStateMatchingIndex,
    
  1460.     );
    
  1461.   }
    
  1462.   popComponentStackInDEV(task);
    
  1463. }
    
  1464. 
    
  1465. function finishFunctionComponent(
    
  1466.   request: Request,
    
  1467.   task: Task,
    
  1468.   keyPath: KeyNode,
    
  1469.   children: ReactNodeList,
    
  1470.   hasId: boolean,
    
  1471.   formStateCount: number,
    
  1472.   formStateMatchingIndex: number,
    
  1473. ) {
    
  1474.   let didEmitFormStateMarkers = false;
    
  1475.   if (formStateCount !== 0 && request.formState !== null) {
    
  1476.     // For each useFormState hook, emit a marker that indicates whether we
    
  1477.     // rendered using the form state passed at the root. We only emit these
    
  1478.     // markers if form state is passed at the root.
    
  1479.     const segment = task.blockedSegment;
    
  1480.     if (segment === null) {
    
  1481.       // Implies we're in reumable mode.
    
  1482.     } else {
    
  1483.       didEmitFormStateMarkers = true;
    
  1484.       const target = segment.chunks;
    
  1485.       for (let i = 0; i < formStateCount; i++) {
    
  1486.         if (i === formStateMatchingIndex) {
    
  1487.           pushFormStateMarkerIsMatching(target);
    
  1488.         } else {
    
  1489.           pushFormStateMarkerIsNotMatching(target);
    
  1490.         }
    
  1491.       }
    
  1492.     }
    
  1493.   }
    
  1494. 
    
  1495.   const prevKeyPath = task.keyPath;
    
  1496.   task.keyPath = keyPath;
    
  1497.   if (hasId) {
    
  1498.     // This component materialized an id. We treat this as its own level, with
    
  1499.     // a single "child" slot.
    
  1500.     const prevTreeContext = task.treeContext;
    
  1501.     const totalChildren = 1;
    
  1502.     const index = 0;
    
  1503.     // Modify the id context. Because we'll need to reset this if something
    
  1504.     // suspends or errors, we'll use the non-destructive render path.
    
  1505.     task.treeContext = pushTreeContext(prevTreeContext, totalChildren, index);
    
  1506.     renderNode(request, task, children, -1);
    
  1507.     // Like the other contexts, this does not need to be in a finally block
    
  1508.     // because renderNode takes care of unwinding the stack.
    
  1509.     task.treeContext = prevTreeContext;
    
  1510.   } else if (didEmitFormStateMarkers) {
    
  1511.     // If there were formState hooks, we must use the non-destructive path
    
  1512.     // because this component is not a pure indirection; we emitted markers
    
  1513.     // to the stream.
    
  1514.     renderNode(request, task, children, -1);
    
  1515.   } else {
    
  1516.     // We're now successfully past this task, and we haven't modified the
    
  1517.     // context stack. We don't have to pop back to the previous task every
    
  1518.     // again, so we can use the destructive recursive form.
    
  1519.     renderNodeDestructive(request, task, null, children, -1);
    
  1520.   }
    
  1521.   task.keyPath = prevKeyPath;
    
  1522. }
    
  1523. 
    
  1524. function validateFunctionComponentInDev(Component: any): void {
    
  1525.   if (__DEV__) {
    
  1526.     if (Component) {
    
  1527.       if (Component.childContextTypes) {
    
  1528.         console.error(
    
  1529.           '%s(...): childContextTypes cannot be defined on a function component.',
    
  1530.           Component.displayName || Component.name || 'Component',
    
  1531.         );
    
  1532.       }
    
  1533.     }
    
  1534. 
    
  1535.     if (Component.defaultProps !== undefined) {
    
  1536.       const componentName = getComponentNameFromType(Component) || 'Unknown';
    
  1537. 
    
  1538.       if (!didWarnAboutDefaultPropsOnFunctionComponent[componentName]) {
    
  1539.         console.error(
    
  1540.           '%s: Support for defaultProps will be removed from function components ' +
    
  1541.             'in a future major release. Use JavaScript default parameters instead.',
    
  1542.           componentName,
    
  1543.         );
    
  1544.         didWarnAboutDefaultPropsOnFunctionComponent[componentName] = true;
    
  1545.       }
    
  1546.     }
    
  1547. 
    
  1548.     if (typeof Component.getDerivedStateFromProps === 'function') {
    
  1549.       const componentName = getComponentNameFromType(Component) || 'Unknown';
    
  1550. 
    
  1551.       if (!didWarnAboutGetDerivedStateOnFunctionComponent[componentName]) {
    
  1552.         console.error(
    
  1553.           '%s: Function components do not support getDerivedStateFromProps.',
    
  1554.           componentName,
    
  1555.         );
    
  1556.         didWarnAboutGetDerivedStateOnFunctionComponent[componentName] = true;
    
  1557.       }
    
  1558.     }
    
  1559. 
    
  1560.     if (
    
  1561.       typeof Component.contextType === 'object' &&
    
  1562.       Component.contextType !== null
    
  1563.     ) {
    
  1564.       const componentName = getComponentNameFromType(Component) || 'Unknown';
    
  1565. 
    
  1566.       if (!didWarnAboutContextTypeOnFunctionComponent[componentName]) {
    
  1567.         console.error(
    
  1568.           '%s: Function components do not support contextType.',
    
  1569.           componentName,
    
  1570.         );
    
  1571.         didWarnAboutContextTypeOnFunctionComponent[componentName] = true;
    
  1572.       }
    
  1573.     }
    
  1574.   }
    
  1575. }
    
  1576. 
    
  1577. function resolveDefaultProps(Component: any, baseProps: Object): Object {
    
  1578.   if (Component && Component.defaultProps) {
    
  1579.     // Resolve default props. Taken from ReactElement
    
  1580.     const props = assign({}, baseProps);
    
  1581.     const defaultProps = Component.defaultProps;
    
  1582.     for (const propName in defaultProps) {
    
  1583.       if (props[propName] === undefined) {
    
  1584.         props[propName] = defaultProps[propName];
    
  1585.       }
    
  1586.     }
    
  1587.     return props;
    
  1588.   }
    
  1589.   return baseProps;
    
  1590. }
    
  1591. 
    
  1592. function renderForwardRef(
    
  1593.   request: Request,
    
  1594.   task: Task,
    
  1595.   keyPath: KeyNode,
    
  1596.   prevThenableState: null | ThenableState,
    
  1597.   type: any,
    
  1598.   props: Object,
    
  1599.   ref: any,
    
  1600. ): void {
    
  1601.   pushFunctionComponentStackInDEV(task, type.render);
    
  1602.   const children = renderWithHooks(
    
  1603.     request,
    
  1604.     task,
    
  1605.     keyPath,
    
  1606.     prevThenableState,
    
  1607.     type.render,
    
  1608.     props,
    
  1609.     ref,
    
  1610.   );
    
  1611.   const hasId = checkDidRenderIdHook();
    
  1612.   const formStateCount = getFormStateCount();
    
  1613.   const formStateMatchingIndex = getFormStateMatchingIndex();
    
  1614.   finishFunctionComponent(
    
  1615.     request,
    
  1616.     task,
    
  1617.     keyPath,
    
  1618.     children,
    
  1619.     hasId,
    
  1620.     formStateCount,
    
  1621.     formStateMatchingIndex,
    
  1622.   );
    
  1623.   popComponentStackInDEV(task);
    
  1624. }
    
  1625. 
    
  1626. function renderMemo(
    
  1627.   request: Request,
    
  1628.   task: Task,
    
  1629.   keyPath: KeyNode,
    
  1630.   prevThenableState: ThenableState | null,
    
  1631.   type: any,
    
  1632.   props: Object,
    
  1633.   ref: any,
    
  1634. ): void {
    
  1635.   const innerType = type.type;
    
  1636.   const resolvedProps = resolveDefaultProps(innerType, props);
    
  1637.   renderElement(
    
  1638.     request,
    
  1639.     task,
    
  1640.     keyPath,
    
  1641.     prevThenableState,
    
  1642.     innerType,
    
  1643.     resolvedProps,
    
  1644.     ref,
    
  1645.   );
    
  1646. }
    
  1647. 
    
  1648. function renderContextConsumer(
    
  1649.   request: Request,
    
  1650.   task: Task,
    
  1651.   keyPath: KeyNode,
    
  1652.   context: ReactContext<any>,
    
  1653.   props: Object,
    
  1654. ): void {
    
  1655.   // The logic below for Context differs depending on PROD or DEV mode. In
    
  1656.   // DEV mode, we create a separate object for Context.Consumer that acts
    
  1657.   // like a proxy to Context. This proxy object adds unnecessary code in PROD
    
  1658.   // so we use the old behaviour (Context.Consumer references Context) to
    
  1659.   // reduce size and overhead. The separate object references context via
    
  1660.   // a property called "_context", which also gives us the ability to check
    
  1661.   // in DEV mode if this property exists or not and warn if it does not.
    
  1662.   if (__DEV__) {
    
  1663.     if ((context: any)._context === undefined) {
    
  1664.       // This may be because it's a Context (rather than a Consumer).
    
  1665.       // Or it may be because it's older React where they're the same thing.
    
  1666.       // We only want to warn if we're sure it's a new React.
    
  1667.       if (context !== context.Consumer) {
    
  1668.         if (!hasWarnedAboutUsingContextAsConsumer) {
    
  1669.           hasWarnedAboutUsingContextAsConsumer = true;
    
  1670.           console.error(
    
  1671.             'Rendering <Context> directly is not supported and will be removed in ' +
    
  1672.               'a future major release. Did you mean to render <Context.Consumer> instead?',
    
  1673.           );
    
  1674.         }
    
  1675.       }
    
  1676.     } else {
    
  1677.       context = (context: any)._context;
    
  1678.     }
    
  1679.   }
    
  1680.   const render = props.children;
    
  1681. 
    
  1682.   if (__DEV__) {
    
  1683.     if (typeof render !== 'function') {
    
  1684.       console.error(
    
  1685.         'A context consumer was rendered with multiple children, or a child ' +
    
  1686.           "that isn't a function. A context consumer expects a single child " +
    
  1687.           'that is a function. If you did pass a function, make sure there ' +
    
  1688.           'is no trailing or leading whitespace around it.',
    
  1689.       );
    
  1690.     }
    
  1691.   }
    
  1692. 
    
  1693.   const newValue = readContext(context);
    
  1694.   const newChildren = render(newValue);
    
  1695. 
    
  1696.   const prevKeyPath = task.keyPath;
    
  1697.   task.keyPath = keyPath;
    
  1698.   renderNodeDestructive(request, task, null, newChildren, -1);
    
  1699.   task.keyPath = prevKeyPath;
    
  1700. }
    
  1701. 
    
  1702. function renderContextProvider(
    
  1703.   request: Request,
    
  1704.   task: Task,
    
  1705.   keyPath: KeyNode,
    
  1706.   type: ReactProviderType<any>,
    
  1707.   props: Object,
    
  1708. ): void {
    
  1709.   const context = type._context;
    
  1710.   const value = props.value;
    
  1711.   const children = props.children;
    
  1712.   let prevSnapshot;
    
  1713.   if (__DEV__) {
    
  1714.     prevSnapshot = task.context;
    
  1715.   }
    
  1716.   const prevKeyPath = task.keyPath;
    
  1717.   task.context = pushProvider(context, value);
    
  1718.   task.keyPath = keyPath;
    
  1719.   renderNodeDestructive(request, task, null, children, -1);
    
  1720.   task.context = popProvider(context);
    
  1721.   task.keyPath = prevKeyPath;
    
  1722.   if (__DEV__) {
    
  1723.     if (prevSnapshot !== task.context) {
    
  1724.       console.error(
    
  1725.         'Popping the context provider did not return back to the original snapshot. This is a bug in React.',
    
  1726.       );
    
  1727.     }
    
  1728.   }
    
  1729. }
    
  1730. 
    
  1731. function renderLazyComponent(
    
  1732.   request: Request,
    
  1733.   task: Task,
    
  1734.   keyPath: KeyNode,
    
  1735.   prevThenableState: ThenableState | null,
    
  1736.   lazyComponent: LazyComponentType<any, any>,
    
  1737.   props: Object,
    
  1738.   ref: any,
    
  1739. ): void {
    
  1740.   pushBuiltInComponentStackInDEV(task, 'Lazy');
    
  1741.   const payload = lazyComponent._payload;
    
  1742.   const init = lazyComponent._init;
    
  1743.   const Component = init(payload);
    
  1744.   const resolvedProps = resolveDefaultProps(Component, props);
    
  1745.   renderElement(
    
  1746.     request,
    
  1747.     task,
    
  1748.     keyPath,
    
  1749.     prevThenableState,
    
  1750.     Component,
    
  1751.     resolvedProps,
    
  1752.     ref,
    
  1753.   );
    
  1754.   popComponentStackInDEV(task);
    
  1755. }
    
  1756. 
    
  1757. function renderOffscreen(
    
  1758.   request: Request,
    
  1759.   task: Task,
    
  1760.   keyPath: KeyNode,
    
  1761.   props: Object,
    
  1762. ): void {
    
  1763.   const mode: ?OffscreenMode = (props.mode: any);
    
  1764.   if (mode === 'hidden') {
    
  1765.     // A hidden Offscreen boundary is not server rendered. Prerendering happens
    
  1766.     // on the client.
    
  1767.   } else {
    
  1768.     // A visible Offscreen boundary is treated exactly like a fragment: a
    
  1769.     // pure indirection.
    
  1770.     const prevKeyPath = task.keyPath;
    
  1771.     task.keyPath = keyPath;
    
  1772.     renderNodeDestructive(request, task, null, props.children, -1);
    
  1773.     task.keyPath = prevKeyPath;
    
  1774.   }
    
  1775. }
    
  1776. 
    
  1777. function renderElement(
    
  1778.   request: Request,
    
  1779.   task: Task,
    
  1780.   keyPath: KeyNode,
    
  1781.   prevThenableState: ThenableState | null,
    
  1782.   type: any,
    
  1783.   props: Object,
    
  1784.   ref: any,
    
  1785. ): void {
    
  1786.   if (typeof type === 'function') {
    
  1787.     if (shouldConstruct(type)) {
    
  1788.       renderClassComponent(request, task, keyPath, type, props);
    
  1789.       return;
    
  1790.     } else {
    
  1791.       renderIndeterminateComponent(
    
  1792.         request,
    
  1793.         task,
    
  1794.         keyPath,
    
  1795.         prevThenableState,
    
  1796.         type,
    
  1797.         props,
    
  1798.       );
    
  1799.       return;
    
  1800.     }
    
  1801.   }
    
  1802.   if (typeof type === 'string') {
    
  1803.     renderHostElement(request, task, keyPath, type, props);
    
  1804.     return;
    
  1805.   }
    
  1806. 
    
  1807.   switch (type) {
    
  1808.     // LegacyHidden acts the same as a fragment. This only works because we
    
  1809.     // currently assume that every instance of LegacyHidden is accompanied by a
    
  1810.     // host component wrapper. In the hidden mode, the host component is given a
    
  1811.     // `hidden` attribute, which ensures that the initial HTML is not visible.
    
  1812.     // To support the use of LegacyHidden as a true fragment, without an extra
    
  1813.     // DOM node, we would have to hide the initial HTML in some other way.
    
  1814.     // TODO: Delete in LegacyHidden. It's an unstable API only used in the
    
  1815.     // www build. As a migration step, we could add a special prop to Offscreen
    
  1816.     // that simulates the old behavior (no hiding, no change to effects).
    
  1817.     case REACT_LEGACY_HIDDEN_TYPE:
    
  1818.     case REACT_DEBUG_TRACING_MODE_TYPE:
    
  1819.     case REACT_STRICT_MODE_TYPE:
    
  1820.     case REACT_PROFILER_TYPE:
    
  1821.     case REACT_FRAGMENT_TYPE: {
    
  1822.       const prevKeyPath = task.keyPath;
    
  1823.       task.keyPath = keyPath;
    
  1824.       renderNodeDestructive(request, task, null, props.children, -1);
    
  1825.       task.keyPath = prevKeyPath;
    
  1826.       return;
    
  1827.     }
    
  1828.     case REACT_OFFSCREEN_TYPE: {
    
  1829.       renderOffscreen(request, task, keyPath, props);
    
  1830.       return;
    
  1831.     }
    
  1832.     case REACT_SUSPENSE_LIST_TYPE: {
    
  1833.       pushBuiltInComponentStackInDEV(task, 'SuspenseList');
    
  1834.       // TODO: SuspenseList should control the boundaries.
    
  1835.       const prevKeyPath = task.keyPath;
    
  1836.       task.keyPath = keyPath;
    
  1837.       renderNodeDestructive(request, task, null, props.children, -1);
    
  1838.       task.keyPath = prevKeyPath;
    
  1839.       popComponentStackInDEV(task);
    
  1840.       return;
    
  1841.     }
    
  1842.     case REACT_SCOPE_TYPE: {
    
  1843.       if (enableScopeAPI) {
    
  1844.         const prevKeyPath = task.keyPath;
    
  1845.         task.keyPath = keyPath;
    
  1846.         renderNodeDestructive(request, task, null, props.children, -1);
    
  1847.         task.keyPath = prevKeyPath;
    
  1848.         return;
    
  1849.       }
    
  1850.       throw new Error('ReactDOMServer does not yet support scope components.');
    
  1851.     }
    
  1852.     case REACT_SUSPENSE_TYPE: {
    
  1853.       if (
    
  1854.         enableSuspenseAvoidThisFallbackFizz &&
    
  1855.         props.unstable_avoidThisFallback === true
    
  1856.       ) {
    
  1857.         renderBackupSuspenseBoundary(request, task, keyPath, props);
    
  1858.       } else {
    
  1859.         renderSuspenseBoundary(request, task, keyPath, props);
    
  1860.       }
    
  1861.       return;
    
  1862.     }
    
  1863.   }
    
  1864. 
    
  1865.   if (typeof type === 'object' && type !== null) {
    
  1866.     switch (type.$$typeof) {
    
  1867.       case REACT_FORWARD_REF_TYPE: {
    
  1868.         renderForwardRef(
    
  1869.           request,
    
  1870.           task,
    
  1871.           keyPath,
    
  1872.           prevThenableState,
    
  1873.           type,
    
  1874.           props,
    
  1875.           ref,
    
  1876.         );
    
  1877.         return;
    
  1878.       }
    
  1879.       case REACT_MEMO_TYPE: {
    
  1880.         renderMemo(request, task, keyPath, prevThenableState, type, props, ref);
    
  1881.         return;
    
  1882.       }
    
  1883.       case REACT_PROVIDER_TYPE: {
    
  1884.         renderContextProvider(request, task, keyPath, type, props);
    
  1885.         return;
    
  1886.       }
    
  1887.       case REACT_CONTEXT_TYPE: {
    
  1888.         renderContextConsumer(request, task, keyPath, type, props);
    
  1889.         return;
    
  1890.       }
    
  1891.       case REACT_LAZY_TYPE: {
    
  1892.         renderLazyComponent(
    
  1893.           request,
    
  1894.           task,
    
  1895.           keyPath,
    
  1896.           prevThenableState,
    
  1897.           type,
    
  1898.           props,
    
  1899.         );
    
  1900.         return;
    
  1901.       }
    
  1902.     }
    
  1903.   }
    
  1904. 
    
  1905.   let info = '';
    
  1906.   if (__DEV__) {
    
  1907.     if (
    
  1908.       type === undefined ||
    
  1909.       (typeof type === 'object' &&
    
  1910.         type !== null &&
    
  1911.         Object.keys(type).length === 0)
    
  1912.     ) {
    
  1913.       info +=
    
  1914.         ' You likely forgot to export your component from the file ' +
    
  1915.         "it's defined in, or you might have mixed up default and " +
    
  1916.         'named imports.';
    
  1917.     }
    
  1918.   }
    
  1919. 
    
  1920.   throw new Error(
    
  1921.     'Element type is invalid: expected a string (for built-in ' +
    
  1922.       'components) or a class/function (for composite components) ' +
    
  1923.       `but got: ${type == null ? type : typeof type}.${info}`,
    
  1924.   );
    
  1925. }
    
  1926. 
    
  1927. function resumeNode(
    
  1928.   request: Request,
    
  1929.   task: ReplayTask,
    
  1930.   segmentId: number,
    
  1931.   node: ReactNodeList,
    
  1932.   childIndex: number,
    
  1933. ): void {
    
  1934.   const prevReplay = task.replay;
    
  1935.   const blockedBoundary = task.blockedBoundary;
    
  1936.   const resumedSegment = createPendingSegment(
    
  1937.     request,
    
  1938.     0,
    
  1939.     null,
    
  1940.     task.formatContext,
    
  1941.     false,
    
  1942.     false,
    
  1943.   );
    
  1944.   resumedSegment.id = segmentId;
    
  1945.   resumedSegment.parentFlushed = true;
    
  1946.   try {
    
  1947.     // Convert the current ReplayTask to a RenderTask.
    
  1948.     const renderTask: RenderTask = (task: any);
    
  1949.     renderTask.replay = null;
    
  1950.     renderTask.blockedSegment = resumedSegment;
    
  1951.     renderNode(request, task, node, childIndex);
    
  1952.     resumedSegment.status = COMPLETED;
    
  1953.     if (blockedBoundary === null) {
    
  1954.       request.completedRootSegment = resumedSegment;
    
  1955.     } else {
    
  1956.       queueCompletedSegment(blockedBoundary, resumedSegment);
    
  1957.       if (blockedBoundary.parentFlushed) {
    
  1958.         request.partialBoundaries.push(blockedBoundary);
    
  1959.       }
    
  1960.     }
    
  1961.   } finally {
    
  1962.     // Restore to a ReplayTask.
    
  1963.     task.replay = prevReplay;
    
  1964.     task.blockedSegment = null;
    
  1965.   }
    
  1966. }
    
  1967. 
    
  1968. function replayElement(
    
  1969.   request: Request,
    
  1970.   task: ReplayTask,
    
  1971.   keyPath: KeyNode,
    
  1972.   prevThenableState: ThenableState | null,
    
  1973.   name: null | string,
    
  1974.   keyOrIndex: number | string,
    
  1975.   childIndex: number,
    
  1976.   type: any,
    
  1977.   props: Object,
    
  1978.   ref: any,
    
  1979.   replay: ReplaySet,
    
  1980. ): void {
    
  1981.   // We're replaying. Find the path to follow.
    
  1982.   const replayNodes = replay.nodes;
    
  1983.   for (let i = 0; i < replayNodes.length; i++) {
    
  1984.     // Flow doesn't support refinement on tuples so we do it manually here.
    
  1985.     const node = replayNodes[i];
    
  1986.     if (keyOrIndex !== node[1]) {
    
  1987.       continue;
    
  1988.     }
    
  1989.     if (node.length === 4) {
    
  1990.       // Matched a replayable path.
    
  1991.       // Let's double check that the component name matches as a precaution.
    
  1992.       if (name !== null && name !== node[0]) {
    
  1993.         throw new Error(
    
  1994.           'Expected the resume to render <' +
    
  1995.             (node[0]: any) +
    
  1996.             '> in this slot but instead it rendered <' +
    
  1997.             name +
    
  1998.             '>. ' +
    
  1999.             "The tree doesn't match so React will fallback to client rendering.",
    
  2000.         );
    
  2001.       }
    
  2002.       const childNodes = node[2];
    
  2003.       const childSlots = node[3];
    
  2004.       const currentNode = task.node;
    
  2005.       task.replay = {nodes: childNodes, slots: childSlots, pendingTasks: 1};
    
  2006.       try {
    
  2007.         renderElement(
    
  2008.           request,
    
  2009.           task,
    
  2010.           keyPath,
    
  2011.           prevThenableState,
    
  2012.           type,
    
  2013.           props,
    
  2014.           ref,
    
  2015.         );
    
  2016.         if (
    
  2017.           task.replay.pendingTasks === 1 &&
    
  2018.           task.replay.nodes.length > 0
    
  2019.           // TODO check remaining slots
    
  2020.         ) {
    
  2021.           throw new Error(
    
  2022.             "Couldn't find all resumable slots by key/index during replaying. " +
    
  2023.               "The tree doesn't match so React will fallback to client rendering.",
    
  2024.           );
    
  2025.         }
    
  2026.         task.replay.pendingTasks--;
    
  2027.       } catch (x) {
    
  2028.         if (
    
  2029.           typeof x === 'object' &&
    
  2030.           x !== null &&
    
  2031.           (x === SuspenseException || typeof x.then === 'function')
    
  2032.         ) {
    
  2033.           // Suspend
    
  2034.           if (task.node === currentNode) {
    
  2035.             // This same element suspended so we need to pop the replay we just added.
    
  2036.             task.replay = replay;
    
  2037.           }
    
  2038.           throw x;
    
  2039.         }
    
  2040.         task.replay.pendingTasks--;
    
  2041.         // Unlike regular render, we don't terminate the siblings if we error
    
  2042.         // during a replay. That's because this component didn't actually error
    
  2043.         // in the original prerender. What's unable to complete is the child
    
  2044.         // replay nodes which might be Suspense boundaries which are able to
    
  2045.         // absorb the error and we can still continue with siblings.
    
  2046.         erroredReplay(request, task.blockedBoundary, x, childNodes, childSlots);
    
  2047.       }
    
  2048.       task.replay = replay;
    
  2049.     } else {
    
  2050.       // Let's double check that the component type matches.
    
  2051.       if (type !== REACT_SUSPENSE_TYPE) {
    
  2052.         const expectedType = 'Suspense';
    
  2053.         throw new Error(
    
  2054.           'Expected the resume to render <' +
    
  2055.             expectedType +
    
  2056.             '> in this slot but instead it rendered <' +
    
  2057.             (getComponentNameFromType(type) || 'Unknown') +
    
  2058.             '>. ' +
    
  2059.             "The tree doesn't match so React will fallback to client rendering.",
    
  2060.         );
    
  2061.       }
    
  2062.       // Matched a replayable path.
    
  2063.       replaySuspenseBoundary(
    
  2064.         request,
    
  2065.         task,
    
  2066.         keyPath,
    
  2067.         props,
    
  2068.         node[5],
    
  2069.         node[2],
    
  2070.         node[3],
    
  2071.         node[4] === null ? [] : node[4][2],
    
  2072.         node[4] === null ? null : node[4][3],
    
  2073.       );
    
  2074.     }
    
  2075.     // We finished rendering this node, so now we can consume this
    
  2076.     // slot. This must happen after in case we rerender this task.
    
  2077.     replayNodes.splice(i, 1);
    
  2078.     return;
    
  2079.   }
    
  2080.   // We didn't find any matching nodes. We assume that this element was already
    
  2081.   // rendered in the prelude and skip it.
    
  2082. }
    
  2083. 
    
  2084. // $FlowFixMe[missing-local-annot]
    
  2085. function validateIterable(iterable, iteratorFn: Function): void {
    
  2086.   if (__DEV__) {
    
  2087.     // We don't support rendering Generators because it's a mutation.
    
  2088.     // See https://github.com/facebook/react/issues/12995
    
  2089.     if (
    
  2090.       typeof Symbol === 'function' &&
    
  2091.       iterable[Symbol.toStringTag] === 'Generator'
    
  2092.     ) {
    
  2093.       if (!didWarnAboutGenerators) {
    
  2094.         console.error(
    
  2095.           'Using Generators as children is unsupported and will likely yield ' +
    
  2096.             'unexpected results because enumerating a generator mutates it. ' +
    
  2097.             'You may convert it to an array with `Array.from()` or the ' +
    
  2098.             '`[...spread]` operator before rendering. Keep in mind ' +
    
  2099.             'you might need to polyfill these features for older browsers.',
    
  2100.         );
    
  2101.       }
    
  2102.       didWarnAboutGenerators = true;
    
  2103.     }
    
  2104. 
    
  2105.     // Warn about using Maps as children
    
  2106.     if ((iterable: any).entries === iteratorFn) {
    
  2107.       if (!didWarnAboutMaps) {
    
  2108.         console.error(
    
  2109.           'Using Maps as children is not supported. ' +
    
  2110.             'Use an array of keyed ReactElements instead.',
    
  2111.         );
    
  2112.       }
    
  2113.       didWarnAboutMaps = true;
    
  2114.     }
    
  2115.   }
    
  2116. }
    
  2117. 
    
  2118. function renderNodeDestructive(
    
  2119.   request: Request,
    
  2120.   task: Task,
    
  2121.   // The thenable state reused from the previous attempt, if any. This is almost
    
  2122.   // always null, except when called by retryTask.
    
  2123.   prevThenableState: ThenableState | null,
    
  2124.   node: ReactNodeList,
    
  2125.   childIndex: number,
    
  2126. ): void {
    
  2127.   if (__DEV__) {
    
  2128.     // In Dev we wrap renderNodeDestructiveImpl in a try / catch so we can capture
    
  2129.     // a component stack at the right place in the tree. We don't do this in renderNode
    
  2130.     // becuase it is not called at every layer of the tree and we may lose frames
    
  2131.     try {
    
  2132.       return renderNodeDestructiveImpl(
    
  2133.         request,
    
  2134.         task,
    
  2135.         prevThenableState,
    
  2136.         node,
    
  2137.         childIndex,
    
  2138.       );
    
  2139.     } catch (x) {
    
  2140.       if (typeof x === 'object' && x !== null && typeof x.then === 'function') {
    
  2141.         // This is a Wakable, noop
    
  2142.       } else {
    
  2143.         // This is an error, stash the component stack if it is null.
    
  2144.         lastBoundaryErrorComponentStackDev =
    
  2145.           lastBoundaryErrorComponentStackDev !== null
    
  2146.             ? lastBoundaryErrorComponentStackDev
    
  2147.             : getCurrentStackInDEV();
    
  2148.       }
    
  2149.       // rethrow so normal suspense logic can handle thrown value accordingly
    
  2150.       throw x;
    
  2151.     }
    
  2152.   } else {
    
  2153.     return renderNodeDestructiveImpl(
    
  2154.       request,
    
  2155.       task,
    
  2156.       prevThenableState,
    
  2157.       node,
    
  2158.       childIndex,
    
  2159.     );
    
  2160.   }
    
  2161. }
    
  2162. 
    
  2163. // This function by it self renders a node and consumes the task by mutating it
    
  2164. // to update the current execution state.
    
  2165. function renderNodeDestructiveImpl(
    
  2166.   request: Request,
    
  2167.   task: Task,
    
  2168.   prevThenableState: ThenableState | null,
    
  2169.   node: ReactNodeList,
    
  2170.   childIndex: number,
    
  2171. ): void {
    
  2172.   if (task.replay !== null && typeof task.replay.slots === 'number') {
    
  2173.     // TODO: Figure out a cheaper place than this hot path to do this check.
    
  2174.     const resumeSegmentID = task.replay.slots;
    
  2175.     resumeNode(request, task, resumeSegmentID, node, childIndex);
    
  2176.     return;
    
  2177.   }
    
  2178.   // Stash the node we're working on. We'll pick up from this task in case
    
  2179.   // something suspends.
    
  2180.   task.node = node;
    
  2181.   task.childIndex = childIndex;
    
  2182. 
    
  2183.   // Handle object types
    
  2184.   if (typeof node === 'object' && node !== null) {
    
  2185.     switch ((node: any).$$typeof) {
    
  2186.       case REACT_ELEMENT_TYPE: {
    
  2187.         const element: any = node;
    
  2188.         const type = element.type;
    
  2189.         const key = element.key;
    
  2190.         const props = element.props;
    
  2191.         const ref = element.ref;
    
  2192.         const name = getComponentNameFromType(type);
    
  2193.         const keyOrIndex =
    
  2194.           key == null ? (childIndex === -1 ? 0 : childIndex) : key;
    
  2195.         const keyPath = [task.keyPath, name, keyOrIndex];
    
  2196.         if (task.replay !== null) {
    
  2197.           replayElement(
    
  2198.             request,
    
  2199.             task,
    
  2200.             keyPath,
    
  2201.             prevThenableState,
    
  2202.             name,
    
  2203.             keyOrIndex,
    
  2204.             childIndex,
    
  2205.             type,
    
  2206.             props,
    
  2207.             ref,
    
  2208.             task.replay,
    
  2209.           );
    
  2210.           // No matches found for this node. We assume it's already emitted in the
    
  2211.           // prelude and skip it during the replay.
    
  2212.         } else {
    
  2213.           // We're doing a plain render.
    
  2214.           renderElement(
    
  2215.             request,
    
  2216.             task,
    
  2217.             keyPath,
    
  2218.             prevThenableState,
    
  2219.             type,
    
  2220.             props,
    
  2221.             ref,
    
  2222.           );
    
  2223.         }
    
  2224.         return;
    
  2225.       }
    
  2226.       case REACT_PORTAL_TYPE:
    
  2227.         throw new Error(
    
  2228.           'Portals are not currently supported by the server renderer. ' +
    
  2229.             'Render them conditionally so that they only appear on the client render.',
    
  2230.         );
    
  2231.       case REACT_LAZY_TYPE: {
    
  2232.         const lazyNode: LazyComponentType<any, any> = (node: any);
    
  2233.         const payload = lazyNode._payload;
    
  2234.         const init = lazyNode._init;
    
  2235.         let resolvedNode;
    
  2236.         if (__DEV__) {
    
  2237.           try {
    
  2238.             resolvedNode = init(payload);
    
  2239.           } catch (x) {
    
  2240.             if (
    
  2241.               typeof x === 'object' &&
    
  2242.               x !== null &&
    
  2243.               typeof x.then === 'function'
    
  2244.             ) {
    
  2245.               // this Lazy initializer is suspending. push a temporary frame onto the stack so it can be
    
  2246.               // popped off in spawnNewSuspendedTask. This aligns stack behavior between Lazy in element position
    
  2247.               // vs Component position. We do not want the frame for Errors so we exclusively do this in
    
  2248.               // the wakeable branch
    
  2249.               pushBuiltInComponentStackInDEV(task, 'Lazy');
    
  2250.             }
    
  2251.             throw x;
    
  2252.           }
    
  2253.         } else {
    
  2254.           resolvedNode = init(payload);
    
  2255.         }
    
  2256.         renderNodeDestructive(request, task, null, resolvedNode, childIndex);
    
  2257.         return;
    
  2258.       }
    
  2259.     }
    
  2260. 
    
  2261.     if (isArray(node)) {
    
  2262.       renderChildrenArray(request, task, node, childIndex);
    
  2263.       return;
    
  2264.     }
    
  2265. 
    
  2266.     const iteratorFn = getIteratorFn(node);
    
  2267.     if (iteratorFn) {
    
  2268.       if (__DEV__) {
    
  2269.         validateIterable(node, iteratorFn);
    
  2270.       }
    
  2271.       const iterator = iteratorFn.call(node);
    
  2272.       if (iterator) {
    
  2273.         // We need to know how many total children are in this set, so that we
    
  2274.         // can allocate enough id slots to acommodate them. So we must exhaust
    
  2275.         // the iterator before we start recursively rendering the children.
    
  2276.         // TODO: This is not great but I think it's inherent to the id
    
  2277.         // generation algorithm.
    
  2278.         let step = iterator.next();
    
  2279.         // If there are not entries, we need to push an empty so we start by checking that.
    
  2280.         if (!step.done) {
    
  2281.           const children = [];
    
  2282.           do {
    
  2283.             children.push(step.value);
    
  2284.             step = iterator.next();
    
  2285.           } while (!step.done);
    
  2286.           renderChildrenArray(request, task, children, childIndex);
    
  2287.           return;
    
  2288.         }
    
  2289.         return;
    
  2290.       }
    
  2291.     }
    
  2292. 
    
  2293.     // Usables are a valid React node type. When React encounters a Usable in
    
  2294.     // a child position, it unwraps it using the same algorithm as `use`. For
    
  2295.     // example, for promises, React will throw an exception to unwind the
    
  2296.     // stack, then replay the component once the promise resolves.
    
  2297.     //
    
  2298.     // A difference from `use` is that React will keep unwrapping the value
    
  2299.     // until it reaches a non-Usable type.
    
  2300.     //
    
  2301.     // e.g. Usable<Usable<Usable<T>>> should resolve to T
    
  2302.     const maybeUsable: Object = node;
    
  2303.     if (typeof maybeUsable.then === 'function') {
    
  2304.       const thenable: Thenable<ReactNodeList> = (maybeUsable: any);
    
  2305.       return renderNodeDestructiveImpl(
    
  2306.         request,
    
  2307.         task,
    
  2308.         null,
    
  2309.         unwrapThenable(thenable),
    
  2310.         childIndex,
    
  2311.       );
    
  2312.     }
    
  2313. 
    
  2314.     if (
    
  2315.       maybeUsable.$$typeof === REACT_CONTEXT_TYPE ||
    
  2316.       maybeUsable.$$typeof === REACT_SERVER_CONTEXT_TYPE
    
  2317.     ) {
    
  2318.       const context: ReactContext<ReactNodeList> = (maybeUsable: any);
    
  2319.       return renderNodeDestructiveImpl(
    
  2320.         request,
    
  2321.         task,
    
  2322.         null,
    
  2323.         readContext(context),
    
  2324.         childIndex,
    
  2325.       );
    
  2326.     }
    
  2327. 
    
  2328.     // $FlowFixMe[method-unbinding]
    
  2329.     const childString = Object.prototype.toString.call(node);
    
  2330. 
    
  2331.     throw new Error(
    
  2332.       `Objects are not valid as a React child (found: ${
    
  2333.         childString === '[object Object]'
    
  2334.           ? 'object with keys {' + Object.keys(node).join(', ') + '}'
    
  2335.           : childString
    
  2336.       }). ` +
    
  2337.         'If you meant to render a collection of children, use an array ' +
    
  2338.         'instead.',
    
  2339.     );
    
  2340.   }
    
  2341. 
    
  2342.   if (typeof node === 'string') {
    
  2343.     const segment = task.blockedSegment;
    
  2344.     if (segment === null) {
    
  2345.       // We assume a text node doesn't have a representation in the replay set,
    
  2346.       // since it can't postpone. If it does, it'll be left unmatched and error.
    
  2347.     } else {
    
  2348.       segment.lastPushedText = pushTextInstance(
    
  2349.         segment.chunks,
    
  2350.         node,
    
  2351.         request.renderState,
    
  2352.         segment.lastPushedText,
    
  2353.       );
    
  2354.     }
    
  2355.     return;
    
  2356.   }
    
  2357. 
    
  2358.   if (typeof node === 'number') {
    
  2359.     const segment = task.blockedSegment;
    
  2360.     if (segment === null) {
    
  2361.       // We assume a text node doesn't have a representation in the replay set,
    
  2362.       // since it can't postpone. If it does, it'll be left unmatched and error.
    
  2363.     } else {
    
  2364.       segment.lastPushedText = pushTextInstance(
    
  2365.         segment.chunks,
    
  2366.         '' + node,
    
  2367.         request.renderState,
    
  2368.         segment.lastPushedText,
    
  2369.       );
    
  2370.     }
    
  2371.     return;
    
  2372.   }
    
  2373. 
    
  2374.   if (__DEV__) {
    
  2375.     if (typeof node === 'function') {
    
  2376.       console.error(
    
  2377.         'Functions are not valid as a React child. This may happen if ' +
    
  2378.           'you return a Component instead of <Component /> from render. ' +
    
  2379.           'Or maybe you meant to call this function rather than return it.',
    
  2380.       );
    
  2381.     }
    
  2382.   }
    
  2383. }
    
  2384. 
    
  2385. function replayFragment(
    
  2386.   request: Request,
    
  2387.   task: ReplayTask,
    
  2388.   children: Array<any>,
    
  2389.   childIndex: number,
    
  2390. ): void {
    
  2391.   // If we're supposed follow this array, we'd expect to see a ReplayNode matching
    
  2392.   // this fragment.
    
  2393.   const replay = task.replay;
    
  2394.   const replayNodes = replay.nodes;
    
  2395.   for (let j = 0; j < replayNodes.length; j++) {
    
  2396.     const node = replayNodes[j];
    
  2397.     if (node[1] !== childIndex) {
    
  2398.       continue;
    
  2399.     }
    
  2400.     // Matched a replayable path.
    
  2401.     const childNodes = node[2];
    
  2402.     const childSlots = node[3];
    
  2403.     task.replay = {nodes: childNodes, slots: childSlots, pendingTasks: 1};
    
  2404.     try {
    
  2405.       renderChildrenArray(request, task, children, -1);
    
  2406.       if (task.replay.pendingTasks === 1 && task.replay.nodes.length > 0) {
    
  2407.         throw new Error(
    
  2408.           "Couldn't find all resumable slots by key/index during replaying. " +
    
  2409.             "The tree doesn't match so React will fallback to client rendering.",
    
  2410.         );
    
  2411.       }
    
  2412.       task.replay.pendingTasks--;
    
  2413.     } catch (x) {
    
  2414.       if (
    
  2415.         typeof x === 'object' &&
    
  2416.         x !== null &&
    
  2417.         (x === SuspenseException || typeof x.then === 'function')
    
  2418.       ) {
    
  2419.         // Suspend
    
  2420.         throw x;
    
  2421.       }
    
  2422.       task.replay.pendingTasks--;
    
  2423.       // Unlike regular render, we don't terminate the siblings if we error
    
  2424.       // during a replay. That's because this component didn't actually error
    
  2425.       // in the original prerender. What's unable to complete is the child
    
  2426.       // replay nodes which might be Suspense boundaries which are able to
    
  2427.       // absorb the error and we can still continue with siblings.
    
  2428.       // This is an error, stash the component stack if it is null.
    
  2429.       erroredReplay(request, task.blockedBoundary, x, childNodes, childSlots);
    
  2430.     }
    
  2431.     task.replay = replay;
    
  2432.     // We finished rendering this node, so now we can consume this
    
  2433.     // slot. This must happen after in case we rerender this task.
    
  2434.     replayNodes.splice(j, 1);
    
  2435.     break;
    
  2436.   }
    
  2437. }
    
  2438. 
    
  2439. function renderChildrenArray(
    
  2440.   request: Request,
    
  2441.   task: Task,
    
  2442.   children: Array<any>,
    
  2443.   childIndex: number,
    
  2444. ): void {
    
  2445.   const prevKeyPath = task.keyPath;
    
  2446.   if (childIndex !== -1) {
    
  2447.     task.keyPath = [task.keyPath, 'Fragment', childIndex];
    
  2448.     if (task.replay !== null) {
    
  2449.       replayFragment(
    
  2450.         request,
    
  2451.         // $FlowFixMe: Refined.
    
  2452.         task,
    
  2453.         children,
    
  2454.         childIndex,
    
  2455.       );
    
  2456.       task.keyPath = prevKeyPath;
    
  2457.       return;
    
  2458.     }
    
  2459.   }
    
  2460.   const prevTreeContext = task.treeContext;
    
  2461.   const totalChildren = children.length;
    
  2462. 
    
  2463.   if (task.replay !== null) {
    
  2464.     // Replay
    
  2465.     // First we need to check if we have any resume slots at this level.
    
  2466.     const resumeSlots = task.replay.slots;
    
  2467.     if (resumeSlots !== null && typeof resumeSlots === 'object') {
    
  2468.       for (let i = 0; i < totalChildren; i++) {
    
  2469.         const node = children[i];
    
  2470.         task.treeContext = pushTreeContext(prevTreeContext, totalChildren, i);
    
  2471.         // We need to use the non-destructive form so that we can safely pop back
    
  2472.         // up and render the sibling if something suspends.
    
  2473.         const resumeSegmentID = resumeSlots[i];
    
  2474.         // TODO: If this errors we should still continue with the next sibling.
    
  2475.         if (typeof resumeSegmentID === 'number') {
    
  2476.           resumeNode(request, task, resumeSegmentID, node, i);
    
  2477.           // We finished rendering this node, so now we can consume this
    
  2478.           // slot. This must happen after in case we rerender this task.
    
  2479.           delete resumeSlots[i];
    
  2480.         } else {
    
  2481.           renderNode(request, task, node, i);
    
  2482.         }
    
  2483.       }
    
  2484.       task.treeContext = prevTreeContext;
    
  2485.       task.keyPath = prevKeyPath;
    
  2486.       return;
    
  2487.     }
    
  2488.   }
    
  2489. 
    
  2490.   for (let i = 0; i < totalChildren; i++) {
    
  2491.     const node = children[i];
    
  2492.     task.treeContext = pushTreeContext(prevTreeContext, totalChildren, i);
    
  2493.     // We need to use the non-destructive form so that we can safely pop back
    
  2494.     // up and render the sibling if something suspends.
    
  2495.     renderNode(request, task, node, i);
    
  2496.   }
    
  2497. 
    
  2498.   // Because this context is always set right before rendering every child, we
    
  2499.   // only need to reset it to the previous value at the very end.
    
  2500.   task.treeContext = prevTreeContext;
    
  2501.   task.keyPath = prevKeyPath;
    
  2502. }
    
  2503. 
    
  2504. function trackPostpone(
    
  2505.   request: Request,
    
  2506.   trackedPostpones: PostponedHoles,
    
  2507.   task: Task,
    
  2508.   segment: Segment,
    
  2509. ): void {
    
  2510.   segment.status = POSTPONED;
    
  2511. 
    
  2512.   const keyPath = task.keyPath;
    
  2513.   const boundary = task.blockedBoundary;
    
  2514. 
    
  2515.   if (boundary === null) {
    
  2516.     segment.id = request.nextSegmentId++;
    
  2517.     trackedPostpones.rootSlots = segment.id;
    
  2518.     if (request.completedRootSegment !== null) {
    
  2519.       // Postpone the root if this was a deeper segment.
    
  2520.       request.completedRootSegment.status = POSTPONED;
    
  2521.     }
    
  2522.     return;
    
  2523.   }
    
  2524. 
    
  2525.   if (boundary !== null && boundary.status === PENDING) {
    
  2526.     boundary.status = POSTPONED;
    
  2527.     // We need to eagerly assign it an ID because we'll need to refer to
    
  2528.     // it before flushing and we know that we can't inline it.
    
  2529.     boundary.rootSegmentID = request.nextSegmentId++;
    
  2530. 
    
  2531.     const boundaryKeyPath = boundary.trackedContentKeyPath;
    
  2532.     if (boundaryKeyPath === null) {
    
  2533.       throw new Error(
    
  2534.         'It should not be possible to postpone at the root. This is a bug in React.',
    
  2535.       );
    
  2536.     }
    
  2537. 
    
  2538.     const fallbackReplayNode = boundary.trackedFallbackNode;
    
  2539. 
    
  2540.     const children: Array<ReplayNode> = [];
    
  2541.     if (boundaryKeyPath === keyPath && task.childIndex === -1) {
    
  2542.       // Since we postponed directly in the Suspense boundary we can't have written anything
    
  2543.       // to its segment. Therefore this will end up becoming the root segment.
    
  2544.       segment.id = boundary.rootSegmentID;
    
  2545.       // We postponed directly inside the Suspense boundary so we mark this for resuming.
    
  2546.       const boundaryNode: ReplaySuspenseBoundary = [
    
  2547.         boundaryKeyPath[1],
    
  2548.         boundaryKeyPath[2],
    
  2549.         children,
    
  2550.         boundary.rootSegmentID,
    
  2551.         fallbackReplayNode,
    
  2552.         boundary.rootSegmentID,
    
  2553.       ];
    
  2554.       trackedPostpones.workingMap.set(boundaryKeyPath, boundaryNode);
    
  2555.       addToReplayParent(boundaryNode, boundaryKeyPath[0], trackedPostpones);
    
  2556.       return;
    
  2557.     } else {
    
  2558.       let boundaryNode: void | ReplayNode =
    
  2559.         trackedPostpones.workingMap.get(boundaryKeyPath);
    
  2560.       if (boundaryNode === undefined) {
    
  2561.         boundaryNode = [
    
  2562.           boundaryKeyPath[1],
    
  2563.           boundaryKeyPath[2],
    
  2564.           children,
    
  2565.           null,
    
  2566.           fallbackReplayNode,
    
  2567.           boundary.rootSegmentID,
    
  2568.         ];
    
  2569.         trackedPostpones.workingMap.set(boundaryKeyPath, boundaryNode);
    
  2570.         addToReplayParent(boundaryNode, boundaryKeyPath[0], trackedPostpones);
    
  2571.       } else {
    
  2572.         // Upgrade to ReplaySuspenseBoundary.
    
  2573.         const suspenseBoundary: ReplaySuspenseBoundary = (boundaryNode: any);
    
  2574.         suspenseBoundary[4] = fallbackReplayNode;
    
  2575.         suspenseBoundary[5] = boundary.rootSegmentID;
    
  2576.       }
    
  2577.       // Fall through to add the child node.
    
  2578.     }
    
  2579.   }
    
  2580. 
    
  2581.   // We know that this will leave a hole so we might as well assign an ID now.
    
  2582.   // We might have one already if we had a parent that gave us its ID.
    
  2583.   if (segment.id === -1) {
    
  2584.     if (segment.parentFlushed && boundary !== null) {
    
  2585.       // If this segment's parent was already flushed, it means we really just
    
  2586.       // skipped the parent and this segment is now the root.
    
  2587.       segment.id = boundary.rootSegmentID;
    
  2588.     } else {
    
  2589.       segment.id = request.nextSegmentId++;
    
  2590.     }
    
  2591.   }
    
  2592. 
    
  2593.   if (task.childIndex === -1) {
    
  2594.     // Resume starting from directly inside the previous parent element.
    
  2595.     if (keyPath === null) {
    
  2596.       trackedPostpones.rootSlots = segment.id;
    
  2597.     } else {
    
  2598.       const workingMap = trackedPostpones.workingMap;
    
  2599.       let resumableNode = workingMap.get(keyPath);
    
  2600.       if (resumableNode === undefined) {
    
  2601.         resumableNode = [
    
  2602.           keyPath[1],
    
  2603.           keyPath[2],
    
  2604.           ([]: Array<ReplayNode>),
    
  2605.           segment.id,
    
  2606.         ];
    
  2607.         addToReplayParent(resumableNode, keyPath[0], trackedPostpones);
    
  2608.       } else {
    
  2609.         resumableNode[3] = segment.id;
    
  2610.       }
    
  2611.     }
    
  2612.   } else {
    
  2613.     let slots;
    
  2614.     if (keyPath === null) {
    
  2615.       slots = trackedPostpones.rootSlots;
    
  2616.       if (slots === null) {
    
  2617.         slots = trackedPostpones.rootSlots = ({}: {[index: number]: number});
    
  2618.       } else if (typeof slots === 'number') {
    
  2619.         throw new Error(
    
  2620.           'It should not be possible to postpone both at the root of an element ' +
    
  2621.             'as well as a slot below. This is a bug in React.',
    
  2622.         );
    
  2623.       }
    
  2624.     } else {
    
  2625.       const workingMap = trackedPostpones.workingMap;
    
  2626.       let resumableNode = workingMap.get(keyPath);
    
  2627.       if (resumableNode === undefined) {
    
  2628.         slots = ({}: {[index: number]: number});
    
  2629.         resumableNode = ([
    
  2630.           keyPath[1],
    
  2631.           keyPath[2],
    
  2632.           ([]: Array<ReplayNode>),
    
  2633.           slots,
    
  2634.         ]: ReplayNode);
    
  2635.         workingMap.set(keyPath, resumableNode);
    
  2636.         addToReplayParent(resumableNode, keyPath[0], trackedPostpones);
    
  2637.       } else {
    
  2638.         slots = resumableNode[3];
    
  2639.         if (slots === null) {
    
  2640.           slots = resumableNode[3] = ({}: {[index: number]: number});
    
  2641.         } else if (typeof slots === 'number') {
    
  2642.           throw new Error(
    
  2643.             'It should not be possible to postpone both at the root of an element ' +
    
  2644.               'as well as a slot below. This is a bug in React.',
    
  2645.           );
    
  2646.         }
    
  2647.       }
    
  2648.     }
    
  2649.     slots[task.childIndex] = segment.id;
    
  2650.   }
    
  2651. }
    
  2652. 
    
  2653. function injectPostponedHole(
    
  2654.   request: Request,
    
  2655.   task: RenderTask,
    
  2656.   reason: string,
    
  2657. ): Segment {
    
  2658.   logPostpone(request, reason);
    
  2659.   // Something suspended, we'll need to create a new segment and resolve it later.
    
  2660.   const segment = task.blockedSegment;
    
  2661.   const insertionIndex = segment.chunks.length;
    
  2662.   const newSegment = createPendingSegment(
    
  2663.     request,
    
  2664.     insertionIndex,
    
  2665.     null,
    
  2666.     task.formatContext,
    
  2667.     // Adopt the parent segment's leading text embed
    
  2668.     segment.lastPushedText,
    
  2669.     // Assume we are text embedded at the trailing edge
    
  2670.     true,
    
  2671.   );
    
  2672.   segment.children.push(newSegment);
    
  2673.   // Reset lastPushedText for current Segment since the new Segment "consumed" it
    
  2674.   segment.lastPushedText = false;
    
  2675.   return newSegment;
    
  2676. }
    
  2677. 
    
  2678. function spawnNewSuspendedReplayTask(
    
  2679.   request: Request,
    
  2680.   task: ReplayTask,
    
  2681.   thenableState: ThenableState | null,
    
  2682.   x: Wakeable,
    
  2683. ): void {
    
  2684.   const newTask = createReplayTask(
    
  2685.     request,
    
  2686.     thenableState,
    
  2687.     task.replay,
    
  2688.     task.node,
    
  2689.     task.childIndex,
    
  2690.     task.blockedBoundary,
    
  2691.     task.abortSet,
    
  2692.     task.keyPath,
    
  2693.     task.formatContext,
    
  2694.     task.legacyContext,
    
  2695.     task.context,
    
  2696.     task.treeContext,
    
  2697.   );
    
  2698. 
    
  2699.   if (__DEV__) {
    
  2700.     if (task.componentStack !== null) {
    
  2701.       // We pop one task off the stack because the node that suspended will be tried again,
    
  2702.       // which will add it back onto the stack.
    
  2703.       newTask.componentStack = task.componentStack.parent;
    
  2704.     }
    
  2705.   }
    
  2706.   const ping = newTask.ping;
    
  2707.   x.then(ping, ping);
    
  2708. }
    
  2709. 
    
  2710. function spawnNewSuspendedRenderTask(
    
  2711.   request: Request,
    
  2712.   task: RenderTask,
    
  2713.   thenableState: ThenableState | null,
    
  2714.   x: Wakeable,
    
  2715. ): void {
    
  2716.   // Something suspended, we'll need to create a new segment and resolve it later.
    
  2717.   const segment = task.blockedSegment;
    
  2718.   const insertionIndex = segment.chunks.length;
    
  2719.   const newSegment = createPendingSegment(
    
  2720.     request,
    
  2721.     insertionIndex,
    
  2722.     null,
    
  2723.     task.formatContext,
    
  2724.     // Adopt the parent segment's leading text embed
    
  2725.     segment.lastPushedText,
    
  2726.     // Assume we are text embedded at the trailing edge
    
  2727.     true,
    
  2728.   );
    
  2729.   segment.children.push(newSegment);
    
  2730.   // Reset lastPushedText for current Segment since the new Segment "consumed" it
    
  2731.   segment.lastPushedText = false;
    
  2732.   const newTask = createRenderTask(
    
  2733.     request,
    
  2734.     thenableState,
    
  2735.     task.node,
    
  2736.     task.childIndex,
    
  2737.     task.blockedBoundary,
    
  2738.     newSegment,
    
  2739.     task.abortSet,
    
  2740.     task.keyPath,
    
  2741.     task.formatContext,
    
  2742.     task.legacyContext,
    
  2743.     task.context,
    
  2744.     task.treeContext,
    
  2745.   );
    
  2746. 
    
  2747.   if (__DEV__) {
    
  2748.     if (task.componentStack !== null) {
    
  2749.       // We pop one task off the stack because the node that suspended will be tried again,
    
  2750.       // which will add it back onto the stack.
    
  2751.       newTask.componentStack = task.componentStack.parent;
    
  2752.     }
    
  2753.   }
    
  2754.   const ping = newTask.ping;
    
  2755.   x.then(ping, ping);
    
  2756. }
    
  2757. 
    
  2758. // This is a non-destructive form of rendering a node. If it suspends it spawns
    
  2759. // a new task and restores the context of this task to what it was before.
    
  2760. function renderNode(
    
  2761.   request: Request,
    
  2762.   task: Task,
    
  2763.   node: ReactNodeList,
    
  2764.   childIndex: number,
    
  2765. ): void {
    
  2766.   // Snapshot the current context in case something throws to interrupt the
    
  2767.   // process.
    
  2768.   const previousFormatContext = task.formatContext;
    
  2769.   const previousLegacyContext = task.legacyContext;
    
  2770.   const previousContext = task.context;
    
  2771.   const previousKeyPath = task.keyPath;
    
  2772.   const previousTreeContext = task.treeContext;
    
  2773.   let previousComponentStack = null;
    
  2774.   if (__DEV__) {
    
  2775.     previousComponentStack = task.componentStack;
    
  2776.   }
    
  2777.   let x;
    
  2778.   // Store how much we've pushed at this point so we can reset it in case something
    
  2779.   // suspended partially through writing something.
    
  2780.   const segment = task.blockedSegment;
    
  2781.   if (segment === null) {
    
  2782.     // Replay
    
  2783.     try {
    
  2784.       return renderNodeDestructive(request, task, null, node, childIndex);
    
  2785.     } catch (thrownValue) {
    
  2786.       resetHooksState();
    
  2787. 
    
  2788.       x =
    
  2789.         thrownValue === SuspenseException
    
  2790.           ? // This is a special type of exception used for Suspense. For historical
    
  2791.             // reasons, the rest of the Suspense implementation expects the thrown
    
  2792.             // value to be a thenable, because before `use` existed that was the
    
  2793.             // (unstable) API for suspending. This implementation detail can change
    
  2794.             // later, once we deprecate the old API in favor of `use`.
    
  2795.             getSuspendedThenable()
    
  2796.           : thrownValue;
    
  2797. 
    
  2798.       if (typeof x === 'object' && x !== null) {
    
  2799.         // $FlowFixMe[method-unbinding]
    
  2800.         if (typeof x.then === 'function') {
    
  2801.           const wakeable: Wakeable = (x: any);
    
  2802.           const thenableState = getThenableStateAfterSuspending();
    
  2803.           spawnNewSuspendedReplayTask(
    
  2804.             request,
    
  2805.             // $FlowFixMe: Refined.
    
  2806.             task,
    
  2807.             thenableState,
    
  2808.             wakeable,
    
  2809.           );
    
  2810. 
    
  2811.           // Restore the context. We assume that this will be restored by the inner
    
  2812.           // functions in case nothing throws so we don't use "finally" here.
    
  2813.           task.formatContext = previousFormatContext;
    
  2814.           task.legacyContext = previousLegacyContext;
    
  2815.           task.context = previousContext;
    
  2816.           task.keyPath = previousKeyPath;
    
  2817.           task.treeContext = previousTreeContext;
    
  2818.           // Restore all active ReactContexts to what they were before.
    
  2819.           switchContext(previousContext);
    
  2820.           if (__DEV__) {
    
  2821.             task.componentStack = previousComponentStack;
    
  2822.           }
    
  2823.           return;
    
  2824.         }
    
  2825.       }
    
  2826. 
    
  2827.       // TODO: Abort any undiscovered Suspense boundaries in the ReplayNode.
    
  2828.     }
    
  2829.   } else {
    
  2830.     // Render
    
  2831.     const childrenLength = segment.children.length;
    
  2832.     const chunkLength = segment.chunks.length;
    
  2833.     try {
    
  2834.       return renderNodeDestructive(request, task, null, node, childIndex);
    
  2835.     } catch (thrownValue) {
    
  2836.       resetHooksState();
    
  2837. 
    
  2838.       // Reset the write pointers to where we started.
    
  2839.       segment.children.length = childrenLength;
    
  2840.       segment.chunks.length = chunkLength;
    
  2841. 
    
  2842.       x =
    
  2843.         thrownValue === SuspenseException
    
  2844.           ? // This is a special type of exception used for Suspense. For historical
    
  2845.             // reasons, the rest of the Suspense implementation expects the thrown
    
  2846.             // value to be a thenable, because before `use` existed that was the
    
  2847.             // (unstable) API for suspending. This implementation detail can change
    
  2848.             // later, once we deprecate the old API in favor of `use`.
    
  2849.             getSuspendedThenable()
    
  2850.           : thrownValue;
    
  2851. 
    
  2852.       if (typeof x === 'object' && x !== null) {
    
  2853.         // $FlowFixMe[method-unbinding]
    
  2854.         if (typeof x.then === 'function') {
    
  2855.           const wakeable: Wakeable = (x: any);
    
  2856.           const thenableState = getThenableStateAfterSuspending();
    
  2857.           spawnNewSuspendedRenderTask(
    
  2858.             request,
    
  2859.             // $FlowFixMe: Refined.
    
  2860.             task,
    
  2861.             thenableState,
    
  2862.             wakeable,
    
  2863.           );
    
  2864. 
    
  2865.           // Restore the context. We assume that this will be restored by the inner
    
  2866.           // functions in case nothing throws so we don't use "finally" here.
    
  2867.           task.formatContext = previousFormatContext;
    
  2868.           task.legacyContext = previousLegacyContext;
    
  2869.           task.context = previousContext;
    
  2870.           task.keyPath = previousKeyPath;
    
  2871.           task.treeContext = previousTreeContext;
    
  2872.           // Restore all active ReactContexts to what they were before.
    
  2873.           switchContext(previousContext);
    
  2874.           if (__DEV__) {
    
  2875.             task.componentStack = previousComponentStack;
    
  2876.           }
    
  2877.           return;
    
  2878.         }
    
  2879.         if (
    
  2880.           enablePostpone &&
    
  2881.           request.trackedPostpones !== null &&
    
  2882.           x.$$typeof === REACT_POSTPONE_TYPE &&
    
  2883.           task.blockedBoundary !== null // bubble if we're postponing in the shell
    
  2884.         ) {
    
  2885.           // If we're tracking postpones, we inject a hole here and continue rendering
    
  2886.           // sibling. Similar to suspending. If we're not tracking, we treat it more like
    
  2887.           // an error. Notably this doesn't spawn a new task since nothing will fill it
    
  2888.           // in during this prerender.
    
  2889.           const postponeInstance: Postpone = (x: any);
    
  2890.           const trackedPostpones = request.trackedPostpones;
    
  2891.           const postponedSegment = injectPostponedHole(
    
  2892.             request,
    
  2893.             ((task: any): RenderTask), // We don't use ReplayTasks in prerenders.
    
  2894.             postponeInstance.message,
    
  2895.           );
    
  2896.           trackPostpone(request, trackedPostpones, task, postponedSegment);
    
  2897. 
    
  2898.           // Restore the context. We assume that this will be restored by the inner
    
  2899.           // functions in case nothing throws so we don't use "finally" here.
    
  2900.           task.formatContext = previousFormatContext;
    
  2901.           task.legacyContext = previousLegacyContext;
    
  2902.           task.context = previousContext;
    
  2903.           task.keyPath = previousKeyPath;
    
  2904.           task.treeContext = previousTreeContext;
    
  2905.           // Restore all active ReactContexts to what they were before.
    
  2906.           switchContext(previousContext);
    
  2907.           if (__DEV__) {
    
  2908.             task.componentStack = previousComponentStack;
    
  2909.           }
    
  2910.           lastBoundaryErrorComponentStackDev = null;
    
  2911.           return;
    
  2912.         }
    
  2913.       }
    
  2914.     }
    
  2915.   }
    
  2916.   // Restore the context. We assume that this will be restored by the inner
    
  2917.   // functions in case nothing throws so we don't use "finally" here.
    
  2918.   task.formatContext = previousFormatContext;
    
  2919.   task.legacyContext = previousLegacyContext;
    
  2920.   task.context = previousContext;
    
  2921.   task.keyPath = previousKeyPath;
    
  2922.   task.treeContext = previousTreeContext;
    
  2923.   // Restore all active ReactContexts to what they were before.
    
  2924.   switchContext(previousContext);
    
  2925.   if (__DEV__) {
    
  2926.     task.componentStack = previousComponentStack;
    
  2927.   }
    
  2928.   // We assume that we don't need the correct context.
    
  2929.   // Let's terminate the rest of the tree and don't render any siblings.
    
  2930.   throw x;
    
  2931. }
    
  2932. 
    
  2933. function erroredReplay(
    
  2934.   request: Request,
    
  2935.   boundary: Root | SuspenseBoundary,
    
  2936.   error: mixed,
    
  2937.   replayNodes: ReplayNode[],
    
  2938.   resumeSlots: ResumeSlots,
    
  2939. ): void {
    
  2940.   // Erroring during a replay doesn't actually cause an error by itself because
    
  2941.   // that component has already rendered. What causes the error is the resumable
    
  2942.   // points that we did not yet finish which will be below the point of the reset.
    
  2943.   // For example, if we're replaying a path to a Suspense boundary that is not done
    
  2944.   // that doesn't error the parent Suspense boundary.
    
  2945.   // This might be a bit strange that the error in a parent gets thrown at a child.
    
  2946.   // We log it only once and reuse the digest.
    
  2947.   let errorDigest;
    
  2948.   if (
    
  2949.     enablePostpone &&
    
  2950.     typeof error === 'object' &&
    
  2951.     error !== null &&
    
  2952.     error.$$typeof === REACT_POSTPONE_TYPE
    
  2953.   ) {
    
  2954.     const postponeInstance: Postpone = (error: any);
    
  2955.     logPostpone(request, postponeInstance.message);
    
  2956.     // TODO: Figure out a better signal than a magic digest value.
    
  2957.     errorDigest = 'POSTPONE';
    
  2958.   } else {
    
  2959.     errorDigest = logRecoverableError(request, error);
    
  2960.   }
    
  2961.   abortRemainingReplayNodes(
    
  2962.     request,
    
  2963.     boundary,
    
  2964.     replayNodes,
    
  2965.     resumeSlots,
    
  2966.     error,
    
  2967.     errorDigest,
    
  2968.   );
    
  2969. }
    
  2970. 
    
  2971. function erroredTask(
    
  2972.   request: Request,
    
  2973.   boundary: Root | SuspenseBoundary,
    
  2974.   error: mixed,
    
  2975. ) {
    
  2976.   // Report the error to a global handler.
    
  2977.   let errorDigest;
    
  2978.   if (
    
  2979.     enablePostpone &&
    
  2980.     typeof error === 'object' &&
    
  2981.     error !== null &&
    
  2982.     error.$$typeof === REACT_POSTPONE_TYPE
    
  2983.   ) {
    
  2984.     const postponeInstance: Postpone = (error: any);
    
  2985.     logPostpone(request, postponeInstance.message);
    
  2986.     // TODO: Figure out a better signal than a magic digest value.
    
  2987.     errorDigest = 'POSTPONE';
    
  2988.   } else {
    
  2989.     errorDigest = logRecoverableError(request, error);
    
  2990.   }
    
  2991.   if (boundary === null) {
    
  2992.     lastBoundaryErrorComponentStackDev = null;
    
  2993.     fatalError(request, error);
    
  2994.   } else {
    
  2995.     boundary.pendingTasks--;
    
  2996.     if (boundary.status !== CLIENT_RENDERED) {
    
  2997.       boundary.status = CLIENT_RENDERED;
    
  2998.       boundary.errorDigest = errorDigest;
    
  2999.       if (__DEV__) {
    
  3000.         captureBoundaryErrorDetailsDev(boundary, error);
    
  3001.       }
    
  3002. 
    
  3003.       // Regardless of what happens next, this boundary won't be displayed,
    
  3004.       // so we can flush it, if the parent already flushed.
    
  3005.       if (boundary.parentFlushed) {
    
  3006.         // We don't have a preference where in the queue this goes since it's likely
    
  3007.         // to error on the client anyway. However, intentionally client-rendered
    
  3008.         // boundaries should be flushed earlier so that they can start on the client.
    
  3009.         // We reuse the same queue for errors.
    
  3010.         request.clientRenderedBoundaries.push(boundary);
    
  3011.       }
    
  3012.     } else {
    
  3013.       lastBoundaryErrorComponentStackDev = null;
    
  3014.     }
    
  3015.   }
    
  3016. 
    
  3017.   request.allPendingTasks--;
    
  3018.   if (request.allPendingTasks === 0) {
    
  3019.     const onAllReady = request.onAllReady;
    
  3020.     onAllReady();
    
  3021.   }
    
  3022. }
    
  3023. 
    
  3024. function abortTaskSoft(this: Request, task: Task): void {
    
  3025.   // This aborts task without aborting the parent boundary that it blocks.
    
  3026.   // It's used for when we didn't need this task to complete the tree.
    
  3027.   // If task was needed, then it should use abortTask instead.
    
  3028.   const request: Request = this;
    
  3029.   const boundary = task.blockedBoundary;
    
  3030.   const segment = task.blockedSegment;
    
  3031.   if (segment !== null) {
    
  3032.     segment.status = ABORTED;
    
  3033.     finishedTask(request, boundary, segment);
    
  3034.   }
    
  3035. }
    
  3036. 
    
  3037. function abortRemainingSuspenseBoundary(
    
  3038.   request: Request,
    
  3039.   rootSegmentID: number,
    
  3040.   error: mixed,
    
  3041.   errorDigest: ?string,
    
  3042. ): void {
    
  3043.   const resumedBoundary = createSuspenseBoundary(request, new Set());
    
  3044.   resumedBoundary.parentFlushed = true;
    
  3045.   // We restore the same id of this boundary as was used during prerender.
    
  3046.   resumedBoundary.rootSegmentID = rootSegmentID;
    
  3047. 
    
  3048.   resumedBoundary.status = CLIENT_RENDERED;
    
  3049.   resumedBoundary.errorDigest = errorDigest;
    
  3050.   if (__DEV__) {
    
  3051.     const errorPrefix = 'The server did not finish this Suspense boundary: ';
    
  3052.     let errorMessage;
    
  3053.     if (error && typeof error.message === 'string') {
    
  3054.       errorMessage = errorPrefix + error.message;
    
  3055.     } else {
    
  3056.       // eslint-disable-next-line react-internal/safe-string-coercion
    
  3057.       errorMessage = errorPrefix + String(error);
    
  3058.     }
    
  3059.     const previousTaskInDev = currentTaskInDEV;
    
  3060.     currentTaskInDEV = null;
    
  3061.     try {
    
  3062.       captureBoundaryErrorDetailsDev(resumedBoundary, errorMessage);
    
  3063.     } finally {
    
  3064.       currentTaskInDEV = previousTaskInDev;
    
  3065.     }
    
  3066.   }
    
  3067.   if (resumedBoundary.parentFlushed) {
    
  3068.     request.clientRenderedBoundaries.push(resumedBoundary);
    
  3069.   }
    
  3070. }
    
  3071. 
    
  3072. function abortRemainingReplayNodes(
    
  3073.   request: Request,
    
  3074.   boundary: Root | SuspenseBoundary,
    
  3075.   nodes: Array<ReplayNode>,
    
  3076.   slots: ResumeSlots,
    
  3077.   error: mixed,
    
  3078.   errorDigest: ?string,
    
  3079. ): void {
    
  3080.   for (let i = 0; i < nodes.length; i++) {
    
  3081.     const node = nodes[i];
    
  3082.     if (node.length === 4) {
    
  3083.       abortRemainingReplayNodes(
    
  3084.         request,
    
  3085.         boundary,
    
  3086.         node[2],
    
  3087.         node[3],
    
  3088.         error,
    
  3089.         errorDigest,
    
  3090.       );
    
  3091.     } else {
    
  3092.       const boundaryNode: ReplaySuspenseBoundary = node;
    
  3093.       const rootSegmentID = boundaryNode[5];
    
  3094.       abortRemainingSuspenseBoundary(
    
  3095.         request,
    
  3096.         rootSegmentID,
    
  3097.         error,
    
  3098.         errorDigest,
    
  3099.       );
    
  3100.     }
    
  3101.   }
    
  3102.   // Empty the set, since we've cleared it now.
    
  3103.   nodes.length = 0;
    
  3104. 
    
  3105.   if (slots !== null) {
    
  3106.     // We had something still to resume in the parent boundary. We must trigger
    
  3107.     // the error on the parent boundary since it's not able to complete.
    
  3108.     if (boundary === null) {
    
  3109.       throw new Error(
    
  3110.         'We should not have any resumable nodes in the shell. ' +
    
  3111.           'This is a bug in React.',
    
  3112.       );
    
  3113.     } else if (boundary.status !== CLIENT_RENDERED) {
    
  3114.       boundary.status = CLIENT_RENDERED;
    
  3115.       boundary.errorDigest = errorDigest;
    
  3116.       if (__DEV__) {
    
  3117.         captureBoundaryErrorDetailsDev(boundary, error);
    
  3118.       }
    
  3119.       if (boundary.parentFlushed) {
    
  3120.         request.clientRenderedBoundaries.push(boundary);
    
  3121.       }
    
  3122.     }
    
  3123.     // Empty the set
    
  3124.     if (typeof slots === 'object') {
    
  3125.       for (const index in slots) {
    
  3126.         delete slots[(index: any)];
    
  3127.       }
    
  3128.     }
    
  3129.   }
    
  3130. }
    
  3131. 
    
  3132. function abortTask(task: Task, request: Request, error: mixed): void {
    
  3133.   // This aborts the task and aborts the parent that it blocks, putting it into
    
  3134.   // client rendered mode.
    
  3135.   const boundary = task.blockedBoundary;
    
  3136.   const segment = task.blockedSegment;
    
  3137.   if (segment !== null) {
    
  3138.     segment.status = ABORTED;
    
  3139.   }
    
  3140. 
    
  3141.   if (boundary === null) {
    
  3142.     if (request.status !== CLOSING && request.status !== CLOSED) {
    
  3143.       const replay: null | ReplaySet = task.replay;
    
  3144.       if (replay === null) {
    
  3145.         // We didn't complete the root so we have nothing to show. We can close
    
  3146.         // the request;
    
  3147.         logRecoverableError(request, error);
    
  3148.         fatalError(request, error);
    
  3149.         return;
    
  3150.       } else {
    
  3151.         // If the shell aborts during a replay, that's not a fatal error. Instead
    
  3152.         // we should be able to recover by client rendering all the root boundaries in
    
  3153.         // the ReplaySet.
    
  3154.         replay.pendingTasks--;
    
  3155.         if (replay.pendingTasks === 0 && replay.nodes.length > 0) {
    
  3156.           const errorDigest = logRecoverableError(request, error);
    
  3157.           abortRemainingReplayNodes(
    
  3158.             request,
    
  3159.             null,
    
  3160.             replay.nodes,
    
  3161.             replay.slots,
    
  3162.             error,
    
  3163.             errorDigest,
    
  3164.           );
    
  3165.         }
    
  3166.         request.pendingRootTasks--;
    
  3167.         if (request.pendingRootTasks === 0) {
    
  3168.           request.onShellError = noop;
    
  3169.           const onShellReady = request.onShellReady;
    
  3170.           onShellReady();
    
  3171.         }
    
  3172.       }
    
  3173.     }
    
  3174.   } else {
    
  3175.     boundary.pendingTasks--;
    
  3176.     if (boundary.status !== CLIENT_RENDERED) {
    
  3177.       boundary.status = CLIENT_RENDERED;
    
  3178.       boundary.errorDigest = logRecoverableError(request, error);
    
  3179.       if (__DEV__) {
    
  3180.         const errorPrefix =
    
  3181.           'The server did not finish this Suspense boundary: ';
    
  3182.         let errorMessage;
    
  3183.         if (error && typeof error.message === 'string') {
    
  3184.           errorMessage = errorPrefix + error.message;
    
  3185.         } else {
    
  3186.           // eslint-disable-next-line react-internal/safe-string-coercion
    
  3187.           errorMessage = errorPrefix + String(error);
    
  3188.         }
    
  3189.         const previousTaskInDev = currentTaskInDEV;
    
  3190.         currentTaskInDEV = task;
    
  3191.         try {
    
  3192.           captureBoundaryErrorDetailsDev(boundary, errorMessage);
    
  3193.         } finally {
    
  3194.           currentTaskInDEV = previousTaskInDev;
    
  3195.         }
    
  3196.       }
    
  3197.       if (boundary.parentFlushed) {
    
  3198.         request.clientRenderedBoundaries.push(boundary);
    
  3199.       }
    
  3200.     }
    
  3201. 
    
  3202.     // If this boundary was still pending then we haven't already cancelled its fallbacks.
    
  3203.     // We'll need to abort the fallbacks, which will also error that parent boundary.
    
  3204.     boundary.fallbackAbortableTasks.forEach(fallbackTask =>
    
  3205.       abortTask(fallbackTask, request, error),
    
  3206.     );
    
  3207.     boundary.fallbackAbortableTasks.clear();
    
  3208.   }
    
  3209. 
    
  3210.   request.allPendingTasks--;
    
  3211.   if (request.allPendingTasks === 0) {
    
  3212.     const onAllReady = request.onAllReady;
    
  3213.     onAllReady();
    
  3214.   }
    
  3215. }
    
  3216. 
    
  3217. function queueCompletedSegment(
    
  3218.   boundary: SuspenseBoundary,
    
  3219.   segment: Segment,
    
  3220. ): void {
    
  3221.   if (
    
  3222.     segment.chunks.length === 0 &&
    
  3223.     segment.children.length === 1 &&
    
  3224.     segment.children[0].boundary === null
    
  3225.   ) {
    
  3226.     // This is an empty segment. There's nothing to write, so we can instead transfer the ID
    
  3227.     // to the child. That way any existing references point to the child.
    
  3228.     const childSegment = segment.children[0];
    
  3229.     childSegment.id = segment.id;
    
  3230.     childSegment.parentFlushed = true;
    
  3231.     if (childSegment.status === COMPLETED) {
    
  3232.       queueCompletedSegment(boundary, childSegment);
    
  3233.     }
    
  3234.   } else {
    
  3235.     const completedSegments = boundary.completedSegments;
    
  3236.     completedSegments.push(segment);
    
  3237.   }
    
  3238. }
    
  3239. 
    
  3240. function finishedTask(
    
  3241.   request: Request,
    
  3242.   boundary: Root | SuspenseBoundary,
    
  3243.   segment: null | Segment,
    
  3244. ) {
    
  3245.   if (boundary === null) {
    
  3246.     if (segment !== null && segment.parentFlushed) {
    
  3247.       if (request.completedRootSegment !== null) {
    
  3248.         throw new Error(
    
  3249.           'There can only be one root segment. This is a bug in React.',
    
  3250.         );
    
  3251.       }
    
  3252. 
    
  3253.       request.completedRootSegment = segment;
    
  3254.     }
    
  3255.     request.pendingRootTasks--;
    
  3256.     if (request.pendingRootTasks === 0) {
    
  3257.       // We have completed the shell so the shell can't error anymore.
    
  3258.       request.onShellError = noop;
    
  3259.       const onShellReady = request.onShellReady;
    
  3260.       onShellReady();
    
  3261.     }
    
  3262.   } else {
    
  3263.     boundary.pendingTasks--;
    
  3264.     if (boundary.status === CLIENT_RENDERED) {
    
  3265.       // This already errored.
    
  3266.     } else if (boundary.pendingTasks === 0) {
    
  3267.       if (boundary.status === PENDING) {
    
  3268.         boundary.status = COMPLETED;
    
  3269.       }
    
  3270.       // This must have been the last segment we were waiting on. This boundary is now complete.
    
  3271.       if (segment !== null && segment.parentFlushed) {
    
  3272.         // Our parent segment already flushed, so we need to schedule this segment to be emitted.
    
  3273.         // If it is a segment that was aborted, we'll write other content instead so we don't need
    
  3274.         // to emit it.
    
  3275.         if (segment.status === COMPLETED) {
    
  3276.           queueCompletedSegment(boundary, segment);
    
  3277.         }
    
  3278.       }
    
  3279.       if (boundary.parentFlushed) {
    
  3280.         // The segment might be part of a segment that didn't flush yet, but if the boundary's
    
  3281.         // parent flushed, we need to schedule the boundary to be emitted.
    
  3282.         request.completedBoundaries.push(boundary);
    
  3283.       }
    
  3284. 
    
  3285.       // We can now cancel any pending task on the fallback since we won't need to show it anymore.
    
  3286.       // This needs to happen after we read the parentFlushed flags because aborting can finish
    
  3287.       // work which can trigger user code, which can start flushing, which can change those flags.
    
  3288.       // If the boundary was POSTPONED, we still need to finish the fallback first.
    
  3289.       if (boundary.status === COMPLETED) {
    
  3290.         boundary.fallbackAbortableTasks.forEach(abortTaskSoft, request);
    
  3291.         boundary.fallbackAbortableTasks.clear();
    
  3292.       }
    
  3293.     } else {
    
  3294.       if (segment !== null && segment.parentFlushed) {
    
  3295.         // Our parent already flushed, so we need to schedule this segment to be emitted.
    
  3296.         // If it is a segment that was aborted, we'll write other content instead so we don't need
    
  3297.         // to emit it.
    
  3298.         if (segment.status === COMPLETED) {
    
  3299.           queueCompletedSegment(boundary, segment);
    
  3300.           const completedSegments = boundary.completedSegments;
    
  3301.           if (completedSegments.length === 1) {
    
  3302.             // This is the first time since we last flushed that we completed anything.
    
  3303.             // We can schedule this boundary to emit its partially completed segments early
    
  3304.             // in case the parent has already been flushed.
    
  3305.             if (boundary.parentFlushed) {
    
  3306.               request.partialBoundaries.push(boundary);
    
  3307.             }
    
  3308.           }
    
  3309.         }
    
  3310.       }
    
  3311.     }
    
  3312.   }
    
  3313. 
    
  3314.   request.allPendingTasks--;
    
  3315.   if (request.allPendingTasks === 0) {
    
  3316.     // This needs to be called at the very end so that we can synchronously write the result
    
  3317.     // in the callback if needed.
    
  3318.     const onAllReady = request.onAllReady;
    
  3319.     onAllReady();
    
  3320.   }
    
  3321. }
    
  3322. 
    
  3323. function retryTask(request: Request, task: Task): void {
    
  3324.   if (enableFloat) {
    
  3325.     const blockedBoundary = task.blockedBoundary;
    
  3326.     setCurrentlyRenderingBoundaryResourcesTarget(
    
  3327.       request.renderState,
    
  3328.       blockedBoundary ? blockedBoundary.resources : null,
    
  3329.     );
    
  3330.   }
    
  3331.   const segment = task.blockedSegment;
    
  3332.   if (segment === null) {
    
  3333.     retryReplayTask(
    
  3334.       request,
    
  3335.       // $FlowFixMe: Refined.
    
  3336.       task,
    
  3337.     );
    
  3338.   } else {
    
  3339.     retryRenderTask(
    
  3340.       request,
    
  3341.       // $FlowFixMe: Refined.
    
  3342.       task,
    
  3343.       segment,
    
  3344.     );
    
  3345.   }
    
  3346. }
    
  3347. 
    
  3348. function retryRenderTask(
    
  3349.   request: Request,
    
  3350.   task: RenderTask,
    
  3351.   segment: Segment,
    
  3352. ): void {
    
  3353.   if (segment.status !== PENDING) {
    
  3354.     // We completed this by other means before we had a chance to retry it.
    
  3355.     return;
    
  3356.   }
    
  3357.   // We restore the context to what it was when we suspended.
    
  3358.   // We don't restore it after we leave because it's likely that we'll end up
    
  3359.   // needing a very similar context soon again.
    
  3360.   switchContext(task.context);
    
  3361.   let prevTaskInDEV = null;
    
  3362.   if (__DEV__) {
    
  3363.     prevTaskInDEV = currentTaskInDEV;
    
  3364.     currentTaskInDEV = task;
    
  3365.   }
    
  3366. 
    
  3367.   const childrenLength = segment.children.length;
    
  3368.   const chunkLength = segment.chunks.length;
    
  3369.   try {
    
  3370.     // We call the destructive form that mutates this task. That way if something
    
  3371.     // suspends again, we can reuse the same task instead of spawning a new one.
    
  3372. 
    
  3373.     // Reset the task's thenable state before continuing, so that if a later
    
  3374.     // component suspends we can reuse the same task object. If the same
    
  3375.     // component suspends again, the thenable state will be restored.
    
  3376.     const prevThenableState = task.thenableState;
    
  3377.     task.thenableState = null;
    
  3378. 
    
  3379.     renderNodeDestructive(
    
  3380.       request,
    
  3381.       task,
    
  3382.       prevThenableState,
    
  3383.       task.node,
    
  3384.       task.childIndex,
    
  3385.     );
    
  3386.     pushSegmentFinale(
    
  3387.       segment.chunks,
    
  3388.       request.renderState,
    
  3389.       segment.lastPushedText,
    
  3390.       segment.textEmbedded,
    
  3391.     );
    
  3392. 
    
  3393.     task.abortSet.delete(task);
    
  3394.     segment.status = COMPLETED;
    
  3395.     finishedTask(request, task.blockedBoundary, segment);
    
  3396.   } catch (thrownValue) {
    
  3397.     resetHooksState();
    
  3398. 
    
  3399.     // Reset the write pointers to where we started.
    
  3400.     segment.children.length = childrenLength;
    
  3401.     segment.chunks.length = chunkLength;
    
  3402. 
    
  3403.     const x =
    
  3404.       thrownValue === SuspenseException
    
  3405.         ? // This is a special type of exception used for Suspense. For historical
    
  3406.           // reasons, the rest of the Suspense implementation expects the thrown
    
  3407.           // value to be a thenable, because before `use` existed that was the
    
  3408.           // (unstable) API for suspending. This implementation detail can change
    
  3409.           // later, once we deprecate the old API in favor of `use`.
    
  3410.           getSuspendedThenable()
    
  3411.         : thrownValue;
    
  3412. 
    
  3413.     if (typeof x === 'object' && x !== null) {
    
  3414.       // $FlowFixMe[method-unbinding]
    
  3415.       if (typeof x.then === 'function') {
    
  3416.         // Something suspended again, let's pick it back up later.
    
  3417.         const ping = task.ping;
    
  3418.         x.then(ping, ping);
    
  3419.         task.thenableState = getThenableStateAfterSuspending();
    
  3420.         return;
    
  3421.       } else if (
    
  3422.         enablePostpone &&
    
  3423.         request.trackedPostpones !== null &&
    
  3424.         x.$$typeof === REACT_POSTPONE_TYPE
    
  3425.       ) {
    
  3426.         // If we're tracking postpones, we mark this segment as postponed and finish
    
  3427.         // the task without filling it in. If we're not tracking, we treat it more like
    
  3428.         // an error.
    
  3429.         const trackedPostpones = request.trackedPostpones;
    
  3430.         task.abortSet.delete(task);
    
  3431.         const postponeInstance: Postpone = (x: any);
    
  3432.         logPostpone(request, postponeInstance.message);
    
  3433.         trackPostpone(request, trackedPostpones, task, segment);
    
  3434.         finishedTask(request, task.blockedBoundary, segment);
    
  3435.         lastBoundaryErrorComponentStackDev = null;
    
  3436.         return;
    
  3437.       }
    
  3438.     }
    
  3439.     task.abortSet.delete(task);
    
  3440.     segment.status = ERRORED;
    
  3441.     erroredTask(request, task.blockedBoundary, x);
    
  3442.     return;
    
  3443.   } finally {
    
  3444.     if (enableFloat) {
    
  3445.       setCurrentlyRenderingBoundaryResourcesTarget(request.renderState, null);
    
  3446.     }
    
  3447.     if (__DEV__) {
    
  3448.       currentTaskInDEV = prevTaskInDEV;
    
  3449.     }
    
  3450.   }
    
  3451. }
    
  3452. 
    
  3453. function retryReplayTask(request: Request, task: ReplayTask): void {
    
  3454.   if (task.replay.pendingTasks === 0) {
    
  3455.     // There are no pending tasks working on this set, so we must have aborted.
    
  3456.     return;
    
  3457.   }
    
  3458. 
    
  3459.   // We restore the context to what it was when we suspended.
    
  3460.   // We don't restore it after we leave because it's likely that we'll end up
    
  3461.   // needing a very similar context soon again.
    
  3462.   switchContext(task.context);
    
  3463.   let prevTaskInDEV = null;
    
  3464.   if (__DEV__) {
    
  3465.     prevTaskInDEV = currentTaskInDEV;
    
  3466.     currentTaskInDEV = task;
    
  3467.   }
    
  3468. 
    
  3469.   try {
    
  3470.     // We call the destructive form that mutates this task. That way if something
    
  3471.     // suspends again, we can reuse the same task instead of spawning a new one.
    
  3472. 
    
  3473.     // Reset the task's thenable state before continuing, so that if a later
    
  3474.     // component suspends we can reuse the same task object. If the same
    
  3475.     // component suspends again, the thenable state will be restored.
    
  3476.     const prevThenableState = task.thenableState;
    
  3477.     task.thenableState = null;
    
  3478. 
    
  3479.     renderNodeDestructive(
    
  3480.       request,
    
  3481.       task,
    
  3482.       prevThenableState,
    
  3483.       task.node,
    
  3484.       task.childIndex,
    
  3485.     );
    
  3486. 
    
  3487.     if (task.replay.pendingTasks === 1 && task.replay.nodes.length > 0) {
    
  3488.       throw new Error(
    
  3489.         "Couldn't find all resumable slots by key/index during replaying. " +
    
  3490.           "The tree doesn't match so React will fallback to client rendering.",
    
  3491.       );
    
  3492.     }
    
  3493.     task.replay.pendingTasks--;
    
  3494. 
    
  3495.     task.abortSet.delete(task);
    
  3496.     finishedTask(request, task.blockedBoundary, null);
    
  3497.   } catch (thrownValue) {
    
  3498.     resetHooksState();
    
  3499. 
    
  3500.     const x =
    
  3501.       thrownValue === SuspenseException
    
  3502.         ? // This is a special type of exception used for Suspense. For historical
    
  3503.           // reasons, the rest of the Suspense implementation expects the thrown
    
  3504.           // value to be a thenable, because before `use` existed that was the
    
  3505.           // (unstable) API for suspending. This implementation detail can change
    
  3506.           // later, once we deprecate the old API in favor of `use`.
    
  3507.           getSuspendedThenable()
    
  3508.         : thrownValue;
    
  3509. 
    
  3510.     if (typeof x === 'object' && x !== null) {
    
  3511.       // $FlowFixMe[method-unbinding]
    
  3512.       if (typeof x.then === 'function') {
    
  3513.         // Something suspended again, let's pick it back up later.
    
  3514.         const ping = task.ping;
    
  3515.         x.then(ping, ping);
    
  3516.         task.thenableState = getThenableStateAfterSuspending();
    
  3517.         return;
    
  3518.       }
    
  3519.     }
    
  3520.     task.replay.pendingTasks--;
    
  3521.     task.abortSet.delete(task);
    
  3522.     erroredReplay(
    
  3523.       request,
    
  3524.       task.blockedBoundary,
    
  3525.       x,
    
  3526.       task.replay.nodes,
    
  3527.       task.replay.slots,
    
  3528.     );
    
  3529.     request.pendingRootTasks--;
    
  3530.     if (request.pendingRootTasks === 0) {
    
  3531.       request.onShellError = noop;
    
  3532.       const onShellReady = request.onShellReady;
    
  3533.       onShellReady();
    
  3534.     }
    
  3535.     request.allPendingTasks--;
    
  3536.     if (request.allPendingTasks === 0) {
    
  3537.       const onAllReady = request.onAllReady;
    
  3538.       onAllReady();
    
  3539.     }
    
  3540.     return;
    
  3541.   } finally {
    
  3542.     if (enableFloat) {
    
  3543.       setCurrentlyRenderingBoundaryResourcesTarget(request.renderState, null);
    
  3544.     }
    
  3545.     if (__DEV__) {
    
  3546.       currentTaskInDEV = prevTaskInDEV;
    
  3547.     }
    
  3548.   }
    
  3549. }
    
  3550. 
    
  3551. export function performWork(request: Request): void {
    
  3552.   if (request.status === CLOSED) {
    
  3553.     return;
    
  3554.   }
    
  3555.   const prevContext = getActiveContext();
    
  3556.   const prevDispatcher = ReactCurrentDispatcher.current;
    
  3557.   ReactCurrentDispatcher.current = HooksDispatcher;
    
  3558.   let prevCacheDispatcher;
    
  3559.   if (enableCache) {
    
  3560.     prevCacheDispatcher = ReactCurrentCache.current;
    
  3561.     ReactCurrentCache.current = DefaultCacheDispatcher;
    
  3562.   }
    
  3563. 
    
  3564.   const prevRequest = currentRequest;
    
  3565.   currentRequest = request;
    
  3566. 
    
  3567.   let prevGetCurrentStackImpl;
    
  3568.   if (__DEV__) {
    
  3569.     prevGetCurrentStackImpl = ReactDebugCurrentFrame.getCurrentStack;
    
  3570.     ReactDebugCurrentFrame.getCurrentStack = getCurrentStackInDEV;
    
  3571.   }
    
  3572.   const prevResumableState = currentResumableState;
    
  3573.   setCurrentResumableState(request.resumableState);
    
  3574.   try {
    
  3575.     const pingedTasks = request.pingedTasks;
    
  3576.     let i;
    
  3577.     for (i = 0; i < pingedTasks.length; i++) {
    
  3578.       const task = pingedTasks[i];
    
  3579.       retryTask(request, task);
    
  3580.     }
    
  3581.     pingedTasks.splice(0, i);
    
  3582.     if (request.destination !== null) {
    
  3583.       flushCompletedQueues(request, request.destination);
    
  3584.     }
    
  3585.   } catch (error) {
    
  3586.     logRecoverableError(request, error);
    
  3587.     fatalError(request, error);
    
  3588.   } finally {
    
  3589.     setCurrentResumableState(prevResumableState);
    
  3590.     ReactCurrentDispatcher.current = prevDispatcher;
    
  3591.     if (enableCache) {
    
  3592.       ReactCurrentCache.current = prevCacheDispatcher;
    
  3593.     }
    
  3594. 
    
  3595.     if (__DEV__) {
    
  3596.       ReactDebugCurrentFrame.getCurrentStack = prevGetCurrentStackImpl;
    
  3597.     }
    
  3598.     if (prevDispatcher === HooksDispatcher) {
    
  3599.       // This means that we were in a reentrant work loop. This could happen
    
  3600.       // in a renderer that supports synchronous work like renderToString,
    
  3601.       // when it's called from within another renderer.
    
  3602.       // Normally we don't bother switching the contexts to their root/default
    
  3603.       // values when leaving because we'll likely need the same or similar
    
  3604.       // context again. However, when we're inside a synchronous loop like this
    
  3605.       // we'll to restore the context to what it was before returning.
    
  3606.       switchContext(prevContext);
    
  3607.     }
    
  3608.     currentRequest = prevRequest;
    
  3609.   }
    
  3610. }
    
  3611. 
    
  3612. function flushSubtree(
    
  3613.   request: Request,
    
  3614.   destination: Destination,
    
  3615.   segment: Segment,
    
  3616. ): boolean {
    
  3617.   segment.parentFlushed = true;
    
  3618.   switch (segment.status) {
    
  3619.     case PENDING: {
    
  3620.       // We're emitting a placeholder for this segment to be filled in later.
    
  3621.       // Therefore we'll need to assign it an ID - to refer to it by.
    
  3622.       segment.id = request.nextSegmentId++;
    
  3623.       // Fallthrough
    
  3624.     }
    
  3625.     case POSTPONED: {
    
  3626.       const segmentID = segment.id;
    
  3627.       // When this segment finally completes it won't be embedded in text since it will flush separately
    
  3628.       segment.lastPushedText = false;
    
  3629.       segment.textEmbedded = false;
    
  3630.       return writePlaceholder(destination, request.renderState, segmentID);
    
  3631.     }
    
  3632.     case COMPLETED: {
    
  3633.       segment.status = FLUSHED;
    
  3634.       let r = true;
    
  3635.       const chunks = segment.chunks;
    
  3636.       let chunkIdx = 0;
    
  3637.       const children = segment.children;
    
  3638. 
    
  3639.       for (let childIdx = 0; childIdx < children.length; childIdx++) {
    
  3640.         const nextChild = children[childIdx];
    
  3641.         // Write all the chunks up until the next child.
    
  3642.         for (; chunkIdx < nextChild.index; chunkIdx++) {
    
  3643.           writeChunk(destination, chunks[chunkIdx]);
    
  3644.         }
    
  3645.         r = flushSegment(request, destination, nextChild);
    
  3646.       }
    
  3647.       // Finally just write all the remaining chunks
    
  3648.       for (; chunkIdx < chunks.length - 1; chunkIdx++) {
    
  3649.         writeChunk(destination, chunks[chunkIdx]);
    
  3650.       }
    
  3651.       if (chunkIdx < chunks.length) {
    
  3652.         r = writeChunkAndReturn(destination, chunks[chunkIdx]);
    
  3653.       }
    
  3654.       return r;
    
  3655.     }
    
  3656.     default: {
    
  3657.       throw new Error(
    
  3658.         'Aborted, errored or already flushed boundaries should not be flushed again. This is a bug in React.',
    
  3659.       );
    
  3660.     }
    
  3661.   }
    
  3662. }
    
  3663. 
    
  3664. function flushSegment(
    
  3665.   request: Request,
    
  3666.   destination: Destination,
    
  3667.   segment: Segment,
    
  3668. ): boolean {
    
  3669.   const boundary = segment.boundary;
    
  3670.   if (boundary === null) {
    
  3671.     // Not a suspense boundary.
    
  3672.     return flushSubtree(request, destination, segment);
    
  3673.   }
    
  3674. 
    
  3675.   boundary.parentFlushed = true;
    
  3676.   // This segment is a Suspense boundary. We need to decide whether to
    
  3677.   // emit the content or the fallback now.
    
  3678.   if (boundary.status === CLIENT_RENDERED) {
    
  3679.     // Emit a client rendered suspense boundary wrapper.
    
  3680.     // We never queue the inner boundary so we'll never emit its content or partial segments.
    
  3681. 
    
  3682.     writeStartClientRenderedSuspenseBoundary(
    
  3683.       destination,
    
  3684.       request.renderState,
    
  3685.       boundary.errorDigest,
    
  3686.       boundary.errorMessage,
    
  3687.       boundary.errorComponentStack,
    
  3688.     );
    
  3689.     // Flush the fallback.
    
  3690.     flushSubtree(request, destination, segment);
    
  3691. 
    
  3692.     return writeEndClientRenderedSuspenseBoundary(
    
  3693.       destination,
    
  3694.       request.renderState,
    
  3695.     );
    
  3696.   } else if (boundary.status !== COMPLETED) {
    
  3697.     if (boundary.status === PENDING) {
    
  3698.       // For pending boundaries we lazily assign an ID to the boundary
    
  3699.       // and root segment.
    
  3700.       boundary.rootSegmentID = request.nextSegmentId++;
    
  3701.     }
    
  3702. 
    
  3703.     if (boundary.completedSegments.length > 0) {
    
  3704.       // If this is at least partially complete, we can queue it to be partially emitted early.
    
  3705.       request.partialBoundaries.push(boundary);
    
  3706.     }
    
  3707. 
    
  3708.     // This boundary is still loading. Emit a pending suspense boundary wrapper.
    
  3709. 
    
  3710.     const id = boundary.rootSegmentID;
    
  3711.     writeStartPendingSuspenseBoundary(destination, request.renderState, id);
    
  3712. 
    
  3713.     // Flush the fallback.
    
  3714.     flushSubtree(request, destination, segment);
    
  3715. 
    
  3716.     return writeEndPendingSuspenseBoundary(destination, request.renderState);
    
  3717.   } else if (boundary.byteSize > request.progressiveChunkSize) {
    
  3718.     // This boundary is large and will be emitted separately so that we can progressively show
    
  3719.     // other content. We add it to the queue during the flush because we have to ensure that
    
  3720.     // the parent flushes first so that there's something to inject it into.
    
  3721.     // We also have to make sure that it's emitted into the queue in a deterministic slot.
    
  3722.     // I.e. we can't insert it here when it completes.
    
  3723. 
    
  3724.     // Assign an ID to refer to the future content by.
    
  3725.     boundary.rootSegmentID = request.nextSegmentId++;
    
  3726. 
    
  3727.     request.completedBoundaries.push(boundary);
    
  3728.     // Emit a pending rendered suspense boundary wrapper.
    
  3729.     writeStartPendingSuspenseBoundary(
    
  3730.       destination,
    
  3731.       request.renderState,
    
  3732.       boundary.rootSegmentID,
    
  3733.     );
    
  3734. 
    
  3735.     // Flush the fallback.
    
  3736.     flushSubtree(request, destination, segment);
    
  3737. 
    
  3738.     return writeEndPendingSuspenseBoundary(destination, request.renderState);
    
  3739.   } else {
    
  3740.     if (enableFloat) {
    
  3741.       hoistResources(request.renderState, boundary.resources);
    
  3742.     }
    
  3743.     // We can inline this boundary's content as a complete boundary.
    
  3744.     writeStartCompletedSuspenseBoundary(destination, request.renderState);
    
  3745. 
    
  3746.     const completedSegments = boundary.completedSegments;
    
  3747. 
    
  3748.     if (completedSegments.length !== 1) {
    
  3749.       throw new Error(
    
  3750.         'A previously unvisited boundary must have exactly one root segment. This is a bug in React.',
    
  3751.       );
    
  3752.     }
    
  3753. 
    
  3754.     const contentSegment = completedSegments[0];
    
  3755.     flushSegment(request, destination, contentSegment);
    
  3756. 
    
  3757.     return writeEndCompletedSuspenseBoundary(destination, request.renderState);
    
  3758.   }
    
  3759. }
    
  3760. 
    
  3761. function flushClientRenderedBoundary(
    
  3762.   request: Request,
    
  3763.   destination: Destination,
    
  3764.   boundary: SuspenseBoundary,
    
  3765. ): boolean {
    
  3766.   return writeClientRenderBoundaryInstruction(
    
  3767.     destination,
    
  3768.     request.resumableState,
    
  3769.     request.renderState,
    
  3770.     boundary.rootSegmentID,
    
  3771.     boundary.errorDigest,
    
  3772.     boundary.errorMessage,
    
  3773.     boundary.errorComponentStack,
    
  3774.   );
    
  3775. }
    
  3776. 
    
  3777. function flushSegmentContainer(
    
  3778.   request: Request,
    
  3779.   destination: Destination,
    
  3780.   segment: Segment,
    
  3781. ): boolean {
    
  3782.   writeStartSegment(
    
  3783.     destination,
    
  3784.     request.renderState,
    
  3785.     segment.parentFormatContext,
    
  3786.     segment.id,
    
  3787.   );
    
  3788.   flushSegment(request, destination, segment);
    
  3789.   return writeEndSegment(destination, segment.parentFormatContext);
    
  3790. }
    
  3791. 
    
  3792. function flushCompletedBoundary(
    
  3793.   request: Request,
    
  3794.   destination: Destination,
    
  3795.   boundary: SuspenseBoundary,
    
  3796. ): boolean {
    
  3797.   if (enableFloat) {
    
  3798.     setCurrentlyRenderingBoundaryResourcesTarget(
    
  3799.       request.renderState,
    
  3800.       boundary.resources,
    
  3801.     );
    
  3802.   }
    
  3803.   const completedSegments = boundary.completedSegments;
    
  3804.   let i = 0;
    
  3805.   for (; i < completedSegments.length; i++) {
    
  3806.     const segment = completedSegments[i];
    
  3807.     flushPartiallyCompletedSegment(request, destination, boundary, segment);
    
  3808.   }
    
  3809.   completedSegments.length = 0;
    
  3810. 
    
  3811.   if (enableFloat) {
    
  3812.     writeResourcesForBoundary(
    
  3813.       destination,
    
  3814.       boundary.resources,
    
  3815.       request.renderState,
    
  3816.     );
    
  3817.   }
    
  3818. 
    
  3819.   return writeCompletedBoundaryInstruction(
    
  3820.     destination,
    
  3821.     request.resumableState,
    
  3822.     request.renderState,
    
  3823.     boundary.rootSegmentID,
    
  3824.     boundary.resources,
    
  3825.   );
    
  3826. }
    
  3827. 
    
  3828. function flushPartialBoundary(
    
  3829.   request: Request,
    
  3830.   destination: Destination,
    
  3831.   boundary: SuspenseBoundary,
    
  3832. ): boolean {
    
  3833.   if (enableFloat) {
    
  3834.     setCurrentlyRenderingBoundaryResourcesTarget(
    
  3835.       request.renderState,
    
  3836.       boundary.resources,
    
  3837.     );
    
  3838.   }
    
  3839.   const completedSegments = boundary.completedSegments;
    
  3840.   let i = 0;
    
  3841.   for (; i < completedSegments.length; i++) {
    
  3842.     const segment = completedSegments[i];
    
  3843.     if (
    
  3844.       !flushPartiallyCompletedSegment(request, destination, boundary, segment)
    
  3845.     ) {
    
  3846.       i++;
    
  3847.       completedSegments.splice(0, i);
    
  3848.       // Only write as much as the buffer wants. Something higher priority
    
  3849.       // might want to write later.
    
  3850.       return false;
    
  3851.     }
    
  3852.   }
    
  3853.   completedSegments.splice(0, i);
    
  3854. 
    
  3855.   if (enableFloat) {
    
  3856.     // The way this is structured we only write resources for partial boundaries
    
  3857.     // if there is no backpressure. Later before we complete the boundary we
    
  3858.     // will write resources regardless of backpressure before we emit the
    
  3859.     // completion instruction
    
  3860.     return writeResourcesForBoundary(
    
  3861.       destination,
    
  3862.       boundary.resources,
    
  3863.       request.renderState,
    
  3864.     );
    
  3865.   } else {
    
  3866.     return true;
    
  3867.   }
    
  3868. }
    
  3869. 
    
  3870. function flushPartiallyCompletedSegment(
    
  3871.   request: Request,
    
  3872.   destination: Destination,
    
  3873.   boundary: SuspenseBoundary,
    
  3874.   segment: Segment,
    
  3875. ): boolean {
    
  3876.   if (segment.status === FLUSHED) {
    
  3877.     // We've already flushed this inline.
    
  3878.     return true;
    
  3879.   }
    
  3880. 
    
  3881.   const segmentID = segment.id;
    
  3882.   if (segmentID === -1) {
    
  3883.     // This segment wasn't previously referred to. This happens at the root of
    
  3884.     // a boundary. We make kind of a leap here and assume this is the root.
    
  3885.     const rootSegmentID = (segment.id = boundary.rootSegmentID);
    
  3886. 
    
  3887.     if (rootSegmentID === -1) {
    
  3888.       throw new Error(
    
  3889.         'A root segment ID must have been assigned by now. This is a bug in React.',
    
  3890.       );
    
  3891.     }
    
  3892. 
    
  3893.     return flushSegmentContainer(request, destination, segment);
    
  3894.   } else if (segmentID === boundary.rootSegmentID) {
    
  3895.     // When we emit postponed boundaries, we might have assigned the ID already
    
  3896.     // but it's still the root segment so we can't inject it into the parent yet.
    
  3897.     return flushSegmentContainer(request, destination, segment);
    
  3898.   } else {
    
  3899.     flushSegmentContainer(request, destination, segment);
    
  3900.     return writeCompletedSegmentInstruction(
    
  3901.       destination,
    
  3902.       request.resumableState,
    
  3903.       request.renderState,
    
  3904.       segmentID,
    
  3905.     );
    
  3906.   }
    
  3907. }
    
  3908. 
    
  3909. function flushCompletedQueues(
    
  3910.   request: Request,
    
  3911.   destination: Destination,
    
  3912. ): void {
    
  3913.   beginWriting(destination);
    
  3914.   try {
    
  3915.     // The structure of this is to go through each queue one by one and write
    
  3916.     // until the sink tells us to stop. When we should stop, we still finish writing
    
  3917.     // that item fully and then yield. At that point we remove the already completed
    
  3918.     // items up until the point we completed them.
    
  3919. 
    
  3920.     let i;
    
  3921.     const completedRootSegment = request.completedRootSegment;
    
  3922.     if (completedRootSegment !== null) {
    
  3923.       if (completedRootSegment.status === POSTPONED) {
    
  3924.         // We postponed the root, so we write nothing.
    
  3925.         return;
    
  3926.       } else if (request.pendingRootTasks === 0) {
    
  3927.         if (enableFloat) {
    
  3928.           writePreamble(
    
  3929.             destination,
    
  3930.             request.resumableState,
    
  3931.             request.renderState,
    
  3932.             request.allPendingTasks === 0 && request.trackedPostpones === null,
    
  3933.           );
    
  3934.         }
    
  3935. 
    
  3936.         flushSegment(request, destination, completedRootSegment);
    
  3937.         request.completedRootSegment = null;
    
  3938.         writeCompletedRoot(destination, request.renderState);
    
  3939.       } else {
    
  3940.         // We haven't flushed the root yet so we don't need to check any other branches further down
    
  3941.         return;
    
  3942.       }
    
  3943.     }
    
  3944. 
    
  3945.     if (enableFloat) {
    
  3946.       writeHoistables(destination, request.resumableState, request.renderState);
    
  3947.     }
    
  3948. 
    
  3949.     // We emit client rendering instructions for already emitted boundaries first.
    
  3950.     // This is so that we can signal to the client to start client rendering them as
    
  3951.     // soon as possible.
    
  3952.     const clientRenderedBoundaries = request.clientRenderedBoundaries;
    
  3953.     for (i = 0; i < clientRenderedBoundaries.length; i++) {
    
  3954.       const boundary = clientRenderedBoundaries[i];
    
  3955.       if (!flushClientRenderedBoundary(request, destination, boundary)) {
    
  3956.         request.destination = null;
    
  3957.         i++;
    
  3958.         clientRenderedBoundaries.splice(0, i);
    
  3959.         return;
    
  3960.       }
    
  3961.     }
    
  3962.     clientRenderedBoundaries.splice(0, i);
    
  3963. 
    
  3964.     // Next we emit any complete boundaries. It's better to favor boundaries
    
  3965.     // that are completely done since we can actually show them, than it is to emit
    
  3966.     // any individual segments from a partially complete boundary.
    
  3967.     const completedBoundaries = request.completedBoundaries;
    
  3968.     for (i = 0; i < completedBoundaries.length; i++) {
    
  3969.       const boundary = completedBoundaries[i];
    
  3970.       if (!flushCompletedBoundary(request, destination, boundary)) {
    
  3971.         request.destination = null;
    
  3972.         i++;
    
  3973.         completedBoundaries.splice(0, i);
    
  3974.         return;
    
  3975.       }
    
  3976.     }
    
  3977.     completedBoundaries.splice(0, i);
    
  3978. 
    
  3979.     // Allow anything written so far to flush to the underlying sink before
    
  3980.     // we continue with lower priorities.
    
  3981.     completeWriting(destination);
    
  3982.     beginWriting(destination);
    
  3983. 
    
  3984.     // TODO: Here we'll emit data used by hydration.
    
  3985. 
    
  3986.     // Next we emit any segments of any boundaries that are partially complete
    
  3987.     // but not deeply complete.
    
  3988.     const partialBoundaries = request.partialBoundaries;
    
  3989.     for (i = 0; i < partialBoundaries.length; i++) {
    
  3990.       const boundary = partialBoundaries[i];
    
  3991.       if (!flushPartialBoundary(request, destination, boundary)) {
    
  3992.         request.destination = null;
    
  3993.         i++;
    
  3994.         partialBoundaries.splice(0, i);
    
  3995.         return;
    
  3996.       }
    
  3997.     }
    
  3998.     partialBoundaries.splice(0, i);
    
  3999. 
    
  4000.     // Next we check the completed boundaries again. This may have had
    
  4001.     // boundaries added to it in case they were too larged to be inlined.
    
  4002.     // New ones might be added in this loop.
    
  4003.     const largeBoundaries = request.completedBoundaries;
    
  4004.     for (i = 0; i < largeBoundaries.length; i++) {
    
  4005.       const boundary = largeBoundaries[i];
    
  4006.       if (!flushCompletedBoundary(request, destination, boundary)) {
    
  4007.         request.destination = null;
    
  4008.         i++;
    
  4009.         largeBoundaries.splice(0, i);
    
  4010.         return;
    
  4011.       }
    
  4012.     }
    
  4013.     largeBoundaries.splice(0, i);
    
  4014.   } finally {
    
  4015.     if (
    
  4016.       request.allPendingTasks === 0 &&
    
  4017.       request.pingedTasks.length === 0 &&
    
  4018.       request.clientRenderedBoundaries.length === 0 &&
    
  4019.       request.completedBoundaries.length === 0
    
  4020.       // We don't need to check any partially completed segments because
    
  4021.       // either they have pending task or they're complete.
    
  4022.     ) {
    
  4023.       request.flushScheduled = false;
    
  4024.       if (enableFloat) {
    
  4025.         // We write the trailing tags but only if don't have any data to resume.
    
  4026.         // If we need to resume we'll write the postamble in the resume instead.
    
  4027.         if (!enablePostpone || request.trackedPostpones === null) {
    
  4028.           writePostamble(destination, request.resumableState);
    
  4029.         }
    
  4030.       }
    
  4031.       completeWriting(destination);
    
  4032.       flushBuffered(destination);
    
  4033.       if (__DEV__) {
    
  4034.         if (request.abortableTasks.size !== 0) {
    
  4035.           console.error(
    
  4036.             'There was still abortable task at the root when we closed. This is a bug in React.',
    
  4037.           );
    
  4038.         }
    
  4039.       }
    
  4040.       // We're done.
    
  4041.       close(destination);
    
  4042.       // We need to stop flowing now because we do not want any async contexts which might call
    
  4043.       // float methods to initiate any flushes after this point
    
  4044.       stopFlowing(request);
    
  4045.     } else {
    
  4046.       completeWriting(destination);
    
  4047.       flushBuffered(destination);
    
  4048.     }
    
  4049.   }
    
  4050. }
    
  4051. 
    
  4052. export function startWork(request: Request): void {
    
  4053.   request.flushScheduled = request.destination !== null;
    
  4054.   if (supportsRequestStorage) {
    
  4055.     scheduleWork(() => requestStorage.run(request, performWork, request));
    
  4056.   } else {
    
  4057.     scheduleWork(() => performWork(request));
    
  4058.   }
    
  4059. }
    
  4060. 
    
  4061. function enqueueFlush(request: Request): void {
    
  4062.   if (
    
  4063.     request.flushScheduled === false &&
    
  4064.     // If there are pinged tasks we are going to flush anyway after work completes
    
  4065.     request.pingedTasks.length === 0 &&
    
  4066.     // If there is no destination there is nothing we can flush to. A flush will
    
  4067.     // happen when we start flowing again
    
  4068.     request.destination !== null
    
  4069.   ) {
    
  4070.     request.flushScheduled = true;
    
  4071.     scheduleWork(() => {
    
  4072.       // We need to existence check destination again here because it might go away
    
  4073.       // in between the enqueueFlush call and the work execution
    
  4074.       const destination = request.destination;
    
  4075.       if (destination) {
    
  4076.         flushCompletedQueues(request, destination);
    
  4077.       } else {
    
  4078.         request.flushScheduled = false;
    
  4079.       }
    
  4080.     });
    
  4081.   }
    
  4082. }
    
  4083. 
    
  4084. export function startFlowing(request: Request, destination: Destination): void {
    
  4085.   if (request.status === CLOSING) {
    
  4086.     request.status = CLOSED;
    
  4087.     closeWithError(destination, request.fatalError);
    
  4088.     return;
    
  4089.   }
    
  4090.   if (request.status === CLOSED) {
    
  4091.     return;
    
  4092.   }
    
  4093.   if (request.destination !== null) {
    
  4094.     // We're already flowing.
    
  4095.     return;
    
  4096.   }
    
  4097.   request.destination = destination;
    
  4098.   try {
    
  4099.     flushCompletedQueues(request, destination);
    
  4100.   } catch (error) {
    
  4101.     logRecoverableError(request, error);
    
  4102.     fatalError(request, error);
    
  4103.   }
    
  4104. }
    
  4105. 
    
  4106. export function stopFlowing(request: Request): void {
    
  4107.   request.destination = null;
    
  4108. }
    
  4109. 
    
  4110. // This is called to early terminate a request. It puts all pending boundaries in client rendered state.
    
  4111. export function abort(request: Request, reason: mixed): void {
    
  4112.   try {
    
  4113.     const abortableTasks = request.abortableTasks;
    
  4114.     if (abortableTasks.size > 0) {
    
  4115.       const error =
    
  4116.         reason === undefined
    
  4117.           ? new Error('The render was aborted by the server without a reason.')
    
  4118.           : reason;
    
  4119.       abortableTasks.forEach(task => abortTask(task, request, error));
    
  4120.       abortableTasks.clear();
    
  4121.     }
    
  4122.     if (request.destination !== null) {
    
  4123.       flushCompletedQueues(request, request.destination);
    
  4124.     }
    
  4125.   } catch (error) {
    
  4126.     logRecoverableError(request, error);
    
  4127.     fatalError(request, error);
    
  4128.   }
    
  4129. }
    
  4130. 
    
  4131. export function flushResources(request: Request): void {
    
  4132.   enqueueFlush(request);
    
  4133. }
    
  4134. 
    
  4135. export function getFormState(
    
  4136.   request: Request,
    
  4137. ): ReactFormState<any, any> | null {
    
  4138.   return request.formState;
    
  4139. }
    
  4140. 
    
  4141. export function getResumableState(request: Request): ResumableState {
    
  4142.   return request.resumableState;
    
  4143. }
    
  4144. 
    
  4145. export function getRenderState(request: Request): RenderState {
    
  4146.   return request.renderState;
    
  4147. }
    
  4148. 
    
  4149. function addToReplayParent(
    
  4150.   node: ReplayNode,
    
  4151.   parentKeyPath: Root | KeyNode,
    
  4152.   trackedPostpones: PostponedHoles,
    
  4153. ): void {
    
  4154.   if (parentKeyPath === null) {
    
  4155.     trackedPostpones.rootNodes.push(node);
    
  4156.   } else {
    
  4157.     const workingMap = trackedPostpones.workingMap;
    
  4158.     let parentNode = workingMap.get(parentKeyPath);
    
  4159.     if (parentNode === undefined) {
    
  4160.       parentNode = ([
    
  4161.         parentKeyPath[1],
    
  4162.         parentKeyPath[2],
    
  4163.         ([]: Array<ReplayNode>),
    
  4164.         null,
    
  4165.       ]: ReplayNode);
    
  4166.       workingMap.set(parentKeyPath, parentNode);
    
  4167.       addToReplayParent(parentNode, parentKeyPath[0], trackedPostpones);
    
  4168.     }
    
  4169.     parentNode[2].push(node);
    
  4170.   }
    
  4171. }
    
  4172. 
    
  4173. export type PostponedState = {
    
  4174.   nextSegmentId: number,
    
  4175.   rootFormatContext: FormatContext,
    
  4176.   progressiveChunkSize: number,
    
  4177.   resumableState: ResumableState,
    
  4178.   replayNodes: Array<ReplayNode>,
    
  4179.   replaySlots: ResumeSlots,
    
  4180. };
    
  4181. 
    
  4182. // Returns the state of a postponed request or null if nothing was postponed.
    
  4183. export function getPostponedState(request: Request): null | PostponedState {
    
  4184.   const trackedPostpones = request.trackedPostpones;
    
  4185.   if (
    
  4186.     trackedPostpones === null ||
    
  4187.     (trackedPostpones.rootNodes.length === 0 &&
    
  4188.       trackedPostpones.rootSlots === null)
    
  4189.   ) {
    
  4190.     // Reset. Let the flushing behave as if we completed the whole document.
    
  4191.     request.trackedPostpones = null;
    
  4192.     return null;
    
  4193.   }
    
  4194.   if (
    
  4195.     request.completedRootSegment !== null &&
    
  4196.     request.completedRootSegment.status === POSTPONED
    
  4197.   ) {
    
  4198.     // We postponed the root so we didn't flush anything.
    
  4199.     resetResumableState(request.resumableState, request.renderState);
    
  4200.   }
    
  4201.   return {
    
  4202.     nextSegmentId: request.nextSegmentId,
    
  4203.     rootFormatContext: request.rootFormatContext,
    
  4204.     progressiveChunkSize: request.progressiveChunkSize,
    
  4205.     resumableState: request.resumableState,
    
  4206.     replayNodes: trackedPostpones.rootNodes,
    
  4207.     replaySlots: trackedPostpones.rootSlots,
    
  4208.   };
    
  4209. }