@newrelic/browser-agent 1.311.0 → 1.312.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 (81) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cjs/common/constants/agent-constants.js +4 -5
  3. package/dist/cjs/common/constants/env.cdn.js +2 -2
  4. package/dist/cjs/common/constants/env.npm.js +2 -2
  5. package/dist/cjs/common/util/script-tracker.js +2 -0
  6. package/dist/cjs/common/util/stringify.js +6 -21
  7. package/dist/cjs/common/util/v2.js +90 -6
  8. package/dist/cjs/common/wrap/wrap-fetch.js +10 -5
  9. package/dist/cjs/common/wrap/wrap-function.js +17 -9
  10. package/dist/cjs/common/wrap/wrap-logger.js +6 -4
  11. package/dist/cjs/common/wrap/wrap-xhr.js +3 -1
  12. package/dist/cjs/features/ajax/aggregate/index.js +24 -6
  13. package/dist/cjs/features/ajax/instrument/index.js +12 -10
  14. package/dist/cjs/features/generic_events/instrument/index.js +3 -3
  15. package/dist/cjs/features/logging/aggregate/index.js +12 -7
  16. package/dist/cjs/features/logging/instrument/index.js +6 -4
  17. package/dist/cjs/features/logging/shared/utils.js +4 -4
  18. package/dist/cjs/features/soft_navigations/aggregate/ajax-node.js +6 -3
  19. package/dist/cjs/loaders/api/register.js +24 -23
  20. package/dist/cjs/loaders/api/wrapLogger.js +2 -2
  21. package/dist/esm/common/constants/agent-constants.js +4 -5
  22. package/dist/esm/common/constants/env.cdn.js +2 -2
  23. package/dist/esm/common/constants/env.npm.js +2 -2
  24. package/dist/esm/common/util/script-tracker.js +2 -2
  25. package/dist/esm/common/util/stringify.js +6 -21
  26. package/dist/esm/common/util/v2.js +86 -6
  27. package/dist/esm/common/wrap/wrap-fetch.js +10 -5
  28. package/dist/esm/common/wrap/wrap-function.js +17 -9
  29. package/dist/esm/common/wrap/wrap-logger.js +6 -4
  30. package/dist/esm/common/wrap/wrap-xhr.js +3 -1
  31. package/dist/esm/features/ajax/aggregate/index.js +24 -6
  32. package/dist/esm/features/ajax/instrument/index.js +12 -10
  33. package/dist/esm/features/generic_events/instrument/index.js +3 -3
  34. package/dist/esm/features/logging/aggregate/index.js +13 -8
  35. package/dist/esm/features/logging/instrument/index.js +6 -4
  36. package/dist/esm/features/logging/shared/utils.js +4 -4
  37. package/dist/esm/features/soft_navigations/aggregate/ajax-node.js +6 -3
  38. package/dist/esm/loaders/api/register.js +24 -23
  39. package/dist/esm/loaders/api/wrapLogger.js +2 -2
  40. package/dist/types/common/constants/agent-constants.d.ts.map +1 -1
  41. package/dist/types/common/util/script-tracker.d.ts +11 -0
  42. package/dist/types/common/util/script-tracker.d.ts.map +1 -1
  43. package/dist/types/common/util/stringify.d.ts.map +1 -1
  44. package/dist/types/common/util/v2.d.ts +37 -0
  45. package/dist/types/common/util/v2.d.ts.map +1 -1
  46. package/dist/types/common/wrap/wrap-fetch.d.ts +1 -1
  47. package/dist/types/common/wrap/wrap-fetch.d.ts.map +1 -1
  48. package/dist/types/common/wrap/wrap-function.d.ts +1 -1
  49. package/dist/types/common/wrap/wrap-function.d.ts.map +1 -1
  50. package/dist/types/common/wrap/wrap-logger.d.ts +1 -1
  51. package/dist/types/common/wrap/wrap-logger.d.ts.map +1 -1
  52. package/dist/types/common/wrap/wrap-xhr.d.ts +1 -1
  53. package/dist/types/common/wrap/wrap-xhr.d.ts.map +1 -1
  54. package/dist/types/features/ajax/aggregate/index.d.ts +2 -1
  55. package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
  56. package/dist/types/features/ajax/instrument/index.d.ts.map +1 -1
  57. package/dist/types/features/logging/aggregate/index.d.ts.map +1 -1
  58. package/dist/types/features/logging/instrument/index.d.ts.map +1 -1
  59. package/dist/types/features/logging/shared/utils.d.ts +2 -2
  60. package/dist/types/features/logging/shared/utils.d.ts.map +1 -1
  61. package/dist/types/features/soft_navigations/aggregate/ajax-node.d.ts +1 -0
  62. package/dist/types/features/soft_navigations/aggregate/ajax-node.d.ts.map +1 -1
  63. package/dist/types/loaders/api/register.d.ts.map +1 -1
  64. package/package.json +3 -3
  65. package/src/common/constants/agent-constants.js +4 -5
  66. package/src/common/util/script-tracker.js +2 -2
  67. package/src/common/util/stringify.js +6 -23
  68. package/src/common/util/v2.js +84 -7
  69. package/src/common/wrap/wrap-fetch.js +12 -5
  70. package/src/common/wrap/wrap-function.js +16 -9
  71. package/src/common/wrap/wrap-logger.js +6 -4
  72. package/src/common/wrap/wrap-xhr.js +3 -1
  73. package/src/features/ajax/aggregate/index.js +22 -6
  74. package/src/features/ajax/instrument/index.js +13 -10
  75. package/src/features/generic_events/instrument/index.js +3 -3
  76. package/src/features/logging/aggregate/index.js +13 -14
  77. package/src/features/logging/instrument/index.js +6 -4
  78. package/src/features/logging/shared/utils.js +4 -4
  79. package/src/features/soft_navigations/aggregate/ajax-node.js +6 -3
  80. package/src/loaders/api/register.js +19 -12
  81. package/src/loaders/api/wrapLogger.js +2 -2
package/CHANGELOG.md CHANGED
@@ -3,6 +3,20 @@
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.312.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.311.0...v1.312.0) (2026-03-30)
7
+
8
+
9
+ ### Features
10
+
11
+ * Automatically Detect MFE AJAX ([#1722](https://github.com/newrelic/newrelic-browser-agent/issues/1722)) ([ed8e90c](https://github.com/newrelic/newrelic-browser-agent/commit/ed8e90cb4e0c6a0749d615a91bec27b799475543))
12
+ * Automatically Detect MFE Logs ([#1721](https://github.com/newrelic/newrelic-browser-agent/issues/1721)) ([c7558a0](https://github.com/newrelic/newrelic-browser-agent/commit/c7558a04cd984640c5bbb9451d83b9eddc24cc78))
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * Revert circular replacer ([#1732](https://github.com/newrelic/newrelic-browser-agent/issues/1732)) ([a82d976](https://github.com/newrelic/newrelic-browser-agent/commit/a82d9761e64d4b8b014c9c686756be052b5461b1))
18
+ * Session replays not working for Angular app running on Safari ([#1733](https://github.com/newrelic/newrelic-browser-agent/issues/1733)) ([8433d47](https://github.com/newrelic/newrelic-browser-agent/commit/8433d470192bdac69bcdbf5476496f733a8e96bf))
19
+
6
20
  ## [1.311.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.310.1...v1.311.0) (2026-03-20)
7
21
 
8
22
 
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.SUPPORTS_REGISTERED_ENTITIES = exports.SESSION_ERROR = exports.MAX_PAYLOAD_SIZE = exports.IDEAL_PAYLOAD_SIZE = exports.DEFAULT_KEY = void 0;
7
7
  var _features = require("../../loaders/features/features");
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
 
@@ -16,8 +16,7 @@ const DEFAULT_KEY = exports.DEFAULT_KEY = 'NR_CONTAINER_AGENT';
16
16
  const SESSION_ERROR = exports.SESSION_ERROR = 'SESSION_ERROR';
17
17
  const SUPPORTS_REGISTERED_ENTITIES = exports.SUPPORTS_REGISTERED_ENTITIES = {
18
18
  [_features.FEATURE_NAMES.logging]: true,
19
- // flip other features here when they are supported by DEM consumers
20
- [_features.FEATURE_NAMES.genericEvents]: false,
21
- [_features.FEATURE_NAMES.jserrors]: false,
22
- [_features.FEATURE_NAMES.ajax]: false
19
+ [_features.FEATURE_NAMES.genericEvents]: true,
20
+ [_features.FEATURE_NAMES.jserrors]: true,
21
+ [_features.FEATURE_NAMES.ajax]: true
23
22
  };
@@ -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.311.0";
20
+ const VERSION = exports.VERSION = "1.312.0-rc.1";
21
21
 
22
22
  /**
23
23
  * Exposes the build type of the agent
@@ -31,4 +31,4 @@ const BUILD_ENV = exports.BUILD_ENV = "CDN";
31
31
  const DIST_METHOD = exports.DIST_METHOD = 'CDN';
32
32
  const RRWEB_PACKAGE_NAME = exports.RRWEB_PACKAGE_NAME = '@newrelic/rrweb';
33
33
  // Babel will inline this with the rrweb (fork) version on CDN builds.
34
- const RRWEB_VERSION = exports.RRWEB_VERSION = "1.0.1";
34
+ const RRWEB_VERSION = exports.RRWEB_VERSION = "1.1.0";
@@ -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.311.0";
20
+ const VERSION = exports.VERSION = "1.312.0-rc.1";
21
21
 
22
22
  /**
23
23
  * Exposes the build type of the agent
@@ -32,4 +32,4 @@ const BUILD_ENV = exports.BUILD_ENV = 'NPM';
32
32
  const DIST_METHOD = exports.DIST_METHOD = 'NPM';
33
33
  const RRWEB_PACKAGE_NAME = exports.RRWEB_PACKAGE_NAME = '@newrelic/rrweb';
34
34
  // Babel will inline this with the rrweb (fork) version on NPM dist esm/cjs builds.
35
- const RRWEB_VERSION = exports.RRWEB_VERSION = "1.0.1";
35
+ const RRWEB_VERSION = exports.RRWEB_VERSION = "1.1.0";
@@ -3,7 +3,9 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
+ exports.extractUrlsFromStack = extractUrlsFromStack;
6
7
  exports.findScriptTimings = findScriptTimings;
8
+ exports.getDeepStackTrace = getDeepStackTrace;
7
9
  exports.thisFile = void 0;
8
10
  var _runtime = require("../constants/runtime");
9
11
  var _now = require("../timing/now");
@@ -12,32 +12,17 @@ var _contextualEe = require("../event-emitter/contextual-ee");
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.
17
15
  * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value MDN - Cyclical object value}
18
- * @returns {Function} A function that filters out circular references while allowing duplicate references.
16
+ * @returns {Function} A function that filters out values it has seen before.
19
17
  */
20
18
  const getCircularReplacer = () => {
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)) {
19
+ const seen = new WeakSet();
20
+ return (key, value) => {
21
+ if (typeof value === 'object' && value !== null) {
22
+ if (seen.has(value)) {
36
23
  return;
37
24
  }
38
- } else {
39
- // First call, initialize with root
40
- stack.push(value);
25
+ seen.add(value);
41
26
  }
42
27
  return value;
43
28
  };
@@ -4,7 +4,13 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.V2_TYPES = void 0;
7
+ exports.findTargetsFromStackTrace = findTargetsFromStackTrace;
8
+ exports.getRegisteredTargetsFromFilename = getRegisteredTargetsFromFilename;
9
+ exports.getRegisteredTargetsFromId = getRegisteredTargetsFromId;
7
10
  exports.getVersion2Attributes = getVersion2Attributes;
11
+ exports.getVersion2DuplicationAttributes = getVersion2DuplicationAttributes;
12
+ exports.shouldDuplicate = shouldDuplicate;
13
+ var _scriptTracker = require("./script-tracker");
8
14
  /**
9
15
  * Copyright 2020-2026 New Relic, Inc. All rights reserved.
10
16
  * SPDX-License-Identifier: Apache-2.0
@@ -21,6 +27,30 @@ const V2_TYPES = exports.V2_TYPES = {
21
27
  BA: 'BA'
22
28
  };
23
29
 
30
+ /**
31
+ * Returns the registered target associated with a given ID. Returns an empty array if no target is found.
32
+ * @param {string|number} id
33
+ * @param {*} agentRef the agent reference
34
+ * @returns {import("../../interfaces/registered-entity").RegisterAPIMetadataTarget[]}
35
+ */
36
+ function getRegisteredTargetsFromId(id, agentRef) {
37
+ if (!id || !agentRef?.init.api.allow_registered_children) return [];
38
+ const registeredEntities = agentRef.runtime.registeredEntities;
39
+ return registeredEntities?.filter(entity => String(entity.metadata.target.id) === String(id)).map(entity => entity.metadata.target) || [];
40
+ }
41
+
42
+ /**
43
+ * Returns the registered target(s) associated with a given filename if found in the resource timing API during registration. Returns an empty array if no target is found.
44
+ * @param {string} filename
45
+ * @param {*} agentRef
46
+ * @returns {import("../../interfaces/registered-entity").RegisterAPIMetadataTarget[]}
47
+ */
48
+ function getRegisteredTargetsFromFilename(filename, agentRef) {
49
+ if (!filename || !agentRef?.init.api.allow_registered_children) return [];
50
+ const registeredEntities = agentRef.runtime.registeredEntities;
51
+ return registeredEntities?.filter(entity => entity.metadata.timings?.asset?.endsWith(filename)).map(entity => entity.metadata.target) || [];
52
+ }
53
+
24
54
  /**
25
55
  * When given a valid target, returns an object with the V2 payload attributes. Returns an empty object otherwise.
26
56
  * @note Field names may change as the schema is finalized
@@ -30,7 +60,7 @@ const V2_TYPES = exports.V2_TYPES = {
30
60
  * @returns {Object} returns an empty object if args are not supplied or the aggregate instance is not supporting version 2
31
61
  */
32
62
  function getVersion2Attributes(target, aggregateInstance) {
33
- if (aggregateInstance?.harvestEndpointVersion !== 2) return {};
63
+ if (!supportsV2(aggregateInstance)) return {};
34
64
  const containerAgentEntityGuid = aggregateInstance.agentRef.runtime.appMetadata.agents[0].entityGuid;
35
65
  /** if there's no target, but we are in v2 mode, this means the data belongs to the container agent */
36
66
  if (!target) {
@@ -40,11 +70,65 @@ function getVersion2Attributes(target, aggregateInstance) {
40
70
  };
41
71
  }
42
72
  /** otherwise, the data belongs to the target (MFE) and should be attributed as such */
73
+ return target.attributes;
74
+ }
75
+
76
+ /**
77
+ * Returns the attributes used for duplicating data in version 2 of the harvest endpoint.
78
+ * If not valid for duplication, returns an empty object.
79
+ * @note BEST PRACTICE - Caller should call shouldDuplicate() before utilizing this method to determine if duplication attributes should be added to the event.
80
+ * @param {import("../../interfaces/registered-entity").RegisterAPIMetadataTarget} target
81
+ * @param {*} aggregateInstance the aggregate instance calling the method
82
+ * @returns {Object}
83
+ */
84
+ function getVersion2DuplicationAttributes(target, aggregateInstance) {
85
+ if (!shouldDuplicate(target, aggregateInstance)) return {};
43
86
  return {
44
- 'source.id': target.id,
45
- 'source.name': target.name,
46
- 'source.type': target.type,
47
- 'parent.id': target.parent?.id || containerAgentEntityGuid,
48
- 'parent.type': target.parent?.type || V2_TYPES.BA
87
+ 'child.id': target.id,
88
+ 'child.type': target.type,
89
+ ...getVersion2Attributes(undefined, aggregateInstance)
49
90
  };
91
+ }
92
+
93
+ /**
94
+ * Determines if an event should be duplicated for a given target and aggregate instance. This is used to determine if duplication attributes should be added to an event and if the event should be sent to the soft nav feature for evaluation.
95
+ * @note This method is intended to be used in conjunction with getVersion2DuplicationAttributes and should be called before it to determine if duplication attributes should be added to an event.
96
+ * @param {import("../../interfaces/registered-entity").RegisterAPIMetadataTarget} target
97
+ * @param {*} aggregateInstance The aggregate instance calling the method. This is needed to check if duplication is enabled and if the harvest endpoint version supports it.
98
+ * @returns {boolean} returns true if the event should be duplicated for the target, false otherwise
99
+ */
100
+ function shouldDuplicate(target, aggregateInstance) {
101
+ return !!target && !!supportsV2(aggregateInstance) && aggregateInstance.agentRef.init.api.duplicate_registered_data;
102
+ }
103
+
104
+ /**
105
+ * Finds the registered targets from the stack trace for a given agent reference.
106
+ * @param {*} agentRef The agent reference to use for finding targets.
107
+ * @returns {Array} An array of targets found from the stack trace. If no targets are found or allowed, returns an array with undefined.
108
+ */
109
+ function findTargetsFromStackTrace(agentRef) {
110
+ if (!agentRef?.init.api.allow_registered_children) return [undefined];
111
+ const targets = [];
112
+ try {
113
+ var urls = (0, _scriptTracker.extractUrlsFromStack)((0, _scriptTracker.getDeepStackTrace)());
114
+ let iterator = urls.length - 1;
115
+ while (urls[iterator]) {
116
+ targets.push(...getRegisteredTargetsFromFilename(urls[iterator--], agentRef));
117
+ }
118
+ } catch (err) {
119
+ // Silent catch to prevent errors from propagating
120
+ }
121
+ if (!targets.length) targets.push(undefined); // if we can't find any targets from the stack trace, return an array with undefined to signify the container agent is the target
122
+ return targets;
123
+ }
124
+
125
+ /**
126
+ * Determines if the aggregate instance supports version 2 of the harvest endpoint. Nearly all the V2 logic "depends" on
127
+ * the harvest endpoint version, so this is the main gatekeeper method for whether or not V2 logic should be executed across the
128
+ * various functions in this module.
129
+ * @param {*} aggregateInstance The aggregate instance to check.
130
+ * @returns {boolean} Returns true if the aggregate instance supports version 2, false otherwise.
131
+ */
132
+ function supportsV2(aggregateInstance) {
133
+ return aggregateInstance?.harvestEndpointVersion === 2;
50
134
  }
@@ -7,6 +7,7 @@ exports.scopedEE = scopedEE;
7
7
  exports.wrapFetch = wrapFetch;
8
8
  var _contextualEe = require("../event-emitter/contextual-ee");
9
9
  var _runtime = require("../constants/runtime");
10
+ var _v = require("../util/v2");
10
11
  /**
11
12
  * Copyright 2020-2026 New Relic, Inc. All rights reserved.
12
13
  * SPDX-License-Identifier: Apache-2.0
@@ -33,7 +34,7 @@ const wrapped = {};
33
34
  * event emitter will be based.
34
35
  * @returns {Object} Scoped event emitter with a debug ID of `fetch`.
35
36
  */
36
- function wrapFetch(sharedEE) {
37
+ function wrapFetch(sharedEE, agentRef) {
37
38
  const ee = scopedEE(sharedEE);
38
39
  if (!(Req && Res && _runtime.globalScope.fetch)) {
39
40
  return ee;
@@ -49,8 +50,10 @@ function wrapFetch(sharedEE) {
49
50
  wrapPromiseMethod(Res[proto], method, bodyPrefix);
50
51
  });
51
52
  wrapPromiseMethod(_runtime.globalScope, 'fetch', prefix);
52
- ee.on(prefix + 'end', function (err, res) {
53
+ ee.on(prefix + 'end', function (err, res, targets) {
53
54
  var ctx = this;
55
+ // undefined target reports to container
56
+ ctx.targets = targets || [undefined];
54
57
  if (res) {
55
58
  var size = res.headers.get('content-length');
56
59
  if (size !== null) {
@@ -75,7 +78,9 @@ function wrapFetch(sharedEE) {
75
78
  if (typeof fn === 'function') {
76
79
  target[name] = function () {
77
80
  var args = [...arguments];
78
- var ctx = {};
81
+ const ctx = {};
82
+ const targets = (0, _v.findTargetsFromStackTrace)(agentRef);
83
+
79
84
  // we are wrapping args in an array so we can preserve the reference
80
85
  ee.emit(prefix + 'before-start', [args], ctx);
81
86
  var dtPayload;
@@ -85,10 +90,10 @@ function wrapFetch(sharedEE) {
85
90
 
86
91
  // Note we need to cast the returned (orig) Promise from native APIs into the current global Promise, which may or may not be our WrappedPromise.
87
92
  return origPromiseFromFetch.then(function (val) {
88
- ee.emit(prefix + 'end', [null, val], origPromiseFromFetch);
93
+ ee.emit(prefix + 'end', [null, val, targets], origPromiseFromFetch);
89
94
  return val;
90
95
  }, function (err) {
91
- ee.emit(prefix + 'end', [err], origPromiseFromFetch);
96
+ ee.emit(prefix + 'end', [err, undefined, targets], origPromiseFromFetch);
92
97
  throw err;
93
98
  });
94
99
  };
@@ -8,8 +8,9 @@ exports.createWrapperWithEmitter = createWrapperWithEmitter;
8
8
  exports.flag = exports.default = void 0;
9
9
  var _contextualEe = require("../event-emitter/contextual-ee");
10
10
  var _bundleId = require("../ids/bundle-id");
11
+ var _v = require("../util/v2");
11
12
  /**
12
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
13
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
13
14
  * SPDX-License-Identifier: Apache-2.0
14
15
  */
15
16
 
@@ -40,7 +41,7 @@ var _default = exports.default = createWrapperWithEmitter;
40
41
  * @param {boolean} always - If `true`, emit events even if already emitting an event.
41
42
  * @returns {function} The wrapped function.
42
43
  */
43
- function createWrapperWithEmitter(emitter, always) {
44
+ function createWrapperWithEmitter(emitter, always, agentRef) {
44
45
  emitter || (emitter = _contextualEe.ee);
45
46
  wrapFn.inPlace = inPlace;
46
47
 
@@ -59,9 +60,10 @@ function createWrapperWithEmitter(emitter, always) {
59
60
  * @param {function|object} getContext - The function or object that will serve as the 'this' context for handlers of events emitted by this wrapper.
60
61
  * @param {string} methodName - The name of the method being wrapped.
61
62
  * @param {boolean} bubble - If true, emitted events should also bubble up to the old emitter upon which the `emitter` in the current scope was based (if it defines one).
63
+ * @param {boolean} [evaluateStack] - If true, the wrapper will attempt to evaluate the stack of the executed wrapped function to find targets of the execution (ex. the MFE source of a console.log).
62
64
  * @returns {function} The wrapped function.
63
65
  */
64
- function wrapFn(fn, prefix, getContext, methodName, bubble) {
66
+ function wrapFn(fn, prefix, getContext, methodName, bubble, evaluateStack) {
65
67
  // Unless fn is both wrappable and unwrapped, return it unchanged.
66
68
  if (notWrappable(fn)) return fn;
67
69
  if (!prefix) prefix = '';
@@ -80,9 +82,15 @@ function createWrapperWithEmitter(emitter, always) {
80
82
  var ctx;
81
83
  var result;
82
84
  let thrownError;
85
+ let targets;
83
86
  try {
84
87
  originalThis = this;
85
88
  args = [...arguments];
89
+
90
+ // certain wrappers can inform the function wrapper to evaluate the stack of the executed wrapped function to find targets of the execution
91
+ // (e.g. wrap-logger can inform this method to find try to find the MFE source of a console.log)
92
+ targets = evaluateStack ? (0, _v.findTargetsFromStackTrace)(agentRef) : [undefined]; // undefined target always maps to the container agent
93
+
86
94
  if (typeof getContext === 'function') {
87
95
  ctx = getContext(args, originalThis);
88
96
  } else {
@@ -93,7 +101,7 @@ function createWrapperWithEmitter(emitter, always) {
93
101
  }
94
102
 
95
103
  // Warning: start events may mutate args!
96
- safeEmit(prefix + 'start', [args, originalThis, methodName], ctx, bubble);
104
+ safeEmit(prefix + 'start', [args, originalThis, methodName, targets], ctx, bubble);
97
105
  const fnStartTime = performance.now();
98
106
  let fnEndTime;
99
107
  try {
@@ -102,7 +110,7 @@ function createWrapperWithEmitter(emitter, always) {
102
110
  return result;
103
111
  } catch (err) {
104
112
  fnEndTime = performance.now();
105
- safeEmit(prefix + 'err', [args, originalThis, err], ctx, bubble);
113
+ safeEmit(prefix + 'err', [args, originalThis, err, targets], ctx, bubble);
106
114
  // rethrow error so we don't effect execution by observing.
107
115
  thrownError = err;
108
116
  throw thrownError;
@@ -119,10 +127,10 @@ function createWrapperWithEmitter(emitter, always) {
119
127
  };
120
128
  // standalone long task message
121
129
  if (task.isLongTask) {
122
- safeEmit('long-task', [task, originalThis], ctx, bubble);
130
+ safeEmit('long-task', [task, originalThis, targets], ctx, bubble);
123
131
  }
124
132
  // -end message also includes the task execution info
125
- safeEmit(prefix + 'end', [args, originalThis, result], ctx, bubble);
133
+ safeEmit(prefix + 'end', [args, originalThis, result, targets], ctx, bubble);
126
134
  }
127
135
  }
128
136
  }
@@ -138,7 +146,7 @@ function createWrapperWithEmitter(emitter, always) {
138
146
  * @param {boolean} [bubble=false] If `true`, emitted events should also bubble up to the old emitter upon which
139
147
  * the `emitter` in the current scope was based (if it defines one).
140
148
  */
141
- function inPlace(obj, methods, prefix, getContext, bubble) {
149
+ function inPlace(obj, methods, prefix, getContext, bubble, evaluateStack) {
142
150
  if (!prefix) prefix = '';
143
151
 
144
152
  // If prefix starts with '-' set this boolean to add the method name to the prefix before passing each one to wrap.
@@ -149,7 +157,7 @@ function createWrapperWithEmitter(emitter, always) {
149
157
 
150
158
  // Unless fn is both wrappable and unwrapped, bail so we don't add extra properties with undefined values.
151
159
  if (notWrappable(fn)) continue;
152
- obj[method] = wrapFn(fn, prependMethodPrefix ? method + prefix : prefix, getContext, method, bubble);
160
+ obj[method] = wrapFn(fn, prependMethodPrefix ? method + prefix : prefix, getContext, method, bubble, evaluateStack);
153
161
  }
154
162
  }
155
163
 
@@ -30,10 +30,10 @@ const contextMap = new Map();
30
30
  * @returns {Object} Scoped event emitter with a debug ID of `logger`.
31
31
  */
32
32
  // eslint-disable-next-line
33
- function wrapLogger(sharedEE, parent, loggerFn, context, autoCaptured = true) {
33
+ function wrapLogger(sharedEE, parent, loggerFn, context, autoCaptured = true, agentRef) {
34
34
  if (!(typeof parent === 'object' && !!parent && typeof loggerFn === 'string' && !!loggerFn && typeof parent[loggerFn] === 'function')) return (0, _console.warn)(29);
35
35
  const ee = scopedEE(sharedEE);
36
- const wrapFn = (0, _wrapFunction.createWrapperWithEmitter)(ee);
36
+ const wrapFn = (0, _wrapFunction.createWrapperWithEmitter)(ee, undefined, agentRef);
37
37
 
38
38
  /**
39
39
  * This section contains the context that will be shared across all invoked calls of the wrapped function,
@@ -46,8 +46,10 @@ function wrapLogger(sharedEE, parent, loggerFn, context, autoCaptured = true) {
46
46
  const contextLookupKey = parent[loggerFn]?.[_wrapFunction.flag] || parent[loggerFn];
47
47
  contextMap.set(contextLookupKey, ctx);
48
48
 
49
- /** observe calls to <loggerFn> and emit events prefixed with `wrap-logger-` */
50
- wrapFn.inPlace(parent, [loggerFn], 'wrap-logger-', () => contextMap.get(contextLookupKey));
49
+ /** observe calls to <loggerFn> and emit events prefixed with `wrap-logger-`
50
+ * inform the inplace wrapper to evaluate the stack for targets of the log execution,
51
+ * so that logs can be attributed to a matching MFE source if being used. */
52
+ wrapFn.inPlace(parent, [loggerFn], 'wrap-logger-', () => contextMap.get(contextLookupKey), undefined, true);
51
53
  return ee;
52
54
  }
53
55
 
@@ -11,6 +11,7 @@ var _eventListenerOpts = require("../event-listener/event-listener-opts");
11
11
  var _wrapFunction = require("./wrap-function");
12
12
  var _runtime = require("../constants/runtime");
13
13
  var _console = require("../util/console");
14
+ var _v = require("../util/v2");
14
15
  /**
15
16
  * Copyright 2020-2026 New Relic, Inc. All rights reserved.
16
17
  * SPDX-License-Identifier: Apache-2.0
@@ -31,7 +32,7 @@ const XHR_PROPS = ['open', 'send']; // these are the specific funcs being wrappe
31
32
  * @returns {Object} Scoped event emitter with a debug ID of `xhr`.
32
33
  */
33
34
  // eslint-disable-next-line
34
- function wrapXhr(sharedEE) {
35
+ function wrapXhr(sharedEE, agentRef) {
35
36
  var baseEE = sharedEE || _contextualEe.ee;
36
37
  const ee = scopedEE(baseEE);
37
38
  if (typeof _runtime.globalScope.XMLHttpRequest === 'undefined') return ee;
@@ -54,6 +55,7 @@ function wrapXhr(sharedEE) {
54
55
  function newXHR(opts) {
55
56
  const xhr = new OrigXHR(opts);
56
57
  const context = ee.context(xhr);
58
+ context.targets = (0, _v.findTargetsFromStackTrace)(agentRef);
57
59
  try {
58
60
  ee.emit('new-xhr', [xhr], context);
59
61
  xhr.addEventListener(READY_STATE_CHANGE, wrapXHR(context), (0, _eventListenerOpts.eventListenerOpts)(false));
@@ -14,6 +14,7 @@ var _aggregateBase = require("../../utils/aggregate-base");
14
14
  var _gql = require("./gql");
15
15
  var _belSerializer = require("../../../common/serialize/bel-serializer");
16
16
  var _nreum = require("../../../common/window/nreum");
17
+ var _v = require("../../../common/util/v2");
17
18
  /**
18
19
  * Copyright 2020-2026 New Relic, Inc. All rights reserved.
19
20
  * SPDX-License-Identifier: Apache-2.0
@@ -32,9 +33,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
32
33
  super.customAttributesAreSeparate = true;
33
34
  }
34
35
  (0, _registerHandler.registerHandler)('returnAjax', event => this.events.add(event), this.featureName, this.ee);
35
- (0, _registerHandler.registerHandler)('xhr', function () {
36
+ (0, _registerHandler.registerHandler)('xhr', function (params, metrics, startTime, endTime, type, target) {
36
37
  // the EE-drain system not only switches "this" but also passes a new EventContext with info. Should consider platform refactor to another system which passes a mutable context around separately and predictably to avoid problems like this.
37
- classThis.storeXhr(...arguments, this); // this switches the context back to the class instance while passing the NR context as an argument -- see "ctx" in storeXhr
38
+ classThis.storeXhr(params, metrics, startTime, endTime, type, target, this); // this switches the context back to the class instance while passing the NR context as an argument -- see "ctx" in storeXhr
38
39
  }, this.featureName, this.ee);
39
40
  this.ee.on('long-task', (task, originator) => {
40
41
  if (originator instanceof (0, _nreum.gosNREUMOriginals)().o.XHR) {
@@ -45,7 +46,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
45
46
  });
46
47
  this.waitForFlags([]).then(() => this.drain());
47
48
  }
48
- storeXhr(params, metrics, startTime, endTime, type, ctx) {
49
+ storeXhr(params, metrics, startTime, endTime, type, target, ctx) {
49
50
  metrics.time = startTime;
50
51
 
51
52
  // send to session traces
@@ -100,12 +101,28 @@ class Aggregate extends _aggregateBase.AggregateBase {
100
101
  query: ctx.parsedOrigin?.search
101
102
  });
102
103
  if (event.gql) this.reportSupportabilityMetric('Ajax/Events/GraphQL/Bytes-Added', (0, _stringify.stringify)(event.gql).length);
104
+
105
+ /** make a copy of the event for the MFE target if it exists */
106
+ if (target) {
107
+ this.events.add({
108
+ ...event,
109
+ targetAttributes: (0, _v.getVersion2Attributes)(target, this)
110
+ });
111
+ if ((0, _v.shouldDuplicate)(target, this)) this.reportContainerEvent({
112
+ ...event,
113
+ targetAttributes: (0, _v.getVersion2DuplicationAttributes)(target, this)
114
+ }, ctx);
115
+ } else {
116
+ this.reportContainerEvent(event, ctx);
117
+ }
118
+ }
119
+ reportContainerEvent(evt, ctx) {
103
120
  const softNavInUse = Boolean(this.agentRef.features?.[_features.FEATURE_NAMES.softNav]);
104
121
  if (softNavInUse) {
105
122
  // when SN is running, pass the event w/ info to it for evaluation -- either part of an interaction or is given back
106
- (0, _handle.handle)('ajax', [event, ctx], undefined, _features.FEATURE_NAMES.softNav, this.ee);
123
+ (0, _handle.handle)('ajax', [evt, ctx], undefined, _features.FEATURE_NAMES.softNav, this.ee);
107
124
  } else {
108
- this.events.add(event);
125
+ this.events.add(evt);
109
126
  }
110
127
  }
111
128
  serializer(eventBuffer) {
@@ -139,7 +156,8 @@ class Aggregate extends _aggregateBase.AggregateBase {
139
156
  // gql decorators are added as custom attributes to alleviate need for new BEL schema
140
157
  const attrParts = (0, _belSerializer.addCustomAttributes)({
141
158
  ...(jsAttributes || {}),
142
- ...(event.gql || {})
159
+ ...(event.gql || {}),
160
+ ...(event.targetAttributes || {}) // used to supply the version 2 attributes, either MFE target or duplication attributes for the main agent app
143
161
  }, addString);
144
162
  fields.unshift((0, _belSerializer.numeric)(attrParts.length));
145
163
  insert += fields.join(',');
@@ -23,7 +23,7 @@ var _now = require("../../../common/timing/now");
23
23
  var _denyList = require("../../../common/deny-list/deny-list");
24
24
  var _extractUrl = require("../../../common/url/extract-url");
25
25
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } /**
26
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
26
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
27
27
  * SPDX-License-Identifier: Apache-2.0
28
28
  */
29
29
  var handlers = ['load', 'error', 'abort', 'timeout'];
@@ -69,8 +69,8 @@ class Instrument extends _instrumentBase.InstrumentBase {
69
69
  } catch (err) {
70
70
  // do nothing
71
71
  }
72
- (0, _wrapFetch.wrapFetch)(this.ee);
73
- (0, _wrapXhr.wrapXhr)(this.ee);
72
+ (0, _wrapFetch.wrapFetch)(this.ee, agentRef);
73
+ (0, _wrapXhr.wrapXhr)(this.ee, agentRef);
74
74
  subscribeToEvents(agentRef, this.ee, this.handler, this.dt);
75
75
  this.importAggregator(agentRef, () => Promise.resolve().then(() => _interopRequireWildcard(require(/* webpackChunkName: "ajax-aggregate" */'../aggregate/index.js'))));
76
76
  }
@@ -294,12 +294,9 @@ function subscribeToEvents(agentRef, ee, handler, dt) {
294
294
  this.metrics = {};
295
295
  this.startTime = (0, _now.now)();
296
296
  this.dt = dtPayload;
297
- if (fetchArguments.length >= 1) this.target = fetchArguments[0];
298
- if (fetchArguments.length >= 2) this.opts = fetchArguments[1];
299
- var opts = this.opts || {};
300
- var target = this.target;
297
+ let [target, opts = {}] = fetchArguments;
301
298
  addUrl(this, (0, _extractUrl.extractUrl)(target));
302
- var method = ('' + (target && target instanceof origRequest && target.method || opts.method || 'GET')).toUpperCase();
299
+ const method = ('' + (target && target instanceof origRequest && target.method || opts.method || 'GET')).toUpperCase();
303
300
  this.params.method = method;
304
301
  this.body = opts.body;
305
302
  this.txSize = (0, _dataSize.dataSize)(opts.body) || 0;
@@ -324,7 +321,8 @@ function subscribeToEvents(agentRef, ee, handler, dt) {
324
321
  rxSize: responseSize,
325
322
  duration: (0, _now.now)() - this.startTime
326
323
  };
327
- handler('xhr', [this.params, metrics, this.startTime, this.endTime, 'fetch'], this, _features.FEATURE_NAMES.ajax);
324
+ const payload = [this.params, metrics, this.startTime, this.endTime, 'fetch'];
325
+ this.targets.forEach(target => reportToAgg(payload, this, target));
328
326
  }
329
327
 
330
328
  // Create report for XHR request that has finished
@@ -348,7 +346,11 @@ function subscribeToEvents(agentRef, ee, handler, dt) {
348
346
 
349
347
  // Always send cbTime, even if no noticeable time was taken.
350
348
  metrics.cbTime = this.cbTime;
351
- handler('xhr', [params, metrics, this.startTime, this.endTime, 'xhr'], this, _features.FEATURE_NAMES.ajax);
349
+ const payload = [params, metrics, this.startTime, this.endTime, 'xhr'];
350
+ this.targets.forEach(target => reportToAgg(payload, this, target));
351
+ }
352
+ function reportToAgg(payload, context, target) {
353
+ handler('xhr', [...payload, target], context, _features.FEATURE_NAMES.ajax);
352
354
  }
353
355
  function captureXhrData(ctx, xhr) {
354
356
  ctx.params.status = xhr.status;
@@ -23,7 +23,7 @@ var _parseUrl = require("../../../common/url/parse-url");
23
23
  var _extractUrl = require("../../../common/url/extract-url");
24
24
  var _wrapWebsocket = require("../../../common/wrap/wrap-websocket");
25
25
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } /**
26
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
26
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
27
27
  * SPDX-License-Identifier: Apache-2.0
28
28
  */
29
29
  class Instrument extends _instrumentBase.InstrumentBase {
@@ -44,8 +44,8 @@ class Instrument extends _instrumentBase.InstrumentBase {
44
44
  let historyEE, websocketsEE;
45
45
  if (websocketsEnabled) websocketsEE = (0, _wrapWebsocket.wrapWebSocket)(this.ee);
46
46
  if (_runtime.isBrowserScope) {
47
- (0, _wrapFetch.wrapFetch)(this.ee);
48
- (0, _wrapXhr.wrapXhr)(this.ee);
47
+ (0, _wrapFetch.wrapFetch)(this.ee, agentRef);
48
+ (0, _wrapXhr.wrapXhr)(this.ee, agentRef);
49
49
  historyEE = (0, _wrapHistory.wrapHistory)(this.ee);
50
50
  if (agentRef.init.user_actions.enabled) {
51
51
  _constants.OBSERVED_EVENTS.forEach(eventType => (0, _eventListenerOpts.windowAddEventListener)(eventType, evt => (0, _handle.handle)('ua', [evt], undefined, this.featureName, this.ee), true));
@@ -78,11 +78,6 @@ class Aggregate extends _aggregateBase.AggregateBase {
78
78
  const modeForThisLog = autoCaptured ? this.loggingMode.auto : this.loggingMode.api;
79
79
  if (!modeForThisLog) return;
80
80
  if (!attributes || typeof attributes !== 'object') attributes = {};
81
- attributes = {
82
- ...attributes,
83
- /** Specific attributes only supplied if harvesting to endpoint version 2 */
84
- ...(0, _v.getVersion2Attributes)(target, this)
85
- };
86
81
  if (typeof level === 'string') level = level.toUpperCase();
87
82
  if (!(0, _utils.isValidLogLevel)(level)) return (0, _console.warn)(30, level);
88
83
  if (modeForThisLog < (_constants.LOGGING_MODE[level] || Infinity)) {
@@ -105,8 +100,18 @@ class Aggregate extends _aggregateBase.AggregateBase {
105
100
  return;
106
101
  }
107
102
  if (typeof message !== 'string' || !message) return (0, _console.warn)(32);
108
- const log = new _log.Log(Math.floor(this.agentRef.runtime.timeKeeper.correctRelativeTimestamp(timestamp)), message, attributes, level);
109
- if (this.events.add(log)) this.reportSupportabilityMetric(LOGGING_EVENT + (autoCaptured ? 'Auto' : 'API') + '/Added');
103
+ const addEvent = attributes => {
104
+ const log = new _log.Log(Math.floor(this.agentRef.runtime.timeKeeper.correctRelativeTimestamp(timestamp)), message, attributes, level);
105
+ if (this.events.add(log)) this.reportSupportabilityMetric(LOGGING_EVENT + (autoCaptured ? 'Auto' : 'API') + '/Added');
106
+ };
107
+ addEvent({
108
+ ...attributes,
109
+ ...(0, _v.getVersion2Attributes)(target, this)
110
+ });
111
+ if ((0, _v.shouldDuplicate)(target, this)) addEvent({
112
+ ...attributes,
113
+ ...(0, _v.getVersion2DuplicationAttributes)(target, this)
114
+ });
110
115
  }
111
116
  serializer(eventBuffer) {
112
117
  const sessionEntity = this.agentRef.runtime.session;