@atlassian/chippy 0.0.0-stage → 0.0.1-beta.1

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 (126) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +188 -2
  3. package/bin/chippy.mjs +134 -0
  4. package/bin/pack.mjs +184 -0
  5. package/lib/avalanche.js +2570 -0
  6. package/lib/avalanche.js.map +7 -0
  7. package/lib/bootstrap.d.ts +1 -0
  8. package/lib/bootstrap.js +51 -0
  9. package/lib/bootstrap.js.map +7 -0
  10. package/lib/browser.js +15 -0
  11. package/lib/browser.js.map +7 -0
  12. package/lib/extension/avalanche.es.js +5396 -0
  13. package/lib/extension/avalanche.es.js.map +1 -0
  14. package/lib/extension/chippy-page-bridge.js +1833 -0
  15. package/lib/extension/inspect-react.js +216 -0
  16. package/lib/extension/layout-lab.js +1453 -0
  17. package/lib/extension/panel-dom.js +90 -0
  18. package/lib/extension/resolve-probes.js +342 -0
  19. package/lib/mcp-skill/LICENSE +201 -0
  20. package/lib/mcp-skill/README.md +150 -0
  21. package/lib/mcp-skill/SKILL.md +330 -0
  22. package/lib/mcp-skill/browser-scripts/playwright-probes.js +368 -0
  23. package/lib/mcp-skill/browser-scripts/playwright-session.js +114 -0
  24. package/lib/mcp-skill/extension/avalanche.es.js +5396 -0
  25. package/lib/mcp-skill/extension/avalanche.es.js.map +1 -0
  26. package/lib/mcp-skill/extension/avalanche.iife.js +5397 -0
  27. package/lib/mcp-skill/extension/avalanche.iife.js.map +1 -0
  28. package/lib/mcp-skill/extension/background.js +323 -0
  29. package/lib/mcp-skill/extension/chippy-page-bridge.js +1833 -0
  30. package/lib/mcp-skill/extension/content-script.js +167 -0
  31. package/lib/mcp-skill/extension/devtools-hook-shim.js +68 -0
  32. package/lib/mcp-skill/extension/early-bootstrap.js +47 -0
  33. package/lib/mcp-skill/extension/icon-128.png +0 -0
  34. package/lib/mcp-skill/extension/icon-16.png +0 -0
  35. package/lib/mcp-skill/extension/icon-32.png +0 -0
  36. package/lib/mcp-skill/extension/icon-48.png +0 -0
  37. package/lib/mcp-skill/extension/inspect-react.js +216 -0
  38. package/lib/mcp-skill/extension/layout-lab.js +1453 -0
  39. package/lib/mcp-skill/extension/logo.svg +14 -0
  40. package/lib/mcp-skill/extension/manifest.json +69 -0
  41. package/lib/mcp-skill/extension/panel-dom.js +90 -0
  42. package/lib/mcp-skill/extension/panel-settings.js +191 -0
  43. package/lib/mcp-skill/extension/popup.html +41 -0
  44. package/lib/mcp-skill/extension/popup.js +80 -0
  45. package/lib/mcp-skill/extension/probe-descriptor.js +241 -0
  46. package/lib/mcp-skill/extension/resolve-probes.js +342 -0
  47. package/lib/mcp-skill/package.json +1 -0
  48. package/lib/mcp-skill/references/README.md +56 -0
  49. package/lib/mcp-skill/references/active-stimulus.md +85 -0
  50. package/lib/mcp-skill/references/avalanche-detector.md +731 -0
  51. package/lib/mcp-skill/references/bdd-router.md +310 -0
  52. package/lib/mcp-skill/references/browser-runtime-api.md +193 -0
  53. package/lib/mcp-skill/references/command-output-formats.md +1075 -0
  54. package/lib/mcp-skill/references/command-surface.md +183 -0
  55. package/lib/mcp-skill/references/cynefin.md +99 -0
  56. package/lib/mcp-skill/references/debug-hook.md +161 -0
  57. package/lib/mcp-skill/references/fixing-performance.md +490 -0
  58. package/lib/mcp-skill/references/hook-metrics.md +199 -0
  59. package/lib/mcp-skill/references/hooks-inspection.md +61 -0
  60. package/lib/mcp-skill/references/jira-taxonomy.md +81 -0
  61. package/lib/mcp-skill/references/mcp.md +122 -0
  62. package/lib/mcp-skill/references/mode-a-injection.md +68 -0
  63. package/lib/mcp-skill/references/operating-model.md +128 -0
  64. package/lib/mcp-skill/references/playwright-cli-setup.md +105 -0
  65. package/lib/mcp-skill/references/playwright-probes.md +205 -0
  66. package/lib/mcp-skill/references/procedures.md +545 -0
  67. package/lib/mcp-skill/references/recipes.txt +114 -0
  68. package/lib/mcp-skill/references/stability-render.md +428 -0
  69. package/lib/mcp-skill/references/test-mode-playbook.md +193 -0
  70. package/lib/mcp-skill/references/troubleshooting.md +54 -0
  71. package/lib/mcp-skill/scripts/avalanche-analyze.cjs +495 -0
  72. package/lib/mcp-skill/scripts/build-avalanche-command.js +34 -0
  73. package/lib/mcp-skill/scripts/build-fiber-state-command.js +19 -0
  74. package/lib/mcp-skill/scripts/chippy-helper-server.js +1710 -0
  75. package/lib/mcp-skill/scripts/chippy-mcp.cjs +273 -0
  76. package/lib/mcp-skill/scripts/chippy.sh +1218 -0
  77. package/lib/mcp-skill/scripts/format-probe-result.js +34 -0
  78. package/lib/mcp-skill/scripts/helper-access.js +144 -0
  79. package/lib/mcp-skill/scripts/probe-annotation-channel.js +116 -0
  80. package/lib/mcp-skill/scripts/search-recipes.js +248 -0
  81. package/lib/mcp-skill/snippets/README.md +89 -0
  82. package/lib/mcp-skill/snippets/count-context-consumers.js +40 -0
  83. package/lib/mcp-skill/snippets/find-context-providers.js +38 -0
  84. package/lib/mcp-skill/snippets/find-legacy-context.js +47 -0
  85. package/lib/mcp-skill/snippets/find-prop.js +83 -0
  86. package/lib/mcp-skill/snippets/hook-metrics-sample.js +69 -0
  87. package/lib/mcp-skill/snippets/hook-metrics.js +60 -0
  88. package/lib/mcp-skill/snippets/hook-tracker.js +198 -0
  89. package/lib/mcp-skill/snippets/stability-nudge.js +101 -0
  90. package/lib/package.json +1 -0
  91. package/lib/plugin.js +119 -0
  92. package/lib/plugin.js.map +7 -0
  93. package/lib/storybook/avalanche-panel-model.js +79 -0
  94. package/lib/storybook/brand.jsx +6 -0
  95. package/lib/storybook/connection-fallback.jsx +47 -0
  96. package/lib/storybook/constants.js +15 -0
  97. package/lib/storybook/context-card.jsx +171 -0
  98. package/lib/storybook/manager.jsx +403 -0
  99. package/lib/storybook/preset.js +7 -0
  100. package/lib/storybook/preview.js +263 -0
  101. package/lib/storybook/probe-annotation-editor.jsx +52 -0
  102. package/lib/storybook/probe-intent.js +23 -0
  103. package/lib/storybook/setup.js +93 -0
  104. package/lib/types/avalanche/commit-metrics-adapter.d.ts +76 -0
  105. package/lib/types/avalanche/commit-stats-indices.d.ts +41 -0
  106. package/lib/types/avalanche/commit-surface-ownership.d.ts +9 -0
  107. package/lib/types/avalanche/detector.d.ts +183 -0
  108. package/lib/types/avalanche/interaction-capture.d.ts +25 -0
  109. package/lib/types/avalanche/internals.d.ts +189 -0
  110. package/lib/types/avalanche/report-serializer.d.ts +283 -0
  111. package/lib/types/avalanche/reporter.d.ts +610 -0
  112. package/lib/types/chippy-bridge/fiber-source.d.ts +37 -0
  113. package/lib/types/chippy-bridge/react-renderer.d.ts +49 -0
  114. package/lib/types/chippy-bridge/source-map.d.ts +30 -0
  115. package/lib/types/debug-render/core.d.ts +72 -0
  116. package/lib/types/layout-lab/fiber-boundaries.d.ts +51 -0
  117. package/lib/types/layout-lab/fiber-walk.d.ts +131 -0
  118. package/lib/types/layout-lab/marker-context.d.ts +94 -0
  119. package/lib/types/package/avalanche.d.ts +6 -0
  120. package/lib/types/package/browser.d.ts +10 -0
  121. package/lib/types/package/plugin.d.ts +15 -0
  122. package/lib/types/plugins/atl/commit-metrics-adapter.d.ts +65 -0
  123. package/lib/types/plugins/atl/metrics-slot.d.ts +37 -0
  124. package/lib/types/plugins/index.d.ts +49 -0
  125. package/lib/types/plugins/jira/context-provider.d.ts +36 -0
  126. package/package.json +104 -4
@@ -0,0 +1,610 @@
1
+ /**
2
+ * Projects the detector's live fiber history into ranked evidence and the
3
+ * serializable report consumed by bridge commands and offline analysis. The
4
+ * detector owns capture-time truth; this layer owns interpretation and output
5
+ * ordering, so presentation changes do not alter hot-path collection.
6
+ */
7
+ import type { ComponentType, ChangedHook, Fiber, ThrashInfo } from './internals.js';
8
+ import type { ReportDTO } from './report-serializer.js';
9
+ import { type CommitMetricsAdapter, type CommitMetricsDelta, type CommitMetricsSnapshot, type IntegrationMode } from './commit-metrics-adapter.js';
10
+ export type SortBy = 'effect' | 'callback' | 'memo' | 'total';
11
+ export interface Source {
12
+ sourceId: string;
13
+ type: ComponentType;
14
+ depth: number;
15
+ renderCount: number;
16
+ causedUpdates: number;
17
+ collateralUpdates: number;
18
+ maxCollateralUpdates: number;
19
+ maxPenetrationDepth: number;
20
+ updateHistory: Record<number, Record<number, ChangedHook> | 'repeat' | string>;
21
+ collateralByCommit: Record<number, number>;
22
+ fiber?: Fiber;
23
+ /** Changed hook indices from this source's latest self-update; maps debugHook indices to debug-render events. */
24
+ debugHookIndices?: number[];
25
+ /**
26
+ * RESTART WASTE TRACKING - identifies sources contributing to restart waste
27
+ *
28
+ * Restarts = wasted renders (component rendered but work was discarded)
29
+ * Attribution: proportional to sources that fired in each commit with restarts
30
+ *
31
+ * Example: Commit has 1000 restarts, 2 sources fire
32
+ * → Each source attributed 500 restarts
33
+ *
34
+ * High restartWaste = this source triggers commits that cause React to restart work
35
+ */
36
+ restartWaste?: number;
37
+ maxRestartWaste?: number;
38
+ restartWasteByCommit?: Record<number, number>;
39
+ /**
40
+ * Components that were amplified when this source triggered them.
41
+ * These are components that re-rendered with unstable hooks (effects, callbacks, memos).
42
+ * Includes both:
43
+ * - This source itself (if it has unstable hooks)
44
+ * - Descendant components that re-rendered with unstable hooks
45
+ */
46
+ amplified?: Array<{
47
+ /** Component that amplified work */
48
+ sourceId: string;
49
+ type: ComponentType;
50
+ depth: number;
51
+ /** Total effect amplifications across all commits */
52
+ effectCount: number;
53
+ /** Total callback amplifications across all commits */
54
+ callbackCount: number;
55
+ /** Total memo amplifications across all commits */
56
+ memoCount: number;
57
+ /** Total hook amplifications (effect + callback + memo) */
58
+ hooksTotal: number;
59
+ /** Commits where this component amplified work */
60
+ commitIds: number[];
61
+ /** Per-commit breakdown: { commitId: { effects, callbacks, memos } } */
62
+ byCommit: Record<number, {
63
+ effects: number;
64
+ callbacks: number;
65
+ memos: number;
66
+ }>;
67
+ /** Latest amplification details */
68
+ lastAmplification: {
69
+ depth: number;
70
+ thrashedHooks: ThrashInfo[];
71
+ fiber?: Fiber;
72
+ };
73
+ }>;
74
+ }
75
+ export interface Amplifier {
76
+ type: ComponentType;
77
+ renderCount: number;
78
+ instancesTotal: number;
79
+ maxInstancesPerCommit: number;
80
+ hooksTotal: number;
81
+ maxHooksPerCommit: number;
82
+ effectCount: number;
83
+ callbackCount: number;
84
+ memoCount: number;
85
+ lastAmplification: {
86
+ depth: number;
87
+ thrashedHooks: ThrashInfo[];
88
+ fiber?: Fiber;
89
+ };
90
+ }
91
+ /**
92
+ * Linked thrasher - connects thrashing info to source identity.
93
+ *
94
+ * This enables:
95
+ * - thrasher.sourceId → navigate to source
96
+ * - thrasher.isSource → prioritize fixes (source+thrasher = high impact)
97
+ * - thrasher.commitIds → correlate with commitStats
98
+ * - source.thrashing → see hook instability for a source
99
+ */
100
+ export interface AmplifierSource {
101
+ /** Human-readable ID (e.g., "Counter#1@5" in dev, "Unknown#1@5" in prod) */
102
+ sourceId: string;
103
+ /** Component type (function reference) */
104
+ type: ComponentType;
105
+ /** Depth in component tree */
106
+ depth: number;
107
+ /** Collateral damage caused by this source (0 if not tracked) */
108
+ collateralUpdates: number;
109
+ /** Total effect amplifications across all commits */
110
+ effectCount: number;
111
+ /** Total callback amplifications across all commits */
112
+ callbackCount: number;
113
+ /** Total memo amplifications across all commits */
114
+ memoCount: number;
115
+ /** Total hook amplifications (effect + callback + memo) */
116
+ hooksTotal: number;
117
+ /** Commits where this component amplified work */
118
+ commitIds: number[];
119
+ /** Per-commit breakdown: { commitId: { effects, callbacks, memos } } */
120
+ byCommit: Record<number, {
121
+ effects: number;
122
+ callbacks: number;
123
+ memos: number;
124
+ }>;
125
+ /** If not a source, which sourceId caused this to re-render? */
126
+ causedBySourceId: string | null;
127
+ /** Latest amplification details */
128
+ lastAmplification: {
129
+ depth: number;
130
+ thrashedHooks: ThrashInfo[];
131
+ fiber?: Fiber;
132
+ };
133
+ }
134
+ /**
135
+ * Commit stats tuple: [collateral, sources, restarts, performedWork, effects, memos, callbacks, contextUpdates, unmountedComponents, unmountedSubtrees]
136
+ * Use these numbers to find which commits to investigate in DevTools.
137
+ * - collateral: components re-rendered as victims
138
+ * - sources: components that initiated updates (update sources)
139
+ * - restarts: extra renders due to external store mutations, priority changes, etc.
140
+ * - performedWork: components with PerformedWork flag (actual work React processed)
141
+ * - effects/memos/callbacks: thrashing hooks (effects likely cause next commit!)
142
+ * - contextUpdates: number of Context Providers whose value changed this commit
143
+ * - unmountedComponents: total component fibers unmounted this commit
144
+ * - unmountedSubtrees: number of distinct unmount "branch starts" (topmost deleted components)
145
+ */
146
+ export type CommitStats = [
147
+ collateral: number,
148
+ sources: number,
149
+ restarts: number,
150
+ performedWork: number,
151
+ effects: number,
152
+ memos: number,
153
+ callbacks: number,
154
+ contextUpdates: number,
155
+ unmountedComponents: number,
156
+ unmountedSubtrees: number
157
+ ];
158
+ /**
159
+ * Human-readable keys for CommitStats tuple positions.
160
+ * Use with commitStats[commit][commitStatsKeys.collateral]
161
+ */
162
+ export declare const commitStatsKeys: {
163
+ readonly collateral: 0;
164
+ readonly sources: 1;
165
+ readonly restarts: 2;
166
+ readonly performedWork: 3;
167
+ readonly effects: 4;
168
+ readonly memos: 5;
169
+ readonly callbacks: 6;
170
+ readonly contextUpdates: 7;
171
+ readonly unmountedComponents: 8;
172
+ readonly unmountedSubtrees: 9;
173
+ };
174
+ /**
175
+ * Report metadata - profiling context and overhead metrics.
176
+ */
177
+ export interface ReportMeta {
178
+ /** Report format version - increment when changing data structure */
179
+ version: number;
180
+ /** Total commits processed */
181
+ commits: number;
182
+ /** Detector configuration used during profiling */
183
+ config: {
184
+ /** Detection level: 'stats' (minimal), 'sources', 'amplifiers', or 'full' */
185
+ detectionLevel: DetectionLevel;
186
+ /** Effect thrashing sensitivity: 'non-primitive' or 'all' */
187
+ effectThrashMode: 'non-primitive' | 'all';
188
+ /** Whether fibers were captured (for debugging) */
189
+ captureFibers: boolean;
190
+ /** Update history size per source */
191
+ historySize: number;
192
+ };
193
+ /** memoizedUpdaters availability and coverage */
194
+ memoizedUpdatersSupport: {
195
+ available: boolean;
196
+ commitsWithData: number;
197
+ coverage: string;
198
+ };
199
+ /**
200
+ * Which commit-hook integration model produced this report. Determines what
201
+ * is reliably present in `commitStats[N]` and `metricsByCommit[N]`.
202
+ *
203
+ * 'adapter:<id>' — A commit metrics adapter (for example `adapter:atl`) drove
204
+ * the commit callback with host render counters. ALL fields below
205
+ * are populated and `metricsByCommit` is present.
206
+ * 'devtools' — Reconstructed entirely from the React DevTools commit hook
207
+ * (fiber walk only). The fiber-walk fields are real measurements:
208
+ * collateral, sources, performedWork, effects, memos, callbacks,
209
+ * contextUpdates. The render-counter math (restarts) is NOT
210
+ * available — `commitStats[N][2]` will be 0 (semantically
211
+ * "unknown / not measurable in this mode") and `metricsByCommit`
212
+ * is empty. This is normal on stock react-dom.
213
+ * 'none' — Detector wasn't installed when this report was generated.
214
+ *
215
+ * Reports before the adapter seam used `'atl'` for `'adapter:atl'`.
216
+ */
217
+ integrationMode: IntegrationMode;
218
+ /** Id of the commit metrics adapter that drove this report, or null. */
219
+ metricsAdapter: string | null;
220
+ /**
221
+ * Per-field availability map for `commitStats[N]` indices. `false` means the
222
+ * value is a placeholder (typically 0) that consumers should treat as unknown
223
+ * rather than a real measurement of zero.
224
+ *
225
+ * Stable across all commits in a given report — set once from integrationMode.
226
+ */
227
+ dataAvailability: {
228
+ /** [0] perCommit.totalCollateral — reconstructed from fiber walk */
229
+ collateral: boolean;
230
+ /** [1] perCommit.selfUpdated.size — reconstructed from fiber walk */
231
+ sources: boolean;
232
+ /** [2] derived from the adapter's render-counter delta — only in 'adapter:<id>' mode */
233
+ restarts: boolean;
234
+ /** [3] perCommit.committedComponentCount — reconstructed from fiber walk */
235
+ performedWork: boolean;
236
+ /** [4] perCommit.effectCount — reconstructed from fiber walk */
237
+ effects: boolean;
238
+ /** [5] perCommit.memoCount — reconstructed from fiber walk */
239
+ memos: boolean;
240
+ /** [6] perCommit.callbackCount — reconstructed from fiber walk */
241
+ callbacks: boolean;
242
+ /** [7] perCommit.contextUpdates — reconstructed from fiber walk */
243
+ contextUpdates: boolean;
244
+ /** Adapter counters in metricsByCommit — only in 'adapter:<id>' mode */
245
+ metricsByCommit: boolean;
246
+ /** @deprecated Alias of `metricsByCommit`, kept for one release. */
247
+ atlByCommit: boolean;
248
+ };
249
+ /** Amplifier summary (when detectionMode: 'amplifierMetrics') */
250
+ amplifiersMetrics?: {
251
+ totalHookThrashes: number;
252
+ affectedComponentCount: number;
253
+ };
254
+ /**
255
+ * Timing summary - shows detector overhead and commit durations.
256
+ * Use this to justify avalanche-detector overhead in production.
257
+ */
258
+ timing: {
259
+ /** Total time spent in detector across all commits (ms) */
260
+ totalDetectorOverhead: number;
261
+ /** Average detector overhead per commit (ms) */
262
+ avgDetectorOverhead: number;
263
+ /** Total time from first to last commit (ms) */
264
+ totalTimeSpan: number;
265
+ /** Average time between commits (ms) */
266
+ avgTimeBetweenCommits: number;
267
+ };
268
+ }
269
+ export interface HostUnmountSummary {
270
+ elements: number;
271
+ text: number;
272
+ tagsTop: Array<[string, number]>;
273
+ testIdsTop: Array<[string, number]>;
274
+ rolesTop: Array<[string, number]>;
275
+ ariaLabels: string[];
276
+ textHints: string[];
277
+ }
278
+ export interface UnmountSample {
279
+ name: string;
280
+ key: string | null;
281
+ depth: number | null;
282
+ count: number;
283
+ source?: {
284
+ fileName?: string;
285
+ lineNumber?: number;
286
+ columnNumber?: number;
287
+ } | null;
288
+ host?: HostUnmountSummary;
289
+ }
290
+ export type InteractionCaptureLevel = 'off' | 'explicit' | 'noisy' | 'all';
291
+ export interface InteractionActionLocation {
292
+ dom: {
293
+ tag: string | null;
294
+ text: string;
295
+ nearestTestId: string | null;
296
+ };
297
+ targetFiber: {
298
+ name: string | null;
299
+ key: string | null;
300
+ source: {
301
+ fileName?: string;
302
+ lineNumber?: number;
303
+ columnNumber?: number;
304
+ } | null;
305
+ ownerStackKind: 'owners' | 'parents';
306
+ ownerStack: Array<{
307
+ name: string | null;
308
+ key: string | null;
309
+ props: string[];
310
+ hookCount: number;
311
+ hookCountTruncated: boolean;
312
+ hookCountMax: number;
313
+ source: {
314
+ fileName?: string;
315
+ lineNumber?: number;
316
+ columnNumber?: number;
317
+ } | null;
318
+ }>;
319
+ } | null;
320
+ }
321
+ export interface InteractionAction {
322
+ /** Sequential action id within this detector capture. */
323
+ id: number;
324
+ /** Native event name, e.g. click, keydown, wheel. */
325
+ action: string;
326
+ /** performance.now() at event capture time. Kept live-only; DTO emits deltaTime. */
327
+ timestamp: number;
328
+ /** DOM-grounded event target plus target component/ownership stack. */
329
+ location: InteractionActionLocation;
330
+ }
331
+ export type PropChangeKind = 'same' | 'ref-changed' | 'scalar-changed' | 'added' | 'removed';
332
+ export interface PropValueSummary {
333
+ type: string;
334
+ isArray: boolean;
335
+ isFunction: boolean;
336
+ isNull: boolean;
337
+ preview: string;
338
+ display?: string;
339
+ length?: number;
340
+ keysTop?: string[];
341
+ sample?: unknown[];
342
+ }
343
+ export interface PropChangeEvidence {
344
+ name: string;
345
+ kind: PropChangeKind;
346
+ prev: PropValueSummary;
347
+ current: PropValueSummary;
348
+ }
349
+ export interface RenderedComponentEvidence {
350
+ sourceId: string;
351
+ name: string;
352
+ depth: number;
353
+ parentSourceId: string | null;
354
+ ownerSourceId: string | null;
355
+ isSelfSource: boolean;
356
+ propChanges: PropChangeEvidence[];
357
+ hasPropChanges: boolean;
358
+ trasherCounts: {
359
+ effects: number;
360
+ callbacks: number;
361
+ memos: number;
362
+ };
363
+ }
364
+ export interface CommitInteractionAction {
365
+ /** Original action id — must match InteractionAction.id for describe-action lookup. */
366
+ id: number;
367
+ /** Last commit id at the moment this action was observed. */
368
+ lastCommitId: number;
369
+ /** Milliseconds from action capture to the linked commit. */
370
+ deltaTime: number;
371
+ action: string;
372
+ location: InteractionAction['location'];
373
+ }
374
+ export interface Report {
375
+ /** Metadata: version, counts, overhead metrics */
376
+ meta: ReportMeta;
377
+ /** Components that caused updates, sorted by collateral damage */
378
+ sources: Source[];
379
+ /** Bridge to DevTools - maps commit ID to sources that fired in that commit */
380
+ sourcesByCommit: Record<number, Array<{
381
+ sourceId: string;
382
+ type: ComponentType;
383
+ depth: number;
384
+ /** Collateral damage caused by this source in THIS commit */
385
+ collateral: number;
386
+ /** Position in top N sources list (1-based), or null if not in top N */
387
+ rank: number | null;
388
+ /** Index into sources[] array for full accumulated data, or null if not in top N */
389
+ sourceIndex: number | null;
390
+ hooks?: Array<{
391
+ index: number;
392
+ prevValue: unknown;
393
+ currentValue: unknown;
394
+ }>;
395
+ isRepeat?: boolean;
396
+ }>>;
397
+ /**
398
+ * Per-commit metrics to find the needle: [collateral, effects, memos, callbacks]
399
+ * High collateral = avalanche. High effects = likely causes next commit!
400
+ */
401
+ commitStats: Record<number, CommitStats>;
402
+ /**
403
+ * Per-commit timing data - correlate restarts with commit duration.
404
+ * High commitStats[N].restarts + high commitTimings[N].timeSinceLastCommit = React wasted work.
405
+ */
406
+ commitTimings: Record<number, CommitTiming>;
407
+ /**
408
+ * Per-commit counter deltas from the commit metrics adapter - low-level React
409
+ * work per commit. With the ATL adapter:
410
+ * metricsByCommit[N].componentRenderWithHooksCount + finishClassComponentCount
411
+ * should equal commitStats[N].collateral + commitStats[N].sources
412
+ */
413
+ metricsByCommit?: Record<number, CommitMetricsDelta>;
414
+ /** @deprecated Alias of `metricsByCommit`, kept for one release. */
415
+ atlByCommit?: Record<number, CommitMetricsDelta>;
416
+ /** @deprecated Use amplifierSources instead for full source linkage */
417
+ amplifiers?: Amplifier[];
418
+ /**
419
+ * Amplifier sources - components that amplified the update cost with hook re-executions.
420
+ * Shows which components responded to updates with expensive work (effects, memos, callbacks).
421
+ * Linked to the source that triggered them.
422
+ */
423
+ amplifierSources?: AmplifierSource[];
424
+ /**
425
+ * Per-commit Suspense boundary transitions.
426
+ * Only populated for commits where at least one boundary entered or left fallback.
427
+ * Empty object when no Suspense transitions occurred during the capture.
428
+ */
429
+ suspenseByCommit: Record<number, SuspenseBoundaryEvent[]>;
430
+ /** Bounded samples of component subtrees unmounted per commit. */
431
+ unmountsByCommit?: Record<number, UnmountSample[]>;
432
+ /** Compact user/browser actions linked to nearby commits. */
433
+ interactionsByCommit?: Record<number, CommitInteractionAction[]>;
434
+ /** Structural evidence for each component that rendered in a commit. */
435
+ renderedByCommit?: Record<number, RenderedComponentEvidence[]>;
436
+ /**
437
+ * Convert this report to a serializable DTO (safe for JSON.stringify, IPC, network).
438
+ * Strips non-serializable fields like Fiber references and ComponentType functions.
439
+ */
440
+ toSerializable(): ReportDTO;
441
+ }
442
+ export interface DetectorOptions {
443
+ ignore?: ComponentType[];
444
+ effectThrashMode?: 'non-primitive' | 'all';
445
+ captureFibers?: boolean;
446
+ /**
447
+ * Detection mode - controls what the detector tracks (tiered for performance):
448
+ *
449
+ * Use DETECTION_LEVEL constants for clarity:
450
+ * - DETECTION_LEVEL.LEVEL1_STATS ('stats'): Only commitStats, super fast
451
+ * - DETECTION_LEVEL.LEVEL2_SOURCES ('sources'): + source tracking
452
+ * - DETECTION_LEVEL.LEVEL3_AMPLIFIER_METRICS ('amplifierMetrics'): + aggregate amplifier counts only
453
+ * - DETECTION_LEVEL.LEVEL3_AMPLIFIERS ('amplifiers'): + full amplifier arrays
454
+ *
455
+ * Shortcuts:
456
+ * - false: Alias for 'stats' (minimal overhead)
457
+ * - true: Alias for 'full' (maximum detail)
458
+ *
459
+ * Performance hierarchy (fastest to slowest):
460
+ * stats < sources < amplifierMetrics < amplifiers ≈ full
461
+ */
462
+ detectionMode?: boolean | 'full' | 'amplifiers' | 'amplifierMetrics' | 'sources' | 'stats' | false;
463
+ historySize?: number;
464
+ /**
465
+ * Browser action capture level. Defaults to 'explicit' to capture high-signal
466
+ * actions (click/keyboard/input) while leaving noisy wheel/move capture opt-in.
467
+ */
468
+ interactionCapture?: InteractionCaptureLevel;
469
+ /** Extra native event names to capture in addition to the selected level. */
470
+ interactionEvents?: string[];
471
+ /** Time window for linking captured actions to a commit, in ms. Default: 1000. */
472
+ interactionWindowMs?: number;
473
+ /**
474
+ * Commit metrics adapter for host render counters. Defaults to the adapter
475
+ * registered with `registerCommitMetricsAdapter` when the detector installs.
476
+ * Pass `null` to run on the DevTools hook only.
477
+ */
478
+ metricsAdapter?: CommitMetricsAdapter | null;
479
+ /** @deprecated Historical option used by older call sites. Prefer `detector.report(topN)` */
480
+ topN?: number;
481
+ }
482
+ export type ThrashMode = 'full' | 'counts' | false;
483
+ export type DetectionLevel = 'stats' | 'sources' | 'amplifierMetrics' | 'amplifiers' | 'full';
484
+ /**
485
+ * Internal amplifier source state - keyed by sourceId for navigation.
486
+ */
487
+ export interface AmplifierSourceState {
488
+ type: ComponentType;
489
+ depth: number;
490
+ effectCount: number;
491
+ callbackCount: number;
492
+ memoCount: number;
493
+ hooksTotal: number;
494
+ commitIds: number[];
495
+ byCommit: Record<number, {
496
+ effects: number;
497
+ callbacks: number;
498
+ memos: number;
499
+ }>;
500
+ /** sourceId that caused this component to re-render (if not self-caused) */
501
+ causedBySourceId: string | null;
502
+ lastAmplification: {
503
+ depth: number;
504
+ thrashedHooks: ThrashInfo[];
505
+ fiber?: Fiber;
506
+ };
507
+ }
508
+ /**
509
+ * Commit timing data - measures React commit duration and detector overhead.
510
+ * All times in milliseconds.
511
+ */
512
+ export interface CommitTiming {
513
+ /** Time from previous commit end to this commit end (includes React work + idle time) */
514
+ timeSinceLastCommit: number;
515
+ /** Time spent in avalanche detector processing (our overhead) */
516
+ detectorOverhead: number;
517
+ }
518
+ /**
519
+ * Internal profiler state - tracks all avalanche detection data.
520
+ *
521
+ * This is the core state structure that accumulates across commits.
522
+ * Most users should use the `Report` interface from `detector.report()` instead.
523
+ */
524
+ export interface ProfilerState {
525
+ /** Total commits processed since install() or reset() */
526
+ commits: number;
527
+ /**
528
+ * All detected sources, keyed by stable source ID (e.g., "Button#1@3").
529
+ * Source = component that caused an update (not just re-rendered).
530
+ */
531
+ sources: Map<string, Omit<Source, 'sourceId'>>;
532
+ /** @deprecated Use amplifierSources instead */
533
+ amplifiers: Map<ComponentType, Omit<Amplifier, 'type'>>;
534
+ /**
535
+ * Amplifier sources keyed by sourceId.
536
+ * Enables source↔amplifier navigation and per-commit breakdown.
537
+ */
538
+ amplifierSources: Map<string, AmplifierSourceState>;
539
+ /**
540
+ * Aggregate thrashing counts (only if detectThrashing: 'counts').
541
+ * Lightweight alternative to full thrashing analysis.
542
+ */
543
+ thrashCounts: null | {
544
+ totalHooks: number;
545
+ affectedComponents: Set<ComponentType>;
546
+ };
547
+ /** Whether React DevTools hook exposes memoizedUpdaters (React 18.3.1+) */
548
+ memoizedUpdatersAvailable: boolean;
549
+ /** Number of commits where memoizedUpdaters data was present */
550
+ commitsWithMemoizedUpdaters: number;
551
+ /** Per-commit stats: [collateral, effects, memos, callbacks] */
552
+ commitStats: Record<number, CommitStats>;
553
+ /**
554
+ * Per-commit counter deltas from the commit metrics adapter.
555
+ * Enables full reconciliation with Avalanche counts.
556
+ */
557
+ metricsByCommit: Record<number, CommitMetricsDelta>;
558
+ /** Last adapter snapshot (for delta calculation) */
559
+ lastMetricsSnapshot: CommitMetricsSnapshot | null;
560
+ /**
561
+ * Per-commit timing data - measures commit duration and detector overhead.
562
+ * Enables correlation: high restarts → longer commit times.
563
+ */
564
+ commitTimings: Record<number, CommitTiming>;
565
+ /** Timestamp when last commit ended (for delta calculation) */
566
+ lastCommitEndTime: number;
567
+ /**
568
+ * Per-commit Suspense boundary transitions.
569
+ * Only populated for commits where at least one boundary entered or left fallback.
570
+ */
571
+ suspenseByCommit: Record<number, SuspenseBoundaryEvent[]>;
572
+ /** Bounded samples of component subtrees unmounted per commit. */
573
+ unmountsByCommit: Record<number, UnmountSample[]>;
574
+ /** Recent action breadcrumbs captured from native events. */
575
+ interactionActions: InteractionAction[];
576
+ /** Compact user/browser actions linked to nearby commits. */
577
+ interactionsByCommit: Record<number, CommitInteractionAction[]>;
578
+ /** Structural evidence for each component that rendered in a commit. */
579
+ renderedByCommit: Record<number, RenderedComponentEvidence[]>;
580
+ }
581
+ /**
582
+ * A Suspense boundary state transition captured during a commit.
583
+ * Only emitted when the boundary actually entered or left the fallback state.
584
+ * Boundaries that render without delay (no fallback shown) produce no events.
585
+ */
586
+ export interface SuspenseBoundaryEvent {
587
+ /** Tree depth of the Suspense fiber */
588
+ depth: number;
589
+ /** true = boundary entered fallback (primary → suspended) */
590
+ entered: boolean;
591
+ /** true = boundary left fallback (suspended → primary revealed) */
592
+ left: boolean;
593
+ /** The Suspense fiber itself (only present when captureFibers is true) */
594
+ fiber?: Fiber;
595
+ }
596
+ export interface ReportOptions {
597
+ /** Minimum collateral damage to include a source in sourcesByCommit (default: 0 - show all) */
598
+ minCollateral?: number;
599
+ }
600
+ export declare function buildReport(state: ProfilerState, thrashMode: ThrashMode, topN: number, sortBy: SortBy, options?: ReportOptions, detectorConfig?: {
601
+ effectThrashMode: 'non-primitive' | 'all';
602
+ captureFibers: boolean;
603
+ historySize: number;
604
+ detectionLevel?: DetectionLevel;
605
+ /**
606
+ * Integration model that produced this report. Determines what's reliable
607
+ * in commitStats and metricsByCommit. Defaults to 'none'.
608
+ */
609
+ integrationMode?: IntegrationMode;
610
+ }): Report;
@@ -0,0 +1,37 @@
1
+ import { type OriginalPosition } from './source-map.js';
2
+ export type FiberSourceLocation = {
3
+ fileName: string;
4
+ lineNumber: number;
5
+ columnNumber: number | null;
6
+ };
7
+ export type StackFrame = {
8
+ url: string;
9
+ line: number;
10
+ column: number;
11
+ function?: string;
12
+ };
13
+ export type SourceFiber = {
14
+ type?: unknown;
15
+ _debugSource?: unknown;
16
+ _debugStack?: unknown;
17
+ };
18
+ export type FetchModule = (url: string) => Promise<string | null>;
19
+ export type CallSiteResolver = {
20
+ locate(frames: readonly StackFrame[]): Promise<FiberSourceLocation | null>;
21
+ readonly stats: {
22
+ modules: number;
23
+ sites: number;
24
+ located: number;
25
+ };
26
+ };
27
+ export declare function parseStackFrames(stack: string): StackFrame[];
28
+ export declare function isVendorPath(path: string): boolean;
29
+ export declare function writerLocationOf(frames: readonly StackFrame[], originalFor: (frame: StackFrame) => OriginalPosition | null): FiberSourceLocation | null;
30
+ export declare function createCallSiteResolver(fetchModule: FetchModule): CallSiteResolver;
31
+ export declare function getFiberStackFrames(fiber: SourceFiber | null | undefined): StackFrame[];
32
+ /** Stable synchronous source, before any async source-map upgrade. */
33
+ export declare function getFiberSourceCandidate(fiber: SourceFiber | null | undefined): FiberSourceLocation | null;
34
+ /** Synchronous source read used by fiber walks and snapshots. */
35
+ export declare function getFiberSource(fiber: SourceFiber | null | undefined): FiberSourceLocation | null;
36
+ /** Resolve a React 19 stack frame through the module's inline or external map. */
37
+ export declare function resolveFiberSource(fiber: SourceFiber | null | undefined, resolver?: CallSiteResolver): Promise<FiberSourceLocation | null>;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Shared React DevTools renderer discovery.
3
+ *
4
+ * React-DOM registers a renderer object into
5
+ * `window.__REACT_DEVTOOLS_GLOBAL_HOOK__.renderers` at injection time (only when
6
+ * the hook exists before react-dom evaluates). That renderer object is the single
7
+ * entry point for several Chippy capabilities:
8
+ *
9
+ * - `currentDispatcherRef` → debug-render hook replay (see debug-render/core.ts)
10
+ * - `scheduleUpdate` → fiber-schedule-update nudge (see fiber-schedule-update.ts)
11
+ *
12
+ * Both used to walk `hook.renderers` themselves. This module is the one place that
13
+ * knows how to enumerate renderers, so capabilities select the field they need
14
+ * without duplicating the iteration (and its Map/entries quirks).
15
+ */
16
+ export type ReactRenderer = {
17
+ currentDispatcherRef?: {
18
+ current: unknown;
19
+ } | null;
20
+ scheduleUpdate?: (fiber: unknown) => void;
21
+ version?: string;
22
+ rendererPackageName?: string;
23
+ [key: string]: unknown;
24
+ };
25
+ export type DevtoolsHookLike = {
26
+ renderers?: Map<number, ReactRenderer> | {
27
+ entries(): IterableIterator<[number, ReactRenderer]>;
28
+ };
29
+ };
30
+ /**
31
+ * Enumerate every registered React renderer. Returns [] when the DevTools hook
32
+ * is absent or empty (production builds without the shim, or hook installed too
33
+ * late for react-dom to register).
34
+ */
35
+ export declare function getReactRenderers(scope?: typeof globalThis): ReactRenderer[];
36
+ /**
37
+ * First renderer that exposes a usable `currentDispatcherRef`. Used by
38
+ * debug-render to patch the hook dispatcher.
39
+ */
40
+ export declare function getDispatcherRef(scope?: typeof globalThis): {
41
+ current: unknown;
42
+ } | null;
43
+ /**
44
+ * First renderer that exposes a callable `scheduleUpdate`. This is the exact
45
+ * function React DevTools uses to force a re-render on a fiber. It is only
46
+ * present on development React builds. Returns null on production builds (or
47
+ * when the DevTools-hook shim was missing at page boot).
48
+ */
49
+ export declare function getScheduleUpdate(scope?: typeof globalThis): ((fiber: unknown) => void) | null;