@newrelic/browser-agent 1.241.0 → 1.243.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 +1465 -0
- 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 +32 -5
- 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/common/session/session-entity.js +20 -2
- package/dist/cjs/common/wrap/wrap-function.js +1 -1
- package/dist/cjs/features/ajax/aggregate/index.js +1 -1
- package/dist/cjs/features/session_replay/aggregate/index.js +84 -50
- package/dist/cjs/features/utils/feature-base.js +1 -2
- package/dist/cjs/features/utils/instrument-base.js +1 -0
- package/dist/cjs/loaders/api/api.js +2 -2
- package/dist/cjs/loaders/api/apiAsync.js +0 -37
- package/dist/cjs/loaders/configure/configure.js +1 -1
- package/dist/cjs/loaders/configure/public-path.js +6 -3
- 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 +32 -5
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/common/dom/query-selector.js +9 -0
- package/dist/esm/common/session/session-entity.js +18 -1
- package/dist/esm/common/wrap/wrap-function.js +1 -1
- package/dist/esm/features/ajax/aggregate/index.js +1 -1
- package/dist/esm/features/session_replay/aggregate/index.js +83 -50
- package/dist/esm/features/utils/feature-base.js +1 -2
- package/dist/esm/features/utils/instrument-base.js +1 -0
- package/dist/esm/loaders/api/api.js +2 -2
- package/dist/esm/loaders/api/apiAsync.js +1 -36
- package/dist/esm/loaders/configure/configure.js +1 -1
- package/dist/esm/loaders/configure/public-path.js +6 -3
- 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/common/session/session-entity.d.ts +5 -0
- package/dist/types/common/session/session-entity.d.ts.map +1 -1
- package/dist/types/features/session_replay/aggregate/index.d.ts +11 -14
- package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/utils/feature-base.d.ts.map +1 -1
- package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
- package/dist/types/loaders/api/api.d.ts.map +1 -1
- package/dist/types/loaders/api/apiAsync.d.ts.map +1 -1
- package/dist/types/loaders/configure/public-path.d.ts +1 -1
- package/dist/types/loaders/configure/public-path.d.ts.map +1 -1
- package/package.json +2 -2
- 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 +33 -4
- package/src/common/dom/query-selector.js +9 -0
- package/src/common/session/session-entity.js +20 -1
- package/src/common/wrap/wrap-function.js +1 -1
- package/src/features/ajax/aggregate/index.js +2 -2
- package/src/features/session_replay/aggregate/index.js +82 -34
- package/src/features/utils/feature-base.js +1 -2
- package/src/features/utils/instrument-base.js +1 -0
- package/src/loaders/api/api.js +1 -2
- package/src/loaders/api/apiAsync.js +1 -39
- package/src/loaders/configure/configure.js +1 -1
- package/src/loaders/configure/public-path.js +6 -3
- package/src/common/aggregate/aggregator.test.js +0 -107
- package/src/common/config/state/configurable.test.js +0 -73
- package/src/common/config/state/info.test.js +0 -31
- package/src/common/config/state/init.test.js +0 -28
- package/src/common/config/state/loader-config.test.js +0 -21
- package/src/common/config/state/runtime.test.js +0 -21
- package/src/common/constants/env.cdn.test.js +0 -7
- package/src/common/constants/env.npm.test.js +0 -7
- package/src/common/constants/env.test.js +0 -7
- package/src/common/constants/runtime.test.js +0 -176
- package/src/common/deny-list/deny-list.test.js +0 -104
- package/src/common/drain/drain.test.js +0 -74
- package/src/common/event-emitter/contextual-ee.component-test.js +0 -293
- package/src/common/event-emitter/handle.test.js +0 -56
- package/src/common/event-emitter/register-handler.test.js +0 -61
- package/src/common/harvest/harvest-scheduler.test.js +0 -492
- package/src/common/harvest/harvest.test.js +0 -813
- package/src/common/ids/id.test.js +0 -92
- package/src/common/ids/unique-id.test.js +0 -58
- package/src/common/session/session-entity.component-test.js +0 -346
- package/src/common/storage/local-storage.test.js +0 -17
- package/src/common/timer/interaction-timer.component-test.js +0 -212
- package/src/common/timer/timer.test.js +0 -99
- package/src/common/timing/nav-timing.test.js +0 -161
- package/src/common/url/canonicalize-url.test.js +0 -45
- package/src/common/url/clean-url.test.js +0 -25
- package/src/common/url/encode.test.js +0 -81
- package/src/common/url/location.test.js +0 -15
- package/src/common/url/parse-url.test.js +0 -110
- package/src/common/url/protocol.test.js +0 -17
- package/src/common/util/console.test.js +0 -34
- package/src/common/util/data-size.test.js +0 -56
- package/src/common/util/feature-flags.test.js +0 -94
- package/src/common/util/get-or-set.test.js +0 -58
- package/src/common/util/invoke.test.js +0 -65
- package/src/common/util/map-own.test.js +0 -52
- package/src/common/util/obfuscate.component-test.js +0 -173
- package/src/common/util/stringify.test.js +0 -49
- package/src/common/util/submit-data.test.js +0 -183
- package/src/common/util/traverse.test.js +0 -50
- package/src/common/vitals/cumulative-layout-shift.test.js +0 -71
- package/src/common/vitals/first-contentful-paint.test.js +0 -124
- package/src/common/vitals/first-input-delay.test.js +0 -88
- package/src/common/vitals/first-paint.test.js +0 -127
- package/src/common/vitals/interaction-to-next-paint.test.js +0 -74
- package/src/common/vitals/largest-contentful-paint.test.js +0 -94
- package/src/common/vitals/long-task.test.js +0 -122
- package/src/common/vitals/time-to-first-byte.test.js +0 -147
- package/src/common/vitals/vital-metric.test.js +0 -171
- package/src/common/wrap/wrap-promise.component-test.js +0 -110
- package/src/features/ajax/instrument/distributed-tracing.test.js +0 -375
- package/src/features/jserrors/aggregate/canonical-function-name.test.js +0 -13
- package/src/features/jserrors/aggregate/compute-stack-trace.test.js +0 -414
- package/src/features/jserrors/aggregate/format-stack-trace.test.js +0 -39
- package/src/features/jserrors/aggregate/string-hash-code.test.js +0 -12
- package/src/features/metrics/aggregate/framework-detection.test.js +0 -332
- package/src/features/page_view_timing/aggregate/index.component-test.js +0 -86
- package/src/features/session_replay/aggregate/index.component-test.js +0 -317
- package/src/features/spa/aggregate/interaction-node.test.js +0 -17
- package/src/features/utils/agent-session.test.js +0 -194
- package/src/features/utils/aggregate-base.test.js +0 -123
- package/src/features/utils/feature-base.test.js +0 -45
- package/src/features/utils/handler-cache.test.js +0 -72
- package/src/features/utils/instrument-base.test.js +0 -216
- package/src/features/utils/lazy-feature-loader.test.js +0 -37
- package/src/loaders/api/api.component-test.js +0 -45
- package/src/loaders/api/api.test.js +0 -85
- package/src/loaders/api/apiAsync.test.js +0 -17
|
@@ -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 = {
|
|
16
|
+
mask_selector: '*',
|
|
14
17
|
block_selector: '[data-nr-block]',
|
|
15
18
|
mask_input_options: {
|
|
16
|
-
|
|
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,
|
|
@@ -91,8 +111,15 @@ const model = () => {
|
|
|
91
111
|
error_sampling_rate: 50,
|
|
92
112
|
// float from 0 - 100
|
|
93
113
|
// recording config settings
|
|
94
|
-
mask_text_selector: '*',
|
|
95
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
|
+
},
|
|
96
123
|
// these properties only have getters because they are enforcable constants and should error if someone tries to override them
|
|
97
124
|
get block_class() {
|
|
98
125
|
return 'nr-block';
|
|
@@ -109,17 +136,17 @@ const model = () => {
|
|
|
109
136
|
return hiddenState.block_selector;
|
|
110
137
|
},
|
|
111
138
|
set block_selector(val) {
|
|
112
|
-
hiddenState.block_selector += ",".concat(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);
|
|
113
140
|
},
|
|
114
141
|
// password: must always be present and true no matter what customer sets
|
|
115
142
|
get mask_input_options() {
|
|
116
143
|
return hiddenState.mask_input_options;
|
|
117
144
|
},
|
|
118
145
|
set mask_input_options(val) {
|
|
119
|
-
hiddenState.mask_input_options = {
|
|
146
|
+
if (val && typeof val === 'object') hiddenState.mask_input_options = {
|
|
120
147
|
...val,
|
|
121
148
|
password: true
|
|
122
|
-
};
|
|
149
|
+
};else (0, _console.warn)('An invalid session_replay.mask_input_option was provided and will not be used', val);
|
|
123
150
|
}
|
|
124
151
|
},
|
|
125
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;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.SessionEntity = exports.SESSION_EVENTS = exports.MODE = void 0;
|
|
6
|
+
exports.SessionEntity = exports.SESSION_EVENT_TYPES = exports.SESSION_EVENTS = exports.MODE = void 0;
|
|
7
7
|
var _uniqueId = require("../ids/unique-id");
|
|
8
8
|
var _console = require("../util/console");
|
|
9
9
|
var _stringify = require("../util/stringify");
|
|
@@ -17,6 +17,7 @@ var _configurable = require("../config/state/configurable");
|
|
|
17
17
|
var _handle = require("../event-emitter/handle");
|
|
18
18
|
var _constants2 = require("../../features/metrics/constants");
|
|
19
19
|
var _features = require("../../loaders/features/features");
|
|
20
|
+
var _eventListenerOpts = require("../event-listener/event-listener-opts");
|
|
20
21
|
const MODE = {
|
|
21
22
|
OFF: 0,
|
|
22
23
|
FULL: 1,
|
|
@@ -31,15 +32,22 @@ const model = {
|
|
|
31
32
|
expiresAt: 0,
|
|
32
33
|
updatedAt: Date.now(),
|
|
33
34
|
sessionReplay: MODE.OFF,
|
|
35
|
+
sessionReplaySentFirstChunk: false,
|
|
34
36
|
sessionTraceMode: MODE.OFF,
|
|
35
37
|
custom: {}
|
|
36
38
|
};
|
|
37
39
|
const SESSION_EVENTS = {
|
|
38
40
|
PAUSE: 'session-pause',
|
|
39
41
|
RESET: 'session-reset',
|
|
40
|
-
RESUME: 'session-resume'
|
|
42
|
+
RESUME: 'session-resume',
|
|
43
|
+
UPDATE: 'session-update'
|
|
41
44
|
};
|
|
42
45
|
exports.SESSION_EVENTS = SESSION_EVENTS;
|
|
46
|
+
const SESSION_EVENT_TYPES = {
|
|
47
|
+
SAME_TAB: 'same-tab',
|
|
48
|
+
CROSS_TAB: 'cross-tab'
|
|
49
|
+
};
|
|
50
|
+
exports.SESSION_EVENT_TYPES = SESSION_EVENT_TYPES;
|
|
43
51
|
class SessionEntity {
|
|
44
52
|
/**
|
|
45
53
|
* Create a self-managing Session Entity. This entity is scoped to the agent identifier which triggered it, allowing for multiple simultaneous session objects to exist.
|
|
@@ -65,6 +73,15 @@ class SessionEntity {
|
|
|
65
73
|
this.ee = _contextualEe.ee.get(agentIdentifier);
|
|
66
74
|
(0, _wrap.wrapEvents)(this.ee);
|
|
67
75
|
this.setup(opts);
|
|
76
|
+
if (_runtime.isBrowserScope) {
|
|
77
|
+
(0, _eventListenerOpts.windowAddEventListener)('storage', event => {
|
|
78
|
+
if (event.key === this.lookupKey) {
|
|
79
|
+
const obj = typeof event.newValue === 'string' ? JSON.parse(event.newValue) : event.newValue;
|
|
80
|
+
this.sync(obj);
|
|
81
|
+
this.ee.emit(SESSION_EVENTS.UPDATE, [SESSION_EVENT_TYPES.CROSS_TAB, this.state]);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
68
85
|
}
|
|
69
86
|
setup(_ref) {
|
|
70
87
|
let {
|
|
@@ -198,6 +215,7 @@ class SessionEntity {
|
|
|
198
215
|
//
|
|
199
216
|
// TODO - compression would need happen here if we decide to do it
|
|
200
217
|
this.storage.set(this.lookupKey, (0, _stringify.stringify)(this.state));
|
|
218
|
+
this.ee.emit(SESSION_EVENTS.UPDATE, [SESSION_EVENT_TYPES.SAME_TAB, this.state]);
|
|
201
219
|
return data;
|
|
202
220
|
} catch (e) {
|
|
203
221
|
// storage is inaccessible
|
|
@@ -218,5 +218,5 @@ function copy(from, to, emitter) {
|
|
|
218
218
|
* @returns {boolean} Whether the passed function is ineligible to be wrapped.
|
|
219
219
|
*/
|
|
220
220
|
function notWrappable(fn) {
|
|
221
|
-
return !(fn && fn
|
|
221
|
+
return !(fn && typeof fn === 'function' && fn.apply && !fn[flag]);
|
|
222
222
|
}
|
|
@@ -83,7 +83,6 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
83
83
|
} else {
|
|
84
84
|
hash = (0, _stringify.stringify)([params.status, params.host, params.pathname]);
|
|
85
85
|
}
|
|
86
|
-
(0, _handle.handle)('bstXhrAgg', ['xhr', hash, params, metrics], undefined, _features.FEATURE_NAMES.sessionTrace, ee);
|
|
87
86
|
|
|
88
87
|
// store as metric
|
|
89
88
|
aggregator.store('xhr', hash, params, metrics);
|
|
@@ -97,6 +96,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
97
96
|
}
|
|
98
97
|
return;
|
|
99
98
|
}
|
|
99
|
+
(0, _handle.handle)('bstXhrAgg', ['xhr', hash, params, metrics], undefined, _features.FEATURE_NAMES.sessionTrace, ee);
|
|
100
100
|
var xhrContext = this;
|
|
101
101
|
var event = {
|
|
102
102
|
method: params.method,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.RRWEB_VERSION = exports.MAX_PAYLOAD_SIZE = exports.IDEAL_PAYLOAD_SIZE = exports.Aggregate = exports.AVG_COMPRESSION = void 0;
|
|
6
|
+
exports.RRWEB_VERSION = exports.RRWEB_EVENT_TYPES = exports.MAX_PAYLOAD_SIZE = exports.IDEAL_PAYLOAD_SIZE = exports.Aggregate = exports.AVG_COMPRESSION = void 0;
|
|
7
7
|
var _registerHandler = require("../../../common/event-emitter/register-handler");
|
|
8
8
|
var _harvestScheduler = require("../../../common/harvest/harvest-scheduler");
|
|
9
9
|
var _constants = require("../constants");
|
|
@@ -15,6 +15,7 @@ var _sharedChannel = require("../../../common/constants/shared-channel");
|
|
|
15
15
|
var _encode = require("../../../common/url/encode");
|
|
16
16
|
var _console = require("../../../common/util/console");
|
|
17
17
|
var _runtime = require("../../../common/constants/runtime");
|
|
18
|
+
var _constants2 = require("../../metrics/constants");
|
|
18
19
|
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); }
|
|
19
20
|
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; } /*
|
|
20
21
|
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
@@ -31,6 +32,33 @@ const RRWEB_VERSION = '2.0.0-alpha.8';
|
|
|
31
32
|
exports.RRWEB_VERSION = RRWEB_VERSION;
|
|
32
33
|
const AVG_COMPRESSION = 0.12;
|
|
33
34
|
exports.AVG_COMPRESSION = AVG_COMPRESSION;
|
|
35
|
+
const RRWEB_EVENT_TYPES = {
|
|
36
|
+
DomContentLoaded: 0,
|
|
37
|
+
Load: 1,
|
|
38
|
+
FullSnapshot: 2,
|
|
39
|
+
IncrementalSnapshot: 3,
|
|
40
|
+
Meta: 4,
|
|
41
|
+
Custom: 5
|
|
42
|
+
};
|
|
43
|
+
exports.RRWEB_EVENT_TYPES = RRWEB_EVENT_TYPES;
|
|
44
|
+
const ABORT_REASONS = {
|
|
45
|
+
RESET: {
|
|
46
|
+
message: 'Session was reset',
|
|
47
|
+
sm: 'Reset'
|
|
48
|
+
},
|
|
49
|
+
IMPORT: {
|
|
50
|
+
message: 'Recorder failed to import',
|
|
51
|
+
sm: 'Import'
|
|
52
|
+
},
|
|
53
|
+
TOO_MANY: {
|
|
54
|
+
message: '429: Too Many Requests',
|
|
55
|
+
sm: 'Too-Many'
|
|
56
|
+
},
|
|
57
|
+
TOO_BIG: {
|
|
58
|
+
message: 'Payload was too large',
|
|
59
|
+
sm: 'Too-Big'
|
|
60
|
+
}
|
|
61
|
+
};
|
|
34
62
|
let recorder, gzipper, u8;
|
|
35
63
|
|
|
36
64
|
/** Vortex caps payload sizes at 1MB */
|
|
@@ -66,8 +94,6 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
66
94
|
/** can shut off efforts to compress the data */
|
|
67
95
|
this.shouldCompress = true;
|
|
68
96
|
|
|
69
|
-
/** Payload metadata -- Should indicate that the payload being sent is the first of a session */
|
|
70
|
-
this.isFirstChunk = false;
|
|
71
97
|
/** Payload metadata -- Should indicate that the payload being sent has a full DOM snapshot. This can happen
|
|
72
98
|
* -- When the recording library begins recording, it starts by taking a DOM snapshot
|
|
73
99
|
* -- When visibility changes from "hidden" -> "visible", it must capture a full snapshot for the replay to work correctly across tabs
|
|
@@ -81,16 +107,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
81
107
|
/** Payload metadata -- Should indicate when a replay blob started recording. Resets each time a harvest occurs.
|
|
82
108
|
* cycle timestamps are used as fallbacks if event timestamps cannot be used
|
|
83
109
|
*/
|
|
84
|
-
this.
|
|
85
|
-
event: {
|
|
86
|
-
first: undefined,
|
|
87
|
-
last: undefined
|
|
88
|
-
},
|
|
89
|
-
cycle: {
|
|
90
|
-
first: undefined,
|
|
91
|
-
last: undefined
|
|
92
|
-
}
|
|
93
|
-
};
|
|
110
|
+
this.cycleTimestamp = undefined;
|
|
94
111
|
|
|
95
112
|
/** A value which increments with every new mutation node reported. Resets after a harvest is sent */
|
|
96
113
|
this.payloadBytesEstimation = 0;
|
|
@@ -104,7 +121,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
104
121
|
if (shouldSetup) {
|
|
105
122
|
// 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.
|
|
106
123
|
this.ee.on(_sessionEntity.SESSION_EVENTS.RESET, () => {
|
|
107
|
-
this.abort();
|
|
124
|
+
this.abort(ABORT_REASONS.RESET);
|
|
108
125
|
});
|
|
109
126
|
|
|
110
127
|
// The SessionEntity class can emit a message indicating the session was paused (visibility change). This feature must stop recording if that occurs.
|
|
@@ -113,9 +130,19 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
113
130
|
});
|
|
114
131
|
// The SessionEntity class can emit a message indicating the session was resumed (visibility change). This feature must start running again (if already running) if that occurs.
|
|
115
132
|
this.ee.on(_sessionEntity.SESSION_EVENTS.RESUME, () => {
|
|
133
|
+
// if the mode changed on a different tab, it needs to update this instance to match
|
|
134
|
+
const {
|
|
135
|
+
session
|
|
136
|
+
} = (0, _config.getRuntime)(this.agentIdentifier);
|
|
137
|
+
this.mode = session.state.sessionReplay;
|
|
116
138
|
if (!this.initialized || this.mode === _sessionEntity.MODE.OFF) return;
|
|
117
139
|
this.startRecording();
|
|
118
140
|
});
|
|
141
|
+
this.ee.on(_sessionEntity.SESSION_EVENTS.UPDATE, (type, data) => {
|
|
142
|
+
if (!this.initialized || this.blocked || type !== _sessionEntity.SESSION_EVENT_TYPES.CROSS_TAB) return;
|
|
143
|
+
if (this.mode !== _sessionEntity.MODE.OFF && data.sessionReplay === _sessionEntity.MODE.OFF) this.abort('Session Entity was set to OFF on another tab');
|
|
144
|
+
this.mode = data.sessionReplay;
|
|
145
|
+
});
|
|
119
146
|
|
|
120
147
|
// Bespoke logic for new endpoint. This will change as downstream dependencies become solidified.
|
|
121
148
|
this.scheduler = new _harvestScheduler.HarvestScheduler('browser/blobs', {
|
|
@@ -131,7 +158,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
131
158
|
this.hasError = true;
|
|
132
159
|
this.errorNoticed = true;
|
|
133
160
|
// run once
|
|
134
|
-
if (this.mode === _sessionEntity.MODE.ERROR) {
|
|
161
|
+
if (this.mode === _sessionEntity.MODE.ERROR && _runtime.globalScope?.document.visibilityState === 'visible') {
|
|
135
162
|
this.mode = _sessionEntity.MODE.FULL;
|
|
136
163
|
// if the error was noticed AFTER the recorder was already imported....
|
|
137
164
|
if (recorder && this.initialized) {
|
|
@@ -191,7 +218,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
191
218
|
// Do not change the webpackChunkName or it will break the webpack nrba-chunking plugin
|
|
192
219
|
recorder = (await Promise.resolve().then(() => _interopRequireWildcard(require( /* webpackChunkName: "recorder" */'rrweb')))).record;
|
|
193
220
|
} catch (err) {
|
|
194
|
-
return this.abort();
|
|
221
|
+
return this.abort(ABORT_REASONS.IMPORT);
|
|
195
222
|
}
|
|
196
223
|
|
|
197
224
|
// FULL mode records AND reports from the beginning, while ERROR mode only records (but does not report).
|
|
@@ -214,7 +241,6 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
214
241
|
this.shouldCompress = false;
|
|
215
242
|
}
|
|
216
243
|
this.startRecording();
|
|
217
|
-
this.isFirstChunk = !!session.isNew;
|
|
218
244
|
this.syncWithSessionManager({
|
|
219
245
|
sessionReplay: this.mode
|
|
220
246
|
});
|
|
@@ -229,14 +255,39 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
229
255
|
this.scheduler.opts.gzip = false;
|
|
230
256
|
}
|
|
231
257
|
// TODO -- Gracefully handle the buffer for retries.
|
|
258
|
+
const {
|
|
259
|
+
session
|
|
260
|
+
} = (0, _config.getRuntime)(this.agentIdentifier);
|
|
261
|
+
if (!session.state.sessionReplaySentFirstChunk) this.syncWithSessionManager({
|
|
262
|
+
sessionReplaySentFirstChunk: true
|
|
263
|
+
});
|
|
232
264
|
this.clearBuffer();
|
|
233
265
|
return [payload];
|
|
234
266
|
}
|
|
235
267
|
getHarvestContents() {
|
|
236
268
|
const agentRuntime = (0, _config.getRuntime)(this.agentIdentifier);
|
|
237
269
|
const info = (0, _config.getInfo)(this.agentIdentifier);
|
|
238
|
-
|
|
239
|
-
|
|
270
|
+
|
|
271
|
+
// do not let the last node be a meta node, since this NEEDS to precede a snapshot
|
|
272
|
+
// we will manually inject it later if we find a payload that is missing a meta node
|
|
273
|
+
const payloadEndsWithMeta = this.events[this.events.length - 1]?.type === RRWEB_EVENT_TYPES.Meta;
|
|
274
|
+
if (payloadEndsWithMeta) {
|
|
275
|
+
this.lastMeta = this.events[this.events.length - 1];
|
|
276
|
+
this.events = this.events.slice(0, this.events.length - 1);
|
|
277
|
+
this.hasMeta = !!this.events.find(x => x.type === RRWEB_EVENT_TYPES.Meta);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// do not let the first node be a full snapshot node, since this NEEDS to be preceded by a meta node
|
|
281
|
+
// we will manually inject it if this happens
|
|
282
|
+
const payloadStartsWithFullSnapshot = this.events[0]?.type === RRWEB_EVENT_TYPES.FullSnapshot;
|
|
283
|
+
if (payloadStartsWithFullSnapshot) {
|
|
284
|
+
this.hasMeta = true;
|
|
285
|
+
this.events.unshift(this.lastMeta);
|
|
286
|
+
}
|
|
287
|
+
const firstEventTimestamp = this.events[0]?.timestamp;
|
|
288
|
+
const lastEventTimestamp = this.events[this.events.length - 1]?.timestamp;
|
|
289
|
+
const firstTimestamp = firstEventTimestamp || this.cycleTimestamp;
|
|
290
|
+
const lastTimestamp = lastEventTimestamp || (0, _config.getRuntime)(this.agentIdentifier).offset + _runtime.globalScope.performance.now();
|
|
240
291
|
return {
|
|
241
292
|
qs: {
|
|
242
293
|
browser_monitoring_key: info.licenseKey,
|
|
@@ -250,12 +301,13 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
250
301
|
'replay.firstTimestamp': firstTimestamp,
|
|
251
302
|
'replay.lastTimestamp': lastTimestamp,
|
|
252
303
|
'replay.durationMs': lastTimestamp - firstTimestamp,
|
|
304
|
+
'replay.nodes': this.events.length,
|
|
253
305
|
agentVersion: agentRuntime.version,
|
|
254
306
|
session: agentRuntime.session.state.value,
|
|
255
307
|
hasMeta: this.hasMeta,
|
|
256
308
|
hasSnapshot: this.hasSnapshot,
|
|
257
309
|
hasError: this.hasError,
|
|
258
|
-
isFirstChunk:
|
|
310
|
+
isFirstChunk: agentRuntime.session.state.sessionReplaySentFirstChunk === false,
|
|
259
311
|
decompressedBytes: this.payloadBytesEstimation,
|
|
260
312
|
'nr.rrweb.version': RRWEB_VERSION
|
|
261
313
|
}, MAX_PAYLOAD_SIZE - this.payloadBytesEstimation).substring(1) // remove the leading '&'
|
|
@@ -267,7 +319,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
267
319
|
onHarvestFinished(result) {
|
|
268
320
|
// The mutual decision for now is to stop recording and clear buffers if ingest is experiencing 429 rate limiting
|
|
269
321
|
if (result.status === 429) {
|
|
270
|
-
this.abort();
|
|
322
|
+
this.abort(ABORT_REASONS.TOO_MANY);
|
|
271
323
|
}
|
|
272
324
|
if (this.blocked) this.scheduler.stopTimer(true);
|
|
273
325
|
}
|
|
@@ -275,7 +327,6 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
275
327
|
/** Clears the buffer (this.events), and resets all payload metadata properties */
|
|
276
328
|
clearBuffer() {
|
|
277
329
|
this.events = [];
|
|
278
|
-
this.isFirstChunk = false;
|
|
279
330
|
this.hasSnapshot = false;
|
|
280
331
|
this.hasMeta = false;
|
|
281
332
|
this.hasError = false;
|
|
@@ -287,7 +338,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
287
338
|
startRecording() {
|
|
288
339
|
if (!recorder) {
|
|
289
340
|
(0, _console.warn)('Recording library was never imported');
|
|
290
|
-
return this.abort();
|
|
341
|
+
return this.abort(ABORT_REASONS.IMPORT);
|
|
291
342
|
}
|
|
292
343
|
this.clearTimestamps();
|
|
293
344
|
// set the fallbacks as early as possible
|
|
@@ -323,7 +374,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
323
374
|
|
|
324
375
|
/** Store a payload in the buffer (this.events). This should be the callback to the recording lib noticing a mutation */
|
|
325
376
|
store(event, isCheckout) {
|
|
326
|
-
this.setTimestamps(
|
|
377
|
+
this.setTimestamps();
|
|
327
378
|
if (this.blocked) return;
|
|
328
379
|
const eventBytes = (0, _stringify.stringify)(event).length;
|
|
329
380
|
/** The estimated size of the payload after compression */
|
|
@@ -331,7 +382,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
331
382
|
// Vortex will block payloads at a certain size, we might as well not send.
|
|
332
383
|
if (payloadSize > MAX_PAYLOAD_SIZE) {
|
|
333
384
|
this.clearBuffer();
|
|
334
|
-
return this.abort();
|
|
385
|
+
return this.abort(ABORT_REASONS.TOO_BIG);
|
|
335
386
|
}
|
|
336
387
|
// Checkout events are flags by the recording lib that indicate a fullsnapshot was taken every n ms. These are important
|
|
337
388
|
// to help reconstruct the replay later and must be included. While waiting and buffering for errors to come through,
|
|
@@ -342,19 +393,13 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
342
393
|
}
|
|
343
394
|
|
|
344
395
|
// meta event
|
|
345
|
-
if (event.type ===
|
|
396
|
+
if (event.type === RRWEB_EVENT_TYPES.Meta) {
|
|
346
397
|
this.hasMeta = true;
|
|
347
398
|
this.lastMeta = event;
|
|
348
399
|
}
|
|
349
400
|
// snapshot event
|
|
350
|
-
if (event.type ===
|
|
401
|
+
if (event.type === RRWEB_EVENT_TYPES.FullSnapshot) {
|
|
351
402
|
this.hasSnapshot = true;
|
|
352
|
-
// small chance that the meta event got separated from its matching snapshot across payload harvests
|
|
353
|
-
// it needs to precede the snapshot, so shove it in first.
|
|
354
|
-
if (!this.hasMeta) {
|
|
355
|
-
this.events.push(this.lastMeta);
|
|
356
|
-
this.hasMeta = true;
|
|
357
|
-
}
|
|
358
403
|
}
|
|
359
404
|
this.events.push(event);
|
|
360
405
|
this.payloadBytesEstimation += eventBytes;
|
|
@@ -372,26 +417,12 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
372
417
|
if (!recorder) return;
|
|
373
418
|
recorder.takeFullSnapshot();
|
|
374
419
|
}
|
|
375
|
-
setTimestamps(
|
|
420
|
+
setTimestamps() {
|
|
376
421
|
// fallbacks if timestamps cannot be derived from rrweb events
|
|
377
|
-
this.
|
|
378
|
-
if (!this.timestamp.cycle.first) this.timestamp.cycle.first = this.timestamp.cycle.last;
|
|
379
|
-
// timestamps based on rrweb events
|
|
380
|
-
if (!event || !event.timestamp) return;
|
|
381
|
-
if (!this.timestamp.event.first) this.timestamp.event.first = event.timestamp;
|
|
382
|
-
this.timestamp.event.last = event.timestamp;
|
|
422
|
+
if (!this.cycleTimestamp) this.cycleTimestamp = (0, _config.getRuntime)(this.agentIdentifier).offset + _runtime.globalScope.performance.now();
|
|
383
423
|
}
|
|
384
424
|
clearTimestamps() {
|
|
385
|
-
this.
|
|
386
|
-
event: {
|
|
387
|
-
first: undefined,
|
|
388
|
-
last: undefined
|
|
389
|
-
},
|
|
390
|
-
cycle: {
|
|
391
|
-
first: undefined,
|
|
392
|
-
last: undefined
|
|
393
|
-
}
|
|
394
|
-
};
|
|
425
|
+
this.cycleTimestamp = undefined;
|
|
395
426
|
}
|
|
396
427
|
|
|
397
428
|
/** Estimate the payload size */
|
|
@@ -403,6 +434,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
403
434
|
|
|
404
435
|
/** Abort the feature, once aborted it will not resume */
|
|
405
436
|
abort() {
|
|
437
|
+
let reason = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
438
|
+
(0, _console.warn)("SR aborted -- ".concat(reason.message));
|
|
439
|
+
this.ee.emit(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ["SessionReplay/Abort/".concat(ABORT_REASONS[reason.sm])]);
|
|
406
440
|
this.blocked = true;
|
|
407
441
|
this.mode = _sessionEntity.MODE.OFF;
|
|
408
442
|
this.stopRecording();
|
|
@@ -4,7 +4,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.FeatureBase = void 0;
|
|
7
|
-
var _config = require("../../common/config/config");
|
|
8
7
|
var _contextualEe = require("../../common/event-emitter/contextual-ee");
|
|
9
8
|
class FeatureBase {
|
|
10
9
|
constructor(agentIdentifier, aggregator, featureName) {
|
|
@@ -13,7 +12,7 @@ class FeatureBase {
|
|
|
13
12
|
/** @type {Aggregator} */
|
|
14
13
|
this.aggregator = aggregator;
|
|
15
14
|
/** @type {ContextualEE} */
|
|
16
|
-
this.ee = _contextualEe.ee.get(agentIdentifier
|
|
15
|
+
this.ee = _contextualEe.ee.get(agentIdentifier);
|
|
17
16
|
/** @type {string} */
|
|
18
17
|
this.featureName = featureName;
|
|
19
18
|
/**
|
|
@@ -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
|
};
|
|
@@ -26,7 +26,7 @@ const CUSTOM_ATTR_GROUP = 'CUSTOM/'; // the subgroup items should be stored unde
|
|
|
26
26
|
exports.CUSTOM_ATTR_GROUP = CUSTOM_ATTR_GROUP;
|
|
27
27
|
function setTopLevelCallers() {
|
|
28
28
|
const nr = (0, _nreum.gosCDN)();
|
|
29
|
-
const funcs = ['setErrorHandler', 'finished', 'addToTrace', '
|
|
29
|
+
const funcs = ['setErrorHandler', 'finished', 'addToTrace', 'addRelease', 'addPageAction', 'setCurrentRouteName', 'setPageViewName', 'setCustomAttribute', 'interaction', 'noticeError', 'setUserId', 'setApplicationVersion', 'start'];
|
|
30
30
|
funcs.forEach(f => {
|
|
31
31
|
nr[f] = function () {
|
|
32
32
|
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
@@ -53,7 +53,7 @@ function setAPI(agentIdentifier, forceDrain) {
|
|
|
53
53
|
const apiInterface = {};
|
|
54
54
|
var instanceEE = _contextualEe.ee.get(agentIdentifier);
|
|
55
55
|
var tracerEE = instanceEE.get('tracer');
|
|
56
|
-
var asyncApiFns = ['setErrorHandler', 'finished', 'addToTrace', '
|
|
56
|
+
var asyncApiFns = ['setErrorHandler', 'finished', 'addToTrace', 'addRelease'];
|
|
57
57
|
var prefix = 'api-';
|
|
58
58
|
var spaPrefix = prefix + 'ixn-';
|
|
59
59
|
|