bippy 0.7.2 → 0.7.3-dev.0ac832d

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/rdt-hook.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  // This module must load before React so renderers can inject into the hook.
2
2
 
3
- import type { ReactDevToolsGlobalHook, ReactRenderer } from "./react-internals/index.js";
3
+ import { callListener } from "./call-listener.js";
4
+ import type { FiberRoot, ReactDevToolsGlobalHook, ReactRenderer } from "./react-internals/index.js";
4
5
 
5
6
  export interface Unsubscribe extends Disposable {
6
7
  (): void;
@@ -14,8 +15,17 @@ interface RendererInjectListener {
14
15
  (renderer: ReactRenderer): void;
15
16
  }
16
17
 
18
+ interface RendererInjectSubscription {
19
+ listener: RendererInjectListener;
20
+ target: ReactDevToolsTarget;
21
+ }
22
+
17
23
  interface RDTHookReplaceListener {
18
- (rdtHook: ReactDevToolsGlobalHook): void;
24
+ (rdtHook: ReactDevToolsGlobalHook, target: ReactDevToolsTarget): void;
25
+ }
26
+
27
+ export interface ReactDevToolsTarget {
28
+ __REACT_DEVTOOLS_GLOBAL_HOOK__?: ReactDevToolsGlobalHook;
19
29
  }
20
30
 
21
31
  export const version = process.env.VERSION;
@@ -25,10 +35,23 @@ const objectDefineProperty = Object.defineProperty;
25
35
 
26
36
  const noOp = (): void => {};
27
37
 
38
+ const getTargetHook = (target: ReactDevToolsTarget): ReactDevToolsGlobalHook | undefined =>
39
+ target.__REACT_DEVTOOLS_GLOBAL_HOOK__;
40
+
28
41
  export const createUnsubscribe = (unsubscribe: () => void): Unsubscribe =>
29
42
  Object.assign(unsubscribe, { [Symbol.dispose]: unsubscribe });
30
43
 
44
+ export const isFiberRootUnmounted = (fiberRoot: FiberRoot): boolean => {
45
+ const rootState = fiberRoot.current.memoizedState;
46
+ return (
47
+ rootState === null ||
48
+ (rootState !== undefined && (rootState.element === null || rootState.element === undefined))
49
+ );
50
+ };
51
+
31
52
  const checkDCE = (functionToCheck: unknown): void => {
53
+ // HACK: Node loads React's unbundled entrypoints, where development guards are intentionally not eliminated.
54
+ if (typeof process !== "undefined" && process.versions?.node) return;
32
55
  try {
33
56
  const code = Function.prototype.toString.call(functionToCheck);
34
57
  if (code.indexOf("^_^") > -1) {
@@ -47,7 +70,7 @@ const checkDCE = (functionToCheck: unknown): void => {
47
70
  export const isRealReactDevtools = (
48
71
  rdtHook: ReactDevToolsGlobalHook | undefined | null = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__,
49
72
  ): boolean => {
50
- return Boolean(rdtHook && "getFiberRoots" in rdtHook);
73
+ return Boolean(rdtHook && "getFiberRoots" in rdtHook && rdtHook._isBippyHook !== true);
51
74
  };
52
75
 
53
76
  // HACK: react-refresh's inject wrapper is the only stock hook whose parameter is named
@@ -69,53 +92,63 @@ export const _onActiveListeners = new Set<ActiveListener>();
69
92
 
70
93
  export const _renderers = new Set<ReactRenderer>();
71
94
 
72
- const rendererInjectListeners = new Set<RendererInjectListener>();
95
+ const activeListenerTargets = new WeakMap<ActiveListener, Set<ReactDevToolsTarget>>();
96
+ const rendererInjectSubscriptions = new Set<RendererInjectSubscription>();
73
97
  const rdtHookReplaceListeners = new Set<RDTHookReplaceListener>();
74
- // HACK: Hook replacement can leave an old inject wrapper in the call chain, so notifications must be deduplicated.
75
- const notifiedRenderers = new WeakSet<ReactRenderer>();
76
- const notifyingInjectByHook = new WeakMap<
77
- ReactDevToolsGlobalHook,
78
- ReactDevToolsGlobalHook["inject"]
79
- >();
80
-
81
- const notifyActiveListeners = (): void => {
82
- for (const listener of _onActiveListeners) {
83
- listener();
98
+ const notifiedRenderersByTarget = new WeakMap<ReactDevToolsTarget, WeakSet<ReactRenderer>>();
99
+
100
+ const addActiveListener = (listener: ActiveListener, target: ReactDevToolsTarget): void => {
101
+ _onActiveListeners.add(listener);
102
+ const targets = activeListenerTargets.get(listener) ?? new Set<ReactDevToolsTarget>();
103
+ targets.add(target);
104
+ activeListenerTargets.set(listener, targets);
105
+ };
106
+
107
+ export const removeActiveListener = (
108
+ listener: ActiveListener,
109
+ target: ReactDevToolsTarget,
110
+ ): void => {
111
+ const targets = activeListenerTargets.get(listener);
112
+ targets?.delete(target);
113
+ if (!targets || targets.size === 0) {
114
+ activeListenerTargets.delete(listener);
115
+ _onActiveListeners.delete(listener);
84
116
  }
85
117
  };
86
118
 
87
- const notifyRendererInjectListeners = (renderer: ReactRenderer): void => {
88
- for (const listener of rendererInjectListeners) {
89
- listener(renderer);
119
+ const notifyActiveListeners = (target: ReactDevToolsTarget): void => {
120
+ for (const listener of _onActiveListeners) {
121
+ if (activeListenerTargets.get(listener)?.has(target)) callListener(listener, undefined);
90
122
  }
91
123
  };
92
124
 
93
- const notifyRDTHookReplaceListeners = (rdtHook: ReactDevToolsGlobalHook): void => {
94
- for (const listener of rdtHookReplaceListeners) {
95
- listener(rdtHook);
125
+ const notifyRendererInjectListeners = (
126
+ target: ReactDevToolsTarget,
127
+ renderer: ReactRenderer,
128
+ ): void => {
129
+ for (const subscription of rendererInjectSubscriptions) {
130
+ if (subscription.target === target) callListener(subscription.listener, subscription, renderer);
96
131
  }
97
132
  };
98
133
 
99
- const setRendererInjectDispatcher = (rdtHook: ReactDevToolsGlobalHook): void => {
100
- if (rdtHook.inject === notifyingInjectByHook.get(rdtHook)) return;
101
- const previousInject = rdtHook.inject;
102
- const nextInject = (renderer: ReactRenderer) => {
103
- const rendererId = previousInject.call(rdtHook, renderer);
104
- if (!notifiedRenderers.has(renderer)) {
105
- notifiedRenderers.add(renderer);
106
- notifyRendererInjectListeners(renderer);
107
- }
108
- return rendererId;
109
- };
110
- rdtHook.inject = nextInject;
111
- notifyingInjectByHook.set(rdtHook, nextInject);
134
+ const notifyRDTHookReplaceListeners = (
135
+ rdtHook: ReactDevToolsGlobalHook,
136
+ target: ReactDevToolsTarget,
137
+ ): void => {
138
+ for (const listener of rdtHookReplaceListeners) {
139
+ callListener(listener, undefined, rdtHook, target);
140
+ }
112
141
  };
113
142
 
114
- export const onRendererInject = (listener: RendererInjectListener): Unsubscribe => {
115
- setRendererInjectDispatcher(getRDTHook());
116
- rendererInjectListeners.add(listener);
143
+ export const onRendererInject = (
144
+ listener: RendererInjectListener,
145
+ target: ReactDevToolsTarget = globalThis,
146
+ ): Unsubscribe => {
147
+ getRDTHook(undefined, target);
148
+ const subscription = { listener, target };
149
+ rendererInjectSubscriptions.add(subscription);
117
150
  return createUnsubscribe(() => {
118
- rendererInjectListeners.delete(listener);
151
+ rendererInjectSubscriptions.delete(subscription);
119
152
  });
120
153
  };
121
154
 
@@ -126,8 +159,13 @@ export const onRDTHookReplace = (listener: RDTHookReplaceListener): Unsubscribe
126
159
  });
127
160
  };
128
161
 
162
+ // A Map built in another realm (an iframe target) fails instanceof, so replacing it
163
+ // would discard that realm's renderer registry. Object.prototype.toString is realm-agnostic.
164
+ export const isRendererMap = (value: unknown): value is Map<number, ReactRenderer> =>
165
+ Object.prototype.toString.call(value) === "[object Map]";
166
+
129
167
  const getRendererMap = (rdtHook: ReactDevToolsGlobalHook): Map<number, ReactRenderer> => {
130
- if (!(rdtHook.renderers instanceof Map)) {
168
+ if (!isRendererMap(rdtHook.renderers)) {
131
169
  rdtHook.renderers = new Map();
132
170
  }
133
171
  return rdtHook.renderers;
@@ -135,6 +173,7 @@ const getRendererMap = (rdtHook: ReactDevToolsGlobalHook): Map<number, ReactRend
135
173
 
136
174
  const trackInjectedRenderer = (
137
175
  rdtHook: ReactDevToolsGlobalHook,
176
+ target: ReactDevToolsTarget,
138
177
  renderers: Map<number, ReactRenderer>,
139
178
  rendererId: number,
140
179
  renderer: ReactRenderer,
@@ -143,28 +182,47 @@ const trackInjectedRenderer = (
143
182
  _renderers.add(renderer);
144
183
  if (!rdtHook._instrumentationIsActive) {
145
184
  rdtHook._instrumentationIsActive = true;
146
- notifyActiveListeners();
185
+ notifyActiveListeners(target);
147
186
  }
187
+ const notifiedRenderers = notifiedRenderersByTarget.get(target) ?? new WeakSet<ReactRenderer>();
188
+ notifiedRenderersByTarget.set(target, notifiedRenderers);
189
+ if (notifiedRenderers.has(renderer)) return;
190
+ notifiedRenderers.add(renderer);
191
+ notifyRendererInjectListeners(target, renderer);
148
192
  };
149
193
 
150
- export const installRDTHook = (onActive?: ActiveListener): ReactDevToolsGlobalHook => {
151
- if (onActive) {
152
- _onActiveListeners.add(onActive);
153
- }
194
+ export const installRDTHook = (
195
+ onActive?: ActiveListener,
196
+ target: ReactDevToolsTarget = globalThis,
197
+ ): ReactDevToolsGlobalHook => {
198
+ if (onActive) addActiveListener(onActive, target);
154
199
  const renderers = new Map<number, ReactRenderer>();
200
+ const fiberRoots = new Map<number, Set<FiberRoot>>();
201
+ // Callers register roots by mutating the returned set, so it must be memoized.
202
+ const getRendererFiberRoots = (rendererId: number): Set<FiberRoot> => {
203
+ const rendererRoots = fiberRoots.get(rendererId) ?? new Set<FiberRoot>();
204
+ fiberRoots.set(rendererId, rendererRoots);
205
+ return rendererRoots;
206
+ };
155
207
  let rendererIdCounter = 0;
156
208
  let rdtHook: ReactDevToolsGlobalHook = {
157
209
  _instrumentationIsActive: false,
158
210
  _instrumentationSource: BIPPY_INSTRUMENTATION_STRING,
211
+ _isBippyHook: true,
159
212
  checkDCE,
213
+ getFiberRoots: getRendererFiberRoots,
160
214
  hasUnsupportedRendererAttached: false,
161
215
  inject: (renderer) => {
162
216
  const nextRendererId = ++rendererIdCounter;
163
- trackInjectedRenderer(rdtHook, renderers, nextRendererId, renderer);
217
+ trackInjectedRenderer(rdtHook, target, renderers, nextRendererId, renderer);
164
218
  return nextRendererId;
165
219
  },
166
220
  on: noOp,
167
- onCommitFiberRoot: noOp,
221
+ onCommitFiberRoot: (rendererId, fiberRoot) => {
222
+ const rendererRoots = getRendererFiberRoots(rendererId);
223
+ if (isFiberRootUnmounted(fiberRoot)) rendererRoots.delete(fiberRoot);
224
+ else rendererRoots.add(fiberRoot);
225
+ },
168
226
  onCommitFiberUnmount: noOp,
169
227
  onPostCommitFiberRoot: noOp,
170
228
  renderers,
@@ -172,13 +230,11 @@ export const installRDTHook = (onActive?: ActiveListener): ReactDevToolsGlobalHo
172
230
  supportsFlight: true,
173
231
  };
174
232
  try {
175
- objectDefineProperty(globalThis, "__REACT_DEVTOOLS_GLOBAL_HOOK__", {
233
+ objectDefineProperty(target, "__REACT_DEVTOOLS_GLOBAL_HOOK__", {
176
234
  configurable: true,
177
235
  enumerable: true,
178
- get() {
179
- return rdtHook;
180
- },
181
- set(newHook) {
236
+ get: () => rdtHook,
237
+ set: (newHook: ReactDevToolsGlobalHook | undefined) => {
182
238
  if (newHook && typeof newHook === "object") {
183
239
  const ourRenderers = rdtHook.renderers;
184
240
  rdtHook = newHook;
@@ -188,13 +244,13 @@ export const installRDTHook = (onActive?: ActiveListener): ReactDevToolsGlobalHo
188
244
  nextRenderers.set(rendererId, renderer);
189
245
  });
190
246
  if (ourRenderers.size > 0 || rdtHookReplaceListeners.size > 0) {
191
- patchRDTHook(onActive);
247
+ patchRDTHook(onActive, target);
192
248
  }
193
- notifyRDTHookReplaceListeners(rdtHook);
249
+ notifyRDTHookReplaceListeners(rdtHook, target);
194
250
  }
195
251
  },
196
252
  });
197
- if (typeof window !== "undefined") {
253
+ if (target === globalThis && typeof window !== "undefined") {
198
254
  // HACK: The DevTools extension uses hasOwnProperty to decide whether it may replace an earlier hook.
199
255
  const originalWindowHasOwnProperty = window.hasOwnProperty.bind(window);
200
256
  const originalHasOwnPropertyDescriptor = Object.getOwnPropertyDescriptor(
@@ -224,17 +280,18 @@ export const installRDTHook = (onActive?: ActiveListener): ReactDevToolsGlobalHo
224
280
  });
225
281
  }
226
282
  } catch {
227
- patchRDTHook(onActive);
283
+ patchRDTHook(onActive, target);
228
284
  }
229
285
  return rdtHook;
230
286
  };
231
287
 
232
- export const patchRDTHook = (onActive?: ActiveListener): void => {
233
- if (onActive) {
234
- _onActiveListeners.add(onActive);
235
- }
288
+ export const patchRDTHook = (
289
+ onActive?: ActiveListener,
290
+ target: ReactDevToolsTarget = globalThis,
291
+ ): void => {
292
+ if (onActive) addActiveListener(onActive, target);
236
293
  let didNotifyActiveListeners = false;
237
- const rdtHook = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;
294
+ const rdtHook = getTargetHook(target);
238
295
  if (!rdtHook) return;
239
296
  const renderers = getRendererMap(rdtHook);
240
297
  if (!rdtHook._instrumentationSource) {
@@ -252,40 +309,42 @@ export const patchRDTHook = (onActive?: ActiveListener): void => {
252
309
  if (renderers.size) {
253
310
  renderers.forEach((renderer) => _renderers.add(renderer));
254
311
  rdtHook._instrumentationIsActive = true;
255
- notifyActiveListeners();
312
+ notifyActiveListeners(target);
256
313
  didNotifyActiveListeners = true;
257
314
  } else if (!isReactDevtools && isReactRefresh(rdtHook)) {
258
315
  // HACK: react-refresh's stub inject never records renderers, so a React app
259
316
  // that injected before bippy loaded is undetectable through the renderers map.
260
317
  // A react-refresh hook implies a dev renderer, so activate immediately.
261
318
  rdtHook._instrumentationIsActive = true;
262
- notifyActiveListeners();
319
+ notifyActiveListeners(target);
263
320
  didNotifyActiveListeners = true;
264
321
  }
265
322
  const previousInject = rdtHook.inject;
266
323
  rdtHook.inject = (renderer) => {
267
324
  const rendererId = previousInject.call(rdtHook, renderer);
268
- trackInjectedRenderer(rdtHook, renderers, rendererId, renderer);
325
+ trackInjectedRenderer(rdtHook, target, renderers, rendererId, renderer);
269
326
  return rendererId;
270
327
  };
271
328
  }
272
329
  if (!didNotifyActiveListeners && (renderers.size || rdtHook._instrumentationIsActive)) {
273
- onActive?.();
330
+ if (onActive) callListener(onActive, undefined);
274
331
  }
275
332
  };
276
333
 
277
- export const hasRDTHook = (): boolean => {
278
- return Object.hasOwn(globalThis, "__REACT_DEVTOOLS_GLOBAL_HOOK__");
279
- };
334
+ export const hasRDTHook = (target: ReactDevToolsTarget = globalThis): boolean =>
335
+ Object.hasOwn(target, "__REACT_DEVTOOLS_GLOBAL_HOOK__");
280
336
 
281
337
  /**
282
338
  * Returns the current React DevTools global hook.
283
339
  */
284
- export const getRDTHook = (onActive?: ActiveListener): ReactDevToolsGlobalHook => {
285
- if (!hasRDTHook()) {
286
- return installRDTHook(onActive);
340
+ export const getRDTHook = (
341
+ onActive?: ActiveListener,
342
+ target: ReactDevToolsTarget = globalThis,
343
+ ): ReactDevToolsGlobalHook => {
344
+ if (!hasRDTHook(target)) {
345
+ return installRDTHook(onActive, target);
287
346
  }
288
347
 
289
- patchRDTHook(onActive);
290
- return globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__ ?? installRDTHook(onActive);
348
+ patchRDTHook(onActive, target);
349
+ return getTargetHook(target) ?? installRDTHook(onActive, target);
291
350
  };
@@ -0,0 +1,84 @@
1
+ // Adapted from ReactFiberTreeReflection.js. Copyright (c) Meta Platforms, Inc. and affiliates. MIT licensed.
2
+ import type { Fiber } from "./types.js";
3
+ import { getReactWorkTagsForFiber } from "./index.js";
4
+
5
+ export const getCurrentFiberFromRoot = (fiber: Fiber): Fiber | null => {
6
+ const alternate = fiber.alternate;
7
+ if (!alternate) return null;
8
+ let firstBranch = fiber;
9
+ let secondBranch = alternate;
10
+
11
+ while (firstBranch.return) {
12
+ const firstParent = firstBranch.return;
13
+ const secondParent = firstParent.alternate;
14
+ if (!secondParent) {
15
+ const nextParent = firstParent.return;
16
+ if (!nextParent) return null;
17
+ firstBranch = secondBranch = nextParent;
18
+ continue;
19
+ }
20
+
21
+ if (firstParent.child === secondParent.child) {
22
+ let child = firstParent.child;
23
+ while (child) {
24
+ if (child === firstBranch || child === secondBranch) {
25
+ let root = firstParent;
26
+ while (root.return) root = root.return;
27
+ if (root.tag !== getReactWorkTagsForFiber(root).HostRoot) return null;
28
+ return child === firstBranch ? fiber : alternate;
29
+ }
30
+ child = child.sibling;
31
+ }
32
+ return null;
33
+ }
34
+
35
+ if (firstBranch.return !== secondBranch.return) {
36
+ firstBranch = firstParent;
37
+ secondBranch = secondParent;
38
+ } else {
39
+ let didFindChild = false;
40
+ let child = firstParent.child;
41
+ while (child) {
42
+ if (child === firstBranch) {
43
+ firstBranch = firstParent;
44
+ secondBranch = secondParent;
45
+ didFindChild = true;
46
+ break;
47
+ }
48
+ if (child === secondBranch) {
49
+ firstBranch = secondParent;
50
+ secondBranch = firstParent;
51
+ didFindChild = true;
52
+ break;
53
+ }
54
+ child = child.sibling;
55
+ }
56
+ if (!didFindChild) {
57
+ child = secondParent.child;
58
+ while (child) {
59
+ if (child === firstBranch) {
60
+ firstBranch = secondParent;
61
+ secondBranch = firstParent;
62
+ didFindChild = true;
63
+ break;
64
+ }
65
+ if (child === secondBranch) {
66
+ firstBranch = firstParent;
67
+ secondBranch = secondParent;
68
+ didFindChild = true;
69
+ break;
70
+ }
71
+ child = child.sibling;
72
+ }
73
+ if (!didFindChild) return null;
74
+ }
75
+ }
76
+ if (firstBranch.alternate !== secondBranch) return null;
77
+ }
78
+
79
+ if (firstBranch.tag !== getReactWorkTagsForFiber(firstBranch).HostRoot) return null;
80
+ const root = firstBranch.stateNode;
81
+ if (typeof root !== "object" || root === null || !("current" in root)) return null;
82
+ if (root.current === firstBranch) return fiber;
83
+ return root.current === secondBranch ? alternate : null;
84
+ };
@@ -18,8 +18,24 @@ export type {
18
18
  } from "./generated/react-work-tags.js";
19
19
  export * from "./types.js";
20
20
 
21
+ interface InheritedWorkTags {
22
+ workTags: Readonly<ReactWorkTagMap>;
23
+ generation: number;
24
+ }
25
+
21
26
  const defaultReactWorkTags = getReactWorkTags();
22
27
  const fiberReactWorkTags = new WeakMap<Fiber, Readonly<ReactWorkTagMap>>();
28
+ const inheritedFiberWorkTags = new WeakMap<Fiber, InheritedWorkTags>();
29
+ let workTagGeneration = 0;
30
+
31
+ const getCachedWorkTags = (fiber: Fiber): Readonly<ReactWorkTagMap> | undefined => {
32
+ const assignedWorkTags = fiberReactWorkTags.get(fiber);
33
+ if (assignedWorkTags) return assignedWorkTags;
34
+ const inheritedWorkTags = inheritedFiberWorkTags.get(fiber);
35
+ return inheritedWorkTags?.generation === workTagGeneration
36
+ ? inheritedWorkTags.workTags
37
+ : undefined;
38
+ };
23
39
 
24
40
  // React's experimental channel historically reported "0.0.0-experimental-<sha>"
25
41
  // as the runtime version; those builds use modern work tags, not the 16.x rows
@@ -39,25 +55,36 @@ export const getReactWorkTagsForRenderer = (
39
55
 
40
56
  export const setReactWorkTagsForFiber = (fiber: Fiber, renderer?: ReactRenderer): void => {
41
57
  const workTags = getReactWorkTagsForRenderer(renderer);
58
+ if (
59
+ getCachedWorkTags(fiber) !== workTags ||
60
+ (fiber.alternate && getCachedWorkTags(fiber.alternate) !== workTags)
61
+ ) {
62
+ workTagGeneration++;
63
+ }
42
64
  fiberReactWorkTags.set(fiber, workTags);
43
65
  if (fiber.alternate) fiberReactWorkTags.set(fiber.alternate, workTags);
44
66
  };
45
67
 
46
68
  export const getReactWorkTagsForFiber = (fiber: Fiber): Readonly<ReactWorkTagMap> => {
47
- const cachedWorkTags = fiberReactWorkTags.get(fiber);
48
- if (cachedWorkTags) return cachedWorkTags;
49
-
50
- const traversedFibers: Fiber[] = [fiber];
51
- let rootFiber = fiber;
52
- while (rootFiber.return) {
53
- rootFiber = rootFiber.return;
54
- traversedFibers.push(rootFiber);
69
+ let workTags = getCachedWorkTags(fiber);
70
+ if (workTags) return workTags;
71
+ const traversedFibers: Fiber[] = [];
72
+ let ancestor = fiber;
73
+ while (!workTags) {
74
+ traversedFibers.push(ancestor);
75
+ const parent = ancestor.return;
76
+ if (!parent) {
77
+ workTags = inheritedFiberWorkTags.get(ancestor)?.workTags ?? defaultReactWorkTags;
78
+ break;
79
+ }
80
+ ancestor = parent;
81
+ workTags = getCachedWorkTags(ancestor);
55
82
  }
56
- const workTags = fiberReactWorkTags.get(rootFiber) ?? defaultReactWorkTags;
83
+ const inheritedWorkTags = { workTags, generation: workTagGeneration };
57
84
  for (const traversedFiber of traversedFibers) {
58
- fiberReactWorkTags.set(traversedFiber, workTags);
85
+ inheritedFiberWorkTags.set(traversedFiber, inheritedWorkTags);
59
86
  if (traversedFiber.alternate) {
60
- fiberReactWorkTags.set(traversedFiber.alternate, workTags);
87
+ inheritedFiberWorkTags.set(traversedFiber.alternate, inheritedWorkTags);
61
88
  }
62
89
  }
63
90
  return workTags;
@@ -31,12 +31,30 @@ export interface ServerComponentInfo {
31
31
  env?: string;
32
32
  owner?: Fiber | ServerComponentInfo | null;
33
33
  debugStack?: Error | null;
34
+ debugLocation?: Error | null;
35
+ }
36
+
37
+ export interface ReactIOInfo {
38
+ name: string;
39
+ start: number;
40
+ end: number;
41
+ byteSize?: number;
42
+ value?: Promise<unknown> | null;
43
+ env?: string;
44
+ owner?: ServerComponentInfo | null;
45
+ stack?: unknown[] | null;
46
+ debugStack?: Error | null;
34
47
  }
35
48
 
36
49
  export interface ReactDebugInfo {
37
- debugLocation?: unknown;
50
+ awaited?: ReactIOInfo;
51
+ debugLocation?: Error | null;
38
52
  env?: string;
39
53
  name?: string;
54
+ owner?: ServerComponentInfo | null;
55
+ stack?: unknown[] | null;
56
+ debugStack?: Error | null;
57
+ time?: number;
40
58
  }
41
59
 
42
60
  export interface ReactMemoCache {
@@ -71,10 +89,10 @@ export interface Fiber<T = unknown> extends Omit<
71
89
  | "tag"
72
90
  | "updateQueue"
73
91
  > {
74
- _debugInfo?: ReactDebugInfo[];
92
+ _debugInfo?: ReactDebugInfo[] | null;
75
93
  _debugOwner?: Fiber | ServerComponentInfo | null;
76
94
  _debugSource?: FiberDebugSource | null;
77
- _debugStack?: Error & { stack: string };
95
+ _debugStack?: Error | null;
78
96
  alternate: Fiber | null;
79
97
  child: Fiber | null;
80
98
  deletions: Fiber[] | null;
@@ -96,6 +114,8 @@ export interface HostFiber<T = unknown> extends Fiber<T> {
96
114
 
97
115
  export interface FiberRoot {
98
116
  current: Fiber;
117
+ effectDuration?: number;
118
+ passiveEffectDuration?: number;
99
119
  }
100
120
 
101
121
  export interface MemoizedState {
@@ -115,6 +135,7 @@ export interface ReactDevToolsEventHandler {
115
135
  export interface ReactDevToolsGlobalHook {
116
136
  _instrumentationIsActive?: boolean;
117
137
  _instrumentationSource?: string;
138
+ _isBippyHook?: boolean;
118
139
  checkDCE: (fn: unknown) => void;
119
140
  emit?: (event: string, data?: unknown) => void;
120
141
  getFiberRoots?: (rendererID: number) => Set<FiberRoot>;
@@ -149,14 +170,20 @@ export type RendererDispatcherRef = CurrentDispatcherRef | LegacyDispatcherRef;
149
170
 
150
171
  export interface ReactRenderer extends Omit<
151
172
  ReactReconciler.DevToolsConfig<unknown, unknown, unknown>,
152
- "findFiberByHostInstance"
173
+ "bundleType" | "findFiberByHostInstance"
153
174
  > {
175
+ bundleType: number;
154
176
  currentDispatcherRef?: RendererDispatcherRef | null;
155
177
  findFiberByHostInstance?: (hostInstance: unknown) => Fiber | null;
156
178
  getCurrentFiber?: () => Fiber | null;
157
179
  overrideContext?: (fiber: Fiber, contextType: unknown, path: string[], value: unknown) => void;
158
180
 
159
- overrideHookState?: (fiber: Fiber, id: number, path: string[], value: unknown) => void;
181
+ overrideHookState?: (
182
+ fiber: Fiber,
183
+ id: number,
184
+ path: Array<number | string>,
185
+ value: unknown,
186
+ ) => void;
160
187
  overrideHookStateDeletePath?: (fiber: Fiber, id: number, path: Array<number | string>) => void;
161
188
  overrideHookStateRenamePath?: (
162
189
  fiber: Fiber,
@@ -164,7 +191,7 @@ export interface ReactRenderer extends Omit<
164
191
  oldPath: Array<number | string>,
165
192
  newPath: Array<number | string>,
166
193
  ) => void;
167
- overrideProps?: (fiber: Fiber, path: string[], value: unknown) => void;
194
+ overrideProps?: (fiber: Fiber, path: Array<number | string>, value: unknown) => void;
168
195
  overridePropsDeletePath?: (fiber: Fiber, path: Array<number | string>) => void;
169
196
  overridePropsRenamePath?: (
170
197
  fiber: Fiber,