@newrelic/browser-agent 1.240.0 → 1.242.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/dist/cjs/cdn/polyfills/lite.js +13 -1
- package/dist/cjs/cdn/polyfills/pro.js +17 -1
- package/dist/cjs/cdn/polyfills/spa.js +18 -1
- package/dist/cjs/common/config/state/init.js +48 -19
- 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/query-selector.js +16 -0
- package/dist/cjs/features/metrics/aggregate/index.js +10 -7
- package/dist/cjs/features/page_view_timing/aggregate/index.js +9 -9
- package/dist/cjs/features/session_replay/aggregate/index.js +90 -40
- package/dist/cjs/features/utils/instrument-base.js +1 -0
- package/dist/cjs/loaders/browser-agent.js +2 -1
- package/dist/esm/cdn/polyfills/lite.js +8 -1
- package/dist/esm/cdn/polyfills/pro.js +13 -2
- package/dist/esm/cdn/polyfills/spa.js +13 -1
- package/dist/esm/common/config/state/init.js +48 -19
- 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/query-selector.js +9 -0
- package/dist/esm/features/metrics/aggregate/index.js +10 -7
- package/dist/esm/features/page_view_timing/aggregate/index.js +9 -9
- package/dist/esm/features/session_replay/aggregate/index.js +90 -40
- package/dist/esm/features/utils/instrument-base.js +1 -0
- package/dist/esm/loaders/browser-agent.js +2 -1
- package/dist/types/common/config/state/init.d.ts.map +1 -1
- package/dist/types/common/dom/query-selector.d.ts +2 -0
- package/dist/types/common/dom/query-selector.d.ts.map +1 -0
- package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/page_view_timing/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/aggregate/index.d.ts +17 -5
- package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
- package/dist/types/loaders/browser-agent.d.ts.map +1 -1
- package/package.json +2 -3
- package/src/cdn/polyfills/lite.js +14 -1
- package/src/cdn/polyfills/pro.js +23 -2
- package/src/cdn/polyfills/spa.js +24 -1
- package/src/common/config/state/init.js +46 -17
- package/src/common/config/state/init.test.js +40 -0
- package/src/common/dom/query-selector.js +9 -0
- package/src/common/dom/query-selector.test.js +24 -0
- package/src/common/harvest/harvest-scheduler.test.js +2 -2
- package/src/features/metrics/aggregate/index.js +5 -3
- package/src/features/page_view_timing/aggregate/index.js +7 -6
- package/src/features/session_replay/aggregate/index.component-test.js +10 -10
- package/src/features/session_replay/aggregate/index.js +71 -34
- package/src/features/utils/instrument-base.js +1 -0
- package/src/loaders/browser-agent.js +3 -1
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
require("../polyfills.js");
|
|
4
|
-
require("
|
|
4
|
+
var _agent = require("../../loaders/agent");
|
|
5
|
+
var _instrument = require("../../features/page_view_event/instrument");
|
|
6
|
+
var _instrument2 = require("../../features/page_view_timing/instrument");
|
|
7
|
+
var _instrument3 = require("../../features/metrics/instrument");
|
|
8
|
+
/**
|
|
9
|
+
* @file Creates a version of the "Lite" agent loader with [core-js]{@link https://github.com/zloirock/core-js}
|
|
10
|
+
* polyfills for pre-ES6 browsers and IE 11.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
new _agent.Agent({
|
|
14
|
+
features: [_instrument.Instrument, _instrument2.Instrument, _instrument3.Instrument],
|
|
15
|
+
loaderType: 'lite-polyfills'
|
|
16
|
+
});
|
|
@@ -1,4 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
require("../polyfills.js");
|
|
4
|
-
require("
|
|
4
|
+
var _agent = require("../../loaders/agent");
|
|
5
|
+
var _instrument = require("../../features/page_view_event/instrument");
|
|
6
|
+
var _instrument2 = require("../../features/page_view_timing/instrument");
|
|
7
|
+
var _instrument3 = require("../../features/metrics/instrument");
|
|
8
|
+
var _instrument4 = require("../../features/jserrors/instrument");
|
|
9
|
+
var _instrument5 = require("../../features/ajax/instrument");
|
|
10
|
+
var _instrument6 = require("../../features/session_trace/instrument");
|
|
11
|
+
var _instrument7 = require("../../features/page_action/instrument");
|
|
12
|
+
/**
|
|
13
|
+
* @file Creates a version of the "PRO" agent loader with [core-js]{@link https://github.com/zloirock/core-js}
|
|
14
|
+
* polyfills for pre-ES6 browsers and IE 11.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
new _agent.Agent({
|
|
18
|
+
features: [_instrument.Instrument, _instrument2.Instrument, _instrument6.Instrument, _instrument5.Instrument, _instrument3.Instrument, _instrument7.Instrument, _instrument4.Instrument],
|
|
19
|
+
loaderType: 'pro-polyfills'
|
|
20
|
+
});
|
|
@@ -1,4 +1,21 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
require("../polyfills.js");
|
|
4
|
-
require("
|
|
4
|
+
var _agent = require("../../loaders/agent");
|
|
5
|
+
var _instrument = require("../../features/page_view_event/instrument");
|
|
6
|
+
var _instrument2 = require("../../features/page_view_timing/instrument");
|
|
7
|
+
var _instrument3 = require("../../features/metrics/instrument");
|
|
8
|
+
var _instrument4 = require("../../features/jserrors/instrument");
|
|
9
|
+
var _instrument5 = require("../../features/ajax/instrument");
|
|
10
|
+
var _instrument6 = require("../../features/session_trace/instrument");
|
|
11
|
+
var _instrument7 = require("../../features/spa/instrument");
|
|
12
|
+
var _instrument8 = require("../../features/page_action/instrument");
|
|
13
|
+
/**
|
|
14
|
+
* @file Creates a version of the "SPA" agent loader with [core-js]{@link https://github.com/zloirock/core-js}
|
|
15
|
+
* polyfills for pre-ES6 browsers and IE 11.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
new _agent.Agent({
|
|
19
|
+
features: [_instrument5.Instrument, _instrument.Instrument, _instrument2.Instrument, _instrument6.Instrument, _instrument3.Instrument, _instrument8.Instrument, _instrument4.Instrument, _instrument7.Instrument],
|
|
20
|
+
loaderType: 'spa-polyfills'
|
|
21
|
+
});
|
|
@@ -6,16 +6,36 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.getConfiguration = getConfiguration;
|
|
7
7
|
exports.getConfigurationValue = getConfigurationValue;
|
|
8
8
|
exports.setConfiguration = setConfiguration;
|
|
9
|
+
var _querySelector = require("../../dom/query-selector");
|
|
9
10
|
var _constants = require("../../session/constants");
|
|
11
|
+
var _console = require("../../util/console");
|
|
10
12
|
var _nreum = require("../../window/nreum");
|
|
11
13
|
var _configurable = require("./configurable");
|
|
12
14
|
const model = () => {
|
|
13
15
|
const hiddenState = {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
mask_selector: '*',
|
|
17
|
+
block_selector: '[data-nr-block]',
|
|
18
|
+
mask_input_options: {
|
|
19
|
+
color: false,
|
|
20
|
+
date: false,
|
|
21
|
+
'datetime-local': false,
|
|
22
|
+
email: false,
|
|
23
|
+
month: false,
|
|
24
|
+
number: false,
|
|
25
|
+
range: false,
|
|
26
|
+
search: false,
|
|
27
|
+
tel: false,
|
|
28
|
+
text: false,
|
|
29
|
+
time: false,
|
|
30
|
+
url: false,
|
|
31
|
+
week: false,
|
|
32
|
+
// unify textarea and select element with text input
|
|
33
|
+
textarea: false,
|
|
34
|
+
select: false,
|
|
35
|
+
password: true // This will be enforced to always be true in the setter
|
|
17
36
|
}
|
|
18
37
|
};
|
|
38
|
+
|
|
19
39
|
return {
|
|
20
40
|
proxy: {
|
|
21
41
|
assets: undefined,
|
|
@@ -86,38 +106,47 @@ const model = () => {
|
|
|
86
106
|
autoStart: true,
|
|
87
107
|
enabled: false,
|
|
88
108
|
harvestTimeSeconds: 60,
|
|
89
|
-
|
|
90
|
-
|
|
109
|
+
sampling_rate: 50,
|
|
110
|
+
// float from 0 - 100
|
|
111
|
+
error_sampling_rate: 50,
|
|
112
|
+
// float from 0 - 100
|
|
91
113
|
// recording config settings
|
|
92
|
-
|
|
93
|
-
|
|
114
|
+
mask_all_inputs: true,
|
|
115
|
+
// this has a getter/setter to facilitate validation of the selectors
|
|
116
|
+
get mask_text_selector() {
|
|
117
|
+
return hiddenState.mask_selector;
|
|
118
|
+
},
|
|
119
|
+
set mask_text_selector(val) {
|
|
120
|
+
if ((0, _querySelector.isValidSelector)(val)) hiddenState.mask_selector = val + ',[data-nr-mask]';else if (val === null) hiddenState.mask_selector = val; // null is acceptable, which completely disables the behavior
|
|
121
|
+
else (0, _console.warn)('An invalid session_replay.mask_selector was provided and will not be used', val);
|
|
122
|
+
},
|
|
94
123
|
// these properties only have getters because they are enforcable constants and should error if someone tries to override them
|
|
95
|
-
get
|
|
124
|
+
get block_class() {
|
|
96
125
|
return 'nr-block';
|
|
97
126
|
},
|
|
98
|
-
get
|
|
127
|
+
get ignore_class() {
|
|
99
128
|
return 'nr-ignore';
|
|
100
129
|
},
|
|
101
|
-
get
|
|
130
|
+
get mask_text_class() {
|
|
102
131
|
return 'nr-mask';
|
|
103
132
|
},
|
|
104
133
|
// props with a getter and setter are used to extend enforcable constants with customer input
|
|
105
134
|
// we must preserve data-nr-block no matter what else the customer sets
|
|
106
|
-
get
|
|
107
|
-
return hiddenState.
|
|
135
|
+
get block_selector() {
|
|
136
|
+
return hiddenState.block_selector;
|
|
108
137
|
},
|
|
109
|
-
set
|
|
110
|
-
hiddenState.
|
|
138
|
+
set block_selector(val) {
|
|
139
|
+
if ((0, _querySelector.isValidSelector)(val)) hiddenState.block_selector += ",".concat(val);else if (val !== '') (0, _console.warn)('An invalid session_replay.block_selector was provided and will not be used', val);
|
|
111
140
|
},
|
|
112
141
|
// password: must always be present and true no matter what customer sets
|
|
113
|
-
get
|
|
114
|
-
return hiddenState.
|
|
142
|
+
get mask_input_options() {
|
|
143
|
+
return hiddenState.mask_input_options;
|
|
115
144
|
},
|
|
116
|
-
set
|
|
117
|
-
hiddenState.
|
|
145
|
+
set mask_input_options(val) {
|
|
146
|
+
if (val && typeof val === 'object') hiddenState.mask_input_options = {
|
|
118
147
|
...val,
|
|
119
148
|
password: true
|
|
120
|
-
};
|
|
149
|
+
};else (0, _console.warn)('An invalid session_replay.mask_input_option was provided and will not be used', val);
|
|
121
150
|
}
|
|
122
151
|
},
|
|
123
152
|
spa: {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.isValidSelector = void 0;
|
|
7
|
+
const isValidSelector = selector => {
|
|
8
|
+
if (!selector || typeof selector !== 'string') return false;
|
|
9
|
+
try {
|
|
10
|
+
document.createDocumentFragment().querySelector(selector);
|
|
11
|
+
} catch {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
return true;
|
|
15
|
+
};
|
|
16
|
+
exports.isValidSelector = isValidSelector;
|
|
@@ -33,13 +33,16 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
33
33
|
this.singleChecks(); // checks that are run only one time, at script load
|
|
34
34
|
this.eachSessionChecks(); // the start of every time user engages with page
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
36
|
+
this.ee.on("drain-".concat(this.featureName), () => {
|
|
37
|
+
// *cli, Mar 23 - Per NR-94597, this feature should only harvest ONCE at the (potential) EoL time of the page.
|
|
38
|
+
scheduler = new _harvestScheduler.HarvestScheduler('jserrors', {
|
|
39
|
+
onUnload: () => this.unload()
|
|
40
|
+
}, this);
|
|
41
|
+
scheduler.harvest.on('jserrors', () => ({
|
|
42
|
+
body: this.aggregator.take(['cm', 'sm'])
|
|
43
|
+
}));
|
|
44
|
+
}); // this is needed to ensure EoL is "on" and sent
|
|
45
|
+
|
|
43
46
|
this.drain();
|
|
44
47
|
}
|
|
45
48
|
storeSupportabilityMetrics(name, value) {
|
|
@@ -58,21 +58,21 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
58
58
|
|
|
59
59
|
/* It's important that CWV api, like "onLCP", is called before this scheduler is initialized. The reason is because they listen to the same
|
|
60
60
|
on vis change or pagehide events, and we'd want ex. onLCP to record the timing (win the race) before we try to send "final harvest". */
|
|
61
|
-
|
|
62
|
-
onFinished: function () {
|
|
63
|
-
return _this.onHarvestFinished(...arguments);
|
|
64
|
-
},
|
|
65
|
-
getPayload: function () {
|
|
66
|
-
return _this.prepareHarvest(...arguments);
|
|
67
|
-
}
|
|
68
|
-
}, this);
|
|
69
|
-
(0, _registerHandler.registerHandler)('timing', (name, value, attrs) => this.addTiming(name, value, attrs), this.featureName, this.ee); // notice CLS is added to all timings via 4th param
|
|
61
|
+
|
|
70
62
|
(0, _registerHandler.registerHandler)('docHidden', msTimestamp => this.endCurrentSession(msTimestamp), this.featureName, this.ee);
|
|
71
63
|
(0, _registerHandler.registerHandler)('winPagehide', msTimestamp => this.recordPageUnload(msTimestamp), this.featureName, this.ee);
|
|
72
64
|
const initialHarvestSeconds = (0, _config.getConfigurationValue)(this.agentIdentifier, 'page_view_timing.initialHarvestSeconds') || 10;
|
|
73
65
|
const harvestTimeSeconds = (0, _config.getConfigurationValue)(this.agentIdentifier, 'page_view_timing.harvestTimeSeconds') || 30;
|
|
74
66
|
// send initial data sooner, then start regular
|
|
75
67
|
this.ee.on("drain-".concat(this.featureName), () => {
|
|
68
|
+
this.scheduler = new _harvestScheduler.HarvestScheduler('events', {
|
|
69
|
+
onFinished: function () {
|
|
70
|
+
return _this.onHarvestFinished(...arguments);
|
|
71
|
+
},
|
|
72
|
+
getPayload: function () {
|
|
73
|
+
return _this.prepareHarvest(...arguments);
|
|
74
|
+
}
|
|
75
|
+
}, this);
|
|
76
76
|
this.scheduler.startTimer(harvestTimeSeconds, initialHarvestSeconds);
|
|
77
77
|
});
|
|
78
78
|
this.drain();
|
|
@@ -14,6 +14,9 @@ var _aggregateBase = require("../../utils/aggregate-base");
|
|
|
14
14
|
var _sharedChannel = require("../../../common/constants/shared-channel");
|
|
15
15
|
var _encode = require("../../../common/url/encode");
|
|
16
16
|
var _console = require("../../../common/util/console");
|
|
17
|
+
var _runtime = require("../../../common/constants/runtime");
|
|
18
|
+
var _constants2 = require("../../metrics/constants");
|
|
19
|
+
var _features = require("../../../loaders/features/features");
|
|
17
20
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
18
21
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } /*
|
|
19
22
|
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
@@ -72,17 +75,30 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
72
75
|
* -- When visibility changes from "hidden" -> "visible", it must capture a full snapshot for the replay to work correctly across tabs
|
|
73
76
|
*/
|
|
74
77
|
this.hasSnapshot = false;
|
|
78
|
+
/** Payload metadata -- Should indicate that the payload being sent has a meta node. The meta node should always precede a snapshot node. */
|
|
79
|
+
this.hasMeta = false;
|
|
75
80
|
/** Payload metadata -- Should indicate that the payload being sent contains an error. Used for query/filter purposes in UI */
|
|
76
81
|
this.hasError = false;
|
|
77
82
|
|
|
78
|
-
/** Payload metadata -- Should indicate when a replay blob started recording. Resets each time a harvest occurs.
|
|
83
|
+
/** Payload metadata -- Should indicate when a replay blob started recording. Resets each time a harvest occurs.
|
|
84
|
+
* cycle timestamps are used as fallbacks if event timestamps cannot be used
|
|
85
|
+
*/
|
|
79
86
|
this.timestamp = {
|
|
80
|
-
|
|
81
|
-
|
|
87
|
+
event: {
|
|
88
|
+
first: undefined,
|
|
89
|
+
last: undefined
|
|
90
|
+
},
|
|
91
|
+
cycle: {
|
|
92
|
+
first: undefined,
|
|
93
|
+
last: undefined
|
|
94
|
+
}
|
|
82
95
|
};
|
|
83
96
|
|
|
84
97
|
/** A value which increments with every new mutation node reported. Resets after a harvest is sent */
|
|
85
98
|
this.payloadBytesEstimation = 0;
|
|
99
|
+
|
|
100
|
+
/** Hold on to the last meta node, so that it can be re-inserted if the meta and snapshot nodes are broken up due to harvesting */
|
|
101
|
+
this.lastMeta = undefined;
|
|
86
102
|
const shouldSetup = (0, _config.getConfigurationValue)(agentIdentifier, 'privacy.cookies_enabled') === true && (0, _config.getConfigurationValue)(agentIdentifier, 'session_trace.enabled') === true;
|
|
87
103
|
|
|
88
104
|
/** The method to stop recording. This defaults to a noop, but is overwritten once the recording library is imported and initialized */
|
|
@@ -90,7 +106,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
90
106
|
if (shouldSetup) {
|
|
91
107
|
// The SessionEntity class can emit a message indicating the session was cleared and reset (expiry, inactivity). This feature must abort and never resume if that occurs.
|
|
92
108
|
this.ee.on(_sessionEntity.SESSION_EVENTS.RESET, () => {
|
|
93
|
-
this.abort();
|
|
109
|
+
this.abort('Session Reset');
|
|
94
110
|
});
|
|
95
111
|
|
|
96
112
|
// The SessionEntity class can emit a message indicating the session was paused (visibility change). This feature must stop recording if that occurs.
|
|
@@ -132,7 +148,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
132
148
|
}, this.featureName, this.ee);
|
|
133
149
|
this.waitForFlags(['sr']).then(_ref => {
|
|
134
150
|
let [flagOn] = _ref;
|
|
135
|
-
return this.initializeRecording(flagOn, Math.random() < (0, _config.getConfigurationValue)(this.agentIdentifier, 'session_replay.
|
|
151
|
+
return this.initializeRecording(flagOn, Math.random() * 100 < (0, _config.getConfigurationValue)(this.agentIdentifier, 'session_replay.error_sampling_rate'), Math.random() * 100 < (0, _config.getConfigurationValue)(this.agentIdentifier, 'session_replay.sampling_rate'));
|
|
136
152
|
}).then(() => _sharedChannel.sharedChannel.onReplayReady(this.mode)); // notify watchers that replay started with the mode
|
|
137
153
|
|
|
138
154
|
this.drain();
|
|
@@ -173,6 +189,12 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
173
189
|
if (this.mode === _sessionEntity.MODE.ERROR && this.errorNoticed) {
|
|
174
190
|
this.mode = _sessionEntity.MODE.FULL;
|
|
175
191
|
}
|
|
192
|
+
try {
|
|
193
|
+
// Do not change the webpackChunkName or it will break the webpack nrba-chunking plugin
|
|
194
|
+
recorder = (await Promise.resolve().then(() => _interopRequireWildcard(require( /* webpackChunkName: "recorder" */'rrweb')))).record;
|
|
195
|
+
} catch (err) {
|
|
196
|
+
return this.abort('Recorder failed to import');
|
|
197
|
+
}
|
|
176
198
|
|
|
177
199
|
// FULL mode records AND reports from the beginning, while ERROR mode only records (but does not report).
|
|
178
200
|
// ERROR mode will do this until an error is thrown, and then switch into FULL mode.
|
|
@@ -181,12 +203,6 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
181
203
|
// We only report (harvest) in FULL mode
|
|
182
204
|
this.scheduler.startTimer(this.harvestTimeSeconds);
|
|
183
205
|
}
|
|
184
|
-
try {
|
|
185
|
-
// Do not change the webpackChunkName or it will break the webpack nrba-chunking plugin
|
|
186
|
-
recorder = (await Promise.resolve().then(() => _interopRequireWildcard(require( /* webpackChunkName: "recorder" */'rrweb')))).record;
|
|
187
|
-
} catch (err) {
|
|
188
|
-
return this.abort();
|
|
189
|
-
}
|
|
190
206
|
try {
|
|
191
207
|
// Do not change the webpackChunkName or it will break the webpack nrba-chunking plugin
|
|
192
208
|
const {
|
|
@@ -221,6 +237,8 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
221
237
|
getHarvestContents() {
|
|
222
238
|
const agentRuntime = (0, _config.getRuntime)(this.agentIdentifier);
|
|
223
239
|
const info = (0, _config.getInfo)(this.agentIdentifier);
|
|
240
|
+
const firstTimestamp = this.timestamp.event.first || this.timestamp.cycle.first;
|
|
241
|
+
const lastTimestamp = this.timestamp.event.last || this.timestamp.cycle.last;
|
|
224
242
|
return {
|
|
225
243
|
qs: {
|
|
226
244
|
browser_monitoring_key: info.licenseKey,
|
|
@@ -231,11 +249,12 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
231
249
|
...(this.shouldCompress && {
|
|
232
250
|
content_encoding: 'gzip'
|
|
233
251
|
}),
|
|
234
|
-
'replay.firstTimestamp':
|
|
235
|
-
'replay.lastTimestamp':
|
|
236
|
-
'replay.durationMs':
|
|
252
|
+
'replay.firstTimestamp': firstTimestamp,
|
|
253
|
+
'replay.lastTimestamp': lastTimestamp,
|
|
254
|
+
'replay.durationMs': lastTimestamp - firstTimestamp,
|
|
237
255
|
agentVersion: agentRuntime.version,
|
|
238
256
|
session: agentRuntime.session.state.value,
|
|
257
|
+
hasMeta: this.hasMeta,
|
|
239
258
|
hasSnapshot: this.hasSnapshot,
|
|
240
259
|
hasError: this.hasError,
|
|
241
260
|
isFirstChunk: this.isFirstChunk,
|
|
@@ -250,7 +269,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
250
269
|
onHarvestFinished(result) {
|
|
251
270
|
// The mutual decision for now is to stop recording and clear buffers if ingest is experiencing 429 rate limiting
|
|
252
271
|
if (result.status === 429) {
|
|
253
|
-
this.abort();
|
|
272
|
+
this.abort('429: Too many requests');
|
|
254
273
|
}
|
|
255
274
|
if (this.blocked) this.scheduler.stopTimer(true);
|
|
256
275
|
}
|
|
@@ -260,6 +279,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
260
279
|
this.events = [];
|
|
261
280
|
this.isFirstChunk = false;
|
|
262
281
|
this.hasSnapshot = false;
|
|
282
|
+
this.hasMeta = false;
|
|
263
283
|
this.hasError = false;
|
|
264
284
|
this.payloadBytesEstimation = 0;
|
|
265
285
|
this.clearTimestamps();
|
|
@@ -269,29 +289,32 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
269
289
|
startRecording() {
|
|
270
290
|
if (!recorder) {
|
|
271
291
|
(0, _console.warn)('Recording library was never imported');
|
|
272
|
-
return this.abort();
|
|
292
|
+
return this.abort('Recorder was never imported');
|
|
273
293
|
}
|
|
294
|
+
this.clearTimestamps();
|
|
295
|
+
// set the fallbacks as early as possible
|
|
296
|
+
this.setTimestamps();
|
|
274
297
|
this.recording = true;
|
|
275
298
|
const {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
299
|
+
block_class,
|
|
300
|
+
ignore_class,
|
|
301
|
+
mask_text_class,
|
|
302
|
+
block_selector,
|
|
303
|
+
mask_input_options,
|
|
304
|
+
mask_text_selector,
|
|
305
|
+
mask_all_inputs
|
|
283
306
|
} = (0, _config.getConfigurationValue)(this.agentIdentifier, 'session_replay');
|
|
284
307
|
// set up rrweb configurations for maximum privacy --
|
|
285
308
|
// https://newrelic.atlassian.net/wiki/spaces/O11Y/pages/2792293280/2023+02+28+Browser+-+Session+Replay#Configuration-options
|
|
286
309
|
const stop = recorder({
|
|
287
310
|
emit: this.store.bind(this),
|
|
288
|
-
blockClass,
|
|
289
|
-
ignoreClass,
|
|
290
|
-
maskTextClass,
|
|
291
|
-
blockSelector,
|
|
292
|
-
maskInputOptions,
|
|
293
|
-
maskTextSelector,
|
|
294
|
-
maskAllInputs,
|
|
311
|
+
blockClass: block_class,
|
|
312
|
+
ignoreClass: ignore_class,
|
|
313
|
+
maskTextClass: mask_text_class,
|
|
314
|
+
blockSelector: block_selector,
|
|
315
|
+
maskInputOptions: mask_input_options,
|
|
316
|
+
maskTextSelector: mask_text_selector,
|
|
317
|
+
maskAllInputs: mask_all_inputs,
|
|
295
318
|
checkoutEveryNms: CHECKOUT_MS[this.mode]
|
|
296
319
|
});
|
|
297
320
|
this.stopRecording = () => {
|
|
@@ -302,6 +325,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
302
325
|
|
|
303
326
|
/** Store a payload in the buffer (this.events). This should be the callback to the recording lib noticing a mutation */
|
|
304
327
|
store(event, isCheckout) {
|
|
328
|
+
this.setTimestamps(event);
|
|
305
329
|
if (this.blocked) return;
|
|
306
330
|
const eventBytes = (0, _stringify.stringify)(event).length;
|
|
307
331
|
/** The estimated size of the payload after compression */
|
|
@@ -309,7 +333,8 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
309
333
|
// Vortex will block payloads at a certain size, we might as well not send.
|
|
310
334
|
if (payloadSize > MAX_PAYLOAD_SIZE) {
|
|
311
335
|
this.clearBuffer();
|
|
312
|
-
|
|
336
|
+
this.ee.emit(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['SessionReplay/Too-Big/Seen'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
337
|
+
return this.abort('Payload too big');
|
|
313
338
|
}
|
|
314
339
|
// Checkout events are flags by the recording lib that indicate a fullsnapshot was taken every n ms. These are important
|
|
315
340
|
// to help reconstruct the replay later and must be included. While waiting and buffering for errors to come through,
|
|
@@ -318,8 +343,22 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
318
343
|
// we are still waiting for an error to throw, so keep wiping the buffer over time
|
|
319
344
|
this.clearBuffer();
|
|
320
345
|
}
|
|
321
|
-
|
|
322
|
-
|
|
346
|
+
|
|
347
|
+
// meta event
|
|
348
|
+
if (event.type === 4) {
|
|
349
|
+
this.hasMeta = true;
|
|
350
|
+
this.lastMeta = event;
|
|
351
|
+
}
|
|
352
|
+
// snapshot event
|
|
353
|
+
if (event.type === 2) {
|
|
354
|
+
this.hasSnapshot = true;
|
|
355
|
+
// small chance that the meta event got separated from its matching snapshot across payload harvests
|
|
356
|
+
// it needs to precede the snapshot, so shove it in first.
|
|
357
|
+
if (!this.hasMeta) {
|
|
358
|
+
this.events.push(this.lastMeta);
|
|
359
|
+
this.hasMeta = true;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
323
362
|
this.events.push(event);
|
|
324
363
|
this.payloadBytesEstimation += eventBytes;
|
|
325
364
|
|
|
@@ -336,15 +375,25 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
336
375
|
if (!recorder) return;
|
|
337
376
|
recorder.takeFullSnapshot();
|
|
338
377
|
}
|
|
339
|
-
setTimestamps(
|
|
340
|
-
if
|
|
341
|
-
|
|
342
|
-
this.timestamp.
|
|
378
|
+
setTimestamps(event) {
|
|
379
|
+
// fallbacks if timestamps cannot be derived from rrweb events
|
|
380
|
+
this.timestamp.cycle.last = (0, _config.getRuntime)(this.agentIdentifier).offset + _runtime.globalScope.performance.now();
|
|
381
|
+
if (!this.timestamp.cycle.first) this.timestamp.cycle.first = this.timestamp.cycle.last;
|
|
382
|
+
// timestamps based on rrweb events
|
|
383
|
+
if (!event || !event.timestamp) return;
|
|
384
|
+
if (!this.timestamp.event.first) this.timestamp.event.first = event.timestamp;
|
|
385
|
+
this.timestamp.event.last = event.timestamp;
|
|
343
386
|
}
|
|
344
387
|
clearTimestamps() {
|
|
345
388
|
this.timestamp = {
|
|
346
|
-
|
|
347
|
-
|
|
389
|
+
event: {
|
|
390
|
+
first: undefined,
|
|
391
|
+
last: undefined
|
|
392
|
+
},
|
|
393
|
+
cycle: {
|
|
394
|
+
first: undefined,
|
|
395
|
+
last: undefined
|
|
396
|
+
}
|
|
348
397
|
};
|
|
349
398
|
}
|
|
350
399
|
|
|
@@ -356,7 +405,8 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
356
405
|
}
|
|
357
406
|
|
|
358
407
|
/** Abort the feature, once aborted it will not resume */
|
|
359
|
-
abort() {
|
|
408
|
+
abort(reason) {
|
|
409
|
+
(0, _console.warn)("SR aborted -- ".concat(reason));
|
|
360
410
|
this.blocked = true;
|
|
361
411
|
this.mode = _sessionEntity.MODE.OFF;
|
|
362
412
|
this.stopRecording();
|
|
@@ -119,6 +119,7 @@ class InstrumentBase extends _featureBase.FeatureBase {
|
|
|
119
119
|
(0, _console.warn)("Downloading and initializing ".concat(this.featureName, " failed..."), e);
|
|
120
120
|
this.abortHandler?.(); // undo any important alterations made to the page
|
|
121
121
|
// not supported yet but nice to do: "abort" this agent's EE for this feature specifically
|
|
122
|
+
(0, _drain.drain)(this.agentIdentifier, this.featureName);
|
|
122
123
|
loadedSuccessfully(false);
|
|
123
124
|
}
|
|
124
125
|
};
|
|
@@ -13,6 +13,7 @@ var _instrument5 = require("../features/ajax/instrument");
|
|
|
13
13
|
var _instrument6 = require("../features/session_trace/instrument");
|
|
14
14
|
var _instrument7 = require("../features/spa/instrument");
|
|
15
15
|
var _instrument8 = require("../features/page_action/instrument");
|
|
16
|
+
var _instrument9 = require("../features/session_replay/instrument");
|
|
16
17
|
/**
|
|
17
18
|
* An agent class with all feature modules available. Features may be disabled and enabled via runtime configuration.
|
|
18
19
|
* The BrowserAgent class is the most convenient and reliable option for most use cases.
|
|
@@ -21,7 +22,7 @@ class BrowserAgent extends _agent.Agent {
|
|
|
21
22
|
constructor(args) {
|
|
22
23
|
super({
|
|
23
24
|
...args,
|
|
24
|
-
features: [_instrument5.Instrument, _instrument.Instrument, _instrument2.Instrument, _instrument6.Instrument, _instrument3.Instrument, _instrument8.Instrument, _instrument4.Instrument, _instrument7.Instrument],
|
|
25
|
+
features: [_instrument5.Instrument, _instrument.Instrument, _instrument2.Instrument, _instrument6.Instrument, _instrument3.Instrument, _instrument8.Instrument, _instrument4.Instrument, _instrument7.Instrument, _instrument9.Instrument],
|
|
25
26
|
loaderType: 'browser-agent'
|
|
26
27
|
});
|
|
27
28
|
}
|
|
@@ -4,4 +4,11 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import '../polyfills.js';
|
|
7
|
-
import '
|
|
7
|
+
import { Agent } from '../../loaders/agent';
|
|
8
|
+
import { Instrument as InstrumentPageViewEvent } from '../../features/page_view_event/instrument';
|
|
9
|
+
import { Instrument as InstrumentPageViewTiming } from '../../features/page_view_timing/instrument';
|
|
10
|
+
import { Instrument as InstrumentMetrics } from '../../features/metrics/instrument';
|
|
11
|
+
new Agent({
|
|
12
|
+
features: [InstrumentPageViewEvent, InstrumentPageViewTiming, InstrumentMetrics],
|
|
13
|
+
loaderType: 'lite-polyfills'
|
|
14
|
+
});
|
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @file Creates a version of the "
|
|
2
|
+
* @file Creates a version of the "PRO" agent loader with [core-js]{@link https://github.com/zloirock/core-js}
|
|
3
3
|
* polyfills for pre-ES6 browsers and IE 11.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import '../polyfills.js';
|
|
7
|
-
import '
|
|
7
|
+
import { Agent } from '../../loaders/agent';
|
|
8
|
+
import { Instrument as InstrumentPageViewEvent } from '../../features/page_view_event/instrument';
|
|
9
|
+
import { Instrument as InstrumentPageViewTiming } from '../../features/page_view_timing/instrument';
|
|
10
|
+
import { Instrument as InstrumentMetrics } from '../../features/metrics/instrument';
|
|
11
|
+
import { Instrument as InstrumentErrors } from '../../features/jserrors/instrument';
|
|
12
|
+
import { Instrument as InstrumentXhr } from '../../features/ajax/instrument';
|
|
13
|
+
import { Instrument as InstrumentSessionTrace } from '../../features/session_trace/instrument';
|
|
14
|
+
import { Instrument as InstrumentPageAction } from '../../features/page_action/instrument';
|
|
15
|
+
new Agent({
|
|
16
|
+
features: [InstrumentPageViewEvent, InstrumentPageViewTiming, InstrumentSessionTrace, InstrumentXhr, InstrumentMetrics, InstrumentPageAction, InstrumentErrors],
|
|
17
|
+
loaderType: 'pro-polyfills'
|
|
18
|
+
});
|
|
@@ -4,4 +4,16 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import '../polyfills.js';
|
|
7
|
-
import '
|
|
7
|
+
import { Agent } from '../../loaders/agent';
|
|
8
|
+
import { Instrument as InstrumentPageViewEvent } from '../../features/page_view_event/instrument';
|
|
9
|
+
import { Instrument as InstrumentPageViewTiming } from '../../features/page_view_timing/instrument';
|
|
10
|
+
import { Instrument as InstrumentMetrics } from '../../features/metrics/instrument';
|
|
11
|
+
import { Instrument as InstrumentErrors } from '../../features/jserrors/instrument';
|
|
12
|
+
import { Instrument as InstrumentXhr } from '../../features/ajax/instrument';
|
|
13
|
+
import { Instrument as InstrumentSessionTrace } from '../../features/session_trace/instrument';
|
|
14
|
+
import { Instrument as InstrumentSpa } from '../../features/spa/instrument';
|
|
15
|
+
import { Instrument as InstrumentPageAction } from '../../features/page_action/instrument';
|
|
16
|
+
new Agent({
|
|
17
|
+
features: [InstrumentXhr, InstrumentPageViewEvent, InstrumentPageViewTiming, InstrumentSessionTrace, InstrumentMetrics, InstrumentPageAction, InstrumentErrors, InstrumentSpa],
|
|
18
|
+
loaderType: 'spa-polyfills'
|
|
19
|
+
});
|