react-render-detective 0.1.2 → 0.2.0

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 (46) hide show
  1. package/README.md +29 -3
  2. package/dist/babel.cjs +9 -0
  3. package/dist/babel.cjs.map +1 -0
  4. package/dist/babel.d.cts +38 -0
  5. package/dist/babel.d.ts +38 -0
  6. package/dist/babel.js +3 -0
  7. package/dist/babel.js.map +1 -0
  8. package/dist/{chunk-SKLDIIL6.cjs → chunk-6YPJQ7M5.cjs} +8 -4
  9. package/dist/chunk-6YPJQ7M5.cjs.map +1 -0
  10. package/dist/chunk-BPNLNIHU.cjs +162 -0
  11. package/dist/chunk-BPNLNIHU.cjs.map +1 -0
  12. package/dist/{chunk-D4G4D5AC.js → chunk-LSIXHWQD.js} +7 -4
  13. package/dist/chunk-LSIXHWQD.js.map +1 -0
  14. package/dist/{chunk-27IWHWSM.js → chunk-MSISKPST.js} +8 -4
  15. package/dist/chunk-MSISKPST.js.map +1 -0
  16. package/dist/chunk-PSCFSJ65.js +160 -0
  17. package/dist/chunk-PSCFSJ65.js.map +1 -0
  18. package/dist/{chunk-U3SAWFCT.cjs → chunk-QQ4VARSD.cjs} +7 -4
  19. package/dist/chunk-QQ4VARSD.cjs.map +1 -0
  20. package/dist/core.cjs +18 -18
  21. package/dist/core.d.cts +5 -3
  22. package/dist/core.d.ts +5 -3
  23. package/dist/core.js +1 -1
  24. package/dist/index.cjs +41 -35
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.cts +14 -2
  27. package/dist/index.d.ts +14 -2
  28. package/dist/index.js +13 -7
  29. package/dist/index.js.map +1 -1
  30. package/dist/overlay.cjs +6 -6
  31. package/dist/overlay.cjs.map +1 -1
  32. package/dist/overlay.js +3 -3
  33. package/dist/overlay.js.map +1 -1
  34. package/dist/{types-CY61yfhK.d.cts → types-BWVaQPHb.d.cts} +7 -0
  35. package/dist/{types-CY61yfhK.d.ts → types-BWVaQPHb.d.ts} +7 -0
  36. package/dist/vite.cjs +48 -0
  37. package/dist/vite.cjs.map +1 -0
  38. package/dist/vite.d.cts +19 -0
  39. package/dist/vite.d.ts +19 -0
  40. package/dist/vite.js +43 -0
  41. package/dist/vite.js.map +1 -0
  42. package/package.json +46 -157
  43. package/dist/chunk-27IWHWSM.js.map +0 -1
  44. package/dist/chunk-D4G4D5AC.js.map +0 -1
  45. package/dist/chunk-SKLDIIL6.cjs.map +0 -1
  46. package/dist/chunk-U3SAWFCT.cjs.map +0 -1
@@ -44,7 +44,8 @@ function explainEvents(component, events) {
44
44
  if (topProp && topProp.share >= 0.5) {
45
45
  headline = `${pct(topProp.share)} of updates followed \`${topProp.key}\` changing by reference while its contents stayed the same.`;
46
46
  const via = latest.parent?.name;
47
- nextStep = `Trace \`${topProp.key}\` back from ${via ? `${via}, which passes it to ${component}` : component}, and stabilise it where it is created${topProp.valueType === "function" ? " (useCallback, or hoist it out of the component)" : " (useMemo, or pass the primitive fields you use)"}.`;
47
+ const viaSource = latest.parent?.source ? ` (${latest.parent.source})` : "";
48
+ nextStep = `Trace \`${topProp.key}\` back from ${via ? `${via}${viaSource}, which passes it to ${component}` : component}, and stabilise it where it is created${topProp.valueType === "function" ? " (useCallback, or hoist it out of the component)" : " (useMemo, or pass the primitive fields you use)"}.`;
48
49
  confidence = "high";
49
50
  } else if (top && top.reason === "parent" && top.share >= 0.5) {
50
51
  headline = `${pct(top.share)} of updates were parent propagation with identical props.`;
@@ -79,6 +80,8 @@ function explainEvents(component, events) {
79
80
  }
80
81
  return {
81
82
  component,
83
+ source: latest.component.source,
84
+ parent: latest.parent ? { name: latest.parent.name, source: latest.parent.source } : void 0,
82
85
  renders: mine.length,
83
86
  mounts: mine.length - updates.length,
84
87
  breakdown,
@@ -95,7 +98,7 @@ function explainEvents(component, events) {
95
98
  }
96
99
  function formatExplanation(e) {
97
100
  const lines = [
98
- `${e.component}`,
101
+ e.source ? `${e.component} ${e.source}` : `${e.component}`,
99
102
  "",
100
103
  `${e.renders} recorded render${e.renders === 1 ? "" : "s"}${e.mounts > 0 ? ` (${e.mounts} mount${e.mounts === 1 ? "" : "s"})` : ""}`,
101
104
  "",
@@ -132,5 +135,5 @@ var fmt = (ms) => `${ms.toFixed(1)}ms`;
132
135
 
133
136
  exports.explainEvents = explainEvents;
134
137
  exports.formatExplanation = formatExplanation;
135
- //# sourceMappingURL=chunk-U3SAWFCT.cjs.map
136
- //# sourceMappingURL=chunk-U3SAWFCT.cjs.map
138
+ //# sourceMappingURL=chunk-QQ4VARSD.cjs.map
139
+ //# sourceMappingURL=chunk-QQ4VARSD.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/explain.ts"],"names":[],"mappings":"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","file":"chunk-QQ4VARSD.cjs","sourcesContent":["import type { Confidence, RenderEvent, RenderReason } from \"./types.js\";\n\nexport interface Explanation {\n component: string;\n /** `File.tsx:12:2`, when the build plugin instrumented this component. */\n source?: string;\n /** Where the component's props come from, and its source if known. */\n parent?: { name: string; source?: string };\n renders: number;\n /** Mounts are excluded from every share below — they cannot be optimised away. */\n mounts: number;\n /** Reason → share of renders, descending. */\n breakdown: Array<{ reason: RenderReason; count: number; share: number }>;\n /** Props that most often changed by reference only, descending. */\n unstableProps: Array<{ key: string; count: number; share: number; valueType: string }>;\n averageSelfDuration: number;\n totalSelfDuration: number;\n potentiallyAvoidableRenders: number;\n estimatedAvoidableTime: number;\n devReplays: number;\n headline: string;\n nextStep: string;\n confidence: Confidence;\n}\n\n/**\n * The flagship \"explain this render\" answer, aggregated across the recorded\n * history of one component. Pure — no React, no console.\n */\nexport function explainEvents(component: string, events: RenderEvent[]): Explanation | undefined {\n const mine = events.filter((e) => e.component.name === component);\n if (mine.length === 0) return undefined;\n\n /*\n * Shares are measured against *updates*, not against every recorded render.\n * Mounts happen once per instance and are not something you can optimise\n * away, so counting them in the denominator quietly deflates the share of\n * whatever is actually driving the re-renders — on a 20-row list, a prop\n * responsible for every single update reads as \"33%\".\n */\n const updates = mine.filter((e) => e.phase !== \"mount\");\n const denominator = updates.length || mine.length;\n\n const reasonCounts = new Map<RenderReason, number>();\n const unstable = new Map<string, { count: number; valueType: string }>();\n let totalSelf = 0;\n let avoidable = 0;\n let avoidableTime = 0;\n let replays = 0;\n let propsWithNewValues = 0;\n let propsReferenceOnly = 0;\n\n for (const e of mine) {\n reasonCounts.set(e.diagnosis.reason, (reasonCounts.get(e.diagnosis.reason) ?? 0) + 1);\n totalSelf += e.timings.selfDuration;\n if (e.devReplay) replays++;\n if (e.diagnosis.potentiallyAvoidable) {\n avoidable++;\n avoidableTime += e.timings.selfDuration;\n }\n if (e.diagnosis.reason === \"props\") {\n if (e.changedProps.some((c) => c.kind !== \"reference\")) propsWithNewValues++;\n else propsReferenceOnly++;\n }\n for (const c of e.changedProps) {\n if (c.kind !== \"reference\") continue;\n const entry = unstable.get(c.key) ?? { count: 0, valueType: c.valueType };\n entry.count++;\n unstable.set(c.key, entry);\n }\n }\n\n const breakdown = [...reasonCounts.entries()]\n .map(([reason, count]) => ({ reason, count, share: count / mine.length }))\n .sort((a, b) => b.count - a.count);\n\n const unstableProps = [...unstable.entries()]\n .map(([key, v]) => ({ key, count: v.count, share: v.count / denominator, valueType: v.valueType }))\n .sort((a, b) => b.count - a.count);\n\n // Ranked over updates, for the same reason the shares are.\n const top = [...reasonCounts.entries()]\n .filter(([reason]) => reason !== \"mount\")\n .map(([reason, count]) => ({ reason, count, share: count / denominator }))\n .sort((a, b) => b.count - a.count)[0];\n const topProp = unstableProps[0];\n const latest = mine[mine.length - 1] as RenderEvent;\n\n let headline: string;\n let nextStep: string;\n let confidence: Confidence = \"medium\";\n\n if (topProp && topProp.share >= 0.5) {\n headline = `${pct(topProp.share)} of updates followed \\`${topProp.key}\\` changing by reference while its contents stayed the same.`;\n // `parent` is the nearest instrumented ancestor — where the prop arrives\n // from, which is not necessarily where it is created. Say the former.\n const via = latest.parent?.name;\n const viaSource = latest.parent?.source ? ` (${latest.parent.source})` : \"\";\n nextStep =\n `Trace \\`${topProp.key}\\` back from ${via ? `${via}${viaSource}, which passes it to ${component}` : component}, ` +\n `and stabilise it where it is created${\n topProp.valueType === \"function\" ? \" (useCallback, or hoist it out of the component)\" : \" (useMemo, or pass the primitive fields you use)\"\n }.`;\n confidence = \"high\";\n } else if (top && top.reason === \"parent\" && top.share >= 0.5) {\n headline = `${pct(top.share)} of updates were parent propagation with identical props.`;\n nextStep = `Measure ${component} with React.memo(). It is only worth it if ${fmt(totalSelf / mine.length)} per render matters here.`;\n confidence = \"high\";\n } else if (top && top.reason === \"context\") {\n headline = `${pct(top.share)} of updates followed a tracked context update.`;\n nextStep = \"Check whether the provider's value is stable, and whether this component needs the whole context value.\";\n confidence = \"medium\";\n } else if (top && (top.reason === \"state\" || top.reason === \"state-or-external\")) {\n headline = `${pct(top.share)} of updates came from inside the component — state or an external store.`;\n nextStep =\n top.reason === \"state\"\n ? \"These are real state updates. Check whether every update needs to change state.\"\n : \"Name the state with useTrackedState to see which value drives these renders.\";\n confidence = top.reason === \"state\" ? \"high\" : \"medium\";\n } else if (top && top.reason === \"props\") {\n // Do not call a bucket \"new values\" without checking that it holds any.\n if (propsReferenceOnly > propsWithNewValues) {\n headline = `${pct(propsReferenceOnly / denominator)} of updates were prop changes where only the reference changed — the contents were identical.`;\n nextStep = `Stabilise the props listed above in whichever component renders ${component}.`;\n confidence = \"high\";\n } else if (propsReferenceOnly > 0) {\n headline =\n `${pct(propsWithNewValues / denominator)} of updates carried genuinely new prop values, ` +\n `and ${pct(propsReferenceOnly / denominator)} changed by reference only.`;\n nextStep = \"The reference-only ones are the avoidable half — start there.\";\n confidence = \"high\";\n } else {\n headline = `${pct(top.share)} of updates were driven by props with genuinely new values.`;\n nextStep = \"These look like legitimate data changes. Look at render cost rather than render count.\";\n confidence = \"high\";\n }\n } else {\n headline = \"Cause could not be determined reliably.\";\n nextStep = \"Instrument the parent, or track the context this component consumes, to narrow it down.\";\n confidence = \"low\";\n }\n\n return {\n component,\n source: latest.component.source,\n parent: latest.parent ? { name: latest.parent.name, source: latest.parent.source } : undefined,\n renders: mine.length,\n mounts: mine.length - updates.length,\n breakdown,\n unstableProps,\n averageSelfDuration: totalSelf / mine.length,\n totalSelfDuration: totalSelf,\n potentiallyAvoidableRenders: avoidable,\n estimatedAvoidableTime: avoidableTime,\n devReplays: replays,\n headline,\n nextStep,\n confidence,\n };\n}\n\nexport function formatExplanation(e: Explanation): string {\n const lines: string[] = [\n e.source ? `${e.component} ${e.source}` : `${e.component}`,\n \"\",\n `${e.renders} recorded render${e.renders === 1 ? \"\" : \"s\"}${e.mounts > 0 ? ` (${e.mounts} mount${e.mounts === 1 ? \"\" : \"s\"})` : \"\"}`,\n \"\",\n \"Why?\",\n ` ${e.headline}`,\n \"\",\n \"Breakdown (share of all renders)\",\n ...e.breakdown.map((b) => ` ${b.reason.padEnd(18)} ${String(b.count).padStart(5)} ${pct(b.share)}`),\n ];\n\n if (e.unstableProps.length > 0) {\n lines.push(\"\", \"Reference-only prop changes (share of updates)\");\n for (const p of e.unstableProps) {\n lines.push(` ${p.key.padEnd(18)} ${String(p.count).padStart(5)} ${pct(p.share)} (${p.valueType})`);\n }\n }\n\n lines.push(\n \"\",\n \"Cost\",\n ` average ${fmt(e.averageSelfDuration)}`,\n ` total ${fmt(e.totalSelfDuration)}`,\n ` potentially avoidable ${e.potentiallyAvoidableRenders} render(s), ~${fmt(e.estimatedAvoidableTime)}`,\n );\n\n if (e.devReplays > 0) {\n lines.push(\n \"\",\n ` ${e.devReplays} development replay(s) observed (StrictMode double-invoke or discarded attempts) — excluded from the counts above.`,\n );\n }\n\n lines.push(\"\", \"Next step\", ` ${e.nextStep}`, \"\", `Confidence: ${e.confidence}`);\n return lines.join(\"\\n\");\n}\n\nconst pct = (n: number): string => `${Math.round(n * 100)}%`;\nconst fmt = (ms: number): string => `${ms.toFixed(1)}ms`;\n"]}
package/dist/core.cjs CHANGED
@@ -1,76 +1,76 @@
1
1
  'use strict';
2
2
 
3
- var chunkSKLDIIL6_cjs = require('./chunk-SKLDIIL6.cjs');
3
+ var chunk6YPJQ7M5_cjs = require('./chunk-6YPJQ7M5.cjs');
4
4
 
5
5
 
6
6
 
7
7
  Object.defineProperty(exports, "Detective", {
8
8
  enumerable: true,
9
- get: function () { return chunkSKLDIIL6_cjs.Detective; }
9
+ get: function () { return chunk6YPJQ7M5_cjs.Detective; }
10
10
  });
11
11
  Object.defineProperty(exports, "RingBuffer", {
12
12
  enumerable: true,
13
- get: function () { return chunkSKLDIIL6_cjs.RingBuffer; }
13
+ get: function () { return chunk6YPJQ7M5_cjs.RingBuffer; }
14
14
  });
15
15
  Object.defineProperty(exports, "defaultConfig", {
16
16
  enumerable: true,
17
- get: function () { return chunkSKLDIIL6_cjs.defaultConfig; }
17
+ get: function () { return chunk6YPJQ7M5_cjs.defaultConfig; }
18
18
  });
19
19
  Object.defineProperty(exports, "detectDev", {
20
20
  enumerable: true,
21
- get: function () { return chunkSKLDIIL6_cjs.detectDev; }
21
+ get: function () { return chunk6YPJQ7M5_cjs.detectDev; }
22
22
  });
23
23
  Object.defineProperty(exports, "diagnose", {
24
24
  enumerable: true,
25
- get: function () { return chunkSKLDIIL6_cjs.diagnose; }
25
+ get: function () { return chunk6YPJQ7M5_cjs.diagnose; }
26
26
  });
27
27
  Object.defineProperty(exports, "diffProps", {
28
28
  enumerable: true,
29
- get: function () { return chunkSKLDIIL6_cjs.diffProps; }
29
+ get: function () { return chunk6YPJQ7M5_cjs.diffProps; }
30
30
  });
31
31
  Object.defineProperty(exports, "formatInspected", {
32
32
  enumerable: true,
33
- get: function () { return chunkSKLDIIL6_cjs.formatInspected; }
33
+ get: function () { return chunk6YPJQ7M5_cjs.formatInspected; }
34
34
  });
35
35
  Object.defineProperty(exports, "getDetective", {
36
36
  enumerable: true,
37
- get: function () { return chunkSKLDIIL6_cjs.getDetective; }
37
+ get: function () { return chunk6YPJQ7M5_cjs.getDetective; }
38
38
  });
39
39
  Object.defineProperty(exports, "inspect", {
40
40
  enumerable: true,
41
- get: function () { return chunkSKLDIIL6_cjs.inspect; }
41
+ get: function () { return chunk6YPJQ7M5_cjs.inspect; }
42
42
  });
43
43
  Object.defineProperty(exports, "isPlainObject", {
44
44
  enumerable: true,
45
- get: function () { return chunkSKLDIIL6_cjs.isPlainObject; }
45
+ get: function () { return chunk6YPJQ7M5_cjs.isPlainObject; }
46
46
  });
47
47
  Object.defineProperty(exports, "isReactElement", {
48
48
  enumerable: true,
49
- get: function () { return chunkSKLDIIL6_cjs.isReactElement; }
49
+ get: function () { return chunk6YPJQ7M5_cjs.isReactElement; }
50
50
  });
51
51
  Object.defineProperty(exports, "matches", {
52
52
  enumerable: true,
53
- get: function () { return chunkSKLDIIL6_cjs.matches; }
53
+ get: function () { return chunk6YPJQ7M5_cjs.matches; }
54
54
  });
55
55
  Object.defineProperty(exports, "mergeConfig", {
56
56
  enumerable: true,
57
- get: function () { return chunkSKLDIIL6_cjs.mergeConfig; }
57
+ get: function () { return chunk6YPJQ7M5_cjs.mergeConfig; }
58
58
  });
59
59
  Object.defineProperty(exports, "severityFor", {
60
60
  enumerable: true,
61
- get: function () { return chunkSKLDIIL6_cjs.severityFor; }
61
+ get: function () { return chunk6YPJQ7M5_cjs.severityFor; }
62
62
  });
63
63
  Object.defineProperty(exports, "shallowEqual", {
64
64
  enumerable: true,
65
- get: function () { return chunkSKLDIIL6_cjs.shallowEqual; }
65
+ get: function () { return chunk6YPJQ7M5_cjs.shallowEqual; }
66
66
  });
67
67
  Object.defineProperty(exports, "shouldInstrument", {
68
68
  enumerable: true,
69
- get: function () { return chunkSKLDIIL6_cjs.shouldInstrument; }
69
+ get: function () { return chunk6YPJQ7M5_cjs.shouldInstrument; }
70
70
  });
71
71
  Object.defineProperty(exports, "valueType", {
72
72
  enumerable: true,
73
- get: function () { return chunkSKLDIIL6_cjs.valueType; }
73
+ get: function () { return chunk6YPJQ7M5_cjs.valueType; }
74
74
  });
75
75
  //# sourceMappingURL=core.cjs.map
76
76
  //# sourceMappingURL=core.cjs.map
package/dist/core.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { D as DetectiveConfig, T as TrackedStateChange, R as RenderReason, a as DetectiveOptions, b as RenderEvent, c as RenderPhase, C as ContextChange, d as ComponentStats, A as AppStats, P as PropChange, e as Thresholds, f as Diagnosis, I as Inspected, g as InspectionLimits, h as PropValueType } from './types-CY61yfhK.cjs';
2
- export { i as ComponentInfo, j as Confidence, M as Mode, k as PropChangeKind, l as RenderTimings } from './types-CY61yfhK.cjs';
1
+ import { D as DetectiveConfig, T as TrackedStateChange, R as RenderReason, a as DetectiveOptions, b as RenderEvent, c as RenderPhase, C as ContextChange, d as ComponentStats, A as AppStats, P as PropChange, e as Thresholds, f as Diagnosis, I as Inspected, g as InspectionLimits, h as PropValueType } from './types-BWVaQPHb.cjs';
2
+ export { i as ComponentInfo, j as Confidence, M as Mode, k as PropChangeKind, l as RenderTimings } from './types-BWVaQPHb.cjs';
3
3
 
4
4
  /** What an instrumentation site reports at commit time. */
5
5
  interface CommitInput {
@@ -13,6 +13,8 @@ interface CommitInput {
13
13
  interface NodeRecord {
14
14
  id: string;
15
15
  name: string;
16
+ /** `File.tsx:12:2` when the build plugin instrumented this component. */
17
+ source?: string;
16
18
  /** Nearest instrumented ancestor, held directly so diagnosis never needs the registry. */
17
19
  parent?: NodeRecord;
18
20
  depth: number;
@@ -81,7 +83,7 @@ declare class Detective {
81
83
  * discards one of the two, and only the surviving fiber's effect attaches —
82
84
  * so discarded nodes are simply garbage collected.
83
85
  */
84
- createNode(name: string, parent: NodeRecord | undefined): NodeRecord | undefined;
86
+ createNode(name: string, parent: NodeRecord | undefined, source?: string): NodeRecord | undefined;
85
87
  attach(node: NodeRecord): void;
86
88
  detach(node: NodeRecord): void;
87
89
  /** Hot path. Must stay allocation-free and O(1). */
package/dist/core.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { D as DetectiveConfig, T as TrackedStateChange, R as RenderReason, a as DetectiveOptions, b as RenderEvent, c as RenderPhase, C as ContextChange, d as ComponentStats, A as AppStats, P as PropChange, e as Thresholds, f as Diagnosis, I as Inspected, g as InspectionLimits, h as PropValueType } from './types-CY61yfhK.js';
2
- export { i as ComponentInfo, j as Confidence, M as Mode, k as PropChangeKind, l as RenderTimings } from './types-CY61yfhK.js';
1
+ import { D as DetectiveConfig, T as TrackedStateChange, R as RenderReason, a as DetectiveOptions, b as RenderEvent, c as RenderPhase, C as ContextChange, d as ComponentStats, A as AppStats, P as PropChange, e as Thresholds, f as Diagnosis, I as Inspected, g as InspectionLimits, h as PropValueType } from './types-BWVaQPHb.js';
2
+ export { i as ComponentInfo, j as Confidence, M as Mode, k as PropChangeKind, l as RenderTimings } from './types-BWVaQPHb.js';
3
3
 
4
4
  /** What an instrumentation site reports at commit time. */
5
5
  interface CommitInput {
@@ -13,6 +13,8 @@ interface CommitInput {
13
13
  interface NodeRecord {
14
14
  id: string;
15
15
  name: string;
16
+ /** `File.tsx:12:2` when the build plugin instrumented this component. */
17
+ source?: string;
16
18
  /** Nearest instrumented ancestor, held directly so diagnosis never needs the registry. */
17
19
  parent?: NodeRecord;
18
20
  depth: number;
@@ -81,7 +83,7 @@ declare class Detective {
81
83
  * discards one of the two, and only the surviving fiber's effect attaches —
82
84
  * so discarded nodes are simply garbage collected.
83
85
  */
84
- createNode(name: string, parent: NodeRecord | undefined): NodeRecord | undefined;
86
+ createNode(name: string, parent: NodeRecord | undefined, source?: string): NodeRecord | undefined;
85
87
  attach(node: NodeRecord): void;
86
88
  detach(node: NodeRecord): void;
87
89
  /** Hot path. Must stay allocation-free and O(1). */
package/dist/core.js CHANGED
@@ -1,3 +1,3 @@
1
- export { Detective, RingBuffer, defaultConfig, detectDev, diagnose, diffProps, formatInspected, getDetective, inspect, isPlainObject, isReactElement, matches, mergeConfig, severityFor, shallowEqual, shouldInstrument, valueType } from './chunk-27IWHWSM.js';
1
+ export { Detective, RingBuffer, defaultConfig, detectDev, diagnose, diffProps, formatInspected, getDetective, inspect, isPlainObject, isReactElement, matches, mergeConfig, severityFor, shallowEqual, shouldInstrument, valueType } from './chunk-MSISKPST.js';
2
2
  //# sourceMappingURL=core.js.map
3
3
  //# sourceMappingURL=core.js.map
package/dist/index.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var chunkU3SAWFCT_cjs = require('./chunk-U3SAWFCT.cjs');
4
- var chunkSKLDIIL6_cjs = require('./chunk-SKLDIIL6.cjs');
3
+ var chunkQQ4VARSD_cjs = require('./chunk-QQ4VARSD.cjs');
4
+ var chunk6YPJQ7M5_cjs = require('./chunk-6YPJQ7M5.cjs');
5
5
  var React = require('react');
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
7
 
@@ -65,8 +65,11 @@ function printVerbose(event) {
65
65
  group.call(console, header);
66
66
  try {
67
67
  console.log(`Cause: ${label(event)} (confidence: ${diagnosis.confidence})`);
68
+ if (event.component.source) console.log(`Source: ${event.component.source}`);
68
69
  if (event.parent) {
69
- console.log(`Parent: ${event.parent.name}${event.parentRendered ? " (re-rendered in this commit)" : " (did not re-render)"}`);
70
+ console.log(
71
+ `Parent: ${event.parent.name}${event.parent.source ? ` (${event.parent.source})` : ""}${event.parentRendered ? " \u2014 re-rendered in this commit" : " \u2014 did not re-render"}`
72
+ );
70
73
  }
71
74
  console.log(
72
75
  `Cost: self ${timings.selfDuration.toFixed(1)}ms \xB7 subtree ${timings.subtreeDuration.toFixed(1)}ms` + (timings.accountedDescendantDuration > 0 ? ` (${timings.accountedDescendantDuration.toFixed(1)}ms in instrumented children)` : "")
@@ -81,7 +84,7 @@ function printVerbose(event) {
81
84
  }
82
85
  if (event.trackedState.length > 0) {
83
86
  console.log(
84
- `State: ${event.trackedState.map((s) => `${s.name}: ${chunkSKLDIIL6_cjs.formatInspected(s.previous)} \u2192 ${chunkSKLDIIL6_cjs.formatInspected(s.current)}`).join(", ")}`
87
+ `State: ${event.trackedState.map((s) => `${s.name}: ${chunk6YPJQ7M5_cjs.formatInspected(s.previous)} \u2192 ${chunk6YPJQ7M5_cjs.formatInspected(s.current)}`).join(", ")}`
85
88
  );
86
89
  }
87
90
  if (event.contextChanges.length > 0) {
@@ -98,11 +101,11 @@ function printVerbose(event) {
98
101
  }
99
102
  function propLine(c) {
100
103
  const head = `${c.key} (${c.valueType}) \u2014 ${describeKind(c)}`;
101
- if (c.kind === "added") return `${head}: ${chunkSKLDIIL6_cjs.formatInspected(c.current)}`;
102
- if (c.kind === "removed") return `${head}: was ${chunkSKLDIIL6_cjs.formatInspected(c.previous)}`;
104
+ if (c.kind === "added") return `${head}: ${chunk6YPJQ7M5_cjs.formatInspected(c.current)}`;
105
+ if (c.kind === "removed") return `${head}: was ${chunk6YPJQ7M5_cjs.formatInspected(c.previous)}`;
103
106
  return `${head}
104
- previous: ${chunkSKLDIIL6_cjs.formatInspected(c.previous)}
105
- current: ${chunkSKLDIIL6_cjs.formatInspected(c.current)}`;
107
+ previous: ${chunk6YPJQ7M5_cjs.formatInspected(c.previous)}
108
+ current: ${chunk6YPJQ7M5_cjs.formatInspected(c.current)}`;
106
109
  }
107
110
  function describeKind(c) {
108
111
  switch (c.kind) {
@@ -137,11 +140,11 @@ function label(event) {
137
140
  }
138
141
  var AncestryContext = React.createContext(void 0);
139
142
  AncestryContext.displayName = "RenderDetectiveAncestry";
140
- function useInstrumentedNode(name, props) {
141
- const detective = chunkSKLDIIL6_cjs.getDetective();
143
+ function useInstrumentedNode(name, props, source) {
144
+ const detective = chunk6YPJQ7M5_cjs.getDetective();
142
145
  const parent = React.useContext(AncestryContext);
143
146
  const [node] = React.useState(
144
- () => detective.enabled ? detective.createNode(name, parent) : void 0
147
+ () => detective.enabled ? detective.createNode(name, parent, source) : void 0
145
148
  );
146
149
  React.useLayoutEffect(() => {
147
150
  if (!node) return;
@@ -184,13 +187,16 @@ function componentName(component, fallback = "Anonymous") {
184
187
  if (c.type) return componentName(c.type, fallback);
185
188
  return fallback;
186
189
  }
190
+ var IS_WRAPPER = /* @__PURE__ */ Symbol.for("react-render-detective.wrapper");
187
191
  function withRenderDetective(Component, options = {}) {
192
+ if (Component[IS_WRAPPER]) return Component;
188
193
  const name = options.name ?? componentName(Component);
189
194
  function RenderDetected(props) {
190
- const { node, onRender } = useInstrumentedNode(name, props);
195
+ const { node, onRender } = useInstrumentedNode(name, props, options.source);
191
196
  return renderInstrumented(node, onRender, /* @__PURE__ */ jsxRuntime.jsx(Component, { ...props }));
192
197
  }
193
198
  RenderDetected.displayName = `RenderDetective(${name})`;
199
+ RenderDetected[IS_WRAPPER] = true;
194
200
  return RenderDetected;
195
201
  }
196
202
 
@@ -200,7 +206,7 @@ function RenderDetective({ name, children }) {
200
206
  return renderInstrumented(node, onRender, children);
201
207
  }
202
208
  function useRenderDiagnostics(name, props) {
203
- const detective = chunkSKLDIIL6_cjs.getDetective();
209
+ const detective = chunk6YPJQ7M5_cjs.getDetective();
204
210
  const parent = React.useContext(AncestryContext);
205
211
  const [node] = React.useState(() => {
206
212
  if (!detective.enabled) return void 0;
@@ -235,7 +241,7 @@ function useRenderDiagnostics(name, props) {
235
241
  }
236
242
  var useOwnerId = typeof React__namespace.useId === "function" ? React__namespace.useId : () => "";
237
243
  function useTrackedState(name, initial) {
238
- const detective = chunkSKLDIIL6_cjs.getDetective();
244
+ const detective = chunk6YPJQ7M5_cjs.getDetective();
239
245
  const node = React.useContext(AncestryContext);
240
246
  const ownerId = useOwnerId();
241
247
  const [state, setState] = React.useState(initial);
@@ -245,8 +251,8 @@ function useTrackedState(name, initial) {
245
251
  if (node && owns && !Object.is(previous.current, state)) {
246
252
  detective.recordStateChange(node, {
247
253
  name,
248
- previous: chunkSKLDIIL6_cjs.inspect(previous.current, detective.config.inspection),
249
- current: chunkSKLDIIL6_cjs.inspect(state, detective.config.inspection)
254
+ previous: chunk6YPJQ7M5_cjs.inspect(previous.current, detective.config.inspection),
255
+ current: chunk6YPJQ7M5_cjs.inspect(state, detective.config.inspection)
250
256
  });
251
257
  previous.current = state;
252
258
  } else if (!owns) {
@@ -255,7 +261,7 @@ function useTrackedState(name, initial) {
255
261
  return [state, setState];
256
262
  }
257
263
  function useTrackedEffect(name, effect, deps) {
258
- const detective = chunkSKLDIIL6_cjs.getDetective();
264
+ const detective = chunk6YPJQ7M5_cjs.getDetective();
259
265
  const previous = React.useRef(void 0);
260
266
  const changed = React.useRef([]);
261
267
  if (detective.enabled) {
@@ -271,7 +277,7 @@ function useTrackedEffect(name, effect, deps) {
271
277
  }, deps);
272
278
  }
273
279
  function useTrackedContextValue(contextName, value) {
274
- const detective = chunkSKLDIIL6_cjs.getDetective();
280
+ const detective = chunk6YPJQ7M5_cjs.getDetective();
275
281
  const previous = React.useRef(void 0);
276
282
  const first = React.useRef(true);
277
283
  if (detective.enabled) {
@@ -279,8 +285,8 @@ function useTrackedContextValue(contextName, value) {
279
285
  first.current = false;
280
286
  } else if (!Object.is(previous.current, value)) {
281
287
  const prev = previous.current;
282
- const equal = chunkSKLDIIL6_cjs.shallowEqual(prev, value, detective.config);
283
- const changedKeys = isRecord(prev) && isRecord(value) ? chunkSKLDIIL6_cjs.diffProps(prev, value, detective.config).changed.map((c) => c.key) : [];
288
+ const equal = chunk6YPJQ7M5_cjs.shallowEqual(prev, value, detective.config);
289
+ const changedKeys = isRecord(prev) && isRecord(value) ? chunk6YPJQ7M5_cjs.diffProps(prev, value, detective.config).changed.map((c) => c.key) : [];
284
290
  detective.recordContextChange({
285
291
  contextName,
286
292
  changedKeys,
@@ -299,7 +305,7 @@ function isRecord(v) {
299
305
  // src/index.ts
300
306
  var REPORTER = /* @__PURE__ */ Symbol.for("react-render-detective.reporter");
301
307
  function init(options = {}) {
302
- const detective = chunkSKLDIIL6_cjs.getDetective();
308
+ const detective = chunk6YPJQ7M5_cjs.getDetective();
303
309
  detective.init(options);
304
310
  const g = globalThis;
305
311
  g[REPORTER]?.();
@@ -313,41 +319,41 @@ function init(options = {}) {
313
319
  }
314
320
  }
315
321
  function configure(options) {
316
- chunkSKLDIIL6_cjs.getDetective().configure(options);
322
+ chunk6YPJQ7M5_cjs.getDetective().configure(options);
317
323
  }
318
324
  function getConfig() {
319
- return chunkSKLDIIL6_cjs.getDetective().config;
325
+ return chunk6YPJQ7M5_cjs.getDetective().config;
320
326
  }
321
327
  function isEnabled() {
322
- return chunkSKLDIIL6_cjs.getDetective().enabled;
328
+ return chunk6YPJQ7M5_cjs.getDetective().enabled;
323
329
  }
324
330
  function getEvents() {
325
- return chunkSKLDIIL6_cjs.getDetective().getEvents();
331
+ return chunk6YPJQ7M5_cjs.getDetective().getEvents();
326
332
  }
327
333
  function getStats() {
328
- return chunkSKLDIIL6_cjs.getDetective().getStats();
334
+ return chunk6YPJQ7M5_cjs.getDetective().getStats();
329
335
  }
330
336
  function getComponentStats(name) {
331
- return chunkSKLDIIL6_cjs.getDetective().getComponentStats(name);
337
+ return chunk6YPJQ7M5_cjs.getDetective().getComponentStats(name);
332
338
  }
333
339
  function subscribe(listener) {
334
- return chunkSKLDIIL6_cjs.getDetective().subscribe(listener);
340
+ return chunk6YPJQ7M5_cjs.getDetective().subscribe(listener);
335
341
  }
336
342
  function clear() {
337
- chunkSKLDIIL6_cjs.getDetective().clear();
343
+ chunk6YPJQ7M5_cjs.getDetective().clear();
338
344
  }
339
345
  function reset() {
340
346
  const g = globalThis;
341
347
  g[REPORTER]?.();
342
348
  g[REPORTER] = void 0;
343
- chunkSKLDIIL6_cjs.getDetective().reset();
349
+ chunk6YPJQ7M5_cjs.getDetective().reset();
344
350
  }
345
351
  function explain(componentName2) {
346
- const explanation = chunkU3SAWFCT_cjs.explainEvents(componentName2, getEvents());
347
- return explanation ? chunkU3SAWFCT_cjs.formatExplanation(explanation) : void 0;
352
+ const explanation = chunkQQ4VARSD_cjs.explainEvents(componentName2, getEvents());
353
+ return explanation ? chunkQQ4VARSD_cjs.formatExplanation(explanation) : void 0;
348
354
  }
349
355
  function explainStructured(componentName2) {
350
- return chunkU3SAWFCT_cjs.explainEvents(componentName2, getEvents());
356
+ return chunkQQ4VARSD_cjs.explainEvents(componentName2, getEvents());
351
357
  }
352
358
  function printStats() {
353
359
  const s = getStats();
@@ -391,11 +397,11 @@ var ReactRenderDetective = {
391
397
 
392
398
  Object.defineProperty(exports, "explainEvents", {
393
399
  enumerable: true,
394
- get: function () { return chunkU3SAWFCT_cjs.explainEvents; }
400
+ get: function () { return chunkQQ4VARSD_cjs.explainEvents; }
395
401
  });
396
402
  Object.defineProperty(exports, "formatExplanation", {
397
403
  enumerable: true,
398
- get: function () { return chunkU3SAWFCT_cjs.formatExplanation; }
404
+ get: function () { return chunkQQ4VARSD_cjs.formatExplanation; }
399
405
  });
400
406
  exports.ReactRenderDetective = ReactRenderDetective;
401
407
  exports.RenderDetective = RenderDetective;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/console/reporter.ts","../src/react/ancestry.ts","../src/react/instrument.tsx","../src/react/withRenderDetective.tsx","../src/react/RenderDetective.tsx","../src/react/hooks.ts","../src/index.ts"],"names":["formatInspected","createContext","getDetective","useContext","useState","useLayoutEffect","useCallback","jsx","Profiler","useRef","useEffect","React","inspect","shallowEqual","diffProps","componentName","explainEvents","formatExplanation"],"mappings":"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","file":"index.cjs","sourcesContent":["import { formatInspected } from \"../core/inspect.js\";\nimport type { Detective } from \"../core/store.js\";\nimport type { PropChange, RenderEvent } from \"../core/types.js\";\n\nconst ICON: Record<string, string> = {\n normal: \"·\",\n monitor: \"•\",\n slow: \"▲\",\n \"very-slow\": \"▲▲\",\n critical: \"■\",\n};\n\n/** Attaches console output. Returns an unsubscribe so init() stays idempotent. */\nexport function attachConsoleReporter(detective: Detective): () => void {\n return detective.subscribe((event) => {\n const mode = detective.config.mode;\n if (mode === \"silent\") return;\n try {\n if (mode === \"verbose\") printVerbose(event);\n else printConcise(event, detective.config.slowRenderThreshold);\n } catch {\n /* ignore */\n }\n });\n}\n\nfunction printConcise(event: RenderEvent, slowThreshold: number): void {\n const { diagnosis, timings, component } = event;\n const changed = event.changedProps.map((c) => c.key).join(\", \");\n const parts = [\n `[RRD] ${component.name} #${event.renderNumber}`,\n `Reason: ${label(event)}`,\n changed ? `Changed: ${changed}` : undefined,\n timings.subtreeDuration > 0 ? `Duration: ${timings.selfDuration.toFixed(1)}ms` : undefined,\n ].filter(Boolean);\n\n const line = `${ICON[diagnosis.severity] ?? \"·\"} ${parts.join(\" \")}`;\n const slow = timings.selfDuration >= slowThreshold;\n if (slow) console.warn(line);\n else console.log(line);\n}\n\nfunction printVerbose(event: RenderEvent): void {\n const { diagnosis, timings } = event;\n const header = `[RRD] ${event.component.name} #${event.renderNumber} — ${diagnosis.summary}`;\n\n const group = diagnosis.severity === \"normal\" ? console.groupCollapsed : console.group;\n group.call(console, header);\n try {\n console.log(`Cause: ${label(event)} (confidence: ${diagnosis.confidence})`);\n if (event.parent) {\n console.log(`Parent: ${event.parent.name}${event.parentRendered ? \" (re-rendered in this commit)\" : \" (did not re-render)\"}`);\n }\n console.log(\n `Cost: self ${timings.selfDuration.toFixed(1)}ms · subtree ${timings.subtreeDuration.toFixed(1)}ms` +\n (timings.accountedDescendantDuration > 0\n ? ` (${timings.accountedDescendantDuration.toFixed(1)}ms in instrumented children)`\n : \"\"),\n );\n\n if (event.changedProps.length > 0) {\n console.groupCollapsed(`Props changed (${event.changedProps.length})`);\n for (const c of event.changedProps) console.log(propLine(c));\n console.groupEnd();\n }\n if (event.unchangedProps.length > 0) {\n console.log(`Props same: ${event.unchangedProps.join(\", \")}`);\n }\n if (event.trackedState.length > 0) {\n console.log(\n `State: ${event.trackedState\n .map((s) => `${s.name}: ${formatInspected(s.previous)} → ${formatInspected(s.current)}`)\n .join(\", \")}`,\n );\n }\n if (event.contextChanges.length > 0) {\n console.log(`Context: ${event.contextChanges.map((c) => c.contextName).join(\", \")} changed in this commit`);\n }\n\n console.groupCollapsed(\"Evidence\");\n for (const line of diagnosis.evidence) console.log(`• ${line}`);\n console.groupEnd();\n\n if (diagnosis.suggestion) console.log(`Next step: ${diagnosis.suggestion}`);\n if (diagnosis.potentiallyAvoidable) console.log(\"Flag: potentially avoidable render\");\n } finally {\n console.groupEnd();\n }\n}\n\nfunction propLine(c: PropChange): string {\n const head = `${c.key} (${c.valueType}) — ${describeKind(c)}`;\n if (c.kind === \"added\") return `${head}: ${formatInspected(c.current)}`;\n if (c.kind === \"removed\") return `${head}: was ${formatInspected(c.previous)}`;\n return `${head}\\n previous: ${formatInspected(c.previous)}\\n current: ${formatInspected(c.current)}`;\n}\n\nfunction describeKind(c: PropChange): string {\n switch (c.kind) {\n case \"added\":\n return \"added\";\n case \"removed\":\n return \"removed\";\n case \"value\":\n return \"value changed\";\n case \"reference\":\n if (c.valueType === \"function\") return c.sourceEqual ? \"new function, identical source\" : \"new function reference\";\n return c.shallowEqual === true ? \"reference changed, contents identical\" : \"reference changed\";\n }\n}\n\nfunction label(event: RenderEvent): string {\n switch (event.diagnosis.reason) {\n case \"mount\":\n return \"mount\";\n case \"props\":\n return event.changedProps.every((c) => c.kind === \"reference\") ? \"prop changed (reference only)\" : \"prop changed\";\n case \"parent\":\n return \"parent rendered\";\n case \"context\":\n return \"context update\";\n case \"state\":\n return \"state changed\";\n case \"state-or-external\":\n return \"state or external store\";\n case \"unknown\":\n return \"undetermined\";\n }\n}\n","import { createContext } from \"react\";\nimport type { NodeRecord } from \"../core/store.js\";\n\n/**\n * Carries the nearest instrumented ancestor down the tree.\n *\n * The value is the node object, created once per mounted instance and **never\n * replaced**. That is deliberate: a context value that changed per render would\n * force every instrumented descendant to re-render, defeating `React.memo` and\n * changing the behaviour of the very app we are measuring.\n */\nexport const AncestryContext = createContext<NodeRecord | undefined>(undefined);\nAncestryContext.displayName = \"RenderDetectiveAncestry\";\n","import { Profiler, useCallback, useContext, useLayoutEffect, useState } from \"react\";\nimport type { ProfilerOnRenderCallback, ReactNode } from \"react\";\nimport { getDetective } from \"../core/store.js\";\nimport type { NodeRecord } from \"../core/store.js\";\nimport type { RenderPhase } from \"../core/types.js\";\nimport { AncestryContext } from \"./ancestry.js\";\n\nexport interface TrackOptions {\n /** Overrides the inferred component name. Required for anonymous components. */\n name?: string;\n}\n\n/**\n * Registers the current component, records this render attempt, and returns the\n * node plus a Profiler callback. All of it is a no-op when disabled.\n */\nexport function useInstrumentedNode(\n name: string,\n props: Record<string, unknown>,\n): { node: NodeRecord | undefined; onRender: ProfilerOnRenderCallback } {\n const detective = getDetective();\n const parent = useContext(AncestryContext);\n\n // The active/inactive decision is frozen per instance so the element tree\n // shape never changes underneath a mounted component.\n const [node] = useState<NodeRecord | undefined>(() =>\n detective.enabled ? detective.createNode(name, parent) : undefined,\n );\n\n useLayoutEffect(() => {\n if (!node) return;\n detective.attach(node);\n return () => detective.detach(node);\n }, [detective, node]);\n\n if (node) {\n try {\n detective.recordAttempt(node, props);\n } catch {\n /* never break the app being debugged (§46) */\n }\n }\n\n const onRender = useCallback<ProfilerOnRenderCallback>(\n (_id, phase, actualDuration, baseDuration, startTime, commitTime) => {\n if (!node) return;\n try {\n detective.recordCommit(node, {\n phase: phase as RenderPhase,\n subtreeDuration: actualDuration,\n baseDuration,\n startTime,\n commitTime,\n });\n } catch {\n /* ignore */\n }\n },\n [detective, node],\n );\n\n return { node, onRender };\n}\n\nexport function renderInstrumented(\n node: NodeRecord | undefined,\n onRender: ProfilerOnRenderCallback,\n children: ReactNode,\n): ReactNode {\n if (!node) return children;\n return (\n <Profiler id={node.id} onRender={onRender}>\n <AncestryContext.Provider value={node}>{children}</AncestryContext.Provider>\n </Profiler>\n );\n}\n\nexport function componentName(component: unknown, fallback = \"Anonymous\"): string {\n const c = component as { displayName?: string; name?: string; type?: unknown } | null;\n if (!c) return fallback;\n if (typeof c.displayName === \"string\" && c.displayName) return c.displayName;\n if (typeof c.name === \"string\" && c.name) return c.name;\n // React.memo / forwardRef wrappers keep the real component on `.type`.\n if (c.type) return componentName(c.type, fallback);\n return fallback;\n}\n","import type { ComponentType } from \"react\";\nimport { componentName, renderInstrumented, useInstrumentedNode } from \"./instrument.js\";\nimport type { TrackOptions } from \"./instrument.js\";\n\n/**\n * Wraps a component with full instrumentation: render counting, prop diffing,\n * parent attribution and Profiler timings.\n *\n * This is the most accurate integration mode — the wrapper's render function\n * runs exactly when the wrapped component re-renders, and it receives the same\n * props, so every diagnosis has first-hand evidence.\n *\n * Ref note: on React 19 `ref` is an ordinary prop and passes through the spread.\n * On React 18 and below, wrap with `forwardRef` yourself if the component needs\n * a ref.\n */\nexport function withRenderDetective<P extends object>(\n Component: ComponentType<P>,\n options: TrackOptions = {},\n): ComponentType<P> {\n const name = options.name ?? componentName(Component);\n\n function RenderDetected(props: P) {\n const { node, onRender } = useInstrumentedNode(name, props as Record<string, unknown>);\n return renderInstrumented(node, onRender, <Component {...props} />);\n }\n\n RenderDetected.displayName = `RenderDetective(${name})`;\n return RenderDetected;\n}\n","import type { ReactNode } from \"react\";\nimport { renderInstrumented, useInstrumentedNode } from \"./instrument.js\";\n\nexport interface RenderDetectiveProps {\n name: string;\n children: ReactNode;\n}\n\n/**\n * Zero-refactor integration: drop it around a subtree.\n *\n * <RenderDetective name=\"UserProfile\"><UserProfile /></RenderDetective>\n *\n * Accuracy note: this boundary only sees the `children` **element**, not the\n * wrapped component's props. It therefore reports timings, parent propagation\n * and children-element churn accurately, but it cannot attribute a render to an\n * individual prop. Use `withRenderDetective` when you want per-prop diagnosis.\n */\nexport function RenderDetective({ name, children }: RenderDetectiveProps): ReactNode {\n const { node, onRender } = useInstrumentedNode(name, { children });\n return renderInstrumented(node, onRender, children);\n}\n","import * as React from \"react\";\nimport { useContext, useEffect, useRef, useState } from \"react\";\nimport type { Dispatch, SetStateAction } from \"react\";\nimport { getDetective } from \"../core/store.js\";\nimport { inspect } from \"../core/inspect.js\";\nimport { diffProps, shallowEqual } from \"../core/compare.js\";\nimport type { RenderEvent } from \"../core/types.js\";\nimport { AncestryContext } from \"./ancestry.js\";\n\n/**\n * In-component diagnostics without wrapping.\n *\n * Limitation, by design: a hook cannot install a `<Profiler>` around its own\n * component, so **no duration is available in this mode** — timings are\n * reported as 0. Everything else (render count, prop diffing, parent\n * attribution) is as accurate as the HOC.\n */\nexport function useRenderDiagnostics(name: string, props?: Record<string, unknown>): RenderEvent | undefined {\n const detective = getDetective();\n const parent = useContext(AncestryContext);\n const [node] = useState(() => {\n if (!detective.enabled) return undefined;\n const created = detective.createNode(name, parent);\n if (created) detective.hookModeNodes.add(created);\n return created;\n });\n const last = useRef<RenderEvent | undefined>(undefined);\n\n useEffect(() => {\n if (!node) return;\n detective.attach(node);\n const unsubscribe = detective.subscribe((event) => {\n if (event.component.id === node.id) last.current = event;\n });\n return () => {\n unsubscribe();\n detective.detach(node);\n };\n }, [detective, node]);\n\n if (node) {\n const isMount = !node.seenCommit;\n detective.recordAttempt(node, props ?? {});\n // No Profiler in hook mode: enqueue with a sentinel commit time, which the\n // flush pass resolves to the next real commit that follows this render.\n detective.recordCommit(node, {\n phase: isMount ? \"mount\" : \"update\",\n subtreeDuration: 0,\n baseDuration: 0,\n startTime: 0,\n commitTime: -1,\n });\n }\n\n return last.current;\n}\n\n/**\n * React 18+. Used only to identify *which* component under an instrumented node\n * owns the tracked state; `useId` is stable across StrictMode's double render,\n * which a `useRef` token would not be. Older React simply loses the guard.\n */\nconst useOwnerId: () => string =\n typeof (React as { useId?: () => string }).useId === \"function\"\n ? (React as unknown as { useId: () => string }).useId\n : () => \"\";\n\n/**\n * `useState` with a label. The only reliable way to attribute a render to a\n * specific piece of local state without React internals.\n *\n * **Call this inside a component that is itself instrumented** (wrapped with\n * `withRenderDetective`, or using `useRenderDiagnostics`). A hook cannot see\n * which component called it, only the nearest instrumented ancestor — so state\n * named in an uninstrumented descendant would be reported as the ancestor's.\n * The first caller under a given node claims it and later callers are ignored,\n * which stops a descendant from stealing the attribution, but it cannot rescue\n * the case where the ancestor never tracks state of its own.\n */\nexport function useTrackedState<S>(name: string, initial: S | (() => S)): [S, Dispatch<SetStateAction<S>>] {\n const detective = getDetective();\n const node = useContext(AncestryContext);\n const ownerId = useOwnerId();\n const [state, setState] = useState<S>(initial);\n const previous = useRef<S>(state);\n\n if (node && node.stateOwner === undefined) node.stateOwner = ownerId;\n const owns = !node || node.stateOwner === ownerId;\n\n if (node && owns && !Object.is(previous.current, state)) {\n detective.recordStateChange(node, {\n name,\n previous: inspect(previous.current, detective.config.inspection),\n current: inspect(state, detective.config.inspection),\n });\n previous.current = state;\n } else if (!owns) {\n previous.current = state;\n }\n\n return [state, setState];\n}\n\n/**\n * `useEffect` that reports which of **its own declared** dependencies changed.\n *\n * This does not analyse whether the dependency list is correct — that needs\n * static analysis, not runtime observation (see docs/FEASIBILITY.md §9).\n */\nexport function useTrackedEffect(\n name: string,\n effect: () => void | (() => void),\n deps: unknown[],\n): void {\n const detective = getDetective();\n const previous = useRef<unknown[] | undefined>(undefined);\n const changed = useRef<number[]>([]);\n\n if (detective.enabled) {\n const prev = previous.current;\n changed.current = prev\n ? deps.map((d, i) => (Object.is(d, prev[i]) ? -1 : i)).filter((i) => i >= 0)\n : [];\n previous.current = deps;\n }\n\n useEffect(() => {\n if (detective.enabled && detective.config.mode !== \"silent\" && changed.current.length > 0) {\n // eslint-disable-next-line no-console\n console.debug(`[RRD] effect ${name} ran — deps changed at index ${changed.current.join(\", \")}`);\n }\n return effect();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps);\n}\n\n/**\n * Reports when a context value changes, from inside the provider.\n * Returns the value untouched.\n */\nexport function useTrackedContextValue<T>(contextName: string, value: T): T {\n const detective = getDetective();\n const previous = useRef<T | undefined>(undefined);\n const first = useRef(true);\n\n if (detective.enabled) {\n if (first.current) {\n first.current = false;\n } else if (!Object.is(previous.current, value)) {\n const prev = previous.current;\n const equal = shallowEqual(prev, value, detective.config);\n const changedKeys =\n isRecord(prev) && isRecord(value)\n ? diffProps(prev, value, detective.config).changed.map((c) => c.key)\n : [];\n detective.recordContextChange({\n contextName,\n changedKeys,\n referenceOnly: equal === true,\n commitTime: -1,\n });\n }\n previous.current = value;\n }\n\n return value;\n}\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === \"object\" && v !== null && !Array.isArray(v);\n}\n","import { attachConsoleReporter } from \"./console/reporter.js\";\nimport { explainEvents, formatExplanation } from \"./core/explain.js\";\nimport { getDetective } from \"./core/store.js\";\nimport type {\n AppStats,\n ComponentStats,\n DetectiveConfig,\n DetectiveOptions,\n RenderEvent,\n} from \"./core/types.js\";\n\nconst REPORTER = Symbol.for(\"react-render-detective.reporter\");\ntype Global = typeof globalThis & { [REPORTER]?: () => void };\n\n/**\n * Enable diagnostics. Safe to call repeatedly — Fast Refresh, duplicate module\n * copies and re-entrant setup all reconfigure the single instance rather than\n * stacking listeners (§47).\n */\nexport function init(options: DetectiveOptions = {}): void {\n const detective = getDetective();\n detective.init(options);\n\n const g = globalThis as Global;\n g[REPORTER]?.();\n g[REPORTER] = undefined;\n\n if (detective.enabled && detective.config.mode !== \"silent\") {\n g[REPORTER] = attachConsoleReporter(detective);\n } else if (!detective.enabled) {\n // Never fail silently: a disabled detective looks identical to a broken one.\n console.info(\n \"[RRD] Diagnostics are disabled\" +\n (detective.config.enabled === false ? \" (enabled: false, or a production build was detected).\" : \".\"),\n );\n }\n}\n\n/** Update configuration without re-initialising. */\nexport function configure(options: DetectiveOptions): void {\n getDetective().configure(options);\n}\n\nexport function getConfig(): Readonly<DetectiveConfig> {\n return getDetective().config;\n}\n\nexport function isEnabled(): boolean {\n return getDetective().enabled;\n}\n\n/** Oldest → newest, bounded by `maxEvents`. */\nexport function getEvents(): RenderEvent[] {\n return getDetective().getEvents();\n}\n\nexport function getStats(): AppStats {\n return getDetective().getStats();\n}\n\nexport function getComponentStats(name?: string): ComponentStats[] {\n return getDetective().getComponentStats(name);\n}\n\n/** Live event stream. Returns an unsubscribe function. */\nexport function subscribe(listener: (event: RenderEvent) => void): () => void {\n return getDetective().subscribe(listener);\n}\n\n/** Clears recorded events and statistics; instrumentation stays attached. */\nexport function clear(): void {\n getDetective().clear();\n}\n\n/** Full teardown, including configuration. Mainly for tests and HMR disposal. */\nexport function reset(): void {\n const g = globalThis as Global;\n g[REPORTER]?.();\n g[REPORTER] = undefined;\n getDetective().reset();\n}\n\n/**\n * The flagship answer: aggregate every recorded render of one component into a\n * single, human explanation. Returns `undefined` if nothing was recorded.\n */\nexport function explain(componentName: string): string | undefined {\n const explanation = explainEvents(componentName, getEvents());\n return explanation ? formatExplanation(explanation) : undefined;\n}\n\n/** Structured form of `explain`, for building UIs on top. */\nexport function explainStructured(componentName: string) {\n return explainEvents(componentName, getEvents());\n}\n\n/** Prints the application-level dashboard (§53). */\nexport function printStats(): void {\n const s = getStats();\n const lines = [\n \"React Render Detective\",\n \"\",\n `Components ${s.components}`,\n `Total renders ${s.totalRenders}`,\n `Total render time ${s.totalRenderTime.toFixed(1)}ms`,\n `Slow renders ${s.slowRenders}`,\n `Potentially avoidable ${s.potentiallyAvoidableRenders}`,\n ];\n if (s.devReplays > 0) {\n lines.push(`Development replays ${s.devReplays} (StrictMode / discarded — not counted above)`);\n }\n if (s.mostExpensive.length > 0) {\n lines.push(\"\", \"Top by cumulative render time\");\n for (const [i, c] of s.mostExpensive.slice(0, 5).entries()) {\n lines.push(\n `${String(i + 1).padStart(2)}. ${c.name.padEnd(22)} ${String(c.renderCount).padStart(6)} renders ${c.totalSelfDuration\n .toFixed(1)\n .padStart(8)}ms (avg ${c.averageSelfDuration.toFixed(1)}ms, p95 ${c.p95SelfDuration.toFixed(1)}ms)`,\n );\n }\n }\n console.log(lines.join(\"\\n\"));\n}\n\n/** Namespaced form, matching the documented `ReactRenderDetective.init()` usage. */\nexport const ReactRenderDetective = {\n init,\n configure,\n getConfig,\n isEnabled,\n getEvents,\n getStats,\n getComponentStats,\n subscribe,\n clear,\n reset,\n explain,\n explainStructured,\n printStats,\n};\n\nexport { withRenderDetective } from \"./react/withRenderDetective.js\";\nexport { RenderDetective } from \"./react/RenderDetective.js\";\nexport type { RenderDetectiveProps } from \"./react/RenderDetective.js\";\nexport {\n useRenderDiagnostics,\n useTrackedState,\n useTrackedEffect,\n useTrackedContextValue,\n} from \"./react/hooks.js\";\nexport type { TrackOptions } from \"./react/instrument.js\";\nexport { explainEvents, formatExplanation } from \"./core/explain.js\";\nexport type { Explanation } from \"./core/explain.js\";\nexport type * from \"./core/types.js\";\n"]}
1
+ {"version":3,"sources":["../src/console/reporter.ts","../src/react/ancestry.ts","../src/react/instrument.tsx","../src/react/withRenderDetective.tsx","../src/react/RenderDetective.tsx","../src/react/hooks.ts","../src/index.ts"],"names":["formatInspected","createContext","getDetective","useContext","useState","useLayoutEffect","useCallback","jsx","Profiler","useRef","useEffect","React","inspect","shallowEqual","diffProps","componentName","explainEvents","formatExplanation"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,IAAM,IAAA,GAA+B;AAAA,EACnC,MAAA,EAAQ,MAAA;AAAA,EACR,OAAA,EAAS,QAAA;AAAA,EACT,IAAA,EAAM,QAAA;AAAA,EACN,WAAA,EAAa,cAAA;AAAA,EACb,QAAA,EAAU;AACZ,CAAA;AAGO,SAAS,sBAAsB,SAAA,EAAkC;AACtE,EAAA,OAAO,SAAA,CAAU,SAAA,CAAU,CAAC,KAAA,KAAU;AACpC,IAAA,MAAM,IAAA,GAAO,UAAU,MAAA,CAAO,IAAA;AAC9B,IAAA,IAAI,SAAS,QAAA,EAAU;AACvB,IAAA,IAAI;AACF,MAAA,IAAI,IAAA,KAAS,SAAA,EAAW,YAAA,CAAa,KAAK,CAAA;AAAA,WACrC,YAAA,CAAa,KAAA,EAAO,SAAA,CAAU,MAAA,CAAO,mBAAmB,CAAA;AAAA,IAC/D,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF,CAAC,CAAA;AACH;AAEA,SAAS,YAAA,CAAa,OAAoB,aAAA,EAA6B;AACrE,EAAA,MAAM,EAAE,SAAA,EAAW,OAAA,EAAS,SAAA,EAAU,GAAI,KAAA;AAC1C,EAAA,MAAM,OAAA,GAAU,KAAA,CAAM,YAAA,CAAa,GAAA,CAAI,CAAC,MAAM,CAAA,CAAE,GAAG,CAAA,CAAE,IAAA,CAAK,IAAI,CAAA;AAC9D,EAAA,MAAM,KAAA,GAAQ;AAAA,IACZ,CAAA,MAAA,EAAS,SAAA,CAAU,IAAI,CAAA,EAAA,EAAK,MAAM,YAAY,CAAA,CAAA;AAAA,IAC9C,CAAA,QAAA,EAAW,KAAA,CAAM,KAAK,CAAC,CAAA,CAAA;AAAA,IACvB,OAAA,GAAU,CAAA,SAAA,EAAY,OAAO,CAAA,CAAA,GAAK,MAAA;AAAA,IAClC,OAAA,CAAQ,kBAAkB,CAAA,GAAI,CAAA,UAAA,EAAa,QAAQ,YAAA,CAAa,OAAA,CAAQ,CAAC,CAAC,CAAA,EAAA,CAAA,GAAO;AAAA,GACnF,CAAE,OAAO,OAAO,CAAA;AAEhB,EAAA,MAAM,IAAA,GAAO,CAAA,EAAG,IAAA,CAAK,SAAA,CAAU,QAAQ,CAAA,IAAK,MAAG,CAAA,CAAA,EAAI,KAAA,CAAM,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA;AACnE,EAAA,MAAM,IAAA,GAAO,QAAQ,YAAA,IAAgB,aAAA;AACrC,EAAA,IAAI,IAAA,EAAM,OAAA,CAAQ,IAAA,CAAK,IAAI,CAAA;AAAA,OACtB,OAAA,CAAQ,IAAI,IAAI,CAAA;AACvB;AAEA,SAAS,aAAa,KAAA,EAA0B;AAC9C,EAAA,MAAM,EAAE,SAAA,EAAW,OAAA,EAAQ,GAAI,KAAA;AAC/B,EAAA,MAAM,MAAA,GAAS,CAAA,MAAA,EAAS,KAAA,CAAM,SAAA,CAAU,IAAI,KAAK,KAAA,CAAM,YAAY,CAAA,QAAA,EAAM,SAAA,CAAU,OAAO,CAAA,CAAA;AAE1F,EAAA,MAAM,QAAQ,SAAA,CAAU,QAAA,KAAa,QAAA,GAAW,OAAA,CAAQ,iBAAiB,OAAA,CAAQ,KAAA;AACjF,EAAA,KAAA,CAAM,IAAA,CAAK,SAAS,MAAM,CAAA;AAC1B,EAAA,IAAI;AACF,IAAA,OAAA,CAAQ,GAAA,CAAI,gBAAgB,KAAA,CAAM,KAAK,CAAC,CAAA,cAAA,EAAiB,SAAA,CAAU,UAAU,CAAA,CAAA,CAAG,CAAA;AAChF,IAAA,IAAI,KAAA,CAAM,UAAU,MAAA,EAAQ,OAAA,CAAQ,IAAI,CAAA,aAAA,EAAgB,KAAA,CAAM,SAAA,CAAU,MAAM,CAAA,CAAE,CAAA;AAChF,IAAA,IAAI,MAAM,MAAA,EAAQ;AAChB,MAAA,OAAA,CAAQ,GAAA;AAAA,QACN,gBAAgB,KAAA,CAAM,MAAA,CAAO,IAAI,CAAA,EAAG,KAAA,CAAM,OAAO,MAAA,GAAS,CAAA,EAAA,EAAK,KAAA,CAAM,MAAA,CAAO,MAAM,CAAA,CAAA,CAAA,GAAM,EAAE,GACrF,KAAA,CAAM,cAAA,GAAiB,uCAAkC,2BAAsB,CAAA;AAAA,OACtF;AAAA,IACF;AACA,IAAA,OAAA,CAAQ,GAAA;AAAA,MACN,CAAA,kBAAA,EAAqB,QAAQ,YAAA,CAAa,OAAA,CAAQ,CAAC,CAAC,CAAA,gBAAA,EAAgB,OAAA,CAAQ,eAAA,CAAgB,OAAA,CAAQ,CAAC,CAAC,CAAA,EAAA,CAAA,IACnG,OAAA,CAAQ,8BAA8B,CAAA,GACnC,CAAA,EAAA,EAAK,QAAQ,2BAAA,CAA4B,OAAA,CAAQ,CAAC,CAAC,CAAA,4BAAA,CAAA,GACnD,EAAA;AAAA,KACR;AAEA,IAAA,IAAI,KAAA,CAAM,YAAA,CAAa,MAAA,GAAS,CAAA,EAAG;AACjC,MAAA,OAAA,CAAQ,cAAA,CAAe,CAAA,eAAA,EAAkB,KAAA,CAAM,YAAA,CAAa,MAAM,CAAA,CAAA,CAAG,CAAA;AACrE,MAAA,KAAA,MAAW,KAAK,KAAA,CAAM,YAAA,UAAsB,GAAA,CAAI,QAAA,CAAS,CAAC,CAAC,CAAA;AAC3D,MAAA,OAAA,CAAQ,QAAA,EAAS;AAAA,IACnB;AACA,IAAA,IAAI,KAAA,CAAM,cAAA,CAAe,MAAA,GAAS,CAAA,EAAG;AACnC,MAAA,OAAA,CAAQ,IAAI,CAAA,aAAA,EAAgB,KAAA,CAAM,eAAe,IAAA,CAAK,IAAI,CAAC,CAAA,CAAE,CAAA;AAAA,IAC/D;AACA,IAAA,IAAI,KAAA,CAAM,YAAA,CAAa,MAAA,GAAS,CAAA,EAAG;AACjC,MAAA,OAAA,CAAQ,GAAA;AAAA,QACN,CAAA,aAAA,EAAgB,MAAM,YAAA,CACnB,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,EAAG,CAAA,CAAE,IAAI,CAAA,EAAA,EAAKA,iCAAA,CAAgB,EAAE,QAAQ,CAAC,CAAA,QAAA,EAAMA,iCAAA,CAAgB,CAAA,CAAE,OAAO,CAAC,CAAA,CAAE,CAAA,CACtF,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,OACf;AAAA,IACF;AACA,IAAA,IAAI,KAAA,CAAM,cAAA,CAAe,MAAA,GAAS,CAAA,EAAG;AACnC,MAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,aAAA,EAAgB,KAAA,CAAM,cAAA,CAAe,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,WAAW,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC,CAAA,uBAAA,CAAyB,CAAA;AAAA,IAChH;AAEA,IAAA,OAAA,CAAQ,eAAe,UAAU,CAAA;AACjC,IAAA,KAAA,MAAW,QAAQ,SAAA,CAAU,QAAA,UAAkB,GAAA,CAAI,CAAA,OAAA,EAAK,IAAI,CAAA,CAAE,CAAA;AAC9D,IAAA,OAAA,CAAQ,QAAA,EAAS;AAEjB,IAAA,IAAI,UAAU,UAAA,EAAY,OAAA,CAAQ,IAAI,CAAA,aAAA,EAAgB,SAAA,CAAU,UAAU,CAAA,CAAE,CAAA;AAC5E,IAAA,IAAI,SAAA,CAAU,oBAAA,EAAsB,OAAA,CAAQ,GAAA,CAAI,2CAA2C,CAAA;AAAA,EAC7F,CAAA,SAAE;AACA,IAAA,OAAA,CAAQ,QAAA,EAAS;AAAA,EACnB;AACF;AAEA,SAAS,SAAS,CAAA,EAAuB;AACvC,EAAA,MAAM,IAAA,GAAO,CAAA,EAAG,CAAA,CAAE,GAAG,CAAA,EAAA,EAAK,EAAE,SAAS,CAAA,SAAA,EAAO,YAAA,CAAa,CAAC,CAAC,CAAA,CAAA;AAC3D,EAAA,IAAI,CAAA,CAAE,IAAA,KAAS,OAAA,EAAS,OAAO,CAAA,EAAG,IAAI,CAAA,EAAA,EAAKA,iCAAA,CAAgB,CAAA,CAAE,OAAO,CAAC,CAAA,CAAA;AACrE,EAAA,IAAI,CAAA,CAAE,IAAA,KAAS,SAAA,EAAW,OAAO,CAAA,EAAG,IAAI,CAAA,MAAA,EAASA,iCAAA,CAAgB,CAAA,CAAE,QAAQ,CAAC,CAAA,CAAA;AAC5E,EAAA,OAAO,GAAG,IAAI;AAAA,cAAA,EAAmBA,iCAAA,CAAgB,CAAA,CAAE,QAAQ,CAAC;AAAA,cAAA,EAAmBA,iCAAA,CAAgB,CAAA,CAAE,OAAO,CAAC,CAAA,CAAA;AAC3G;AAEA,SAAS,aAAa,CAAA,EAAuB;AAC3C,EAAA,QAAQ,EAAE,IAAA;AAAM,IACd,KAAK,OAAA;AACH,MAAA,OAAO,OAAA;AAAA,IACT,KAAK,SAAA;AACH,MAAA,OAAO,SAAA;AAAA,IACT,KAAK,OAAA;AACH,MAAA,OAAO,eAAA;AAAA,IACT,KAAK,WAAA;AACH,MAAA,IAAI,EAAE,SAAA,KAAc,UAAA,EAAY,OAAO,CAAA,CAAE,cAAc,gCAAA,GAAmC,wBAAA;AAC1F,MAAA,OAAO,CAAA,CAAE,YAAA,KAAiB,IAAA,GAAO,uCAAA,GAA0C,mBAAA;AAAA;AAEjF;AAEA,SAAS,MAAM,KAAA,EAA4B;AACzC,EAAA,QAAQ,KAAA,CAAM,UAAU,MAAA;AAAQ,IAC9B,KAAK,OAAA;AACH,MAAA,OAAO,OAAA;AAAA,IACT,KAAK,OAAA;AACH,MAAA,OAAO,KAAA,CAAM,aAAa,KAAA,CAAM,CAAC,MAAM,CAAA,CAAE,IAAA,KAAS,WAAW,CAAA,GAAI,+BAAA,GAAkC,cAAA;AAAA,IACrG,KAAK,QAAA;AACH,MAAA,OAAO,iBAAA;AAAA,IACT,KAAK,SAAA;AACH,MAAA,OAAO,gBAAA;AAAA,IACT,KAAK,OAAA;AACH,MAAA,OAAO,eAAA;AAAA,IACT,KAAK,mBAAA;AACH,MAAA,OAAO,yBAAA;AAAA,IACT,KAAK,SAAA;AACH,MAAA,OAAO,cAAA;AAAA;AAEb;ACzHO,IAAM,eAAA,GAAkBC,oBAAsC,MAAS,CAAA;AAC9E,eAAA,CAAgB,WAAA,GAAc,yBAAA;ACSvB,SAAS,mBAAA,CACd,IAAA,EACA,KAAA,EACA,MAAA,EACsE;AACtE,EAAA,MAAM,YAAYC,8BAAA,EAAa;AAC/B,EAAA,MAAM,MAAA,GAASC,iBAAW,eAAe,CAAA;AAIzC,EAAA,MAAM,CAAC,IAAI,CAAA,GAAIC,cAAA;AAAA,IAAiC,MAC9C,UAAU,OAAA,GAAU,SAAA,CAAU,WAAW,IAAA,EAAM,MAAA,EAAQ,MAAM,CAAA,GAAI;AAAA,GACnE;AAEA,EAAAC,qBAAA,CAAgB,MAAM;AACpB,IAAA,IAAI,CAAC,IAAA,EAAM;AACX,IAAA,SAAA,CAAU,OAAO,IAAI,CAAA;AACrB,IAAA,OAAO,MAAM,SAAA,CAAU,MAAA,CAAO,IAAI,CAAA;AAAA,EACpC,CAAA,EAAG,CAAC,SAAA,EAAW,IAAI,CAAC,CAAA;AAEpB,EAAA,IAAI,IAAA,EAAM;AACR,IAAA,IAAI;AACF,MAAA,SAAA,CAAU,aAAA,CAAc,MAAM,KAAK,CAAA;AAAA,IACrC,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF;AAEA,EAAA,MAAM,QAAA,GAAWC,iBAAA;AAAA,IACf,CAAC,GAAA,EAAK,KAAA,EAAO,cAAA,EAAgB,YAAA,EAAc,WAAW,UAAA,KAAe;AACnE,MAAA,IAAI,CAAC,IAAA,EAAM;AACX,MAAA,IAAI;AACF,QAAA,SAAA,CAAU,aAAa,IAAA,EAAM;AAAA,UAC3B,KAAA;AAAA,UACA,eAAA,EAAiB,cAAA;AAAA,UACjB,YAAA;AAAA,UACA,SAAA;AAAA,UACA;AAAA,SACD,CAAA;AAAA,MACH,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF,CAAA;AAAA,IACA,CAAC,WAAW,IAAI;AAAA,GAClB;AAEA,EAAA,OAAO,EAAE,MAAM,QAAA,EAAS;AAC1B;AAEO,SAAS,kBAAA,CACd,IAAA,EACA,QAAA,EACA,QAAA,EACW;AACX,EAAA,IAAI,CAAC,MAAM,OAAO,QAAA;AAClB,EAAA,uBACEC,cAAA,CAACC,cAAA,EAAA,EAAS,EAAA,EAAI,IAAA,CAAK,EAAA,EAAI,QAAA,EACrB,QAAA,kBAAAD,cAAA,CAAC,eAAA,CAAgB,QAAA,EAAhB,EAAyB,KAAA,EAAO,IAAA,EAAO,UAAS,CAAA,EACnD,CAAA;AAEJ;AAEO,SAAS,aAAA,CAAc,SAAA,EAAoB,QAAA,GAAW,WAAA,EAAqB;AAChF,EAAA,MAAM,CAAA,GAAI,SAAA;AACV,EAAA,IAAI,CAAC,GAAG,OAAO,QAAA;AACf,EAAA,IAAI,OAAO,CAAA,CAAE,WAAA,KAAgB,YAAY,CAAA,CAAE,WAAA,SAAoB,CAAA,CAAE,WAAA;AACjE,EAAA,IAAI,OAAO,CAAA,CAAE,IAAA,KAAS,YAAY,CAAA,CAAE,IAAA,SAAa,CAAA,CAAE,IAAA;AAEnD,EAAA,IAAI,EAAE,IAAA,EAAM,OAAO,aAAA,CAAc,CAAA,CAAE,MAAM,QAAQ,CAAA;AACjD,EAAA,OAAO,QAAA;AACT;ACxFA,IAAM,UAAA,mBAAa,MAAA,CAAO,GAAA,CAAI,gCAAgC,CAAA;AAgBvD,SAAS,mBAAA,CACd,SAAA,EACA,OAAA,GAAwB,EAAC,EACP;AAOlB,EAAA,IAAK,SAAA,CAAyC,UAAU,CAAA,EAAG,OAAO,SAAA;AAElE,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,IAAA,IAAQ,aAAA,CAAc,SAAS,CAAA;AAEpD,EAAA,SAAS,eAAe,KAAA,EAAU;AAChC,IAAA,MAAM,EAAE,MAAM,QAAA,EAAS,GAAI,oBAAoB,IAAA,EAAM,KAAA,EAAkC,QAAQ,MAAM,CAAA;AACrG,IAAA,OAAO,kBAAA,CAAmB,MAAM,QAAA,kBAAUA,eAAC,SAAA,EAAA,EAAW,GAAG,OAAO,CAAE,CAAA;AAAA,EACpE;AAEA,EAAA,cAAA,CAAe,WAAA,GAAc,mBAAmB,IAAI,CAAA,CAAA,CAAA;AACpD,EAAC,cAAA,CAA8C,UAAU,CAAA,GAAI,IAAA;AAC7D,EAAA,OAAO,cAAA;AACT;;;ACvBO,SAAS,eAAA,CAAgB,EAAE,IAAA,EAAM,QAAA,EAAS,EAAoC;AACnF,EAAA,MAAM,EAAE,MAAM,QAAA,EAAS,GAAI,oBAAoB,IAAA,EAAM,EAAE,UAAU,CAAA;AACjE,EAAA,OAAO,kBAAA,CAAmB,IAAA,EAAM,QAAA,EAAU,QAAQ,CAAA;AACpD;ACJO,SAAS,oBAAA,CAAqB,MAAc,KAAA,EAA0D;AAC3G,EAAA,MAAM,YAAYL,8BAAA,EAAa;AAC/B,EAAA,MAAM,MAAA,GAASC,iBAAW,eAAe,CAAA;AACzC,EAAA,MAAM,CAAC,IAAI,CAAA,GAAIC,cAAAA,CAAS,MAAM;AAC5B,IAAA,IAAI,CAAC,SAAA,CAAU,OAAA,EAAS,OAAO,MAAA;AAC/B,IAAA,MAAM,OAAA,GAAU,SAAA,CAAU,UAAA,CAAW,IAAA,EAAM,MAAM,CAAA;AACjD,IAAA,IAAI,OAAA,EAAS,SAAA,CAAU,aAAA,CAAc,GAAA,CAAI,OAAO,CAAA;AAChD,IAAA,OAAO,OAAA;AAAA,EACT,CAAC,CAAA;AACD,EAAA,MAAM,IAAA,GAAOK,aAAgC,MAAS,CAAA;AAEtD,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,IAAA,EAAM;AACX,IAAA,SAAA,CAAU,OAAO,IAAI,CAAA;AACrB,IAAA,MAAM,WAAA,GAAc,SAAA,CAAU,SAAA,CAAU,CAAC,KAAA,KAAU;AACjD,MAAA,IAAI,MAAM,SAAA,CAAU,EAAA,KAAO,IAAA,CAAK,EAAA,OAAS,OAAA,GAAU,KAAA;AAAA,IACrD,CAAC,CAAA;AACD,IAAA,OAAO,MAAM;AACX,MAAA,WAAA,EAAY;AACZ,MAAA,SAAA,CAAU,OAAO,IAAI,CAAA;AAAA,IACvB,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,SAAA,EAAW,IAAI,CAAC,CAAA;AAEpB,EAAA,IAAI,IAAA,EAAM;AACR,IAAA,MAAM,OAAA,GAAU,CAAC,IAAA,CAAK,UAAA;AACtB,IAAA,SAAA,CAAU,aAAA,CAAc,IAAA,EAAM,KAAA,IAAS,EAAE,CAAA;AAGzC,IAAA,SAAA,CAAU,aAAa,IAAA,EAAM;AAAA,MAC3B,KAAA,EAAO,UAAU,OAAA,GAAU,QAAA;AAAA,MAC3B,eAAA,EAAiB,CAAA;AAAA,MACjB,YAAA,EAAc,CAAA;AAAA,MACd,SAAA,EAAW,CAAA;AAAA,MACX,UAAA,EAAY;AAAA,KACb,CAAA;AAAA,EACH;AAEA,EAAA,OAAO,IAAA,CAAK,OAAA;AACd;AAOA,IAAM,UAAA,GACJ,OAA2CC,gBAAA,CAAA,KAAA,KAAU,UAAA,GACHA,yBAC9C,MAAM,EAAA;AAcL,SAAS,eAAA,CAAmB,MAAc,OAAA,EAA0D;AACzG,EAAA,MAAM,YAAYT,8BAAA,EAAa;AAC/B,EAAA,MAAM,IAAA,GAAOC,iBAAW,eAAe,CAAA;AACvC,EAAA,MAAM,UAAU,UAAA,EAAW;AAC3B,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAIC,eAAY,OAAO,CAAA;AAC7C,EAAA,MAAM,QAAA,GAAWK,aAAU,KAAK,CAAA;AAEhC,EAAA,IAAI,IAAA,IAAQ,IAAA,CAAK,UAAA,KAAe,MAAA,OAAgB,UAAA,GAAa,OAAA;AAC7D,EAAA,MAAM,IAAA,GAAO,CAAC,IAAA,IAAQ,IAAA,CAAK,UAAA,KAAe,OAAA;AAE1C,EAAA,IAAI,IAAA,IAAQ,QAAQ,CAAC,MAAA,CAAO,GAAG,QAAA,CAAS,OAAA,EAAS,KAAK,CAAA,EAAG;AACvD,IAAA,SAAA,CAAU,kBAAkB,IAAA,EAAM;AAAA,MAChC,IAAA;AAAA,MACA,UAAUG,yBAAA,CAAQ,QAAA,CAAS,OAAA,EAAS,SAAA,CAAU,OAAO,UAAU,CAAA;AAAA,MAC/D,OAAA,EAASA,yBAAA,CAAQ,KAAA,EAAO,SAAA,CAAU,OAAO,UAAU;AAAA,KACpD,CAAA;AACD,IAAA,QAAA,CAAS,OAAA,GAAU,KAAA;AAAA,EACrB,CAAA,MAAA,IAAW,CAAC,IAAA,EAAM;AAChB,IAAA,QAAA,CAAS,OAAA,GAAU,KAAA;AAAA,EACrB;AAEA,EAAA,OAAO,CAAC,OAAO,QAAQ,CAAA;AACzB;AAQO,SAAS,gBAAA,CACd,IAAA,EACA,MAAA,EACA,IAAA,EACM;AACN,EAAA,MAAM,YAAYV,8BAAA,EAAa;AAC/B,EAAA,MAAM,QAAA,GAAWO,aAA8B,MAAS,CAAA;AACxD,EAAA,MAAM,OAAA,GAAUA,YAAA,CAAiB,EAAE,CAAA;AAEnC,EAAA,IAAI,UAAU,OAAA,EAAS;AACrB,IAAA,MAAM,OAAO,QAAA,CAAS,OAAA;AACtB,IAAA,OAAA,CAAQ,OAAA,GAAU,OACd,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,EAAG,CAAA,KAAO,MAAA,CAAO,EAAA,CAAG,CAAA,EAAG,IAAA,CAAK,CAAC,CAAC,CAAA,GAAI,EAAA,GAAK,CAAE,CAAA,CAAE,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,IAAK,CAAC,CAAA,GACzE,EAAC;AACL,IAAA,QAAA,CAAS,OAAA,GAAU,IAAA;AAAA,EACrB;AAEA,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,SAAA,CAAU,WAAW,SAAA,CAAU,MAAA,CAAO,SAAS,QAAA,IAAY,OAAA,CAAQ,OAAA,CAAQ,MAAA,GAAS,CAAA,EAAG;AAEzF,MAAA,OAAA,CAAQ,KAAA,CAAM,gBAAgB,IAAI,CAAA,kCAAA,EAAgC,QAAQ,OAAA,CAAQ,IAAA,CAAK,IAAI,CAAC,CAAA,CAAE,CAAA;AAAA,IAChG;AACA,IAAA,OAAO,MAAA,EAAO;AAAA,EAEhB,GAAG,IAAI,CAAA;AACT;AAMO,SAAS,sBAAA,CAA0B,aAAqB,KAAA,EAAa;AAC1E,EAAA,MAAM,YAAYR,8BAAA,EAAa;AAC/B,EAAA,MAAM,QAAA,GAAWO,aAAsB,MAAS,CAAA;AAChD,EAAA,MAAM,KAAA,GAAQA,aAAO,IAAI,CAAA;AAEzB,EAAA,IAAI,UAAU,OAAA,EAAS;AACrB,IAAA,IAAI,MAAM,OAAA,EAAS;AACjB,MAAA,KAAA,CAAM,OAAA,GAAU,KAAA;AAAA,IAClB,WAAW,CAAC,MAAA,CAAO,GAAG,QAAA,CAAS,OAAA,EAAS,KAAK,CAAA,EAAG;AAC9C,MAAA,MAAM,OAAO,QAAA,CAAS,OAAA;AACtB,MAAA,MAAM,KAAA,GAAQI,8BAAA,CAAa,IAAA,EAAM,KAAA,EAAO,UAAU,MAAM,CAAA;AACxD,MAAA,MAAM,WAAA,GACJ,SAAS,IAAI,CAAA,IAAK,SAAS,KAAK,CAAA,GAC5BC,4BAAU,IAAA,EAAM,KAAA,EAAO,UAAU,MAAM,CAAA,CAAE,QAAQ,GAAA,CAAI,CAAC,MAAM,CAAA,CAAE,GAAG,IACjE,EAAC;AACP,MAAA,SAAA,CAAU,mBAAA,CAAoB;AAAA,QAC5B,WAAA;AAAA,QACA,WAAA;AAAA,QACA,eAAe,KAAA,KAAU,IAAA;AAAA,QACzB,UAAA,EAAY;AAAA,OACb,CAAA;AAAA,IACH;AACA,IAAA,QAAA,CAAS,OAAA,GAAU,KAAA;AAAA,EACrB;AAEA,EAAA,OAAO,KAAA;AACT;AAEA,SAAS,SAAS,CAAA,EAA0C;AAC1D,EAAA,OAAO,OAAO,MAAM,QAAA,IAAY,CAAA,KAAM,QAAQ,CAAC,KAAA,CAAM,QAAQ,CAAC,CAAA;AAChE;;;AC/JA,IAAM,QAAA,mBAAW,MAAA,CAAO,GAAA,CAAI,iCAAiC,CAAA;AAQtD,SAAS,IAAA,CAAK,OAAA,GAA4B,EAAC,EAAS;AACzD,EAAA,MAAM,YAAYZ,8BAAA,EAAa;AAC/B,EAAA,SAAA,CAAU,KAAK,OAAO,CAAA;AAEtB,EAAA,MAAM,CAAA,GAAI,UAAA;AACV,EAAA,CAAA,CAAE,QAAQ,CAAA,IAAI;AACd,EAAA,CAAA,CAAE,QAAQ,CAAA,GAAI,MAAA;AAEd,EAAA,IAAI,SAAA,CAAU,OAAA,IAAW,SAAA,CAAU,MAAA,CAAO,SAAS,QAAA,EAAU;AAC3D,IAAA,CAAA,CAAE,QAAQ,CAAA,GAAI,qBAAA,CAAsB,SAAS,CAAA;AAAA,EAC/C,CAAA,MAAA,IAAW,CAAC,SAAA,CAAU,OAAA,EAAS;AAE7B,IAAA,OAAA,CAAQ,IAAA;AAAA,MACN,gCAAA,IACG,SAAA,CAAU,MAAA,CAAO,OAAA,KAAY,QAAQ,wDAAA,GAA2D,GAAA;AAAA,KACrG;AAAA,EACF;AACF;AAGO,SAAS,UAAU,OAAA,EAAiC;AACzD,EAAAA,8BAAA,EAAa,CAAE,UAAU,OAAO,CAAA;AAClC;AAEO,SAAS,SAAA,GAAuC;AACrD,EAAA,OAAOA,gCAAa,CAAE,MAAA;AACxB;AAEO,SAAS,SAAA,GAAqB;AACnC,EAAA,OAAOA,gCAAa,CAAE,OAAA;AACxB;AAGO,SAAS,SAAA,GAA2B;AACzC,EAAA,OAAOA,8BAAA,GAAe,SAAA,EAAU;AAClC;AAEO,SAAS,QAAA,GAAqB;AACnC,EAAA,OAAOA,8BAAA,GAAe,QAAA,EAAS;AACjC;AAEO,SAAS,kBAAkB,IAAA,EAAiC;AACjE,EAAA,OAAOA,8BAAA,EAAa,CAAE,iBAAA,CAAkB,IAAI,CAAA;AAC9C;AAGO,SAAS,UAAU,QAAA,EAAoD;AAC5E,EAAA,OAAOA,8BAAA,EAAa,CAAE,SAAA,CAAU,QAAQ,CAAA;AAC1C;AAGO,SAAS,KAAA,GAAc;AAC5B,EAAAA,8BAAA,GAAe,KAAA,EAAM;AACvB;AAGO,SAAS,KAAA,GAAc;AAC5B,EAAA,MAAM,CAAA,GAAI,UAAA;AACV,EAAA,CAAA,CAAE,QAAQ,CAAA,IAAI;AACd,EAAA,CAAA,CAAE,QAAQ,CAAA,GAAI,MAAA;AACd,EAAAA,8BAAA,GAAe,KAAA,EAAM;AACvB;AAMO,SAAS,QAAQa,cAAAA,EAA2C;AACjE,EAAA,MAAM,WAAA,GAAcC,+BAAA,CAAcD,cAAAA,EAAe,SAAA,EAAW,CAAA;AAC5D,EAAA,OAAO,WAAA,GAAcE,mCAAA,CAAkB,WAAW,CAAA,GAAI,MAAA;AACxD;AAGO,SAAS,kBAAkBF,cAAAA,EAAuB;AACvD,EAAA,OAAOC,+BAAA,CAAcD,cAAAA,EAAe,SAAA,EAAW,CAAA;AACjD;AAGO,SAAS,UAAA,GAAmB;AACjC,EAAA,MAAM,IAAI,QAAA,EAAS;AACnB,EAAA,MAAM,KAAA,GAAQ;AAAA,IACZ,wBAAA;AAAA,IACA,EAAA;AAAA,IACA,CAAA,yBAAA,EAA4B,EAAE,UAAU,CAAA,CAAA;AAAA,IACxC,CAAA,yBAAA,EAA4B,EAAE,YAAY,CAAA,CAAA;AAAA,IAC1C,CAAA,yBAAA,EAA4B,CAAA,CAAE,eAAA,CAAgB,OAAA,CAAQ,CAAC,CAAC,CAAA,EAAA,CAAA;AAAA,IACxD,CAAA,yBAAA,EAA4B,EAAE,WAAW,CAAA,CAAA;AAAA,IACzC,CAAA,yBAAA,EAA4B,EAAE,2BAA2B,CAAA;AAAA,GAC3D;AACA,EAAA,IAAI,CAAA,CAAE,aAAa,CAAA,EAAG;AACpB,IAAA,KAAA,CAAM,IAAA,CAAK,CAAA,yBAAA,EAA4B,CAAA,CAAE,UAAU,CAAA,kDAAA,CAA+C,CAAA;AAAA,EACpG;AACA,EAAA,IAAI,CAAA,CAAE,aAAA,CAAc,MAAA,GAAS,CAAA,EAAG;AAC9B,IAAA,KAAA,CAAM,IAAA,CAAK,IAAI,+BAA+B,CAAA;AAC9C,IAAA,KAAA,MAAW,CAAC,CAAA,EAAG,CAAC,CAAA,IAAK,CAAA,CAAE,aAAA,CAAc,KAAA,CAAM,CAAA,EAAG,CAAC,CAAA,CAAE,OAAA,EAAQ,EAAG;AAC1D,MAAA,KAAA,CAAM,IAAA;AAAA,QACJ,GAAG,MAAA,CAAO,CAAA,GAAI,CAAC,CAAA,CAAE,QAAA,CAAS,CAAC,CAAC,CAAA,EAAA,EAAK,EAAE,IAAA,CAAK,MAAA,CAAO,EAAE,CAAC,CAAA,CAAA,EAAI,OAAO,CAAA,CAAE,WAAW,EAAE,QAAA,CAAS,CAAC,CAAC,CAAA,UAAA,EAAa,EAAE,iBAAA,CACnG,OAAA,CAAQ,CAAC,CAAA,CACT,QAAA,CAAS,CAAC,CAAC,CAAA,SAAA,EAAY,EAAE,mBAAA,CAAoB,OAAA,CAAQ,CAAC,CAAC,CAAA,QAAA,EAAW,EAAE,eAAA,CAAgB,OAAA,CAAQ,CAAC,CAAC,CAAA,GAAA;AAAA,OACnG;AAAA,IACF;AAAA,EACF;AACA,EAAA,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,IAAA,CAAK,IAAI,CAAC,CAAA;AAC9B;AAGO,IAAM,oBAAA,GAAuB;AAAA,EAClC,IAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA;AAAA,EACA,iBAAA;AAAA,EACA,SAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,OAAA;AAAA,EACA,iBAAA;AAAA,EACA;AACF","file":"index.cjs","sourcesContent":["import { formatInspected } from \"../core/inspect.js\";\nimport type { Detective } from \"../core/store.js\";\nimport type { PropChange, RenderEvent } from \"../core/types.js\";\n\nconst ICON: Record<string, string> = {\n normal: \"·\",\n monitor: \"•\",\n slow: \"▲\",\n \"very-slow\": \"▲▲\",\n critical: \"■\",\n};\n\n/** Attaches console output. Returns an unsubscribe so init() stays idempotent. */\nexport function attachConsoleReporter(detective: Detective): () => void {\n return detective.subscribe((event) => {\n const mode = detective.config.mode;\n if (mode === \"silent\") return;\n try {\n if (mode === \"verbose\") printVerbose(event);\n else printConcise(event, detective.config.slowRenderThreshold);\n } catch {\n /* ignore */\n }\n });\n}\n\nfunction printConcise(event: RenderEvent, slowThreshold: number): void {\n const { diagnosis, timings, component } = event;\n const changed = event.changedProps.map((c) => c.key).join(\", \");\n const parts = [\n `[RRD] ${component.name} #${event.renderNumber}`,\n `Reason: ${label(event)}`,\n changed ? `Changed: ${changed}` : undefined,\n timings.subtreeDuration > 0 ? `Duration: ${timings.selfDuration.toFixed(1)}ms` : undefined,\n ].filter(Boolean);\n\n const line = `${ICON[diagnosis.severity] ?? \"·\"} ${parts.join(\" \")}`;\n const slow = timings.selfDuration >= slowThreshold;\n if (slow) console.warn(line);\n else console.log(line);\n}\n\nfunction printVerbose(event: RenderEvent): void {\n const { diagnosis, timings } = event;\n const header = `[RRD] ${event.component.name} #${event.renderNumber} — ${diagnosis.summary}`;\n\n const group = diagnosis.severity === \"normal\" ? console.groupCollapsed : console.group;\n group.call(console, header);\n try {\n console.log(`Cause: ${label(event)} (confidence: ${diagnosis.confidence})`);\n if (event.component.source) console.log(`Source: ${event.component.source}`);\n if (event.parent) {\n console.log(\n `Parent: ${event.parent.name}${event.parent.source ? ` (${event.parent.source})` : \"\"}` +\n `${event.parentRendered ? \" — re-rendered in this commit\" : \" — did not re-render\"}`,\n );\n }\n console.log(\n `Cost: self ${timings.selfDuration.toFixed(1)}ms · subtree ${timings.subtreeDuration.toFixed(1)}ms` +\n (timings.accountedDescendantDuration > 0\n ? ` (${timings.accountedDescendantDuration.toFixed(1)}ms in instrumented children)`\n : \"\"),\n );\n\n if (event.changedProps.length > 0) {\n console.groupCollapsed(`Props changed (${event.changedProps.length})`);\n for (const c of event.changedProps) console.log(propLine(c));\n console.groupEnd();\n }\n if (event.unchangedProps.length > 0) {\n console.log(`Props same: ${event.unchangedProps.join(\", \")}`);\n }\n if (event.trackedState.length > 0) {\n console.log(\n `State: ${event.trackedState\n .map((s) => `${s.name}: ${formatInspected(s.previous)} → ${formatInspected(s.current)}`)\n .join(\", \")}`,\n );\n }\n if (event.contextChanges.length > 0) {\n console.log(`Context: ${event.contextChanges.map((c) => c.contextName).join(\", \")} changed in this commit`);\n }\n\n console.groupCollapsed(\"Evidence\");\n for (const line of diagnosis.evidence) console.log(`• ${line}`);\n console.groupEnd();\n\n if (diagnosis.suggestion) console.log(`Next step: ${diagnosis.suggestion}`);\n if (diagnosis.potentiallyAvoidable) console.log(\"Flag: potentially avoidable render\");\n } finally {\n console.groupEnd();\n }\n}\n\nfunction propLine(c: PropChange): string {\n const head = `${c.key} (${c.valueType}) — ${describeKind(c)}`;\n if (c.kind === \"added\") return `${head}: ${formatInspected(c.current)}`;\n if (c.kind === \"removed\") return `${head}: was ${formatInspected(c.previous)}`;\n return `${head}\\n previous: ${formatInspected(c.previous)}\\n current: ${formatInspected(c.current)}`;\n}\n\nfunction describeKind(c: PropChange): string {\n switch (c.kind) {\n case \"added\":\n return \"added\";\n case \"removed\":\n return \"removed\";\n case \"value\":\n return \"value changed\";\n case \"reference\":\n if (c.valueType === \"function\") return c.sourceEqual ? \"new function, identical source\" : \"new function reference\";\n return c.shallowEqual === true ? \"reference changed, contents identical\" : \"reference changed\";\n }\n}\n\nfunction label(event: RenderEvent): string {\n switch (event.diagnosis.reason) {\n case \"mount\":\n return \"mount\";\n case \"props\":\n return event.changedProps.every((c) => c.kind === \"reference\") ? \"prop changed (reference only)\" : \"prop changed\";\n case \"parent\":\n return \"parent rendered\";\n case \"context\":\n return \"context update\";\n case \"state\":\n return \"state changed\";\n case \"state-or-external\":\n return \"state or external store\";\n case \"unknown\":\n return \"undetermined\";\n }\n}\n","import { createContext } from \"react\";\nimport type { NodeRecord } from \"../core/store.js\";\n\n/**\n * Carries the nearest instrumented ancestor down the tree.\n *\n * The value is the node object, created once per mounted instance and **never\n * replaced**. That is deliberate: a context value that changed per render would\n * force every instrumented descendant to re-render, defeating `React.memo` and\n * changing the behaviour of the very app we are measuring.\n */\nexport const AncestryContext = createContext<NodeRecord | undefined>(undefined);\nAncestryContext.displayName = \"RenderDetectiveAncestry\";\n","import { Profiler, useCallback, useContext, useLayoutEffect, useState } from \"react\";\nimport type { ProfilerOnRenderCallback, ReactNode } from \"react\";\nimport { getDetective } from \"../core/store.js\";\nimport type { NodeRecord } from \"../core/store.js\";\nimport type { RenderPhase } from \"../core/types.js\";\nimport { AncestryContext } from \"./ancestry.js\";\n\nexport interface TrackOptions {\n /** Overrides the inferred component name. Required for anonymous components. */\n name?: string;\n /**\n * `path/to/File.tsx:12:2`. Supplied automatically by the build plugin; there\n * is no runtime way to obtain it.\n */\n source?: string;\n}\n\n/**\n * Registers the current component, records this render attempt, and returns the\n * node plus a Profiler callback. All of it is a no-op when disabled.\n */\nexport function useInstrumentedNode(\n name: string,\n props: Record<string, unknown>,\n source?: string,\n): { node: NodeRecord | undefined; onRender: ProfilerOnRenderCallback } {\n const detective = getDetective();\n const parent = useContext(AncestryContext);\n\n // The active/inactive decision is frozen per instance so the element tree\n // shape never changes underneath a mounted component.\n const [node] = useState<NodeRecord | undefined>(() =>\n detective.enabled ? detective.createNode(name, parent, source) : undefined,\n );\n\n useLayoutEffect(() => {\n if (!node) return;\n detective.attach(node);\n return () => detective.detach(node);\n }, [detective, node]);\n\n if (node) {\n try {\n detective.recordAttempt(node, props);\n } catch {\n /* never break the app being debugged (§46) */\n }\n }\n\n const onRender = useCallback<ProfilerOnRenderCallback>(\n (_id, phase, actualDuration, baseDuration, startTime, commitTime) => {\n if (!node) return;\n try {\n detective.recordCommit(node, {\n phase: phase as RenderPhase,\n subtreeDuration: actualDuration,\n baseDuration,\n startTime,\n commitTime,\n });\n } catch {\n /* ignore */\n }\n },\n [detective, node],\n );\n\n return { node, onRender };\n}\n\nexport function renderInstrumented(\n node: NodeRecord | undefined,\n onRender: ProfilerOnRenderCallback,\n children: ReactNode,\n): ReactNode {\n if (!node) return children;\n return (\n <Profiler id={node.id} onRender={onRender}>\n <AncestryContext.Provider value={node}>{children}</AncestryContext.Provider>\n </Profiler>\n );\n}\n\nexport function componentName(component: unknown, fallback = \"Anonymous\"): string {\n const c = component as { displayName?: string; name?: string; type?: unknown } | null;\n if (!c) return fallback;\n if (typeof c.displayName === \"string\" && c.displayName) return c.displayName;\n if (typeof c.name === \"string\" && c.name) return c.name;\n // React.memo / forwardRef wrappers keep the real component on `.type`.\n if (c.type) return componentName(c.type, fallback);\n return fallback;\n}\n","import type { ComponentType } from \"react\";\n\n/** Marks a component as one of ours, so it is never wrapped twice. */\nconst IS_WRAPPER = Symbol.for(\"react-render-detective.wrapper\");\nimport { componentName, renderInstrumented, useInstrumentedNode } from \"./instrument.js\";\nimport type { TrackOptions } from \"./instrument.js\";\n\n/**\n * Wraps a component with full instrumentation: render counting, prop diffing,\n * parent attribution and Profiler timings.\n *\n * This is the most accurate integration mode — the wrapper's render function\n * runs exactly when the wrapped component re-renders, and it receives the same\n * props, so every diagnosis has first-hand evidence.\n *\n * Ref note: on React 19 `ref` is an ordinary prop and passes through the spread.\n * On React 18 and below, wrap with `forwardRef` yourself if the component needs\n * a ref.\n */\nexport function withRenderDetective<P extends object>(\n Component: ComponentType<P>,\n options: TrackOptions = {},\n): ComponentType<P> {\n /*\n * Wrapping a wrapper would make it render itself. That is unreachable through\n * careful hand-wrapping but trivially reachable once a build plugin is\n * wrapping everything, and the failure mode is a stack overflow before first\n * paint — so return the existing wrapper instead.\n */\n if ((Component as { [IS_WRAPPER]?: boolean })[IS_WRAPPER]) return Component;\n\n const name = options.name ?? componentName(Component);\n\n function RenderDetected(props: P) {\n const { node, onRender } = useInstrumentedNode(name, props as Record<string, unknown>, options.source);\n return renderInstrumented(node, onRender, <Component {...props} />);\n }\n\n RenderDetected.displayName = `RenderDetective(${name})`;\n (RenderDetected as { [IS_WRAPPER]?: boolean })[IS_WRAPPER] = true;\n return RenderDetected;\n}\n","import type { ReactNode } from \"react\";\nimport { renderInstrumented, useInstrumentedNode } from \"./instrument.js\";\n\nexport interface RenderDetectiveProps {\n name: string;\n children: ReactNode;\n}\n\n/**\n * Zero-refactor integration: drop it around a subtree.\n *\n * <RenderDetective name=\"UserProfile\"><UserProfile /></RenderDetective>\n *\n * Accuracy note: this boundary only sees the `children` **element**, not the\n * wrapped component's props. It therefore reports timings, parent propagation\n * and children-element churn accurately, but it cannot attribute a render to an\n * individual prop. Use `withRenderDetective` when you want per-prop diagnosis.\n */\nexport function RenderDetective({ name, children }: RenderDetectiveProps): ReactNode {\n const { node, onRender } = useInstrumentedNode(name, { children });\n return renderInstrumented(node, onRender, children);\n}\n","import * as React from \"react\";\nimport { useContext, useEffect, useRef, useState } from \"react\";\nimport type { Dispatch, SetStateAction } from \"react\";\nimport { getDetective } from \"../core/store.js\";\nimport { inspect } from \"../core/inspect.js\";\nimport { diffProps, shallowEqual } from \"../core/compare.js\";\nimport type { RenderEvent } from \"../core/types.js\";\nimport { AncestryContext } from \"./ancestry.js\";\n\n/**\n * In-component diagnostics without wrapping.\n *\n * Limitation, by design: a hook cannot install a `<Profiler>` around its own\n * component, so **no duration is available in this mode** — timings are\n * reported as 0. Everything else (render count, prop diffing, parent\n * attribution) is as accurate as the HOC.\n */\nexport function useRenderDiagnostics(name: string, props?: Record<string, unknown>): RenderEvent | undefined {\n const detective = getDetective();\n const parent = useContext(AncestryContext);\n const [node] = useState(() => {\n if (!detective.enabled) return undefined;\n const created = detective.createNode(name, parent);\n if (created) detective.hookModeNodes.add(created);\n return created;\n });\n const last = useRef<RenderEvent | undefined>(undefined);\n\n useEffect(() => {\n if (!node) return;\n detective.attach(node);\n const unsubscribe = detective.subscribe((event) => {\n if (event.component.id === node.id) last.current = event;\n });\n return () => {\n unsubscribe();\n detective.detach(node);\n };\n }, [detective, node]);\n\n if (node) {\n const isMount = !node.seenCommit;\n detective.recordAttempt(node, props ?? {});\n // No Profiler in hook mode: enqueue with a sentinel commit time, which the\n // flush pass resolves to the next real commit that follows this render.\n detective.recordCommit(node, {\n phase: isMount ? \"mount\" : \"update\",\n subtreeDuration: 0,\n baseDuration: 0,\n startTime: 0,\n commitTime: -1,\n });\n }\n\n return last.current;\n}\n\n/**\n * React 18+. Used only to identify *which* component under an instrumented node\n * owns the tracked state; `useId` is stable across StrictMode's double render,\n * which a `useRef` token would not be. Older React simply loses the guard.\n */\nconst useOwnerId: () => string =\n typeof (React as { useId?: () => string }).useId === \"function\"\n ? (React as unknown as { useId: () => string }).useId\n : () => \"\";\n\n/**\n * `useState` with a label. The only reliable way to attribute a render to a\n * specific piece of local state without React internals.\n *\n * **Call this inside a component that is itself instrumented** (wrapped with\n * `withRenderDetective`, or using `useRenderDiagnostics`). A hook cannot see\n * which component called it, only the nearest instrumented ancestor — so state\n * named in an uninstrumented descendant would be reported as the ancestor's.\n * The first caller under a given node claims it and later callers are ignored,\n * which stops a descendant from stealing the attribution, but it cannot rescue\n * the case where the ancestor never tracks state of its own.\n */\nexport function useTrackedState<S>(name: string, initial: S | (() => S)): [S, Dispatch<SetStateAction<S>>] {\n const detective = getDetective();\n const node = useContext(AncestryContext);\n const ownerId = useOwnerId();\n const [state, setState] = useState<S>(initial);\n const previous = useRef<S>(state);\n\n if (node && node.stateOwner === undefined) node.stateOwner = ownerId;\n const owns = !node || node.stateOwner === ownerId;\n\n if (node && owns && !Object.is(previous.current, state)) {\n detective.recordStateChange(node, {\n name,\n previous: inspect(previous.current, detective.config.inspection),\n current: inspect(state, detective.config.inspection),\n });\n previous.current = state;\n } else if (!owns) {\n previous.current = state;\n }\n\n return [state, setState];\n}\n\n/**\n * `useEffect` that reports which of **its own declared** dependencies changed.\n *\n * This does not analyse whether the dependency list is correct — that needs\n * static analysis, not runtime observation (see docs/FEASIBILITY.md §9).\n */\nexport function useTrackedEffect(\n name: string,\n effect: () => void | (() => void),\n deps: unknown[],\n): void {\n const detective = getDetective();\n const previous = useRef<unknown[] | undefined>(undefined);\n const changed = useRef<number[]>([]);\n\n if (detective.enabled) {\n const prev = previous.current;\n changed.current = prev\n ? deps.map((d, i) => (Object.is(d, prev[i]) ? -1 : i)).filter((i) => i >= 0)\n : [];\n previous.current = deps;\n }\n\n useEffect(() => {\n if (detective.enabled && detective.config.mode !== \"silent\" && changed.current.length > 0) {\n // eslint-disable-next-line no-console\n console.debug(`[RRD] effect ${name} ran — deps changed at index ${changed.current.join(\", \")}`);\n }\n return effect();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps);\n}\n\n/**\n * Reports when a context value changes, from inside the provider.\n * Returns the value untouched.\n */\nexport function useTrackedContextValue<T>(contextName: string, value: T): T {\n const detective = getDetective();\n const previous = useRef<T | undefined>(undefined);\n const first = useRef(true);\n\n if (detective.enabled) {\n if (first.current) {\n first.current = false;\n } else if (!Object.is(previous.current, value)) {\n const prev = previous.current;\n const equal = shallowEqual(prev, value, detective.config);\n const changedKeys =\n isRecord(prev) && isRecord(value)\n ? diffProps(prev, value, detective.config).changed.map((c) => c.key)\n : [];\n detective.recordContextChange({\n contextName,\n changedKeys,\n referenceOnly: equal === true,\n commitTime: -1,\n });\n }\n previous.current = value;\n }\n\n return value;\n}\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === \"object\" && v !== null && !Array.isArray(v);\n}\n","import { attachConsoleReporter } from \"./console/reporter.js\";\nimport { explainEvents, formatExplanation } from \"./core/explain.js\";\nimport { getDetective } from \"./core/store.js\";\nimport type {\n AppStats,\n ComponentStats,\n DetectiveConfig,\n DetectiveOptions,\n RenderEvent,\n} from \"./core/types.js\";\n\nconst REPORTER = Symbol.for(\"react-render-detective.reporter\");\ntype Global = typeof globalThis & { [REPORTER]?: () => void };\n\n/**\n * Enable diagnostics. Safe to call repeatedly — Fast Refresh, duplicate module\n * copies and re-entrant setup all reconfigure the single instance rather than\n * stacking listeners (§47).\n */\nexport function init(options: DetectiveOptions = {}): void {\n const detective = getDetective();\n detective.init(options);\n\n const g = globalThis as Global;\n g[REPORTER]?.();\n g[REPORTER] = undefined;\n\n if (detective.enabled && detective.config.mode !== \"silent\") {\n g[REPORTER] = attachConsoleReporter(detective);\n } else if (!detective.enabled) {\n // Never fail silently: a disabled detective looks identical to a broken one.\n console.info(\n \"[RRD] Diagnostics are disabled\" +\n (detective.config.enabled === false ? \" (enabled: false, or a production build was detected).\" : \".\"),\n );\n }\n}\n\n/** Update configuration without re-initialising. */\nexport function configure(options: DetectiveOptions): void {\n getDetective().configure(options);\n}\n\nexport function getConfig(): Readonly<DetectiveConfig> {\n return getDetective().config;\n}\n\nexport function isEnabled(): boolean {\n return getDetective().enabled;\n}\n\n/** Oldest → newest, bounded by `maxEvents`. */\nexport function getEvents(): RenderEvent[] {\n return getDetective().getEvents();\n}\n\nexport function getStats(): AppStats {\n return getDetective().getStats();\n}\n\nexport function getComponentStats(name?: string): ComponentStats[] {\n return getDetective().getComponentStats(name);\n}\n\n/** Live event stream. Returns an unsubscribe function. */\nexport function subscribe(listener: (event: RenderEvent) => void): () => void {\n return getDetective().subscribe(listener);\n}\n\n/** Clears recorded events and statistics; instrumentation stays attached. */\nexport function clear(): void {\n getDetective().clear();\n}\n\n/** Full teardown, including configuration. Mainly for tests and HMR disposal. */\nexport function reset(): void {\n const g = globalThis as Global;\n g[REPORTER]?.();\n g[REPORTER] = undefined;\n getDetective().reset();\n}\n\n/**\n * The flagship answer: aggregate every recorded render of one component into a\n * single, human explanation. Returns `undefined` if nothing was recorded.\n */\nexport function explain(componentName: string): string | undefined {\n const explanation = explainEvents(componentName, getEvents());\n return explanation ? formatExplanation(explanation) : undefined;\n}\n\n/** Structured form of `explain`, for building UIs on top. */\nexport function explainStructured(componentName: string) {\n return explainEvents(componentName, getEvents());\n}\n\n/** Prints the application-level dashboard (§53). */\nexport function printStats(): void {\n const s = getStats();\n const lines = [\n \"React Render Detective\",\n \"\",\n `Components ${s.components}`,\n `Total renders ${s.totalRenders}`,\n `Total render time ${s.totalRenderTime.toFixed(1)}ms`,\n `Slow renders ${s.slowRenders}`,\n `Potentially avoidable ${s.potentiallyAvoidableRenders}`,\n ];\n if (s.devReplays > 0) {\n lines.push(`Development replays ${s.devReplays} (StrictMode / discarded — not counted above)`);\n }\n if (s.mostExpensive.length > 0) {\n lines.push(\"\", \"Top by cumulative render time\");\n for (const [i, c] of s.mostExpensive.slice(0, 5).entries()) {\n lines.push(\n `${String(i + 1).padStart(2)}. ${c.name.padEnd(22)} ${String(c.renderCount).padStart(6)} renders ${c.totalSelfDuration\n .toFixed(1)\n .padStart(8)}ms (avg ${c.averageSelfDuration.toFixed(1)}ms, p95 ${c.p95SelfDuration.toFixed(1)}ms)`,\n );\n }\n }\n console.log(lines.join(\"\\n\"));\n}\n\n/** Namespaced form, matching the documented `ReactRenderDetective.init()` usage. */\nexport const ReactRenderDetective = {\n init,\n configure,\n getConfig,\n isEnabled,\n getEvents,\n getStats,\n getComponentStats,\n subscribe,\n clear,\n reset,\n explain,\n explainStructured,\n printStats,\n};\n\nexport { withRenderDetective } from \"./react/withRenderDetective.js\";\nexport { RenderDetective } from \"./react/RenderDetective.js\";\nexport type { RenderDetectiveProps } from \"./react/RenderDetective.js\";\nexport {\n useRenderDiagnostics,\n useTrackedState,\n useTrackedEffect,\n useTrackedContextValue,\n} from \"./react/hooks.js\";\nexport type { TrackOptions } from \"./react/instrument.js\";\nexport { explainEvents, formatExplanation } from \"./core/explain.js\";\nexport type { Explanation } from \"./core/explain.js\";\nexport type * from \"./core/types.js\";\n"]}