react-render-detective 0.5.0 → 0.6.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 (49) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +2 -2
  3. package/dist/babel.cjs +2 -2
  4. package/dist/babel.d.cts +11 -0
  5. package/dist/babel.d.ts +11 -0
  6. package/dist/babel.js +1 -1
  7. package/dist/{chunk-DI5STJM4.js → chunk-3TZNM7JO.js} +66 -3
  8. package/dist/chunk-3TZNM7JO.js.map +1 -0
  9. package/dist/{chunk-HOTY7J3X.js → chunk-D7M4UC3Y.js} +26 -2
  10. package/dist/chunk-D7M4UC3Y.js.map +1 -0
  11. package/dist/{chunk-PHYYA67T.cjs → chunk-EXDRRUUQ.cjs} +69 -2
  12. package/dist/chunk-EXDRRUUQ.cjs.map +1 -0
  13. package/dist/{chunk-OSPTERGK.cjs → chunk-LCGXVRJR.cjs} +66 -3
  14. package/dist/chunk-LCGXVRJR.cjs.map +1 -0
  15. package/dist/{chunk-MCIQMYNR.js → chunk-QVHAC7AD.js} +69 -2
  16. package/dist/chunk-QVHAC7AD.js.map +1 -0
  17. package/dist/{chunk-JD4IJ4MN.cjs → chunk-RGT44QQI.cjs} +26 -2
  18. package/dist/chunk-RGT44QQI.cjs.map +1 -0
  19. package/dist/core.cjs +18 -18
  20. package/dist/core.d.cts +13 -3
  21. package/dist/core.d.ts +13 -3
  22. package/dist/core.js +1 -1
  23. package/dist/index.cjs +72 -274
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.d.cts +35 -115
  26. package/dist/index.d.ts +35 -115
  27. package/dist/index.js +41 -237
  28. package/dist/index.js.map +1 -1
  29. package/dist/interactions.cjs +252 -0
  30. package/dist/interactions.cjs.map +1 -0
  31. package/dist/interactions.d.cts +110 -0
  32. package/dist/interactions.d.ts +110 -0
  33. package/dist/interactions.js +241 -0
  34. package/dist/interactions.js.map +1 -0
  35. package/dist/overlay.cjs +5 -5
  36. package/dist/overlay.js +2 -2
  37. package/dist/testing.d.cts +1 -1
  38. package/dist/testing.d.ts +1 -1
  39. package/dist/{types-gl13xwmN.d.cts → types-CRET8EhB.d.cts} +20 -2
  40. package/dist/{types-gl13xwmN.d.ts → types-CRET8EhB.d.ts} +20 -2
  41. package/dist/vite.cjs +2 -2
  42. package/dist/vite.js +1 -1
  43. package/package.json +6 -1
  44. package/dist/chunk-DI5STJM4.js.map +0 -1
  45. package/dist/chunk-HOTY7J3X.js.map +0 -1
  46. package/dist/chunk-JD4IJ4MN.cjs.map +0 -1
  47. package/dist/chunk-MCIQMYNR.js.map +0 -1
  48. package/dist/chunk-OSPTERGK.cjs.map +0 -1
  49. package/dist/chunk-PHYYA67T.cjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/interactions.ts","../src/interactions/index.ts"],"names":["getDetective"],"mappings":"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","file":"interactions.cjs","sourcesContent":["import type { Confidence, RenderEvent } from \"./types.js\";\n\n/**\n * Interaction-scoped attribution.\n *\n * Performance is felt per interaction, not in aggregate — and INP is the metric\n * teams are actually judged on. This joins the two halves: the browser says a\n * keystroke took 240ms, and the render events say which components spent it and\n * why.\n *\n * Uses `PerformanceObserver` with `event` timing, a public browser API. Where it\n * is unsupported (older Safari, jsdom) interaction tracking simply stays empty\n * rather than guessing.\n */\nexport interface InteractionRecord {\n id: string;\n /** `click`, `keydown`, `pointerup`… */\n type: string;\n /** Best-effort description of what was interacted with. */\n target?: string;\n startTime: number;\n /** Browser-reported event duration — the number INP is computed from. */\n durationMs: number;\n /**\n * For a manually measured interaction: how long the synchronous action took.\n * Immune to throttling, unlike the full window, which waits on a frame.\n */\n handlerMs?: number;\n /** Renders committed inside this interaction's window. */\n renders: RenderEvent[];\n /** Sum of self durations for those renders. */\n renderTimeMs: number;\n /** Render time that no observable input change explains. */\n avoidableRenderTimeMs: number;\n}\n\nexport interface InteractionSummary {\n interaction: InteractionRecord;\n /** Components ordered by cost within this interaction. */\n contributors: Array<{ component: string; source?: string; renders: number; totalMs: number; cause: string }>;\n headline: string;\n nextStep: string;\n confidence: Confidence;\n}\n\n/** Entries React commits slightly after the event finishes; allow for that. */\nconst COMMIT_SLACK_MS = 100;\n\n/** Used when requestAnimationFrame cannot fire — a hidden tab, or jsdom. */\nconst FALLBACK_CLOSE_MS = 50;\n\ninterface RawEventTiming {\n name: string;\n startTime: number;\n duration: number;\n target?: string;\n handlerMs?: number;\n}\n\nexport class InteractionTracker {\n private records: InteractionRecord[] = [];\n private observer: { disconnect: () => void } | undefined;\n private nextId = 0;\n\n constructor(private capacity = 50) {}\n\n /** Returns false when the browser cannot report event timing. */\n start(): boolean {\n if (this.observer) return true;\n const PO = (globalThis as { PerformanceObserver?: typeof PerformanceObserver }).PerformanceObserver;\n const supported = PO?.supportedEntryTypes?.includes(\"event\");\n if (!PO || !supported) return false;\n\n try {\n const observer = new PO((list) => {\n for (const entry of list.getEntries()) {\n const timing = entry as PerformanceEntry & { duration: number; target?: Element };\n this.record({\n name: timing.name,\n startTime: timing.startTime,\n duration: timing.duration,\n target: describeTarget(timing.target),\n });\n }\n });\n // 16ms: anything that misses a frame is worth attributing.\n observer.observe({ type: \"event\", buffered: true, durationThreshold: 16 } as PerformanceObserverInit);\n this.observer = observer;\n return true;\n } catch {\n return false;\n }\n }\n\n stop(): void {\n this.observer?.disconnect();\n this.observer = undefined;\n }\n\n /** Is the automatic path available in this browser? */\n get automatic(): boolean {\n return this.observer !== undefined;\n }\n\n /**\n * Time an interaction by hand.\n *\n * The automatic path depends on the Event Timing API, which Safari only\n * gained in 16.4 and which does not fire for synthetic input at all — so\n * anything driven by a test harness records nothing. This measures a specific\n * action instead, up to the paint that follows it, and needs no browser\n * support beyond `performance.now`.\n */\n measure<T>(label: string, action: () => T): T {\n const startTime = now();\n let handlerMs = 0;\n let finished = false;\n const finish = (): void => {\n if (finished) return;\n finished = true;\n this.record({ name: label, startTime, duration: now() - startTime, handlerMs });\n };\n\n let result: T;\n try {\n result = action();\n handlerMs = now() - startTime;\n } catch (error) {\n handlerMs = now() - startTime;\n finish();\n throw error;\n }\n\n /*\n * Close the window after the frame in which React commits and paints.\n *\n * The timer is not belt-and-braces: `requestAnimationFrame` does not fire\n * in a hidden tab, and does not exist in jsdom, so without it an\n * interaction would simply never be recorded. Whichever fires first wins;\n * the fallback is long enough that rAF normally does.\n */\n const raf = (globalThis as { requestAnimationFrame?: (cb: () => void) => void }).requestAnimationFrame;\n if (raf) raf(() => raf(finish));\n setTimeout(finish, FALLBACK_CLOSE_MS);\n\n return result;\n }\n\n /** Exposed for tests and for `measure`. */\n record(timing: RawEventTiming): InteractionRecord {\n const record: InteractionRecord = {\n id: `interaction_${++this.nextId}`,\n type: timing.name,\n target: timing.target,\n startTime: timing.startTime,\n durationMs: timing.duration,\n handlerMs: timing.handlerMs,\n renders: [],\n renderTimeMs: 0,\n avoidableRenderTimeMs: 0,\n };\n this.records.push(record);\n if (this.records.length > this.capacity) this.records.shift();\n return record;\n }\n\n clear(): void {\n this.records.length = 0;\n }\n\n /**\n * Joins render events to interactions by commit time. A render belongs to an\n * interaction when it committed between the event starting and shortly after\n * it finished — React commits just after the event handler returns.\n */\n attribute(events: RenderEvent[]): InteractionRecord[] {\n for (const record of this.records) {\n const from = record.startTime;\n const to = record.startTime + record.durationMs + COMMIT_SLACK_MS;\n record.renders = events.filter((e) => e.timings.commitTime >= from && e.timings.commitTime <= to);\n record.renderTimeMs = record.renders.reduce((a, e) => a + e.timings.selfDuration, 0);\n record.avoidableRenderTimeMs = record.renders\n .filter((e) => e.diagnosis.potentiallyAvoidable)\n .reduce((a, e) => a + e.timings.selfDuration, 0);\n }\n return [...this.records].sort((a, b) => b.durationMs - a.durationMs);\n }\n}\n\nexport function summarise(record: InteractionRecord): InteractionSummary {\n const byComponent = new Map<string, { renders: number; totalMs: number; source?: string; cause: string }>();\n for (const event of record.renders) {\n const entry = byComponent.get(event.component.name) ?? {\n renders: 0,\n totalMs: 0,\n source: event.component.source,\n cause: event.diagnosis.reason,\n };\n entry.renders++;\n entry.totalMs += event.timings.selfDuration;\n byComponent.set(event.component.name, entry);\n }\n\n const contributors = [...byComponent.entries()]\n .map(([component, v]) => ({ component, source: v.source, renders: v.renders, totalMs: v.totalMs, cause: v.cause }))\n .sort((a, b) => b.totalMs - a.totalMs);\n\n const top = contributors[0];\n\n /*\n * A manually measured window waits for a frame, and a throttled or hidden tab\n * can stretch that to hundreds of milliseconds of pure idling. Attributing\n * that to the app would be inventing a problem, so when the window is far\n * larger than the work inside it, reason about the work instead.\n */\n const accounted = (record.handlerMs ?? record.durationMs) + record.renderTimeMs;\n const idleWindow = record.handlerMs !== undefined && record.durationMs > accounted * 3 && record.durationMs - accounted > 100;\n const effectiveMs = idleWindow ? accounted : record.durationMs;\n const share = effectiveMs > 0 ? record.renderTimeMs / effectiveMs : 0;\n\n let headline: string;\n let nextStep: string;\n let confidence: Confidence = \"medium\";\n\n if (idleWindow) {\n headline =\n `${record.type}: ${fmt(record.handlerMs ?? 0)} in the handler and ${fmt(record.renderTimeMs)} rendering. ` +\n `The measured window was ${fmt(record.durationMs)}, but most of that was the page waiting for a frame — ignore it.`;\n nextStep =\n record.renderTimeMs > (record.handlerMs ?? 0)\n ? `Rendering dominates the real work${top ? `; start with ${top.component}` : \"\"}.`\n : \"The handler itself costs more than rendering. Profile the handler, not React.\";\n return { interaction: record, contributors, headline, nextStep, confidence: \"medium\" };\n }\n\n if (record.renders.length === 0) {\n headline = `${record.type} took ${fmt(record.durationMs)}, and no instrumented component rendered inside it.`;\n nextStep =\n \"The cost is somewhere other than React rendering — an event handler, a layout, or an uninstrumented component. Instrument more of the tree to narrow it down.\";\n confidence = \"low\";\n } else if (share >= 0.4 && record.avoidableRenderTimeMs > 0) {\n headline =\n `${record.type} took ${fmt(record.durationMs)}; ${fmt(record.renderTimeMs)} of it was rendering, ` +\n `and ${fmt(record.avoidableRenderTimeMs)} of that had no input change to explain it.`;\n nextStep = top\n ? `Start with ${top.component}${top.source ? ` (${top.source})` : \"\"} — ${fmt(top.totalMs)} across ${top.renders} render${top.renders === 1 ? \"\" : \"s\"}.`\n : \"Look at the top contributor below.\";\n confidence = \"high\";\n } else if (share >= 0.4) {\n headline = `${record.type} took ${fmt(record.durationMs)}; ${fmt(record.renderTimeMs)} of it was rendering, all of it explained by real input changes.`;\n nextStep = \"This is genuine work. Make the renders cheaper rather than fewer — or do less of it per interaction.\";\n confidence = \"high\";\n } else {\n headline = `${record.type} took ${fmt(record.durationMs)}, but only ${fmt(record.renderTimeMs)} was React rendering.`;\n nextStep = \"Most of the cost is outside rendering — event handlers, layout or paint. A browser profile will show more than this tool can.\";\n confidence = \"medium\";\n }\n\n return { interaction: record, contributors, headline, nextStep, confidence };\n}\n\nexport function formatInteraction(summary: InteractionSummary): string {\n const { interaction: i } = summary;\n const lines = [\n `${i.type}${i.target ? ` on ${i.target}` : \"\"} ${fmt(i.durationMs)}`,\n \"\",\n summary.headline,\n ];\n\n if (summary.contributors.length > 0) {\n lines.push(\"\", \"Rendering inside this interaction\");\n for (const c of summary.contributors.slice(0, 8)) {\n lines.push(\n ` ${c.component.padEnd(22)} ${String(c.renders).padStart(4)} render(s) ${fmt(c.totalMs).padStart(8)} ${c.cause}`,\n );\n }\n }\n\n lines.push(\"\", `Next step`, ` ${summary.nextStep}`, \"\", `Confidence: ${summary.confidence}`);\n return lines.join(\"\\n\");\n}\n\nfunction describeTarget(target: unknown): string | undefined {\n if (!target || typeof target !== \"object\") return undefined;\n const el = target as { tagName?: string; id?: string; className?: unknown; textContent?: string | null };\n if (!el.tagName) return undefined;\n const tag = el.tagName.toLowerCase();\n if (el.id) return `${tag}#${el.id}`;\n const className = typeof el.className === \"string\" ? el.className.trim().split(/\\s+/)[0] : undefined;\n if (className) return `${tag}.${className}`;\n const text = el.textContent?.trim().slice(0, 20);\n return text ? `${tag} \"${text}\"` : tag;\n}\n\nconst fmt = (ms: number): string => `${ms.toFixed(1)}ms`;\n\nconst now = (): number =>\n typeof performance !== \"undefined\" && typeof performance.now === \"function\" ? performance.now() : Date.now();\n","/**\n * Interaction and INP attribution — opt-in.\n *\n * Split out of the root entry in 0.6.0. It was started by `init()`, which meant\n * every consumer paid ~2 KB for it whether or not they used it, and the root\n * entry's budget had then been raised three releases running. Splitting was the\n * committed alternative to raising it a fourth time.\n *\n * import { startInteractionTracking, printInteractions } from \"react-render-detective/interactions\";\n * startInteractionTracking();\n */\nimport { formatInteraction, InteractionTracker, summarise } from \"../core/interactions.js\";\nimport type { InteractionRecord, InteractionSummary } from \"../core/interactions.js\";\nimport { getDetective } from \"../core/store.js\";\n\nconst TRACKER = Symbol.for(\"react-render-detective.interactions\");\ntype Global = typeof globalThis & { [TRACKER]?: InteractionTracker };\n\nfunction tracker(): InteractionTracker {\n const g = globalThis as Global;\n if (!g[TRACKER]) {\n const created = new InteractionTracker();\n g[TRACKER] = created;\n // Follow the detective's lifecycle so `clear()` and `reset()` stay coherent\n // without the root entry needing to know this module exists.\n const detective = getDetective();\n detective.registerClearHook(() => created.clear());\n detective.registerResetHook(() => {\n created.stop();\n (globalThis as Global)[TRACKER] = undefined;\n });\n }\n return g[TRACKER] as InteractionTracker;\n}\n\n/**\n * Begin capturing interactions. Returns `false` where the browser cannot report\n * event timing (Safari before 16.4, jsdom) — use `measureInteraction` there.\n */\nexport function startInteractionTracking(): boolean {\n return tracker().start();\n}\n\nexport function stopInteractionTracking(): void {\n tracker().stop();\n}\n\n/** Interactions, slowest first, with the renders committed inside each. */\nexport function getInteractions(): InteractionRecord[] {\n return tracker().attribute(getDetective().getEvents());\n}\n\n/** Structured analysis of one interaction. Defaults to the slowest recorded. */\nexport function explainInteractionStructured(id?: string): InteractionSummary | undefined {\n const records = getInteractions();\n const record = id ? records.find((r) => r.id === id) : records[0];\n return record ? summarise(record) : undefined;\n}\n\nexport function explainInteraction(id?: string): string | undefined {\n const summary = explainInteractionStructured(id);\n return summary ? formatInteraction(summary) : undefined;\n}\n\nexport function printInteractions(limit = 5): void {\n const records = getInteractions().slice(0, limit);\n if (records.length === 0) {\n console.log(\n tracker().automatic\n ? \"No interactions recorded yet. Event timing is working — nothing has taken longer than 16ms.\\n\" +\n \"Synthetic clicks from a test harness never produce these entries; use measureInteraction() there.\"\n : \"This browser does not report event timing (Safari before 16.4, jsdom), or tracking was never started.\\n\" +\n \"Call startInteractionTracking(), or use measureInteraction(label, fn) to time interactions by hand.\",\n );\n return;\n }\n console.log(records.map((r) => formatInteraction(summarise(r))).join(\"\\n\\n\"));\n}\n\n/**\n * Time one interaction explicitly, up to the paint that follows it. Needed\n * wherever the automatic path cannot see: Safari before 16.4, and any synthetic\n * input, which never produces Event Timing entries.\n */\nexport function measureInteraction<T>(label: string, action: () => T): T {\n return tracker().measure(label, action);\n}\n\nexport { InteractionTracker, summarise as summariseInteraction, formatInteraction } from \"../core/interactions.js\";\nexport type { InteractionRecord, InteractionSummary } from \"../core/interactions.js\";\n"]}
@@ -0,0 +1,110 @@
1
+ import { b as RenderEvent, j as Confidence } from './types-CRET8EhB.cjs';
2
+
3
+ /**
4
+ * Interaction-scoped attribution.
5
+ *
6
+ * Performance is felt per interaction, not in aggregate — and INP is the metric
7
+ * teams are actually judged on. This joins the two halves: the browser says a
8
+ * keystroke took 240ms, and the render events say which components spent it and
9
+ * why.
10
+ *
11
+ * Uses `PerformanceObserver` with `event` timing, a public browser API. Where it
12
+ * is unsupported (older Safari, jsdom) interaction tracking simply stays empty
13
+ * rather than guessing.
14
+ */
15
+ interface InteractionRecord {
16
+ id: string;
17
+ /** `click`, `keydown`, `pointerup`… */
18
+ type: string;
19
+ /** Best-effort description of what was interacted with. */
20
+ target?: string;
21
+ startTime: number;
22
+ /** Browser-reported event duration — the number INP is computed from. */
23
+ durationMs: number;
24
+ /**
25
+ * For a manually measured interaction: how long the synchronous action took.
26
+ * Immune to throttling, unlike the full window, which waits on a frame.
27
+ */
28
+ handlerMs?: number;
29
+ /** Renders committed inside this interaction's window. */
30
+ renders: RenderEvent[];
31
+ /** Sum of self durations for those renders. */
32
+ renderTimeMs: number;
33
+ /** Render time that no observable input change explains. */
34
+ avoidableRenderTimeMs: number;
35
+ }
36
+ interface InteractionSummary {
37
+ interaction: InteractionRecord;
38
+ /** Components ordered by cost within this interaction. */
39
+ contributors: Array<{
40
+ component: string;
41
+ source?: string;
42
+ renders: number;
43
+ totalMs: number;
44
+ cause: string;
45
+ }>;
46
+ headline: string;
47
+ nextStep: string;
48
+ confidence: Confidence;
49
+ }
50
+ interface RawEventTiming {
51
+ name: string;
52
+ startTime: number;
53
+ duration: number;
54
+ target?: string;
55
+ handlerMs?: number;
56
+ }
57
+ declare class InteractionTracker {
58
+ private capacity;
59
+ private records;
60
+ private observer;
61
+ private nextId;
62
+ constructor(capacity?: number);
63
+ /** Returns false when the browser cannot report event timing. */
64
+ start(): boolean;
65
+ stop(): void;
66
+ /** Is the automatic path available in this browser? */
67
+ get automatic(): boolean;
68
+ /**
69
+ * Time an interaction by hand.
70
+ *
71
+ * The automatic path depends on the Event Timing API, which Safari only
72
+ * gained in 16.4 and which does not fire for synthetic input at all — so
73
+ * anything driven by a test harness records nothing. This measures a specific
74
+ * action instead, up to the paint that follows it, and needs no browser
75
+ * support beyond `performance.now`.
76
+ */
77
+ measure<T>(label: string, action: () => T): T;
78
+ /** Exposed for tests and for `measure`. */
79
+ record(timing: RawEventTiming): InteractionRecord;
80
+ clear(): void;
81
+ /**
82
+ * Joins render events to interactions by commit time. A render belongs to an
83
+ * interaction when it committed between the event starting and shortly after
84
+ * it finished — React commits just after the event handler returns.
85
+ */
86
+ attribute(events: RenderEvent[]): InteractionRecord[];
87
+ }
88
+ declare function summarise(record: InteractionRecord): InteractionSummary;
89
+ declare function formatInteraction(summary: InteractionSummary): string;
90
+
91
+ /**
92
+ * Begin capturing interactions. Returns `false` where the browser cannot report
93
+ * event timing (Safari before 16.4, jsdom) — use `measureInteraction` there.
94
+ */
95
+ declare function startInteractionTracking(): boolean;
96
+ declare function stopInteractionTracking(): void;
97
+ /** Interactions, slowest first, with the renders committed inside each. */
98
+ declare function getInteractions(): InteractionRecord[];
99
+ /** Structured analysis of one interaction. Defaults to the slowest recorded. */
100
+ declare function explainInteractionStructured(id?: string): InteractionSummary | undefined;
101
+ declare function explainInteraction(id?: string): string | undefined;
102
+ declare function printInteractions(limit?: number): void;
103
+ /**
104
+ * Time one interaction explicitly, up to the paint that follows it. Needed
105
+ * wherever the automatic path cannot see: Safari before 16.4, and any synthetic
106
+ * input, which never produces Event Timing entries.
107
+ */
108
+ declare function measureInteraction<T>(label: string, action: () => T): T;
109
+
110
+ export { type InteractionRecord, type InteractionSummary, InteractionTracker, explainInteraction, explainInteractionStructured, formatInteraction, getInteractions, measureInteraction, printInteractions, startInteractionTracking, stopInteractionTracking, summarise as summariseInteraction };
@@ -0,0 +1,110 @@
1
+ import { b as RenderEvent, j as Confidence } from './types-CRET8EhB.js';
2
+
3
+ /**
4
+ * Interaction-scoped attribution.
5
+ *
6
+ * Performance is felt per interaction, not in aggregate — and INP is the metric
7
+ * teams are actually judged on. This joins the two halves: the browser says a
8
+ * keystroke took 240ms, and the render events say which components spent it and
9
+ * why.
10
+ *
11
+ * Uses `PerformanceObserver` with `event` timing, a public browser API. Where it
12
+ * is unsupported (older Safari, jsdom) interaction tracking simply stays empty
13
+ * rather than guessing.
14
+ */
15
+ interface InteractionRecord {
16
+ id: string;
17
+ /** `click`, `keydown`, `pointerup`… */
18
+ type: string;
19
+ /** Best-effort description of what was interacted with. */
20
+ target?: string;
21
+ startTime: number;
22
+ /** Browser-reported event duration — the number INP is computed from. */
23
+ durationMs: number;
24
+ /**
25
+ * For a manually measured interaction: how long the synchronous action took.
26
+ * Immune to throttling, unlike the full window, which waits on a frame.
27
+ */
28
+ handlerMs?: number;
29
+ /** Renders committed inside this interaction's window. */
30
+ renders: RenderEvent[];
31
+ /** Sum of self durations for those renders. */
32
+ renderTimeMs: number;
33
+ /** Render time that no observable input change explains. */
34
+ avoidableRenderTimeMs: number;
35
+ }
36
+ interface InteractionSummary {
37
+ interaction: InteractionRecord;
38
+ /** Components ordered by cost within this interaction. */
39
+ contributors: Array<{
40
+ component: string;
41
+ source?: string;
42
+ renders: number;
43
+ totalMs: number;
44
+ cause: string;
45
+ }>;
46
+ headline: string;
47
+ nextStep: string;
48
+ confidence: Confidence;
49
+ }
50
+ interface RawEventTiming {
51
+ name: string;
52
+ startTime: number;
53
+ duration: number;
54
+ target?: string;
55
+ handlerMs?: number;
56
+ }
57
+ declare class InteractionTracker {
58
+ private capacity;
59
+ private records;
60
+ private observer;
61
+ private nextId;
62
+ constructor(capacity?: number);
63
+ /** Returns false when the browser cannot report event timing. */
64
+ start(): boolean;
65
+ stop(): void;
66
+ /** Is the automatic path available in this browser? */
67
+ get automatic(): boolean;
68
+ /**
69
+ * Time an interaction by hand.
70
+ *
71
+ * The automatic path depends on the Event Timing API, which Safari only
72
+ * gained in 16.4 and which does not fire for synthetic input at all — so
73
+ * anything driven by a test harness records nothing. This measures a specific
74
+ * action instead, up to the paint that follows it, and needs no browser
75
+ * support beyond `performance.now`.
76
+ */
77
+ measure<T>(label: string, action: () => T): T;
78
+ /** Exposed for tests and for `measure`. */
79
+ record(timing: RawEventTiming): InteractionRecord;
80
+ clear(): void;
81
+ /**
82
+ * Joins render events to interactions by commit time. A render belongs to an
83
+ * interaction when it committed between the event starting and shortly after
84
+ * it finished — React commits just after the event handler returns.
85
+ */
86
+ attribute(events: RenderEvent[]): InteractionRecord[];
87
+ }
88
+ declare function summarise(record: InteractionRecord): InteractionSummary;
89
+ declare function formatInteraction(summary: InteractionSummary): string;
90
+
91
+ /**
92
+ * Begin capturing interactions. Returns `false` where the browser cannot report
93
+ * event timing (Safari before 16.4, jsdom) — use `measureInteraction` there.
94
+ */
95
+ declare function startInteractionTracking(): boolean;
96
+ declare function stopInteractionTracking(): void;
97
+ /** Interactions, slowest first, with the renders committed inside each. */
98
+ declare function getInteractions(): InteractionRecord[];
99
+ /** Structured analysis of one interaction. Defaults to the slowest recorded. */
100
+ declare function explainInteractionStructured(id?: string): InteractionSummary | undefined;
101
+ declare function explainInteraction(id?: string): string | undefined;
102
+ declare function printInteractions(limit?: number): void;
103
+ /**
104
+ * Time one interaction explicitly, up to the paint that follows it. Needed
105
+ * wherever the automatic path cannot see: Safari before 16.4, and any synthetic
106
+ * input, which never produces Event Timing entries.
107
+ */
108
+ declare function measureInteraction<T>(label: string, action: () => T): T;
109
+
110
+ export { type InteractionRecord, type InteractionSummary, InteractionTracker, explainInteraction, explainInteractionStructured, formatInteraction, getInteractions, measureInteraction, printInteractions, startInteractionTracking, stopInteractionTracking, summarise as summariseInteraction };
@@ -0,0 +1,241 @@
1
+ import { getDetective } from './chunk-3TZNM7JO.js';
2
+
3
+ // src/core/interactions.ts
4
+ var COMMIT_SLACK_MS = 100;
5
+ var FALLBACK_CLOSE_MS = 50;
6
+ var InteractionTracker = class {
7
+ constructor(capacity = 50) {
8
+ this.capacity = capacity;
9
+ this.records = [];
10
+ this.nextId = 0;
11
+ }
12
+ /** Returns false when the browser cannot report event timing. */
13
+ start() {
14
+ if (this.observer) return true;
15
+ const PO = globalThis.PerformanceObserver;
16
+ const supported = PO?.supportedEntryTypes?.includes("event");
17
+ if (!PO || !supported) return false;
18
+ try {
19
+ const observer = new PO((list) => {
20
+ for (const entry of list.getEntries()) {
21
+ const timing = entry;
22
+ this.record({
23
+ name: timing.name,
24
+ startTime: timing.startTime,
25
+ duration: timing.duration,
26
+ target: describeTarget(timing.target)
27
+ });
28
+ }
29
+ });
30
+ observer.observe({ type: "event", buffered: true, durationThreshold: 16 });
31
+ this.observer = observer;
32
+ return true;
33
+ } catch {
34
+ return false;
35
+ }
36
+ }
37
+ stop() {
38
+ this.observer?.disconnect();
39
+ this.observer = void 0;
40
+ }
41
+ /** Is the automatic path available in this browser? */
42
+ get automatic() {
43
+ return this.observer !== void 0;
44
+ }
45
+ /**
46
+ * Time an interaction by hand.
47
+ *
48
+ * The automatic path depends on the Event Timing API, which Safari only
49
+ * gained in 16.4 and which does not fire for synthetic input at all — so
50
+ * anything driven by a test harness records nothing. This measures a specific
51
+ * action instead, up to the paint that follows it, and needs no browser
52
+ * support beyond `performance.now`.
53
+ */
54
+ measure(label, action) {
55
+ const startTime = now();
56
+ let handlerMs = 0;
57
+ let finished = false;
58
+ const finish = () => {
59
+ if (finished) return;
60
+ finished = true;
61
+ this.record({ name: label, startTime, duration: now() - startTime, handlerMs });
62
+ };
63
+ let result;
64
+ try {
65
+ result = action();
66
+ handlerMs = now() - startTime;
67
+ } catch (error) {
68
+ handlerMs = now() - startTime;
69
+ finish();
70
+ throw error;
71
+ }
72
+ const raf = globalThis.requestAnimationFrame;
73
+ if (raf) raf(() => raf(finish));
74
+ setTimeout(finish, FALLBACK_CLOSE_MS);
75
+ return result;
76
+ }
77
+ /** Exposed for tests and for `measure`. */
78
+ record(timing) {
79
+ const record = {
80
+ id: `interaction_${++this.nextId}`,
81
+ type: timing.name,
82
+ target: timing.target,
83
+ startTime: timing.startTime,
84
+ durationMs: timing.duration,
85
+ handlerMs: timing.handlerMs,
86
+ renders: [],
87
+ renderTimeMs: 0,
88
+ avoidableRenderTimeMs: 0
89
+ };
90
+ this.records.push(record);
91
+ if (this.records.length > this.capacity) this.records.shift();
92
+ return record;
93
+ }
94
+ clear() {
95
+ this.records.length = 0;
96
+ }
97
+ /**
98
+ * Joins render events to interactions by commit time. A render belongs to an
99
+ * interaction when it committed between the event starting and shortly after
100
+ * it finished — React commits just after the event handler returns.
101
+ */
102
+ attribute(events) {
103
+ for (const record of this.records) {
104
+ const from = record.startTime;
105
+ const to = record.startTime + record.durationMs + COMMIT_SLACK_MS;
106
+ record.renders = events.filter((e) => e.timings.commitTime >= from && e.timings.commitTime <= to);
107
+ record.renderTimeMs = record.renders.reduce((a, e) => a + e.timings.selfDuration, 0);
108
+ record.avoidableRenderTimeMs = record.renders.filter((e) => e.diagnosis.potentiallyAvoidable).reduce((a, e) => a + e.timings.selfDuration, 0);
109
+ }
110
+ return [...this.records].sort((a, b) => b.durationMs - a.durationMs);
111
+ }
112
+ };
113
+ function summarise(record) {
114
+ const byComponent = /* @__PURE__ */ new Map();
115
+ for (const event of record.renders) {
116
+ const entry = byComponent.get(event.component.name) ?? {
117
+ renders: 0,
118
+ totalMs: 0,
119
+ source: event.component.source,
120
+ cause: event.diagnosis.reason
121
+ };
122
+ entry.renders++;
123
+ entry.totalMs += event.timings.selfDuration;
124
+ byComponent.set(event.component.name, entry);
125
+ }
126
+ const contributors = [...byComponent.entries()].map(([component, v]) => ({ component, source: v.source, renders: v.renders, totalMs: v.totalMs, cause: v.cause })).sort((a, b) => b.totalMs - a.totalMs);
127
+ const top = contributors[0];
128
+ const accounted = (record.handlerMs ?? record.durationMs) + record.renderTimeMs;
129
+ const idleWindow = record.handlerMs !== void 0 && record.durationMs > accounted * 3 && record.durationMs - accounted > 100;
130
+ const effectiveMs = idleWindow ? accounted : record.durationMs;
131
+ const share = effectiveMs > 0 ? record.renderTimeMs / effectiveMs : 0;
132
+ let headline;
133
+ let nextStep;
134
+ let confidence = "medium";
135
+ if (idleWindow) {
136
+ headline = `${record.type}: ${fmt(record.handlerMs ?? 0)} in the handler and ${fmt(record.renderTimeMs)} rendering. The measured window was ${fmt(record.durationMs)}, but most of that was the page waiting for a frame \u2014 ignore it.`;
137
+ nextStep = record.renderTimeMs > (record.handlerMs ?? 0) ? `Rendering dominates the real work${top ? `; start with ${top.component}` : ""}.` : "The handler itself costs more than rendering. Profile the handler, not React.";
138
+ return { interaction: record, contributors, headline, nextStep, confidence: "medium" };
139
+ }
140
+ if (record.renders.length === 0) {
141
+ headline = `${record.type} took ${fmt(record.durationMs)}, and no instrumented component rendered inside it.`;
142
+ nextStep = "The cost is somewhere other than React rendering \u2014 an event handler, a layout, or an uninstrumented component. Instrument more of the tree to narrow it down.";
143
+ confidence = "low";
144
+ } else if (share >= 0.4 && record.avoidableRenderTimeMs > 0) {
145
+ headline = `${record.type} took ${fmt(record.durationMs)}; ${fmt(record.renderTimeMs)} of it was rendering, and ${fmt(record.avoidableRenderTimeMs)} of that had no input change to explain it.`;
146
+ nextStep = top ? `Start with ${top.component}${top.source ? ` (${top.source})` : ""} \u2014 ${fmt(top.totalMs)} across ${top.renders} render${top.renders === 1 ? "" : "s"}.` : "Look at the top contributor below.";
147
+ confidence = "high";
148
+ } else if (share >= 0.4) {
149
+ headline = `${record.type} took ${fmt(record.durationMs)}; ${fmt(record.renderTimeMs)} of it was rendering, all of it explained by real input changes.`;
150
+ nextStep = "This is genuine work. Make the renders cheaper rather than fewer \u2014 or do less of it per interaction.";
151
+ confidence = "high";
152
+ } else {
153
+ headline = `${record.type} took ${fmt(record.durationMs)}, but only ${fmt(record.renderTimeMs)} was React rendering.`;
154
+ nextStep = "Most of the cost is outside rendering \u2014 event handlers, layout or paint. A browser profile will show more than this tool can.";
155
+ confidence = "medium";
156
+ }
157
+ return { interaction: record, contributors, headline, nextStep, confidence };
158
+ }
159
+ function formatInteraction(summary) {
160
+ const { interaction: i } = summary;
161
+ const lines = [
162
+ `${i.type}${i.target ? ` on ${i.target}` : ""} ${fmt(i.durationMs)}`,
163
+ "",
164
+ summary.headline
165
+ ];
166
+ if (summary.contributors.length > 0) {
167
+ lines.push("", "Rendering inside this interaction");
168
+ for (const c of summary.contributors.slice(0, 8)) {
169
+ lines.push(
170
+ ` ${c.component.padEnd(22)} ${String(c.renders).padStart(4)} render(s) ${fmt(c.totalMs).padStart(8)} ${c.cause}`
171
+ );
172
+ }
173
+ }
174
+ lines.push("", `Next step`, ` ${summary.nextStep}`, "", `Confidence: ${summary.confidence}`);
175
+ return lines.join("\n");
176
+ }
177
+ function describeTarget(target) {
178
+ if (!target || typeof target !== "object") return void 0;
179
+ const el = target;
180
+ if (!el.tagName) return void 0;
181
+ const tag = el.tagName.toLowerCase();
182
+ if (el.id) return `${tag}#${el.id}`;
183
+ const className = typeof el.className === "string" ? el.className.trim().split(/\s+/)[0] : void 0;
184
+ if (className) return `${tag}.${className}`;
185
+ const text = el.textContent?.trim().slice(0, 20);
186
+ return text ? `${tag} "${text}"` : tag;
187
+ }
188
+ var fmt = (ms) => `${ms.toFixed(1)}ms`;
189
+ var now = () => typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
190
+
191
+ // src/interactions/index.ts
192
+ var TRACKER = /* @__PURE__ */ Symbol.for("react-render-detective.interactions");
193
+ function tracker() {
194
+ const g = globalThis;
195
+ if (!g[TRACKER]) {
196
+ const created = new InteractionTracker();
197
+ g[TRACKER] = created;
198
+ const detective = getDetective();
199
+ detective.registerClearHook(() => created.clear());
200
+ detective.registerResetHook(() => {
201
+ created.stop();
202
+ globalThis[TRACKER] = void 0;
203
+ });
204
+ }
205
+ return g[TRACKER];
206
+ }
207
+ function startInteractionTracking() {
208
+ return tracker().start();
209
+ }
210
+ function stopInteractionTracking() {
211
+ tracker().stop();
212
+ }
213
+ function getInteractions() {
214
+ return tracker().attribute(getDetective().getEvents());
215
+ }
216
+ function explainInteractionStructured(id) {
217
+ const records = getInteractions();
218
+ const record = id ? records.find((r) => r.id === id) : records[0];
219
+ return record ? summarise(record) : void 0;
220
+ }
221
+ function explainInteraction(id) {
222
+ const summary = explainInteractionStructured(id);
223
+ return summary ? formatInteraction(summary) : void 0;
224
+ }
225
+ function printInteractions(limit = 5) {
226
+ const records = getInteractions().slice(0, limit);
227
+ if (records.length === 0) {
228
+ console.log(
229
+ tracker().automatic ? "No interactions recorded yet. Event timing is working \u2014 nothing has taken longer than 16ms.\nSynthetic clicks from a test harness never produce these entries; use measureInteraction() there." : "This browser does not report event timing (Safari before 16.4, jsdom), or tracking was never started.\nCall startInteractionTracking(), or use measureInteraction(label, fn) to time interactions by hand."
230
+ );
231
+ return;
232
+ }
233
+ console.log(records.map((r) => formatInteraction(summarise(r))).join("\n\n"));
234
+ }
235
+ function measureInteraction(label, action) {
236
+ return tracker().measure(label, action);
237
+ }
238
+
239
+ export { InteractionTracker, explainInteraction, explainInteractionStructured, formatInteraction, getInteractions, measureInteraction, printInteractions, startInteractionTracking, stopInteractionTracking, summarise as summariseInteraction };
240
+ //# sourceMappingURL=interactions.js.map
241
+ //# sourceMappingURL=interactions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/interactions.ts","../src/interactions/index.ts"],"names":[],"mappings":"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","file":"interactions.js","sourcesContent":["import type { Confidence, RenderEvent } from \"./types.js\";\n\n/**\n * Interaction-scoped attribution.\n *\n * Performance is felt per interaction, not in aggregate — and INP is the metric\n * teams are actually judged on. This joins the two halves: the browser says a\n * keystroke took 240ms, and the render events say which components spent it and\n * why.\n *\n * Uses `PerformanceObserver` with `event` timing, a public browser API. Where it\n * is unsupported (older Safari, jsdom) interaction tracking simply stays empty\n * rather than guessing.\n */\nexport interface InteractionRecord {\n id: string;\n /** `click`, `keydown`, `pointerup`… */\n type: string;\n /** Best-effort description of what was interacted with. */\n target?: string;\n startTime: number;\n /** Browser-reported event duration — the number INP is computed from. */\n durationMs: number;\n /**\n * For a manually measured interaction: how long the synchronous action took.\n * Immune to throttling, unlike the full window, which waits on a frame.\n */\n handlerMs?: number;\n /** Renders committed inside this interaction's window. */\n renders: RenderEvent[];\n /** Sum of self durations for those renders. */\n renderTimeMs: number;\n /** Render time that no observable input change explains. */\n avoidableRenderTimeMs: number;\n}\n\nexport interface InteractionSummary {\n interaction: InteractionRecord;\n /** Components ordered by cost within this interaction. */\n contributors: Array<{ component: string; source?: string; renders: number; totalMs: number; cause: string }>;\n headline: string;\n nextStep: string;\n confidence: Confidence;\n}\n\n/** Entries React commits slightly after the event finishes; allow for that. */\nconst COMMIT_SLACK_MS = 100;\n\n/** Used when requestAnimationFrame cannot fire — a hidden tab, or jsdom. */\nconst FALLBACK_CLOSE_MS = 50;\n\ninterface RawEventTiming {\n name: string;\n startTime: number;\n duration: number;\n target?: string;\n handlerMs?: number;\n}\n\nexport class InteractionTracker {\n private records: InteractionRecord[] = [];\n private observer: { disconnect: () => void } | undefined;\n private nextId = 0;\n\n constructor(private capacity = 50) {}\n\n /** Returns false when the browser cannot report event timing. */\n start(): boolean {\n if (this.observer) return true;\n const PO = (globalThis as { PerformanceObserver?: typeof PerformanceObserver }).PerformanceObserver;\n const supported = PO?.supportedEntryTypes?.includes(\"event\");\n if (!PO || !supported) return false;\n\n try {\n const observer = new PO((list) => {\n for (const entry of list.getEntries()) {\n const timing = entry as PerformanceEntry & { duration: number; target?: Element };\n this.record({\n name: timing.name,\n startTime: timing.startTime,\n duration: timing.duration,\n target: describeTarget(timing.target),\n });\n }\n });\n // 16ms: anything that misses a frame is worth attributing.\n observer.observe({ type: \"event\", buffered: true, durationThreshold: 16 } as PerformanceObserverInit);\n this.observer = observer;\n return true;\n } catch {\n return false;\n }\n }\n\n stop(): void {\n this.observer?.disconnect();\n this.observer = undefined;\n }\n\n /** Is the automatic path available in this browser? */\n get automatic(): boolean {\n return this.observer !== undefined;\n }\n\n /**\n * Time an interaction by hand.\n *\n * The automatic path depends on the Event Timing API, which Safari only\n * gained in 16.4 and which does not fire for synthetic input at all — so\n * anything driven by a test harness records nothing. This measures a specific\n * action instead, up to the paint that follows it, and needs no browser\n * support beyond `performance.now`.\n */\n measure<T>(label: string, action: () => T): T {\n const startTime = now();\n let handlerMs = 0;\n let finished = false;\n const finish = (): void => {\n if (finished) return;\n finished = true;\n this.record({ name: label, startTime, duration: now() - startTime, handlerMs });\n };\n\n let result: T;\n try {\n result = action();\n handlerMs = now() - startTime;\n } catch (error) {\n handlerMs = now() - startTime;\n finish();\n throw error;\n }\n\n /*\n * Close the window after the frame in which React commits and paints.\n *\n * The timer is not belt-and-braces: `requestAnimationFrame` does not fire\n * in a hidden tab, and does not exist in jsdom, so without it an\n * interaction would simply never be recorded. Whichever fires first wins;\n * the fallback is long enough that rAF normally does.\n */\n const raf = (globalThis as { requestAnimationFrame?: (cb: () => void) => void }).requestAnimationFrame;\n if (raf) raf(() => raf(finish));\n setTimeout(finish, FALLBACK_CLOSE_MS);\n\n return result;\n }\n\n /** Exposed for tests and for `measure`. */\n record(timing: RawEventTiming): InteractionRecord {\n const record: InteractionRecord = {\n id: `interaction_${++this.nextId}`,\n type: timing.name,\n target: timing.target,\n startTime: timing.startTime,\n durationMs: timing.duration,\n handlerMs: timing.handlerMs,\n renders: [],\n renderTimeMs: 0,\n avoidableRenderTimeMs: 0,\n };\n this.records.push(record);\n if (this.records.length > this.capacity) this.records.shift();\n return record;\n }\n\n clear(): void {\n this.records.length = 0;\n }\n\n /**\n * Joins render events to interactions by commit time. A render belongs to an\n * interaction when it committed between the event starting and shortly after\n * it finished — React commits just after the event handler returns.\n */\n attribute(events: RenderEvent[]): InteractionRecord[] {\n for (const record of this.records) {\n const from = record.startTime;\n const to = record.startTime + record.durationMs + COMMIT_SLACK_MS;\n record.renders = events.filter((e) => e.timings.commitTime >= from && e.timings.commitTime <= to);\n record.renderTimeMs = record.renders.reduce((a, e) => a + e.timings.selfDuration, 0);\n record.avoidableRenderTimeMs = record.renders\n .filter((e) => e.diagnosis.potentiallyAvoidable)\n .reduce((a, e) => a + e.timings.selfDuration, 0);\n }\n return [...this.records].sort((a, b) => b.durationMs - a.durationMs);\n }\n}\n\nexport function summarise(record: InteractionRecord): InteractionSummary {\n const byComponent = new Map<string, { renders: number; totalMs: number; source?: string; cause: string }>();\n for (const event of record.renders) {\n const entry = byComponent.get(event.component.name) ?? {\n renders: 0,\n totalMs: 0,\n source: event.component.source,\n cause: event.diagnosis.reason,\n };\n entry.renders++;\n entry.totalMs += event.timings.selfDuration;\n byComponent.set(event.component.name, entry);\n }\n\n const contributors = [...byComponent.entries()]\n .map(([component, v]) => ({ component, source: v.source, renders: v.renders, totalMs: v.totalMs, cause: v.cause }))\n .sort((a, b) => b.totalMs - a.totalMs);\n\n const top = contributors[0];\n\n /*\n * A manually measured window waits for a frame, and a throttled or hidden tab\n * can stretch that to hundreds of milliseconds of pure idling. Attributing\n * that to the app would be inventing a problem, so when the window is far\n * larger than the work inside it, reason about the work instead.\n */\n const accounted = (record.handlerMs ?? record.durationMs) + record.renderTimeMs;\n const idleWindow = record.handlerMs !== undefined && record.durationMs > accounted * 3 && record.durationMs - accounted > 100;\n const effectiveMs = idleWindow ? accounted : record.durationMs;\n const share = effectiveMs > 0 ? record.renderTimeMs / effectiveMs : 0;\n\n let headline: string;\n let nextStep: string;\n let confidence: Confidence = \"medium\";\n\n if (idleWindow) {\n headline =\n `${record.type}: ${fmt(record.handlerMs ?? 0)} in the handler and ${fmt(record.renderTimeMs)} rendering. ` +\n `The measured window was ${fmt(record.durationMs)}, but most of that was the page waiting for a frame — ignore it.`;\n nextStep =\n record.renderTimeMs > (record.handlerMs ?? 0)\n ? `Rendering dominates the real work${top ? `; start with ${top.component}` : \"\"}.`\n : \"The handler itself costs more than rendering. Profile the handler, not React.\";\n return { interaction: record, contributors, headline, nextStep, confidence: \"medium\" };\n }\n\n if (record.renders.length === 0) {\n headline = `${record.type} took ${fmt(record.durationMs)}, and no instrumented component rendered inside it.`;\n nextStep =\n \"The cost is somewhere other than React rendering — an event handler, a layout, or an uninstrumented component. Instrument more of the tree to narrow it down.\";\n confidence = \"low\";\n } else if (share >= 0.4 && record.avoidableRenderTimeMs > 0) {\n headline =\n `${record.type} took ${fmt(record.durationMs)}; ${fmt(record.renderTimeMs)} of it was rendering, ` +\n `and ${fmt(record.avoidableRenderTimeMs)} of that had no input change to explain it.`;\n nextStep = top\n ? `Start with ${top.component}${top.source ? ` (${top.source})` : \"\"} — ${fmt(top.totalMs)} across ${top.renders} render${top.renders === 1 ? \"\" : \"s\"}.`\n : \"Look at the top contributor below.\";\n confidence = \"high\";\n } else if (share >= 0.4) {\n headline = `${record.type} took ${fmt(record.durationMs)}; ${fmt(record.renderTimeMs)} of it was rendering, all of it explained by real input changes.`;\n nextStep = \"This is genuine work. Make the renders cheaper rather than fewer — or do less of it per interaction.\";\n confidence = \"high\";\n } else {\n headline = `${record.type} took ${fmt(record.durationMs)}, but only ${fmt(record.renderTimeMs)} was React rendering.`;\n nextStep = \"Most of the cost is outside rendering — event handlers, layout or paint. A browser profile will show more than this tool can.\";\n confidence = \"medium\";\n }\n\n return { interaction: record, contributors, headline, nextStep, confidence };\n}\n\nexport function formatInteraction(summary: InteractionSummary): string {\n const { interaction: i } = summary;\n const lines = [\n `${i.type}${i.target ? ` on ${i.target}` : \"\"} ${fmt(i.durationMs)}`,\n \"\",\n summary.headline,\n ];\n\n if (summary.contributors.length > 0) {\n lines.push(\"\", \"Rendering inside this interaction\");\n for (const c of summary.contributors.slice(0, 8)) {\n lines.push(\n ` ${c.component.padEnd(22)} ${String(c.renders).padStart(4)} render(s) ${fmt(c.totalMs).padStart(8)} ${c.cause}`,\n );\n }\n }\n\n lines.push(\"\", `Next step`, ` ${summary.nextStep}`, \"\", `Confidence: ${summary.confidence}`);\n return lines.join(\"\\n\");\n}\n\nfunction describeTarget(target: unknown): string | undefined {\n if (!target || typeof target !== \"object\") return undefined;\n const el = target as { tagName?: string; id?: string; className?: unknown; textContent?: string | null };\n if (!el.tagName) return undefined;\n const tag = el.tagName.toLowerCase();\n if (el.id) return `${tag}#${el.id}`;\n const className = typeof el.className === \"string\" ? el.className.trim().split(/\\s+/)[0] : undefined;\n if (className) return `${tag}.${className}`;\n const text = el.textContent?.trim().slice(0, 20);\n return text ? `${tag} \"${text}\"` : tag;\n}\n\nconst fmt = (ms: number): string => `${ms.toFixed(1)}ms`;\n\nconst now = (): number =>\n typeof performance !== \"undefined\" && typeof performance.now === \"function\" ? performance.now() : Date.now();\n","/**\n * Interaction and INP attribution — opt-in.\n *\n * Split out of the root entry in 0.6.0. It was started by `init()`, which meant\n * every consumer paid ~2 KB for it whether or not they used it, and the root\n * entry's budget had then been raised three releases running. Splitting was the\n * committed alternative to raising it a fourth time.\n *\n * import { startInteractionTracking, printInteractions } from \"react-render-detective/interactions\";\n * startInteractionTracking();\n */\nimport { formatInteraction, InteractionTracker, summarise } from \"../core/interactions.js\";\nimport type { InteractionRecord, InteractionSummary } from \"../core/interactions.js\";\nimport { getDetective } from \"../core/store.js\";\n\nconst TRACKER = Symbol.for(\"react-render-detective.interactions\");\ntype Global = typeof globalThis & { [TRACKER]?: InteractionTracker };\n\nfunction tracker(): InteractionTracker {\n const g = globalThis as Global;\n if (!g[TRACKER]) {\n const created = new InteractionTracker();\n g[TRACKER] = created;\n // Follow the detective's lifecycle so `clear()` and `reset()` stay coherent\n // without the root entry needing to know this module exists.\n const detective = getDetective();\n detective.registerClearHook(() => created.clear());\n detective.registerResetHook(() => {\n created.stop();\n (globalThis as Global)[TRACKER] = undefined;\n });\n }\n return g[TRACKER] as InteractionTracker;\n}\n\n/**\n * Begin capturing interactions. Returns `false` where the browser cannot report\n * event timing (Safari before 16.4, jsdom) — use `measureInteraction` there.\n */\nexport function startInteractionTracking(): boolean {\n return tracker().start();\n}\n\nexport function stopInteractionTracking(): void {\n tracker().stop();\n}\n\n/** Interactions, slowest first, with the renders committed inside each. */\nexport function getInteractions(): InteractionRecord[] {\n return tracker().attribute(getDetective().getEvents());\n}\n\n/** Structured analysis of one interaction. Defaults to the slowest recorded. */\nexport function explainInteractionStructured(id?: string): InteractionSummary | undefined {\n const records = getInteractions();\n const record = id ? records.find((r) => r.id === id) : records[0];\n return record ? summarise(record) : undefined;\n}\n\nexport function explainInteraction(id?: string): string | undefined {\n const summary = explainInteractionStructured(id);\n return summary ? formatInteraction(summary) : undefined;\n}\n\nexport function printInteractions(limit = 5): void {\n const records = getInteractions().slice(0, limit);\n if (records.length === 0) {\n console.log(\n tracker().automatic\n ? \"No interactions recorded yet. Event timing is working — nothing has taken longer than 16ms.\\n\" +\n \"Synthetic clicks from a test harness never produce these entries; use measureInteraction() there.\"\n : \"This browser does not report event timing (Safari before 16.4, jsdom), or tracking was never started.\\n\" +\n \"Call startInteractionTracking(), or use measureInteraction(label, fn) to time interactions by hand.\",\n );\n return;\n }\n console.log(records.map((r) => formatInteraction(summarise(r))).join(\"\\n\\n\"));\n}\n\n/**\n * Time one interaction explicitly, up to the paint that follows it. Needed\n * wherever the automatic path cannot see: Safari before 16.4, and any synthetic\n * input, which never produces Event Timing entries.\n */\nexport function measureInteraction<T>(label: string, action: () => T): T {\n return tracker().measure(label, action);\n}\n\nexport { InteractionTracker, summarise as summariseInteraction, formatInteraction } from \"../core/interactions.js\";\nexport type { InteractionRecord, InteractionSummary } from \"../core/interactions.js\";\n"]}
package/dist/overlay.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var chunkJD4IJ4MN_cjs = require('./chunk-JD4IJ4MN.cjs');
4
- var chunkOSPTERGK_cjs = require('./chunk-OSPTERGK.cjs');
3
+ var chunkRGT44QQI_cjs = require('./chunk-RGT44QQI.cjs');
4
+ var chunkLCGXVRJR_cjs = require('./chunk-LCGXVRJR.cjs');
5
5
 
6
6
  // src/overlay/overlay.ts
7
7
  var MOUNTED = /* @__PURE__ */ Symbol.for("react-render-detective.overlay");
@@ -15,7 +15,7 @@ function mountOverlay() {
15
15
  } };
16
16
  return noop;
17
17
  }
18
- const detective = chunkOSPTERGK_cjs.getDetective();
18
+ const detective = chunkLCGXVRJR_cjs.getDetective();
19
19
  const root = document.createElement("div");
20
20
  root.setAttribute("data-rrd-overlay", "");
21
21
  root.attachShadow({ mode: "open" });
@@ -77,8 +77,8 @@ function mountOverlay() {
77
77
  </div>`;
78
78
  }).join("");
79
79
  if (selected) {
80
- const explanation = chunkJD4IJ4MN_cjs.explainEvents(selected, detective.getEvents(), detective.lifecycleOf(selected));
81
- detailEl.textContent = explanation ? chunkJD4IJ4MN_cjs.formatExplanation(explanation) : `No recorded renders for ${selected}.`;
80
+ const explanation = chunkRGT44QQI_cjs.explainEvents(selected, detective.getEvents(), detective.lifecycleOf(selected));
81
+ detailEl.textContent = explanation ? chunkRGT44QQI_cjs.formatExplanation(explanation) : `No recorded renders for ${selected}.`;
82
82
  }
83
83
  }
84
84
  const handle = {
package/dist/overlay.js CHANGED
@@ -1,5 +1,5 @@
1
- import { explainEvents, formatExplanation } from './chunk-HOTY7J3X.js';
2
- import { getDetective } from './chunk-DI5STJM4.js';
1
+ import { explainEvents, formatExplanation } from './chunk-D7M4UC3Y.js';
2
+ import { getDetective } from './chunk-3TZNM7JO.js';
3
3
 
4
4
  // src/overlay/overlay.ts
5
5
  var MOUNTED = /* @__PURE__ */ Symbol.for("react-render-detective.overlay");
@@ -1,4 +1,4 @@
1
- import { b as RenderEvent } from './types-gl13xwmN.cjs';
1
+ import { b as RenderEvent } from './types-CRET8EhB.cjs';
2
2
 
3
3
  /**
4
4
  * Render-regression testing.
package/dist/testing.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { b as RenderEvent } from './types-gl13xwmN.js';
1
+ import { b as RenderEvent } from './types-CRET8EhB.js';
2
2
 
3
3
  /**
4
4
  * Render-regression testing.