1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @flow
    
  8.  */
    
  9. 
    
  10. import {
    
  11.   ComponentFilterDisplayName,
    
  12.   ComponentFilterElementType,
    
  13.   ComponentFilterHOC,
    
  14.   ComponentFilterLocation,
    
  15.   ElementTypeClass,
    
  16.   ElementTypeContext,
    
  17.   ElementTypeFunction,
    
  18.   ElementTypeForwardRef,
    
  19.   ElementTypeHostComponent,
    
  20.   ElementTypeMemo,
    
  21.   ElementTypeOtherOrUnknown,
    
  22.   ElementTypeProfiler,
    
  23.   ElementTypeRoot,
    
  24.   ElementTypeSuspense,
    
  25.   ElementTypeSuspenseList,
    
  26.   ElementTypeTracingMarker,
    
  27.   StrictMode,
    
  28. } from 'react-devtools-shared/src/frontend/types';
    
  29. import {
    
  30.   deletePathInObject,
    
  31.   getDisplayName,
    
  32.   getWrappedDisplayName,
    
  33.   getDefaultComponentFilters,
    
  34.   getInObject,
    
  35.   getUID,
    
  36.   renamePathInObject,
    
  37.   setInObject,
    
  38.   utfEncodeString,
    
  39. } from 'react-devtools-shared/src/utils';
    
  40. import {sessionStorageGetItem} from 'react-devtools-shared/src/storage';
    
  41. import {
    
  42.   gt,
    
  43.   gte,
    
  44.   serializeToString,
    
  45. } from 'react-devtools-shared/src/backend/utils';
    
  46. import {
    
  47.   cleanForBridge,
    
  48.   copyWithDelete,
    
  49.   copyWithRename,
    
  50.   copyWithSet,
    
  51.   getEffectDurations,
    
  52. } from './utils';
    
  53. import {
    
  54.   __DEBUG__,
    
  55.   PROFILING_FLAG_BASIC_SUPPORT,
    
  56.   PROFILING_FLAG_TIMELINE_SUPPORT,
    
  57.   SESSION_STORAGE_RELOAD_AND_PROFILE_KEY,
    
  58.   SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY,
    
  59.   TREE_OPERATION_ADD,
    
  60.   TREE_OPERATION_REMOVE,
    
  61.   TREE_OPERATION_REMOVE_ROOT,
    
  62.   TREE_OPERATION_REORDER_CHILDREN,
    
  63.   TREE_OPERATION_SET_SUBTREE_MODE,
    
  64.   TREE_OPERATION_UPDATE_ERRORS_OR_WARNINGS,
    
  65.   TREE_OPERATION_UPDATE_TREE_BASE_DURATION,
    
  66. } from '../constants';
    
  67. import {inspectHooksOfFiber} from 'react-debug-tools';
    
  68. import {
    
  69.   patchConsoleUsingWindowValues,
    
  70.   registerRenderer as registerRendererWithConsole,
    
  71.   patchForStrictMode as patchConsoleForStrictMode,
    
  72.   unpatchForStrictMode as unpatchConsoleForStrictMode,
    
  73. } from './console';
    
  74. import {
    
  75.   CONCURRENT_MODE_NUMBER,
    
  76.   CONCURRENT_MODE_SYMBOL_STRING,
    
  77.   DEPRECATED_ASYNC_MODE_SYMBOL_STRING,
    
  78.   PROVIDER_NUMBER,
    
  79.   PROVIDER_SYMBOL_STRING,
    
  80.   CONTEXT_NUMBER,
    
  81.   CONTEXT_SYMBOL_STRING,
    
  82.   STRICT_MODE_NUMBER,
    
  83.   STRICT_MODE_SYMBOL_STRING,
    
  84.   PROFILER_NUMBER,
    
  85.   PROFILER_SYMBOL_STRING,
    
  86.   SCOPE_NUMBER,
    
  87.   SCOPE_SYMBOL_STRING,
    
  88.   FORWARD_REF_NUMBER,
    
  89.   FORWARD_REF_SYMBOL_STRING,
    
  90.   MEMO_NUMBER,
    
  91.   MEMO_SYMBOL_STRING,
    
  92.   SERVER_CONTEXT_SYMBOL_STRING,
    
  93. } from './ReactSymbols';
    
  94. import {format} from './utils';
    
  95. import {enableStyleXFeatures} from 'react-devtools-feature-flags';
    
  96. import is from 'shared/objectIs';
    
  97. import hasOwnProperty from 'shared/hasOwnProperty';
    
  98. import {getStyleXData} from './StyleX/utils';
    
  99. import {createProfilingHooks} from './profilingHooks';
    
  100. 
    
  101. import type {GetTimelineData, ToggleProfilingStatus} from './profilingHooks';
    
  102. import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
    
  103. import type {
    
  104.   ChangeDescription,
    
  105.   CommitDataBackend,
    
  106.   DevToolsHook,
    
  107.   InspectedElement,
    
  108.   InspectedElementPayload,
    
  109.   InstanceAndStyle,
    
  110.   NativeType,
    
  111.   PathFrame,
    
  112.   PathMatch,
    
  113.   ProfilingDataBackend,
    
  114.   ProfilingDataForRootBackend,
    
  115.   ReactRenderer,
    
  116.   RendererInterface,
    
  117.   SerializedElement,
    
  118.   WorkTagMap,
    
  119. } from './types';
    
  120. import type {
    
  121.   ComponentFilter,
    
  122.   ElementType,
    
  123.   Plugins,
    
  124. } from 'react-devtools-shared/src/frontend/types';
    
  125. 
    
  126. type getDisplayNameForFiberType = (fiber: Fiber) => string | null;
    
  127. type getTypeSymbolType = (type: any) => symbol | number;
    
  128. 
    
  129. type ReactPriorityLevelsType = {
    
  130.   ImmediatePriority: number,
    
  131.   UserBlockingPriority: number,
    
  132.   NormalPriority: number,
    
  133.   LowPriority: number,
    
  134.   IdlePriority: number,
    
  135.   NoPriority: number,
    
  136. };
    
  137. 
    
  138. function getFiberFlags(fiber: Fiber): number {
    
  139.   // The name of this field changed from "effectTag" to "flags"
    
  140.   return fiber.flags !== undefined ? fiber.flags : (fiber: any).effectTag;
    
  141. }
    
  142. 
    
  143. // Some environments (e.g. React Native / Hermes) don't support the performance API yet.
    
  144. const getCurrentTime =
    
  145.   // $FlowFixMe[method-unbinding]
    
  146.   typeof performance === 'object' && typeof performance.now === 'function'
    
  147.     ? () => performance.now()
    
  148.     : () => Date.now();
    
  149. 
    
  150. export function getInternalReactConstants(version: string): {
    
  151.   getDisplayNameForFiber: getDisplayNameForFiberType,
    
  152.   getTypeSymbol: getTypeSymbolType,
    
  153.   ReactPriorityLevels: ReactPriorityLevelsType,
    
  154.   ReactTypeOfWork: WorkTagMap,
    
  155.   StrictModeBits: number,
    
  156. } {
    
  157.   // **********************************************************
    
  158.   // The section below is copied from files in React repo.
    
  159.   // Keep it in sync, and add version guards if it changes.
    
  160.   //
    
  161.   // Technically these priority levels are invalid for versions before 16.9,
    
  162.   // but 16.9 is the first version to report priority level to DevTools,
    
  163.   // so we can avoid checking for earlier versions and support pre-16.9 canary releases in the process.
    
  164.   let ReactPriorityLevels: ReactPriorityLevelsType = {
    
  165.     ImmediatePriority: 99,
    
  166.     UserBlockingPriority: 98,
    
  167.     NormalPriority: 97,
    
  168.     LowPriority: 96,
    
  169.     IdlePriority: 95,
    
  170.     NoPriority: 90,
    
  171.   };
    
  172. 
    
  173.   if (gt(version, '17.0.2')) {
    
  174.     ReactPriorityLevels = {
    
  175.       ImmediatePriority: 1,
    
  176.       UserBlockingPriority: 2,
    
  177.       NormalPriority: 3,
    
  178.       LowPriority: 4,
    
  179.       IdlePriority: 5,
    
  180.       NoPriority: 0,
    
  181.     };
    
  182.   }
    
  183. 
    
  184.   let StrictModeBits = 0;
    
  185.   if (gte(version, '18.0.0-alpha')) {
    
  186.     // 18+
    
  187.     StrictModeBits = 0b011000;
    
  188.   } else if (gte(version, '16.9.0')) {
    
  189.     // 16.9 - 17
    
  190.     StrictModeBits = 0b1;
    
  191.   } else if (gte(version, '16.3.0')) {
    
  192.     // 16.3 - 16.8
    
  193.     StrictModeBits = 0b10;
    
  194.   }
    
  195. 
    
  196.   let ReactTypeOfWork: WorkTagMap = ((null: any): WorkTagMap);
    
  197. 
    
  198.   // **********************************************************
    
  199.   // The section below is copied from files in React repo.
    
  200.   // Keep it in sync, and add version guards if it changes.
    
  201.   //
    
  202.   // TODO Update the gt() check below to be gte() whichever the next version number is.
    
  203.   // Currently the version in Git is 17.0.2 (but that version has not been/may not end up being released).
    
  204.   if (gt(version, '17.0.1')) {
    
  205.     ReactTypeOfWork = {
    
  206.       CacheComponent: 24, // Experimental
    
  207.       ClassComponent: 1,
    
  208.       ContextConsumer: 9,
    
  209.       ContextProvider: 10,
    
  210.       CoroutineComponent: -1, // Removed
    
  211.       CoroutineHandlerPhase: -1, // Removed
    
  212.       DehydratedSuspenseComponent: 18, // Behind a flag
    
  213.       ForwardRef: 11,
    
  214.       Fragment: 7,
    
  215.       FunctionComponent: 0,
    
  216.       HostComponent: 5,
    
  217.       HostPortal: 4,
    
  218.       HostRoot: 3,
    
  219.       HostHoistable: 26, // In reality, 18.2+. But doesn't hurt to include it here
    
  220.       HostSingleton: 27, // Same as above
    
  221.       HostText: 6,
    
  222.       IncompleteClassComponent: 17,
    
  223.       IndeterminateComponent: 2,
    
  224.       LazyComponent: 16,
    
  225.       LegacyHiddenComponent: 23,
    
  226.       MemoComponent: 14,
    
  227.       Mode: 8,
    
  228.       OffscreenComponent: 22, // Experimental
    
  229.       Profiler: 12,
    
  230.       ScopeComponent: 21, // Experimental
    
  231.       SimpleMemoComponent: 15,
    
  232.       SuspenseComponent: 13,
    
  233.       SuspenseListComponent: 19, // Experimental
    
  234.       TracingMarkerComponent: 25, // Experimental - This is technically in 18 but we don't
    
  235.       // want to fork again so we're adding it here instead
    
  236.       YieldComponent: -1, // Removed
    
  237.     };
    
  238.   } else if (gte(version, '17.0.0-alpha')) {
    
  239.     ReactTypeOfWork = {
    
  240.       CacheComponent: -1, // Doesn't exist yet
    
  241.       ClassComponent: 1,
    
  242.       ContextConsumer: 9,
    
  243.       ContextProvider: 10,
    
  244.       CoroutineComponent: -1, // Removed
    
  245.       CoroutineHandlerPhase: -1, // Removed
    
  246.       DehydratedSuspenseComponent: 18, // Behind a flag
    
  247.       ForwardRef: 11,
    
  248.       Fragment: 7,
    
  249.       FunctionComponent: 0,
    
  250.       HostComponent: 5,
    
  251.       HostPortal: 4,
    
  252.       HostRoot: 3,
    
  253.       HostHoistable: -1, // Doesn't exist yet
    
  254.       HostSingleton: -1, // Doesn't exist yet
    
  255.       HostText: 6,
    
  256.       IncompleteClassComponent: 17,
    
  257.       IndeterminateComponent: 2,
    
  258.       LazyComponent: 16,
    
  259.       LegacyHiddenComponent: 24,
    
  260.       MemoComponent: 14,
    
  261.       Mode: 8,
    
  262.       OffscreenComponent: 23, // Experimental
    
  263.       Profiler: 12,
    
  264.       ScopeComponent: 21, // Experimental
    
  265.       SimpleMemoComponent: 15,
    
  266.       SuspenseComponent: 13,
    
  267.       SuspenseListComponent: 19, // Experimental
    
  268.       TracingMarkerComponent: -1, // Doesn't exist yet
    
  269.       YieldComponent: -1, // Removed
    
  270.     };
    
  271.   } else if (gte(version, '16.6.0-beta.0')) {
    
  272.     ReactTypeOfWork = {
    
  273.       CacheComponent: -1, // Doesn't exist yet
    
  274.       ClassComponent: 1,
    
  275.       ContextConsumer: 9,
    
  276.       ContextProvider: 10,
    
  277.       CoroutineComponent: -1, // Removed
    
  278.       CoroutineHandlerPhase: -1, // Removed
    
  279.       DehydratedSuspenseComponent: 18, // Behind a flag
    
  280.       ForwardRef: 11,
    
  281.       Fragment: 7,
    
  282.       FunctionComponent: 0,
    
  283.       HostComponent: 5,
    
  284.       HostPortal: 4,
    
  285.       HostRoot: 3,
    
  286.       HostHoistable: -1, // Doesn't exist yet
    
  287.       HostSingleton: -1, // Doesn't exist yet
    
  288.       HostText: 6,
    
  289.       IncompleteClassComponent: 17,
    
  290.       IndeterminateComponent: 2,
    
  291.       LazyComponent: 16,
    
  292.       LegacyHiddenComponent: -1,
    
  293.       MemoComponent: 14,
    
  294.       Mode: 8,
    
  295.       OffscreenComponent: -1, // Experimental
    
  296.       Profiler: 12,
    
  297.       ScopeComponent: -1, // Experimental
    
  298.       SimpleMemoComponent: 15,
    
  299.       SuspenseComponent: 13,
    
  300.       SuspenseListComponent: 19, // Experimental
    
  301.       TracingMarkerComponent: -1, // Doesn't exist yet
    
  302.       YieldComponent: -1, // Removed
    
  303.     };
    
  304.   } else if (gte(version, '16.4.3-alpha')) {
    
  305.     ReactTypeOfWork = {
    
  306.       CacheComponent: -1, // Doesn't exist yet
    
  307.       ClassComponent: 2,
    
  308.       ContextConsumer: 11,
    
  309.       ContextProvider: 12,
    
  310.       CoroutineComponent: -1, // Removed
    
  311.       CoroutineHandlerPhase: -1, // Removed
    
  312.       DehydratedSuspenseComponent: -1, // Doesn't exist yet
    
  313.       ForwardRef: 13,
    
  314.       Fragment: 9,
    
  315.       FunctionComponent: 0,
    
  316.       HostComponent: 7,
    
  317.       HostPortal: 6,
    
  318.       HostRoot: 5,
    
  319.       HostHoistable: -1, // Doesn't exist yet
    
  320.       HostSingleton: -1, // Doesn't exist yet
    
  321.       HostText: 8,
    
  322.       IncompleteClassComponent: -1, // Doesn't exist yet
    
  323.       IndeterminateComponent: 4,
    
  324.       LazyComponent: -1, // Doesn't exist yet
    
  325.       LegacyHiddenComponent: -1,
    
  326.       MemoComponent: -1, // Doesn't exist yet
    
  327.       Mode: 10,
    
  328.       OffscreenComponent: -1, // Experimental
    
  329.       Profiler: 15,
    
  330.       ScopeComponent: -1, // Experimental
    
  331.       SimpleMemoComponent: -1, // Doesn't exist yet
    
  332.       SuspenseComponent: 16,
    
  333.       SuspenseListComponent: -1, // Doesn't exist yet
    
  334.       TracingMarkerComponent: -1, // Doesn't exist yet
    
  335.       YieldComponent: -1, // Removed
    
  336.     };
    
  337.   } else {
    
  338.     ReactTypeOfWork = {
    
  339.       CacheComponent: -1, // Doesn't exist yet
    
  340.       ClassComponent: 2,
    
  341.       ContextConsumer: 12,
    
  342.       ContextProvider: 13,
    
  343.       CoroutineComponent: 7,
    
  344.       CoroutineHandlerPhase: 8,
    
  345.       DehydratedSuspenseComponent: -1, // Doesn't exist yet
    
  346.       ForwardRef: 14,
    
  347.       Fragment: 10,
    
  348.       FunctionComponent: 1,
    
  349.       HostComponent: 5,
    
  350.       HostPortal: 4,
    
  351.       HostRoot: 3,
    
  352.       HostHoistable: -1, // Doesn't exist yet
    
  353.       HostSingleton: -1, // Doesn't exist yet
    
  354.       HostText: 6,
    
  355.       IncompleteClassComponent: -1, // Doesn't exist yet
    
  356.       IndeterminateComponent: 0,
    
  357.       LazyComponent: -1, // Doesn't exist yet
    
  358.       LegacyHiddenComponent: -1,
    
  359.       MemoComponent: -1, // Doesn't exist yet
    
  360.       Mode: 11,
    
  361.       OffscreenComponent: -1, // Experimental
    
  362.       Profiler: 15,
    
  363.       ScopeComponent: -1, // Experimental
    
  364.       SimpleMemoComponent: -1, // Doesn't exist yet
    
  365.       SuspenseComponent: 16,
    
  366.       SuspenseListComponent: -1, // Doesn't exist yet
    
  367.       TracingMarkerComponent: -1, // Doesn't exist yet
    
  368.       YieldComponent: 9,
    
  369.     };
    
  370.   }
    
  371.   // **********************************************************
    
  372.   // End of copied code.
    
  373.   // **********************************************************
    
  374. 
    
  375.   function getTypeSymbol(type: any): symbol | number {
    
  376.     const symbolOrNumber =
    
  377.       typeof type === 'object' && type !== null ? type.$$typeof : type;
    
  378. 
    
  379.     return typeof symbolOrNumber === 'symbol'
    
  380.       ? // $FlowFixMe[incompatible-return] `toString()` doesn't match the type signature?
    
  381.         symbolOrNumber.toString()
    
  382.       : symbolOrNumber;
    
  383.   }
    
  384. 
    
  385.   const {
    
  386.     CacheComponent,
    
  387.     ClassComponent,
    
  388.     IncompleteClassComponent,
    
  389.     FunctionComponent,
    
  390.     IndeterminateComponent,
    
  391.     ForwardRef,
    
  392.     HostRoot,
    
  393.     HostHoistable,
    
  394.     HostSingleton,
    
  395.     HostComponent,
    
  396.     HostPortal,
    
  397.     HostText,
    
  398.     Fragment,
    
  399.     LazyComponent,
    
  400.     LegacyHiddenComponent,
    
  401.     MemoComponent,
    
  402.     OffscreenComponent,
    
  403.     Profiler,
    
  404.     ScopeComponent,
    
  405.     SimpleMemoComponent,
    
  406.     SuspenseComponent,
    
  407.     SuspenseListComponent,
    
  408.     TracingMarkerComponent,
    
  409.   } = ReactTypeOfWork;
    
  410. 
    
  411.   function resolveFiberType(type: any): $FlowFixMe {
    
  412.     const typeSymbol = getTypeSymbol(type);
    
  413.     switch (typeSymbol) {
    
  414.       case MEMO_NUMBER:
    
  415.       case MEMO_SYMBOL_STRING:
    
  416.         // recursively resolving memo type in case of memo(forwardRef(Component))
    
  417.         return resolveFiberType(type.type);
    
  418.       case FORWARD_REF_NUMBER:
    
  419.       case FORWARD_REF_SYMBOL_STRING:
    
  420.         return type.render;
    
  421.       default:
    
  422.         return type;
    
  423.     }
    
  424.   }
    
  425. 
    
  426.   // NOTICE Keep in sync with shouldFilterFiber() and other get*ForFiber methods
    
  427.   function getDisplayNameForFiber(fiber: Fiber): string | null {
    
  428.     const {elementType, type, tag} = fiber;
    
  429. 
    
  430.     let resolvedType = type;
    
  431.     if (typeof type === 'object' && type !== null) {
    
  432.       resolvedType = resolveFiberType(type);
    
  433.     }
    
  434. 
    
  435.     let resolvedContext: any = null;
    
  436. 
    
  437.     switch (tag) {
    
  438.       case CacheComponent:
    
  439.         return 'Cache';
    
  440.       case ClassComponent:
    
  441.       case IncompleteClassComponent:
    
  442.         return getDisplayName(resolvedType);
    
  443.       case FunctionComponent:
    
  444.       case IndeterminateComponent:
    
  445.         return getDisplayName(resolvedType);
    
  446.       case ForwardRef:
    
  447.         return getWrappedDisplayName(
    
  448.           elementType,
    
  449.           resolvedType,
    
  450.           'ForwardRef',
    
  451.           'Anonymous',
    
  452.         );
    
  453.       case HostRoot:
    
  454.         const fiberRoot = fiber.stateNode;
    
  455.         if (fiberRoot != null && fiberRoot._debugRootType !== null) {
    
  456.           return fiberRoot._debugRootType;
    
  457.         }
    
  458.         return null;
    
  459.       case HostComponent:
    
  460.       case HostSingleton:
    
  461.       case HostHoistable:
    
  462.         return type;
    
  463.       case HostPortal:
    
  464.       case HostText:
    
  465.         return null;
    
  466.       case Fragment:
    
  467.         return 'Fragment';
    
  468.       case LazyComponent:
    
  469.         // This display name will not be user visible.
    
  470.         // Once a Lazy component loads its inner component, React replaces the tag and type.
    
  471.         // This display name will only show up in console logs when DevTools DEBUG mode is on.
    
  472.         return 'Lazy';
    
  473.       case MemoComponent:
    
  474.       case SimpleMemoComponent:
    
  475.         // Display name in React does not use `Memo` as a wrapper but fallback name.
    
  476.         return getWrappedDisplayName(
    
  477.           elementType,
    
  478.           resolvedType,
    
  479.           'Memo',
    
  480.           'Anonymous',
    
  481.         );
    
  482.       case SuspenseComponent:
    
  483.         return 'Suspense';
    
  484.       case LegacyHiddenComponent:
    
  485.         return 'LegacyHidden';
    
  486.       case OffscreenComponent:
    
  487.         return 'Offscreen';
    
  488.       case ScopeComponent:
    
  489.         return 'Scope';
    
  490.       case SuspenseListComponent:
    
  491.         return 'SuspenseList';
    
  492.       case Profiler:
    
  493.         return 'Profiler';
    
  494.       case TracingMarkerComponent:
    
  495.         return 'TracingMarker';
    
  496.       default:
    
  497.         const typeSymbol = getTypeSymbol(type);
    
  498. 
    
  499.         switch (typeSymbol) {
    
  500.           case CONCURRENT_MODE_NUMBER:
    
  501.           case CONCURRENT_MODE_SYMBOL_STRING:
    
  502.           case DEPRECATED_ASYNC_MODE_SYMBOL_STRING:
    
  503.             return null;
    
  504.           case PROVIDER_NUMBER:
    
  505.           case PROVIDER_SYMBOL_STRING:
    
  506.             // 16.3.0 exposed the context object as "context"
    
  507.             // PR #12501 changed it to "_context" for 16.3.1+
    
  508.             // NOTE Keep in sync with inspectElementRaw()
    
  509.             resolvedContext = fiber.type._context || fiber.type.context;
    
  510.             return `${resolvedContext.displayName || 'Context'}.Provider`;
    
  511.           case CONTEXT_NUMBER:
    
  512.           case CONTEXT_SYMBOL_STRING:
    
  513.           case SERVER_CONTEXT_SYMBOL_STRING:
    
  514.             // 16.3-16.5 read from "type" because the Consumer is the actual context object.
    
  515.             // 16.6+ should read from "type._context" because Consumer can be different (in DEV).
    
  516.             // NOTE Keep in sync with inspectElementRaw()
    
  517.             resolvedContext = fiber.type._context || fiber.type;
    
  518. 
    
  519.             // NOTE: TraceUpdatesBackendManager depends on the name ending in '.Consumer'
    
  520.             // If you change the name, figure out a more resilient way to detect it.
    
  521.             return `${resolvedContext.displayName || 'Context'}.Consumer`;
    
  522.           case STRICT_MODE_NUMBER:
    
  523.           case STRICT_MODE_SYMBOL_STRING:
    
  524.             return null;
    
  525.           case PROFILER_NUMBER:
    
  526.           case PROFILER_SYMBOL_STRING:
    
  527.             return `Profiler(${fiber.memoizedProps.id})`;
    
  528.           case SCOPE_NUMBER:
    
  529.           case SCOPE_SYMBOL_STRING:
    
  530.             return 'Scope';
    
  531.           default:
    
  532.             // Unknown element type.
    
  533.             // This may mean a new element type that has not yet been added to DevTools.
    
  534.             return null;
    
  535.         }
    
  536.     }
    
  537.   }
    
  538. 
    
  539.   return {
    
  540.     getDisplayNameForFiber,
    
  541.     getTypeSymbol,
    
  542.     ReactPriorityLevels,
    
  543.     ReactTypeOfWork,
    
  544.     StrictModeBits,
    
  545.   };
    
  546. }
    
  547. 
    
  548. // Map of one or more Fibers in a pair to their unique id number.
    
  549. // We track both Fibers to support Fast Refresh,
    
  550. // which may forcefully replace one of the pair as part of hot reloading.
    
  551. // In that case it's still important to be able to locate the previous ID during subsequent renders.
    
  552. const fiberToIDMap: Map<Fiber, number> = new Map();
    
  553. 
    
  554. // Map of id to one (arbitrary) Fiber in a pair.
    
  555. // This Map is used to e.g. get the display name for a Fiber or schedule an update,
    
  556. // operations that should be the same whether the current and work-in-progress Fiber is used.
    
  557. const idToArbitraryFiberMap: Map<number, Fiber> = new Map();
    
  558. 
    
  559. export function attach(
    
  560.   hook: DevToolsHook,
    
  561.   rendererID: number,
    
  562.   renderer: ReactRenderer,
    
  563.   global: Object,
    
  564. ): RendererInterface {
    
  565.   // Newer versions of the reconciler package also specific reconciler version.
    
  566.   // If that version number is present, use it.
    
  567.   // Third party renderer versions may not match the reconciler version,
    
  568.   // and the latter is what's important in terms of tags and symbols.
    
  569.   const version = renderer.reconcilerVersion || renderer.version;
    
  570. 
    
  571.   const {
    
  572.     getDisplayNameForFiber,
    
  573.     getTypeSymbol,
    
  574.     ReactPriorityLevels,
    
  575.     ReactTypeOfWork,
    
  576.     StrictModeBits,
    
  577.   } = getInternalReactConstants(version);
    
  578.   const {
    
  579.     CacheComponent,
    
  580.     ClassComponent,
    
  581.     ContextConsumer,
    
  582.     DehydratedSuspenseComponent,
    
  583.     ForwardRef,
    
  584.     Fragment,
    
  585.     FunctionComponent,
    
  586.     HostRoot,
    
  587.     HostHoistable,
    
  588.     HostSingleton,
    
  589.     HostPortal,
    
  590.     HostComponent,
    
  591.     HostText,
    
  592.     IncompleteClassComponent,
    
  593.     IndeterminateComponent,
    
  594.     LegacyHiddenComponent,
    
  595.     MemoComponent,
    
  596.     OffscreenComponent,
    
  597.     SimpleMemoComponent,
    
  598.     SuspenseComponent,
    
  599.     SuspenseListComponent,
    
  600.     TracingMarkerComponent,
    
  601.   } = ReactTypeOfWork;
    
  602.   const {
    
  603.     ImmediatePriority,
    
  604.     UserBlockingPriority,
    
  605.     NormalPriority,
    
  606.     LowPriority,
    
  607.     IdlePriority,
    
  608.     NoPriority,
    
  609.   } = ReactPriorityLevels;
    
  610. 
    
  611.   const {
    
  612.     getLaneLabelMap,
    
  613.     injectProfilingHooks,
    
  614.     overrideHookState,
    
  615.     overrideHookStateDeletePath,
    
  616.     overrideHookStateRenamePath,
    
  617.     overrideProps,
    
  618.     overridePropsDeletePath,
    
  619.     overridePropsRenamePath,
    
  620.     scheduleRefresh,
    
  621.     setErrorHandler,
    
  622.     setSuspenseHandler,
    
  623.     scheduleUpdate,
    
  624.   } = renderer;
    
  625.   const supportsTogglingError =
    
  626.     typeof setErrorHandler === 'function' &&
    
  627.     typeof scheduleUpdate === 'function';
    
  628.   const supportsTogglingSuspense =
    
  629.     typeof setSuspenseHandler === 'function' &&
    
  630.     typeof scheduleUpdate === 'function';
    
  631. 
    
  632.   if (typeof scheduleRefresh === 'function') {
    
  633.     // When Fast Refresh updates a component, the frontend may need to purge cached information.
    
  634.     // For example, ASTs cached for the component (for named hooks) may no longer be valid.
    
  635.     // Send a signal to the frontend to purge this cached information.
    
  636.     // The "fastRefreshScheduled" dispatched is global (not Fiber or even Renderer specific).
    
  637.     // This is less effecient since it means the front-end will need to purge the entire cache,
    
  638.     // but this is probably an okay trade off in order to reduce coupling between the DevTools and Fast Refresh.
    
  639.     renderer.scheduleRefresh = (...args) => {
    
  640.       try {
    
  641.         hook.emit('fastRefreshScheduled');
    
  642.       } finally {
    
  643.         return scheduleRefresh(...args);
    
  644.       }
    
  645.     };
    
  646.   }
    
  647. 
    
  648.   let getTimelineData: null | GetTimelineData = null;
    
  649.   let toggleProfilingStatus: null | ToggleProfilingStatus = null;
    
  650.   if (typeof injectProfilingHooks === 'function') {
    
  651.     const response = createProfilingHooks({
    
  652.       getDisplayNameForFiber,
    
  653.       getIsProfiling: () => isProfiling,
    
  654.       getLaneLabelMap,
    
  655.       currentDispatcherRef: renderer.currentDispatcherRef,
    
  656.       workTagMap: ReactTypeOfWork,
    
  657.       reactVersion: version,
    
  658.     });
    
  659. 
    
  660.     // Pass the Profiling hooks to the reconciler for it to call during render.
    
  661.     injectProfilingHooks(response.profilingHooks);
    
  662. 
    
  663.     // Hang onto this toggle so we can notify the external methods of profiling status changes.
    
  664.     getTimelineData = response.getTimelineData;
    
  665.     toggleProfilingStatus = response.toggleProfilingStatus;
    
  666.   }
    
  667. 
    
  668.   // Tracks Fibers with recently changed number of error/warning messages.
    
  669.   // These collections store the Fiber rather than the ID,
    
  670.   // in order to avoid generating an ID for Fibers that never get mounted
    
  671.   // (due to e.g. Suspense or error boundaries).
    
  672.   // onErrorOrWarning() adds Fibers and recordPendingErrorsAndWarnings() later clears them.
    
  673.   const fibersWithChangedErrorOrWarningCounts: Set<Fiber> = new Set();
    
  674.   const pendingFiberToErrorsMap: Map<Fiber, Map<string, number>> = new Map();
    
  675.   const pendingFiberToWarningsMap: Map<Fiber, Map<string, number>> = new Map();
    
  676. 
    
  677.   // Mapping of fiber IDs to error/warning messages and counts.
    
  678.   const fiberIDToErrorsMap: Map<number, Map<string, number>> = new Map();
    
  679.   const fiberIDToWarningsMap: Map<number, Map<string, number>> = new Map();
    
  680. 
    
  681.   function clearErrorsAndWarnings() {
    
  682.     // eslint-disable-next-line no-for-of-loops/no-for-of-loops
    
  683.     for (const id of fiberIDToErrorsMap.keys()) {
    
  684.       const fiber = idToArbitraryFiberMap.get(id);
    
  685.       if (fiber != null) {
    
  686.         fibersWithChangedErrorOrWarningCounts.add(fiber);
    
  687.         updateMostRecentlyInspectedElementIfNecessary(id);
    
  688.       }
    
  689.     }
    
  690. 
    
  691.     // eslint-disable-next-line no-for-of-loops/no-for-of-loops
    
  692.     for (const id of fiberIDToWarningsMap.keys()) {
    
  693.       const fiber = idToArbitraryFiberMap.get(id);
    
  694.       if (fiber != null) {
    
  695.         fibersWithChangedErrorOrWarningCounts.add(fiber);
    
  696.         updateMostRecentlyInspectedElementIfNecessary(id);
    
  697.       }
    
  698.     }
    
  699. 
    
  700.     fiberIDToErrorsMap.clear();
    
  701.     fiberIDToWarningsMap.clear();
    
  702. 
    
  703.     flushPendingEvents();
    
  704.   }
    
  705. 
    
  706.   function clearMessageCountHelper(
    
  707.     fiberID: number,
    
  708.     pendingFiberToMessageCountMap: Map<Fiber, Map<string, number>>,
    
  709.     fiberIDToMessageCountMap: Map<number, Map<string, number>>,
    
  710.   ) {
    
  711.     const fiber = idToArbitraryFiberMap.get(fiberID);
    
  712.     if (fiber != null) {
    
  713.       // Throw out any pending changes.
    
  714.       pendingFiberToErrorsMap.delete(fiber);
    
  715. 
    
  716.       if (fiberIDToMessageCountMap.has(fiberID)) {
    
  717.         fiberIDToMessageCountMap.delete(fiberID);
    
  718. 
    
  719.         // If previous flushed counts have changed, schedule an update too.
    
  720.         fibersWithChangedErrorOrWarningCounts.add(fiber);
    
  721.         flushPendingEvents();
    
  722. 
    
  723.         updateMostRecentlyInspectedElementIfNecessary(fiberID);
    
  724.       } else {
    
  725.         fibersWithChangedErrorOrWarningCounts.delete(fiber);
    
  726.       }
    
  727.     }
    
  728.   }
    
  729. 
    
  730.   function clearErrorsForFiberID(fiberID: number) {
    
  731.     clearMessageCountHelper(
    
  732.       fiberID,
    
  733.       pendingFiberToErrorsMap,
    
  734.       fiberIDToErrorsMap,
    
  735.     );
    
  736.   }
    
  737. 
    
  738.   function clearWarningsForFiberID(fiberID: number) {
    
  739.     clearMessageCountHelper(
    
  740.       fiberID,
    
  741.       pendingFiberToWarningsMap,
    
  742.       fiberIDToWarningsMap,
    
  743.     );
    
  744.   }
    
  745. 
    
  746.   function updateMostRecentlyInspectedElementIfNecessary(
    
  747.     fiberID: number,
    
  748.   ): void {
    
  749.     if (
    
  750.       mostRecentlyInspectedElement !== null &&
    
  751.       mostRecentlyInspectedElement.id === fiberID
    
  752.     ) {
    
  753.       hasElementUpdatedSinceLastInspected = true;
    
  754.     }
    
  755.   }
    
  756. 
    
  757.   // Called when an error or warning is logged during render, commit, or passive (including unmount functions).
    
  758.   function onErrorOrWarning(
    
  759.     fiber: Fiber,
    
  760.     type: 'error' | 'warn',
    
  761.     args: $ReadOnlyArray<any>,
    
  762.   ): void {
    
  763.     if (type === 'error') {
    
  764.       const maybeID = getFiberIDUnsafe(fiber);
    
  765.       // if this is an error simulated by us to trigger error boundary, ignore
    
  766.       if (maybeID != null && forceErrorForFiberIDs.get(maybeID) === true) {
    
  767.         return;
    
  768.       }
    
  769.     }
    
  770.     const message = format(...args);
    
  771.     if (__DEBUG__) {
    
  772.       debug('onErrorOrWarning', fiber, null, `${type}: "${message}"`);
    
  773.     }
    
  774. 
    
  775.     // Mark this Fiber as needed its warning/error count updated during the next flush.
    
  776.     fibersWithChangedErrorOrWarningCounts.add(fiber);
    
  777. 
    
  778.     // Track the warning/error for later.
    
  779.     const fiberMap =
    
  780.       type === 'error' ? pendingFiberToErrorsMap : pendingFiberToWarningsMap;
    
  781.     const messageMap = fiberMap.get(fiber);
    
  782.     if (messageMap != null) {
    
  783.       const count = messageMap.get(message) || 0;
    
  784.       messageMap.set(message, count + 1);
    
  785.     } else {
    
  786.       fiberMap.set(fiber, new Map([[message, 1]]));
    
  787.     }
    
  788. 
    
  789.     // Passive effects may trigger errors or warnings too;
    
  790.     // In this case, we should wait until the rest of the passive effects have run,
    
  791.     // but we shouldn't wait until the next commit because that might be a long time.
    
  792.     // This would also cause "tearing" between an inspected Component and the tree view.
    
  793.     // Then again we don't want to flush too soon because this could be an error during async rendering.
    
  794.     // Use a debounce technique to ensure that we'll eventually flush.
    
  795.     flushPendingErrorsAndWarningsAfterDelay();
    
  796.   }
    
  797. 
    
  798.   // Patching the console enables DevTools to do a few useful things:
    
  799.   // * Append component stacks to warnings and error messages
    
  800.   // * Disable logging during re-renders to inspect hooks (see inspectHooksOfFiber)
    
  801.   registerRendererWithConsole(renderer, onErrorOrWarning);
    
  802. 
    
  803.   // The renderer interface can't read these preferences directly,
    
  804.   // because it is stored in localStorage within the context of the extension.
    
  805.   // It relies on the extension to pass the preference through via the global.
    
  806.   patchConsoleUsingWindowValues();
    
  807. 
    
  808.   const debug = (
    
  809.     name: string,
    
  810.     fiber: Fiber,
    
  811.     parentFiber: ?Fiber,
    
  812.     extraString: string = '',
    
  813.   ): void => {
    
  814.     if (__DEBUG__) {
    
  815.       const displayName =
    
  816.         fiber.tag + ':' + (getDisplayNameForFiber(fiber) || 'null');
    
  817. 
    
  818.       const maybeID = getFiberIDUnsafe(fiber) || '<no id>';
    
  819.       const parentDisplayName = parentFiber
    
  820.         ? parentFiber.tag +
    
  821.           ':' +
    
  822.           (getDisplayNameForFiber(parentFiber) || 'null')
    
  823.         : '';
    
  824.       const maybeParentID = parentFiber
    
  825.         ? getFiberIDUnsafe(parentFiber) || '<no-id>'
    
  826.         : '';
    
  827. 
    
  828.       console.groupCollapsed(
    
  829.         `[renderer] %c${name} %c${displayName} (${maybeID}) %c${
    
  830.           parentFiber ? `${parentDisplayName} (${maybeParentID})` : ''
    
  831.         } %c${extraString}`,
    
  832.         'color: red; font-weight: bold;',
    
  833.         'color: blue;',
    
  834.         'color: purple;',
    
  835.         'color: black;',
    
  836.       );
    
  837.       console.log(new Error().stack.split('\n').slice(1).join('\n'));
    
  838.       console.groupEnd();
    
  839.     }
    
  840.   };
    
  841. 
    
  842.   // Configurable Components tree filters.
    
  843.   const hideElementsWithDisplayNames: Set<RegExp> = new Set();
    
  844.   const hideElementsWithPaths: Set<RegExp> = new Set();
    
  845.   const hideElementsWithTypes: Set<ElementType> = new Set();
    
  846. 
    
  847.   // Highlight updates
    
  848.   let traceUpdatesEnabled: boolean = false;
    
  849.   const traceUpdatesForNodes: Set<NativeType> = new Set();
    
  850. 
    
  851.   function applyComponentFilters(componentFilters: Array<ComponentFilter>) {
    
  852.     hideElementsWithTypes.clear();
    
  853.     hideElementsWithDisplayNames.clear();
    
  854.     hideElementsWithPaths.clear();
    
  855. 
    
  856.     componentFilters.forEach(componentFilter => {
    
  857.       if (!componentFilter.isEnabled) {
    
  858.         return;
    
  859.       }
    
  860. 
    
  861.       switch (componentFilter.type) {
    
  862.         case ComponentFilterDisplayName:
    
  863.           if (componentFilter.isValid && componentFilter.value !== '') {
    
  864.             hideElementsWithDisplayNames.add(
    
  865.               new RegExp(componentFilter.value, 'i'),
    
  866.             );
    
  867.           }
    
  868.           break;
    
  869.         case ComponentFilterElementType:
    
  870.           hideElementsWithTypes.add(componentFilter.value);
    
  871.           break;
    
  872.         case ComponentFilterLocation:
    
  873.           if (componentFilter.isValid && componentFilter.value !== '') {
    
  874.             hideElementsWithPaths.add(new RegExp(componentFilter.value, 'i'));
    
  875.           }
    
  876.           break;
    
  877.         case ComponentFilterHOC:
    
  878.           hideElementsWithDisplayNames.add(new RegExp('\\('));
    
  879.           break;
    
  880.         default:
    
  881.           console.warn(
    
  882.             `Invalid component filter type "${componentFilter.type}"`,
    
  883.           );
    
  884.           break;
    
  885.       }
    
  886.     });
    
  887.   }
    
  888. 
    
  889.   // The renderer interface can't read saved component filters directly,
    
  890.   // because they are stored in localStorage within the context of the extension.
    
  891.   // Instead it relies on the extension to pass filters through.
    
  892.   if (window.__REACT_DEVTOOLS_COMPONENT_FILTERS__ != null) {
    
  893.     applyComponentFilters(window.__REACT_DEVTOOLS_COMPONENT_FILTERS__);
    
  894.   } else {
    
  895.     // Unfortunately this feature is not expected to work for React Native for now.
    
  896.     // It would be annoying for us to spam YellowBox warnings with unactionable stuff,
    
  897.     // so for now just skip this message...
    
  898.     //console.warn('⚛️ DevTools: Could not locate saved component filters');
    
  899. 
    
  900.     // Fallback to assuming the default filters in this case.
    
  901.     applyComponentFilters(getDefaultComponentFilters());
    
  902.   }
    
  903. 
    
  904.   // If necessary, we can revisit optimizing this operation.
    
  905.   // For example, we could add a new recursive unmount tree operation.
    
  906.   // The unmount operations are already significantly smaller than mount operations though.
    
  907.   // This is something to keep in mind for later.
    
  908.   function updateComponentFilters(componentFilters: Array<ComponentFilter>) {
    
  909.     if (isProfiling) {
    
  910.       // Re-mounting a tree while profiling is in progress might break a lot of assumptions.
    
  911.       // If necessary, we could support this- but it doesn't seem like a necessary use case.
    
  912.       throw Error('Cannot modify filter preferences while profiling');
    
  913.     }
    
  914. 
    
  915.     // Recursively unmount all roots.
    
  916.     hook.getFiberRoots(rendererID).forEach(root => {
    
  917.       currentRootID = getOrGenerateFiberID(root.current);
    
  918.       // The TREE_OPERATION_REMOVE_ROOT operation serves two purposes:
    
  919.       // 1. It avoids sending unnecessary bridge traffic to clear a root.
    
  920.       // 2. It preserves Fiber IDs when remounting (below) which in turn ID to error/warning mapping.
    
  921.       pushOperation(TREE_OPERATION_REMOVE_ROOT);
    
  922.       flushPendingEvents(root);
    
  923.       currentRootID = -1;
    
  924.     });
    
  925. 
    
  926.     applyComponentFilters(componentFilters);
    
  927. 
    
  928.     // Reset pseudo counters so that new path selections will be persisted.
    
  929.     rootDisplayNameCounter.clear();
    
  930. 
    
  931.     // Recursively re-mount all roots with new filter criteria applied.
    
  932.     hook.getFiberRoots(rendererID).forEach(root => {
    
  933.       currentRootID = getOrGenerateFiberID(root.current);
    
  934.       setRootPseudoKey(currentRootID, root.current);
    
  935.       mountFiberRecursively(root.current, null, false, false);
    
  936.       flushPendingEvents(root);
    
  937.       currentRootID = -1;
    
  938.     });
    
  939. 
    
  940.     // Also re-evaluate all error and warning counts given the new filters.
    
  941.     reevaluateErrorsAndWarnings();
    
  942.     flushPendingEvents();
    
  943.   }
    
  944. 
    
  945.   // NOTICE Keep in sync with get*ForFiber methods
    
  946.   function shouldFilterFiber(fiber: Fiber): boolean {
    
  947.     const {_debugSource, tag, type, key} = fiber;
    
  948. 
    
  949.     switch (tag) {
    
  950.       case DehydratedSuspenseComponent:
    
  951.         // TODO: ideally we would show dehydrated Suspense immediately.
    
  952.         // However, it has some special behavior (like disconnecting
    
  953.         // an alternate and turning into real Suspense) which breaks DevTools.
    
  954.         // For now, ignore it, and only show it once it gets hydrated.
    
  955.         // https://github.com/bvaughn/react-devtools-experimental/issues/197
    
  956.         return true;
    
  957.       case HostPortal:
    
  958.       case HostText:
    
  959.       case LegacyHiddenComponent:
    
  960.       case OffscreenComponent:
    
  961.         return true;
    
  962.       case HostRoot:
    
  963.         // It is never valid to filter the root element.
    
  964.         return false;
    
  965.       case Fragment:
    
  966.         return key === null;
    
  967.       default:
    
  968.         const typeSymbol = getTypeSymbol(type);
    
  969. 
    
  970.         switch (typeSymbol) {
    
  971.           case CONCURRENT_MODE_NUMBER:
    
  972.           case CONCURRENT_MODE_SYMBOL_STRING:
    
  973.           case DEPRECATED_ASYNC_MODE_SYMBOL_STRING:
    
  974.           case STRICT_MODE_NUMBER:
    
  975.           case STRICT_MODE_SYMBOL_STRING:
    
  976.             return true;
    
  977.           default:
    
  978.             break;
    
  979.         }
    
  980.     }
    
  981. 
    
  982.     const elementType = getElementTypeForFiber(fiber);
    
  983.     if (hideElementsWithTypes.has(elementType)) {
    
  984.       return true;
    
  985.     }
    
  986. 
    
  987.     if (hideElementsWithDisplayNames.size > 0) {
    
  988.       const displayName = getDisplayNameForFiber(fiber);
    
  989.       if (displayName != null) {
    
  990.         // eslint-disable-next-line no-for-of-loops/no-for-of-loops
    
  991.         for (const displayNameRegExp of hideElementsWithDisplayNames) {
    
  992.           if (displayNameRegExp.test(displayName)) {
    
  993.             return true;
    
  994.           }
    
  995.         }
    
  996.       }
    
  997.     }
    
  998. 
    
  999.     if (_debugSource != null && hideElementsWithPaths.size > 0) {
    
  1000.       const {fileName} = _debugSource;
    
  1001.       // eslint-disable-next-line no-for-of-loops/no-for-of-loops
    
  1002.       for (const pathRegExp of hideElementsWithPaths) {
    
  1003.         if (pathRegExp.test(fileName)) {
    
  1004.           return true;
    
  1005.         }
    
  1006.       }
    
  1007.     }
    
  1008. 
    
  1009.     return false;
    
  1010.   }
    
  1011. 
    
  1012.   // NOTICE Keep in sync with shouldFilterFiber() and other get*ForFiber methods
    
  1013.   function getElementTypeForFiber(fiber: Fiber): ElementType {
    
  1014.     const {type, tag} = fiber;
    
  1015. 
    
  1016.     switch (tag) {
    
  1017.       case ClassComponent:
    
  1018.       case IncompleteClassComponent:
    
  1019.         return ElementTypeClass;
    
  1020.       case FunctionComponent:
    
  1021.       case IndeterminateComponent:
    
  1022.         return ElementTypeFunction;
    
  1023.       case ForwardRef:
    
  1024.         return ElementTypeForwardRef;
    
  1025.       case HostRoot:
    
  1026.         return ElementTypeRoot;
    
  1027.       case HostComponent:
    
  1028.       case HostHoistable:
    
  1029.       case HostSingleton:
    
  1030.         return ElementTypeHostComponent;
    
  1031.       case HostPortal:
    
  1032.       case HostText:
    
  1033.       case Fragment:
    
  1034.         return ElementTypeOtherOrUnknown;
    
  1035.       case MemoComponent:
    
  1036.       case SimpleMemoComponent:
    
  1037.         return ElementTypeMemo;
    
  1038.       case SuspenseComponent:
    
  1039.         return ElementTypeSuspense;
    
  1040.       case SuspenseListComponent:
    
  1041.         return ElementTypeSuspenseList;
    
  1042.       case TracingMarkerComponent:
    
  1043.         return ElementTypeTracingMarker;
    
  1044.       default:
    
  1045.         const typeSymbol = getTypeSymbol(type);
    
  1046. 
    
  1047.         switch (typeSymbol) {
    
  1048.           case CONCURRENT_MODE_NUMBER:
    
  1049.           case CONCURRENT_MODE_SYMBOL_STRING:
    
  1050.           case DEPRECATED_ASYNC_MODE_SYMBOL_STRING:
    
  1051.             return ElementTypeOtherOrUnknown;
    
  1052.           case PROVIDER_NUMBER:
    
  1053.           case PROVIDER_SYMBOL_STRING:
    
  1054.             return ElementTypeContext;
    
  1055.           case CONTEXT_NUMBER:
    
  1056.           case CONTEXT_SYMBOL_STRING:
    
  1057.             return ElementTypeContext;
    
  1058.           case STRICT_MODE_NUMBER:
    
  1059.           case STRICT_MODE_SYMBOL_STRING:
    
  1060.             return ElementTypeOtherOrUnknown;
    
  1061.           case PROFILER_NUMBER:
    
  1062.           case PROFILER_SYMBOL_STRING:
    
  1063.             return ElementTypeProfiler;
    
  1064.           default:
    
  1065.             return ElementTypeOtherOrUnknown;
    
  1066.         }
    
  1067.     }
    
  1068.   }
    
  1069. 
    
  1070.   // When profiling is supported, we store the latest tree base durations for each Fiber.
    
  1071.   // This is so that we can quickly capture a snapshot of those values if profiling starts.
    
  1072.   // If we didn't store these values, we'd have to crawl the tree when profiling started,
    
  1073.   // and use a slow path to find each of the current Fibers.
    
  1074.   const idToTreeBaseDurationMap: Map<number, number> = new Map();
    
  1075. 
    
  1076.   // When profiling is supported, we store the latest tree base durations for each Fiber.
    
  1077.   // This map enables us to filter these times by root when sending them to the frontend.
    
  1078.   const idToRootMap: Map<number, number> = new Map();
    
  1079. 
    
  1080.   // When a mount or update is in progress, this value tracks the root that is being operated on.
    
  1081.   let currentRootID: number = -1;
    
  1082. 
    
  1083.   // Returns the unique ID for a Fiber or generates and caches a new one if the Fiber hasn't been seen before.
    
  1084.   // Once this method has been called for a Fiber, untrackFiberID() should always be called later to avoid leaking.
    
  1085.   function getOrGenerateFiberID(fiber: Fiber): number {
    
  1086.     let id = null;
    
  1087.     if (fiberToIDMap.has(fiber)) {
    
  1088.       id = fiberToIDMap.get(fiber);
    
  1089.     } else {
    
  1090.       const {alternate} = fiber;
    
  1091.       if (alternate !== null && fiberToIDMap.has(alternate)) {
    
  1092.         id = fiberToIDMap.get(alternate);
    
  1093.       }
    
  1094.     }
    
  1095. 
    
  1096.     let didGenerateID = false;
    
  1097.     if (id === null) {
    
  1098.       didGenerateID = true;
    
  1099.       id = getUID();
    
  1100.     }
    
  1101. 
    
  1102.     // This refinement is for Flow purposes only.
    
  1103.     const refinedID = ((id: any): number);
    
  1104. 
    
  1105.     // Make sure we're tracking this Fiber
    
  1106.     // e.g. if it just mounted or an error was logged during initial render.
    
  1107.     if (!fiberToIDMap.has(fiber)) {
    
  1108.       fiberToIDMap.set(fiber, refinedID);
    
  1109.       idToArbitraryFiberMap.set(refinedID, fiber);
    
  1110.     }
    
  1111. 
    
  1112.     // Also make sure we're tracking its alternate,
    
  1113.     // e.g. in case this is the first update after mount.
    
  1114.     const {alternate} = fiber;
    
  1115.     if (alternate !== null) {
    
  1116.       if (!fiberToIDMap.has(alternate)) {
    
  1117.         fiberToIDMap.set(alternate, refinedID);
    
  1118.       }
    
  1119.     }
    
  1120. 
    
  1121.     if (__DEBUG__) {
    
  1122.       if (didGenerateID) {
    
  1123.         debug(
    
  1124.           'getOrGenerateFiberID()',
    
  1125.           fiber,
    
  1126.           fiber.return,
    
  1127.           'Generated a new UID',
    
  1128.         );
    
  1129.       }
    
  1130.     }
    
  1131. 
    
  1132.     return refinedID;
    
  1133.   }
    
  1134. 
    
  1135.   // Returns an ID if one has already been generated for the Fiber or throws.
    
  1136.   function getFiberIDThrows(fiber: Fiber): number {
    
  1137.     const maybeID = getFiberIDUnsafe(fiber);
    
  1138.     if (maybeID !== null) {
    
  1139.       return maybeID;
    
  1140.     }
    
  1141.     throw Error(
    
  1142.       `Could not find ID for Fiber "${getDisplayNameForFiber(fiber) || ''}"`,
    
  1143.     );
    
  1144.   }
    
  1145. 
    
  1146.   // Returns an ID if one has already been generated for the Fiber or null if one has not been generated.
    
  1147.   // Use this method while e.g. logging to avoid over-retaining Fibers.
    
  1148.   function getFiberIDUnsafe(fiber: Fiber): number | null {
    
  1149.     if (fiberToIDMap.has(fiber)) {
    
  1150.       return ((fiberToIDMap.get(fiber): any): number);
    
  1151.     } else {
    
  1152.       const {alternate} = fiber;
    
  1153.       if (alternate !== null && fiberToIDMap.has(alternate)) {
    
  1154.         return ((fiberToIDMap.get(alternate): any): number);
    
  1155.       }
    
  1156.     }
    
  1157.     return null;
    
  1158.   }
    
  1159. 
    
  1160.   // Removes a Fiber (and its alternate) from the Maps used to track their id.
    
  1161.   // This method should always be called when a Fiber is unmounting.
    
  1162.   function untrackFiberID(fiber: Fiber) {
    
  1163.     if (__DEBUG__) {
    
  1164.       debug('untrackFiberID()', fiber, fiber.return, 'schedule after delay');
    
  1165.     }
    
  1166. 
    
  1167.     // Untrack Fibers after a slight delay in order to support a Fast Refresh edge case:
    
  1168.     // 1. Component type is updated and Fast Refresh schedules an update+remount.
    
  1169.     // 2. flushPendingErrorsAndWarningsAfterDelay() runs, sees the old Fiber is no longer mounted
    
  1170.     //    (it's been disconnected by Fast Refresh), and calls untrackFiberID() to clear it from the Map.
    
  1171.     // 3. React flushes pending passive effects before it runs the next render,
    
  1172.     //    which logs an error or warning, which causes a new ID to be generated for this Fiber.
    
  1173.     // 4. DevTools now tries to unmount the old Component with the new ID.
    
  1174.     //
    
  1175.     // The underlying problem here is the premature clearing of the Fiber ID,
    
  1176.     // but DevTools has no way to detect that a given Fiber has been scheduled for Fast Refresh.
    
  1177.     // (The "_debugNeedsRemount" flag won't necessarily be set.)
    
  1178.     //
    
  1179.     // The best we can do is to delay untracking by a small amount,
    
  1180.     // and give React time to process the Fast Refresh delay.
    
  1181. 
    
  1182.     untrackFibersSet.add(fiber);
    
  1183. 
    
  1184.     // React may detach alternate pointers during unmount;
    
  1185.     // Since our untracking code is async, we should explicily track the pending alternate here as well.
    
  1186.     const alternate = fiber.alternate;
    
  1187.     if (alternate !== null) {
    
  1188.       untrackFibersSet.add(alternate);
    
  1189.     }
    
  1190. 
    
  1191.     if (untrackFibersTimeoutID === null) {
    
  1192.       untrackFibersTimeoutID = setTimeout(untrackFibers, 1000);
    
  1193.     }
    
  1194.   }
    
  1195. 
    
  1196.   const untrackFibersSet: Set<Fiber> = new Set();
    
  1197.   let untrackFibersTimeoutID: TimeoutID | null = null;
    
  1198. 
    
  1199.   function untrackFibers() {
    
  1200.     if (untrackFibersTimeoutID !== null) {
    
  1201.       clearTimeout(untrackFibersTimeoutID);
    
  1202.       untrackFibersTimeoutID = null;
    
  1203.     }
    
  1204. 
    
  1205.     untrackFibersSet.forEach(fiber => {
    
  1206.       const fiberID = getFiberIDUnsafe(fiber);
    
  1207.       if (fiberID !== null) {
    
  1208.         idToArbitraryFiberMap.delete(fiberID);
    
  1209. 
    
  1210.         // Also clear any errors/warnings associated with this fiber.
    
  1211.         clearErrorsForFiberID(fiberID);
    
  1212.         clearWarningsForFiberID(fiberID);
    
  1213.       }
    
  1214. 
    
  1215.       fiberToIDMap.delete(fiber);
    
  1216. 
    
  1217.       const {alternate} = fiber;
    
  1218.       if (alternate !== null) {
    
  1219.         fiberToIDMap.delete(alternate);
    
  1220.       }
    
  1221. 
    
  1222.       if (forceErrorForFiberIDs.has(fiberID)) {
    
  1223.         forceErrorForFiberIDs.delete(fiberID);
    
  1224.         if (forceErrorForFiberIDs.size === 0 && setErrorHandler != null) {
    
  1225.           setErrorHandler(shouldErrorFiberAlwaysNull);
    
  1226.         }
    
  1227.       }
    
  1228.     });
    
  1229.     untrackFibersSet.clear();
    
  1230.   }
    
  1231. 
    
  1232.   function getChangeDescription(
    
  1233.     prevFiber: Fiber | null,
    
  1234.     nextFiber: Fiber,
    
  1235.   ): ChangeDescription | null {
    
  1236.     switch (getElementTypeForFiber(nextFiber)) {
    
  1237.       case ElementTypeClass:
    
  1238.       case ElementTypeFunction:
    
  1239.       case ElementTypeMemo:
    
  1240.       case ElementTypeForwardRef:
    
  1241.         if (prevFiber === null) {
    
  1242.           return {
    
  1243.             context: null,
    
  1244.             didHooksChange: false,
    
  1245.             isFirstMount: true,
    
  1246.             props: null,
    
  1247.             state: null,
    
  1248.           };
    
  1249.         } else {
    
  1250.           const data: ChangeDescription = {
    
  1251.             context: getContextChangedKeys(nextFiber),
    
  1252.             didHooksChange: false,
    
  1253.             isFirstMount: false,
    
  1254.             props: getChangedKeys(
    
  1255.               prevFiber.memoizedProps,
    
  1256.               nextFiber.memoizedProps,
    
  1257.             ),
    
  1258.             state: getChangedKeys(
    
  1259.               prevFiber.memoizedState,
    
  1260.               nextFiber.memoizedState,
    
  1261.             ),
    
  1262.           };
    
  1263. 
    
  1264.           // Only traverse the hooks list once, depending on what info we're returning.
    
  1265.           const indices = getChangedHooksIndices(
    
  1266.             prevFiber.memoizedState,
    
  1267.             nextFiber.memoizedState,
    
  1268.           );
    
  1269.           data.hooks = indices;
    
  1270.           data.didHooksChange = indices !== null && indices.length > 0;
    
  1271. 
    
  1272.           return data;
    
  1273.         }
    
  1274.       default:
    
  1275.         return null;
    
  1276.     }
    
  1277.   }
    
  1278. 
    
  1279.   function updateContextsForFiber(fiber: Fiber) {
    
  1280.     switch (getElementTypeForFiber(fiber)) {
    
  1281.       case ElementTypeClass:
    
  1282.       case ElementTypeForwardRef:
    
  1283.       case ElementTypeFunction:
    
  1284.       case ElementTypeMemo:
    
  1285.         if (idToContextsMap !== null) {
    
  1286.           const id = getFiberIDThrows(fiber);
    
  1287.           const contexts = getContextsForFiber(fiber);
    
  1288.           if (contexts !== null) {
    
  1289.             // $FlowFixMe[incompatible-use] found when upgrading Flow
    
  1290.             idToContextsMap.set(id, contexts);
    
  1291.           }
    
  1292.         }
    
  1293.         break;
    
  1294.       default:
    
  1295.         break;
    
  1296.     }
    
  1297.   }
    
  1298. 
    
  1299.   // Differentiates between a null context value and no context.
    
  1300.   const NO_CONTEXT = {};
    
  1301. 
    
  1302.   function getContextsForFiber(fiber: Fiber): [Object, any] | null {
    
  1303.     let legacyContext = NO_CONTEXT;
    
  1304.     let modernContext = NO_CONTEXT;
    
  1305. 
    
  1306.     switch (getElementTypeForFiber(fiber)) {
    
  1307.       case ElementTypeClass:
    
  1308.         const instance = fiber.stateNode;
    
  1309.         if (instance != null) {
    
  1310.           if (
    
  1311.             instance.constructor &&
    
  1312.             instance.constructor.contextType != null
    
  1313.           ) {
    
  1314.             modernContext = instance.context;
    
  1315.           } else {
    
  1316.             legacyContext = instance.context;
    
  1317.             if (legacyContext && Object.keys(legacyContext).length === 0) {
    
  1318.               legacyContext = NO_CONTEXT;
    
  1319.             }
    
  1320.           }
    
  1321.         }
    
  1322.         return [legacyContext, modernContext];
    
  1323.       case ElementTypeForwardRef:
    
  1324.       case ElementTypeFunction:
    
  1325.       case ElementTypeMemo:
    
  1326.         const dependencies = fiber.dependencies;
    
  1327.         if (dependencies && dependencies.firstContext) {
    
  1328.           modernContext = dependencies.firstContext;
    
  1329.         }
    
  1330. 
    
  1331.         return [legacyContext, modernContext];
    
  1332.       default:
    
  1333.         return null;
    
  1334.     }
    
  1335.   }
    
  1336. 
    
  1337.   // Record all contexts at the time profiling is started.
    
  1338.   // Fibers only store the current context value,
    
  1339.   // so we need to track them separately in order to determine changed keys.
    
  1340.   function crawlToInitializeContextsMap(fiber: Fiber) {
    
  1341.     const id = getFiberIDUnsafe(fiber);
    
  1342. 
    
  1343.     // Not all Fibers in the subtree have mounted yet.
    
  1344.     // For example, Offscreen (hidden) or Suspense (suspended) subtrees won't yet be tracked.
    
  1345.     // We can safely skip these subtrees.
    
  1346.     if (id !== null) {
    
  1347.       updateContextsForFiber(fiber);
    
  1348. 
    
  1349.       let current = fiber.child;
    
  1350.       while (current !== null) {
    
  1351.         crawlToInitializeContextsMap(current);
    
  1352.         current = current.sibling;
    
  1353.       }
    
  1354.     }
    
  1355.   }
    
  1356. 
    
  1357.   function getContextChangedKeys(fiber: Fiber): null | boolean | Array<string> {
    
  1358.     if (idToContextsMap !== null) {
    
  1359.       const id = getFiberIDThrows(fiber);
    
  1360.       // $FlowFixMe[incompatible-use] found when upgrading Flow
    
  1361.       const prevContexts = idToContextsMap.has(id)
    
  1362.         ? // $FlowFixMe[incompatible-use] found when upgrading Flow
    
  1363.           idToContextsMap.get(id)
    
  1364.         : null;
    
  1365.       const nextContexts = getContextsForFiber(fiber);
    
  1366. 
    
  1367.       if (prevContexts == null || nextContexts == null) {
    
  1368.         return null;
    
  1369.       }
    
  1370. 
    
  1371.       const [prevLegacyContext, prevModernContext] = prevContexts;
    
  1372.       const [nextLegacyContext, nextModernContext] = nextContexts;
    
  1373. 
    
  1374.       switch (getElementTypeForFiber(fiber)) {
    
  1375.         case ElementTypeClass:
    
  1376.           if (prevContexts && nextContexts) {
    
  1377.             if (nextLegacyContext !== NO_CONTEXT) {
    
  1378.               return getChangedKeys(prevLegacyContext, nextLegacyContext);
    
  1379.             } else if (nextModernContext !== NO_CONTEXT) {
    
  1380.               return prevModernContext !== nextModernContext;
    
  1381.             }
    
  1382.           }
    
  1383.           break;
    
  1384.         case ElementTypeForwardRef:
    
  1385.         case ElementTypeFunction:
    
  1386.         case ElementTypeMemo:
    
  1387.           if (nextModernContext !== NO_CONTEXT) {
    
  1388.             let prevContext = prevModernContext;
    
  1389.             let nextContext = nextModernContext;
    
  1390. 
    
  1391.             while (prevContext && nextContext) {
    
  1392.               // Note this only works for versions of React that support this key (e.v. 18+)
    
  1393.               // For older versions, there's no good way to read the current context value after render has completed.
    
  1394.               // This is because React maintains a stack of context values during render,
    
  1395.               // but by the time DevTools is called, render has finished and the stack is empty.
    
  1396.               if (!is(prevContext.memoizedValue, nextContext.memoizedValue)) {
    
  1397.                 return true;
    
  1398.               }
    
  1399. 
    
  1400.               prevContext = prevContext.next;
    
  1401.               nextContext = nextContext.next;
    
  1402.             }
    
  1403. 
    
  1404.             return false;
    
  1405.           }
    
  1406.           break;
    
  1407.         default:
    
  1408.           break;
    
  1409.       }
    
  1410.     }
    
  1411.     return null;
    
  1412.   }
    
  1413. 
    
  1414.   function isHookThatCanScheduleUpdate(hookObject: any) {
    
  1415.     const queue = hookObject.queue;
    
  1416.     if (!queue) {
    
  1417.       return false;
    
  1418.     }
    
  1419. 
    
  1420.     const boundHasOwnProperty = hasOwnProperty.bind(queue);
    
  1421. 
    
  1422.     // Detect the shape of useState() / useReducer() / useTransition()
    
  1423.     // using the attributes that are unique to these hooks
    
  1424.     // but also stable (e.g. not tied to current Lanes implementation)
    
  1425.     // We don't check for dispatch property, because useTransition doesn't have it
    
  1426.     if (boundHasOwnProperty('pending')) {
    
  1427.       return true;
    
  1428.     }
    
  1429. 
    
  1430.     // Detect useSyncExternalStore()
    
  1431.     return (
    
  1432.       boundHasOwnProperty('value') &&
    
  1433.       boundHasOwnProperty('getSnapshot') &&
    
  1434.       typeof queue.getSnapshot === 'function'
    
  1435.     );
    
  1436.   }
    
  1437. 
    
  1438.   function didStatefulHookChange(prev: any, next: any): boolean {
    
  1439.     const prevMemoizedState = prev.memoizedState;
    
  1440.     const nextMemoizedState = next.memoizedState;
    
  1441. 
    
  1442.     if (isHookThatCanScheduleUpdate(prev)) {
    
  1443.       return prevMemoizedState !== nextMemoizedState;
    
  1444.     }
    
  1445. 
    
  1446.     return false;
    
  1447.   }
    
  1448. 
    
  1449.   function getChangedHooksIndices(prev: any, next: any): null | Array<number> {
    
  1450.     if (prev == null || next == null) {
    
  1451.       return null;
    
  1452.     }
    
  1453. 
    
  1454.     const indices = [];
    
  1455.     let index = 0;
    
  1456.     if (
    
  1457.       next.hasOwnProperty('baseState') &&
    
  1458.       next.hasOwnProperty('memoizedState') &&
    
  1459.       next.hasOwnProperty('next') &&
    
  1460.       next.hasOwnProperty('queue')
    
  1461.     ) {
    
  1462.       while (next !== null) {
    
  1463.         if (didStatefulHookChange(prev, next)) {
    
  1464.           indices.push(index);
    
  1465.         }
    
  1466.         next = next.next;
    
  1467.         prev = prev.next;
    
  1468.         index++;
    
  1469.       }
    
  1470.     }
    
  1471. 
    
  1472.     return indices;
    
  1473.   }
    
  1474. 
    
  1475.   function getChangedKeys(prev: any, next: any): null | Array<string> {
    
  1476.     if (prev == null || next == null) {
    
  1477.       return null;
    
  1478.     }
    
  1479. 
    
  1480.     // We can't report anything meaningful for hooks changes.
    
  1481.     if (
    
  1482.       next.hasOwnProperty('baseState') &&
    
  1483.       next.hasOwnProperty('memoizedState') &&
    
  1484.       next.hasOwnProperty('next') &&
    
  1485.       next.hasOwnProperty('queue')
    
  1486.     ) {
    
  1487.       return null;
    
  1488.     }
    
  1489. 
    
  1490.     const keys = new Set([...Object.keys(prev), ...Object.keys(next)]);
    
  1491.     const changedKeys = [];
    
  1492.     // eslint-disable-next-line no-for-of-loops/no-for-of-loops
    
  1493.     for (const key of keys) {
    
  1494.       if (prev[key] !== next[key]) {
    
  1495.         changedKeys.push(key);
    
  1496.       }
    
  1497.     }
    
  1498. 
    
  1499.     return changedKeys;
    
  1500.   }
    
  1501. 
    
  1502.   // eslint-disable-next-line no-unused-vars
    
  1503.   function didFiberRender(prevFiber: Fiber, nextFiber: Fiber): boolean {
    
  1504.     switch (nextFiber.tag) {
    
  1505.       case ClassComponent:
    
  1506.       case FunctionComponent:
    
  1507.       case ContextConsumer:
    
  1508.       case MemoComponent:
    
  1509.       case SimpleMemoComponent:
    
  1510.       case ForwardRef:
    
  1511.         // For types that execute user code, we check PerformedWork effect.
    
  1512.         // We don't reflect bailouts (either referential or sCU) in DevTools.
    
  1513.         // TODO: This flag is a leaked implementation detail. Once we start
    
  1514.         // releasing DevTools in lockstep with React, we should import a
    
  1515.         // function from the reconciler instead.
    
  1516.         const PerformedWork = 0b000000000000000000000000001;
    
  1517.         return (getFiberFlags(nextFiber) & PerformedWork) === PerformedWork;
    
  1518.       // Note: ContextConsumer only gets PerformedWork effect in 16.3.3+
    
  1519.       // so it won't get highlighted with React 16.3.0 to 16.3.2.
    
  1520.       default:
    
  1521.         // For host components and other types, we compare inputs
    
  1522.         // to determine whether something is an update.
    
  1523.         return (
    
  1524.           prevFiber.memoizedProps !== nextFiber.memoizedProps ||
    
  1525.           prevFiber.memoizedState !== nextFiber.memoizedState ||
    
  1526.           prevFiber.ref !== nextFiber.ref
    
  1527.         );
    
  1528.     }
    
  1529.   }
    
  1530. 
    
  1531.   type OperationsArray = Array<number>;
    
  1532. 
    
  1533.   type StringTableEntry = {
    
  1534.     encodedString: Array<number>,
    
  1535.     id: number,
    
  1536.   };
    
  1537. 
    
  1538.   const pendingOperations: OperationsArray = [];
    
  1539.   const pendingRealUnmountedIDs: Array<number> = [];
    
  1540.   const pendingSimulatedUnmountedIDs: Array<number> = [];
    
  1541.   let pendingOperationsQueue: Array<OperationsArray> | null = [];
    
  1542.   const pendingStringTable: Map<string, StringTableEntry> = new Map();
    
  1543.   let pendingStringTableLength: number = 0;
    
  1544.   let pendingUnmountedRootID: number | null = null;
    
  1545. 
    
  1546.   function pushOperation(op: number): void {
    
  1547.     if (__DEV__) {
    
  1548.       if (!Number.isInteger(op)) {
    
  1549.         console.error(
    
  1550.           'pushOperation() was called but the value is not an integer.',
    
  1551.           op,
    
  1552.         );
    
  1553.       }
    
  1554.     }
    
  1555.     pendingOperations.push(op);
    
  1556.   }
    
  1557. 
    
  1558.   function shouldBailoutWithPendingOperations() {
    
  1559.     if (isProfiling) {
    
  1560.       if (
    
  1561.         currentCommitProfilingMetadata != null &&
    
  1562.         currentCommitProfilingMetadata.durations.length > 0
    
  1563.       ) {
    
  1564.         return false;
    
  1565.       }
    
  1566.     }
    
  1567. 
    
  1568.     return (
    
  1569.       pendingOperations.length === 0 &&
    
  1570.       pendingRealUnmountedIDs.length === 0 &&
    
  1571.       pendingSimulatedUnmountedIDs.length === 0 &&
    
  1572.       pendingUnmountedRootID === null
    
  1573.     );
    
  1574.   }
    
  1575. 
    
  1576.   function flushOrQueueOperations(operations: OperationsArray): void {
    
  1577.     if (shouldBailoutWithPendingOperations()) {
    
  1578.       return;
    
  1579.     }
    
  1580. 
    
  1581.     if (pendingOperationsQueue !== null) {
    
  1582.       pendingOperationsQueue.push(operations);
    
  1583.     } else {
    
  1584.       hook.emit('operations', operations);
    
  1585.     }
    
  1586.   }
    
  1587. 
    
  1588.   let flushPendingErrorsAndWarningsAfterDelayTimeoutID: null | TimeoutID = null;
    
  1589. 
    
  1590.   function clearPendingErrorsAndWarningsAfterDelay() {
    
  1591.     if (flushPendingErrorsAndWarningsAfterDelayTimeoutID !== null) {
    
  1592.       clearTimeout(flushPendingErrorsAndWarningsAfterDelayTimeoutID);
    
  1593.       flushPendingErrorsAndWarningsAfterDelayTimeoutID = null;
    
  1594.     }
    
  1595.   }
    
  1596. 
    
  1597.   function flushPendingErrorsAndWarningsAfterDelay() {
    
  1598.     clearPendingErrorsAndWarningsAfterDelay();
    
  1599. 
    
  1600.     flushPendingErrorsAndWarningsAfterDelayTimeoutID = setTimeout(() => {
    
  1601.       flushPendingErrorsAndWarningsAfterDelayTimeoutID = null;
    
  1602. 
    
  1603.       if (pendingOperations.length > 0) {
    
  1604.         // On the off chance that something else has pushed pending operations,
    
  1605.         // we should bail on warnings; it's probably not safe to push midway.
    
  1606.         return;
    
  1607.       }
    
  1608. 
    
  1609.       recordPendingErrorsAndWarnings();
    
  1610. 
    
  1611.       if (shouldBailoutWithPendingOperations()) {
    
  1612.         // No warnings or errors to flush; we can bail out early here too.
    
  1613.         return;
    
  1614.       }
    
  1615. 
    
  1616.       // We can create a smaller operations array than flushPendingEvents()
    
  1617.       // because we only need to flush warning and error counts.
    
  1618.       // Only a few pieces of fixed information are required up front.
    
  1619.       const operations: OperationsArray = new Array(
    
  1620.         3 + pendingOperations.length,
    
  1621.       );
    
  1622.       operations[0] = rendererID;
    
  1623.       operations[1] = currentRootID;
    
  1624.       operations[2] = 0; // String table size
    
  1625.       for (let j = 0; j < pendingOperations.length; j++) {
    
  1626.         operations[3 + j] = pendingOperations[j];
    
  1627.       }
    
  1628. 
    
  1629.       flushOrQueueOperations(operations);
    
  1630. 
    
  1631.       pendingOperations.length = 0;
    
  1632.     }, 1000);
    
  1633.   }
    
  1634. 
    
  1635.   function reevaluateErrorsAndWarnings() {
    
  1636.     fibersWithChangedErrorOrWarningCounts.clear();
    
  1637.     fiberIDToErrorsMap.forEach((countMap, fiberID) => {
    
  1638.       const fiber = idToArbitraryFiberMap.get(fiberID);
    
  1639.       if (fiber != null) {
    
  1640.         fibersWithChangedErrorOrWarningCounts.add(fiber);
    
  1641.       }
    
  1642.     });
    
  1643.     fiberIDToWarningsMap.forEach((countMap, fiberID) => {
    
  1644.       const fiber = idToArbitraryFiberMap.get(fiberID);
    
  1645.       if (fiber != null) {
    
  1646.         fibersWithChangedErrorOrWarningCounts.add(fiber);
    
  1647.       }
    
  1648.     });
    
  1649.     recordPendingErrorsAndWarnings();
    
  1650.   }
    
  1651. 
    
  1652.   function mergeMapsAndGetCountHelper(
    
  1653.     fiber: Fiber,
    
  1654.     fiberID: number,
    
  1655.     pendingFiberToMessageCountMap: Map<Fiber, Map<string, number>>,
    
  1656.     fiberIDToMessageCountMap: Map<number, Map<string, number>>,
    
  1657.   ): number {
    
  1658.     let newCount = 0;
    
  1659. 
    
  1660.     let messageCountMap = fiberIDToMessageCountMap.get(fiberID);
    
  1661. 
    
  1662.     const pendingMessageCountMap = pendingFiberToMessageCountMap.get(fiber);
    
  1663.     if (pendingMessageCountMap != null) {
    
  1664.       if (messageCountMap == null) {
    
  1665.         messageCountMap = pendingMessageCountMap;
    
  1666. 
    
  1667.         fiberIDToMessageCountMap.set(fiberID, pendingMessageCountMap);
    
  1668.       } else {
    
  1669.         // This Flow refinement should not be necessary and yet...
    
  1670.         const refinedMessageCountMap = ((messageCountMap: any): Map<
    
  1671.           string,
    
  1672.           number,
    
  1673.         >);
    
  1674. 
    
  1675.         pendingMessageCountMap.forEach((pendingCount, message) => {
    
  1676.           const previousCount = refinedMessageCountMap.get(message) || 0;
    
  1677.           refinedMessageCountMap.set(message, previousCount + pendingCount);
    
  1678.         });
    
  1679.       }
    
  1680.     }
    
  1681. 
    
  1682.     if (!shouldFilterFiber(fiber)) {
    
  1683.       if (messageCountMap != null) {
    
  1684.         messageCountMap.forEach(count => {
    
  1685.           newCount += count;
    
  1686.         });
    
  1687.       }
    
  1688.     }
    
  1689. 
    
  1690.     pendingFiberToMessageCountMap.delete(fiber);
    
  1691. 
    
  1692.     return newCount;
    
  1693.   }
    
  1694. 
    
  1695.   function recordPendingErrorsAndWarnings() {
    
  1696.     clearPendingErrorsAndWarningsAfterDelay();
    
  1697. 
    
  1698.     fibersWithChangedErrorOrWarningCounts.forEach(fiber => {
    
  1699.       const fiberID = getFiberIDUnsafe(fiber);
    
  1700.       if (fiberID === null) {
    
  1701.         // Don't send updates for Fibers that didn't mount due to e.g. Suspense or an error boundary.
    
  1702.       } else {
    
  1703.         const errorCount = mergeMapsAndGetCountHelper(
    
  1704.           fiber,
    
  1705.           fiberID,
    
  1706.           pendingFiberToErrorsMap,
    
  1707.           fiberIDToErrorsMap,
    
  1708.         );
    
  1709.         const warningCount = mergeMapsAndGetCountHelper(
    
  1710.           fiber,
    
  1711.           fiberID,
    
  1712.           pendingFiberToWarningsMap,
    
  1713.           fiberIDToWarningsMap,
    
  1714.         );
    
  1715. 
    
  1716.         pushOperation(TREE_OPERATION_UPDATE_ERRORS_OR_WARNINGS);
    
  1717.         pushOperation(fiberID);
    
  1718.         pushOperation(errorCount);
    
  1719.         pushOperation(warningCount);
    
  1720.       }
    
  1721. 
    
  1722.       // Always clean up so that we don't leak.
    
  1723.       pendingFiberToErrorsMap.delete(fiber);
    
  1724.       pendingFiberToWarningsMap.delete(fiber);
    
  1725.     });
    
  1726.     fibersWithChangedErrorOrWarningCounts.clear();
    
  1727.   }
    
  1728. 
    
  1729.   function flushPendingEvents(root: Object): void {
    
  1730.     // Add any pending errors and warnings to the operations array.
    
  1731.     // We do this just before flushing, so we can ignore errors for no-longer-mounted Fibers.
    
  1732.     recordPendingErrorsAndWarnings();
    
  1733. 
    
  1734.     if (shouldBailoutWithPendingOperations()) {
    
  1735.       // If we aren't profiling, we can just bail out here.
    
  1736.       // No use sending an empty update over the bridge.
    
  1737.       //
    
  1738.       // The Profiler stores metadata for each commit and reconstructs the app tree per commit using:
    
  1739.       // (1) an initial tree snapshot and
    
  1740.       // (2) the operations array for each commit
    
  1741.       // Because of this, it's important that the operations and metadata arrays align,
    
  1742.       // So it's important not to omit even empty operations while profiling is active.
    
  1743.       return;
    
  1744.     }
    
  1745. 
    
  1746.     const numUnmountIDs =
    
  1747.       pendingRealUnmountedIDs.length +
    
  1748.       pendingSimulatedUnmountedIDs.length +
    
  1749.       (pendingUnmountedRootID === null ? 0 : 1);
    
  1750. 
    
  1751.     const operations = new Array<number>(
    
  1752.       // Identify which renderer this update is coming from.
    
  1753.       2 + // [rendererID, rootFiberID]
    
  1754.         // How big is the string table?
    
  1755.         1 + // [stringTableLength]
    
  1756.         // Then goes the actual string table.
    
  1757.         pendingStringTableLength +
    
  1758.         // All unmounts are batched in a single message.
    
  1759.         // [TREE_OPERATION_REMOVE, removedIDLength, ...ids]
    
  1760.         (numUnmountIDs > 0 ? 2 + numUnmountIDs : 0) +
    
  1761.         // Regular operations
    
  1762.         pendingOperations.length,
    
  1763.     );
    
  1764. 
    
  1765.     // Identify which renderer this update is coming from.
    
  1766.     // This enables roots to be mapped to renderers,
    
  1767.     // Which in turn enables fiber props, states, and hooks to be inspected.
    
  1768.     let i = 0;
    
  1769.     operations[i++] = rendererID;
    
  1770.     operations[i++] = currentRootID;
    
  1771. 
    
  1772.     // Now fill in the string table.
    
  1773.     // [stringTableLength, str1Length, ...str1, str2Length, ...str2, ...]
    
  1774.     operations[i++] = pendingStringTableLength;
    
  1775.     pendingStringTable.forEach((entry, stringKey) => {
    
  1776.       const encodedString = entry.encodedString;
    
  1777. 
    
  1778.       // Don't use the string length.
    
  1779.       // It won't work for multibyte characters (like emoji).
    
  1780.       const length = encodedString.length;
    
  1781. 
    
  1782.       operations[i++] = length;
    
  1783.       for (let j = 0; j < length; j++) {
    
  1784.         operations[i + j] = encodedString[j];
    
  1785.       }
    
  1786. 
    
  1787.       i += length;
    
  1788.     });
    
  1789. 
    
  1790.     if (numUnmountIDs > 0) {
    
  1791.       // All unmounts except roots are batched in a single message.
    
  1792.       operations[i++] = TREE_OPERATION_REMOVE;
    
  1793.       // The first number is how many unmounted IDs we're gonna send.
    
  1794.       operations[i++] = numUnmountIDs;
    
  1795.       // Fill in the real unmounts in the reverse order.
    
  1796.       // They were inserted parents-first by React, but we want children-first.
    
  1797.       // So we traverse our array backwards.
    
  1798.       for (let j = pendingRealUnmountedIDs.length - 1; j >= 0; j--) {
    
  1799.         operations[i++] = pendingRealUnmountedIDs[j];
    
  1800.       }
    
  1801.       // Fill in the simulated unmounts (hidden Suspense subtrees) in their order.
    
  1802.       // (We want children to go before parents.)
    
  1803.       // They go *after* the real unmounts because we know for sure they won't be
    
  1804.       // children of already pushed "real" IDs. If they were, we wouldn't be able
    
  1805.       // to discover them during the traversal, as they would have been deleted.
    
  1806.       for (let j = 0; j < pendingSimulatedUnmountedIDs.length; j++) {
    
  1807.         operations[i + j] = pendingSimulatedUnmountedIDs[j];
    
  1808.       }
    
  1809.       i += pendingSimulatedUnmountedIDs.length;
    
  1810.       // The root ID should always be unmounted last.
    
  1811.       if (pendingUnmountedRootID !== null) {
    
  1812.         operations[i] = pendingUnmountedRootID;
    
  1813.         i++;
    
  1814.       }
    
  1815.     }
    
  1816.     // Fill in the rest of the operations.
    
  1817.     for (let j = 0; j < pendingOperations.length; j++) {
    
  1818.       operations[i + j] = pendingOperations[j];
    
  1819.     }
    
  1820.     i += pendingOperations.length;
    
  1821. 
    
  1822.     // Let the frontend know about tree operations.
    
  1823.     flushOrQueueOperations(operations);
    
  1824. 
    
  1825.     // Reset all of the pending state now that we've told the frontend about it.
    
  1826.     pendingOperations.length = 0;
    
  1827.     pendingRealUnmountedIDs.length = 0;
    
  1828.     pendingSimulatedUnmountedIDs.length = 0;
    
  1829.     pendingUnmountedRootID = null;
    
  1830.     pendingStringTable.clear();
    
  1831.     pendingStringTableLength = 0;
    
  1832.   }
    
  1833. 
    
  1834.   function getStringID(string: string | null): number {
    
  1835.     if (string === null) {
    
  1836.       return 0;
    
  1837.     }
    
  1838.     const existingEntry = pendingStringTable.get(string);
    
  1839.     if (existingEntry !== undefined) {
    
  1840.       return existingEntry.id;
    
  1841.     }
    
  1842. 
    
  1843.     const id = pendingStringTable.size + 1;
    
  1844.     const encodedString = utfEncodeString(string);
    
  1845. 
    
  1846.     pendingStringTable.set(string, {
    
  1847.       encodedString,
    
  1848.       id,
    
  1849.     });
    
  1850. 
    
  1851.     // The string table total length needs to account both for the string length,
    
  1852.     // and for the array item that contains the length itself.
    
  1853.     //
    
  1854.     // Don't use string length for this table.
    
  1855.     // It won't work for multibyte characters (like emoji).
    
  1856.     pendingStringTableLength += encodedString.length + 1;
    
  1857. 
    
  1858.     return id;
    
  1859.   }
    
  1860. 
    
  1861.   function recordMount(fiber: Fiber, parentFiber: Fiber | null) {
    
  1862.     const isRoot = fiber.tag === HostRoot;
    
  1863.     const id = getOrGenerateFiberID(fiber);
    
  1864. 
    
  1865.     if (__DEBUG__) {
    
  1866.       debug('recordMount()', fiber, parentFiber);
    
  1867.     }
    
  1868. 
    
  1869.     const hasOwnerMetadata = fiber.hasOwnProperty('_debugOwner');
    
  1870.     const isProfilingSupported = fiber.hasOwnProperty('treeBaseDuration');
    
  1871. 
    
  1872.     // Adding a new field here would require a bridge protocol version bump (a backwads breaking change).
    
  1873.     // Instead let's re-purpose a pre-existing field to carry more information.
    
  1874.     let profilingFlags = 0;
    
  1875.     if (isProfilingSupported) {
    
  1876.       profilingFlags = PROFILING_FLAG_BASIC_SUPPORT;
    
  1877.       if (typeof injectProfilingHooks === 'function') {
    
  1878.         profilingFlags |= PROFILING_FLAG_TIMELINE_SUPPORT;
    
  1879.       }
    
  1880.     }
    
  1881. 
    
  1882.     if (isRoot) {
    
  1883.       pushOperation(TREE_OPERATION_ADD);
    
  1884.       pushOperation(id);
    
  1885.       pushOperation(ElementTypeRoot);
    
  1886.       pushOperation((fiber.mode & StrictModeBits) !== 0 ? 1 : 0);
    
  1887.       pushOperation(profilingFlags);
    
  1888.       pushOperation(StrictModeBits !== 0 ? 1 : 0);
    
  1889.       pushOperation(hasOwnerMetadata ? 1 : 0);
    
  1890. 
    
  1891.       if (isProfiling) {
    
  1892.         if (displayNamesByRootID !== null) {
    
  1893.           displayNamesByRootID.set(id, getDisplayNameForRoot(fiber));
    
  1894.         }
    
  1895.       }
    
  1896.     } else {
    
  1897.       const {key} = fiber;
    
  1898.       const displayName = getDisplayNameForFiber(fiber);
    
  1899.       const elementType = getElementTypeForFiber(fiber);
    
  1900.       const {_debugOwner} = fiber;
    
  1901. 
    
  1902.       // Ideally we should call getFiberIDThrows() for _debugOwner,
    
  1903.       // since owners are almost always higher in the tree (and so have already been processed),
    
  1904.       // but in some (rare) instances reported in open source, a descendant mounts before an owner.
    
  1905.       // Since this is a DEV only field it's probably okay to also just lazily generate and ID here if needed.
    
  1906.       // See https://github.com/facebook/react/issues/21445
    
  1907.       const ownerID =
    
  1908.         _debugOwner != null ? getOrGenerateFiberID(_debugOwner) : 0;
    
  1909.       const parentID = parentFiber ? getFiberIDThrows(parentFiber) : 0;
    
  1910. 
    
  1911.       const displayNameStringID = getStringID(displayName);
    
  1912. 
    
  1913.       // This check is a guard to handle a React element that has been modified
    
  1914.       // in such a way as to bypass the default stringification of the "key" property.
    
  1915.       const keyString = key === null ? null : String(key);
    
  1916.       const keyStringID = getStringID(keyString);
    
  1917. 
    
  1918.       pushOperation(TREE_OPERATION_ADD);
    
  1919.       pushOperation(id);
    
  1920.       pushOperation(elementType);
    
  1921.       pushOperation(parentID);
    
  1922.       pushOperation(ownerID);
    
  1923.       pushOperation(displayNameStringID);
    
  1924.       pushOperation(keyStringID);
    
  1925. 
    
  1926.       // If this subtree has a new mode, let the frontend know.
    
  1927.       if (
    
  1928.         (fiber.mode & StrictModeBits) !== 0 &&
    
  1929.         (((parentFiber: any): Fiber).mode & StrictModeBits) === 0
    
  1930.       ) {
    
  1931.         pushOperation(TREE_OPERATION_SET_SUBTREE_MODE);
    
  1932.         pushOperation(id);
    
  1933.         pushOperation(StrictMode);
    
  1934.       }
    
  1935.     }
    
  1936. 
    
  1937.     if (isProfilingSupported) {
    
  1938.       idToRootMap.set(id, currentRootID);
    
  1939. 
    
  1940.       recordProfilingDurations(fiber);
    
  1941.     }
    
  1942.   }
    
  1943. 
    
  1944.   function recordUnmount(fiber: Fiber, isSimulated: boolean) {
    
  1945.     if (__DEBUG__) {
    
  1946.       debug(
    
  1947.         'recordUnmount()',
    
  1948.         fiber,
    
  1949.         null,
    
  1950.         isSimulated ? 'unmount is simulated' : '',
    
  1951.       );
    
  1952.     }
    
  1953. 
    
  1954.     if (trackedPathMatchFiber !== null) {
    
  1955.       // We're in the process of trying to restore previous selection.
    
  1956.       // If this fiber matched but is being unmounted, there's no use trying.
    
  1957.       // Reset the state so we don't keep holding onto it.
    
  1958.       if (
    
  1959.         fiber === trackedPathMatchFiber ||
    
  1960.         fiber === trackedPathMatchFiber.alternate
    
  1961.       ) {
    
  1962.         setTrackedPath(null);
    
  1963.       }
    
  1964.     }
    
  1965. 
    
  1966.     const unsafeID = getFiberIDUnsafe(fiber);
    
  1967.     if (unsafeID === null) {
    
  1968.       // If we've never seen this Fiber, it might be inside of a legacy render Suspense fragment (so the store is not even aware of it).
    
  1969.       // In that case we can just ignore it or it will cause errors later on.
    
  1970.       // One example of this is a Lazy component that never resolves before being unmounted.
    
  1971.       //
    
  1972.       // This also might indicate a Fast Refresh force-remount scenario.
    
  1973.       //
    
  1974.       // TODO: This is fragile and can obscure actual bugs.
    
  1975.       return;
    
  1976.     }
    
  1977. 
    
  1978.     // Flow refinement.
    
  1979.     const id = ((unsafeID: any): number);
    
  1980.     const isRoot = fiber.tag === HostRoot;
    
  1981.     if (isRoot) {
    
  1982.       // Roots must be removed only after all children (pending and simulated) have been removed.
    
  1983.       // So we track it separately.
    
  1984.       pendingUnmountedRootID = id;
    
  1985.     } else if (!shouldFilterFiber(fiber)) {
    
  1986.       // To maintain child-first ordering,
    
  1987.       // we'll push it into one of these queues,
    
  1988.       // and later arrange them in the correct order.
    
  1989.       if (isSimulated) {
    
  1990.         pendingSimulatedUnmountedIDs.push(id);
    
  1991.       } else {
    
  1992.         pendingRealUnmountedIDs.push(id);
    
  1993.       }
    
  1994.     }
    
  1995. 
    
  1996.     if (!fiber._debugNeedsRemount) {
    
  1997.       untrackFiberID(fiber);
    
  1998. 
    
  1999.       const isProfilingSupported = fiber.hasOwnProperty('treeBaseDuration');
    
  2000.       if (isProfilingSupported) {
    
  2001.         idToRootMap.delete(id);
    
  2002.         idToTreeBaseDurationMap.delete(id);
    
  2003.       }
    
  2004.     }
    
  2005.   }
    
  2006. 
    
  2007.   function mountFiberRecursively(
    
  2008.     firstChild: Fiber,
    
  2009.     parentFiber: Fiber | null,
    
  2010.     traverseSiblings: boolean,
    
  2011.     traceNearestHostComponentUpdate: boolean,
    
  2012.   ) {
    
  2013.     // Iterate over siblings rather than recursing.
    
  2014.     // This reduces the chance of stack overflow for wide trees (e.g. lists with many items).
    
  2015.     let fiber: Fiber | null = firstChild;
    
  2016.     while (fiber !== null) {
    
  2017.       // Generate an ID even for filtered Fibers, in case it's needed later (e.g. for Profiling).
    
  2018.       getOrGenerateFiberID(fiber);
    
  2019. 
    
  2020.       if (__DEBUG__) {
    
  2021.         debug('mountFiberRecursively()', fiber, parentFiber);
    
  2022.       }
    
  2023. 
    
  2024.       // If we have the tree selection from previous reload, try to match this Fiber.
    
  2025.       // Also remember whether to do the same for siblings.
    
  2026.       const mightSiblingsBeOnTrackedPath =
    
  2027.         updateTrackedPathStateBeforeMount(fiber);
    
  2028. 
    
  2029.       const shouldIncludeInTree = !shouldFilterFiber(fiber);
    
  2030.       if (shouldIncludeInTree) {
    
  2031.         recordMount(fiber, parentFiber);
    
  2032.       }
    
  2033. 
    
  2034.       if (traceUpdatesEnabled) {
    
  2035.         if (traceNearestHostComponentUpdate) {
    
  2036.           const elementType = getElementTypeForFiber(fiber);
    
  2037.           // If an ancestor updated, we should mark the nearest host nodes for highlighting.
    
  2038.           if (elementType === ElementTypeHostComponent) {
    
  2039.             traceUpdatesForNodes.add(fiber.stateNode);
    
  2040.             traceNearestHostComponentUpdate = false;
    
  2041.           }
    
  2042.         }
    
  2043. 
    
  2044.         // We intentionally do not re-enable the traceNearestHostComponentUpdate flag in this branch,
    
  2045.         // because we don't want to highlight every host node inside of a newly mounted subtree.
    
  2046.       }
    
  2047. 
    
  2048.       const isSuspense = fiber.tag === ReactTypeOfWork.SuspenseComponent;
    
  2049.       if (isSuspense) {
    
  2050.         const isTimedOut = fiber.memoizedState !== null;
    
  2051.         if (isTimedOut) {
    
  2052.           // Special case: if Suspense mounts in a timed-out state,
    
  2053.           // get the fallback child from the inner fragment and mount
    
  2054.           // it as if it was our own child. Updates handle this too.
    
  2055.           const primaryChildFragment = fiber.child;
    
  2056.           const fallbackChildFragment = primaryChildFragment
    
  2057.             ? primaryChildFragment.sibling
    
  2058.             : null;
    
  2059.           const fallbackChild = fallbackChildFragment
    
  2060.             ? fallbackChildFragment.child
    
  2061.             : null;
    
  2062.           if (fallbackChild !== null) {
    
  2063.             mountFiberRecursively(
    
  2064.               fallbackChild,
    
  2065.               shouldIncludeInTree ? fiber : parentFiber,
    
  2066.               true,
    
  2067.               traceNearestHostComponentUpdate,
    
  2068.             );
    
  2069.           }
    
  2070.         } else {
    
  2071.           let primaryChild: Fiber | null = null;
    
  2072.           const areSuspenseChildrenConditionallyWrapped =
    
  2073.             OffscreenComponent === -1;
    
  2074.           if (areSuspenseChildrenConditionallyWrapped) {
    
  2075.             primaryChild = fiber.child;
    
  2076.           } else if (fiber.child !== null) {
    
  2077.             primaryChild = fiber.child.child;
    
  2078.           }
    
  2079.           if (primaryChild !== null) {
    
  2080.             mountFiberRecursively(
    
  2081.               primaryChild,
    
  2082.               shouldIncludeInTree ? fiber : parentFiber,
    
  2083.               true,
    
  2084.               traceNearestHostComponentUpdate,
    
  2085.             );
    
  2086.           }
    
  2087.         }
    
  2088.       } else {
    
  2089.         if (fiber.child !== null) {
    
  2090.           mountFiberRecursively(
    
  2091.             fiber.child,
    
  2092.             shouldIncludeInTree ? fiber : parentFiber,
    
  2093.             true,
    
  2094.             traceNearestHostComponentUpdate,
    
  2095.           );
    
  2096.         }
    
  2097.       }
    
  2098. 
    
  2099.       // We're exiting this Fiber now, and entering its siblings.
    
  2100.       // If we have selection to restore, we might need to re-activate tracking.
    
  2101.       updateTrackedPathStateAfterMount(mightSiblingsBeOnTrackedPath);
    
  2102. 
    
  2103.       fiber = traverseSiblings ? fiber.sibling : null;
    
  2104.     }
    
  2105.   }
    
  2106. 
    
  2107.   // We use this to simulate unmounting for Suspense trees
    
  2108.   // when we switch from primary to fallback.
    
  2109.   function unmountFiberChildrenRecursively(fiber: Fiber) {
    
  2110.     if (__DEBUG__) {
    
  2111.       debug('unmountFiberChildrenRecursively()', fiber);
    
  2112.     }
    
  2113. 
    
  2114.     // We might meet a nested Suspense on our way.
    
  2115.     const isTimedOutSuspense =
    
  2116.       fiber.tag === ReactTypeOfWork.SuspenseComponent &&
    
  2117.       fiber.memoizedState !== null;
    
  2118. 
    
  2119.     let child = fiber.child;
    
  2120.     if (isTimedOutSuspense) {
    
  2121.       // If it's showing fallback tree, let's traverse it instead.
    
  2122.       const primaryChildFragment = fiber.child;
    
  2123.       const fallbackChildFragment = primaryChildFragment
    
  2124.         ? primaryChildFragment.sibling
    
  2125.         : null;
    
  2126.       // Skip over to the real Fiber child.
    
  2127.       child = fallbackChildFragment ? fallbackChildFragment.child : null;
    
  2128.     }
    
  2129. 
    
  2130.     while (child !== null) {
    
  2131.       // Record simulated unmounts children-first.
    
  2132.       // We skip nodes without return because those are real unmounts.
    
  2133.       if (child.return !== null) {
    
  2134.         unmountFiberChildrenRecursively(child);
    
  2135.         recordUnmount(child, true);
    
  2136.       }
    
  2137.       child = child.sibling;
    
  2138.     }
    
  2139.   }
    
  2140. 
    
  2141.   function recordProfilingDurations(fiber: Fiber) {
    
  2142.     const id = getFiberIDThrows(fiber);
    
  2143.     const {actualDuration, treeBaseDuration} = fiber;
    
  2144. 
    
  2145.     idToTreeBaseDurationMap.set(id, treeBaseDuration || 0);
    
  2146. 
    
  2147.     if (isProfiling) {
    
  2148.       const {alternate} = fiber;
    
  2149. 
    
  2150.       // It's important to update treeBaseDuration even if the current Fiber did not render,
    
  2151.       // because it's possible that one of its descendants did.
    
  2152.       if (
    
  2153.         alternate == null ||
    
  2154.         treeBaseDuration !== alternate.treeBaseDuration
    
  2155.       ) {
    
  2156.         // Tree base duration updates are included in the operations typed array.
    
  2157.         // So we have to convert them from milliseconds to microseconds so we can send them as ints.
    
  2158.         const convertedTreeBaseDuration = Math.floor(
    
  2159.           (treeBaseDuration || 0) * 1000,
    
  2160.         );
    
  2161.         pushOperation(TREE_OPERATION_UPDATE_TREE_BASE_DURATION);
    
  2162.         pushOperation(id);
    
  2163.         pushOperation(convertedTreeBaseDuration);
    
  2164.       }
    
  2165. 
    
  2166.       if (alternate == null || didFiberRender(alternate, fiber)) {
    
  2167.         if (actualDuration != null) {
    
  2168.           // The actual duration reported by React includes time spent working on children.
    
  2169.           // This is useful information, but it's also useful to be able to exclude child durations.
    
  2170.           // The frontend can't compute this, since the immediate children may have been filtered out.
    
  2171.           // So we need to do this on the backend.
    
  2172.           // Note that this calculated self duration is not the same thing as the base duration.
    
  2173.           // The two are calculated differently (tree duration does not accumulate).
    
  2174.           let selfDuration = actualDuration;
    
  2175.           let child = fiber.child;
    
  2176.           while (child !== null) {
    
  2177.             selfDuration -= child.actualDuration || 0;
    
  2178.             child = child.sibling;
    
  2179.           }
    
  2180. 
    
  2181.           // If profiling is active, store durations for elements that were rendered during the commit.
    
  2182.           // Note that we should do this for any fiber we performed work on, regardless of its actualDuration value.
    
  2183.           // In some cases actualDuration might be 0 for fibers we worked on (particularly if we're using Date.now)
    
  2184.           // In other cases (e.g. Memo) actualDuration might be greater than 0 even if we "bailed out".
    
  2185.           const metadata =
    
  2186.             ((currentCommitProfilingMetadata: any): CommitProfilingData);
    
  2187.           metadata.durations.push(id, actualDuration, selfDuration);
    
  2188.           metadata.maxActualDuration = Math.max(
    
  2189.             metadata.maxActualDuration,
    
  2190.             actualDuration,
    
  2191.           );
    
  2192. 
    
  2193.           if (recordChangeDescriptions) {
    
  2194.             const changeDescription = getChangeDescription(alternate, fiber);
    
  2195.             if (changeDescription !== null) {
    
  2196.               if (metadata.changeDescriptions !== null) {
    
  2197.                 metadata.changeDescriptions.set(id, changeDescription);
    
  2198.               }
    
  2199.             }
    
  2200. 
    
  2201.             updateContextsForFiber(fiber);
    
  2202.           }
    
  2203.         }
    
  2204.       }
    
  2205.     }
    
  2206.   }
    
  2207. 
    
  2208.   function recordResetChildren(fiber: Fiber, childSet: Fiber) {
    
  2209.     if (__DEBUG__) {
    
  2210.       debug('recordResetChildren()', childSet, fiber);
    
  2211.     }
    
  2212.     // The frontend only really cares about the displayName, key, and children.
    
  2213.     // The first two don't really change, so we are only concerned with the order of children here.
    
  2214.     // This is trickier than a simple comparison though, since certain types of fibers are filtered.
    
  2215.     const nextChildren: Array<number> = [];
    
  2216. 
    
  2217.     // This is a naive implementation that shallowly recourses children.
    
  2218.     // We might want to revisit this if it proves to be too inefficient.
    
  2219.     let child: null | Fiber = childSet;
    
  2220.     while (child !== null) {
    
  2221.       findReorderedChildrenRecursively(child, nextChildren);
    
  2222.       child = child.sibling;
    
  2223.     }
    
  2224. 
    
  2225.     const numChildren = nextChildren.length;
    
  2226.     if (numChildren < 2) {
    
  2227.       // No need to reorder.
    
  2228.       return;
    
  2229.     }
    
  2230.     pushOperation(TREE_OPERATION_REORDER_CHILDREN);
    
  2231.     pushOperation(getFiberIDThrows(fiber));
    
  2232.     pushOperation(numChildren);
    
  2233.     for (let i = 0; i < nextChildren.length; i++) {
    
  2234.       pushOperation(nextChildren[i]);
    
  2235.     }
    
  2236.   }
    
  2237. 
    
  2238.   function findReorderedChildrenRecursively(
    
  2239.     fiber: Fiber,
    
  2240.     nextChildren: Array<number>,
    
  2241.   ) {
    
  2242.     if (!shouldFilterFiber(fiber)) {
    
  2243.       nextChildren.push(getFiberIDThrows(fiber));
    
  2244.     } else {
    
  2245.       let child = fiber.child;
    
  2246.       const isTimedOutSuspense =
    
  2247.         fiber.tag === SuspenseComponent && fiber.memoizedState !== null;
    
  2248.       if (isTimedOutSuspense) {
    
  2249.         // Special case: if Suspense mounts in a timed-out state,
    
  2250.         // get the fallback child from the inner fragment,
    
  2251.         // and skip over the primary child.
    
  2252.         const primaryChildFragment = fiber.child;
    
  2253.         const fallbackChildFragment = primaryChildFragment
    
  2254.           ? primaryChildFragment.sibling
    
  2255.           : null;
    
  2256.         const fallbackChild = fallbackChildFragment
    
  2257.           ? fallbackChildFragment.child
    
  2258.           : null;
    
  2259.         if (fallbackChild !== null) {
    
  2260.           child = fallbackChild;
    
  2261.         }
    
  2262.       }
    
  2263.       while (child !== null) {
    
  2264.         findReorderedChildrenRecursively(child, nextChildren);
    
  2265.         child = child.sibling;
    
  2266.       }
    
  2267.     }
    
  2268.   }
    
  2269. 
    
  2270.   // Returns whether closest unfiltered fiber parent needs to reset its child list.
    
  2271.   function updateFiberRecursively(
    
  2272.     nextFiber: Fiber,
    
  2273.     prevFiber: Fiber,
    
  2274.     parentFiber: Fiber | null,
    
  2275.     traceNearestHostComponentUpdate: boolean,
    
  2276.   ): boolean {
    
  2277.     const id = getOrGenerateFiberID(nextFiber);
    
  2278. 
    
  2279.     if (__DEBUG__) {
    
  2280.       debug('updateFiberRecursively()', nextFiber, parentFiber);
    
  2281.     }
    
  2282. 
    
  2283.     if (traceUpdatesEnabled) {
    
  2284.       const elementType = getElementTypeForFiber(nextFiber);
    
  2285.       if (traceNearestHostComponentUpdate) {
    
  2286.         // If an ancestor updated, we should mark the nearest host nodes for highlighting.
    
  2287.         if (elementType === ElementTypeHostComponent) {
    
  2288.           traceUpdatesForNodes.add(nextFiber.stateNode);
    
  2289.           traceNearestHostComponentUpdate = false;
    
  2290.         }
    
  2291.       } else {
    
  2292.         if (
    
  2293.           elementType === ElementTypeFunction ||
    
  2294.           elementType === ElementTypeClass ||
    
  2295.           elementType === ElementTypeContext ||
    
  2296.           elementType === ElementTypeMemo ||
    
  2297.           elementType === ElementTypeForwardRef
    
  2298.         ) {
    
  2299.           // Otherwise if this is a traced ancestor, flag for the nearest host descendant(s).
    
  2300.           traceNearestHostComponentUpdate = didFiberRender(
    
  2301.             prevFiber,
    
  2302.             nextFiber,
    
  2303.           );
    
  2304.         }
    
  2305.       }
    
  2306.     }
    
  2307. 
    
  2308.     if (
    
  2309.       mostRecentlyInspectedElement !== null &&
    
  2310.       mostRecentlyInspectedElement.id === id &&
    
  2311.       didFiberRender(prevFiber, nextFiber)
    
  2312.     ) {
    
  2313.       // If this Fiber has updated, clear cached inspected data.
    
  2314.       // If it is inspected again, it may need to be re-run to obtain updated hooks values.
    
  2315.       hasElementUpdatedSinceLastInspected = true;
    
  2316.     }
    
  2317. 
    
  2318.     const shouldIncludeInTree = !shouldFilterFiber(nextFiber);
    
  2319.     const isSuspense = nextFiber.tag === SuspenseComponent;
    
  2320.     let shouldResetChildren = false;
    
  2321.     // The behavior of timed-out Suspense trees is unique.
    
  2322.     // Rather than unmount the timed out content (and possibly lose important state),
    
  2323.     // React re-parents this content within a hidden Fragment while the fallback is showing.
    
  2324.     // This behavior doesn't need to be observable in the DevTools though.
    
  2325.     // It might even result in a bad user experience for e.g. node selection in the Elements panel.
    
  2326.     // The easiest fix is to strip out the intermediate Fragment fibers,
    
  2327.     // so the Elements panel and Profiler don't need to special case them.
    
  2328.     // Suspense components only have a non-null memoizedState if they're timed-out.
    
  2329.     const prevDidTimeout = isSuspense && prevFiber.memoizedState !== null;
    
  2330.     const nextDidTimeOut = isSuspense && nextFiber.memoizedState !== null;
    
  2331.     // The logic below is inspired by the code paths in updateSuspenseComponent()
    
  2332.     // inside ReactFiberBeginWork in the React source code.
    
  2333.     if (prevDidTimeout && nextDidTimeOut) {
    
  2334.       // Fallback -> Fallback:
    
  2335.       // 1. Reconcile fallback set.
    
  2336.       const nextFiberChild = nextFiber.child;
    
  2337.       const nextFallbackChildSet = nextFiberChild
    
  2338.         ? nextFiberChild.sibling
    
  2339.         : null;
    
  2340.       // Note: We can't use nextFiber.child.sibling.alternate
    
  2341.       // because the set is special and alternate may not exist.
    
  2342.       const prevFiberChild = prevFiber.child;
    
  2343.       const prevFallbackChildSet = prevFiberChild
    
  2344.         ? prevFiberChild.sibling
    
  2345.         : null;
    
  2346. 
    
  2347.       if (prevFallbackChildSet == null && nextFallbackChildSet != null) {
    
  2348.         mountFiberRecursively(
    
  2349.           nextFallbackChildSet,
    
  2350.           shouldIncludeInTree ? nextFiber : parentFiber,
    
  2351.           true,
    
  2352.           traceNearestHostComponentUpdate,
    
  2353.         );
    
  2354. 
    
  2355.         shouldResetChildren = true;
    
  2356.       }
    
  2357. 
    
  2358.       if (
    
  2359.         nextFallbackChildSet != null &&
    
  2360.         prevFallbackChildSet != null &&
    
  2361.         updateFiberRecursively(
    
  2362.           nextFallbackChildSet,
    
  2363.           prevFallbackChildSet,
    
  2364.           nextFiber,
    
  2365.           traceNearestHostComponentUpdate,
    
  2366.         )
    
  2367.       ) {
    
  2368.         shouldResetChildren = true;
    
  2369.       }
    
  2370.     } else if (prevDidTimeout && !nextDidTimeOut) {
    
  2371.       // Fallback -> Primary:
    
  2372.       // 1. Unmount fallback set
    
  2373.       // Note: don't emulate fallback unmount because React actually did it.
    
  2374.       // 2. Mount primary set
    
  2375.       const nextPrimaryChildSet = nextFiber.child;
    
  2376.       if (nextPrimaryChildSet !== null) {
    
  2377.         mountFiberRecursively(
    
  2378.           nextPrimaryChildSet,
    
  2379.           shouldIncludeInTree ? nextFiber : parentFiber,
    
  2380.           true,
    
  2381.           traceNearestHostComponentUpdate,
    
  2382.         );
    
  2383.       }
    
  2384.       shouldResetChildren = true;
    
  2385.     } else if (!prevDidTimeout && nextDidTimeOut) {
    
  2386.       // Primary -> Fallback:
    
  2387.       // 1. Hide primary set
    
  2388.       // This is not a real unmount, so it won't get reported by React.
    
  2389.       // We need to manually walk the previous tree and record unmounts.
    
  2390.       unmountFiberChildrenRecursively(prevFiber);
    
  2391.       // 2. Mount fallback set
    
  2392.       const nextFiberChild = nextFiber.child;
    
  2393.       const nextFallbackChildSet = nextFiberChild
    
  2394.         ? nextFiberChild.sibling
    
  2395.         : null;
    
  2396.       if (nextFallbackChildSet != null) {
    
  2397.         mountFiberRecursively(
    
  2398.           nextFallbackChildSet,
    
  2399.           shouldIncludeInTree ? nextFiber : parentFiber,
    
  2400.           true,
    
  2401.           traceNearestHostComponentUpdate,
    
  2402.         );
    
  2403.         shouldResetChildren = true;
    
  2404.       }
    
  2405.     } else {
    
  2406.       // Common case: Primary -> Primary.
    
  2407.       // This is the same code path as for non-Suspense fibers.
    
  2408.       if (nextFiber.child !== prevFiber.child) {
    
  2409.         // If the first child is different, we need to traverse them.
    
  2410.         // Each next child will be either a new child (mount) or an alternate (update).
    
  2411.         let nextChild = nextFiber.child;
    
  2412.         let prevChildAtSameIndex = prevFiber.child;
    
  2413.         while (nextChild) {
    
  2414.           // We already know children will be referentially different because
    
  2415.           // they are either new mounts or alternates of previous children.
    
  2416.           // Schedule updates and mounts depending on whether alternates exist.
    
  2417.           // We don't track deletions here because they are reported separately.
    
  2418.           if (nextChild.alternate) {
    
  2419.             const prevChild = nextChild.alternate;
    
  2420.             if (
    
  2421.               updateFiberRecursively(
    
  2422.                 nextChild,
    
  2423.                 prevChild,
    
  2424.                 shouldIncludeInTree ? nextFiber : parentFiber,
    
  2425.                 traceNearestHostComponentUpdate,
    
  2426.               )
    
  2427.             ) {
    
  2428.               // If a nested tree child order changed but it can't handle its own
    
  2429.               // child order invalidation (e.g. because it's filtered out like host nodes),
    
  2430.               // propagate the need to reset child order upwards to this Fiber.
    
  2431.               shouldResetChildren = true;
    
  2432.             }
    
  2433.             // However we also keep track if the order of the children matches
    
  2434.             // the previous order. They are always different referentially, but
    
  2435.             // if the instances line up conceptually we'll want to know that.
    
  2436.             if (prevChild !== prevChildAtSameIndex) {
    
  2437.               shouldResetChildren = true;
    
  2438.             }
    
  2439.           } else {
    
  2440.             mountFiberRecursively(
    
  2441.               nextChild,
    
  2442.               shouldIncludeInTree ? nextFiber : parentFiber,
    
  2443.               false,
    
  2444.               traceNearestHostComponentUpdate,
    
  2445.             );
    
  2446.             shouldResetChildren = true;
    
  2447.           }
    
  2448.           // Try the next child.
    
  2449.           nextChild = nextChild.sibling;
    
  2450.           // Advance the pointer in the previous list so that we can
    
  2451.           // keep comparing if they line up.
    
  2452.           if (!shouldResetChildren && prevChildAtSameIndex !== null) {
    
  2453.             prevChildAtSameIndex = prevChildAtSameIndex.sibling;
    
  2454.           }
    
  2455.         }
    
  2456.         // If we have no more children, but used to, they don't line up.
    
  2457.         if (prevChildAtSameIndex !== null) {
    
  2458.           shouldResetChildren = true;
    
  2459.         }
    
  2460.       } else {
    
  2461.         if (traceUpdatesEnabled) {
    
  2462.           // If we're tracing updates and we've bailed out before reaching a host node,
    
  2463.           // we should fall back to recursively marking the nearest host descendants for highlight.
    
  2464.           if (traceNearestHostComponentUpdate) {
    
  2465.             const hostFibers = findAllCurrentHostFibers(
    
  2466.               getFiberIDThrows(nextFiber),
    
  2467.             );
    
  2468.             hostFibers.forEach(hostFiber => {
    
  2469.               traceUpdatesForNodes.add(hostFiber.stateNode);
    
  2470.             });
    
  2471.           }
    
  2472.         }
    
  2473.       }
    
  2474.     }
    
  2475. 
    
  2476.     if (shouldIncludeInTree) {
    
  2477.       const isProfilingSupported = nextFiber.hasOwnProperty('treeBaseDuration');
    
  2478.       if (isProfilingSupported) {
    
  2479.         recordProfilingDurations(nextFiber);
    
  2480.       }
    
  2481.     }
    
  2482.     if (shouldResetChildren) {
    
  2483.       // We need to crawl the subtree for closest non-filtered Fibers
    
  2484.       // so that we can display them in a flat children set.
    
  2485.       if (shouldIncludeInTree) {
    
  2486.         // Normally, search for children from the rendered child.
    
  2487.         let nextChildSet = nextFiber.child;
    
  2488.         if (nextDidTimeOut) {
    
  2489.           // Special case: timed-out Suspense renders the fallback set.
    
  2490.           const nextFiberChild = nextFiber.child;
    
  2491.           nextChildSet = nextFiberChild ? nextFiberChild.sibling : null;
    
  2492.         }
    
  2493.         if (nextChildSet != null) {
    
  2494.           recordResetChildren(nextFiber, nextChildSet);
    
  2495.         }
    
  2496.         // We've handled the child order change for this Fiber.
    
  2497.         // Since it's included, there's no need to invalidate parent child order.
    
  2498.         return false;
    
  2499.       } else {
    
  2500.         // Let the closest unfiltered parent Fiber reset its child order instead.
    
  2501.         return true;
    
  2502.       }
    
  2503.     } else {
    
  2504.       return false;
    
  2505.     }
    
  2506.   }
    
  2507. 
    
  2508.   function cleanup() {
    
  2509.     // We don't patch any methods so there is no cleanup.
    
  2510.   }
    
  2511. 
    
  2512.   function rootSupportsProfiling(root: any) {
    
  2513.     if (root.memoizedInteractions != null) {
    
  2514.       // v16 builds include this field for the scheduler/tracing API.
    
  2515.       return true;
    
  2516.     } else if (
    
  2517.       root.current != null &&
    
  2518.       root.current.hasOwnProperty('treeBaseDuration')
    
  2519.     ) {
    
  2520.       // The scheduler/tracing API was removed in v17 though
    
  2521.       // so we need to check a non-root Fiber.
    
  2522.       return true;
    
  2523.     } else {
    
  2524.       return false;
    
  2525.     }
    
  2526.   }
    
  2527. 
    
  2528.   function flushInitialOperations() {
    
  2529.     const localPendingOperationsQueue = pendingOperationsQueue;
    
  2530. 
    
  2531.     pendingOperationsQueue = null;
    
  2532. 
    
  2533.     if (
    
  2534.       localPendingOperationsQueue !== null &&
    
  2535.       localPendingOperationsQueue.length > 0
    
  2536.     ) {
    
  2537.       // We may have already queued up some operations before the frontend connected
    
  2538.       // If so, let the frontend know about them.
    
  2539.       localPendingOperationsQueue.forEach(operations => {
    
  2540.         hook.emit('operations', operations);
    
  2541.       });
    
  2542.     } else {
    
  2543.       // Before the traversals, remember to start tracking
    
  2544.       // our path in case we have selection to restore.
    
  2545.       if (trackedPath !== null) {
    
  2546.         mightBeOnTrackedPath = true;
    
  2547.       }
    
  2548.       // If we have not been profiling, then we can just walk the tree and build up its current state as-is.
    
  2549.       hook.getFiberRoots(rendererID).forEach(root => {
    
  2550.         currentRootID = getOrGenerateFiberID(root.current);
    
  2551.         setRootPseudoKey(currentRootID, root.current);
    
  2552. 
    
  2553.         // Handle multi-renderer edge-case where only some v16 renderers support profiling.
    
  2554.         if (isProfiling && rootSupportsProfiling(root)) {
    
  2555.           // If profiling is active, store commit time and duration.
    
  2556.           // The frontend may request this information after profiling has stopped.
    
  2557.           currentCommitProfilingMetadata = {
    
  2558.             changeDescriptions: recordChangeDescriptions ? new Map() : null,
    
  2559.             durations: [],
    
  2560.             commitTime: getCurrentTime() - profilingStartTime,
    
  2561.             maxActualDuration: 0,
    
  2562.             priorityLevel: null,
    
  2563.             updaters: getUpdatersList(root),
    
  2564.             effectDuration: null,
    
  2565.             passiveEffectDuration: null,
    
  2566.           };
    
  2567.         }
    
  2568. 
    
  2569.         mountFiberRecursively(root.current, null, false, false);
    
  2570.         flushPendingEvents(root);
    
  2571.         currentRootID = -1;
    
  2572.       });
    
  2573.     }
    
  2574.   }
    
  2575. 
    
  2576.   function getUpdatersList(root: any): Array<SerializedElement> | null {
    
  2577.     return root.memoizedUpdaters != null
    
  2578.       ? Array.from(root.memoizedUpdaters)
    
  2579.           .filter(fiber => getFiberIDUnsafe(fiber) !== null)
    
  2580.           .map(fiberToSerializedElement)
    
  2581.       : null;
    
  2582.   }
    
  2583. 
    
  2584.   function handleCommitFiberUnmount(fiber: any) {
    
  2585.     // If the untrackFiberSet already has the unmounted Fiber, this means we've already
    
  2586.     // recordedUnmount, so we don't need to do it again. If we don't do this, we might
    
  2587.     // end up double-deleting Fibers in some cases (like Legacy Suspense).
    
  2588.     if (!untrackFibersSet.has(fiber)) {
    
  2589.       // This is not recursive.
    
  2590.       // We can't traverse fibers after unmounting so instead
    
  2591.       // we rely on React telling us about each unmount.
    
  2592.       recordUnmount(fiber, false);
    
  2593.     }
    
  2594.   }
    
  2595. 
    
  2596.   function handlePostCommitFiberRoot(root: any) {
    
  2597.     if (isProfiling && rootSupportsProfiling(root)) {
    
  2598.       if (currentCommitProfilingMetadata !== null) {
    
  2599.         const {effectDuration, passiveEffectDuration} =
    
  2600.           getEffectDurations(root);
    
  2601.         // $FlowFixMe[incompatible-use] found when upgrading Flow
    
  2602.         currentCommitProfilingMetadata.effectDuration = effectDuration;
    
  2603.         // $FlowFixMe[incompatible-use] found when upgrading Flow
    
  2604.         currentCommitProfilingMetadata.passiveEffectDuration =
    
  2605.           passiveEffectDuration;
    
  2606.       }
    
  2607.     }
    
  2608.   }
    
  2609. 
    
  2610.   function handleCommitFiberRoot(root: any, priorityLevel: void | number) {
    
  2611.     const current = root.current;
    
  2612.     const alternate = current.alternate;
    
  2613. 
    
  2614.     // Flush any pending Fibers that we are untracking before processing the new commit.
    
  2615.     // If we don't do this, we might end up double-deleting Fibers in some cases (like Legacy Suspense).
    
  2616.     untrackFibers();
    
  2617. 
    
  2618.     currentRootID = getOrGenerateFiberID(current);
    
  2619. 
    
  2620.     // Before the traversals, remember to start tracking
    
  2621.     // our path in case we have selection to restore.
    
  2622.     if (trackedPath !== null) {
    
  2623.       mightBeOnTrackedPath = true;
    
  2624.     }
    
  2625. 
    
  2626.     if (traceUpdatesEnabled) {
    
  2627.       traceUpdatesForNodes.clear();
    
  2628.     }
    
  2629. 
    
  2630.     // Handle multi-renderer edge-case where only some v16 renderers support profiling.
    
  2631.     const isProfilingSupported = rootSupportsProfiling(root);
    
  2632. 
    
  2633.     if (isProfiling && isProfilingSupported) {
    
  2634.       // If profiling is active, store commit time and duration.
    
  2635.       // The frontend may request this information after profiling has stopped.
    
  2636.       currentCommitProfilingMetadata = {
    
  2637.         changeDescriptions: recordChangeDescriptions ? new Map() : null,
    
  2638.         durations: [],
    
  2639.         commitTime: getCurrentTime() - profilingStartTime,
    
  2640.         maxActualDuration: 0,
    
  2641.         priorityLevel:
    
  2642.           priorityLevel == null ? null : formatPriorityLevel(priorityLevel),
    
  2643. 
    
  2644.         updaters: getUpdatersList(root),
    
  2645. 
    
  2646.         // Initialize to null; if new enough React version is running,
    
  2647.         // these values will be read during separate handlePostCommitFiberRoot() call.
    
  2648.         effectDuration: null,
    
  2649.         passiveEffectDuration: null,
    
  2650.       };
    
  2651.     }
    
  2652. 
    
  2653.     if (alternate) {
    
  2654.       // TODO: relying on this seems a bit fishy.
    
  2655.       const wasMounted =
    
  2656.         alternate.memoizedState != null &&
    
  2657.         alternate.memoizedState.element != null &&
    
  2658.         // A dehydrated root is not considered mounted
    
  2659.         alternate.memoizedState.isDehydrated !== true;
    
  2660.       const isMounted =
    
  2661.         current.memoizedState != null &&
    
  2662.         current.memoizedState.element != null &&
    
  2663.         // A dehydrated root is not considered mounted
    
  2664.         current.memoizedState.isDehydrated !== true;
    
  2665.       if (!wasMounted && isMounted) {
    
  2666.         // Mount a new root.
    
  2667.         setRootPseudoKey(currentRootID, current);
    
  2668.         mountFiberRecursively(current, null, false, false);
    
  2669.       } else if (wasMounted && isMounted) {
    
  2670.         // Update an existing root.
    
  2671.         updateFiberRecursively(current, alternate, null, false);
    
  2672.       } else if (wasMounted && !isMounted) {
    
  2673.         // Unmount an existing root.
    
  2674.         removeRootPseudoKey(currentRootID);
    
  2675.         recordUnmount(current, false);
    
  2676.       }
    
  2677.     } else {
    
  2678.       // Mount a new root.
    
  2679.       setRootPseudoKey(currentRootID, current);
    
  2680.       mountFiberRecursively(current, null, false, false);
    
  2681.     }
    
  2682. 
    
  2683.     if (isProfiling && isProfilingSupported) {
    
  2684.       if (!shouldBailoutWithPendingOperations()) {
    
  2685.         const commitProfilingMetadata =
    
  2686.           ((rootToCommitProfilingMetadataMap: any): CommitProfilingMetadataMap).get(
    
  2687.             currentRootID,
    
  2688.           );
    
  2689. 
    
  2690.         if (commitProfilingMetadata != null) {
    
  2691.           commitProfilingMetadata.push(
    
  2692.             ((currentCommitProfilingMetadata: any): CommitProfilingData),
    
  2693.           );
    
  2694.         } else {
    
  2695.           ((rootToCommitProfilingMetadataMap: any): CommitProfilingMetadataMap).set(
    
  2696.             currentRootID,
    
  2697.             [((currentCommitProfilingMetadata: any): CommitProfilingData)],
    
  2698.           );
    
  2699.         }
    
  2700.       }
    
  2701.     }
    
  2702. 
    
  2703.     // We're done here.
    
  2704.     flushPendingEvents(root);
    
  2705. 
    
  2706.     if (traceUpdatesEnabled) {
    
  2707.       hook.emit('traceUpdates', traceUpdatesForNodes);
    
  2708.     }
    
  2709. 
    
  2710.     currentRootID = -1;
    
  2711.   }
    
  2712. 
    
  2713.   function findAllCurrentHostFibers(id: number): $ReadOnlyArray<Fiber> {
    
  2714.     const fibers = [];
    
  2715.     const fiber = findCurrentFiberUsingSlowPathById(id);
    
  2716.     if (!fiber) {
    
  2717.       return fibers;
    
  2718.     }
    
  2719. 
    
  2720.     // Next we'll drill down this component to find all HostComponent/Text.
    
  2721.     let node: Fiber = fiber;
    
  2722.     while (true) {
    
  2723.       if (node.tag === HostComponent || node.tag === HostText) {
    
  2724.         fibers.push(node);
    
  2725.       } else if (node.child) {
    
  2726.         node.child.return = node;
    
  2727.         node = node.child;
    
  2728.         continue;
    
  2729.       }
    
  2730.       if (node === fiber) {
    
  2731.         return fibers;
    
  2732.       }
    
  2733.       while (!node.sibling) {
    
  2734.         if (!node.return || node.return === fiber) {
    
  2735.           return fibers;
    
  2736.         }
    
  2737.         node = node.return;
    
  2738.       }
    
  2739.       node.sibling.return = node.return;
    
  2740.       node = node.sibling;
    
  2741.     }
    
  2742.     // Flow needs the return here, but ESLint complains about it.
    
  2743.     // eslint-disable-next-line no-unreachable
    
  2744.     return fibers;
    
  2745.   }
    
  2746. 
    
  2747.   function findNativeNodesForFiberID(id: number) {
    
  2748.     try {
    
  2749.       const fiber = findCurrentFiberUsingSlowPathById(id);
    
  2750.       if (fiber === null) {
    
  2751.         return null;
    
  2752.       }
    
  2753. 
    
  2754.       const hostFibers = findAllCurrentHostFibers(id);
    
  2755.       return hostFibers.map(hostFiber => hostFiber.stateNode).filter(Boolean);
    
  2756.     } catch (err) {
    
  2757.       // The fiber might have unmounted by now.
    
  2758.       return null;
    
  2759.     }
    
  2760.   }
    
  2761. 
    
  2762.   function getDisplayNameForFiberID(id: number): null | string {
    
  2763.     const fiber = idToArbitraryFiberMap.get(id);
    
  2764.     return fiber != null ? getDisplayNameForFiber(fiber) : null;
    
  2765.   }
    
  2766. 
    
  2767.   function getFiberForNative(hostInstance: NativeType) {
    
  2768.     return renderer.findFiberByHostInstance(hostInstance);
    
  2769.   }
    
  2770. 
    
  2771.   function getFiberIDForNative(
    
  2772.     hostInstance: NativeType,
    
  2773.     findNearestUnfilteredAncestor: boolean = false,
    
  2774.   ) {
    
  2775.     let fiber = renderer.findFiberByHostInstance(hostInstance);
    
  2776.     if (fiber != null) {
    
  2777.       if (findNearestUnfilteredAncestor) {
    
  2778.         while (fiber !== null && shouldFilterFiber(fiber)) {
    
  2779.           fiber = fiber.return;
    
  2780.         }
    
  2781.       }
    
  2782.       return getFiberIDThrows(((fiber: any): Fiber));
    
  2783.     }
    
  2784.     return null;
    
  2785.   }
    
  2786. 
    
  2787.   // This function is copied from React and should be kept in sync:
    
  2788.   // https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberTreeReflection.js
    
  2789.   function assertIsMounted(fiber: Fiber) {
    
  2790.     if (getNearestMountedFiber(fiber) !== fiber) {
    
  2791.       throw new Error('Unable to find node on an unmounted component.');
    
  2792.     }
    
  2793.   }
    
  2794. 
    
  2795.   // This function is copied from React and should be kept in sync:
    
  2796.   // https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberTreeReflection.js
    
  2797.   function getNearestMountedFiber(fiber: Fiber): null | Fiber {
    
  2798.     let node = fiber;
    
  2799.     let nearestMounted: null | Fiber = fiber;
    
  2800.     if (!fiber.alternate) {
    
  2801.       // If there is no alternate, this might be a new tree that isn't inserted
    
  2802.       // yet. If it is, then it will have a pending insertion effect on it.
    
  2803.       let nextNode: Fiber = node;
    
  2804.       do {
    
  2805.         node = nextNode;
    
  2806.         // TODO: This function, and these flags, are a leaked implementation
    
  2807.         // detail. Once we start releasing DevTools in lockstep with React, we
    
  2808.         // should import a function from the reconciler instead.
    
  2809.         const Placement = 0b000000000000000000000000010;
    
  2810.         const Hydrating = 0b000000000000001000000000000;
    
  2811.         if ((node.flags & (Placement | Hydrating)) !== 0) {
    
  2812.           // This is an insertion or in-progress hydration. The nearest possible
    
  2813.           // mounted fiber is the parent but we need to continue to figure out
    
  2814.           // if that one is still mounted.
    
  2815.           nearestMounted = node.return;
    
  2816.         }
    
  2817.         // $FlowFixMe[incompatible-type] we bail out when we get a null
    
  2818.         nextNode = node.return;
    
  2819.       } while (nextNode);
    
  2820.     } else {
    
  2821.       while (node.return) {
    
  2822.         node = node.return;
    
  2823.       }
    
  2824.     }
    
  2825.     if (node.tag === HostRoot) {
    
  2826.       // TODO: Check if this was a nested HostRoot when used with
    
  2827.       // renderContainerIntoSubtree.
    
  2828.       return nearestMounted;
    
  2829.     }
    
  2830.     // If we didn't hit the root, that means that we're in an disconnected tree
    
  2831.     // that has been unmounted.
    
  2832.     return null;
    
  2833.   }
    
  2834. 
    
  2835.   // This function is copied from React and should be kept in sync:
    
  2836.   // https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberTreeReflection.js
    
  2837.   // It would be nice if we updated React to inject this function directly (vs just indirectly via findDOMNode).
    
  2838.   // BEGIN copied code
    
  2839.   function findCurrentFiberUsingSlowPathById(id: number): Fiber | null {
    
  2840.     const fiber = idToArbitraryFiberMap.get(id);
    
  2841.     if (fiber == null) {
    
  2842.       console.warn(`Could not find Fiber with id "${id}"`);
    
  2843.       return null;
    
  2844.     }
    
  2845. 
    
  2846.     const alternate = fiber.alternate;
    
  2847.     if (!alternate) {
    
  2848.       // If there is no alternate, then we only need to check if it is mounted.
    
  2849.       const nearestMounted = getNearestMountedFiber(fiber);
    
  2850. 
    
  2851.       if (nearestMounted === null) {
    
  2852.         throw new Error('Unable to find node on an unmounted component.');
    
  2853.       }
    
  2854. 
    
  2855.       if (nearestMounted !== fiber) {
    
  2856.         return null;
    
  2857.       }
    
  2858.       return fiber;
    
  2859.     }
    
  2860.     // If we have two possible branches, we'll walk backwards up to the root
    
  2861.     // to see what path the root points to. On the way we may hit one of the
    
  2862.     // special cases and we'll deal with them.
    
  2863.     let a: Fiber = fiber;
    
  2864.     let b: Fiber = alternate;
    
  2865.     while (true) {
    
  2866.       const parentA = a.return;
    
  2867.       if (parentA === null) {
    
  2868.         // We're at the root.
    
  2869.         break;
    
  2870.       }
    
  2871.       const parentB = parentA.alternate;
    
  2872.       if (parentB === null) {
    
  2873.         // There is no alternate. This is an unusual case. Currently, it only
    
  2874.         // happens when a Suspense component is hidden. An extra fragment fiber
    
  2875.         // is inserted in between the Suspense fiber and its children. Skip
    
  2876.         // over this extra fragment fiber and proceed to the next parent.
    
  2877.         const nextParent = parentA.return;
    
  2878.         if (nextParent !== null) {
    
  2879.           a = b = nextParent;
    
  2880.           continue;
    
  2881.         }
    
  2882.         // If there's no parent, we're at the root.
    
  2883.         break;
    
  2884.       }
    
  2885. 
    
  2886.       // If both copies of the parent fiber point to the same child, we can
    
  2887.       // assume that the child is current. This happens when we bailout on low
    
  2888.       // priority: the bailed out fiber's child reuses the current child.
    
  2889.       if (parentA.child === parentB.child) {
    
  2890.         let child = parentA.child;
    
  2891.         while (child) {
    
  2892.           if (child === a) {
    
  2893.             // We've determined that A is the current branch.
    
  2894.             assertIsMounted(parentA);
    
  2895.             return fiber;
    
  2896.           }
    
  2897.           if (child === b) {
    
  2898.             // We've determined that B is the current branch.
    
  2899.             assertIsMounted(parentA);
    
  2900.             return alternate;
    
  2901.           }
    
  2902.           child = child.sibling;
    
  2903.         }
    
  2904. 
    
  2905.         // We should never have an alternate for any mounting node. So the only
    
  2906.         // way this could possibly happen is if this was unmounted, if at all.
    
  2907.         throw new Error('Unable to find node on an unmounted component.');
    
  2908.       }
    
  2909. 
    
  2910.       if (a.return !== b.return) {
    
  2911.         // The return pointer of A and the return pointer of B point to different
    
  2912.         // fibers. We assume that return pointers never criss-cross, so A must
    
  2913.         // belong to the child set of A.return, and B must belong to the child
    
  2914.         // set of B.return.
    
  2915.         a = parentA;
    
  2916.         b = parentB;
    
  2917.       } else {
    
  2918.         // The return pointers point to the same fiber. We'll have to use the
    
  2919.         // default, slow path: scan the child sets of each parent alternate to see
    
  2920.         // which child belongs to which set.
    
  2921.         //
    
  2922.         // Search parent A's child set
    
  2923.         let didFindChild = false;
    
  2924.         let child = parentA.child;
    
  2925.         while (child) {
    
  2926.           if (child === a) {
    
  2927.             didFindChild = true;
    
  2928.             a = parentA;
    
  2929.             b = parentB;
    
  2930.             break;
    
  2931.           }
    
  2932.           if (child === b) {
    
  2933.             didFindChild = true;
    
  2934.             b = parentA;
    
  2935.             a = parentB;
    
  2936.             break;
    
  2937.           }
    
  2938.           child = child.sibling;
    
  2939.         }
    
  2940.         if (!didFindChild) {
    
  2941.           // Search parent B's child set
    
  2942.           child = parentB.child;
    
  2943.           while (child) {
    
  2944.             if (child === a) {
    
  2945.               didFindChild = true;
    
  2946.               a = parentB;
    
  2947.               b = parentA;
    
  2948.               break;
    
  2949.             }
    
  2950.             if (child === b) {
    
  2951.               didFindChild = true;
    
  2952.               b = parentB;
    
  2953.               a = parentA;
    
  2954.               break;
    
  2955.             }
    
  2956.             child = child.sibling;
    
  2957.           }
    
  2958. 
    
  2959.           if (!didFindChild) {
    
  2960.             throw new Error(
    
  2961.               'Child was not found in either parent set. This indicates a bug ' +
    
  2962.                 'in React related to the return pointer. Please file an issue.',
    
  2963.             );
    
  2964.           }
    
  2965.         }
    
  2966.       }
    
  2967. 
    
  2968.       if (a.alternate !== b) {
    
  2969.         throw new Error(
    
  2970.           "Return fibers should always be each others' alternates. " +
    
  2971.             'This error is likely caused by a bug in React. Please file an issue.',
    
  2972.         );
    
  2973.       }
    
  2974.     }
    
  2975. 
    
  2976.     // If the root is not a host container, we're in a disconnected tree. I.e.
    
  2977.     // unmounted.
    
  2978.     if (a.tag !== HostRoot) {
    
  2979.       throw new Error('Unable to find node on an unmounted component.');
    
  2980.     }
    
  2981. 
    
  2982.     if (a.stateNode.current === a) {
    
  2983.       // We've determined that A is the current branch.
    
  2984.       return fiber;
    
  2985.     }
    
  2986.     // Otherwise B has to be current branch.
    
  2987.     return alternate;
    
  2988.   }
    
  2989. 
    
  2990.   // END copied code
    
  2991. 
    
  2992.   function prepareViewAttributeSource(
    
  2993.     id: number,
    
  2994.     path: Array<string | number>,
    
  2995.   ): void {
    
  2996.     if (isMostRecentlyInspectedElement(id)) {
    
  2997.       window.$attribute = getInObject(
    
  2998.         ((mostRecentlyInspectedElement: any): InspectedElement),
    
  2999.         path,
    
  3000.       );
    
  3001.     }
    
  3002.   }
    
  3003. 
    
  3004.   function prepareViewElementSource(id: number): void {
    
  3005.     const fiber = idToArbitraryFiberMap.get(id);
    
  3006.     if (fiber == null) {
    
  3007.       console.warn(`Could not find Fiber with id "${id}"`);
    
  3008.       return;
    
  3009.     }
    
  3010. 
    
  3011.     const {elementType, tag, type} = fiber;
    
  3012. 
    
  3013.     switch (tag) {
    
  3014.       case ClassComponent:
    
  3015.       case IncompleteClassComponent:
    
  3016.       case IndeterminateComponent:
    
  3017.       case FunctionComponent:
    
  3018.         global.$type = type;
    
  3019.         break;
    
  3020.       case ForwardRef:
    
  3021.         global.$type = type.render;
    
  3022.         break;
    
  3023.       case MemoComponent:
    
  3024.       case SimpleMemoComponent:
    
  3025.         global.$type =
    
  3026.           elementType != null && elementType.type != null
    
  3027.             ? elementType.type
    
  3028.             : type;
    
  3029.         break;
    
  3030.       default:
    
  3031.         global.$type = null;
    
  3032.         break;
    
  3033.     }
    
  3034.   }
    
  3035. 
    
  3036.   function fiberToSerializedElement(fiber: Fiber): SerializedElement {
    
  3037.     return {
    
  3038.       displayName: getDisplayNameForFiber(fiber) || 'Anonymous',
    
  3039.       id: getFiberIDThrows(fiber),
    
  3040.       key: fiber.key,
    
  3041.       type: getElementTypeForFiber(fiber),
    
  3042.     };
    
  3043.   }
    
  3044. 
    
  3045.   function getOwnersList(id: number): Array<SerializedElement> | null {
    
  3046.     const fiber = findCurrentFiberUsingSlowPathById(id);
    
  3047.     if (fiber == null) {
    
  3048.       return null;
    
  3049.     }
    
  3050. 
    
  3051.     const {_debugOwner} = fiber;
    
  3052. 
    
  3053.     const owners: Array<SerializedElement> = [fiberToSerializedElement(fiber)];
    
  3054. 
    
  3055.     if (_debugOwner) {
    
  3056.       let owner: null | Fiber = _debugOwner;
    
  3057.       while (owner !== null) {
    
  3058.         owners.unshift(fiberToSerializedElement(owner));
    
  3059.         owner = owner._debugOwner || null;
    
  3060.       }
    
  3061.     }
    
  3062. 
    
  3063.     return owners;
    
  3064.   }
    
  3065. 
    
  3066.   // Fast path props lookup for React Native style editor.
    
  3067.   // Could use inspectElementRaw() but that would require shallow rendering hooks components,
    
  3068.   // and could also mess with memoization.
    
  3069.   function getInstanceAndStyle(id: number): InstanceAndStyle {
    
  3070.     let instance = null;
    
  3071.     let style = null;
    
  3072. 
    
  3073.     const fiber = findCurrentFiberUsingSlowPathById(id);
    
  3074.     if (fiber !== null) {
    
  3075.       instance = fiber.stateNode;
    
  3076. 
    
  3077.       if (fiber.memoizedProps !== null) {
    
  3078.         style = fiber.memoizedProps.style;
    
  3079.       }
    
  3080.     }
    
  3081. 
    
  3082.     return {instance, style};
    
  3083.   }
    
  3084. 
    
  3085.   function isErrorBoundary(fiber: Fiber): boolean {
    
  3086.     const {tag, type} = fiber;
    
  3087. 
    
  3088.     switch (tag) {
    
  3089.       case ClassComponent:
    
  3090.       case IncompleteClassComponent:
    
  3091.         const instance = fiber.stateNode;
    
  3092.         return (
    
  3093.           typeof type.getDerivedStateFromError === 'function' ||
    
  3094.           (instance !== null &&
    
  3095.             typeof instance.componentDidCatch === 'function')
    
  3096.         );
    
  3097.       default:
    
  3098.         return false;
    
  3099.     }
    
  3100.   }
    
  3101. 
    
  3102.   function getNearestErrorBoundaryID(fiber: Fiber): number | null {
    
  3103.     let parent = fiber.return;
    
  3104.     while (parent !== null) {
    
  3105.       if (isErrorBoundary(parent)) {
    
  3106.         return getFiberIDUnsafe(parent);
    
  3107.       }
    
  3108.       parent = parent.return;
    
  3109.     }
    
  3110.     return null;
    
  3111.   }
    
  3112. 
    
  3113.   function inspectElementRaw(id: number): InspectedElement | null {
    
  3114.     const fiber = findCurrentFiberUsingSlowPathById(id);
    
  3115.     if (fiber == null) {
    
  3116.       return null;
    
  3117.     }
    
  3118. 
    
  3119.     const {
    
  3120.       _debugOwner,
    
  3121.       _debugSource,
    
  3122.       stateNode,
    
  3123.       key,
    
  3124.       memoizedProps,
    
  3125.       memoizedState,
    
  3126.       dependencies,
    
  3127.       tag,
    
  3128.       type,
    
  3129.     } = fiber;
    
  3130. 
    
  3131.     const elementType = getElementTypeForFiber(fiber);
    
  3132. 
    
  3133.     const usesHooks =
    
  3134.       (tag === FunctionComponent ||
    
  3135.         tag === SimpleMemoComponent ||
    
  3136.         tag === ForwardRef) &&
    
  3137.       (!!memoizedState || !!dependencies);
    
  3138. 
    
  3139.     // TODO Show custom UI for Cache like we do for Suspense
    
  3140.     // For now, just hide state data entirely since it's not meant to be inspected.
    
  3141.     const showState = !usesHooks && tag !== CacheComponent;
    
  3142. 
    
  3143.     const typeSymbol = getTypeSymbol(type);
    
  3144. 
    
  3145.     let canViewSource = false;
    
  3146.     let context = null;
    
  3147.     if (
    
  3148.       tag === ClassComponent ||
    
  3149.       tag === FunctionComponent ||
    
  3150.       tag === IncompleteClassComponent ||
    
  3151.       tag === IndeterminateComponent ||
    
  3152.       tag === MemoComponent ||
    
  3153.       tag === ForwardRef ||
    
  3154.       tag === SimpleMemoComponent
    
  3155.     ) {
    
  3156.       canViewSource = true;
    
  3157.       if (stateNode && stateNode.context != null) {
    
  3158.         // Don't show an empty context object for class components that don't use the context API.
    
  3159.         const shouldHideContext =
    
  3160.           elementType === ElementTypeClass &&
    
  3161.           !(type.contextTypes || type.contextType);
    
  3162. 
    
  3163.         if (!shouldHideContext) {
    
  3164.           context = stateNode.context;
    
  3165.         }
    
  3166.       }
    
  3167.     } else if (
    
  3168.       typeSymbol === CONTEXT_NUMBER ||
    
  3169.       typeSymbol === CONTEXT_SYMBOL_STRING
    
  3170.     ) {
    
  3171.       // 16.3-16.5 read from "type" because the Consumer is the actual context object.
    
  3172.       // 16.6+ should read from "type._context" because Consumer can be different (in DEV).
    
  3173.       // NOTE Keep in sync with getDisplayNameForFiber()
    
  3174.       const consumerResolvedContext = type._context || type;
    
  3175. 
    
  3176.       // Global context value.
    
  3177.       context = consumerResolvedContext._currentValue || null;
    
  3178. 
    
  3179.       // Look for overridden value.
    
  3180.       let current = ((fiber: any): Fiber).return;
    
  3181.       while (current !== null) {
    
  3182.         const currentType = current.type;
    
  3183.         const currentTypeSymbol = getTypeSymbol(currentType);
    
  3184.         if (
    
  3185.           currentTypeSymbol === PROVIDER_NUMBER ||
    
  3186.           currentTypeSymbol === PROVIDER_SYMBOL_STRING
    
  3187.         ) {
    
  3188.           // 16.3.0 exposed the context object as "context"
    
  3189.           // PR #12501 changed it to "_context" for 16.3.1+
    
  3190.           // NOTE Keep in sync with getDisplayNameForFiber()
    
  3191.           const providerResolvedContext =
    
  3192.             currentType._context || currentType.context;
    
  3193.           if (providerResolvedContext === consumerResolvedContext) {
    
  3194.             context = current.memoizedProps.value;
    
  3195.             break;
    
  3196.           }
    
  3197.         }
    
  3198. 
    
  3199.         current = current.return;
    
  3200.       }
    
  3201.     }
    
  3202. 
    
  3203.     let hasLegacyContext = false;
    
  3204.     if (context !== null) {
    
  3205.       hasLegacyContext = !!type.contextTypes;
    
  3206. 
    
  3207.       // To simplify hydration and display logic for context, wrap in a value object.
    
  3208.       // Otherwise simple values (e.g. strings, booleans) become harder to handle.
    
  3209.       context = {value: context};
    
  3210.     }
    
  3211. 
    
  3212.     let owners = null;
    
  3213.     if (_debugOwner) {
    
  3214.       owners = ([]: Array<SerializedElement>);
    
  3215.       let owner: null | Fiber = _debugOwner;
    
  3216.       while (owner !== null) {
    
  3217.         owners.push(fiberToSerializedElement(owner));
    
  3218.         owner = owner._debugOwner || null;
    
  3219.       }
    
  3220.     }
    
  3221. 
    
  3222.     const isTimedOutSuspense =
    
  3223.       tag === SuspenseComponent && memoizedState !== null;
    
  3224. 
    
  3225.     let hooks = null;
    
  3226.     if (usesHooks) {
    
  3227.       const originalConsoleMethods: {[string]: $FlowFixMe} = {};
    
  3228. 
    
  3229.       // Temporarily disable all console logging before re-running the hook.
    
  3230.       for (const method in console) {
    
  3231.         try {
    
  3232.           originalConsoleMethods[method] = console[method];
    
  3233.           // $FlowFixMe[prop-missing]
    
  3234.           console[method] = () => {};
    
  3235.         } catch (error) {}
    
  3236.       }
    
  3237. 
    
  3238.       try {
    
  3239.         hooks = inspectHooksOfFiber(
    
  3240.           fiber,
    
  3241.           (renderer.currentDispatcherRef: any),
    
  3242.           true, // Include source location info for hooks
    
  3243.         );
    
  3244.       } finally {
    
  3245.         // Restore original console functionality.
    
  3246.         for (const method in originalConsoleMethods) {
    
  3247.           try {
    
  3248.             // $FlowFixMe[prop-missing]
    
  3249.             console[method] = originalConsoleMethods[method];
    
  3250.           } catch (error) {}
    
  3251.         }
    
  3252.       }
    
  3253.     }
    
  3254. 
    
  3255.     let rootType = null;
    
  3256.     let current = fiber;
    
  3257.     while (current.return !== null) {
    
  3258.       current = current.return;
    
  3259.     }
    
  3260.     const fiberRoot = current.stateNode;
    
  3261.     if (fiberRoot != null && fiberRoot._debugRootType !== null) {
    
  3262.       rootType = fiberRoot._debugRootType;
    
  3263.     }
    
  3264. 
    
  3265.     const errors = fiberIDToErrorsMap.get(id) || new Map();
    
  3266.     const warnings = fiberIDToWarningsMap.get(id) || new Map();
    
  3267. 
    
  3268.     let isErrored = false;
    
  3269.     let targetErrorBoundaryID;
    
  3270.     if (isErrorBoundary(fiber)) {
    
  3271.       // if the current inspected element is an error boundary,
    
  3272.       // either that we want to use it to toggle off error state
    
  3273.       // or that we allow to force error state on it if it's within another
    
  3274.       // error boundary
    
  3275.       //
    
  3276.       // TODO: This flag is a leaked implementation detail. Once we start
    
  3277.       // releasing DevTools in lockstep with React, we should import a function
    
  3278.       // from the reconciler instead.
    
  3279.       const DidCapture = 0b000000000000000000010000000;
    
  3280.       isErrored =
    
  3281.         (fiber.flags & DidCapture) !== 0 ||
    
  3282.         forceErrorForFiberIDs.get(id) === true;
    
  3283.       targetErrorBoundaryID = isErrored ? id : getNearestErrorBoundaryID(fiber);
    
  3284.     } else {
    
  3285.       targetErrorBoundaryID = getNearestErrorBoundaryID(fiber);
    
  3286.     }
    
  3287. 
    
  3288.     const plugins: Plugins = {
    
  3289.       stylex: null,
    
  3290.     };
    
  3291. 
    
  3292.     if (enableStyleXFeatures) {
    
  3293.       if (memoizedProps != null && memoizedProps.hasOwnProperty('xstyle')) {
    
  3294.         plugins.stylex = getStyleXData(memoizedProps.xstyle);
    
  3295.       }
    
  3296.     }
    
  3297. 
    
  3298.     return {
    
  3299.       id,
    
  3300. 
    
  3301.       // Does the current renderer support editable hooks and function props?
    
  3302.       canEditHooks: typeof overrideHookState === 'function',
    
  3303.       canEditFunctionProps: typeof overrideProps === 'function',
    
  3304. 
    
  3305.       // Does the current renderer support advanced editing interface?
    
  3306.       canEditHooksAndDeletePaths:
    
  3307.         typeof overrideHookStateDeletePath === 'function',
    
  3308.       canEditHooksAndRenamePaths:
    
  3309.         typeof overrideHookStateRenamePath === 'function',
    
  3310.       canEditFunctionPropsDeletePaths:
    
  3311.         typeof overridePropsDeletePath === 'function',
    
  3312.       canEditFunctionPropsRenamePaths:
    
  3313.         typeof overridePropsRenamePath === 'function',
    
  3314. 
    
  3315.       canToggleError: supportsTogglingError && targetErrorBoundaryID != null,
    
  3316.       // Is this error boundary in error state.
    
  3317.       isErrored,
    
  3318.       targetErrorBoundaryID,
    
  3319. 
    
  3320.       canToggleSuspense:
    
  3321.         supportsTogglingSuspense &&
    
  3322.         // If it's showing the real content, we can always flip fallback.
    
  3323.         (!isTimedOutSuspense ||
    
  3324.           // If it's showing fallback because we previously forced it to,
    
  3325.           // allow toggling it back to remove the fallback override.
    
  3326.           forceFallbackForSuspenseIDs.has(id)),
    
  3327. 
    
  3328.       // Can view component source location.
    
  3329.       canViewSource,
    
  3330. 
    
  3331.       // Does the component have legacy context attached to it.
    
  3332.       hasLegacyContext,
    
  3333. 
    
  3334.       key: key != null ? key : null,
    
  3335. 
    
  3336.       displayName: getDisplayNameForFiber(fiber),
    
  3337.       type: elementType,
    
  3338. 
    
  3339.       // Inspectable properties.
    
  3340.       // TODO Review sanitization approach for the below inspectable values.
    
  3341.       context,
    
  3342.       hooks,
    
  3343.       props: memoizedProps,
    
  3344.       state: showState ? memoizedState : null,
    
  3345.       errors: Array.from(errors.entries()),
    
  3346.       warnings: Array.from(warnings.entries()),
    
  3347. 
    
  3348.       // List of owners
    
  3349.       owners,
    
  3350. 
    
  3351.       // Location of component in source code.
    
  3352.       source: _debugSource || null,
    
  3353. 
    
  3354.       rootType,
    
  3355.       rendererPackageName: renderer.rendererPackageName,
    
  3356.       rendererVersion: renderer.version,
    
  3357. 
    
  3358.       plugins,
    
  3359.     };
    
  3360.   }
    
  3361. 
    
  3362.   let mostRecentlyInspectedElement: InspectedElement | null = null;
    
  3363.   let hasElementUpdatedSinceLastInspected: boolean = false;
    
  3364.   let currentlyInspectedPaths: Object = {};
    
  3365. 
    
  3366.   function isMostRecentlyInspectedElement(id: number): boolean {
    
  3367.     return (
    
  3368.       mostRecentlyInspectedElement !== null &&
    
  3369.       mostRecentlyInspectedElement.id === id
    
  3370.     );
    
  3371.   }
    
  3372. 
    
  3373.   function isMostRecentlyInspectedElementCurrent(id: number): boolean {
    
  3374.     return (
    
  3375.       isMostRecentlyInspectedElement(id) && !hasElementUpdatedSinceLastInspected
    
  3376.     );
    
  3377.   }
    
  3378. 
    
  3379.   // Track the intersection of currently inspected paths,
    
  3380.   // so that we can send their data along if the element is re-rendered.
    
  3381.   function mergeInspectedPaths(path: Array<string | number>) {
    
  3382.     let current = currentlyInspectedPaths;
    
  3383.     path.forEach(key => {
    
  3384.       if (!current[key]) {
    
  3385.         current[key] = {};
    
  3386.       }
    
  3387.       current = current[key];
    
  3388.     });
    
  3389.   }
    
  3390. 
    
  3391.   function createIsPathAllowed(
    
  3392.     key: string | null,
    
  3393.     secondaryCategory: 'hooks' | null,
    
  3394.   ) {
    
  3395.     // This function helps prevent previously-inspected paths from being dehydrated in updates.
    
  3396.     // This is important to avoid a bad user experience where expanded toggles collapse on update.
    
  3397.     return function isPathAllowed(path: Array<string | number>): boolean {
    
  3398.       switch (secondaryCategory) {
    
  3399.         case 'hooks':
    
  3400.           if (path.length === 1) {
    
  3401.             // Never dehydrate the "hooks" object at the top levels.
    
  3402.             return true;
    
  3403.           }
    
  3404. 
    
  3405.           if (
    
  3406.             path[path.length - 2] === 'hookSource' &&
    
  3407.             path[path.length - 1] === 'fileName'
    
  3408.           ) {
    
  3409.             // It's important to preserve the full file name (URL) for hook sources
    
  3410.             // in case the user has enabled the named hooks feature.
    
  3411.             // Otherwise the frontend may end up with a partial URL which it can't load.
    
  3412.             return true;
    
  3413.           }
    
  3414. 
    
  3415.           if (
    
  3416.             path[path.length - 1] === 'subHooks' ||
    
  3417.             path[path.length - 2] === 'subHooks'
    
  3418.           ) {
    
  3419.             // Dehydrating the 'subHooks' property makes the HooksTree UI a lot more complicated,
    
  3420.             // so it's easiest for now if we just don't break on this boundary.
    
  3421.             // We can always dehydrate a level deeper (in the value object).
    
  3422.             return true;
    
  3423.           }
    
  3424.           break;
    
  3425.         default:
    
  3426.           break;
    
  3427.       }
    
  3428. 
    
  3429.       let current =
    
  3430.         key === null ? currentlyInspectedPaths : currentlyInspectedPaths[key];
    
  3431.       if (!current) {
    
  3432.         return false;
    
  3433.       }
    
  3434.       for (let i = 0; i < path.length; i++) {
    
  3435.         current = current[path[i]];
    
  3436.         if (!current) {
    
  3437.           return false;
    
  3438.         }
    
  3439.       }
    
  3440.       return true;
    
  3441.     };
    
  3442.   }
    
  3443. 
    
  3444.   function updateSelectedElement(inspectedElement: InspectedElement): void {
    
  3445.     const {hooks, id, props} = inspectedElement;
    
  3446. 
    
  3447.     const fiber = idToArbitraryFiberMap.get(id);
    
  3448.     if (fiber == null) {
    
  3449.       console.warn(`Could not find Fiber with id "${id}"`);
    
  3450.       return;
    
  3451.     }
    
  3452. 
    
  3453.     const {elementType, stateNode, tag, type} = fiber;
    
  3454. 
    
  3455.     switch (tag) {
    
  3456.       case ClassComponent:
    
  3457.       case IncompleteClassComponent:
    
  3458.       case IndeterminateComponent:
    
  3459.         global.$r = stateNode;
    
  3460.         break;
    
  3461.       case FunctionComponent:
    
  3462.         global.$r = {
    
  3463.           hooks,
    
  3464.           props,
    
  3465.           type,
    
  3466.         };
    
  3467.         break;
    
  3468.       case ForwardRef:
    
  3469.         global.$r = {
    
  3470.           hooks,
    
  3471.           props,
    
  3472.           type: type.render,
    
  3473.         };
    
  3474.         break;
    
  3475.       case MemoComponent:
    
  3476.       case SimpleMemoComponent:
    
  3477.         global.$r = {
    
  3478.           hooks,
    
  3479.           props,
    
  3480.           type:
    
  3481.             elementType != null && elementType.type != null
    
  3482.               ? elementType.type
    
  3483.               : type,
    
  3484.         };
    
  3485.         break;
    
  3486.       default:
    
  3487.         global.$r = null;
    
  3488.         break;
    
  3489.     }
    
  3490.   }
    
  3491. 
    
  3492.   function storeAsGlobal(
    
  3493.     id: number,
    
  3494.     path: Array<string | number>,
    
  3495.     count: number,
    
  3496.   ): void {
    
  3497.     if (isMostRecentlyInspectedElement(id)) {
    
  3498.       const value = getInObject(
    
  3499.         ((mostRecentlyInspectedElement: any): InspectedElement),
    
  3500.         path,
    
  3501.       );
    
  3502.       const key = `$reactTemp${count}`;
    
  3503. 
    
  3504.       window[key] = value;
    
  3505. 
    
  3506.       console.log(key);
    
  3507.       console.log(value);
    
  3508.     }
    
  3509.   }
    
  3510. 
    
  3511.   function getSerializedElementValueByPath(
    
  3512.     id: number,
    
  3513.     path: Array<string | number>,
    
  3514.   ): ?string {
    
  3515.     if (isMostRecentlyInspectedElement(id)) {
    
  3516.       const valueToCopy = getInObject(
    
  3517.         ((mostRecentlyInspectedElement: any): InspectedElement),
    
  3518.         path,
    
  3519.       );
    
  3520. 
    
  3521.       return serializeToString(valueToCopy);
    
  3522.     }
    
  3523.   }
    
  3524. 
    
  3525.   function inspectElement(
    
  3526.     requestID: number,
    
  3527.     id: number,
    
  3528.     path: Array<string | number> | null,
    
  3529.     forceFullData: boolean,
    
  3530.   ): InspectedElementPayload {
    
  3531.     if (path !== null) {
    
  3532.       mergeInspectedPaths(path);
    
  3533.     }
    
  3534. 
    
  3535.     if (isMostRecentlyInspectedElement(id) && !forceFullData) {
    
  3536.       if (!hasElementUpdatedSinceLastInspected) {
    
  3537.         if (path !== null) {
    
  3538.           let secondaryCategory = null;
    
  3539.           if (path[0] === 'hooks') {
    
  3540.             secondaryCategory = 'hooks';
    
  3541.           }
    
  3542. 
    
  3543.           // If this element has not been updated since it was last inspected,
    
  3544.           // we can just return the subset of data in the newly-inspected path.
    
  3545.           return {
    
  3546.             id,
    
  3547.             responseID: requestID,
    
  3548.             type: 'hydrated-path',
    
  3549.             path,
    
  3550.             value: cleanForBridge(
    
  3551.               getInObject(
    
  3552.                 ((mostRecentlyInspectedElement: any): InspectedElement),
    
  3553.                 path,
    
  3554.               ),
    
  3555.               createIsPathAllowed(null, secondaryCategory),
    
  3556.               path,
    
  3557.             ),
    
  3558.           };
    
  3559.         } else {
    
  3560.           // If this element has not been updated since it was last inspected, we don't need to return it.
    
  3561.           // Instead we can just return the ID to indicate that it has not changed.
    
  3562.           return {
    
  3563.             id,
    
  3564.             responseID: requestID,
    
  3565.             type: 'no-change',
    
  3566.           };
    
  3567.         }
    
  3568.       }
    
  3569.     } else {
    
  3570.       currentlyInspectedPaths = {};
    
  3571.     }
    
  3572. 
    
  3573.     hasElementUpdatedSinceLastInspected = false;
    
  3574. 
    
  3575.     try {
    
  3576.       mostRecentlyInspectedElement = inspectElementRaw(id);
    
  3577.     } catch (error) {
    
  3578.       // the error name is synced with ReactDebugHooks
    
  3579.       if (error.name === 'ReactDebugToolsRenderError') {
    
  3580.         let message = 'Error rendering inspected element.';
    
  3581.         let stack;
    
  3582.         // Log error & cause for user to debug
    
  3583.         console.error(message + '\n\n', error);
    
  3584.         if (error.cause != null) {
    
  3585.           const fiber = findCurrentFiberUsingSlowPathById(id);
    
  3586.           const componentName =
    
  3587.             fiber != null ? getDisplayNameForFiber(fiber) : null;
    
  3588.           console.error(
    
  3589.             'React DevTools encountered an error while trying to inspect hooks. ' +
    
  3590.               'This is most likely caused by an error in current inspected component' +
    
  3591.               (componentName != null ? `: "${componentName}".` : '.') +
    
  3592.               '\nThe error thrown in the component is: \n\n',
    
  3593.             error.cause,
    
  3594.           );
    
  3595.           if (error.cause instanceof Error) {
    
  3596.             message = error.cause.message || message;
    
  3597.             stack = error.cause.stack;
    
  3598.           }
    
  3599.         }
    
  3600. 
    
  3601.         return {
    
  3602.           type: 'error',
    
  3603.           errorType: 'user',
    
  3604.           id,
    
  3605.           responseID: requestID,
    
  3606.           message,
    
  3607.           stack,
    
  3608.         };
    
  3609.       }
    
  3610. 
    
  3611.       // the error name is synced with ReactDebugHooks
    
  3612.       if (error.name === 'ReactDebugToolsUnsupportedHookError') {
    
  3613.         return {
    
  3614.           type: 'error',
    
  3615.           errorType: 'unknown-hook',
    
  3616.           id,
    
  3617.           responseID: requestID,
    
  3618.           message:
    
  3619.             'Unsupported hook in the react-debug-tools package: ' +
    
  3620.             error.message,
    
  3621.         };
    
  3622.       }
    
  3623. 
    
  3624.       // Log Uncaught Error
    
  3625.       console.error('Error inspecting element.\n\n', error);
    
  3626. 
    
  3627.       return {
    
  3628.         type: 'error',
    
  3629.         errorType: 'uncaught',
    
  3630.         id,
    
  3631.         responseID: requestID,
    
  3632.         message: error.message,
    
  3633.         stack: error.stack,
    
  3634.       };
    
  3635.     }
    
  3636. 
    
  3637.     if (mostRecentlyInspectedElement === null) {
    
  3638.       return {
    
  3639.         id,
    
  3640.         responseID: requestID,
    
  3641.         type: 'not-found',
    
  3642.       };
    
  3643.     }
    
  3644. 
    
  3645.     // Any time an inspected element has an update,
    
  3646.     // we should update the selected $r value as wel.
    
  3647.     // Do this before dehydration (cleanForBridge).
    
  3648.     updateSelectedElement(mostRecentlyInspectedElement);
    
  3649. 
    
  3650.     // Clone before cleaning so that we preserve the full data.
    
  3651.     // This will enable us to send patches without re-inspecting if hydrated paths are requested.
    
  3652.     // (Reducing how often we shallow-render is a better DX for function components that use hooks.)
    
  3653.     const cleanedInspectedElement = {...mostRecentlyInspectedElement};
    
  3654.     // $FlowFixMe[prop-missing] found when upgrading Flow
    
  3655.     cleanedInspectedElement.context = cleanForBridge(
    
  3656.       cleanedInspectedElement.context,
    
  3657.       createIsPathAllowed('context', null),
    
  3658.     );
    
  3659.     // $FlowFixMe[prop-missing] found when upgrading Flow
    
  3660.     cleanedInspectedElement.hooks = cleanForBridge(
    
  3661.       cleanedInspectedElement.hooks,
    
  3662.       createIsPathAllowed('hooks', 'hooks'),
    
  3663.     );
    
  3664.     // $FlowFixMe[prop-missing] found when upgrading Flow
    
  3665.     cleanedInspectedElement.props = cleanForBridge(
    
  3666.       cleanedInspectedElement.props,
    
  3667.       createIsPathAllowed('props', null),
    
  3668.     );
    
  3669.     // $FlowFixMe[prop-missing] found when upgrading Flow
    
  3670.     cleanedInspectedElement.state = cleanForBridge(
    
  3671.       cleanedInspectedElement.state,
    
  3672.       createIsPathAllowed('state', null),
    
  3673.     );
    
  3674. 
    
  3675.     return {
    
  3676.       id,
    
  3677.       responseID: requestID,
    
  3678.       type: 'full-data',
    
  3679.       // $FlowFixMe[prop-missing] found when upgrading Flow
    
  3680.       value: cleanedInspectedElement,
    
  3681.     };
    
  3682.   }
    
  3683. 
    
  3684.   function logElementToConsole(id: number) {
    
  3685.     const result = isMostRecentlyInspectedElementCurrent(id)
    
  3686.       ? mostRecentlyInspectedElement
    
  3687.       : inspectElementRaw(id);
    
  3688.     if (result === null) {
    
  3689.       console.warn(`Could not find Fiber with id "${id}"`);
    
  3690.       return;
    
  3691.     }
    
  3692. 
    
  3693.     const supportsGroup = typeof console.groupCollapsed === 'function';
    
  3694.     if (supportsGroup) {
    
  3695.       console.groupCollapsed(
    
  3696.         `[Click to expand] %c<${result.displayName || 'Component'} />`,
    
  3697.         // --dom-tag-name-color is the CSS variable Chrome styles HTML elements with in the console.
    
  3698.         'color: var(--dom-tag-name-color); font-weight: normal;',
    
  3699.       );
    
  3700.     }
    
  3701.     if (result.props !== null) {
    
  3702.       console.log('Props:', result.props);
    
  3703.     }
    
  3704.     if (result.state !== null) {
    
  3705.       console.log('State:', result.state);
    
  3706.     }
    
  3707.     if (result.hooks !== null) {
    
  3708.       console.log('Hooks:', result.hooks);
    
  3709.     }
    
  3710.     const nativeNodes = findNativeNodesForFiberID(id);
    
  3711.     if (nativeNodes !== null) {
    
  3712.       console.log('Nodes:', nativeNodes);
    
  3713.     }
    
  3714.     if (result.source !== null) {
    
  3715.       console.log('Location:', result.source);
    
  3716.     }
    
  3717.     if (window.chrome || /firefox/i.test(navigator.userAgent)) {
    
  3718.       console.log(
    
  3719.         'Right-click any value to save it as a global variable for further inspection.',
    
  3720.       );
    
  3721.     }
    
  3722.     if (supportsGroup) {
    
  3723.       console.groupEnd();
    
  3724.     }
    
  3725.   }
    
  3726. 
    
  3727.   function deletePath(
    
  3728.     type: 'context' | 'hooks' | 'props' | 'state',
    
  3729.     id: number,
    
  3730.     hookID: ?number,
    
  3731.     path: Array<string | number>,
    
  3732.   ): void {
    
  3733.     const fiber = findCurrentFiberUsingSlowPathById(id);
    
  3734.     if (fiber !== null) {
    
  3735.       const instance = fiber.stateNode;
    
  3736. 
    
  3737.       switch (type) {
    
  3738.         case 'context':
    
  3739.           // To simplify hydration and display of primitive context values (e.g. number, string)
    
  3740.           // the inspectElement() method wraps context in a {value: ...} object.
    
  3741.           // We need to remove the first part of the path (the "value") before continuing.
    
  3742.           path = path.slice(1);
    
  3743. 
    
  3744.           switch (fiber.tag) {
    
  3745.             case ClassComponent:
    
  3746.               if (path.length === 0) {
    
  3747.                 // Simple context value (noop)
    
  3748.               } else {
    
  3749.                 deletePathInObject(instance.context, path);
    
  3750.               }
    
  3751.               instance.forceUpdate();
    
  3752.               break;
    
  3753.             case FunctionComponent:
    
  3754.               // Function components using legacy context are not editable
    
  3755.               // because there's no instance on which to create a cloned, mutated context.
    
  3756.               break;
    
  3757.           }
    
  3758.           break;
    
  3759.         case 'hooks':
    
  3760.           if (typeof overrideHookStateDeletePath === 'function') {
    
  3761.             overrideHookStateDeletePath(fiber, ((hookID: any): number), path);
    
  3762.           }
    
  3763.           break;
    
  3764.         case 'props':
    
  3765.           if (instance === null) {
    
  3766.             if (typeof overridePropsDeletePath === 'function') {
    
  3767.               overridePropsDeletePath(fiber, path);
    
  3768.             }
    
  3769.           } else {
    
  3770.             fiber.pendingProps = copyWithDelete(instance.props, path);
    
  3771.             instance.forceUpdate();
    
  3772.           }
    
  3773.           break;
    
  3774.         case 'state':
    
  3775.           deletePathInObject(instance.state, path);
    
  3776.           instance.forceUpdate();
    
  3777.           break;
    
  3778.       }
    
  3779.     }
    
  3780.   }
    
  3781. 
    
  3782.   function renamePath(
    
  3783.     type: 'context' | 'hooks' | 'props' | 'state',
    
  3784.     id: number,
    
  3785.     hookID: ?number,
    
  3786.     oldPath: Array<string | number>,
    
  3787.     newPath: Array<string | number>,
    
  3788.   ): void {
    
  3789.     const fiber = findCurrentFiberUsingSlowPathById(id);
    
  3790.     if (fiber !== null) {
    
  3791.       const instance = fiber.stateNode;
    
  3792. 
    
  3793.       switch (type) {
    
  3794.         case 'context':
    
  3795.           // To simplify hydration and display of primitive context values (e.g. number, string)
    
  3796.           // the inspectElement() method wraps context in a {value: ...} object.
    
  3797.           // We need to remove the first part of the path (the "value") before continuing.
    
  3798.           oldPath = oldPath.slice(1);
    
  3799.           newPath = newPath.slice(1);
    
  3800. 
    
  3801.           switch (fiber.tag) {
    
  3802.             case ClassComponent:
    
  3803.               if (oldPath.length === 0) {
    
  3804.                 // Simple context value (noop)
    
  3805.               } else {
    
  3806.                 renamePathInObject(instance.context, oldPath, newPath);
    
  3807.               }
    
  3808.               instance.forceUpdate();
    
  3809.               break;
    
  3810.             case FunctionComponent:
    
  3811.               // Function components using legacy context are not editable
    
  3812.               // because there's no instance on which to create a cloned, mutated context.
    
  3813.               break;
    
  3814.           }
    
  3815.           break;
    
  3816.         case 'hooks':
    
  3817.           if (typeof overrideHookStateRenamePath === 'function') {
    
  3818.             overrideHookStateRenamePath(
    
  3819.               fiber,
    
  3820.               ((hookID: any): number),
    
  3821.               oldPath,
    
  3822.               newPath,
    
  3823.             );
    
  3824.           }
    
  3825.           break;
    
  3826.         case 'props':
    
  3827.           if (instance === null) {
    
  3828.             if (typeof overridePropsRenamePath === 'function') {
    
  3829.               overridePropsRenamePath(fiber, oldPath, newPath);
    
  3830.             }
    
  3831.           } else {
    
  3832.             fiber.pendingProps = copyWithRename(
    
  3833.               instance.props,
    
  3834.               oldPath,
    
  3835.               newPath,
    
  3836.             );
    
  3837.             instance.forceUpdate();
    
  3838.           }
    
  3839.           break;
    
  3840.         case 'state':
    
  3841.           renamePathInObject(instance.state, oldPath, newPath);
    
  3842.           instance.forceUpdate();
    
  3843.           break;
    
  3844.       }
    
  3845.     }
    
  3846.   }
    
  3847. 
    
  3848.   function overrideValueAtPath(
    
  3849.     type: 'context' | 'hooks' | 'props' | 'state',
    
  3850.     id: number,
    
  3851.     hookID: ?number,
    
  3852.     path: Array<string | number>,
    
  3853.     value: any,
    
  3854.   ): void {
    
  3855.     const fiber = findCurrentFiberUsingSlowPathById(id);
    
  3856.     if (fiber !== null) {
    
  3857.       const instance = fiber.stateNode;
    
  3858. 
    
  3859.       switch (type) {
    
  3860.         case 'context':
    
  3861.           // To simplify hydration and display of primitive context values (e.g. number, string)
    
  3862.           // the inspectElement() method wraps context in a {value: ...} object.
    
  3863.           // We need to remove the first part of the path (the "value") before continuing.
    
  3864.           path = path.slice(1);
    
  3865. 
    
  3866.           switch (fiber.tag) {
    
  3867.             case ClassComponent:
    
  3868.               if (path.length === 0) {
    
  3869.                 // Simple context value
    
  3870.                 instance.context = value;
    
  3871.               } else {
    
  3872.                 setInObject(instance.context, path, value);
    
  3873.               }
    
  3874.               instance.forceUpdate();
    
  3875.               break;
    
  3876.             case FunctionComponent:
    
  3877.               // Function components using legacy context are not editable
    
  3878.               // because there's no instance on which to create a cloned, mutated context.
    
  3879.               break;
    
  3880.           }
    
  3881.           break;
    
  3882.         case 'hooks':
    
  3883.           if (typeof overrideHookState === 'function') {
    
  3884.             overrideHookState(fiber, ((hookID: any): number), path, value);
    
  3885.           }
    
  3886.           break;
    
  3887.         case 'props':
    
  3888.           switch (fiber.tag) {
    
  3889.             case ClassComponent:
    
  3890.               fiber.pendingProps = copyWithSet(instance.props, path, value);
    
  3891.               instance.forceUpdate();
    
  3892.               break;
    
  3893.             default:
    
  3894.               if (typeof overrideProps === 'function') {
    
  3895.                 overrideProps(fiber, path, value);
    
  3896.               }
    
  3897.               break;
    
  3898.           }
    
  3899.           break;
    
  3900.         case 'state':
    
  3901.           switch (fiber.tag) {
    
  3902.             case ClassComponent:
    
  3903.               setInObject(instance.state, path, value);
    
  3904.               instance.forceUpdate();
    
  3905.               break;
    
  3906.           }
    
  3907.           break;
    
  3908.       }
    
  3909.     }
    
  3910.   }
    
  3911. 
    
  3912.   type CommitProfilingData = {
    
  3913.     changeDescriptions: Map<number, ChangeDescription> | null,
    
  3914.     commitTime: number,
    
  3915.     durations: Array<number>,
    
  3916.     effectDuration: number | null,
    
  3917.     maxActualDuration: number,
    
  3918.     passiveEffectDuration: number | null,
    
  3919.     priorityLevel: string | null,
    
  3920.     updaters: Array<SerializedElement> | null,
    
  3921.   };
    
  3922. 
    
  3923.   type CommitProfilingMetadataMap = Map<number, Array<CommitProfilingData>>;
    
  3924.   type DisplayNamesByRootID = Map<number, string>;
    
  3925. 
    
  3926.   let currentCommitProfilingMetadata: CommitProfilingData | null = null;
    
  3927.   let displayNamesByRootID: DisplayNamesByRootID | null = null;
    
  3928.   let idToContextsMap: Map<number, any> | null = null;
    
  3929.   let initialTreeBaseDurationsMap: Map<number, number> | null = null;
    
  3930.   let initialIDToRootMap: Map<number, number> | null = null;
    
  3931.   let isProfiling: boolean = false;
    
  3932.   let profilingStartTime: number = 0;
    
  3933.   let recordChangeDescriptions: boolean = false;
    
  3934.   let rootToCommitProfilingMetadataMap: CommitProfilingMetadataMap | null =
    
  3935.     null;
    
  3936. 
    
  3937.   function getProfilingData(): ProfilingDataBackend {
    
  3938.     const dataForRoots: Array<ProfilingDataForRootBackend> = [];
    
  3939. 
    
  3940.     if (rootToCommitProfilingMetadataMap === null) {
    
  3941.       throw Error(
    
  3942.         'getProfilingData() called before any profiling data was recorded',
    
  3943.       );
    
  3944.     }
    
  3945. 
    
  3946.     rootToCommitProfilingMetadataMap.forEach(
    
  3947.       (commitProfilingMetadata, rootID) => {
    
  3948.         const commitData: Array<CommitDataBackend> = [];
    
  3949.         const initialTreeBaseDurations: Array<[number, number]> = [];
    
  3950. 
    
  3951.         const displayName =
    
  3952.           (displayNamesByRootID !== null && displayNamesByRootID.get(rootID)) ||
    
  3953.           'Unknown';
    
  3954. 
    
  3955.         if (initialTreeBaseDurationsMap != null) {
    
  3956.           initialTreeBaseDurationsMap.forEach((treeBaseDuration, id) => {
    
  3957.             if (
    
  3958.               initialIDToRootMap != null &&
    
  3959.               initialIDToRootMap.get(id) === rootID
    
  3960.             ) {
    
  3961.               // We don't need to convert milliseconds to microseconds in this case,
    
  3962.               // because the profiling summary is JSON serialized.
    
  3963.               initialTreeBaseDurations.push([id, treeBaseDuration]);
    
  3964.             }
    
  3965.           });
    
  3966.         }
    
  3967. 
    
  3968.         commitProfilingMetadata.forEach((commitProfilingData, commitIndex) => {
    
  3969.           const {
    
  3970.             changeDescriptions,
    
  3971.             durations,
    
  3972.             effectDuration,
    
  3973.             maxActualDuration,
    
  3974.             passiveEffectDuration,
    
  3975.             priorityLevel,
    
  3976.             commitTime,
    
  3977.             updaters,
    
  3978.           } = commitProfilingData;
    
  3979. 
    
  3980.           const fiberActualDurations: Array<[number, number]> = [];
    
  3981.           const fiberSelfDurations: Array<[number, number]> = [];
    
  3982.           for (let i = 0; i < durations.length; i += 3) {
    
  3983.             const fiberID = durations[i];
    
  3984.             fiberActualDurations.push([fiberID, durations[i + 1]]);
    
  3985.             fiberSelfDurations.push([fiberID, durations[i + 2]]);
    
  3986.           }
    
  3987. 
    
  3988.           commitData.push({
    
  3989.             changeDescriptions:
    
  3990.               changeDescriptions !== null
    
  3991.                 ? Array.from(changeDescriptions.entries())
    
  3992.                 : null,
    
  3993.             duration: maxActualDuration,
    
  3994.             effectDuration,
    
  3995.             fiberActualDurations,
    
  3996.             fiberSelfDurations,
    
  3997.             passiveEffectDuration,
    
  3998.             priorityLevel,
    
  3999.             timestamp: commitTime,
    
  4000.             updaters,
    
  4001.           });
    
  4002.         });
    
  4003. 
    
  4004.         dataForRoots.push({
    
  4005.           commitData,
    
  4006.           displayName,
    
  4007.           initialTreeBaseDurations,
    
  4008.           rootID,
    
  4009.         });
    
  4010.       },
    
  4011.     );
    
  4012. 
    
  4013.     let timelineData = null;
    
  4014.     if (typeof getTimelineData === 'function') {
    
  4015.       const currentTimelineData = getTimelineData();
    
  4016.       if (currentTimelineData) {
    
  4017.         const {
    
  4018.           batchUIDToMeasuresMap,
    
  4019.           internalModuleSourceToRanges,
    
  4020.           laneToLabelMap,
    
  4021.           laneToReactMeasureMap,
    
  4022.           ...rest
    
  4023.         } = currentTimelineData;
    
  4024. 
    
  4025.         timelineData = {
    
  4026.           ...rest,
    
  4027. 
    
  4028.           // Most of the data is safe to parse as-is,
    
  4029.           // but we need to convert the nested Arrays back to Maps.
    
  4030.           // Most of the data is safe to serialize as-is,
    
  4031.           // but we need to convert the Maps to nested Arrays.
    
  4032.           batchUIDToMeasuresKeyValueArray: Array.from(
    
  4033.             batchUIDToMeasuresMap.entries(),
    
  4034.           ),
    
  4035.           internalModuleSourceToRanges: Array.from(
    
  4036.             internalModuleSourceToRanges.entries(),
    
  4037.           ),
    
  4038.           laneToLabelKeyValueArray: Array.from(laneToLabelMap.entries()),
    
  4039.           laneToReactMeasureKeyValueArray: Array.from(
    
  4040.             laneToReactMeasureMap.entries(),
    
  4041.           ),
    
  4042.         };
    
  4043.       }
    
  4044.     }
    
  4045. 
    
  4046.     return {
    
  4047.       dataForRoots,
    
  4048.       rendererID,
    
  4049.       timelineData,
    
  4050.     };
    
  4051.   }
    
  4052. 
    
  4053.   function startProfiling(shouldRecordChangeDescriptions: boolean) {
    
  4054.     if (isProfiling) {
    
  4055.       return;
    
  4056.     }
    
  4057. 
    
  4058.     recordChangeDescriptions = shouldRecordChangeDescriptions;
    
  4059. 
    
  4060.     // Capture initial values as of the time profiling starts.
    
  4061.     // It's important we snapshot both the durations and the id-to-root map,
    
  4062.     // since either of these may change during the profiling session
    
  4063.     // (e.g. when a fiber is re-rendered or when a fiber gets removed).
    
  4064.     displayNamesByRootID = new Map();
    
  4065.     initialTreeBaseDurationsMap = new Map(idToTreeBaseDurationMap);
    
  4066.     initialIDToRootMap = new Map(idToRootMap);
    
  4067.     idToContextsMap = new Map();
    
  4068. 
    
  4069.     hook.getFiberRoots(rendererID).forEach(root => {
    
  4070.       const rootID = getFiberIDThrows(root.current);
    
  4071.       ((displayNamesByRootID: any): DisplayNamesByRootID).set(
    
  4072.         rootID,
    
  4073.         getDisplayNameForRoot(root.current),
    
  4074.       );
    
  4075. 
    
  4076.       if (shouldRecordChangeDescriptions) {
    
  4077.         // Record all contexts at the time profiling is started.
    
  4078.         // Fibers only store the current context value,
    
  4079.         // so we need to track them separately in order to determine changed keys.
    
  4080.         crawlToInitializeContextsMap(root.current);
    
  4081.       }
    
  4082.     });
    
  4083. 
    
  4084.     isProfiling = true;
    
  4085.     profilingStartTime = getCurrentTime();
    
  4086.     rootToCommitProfilingMetadataMap = new Map();
    
  4087. 
    
  4088.     if (toggleProfilingStatus !== null) {
    
  4089.       toggleProfilingStatus(true);
    
  4090.     }
    
  4091.   }
    
  4092. 
    
  4093.   function stopProfiling() {
    
  4094.     isProfiling = false;
    
  4095.     recordChangeDescriptions = false;
    
  4096. 
    
  4097.     if (toggleProfilingStatus !== null) {
    
  4098.       toggleProfilingStatus(false);
    
  4099.     }
    
  4100.   }
    
  4101. 
    
  4102.   // Automatically start profiling so that we don't miss timing info from initial "mount".
    
  4103.   if (
    
  4104.     sessionStorageGetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true'
    
  4105.   ) {
    
  4106.     startProfiling(
    
  4107.       sessionStorageGetItem(SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY) ===
    
  4108.         'true',
    
  4109.     );
    
  4110.   }
    
  4111. 
    
  4112.   // React will switch between these implementations depending on whether
    
  4113.   // we have any manually suspended/errored-out Fibers or not.
    
  4114.   function shouldErrorFiberAlwaysNull() {
    
  4115.     return null;
    
  4116.   }
    
  4117. 
    
  4118.   // Map of id and its force error status: true (error), false (toggled off),
    
  4119.   // null (do nothing)
    
  4120.   const forceErrorForFiberIDs = new Map<number | null, $FlowFixMe>();
    
  4121. 
    
  4122.   function shouldErrorFiberAccordingToMap(fiber: any) {
    
  4123.     if (typeof setErrorHandler !== 'function') {
    
  4124.       throw new Error(
    
  4125.         'Expected overrideError() to not get called for earlier React versions.',
    
  4126.       );
    
  4127.     }
    
  4128. 
    
  4129.     const id = getFiberIDUnsafe(fiber);
    
  4130.     if (id === null) {
    
  4131.       return null;
    
  4132.     }
    
  4133. 
    
  4134.     let status = null;
    
  4135.     if (forceErrorForFiberIDs.has(id)) {
    
  4136.       status = forceErrorForFiberIDs.get(id);
    
  4137.       if (status === false) {
    
  4138.         // TRICKY overrideError adds entries to this Map,
    
  4139.         // so ideally it would be the method that clears them too,
    
  4140.         // but that would break the functionality of the feature,
    
  4141.         // since DevTools needs to tell React to act differently than it normally would
    
  4142.         // (don't just re-render the failed boundary, but reset its errored state too).
    
  4143.         // So we can only clear it after telling React to reset the state.
    
  4144.         // Technically this is premature and we should schedule it for later,
    
  4145.         // since the render could always fail without committing the updated error boundary,
    
  4146.         // but since this is a DEV-only feature, the simplicity is worth the trade off.
    
  4147.         forceErrorForFiberIDs.delete(id);
    
  4148. 
    
  4149.         if (forceErrorForFiberIDs.size === 0) {
    
  4150.           // Last override is gone. Switch React back to fast path.
    
  4151.           setErrorHandler(shouldErrorFiberAlwaysNull);
    
  4152.         }
    
  4153.       }
    
  4154.     }
    
  4155.     return status;
    
  4156.   }
    
  4157. 
    
  4158.   function overrideError(id: number, forceError: boolean) {
    
  4159.     if (
    
  4160.       typeof setErrorHandler !== 'function' ||
    
  4161.       typeof scheduleUpdate !== 'function'
    
  4162.     ) {
    
  4163.       throw new Error(
    
  4164.         'Expected overrideError() to not get called for earlier React versions.',
    
  4165.       );
    
  4166.     }
    
  4167. 
    
  4168.     forceErrorForFiberIDs.set(id, forceError);
    
  4169. 
    
  4170.     if (forceErrorForFiberIDs.size === 1) {
    
  4171.       // First override is added. Switch React to slower path.
    
  4172.       setErrorHandler(shouldErrorFiberAccordingToMap);
    
  4173.     }
    
  4174. 
    
  4175.     const fiber = idToArbitraryFiberMap.get(id);
    
  4176.     if (fiber != null) {
    
  4177.       scheduleUpdate(fiber);
    
  4178.     }
    
  4179.   }
    
  4180. 
    
  4181.   function shouldSuspendFiberAlwaysFalse() {
    
  4182.     return false;
    
  4183.   }
    
  4184. 
    
  4185.   const forceFallbackForSuspenseIDs = new Set<number>();
    
  4186. 
    
  4187.   function shouldSuspendFiberAccordingToSet(fiber: any) {
    
  4188.     const maybeID = getFiberIDUnsafe(((fiber: any): Fiber));
    
  4189.     return maybeID !== null && forceFallbackForSuspenseIDs.has(maybeID);
    
  4190.   }
    
  4191. 
    
  4192.   function overrideSuspense(id: number, forceFallback: boolean) {
    
  4193.     if (
    
  4194.       typeof setSuspenseHandler !== 'function' ||
    
  4195.       typeof scheduleUpdate !== 'function'
    
  4196.     ) {
    
  4197.       throw new Error(
    
  4198.         'Expected overrideSuspense() to not get called for earlier React versions.',
    
  4199.       );
    
  4200.     }
    
  4201.     if (forceFallback) {
    
  4202.       forceFallbackForSuspenseIDs.add(id);
    
  4203.       if (forceFallbackForSuspenseIDs.size === 1) {
    
  4204.         // First override is added. Switch React to slower path.
    
  4205.         setSuspenseHandler(shouldSuspendFiberAccordingToSet);
    
  4206.       }
    
  4207.     } else {
    
  4208.       forceFallbackForSuspenseIDs.delete(id);
    
  4209.       if (forceFallbackForSuspenseIDs.size === 0) {
    
  4210.         // Last override is gone. Switch React back to fast path.
    
  4211.         setSuspenseHandler(shouldSuspendFiberAlwaysFalse);
    
  4212.       }
    
  4213.     }
    
  4214.     const fiber = idToArbitraryFiberMap.get(id);
    
  4215.     if (fiber != null) {
    
  4216.       scheduleUpdate(fiber);
    
  4217.     }
    
  4218.   }
    
  4219. 
    
  4220.   // Remember if we're trying to restore the selection after reload.
    
  4221.   // In that case, we'll do some extra checks for matching mounts.
    
  4222.   let trackedPath: Array<PathFrame> | null = null;
    
  4223.   let trackedPathMatchFiber: Fiber | null = null;
    
  4224.   let trackedPathMatchDepth = -1;
    
  4225.   let mightBeOnTrackedPath = false;
    
  4226. 
    
  4227.   function setTrackedPath(path: Array<PathFrame> | null) {
    
  4228.     if (path === null) {
    
  4229.       trackedPathMatchFiber = null;
    
  4230.       trackedPathMatchDepth = -1;
    
  4231.       mightBeOnTrackedPath = false;
    
  4232.     }
    
  4233.     trackedPath = path;
    
  4234.   }
    
  4235. 
    
  4236.   // We call this before traversing a new mount.
    
  4237.   // It remembers whether this Fiber is the next best match for tracked path.
    
  4238.   // The return value signals whether we should keep matching siblings or not.
    
  4239.   function updateTrackedPathStateBeforeMount(fiber: Fiber): boolean {
    
  4240.     if (trackedPath === null || !mightBeOnTrackedPath) {
    
  4241.       // Fast path: there's nothing to track so do nothing and ignore siblings.
    
  4242.       return false;
    
  4243.     }
    
  4244.     const returnFiber = fiber.return;
    
  4245.     const returnAlternate = returnFiber !== null ? returnFiber.alternate : null;
    
  4246.     // By now we know there's some selection to restore, and this is a new Fiber.
    
  4247.     // Is this newly mounted Fiber a direct child of the current best match?
    
  4248.     // (This will also be true for new roots if we haven't matched anything yet.)
    
  4249.     if (
    
  4250.       trackedPathMatchFiber === returnFiber ||
    
  4251.       (trackedPathMatchFiber === returnAlternate && returnAlternate !== null)
    
  4252.     ) {
    
  4253.       // Is this the next Fiber we should select? Let's compare the frames.
    
  4254.       const actualFrame = getPathFrame(fiber);
    
  4255.       // $FlowFixMe[incompatible-use] found when upgrading Flow
    
  4256.       const expectedFrame = trackedPath[trackedPathMatchDepth + 1];
    
  4257.       if (expectedFrame === undefined) {
    
  4258.         throw new Error('Expected to see a frame at the next depth.');
    
  4259.       }
    
  4260.       if (
    
  4261.         actualFrame.index === expectedFrame.index &&
    
  4262.         actualFrame.key === expectedFrame.key &&
    
  4263.         actualFrame.displayName === expectedFrame.displayName
    
  4264.       ) {
    
  4265.         // We have our next match.
    
  4266.         trackedPathMatchFiber = fiber;
    
  4267.         trackedPathMatchDepth++;
    
  4268.         // Are we out of frames to match?
    
  4269.         // $FlowFixMe[incompatible-use] found when upgrading Flow
    
  4270.         if (trackedPathMatchDepth === trackedPath.length - 1) {
    
  4271.           // There's nothing that can possibly match afterwards.
    
  4272.           // Don't check the children.
    
  4273.           mightBeOnTrackedPath = false;
    
  4274.         } else {
    
  4275.           // Check the children, as they might reveal the next match.
    
  4276.           mightBeOnTrackedPath = true;
    
  4277.         }
    
  4278.         // In either case, since we have a match, we don't need
    
  4279.         // to check the siblings. They'll never match.
    
  4280.         return false;
    
  4281.       }
    
  4282.     }
    
  4283.     // This Fiber's parent is on the path, but this Fiber itself isn't.
    
  4284.     // There's no need to check its children--they won't be on the path either.
    
  4285.     mightBeOnTrackedPath = false;
    
  4286.     // However, one of its siblings may be on the path so keep searching.
    
  4287.     return true;
    
  4288.   }
    
  4289. 
    
  4290.   function updateTrackedPathStateAfterMount(
    
  4291.     mightSiblingsBeOnTrackedPath: boolean,
    
  4292.   ) {
    
  4293.     // updateTrackedPathStateBeforeMount() told us whether to match siblings.
    
  4294.     // Now that we're entering siblings, let's use that information.
    
  4295.     mightBeOnTrackedPath = mightSiblingsBeOnTrackedPath;
    
  4296.   }
    
  4297. 
    
  4298.   // Roots don't have a real persistent identity.
    
  4299.   // A root's "pseudo key" is "childDisplayName:indexWithThatName".
    
  4300.   // For example, "App:0" or, in case of similar roots, "Story:0", "Story:1", etc.
    
  4301.   // We will use this to try to disambiguate roots when restoring selection between reloads.
    
  4302.   const rootPseudoKeys: Map<number, string> = new Map();
    
  4303.   const rootDisplayNameCounter: Map<string, number> = new Map();
    
  4304. 
    
  4305.   function setRootPseudoKey(id: number, fiber: Fiber) {
    
  4306.     const name = getDisplayNameForRoot(fiber);
    
  4307.     const counter = rootDisplayNameCounter.get(name) || 0;
    
  4308.     rootDisplayNameCounter.set(name, counter + 1);
    
  4309.     const pseudoKey = `${name}:${counter}`;
    
  4310.     rootPseudoKeys.set(id, pseudoKey);
    
  4311.   }
    
  4312. 
    
  4313.   function removeRootPseudoKey(id: number) {
    
  4314.     const pseudoKey = rootPseudoKeys.get(id);
    
  4315.     if (pseudoKey === undefined) {
    
  4316.       throw new Error('Expected root pseudo key to be known.');
    
  4317.     }
    
  4318.     const name = pseudoKey.slice(0, pseudoKey.lastIndexOf(':'));
    
  4319.     const counter = rootDisplayNameCounter.get(name);
    
  4320.     if (counter === undefined) {
    
  4321.       throw new Error('Expected counter to be known.');
    
  4322.     }
    
  4323.     if (counter > 1) {
    
  4324.       rootDisplayNameCounter.set(name, counter - 1);
    
  4325.     } else {
    
  4326.       rootDisplayNameCounter.delete(name);
    
  4327.     }
    
  4328.     rootPseudoKeys.delete(id);
    
  4329.   }
    
  4330. 
    
  4331.   function getDisplayNameForRoot(fiber: Fiber): string {
    
  4332.     let preferredDisplayName = null;
    
  4333.     let fallbackDisplayName = null;
    
  4334.     let child = fiber.child;
    
  4335.     // Go at most three levels deep into direct children
    
  4336.     // while searching for a child that has a displayName.
    
  4337.     for (let i = 0; i < 3; i++) {
    
  4338.       if (child === null) {
    
  4339.         break;
    
  4340.       }
    
  4341.       const displayName = getDisplayNameForFiber(child);
    
  4342.       if (displayName !== null) {
    
  4343.         // Prefer display names that we get from user-defined components.
    
  4344.         // We want to avoid using e.g. 'Suspense' unless we find nothing else.
    
  4345.         if (typeof child.type === 'function') {
    
  4346.           // There's a few user-defined tags, but we'll prefer the ones
    
  4347.           // that are usually explicitly named (function or class components).
    
  4348.           preferredDisplayName = displayName;
    
  4349.         } else if (fallbackDisplayName === null) {
    
  4350.           fallbackDisplayName = displayName;
    
  4351.         }
    
  4352.       }
    
  4353.       if (preferredDisplayName !== null) {
    
  4354.         break;
    
  4355.       }
    
  4356.       child = child.child;
    
  4357.     }
    
  4358.     return preferredDisplayName || fallbackDisplayName || 'Anonymous';
    
  4359.   }
    
  4360. 
    
  4361.   function getPathFrame(fiber: Fiber): PathFrame {
    
  4362.     const {key} = fiber;
    
  4363.     let displayName = getDisplayNameForFiber(fiber);
    
  4364.     const index = fiber.index;
    
  4365.     switch (fiber.tag) {
    
  4366.       case HostRoot:
    
  4367.         // Roots don't have a real displayName, index, or key.
    
  4368.         // Instead, we'll use the pseudo key (childDisplayName:indexWithThatName).
    
  4369.         const id = getFiberIDThrows(fiber);
    
  4370.         const pseudoKey = rootPseudoKeys.get(id);
    
  4371.         if (pseudoKey === undefined) {
    
  4372.           throw new Error('Expected mounted root to have known pseudo key.');
    
  4373.         }
    
  4374.         displayName = pseudoKey;
    
  4375.         break;
    
  4376.       case HostComponent:
    
  4377.         displayName = fiber.type;
    
  4378.         break;
    
  4379.       default:
    
  4380.         break;
    
  4381.     }
    
  4382.     return {
    
  4383.       displayName,
    
  4384.       key,
    
  4385.       index,
    
  4386.     };
    
  4387.   }
    
  4388. 
    
  4389.   // Produces a serializable representation that does a best effort
    
  4390.   // of identifying a particular Fiber between page reloads.
    
  4391.   // The return path will contain Fibers that are "invisible" to the store
    
  4392.   // because their keys and indexes are important to restoring the selection.
    
  4393.   function getPathForElement(id: number): Array<PathFrame> | null {
    
  4394.     let fiber: ?Fiber = idToArbitraryFiberMap.get(id);
    
  4395.     if (fiber == null) {
    
  4396.       return null;
    
  4397.     }
    
  4398.     const keyPath = [];
    
  4399.     while (fiber !== null) {
    
  4400.       // $FlowFixMe[incompatible-call] found when upgrading Flow
    
  4401.       keyPath.push(getPathFrame(fiber));
    
  4402.       // $FlowFixMe[incompatible-use] found when upgrading Flow
    
  4403.       fiber = fiber.return;
    
  4404.     }
    
  4405.     keyPath.reverse();
    
  4406.     return keyPath;
    
  4407.   }
    
  4408. 
    
  4409.   function getBestMatchForTrackedPath(): PathMatch | null {
    
  4410.     if (trackedPath === null) {
    
  4411.       // Nothing to match.
    
  4412.       return null;
    
  4413.     }
    
  4414.     if (trackedPathMatchFiber === null) {
    
  4415.       // We didn't find anything.
    
  4416.       return null;
    
  4417.     }
    
  4418.     // Find the closest Fiber store is aware of.
    
  4419.     let fiber: null | Fiber = trackedPathMatchFiber;
    
  4420.     while (fiber !== null && shouldFilterFiber(fiber)) {
    
  4421.       fiber = fiber.return;
    
  4422.     }
    
  4423.     if (fiber === null) {
    
  4424.       return null;
    
  4425.     }
    
  4426.     return {
    
  4427.       id: getFiberIDThrows(fiber),
    
  4428.       // $FlowFixMe[incompatible-use] found when upgrading Flow
    
  4429.       isFullMatch: trackedPathMatchDepth === trackedPath.length - 1,
    
  4430.     };
    
  4431.   }
    
  4432. 
    
  4433.   const formatPriorityLevel = (priorityLevel: ?number) => {
    
  4434.     if (priorityLevel == null) {
    
  4435.       return 'Unknown';
    
  4436.     }
    
  4437. 
    
  4438.     switch (priorityLevel) {
    
  4439.       case ImmediatePriority:
    
  4440.         return 'Immediate';
    
  4441.       case UserBlockingPriority:
    
  4442.         return 'User-Blocking';
    
  4443.       case NormalPriority:
    
  4444.         return 'Normal';
    
  4445.       case LowPriority:
    
  4446.         return 'Low';
    
  4447.       case IdlePriority:
    
  4448.         return 'Idle';
    
  4449.       case NoPriority:
    
  4450.       default:
    
  4451.         return 'Unknown';
    
  4452.     }
    
  4453.   };
    
  4454. 
    
  4455.   function setTraceUpdatesEnabled(isEnabled: boolean): void {
    
  4456.     traceUpdatesEnabled = isEnabled;
    
  4457.   }
    
  4458. 
    
  4459.   function hasFiberWithId(id: number): boolean {
    
  4460.     return idToArbitraryFiberMap.has(id);
    
  4461.   }
    
  4462. 
    
  4463.   return {
    
  4464.     cleanup,
    
  4465.     clearErrorsAndWarnings,
    
  4466.     clearErrorsForFiberID,
    
  4467.     clearWarningsForFiberID,
    
  4468.     getSerializedElementValueByPath,
    
  4469.     deletePath,
    
  4470.     findNativeNodesForFiberID,
    
  4471.     flushInitialOperations,
    
  4472.     getBestMatchForTrackedPath,
    
  4473.     getDisplayNameForFiberID,
    
  4474.     getFiberForNative,
    
  4475.     getFiberIDForNative,
    
  4476.     getInstanceAndStyle,
    
  4477.     getOwnersList,
    
  4478.     getPathForElement,
    
  4479.     getProfilingData,
    
  4480.     handleCommitFiberRoot,
    
  4481.     handleCommitFiberUnmount,
    
  4482.     handlePostCommitFiberRoot,
    
  4483.     hasFiberWithId,
    
  4484.     inspectElement,
    
  4485.     logElementToConsole,
    
  4486.     patchConsoleForStrictMode,
    
  4487.     prepareViewAttributeSource,
    
  4488.     prepareViewElementSource,
    
  4489.     overrideError,
    
  4490.     overrideSuspense,
    
  4491.     overrideValueAtPath,
    
  4492.     renamePath,
    
  4493.     renderer,
    
  4494.     setTraceUpdatesEnabled,
    
  4495.     setTrackedPath,
    
  4496.     startProfiling,
    
  4497.     stopProfiling,
    
  4498.     storeAsGlobal,
    
  4499.     unpatchConsoleForStrictMode,
    
  4500.     updateComponentFilters,
    
  4501.   };
    
  4502. }