packages/react-server/src/ReactFlightServer.js JAVASCRIPT 6,646 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 6,646.
1/**2 * Copyright (c) Meta Platforms, Inc. and affiliates.3 *4 * This source code is licensed under the MIT license found in the5 * LICENSE file in the root directory of this source tree.6 *7 * @flow8 */910import type {Chunk, BinaryChunk, Destination} from './ReactServerStreamConfig';1112import type {TemporaryReferenceSet} from './ReactFlightServerTemporaryReferences';1314import {15  enableTaint,16  enableProfilerTimer,17  enableComponentPerformanceTrack,18  enableAsyncDebugInfo,19} from 'shared/ReactFeatureFlags';2021import {22  scheduleWork,23  scheduleMicrotask,24  flushBuffered,25  beginWriting,26  writeChunk,27  writeChunkAndReturn,28  stringToChunk,29  typedArrayToBinaryChunk,30  byteLengthOfChunk,31  byteLengthOfBinaryChunk,32  completeWriting,33  close,34  closeWithError,35} from './ReactServerStreamConfig';3637export type {Destination, Chunk} from './ReactServerStreamConfig';3839import type {40  ClientManifest,41  ClientReferenceMetadata,42  ClientReference,43  ClientReferenceKey,44  ServerReference,45  ServerReferenceId,46  Hints,47  HintCode,48  HintModel,49  FormatContext,50} from './ReactFlightServerConfig';51import type {ThenableState} from './ReactFlightThenable';52import type {53  Wakeable,54  Thenable,55  PendingThenable,56  FulfilledThenable,57  RejectedThenable,58  ReactDebugInfo,59  ReactDebugInfoEntry,60  ReactComponentInfo,61  ReactIOInfo,62  ReactAsyncInfo,63  ReactStackTrace,64  ReactCallSite,65  ReactFunctionLocation,66  ReactErrorInfo,67  ReactErrorInfoDev,68  ReactKey,69} from 'shared/ReactTypes';70import type {ReactElement} from 'shared/ReactElementType';71import type {LazyComponent} from 'react/src/ReactLazy';72import type {73  AsyncSequence,74  IONode,75  PromiseNode,76  UnresolvedPromiseNode,77} from './ReactFlightAsyncSequence';7879import {80  resolveClientReferenceMetadata,81  getServerReferenceId,82  getServerReferenceBoundArguments,83  getServerReferenceLocation,84  getClientReferenceKey,85  isClientReference,86  isServerReference,87  supportsRequestStorage,88  requestStorage,89  createHints,90  createRootFormatContext,91  getChildFormatContext,92  initAsyncDebugInfo,93  markAsyncSequenceRootTask,94  getCurrentAsyncSequence,95  getAsyncSequenceFromPromise,96  parseStackTrace,97  parseStackTracePrivate,98  supportsComponentStorage,99  componentStorage,100  unbadgeConsole,101} from './ReactFlightServerConfig';102103import {104  resolveTemporaryReference,105  isOpaqueTemporaryReference,106} from './ReactFlightServerTemporaryReferences';107108import {109  HooksDispatcher,110  prepareToUseHooksForRequest,111  prepareToUseHooksForComponent,112  getThenableStateAfterSuspending,113  getTrackedThenablesAfterRendering,114  resetHooksForRequest,115} from './ReactFlightHooks';116import {DefaultAsyncDispatcher} from './flight/ReactFlightAsyncDispatcher';117118import {resolveOwner, setCurrentOwner} from './flight/ReactFlightCurrentOwner';119120import {getOwnerStackByComponentInfoInDev} from 'shared/ReactComponentInfoStack';121import {resetOwnerStackLimit} from 'shared/ReactOwnerStackReset';122123import noop from 'shared/noop';124125import {126  callComponentInDEV,127  callLazyInitInDEV,128  callIteratorInDEV,129} from './ReactFlightCallUserSpace';130131import {132  getIteratorFn,133  REACT_ELEMENT_TYPE,134  REACT_LEGACY_ELEMENT_TYPE,135  REACT_FORWARD_REF_TYPE,136  REACT_FRAGMENT_TYPE,137  REACT_LAZY_TYPE,138  REACT_MEMO_TYPE,139  ASYNC_ITERATOR,140  REACT_OPTIMISTIC_KEY,141} from 'shared/ReactSymbols';142143import {144  describeObjectForErrorMessage,145  isGetter,146  isSimpleObject,147  jsxPropsParents,148  jsxChildrenParents,149  objectName,150} from 'shared/ReactSerializationErrors';151152import ReactSharedInternals from './ReactSharedInternalsServer';153import isArray from 'shared/isArray';154import getPrototypeOf from 'shared/getPrototypeOf';155import hasOwnProperty from 'shared/hasOwnProperty';156import binaryToComparableString from 'shared/binaryToComparableString';157158import {SuspenseException, getSuspendedThenable} from './ReactFlightThenable';159160import {161  IO_NODE,162  PROMISE_NODE,163  AWAIT_NODE,164  UNRESOLVED_AWAIT_NODE,165  UNRESOLVED_PROMISE_NODE,166} from './ReactFlightAsyncSequence';167168// DEV-only set containing internal objects that should not be limited and turned into getters.169const doNotLimit: WeakSet<Reference> = __DEV__ ? new WeakSet() : (null as any);170171function defaultFilterStackFrame(172  filename: string,173  functionName: string,174): boolean {175  return (176    filename !== '' &&177    !filename.startsWith('node:') &&178    !filename.includes('node_modules')179  );180}181182function devirtualizeURL(url: string): string {183  if (url.startsWith('about://React/')) {184    // This callsite is a virtual fake callsite that came from another Flight client.185    // We need to reverse it back into the original location by stripping its prefix186    // and suffix. We don't need the environment name because it's available on the187    // parent object that will contain the stack.188    const envIdx = url.indexOf('/', 'about://React/'.length);189    const suffixIdx = url.lastIndexOf('?');190    if (envIdx > -1 && suffixIdx > -1) {191      return decodeURI(url.slice(envIdx + 1, suffixIdx));192    }193  }194  return url;195}196197function isPromiseCreationInternal(url: string, functionName: string): boolean {198  // Various internals of the JS VM can create Promises but the call frame of the199  // internals are not very interesting for our purposes so we need to skip those.200  if (url === 'node:internal/async_hooks') {201    // Ignore the stack frames from the async hooks themselves.202    return true;203  }204  if (url !== '') {205    return false;206  }207  switch (functionName) {208    case 'new Promise':209    case 'Function.withResolvers':210    case 'Function.reject':211    case 'Function.resolve':212    case 'Function.all':213    case 'Function.allSettled':214    case 'Function.race':215    case 'Function.try':216      return true;217    default:218      return false;219  }220}221222function stripLeadingPromiseCreationFrames(223  stack: ReactStackTrace,224): ReactStackTrace {225  for (let i = 0; i < stack.length; i++) {226    const callsite = stack[i];227    const functionName = callsite[0];228    const url = callsite[1];229    if (!isPromiseCreationInternal(url, functionName)) {230      if (i > 0) {231        return stack.slice(i);232      } else {233        return stack;234      }235    }236  }237  return [];238}239240function findCalledFunctionNameFromStackTrace(241  request: Request,242  stack: ReactStackTrace,243): string {244  // Gets the name of the first function called from first party code.245  let bestMatch = '';246  const filterStackFrame = request.filterStackFrame;247  for (let i = 0; i < stack.length; i++) {248    const callsite = stack[i];249    const functionName = callsite[0];250    const url = devirtualizeURL(callsite[1]);251    const lineNumber = callsite[2];252    const columnNumber = callsite[3];253    if (254      filterStackFrame(url, functionName, lineNumber, columnNumber) &&255      // Don't consider anonymous code first party even if the filter wants to include them in the stack.256      url !== ''257    ) {258      if (bestMatch === '') {259        // If we had no good stack frames for internal calls, just use the last260        // first party function name.261        return functionName;262      }263      return bestMatch;264    } else {265      bestMatch = functionName;266    }267  }268  return '';269}270271function filterStackTrace(272  request: Request,273  stack: ReactStackTrace,274): ReactStackTrace {275  // Since stacks can be quite large and we pass a lot of them, we filter them out eagerly276  // to save bandwidth even in DEV. We'll also replay these stacks on the client so by277  // stripping them early we avoid that overhead. Otherwise we'd normally just rely on278  // the DevTools or framework's ignore lists to filter them out.279  const filterStackFrame = request.filterStackFrame;280  const filteredStack: ReactStackTrace = [];281  for (let i = 0; i < stack.length; i++) {282    const callsite = stack[i];283    const functionName = callsite[0];284    const url = devirtualizeURL(callsite[1]);285    const lineNumber = callsite[2];286    const columnNumber = callsite[3];287    if (filterStackFrame(url, functionName, lineNumber, columnNumber)) {288      // Use a clone because the Flight protocol isn't yet resilient to deduping289      // objects in the debug info. TODO: Support deduping stacks.290      const clone: ReactCallSite = callsite.slice(0) as any;291      clone[1] = url;292      filteredStack.push(clone);293    }294  }295  return filteredStack;296}297298function hasUnfilteredFrame(request: Request, stack: ReactStackTrace): boolean {299  const filterStackFrame = request.filterStackFrame;300  for (let i = 0; i < stack.length; i++) {301    const callsite = stack[i];302    const functionName = callsite[0];303    const url = devirtualizeURL(callsite[1]);304    const lineNumber = callsite[2];305    const columnNumber = callsite[3];306    // Ignore async stack frames because they're not "real". We'd expect to have at least307    // one non-async frame if we're actually executing inside a first party function.308    // Otherwise we might just be in the resume of a third party function that resumed309    // inside a first party stack.310    const isAsync = callsite[6];311    if (312      !isAsync &&313      filterStackFrame(url, functionName, lineNumber, columnNumber) &&314      // Ignore anonymous stack frames like internals. They are also not in first party315      // code even though it might be useful to include them in the final stack.316      url !== ''317    ) {318      return true;319    }320  }321  return false;322}323324function isPromiseAwaitInternal(url: string, functionName: string): boolean {325  // Various internals of the JS VM can await internally on a Promise. If those are at326  // the top of the stack then we don't want to consider them as internal frames. The327  // true "await" conceptually is the thing that called the helper.328  // Ideally we'd also include common third party helpers for this.329  if (url === 'node:internal/async_hooks') {330    // Ignore the stack frames from the async hooks themselves.331    return true;332  }333  if (url !== '') {334    return false;335  }336  switch (functionName) {337    case 'Promise.then':338    case 'Promise.catch':339    case 'Promise.finally':340    case 'Function.reject':341    case 'Function.resolve':342    case 'Function.all':343    case 'Function.allSettled':344    case 'Function.any':345    case 'Function.race':346    case 'Function.try':347    case 'Function.withResolvers':348      return true;349    default:350      return false;351  }352}353354export function isAwaitInUserspace(355  request: Request,356  stack: ReactStackTrace,357): boolean {358  let firstFrame = 0;359  while (360    stack.length > firstFrame &&361    isPromiseAwaitInternal(stack[firstFrame][1], stack[firstFrame][0])362  ) {363    // Skip the internal frame that awaits itself.364    firstFrame++;365  }366  if (stack.length > firstFrame) {367    // Check if the very first stack frame that awaited this Promise was in user space.368    // TODO: This doesn't take into account wrapper functions such as our fake .then()369    // in FlightClient which will always be considered third party awaits if you call370    // .then directly.371    const filterStackFrame = request.filterStackFrame;372    const callsite = stack[firstFrame];373    const functionName = callsite[0];374    const url = devirtualizeURL(callsite[1]);375    const lineNumber = callsite[2];376    const columnNumber = callsite[3];377    return (378      filterStackFrame(url, functionName, lineNumber, columnNumber) &&379      url !== ''380    );381  }382  return false;383}384385initAsyncDebugInfo();386387function patchConsole(consoleInst: typeof console, methodName: string) {388  const descriptor = Object.getOwnPropertyDescriptor(consoleInst, methodName);389  if (390    descriptor &&391    (descriptor.configurable || descriptor.writable) &&392    typeof descriptor.value === 'function'393  ) {394    const originalMethod = descriptor.value;395    const originalName = Object.getOwnPropertyDescriptor(396      // $FlowFixMe[incompatible-type]: We should be able to get descriptors from any function.397      originalMethod,398      'name',399    );400    const wrapperMethod = function (this: typeof console) {401      const request = resolveRequest();402      if (methodName === 'assert' && arguments[0]) {403        // assert doesn't emit anything unless first argument is falsy so we can skip it.404      } else if (request !== null) {405        // Extract the stack. Not all console logs print the full stack but they have at406        // least the line it was called from. We could optimize transfer by keeping just407        // one stack frame but keeping it simple for now and include all frames.408        const stack = filterStackTrace(409          request,410          parseStackTracePrivate(new Error('react-stack-top-frame'), 1) || [],411        );412        request.pendingDebugChunks++;413        const owner: null | ReactComponentInfo = resolveOwner();414        const args = Array.from(arguments);415        // Extract the env if this is a console log that was replayed from another env.416        let env = unbadgeConsole(methodName, args);417        if (env === null) {418          // Otherwise add the current environment.419          env = (0, request.environmentName)();420        }421422        emitConsoleChunk(request, methodName, owner, env, stack, args);423      }424      // $FlowFixMe[incompatible-call]425      // $FlowFixMe[incompatible-type]426      return originalMethod.apply(this, arguments);427    };428    if (originalName) {429      Object.defineProperty(430        wrapperMethod,431        // $FlowFixMe[cannot-write] yes it is432        'name',433        originalName,434      );435    }436    Object.defineProperty(consoleInst, methodName, {437      value: wrapperMethod,438    });439  }440}441442// $FlowFixMe[invalid-compare]443if (__DEV__ && typeof console === 'object' && console !== null) {444  // Instrument console to capture logs for replaying on the client.445  patchConsole(console, 'assert');446  patchConsole(console, 'debug');447  patchConsole(console, 'dir');448  patchConsole(console, 'dirxml');449  patchConsole(console, 'error');450  patchConsole(console, 'group');451  patchConsole(console, 'groupCollapsed');452  patchConsole(console, 'groupEnd');453  patchConsole(console, 'info');454  patchConsole(console, 'log');455  patchConsole(console, 'table');456  patchConsole(console, 'trace');457  patchConsole(console, 'warn');458}459460function getCurrentStackInDEV(): string {461  if (__DEV__) {462    const owner: null | ReactComponentInfo = resolveOwner();463    if (owner === null) {464      return '';465    }466    return getOwnerStackByComponentInfoInDev(owner);467  }468  return '';469}470471const ObjectPrototype = Object.prototype;472473const stringify = JSON.stringify;474475type ReactJSONValue =476  | string477  | boolean478  | number479  | null480  | $ReadOnlyArray<ReactClientValue>481  | ReactClientObject;482483// Serializable values484export type ReactClientValue =485  // Server Elements and Lazy Components are unwrapped on the Server486  | React$Element<component(...props: any)>487  | LazyComponent<ReactClientValue, any>488  // References are passed by their value489  | ClientReference<any>490  | ServerReference<any>491  // The rest are passed as is. Sub-types can be passed in but lose their492  // subtype, so the receiver can only accept once of these.493  | React$Element<string>494  | React$Element<ClientReference<any> & any>495  | ReactComponentInfo496  | ReactErrorInfo497  | string498  | boolean499  | number500  | symbol501  | null502  | void503  | bigint504  | ReadableStream505  | $AsyncIterable<ReactClientValue, ReactClientValue, void>506  | $AsyncIterator<ReactClientValue, ReactClientValue, void>507  | Iterable<ReactClientValue>508  | Iterator<ReactClientValue>509  | Array<ReactClientValue>510  | Map<ReactClientValue, ReactClientValue>511  | Set<ReactClientValue>512  | FormData513  | $ArrayBufferView514  | ArrayBuffer515  | Date516  | ReactClientObject517  | Promise<ReactClientValue>; // Thenable<ReactClientValue>518519type ReactClientObject = {+[key: string]: ReactClientValue};520521// task status522const PENDING = 0;523const COMPLETED = 1;524const ABORTED = 3;525const ERRORED = 4;526const RENDERING = 5;527528type Task = {529  id: number,530  status: 0 | 1 | 3 | 4 | 5,531  model: ReactClientValue,532  ping: () => void,533  keyPath: ReactKey, // parent server component keys534  implicitSlot: boolean, // true if the root server component of this sequence had a null key535  formatContext: FormatContext, // an approximate parent context from host components536  thenableState: ThenableState | null,537  timed: boolean, // Profiling-only. Whether we need to track the completion time of this task.538  time: number, // Profiling-only. The last time stamp emitted for this task.539  environmentName: string, // DEV-only. Used to track if the environment for this task changed.540  debugOwner: null | ReactComponentInfo, // DEV-only541  debugStack: null | Error, // DEV-only542  debugTask: null | ConsoleTask, // DEV-only543};544545interface Reference {}546547type ReactClientReference = Reference & ReactClientValue;548549type DeferredDebugStore = {550  retained: Map<number, ReactClientReference | string>,551  existing: Map<ReactClientReference | string, number>,552};553554const __PROTO__ = '__proto__';555556const OPENING = 10;557const OPEN = 11;558const ABORTING = 12;559const CLOSING = 13;560const CLOSED = 14;561562const RENDER = 20;563const PRERENDER = 21;564565// Marker pushed before a [headerChunk, contentChunk] pair in566// completedRegularChunks / completedDebugChunks to signal that the next two567// entries must be written atomically — see emitTextChunk and568// emitTypedArrayChunk for why, and flushCompletedChunks for how it's read.569const NEXT_TWO_CHUNKS_ARE_ATOMIC: symbol = Symbol();570571export type Request = {572  status: 10 | 11 | 12 | 13 | 14,573  type: 20 | 21,574  flushScheduled: boolean,575  fatalError: mixed,576  destination: null | Destination,577  bundlerConfig: ClientManifest,578  cache: Map<Function, mixed>,579  cacheController: AbortController,580  nextChunkId: number,581  pendingChunks: number,582  hints: Hints,583  abortableTasks: Set<Task>,584  pingedTasks: Array<Task>,585  completedImportChunks: Array<Chunk>,586  completedHintChunks: Array<Chunk>,587  // Text and TypedArray rows are pushed as a NEXT_TWO_CHUNKS_ARE_ATOMIC588  // sentinel followed by their [headerChunk, contentChunk] pair, so that589  // flushCompletedChunks can write the pair atomically and never strand the590  // content chunk on a backpressure break.591  completedRegularChunks: Array<592    Chunk | BinaryChunk | typeof NEXT_TWO_CHUNKS_ARE_ATOMIC,593  >,594  completedErrorChunks: Array<Chunk>,595  writtenSymbols: Map<symbol, number>,596  writtenClientReferences: Map<ClientReferenceKey, number>,597  writtenServerReferences: Map<ServerReference<any>, number>,598  writtenObjects: WeakMap<Reference, string>,599  temporaryReferences: void | TemporaryReferenceSet,600  identifierPrefix: string,601  identifierCount: number,602  taintCleanupQueue: Array<string | bigint>,603  onError: (error: mixed) => ?string,604  onAllReady: () => void,605  onFatalError: mixed => void,606  // Profiling-only607  timeOrigin: number,608  abortTime: number,609  // DEV-only610  pendingDebugChunks: number,611  // See completedRegularChunks for why some entries are preceded by the612  // NEXT_TWO_CHUNKS_ARE_ATOMIC sentinel.613  completedDebugChunks: Array<614    Chunk | BinaryChunk | typeof NEXT_TWO_CHUNKS_ARE_ATOMIC,615  >,616  debugDestination: null | Destination,617  environmentName: () => string,618  filterStackFrame: (619    url: string,620    functionName: string,621    lineNumber: number,622    columnNumber: number,623  ) => boolean,624  didWarnForKey: null | WeakSet<ReactComponentInfo>,625  writtenDebugObjects: WeakMap<Reference, string>,626  deferredDebugObjects: null | DeferredDebugStore,627};628629const {630  TaintRegistryObjects,631  TaintRegistryValues,632  TaintRegistryByteLengths,633  TaintRegistryPendingRequests,634} = ReactSharedInternals;635636function throwTaintViolation(message: string) {637  // eslint-disable-next-line react-internal/prod-error-codes638  throw new Error(message);639}640641function cleanupTaintQueue(request: Request): void {642  const cleanupQueue = request.taintCleanupQueue;643  TaintRegistryPendingRequests.delete(cleanupQueue);644  for (let i = 0; i < cleanupQueue.length; i++) {645    const entryValue = cleanupQueue[i];646    const entry = TaintRegistryValues.get(entryValue);647    if (entry !== undefined) {648      if (entry.count === 1) {649        TaintRegistryValues.delete(entryValue);650      } else {651        entry.count--;652      }653    }654  }655  cleanupQueue.length = 0;656}657658function defaultErrorHandler(error: mixed) {659  console['error'](error);660  // Don't transform to our wrapper661}662663function RequestInstance(664  this: $FlowFixMe,665  type: 20 | 21,666  model: ReactClientValue,667  bundlerConfig: ClientManifest,668  onError: void | ((error: mixed) => ?string),669  onAllReady: () => void,670  onFatalError: (error: mixed) => void,671  identifierPrefix?: string,672  temporaryReferences: void | TemporaryReferenceSet,673  debugStartTime: void | number, // Profiling-only674  environmentName: void | string | (() => string), // DEV-only675  filterStackFrame: void | ((url: string, functionName: string) => boolean), // DEV-only676  keepDebugAlive: boolean, // DEV-only677) {678  if (679    ReactSharedInternals.A !== null &&680    ReactSharedInternals.A !== DefaultAsyncDispatcher681  ) {682    throw new Error(683      'Currently React only supports one RSC renderer at a time.',684    );685  }686  ReactSharedInternals.A = DefaultAsyncDispatcher;687  if (__DEV__) {688    // Unlike Fizz or Fiber, we don't reset this and just keep it on permanently.689    // This lets it act more like the AsyncDispatcher so that we can get the690    // stack asynchronously too.691    ReactSharedInternals.getCurrentStack = getCurrentStackInDEV;692  }693694  const abortSet: Set<Task> = new Set();695  const pingedTasks: Array<Task> = [];696  const cleanupQueue: Array<string | bigint> = [];697  if (enableTaint) {698    TaintRegistryPendingRequests.add(cleanupQueue);699  }700  const hints = createHints();701  this.type = type;702  this.status = OPENING;703  this.flushScheduled = false;704  this.fatalError = null;705  this.destination = null;706  this.bundlerConfig = bundlerConfig;707  this.cache = new Map();708  this.cacheController = new AbortController();709  this.nextChunkId = 0;710  this.pendingChunks = 0;711  this.hints = hints;712  this.abortableTasks = abortSet;713  this.pingedTasks = pingedTasks;714  this.completedImportChunks = [] as Array<Chunk>;715  this.completedHintChunks = [] as Array<Chunk>;716  this.completedRegularChunks = [] as Array<717    Chunk | BinaryChunk | typeof NEXT_TWO_CHUNKS_ARE_ATOMIC,718  >;719  this.completedErrorChunks = [] as Array<Chunk>;720  this.writtenSymbols = new Map();721  this.writtenClientReferences = new Map();722  this.writtenServerReferences = new Map();723  this.writtenObjects = new WeakMap();724  this.temporaryReferences = temporaryReferences;725  this.identifierPrefix = identifierPrefix || '';726  this.identifierCount = 1;727  this.taintCleanupQueue = cleanupQueue;728  this.onError = onError === undefined ? defaultErrorHandler : onError;729  this.onAllReady = onAllReady;730  this.onFatalError = onFatalError;731732  if (__DEV__) {733    this.pendingDebugChunks = 0;734    this.completedDebugChunks = [] as Array<735      Chunk | BinaryChunk | typeof NEXT_TWO_CHUNKS_ARE_ATOMIC,736    >;737    this.debugDestination = null;738    this.environmentName =739      environmentName === undefined740        ? () => 'Server'741        : typeof environmentName !== 'function'742          ? () => environmentName743          : environmentName;744    this.filterStackFrame =745      filterStackFrame === undefined746        ? defaultFilterStackFrame747        : filterStackFrame;748    this.didWarnForKey = null;749    this.writtenDebugObjects = new WeakMap();750    this.deferredDebugObjects = keepDebugAlive751      ? {752          retained: new Map(),753          existing: new Map(),754        }755      : null;756  }757758  let timeOrigin: number;759  if (760    enableProfilerTimer &&761    (enableComponentPerformanceTrack || enableAsyncDebugInfo)762  ) {763    // We start by serializing the time origin. Any future timestamps will be764    // emitted relatively to this origin. Instead of using performance.timeOrigin765    // as this origin, we use the timestamp at the start of the request.766    // This avoids leaking unnecessary information like how long the server has767    // been running and allows for more compact representation of each timestamp.768    // The time origin is stored as an offset in the time space of this environment.769    if (typeof debugStartTime === 'number') {770      // We expect `startTime` to be an absolute timestamp, so relativize it to match the other case.771      timeOrigin = this.timeOrigin =772        debugStartTime -773        // $FlowFixMe[prop-missing]774        performance.timeOrigin;775    } else {776      timeOrigin = this.timeOrigin = performance.now();777    }778    emitTimeOriginChunk(779      this,780      timeOrigin +781        // $FlowFixMe[prop-missing]782        performance.timeOrigin,783    );784    this.abortTime = -0.0;785  } else {786    timeOrigin = 0;787  }788789  const rootTask = createTask(790    this,791    model,792    null,793    false,794    createRootFormatContext(),795    abortSet,796    timeOrigin,797    null,798    null,799    null,800  );801  pingedTasks.push(rootTask);802}803804export function createRequest(805  model: ReactClientValue,806  bundlerConfig: ClientManifest,807  onError: void | ((error: mixed) => ?string),808  identifierPrefix: void | string,809  temporaryReferences: void | TemporaryReferenceSet,810  debugStartTime: void | number, // Profiling-only811  environmentName: void | string | (() => string), // DEV-only812  filterStackFrame: void | ((url: string, functionName: string) => boolean), // DEV-only813  keepDebugAlive: boolean, // DEV-only814): Request {815  if (__DEV__) {816    resetOwnerStackLimit();817  }818819  // $FlowFixMe[invalid-constructor]: the shapes are exact here but Flow doesn't like constructors820  return new RequestInstance(821    RENDER,822    model,823    bundlerConfig,824    onError,825    noop,826    noop,827    identifierPrefix,828    temporaryReferences,829    debugStartTime,830    environmentName,831    filterStackFrame,832    keepDebugAlive,833  );834}835836export function createPrerenderRequest(837  model: ReactClientValue,838  bundlerConfig: ClientManifest,839  onAllReady: () => void,840  onFatalError: () => void,841  onError: void | ((error: mixed) => ?string),842  identifierPrefix: void | string,843  temporaryReferences: void | TemporaryReferenceSet,844  debugStartTime: void | number, // Profiling-only845  environmentName: void | string | (() => string), // DEV-only846  filterStackFrame: void | ((url: string, functionName: string) => boolean), // DEV-only847  keepDebugAlive: boolean, // DEV-only848): Request {849  if (__DEV__) {850    resetOwnerStackLimit();851  }852853  // $FlowFixMe[invalid-constructor]: the shapes are exact here but Flow doesn't like constructors854  return new RequestInstance(855    PRERENDER,856    model,857    bundlerConfig,858    onError,859    onAllReady,860    onFatalError,861    identifierPrefix,862    temporaryReferences,863    debugStartTime,864    environmentName,865    filterStackFrame,866    keepDebugAlive,867  );868}869870let currentRequest: null | Request = null;871872export function resolveRequest(): null | Request {873  if (currentRequest) return currentRequest;874  // $FlowFixMe[constant-condition]875  if (supportsRequestStorage) {876    const store = requestStorage.getStore();877    if (store) return store;878  }879  return null;880}881882function isTypedArray(value: any): boolean {883  if (value instanceof ArrayBuffer) {884    return true;885  }886  if (value instanceof Int8Array) {887    return true;888  }889  if (value instanceof Uint8Array) {890    return true;891  }892  if (value instanceof Uint8ClampedArray) {893    return true;894  }895  if (value instanceof Int16Array) {896    return true;897  }898  if (value instanceof Uint16Array) {899    return true;900  }901  if (value instanceof Int32Array) {902    return true;903  }904  if (value instanceof Uint32Array) {905    return true;906  }907  if (value instanceof Float32Array) {908    return true;909  }910  if (value instanceof Float64Array) {911    return true;912  }913  if (value instanceof BigInt64Array) {914    return true;915  }916  if (value instanceof BigUint64Array) {917    return true;918  }919  if (value instanceof DataView) {920    return true;921  }922  return false;923}924925function serializeDebugThenable(926  request: Request,927  counter: {objectLimit: number},928  thenable: Thenable<any>,929): string {930  // Like serializeThenable but for renderDebugModel931  request.pendingDebugChunks++;932  const id = request.nextChunkId++;933  const ref = serializePromiseID(id);934  request.writtenDebugObjects.set(thenable, ref);935936  switch (thenable.status) {937    case 'fulfilled': {938      emitOutlinedDebugModelChunk(request, id, counter, thenable.value);939      return ref;940    }941    case 'rejected': {942      const x = thenable.reason;943      // We don't log these errors since they didn't actually throw into Flight.944      const digest = '';945      emitErrorChunk(request, id, digest, x, true, null);946      return ref;947    }948  }949950  if (request.status === ABORTING) {951    // Ensure that we have time to emit the halt chunk if we're sync aborting.952    emitDebugHaltChunk(request, id);953    return ref;954  }955956  const deferredDebugObjects = request.deferredDebugObjects;957  if (deferredDebugObjects !== null) {958    // For Promises that are not yet resolved, we always defer them. They are async anyway so it's959    // safe to defer them. This also ensures that we don't eagerly call .then() on a Promise that960    // otherwise wouldn't have initialized. It also ensures that we don't "handle" a rejection961    // that otherwise would have triggered unhandled rejection.962    deferredDebugObjects.retained.set(id, thenable as any);963    const deferredRef = '$Y@' + id.toString(16);964    // We can now refer to the deferred object in the future.965    request.writtenDebugObjects.set(thenable, deferredRef);966    return deferredRef;967  }968969  let cancelled = false;970971  thenable.then(972    value => {973      if (cancelled) {974        return;975      }976      cancelled = true;977      if (request.status === ABORTING) {978        emitDebugHaltChunk(request, id);979        enqueueFlush(request);980        return;981      }982      if (983        (isArray(value) && value.length > 200) ||984        (isTypedArray(value) && value.byteLength > 1000)985      ) {986        // If this should be deferred, but we don't have a debug channel installed987        // it would get omitted. We can't omit outlined models but we can avoid988        // resolving the Promise at all by halting it.989        emitDebugHaltChunk(request, id);990        enqueueFlush(request);991        return;992      }993      emitOutlinedDebugModelChunk(request, id, counter, value);994      enqueueFlush(request);995    },996    reason => {997      if (cancelled) {998        return;999      }1000      cancelled = true;1001      if (request.status === ABORTING) {1002        emitDebugHaltChunk(request, id);1003        enqueueFlush(request);1004        return;1005      }1006      // We don't log these errors since they didn't actually throw into Flight.1007      const digest = '';1008      emitErrorChunk(request, id, digest, reason, true, null);1009      enqueueFlush(request);1010    },1011  );10121013  // We don't use scheduleMicrotask here because it doesn't actually schedule a microtask1014  // in all our configs which is annoying.1015  Promise.resolve().then(() => {1016    // If we don't resolve the Promise within a microtask. Leave it as hanging since we1017    // don't want to block the render forever on a Promise that might never resolve.1018    if (cancelled) {1019      return;1020    }1021    cancelled = true;1022    emitDebugHaltChunk(request, id);1023    enqueueFlush(request);1024    // Clean up the request so we don't leak this forever.1025    request = null as any;1026    counter = null as any;1027  });10281029  return ref;1030}10311032function emitRequestedDebugThenable(1033  request: Request,1034  id: number,1035  counter: {objectLimit: number},1036  thenable: Thenable<any>,1037): void {1038  thenable.then(1039    value => {1040      if (request.status === ABORTING) {1041        emitDebugHaltChunk(request, id);1042        enqueueFlush(request);1043        return;1044      }1045      emitOutlinedDebugModelChunk(request, id, counter, value);1046      enqueueFlush(request);1047    },1048    reason => {1049      if (request.status === ABORTING) {1050        emitDebugHaltChunk(request, id);1051        enqueueFlush(request);1052        return;1053      }1054      // We don't log these errors since they didn't actually throw into Flight.1055      const digest = '';1056      emitErrorChunk(request, id, digest, reason, true, null);1057      enqueueFlush(request);1058    },1059  );1060}10611062function serializeThenable(1063  request: Request,1064  task: Task,1065  thenable: Thenable<any>,1066): number {1067  const newTask = createTask(1068    request,1069    thenable as any, // will be replaced by the value before we retry. used for debug info.1070    task.keyPath, // the server component sequence continues through Promise-as-a-child.1071    task.implicitSlot,1072    task.formatContext,1073    request.abortableTasks,1074    enableProfilerTimer &&1075      (enableComponentPerformanceTrack || enableAsyncDebugInfo)1076      ? task.time1077      : 0,1078    __DEV__ ? task.debugOwner : null,1079    __DEV__ ? task.debugStack : null,1080    __DEV__ ? task.debugTask : null,1081  );10821083  switch (thenable.status) {1084    case 'fulfilled': {1085      forwardDebugInfoFromThenable(request, newTask, thenable, null, null);1086      // We have the resolved value, we can go ahead and schedule it for serialization.1087      newTask.model = thenable.value;1088      pingTask(request, newTask);1089      return newTask.id;1090    }1091    case 'rejected': {1092      forwardDebugInfoFromThenable(request, newTask, thenable, null, null);1093      const x = thenable.reason;1094      erroredTask(request, newTask, x);1095      return newTask.id;1096    }1097    default: {1098      if (request.status === ABORTING) {1099        // We can no longer accept any resolved values1100        request.abortableTasks.delete(newTask);1101        if (request.type === PRERENDER) {1102          haltTask(newTask, request);1103          finishHaltedTask(newTask, request);1104        } else {1105          const errorId: number = request.fatalError as any;1106          abortTask(newTask, request, errorId);1107          finishAbortedTask(newTask, request, errorId);1108        }1109        return newTask.id;1110      }1111      if (typeof thenable.status === 'string') {1112        // Only instrument the thenable if the status if not defined. If1113        // it's defined, but an unknown value, assume it's been instrumented by1114        // some custom userspace implementation. We treat it as "pending".1115        break;1116      }1117      const pendingThenable: PendingThenable<mixed> = thenable as any;1118      pendingThenable.status = 'pending';1119      pendingThenable.then(1120        fulfilledValue => {1121          if (thenable.status === 'pending') {1122            const fulfilledThenable: FulfilledThenable<mixed> = thenable as any;1123            fulfilledThenable.status = 'fulfilled';1124            fulfilledThenable.value = fulfilledValue;1125          }1126        },1127        (error: mixed) => {1128          if (thenable.status === 'pending') {1129            const rejectedThenable: RejectedThenable<mixed> = thenable as any;1130            rejectedThenable.status = 'rejected';1131            rejectedThenable.reason = error;1132          }1133        },1134      );1135      break;1136    }1137  }11381139  thenable.then(1140    value => {1141      forwardDebugInfoFromCurrentContext(request, newTask, thenable);1142      newTask.model = value;1143      pingTask(request, newTask);1144    },1145    reason => {1146      if (newTask.status === PENDING) {1147        if (1148          enableProfilerTimer &&1149          (enableComponentPerformanceTrack || enableAsyncDebugInfo)1150        ) {1151          // If this is async we need to time when this task finishes.1152          newTask.timed = true;1153        }1154        // We expect that the only status it might be otherwise is ABORTED.1155        // When we abort we emit chunks in each pending task slot and don't need1156        // to do so again here.1157        erroredTask(request, newTask, reason);1158        enqueueFlush(request);1159      }1160    },1161  );11621163  return newTask.id;1164}11651166function serializeReadableStream(1167  request: Request,1168  task: Task,1169  stream: ReadableStream,1170): string {1171  // Detect if this is a BYOB stream. BYOB streams should be able to be read as bytes on the1172  // receiving side. It also implies that different chunks can be split up or merged as opposed1173  // to a readable stream that happens to have Uint8Array as the type which might expect it to be1174  // received in the same slices.1175  // $FlowFixMe[prop-missing]: This is a Node.js extension.1176  let supportsBYOB: void | boolean = stream.supportsBYOB;1177  if (supportsBYOB === undefined) {1178    try {1179      // $FlowFixMe[extra-arg]: This argument is accepted.1180      stream.getReader({mode: 'byob'}).releaseLock();1181      supportsBYOB = true;1182    } catch (x) {1183      supportsBYOB = false;1184    }1185  }1186  // At this point supportsBYOB is guaranteed to be a boolean.1187  const isByteStream: boolean = supportsBYOB;11881189  const reader = stream.getReader();11901191  // This task won't actually be retried. We just use it to attempt synchronous renders.1192  const streamTask = createTask(1193    request,1194    task.model,1195    task.keyPath,1196    task.implicitSlot,1197    task.formatContext,1198    request.abortableTasks,1199    enableProfilerTimer &&1200      (enableComponentPerformanceTrack || enableAsyncDebugInfo)1201      ? task.time1202      : 0,1203    __DEV__ ? task.debugOwner : null,1204    __DEV__ ? task.debugStack : null,1205    __DEV__ ? task.debugTask : null,1206  );12071208  // The task represents the Stop row. This adds a Start row.1209  request.pendingChunks++;1210  const startStreamRow =1211    streamTask.id.toString(16) + ':' + (isByteStream ? 'r' : 'R') + '\n';1212  request.completedRegularChunks.push(stringToChunk(startStreamRow));12131214  function progress(entry: {done: boolean, value: ReactClientValue, ...}) {1215    if (streamTask.status !== PENDING) {1216      return;1217    }12181219    if (entry.done) {1220      streamTask.status = COMPLETED;1221      const endStreamRow = streamTask.id.toString(16) + ':C\n';1222      request.completedRegularChunks.push(stringToChunk(endStreamRow));1223      request.abortableTasks.delete(streamTask);1224      request.cacheController.signal.removeEventListener('abort', abortStream);1225      enqueueFlush(request);1226      callOnAllReadyIfReady(request);1227    } else {1228      try {1229        request.pendingChunks++;1230        streamTask.model = entry.value;1231        if (isByteStream) {1232          // Chunks of byte streams are always Uint8Array instances.1233          const chunk: Uint8Array = streamTask.model as any;1234          emitTypedArrayChunk(request, streamTask.id, 'b', chunk, false);1235        } else {1236          tryStreamTask(request, streamTask);1237        }1238        enqueueFlush(request);1239        reader.read().then(progress, error);1240      } catch (x) {1241        error(x);1242      }1243    }1244  }1245  function error(reason: mixed) {1246    if (streamTask.status !== PENDING) {1247      return;1248    }1249    request.cacheController.signal.removeEventListener('abort', abortStream);1250    erroredTask(request, streamTask, reason);1251    enqueueFlush(request);12521253    // $FlowFixMe[incompatible-type] should be able to pass mixed1254    // $FlowFixMe[incompatible-use]1255    reader.cancel(reason).then(error, error);1256  }1257  function abortStream() {1258    if (streamTask.status !== PENDING) {1259      return;1260    }1261    const signal = request.cacheController.signal;1262    signal.removeEventListener('abort', abortStream);1263    const reason = signal.reason;1264    if (request.type === PRERENDER) {1265      request.abortableTasks.delete(streamTask);1266      haltTask(streamTask, request);1267      finishHaltedTask(streamTask, request);1268    } else {1269      // TODO: Make this use abortTask() instead.1270      erroredTask(request, streamTask, reason);1271      enqueueFlush(request);1272    }1273    // $FlowFixMe[incompatible-use] should be able to pass mixed1274    reader.cancel(reason).then(error, error);1275  }12761277  request.cacheController.signal.addEventListener('abort', abortStream);1278  reader.read().then(progress, error);1279  return serializeByValueID(streamTask.id);1280}12811282function serializeAsyncIterable(1283  request: Request,1284  task: Task,1285  iterable: $AsyncIterable<ReactClientValue, ReactClientValue, void>,1286  iterator: $AsyncIterator<ReactClientValue, ReactClientValue, void>,1287): string {1288  // Generators/Iterators are Iterables but they're also their own iterator1289  // functions. If that's the case, we treat them as single-shot. Otherwise,1290  // we assume that this iterable might be a multi-shot and allow it to be1291  // iterated more than once on the client.1292  const isIterator = iterable === iterator;12931294  // This task won't actually be retried. We just use it to attempt synchronous renders.1295  const streamTask = createTask(1296    request,1297    task.model,1298    task.keyPath,1299    task.implicitSlot,1300    task.formatContext,1301    request.abortableTasks,1302    enableProfilerTimer &&1303      (enableComponentPerformanceTrack || enableAsyncDebugInfo)1304      ? task.time1305      : 0,1306    __DEV__ ? task.debugOwner : null,1307    __DEV__ ? task.debugStack : null,1308    __DEV__ ? task.debugTask : null,1309  );13101311  if (__DEV__) {1312    const debugInfo: ?ReactDebugInfo = (iterable as any)._debugInfo;1313    if (debugInfo) {1314      forwardDebugInfo(request, streamTask, debugInfo);1315    }1316  }13171318  // The task represents the Stop row. This adds a Start row.1319  request.pendingChunks++;1320  const startStreamRow =1321    streamTask.id.toString(16) + ':' + (isIterator ? 'x' : 'X') + '\n';1322  request.completedRegularChunks.push(stringToChunk(startStreamRow));13231324  function progress(1325    entry:1326      | {done: false, +value: ReactClientValue, ...}1327      | {done: true, +value: ReactClientValue, ...},1328  ) {1329    if (streamTask.status !== PENDING) {1330      return;1331    }13321333    if (entry.done) {1334      streamTask.status = COMPLETED;1335      let endStreamRow;1336      if (entry.value === undefined) {1337        endStreamRow = streamTask.id.toString(16) + ':C\n';1338      } else {1339        // Unlike streams, the last value may not be undefined. If it's not1340        // we outline it and encode a reference to it in the closing instruction.1341        try {1342          const chunkId = outlineModel(request, entry.value);1343          endStreamRow =1344            streamTask.id.toString(16) +1345            ':C' +1346            stringify(serializeByValueID(chunkId)) +1347            '\n';1348        } catch (x) {1349          error(x);1350          return;1351        }1352      }1353      request.completedRegularChunks.push(stringToChunk(endStreamRow));1354      request.abortableTasks.delete(streamTask);1355      request.cacheController.signal.removeEventListener(1356        'abort',1357        abortIterable,1358      );1359      enqueueFlush(request);1360      callOnAllReadyIfReady(request);1361    } else {1362      try {1363        streamTask.model = entry.value;1364        request.pendingChunks++;1365        tryStreamTask(request, streamTask);1366        enqueueFlush(request);1367        if (__DEV__) {1368          callIteratorInDEV(iterator, progress, error);1369        } else {1370          iterator.next().then(progress, error);1371        }1372      } catch (x) {1373        error(x);1374        return;1375      }1376    }1377  }1378  function error(reason: mixed) {1379    if (streamTask.status !== PENDING) {1380      return;1381    }1382    request.cacheController.signal.removeEventListener('abort', abortIterable);1383    erroredTask(request, streamTask, reason);1384    enqueueFlush(request);1385    if (typeof (iterator as any).throw === 'function') {1386      // The iterator protocol doesn't necessarily include this but a generator do.1387      // $FlowFixMe[prop-missing] should be able to pass mixed1388      iterator.throw(reason).then(error, error);1389    }1390  }1391  function abortIterable() {1392    if (streamTask.status !== PENDING) {1393      return;1394    }1395    const signal = request.cacheController.signal;1396    signal.removeEventListener('abort', abortIterable);1397    const reason = signal.reason;1398    if (request.type === PRERENDER) {1399      request.abortableTasks.delete(streamTask);1400      haltTask(streamTask, request);1401      finishHaltedTask(streamTask, request);1402    } else {1403      // TODO: Make this use abortTask() instead.1404      erroredTask(request, streamTask, signal.reason);1405      enqueueFlush(request);1406    }1407    if (typeof (iterator as any).throw === 'function') {1408      // The iterator protocol doesn't necessarily include this but a generator do.1409      // $FlowFixMe[prop-missing] should be able to pass mixed1410      iterator.throw(reason).then(error, error);1411    }1412  }1413  request.cacheController.signal.addEventListener('abort', abortIterable);1414  if (__DEV__) {1415    callIteratorInDEV(iterator, progress, error);1416  } else {1417    iterator.next().then(progress, error);1418  }1419  return serializeByValueID(streamTask.id);1420}14211422export function emitHint<Code: HintCode>(1423  request: Request,1424  code: Code,1425  model: HintModel<Code>,1426): void {1427  emitHintChunk(request, code, model);1428  enqueueFlush(request);1429}14301431export function getHints(request: Request): Hints {1432  return request.hints;1433}14341435export function getCache(request: Request): Map<Function, mixed> {1436  return request.cache;1437}14381439function readThenable<T>(thenable: Thenable<T>): T {1440  if (thenable.status === 'fulfilled') {1441    return thenable.value;1442  } else if (thenable.status === 'rejected') {1443    throw thenable.reason;1444  }1445  throw thenable;1446}14471448function createLazyWrapperAroundWakeable(1449  request: Request,1450  task: Task,1451  wakeable: Wakeable,1452) {1453  // This is a temporary fork of the `use` implementation until we accept1454  // promises everywhere.1455  const thenable: Thenable<mixed> = wakeable as any;1456  switch (thenable.status) {1457    case 'fulfilled': {1458      forwardDebugInfoFromThenable(request, task, thenable, null, null);1459      return thenable.value;1460    }1461    case 'rejected':1462      forwardDebugInfoFromThenable(request, task, thenable, null, null);1463      break;1464    default: {1465      if (typeof thenable.status === 'string') {1466        // Only instrument the thenable if the status if not defined. If1467        // it's defined, but an unknown value, assume it's been instrumented by1468        // some custom userspace implementation. We treat it as "pending".1469        break;1470      }1471      const pendingThenable: PendingThenable<mixed> = thenable as any;1472      pendingThenable.status = 'pending';1473      pendingThenable.then(1474        fulfilledValue => {1475          forwardDebugInfoFromCurrentContext(request, task, thenable);1476          if (thenable.status === 'pending') {1477            const fulfilledThenable: FulfilledThenable<mixed> = thenable as any;1478            fulfilledThenable.status = 'fulfilled';1479            fulfilledThenable.value = fulfilledValue;1480          }1481        },1482        (error: mixed) => {1483          forwardDebugInfoFromCurrentContext(request, task, thenable);1484          if (thenable.status === 'pending') {1485            const rejectedThenable: RejectedThenable<mixed> = thenable as any;1486            rejectedThenable.status = 'rejected';1487            rejectedThenable.reason = error;1488          }1489        },1490      );1491      break;1492    }1493  }1494  const lazyType: LazyComponent<any, Thenable<any>> = {1495    $$typeof: REACT_LAZY_TYPE,1496    _payload: thenable,1497    _init: readThenable,1498  };1499  return lazyType;1500}15011502function callWithDebugContextInDEV<A, T>(1503  request: Request,1504  task: Task,1505  callback: A => T,1506  arg: A,1507): T {1508  // We don't have a Server Component instance associated with this callback and1509  // the nearest context is likely a Client Component being serialized. We create1510  // a fake owner during this callback so we can get the stack trace from it.1511  // This also gets sent to the client as the owner for the replaying log.1512  const componentDebugInfo: ReactComponentInfo = {1513    name: '',1514    env: task.environmentName,1515    key: null,1516    owner: task.debugOwner,1517  };1518  // $FlowFixMe[cannot-write]1519  componentDebugInfo.stack =1520    task.debugStack === null1521      ? null1522      : filterStackTrace(request, parseStackTrace(task.debugStack, 1));1523  // $FlowFixMe[cannot-write]1524  componentDebugInfo.debugStack = task.debugStack;1525  // $FlowFixMe[cannot-write]1526  componentDebugInfo.debugTask = task.debugTask;1527  const debugTask = task.debugTask;1528  // We don't need the async component storage context here so we only set the1529  // synchronous tracking of owner.1530  setCurrentOwner(componentDebugInfo);1531  try {1532    if (debugTask) {1533      return debugTask.run(callback.bind(null, arg));1534    }1535    return callback(arg);1536  } finally {1537    setCurrentOwner(null);1538  }1539}15401541const voidHandler = () => {};15421543function processServerComponentReturnValue(1544  request: Request,1545  task: Task,1546  Component: any,1547  result: any,1548): any {1549  // A Server Component's return value has a few special properties due to being1550  // in the return position of a Component. We convert them here.1551  if (1552    typeof result !== 'object' ||1553    result === null ||1554    isClientReference(result)1555  ) {1556    return result;1557  }15581559  if (typeof result.then === 'function') {1560    // When the return value is in children position we can resolve it immediately,1561    // to its value without a wrapper if it's synchronously available.1562    const thenable: Thenable<any> = result;1563    if (__DEV__) {1564      // If the thenable resolves to an element, then it was in a static position,1565      // the return value of a Server Component. That doesn't need further validation1566      // of keys. The Server Component itself would have had a key.1567      thenable.then(resolvedValue => {1568        if (1569          typeof resolvedValue === 'object' &&1570          resolvedValue !== null &&1571          resolvedValue.$$typeof === REACT_ELEMENT_TYPE1572        ) {1573          resolvedValue._store.validated = 1;1574        }1575      }, voidHandler);1576    }1577    // TODO: Once we accept Promises as children on the client, we can just return1578    // the thenable here.1579    return createLazyWrapperAroundWakeable(request, task, result);1580  }15811582  if (__DEV__) {1583    if ((result as any).$$typeof === REACT_ELEMENT_TYPE) {1584      // If the server component renders to an element, then it was in a static position.1585      // That doesn't need further validation of keys. The Server Component itself would1586      // have had a key.1587      (result as any)._store.validated = 1;1588    }1589  }15901591  // Normally we'd serialize an Iterator/AsyncIterator as a single-shot which is not compatible1592  // to be rendered as a React Child. However, because we have the function to recreate1593  // an iterable from rendering the element again, we can effectively treat it as multi-1594  // shot. Therefore we treat this as an Iterable/AsyncIterable, whether it was one or not, by1595  // adding a wrapper so that this component effectively renders down to an AsyncIterable.1596  const iteratorFn = getIteratorFn(result);1597  if (iteratorFn) {1598    const iterableChild = result;1599    const multiShot = {1600      [Symbol.iterator]: function () {1601        const iterator = iteratorFn.call(iterableChild);1602        if (__DEV__) {1603          // If this was an Iterator but not a GeneratorFunction we warn because1604          // it might have been a mistake. Technically you can make this mistake with1605          // GeneratorFunctions and even single-shot Iterables too but it's extra1606          // tempting to try to return the value from a generator.1607          if (iterator === iterableChild) {1608            const isGeneratorComponent =1609              // $FlowFixMe[method-unbinding]1610              Object.prototype.toString.call(Component) ===1611                '[object GeneratorFunction]' &&1612              // $FlowFixMe[method-unbinding]1613              Object.prototype.toString.call(iterableChild) ===1614                '[object Generator]';1615            if (!isGeneratorComponent) {1616              callWithDebugContextInDEV(request, task, () => {1617                console.error(1618                  'Returning an Iterator from a Server Component is not supported ' +1619                    'since it cannot be looped over more than once. ',1620                );1621              });1622            }1623          }1624        }1625        return iterator as any;1626      },1627    };1628    if (__DEV__) {1629      (multiShot as any)._debugInfo = iterableChild._debugInfo;1630    }1631    return multiShot;1632  }1633  if (1634    typeof (result as any)[ASYNC_ITERATOR] === 'function' &&1635    (typeof ReadableStream !== 'function' ||1636      !(result instanceof ReadableStream))1637  ) {1638    const iterableChild = result;1639    const multishot = {1640      [ASYNC_ITERATOR]: function () {1641        const iterator = (iterableChild as any)[ASYNC_ITERATOR]();1642        if (__DEV__) {1643          // If this was an AsyncIterator but not an AsyncGeneratorFunction we warn because1644          // it might have been a mistake. Technically you can make this mistake with1645          // AsyncGeneratorFunctions and even single-shot AsyncIterables too but it's extra1646          // tempting to try to return the value from a generator.1647          if (iterator === iterableChild) {1648            const isGeneratorComponent =1649              // $FlowFixMe[method-unbinding]1650              Object.prototype.toString.call(Component) ===1651                '[object AsyncGeneratorFunction]' &&1652              // $FlowFixMe[method-unbinding]1653              Object.prototype.toString.call(iterableChild) ===1654                '[object AsyncGenerator]';1655            if (!isGeneratorComponent) {1656              callWithDebugContextInDEV(request, task, () => {1657                console.error(1658                  'Returning an AsyncIterator from a Server Component is not supported ' +1659                    'since it cannot be looped over more than once. ',1660                );1661              });1662            }1663          }1664        }1665        return iterator;1666      },1667    };1668    if (__DEV__) {1669      (multishot as any)._debugInfo = iterableChild._debugInfo;1670    }1671    return multishot;1672  }1673  return result;1674}16751676function renderFunctionComponent<Props>(1677  request: Request,1678  task: Task,1679  key: ReactKey,1680  Component: (p: Props, arg: void) => any,1681  props: Props,1682  validated: number, // DEV-only1683): ReactJSONValue {1684  // Reset the task's thenable state before continuing, so that if a later1685  // component suspends we can reuse the same task object. If the same1686  // component suspends again, the thenable state will be restored.1687  const prevThenableState = task.thenableState;1688  task.thenableState = null;16891690  let result;16911692  let componentDebugInfo: ReactComponentInfo;1693  if (__DEV__) {1694    if (!canEmitDebugInfo) {1695      // We don't have a chunk to assign debug info. We need to outline this1696      // component to assign it an ID.1697      return outlineTask(request, task);1698    } else if (prevThenableState !== null) {1699      // This is a replay and we've already emitted the debug info of this component1700      // in the first pass. We skip emitting a duplicate line.1701      // As a hack we stashed the previous component debug info on this object in DEV.1702      componentDebugInfo = (prevThenableState as any)._componentDebugInfo;1703    } else {1704      // This is a new component in the same task so we can emit more debug info.1705      const componentDebugID = task.id;1706      const componentName =1707        (Component as any).displayName || Component.name || '';1708      const componentEnv = (0, request.environmentName)();1709      request.pendingChunks++;1710      componentDebugInfo = {1711        name: componentName,1712        env: componentEnv,1713        key: key,1714        owner: task.debugOwner,1715      } as ReactComponentInfo;1716      // $FlowFixMe[cannot-write]1717      componentDebugInfo.stack =1718        task.debugStack === null1719          ? null1720          : filterStackTrace(request, parseStackTrace(task.debugStack, 1));1721      // $FlowFixMe[cannot-write]1722      componentDebugInfo.props = props;1723      // $FlowFixMe[cannot-write]1724      componentDebugInfo.debugStack = task.debugStack;1725      // $FlowFixMe[cannot-write]1726      componentDebugInfo.debugTask = task.debugTask;17271728      // We outline this model eagerly so that we can refer to by reference as an owner.1729      // If we had a smarter way to dedupe we might not have to do this if there ends up1730      // being no references to this as an owner.17311732      outlineComponentInfo(request, componentDebugInfo);17331734      // Track when we started rendering this component.1735      if (1736        enableProfilerTimer &&1737        (enableComponentPerformanceTrack || enableAsyncDebugInfo)1738      ) {1739        advanceTaskTime(request, task, performance.now());1740      }17411742      emitDebugChunk(request, componentDebugID, componentDebugInfo);17431744      // We've emitted the latest environment for this task so we track that.1745      task.environmentName = componentEnv;17461747      if (validated === 2) {1748        warnForMissingKey(request, key, componentDebugInfo, task.debugTask);1749      }1750    }1751    prepareToUseHooksForComponent(prevThenableState, componentDebugInfo);1752    // $FlowFixMe[constant-condition]1753    if (supportsComponentStorage) {1754      // Run the component in an Async Context that tracks the current owner.1755      if (task.debugTask) {1756        result = task.debugTask.run(1757          // $FlowFixMe[method-unbinding]1758          componentStorage.run.bind(1759            componentStorage,1760            componentDebugInfo,1761            callComponentInDEV,1762            Component,1763            props,1764            componentDebugInfo,1765          ),1766        );1767      } else {1768        result = componentStorage.run(1769          componentDebugInfo,1770          callComponentInDEV,1771          Component,1772          props,1773          componentDebugInfo,1774        );1775      }1776    } else {1777      if (task.debugTask) {1778        result = task.debugTask.run(1779          callComponentInDEV.bind(null, Component, props, componentDebugInfo),1780        );1781      } else {1782        result = callComponentInDEV(Component, props, componentDebugInfo);1783      }1784    }1785  } else {1786    componentDebugInfo = null as any;1787    prepareToUseHooksForComponent(prevThenableState, null);1788    // The secondArg is always undefined in Server Components since refs error early.1789    const secondArg = undefined;1790    result = Component(props, secondArg);1791  }17921793  if (request.status === ABORTING) {1794    if (1795      typeof result === 'object' &&1796      // $FlowFixMe[invalid-compare]1797      result !== null &&1798      typeof result.then === 'function' &&1799      !isClientReference(result)1800    ) {1801      result.then(voidHandler, voidHandler);1802    }1803    // If we aborted during rendering we should interrupt the render but1804    // we don't need to provide an error because the renderer will encode1805    // the abort error as the reason.1806    // eslint-disable-next-line no-throw-literal1807    throw null;1808  }18091810  if (__DEV__ || (enableProfilerTimer && enableAsyncDebugInfo)) {1811    // Forward any debug information for any Promises that we use():ed during the render.1812    // We do this at the end so that we don't keep doing this for each retry.1813    const trackedThenables = getTrackedThenablesAfterRendering();1814    if (trackedThenables !== null) {1815      const stacks: Array<Error> =1816        __DEV__ && enableAsyncDebugInfo1817          ? (trackedThenables as any)._stacks ||1818            ((trackedThenables as any)._stacks = [])1819          : (null as any);1820      for (let i = 0; i < trackedThenables.length; i++) {1821        const stack = __DEV__ && enableAsyncDebugInfo ? stacks[i] : null;1822        forwardDebugInfoFromThenable(1823          request,1824          task,1825          trackedThenables[i],1826          __DEV__ ? componentDebugInfo : null,1827          stack,1828        );1829      }1830    }1831  }18321833  // Apply special cases.1834  result = processServerComponentReturnValue(request, task, Component, result);18351836  if (__DEV__) {1837    // From this point on, the parent is the component we just rendered until we1838    // hit another JSX element.1839    task.debugOwner = componentDebugInfo;1840    // Unfortunately, we don't have a stack frame for this position. Conceptually1841    // it would be the location of the `return` inside component that just rendered.1842    task.debugStack = null;1843    task.debugTask = null;1844  }18451846  // Track this element's key on the Server Component on the keyPath context..1847  const prevKeyPath = task.keyPath;1848  const prevImplicitSlot = task.implicitSlot;1849  if (key !== null) {1850    // Append the key to the path. Technically a null key should really add the child1851    // index. We don't do that to hold the payload small and implementation simple.1852    if (key === REACT_OPTIMISTIC_KEY || prevKeyPath === REACT_OPTIMISTIC_KEY) {1853      // The optimistic key is viral. It turns the whole key into optimistic if any part is.1854      task.keyPath = REACT_OPTIMISTIC_KEY;1855    } else {1856      task.keyPath = prevKeyPath === null ? key : prevKeyPath + ',' + key;1857    }1858  } else if (prevKeyPath === null) {1859    // This sequence of Server Components has no keys. This means that it was rendered1860    // in a slot that needs to assign an implicit key. Even if children below have1861    // explicit keys, they should not be used for the outer most key since it might1862    // collide with other slots in that set.1863    task.implicitSlot = true;1864  }1865  const json = renderModelDestructive(request, task, emptyRoot, '', result);1866  task.keyPath = prevKeyPath;1867  task.implicitSlot = prevImplicitSlot;1868  return json;1869}18701871function warnForMissingKey(1872  request: Request,1873  key: ReactKey,1874  componentDebugInfo: ReactComponentInfo,1875  debugTask: null | ConsoleTask,1876): void {1877  if (__DEV__) {1878    let didWarnForKey = request.didWarnForKey;1879    if (didWarnForKey == null) {1880      didWarnForKey = request.didWarnForKey = new WeakSet();1881    }1882    const parentOwner = componentDebugInfo.owner;1883    if (parentOwner != null) {1884      if (didWarnForKey.has(parentOwner)) {1885        // We already warned for other children in this parent.1886        return;1887      }1888      didWarnForKey.add(parentOwner);1889    }18901891    // Call with the server component as the currently rendering component1892    // for context.1893    const logKeyError = () => {1894      console.error(1895        'Each child in a list should have a unique "key" prop.' +1896          '%s%s See https://react.dev/link/warning-keys for more information.',1897        '',1898        '',1899      );1900    };19011902    // $FlowFixMe[constant-condition]1903    if (supportsComponentStorage) {1904      // Run the component in an Async Context that tracks the current owner.1905      if (debugTask) {1906        debugTask.run(1907          // $FlowFixMe[method-unbinding]1908          componentStorage.run.bind(1909            componentStorage,1910            componentDebugInfo,1911            callComponentInDEV,1912            logKeyError,1913            null,1914            componentDebugInfo,1915          ),1916        );1917      } else {1918        componentStorage.run(1919          componentDebugInfo,1920          callComponentInDEV,1921          logKeyError,1922          null,1923          componentDebugInfo,1924        );1925      }1926    } else {1927      if (debugTask) {1928        debugTask.run(1929          callComponentInDEV.bind(null, logKeyError, null, componentDebugInfo),1930        );1931      } else {1932        callComponentInDEV(logKeyError, null, componentDebugInfo);1933      }1934    }1935  }1936}19371938function renderFragment(1939  request: Request,1940  task: Task,1941  children: $ReadOnlyArray<ReactClientValue>,1942): ReactJSONValue {1943  if (__DEV__) {1944    for (let i = 0; i < children.length; i++) {1945      const child = children[i];1946      if (1947        child !== null &&1948        typeof child === 'object' &&1949        child.$$typeof === REACT_ELEMENT_TYPE1950      ) {1951        const element: ReactElement = child as any;1952        if (element.key === null && !element._store.validated) {1953          element._store.validated = 2;1954        }1955      }1956    }1957  }19581959  if (task.keyPath !== null) {1960    // We have a Server Component that specifies a key but we're now splitting1961    // the tree using a fragment.1962    const fragment = __DEV__1963      ? [1964          REACT_ELEMENT_TYPE,1965          REACT_FRAGMENT_TYPE,1966          task.keyPath,1967          {children},1968          null,1969          null,1970          0,1971        ]1972      : [REACT_ELEMENT_TYPE, REACT_FRAGMENT_TYPE, task.keyPath, {children}];1973    if (!task.implicitSlot) {1974      // If this was keyed inside a set. I.e. the outer Server Component was keyed1975      // then we need to handle reorders of the whole set. To do this we need to wrap1976      // this array in a keyed Fragment.1977      return fragment;1978    }1979    // If the outer Server Component was implicit but then an inner one had a key1980    // we don't actually need to be able to move the whole set around. It'll always be1981    // in an implicit slot. The key only exists to be able to reset the state of the1982    // children. We could achieve the same effect by passing on the keyPath to the next1983    // set of components inside the fragment. This would also allow a keyless fragment1984    // reconcile against a single child.1985    // Unfortunately because of JSON.stringify, we can't call the recursive loop for1986    // each child within this context because we can't return a set with already resolved1987    // values. E.g. a string would get double encoded. Returning would pop the context.1988    // So instead, we wrap it with an unkeyed fragment and inner keyed fragment.1989    return [fragment];1990  }1991  // Since we're yielding here, that implicitly resets the keyPath context on the1992  // way up. Which is what we want since we've consumed it. If this changes to1993  // be recursive serialization, we need to reset the keyPath and implicitSlot,1994  // before recursing here.1995  if (__DEV__) {1996    const debugInfo: ?ReactDebugInfo = (children as any)._debugInfo;1997    if (debugInfo) {1998      // If this came from Flight, forward any debug info into this new row.1999      if (!canEmitDebugInfo) {2000        // We don't have a chunk to assign debug info. We need to outline this

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.