@flotrace/runtime-core 2.0.0 → 2.2.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/dist/index.d.mts CHANGED
@@ -26,7 +26,7 @@ type SerializedValue = null | boolean | number | string | SerializedValue[] | {
26
26
  /**
27
27
  * Messages sent from runtime to extension
28
28
  */
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 | RuntimePongMessage;
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;
30
30
  interface RuntimeReadyMessage {
31
31
  type: 'runtime:ready';
32
32
  appName?: string;
@@ -39,6 +39,15 @@ interface RuntimeReadyMessage {
39
39
  appId?: string;
40
40
  /** App version — shown in the desktop connection panel for diagnostic purposes. */
41
41
  appVersion?: string;
42
+ /** Auto-detected framework family. Adapters populate this from runtime probes
43
+ * (DOM signals on web, optional-require on native). */
44
+ frameworkName?: 'next' | 'expo' | 'rn-cli' | 'plain-react';
45
+ /** Framework version when the adapter can resolve it (e.g. Next.js via
46
+ * `require('next/package.json').version`). Missing when unavailable. */
47
+ frameworkVersion?: string;
48
+ /** React Native version from `Platform.constants.reactNativeVersion`, formatted
49
+ * as "major.minor.patch". Native-only; web adapter leaves this undefined. */
50
+ reactNativeVersion?: string;
42
51
  }
43
52
  interface RuntimeRenderMessage {
44
53
  type: 'runtime:render';
@@ -610,6 +619,103 @@ interface RuntimeLocalStateCorrelationMessage {
610
619
  hookIndex: number;
611
620
  timestamp: number;
612
621
  }
622
+ /**
623
+ * Confidence of a single trace step boundary.
624
+ * - `exact`: reference identity (`===`) preserved across the step.
625
+ * - `fingerprint-match`: only structural match via valueFingerprint(). Primitive
626
+ * collisions are possible here (e.g., `count: 5` at multiple call sites).
627
+ */
628
+ type TraceConfidence = 'exact' | 'fingerprint-match';
629
+ /**
630
+ * A single step in a value-origin chain.
631
+ * Chain ordering: consumer first, origin last.
632
+ */
633
+ type TraceStep = {
634
+ kind: 'prop';
635
+ nodeId: string;
636
+ componentName: string;
637
+ /** Dot-path into the component's props (e.g., ['user', 'profile', 'avatarUrl']). */
638
+ propPath: string[];
639
+ /** If this step came via a rename edge in the drilling graph. */
640
+ renamedFrom?: string;
641
+ confidence: TraceConfidence;
642
+ } | {
643
+ kind: 'hook-state';
644
+ nodeId: string;
645
+ componentName: string;
646
+ hookIndex: number;
647
+ hookType: HookType;
648
+ /** Sub-path into the hook's current value, when the traced leaf is nested. */
649
+ subPath?: string[];
650
+ confidence: TraceConfidence;
651
+ } | {
652
+ kind: 'store';
653
+ source: 'zustand' | 'redux' | 'tanstack-query';
654
+ storeName: string;
655
+ /** Key path into the store state where the matching value lives. */
656
+ keyPath: string[];
657
+ confidence: TraceConfidence;
658
+ } | {
659
+ kind: 'api';
660
+ requestId: string;
661
+ method: string;
662
+ /** URL path only — query params stripped for privacy. */
663
+ urlPath: string;
664
+ status?: number;
665
+ /** Age of the fetch at trace resolution time. */
666
+ ageMs: number;
667
+ /** True when the 3s FETCH_ORIGIN_TTL_MS window has lapsed. */
668
+ expired?: boolean;
669
+ } | {
670
+ kind: 'context';
671
+ contextName: string;
672
+ providerNodeId?: string;
673
+ confidence: TraceConfidence;
674
+ } | {
675
+ /** Value is the cached result of a useMemo/useCallback on the same fiber.
676
+ * `depCount` tells the UI how many upstream inputs the memo depends on
677
+ * (one of which the user may want to click-trace next). */
678
+ kind: 'derived';
679
+ nodeId: string;
680
+ componentName: string;
681
+ hookIndex: number;
682
+ hookType: 'useMemo' | 'useCallback';
683
+ depCount: number;
684
+ confidence: TraceConfidence;
685
+ };
686
+ /**
687
+ * Result of a single value-trace request.
688
+ */
689
+ interface ValueTrace {
690
+ /** Round-trip ID — mirrors the requestId from ext:traceValue. */
691
+ requestId: string;
692
+ rootNodeId: string;
693
+ /** Dot-path from the component when tracing a prop (e.g., ['user','profile','avatarUrl']). */
694
+ rootPropPath?: string[];
695
+ /** When tracing a hook value, addresses hook index + optional sub-path inside its value. */
696
+ rootHookPath?: {
697
+ hookIndex: number;
698
+ subPath?: string[];
699
+ };
700
+ /** Ordered steps — index 0 is the consumer, last index is the origin. */
701
+ steps: TraceStep[];
702
+ /** Wall-clock time the resolver completed. */
703
+ resolvedAtMs: number;
704
+ /** True when the 50ms budget tripped and the chain is partial. */
705
+ truncated?: boolean;
706
+ /**
707
+ * Optional error hint for friendly empty states.
708
+ * - `value-not-found`: target path doesn't exist on the current fiber.
709
+ * - `no-fiber`: nodeId no longer present in fiberRefMap (component unmounted).
710
+ * - `budget-exceeded`: bailed before finding origin.
711
+ */
712
+ error?: 'value-not-found' | 'no-fiber' | 'budget-exceeded';
713
+ }
714
+ interface RuntimeValueTraceMessage {
715
+ type: 'runtime:valueTrace';
716
+ trace: ValueTrace;
717
+ timestamp: number;
718
+ }
613
719
  /**
614
720
  * Messages received from extension
615
721
  */
@@ -664,6 +770,23 @@ type ExtensionToRuntimeMessage = {
664
770
  type: 'ext:startTanstackTracking';
665
771
  } | {
666
772
  type: 'ext:stopTanstackTracking';
773
+ }
774
+ /**
775
+ * Value Lineage — resolve the origin of a specific prop or hook value.
776
+ * Either `propPath` or `hookPath` must be set (exactly one).
777
+ */
778
+ | {
779
+ type: 'ext:traceValue';
780
+ /** Round-trip ID — echoed back in runtime:valueTrace. */
781
+ requestId: string;
782
+ nodeId: string;
783
+ /** When tracing a prop: dot-path like ['user','profile','avatarUrl']. Index 0 is the top-level prop key. */
784
+ propPath?: string[];
785
+ /** When tracing a hook value: hook index + optional nested sub-path inside its value. */
786
+ hookPath?: {
787
+ hookIndex: number;
788
+ subPath?: string[];
789
+ };
667
790
  };
668
791
  interface TrackingOptions {
669
792
  trackAllRenders?: boolean;
@@ -736,16 +859,54 @@ interface FloTraceConfig {
736
859
  * be hidden by strict mode (e.g. monorepo packages resolved into
737
860
  * `node_modules/@workspace/ui`). Only consulted when `userOnlyStrict` is on. */
738
861
  userAllowPatterns?: RegExp[];
862
+ /** Auto-detected framework family. Adapters set this via platform-specific probes
863
+ * (DOM signals on web; optional-require on native). Not a user-facing knob. */
864
+ frameworkName?: 'next' | 'expo' | 'rn-cli' | 'plain-react';
865
+ /** Framework version when the adapter can resolve it. Not a user-facing knob. */
866
+ frameworkVersion?: string;
867
+ /** React Native version from `Platform.constants.reactNativeVersion`, formatted
868
+ * "major.minor.patch". Native adapter only. */
869
+ reactNativeVersion?: string;
739
870
  }
740
871
  /** Keys that stay optional in DEFAULT_CONFIG. These are populated by adapters (web/native)
741
872
  * at call-time — the default object should not pretend to know a platform or LAN token. */
742
- type OptionalConfigKeys = 'getAppUrl' | 'platform' | 'appId' | 'appVersion' | 'host' | 'authToken' | 'userOnlyStrict' | 'userAllowPatterns';
873
+ type OptionalConfigKeys = 'getAppUrl' | 'platform' | 'appId' | 'appVersion' | 'host' | 'authToken' | 'userOnlyStrict' | 'userAllowPatterns' | 'frameworkName' | 'frameworkVersion' | 'reactNativeVersion';
743
874
  type ResolvedFloTraceConfig = Required<Omit<FloTraceConfig, OptionalConfigKeys>> & Pick<FloTraceConfig, OptionalConfigKeys>;
744
875
  /**
745
876
  * Default configuration
746
877
  */
747
878
  declare const DEFAULT_CONFIG: ResolvedFloTraceConfig;
748
879
 
880
+ /**
881
+ * Value Lineage — origin tracing resolver.
882
+ *
883
+ * Given a component's prop-path or hook-path, walk upward through three layers
884
+ * to find where the value originated:
885
+ *
886
+ * 1. Prop chain — walk fiber.return upward, match fingerprint at each level
887
+ * 2. Store match — scan Zustand / Redux / TanStack Query live snapshots
888
+ * 3. API match — feed matched reference into fetchOriginRegistry WeakMap
889
+ *
890
+ * On-demand only — never runs per-render. Hard 50 ms wall-clock budget.
891
+ * Uses reference identity (`===`) before fingerprinting to keep common cases fast.
892
+ *
893
+ * See docs/PRD-VALUE-LINEAGE.md §6 and docs/IMPLEMENTATION-PLAN-VALUE-LINEAGE.md Phase 2.
894
+ */
895
+
896
+ interface ValueTraceInput {
897
+ nodeId: string;
898
+ propPath?: string[];
899
+ hookPath?: {
900
+ hookIndex: number;
901
+ subPath?: string[];
902
+ };
903
+ }
904
+ /**
905
+ * Resolve the origin chain for a specific prop or hook value on a component.
906
+ * Returns `{requestId}`-less trace — caller adds the round-trip id.
907
+ */
908
+ declare function resolveValueTrace(input: ValueTraceInput): Omit<ValueTrace, 'requestId'>;
909
+
749
910
  /**
750
911
  * Fiber Tree Walker for @flotrace/runtime
751
912
  *
@@ -1157,7 +1318,13 @@ declare class FloTraceWebSocketClient {
1157
1318
  */
1158
1319
  get connected(): boolean;
1159
1320
  /**
1160
- * Get React version if available
1321
+ * Get React version if available.
1322
+ *
1323
+ * Historical note: an earlier implementation read `globalThis.React?.version` —
1324
+ * but React is an ES-module import in modern bundles (Vite/webpack/Next.js) and
1325
+ * is never placed on `globalThis`, so the probe returned undefined for every
1326
+ * typical bundled app. Reading `React.version` via a direct import is
1327
+ * authoritative across web (both CJS and ESM bundles), React Native, and SSR.
1161
1328
  */
1162
1329
  private getReactVersion;
1163
1330
  }
@@ -1200,6 +1367,16 @@ declare function installZustandTracker(stores: Record<string, ZustandStoreApi>,
1200
1367
  * Uninstall Zustand store tracking, unsubscribing from all stores.
1201
1368
  */
1202
1369
  declare function uninstallZustandTracker(): void;
1370
+ /**
1371
+ * Snapshot of every tracked Zustand store's current **raw** state (not
1372
+ * serialized). Used by the Value Lineage resolver to match component values
1373
+ * against store keys via reference identity + structural fingerprint, and to
1374
+ * pass raw object references into `findFetchOrigin()` for API correlation.
1375
+ *
1376
+ * Returns a fresh map on each call — does not cache. Cheap because `.getState()`
1377
+ * is O(1) on Zustand.
1378
+ */
1379
+ declare function getZustandSnapshot(): Map<string, Record<string, unknown>>;
1203
1380
 
1204
1381
  /**
1205
1382
  * Redux Store Tracker for @flotrace/runtime
@@ -1235,6 +1412,11 @@ declare function installReduxTracker(store: ReduxStoreApi, client: FloTraceWebSo
1235
1412
  * Uninstall Redux store tracking.
1236
1413
  */
1237
1414
  declare function uninstallReduxTracker(): void;
1415
+ /**
1416
+ * Current raw Redux state, or null when the tracker isn't installed. Used by
1417
+ * the Value Lineage resolver.
1418
+ */
1419
+ declare function getReduxSnapshot(): Record<string, unknown> | null;
1238
1420
 
1239
1421
  /**
1240
1422
  * TanStack Query Tracker for @flotrace/runtime
@@ -1333,6 +1515,18 @@ declare function installTanStackQueryTracker(queryClient: TanStackQueryClientApi
1333
1515
  * Uninstall TanStack Query tracking.
1334
1516
  */
1335
1517
  declare function uninstallTanStackQueryTracker(): void;
1518
+ /**
1519
+ * Snapshot of every cached query's **raw** data (not serialized) keyed by
1520
+ * queryHash. Used by the Value Lineage resolver to match component values
1521
+ * against TanStack Query cache via reference identity + structural fingerprint,
1522
+ * and to pass raw references into `findFetchOrigin()`.
1523
+ *
1524
+ * Returns an empty map when the tracker isn't installed.
1525
+ */
1526
+ declare function getTanstackSnapshot(): Map<string, {
1527
+ queryKey: unknown[];
1528
+ data: unknown;
1529
+ }>;
1336
1530
 
1337
1531
  /**
1338
1532
  * Component Event Timeline for @flotrace/runtime
@@ -1445,6 +1639,45 @@ declare function detectServerComponent(fiber: Fiber): boolean;
1445
1639
  /** Reset detection state (useful for tests / hot-reload) */
1446
1640
  declare function resetNextjsDetection(): void;
1447
1641
 
1642
+ /**
1643
+ * Framework + framework-version detection (web adapter).
1644
+ *
1645
+ * Detects whether the running page is Next.js vs plain React and attempts to
1646
+ * resolve the Next.js version via the package's own `package.json`. All signals
1647
+ * are probed at runtime; absence is non-fatal and simply returns `undefined`.
1648
+ *
1649
+ * Node version of the project is intentionally NOT captured — `process.version`
1650
+ * is unavailable in browsers, and surfacing it would require user bundler
1651
+ * config (Vite `define`, Next.js `env`), which breaks the zero-config contract.
1652
+ */
1653
+ interface FrameworkInfo {
1654
+ frameworkName?: 'next' | 'expo' | 'rn-cli' | 'plain-react';
1655
+ frameworkVersion?: string;
1656
+ }
1657
+ /**
1658
+ * Detect the web framework hosting this page and, when possible, its version.
1659
+ *
1660
+ * - Next.js: presence detected via `window.__NEXT_DATA__` (App Router + Pages
1661
+ * Router both set it) or the `<script id="__NEXT_DATA__">` element.
1662
+ * - Otherwise: reports `plain-react` (covers Vite, CRA, Remix, and anything else
1663
+ * not positively identified as Next.js).
1664
+ *
1665
+ * Returns `{}` in non-DOM contexts (SSR on Node, first render of a Client
1666
+ * Component). `FloTraceProvider` only creates the WebSocket singleton on the
1667
+ * client, so the empty-object branch is harmless — detection re-runs after
1668
+ * hydration with a DOM available.
1669
+ *
1670
+ * **About `frameworkVersion`**: in a Next.js *client* bundle, `globalThis.require`
1671
+ * is normally not defined — so the version probe below usually no-ops and we
1672
+ * report `frameworkName: 'next'` with `frameworkVersion: undefined`. This is
1673
+ * by design: version detection is best-effort, the name alone is enough for
1674
+ * admin-side categorization, and the string-concat require trick exists
1675
+ * primarily to keep non-Next bundlers from failing the build on a static
1676
+ * `require('next/package.json')` they can't resolve. Framework *detection*
1677
+ * works reliably via `__NEXT_DATA__` regardless.
1678
+ */
1679
+ declare function detectWebFramework(): FrameworkInfo;
1680
+
1448
1681
  /**
1449
1682
  * RSC payload fetch interceptor for Next.js App Router.
1450
1683
  * Patches globalThis.fetch to detect Next.js data / RSC fetch requests,
@@ -1462,4 +1695,4 @@ declare function installRscPayloadInterceptor(client: FloTraceWebSocketClient):
1462
1695
  /** Remove the RSC payload interceptor and restore original fetch */
1463
1696
  declare function uninstallRscPayloadInterceptor(): void;
1464
1697
 
1465
- export { DEFAULT_CONFIG, type DetailedRenderReason, type DetailedRenderReasonType, type EffectInfo, type Fiber$1 as Fiber, type FiberEffect, type FiberHookState, type FiberTreeWalkerOptions, type FloTraceConfig, FloTraceWebSocketClient, type HookInfo, type HookType, type LiveTreeNode, type MutationCorrelation, type NetworkRequestEntry, type PropChange, type ReduxStoreApi, type ResolvedFloTraceConfig, type RuntimeTreeDiffMessage, type SerializedValue, type TanStackMutationInfo, type TanStackQueryClientApi, type TanStackQueryEvent, type TanStackQueryInfo, type TimelineEvent, type TimelineEventType, type TrackingOptions, type ZustandStoreApi, buildAncestorChain, clearFetchOriginTags, detectServerComponent, disposeWebSocketClient, findFetchOrigin, getChangedKeys, getComponentNameFromFiber, getCurrentRenderingFiber, getDetailedRenderReason, getFiberRefMap, getNodeEffects, getNodeHooks, getNodeProps, getTimeline, getWebSocketClient, hasActiveTags, inspectEffects, inspectHooks, installFiberTreeWalker, installReduxTracker, installRscPayloadInterceptor, installTanStackQueryTracker, installTimelineTracker, installZustandTracker, isReduxStore, isTanStackQueryClient, maybeEmitNextjsContext, recordTimelineEvent, requestFullSnapshot, requestTreeSnapshot, resetNextjsDetection, serializeProps, serializeValue, tagFetchData, uninstallFiberTreeWalker, uninstallReduxTracker, uninstallRscPayloadInterceptor, uninstallTanStackQueryTracker, uninstallTimelineTracker, uninstallZustandTracker };
1698
+ 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 };
package/dist/index.d.ts CHANGED
@@ -26,7 +26,7 @@ type SerializedValue = null | boolean | number | string | SerializedValue[] | {
26
26
  /**
27
27
  * Messages sent from runtime to extension
28
28
  */
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 | RuntimePongMessage;
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;
30
30
  interface RuntimeReadyMessage {
31
31
  type: 'runtime:ready';
32
32
  appName?: string;
@@ -39,6 +39,15 @@ interface RuntimeReadyMessage {
39
39
  appId?: string;
40
40
  /** App version — shown in the desktop connection panel for diagnostic purposes. */
41
41
  appVersion?: string;
42
+ /** Auto-detected framework family. Adapters populate this from runtime probes
43
+ * (DOM signals on web, optional-require on native). */
44
+ frameworkName?: 'next' | 'expo' | 'rn-cli' | 'plain-react';
45
+ /** Framework version when the adapter can resolve it (e.g. Next.js via
46
+ * `require('next/package.json').version`). Missing when unavailable. */
47
+ frameworkVersion?: string;
48
+ /** React Native version from `Platform.constants.reactNativeVersion`, formatted
49
+ * as "major.minor.patch". Native-only; web adapter leaves this undefined. */
50
+ reactNativeVersion?: string;
42
51
  }
43
52
  interface RuntimeRenderMessage {
44
53
  type: 'runtime:render';
@@ -610,6 +619,103 @@ interface RuntimeLocalStateCorrelationMessage {
610
619
  hookIndex: number;
611
620
  timestamp: number;
612
621
  }
622
+ /**
623
+ * Confidence of a single trace step boundary.
624
+ * - `exact`: reference identity (`===`) preserved across the step.
625
+ * - `fingerprint-match`: only structural match via valueFingerprint(). Primitive
626
+ * collisions are possible here (e.g., `count: 5` at multiple call sites).
627
+ */
628
+ type TraceConfidence = 'exact' | 'fingerprint-match';
629
+ /**
630
+ * A single step in a value-origin chain.
631
+ * Chain ordering: consumer first, origin last.
632
+ */
633
+ type TraceStep = {
634
+ kind: 'prop';
635
+ nodeId: string;
636
+ componentName: string;
637
+ /** Dot-path into the component's props (e.g., ['user', 'profile', 'avatarUrl']). */
638
+ propPath: string[];
639
+ /** If this step came via a rename edge in the drilling graph. */
640
+ renamedFrom?: string;
641
+ confidence: TraceConfidence;
642
+ } | {
643
+ kind: 'hook-state';
644
+ nodeId: string;
645
+ componentName: string;
646
+ hookIndex: number;
647
+ hookType: HookType;
648
+ /** Sub-path into the hook's current value, when the traced leaf is nested. */
649
+ subPath?: string[];
650
+ confidence: TraceConfidence;
651
+ } | {
652
+ kind: 'store';
653
+ source: 'zustand' | 'redux' | 'tanstack-query';
654
+ storeName: string;
655
+ /** Key path into the store state where the matching value lives. */
656
+ keyPath: string[];
657
+ confidence: TraceConfidence;
658
+ } | {
659
+ kind: 'api';
660
+ requestId: string;
661
+ method: string;
662
+ /** URL path only — query params stripped for privacy. */
663
+ urlPath: string;
664
+ status?: number;
665
+ /** Age of the fetch at trace resolution time. */
666
+ ageMs: number;
667
+ /** True when the 3s FETCH_ORIGIN_TTL_MS window has lapsed. */
668
+ expired?: boolean;
669
+ } | {
670
+ kind: 'context';
671
+ contextName: string;
672
+ providerNodeId?: string;
673
+ confidence: TraceConfidence;
674
+ } | {
675
+ /** Value is the cached result of a useMemo/useCallback on the same fiber.
676
+ * `depCount` tells the UI how many upstream inputs the memo depends on
677
+ * (one of which the user may want to click-trace next). */
678
+ kind: 'derived';
679
+ nodeId: string;
680
+ componentName: string;
681
+ hookIndex: number;
682
+ hookType: 'useMemo' | 'useCallback';
683
+ depCount: number;
684
+ confidence: TraceConfidence;
685
+ };
686
+ /**
687
+ * Result of a single value-trace request.
688
+ */
689
+ interface ValueTrace {
690
+ /** Round-trip ID — mirrors the requestId from ext:traceValue. */
691
+ requestId: string;
692
+ rootNodeId: string;
693
+ /** Dot-path from the component when tracing a prop (e.g., ['user','profile','avatarUrl']). */
694
+ rootPropPath?: string[];
695
+ /** When tracing a hook value, addresses hook index + optional sub-path inside its value. */
696
+ rootHookPath?: {
697
+ hookIndex: number;
698
+ subPath?: string[];
699
+ };
700
+ /** Ordered steps — index 0 is the consumer, last index is the origin. */
701
+ steps: TraceStep[];
702
+ /** Wall-clock time the resolver completed. */
703
+ resolvedAtMs: number;
704
+ /** True when the 50ms budget tripped and the chain is partial. */
705
+ truncated?: boolean;
706
+ /**
707
+ * Optional error hint for friendly empty states.
708
+ * - `value-not-found`: target path doesn't exist on the current fiber.
709
+ * - `no-fiber`: nodeId no longer present in fiberRefMap (component unmounted).
710
+ * - `budget-exceeded`: bailed before finding origin.
711
+ */
712
+ error?: 'value-not-found' | 'no-fiber' | 'budget-exceeded';
713
+ }
714
+ interface RuntimeValueTraceMessage {
715
+ type: 'runtime:valueTrace';
716
+ trace: ValueTrace;
717
+ timestamp: number;
718
+ }
613
719
  /**
614
720
  * Messages received from extension
615
721
  */
@@ -664,6 +770,23 @@ type ExtensionToRuntimeMessage = {
664
770
  type: 'ext:startTanstackTracking';
665
771
  } | {
666
772
  type: 'ext:stopTanstackTracking';
773
+ }
774
+ /**
775
+ * Value Lineage — resolve the origin of a specific prop or hook value.
776
+ * Either `propPath` or `hookPath` must be set (exactly one).
777
+ */
778
+ | {
779
+ type: 'ext:traceValue';
780
+ /** Round-trip ID — echoed back in runtime:valueTrace. */
781
+ requestId: string;
782
+ nodeId: string;
783
+ /** When tracing a prop: dot-path like ['user','profile','avatarUrl']. Index 0 is the top-level prop key. */
784
+ propPath?: string[];
785
+ /** When tracing a hook value: hook index + optional nested sub-path inside its value. */
786
+ hookPath?: {
787
+ hookIndex: number;
788
+ subPath?: string[];
789
+ };
667
790
  };
668
791
  interface TrackingOptions {
669
792
  trackAllRenders?: boolean;
@@ -736,16 +859,54 @@ interface FloTraceConfig {
736
859
  * be hidden by strict mode (e.g. monorepo packages resolved into
737
860
  * `node_modules/@workspace/ui`). Only consulted when `userOnlyStrict` is on. */
738
861
  userAllowPatterns?: RegExp[];
862
+ /** Auto-detected framework family. Adapters set this via platform-specific probes
863
+ * (DOM signals on web; optional-require on native). Not a user-facing knob. */
864
+ frameworkName?: 'next' | 'expo' | 'rn-cli' | 'plain-react';
865
+ /** Framework version when the adapter can resolve it. Not a user-facing knob. */
866
+ frameworkVersion?: string;
867
+ /** React Native version from `Platform.constants.reactNativeVersion`, formatted
868
+ * "major.minor.patch". Native adapter only. */
869
+ reactNativeVersion?: string;
739
870
  }
740
871
  /** Keys that stay optional in DEFAULT_CONFIG. These are populated by adapters (web/native)
741
872
  * at call-time — the default object should not pretend to know a platform or LAN token. */
742
- type OptionalConfigKeys = 'getAppUrl' | 'platform' | 'appId' | 'appVersion' | 'host' | 'authToken' | 'userOnlyStrict' | 'userAllowPatterns';
873
+ type OptionalConfigKeys = 'getAppUrl' | 'platform' | 'appId' | 'appVersion' | 'host' | 'authToken' | 'userOnlyStrict' | 'userAllowPatterns' | 'frameworkName' | 'frameworkVersion' | 'reactNativeVersion';
743
874
  type ResolvedFloTraceConfig = Required<Omit<FloTraceConfig, OptionalConfigKeys>> & Pick<FloTraceConfig, OptionalConfigKeys>;
744
875
  /**
745
876
  * Default configuration
746
877
  */
747
878
  declare const DEFAULT_CONFIG: ResolvedFloTraceConfig;
748
879
 
880
+ /**
881
+ * Value Lineage — origin tracing resolver.
882
+ *
883
+ * Given a component's prop-path or hook-path, walk upward through three layers
884
+ * to find where the value originated:
885
+ *
886
+ * 1. Prop chain — walk fiber.return upward, match fingerprint at each level
887
+ * 2. Store match — scan Zustand / Redux / TanStack Query live snapshots
888
+ * 3. API match — feed matched reference into fetchOriginRegistry WeakMap
889
+ *
890
+ * On-demand only — never runs per-render. Hard 50 ms wall-clock budget.
891
+ * Uses reference identity (`===`) before fingerprinting to keep common cases fast.
892
+ *
893
+ * See docs/PRD-VALUE-LINEAGE.md §6 and docs/IMPLEMENTATION-PLAN-VALUE-LINEAGE.md Phase 2.
894
+ */
895
+
896
+ interface ValueTraceInput {
897
+ nodeId: string;
898
+ propPath?: string[];
899
+ hookPath?: {
900
+ hookIndex: number;
901
+ subPath?: string[];
902
+ };
903
+ }
904
+ /**
905
+ * Resolve the origin chain for a specific prop or hook value on a component.
906
+ * Returns `{requestId}`-less trace — caller adds the round-trip id.
907
+ */
908
+ declare function resolveValueTrace(input: ValueTraceInput): Omit<ValueTrace, 'requestId'>;
909
+
749
910
  /**
750
911
  * Fiber Tree Walker for @flotrace/runtime
751
912
  *
@@ -1157,7 +1318,13 @@ declare class FloTraceWebSocketClient {
1157
1318
  */
1158
1319
  get connected(): boolean;
1159
1320
  /**
1160
- * Get React version if available
1321
+ * Get React version if available.
1322
+ *
1323
+ * Historical note: an earlier implementation read `globalThis.React?.version` —
1324
+ * but React is an ES-module import in modern bundles (Vite/webpack/Next.js) and
1325
+ * is never placed on `globalThis`, so the probe returned undefined for every
1326
+ * typical bundled app. Reading `React.version` via a direct import is
1327
+ * authoritative across web (both CJS and ESM bundles), React Native, and SSR.
1161
1328
  */
1162
1329
  private getReactVersion;
1163
1330
  }
@@ -1200,6 +1367,16 @@ declare function installZustandTracker(stores: Record<string, ZustandStoreApi>,
1200
1367
  * Uninstall Zustand store tracking, unsubscribing from all stores.
1201
1368
  */
1202
1369
  declare function uninstallZustandTracker(): void;
1370
+ /**
1371
+ * Snapshot of every tracked Zustand store's current **raw** state (not
1372
+ * serialized). Used by the Value Lineage resolver to match component values
1373
+ * against store keys via reference identity + structural fingerprint, and to
1374
+ * pass raw object references into `findFetchOrigin()` for API correlation.
1375
+ *
1376
+ * Returns a fresh map on each call — does not cache. Cheap because `.getState()`
1377
+ * is O(1) on Zustand.
1378
+ */
1379
+ declare function getZustandSnapshot(): Map<string, Record<string, unknown>>;
1203
1380
 
1204
1381
  /**
1205
1382
  * Redux Store Tracker for @flotrace/runtime
@@ -1235,6 +1412,11 @@ declare function installReduxTracker(store: ReduxStoreApi, client: FloTraceWebSo
1235
1412
  * Uninstall Redux store tracking.
1236
1413
  */
1237
1414
  declare function uninstallReduxTracker(): void;
1415
+ /**
1416
+ * Current raw Redux state, or null when the tracker isn't installed. Used by
1417
+ * the Value Lineage resolver.
1418
+ */
1419
+ declare function getReduxSnapshot(): Record<string, unknown> | null;
1238
1420
 
1239
1421
  /**
1240
1422
  * TanStack Query Tracker for @flotrace/runtime
@@ -1333,6 +1515,18 @@ declare function installTanStackQueryTracker(queryClient: TanStackQueryClientApi
1333
1515
  * Uninstall TanStack Query tracking.
1334
1516
  */
1335
1517
  declare function uninstallTanStackQueryTracker(): void;
1518
+ /**
1519
+ * Snapshot of every cached query's **raw** data (not serialized) keyed by
1520
+ * queryHash. Used by the Value Lineage resolver to match component values
1521
+ * against TanStack Query cache via reference identity + structural fingerprint,
1522
+ * and to pass raw references into `findFetchOrigin()`.
1523
+ *
1524
+ * Returns an empty map when the tracker isn't installed.
1525
+ */
1526
+ declare function getTanstackSnapshot(): Map<string, {
1527
+ queryKey: unknown[];
1528
+ data: unknown;
1529
+ }>;
1336
1530
 
1337
1531
  /**
1338
1532
  * Component Event Timeline for @flotrace/runtime
@@ -1445,6 +1639,45 @@ declare function detectServerComponent(fiber: Fiber): boolean;
1445
1639
  /** Reset detection state (useful for tests / hot-reload) */
1446
1640
  declare function resetNextjsDetection(): void;
1447
1641
 
1642
+ /**
1643
+ * Framework + framework-version detection (web adapter).
1644
+ *
1645
+ * Detects whether the running page is Next.js vs plain React and attempts to
1646
+ * resolve the Next.js version via the package's own `package.json`. All signals
1647
+ * are probed at runtime; absence is non-fatal and simply returns `undefined`.
1648
+ *
1649
+ * Node version of the project is intentionally NOT captured — `process.version`
1650
+ * is unavailable in browsers, and surfacing it would require user bundler
1651
+ * config (Vite `define`, Next.js `env`), which breaks the zero-config contract.
1652
+ */
1653
+ interface FrameworkInfo {
1654
+ frameworkName?: 'next' | 'expo' | 'rn-cli' | 'plain-react';
1655
+ frameworkVersion?: string;
1656
+ }
1657
+ /**
1658
+ * Detect the web framework hosting this page and, when possible, its version.
1659
+ *
1660
+ * - Next.js: presence detected via `window.__NEXT_DATA__` (App Router + Pages
1661
+ * Router both set it) or the `<script id="__NEXT_DATA__">` element.
1662
+ * - Otherwise: reports `plain-react` (covers Vite, CRA, Remix, and anything else
1663
+ * not positively identified as Next.js).
1664
+ *
1665
+ * Returns `{}` in non-DOM contexts (SSR on Node, first render of a Client
1666
+ * Component). `FloTraceProvider` only creates the WebSocket singleton on the
1667
+ * client, so the empty-object branch is harmless — detection re-runs after
1668
+ * hydration with a DOM available.
1669
+ *
1670
+ * **About `frameworkVersion`**: in a Next.js *client* bundle, `globalThis.require`
1671
+ * is normally not defined — so the version probe below usually no-ops and we
1672
+ * report `frameworkName: 'next'` with `frameworkVersion: undefined`. This is
1673
+ * by design: version detection is best-effort, the name alone is enough for
1674
+ * admin-side categorization, and the string-concat require trick exists
1675
+ * primarily to keep non-Next bundlers from failing the build on a static
1676
+ * `require('next/package.json')` they can't resolve. Framework *detection*
1677
+ * works reliably via `__NEXT_DATA__` regardless.
1678
+ */
1679
+ declare function detectWebFramework(): FrameworkInfo;
1680
+
1448
1681
  /**
1449
1682
  * RSC payload fetch interceptor for Next.js App Router.
1450
1683
  * Patches globalThis.fetch to detect Next.js data / RSC fetch requests,
@@ -1462,4 +1695,4 @@ declare function installRscPayloadInterceptor(client: FloTraceWebSocketClient):
1462
1695
  /** Remove the RSC payload interceptor and restore original fetch */
1463
1696
  declare function uninstallRscPayloadInterceptor(): void;
1464
1697
 
1465
- export { DEFAULT_CONFIG, type DetailedRenderReason, type DetailedRenderReasonType, type EffectInfo, type Fiber$1 as Fiber, type FiberEffect, type FiberHookState, type FiberTreeWalkerOptions, type FloTraceConfig, FloTraceWebSocketClient, type HookInfo, type HookType, type LiveTreeNode, type MutationCorrelation, type NetworkRequestEntry, type PropChange, type ReduxStoreApi, type ResolvedFloTraceConfig, type RuntimeTreeDiffMessage, type SerializedValue, type TanStackMutationInfo, type TanStackQueryClientApi, type TanStackQueryEvent, type TanStackQueryInfo, type TimelineEvent, type TimelineEventType, type TrackingOptions, type ZustandStoreApi, buildAncestorChain, clearFetchOriginTags, detectServerComponent, disposeWebSocketClient, findFetchOrigin, getChangedKeys, getComponentNameFromFiber, getCurrentRenderingFiber, getDetailedRenderReason, getFiberRefMap, getNodeEffects, getNodeHooks, getNodeProps, getTimeline, getWebSocketClient, hasActiveTags, inspectEffects, inspectHooks, installFiberTreeWalker, installReduxTracker, installRscPayloadInterceptor, installTanStackQueryTracker, installTimelineTracker, installZustandTracker, isReduxStore, isTanStackQueryClient, maybeEmitNextjsContext, recordTimelineEvent, requestFullSnapshot, requestTreeSnapshot, resetNextjsDetection, serializeProps, serializeValue, tagFetchData, uninstallFiberTreeWalker, uninstallReduxTracker, uninstallRscPayloadInterceptor, uninstallTanStackQueryTracker, uninstallTimelineTracker, uninstallZustandTracker };
1698
+ 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 };