@newrelic/browser-agent 1.310.1 → 1.311.0-rc.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.
Files changed (131) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/cjs/common/config/configurable.js +2 -2
  3. package/dist/cjs/common/config/runtime.js +8 -2
  4. package/dist/cjs/common/constants/env.cdn.js +1 -1
  5. package/dist/cjs/common/constants/env.npm.js +1 -1
  6. package/dist/cjs/common/constants/runtime.js +17 -4
  7. package/dist/cjs/common/deny-list/deny-list.js +2 -2
  8. package/dist/cjs/common/drain/drain.js +27 -37
  9. package/dist/cjs/common/harvest/harvester.js +1 -3
  10. package/dist/cjs/common/session/session-entity.js +7 -8
  11. package/dist/cjs/common/util/console.js +1 -2
  12. package/dist/cjs/common/util/feature-flags.js +5 -16
  13. package/dist/cjs/common/util/script-tracker.js +22 -4
  14. package/dist/cjs/common/util/stringify.js +22 -7
  15. package/dist/cjs/common/vitals/largest-contentful-paint.js +0 -1
  16. package/dist/cjs/common/vitals/load-time.js +3 -2
  17. package/dist/cjs/common/vitals/time-to-first-byte.js +2 -2
  18. package/dist/cjs/features/jserrors/aggregate/index.js +0 -4
  19. package/dist/cjs/features/page_view_event/aggregate/index.js +3 -3
  20. package/dist/cjs/features/page_view_event/aggregate/initialized-features.js +3 -5
  21. package/dist/cjs/features/page_view_event/instrument/index.js +3 -5
  22. package/dist/cjs/features/page_view_timing/aggregate/index.js +2 -0
  23. package/dist/cjs/features/session_trace/aggregate/index.js +4 -3
  24. package/dist/cjs/features/utils/agent-session.js +3 -4
  25. package/dist/cjs/features/utils/aggregate-base.js +4 -6
  26. package/dist/cjs/features/utils/feature-base.js +6 -7
  27. package/dist/cjs/features/utils/instrument-base.js +7 -8
  28. package/dist/cjs/loaders/api/register-api-types.js +1 -1
  29. package/dist/cjs/loaders/api/register.js +1 -1
  30. package/dist/cjs/loaders/api/sharedHandlers.js +2 -4
  31. package/dist/cjs/loaders/configure/configure.js +23 -21
  32. package/dist/esm/common/config/configurable.js +2 -2
  33. package/dist/esm/common/config/runtime.js +8 -2
  34. package/dist/esm/common/constants/env.cdn.js +1 -1
  35. package/dist/esm/common/constants/env.npm.js +1 -1
  36. package/dist/esm/common/constants/runtime.js +15 -2
  37. package/dist/esm/common/deny-list/deny-list.js +2 -2
  38. package/dist/esm/common/drain/drain.js +27 -36
  39. package/dist/esm/common/harvest/harvester.js +1 -3
  40. package/dist/esm/common/session/session-entity.js +7 -8
  41. package/dist/esm/common/util/console.js +1 -2
  42. package/dist/esm/common/util/feature-flags.js +5 -14
  43. package/dist/esm/common/util/script-tracker.js +22 -5
  44. package/dist/esm/common/util/stringify.js +22 -7
  45. package/dist/esm/common/vitals/largest-contentful-paint.js +0 -1
  46. package/dist/esm/common/vitals/load-time.js +4 -3
  47. package/dist/esm/common/vitals/time-to-first-byte.js +3 -3
  48. package/dist/esm/features/jserrors/aggregate/index.js +0 -4
  49. package/dist/esm/features/page_view_event/aggregate/index.js +4 -4
  50. package/dist/esm/features/page_view_event/aggregate/initialized-features.js +3 -5
  51. package/dist/esm/features/page_view_event/instrument/index.js +3 -5
  52. package/dist/esm/features/page_view_timing/aggregate/index.js +3 -1
  53. package/dist/esm/features/session_trace/aggregate/index.js +5 -4
  54. package/dist/esm/features/utils/agent-session.js +3 -4
  55. package/dist/esm/features/utils/aggregate-base.js +4 -6
  56. package/dist/esm/features/utils/feature-base.js +6 -7
  57. package/dist/esm/features/utils/instrument-base.js +7 -8
  58. package/dist/esm/loaders/api/register-api-types.js +1 -1
  59. package/dist/esm/loaders/api/register.js +1 -1
  60. package/dist/esm/loaders/api/sharedHandlers.js +2 -4
  61. package/dist/esm/loaders/configure/configure.js +24 -21
  62. package/dist/tsconfig.tsbuildinfo +1 -1
  63. package/dist/types/common/config/configurable.d.ts.map +1 -1
  64. package/dist/types/common/config/runtime.d.ts.map +1 -1
  65. package/dist/types/common/constants/runtime.d.ts +1 -1
  66. package/dist/types/common/constants/runtime.d.ts.map +1 -1
  67. package/dist/types/common/drain/drain.d.ts +6 -6
  68. package/dist/types/common/drain/drain.d.ts.map +1 -1
  69. package/dist/types/common/harvest/harvester.d.ts.map +1 -1
  70. package/dist/types/common/session/session-entity.d.ts +2 -2
  71. package/dist/types/common/session/session-entity.d.ts.map +1 -1
  72. package/dist/types/common/util/console.d.ts.map +1 -1
  73. package/dist/types/common/util/feature-flags.d.ts +3 -5
  74. package/dist/types/common/util/feature-flags.d.ts.map +1 -1
  75. package/dist/types/common/util/script-tracker.d.ts +5 -0
  76. package/dist/types/common/util/script-tracker.d.ts.map +1 -1
  77. package/dist/types/common/util/stringify.d.ts.map +1 -1
  78. package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
  79. package/dist/types/features/page_view_event/aggregate/initialized-features.d.ts +2 -2
  80. package/dist/types/features/page_view_event/aggregate/initialized-features.d.ts.map +1 -1
  81. package/dist/types/features/page_view_event/instrument/index.d.ts +1 -1
  82. package/dist/types/features/page_view_event/instrument/index.d.ts.map +1 -1
  83. package/dist/types/features/page_view_timing/aggregate/index.d.ts.map +1 -1
  84. package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
  85. package/dist/types/features/utils/agent-session.d.ts.map +1 -1
  86. package/dist/types/features/utils/aggregate-base.d.ts +0 -1
  87. package/dist/types/features/utils/aggregate-base.d.ts.map +1 -1
  88. package/dist/types/features/utils/feature-base.d.ts +3 -3
  89. package/dist/types/features/utils/feature-base.d.ts.map +1 -1
  90. package/dist/types/features/utils/instrument-base.d.ts +2 -3
  91. package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
  92. package/dist/types/loaders/api/register-api-types.d.ts +1 -1
  93. package/dist/types/loaders/api/register-api-types.d.ts.map +1 -1
  94. package/dist/types/loaders/api/sharedHandlers.d.ts.map +1 -1
  95. package/dist/types/loaders/configure/configure.d.ts.map +1 -1
  96. package/package.json +2 -2
  97. package/src/common/config/configurable.js +2 -1
  98. package/src/common/config/runtime.js +8 -2
  99. package/src/common/constants/runtime.js +16 -2
  100. package/src/common/deny-list/deny-list.js +2 -2
  101. package/src/common/drain/__mocks__/drain.js +2 -1
  102. package/src/common/drain/drain.js +27 -37
  103. package/src/common/harvest/harvester.js +1 -3
  104. package/src/common/session/session-entity.js +7 -8
  105. package/src/common/util/console.js +1 -2
  106. package/src/common/util/feature-flags.js +5 -17
  107. package/src/common/util/script-tracker.js +23 -5
  108. package/src/common/util/stringify.js +24 -7
  109. package/src/common/vitals/largest-contentful-paint.js +0 -1
  110. package/src/common/vitals/load-time.js +4 -3
  111. package/src/common/vitals/time-to-first-byte.js +3 -3
  112. package/src/features/jserrors/aggregate/index.js +0 -4
  113. package/src/features/page_view_event/aggregate/index.js +4 -4
  114. package/src/features/page_view_event/aggregate/initialized-features.js +3 -5
  115. package/src/features/page_view_event/instrument/index.js +3 -5
  116. package/src/features/page_view_timing/aggregate/index.js +4 -1
  117. package/src/features/session_trace/aggregate/index.js +5 -4
  118. package/src/features/utils/__mocks__/feature-base.js +3 -3
  119. package/src/features/utils/agent-session.js +3 -4
  120. package/src/features/utils/aggregate-base.js +4 -6
  121. package/src/features/utils/feature-base.js +6 -7
  122. package/src/features/utils/instrument-base.js +7 -9
  123. package/src/loaders/api/register-api-types.js +1 -1
  124. package/src/loaders/api/register.js +1 -1
  125. package/src/loaders/api/sharedHandlers.js +2 -4
  126. package/src/loaders/configure/configure.js +30 -26
  127. package/dist/cjs/features/utils/nr1-debugger.js +0 -30
  128. package/dist/esm/features/utils/nr1-debugger.js +0 -23
  129. package/dist/types/features/utils/nr1-debugger.d.ts +0 -2
  130. package/dist/types/features/utils/nr1-debugger.d.ts.map +0 -1
  131. package/src/features/utils/nr1-debugger.js +0 -26
package/CHANGELOG.md CHANGED
@@ -3,6 +3,22 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [1.311.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.310.1...v1.311.0) (2026-03-20)
7
+
8
+
9
+ ### Features
10
+
11
+ * agentIdentifier removal ([#1712](https://github.com/newrelic/newrelic-browser-agent/issues/1712)) ([d9f6711](https://github.com/newrelic/newrelic-browser-agent/commit/d9f6711e4f1073b1e7713fe7966721fd6b31b864))
12
+ * Attribute original page URL to all PageViewTiming nodes ([#1716](https://github.com/newrelic/newrelic-browser-agent/issues/1716)) ([847b6a7](https://github.com/newrelic/newrelic-browser-agent/commit/847b6a72650a113fdf2f074a6a057ac601aafb02))
13
+ * improve MFE timing logic ([#1714](https://github.com/newrelic/newrelic-browser-agent/issues/1714)) ([9a3ef06](https://github.com/newrelic/newrelic-browser-agent/commit/9a3ef06652a36a05b950f9154969a767fa3b731e))
14
+
15
+
16
+ ### Bug Fixes
17
+
18
+ * Circular replacer removed reused objects from harvests ([#1718](https://github.com/newrelic/newrelic-browser-agent/issues/1718)) ([76024d0](https://github.com/newrelic/newrelic-browser-agent/commit/76024d0f3465db42ddaa297d0f87a9e21f2f9b03))
19
+ * Enforce string IDs for new MFE registrations ([#1725](https://github.com/newrelic/newrelic-browser-agent/issues/1725)) ([38fae07](https://github.com/newrelic/newrelic-browser-agent/commit/38fae0758645c5f0cd26f7067be973f7ee807819))
20
+ * Update RegExp to prevent un-minimized webpack build issue ([#1711](https://github.com/newrelic/newrelic-browser-agent/issues/1711)) ([1a2408f](https://github.com/newrelic/newrelic-browser-agent/commit/1a2408f15c8ae96396cf3597058a5d18fa5cb4fd))
21
+
6
22
  ## [1.310.1](https://github.com/newrelic/newrelic-browser-agent/compare/v1.310.0...v1.310.1) (2026-02-19)
7
23
 
8
24
 
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.getModeledObject = getModeledObject;
7
7
  var _console = require("../util/console");
8
8
  /**
9
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
9
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
10
10
  * SPDX-License-Identifier: Apache-2.0
11
11
  */
12
12
 
@@ -24,7 +24,7 @@ function getModeledObject(obj, model) {
24
24
  output[key] = null;
25
25
  continue;
26
26
  }
27
- if (Array.isArray(obj[key]) && Array.isArray(model[key])) output[key] = Array.from(new Set([...obj[key], ...model[key]]));else if (typeof obj[key] === 'object' && typeof model[key] === 'object') output[key] = getModeledObject(obj[key], model[key]);else output[key] = obj[key];
27
+ if (Array.isArray(obj[key]) && Array.isArray(model[key])) output[key] = Array.from(new Set([...obj[key], ...model[key]]));else if (obj[key] instanceof Map || obj[key] instanceof Set || obj[key] instanceof Date || obj[key] instanceof RegExp) output[key] = obj[key];else if (typeof obj[key] === 'object' && typeof model[key] === 'object') output[key] = getModeledObject(obj[key], model[key]);else output[key] = obj[key];
28
28
  } catch (e) {
29
29
  if (!output[key]) (0, _console.warn)(1, e);
30
30
  }
@@ -8,7 +8,7 @@ var _configurable = require("./configurable");
8
8
  var _runtime = require("../constants/runtime");
9
9
  var _env = require("../constants/env.npm");
10
10
  /**
11
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
11
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
12
12
  * SPDX-License-Identifier: Apache-2.0
13
13
  */
14
14
 
@@ -27,8 +27,12 @@ const hiddenState = {
27
27
  consented: false
28
28
  };
29
29
  const RuntimeModel = {
30
+ /** @type {{[key: string]: number} | undefined} */
31
+ activatedFeatures: undefined,
30
32
  /** Agent-specific metadata found in the RUM call response. ex. entityGuid */
31
33
  appMetadata: {},
34
+ /** @type {boolean} */
35
+ configured: false,
32
36
  get consented() {
33
37
  return this.session?.state?.consent || hiddenState.consented;
34
38
  },
@@ -36,8 +40,10 @@ const RuntimeModel = {
36
40
  hiddenState.consented = value;
37
41
  },
38
42
  customTransaction: undefined,
39
- denyList: undefined,
43
+ denyList: [],
40
44
  disabled: false,
45
+ /** @type {Map<string, {staged: boolean, priority: number}>} */
46
+ drainRegistry: new Map(),
41
47
  harvester: undefined,
42
48
  isolatedBacklog: false,
43
49
  isRecording: false,
@@ -17,7 +17,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.RRWEB_PACKAGE_NAME = exports.D
17
17
  /**
18
18
  * Exposes the version of the agent
19
19
  */
20
- const VERSION = exports.VERSION = "1.310.1";
20
+ const VERSION = exports.VERSION = "1.311.0-rc.1";
21
21
 
22
22
  /**
23
23
  * Exposes the build type of the agent
@@ -17,7 +17,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.RRWEB_PACKAGE_NAME = exports.D
17
17
  /**
18
18
  * Exposes the version of the agent
19
19
  */
20
- const VERSION = exports.VERSION = "1.310.1";
20
+ const VERSION = exports.VERSION = "1.311.0-rc.1";
21
21
 
22
22
  /**
23
23
  * Exposes the build type of the agent
@@ -3,10 +3,10 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.supportsNavTimingL2 = exports.originTime = exports.loadedAsDeferredBrowserScript = exports.isiOS = exports.isWorkerScope = exports.isBrowserScope = exports.initiallyHidden = exports.initialLocation = exports.iOSBelow16 = exports.globalScope = exports.ffVersion = void 0;
6
+ exports.originTime = exports.loadedAsDeferredBrowserScript = exports.isiOS = exports.isWorkerScope = exports.isBrowserScope = exports.initiallyHidden = exports.initialLocation = exports.iOSBelow16 = exports.globalScope = exports.getNavigationEntry = exports.ffVersion = void 0;
7
7
  var _now = require("../timing/now");
8
8
  /**
9
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
9
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
10
10
  * SPDX-License-Identifier: Apache-2.0
11
11
  */
12
12
 
@@ -54,5 +54,18 @@ const ffVersion = exports.ffVersion = (() => {
54
54
  * @type {number}
55
55
  */
56
56
  const originTime = exports.originTime = Date.now() - (0, _now.now)();
57
- const supportsNavTimingL2 = () => typeof PerformanceNavigationTiming !== 'undefined' && globalScope?.performance?.getEntriesByType('navigation')?.[0]?.responseStart;
58
- exports.supportsNavTimingL2 = supportsNavTimingL2;
57
+
58
+ /**
59
+ * Gets the first navigation entry from the Performance Timeline API.
60
+ * Returns undefined if the entry is not available or invalid.
61
+ * Matches web-vitals validation: checks that responseStart exists, is positive, and is not larger than current time.
62
+ * See: https://github.com/GoogleChrome/web-vitals/issues/137
63
+ * @returns {PerformanceNavigationTiming | undefined}
64
+ */
65
+ const getNavigationEntry = () => {
66
+ const navigationEntry = globalScope?.performance?.getEntriesByType?.('navigation')?.[0];
67
+ if (navigationEntry && navigationEntry.responseStart > 0 && navigationEntry.responseStart < globalScope.performance.now()) {
68
+ return navigationEntry;
69
+ }
70
+ };
71
+ exports.getNavigationEntry = getNavigationEntry;
@@ -7,7 +7,7 @@ exports.hasUndefinedHostname = hasUndefinedHostname;
7
7
  exports.setDenyList = setDenyList;
8
8
  exports.shouldCollectEvent = shouldCollectEvent;
9
9
  /**
10
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
10
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
11
11
  * SPDX-License-Identifier: Apache-2.0
12
12
  */
13
13
 
@@ -92,5 +92,5 @@ function setDenyList(denyListConfig) {
92
92
  function convertToRegularExpression(filter, isPathname = false) {
93
93
  const newFilter = filter.replace(/[.+?^${}()|[\]\\]/g, m => '\\' + m) // use a replacer function to not break apm injection
94
94
  .replace(/\*/g, '.*?'); // use lazy matching instead of greedy
95
- return new RegExp((isPathname ? '^' : '') + newFilter + '$');
95
+ return new RegExp((isPathname ? '^' : '') + newFilter + '\x24'); // x24 == $, but using a literal $ causes webpack to to break when building unminified
96
96
  }
@@ -7,7 +7,6 @@ exports.deregisterDrain = deregisterDrain;
7
7
  exports.drain = drain;
8
8
  exports.registerDrain = registerDrain;
9
9
  var _globalEvent = require("../../common/dispatch/global-event");
10
- var _contextualEe = require("../event-emitter/contextual-ee");
11
10
  var _registerHandler = require("../event-emitter/register-handler");
12
11
  var _features = require("../../loaders/features/features");
13
12
  var _eventContext = require("../event-emitter/event-context");
@@ -16,78 +15,69 @@ var _eventContext = require("../event-emitter/event-context");
16
15
  * SPDX-License-Identifier: Apache-2.0
17
16
  */
18
17
 
19
- const registry = {};
20
-
21
18
  /**
22
19
  * Adds an entry to the centralized drain registry specifying that a particular agent has events of a particular named
23
20
  * event-group "bucket" that should be drained at the time the agent drains all its buffered events. Buffered events
24
21
  * accumulate because instrumentation begins as soon as possible, before the agent has finished lazy-loading the code
25
22
  * responsible for aggregating and reporting captured data.
26
- * @param {string} agentIdentifier - A 16 character string uniquely identifying the agent.
23
+ * @param {Object} agentRef - The agent reference object.
27
24
  * @param {string} group - The named "bucket" for the events this feature will be bucketing for later collection.
28
25
  */
29
- function registerDrain(agentIdentifier, group) {
26
+ function registerDrain(agentRef, group) {
27
+ if (!agentRef) return;
30
28
  // Here `item` captures the registered properties of a feature-group: whether it is ready for its buffered events
31
29
  // to be drained (`staged`) and the `priority` order in which it should be drained relative to other feature groups.
32
30
  const item = {
33
31
  staged: false,
34
32
  priority: _features.featurePriority[group] || 0
35
33
  };
36
- curateRegistry(agentIdentifier);
37
- if (!registry[agentIdentifier].get(group)) registry[agentIdentifier].set(group, item);
34
+ if (!agentRef.runtime.drainRegistry.get(group)) agentRef.runtime.drainRegistry.set(group, item);
38
35
  }
39
36
 
40
37
  /**
41
38
  * Removes an item from the registry and immediately re-checks if the registry is ready to "drain all"
42
- * @param {*} agentIdentifier - A 16 character string uniquely identifying the agent.
39
+ * @param {Object} agentRef - The agent reference object.
43
40
  * @param {*} group - The named "bucket" to be removed from the registry
44
41
  */
45
- function deregisterDrain(agentIdentifier, group) {
46
- if (!agentIdentifier || !registry[agentIdentifier]) return;
47
- if (registry[agentIdentifier].get(group)) registry[agentIdentifier].delete(group);
48
- drainGroup(agentIdentifier, group, false);
49
- if (registry[agentIdentifier].size) checkCanDrainAll(agentIdentifier);
50
- }
51
-
52
- /**
53
- * Registers the specified agent with the centralized event buffer registry if it is not already registered.
54
- * Agents without an identifier (as in the case of some tests) will be excluded from the registry.
55
- * @param {string} agentIdentifier - A 16 character string uniquely identifying an agent.
56
- */
57
- function curateRegistry(agentIdentifier) {
58
- if (!agentIdentifier) throw new Error('agentIdentifier required');
59
- if (!registry[agentIdentifier]) registry[agentIdentifier] = new Map();
42
+ function deregisterDrain(agentRef, group) {
43
+ if (!agentRef) return;
44
+ const drainRegistry = agentRef.runtime.drainRegistry;
45
+ if (!drainRegistry) return;
46
+ if (drainRegistry.get(group)) drainRegistry.delete(group);
47
+ drainGroup(agentRef, group, false);
48
+ if (drainRegistry.size) checkCanDrainAll(agentRef);
60
49
  }
61
50
 
62
51
  /**
63
52
  * Drain buffered events out of the event emitter. Each feature should have its events bucketed by "group" and drain
64
53
  * its own named group explicitly, when ready.
65
- * @param {string} agentIdentifier - A unique 16 character ID corresponding to an instantiated agent.
54
+ * @param {Object} agentRef - The agent reference object.
66
55
  * @param {string} featureName - A named group into which the feature's buffered events are bucketed.
67
56
  * @param {boolean} force - Whether to force the drain to occur immediately, bypassing the registry and staging logic.
68
57
  */
69
- function drain(agentIdentifier = '', featureName = 'feature', force = false) {
70
- curateRegistry(agentIdentifier);
58
+ function drain(agentRef, featureName = 'feature', force = false) {
59
+ if (!agentRef) return;
71
60
  // If the feature for the specified agent is not in the registry, that means the instrument file was bypassed.
72
61
  // This could happen in tests, or loaders that directly import the aggregator. In these cases it is safe to
73
62
  // drain the feature group immediately rather than waiting to drain all at once.
74
- if (!agentIdentifier || !registry[agentIdentifier].get(featureName) || force) return drainGroup(agentIdentifier, featureName);
63
+ if (!agentRef.runtime.drainRegistry.get(featureName) || force) return drainGroup(agentRef, featureName);
75
64
 
76
65
  // When `drain` is called, this feature is ready to drain (staged).
77
- registry[agentIdentifier].get(featureName).staged = true;
78
- checkCanDrainAll(agentIdentifier);
66
+ agentRef.runtime.drainRegistry.get(featureName).staged = true;
67
+ checkCanDrainAll(agentRef);
79
68
  }
80
69
 
81
70
  /** Checks all items in the registry to see if they have been "staged". If ALL items are staged, it will drain all registry items (drainGroup). It not, nothing will happen */
82
- function checkCanDrainAll(agentIdentifier) {
71
+ function checkCanDrainAll(agentRef) {
72
+ if (!agentRef) return;
83
73
  // Only when the event-groups for all features are ready to drain (staged) do we execute the drain. This has the effect
84
74
  // that the last feature to call drain triggers drain for all features.
85
- const items = Array.from(registry[agentIdentifier]);
75
+ const items = Array.from(agentRef.runtime.drainRegistry);
86
76
  if (items.every(([key, values]) => values.staged)) {
87
77
  items.sort((a, b) => a[1].priority - b[1].priority);
88
78
  items.forEach(([group]) => {
89
- registry[agentIdentifier].delete(group);
90
- drainGroup(agentIdentifier, group);
79
+ agentRef.runtime.drainRegistry.delete(group);
80
+ drainGroup(agentRef, group);
91
81
  });
92
82
  }
93
83
  }
@@ -97,12 +87,12 @@ function checkCanDrainAll(agentIdentifier) {
97
87
  * the subscribed handlers for the group.
98
88
  * @param {*} group - The name of a particular feature's event "bucket".
99
89
  */
100
- function drainGroup(agentIdentifier, group, activateGroup = true) {
101
- const baseEE = agentIdentifier ? _contextualEe.ee.get(agentIdentifier) : _contextualEe.ee;
90
+ function drainGroup(agentRef, group, activateGroup = true) {
91
+ if (!agentRef) return;
92
+ const baseEE = agentRef.ee;
102
93
  const handlers = _registerHandler.registerHandler.handlers; // other storage in registerHandler
103
- if (baseEE.aborted || !baseEE.backlog || !handlers) return;
94
+ if (!baseEE || baseEE.aborted || !baseEE.backlog || !handlers) return;
104
95
  (0, _globalEvent.dispatchGlobalEvent)({
105
- agentIdentifier,
106
96
  type: 'lifecycle',
107
97
  name: 'drain',
108
98
  feature: group
@@ -18,7 +18,6 @@ var _encode = require("../url/encode");
18
18
  var _console = require("../util/console");
19
19
  var _stringify = require("../util/stringify");
20
20
  var _submitData = require("../util/submit-data");
21
- var _featureFlags = require("../util/feature-flags");
22
21
  var _globalEvent = require("../dispatch/global-event");
23
22
  /**
24
23
  * Copyright 2020-2026 New Relic, Inc. All rights reserved.
@@ -237,8 +236,7 @@ function send(agentRef, {
237
236
  }
238
237
  }
239
238
  (0, _globalEvent.dispatchGlobalEvent)({
240
- agentIdentifier: agentRef.agentIdentifier,
241
- drained: !!_featureFlags.activatedFeatures?.[agentRef.agentIdentifier],
239
+ drained: !!agentRef.runtime?.activatedFeatures,
242
240
  type: 'data',
243
241
  name: 'harvest',
244
242
  feature: featureName,
@@ -7,7 +7,6 @@ exports.SessionEntity = void 0;
7
7
  var _uniqueId = require("../ids/unique-id");
8
8
  var _console = require("../util/console");
9
9
  var _stringify = require("../util/stringify");
10
- var _contextualEe = require("../event-emitter/contextual-ee");
11
10
  var _timer = require("../timer/timer");
12
11
  var _runtime = require("../constants/runtime");
13
12
  var _constants = require("./constants");
@@ -45,27 +44,27 @@ const model = {
45
44
  };
46
45
  class SessionEntity {
47
46
  /**
48
- * Create a self-managing Session Entity. This entity is scoped to the agent identifier which triggered it, allowing for multiple simultaneous session objects to exist.
47
+ * Create a self-managing Session Entity. This entity is scoped to the agent which triggered it, allowing for multiple simultaneous session objects to exist.
49
48
  * There is one "namespace" an agent can store data in LS -- NRBA_{key}. If there are two agents on one page, and they both use the same key, they could overwrite each other since they would both use the same namespace in LS by default.
50
49
  * The value can be overridden in the constructor, but will default to a unique 16 character hex string
51
50
  * expiresMs and inactiveMs are used to "expire" the session, but can be overridden in the constructor. Pass 0 to disable expiration timers.
52
51
  */
53
52
  constructor(opts) {
54
53
  const {
55
- agentIdentifier,
54
+ agentRef,
56
55
  key,
57
56
  storage
58
57
  } = opts;
59
- if (!agentIdentifier || !key || !storage) {
60
- throw new Error("Missing required field(s):".concat(!agentIdentifier ? ' agentID' : '').concat(!key ? ' key' : '').concat(!storage ? ' storage' : ''));
58
+ if (!agentRef || !key || !storage) {
59
+ throw new Error("Missing required field(s):".concat(!agentRef ? ' agentRef' : '').concat(!key ? ' key' : '').concat(!storage ? ' storage' : ''));
61
60
  }
62
- this.agentIdentifier = agentIdentifier;
61
+ this.agentRef = agentRef;
63
62
  this.storage = storage;
64
63
  this.state = {};
65
64
 
66
65
  // key is intended to act as the k=v pair
67
66
  this.key = key;
68
- this.ee = _contextualEe.ee.get(agentIdentifier);
67
+ this.ee = agentRef.ee;
69
68
  (0, _wrapEvents.wrapEvents)(this.ee);
70
69
  this.setup(opts);
71
70
 
@@ -253,7 +252,7 @@ class SessionEntity {
253
252
  this.expiresTimer?.clear?.();
254
253
  delete this.isNew;
255
254
  this.setup({
256
- agentIdentifier: this.agentIdentifier,
255
+ agentRef: this.agentRef,
257
256
  key: this.key,
258
257
  storage: this.storage,
259
258
  expiresMs: this.expiresMs,
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.warn = warn;
7
7
  var _globalEvent = require("../dispatch/global-event");
8
8
  /**
9
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
9
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
10
10
  * SPDX-License-Identifier: Apache-2.0
11
11
  */
12
12
 
@@ -22,7 +22,6 @@ function warn(code, secondary) {
22
22
  if (typeof console.debug !== 'function') return;
23
23
  console.debug("New Relic Warning: https://github.com/newrelic/newrelic-browser-agent/blob/main/docs/warning-codes.md#".concat(code), secondary);
24
24
  (0, _globalEvent.dispatchGlobalEvent)({
25
- agentIdentifier: null,
26
25
  drained: null,
27
26
  type: 'data',
28
27
  name: 'warn',
@@ -4,37 +4,26 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.activateFeatures = activateFeatures;
7
- exports.activatedFeatures = void 0;
8
7
  var _globalEvent = require("../dispatch/global-event");
9
8
  /**
10
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
9
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
11
10
  * SPDX-License-Identifier: Apache-2.0
12
11
  */
13
12
 
14
- const sentIds = new Set();
15
-
16
- /** A map of feature flags and their values as provided by the rum call -- scoped by agent ID */
17
- const activatedFeatures = exports.activatedFeatures = {};
18
-
19
13
  /**
20
- * Sets the activatedFeatures object, dispatches the global loaded event,
14
+ * Sets the activatedFeatures on the agentRef, dispatches the global loaded event,
21
15
  * and emits the rumresp flag to features
22
16
  * @param {{[key:string]:number}} flags key-val pair of flag names and numeric
23
- * @param {string} agentIdentifier agent instance identifier
17
+ * @param {Object} agentRef agent reference
24
18
  * @returns {void}
25
19
  */
26
20
  function activateFeatures(flags, agentRef) {
27
- const agentIdentifier = agentRef.agentIdentifier;
28
- activatedFeatures[agentIdentifier] ??= {};
29
- if (!flags || typeof flags !== 'object') return;
30
- if (sentIds.has(agentIdentifier)) return;
21
+ if (!flags || typeof flags !== 'object' || !!agentRef.runtime.activatedFeatures) return;
31
22
  agentRef.ee.emit('rumresp', [flags]);
32
- activatedFeatures[agentIdentifier] = flags;
33
- sentIds.add(agentIdentifier);
23
+ agentRef.runtime.activatedFeatures = flags;
34
24
 
35
25
  // let any window level subscribers know that the agent is running, per install docs
36
26
  (0, _globalEvent.dispatchGlobalEvent)({
37
- agentIdentifier,
38
27
  loaded: true,
39
28
  drained: true,
40
29
  type: 'lifecycle',
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.findScriptTimings = findScriptTimings;
7
+ exports.thisFile = void 0;
7
8
  var _runtime = require("../constants/runtime");
8
9
  var _now = require("../timing/now");
9
10
  var _cleanUrl = require("../url/clean-url");
@@ -17,8 +18,16 @@ var _browserStackMatchers = require("./browser-stack-matchers");
17
18
  * @typedef {import('./register-api-types').RegisterAPITimings} RegisterAPITimings
18
19
  */
19
20
 
21
+ /** export for testing purposes */
22
+ let thisFile = exports.thisFile = void 0;
23
+ try {
24
+ exports.thisFile = thisFile = extractUrlsFromStack(getDeepStackTrace()).at(0);
25
+ } catch (err) {
26
+ exports.thisFile = thisFile = extractUrlsFromStack(err).at(0);
27
+ }
28
+
20
29
  /** @type {(entry: PerformanceEntry) => boolean} - A shared function to determine if a performance entry is a valid script or link resource for evaluation */
21
- const validEntryCriteria = entry => entry.initiatorType === 'script' || entry.initiatorType === 'link' && entry.name.endsWith('.js');
30
+ const validEntryCriteria = entry => entry.initiatorType === 'script' || ['link', 'fetch'].includes(entry.initiatorType) && entry.name.endsWith('.js');
22
31
 
23
32
  /** @type {Set<PerformanceResourceTiming>} - A set of resource timing objects that are "valid" -- see "validEntryCriteria" */
24
33
  const scripts = new Set();
@@ -62,9 +71,15 @@ function extractUrlsFromStack(stack) {
62
71
  const lines = stack.split('\n');
63
72
  for (const line of lines) {
64
73
  // Try gecko format first, then chrome
65
- const parts = line.match(_browserStackMatchers.gecko) || line.match(_browserStackMatchers.chrome);
74
+ const parts = line.match(_browserStackMatchers.gecko) || line.match(_browserStackMatchers.chrome) || line.match(_browserStackMatchers.chromeEval);
66
75
  if (parts && parts[2]) {
67
76
  urls.add((0, _cleanUrl.cleanURL)(parts[2]));
77
+ } else {
78
+ // Fallback: match URLs using a generic .js pattern (non-greedy to handle ports and query params)
79
+ const fallbackMatch = line.match(/\(([^)]+\.js):\d+:\d+\)/) || line.match(/^\s+at\s+([^\s(]+\.js):\d+:\d+/);
80
+ if (fallbackMatch && fallbackMatch[1]) {
81
+ urls.add((0, _cleanUrl.cleanURL)(fallbackMatch[1]));
82
+ }
68
83
  }
69
84
  }
70
85
  return [...urls];
@@ -121,9 +136,11 @@ function findScriptTimings() {
121
136
  };
122
137
  const stack = getDeepStackTrace();
123
138
  if (!stack) return timings;
124
- const navUrl = _runtime.globalScope.performance?.getEntriesByType('navigation')?.find(entry => entry.initiatorType === 'navigation')?.name || '';
139
+ const navUrl = _runtime.globalScope.performance?.getEntriesByType('navigation')?.[0]?.name || '';
125
140
  try {
126
- const mfeScriptUrl = extractUrlsFromStack(stack).at(-1); // array of URLs from the stack of the register API caller, the MFE script should be at the bottom
141
+ const urls = extractUrlsFromStack(stack);
142
+ /** if there is exactly one url, this means the MFE script is running in the same file as the agent. Otherwise, lets strip away the known agent file from any other file lines */
143
+ const mfeScriptUrl = (urls.length > 1 ? urls.filter(line => !thisFile.endsWith(line) && !line.endsWith(thisFile)) : urls).at(0);
127
144
  if (!mfeScriptUrl) return timings;
128
145
  if (navUrl.includes(mfeScriptUrl)) {
129
146
  // this means the stack is indicating that the registration came from an inline script or eval, so we won't find a matching script resource - return early with just the URL
@@ -143,6 +160,7 @@ function findScriptTimings() {
143
160
  if (wasPreloaded(mfeScriptUrl)) {
144
161
  timings.asset = mfeScriptUrl;
145
162
  timings.type = 'preload';
163
+
146
164
  // wait for a late PO callback... The timings object can be mutated after the fact since we return a pointer and not a cloned object
147
165
  poSubscribers.push({
148
166
  addedAt: (0, _now.now)(),
@@ -6,23 +6,38 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.stringify = stringify;
7
7
  var _contextualEe = require("../event-emitter/contextual-ee");
8
8
  /**
9
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
9
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
10
10
  * SPDX-License-Identifier: Apache-2.0
11
11
  */
12
12
 
13
13
  /**
14
14
  * Returns a function for use as a replacer parameter in JSON.stringify() to handle circular references.
15
+ * Uses an array to track the current ancestor chain, allowing the same object to appear
16
+ * multiple times in the structure as long as it's not a circular reference.
15
17
  * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value MDN - Cyclical object value}
16
- * @returns {Function} A function that filters out values it has seen before.
18
+ * @returns {Function} A function that filters out circular references while allowing duplicate references.
17
19
  */
18
20
  const getCircularReplacer = () => {
19
- const seen = new WeakSet();
20
- return (key, value) => {
21
- if (typeof value === 'object' && value !== null) {
22
- if (seen.has(value)) {
21
+ const stack = [];
22
+ return function (key, value) {
23
+ if (stack.length > 0) {
24
+ // Find where we are in the stack
25
+ const thisPos = stack.indexOf(this);
26
+ if (~thisPos) {
27
+ // We're still in the stack, trim it
28
+ stack.splice(thisPos + 1);
29
+ } else {
30
+ // We're not in the stack, add ourselves
31
+ stack.push(this);
32
+ }
33
+
34
+ // Check if value is in the current ancestor chain
35
+ if (~stack.indexOf(value)) {
23
36
  return;
24
37
  }
25
- seen.add(value);
38
+ } else {
39
+ // First call, initialize with root
40
+ stack.push(value);
26
41
  }
27
42
  return value;
28
43
  };
@@ -37,7 +37,6 @@ if (_runtime.isBrowserScope) {
37
37
  if (lcpEntry.element?.tagName) attrs.elTag = lcpEntry.element.tagName;
38
38
  }
39
39
  if (attribution.element) attrs.element = attribution.element;
40
- if (attribution.navigationEntry) attrs.pageUrl = (0, _cleanUrl.cleanURL)(attribution.navigationEntry.name); // used to ensure the LCP gets the correct URL at harvest time if a soft nav has occurred before page load
41
40
  if (attribution.url) attrs.elUrl = (0, _cleanUrl.cleanURL)(attribution.url);
42
41
  largestContentfulPaint.update({
43
42
  value,
@@ -9,7 +9,7 @@ var _load = require("../window/load");
9
9
  var _constants = require("./constants");
10
10
  var _vitalMetric = require("./vital-metric");
11
11
  /**
12
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
12
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
13
13
  * SPDX-License-Identifier: Apache-2.0
14
14
  */
15
15
 
@@ -18,8 +18,9 @@ if (_runtime.isBrowserScope) {
18
18
  const perf = _runtime.globalScope.performance;
19
19
  const handler = () => {
20
20
  if (!loadTime.isValid && perf) {
21
+ const navEntry = (0, _runtime.getNavigationEntry)();
21
22
  loadTime.update({
22
- value: (0, _runtime.supportsNavTimingL2)() ? perf.getEntriesByType('navigation')?.[0]?.loadEventEnd : perf.timing?.loadEventEnd - _runtime.originTime
23
+ value: navEntry ? navEntry.loadEventEnd : perf.timing?.loadEventEnd - _runtime.originTime
23
24
  });
24
25
  }
25
26
  };
@@ -9,7 +9,7 @@ var _constants = require("./constants");
9
9
  var _vitalMetric = require("./vital-metric");
10
10
  var _attribution = require("web-vitals/attribution");
11
11
  /**
12
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
12
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
13
13
  * SPDX-License-Identifier: Apache-2.0
14
14
  */
15
15
 
@@ -23,7 +23,7 @@ const timeToFirstByte = exports.timeToFirstByte = new _vitalMetric.VitalMetric(_
23
23
  * - cross-origin iframes specifically in firefox and safari
24
24
  * - onTTFB relies on a truthy `responseStart` value, should ensure that exists before relying on it (seen to be falsy in certain Electron.js cases for instance)
25
25
  */
26
- if (_runtime.isBrowserScope && (0, _runtime.supportsNavTimingL2)() && !_runtime.isiOS && window === window.parent) {
26
+ if (_runtime.isBrowserScope && (0, _runtime.getNavigationEntry)() && !_runtime.isiOS && window === window.parent) {
27
27
  (0, _attribution.onTTFB)(({
28
28
  value,
29
29
  attribution
@@ -180,10 +180,6 @@ class Aggregate extends _aggregateBase.AggregateBase {
180
180
  if (!target) (0, _handle.handle)('trace-jserror', jsErrorEvent, undefined, _features.FEATURE_NAMES.sessionTrace, this.ee);
181
181
  // still send EE events for other features such as above, but stop this one from aggregating internal data
182
182
  if (this.blocked) return;
183
- if (err.__newrelic?.[this.agentIdentifier]) {
184
- params._interactionId = err.__newrelic[this.agentIdentifier].interactionId;
185
- params._interactionNodeId = err.__newrelic[this.agentIdentifier].interactionNodeId;
186
- }
187
183
  if (err.__newrelic?.socketId) {
188
184
  customAttributes.socketId = err.__newrelic.socketId;
189
185
  }
@@ -83,7 +83,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
83
83
  us: info.user,
84
84
  ac: info.account,
85
85
  pr: info.product,
86
- af: (0, _initializedFeatures.getActivatedFeaturesFlags)(this.agentIdentifier).join(','),
86
+ af: (0, _initializedFeatures.getActivatedFeaturesFlags)(this.agentRef).join(','),
87
87
  ...measures,
88
88
  xx: info.extra,
89
89
  ua: info.userAttributes,
@@ -98,9 +98,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
98
98
  }
99
99
  }, this.obfuscator.obfuscateString.bind(this.obfuscator), 'string');
100
100
  if (_runtime.globalScope.performance) {
101
- if ((0, _runtime.supportsNavTimingL2)()) {
101
+ const navTimingEntry = (0, _runtime.getNavigationEntry)();
102
+ if (navTimingEntry) {
102
103
  // Navigation Timing level 2 API that replaced PerformanceTiming & PerformanceNavigation
103
- const navTimingEntry = _runtime.globalScope?.performance?.getEntriesByType('navigation')?.[0];
104
104
  const perf = {
105
105
  timing: (0, _navTiming.addPT)(_runtime.originTime, navTimingEntry, {}),
106
106
  navigation: (0, _navTiming.addPN)(navTimingEntry, {})
@@ -5,7 +5,6 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.getActivatedFeaturesFlags = getActivatedFeaturesFlags;
7
7
  var _features = require("../../../loaders/features/features");
8
- var _nreum = require("../../../common/window/nreum");
9
8
  /**
10
9
  * Copyright 2020-2026 New Relic, Inc. All rights reserved.
11
10
  * SPDX-License-Identifier: Apache-2.0
@@ -14,14 +13,13 @@ var _nreum = require("../../../common/window/nreum");
14
13
  /**
15
14
  * Get an array of flags required by downstream (NR UI) based on the features initialized in this agent
16
15
  * (aka what is running on the page).
17
- * @param {String} agentId - the ID of the initialized agent on the page, mapping to the one under the global 'newrelic' object
16
+ * @param {Object} agentRef - the agent reference object
18
17
  * @returns {String[]} Up to 5 short strings corresponding to ingest mapping of features.
19
18
  */
20
- function getActivatedFeaturesFlags(agentId) {
19
+ function getActivatedFeaturesFlags(agentRef) {
21
20
  const flagArr = [];
22
- const newrelic = (0, _nreum.gosNREUM)();
23
21
  try {
24
- Object.keys(newrelic.initializedAgents[agentId].features).forEach(featName => {
22
+ Object.keys(agentRef.features || {}).forEach(featName => {
25
23
  switch (featName) {
26
24
  case _features.FEATURE_NAMES.ajax:
27
25
  flagArr.push('xhr');