bippy 0.6.1-dev.3a24abf → 0.6.1-dev.93556ef

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.
Files changed (51) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +26 -1
  3. package/dist/core.cjs +1 -1
  4. package/dist/core.d.cts +39 -42
  5. package/dist/core.d.ts +39 -42
  6. package/dist/core.js +1 -1
  7. package/dist/core2.cjs +1 -1
  8. package/dist/core2.d.cts +11 -3
  9. package/dist/core2.d.ts +11 -3
  10. package/dist/core2.js +1 -1
  11. package/dist/{types.d.cts → errors.d.cts} +187 -68
  12. package/dist/{types.d.ts → errors.d.ts} +187 -68
  13. package/dist/index.cjs +1 -1
  14. package/dist/index.d.cts +11 -3
  15. package/dist/index.d.ts +11 -3
  16. package/dist/index.iife.js +1 -1
  17. package/dist/index.js +1 -1
  18. package/dist/install-hook-only.cjs +1 -1
  19. package/dist/install-hook-only.d.cts +8 -0
  20. package/dist/install-hook-only.d.ts +8 -0
  21. package/dist/install-hook-only.iife.js +1 -1
  22. package/dist/install-hook-only.js +1 -1
  23. package/dist/rdt-hook.cjs +1 -1
  24. package/dist/rdt-hook.js +1 -1
  25. package/dist/source.cjs +13 -14
  26. package/dist/source.d.cts +86 -69
  27. package/dist/source.d.ts +86 -69
  28. package/dist/source.js +13 -14
  29. package/package.json +10 -6
  30. package/src/core.ts +246 -293
  31. package/src/errors.ts +34 -0
  32. package/src/install-hook-only.ts +2 -2
  33. package/src/rdt-hook.ts +152 -110
  34. package/src/react-internals/generated/react-work-tags.ts +318 -0
  35. package/src/react-internals/index.ts +67 -0
  36. package/src/react-internals/semver.ts +80 -0
  37. package/src/{types.ts → react-internals/types.ts} +13 -86
  38. package/src/source/error-stack.ts +11 -0
  39. package/src/source/get-display-name-from-source.ts +44 -40
  40. package/src/source/get-source.ts +42 -26
  41. package/src/source/index.ts +11 -1
  42. package/src/source/inspect-hooks.ts +220 -207
  43. package/src/source/owner-stack.ts +86 -128
  44. package/src/source/parse-debug-stack.ts +4 -4
  45. package/src/source/parse-hook-names.ts +14 -53
  46. package/src/source/parse-stack.ts +14 -31
  47. package/src/source/renderer-dispatchers.ts +30 -0
  48. package/src/source/symbolication.ts +703 -135
  49. package/src/generated/react-work-tags.ts +0 -169
  50. package/src/react-internals.ts +0 -67
  51. package/src/unsubscribe.ts +0 -17
package/src/core.ts CHANGED
@@ -10,102 +10,45 @@ import type {
10
10
  MemoizedState,
11
11
  ReactDevToolsGlobalHook,
12
12
  ReactRenderer,
13
- } from "./types.js";
13
+ } from "./react-internals/index.js";
14
14
 
15
15
  import {
16
16
  _onActiveListeners,
17
17
  BIPPY_INSTRUMENTATION_STRING,
18
+ createUnsubscribe,
18
19
  getRDTHook,
19
20
  hasRDTHook,
20
21
  isRealReactDevtools,
22
+ onRDTHookReplace,
21
23
  onRendererInject,
22
24
  } from "./rdt-hook.js";
25
+ import type { Unsubscribe } from "./rdt-hook.js";
23
26
  import {
24
- ClassComponentTag,
25
- CONCURRENT_MODE_NUMBER,
26
- CONCURRENT_MODE_SYMBOL_DESCRIPTION,
27
- CONCURRENT_MODE_SYMBOL_STRING,
28
- ContextConsumerTag,
29
- DEPRECATED_ASYNC_MODE_SYMBOL_DESCRIPTION,
30
- DEPRECATED_ASYNC_MODE_SYMBOL_STRING,
31
- DehydratedSuspenseComponentTag,
32
- ELEMENT_TYPE_SYMBOL_STRING,
33
- ForwardRefTag,
34
- FragmentTag,
35
- FunctionComponentTag,
36
- HostComponentTag,
37
- HostHoistableTag,
38
- HostRootTag,
39
- HostSingletonTag,
40
- HostTextTag,
41
- LegacyHiddenComponentTag,
42
- MemoComponentTag,
27
+ getReactWorkTagsForFiber,
43
28
  MutationMask,
44
- OffscreenComponentTag,
45
29
  ReactBuildType,
46
30
  ReactFiberFlags,
47
- SimpleMemoComponentTag,
48
- SuspenseComponentTag,
49
- TRANSITIONAL_ELEMENT_TYPE_SYMBOL_STRING,
50
- } from "./react-internals.js";
51
- import { toUnsubscribe, type Unsubscribe } from "./unsubscribe.js";
31
+ ReactSymbols,
32
+ setReactWorkTagsForFiber,
33
+ } from "./react-internals/index.js";
52
34
 
35
+ export { getReactWorkTags, ReactSymbols } from "./react-internals/index.js";
36
+ export type { ReactWorkTagMap, ReactWorkTagVersion } from "./react-internals/index.js";
53
37
  export {
54
- ActivityComponentTag,
55
- ClassComponentTag,
56
- CONCURRENT_MODE_NUMBER,
57
- CONCURRENT_MODE_SYMBOL_DESCRIPTION,
58
- CONCURRENT_MODE_SYMBOL_STRING,
59
- ContextConsumerTag,
60
- DEPRECATED_ASYNC_MODE_SYMBOL_DESCRIPTION,
61
- DEPRECATED_ASYNC_MODE_SYMBOL_STRING,
62
- DehydratedSuspenseComponentTag,
63
- ELEMENT_TYPE_SYMBOL_STRING,
64
- ForwardRefTag,
65
- FragmentTag,
66
- FunctionComponentTag,
67
- HostComponentTag,
68
- HostHoistableTag,
69
- HostPortalTag,
70
- HostRootTag,
71
- HostSingletonTag,
72
- HostTextTag,
73
- LazyComponentTag,
74
- LegacyHiddenComponentTag,
75
- MemoComponentTag,
76
- OffscreenComponentTag,
77
- SimpleMemoComponentTag,
78
- SuspenseComponentTag,
79
- SuspenseListComponentTag,
80
- TRANSITIONAL_ELEMENT_TYPE_SYMBOL_STRING,
81
- ViewTransitionComponentTag,
82
- } from "./react-internals.js";
83
-
84
- const { Cloned, PerformedWork } = ReactFiberFlags;
85
-
86
- const isObjectRecord = (value: unknown): value is Record<string, unknown> =>
87
- typeof value === "object" && value !== null;
38
+ BippyError,
39
+ BippyHookInspectionError,
40
+ BippyHookRenderError,
41
+ BippySourceMapError,
42
+ BippyUnsupportedHookError,
43
+ } from "./errors.js";
88
44
 
89
45
  const isComponentType = (value: unknown): value is React.ComponentType<unknown> =>
90
46
  typeof value === "function";
91
47
 
92
- const getPropertyValue = (value: object, key: PropertyKey): unknown => {
93
- let currentValue: object | null = value;
94
- while (currentValue) {
95
- const descriptor = Object.getOwnPropertyDescriptor(currentValue, key);
96
- if (descriptor) {
97
- if ("value" in descriptor) return descriptor.value;
98
- return descriptor.get?.call(value);
99
- }
100
- currentValue = Object.getPrototypeOf(currentValue);
101
- }
102
- return undefined;
103
- };
104
-
105
48
  const getTypeName = (value: object): string | null => {
106
- const displayName = getPropertyValue(value, "displayName");
49
+ const displayName = "displayName" in value ? value.displayName : null;
107
50
  if (typeof displayName === "string" && displayName) return displayName;
108
- const name = getPropertyValue(value, "name");
51
+ const name = "name" in value ? value.name : null;
109
52
  return typeof name === "string" && name ? name : null;
110
53
  };
111
54
 
@@ -113,6 +56,20 @@ interface FiberSelector {
113
56
  (node: Fiber): boolean | Promise<boolean | void> | void;
114
57
  }
115
58
 
59
+ export interface FiberTimings {
60
+ selfTime: number;
61
+ totalTime: number;
62
+ }
63
+
64
+ export interface RenderHandler {
65
+ (fiber: Fiber, phase: RenderPhase): unknown;
66
+ }
67
+
68
+ interface ValueWrite {
69
+ path: string[];
70
+ value: unknown;
71
+ }
72
+
116
73
  /**
117
74
  * Returns `true` if object is a React Element.
118
75
  *
@@ -122,9 +79,8 @@ export const isValidElement = (element: unknown): element is React.ReactElement
122
79
  typeof element === "object" &&
123
80
  element !== null &&
124
81
  "$$typeof" in element &&
125
- [ELEMENT_TYPE_SYMBOL_STRING, TRANSITIONAL_ELEMENT_TYPE_SYMBOL_STRING].includes(
126
- String(element.$$typeof),
127
- );
82
+ (String(element.$$typeof) === ReactSymbols.LEGACY_ELEMENT_SYMBOL_STRING ||
83
+ String(element.$$typeof) === ReactSymbols.ELEMENT_SYMBOL_STRING);
128
84
 
129
85
  /**
130
86
  * Returns `true` if object is a React Fiber.
@@ -139,17 +95,24 @@ export const isValidFiber = (fiber: unknown): fiber is Fiber =>
139
95
  "sibling" in fiber &&
140
96
  "flags" in fiber;
141
97
 
98
+ const isFiberRoot = (fiberRoot: unknown): fiberRoot is FiberRoot =>
99
+ typeof fiberRoot === "object" &&
100
+ fiberRoot !== null &&
101
+ "current" in fiberRoot &&
102
+ isValidFiber(fiberRoot.current);
103
+
142
104
  /**
143
105
  * Returns `true` if fiber is a host fiber. Host fibers are DOM nodes in react-dom, `View` in react-native, etc.
144
106
  *
145
107
  * @see https://reactnative.dev/architecture/glossary#host-view-tree-and-host-view
146
108
  */
147
109
  export const isHostFiber = (fiber: Fiber): fiber is HostFiber => {
110
+ const workTags = getReactWorkTagsForFiber(fiber);
148
111
  switch (fiber.tag) {
149
- case HostComponentTag:
150
- case HostTextTag:
151
- case HostHoistableTag:
152
- case HostSingletonTag:
112
+ case workTags.HostComponent:
113
+ case workTags.HostText:
114
+ case workTags.HostHoistable:
115
+ case workTags.HostSingleton:
153
116
  return true;
154
117
  default:
155
118
  return false;
@@ -162,12 +125,13 @@ export const isHostFiber = (fiber: Fiber): fiber is HostFiber => {
162
125
  * @see https://reactnative.dev/architecture/glossary#react-composite-components
163
126
  */
164
127
  export const isCompositeFiber = (fiber: Fiber): boolean => {
128
+ const workTags = getReactWorkTagsForFiber(fiber);
165
129
  switch (fiber.tag) {
166
- case ClassComponentTag:
167
- case ForwardRefTag:
168
- case FunctionComponentTag:
169
- case MemoComponentTag:
170
- case SimpleMemoComponentTag:
130
+ case workTags.ClassComponent:
131
+ case workTags.ForwardRef:
132
+ case workTags.FunctionComponent:
133
+ case workTags.MemoComponent:
134
+ case workTags.SimpleMemoComponent:
171
135
  return true;
172
136
  default:
173
137
  return false;
@@ -275,15 +239,16 @@ export const didFiberRender = (fiber: Fiber): boolean => {
275
239
  const nextProps = fiber.memoizedProps;
276
240
  const prevProps = fiber.alternate?.memoizedProps || {};
277
241
  const flags = fiber.flags ?? fiber.effectTag ?? 0;
242
+ const workTags = getReactWorkTagsForFiber(fiber);
278
243
 
279
244
  switch (fiber.tag) {
280
- case ClassComponentTag:
281
- case ContextConsumerTag:
282
- case ForwardRefTag:
283
- case FunctionComponentTag:
284
- case MemoComponentTag:
285
- case SimpleMemoComponentTag: {
286
- return (flags & PerformedWork) === PerformedWork;
245
+ case workTags.ClassComponent:
246
+ case workTags.ContextConsumer:
247
+ case workTags.ForwardRef:
248
+ case workTags.FunctionComponent:
249
+ case workTags.MemoComponent:
250
+ case workTags.SimpleMemoComponent: {
251
+ return (flags & ReactFiberFlags.PerformedWork) === ReactFiberFlags.PerformedWork;
287
252
  }
288
253
  default:
289
254
  // Host nodes (DOM, root, etc.)
@@ -301,8 +266,8 @@ export const didFiberRender = (fiber: Fiber): boolean => {
301
266
  */
302
267
  export const didFiberCommit = (fiber: Fiber): boolean => {
303
268
  return Boolean(
304
- (fiber.flags & (MutationMask | Cloned)) !== 0 ||
305
- (fiber.subtreeFlags & (MutationMask | Cloned)) !== 0,
269
+ (fiber.flags & (MutationMask | ReactFiberFlags.Cloned)) !== 0 ||
270
+ (fiber.subtreeFlags & (MutationMask | ReactFiberFlags.Cloned)) !== 0,
306
271
  );
307
272
  };
308
273
 
@@ -341,8 +306,7 @@ export const getFiberStack = (fiber: Fiber): Fiber[] => {
341
306
  let currentFiber = fiber;
342
307
  while (currentFiber.return) {
343
308
  stack.push(currentFiber);
344
- const parentFiber = currentFiber.return;
345
- currentFiber = parentFiber;
309
+ currentFiber = currentFiber.return;
346
310
  }
347
311
  return stack;
348
312
  };
@@ -351,8 +315,9 @@ export const getFiberStack = (fiber: Fiber): Fiber[] => {
351
315
  * Returns `true` if the {@link Fiber} should be filtered out during reconciliation.
352
316
  */
353
317
  const shouldFilterFiber = (fiber: Fiber): boolean => {
318
+ const workTags = getReactWorkTagsForFiber(fiber);
354
319
  switch (fiber.tag) {
355
- case DehydratedSuspenseComponentTag:
320
+ case workTags.DehydratedSuspenseComponent:
356
321
  // TODO: ideally we would show dehydrated Suspense immediately.
357
322
  // However, it has some special behavior (like disconnecting
358
323
  // an alternate and turning into real Suspense) which breaks DevTools.
@@ -360,13 +325,13 @@ const shouldFilterFiber = (fiber: Fiber): boolean => {
360
325
  // https://github.com/bvaughn/react-devtools-experimental/issues/197
361
326
  return true;
362
327
 
363
- case FragmentTag:
364
- case HostTextTag:
365
- case LegacyHiddenComponentTag:
366
- case OffscreenComponentTag:
328
+ case workTags.Fragment:
329
+ case workTags.HostText:
330
+ case workTags.LegacyHiddenComponent:
331
+ case workTags.OffscreenComponent:
367
332
  return true;
368
333
 
369
- case HostRootTag:
334
+ case workTags.HostRoot:
370
335
  // It is never valid to filter the root element.
371
336
  return false;
372
337
 
@@ -376,15 +341,15 @@ const shouldFilterFiber = (fiber: Fiber): boolean => {
376
341
 
377
342
  if (typeof symbolOrNumber === "symbol") {
378
343
  return (
379
- symbolOrNumber.description === CONCURRENT_MODE_SYMBOL_DESCRIPTION ||
380
- symbolOrNumber.description === DEPRECATED_ASYNC_MODE_SYMBOL_DESCRIPTION
344
+ symbolOrNumber.description === ReactSymbols.CONCURRENT_MODE_SYMBOL_DESCRIPTION ||
345
+ symbolOrNumber.description === ReactSymbols.DEPRECATED_ASYNC_MODE_SYMBOL_DESCRIPTION
381
346
  );
382
347
  }
383
348
 
384
349
  switch (symbolOrNumber) {
385
- case CONCURRENT_MODE_NUMBER:
386
- case CONCURRENT_MODE_SYMBOL_STRING:
387
- case DEPRECATED_ASYNC_MODE_SYMBOL_STRING:
350
+ case ReactSymbols.CONCURRENT_MODE_NUMBER:
351
+ case ReactSymbols.CONCURRENT_MODE_SYMBOL_STRING:
352
+ case ReactSymbols.DEPRECATED_ASYNC_MODE_SYMBOL_STRING:
388
353
  return true;
389
354
 
390
355
  default:
@@ -448,6 +413,11 @@ export function traverseFiber(
448
413
  selector: (node: Fiber) => Promise<boolean | void>,
449
414
  ascending?: boolean,
450
415
  ): Promise<Fiber | null>;
416
+ export function traverseFiber(
417
+ fiber: Fiber | null,
418
+ selector: FiberSelector,
419
+ ascending?: boolean,
420
+ ): Fiber | null | Promise<Fiber | null>;
451
421
  export function traverseFiber(
452
422
  fiber: Fiber | null,
453
423
  selector: FiberSelector,
@@ -455,68 +425,47 @@ export function traverseFiber(
455
425
  ): Fiber | null | Promise<Fiber | null> {
456
426
  if (!fiber) return null;
457
427
 
458
- const firstResult = selector(fiber);
459
- if (firstResult instanceof Promise) {
460
- return (async () => {
461
- if ((await firstResult) === true) return fiber;
462
-
463
- let child = ascending ? fiber.return : fiber.child;
464
- while (child) {
465
- const match = await traverseFiberAsync(child, selector, ascending);
466
- if (match) return match;
467
- child = ascending ? null : child.sibling;
468
- }
469
- return null;
470
- })();
428
+ const selection = selector(fiber);
429
+ if (isPromiseLike<boolean | void>(selection)) {
430
+ return Promise.resolve(selection).then((didSelectFiber) =>
431
+ didSelectFiber === true ? fiber : traverseFiberChildren(fiber, selector, ascending),
432
+ );
471
433
  }
434
+ if (selection === true) return fiber;
472
435
 
473
- if (firstResult === true) return fiber;
474
-
475
- let child = ascending ? fiber.return : fiber.child;
476
- while (child) {
477
- const match = traverseFiberSync(child, selector, ascending);
478
- if (match) return match;
479
- child = ascending ? null : child.sibling;
480
- }
481
- return null;
436
+ return traverseFiberChildren(fiber, selector, ascending);
482
437
  }
483
438
 
484
- const traverseFiberSync = (
485
- fiber: Fiber | null,
486
- selector: FiberSelector,
487
- ascending = false,
488
- ): Fiber | null => {
489
- if (!fiber) return null;
490
- const selection = selector(fiber);
491
- if (selection instanceof Promise) return null;
492
- if (selection === true) return fiber;
493
-
494
- let child = ascending ? fiber.return : fiber.child;
495
- while (child) {
496
- const match = traverseFiberSync(child, selector, ascending);
497
- if (match) return match;
439
+ const isPromiseLike = <Result>(value: unknown): value is PromiseLike<Result> =>
440
+ (typeof value === "object" || typeof value === "function") &&
441
+ value !== null &&
442
+ "then" in value &&
443
+ typeof value.then === "function";
498
444
 
499
- child = ascending ? null : child.sibling;
500
- }
501
- return null;
445
+ const traverseFiberChildren = (
446
+ fiber: Fiber,
447
+ selector: FiberSelector,
448
+ ascending: boolean,
449
+ ): Fiber | null | Promise<Fiber | null> => {
450
+ const firstChild = ascending ? fiber.return : fiber.child;
451
+ return traverseFiberSiblings(firstChild, selector, ascending);
502
452
  };
503
453
 
504
- const traverseFiberAsync = async (
454
+ const traverseFiberSiblings = (
505
455
  fiber: Fiber | null,
506
456
  selector: FiberSelector,
507
- ascending = false,
508
- ): Promise<Fiber | null> => {
457
+ ascending: boolean,
458
+ ): Fiber | null | Promise<Fiber | null> => {
509
459
  if (!fiber) return null;
510
- if ((await selector(fiber)) === true) return fiber;
511
-
512
- let child = ascending ? fiber.return : fiber.child;
513
- while (child) {
514
- const match = await traverseFiberAsync(child, selector, ascending);
515
- if (match) return match;
516
460
 
517
- child = ascending ? null : child.sibling;
461
+ const nextSibling = ascending ? null : fiber.sibling;
462
+ const match = traverseFiber(fiber, selector, ascending);
463
+ if (isPromiseLike<Fiber | null>(match)) {
464
+ return Promise.resolve(match).then(
465
+ (resolvedMatch) => resolvedMatch ?? traverseFiberSiblings(nextSibling, selector, ascending),
466
+ );
518
467
  }
519
- return null;
468
+ return match ?? traverseFiberSiblings(nextSibling, selector, ascending);
520
469
  };
521
470
 
522
471
  /**
@@ -528,7 +477,7 @@ const traverseFiberAsync = async (
528
477
  * console.log(selfTime, totalTime);
529
478
  * ```
530
479
  */
531
- export const getTimings = (fiber?: Fiber | null): { selfTime: number; totalTime: number } => {
480
+ export const getTimings = (fiber?: Fiber | null): FiberTimings => {
532
481
  const totalTime = fiber?.actualDuration ?? 0;
533
482
  let selfTime = totalTime;
534
483
  // TODO: calculate a DOM time, which is just host component summed up
@@ -552,8 +501,8 @@ export const hasMemoCache = (fiber: Fiber): boolean => {
552
501
  */
553
502
  export const getType = (type: unknown): null | React.ComponentType<unknown> => {
554
503
  if (isComponentType(type)) return type;
555
- if (!isObjectRecord(type)) return null;
556
- return getType(type.type ?? type.render);
504
+ if (typeof type !== "object" || type === null) return null;
505
+ return getType(Reflect.get(type, "type") ?? Reflect.get(type, "render"));
557
506
  };
558
507
 
559
508
  /**
@@ -605,35 +554,35 @@ export const getLatestFiber = (fiber: Fiber): Fiber => {
605
554
  }
606
555
  for (const root of _fiberRoots) {
607
556
  const latestFiber = traverseFiber(root.current, (innerFiber) => {
608
- if (innerFiber === fiber) return true;
557
+ if (innerFiber === fiber || innerFiber === alternate) return true;
609
558
  });
610
559
  if (latestFiber) return latestFiber;
611
560
  }
612
561
  return fiber;
613
562
  };
614
563
 
615
- export type RenderHandler = <S>(fiber: Fiber, phase: RenderPhase, state?: S) => unknown;
616
-
617
564
  export type RenderPhase = "mount" | "unmount" | "update";
618
565
 
619
- let fiberId = 0;
566
+ let nextFiberId = 0;
620
567
  const fiberIdMap = new WeakMap<Fiber, number>();
621
568
 
622
- export const setFiberId = (fiber: Fiber, id: number = fiberId++): void => {
623
- fiberIdMap.set(fiber, id);
569
+ export const setFiberId = (fiber: Fiber, fiberId: number = nextFiberId++): void => {
570
+ fiberIdMap.set(fiber, fiberId);
571
+ if (Number.isSafeInteger(fiberId) && fiberId >= nextFiberId) {
572
+ nextFiberId = fiberId + 1;
573
+ }
624
574
  };
625
575
 
626
- // Fiber alternates share an identity across double-buffered commits.
627
576
  export const getFiberId = (fiber: Fiber): number => {
628
- let id = fiberIdMap.get(fiber);
629
- if (id === undefined && fiber.alternate) {
630
- id = fiberIdMap.get(fiber.alternate);
577
+ let currentFiberId = fiberIdMap.get(fiber);
578
+ if (currentFiberId === undefined && fiber.alternate) {
579
+ currentFiberId = fiberIdMap.get(fiber.alternate);
631
580
  }
632
- if (id === undefined) {
633
- id = fiberId++;
634
- setFiberId(fiber, id);
581
+ if (currentFiberId === undefined) {
582
+ currentFiberId = nextFiberId++;
583
+ setFiberId(fiber, currentFiberId);
635
584
  }
636
- return id;
585
+ return currentFiberId;
637
586
  };
638
587
 
639
588
  const mountFiberRecursively = (
@@ -644,15 +593,13 @@ const mountFiberRecursively = (
644
593
  let fiber: Fiber | null = firstChild;
645
594
 
646
595
  while (fiber !== null) {
647
- if (!fiberIdMap.has(fiber)) {
648
- getFiberId(fiber);
649
- }
596
+ getFiberId(fiber);
650
597
  const shouldIncludeInTree = !shouldFilterFiber(fiber);
651
598
  if (shouldIncludeInTree && didFiberRender(fiber)) {
652
599
  onRender(fiber, "mount");
653
600
  }
654
601
 
655
- if (fiber.tag === SuspenseComponentTag) {
602
+ if (fiber.tag === getReactWorkTagsForFiber(fiber).SuspenseComponent) {
656
603
  const isTimedOut = fiber.memoizedState !== null;
657
604
  if (isTimedOut) {
658
605
  // Special case: if Suspense mounts in a timed-out state,
@@ -682,18 +629,13 @@ const mountFiberRecursively = (
682
629
  const updateFiberRecursively = (
683
630
  onRender: RenderHandler,
684
631
  nextFiber: Fiber,
685
- prevFiber: Fiber,
686
- parentFiber: Fiber | null,
632
+ prevFiber: Fiber | null,
687
633
  ): void => {
688
- if (!fiberIdMap.has(nextFiber)) {
689
- getFiberId(nextFiber);
690
- }
634
+ getFiberId(nextFiber);
691
635
  if (!prevFiber) return;
692
- if (!fiberIdMap.has(prevFiber)) {
693
- getFiberId(prevFiber);
694
- }
636
+ getFiberId(prevFiber);
695
637
 
696
- const isSuspense = nextFiber.tag === SuspenseComponentTag;
638
+ const isSuspense = nextFiber.tag === getReactWorkTagsForFiber(nextFiber).SuspenseComponent;
697
639
 
698
640
  const shouldIncludeInTree = !shouldFilterFiber(nextFiber);
699
641
  if (shouldIncludeInTree && didFiberRender(nextFiber)) {
@@ -722,7 +664,7 @@ const updateFiberRecursively = (
722
664
  const prevFallbackChildSet = prevFiber.child?.sibling ?? null;
723
665
 
724
666
  if (nextFallbackChildSet !== null && prevFallbackChildSet !== null) {
725
- updateFiberRecursively(onRender, nextFallbackChildSet, prevFallbackChildSet, nextFiber);
667
+ updateFiberRecursively(onRender, nextFallbackChildSet, prevFallbackChildSet);
726
668
  }
727
669
  } else if (prevDidTimeout && !nextDidTimeOut) {
728
670
  // Fallback -> Primary:
@@ -761,14 +703,7 @@ const updateFiberRecursively = (
761
703
  // Schedule updates and mounts depending on whether alternates exist.
762
704
  // We don't track deletions here because they are reported separately.
763
705
  if (nextChild.alternate) {
764
- const prevChild = nextChild.alternate;
765
-
766
- updateFiberRecursively(
767
- onRender,
768
- nextChild,
769
- prevChild,
770
- shouldIncludeInTree ? nextFiber : parentFiber,
771
- );
706
+ updateFiberRecursively(onRender, nextChild, nextChild.alternate);
772
707
  } else {
773
708
  mountFiberRecursively(onRender, nextChild, false);
774
709
  }
@@ -780,7 +715,7 @@ const updateFiberRecursively = (
780
715
  };
781
716
 
782
717
  const unmountFiber = (onRender: RenderHandler, fiber: Fiber): void => {
783
- const isRoot = fiber.tag === HostRootTag;
718
+ const isRoot = fiber.tag === getReactWorkTagsForFiber(fiber).HostRoot;
784
719
 
785
720
  if (isRoot || !shouldFilterFiber(fiber)) {
786
721
  onRender(fiber, "unmount");
@@ -789,7 +724,8 @@ const unmountFiber = (onRender: RenderHandler, fiber: Fiber): void => {
789
724
 
790
725
  const unmountFiberChildrenRecursively = (onRender: RenderHandler, fiber: Fiber): void => {
791
726
  // We might meet a nested Suspense on our way.
792
- const isTimedOutSuspense = fiber.tag === SuspenseComponentTag && fiber.memoizedState !== null;
727
+ const isTimedOutSuspense =
728
+ fiber.tag === getReactWorkTagsForFiber(fiber).SuspenseComponent && fiber.memoizedState !== null;
793
729
  let child = fiber.child;
794
730
 
795
731
  if (isTimedOutSuspense) {
@@ -813,11 +749,9 @@ const unmountFiberChildrenRecursively = (onRender: RenderHandler, fiber: Fiber):
813
749
  }
814
750
  };
815
751
 
816
- let commitId = 0;
817
752
  const rootInstanceMap = new WeakMap<
818
- FiberRoot,
753
+ Fiber | FiberRoot,
819
754
  {
820
- id: number;
821
755
  prevFiber: Fiber | null;
822
756
  }
823
757
  >();
@@ -829,23 +763,23 @@ const rootInstanceMap = new WeakMap<
829
763
  * console.log(phase)
830
764
  * })
831
765
  */
832
- export const traverseRenderedFibers = (root: FiberRoot, onRender: RenderHandler): void => {
766
+ export const traverseRenderedFibers = (root: Fiber | FiberRoot, onRender: RenderHandler): void => {
833
767
  const fiber = "current" in root ? root.current : root;
834
768
 
835
769
  let rootInstance = rootInstanceMap.get(root);
836
770
 
837
771
  if (!rootInstance) {
838
- rootInstance = { id: commitId++, prevFiber: null };
772
+ rootInstance = { prevFiber: null };
839
773
  rootInstanceMap.set(root, rootInstance);
840
774
  }
841
775
 
842
776
  const { prevFiber } = rootInstance;
843
- // if fiberRoot don't have current instance, means it's been unmounted
844
777
  if (!fiber) {
845
- unmountFiber(onRender, fiber);
778
+ if (prevFiber) {
779
+ unmountFiber(onRender, prevFiber);
780
+ }
846
781
  } else if (prevFiber !== null) {
847
782
  const wasMounted =
848
- prevFiber &&
849
783
  prevFiber.memoizedState !== null &&
850
784
  prevFiber.memoizedState.element !== null &&
851
785
  prevFiber.memoizedState.element !== undefined &&
@@ -861,7 +795,7 @@ export const traverseRenderedFibers = (root: FiberRoot, onRender: RenderHandler)
861
795
  if (!wasMounted && isMounted) {
862
796
  mountFiberRecursively(onRender, fiber, false);
863
797
  } else if (wasMounted && isMounted) {
864
- updateFiberRecursively(onRender, fiber, fiber.alternate, null);
798
+ updateFiberRecursively(onRender, fiber, fiber.alternate);
865
799
  } else if (wasMounted && !isMounted) {
866
800
  unmountFiber(onRender, fiber);
867
801
  }
@@ -878,9 +812,8 @@ let areOverrideRenderersWired = false;
878
812
  const wireOverrideRenderers = (): void => {
879
813
  if (!hasRDTHook()) return;
880
814
  const rdtHook = getRDTHook();
881
- if (!rdtHook?.renderers) return;
882
815
 
883
- ensureHookDispatchesToListeners(rdtHook);
816
+ setHookEventDispatchers(rdtHook);
884
817
  for (const renderer of rdtHook.renderers.values()) {
885
818
  overrideRenderers.add(renderer);
886
819
  }
@@ -897,14 +830,13 @@ const getRootRenderer = (fiber: Fiber): ReactRenderer | null => {
897
830
  while (hostRootFiber.return) {
898
831
  hostRootFiber = hostRootFiber.return;
899
832
  }
900
- const rendererId = rootRendererIds.get(hostRootFiber.stateNode);
833
+ const fiberRoot = hostRootFiber.stateNode;
834
+ if (!isFiberRoot(fiberRoot)) return null;
835
+ const rendererId = rootRendererIds.get(fiberRoot);
901
836
  if (rendererId === undefined) return null;
902
- return getRDTHook().renderers?.get(rendererId) ?? null;
837
+ return getRDTHook().renderers.get(rendererId) ?? null;
903
838
  };
904
839
 
905
- // dispatch to the renderer that owns the fiber's root when known; renderers
906
- // that never committed through bippy's hook patch fall back to "try all",
907
- // which matches the pre-ownership behavior for single-renderer apps
908
840
  const resolveOverrideRenderers = (fiber: Fiber): ReactRenderer[] => {
909
841
  wireOverrideRenderers();
910
842
  const rootRenderer = getRootRenderer(fiber);
@@ -930,7 +862,7 @@ const getHookStateDispatch = (
930
862
  hookIndex: number,
931
863
  ): ((value: unknown) => void) | null => {
932
864
  let hookState = fiber.memoizedState;
933
- for (let i = 0; i < hookIndex; i++) {
865
+ for (let currentHookIndex = 0; currentHookIndex < hookIndex; currentHookIndex++) {
934
866
  if (!hookState?.next) return null;
935
867
  hookState = hookState.next;
936
868
  }
@@ -963,28 +895,30 @@ const isPOJO = (maybePOJO: unknown): maybePOJO is Record<string, unknown> => {
963
895
  const buildPathsFromValue = (
964
896
  maybePOJO: Record<string, unknown>,
965
897
  basePath: string[] = [],
966
- ): Array<{ path: string[]; value: unknown }> => {
967
- if (!isPOJO(maybePOJO)) {
898
+ ancestors = new WeakSet<object>(),
899
+ ): ValueWrite[] => {
900
+ if (ancestors.has(maybePOJO)) {
968
901
  return [{ path: basePath, value: maybePOJO }];
969
902
  }
970
903
 
971
- const paths: Array<{ path: string[]; value: unknown }> = [];
904
+ ancestors.add(maybePOJO);
905
+ const paths: ValueWrite[] = [];
972
906
 
973
- for (const key in maybePOJO) {
974
- const value = maybePOJO[key];
907
+ for (const [key, value] of Object.entries(maybePOJO)) {
975
908
  const path = basePath.concat(key);
976
909
 
977
910
  if (isPOJO(value)) {
978
- paths.push(...buildPathsFromValue(value, path));
911
+ paths.push(...buildPathsFromValue(value, path, ancestors));
979
912
  } else {
980
913
  paths.push({ path, value });
981
914
  }
982
915
  }
983
916
 
917
+ ancestors.delete(maybePOJO);
984
918
  return paths;
985
919
  };
986
920
 
987
- const buildValueWrites = (partialValue: unknown): Array<{ path: string[]; value: unknown }> =>
921
+ const buildValueWrites = (partialValue: unknown): ValueWrite[] =>
988
922
  isPOJO(partialValue) ? buildPathsFromValue(partialValue) : [{ path: [], value: partialValue }];
989
923
 
990
924
  export const overrideProps = (fiber: Fiber, partialValue: Record<string, unknown>) => {
@@ -998,9 +932,9 @@ export const overrideHookState = (fiber: Fiber, id: number, partialValue: unknow
998
932
  const renderers = resolveOverrideRenderers(fiber).filter((renderer) =>
999
933
  Boolean(renderer.overrideHookState),
1000
934
  );
1001
- const writes = buildValueWrites(partialValue);
1002
935
 
1003
936
  if (renderers.length > 0) {
937
+ const writes = buildValueWrites(partialValue);
1004
938
  for (const renderer of renderers) {
1005
939
  for (const { path, value } of writes) {
1006
940
  try {
@@ -1048,18 +982,11 @@ export interface InstrumentationOptions {
1048
982
  onScheduleFiberRoot?: (rendererID: number, root: FiberRoot, children: React.ReactNode) => unknown;
1049
983
  }
1050
984
 
1051
- const commitFiberRootListeners = new Set<
1052
- NonNullable<InstrumentationOptions["onCommitFiberRoot"]>
1053
- >();
1054
- const commitFiberUnmountListeners = new Set<
1055
- NonNullable<InstrumentationOptions["onCommitFiberUnmount"]>
1056
- >();
1057
- const postCommitFiberRootListeners = new Set<
1058
- NonNullable<InstrumentationOptions["onPostCommitFiberRoot"]>
1059
- >();
1060
- const scheduleFiberRootListeners = new Set<
1061
- NonNullable<InstrumentationOptions["onScheduleFiberRoot"]>
1062
- >();
985
+ interface InstrumentationSubscription {
986
+ options: InstrumentationOptions;
987
+ }
988
+
989
+ const instrumentationSubscriptions = new Set<InstrumentationSubscription>();
1063
990
 
1064
991
  interface HookDispatchers {
1065
992
  onCommitFiberRoot: ReactDevToolsGlobalHook["onCommitFiberRoot"];
@@ -1069,6 +996,7 @@ interface HookDispatchers {
1069
996
  }
1070
997
 
1071
998
  const hookDispatchers = new WeakMap<ReactDevToolsGlobalHook, Partial<HookDispatchers>>();
999
+ let didSubscribeToHookReplacements = false;
1072
1000
 
1073
1001
  const rootRendererIds = new WeakMap<FiberRoot, number>();
1074
1002
 
@@ -1077,7 +1005,7 @@ const rootRendererIds = new WeakMap<FiberRoot, number>();
1077
1005
  // next instrument() call installs a fresh wrapper over it; a superseded
1078
1006
  // wrapper still forwards the previous chain but skips the listeners so
1079
1007
  // they never fire twice.
1080
- const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void => {
1008
+ const setHookEventDispatchers = (rdtHook: ReactDevToolsGlobalHook): void => {
1081
1009
  const dispatchers = hookDispatchers.get(rdtHook) ?? {};
1082
1010
  hookDispatchers.set(rdtHook, dispatchers);
1083
1011
 
@@ -1092,8 +1020,11 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
1092
1020
  priority,
1093
1021
  didError,
1094
1022
  ) => {
1095
- prevOnCommitFiberRoot?.(rendererID, root, priority, didError);
1023
+ if (prevOnCommitFiberRoot) {
1024
+ prevOnCommitFiberRoot.call(rdtHook, rendererID, root, priority, didError);
1025
+ }
1096
1026
  if (hookDispatchers.get(rdtHook)?.onCommitFiberRoot !== dispatchCommitFiberRoot) return;
1027
+ setReactWorkTagsForFiber(root.current, rdtHook.renderers.get(rendererID));
1097
1028
  const rootMemoizedState = root.current.memoizedState;
1098
1029
  const isUnmounting =
1099
1030
  rootMemoizedState === null ||
@@ -1106,8 +1037,10 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
1106
1037
  _fiberRoots.add(root);
1107
1038
  rootRendererIds.set(root, rendererID);
1108
1039
  }
1109
- for (const listener of commitFiberRootListeners) {
1110
- listener(rendererID, root, priority, didError);
1040
+ for (const { options } of instrumentationSubscriptions) {
1041
+ if (options.onCommitFiberRoot) {
1042
+ options.onCommitFiberRoot(rendererID, root, priority, didError);
1043
+ }
1111
1044
  }
1112
1045
  };
1113
1046
  dispatchers.onCommitFiberRoot = dispatchCommitFiberRoot;
@@ -1123,12 +1056,17 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
1123
1056
  rendererID,
1124
1057
  fiber,
1125
1058
  ) => {
1126
- prevOnCommitFiberUnmount?.(rendererID, fiber);
1059
+ setReactWorkTagsForFiber(fiber, rdtHook.renderers.get(rendererID));
1060
+ if (prevOnCommitFiberUnmount) {
1061
+ prevOnCommitFiberUnmount.call(rdtHook, rendererID, fiber);
1062
+ }
1127
1063
  if (hookDispatchers.get(rdtHook)?.onCommitFiberUnmount !== dispatchCommitFiberUnmount) {
1128
1064
  return;
1129
1065
  }
1130
- for (const listener of commitFiberUnmountListeners) {
1131
- listener(rendererID, fiber);
1066
+ for (const { options } of instrumentationSubscriptions) {
1067
+ if (options.onCommitFiberUnmount) {
1068
+ options.onCommitFiberUnmount(rendererID, fiber);
1069
+ }
1132
1070
  }
1133
1071
  };
1134
1072
  dispatchers.onCommitFiberUnmount = dispatchCommitFiberUnmount;
@@ -1144,12 +1082,16 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
1144
1082
  rendererID,
1145
1083
  root,
1146
1084
  ) => {
1147
- prevOnPostCommitFiberRoot?.(rendererID, root);
1085
+ if (prevOnPostCommitFiberRoot) {
1086
+ prevOnPostCommitFiberRoot.call(rdtHook, rendererID, root);
1087
+ }
1148
1088
  if (hookDispatchers.get(rdtHook)?.onPostCommitFiberRoot !== dispatchPostCommitFiberRoot) {
1149
1089
  return;
1150
1090
  }
1151
- for (const listener of postCommitFiberRootListeners) {
1152
- listener(rendererID, root);
1091
+ for (const { options } of instrumentationSubscriptions) {
1092
+ if (options.onPostCommitFiberRoot) {
1093
+ options.onPostCommitFiberRoot(rendererID, root);
1094
+ }
1153
1095
  }
1154
1096
  };
1155
1097
  dispatchers.onPostCommitFiberRoot = dispatchPostCommitFiberRoot;
@@ -1166,10 +1108,14 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
1166
1108
  root,
1167
1109
  children,
1168
1110
  ) => {
1169
- prevOnScheduleFiberRoot?.(rendererID, root, children);
1111
+ if (prevOnScheduleFiberRoot) {
1112
+ prevOnScheduleFiberRoot.call(rdtHook, rendererID, root, children);
1113
+ }
1170
1114
  if (hookDispatchers.get(rdtHook)?.onScheduleFiberRoot !== dispatchScheduleFiberRoot) return;
1171
- for (const listener of scheduleFiberRootListeners) {
1172
- listener(rendererID, root, children);
1115
+ for (const { options } of instrumentationSubscriptions) {
1116
+ if (options.onScheduleFiberRoot) {
1117
+ options.onScheduleFiberRoot(rendererID, root, children);
1118
+ }
1173
1119
  }
1174
1120
  };
1175
1121
  dispatchers.onScheduleFiberRoot = dispatchScheduleFiberRoot;
@@ -1182,8 +1128,7 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
1182
1128
  * dispatches to a set of listeners, so multiple `instrument` calls compose
1183
1129
  * without stacking patches. Returns an unsubscribe function that removes
1184
1130
  * exactly the handlers this call registered.
1185
- * The returned function is also a `Disposable`, so it composes with other
1186
- * bippy subscriptions through `using`.
1131
+ * The unsubscribe function can also be disposed through `using`.
1187
1132
  * @example
1188
1133
  * const unsubscribe = instrument({
1189
1134
  * onActive() {
@@ -1198,28 +1143,20 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
1198
1143
  export const instrument = (options: InstrumentationOptions): Unsubscribe => {
1199
1144
  const rdtHook = getRDTHook(options.onActive);
1200
1145
 
1146
+ if (!didSubscribeToHookReplacements) {
1147
+ onRDTHookReplace(setHookEventDispatchers);
1148
+ didSubscribeToHookReplacements = true;
1149
+ }
1150
+
1201
1151
  rdtHook._instrumentationSource = options.name ?? BIPPY_INSTRUMENTATION_STRING;
1202
1152
 
1203
- ensureHookDispatchesToListeners(rdtHook);
1204
-
1205
- const {
1206
- onActive,
1207
- onCommitFiberRoot,
1208
- onCommitFiberUnmount,
1209
- onPostCommitFiberRoot,
1210
- onScheduleFiberRoot,
1211
- } = options;
1212
- if (onCommitFiberRoot) commitFiberRootListeners.add(onCommitFiberRoot);
1213
- if (onCommitFiberUnmount) commitFiberUnmountListeners.add(onCommitFiberUnmount);
1214
- if (onPostCommitFiberRoot) postCommitFiberRootListeners.add(onPostCommitFiberRoot);
1215
- if (onScheduleFiberRoot) scheduleFiberRootListeners.add(onScheduleFiberRoot);
1216
-
1217
- return toUnsubscribe(() => {
1218
- if (onActive) _onActiveListeners.delete(onActive);
1219
- if (onCommitFiberRoot) commitFiberRootListeners.delete(onCommitFiberRoot);
1220
- if (onCommitFiberUnmount) commitFiberUnmountListeners.delete(onCommitFiberUnmount);
1221
- if (onPostCommitFiberRoot) postCommitFiberRootListeners.delete(onPostCommitFiberRoot);
1222
- if (onScheduleFiberRoot) scheduleFiberRootListeners.delete(onScheduleFiberRoot);
1153
+ setHookEventDispatchers(rdtHook);
1154
+ const subscription: InstrumentationSubscription = { options };
1155
+ instrumentationSubscriptions.add(subscription);
1156
+
1157
+ return createUnsubscribe(() => {
1158
+ if (options.onActive) _onActiveListeners.delete(options.onActive);
1159
+ instrumentationSubscriptions.delete(subscription);
1223
1160
  });
1224
1161
  };
1225
1162
 
@@ -1231,30 +1168,35 @@ const isFiberPropertyKey = (key: string): boolean =>
1231
1168
  key.startsWith("__reactInternalInstance$") ||
1232
1169
  key.startsWith("__reactFiber");
1233
1170
 
1234
- const getLegacyRootFiber = (hostInstance: Record<string, unknown>): Fiber | null => {
1235
- const reactRootContainer = hostInstance._reactRootContainer;
1236
- if (!isObjectRecord(reactRootContainer)) return null;
1237
- const internalRoot = reactRootContainer._internalRoot;
1238
- if (!isObjectRecord(internalRoot)) return null;
1239
- const current = internalRoot.current;
1240
- if (!isObjectRecord(current)) return null;
1241
- const child = current.child;
1171
+ const getLegacyRootFiber = (hostInstance: object): Fiber | null => {
1172
+ const reactRootContainer = Reflect.get(hostInstance, "_reactRootContainer");
1173
+ if (typeof reactRootContainer !== "object" || reactRootContainer === null) return null;
1174
+ const internalRoot = Reflect.get(reactRootContainer, "_internalRoot");
1175
+ if (typeof internalRoot !== "object" || internalRoot === null) return null;
1176
+ const current = Reflect.get(internalRoot, "current");
1177
+ if (typeof current !== "object" || current === null) return null;
1178
+ const child = Reflect.get(current, "child");
1242
1179
  return isValidFiber(child) ? child : null;
1243
1180
  };
1244
1181
 
1245
- const getInternalInstanceHandle = (hostInstance: Record<string, unknown>): Fiber | null => {
1182
+ const getInternalInstanceHandle = (hostInstance: object): Fiber | null => {
1246
1183
  const internalInstanceHandle =
1247
- hostInstance.__internalInstanceHandle ?? hostInstance._internalInstanceHandle;
1184
+ Reflect.get(hostInstance, "__internalInstanceHandle") ??
1185
+ Reflect.get(hostInstance, "_internalInstanceHandle");
1248
1186
  return isValidFiber(internalInstanceHandle) ? internalInstanceHandle : null;
1249
1187
  };
1250
1188
 
1251
1189
  const getPublicHostInstance = (stateNode: unknown): unknown => {
1252
- if (!isObjectRecord(stateNode)) return stateNode;
1253
- const canonical = stateNode.canonical;
1254
- if (isObjectRecord(canonical) && isObjectRecord(canonical.publicInstance)) {
1255
- return canonical.publicInstance;
1190
+ if (typeof stateNode !== "object" || stateNode === null) return stateNode;
1191
+ const canonical = Reflect.get(stateNode, "canonical");
1192
+ if (typeof canonical === "object" && canonical !== null) {
1193
+ const publicInstance = Reflect.get(canonical, "publicInstance");
1194
+ if (typeof publicInstance === "object" && publicInstance !== null) {
1195
+ return publicInstance;
1196
+ }
1256
1197
  }
1257
- return typeof stateNode._nativeTag === "number" ? stateNode._nativeTag : stateNode;
1198
+ const nativeTag = Reflect.get(stateNode, "_nativeTag");
1199
+ return typeof nativeTag === "number" ? nativeTag : stateNode;
1258
1200
  };
1259
1201
 
1260
1202
  export const getFiberFromHostInstance = <T>(hostInstance: T): Fiber | null => {
@@ -1268,7 +1210,7 @@ export const getFiberFromHostInstance = <T>(hostInstance: T): Fiber | null => {
1268
1210
  }
1269
1211
  }
1270
1212
 
1271
- if (isObjectRecord(hostInstance)) {
1213
+ if (typeof hostInstance === "object" && hostInstance !== null) {
1272
1214
  const legacyRootFiber = getLegacyRootFiber(hostInstance);
1273
1215
  if (legacyRootFiber) return legacyRootFiber;
1274
1216
 
@@ -1276,14 +1218,14 @@ export const getFiberFromHostInstance = <T>(hostInstance: T): Fiber | null => {
1276
1218
  if (internalInstanceHandle) return internalInstanceHandle;
1277
1219
 
1278
1220
  for (const knownKey of knownFiberPropertyKeys) {
1279
- const fiber = hostInstance[knownKey];
1221
+ const fiber = Reflect.get(hostInstance, knownKey);
1280
1222
  if (isValidFiber(fiber)) return fiber;
1281
1223
  }
1282
1224
 
1283
1225
  for (const key of Object.keys(hostInstance)) {
1284
1226
  if (isFiberPropertyKey(key)) {
1285
1227
  knownFiberPropertyKeys.add(key);
1286
- const fiber = hostInstance[key];
1228
+ const fiber = Reflect.get(hostInstance, key);
1287
1229
  if (isValidFiber(fiber)) return fiber;
1288
1230
  }
1289
1231
  }
@@ -1307,6 +1249,17 @@ export const getFiberFromHostInstance = <T>(hostInstance: T): Fiber | null => {
1307
1249
  return null;
1308
1250
  };
1309
1251
 
1310
- export * from "./rdt-hook.js";
1311
- export * from "./unsubscribe.js";
1312
- export type * from "./types.js";
1252
+ export {
1253
+ BIPPY_INSTRUMENTATION_STRING,
1254
+ _onActiveListeners,
1255
+ _renderers,
1256
+ getRDTHook,
1257
+ hasRDTHook,
1258
+ installRDTHook,
1259
+ isRealReactDevtools,
1260
+ onRendererInject,
1261
+ patchRDTHook,
1262
+ version,
1263
+ } from "./rdt-hook.js";
1264
+ export type { Unsubscribe } from "./rdt-hook.js";
1265
+ export type * from "./react-internals/index.js";