rerender-lens 0.3.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 +118 -0
  2. package/README.md +192 -353
  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-BHGUUo-p.d.ts → devtools-BYWat7nQ.d.ts} +17 -3
  10. package/dist/{devtools-DmhWiWcN.d.cts → devtools-DJi_6AfJ.d.cts} +17 -3
  11. package/dist/index.cjs +635 -109
  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 +620 -110
  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 +235 -30
  32. package/dist/playwright.cjs.map +1 -1
  33. package/dist/playwright.d.cts +8 -6
  34. package/dist/playwright.d.ts +8 -6
  35. package/dist/playwright.js +235 -31
  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 +1100 -337
  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 +308 -95
  45. package/dist/setup.cjs.map +1 -1
  46. package/dist/setup.js +308 -95
  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 +452 -87
  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 +452 -87
  57. package/dist/vitest-setup.js.map +1 -1
  58. package/dist/vitest.cjs +515 -123
  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 +516 -124
  63. package/dist/vitest.js.map +1 -1
  64. package/package.json +41 -5
  65. package/panel/panel.css +1 -34
  66. package/panel/panel.js +865 -713
@@ -1,10 +1,39 @@
1
- var RerenderLens = (function (exports) {
2
- 'use strict';
1
+ /* rerender-lens 0.5.0 — rerender-lens.core.js: types, version, diff, report, state, overlay. Built by scripts/build-vendor.mjs; do not edit. */
2
+ (() => {
3
+ var __defProp = Object.defineProperty;
4
+ var __export = (target, all) => {
5
+ for (var name in all)
6
+ __defProp(target, name, { get: all[name], enumerable: true });
7
+ };
3
8
 
4
9
  // src/types.ts
10
+ var types_exports = {};
11
+ __export(types_exports, {
12
+ MARKER: () => MARKER
13
+ });
5
14
  var MARKER = "rerenderLens";
6
15
 
16
+ // src/version.ts
17
+ var version_exports = {};
18
+ __export(version_exports, {
19
+ VERSION: () => VERSION
20
+ });
21
+ var VERSION = true ? "0.5.0" : "dev";
22
+
7
23
  // src/diff.ts
24
+ var diff_exports = {};
25
+ __export(diff_exports, {
26
+ AVOIDABLE_KINDS: () => AVOIDABLE_KINDS,
27
+ DEFAULT_DIFF_BUDGET: () => DEFAULT_DIFF_BUDGET,
28
+ beginDiffScope: () => beginDiffScope,
29
+ classify: () => classify,
30
+ deepEqual: () => deepEqual,
31
+ diffBudgetExhausted: () => diffBudgetExhausted,
32
+ diffLeaves: () => diffLeaves,
33
+ diffRecords: () => diffRecords,
34
+ firstDifferentPath: () => firstDifferentPath,
35
+ isReactElement: () => isReactElement
36
+ });
8
37
  var REACT_ELEMENT = /* @__PURE__ */ Symbol.for("react.element");
9
38
  var REACT_TRANSITIONAL_ELEMENT = /* @__PURE__ */ Symbol.for("react.transitional.element");
10
39
  function isReactElement(v) {
@@ -27,10 +56,49 @@ var RerenderLens = (function (exports) {
27
56
  set.add(b);
28
57
  return false;
29
58
  }
59
+ var scope = null;
60
+ var DEFAULT_DIFF_BUDGET = 5e4;
61
+ function beginDiffScope(budget = 2e5) {
62
+ const previous = scope;
63
+ scope = { cache: /* @__PURE__ */ new WeakMap(), remaining: budget, exhausted: false };
64
+ return () => {
65
+ scope = previous;
66
+ };
67
+ }
68
+ function diffBudgetExhausted() {
69
+ return scope?.exhausted === true;
70
+ }
30
71
  function deepEqual(a, b, seen = /* @__PURE__ */ new Map()) {
31
72
  if (Object.is(a, b)) return true;
32
73
  if (typeof a !== typeof b) return false;
33
74
  if (typeof a !== "object" || a === null || b === null) return false;
75
+ if (!scope) {
76
+ const end = beginDiffScope(DEFAULT_DIFF_BUDGET);
77
+ try {
78
+ return deepEqual(a, b, seen);
79
+ } finally {
80
+ end();
81
+ }
82
+ }
83
+ const sc = scope;
84
+ const objA = a;
85
+ const objB = b;
86
+ const hit = sc.cache.get(objA)?.get(objB);
87
+ if (hit !== void 0) return hit;
88
+ if (--sc.remaining < 0) {
89
+ sc.exhausted = true;
90
+ return false;
91
+ }
92
+ const result = deepEqualObjects(objA, objB, seen);
93
+ let m = sc.cache.get(objA);
94
+ if (!m) {
95
+ m = /* @__PURE__ */ new WeakMap();
96
+ sc.cache.set(objA, m);
97
+ }
98
+ m.set(objB, result);
99
+ return result;
100
+ }
101
+ function deepEqualObjects(a, b, seen) {
34
102
  const objA = a;
35
103
  const objB = b;
36
104
  if (remember(seen, objA, objB)) return true;
@@ -59,6 +127,10 @@ var RerenderLens = (function (exports) {
59
127
  }
60
128
  if (a instanceof Set) {
61
129
  if (!(b instanceof Set) || a.size !== b.size) return false;
130
+ if (a.size > 50) {
131
+ for (const v of a) if (!b.has(v)) return false;
132
+ return true;
133
+ }
62
134
  outer: for (const v of a) {
63
135
  if (b.has(v)) continue;
64
136
  for (const w of b) if (deepEqual(v, w, seen)) continue outer;
@@ -74,14 +146,16 @@ var RerenderLens = (function (exports) {
74
146
  const ka = Object.keys(a);
75
147
  const kb = Object.keys(b);
76
148
  if (ka.length !== kb.length) return false;
149
+ const rb = b;
77
150
  for (const k of ka) {
78
151
  if (!Object.prototype.hasOwnProperty.call(b, k)) return false;
79
- if (!deepEqual(a[k], b[k], seen)) return false;
152
+ if (!deepEqual(a[k], rb[k], seen)) return false;
80
153
  }
81
154
  return true;
82
155
  }
83
156
  return false;
84
157
  }
158
+ var AVOIDABLE_KINDS = /* @__PURE__ */ new Set(["deep-equal", "function", "element"]);
85
159
  function classify(prev, next) {
86
160
  if (typeof prev === "function" && typeof next === "function") {
87
161
  return prev.name === next.name && prev.toString() === next.toString() ? "function" : "different";
@@ -118,33 +192,60 @@ var RerenderLens = (function (exports) {
118
192
  if (Object.is(a, b)) continue;
119
193
  const kind = classify(a, b);
120
194
  if (kind === "different") {
121
- changes.push({ path: firstDifferentPath(a, b, path), kind, prev: a, next: b });
195
+ changes.push({ path: diffBudgetExhausted() ? path : firstDifferentPath(a, b, path) ?? path, kind, prev: a, next: b });
122
196
  } else {
123
197
  changes.push({ path, kind, prev: a, next: b });
124
198
  }
125
199
  }
126
200
  return changes;
127
201
  }
128
- function firstDifferentPath(a, b, path, depth = 0) {
129
- if (depth > 8) return path;
202
+ function firstDifferentPath(a, b, path = "", depth = 0) {
203
+ if (depth > 8) return path || "(value)";
130
204
  if (Array.isArray(a) && Array.isArray(b)) {
131
- if (a.length !== b.length) return `${path}.length`;
205
+ if (a.length !== b.length) return path ? `${path}.length` : "length";
132
206
  for (let i = 0; i < a.length; i++) {
133
207
  if (!deepEqual(a[i], b[i])) return firstDifferentPath(a[i], b[i], joinPath(path, i), depth + 1);
134
208
  }
135
- return path;
209
+ return null;
136
210
  }
137
211
  if (isPlainObject(a) && isPlainObject(b)) {
138
212
  const keys = /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)]);
139
213
  for (const k of keys) {
140
214
  if (!deepEqual(a[k], b[k])) return firstDifferentPath(a[k], b[k], joinPath(path, k), depth + 1);
141
215
  }
142
- return path;
216
+ return null;
143
217
  }
144
- return path;
218
+ return deepEqual(a, b) ? null : path || "(value)";
219
+ }
220
+ function diffLeaves(a, b, limit = 20, path = "", out = []) {
221
+ if (out.length >= limit || Object.is(a, b)) return out;
222
+ if (Array.isArray(a) && Array.isArray(b)) {
223
+ const n = Math.max(a.length, b.length);
224
+ for (let i = 0; i < n && out.length < limit; i++) diffLeaves(a[i], b[i], limit, joinPath(path, i), out);
225
+ return out;
226
+ }
227
+ if (isPlainObject(a) && isPlainObject(b)) {
228
+ for (const k of /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)])) {
229
+ if (out.length >= limit) break;
230
+ diffLeaves(a[k], b[k], limit, joinPath(path, k), out);
231
+ }
232
+ return out;
233
+ }
234
+ if (!deepEqual(a, b)) out.push({ path: path || "(value)", prev: a, next: b });
235
+ return out;
145
236
  }
146
237
 
147
238
  // src/report.ts
239
+ var report_exports = {};
240
+ __export(report_exports, {
241
+ KIND_LABEL: () => KIND_LABEL,
242
+ buildReport: () => buildReport,
243
+ fixFor: () => fixFor,
244
+ hookLabel: () => hookLabel,
245
+ printReport: () => printReport,
246
+ storeAdvice: () => storeAdvice,
247
+ summarize: () => summarize
248
+ });
148
249
  var now = () => typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
149
250
  var hookLabel = (c) => c.custom && c.custom.length ? `${c.custom.join(" \u203A ")} \u203A ${c.hook}#${c.index}` : `${c.hook} #${c.index}`;
150
251
  function storeAdvice(custom) {
@@ -159,10 +260,12 @@ var RerenderLens = (function (exports) {
159
260
  }
160
261
  var isGenuine = (c) => c.kind === "different" || c.kind === "added" || c.kind === "removed";
161
262
  var isChildren = (change) => change.path === "children";
263
+ var isRefObject = (change) => change.path === "ref" && change.kind === "deep-equal";
162
264
  function fixFor(change) {
163
265
  if (isChildren(change) && (change.kind === "element" || change.kind === "deep-equal")) {
164
266
  return `lift the children out of the parent's render: memoize them with useMemo, hoist static elements to module scope, or render them from a component that does not re-render`;
165
267
  }
268
+ if (isRefObject(change)) return `create the ref once with useRef (or createRef outside the render) and pass the same object every time`;
166
269
  switch (change.kind) {
167
270
  case "function":
168
271
  return `wrap it in useCallback (or hoist it out of the parent's render)`;
@@ -178,6 +281,7 @@ var RerenderLens = (function (exports) {
178
281
  if (isChildren(change) && (change.kind === "element" || change.kind === "deep-equal")) {
179
282
  return `children are new React elements with the same types and props on every render of the parent`;
180
283
  }
284
+ if (isRefObject(change)) return `prop "ref" is a new ref object on every render (React re-attaches it each time)`;
181
285
  switch (change.kind) {
182
286
  case "deep-equal":
183
287
  return `prop "${change.path}" is a new reference but deep-equal to the previous value`;
@@ -205,11 +309,21 @@ var RerenderLens = (function (exports) {
205
309
  if (causes.length === 0) trigger = "parent";
206
310
  else if (causes.length === 1) trigger = causes[0];
207
311
  else trigger = "mixed";
208
- const avoidable = trigger === "parent";
312
+ const nothingChanged = input.propChanges.length === 0 && stateChanges.length === 0 && hookChanges.length === 0;
313
+ const cacheHit = input.compiled === true && trigger === "parent" && input.propChanges.length === 0;
314
+ const revealed = input.revealed === true && trigger === "parent";
315
+ const avoidable = trigger === "parent" && !cacheHit && !revealed;
209
316
  const reasons = [];
210
- if (avoidable && input.propChanges.length === 0 && stateChanges.length === 0 && hookChanges.length === 0) {
317
+ const memoFix = input.classComponent ? `Make "${input.component}" extend PureComponent (or implement shouldComponentUpdate)` : `Wrap "${input.component}" in React.memo (or extend PureComponent)`;
318
+ if (cacheHit) {
319
+ reasons.push(`compiled by React Compiler: its output is memoized and the inputs did not change, so this render is cheap; nothing to fix.`);
320
+ } else if (revealed) {
321
+ reasons.push(
322
+ `re-rendered because the Suspense boundary above it switched from its fallback to content: React re-renders the content it kept hidden while the fallback was shown. A one-off; nothing to fix.`
323
+ );
324
+ } else if (avoidable && nothingChanged) {
211
325
  const who = input.parent ? `<${input.parent.name}> re-rendered (${describeTrigger(input.parent.trigger)})` : "its parent re-rendered";
212
- reasons.push(`re-rendered with identical props because ${who}. Wrap "${input.component}" in React.memo (or extend PureComponent).`);
326
+ reasons.push(`re-rendered with identical props because ${who}. ${memoFix}.`);
213
327
  } else if (avoidable && input.parent) {
214
328
  reasons.push(`caused by <${input.parent.name}> re-rendering (${describeTrigger(input.parent.trigger)}).`);
215
329
  }
@@ -218,9 +332,9 @@ var RerenderLens = (function (exports) {
218
332
  reasons.push(fix ? `${describe(c)}: ${fix}.` : `${describe(c)}.`);
219
333
  }
220
334
  const memoized = input.memoized !== false;
221
- if (avoidable && !memoized && input.propChanges.length > 0) {
335
+ if (avoidable && !memoized && !input.compiled && input.propChanges.length > 0) {
222
336
  reasons.push(
223
- `"${input.component}" is not memoized, so fixing the props alone will not stop this re-render: also wrap it in React.memo (or extend PureComponent).`
337
+ `"${input.component}" is not memoized, so fixing the props alone will not stop this re-render: also ${input.classComponent ? "extend PureComponent (or implement shouldComponentUpdate)" : "wrap it in React.memo (or extend PureComponent)"}.`
224
338
  );
225
339
  }
226
340
  for (const c of stateChanges) {
@@ -232,6 +346,8 @@ var RerenderLens = (function (exports) {
232
346
  const where = c.provider && c.provider.component ? ` (provided by <${c.provider.component}>)` : "";
233
347
  const keys = c.changedKeys && typeof c.totalKeys === "number" && c.changedKeys.length > 0 && c.changedKeys.length < c.totalKeys ? `: only ${c.changedKeys.map((k) => `"${k}"`).join(", ")} of ${c.totalKeys} keys changed, yet every consumer re-renders. Split the context or memoize the slices consumers read` : "";
234
348
  reasons.push(`${c.path} changed${where}${keys}.`);
349
+ } else if (c.hook === "useDeferredValue" && isGenuine(c)) {
350
+ reasons.push(`${hookLabel(c)} caught up with its latest value: this is the deferred second render React schedules after the urgent one; nothing to fix.`);
235
351
  } else if (isGenuine(c)) reasons.push(`${hookLabel(c)} changed.`);
236
352
  else if (isStateHook(c))
237
353
  reasons.push(`${hookLabel(c)} was set to a value deep-equal to the current one (new reference, same contents): reuse the existing object or bail out before calling the setter.`);
@@ -262,6 +378,7 @@ var RerenderLens = (function (exports) {
262
378
  reasons,
263
379
  time: now()
264
380
  };
381
+ if (input.compiled) report.compiled = true;
265
382
  if (input.selfDuration !== void 0) report.selfDuration = input.selfDuration;
266
383
  if (input.treeDuration !== void 0) report.treeDuration = input.treeDuration;
267
384
  if (input.commitPriority) report.commitPriority = input.commitPriority;
@@ -298,13 +415,13 @@ var RerenderLens = (function (exports) {
298
415
  removed: "removed"
299
416
  };
300
417
  function summarize(report) {
301
- const counts2 = /* @__PURE__ */ new Map();
302
- for (const c of [...report.propChanges, ...report.stateChanges, ...report.hookChanges]) {
303
- counts2.set(c.kind, (counts2.get(c.kind) ?? 0) + 1);
418
+ const counts = /* @__PURE__ */ new Map();
419
+ for (const c of [...report.propChanges || [], ...report.stateChanges || [], ...report.hookChanges || []]) {
420
+ counts.set(c.kind, (counts.get(c.kind) ?? 0) + 1);
304
421
  }
305
- const parts = [...counts2].map(([kind, n]) => `${n} ${KIND_LABEL[kind]}`);
306
- if (parts.length === 0) parts.push("no changes");
307
- return parts.join(", ");
422
+ const parts2 = [...counts].map(([kind, n]) => `${n} ${KIND_LABEL[kind] || kind}`);
423
+ if (parts2.length === 0) parts2.push("no changes");
424
+ return parts2.join(", ");
308
425
  }
309
426
  function printReport(report, options) {
310
427
  const c = options.console ?? console;
@@ -324,12 +441,18 @@ var RerenderLens = (function (exports) {
324
441
  }
325
442
 
326
443
  // src/state.ts
444
+ var state_exports = {};
445
+ __export(state_exports, {
446
+ dispatch: () => dispatch,
447
+ getState: () => getState,
448
+ warnOnce: () => warnOnce
449
+ });
327
450
  var KEY = /* @__PURE__ */ Symbol.for("rerender-lens.state");
328
451
  function getState() {
329
452
  const g = globalThis;
330
453
  let s = g[KEY];
331
454
  if (!s) {
332
- s = { options: {}, enabled: false, printed: /* @__PURE__ */ new Map(), detach: null, nextInstanceId: 1, nextCommitId: 1, warnedOnce: /* @__PURE__ */ new Set(), scheduled: 0, commits: 0, overheadMs: 0, maxCommitMs: 0 };
455
+ s = { options: {}, enabled: false, printed: /* @__PURE__ */ new Map(), detach: null, nextInstanceId: 1, nextCommitId: 1, warnedOnce: /* @__PURE__ */ new Set(), scheduled: 0, commits: 0, overheadMs: 0, maxCommitMs: 0, truncated: 0 };
333
456
  g[KEY] = s;
334
457
  }
335
458
  return s;
@@ -342,7 +465,7 @@ var RerenderLens = (function (exports) {
342
465
  }
343
466
  function dispatch(report, override) {
344
467
  const s = getState();
345
- const options = s.options;
468
+ const options = override ? { ...s.options, ...override } : s.options;
346
469
  if (!options.silent && (report.avoidable || options.logAll)) {
347
470
  const max = options.maxReportsPerComponent ?? 0;
348
471
  const n = (s.printed.get(report.component) ?? 0) + 1;
@@ -359,12 +482,348 @@ var RerenderLens = (function (exports) {
359
482
  try {
360
483
  options.notifier(report);
361
484
  } catch (err) {
362
- (options.console ?? console).warn("[rerender-lens] notifier threw", err);
485
+ warnOnce("notifier", `notifier threw: ${String(err)}`);
363
486
  }
364
487
  }
365
488
  }
366
489
 
490
+ // src/overlay.ts
491
+ var overlay_exports = {};
492
+ __export(overlay_exports, {
493
+ clearHighlight: () => clearHighlight,
494
+ destroyOverlay: () => destroyOverlay,
495
+ flash: () => flash,
496
+ highlight: () => highlight
497
+ });
498
+ var ROOT_ID = "rerender-lens-overlay";
499
+ var layer = null;
500
+ function ensureLayer() {
501
+ if (typeof document === "undefined" || !document.body) return null;
502
+ if (layer && layer.root.isConnected) return layer;
503
+ let root = document.getElementById(ROOT_ID);
504
+ if (!root) {
505
+ root = document.createElement("div");
506
+ root.id = ROOT_ID;
507
+ root.setAttribute("aria-hidden", "true");
508
+ Object.assign(root.style, {
509
+ position: "fixed",
510
+ inset: "0",
511
+ pointerEvents: "none",
512
+ zIndex: "2147483647",
513
+ overflow: "visible"
514
+ });
515
+ document.body.appendChild(root);
516
+ }
517
+ layer = { root, sticky: [] };
518
+ return layer;
519
+ }
520
+ function box(rect, color, fill, label) {
521
+ const b = document.createElement("div");
522
+ Object.assign(b.style, {
523
+ position: "absolute",
524
+ left: `${rect.left}px`,
525
+ top: `${rect.top}px`,
526
+ width: `${Math.max(rect.width, 2)}px`,
527
+ height: `${Math.max(rect.height, 2)}px`,
528
+ boxSizing: "border-box",
529
+ border: `2px solid ${color}`,
530
+ background: fill,
531
+ borderRadius: "2px"
532
+ });
533
+ if (label) {
534
+ const tag = document.createElement("span");
535
+ tag.textContent = label;
536
+ Object.assign(tag.style, {
537
+ position: "absolute",
538
+ left: "-2px",
539
+ top: rect.top > 20 ? "-20px" : "100%",
540
+ font: "11px/16px system-ui, sans-serif",
541
+ color: "#fff",
542
+ background: color,
543
+ padding: "1px 5px",
544
+ borderRadius: "2px",
545
+ whiteSpace: "nowrap"
546
+ });
547
+ b.appendChild(tag);
548
+ }
549
+ return b;
550
+ }
551
+ function highlight(target) {
552
+ const l = ensureLayer();
553
+ if (!l) return;
554
+ for (const s of l.sticky) s.remove();
555
+ l.sticky = [];
556
+ if (!target) return;
557
+ const frag = document.createDocumentFragment();
558
+ for (const rect of measure(target.nodes)) {
559
+ const b = box(rect, "#1a73e8", "rgba(26, 115, 232, 0.12)", target.label);
560
+ frag.appendChild(b);
561
+ l.sticky.push(b);
562
+ }
563
+ l.root.appendChild(frag);
564
+ }
565
+ var MAX_BOXES = 100;
566
+ function measure(nodes) {
567
+ const rects = [];
568
+ for (const node of nodes) {
569
+ if (rects.length >= MAX_BOXES) break;
570
+ const rect = node.getBoundingClientRect();
571
+ if (rect.width === 0 && rect.height === 0) continue;
572
+ rects.push(rect);
573
+ }
574
+ return rects;
575
+ }
576
+ function clearHighlight() {
577
+ highlight(null);
578
+ }
579
+ function flash(target, duration = 500) {
580
+ const l = ensureLayer();
581
+ if (!l) return;
582
+ const frag = document.createDocumentFragment();
583
+ const boxes = [];
584
+ for (const rect of measure(target.nodes)) {
585
+ const b = box(rect, "#d93025", "rgba(217, 48, 37, 0.15)");
586
+ b.style.transition = `opacity ${duration}ms ease-out`;
587
+ frag.appendChild(b);
588
+ boxes.push(b);
589
+ }
590
+ if (!boxes.length) return;
591
+ l.root.appendChild(frag);
592
+ requestAnimationFrame(() => {
593
+ for (const b of boxes) b.style.opacity = "0";
594
+ });
595
+ setTimeout(() => {
596
+ for (const b of boxes) b.remove();
597
+ }, duration + 50);
598
+ }
599
+ function destroyOverlay() {
600
+ if (layer) layer.root.remove();
601
+ layer = null;
602
+ }
603
+
604
+ // vendor-rerender-lens.core.js
605
+ var parts = globalThis.__RERENDER_LENS_PARTS__ = globalThis.__RERENDER_LENS_PARTS__ || {};
606
+ parts["types"] = types_exports;
607
+ parts["version"] = version_exports;
608
+ parts["diff"] = diff_exports;
609
+ parts["report"] = report_exports;
610
+ parts["state"] = state_exports;
611
+ parts["overlay"] = overlay_exports;
612
+ })();
613
+
614
+ ;
615
+ /* rerender-lens 0.5.0 — rerender-lens.engine.js: fiber, tracker, hookNames. Built by scripts/build-vendor.mjs; do not edit. */
616
+ (() => {
617
+ var __create = Object.create;
618
+ var __defProp = Object.defineProperty;
619
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
620
+ var __getOwnPropNames = Object.getOwnPropertyNames;
621
+ var __getProtoOf = Object.getPrototypeOf;
622
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
623
+ var __commonJS = (cb, mod) => function __require() {
624
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
625
+ };
626
+ var __export = (target, all) => {
627
+ for (var name in all)
628
+ __defProp(target, name, { get: all[name], enumerable: true });
629
+ };
630
+ var __copyProps = (to, from, except, desc) => {
631
+ if (from && typeof from === "object" || typeof from === "function") {
632
+ for (let key of __getOwnPropNames(from))
633
+ if (!__hasOwnProp.call(to, key) && key !== except)
634
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
635
+ }
636
+ return to;
637
+ };
638
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
639
+ // If the importer is in node compatibility mode or this is not an ESM
640
+ // file that has been converted to a CommonJS file using a Babel-
641
+ // compatible transform (i.e. "__esModule" has not been set), then set
642
+ // "default" to the CommonJS "module.exports" for node compatibility.
643
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
644
+ mod
645
+ ));
646
+
647
+ // rerender-lens-part:diff
648
+ var require_diff = __commonJS({
649
+ "rerender-lens-part:diff"(exports, module) {
650
+ var p = globalThis.__RERENDER_LENS_PARTS__;
651
+ if (!p || !p["diff"]) throw new Error("rerender-lens: load vendor/rerender-lens.core.js and vendor/rerender-lens.engine.js before vendor/rerender-lens.js");
652
+ module.exports = p["diff"];
653
+ }
654
+ });
655
+
656
+ // rerender-lens-part:report
657
+ var require_report = __commonJS({
658
+ "rerender-lens-part:report"(exports, module) {
659
+ var p = globalThis.__RERENDER_LENS_PARTS__;
660
+ if (!p || !p["report"]) throw new Error("rerender-lens: load vendor/rerender-lens.core.js and vendor/rerender-lens.engine.js before vendor/rerender-lens.js");
661
+ module.exports = p["report"];
662
+ }
663
+ });
664
+
665
+ // rerender-lens-part:state
666
+ var require_state = __commonJS({
667
+ "rerender-lens-part:state"(exports, module) {
668
+ var p = globalThis.__RERENDER_LENS_PARTS__;
669
+ if (!p || !p["state"]) throw new Error("rerender-lens: load vendor/rerender-lens.core.js and vendor/rerender-lens.engine.js before vendor/rerender-lens.js");
670
+ module.exports = p["state"];
671
+ }
672
+ });
673
+
674
+ // rerender-lens-part:types
675
+ var require_types = __commonJS({
676
+ "rerender-lens-part:types"(exports, module) {
677
+ var p = globalThis.__RERENDER_LENS_PARTS__;
678
+ if (!p || !p["types"]) throw new Error("rerender-lens: load vendor/rerender-lens.core.js and vendor/rerender-lens.engine.js before vendor/rerender-lens.js");
679
+ module.exports = p["types"];
680
+ }
681
+ });
682
+
683
+ // src/fiber.ts
684
+ var fiber_exports = {};
685
+ __export(fiber_exports, {
686
+ COMMIT_TIME_BUDGET_MS: () => COMMIT_TIME_BUDGET_MS,
687
+ MAX_REPORTS_PER_COMMIT: () => MAX_REPORTS_PER_COMMIT,
688
+ attach: () => attach,
689
+ durationsOf: () => durationsOf,
690
+ ensureDevtoolsHook: () => ensureDevtoolsHook,
691
+ fiberById: () => fiberById,
692
+ fiberForNode: () => fiberForNode,
693
+ fiberName: () => fiberName,
694
+ fiberType: () => fiberType,
695
+ findProvider: () => findProvider,
696
+ getDispatcherRef: () => getDispatcherRef,
697
+ getRenderers: () => getRenderers,
698
+ hostNodesOf: () => hostNodesOf,
699
+ instanceIdOf: () => instanceIdOf,
700
+ isCompiledFiber: () => isCompiledFiber,
701
+ isMemoizedFiber: () => isMemoizedFiber,
702
+ isProductionReact: () => isProductionReact,
703
+ nearestComponent: () => nearestComponent,
704
+ onCommit: () => onCommit,
705
+ parseStackLocation: () => parseStackLocation,
706
+ priorityLabel: () => priorityLabel,
707
+ snapshotContexts: () => snapshotContexts,
708
+ snapshotHooks: () => snapshotHooks,
709
+ sourceOf: () => sourceOf
710
+ });
711
+ var import_diff = __toESM(require_diff(), 1);
712
+ var import_report = __toESM(require_report(), 1);
713
+ var import_state2 = __toESM(require_state(), 1);
714
+
715
+ // src/tracker.ts
716
+ var tracker_exports = {};
717
+ __export(tracker_exports, {
718
+ configure: () => configure,
719
+ disable: () => disable,
720
+ getDisplayName: () => getDisplayName,
721
+ init: () => init,
722
+ isEnabled: () => isEnabled,
723
+ shouldTrack: () => shouldTrack,
724
+ track: () => track
725
+ });
726
+ var import_types = __toESM(require_types(), 1);
727
+ var import_state = __toESM(require_state(), 1);
728
+ var MEMO = /* @__PURE__ */ Symbol.for("react.memo");
729
+ var FORWARD_REF = /* @__PURE__ */ Symbol.for("react.forward_ref");
730
+ var isMemo = (t) => typeof t === "object" && t !== null && t.$$typeof === MEMO;
731
+ var isForwardRef = (t) => typeof t === "object" && t !== null && t.$$typeof === FORWARD_REF;
732
+ var isClass = (t) => typeof t === "function" && !!t.prototype?.isReactComponent;
733
+ var isPureClass = (t) => isClass(t) && !!t.prototype.isPureReactComponent;
734
+ var isComponentLike = (t) => typeof t === "function" || isMemo(t) || isForwardRef(t);
735
+ function getDisplayName(type) {
736
+ if (typeof type === "string") return type;
737
+ if (isMemo(type)) return type.displayName ?? getDisplayName(type.type);
738
+ if (isForwardRef(type)) return type.displayName ?? getDisplayName(type.render);
739
+ if (typeof type === "function") {
740
+ const t = type;
741
+ return t.displayName ?? (t.name || "Anonymous");
742
+ }
743
+ return "Anonymous";
744
+ }
745
+ function hasMarker(type) {
746
+ if (type === null || typeof type !== "function" && typeof type !== "object") return false;
747
+ if (type[import_types.MARKER] === true) return true;
748
+ if (isMemo(type)) return hasMarker(type.type);
749
+ if (isForwardRef(type)) return hasMarker(type.render);
750
+ return false;
751
+ }
752
+ function matches(m, name) {
753
+ if (typeof m === "string") return m === name;
754
+ if (m instanceof RegExp) return m.test(name);
755
+ return m(name);
756
+ }
757
+ function shouldTrack(type, o) {
758
+ if (!isComponentLike(type)) return false;
759
+ const name = getDisplayName(type);
760
+ if (o.exclude?.some((m) => matches(m, name))) return false;
761
+ if (hasMarker(type)) return true;
762
+ if (o.include?.some((m) => matches(m, name))) return true;
763
+ if (o.trackAllComponents) return true;
764
+ if (o.trackAllMemoized) return isMemo(type) || isPureClass(type);
765
+ return false;
766
+ }
767
+ function init(options = {}) {
768
+ const s = (0, import_state.getState)();
769
+ s.options = { ...options };
770
+ s.printed.clear();
771
+ if (!s.enabled) {
772
+ s.detach = attach();
773
+ s.enabled = true;
774
+ }
775
+ return disable;
776
+ }
777
+ function configure(options) {
778
+ const s = (0, import_state.getState)();
779
+ s.options = { ...s.options, ...options };
780
+ }
781
+ function disable() {
782
+ const s = (0, import_state.getState)();
783
+ if (!s.enabled) return;
784
+ s.detach?.();
785
+ s.detach = null;
786
+ s.enabled = false;
787
+ }
788
+ function isEnabled() {
789
+ return (0, import_state.getState)().enabled;
790
+ }
791
+ function track(component, name) {
792
+ const t = component;
793
+ t[import_types.MARKER] = true;
794
+ if (name) t.displayName = name;
795
+ return component;
796
+ }
797
+
367
798
  // src/hookNames.ts
799
+ var hookNames_exports = {};
800
+ __export(hookNames_exports, {
801
+ HOOK_NODE_COUNT: () => NODE_COUNT,
802
+ clearHookNameCache: () => clearHookNameCache,
803
+ customHooksFromStack: () => customHooksFromStack,
804
+ resolveHookNames: () => resolveHookNames
805
+ });
806
+ var NODE_COUNT = {
807
+ useState: 1,
808
+ useReducer: 1,
809
+ useRef: 1,
810
+ useMemo: 1,
811
+ useCallback: 1,
812
+ useEffect: 1,
813
+ useLayoutEffect: 1,
814
+ useInsertionEffect: 1,
815
+ useImperativeHandle: 1,
816
+ useSyncExternalStore: 2,
817
+ useTransition: 2,
818
+ useDeferredValue: 1,
819
+ useId: 1,
820
+ useOptimistic: 1,
821
+ useActionState: 3,
822
+ useFormState: 3,
823
+ useEffectEvent: 1,
824
+ useCacheRefresh: 1
825
+ };
826
+ var BUILTIN_HOOKS = /* @__PURE__ */ new Set([...Object.keys(NODE_COUNT), "useContext", "useDebugValue", "use"]);
368
827
  var noop = () => {
369
828
  };
370
829
  function customHooksFromStack(stack, componentName, marker) {
@@ -379,7 +838,7 @@ var RerenderLens = (function (exports) {
379
838
  continue;
380
839
  }
381
840
  if (name === componentName || name.endsWith("." + componentName)) break;
382
- if (/^use[A-Z0-9_]/.test(name)) names.push(name);
841
+ if (/^use[A-Z0-9_]/.test(name) && !BUILTIN_HOOKS.has(name)) names.push(name);
383
842
  if (names.length > 8) break;
384
843
  }
385
844
  return names;
@@ -528,6 +987,8 @@ var RerenderLens = (function (exports) {
528
987
  }
529
988
  return result.byNode;
530
989
  }
990
+ function clearHookNameCache() {
991
+ }
531
992
 
532
993
  // src/fiber.ts
533
994
  var FunctionComponent = 0;
@@ -616,10 +1077,10 @@ var RerenderLens = (function (exports) {
616
1077
  try {
617
1078
  onCommit(root, priorityLabel(priority));
618
1079
  } catch (err) {
619
- warnOnce("commit", `failed to inspect a commit: ${String(err)}`);
1080
+ (0, import_state2.warnOnce)("commit", `failed to inspect a commit: ${String(err)}`);
620
1081
  } finally {
621
1082
  const spent = nowMs() - started;
622
- const s = getState();
1083
+ const s = (0, import_state2.getState)();
623
1084
  s.overheadMs += spent;
624
1085
  if (spent > s.maxCommitMs) s.maxCommitMs = spent;
625
1086
  }
@@ -628,7 +1089,7 @@ var RerenderLens = (function (exports) {
628
1089
  hook.onCommitFiberRoot = patched;
629
1090
  const previousSchedule = hook.onScheduleFiberRoot;
630
1091
  const patchedSchedule = function(id, root, children) {
631
- getState().scheduled++;
1092
+ (0, import_state2.getState)().scheduled++;
632
1093
  if (typeof previousSchedule === "function") return previousSchedule.call(this, id, root, children);
633
1094
  };
634
1095
  hook.onScheduleFiberRoot = patchedSchedule;
@@ -679,6 +1140,11 @@ var RerenderLens = (function (exports) {
679
1140
  }
680
1141
  return false;
681
1142
  }
1143
+ function isCompiledFiber(fiber) {
1144
+ if (fiber.tag === ClassComponent) return false;
1145
+ const cache2 = fiber.updateQueue?.memoCache;
1146
+ return !!cache2 && Array.isArray(cache2.data);
1147
+ }
682
1148
  function durationsOf(fiber) {
683
1149
  if (typeof fiber.actualDuration !== "number") return null;
684
1150
  const tree = fiber.actualDuration;
@@ -702,11 +1168,14 @@ var RerenderLens = (function (exports) {
702
1168
  var ids = /* @__PURE__ */ new WeakMap();
703
1169
  var fibersById = /* @__PURE__ */ new Map();
704
1170
  var hasWeakRef = typeof WeakRef === "function";
705
- function remember2(id, fiber) {
1171
+ var pruneAt = 5e3;
1172
+ function remember(id, fiber) {
706
1173
  if (!hasWeakRef) return;
1174
+ if (fibersById.get(id)?.deref() === fiber) return;
707
1175
  fibersById.set(id, new WeakRef(fiber));
708
- if (fibersById.size > 5e3) {
1176
+ if (fibersById.size > pruneAt) {
709
1177
  for (const [k, ref] of fibersById) if (!ref.deref()) fibersById.delete(k);
1178
+ pruneAt = Math.max(5e3, fibersById.size * 2);
710
1179
  }
711
1180
  }
712
1181
  function instanceIdOf(fiber) {
@@ -723,13 +1192,13 @@ var RerenderLens = (function (exports) {
723
1192
  const existing = instanceIdOf(fiber);
724
1193
  if (existing !== void 0) {
725
1194
  ids.set(fiber, existing);
726
- remember2(existing, fiber);
1195
+ remember(existing, fiber);
727
1196
  return existing;
728
1197
  }
729
- const s = getState();
1198
+ const s = (0, import_state2.getState)();
730
1199
  const id = s.nextInstanceId++;
731
1200
  ids.set(fiber, id);
732
- remember2(id, fiber);
1201
+ remember(id, fiber);
733
1202
  return id;
734
1203
  }
735
1204
  function hostNodesOf(fiber, limit = 50) {
@@ -761,7 +1230,7 @@ var RerenderLens = (function (exports) {
761
1230
  }
762
1231
  var INTERNAL_FRAME = /react-dom|react_jsx|jsx-(dev-)?runtime|\/react\/|node_modules\/react|react-stack-bottom-frame|react_stack_bottom_frame|scheduler/;
763
1232
  function parseStackLocation(stack) {
764
- for (const line of stack.split("\n")) {
1233
+ for (const line of stack.split("\n", 24)) {
765
1234
  const m = /(?:at\s+(?:.*?\s+)?\(?|@)?((?:https?|file|webpack|vite|blob):[^\s()]+?):(\d+):(\d+)\)?\s*$/.exec(line.trim());
766
1235
  if (!m || !m[1] || INTERNAL_FRAME.test(m[1])) continue;
767
1236
  return { fileName: m[1], lineNumber: Number(m[2]), columnNumber: Number(m[3]) };
@@ -777,9 +1246,16 @@ var RerenderLens = (function (exports) {
777
1246
  return out;
778
1247
  }
779
1248
  const st = fiber._debugStack;
780
- const text = typeof st === "string" ? st : st && typeof st.stack === "string" ? st.stack : null;
781
- return text ? parseStackLocation(text) : void 0;
1249
+ if (st && typeof st === "object") {
1250
+ if (sourceCache.has(st)) return sourceCache.get(st);
1251
+ const text = typeof st.stack === "string" ? st.stack : null;
1252
+ const out = text ? parseStackLocation(text) : void 0;
1253
+ sourceCache.set(st, out);
1254
+ return out;
1255
+ }
1256
+ return typeof st === "string" ? parseStackLocation(st) : void 0;
782
1257
  }
1258
+ var sourceCache = /* @__PURE__ */ new WeakMap();
783
1259
  function bumpCount(fiber) {
784
1260
  const prev = counts.get(fiber) ?? (fiber.alternate ? counts.get(fiber.alternate) : void 0) ?? 0;
785
1261
  const n = prev + 1;
@@ -803,7 +1279,7 @@ var RerenderLens = (function (exports) {
803
1279
  }
804
1280
  var isStateNode = (n) => !!n.queue && typeof n.queue.lastRenderedReducer === "function";
805
1281
  var isStoreNode = (n) => !!n.queue && typeof n.queue.getSnapshot === "function";
806
- function hookLabel2(node) {
1282
+ function hookLabel(node) {
807
1283
  if (isStoreNode(node)) return "useSyncExternalStore";
808
1284
  if (isStateNode(node)) {
809
1285
  const r = node.queue.lastRenderedReducer;
@@ -828,7 +1304,7 @@ var RerenderLens = (function (exports) {
828
1304
  let i = 0;
829
1305
  for (let n = first; n; n = n.next, i++) {
830
1306
  if (!isStateNode(n) && !isStoreNode(n)) continue;
831
- const hook = labels?.[i] ?? hookLabel2(n);
1307
+ const hook = labels?.[i] ?? hookLabel(n);
832
1308
  out.push({ path: `${hook}#${i}`, hook, index: i, value: n.memoizedState });
833
1309
  }
834
1310
  return out;
@@ -847,13 +1323,14 @@ var RerenderLens = (function (exports) {
847
1323
  const labels = hookLabelsFor(fiber, b);
848
1324
  let i = 0;
849
1325
  while (a && b) {
850
- if ((isStateNode(b) || isStoreNode(b)) && !Object.is(a.memoizedState, b.memoizedState)) {
851
- const hook = labels?.[i] ?? hookLabel2(b);
1326
+ const deferred = labels?.[i] === "useDeferredValue";
1327
+ if ((deferred || isStateNode(b) || isStoreNode(b)) && !Object.is(a.memoizedState, b.memoizedState)) {
1328
+ const hook = labels?.[i] ?? hookLabel(b);
852
1329
  out.push({
853
1330
  path: `${hook}#${i}`,
854
1331
  hook,
855
1332
  index: i,
856
- kind: classify(a.memoizedState, b.memoizedState),
1333
+ kind: (0, import_diff.classify)(a.memoizedState, b.memoizedState),
857
1334
  prev: a.memoizedState,
858
1335
  next: b.memoizedState
859
1336
  });
@@ -875,12 +1352,16 @@ var RerenderLens = (function (exports) {
875
1352
  }
876
1353
  return null;
877
1354
  }
878
- var isPlainObject2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v) && Object.getPrototypeOf(v) === Object.prototype;
1355
+ var isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v) && Object.getPrototypeOf(v) === Object.prototype;
879
1356
  function diffContexts(fiber, alt) {
880
1357
  const out = [];
881
1358
  let a = alt.dependencies?.firstContext ?? null;
882
1359
  let b = fiber.dependencies?.firstContext ?? null;
883
1360
  let i = 0;
1361
+ if (b && !("memoizedValue" in b)) {
1362
+ (0, import_state2.warnOnce)("contextValues", "this React does not record context values on the fiber (React < 18): context changes are reported as plain parent re-renders.");
1363
+ return out;
1364
+ }
884
1365
  while (a && b) {
885
1366
  if (a.context === b.context && !Object.is(a.memoizedValue, b.memoizedValue)) {
886
1367
  const name = b.context.displayName ?? "Context";
@@ -888,7 +1369,7 @@ var RerenderLens = (function (exports) {
888
1369
  path: `useContext(${name})`,
889
1370
  hook: "useContext",
890
1371
  index: i,
891
- kind: classify(a.memoizedValue, b.memoizedValue),
1372
+ kind: (0, import_diff.classify)(a.memoizedValue, b.memoizedValue),
892
1373
  prev: a.memoizedValue,
893
1374
  next: b.memoizedValue
894
1375
  };
@@ -897,7 +1378,7 @@ var RerenderLens = (function (exports) {
897
1378
  const path = componentPath(provider);
898
1379
  change.provider = { component: path[path.length - 1] ?? null, path };
899
1380
  }
900
- if (isPlainObject2(a.memoizedValue) && isPlainObject2(b.memoizedValue)) {
1381
+ if (isPlainObject(a.memoizedValue) && isPlainObject(b.memoizedValue)) {
901
1382
  const prev = a.memoizedValue;
902
1383
  const next = b.memoizedValue;
903
1384
  const keys = /* @__PURE__ */ new Set([...Object.keys(prev), ...Object.keys(next)]);
@@ -912,16 +1393,34 @@ var RerenderLens = (function (exports) {
912
1393
  }
913
1394
  return out;
914
1395
  }
915
- var isGenuine2 = (c) => c.kind === "different" || c.kind === "added" || c.kind === "removed";
1396
+ var isGenuine = (c) => c.kind === "different" || c.kind === "added" || c.kind === "removed";
1397
+ var isRefObject = (v) => {
1398
+ if (typeof v !== "object" || v === null) return false;
1399
+ const keys = Object.keys(v);
1400
+ return keys.length === 1 && keys[0] === "current";
1401
+ };
1402
+ function diffProps(prev, next) {
1403
+ if (prev === next) return [];
1404
+ const pr = prev.ref;
1405
+ const nr = next.ref;
1406
+ if (pr !== nr && isRefObject(pr) && isRefObject(nr)) {
1407
+ const { ref: _p, ...restPrev } = prev;
1408
+ const { ref: _n, ...restNext } = next;
1409
+ void _p;
1410
+ void _n;
1411
+ return [{ path: "ref", kind: "deep-equal", prev: pr, next: nr }, ...(0, import_diff.diffRecords)(restPrev, restNext)];
1412
+ }
1413
+ return (0, import_diff.diffRecords)(prev, next);
1414
+ }
916
1415
  function analyze(fiber, alt, trackHooks) {
917
1416
  const prevProps = alt.memoizedProps ?? {};
918
1417
  const nextProps = fiber.memoizedProps ?? {};
919
- const propChanges = prevProps === nextProps ? [] : diffRecords(prevProps, nextProps);
1418
+ const propChanges = diffProps(prevProps, nextProps);
920
1419
  let stateChanges = [];
921
1420
  let hookChanges = [];
922
1421
  if (fiber.tag === ClassComponent) {
923
1422
  if (alt.memoizedState !== fiber.memoizedState) {
924
- stateChanges = diffRecords(
1423
+ stateChanges = (0, import_diff.diffRecords)(
925
1424
  alt.memoizedState ?? {},
926
1425
  fiber.memoizedState ?? {}
927
1426
  );
@@ -929,11 +1428,11 @@ var RerenderLens = (function (exports) {
929
1428
  } else if (trackHooks) {
930
1429
  hookChanges = [...diffHooks(fiber, alt), ...diffContexts(fiber, alt)];
931
1430
  }
932
- const isStateHook = (h) => h.hook !== "useContext" && h.hook !== "useSyncExternalStore";
1431
+ const isStateHook = (h) => h.hook !== "useContext" && h.hook !== "useSyncExternalStore" && h.hook !== "useDeferredValue";
933
1432
  const causes = [];
934
- if (propChanges.some(isGenuine2)) causes.push("props");
935
- if (stateChanges.some(isGenuine2) || hookChanges.some((h) => isStateHook(h) && isGenuine2(h))) causes.push("state");
936
- if (hookChanges.some((h) => !isStateHook(h) && isGenuine2(h))) causes.push("hooks");
1433
+ if (propChanges.some(isGenuine)) causes.push("props");
1434
+ if (stateChanges.some(isGenuine) || hookChanges.some((h) => isStateHook(h) && isGenuine(h))) causes.push("state");
1435
+ if (hookChanges.some((h) => !isStateHook(h) && isGenuine(h))) causes.push("hooks");
937
1436
  const trigger = causes.length === 0 ? "parent" : causes.length === 1 ? causes[0] : "mixed";
938
1437
  return { propChanges, stateChanges, hookChanges, trigger };
939
1438
  }
@@ -967,8 +1466,28 @@ var RerenderLens = (function (exports) {
967
1466
  }
968
1467
  var lastCommit = null;
969
1468
  var EFFECT_LOOP_WINDOW_MS = 50;
1469
+ var MAX_REPORTS_PER_COMMIT = 200;
1470
+ var COMMIT_TIME_BUDGET_MS = 25;
1471
+ function isDeferredCatchUp(fiber) {
1472
+ const types = fiber._debugHookTypes;
1473
+ if (!types || !types.includes("useDeferredValue") || !fiber.alternate) return false;
1474
+ const first = fiber.memoizedState;
1475
+ if (!isHookList(first)) return false;
1476
+ const labels = hookLabelsFor(fiber, first);
1477
+ if (!labels) return false;
1478
+ let a = fiber.alternate.memoizedState;
1479
+ let b = first;
1480
+ for (let i = 0; a && b; a = a.next, b = b.next, i++) {
1481
+ if (labels[i] === "useDeferredValue" && !Object.is(a.memoizedState, b.memoizedState)) return true;
1482
+ }
1483
+ return false;
1484
+ }
1485
+ function isUnder(fiber, boundaries) {
1486
+ for (let f = fiber.return; f && f.tag !== HostRoot; f = f.return) if (f.tag === SuspenseComponent && boundaries.includes(f)) return true;
1487
+ return false;
1488
+ }
970
1489
  function onCommit(root, commitPriority) {
971
- const s = getState();
1490
+ const s = (0, import_state2.getState)();
972
1491
  if (!s.enabled) return;
973
1492
  s.commits++;
974
1493
  const o = s.options;
@@ -976,7 +1495,7 @@ var RerenderLens = (function (exports) {
976
1495
  const rendered = [];
977
1496
  const renderedNames = /* @__PURE__ */ new Set();
978
1497
  let hot = false;
979
- let suspenseResolved = false;
1498
+ let resolvedBoundaries = null;
980
1499
  const stack = [root.current];
981
1500
  while (stack.length) {
982
1501
  const fiber = stack.pop();
@@ -986,7 +1505,7 @@ var RerenderLens = (function (exports) {
986
1505
  renderedNames.add(fiberName(fiber));
987
1506
  if (shouldTrack(fiberType(fiber), o)) rendered.push(fiber);
988
1507
  }
989
- if (fiber.tag === SuspenseComponent && alt && alt.memoizedState !== null && fiber.memoizedState === null) suspenseResolved = true;
1508
+ if (fiber.tag === SuspenseComponent && alt && alt.memoizedState !== null && fiber.memoizedState === null) (resolvedBoundaries ?? (resolvedBoundaries = [])).push(fiber);
990
1509
  if (!alt || fiber.child !== alt.child) {
991
1510
  for (let c = fiber.child; c; c = c.sibling) stack.push(c);
992
1511
  }
@@ -997,10 +1516,11 @@ var RerenderLens = (function (exports) {
997
1516
  const updaters = updatersOf(root);
998
1517
  let commitCause;
999
1518
  let afterCommit;
1000
- if (lastCommit && at - lastCommit.at < EFFECT_LOOP_WINDOW_MS && updaters.some((u) => lastCommit.rendered.has(u))) {
1519
+ const inputDriven = commitPriority === "immediate" || commitPriority === "user-blocking";
1520
+ if (!inputDriven && lastCommit && at - lastCommit.at < EFFECT_LOOP_WINDOW_MS && updaters.some((u) => lastCommit.rendered.has(u)) && !rendered.some(isDeferredCatchUp)) {
1001
1521
  commitCause = "effect-after-commit";
1002
1522
  afterCommit = lastCommit.id;
1003
- } else if (suspenseResolved) commitCause = "suspense-resolved";
1523
+ } else if (resolvedBoundaries) commitCause = "suspense-resolved";
1004
1524
  if (rendered.length === 0) {
1005
1525
  lastCommit = { id: s.nextCommitId, at, rendered: renderedNames };
1006
1526
  return;
@@ -1009,153 +1529,414 @@ var RerenderLens = (function (exports) {
1009
1529
  lastCommit = { id: commitId, at, rendered: renderedNames };
1010
1530
  const dispatcherRef = o.resolveHookNames ? getDispatcherRef() : null;
1011
1531
  const parentCache = /* @__PURE__ */ new Map();
1012
- for (const fiber of rendered) {
1013
- const alt = fiber.alternate;
1014
- const a = analyze(fiber, alt, trackHooks);
1015
- const durations = durationsOf(fiber);
1016
- let hookState = o.includeState !== false && trackHooks ? snapshotHooks(fiber) : void 0;
1017
- if (dispatcherRef && fiber.tag !== ClassComponent && (a.hookChanges.length || hookState && hookState.length)) {
1018
- const names = resolveHookNames(fiber, dispatcherRef);
1019
- if (names.size) {
1020
- for (const c of a.hookChanges) if (c.hook !== "useContext" && names.has(c.index)) c.custom = names.get(c.index);
1021
- if (hookState) hookState = hookState.map((h) => names.has(h.index) ? { ...h, custom: names.get(h.index) } : h);
1532
+ const endDiffScope = (0, import_diff.beginDiffScope)();
1533
+ const deadline = at + COMMIT_TIME_BUDGET_MS;
1534
+ try {
1535
+ for (let i = 0; i < rendered.length; i++) {
1536
+ if (i >= MAX_REPORTS_PER_COMMIT || (i & 15) === 15 && nowMs() > deadline) {
1537
+ s.truncated += rendered.length - i;
1538
+ (0, import_state2.warnOnce)("truncated", `a commit rendered ${rendered.length} tracked components; only ${i} were reported (see info().truncated). Narrow \`include\` or turn off includeState.`);
1539
+ break;
1540
+ }
1541
+ const fiber = rendered[i];
1542
+ const alt = fiber.alternate;
1543
+ const a = analyze(fiber, alt, trackHooks);
1544
+ const durations = durationsOf(fiber);
1545
+ let hookState = o.includeState !== false && trackHooks ? snapshotHooks(fiber) : void 0;
1546
+ if (dispatcherRef && fiber.tag !== ClassComponent && (a.hookChanges.length || hookState && hookState.length)) {
1547
+ const names = resolveHookNames(fiber, dispatcherRef);
1548
+ if (names.size) {
1549
+ for (const c of a.hookChanges) if (c.hook !== "useContext" && names.has(c.index)) c.custom = names.get(c.index);
1550
+ if (hookState) hookState = hookState.map((h) => names.has(h.index) ? { ...h, custom: names.get(h.index) } : h);
1551
+ }
1022
1552
  }
1553
+ const report = (0, import_report.buildReport)({
1554
+ component: fiberName(fiber),
1555
+ instanceId: instanceId(fiber),
1556
+ renderCount: bumpCount(fiber),
1557
+ prevProps: alt.memoizedProps ?? {},
1558
+ nextProps: fiber.memoizedProps ?? {},
1559
+ propChanges: a.propChanges,
1560
+ stateChanges: a.stateChanges,
1561
+ hookChanges: a.hookChanges,
1562
+ ...o.includeState !== false ? {
1563
+ hookState,
1564
+ contexts: trackHooks ? snapshotContexts(fiber) : void 0,
1565
+ state: fiber.tag === ClassComponent && fiber.memoizedState && typeof fiber.memoizedState === "object" ? fiber.memoizedState : void 0
1566
+ } : {},
1567
+ updaters,
1568
+ commitCause,
1569
+ afterCommit,
1570
+ key: fiber.key === null || fiber.key === void 0 ? null : String(fiber.key),
1571
+ parent: a.trigger === "parent" ? nearestRenderedAncestor(fiber, parentCache, trackHooks) : null,
1572
+ owner: ownerName(fiber),
1573
+ path: componentPath(fiber),
1574
+ memoized: isMemoizedFiber(fiber),
1575
+ compiled: isCompiledFiber(fiber),
1576
+ classComponent: fiber.tag === ClassComponent,
1577
+ revealed: resolvedBoundaries !== null && a.trigger === "parent" && isUnder(fiber, resolvedBoundaries),
1578
+ selfDuration: durations ? durations.self : void 0,
1579
+ treeDuration: durations ? durations.tree : void 0,
1580
+ commitId,
1581
+ commitPriority,
1582
+ source: sourceOf(fiber)
1583
+ });
1584
+ (0, import_state2.dispatch)(report);
1023
1585
  }
1024
- const report = buildReport({
1025
- component: fiberName(fiber),
1026
- instanceId: instanceId(fiber),
1027
- renderCount: bumpCount(fiber),
1028
- prevProps: alt.memoizedProps ?? {},
1029
- nextProps: fiber.memoizedProps ?? {},
1030
- propChanges: a.propChanges,
1031
- stateChanges: a.stateChanges,
1032
- hookChanges: a.hookChanges,
1033
- ...o.includeState !== false ? {
1034
- hookState,
1035
- contexts: trackHooks ? snapshotContexts(fiber) : void 0,
1036
- state: fiber.tag === ClassComponent && fiber.memoizedState && typeof fiber.memoizedState === "object" ? fiber.memoizedState : void 0
1037
- } : {},
1038
- updaters,
1039
- commitCause,
1040
- afterCommit,
1041
- key: fiber.key === null || fiber.key === void 0 ? null : String(fiber.key),
1042
- parent: a.trigger === "parent" ? nearestRenderedAncestor(fiber, parentCache, trackHooks) : null,
1043
- owner: ownerName(fiber),
1044
- path: componentPath(fiber),
1045
- memoized: isMemoizedFiber(fiber),
1046
- selfDuration: durations ? durations.self : void 0,
1047
- treeDuration: durations ? durations.tree : void 0,
1048
- commitId,
1049
- commitPriority,
1050
- source: sourceOf(fiber)
1051
- });
1052
- dispatch(report);
1586
+ } finally {
1587
+ endDiffScope();
1053
1588
  }
1054
1589
  }
1055
1590
 
1056
- // src/tracker.ts
1057
- var MEMO = /* @__PURE__ */ Symbol.for("react.memo");
1058
- var FORWARD_REF = /* @__PURE__ */ Symbol.for("react.forward_ref");
1059
- var isMemo = (t) => typeof t === "object" && t !== null && t.$$typeof === MEMO;
1060
- var isForwardRef = (t) => typeof t === "object" && t !== null && t.$$typeof === FORWARD_REF;
1061
- var isClass = (t) => typeof t === "function" && !!t.prototype?.isReactComponent;
1062
- var isPureClass = (t) => isClass(t) && !!t.prototype.isPureReactComponent;
1063
- var isComponentLike = (t) => typeof t === "function" || isMemo(t) || isForwardRef(t);
1064
- function getDisplayName(type) {
1065
- if (typeof type === "string") return type;
1066
- if (isMemo(type)) return type.displayName ?? getDisplayName(type.type);
1067
- if (isForwardRef(type)) return type.displayName ?? getDisplayName(type.render);
1068
- if (typeof type === "function") {
1069
- const t = type;
1070
- return t.displayName ?? (t.name || "Anonymous");
1591
+ // vendor-rerender-lens.engine.js
1592
+ var parts = globalThis.__RERENDER_LENS_PARTS__ = globalThis.__RERENDER_LENS_PARTS__ || {};
1593
+ parts["fiber"] = fiber_exports;
1594
+ parts["tracker"] = tracker_exports;
1595
+ parts["hookNames"] = hookNames_exports;
1596
+ })();
1597
+
1598
+ ;
1599
+ /* rerender-lens 0.5.0 — rerender-lens.js: devtools bridge + notifiers (window.RerenderLens). Built by scripts/build-vendor.mjs; do not edit. */
1600
+ "use strict";
1601
+ var RerenderLens = (() => {
1602
+ var __create = Object.create;
1603
+ var __defProp = Object.defineProperty;
1604
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
1605
+ var __getOwnPropNames = Object.getOwnPropertyNames;
1606
+ var __getProtoOf = Object.getPrototypeOf;
1607
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
1608
+ var __commonJS = (cb, mod) => function __require() {
1609
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
1610
+ };
1611
+ var __export = (target, all) => {
1612
+ for (var name in all)
1613
+ __defProp(target, name, { get: all[name], enumerable: true });
1614
+ };
1615
+ var __copyProps = (to, from, except, desc) => {
1616
+ if (from && typeof from === "object" || typeof from === "function") {
1617
+ for (let key of __getOwnPropNames(from))
1618
+ if (!__hasOwnProp.call(to, key) && key !== except)
1619
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
1071
1620
  }
1072
- return "Anonymous";
1073
- }
1074
- function hasMarker(type) {
1075
- if (type === null || typeof type !== "function" && typeof type !== "object") return false;
1076
- if (type[MARKER] === true) return true;
1077
- if (isMemo(type)) return hasMarker(type.type);
1078
- if (isForwardRef(type)) return hasMarker(type.render);
1079
- return false;
1080
- }
1081
- function matches(m, name) {
1082
- if (typeof m === "string") return m === name;
1083
- if (m instanceof RegExp) return m.test(name);
1084
- return m(name);
1085
- }
1086
- function shouldTrack(type, o) {
1087
- if (!isComponentLike(type)) return false;
1088
- const name = getDisplayName(type);
1089
- if (o.exclude?.some((m) => matches(m, name))) return false;
1090
- if (hasMarker(type)) return true;
1091
- if (o.include?.some((m) => matches(m, name))) return true;
1092
- if (o.trackAllComponents) return true;
1093
- if (o.trackAllMemoized) return isMemo(type) || isPureClass(type);
1094
- return false;
1095
- }
1096
- function init(options = {}) {
1097
- const s = getState();
1098
- s.options = { ...options };
1099
- s.printed.clear();
1100
- if (!s.enabled) {
1101
- s.detach = attach();
1102
- s.enabled = true;
1621
+ return to;
1622
+ };
1623
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
1624
+ // If the importer is in node compatibility mode or this is not an ESM
1625
+ // file that has been converted to a CommonJS file using a Babel-
1626
+ // compatible transform (i.e. "__esModule" has not been set), then set
1627
+ // "default" to the CommonJS "module.exports" for node compatibility.
1628
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
1629
+ mod
1630
+ ));
1631
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
1632
+
1633
+ // rerender-lens-part:types
1634
+ var require_types = __commonJS({
1635
+ "rerender-lens-part:types"(exports, module) {
1636
+ var p = globalThis.__RERENDER_LENS_PARTS__;
1637
+ if (!p || !p["types"]) throw new Error("rerender-lens: load vendor/rerender-lens.core.js and vendor/rerender-lens.engine.js before vendor/rerender-lens.js");
1638
+ module.exports = p["types"];
1639
+ }
1640
+ });
1641
+
1642
+ // rerender-lens-part:tracker
1643
+ var require_tracker = __commonJS({
1644
+ "rerender-lens-part:tracker"(exports, module) {
1645
+ var p = globalThis.__RERENDER_LENS_PARTS__;
1646
+ if (!p || !p["tracker"]) throw new Error("rerender-lens: load vendor/rerender-lens.core.js and vendor/rerender-lens.engine.js before vendor/rerender-lens.js");
1647
+ module.exports = p["tracker"];
1648
+ }
1649
+ });
1650
+
1651
+ // rerender-lens-part:fiber
1652
+ var require_fiber = __commonJS({
1653
+ "rerender-lens-part:fiber"(exports, module) {
1654
+ var p = globalThis.__RERENDER_LENS_PARTS__;
1655
+ if (!p || !p["fiber"]) throw new Error("rerender-lens: load vendor/rerender-lens.core.js and vendor/rerender-lens.engine.js before vendor/rerender-lens.js");
1656
+ module.exports = p["fiber"];
1657
+ }
1658
+ });
1659
+
1660
+ // rerender-lens-part:diff
1661
+ var require_diff = __commonJS({
1662
+ "rerender-lens-part:diff"(exports, module) {
1663
+ var p = globalThis.__RERENDER_LENS_PARTS__;
1664
+ if (!p || !p["diff"]) throw new Error("rerender-lens: load vendor/rerender-lens.core.js and vendor/rerender-lens.engine.js before vendor/rerender-lens.js");
1665
+ module.exports = p["diff"];
1666
+ }
1667
+ });
1668
+
1669
+ // rerender-lens-part:report
1670
+ var require_report = __commonJS({
1671
+ "rerender-lens-part:report"(exports, module) {
1672
+ var p = globalThis.__RERENDER_LENS_PARTS__;
1673
+ if (!p || !p["report"]) throw new Error("rerender-lens: load vendor/rerender-lens.core.js and vendor/rerender-lens.engine.js before vendor/rerender-lens.js");
1674
+ module.exports = p["report"];
1675
+ }
1676
+ });
1677
+
1678
+ // rerender-lens-part:state
1679
+ var require_state = __commonJS({
1680
+ "rerender-lens-part:state"(exports, module) {
1681
+ var p = globalThis.__RERENDER_LENS_PARTS__;
1682
+ if (!p || !p["state"]) throw new Error("rerender-lens: load vendor/rerender-lens.core.js and vendor/rerender-lens.engine.js before vendor/rerender-lens.js");
1683
+ module.exports = p["state"];
1684
+ }
1685
+ });
1686
+
1687
+ // rerender-lens-part:overlay
1688
+ var require_overlay = __commonJS({
1689
+ "rerender-lens-part:overlay"(exports, module) {
1690
+ var p = globalThis.__RERENDER_LENS_PARTS__;
1691
+ if (!p || !p["overlay"]) throw new Error("rerender-lens: load vendor/rerender-lens.core.js and vendor/rerender-lens.engine.js before vendor/rerender-lens.js");
1692
+ module.exports = p["overlay"];
1693
+ }
1694
+ });
1695
+
1696
+ // rerender-lens-part:version
1697
+ var require_version = __commonJS({
1698
+ "rerender-lens-part:version"(exports, module) {
1699
+ var p = globalThis.__RERENDER_LENS_PARTS__;
1700
+ if (!p || !p["version"]) throw new Error("rerender-lens: load vendor/rerender-lens.core.js and vendor/rerender-lens.engine.js before vendor/rerender-lens.js");
1701
+ module.exports = p["version"];
1702
+ }
1703
+ });
1704
+
1705
+ // src/inject.ts
1706
+ var inject_exports = {};
1707
+ __export(inject_exports, {
1708
+ DEVTOOLS_MARKER: () => DEVTOOLS_MARKER,
1709
+ MARKER: () => import_types.MARKER,
1710
+ PROTOCOL_VERSION: () => PROTOCOL_VERSION,
1711
+ VERSION: () => import_version2.VERSION,
1712
+ buildReport: () => import_report.buildReport,
1713
+ classify: () => import_diff4.classify,
1714
+ combineNotifiers: () => combineNotifiers,
1715
+ configure: () => import_tracker2.configure,
1716
+ createCollector: () => createCollector,
1717
+ createDevtoolsNotifier: () => createDevtoolsNotifier,
1718
+ deepEqual: () => import_diff4.deepEqual,
1719
+ diffRecords: () => import_diff4.diffRecords,
1720
+ disable: () => import_tracker2.disable,
1721
+ ensureDevtoolsHook: () => import_fiber2.ensureDevtoolsHook,
1722
+ getDisplayName: () => import_tracker2.getDisplayName,
1723
+ getRenderers: () => import_fiber2.getRenderers,
1724
+ init: () => import_tracker2.init,
1725
+ isEnabled: () => import_tracker2.isEnabled,
1726
+ isProductionReact: () => import_fiber2.isProductionReact,
1727
+ printReport: () => import_report.printReport,
1728
+ serialize: () => serialize,
1729
+ shouldTrack: () => import_tracker2.shouldTrack,
1730
+ summarize: () => import_report.summarize,
1731
+ track: () => import_tracker2.track
1732
+ });
1733
+ var import_types = __toESM(require_types(), 1);
1734
+ var import_tracker2 = __toESM(require_tracker(), 1);
1735
+ var import_fiber2 = __toESM(require_fiber(), 1);
1736
+ var import_diff4 = __toESM(require_diff(), 1);
1737
+ var import_report = __toESM(require_report(), 1);
1738
+
1739
+ // src/fixes.ts
1740
+ var import_diff2 = __toESM(require_diff(), 1);
1741
+
1742
+ // src/causes.ts
1743
+ var import_diff = __toESM(require_diff(), 1);
1744
+ var isAncestorReport = (anc, r) => anc.path.length < r.path.length && r.path[anc.path.length] === anc.component && anc.path.every((p, i) => r.path[i] === p);
1745
+ var bump = (m, key, n = 1) => void m.set(key, (m.get(key) || 0) + n);
1746
+ function indexByComponent(reports) {
1747
+ const index = /* @__PURE__ */ new Map();
1748
+ for (const r of reports) {
1749
+ const list = index.get(r.component);
1750
+ if (list) list.push(r);
1751
+ else index.set(r.component, [r]);
1752
+ }
1753
+ return index;
1754
+ }
1755
+ function rootCauseOf(r, commitReports, index = indexByComponent(commitReports)) {
1756
+ let cur = r;
1757
+ const seen = /* @__PURE__ */ new Set([r]);
1758
+ while (cur.trigger === "parent" && cur.parent) {
1759
+ const parent = cur.parent;
1760
+ const candidates = index.get(parent.name);
1761
+ const p = candidates && candidates.find((x) => isAncestorReport(x, cur));
1762
+ if (!p || seen.has(p)) return { name: parent.name, trigger: parent.trigger, report: null };
1763
+ seen.add(p);
1764
+ cur = p;
1765
+ }
1766
+ return cur === r ? null : { name: cur.component, trigger: cur.trigger, report: cur };
1767
+ }
1768
+ function rootCausesOf(reports) {
1769
+ const roots = /* @__PURE__ */ new Map();
1770
+ const rootByReport = /* @__PURE__ */ new Map();
1771
+ const index = indexByComponent(reports);
1772
+ let avoidable = 0;
1773
+ let wasted = 0;
1774
+ for (const r of reports) {
1775
+ if (!r.avoidable) continue;
1776
+ avoidable++;
1777
+ if (typeof r.selfDuration === "number") wasted += r.selfDuration;
1778
+ const root = rootCauseOf(r, reports, index);
1779
+ const name = root ? root.name : r.parent && r.parent.name || "(unknown)";
1780
+ const trigger = root ? root.trigger : r.parent && r.parent.trigger || "parent";
1781
+ rootByReport.set(r, name);
1782
+ let agg = roots.get(name);
1783
+ if (!agg) {
1784
+ agg = { name, trigger, count: 0, components: /* @__PURE__ */ new Map() };
1785
+ roots.set(name, agg);
1786
+ }
1787
+ agg.count++;
1788
+ bump(agg.components, r.component);
1103
1789
  }
1104
- return disable;
1105
- }
1106
- function configure(options) {
1107
- const s = getState();
1108
- s.options = { ...s.options, ...options };
1790
+ return { roots: [...roots.values()].sort((a, b) => b.count - a.count), rootByReport, avoidable, wasted };
1109
1791
  }
1110
- function disable() {
1111
- const s = getState();
1112
- if (!s.enabled) return;
1113
- s.detach?.();
1114
- s.detach = null;
1115
- s.enabled = false;
1792
+ function groupByCommit(reports) {
1793
+ const out = /* @__PURE__ */ new Map();
1794
+ for (const r of reports) {
1795
+ if (typeof r.commitId !== "number" || r.commitId <= 0) continue;
1796
+ const list = out.get(r.commitId);
1797
+ if (list) list.push(r);
1798
+ else out.set(r.commitId, [r]);
1799
+ }
1800
+ return out;
1116
1801
  }
1117
- function isEnabled() {
1118
- return getState().enabled;
1802
+ function rankRootCauses(reports) {
1803
+ const byName = /* @__PURE__ */ new Map();
1804
+ for (const commit of groupByCommit(reports).values()) {
1805
+ for (const root of rootCausesOf(commit).roots) {
1806
+ let agg = byName.get(root.name);
1807
+ if (!agg) {
1808
+ agg = { name: root.name, trigger: root.trigger, commits: 0, count: 0, components: /* @__PURE__ */ new Map() };
1809
+ byName.set(root.name, agg);
1810
+ }
1811
+ agg.commits++;
1812
+ agg.count += root.count;
1813
+ for (const [c, n] of root.components) bump(agg.components, c, n);
1814
+ }
1815
+ }
1816
+ return [...byName.values()].sort((a, b) => b.count - a.count || b.commits - a.commits || a.name.localeCompare(b.name));
1119
1817
  }
1120
- function track(component, name) {
1121
- const t = component;
1122
- t[MARKER] = true;
1123
- if (name) t.displayName = name;
1124
- return component;
1818
+ var plural = (n, word) => `${n} ${word}${n === 1 ? "" : "s"}`;
1819
+ function formatRootCauses(reports, limit = 5) {
1820
+ const ranked = rankRootCauses(reports);
1821
+ const lines = ranked.slice(0, limit).map((root, i) => `${String(i + 1).padStart(2)}. <${root.name}> (${root.trigger}) started ${plural(root.commits, "commit")}, ${plural(root.count, "avoidable re-render")} (${[...root.components].map(([c, n]) => `<${c}>${n > 1 ? " x" + n : ""}`).join(", ")})`);
1822
+ if (ranked.length > limit) lines.push(` \u2026 ${ranked.length - limit} more`);
1823
+ return lines.join("\n");
1125
1824
  }
1126
1825
 
1127
1826
  // src/fixes.ts
1128
- var AVOIDABLE = /* @__PURE__ */ new Set(["deep-equal", "function", "element"]);
1827
+ var MAX_FIX_REPORTS = 50;
1129
1828
  var rootOf = (path) => path.split(/[.[]/)[0] || path;
1829
+ var identifier = (name) => /^[A-Za-z_$][\w$]*$/.test(name) ? name : "value";
1830
+ function shortValue(v, max = 60) {
1831
+ let s;
1832
+ try {
1833
+ s = JSON.stringify(v);
1834
+ } catch {
1835
+ s = String(v);
1836
+ }
1837
+ if (s === void 0) s = String(v);
1838
+ return s.length > max ? s.slice(0, max - 3) + "..." : s;
1839
+ }
1130
1840
  function fixesFor(r) {
1131
1841
  const out = [];
1132
- const owner = r.owner || r.parent && r.parent.name || "?";
1133
- const changes = [...r.propChanges, ...r.stateChanges, ...r.hookChanges];
1842
+ const ownerName = r.owner || r.parent && r.parent.name || null;
1843
+ const propChanges = r.propChanges || [];
1844
+ const changes = [...propChanges, ...r.stateChanges || [], ...r.hookChanges || []];
1134
1845
  if (r.avoidable && (changes.length === 0 || r.memoized === false)) {
1846
+ const identical = changes.length === 0;
1135
1847
  out.push({
1136
1848
  kind: "memo",
1137
1849
  owner: r.component,
1138
1850
  target: r.component,
1139
1851
  prop: null,
1140
1852
  label: `Wrap <${r.component}> in React.memo`,
1141
- detail: changes.length === 0 ? `<${r.component}> re-rendered with identical props because <${r.parent && r.parent.name || "its parent"}> re-rendered.` : `<${r.component}> is not memoized: fixing its props alone will not stop the re-render.`
1853
+ detail: identical ? `<${r.component}> re-rendered with identical props because <${r.parent && r.parent.name || "its parent"}> re-rendered.` : `<${r.component}> is not memoized: fixing its props alone will not stop the re-render.`,
1854
+ snippet: `// ${r.component}
1855
+ import { memo } from 'react';
1856
+
1857
+ export const ${r.component} = memo(function ${r.component}(props) {
1858
+ // ...
1859
+ });
1860
+ // class components: extend PureComponent instead`
1142
1861
  });
1143
1862
  }
1144
- for (const c of r.propChanges) {
1145
- if (!AVOIDABLE.has(c.kind)) continue;
1863
+ for (const c of propChanges) {
1864
+ if (!import_diff2.AVOIDABLE_KINDS.has(c.kind)) continue;
1865
+ const owner = ownerName || "?";
1146
1866
  const root = rootOf(c.path);
1867
+ const id = identifier(root);
1147
1868
  if (root === "children" && (c.kind === "element" || c.kind === "deep-equal")) {
1148
- out.push({ kind: "children", owner, target: r.component, prop: "children", label: `memoize children of <${r.component}> in <${owner}>`, detail: `<${owner}> re-creates the children of <${r.component}> on every render.` });
1869
+ out.push({
1870
+ kind: "children",
1871
+ owner,
1872
+ target: r.component,
1873
+ prop: "children",
1874
+ label: `memoize children of <${r.component}> in <${owner}>`,
1875
+ detail: `<${owner}> re-creates the children of <${r.component}> on every render; they have the same types and props each time.`,
1876
+ snippet: `// ${owner}
1877
+ import { useMemo } from 'react';
1878
+
1879
+ const children = useMemo(() => (
1880
+ <>{/* the same elements */}</>
1881
+ ), [/* deps */]);
1882
+
1883
+ <${r.component}>{children}</${r.component}>
1884
+
1885
+ // static children: hoist them to module scope
1886
+ const STATIC = <em>hi</em>;`
1887
+ });
1149
1888
  } else if (c.kind === "function") {
1150
- out.push({ kind: "useCallback", owner, target: r.component, prop: root, label: `useCallback(${root}) in <${owner}>`, detail: `prop "${c.path}" of <${r.component}> is a new function on every render of <${owner}>.` });
1889
+ out.push({
1890
+ kind: "useCallback",
1891
+ owner,
1892
+ target: r.component,
1893
+ prop: root,
1894
+ label: `useCallback(${root}) in <${owner}>`,
1895
+ detail: `prop "${c.path}" of <${r.component}> is a new function on every render of <${owner}>.`,
1896
+ snippet: `// ${owner}
1897
+ import { useCallback } from 'react';
1898
+
1899
+ const ${id} = useCallback((/* args */) => {
1900
+ // ...
1901
+ }, [/* deps */]);
1902
+
1903
+ <${r.component} ${root}={${id}} />`
1904
+ });
1151
1905
  } else if (c.kind === "element") {
1152
- out.push({ kind: "useMemoElement", owner, target: r.component, prop: root, label: `memoize element prop ${root} in <${owner}>`, detail: `prop "${c.path}" of <${r.component}> is a new element with the same type and props on every render of <${owner}>.` });
1906
+ out.push({
1907
+ kind: "useMemoElement",
1908
+ owner,
1909
+ target: r.component,
1910
+ prop: root,
1911
+ label: `memoize element prop ${root} in <${owner}>`,
1912
+ detail: `prop "${c.path}" of <${r.component}> is a new element with the same type and props on every render of <${owner}>.`,
1913
+ snippet: `// ${owner}
1914
+ import { useMemo } from 'react';
1915
+
1916
+ const ${id} = useMemo(() => ${shortValue(c.next, 40)}, [/* deps */]);
1917
+ // or pass it as children from a component that does not re-render`
1918
+ });
1153
1919
  } else {
1154
- out.push({ kind: "useMemo", owner, target: r.component, prop: root, label: `useMemo(${root}) in <${owner}>`, detail: `prop "${c.path}" of <${r.component}> is a new ${Array.isArray(c.next) ? "array" : "object"} with the same contents on every render of <${owner}>.` });
1920
+ const isArray = Array.isArray(c.next);
1921
+ out.push({
1922
+ kind: "useMemo",
1923
+ owner,
1924
+ target: r.component,
1925
+ prop: root,
1926
+ label: `useMemo(${root}) in <${owner}>`,
1927
+ detail: `prop "${c.path}" of <${r.component}> is a new ${isArray ? "array" : "object"} with the same contents on every render of <${owner}>.`,
1928
+ snippet: `// ${owner}
1929
+ import { useMemo } from 'react';
1930
+
1931
+ const ${id} = useMemo(() => (${shortValue(c.next, 80)}), [/* deps */]);
1932
+
1933
+ // or, when it never changes, hoist it to module scope:
1934
+ const ${id.toUpperCase()} = ${shortValue(c.next, 80)};`
1935
+ });
1155
1936
  }
1156
1937
  }
1157
- for (const c of [...r.stateChanges, ...r.hookChanges]) {
1158
- const isContext = c.hook === "useContext";
1938
+ for (const c of [...r.stateChanges || [], ...r.hookChanges || []]) {
1939
+ const isContext = c.hook === "useContext" || /^useContext/.test(c.path);
1159
1940
  const ctxName = isContext ? (/useContext\((.*)\)/.exec(c.path) || [])[1] || "Context" : "";
1160
1941
  const providerOwner = isContext && c.provider && c.provider.component ? c.provider.component : null;
1161
1942
  if (isContext && c.kind === "different" && c.changedKeys && typeof c.totalKeys === "number" && c.changedKeys.length > 0 && c.changedKeys.length < c.totalKeys) {
@@ -1165,20 +1946,63 @@ var RerenderLens = (function (exports) {
1165
1946
  target: r.component,
1166
1947
  prop: ctxName,
1167
1948
  label: `split ${ctxName}${providerOwner ? ` in <${providerOwner}>` : ""}: only ${c.changedKeys.join(", ")} changed`,
1168
- detail: `${c.changedKeys.length} of ${c.totalKeys} keys changed in ${ctxName}, yet every consumer re-rendered.`
1949
+ detail: `${c.changedKeys.map((k) => `"${k}"`).join(", ")} of ${c.totalKeys} keys changed in ${ctxName}, yet every consumer (like <${r.component}>) re-rendered. Consumers that read the other keys re-render for nothing.`,
1950
+ snippet: `// ${providerOwner || "Provider"}
1951
+ // one context per independently-changing slice
1952
+ const ${identifier(ctxName)}Static = createContext(...);
1953
+ const ${identifier(ctxName)}${c.changedKeys.map((k) => k[0].toUpperCase() + k.slice(1)).join("")} = createContext(...);
1954
+
1955
+ // or keep one context and let consumers select a slice:
1956
+ const ${c.changedKeys[0]} = useContextSelector(${ctxName}, (v) => v.${c.changedKeys[0]});`
1169
1957
  });
1170
1958
  continue;
1171
1959
  }
1172
- if (!AVOIDABLE.has(c.kind)) continue;
1960
+ if (!import_diff2.AVOIDABLE_KINDS.has(c.kind)) continue;
1173
1961
  if (isContext) {
1174
- out.push({ kind: "contextValue", owner: providerOwner || `${ctxName}.Provider`, target: r.component, prop: ctxName, label: `memoize the ${ctxName} provider value${providerOwner ? ` in <${providerOwner}>` : ""}`, detail: `${ctxName} produced a new value that is deep-equal to the previous one.` });
1962
+ out.push({
1963
+ kind: "contextValue",
1964
+ owner: providerOwner || `${ctxName}.Provider`,
1965
+ target: r.component,
1966
+ prop: ctxName,
1967
+ label: `memoize the ${ctxName} provider value${providerOwner ? ` in <${providerOwner}>` : ""}`,
1968
+ detail: `<${r.component}> re-rendered because ${ctxName} produced a new value that is deep-equal to the previous one${providerOwner ? ` (Provider rendered by <${providerOwner}>)` : ""}.`,
1969
+ snippet: `// ${providerOwner || `where <${ctxName}.Provider> is rendered`}
1970
+ const value = useMemo(() => ({ /* ... */ }), [/* deps */]);
1971
+ <${ctxName}.Provider value={value}>`
1972
+ });
1175
1973
  } else if (c.hook === "useSyncExternalStore") {
1176
1974
  const chain = c.custom || [];
1177
1975
  const redux = chain.some((n) => /^use(App)?Selector$/.test(n));
1178
1976
  const zustand = !redux && chain.some((n) => /^use[A-Z]\w*Store$/.test(n) || n === "useStore" || n === "useBoundStore");
1179
- out.push({ kind: "storeSnapshot", owner: r.component, target: r.component, prop: c.path, label: redux ? `memoize the selector in <${r.component}>` : zustand ? `useShallow in <${r.component}>` : `stable getSnapshot in <${r.component}>`, detail: storeAdvice(c.custom) });
1977
+ const via = chain.length ? ` via ${chain.join(" \u203A ")}` : "";
1978
+ out.push({
1979
+ kind: "storeSnapshot",
1980
+ owner: r.component,
1981
+ target: r.component,
1982
+ prop: c.path,
1983
+ label: redux ? `memoize the selector in <${r.component}>` : zustand ? `useShallow in <${r.component}>` : `stable getSnapshot in <${r.component}>`,
1984
+ detail: redux ? `the selector${via} returns a new object on every call, so the component re-renders on every store change.` : zustand ? `the store selector${via} returns a new object on every call, so the component re-renders on every store change.` : `${c.path}${via} returned a new reference with the same contents; getSnapshot must return a cached value.`,
1985
+ snippet: redux ? `// ${r.component}
1986
+ import { shallowEqual } from 'react-redux';
1987
+ const slice = useSelector(selectSlice, shallowEqual);
1988
+ // or memoize: const selectSlice = createSelector([selectA, selectB], (a, b) => ({ a, b }));` : zustand ? `// ${r.component}
1989
+ import { useShallow } from 'zustand/react/shallow';
1990
+ const { a, b } = useStore(useShallow((s) => ({ a: s.a, b: s.b })));
1991
+ // or select a primitive: const a = useStore((s) => s.a);` : `// ${r.component}
1992
+ // getSnapshot must return the same reference while the data is unchanged
1993
+ const snapshot = useSyncExternalStore(subscribe, store.getSnapshot /* cached */);`
1994
+ });
1180
1995
  } else {
1181
- out.push({ kind: "bailout", owner: r.component, target: r.component, prop: c.path, label: `bail out before setting ${c.path} in <${r.component}>`, detail: `${c.path} was set to a value deep-equal to the current one (new reference).` });
1996
+ out.push({
1997
+ kind: "bailout",
1998
+ owner: r.component,
1999
+ target: r.component,
2000
+ prop: c.path,
2001
+ label: `bail out before setting ${c.path} in <${r.component}>`,
2002
+ detail: `${c.path} was set to a value deep-equal to the current one (new reference).`,
2003
+ snippet: `// ${r.component}
2004
+ setState((prev) => (deepEqual(prev, next) ? prev : next));`
2005
+ });
1182
2006
  }
1183
2007
  }
1184
2008
  return out;
@@ -1192,11 +2016,12 @@ var RerenderLens = (function (exports) {
1192
2016
  const key = fixKey(f);
1193
2017
  let agg = byKey.get(key);
1194
2018
  if (!agg) {
1195
- agg = { ...f, key, count: 0, components: {} };
2019
+ agg = { ...f, key, count: 0, components: {}, reports: [] };
1196
2020
  byKey.set(key, agg);
1197
2021
  }
1198
2022
  agg.count++;
1199
2023
  agg.components[r.component] = (agg.components[r.component] || 0) + 1;
2024
+ if (agg.reports.length < MAX_FIX_REPORTS) agg.reports.push(r);
1200
2025
  }
1201
2026
  }
1202
2027
  return [...byKey.values()].sort((a, b) => b.count - a.count || a.label.localeCompare(b.label));
@@ -1206,6 +2031,8 @@ var RerenderLens = (function (exports) {
1206
2031
  if (!ranked.length) return "No avoidable re-renders.";
1207
2032
  const lines = ranked.slice(0, limit).map((f, i) => `${String(i + 1).padStart(2)}. ${f.label} (removes ${f.count}: ${Object.entries(f.components).map(([c, n]) => `<${c}>${n > 1 ? " x" + n : ""}`).join(", ")})`);
1208
2033
  if (ranked.length > limit) lines.push(` \u2026 ${ranked.length - limit} more`);
2034
+ const roots = formatRootCauses(reports, 5);
2035
+ if (roots) lines.push("", "Root causes:", roots);
1209
2036
  return lines.join("\n");
1210
2037
  }
1211
2038
 
@@ -1242,25 +2069,25 @@ var RerenderLens = (function (exports) {
1242
2069
 
1243
2070
  // src/budget.ts
1244
2071
  function avoidableCounts(reports) {
1245
- const counts2 = {};
1246
- for (const r of reports) if (r.avoidable) counts2[r.component] = (counts2[r.component] || 0) + 1;
1247
- return counts2;
2072
+ const counts = {};
2073
+ for (const r of reports) if (r.avoidable) counts[r.component] = (counts[r.component] || 0) + 1;
2074
+ return counts;
1248
2075
  }
1249
2076
  function checkBudget(reports, budget) {
1250
2077
  const b = typeof budget === "number" ? { "*": budget } : budget;
1251
2078
  const fallback = typeof b["*"] === "number" ? b["*"] : 0;
1252
- const counts2 = avoidableCounts(reports);
2079
+ const counts = avoidableCounts(reports);
1253
2080
  const violations = [];
1254
2081
  const slack = [];
1255
- const names = /* @__PURE__ */ new Set([...Object.keys(counts2), ...Object.keys(b).filter((k) => k !== "*")]);
2082
+ const names = /* @__PURE__ */ new Set([...Object.keys(counts), ...Object.keys(b).filter((k) => k !== "*")]);
1256
2083
  for (const component of names) {
1257
2084
  const allowed = typeof b[component] === "number" ? b[component] : fallback;
1258
- const avoidable = counts2[component] || 0;
2085
+ const avoidable = counts[component] || 0;
1259
2086
  if (avoidable > allowed) violations.push({ component, avoidable, allowed });
1260
2087
  else if (avoidable < allowed) slack.push({ component, avoidable, allowed });
1261
2088
  }
1262
2089
  violations.sort((x, y) => y.avoidable - y.allowed - (x.avoidable - x.allowed));
1263
- return { ok: violations.length === 0, violations, slack, counts: counts2 };
2090
+ return { ok: violations.length === 0, violations, slack, counts };
1264
2091
  }
1265
2092
  function assertWithinBudget(reports, budget) {
1266
2093
  const result = checkBudget(reports, budget);
@@ -1310,101 +2137,19 @@ ${formatFixes(reports)}`);
1310
2137
  };
1311
2138
  }
1312
2139
 
1313
- // src/overlay.ts
1314
- var ROOT_ID = "rerender-lens-overlay";
1315
- var layer = null;
1316
- function ensureLayer() {
1317
- if (typeof document === "undefined" || !document.body) return null;
1318
- if (layer && layer.root.isConnected) return layer;
1319
- let root = document.getElementById(ROOT_ID);
1320
- if (!root) {
1321
- root = document.createElement("div");
1322
- root.id = ROOT_ID;
1323
- root.setAttribute("aria-hidden", "true");
1324
- Object.assign(root.style, {
1325
- position: "fixed",
1326
- inset: "0",
1327
- pointerEvents: "none",
1328
- zIndex: "2147483647",
1329
- overflow: "visible"
1330
- });
1331
- document.body.appendChild(root);
1332
- }
1333
- layer = { root, sticky: [] };
1334
- return layer;
1335
- }
1336
- function box(rect, color, fill, label) {
1337
- const b = document.createElement("div");
1338
- Object.assign(b.style, {
1339
- position: "absolute",
1340
- left: `${rect.left}px`,
1341
- top: `${rect.top}px`,
1342
- width: `${Math.max(rect.width, 2)}px`,
1343
- height: `${Math.max(rect.height, 2)}px`,
1344
- boxSizing: "border-box",
1345
- border: `2px solid ${color}`,
1346
- background: fill,
1347
- borderRadius: "2px"
1348
- });
1349
- if (label) {
1350
- const tag = document.createElement("span");
1351
- tag.textContent = label;
1352
- Object.assign(tag.style, {
1353
- position: "absolute",
1354
- left: "-2px",
1355
- top: rect.top > 20 ? "-20px" : "100%",
1356
- font: "11px/16px system-ui, sans-serif",
1357
- color: "#fff",
1358
- background: color,
1359
- padding: "1px 5px",
1360
- borderRadius: "2px",
1361
- whiteSpace: "nowrap"
1362
- });
1363
- b.appendChild(tag);
1364
- }
1365
- return b;
1366
- }
1367
- function highlight(target) {
1368
- const l = ensureLayer();
1369
- if (!l) return;
1370
- for (const s of l.sticky) s.remove();
1371
- l.sticky = [];
1372
- if (!target) return;
1373
- for (const node of target.nodes) {
1374
- const rect = node.getBoundingClientRect();
1375
- if (rect.width === 0 && rect.height === 0) continue;
1376
- const b = box(rect, "#1a73e8", "rgba(26, 115, 232, 0.12)", target.label);
1377
- l.root.appendChild(b);
1378
- l.sticky.push(b);
1379
- }
1380
- }
1381
- function clearHighlight() {
1382
- highlight(null);
1383
- }
1384
- function flash(target, duration = 500) {
1385
- const l = ensureLayer();
1386
- if (!l) return;
1387
- for (const node of target.nodes) {
1388
- const rect = node.getBoundingClientRect();
1389
- if (rect.width === 0 && rect.height === 0) continue;
1390
- const b = box(rect, "#d93025", "rgba(217, 48, 37, 0.15)");
1391
- b.style.transition = `opacity ${duration}ms ease-out`;
1392
- l.root.appendChild(b);
1393
- requestAnimationFrame(() => {
1394
- b.style.opacity = "0";
1395
- });
1396
- setTimeout(() => b.remove(), duration + 50);
1397
- }
1398
- }
1399
-
1400
- // src/version.ts
1401
- var VERSION = "0.3.0" ;
1402
-
1403
2140
  // src/devtools.ts
2141
+ var import_diff3 = __toESM(require_diff(), 1);
2142
+ var import_tracker = __toESM(require_tracker(), 1);
2143
+ var import_state = __toESM(require_state(), 1);
2144
+ var import_fiber = __toESM(require_fiber(), 1);
2145
+ var import_overlay = __toESM(require_overlay(), 1);
2146
+ var import_version = __toESM(require_version(), 1);
1404
2147
  var DEVTOOLS_MARKER = "__rerenderLens";
1405
2148
  var PROTOCOL_VERSION = 2;
1406
2149
  var DEFAULT_CHANNEL = "rerender-lens";
1407
- function serialize(value, maxDepth = 6, seen = /* @__PURE__ */ new WeakSet(), depth = 0) {
2150
+ var SERIALIZE_MAX_ENTRIES = 100;
2151
+ var SERIALIZE_MAX_NODES = 2e4;
2152
+ function serialize(value, maxDepth = 4, seen = /* @__PURE__ */ new WeakSet(), depth = 0, budget = { nodes: SERIALIZE_MAX_NODES }) {
1408
2153
  if (value === null || value === void 0) return value;
1409
2154
  const t = typeof value;
1410
2155
  if (t === "string" || t === "boolean") return value;
@@ -1415,24 +2160,56 @@ ${formatFixes(reports)}`);
1415
2160
  const obj = value;
1416
2161
  if (seen.has(obj)) return "[Circular]";
1417
2162
  if (depth >= maxDepth) return "[\u2026]";
2163
+ if (--budget.nodes < 0) return "[\u2026]";
2164
+ const next = (v) => serialize(v, maxDepth, seen, depth + 1, budget);
1418
2165
  seen.add(obj);
1419
2166
  try {
1420
- if (isReactElement(obj)) {
1421
- const out2 = { $type: "element", name: getDisplayName(obj.type) };
2167
+ if ((0, import_diff3.isReactElement)(obj)) {
2168
+ const out2 = { $type: "element", name: (0, import_tracker.getDisplayName)(obj.type) };
1422
2169
  if (obj.key !== null && obj.key !== void 0) out2.key = String(obj.key);
1423
- out2.props = serialize(obj.props, maxDepth, seen, depth + 1);
2170
+ out2.props = next(obj.props);
1424
2171
  return out2;
1425
2172
  }
1426
2173
  if (obj instanceof Date) return { $type: "Date", value: obj.toISOString() };
1427
2174
  if (obj instanceof RegExp) return { $type: "RegExp", value: String(obj) };
2175
+ if (ArrayBuffer.isView(obj) || obj instanceof ArrayBuffer) {
2176
+ const bin = obj;
2177
+ return { $type: bin.constructor?.name || "ArrayBuffer", length: typeof bin.length === "number" ? bin.length : bin.byteLength };
2178
+ }
2179
+ if (obj instanceof Promise) return "[Promise]";
2180
+ if (typeof Node !== "undefined" && obj instanceof Node) return typeof Element !== "undefined" && obj instanceof Element ? `<${obj.tagName.toLowerCase()}>` : `[${obj.nodeName}]`;
2181
+ if (obj === globalThis) return "[Window]";
1428
2182
  if (obj instanceof Map) {
1429
- return { $type: "Map", entries: [...obj].map(([k, v]) => [serialize(k, maxDepth, seen, depth + 1), serialize(v, maxDepth, seen, depth + 1)]) };
2183
+ const entries = [];
2184
+ for (const [k, v] of obj) {
2185
+ if (entries.length >= SERIALIZE_MAX_ENTRIES) {
2186
+ entries.push(["\u2026", `+${obj.size - SERIALIZE_MAX_ENTRIES} more`]);
2187
+ break;
2188
+ }
2189
+ entries.push([next(k), next(v)]);
2190
+ }
2191
+ return { $type: "Map", entries };
2192
+ }
2193
+ if (obj instanceof Set) {
2194
+ const values = [];
2195
+ for (const v of obj) {
2196
+ if (values.length >= SERIALIZE_MAX_ENTRIES) {
2197
+ values.push(`\u2026+${obj.size - SERIALIZE_MAX_ENTRIES} more`);
2198
+ break;
2199
+ }
2200
+ values.push(next(v));
2201
+ }
2202
+ return { $type: "Set", values };
2203
+ }
2204
+ if (Array.isArray(obj)) {
2205
+ const out2 = obj.slice(0, SERIALIZE_MAX_ENTRIES).map(next);
2206
+ if (obj.length > SERIALIZE_MAX_ENTRIES) out2.push(`\u2026+${obj.length - SERIALIZE_MAX_ENTRIES} more`);
2207
+ return out2;
1430
2208
  }
1431
- if (obj instanceof Set) return { $type: "Set", values: [...obj].map((v) => serialize(v, maxDepth, seen, depth + 1)) };
1432
- if (Array.isArray(obj)) return obj.map((v) => serialize(v, maxDepth, seen, depth + 1));
1433
- if (typeof Element !== "undefined" && obj instanceof Element) return `<${obj.tagName.toLowerCase()}>`;
1434
2209
  const out = {};
1435
- for (const k of Object.keys(obj)) out[k] = serialize(obj[k], maxDepth, seen, depth + 1);
2210
+ const keys = Object.keys(obj);
2211
+ for (const k of keys.slice(0, SERIALIZE_MAX_ENTRIES)) out[k] = next(obj[k]);
2212
+ if (keys.length > SERIALIZE_MAX_ENTRIES) out["\u2026"] = `+${keys.length - SERIALIZE_MAX_ENTRIES} more`;
1436
2213
  const proto = Object.getPrototypeOf(obj);
1437
2214
  if (proto && proto !== Object.prototype && proto.constructor?.name) out.$type = proto.constructor.name;
1438
2215
  return out;
@@ -1472,33 +2249,42 @@ ${formatFixes(reports)}`);
1472
2249
  }
1473
2250
  function createDevtoolsNotifier(options = {}) {
1474
2251
  const bufferSize = options.bufferSize ?? 300;
1475
- const maxDepth = options.maxDepth ?? 6;
2252
+ const maxDepth = options.maxDepth ?? 4;
1476
2253
  const target = options.target ?? (typeof window !== "undefined" ? window : void 0);
1477
2254
  const buffer = [];
1478
2255
  let seq = 0;
1479
2256
  let flashOn = options.flashAvoidable ?? false;
2257
+ let live = !target || typeof target.addEventListener !== "function";
2258
+ if (!live) {
2259
+ target.addEventListener("message", (event) => {
2260
+ const data = event.data;
2261
+ if ((event.source === target || !event.source) && data && data.__rerenderLensReady === true) live = true;
2262
+ });
2263
+ }
1480
2264
  const post = (type, payload) => {
1481
- if (!target) return;
2265
+ if (!target || type === "report" && !live) return;
1482
2266
  const msg = { [DEVTOOLS_MARKER]: true, version: PROTOCOL_VERSION, type, payload };
1483
2267
  target.postMessage(msg, "*");
1484
2268
  };
1485
2269
  const source = options.source ?? "page";
1486
2270
  const info = () => ({
1487
2271
  count: buffer.length,
1488
- library: VERSION,
2272
+ library: import_version.VERSION,
1489
2273
  protocol: PROTOCOL_VERSION,
1490
- react: getRenderers(),
1491
- production: isProductionReact(),
1492
- enabled: isEnabled(),
1493
- options: serializeOptions(getState().options),
2274
+ react: (0, import_fiber.getRenderers)(),
2275
+ production: (0, import_fiber.isProductionReact)(),
2276
+ enabled: (0, import_tracker.isEnabled)(),
2277
+ options: serializeOptions((0, import_state.getState)().options),
1494
2278
  source,
1495
2279
  injected: typeof window !== "undefined" && typeof window.__RERENDER_LENS_INJECTED__ === "string",
1496
- scheduled: getState().scheduled,
1497
- commits: getState().commits,
1498
- overhead: { totalMs: getState().overheadMs, maxCommitMs: getState().maxCommitMs }
2280
+ scheduled: (0, import_state.getState)().scheduled,
2281
+ commits: (0, import_state.getState)().commits,
2282
+ overhead: { totalMs: (0, import_state.getState)().overheadMs, maxCommitMs: (0, import_state.getState)().maxCommitMs },
2283
+ truncated: (0, import_state.getState)().truncated
1499
2284
  });
1500
2285
  const bridge = {
1501
2286
  replay: () => {
2287
+ live = true;
1502
2288
  post("hello", info());
1503
2289
  for (const e of buffer) post("report", e.payload);
1504
2290
  },
@@ -1510,7 +2296,7 @@ ${formatFixes(reports)}`);
1510
2296
  return buffer.length;
1511
2297
  },
1512
2298
  version: PROTOCOL_VERSION,
1513
- library: VERSION,
2299
+ library: import_version.VERSION,
1514
2300
  info,
1515
2301
  pull: (since = 0) => {
1516
2302
  const first = buffer[0];
@@ -1519,41 +2305,41 @@ ${formatFixes(reports)}`);
1519
2305
  return { seq, reports, dropped };
1520
2306
  },
1521
2307
  configure: (next) => {
1522
- configure(deserializeOptions(next ?? {}));
1523
- return serializeOptions(getState().options);
2308
+ (0, import_tracker.configure)(deserializeOptions(next ?? {}));
2309
+ return serializeOptions((0, import_state.getState)().options);
1524
2310
  },
1525
- getOptions: () => serializeOptions(getState().options),
2311
+ getOptions: () => serializeOptions((0, import_state.getState)().options),
1526
2312
  highlight: (id) => {
1527
2313
  if (id === null || id === void 0) {
1528
- clearHighlight();
2314
+ (0, import_overlay.clearHighlight)();
1529
2315
  return true;
1530
2316
  }
1531
- const fiber = fiberById(id);
2317
+ const fiber = (0, import_fiber.fiberById)(id);
1532
2318
  if (!fiber) {
1533
- clearHighlight();
2319
+ (0, import_overlay.clearHighlight)();
1534
2320
  return false;
1535
2321
  }
1536
- const nodes = hostNodesOf(fiber);
1537
- highlight({ nodes, label: fiberName(fiber) });
2322
+ const nodes = (0, import_fiber.hostNodesOf)(fiber);
2323
+ (0, import_overlay.highlight)({ nodes, label: (0, import_fiber.fiberName)(fiber) });
1538
2324
  return nodes.length > 0;
1539
2325
  },
1540
2326
  flashAvoidable: (on) => {
1541
2327
  flashOn = !!on;
1542
2328
  },
1543
2329
  inspect: (node) => {
1544
- const comp = nearestComponent(fiberForNode(node));
2330
+ const comp = (0, import_fiber.nearestComponent)((0, import_fiber.fiberForNode)(node));
1545
2331
  if (!comp) return null;
1546
- const id = instanceIdOf(comp) ?? null;
2332
+ const id = (0, import_fiber.instanceIdOf)(comp) ?? null;
1547
2333
  const path = [];
1548
2334
  let f = comp.return;
1549
2335
  while (f) {
1550
- if (f.tag === 0 || f.tag === 1 || f.tag === 11 || f.tag === 14 || f.tag === 15) path.unshift(fiberName(f));
2336
+ if (f.tag === 0 || f.tag === 1 || f.tag === 11 || f.tag === 14 || f.tag === 15) path.unshift((0, import_fiber.fiberName)(f));
1551
2337
  f = f.return;
1552
2338
  }
1553
2339
  return {
1554
- component: fiberName(comp),
2340
+ component: (0, import_fiber.fiberName)(comp),
1555
2341
  instanceId: id,
1556
- tracked: shouldTrack(fiberType(comp), getState().options),
2342
+ tracked: (0, import_tracker.shouldTrack)((0, import_fiber.fiberType)(comp), (0, import_state.getState)().options),
1557
2343
  path,
1558
2344
  reports: id === null ? [] : buffer.filter((e) => e.instanceId === id).slice(-20).map((e) => e.payload)
1559
2345
  };
@@ -1681,37 +2467,14 @@ ${formatFixes(reports)}`);
1681
2467
  if (buffer.length > bufferSize) buffer.splice(0, buffer.length - bufferSize);
1682
2468
  postAll("report", payload);
1683
2469
  if (flashOn && report.avoidable && report.instanceId) {
1684
- const fiber = fiberById(report.instanceId);
1685
- if (fiber) flash({ nodes: hostNodesOf(fiber), label: report.component });
2470
+ const fiber = (0, import_fiber.fiberById)(report.instanceId);
2471
+ if (fiber) (0, import_overlay.flash)({ nodes: (0, import_fiber.hostNodesOf)(fiber), label: report.component });
1686
2472
  }
1687
2473
  };
1688
2474
  }
1689
2475
 
1690
- exports.DEVTOOLS_MARKER = DEVTOOLS_MARKER;
1691
- exports.MARKER = MARKER;
1692
- exports.PROTOCOL_VERSION = PROTOCOL_VERSION;
1693
- exports.VERSION = VERSION;
1694
- exports.buildReport = buildReport;
1695
- exports.classify = classify;
1696
- exports.combineNotifiers = combineNotifiers;
1697
- exports.configure = configure;
1698
- exports.createCollector = createCollector;
1699
- exports.createDevtoolsNotifier = createDevtoolsNotifier;
1700
- exports.deepEqual = deepEqual;
1701
- exports.diffRecords = diffRecords;
1702
- exports.disable = disable;
1703
- exports.ensureDevtoolsHook = ensureDevtoolsHook;
1704
- exports.getDisplayName = getDisplayName;
1705
- exports.getRenderers = getRenderers;
1706
- exports.init = init;
1707
- exports.isEnabled = isEnabled;
1708
- exports.isProductionReact = isProductionReact;
1709
- exports.printReport = printReport;
1710
- exports.serialize = serialize;
1711
- exports.shouldTrack = shouldTrack;
1712
- exports.summarize = summarize;
1713
- exports.track = track;
1714
-
1715
- return exports;
1716
-
1717
- })({});
2476
+ // src/inject.ts
2477
+ var import_version2 = __toESM(require_version(), 1);
2478
+ return __toCommonJS(inject_exports);
2479
+ })();
2480
+ globalThis.RerenderLens = RerenderLens;