@flotrace/runtime-core 2.2.3 → 2.3.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.
- package/README.md +94 -0
- package/dist/chunk-QLOJU5F2.mjs +181 -0
- package/dist/index.d.mts +380 -22
- package/dist/index.d.ts +380 -22
- package/dist/index.js +681 -106
- package/dist/index.mjs +557 -106
- package/dist/jsx-dev-runtime.d.mts +64 -0
- package/dist/jsx-dev-runtime.d.ts +64 -0
- package/dist/jsx-dev-runtime.js +179 -0
- package/dist/jsx-dev-runtime.mjs +46 -0
- package/dist/jsx-runtime.d.mts +1 -0
- package/dist/jsx-runtime.d.ts +1 -0
- package/dist/jsx-runtime.js +34 -0
- package/dist/jsx-runtime.mjs +7 -0
- package/package.json +12 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,8 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utilities for the @flotrace/runtime-core JSX runtime entries.
|
|
3
|
+
*
|
|
4
|
+
* The JSX runtime (jsx-dev-runtime.ts) writes the FLOTRACE_SOURCE symbol onto
|
|
5
|
+
* a JSX element's props at creation time. The fiber walker reads the same
|
|
6
|
+
* symbol back from `fiber.memoizedProps` during tree walking. Centralising
|
|
7
|
+
* the symbol identity + helpers here means there's exactly one place to look
|
|
8
|
+
* for "what shape is fiber.memoizedProps[FLOTRACE_SOURCE]".
|
|
9
|
+
*
|
|
10
|
+
* Per PRD-JSX-RUNTIME.md §8 + IMPLEMENTATION-PLAN-JSX-RUNTIME.md Phase 1.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Global symbol so the same identity is reachable from any module — including
|
|
14
|
+
* dynamically-loaded chunks and multiple bundled copies of runtime-core that
|
|
15
|
+
* may end up linked into the same app (workspace + npm registry mix). The
|
|
16
|
+
* `Symbol.for()` registry guarantees one global slot.
|
|
17
|
+
*/
|
|
18
|
+
declare const FLOTRACE_SOURCE: unique symbol;
|
|
19
|
+
/**
|
|
20
|
+
* Adoption sentinel — the dev runtime sets this on first jsxDEV call so the
|
|
21
|
+
* walker (and `runtime:ready` event) can detect that the user opted in via
|
|
22
|
+
* `"jsxImportSource": "@flotrace/runtime-core"`. No per-call telemetry; this
|
|
23
|
+
* is a one-time boolean.
|
|
24
|
+
*/
|
|
25
|
+
declare const JSX_RUNTIME_ACTIVE_KEY: unique symbol;
|
|
26
|
+
/**
|
|
27
|
+
* Source attribution attached to a React element's props at JSX-creation time.
|
|
28
|
+
* Stored under the `FLOTRACE_SOURCE` symbol key so it doesn't appear in
|
|
29
|
+
* `Object.keys(props)` or React's unknown-DOM-prop warnings.
|
|
30
|
+
*/
|
|
31
|
+
interface FlotraceJsxSource {
|
|
32
|
+
/** Normalized file path (bundler prefixes stripped). */
|
|
33
|
+
fileName: string;
|
|
34
|
+
/** 1-indexed line number. */
|
|
35
|
+
lineNumber: number;
|
|
36
|
+
/** 1-indexed column number. */
|
|
37
|
+
columnNumber: number;
|
|
38
|
+
/** FNV-1a 32-bit hash of `${fileName}:${lineNumber}:${columnNumber}`, 8 hex chars. */
|
|
39
|
+
callSiteId: string;
|
|
40
|
+
/** Map of prop key → inline-literal kind, only present when literals detected. */
|
|
41
|
+
inline?: Record<string, 'fn' | 'obj' | 'arr'>;
|
|
42
|
+
}
|
|
43
|
+
/** Subset of the compiler-supplied `source` argument passed to jsxDEV. */
|
|
44
|
+
interface JsxSourceArg {
|
|
45
|
+
fileName: string;
|
|
46
|
+
lineNumber: number;
|
|
47
|
+
columnNumber: number;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Normalize a bundler-specific file path to a canonical form. Different
|
|
51
|
+
* bundlers emit different path styles for the SAME line of source code:
|
|
52
|
+
*
|
|
53
|
+
* Vite dev → `file:///abs/src/Foo.tsx`
|
|
54
|
+
* Webpack dev → `webpack-internal:///./src/Foo.tsx`
|
|
55
|
+
* esbuild → `./src/Foo.tsx`
|
|
56
|
+
* Next.js Turbopack → `[project]/src/Foo.tsx` (left as-is — no known prefix)
|
|
57
|
+
* Windows → `C:\Users\foo\src\Foo.tsx`
|
|
58
|
+
*
|
|
59
|
+
* Without normalization, the same line of code produces a different
|
|
60
|
+
* `callSiteId` after a bundler swap (or even after switching between Next.js
|
|
61
|
+
* Webpack and Turbopack), breaking per-callsite metrics + HMR-stable watches.
|
|
62
|
+
*
|
|
63
|
+
* Normalization rules (order matters):
|
|
64
|
+
* 1. Strip `file://` prefix.
|
|
65
|
+
* 2. Strip `webpack-internal:///./` prefix.
|
|
66
|
+
* 3. Trim leading `./`.
|
|
67
|
+
* 4. Lowercase Windows drive letters (`C:\` → `c:\`).
|
|
68
|
+
*/
|
|
69
|
+
declare function normalizeJsxSourcePath(fileName: string): string;
|
|
70
|
+
/**
|
|
71
|
+
* FNV-1a 32-bit hash → 8-char hex string. Fast, stable, and sufficient for
|
|
72
|
+
* a non-cryptographic per-callsite identity. Operates on the NORMALIZED path
|
|
73
|
+
* so the same source line produces the same hash regardless of bundler.
|
|
74
|
+
*
|
|
75
|
+
* Collision probability across a 5000-callsite app via the birthday paradox
|
|
76
|
+
* on a 32-bit hash space: 1 − e^(−5000²/(2 × 2³²)) ≈ 0.3%. Acceptable for a
|
|
77
|
+
* UI key — a one-in-300 chance of two callsites sharing a row in the Hot
|
|
78
|
+
* Call Sites table is preferable to the wire-format weight of a longer hash.
|
|
79
|
+
* Not suitable as a security token.
|
|
80
|
+
*/
|
|
81
|
+
declare function computeCallSiteId(source: JsxSourceArg): string;
|
|
82
|
+
/**
|
|
83
|
+
* Record a render timestamp for a call site. Caller may inject `now` for
|
|
84
|
+
* deterministic tests; production callers use the default `performance.now()`.
|
|
85
|
+
*/
|
|
86
|
+
declare function recordCallSiteRender(callSiteId: string, now?: number): void;
|
|
87
|
+
/** Read-only snapshot of recorded timestamps for a call site. */
|
|
88
|
+
declare function getCallSiteRenders(callSiteId: string): readonly number[];
|
|
89
|
+
/**
|
|
90
|
+
* Renders-per-second over the last `windowMs` ms. Walks backwards through the
|
|
91
|
+
* ring buffer; stops at the first entry older than the cutoff (entries are
|
|
92
|
+
* monotonically non-decreasing because the runtime only ever appends).
|
|
93
|
+
*
|
|
94
|
+
* Caller may inject `now` for deterministic tests.
|
|
95
|
+
*/
|
|
96
|
+
declare function getCallSiteRenderRate(callSiteId: string, windowMs?: number, now?: number): number;
|
|
97
|
+
/**
|
|
98
|
+
* Clear all ring-buffer state. Called by the walker uninstall path so HMR
|
|
99
|
+
* rapid-reconnect cycles don't accumulate stale entries across sessions.
|
|
100
|
+
*/
|
|
101
|
+
declare function clearCallSiteRenders(): void;
|
|
102
|
+
/**
|
|
103
|
+
* Compute a `runtime:callSiteMetrics` payload from the ring buffer — one
|
|
104
|
+
* entry per callsite that has rendered within the last `windowMs` ms (5s
|
|
105
|
+
* default). Callsites with zero recent activity are omitted so the wire-
|
|
106
|
+
* format payload stays small on idle apps.
|
|
107
|
+
*
|
|
108
|
+
* Returns `null` when no callsite has activity → caller skips the WebSocket
|
|
109
|
+
* send entirely. This is load-bearing: a 5000-callsite app with no live
|
|
110
|
+
* renders should NOT emit a 5000-entry map of zeros every second.
|
|
111
|
+
*
|
|
112
|
+
* The `now` arg lets tests inject deterministic timestamps.
|
|
113
|
+
*/
|
|
114
|
+
declare function computeCallSiteMetricsPayload(windowMs?: number, now?: number): Record<string, number> | null;
|
|
115
|
+
/**
|
|
116
|
+
* Payload emitted whenever a JSX call site fires twice in the same commit
|
|
117
|
+
* with the same React `key`. Shape mirrors `RuntimeDuplicateKeyMessage` minus
|
|
118
|
+
* the wire-format envelope (`type` + `timestamp`) — those are added by the
|
|
119
|
+
* caller (typically `FloTraceProvider`) at WS-send time.
|
|
120
|
+
*/
|
|
121
|
+
interface DuplicateKeyEvent {
|
|
122
|
+
callSiteId: string;
|
|
123
|
+
fileName: string;
|
|
124
|
+
lineNumber: number;
|
|
125
|
+
columnNumber: number;
|
|
126
|
+
duplicateKey: string;
|
|
127
|
+
occurrences: number;
|
|
128
|
+
}
|
|
129
|
+
declare function setDuplicateKeyEmitter(emitter: ((evt: DuplicateKeyEvent) => void) | null): void;
|
|
130
|
+
/**
|
|
131
|
+
* One-time boolean stored on `globalThis` under `JSX_RUNTIME_ACTIVE_KEY`. The
|
|
132
|
+
* dev jsx-runtime sets it on first `jsxDEV` call; the walker reads it to
|
|
133
|
+
* include `jsxRuntimeActive: true` on `runtime:ready` for adoption telemetry.
|
|
134
|
+
*
|
|
135
|
+
* No per-call telemetry — this is a single boolean, set once.
|
|
136
|
+
*/
|
|
137
|
+
declare function markJsxRuntimeActive(): void;
|
|
138
|
+
declare function isJsxRuntimeActive(): boolean;
|
|
139
|
+
/**
|
|
140
|
+
* Detect props that look like fresh-each-render literals — the #1 React perf
|
|
141
|
+
* footgun (an inline `onClick={() => ...}` invalidates `memo` + breaks
|
|
142
|
+
* `useEffect` dep arrays). This signal can ONLY be observed at JSX-creation
|
|
143
|
+
* time; after React commits, the prop on the fiber looks identical whether it
|
|
144
|
+
* was a literal or `useCallback`/`useMemo`'d ref.
|
|
145
|
+
*
|
|
146
|
+
* Heuristics (conservative — false-negatives are acceptable; false-positives
|
|
147
|
+
* train users to ignore the warning, so we err toward silence):
|
|
148
|
+
*
|
|
149
|
+
* - **Functions**: only flagged when the fn has no `.name`. `useCallback`'d
|
|
150
|
+
* fns preserve the inner fn's name, named methods are hoisted — the
|
|
151
|
+
* anonymous-arrow case is what catches `{() => doX()}` and `{e => h(e)}`.
|
|
152
|
+
* - **Arrays**: only non-empty arrays. `[]` literals are usually intentional
|
|
153
|
+
* empty defaults and not a perf concern in their own right.
|
|
154
|
+
* - **Plain objects**: detected via prototype check (`Object.prototype` or
|
|
155
|
+
* `null` prototype). React elements (`$$typeof`), class instances, dates,
|
|
156
|
+
* maps, sets — all skipped.
|
|
157
|
+
*
|
|
158
|
+
* Returns `undefined` when nothing is flagged, so callers can use a single
|
|
159
|
+
* truthy check before serialising.
|
|
160
|
+
*/
|
|
161
|
+
declare function detectInlineLiterals(props: Record<string, unknown>): Record<string, 'fn' | 'obj' | 'arr'> | undefined;
|
|
162
|
+
|
|
1
163
|
/**
|
|
2
164
|
* Types for @flotrace/runtime package
|
|
3
165
|
* These mirror the shared types from the extension but are standalone
|
|
4
166
|
* to avoid importing from the extension package.
|
|
5
167
|
*/
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Confidence tier for `LiveTreeNode` source attribution. Drives the
|
|
171
|
+
* OriginBadge variant in the renderer:
|
|
172
|
+
*
|
|
173
|
+
* - `'exact'` — JSX-runtime symbol present OR `_debugSource.fileName`
|
|
174
|
+
* populated (Babel JSX plugin / React 17–18 dev).
|
|
175
|
+
* - `'inferred'` — path resolved via the owner-chain walk or
|
|
176
|
+
* `_debugStack.stack` first-non-react frame (R19+).
|
|
177
|
+
* - `'package'` — fiber classified as framework/library; user clicks won't
|
|
178
|
+
* land in user code anyway.
|
|
179
|
+
* - `'unknown'` — no signal at any tier. Renders an amber `?` pill so the
|
|
180
|
+
* UI is honest about what it doesn't know.
|
|
181
|
+
*/
|
|
182
|
+
type SourceConfidence = 'exact' | 'inferred' | 'package' | 'unknown';
|
|
6
183
|
/**
|
|
7
184
|
* Serialized value for safe transmission over WebSocket
|
|
8
185
|
*/
|
|
@@ -26,7 +203,7 @@ type SerializedValue = null | boolean | number | string | SerializedValue[] | {
|
|
|
26
203
|
/**
|
|
27
204
|
* Messages sent from runtime to extension
|
|
28
205
|
*/
|
|
29
|
-
type RuntimeMessage = RuntimeReadyMessage | RuntimeRenderMessage | RuntimePropsUpdateMessage | RuntimeNodePropsMessage | RuntimeZustandUpdateMessage | RuntimeReduxUpdateMessage | RuntimeRouterUpdateMessage | RuntimeContextUpdateMessage | RuntimeDisconnectMessage | RuntimeTreeSnapshotMessage | RuntimeTreeDiffMessage | RuntimeNodeHooksMessage | RuntimeNodeEffectsMessage | RuntimeDetailedRenderReasonMessage | RuntimeTimelineEventMessage | RuntimeTanStackQueryUpdateMessage | RuntimeRenderTriggerMessage | RuntimeRenderCascadeMessage | RuntimePropDrillingMessage | RuntimeActionStateMessage | RuntimeOptimisticDiffMessage | RuntimeNextjsContextMessage | RuntimeRscPayloadMessage | RuntimeHydrationEventMessage | RuntimeNetworkRequestMessage | RuntimeLocalStateCorrelationMessage | RuntimeValueTraceMessage | RuntimePongMessage;
|
|
206
|
+
type RuntimeMessage = RuntimeReadyMessage | RuntimeRenderMessage | RuntimePropsUpdateMessage | RuntimeNodePropsMessage | RuntimeZustandUpdateMessage | RuntimeReduxUpdateMessage | RuntimeRouterUpdateMessage | RuntimeContextUpdateMessage | RuntimeDisconnectMessage | RuntimeTreeSnapshotMessage | RuntimeTreeDiffMessage | RuntimeNodeHooksMessage | RuntimeNodeEffectsMessage | RuntimeDetailedRenderReasonMessage | RuntimeTimelineEventMessage | RuntimeTanStackQueryUpdateMessage | RuntimeRenderTriggerMessage | RuntimeRenderCascadeMessage | RuntimePropDrillingMessage | RuntimeActionStateMessage | RuntimeOptimisticDiffMessage | RuntimeNextjsContextMessage | RuntimeRscPayloadMessage | RuntimeHydrationEventMessage | RuntimeCallSiteMetricsMessage | RuntimeDuplicateKeyMessage | RuntimeNetworkRequestMessage | RuntimeLocalStateCorrelationMessage | RuntimeValueTraceMessage | RuntimePongMessage;
|
|
30
207
|
interface RuntimeReadyMessage {
|
|
31
208
|
type: 'runtime:ready';
|
|
32
209
|
appName?: string;
|
|
@@ -55,6 +232,14 @@ interface RuntimeReadyMessage {
|
|
|
55
232
|
* release script keeps runtime-core pinned identically, so a separate
|
|
56
233
|
* core-version field would be redundant. */
|
|
57
234
|
runtimeVersion?: string;
|
|
235
|
+
/**
|
|
236
|
+
* Milestone 8 Phase 5 — adoption signal for the JSX-runtime opt-in
|
|
237
|
+
* (`"jsxImportSource": "@flotrace/runtime-core"`). `true` when the dev
|
|
238
|
+
* runtime's global sentinel has been set (i.e. at least one user
|
|
239
|
+
* component went through `jsxDEV`). Desktop forwards to telemetry so
|
|
240
|
+
* the admin dashboard can track rollout rate. No PII; one-time boolean.
|
|
241
|
+
*/
|
|
242
|
+
jsxRuntimeActive?: boolean;
|
|
58
243
|
}
|
|
59
244
|
interface RuntimeRenderMessage {
|
|
60
245
|
type: 'runtime:render';
|
|
@@ -226,6 +411,22 @@ interface LiveTreeNode {
|
|
|
226
411
|
isLibrary?: boolean;
|
|
227
412
|
/** Short display label for the library source (e.g. 'framer', 'fontawesome', 'sonner') */
|
|
228
413
|
libraryName?: string;
|
|
414
|
+
/**
|
|
415
|
+
* Source attribution captured at JSX-creation time by the optional
|
|
416
|
+
* `@flotrace/runtime-core/jsx-dev-runtime` opt-in. Present only when the
|
|
417
|
+
* user has set `"jsxImportSource": "@flotrace/runtime-core"` in their
|
|
418
|
+
* tsconfig.json — the highest-confidence source signal available.
|
|
419
|
+
*
|
|
420
|
+
* When present, `filePath` / `lineNumber` are filled from `jsxSource` so
|
|
421
|
+
* existing consumers (click-to-IDE, breadcrumb path display) keep working
|
|
422
|
+
* without changes.
|
|
423
|
+
*/
|
|
424
|
+
jsxSource?: FlotraceJsxSource;
|
|
425
|
+
/**
|
|
426
|
+
* Confidence tier of the resolved source. See `SourceConfidence` doc-comment
|
|
427
|
+
* for the four tiers and what each means for the UI.
|
|
428
|
+
*/
|
|
429
|
+
sourceConfidence?: SourceConfidence;
|
|
229
430
|
}
|
|
230
431
|
/**
|
|
231
432
|
* Enhanced render reason with specific prop/state/context changes.
|
|
@@ -460,6 +661,8 @@ interface CascadeNode {
|
|
|
460
661
|
children: CascadeNode[];
|
|
461
662
|
depth: number;
|
|
462
663
|
isMemoized: boolean;
|
|
664
|
+
/** JSX-runtime attribution (Milestone 8 Phase 6) — mirror of shared LiveTreeNode field. */
|
|
665
|
+
jsxSource?: FlotraceJsxSource;
|
|
463
666
|
}
|
|
464
667
|
type LanePriority = 'sync' | 'discrete' | 'continuous' | 'default' | 'transition' | 'deferred' | 'idle' | 'offscreen';
|
|
465
668
|
interface LaneInfo {
|
|
@@ -494,6 +697,8 @@ interface PropDrillingChainNode {
|
|
|
494
697
|
role: 'source' | 'passthrough' | 'consumer';
|
|
495
698
|
hookCount: number;
|
|
496
699
|
hasContextHook: boolean;
|
|
700
|
+
/** JSX-runtime attribution (Milestone 8 Phase 6). */
|
|
701
|
+
jsxSource?: FlotraceJsxSource;
|
|
497
702
|
}
|
|
498
703
|
interface PropDrillingChain {
|
|
499
704
|
chainId: string;
|
|
@@ -521,21 +726,27 @@ interface RuntimePropDrillingMessage {
|
|
|
521
726
|
treeSize: number;
|
|
522
727
|
};
|
|
523
728
|
}
|
|
729
|
+
/**
|
|
730
|
+
* State of a single useActionState / useOptimistic hook instance on a fiber.
|
|
731
|
+
* Mirror of `ActionStateEntry` in flotrace-desktop's `shared/liveMessages.ts`
|
|
732
|
+
* — keep the field set in sync.
|
|
733
|
+
*/
|
|
734
|
+
interface ActionStateEntry {
|
|
735
|
+
hookIndex: number;
|
|
736
|
+
hookKind: 'action' | 'optimistic';
|
|
737
|
+
isPending: boolean;
|
|
738
|
+
state: SerializedValue;
|
|
739
|
+
error?: SerializedValue;
|
|
740
|
+
pendingSince?: number;
|
|
741
|
+
durationMs?: number;
|
|
742
|
+
}
|
|
524
743
|
/** Sent whenever a useActionState or useOptimistic hook changes on any fiber */
|
|
525
744
|
interface RuntimeActionStateMessage {
|
|
526
745
|
type: 'runtime:actionState';
|
|
527
746
|
nodeId: string;
|
|
528
747
|
componentName: string;
|
|
529
748
|
/** One entry per useActionState / useOptimistic hook on this fiber */
|
|
530
|
-
actions:
|
|
531
|
-
hookIndex: number;
|
|
532
|
-
hookKind: 'action' | 'optimistic';
|
|
533
|
-
isPending: boolean;
|
|
534
|
-
state: SerializedValue;
|
|
535
|
-
error?: SerializedValue;
|
|
536
|
-
pendingSince?: number;
|
|
537
|
-
durationMs?: number;
|
|
538
|
-
}>;
|
|
749
|
+
actions: ActionStateEntry[];
|
|
539
750
|
timestamp: number;
|
|
540
751
|
}
|
|
541
752
|
/** Sent when a useOptimistic value diverges from its underlying actual value */
|
|
@@ -557,12 +768,17 @@ interface RuntimeNextjsContextMessage {
|
|
|
557
768
|
initialRoute?: string;
|
|
558
769
|
timestamp: number;
|
|
559
770
|
}
|
|
771
|
+
/**
|
|
772
|
+
* RSC / Next.js cache header status. Mirror of the union in
|
|
773
|
+
* `shared/liveMessages.ts`'s `RscPayloadEntry.cacheStatus` — keep in sync.
|
|
774
|
+
*/
|
|
775
|
+
type RscCacheStatus = 'HIT' | 'MISS' | 'STALE' | 'unknown';
|
|
560
776
|
/** Sent when an RSC / Next.js data fetch is intercepted (metadata only, no values) */
|
|
561
777
|
interface RuntimeRscPayloadMessage {
|
|
562
778
|
type: 'runtime:rscPayload';
|
|
563
779
|
route: string;
|
|
564
780
|
payloadSizeBytes: number;
|
|
565
|
-
cacheStatus:
|
|
781
|
+
cacheStatus: RscCacheStatus;
|
|
566
782
|
timestamp: number;
|
|
567
783
|
}
|
|
568
784
|
/** Sent when React hydration completes or a mismatch is detected */
|
|
@@ -573,6 +789,48 @@ interface RuntimeHydrationEventMessage {
|
|
|
573
789
|
errorMessage?: string;
|
|
574
790
|
timestamp: number;
|
|
575
791
|
}
|
|
792
|
+
/**
|
|
793
|
+
* Per-callsite render frequency snapshot. Emitted at most once per second by
|
|
794
|
+
* the runtime when the JSX-runtime opt-in is active — the ring buffer in
|
|
795
|
+
* `jsxRuntimeUtils.ts` is the source of truth, this message is a periodic
|
|
796
|
+
* compaction of the buffer into "renders/sec over last 5s" so the desktop
|
|
797
|
+
* doesn't need to mirror the buffer.
|
|
798
|
+
*
|
|
799
|
+
* Independent of the React Profiler — works in concurrent rendering where the
|
|
800
|
+
* Profiler may miss commits. Only emitted when there's at least one callsite
|
|
801
|
+
* with non-zero recent activity (no metric-flood on idle apps).
|
|
802
|
+
*/
|
|
803
|
+
interface RuntimeCallSiteMetricsMessage {
|
|
804
|
+
type: 'runtime:callSiteMetrics';
|
|
805
|
+
/** Map of callSiteId → renders/sec over the last 5-second window. */
|
|
806
|
+
metrics: Record<string, number>;
|
|
807
|
+
timestamp: number;
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* Duplicate-key warning. Emitted by the JSX runtime when it observes the same
|
|
811
|
+
* `(callSiteId, key)` pair on two or more JSX calls within a single commit —
|
|
812
|
+
* the classic `{items.map(item => <Row key={item.id} />)}` pattern where
|
|
813
|
+
* `item.id` repeats. React logs a console warning for this; we surface it
|
|
814
|
+
* with full file:line attribution so the user can navigate directly to the
|
|
815
|
+
* map call site.
|
|
816
|
+
*
|
|
817
|
+
* One message per (callSiteId, duplicateKey) per emission window — the
|
|
818
|
+
* runtime de-duplicates so a list with 100 duplicate rows doesn't spam 100
|
|
819
|
+
* messages.
|
|
820
|
+
*/
|
|
821
|
+
interface RuntimeDuplicateKeyMessage {
|
|
822
|
+
type: 'runtime:duplicateKey';
|
|
823
|
+
/** The JSX call site that produced the duplicates. */
|
|
824
|
+
callSiteId: string;
|
|
825
|
+
fileName: string;
|
|
826
|
+
lineNumber: number;
|
|
827
|
+
columnNumber: number;
|
|
828
|
+
/** The key value that appeared more than once. */
|
|
829
|
+
duplicateKey: string;
|
|
830
|
+
/** How many times the same key fired at this call site in the commit. */
|
|
831
|
+
occurrences: number;
|
|
832
|
+
timestamp: number;
|
|
833
|
+
}
|
|
576
834
|
/** Metadata for a single intercepted network request. Privacy-first: no bodies, no query params, no auth headers. */
|
|
577
835
|
interface NetworkRequestEntry {
|
|
578
836
|
/** Incrementing request ID */
|
|
@@ -646,6 +904,13 @@ type TraceStep = {
|
|
|
646
904
|
/** If this step came via a rename edge in the drilling graph. */
|
|
647
905
|
renamedFrom?: string;
|
|
648
906
|
confidence: TraceConfidence;
|
|
907
|
+
/**
|
|
908
|
+
* JSX-runtime attribution of the PARENT fiber that wrote the value into
|
|
909
|
+
* this prop (Milestone 8 Phase 6). Captured by `readJsxSourceFromFiber`
|
|
910
|
+
* on the ancestor at trace-resolution time. Undefined when the parent
|
|
911
|
+
* fiber lacks attribution.
|
|
912
|
+
*/
|
|
913
|
+
callSiteOfParentJsx?: FlotraceJsxSource;
|
|
649
914
|
} | {
|
|
650
915
|
kind: 'hook-state';
|
|
651
916
|
nodeId: string;
|
|
@@ -708,15 +973,12 @@ interface ValueTrace {
|
|
|
708
973
|
steps: TraceStep[];
|
|
709
974
|
/** Wall-clock time the resolver completed. */
|
|
710
975
|
resolvedAtMs: number;
|
|
711
|
-
/** True when the 50ms budget tripped and the chain is partial. */
|
|
712
|
-
truncated?: boolean;
|
|
713
976
|
/**
|
|
714
977
|
* Optional error hint for friendly empty states.
|
|
715
978
|
* - `value-not-found`: target path doesn't exist on the current fiber.
|
|
716
979
|
* - `no-fiber`: nodeId no longer present in fiberRefMap (component unmounted).
|
|
717
|
-
* - `budget-exceeded`: bailed before finding origin.
|
|
718
980
|
*/
|
|
719
|
-
error?: 'value-not-found' | 'no-fiber'
|
|
981
|
+
error?: 'value-not-found' | 'no-fiber';
|
|
720
982
|
}
|
|
721
983
|
interface RuntimeValueTraceMessage {
|
|
722
984
|
type: 'runtime:valueTrace';
|
|
@@ -898,7 +1160,8 @@ declare const DEFAULT_CONFIG: ResolvedFloTraceConfig;
|
|
|
898
1160
|
* 2. Store match — scan Zustand / Redux / TanStack Query live snapshots
|
|
899
1161
|
* 3. API match — feed matched reference into fetchOriginRegistry WeakMap
|
|
900
1162
|
*
|
|
901
|
-
* On-demand only — never runs per-render.
|
|
1163
|
+
* On-demand only — never runs per-render. Bounded by MAX_PROP_CHAIN_DEPTH and
|
|
1164
|
+
* SCAN_DEPTH structural limits; no wall-clock budget so every trace runs to origin.
|
|
902
1165
|
* Uses reference identity (`===`) before fingerprinting to keep common cases fast.
|
|
903
1166
|
*
|
|
904
1167
|
* See docs/PRD-VALUE-LINEAGE.md §6 and docs/IMPLEMENTATION-PLAN-VALUE-LINEAGE.md Phase 2.
|
|
@@ -1183,7 +1446,7 @@ declare function uninstallFiberTreeWalker(): void;
|
|
|
1183
1446
|
*
|
|
1184
1447
|
* Works across React 18 and 19 by probing different internal property names.
|
|
1185
1448
|
*/
|
|
1186
|
-
interface FiberLike {
|
|
1449
|
+
interface FiberLike$1 {
|
|
1187
1450
|
type: {
|
|
1188
1451
|
name?: string;
|
|
1189
1452
|
displayName?: string;
|
|
@@ -1196,7 +1459,7 @@ interface FiberLike {
|
|
|
1196
1459
|
displayName?: string;
|
|
1197
1460
|
};
|
|
1198
1461
|
} | ((...args: unknown[]) => unknown) | string | null;
|
|
1199
|
-
return: FiberLike | null;
|
|
1462
|
+
return: FiberLike$1 | null;
|
|
1200
1463
|
tag: number;
|
|
1201
1464
|
}
|
|
1202
1465
|
/**
|
|
@@ -1206,16 +1469,16 @@ interface FiberLike {
|
|
|
1206
1469
|
* Strategy 2 (React 19): __CLIENT_INTERNALS...owner field (renamed + flattened)
|
|
1207
1470
|
* Both return the fiber currently being rendered (null between renders).
|
|
1208
1471
|
*/
|
|
1209
|
-
declare function getCurrentRenderingFiber(): FiberLike | null;
|
|
1472
|
+
declare function getCurrentRenderingFiber(): FiberLike$1 | null;
|
|
1210
1473
|
/**
|
|
1211
1474
|
* Extract display name from a fiber node.
|
|
1212
1475
|
*/
|
|
1213
|
-
declare function getComponentNameFromFiber(fiber: FiberLike): string | null;
|
|
1476
|
+
declare function getComponentNameFromFiber(fiber: FiberLike$1): string | null;
|
|
1214
1477
|
/**
|
|
1215
1478
|
* Walk up the fiber tree to build an ancestor chain of component names.
|
|
1216
1479
|
* Stops after 10 levels to prevent excessive traversal.
|
|
1217
1480
|
*/
|
|
1218
|
-
declare function buildAncestorChain(fiber: FiberLike): string[];
|
|
1481
|
+
declare function buildAncestorChain(fiber: FiberLike$1): string[];
|
|
1219
1482
|
|
|
1220
1483
|
/**
|
|
1221
1484
|
* Hook Inspector for @flotrace/runtime
|
|
@@ -1716,4 +1979,99 @@ declare function installRscPayloadInterceptor(client: FloTraceWebSocketClient):
|
|
|
1716
1979
|
/** Remove the RSC payload interceptor and restore original fetch */
|
|
1717
1980
|
declare function uninstallRscPayloadInterceptor(): void;
|
|
1718
1981
|
|
|
1719
|
-
|
|
1982
|
+
/**
|
|
1983
|
+
* Fiber debug recorder — buffers fiber + LiveTreeNode observations into
|
|
1984
|
+
* capped in-memory tables, then dumps them to the console *on demand*.
|
|
1985
|
+
*
|
|
1986
|
+
* Why not log eagerly? `getComponentName` runs once per fiber per commit.
|
|
1987
|
+
* A 200-node tree at 60Hz produces ~12k console entries/sec, which OOMs
|
|
1988
|
+
* DevTools. The recorder keeps recording silent until you ask for a dump.
|
|
1989
|
+
*
|
|
1990
|
+
* Usage (from any console — Next.js app, FloTrace renderer, Electron main):
|
|
1991
|
+
* globalThis.__FT_DEBUG = true // start recording
|
|
1992
|
+
* __ft.dump() // print digest (fibers + snapshots)
|
|
1993
|
+
* __ft.fibers() // just the fiber-name table
|
|
1994
|
+
* __ft.snapshots() // just the snapshot timeline
|
|
1995
|
+
* __ft.tail(20) // recent activity
|
|
1996
|
+
* __ft.clear() // reset buffers
|
|
1997
|
+
* __ft.export() // returns a JSON object
|
|
1998
|
+
* __ft.download() // browser-only: save buffers as JSON
|
|
1999
|
+
* __ft.size() // current buffer sizes
|
|
2000
|
+
*
|
|
2001
|
+
* All recorders are no-ops when __FT_DEBUG is falsy, so the wired call
|
|
2002
|
+
* sites cost ~1 boolean check each.
|
|
2003
|
+
*/
|
|
2004
|
+
|
|
2005
|
+
interface FiberRecord {
|
|
2006
|
+
/** Resolved component name (what FloTrace ultimately shows on the node). */
|
|
2007
|
+
name: string;
|
|
2008
|
+
/** Raw `fiber.type.name` — present even for ForwardRef/Memo's inner render. */
|
|
2009
|
+
rawName: string | undefined;
|
|
2010
|
+
/** Raw `fiber.type.displayName` — what the author explicitly set. */
|
|
2011
|
+
rawDisplayName: string | undefined;
|
|
2012
|
+
kind: 'function' | 'class' | 'forwardRef' | 'memo' | 'host' | 'unknown';
|
|
2013
|
+
/** Fiber tag number (0=Function, 1=Class, 11=ForwardRef, 14=Memo, etc.). */
|
|
2014
|
+
fiberTag: number | undefined;
|
|
2015
|
+
looksMinified: boolean;
|
|
2016
|
+
count: number;
|
|
2017
|
+
contexts: Set<string>;
|
|
2018
|
+
firstSeen: number;
|
|
2019
|
+
lastSeen: number;
|
|
2020
|
+
/** First captured _debugSource (if any) — only available in dev builds. */
|
|
2021
|
+
source?: {
|
|
2022
|
+
fileName?: string;
|
|
2023
|
+
lineNumber?: number;
|
|
2024
|
+
};
|
|
2025
|
+
/** First non-null react key observed (useful for diagnosing list rows). */
|
|
2026
|
+
exampleKey?: string;
|
|
2027
|
+
}
|
|
2028
|
+
interface TreeRecord {
|
|
2029
|
+
ts: number;
|
|
2030
|
+
ctx: string;
|
|
2031
|
+
rootName: string;
|
|
2032
|
+
totalNodes: number;
|
|
2033
|
+
maxDepth: number;
|
|
2034
|
+
minifiedLike: number;
|
|
2035
|
+
topNames: string;
|
|
2036
|
+
}
|
|
2037
|
+
declare global {
|
|
2038
|
+
var __FT_DEBUG: boolean | undefined;
|
|
2039
|
+
var __ft: FtConsoleApi | undefined;
|
|
2040
|
+
}
|
|
2041
|
+
declare function setFiberDebug(enabled: boolean): void;
|
|
2042
|
+
type FiberLike = {
|
|
2043
|
+
tag?: number;
|
|
2044
|
+
type?: unknown;
|
|
2045
|
+
key?: string | null;
|
|
2046
|
+
_debugSource?: {
|
|
2047
|
+
fileName?: string;
|
|
2048
|
+
lineNumber?: number;
|
|
2049
|
+
} | null;
|
|
2050
|
+
};
|
|
2051
|
+
declare function describeFiberType(fiber: FiberLike): {
|
|
2052
|
+
kind: FiberRecord['kind'];
|
|
2053
|
+
name: string | undefined;
|
|
2054
|
+
displayName: string | undefined;
|
|
2055
|
+
resolved: string;
|
|
2056
|
+
looksMinified: boolean;
|
|
2057
|
+
};
|
|
2058
|
+
declare function logTreeSnapshot(tree: LiveTreeNode | null, context?: string): void;
|
|
2059
|
+
declare const logTreeSummary: typeof logTreeSnapshot;
|
|
2060
|
+
interface FtConsoleApi {
|
|
2061
|
+
dump(): void;
|
|
2062
|
+
fibers(): void;
|
|
2063
|
+
snapshots(): void;
|
|
2064
|
+
tail(n?: number): void;
|
|
2065
|
+
clear(): void;
|
|
2066
|
+
size(): {
|
|
2067
|
+
fibers: number;
|
|
2068
|
+
snapshots: number;
|
|
2069
|
+
};
|
|
2070
|
+
export(): {
|
|
2071
|
+
fibers: Array<Record<string, unknown>>;
|
|
2072
|
+
snapshots: TreeRecord[];
|
|
2073
|
+
};
|
|
2074
|
+
download(filename?: string): void;
|
|
2075
|
+
}
|
|
2076
|
+
|
|
2077
|
+
export { type ActionStateEntry, DEFAULT_CONFIG, type DetailedRenderReason, type DetailedRenderReasonType, type DuplicateKeyEvent, type EffectInfo, FLOTRACE_SOURCE, type Fiber$1 as Fiber, type FiberEffect, type FiberHookState, type FiberTreeWalkerOptions, type FloTraceConfig, FloTraceWebSocketClient, type FlotraceJsxSource, type FrameworkInfo, type HookInfo, type HookType, JSX_RUNTIME_ACTIVE_KEY, type LiveTreeNode, type MutationCorrelation, type NetworkRequestEntry, type PropChange, type ReduxStoreApi, type ResolvedFloTraceConfig, type RscCacheStatus, type RuntimeActionStateMessage, type RuntimeCallSiteMetricsMessage, type RuntimeDuplicateKeyMessage, type RuntimeHydrationEventMessage, type RuntimeNextjsContextMessage, type RuntimeOptimisticDiffMessage, type RuntimeRscPayloadMessage, type RuntimeTreeDiffMessage, type RuntimeValueTraceMessage, type SerializedValue, type SourceConfidence, type TanStackMutationInfo, type TanStackQueryClientApi, type TanStackQueryEvent, type TanStackQueryInfo, type TimelineEvent, type TimelineEventType, type TraceConfidence, type TraceStep, type TrackingOptions, type ValueTrace, type ValueTraceInput, type ZustandStoreApi, buildAncestorChain, clearCallSiteRenders, clearFetchOriginTags, computeCallSiteId, computeCallSiteMetricsPayload, describeFiberType, detectInlineLiterals, detectServerComponent, detectWebFramework, disposeWebSocketClient, findFetchOrigin, getCallSiteRenderRate, getCallSiteRenders, getChangedKeys, getComponentNameFromFiber, getCurrentRenderingFiber, getDetailedRenderReason, getFiberRefMap, getNodeEffects, getNodeHooks, getNodeProps, getReduxSnapshot, getTanstackSnapshot, getTimeline, getWebSocketClient, getZustandSnapshot, hasActiveTags, inspectEffects, inspectHooks, installFiberTreeWalker, installReduxTracker, installRscPayloadInterceptor, installTanStackQueryTracker, installTimelineTracker, installZustandTracker, isJsxRuntimeActive, isReduxStore, isTanStackQueryClient, logTreeSnapshot, logTreeSummary, markJsxRuntimeActive, maybeEmitNextjsContext, normalizeJsxSourcePath, recordCallSiteRender, recordTimelineEvent, requestFullSnapshot, requestTreeSnapshot, resetNextjsDetection, resolveValueTrace, serializeProps, serializeValue, setDuplicateKeyEmitter, setFiberDebug, tagFetchData, uninstallFiberTreeWalker, uninstallReduxTracker, uninstallRscPayloadInterceptor, uninstallTanStackQueryTracker, uninstallTimelineTracker, uninstallZustandTracker };
|