react-render-detective 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +299 -0
  2. package/README.md +2 -2
  3. package/dist/babel.cjs +2 -2
  4. package/dist/babel.d.cts +11 -0
  5. package/dist/babel.d.ts +11 -0
  6. package/dist/babel.js +1 -1
  7. package/dist/{chunk-QOGTEVBP.js → chunk-3TZNM7JO.js} +103 -4
  8. package/dist/chunk-3TZNM7JO.js.map +1 -0
  9. package/dist/{chunk-HOTY7J3X.js → chunk-D7M4UC3Y.js} +26 -2
  10. package/dist/chunk-D7M4UC3Y.js.map +1 -0
  11. package/dist/{chunk-PHYYA67T.cjs → chunk-EXDRRUUQ.cjs} +69 -2
  12. package/dist/chunk-EXDRRUUQ.cjs.map +1 -0
  13. package/dist/{chunk-DZ3BZ654.cjs → chunk-LCGXVRJR.cjs} +103 -4
  14. package/dist/chunk-LCGXVRJR.cjs.map +1 -0
  15. package/dist/{chunk-MCIQMYNR.js → chunk-QVHAC7AD.js} +69 -2
  16. package/dist/chunk-QVHAC7AD.js.map +1 -0
  17. package/dist/{chunk-JD4IJ4MN.cjs → chunk-RGT44QQI.cjs} +26 -2
  18. package/dist/chunk-RGT44QQI.cjs.map +1 -0
  19. package/dist/core.cjs +18 -18
  20. package/dist/core.d.cts +27 -3
  21. package/dist/core.d.ts +27 -3
  22. package/dist/core.js +1 -1
  23. package/dist/index.cjs +197 -295
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.d.cts +35 -115
  26. package/dist/index.d.ts +35 -115
  27. package/dist/index.js +166 -258
  28. package/dist/index.js.map +1 -1
  29. package/dist/interactions.cjs +252 -0
  30. package/dist/interactions.cjs.map +1 -0
  31. package/dist/interactions.d.cts +110 -0
  32. package/dist/interactions.d.ts +110 -0
  33. package/dist/interactions.js +241 -0
  34. package/dist/interactions.js.map +1 -0
  35. package/dist/overlay.cjs +5 -5
  36. package/dist/overlay.js +2 -2
  37. package/dist/testing.d.cts +1 -1
  38. package/dist/testing.d.ts +1 -1
  39. package/dist/{types-gl13xwmN.d.cts → types-CRET8EhB.d.cts} +20 -2
  40. package/dist/{types-gl13xwmN.d.ts → types-CRET8EhB.d.ts} +20 -2
  41. package/dist/vite.cjs +2 -2
  42. package/dist/vite.js +1 -1
  43. package/package.json +14 -4
  44. package/dist/chunk-DZ3BZ654.cjs.map +0 -1
  45. package/dist/chunk-HOTY7J3X.js.map +0 -1
  46. package/dist/chunk-JD4IJ4MN.cjs.map +0 -1
  47. package/dist/chunk-MCIQMYNR.js.map +0 -1
  48. package/dist/chunk-PHYYA67T.cjs.map +0 -1
  49. package/dist/chunk-QOGTEVBP.js.map +0 -1
package/dist/index.d.ts CHANGED
@@ -1,96 +1,8 @@
1
- import { b as RenderEvent, j as Confidence, R as RenderReason, a as DetectiveOptions, D as DetectiveConfig, A as AppStats, d as ComponentStats } from './types-gl13xwmN.js';
2
- export { i as ComponentInfo, C as ContextChange, f as Diagnosis, I as Inspected, g as InspectionLimits, M as Mode, P as PropChange, k as PropChangeKind, h as PropValueType, c as RenderPhase, l as RenderTimings, e as Thresholds, T as TrackedStateChange } from './types-gl13xwmN.js';
1
+ import { j as Confidence, b as RenderEvent, R as RenderReason, a as DetectiveOptions, D as DetectiveConfig, A as AppStats, d as ComponentStats } from './types-CRET8EhB.js';
2
+ export { i as ComponentInfo, C as ContextChange, f as Diagnosis, I as Inspected, g as InspectionLimits, M as Mode, P as PropChange, k as PropChangeKind, h as PropValueType, c as RenderPhase, l as RenderTimings, S as SelectorChange, e as Thresholds, T as TrackedStateChange } from './types-CRET8EhB.js';
3
3
  import { RenderProfile } from './testing.js';
4
4
  import { ComponentType, ReactNode, Dispatch, SetStateAction } from 'react';
5
5
 
6
- /**
7
- * Interaction-scoped attribution.
8
- *
9
- * Performance is felt per interaction, not in aggregate — and INP is the metric
10
- * teams are actually judged on. This joins the two halves: the browser says a
11
- * keystroke took 240ms, and the render events say which components spent it and
12
- * why.
13
- *
14
- * Uses `PerformanceObserver` with `event` timing, a public browser API. Where it
15
- * is unsupported (older Safari, jsdom) interaction tracking simply stays empty
16
- * rather than guessing.
17
- */
18
- interface InteractionRecord {
19
- id: string;
20
- /** `click`, `keydown`, `pointerup`… */
21
- type: string;
22
- /** Best-effort description of what was interacted with. */
23
- target?: string;
24
- startTime: number;
25
- /** Browser-reported event duration — the number INP is computed from. */
26
- durationMs: number;
27
- /**
28
- * For a manually measured interaction: how long the synchronous action took.
29
- * Immune to throttling, unlike the full window, which waits on a frame.
30
- */
31
- handlerMs?: number;
32
- /** Renders committed inside this interaction's window. */
33
- renders: RenderEvent[];
34
- /** Sum of self durations for those renders. */
35
- renderTimeMs: number;
36
- /** Render time that no observable input change explains. */
37
- avoidableRenderTimeMs: number;
38
- }
39
- interface InteractionSummary {
40
- interaction: InteractionRecord;
41
- /** Components ordered by cost within this interaction. */
42
- contributors: Array<{
43
- component: string;
44
- source?: string;
45
- renders: number;
46
- totalMs: number;
47
- cause: string;
48
- }>;
49
- headline: string;
50
- nextStep: string;
51
- confidence: Confidence;
52
- }
53
- interface RawEventTiming {
54
- name: string;
55
- startTime: number;
56
- duration: number;
57
- target?: string;
58
- handlerMs?: number;
59
- }
60
- declare class InteractionTracker {
61
- private capacity;
62
- private records;
63
- private observer;
64
- private nextId;
65
- constructor(capacity?: number);
66
- /** Returns false when the browser cannot report event timing. */
67
- start(): boolean;
68
- stop(): void;
69
- /** Is the automatic path available in this browser? */
70
- get automatic(): boolean;
71
- /**
72
- * Time an interaction by hand.
73
- *
74
- * The automatic path depends on the Event Timing API, which Safari only
75
- * gained in 16.4 and which does not fire for synthetic input at all — so
76
- * anything driven by a test harness records nothing. This measures a specific
77
- * action instead, up to the paint that follows it, and needs no browser
78
- * support beyond `performance.now`.
79
- */
80
- measure<T>(label: string, action: () => T): T;
81
- /** Exposed for tests and for `measure`. */
82
- record(timing: RawEventTiming): InteractionRecord;
83
- clear(): void;
84
- /**
85
- * Joins render events to interactions by commit time. A render belongs to an
86
- * interaction when it committed between the event starting and shortly after
87
- * it finished — React commits just after the event handler returns.
88
- */
89
- attribute(events: RenderEvent[]): InteractionRecord[];
90
- }
91
- declare function summarise(record: InteractionRecord): InteractionSummary;
92
- declare function formatInteraction(summary: InteractionSummary): string;
93
-
94
6
  /**
95
7
  * "Where should I spend my next hour?"
96
8
  *
@@ -171,6 +83,31 @@ interface RenderDetectiveProps {
171
83
  */
172
84
  declare function RenderDetective({ name, children }: RenderDetectiveProps): ReactNode;
173
85
 
86
+ interface TrackSelectorOptions {
87
+ /** Readable label — a property path where one could be derived, else the call site. */
88
+ name?: string;
89
+ /** `File.tsx:12:3` of the call, supplied by the build plugin. */
90
+ source?: string;
91
+ }
92
+ /**
93
+ * Records a store value and returns it untouched.
94
+ *
95
+ * Pass-through by construction: the value is returned as given, and nothing
96
+ * here can change what the component receives.
97
+ */
98
+ declare function trackSelector<T>(value: T, options?: TrackSelectorOptions): T;
99
+ /**
100
+ * Wraps a store hook so its results are attributed. Signature-preserving: extra
101
+ * arguments (react-redux's equality function, for instance) pass straight
102
+ * through, because changing them would change the app's behaviour.
103
+ *
104
+ * const useSelector = createTrackedSelectorHook(useReduxSelector);
105
+ *
106
+ * The build plugin rewrites `useSelector` imports for you; use this directly for
107
+ * a custom store hook, or where the plugin cannot run.
108
+ */
109
+ declare function createTrackedSelectorHook<A extends unknown[], R>(hook: (...args: A) => R, options?: TrackSelectorOptions): (...args: A) => R;
110
+
174
111
  /**
175
112
  * In-component diagnostics without wrapping.
176
113
  *
@@ -231,6 +168,13 @@ interface Explanation {
231
168
  share: number;
232
169
  valueType: string;
233
170
  }>;
171
+ /** Store selectors that returned a new reference with identical contents. */
172
+ unstableSelectors: Array<{
173
+ name: string;
174
+ source?: string;
175
+ count: number;
176
+ share: number;
177
+ }>;
234
178
  averageSelfDuration: number;
235
179
  totalSelfDuration: number;
236
180
  potentiallyAvoidableRenders: number;
@@ -280,25 +224,6 @@ declare function explain(componentName: string): string | undefined;
280
224
  declare function explainStructured(componentName: string): Explanation | undefined;
281
225
  /** Snapshot render behaviour for regression testing. See `react-render-detective/testing`. */
282
226
  declare function getRenderProfile(scenario: string): RenderProfile;
283
- /**
284
- * Interactions, slowest first, with the renders that happened inside each.
285
- *
286
- * This is the bridge from render causality to what a user actually feels: the
287
- * browser reports how long the interaction took, and the render events say
288
- * which components spent that time and why.
289
- */
290
- declare function getInteractions(): InteractionRecord[];
291
- /** Structured analysis of one interaction. Defaults to the slowest recorded. */
292
- declare function explainInteractionStructured(id?: string): InteractionSummary | undefined;
293
- declare function explainInteraction(id?: string): string | undefined;
294
- declare function printInteractions(limit?: number): void;
295
- /**
296
- * Time one interaction explicitly, up to the paint that follows it.
297
- *
298
- * Needed wherever the automatic path cannot see: Safari before 16.4, and any
299
- * synthetic input, which never produces Event Timing entries.
300
- */
301
- declare function measureInteraction<T>(label: string, action: () => T): T;
302
227
  /**
303
228
  * Components ranked by estimated recoverable time — the triage view. Render
304
229
  * counts answer the wrong question; this answers "what should I fix first?".
@@ -323,13 +248,8 @@ declare const ReactRenderDetective: {
323
248
  explainStructured: typeof explainStructured;
324
249
  getOpportunities: typeof getOpportunities;
325
250
  printOpportunities: typeof printOpportunities;
326
- getInteractions: typeof getInteractions;
327
- explainInteraction: typeof explainInteraction;
328
- explainInteractionStructured: typeof explainInteractionStructured;
329
- printInteractions: typeof printInteractions;
330
- measureInteraction: typeof measureInteraction;
331
251
  getRenderProfile: typeof getRenderProfile;
332
252
  printStats: typeof printStats;
333
253
  };
334
254
 
335
- export { AppStats, ComponentStats, Confidence, DetectiveConfig, DetectiveOptions, type Explanation, type InteractionRecord, type InteractionSummary, InteractionTracker, type Opportunity, ReactRenderDetective, RenderDetective, type RenderDetectiveProps, RenderEvent, RenderReason, type TrackOptions, clear, configure, explain, explainEvents, explainInteraction, explainInteractionStructured, explainStructured, formatExplanation, formatInteraction, formatOpportunities, getComponentStats, getConfig, getEvents, getInteractions, getOpportunities, getRenderProfile, getStats, init, isEnabled, measureInteraction, printInteractions, printOpportunities, printStats, rankOpportunities, reset, subscribe, summarise as summariseInteraction, useRenderDiagnostics, useTrackedContextValue, useTrackedEffect, useTrackedState, withRenderDetective };
255
+ export { AppStats, ComponentStats, Confidence, DetectiveConfig, DetectiveOptions, type Explanation, type Opportunity, ReactRenderDetective, RenderDetective, type RenderDetectiveProps, RenderEvent, RenderReason, type TrackOptions, type TrackSelectorOptions, clear, configure, createTrackedSelectorHook, explain, explainEvents, explainStructured, formatExplanation, formatOpportunities, getComponentStats, getConfig, getEvents, getOpportunities, getRenderProfile, getStats, init, isEnabled, printOpportunities, printStats, rankOpportunities, reset, subscribe, trackSelector, useRenderDiagnostics, useTrackedContextValue, useTrackedEffect, useTrackedState, withRenderDetective };