bippy 0.6.1-dev.d7876ea → 0.6.1-dev.f9a65c0

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 (50) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +211 -445
  3. package/dist/core.cjs +1 -1
  4. package/dist/core.d.cts +25 -75
  5. package/dist/core.d.ts +25 -75
  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/errors.d.cts +33 -59
  12. package/dist/errors.d.ts +33 -59
  13. package/dist/index.cjs +1 -1
  14. package/dist/index.d.cts +14 -3
  15. package/dist/index.d.ts +14 -3
  16. package/dist/index.js +1 -1
  17. package/dist/install-hook-only.cjs +1 -1
  18. package/dist/install-hook-only.d.cts +8 -0
  19. package/dist/install-hook-only.d.ts +8 -0
  20. package/dist/install-hook-only.js +1 -1
  21. package/dist/rdt-hook.cjs +1 -1
  22. package/dist/rdt-hook.js +1 -1
  23. package/dist/source.cjs +12 -13
  24. package/dist/source.d.cts +85 -77
  25. package/dist/source.d.ts +85 -77
  26. package/dist/source.js +12 -13
  27. package/package.json +6 -3
  28. package/src/core.ts +146 -579
  29. package/src/errors.ts +0 -65
  30. package/src/index.ts +1 -0
  31. package/src/install-hook-only.ts +2 -2
  32. package/src/rdt-hook.ts +39 -45
  33. package/src/{generated/react-work-tags.js → react-internals/generated/react-work-tags.ts} +85 -7
  34. package/src/{react-internals.ts → react-internals/index.ts} +6 -4
  35. package/src/{semver.ts → react-internals/semver.ts} +2 -0
  36. package/src/{types.ts → react-internals/types.ts} +1 -84
  37. package/src/react.ts +76 -0
  38. package/src/source/get-display-name-from-source.ts +44 -40
  39. package/src/source/get-source.ts +42 -26
  40. package/src/source/index.ts +2 -0
  41. package/src/source/inspect-hooks.ts +74 -96
  42. package/src/source/owner-stack.ts +66 -91
  43. package/src/source/parse-hook-names.ts +14 -53
  44. package/src/source/parse-stack.ts +14 -31
  45. package/src/source/renderer-dispatchers.ts +30 -0
  46. package/src/source/symbolication.ts +421 -108
  47. package/dist/index.iife.js +0 -9
  48. package/dist/install-hook-only.iife.js +0 -9
  49. package/src/generated/react-work-tags.d.ts +0 -261
  50. package/src/unsubscribe.ts +0 -17
@@ -1,8 +1,29 @@
1
- import { Fiber } from "../types.js";
1
+ import type { Fiber } from "../react-internals/index.js";
2
2
  import { getDisplayName } from "../core.js";
3
- import { getParentStack } from "./owner-stack.js";
4
- import { getSourceFromSourceMap, getSourceMap } from "./symbolication.js";
5
- import { StackFrame } from "./parse-stack.js";
3
+ import { getDefinitionFrameFromOwnedChild, getRawParentStack } from "./owner-stack.js";
4
+ import {
5
+ getSourceContentFromSourceMap,
6
+ getSourceFromSourceMap,
7
+ getSourceMap,
8
+ type SourceFetch,
9
+ } from "./symbolication.js";
10
+ import type { StackFrame } from "./parse-stack.js";
11
+
12
+ const COMPONENT_DECLARATION_PATTERNS = [
13
+ /(?:^|export\s+)(?:const|let|var)\s+(\w+)\s*=/,
14
+ /(?:^|export\s+)function\s+(\w+)/,
15
+ /(?:^|export\s+)class\s+(\w+)/,
16
+ ];
17
+
18
+ const findComponentDeclarationOnLine = (line: string): string | null => {
19
+ for (const pattern of COMPONENT_DECLARATION_PATTERNS) {
20
+ const match = line.match(pattern);
21
+ if (match?.[1]) return match[1];
22
+ }
23
+ return null;
24
+ };
25
+
26
+ const MAX_DECLARATION_LINE_DISTANCE = 5;
6
27
 
7
28
  const extractComponentNameFromSource = (
8
29
  sourceContent: string,
@@ -15,27 +36,16 @@ const extractComponentNameFromSource = (
15
36
  return null;
16
37
  }
17
38
 
18
- const startLine = Math.max(0, targetLineIndex - 5);
19
- const endLine = Math.min(lines.length, targetLineIndex + 5);
20
- const contextLines = lines.slice(startLine, endLine).join("\n");
21
-
22
- const arrowFunctionPattern = /(?:^|export\s+)(?:const|let|var)\s+(\w+)\s*=/m;
23
- const functionPattern = /(?:^|export\s+)function\s+(\w+)/m;
24
- const classPattern = /(?:^|export\s+)class\s+(\w+)/m;
25
-
26
- const arrowMatch = contextLines.match(arrowFunctionPattern);
27
- if (arrowMatch?.[1]) {
28
- return arrowMatch[1];
29
- }
30
-
31
- const functionMatch = contextLines.match(functionPattern);
32
- if (functionMatch?.[1]) {
33
- return functionMatch[1];
34
- }
35
-
36
- const classMatch = contextLines.match(classPattern);
37
- if (classMatch?.[1]) {
38
- return classMatch[1];
39
+ for (let lineDistance = 0; lineDistance <= MAX_DECLARATION_LINE_DISTANCE; lineDistance++) {
40
+ const lineIndexes =
41
+ lineDistance === 0
42
+ ? [targetLineIndex]
43
+ : [targetLineIndex - lineDistance, targetLineIndex + lineDistance];
44
+ for (const lineIndex of lineIndexes) {
45
+ if (lineIndex < 0 || lineIndex >= lines.length) continue;
46
+ const declarationName = findComponentDeclarationOnLine(lines[lineIndex]);
47
+ if (declarationName) return declarationName;
48
+ }
39
49
  }
40
50
 
41
51
  return null;
@@ -44,10 +54,11 @@ const extractComponentNameFromSource = (
44
54
  export const getDisplayNameFromSource = async (
45
55
  fiber: Fiber,
46
56
  cache = true,
47
- fetchFn?: (url: string) => Promise<Response>,
57
+ fetchFn?: SourceFetch,
48
58
  ): Promise<string | null> => {
49
- const parentStackFrames = await getParentStack(fiber, cache, fetchFn);
50
- const stackFrame = parentStackFrames.filter((innerFrame) => innerFrame.fileName)[0];
59
+ const stackFrame =
60
+ getDefinitionFrameFromOwnedChild(fiber) ??
61
+ getRawParentStack(fiber).find((innerFrame) => innerFrame.fileName);
51
62
 
52
63
  if (!stackFrame?.fileName) {
53
64
  return getDisplayName(fiber.type);
@@ -73,21 +84,14 @@ export const getDisplayNameFromSource = async (
73
84
  return getDisplayName(fiber.type);
74
85
  }
75
86
 
76
- if (!bundleSourceMap.sourcesContent) {
77
- return getDisplayName(fiber.type);
78
- }
79
-
80
- const sourceIndex = bundleSourceMap.sources.indexOf(source.fileName);
81
- if (sourceIndex === -1 || !bundleSourceMap.sourcesContent[sourceIndex]) {
82
- return getDisplayName(fiber.type);
83
- }
84
-
85
- const sourceContent = bundleSourceMap.sourcesContent[sourceIndex];
86
- const extractedName = extractComponentNameFromSource(sourceContent, source.lineNumber);
87
+ const sourceContent = getSourceContentFromSourceMap(bundleSourceMap, source.fileName);
88
+ const extractedName = sourceContent
89
+ ? extractComponentNameFromSource(sourceContent, source.lineNumber)
90
+ : null;
87
91
 
88
92
  if (extractedName) {
89
93
  return extractedName;
90
94
  }
91
95
 
92
- return getDisplayName(fiber.type);
96
+ return source.functionName ?? getDisplayName(fiber.type);
93
97
  };
@@ -1,6 +1,7 @@
1
- import { Fiber } from "../types.js";
1
+ import type { Fiber } from "../react-internals/index.js";
2
+ import { getDisplayName } from "../core.js";
2
3
 
3
- import { FiberSource } from "./types.js";
4
+ import type { FiberSource } from "./types.js";
4
5
  import {
5
6
  SCHEME_REGEX,
6
7
  INTERNAL_SCHEME_PREFIXES,
@@ -12,8 +13,13 @@ import {
12
13
  } from "./constants.js";
13
14
  import { getDefinitionFrameFromOwnedChild, getParentStack, hasDebugStack } from "./owner-stack.js";
14
15
  import { parseDebugStack } from "./parse-debug-stack.js";
15
- import { StackFrame } from "./parse-stack.js";
16
- import { symbolicateStack } from "./symbolication.js";
16
+ import { parseStack, type StackFrame } from "./parse-stack.js";
17
+ import {
18
+ getSourceFromSourceMapByFunctionName,
19
+ getSourceMap,
20
+ symbolicateStack,
21
+ type SourceFetch,
22
+ } from "./symbolication.js";
17
23
 
18
24
  export const hasDebugSource = (
19
25
  fiber: Fiber,
@@ -26,10 +32,8 @@ export const hasDebugSource = (
26
32
  }
27
33
  return (
28
34
  typeof debugSource === "object" &&
29
- debugSource !== null &&
30
- "fileName" in debugSource &&
31
35
  typeof debugSource.fileName === "string" &&
32
- "lineNumber" in debugSource &&
36
+ debugSource.fileName !== "(native)" &&
33
37
  typeof debugSource.lineNumber === "number"
34
38
  );
35
39
  };
@@ -63,6 +67,30 @@ const getUsageFrameFromDebugStack = (fiber: Fiber): StackFrame | null => {
63
67
  return null;
64
68
  };
65
69
 
70
+ const getSourceByComponentName = async (
71
+ fiber: Fiber,
72
+ cache: boolean,
73
+ fetchFn: SourceFetch | undefined,
74
+ ): Promise<FiberSource | null> => {
75
+ const functionName = getDisplayName(fiber.type);
76
+ if (!functionName) return null;
77
+
78
+ const runtimeStackFrames = parseStack(new Error().stack ?? "");
79
+ const visitedFileNames = new Set<string>();
80
+ for (const stackFrame of runtimeStackFrames) {
81
+ const fileName = stackFrame.fileName;
82
+ if (!fileName || visitedFileNames.has(fileName)) {
83
+ continue;
84
+ }
85
+ visitedFileNames.add(fileName);
86
+ const sourceMap = await getSourceMap(fileName, cache, fetchFn);
87
+ if (!sourceMap) continue;
88
+ const source = getSourceFromSourceMapByFunctionName(sourceMap, functionName);
89
+ if (source && !source.isIgnoreListed) return toFiberSource(source);
90
+ }
91
+ return null;
92
+ };
93
+
66
94
  /**
67
95
  * Returns the source of where the component is used. Available only in dev, for composite {@link Fiber}s.
68
96
  *
@@ -92,11 +120,10 @@ const getUsageFrameFromDebugStack = (fiber: Fiber): StackFrame | null => {
92
120
  export const getSource = async (
93
121
  fiber: Fiber,
94
122
  cache = true,
95
- fetchFn?: (url: string) => Promise<Response>,
123
+ fetchFn?: SourceFetch,
96
124
  ): Promise<FiberSource | null> => {
97
125
  if (hasDebugSource(fiber)) {
98
- const debugSource = fiber._debugSource;
99
- return debugSource || null;
126
+ return fiber._debugSource;
100
127
  }
101
128
 
102
129
  const debugStackFrame =
@@ -115,16 +142,11 @@ export const getSource = async (
115
142
  return toFiberSource(stackFrame);
116
143
  }
117
144
  }
118
- return null;
145
+ return getSourceByComponentName(fiber, cache, fetchFn);
119
146
  };
120
147
 
121
148
  const getPathSegmentCount = (path: string): number => path.split("/").filter(Boolean).length;
122
149
 
123
- const getFirstPathSegment = (path: string): string | null => {
124
- const segments = path.split("/").filter(Boolean);
125
- return segments[0] ?? null;
126
- };
127
-
128
150
  const stripSingleBasePathPrefix = (path: string): string => {
129
151
  const firstSlashIndex = path.indexOf("/", 1);
130
152
  if (firstSlashIndex === -1) {
@@ -141,15 +163,12 @@ const stripSingleBasePathPrefix = (path: string): string => {
141
163
  return path;
142
164
  }
143
165
 
144
- if (getPathSegmentCount(remainderPath) < 2) {
145
- return path;
146
- }
147
-
148
- const firstRemainderSegment = getFirstPathSegment(remainderPath);
149
- if (!firstRemainderSegment) {
166
+ const remainderSegments = remainderPath.split("/").filter(Boolean);
167
+ if (remainderSegments.length < 2) {
150
168
  return path;
151
169
  }
152
170
 
171
+ const firstRemainderSegment = remainderSegments[0];
153
172
  if (firstRemainderSegment.startsWith("@")) {
154
173
  return path;
155
174
  }
@@ -180,9 +199,6 @@ export const normalizeFileName = (fileName: string): string => {
180
199
  const parsedUrl = new URL(normalizedFileName);
181
200
  normalizedFileName = parsedUrl.pathname;
182
201
  } catch {}
183
- }
184
-
185
- if (isHttpUrl) {
186
202
  normalizedFileName = stripSingleBasePathPrefix(normalizedFileName);
187
203
  }
188
204
 
@@ -215,7 +231,7 @@ export const normalizeFileName = (fileName: string): string => {
215
231
  }
216
232
  }
217
233
 
218
- if (!isWindowsDrivePath && SCHEME_REGEX.test(normalizedFileName)) {
234
+ if (!isWindowsDrivePath) {
219
235
  const schemeMatch = normalizedFileName.match(SCHEME_REGEX);
220
236
  if (schemeMatch) {
221
237
  normalizedFileName = normalizedFileName.slice(schemeMatch[0].length);
@@ -1,7 +1,9 @@
1
1
  export { formatOwnerStack, getOwnerStack, getParentStack, hasDebugStack } from "./owner-stack.js";
2
2
  export { getSource, isSourceFile, normalizeFileName } from "./get-source.js";
3
3
  export {
4
+ getSourceContentFromSourceMap,
4
5
  getSourceFromSourceMap,
6
+ getSourceFromSourceMapByFunctionName,
5
7
  getSourceMap,
6
8
  symbolicateStack,
7
9
  type DecodedSourceMapSection,
@@ -4,15 +4,19 @@ import type {
4
4
  MemoizedState,
5
5
  ReactContext,
6
6
  RendererDispatcherRef,
7
- } from "../types.js";
7
+ } from "../react-internals/index.js";
8
8
  import {
9
9
  BippyHookInspectionError,
10
10
  BippyHookRenderError,
11
11
  BippyUnsupportedHookError,
12
12
  } from "../errors.js";
13
- import { getReactWorkTagsForFiber } from "../react-internals.js";
13
+ import { getReactWorkTagsForFiber } from "../react-internals/index.js";
14
14
  import { parseStack, type StackFrame } from "./parse-stack.js";
15
- import { getRDTHook, _renderers } from "../rdt-hook.js";
15
+ import {
16
+ getRendererDispatcherRefs,
17
+ readDispatcher,
18
+ writeDispatcher,
19
+ } from "./renderer-dispatchers.js";
16
20
 
17
21
  const REACT_CONTEXT_TYPE = Symbol.for("react.context");
18
22
  const REACT_MEMO_CACHE_SENTINEL = Symbol.for("react.memo_cache_sentinel");
@@ -77,7 +81,7 @@ let currentThenableIndex = 0;
77
81
  let currentThenableState: unknown[] | null = null;
78
82
 
79
83
  const SuspenseException: unknown = new Error(
80
- "Suspense Exception: This is not a real error! It's an implementation detail of `use` to interrupt the current render.",
84
+ "Suspense interrupted this render. This error is an internal implementation detail of `use`.",
81
85
  );
82
86
 
83
87
  const parseErrorStack = (error: Error): StackFrame[] =>
@@ -89,28 +93,31 @@ const nextHook = (): MemoizedState | null => {
89
93
  return hook;
90
94
  };
91
95
 
92
- const isObjectRecord = (value: unknown): value is Record<string, unknown> =>
93
- typeof value === "object" && value !== null;
94
-
95
96
  const isInspectableThenable = (value: unknown): value is InspectableThenable =>
96
- isObjectRecord(value) && typeof value.then === "function";
97
+ typeof value === "object" &&
98
+ value !== null &&
99
+ "then" in value &&
100
+ typeof value.then === "function";
97
101
 
98
102
  const isInspectableRef = (value: unknown): value is InspectableRef =>
99
- isObjectRecord(value) && "current" in value;
103
+ typeof value === "object" && value !== null && "current" in value;
100
104
 
101
105
  const isReactContext = (value: unknown): value is ReactContext<unknown> =>
102
- isObjectRecord(value) && "_currentValue" in value;
106
+ typeof value === "object" && value !== null && "_currentValue" in value;
103
107
 
104
108
  const isContextDependency = (value: unknown): value is ContextDependency<unknown> =>
105
- isObjectRecord(value) && "context" in value && "next" in value;
109
+ typeof value === "object" && value !== null && "context" in value && "next" in value;
106
110
 
107
111
  const isForwardRefRenderType = (value: unknown): value is ForwardRefRenderType =>
108
- isObjectRecord(value) && typeof value.render === "function";
112
+ typeof value === "object" &&
113
+ value !== null &&
114
+ "render" in value &&
115
+ typeof value.render === "function";
109
116
 
110
117
  const readContext = (context: InspectableReactContext): unknown => {
111
118
  if (currentFiber === null) return context._currentValue;
112
119
  if (currentContextDependency === null) {
113
- throw new BippyHookInspectionError("Context reads do not line up with context dependencies.");
120
+ throw new BippyHookInspectionError("Context reads don’t match context dependencies.");
114
121
  }
115
122
  if (Object.hasOwn(currentContextDependency, "memoizedValue")) {
116
123
  const value = currentContextDependency.memoizedValue;
@@ -120,27 +127,6 @@ const readContext = (context: InspectableReactContext): unknown => {
120
127
  return context._currentValue;
121
128
  };
122
129
 
123
- const getDispatcherRef = (): RendererDispatcherRef | null => {
124
- const rdtHook = getRDTHook();
125
- const allRenderers = [..._renderers, ...rdtHook.renderers.values()];
126
- for (const renderer of allRenderers) {
127
- const ref = renderer.currentDispatcherRef;
128
- if (ref) return ref;
129
- }
130
- return null;
131
- };
132
-
133
- const getDispatcherFromRef = (ref: RendererDispatcherRef): unknown =>
134
- "H" in ref ? ref.H : ref.current;
135
-
136
- const setDispatcherOnRef = (ref: RendererDispatcherRef, dispatcher: unknown): void => {
137
- if ("H" in ref) {
138
- ref.H = dispatcher;
139
- } else {
140
- ref.current = dispatcher;
141
- }
142
- };
143
-
144
130
  const pushHookLogEntry = (
145
131
  primitive: string,
146
132
  value: unknown,
@@ -157,7 +143,7 @@ const pushHookLogEntry = (
157
143
  };
158
144
 
159
145
  const dispatcherUse = (usable: unknown): unknown => {
160
- if (isObjectRecord(usable)) {
146
+ if (typeof usable === "object" && usable !== null) {
161
147
  if (isInspectableThenable(usable)) {
162
148
  const cachedThenable =
163
149
  currentThenableState !== null && currentThenableIndex < currentThenableState.length
@@ -176,7 +162,7 @@ const dispatcherUse = (usable: unknown): unknown => {
176
162
  pushHookLogEntry("Unresolved", thenable, "Use");
177
163
  throw SuspenseException;
178
164
  }
179
- if (usable.$$typeof === REACT_CONTEXT_TYPE && "_currentValue" in usable) {
165
+ if ("$$typeof" in usable && usable.$$typeof === REACT_CONTEXT_TYPE && isReactContext(usable)) {
180
166
  const context: InspectableReactContext = {
181
167
  _currentValue: usable._currentValue,
182
168
  displayName: typeof usable.displayName === "string" ? usable.displayName : undefined,
@@ -186,7 +172,7 @@ const dispatcherUse = (usable: unknown): unknown => {
186
172
  return value;
187
173
  }
188
174
  }
189
- throw new BippyHookInspectionError("An unsupported type was passed to use(): " + String(usable));
175
+ throw new BippyHookInspectionError("use() received an unsupported value: " + String(usable));
190
176
  };
191
177
 
192
178
  const dispatcherUseContext = (context: InspectableReactContext): unknown => {
@@ -251,10 +237,7 @@ const dispatcherUseEffect = (create: () => void): void => {
251
237
 
252
238
  const dispatcherUseImperativeHandle = (ref: unknown): void => {
253
239
  nextHook();
254
- let instance: unknown;
255
- if (ref !== null && typeof ref === "object" && "current" in ref) {
256
- instance = ref.current;
257
- }
240
+ const instance = isInspectableRef(ref) ? ref.current : undefined;
258
241
  pushHookLogEntry("ImperativeHandle", instance, "ImperativeHandle");
259
242
  };
260
243
 
@@ -380,15 +363,8 @@ const createActionStateDispatcher =
380
363
  const hook = nextHook();
381
364
  nextHook();
382
365
  nextHook();
383
- const stackError = new Error();
384
366
  const { value, error } = inspectActionStateHook(hook, initialState);
385
- hookLog.push({
386
- displayName: null,
387
- primitive,
388
- stackError,
389
- value,
390
- dispatcherHookName: primitive,
391
- });
367
+ pushHookLogEntry(primitive, value, primitive);
392
368
  if (error !== null) throw error;
393
369
  return [value, () => {}, false];
394
370
  };
@@ -436,15 +412,12 @@ const dispatcher = {
436
412
  useEffectEvent: dispatcherUseEffectEvent,
437
413
  };
438
414
 
439
- const dispatcherProxy =
440
- typeof Proxy === "undefined"
441
- ? dispatcher
442
- : new Proxy(dispatcher, {
443
- get(target, propertyName: string) {
444
- if (Object.hasOwn(target, propertyName)) return Reflect.get(target, propertyName);
445
- throw new BippyUnsupportedHookError("Missing method in Dispatcher: " + propertyName);
446
- },
447
- });
415
+ const dispatcherProxy = new Proxy(dispatcher, {
416
+ get(target, propertyName: string) {
417
+ if (Object.hasOwn(target, propertyName)) return Reflect.get(target, propertyName);
418
+ throw new BippyUnsupportedHookError("The React dispatcher is missing method " + propertyName);
419
+ },
420
+ });
448
421
 
449
422
  const getPrimitiveStackCache = (): Map<string, StackFrame[]> => {
450
423
  if (primitiveStackCache !== null) return primitiveStackCache;
@@ -457,38 +430,34 @@ const getPrimitiveStackCache = (): Map<string, StackFrame[]> => {
457
430
  dispatcher.useState(null);
458
431
  dispatcher.useReducer((state: unknown) => state, null);
459
432
  dispatcher.useRef(null);
460
- if (typeof dispatcher.useCacheRefresh === "function") dispatcher.useCacheRefresh();
433
+ dispatcher.useCacheRefresh();
461
434
  dispatcher.useLayoutEffect(() => {});
462
435
  dispatcher.useInsertionEffect(() => {});
463
436
  dispatcher.useEffect(() => {});
464
- dispatcher.useImperativeHandle(undefined, () => null);
437
+ dispatcher.useImperativeHandle(undefined);
465
438
  dispatcher.useDebugValue(null);
466
439
  dispatcher.useCallback(() => {});
467
440
  dispatcher.useTransition();
468
441
  dispatcher.useSyncExternalStore(
469
442
  () => () => {},
470
443
  () => null,
471
- () => null,
472
444
  );
473
445
  dispatcher.useDeferredValue(null);
474
446
  dispatcher.useMemo(() => null);
475
- dispatcher.useOptimistic(null, (state: unknown) => state);
447
+ dispatcher.useOptimistic(null);
476
448
  dispatcher.useFormState((state: unknown) => state, null);
477
449
  dispatcher.useActionState((state: unknown) => state, null);
478
450
  dispatcher.useHostTransitionStatus();
479
- if (typeof dispatcher.useMemoCache === "function") dispatcher.useMemoCache(0);
480
- if (typeof dispatcher.use === "function") {
481
- dispatcher.use({ $$typeof: REACT_CONTEXT_TYPE, _currentValue: null });
482
- const fulfilledPromise = Promise.resolve(null);
483
- Reflect.set(fulfilledPromise, "status", "fulfilled");
484
- Reflect.set(fulfilledPromise, "value", null);
485
- dispatcher.use(fulfilledPromise);
486
- try {
487
- dispatcher.use(new Promise<never>(() => {}));
488
- } catch {}
489
- }
451
+ dispatcher.use({ $$typeof: REACT_CONTEXT_TYPE, _currentValue: null });
452
+ const fulfilledPromise = Promise.resolve(null);
453
+ Reflect.set(fulfilledPromise, "status", "fulfilled");
454
+ Reflect.set(fulfilledPromise, "value", null);
455
+ dispatcher.use(fulfilledPromise);
456
+ try {
457
+ dispatcher.use(new Promise<never>(() => {}));
458
+ } catch {}
490
459
  dispatcher.useId();
491
- if (typeof dispatcher.useEffectEvent === "function") dispatcher.useEffectEvent(() => {});
460
+ dispatcher.useEffectEvent(() => {});
492
461
  } finally {
493
462
  capturedHookLog = hookLog;
494
463
  hookLog = [];
@@ -719,7 +688,10 @@ const setupContexts = (contextMap: Map<ReactContext<unknown>, unknown>, fiber: F
719
688
  while (current) {
720
689
  if (current.tag === getReactWorkTagsForFiber(current).ContextProvider) {
721
690
  const providerType = current.type;
722
- const nestedContext = isObjectRecord(providerType) ? providerType._context : undefined;
691
+ const nestedContext =
692
+ typeof providerType === "object" && providerType !== null && "_context" in providerType
693
+ ? providerType._context
694
+ : undefined;
723
695
  const context = isReactContext(nestedContext)
724
696
  ? nestedContext
725
697
  : isReactContext(providerType)
@@ -743,7 +715,7 @@ const restoreContexts = (contextMap: Map<ReactContext<unknown>, unknown>): void
743
715
  const handleRenderFunctionError = (error: unknown): void => {
744
716
  if (error === SuspenseException) return;
745
717
  if (error instanceof BippyUnsupportedHookError) throw error;
746
- throw new BippyHookRenderError("Error rendering inspected component", error);
718
+ throw new BippyHookRenderError("Bippy couldn’t render the inspected component", error);
747
719
  };
748
720
 
749
721
  const resolveDefaultProps = (
@@ -754,13 +726,14 @@ const resolveDefaultProps = (
754
726
  component &&
755
727
  typeof component === "object" &&
756
728
  "defaultProps" in component &&
757
- isObjectRecord(component.defaultProps)
729
+ typeof component.defaultProps === "object" &&
730
+ component.defaultProps !== null
758
731
  ) {
759
732
  const props = { ...baseProps };
760
733
  const defaultProps = component.defaultProps;
761
- for (const propName in defaultProps) {
734
+ for (const [propName, value] of Object.entries(defaultProps)) {
762
735
  if (props[propName] === undefined) {
763
- props[propName] = defaultProps[propName];
736
+ props[propName] = value;
764
737
  }
765
738
  }
766
739
  return props;
@@ -791,8 +764,8 @@ const performDispatcherInspection = (
791
764
  dispatcherRef: RendererDispatcherRef,
792
765
  renderFn: () => void,
793
766
  ): HooksTree => {
794
- const previousDispatcher = getDispatcherFromRef(dispatcherRef);
795
- setDispatcherOnRef(dispatcherRef, dispatcherProxy);
767
+ const previousDispatcher = readDispatcher(dispatcherRef);
768
+ writeDispatcher(dispatcherRef, dispatcherProxy);
796
769
 
797
770
  let capturedHookLog: HookLogEntry[] = [];
798
771
  let ancestorStackError: Error | undefined;
@@ -805,7 +778,7 @@ const performDispatcherInspection = (
805
778
  } finally {
806
779
  capturedHookLog = hookLog;
807
780
  hookLog = [];
808
- setDispatcherOnRef(dispatcherRef, previousDispatcher);
781
+ writeDispatcher(dispatcherRef, previousDispatcher);
809
782
  }
810
783
 
811
784
  const rootStack = ancestorStackError !== undefined ? parseErrorStack(ancestorStackError) : [];
@@ -813,33 +786,40 @@ const performDispatcherInspection = (
813
786
  };
814
787
 
815
788
  const requireDispatcherRef = (): RendererDispatcherRef => {
816
- const dispatcherRef = getDispatcherRef();
789
+ const dispatcherRef = getRendererDispatcherRefs()[0];
817
790
  if (!dispatcherRef) {
818
791
  throw new BippyHookInspectionError(
819
- "No React renderer found. Make sure React is loaded and bippy's hook is installed.",
792
+ "Bippy couldn’t find a React renderer. Load React and install Bippy’s hook.",
820
793
  );
821
794
  }
822
795
  return dispatcherRef;
823
796
  };
824
797
 
825
798
  const resolveContextDependency = (fiber: Fiber): void => {
799
+ const legacyDependenciesKey = ["dependencies_old", "dependencies_new"].find((key) =>
800
+ Object.hasOwn(fiber, key),
801
+ );
826
802
  if (Object.hasOwn(fiber, "dependencies")) {
827
803
  const dependencies = fiber.dependencies;
828
804
  currentContextDependency = dependencies !== null ? dependencies.firstContext : null;
829
- } else if (Object.hasOwn(fiber, "dependencies_old")) {
830
- const dependencies: unknown = Reflect.get(fiber, "dependencies_old");
831
- const firstContext = isObjectRecord(dependencies) ? dependencies.firstContext : null;
832
- currentContextDependency = isContextDependency(firstContext) ? firstContext : null;
833
- } else if (Object.hasOwn(fiber, "dependencies_new")) {
834
- const dependencies: unknown = Reflect.get(fiber, "dependencies_new");
835
- const firstContext = isObjectRecord(dependencies) ? dependencies.firstContext : null;
805
+ } else if (legacyDependenciesKey) {
806
+ const dependencies: unknown = Reflect.get(fiber, legacyDependenciesKey);
807
+ const firstContext =
808
+ typeof dependencies === "object" && dependencies !== null && "firstContext" in dependencies
809
+ ? dependencies.firstContext
810
+ : null;
836
811
  currentContextDependency = isContextDependency(firstContext) ? firstContext : null;
837
812
  } else if (Object.hasOwn(fiber, "contextDependencies")) {
838
813
  const contextDependencies: unknown = Reflect.get(fiber, "contextDependencies");
839
- const firstContext = isObjectRecord(contextDependencies) ? contextDependencies.first : null;
814
+ const firstContext =
815
+ typeof contextDependencies === "object" &&
816
+ contextDependencies !== null &&
817
+ "first" in contextDependencies
818
+ ? contextDependencies.first
819
+ : null;
840
820
  currentContextDependency = isContextDependency(firstContext) ? firstContext : null;
841
821
  } else {
842
- throw new BippyHookInspectionError("Unsupported React version.");
822
+ throw new BippyHookInspectionError("Bippy doesn’t support this React version.");
843
823
  }
844
824
  };
845
825
 
@@ -852,9 +832,7 @@ export const getFiberHooks = (fiber: Fiber): HooksTree => {
852
832
  fiber.tag !== workTags.SimpleMemoComponent &&
853
833
  fiber.tag !== workTags.ForwardRef
854
834
  ) {
855
- throw new BippyHookInspectionError(
856
- "Unknown Fiber. Needs to be a function component to inspect hooks.",
857
- );
835
+ throw new BippyHookInspectionError("Hook inspection requires a function component Fiber.");
858
836
  }
859
837
 
860
838
  getPrimitiveStackCache();