@newrelic/browser-agent 1.275.0 → 1.277.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 +26 -0
- package/dist/cjs/common/config/init.js +44 -8
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/features/ajax/instrument/index.js +1 -1
- package/dist/cjs/features/generic_events/aggregate/index.js +82 -8
- package/dist/cjs/features/generic_events/constants.js +8 -2
- package/dist/cjs/features/generic_events/instrument/index.js +20 -8
- package/dist/cjs/features/jserrors/aggregate/index.js +3 -2
- package/dist/cjs/features/jserrors/aggregate/internal-errors.js +2 -2
- package/dist/cjs/features/jserrors/instrument/index.js +2 -2
- package/dist/cjs/features/metrics/aggregate/index.js +1 -35
- package/dist/cjs/features/soft_navigations/aggregate/index.js +28 -11
- package/dist/cjs/features/soft_navigations/aggregate/initial-page-load-interaction.js +2 -1
- package/dist/cjs/features/soft_navigations/aggregate/interaction.js +12 -12
- package/dist/cjs/features/soft_navigations/constants.js +5 -2
- package/dist/cjs/loaders/api/api-methods.js +1 -1
- package/dist/cjs/loaders/api/api.js +2 -1
- package/dist/cjs/loaders/micro-agent-base.js +10 -0
- package/dist/esm/common/config/init.js +39 -3
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/features/ajax/instrument/index.js +1 -1
- package/dist/esm/features/generic_events/aggregate/index.js +84 -10
- package/dist/esm/features/generic_events/constants.js +7 -1
- package/dist/esm/features/generic_events/instrument/index.js +21 -9
- package/dist/esm/features/jserrors/aggregate/index.js +3 -2
- package/dist/esm/features/jserrors/aggregate/internal-errors.js +2 -2
- package/dist/esm/features/jserrors/instrument/index.js +2 -2
- package/dist/esm/features/metrics/aggregate/index.js +1 -35
- package/dist/esm/features/soft_navigations/aggregate/index.js +29 -12
- package/dist/esm/features/soft_navigations/aggregate/initial-page-load-interaction.js +2 -1
- package/dist/esm/features/soft_navigations/aggregate/interaction.js +13 -13
- package/dist/esm/features/soft_navigations/constants.js +4 -1
- package/dist/esm/loaders/api/api-methods.js +1 -1
- package/dist/esm/loaders/api/api.js +2 -1
- package/dist/esm/loaders/micro-agent-base.js +10 -0
- package/dist/types/common/config/init.d.ts.map +1 -1
- package/dist/types/features/generic_events/aggregate/index.d.ts +2 -0
- package/dist/types/features/generic_events/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/generic_events/constants.d.ts +6 -0
- package/dist/types/features/generic_events/constants.d.ts.map +1 -1
- package/dist/types/features/generic_events/instrument/index.d.ts.map +1 -1
- package/dist/types/features/jserrors/aggregate/index.d.ts +2 -1
- package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/jserrors/aggregate/internal-errors.d.ts +1 -1
- package/dist/types/features/jserrors/aggregate/internal-errors.d.ts.map +1 -1
- package/dist/types/features/metrics/aggregate/index.d.ts +0 -1
- package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/aggregate/index.d.ts +1 -0
- package/dist/types/features/soft_navigations/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/aggregate/initial-page-load-interaction.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/aggregate/interaction.d.ts +3 -3
- package/dist/types/features/soft_navigations/aggregate/interaction.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/constants.d.ts +1 -0
- package/dist/types/features/soft_navigations/constants.d.ts.map +1 -1
- package/dist/types/loaders/api/api.d.ts +1 -0
- package/dist/types/loaders/api/api.d.ts.map +1 -1
- package/dist/types/loaders/micro-agent-base.d.ts +7 -0
- package/dist/types/loaders/micro-agent-base.d.ts.map +1 -1
- package/package.json +13 -1
- package/src/common/config/init.js +21 -3
- package/src/features/ajax/instrument/index.js +1 -1
- package/src/features/generic_events/aggregate/index.js +88 -10
- package/src/features/generic_events/constants.js +8 -0
- package/src/features/generic_events/instrument/index.js +21 -10
- package/src/features/jserrors/aggregate/index.js +3 -2
- package/src/features/jserrors/aggregate/internal-errors.js +2 -2
- package/src/features/jserrors/instrument/index.js +2 -2
- package/src/features/metrics/aggregate/index.js +1 -33
- package/src/features/soft_navigations/aggregate/index.js +22 -11
- package/src/features/soft_navigations/aggregate/initial-page-load-interaction.js +2 -1
- package/src/features/soft_navigations/aggregate/interaction.js +13 -12
- package/src/features/soft_navigations/constants.js +3 -1
- package/src/loaders/api/api-methods.js +1 -1
- package/src/loaders/api/api.js +3 -1
- package/src/loaders/micro-agent-base.js +10 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,32 @@
|
|
|
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.277.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.276.0...v1.277.0) (2024-12-18)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* Add custom events API ([#1263](https://github.com/newrelic/newrelic-browser-agent/issues/1263)) ([9395415](https://github.com/newrelic/newrelic-browser-agent/commit/9395415d942b55e88e89438aa203c6a1642d9e6b))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* Soft navigation bug fixes and new soft navigation tests ([#1268](https://github.com/newrelic/newrelic-browser-agent/issues/1268)) ([7624928](https://github.com/newrelic/newrelic-browser-agent/commit/762492896a7b96564269aab1aadeb6e44a4da242))
|
|
17
|
+
|
|
18
|
+
## [1.276.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.275.0...v1.276.0) (2024-12-16)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### Features
|
|
22
|
+
|
|
23
|
+
* allow feature flags to control experimental features ([#1282](https://github.com/newrelic/newrelic-browser-agent/issues/1282)) ([537e72d](https://github.com/newrelic/newrelic-browser-agent/commit/537e72da0821792006abd16c41ffa025fd73b474))
|
|
24
|
+
* Capture Page Resource Assets ([#1257](https://github.com/newrelic/newrelic-browser-agent/issues/1257)) ([e4c7deb](https://github.com/newrelic/newrelic-browser-agent/commit/e4c7debe2a1653efdad8940c55a71dd0140ce900))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Bug Fixes
|
|
28
|
+
|
|
29
|
+
* Fix syntax error in fallback when tracking xhr readyState ([#1272](https://github.com/newrelic/newrelic-browser-agent/issues/1272)) ([2bb6b5b](https://github.com/newrelic/newrelic-browser-agent/commit/2bb6b5b2e104a6b3ac9ceb70d396f65cfb61c1bd))
|
|
30
|
+
* Ignore reserved attribute names on UserActions tied to the window ([#1261](https://github.com/newrelic/newrelic-browser-agent/issues/1261)) ([8410dbe](https://github.com/newrelic/newrelic-browser-agent/commit/8410dbeef30aeb0e578c63a293ec311c3d42f8da))
|
|
31
|
+
|
|
6
32
|
## [1.275.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.274.0...v1.275.0) (2024-12-03)
|
|
7
33
|
|
|
8
34
|
|
|
@@ -6,15 +6,22 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.getConfiguration = getConfiguration;
|
|
7
7
|
exports.getConfigurationValue = getConfigurationValue;
|
|
8
8
|
exports.setConfiguration = setConfiguration;
|
|
9
|
-
var _constants = require("../../features/
|
|
9
|
+
var _constants = require("../../features/generic_events/constants");
|
|
10
|
+
var _constants2 = require("../../features/logging/constants");
|
|
10
11
|
var _querySelector = require("../dom/query-selector");
|
|
11
|
-
var
|
|
12
|
+
var _constants3 = require("../session/constants");
|
|
12
13
|
var _console = require("../util/console");
|
|
13
14
|
var _nreum = require("../window/nreum");
|
|
14
15
|
var _configurable = require("./configurable");
|
|
15
16
|
const nrMask = '[data-nr-mask]';
|
|
16
17
|
const model = () => {
|
|
17
18
|
const hiddenState = {
|
|
19
|
+
feature_flags: [],
|
|
20
|
+
experimental: {
|
|
21
|
+
marks: false,
|
|
22
|
+
measures: false,
|
|
23
|
+
resources: false
|
|
24
|
+
},
|
|
18
25
|
mask_selector: '*',
|
|
19
26
|
block_selector: '[data-nr-block]',
|
|
20
27
|
mask_input_options: {
|
|
@@ -52,7 +59,12 @@ const model = () => {
|
|
|
52
59
|
cors_use_tracecontext_headers: undefined,
|
|
53
60
|
allowed_origins: undefined
|
|
54
61
|
},
|
|
55
|
-
feature_flags
|
|
62
|
+
get feature_flags() {
|
|
63
|
+
return hiddenState.feature_flags;
|
|
64
|
+
},
|
|
65
|
+
set feature_flags(val) {
|
|
66
|
+
hiddenState.feature_flags = val;
|
|
67
|
+
},
|
|
56
68
|
generic_events: {
|
|
57
69
|
enabled: true,
|
|
58
70
|
harvestTimeSeconds: 30,
|
|
@@ -70,7 +82,7 @@ const model = () => {
|
|
|
70
82
|
enabled: true,
|
|
71
83
|
harvestTimeSeconds: 10,
|
|
72
84
|
autoStart: true,
|
|
73
|
-
level:
|
|
85
|
+
level: _constants2.LOG_LEVELS.INFO
|
|
74
86
|
},
|
|
75
87
|
metrics: {
|
|
76
88
|
enabled: true,
|
|
@@ -90,8 +102,32 @@ const model = () => {
|
|
|
90
102
|
autoStart: true
|
|
91
103
|
},
|
|
92
104
|
performance: {
|
|
93
|
-
capture_marks
|
|
94
|
-
|
|
105
|
+
get capture_marks() {
|
|
106
|
+
return hiddenState.feature_flags.includes(_constants.FEATURE_FLAGS.MARKS) || hiddenState.experimental.marks;
|
|
107
|
+
},
|
|
108
|
+
set capture_marks(val) {
|
|
109
|
+
hiddenState.experimental.marks = val;
|
|
110
|
+
},
|
|
111
|
+
get capture_measures() {
|
|
112
|
+
return hiddenState.feature_flags.includes(_constants.FEATURE_FLAGS.MEASURES) || hiddenState.experimental.measures;
|
|
113
|
+
},
|
|
114
|
+
set capture_measures(val) {
|
|
115
|
+
hiddenState.experimental.measures = val;
|
|
116
|
+
},
|
|
117
|
+
resources: {
|
|
118
|
+
// whether to run this subfeature or not in the generic_events feature. false by default through experimental phase, but flipped to true once GA'd
|
|
119
|
+
get enabled() {
|
|
120
|
+
return hiddenState.feature_flags.includes(_constants.FEATURE_FLAGS.RESOURCES) || hiddenState.experimental.resources;
|
|
121
|
+
},
|
|
122
|
+
set enabled(val) {
|
|
123
|
+
hiddenState.experimental.resources = val;
|
|
124
|
+
},
|
|
125
|
+
asset_types: [],
|
|
126
|
+
// MDN types to collect, empty array will collect all types
|
|
127
|
+
first_party_domains: [],
|
|
128
|
+
// when included, will decorate the resource as first party if matching
|
|
129
|
+
ignore_newrelic: true // ignore capturing internal agent scripts and harvest calls
|
|
130
|
+
}
|
|
95
131
|
},
|
|
96
132
|
privacy: {
|
|
97
133
|
cookies_enabled: true
|
|
@@ -103,8 +139,8 @@ const model = () => {
|
|
|
103
139
|
beacon: undefined // likewise for the url to which we send analytics
|
|
104
140
|
},
|
|
105
141
|
session: {
|
|
106
|
-
expiresMs:
|
|
107
|
-
inactiveMs:
|
|
142
|
+
expiresMs: _constants3.DEFAULT_EXPIRES_MS,
|
|
143
|
+
inactiveMs: _constants3.DEFAULT_INACTIVE_MS
|
|
108
144
|
},
|
|
109
145
|
session_replay: {
|
|
110
146
|
// feature settings
|
|
@@ -12,7 +12,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_EN
|
|
|
12
12
|
/**
|
|
13
13
|
* Exposes the version of the agent
|
|
14
14
|
*/
|
|
15
|
-
const VERSION = exports.VERSION = "1.
|
|
15
|
+
const VERSION = exports.VERSION = "1.277.0";
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Exposes the build type of the agent
|
|
@@ -12,7 +12,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_EN
|
|
|
12
12
|
/**
|
|
13
13
|
* Exposes the version of the agent
|
|
14
14
|
*/
|
|
15
|
-
const VERSION = exports.VERSION = "1.
|
|
15
|
+
const VERSION = exports.VERSION = "1.277.0";
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Exposes the build type of the agent
|
|
@@ -346,7 +346,7 @@ function subscribeToEvents(agentRef, ee, handler, dt) {
|
|
|
346
346
|
if ((0, _denyList.hasUndefinedHostname)(params)) return; // don't bother with XHR of url with no hostname
|
|
347
347
|
|
|
348
348
|
metrics.duration = (0, _now.now)() - this.startTime;
|
|
349
|
-
if (!this.
|
|
349
|
+
if (!this.loadCaptureCalled && xhr.readyState === 4) {
|
|
350
350
|
captureXhrData(this, xhr);
|
|
351
351
|
} else if (params.status == null) {
|
|
352
352
|
params.status = 0;
|
|
@@ -18,6 +18,7 @@ var _traverse = require("../../../common/util/traverse");
|
|
|
18
18
|
var _features = require("../../../loaders/features/features");
|
|
19
19
|
var _userActionsAggregator = require("./user-actions/user-actions-aggregator");
|
|
20
20
|
var _iframe = require("../../../common/dom/iframe");
|
|
21
|
+
var _handle = require("../../../common/event-emitter/handle");
|
|
21
22
|
/*
|
|
22
23
|
* Copyright 2020 New Relic Corporation. All rights reserved.
|
|
23
24
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -36,12 +37,21 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
36
37
|
this.deregisterDrain();
|
|
37
38
|
return;
|
|
38
39
|
}
|
|
40
|
+
this.trackSupportabilityMetrics();
|
|
41
|
+
(0, _registerHandler.registerHandler)('api-recordCustomEvent', (timestamp, eventType, attributes) => {
|
|
42
|
+
if (_constants.RESERVED_EVENT_TYPES.includes(eventType)) return (0, _console.warn)(46);
|
|
43
|
+
this.addEvent({
|
|
44
|
+
eventType,
|
|
45
|
+
timestamp: this.toEpoch(timestamp),
|
|
46
|
+
...attributes
|
|
47
|
+
});
|
|
48
|
+
}, this.featureName, this.ee);
|
|
39
49
|
if (agentRef.init.page_action.enabled) {
|
|
40
50
|
(0, _registerHandler.registerHandler)('api-addPageAction', (timestamp, name, attributes) => {
|
|
41
51
|
this.addEvent({
|
|
42
52
|
...attributes,
|
|
43
53
|
eventType: 'PageAction',
|
|
44
|
-
timestamp:
|
|
54
|
+
timestamp: this.toEpoch(timestamp),
|
|
45
55
|
timeSinceLoad: timestamp / 1000,
|
|
46
56
|
actionName: name,
|
|
47
57
|
referrerUrl: this.referrerUrl,
|
|
@@ -67,7 +77,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
67
77
|
} = aggregatedUserAction.event;
|
|
68
78
|
this.addEvent({
|
|
69
79
|
eventType: 'UserAction',
|
|
70
|
-
timestamp:
|
|
80
|
+
timestamp: this.toEpoch(timeStamp),
|
|
71
81
|
action: type,
|
|
72
82
|
actionCount: aggregatedUserAction.count,
|
|
73
83
|
actionDuration: aggregatedUserAction.relativeMs[aggregatedUserAction.relativeMs.length - 1],
|
|
@@ -77,19 +87,28 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
77
87
|
...((0, _iframe.isIFrameWindow)(window) && {
|
|
78
88
|
iframe: true
|
|
79
89
|
}),
|
|
80
|
-
...(
|
|
90
|
+
...(canTrustTargetAttribute('id') && {
|
|
81
91
|
targetId: target.id
|
|
82
92
|
}),
|
|
83
|
-
...(
|
|
93
|
+
...(canTrustTargetAttribute('tagName') && {
|
|
84
94
|
targetTag: target.tagName
|
|
85
95
|
}),
|
|
86
|
-
...(
|
|
96
|
+
...(canTrustTargetAttribute('type') && {
|
|
87
97
|
targetType: target.type
|
|
88
98
|
}),
|
|
89
|
-
...(
|
|
99
|
+
...(canTrustTargetAttribute('className') && {
|
|
90
100
|
targetClass: target.className
|
|
91
101
|
})
|
|
92
102
|
});
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Only trust attributes that exist on HTML element targets, which excludes the window and the document targets
|
|
106
|
+
* @param {string} attribute The attribute to check for on the target element
|
|
107
|
+
* @returns {boolean} Whether the target element has the attribute and can be trusted
|
|
108
|
+
*/
|
|
109
|
+
function canTrustTargetAttribute(attribute) {
|
|
110
|
+
return !!(aggregatedUserAction.selectorPath !== 'window' && aggregatedUserAction.selectorPath !== 'document' && target instanceof HTMLElement && target?.[attribute]);
|
|
111
|
+
}
|
|
93
112
|
}
|
|
94
113
|
} catch (e) {
|
|
95
114
|
// do nothing for now
|
|
@@ -116,10 +135,11 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
116
135
|
const observer = new PerformanceObserver(list => {
|
|
117
136
|
list.getEntries().forEach(entry => {
|
|
118
137
|
try {
|
|
138
|
+
(0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/' + type + '/Seen']);
|
|
119
139
|
this.addEvent({
|
|
120
140
|
eventType: 'BrowserPerformance',
|
|
121
|
-
timestamp:
|
|
122
|
-
entryName: entry.name,
|
|
141
|
+
timestamp: this.toEpoch(entry.startTime),
|
|
142
|
+
entryName: (0, _cleanUrl.cleanURL)(entry.name),
|
|
123
143
|
entryDuration: entry.duration,
|
|
124
144
|
entryType: type,
|
|
125
145
|
...(entry.detail && {
|
|
@@ -139,6 +159,47 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
139
159
|
// Something failed in our set up, likely the browser does not support PO's... do nothing
|
|
140
160
|
}
|
|
141
161
|
}
|
|
162
|
+
if (_runtime.isBrowserScope && agentRef.init.performance.resources.enabled) {
|
|
163
|
+
(0, _registerHandler.registerHandler)('browserPerformance.resource', entry => {
|
|
164
|
+
try {
|
|
165
|
+
// convert the entry to a plain object and separate the name and duration from the object
|
|
166
|
+
// you need to do this to be able to spread it into the addEvent call later, and name and duration
|
|
167
|
+
// would be duplicative of entryName and entryDuration and are protected keys in NR1
|
|
168
|
+
const {
|
|
169
|
+
name,
|
|
170
|
+
duration,
|
|
171
|
+
...entryObject
|
|
172
|
+
} = entry.toJSON();
|
|
173
|
+
let firstParty = false;
|
|
174
|
+
try {
|
|
175
|
+
const entryDomain = new URL(name).hostname;
|
|
176
|
+
const isNr = entryDomain.includes('newrelic.com') || entryDomain.includes('nr-data.net') || entryDomain.includes('nr-local.net');
|
|
177
|
+
/** decide if we should ignore nr-specific assets */
|
|
178
|
+
if (this.agentRef.init.performance.resources.ignore_newrelic && isNr) return;
|
|
179
|
+
/** decide if we should ignore the asset type (empty means allow everything, which is the default) */
|
|
180
|
+
if (this.agentRef.init.performance.resources.asset_types.length && !this.agentRef.init.performance.resources.asset_types.includes(entryObject.initiatorType)) return;
|
|
181
|
+
/** decide if the entryDomain is a first party domain */
|
|
182
|
+
firstParty = entryDomain === _runtime.globalScope?.location.hostname || agentRef.init.performance.resources.first_party_domains.includes(entryDomain);
|
|
183
|
+
if (firstParty) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/FirstPartyResource/Seen']);
|
|
184
|
+
if (isNr) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/NrResource/Seen']);
|
|
185
|
+
} catch (err) {
|
|
186
|
+
// couldnt parse the URL, so firstParty will just default to false
|
|
187
|
+
}
|
|
188
|
+
(0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/Resource/Seen']);
|
|
189
|
+
const event = {
|
|
190
|
+
...entryObject,
|
|
191
|
+
eventType: 'BrowserPerformance',
|
|
192
|
+
timestamp: Math.floor(agentRef.runtime.timeKeeper.correctRelativeTimestamp(entryObject.startTime)),
|
|
193
|
+
entryName: name,
|
|
194
|
+
entryDuration: duration,
|
|
195
|
+
firstParty
|
|
196
|
+
};
|
|
197
|
+
this.addEvent(event);
|
|
198
|
+
} catch (err) {
|
|
199
|
+
this.ee.emit('internal-error', [err, 'GenericEvents-Resource']);
|
|
200
|
+
}
|
|
201
|
+
}, this.featureName, this.ee);
|
|
202
|
+
}
|
|
142
203
|
this.harvestScheduler = new _harvestScheduler.HarvestScheduler(_features.FEATURE_TO_ENDPOINT[this.featureName], {
|
|
143
204
|
onFinished: result => this.postHarvestCleanup(result.sent && result.retry),
|
|
144
205
|
onUnload: () => addUserAction?.(this.userActionAggregator.aggregationEvent)
|
|
@@ -209,5 +270,18 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
209
270
|
at: this.agentRef.info.atts
|
|
210
271
|
};
|
|
211
272
|
}
|
|
273
|
+
toEpoch(timestamp) {
|
|
274
|
+
return Math.floor(this.agentRef.runtime.timeKeeper.correctRelativeTimestamp(timestamp));
|
|
275
|
+
}
|
|
276
|
+
trackSupportabilityMetrics() {
|
|
277
|
+
/** track usage SMs to improve these experimental features */
|
|
278
|
+
const configPerfTag = 'Config/Performance/';
|
|
279
|
+
if (this.agentRef.init.performance.capture_marks) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'CaptureMarks/Enabled']);
|
|
280
|
+
if (this.agentRef.init.performance.capture_measures) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'CaptureMeasures/Enabled']);
|
|
281
|
+
if (this.agentRef.init.performance.resources.enabled) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/Enabled']);
|
|
282
|
+
if (this.agentRef.init.performance.resources.asset_types?.length !== 0) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/AssetTypes/Changed']);
|
|
283
|
+
if (this.agentRef.init.performance.resources.first_party_domains?.length !== 0) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/FirstPartyDomains/Changed']);
|
|
284
|
+
if (this.agentRef.init.performance.resources.ignore_newrelic === false) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/IgnoreNewrelic/Changed']);
|
|
285
|
+
}
|
|
212
286
|
}
|
|
213
287
|
exports.Aggregate = Aggregate;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.RAGE_CLICK_THRESHOLD_MS = exports.RAGE_CLICK_THRESHOLD_EVENTS = exports.OBSERVED_WINDOW_EVENTS = exports.OBSERVED_EVENTS = exports.MAX_PAYLOAD_SIZE = exports.IDEAL_PAYLOAD_SIZE = exports.FEATURE_NAME = void 0;
|
|
6
|
+
exports.RESERVED_EVENT_TYPES = exports.RAGE_CLICK_THRESHOLD_MS = exports.RAGE_CLICK_THRESHOLD_EVENTS = exports.OBSERVED_WINDOW_EVENTS = exports.OBSERVED_EVENTS = exports.MAX_PAYLOAD_SIZE = exports.IDEAL_PAYLOAD_SIZE = exports.FEATURE_NAME = exports.FEATURE_FLAGS = void 0;
|
|
7
7
|
var _features = require("../../loaders/features/features");
|
|
8
8
|
const FEATURE_NAME = exports.FEATURE_NAME = _features.FEATURE_NAMES.genericEvents;
|
|
9
9
|
const IDEAL_PAYLOAD_SIZE = exports.IDEAL_PAYLOAD_SIZE = 64000;
|
|
@@ -11,4 +11,10 @@ const MAX_PAYLOAD_SIZE = exports.MAX_PAYLOAD_SIZE = 1000000;
|
|
|
11
11
|
const OBSERVED_EVENTS = exports.OBSERVED_EVENTS = ['auxclick', 'click', 'copy', 'keydown', 'paste', 'scrollend'];
|
|
12
12
|
const OBSERVED_WINDOW_EVENTS = exports.OBSERVED_WINDOW_EVENTS = ['focus', 'blur'];
|
|
13
13
|
const RAGE_CLICK_THRESHOLD_EVENTS = exports.RAGE_CLICK_THRESHOLD_EVENTS = 4;
|
|
14
|
-
const RAGE_CLICK_THRESHOLD_MS = exports.RAGE_CLICK_THRESHOLD_MS = 1000;
|
|
14
|
+
const RAGE_CLICK_THRESHOLD_MS = exports.RAGE_CLICK_THRESHOLD_MS = 1000;
|
|
15
|
+
const RESERVED_EVENT_TYPES = exports.RESERVED_EVENT_TYPES = ['PageAction', 'UserAction', 'BrowserPerformance'];
|
|
16
|
+
const FEATURE_FLAGS = exports.FEATURE_FLAGS = {
|
|
17
|
+
MARKS: 'experimental.marks',
|
|
18
|
+
MEASURES: 'experimental.measures',
|
|
19
|
+
RESOURCES: 'experimental.resources'
|
|
20
|
+
};
|
|
@@ -17,14 +17,26 @@ class Instrument extends _instrumentBase.InstrumentBase {
|
|
|
17
17
|
static featureName = _constants.FEATURE_NAME;
|
|
18
18
|
constructor(agentRef, auto = true) {
|
|
19
19
|
super(agentRef, _constants.FEATURE_NAME, auto);
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
20
|
+
/** config values that gate whether the generic events aggregator should be imported at all */
|
|
21
|
+
const genericEventSourceConfigs = [agentRef.init.page_action.enabled, agentRef.init.performance.capture_marks, agentRef.init.performance.capture_measures, agentRef.init.user_actions.enabled, agentRef.init.performance.resources.enabled];
|
|
22
|
+
if (_runtime.isBrowserScope) {
|
|
23
|
+
if (agentRef.init.user_actions.enabled) {
|
|
24
|
+
_constants.OBSERVED_EVENTS.forEach(eventType => (0, _eventListenerOpts.windowAddEventListener)(eventType, evt => (0, _handle.handle)('ua', [evt], undefined, this.featureName, this.ee), true));
|
|
25
|
+
_constants.OBSERVED_WINDOW_EVENTS.forEach(eventType => (0, _eventListenerOpts.windowAddEventListener)(eventType, evt => (0, _handle.handle)('ua', [evt], undefined, this.featureName, this.ee))
|
|
26
|
+
// Capture is not used here so that we don't get element focus/blur events, only the window's as they do not bubble. They are also not cancellable, so no worries about being front of line.
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
if (agentRef.init.performance.resources.enabled && _runtime.globalScope.PerformanceObserver?.supportedEntryTypes.includes('resource')) {
|
|
30
|
+
const observer = new PerformanceObserver(list => {
|
|
31
|
+
list.getEntries().forEach(entry => {
|
|
32
|
+
(0, _handle.handle)('browserPerformance.resource', [entry], undefined, this.featureName, this.ee);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
observer.observe({
|
|
36
|
+
type: 'resource',
|
|
37
|
+
buffered: true
|
|
38
|
+
});
|
|
39
|
+
}
|
|
28
40
|
}
|
|
29
41
|
|
|
30
42
|
/** If any of the sources are active, import the aggregator. otherwise deregister */
|
|
@@ -112,9 +112,10 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
112
112
|
* @param {boolean=} internal if the error was "caught" and deemed "internal" before reporting to the jserrors feature
|
|
113
113
|
* @param {object=} customAttributes any custom attributes to be included in the error payload
|
|
114
114
|
* @param {boolean=} hasReplay a flag indicating if the error occurred during a replay session
|
|
115
|
+
* @param {string=} swallowReason a string indicating pre-defined reason if swallowing the error. Mainly used by the internal error SMs.
|
|
115
116
|
* @returns
|
|
116
117
|
*/
|
|
117
|
-
storeError(err, time, internal, customAttributes, hasReplay) {
|
|
118
|
+
storeError(err, time, internal, customAttributes, hasReplay, swallowReason) {
|
|
118
119
|
if (!err) return;
|
|
119
120
|
// are we in an interaction
|
|
120
121
|
time = time || (0, _now.now)();
|
|
@@ -132,7 +133,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
132
133
|
const {
|
|
133
134
|
shouldSwallow,
|
|
134
135
|
reason
|
|
135
|
-
} = (0, _internalErrors.evaluateInternalError)(stackInfo, internal);
|
|
136
|
+
} = (0, _internalErrors.evaluateInternalError)(stackInfo, internal, swallowReason);
|
|
136
137
|
if (shouldSwallow) {
|
|
137
138
|
(0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Internal/Error/' + reason], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
138
139
|
return;
|
|
@@ -11,10 +11,10 @@ const REASON_SECURITY_POLICY = 'Security-Policy';
|
|
|
11
11
|
* @param {Object} stackInfo - The error stack information.
|
|
12
12
|
* @returns {boolean} - Whether the error should be swallowed or not.
|
|
13
13
|
*/
|
|
14
|
-
function evaluateInternalError(stackInfo, internal) {
|
|
14
|
+
function evaluateInternalError(stackInfo, internal, reason) {
|
|
15
15
|
const output = {
|
|
16
16
|
shouldSwallow: internal || false,
|
|
17
|
-
reason: 'Other'
|
|
17
|
+
reason: reason || 'Other'
|
|
18
18
|
};
|
|
19
19
|
const leadingFrame = stackInfo.frames?.[0];
|
|
20
20
|
/** If we cant otherwise determine from the frames and message, the default of internal + reason will be the fallback */
|
|
@@ -26,9 +26,9 @@ class Instrument extends _instrumentBase.InstrumentBase {
|
|
|
26
26
|
// this try-catch can be removed when IE11 is completely unsupported & gone
|
|
27
27
|
this.removeOnAbort = new AbortController();
|
|
28
28
|
} catch (e) {}
|
|
29
|
-
this.ee.on('internal-error', error => {
|
|
29
|
+
this.ee.on('internal-error', (error, reason) => {
|
|
30
30
|
if (!this.abortHandler) return;
|
|
31
|
-
(0, _handle.handle)('ierr', [(0, _castError.castError)(error), (0, _now.now)(), true, {}, this.#replayRunning], undefined, this.featureName, this.ee);
|
|
31
|
+
(0, _handle.handle)('ierr', [(0, _castError.castError)(error), (0, _now.now)(), true, {}, this.#replayRunning, reason], undefined, this.featureName, this.ee);
|
|
32
32
|
});
|
|
33
33
|
this.ee.on(_constants2.SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, isRunning => {
|
|
34
34
|
this.#replayRunning = isRunning;
|
|
@@ -154,41 +154,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
154
154
|
});
|
|
155
155
|
}
|
|
156
156
|
unload() {
|
|
157
|
-
|
|
158
|
-
if (this.resourcesSent) return;
|
|
159
|
-
this.resourcesSent = true; // make sure this only gets sent once
|
|
160
|
-
|
|
161
|
-
// Capture SMs around network resources using the performance API to assess
|
|
162
|
-
// work to split this out from the ST nodes
|
|
163
|
-
// differentiate between internal+external and ajax+non-ajax
|
|
164
|
-
const ajaxResources = ['beacon', 'fetch', 'xmlhttprequest'];
|
|
165
|
-
const internalUrls = ['nr-data.net', 'newrelic.com', 'nr-local.net', 'localhost'];
|
|
166
|
-
function isInternal(x) {
|
|
167
|
-
return internalUrls.some(y => x.name.indexOf(y) >= 0);
|
|
168
|
-
}
|
|
169
|
-
function isAjax(x) {
|
|
170
|
-
return ajaxResources.includes(x.initiatorType);
|
|
171
|
-
}
|
|
172
|
-
const allResources = performance?.getEntriesByType('resource') || [];
|
|
173
|
-
allResources.forEach(entry => {
|
|
174
|
-
if (isInternal(entry)) {
|
|
175
|
-
if (isAjax(entry)) this.storeSupportabilityMetrics('Generic/Resources/Ajax/Internal');else this.storeSupportabilityMetrics('Generic/Resources/Non-Ajax/Internal');
|
|
176
|
-
} else {
|
|
177
|
-
if (isAjax(entry)) this.storeSupportabilityMetrics('Generic/Resources/Ajax/External');else this.storeSupportabilityMetrics('Generic/Resources/Non-Ajax/External');
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
// Capture SMs for performance markers and measures to assess the usage and possible inclusion of this
|
|
182
|
-
// data in the agent for use in NR
|
|
183
|
-
if (typeof performance !== 'undefined') {
|
|
184
|
-
const markers = performance.getEntriesByType('mark');
|
|
185
|
-
const measures = performance.getEntriesByType('measure');
|
|
186
|
-
if (markers.length) this.storeSupportabilityMetrics('Generic/Performance/Mark/Seen', markers.length);
|
|
187
|
-
if (measures.length) this.storeSupportabilityMetrics('Generic/Performance/Measure/Seen', measures.length);
|
|
188
|
-
}
|
|
189
|
-
} catch (e) {
|
|
190
|
-
// do nothing
|
|
191
|
-
}
|
|
157
|
+
// do nothing for now, marks and measures and resources stats are now being captured by the ge feature
|
|
192
158
|
}
|
|
193
159
|
}
|
|
194
160
|
exports.Aggregate = Aggregate;
|
|
@@ -26,20 +26,23 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
26
26
|
this.interactionsToHarvest = this.events;
|
|
27
27
|
this.domObserver = domObserver;
|
|
28
28
|
this.initialPageLoadInteraction = new _initialPageLoadInteraction.InitialPageLoadInteraction(agentRef.agentIdentifier);
|
|
29
|
+
this.initialPageLoadInteraction.onDone.push(() => {
|
|
30
|
+
// this ensures the .end() method also works with iPL
|
|
31
|
+
this.initialPageLoadInteraction.forceSave = true; // unless forcibly ignored, iPL always finish by default
|
|
32
|
+
this.interactionsToHarvest.add(this.initialPageLoadInteraction);
|
|
33
|
+
this.initialPageLoadInteraction = null;
|
|
34
|
+
});
|
|
29
35
|
_timeToFirstByte.timeToFirstByte.subscribe(({
|
|
30
36
|
attrs
|
|
31
37
|
}) => {
|
|
32
38
|
const loadEventTime = attrs.navigationEntry.loadEventEnd;
|
|
33
|
-
this.initialPageLoadInteraction.forceSave = true;
|
|
34
39
|
this.initialPageLoadInteraction.done(loadEventTime);
|
|
35
|
-
this.interactionsToHarvest.add(this.initialPageLoadInteraction);
|
|
36
|
-
this.initialPageLoadInteraction = null;
|
|
37
40
|
// Report metric on the initial page load time
|
|
38
41
|
(0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, ['SoftNav/Interaction/InitialPageLoad/Duration/Ms', Math.round(loadEventTime)], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
39
42
|
});
|
|
40
43
|
this.latestRouteSetByApi = null;
|
|
41
44
|
this.interactionInProgress = null; // aside from the "page load" interaction, there can only ever be 1 ongoing at a time
|
|
42
|
-
|
|
45
|
+
this.latestHistoryUrl = null;
|
|
43
46
|
this.blocked = false;
|
|
44
47
|
this.waitForFlags(['spa']).then(([spaOn]) => {
|
|
45
48
|
if (spaOn) {
|
|
@@ -59,7 +62,11 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
59
62
|
|
|
60
63
|
// By default, a complete UI driven interaction requires event -> URL change -> DOM mod in that exact order.
|
|
61
64
|
(0, _registerHandler.registerHandler)('newUIEvent', event => this.startUIInteraction(event.type, Math.floor(event.timeStamp), event.target), this.featureName, this.ee);
|
|
62
|
-
(0, _registerHandler.registerHandler)('newURL', (timestamp, url) =>
|
|
65
|
+
(0, _registerHandler.registerHandler)('newURL', (timestamp, url) => {
|
|
66
|
+
// In the case of 'popstate' trigger, by the time the event fires, the URL has already changed, so we need to store what-will-be the *previous* URL for oldURL of next popstate ixn.
|
|
67
|
+
this.latestHistoryUrl = url;
|
|
68
|
+
this.interactionInProgress?.updateHistory(timestamp, url);
|
|
69
|
+
}, this.featureName, this.ee);
|
|
63
70
|
(0, _registerHandler.registerHandler)('newDom', timestamp => {
|
|
64
71
|
this.interactionInProgress?.updateDom(timestamp);
|
|
65
72
|
if (this.interactionInProgress?.seenHistoryAndDomChange()) this.interactionInProgress.done();
|
|
@@ -71,20 +78,23 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
71
78
|
serializer(eventBuffer) {
|
|
72
79
|
// The payload depacker takes the first ixn of a payload (if there are multiple ixns) and positively offset the subsequent ixns timestamps by that amount.
|
|
73
80
|
// In order to accurately portray the real start & end times of the 2nd & onward ixns, we hence need to negatively offset their start timestamps with that of the 1st ixn.
|
|
74
|
-
let firstIxnStartTime
|
|
81
|
+
let firstIxnStartTime;
|
|
75
82
|
const serializedIxnList = [];
|
|
76
83
|
for (const interaction of eventBuffer) {
|
|
77
84
|
serializedIxnList.push(interaction.serialize(firstIxnStartTime));
|
|
78
|
-
if (
|
|
85
|
+
if (firstIxnStartTime === undefined) firstIxnStartTime = Math.floor(interaction.start); // careful not to match or overwrite on 0 value!
|
|
79
86
|
}
|
|
80
87
|
return "bel.7;".concat(serializedIxnList.join(';'));
|
|
81
88
|
}
|
|
82
89
|
startUIInteraction(eventName, startedAt, sourceElem) {
|
|
83
90
|
// this is throttled by instrumentation so that it isn't excessively called
|
|
84
91
|
if (this.interactionInProgress?.createdByApi) return; // api-started interactions cannot be disrupted aka cancelled by UI events (and the vice versa applies as well)
|
|
85
|
-
if (this.interactionInProgress?.done() === false) return;
|
|
86
|
-
|
|
87
|
-
|
|
92
|
+
if (this.interactionInProgress?.done() === false) return; // current in-progress is blocked from closing, e.g. by 'waitForEnd' api option
|
|
93
|
+
|
|
94
|
+
const oldURL = eventName === _constants2.INTERACTION_TRIGGERS[3] ? this.latestHistoryUrl : undefined; // see related comment in 'newURL' handler above, 'popstate'
|
|
95
|
+
this.interactionInProgress = new _interaction.Interaction(this.agentIdentifier, eventName, startedAt, this.latestRouteSetByApi, oldURL);
|
|
96
|
+
if (eventName === _constants2.INTERACTION_TRIGGERS[0]) {
|
|
97
|
+
// 'click'
|
|
88
98
|
const sourceElemText = getActionText(sourceElem);
|
|
89
99
|
if (sourceElemText) this.interactionInProgress.customAttributes.actionText = sourceElemText;
|
|
90
100
|
}
|
|
@@ -130,7 +140,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
130
140
|
// reverse search for the latest completed interaction for efficiency
|
|
131
141
|
const finishedInteraction = interactionsBuffer[idx];
|
|
132
142
|
if (finishedInteraction.isActiveDuring(timestamp)) {
|
|
133
|
-
if (finishedInteraction.trigger !==
|
|
143
|
+
if (finishedInteraction.trigger !== _constants2.IPL_TRIGGER_NAME) return finishedInteraction;
|
|
134
144
|
// It's possible that a complete interaction occurs before page is fully loaded, so we need to consider if a route-change ixn may have overlapped this iPL
|
|
135
145
|
else saveIxn = finishedInteraction;
|
|
136
146
|
}
|
|
@@ -194,9 +204,16 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
194
204
|
// In here, 'this' refers to the EventContext specific to per InteractionHandle instance spawned by each .interaction() api call.
|
|
195
205
|
// Each api call aka IH instance would therefore retain a reference to either the in-progress interaction *at the time of the call* OR a new api-started interaction.
|
|
196
206
|
this.associatedInteraction = thisClass.getInteractionFor(time);
|
|
207
|
+
if (this.associatedInteraction?.trigger === _constants2.IPL_TRIGGER_NAME) this.associatedInteraction = null; // the api get-interaction method cannot target IPL
|
|
197
208
|
if (!this.associatedInteraction) {
|
|
198
209
|
// This new api-driven interaction will be the target of any subsequent .interaction() call, until it is closed by EITHER .end() OR the regular seenHistoryAndDomChange process.
|
|
199
210
|
this.associatedInteraction = thisClass.interactionInProgress = new _interaction.Interaction(thisClass.agentIdentifier, _constants2.API_TRIGGER_NAME, time, thisClass.latestRouteSetByApi);
|
|
211
|
+
thisClass.domObserver.observe(document.body, {
|
|
212
|
+
attributes: true,
|
|
213
|
+
childList: true,
|
|
214
|
+
subtree: true,
|
|
215
|
+
characterData: true
|
|
216
|
+
}); // start observing for DOM changes like a regular UI-driven interaction
|
|
200
217
|
thisClass.setClosureHandlers();
|
|
201
218
|
}
|
|
202
219
|
if (waitForEnd === true) this.associatedInteraction.keepOpenUntilEndApi = true;
|
|
@@ -10,9 +10,10 @@ var _belSerializer = require("../../../common/serialize/bel-serializer");
|
|
|
10
10
|
var _firstPaint = require("../../../common/vitals/first-paint");
|
|
11
11
|
var _firstContentfulPaint = require("../../../common/vitals/first-contentful-paint");
|
|
12
12
|
var _info = require("../../../common/config/info");
|
|
13
|
+
var _constants = require("../constants");
|
|
13
14
|
class InitialPageLoadInteraction extends _interaction.Interaction {
|
|
14
15
|
constructor(agentIdentifier) {
|
|
15
|
-
super(agentIdentifier,
|
|
16
|
+
super(agentIdentifier, _constants.IPL_TRIGGER_NAME, 0, null);
|
|
16
17
|
const agentInfo = (0, _info.getInfo)(agentIdentifier);
|
|
17
18
|
this.queueTime = agentInfo.queueTime;
|
|
18
19
|
this.appTime = agentInfo.applicationTime;
|