@newrelic/browser-agent 1.255.0 → 1.256.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 +15 -0
- package/dist/cjs/common/constants/env.cdn.js +2 -2
- package/dist/cjs/common/constants/env.npm.js +2 -2
- package/dist/cjs/common/constants/runtime.js +1 -1
- package/dist/cjs/common/harvest/harvest.js +1 -0
- package/dist/cjs/common/session/session-entity.js +2 -1
- package/dist/cjs/common/timer/interaction-timer.js +16 -2
- package/dist/cjs/common/timing/time-keeper.js +1 -2
- package/dist/cjs/features/jserrors/aggregate/index.js +16 -6
- package/dist/cjs/features/jserrors/instrument/index.js +8 -3
- package/dist/cjs/features/session_replay/aggregate/index.js +48 -29
- package/dist/cjs/features/session_replay/constants.js +2 -1
- package/dist/cjs/features/session_replay/instrument/index.js +9 -2
- package/dist/cjs/features/session_replay/shared/recorder-events.js +1 -9
- package/dist/cjs/features/session_replay/shared/recorder.js +22 -50
- package/dist/cjs/features/session_replay/shared/utils.js +12 -0
- package/dist/cjs/features/session_trace/aggregate/index.js +19 -22
- package/dist/cjs/loaders/api/api.js +7 -1
- package/dist/cjs/loaders/configure/configure.js +1 -0
- package/dist/esm/common/constants/env.cdn.js +2 -2
- package/dist/esm/common/constants/env.npm.js +2 -2
- package/dist/esm/common/constants/runtime.js +1 -1
- package/dist/esm/common/harvest/harvest.js +1 -0
- package/dist/esm/common/session/session-entity.js +2 -1
- package/dist/esm/common/timer/interaction-timer.js +16 -2
- package/dist/esm/common/timing/time-keeper.js +1 -3
- package/dist/esm/features/jserrors/aggregate/index.js +16 -6
- package/dist/esm/features/jserrors/instrument/index.js +8 -3
- package/dist/esm/features/session_replay/aggregate/index.js +48 -29
- package/dist/esm/features/session_replay/constants.js +2 -1
- package/dist/esm/features/session_replay/instrument/index.js +9 -2
- package/dist/esm/features/session_replay/shared/recorder-events.js +1 -9
- package/dist/esm/features/session_replay/shared/recorder.js +23 -51
- package/dist/esm/features/session_replay/shared/utils.js +11 -0
- package/dist/esm/features/session_trace/aggregate/index.js +19 -22
- package/dist/esm/loaders/api/api.js +7 -1
- package/dist/esm/loaders/configure/configure.js +1 -0
- package/dist/types/common/constants/runtime.d.ts.map +1 -1
- package/dist/types/common/session/session-entity.d.ts.map +1 -1
- package/dist/types/common/timer/interaction-timer.d.ts +2 -0
- package/dist/types/common/timer/interaction-timer.d.ts.map +1 -1
- package/dist/types/common/timing/time-keeper.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/instrument/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/aggregate/index.d.ts +5 -2
- package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/constants.d.ts +1 -0
- package/dist/types/features/session_replay/constants.d.ts.map +1 -1
- package/dist/types/features/session_replay/instrument/index.d.ts +1 -0
- package/dist/types/features/session_replay/instrument/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/shared/recorder-events.d.ts +0 -8
- package/dist/types/features/session_replay/shared/recorder-events.d.ts.map +1 -1
- package/dist/types/features/session_replay/shared/recorder.d.ts +1 -17
- package/dist/types/features/session_replay/shared/recorder.d.ts.map +1 -1
- package/dist/types/features/session_replay/shared/utils.d.ts +8 -0
- package/dist/types/features/session_replay/shared/utils.d.ts.map +1 -1
- package/dist/types/features/session_trace/aggregate/index.d.ts +2 -1
- package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
- package/dist/types/loaders/api/api.d.ts.map +1 -1
- package/dist/types/loaders/configure/configure.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/common/constants/runtime.js +1 -1
- package/src/common/harvest/harvest.js +1 -1
- package/src/common/session/session-entity.js +2 -1
- package/src/common/timer/interaction-timer.js +17 -2
- package/src/common/timing/time-keeper.js +1 -3
- package/src/features/jserrors/aggregate/index.js +15 -6
- package/src/features/jserrors/instrument/index.js +9 -4
- package/src/features/session_replay/aggregate/index.js +43 -25
- package/src/features/session_replay/constants.js +2 -1
- package/src/features/session_replay/instrument/index.js +7 -2
- package/src/features/session_replay/shared/recorder-events.js +1 -6
- package/src/features/session_replay/shared/recorder.js +21 -27
- package/src/features/session_replay/shared/utils.js +12 -0
- package/src/features/session_trace/aggregate/index.js +18 -16
- package/src/loaders/api/api.js +10 -1
- package/src/loaders/configure/configure.js +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,21 @@
|
|
|
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.256.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.255.0...v1.256.0) (2024-04-11)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* Adjust Session Replay Error Tracking ([#951](https://github.com/newrelic/newrelic-browser-agent/issues/951)) ([91d65b5](https://github.com/newrelic/newrelic-browser-agent/commit/91d65b5b7b5b7e753a6603150fd4bb7d2543babd))
|
|
12
|
+
* Allow unmasking elements with nr-unmask selectors ([#949](https://github.com/newrelic/newrelic-browser-agent/issues/949)) ([e17aa25](https://github.com/newrelic/newrelic-browser-agent/commit/e17aa25ee098115ad23a5fb9ae268a4b5769fac1))
|
|
13
|
+
* Generate PTID in Agent ([#964](https://github.com/newrelic/newrelic-browser-agent/issues/964)) ([af7b676](https://github.com/newrelic/newrelic-browser-agent/commit/af7b6764f40cb1ddfb3ab2ca16d05d8e4f459f4e))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
* Resume Page Focus Now Checks Session State ([#961](https://github.com/newrelic/newrelic-browser-agent/issues/961)) ([e48af6b](https://github.com/newrelic/newrelic-browser-agent/commit/e48af6beb369daf6ddc8231daa040f0d9d204d5f))
|
|
19
|
+
* stabilize timestamp corrections ([#966](https://github.com/newrelic/newrelic-browser-agent/issues/966)) ([4fbe962](https://github.com/newrelic/newrelic-browser-agent/commit/4fbe962d7b268968df96da59058e2e53c527c5eb))
|
|
20
|
+
|
|
6
21
|
## [1.255.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.254.1...v1.255.0) (2024-04-04)
|
|
7
22
|
|
|
8
23
|
|
|
@@ -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.256.0";
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Exposes the build type of the agent
|
|
@@ -28,4 +28,4 @@ const DIST_METHOD = exports.DIST_METHOD = 'CDN';
|
|
|
28
28
|
/**
|
|
29
29
|
* Exposes the lib version of rrweb
|
|
30
30
|
*/
|
|
31
|
-
const RRWEB_VERSION = exports.RRWEB_VERSION = "2.0.0-alpha.
|
|
31
|
+
const RRWEB_VERSION = exports.RRWEB_VERSION = "2.0.0-alpha.12";
|
|
@@ -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.256.0";
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Exposes the build type of the agent
|
|
@@ -29,4 +29,4 @@ const DIST_METHOD = exports.DIST_METHOD = 'NPM';
|
|
|
29
29
|
/**
|
|
30
30
|
* Exposes the lib version of rrweb
|
|
31
31
|
*/
|
|
32
|
-
const RRWEB_VERSION = exports.RRWEB_VERSION = "2.0.0-alpha.
|
|
32
|
+
const RRWEB_VERSION = exports.RRWEB_VERSION = "2.0.0-alpha.12";
|
|
@@ -44,4 +44,4 @@ const ffVersion = exports.ffVersion = (() => {
|
|
|
44
44
|
const isIE = exports.isIE = Boolean(isBrowserScope && window.document.documentMode); // deprecated property that only works in IE
|
|
45
45
|
|
|
46
46
|
const supportsSendBeacon = exports.supportsSendBeacon = !!globalScope.navigator?.sendBeacon;
|
|
47
|
-
const offset = exports.offset = Math.floor(
|
|
47
|
+
const offset = exports.offset = Math.floor(Date.now() - performance.now());
|
|
@@ -127,7 +127,8 @@ class SessionEntity {
|
|
|
127
127
|
this.write((0, _configurable.getModeledObject)(this.state, model));
|
|
128
128
|
},
|
|
129
129
|
ee: this.ee,
|
|
130
|
-
refreshEvents: ['click', 'keydown', 'scroll']
|
|
130
|
+
refreshEvents: ['click', 'keydown', 'scroll'],
|
|
131
|
+
readStorage: () => this.storage.get(this.lookupKey)
|
|
131
132
|
}, this.state.inactiveAt - Date.now());
|
|
132
133
|
} else {
|
|
133
134
|
this.state.inactiveAt = Infinity;
|
|
@@ -15,6 +15,9 @@ class InteractionTimer extends _timer.Timer {
|
|
|
15
15
|
this.onRefresh = typeof opts.onRefresh === 'function' ? opts.onRefresh : () => {/* noop */};
|
|
16
16
|
this.onResume = typeof opts.onResume === 'function' ? opts.onResume : () => {/* noop */};
|
|
17
17
|
|
|
18
|
+
/** used to double-check LS state at resume time */
|
|
19
|
+
this.readStorage = opts.readStorage;
|
|
20
|
+
|
|
18
21
|
// used by pause/resume
|
|
19
22
|
this.remainingMs = undefined;
|
|
20
23
|
if (!opts.refreshEvents) opts.refreshEvents = ['click', 'keydown', 'scroll'];
|
|
@@ -68,8 +71,19 @@ class InteractionTimer extends _timer.Timer {
|
|
|
68
71
|
this.remainingMs = this.initialMs - (Date.now() - this.startTimestamp);
|
|
69
72
|
}
|
|
70
73
|
resume() {
|
|
71
|
-
|
|
72
|
-
|
|
74
|
+
try {
|
|
75
|
+
const lsData = this.readStorage();
|
|
76
|
+
const obj = typeof lsData === 'string' ? JSON.parse(lsData) : lsData;
|
|
77
|
+
if (isExpired(obj.expiresAt) || isExpired(obj.inactiveAt)) this.end();else {
|
|
78
|
+
this.refresh();
|
|
79
|
+
this.onResume(); // emit resume event after state updated
|
|
80
|
+
}
|
|
81
|
+
} catch (err) {
|
|
82
|
+
this.end();
|
|
83
|
+
}
|
|
84
|
+
function isExpired(timestamp) {
|
|
85
|
+
return Date.now() > timestamp;
|
|
86
|
+
}
|
|
73
87
|
}
|
|
74
88
|
refresh(cb, ms) {
|
|
75
89
|
this.clear();
|
|
@@ -4,7 +4,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.TimeKeeper = void 0;
|
|
7
|
-
var _runtime = require("../constants/runtime");
|
|
8
7
|
/**
|
|
9
8
|
* Class used to adjust the timestamp of harvested data to New Relic server time. This
|
|
10
9
|
* is done by tracking the performance timings of the RUM call and applying a calculation
|
|
@@ -37,7 +36,7 @@ class TimeKeeper {
|
|
|
37
36
|
*/
|
|
38
37
|
#ready = false;
|
|
39
38
|
constructor() {
|
|
40
|
-
this.#originTime =
|
|
39
|
+
this.#originTime = Date.now() - performance.now();
|
|
41
40
|
}
|
|
42
41
|
get ready() {
|
|
43
42
|
return this.#ready;
|
|
@@ -42,9 +42,13 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
42
42
|
this.bufferedErrorsUnderSpa = {};
|
|
43
43
|
this.currentBody = undefined;
|
|
44
44
|
this.errorOnPage = false;
|
|
45
|
+
this.replayAborted = false;
|
|
45
46
|
|
|
46
47
|
// this will need to change to match whatever ee we use in the instrument
|
|
47
48
|
this.ee.on('interactionDone', (interaction, wasSaved) => this.onInteractionDone(interaction, wasSaved));
|
|
49
|
+
this.ee.on('REPLAY_ABORTED', () => {
|
|
50
|
+
this.replayAborted = true;
|
|
51
|
+
});
|
|
48
52
|
(0, _registerHandler.registerHandler)('err', function () {
|
|
49
53
|
return _this.storeError(...arguments);
|
|
50
54
|
}, this.featureName, this.ee);
|
|
@@ -89,9 +93,16 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
89
93
|
if (releaseIds !== '{}') {
|
|
90
94
|
payload.qs.ri = releaseIds;
|
|
91
95
|
}
|
|
92
|
-
if (body && body.err && body.err.length
|
|
93
|
-
|
|
94
|
-
|
|
96
|
+
if (body && body.err && body.err.length) {
|
|
97
|
+
if (this.replayAborted) {
|
|
98
|
+
body.err.forEach(e => {
|
|
99
|
+
delete e.params?.hasReplay;
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
if (!this.errorOnPage) {
|
|
103
|
+
payload.qs.pve = '1';
|
|
104
|
+
this.errorOnPage = true;
|
|
105
|
+
}
|
|
95
106
|
}
|
|
96
107
|
return payload;
|
|
97
108
|
}
|
|
@@ -136,7 +147,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
136
147
|
}
|
|
137
148
|
return canonicalStackString;
|
|
138
149
|
}
|
|
139
|
-
storeError(err, time, internal, customAttributes) {
|
|
150
|
+
storeError(err, time, internal, customAttributes, hasReplay) {
|
|
140
151
|
// are we in an interaction
|
|
141
152
|
time = time || (0, _now.now)();
|
|
142
153
|
const agentRuntime = (0, _config.getRuntime)(this.agentIdentifier);
|
|
@@ -187,7 +198,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
187
198
|
params.pageview = 1;
|
|
188
199
|
this.pageviewReported[bucketHash] = true;
|
|
189
200
|
}
|
|
190
|
-
if (
|
|
201
|
+
if (hasReplay && !this.replayAborted) params.hasReplay = hasReplay;
|
|
191
202
|
params.firstOccurrenceTimestamp = this.observedAt[bucketHash];
|
|
192
203
|
params.timestamp = this.observedAt[bucketHash];
|
|
193
204
|
var type = internal ? 'ierr' : 'err';
|
|
@@ -198,7 +209,6 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
198
209
|
// Trace sends the error in its payload, and both trace & replay simply listens for any error to occur.
|
|
199
210
|
const jsErrorEvent = [type, bucketHash, params, newMetrics, customAttributes];
|
|
200
211
|
(0, _handle.handle)('errorAgg', jsErrorEvent, undefined, _features.FEATURE_NAMES.sessionTrace, this.ee);
|
|
201
|
-
(0, _handle.handle)('errorAgg', jsErrorEvent, undefined, _features.FEATURE_NAMES.sessionReplay, this.ee);
|
|
202
212
|
// still send EE events for other features such as above, but stop this one from aggregating internal data
|
|
203
213
|
if (this.blocked) return;
|
|
204
214
|
const softNavInUse = Boolean((0, _nreum.getNREUMInitializedAgent)(this.agentIdentifier)?.features[_features.FEATURE_NAMES.softNav]);
|
|
@@ -13,6 +13,7 @@ var _eventListenerOpts = require("../../../common/event-listener/event-listener-
|
|
|
13
13
|
var _stringify = require("../../../common/util/stringify");
|
|
14
14
|
var _uncaughtError = require("./uncaught-error");
|
|
15
15
|
var _now = require("../../../common/timing/now");
|
|
16
|
+
var _constants2 = require("../../session_replay/constants");
|
|
16
17
|
/*
|
|
17
18
|
* Copyright 2020 New Relic Corporation. All rights reserved.
|
|
18
19
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -21,6 +22,7 @@ var _now = require("../../../common/timing/now");
|
|
|
21
22
|
class Instrument extends _instrumentBase.InstrumentBase {
|
|
22
23
|
static featureName = _constants.FEATURE_NAME;
|
|
23
24
|
#seenErrors = new Set();
|
|
25
|
+
#replayRunning = false;
|
|
24
26
|
constructor(agentIdentifier, aggregator) {
|
|
25
27
|
let auto = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
|
|
26
28
|
super(agentIdentifier, aggregator, _constants.FEATURE_NAME, auto);
|
|
@@ -37,13 +39,16 @@ class Instrument extends _instrumentBase.InstrumentBase {
|
|
|
37
39
|
});
|
|
38
40
|
this.ee.on('internal-error', error => {
|
|
39
41
|
if (!this.abortHandler) return;
|
|
40
|
-
(0, _handle.handle)('ierr', [this.#castError(error), (0, _now.now)(), true], undefined, _features.FEATURE_NAMES.jserrors, this.ee);
|
|
42
|
+
(0, _handle.handle)('ierr', [this.#castError(error), (0, _now.now)(), true, {}, this.#replayRunning], undefined, _features.FEATURE_NAMES.jserrors, this.ee);
|
|
43
|
+
});
|
|
44
|
+
this.ee.on(_constants2.SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, isRunning => {
|
|
45
|
+
this.#replayRunning = isRunning;
|
|
41
46
|
});
|
|
42
47
|
_runtime.globalScope.addEventListener('unhandledrejection', promiseRejectionEvent => {
|
|
43
48
|
if (!this.abortHandler) return;
|
|
44
49
|
(0, _handle.handle)('err', [this.#castPromiseRejectionEvent(promiseRejectionEvent), (0, _now.now)(), false, {
|
|
45
50
|
unhandledPromiseRejection: 1
|
|
46
|
-
}], undefined, _features.FEATURE_NAMES.jserrors, this.ee);
|
|
51
|
+
}, this.#replayRunning], undefined, _features.FEATURE_NAMES.jserrors, this.ee);
|
|
47
52
|
}, (0, _eventListenerOpts.eventListenerOpts)(false, this.removeOnAbort?.signal));
|
|
48
53
|
_runtime.globalScope.addEventListener('error', errorEvent => {
|
|
49
54
|
if (!this.abortHandler) return;
|
|
@@ -56,7 +61,7 @@ class Instrument extends _instrumentBase.InstrumentBase {
|
|
|
56
61
|
this.#seenErrors.delete(errorEvent.error);
|
|
57
62
|
return;
|
|
58
63
|
}
|
|
59
|
-
(0, _handle.handle)('err', [this.#castErrorEvent(errorEvent), (0, _now.now)()], undefined, _features.FEATURE_NAMES.jserrors, this.ee);
|
|
64
|
+
(0, _handle.handle)('err', [this.#castErrorEvent(errorEvent), (0, _now.now)(), false, {}, this.#replayRunning], undefined, _features.FEATURE_NAMES.jserrors, this.ee);
|
|
60
65
|
}, (0, _eventListenerOpts.eventListenerOpts)(false, this.removeOnAbort?.signal));
|
|
61
66
|
this.abortHandler = this.#abort; // we also use this as a flag to denote that the feature is active or on and handling errors
|
|
62
67
|
this.importAggregator();
|
|
@@ -22,6 +22,7 @@ var _stringify = require("../../../common/util/stringify");
|
|
|
22
22
|
var _stylesheetEvaluator = require("../shared/stylesheet-evaluator");
|
|
23
23
|
var _drain = require("../../../common/drain/drain");
|
|
24
24
|
var _now = require("../../../common/timing/now");
|
|
25
|
+
var _utils = require("../shared/utils");
|
|
25
26
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
26
27
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } /*
|
|
27
28
|
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
@@ -35,6 +36,8 @@ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e;
|
|
|
35
36
|
*/
|
|
36
37
|
class Aggregate extends _aggregateBase.AggregateBase {
|
|
37
38
|
static featureName = _constants.FEATURE_NAME;
|
|
39
|
+
mode = _constants3.MODE.OFF;
|
|
40
|
+
|
|
38
41
|
// pass the recorder into the aggregator
|
|
39
42
|
constructor(agentIdentifier, aggregator, args) {
|
|
40
43
|
super(agentIdentifier, aggregator, _constants.FEATURE_NAME);
|
|
@@ -48,23 +51,18 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
48
51
|
this.gzipper = undefined;
|
|
49
52
|
/** populated with the u8 string lib async */
|
|
50
53
|
this.u8 = undefined;
|
|
51
|
-
/** the mode to start in. Defaults to off */
|
|
52
|
-
const {
|
|
53
|
-
session
|
|
54
|
-
} = (0, _config.getRuntime)(this.agentIdentifier);
|
|
55
|
-
this.mode = session.state.sessionReplayMode || _constants3.MODE.OFF;
|
|
56
54
|
|
|
57
55
|
/** set by BCS response */
|
|
58
56
|
this.entitled = false;
|
|
59
57
|
/** set at BCS response, stored in runtime */
|
|
60
58
|
this.timeKeeper = undefined;
|
|
61
59
|
this.recorder = args?.recorder;
|
|
62
|
-
|
|
60
|
+
this.preloaded = !!this.recorder;
|
|
61
|
+
this.errorNoticed = args?.errorNoticed || false;
|
|
63
62
|
(0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/Enabled'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
64
63
|
|
|
65
64
|
// 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.
|
|
66
65
|
this.ee.on(_constants3.SESSION_EVENTS.RESET, () => {
|
|
67
|
-
this.scheduler.runHarvest();
|
|
68
66
|
this.abort(_constants.ABORT_REASONS.RESET);
|
|
69
67
|
});
|
|
70
68
|
|
|
@@ -108,17 +106,6 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
108
106
|
(0, _registerHandler.registerHandler)(_constants.SR_EVENT_EMITTER_TYPES.PAUSE, () => {
|
|
109
107
|
this.forceStop(this.mode !== _constants3.MODE.ERROR);
|
|
110
108
|
}, this.featureName, this.ee);
|
|
111
|
-
|
|
112
|
-
// Wait for an error to be reported. This currently is wrapped around the "Error" feature. This is a feature-feature dependency.
|
|
113
|
-
// This was to ensure that all errors, including those on the page before load and those handled with "noticeError" are accounted for. Needs evalulation
|
|
114
|
-
(0, _registerHandler.registerHandler)('errorAgg', e => {
|
|
115
|
-
this.errorNoticed = true;
|
|
116
|
-
if (this.recorder) this.recorder.currentBufferTarget.hasError = true;
|
|
117
|
-
// run once
|
|
118
|
-
if (this.mode === _constants3.MODE.ERROR && _runtime.globalScope?.document.visibilityState === 'visible') {
|
|
119
|
-
this.switchToFull();
|
|
120
|
-
}
|
|
121
|
-
}, this.featureName, this.ee);
|
|
122
109
|
const {
|
|
123
110
|
error_sampling_rate,
|
|
124
111
|
sampling_rate,
|
|
@@ -159,6 +146,13 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
159
146
|
(0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/SamplingRate/Value', sampling_rate], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
160
147
|
(0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/ErrorSamplingRate/Value', error_sampling_rate], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
161
148
|
}
|
|
149
|
+
handleError(e) {
|
|
150
|
+
if (this.recorder) this.recorder.currentBufferTarget.hasError = true;
|
|
151
|
+
// run once
|
|
152
|
+
if (this.mode === _constants3.MODE.ERROR && _runtime.globalScope?.document.visibilityState === 'visible') {
|
|
153
|
+
this.switchToFull();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
162
156
|
switchToFull() {
|
|
163
157
|
this.mode = _constants3.MODE.FULL;
|
|
164
158
|
// if the error was noticed AFTER the recorder was already imported....
|
|
@@ -223,12 +217,13 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
223
217
|
} catch (err) {
|
|
224
218
|
return this.abort(_constants.ABORT_REASONS.IMPORT);
|
|
225
219
|
}
|
|
220
|
+
} else {
|
|
221
|
+
this.recorder.parent = this;
|
|
226
222
|
}
|
|
227
223
|
|
|
228
224
|
// If an error was noticed before the mode could be set (like in the early lifecycle of the page), immediately set to FULL mode
|
|
229
|
-
if (this.mode === _constants3.MODE.ERROR && this.errorNoticed)
|
|
230
|
-
|
|
231
|
-
}
|
|
225
|
+
if (this.mode === _constants3.MODE.ERROR && this.errorNoticed) this.mode = _constants3.MODE.FULL;
|
|
226
|
+
if (!this.preloaded) this.ee.on('err', e => this.handleError(e));
|
|
232
227
|
|
|
233
228
|
// FULL mode records AND reports from the beginning, while ERROR mode only records (but does not report).
|
|
234
229
|
// ERROR mode will do this until an error is thrown, and then switch into FULL mode.
|
|
@@ -269,21 +264,39 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
269
264
|
this.recorder.clearBuffer();
|
|
270
265
|
return;
|
|
271
266
|
}
|
|
267
|
+
(0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['SessionReplay/Harvest/Attempts'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
272
268
|
let len = 0;
|
|
273
269
|
if (!!this.gzipper && !!this.u8) {
|
|
274
|
-
payload.body = this.gzipper(this.u8("[".concat(payload.body.map(
|
|
275
|
-
|
|
276
|
-
|
|
270
|
+
payload.body = this.gzipper(this.u8("[".concat(payload.body.map(_ref2 => {
|
|
271
|
+
let {
|
|
272
|
+
__serialized,
|
|
273
|
+
...e
|
|
274
|
+
} = _ref2;
|
|
275
|
+
if (e.__newrelic && __serialized) return __serialized;
|
|
276
|
+
const output = {
|
|
277
|
+
...e
|
|
278
|
+
};
|
|
279
|
+
if (!output.__newrelic) {
|
|
280
|
+
output.__newrelic = (0, _utils.buildNRMetaNode)(e.timestamp, this.timeKeeper);
|
|
281
|
+
output.timestamp = this.timeKeeper.correctAbsoluteTimestamp(e.timestamp);
|
|
282
|
+
}
|
|
283
|
+
return (0, _stringify.stringify)(output);
|
|
277
284
|
}).join(','), "]")));
|
|
278
285
|
len = payload.body.length;
|
|
279
286
|
this.scheduler.opts.gzip = true;
|
|
280
287
|
} else {
|
|
281
|
-
payload.body = payload.body.map(
|
|
288
|
+
payload.body = payload.body.map(_ref3 => {
|
|
282
289
|
let {
|
|
283
290
|
__serialized,
|
|
284
291
|
...node
|
|
285
|
-
} =
|
|
286
|
-
return node;
|
|
292
|
+
} = _ref3;
|
|
293
|
+
if (node.__newrelic) return node;
|
|
294
|
+
const output = {
|
|
295
|
+
...node
|
|
296
|
+
};
|
|
297
|
+
output.__newrelic = (0, _utils.buildNRMetaNode)(node.timestamp, this.timeKeeper);
|
|
298
|
+
output.timestamp = this.timeKeeper.correctAbsoluteTimestamp(node.timestamp);
|
|
299
|
+
return output;
|
|
287
300
|
});
|
|
288
301
|
len = (0, _stringify.stringify)(payload.body).length;
|
|
289
302
|
this.scheduler.opts.gzip = false;
|
|
@@ -303,6 +316,11 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
303
316
|
if (recorderEvents.type === 'preloaded') this.scheduler.runHarvest(opts);
|
|
304
317
|
return [payload];
|
|
305
318
|
}
|
|
319
|
+
getCorrectedTimestamp(node) {
|
|
320
|
+
if (!node.timestamp) return;
|
|
321
|
+
if (node.__newrelic) return node.timestamp;
|
|
322
|
+
return this.timeKeeper.correctAbsoluteTimestamp(node.timestamp);
|
|
323
|
+
}
|
|
306
324
|
getHarvestContents(recorderEvents) {
|
|
307
325
|
recorderEvents ??= this.recorder.getEvents();
|
|
308
326
|
let events = recorderEvents.events;
|
|
@@ -328,8 +346,8 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
328
346
|
recorderEvents.hasMeta = !!events.find(x => x.type === _constants.RRWEB_EVENT_TYPES.Meta);
|
|
329
347
|
}
|
|
330
348
|
const relativeNow = (0, _now.now)();
|
|
331
|
-
const firstEventTimestamp = events[0]
|
|
332
|
-
const lastEventTimestamp = events[events.length - 1]
|
|
349
|
+
const firstEventTimestamp = this.getCorrectedTimestamp(events[0]); // from rrweb node
|
|
350
|
+
const lastEventTimestamp = this.getCorrectedTimestamp(events[events.length - 1]); // from rrweb node
|
|
333
351
|
const firstTimestamp = firstEventTimestamp || this.timeKeeper.correctAbsoluteTimestamp(recorderEvents.cycleTimestamp); // from rrweb node || from when the harvest cycle started
|
|
334
352
|
const lastTimestamp = lastEventTimestamp || this.timeKeeper.convertRelativeTimestamp(relativeNow);
|
|
335
353
|
const agentMetadata = agentRuntime.appMetadata?.agents?.[0] || {};
|
|
@@ -364,6 +382,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
364
382
|
invalidStylesheetsDetected: _stylesheetEvaluator.stylesheetEvaluator.invalidStylesheetsDetected,
|
|
365
383
|
inlinedAllStylesheets: recorderEvents.inlinedAllStylesheets,
|
|
366
384
|
'rrweb.version': _env.RRWEB_VERSION,
|
|
385
|
+
'payload.type': recorderEvents.type,
|
|
367
386
|
// customer-defined data should go last so that if it exceeds the query param padding limit it will be truncated instead of important attrs
|
|
368
387
|
...(endUserId && {
|
|
369
388
|
'enduser.id': endUserId
|
|
@@ -9,7 +9,8 @@ var _features = require("../../loaders/features/features");
|
|
|
9
9
|
const FEATURE_NAME = exports.FEATURE_NAME = _features.FEATURE_NAMES.sessionReplay;
|
|
10
10
|
const SR_EVENT_EMITTER_TYPES = exports.SR_EVENT_EMITTER_TYPES = {
|
|
11
11
|
RECORD: 'recordReplay',
|
|
12
|
-
PAUSE: 'pauseReplay'
|
|
12
|
+
PAUSE: 'pauseReplay',
|
|
13
|
+
REPLAY_RUNNING: 'replayRunning'
|
|
13
14
|
};
|
|
14
15
|
const AVG_COMPRESSION = exports.AVG_COMPRESSION = 0.12;
|
|
15
16
|
const RRWEB_EVENT_TYPES = exports.RRWEB_EVENT_TYPES = {
|
|
@@ -29,6 +29,11 @@ class Instrument extends _instrumentBase.InstrumentBase {
|
|
|
29
29
|
session = JSON.parse(localStorage.getItem("".concat(_constants.PREFIX, "_").concat(_constants.DEFAULT_KEY)));
|
|
30
30
|
} catch (err) {}
|
|
31
31
|
if (this.#canPreloadRecorder(session)) {
|
|
32
|
+
/** If this is preloaded, set up a buffer, if not, later when sampling we will set up a .on for live events */
|
|
33
|
+
this.ee.on('err', e => {
|
|
34
|
+
this.errorNoticed = true;
|
|
35
|
+
if (this.featAggregate) this.featAggregate.handleError();
|
|
36
|
+
});
|
|
32
37
|
this.#startRecording(session?.sessionReplayMode);
|
|
33
38
|
} else {
|
|
34
39
|
this.importAggregator();
|
|
@@ -55,12 +60,14 @@ class Instrument extends _instrumentBase.InstrumentBase {
|
|
|
55
60
|
} = await Promise.resolve().then(() => _interopRequireWildcard(require( /* webpackChunkName: "recorder" */'../shared/recorder')));
|
|
56
61
|
this.recorder = new Recorder({
|
|
57
62
|
mode,
|
|
58
|
-
agentIdentifier: this.agentIdentifier
|
|
63
|
+
agentIdentifier: this.agentIdentifier,
|
|
64
|
+
ee: this.ee
|
|
59
65
|
});
|
|
60
66
|
this.recorder.startRecording();
|
|
61
67
|
this.abortHandler = this.recorder.stopRecording;
|
|
62
68
|
this.importAggregator({
|
|
63
|
-
recorder: this.recorder
|
|
69
|
+
recorder: this.recorder,
|
|
70
|
+
errorNoticed: this.errorNoticed
|
|
64
71
|
});
|
|
65
72
|
}
|
|
66
73
|
}
|
|
@@ -5,21 +5,13 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.RecorderEvents = void 0;
|
|
7
7
|
class RecorderEvents {
|
|
8
|
-
constructor(
|
|
9
|
-
let {
|
|
10
|
-
canCorrectTimestamps
|
|
11
|
-
} = _ref;
|
|
8
|
+
constructor() {
|
|
12
9
|
/** The buffer to hold recorder event nodes */
|
|
13
10
|
this.events = [];
|
|
14
11
|
/** Payload metadata -- Should indicate when a replay blob started recording. Resets each time a harvest occurs.
|
|
15
12
|
* cycle timestamps are used as fallbacks if event timestamps cannot be used
|
|
16
13
|
*/
|
|
17
14
|
this.cycleTimestamp = Date.now();
|
|
18
|
-
/** Payload metadata -- Whether timestamps can be corrected, defaults as false, can be set to true if timekeeper is present at init time. Used to determine
|
|
19
|
-
* if harvest needs to re-loop through nodes and correct them before sending. Ideal behavior is to correct them as they flow into the recorder
|
|
20
|
-
* to prevent re-looping, but is not always possible since the timekeeper is not set until after page load and the recorder can be preloaded.
|
|
21
|
-
*/
|
|
22
|
-
this.canCorrectTimestamps = !!canCorrectTimestamps;
|
|
23
15
|
/** A value which increments with every new mutation node reported. Resets after a harvest is sent */
|
|
24
16
|
this.payloadBytesEstimation = 0;
|
|
25
17
|
/** Payload metadata -- Should indicate that the payload being sent has a full DOM snapshot. This can happen
|
|
@@ -14,6 +14,7 @@ var _stylesheetEvaluator = require("./stylesheet-evaluator");
|
|
|
14
14
|
var _handle = require("../../../common/event-emitter/handle");
|
|
15
15
|
var _constants3 = require("../../metrics/constants");
|
|
16
16
|
var _features = require("../../../loaders/features/features");
|
|
17
|
+
var _utils = require("./utils");
|
|
17
18
|
class Recorder {
|
|
18
19
|
/** Each page mutation or event will be stored (raw) in this array. This array will be cleared on each harvest */
|
|
19
20
|
#events;
|
|
@@ -24,15 +25,9 @@ class Recorder {
|
|
|
24
25
|
/** flag that if true, blocks events from being "stored". Only set to true when a full snapshot has incomplete nodes (only stylesheets ATM) */
|
|
25
26
|
#fixing = false;
|
|
26
27
|
constructor(parent) {
|
|
27
|
-
this.#events = new _recorderEvents.RecorderEvents(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
this.#backloggedEvents = new _recorderEvents.RecorderEvents({
|
|
31
|
-
canCorrectTimestamps: !!parent.timeKeeper?.ready
|
|
32
|
-
});
|
|
33
|
-
this.#preloaded = [new _recorderEvents.RecorderEvents({
|
|
34
|
-
canCorrectTimestamps: !!parent.timeKeeper?.ready
|
|
35
|
-
})];
|
|
28
|
+
this.#events = new _recorderEvents.RecorderEvents();
|
|
29
|
+
this.#backloggedEvents = new _recorderEvents.RecorderEvents();
|
|
30
|
+
this.#preloaded = [new _recorderEvents.RecorderEvents()];
|
|
36
31
|
/** True when actively recording, false when paused or stopped */
|
|
37
32
|
this.recording = false;
|
|
38
33
|
/** The pointer to the current bucket holding rrweb events */
|
|
@@ -49,18 +44,12 @@ class Recorder {
|
|
|
49
44
|
this.stopRecording = () => {/* no-op until set by rrweb initializer */};
|
|
50
45
|
}
|
|
51
46
|
getEvents() {
|
|
52
|
-
if (this.#preloaded[0]?.events.length) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
events: preloadedEvents,
|
|
57
|
-
type: 'preloaded'
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
const backloggedEvents = this.returnCorrectTimestamps(this.#backloggedEvents);
|
|
61
|
-
const events = this.returnCorrectTimestamps(this.#events);
|
|
47
|
+
if (this.#preloaded[0]?.events.length) return {
|
|
48
|
+
...this.#preloaded[0],
|
|
49
|
+
type: 'preloaded'
|
|
50
|
+
};
|
|
62
51
|
return {
|
|
63
|
-
events: [...backloggedEvents, ...events].filter(x => x),
|
|
52
|
+
events: [...this.#backloggedEvents.events, ...this.#events.events].filter(x => x),
|
|
64
53
|
type: 'standard',
|
|
65
54
|
cycleTimestamp: Math.min(this.#backloggedEvents.cycleTimestamp, this.#events.cycleTimestamp),
|
|
66
55
|
payloadBytesEstimation: this.#backloggedEvents.payloadBytesEstimation + this.#events.payloadBytesEstimation,
|
|
@@ -71,34 +60,10 @@ class Recorder {
|
|
|
71
60
|
};
|
|
72
61
|
}
|
|
73
62
|
|
|
74
|
-
/**
|
|
75
|
-
* Returns time-corrected events. If the events were correctable from the beginning, this correction will have already been applied.
|
|
76
|
-
* @param {SessionReplayEvent[]} events The array of buffered SR nodes
|
|
77
|
-
* @returns {CorrectedSessionReplayEvent[]}
|
|
78
|
-
*/
|
|
79
|
-
returnCorrectTimestamps(events) {
|
|
80
|
-
if (!this.parent.timeKeeper?.ready) return events.events;
|
|
81
|
-
return events.canCorrectTimestamps ? events.events : events.events.map(_ref => {
|
|
82
|
-
let {
|
|
83
|
-
__serialized,
|
|
84
|
-
timestamp,
|
|
85
|
-
...e
|
|
86
|
-
} = _ref;
|
|
87
|
-
return {
|
|
88
|
-
timestamp: this.parent.timeKeeper.correctAbsoluteTimestamp(timestamp),
|
|
89
|
-
...e
|
|
90
|
-
};
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
|
|
94
63
|
/** Clears the buffer (this.#events), and resets all payload metadata properties */
|
|
95
64
|
clearBuffer() {
|
|
96
|
-
if (this.#preloaded[0]?.events.length) this.#preloaded.shift();else if (this.parent.mode === _constants2.MODE.ERROR) this.#backloggedEvents = this.#events;else this.#backloggedEvents = new _recorderEvents.RecorderEvents(
|
|
97
|
-
|
|
98
|
-
});
|
|
99
|
-
this.#events = new _recorderEvents.RecorderEvents({
|
|
100
|
-
canCorrectTimestamps: !!this.parent.timeKeeper?.ready
|
|
101
|
-
});
|
|
65
|
+
if (this.#preloaded[0]?.events.length) this.#preloaded.shift();else if (this.parent.mode === _constants2.MODE.ERROR) this.#backloggedEvents = this.#events;else this.#backloggedEvents = new _recorderEvents.RecorderEvents();
|
|
66
|
+
this.#events = new _recorderEvents.RecorderEvents();
|
|
102
67
|
}
|
|
103
68
|
|
|
104
69
|
/** Begin recording using configured recording lib */
|
|
@@ -116,6 +81,10 @@ class Recorder {
|
|
|
116
81
|
inline_images,
|
|
117
82
|
collect_fonts
|
|
118
83
|
} = (0, _config.getConfigurationValue)(this.parent.agentIdentifier, 'session_replay');
|
|
84
|
+
const customMasker = (text, element) => {
|
|
85
|
+
if (element?.type?.toLowerCase() !== 'password' && (element?.dataset.nrUnmask !== undefined || element?.classList.contains('nr-unmask'))) return text;
|
|
86
|
+
return '*'.repeat(text.length);
|
|
87
|
+
};
|
|
119
88
|
// set up rrweb configurations for maximum privacy --
|
|
120
89
|
// https://newrelic.atlassian.net/wiki/spaces/O11Y/pages/2792293280/2023+02+28+Browser+-+Session+Replay#Configuration-options
|
|
121
90
|
const stop = (0, _rrweb.record)({
|
|
@@ -126,14 +95,18 @@ class Recorder {
|
|
|
126
95
|
blockSelector: block_selector,
|
|
127
96
|
maskInputOptions: mask_input_options,
|
|
128
97
|
maskTextSelector: mask_text_selector,
|
|
98
|
+
maskTextFn: customMasker,
|
|
129
99
|
maskAllInputs: mask_all_inputs,
|
|
100
|
+
maskInputFn: customMasker,
|
|
130
101
|
inlineStylesheet: inline_stylesheet,
|
|
131
102
|
inlineImages: inline_images,
|
|
132
103
|
collectFonts: collect_fonts,
|
|
133
104
|
checkoutEveryNms: _constants.CHECKOUT_MS[this.parent.mode]
|
|
134
105
|
});
|
|
106
|
+
this.parent.ee.emit(_constants.SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, [true, this.parent.mode]);
|
|
135
107
|
this.stopRecording = () => {
|
|
136
108
|
this.recording = false;
|
|
109
|
+
this.parent.ee.emit(_constants.SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, [false, this.parent.mode]);
|
|
137
110
|
stop();
|
|
138
111
|
};
|
|
139
112
|
}
|
|
@@ -177,7 +150,8 @@ class Recorder {
|
|
|
177
150
|
if (!event) return;
|
|
178
151
|
if (!this.parent.scheduler && this.#preloaded.length) this.currentBufferTarget = this.#preloaded[this.#preloaded.length - 1];else this.currentBufferTarget = this.#events;
|
|
179
152
|
if (this.parent.blocked) return;
|
|
180
|
-
if (this.
|
|
153
|
+
if (this.parent.timeKeeper?.ready && !event.__newrelic) {
|
|
154
|
+
event.__newrelic = (0, _utils.buildNRMetaNode)(event.timestamp, this.parent.timeKeeper);
|
|
181
155
|
event.timestamp = this.parent.timeKeeper.correctAbsoluteTimestamp(event.timestamp);
|
|
182
156
|
}
|
|
183
157
|
event.__serialized = (0, _stringify.stringify)(event);
|
|
@@ -212,9 +186,7 @@ class Recorder {
|
|
|
212
186
|
this.parent.scheduler.runHarvest();
|
|
213
187
|
} else {
|
|
214
188
|
// we are still in "preload" and it triggered a "stop point". Make a new set, which will get pointed at on next cycle
|
|
215
|
-
this.#preloaded.push(new _recorderEvents.RecorderEvents(
|
|
216
|
-
canCorrectTimestamps: !!this.parent.timeKeeper?.ready
|
|
217
|
-
}));
|
|
189
|
+
this.#preloaded.push(new _recorderEvents.RecorderEvents());
|
|
218
190
|
}
|
|
219
191
|
}
|
|
220
192
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
+
exports.buildNRMetaNode = buildNRMetaNode;
|
|
6
7
|
exports.canImportReplayAgg = canImportReplayAgg;
|
|
7
8
|
exports.enableSessionTracking = void 0;
|
|
8
9
|
exports.isPreloadAllowed = isPreloadAllowed;
|
|
@@ -23,4 +24,15 @@ function isPreloadAllowed(agentId) {
|
|
|
23
24
|
function canImportReplayAgg(agentId, sessionMgr) {
|
|
24
25
|
if (!hasReplayPrerequisite(agentId)) return false;
|
|
25
26
|
return !!sessionMgr?.isNew || !!sessionMgr?.state.sessionReplayMode; // Session Replay should only try to run if already running from a previous page, or at the beginning of a session
|
|
27
|
+
}
|
|
28
|
+
function buildNRMetaNode(timestamp, timeKeeper) {
|
|
29
|
+
const correctedTimestamp = timeKeeper.correctAbsoluteTimestamp(timestamp);
|
|
30
|
+
return {
|
|
31
|
+
originalTimestamp: timestamp,
|
|
32
|
+
correctedTimestamp,
|
|
33
|
+
timestampDiff: timestamp - correctedTimestamp,
|
|
34
|
+
timeKeeperOriginTime: timeKeeper.originTime,
|
|
35
|
+
timeKeeperCorrectedOriginTime: timeKeeper.correctedOriginTime,
|
|
36
|
+
timeKeeperDiff: Math.floor(timeKeeper.originTime - timeKeeper.correctedOriginTime)
|
|
37
|
+
};
|
|
26
38
|
}
|