@newrelic/browser-agent 1.280.0 → 1.282.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 +14 -0
- package/dist/cjs/common/config/init.js +2 -1
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/dom/selector-path.js +20 -3
- package/dist/cjs/common/wrap/wrap-function.js +1 -0
- package/dist/cjs/common/wrap/wrap-websocket.js +23 -39
- package/dist/cjs/features/generic_events/aggregate/index.js +21 -14
- package/dist/cjs/features/generic_events/aggregate/user-actions/aggregated-user-action.js +2 -1
- package/dist/cjs/features/generic_events/aggregate/user-actions/user-actions-aggregator.js +20 -6
- package/dist/cjs/features/metrics/aggregate/index.js +7 -9
- package/dist/cjs/features/metrics/constants.js +3 -5
- package/dist/cjs/features/metrics/instrument/index.js +8 -11
- package/dist/esm/common/config/init.js +2 -1
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/common/dom/selector-path.js +20 -3
- package/dist/esm/common/wrap/wrap-function.js +1 -1
- package/dist/esm/common/wrap/wrap-websocket.js +23 -39
- package/dist/esm/features/generic_events/aggregate/index.js +21 -14
- package/dist/esm/features/generic_events/aggregate/user-actions/aggregated-user-action.js +2 -1
- package/dist/esm/features/generic_events/aggregate/user-actions/user-actions-aggregator.js +20 -6
- package/dist/esm/features/metrics/aggregate/index.js +8 -10
- package/dist/esm/features/metrics/constants.js +2 -3
- package/dist/esm/features/metrics/instrument/index.js +9 -11
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types/common/aggregate/event-aggregator.d.ts +1 -3
- package/dist/types/common/aggregate/event-aggregator.d.ts.map +1 -1
- package/dist/types/common/constants/agent-constants.d.ts.map +1 -1
- package/dist/types/common/constants/env.cdn.d.ts.map +1 -1
- package/dist/types/common/constants/env.d.ts.map +1 -1
- package/dist/types/common/constants/env.npm.d.ts.map +1 -1
- package/dist/types/common/dom/selector-path.d.ts +1 -1
- package/dist/types/common/dom/selector-path.d.ts.map +1 -1
- package/dist/types/common/session/constants.d.ts.map +1 -1
- package/dist/types/common/url/clean-url.d.ts +1 -1
- package/dist/types/common/url/clean-url.d.ts.map +1 -1
- package/dist/types/common/util/traverse.d.ts +1 -1
- package/dist/types/common/util/traverse.d.ts.map +1 -1
- package/dist/types/common/window/page-visibility.d.ts +1 -1
- package/dist/types/common/window/page-visibility.d.ts.map +1 -1
- package/dist/types/common/wrap/wrap-function.d.ts +11 -1
- package/dist/types/common/wrap/wrap-function.d.ts.map +1 -1
- package/dist/types/common/wrap/wrap-websocket.d.ts.map +1 -1
- package/dist/types/features/generic_events/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/generic_events/aggregate/user-actions/aggregated-user-action.d.ts +2 -1
- package/dist/types/features/generic_events/aggregate/user-actions/aggregated-user-action.d.ts.map +1 -1
- package/dist/types/features/generic_events/aggregate/user-actions/user-actions-aggregator.d.ts +1 -1
- package/dist/types/features/generic_events/aggregate/user-actions/user-actions-aggregator.d.ts.map +1 -1
- package/dist/types/features/generic_events/constants.d.ts.map +1 -1
- package/dist/types/features/logging/constants.d.ts.map +1 -1
- package/dist/types/features/metrics/constants.d.ts +1 -0
- package/dist/types/features/metrics/constants.d.ts.map +1 -1
- package/dist/types/features/session_replay/constants.d.ts.map +1 -1
- package/dist/types/features/session_trace/constants.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/constants.d.ts.map +1 -1
- package/dist/types/features/spa/constants.d.ts.map +1 -1
- package/dist/types/features/utils/feature-base.d.ts.map +1 -1
- package/dist/types/features/utils/instrument-base.d.ts +2 -2
- package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
- package/dist/types/loaders/agent-base.d.ts +2 -2
- package/dist/types/loaders/agent-base.d.ts.map +1 -1
- package/dist/types/loaders/api/api.d.ts +0 -10
- package/dist/types/loaders/api/api.d.ts.map +1 -1
- package/dist/types/loaders/features/features.d.ts.map +1 -1
- package/dist/types/loaders/micro-agent-base.d.ts +6 -6
- package/dist/types/loaders/micro-agent-base.d.ts.map +1 -1
- package/dist/types/loaders/micro-agent.d.ts +1 -1
- package/dist/types/loaders/micro-agent.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/common/config/init.js +1 -1
- package/src/common/dom/selector-path.js +15 -3
- package/src/common/wrap/wrap-function.js +1 -1
- package/src/common/wrap/wrap-websocket.js +24 -40
- package/src/features/generic_events/aggregate/index.js +21 -6
- package/src/features/generic_events/aggregate/user-actions/aggregated-user-action.js +2 -1
- package/src/features/generic_events/aggregate/user-actions/user-actions-aggregator.js +11 -7
- package/src/features/metrics/aggregate/index.js +8 -8
- package/src/features/metrics/constants.js +2 -2
- package/src/features/metrics/instrument/index.js +9 -9
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,20 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [1.282.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.281.0...v1.282.0) (2025-02-13)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* Re-implement wrap-websocket into agent ([#1342](https://github.com/newrelic/newrelic-browser-agent/issues/1342)) ([9b2756f](https://github.com/newrelic/newrelic-browser-agent/commit/9b2756f520cb946192e72d138e14fb4fca75a7b8))
|
|
12
|
+
|
|
13
|
+
## [1.281.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.280.0...v1.281.0) (2025-02-04)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Features
|
|
17
|
+
|
|
18
|
+
* Capture Nearest UserAction Fields ([#1267](https://github.com/newrelic/newrelic-browser-agent/issues/1267)) ([d410937](https://github.com/newrelic/newrelic-browser-agent/commit/d410937983545a6a6aa39c52c3762f621acf1110))
|
|
19
|
+
|
|
6
20
|
## [1.280.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.279.1...v1.280.0) (2025-01-31)
|
|
7
21
|
|
|
8
22
|
|
|
@@ -17,7 +17,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_EN
|
|
|
17
17
|
/**
|
|
18
18
|
* Exposes the version of the agent
|
|
19
19
|
*/
|
|
20
|
-
const VERSION = exports.VERSION = "1.
|
|
20
|
+
const VERSION = exports.VERSION = "1.282.0";
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Exposes the build type of the agent
|
|
@@ -17,7 +17,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_EN
|
|
|
17
17
|
/**
|
|
18
18
|
* Exposes the version of the agent
|
|
19
19
|
*/
|
|
20
|
-
const VERSION = exports.VERSION = "1.
|
|
20
|
+
const VERSION = exports.VERSION = "1.282.0";
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Exposes the build type of the agent
|
|
@@ -16,8 +16,11 @@ exports.generateSelectorPath = void 0;
|
|
|
16
16
|
* @param {boolean} includeClass
|
|
17
17
|
* @returns {string|undefined}
|
|
18
18
|
*/
|
|
19
|
-
const generateSelectorPath = elem => {
|
|
20
|
-
if (!elem) return
|
|
19
|
+
const generateSelectorPath = (elem, targetFields = []) => {
|
|
20
|
+
if (!elem) return {
|
|
21
|
+
path: undefined,
|
|
22
|
+
nearestFields: {}
|
|
23
|
+
};
|
|
21
24
|
const getNthOfTypeIndex = node => {
|
|
22
25
|
try {
|
|
23
26
|
let i = 1;
|
|
@@ -35,12 +38,16 @@ const generateSelectorPath = elem => {
|
|
|
35
38
|
};
|
|
36
39
|
let pathSelector = '';
|
|
37
40
|
let index = getNthOfTypeIndex(elem);
|
|
41
|
+
const nearestFields = {};
|
|
38
42
|
try {
|
|
39
43
|
while (elem?.tagName) {
|
|
40
44
|
const {
|
|
41
45
|
id,
|
|
42
46
|
localName
|
|
43
47
|
} = elem;
|
|
48
|
+
targetFields.forEach(field => {
|
|
49
|
+
nearestFields[nearestAttrName(field)] ||= elem[field];
|
|
50
|
+
});
|
|
44
51
|
const selector = [localName, id ? "#".concat(id) : '', pathSelector ? ">".concat(pathSelector) : ''].join('');
|
|
45
52
|
pathSelector = selector;
|
|
46
53
|
elem = elem.parentNode;
|
|
@@ -48,6 +55,16 @@ const generateSelectorPath = elem => {
|
|
|
48
55
|
} catch (err) {
|
|
49
56
|
// do nothing for now
|
|
50
57
|
}
|
|
51
|
-
|
|
58
|
+
const path = pathSelector ? index ? "".concat(pathSelector, ":nth-of-type(").concat(index, ")") : pathSelector : undefined;
|
|
59
|
+
return {
|
|
60
|
+
path,
|
|
61
|
+
nearestFields
|
|
62
|
+
};
|
|
63
|
+
function nearestAttrName(originalFieldName) {
|
|
64
|
+
/** preserve original renaming structure for pre-existing field maps */
|
|
65
|
+
if (originalFieldName === 'tagName') originalFieldName = 'tag';
|
|
66
|
+
if (originalFieldName === 'className') originalFieldName = 'class';
|
|
67
|
+
return "nearest".concat(originalFieldName.charAt(0).toUpperCase() + originalFieldName.slice(1));
|
|
68
|
+
}
|
|
52
69
|
};
|
|
53
70
|
exports.generateSelectorPath = generateSelectorPath;
|
|
@@ -30,49 +30,33 @@ function wrapWebSocket(sharedEE) {
|
|
|
30
30
|
sharedEE.emit(WEBSOCKET_TAG + message, [timestamp, timestamp - createdAt, isLoaded, socketId, ...data]);
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
33
|
+
class WrappedWebSocket extends WebSocket {
|
|
34
|
+
static name = 'WebSocket';
|
|
35
|
+
constructor(...args) {
|
|
36
|
+
super(...args);
|
|
37
|
+
const socketId = (0, _uniqueId.generateRandomHexString)(6);
|
|
38
|
+
this.report = reporter(socketId);
|
|
39
|
+
this.report('new');
|
|
40
|
+
const events = ['message', 'error', 'open', 'close'];
|
|
41
|
+
/** add event listeners */
|
|
42
|
+
events.forEach(evt => {
|
|
43
|
+
this.addEventListener(evt, function (e) {
|
|
44
|
+
this.report(ADD_EVENT_LISTENER_TAG, {
|
|
45
|
+
eventType: evt,
|
|
46
|
+
event: e
|
|
47
|
+
});
|
|
48
48
|
});
|
|
49
49
|
});
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (originalProp) {
|
|
59
|
-
Object.defineProperty(proxiedProp, 'name', {
|
|
60
|
-
value: prop
|
|
61
|
-
});
|
|
62
|
-
function proxiedProp() {
|
|
63
|
-
report(prop, ...arguments);
|
|
64
|
-
try {
|
|
65
|
-
return originalProp.apply(this, arguments);
|
|
66
|
-
} catch (err) {
|
|
67
|
-
report(prop + '-err', ...arguments);
|
|
68
|
-
// rethrow error so we don't effect execution by observing.
|
|
69
|
-
throw err;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
ws[prop] = proxiedProp;
|
|
50
|
+
}
|
|
51
|
+
send(...args) {
|
|
52
|
+
this.report('send', ...args);
|
|
53
|
+
try {
|
|
54
|
+
return super.send(...args);
|
|
55
|
+
} catch (err) {
|
|
56
|
+
this.report('send-err', ...args);
|
|
57
|
+
throw err;
|
|
73
58
|
}
|
|
74
59
|
}
|
|
75
|
-
return ws;
|
|
76
60
|
}
|
|
77
61
|
_runtime.globalScope.WebSocket = WrappedWebSocket;
|
|
78
62
|
return sharedEE;
|
|
@@ -61,7 +61,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
61
61
|
});
|
|
62
62
|
}, this.featureName, this.ee);
|
|
63
63
|
}
|
|
64
|
-
let addUserAction;
|
|
64
|
+
let addUserAction = () => {/** no-op */};
|
|
65
65
|
if (_runtime.isBrowserScope && agentRef.init.user_actions.enabled) {
|
|
66
66
|
this.userActionAggregator = new _userActionsAggregator.UserActionsAggregator();
|
|
67
67
|
this.harvestOpts.beforeUnload = () => addUserAction?.(this.userActionAggregator.aggregationEvent);
|
|
@@ -87,20 +87,27 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
87
87
|
...((0, _iframe.isIFrameWindow)(window) && {
|
|
88
88
|
iframe: true
|
|
89
89
|
}),
|
|
90
|
-
...(
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
...(canTrustTargetAttribute('type') && {
|
|
97
|
-
targetType: target.type
|
|
98
|
-
}),
|
|
99
|
-
...(canTrustTargetAttribute('className') && {
|
|
100
|
-
targetClass: target.className
|
|
101
|
-
})
|
|
90
|
+
...this.agentRef.init.user_actions.elementAttributes.reduce((acc, field) => {
|
|
91
|
+
/** prevent us from capturing an obscenely long value */
|
|
92
|
+
if (canTrustTargetAttribute(field)) acc[targetAttrName(field)] = String(target[field]).trim().slice(0, 128);
|
|
93
|
+
return acc;
|
|
94
|
+
}, {}),
|
|
95
|
+
...aggregatedUserAction.nearestTargetFields
|
|
102
96
|
});
|
|
103
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Returns the original target field name with `target` prepended and camelCased
|
|
100
|
+
* @param {string} originalFieldName
|
|
101
|
+
* @returns {string} the target field name
|
|
102
|
+
*/
|
|
103
|
+
function targetAttrName(originalFieldName) {
|
|
104
|
+
/** preserve original renaming structure for pre-existing field maps */
|
|
105
|
+
if (originalFieldName === 'tagName') originalFieldName = 'tag';
|
|
106
|
+
if (originalFieldName === 'className') originalFieldName = 'class';
|
|
107
|
+
/** return the original field name, cap'd and prepended with target to match formatting */
|
|
108
|
+
return "target".concat(originalFieldName.charAt(0).toUpperCase() + originalFieldName.slice(1));
|
|
109
|
+
}
|
|
110
|
+
|
|
104
111
|
/**
|
|
105
112
|
* Only trust attributes that exist on HTML element targets, which excludes the window and the document targets
|
|
106
113
|
* @param {string} attribute The attribute to check for on the target element
|
|
@@ -116,7 +123,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
116
123
|
};
|
|
117
124
|
(0, _registerHandler.registerHandler)('ua', evt => {
|
|
118
125
|
/** the processor will return the previously aggregated event if it has been completed by processing the current event */
|
|
119
|
-
addUserAction(this.userActionAggregator.process(evt));
|
|
126
|
+
addUserAction(this.userActionAggregator.process(evt, this.agentRef.init.user_actions.elementAttributes));
|
|
120
127
|
}, this.featureName, this.ee);
|
|
121
128
|
}
|
|
122
129
|
|
|
@@ -11,13 +11,14 @@ var _constants = require("../../constants");
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
class AggregatedUserAction {
|
|
14
|
-
constructor(evt, selectorPath) {
|
|
14
|
+
constructor(evt, selectorPath, nearestTargetFields) {
|
|
15
15
|
this.event = evt;
|
|
16
16
|
this.count = 1;
|
|
17
17
|
this.originMs = Math.floor(evt.timeStamp);
|
|
18
18
|
this.relativeMs = [0];
|
|
19
19
|
this.selectorPath = selectorPath;
|
|
20
20
|
this.rageClick = undefined;
|
|
21
|
+
this.nearestTargetFields = nearestTargetFields;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
/**
|
|
@@ -31,9 +31,12 @@ class UserActionsAggregator {
|
|
|
31
31
|
* @param {Event} evt The event supplied by the addEventListener callback
|
|
32
32
|
* @returns {AggregatedUserAction|undefined} The previous aggregation set if it has been completed by processing the current event
|
|
33
33
|
*/
|
|
34
|
-
process(evt) {
|
|
34
|
+
process(evt, targetFields) {
|
|
35
35
|
if (!evt) return;
|
|
36
|
-
const
|
|
36
|
+
const {
|
|
37
|
+
selectorPath,
|
|
38
|
+
nearestTargetFields
|
|
39
|
+
} = getSelectorPath(evt, targetFields);
|
|
37
40
|
const aggregationKey = getAggregationKey(evt, selectorPath);
|
|
38
41
|
if (!!aggregationKey && aggregationKey === this.#aggregationKey) {
|
|
39
42
|
// an aggregation exists already, so lets just continue to increment
|
|
@@ -43,7 +46,7 @@ class UserActionsAggregator {
|
|
|
43
46
|
const finishedEvent = this.#aggregationEvent;
|
|
44
47
|
// then set as this new event aggregation
|
|
45
48
|
this.#aggregationKey = aggregationKey;
|
|
46
|
-
this.#aggregationEvent = new _aggregatedUserAction.AggregatedUserAction(evt, selectorPath);
|
|
49
|
+
this.#aggregationEvent = new _aggregatedUserAction.AggregatedUserAction(evt, selectorPath, nearestTargetFields);
|
|
47
50
|
return finishedEvent;
|
|
48
51
|
}
|
|
49
52
|
}
|
|
@@ -56,13 +59,24 @@ class UserActionsAggregator {
|
|
|
56
59
|
* @returns {string}
|
|
57
60
|
*/
|
|
58
61
|
exports.UserActionsAggregator = UserActionsAggregator;
|
|
59
|
-
function getSelectorPath(evt) {
|
|
62
|
+
function getSelectorPath(evt, targetFields) {
|
|
60
63
|
let selectorPath;
|
|
64
|
+
let nearestTargetFields = {};
|
|
61
65
|
if (_constants.OBSERVED_WINDOW_EVENTS.includes(evt.type) || evt.target === window) selectorPath = 'window';else if (evt.target === document) selectorPath = 'document';
|
|
62
66
|
// if still no selectorPath, generate one from target tree that includes elem ids
|
|
63
|
-
else
|
|
67
|
+
else {
|
|
68
|
+
const {
|
|
69
|
+
path,
|
|
70
|
+
nearestFields
|
|
71
|
+
} = (0, _selectorPath.generateSelectorPath)(evt.target, targetFields);
|
|
72
|
+
selectorPath = path;
|
|
73
|
+
nearestTargetFields = nearestFields;
|
|
74
|
+
}
|
|
64
75
|
// if STILL no selectorPath, it will return undefined which will skip aggregation for this event
|
|
65
|
-
return
|
|
76
|
+
return {
|
|
77
|
+
selectorPath,
|
|
78
|
+
nearestTargetFields
|
|
79
|
+
};
|
|
66
80
|
}
|
|
67
81
|
|
|
68
82
|
/**
|
|
@@ -13,14 +13,13 @@ var _eventListenerOpts = require("../../../common/event-listener/event-listener-
|
|
|
13
13
|
var _runtime = require("../../../common/constants/runtime");
|
|
14
14
|
var _aggregateBase = require("../../utils/aggregate-base");
|
|
15
15
|
var _iframe = require("../../../common/dom/iframe");
|
|
16
|
+
var _wrapWebsocket = require("../../../common/wrap/wrap-websocket");
|
|
17
|
+
var _websocketDetection = require("./websocket-detection");
|
|
16
18
|
/**
|
|
17
19
|
* Copyright 2020-2025 New Relic, Inc. All rights reserved.
|
|
18
20
|
* SPDX-License-Identifier: Apache-2.0
|
|
19
21
|
*/
|
|
20
22
|
|
|
21
|
-
// import { WEBSOCKET_TAG } from '../../../common/wrap/wrap-websocket'
|
|
22
|
-
// import { handleWebsocketEvents } from './websocket-detection'
|
|
23
|
-
|
|
24
23
|
class Aggregate extends _aggregateBase.AggregateBase {
|
|
25
24
|
static featureName = _constants.FEATURE_NAME;
|
|
26
25
|
constructor(agentRef) {
|
|
@@ -134,12 +133,11 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
134
133
|
subtree: true
|
|
135
134
|
});
|
|
136
135
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
// })
|
|
136
|
+
_constants.WATCHABLE_WEB_SOCKET_EVENTS.forEach(tag => {
|
|
137
|
+
(0, _registerHandler.registerHandler)('buffered-' + _wrapWebsocket.WEBSOCKET_TAG + tag, (...args) => {
|
|
138
|
+
(0, _websocketDetection.handleWebsocketEvents)(this.storeSupportabilityMetrics.bind(this), tag, ...args);
|
|
139
|
+
}, this.featureName, this.ee);
|
|
140
|
+
});
|
|
143
141
|
}
|
|
144
142
|
eachSessionChecks() {
|
|
145
143
|
if (!_runtime.isBrowserScope) return;
|
|
@@ -3,19 +3,17 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.SUPPORTABILITY_METRIC_CHANNEL = exports.SUPPORTABILITY_METRIC = exports.FEATURE_NAME = exports.CUSTOM_METRIC_CHANNEL = exports.CUSTOM_METRIC = void 0;
|
|
6
|
+
exports.WATCHABLE_WEB_SOCKET_EVENTS = exports.SUPPORTABILITY_METRIC_CHANNEL = exports.SUPPORTABILITY_METRIC = exports.FEATURE_NAME = exports.CUSTOM_METRIC_CHANNEL = exports.CUSTOM_METRIC = void 0;
|
|
7
|
+
var _wrapWebsocket = require("../../common/wrap/wrap-websocket");
|
|
7
8
|
var _features = require("../../loaders/features/features");
|
|
8
9
|
/**
|
|
9
10
|
* Copyright 2020-2025 New Relic, Inc. All rights reserved.
|
|
10
11
|
* SPDX-License-Identifier: Apache-2.0
|
|
11
12
|
*/
|
|
12
13
|
|
|
13
|
-
// import { ADD_EVENT_LISTENER_TAG } from '../../common/wrap/wrap-websocket'
|
|
14
|
-
|
|
15
14
|
const FEATURE_NAME = exports.FEATURE_NAME = _features.FEATURE_NAMES.metrics;
|
|
16
15
|
const SUPPORTABILITY_METRIC = exports.SUPPORTABILITY_METRIC = 'sm';
|
|
17
16
|
const CUSTOM_METRIC = exports.CUSTOM_METRIC = 'cm';
|
|
18
17
|
const SUPPORTABILITY_METRIC_CHANNEL = exports.SUPPORTABILITY_METRIC_CHANNEL = 'storeSupportabilityMetrics';
|
|
19
18
|
const CUSTOM_METRIC_CHANNEL = exports.CUSTOM_METRIC_CHANNEL = 'storeEventMetrics';
|
|
20
|
-
|
|
21
|
-
// export const WATCHABLE_WEB_SOCKET_EVENTS = ['new', 'send', 'close', ADD_EVENT_LISTENER_TAG]
|
|
19
|
+
const WATCHABLE_WEB_SOCKET_EVENTS = exports.WATCHABLE_WEB_SOCKET_EVENTS = ['new', 'send', 'close', _wrapWebsocket.ADD_EVENT_LISTENER_TAG];
|
|
@@ -4,6 +4,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.Metrics = exports.Instrument = void 0;
|
|
7
|
+
var _handle = require("../../../common/event-emitter/handle");
|
|
8
|
+
var _wrapWebsocket = require("../../../common/wrap/wrap-websocket");
|
|
7
9
|
var _instrumentBase = require("../../utils/instrument-base");
|
|
8
10
|
var _constants = require("../constants");
|
|
9
11
|
/**
|
|
@@ -11,21 +13,16 @@ var _constants = require("../constants");
|
|
|
11
13
|
* SPDX-License-Identifier: Apache-2.0
|
|
12
14
|
*/
|
|
13
15
|
|
|
14
|
-
// import { handle } from '../../../common/event-emitter/handle'
|
|
15
|
-
// import { WEBSOCKET_TAG, wrapWebSocket } from '../../../common/wrap/wrap-websocket'
|
|
16
|
-
|
|
17
16
|
class Instrument extends _instrumentBase.InstrumentBase {
|
|
18
17
|
static featureName = _constants.FEATURE_NAME;
|
|
19
18
|
constructor(agentRef, auto = true) {
|
|
20
19
|
super(agentRef, _constants.FEATURE_NAME, auto);
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
// })
|
|
28
|
-
|
|
20
|
+
(0, _wrapWebsocket.wrapWebSocket)(this.ee);
|
|
21
|
+
_constants.WATCHABLE_WEB_SOCKET_EVENTS.forEach(suffix => {
|
|
22
|
+
this.ee.on(_wrapWebsocket.WEBSOCKET_TAG + suffix, (...args) => {
|
|
23
|
+
(0, _handle.handle)('buffered-' + _wrapWebsocket.WEBSOCKET_TAG + suffix, [...args], undefined, this.featureName, this.ee);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
29
26
|
this.importAggregator(agentRef);
|
|
30
27
|
}
|
|
31
28
|
}
|
|
@@ -10,8 +10,11 @@
|
|
|
10
10
|
* @param {boolean} includeClass
|
|
11
11
|
* @returns {string|undefined}
|
|
12
12
|
*/
|
|
13
|
-
export const generateSelectorPath = elem => {
|
|
14
|
-
if (!elem) return
|
|
13
|
+
export const generateSelectorPath = (elem, targetFields = []) => {
|
|
14
|
+
if (!elem) return {
|
|
15
|
+
path: undefined,
|
|
16
|
+
nearestFields: {}
|
|
17
|
+
};
|
|
15
18
|
const getNthOfTypeIndex = node => {
|
|
16
19
|
try {
|
|
17
20
|
let i = 1;
|
|
@@ -29,12 +32,16 @@ export const generateSelectorPath = elem => {
|
|
|
29
32
|
};
|
|
30
33
|
let pathSelector = '';
|
|
31
34
|
let index = getNthOfTypeIndex(elem);
|
|
35
|
+
const nearestFields = {};
|
|
32
36
|
try {
|
|
33
37
|
while (elem?.tagName) {
|
|
34
38
|
const {
|
|
35
39
|
id,
|
|
36
40
|
localName
|
|
37
41
|
} = elem;
|
|
42
|
+
targetFields.forEach(field => {
|
|
43
|
+
nearestFields[nearestAttrName(field)] ||= elem[field];
|
|
44
|
+
});
|
|
38
45
|
const selector = [localName, id ? "#".concat(id) : '', pathSelector ? ">".concat(pathSelector) : ''].join('');
|
|
39
46
|
pathSelector = selector;
|
|
40
47
|
elem = elem.parentNode;
|
|
@@ -42,5 +49,15 @@ export const generateSelectorPath = elem => {
|
|
|
42
49
|
} catch (err) {
|
|
43
50
|
// do nothing for now
|
|
44
51
|
}
|
|
45
|
-
|
|
52
|
+
const path = pathSelector ? index ? "".concat(pathSelector, ":nth-of-type(").concat(index, ")") : pathSelector : undefined;
|
|
53
|
+
return {
|
|
54
|
+
path,
|
|
55
|
+
nearestFields
|
|
56
|
+
};
|
|
57
|
+
function nearestAttrName(originalFieldName) {
|
|
58
|
+
/** preserve original renaming structure for pre-existing field maps */
|
|
59
|
+
if (originalFieldName === 'tagName') originalFieldName = 'tag';
|
|
60
|
+
if (originalFieldName === 'className') originalFieldName = 'class';
|
|
61
|
+
return "nearest".concat(originalFieldName.charAt(0).toUpperCase() + originalFieldName.slice(1));
|
|
62
|
+
}
|
|
46
63
|
};
|
|
@@ -173,7 +173,7 @@ function report(args, emitter) {
|
|
|
173
173
|
* Defaults to the global event emitter.
|
|
174
174
|
* @returns {object} - The destination founction or object with copied properties.
|
|
175
175
|
*/
|
|
176
|
-
function copy(from, to, emitter) {
|
|
176
|
+
export function copy(from, to, emitter) {
|
|
177
177
|
if (Object.defineProperty && Object.keys) {
|
|
178
178
|
// Create accessors that proxy to actual function
|
|
179
179
|
try {
|
|
@@ -22,49 +22,33 @@ export function wrapWebSocket(sharedEE) {
|
|
|
22
22
|
sharedEE.emit(WEBSOCKET_TAG + message, [timestamp, timestamp - createdAt, isLoaded, socketId, ...data]);
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
25
|
+
class WrappedWebSocket extends WebSocket {
|
|
26
|
+
static name = 'WebSocket';
|
|
27
|
+
constructor(...args) {
|
|
28
|
+
super(...args);
|
|
29
|
+
const socketId = generateRandomHexString(6);
|
|
30
|
+
this.report = reporter(socketId);
|
|
31
|
+
this.report('new');
|
|
32
|
+
const events = ['message', 'error', 'open', 'close'];
|
|
33
|
+
/** add event listeners */
|
|
34
|
+
events.forEach(evt => {
|
|
35
|
+
this.addEventListener(evt, function (e) {
|
|
36
|
+
this.report(ADD_EVENT_LISTENER_TAG, {
|
|
37
|
+
eventType: evt,
|
|
38
|
+
event: e
|
|
39
|
+
});
|
|
40
40
|
});
|
|
41
41
|
});
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (originalProp) {
|
|
51
|
-
Object.defineProperty(proxiedProp, 'name', {
|
|
52
|
-
value: prop
|
|
53
|
-
});
|
|
54
|
-
function proxiedProp() {
|
|
55
|
-
report(prop, ...arguments);
|
|
56
|
-
try {
|
|
57
|
-
return originalProp.apply(this, arguments);
|
|
58
|
-
} catch (err) {
|
|
59
|
-
report(prop + '-err', ...arguments);
|
|
60
|
-
// rethrow error so we don't effect execution by observing.
|
|
61
|
-
throw err;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
ws[prop] = proxiedProp;
|
|
42
|
+
}
|
|
43
|
+
send(...args) {
|
|
44
|
+
this.report('send', ...args);
|
|
45
|
+
try {
|
|
46
|
+
return super.send(...args);
|
|
47
|
+
} catch (err) {
|
|
48
|
+
this.report('send-err', ...args);
|
|
49
|
+
throw err;
|
|
65
50
|
}
|
|
66
51
|
}
|
|
67
|
-
return ws;
|
|
68
52
|
}
|
|
69
53
|
globalScope.WebSocket = WrappedWebSocket;
|
|
70
54
|
return sharedEE;
|
|
@@ -54,7 +54,7 @@ export class Aggregate extends AggregateBase {
|
|
|
54
54
|
});
|
|
55
55
|
}, this.featureName, this.ee);
|
|
56
56
|
}
|
|
57
|
-
let addUserAction;
|
|
57
|
+
let addUserAction = () => {/** no-op */};
|
|
58
58
|
if (isBrowserScope && agentRef.init.user_actions.enabled) {
|
|
59
59
|
this.userActionAggregator = new UserActionsAggregator();
|
|
60
60
|
this.harvestOpts.beforeUnload = () => addUserAction?.(this.userActionAggregator.aggregationEvent);
|
|
@@ -80,20 +80,27 @@ export class Aggregate extends AggregateBase {
|
|
|
80
80
|
...(isIFrameWindow(window) && {
|
|
81
81
|
iframe: true
|
|
82
82
|
}),
|
|
83
|
-
...(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
...(canTrustTargetAttribute('type') && {
|
|
90
|
-
targetType: target.type
|
|
91
|
-
}),
|
|
92
|
-
...(canTrustTargetAttribute('className') && {
|
|
93
|
-
targetClass: target.className
|
|
94
|
-
})
|
|
83
|
+
...this.agentRef.init.user_actions.elementAttributes.reduce((acc, field) => {
|
|
84
|
+
/** prevent us from capturing an obscenely long value */
|
|
85
|
+
if (canTrustTargetAttribute(field)) acc[targetAttrName(field)] = String(target[field]).trim().slice(0, 128);
|
|
86
|
+
return acc;
|
|
87
|
+
}, {}),
|
|
88
|
+
...aggregatedUserAction.nearestTargetFields
|
|
95
89
|
});
|
|
96
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Returns the original target field name with `target` prepended and camelCased
|
|
93
|
+
* @param {string} originalFieldName
|
|
94
|
+
* @returns {string} the target field name
|
|
95
|
+
*/
|
|
96
|
+
function targetAttrName(originalFieldName) {
|
|
97
|
+
/** preserve original renaming structure for pre-existing field maps */
|
|
98
|
+
if (originalFieldName === 'tagName') originalFieldName = 'tag';
|
|
99
|
+
if (originalFieldName === 'className') originalFieldName = 'class';
|
|
100
|
+
/** return the original field name, cap'd and prepended with target to match formatting */
|
|
101
|
+
return "target".concat(originalFieldName.charAt(0).toUpperCase() + originalFieldName.slice(1));
|
|
102
|
+
}
|
|
103
|
+
|
|
97
104
|
/**
|
|
98
105
|
* Only trust attributes that exist on HTML element targets, which excludes the window and the document targets
|
|
99
106
|
* @param {string} attribute The attribute to check for on the target element
|
|
@@ -109,7 +116,7 @@ export class Aggregate extends AggregateBase {
|
|
|
109
116
|
};
|
|
110
117
|
registerHandler('ua', evt => {
|
|
111
118
|
/** the processor will return the previously aggregated event if it has been completed by processing the current event */
|
|
112
|
-
addUserAction(this.userActionAggregator.process(evt));
|
|
119
|
+
addUserAction(this.userActionAggregator.process(evt, this.agentRef.init.user_actions.elementAttributes));
|
|
113
120
|
}, this.featureName, this.ee);
|
|
114
121
|
}
|
|
115
122
|
|
|
@@ -4,13 +4,14 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { RAGE_CLICK_THRESHOLD_EVENTS, RAGE_CLICK_THRESHOLD_MS } from '../../constants';
|
|
6
6
|
export class AggregatedUserAction {
|
|
7
|
-
constructor(evt, selectorPath) {
|
|
7
|
+
constructor(evt, selectorPath, nearestTargetFields) {
|
|
8
8
|
this.event = evt;
|
|
9
9
|
this.count = 1;
|
|
10
10
|
this.originMs = Math.floor(evt.timeStamp);
|
|
11
11
|
this.relativeMs = [0];
|
|
12
12
|
this.selectorPath = selectorPath;
|
|
13
13
|
this.rageClick = undefined;
|
|
14
|
+
this.nearestTargetFields = nearestTargetFields;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
/**
|