@flotrace/runtime 2.0.0 → 2.2.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.js CHANGED
@@ -579,6 +579,19 @@ function safeTrackerOp(name, op) {
579
579
  console.error(`[FloTrace] ${name}:`, error);
580
580
  }
581
581
  }
582
+ function deriveWebAppName() {
583
+ if (typeof document !== "undefined") {
584
+ const metaName = document.querySelector('meta[name="application-name"]')?.getAttribute("content")?.trim();
585
+ if (metaName) return metaName;
586
+ const title = document.title?.trim();
587
+ if (title) return title;
588
+ }
589
+ if (typeof location !== "undefined" && location.hostname) return location.hostname;
590
+ return import_runtime_core3.DEFAULT_CONFIG.appName;
591
+ }
592
+ function deriveWebAppId() {
593
+ return typeof location !== "undefined" && location.origin ? location.origin : "web-app";
594
+ }
582
595
  var FloTraceContext = (0, import_react.createContext)(null);
583
596
  function useFloTrace() {
584
597
  return (0, import_react.useContext)(FloTraceContext);
@@ -590,13 +603,20 @@ function FloTraceProvider({ children, config = {}, stores, reduxStore, queryClie
590
603
  );
591
604
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children });
592
605
  }
606
+ const framework = (0, import_runtime_core3.detectWebFramework)();
593
607
  const mergedConfig = {
594
608
  ...import_runtime_core3.DEFAULT_CONFIG,
595
609
  // Web default: expose the current page URL as the `appUrl` in runtime:ready.
596
610
  // Runtime-core defaults this to undefined so it stays platform-agnostic.
597
611
  getAppUrl: () => typeof window !== "undefined" ? window.location.href : void 0,
598
612
  platform: "web",
599
- ...config
613
+ ...config,
614
+ // Derived values fill in only when the user didn't supply a static one.
615
+ // Placed AFTER `...config` so explicit user values still win via `??`.
616
+ appName: config.appName ?? deriveWebAppName(),
617
+ appId: config.appId ?? deriveWebAppId(),
618
+ frameworkName: config.frameworkName ?? framework.frameworkName,
619
+ frameworkVersion: config.frameworkVersion ?? framework.frameworkVersion
600
620
  };
601
621
  const [connected, setConnected] = import_react.default.useState(false);
602
622
  const trackingOptionsRef = (0, import_react.useRef)({});
@@ -631,8 +651,12 @@ function FloTraceProvider({ children, config = {}, stores, reduxStore, queryClie
631
651
  const unsubMessage = client3.onMessage((message) => {
632
652
  try {
633
653
  switch (message.type) {
654
+ // Heartbeat liveness is handled by the dedicated `runtime:pong` path
655
+ // in websocketClient. We intentionally do NOT re-send `runtime:ready`
656
+ // on every `ext:ping` — a truncated ready (only appName, no appId /
657
+ // platform / versions) would clobber the server's client registry
658
+ // metadata on every 5s tick. The initial `onopen` ready is authoritative.
634
659
  case "ext:ping":
635
- client3.sendImmediate({ type: "runtime:ready", appName: mergedConfig.appName });
636
660
  break;
637
661
  case "ext:startTracking":
638
662
  trackingOptionsRef.current = message.options || {};
@@ -730,6 +754,37 @@ function FloTraceProvider({ children, config = {}, stores, reduxStore, queryClie
730
754
  }
731
755
  break;
732
756
  }
757
+ // Value Lineage — resolve the origin chain for a prop or hook value.
758
+ case "ext:traceValue": {
759
+ try {
760
+ const trace = (0, import_runtime_core3.resolveValueTrace)({
761
+ nodeId: message.nodeId,
762
+ propPath: message.propPath,
763
+ hookPath: message.hookPath
764
+ });
765
+ client3.sendImmediate({
766
+ type: "runtime:valueTrace",
767
+ trace: { requestId: message.requestId, ...trace },
768
+ timestamp: Date.now()
769
+ });
770
+ } catch (error) {
771
+ console.error("[FloTrace] resolveValueTrace threw:", error);
772
+ client3.sendImmediate({
773
+ type: "runtime:valueTrace",
774
+ trace: {
775
+ requestId: message.requestId,
776
+ rootNodeId: message.nodeId,
777
+ rootPropPath: message.propPath,
778
+ rootHookPath: message.hookPath,
779
+ steps: [],
780
+ resolvedAtMs: Date.now(),
781
+ error: "value-not-found"
782
+ },
783
+ timestamp: Date.now()
784
+ });
785
+ }
786
+ break;
787
+ }
733
788
  case "ext:startNetworkCapture":
734
789
  safeTrackerOp("Network capture start", () => installNetworkTracker(client3));
735
790
  break;
package/dist/index.mjs CHANGED
@@ -25,7 +25,9 @@ import {
25
25
  uninstallTanStackQueryTracker,
26
26
  installTimelineTracker,
27
27
  uninstallTimelineTracker,
28
- getTimeline
28
+ getTimeline,
29
+ detectWebFramework,
30
+ resolveValueTrace
29
31
  } from "@flotrace/runtime-core";
30
32
 
31
33
  // src/routerTracker.ts
@@ -566,6 +568,19 @@ function safeTrackerOp(name, op) {
566
568
  console.error(`[FloTrace] ${name}:`, error);
567
569
  }
568
570
  }
571
+ function deriveWebAppName() {
572
+ if (typeof document !== "undefined") {
573
+ const metaName = document.querySelector('meta[name="application-name"]')?.getAttribute("content")?.trim();
574
+ if (metaName) return metaName;
575
+ const title = document.title?.trim();
576
+ if (title) return title;
577
+ }
578
+ if (typeof location !== "undefined" && location.hostname) return location.hostname;
579
+ return DEFAULT_CONFIG.appName;
580
+ }
581
+ function deriveWebAppId() {
582
+ return typeof location !== "undefined" && location.origin ? location.origin : "web-app";
583
+ }
569
584
  var FloTraceContext = createContext(null);
570
585
  function useFloTrace() {
571
586
  return useContext(FloTraceContext);
@@ -577,13 +592,20 @@ function FloTraceProvider({ children, config = {}, stores, reduxStore, queryClie
577
592
  );
578
593
  return /* @__PURE__ */ jsx(Fragment, { children });
579
594
  }
595
+ const framework = detectWebFramework();
580
596
  const mergedConfig = {
581
597
  ...DEFAULT_CONFIG,
582
598
  // Web default: expose the current page URL as the `appUrl` in runtime:ready.
583
599
  // Runtime-core defaults this to undefined so it stays platform-agnostic.
584
600
  getAppUrl: () => typeof window !== "undefined" ? window.location.href : void 0,
585
601
  platform: "web",
586
- ...config
602
+ ...config,
603
+ // Derived values fill in only when the user didn't supply a static one.
604
+ // Placed AFTER `...config` so explicit user values still win via `??`.
605
+ appName: config.appName ?? deriveWebAppName(),
606
+ appId: config.appId ?? deriveWebAppId(),
607
+ frameworkName: config.frameworkName ?? framework.frameworkName,
608
+ frameworkVersion: config.frameworkVersion ?? framework.frameworkVersion
587
609
  };
588
610
  const [connected, setConnected] = React.useState(false);
589
611
  const trackingOptionsRef = useRef({});
@@ -618,8 +640,12 @@ function FloTraceProvider({ children, config = {}, stores, reduxStore, queryClie
618
640
  const unsubMessage = client3.onMessage((message) => {
619
641
  try {
620
642
  switch (message.type) {
643
+ // Heartbeat liveness is handled by the dedicated `runtime:pong` path
644
+ // in websocketClient. We intentionally do NOT re-send `runtime:ready`
645
+ // on every `ext:ping` — a truncated ready (only appName, no appId /
646
+ // platform / versions) would clobber the server's client registry
647
+ // metadata on every 5s tick. The initial `onopen` ready is authoritative.
621
648
  case "ext:ping":
622
- client3.sendImmediate({ type: "runtime:ready", appName: mergedConfig.appName });
623
649
  break;
624
650
  case "ext:startTracking":
625
651
  trackingOptionsRef.current = message.options || {};
@@ -717,6 +743,37 @@ function FloTraceProvider({ children, config = {}, stores, reduxStore, queryClie
717
743
  }
718
744
  break;
719
745
  }
746
+ // Value Lineage — resolve the origin chain for a prop or hook value.
747
+ case "ext:traceValue": {
748
+ try {
749
+ const trace = resolveValueTrace({
750
+ nodeId: message.nodeId,
751
+ propPath: message.propPath,
752
+ hookPath: message.hookPath
753
+ });
754
+ client3.sendImmediate({
755
+ type: "runtime:valueTrace",
756
+ trace: { requestId: message.requestId, ...trace },
757
+ timestamp: Date.now()
758
+ });
759
+ } catch (error) {
760
+ console.error("[FloTrace] resolveValueTrace threw:", error);
761
+ client3.sendImmediate({
762
+ type: "runtime:valueTrace",
763
+ trace: {
764
+ requestId: message.requestId,
765
+ rootNodeId: message.nodeId,
766
+ rootPropPath: message.propPath,
767
+ rootHookPath: message.hookPath,
768
+ steps: [],
769
+ resolvedAtMs: Date.now(),
770
+ error: "value-not-found"
771
+ },
772
+ timestamp: Date.now()
773
+ });
774
+ }
775
+ break;
776
+ }
720
777
  case "ext:startNetworkCapture":
721
778
  safeTrackerOp("Network capture start", () => installNetworkTracker(client3));
722
779
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flotrace/runtime",
3
- "version": "2.0.0",
3
+ "version": "2.2.1",
4
4
  "description": "Runtime package for FloTrace - enables real-time render tracking in your React app",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -26,7 +26,7 @@
26
26
  "release:major": "npm version major && npm publish"
27
27
  },
28
28
  "dependencies": {
29
- "@flotrace/runtime-core": "2.0.0"
29
+ "@flotrace/runtime-core": "2.2.1"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "react": ">=16.9.0",