packages/react-server/src/ReactFlightServer.js JAVASCRIPT 7,027 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 7,027.
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  enableFlightWeakThenables,19  enableFlightObjectReferences,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  writtenImportStrings: Map<string, string>,620  // The combined length of the keys in writtenImportStrings.621  writtenImportStringsSize: number,622  temporaryReferences: void | TemporaryReferenceSet,623  identifierPrefix: string,624  identifierCount: number,625  taintCleanupQueue: Array<string | bigint>,626  onError: (error: mixed) => ?string,627  onAllReady: () => void,628  onFatalError: mixed => void,629  // Profiling-only630  timeOrigin: number,631  abortTime: number,632  // DEV-only633  pendingDebugChunks: number,634  // See completedRegularChunks for why some entries are preceded by the635  // NEXT_TWO_CHUNKS_ARE_ATOMIC sentinel.636  completedDebugChunks: Array<637    Chunk | BinaryChunk | typeof NEXT_TWO_CHUNKS_ARE_ATOMIC,638  >,639  debugDestination: null | Destination,640  environmentName: () => string,641  filterStackFrame: (642    url: string,643    functionName: string,644    lineNumber: number,645    columnNumber: number,646  ) => boolean,647  didWarnForKey: null | WeakSet<ReactComponentInfo>,648  writtenDebugObjects: WeakMap<Reference, string>,649  deferredDebugObjects: null | DeferredDebugStore,650};651652const {653  TaintRegistryObjects,654  TaintRegistryValues,655  TaintRegistryByteLengths,656  TaintRegistryPendingRequests,657} = ReactSharedInternals;658659function throwTaintViolation(message: string) {660  // eslint-disable-next-line react-internal/prod-error-codes661  throw new Error(message);662}663664function cleanupTaintQueue(request: Request): void {665  const cleanupQueue = request.taintCleanupQueue;666  TaintRegistryPendingRequests.delete(cleanupQueue);667  for (let i = 0; i < cleanupQueue.length; i++) {668    const entryValue = cleanupQueue[i];669    const entry = TaintRegistryValues.get(entryValue);670    if (entry !== undefined) {671      if (entry.count === 1) {672        TaintRegistryValues.delete(entryValue);673      } else {674        entry.count--;675      }676    }677  }678  cleanupQueue.length = 0;679}680681function defaultErrorHandler(error: mixed) {682  console['error'](error);683  // Don't transform to our wrapper684}685686function RequestInstance(687  this: $FlowFixMe,688  type: 20 | 21,689  model: ReactClientValue,690  bundlerConfig: ClientManifest,691  onError: void | ((error: mixed) => ?string),692  onAllReady: () => void,693  onFatalError: (error: mixed) => void,694  identifierPrefix?: string,695  temporaryReferences: void | TemporaryReferenceSet,696  debugStartTime: void | number, // Profiling-only697  environmentName: void | string | (() => string), // DEV-only698  filterStackFrame: void | ((url: string, functionName: string) => boolean), // DEV-only699  keepDebugAlive: boolean, // DEV-only700) {701  if (702    ReactSharedInternals.A !== null &&703    ReactSharedInternals.A !== DefaultAsyncDispatcher704  ) {705    throw new Error(706      'Currently React only supports one RSC renderer at a time.',707    );708  }709  ReactSharedInternals.A = DefaultAsyncDispatcher;710  if (__DEV__) {711    // Unlike Fizz or Fiber, we don't reset this and just keep it on permanently.712    // This lets it act more like the AsyncDispatcher so that we can get the713    // stack asynchronously too.714    ReactSharedInternals.getCurrentStack = getCurrentStackInDEV;715  }716717  const abortSet: Set<Task> = new Set();718  const pingedTasks: Array<Task> = [];719  const cleanupQueue: Array<string | bigint> = [];720  if (enableTaint) {721    TaintRegistryPendingRequests.add(cleanupQueue);722  }723  const hints = createHints();724  this.type = type;725  this.status = OPENING;726  this.flushScheduled = false;727  this.fatalError = null;728  this.destination = null;729  this.bundlerConfig = bundlerConfig;730  this.cache = new Map();731  this.cacheController = new AbortController();732  this.nextChunkId = 0;733  this.pendingChunks = 0;734  this.hints = hints;735  this.abortableTasks = abortSet;736  this.pingedTasks = pingedTasks;737  this.completedImportChunks = [] as Array<Chunk>;738  this.completedHintChunks = [] as Array<Chunk>;739  this.completedRegularChunks = [] as Array<740    Chunk | BinaryChunk | typeof NEXT_TWO_CHUNKS_ARE_ATOMIC,741  >;742  this.completedErrorChunks = [] as Array<Chunk>;743  this.writtenSymbols = new Map();744  this.writtenClientReferences = new Map();745  this.writtenServerReferences = new Map();746  this.writtenObjects = new WeakMap();747  this.writtenImportStrings = new Map();748  this.writtenImportStringsSize = 0;749  this.temporaryReferences = temporaryReferences;750  this.identifierPrefix = identifierPrefix || '';751  this.identifierCount = 1;752  this.taintCleanupQueue = cleanupQueue;753  this.onError = onError === undefined ? defaultErrorHandler : onError;754  this.onAllReady = onAllReady;755  this.onFatalError = onFatalError;756757  if (__DEV__) {758    this.pendingDebugChunks = 0;759    this.completedDebugChunks = [] as Array<760      Chunk | BinaryChunk | typeof NEXT_TWO_CHUNKS_ARE_ATOMIC,761    >;762    this.debugDestination = null;763    this.environmentName =764      environmentName === undefined765        ? () => 'Server'766        : typeof environmentName !== 'function'767          ? () => environmentName768          : environmentName;769    this.filterStackFrame =770      filterStackFrame === undefined771        ? defaultFilterStackFrame772        : filterStackFrame;773    this.didWarnForKey = null;774    this.writtenDebugObjects = new WeakMap();775    this.deferredDebugObjects = keepDebugAlive776      ? {777          retained: new Map(),778          existing: new Map(),779        }780      : null;781  }782783  let timeOrigin: number;784  if (enableProfilerTimer) {785    // We start by serializing the time origin. Any future timestamps will be786    // emitted relatively to this origin. Instead of using performance.timeOrigin787    // as this origin, we use the timestamp at the start of the request.788    // This avoids leaking unnecessary information like how long the server has789    // been running and allows for more compact representation of each timestamp.790    // The time origin is stored as an offset in the time space of this environment.791    if (typeof debugStartTime === 'number') {792      // We expect `startTime` to be an absolute timestamp, so relativize it to match the other case.793      timeOrigin = this.timeOrigin =794        debugStartTime -795        // $FlowFixMe[prop-missing]796        performance.timeOrigin;797    } else {798      timeOrigin = this.timeOrigin = performance.now();799    }800    emitTimeOriginChunk(801      this,802      timeOrigin +803        // $FlowFixMe[prop-missing]804        performance.timeOrigin,805    );806    this.abortTime = -0.0;807  } else {808    timeOrigin = 0;809  }810811  const rootTask = createTask(812    this,813    model,814    null,815    false,816    createRootFormatContext(),817    abortSet,818    timeOrigin,819    null,820    null,821    null,822  );823  pingedTasks.push(rootTask);824}825826export function createRequest(827  model: ReactClientValue,828  bundlerConfig: ClientManifest,829  onError: void | ((error: mixed) => ?string),830  identifierPrefix: void | string,831  temporaryReferences: void | TemporaryReferenceSet,832  debugStartTime: void | number, // Profiling-only833  environmentName: void | string | (() => string), // DEV-only834  filterStackFrame: void | ((url: string, functionName: string) => boolean), // DEV-only835  keepDebugAlive: boolean, // DEV-only836): Request {837  if (__DEV__) {838    resetOwnerStackLimit();839  }840841  // $FlowFixMe[invalid-constructor]: the shapes are exact here but Flow doesn't like constructors842  return new RequestInstance(843    RENDER,844    model,845    bundlerConfig,846    onError,847    noop,848    noop,849    identifierPrefix,850    temporaryReferences,851    debugStartTime,852    environmentName,853    filterStackFrame,854    keepDebugAlive,855  );856}857858export function createPrerenderRequest(859  model: ReactClientValue,860  bundlerConfig: ClientManifest,861  onAllReady: () => void,862  onFatalError: () => void,863  onError: void | ((error: mixed) => ?string),864  identifierPrefix: void | string,865  temporaryReferences: void | TemporaryReferenceSet,866  debugStartTime: void | number, // Profiling-only867  environmentName: void | string | (() => string), // DEV-only868  filterStackFrame: void | ((url: string, functionName: string) => boolean), // DEV-only869  keepDebugAlive: boolean, // DEV-only870): Request {871  if (__DEV__) {872    resetOwnerStackLimit();873  }874875  // $FlowFixMe[invalid-constructor]: the shapes are exact here but Flow doesn't like constructors876  return new RequestInstance(877    PRERENDER,878    model,879    bundlerConfig,880    onError,881    onAllReady,882    onFatalError,883    identifierPrefix,884    temporaryReferences,885    debugStartTime,886    environmentName,887    filterStackFrame,888    keepDebugAlive,889  );890}891892let currentRequest: null | Request = null;893894export function resolveRequest(): null | Request {895  if (currentRequest) return currentRequest;896  // $FlowFixMe[constant-condition]897  if (supportsRequestStorage) {898    const store = requestStorage.getStore();899    if (store) return store;900  }901  return null;902}903904function isTypedArray(value: any): boolean {905  if (value instanceof ArrayBuffer) {906    return true;907  }908  if (value instanceof Int8Array) {909    return true;910  }911  if (value instanceof Uint8Array) {912    return true;913  }914  if (value instanceof Uint8ClampedArray) {915    return true;916  }917  if (value instanceof Int16Array) {918    return true;919  }920  if (value instanceof Uint16Array) {921    return true;922  }923  if (value instanceof Int32Array) {924    return true;925  }926  if (value instanceof Uint32Array) {927    return true;928  }929  if (value instanceof Float32Array) {930    return true;931  }932  if (value instanceof Float64Array) {933    return true;934  }935  if (value instanceof BigInt64Array) {936    return true;937  }938  if (value instanceof BigUint64Array) {939    return true;940  }941  if (value instanceof DataView) {942    return true;943  }944  return false;945}946947function serializeDebugThenable(948  request: Request,949  counter: {objectLimit: number},950  thenable: Thenable<any>,951): string {952  // Like serializeThenable but for renderDebugModel953  request.pendingDebugChunks++;954  const id = request.nextChunkId++;955  const ref = serializePromiseID(id);956  request.writtenDebugObjects.set(thenable, ref);957958  switch (thenable.status) {959    case 'fulfilled': {960      emitOutlinedDebugModelChunk(request, id, counter, thenable.value);961      return ref;962    }963    case 'rejected': {964      const x = thenable.reason;965      // We don't log these errors since they didn't actually throw into Flight.966      const digest = '';967      emitErrorChunk(request, id, digest, x, true, null);968      return ref;969    }970  }971972  if (request.status === ABORTING) {973    // Ensure that we have time to emit the halt chunk if we're sync aborting.974    emitDebugHaltChunk(request, id);975    return ref;976  }977978  const deferredDebugObjects = request.deferredDebugObjects;979  if (deferredDebugObjects !== null) {980    // For Promises that are not yet resolved, we always defer them. They are async anyway so it's981    // safe to defer them. This also ensures that we don't eagerly call .then() on a Promise that982    // otherwise wouldn't have initialized. It also ensures that we don't "handle" a rejection983    // that otherwise would have triggered unhandled rejection.984    deferredDebugObjects.retained.set(id, thenable as any);985    const deferredRef = '$Y@' + id.toString(16);986    // We can now refer to the deferred object in the future.987    request.writtenDebugObjects.set(thenable, deferredRef);988    return deferredRef;989  }990991  let cancelled = false;992993  thenable.then(994    value => {995      if (cancelled) {996        return;997      }998      cancelled = true;999      if (request.status === ABORTING) {1000        emitDebugHaltChunk(request, id);1001        enqueueFlush(request);1002        return;1003      }1004      if (1005        (isArray(value) && value.length > 200) ||1006        (isTypedArray(value) && value.byteLength > 1000)1007      ) {1008        // If this should be deferred, but we don't have a debug channel installed1009        // it would get omitted. We can't omit outlined models but we can avoid1010        // resolving the Promise at all by halting it.1011        emitDebugHaltChunk(request, id);1012        enqueueFlush(request);1013        return;1014      }1015      emitOutlinedDebugModelChunk(request, id, counter, value);1016      enqueueFlush(request);1017    },1018    reason => {1019      if (cancelled) {1020        return;1021      }1022      cancelled = true;1023      if (request.status === ABORTING) {1024        emitDebugHaltChunk(request, id);1025        enqueueFlush(request);1026        return;1027      }1028      // We don't log these errors since they didn't actually throw into Flight.1029      const digest = '';1030      emitErrorChunk(request, id, digest, reason, true, null);1031      enqueueFlush(request);1032    },1033  );10341035  // We don't use scheduleMicrotask here because it doesn't actually schedule a microtask1036  // in all our configs which is annoying.1037  Promise.resolve().then(() => {1038    // If we don't resolve the Promise within a microtask. Leave it as hanging since we1039    // don't want to block the render forever on a Promise that might never resolve.1040    if (cancelled) {1041      return;1042    }1043    cancelled = true;1044    emitDebugHaltChunk(request, id);1045    enqueueFlush(request);1046    // Clean up the request so we don't leak this forever.1047    request = null as any;1048    counter = null as any;1049  });10501051  return ref;1052}10531054function emitRequestedDebugThenable(1055  request: Request,1056  id: number,1057  counter: {objectLimit: number},1058  thenable: Thenable<any>,1059): void {1060  thenable.then(1061    value => {1062      if (request.status === ABORTING) {1063        emitDebugHaltChunk(request, id);1064        enqueueFlush(request);1065        return;1066      }1067      emitOutlinedDebugModelChunk(request, id, counter, value);1068      enqueueFlush(request);1069    },1070    reason => {1071      if (request.status === ABORTING) {1072        emitDebugHaltChunk(request, id);1073        enqueueFlush(request);1074        return;1075      }1076      // We don't log these errors since they didn't actually throw into Flight.1077      const digest = '';1078      emitErrorChunk(request, id, digest, reason, true, null);1079      enqueueFlush(request);1080    },1081  );1082}10831084function createThenableTask(1085  request: Request,1086  task: Task,1087  thenable: Thenable<any>,1088): Task {1089  return createTask(1090    request,1091    thenable as any, // will be replaced by the value before we retry. used for debug info.1092    task.keyPath, // the server component sequence continues through Promise-as-a-child.1093    task.implicitSlot,1094    task.formatContext,1095    request.abortableTasks,1096    enableProfilerTimer ? task.time : 0,1097    __DEV__ ? task.debugOwner : null,1098    __DEV__ ? task.debugStack : null,1099    __DEV__ ? task.debugTask : null,1100  );1101}11021103function serializeThenable(1104  request: Request,1105  task: Task,1106  thenable: Thenable<any>,1107): number {1108  switch (thenable.status) {1109    case 'fulfilled': {1110      const newTask = createThenableTask(request, task, thenable);1111      forwardDebugInfoFromThenable(request, newTask, thenable, null, null);1112      // We have the resolved value, we can go ahead and schedule it for serialization.1113      newTask.model = thenable.value;1114      pingTask(request, newTask);1115      return newTask.id;1116    }1117    case 'rejected': {1118      const newTask = createThenableTask(request, task, thenable);1119      forwardDebugInfoFromThenable(request, newTask, thenable, null, null);1120      const x = thenable.reason;1121      erroredTask(request, newTask, x);1122      return newTask.id;1123    }1124    case 'pending_weak': {1125      if (enableFlightWeakThenables) {1126        // A weak-pending thenable doesn't block the stream from closing, so1127        // we don't create a task for it yet. We only reserve an id for its1128        // reference. If it settles while the stream is still open, we1129        // create the task at that point, the same as if we had serialized1130        // an already settled thenable.1131        //1132        // Delivery is driven by the thenable's notification. If the stream1133        // closes before the listeners are notified, the value is dropped1134        // and the reference is left unfulfilled. Since the stream may close1135        // synchronously when the last task completes, a thenable that1136        // notifies its listeners synchronously (unlike a native Promise,1137        // which notifies in a microtask) is guaranteed delivery of any1138        // value it settles with before the stream closes.1139        const id = request.nextChunkId++;1140        // The parent task is mutated as serialization continues, so we1141        // snapshot the context that the new task needs if it's created1142        // later.1143        const keyPath = task.keyPath;1144        const implicitSlot = task.implicitSlot;1145        const formatContext = task.formatContext;1146        const lastTimestamp = enableProfilerTimer ? task.time : 0;1147        const debugOwner = __DEV__ ? task.debugOwner : null;1148        const debugStack = __DEV__ ? task.debugStack : null;1149        const debugTask = __DEV__ ? task.debugTask : null;1150        let settled = false;1151        thenable.then(1152          (value: any) => {1153            if (settled || request.status > OPEN) {1154              // Too late. The stream already closed (or the request was1155              // aborted), so the reference stays unfulfilled.1156              return;1157            }1158            settled = true;1159            const newTask = createTaskWithID(1160              request,1161              id,1162              value,1163              keyPath,1164              implicitSlot,1165              formatContext,1166              request.abortableTasks,1167              lastTimestamp,1168              debugOwner,1169              debugStack,1170              debugTask,1171            );1172            forwardDebugInfoFromCurrentContext(request, newTask, thenable);1173            pingTask(request, newTask);1174          },1175          (reason: mixed) => {1176            if (settled || request.status > OPEN) {1177              return;1178            }1179            settled = true;1180            const newTask = createTaskWithID(1181              request,1182              id,1183              thenable as any, // never rendered. used for debug info.1184              keyPath,1185              implicitSlot,1186              formatContext,1187              request.abortableTasks,1188              lastTimestamp,1189              debugOwner,1190              debugStack,1191              debugTask,1192            );1193            if (enableProfilerTimer) {1194              // If this is async we need to time when this task finishes.1195              newTask.timed = true;1196            }1197            erroredTask(request, newTask, reason);1198            enqueueFlush(request);1199          },1200        );1201        return id;1202      }1203      // Fallthrough1204    }1205    default: {1206      const newTask = createThenableTask(request, task, thenable);1207      if (request.status === ABORTING) {1208        // We can no longer accept any resolved values1209        request.abortableTasks.delete(newTask);1210        if (request.type === PRERENDER) {1211          haltTask(newTask, request);1212          finishHaltedTask(newTask, request);1213        } else {1214          const errorId: number = request.fatalError as any;1215          abortTask(newTask, request, errorId);1216          finishAbortedTask(newTask, request, errorId);1217        }1218        return newTask.id;1219      }1220      if (typeof thenable.status !== 'string') {1221        // Only instrument the thenable if the status if not defined. If1222        // it's defined, but an unknown value, assume it's been instrumented by1223        // some custom userspace implementation. We treat it as "pending".1224        const pendingThenable: PendingThenable<mixed> = thenable as any;1225        pendingThenable.status = 'pending';1226        pendingThenable.then(1227          fulfilledValue => {1228            if (thenable.status === 'pending') {1229              const fulfilledThenable: FulfilledThenable<mixed> =1230                thenable as any;1231              fulfilledThenable.status = 'fulfilled';1232              fulfilledThenable.value = fulfilledValue;1233            }1234          },1235          (error: mixed) => {1236            if (thenable.status === 'pending') {1237              const rejectedThenable: RejectedThenable<mixed> = thenable as any;1238              rejectedThenable.status = 'rejected';1239              rejectedThenable.reason = error;1240            }1241          },1242        );1243      }1244      thenable.then(1245        value => {1246          forwardDebugInfoFromCurrentContext(request, newTask, thenable);1247          newTask.model = value;1248          pingTask(request, newTask);1249        },1250        reason => {1251          if (newTask.status === PENDING) {1252            if (enableProfilerTimer) {1253              // If this is async we need to time when this task finishes.1254              newTask.timed = true;1255            }1256            // We expect that the only status it might be otherwise is ABORTED.1257            // When we abort we emit chunks in each pending task slot and don't need1258            // to do so again here.1259            erroredTask(request, newTask, reason);1260            enqueueFlush(request);1261          }1262        },1263      );1264      return newTask.id;1265    }1266  }1267}12681269function serializeReadableStream(1270  request: Request,1271  task: Task,1272  stream: ReadableStream,1273): string {1274  // Detect if this is a BYOB stream. BYOB streams should be able to be read as bytes on the1275  // receiving side. It also implies that different chunks can be split up or merged as opposed1276  // to a readable stream that happens to have Uint8Array as the type which might expect it to be1277  // received in the same slices.1278  // $FlowFixMe[prop-missing]: This is a Node.js extension.1279  let supportsBYOB: void | boolean = stream.supportsBYOB;1280  if (supportsBYOB === undefined) {1281    try {1282      // $FlowFixMe[extra-arg]: This argument is accepted.1283      stream.getReader({mode: 'byob'}).releaseLock();1284      supportsBYOB = true;1285    } catch (x) {1286      supportsBYOB = false;1287    }1288  }1289  // At this point supportsBYOB is guaranteed to be a boolean.1290  const isByteStream: boolean = supportsBYOB;12911292  const reader = stream.getReader();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 ? task.time : 0,1303    __DEV__ ? task.debugOwner : null,1304    __DEV__ ? task.debugStack : null,1305    __DEV__ ? task.debugTask : null,1306  );13071308  // The task represents the Stop row. This adds a Start row.1309  request.pendingChunks++;1310  const startStreamRow =1311    streamTask.id.toString(16) + ':' + (isByteStream ? 'r' : 'R') + '\n';1312  request.completedRegularChunks.push(stringToChunk(startStreamRow));13131314  function progress(entry: {done: boolean, value: ReactClientValue, ...}) {1315    if (streamTask.status !== PENDING) {1316      return;1317    }13181319    if (entry.done) {1320      streamTask.status = COMPLETED;1321      const endStreamRow = streamTask.id.toString(16) + ':C\n';1322      request.completedRegularChunks.push(stringToChunk(endStreamRow));1323      request.abortableTasks.delete(streamTask);1324      request.cacheController.signal.removeEventListener('abort', abortStream);1325      enqueueFlush(request);1326      callOnAllReadyIfReady(request);1327    } else {1328      try {1329        request.pendingChunks++;1330        streamTask.model = entry.value;1331        if (isByteStream) {1332          // Chunks of byte streams are always Uint8Array instances.1333          const chunk: Uint8Array = streamTask.model as any;1334          emitTypedArrayChunk(request, streamTask.id, 'b', chunk, false);1335        } else {1336          tryStreamTask(request, streamTask);1337        }1338        enqueueFlush(request);1339        reader.read().then(progress, error);1340      } catch (x) {1341        error(x);1342      }1343    }1344  }1345  function error(reason: mixed) {1346    if (streamTask.status !== PENDING) {1347      return;1348    }1349    request.cacheController.signal.removeEventListener('abort', abortStream);1350    erroredTask(request, streamTask, reason);1351    enqueueFlush(request);13521353    // $FlowFixMe[incompatible-type] should be able to pass mixed1354    // $FlowFixMe[incompatible-use]1355    reader.cancel(reason).then(error, error);1356  }1357  function abortStream() {1358    if (streamTask.status !== PENDING) {1359      return;1360    }1361    const signal = request.cacheController.signal;1362    signal.removeEventListener('abort', abortStream);1363    const reason = signal.reason;1364    if (request.type === PRERENDER) {1365      request.abortableTasks.delete(streamTask);1366      haltTask(streamTask, request);1367      finishHaltedTask(streamTask, request);1368    } else {1369      // TODO: Make this use abortTask() instead.1370      erroredTask(request, streamTask, reason);1371      enqueueFlush(request);1372    }1373    // $FlowFixMe[incompatible-use] should be able to pass mixed1374    reader.cancel(reason).then(error, error);1375  }13761377  request.cacheController.signal.addEventListener('abort', abortStream);1378  reader.read().then(progress, error);1379  return serializeByValueID(streamTask.id);1380}13811382function serializeAsyncIterable(1383  request: Request,1384  task: Task,1385  iterable: $AsyncIterable<ReactClientValue, ReactClientValue, void>,1386  iterator: $AsyncIterator<ReactClientValue, ReactClientValue, void>,1387): string {1388  // Generators/Iterators are Iterables but they're also their own iterator1389  // functions. If that's the case, we treat them as single-shot. Otherwise,1390  // we assume that this iterable might be a multi-shot and allow it to be1391  // iterated more than once on the client.1392  const isIterator = iterable === iterator;13931394  // This task won't actually be retried. We just use it to attempt synchronous renders.1395  const streamTask = createTask(1396    request,1397    task.model,1398    task.keyPath,1399    task.implicitSlot,1400    task.formatContext,1401    request.abortableTasks,1402    enableProfilerTimer ? task.time : 0,1403    __DEV__ ? task.debugOwner : null,1404    __DEV__ ? task.debugStack : null,1405    __DEV__ ? task.debugTask : null,1406  );14071408  if (__DEV__) {1409    const debugInfo: ?ReactDebugInfo = (iterable as any)._debugInfo;1410    if (debugInfo) {1411      forwardDebugInfo(request, streamTask, debugInfo);1412    }1413  }14141415  // The task represents the Stop row. This adds a Start row.1416  request.pendingChunks++;1417  const startStreamRow =1418    streamTask.id.toString(16) + ':' + (isIterator ? 'x' : 'X') + '\n';1419  request.completedRegularChunks.push(stringToChunk(startStreamRow));14201421  function progress(1422    entry:1423      | {done: false, +value: ReactClientValue, ...}1424      | {done: true, +value: ReactClientValue, ...},1425  ) {1426    if (streamTask.status !== PENDING) {1427      return;1428    }14291430    if (entry.done) {1431      streamTask.status = COMPLETED;1432      let endStreamRow;1433      if (entry.value === undefined) {1434        endStreamRow = streamTask.id.toString(16) + ':C\n';1435      } else {1436        // Unlike streams, the last value may not be undefined. If it's not1437        // we outline it and encode a reference to it in the closing instruction.1438        try {1439          const chunkId = outlineModel(request, entry.value);1440          endStreamRow =1441            streamTask.id.toString(16) +1442            ':C' +1443            stringify(serializeByValueID(chunkId)) +1444            '\n';1445        } catch (x) {1446          error(x);1447          return;1448        }1449      }1450      request.completedRegularChunks.push(stringToChunk(endStreamRow));1451      request.abortableTasks.delete(streamTask);1452      request.cacheController.signal.removeEventListener(1453        'abort',1454        abortIterable,1455      );1456      enqueueFlush(request);1457      callOnAllReadyIfReady(request);1458    } else {1459      try {1460        streamTask.model = entry.value;1461        request.pendingChunks++;1462        tryStreamTask(request, streamTask);1463        enqueueFlush(request);1464        if (__DEV__) {1465          callIteratorInDEV(iterator, progress, error);1466        } else {1467          iterator.next().then(progress, error);1468        }1469      } catch (x) {1470        error(x);1471        return;1472      }1473    }1474  }1475  function error(reason: mixed) {1476    if (streamTask.status !== PENDING) {1477      return;1478    }1479    request.cacheController.signal.removeEventListener('abort', abortIterable);1480    erroredTask(request, streamTask, reason);1481    enqueueFlush(request);1482    if (typeof (iterator as any).throw === 'function') {1483      // The iterator protocol doesn't necessarily include this but a generator do.1484      // $FlowFixMe[prop-missing] should be able to pass mixed1485      iterator.throw(reason).then(noop, noop);1486    }1487  }1488  function abortIterable() {1489    if (streamTask.status !== PENDING) {1490      return;1491    }1492    const signal = request.cacheController.signal;1493    signal.removeEventListener('abort', abortIterable);1494    const reason = signal.reason;1495    if (request.type === PRERENDER) {1496      request.abortableTasks.delete(streamTask);1497      haltTask(streamTask, request);1498      finishHaltedTask(streamTask, request);1499    } else {1500      // TODO: Make this use abortTask() instead.1501      erroredTask(request, streamTask, signal.reason);1502      enqueueFlush(request);1503    }1504    if (typeof (iterator as any).throw === 'function') {1505      // TODO: Premature exits should call return() on the iterator if it exists1506      // to allow cleanup. See https://tc39.es/ecma262/multipage/control-abstraction-objects.html#table-async-iterator-optional1507      // The iterator protocol doesn't necessarily include this but a generator do.1508      // $FlowFixMe[prop-missing] should be able to pass mixed1509      iterator.throw(reason).then(noop, noop);1510    }1511  }1512  request.cacheController.signal.addEventListener('abort', abortIterable);1513  if (__DEV__) {1514    callIteratorInDEV(iterator, progress, error);1515  } else {1516    iterator.next().then(progress, error);1517  }1518  return serializeByValueID(streamTask.id);1519}15201521export function emitHint<Code: HintCode>(1522  request: Request,1523  code: Code,1524  model: HintModel<Code>,1525): void {1526  emitHintChunk(request, code, model);1527  enqueueFlush(request);1528}15291530export function getHints(request: Request): Hints {1531  return request.hints;1532}15331534export function getCache(request: Request): Map<Function, mixed> {1535  return request.cache;1536}15371538function readThenable<T>(thenable: Thenable<T>): T {1539  if (thenable.status === 'fulfilled') {1540    return thenable.value;1541  } else if (thenable.status === 'rejected') {1542    throw thenable.reason;1543  }1544  throw thenable;1545}15461547function createLazyWrapperAroundWakeable(1548  request: Request,1549  task: Task,1550  wakeable: Wakeable,1551) {1552  // This is a temporary fork of the `use` implementation until we accept1553  // promises everywhere.1554  const thenable: Thenable<mixed> = wakeable as any;1555  switch (thenable.status) {1556    case 'fulfilled': {1557      forwardDebugInfoFromThenable(request, task, thenable, null, null);1558      return thenable.value;1559    }1560    case 'rejected':1561      forwardDebugInfoFromThenable(request, task, thenable, null, null);1562      break;1563    default: {1564      if (typeof thenable.status === 'string') {1565        // Only instrument the thenable if the status if not defined. If1566        // it's defined, but an unknown value, assume it's been instrumented by1567        // some custom userspace implementation. We treat it as "pending".1568        break;1569      }1570      const pendingThenable: PendingThenable<mixed> = thenable as any;1571      pendingThenable.status = 'pending';1572      pendingThenable.then(1573        fulfilledValue => {1574          forwardDebugInfoFromCurrentContext(request, task, thenable);1575          if (thenable.status === 'pending') {1576            const fulfilledThenable: FulfilledThenable<mixed> = thenable as any;1577            fulfilledThenable.status = 'fulfilled';1578            fulfilledThenable.value = fulfilledValue;1579          }1580        },1581        (error: mixed) => {1582          forwardDebugInfoFromCurrentContext(request, task, thenable);1583          if (thenable.status === 'pending') {1584            const rejectedThenable: RejectedThenable<mixed> = thenable as any;1585            rejectedThenable.status = 'rejected';1586            rejectedThenable.reason = error;1587          }1588        },1589      );1590      break;1591    }1592  }1593  const lazyType: LazyComponent<any, Thenable<any>> = {1594    $$typeof: REACT_LAZY_TYPE,1595    _payload: thenable,1596    _init: readThenable,1597  };1598  return lazyType;1599}16001601function callWithDebugContextInDEV<A, T>(1602  request: Request,1603  task: Task,1604  callback: A => T,1605  arg: A,1606): T {1607  // We don't have a Server Component instance associated with this callback and1608  // the nearest context is likely a Client Component being serialized. We create1609  // a fake owner during this callback so we can get the stack trace from it.1610  // This also gets sent to the client as the owner for the replaying log.1611  const componentDebugInfo: ReactComponentInfo = {1612    name: '',1613    env: task.environmentName,1614    key: null,1615    owner: task.debugOwner,1616  };1617  // $FlowFixMe[cannot-write]1618  componentDebugInfo.stack =1619    task.debugStack === null1620      ? null1621      : filterStackTrace(request, parseStackTrace(task.debugStack, 1));1622  // $FlowFixMe[cannot-write]1623  componentDebugInfo.debugStack = task.debugStack;1624  // $FlowFixMe[cannot-write]1625  componentDebugInfo.debugTask = task.debugTask;1626  const debugTask = task.debugTask;1627  // We don't need the async component storage context here so we only set the1628  // synchronous tracking of owner.1629  setCurrentOwner(componentDebugInfo);1630  try {1631    if (debugTask) {1632      return debugTask.run(callback.bind(null, arg));1633    }1634    return callback(arg);1635  } finally {1636    setCurrentOwner(null);1637  }1638}16391640const voidHandler = () => {};16411642function processServerComponentReturnValue(1643  request: Request,1644  task: Task,1645  Component: any,1646  result: any,1647): any {1648  // A Server Component's return value has a few special properties due to being1649  // in the return position of a Component. We convert them here.1650  if (1651    typeof result !== 'object' ||1652    result === null ||1653    isClientReference(result)1654  ) {1655    return result;1656  }16571658  if (typeof result.then === 'function') {1659    // When the return value is in children position we can resolve it immediately,1660    // to its value without a wrapper if it's synchronously available.1661    const thenable: Thenable<any> = result;1662    if (__DEV__) {1663      // If the thenable resolves to an element, then it was in a static position,1664      // the return value of a Server Component. That doesn't need further validation1665      // of keys. The Server Component itself would have had a key.1666      thenable.then(resolvedValue => {1667        if (1668          typeof resolvedValue === 'object' &&1669          resolvedValue !== null &&1670          resolvedValue.$$typeof === REACT_ELEMENT_TYPE1671        ) {1672          resolvedValue._store.validated = 1;1673        }1674      }, voidHandler);1675    }1676    // TODO: Once we accept Promises as children on the client, we can just return1677    // the thenable here.1678    return createLazyWrapperAroundWakeable(request, task, result);1679  }16801681  if (__DEV__) {1682    if ((result as any).$$typeof === REACT_ELEMENT_TYPE) {1683      // If the server component renders to an element, then it was in a static position.1684      // That doesn't need further validation of keys. The Server Component itself would1685      // have had a key.1686      (result as any)._store.validated = 1;1687    }1688  }16891690  // Normally we'd serialize an Iterator/AsyncIterator as a single-shot which is not compatible1691  // to be rendered as a React Child. However, because we have the function to recreate1692  // an iterable from rendering the element again, we can effectively treat it as multi-1693  // shot. Therefore we treat this as an Iterable/AsyncIterable, whether it was one or not, by1694  // adding a wrapper so that this component effectively renders down to an AsyncIterable.1695  const iteratorFn = getIteratorFn(result);1696  if (iteratorFn) {1697    const iterableChild = result;1698    const multiShot = {1699      [Symbol.iterator]: function () {1700        const iterator = iteratorFn.call(iterableChild);1701        if (__DEV__) {1702          // If this was an Iterator but not a GeneratorFunction we warn because1703          // it might have been a mistake. Technically you can make this mistake with1704          // GeneratorFunctions and even single-shot Iterables too but it's extra1705          // tempting to try to return the value from a generator.1706          if (iterator === iterableChild) {1707            const isGeneratorComponent =1708              // $FlowFixMe[method-unbinding]1709              Object.prototype.toString.call(Component) ===1710                '[object GeneratorFunction]' &&1711              // $FlowFixMe[method-unbinding]1712              Object.prototype.toString.call(iterableChild) ===1713                '[object Generator]';1714            if (!isGeneratorComponent) {1715              callWithDebugContextInDEV(request, task, () => {1716                console.error(1717                  'Returning an Iterator from a Server Component is not supported ' +1718                    'since it cannot be looped over more than once. ',1719                );1720              });1721            }1722          }1723        }1724        return iterator as any;1725      },1726    };1727    if (__DEV__) {1728      (multiShot as any)._debugInfo = iterableChild._debugInfo;1729    }1730    return multiShot;1731  }1732  if (1733    typeof (result as any)[ASYNC_ITERATOR] === 'function' &&1734    (typeof ReadableStream !== 'function' ||1735      !(result instanceof ReadableStream))1736  ) {1737    const iterableChild = result;1738    const multishot = {1739      [ASYNC_ITERATOR]: function () {1740        const iterator = (iterableChild as any)[ASYNC_ITERATOR]();1741        if (__DEV__) {1742          // If this was an AsyncIterator but not an AsyncGeneratorFunction we warn because1743          // it might have been a mistake. Technically you can make this mistake with1744          // AsyncGeneratorFunctions and even single-shot AsyncIterables too but it's extra1745          // tempting to try to return the value from a generator.1746          if (iterator === iterableChild) {1747            const isGeneratorComponent =1748              // $FlowFixMe[method-unbinding]1749              Object.prototype.toString.call(Component) ===1750                '[object AsyncGeneratorFunction]' &&1751              // $FlowFixMe[method-unbinding]1752              Object.prototype.toString.call(iterableChild) ===1753                '[object AsyncGenerator]';1754            if (!isGeneratorComponent) {1755              callWithDebugContextInDEV(request, task, () => {1756                console.error(1757                  'Returning an AsyncIterator from a Server Component is not supported ' +1758                    'since it cannot be looped over more than once. ',1759                );1760              });1761            }1762          }1763        }1764        return iterator;1765      },1766    };1767    if (__DEV__) {1768      (multishot as any)._debugInfo = iterableChild._debugInfo;1769    }1770    return multishot;1771  }1772  return result;1773}17741775function renderFunctionComponent<Props>(1776  request: Request,1777  task: Task,1778  key: ReactKey,1779  Component: (p: Props, arg: void) => any,1780  props: Props,1781  validated: number, // DEV-only1782): ReactJSONValue {1783  // Reset the task's thenable state before continuing, so that if a later1784  // component suspends we can reuse the same task object. If the same1785  // component suspends again, the thenable state will be restored.1786  const prevThenableState = task.thenableState;1787  task.thenableState = null;17881789  let result;17901791  let componentDebugInfo: ReactComponentInfo;1792  if (__DEV__) {1793    if (!canEmitDebugInfo) {1794      // We don't have a chunk to assign debug info. We need to outline this1795      // component to assign it an ID.1796      return outlineTask(request, task);1797    } else if (prevThenableState !== null) {1798      // This is a replay and we've already emitted the debug info of this component1799      // in the first pass. We skip emitting a duplicate line.1800      // As a hack we stashed the previous component debug info on this object in DEV.1801      componentDebugInfo = (prevThenableState as any)._componentDebugInfo;1802    } else {1803      // This is a new component in the same task so we can emit more debug info.1804      const componentDebugID = task.id;1805      const componentName =1806        (Component as any).displayName || Component.name || '';1807      const componentEnv = (0, request.environmentName)();1808      request.pendingChunks++;1809      componentDebugInfo = {1810        name: componentName,1811        env: componentEnv,1812        key: key,1813        owner: task.debugOwner,1814      } as ReactComponentInfo;1815      // $FlowFixMe[cannot-write]1816      componentDebugInfo.stack =1817        task.debugStack === null1818          ? null1819          : filterStackTrace(request, parseStackTrace(task.debugStack, 1));1820      // $FlowFixMe[cannot-write]1821      componentDebugInfo.props = props;1822      // $FlowFixMe[cannot-write]1823      componentDebugInfo.debugStack = task.debugStack;1824      // $FlowFixMe[cannot-write]1825      componentDebugInfo.debugTask = task.debugTask;18261827      // We outline this model eagerly so that we can refer to by reference as an owner.1828      // If we had a smarter way to dedupe we might not have to do this if there ends up1829      // being no references to this as an owner.18301831      outlineComponentInfo(request, componentDebugInfo);18321833      // Track when we started rendering this component.1834      if (enableProfilerTimer) {1835        advanceTaskTime(request, task, performance.now());1836      }18371838      emitDebugChunk(request, componentDebugID, componentDebugInfo);18391840      // We've emitted the latest environment for this task so we track that.1841      task.environmentName = componentEnv;18421843      if (validated === 2) {1844        warnForMissingKey(request, key, componentDebugInfo, task.debugTask);1845      }1846    }1847    prepareToUseHooksForComponent(prevThenableState, componentDebugInfo);1848    // $FlowFixMe[constant-condition]1849    if (supportsComponentStorage) {1850      // Run the component in an Async Context that tracks the current owner.1851      if (task.debugTask) {1852        result = task.debugTask.run(1853          // $FlowFixMe[method-unbinding]1854          componentStorage.run.bind(1855            componentStorage,1856            componentDebugInfo,1857            callComponentInDEV,1858            Component,1859            props,1860            componentDebugInfo,1861          ),1862        );1863      } else {1864        result = componentStorage.run(1865          componentDebugInfo,1866          callComponentInDEV,1867          Component,1868          props,1869          componentDebugInfo,1870        );1871      }1872    } else {1873      if (task.debugTask) {1874        result = task.debugTask.run(1875          callComponentInDEV.bind(null, Component, props, componentDebugInfo),1876        );1877      } else {1878        result = callComponentInDEV(Component, props, componentDebugInfo);1879      }1880    }1881  } else {1882    componentDebugInfo = null as any;1883    prepareToUseHooksForComponent(prevThenableState, null);1884    // The secondArg is always undefined in Server Components since refs error early.1885    const secondArg = undefined;1886    result = Component(props, secondArg);1887  }18881889  if (request.status === ABORTING) {1890    if (1891      typeof result === 'object' &&1892      // $FlowFixMe[invalid-compare]1893      result !== null &&1894      typeof result.then === 'function' &&1895      !isClientReference(result)1896    ) {1897      result.then(voidHandler, voidHandler);1898    }1899    // If we aborted during rendering we should interrupt the render but1900    // we don't need to provide an error because the renderer will encode1901    // the abort error as the reason.1902    // eslint-disable-next-line no-throw-literal1903    throw null;1904  }19051906  if (__DEV__ || enableProfilerTimer) {1907    // Forward any debug information for any Promises that we use():ed during the render.1908    // We do this at the end so that we don't keep doing this for each retry.1909    const trackedThenables = getTrackedThenablesAfterRendering();1910    if (trackedThenables !== null) {1911      const stacks: Array<Error> = __DEV__1912        ? (trackedThenables as any)._stacks ||1913          ((trackedThenables as any)._stacks = [])1914        : (null as any);1915      for (let i = 0; i < trackedThenables.length; i++) {1916        const stack = __DEV__ ? stacks[i] : null;1917        forwardDebugInfoFromThenable(1918          request,1919          task,1920          trackedThenables[i],1921          __DEV__ ? componentDebugInfo : null,1922          stack,1923        );1924      }1925    }1926  }19271928  // Apply special cases.1929  result = processServerComponentReturnValue(request, task, Component, result);19301931  if (__DEV__) {1932    // From this point on, the parent is the component we just rendered until we1933    // hit another JSX element.1934    task.debugOwner = componentDebugInfo;1935    // Unfortunately, we don't have a stack frame for this position. Conceptually1936    // it would be the location of the `return` inside component that just rendered.1937    task.debugStack = null;1938    task.debugTask = null;1939  }19401941  // Track this element's key on the Server Component on the keyPath context..1942  const prevKeyPath = task.keyPath;1943  const prevImplicitSlot = task.implicitSlot;1944  if (key !== null) {1945    // Append the key to the path. Technically a null key should really add the child1946    // index. We don't do that to hold the payload small and implementation simple.1947    if (key === REACT_OPTIMISTIC_KEY || prevKeyPath === REACT_OPTIMISTIC_KEY) {1948      // The optimistic key is viral. It turns the whole key into optimistic if any part is.1949      task.keyPath = REACT_OPTIMISTIC_KEY;1950    } else {1951      task.keyPath = prevKeyPath === null ? key : prevKeyPath + ',' + key;1952    }1953  } else if (prevKeyPath === null) {1954    // This sequence of Server Components has no keys. This means that it was rendered1955    // in a slot that needs to assign an implicit key. Even if children below have1956    // explicit keys, they should not be used for the outer most key since it might1957    // collide with other slots in that set.1958    task.implicitSlot = true;1959  }1960  const json = renderModelDestructive(request, task, emptyRoot, '', result);1961  task.keyPath = prevKeyPath;1962  task.implicitSlot = prevImplicitSlot;1963  return json;1964}19651966function warnForMissingKey(1967  request: Request,1968  key: ReactKey,1969  componentDebugInfo: ReactComponentInfo,1970  debugTask: null | ConsoleTask,1971): void {1972  if (__DEV__) {1973    let didWarnForKey = request.didWarnForKey;1974    if (didWarnForKey == null) {1975      didWarnForKey = request.didWarnForKey = new WeakSet();1976    }1977    const parentOwner = componentDebugInfo.owner;1978    if (parentOwner != null) {1979      if (didWarnForKey.has(parentOwner)) {1980        // We already warned for other children in this parent.1981        return;1982      }1983      didWarnForKey.add(parentOwner);1984    }19851986    // Call with the server component as the currently rendering component1987    // for context.1988    const logKeyError = () => {1989      console.error(1990        'Each child in a list should have a unique "key" prop.' +1991          '%s%s See https://react.dev/link/warning-keys for more information.',1992        '',1993        '',1994      );1995    };19961997    // $FlowFixMe[constant-condition]1998    if (supportsComponentStorage) {1999      // Run the component in an Async Context that tracks the current owner.2000      if (debugTask) {

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.

Generated by searchcode.com · powered by code_get_file + code_get_findings