@flotrace/runtime-core 2.0.0 → 2.0.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
@@ -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';
@@ -736,10 +745,18 @@ interface FloTraceConfig {
736
745
  * be hidden by strict mode (e.g. monorepo packages resolved into
737
746
  * `node_modules/@workspace/ui`). Only consulted when `userOnlyStrict` is on. */
738
747
  userAllowPatterns?: RegExp[];
748
+ /** Auto-detected framework family. Adapters set this via platform-specific probes
749
+ * (DOM signals on web; optional-require on native). Not a user-facing knob. */
750
+ frameworkName?: 'next' | 'expo' | 'rn-cli' | 'plain-react';
751
+ /** Framework version when the adapter can resolve it. Not a user-facing knob. */
752
+ frameworkVersion?: string;
753
+ /** React Native version from `Platform.constants.reactNativeVersion`, formatted
754
+ * "major.minor.patch". Native adapter only. */
755
+ reactNativeVersion?: string;
739
756
  }
740
757
  /** Keys that stay optional in DEFAULT_CONFIG. These are populated by adapters (web/native)
741
758
  * 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';
759
+ type OptionalConfigKeys = 'getAppUrl' | 'platform' | 'appId' | 'appVersion' | 'host' | 'authToken' | 'userOnlyStrict' | 'userAllowPatterns' | 'frameworkName' | 'frameworkVersion' | 'reactNativeVersion';
743
760
  type ResolvedFloTraceConfig = Required<Omit<FloTraceConfig, OptionalConfigKeys>> & Pick<FloTraceConfig, OptionalConfigKeys>;
744
761
  /**
745
762
  * Default configuration
@@ -1157,7 +1174,13 @@ declare class FloTraceWebSocketClient {
1157
1174
  */
1158
1175
  get connected(): boolean;
1159
1176
  /**
1160
- * Get React version if available
1177
+ * Get React version if available.
1178
+ *
1179
+ * Historical note: an earlier implementation read `globalThis.React?.version` —
1180
+ * but React is an ES-module import in modern bundles (Vite/webpack/Next.js) and
1181
+ * is never placed on `globalThis`, so the probe returned undefined for every
1182
+ * typical bundled app. Reading `React.version` via a direct import is
1183
+ * authoritative across web (both CJS and ESM bundles), React Native, and SSR.
1161
1184
  */
1162
1185
  private getReactVersion;
1163
1186
  }
@@ -1445,6 +1468,45 @@ declare function detectServerComponent(fiber: Fiber): boolean;
1445
1468
  /** Reset detection state (useful for tests / hot-reload) */
1446
1469
  declare function resetNextjsDetection(): void;
1447
1470
 
1471
+ /**
1472
+ * Framework + framework-version detection (web adapter).
1473
+ *
1474
+ * Detects whether the running page is Next.js vs plain React and attempts to
1475
+ * resolve the Next.js version via the package's own `package.json`. All signals
1476
+ * are probed at runtime; absence is non-fatal and simply returns `undefined`.
1477
+ *
1478
+ * Node version of the project is intentionally NOT captured — `process.version`
1479
+ * is unavailable in browsers, and surfacing it would require user bundler
1480
+ * config (Vite `define`, Next.js `env`), which breaks the zero-config contract.
1481
+ */
1482
+ interface FrameworkInfo {
1483
+ frameworkName?: 'next' | 'expo' | 'rn-cli' | 'plain-react';
1484
+ frameworkVersion?: string;
1485
+ }
1486
+ /**
1487
+ * Detect the web framework hosting this page and, when possible, its version.
1488
+ *
1489
+ * - Next.js: presence detected via `window.__NEXT_DATA__` (App Router + Pages
1490
+ * Router both set it) or the `<script id="__NEXT_DATA__">` element.
1491
+ * - Otherwise: reports `plain-react` (covers Vite, CRA, Remix, and anything else
1492
+ * not positively identified as Next.js).
1493
+ *
1494
+ * Returns `{}` in non-DOM contexts (SSR on Node, first render of a Client
1495
+ * Component). `FloTraceProvider` only creates the WebSocket singleton on the
1496
+ * client, so the empty-object branch is harmless — detection re-runs after
1497
+ * hydration with a DOM available.
1498
+ *
1499
+ * **About `frameworkVersion`**: in a Next.js *client* bundle, `globalThis.require`
1500
+ * is normally not defined — so the version probe below usually no-ops and we
1501
+ * report `frameworkName: 'next'` with `frameworkVersion: undefined`. This is
1502
+ * by design: version detection is best-effort, the name alone is enough for
1503
+ * admin-side categorization, and the string-concat require trick exists
1504
+ * primarily to keep non-Next bundlers from failing the build on a static
1505
+ * `require('next/package.json')` they can't resolve. Framework *detection*
1506
+ * works reliably via `__NEXT_DATA__` regardless.
1507
+ */
1508
+ declare function detectWebFramework(): FrameworkInfo;
1509
+
1448
1510
  /**
1449
1511
  * RSC payload fetch interceptor for Next.js App Router.
1450
1512
  * Patches globalThis.fetch to detect Next.js data / RSC fetch requests,
@@ -1462,4 +1524,4 @@ declare function installRscPayloadInterceptor(client: FloTraceWebSocketClient):
1462
1524
  /** Remove the RSC payload interceptor and restore original fetch */
1463
1525
  declare function uninstallRscPayloadInterceptor(): void;
1464
1526
 
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 };
1527
+ 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 SerializedValue, type TanStackMutationInfo, type TanStackQueryClientApi, type TanStackQueryEvent, type TanStackQueryInfo, type TimelineEvent, type TimelineEventType, type TrackingOptions, type ZustandStoreApi, buildAncestorChain, clearFetchOriginTags, detectServerComponent, detectWebFramework, 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 };
package/dist/index.d.ts CHANGED
@@ -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';
@@ -736,10 +745,18 @@ interface FloTraceConfig {
736
745
  * be hidden by strict mode (e.g. monorepo packages resolved into
737
746
  * `node_modules/@workspace/ui`). Only consulted when `userOnlyStrict` is on. */
738
747
  userAllowPatterns?: RegExp[];
748
+ /** Auto-detected framework family. Adapters set this via platform-specific probes
749
+ * (DOM signals on web; optional-require on native). Not a user-facing knob. */
750
+ frameworkName?: 'next' | 'expo' | 'rn-cli' | 'plain-react';
751
+ /** Framework version when the adapter can resolve it. Not a user-facing knob. */
752
+ frameworkVersion?: string;
753
+ /** React Native version from `Platform.constants.reactNativeVersion`, formatted
754
+ * "major.minor.patch". Native adapter only. */
755
+ reactNativeVersion?: string;
739
756
  }
740
757
  /** Keys that stay optional in DEFAULT_CONFIG. These are populated by adapters (web/native)
741
758
  * 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';
759
+ type OptionalConfigKeys = 'getAppUrl' | 'platform' | 'appId' | 'appVersion' | 'host' | 'authToken' | 'userOnlyStrict' | 'userAllowPatterns' | 'frameworkName' | 'frameworkVersion' | 'reactNativeVersion';
743
760
  type ResolvedFloTraceConfig = Required<Omit<FloTraceConfig, OptionalConfigKeys>> & Pick<FloTraceConfig, OptionalConfigKeys>;
744
761
  /**
745
762
  * Default configuration
@@ -1157,7 +1174,13 @@ declare class FloTraceWebSocketClient {
1157
1174
  */
1158
1175
  get connected(): boolean;
1159
1176
  /**
1160
- * Get React version if available
1177
+ * Get React version if available.
1178
+ *
1179
+ * Historical note: an earlier implementation read `globalThis.React?.version` —
1180
+ * but React is an ES-module import in modern bundles (Vite/webpack/Next.js) and
1181
+ * is never placed on `globalThis`, so the probe returned undefined for every
1182
+ * typical bundled app. Reading `React.version` via a direct import is
1183
+ * authoritative across web (both CJS and ESM bundles), React Native, and SSR.
1161
1184
  */
1162
1185
  private getReactVersion;
1163
1186
  }
@@ -1445,6 +1468,45 @@ declare function detectServerComponent(fiber: Fiber): boolean;
1445
1468
  /** Reset detection state (useful for tests / hot-reload) */
1446
1469
  declare function resetNextjsDetection(): void;
1447
1470
 
1471
+ /**
1472
+ * Framework + framework-version detection (web adapter).
1473
+ *
1474
+ * Detects whether the running page is Next.js vs plain React and attempts to
1475
+ * resolve the Next.js version via the package's own `package.json`. All signals
1476
+ * are probed at runtime; absence is non-fatal and simply returns `undefined`.
1477
+ *
1478
+ * Node version of the project is intentionally NOT captured — `process.version`
1479
+ * is unavailable in browsers, and surfacing it would require user bundler
1480
+ * config (Vite `define`, Next.js `env`), which breaks the zero-config contract.
1481
+ */
1482
+ interface FrameworkInfo {
1483
+ frameworkName?: 'next' | 'expo' | 'rn-cli' | 'plain-react';
1484
+ frameworkVersion?: string;
1485
+ }
1486
+ /**
1487
+ * Detect the web framework hosting this page and, when possible, its version.
1488
+ *
1489
+ * - Next.js: presence detected via `window.__NEXT_DATA__` (App Router + Pages
1490
+ * Router both set it) or the `<script id="__NEXT_DATA__">` element.
1491
+ * - Otherwise: reports `plain-react` (covers Vite, CRA, Remix, and anything else
1492
+ * not positively identified as Next.js).
1493
+ *
1494
+ * Returns `{}` in non-DOM contexts (SSR on Node, first render of a Client
1495
+ * Component). `FloTraceProvider` only creates the WebSocket singleton on the
1496
+ * client, so the empty-object branch is harmless — detection re-runs after
1497
+ * hydration with a DOM available.
1498
+ *
1499
+ * **About `frameworkVersion`**: in a Next.js *client* bundle, `globalThis.require`
1500
+ * is normally not defined — so the version probe below usually no-ops and we
1501
+ * report `frameworkName: 'next'` with `frameworkVersion: undefined`. This is
1502
+ * by design: version detection is best-effort, the name alone is enough for
1503
+ * admin-side categorization, and the string-concat require trick exists
1504
+ * primarily to keep non-Next bundlers from failing the build on a static
1505
+ * `require('next/package.json')` they can't resolve. Framework *detection*
1506
+ * works reliably via `__NEXT_DATA__` regardless.
1507
+ */
1508
+ declare function detectWebFramework(): FrameworkInfo;
1509
+
1448
1510
  /**
1449
1511
  * RSC payload fetch interceptor for Next.js App Router.
1450
1512
  * Patches globalThis.fetch to detect Next.js data / RSC fetch requests,
@@ -1462,4 +1524,4 @@ declare function installRscPayloadInterceptor(client: FloTraceWebSocketClient):
1462
1524
  /** Remove the RSC payload interceptor and restore original fetch */
1463
1525
  declare function uninstallRscPayloadInterceptor(): void;
1464
1526
 
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 };
1527
+ 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 SerializedValue, type TanStackMutationInfo, type TanStackQueryClientApi, type TanStackQueryEvent, type TanStackQueryInfo, type TimelineEvent, type TimelineEventType, type TrackingOptions, type ZustandStoreApi, buildAncestorChain, clearFetchOriginTags, detectServerComponent, detectWebFramework, 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 };
package/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
@@ -25,6 +35,7 @@ __export(index_exports, {
25
35
  buildAncestorChain: () => buildAncestorChain,
26
36
  clearFetchOriginTags: () => clearFetchOriginTags,
27
37
  detectServerComponent: () => detectServerComponent,
38
+ detectWebFramework: () => detectWebFramework,
28
39
  disposeWebSocketClient: () => disposeWebSocketClient,
29
40
  findFetchOrigin: () => findFetchOrigin,
30
41
  getChangedKeys: () => getChangedKeys,
@@ -243,6 +254,7 @@ function getChangedKeys(prev, next) {
243
254
  }
244
255
 
245
256
  // src/websocketClient.ts
257
+ var import_react = __toESM(require("react"));
246
258
  var _FloTraceWebSocketClient = class _FloTraceWebSocketClient {
247
259
  constructor(config = {}) {
248
260
  this.ws = null;
@@ -290,7 +302,10 @@ var _FloTraceWebSocketClient = class _FloTraceWebSocketClient {
290
302
  appUrl: this.config.getAppUrl?.(),
291
303
  platform: this.config.platform,
292
304
  appId: this.config.appId,
293
- appVersion: this.config.appVersion
305
+ appVersion: this.config.appVersion,
306
+ frameworkName: this.config.frameworkName,
307
+ frameworkVersion: this.config.frameworkVersion,
308
+ reactNativeVersion: this.config.reactNativeVersion
294
309
  });
295
310
  this.flush();
296
311
  };
@@ -474,12 +489,17 @@ var _FloTraceWebSocketClient = class _FloTraceWebSocketClient {
474
489
  return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
475
490
  }
476
491
  /**
477
- * Get React version if available
492
+ * Get React version if available.
493
+ *
494
+ * Historical note: an earlier implementation read `globalThis.React?.version` —
495
+ * but React is an ES-module import in modern bundles (Vite/webpack/Next.js) and
496
+ * is never placed on `globalThis`, so the probe returned undefined for every
497
+ * typical bundled app. Reading `React.version` via a direct import is
498
+ * authoritative across web (both CJS and ESM bundles), React Native, and SSR.
478
499
  */
479
500
  getReactVersion() {
480
501
  try {
481
- const React = globalThis.React;
482
- return React?.version;
502
+ return import_react.default.version;
483
503
  } catch {
484
504
  return void 0;
485
505
  }
@@ -3638,6 +3658,24 @@ function safeCall(fn, fallback) {
3638
3658
  return fallback;
3639
3659
  }
3640
3660
  }
3661
+
3662
+ // src/frameworkDetect.ts
3663
+ function detectWebFramework() {
3664
+ if (typeof document === "undefined") return {};
3665
+ const hasNextData = !!globalThis.__NEXT_DATA__ || !!document.querySelector("script#__NEXT_DATA__");
3666
+ if (!hasNextData) return { frameworkName: "plain-react" };
3667
+ let version;
3668
+ try {
3669
+ const req = globalThis.require;
3670
+ if (typeof req === "function") {
3671
+ const id = "next/package.json";
3672
+ const pkg = req(id);
3673
+ version = pkg?.version;
3674
+ }
3675
+ } catch {
3676
+ }
3677
+ return { frameworkName: "next", frameworkVersion: version };
3678
+ }
3641
3679
  // Annotate the CommonJS export names for ESM import in node:
3642
3680
  0 && (module.exports = {
3643
3681
  DEFAULT_CONFIG,
@@ -3645,6 +3683,7 @@ function safeCall(fn, fallback) {
3645
3683
  buildAncestorChain,
3646
3684
  clearFetchOriginTags,
3647
3685
  detectServerComponent,
3686
+ detectWebFramework,
3648
3687
  disposeWebSocketClient,
3649
3688
  findFetchOrigin,
3650
3689
  getChangedKeys,
package/dist/index.mjs CHANGED
@@ -176,6 +176,7 @@ function getChangedKeys(prev, next) {
176
176
  }
177
177
 
178
178
  // src/websocketClient.ts
179
+ import React from "react";
179
180
  var _FloTraceWebSocketClient = class _FloTraceWebSocketClient {
180
181
  constructor(config = {}) {
181
182
  this.ws = null;
@@ -223,7 +224,10 @@ var _FloTraceWebSocketClient = class _FloTraceWebSocketClient {
223
224
  appUrl: this.config.getAppUrl?.(),
224
225
  platform: this.config.platform,
225
226
  appId: this.config.appId,
226
- appVersion: this.config.appVersion
227
+ appVersion: this.config.appVersion,
228
+ frameworkName: this.config.frameworkName,
229
+ frameworkVersion: this.config.frameworkVersion,
230
+ reactNativeVersion: this.config.reactNativeVersion
227
231
  });
228
232
  this.flush();
229
233
  };
@@ -407,12 +411,17 @@ var _FloTraceWebSocketClient = class _FloTraceWebSocketClient {
407
411
  return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
408
412
  }
409
413
  /**
410
- * Get React version if available
414
+ * Get React version if available.
415
+ *
416
+ * Historical note: an earlier implementation read `globalThis.React?.version` —
417
+ * but React is an ES-module import in modern bundles (Vite/webpack/Next.js) and
418
+ * is never placed on `globalThis`, so the probe returned undefined for every
419
+ * typical bundled app. Reading `React.version` via a direct import is
420
+ * authoritative across web (both CJS and ESM bundles), React Native, and SSR.
411
421
  */
412
422
  getReactVersion() {
413
423
  try {
414
- const React = globalThis.React;
415
- return React?.version;
424
+ return React.version;
416
425
  } catch {
417
426
  return void 0;
418
427
  }
@@ -3571,12 +3580,31 @@ function safeCall(fn, fallback) {
3571
3580
  return fallback;
3572
3581
  }
3573
3582
  }
3583
+
3584
+ // src/frameworkDetect.ts
3585
+ function detectWebFramework() {
3586
+ if (typeof document === "undefined") return {};
3587
+ const hasNextData = !!globalThis.__NEXT_DATA__ || !!document.querySelector("script#__NEXT_DATA__");
3588
+ if (!hasNextData) return { frameworkName: "plain-react" };
3589
+ let version;
3590
+ try {
3591
+ const req = globalThis.require;
3592
+ if (typeof req === "function") {
3593
+ const id = "next/package.json";
3594
+ const pkg = req(id);
3595
+ version = pkg?.version;
3596
+ }
3597
+ } catch {
3598
+ }
3599
+ return { frameworkName: "next", frameworkVersion: version };
3600
+ }
3574
3601
  export {
3575
3602
  DEFAULT_CONFIG,
3576
3603
  FloTraceWebSocketClient,
3577
3604
  buildAncestorChain,
3578
3605
  clearFetchOriginTags,
3579
3606
  detectServerComponent,
3607
+ detectWebFramework,
3580
3608
  disposeWebSocketClient,
3581
3609
  findFetchOrigin,
3582
3610
  getChangedKeys,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flotrace/runtime-core",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "Platform-agnostic core for FloTrace runtime — fiber walker, analyzers, trackers. Shared by @flotrace/runtime (web) and @flotrace/runtime-native (React Native).",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",