@newrelic/browser-agent 1.308.0 → 1.309.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 +15 -0
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/drain/drain.js +2 -2
- package/dist/cjs/common/session/session-entity.js +1 -1
- package/dist/cjs/common/util/webdriver-detection.js +58 -0
- package/dist/cjs/common/window/nreum.js +7 -1
- package/dist/cjs/features/logging/constants.js +1 -1
- package/dist/cjs/features/page_view_event/aggregate/index.js +8 -7
- package/dist/cjs/features/page_view_timing/aggregate/index.js +3 -1
- package/dist/cjs/features/soft_navigations/aggregate/index.js +2 -0
- package/dist/cjs/loaders/api/register-api-types.js +2 -0
- package/dist/cjs/loaders/api/register.js +12 -5
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/common/drain/drain.js +2 -2
- package/dist/esm/common/session/session-entity.js +1 -1
- package/dist/esm/common/util/webdriver-detection.js +53 -0
- package/dist/esm/common/window/nreum.js +7 -1
- package/dist/esm/features/logging/constants.js +1 -1
- package/dist/esm/features/page_view_event/aggregate/index.js +8 -7
- package/dist/esm/features/page_view_timing/aggregate/index.js +3 -1
- package/dist/esm/features/soft_navigations/aggregate/index.js +2 -0
- package/dist/esm/loaders/api/register-api-types.js +2 -0
- package/dist/esm/loaders/api/register.js +12 -5
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/common/util/webdriver-detection.d.ts +18 -0
- package/dist/types/common/util/webdriver-detection.d.ts.map +1 -0
- package/dist/types/common/window/nreum.d.ts.map +1 -1
- package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/page_view_timing/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/aggregate/index.d.ts.map +1 -1
- package/dist/types/loaders/api/register-api-types.d.ts +9 -0
- package/dist/types/loaders/api/register-api-types.d.ts.map +1 -1
- package/dist/types/loaders/api/register.d.ts +0 -3
- package/dist/types/loaders/api/register.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/common/drain/drain.js +3 -3
- package/src/common/session/session-entity.js +1 -1
- package/src/common/util/webdriver-detection.js +57 -0
- package/src/common/window/nreum.js +7 -1
- package/src/features/logging/constants.js +1 -1
- package/src/features/page_view_event/aggregate/index.js +3 -5
- package/src/features/page_view_timing/aggregate/index.js +3 -1
- package/src/features/soft_navigations/aggregate/index.js +2 -0
- package/src/loaders/api/register-api-types.js +2 -0
- package/src/loaders/api/register.js +14 -4
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,21 @@
|
|
|
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.309.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.308.0...v1.309.0) (2026-02-03)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* Add WebDriver detection attribute to PageView & PageViewTiming ([#1679](https://github.com/newrelic/newrelic-browser-agent/issues/1679)) ([cd70213](https://github.com/newrelic/newrelic-browser-agent/commit/cd70213f6ef9898d1e40678fad850fbf03150570))
|
|
12
|
+
* Improve MFE tag setting ([#1683](https://github.com/newrelic/newrelic-browser-agent/issues/1683)) ([73c0f4c](https://github.com/newrelic/newrelic-browser-agent/commit/73c0f4cd13b9ae894caefcc071b84a6d2d2ad7d6))
|
|
13
|
+
* Warn when more than one agent is running ([#1686](https://github.com/newrelic/newrelic-browser-agent/issues/1686)) ([0deae4b](https://github.com/newrelic/newrelic-browser-agent/commit/0deae4b1743b3dd7674e0abd729fc9c9aba6582c))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
* allow child registrations from blocked parents ([#1677](https://github.com/newrelic/newrelic-browser-agent/issues/1677)) ([f3880e4](https://github.com/newrelic/newrelic-browser-agent/commit/f3880e41a638e68200572190ad0eb1af21f99ad4))
|
|
19
|
+
* Micro agent duplicate handlers ([#1658](https://github.com/newrelic/newrelic-browser-agent/issues/1658)) ([203aa30](https://github.com/newrelic/newrelic-browser-agent/commit/203aa303a939d9de67e62276d457f6ba6378e931))
|
|
20
|
+
|
|
6
21
|
## [1.308.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.307.0...v1.308.0) (2026-01-08)
|
|
7
22
|
|
|
8
23
|
|
|
@@ -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.309.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.309.0";
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Exposes the build type of the agent
|
|
@@ -12,7 +12,7 @@ var _registerHandler = require("../event-emitter/register-handler");
|
|
|
12
12
|
var _features = require("../../loaders/features/features");
|
|
13
13
|
var _eventContext = require("../event-emitter/event-context");
|
|
14
14
|
/**
|
|
15
|
-
* Copyright 2020-
|
|
15
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
16
16
|
* SPDX-License-Identifier: Apache-2.0
|
|
17
17
|
*/
|
|
18
18
|
|
|
@@ -123,7 +123,7 @@ function drainGroup(agentIdentifier, group, activateGroup = true) {
|
|
|
123
123
|
// registration *should* be an array of: [targetEE, eventHandler]
|
|
124
124
|
// certain browser polyfills of .values and .entries pass the prototype methods into the callback,
|
|
125
125
|
// which breaks this assumption and throws errors. So we make sure here that we only call .on() if its an actual NR EE
|
|
126
|
-
if (registration[0]?.on && registration[0]
|
|
126
|
+
if (registration[0]?.on && registration[0].context() instanceof _eventContext.EventContext && !registration[0].listeners(eventType).includes(registration[1])) registration[0].on(eventType, registration[1]);
|
|
127
127
|
});
|
|
128
128
|
});
|
|
129
129
|
}
|
|
@@ -20,7 +20,7 @@ var _features = require("../../loaders/features/features");
|
|
|
20
20
|
var _eventListenerOpts = require("../event-listener/event-listener-opts");
|
|
21
21
|
var _constants3 = require("../../features/logging/constants");
|
|
22
22
|
/**
|
|
23
|
-
* Copyright 2020-
|
|
23
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
24
24
|
* SPDX-License-Identifier: Apache-2.0
|
|
25
25
|
*/
|
|
26
26
|
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.webdriverDetected = void 0;
|
|
7
|
+
var _runtime = require("../constants/runtime");
|
|
8
|
+
/**
|
|
9
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
10
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Detects if the page is being controlled by WebDriver or automation tools derived from it.
|
|
15
|
+
* Checks for common indicators including:
|
|
16
|
+
* - navigator.webdriver property (standard WebDriver flag)
|
|
17
|
+
* - window.document.__webdriver_evaluate (WebDriver internal property)
|
|
18
|
+
* - window.document.__selenium_unwrapped (Selenium property)
|
|
19
|
+
* - window.document.__driver_evaluate (WebDriver internal property)
|
|
20
|
+
* - window.document.__webdriver_script_function (WebDriver internal property)
|
|
21
|
+
* - window.callPhantom (PhantomJS property)
|
|
22
|
+
* - window._phantom (PhantomJS property)
|
|
23
|
+
* - window.__nightmare (Nightmare.js property)
|
|
24
|
+
* - window.domAutomation (Chrome automation property)
|
|
25
|
+
* - window.domAutomationController (Chrome automation property)
|
|
26
|
+
*
|
|
27
|
+
* @type {boolean}
|
|
28
|
+
*/
|
|
29
|
+
const webdriverDetected = exports.webdriverDetected = (() => {
|
|
30
|
+
try {
|
|
31
|
+
// Standard WebDriver flag
|
|
32
|
+
if (typeof navigator !== 'undefined' && navigator.webdriver === true) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Check for various automation-related properties
|
|
37
|
+
if (_runtime.isBrowserScope) {
|
|
38
|
+
// WebDriver internal properties
|
|
39
|
+
if (document.__webdriver_evaluate || document.__selenium_unwrapped || document.__driver_evaluate || document.__webdriver_script_function) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// PhantomJS detection
|
|
44
|
+
if (window.callPhantom || window._phantom) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Nightmare.js detection
|
|
49
|
+
if (window.__nightmare) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
} catch (err) {
|
|
55
|
+
// If any errors occur during detection, assume not automated
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
})();
|
|
@@ -16,9 +16,10 @@ exports.gosNREUMOriginals = gosNREUMOriginals;
|
|
|
16
16
|
exports.setNREUMInitializedAgent = setNREUMInitializedAgent;
|
|
17
17
|
var _runtime = require("../constants/runtime");
|
|
18
18
|
var _now = require("../timing/now");
|
|
19
|
+
var _console = require("../util/console");
|
|
19
20
|
var _monkeyPatched = require("../util/monkey-patched");
|
|
20
21
|
/**
|
|
21
|
-
* Copyright 2020-
|
|
22
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
22
23
|
* SPDX-License-Identifier: Apache-2.0
|
|
23
24
|
*/
|
|
24
25
|
|
|
@@ -87,6 +88,11 @@ function setNREUMInitializedAgent(id, newAgentInstance) {
|
|
|
87
88
|
date: new Date()
|
|
88
89
|
};
|
|
89
90
|
nr.initializedAgents[id] = newAgentInstance;
|
|
91
|
+
|
|
92
|
+
// Warn if using more than one agent, but only once per agent load
|
|
93
|
+
if (Object.keys(nr.initializedAgents).length === 2) {
|
|
94
|
+
(0, _console.warn)(69);
|
|
95
|
+
}
|
|
90
96
|
}
|
|
91
97
|
|
|
92
98
|
/**
|
|
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.LOG_LEVELS = exports.LOGGING_MODE = exports.LOGGING_EVENT_EMITTER_CHANNEL = exports.FEATURE_NAME = void 0;
|
|
7
7
|
var _features = require("../../loaders/features/features");
|
|
8
8
|
/**
|
|
9
|
-
* Copyright 2020-
|
|
9
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
10
10
|
* SPDX-License-Identifier: Apache-2.0
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -22,9 +22,10 @@ var _traverse = require("../../../common/util/traverse");
|
|
|
22
22
|
var _harvester = require("../../../common/harvest/harvester");
|
|
23
23
|
var _features = require("../../../loaders/features/features");
|
|
24
24
|
var _submitData = require("../../../common/util/submit-data");
|
|
25
|
+
var _webdriverDetection = require("../../../common/util/webdriver-detection");
|
|
25
26
|
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
27
|
/**
|
|
27
|
-
* Copyright 2020-
|
|
28
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
28
29
|
* SPDX-License-Identifier: Apache-2.0
|
|
29
30
|
*/
|
|
30
31
|
|
|
@@ -90,12 +91,12 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
90
91
|
};
|
|
91
92
|
if (this.agentRef.runtime.session) queryParameters.fsh = Number(this.agentRef.runtime.session.isNew); // "first session harvest" aka RUM request or PageView event of a session
|
|
92
93
|
|
|
93
|
-
let body
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
}
|
|
94
|
+
let body = (0, _traverse.applyFnToProps)({
|
|
95
|
+
ja: {
|
|
96
|
+
...customAttributes,
|
|
97
|
+
webdriverDetected: _webdriverDetection.webdriverDetected
|
|
98
|
+
}
|
|
99
|
+
}, this.obfuscator.obfuscateString.bind(this.obfuscator), 'string');
|
|
99
100
|
if (_runtime.globalScope.performance) {
|
|
100
101
|
if ((0, _runtime.supportsNavTimingL2)()) {
|
|
101
102
|
// Navigation Timing level 2 API that replaced PerformanceTiming & PerformanceNavigation
|
|
@@ -20,8 +20,9 @@ var _constants2 = require("../../../common/vitals/constants");
|
|
|
20
20
|
var _runtime = require("../../../common/constants/runtime");
|
|
21
21
|
var _eventOrigin = require("../../../common/util/event-origin");
|
|
22
22
|
var _loadTime = require("../../../common/vitals/load-time");
|
|
23
|
+
var _webdriverDetection = require("../../../common/util/webdriver-detection");
|
|
23
24
|
/**
|
|
24
|
-
* Copyright 2020-
|
|
25
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
25
26
|
* SPDX-License-Identifier: Apache-2.0
|
|
26
27
|
*/
|
|
27
28
|
|
|
@@ -133,6 +134,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
133
134
|
timingAttributes[key] = val;
|
|
134
135
|
}
|
|
135
136
|
});
|
|
137
|
+
timingAttributes.webdriverDetected = _webdriverDetection.webdriverDetected;
|
|
136
138
|
}
|
|
137
139
|
preHarvestChecks() {
|
|
138
140
|
this.checkForFirstInteraction();
|
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.Aggregate = void 0;
|
|
7
7
|
var _handle = require("../../../common/event-emitter/handle");
|
|
8
8
|
var _registerHandler = require("../../../common/event-emitter/register-handler");
|
|
9
|
+
var _webdriverDetection = require("../../../common/util/webdriver-detection");
|
|
9
10
|
var _loadTime = require("../../../common/vitals/load-time");
|
|
10
11
|
var _features = require("../../../loaders/features/features");
|
|
11
12
|
var _aggregateBase = require("../../utils/aggregate-base");
|
|
@@ -31,6 +32,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
31
32
|
this.initialPageLoadInteraction.onDone.push(() => {
|
|
32
33
|
// this ensures the .end() method also works with iPL
|
|
33
34
|
if (agentRef.runtime.session?.isNew) this.initialPageLoadInteraction.customAttributes.isFirstOfSession = true; // mark the hard page load as first of its session
|
|
35
|
+
this.initialPageLoadInteraction.customAttributes.webdriverDetected = _webdriverDetection.webdriverDetected;
|
|
34
36
|
this.initialPageLoadInteraction.forceSave = true; // unless forcibly ignored, iPL always finish by default
|
|
35
37
|
const ixn = this.initialPageLoadInteraction;
|
|
36
38
|
this.events.add(ixn); // add the iPL ixn to the buffer for harvest
|
|
@@ -26,6 +26,7 @@ exports.default = void 0;
|
|
|
26
26
|
* @typedef {Object} RegisterAPIConstructor
|
|
27
27
|
* @property {string|number} id - The unique id for the registered entity. This will be assigned to any synthesized entities.
|
|
28
28
|
* @property {string} name - The readable name for the registered entity. This will be assigned to any synthesized entities.
|
|
29
|
+
* @property {{[key: string]: any}} [tags] - The tags for the registered entity as key-value pairs. This will be assigned to any synthesized entities. Tags are converted to source.* attributes (e.g., {environment: 'production'} becomes source.environment: 'production').
|
|
29
30
|
* @property {boolean} [isolated] - When true, each registration creates an isolated instance. When false, multiple registrations with the same id and isolated: false will share a single instance, including all custom attributes, ids, names, and metadata. Calling deregister on a shared instance will deregister it for all entities using the instance. Defaults to true.
|
|
30
31
|
* @property {string} [parentId] - The parentId for the registered entity. If none was supplied, it will assume the entity guid from the main agent.
|
|
31
32
|
*/
|
|
@@ -36,6 +37,7 @@ exports.default = void 0;
|
|
|
36
37
|
* @property {string} [target.licenseKey] - The license key for the registered entity. If none was supplied, it will assume the license key from the main agent.
|
|
37
38
|
* @property {string} target.id - The ID for the registered entity.
|
|
38
39
|
* @property {string} target.name - The name returned for the registered entity.
|
|
40
|
+
* @property {{[key: string]: any}} [target.tags] - The tags for the registered entity as key-value pairs.
|
|
39
41
|
* @property {string} [target.parentId] - The parentId for the registered entity. If none was supplied, it will assume the entity guid from the main agent.
|
|
40
42
|
* @property {boolean} [target.isolated] - When true, each registration creates an isolated instance. When false, multiple registrations with the same id and isolated: false will share a single instance, including all custom attributes, ids, names, and metadata. Calling deregister on a shared instance will deregister it for all entities using the instance. Defaults to true.
|
|
41
43
|
*/
|
|
@@ -27,6 +27,8 @@ var _recordCustomEvent = require("./recordCustomEvent");
|
|
|
27
27
|
* @typedef {import('./register-api-types').RegisterAPI} RegisterAPI
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
|
+
const PROTECTED_KEYS = ['name', 'id', 'type'];
|
|
31
|
+
|
|
30
32
|
/**
|
|
31
33
|
* @experimental
|
|
32
34
|
* IMPORTANT: This feature is being developed for use internally and is not in a public-facing production-ready state.
|
|
@@ -53,10 +55,14 @@ function register(agentRef, target, parent) {
|
|
|
53
55
|
target.licenseKey ||= agentRef.info.licenseKey; // will inherit the license key from the container agent if not provided for brevity. A future state may dictate that we need different license keys to do different things.
|
|
54
56
|
target.blocked = false;
|
|
55
57
|
target.parent = parent || {};
|
|
56
|
-
if (
|
|
58
|
+
if (typeof target.tags !== 'object' || target.tags === null || Array.isArray(target.tags)) target.tags = {};
|
|
57
59
|
const attrs = {};
|
|
58
|
-
|
|
59
|
-
|
|
60
|
+
|
|
61
|
+
// Process tags object and add to attrs, excluding protected keys
|
|
62
|
+
Object.entries(target.tags).forEach(([key, value]) => {
|
|
63
|
+
if (!PROTECTED_KEYS.includes(key)) {
|
|
64
|
+
attrs["source.".concat(key)] = value;
|
|
65
|
+
}
|
|
60
66
|
});
|
|
61
67
|
target.isolated ??= true;
|
|
62
68
|
|
|
@@ -167,12 +173,13 @@ function register(agentRef, target, parent) {
|
|
|
167
173
|
* @returns
|
|
168
174
|
*/
|
|
169
175
|
const report = (methodToCall, args, target) => {
|
|
170
|
-
if
|
|
176
|
+
/** Even if we are blocked, if registering we should still return a child register API so nested API calls do not throw errors */
|
|
177
|
+
if (isBlocked() && methodToCall !== register) return;
|
|
171
178
|
/** set the timestamp before the async part of waiting for the rum response for better accuracy */
|
|
172
179
|
const timestamp = (0, _now.now)();
|
|
173
180
|
(0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, ["API/register/".concat(methodToCall.name, "/called")], undefined, _features.FEATURE_NAMES.metrics, agentRef.ee);
|
|
174
181
|
try {
|
|
175
|
-
const shouldDuplicate = agentRef.init.api.duplicate_registered_data && methodToCall
|
|
182
|
+
const shouldDuplicate = agentRef.init.api.duplicate_registered_data && methodToCall !== register;
|
|
176
183
|
if (shouldDuplicate) {
|
|
177
184
|
let duplicatedArgs = args;
|
|
178
185
|
if (args[1] instanceof Object) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2020-
|
|
2
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -115,7 +115,7 @@ function drainGroup(agentIdentifier, group, activateGroup = true) {
|
|
|
115
115
|
// registration *should* be an array of: [targetEE, eventHandler]
|
|
116
116
|
// certain browser polyfills of .values and .entries pass the prototype methods into the callback,
|
|
117
117
|
// which breaks this assumption and throws errors. So we make sure here that we only call .on() if its an actual NR EE
|
|
118
|
-
if (registration[0]?.on && registration[0]
|
|
118
|
+
if (registration[0]?.on && registration[0].context() instanceof EventContext && !registration[0].listeners(eventType).includes(registration[1])) registration[0].on(eventType, registration[1]);
|
|
119
119
|
});
|
|
120
120
|
});
|
|
121
121
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { isBrowserScope } from '../constants/runtime';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Detects if the page is being controlled by WebDriver or automation tools derived from it.
|
|
10
|
+
* Checks for common indicators including:
|
|
11
|
+
* - navigator.webdriver property (standard WebDriver flag)
|
|
12
|
+
* - window.document.__webdriver_evaluate (WebDriver internal property)
|
|
13
|
+
* - window.document.__selenium_unwrapped (Selenium property)
|
|
14
|
+
* - window.document.__driver_evaluate (WebDriver internal property)
|
|
15
|
+
* - window.document.__webdriver_script_function (WebDriver internal property)
|
|
16
|
+
* - window.callPhantom (PhantomJS property)
|
|
17
|
+
* - window._phantom (PhantomJS property)
|
|
18
|
+
* - window.__nightmare (Nightmare.js property)
|
|
19
|
+
* - window.domAutomation (Chrome automation property)
|
|
20
|
+
* - window.domAutomationController (Chrome automation property)
|
|
21
|
+
*
|
|
22
|
+
* @type {boolean}
|
|
23
|
+
*/
|
|
24
|
+
export const webdriverDetected = (() => {
|
|
25
|
+
try {
|
|
26
|
+
// Standard WebDriver flag
|
|
27
|
+
if (typeof navigator !== 'undefined' && navigator.webdriver === true) {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Check for various automation-related properties
|
|
32
|
+
if (isBrowserScope) {
|
|
33
|
+
// WebDriver internal properties
|
|
34
|
+
if (document.__webdriver_evaluate || document.__selenium_unwrapped || document.__driver_evaluate || document.__webdriver_script_function) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// PhantomJS detection
|
|
39
|
+
if (window.callPhantom || window._phantom) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Nightmare.js detection
|
|
44
|
+
if (window.__nightmare) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
} catch (err) {
|
|
50
|
+
// If any errors occur during detection, assume not automated
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
})();
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2020-
|
|
2
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
import { globalScope } from '../constants/runtime';
|
|
6
6
|
import { now } from '../timing/now';
|
|
7
|
+
import { warn } from '../util/console';
|
|
7
8
|
import { isNative } from '../util/monkey-patched';
|
|
8
9
|
export const defaults = {
|
|
9
10
|
beacon: 'bam.nr-data.net',
|
|
@@ -70,6 +71,11 @@ export function setNREUMInitializedAgent(id, newAgentInstance) {
|
|
|
70
71
|
date: new Date()
|
|
71
72
|
};
|
|
72
73
|
nr.initializedAgents[id] = newAgentInstance;
|
|
74
|
+
|
|
75
|
+
// Warn if using more than one agent, but only once per agent load
|
|
76
|
+
if (Object.keys(nr.initializedAgents).length === 2) {
|
|
77
|
+
warn(69);
|
|
78
|
+
}
|
|
73
79
|
}
|
|
74
80
|
|
|
75
81
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2020-
|
|
2
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
import { globalScope, isBrowserScope, originTime, supportsNavTimingL2 } from '../../../common/constants/runtime';
|
|
@@ -20,6 +20,7 @@ import { applyFnToProps } from '../../../common/util/traverse';
|
|
|
20
20
|
import { send } from '../../../common/harvest/harvester';
|
|
21
21
|
import { FEATURE_NAMES, FEATURE_TO_ENDPOINT } from '../../../loaders/features/features';
|
|
22
22
|
import { getSubmitMethod } from '../../../common/util/submit-data';
|
|
23
|
+
import { webdriverDetected } from '../../../common/util/webdriver-detection';
|
|
23
24
|
export class Aggregate extends AggregateBase {
|
|
24
25
|
static featureName = CONSTANTS.FEATURE_NAME;
|
|
25
26
|
constructor(agentRef) {
|
|
@@ -82,12 +83,12 @@ export class Aggregate extends AggregateBase {
|
|
|
82
83
|
};
|
|
83
84
|
if (this.agentRef.runtime.session) queryParameters.fsh = Number(this.agentRef.runtime.session.isNew); // "first session harvest" aka RUM request or PageView event of a session
|
|
84
85
|
|
|
85
|
-
let body
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
}
|
|
86
|
+
let body = applyFnToProps({
|
|
87
|
+
ja: {
|
|
88
|
+
...customAttributes,
|
|
89
|
+
webdriverDetected
|
|
90
|
+
}
|
|
91
|
+
}, this.obfuscator.obfuscateString.bind(this.obfuscator), 'string');
|
|
91
92
|
if (globalScope.performance) {
|
|
92
93
|
if (supportsNavTimingL2()) {
|
|
93
94
|
// Navigation Timing level 2 API that replaced PerformanceTiming & PerformanceNavigation
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2020-
|
|
2
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -19,6 +19,7 @@ import { VITAL_NAMES } from '../../../common/vitals/constants';
|
|
|
19
19
|
import { initiallyHidden } from '../../../common/constants/runtime';
|
|
20
20
|
import { eventOrigin } from '../../../common/util/event-origin';
|
|
21
21
|
import { loadTime } from '../../../common/vitals/load-time';
|
|
22
|
+
import { webdriverDetected } from '../../../common/util/webdriver-detection';
|
|
22
23
|
export class Aggregate extends AggregateBase {
|
|
23
24
|
static featureName = FEATURE_NAME;
|
|
24
25
|
#handleVitalMetric = ({
|
|
@@ -127,6 +128,7 @@ export class Aggregate extends AggregateBase {
|
|
|
127
128
|
timingAttributes[key] = val;
|
|
128
129
|
}
|
|
129
130
|
});
|
|
131
|
+
timingAttributes.webdriverDetected = webdriverDetected;
|
|
130
132
|
}
|
|
131
133
|
preHarvestChecks() {
|
|
132
134
|
this.checkForFirstInteraction();
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { handle } from '../../../common/event-emitter/handle';
|
|
6
6
|
import { registerHandler } from '../../../common/event-emitter/register-handler';
|
|
7
|
+
import { webdriverDetected } from '../../../common/util/webdriver-detection';
|
|
7
8
|
import { loadTime } from '../../../common/vitals/load-time';
|
|
8
9
|
import { FEATURE_NAMES } from '../../../loaders/features/features';
|
|
9
10
|
import { AggregateBase } from '../../utils/aggregate-base';
|
|
@@ -24,6 +25,7 @@ export class Aggregate extends AggregateBase {
|
|
|
24
25
|
this.initialPageLoadInteraction.onDone.push(() => {
|
|
25
26
|
// this ensures the .end() method also works with iPL
|
|
26
27
|
if (agentRef.runtime.session?.isNew) this.initialPageLoadInteraction.customAttributes.isFirstOfSession = true; // mark the hard page load as first of its session
|
|
28
|
+
this.initialPageLoadInteraction.customAttributes.webdriverDetected = webdriverDetected;
|
|
27
29
|
this.initialPageLoadInteraction.forceSave = true; // unless forcibly ignored, iPL always finish by default
|
|
28
30
|
const ixn = this.initialPageLoadInteraction;
|
|
29
31
|
this.events.add(ixn); // add the iPL ixn to the buffer for harvest
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
* @typedef {Object} RegisterAPIConstructor
|
|
23
23
|
* @property {string|number} id - The unique id for the registered entity. This will be assigned to any synthesized entities.
|
|
24
24
|
* @property {string} name - The readable name for the registered entity. This will be assigned to any synthesized entities.
|
|
25
|
+
* @property {{[key: string]: any}} [tags] - The tags for the registered entity as key-value pairs. This will be assigned to any synthesized entities. Tags are converted to source.* attributes (e.g., {environment: 'production'} becomes source.environment: 'production').
|
|
25
26
|
* @property {boolean} [isolated] - When true, each registration creates an isolated instance. When false, multiple registrations with the same id and isolated: false will share a single instance, including all custom attributes, ids, names, and metadata. Calling deregister on a shared instance will deregister it for all entities using the instance. Defaults to true.
|
|
26
27
|
* @property {string} [parentId] - The parentId for the registered entity. If none was supplied, it will assume the entity guid from the main agent.
|
|
27
28
|
*/
|
|
@@ -33,6 +34,7 @@
|
|
|
33
34
|
* @property {string} [target.licenseKey] - The license key for the registered entity. If none was supplied, it will assume the license key from the main agent.
|
|
34
35
|
* @property {string} target.id - The ID for the registered entity.
|
|
35
36
|
* @property {string} target.name - The name returned for the registered entity.
|
|
37
|
+
* @property {{[key: string]: any}} [target.tags] - The tags for the registered entity as key-value pairs.
|
|
36
38
|
* @property {string} [target.parentId] - The parentId for the registered entity. If none was supplied, it will assume the entity guid from the main agent.
|
|
37
39
|
* @property {boolean} [target.isolated] - When true, each registration creates an isolated instance. When false, multiple registrations with the same id and isolated: false will share a single instance, including all custom attributes, ids, names, and metadata. Calling deregister on a shared instance will deregister it for all entities using the instance. Defaults to true.
|
|
38
40
|
*/
|
|
@@ -21,6 +21,8 @@ import { recordCustomEvent } from './recordCustomEvent';
|
|
|
21
21
|
* @typedef {import('./register-api-types').RegisterAPI} RegisterAPI
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
+
const PROTECTED_KEYS = ['name', 'id', 'type'];
|
|
25
|
+
|
|
24
26
|
/**
|
|
25
27
|
* @experimental
|
|
26
28
|
* IMPORTANT: This feature is being developed for use internally and is not in a public-facing production-ready state.
|
|
@@ -47,10 +49,14 @@ function register(agentRef, target, parent) {
|
|
|
47
49
|
target.licenseKey ||= agentRef.info.licenseKey; // will inherit the license key from the container agent if not provided for brevity. A future state may dictate that we need different license keys to do different things.
|
|
48
50
|
target.blocked = false;
|
|
49
51
|
target.parent = parent || {};
|
|
50
|
-
if (
|
|
52
|
+
if (typeof target.tags !== 'object' || target.tags === null || Array.isArray(target.tags)) target.tags = {};
|
|
51
53
|
const attrs = {};
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
|
|
55
|
+
// Process tags object and add to attrs, excluding protected keys
|
|
56
|
+
Object.entries(target.tags).forEach(([key, value]) => {
|
|
57
|
+
if (!PROTECTED_KEYS.includes(key)) {
|
|
58
|
+
attrs["source.".concat(key)] = value;
|
|
59
|
+
}
|
|
54
60
|
});
|
|
55
61
|
target.isolated ??= true;
|
|
56
62
|
|
|
@@ -161,12 +167,13 @@ function register(agentRef, target, parent) {
|
|
|
161
167
|
* @returns
|
|
162
168
|
*/
|
|
163
169
|
const report = (methodToCall, args, target) => {
|
|
164
|
-
if
|
|
170
|
+
/** Even if we are blocked, if registering we should still return a child register API so nested API calls do not throw errors */
|
|
171
|
+
if (isBlocked() && methodToCall !== register) return;
|
|
165
172
|
/** set the timestamp before the async part of waiting for the rum response for better accuracy */
|
|
166
173
|
const timestamp = now();
|
|
167
174
|
handle(SUPPORTABILITY_METRIC_CHANNEL, ["API/register/".concat(methodToCall.name, "/called")], undefined, FEATURE_NAMES.metrics, agentRef.ee);
|
|
168
175
|
try {
|
|
169
|
-
const shouldDuplicate = agentRef.init.api.duplicate_registered_data && methodToCall
|
|
176
|
+
const shouldDuplicate = agentRef.init.api.duplicate_registered_data && methodToCall !== register;
|
|
170
177
|
if (shouldDuplicate) {
|
|
171
178
|
let duplicatedArgs = args;
|
|
172
179
|
if (args[1] instanceof Object) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../src/index.js","../src/cdn/experimental.js","../src/cdn/lite.js","../src/cdn/pro.js","../src/cdn/spa.js","../src/common/aggregate/aggregator.js","../src/common/aggregate/event-aggregator.js","../src/common/config/configurable.js","../src/common/config/info.js","../src/common/config/init-types.js","../src/common/config/init.js","../src/common/config/loader-config.js","../src/common/config/runtime.js","../src/common/constants/agent-constants.js","../src/common/constants/env.cdn.js","../src/common/constants/env.js","../src/common/constants/env.npm.js","../src/common/constants/runtime.js","../src/common/constants/shared-channel.js","../src/common/deny-list/deny-list.js","../src/common/dispatch/global-event.js","../src/common/dom/iframe.js","../src/common/dom/query-selector.js","../src/common/dom/selector-path.js","../src/common/drain/drain.js","../src/common/event-emitter/contextual-ee.js","../src/common/event-emitter/event-context.js","../src/common/event-emitter/handle.js","../src/common/event-emitter/register-handler.js","../src/common/event-listener/event-listener-opts.js","../src/common/harvest/harvester.js","../src/common/harvest/types.js","../src/common/ids/bundle-id.js","../src/common/ids/id.js","../src/common/ids/unique-id.js","../src/common/serialize/bel-serializer.js","../src/common/session/constants.js","../src/common/session/session-entity.js","../src/common/storage/local-storage.js","../src/common/timer/interaction-timer.js","../src/common/timer/timer.js","../src/common/timing/nav-timing.js","../src/common/timing/now.js","../src/common/timing/time-keeper.js","../src/common/unload/eol.js","../src/common/url/canonicalize-url.js","../src/common/url/clean-url.js","../src/common/url/encode.js","../src/common/url/extract-url.js","../src/common/url/location.js","../src/common/url/parse-url.js","../src/common/url/protocol.js","../src/common/util/attribute-size.js","../src/common/util/console.js","../src/common/util/data-size.js","../src/common/util/event-origin.js","../src/common/util/feature-flags.js","../src/common/util/get-or-set.js","../src/common/util/invoke.js","../src/common/util/monkey-patched.js","../src/common/util/obfuscate.js","../src/common/util/stringify.js","../src/common/util/submit-data.js","../src/common/util/text.js","../src/common/util/traverse.js","../src/common/util/type-check.js","../src/common/util/v2.js","../src/common/vitals/constants.js","../src/common/vitals/cumulative-layout-shift.js","../src/common/vitals/first-contentful-paint.js","../src/common/vitals/first-paint.js","../src/common/vitals/interaction-to-next-paint.js","../src/common/vitals/largest-contentful-paint.js","../src/common/vitals/load-time.js","../src/common/vitals/time-to-first-byte.js","../src/common/vitals/vital-metric.js","../src/common/window/load.js","../src/common/window/nreum.js","../src/common/window/page-visibility.js","../src/common/wrap/wrap-events.js","../src/common/wrap/wrap-fetch.js","../src/common/wrap/wrap-function.js","../src/common/wrap/wrap-history.js","../src/common/wrap/wrap-jsonp.js","../src/common/wrap/wrap-logger.js","../src/common/wrap/wrap-mutation.js","../src/common/wrap/wrap-promise.js","../src/common/wrap/wrap-timer.js","../src/common/wrap/wrap-websocket.js","../src/common/wrap/wrap-xhr.js","../src/features/ajax/constants.js","../src/features/ajax/index.js","../src/features/ajax/aggregate/gql.js","../src/features/ajax/aggregate/index.js","../src/features/ajax/instrument/distributed-tracing.js","../src/features/ajax/instrument/index.js","../src/features/ajax/instrument/response-size.js","../src/features/generic_events/constants.js","../src/features/generic_events/index.js","../src/features/generic_events/aggregate/index.js","../src/features/generic_events/aggregate/user-actions/aggregated-user-action.js","../src/features/generic_events/aggregate/user-actions/user-actions-aggregator.js","../src/features/generic_events/instrument/index.js","../src/features/jserrors/constants.js","../src/features/jserrors/index.js","../src/features/jserrors/aggregate/canonical-function-name.js","../src/features/jserrors/aggregate/cause-string.js","../src/features/jserrors/aggregate/compute-stack-trace.js","../src/features/jserrors/aggregate/format-stack-trace.js","../src/features/jserrors/aggregate/index.js","../src/features/jserrors/aggregate/internal-errors.js","../src/features/jserrors/aggregate/string-hash-code.js","../src/features/jserrors/instrument/index.js","../src/features/jserrors/shared/cast-error.js","../src/features/jserrors/shared/uncaught-error.js","../src/features/logging/constants.js","../src/features/logging/index.js","../src/features/logging/aggregate/index.js","../src/features/logging/instrument/index.js","../src/features/logging/shared/log.js","../src/features/logging/shared/utils.js","../src/features/metrics/constants.js","../src/features/metrics/index.js","../src/features/metrics/aggregate/framework-detection.js","../src/features/metrics/aggregate/harvest-metadata.js","../src/features/metrics/aggregate/index.js","../src/features/metrics/instrument/index.js","../src/features/page_action/constants.js","../src/features/page_action/index.js","../src/features/page_action/instrument/index.js","../src/features/page_view_event/constants.js","../src/features/page_view_event/index.js","../src/features/page_view_event/aggregate/index.js","../src/features/page_view_event/aggregate/initialized-features.js","../src/features/page_view_event/instrument/index.js","../src/features/page_view_timing/constants.js","../src/features/page_view_timing/index.js","../src/features/page_view_timing/aggregate/index.js","../src/features/page_view_timing/instrument/index.js","../src/features/session_replay/constants.js","../src/features/session_replay/index.js","../src/features/session_replay/aggregate/index.js","../src/features/session_replay/instrument/index.js","../src/features/session_replay/shared/recorder-events.js","../src/features/session_replay/shared/recorder.js","../src/features/session_replay/shared/stylesheet-evaluator.js","../src/features/session_replay/shared/utils.js","../src/features/session_trace/constants.js","../src/features/session_trace/index.js","../src/features/session_trace/aggregate/index.js","../src/features/session_trace/aggregate/trace/node.js","../src/features/session_trace/aggregate/trace/storage.js","../src/features/session_trace/aggregate/trace/utils.js","../src/features/session_trace/instrument/index.js","../src/features/soft_navigations/constants.js","../src/features/soft_navigations/index.js","../src/features/soft_navigations/aggregate/ajax-node.js","../src/features/soft_navigations/aggregate/bel-node.js","../src/features/soft_navigations/aggregate/index.js","../src/features/soft_navigations/aggregate/initial-page-load-interaction.js","../src/features/soft_navigations/aggregate/interaction.js","../src/features/soft_navigations/instrument/index.js","../src/features/spa/constants.js","../src/features/spa/index.js","../src/features/spa/aggregate/index.js","../src/features/spa/aggregate/interaction-node.js","../src/features/spa/aggregate/interaction.js","../src/features/spa/aggregate/serializer.js","../src/features/spa/instrument/index.js","../src/features/utils/agent-session.js","../src/features/utils/aggregate-base.js","../src/features/utils/event-buffer.js","../src/features/utils/feature-base.js","../src/features/utils/feature-gates.js","../src/features/utils/instrument-base.js","../src/features/utils/nr1-debugger.js","../src/interfaces/registered-entity.js","../src/loaders/agent-base.js","../src/loaders/agent.js","../src/loaders/api-base.js","../src/loaders/browser-agent.js","../src/loaders/micro-agent-base.js","../src/loaders/micro-agent.js","../src/loaders/api/addPageAction.js","../src/loaders/api/addRelease.js","../src/loaders/api/addToTrace.js","../src/loaders/api/consent.js","../src/loaders/api/constants.js","../src/loaders/api/finished.js","../src/loaders/api/interaction-types.js","../src/loaders/api/interaction.js","../src/loaders/api/log.js","../src/loaders/api/measure.js","../src/loaders/api/noticeError.js","../src/loaders/api/pauseReplay.js","../src/loaders/api/recordCustomEvent.js","../src/loaders/api/recordReplay.js","../src/loaders/api/register-api-types.js","../src/loaders/api/register.js","../src/loaders/api/setApplicationVersion.js","../src/loaders/api/setCustomAttribute.js","../src/loaders/api/setErrorHandler.js","../src/loaders/api/setPageViewName.js","../src/loaders/api/setUserId.js","../src/loaders/api/sharedHandlers.js","../src/loaders/api/start.js","../src/loaders/api/topLevelCallers.js","../src/loaders/api/wrapLogger.js","../src/loaders/configure/configure.js","../src/loaders/configure/nonce.js","../src/loaders/configure/public-path.js","../src/loaders/features/enabled-features.js","../src/loaders/features/featureDependencies.js","../src/loaders/features/features.js"],"version":"5.9.3"}
|
|
1
|
+
{"root":["../src/index.js","../src/cdn/experimental.js","../src/cdn/lite.js","../src/cdn/pro.js","../src/cdn/spa.js","../src/common/aggregate/aggregator.js","../src/common/aggregate/event-aggregator.js","../src/common/config/configurable.js","../src/common/config/info.js","../src/common/config/init-types.js","../src/common/config/init.js","../src/common/config/loader-config.js","../src/common/config/runtime.js","../src/common/constants/agent-constants.js","../src/common/constants/env.cdn.js","../src/common/constants/env.js","../src/common/constants/env.npm.js","../src/common/constants/runtime.js","../src/common/constants/shared-channel.js","../src/common/deny-list/deny-list.js","../src/common/dispatch/global-event.js","../src/common/dom/iframe.js","../src/common/dom/query-selector.js","../src/common/dom/selector-path.js","../src/common/drain/drain.js","../src/common/event-emitter/contextual-ee.js","../src/common/event-emitter/event-context.js","../src/common/event-emitter/handle.js","../src/common/event-emitter/register-handler.js","../src/common/event-listener/event-listener-opts.js","../src/common/harvest/harvester.js","../src/common/harvest/types.js","../src/common/ids/bundle-id.js","../src/common/ids/id.js","../src/common/ids/unique-id.js","../src/common/serialize/bel-serializer.js","../src/common/session/constants.js","../src/common/session/session-entity.js","../src/common/storage/local-storage.js","../src/common/timer/interaction-timer.js","../src/common/timer/timer.js","../src/common/timing/nav-timing.js","../src/common/timing/now.js","../src/common/timing/time-keeper.js","../src/common/unload/eol.js","../src/common/url/canonicalize-url.js","../src/common/url/clean-url.js","../src/common/url/encode.js","../src/common/url/extract-url.js","../src/common/url/location.js","../src/common/url/parse-url.js","../src/common/url/protocol.js","../src/common/util/attribute-size.js","../src/common/util/console.js","../src/common/util/data-size.js","../src/common/util/event-origin.js","../src/common/util/feature-flags.js","../src/common/util/get-or-set.js","../src/common/util/invoke.js","../src/common/util/monkey-patched.js","../src/common/util/obfuscate.js","../src/common/util/stringify.js","../src/common/util/submit-data.js","../src/common/util/text.js","../src/common/util/traverse.js","../src/common/util/type-check.js","../src/common/util/v2.js","../src/common/util/webdriver-detection.js","../src/common/vitals/constants.js","../src/common/vitals/cumulative-layout-shift.js","../src/common/vitals/first-contentful-paint.js","../src/common/vitals/first-paint.js","../src/common/vitals/interaction-to-next-paint.js","../src/common/vitals/largest-contentful-paint.js","../src/common/vitals/load-time.js","../src/common/vitals/time-to-first-byte.js","../src/common/vitals/vital-metric.js","../src/common/window/load.js","../src/common/window/nreum.js","../src/common/window/page-visibility.js","../src/common/wrap/wrap-events.js","../src/common/wrap/wrap-fetch.js","../src/common/wrap/wrap-function.js","../src/common/wrap/wrap-history.js","../src/common/wrap/wrap-jsonp.js","../src/common/wrap/wrap-logger.js","../src/common/wrap/wrap-mutation.js","../src/common/wrap/wrap-promise.js","../src/common/wrap/wrap-timer.js","../src/common/wrap/wrap-websocket.js","../src/common/wrap/wrap-xhr.js","../src/features/ajax/constants.js","../src/features/ajax/index.js","../src/features/ajax/aggregate/gql.js","../src/features/ajax/aggregate/index.js","../src/features/ajax/instrument/distributed-tracing.js","../src/features/ajax/instrument/index.js","../src/features/ajax/instrument/response-size.js","../src/features/generic_events/constants.js","../src/features/generic_events/index.js","../src/features/generic_events/aggregate/index.js","../src/features/generic_events/aggregate/user-actions/aggregated-user-action.js","../src/features/generic_events/aggregate/user-actions/user-actions-aggregator.js","../src/features/generic_events/instrument/index.js","../src/features/jserrors/constants.js","../src/features/jserrors/index.js","../src/features/jserrors/aggregate/canonical-function-name.js","../src/features/jserrors/aggregate/cause-string.js","../src/features/jserrors/aggregate/compute-stack-trace.js","../src/features/jserrors/aggregate/format-stack-trace.js","../src/features/jserrors/aggregate/index.js","../src/features/jserrors/aggregate/internal-errors.js","../src/features/jserrors/aggregate/string-hash-code.js","../src/features/jserrors/instrument/index.js","../src/features/jserrors/shared/cast-error.js","../src/features/jserrors/shared/uncaught-error.js","../src/features/logging/constants.js","../src/features/logging/index.js","../src/features/logging/aggregate/index.js","../src/features/logging/instrument/index.js","../src/features/logging/shared/log.js","../src/features/logging/shared/utils.js","../src/features/metrics/constants.js","../src/features/metrics/index.js","../src/features/metrics/aggregate/framework-detection.js","../src/features/metrics/aggregate/harvest-metadata.js","../src/features/metrics/aggregate/index.js","../src/features/metrics/instrument/index.js","../src/features/page_action/constants.js","../src/features/page_action/index.js","../src/features/page_action/instrument/index.js","../src/features/page_view_event/constants.js","../src/features/page_view_event/index.js","../src/features/page_view_event/aggregate/index.js","../src/features/page_view_event/aggregate/initialized-features.js","../src/features/page_view_event/instrument/index.js","../src/features/page_view_timing/constants.js","../src/features/page_view_timing/index.js","../src/features/page_view_timing/aggregate/index.js","../src/features/page_view_timing/instrument/index.js","../src/features/session_replay/constants.js","../src/features/session_replay/index.js","../src/features/session_replay/aggregate/index.js","../src/features/session_replay/instrument/index.js","../src/features/session_replay/shared/recorder-events.js","../src/features/session_replay/shared/recorder.js","../src/features/session_replay/shared/stylesheet-evaluator.js","../src/features/session_replay/shared/utils.js","../src/features/session_trace/constants.js","../src/features/session_trace/index.js","../src/features/session_trace/aggregate/index.js","../src/features/session_trace/aggregate/trace/node.js","../src/features/session_trace/aggregate/trace/storage.js","../src/features/session_trace/aggregate/trace/utils.js","../src/features/session_trace/instrument/index.js","../src/features/soft_navigations/constants.js","../src/features/soft_navigations/index.js","../src/features/soft_navigations/aggregate/ajax-node.js","../src/features/soft_navigations/aggregate/bel-node.js","../src/features/soft_navigations/aggregate/index.js","../src/features/soft_navigations/aggregate/initial-page-load-interaction.js","../src/features/soft_navigations/aggregate/interaction.js","../src/features/soft_navigations/instrument/index.js","../src/features/spa/constants.js","../src/features/spa/index.js","../src/features/spa/aggregate/index.js","../src/features/spa/aggregate/interaction-node.js","../src/features/spa/aggregate/interaction.js","../src/features/spa/aggregate/serializer.js","../src/features/spa/instrument/index.js","../src/features/utils/agent-session.js","../src/features/utils/aggregate-base.js","../src/features/utils/event-buffer.js","../src/features/utils/feature-base.js","../src/features/utils/feature-gates.js","../src/features/utils/instrument-base.js","../src/features/utils/nr1-debugger.js","../src/interfaces/registered-entity.js","../src/loaders/agent-base.js","../src/loaders/agent.js","../src/loaders/api-base.js","../src/loaders/browser-agent.js","../src/loaders/micro-agent-base.js","../src/loaders/micro-agent.js","../src/loaders/api/addPageAction.js","../src/loaders/api/addRelease.js","../src/loaders/api/addToTrace.js","../src/loaders/api/consent.js","../src/loaders/api/constants.js","../src/loaders/api/finished.js","../src/loaders/api/interaction-types.js","../src/loaders/api/interaction.js","../src/loaders/api/log.js","../src/loaders/api/measure.js","../src/loaders/api/noticeError.js","../src/loaders/api/pauseReplay.js","../src/loaders/api/recordCustomEvent.js","../src/loaders/api/recordReplay.js","../src/loaders/api/register-api-types.js","../src/loaders/api/register.js","../src/loaders/api/setApplicationVersion.js","../src/loaders/api/setCustomAttribute.js","../src/loaders/api/setErrorHandler.js","../src/loaders/api/setPageViewName.js","../src/loaders/api/setUserId.js","../src/loaders/api/sharedHandlers.js","../src/loaders/api/start.js","../src/loaders/api/topLevelCallers.js","../src/loaders/api/wrapLogger.js","../src/loaders/configure/configure.js","../src/loaders/configure/nonce.js","../src/loaders/configure/public-path.js","../src/loaders/features/enabled-features.js","../src/loaders/features/featureDependencies.js","../src/loaders/features/features.js"],"version":"5.9.3"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detects if the page is being controlled by WebDriver or automation tools derived from it.
|
|
3
|
+
* Checks for common indicators including:
|
|
4
|
+
* - navigator.webdriver property (standard WebDriver flag)
|
|
5
|
+
* - window.document.__webdriver_evaluate (WebDriver internal property)
|
|
6
|
+
* - window.document.__selenium_unwrapped (Selenium property)
|
|
7
|
+
* - window.document.__driver_evaluate (WebDriver internal property)
|
|
8
|
+
* - window.document.__webdriver_script_function (WebDriver internal property)
|
|
9
|
+
* - window.callPhantom (PhantomJS property)
|
|
10
|
+
* - window._phantom (PhantomJS property)
|
|
11
|
+
* - window.__nightmare (Nightmare.js property)
|
|
12
|
+
* - window.domAutomation (Chrome automation property)
|
|
13
|
+
* - window.domAutomationController (Chrome automation property)
|
|
14
|
+
*
|
|
15
|
+
* @type {boolean}
|
|
16
|
+
*/
|
|
17
|
+
export const webdriverDetected: boolean;
|
|
18
|
+
//# sourceMappingURL=webdriver-detection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webdriver-detection.d.ts","sourceRoot":"","sources":["../../../../src/common/util/webdriver-detection.js"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;GAeG;AACH,gCAFU,OAAO,CAmCb"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nreum.d.ts","sourceRoot":"","sources":["../../../../src/common/window/nreum.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"nreum.d.ts","sourceRoot":"","sources":["../../../../src/common/window/nreum.js"],"names":[],"mappings":"AAcA,gCAMC;AAED,oCAWC;AAED,4CASC;AAED,oCASC;AAED,yCAkBC;AAED,+EAaC;AAED;;;;GAIG;AACH,uDAGC;AAED,uDAGC;AAED,yCAGC;AAED,8BAMC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/page_view_event/aggregate/index.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/page_view_event/aggregate/index.js"],"names":[],"mappings":"AAwBA;IACE,2BAA2C;IAE3C,2BA4BC;IAzBC,iBAAoB;IAEpB,wBAAwB;IACxB,8BAA8B;IAC9B,8BAA8B;IAC9B,gBAAgB;IAsBlB;;;;OAIG;IACH,2BAFW,GAAC,QAiEX;IAbC;;;;;;;;;;;;;;mBAOC;IANC,iCAAyB;IAc7B,kCAEC;IAED;;;;;;aAiGC;CACF;8BAzN6B,4BAA4B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/page_view_timing/aggregate/index.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/page_view_timing/aggregate/index.js"],"names":[],"mappings":"AAuBA;IACE,2BAAiC;IAMjC,2BA4BC;IA1BC,4BAA+B;IAC/B,0BAA6B;IA2B/B;;;OAGG;IACH,6BAFW,MAAM,QAOhB;IAED;;;;MA6BC;IAED;;;OAGG;IACH,4BAFa,IAAI,CAahB;IAED,gDAWC;IAED,4BAGC;IAGD,qCAwBC;;CACF;8BAzJ6B,4BAA4B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/soft_navigations/aggregate/index.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/soft_navigations/aggregate/index.js"],"names":[],"mappings":"AAeA;IACE,2BAAiC;IACjC;;OAgEC;IA3DC,2BAAwC;IACxC,iBAA8B;IAE9B,uDAA0E;IAe1E,yBAA+B;IAC/B,0CAAiC;IACjC,yBAA4C;IAyC9C,qCAUC;IAED,0EAmBC;IAED,2BAiBC;IAED;;;;;;;OAOG;IACH,6BAHW,mBAAmB,OAsB7B;;CAiHF;8BA1Q6B,4BAA4B;2CAGf,iCAAiC;4BAChD,eAAe"}
|
|
@@ -68,6 +68,12 @@ export type RegisterAPIConstructor = {
|
|
|
68
68
|
* - The readable name for the registered entity. This will be assigned to any synthesized entities.
|
|
69
69
|
*/
|
|
70
70
|
name: string;
|
|
71
|
+
/**
|
|
72
|
+
* - The tags for the registered entity as key-value pairs. This will be assigned to any synthesized entities. Tags are converted to source.* attributes (e.g., {environment: 'production'} becomes source.environment: 'production').
|
|
73
|
+
*/
|
|
74
|
+
tags?: {
|
|
75
|
+
[key: string]: any;
|
|
76
|
+
} | undefined;
|
|
71
77
|
/**
|
|
72
78
|
* - When true, each registration creates an isolated instance. When false, multiple registrations with the same id and isolated: false will share a single instance, including all custom attributes, ids, names, and metadata. Calling deregister on a shared instance will deregister it for all entities using the instance. Defaults to true.
|
|
73
79
|
*/
|
|
@@ -89,6 +95,9 @@ export type RegisterAPIMetadata = {
|
|
|
89
95
|
licenseKey?: string | undefined;
|
|
90
96
|
id: string;
|
|
91
97
|
name: string;
|
|
98
|
+
tags?: {
|
|
99
|
+
[key: string]: any;
|
|
100
|
+
} | undefined;
|
|
92
101
|
parentId?: string | undefined;
|
|
93
102
|
isolated?: boolean | undefined;
|
|
94
103
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register-api-types.d.ts","sourceRoot":"","sources":["../../../../src/loaders/api/register-api-types.js"],"names":[],"mappings":";;;;;;mBAOc,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,IAAI;;;;SAC3C,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAA;KAAC,KAAK,IAAI;;;;iBACxH,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,KAAK,IAAI;;;;cAC1D,CAAC,MAAM,EAAE,sBAAsB,KAAK,WAAW;;;;gBAC/C,MAAM,IAAI;;;;uBACV,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,IAAI;;;;aAChD,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAA;KAAC,KAAK,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAA;KAAC,CAAC;;;;2BACrL,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI;;;;wBAC9B,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,IAAI;;;;eAClF,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,YAAY,CAAC,EAAE,OAAO,KAAK,IAAI;;;;cACtD,mBAAmB;;;;;;QAKnB,MAAM,GAAC,MAAM;;;;UACb,MAAM
|
|
1
|
+
{"version":3,"file":"register-api-types.d.ts","sourceRoot":"","sources":["../../../../src/loaders/api/register-api-types.js"],"names":[],"mappings":";;;;;;mBAOc,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,IAAI;;;;SAC3C,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAA;KAAC,KAAK,IAAI;;;;iBACxH,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,KAAK,IAAI;;;;cAC1D,CAAC,MAAM,EAAE,sBAAsB,KAAK,WAAW;;;;gBAC/C,MAAM,IAAI;;;;uBACV,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,IAAI;;;;aAChD,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAA;KAAC,KAAK,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAA;KAAC,CAAC;;;;2BACrL,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI;;;;wBAC9B,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,IAAI;;;;eAClF,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,YAAY,CAAC,EAAE,OAAO,KAAK,IAAI;;;;cACtD,mBAAmB;;;;;;QAKnB,MAAM,GAAC,MAAM;;;;UACb,MAAM;;;;;;;;;;;;;;;;;;;;sBAQN,MAAM;;;;YAEjB;QAA2B,UAAU;QACX,EAAE,EAAjB,MAAM;QACS,IAAI,EAAnB,MAAM;QACwB,IAAI;;;QAClB,QAAQ;QACP,QAAQ;KACtC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../../../src/loaders/api/register.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../../../src/loaders/api/register.js"],"names":[],"mappings":"AAyBA;;;;GAIG;AACH,mDAIC;0BAdY,OAAO,sBAAsB,EAAE,WAAW"}
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2020-
|
|
2
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -71,7 +71,7 @@ export function drain (agentIdentifier = '', featureName = 'feature', force = fa
|
|
|
71
71
|
|
|
72
72
|
/** 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 */
|
|
73
73
|
function checkCanDrainAll (agentIdentifier) {
|
|
74
|
-
// Only when the event-groups for all features are ready to drain (staged) do we execute the drain. This has the effect
|
|
74
|
+
// Only when the event-groups for all features are ready to drain (staged) do we execute the drain. This has the effect
|
|
75
75
|
// that the last feature to call drain triggers drain for all features.
|
|
76
76
|
const items = Array.from(registry[agentIdentifier])
|
|
77
77
|
if (items.every(([key, values]) => values.staged)) {
|
|
@@ -115,7 +115,7 @@ function drainGroup (agentIdentifier, group, activateGroup = true) {
|
|
|
115
115
|
// registration *should* be an array of: [targetEE, eventHandler]
|
|
116
116
|
// certain browser polyfills of .values and .entries pass the prototype methods into the callback,
|
|
117
117
|
// which breaks this assumption and throws errors. So we make sure here that we only call .on() if its an actual NR EE
|
|
118
|
-
if (registration[0]?.on && registration[0]
|
|
118
|
+
if (registration[0]?.on && registration[0].context() instanceof EventContext && !registration[0].listeners(eventType).includes(registration[1])) registration[0].on(eventType, registration[1])
|
|
119
119
|
})
|
|
120
120
|
})
|
|
121
121
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { isBrowserScope } from '../constants/runtime'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Detects if the page is being controlled by WebDriver or automation tools derived from it.
|
|
10
|
+
* Checks for common indicators including:
|
|
11
|
+
* - navigator.webdriver property (standard WebDriver flag)
|
|
12
|
+
* - window.document.__webdriver_evaluate (WebDriver internal property)
|
|
13
|
+
* - window.document.__selenium_unwrapped (Selenium property)
|
|
14
|
+
* - window.document.__driver_evaluate (WebDriver internal property)
|
|
15
|
+
* - window.document.__webdriver_script_function (WebDriver internal property)
|
|
16
|
+
* - window.callPhantom (PhantomJS property)
|
|
17
|
+
* - window._phantom (PhantomJS property)
|
|
18
|
+
* - window.__nightmare (Nightmare.js property)
|
|
19
|
+
* - window.domAutomation (Chrome automation property)
|
|
20
|
+
* - window.domAutomationController (Chrome automation property)
|
|
21
|
+
*
|
|
22
|
+
* @type {boolean}
|
|
23
|
+
*/
|
|
24
|
+
export const webdriverDetected = (() => {
|
|
25
|
+
try {
|
|
26
|
+
// Standard WebDriver flag
|
|
27
|
+
if (typeof navigator !== 'undefined' && navigator.webdriver === true) {
|
|
28
|
+
return true
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Check for various automation-related properties
|
|
32
|
+
if (isBrowserScope) {
|
|
33
|
+
// WebDriver internal properties
|
|
34
|
+
if (document.__webdriver_evaluate ||
|
|
35
|
+
document.__selenium_unwrapped ||
|
|
36
|
+
document.__driver_evaluate ||
|
|
37
|
+
document.__webdriver_script_function) {
|
|
38
|
+
return true
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// PhantomJS detection
|
|
42
|
+
if (window.callPhantom || window._phantom) {
|
|
43
|
+
return true
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Nightmare.js detection
|
|
47
|
+
if (window.__nightmare) {
|
|
48
|
+
return true
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return false
|
|
53
|
+
} catch (err) {
|
|
54
|
+
// If any errors occur during detection, assume not automated
|
|
55
|
+
return false
|
|
56
|
+
}
|
|
57
|
+
})()
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2020-
|
|
2
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
import { globalScope } from '../constants/runtime'
|
|
6
6
|
import { now } from '../timing/now'
|
|
7
|
+
import { warn } from '../util/console'
|
|
7
8
|
import { isNative } from '../util/monkey-patched'
|
|
8
9
|
|
|
9
10
|
export const defaults = {
|
|
@@ -82,6 +83,11 @@ export function setNREUMInitializedAgent (id, newAgentInstance) {
|
|
|
82
83
|
date: new Date()
|
|
83
84
|
}
|
|
84
85
|
nr.initializedAgents[id] = newAgentInstance
|
|
86
|
+
|
|
87
|
+
// Warn if using more than one agent, but only once per agent load
|
|
88
|
+
if (Object.keys(nr.initializedAgents).length === 2) {
|
|
89
|
+
warn(69)
|
|
90
|
+
}
|
|
85
91
|
}
|
|
86
92
|
|
|
87
93
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2020-
|
|
2
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
import { globalScope, isBrowserScope, originTime, supportsNavTimingL2 } from '../../../common/constants/runtime'
|
|
@@ -20,6 +20,7 @@ import { applyFnToProps } from '../../../common/util/traverse'
|
|
|
20
20
|
import { send } from '../../../common/harvest/harvester'
|
|
21
21
|
import { FEATURE_NAMES, FEATURE_TO_ENDPOINT } from '../../../loaders/features/features'
|
|
22
22
|
import { getSubmitMethod } from '../../../common/util/submit-data'
|
|
23
|
+
import { webdriverDetected } from '../../../common/util/webdriver-detection'
|
|
23
24
|
|
|
24
25
|
export class Aggregate extends AggregateBase {
|
|
25
26
|
static featureName = CONSTANTS.FEATURE_NAME
|
|
@@ -87,10 +88,7 @@ export class Aggregate extends AggregateBase {
|
|
|
87
88
|
|
|
88
89
|
if (this.agentRef.runtime.session) queryParameters.fsh = Number(this.agentRef.runtime.session.isNew) // "first session harvest" aka RUM request or PageView event of a session
|
|
89
90
|
|
|
90
|
-
let body
|
|
91
|
-
if (typeof customAttributes === 'object' && Object.keys(customAttributes).length > 0) {
|
|
92
|
-
body = applyFnToProps({ ja: customAttributes }, this.obfuscator.obfuscateString.bind(this.obfuscator), 'string')
|
|
93
|
-
}
|
|
91
|
+
let body = applyFnToProps({ ja: { ...customAttributes, webdriverDetected } }, this.obfuscator.obfuscateString.bind(this.obfuscator), 'string')
|
|
94
92
|
|
|
95
93
|
if (globalScope.performance) {
|
|
96
94
|
if (supportsNavTimingL2()) { // Navigation Timing level 2 API that replaced PerformanceTiming & PerformanceNavigation
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2020-
|
|
2
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -19,6 +19,7 @@ import { VITAL_NAMES } from '../../../common/vitals/constants'
|
|
|
19
19
|
import { initiallyHidden } from '../../../common/constants/runtime'
|
|
20
20
|
import { eventOrigin } from '../../../common/util/event-origin'
|
|
21
21
|
import { loadTime } from '../../../common/vitals/load-time'
|
|
22
|
+
import { webdriverDetected } from '../../../common/util/webdriver-detection'
|
|
22
23
|
|
|
23
24
|
export class Aggregate extends AggregateBase {
|
|
24
25
|
static featureName = FEATURE_NAME
|
|
@@ -126,6 +127,7 @@ export class Aggregate extends AggregateBase {
|
|
|
126
127
|
timingAttributes[key] = val
|
|
127
128
|
}
|
|
128
129
|
})
|
|
130
|
+
timingAttributes.webdriverDetected = webdriverDetected
|
|
129
131
|
}
|
|
130
132
|
|
|
131
133
|
preHarvestChecks () {
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { handle } from '../../../common/event-emitter/handle'
|
|
6
6
|
import { registerHandler } from '../../../common/event-emitter/register-handler'
|
|
7
|
+
import { webdriverDetected } from '../../../common/util/webdriver-detection'
|
|
7
8
|
import { loadTime } from '../../../common/vitals/load-time'
|
|
8
9
|
import { FEATURE_NAMES } from '../../../loaders/features/features'
|
|
9
10
|
import { AggregateBase } from '../../utils/aggregate-base'
|
|
@@ -25,6 +26,7 @@ export class Aggregate extends AggregateBase {
|
|
|
25
26
|
this.initialPageLoadInteraction = new InitialPageLoadInteraction(agentRef)
|
|
26
27
|
this.initialPageLoadInteraction.onDone.push(() => { // this ensures the .end() method also works with iPL
|
|
27
28
|
if (agentRef.runtime.session?.isNew) this.initialPageLoadInteraction.customAttributes.isFirstOfSession = true // mark the hard page load as first of its session
|
|
29
|
+
this.initialPageLoadInteraction.customAttributes.webdriverDetected = webdriverDetected
|
|
28
30
|
this.initialPageLoadInteraction.forceSave = true // unless forcibly ignored, iPL always finish by default
|
|
29
31
|
const ixn = this.initialPageLoadInteraction
|
|
30
32
|
this.events.add(ixn) // add the iPL ixn to the buffer for harvest
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
* @typedef {Object} RegisterAPIConstructor
|
|
23
23
|
* @property {string|number} id - The unique id for the registered entity. This will be assigned to any synthesized entities.
|
|
24
24
|
* @property {string} name - The readable name for the registered entity. This will be assigned to any synthesized entities.
|
|
25
|
+
* @property {{[key: string]: any}} [tags] - The tags for the registered entity as key-value pairs. This will be assigned to any synthesized entities. Tags are converted to source.* attributes (e.g., {environment: 'production'} becomes source.environment: 'production').
|
|
25
26
|
* @property {boolean} [isolated] - When true, each registration creates an isolated instance. When false, multiple registrations with the same id and isolated: false will share a single instance, including all custom attributes, ids, names, and metadata. Calling deregister on a shared instance will deregister it for all entities using the instance. Defaults to true.
|
|
26
27
|
* @property {string} [parentId] - The parentId for the registered entity. If none was supplied, it will assume the entity guid from the main agent.
|
|
27
28
|
*/
|
|
@@ -33,6 +34,7 @@
|
|
|
33
34
|
* @property {string} [target.licenseKey] - The license key for the registered entity. If none was supplied, it will assume the license key from the main agent.
|
|
34
35
|
* @property {string} target.id - The ID for the registered entity.
|
|
35
36
|
* @property {string} target.name - The name returned for the registered entity.
|
|
37
|
+
* @property {{[key: string]: any}} [target.tags] - The tags for the registered entity as key-value pairs.
|
|
36
38
|
* @property {string} [target.parentId] - The parentId for the registered entity. If none was supplied, it will assume the entity guid from the main agent.
|
|
37
39
|
* @property {boolean} [target.isolated] - When true, each registration creates an isolated instance. When false, multiple registrations with the same id and isolated: false will share a single instance, including all custom attributes, ids, names, and metadata. Calling deregister on a shared instance will deregister it for all entities using the instance. Defaults to true.
|
|
38
40
|
*/
|
|
@@ -21,6 +21,8 @@ import { recordCustomEvent } from './recordCustomEvent'
|
|
|
21
21
|
* @typedef {import('./register-api-types').RegisterAPI} RegisterAPI
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
+
const PROTECTED_KEYS = ['name', 'id', 'type']
|
|
25
|
+
|
|
24
26
|
/**
|
|
25
27
|
* @experimental
|
|
26
28
|
* IMPORTANT: This feature is being developed for use internally and is not in a public-facing production-ready state.
|
|
@@ -48,10 +50,17 @@ function register (agentRef, target, parent) {
|
|
|
48
50
|
target.licenseKey ||= agentRef.info.licenseKey // will inherit the license key from the container agent if not provided for brevity. A future state may dictate that we need different license keys to do different things.
|
|
49
51
|
target.blocked = false
|
|
50
52
|
target.parent = parent || {}
|
|
51
|
-
if (
|
|
53
|
+
if (typeof target.tags !== 'object' || target.tags === null || Array.isArray(target.tags)) target.tags = {}
|
|
52
54
|
|
|
53
55
|
const attrs = {}
|
|
54
|
-
|
|
56
|
+
|
|
57
|
+
// Process tags object and add to attrs, excluding protected keys
|
|
58
|
+
Object.entries(target.tags).forEach(([key, value]) => {
|
|
59
|
+
if (!PROTECTED_KEYS.includes(key)) {
|
|
60
|
+
attrs[`source.${key}`] = value
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
|
|
55
64
|
target.isolated ??= true
|
|
56
65
|
|
|
57
66
|
/** @type {Function} a function that is set and reports when APIs are triggered -- warns the customer of the invalid state */
|
|
@@ -136,12 +145,13 @@ function register (agentRef, target, parent) {
|
|
|
136
145
|
* @returns
|
|
137
146
|
*/
|
|
138
147
|
const report = (methodToCall, args, target) => {
|
|
139
|
-
if
|
|
148
|
+
/** Even if we are blocked, if registering we should still return a child register API so nested API calls do not throw errors */
|
|
149
|
+
if (isBlocked() && methodToCall !== register) return
|
|
140
150
|
/** set the timestamp before the async part of waiting for the rum response for better accuracy */
|
|
141
151
|
const timestamp = now()
|
|
142
152
|
handle(SUPPORTABILITY_METRIC_CHANNEL, [`API/register/${methodToCall.name}/called`], undefined, FEATURE_NAMES.metrics, agentRef.ee)
|
|
143
153
|
try {
|
|
144
|
-
const shouldDuplicate = agentRef.init.api.duplicate_registered_data && methodToCall
|
|
154
|
+
const shouldDuplicate = agentRef.init.api.duplicate_registered_data && methodToCall !== register
|
|
145
155
|
if (shouldDuplicate) {
|
|
146
156
|
let duplicatedArgs = args
|
|
147
157
|
if (args[1] instanceof Object) {
|