@flotrace/runtime-core 2.2.4 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1,8 +1,243 @@
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
+ * Top-priority "is this fiber a user-defined component?" check.
84
+ *
85
+ * Three routes, checked in order of precision (fastest + most reliable
86
+ * first). Returns `true` as soon as ANY route produces positive evidence
87
+ * that the fiber's source code lives outside `node_modules`.
88
+ *
89
+ * Route A — `fiber.type[FLOTRACE_SRC_ATTR]` (babel plugin):
90
+ * The `@flotrace/runtime-core/babel-plugin` declaration-tagging pass
91
+ * writes a JSON `{f,l,c}` payload onto every PascalCase function /
92
+ * class / variable in user code. Works for: React Native (Babel),
93
+ * Vite + React (Babel), CRA, Webpack + Babel, Next.js Pages Router
94
+ * (if Babel is opted in). Doesn't work for: Next.js with SWC.
95
+ *
96
+ * Route B — `fiber._debugSource.fileName` (React 18 + Babel JSX source):
97
+ * The `@babel/plugin-transform-react-jsx-source` plugin (auto-included
98
+ * by RN's preset and CRA/Vite's React preset) injects `__source` on
99
+ * every JSX element, which React 18 captures onto `fiber._debugSource`.
100
+ * Empty under React 19+ (the field was deprecated upstream).
101
+ *
102
+ * Route C — `fiber._debugStack.stack` (React 19+ web — Next.js SWC, Vite):
103
+ * React 19 replaced `_debugSource` with an Error captured at JSX
104
+ * creation time. We parse the first non-React stack frame for a path.
105
+ * This is what makes Next.js (SWC, no babel config possible without
106
+ * losing SWC) work — the React 19 reconciler attaches stack-frame
107
+ * info regardless of the compiler used.
108
+ *
109
+ * For ALL routes the "is user code" criterion is the same: the resolved
110
+ * file path must not include `node_modules`. That string check is enough
111
+ * because every modern bundler resolves third-party deps through a path
112
+ * containing `/node_modules/` — there's no realistic false-negative.
113
+ *
114
+ * Returns `false` for:
115
+ * - Host components (string `fiber.type` like `'View'` / `'div'`)
116
+ * - Library / framework components (paths in `node_modules`, plus
117
+ * bundle-URL frames which are skipped by `parseFirstNonReactFrame`)
118
+ * - Fibers with no source signal at all (degrade to existing heuristics)
119
+ *
120
+ * Used by `fiberTreeWalker.ts` as a top-priority short-circuit before
121
+ * name-list / regex / path heuristics fire. A fiber that returns `true`
122
+ * here is authoritatively user code — every framework/library
123
+ * classification downstream should bypass.
124
+ *
125
+ * Generic over the fiber-like shape so cascadeAnalyzer / propDrillingAnalyzer
126
+ * / valueTraceResolver can all share one definition without importing the
127
+ * walker's `Fiber` type.
128
+ */
129
+ declare function isUserComponent(fiber: {
130
+ type?: unknown;
131
+ memoizedProps?: Record<string, unknown> | null;
132
+ _debugSource?: {
133
+ fileName: string;
134
+ lineNumber?: number;
135
+ } | null;
136
+ _debugStack?: {
137
+ stack?: string;
138
+ } | null;
139
+ }): boolean;
140
+ /**
141
+ * Record a render timestamp for a call site. Caller may inject `now` for
142
+ * deterministic tests; production callers use the default `performance.now()`.
143
+ */
144
+ declare function recordCallSiteRender(callSiteId: string, now?: number): void;
145
+ /** Read-only snapshot of recorded timestamps for a call site. */
146
+ declare function getCallSiteRenders(callSiteId: string): readonly number[];
147
+ /**
148
+ * Renders-per-second over the last `windowMs` ms. Walks backwards through the
149
+ * ring buffer; stops at the first entry older than the cutoff (entries are
150
+ * monotonically non-decreasing because the runtime only ever appends).
151
+ *
152
+ * Caller may inject `now` for deterministic tests.
153
+ */
154
+ declare function getCallSiteRenderRate(callSiteId: string, windowMs?: number, now?: number): number;
155
+ /**
156
+ * Clear all ring-buffer state. Called by the walker uninstall path so HMR
157
+ * rapid-reconnect cycles don't accumulate stale entries across sessions.
158
+ */
159
+ declare function clearCallSiteRenders(): void;
160
+ /**
161
+ * Compute a `runtime:callSiteMetrics` payload from the ring buffer — one
162
+ * entry per callsite that has rendered within the last `windowMs` ms (5s
163
+ * default). Callsites with zero recent activity are omitted so the wire-
164
+ * format payload stays small on idle apps.
165
+ *
166
+ * Returns `null` when no callsite has activity → caller skips the WebSocket
167
+ * send entirely. This is load-bearing: a 5000-callsite app with no live
168
+ * renders should NOT emit a 5000-entry map of zeros every second.
169
+ *
170
+ * The `now` arg lets tests inject deterministic timestamps.
171
+ */
172
+ declare function computeCallSiteMetricsPayload(windowMs?: number, now?: number): Record<string, number> | null;
173
+ /**
174
+ * Payload emitted whenever a JSX call site fires twice in the same commit
175
+ * with the same React `key`. Shape mirrors `RuntimeDuplicateKeyMessage` minus
176
+ * the wire-format envelope (`type` + `timestamp`) — those are added by the
177
+ * caller (typically `FloTraceProvider`) at WS-send time.
178
+ */
179
+ interface DuplicateKeyEvent {
180
+ callSiteId: string;
181
+ fileName: string;
182
+ lineNumber: number;
183
+ columnNumber: number;
184
+ duplicateKey: string;
185
+ occurrences: number;
186
+ }
187
+ declare function setDuplicateKeyEmitter(emitter: ((evt: DuplicateKeyEvent) => void) | null): void;
188
+ /**
189
+ * One-time boolean stored on `globalThis` under `JSX_RUNTIME_ACTIVE_KEY`. The
190
+ * dev jsx-runtime sets it on first `jsxDEV` call; the walker reads it to
191
+ * include `jsxRuntimeActive: true` on `runtime:ready` for adoption telemetry.
192
+ *
193
+ * No per-call telemetry — this is a single boolean, set once.
194
+ */
195
+ declare function markJsxRuntimeActive(): void;
196
+ declare function isJsxRuntimeActive(): boolean;
197
+ /**
198
+ * Detect props that look like fresh-each-render literals — the #1 React perf
199
+ * footgun (an inline `onClick={() => ...}` invalidates `memo` + breaks
200
+ * `useEffect` dep arrays). This signal can ONLY be observed at JSX-creation
201
+ * time; after React commits, the prop on the fiber looks identical whether it
202
+ * was a literal or `useCallback`/`useMemo`'d ref.
203
+ *
204
+ * Heuristics (conservative — false-negatives are acceptable; false-positives
205
+ * train users to ignore the warning, so we err toward silence):
206
+ *
207
+ * - **Functions**: only flagged when the fn has no `.name`. `useCallback`'d
208
+ * fns preserve the inner fn's name, named methods are hoisted — the
209
+ * anonymous-arrow case is what catches `{() => doX()}` and `{e => h(e)}`.
210
+ * - **Arrays**: only non-empty arrays. `[]` literals are usually intentional
211
+ * empty defaults and not a perf concern in their own right.
212
+ * - **Plain objects**: detected via prototype check (`Object.prototype` or
213
+ * `null` prototype). React elements (`$$typeof`), class instances, dates,
214
+ * maps, sets — all skipped.
215
+ *
216
+ * Returns `undefined` when nothing is flagged, so callers can use a single
217
+ * truthy check before serialising.
218
+ */
219
+ declare function detectInlineLiterals(props: Record<string, unknown>): Record<string, 'fn' | 'obj' | 'arr'> | undefined;
220
+
1
221
  /**
2
222
  * Types for @flotrace/runtime package
3
223
  * These mirror the shared types from the extension but are standalone
4
224
  * to avoid importing from the extension package.
5
225
  */
226
+
227
+ /**
228
+ * Confidence tier for `LiveTreeNode` source attribution. Drives the
229
+ * OriginBadge variant in the renderer:
230
+ *
231
+ * - `'exact'` — JSX-runtime symbol present OR `_debugSource.fileName`
232
+ * populated (Babel JSX plugin / React 17–18 dev).
233
+ * - `'inferred'` — path resolved via the owner-chain walk or
234
+ * `_debugStack.stack` first-non-react frame (R19+).
235
+ * - `'package'` — fiber classified as framework/library; user clicks won't
236
+ * land in user code anyway.
237
+ * - `'unknown'` — no signal at any tier. Renders an amber `?` pill so the
238
+ * UI is honest about what it doesn't know.
239
+ */
240
+ type SourceConfidence = 'exact' | 'inferred' | 'package' | 'unknown';
6
241
  /**
7
242
  * Serialized value for safe transmission over WebSocket
8
243
  */
@@ -26,7 +261,7 @@ type SerializedValue = null | boolean | number | string | SerializedValue[] | {
26
261
  /**
27
262
  * Messages sent from runtime to extension
28
263
  */
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;
264
+ 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
265
  interface RuntimeReadyMessage {
31
266
  type: 'runtime:ready';
32
267
  appName?: string;
@@ -55,6 +290,14 @@ interface RuntimeReadyMessage {
55
290
  * release script keeps runtime-core pinned identically, so a separate
56
291
  * core-version field would be redundant. */
57
292
  runtimeVersion?: string;
293
+ /**
294
+ * Milestone 8 Phase 5 — adoption signal for the JSX-runtime opt-in
295
+ * (`"jsxImportSource": "@flotrace/runtime-core"`). `true` when the dev
296
+ * runtime's global sentinel has been set (i.e. at least one user
297
+ * component went through `jsxDEV`). Desktop forwards to telemetry so
298
+ * the admin dashboard can track rollout rate. No PII; one-time boolean.
299
+ */
300
+ jsxRuntimeActive?: boolean;
58
301
  }
59
302
  interface RuntimeRenderMessage {
60
303
  type: 'runtime:render';
@@ -226,6 +469,22 @@ interface LiveTreeNode {
226
469
  isLibrary?: boolean;
227
470
  /** Short display label for the library source (e.g. 'framer', 'fontawesome', 'sonner') */
228
471
  libraryName?: string;
472
+ /**
473
+ * Source attribution captured at JSX-creation time by the optional
474
+ * `@flotrace/runtime-core/jsx-dev-runtime` opt-in. Present only when the
475
+ * user has set `"jsxImportSource": "@flotrace/runtime-core"` in their
476
+ * tsconfig.json — the highest-confidence source signal available.
477
+ *
478
+ * When present, `filePath` / `lineNumber` are filled from `jsxSource` so
479
+ * existing consumers (click-to-IDE, breadcrumb path display) keep working
480
+ * without changes.
481
+ */
482
+ jsxSource?: FlotraceJsxSource;
483
+ /**
484
+ * Confidence tier of the resolved source. See `SourceConfidence` doc-comment
485
+ * for the four tiers and what each means for the UI.
486
+ */
487
+ sourceConfidence?: SourceConfidence;
229
488
  }
230
489
  /**
231
490
  * Enhanced render reason with specific prop/state/context changes.
@@ -460,6 +719,8 @@ interface CascadeNode {
460
719
  children: CascadeNode[];
461
720
  depth: number;
462
721
  isMemoized: boolean;
722
+ /** JSX-runtime attribution (Milestone 8 Phase 6) — mirror of shared LiveTreeNode field. */
723
+ jsxSource?: FlotraceJsxSource;
463
724
  }
464
725
  type LanePriority = 'sync' | 'discrete' | 'continuous' | 'default' | 'transition' | 'deferred' | 'idle' | 'offscreen';
465
726
  interface LaneInfo {
@@ -494,6 +755,8 @@ interface PropDrillingChainNode {
494
755
  role: 'source' | 'passthrough' | 'consumer';
495
756
  hookCount: number;
496
757
  hasContextHook: boolean;
758
+ /** JSX-runtime attribution (Milestone 8 Phase 6). */
759
+ jsxSource?: FlotraceJsxSource;
497
760
  }
498
761
  interface PropDrillingChain {
499
762
  chainId: string;
@@ -521,21 +784,27 @@ interface RuntimePropDrillingMessage {
521
784
  treeSize: number;
522
785
  };
523
786
  }
787
+ /**
788
+ * State of a single useActionState / useOptimistic hook instance on a fiber.
789
+ * Mirror of `ActionStateEntry` in flotrace-desktop's `shared/liveMessages.ts`
790
+ * — keep the field set in sync.
791
+ */
792
+ interface ActionStateEntry {
793
+ hookIndex: number;
794
+ hookKind: 'action' | 'optimistic';
795
+ isPending: boolean;
796
+ state: SerializedValue;
797
+ error?: SerializedValue;
798
+ pendingSince?: number;
799
+ durationMs?: number;
800
+ }
524
801
  /** Sent whenever a useActionState or useOptimistic hook changes on any fiber */
525
802
  interface RuntimeActionStateMessage {
526
803
  type: 'runtime:actionState';
527
804
  nodeId: string;
528
805
  componentName: string;
529
806
  /** One entry per useActionState / useOptimistic hook on this fiber */
530
- actions: Array<{
531
- hookIndex: number;
532
- hookKind: 'action' | 'optimistic';
533
- isPending: boolean;
534
- state: SerializedValue;
535
- error?: SerializedValue;
536
- pendingSince?: number;
537
- durationMs?: number;
538
- }>;
807
+ actions: ActionStateEntry[];
539
808
  timestamp: number;
540
809
  }
541
810
  /** Sent when a useOptimistic value diverges from its underlying actual value */
@@ -557,12 +826,17 @@ interface RuntimeNextjsContextMessage {
557
826
  initialRoute?: string;
558
827
  timestamp: number;
559
828
  }
829
+ /**
830
+ * RSC / Next.js cache header status. Mirror of the union in
831
+ * `shared/liveMessages.ts`'s `RscPayloadEntry.cacheStatus` — keep in sync.
832
+ */
833
+ type RscCacheStatus = 'HIT' | 'MISS' | 'STALE' | 'unknown';
560
834
  /** Sent when an RSC / Next.js data fetch is intercepted (metadata only, no values) */
561
835
  interface RuntimeRscPayloadMessage {
562
836
  type: 'runtime:rscPayload';
563
837
  route: string;
564
838
  payloadSizeBytes: number;
565
- cacheStatus: 'HIT' | 'MISS' | 'STALE' | 'unknown';
839
+ cacheStatus: RscCacheStatus;
566
840
  timestamp: number;
567
841
  }
568
842
  /** Sent when React hydration completes or a mismatch is detected */
@@ -573,6 +847,48 @@ interface RuntimeHydrationEventMessage {
573
847
  errorMessage?: string;
574
848
  timestamp: number;
575
849
  }
850
+ /**
851
+ * Per-callsite render frequency snapshot. Emitted at most once per second by
852
+ * the runtime when the JSX-runtime opt-in is active — the ring buffer in
853
+ * `jsxRuntimeUtils.ts` is the source of truth, this message is a periodic
854
+ * compaction of the buffer into "renders/sec over last 5s" so the desktop
855
+ * doesn't need to mirror the buffer.
856
+ *
857
+ * Independent of the React Profiler — works in concurrent rendering where the
858
+ * Profiler may miss commits. Only emitted when there's at least one callsite
859
+ * with non-zero recent activity (no metric-flood on idle apps).
860
+ */
861
+ interface RuntimeCallSiteMetricsMessage {
862
+ type: 'runtime:callSiteMetrics';
863
+ /** Map of callSiteId → renders/sec over the last 5-second window. */
864
+ metrics: Record<string, number>;
865
+ timestamp: number;
866
+ }
867
+ /**
868
+ * Duplicate-key warning. Emitted by the JSX runtime when it observes the same
869
+ * `(callSiteId, key)` pair on two or more JSX calls within a single commit —
870
+ * the classic `{items.map(item => <Row key={item.id} />)}` pattern where
871
+ * `item.id` repeats. React logs a console warning for this; we surface it
872
+ * with full file:line attribution so the user can navigate directly to the
873
+ * map call site.
874
+ *
875
+ * One message per (callSiteId, duplicateKey) per emission window — the
876
+ * runtime de-duplicates so a list with 100 duplicate rows doesn't spam 100
877
+ * messages.
878
+ */
879
+ interface RuntimeDuplicateKeyMessage {
880
+ type: 'runtime:duplicateKey';
881
+ /** The JSX call site that produced the duplicates. */
882
+ callSiteId: string;
883
+ fileName: string;
884
+ lineNumber: number;
885
+ columnNumber: number;
886
+ /** The key value that appeared more than once. */
887
+ duplicateKey: string;
888
+ /** How many times the same key fired at this call site in the commit. */
889
+ occurrences: number;
890
+ timestamp: number;
891
+ }
576
892
  /** Metadata for a single intercepted network request. Privacy-first: no bodies, no query params, no auth headers. */
577
893
  interface NetworkRequestEntry {
578
894
  /** Incrementing request ID */
@@ -646,6 +962,13 @@ type TraceStep = {
646
962
  /** If this step came via a rename edge in the drilling graph. */
647
963
  renamedFrom?: string;
648
964
  confidence: TraceConfidence;
965
+ /**
966
+ * JSX-runtime attribution of the PARENT fiber that wrote the value into
967
+ * this prop (Milestone 8 Phase 6). Captured by `readJsxSourceFromFiber`
968
+ * on the ancestor at trace-resolution time. Undefined when the parent
969
+ * fiber lacks attribution.
970
+ */
971
+ callSiteOfParentJsx?: FlotraceJsxSource;
649
972
  } | {
650
973
  kind: 'hook-state';
651
974
  nodeId: string;
@@ -1181,7 +1504,7 @@ declare function uninstallFiberTreeWalker(): void;
1181
1504
  *
1182
1505
  * Works across React 18 and 19 by probing different internal property names.
1183
1506
  */
1184
- interface FiberLike {
1507
+ interface FiberLike$1 {
1185
1508
  type: {
1186
1509
  name?: string;
1187
1510
  displayName?: string;
@@ -1194,7 +1517,7 @@ interface FiberLike {
1194
1517
  displayName?: string;
1195
1518
  };
1196
1519
  } | ((...args: unknown[]) => unknown) | string | null;
1197
- return: FiberLike | null;
1520
+ return: FiberLike$1 | null;
1198
1521
  tag: number;
1199
1522
  }
1200
1523
  /**
@@ -1204,16 +1527,16 @@ interface FiberLike {
1204
1527
  * Strategy 2 (React 19): __CLIENT_INTERNALS...owner field (renamed + flattened)
1205
1528
  * Both return the fiber currently being rendered (null between renders).
1206
1529
  */
1207
- declare function getCurrentRenderingFiber(): FiberLike | null;
1530
+ declare function getCurrentRenderingFiber(): FiberLike$1 | null;
1208
1531
  /**
1209
1532
  * Extract display name from a fiber node.
1210
1533
  */
1211
- declare function getComponentNameFromFiber(fiber: FiberLike): string | null;
1534
+ declare function getComponentNameFromFiber(fiber: FiberLike$1): string | null;
1212
1535
  /**
1213
1536
  * Walk up the fiber tree to build an ancestor chain of component names.
1214
1537
  * Stops after 10 levels to prevent excessive traversal.
1215
1538
  */
1216
- declare function buildAncestorChain(fiber: FiberLike): string[];
1539
+ declare function buildAncestorChain(fiber: FiberLike$1): string[];
1217
1540
 
1218
1541
  /**
1219
1542
  * Hook Inspector for @flotrace/runtime
@@ -1714,4 +2037,99 @@ declare function installRscPayloadInterceptor(client: FloTraceWebSocketClient):
1714
2037
  /** Remove the RSC payload interceptor and restore original fetch */
1715
2038
  declare function uninstallRscPayloadInterceptor(): void;
1716
2039
 
1717
- export { DEFAULT_CONFIG, type DetailedRenderReason, type DetailedRenderReasonType, type EffectInfo, type Fiber$1 as Fiber, type FiberEffect, type FiberHookState, type FiberTreeWalkerOptions, type FloTraceConfig, FloTraceWebSocketClient, type FrameworkInfo, type HookInfo, type HookType, type LiveTreeNode, type MutationCorrelation, type NetworkRequestEntry, type PropChange, type ReduxStoreApi, type ResolvedFloTraceConfig, type RuntimeTreeDiffMessage, type RuntimeValueTraceMessage, type SerializedValue, 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, clearFetchOriginTags, detectServerComponent, detectWebFramework, disposeWebSocketClient, findFetchOrigin, getChangedKeys, getComponentNameFromFiber, getCurrentRenderingFiber, getDetailedRenderReason, getFiberRefMap, getNodeEffects, getNodeHooks, getNodeProps, getReduxSnapshot, getTanstackSnapshot, getTimeline, getWebSocketClient, getZustandSnapshot, hasActiveTags, inspectEffects, inspectHooks, installFiberTreeWalker, installReduxTracker, installRscPayloadInterceptor, installTanStackQueryTracker, installTimelineTracker, installZustandTracker, isReduxStore, isTanStackQueryClient, maybeEmitNextjsContext, recordTimelineEvent, requestFullSnapshot, requestTreeSnapshot, resetNextjsDetection, resolveValueTrace, serializeProps, serializeValue, tagFetchData, uninstallFiberTreeWalker, uninstallReduxTracker, uninstallRscPayloadInterceptor, uninstallTanStackQueryTracker, uninstallTimelineTracker, uninstallZustandTracker };
2040
+ /**
2041
+ * Fiber debug recorder — buffers fiber + LiveTreeNode observations into
2042
+ * capped in-memory tables, then dumps them to the console *on demand*.
2043
+ *
2044
+ * Why not log eagerly? `getComponentName` runs once per fiber per commit.
2045
+ * A 200-node tree at 60Hz produces ~12k console entries/sec, which OOMs
2046
+ * DevTools. The recorder keeps recording silent until you ask for a dump.
2047
+ *
2048
+ * Usage (from any console — Next.js app, FloTrace renderer, Electron main):
2049
+ * globalThis.__FT_DEBUG = true // start recording
2050
+ * __ft.dump() // print digest (fibers + snapshots)
2051
+ * __ft.fibers() // just the fiber-name table
2052
+ * __ft.snapshots() // just the snapshot timeline
2053
+ * __ft.tail(20) // recent activity
2054
+ * __ft.clear() // reset buffers
2055
+ * __ft.export() // returns a JSON object
2056
+ * __ft.download() // browser-only: save buffers as JSON
2057
+ * __ft.size() // current buffer sizes
2058
+ *
2059
+ * All recorders are no-ops when __FT_DEBUG is falsy, so the wired call
2060
+ * sites cost ~1 boolean check each.
2061
+ */
2062
+
2063
+ interface FiberRecord {
2064
+ /** Resolved component name (what FloTrace ultimately shows on the node). */
2065
+ name: string;
2066
+ /** Raw `fiber.type.name` — present even for ForwardRef/Memo's inner render. */
2067
+ rawName: string | undefined;
2068
+ /** Raw `fiber.type.displayName` — what the author explicitly set. */
2069
+ rawDisplayName: string | undefined;
2070
+ kind: 'function' | 'class' | 'forwardRef' | 'memo' | 'host' | 'unknown';
2071
+ /** Fiber tag number (0=Function, 1=Class, 11=ForwardRef, 14=Memo, etc.). */
2072
+ fiberTag: number | undefined;
2073
+ looksMinified: boolean;
2074
+ count: number;
2075
+ contexts: Set<string>;
2076
+ firstSeen: number;
2077
+ lastSeen: number;
2078
+ /** First captured _debugSource (if any) — only available in dev builds. */
2079
+ source?: {
2080
+ fileName?: string;
2081
+ lineNumber?: number;
2082
+ };
2083
+ /** First non-null react key observed (useful for diagnosing list rows). */
2084
+ exampleKey?: string;
2085
+ }
2086
+ interface TreeRecord {
2087
+ ts: number;
2088
+ ctx: string;
2089
+ rootName: string;
2090
+ totalNodes: number;
2091
+ maxDepth: number;
2092
+ minifiedLike: number;
2093
+ topNames: string;
2094
+ }
2095
+ declare global {
2096
+ var __FT_DEBUG: boolean | undefined;
2097
+ var __ft: FtConsoleApi | undefined;
2098
+ }
2099
+ declare function setFiberDebug(enabled: boolean): void;
2100
+ type FiberLike = {
2101
+ tag?: number;
2102
+ type?: unknown;
2103
+ key?: string | null;
2104
+ _debugSource?: {
2105
+ fileName?: string;
2106
+ lineNumber?: number;
2107
+ } | null;
2108
+ };
2109
+ declare function describeFiberType(fiber: FiberLike): {
2110
+ kind: FiberRecord['kind'];
2111
+ name: string | undefined;
2112
+ displayName: string | undefined;
2113
+ resolved: string;
2114
+ looksMinified: boolean;
2115
+ };
2116
+ declare function logTreeSnapshot(tree: LiveTreeNode | null, context?: string): void;
2117
+ declare const logTreeSummary: typeof logTreeSnapshot;
2118
+ interface FtConsoleApi {
2119
+ dump(): void;
2120
+ fibers(): void;
2121
+ snapshots(): void;
2122
+ tail(n?: number): void;
2123
+ clear(): void;
2124
+ size(): {
2125
+ fibers: number;
2126
+ snapshots: number;
2127
+ };
2128
+ export(): {
2129
+ fibers: Array<Record<string, unknown>>;
2130
+ snapshots: TreeRecord[];
2131
+ };
2132
+ download(filename?: string): void;
2133
+ }
2134
+
2135
+ 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, isUserComponent, logTreeSnapshot, logTreeSummary, markJsxRuntimeActive, maybeEmitNextjsContext, normalizeJsxSourcePath, recordCallSiteRender, recordTimelineEvent, requestFullSnapshot, requestTreeSnapshot, resetNextjsDetection, resolveValueTrace, serializeProps, serializeValue, setDuplicateKeyEmitter, setFiberDebug, tagFetchData, uninstallFiberTreeWalker, uninstallReduxTracker, uninstallRscPayloadInterceptor, uninstallTanStackQueryTracker, uninstallTimelineTracker, uninstallZustandTracker };