packages/react-server/src/ReactFlightServer.js JAVASCRIPT 6,820 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 6,820.
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  enableFlightWeakThenables,20} from 'shared/ReactFeatureFlags';2122import {23  scheduleWork,24  scheduleMicrotask,25  flushBuffered,26  beginWriting,27  writeChunk,28  writeChunkAndReturn,29  stringToChunk,30  typedArrayToBinaryChunk,31  byteLengthOfChunk,32  byteLengthOfBinaryChunk,33  completeWriting,34  close,35  closeWithError,36} from './ReactServerStreamConfig';3738export type {Destination, Chunk} from './ReactServerStreamConfig';3940import type {41  ClientManifest,42  ClientReferenceMetadata,43  ClientReference,44  ClientReferenceKey,45  ServerReference,46  ServerReferenceId,47  Hints,48  HintCode,49  HintModel,50  FormatContext,51} from './ReactFlightServerConfig';52import type {ThenableState} from './ReactFlightThenable';53import type {54  Wakeable,55  Thenable,56  PendingThenable,57  FulfilledThenable,58  RejectedThenable,59  ReactDebugInfo,60  ReactDebugInfoEntry,61  ReactComponentInfo,62  ReactIOInfo,63  ReactAsyncInfo,64  ReactStackTrace,65  ReactCallSite,66  ReactFunctionLocation,67  ReactErrorInfo,68  ReactErrorInfoDev,69  ReactKey,70} from 'shared/ReactTypes';71import type {ReactElement} from 'shared/ReactElementType';72import type {LazyComponent} from 'react/src/ReactLazy';73import type {74  AsyncSequence,75  IONode,76  PromiseNode,77  UnresolvedPromiseNode,78} from './ReactFlightAsyncSequence';7980import {81  resolveClientReferenceMetadata,82  getServerReferenceId,83  getServerReferenceBoundArguments,84  getServerReferenceLocation,85  getClientReferenceKey,86  isClientReference,87  isServerReference,88  supportsRequestStorage,89  requestStorage,90  createHints,91  createRootFormatContext,92  getChildFormatContext,93  initAsyncDebugInfo,94  markAsyncSequenceRootTask,95  getCurrentAsyncSequence,96  getAsyncSequenceFromPromise,97  parseStackTrace,98  parseStackTracePrivate,99  supportsComponentStorage,100  componentStorage,101  unbadgeConsole,102} from './ReactFlightServerConfig';103104import {105  resolveTemporaryReference,106  isOpaqueTemporaryReference,107} from './ReactFlightServerTemporaryReferences';108109import {110  HooksDispatcher,111  prepareToUseHooksForRequest,112  prepareToUseHooksForComponent,113  getThenableStateAfterSuspending,114  getTrackedThenablesAfterRendering,115  resetHooksForRequest,116} from './ReactFlightHooks';117import {DefaultAsyncDispatcher} from './flight/ReactFlightAsyncDispatcher';118119import {resolveOwner, setCurrentOwner} from './flight/ReactFlightCurrentOwner';120121import {getOwnerStackByComponentInfoInDev} from 'shared/ReactComponentInfoStack';122import {resetOwnerStackLimit} from 'shared/ReactOwnerStackReset';123124import noop from 'shared/noop';125126import {127  callComponentInDEV,128  callLazyInitInDEV,129  callIteratorInDEV,130} from './ReactFlightCallUserSpace';131132import {133  getIteratorFn,134  REACT_ELEMENT_TYPE,135  REACT_LEGACY_ELEMENT_TYPE,136  REACT_FORWARD_REF_TYPE,137  REACT_FRAGMENT_TYPE,138  REACT_LAZY_TYPE,139  REACT_MEMO_TYPE,140  ASYNC_ITERATOR,141  REACT_OPTIMISTIC_KEY,142} from 'shared/ReactSymbols';143144import {145  describeObjectForErrorMessage,146  isGetter,147  isSimpleObject,148  jsxPropsParents,149  jsxChildrenParents,150  objectName,151} from 'shared/ReactSerializationErrors';152153import ReactSharedInternals from './ReactSharedInternalsServer';154import isArray from 'shared/isArray';155import getPrototypeOf from 'shared/getPrototypeOf';156import hasOwnProperty from 'shared/hasOwnProperty';157import binaryToComparableString from 'shared/binaryToComparableString';158159import {SuspenseException, getSuspendedThenable} from './ReactFlightThenable';160161import {162  IO_NODE,163  PROMISE_NODE,164  AWAIT_NODE,165  UNRESOLVED_AWAIT_NODE,166  UNRESOLVED_PROMISE_NODE,167} from './ReactFlightAsyncSequence';168169// DEV-only set containing internal objects that should not be limited and turned into getters.170const doNotLimit: WeakSet<Reference> = __DEV__ ? new WeakSet() : (null as any);171172function defaultFilterStackFrame(173  filename: string,174  functionName: string,175): boolean {176  return (177    filename !== '' &&178    !filename.startsWith('node:') &&179    !filename.includes('node_modules')180  );181}182183function devirtualizeURL(url: string): string {184  if (url.startsWith('about://React/')) {185    // This callsite is a virtual fake callsite that came from another Flight client.186    // We need to reverse it back into the original location by stripping its prefix187    // and suffix. We don't need the environment name because it's available on the188    // parent object that will contain the stack.189    const envIdx = url.indexOf('/', 'about://React/'.length);190    const suffixIdx = url.lastIndexOf('?');191    if (envIdx > -1 && suffixIdx > -1) {192      return decodeURI(url.slice(envIdx + 1, suffixIdx));193    }194  }195  return url;196}197198function isPromiseCreationInternal(url: string, functionName: string): boolean {199  // Various internals of the JS VM can create Promises but the call frame of the200  // internals are not very interesting for our purposes so we need to skip those.201  if (url === 'node:internal/async_hooks') {202    // Ignore the stack frames from the async hooks themselves.203    return true;204  }205  if (url !== '') {206    return false;207  }208  // V8 used to name the frames of static methods on the Promise constructor209  // "Function.x" but newer versions name them "Promise.x". We match both.210  switch (functionName) {211    case 'new Promise':212    case 'Function.withResolvers':213    case 'Promise.withResolvers':214    case 'Function.reject':215    case 'Promise.reject':216    case 'Function.resolve':217    case 'Promise.resolve':218    case 'Function.all':219    case 'Promise.all':220    case 'Function.allSettled':221    case 'Promise.allSettled':222    case 'Function.race':223    case 'Promise.race':224    case 'Function.try':225    case 'Promise.try':226      return true;227    default:228      return false;229  }230}231232function stripLeadingPromiseCreationFrames(233  stack: ReactStackTrace,234): ReactStackTrace {235  for (let i = 0; i < stack.length; i++) {236    const callsite = stack[i];237    const functionName = callsite[0];238    const url = callsite[1];239    if (!isPromiseCreationInternal(url, functionName)) {240      if (i > 0) {241        return stack.slice(i);242      } else {243        return stack;244      }245    }246  }247  return [];248}249250function findCalledFunctionNameFromStackTrace(251  request: Request,252  stack: ReactStackTrace,253): string {254  // Gets the name of the first function called from first party code.255  let bestMatch = '';256  const filterStackFrame = request.filterStackFrame;257  for (let i = 0; i < stack.length; i++) {258    const callsite = stack[i];259    const functionName = callsite[0];260    const url = devirtualizeURL(callsite[1]);261    const lineNumber = callsite[2];262    const columnNumber = callsite[3];263    if (264      filterStackFrame(url, functionName, lineNumber, columnNumber) &&265      // Don't consider anonymous code first party even if the filter wants to include them in the stack.266      url !== ''267    ) {268      if (bestMatch === '') {269        // If we had no good stack frames for internal calls, just use the last270        // first party function name.271        return functionName;272      }273      return bestMatch;274    } else {275      bestMatch = functionName;276    }277  }278  return '';279}280281function filterStackTrace(282  request: Request,283  stack: ReactStackTrace,284): ReactStackTrace {285  // Since stacks can be quite large and we pass a lot of them, we filter them out eagerly286  // to save bandwidth even in DEV. We'll also replay these stacks on the client so by287  // stripping them early we avoid that overhead. Otherwise we'd normally just rely on288  // the DevTools or framework's ignore lists to filter them out.289  const filterStackFrame = request.filterStackFrame;290  const filteredStack: ReactStackTrace = [];291  for (let i = 0; i < stack.length; i++) {292    const callsite = stack[i];293    const functionName = callsite[0];294    const url = devirtualizeURL(callsite[1]);295    const lineNumber = callsite[2];296    const columnNumber = callsite[3];297    if (filterStackFrame(url, functionName, lineNumber, columnNumber)) {298      // Use a clone because the Flight protocol isn't yet resilient to deduping299      // objects in the debug info. TODO: Support deduping stacks.300      const clone: ReactCallSite = callsite.slice(0) as any;301      clone[1] = url;302      filteredStack.push(clone);303    }304  }305  return filteredStack;306}307308function hasUnfilteredFrame(request: Request, stack: ReactStackTrace): boolean {309  const filterStackFrame = request.filterStackFrame;310  for (let i = 0; i < stack.length; i++) {311    const callsite = stack[i];312    const functionName = callsite[0];313    const url = devirtualizeURL(callsite[1]);314    const lineNumber = callsite[2];315    const columnNumber = callsite[3];316    // Ignore async stack frames because they're not "real". We'd expect to have at least317    // one non-async frame if we're actually executing inside a first party function.318    // Otherwise we might just be in the resume of a third party function that resumed319    // inside a first party stack.320    const isAsync = callsite[6];321    if (322      !isAsync &&323      filterStackFrame(url, functionName, lineNumber, columnNumber) &&324      // Ignore anonymous stack frames like internals. They are also not in first party325      // code even though it might be useful to include them in the final stack.326      url !== ''327    ) {328      return true;329    }330  }331  return false;332}333334function isPromiseAwaitInternal(url: string, functionName: string): boolean {335  // Various internals of the JS VM can await internally on a Promise. If those are at336  // the top of the stack then we don't want to consider them as internal frames. The337  // true "await" conceptually is the thing that called the helper.338  // Ideally we'd also include common third party helpers for this.339  if (url === 'node:internal/async_hooks') {340    // Ignore the stack frames from the async hooks themselves.341    return true;342  }343  if (url !== '') {344    return false;345  }346  // V8 used to name the frames of static methods on the Promise constructor347  // "Function.x" but newer versions name them "Promise.x". We match both.348  switch (functionName) {349    case 'Promise.then':350    case 'Promise.catch':351    case 'Promise.finally':352    case 'Function.reject':353    case 'Promise.reject':354    case 'Function.resolve':355    case 'Promise.resolve':356    case 'Function.all':357    case 'Promise.all':358    case 'Function.allSettled':359    case 'Promise.allSettled':360    case 'Function.any':361    case 'Promise.any':362    case 'Function.race':363    case 'Promise.race':364    case 'Function.try':365    case 'Promise.try':366    case 'Function.withResolvers':367    case 'Promise.withResolvers':368      return true;369    default:370      return false;371  }372}373374export function isAwaitInUserspace(375  request: Request,376  stack: ReactStackTrace,377): boolean {378  let firstFrame = 0;379  while (380    stack.length > firstFrame &&381    isPromiseAwaitInternal(stack[firstFrame][1], stack[firstFrame][0])382  ) {383    // Skip the internal frame that awaits itself.384    firstFrame++;385  }386  if (stack.length > firstFrame) {387    // Check if the very first stack frame that awaited this Promise was in user space.388    // TODO: This doesn't take into account wrapper functions such as our fake .then()389    // in FlightClient which will always be considered third party awaits if you call390    // .then directly.391    const filterStackFrame = request.filterStackFrame;392    const callsite = stack[firstFrame];393    const functionName = callsite[0];394    const url = devirtualizeURL(callsite[1]);395    const lineNumber = callsite[2];396    const columnNumber = callsite[3];397    return (398      filterStackFrame(url, functionName, lineNumber, columnNumber) &&399      url !== ''400    );401  }402  return false;403}404405initAsyncDebugInfo();406407function patchConsole(consoleInst: typeof console, methodName: string) {408  const descriptor = Object.getOwnPropertyDescriptor(consoleInst, methodName);409  if (410    descriptor &&411    (descriptor.configurable || descriptor.writable) &&412    typeof descriptor.value === 'function'413  ) {414    const originalMethod = descriptor.value;415    const originalName = Object.getOwnPropertyDescriptor(416      // $FlowFixMe[incompatible-type]: We should be able to get descriptors from any function.417      originalMethod,418      'name',419    );420    const wrapperMethod = function (this: typeof console) {421      const request = resolveRequest();422      if (methodName === 'assert' && arguments[0]) {423        // assert doesn't emit anything unless first argument is falsy so we can skip it.424      } else if (request !== null) {425        // Extract the stack. Not all console logs print the full stack but they have at426        // least the line it was called from. We could optimize transfer by keeping just427        // one stack frame but keeping it simple for now and include all frames.428        const stack = filterStackTrace(429          request,430          parseStackTracePrivate(new Error('react-stack-top-frame'), 1) || [],431        );432        request.pendingDebugChunks++;433        const owner: null | ReactComponentInfo = resolveOwner();434        const args = Array.from(arguments);435        // Extract the env if this is a console log that was replayed from another env.436        let env = unbadgeConsole(methodName, args);437        if (env === null) {438          // Otherwise add the current environment.439          env = (0, request.environmentName)();440        }441442        emitConsoleChunk(request, methodName, owner, env, stack, args);443      }444      // $FlowFixMe[incompatible-call]445      // $FlowFixMe[incompatible-type]446      return originalMethod.apply(this, arguments);447    };448    if (originalName) {449      Object.defineProperty(450        wrapperMethod,451        // $FlowFixMe[cannot-write] yes it is452        'name',453        originalName,454      );455    }456    Object.defineProperty(consoleInst, methodName, {457      value: wrapperMethod,458    });459  }460}461462// $FlowFixMe[invalid-compare]463if (__DEV__ && typeof console === 'object' && console !== null) {464  // Instrument console to capture logs for replaying on the client.465  patchConsole(console, 'assert');466  patchConsole(console, 'debug');467  patchConsole(console, 'dir');468  patchConsole(console, 'dirxml');469  patchConsole(console, 'error');470  patchConsole(console, 'group');471  patchConsole(console, 'groupCollapsed');472  patchConsole(console, 'groupEnd');473  patchConsole(console, 'info');474  patchConsole(console, 'log');475  patchConsole(console, 'table');476  patchConsole(console, 'trace');477  patchConsole(console, 'warn');478}479480function getCurrentStackInDEV(): string {481  if (__DEV__) {482    const owner: null | ReactComponentInfo = resolveOwner();483    if (owner === null) {484      return '';485    }486    return getOwnerStackByComponentInfoInDev(owner);487  }488  return '';489}490491const ObjectPrototype = Object.prototype;492493const stringify = JSON.stringify;494495type ReactJSONValue =496  | string497  | boolean498  | number499  | null500  | $ReadOnlyArray<ReactClientValue>501  | ReactClientObject;502503// Serializable values504export type ReactClientValue =505  // Server Elements and Lazy Components are unwrapped on the Server506  | React$Element<component(...props: any)>507  | LazyComponent<ReactClientValue, any>508  // References are passed by their value509  | ClientReference<any>510  | ServerReference<any>511  // The rest are passed as is. Sub-types can be passed in but lose their512  // subtype, so the receiver can only accept once of these.513  | React$Element<string>514  | React$Element<ClientReference<any> & any>515  | ReactComponentInfo516  | ReactErrorInfo517  | string518  | boolean519  | number520  | symbol521  | null522  | void523  | bigint524  | ReadableStream525  | $AsyncIterable<ReactClientValue, ReactClientValue, void>526  | $AsyncIterator<ReactClientValue, ReactClientValue, void>527  | Iterable<ReactClientValue>528  | Iterator<ReactClientValue>529  | Array<ReactClientValue>530  | Map<ReactClientValue, ReactClientValue>531  | Set<ReactClientValue>532  | FormData533  | $ArrayBufferView534  | ArrayBuffer535  | Date536  | ReactClientObject537  | Promise<ReactClientValue>; // Thenable<ReactClientValue>538539type ReactClientObject = {+[key: string]: ReactClientValue};540541// task status542const PENDING = 0;543const COMPLETED = 1;544const ABORTED = 3;545const ERRORED = 4;546const RENDERING = 5;547548type Task = {549  id: number,550  status: 0 | 1 | 3 | 4 | 5,551  model: ReactClientValue,552  ping: () => void,553  keyPath: ReactKey, // parent server component keys554  implicitSlot: boolean, // true if the root server component of this sequence had a null key555  formatContext: FormatContext, // an approximate parent context from host components556  thenableState: ThenableState | null,557  timed: boolean, // Profiling-only. Whether we need to track the completion time of this task.558  time: number, // Profiling-only. The last time stamp emitted for this task.559  environmentName: string, // DEV-only. Used to track if the environment for this task changed.560  debugOwner: null | ReactComponentInfo, // DEV-only561  debugStack: null | Error, // DEV-only562  debugTask: null | ConsoleTask, // DEV-only563};564565interface Reference {}566567type ReactClientReference = Reference & ReactClientValue;568569type DeferredDebugStore = {570  retained: Map<number, ReactClientReference | string>,571  existing: Map<ReactClientReference | string, number>,572};573574const __PROTO__ = '__proto__';575576const OPENING = 10;577const OPEN = 11;578const ABORTING = 12;579const CLOSING = 13;580const CLOSED = 14;581582const RENDER = 20;583const PRERENDER = 21;584585// Marker pushed before a [headerChunk, contentChunk] pair in586// completedRegularChunks / completedDebugChunks to signal that the next two587// entries must be written atomically — see emitTextChunk and588// emitTypedArrayChunk for why, and flushCompletedChunks for how it's read.589const NEXT_TWO_CHUNKS_ARE_ATOMIC: symbol = Symbol();590591export type Request = {592  status: 10 | 11 | 12 | 13 | 14,593  type: 20 | 21,594  flushScheduled: boolean,595  fatalError: mixed,596  destination: null | Destination,597  bundlerConfig: ClientManifest,598  cache: Map<Function, mixed>,599  cacheController: AbortController,600  nextChunkId: number,601  pendingChunks: number,602  hints: Hints,603  abortableTasks: Set<Task>,604  pingedTasks: Array<Task>,605  completedImportChunks: Array<Chunk>,606  completedHintChunks: Array<Chunk>,607  // Text and TypedArray rows are pushed as a NEXT_TWO_CHUNKS_ARE_ATOMIC608  // sentinel followed by their [headerChunk, contentChunk] pair, so that609  // flushCompletedChunks can write the pair atomically and never strand the610  // content chunk on a backpressure break.611  completedRegularChunks: Array<612    Chunk | BinaryChunk | typeof NEXT_TWO_CHUNKS_ARE_ATOMIC,613  >,614  completedErrorChunks: Array<Chunk>,615  writtenSymbols: Map<symbol, number>,616  writtenClientReferences: Map<ClientReferenceKey, number>,617  writtenServerReferences: Map<ServerReference<any>, number>,618  writtenObjects: WeakMap<Reference, string>,619  temporaryReferences: void | TemporaryReferenceSet,620  identifierPrefix: string,621  identifierCount: number,622  taintCleanupQueue: Array<string | bigint>,623  onError: (error: mixed) => ?string,624  onAllReady: () => void,625  onFatalError: mixed => void,626  // Profiling-only627  timeOrigin: number,628  abortTime: number,629  // DEV-only630  pendingDebugChunks: number,631  // See completedRegularChunks for why some entries are preceded by the632  // NEXT_TWO_CHUNKS_ARE_ATOMIC sentinel.633  completedDebugChunks: Array<634    Chunk | BinaryChunk | typeof NEXT_TWO_CHUNKS_ARE_ATOMIC,635  >,636  debugDestination: null | Destination,637  environmentName: () => string,638  filterStackFrame: (639    url: string,640    functionName: string,641    lineNumber: number,642    columnNumber: number,643  ) => boolean,644  didWarnForKey: null | WeakSet<ReactComponentInfo>,645  writtenDebugObjects: WeakMap<Reference, string>,646  deferredDebugObjects: null | DeferredDebugStore,647};648649const {650  TaintRegistryObjects,651  TaintRegistryValues,652  TaintRegistryByteLengths,653  TaintRegistryPendingRequests,654} = ReactSharedInternals;655656function throwTaintViolation(message: string) {657  // eslint-disable-next-line react-internal/prod-error-codes658  throw new Error(message);659}660661function cleanupTaintQueue(request: Request): void {662  const cleanupQueue = request.taintCleanupQueue;663  TaintRegistryPendingRequests.delete(cleanupQueue);664  for (let i = 0; i < cleanupQueue.length; i++) {665    const entryValue = cleanupQueue[i];666    const entry = TaintRegistryValues.get(entryValue);667    if (entry !== undefined) {668      if (entry.count === 1) {669        TaintRegistryValues.delete(entryValue);670      } else {671        entry.count--;672      }673    }674  }675  cleanupQueue.length = 0;676}677678function defaultErrorHandler(error: mixed) {679  console['error'](error);680  // Don't transform to our wrapper681}682683function RequestInstance(684  this: $FlowFixMe,685  type: 20 | 21,686  model: ReactClientValue,687  bundlerConfig: ClientManifest,688  onError: void | ((error: mixed) => ?string),689  onAllReady: () => void,690  onFatalError: (error: mixed) => void,691  identifierPrefix?: string,692  temporaryReferences: void | TemporaryReferenceSet,693  debugStartTime: void | number, // Profiling-only694  environmentName: void | string | (() => string), // DEV-only695  filterStackFrame: void | ((url: string, functionName: string) => boolean), // DEV-only696  keepDebugAlive: boolean, // DEV-only697) {698  if (699    ReactSharedInternals.A !== null &&700    ReactSharedInternals.A !== DefaultAsyncDispatcher701  ) {702    throw new Error(703      'Currently React only supports one RSC renderer at a time.',704    );705  }706  ReactSharedInternals.A = DefaultAsyncDispatcher;707  if (__DEV__) {708    // Unlike Fizz or Fiber, we don't reset this and just keep it on permanently.709    // This lets it act more like the AsyncDispatcher so that we can get the710    // stack asynchronously too.711    ReactSharedInternals.getCurrentStack = getCurrentStackInDEV;712  }713714  const abortSet: Set<Task> = new Set();715  const pingedTasks: Array<Task> = [];716  const cleanupQueue: Array<string | bigint> = [];717  if (enableTaint) {718    TaintRegistryPendingRequests.add(cleanupQueue);719  }720  const hints = createHints();721  this.type = type;722  this.status = OPENING;723  this.flushScheduled = false;724  this.fatalError = null;725  this.destination = null;726  this.bundlerConfig = bundlerConfig;727  this.cache = new Map();728  this.cacheController = new AbortController();729  this.nextChunkId = 0;730  this.pendingChunks = 0;731  this.hints = hints;732  this.abortableTasks = abortSet;733  this.pingedTasks = pingedTasks;734  this.completedImportChunks = [] as Array<Chunk>;735  this.completedHintChunks = [] as Array<Chunk>;736  this.completedRegularChunks = [] as Array<737    Chunk | BinaryChunk | typeof NEXT_TWO_CHUNKS_ARE_ATOMIC,738  >;739  this.completedErrorChunks = [] as Array<Chunk>;740  this.writtenSymbols = new Map();741  this.writtenClientReferences = new Map();742  this.writtenServerReferences = new Map();743  this.writtenObjects = new WeakMap();744  this.temporaryReferences = temporaryReferences;745  this.identifierPrefix = identifierPrefix || '';746  this.identifierCount = 1;747  this.taintCleanupQueue = cleanupQueue;748  this.onError = onError === undefined ? defaultErrorHandler : onError;749  this.onAllReady = onAllReady;750  this.onFatalError = onFatalError;751752  if (__DEV__) {753    this.pendingDebugChunks = 0;754    this.completedDebugChunks = [] as Array<755      Chunk | BinaryChunk | typeof NEXT_TWO_CHUNKS_ARE_ATOMIC,756    >;757    this.debugDestination = null;758    this.environmentName =759      environmentName === undefined760        ? () => 'Server'761        : typeof environmentName !== 'function'762          ? () => environmentName763          : environmentName;764    this.filterStackFrame =765      filterStackFrame === undefined766        ? defaultFilterStackFrame767        : filterStackFrame;768    this.didWarnForKey = null;769    this.writtenDebugObjects = new WeakMap();770    this.deferredDebugObjects = keepDebugAlive771      ? {772          retained: new Map(),773          existing: new Map(),774        }775      : null;776  }777778  let timeOrigin: number;779  if (780    enableProfilerTimer &&781    (enableComponentPerformanceTrack || enableAsyncDebugInfo)782  ) {783    // We start by serializing the time origin. Any future timestamps will be784    // emitted relatively to this origin. Instead of using performance.timeOrigin785    // as this origin, we use the timestamp at the start of the request.786    // This avoids leaking unnecessary information like how long the server has787    // been running and allows for more compact representation of each timestamp.788    // The time origin is stored as an offset in the time space of this environment.789    if (typeof debugStartTime === 'number') {790      // We expect `startTime` to be an absolute timestamp, so relativize it to match the other case.791      timeOrigin = this.timeOrigin =792        debugStartTime -793        // $FlowFixMe[prop-missing]794        performance.timeOrigin;795    } else {796      timeOrigin = this.timeOrigin = performance.now();797    }798    emitTimeOriginChunk(799      this,800      timeOrigin +801        // $FlowFixMe[prop-missing]802        performance.timeOrigin,803    );804    this.abortTime = -0.0;805  } else {806    timeOrigin = 0;807  }808809  const rootTask = createTask(810    this,811    model,812    null,813    false,814    createRootFormatContext(),815    abortSet,816    timeOrigin,817    null,818    null,819    null,820  );821  pingedTasks.push(rootTask);822}823824export function createRequest(825  model: ReactClientValue,826  bundlerConfig: ClientManifest,827  onError: void | ((error: mixed) => ?string),828  identifierPrefix: void | string,829  temporaryReferences: void | TemporaryReferenceSet,830  debugStartTime: void | number, // Profiling-only831  environmentName: void | string | (() => string), // DEV-only832  filterStackFrame: void | ((url: string, functionName: string) => boolean), // DEV-only833  keepDebugAlive: boolean, // DEV-only834): Request {835  if (__DEV__) {836    resetOwnerStackLimit();837  }838839  // $FlowFixMe[invalid-constructor]: the shapes are exact here but Flow doesn't like constructors840  return new RequestInstance(841    RENDER,842    model,843    bundlerConfig,844    onError,845    noop,846    noop,847    identifierPrefix,848    temporaryReferences,849    debugStartTime,850    environmentName,851    filterStackFrame,852    keepDebugAlive,853  );854}855856export function createPrerenderRequest(857  model: ReactClientValue,858  bundlerConfig: ClientManifest,859  onAllReady: () => void,860  onFatalError: () => void,861  onError: void | ((error: mixed) => ?string),862  identifierPrefix: void | string,863  temporaryReferences: void | TemporaryReferenceSet,864  debugStartTime: void | number, // Profiling-only865  environmentName: void | string | (() => string), // DEV-only866  filterStackFrame: void | ((url: string, functionName: string) => boolean), // DEV-only867  keepDebugAlive: boolean, // DEV-only868): Request {869  if (__DEV__) {870    resetOwnerStackLimit();871  }872873  // $FlowFixMe[invalid-constructor]: the shapes are exact here but Flow doesn't like constructors874  return new RequestInstance(875    PRERENDER,876    model,877    bundlerConfig,878    onError,879    onAllReady,880    onFatalError,881    identifierPrefix,882    temporaryReferences,883    debugStartTime,884    environmentName,885    filterStackFrame,886    keepDebugAlive,887  );888}889890let currentRequest: null | Request = null;891892export function resolveRequest(): null | Request {893  if (currentRequest) return currentRequest;894  // $FlowFixMe[constant-condition]895  if (supportsRequestStorage) {896    const store = requestStorage.getStore();897    if (store) return store;898  }899  return null;900}901902function isTypedArray(value: any): boolean {903  if (value instanceof ArrayBuffer) {904    return true;905  }906  if (value instanceof Int8Array) {907    return true;908  }909  if (value instanceof Uint8Array) {910    return true;911  }912  if (value instanceof Uint8ClampedArray) {913    return true;914  }915  if (value instanceof Int16Array) {916    return true;917  }918  if (value instanceof Uint16Array) {919    return true;920  }921  if (value instanceof Int32Array) {922    return true;923  }924  if (value instanceof Uint32Array) {925    return true;926  }927  if (value instanceof Float32Array) {928    return true;929  }930  if (value instanceof Float64Array) {931    return true;932  }933  if (value instanceof BigInt64Array) {934    return true;935  }936  if (value instanceof BigUint64Array) {937    return true;938  }939  if (value instanceof DataView) {940    return true;941  }942  return false;943}944945function serializeDebugThenable(946  request: Request,947  counter: {objectLimit: number},948  thenable: Thenable<any>,949): string {950  // Like serializeThenable but for renderDebugModel951  request.pendingDebugChunks++;952  const id = request.nextChunkId++;953  const ref = serializePromiseID(id);954  request.writtenDebugObjects.set(thenable, ref);955956  switch (thenable.status) {957    case 'fulfilled': {958      emitOutlinedDebugModelChunk(request, id, counter, thenable.value);959      return ref;960    }961    case 'rejected': {962      const x = thenable.reason;963      // We don't log these errors since they didn't actually throw into Flight.964      const digest = '';965      emitErrorChunk(request, id, digest, x, true, null);966      return ref;967    }968  }969970  if (request.status === ABORTING) {971    // Ensure that we have time to emit the halt chunk if we're sync aborting.972    emitDebugHaltChunk(request, id);973    return ref;974  }975976  const deferredDebugObjects = request.deferredDebugObjects;977  if (deferredDebugObjects !== null) {978    // For Promises that are not yet resolved, we always defer them. They are async anyway so it's979    // safe to defer them. This also ensures that we don't eagerly call .then() on a Promise that980    // otherwise wouldn't have initialized. It also ensures that we don't "handle" a rejection981    // that otherwise would have triggered unhandled rejection.982    deferredDebugObjects.retained.set(id, thenable as any);983    const deferredRef = '$Y@' + id.toString(16);984    // We can now refer to the deferred object in the future.985    request.writtenDebugObjects.set(thenable, deferredRef);986    return deferredRef;987  }988989  let cancelled = false;990991  thenable.then(992    value => {993      if (cancelled) {994        return;995      }996      cancelled = true;997      if (request.status === ABORTING) {998        emitDebugHaltChunk(request, id);999        enqueueFlush(request);1000        return;1001      }1002      if (1003        (isArray(value) && value.length > 200) ||1004        (isTypedArray(value) && value.byteLength > 1000)1005      ) {1006        // If this should be deferred, but we don't have a debug channel installed1007        // it would get omitted. We can't omit outlined models but we can avoid1008        // resolving the Promise at all by halting it.1009        emitDebugHaltChunk(request, id);1010        enqueueFlush(request);1011        return;1012      }1013      emitOutlinedDebugModelChunk(request, id, counter, value);1014      enqueueFlush(request);1015    },1016    reason => {1017      if (cancelled) {1018        return;1019      }1020      cancelled = true;1021      if (request.status === ABORTING) {1022        emitDebugHaltChunk(request, id);1023        enqueueFlush(request);1024        return;1025      }1026      // We don't log these errors since they didn't actually throw into Flight.1027      const digest = '';1028      emitErrorChunk(request, id, digest, reason, true, null);1029      enqueueFlush(request);1030    },1031  );10321033  // We don't use scheduleMicrotask here because it doesn't actually schedule a microtask1034  // in all our configs which is annoying.1035  Promise.resolve().then(() => {1036    // If we don't resolve the Promise within a microtask. Leave it as hanging since we1037    // don't want to block the render forever on a Promise that might never resolve.1038    if (cancelled) {1039      return;1040    }1041    cancelled = true;1042    emitDebugHaltChunk(request, id);1043    enqueueFlush(request);1044    // Clean up the request so we don't leak this forever.1045    request = null as any;1046    counter = null as any;1047  });10481049  return ref;1050}10511052function emitRequestedDebugThenable(1053  request: Request,1054  id: number,1055  counter: {objectLimit: number},1056  thenable: Thenable<any>,1057): void {1058  thenable.then(1059    value => {1060      if (request.status === ABORTING) {1061        emitDebugHaltChunk(request, id);1062        enqueueFlush(request);1063        return;1064      }1065      emitOutlinedDebugModelChunk(request, id, counter, value);1066      enqueueFlush(request);1067    },1068    reason => {1069      if (request.status === ABORTING) {1070        emitDebugHaltChunk(request, id);1071        enqueueFlush(request);1072        return;1073      }1074      // We don't log these errors since they didn't actually throw into Flight.1075      const digest = '';1076      emitErrorChunk(request, id, digest, reason, true, null);1077      enqueueFlush(request);1078    },1079  );1080}10811082function createThenableTask(1083  request: Request,1084  task: Task,1085  thenable: Thenable<any>,1086): Task {1087  return createTask(1088    request,1089    thenable as any, // will be replaced by the value before we retry. used for debug info.1090    task.keyPath, // the server component sequence continues through Promise-as-a-child.1091    task.implicitSlot,1092    task.formatContext,1093    request.abortableTasks,1094    enableProfilerTimer &&1095      (enableComponentPerformanceTrack || enableAsyncDebugInfo)1096      ? task.time1097      : 0,1098    __DEV__ ? task.debugOwner : null,1099    __DEV__ ? task.debugStack : null,1100    __DEV__ ? task.debugTask : null,1101  );1102}11031104function serializeThenable(1105  request: Request,1106  task: Task,1107  thenable: Thenable<any>,1108): number {1109  switch (thenable.status) {1110    case 'fulfilled': {1111      const newTask = createThenableTask(request, task, thenable);1112      forwardDebugInfoFromThenable(request, newTask, thenable, null, null);1113      // We have the resolved value, we can go ahead and schedule it for serialization.1114      newTask.model = thenable.value;1115      pingTask(request, newTask);1116      return newTask.id;1117    }1118    case 'rejected': {1119      const newTask = createThenableTask(request, task, thenable);1120      forwardDebugInfoFromThenable(request, newTask, thenable, null, null);1121      const x = thenable.reason;1122      erroredTask(request, newTask, x);1123      return newTask.id;1124    }1125    case 'pending_weak': {1126      if (enableFlightWeakThenables) {1127        // A weak-pending thenable doesn't block the stream from closing, so1128        // we don't create a task for it yet. We only reserve an id for its1129        // reference. If it settles while the stream is still open, we1130        // create the task at that point, the same as if we had serialized1131        // an already settled thenable.1132        //1133        // Delivery is driven by the thenable's notification. If the stream1134        // closes before the listeners are notified, the value is dropped1135        // and the reference is left unfulfilled. Since the stream may close1136        // synchronously when the last task completes, a thenable that1137        // notifies its listeners synchronously (unlike a native Promise,1138        // which notifies in a microtask) is guaranteed delivery of any1139        // value it settles with before the stream closes.1140        const id = request.nextChunkId++;1141        // The parent task is mutated as serialization continues, so we1142        // snapshot the context that the new task needs if it's created1143        // later.1144        const keyPath = task.keyPath;1145        const implicitSlot = task.implicitSlot;1146        const formatContext = task.formatContext;1147        const lastTimestamp =1148          enableProfilerTimer &&1149          (enableComponentPerformanceTrack || enableAsyncDebugInfo)1150            ? task.time1151            : 0;1152        const debugOwner = __DEV__ ? task.debugOwner : null;1153        const debugStack = __DEV__ ? task.debugStack : null;1154        const debugTask = __DEV__ ? task.debugTask : null;1155        let settled = false;1156        thenable.then(1157          (value: any) => {1158            if (settled || request.status > OPEN) {1159              // Too late. The stream already closed (or the request was1160              // aborted), so the reference stays unfulfilled.1161              return;1162            }1163            settled = true;1164            const newTask = createTaskWithID(1165              request,1166              id,1167              value,1168              keyPath,1169              implicitSlot,1170              formatContext,1171              request.abortableTasks,1172              lastTimestamp,1173              debugOwner,1174              debugStack,1175              debugTask,1176            );1177            forwardDebugInfoFromCurrentContext(request, newTask, thenable);1178            pingTask(request, newTask);1179          },1180          (reason: mixed) => {1181            if (settled || request.status > OPEN) {1182              return;1183            }1184            settled = true;1185            const newTask = createTaskWithID(1186              request,1187              id,1188              thenable as any, // never rendered. used for debug info.1189              keyPath,1190              implicitSlot,1191              formatContext,1192              request.abortableTasks,1193              lastTimestamp,1194              debugOwner,1195              debugStack,1196              debugTask,1197            );1198            if (1199              enableProfilerTimer &&1200              (enableComponentPerformanceTrack || enableAsyncDebugInfo)1201            ) {1202              // If this is async we need to time when this task finishes.1203              newTask.timed = true;1204            }1205            erroredTask(request, newTask, reason);1206            enqueueFlush(request);1207          },1208        );1209        return id;1210      }1211      // Fallthrough1212    }1213    default: {1214      const newTask = createThenableTask(request, task, thenable);1215      if (request.status === ABORTING) {1216        // We can no longer accept any resolved values1217        request.abortableTasks.delete(newTask);1218        if (request.type === PRERENDER) {1219          haltTask(newTask, request);1220          finishHaltedTask(newTask, request);1221        } else {1222          const errorId: number = request.fatalError as any;1223          abortTask(newTask, request, errorId);1224          finishAbortedTask(newTask, request, errorId);1225        }1226        return newTask.id;1227      }1228      if (typeof thenable.status !== 'string') {1229        // Only instrument the thenable if the status if not defined. If1230        // it's defined, but an unknown value, assume it's been instrumented by1231        // some custom userspace implementation. We treat it as "pending".1232        const pendingThenable: PendingThenable<mixed> = thenable as any;1233        pendingThenable.status = 'pending';1234        pendingThenable.then(1235          fulfilledValue => {1236            if (thenable.status === 'pending') {1237              const fulfilledThenable: FulfilledThenable<mixed> =1238                thenable as any;1239              fulfilledThenable.status = 'fulfilled';1240              fulfilledThenable.value = fulfilledValue;1241            }1242          },1243          (error: mixed) => {1244            if (thenable.status === 'pending') {1245              const rejectedThenable: RejectedThenable<mixed> = thenable as any;1246              rejectedThenable.status = 'rejected';1247              rejectedThenable.reason = error;1248            }1249          },1250        );1251      }1252      thenable.then(1253        value => {1254          forwardDebugInfoFromCurrentContext(request, newTask, thenable);1255          newTask.model = value;1256          pingTask(request, newTask);1257        },1258        reason => {1259          if (newTask.status === PENDING) {1260            if (1261              enableProfilerTimer &&1262              (enableComponentPerformanceTrack || enableAsyncDebugInfo)1263            ) {1264              // If this is async we need to time when this task finishes.1265              newTask.timed = true;1266            }1267            // We expect that the only status it might be otherwise is ABORTED.1268            // When we abort we emit chunks in each pending task slot and don't need1269            // to do so again here.1270            erroredTask(request, newTask, reason);1271            enqueueFlush(request);1272          }1273        },1274      );1275      return newTask.id;1276    }1277  }1278}12791280function serializeReadableStream(1281  request: Request,1282  task: Task,1283  stream: ReadableStream,1284): string {1285  // Detect if this is a BYOB stream. BYOB streams should be able to be read as bytes on the1286  // receiving side. It also implies that different chunks can be split up or merged as opposed1287  // to a readable stream that happens to have Uint8Array as the type which might expect it to be1288  // received in the same slices.1289  // $FlowFixMe[prop-missing]: This is a Node.js extension.1290  let supportsBYOB: void | boolean = stream.supportsBYOB;1291  if (supportsBYOB === undefined) {1292    try {1293      // $FlowFixMe[extra-arg]: This argument is accepted.1294      stream.getReader({mode: 'byob'}).releaseLock();1295      supportsBYOB = true;1296    } catch (x) {1297      supportsBYOB = false;1298    }1299  }1300  // At this point supportsBYOB is guaranteed to be a boolean.1301  const isByteStream: boolean = supportsBYOB;13021303  const reader = stream.getReader();13041305  // This task won't actually be retried. We just use it to attempt synchronous renders.1306  const streamTask = createTask(1307    request,1308    task.model,1309    task.keyPath,1310    task.implicitSlot,1311    task.formatContext,1312    request.abortableTasks,1313    enableProfilerTimer &&1314      (enableComponentPerformanceTrack || enableAsyncDebugInfo)1315      ? task.time1316      : 0,1317    __DEV__ ? task.debugOwner : null,1318    __DEV__ ? task.debugStack : null,1319    __DEV__ ? task.debugTask : null,1320  );13211322  // The task represents the Stop row. This adds a Start row.1323  request.pendingChunks++;1324  const startStreamRow =1325    streamTask.id.toString(16) + ':' + (isByteStream ? 'r' : 'R') + '\n';1326  request.completedRegularChunks.push(stringToChunk(startStreamRow));13271328  function progress(entry: {done: boolean, value: ReactClientValue, ...}) {1329    if (streamTask.status !== PENDING) {1330      return;1331    }13321333    if (entry.done) {1334      streamTask.status = COMPLETED;1335      const endStreamRow = streamTask.id.toString(16) + ':C\n';1336      request.completedRegularChunks.push(stringToChunk(endStreamRow));1337      request.abortableTasks.delete(streamTask);1338      request.cacheController.signal.removeEventListener('abort', abortStream);1339      enqueueFlush(request);1340      callOnAllReadyIfReady(request);1341    } else {1342      try {1343        request.pendingChunks++;1344        streamTask.model = entry.value;1345        if (isByteStream) {1346          // Chunks of byte streams are always Uint8Array instances.1347          const chunk: Uint8Array = streamTask.model as any;1348          emitTypedArrayChunk(request, streamTask.id, 'b', chunk, false);1349        } else {1350          tryStreamTask(request, streamTask);1351        }1352        enqueueFlush(request);1353        reader.read().then(progress, error);1354      } catch (x) {1355        error(x);1356      }1357    }1358  }1359  function error(reason: mixed) {1360    if (streamTask.status !== PENDING) {1361      return;1362    }1363    request.cacheController.signal.removeEventListener('abort', abortStream);1364    erroredTask(request, streamTask, reason);1365    enqueueFlush(request);13661367    // $FlowFixMe[incompatible-type] should be able to pass mixed1368    // $FlowFixMe[incompatible-use]1369    reader.cancel(reason).then(error, error);1370  }1371  function abortStream() {1372    if (streamTask.status !== PENDING) {1373      return;1374    }1375    const signal = request.cacheController.signal;1376    signal.removeEventListener('abort', abortStream);1377    const reason = signal.reason;1378    if (request.type === PRERENDER) {1379      request.abortableTasks.delete(streamTask);1380      haltTask(streamTask, request);1381      finishHaltedTask(streamTask, request);1382    } else {1383      // TODO: Make this use abortTask() instead.1384      erroredTask(request, streamTask, reason);1385      enqueueFlush(request);1386    }1387    // $FlowFixMe[incompatible-use] should be able to pass mixed1388    reader.cancel(reason).then(error, error);1389  }13901391  request.cacheController.signal.addEventListener('abort', abortStream);1392  reader.read().then(progress, error);1393  return serializeByValueID(streamTask.id);1394}13951396function serializeAsyncIterable(1397  request: Request,1398  task: Task,1399  iterable: $AsyncIterable<ReactClientValue, ReactClientValue, void>,1400  iterator: $AsyncIterator<ReactClientValue, ReactClientValue, void>,1401): string {1402  // Generators/Iterators are Iterables but they're also their own iterator1403  // functions. If that's the case, we treat them as single-shot. Otherwise,1404  // we assume that this iterable might be a multi-shot and allow it to be1405  // iterated more than once on the client.1406  const isIterator = iterable === iterator;14071408  // This task won't actually be retried. We just use it to attempt synchronous renders.1409  const streamTask = createTask(1410    request,1411    task.model,1412    task.keyPath,1413    task.implicitSlot,1414    task.formatContext,1415    request.abortableTasks,1416    enableProfilerTimer &&1417      (enableComponentPerformanceTrack || enableAsyncDebugInfo)1418      ? task.time1419      : 0,1420    __DEV__ ? task.debugOwner : null,1421    __DEV__ ? task.debugStack : null,1422    __DEV__ ? task.debugTask : null,1423  );14241425  if (__DEV__) {1426    const debugInfo: ?ReactDebugInfo = (iterable as any)._debugInfo;1427    if (debugInfo) {1428      forwardDebugInfo(request, streamTask, debugInfo);1429    }1430  }14311432  // The task represents the Stop row. This adds a Start row.1433  request.pendingChunks++;1434  const startStreamRow =1435    streamTask.id.toString(16) + ':' + (isIterator ? 'x' : 'X') + '\n';1436  request.completedRegularChunks.push(stringToChunk(startStreamRow));14371438  function progress(1439    entry:1440      | {done: false, +value: ReactClientValue, ...}1441      | {done: true, +value: ReactClientValue, ...},1442  ) {1443    if (streamTask.status !== PENDING) {1444      return;1445    }14461447    if (entry.done) {1448      streamTask.status = COMPLETED;1449      let endStreamRow;1450      if (entry.value === undefined) {1451        endStreamRow = streamTask.id.toString(16) + ':C\n';1452      } else {1453        // Unlike streams, the last value may not be undefined. If it's not1454        // we outline it and encode a reference to it in the closing instruction.1455        try {1456          const chunkId = outlineModel(request, entry.value);1457          endStreamRow =1458            streamTask.id.toString(16) +1459            ':C' +1460            stringify(serializeByValueID(chunkId)) +1461            '\n';1462        } catch (x) {1463          error(x);1464          return;1465        }1466      }1467      request.completedRegularChunks.push(stringToChunk(endStreamRow));1468      request.abortableTasks.delete(streamTask);1469      request.cacheController.signal.removeEventListener(1470        'abort',1471        abortIterable,1472      );1473      enqueueFlush(request);1474      callOnAllReadyIfReady(request);1475    } else {1476      try {1477        streamTask.model = entry.value;1478        request.pendingChunks++;1479        tryStreamTask(request, streamTask);1480        enqueueFlush(request);1481        if (__DEV__) {1482          callIteratorInDEV(iterator, progress, error);1483        } else {1484          iterator.next().then(progress, error);1485        }1486      } catch (x) {1487        error(x);1488        return;1489      }1490    }1491  }1492  function error(reason: mixed) {1493    if (streamTask.status !== PENDING) {1494      return;1495    }1496    request.cacheController.signal.removeEventListener('abort', abortIterable);1497    erroredTask(request, streamTask, reason);1498    enqueueFlush(request);1499    if (typeof (iterator as any).throw === 'function') {1500      // The iterator protocol doesn't necessarily include this but a generator do.1501      // $FlowFixMe[prop-missing] should be able to pass mixed1502      iterator.throw(reason).then(noop, noop);1503    }1504  }1505  function abortIterable() {1506    if (streamTask.status !== PENDING) {1507      return;1508    }1509    const signal = request.cacheController.signal;1510    signal.removeEventListener('abort', abortIterable);1511    const reason = signal.reason;1512    if (request.type === PRERENDER) {1513      request.abortableTasks.delete(streamTask);1514      haltTask(streamTask, request);1515      finishHaltedTask(streamTask, request);1516    } else {1517      // TODO: Make this use abortTask() instead.1518      erroredTask(request, streamTask, signal.reason);1519      enqueueFlush(request);1520    }1521    if (typeof (iterator as any).throw === 'function') {1522      // TODO: Premature exits should call return() on the iterator if it exists1523      // to allow cleanup. See https://tc39.es/ecma262/multipage/control-abstraction-objects.html#table-async-iterator-optional1524      // The iterator protocol doesn't necessarily include this but a generator do.1525      // $FlowFixMe[prop-missing] should be able to pass mixed1526      iterator.throw(reason).then(noop, noop);1527    }1528  }1529  request.cacheController.signal.addEventListener('abort', abortIterable);1530  if (__DEV__) {1531    callIteratorInDEV(iterator, progress, error);1532  } else {1533    iterator.next().then(progress, error);1534  }1535  return serializeByValueID(streamTask.id);1536}15371538export function emitHint<Code: HintCode>(1539  request: Request,1540  code: Code,1541  model: HintModel<Code>,1542): void {1543  emitHintChunk(request, code, model);1544  enqueueFlush(request);1545}15461547export function getHints(request: Request): Hints {1548  return request.hints;1549}15501551export function getCache(request: Request): Map<Function, mixed> {1552  return request.cache;1553}15541555function readThenable<T>(thenable: Thenable<T>): T {1556  if (thenable.status === 'fulfilled') {1557    return thenable.value;1558  } else if (thenable.status === 'rejected') {1559    throw thenable.reason;1560  }1561  throw thenable;1562}15631564function createLazyWrapperAroundWakeable(1565  request: Request,1566  task: Task,1567  wakeable: Wakeable,1568) {1569  // This is a temporary fork of the `use` implementation until we accept1570  // promises everywhere.1571  const thenable: Thenable<mixed> = wakeable as any;1572  switch (thenable.status) {1573    case 'fulfilled': {1574      forwardDebugInfoFromThenable(request, task, thenable, null, null);1575      return thenable.value;1576    }1577    case 'rejected':1578      forwardDebugInfoFromThenable(request, task, thenable, null, null);1579      break;1580    default: {1581      if (typeof thenable.status === 'string') {1582        // Only instrument the thenable if the status if not defined. If1583        // it's defined, but an unknown value, assume it's been instrumented by1584        // some custom userspace implementation. We treat it as "pending".1585        break;1586      }1587      const pendingThenable: PendingThenable<mixed> = thenable as any;1588      pendingThenable.status = 'pending';1589      pendingThenable.then(1590        fulfilledValue => {1591          forwardDebugInfoFromCurrentContext(request, task, thenable);1592          if (thenable.status === 'pending') {1593            const fulfilledThenable: FulfilledThenable<mixed> = thenable as any;1594            fulfilledThenable.status = 'fulfilled';1595            fulfilledThenable.value = fulfilledValue;1596          }1597        },1598        (error: mixed) => {1599          forwardDebugInfoFromCurrentContext(request, task, thenable);1600          if (thenable.status === 'pending') {1601            const rejectedThenable: RejectedThenable<mixed> = thenable as any;1602            rejectedThenable.status = 'rejected';1603            rejectedThenable.reason = error;1604          }1605        },1606      );1607      break;1608    }1609  }1610  const lazyType: LazyComponent<any, Thenable<any>> = {1611    $$typeof: REACT_LAZY_TYPE,1612    _payload: thenable,1613    _init: readThenable,1614  };1615  return lazyType;1616}16171618function callWithDebugContextInDEV<A, T>(1619  request: Request,1620  task: Task,1621  callback: A => T,1622  arg: A,1623): T {1624  // We don't have a Server Component instance associated with this callback and1625  // the nearest context is likely a Client Component being serialized. We create1626  // a fake owner during this callback so we can get the stack trace from it.1627  // This also gets sent to the client as the owner for the replaying log.1628  const componentDebugInfo: ReactComponentInfo = {1629    name: '',1630    env: task.environmentName,1631    key: null,1632    owner: task.debugOwner,1633  };1634  // $FlowFixMe[cannot-write]1635  componentDebugInfo.stack =1636    task.debugStack === null1637      ? null1638      : filterStackTrace(request, parseStackTrace(task.debugStack, 1));1639  // $FlowFixMe[cannot-write]1640  componentDebugInfo.debugStack = task.debugStack;1641  // $FlowFixMe[cannot-write]1642  componentDebugInfo.debugTask = task.debugTask;1643  const debugTask = task.debugTask;1644  // We don't need the async component storage context here so we only set the1645  // synchronous tracking of owner.1646  setCurrentOwner(componentDebugInfo);1647  try {1648    if (debugTask) {1649      return debugTask.run(callback.bind(null, arg));1650    }1651    return callback(arg);1652  } finally {1653    setCurrentOwner(null);1654  }1655}16561657const voidHandler = () => {};16581659function processServerComponentReturnValue(1660  request: Request,1661  task: Task,1662  Component: any,1663  result: any,1664): any {1665  // A Server Component's return value has a few special properties due to being1666  // in the return position of a Component. We convert them here.1667  if (1668    typeof result !== 'object' ||1669    result === null ||1670    isClientReference(result)1671  ) {1672    return result;1673  }16741675  if (typeof result.then === 'function') {1676    // When the return value is in children position we can resolve it immediately,1677    // to its value without a wrapper if it's synchronously available.1678    const thenable: Thenable<any> = result;1679    if (__DEV__) {1680      // If the thenable resolves to an element, then it was in a static position,1681      // the return value of a Server Component. That doesn't need further validation1682      // of keys. The Server Component itself would have had a key.1683      thenable.then(resolvedValue => {1684        if (1685          typeof resolvedValue === 'object' &&1686          resolvedValue !== null &&1687          resolvedValue.$$typeof === REACT_ELEMENT_TYPE1688        ) {1689          resolvedValue._store.validated = 1;1690        }1691      }, voidHandler);1692    }1693    // TODO: Once we accept Promises as children on the client, we can just return1694    // the thenable here.1695    return createLazyWrapperAroundWakeable(request, task, result);1696  }16971698  if (__DEV__) {1699    if ((result as any).$$typeof === REACT_ELEMENT_TYPE) {1700      // If the server component renders to an element, then it was in a static position.1701      // That doesn't need further validation of keys. The Server Component itself would1702      // have had a key.1703      (result as any)._store.validated = 1;1704    }1705  }17061707  // Normally we'd serialize an Iterator/AsyncIterator as a single-shot which is not compatible1708  // to be rendered as a React Child. However, because we have the function to recreate1709  // an iterable from rendering the element again, we can effectively treat it as multi-1710  // shot. Therefore we treat this as an Iterable/AsyncIterable, whether it was one or not, by1711  // adding a wrapper so that this component effectively renders down to an AsyncIterable.1712  const iteratorFn = getIteratorFn(result);1713  if (iteratorFn) {1714    const iterableChild = result;1715    const multiShot = {1716      [Symbol.iterator]: function () {1717        const iterator = iteratorFn.call(iterableChild);1718        if (__DEV__) {1719          // If this was an Iterator but not a GeneratorFunction we warn because1720          // it might have been a mistake. Technically you can make this mistake with1721          // GeneratorFunctions and even single-shot Iterables too but it's extra1722          // tempting to try to return the value from a generator.1723          if (iterator === iterableChild) {1724            const isGeneratorComponent =1725              // $FlowFixMe[method-unbinding]1726              Object.prototype.toString.call(Component) ===1727                '[object GeneratorFunction]' &&1728              // $FlowFixMe[method-unbinding]1729              Object.prototype.toString.call(iterableChild) ===1730                '[object Generator]';1731            if (!isGeneratorComponent) {1732              callWithDebugContextInDEV(request, task, () => {1733                console.error(1734                  'Returning an Iterator from a Server Component is not supported ' +1735                    'since it cannot be looped over more than once. ',1736                );1737              });1738            }1739          }1740        }1741        return iterator as any;1742      },1743    };1744    if (__DEV__) {1745      (multiShot as any)._debugInfo = iterableChild._debugInfo;1746    }1747    return multiShot;1748  }1749  if (1750    typeof (result as any)[ASYNC_ITERATOR] === 'function' &&1751    (typeof ReadableStream !== 'function' ||1752      !(result instanceof ReadableStream))1753  ) {1754    const iterableChild = result;1755    const multishot = {1756      [ASYNC_ITERATOR]: function () {1757        const iterator = (iterableChild as any)[ASYNC_ITERATOR]();1758        if (__DEV__) {1759          // If this was an AsyncIterator but not an AsyncGeneratorFunction we warn because1760          // it might have been a mistake. Technically you can make this mistake with1761          // AsyncGeneratorFunctions and even single-shot AsyncIterables too but it's extra1762          // tempting to try to return the value from a generator.1763          if (iterator === iterableChild) {1764            const isGeneratorComponent =1765              // $FlowFixMe[method-unbinding]1766              Object.prototype.toString.call(Component) ===1767                '[object AsyncGeneratorFunction]' &&1768              // $FlowFixMe[method-unbinding]1769              Object.prototype.toString.call(iterableChild) ===1770                '[object AsyncGenerator]';1771            if (!isGeneratorComponent) {1772              callWithDebugContextInDEV(request, task, () => {1773                console.error(1774                  'Returning an AsyncIterator from a Server Component is not supported ' +1775                    'since it cannot be looped over more than once. ',1776                );1777              });1778            }1779          }1780        }1781        return iterator;1782      },1783    };1784    if (__DEV__) {1785      (multishot as any)._debugInfo = iterableChild._debugInfo;1786    }1787    return multishot;1788  }1789  return result;1790}17911792function renderFunctionComponent<Props>(1793  request: Request,1794  task: Task,1795  key: ReactKey,1796  Component: (p: Props, arg: void) => any,1797  props: Props,1798  validated: number, // DEV-only1799): ReactJSONValue {1800  // Reset the task's thenable state before continuing, so that if a later1801  // component suspends we can reuse the same task object. If the same1802  // component suspends again, the thenable state will be restored.1803  const prevThenableState = task.thenableState;1804  task.thenableState = null;18051806  let result;18071808  let componentDebugInfo: ReactComponentInfo;1809  if (__DEV__) {1810    if (!canEmitDebugInfo) {1811      // We don't have a chunk to assign debug info. We need to outline this1812      // component to assign it an ID.1813      return outlineTask(request, task);1814    } else if (prevThenableState !== null) {1815      // This is a replay and we've already emitted the debug info of this component1816      // in the first pass. We skip emitting a duplicate line.1817      // As a hack we stashed the previous component debug info on this object in DEV.1818      componentDebugInfo = (prevThenableState as any)._componentDebugInfo;1819    } else {1820      // This is a new component in the same task so we can emit more debug info.1821      const componentDebugID = task.id;1822      const componentName =1823        (Component as any).displayName || Component.name || '';1824      const componentEnv = (0, request.environmentName)();1825      request.pendingChunks++;1826      componentDebugInfo = {1827        name: componentName,1828        env: componentEnv,1829        key: key,1830        owner: task.debugOwner,1831      } as ReactComponentInfo;1832      // $FlowFixMe[cannot-write]1833      componentDebugInfo.stack =1834        task.debugStack === null1835          ? null1836          : filterStackTrace(request, parseStackTrace(task.debugStack, 1));1837      // $FlowFixMe[cannot-write]1838      componentDebugInfo.props = props;1839      // $FlowFixMe[cannot-write]1840      componentDebugInfo.debugStack = task.debugStack;1841      // $FlowFixMe[cannot-write]1842      componentDebugInfo.debugTask = task.debugTask;18431844      // We outline this model eagerly so that we can refer to by reference as an owner.1845      // If we had a smarter way to dedupe we might not have to do this if there ends up1846      // being no references to this as an owner.18471848      outlineComponentInfo(request, componentDebugInfo);18491850      // Track when we started rendering this component.1851      if (1852        enableProfilerTimer &&1853        (enableComponentPerformanceTrack || enableAsyncDebugInfo)1854      ) {1855        advanceTaskTime(request, task, performance.now());1856      }18571858      emitDebugChunk(request, componentDebugID, componentDebugInfo);18591860      // We've emitted the latest environment for this task so we track that.1861      task.environmentName = componentEnv;18621863      if (validated === 2) {1864        warnForMissingKey(request, key, componentDebugInfo, task.debugTask);1865      }1866    }1867    prepareToUseHooksForComponent(prevThenableState, componentDebugInfo);1868    // $FlowFixMe[constant-condition]1869    if (supportsComponentStorage) {1870      // Run the component in an Async Context that tracks the current owner.1871      if (task.debugTask) {1872        result = task.debugTask.run(1873          // $FlowFixMe[method-unbinding]1874          componentStorage.run.bind(1875            componentStorage,1876            componentDebugInfo,1877            callComponentInDEV,1878            Component,1879            props,1880            componentDebugInfo,1881          ),1882        );1883      } else {1884        result = componentStorage.run(1885          componentDebugInfo,1886          callComponentInDEV,1887          Component,1888          props,1889          componentDebugInfo,1890        );1891      }1892    } else {1893      if (task.debugTask) {1894        result = task.debugTask.run(1895          callComponentInDEV.bind(null, Component, props, componentDebugInfo),1896        );1897      } else {1898        result = callComponentInDEV(Component, props, componentDebugInfo);1899      }1900    }1901  } else {1902    componentDebugInfo = null as any;1903    prepareToUseHooksForComponent(prevThenableState, null);1904    // The secondArg is always undefined in Server Components since refs error early.1905    const secondArg = undefined;1906    result = Component(props, secondArg);1907  }19081909  if (request.status === ABORTING) {1910    if (1911      typeof result === 'object' &&1912      // $FlowFixMe[invalid-compare]1913      result !== null &&1914      typeof result.then === 'function' &&1915      !isClientReference(result)1916    ) {1917      result.then(voidHandler, voidHandler);1918    }1919    // If we aborted during rendering we should interrupt the render but1920    // we don't need to provide an error because the renderer will encode1921    // the abort error as the reason.1922    // eslint-disable-next-line no-throw-literal1923    throw null;1924  }19251926  if (__DEV__ || (enableProfilerTimer && enableAsyncDebugInfo)) {1927    // Forward any debug information for any Promises that we use():ed during the render.1928    // We do this at the end so that we don't keep doing this for each retry.1929    const trackedThenables = getTrackedThenablesAfterRendering();1930    if (trackedThenables !== null) {1931      const stacks: Array<Error> =1932        __DEV__ && enableAsyncDebugInfo1933          ? (trackedThenables as any)._stacks ||1934            ((trackedThenables as any)._stacks = [])1935          : (null as any);1936      for (let i = 0; i < trackedThenables.length; i++) {1937        const stack = __DEV__ && enableAsyncDebugInfo ? stacks[i] : null;1938        forwardDebugInfoFromThenable(1939          request,1940          task,1941          trackedThenables[i],1942          __DEV__ ? componentDebugInfo : null,1943          stack,1944        );1945      }1946    }1947  }19481949  // Apply special cases.1950  result = processServerComponentReturnValue(request, task, Component, result);19511952  if (__DEV__) {1953    // From this point on, the parent is the component we just rendered until we1954    // hit another JSX element.1955    task.debugOwner = componentDebugInfo;1956    // Unfortunately, we don't have a stack frame for this position. Conceptually1957    // it would be the location of the `return` inside component that just rendered.1958    task.debugStack = null;1959    task.debugTask = null;1960  }19611962  // Track this element's key on the Server Component on the keyPath context..1963  const prevKeyPath = task.keyPath;1964  const prevImplicitSlot = task.implicitSlot;1965  if (key !== null) {1966    // Append the key to the path. Technically a null key should really add the child1967    // index. We don't do that to hold the payload small and implementation simple.1968    if (key === REACT_OPTIMISTIC_KEY || prevKeyPath === REACT_OPTIMISTIC_KEY) {1969      // The optimistic key is viral. It turns the whole key into optimistic if any part is.1970      task.keyPath = REACT_OPTIMISTIC_KEY;1971    } else {1972      task.keyPath = prevKeyPath === null ? key : prevKeyPath + ',' + key;1973    }1974  } else if (prevKeyPath === null) {1975    // This sequence of Server Components has no keys. This means that it was rendered1976    // in a slot that needs to assign an implicit key. Even if children below have1977    // explicit keys, they should not be used for the outer most key since it might1978    // collide with other slots in that set.1979    task.implicitSlot = true;1980  }1981  const json = renderModelDestructive(request, task, emptyRoot, '', result);1982  task.keyPath = prevKeyPath;1983  task.implicitSlot = prevImplicitSlot;1984  return json;1985}19861987function warnForMissingKey(1988  request: Request,1989  key: ReactKey,1990  componentDebugInfo: ReactComponentInfo,1991  debugTask: null | ConsoleTask,1992): void {1993  if (__DEV__) {1994    let didWarnForKey = request.didWarnForKey;1995    if (didWarnForKey == null) {1996      didWarnForKey = request.didWarnForKey = new WeakSet();1997    }1998    const parentOwner = componentDebugInfo.owner;1999    if (parentOwner != null) {2000      if (didWarnForKey.has(parentOwner)) {

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.