@newrelic/browser-agent 1.279.1 → 1.281.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 +19 -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/vitals/constants.js +2 -5
- package/dist/cjs/common/vitals/first-input-delay.js +36 -0
- package/dist/cjs/features/generic_events/aggregate/index.js +32 -24
- 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/logging/aggregate/index.js +4 -3
- package/dist/cjs/features/page_view_timing/aggregate/index.js +2 -2
- package/dist/cjs/features/session_trace/aggregate/trace/storage.js +7 -0
- package/dist/cjs/loaders/agent.js +2 -3
- package/dist/cjs/loaders/micro-agent-base.js +2 -2
- package/dist/cjs/loaders/micro-agent.js +2 -3
- 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/vitals/constants.js +1 -4
- package/dist/esm/common/vitals/first-input-delay.js +29 -0
- package/dist/esm/features/generic_events/aggregate/index.js +32 -24
- 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/logging/aggregate/index.js +4 -3
- package/dist/esm/features/page_view_timing/aggregate/index.js +2 -2
- package/dist/esm/features/session_trace/aggregate/trace/storage.js +7 -0
- package/dist/esm/loaders/agent.js +2 -3
- package/dist/esm/loaders/micro-agent-base.js +2 -2
- package/dist/esm/loaders/micro-agent.js +2 -3
- 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/vitals/constants.d.ts +1 -4
- package/dist/types/common/vitals/first-input-delay.d.ts +3 -0
- package/dist/types/common/vitals/first-input-delay.d.ts.map +1 -0
- 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/logging/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_trace/aggregate/trace/storage.d.ts.map +1 -1
- package/dist/types/loaders/agent.d.ts +1 -2
- package/dist/types/loaders/agent.d.ts.map +1 -1
- package/dist/types/loaders/micro-agent-base.d.ts +0 -1
- package/dist/types/loaders/micro-agent-base.d.ts.map +1 -1
- package/dist/types/loaders/micro-agent.d.ts +1 -2
- 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/vitals/constants.js +1 -5
- package/src/common/vitals/first-input-delay.js +28 -0
- package/src/features/generic_events/aggregate/index.js +32 -16
- 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/logging/aggregate/index.js +4 -3
- package/src/features/page_view_timing/aggregate/index.js +2 -2
- package/src/features/session_trace/aggregate/trace/storage.js +5 -0
- package/src/loaders/agent.js +2 -3
- package/src/loaders/micro-agent-base.js +2 -2
- package/src/loaders/micro-agent.js +2 -3
- package/dist/cjs/common/vitals/first-interaction.js +0 -44
- package/dist/esm/common/vitals/first-interaction.js +0 -38
- package/dist/types/common/vitals/first-interaction.d.ts +0 -3
- package/dist/types/common/vitals/first-interaction.d.ts.map +0 -1
- package/src/common/vitals/first-interaction.js +0 -38
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,25 @@
|
|
|
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.281.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.280.0...v1.281.0) (2025-02-04)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* Capture Nearest UserAction Fields ([#1267](https://github.com/newrelic/newrelic-browser-agent/issues/1267)) ([d410937](https://github.com/newrelic/newrelic-browser-agent/commit/d410937983545a6a6aa39c52c3762f621acf1110))
|
|
12
|
+
|
|
13
|
+
## [1.280.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.279.1...v1.280.0) (2025-01-31)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Features
|
|
17
|
+
|
|
18
|
+
* Remove agentIdentifier argument from agent constructors ([#1353](https://github.com/newrelic/newrelic-browser-agent/issues/1353)) ([cb866e5](https://github.com/newrelic/newrelic-browser-agent/commit/cb866e5678bf6aa898c082f2be83145a5014fd0e))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### Bug Fixes
|
|
22
|
+
|
|
23
|
+
* Roll back to previous FirstInteraction implementation ([#1359](https://github.com/newrelic/newrelic-browser-agent/issues/1359)) ([c2e22ab](https://github.com/newrelic/newrelic-browser-agent/commit/c2e22ab49b00e9cfbeb54664a820e1aa4ed28a53))
|
|
24
|
+
|
|
6
25
|
## [1.279.1](https://github.com/newrelic/newrelic-browser-agent/compare/v1.279.0...v1.279.1) (2025-01-28)
|
|
7
26
|
|
|
8
27
|
|
|
@@ -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.281.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.281.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;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.VITAL_NAMES =
|
|
6
|
+
exports.VITAL_NAMES = void 0;
|
|
7
7
|
/**
|
|
8
8
|
* Copyright 2020-2025 New Relic, Inc. All rights reserved.
|
|
9
9
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -11,12 +11,9 @@ exports.VITAL_NAMES = exports.PERFORMANCE_ENTRY_TYPE = void 0;
|
|
|
11
11
|
const VITAL_NAMES = exports.VITAL_NAMES = {
|
|
12
12
|
FIRST_PAINT: 'fp',
|
|
13
13
|
FIRST_CONTENTFUL_PAINT: 'fcp',
|
|
14
|
-
|
|
14
|
+
FIRST_INPUT_DELAY: 'fi',
|
|
15
15
|
LARGEST_CONTENTFUL_PAINT: 'lcp',
|
|
16
16
|
CUMULATIVE_LAYOUT_SHIFT: 'cls',
|
|
17
17
|
INTERACTION_TO_NEXT_PAINT: 'inp',
|
|
18
18
|
TIME_TO_FIRST_BYTE: 'ttfb'
|
|
19
|
-
};
|
|
20
|
-
const PERFORMANCE_ENTRY_TYPE = exports.PERFORMANCE_ENTRY_TYPE = {
|
|
21
|
-
FIRST_INPUT: 'first-input'
|
|
22
19
|
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.firstInputDelay = void 0;
|
|
7
|
+
var _attribution = require("web-vitals/attribution");
|
|
8
|
+
var _vitalMetric = require("./vital-metric");
|
|
9
|
+
var _constants = require("./constants");
|
|
10
|
+
var _runtime = require("../constants/runtime");
|
|
11
|
+
/**
|
|
12
|
+
* Copyright 2020-2025 New Relic, Inc. All rights reserved.
|
|
13
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const firstInputDelay = exports.firstInputDelay = new _vitalMetric.VitalMetric(_constants.VITAL_NAMES.FIRST_INPUT_DELAY);
|
|
17
|
+
if (_runtime.isBrowserScope) {
|
|
18
|
+
(0, _attribution.onFID)(({
|
|
19
|
+
value,
|
|
20
|
+
attribution
|
|
21
|
+
}) => {
|
|
22
|
+
if (_runtime.initiallyHidden || firstInputDelay.isValid) return;
|
|
23
|
+
const attrs = {
|
|
24
|
+
type: attribution.eventType,
|
|
25
|
+
fid: Math.round(value),
|
|
26
|
+
eventTarget: attribution.eventTarget,
|
|
27
|
+
loadState: attribution.loadState
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// CWV will only report one (THE) first-input entry to us; fid isn't reported if there are no user interactions occurs before the *first* page hiding.
|
|
31
|
+
firstInputDelay.update({
|
|
32
|
+
value: attribution.eventTime,
|
|
33
|
+
attrs
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
}
|
|
@@ -18,6 +18,7 @@ var _userActionsAggregator = require("./user-actions/user-actions-aggregator");
|
|
|
18
18
|
var _iframe = require("../../../common/dom/iframe");
|
|
19
19
|
var _handle = require("../../../common/event-emitter/handle");
|
|
20
20
|
var _typeCheck = require("../../../common/util/type-check");
|
|
21
|
+
var _features = require("../../../loaders/features/features");
|
|
21
22
|
/**
|
|
22
23
|
* Copyright 2020-2025 New Relic, Inc. All rights reserved.
|
|
23
24
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -60,7 +61,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
60
61
|
});
|
|
61
62
|
}, this.featureName, this.ee);
|
|
62
63
|
}
|
|
63
|
-
let addUserAction;
|
|
64
|
+
let addUserAction = () => {/** no-op */};
|
|
64
65
|
if (_runtime.isBrowserScope && agentRef.init.user_actions.enabled) {
|
|
65
66
|
this.userActionAggregator = new _userActionsAggregator.UserActionsAggregator();
|
|
66
67
|
this.harvestOpts.beforeUnload = () => addUserAction?.(this.userActionAggregator.aggregationEvent);
|
|
@@ -86,20 +87,27 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
86
87
|
...((0, _iframe.isIFrameWindow)(window) && {
|
|
87
88
|
iframe: true
|
|
88
89
|
}),
|
|
89
|
-
...(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
...(canTrustTargetAttribute('type') && {
|
|
96
|
-
targetType: target.type
|
|
97
|
-
}),
|
|
98
|
-
...(canTrustTargetAttribute('className') && {
|
|
99
|
-
targetClass: target.className
|
|
100
|
-
})
|
|
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
|
|
101
96
|
});
|
|
102
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
|
+
|
|
103
111
|
/**
|
|
104
112
|
* Only trust attributes that exist on HTML element targets, which excludes the window and the document targets
|
|
105
113
|
* @param {string} attribute The attribute to check for on the target element
|
|
@@ -115,7 +123,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
115
123
|
};
|
|
116
124
|
(0, _registerHandler.registerHandler)('ua', evt => {
|
|
117
125
|
/** the processor will return the previously aggregated event if it has been completed by processing the current event */
|
|
118
|
-
addUserAction(this.userActionAggregator.process(evt));
|
|
126
|
+
addUserAction(this.userActionAggregator.process(evt, this.agentRef.init.user_actions.elementAttributes));
|
|
119
127
|
}, this.featureName, this.ee);
|
|
120
128
|
}
|
|
121
129
|
|
|
@@ -134,7 +142,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
134
142
|
const observer = new PerformanceObserver(list => {
|
|
135
143
|
list.getEntries().forEach(entry => {
|
|
136
144
|
try {
|
|
137
|
-
(0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/' + type + '/Seen']);
|
|
145
|
+
(0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/' + type + '/Seen'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
138
146
|
const detailObj = agentRef.init.performance.capture_detail ? createDetailAttrs(entry.detail) : {};
|
|
139
147
|
this.addEvent({
|
|
140
148
|
...detailObj,
|
|
@@ -196,12 +204,12 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
196
204
|
if (this.agentRef.init.performance.resources.asset_types.length && !this.agentRef.init.performance.resources.asset_types.includes(entryObject.initiatorType)) return;
|
|
197
205
|
/** decide if the entryDomain is a first party domain */
|
|
198
206
|
firstParty = entryDomain === _runtime.globalScope?.location.hostname || agentRef.init.performance.resources.first_party_domains.includes(entryDomain);
|
|
199
|
-
if (firstParty) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/FirstPartyResource/Seen']);
|
|
200
|
-
if (isNr) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/NrResource/Seen']);
|
|
207
|
+
if (firstParty) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/FirstPartyResource/Seen'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
208
|
+
if (isNr) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/NrResource/Seen'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
201
209
|
} catch (err) {
|
|
202
210
|
// couldnt parse the URL, so firstParty will just default to false
|
|
203
211
|
}
|
|
204
|
-
(0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/Resource/Seen']);
|
|
212
|
+
(0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/Resource/Seen'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
205
213
|
const event = {
|
|
206
214
|
...entryObject,
|
|
207
215
|
eventType: 'BrowserPerformance',
|
|
@@ -287,12 +295,12 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
287
295
|
trackSupportabilityMetrics() {
|
|
288
296
|
/** track usage SMs to improve these experimental features */
|
|
289
297
|
const configPerfTag = 'Config/Performance/';
|
|
290
|
-
if (this.agentRef.init.performance.capture_marks) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'CaptureMarks/Enabled']);
|
|
291
|
-
if (this.agentRef.init.performance.capture_measures) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'CaptureMeasures/Enabled']);
|
|
292
|
-
if (this.agentRef.init.performance.resources.enabled) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/Enabled']);
|
|
293
|
-
if (this.agentRef.init.performance.resources.asset_types?.length !== 0) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/AssetTypes/Changed']);
|
|
294
|
-
if (this.agentRef.init.performance.resources.first_party_domains?.length !== 0) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/FirstPartyDomains/Changed']);
|
|
295
|
-
if (this.agentRef.init.performance.resources.ignore_newrelic === false) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/IgnoreNewrelic/Changed']);
|
|
298
|
+
if (this.agentRef.init.performance.capture_marks) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'CaptureMarks/Enabled'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
299
|
+
if (this.agentRef.init.performance.capture_measures) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'CaptureMeasures/Enabled'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
300
|
+
if (this.agentRef.init.performance.resources.enabled) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/Enabled'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
301
|
+
if (this.agentRef.init.performance.resources.asset_types?.length !== 0) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/AssetTypes/Changed'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
302
|
+
if (this.agentRef.init.performance.resources.first_party_domains?.length !== 0) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/FirstPartyDomains/Changed'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
303
|
+
if (this.agentRef.init.performance.resources.ignore_newrelic === false) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/IgnoreNewrelic/Changed'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
296
304
|
}
|
|
297
305
|
}
|
|
298
306
|
exports.Aggregate = Aggregate;
|
|
@@ -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
|
/**
|
|
@@ -15,6 +15,7 @@ var _log = require("../shared/log");
|
|
|
15
15
|
var _utils = require("../shared/utils");
|
|
16
16
|
var _traverse = require("../../../common/util/traverse");
|
|
17
17
|
var _agentConstants = require("../../../common/constants/agent-constants");
|
|
18
|
+
var _features = require("../../../loaders/features/features");
|
|
18
19
|
/**
|
|
19
20
|
* Copyright 2020-2025 New Relic, Inc. All rights reserved.
|
|
20
21
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -59,17 +60,17 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
59
60
|
const failToHarvestMessage = 'Logging/Harvest/Failed/Seen';
|
|
60
61
|
if (logBytes > _agentConstants.MAX_PAYLOAD_SIZE) {
|
|
61
62
|
// cannot possibly send this, even with an empty buffer
|
|
62
|
-
(0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, [failToHarvestMessage, logBytes]);
|
|
63
|
+
(0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, [failToHarvestMessage, logBytes], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
63
64
|
(0, _console.warn)(31, log.message.slice(0, 25) + '...');
|
|
64
65
|
return;
|
|
65
66
|
}
|
|
66
67
|
if (this.events.wouldExceedMaxSize(logBytes)) {
|
|
67
|
-
(0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, ['Logging/Harvest/Early/Seen', this.events.byteSize() + logBytes]);
|
|
68
|
+
(0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, ['Logging/Harvest/Early/Seen', this.events.byteSize() + logBytes], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
68
69
|
this.agentRef.runtime.harvester.triggerHarvestFor(this); // force a harvest synchronously to try adding again
|
|
69
70
|
}
|
|
70
71
|
if (!this.events.add(log)) {
|
|
71
72
|
// still failed after a harvest attempt despite not being too large would mean harvest failed with options.retry
|
|
72
|
-
(0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, [failToHarvestMessage, logBytes]);
|
|
73
|
+
(0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, [failToHarvestMessage, logBytes], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
73
74
|
(0, _console.warn)(31, log.message.slice(0, 25) + '...');
|
|
74
75
|
}
|
|
75
76
|
}
|
|
@@ -12,8 +12,8 @@ var _features = require("../../../loaders/features/features");
|
|
|
12
12
|
var _aggregateBase = require("../../utils/aggregate-base");
|
|
13
13
|
var _cumulativeLayoutShift = require("../../../common/vitals/cumulative-layout-shift");
|
|
14
14
|
var _firstContentfulPaint = require("../../../common/vitals/first-contentful-paint");
|
|
15
|
+
var _firstInputDelay = require("../../../common/vitals/first-input-delay");
|
|
15
16
|
var _firstPaint = require("../../../common/vitals/first-paint");
|
|
16
|
-
var _firstInteraction = require("../../../common/vitals/first-interaction");
|
|
17
17
|
var _interactionToNextPaint = require("../../../common/vitals/interaction-to-next-paint");
|
|
18
18
|
var _largestContentfulPaint = require("../../../common/vitals/largest-contentful-paint");
|
|
19
19
|
var _timeToFirstByte = require("../../../common/vitals/time-to-first-byte");
|
|
@@ -42,8 +42,8 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
42
42
|
this.waitForFlags([]).then(() => {
|
|
43
43
|
_firstPaint.firstPaint.subscribe(this.#handleVitalMetric);
|
|
44
44
|
_firstContentfulPaint.firstContentfulPaint.subscribe(this.#handleVitalMetric);
|
|
45
|
+
_firstInputDelay.firstInputDelay.subscribe(this.#handleVitalMetric);
|
|
45
46
|
_largestContentfulPaint.largestContentfulPaint.subscribe(this.#handleVitalMetric);
|
|
46
|
-
_firstInteraction.firstInteraction.subscribe(this.#handleVitalMetric);
|
|
47
47
|
_interactionToNextPaint.interactionToNextPaint.subscribe(this.#handleVitalMetric);
|
|
48
48
|
_timeToFirstByte.timeToFirstByte.subscribe(({
|
|
49
49
|
attrs
|
|
@@ -144,6 +144,13 @@ class TraceStorage {
|
|
|
144
144
|
this.storeTiming({
|
|
145
145
|
[name]: value
|
|
146
146
|
});
|
|
147
|
+
if (hasFID(name, attrs)) this.storeEvent({
|
|
148
|
+
type: 'fid',
|
|
149
|
+
target: 'document'
|
|
150
|
+
}, 'document', value, value + attrs.fid);
|
|
151
|
+
function hasFID(name, attrs) {
|
|
152
|
+
return name === 'fi' && !!attrs && typeof attrs.fid === 'number';
|
|
153
|
+
}
|
|
147
154
|
}
|
|
148
155
|
storeTiming(timingEntry, isAbsoluteTimestamp = false) {
|
|
149
156
|
if (!timingEntry) return;
|
|
@@ -35,10 +35,9 @@ var _runtime = require("../common/constants/runtime");
|
|
|
35
35
|
class Agent extends _agentBase.AgentBase {
|
|
36
36
|
/**
|
|
37
37
|
* @param {Object} options Options to initialize agent with
|
|
38
|
-
* @param {string} [agentIdentifier] Optional identifier of agent
|
|
39
38
|
*/
|
|
40
|
-
constructor(options
|
|
41
|
-
super(
|
|
39
|
+
constructor(options) {
|
|
40
|
+
super();
|
|
42
41
|
if (!_runtime.globalScope) {
|
|
43
42
|
// We could not determine the runtime environment. Short-circuite the agent here
|
|
44
43
|
// to avoid possible exceptions later that may cause issues with customer's application.
|
|
@@ -13,8 +13,8 @@ var _uniqueId = require("../common/ids/unique-id");
|
|
|
13
13
|
|
|
14
14
|
class MicroAgentBase {
|
|
15
15
|
agentIdentifier;
|
|
16
|
-
constructor(
|
|
17
|
-
this.agentIdentifier =
|
|
16
|
+
constructor() {
|
|
17
|
+
this.agentIdentifier = (0, _uniqueId.generateRandomHexString)(16);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
/**
|
|
@@ -27,10 +27,9 @@ const nonAutoFeatures = [_features.FEATURE_NAMES.jserrors, _features.FEATURE_NAM
|
|
|
27
27
|
class MicroAgent extends _microAgentBase.MicroAgentBase {
|
|
28
28
|
/**
|
|
29
29
|
* @param {Object} options - Specifies features and runtime configuration,
|
|
30
|
-
* @param {string=} agentIdentifier - The optional unique ID of the agent.
|
|
31
30
|
*/
|
|
32
|
-
constructor(options
|
|
33
|
-
super(
|
|
31
|
+
constructor(options) {
|
|
32
|
+
super();
|
|
34
33
|
this.features = {};
|
|
35
34
|
(0, _nreum.setNREUMInitializedAgent)(this.agentIdentifier, this);
|
|
36
35
|
(0, _configure.configure)(this, {
|
|
@@ -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
|
};
|
|
@@ -5,12 +5,9 @@
|
|
|
5
5
|
export const VITAL_NAMES = {
|
|
6
6
|
FIRST_PAINT: 'fp',
|
|
7
7
|
FIRST_CONTENTFUL_PAINT: 'fcp',
|
|
8
|
-
|
|
8
|
+
FIRST_INPUT_DELAY: 'fi',
|
|
9
9
|
LARGEST_CONTENTFUL_PAINT: 'lcp',
|
|
10
10
|
CUMULATIVE_LAYOUT_SHIFT: 'cls',
|
|
11
11
|
INTERACTION_TO_NEXT_PAINT: 'inp',
|
|
12
12
|
TIME_TO_FIRST_BYTE: 'ttfb'
|
|
13
|
-
};
|
|
14
|
-
export const PERFORMANCE_ENTRY_TYPE = {
|
|
15
|
-
FIRST_INPUT: 'first-input'
|
|
16
13
|
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2020-2025 New Relic, Inc. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import { onFID } from 'web-vitals/attribution';
|
|
6
|
+
import { VitalMetric } from './vital-metric';
|
|
7
|
+
import { VITAL_NAMES } from './constants';
|
|
8
|
+
import { initiallyHidden, isBrowserScope } from '../constants/runtime';
|
|
9
|
+
export const firstInputDelay = new VitalMetric(VITAL_NAMES.FIRST_INPUT_DELAY);
|
|
10
|
+
if (isBrowserScope) {
|
|
11
|
+
onFID(({
|
|
12
|
+
value,
|
|
13
|
+
attribution
|
|
14
|
+
}) => {
|
|
15
|
+
if (initiallyHidden || firstInputDelay.isValid) return;
|
|
16
|
+
const attrs = {
|
|
17
|
+
type: attribution.eventType,
|
|
18
|
+
fid: Math.round(value),
|
|
19
|
+
eventTarget: attribution.eventTarget,
|
|
20
|
+
loadState: attribution.loadState
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// CWV will only report one (THE) first-input entry to us; fid isn't reported if there are no user interactions occurs before the *first* page hiding.
|
|
24
|
+
firstInputDelay.update({
|
|
25
|
+
value: attribution.eventTime,
|
|
26
|
+
attrs
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
}
|