rerender-lens 0.4.0 → 0.5.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 (66) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/README.md +192 -362
  3. package/dist/{budget-COBu7jBU.d.cts → budget-BnJlYwrV.d.ts} +20 -10
  4. package/dist/{budget-LkNjGtRc.d.ts → budget-CRswn-nC.d.cts} +20 -10
  5. package/dist/cli.cjs +248 -31
  6. package/dist/cli.cjs.map +1 -1
  7. package/dist/cli.js +248 -31
  8. package/dist/cli.js.map +1 -1
  9. package/dist/{devtools-CZebzpF6.d.ts → devtools-BYWat7nQ.d.ts} +7 -1
  10. package/dist/{devtools-BkCct3cJ.d.cts → devtools-DJi_6AfJ.d.cts} +7 -1
  11. package/dist/index.cjs +439 -41
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.d.cts +131 -8
  14. package/dist/index.d.ts +131 -8
  15. package/dist/index.js +424 -42
  16. package/dist/index.js.map +1 -1
  17. package/dist/jest-setup.cjs +1673 -0
  18. package/dist/jest-setup.cjs.map +1 -0
  19. package/dist/jest-setup.d.cts +8 -0
  20. package/dist/jest-setup.d.ts +8 -0
  21. package/dist/jest-setup.js +1671 -0
  22. package/dist/jest-setup.js.map +1 -0
  23. package/dist/jest.cjs +1760 -0
  24. package/dist/jest.cjs.map +1 -0
  25. package/dist/jest.d.cts +23 -0
  26. package/dist/jest.d.ts +23 -0
  27. package/dist/jest.js +1751 -0
  28. package/dist/jest.js.map +1 -0
  29. package/dist/{notifiers-BjjGSHpp.d.ts → notifiers-DwKlvv4H.d.cts} +8 -4
  30. package/dist/{notifiers-BGQtWKfX.d.cts → notifiers-FNzKHs0a.d.ts} +8 -4
  31. package/dist/playwright.cjs +230 -28
  32. package/dist/playwright.cjs.map +1 -1
  33. package/dist/playwright.d.cts +4 -4
  34. package/dist/playwright.d.ts +4 -4
  35. package/dist/playwright.js +230 -28
  36. package/dist/playwright.js.map +1 -1
  37. package/dist/relay.cjs +10 -2
  38. package/dist/relay.cjs.map +1 -1
  39. package/dist/relay.js +10 -2
  40. package/dist/relay.js.map +1 -1
  41. package/dist/rerender-lens.iife.js +356 -54
  42. package/dist/runner-report-B8Sc3FZO.d.cts +46 -0
  43. package/dist/runner-report-CW8X5RwK.d.ts +46 -0
  44. package/dist/setup.cjs +112 -27
  45. package/dist/setup.cjs.map +1 -1
  46. package/dist/setup.js +112 -27
  47. package/dist/setup.js.map +1 -1
  48. package/dist/{types-BzUEVkxJ.d.cts → types-DW5-N2XH.d.cts} +55 -2
  49. package/dist/{types-BzUEVkxJ.d.ts → types-DW5-N2XH.d.ts} +55 -2
  50. package/dist/vite.d.cts +1 -1
  51. package/dist/vite.d.ts +1 -1
  52. package/dist/vitest-setup.cjs +337 -41
  53. package/dist/vitest-setup.cjs.map +1 -1
  54. package/dist/vitest-setup.d.cts +4 -4
  55. package/dist/vitest-setup.d.ts +4 -4
  56. package/dist/vitest-setup.js +337 -41
  57. package/dist/vitest-setup.js.map +1 -1
  58. package/dist/vitest.cjs +400 -77
  59. package/dist/vitest.cjs.map +1 -1
  60. package/dist/vitest.d.cts +21 -39
  61. package/dist/vitest.d.ts +21 -39
  62. package/dist/vitest.js +401 -78
  63. package/dist/vitest.js.map +1 -1
  64. package/package.json +40 -4
  65. package/panel/panel.css +1 -0
  66. package/panel/panel.js +499 -309
package/dist/index.d.cts CHANGED
@@ -1,9 +1,9 @@
1
- import { O as Options, C as ChangeKind, a as Change, H as HookChange, b as HookSnapshot, P as ParentInfo, c as CommitPriority, S as SourceLocation, d as CommitCause, R as RenderReport } from './types-BzUEVkxJ.cjs';
2
- export { e as ComponentMatcher, M as MARKER, N as Notifier, f as RenderTrigger } from './types-BzUEVkxJ.cjs';
3
- import { F as Fiber } from './devtools-BkCct3cJ.cjs';
4
- export { C as ChannelCommand, a as ChannelReply, D as DEFAULT_CHANNEL, b as DEVTOOLS_MARKER, c as DevtoolsBridge, d as DevtoolsMessage, e as DevtoolsNotifierOptions, H as HelloPayload, I as InspectResult, P as PROTOCOL_VERSION, f as PullResult, R as RendererInfo, S as SerializableOptions, g as createDevtoolsNotifier, h as deserializeOptions, i as ensureDevtoolsHook, j as getRenderers, k as isProductionReact, s as serialize, l as serializeOptions } from './devtools-BkCct3cJ.cjs';
5
- export { B as Budget, a as BudgetResult, b as BudgetViolation, F as Fix, d as FixKind, R as RankedFix, e as assertWithinBudget, g as avoidableCounts, c as checkBudget, h as fixKey, i as fixesFor, f as formatFixes, r as rankFixes, t as toBudget } from './budget-COBu7jBU.cjs';
6
- export { C as Collector, a as CompareRow, b as Comparison, S as SessionSummary, c as combineNotifiers, d as compareSummaries, e as createCollector, f as formatComparison, p as parseExport, s as summarizeReports } from './notifiers-BGQtWKfX.cjs';
1
+ import { O as Options, C as ChangeKind, b as Change, H as HookChange, c as HookSnapshot, P as ParentInfo, d as CommitPriority, S as SourceLocation, e as CommitCause, R as RenderReport, a as ReportLike } from './types-DW5-N2XH.cjs';
2
+ export { f as ChangeLike, g as ComponentMatcher, M as MARKER, N as Notifier, h as RenderTrigger } from './types-DW5-N2XH.cjs';
3
+ import { F as Fiber } from './devtools-DJi_6AfJ.cjs';
4
+ export { C as ChannelCommand, a as ChannelReply, D as DEFAULT_CHANNEL, b as DEVTOOLS_MARKER, c as DevtoolsBridge, d as DevtoolsMessage, e as DevtoolsNotifierOptions, H as HelloPayload, I as InspectResult, P as PROTOCOL_VERSION, f as PullResult, R as RendererInfo, S as SerializableOptions, g as createDevtoolsNotifier, h as deserializeOptions, i as ensureDevtoolsHook, j as getRenderers, k as isProductionReact, s as serialize, l as serializeOptions } from './devtools-DJi_6AfJ.cjs';
5
+ export { B as Budget, a as BudgetResult, b as BudgetViolation, F as Fix, d as FixKind, R as RankedFix, e as assertWithinBudget, g as avoidableCounts, c as checkBudget, h as fixKey, i as fixesFor, f as formatFixes, r as rankFixes, s as shortValue, t as toBudget } from './budget-CRswn-nC.cjs';
6
+ export { C as Collector, a as CompareRow, b as Comparison, S as SessionSummary, c as combineNotifiers, d as compareSessions, e as compareSummaries, f as createCollector, g as formatComparison, p as parseExport, s as summarizeReports, h as summarizeSession } from './notifiers-DwKlvv4H.cjs';
7
7
 
8
8
  declare function getDisplayName(type: unknown): string;
9
9
  /** Decide whether a component type is tracked under the given options. */
@@ -69,6 +69,8 @@ type Seen = Map<object, Set<object>>;
69
69
  * React elements and cyclic references. Functions are equal only by reference.
70
70
  */
71
71
  declare function deepEqual(a: unknown, b: unknown, seen?: Seen): boolean;
72
+ /** Change kinds that mean "new reference, same contents": the ones a fix removes. */
73
+ declare const AVOIDABLE_KINDS: ReadonlySet<string>;
72
74
  /** Classify why two values differ. Assumes `!Object.is(prev, next)`. */
73
75
  declare function classify(prev: unknown, next: unknown): ChangeKind;
74
76
  /**
@@ -77,6 +79,19 @@ declare function classify(prev: unknown, next: unknown): ChangeKind;
77
79
  * actually differs is reported in `path` to make the console output actionable.
78
80
  */
79
81
  declare function diffRecords(prev: Record<string, unknown> | undefined | null, next: Record<string, unknown> | undefined | null, basePath?: string): Change[];
82
+ /**
83
+ * Walk plain objects/arrays to find the deepest path where the values first differ, e.g. `style.color`
84
+ * or `items[2].id`; null when they are deep-equal. Two different values with no path to descend into
85
+ * (primitives at the root) give `(value)`. Works on live values and on the panel's serialized ones.
86
+ */
87
+ declare function firstDifferentPath(a: unknown, b: unknown, path?: string, depth?: number): string | null;
88
+ interface Leaf {
89
+ path: string;
90
+ prev: unknown;
91
+ next: unknown;
92
+ }
93
+ /** Every leaf at which two values differ, at most `limit` of them (the panel's diff view of `different` changes). */
94
+ declare function diffLeaves(a: unknown, b: unknown, limit?: number, path?: string, out?: Leaf[]): Leaf[];
80
95
 
81
96
  interface BuildInput {
82
97
  component: string;
@@ -98,6 +113,12 @@ interface BuildInput {
98
113
  path?: string[];
99
114
  /** Default true: `useWhyRerender` and the console output assume the component decides on props alone. */
100
115
  memoized?: boolean;
116
+ /** The component was compiled by React Compiler (its output is memoized per input). */
117
+ compiled?: boolean;
118
+ /** A class component: the memoization advice names PureComponent / shouldComponentUpdate instead of React.memo. */
119
+ classComponent?: boolean;
120
+ /** The component sits under a Suspense boundary that switched from its fallback to content in this commit. */
121
+ revealed?: boolean;
101
122
  selfDuration?: number;
102
123
  treeDuration?: number;
103
124
  commitId?: number;
@@ -117,11 +138,113 @@ declare const hookLabel: (c: {
117
138
  /** Store-specific advice for a `useSyncExternalStore` snapshot that is a new reference with equal contents. */
118
139
  declare function storeAdvice(custom: string[] | undefined): string;
119
140
  declare function buildReport(input: BuildInput): RenderReport;
120
- declare function summarize(report: RenderReport): string;
141
+ /** Short label per change kind, as printed by the console output and the panel. */
142
+ declare const KIND_LABEL: Record<Change['kind'], string>;
143
+ /** `1 equal by value, 2 new function`, or `no changes`. Shared with the panel (serialized reports). */
144
+ declare function summarize(report: Pick<ReportLike, 'propChanges' | 'stateChanges' | 'hookChanges'>): string;
121
145
  /** Default notifier: prints a console group per report. */
122
146
  declare function printReport(report: RenderReport, options: Options): void;
123
147
 
148
+ interface RootCause {
149
+ name: string;
150
+ trigger: string;
151
+ /** Avoidable re-renders in the commit that trace back to this component. */
152
+ count: number;
153
+ components: Map<string, number>;
154
+ }
155
+ interface ContextStat {
156
+ name: string;
157
+ consumers: number;
158
+ avoidable: number;
159
+ components: Map<string, number>;
160
+ commits: Set<number>;
161
+ /** Components rendering the Provider (usually one). */
162
+ providers: Map<string, number>;
163
+ /** Keys that changed in object values, and the largest key count seen. */
164
+ changedKeys: Set<string>;
165
+ totalKeys: number;
166
+ }
167
+ interface CommitAnalysis<R extends ReportLike = RenderReport> {
168
+ /** The commit id (`commitId` of its reports), 0 when the reports carry none. */
169
+ id: number;
170
+ total: number;
171
+ avoidable: number;
172
+ /** Sum of `selfDuration` over the avoidable reports, in ms. */
173
+ wasted: number;
174
+ roots: RootCause[];
175
+ /** Root cause name of every avoidable report (what `roots` was aggregated from). */
176
+ rootByReport: Map<R, string>;
177
+ contexts: ContextStat[];
178
+ reports: R[];
179
+ }
180
+ interface CascadeNode<R extends ReportLike = RenderReport> {
181
+ name: string;
182
+ children: Map<string, CascadeNode<R>>;
183
+ report: R | null;
184
+ count?: number;
185
+ avoidable?: number;
186
+ }
187
+ interface RootSummary<R extends ReportLike = RenderReport, A extends CommitAnalysis<R> = CommitAnalysis<R>> {
188
+ name: string;
189
+ trigger: string;
190
+ /** Newest first. */
191
+ commits: {
192
+ key: number;
193
+ analysis: A;
194
+ count: number;
195
+ components: Map<string, number>;
196
+ }[];
197
+ /** Avoidable re-renders across every commit. */
198
+ total: number;
199
+ components: Map<string, number>;
200
+ /** The avoidable reports that trace back to this root cause. */
201
+ affected: R[];
202
+ }
203
+ /** One root cause across a whole run: how many commits it started and what those cost. */
204
+ interface RankedRootCause {
205
+ name: string;
206
+ trigger: string;
207
+ commits: number;
208
+ /** Avoidable re-renders it caused. */
209
+ count: number;
210
+ components: Map<string, number>;
211
+ }
212
+ /** One commit's reports grouped by component name, so a parent lookup is O(same-named reports) instead of O(commit). */
213
+ declare function indexByComponent<R extends ReportLike>(reports: readonly R[]): Map<string, R[]>;
214
+ /**
215
+ * Walk `parent` links inside one commit up to the component whose own change started the cascade.
216
+ * Null when `r` started it itself; `report` is null when the root is an ancestor that is not in the
217
+ * commit (untracked), in which case `name`/`trigger` come from the last `parent` link.
218
+ */
219
+ declare function rootCauseOf<R extends ReportLike>(r: R, commitReports: readonly R[], index?: Map<string, R[]>): {
220
+ name: string;
221
+ trigger: string;
222
+ report: R | null;
223
+ } | null;
224
+ /** Analyze the reports of one commit: what started the cascade, which contexts changed, what it cost. */
225
+ declare function analyzeCommit<R extends ReportLike>(reports: R[]): CommitAnalysis<R>;
226
+ /** Which contexts changed and how many consumers re-rendered because of them. */
227
+ declare function contextAttribution(reports: readonly ReportLike[]): ContextStat[];
228
+ /** Nested cascade for one commit: every report placed under its ancestors (untracked ancestors appear as plain names). */
229
+ declare function cascadeTree<R extends ReportLike>(reports: readonly R[]): CascadeNode<R>;
230
+ /**
231
+ * Every commit a component started (as the root cause), across the whole session.
232
+ * `analyze` lets a caller (the panel) pass its per-commit memoized analysis.
233
+ */
234
+ declare function rootCauseSummary<R extends ReportLike>(name: string, commits: Iterable<[number, R[]]>): RootSummary<R>;
235
+ declare function rootCauseSummary<R extends ReportLike, A extends CommitAnalysis<R>>(name: string, commits: Iterable<[number, R[]]>, analyze: (key: number, reports: R[]) => A): RootSummary<R, A>;
236
+ /** Reports grouped by `commitId` in first-seen order. Reports without one (`useWhyRerender`, protocol 1) are left out. */
237
+ declare function groupByCommit<R extends ReportLike>(reports: readonly R[]): Map<number, R[]>;
238
+ /** Root causes across every commit in `reports`, most avoidable re-renders caused first. */
239
+ declare function rankRootCauses(reports: readonly ReportLike[]): RankedRootCause[];
240
+ /**
241
+ * Multi-line root-cause table for a test failure or a CI log, one line per root cause:
242
+ * `<Page> (state) started 3 commits, 12 avoidable re-renders (<Row> x10, <Toolbar> x2)`.
243
+ * Empty when nothing is avoidable or the reports carry no `commitId`.
244
+ */
245
+ declare function formatRootCauses(reports: readonly ReportLike[], limit?: number): string;
246
+
124
247
  /** Library version, injected by the build; "dev" when running from source. */
125
248
  declare const VERSION: string;
126
249
 
127
- export { Change, ChangeKind, CommitCause, CommitPriority, type DispatcherRef, HookChange, HookSnapshot, Options, ParentInfo, RenderReport, SourceLocation, VERSION, buildReport, classify, configure, customHooksFromStack, deepEqual, diffRecords, disable, getDisplayName, hookLabel, init, isEnabled, printReport, resolveHookNames, shouldTrack, storeAdvice, summarize, track, useWhyRerender };
250
+ export { AVOIDABLE_KINDS, type CascadeNode, Change, ChangeKind, type CommitAnalysis, CommitCause, CommitPriority, type ContextStat, type DispatcherRef, HookChange, HookSnapshot, KIND_LABEL, type Leaf, Options, ParentInfo, type RankedRootCause, RenderReport, ReportLike, type RootCause, type RootSummary, SourceLocation, VERSION, analyzeCommit, buildReport, cascadeTree, classify, configure, contextAttribution, customHooksFromStack, deepEqual, diffLeaves, diffRecords, disable, firstDifferentPath, formatRootCauses, getDisplayName, groupByCommit, hookLabel, indexByComponent, init, isEnabled, printReport, rankRootCauses, resolveHookNames, rootCauseOf, rootCauseSummary, shouldTrack, storeAdvice, summarize, track, useWhyRerender };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { O as Options, C as ChangeKind, a as Change, H as HookChange, b as HookSnapshot, P as ParentInfo, c as CommitPriority, S as SourceLocation, d as CommitCause, R as RenderReport } from './types-BzUEVkxJ.js';
2
- export { e as ComponentMatcher, M as MARKER, N as Notifier, f as RenderTrigger } from './types-BzUEVkxJ.js';
3
- import { F as Fiber } from './devtools-CZebzpF6.js';
4
- export { C as ChannelCommand, a as ChannelReply, D as DEFAULT_CHANNEL, b as DEVTOOLS_MARKER, c as DevtoolsBridge, d as DevtoolsMessage, e as DevtoolsNotifierOptions, H as HelloPayload, I as InspectResult, P as PROTOCOL_VERSION, f as PullResult, R as RendererInfo, S as SerializableOptions, g as createDevtoolsNotifier, h as deserializeOptions, i as ensureDevtoolsHook, j as getRenderers, k as isProductionReact, s as serialize, l as serializeOptions } from './devtools-CZebzpF6.js';
5
- export { B as Budget, a as BudgetResult, b as BudgetViolation, F as Fix, d as FixKind, R as RankedFix, e as assertWithinBudget, g as avoidableCounts, c as checkBudget, h as fixKey, i as fixesFor, f as formatFixes, r as rankFixes, t as toBudget } from './budget-LkNjGtRc.js';
6
- export { C as Collector, a as CompareRow, b as Comparison, S as SessionSummary, c as combineNotifiers, d as compareSummaries, e as createCollector, f as formatComparison, p as parseExport, s as summarizeReports } from './notifiers-BjjGSHpp.js';
1
+ import { O as Options, C as ChangeKind, b as Change, H as HookChange, c as HookSnapshot, P as ParentInfo, d as CommitPriority, S as SourceLocation, e as CommitCause, R as RenderReport, a as ReportLike } from './types-DW5-N2XH.js';
2
+ export { f as ChangeLike, g as ComponentMatcher, M as MARKER, N as Notifier, h as RenderTrigger } from './types-DW5-N2XH.js';
3
+ import { F as Fiber } from './devtools-BYWat7nQ.js';
4
+ export { C as ChannelCommand, a as ChannelReply, D as DEFAULT_CHANNEL, b as DEVTOOLS_MARKER, c as DevtoolsBridge, d as DevtoolsMessage, e as DevtoolsNotifierOptions, H as HelloPayload, I as InspectResult, P as PROTOCOL_VERSION, f as PullResult, R as RendererInfo, S as SerializableOptions, g as createDevtoolsNotifier, h as deserializeOptions, i as ensureDevtoolsHook, j as getRenderers, k as isProductionReact, s as serialize, l as serializeOptions } from './devtools-BYWat7nQ.js';
5
+ export { B as Budget, a as BudgetResult, b as BudgetViolation, F as Fix, d as FixKind, R as RankedFix, e as assertWithinBudget, g as avoidableCounts, c as checkBudget, h as fixKey, i as fixesFor, f as formatFixes, r as rankFixes, s as shortValue, t as toBudget } from './budget-BnJlYwrV.js';
6
+ export { C as Collector, a as CompareRow, b as Comparison, S as SessionSummary, c as combineNotifiers, d as compareSessions, e as compareSummaries, f as createCollector, g as formatComparison, p as parseExport, s as summarizeReports, h as summarizeSession } from './notifiers-FNzKHs0a.js';
7
7
 
8
8
  declare function getDisplayName(type: unknown): string;
9
9
  /** Decide whether a component type is tracked under the given options. */
@@ -69,6 +69,8 @@ type Seen = Map<object, Set<object>>;
69
69
  * React elements and cyclic references. Functions are equal only by reference.
70
70
  */
71
71
  declare function deepEqual(a: unknown, b: unknown, seen?: Seen): boolean;
72
+ /** Change kinds that mean "new reference, same contents": the ones a fix removes. */
73
+ declare const AVOIDABLE_KINDS: ReadonlySet<string>;
72
74
  /** Classify why two values differ. Assumes `!Object.is(prev, next)`. */
73
75
  declare function classify(prev: unknown, next: unknown): ChangeKind;
74
76
  /**
@@ -77,6 +79,19 @@ declare function classify(prev: unknown, next: unknown): ChangeKind;
77
79
  * actually differs is reported in `path` to make the console output actionable.
78
80
  */
79
81
  declare function diffRecords(prev: Record<string, unknown> | undefined | null, next: Record<string, unknown> | undefined | null, basePath?: string): Change[];
82
+ /**
83
+ * Walk plain objects/arrays to find the deepest path where the values first differ, e.g. `style.color`
84
+ * or `items[2].id`; null when they are deep-equal. Two different values with no path to descend into
85
+ * (primitives at the root) give `(value)`. Works on live values and on the panel's serialized ones.
86
+ */
87
+ declare function firstDifferentPath(a: unknown, b: unknown, path?: string, depth?: number): string | null;
88
+ interface Leaf {
89
+ path: string;
90
+ prev: unknown;
91
+ next: unknown;
92
+ }
93
+ /** Every leaf at which two values differ, at most `limit` of them (the panel's diff view of `different` changes). */
94
+ declare function diffLeaves(a: unknown, b: unknown, limit?: number, path?: string, out?: Leaf[]): Leaf[];
80
95
 
81
96
  interface BuildInput {
82
97
  component: string;
@@ -98,6 +113,12 @@ interface BuildInput {
98
113
  path?: string[];
99
114
  /** Default true: `useWhyRerender` and the console output assume the component decides on props alone. */
100
115
  memoized?: boolean;
116
+ /** The component was compiled by React Compiler (its output is memoized per input). */
117
+ compiled?: boolean;
118
+ /** A class component: the memoization advice names PureComponent / shouldComponentUpdate instead of React.memo. */
119
+ classComponent?: boolean;
120
+ /** The component sits under a Suspense boundary that switched from its fallback to content in this commit. */
121
+ revealed?: boolean;
101
122
  selfDuration?: number;
102
123
  treeDuration?: number;
103
124
  commitId?: number;
@@ -117,11 +138,113 @@ declare const hookLabel: (c: {
117
138
  /** Store-specific advice for a `useSyncExternalStore` snapshot that is a new reference with equal contents. */
118
139
  declare function storeAdvice(custom: string[] | undefined): string;
119
140
  declare function buildReport(input: BuildInput): RenderReport;
120
- declare function summarize(report: RenderReport): string;
141
+ /** Short label per change kind, as printed by the console output and the panel. */
142
+ declare const KIND_LABEL: Record<Change['kind'], string>;
143
+ /** `1 equal by value, 2 new function`, or `no changes`. Shared with the panel (serialized reports). */
144
+ declare function summarize(report: Pick<ReportLike, 'propChanges' | 'stateChanges' | 'hookChanges'>): string;
121
145
  /** Default notifier: prints a console group per report. */
122
146
  declare function printReport(report: RenderReport, options: Options): void;
123
147
 
148
+ interface RootCause {
149
+ name: string;
150
+ trigger: string;
151
+ /** Avoidable re-renders in the commit that trace back to this component. */
152
+ count: number;
153
+ components: Map<string, number>;
154
+ }
155
+ interface ContextStat {
156
+ name: string;
157
+ consumers: number;
158
+ avoidable: number;
159
+ components: Map<string, number>;
160
+ commits: Set<number>;
161
+ /** Components rendering the Provider (usually one). */
162
+ providers: Map<string, number>;
163
+ /** Keys that changed in object values, and the largest key count seen. */
164
+ changedKeys: Set<string>;
165
+ totalKeys: number;
166
+ }
167
+ interface CommitAnalysis<R extends ReportLike = RenderReport> {
168
+ /** The commit id (`commitId` of its reports), 0 when the reports carry none. */
169
+ id: number;
170
+ total: number;
171
+ avoidable: number;
172
+ /** Sum of `selfDuration` over the avoidable reports, in ms. */
173
+ wasted: number;
174
+ roots: RootCause[];
175
+ /** Root cause name of every avoidable report (what `roots` was aggregated from). */
176
+ rootByReport: Map<R, string>;
177
+ contexts: ContextStat[];
178
+ reports: R[];
179
+ }
180
+ interface CascadeNode<R extends ReportLike = RenderReport> {
181
+ name: string;
182
+ children: Map<string, CascadeNode<R>>;
183
+ report: R | null;
184
+ count?: number;
185
+ avoidable?: number;
186
+ }
187
+ interface RootSummary<R extends ReportLike = RenderReport, A extends CommitAnalysis<R> = CommitAnalysis<R>> {
188
+ name: string;
189
+ trigger: string;
190
+ /** Newest first. */
191
+ commits: {
192
+ key: number;
193
+ analysis: A;
194
+ count: number;
195
+ components: Map<string, number>;
196
+ }[];
197
+ /** Avoidable re-renders across every commit. */
198
+ total: number;
199
+ components: Map<string, number>;
200
+ /** The avoidable reports that trace back to this root cause. */
201
+ affected: R[];
202
+ }
203
+ /** One root cause across a whole run: how many commits it started and what those cost. */
204
+ interface RankedRootCause {
205
+ name: string;
206
+ trigger: string;
207
+ commits: number;
208
+ /** Avoidable re-renders it caused. */
209
+ count: number;
210
+ components: Map<string, number>;
211
+ }
212
+ /** One commit's reports grouped by component name, so a parent lookup is O(same-named reports) instead of O(commit). */
213
+ declare function indexByComponent<R extends ReportLike>(reports: readonly R[]): Map<string, R[]>;
214
+ /**
215
+ * Walk `parent` links inside one commit up to the component whose own change started the cascade.
216
+ * Null when `r` started it itself; `report` is null when the root is an ancestor that is not in the
217
+ * commit (untracked), in which case `name`/`trigger` come from the last `parent` link.
218
+ */
219
+ declare function rootCauseOf<R extends ReportLike>(r: R, commitReports: readonly R[], index?: Map<string, R[]>): {
220
+ name: string;
221
+ trigger: string;
222
+ report: R | null;
223
+ } | null;
224
+ /** Analyze the reports of one commit: what started the cascade, which contexts changed, what it cost. */
225
+ declare function analyzeCommit<R extends ReportLike>(reports: R[]): CommitAnalysis<R>;
226
+ /** Which contexts changed and how many consumers re-rendered because of them. */
227
+ declare function contextAttribution(reports: readonly ReportLike[]): ContextStat[];
228
+ /** Nested cascade for one commit: every report placed under its ancestors (untracked ancestors appear as plain names). */
229
+ declare function cascadeTree<R extends ReportLike>(reports: readonly R[]): CascadeNode<R>;
230
+ /**
231
+ * Every commit a component started (as the root cause), across the whole session.
232
+ * `analyze` lets a caller (the panel) pass its per-commit memoized analysis.
233
+ */
234
+ declare function rootCauseSummary<R extends ReportLike>(name: string, commits: Iterable<[number, R[]]>): RootSummary<R>;
235
+ declare function rootCauseSummary<R extends ReportLike, A extends CommitAnalysis<R>>(name: string, commits: Iterable<[number, R[]]>, analyze: (key: number, reports: R[]) => A): RootSummary<R, A>;
236
+ /** Reports grouped by `commitId` in first-seen order. Reports without one (`useWhyRerender`, protocol 1) are left out. */
237
+ declare function groupByCommit<R extends ReportLike>(reports: readonly R[]): Map<number, R[]>;
238
+ /** Root causes across every commit in `reports`, most avoidable re-renders caused first. */
239
+ declare function rankRootCauses(reports: readonly ReportLike[]): RankedRootCause[];
240
+ /**
241
+ * Multi-line root-cause table for a test failure or a CI log, one line per root cause:
242
+ * `<Page> (state) started 3 commits, 12 avoidable re-renders (<Row> x10, <Toolbar> x2)`.
243
+ * Empty when nothing is avoidable or the reports carry no `commitId`.
244
+ */
245
+ declare function formatRootCauses(reports: readonly ReportLike[], limit?: number): string;
246
+
124
247
  /** Library version, injected by the build; "dev" when running from source. */
125
248
  declare const VERSION: string;
126
249
 
127
- export { Change, ChangeKind, CommitCause, CommitPriority, type DispatcherRef, HookChange, HookSnapshot, Options, ParentInfo, RenderReport, SourceLocation, VERSION, buildReport, classify, configure, customHooksFromStack, deepEqual, diffRecords, disable, getDisplayName, hookLabel, init, isEnabled, printReport, resolveHookNames, shouldTrack, storeAdvice, summarize, track, useWhyRerender };
250
+ export { AVOIDABLE_KINDS, type CascadeNode, Change, ChangeKind, type CommitAnalysis, CommitCause, CommitPriority, type ContextStat, type DispatcherRef, HookChange, HookSnapshot, KIND_LABEL, type Leaf, Options, ParentInfo, type RankedRootCause, RenderReport, ReportLike, type RootCause, type RootSummary, SourceLocation, VERSION, analyzeCommit, buildReport, cascadeTree, classify, configure, contextAttribution, customHooksFromStack, deepEqual, diffLeaves, diffRecords, disable, firstDifferentPath, formatRootCauses, getDisplayName, groupByCommit, hookLabel, indexByComponent, init, isEnabled, printReport, rankRootCauses, resolveHookNames, rootCauseOf, rootCauseSummary, shouldTrack, storeAdvice, summarize, track, useWhyRerender };