@newrelic/browser-agent 1.312.1 → 1.313.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/dist/cjs/common/config/init-types.js +3 -2
- package/dist/cjs/common/config/init.js +10 -8
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/timing/time-keeper.js +28 -1
- package/dist/cjs/common/util/short-circuit.js +13 -0
- package/dist/cjs/common/util/submit-data.js +2 -9
- package/dist/cjs/common/v2/script-correlation.js +50 -0
- package/dist/cjs/common/v2/script-tracker.js +278 -0
- package/dist/cjs/common/{util/v2.js → v2/utils.js} +4 -4
- package/dist/cjs/common/wrap/wrap-fetch.js +2 -2
- package/dist/cjs/common/wrap/wrap-function.js +2 -2
- package/dist/cjs/common/wrap/wrap-xhr.js +2 -2
- package/dist/cjs/features/ajax/aggregate/index.js +4 -4
- package/dist/cjs/features/generic_events/aggregate/index.js +21 -2
- package/dist/cjs/features/generic_events/instrument/index.js +24 -21
- package/dist/cjs/features/jserrors/aggregate/index.js +206 -40
- package/dist/cjs/features/logging/aggregate/index.js +4 -4
- package/dist/cjs/features/metrics/instrument/index.js +1 -8
- package/dist/cjs/features/session_trace/aggregate/index.js +3 -4
- package/dist/cjs/features/session_trace/aggregate/trace/storage.js +2 -2
- package/dist/cjs/interfaces/registered-entity.js +7 -20
- package/dist/cjs/loaders/api/register-api-types.js +8 -8
- package/dist/cjs/loaders/api/register.js +49 -43
- package/dist/esm/common/config/init-types.js +3 -2
- package/dist/esm/common/config/init.js +10 -8
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/common/timing/time-keeper.js +28 -1
- package/dist/esm/common/util/short-circuit.js +6 -0
- package/dist/esm/common/util/submit-data.js +2 -9
- package/dist/esm/common/v2/script-correlation.js +43 -0
- package/dist/esm/common/v2/script-tracker.js +270 -0
- package/dist/esm/common/{util/v2.js → v2/utils.js} +4 -4
- package/dist/esm/common/wrap/wrap-fetch.js +1 -1
- package/dist/esm/common/wrap/wrap-function.js +1 -1
- package/dist/esm/common/wrap/wrap-xhr.js +1 -1
- package/dist/esm/features/ajax/aggregate/index.js +1 -1
- package/dist/esm/features/generic_events/aggregate/index.js +20 -1
- package/dist/esm/features/generic_events/instrument/index.js +24 -21
- package/dist/esm/features/jserrors/aggregate/index.js +205 -39
- package/dist/esm/features/logging/aggregate/index.js +1 -1
- package/dist/esm/features/metrics/instrument/index.js +2 -9
- package/dist/esm/features/session_trace/aggregate/index.js +4 -5
- package/dist/esm/features/session_trace/aggregate/trace/storage.js +2 -2
- package/dist/esm/interfaces/registered-entity.js +7 -20
- package/dist/esm/loaders/api/register-api-types.js +8 -8
- package/dist/esm/loaders/api/register.js +46 -41
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/common/config/init-types.d.ts +10 -8
- package/dist/types/common/config/init-types.d.ts.map +1 -1
- package/dist/types/common/config/init.d.ts.map +1 -1
- package/dist/types/common/timing/time-keeper.d.ts.map +1 -1
- package/dist/types/common/util/short-circuit.d.ts +8 -0
- package/dist/types/common/util/short-circuit.d.ts.map +1 -0
- package/dist/types/common/util/submit-data.d.ts.map +1 -1
- package/dist/types/common/v2/script-correlation.d.ts +38 -0
- package/dist/types/common/v2/script-correlation.d.ts.map +1 -0
- package/dist/types/common/{util → v2}/script-tracker.d.ts +3 -0
- package/dist/types/common/v2/script-tracker.d.ts.map +1 -0
- package/dist/types/common/{util/v2.d.ts → v2/utils.d.ts} +1 -1
- package/dist/types/common/v2/utils.d.ts.map +1 -0
- package/dist/types/features/generic_events/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/generic_events/instrument/index.d.ts +1 -1
- package/dist/types/features/generic_events/instrument/index.d.ts.map +1 -1
- package/dist/types/features/jserrors/aggregate/index.d.ts +25 -16
- package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/metrics/instrument/index.d.ts.map +1 -1
- package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
- package/dist/types/interfaces/registered-entity.d.ts +0 -10
- package/dist/types/interfaces/registered-entity.d.ts.map +1 -1
- package/dist/types/loaders/api/register-api-types.d.ts +15 -15
- package/dist/types/loaders/api/register-api-types.d.ts.map +1 -1
- package/dist/types/loaders/api/register.d.ts +6 -0
- package/dist/types/loaders/api/register.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/common/config/init-types.js +3 -2
- package/src/common/config/init.js +6 -4
- package/src/common/timing/time-keeper.js +30 -1
- package/src/common/util/short-circuit.js +6 -0
- package/src/common/util/submit-data.js +2 -8
- package/src/common/v2/script-correlation.js +44 -0
- package/src/common/v2/script-tracker.js +260 -0
- package/src/common/{util/v2.js → v2/utils.js} +4 -4
- package/src/common/wrap/wrap-fetch.js +1 -1
- package/src/common/wrap/wrap-function.js +1 -1
- package/src/common/wrap/wrap-xhr.js +1 -1
- package/src/features/ajax/aggregate/index.js +1 -1
- package/src/features/generic_events/aggregate/index.js +20 -1
- package/src/features/generic_events/instrument/index.js +25 -22
- package/src/features/jserrors/aggregate/index.js +200 -43
- package/src/features/logging/aggregate/index.js +1 -1
- package/src/features/metrics/instrument/index.js +2 -13
- package/src/features/session_trace/aggregate/index.js +4 -6
- package/src/features/session_trace/aggregate/trace/storage.js +2 -2
- package/src/interfaces/registered-entity.js +8 -20
- package/src/loaders/api/register-api-types.js +8 -8
- package/src/loaders/api/register.js +42 -34
- package/dist/cjs/common/util/script-tracker.js +0 -204
- package/dist/esm/common/util/script-tracker.js +0 -196
- package/dist/types/common/util/script-tracker.d.ts.map +0 -1
- package/dist/types/common/util/v2.d.ts.map +0 -1
- package/src/common/util/script-tracker.js +0 -189
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.313.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.312.1...v1.313.0) (2026-04-14)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* Apply legal and compliance requirements to register API ([#1742](https://github.com/newrelic/newrelic-browser-agent/issues/1742)) ([24199ee](https://github.com/newrelic/newrelic-browser-agent/commit/24199ee75c0e8d1e74d7d882526fd868b87a93ae))
|
|
12
|
+
* Automatically Detect MFE Errors ([#1720](https://github.com/newrelic/newrelic-browser-agent/issues/1720)) ([281617a](https://github.com/newrelic/newrelic-browser-agent/commit/281617a351459afe82c52e0ce657d3f7b1b5f19c))
|
|
13
|
+
* Improve MicroFrontEndTiming heuristics ([#1735](https://github.com/newrelic/newrelic-browser-agent/issues/1735)) ([e59c60a](https://github.com/newrelic/newrelic-browser-agent/commit/e59c60af34e8f2408ffa4835dc6db4fdcbf756ba))
|
|
14
|
+
* remove "withCredentials" ([#1747](https://github.com/newrelic/newrelic-browser-agent/issues/1747)) ([e84c292](https://github.com/newrelic/newrelic-browser-agent/commit/e84c292b9d17918de0e8e1542fe363e28fa67d33))
|
|
15
|
+
* Report CSP violations as new events ([#1736](https://github.com/newrelic/newrelic-browser-agent/issues/1736)) ([65b5b98](https://github.com/newrelic/newrelic-browser-agent/commit/65b5b988c6cb8d852ad952a3eee94a54864afcd2))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
|
|
20
|
+
* Bad `trace.lastTimestamp` in Safari + back nav edge case ([#1754](https://github.com/newrelic/newrelic-browser-agent/issues/1754)) ([a607ee1](https://github.com/newrelic/newrelic-browser-agent/commit/a607ee1b34bb57fb800f0220074cd27d1f86a32d))
|
|
21
|
+
|
|
6
22
|
## [1.312.1](https://github.com/newrelic/newrelic-browser-agent/compare/v1.312.0...v1.312.1) (2026-04-02)
|
|
7
23
|
|
|
8
24
|
|
|
@@ -17,8 +17,9 @@ exports.default = void 0;
|
|
|
17
17
|
* @property {boolean} [ajax.enabled] - Turn on/off the ajax feature (on by default).
|
|
18
18
|
* @property {boolean} [ajax.autoStart] - If true, the agent will automatically start the ajax feature. Otherwise, it will be in a deferred state until the `start` API method is called.
|
|
19
19
|
* @property {Object} [api]
|
|
20
|
-
* @property {
|
|
21
|
-
* @property {boolean} [api.
|
|
20
|
+
* @property {Object} [api.register]
|
|
21
|
+
* @property {boolean} [api.register.enabled] - If true, the agent will allow registered children to be sent to the server.
|
|
22
|
+
* @property {boolean} [api.register.duplicate_data_to_container] - If true, the agent will capture registered child data to the main agent as well as the registered child.
|
|
22
23
|
* @property {Object} [distributed_tracing]
|
|
23
24
|
* @property {boolean} [distributed_tracing.enabled] - If true, distributed tracing headers will be added to outgoing requests. Requires ajax feature to be running.
|
|
24
25
|
* @property {boolean} [distributed_tracing.exclude_newrelic_header]
|
|
@@ -27,7 +27,7 @@ const InitModelFn = () => {
|
|
|
27
27
|
const hiddenState = {
|
|
28
28
|
feature_flags: [],
|
|
29
29
|
experimental: {
|
|
30
|
-
|
|
30
|
+
register: false,
|
|
31
31
|
resources: false
|
|
32
32
|
},
|
|
33
33
|
mask_selector: '*',
|
|
@@ -60,13 +60,15 @@ const InitModelFn = () => {
|
|
|
60
60
|
autoStart: true
|
|
61
61
|
},
|
|
62
62
|
api: {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
63
|
+
register: {
|
|
64
|
+
get enabled() {
|
|
65
|
+
return hiddenState.feature_flags.includes(_constants.FEATURE_FLAGS.REGISTER) || hiddenState.experimental.register;
|
|
66
|
+
},
|
|
67
|
+
set enabled(val) {
|
|
68
|
+
hiddenState.experimental.register = val;
|
|
69
|
+
},
|
|
70
|
+
duplicate_data_to_container: false
|
|
71
|
+
}
|
|
70
72
|
},
|
|
71
73
|
browser_consent_mode: {
|
|
72
74
|
enabled: 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.
|
|
20
|
+
const VERSION = exports.VERSION = "1.313.0-rc.0";
|
|
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.
|
|
20
|
+
const VERSION = exports.VERSION = "1.313.0-rc.0";
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Exposes the build type of the agent
|
|
@@ -4,10 +4,13 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.TimeKeeper = void 0;
|
|
7
|
+
var _constants = require("../../features/metrics/constants");
|
|
7
8
|
var _runtime = require("../constants/runtime");
|
|
8
9
|
var _monkeyPatched = require("../util/monkey-patched");
|
|
10
|
+
var _handle = require("../event-emitter/handle");
|
|
11
|
+
var _features = require("../../loaders/features/features");
|
|
9
12
|
/**
|
|
10
|
-
* Copyright 2020-
|
|
13
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
11
14
|
* SPDX-License-Identifier: Apache-2.0
|
|
12
15
|
*/
|
|
13
16
|
|
|
@@ -42,11 +45,31 @@ class TimeKeeper {
|
|
|
42
45
|
* @type {boolean}
|
|
43
46
|
*/
|
|
44
47
|
#ready = false;
|
|
48
|
+
#reportedDrift = false;
|
|
45
49
|
constructor(sessionObj) {
|
|
46
50
|
this.#session = sessionObj;
|
|
47
51
|
this.processStoredDiff();
|
|
48
52
|
(0, _monkeyPatched.isNative)(performance.now, Date.now); // will warn the user if these are not native functions. We need these to be native for time in the agent to be accurate in general.
|
|
49
53
|
}
|
|
54
|
+
#detectDrift() {
|
|
55
|
+
if (this.#reportedDrift) return;
|
|
56
|
+
try {
|
|
57
|
+
// Drift detection: measures if performance.now() and Date.now() have become desynchronized
|
|
58
|
+
// This can happen when a machine sleeps and the performance timer freezes while Date continues
|
|
59
|
+
// this can also happen when a user sets their clock forward during the page lifecycle,
|
|
60
|
+
// but we have no way of distinguishing that from actual clock drift so we will just treat it as drift.
|
|
61
|
+
// In either case, the performance timestamps would be inaccurate at that point so we want to detect and report a count of it.
|
|
62
|
+
// We only detect positive drift (performance clock falling behind Date clock)
|
|
63
|
+
// Note: localTimeDiff (server time offset) is NOT part of drift - that's a legitimate offset
|
|
64
|
+
const drift = Date.now() - _runtime.originTime - performance.now();
|
|
65
|
+
if (drift > 1000) {
|
|
66
|
+
this.#reportedDrift = true;
|
|
67
|
+
(0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, ['Generic/TimeKeeper/ClockDrift/Detected', drift], undefined, _features.FEATURE_NAMES.metrics, this.#session.agentRef.ee);
|
|
68
|
+
}
|
|
69
|
+
} catch (err) {
|
|
70
|
+
// Silently ignore drift detection errors to avoid breaking normal operation
|
|
71
|
+
}
|
|
72
|
+
}
|
|
50
73
|
get ready() {
|
|
51
74
|
return this.#ready;
|
|
52
75
|
}
|
|
@@ -91,6 +114,7 @@ class TimeKeeper {
|
|
|
91
114
|
* @returns {number} Corrected unix/epoch timestamp
|
|
92
115
|
*/
|
|
93
116
|
convertRelativeTimestamp(relativeTime) {
|
|
117
|
+
this.#detectDrift();
|
|
94
118
|
return _runtime.originTime + relativeTime;
|
|
95
119
|
}
|
|
96
120
|
|
|
@@ -101,6 +125,7 @@ class TimeKeeper {
|
|
|
101
125
|
* @returns {number}
|
|
102
126
|
*/
|
|
103
127
|
convertAbsoluteTimestamp(timestamp) {
|
|
128
|
+
this.#detectDrift();
|
|
104
129
|
return timestamp - _runtime.originTime;
|
|
105
130
|
}
|
|
106
131
|
|
|
@@ -110,6 +135,7 @@ class TimeKeeper {
|
|
|
110
135
|
* @return {number} Corrected unix/epoch timestamp
|
|
111
136
|
*/
|
|
112
137
|
correctAbsoluteTimestamp(timestamp) {
|
|
138
|
+
this.#detectDrift();
|
|
113
139
|
return timestamp - this.#localTimeDiff;
|
|
114
140
|
}
|
|
115
141
|
|
|
@@ -119,6 +145,7 @@ class TimeKeeper {
|
|
|
119
145
|
* @returns {number}
|
|
120
146
|
*/
|
|
121
147
|
correctRelativeTimestamp(relativeTime) {
|
|
148
|
+
this.#detectDrift();
|
|
122
149
|
return this.correctAbsoluteTimestamp(this.convertRelativeTimestamp(relativeTime));
|
|
123
150
|
}
|
|
124
151
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.ShortCircuit = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
9
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
10
|
+
*/
|
|
11
|
+
/** A special error used to short-circuit the error processing pipeline */
|
|
12
|
+
class ShortCircuit extends Error {}
|
|
13
|
+
exports.ShortCircuit = ShortCircuit;
|
|
@@ -9,7 +9,7 @@ exports.xhr = xhr;
|
|
|
9
9
|
exports.xhrFetch = xhrFetch;
|
|
10
10
|
var _runtime = require("../constants/runtime");
|
|
11
11
|
/**
|
|
12
|
-
* Copyright 2020-
|
|
12
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
13
13
|
* SPDX-License-Identifier: Apache-2.0
|
|
14
14
|
*/
|
|
15
15
|
|
|
@@ -70,8 +70,7 @@ function xhrFetch({
|
|
|
70
70
|
return fetch(url, {
|
|
71
71
|
headers: objHeaders,
|
|
72
72
|
method,
|
|
73
|
-
body
|
|
74
|
-
credentials: 'include'
|
|
73
|
+
body
|
|
75
74
|
});
|
|
76
75
|
}
|
|
77
76
|
|
|
@@ -97,12 +96,6 @@ function xhr({
|
|
|
97
96
|
}) {
|
|
98
97
|
const request = new XMLHttpRequest();
|
|
99
98
|
request.open(method, url, !sync);
|
|
100
|
-
try {
|
|
101
|
-
// Set cookie
|
|
102
|
-
if ('withCredentials' in request) request.withCredentials = true;
|
|
103
|
-
} catch (e) {
|
|
104
|
-
// do nothing
|
|
105
|
-
}
|
|
106
99
|
headers.forEach(header => {
|
|
107
100
|
request.setRequestHeader(header.key, header.value);
|
|
108
101
|
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.ScriptCorrelation = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
9
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Represents script correlation data combining DOM and Performance API information
|
|
13
|
+
*/
|
|
14
|
+
class ScriptCorrelation {
|
|
15
|
+
/** @type {CorrelationTiming} [dom] - DOM-related information */
|
|
16
|
+
dom = new CorrelationTiming();
|
|
17
|
+
/** @type {CorrelationTiming} [performance] - Performance-related information */
|
|
18
|
+
performance = new CorrelationTiming();
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Creates a new ScriptCorrelation instance
|
|
22
|
+
* @param {string} url - The cleaned URL of the script
|
|
23
|
+
*/
|
|
24
|
+
constructor(url) {
|
|
25
|
+
/** @type {string} The cleaned URL of the script */
|
|
26
|
+
this.url = url;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Gets the script timing, using DOM timings if available, otherwise falling back to performance timings or registeredAt as appropriate. This is used to provide the most accurate script timing possible for registered entities.
|
|
31
|
+
* @returns {{start: number, end: number}
|
|
32
|
+
*/
|
|
33
|
+
get script() {
|
|
34
|
+
const start = Math.max(this.dom.start, this.performance.end);
|
|
35
|
+
const end = Math.max(this.dom.end, this.performance.end, start);
|
|
36
|
+
return {
|
|
37
|
+
start,
|
|
38
|
+
end
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
exports.ScriptCorrelation = ScriptCorrelation;
|
|
43
|
+
class CorrelationTiming {
|
|
44
|
+
/** @type {number} [start] - The startTime from the performance entry */
|
|
45
|
+
start = 0;
|
|
46
|
+
/** @type {number} [end] - The responseEnd from the performance entry */
|
|
47
|
+
end = 0;
|
|
48
|
+
/** @type {*} [value] - The entry value */
|
|
49
|
+
value = undefined;
|
|
50
|
+
}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.extractUrlsFromStack = extractUrlsFromStack;
|
|
7
|
+
exports.findScriptTimings = findScriptTimings;
|
|
8
|
+
exports.getDeepStackTrace = getDeepStackTrace;
|
|
9
|
+
exports.thisFile = exports.scriptCorrelations = void 0;
|
|
10
|
+
var _runtime = require("../constants/runtime");
|
|
11
|
+
var _now = require("../timing/now");
|
|
12
|
+
var _cleanUrl = require("../url/clean-url");
|
|
13
|
+
var _browserStackMatchers = require("../util/browser-stack-matchers");
|
|
14
|
+
var _scriptCorrelation = require("./script-correlation");
|
|
15
|
+
/**
|
|
16
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
17
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @typedef {import('./register-api-types').RegisterAPITimings} RegisterAPITimings
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/** export for testing purposes */
|
|
25
|
+
let thisFile = exports.thisFile = void 0;
|
|
26
|
+
try {
|
|
27
|
+
exports.thisFile = thisFile = extractUrlsFromStack(getDeepStackTrace())[0];
|
|
28
|
+
} catch (err) {
|
|
29
|
+
exports.thisFile = thisFile = extractUrlsFromStack(err)[0];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** @type {(entry: PerformanceEntry) => boolean} - A shared function to determine if a performance entry is a valid script or link resource for evaluation */
|
|
33
|
+
const validEntryCriteria = entry => entry.initiatorType === 'script' || ['link', 'fetch'].includes(entry.initiatorType) && entry.name.endsWith('.js');
|
|
34
|
+
|
|
35
|
+
/** @type {Map<string, ScriptCorrelation>} - Central registry for script correlations containing both DOM and Performance data */
|
|
36
|
+
const scriptCorrelations = exports.scriptCorrelations = new Map();
|
|
37
|
+
/** @type {Array<{ test: (entry: PerformanceEntry) => boolean, addedAt: number }>} an array of PerformanceObserver subscribers to check for late emissions */
|
|
38
|
+
let poSubscribers = [];
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Retrieves a script correlation by URL using exact matching
|
|
42
|
+
* @param {string} targetUrl - The URL to find
|
|
43
|
+
* @returns {ScriptCorrelation | undefined} - The correlation object if found
|
|
44
|
+
*/
|
|
45
|
+
function findCorrelation(targetUrl) {
|
|
46
|
+
return scriptCorrelations.get(targetUrl);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Gets or creates a script correlation entry
|
|
51
|
+
* @param {string} url - The cleaned URL
|
|
52
|
+
* @returns {ScriptCorrelation} - The correlation object
|
|
53
|
+
*/
|
|
54
|
+
function getOrCreateCorrelation(url) {
|
|
55
|
+
const existing = findCorrelation(url);
|
|
56
|
+
if (existing) return existing;
|
|
57
|
+
const correlation = new _scriptCorrelation.ScriptCorrelation(url);
|
|
58
|
+
scriptCorrelations.set(url, correlation);
|
|
59
|
+
|
|
60
|
+
// Keep size under control
|
|
61
|
+
if (scriptCorrelations.size > 1000) {
|
|
62
|
+
const firstKey = scriptCorrelations.keys().next().value;
|
|
63
|
+
scriptCorrelations.delete(firstKey);
|
|
64
|
+
}
|
|
65
|
+
return correlation;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Set up a MutationObserver to detect script elements being added to the DOM */
|
|
69
|
+
if (_runtime.globalScope.MutationObserver && _runtime.globalScope.document) {
|
|
70
|
+
const scriptMutationObserver = new MutationObserver(mutations => {
|
|
71
|
+
mutations.forEach(mutation => {
|
|
72
|
+
mutation.addedNodes.forEach(node => {
|
|
73
|
+
if (node.nodeName === 'SCRIPT' && node.src) {
|
|
74
|
+
const cleanedSrc = (0, _cleanUrl.cleanURL)(node.src);
|
|
75
|
+
const correlation = getOrCreateCorrelation(cleanedSrc);
|
|
76
|
+
correlation.dom.start = (0, _now.now)();
|
|
77
|
+
correlation.dom.value = node;
|
|
78
|
+
const setEnd = () => {
|
|
79
|
+
correlation.dom.end = (0, _now.now)();
|
|
80
|
+
};
|
|
81
|
+
['load', 'error'].forEach(event => node.addEventListener(event, setEnd, {
|
|
82
|
+
once: true
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
scriptMutationObserver.observe(_runtime.globalScope.document, {
|
|
89
|
+
childList: true,
|
|
90
|
+
subtree: true
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
if (_runtime.globalScope.PerformanceObserver?.supportedEntryTypes.includes('resource')) {
|
|
94
|
+
/** We must track the script assets this way, because the performance buffer can fill up and when it does that
|
|
95
|
+
* it stops accepting new entries (instead of dropping old entries), which means if the register API is called
|
|
96
|
+
* after the buffer fills up we won't be able to get the script timing information from the resource timing API
|
|
97
|
+
*/
|
|
98
|
+
const scriptObserver = new PerformanceObserver(list => {
|
|
99
|
+
list.getEntries().filter(validEntryCriteria).forEach(entry => {
|
|
100
|
+
// Update correlation with performance data (creates entry if needed)
|
|
101
|
+
const entryUrl = (0, _cleanUrl.cleanURL)(entry.name);
|
|
102
|
+
const correlation = getOrCreateCorrelation(entryUrl);
|
|
103
|
+
correlation.performance.start = Math.floor(entry.startTime);
|
|
104
|
+
correlation.performance.end = Math.floor(entry.responseEnd);
|
|
105
|
+
correlation.performance.value = entry;
|
|
106
|
+
|
|
107
|
+
// Clear resolved or expired subscribers
|
|
108
|
+
const canClear = [];
|
|
109
|
+
poSubscribers.forEach(({
|
|
110
|
+
test,
|
|
111
|
+
addedAt
|
|
112
|
+
}, idx) => {
|
|
113
|
+
if (test(entry) || (0, _now.now)() - addedAt > 10000) canClear.push(idx);
|
|
114
|
+
});
|
|
115
|
+
poSubscribers = poSubscribers.filter((_, idx) => !canClear.includes(idx));
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
scriptObserver.observe({
|
|
119
|
+
type: 'resource',
|
|
120
|
+
buffered: true
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Extracts URLs from stack traces using the same logic as compute-stack-trace.js
|
|
126
|
+
* @param {string} stack The error stack trace
|
|
127
|
+
* @returns {string[]} Array of cleaned URLs found in the stack trace
|
|
128
|
+
*/
|
|
129
|
+
function extractUrlsFromStack(stack) {
|
|
130
|
+
if (!stack || typeof stack !== 'string') return [];
|
|
131
|
+
const urls = new Set();
|
|
132
|
+
const lines = stack.split('\n');
|
|
133
|
+
for (const line of lines) {
|
|
134
|
+
// Try gecko format first, then chrome
|
|
135
|
+
const parts = line.match(_browserStackMatchers.gecko) || line.match(_browserStackMatchers.chrome) || line.match(_browserStackMatchers.chromeEval);
|
|
136
|
+
if (parts && parts[2]) {
|
|
137
|
+
urls.add((0, _cleanUrl.cleanURL)(parts[2]));
|
|
138
|
+
} else {
|
|
139
|
+
// Fallback: match URLs using a generic .js pattern (non-greedy to handle ports and query params)
|
|
140
|
+
const fallbackMatch = line.match(/\(([^)]+\.js):\d+:\d+\)/) || line.match(/^\s+at\s+([^\s(]+\.js):\d+:\d+/);
|
|
141
|
+
if (fallbackMatch && fallbackMatch[1]) {
|
|
142
|
+
urls.add((0, _cleanUrl.cleanURL)(fallbackMatch[1]));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return [...urls];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Returns a deep stack trace by temporarily increasing the stack trace limit.
|
|
151
|
+
* @returns {Error.stack | undefined}
|
|
152
|
+
*/
|
|
153
|
+
function getDeepStackTrace() {
|
|
154
|
+
let stack;
|
|
155
|
+
try {
|
|
156
|
+
const originalStackLimit = Error.stackTraceLimit;
|
|
157
|
+
Error.stackTraceLimit = 50;
|
|
158
|
+
stack = new Error().stack;
|
|
159
|
+
Error.stackTraceLimit = originalStackLimit;
|
|
160
|
+
} catch (e) {
|
|
161
|
+
stack = new Error().stack;
|
|
162
|
+
}
|
|
163
|
+
return stack;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Indicates whether the provided URL matches any script preload link tags in the document.
|
|
168
|
+
* @param {string} targetUrl The URL to match against preload tags
|
|
169
|
+
* @returns {boolean} True if a matching preload link is found, false otherwise
|
|
170
|
+
*/
|
|
171
|
+
function wasPreloaded(targetUrl) {
|
|
172
|
+
if (!targetUrl || !_runtime.globalScope.document) return false;
|
|
173
|
+
try {
|
|
174
|
+
const linkTags = _runtime.globalScope.document.querySelectorAll('link[rel="preload"][as="script"]');
|
|
175
|
+
for (const link of linkTags) {
|
|
176
|
+
// link.href is resolved to an absolute URL by the browser (even if supplied as relative), so we can match exactly against the cleaned target URL
|
|
177
|
+
if ((0, _cleanUrl.cleanURL)(link.href) === targetUrl) return true;
|
|
178
|
+
}
|
|
179
|
+
} catch (error) {
|
|
180
|
+
// Don't let DOM parsing errors break anything
|
|
181
|
+
}
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Checks if a performance entry matches the target MFE script URL using exact matching
|
|
187
|
+
* @param {PerformanceResourceTiming} entry - The resource timing entry
|
|
188
|
+
* @param {string} targetUrl - The MFE script URL to match
|
|
189
|
+
* @returns {boolean} True if the entry matches
|
|
190
|
+
*/
|
|
191
|
+
function entryMatchesUrl(entry, targetUrl) {
|
|
192
|
+
const entryUrl = (0, _cleanUrl.cleanURL)(entry.name);
|
|
193
|
+
return entryUrl === targetUrl;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Applies performance entry timing data to a timings object
|
|
198
|
+
* @param {RegisterAPITimings} timings - The timings object to update
|
|
199
|
+
* @param {PerformanceResourceTiming} entry - The performance entry
|
|
200
|
+
*/
|
|
201
|
+
function applyPerformanceEntry(timings, entry) {
|
|
202
|
+
timings.fetchStart = Math.floor(entry.startTime);
|
|
203
|
+
timings.fetchEnd = Math.floor(entry.responseEnd);
|
|
204
|
+
timings.asset = entry.name;
|
|
205
|
+
timings.type = entry.initiatorType;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Uses the stack of the initiator function, returns script timing information if a script can be found with the resource timing API matching the URL found in the stack.
|
|
210
|
+
* @returns {RegisterAPITimings} Object containing script fetch start and end times, and the asset URL if found
|
|
211
|
+
*/
|
|
212
|
+
function findScriptTimings() {
|
|
213
|
+
const timings = {
|
|
214
|
+
registeredAt: (0, _now.now)(),
|
|
215
|
+
reportedAt: undefined,
|
|
216
|
+
fetchStart: 0,
|
|
217
|
+
fetchEnd: 0,
|
|
218
|
+
scriptStart: 0,
|
|
219
|
+
scriptEnd: 0,
|
|
220
|
+
asset: undefined,
|
|
221
|
+
type: 'unknown'
|
|
222
|
+
};
|
|
223
|
+
const stack = getDeepStackTrace();
|
|
224
|
+
if (!stack) return timings;
|
|
225
|
+
const navUrl = _runtime.globalScope.performance?.getEntriesByType('navigation')?.[0]?.name || '';
|
|
226
|
+
try {
|
|
227
|
+
const urls = extractUrlsFromStack(stack);
|
|
228
|
+
// Filter out agent file from URLs (unless it's the only one)
|
|
229
|
+
const mfeScriptUrl = (urls.length > 1 ? urls.filter(line => thisFile !== line) : urls)[0];
|
|
230
|
+
if (!mfeScriptUrl) return timings;
|
|
231
|
+
|
|
232
|
+
// Check for inline script
|
|
233
|
+
if (navUrl.includes(mfeScriptUrl)) {
|
|
234
|
+
timings.asset = (0, _cleanUrl.cleanURL)(navUrl);
|
|
235
|
+
timings.type = 'inline';
|
|
236
|
+
return timings;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Get correlation data
|
|
240
|
+
timings.correlation = findCorrelation(mfeScriptUrl);
|
|
241
|
+
|
|
242
|
+
// Use correlation's performance entry if available, otherwise check live performance API
|
|
243
|
+
const performanceEntry = timings.correlation?.performance.value || performance.getEntriesByType('resource').find(e => entryMatchesUrl(e, mfeScriptUrl));
|
|
244
|
+
if (performanceEntry) {
|
|
245
|
+
applyPerformanceEntry(timings, performanceEntry);
|
|
246
|
+
} else if (wasPreloaded(mfeScriptUrl)) {
|
|
247
|
+
// Handle preloaded scripts that may report late
|
|
248
|
+
timings.asset = mfeScriptUrl;
|
|
249
|
+
timings.type = 'preload';
|
|
250
|
+
|
|
251
|
+
// Subscribe to late performance observer callbacks
|
|
252
|
+
poSubscribers.push({
|
|
253
|
+
addedAt: (0, _now.now)(),
|
|
254
|
+
test: entry => {
|
|
255
|
+
if (entryMatchesUrl(entry, mfeScriptUrl)) {
|
|
256
|
+
applyPerformanceEntry(timings, entry);
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/*
|
|
265
|
+
* Use getters here because the correlation data may arrive after this function returns the timing object, and we want to provide the most up-to-date timing information possible when the getters are accessed at harvest time.
|
|
266
|
+
* The getters will fall back to fetchEnd if correlation data isn't available yet, which is our best approximation for script execution start when actual script timings can not be determined.
|
|
267
|
+
*/
|
|
268
|
+
Object.defineProperty(timings, 'scriptStart', {
|
|
269
|
+
get: () => timings.correlation?.script.start || timings.fetchEnd
|
|
270
|
+
});
|
|
271
|
+
Object.defineProperty(timings, 'scriptEnd', {
|
|
272
|
+
get: () => timings.correlation?.script.end || timings.registeredAt
|
|
273
|
+
});
|
|
274
|
+
} catch (error) {
|
|
275
|
+
// Don't let stack parsing errors break anything
|
|
276
|
+
}
|
|
277
|
+
return timings;
|
|
278
|
+
}
|
|
@@ -34,7 +34,7 @@ const V2_TYPES = exports.V2_TYPES = {
|
|
|
34
34
|
* @returns {import("../../interfaces/registered-entity").RegisterAPIMetadataTarget[]}
|
|
35
35
|
*/
|
|
36
36
|
function getRegisteredTargetsFromId(id, agentRef) {
|
|
37
|
-
if (!id || !agentRef?.init.api.
|
|
37
|
+
if (!id || !agentRef?.init.api.register.enabled) return [];
|
|
38
38
|
const registeredEntities = agentRef.runtime.registeredEntities;
|
|
39
39
|
return registeredEntities?.filter(entity => String(entity.metadata.target.id) === String(id)).map(entity => entity.metadata.target) || [];
|
|
40
40
|
}
|
|
@@ -46,7 +46,7 @@ function getRegisteredTargetsFromId(id, agentRef) {
|
|
|
46
46
|
* @returns {import("../../interfaces/registered-entity").RegisterAPIMetadataTarget[]}
|
|
47
47
|
*/
|
|
48
48
|
function getRegisteredTargetsFromFilename(filename, agentRef) {
|
|
49
|
-
if (!filename || !agentRef?.init.api.
|
|
49
|
+
if (!filename || !agentRef?.init.api.register.enabled) return [];
|
|
50
50
|
const registeredEntities = agentRef.runtime.registeredEntities;
|
|
51
51
|
return registeredEntities?.filter(entity => entity.metadata.timings?.asset?.endsWith(filename)).map(entity => entity.metadata.target) || [];
|
|
52
52
|
}
|
|
@@ -98,7 +98,7 @@ function getVersion2DuplicationAttributes(target, aggregateInstance) {
|
|
|
98
98
|
* @returns {boolean} returns true if the event should be duplicated for the target, false otherwise
|
|
99
99
|
*/
|
|
100
100
|
function shouldDuplicate(target, aggregateInstance) {
|
|
101
|
-
return !!target && !!supportsV2(aggregateInstance) && aggregateInstance.agentRef.init.api.
|
|
101
|
+
return !!target && !!supportsV2(aggregateInstance) && aggregateInstance.agentRef.init.api.register.duplicate_data_to_container;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
/**
|
|
@@ -107,7 +107,7 @@ function shouldDuplicate(target, aggregateInstance) {
|
|
|
107
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
108
|
*/
|
|
109
109
|
function findTargetsFromStackTrace(agentRef) {
|
|
110
|
-
if (!agentRef?.init.api.
|
|
110
|
+
if (!agentRef?.init.api.register.enabled) return [undefined];
|
|
111
111
|
const targets = [];
|
|
112
112
|
try {
|
|
113
113
|
var urls = (0, _scriptTracker.extractUrlsFromStack)((0, _scriptTracker.getDeepStackTrace)());
|
|
@@ -7,7 +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
|
|
10
|
+
var _utils = require("../v2/utils");
|
|
11
11
|
/**
|
|
12
12
|
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
13
13
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -79,7 +79,7 @@ function wrapFetch(sharedEE, agentRef) {
|
|
|
79
79
|
target[name] = function () {
|
|
80
80
|
var args = [...arguments];
|
|
81
81
|
const ctx = {};
|
|
82
|
-
const targets = (0,
|
|
82
|
+
const targets = (0, _utils.findTargetsFromStackTrace)(agentRef);
|
|
83
83
|
|
|
84
84
|
// we are wrapping args in an array so we can preserve the reference
|
|
85
85
|
ee.emit(prefix + 'before-start', [args], ctx);
|
|
@@ -8,7 +8,7 @@ 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
|
|
11
|
+
var _utils = require("../v2/utils");
|
|
12
12
|
/**
|
|
13
13
|
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
14
14
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -89,7 +89,7 @@ function createWrapperWithEmitter(emitter, always, agentRef) {
|
|
|
89
89
|
|
|
90
90
|
// certain wrappers can inform the function wrapper to evaluate the stack of the executed wrapped function to find targets of the execution
|
|
91
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,
|
|
92
|
+
targets = evaluateStack ? (0, _utils.findTargetsFromStackTrace)(agentRef) : [undefined]; // undefined target always maps to the container agent
|
|
93
93
|
|
|
94
94
|
if (typeof getContext === 'function') {
|
|
95
95
|
ctx = getContext(args, originalThis);
|
|
@@ -11,7 +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
|
|
14
|
+
var _utils = require("../v2/utils");
|
|
15
15
|
/**
|
|
16
16
|
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
17
17
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -55,7 +55,7 @@ function wrapXhr(sharedEE, agentRef) {
|
|
|
55
55
|
function newXHR(opts) {
|
|
56
56
|
const xhr = new OrigXHR(opts);
|
|
57
57
|
const context = ee.context(xhr);
|
|
58
|
-
context.targets = (0,
|
|
58
|
+
context.targets = (0, _utils.findTargetsFromStackTrace)(agentRef);
|
|
59
59
|
try {
|
|
60
60
|
ee.emit('new-xhr', [xhr], context);
|
|
61
61
|
xhr.addEventListener(READY_STATE_CHANGE, wrapXHR(context), (0, _eventListenerOpts.eventListenerOpts)(false));
|
|
@@ -14,7 +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
|
|
17
|
+
var _utils = require("../../../common/v2/utils");
|
|
18
18
|
/**
|
|
19
19
|
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
20
20
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -106,11 +106,11 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
106
106
|
if (target) {
|
|
107
107
|
this.events.add({
|
|
108
108
|
...event,
|
|
109
|
-
targetAttributes: (0,
|
|
109
|
+
targetAttributes: (0, _utils.getVersion2Attributes)(target, this)
|
|
110
110
|
});
|
|
111
|
-
if ((0,
|
|
111
|
+
if ((0, _utils.shouldDuplicate)(target, this)) this.reportContainerEvent({
|
|
112
112
|
...event,
|
|
113
|
-
targetAttributes: (0,
|
|
113
|
+
targetAttributes: (0, _utils.getVersion2DuplicationAttributes)(target, this)
|
|
114
114
|
}, ctx);
|
|
115
115
|
} else {
|
|
116
116
|
this.reportContainerEvent(event, ctx);
|