@newrelic/browser-agent 1.243.0 → 1.244.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 +22 -0
- package/dist/cjs/common/config/state/init.js +6 -0
- package/dist/cjs/common/constants/env.cdn.js +9 -3
- package/dist/cjs/common/constants/env.js +8 -2
- package/dist/cjs/common/constants/env.npm.js +9 -3
- package/dist/cjs/common/util/console.js +2 -2
- package/dist/cjs/features/session_replay/aggregate/index.js +41 -25
- package/dist/cjs/features/spa/aggregate/index.js +3 -2
- package/dist/cjs/loaders/agent-base.js +3 -3
- package/dist/cjs/loaders/agent.js +1 -1
- package/dist/esm/common/config/state/init.js +6 -0
- package/dist/esm/common/constants/env.cdn.js +7 -2
- package/dist/esm/common/constants/env.js +6 -1
- package/dist/esm/common/constants/env.npm.js +7 -2
- package/dist/esm/common/util/console.js +2 -2
- package/dist/esm/features/session_replay/aggregate/index.js +39 -23
- package/dist/esm/features/spa/aggregate/index.js +3 -2
- package/dist/esm/loaders/agent-base.js +3 -3
- package/dist/esm/loaders/agent.js +1 -1
- package/dist/types/common/config/state/init.d.ts.map +1 -1
- package/dist/types/common/constants/env.cdn.d.ts +4 -0
- package/dist/types/common/constants/env.cdn.d.ts.map +1 -1
- package/dist/types/common/constants/env.d.ts +4 -0
- package/dist/types/common/constants/env.d.ts.map +1 -1
- package/dist/types/common/constants/env.npm.d.ts +4 -0
- package/dist/types/common/constants/env.npm.d.ts.map +1 -1
- package/dist/types/common/util/console.d.ts +3 -3
- package/dist/types/common/util/console.d.ts.map +1 -1
- package/dist/types/features/session_replay/aggregate/index.d.ts +2 -1
- package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
- package/dist/types/loaders/agent-base.d.ts +2 -2
- package/dist/types/loaders/agent-base.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/common/config/state/init.js +3 -0
- package/src/common/constants/__mocks__/env.js +1 -0
- package/src/common/constants/env.cdn.js +5 -0
- package/src/common/constants/env.js +5 -0
- package/src/common/constants/env.npm.js +5 -0
- package/src/common/util/console.js +2 -2
- package/src/features/session_replay/aggregate/index.js +38 -24
- package/src/features/spa/aggregate/index.js +3 -2
- package/src/loaders/agent-base.js +3 -3
- package/src/loaders/agent.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,28 @@
|
|
|
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.244.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.243.1...v1.244.0) (2023-10-10)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* Add Session Replay configurations to collect inline assets ([#763](https://github.com/newrelic/newrelic-browser-agent/issues/763)) ([cef08dd](https://github.com/newrelic/newrelic-browser-agent/commit/cef08dd3f0cd99735dbc719e3c075fe83bbc6219))
|
|
12
|
+
* Bump rrweb to 2.0.0.11 and make constant dynamic at build time ([#770](https://github.com/newrelic/newrelic-browser-agent/issues/770)) ([9ea84cf](https://github.com/newrelic/newrelic-browser-agent/commit/9ea84cf247b31af544e2ea7ed0873241ff82eebc))
|
|
13
|
+
* Ensure 15 second minimum error buffer when possible ([#759](https://github.com/newrelic/newrelic-browser-agent/issues/759)) ([8506803](https://github.com/newrelic/newrelic-browser-agent/commit/8506803eaba27b7c603432f8ba0c909b677d3c3b))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
* Fix invalid timestamps ([#771](https://github.com/newrelic/newrelic-browser-agent/issues/771)) ([bc5a57c](https://github.com/newrelic/newrelic-browser-agent/commit/bc5a57c8c42c62c311e24b77f00dc1225a3b5873))
|
|
19
|
+
* Isolate browser interaction node ([#758](https://github.com/newrelic/newrelic-browser-agent/issues/758)) ([b9e8277](https://github.com/newrelic/newrelic-browser-agent/commit/b9e82773c67d710e0f6dc1f892908afc8be004cd))
|
|
20
|
+
|
|
21
|
+
## [1.243.1](https://github.com/newrelic/newrelic-browser-agent/compare/v1.243.0...v1.243.1) (2023-10-04)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Bug Fixes
|
|
25
|
+
|
|
26
|
+
* Improve Session Replay abort metric reliability ([#754](https://github.com/newrelic/newrelic-browser-agent/issues/754)) ([14f08ac](https://github.com/newrelic/newrelic-browser-agent/commit/14f08aca8bf1a610984fc2303604a04910f07db6))
|
|
27
|
+
|
|
6
28
|
## [1.243.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.242.0...v1.243.0) (2023-10-04)
|
|
7
29
|
|
|
8
30
|
|
|
@@ -110,6 +110,12 @@ const model = () => {
|
|
|
110
110
|
// float from 0 - 100
|
|
111
111
|
error_sampling_rate: 50,
|
|
112
112
|
// float from 0 - 100
|
|
113
|
+
collect_fonts: false,
|
|
114
|
+
// serialize fonts for collection without public asset url, this is currently broken in RRWeb -- https://github.com/rrweb-io/rrweb/issues/1304. When fixed, revisit with test cases
|
|
115
|
+
inline_images: false,
|
|
116
|
+
// serialize images for collection without public asset url
|
|
117
|
+
inline_stylesheet: true,
|
|
118
|
+
// serialize css for collection without public asset url
|
|
113
119
|
// recording config settings
|
|
114
120
|
mask_all_inputs: true,
|
|
115
121
|
// this has a getter/setter to facilitate validation of the selectors
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.VERSION = exports.DIST_METHOD = exports.BUILD_ENV = void 0;
|
|
6
|
+
exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_ENV = void 0;
|
|
7
7
|
/**
|
|
8
8
|
* @file This file exposes CDN build environment variables. These variables will
|
|
9
9
|
* be overridden with babel.
|
|
@@ -12,7 +12,7 @@ exports.VERSION = exports.DIST_METHOD = exports.BUILD_ENV = void 0;
|
|
|
12
12
|
/**
|
|
13
13
|
* Exposes the version of the agent
|
|
14
14
|
*/
|
|
15
|
-
const VERSION = "1.
|
|
15
|
+
const VERSION = "1.244.0";
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Exposes the build type of the agent
|
|
@@ -26,4 +26,10 @@ const BUILD_ENV = "CDN";
|
|
|
26
26
|
*/
|
|
27
27
|
exports.BUILD_ENV = BUILD_ENV;
|
|
28
28
|
const DIST_METHOD = 'CDN';
|
|
29
|
-
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Exposes the lib version of rrweb
|
|
32
|
+
*/
|
|
33
|
+
exports.DIST_METHOD = DIST_METHOD;
|
|
34
|
+
const RRWEB_VERSION = "2.0.0-alpha.11";
|
|
35
|
+
exports.RRWEB_VERSION = RRWEB_VERSION;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.VERSION = exports.DIST_METHOD = exports.BUILD_ENV = void 0;
|
|
6
|
+
exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_ENV = void 0;
|
|
7
7
|
var _package = _interopRequireDefault(require("../../../package.json"));
|
|
8
8
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
9
9
|
/**
|
|
@@ -29,4 +29,10 @@ const BUILD_ENV = 'NPM';
|
|
|
29
29
|
*/
|
|
30
30
|
exports.BUILD_ENV = BUILD_ENV;
|
|
31
31
|
const DIST_METHOD = 'NPM';
|
|
32
|
-
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Exposes the lib version of rrweb
|
|
35
|
+
*/
|
|
36
|
+
exports.DIST_METHOD = DIST_METHOD;
|
|
37
|
+
const RRWEB_VERSION = _package.default.dependencies.rrweb;
|
|
38
|
+
exports.RRWEB_VERSION = RRWEB_VERSION;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.VERSION = exports.DIST_METHOD = exports.BUILD_ENV = void 0;
|
|
6
|
+
exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_ENV = void 0;
|
|
7
7
|
/**
|
|
8
8
|
* @file This file exposes NPM build environment variables. These variables will
|
|
9
9
|
* be overridden with babel.
|
|
@@ -12,7 +12,7 @@ exports.VERSION = exports.DIST_METHOD = exports.BUILD_ENV = void 0;
|
|
|
12
12
|
/**
|
|
13
13
|
* Exposes the version of the agent
|
|
14
14
|
*/
|
|
15
|
-
const VERSION = "1.
|
|
15
|
+
const VERSION = "1.244.0";
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Exposes the build type of the agent
|
|
@@ -27,4 +27,10 @@ const BUILD_ENV = 'NPM';
|
|
|
27
27
|
*/
|
|
28
28
|
exports.BUILD_ENV = BUILD_ENV;
|
|
29
29
|
const DIST_METHOD = 'NPM';
|
|
30
|
-
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Exposes the lib version of rrweb
|
|
33
|
+
*/
|
|
34
|
+
exports.DIST_METHOD = DIST_METHOD;
|
|
35
|
+
const RRWEB_VERSION = "2.0.0-alpha.11";
|
|
36
|
+
exports.RRWEB_VERSION = RRWEB_VERSION;
|
|
@@ -6,8 +6,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.warn = warn;
|
|
7
7
|
/**
|
|
8
8
|
* A helper method to warn to the console with New Relic: decoration
|
|
9
|
-
* @param {string} message
|
|
10
|
-
* @param {*} secondary
|
|
9
|
+
* @param {string} message The primary message to warn
|
|
10
|
+
* @param {*} [secondary] Secondary data to include, usually an error or object
|
|
11
11
|
* @returns
|
|
12
12
|
*/
|
|
13
13
|
function warn(message, secondary) {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.
|
|
6
|
+
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");
|
|
@@ -16,6 +16,9 @@ var _encode = require("../../../common/url/encode");
|
|
|
16
16
|
var _console = require("../../../common/util/console");
|
|
17
17
|
var _runtime = require("../../../common/constants/runtime");
|
|
18
18
|
var _constants2 = require("../../metrics/constants");
|
|
19
|
+
var _handle = require("../../../common/event-emitter/handle");
|
|
20
|
+
var _features = require("../../../loaders/features/features");
|
|
21
|
+
var _env = require("../../../common/constants/env.npm");
|
|
19
22
|
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); }
|
|
20
23
|
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; } /*
|
|
21
24
|
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
@@ -27,9 +30,6 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj &&
|
|
|
27
30
|
* It is not production ready, and is not intended to be imported or implemented in any build of the browser agent until
|
|
28
31
|
* functionality is validated and a full user experience is curated.
|
|
29
32
|
*/
|
|
30
|
-
// would be better to get this dynamically in some way
|
|
31
|
-
const RRWEB_VERSION = '2.0.0-alpha.8';
|
|
32
|
-
exports.RRWEB_VERSION = RRWEB_VERSION;
|
|
33
33
|
const AVG_COMPRESSION = 0.12;
|
|
34
34
|
exports.AVG_COMPRESSION = AVG_COMPRESSION;
|
|
35
35
|
const RRWEB_EVENT_TYPES = {
|
|
@@ -57,6 +57,10 @@ const ABORT_REASONS = {
|
|
|
57
57
|
TOO_BIG: {
|
|
58
58
|
message: 'Payload was too large',
|
|
59
59
|
sm: 'Too-Big'
|
|
60
|
+
},
|
|
61
|
+
CROSS_TAB: {
|
|
62
|
+
message: 'Session Entity was set to OFF on another tab',
|
|
63
|
+
sm: 'Cross-Tab'
|
|
60
64
|
}
|
|
61
65
|
};
|
|
62
66
|
let recorder, gzipper, u8;
|
|
@@ -66,10 +70,10 @@ const MAX_PAYLOAD_SIZE = 1000000;
|
|
|
66
70
|
/** Unloading caps around 64kb */
|
|
67
71
|
exports.MAX_PAYLOAD_SIZE = MAX_PAYLOAD_SIZE;
|
|
68
72
|
const IDEAL_PAYLOAD_SIZE = 64000;
|
|
69
|
-
/** Interval between forcing new full snapshots --
|
|
73
|
+
/** Interval between forcing new full snapshots -- 15 seconds in error mode (x2), 5 minutes in full mode */
|
|
70
74
|
exports.IDEAL_PAYLOAD_SIZE = IDEAL_PAYLOAD_SIZE;
|
|
71
75
|
const CHECKOUT_MS = {
|
|
72
|
-
[_sessionEntity.MODE.ERROR]:
|
|
76
|
+
[_sessionEntity.MODE.ERROR]: 15000,
|
|
73
77
|
[_sessionEntity.MODE.FULL]: 300000,
|
|
74
78
|
[_sessionEntity.MODE.OFF]: 0
|
|
75
79
|
};
|
|
@@ -79,6 +83,8 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
79
83
|
super(agentIdentifier, aggregator, _constants.FEATURE_NAME);
|
|
80
84
|
/** Each page mutation or event will be stored (raw) in this array. This array will be cleared on each harvest */
|
|
81
85
|
this.events = [];
|
|
86
|
+
/** Backlog used for a 2-part sliding window to guarantee a 15-30s buffer window */
|
|
87
|
+
this.backloggedEvents = [];
|
|
82
88
|
/** The interval to harvest at. This gets overridden if the size of the payload exceeds certain thresholds */
|
|
83
89
|
this.harvestTimeSeconds = (0, _config.getConfigurationValue)(this.agentIdentifier, 'session_replay.harvestTimeSeconds') || 60;
|
|
84
90
|
/** Set once the recorder has fully initialized after flag checks and sampling */
|
|
@@ -140,7 +146,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
140
146
|
});
|
|
141
147
|
this.ee.on(_sessionEntity.SESSION_EVENTS.UPDATE, (type, data) => {
|
|
142
148
|
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(
|
|
149
|
+
if (this.mode !== _sessionEntity.MODE.OFF && data.sessionReplay === _sessionEntity.MODE.OFF) this.abort(ABORT_REASONS.CROSS_TAB);
|
|
144
150
|
this.mode = data.sessionReplay;
|
|
145
151
|
});
|
|
146
152
|
|
|
@@ -248,6 +254,10 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
248
254
|
prepareHarvest() {
|
|
249
255
|
if (this.events.length === 0 || this.mode !== _sessionEntity.MODE.FULL && !this.blocked) return;
|
|
250
256
|
const payload = this.getHarvestContents();
|
|
257
|
+
if (!payload.body.length) {
|
|
258
|
+
this.clearBuffer();
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
251
261
|
if (this.shouldCompress) {
|
|
252
262
|
payload.body = gzipper(u8((0, _stringify.stringify)(payload.body)));
|
|
253
263
|
this.scheduler.opts.gzip = true;
|
|
@@ -267,6 +277,16 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
267
277
|
getHarvestContents() {
|
|
268
278
|
const agentRuntime = (0, _config.getRuntime)(this.agentIdentifier);
|
|
269
279
|
const info = (0, _config.getInfo)(this.agentIdentifier);
|
|
280
|
+
if (this.backloggedEvents.length) this.events = [...this.backloggedEvents, ...this.events];
|
|
281
|
+
|
|
282
|
+
// do not let the first node be a full snapshot node, since this NEEDS to be preceded by a meta node
|
|
283
|
+
// we will manually inject it if this happens
|
|
284
|
+
const payloadStartsWithFullSnapshot = this.events[0]?.type === RRWEB_EVENT_TYPES.FullSnapshot;
|
|
285
|
+
if (payloadStartsWithFullSnapshot && !!this.lastMeta) {
|
|
286
|
+
this.hasMeta = true;
|
|
287
|
+
this.events.unshift(this.lastMeta); // --> pushed the meta from a previous payload into newer payload... but it still has old timestamps
|
|
288
|
+
this.lastMeta = undefined;
|
|
289
|
+
}
|
|
270
290
|
|
|
271
291
|
// do not let the last node be a meta node, since this NEEDS to precede a snapshot
|
|
272
292
|
// we will manually inject it later if we find a payload that is missing a meta node
|
|
@@ -276,16 +296,8 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
276
296
|
this.events = this.events.slice(0, this.events.length - 1);
|
|
277
297
|
this.hasMeta = !!this.events.find(x => x.type === RRWEB_EVENT_TYPES.Meta);
|
|
278
298
|
}
|
|
279
|
-
|
|
280
|
-
|
|
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;
|
|
299
|
+
const firstEventTimestamp = this.events[0]?.timestamp; // from rrweb node
|
|
300
|
+
const lastEventTimestamp = this.events[this.events.length - 1]?.timestamp; // from rrweb node
|
|
289
301
|
const firstTimestamp = firstEventTimestamp || this.cycleTimestamp;
|
|
290
302
|
const lastTimestamp = lastEventTimestamp || (0, _config.getRuntime)(this.agentIdentifier).offset + _runtime.globalScope.performance.now();
|
|
291
303
|
return {
|
|
@@ -309,7 +321,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
309
321
|
hasError: this.hasError,
|
|
310
322
|
isFirstChunk: agentRuntime.session.state.sessionReplaySentFirstChunk === false,
|
|
311
323
|
decompressedBytes: this.payloadBytesEstimation,
|
|
312
|
-
'nr.rrweb.version': RRWEB_VERSION
|
|
324
|
+
'nr.rrweb.version': _env.RRWEB_VERSION
|
|
313
325
|
}, MAX_PAYLOAD_SIZE - this.payloadBytesEstimation).substring(1) // remove the leading '&'
|
|
314
326
|
},
|
|
315
327
|
|
|
@@ -326,6 +338,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
326
338
|
|
|
327
339
|
/** Clears the buffer (this.events), and resets all payload metadata properties */
|
|
328
340
|
clearBuffer() {
|
|
341
|
+
if (this.mode === _sessionEntity.MODE.ERROR) this.backloggedEvents = this.events;else this.backloggedEvents = [];
|
|
329
342
|
this.events = [];
|
|
330
343
|
this.hasSnapshot = false;
|
|
331
344
|
this.hasMeta = false;
|
|
@@ -340,9 +353,6 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
340
353
|
(0, _console.warn)('Recording library was never imported');
|
|
341
354
|
return this.abort(ABORT_REASONS.IMPORT);
|
|
342
355
|
}
|
|
343
|
-
this.clearTimestamps();
|
|
344
|
-
// set the fallbacks as early as possible
|
|
345
|
-
this.setTimestamps();
|
|
346
356
|
this.recording = true;
|
|
347
357
|
const {
|
|
348
358
|
block_class,
|
|
@@ -351,7 +361,10 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
351
361
|
block_selector,
|
|
352
362
|
mask_input_options,
|
|
353
363
|
mask_text_selector,
|
|
354
|
-
mask_all_inputs
|
|
364
|
+
mask_all_inputs,
|
|
365
|
+
inline_images,
|
|
366
|
+
inline_stylesheet,
|
|
367
|
+
collect_fonts
|
|
355
368
|
} = (0, _config.getConfigurationValue)(this.agentIdentifier, 'session_replay');
|
|
356
369
|
// set up rrweb configurations for maximum privacy --
|
|
357
370
|
// https://newrelic.atlassian.net/wiki/spaces/O11Y/pages/2792293280/2023+02+28+Browser+-+Session+Replay#Configuration-options
|
|
@@ -364,6 +377,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
364
377
|
maskInputOptions: mask_input_options,
|
|
365
378
|
maskTextSelector: mask_text_selector,
|
|
366
379
|
maskAllInputs: mask_all_inputs,
|
|
380
|
+
inlineImages: inline_images,
|
|
381
|
+
inlineStylesheet: inline_stylesheet,
|
|
382
|
+
collectFonts: collect_fonts,
|
|
367
383
|
checkoutEveryNms: CHECKOUT_MS[this.mode]
|
|
368
384
|
});
|
|
369
385
|
this.stopRecording = () => {
|
|
@@ -387,7 +403,8 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
387
403
|
// Checkout events are flags by the recording lib that indicate a fullsnapshot was taken every n ms. These are important
|
|
388
404
|
// to help reconstruct the replay later and must be included. While waiting and buffering for errors to come through,
|
|
389
405
|
// each time we see a new checkout, we can drop the old data.
|
|
390
|
-
|
|
406
|
+
// we need to check for meta because rrweb will flag it as checkout twice, once for meta, then once for snapshot
|
|
407
|
+
if (this.mode === _sessionEntity.MODE.ERROR && isCheckout && event.type === RRWEB_EVENT_TYPES.Meta) {
|
|
391
408
|
// we are still waiting for an error to throw, so keep wiping the buffer over time
|
|
392
409
|
this.clearBuffer();
|
|
393
410
|
}
|
|
@@ -395,7 +412,6 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
395
412
|
// meta event
|
|
396
413
|
if (event.type === RRWEB_EVENT_TYPES.Meta) {
|
|
397
414
|
this.hasMeta = true;
|
|
398
|
-
this.lastMeta = event;
|
|
399
415
|
}
|
|
400
416
|
// snapshot event
|
|
401
417
|
if (event.type === RRWEB_EVENT_TYPES.FullSnapshot) {
|
|
@@ -436,7 +452,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
436
452
|
abort() {
|
|
437
453
|
let reason = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
438
454
|
(0, _console.warn)("SR aborted -- ".concat(reason.message));
|
|
439
|
-
|
|
455
|
+
(0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ["SessionReplay/Abort/".concat(reason.sm)], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
440
456
|
this.blocked = true;
|
|
441
457
|
this.mode = _sessionEntity.MODE.OFF;
|
|
442
458
|
this.stopRecording();
|
|
@@ -21,6 +21,7 @@ var _features = require("../../../loaders/features/features");
|
|
|
21
21
|
var _aggregateBase = require("../../utils/aggregate-base");
|
|
22
22
|
var _firstContentfulPaint = require("../../../common/vitals/first-contentful-paint");
|
|
23
23
|
var _firstPaint = require("../../../common/vitals/first-paint");
|
|
24
|
+
var _bundleId = require("../../../common/ids/bundle-id");
|
|
24
25
|
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); }
|
|
25
26
|
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; }
|
|
26
27
|
/*
|
|
@@ -182,7 +183,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
182
183
|
(0, _registerHandler.registerHandler)(FN_START, function (args, eventSource) {
|
|
183
184
|
var ev = args[0];
|
|
184
185
|
var evName = ev.type;
|
|
185
|
-
var eventNode = ev.
|
|
186
|
+
var eventNode = ev["__nrNode:".concat(_bundleId.bundleId)];
|
|
186
187
|
if (!state.pageLoaded && evName === 'load' && eventSource === window) {
|
|
187
188
|
state.pageLoaded = true;
|
|
188
189
|
// set to null so prevNode is set correctly
|
|
@@ -226,7 +227,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
226
227
|
}
|
|
227
228
|
}
|
|
228
229
|
}
|
|
229
|
-
ev.
|
|
230
|
+
ev["__nrNode:".concat(_bundleId.bundleId)] = state.currentNode;
|
|
230
231
|
}, this.featureName, eventsEE);
|
|
231
232
|
|
|
232
233
|
/**
|
|
@@ -15,7 +15,7 @@ class AgentBase {
|
|
|
15
15
|
* @param {object} [attributes] JSON object with one or more key/value pairs. For example: {key:"value"}. The key is reported as its own PageAction attribute with the specified values.
|
|
16
16
|
*/
|
|
17
17
|
addPageAction(name, attributes) {
|
|
18
|
-
(0, _console.warn)('Call to agent api addPageAction failed. The
|
|
18
|
+
(0, _console.warn)('Call to agent api addPageAction failed. The page action feature is not currently initialized.');
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/**
|
|
@@ -95,13 +95,13 @@ class AgentBase {
|
|
|
95
95
|
* @param {string} id The ID or version of this release; for example, a version number, build number from your CI environment, GitHub SHA, GUID, or a hash of the contents.
|
|
96
96
|
*/
|
|
97
97
|
addRelease(name, id) {
|
|
98
|
-
(0, _console.warn)('Call to agent api addRelease failed. The
|
|
98
|
+
(0, _console.warn)('Call to agent api addRelease failed. The js errors feature is not currently initialized.');
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
/**
|
|
102
102
|
* Starts a set of agent features if not running in "autoStart" mode
|
|
103
103
|
* {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/start/}
|
|
104
|
-
* @param {string|string[]
|
|
104
|
+
* @param {string|string[]} [featureNames] The name(s) of the features to start. If no name(s) are passed, all features will be started
|
|
105
105
|
*/
|
|
106
106
|
start(featureNames) {
|
|
107
107
|
(0, _console.warn)('Call to agent api addRelease failed. The agent is not currently initialized.');
|
|
@@ -112,7 +112,7 @@ class Agent extends _agentBase.AgentBase {
|
|
|
112
112
|
* If you are sending the same event object to New Relic as a PageAction, omit the TYPE attribute. (type is a string to describe what type of event you are marking inside of a session trace.) If included, it will override the event type and cause the PageAction event to be sent incorrectly. Instead, use the name attribute for event information.
|
|
113
113
|
*/
|
|
114
114
|
addToTrace(customAttributes) {
|
|
115
|
-
(0, _console.warn)('Call to agent api addToTrace failed. The
|
|
115
|
+
(0, _console.warn)('Call to agent api addToTrace failed. The session trace feature is not currently initialized.');
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
/**
|
|
@@ -102,6 +102,12 @@ const model = () => {
|
|
|
102
102
|
// float from 0 - 100
|
|
103
103
|
error_sampling_rate: 50,
|
|
104
104
|
// float from 0 - 100
|
|
105
|
+
collect_fonts: false,
|
|
106
|
+
// serialize fonts for collection without public asset url, this is currently broken in RRWeb -- https://github.com/rrweb-io/rrweb/issues/1304. When fixed, revisit with test cases
|
|
107
|
+
inline_images: false,
|
|
108
|
+
// serialize images for collection without public asset url
|
|
109
|
+
inline_stylesheet: true,
|
|
110
|
+
// serialize css for collection without public asset url
|
|
105
111
|
// recording config settings
|
|
106
112
|
mask_all_inputs: true,
|
|
107
113
|
// this has a getter/setter to facilitate validation of the selectors
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
/**
|
|
7
7
|
* Exposes the version of the agent
|
|
8
8
|
*/
|
|
9
|
-
export const VERSION = "1.
|
|
9
|
+
export const VERSION = "1.244.0";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Exposes the build type of the agent
|
|
@@ -17,4 +17,9 @@ export const BUILD_ENV = "CDN";
|
|
|
17
17
|
/**
|
|
18
18
|
* Exposes the distribution method of the agent
|
|
19
19
|
*/
|
|
20
|
-
export const DIST_METHOD = 'CDN';
|
|
20
|
+
export const DIST_METHOD = 'CDN';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Exposes the lib version of rrweb
|
|
24
|
+
*/
|
|
25
|
+
export const RRWEB_VERSION = "2.0.0-alpha.11";
|
|
@@ -20,4 +20,9 @@ export const BUILD_ENV = 'NPM';
|
|
|
20
20
|
/**
|
|
21
21
|
* Exposes the distribution method of the agent
|
|
22
22
|
*/
|
|
23
|
-
export const DIST_METHOD = 'NPM';
|
|
23
|
+
export const DIST_METHOD = 'NPM';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Exposes the lib version of rrweb
|
|
27
|
+
*/
|
|
28
|
+
export const RRWEB_VERSION = pkgJSON.dependencies.rrweb;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
/**
|
|
7
7
|
* Exposes the version of the agent
|
|
8
8
|
*/
|
|
9
|
-
export const VERSION = "1.
|
|
9
|
+
export const VERSION = "1.244.0";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Exposes the build type of the agent
|
|
@@ -18,4 +18,9 @@ export const BUILD_ENV = 'NPM';
|
|
|
18
18
|
* Exposes the distribution method of the agent
|
|
19
19
|
* Valid valuse are CDN, NPM
|
|
20
20
|
*/
|
|
21
|
-
export const DIST_METHOD = 'NPM';
|
|
21
|
+
export const DIST_METHOD = 'NPM';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Exposes the lib version of rrweb
|
|
25
|
+
*/
|
|
26
|
+
export const RRWEB_VERSION = "2.0.0-alpha.11";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A helper method to warn to the console with New Relic: decoration
|
|
3
|
-
* @param {string} message
|
|
4
|
-
* @param {*} secondary
|
|
3
|
+
* @param {string} message The primary message to warn
|
|
4
|
+
* @param {*} [secondary] Secondary data to include, usually an error or object
|
|
5
5
|
* @returns
|
|
6
6
|
*/
|
|
7
7
|
export function warn(message, secondary) {
|
|
@@ -22,9 +22,9 @@ import { obj as encodeObj } from '../../../common/url/encode';
|
|
|
22
22
|
import { warn } from '../../../common/util/console';
|
|
23
23
|
import { globalScope } from '../../../common/constants/runtime';
|
|
24
24
|
import { SUPPORTABILITY_METRIC_CHANNEL } from '../../metrics/constants';
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
import { handle } from '../../../common/event-emitter/handle';
|
|
26
|
+
import { FEATURE_NAMES } from '../../../loaders/features/features';
|
|
27
|
+
import { RRWEB_VERSION } from "../../../common/constants/env.npm";
|
|
28
28
|
export const AVG_COMPRESSION = 0.12;
|
|
29
29
|
export const RRWEB_EVENT_TYPES = {
|
|
30
30
|
DomContentLoaded: 0,
|
|
@@ -50,6 +50,10 @@ const ABORT_REASONS = {
|
|
|
50
50
|
TOO_BIG: {
|
|
51
51
|
message: 'Payload was too large',
|
|
52
52
|
sm: 'Too-Big'
|
|
53
|
+
},
|
|
54
|
+
CROSS_TAB: {
|
|
55
|
+
message: 'Session Entity was set to OFF on another tab',
|
|
56
|
+
sm: 'Cross-Tab'
|
|
53
57
|
}
|
|
54
58
|
};
|
|
55
59
|
let recorder, gzipper, u8;
|
|
@@ -58,9 +62,9 @@ let recorder, gzipper, u8;
|
|
|
58
62
|
export const MAX_PAYLOAD_SIZE = 1000000;
|
|
59
63
|
/** Unloading caps around 64kb */
|
|
60
64
|
export const IDEAL_PAYLOAD_SIZE = 64000;
|
|
61
|
-
/** Interval between forcing new full snapshots --
|
|
65
|
+
/** Interval between forcing new full snapshots -- 15 seconds in error mode (x2), 5 minutes in full mode */
|
|
62
66
|
const CHECKOUT_MS = {
|
|
63
|
-
[MODE.ERROR]:
|
|
67
|
+
[MODE.ERROR]: 15000,
|
|
64
68
|
[MODE.FULL]: 300000,
|
|
65
69
|
[MODE.OFF]: 0
|
|
66
70
|
};
|
|
@@ -70,6 +74,8 @@ export class Aggregate extends AggregateBase {
|
|
|
70
74
|
super(agentIdentifier, aggregator, FEATURE_NAME);
|
|
71
75
|
/** Each page mutation or event will be stored (raw) in this array. This array will be cleared on each harvest */
|
|
72
76
|
this.events = [];
|
|
77
|
+
/** Backlog used for a 2-part sliding window to guarantee a 15-30s buffer window */
|
|
78
|
+
this.backloggedEvents = [];
|
|
73
79
|
/** The interval to harvest at. This gets overridden if the size of the payload exceeds certain thresholds */
|
|
74
80
|
this.harvestTimeSeconds = getConfigurationValue(this.agentIdentifier, 'session_replay.harvestTimeSeconds') || 60;
|
|
75
81
|
/** Set once the recorder has fully initialized after flag checks and sampling */
|
|
@@ -131,7 +137,7 @@ export class Aggregate extends AggregateBase {
|
|
|
131
137
|
});
|
|
132
138
|
this.ee.on(SESSION_EVENTS.UPDATE, (type, data) => {
|
|
133
139
|
if (!this.initialized || this.blocked || type !== SESSION_EVENT_TYPES.CROSS_TAB) return;
|
|
134
|
-
if (this.mode !== MODE.OFF && data.sessionReplay === MODE.OFF) this.abort(
|
|
140
|
+
if (this.mode !== MODE.OFF && data.sessionReplay === MODE.OFF) this.abort(ABORT_REASONS.CROSS_TAB);
|
|
135
141
|
this.mode = data.sessionReplay;
|
|
136
142
|
});
|
|
137
143
|
|
|
@@ -239,6 +245,10 @@ export class Aggregate extends AggregateBase {
|
|
|
239
245
|
prepareHarvest() {
|
|
240
246
|
if (this.events.length === 0 || this.mode !== MODE.FULL && !this.blocked) return;
|
|
241
247
|
const payload = this.getHarvestContents();
|
|
248
|
+
if (!payload.body.length) {
|
|
249
|
+
this.clearBuffer();
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
242
252
|
if (this.shouldCompress) {
|
|
243
253
|
payload.body = gzipper(u8(stringify(payload.body)));
|
|
244
254
|
this.scheduler.opts.gzip = true;
|
|
@@ -258,6 +268,16 @@ export class Aggregate extends AggregateBase {
|
|
|
258
268
|
getHarvestContents() {
|
|
259
269
|
const agentRuntime = getRuntime(this.agentIdentifier);
|
|
260
270
|
const info = getInfo(this.agentIdentifier);
|
|
271
|
+
if (this.backloggedEvents.length) this.events = [...this.backloggedEvents, ...this.events];
|
|
272
|
+
|
|
273
|
+
// do not let the first node be a full snapshot node, since this NEEDS to be preceded by a meta node
|
|
274
|
+
// we will manually inject it if this happens
|
|
275
|
+
const payloadStartsWithFullSnapshot = this.events[0]?.type === RRWEB_EVENT_TYPES.FullSnapshot;
|
|
276
|
+
if (payloadStartsWithFullSnapshot && !!this.lastMeta) {
|
|
277
|
+
this.hasMeta = true;
|
|
278
|
+
this.events.unshift(this.lastMeta); // --> pushed the meta from a previous payload into newer payload... but it still has old timestamps
|
|
279
|
+
this.lastMeta = undefined;
|
|
280
|
+
}
|
|
261
281
|
|
|
262
282
|
// do not let the last node be a meta node, since this NEEDS to precede a snapshot
|
|
263
283
|
// we will manually inject it later if we find a payload that is missing a meta node
|
|
@@ -267,16 +287,8 @@ export class Aggregate extends AggregateBase {
|
|
|
267
287
|
this.events = this.events.slice(0, this.events.length - 1);
|
|
268
288
|
this.hasMeta = !!this.events.find(x => x.type === RRWEB_EVENT_TYPES.Meta);
|
|
269
289
|
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
// we will manually inject it if this happens
|
|
273
|
-
const payloadStartsWithFullSnapshot = this.events[0]?.type === RRWEB_EVENT_TYPES.FullSnapshot;
|
|
274
|
-
if (payloadStartsWithFullSnapshot) {
|
|
275
|
-
this.hasMeta = true;
|
|
276
|
-
this.events.unshift(this.lastMeta);
|
|
277
|
-
}
|
|
278
|
-
const firstEventTimestamp = this.events[0]?.timestamp;
|
|
279
|
-
const lastEventTimestamp = this.events[this.events.length - 1]?.timestamp;
|
|
290
|
+
const firstEventTimestamp = this.events[0]?.timestamp; // from rrweb node
|
|
291
|
+
const lastEventTimestamp = this.events[this.events.length - 1]?.timestamp; // from rrweb node
|
|
280
292
|
const firstTimestamp = firstEventTimestamp || this.cycleTimestamp;
|
|
281
293
|
const lastTimestamp = lastEventTimestamp || getRuntime(this.agentIdentifier).offset + globalScope.performance.now();
|
|
282
294
|
return {
|
|
@@ -317,6 +329,7 @@ export class Aggregate extends AggregateBase {
|
|
|
317
329
|
|
|
318
330
|
/** Clears the buffer (this.events), and resets all payload metadata properties */
|
|
319
331
|
clearBuffer() {
|
|
332
|
+
if (this.mode === MODE.ERROR) this.backloggedEvents = this.events;else this.backloggedEvents = [];
|
|
320
333
|
this.events = [];
|
|
321
334
|
this.hasSnapshot = false;
|
|
322
335
|
this.hasMeta = false;
|
|
@@ -331,9 +344,6 @@ export class Aggregate extends AggregateBase {
|
|
|
331
344
|
warn('Recording library was never imported');
|
|
332
345
|
return this.abort(ABORT_REASONS.IMPORT);
|
|
333
346
|
}
|
|
334
|
-
this.clearTimestamps();
|
|
335
|
-
// set the fallbacks as early as possible
|
|
336
|
-
this.setTimestamps();
|
|
337
347
|
this.recording = true;
|
|
338
348
|
const {
|
|
339
349
|
block_class,
|
|
@@ -342,7 +352,10 @@ export class Aggregate extends AggregateBase {
|
|
|
342
352
|
block_selector,
|
|
343
353
|
mask_input_options,
|
|
344
354
|
mask_text_selector,
|
|
345
|
-
mask_all_inputs
|
|
355
|
+
mask_all_inputs,
|
|
356
|
+
inline_images,
|
|
357
|
+
inline_stylesheet,
|
|
358
|
+
collect_fonts
|
|
346
359
|
} = getConfigurationValue(this.agentIdentifier, 'session_replay');
|
|
347
360
|
// set up rrweb configurations for maximum privacy --
|
|
348
361
|
// https://newrelic.atlassian.net/wiki/spaces/O11Y/pages/2792293280/2023+02+28+Browser+-+Session+Replay#Configuration-options
|
|
@@ -355,6 +368,9 @@ export class Aggregate extends AggregateBase {
|
|
|
355
368
|
maskInputOptions: mask_input_options,
|
|
356
369
|
maskTextSelector: mask_text_selector,
|
|
357
370
|
maskAllInputs: mask_all_inputs,
|
|
371
|
+
inlineImages: inline_images,
|
|
372
|
+
inlineStylesheet: inline_stylesheet,
|
|
373
|
+
collectFonts: collect_fonts,
|
|
358
374
|
checkoutEveryNms: CHECKOUT_MS[this.mode]
|
|
359
375
|
});
|
|
360
376
|
this.stopRecording = () => {
|
|
@@ -378,7 +394,8 @@ export class Aggregate extends AggregateBase {
|
|
|
378
394
|
// Checkout events are flags by the recording lib that indicate a fullsnapshot was taken every n ms. These are important
|
|
379
395
|
// to help reconstruct the replay later and must be included. While waiting and buffering for errors to come through,
|
|
380
396
|
// each time we see a new checkout, we can drop the old data.
|
|
381
|
-
|
|
397
|
+
// we need to check for meta because rrweb will flag it as checkout twice, once for meta, then once for snapshot
|
|
398
|
+
if (this.mode === MODE.ERROR && isCheckout && event.type === RRWEB_EVENT_TYPES.Meta) {
|
|
382
399
|
// we are still waiting for an error to throw, so keep wiping the buffer over time
|
|
383
400
|
this.clearBuffer();
|
|
384
401
|
}
|
|
@@ -386,7 +403,6 @@ export class Aggregate extends AggregateBase {
|
|
|
386
403
|
// meta event
|
|
387
404
|
if (event.type === RRWEB_EVENT_TYPES.Meta) {
|
|
388
405
|
this.hasMeta = true;
|
|
389
|
-
this.lastMeta = event;
|
|
390
406
|
}
|
|
391
407
|
// snapshot event
|
|
392
408
|
if (event.type === RRWEB_EVENT_TYPES.FullSnapshot) {
|
|
@@ -427,7 +443,7 @@ export class Aggregate extends AggregateBase {
|
|
|
427
443
|
abort() {
|
|
428
444
|
let reason = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
429
445
|
warn("SR aborted -- ".concat(reason.message));
|
|
430
|
-
|
|
446
|
+
handle(SUPPORTABILITY_METRIC_CHANNEL, ["SessionReplay/Abort/".concat(reason.sm)], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
431
447
|
this.blocked = true;
|
|
432
448
|
this.mode = MODE.OFF;
|
|
433
449
|
this.stopRecording();
|
|
@@ -20,6 +20,7 @@ import { FEATURE_NAMES } from '../../../loaders/features/features';
|
|
|
20
20
|
import { AggregateBase } from '../../utils/aggregate-base';
|
|
21
21
|
import { firstContentfulPaint } from '../../../common/vitals/first-contentful-paint';
|
|
22
22
|
import { firstPaint } from '../../../common/vitals/first-paint';
|
|
23
|
+
import { bundleId } from '../../../common/ids/bundle-id';
|
|
23
24
|
const {
|
|
24
25
|
FEATURE_NAME,
|
|
25
26
|
INTERACTION_EVENTS,
|
|
@@ -173,7 +174,7 @@ export class Aggregate extends AggregateBase {
|
|
|
173
174
|
register(FN_START, function (args, eventSource) {
|
|
174
175
|
var ev = args[0];
|
|
175
176
|
var evName = ev.type;
|
|
176
|
-
var eventNode = ev.
|
|
177
|
+
var eventNode = ev["__nrNode:".concat(bundleId)];
|
|
177
178
|
if (!state.pageLoaded && evName === 'load' && eventSource === window) {
|
|
178
179
|
state.pageLoaded = true;
|
|
179
180
|
// set to null so prevNode is set correctly
|
|
@@ -217,7 +218,7 @@ export class Aggregate extends AggregateBase {
|
|
|
217
218
|
}
|
|
218
219
|
}
|
|
219
220
|
}
|
|
220
|
-
ev.
|
|
221
|
+
ev["__nrNode:".concat(bundleId)] = state.currentNode;
|
|
221
222
|
}, this.featureName, eventsEE);
|
|
222
223
|
|
|
223
224
|
/**
|
|
@@ -9,7 +9,7 @@ export class AgentBase {
|
|
|
9
9
|
* @param {object} [attributes] JSON object with one or more key/value pairs. For example: {key:"value"}. The key is reported as its own PageAction attribute with the specified values.
|
|
10
10
|
*/
|
|
11
11
|
addPageAction(name, attributes) {
|
|
12
|
-
warn('Call to agent api addPageAction failed. The
|
|
12
|
+
warn('Call to agent api addPageAction failed. The page action feature is not currently initialized.');
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
/**
|
|
@@ -89,13 +89,13 @@ export class AgentBase {
|
|
|
89
89
|
* @param {string} id The ID or version of this release; for example, a version number, build number from your CI environment, GitHub SHA, GUID, or a hash of the contents.
|
|
90
90
|
*/
|
|
91
91
|
addRelease(name, id) {
|
|
92
|
-
warn('Call to agent api addRelease failed. The
|
|
92
|
+
warn('Call to agent api addRelease failed. The js errors feature is not currently initialized.');
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
/**
|
|
96
96
|
* Starts a set of agent features if not running in "autoStart" mode
|
|
97
97
|
* {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/start/}
|
|
98
|
-
* @param {string|string[]
|
|
98
|
+
* @param {string|string[]} [featureNames] The name(s) of the features to start. If no name(s) are passed, all features will be started
|
|
99
99
|
*/
|
|
100
100
|
start(featureNames) {
|
|
101
101
|
warn('Call to agent api addRelease failed. The agent is not currently initialized.');
|
|
@@ -103,7 +103,7 @@ export class Agent extends AgentBase {
|
|
|
103
103
|
* If you are sending the same event object to New Relic as a PageAction, omit the TYPE attribute. (type is a string to describe what type of event you are marking inside of a session trace.) If included, it will override the event type and cause the PageAction event to be sent incorrectly. Instead, use the name attribute for event information.
|
|
104
104
|
*/
|
|
105
105
|
addToTrace(customAttributes) {
|
|
106
|
-
warn('Call to agent api addToTrace failed. The
|
|
106
|
+
warn('Call to agent api addToTrace failed. The session trace feature is not currently initialized.');
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../../src/common/config/state/init.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../../src/common/config/state/init.js"],"names":[],"mappings":"AA0GA,+CAIC;AAED,0DAIC;AAED,+DAYC"}
|
|
@@ -15,4 +15,8 @@ export const BUILD_ENV: string | undefined;
|
|
|
15
15
|
* Exposes the distribution method of the agent
|
|
16
16
|
*/
|
|
17
17
|
export const DIST_METHOD: "CDN";
|
|
18
|
+
/**
|
|
19
|
+
* Exposes the lib version of rrweb
|
|
20
|
+
*/
|
|
21
|
+
export const RRWEB_VERSION: string | undefined;
|
|
18
22
|
//# sourceMappingURL=env.cdn.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.cdn.d.ts","sourceRoot":"","sources":["../../../../src/common/constants/env.cdn.js"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,yCAAgD;AAEhD;;;GAGG;AACH,2CAA8C;AAE9C;;GAEG;AACH,gCAAgC"}
|
|
1
|
+
{"version":3,"file":"env.cdn.d.ts","sourceRoot":"","sources":["../../../../src/common/constants/env.cdn.js"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,yCAAgD;AAEhD;;;GAGG;AACH,2CAA8C;AAE9C;;GAEG;AACH,gCAAgC;AAEhC;;GAEG;AACH,+CAAsD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../../../src/common/constants/env.js"],"names":[],"mappings":"AASA;;GAEG;AACH,0BAAsC;AAEtC;;GAEG;AACH,8BAA8B;AAE9B;;GAEG;AACH,gCAAgC"}
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../../../src/common/constants/env.js"],"names":[],"mappings":"AASA;;GAEG;AACH,0BAAsC;AAEtC;;GAEG;AACH,8BAA8B;AAE9B;;GAEG;AACH,gCAAgC;AAEhC;;GAEG;AACH,gCAAuD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.npm.d.ts","sourceRoot":"","sources":["../../../../src/common/constants/env.npm.js"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,yCAAgD;AAEhD;;;GAGG;AACH,8BAA8B;AAE9B;;;GAGG;AACH,gCAAgC"}
|
|
1
|
+
{"version":3,"file":"env.npm.d.ts","sourceRoot":"","sources":["../../../../src/common/constants/env.npm.js"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,yCAAgD;AAEhD;;;GAGG;AACH,8BAA8B;AAE9B;;;GAGG;AACH,gCAAgC;AAEhC;;GAEG;AACH,+CAAsD"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A helper method to warn to the console with New Relic: decoration
|
|
3
|
-
* @param {string} message
|
|
4
|
-
* @param {*} secondary
|
|
3
|
+
* @param {string} message The primary message to warn
|
|
4
|
+
* @param {*} [secondary] Secondary data to include, usually an error or object
|
|
5
5
|
* @returns
|
|
6
6
|
*/
|
|
7
|
-
export function warn(message: string, secondary
|
|
7
|
+
export function warn(message: string, secondary?: any): void;
|
|
8
8
|
//# sourceMappingURL=console.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"console.d.ts","sourceRoot":"","sources":["../../../../src/common/util/console.js"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,8BAJW,MAAM,
|
|
1
|
+
{"version":3,"file":"console.d.ts","sourceRoot":"","sources":["../../../../src/common/util/console.js"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,8BAJW,MAAM,yBAQhB"}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export const RRWEB_VERSION: "2.0.0-alpha.8";
|
|
2
1
|
export const AVG_COMPRESSION: 0.12;
|
|
3
2
|
export namespace RRWEB_EVENT_TYPES {
|
|
4
3
|
const DomContentLoaded: number;
|
|
@@ -17,6 +16,8 @@ export class Aggregate extends AggregateBase {
|
|
|
17
16
|
constructor(agentIdentifier: any, aggregator: any);
|
|
18
17
|
/** Each page mutation or event will be stored (raw) in this array. This array will be cleared on each harvest */
|
|
19
18
|
events: any[];
|
|
19
|
+
/** Backlog used for a 2-part sliding window to guarantee a 15-30s buffer window */
|
|
20
|
+
backloggedEvents: any[];
|
|
20
21
|
/** The interval to harvest at. This gets overridden if the size of the payload exceeds certain thresholds */
|
|
21
22
|
harvestTimeSeconds: any;
|
|
22
23
|
/** Set once the recorder has fully initialized after flag checks and sampling */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_replay/aggregate/index.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_replay/aggregate/index.js"],"names":[],"mappings":"AA4BA,mCAAmC;;;;;;;;;AAoCnC,uCAAuC;AACvC,uCAAuC;AACvC,iCAAiC;AACjC,uCAAuC;AAIvC;IACE,2BAAiC;IACjC,mDA6GC;IA3GC,iHAAiH;IACjH,cAAgB;IAChB,mFAAmF;IACnF,wBAA0B;IAC1B,8GAA8G;IAC9G,wBAAgH;IAChH,iFAAiF;IACjF,qBAAwB;IACxB,mEAAmE;IACnE,sBAAyB;IACzB,6GAA6G;IAC7G,aAAoB;IAGpB,iEAAiE;IACjE,mBAAsB;IACtB,gDAAgD;IAChD,wBAA0B;IAE1B;;;MAGE;IACF,qBAAwB;IACxB,4IAA4I;IAC5I,iBAAoB;IACpB,+HAA+H;IAC/H,kBAAqB;IAErB;;OAEG;IACH,oBAA+B;IAE/B,qGAAqG;IACrG,+BAA+B;IAE/B,kIAAkI;IAClI,cAAyB;IAOzB,uIAAuI;IACvI,0BAAyE;IA0BvE,wCAKQ;IAgCZ;;;;;;OAMG;IACH,kCALW,OAAO,eACP,OAAO,cACP,OAAO,GACL,IAAI,CAuDhB;IAED;;;;;;;;;oBAkBC;IAED;;;;;;;;;MAoDC;IAED,qCAOC;IAED,kFAAkF;IAClF,oBASC;IAED,qDAAqD;IACrD,uBA4BC;IAED,yHAAyH;IACzH,yCAsCC;IAED,0HAA0H;IAC1H,yBAGC;IAED,sBAGC;IAED,wBAEC;IAED,gCAAgC;IAChC,uCAGC;IAED,yDAAyD;IACzD,yBASC;IAED;;;SAGK;IACL,oCAGC;IAED,yCAGC;CACF;8BAzb6B,4BAA4B;iCALzB,2CAA2C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/spa/aggregate/index.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/spa/aggregate/index.js"],"names":[],"mappings":"AA4BA;IACE,2BAAiC;IACjC,mDA8rBC;IA3rBC;;;;;;;;;;;;;;;;MAgBC;IAED,uBAAsC;CA0qBzC;8BA1sB6B,4BAA4B;2BAJ/B,cAAc"}
|
|
@@ -67,8 +67,8 @@ export class AgentBase {
|
|
|
67
67
|
/**
|
|
68
68
|
* Starts a set of agent features if not running in "autoStart" mode
|
|
69
69
|
* {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/start/}
|
|
70
|
-
* @param {string|string[]
|
|
70
|
+
* @param {string|string[]} [featureNames] The name(s) of the features to start. If no name(s) are passed, all features will be started
|
|
71
71
|
*/
|
|
72
|
-
start(featureNames
|
|
72
|
+
start(featureNames?: string | string[] | undefined): void;
|
|
73
73
|
}
|
|
74
74
|
//# sourceMappingURL=agent-base.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-base.d.ts","sourceRoot":"","sources":["../../../src/loaders/agent-base.js"],"names":[],"mappings":"AAIA;IACE;;;;;OAKG;IACH,oBAHW,MAAM,yCAKhB;IAED;;;;;OAKG;IACH,sBAHW,MAAM,mCAKhB;IAED;;;;;;OAMG;IACH,yBAJW,MAAM,SACN,MAAM,GAAC,MAAM,GAAC,IAAI,uCAK5B;IAED;;;;;OAKG;IACH,mBAHW,KAAK,GAAC,MAAM,+CAKtB;IAED;;;;OAIG;IACH,iBAFW,MAAM,GAAC,IAAI,QAIrB;IAED;;;;;;;OAOG;IACH,6BAJW,MAAM,GAAC,IAAI,QAMrB;IAED;;;;OAIG;IACH,kCAFmB,KAAK,GAAC,MAAM,KAAK,OAAO,GAAG;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,QAI9D;IAED;;;;OAIG;IACH,+CAEC;IAED;;;;;OAKG;IACH,iBAHW,MAAM,MACN,MAAM,QAIhB;IAED;;;;OAIG;IACH
|
|
1
|
+
{"version":3,"file":"agent-base.d.ts","sourceRoot":"","sources":["../../../src/loaders/agent-base.js"],"names":[],"mappings":"AAIA;IACE;;;;;OAKG;IACH,oBAHW,MAAM,yCAKhB;IAED;;;;;OAKG;IACH,sBAHW,MAAM,mCAKhB;IAED;;;;;;OAMG;IACH,yBAJW,MAAM,SACN,MAAM,GAAC,MAAM,GAAC,IAAI,uCAK5B;IAED;;;;;OAKG;IACH,mBAHW,KAAK,GAAC,MAAM,+CAKtB;IAED;;;;OAIG;IACH,iBAFW,MAAM,GAAC,IAAI,QAIrB;IAED;;;;;;;OAOG;IACH,6BAJW,MAAM,GAAC,IAAI,QAMrB;IAED;;;;OAIG;IACH,kCAFmB,KAAK,GAAC,MAAM,KAAK,OAAO,GAAG;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,QAI9D;IAED;;;;OAIG;IACH,+CAEC;IAED;;;;;OAKG;IACH,iBAHW,MAAM,MACN,MAAM,QAIhB;IAED;;;;OAIG;IACH,0DAEC;CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@newrelic/browser-agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.244.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"author": "New Relic Browser Agent Team <browser-agent@newrelic.com>",
|
|
6
6
|
"description": "New Relic Browser Agent",
|
|
@@ -176,7 +176,7 @@
|
|
|
176
176
|
"dependencies": {
|
|
177
177
|
"core-js": "^3.26.0",
|
|
178
178
|
"fflate": "^0.7.4",
|
|
179
|
-
"rrweb": "2.0.0-alpha.
|
|
179
|
+
"rrweb": "2.0.0-alpha.11",
|
|
180
180
|
"web-vitals": "^3.1.0"
|
|
181
181
|
},
|
|
182
182
|
"devDependencies": {
|
|
@@ -63,6 +63,9 @@ const model = () => {
|
|
|
63
63
|
harvestTimeSeconds: 60,
|
|
64
64
|
sampling_rate: 50, // float from 0 - 100
|
|
65
65
|
error_sampling_rate: 50, // float from 0 - 100
|
|
66
|
+
collect_fonts: false, // serialize fonts for collection without public asset url, this is currently broken in RRWeb -- https://github.com/rrweb-io/rrweb/issues/1304. When fixed, revisit with test cases
|
|
67
|
+
inline_images: false, // serialize images for collection without public asset url
|
|
68
|
+
inline_stylesheet: true, // serialize css for collection without public asset url
|
|
66
69
|
// recording config settings
|
|
67
70
|
mask_all_inputs: true,
|
|
68
71
|
// this has a getter/setter to facilitate validation of the selectors
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A helper method to warn to the console with New Relic: decoration
|
|
3
|
-
* @param {string} message
|
|
4
|
-
* @param {*} secondary
|
|
3
|
+
* @param {string} message The primary message to warn
|
|
4
|
+
* @param {*} [secondary] Secondary data to include, usually an error or object
|
|
5
5
|
* @returns
|
|
6
6
|
*/
|
|
7
7
|
export function warn (message, secondary) {
|
|
@@ -22,9 +22,9 @@ import { obj as encodeObj } from '../../../common/url/encode'
|
|
|
22
22
|
import { warn } from '../../../common/util/console'
|
|
23
23
|
import { globalScope } from '../../../common/constants/runtime'
|
|
24
24
|
import { SUPPORTABILITY_METRIC_CHANNEL } from '../../metrics/constants'
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
import { handle } from '../../../common/event-emitter/handle'
|
|
26
|
+
import { FEATURE_NAMES } from '../../../loaders/features/features'
|
|
27
|
+
import { RRWEB_VERSION } from '../../../common/constants/env'
|
|
28
28
|
|
|
29
29
|
export const AVG_COMPRESSION = 0.12
|
|
30
30
|
|
|
@@ -53,6 +53,10 @@ const ABORT_REASONS = {
|
|
|
53
53
|
TOO_BIG: {
|
|
54
54
|
message: 'Payload was too large',
|
|
55
55
|
sm: 'Too-Big'
|
|
56
|
+
},
|
|
57
|
+
CROSS_TAB: {
|
|
58
|
+
message: 'Session Entity was set to OFF on another tab',
|
|
59
|
+
sm: 'Cross-Tab'
|
|
56
60
|
}
|
|
57
61
|
}
|
|
58
62
|
|
|
@@ -62,8 +66,8 @@ let recorder, gzipper, u8
|
|
|
62
66
|
export const MAX_PAYLOAD_SIZE = 1000000
|
|
63
67
|
/** Unloading caps around 64kb */
|
|
64
68
|
export const IDEAL_PAYLOAD_SIZE = 64000
|
|
65
|
-
/** Interval between forcing new full snapshots --
|
|
66
|
-
const CHECKOUT_MS = { [MODE.ERROR]:
|
|
69
|
+
/** Interval between forcing new full snapshots -- 15 seconds in error mode (x2), 5 minutes in full mode */
|
|
70
|
+
const CHECKOUT_MS = { [MODE.ERROR]: 15000, [MODE.FULL]: 300000, [MODE.OFF]: 0 }
|
|
67
71
|
|
|
68
72
|
export class Aggregate extends AggregateBase {
|
|
69
73
|
static featureName = FEATURE_NAME
|
|
@@ -71,6 +75,8 @@ export class Aggregate extends AggregateBase {
|
|
|
71
75
|
super(agentIdentifier, aggregator, FEATURE_NAME)
|
|
72
76
|
/** Each page mutation or event will be stored (raw) in this array. This array will be cleared on each harvest */
|
|
73
77
|
this.events = []
|
|
78
|
+
/** Backlog used for a 2-part sliding window to guarantee a 15-30s buffer window */
|
|
79
|
+
this.backloggedEvents = []
|
|
74
80
|
/** The interval to harvest at. This gets overridden if the size of the payload exceeds certain thresholds */
|
|
75
81
|
this.harvestTimeSeconds = getConfigurationValue(this.agentIdentifier, 'session_replay.harvestTimeSeconds') || 60
|
|
76
82
|
/** Set once the recorder has fully initialized after flag checks and sampling */
|
|
@@ -134,7 +140,7 @@ export class Aggregate extends AggregateBase {
|
|
|
134
140
|
|
|
135
141
|
this.ee.on(SESSION_EVENTS.UPDATE, (type, data) => {
|
|
136
142
|
if (!this.initialized || this.blocked || type !== SESSION_EVENT_TYPES.CROSS_TAB) return
|
|
137
|
-
if (this.mode !== MODE.OFF && data.sessionReplay === MODE.OFF) this.abort(
|
|
143
|
+
if (this.mode !== MODE.OFF && data.sessionReplay === MODE.OFF) this.abort(ABORT_REASONS.CROSS_TAB)
|
|
138
144
|
this.mode = data.sessionReplay
|
|
139
145
|
})
|
|
140
146
|
|
|
@@ -241,7 +247,10 @@ export class Aggregate extends AggregateBase {
|
|
|
241
247
|
prepareHarvest () {
|
|
242
248
|
if (this.events.length === 0 || (this.mode !== MODE.FULL && !this.blocked)) return
|
|
243
249
|
const payload = this.getHarvestContents()
|
|
244
|
-
|
|
250
|
+
if (!payload.body.length) {
|
|
251
|
+
this.clearBuffer()
|
|
252
|
+
return
|
|
253
|
+
}
|
|
245
254
|
if (this.shouldCompress) {
|
|
246
255
|
payload.body = gzipper(u8(stringify(payload.body)))
|
|
247
256
|
this.scheduler.opts.gzip = true
|
|
@@ -259,6 +268,17 @@ export class Aggregate extends AggregateBase {
|
|
|
259
268
|
const agentRuntime = getRuntime(this.agentIdentifier)
|
|
260
269
|
const info = getInfo(this.agentIdentifier)
|
|
261
270
|
|
|
271
|
+
if (this.backloggedEvents.length) this.events = [...this.backloggedEvents, ...this.events]
|
|
272
|
+
|
|
273
|
+
// do not let the first node be a full snapshot node, since this NEEDS to be preceded by a meta node
|
|
274
|
+
// we will manually inject it if this happens
|
|
275
|
+
const payloadStartsWithFullSnapshot = this.events[0]?.type === RRWEB_EVENT_TYPES.FullSnapshot
|
|
276
|
+
if (payloadStartsWithFullSnapshot && !!this.lastMeta) {
|
|
277
|
+
this.hasMeta = true
|
|
278
|
+
this.events.unshift(this.lastMeta) // --> pushed the meta from a previous payload into newer payload... but it still has old timestamps
|
|
279
|
+
this.lastMeta = undefined
|
|
280
|
+
}
|
|
281
|
+
|
|
262
282
|
// do not let the last node be a meta node, since this NEEDS to precede a snapshot
|
|
263
283
|
// we will manually inject it later if we find a payload that is missing a meta node
|
|
264
284
|
const payloadEndsWithMeta = this.events[this.events.length - 1]?.type === RRWEB_EVENT_TYPES.Meta
|
|
@@ -268,16 +288,8 @@ export class Aggregate extends AggregateBase {
|
|
|
268
288
|
this.hasMeta = !!this.events.find(x => x.type === RRWEB_EVENT_TYPES.Meta)
|
|
269
289
|
}
|
|
270
290
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
const payloadStartsWithFullSnapshot = this.events[0]?.type === RRWEB_EVENT_TYPES.FullSnapshot
|
|
274
|
-
if (payloadStartsWithFullSnapshot) {
|
|
275
|
-
this.hasMeta = true
|
|
276
|
-
this.events.unshift(this.lastMeta)
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
const firstEventTimestamp = this.events[0]?.timestamp
|
|
280
|
-
const lastEventTimestamp = this.events[this.events.length - 1]?.timestamp
|
|
291
|
+
const firstEventTimestamp = this.events[0]?.timestamp // from rrweb node
|
|
292
|
+
const lastEventTimestamp = this.events[this.events.length - 1]?.timestamp // from rrweb node
|
|
281
293
|
const firstTimestamp = firstEventTimestamp || this.cycleTimestamp
|
|
282
294
|
const lastTimestamp = lastEventTimestamp || getRuntime(this.agentIdentifier).offset + globalScope.performance.now()
|
|
283
295
|
return {
|
|
@@ -317,6 +329,8 @@ export class Aggregate extends AggregateBase {
|
|
|
317
329
|
|
|
318
330
|
/** Clears the buffer (this.events), and resets all payload metadata properties */
|
|
319
331
|
clearBuffer () {
|
|
332
|
+
if (this.mode === MODE.ERROR) this.backloggedEvents = this.events
|
|
333
|
+
else this.backloggedEvents = []
|
|
320
334
|
this.events = []
|
|
321
335
|
this.hasSnapshot = false
|
|
322
336
|
this.hasMeta = false
|
|
@@ -331,11 +345,8 @@ export class Aggregate extends AggregateBase {
|
|
|
331
345
|
warn('Recording library was never imported')
|
|
332
346
|
return this.abort(ABORT_REASONS.IMPORT)
|
|
333
347
|
}
|
|
334
|
-
this.clearTimestamps()
|
|
335
|
-
// set the fallbacks as early as possible
|
|
336
|
-
this.setTimestamps()
|
|
337
348
|
this.recording = true
|
|
338
|
-
const { block_class, ignore_class, mask_text_class, block_selector, mask_input_options, mask_text_selector, mask_all_inputs } = getConfigurationValue(this.agentIdentifier, 'session_replay')
|
|
349
|
+
const { block_class, ignore_class, mask_text_class, block_selector, mask_input_options, mask_text_selector, mask_all_inputs, inline_images, inline_stylesheet, collect_fonts } = getConfigurationValue(this.agentIdentifier, 'session_replay')
|
|
339
350
|
// set up rrweb configurations for maximum privacy --
|
|
340
351
|
// https://newrelic.atlassian.net/wiki/spaces/O11Y/pages/2792293280/2023+02+28+Browser+-+Session+Replay#Configuration-options
|
|
341
352
|
const stop = recorder({
|
|
@@ -347,6 +358,9 @@ export class Aggregate extends AggregateBase {
|
|
|
347
358
|
maskInputOptions: mask_input_options,
|
|
348
359
|
maskTextSelector: mask_text_selector,
|
|
349
360
|
maskAllInputs: mask_all_inputs,
|
|
361
|
+
inlineImages: inline_images,
|
|
362
|
+
inlineStylesheet: inline_stylesheet,
|
|
363
|
+
collectFonts: collect_fonts,
|
|
350
364
|
checkoutEveryNms: CHECKOUT_MS[this.mode]
|
|
351
365
|
})
|
|
352
366
|
|
|
@@ -371,7 +385,8 @@ export class Aggregate extends AggregateBase {
|
|
|
371
385
|
// Checkout events are flags by the recording lib that indicate a fullsnapshot was taken every n ms. These are important
|
|
372
386
|
// to help reconstruct the replay later and must be included. While waiting and buffering for errors to come through,
|
|
373
387
|
// each time we see a new checkout, we can drop the old data.
|
|
374
|
-
|
|
388
|
+
// we need to check for meta because rrweb will flag it as checkout twice, once for meta, then once for snapshot
|
|
389
|
+
if (this.mode === MODE.ERROR && isCheckout && event.type === RRWEB_EVENT_TYPES.Meta) {
|
|
375
390
|
// we are still waiting for an error to throw, so keep wiping the buffer over time
|
|
376
391
|
this.clearBuffer()
|
|
377
392
|
}
|
|
@@ -379,7 +394,6 @@ export class Aggregate extends AggregateBase {
|
|
|
379
394
|
// meta event
|
|
380
395
|
if (event.type === RRWEB_EVENT_TYPES.Meta) {
|
|
381
396
|
this.hasMeta = true
|
|
382
|
-
this.lastMeta = event
|
|
383
397
|
}
|
|
384
398
|
// snapshot event
|
|
385
399
|
if (event.type === RRWEB_EVENT_TYPES.FullSnapshot) {
|
|
@@ -421,7 +435,7 @@ export class Aggregate extends AggregateBase {
|
|
|
421
435
|
/** Abort the feature, once aborted it will not resume */
|
|
422
436
|
abort (reason = {}) {
|
|
423
437
|
warn(`SR aborted -- ${reason.message}`)
|
|
424
|
-
|
|
438
|
+
handle(SUPPORTABILITY_METRIC_CHANNEL, [`SessionReplay/Abort/${reason.sm}`], undefined, FEATURE_NAMES.metrics, this.ee)
|
|
425
439
|
this.blocked = true
|
|
426
440
|
this.mode = MODE.OFF
|
|
427
441
|
this.stopRecording()
|
|
@@ -20,6 +20,7 @@ import { FEATURE_NAMES } from '../../../loaders/features/features'
|
|
|
20
20
|
import { AggregateBase } from '../../utils/aggregate-base'
|
|
21
21
|
import { firstContentfulPaint } from '../../../common/vitals/first-contentful-paint'
|
|
22
22
|
import { firstPaint } from '../../../common/vitals/first-paint'
|
|
23
|
+
import { bundleId } from '../../../common/ids/bundle-id'
|
|
23
24
|
|
|
24
25
|
const {
|
|
25
26
|
FEATURE_NAME, INTERACTION_EVENTS, MAX_TIMER_BUDGET, FN_START, FN_END, CB_START, INTERACTION_API, REMAINING,
|
|
@@ -166,7 +167,7 @@ export class Aggregate extends AggregateBase {
|
|
|
166
167
|
register(FN_START, function (args, eventSource) {
|
|
167
168
|
var ev = args[0]
|
|
168
169
|
var evName = ev.type
|
|
169
|
-
var eventNode = ev
|
|
170
|
+
var eventNode = ev[`__nrNode:${bundleId}`]
|
|
170
171
|
|
|
171
172
|
if (!state.pageLoaded && evName === 'load' && eventSource === window) {
|
|
172
173
|
state.pageLoaded = true
|
|
@@ -216,7 +217,7 @@ export class Aggregate extends AggregateBase {
|
|
|
216
217
|
}
|
|
217
218
|
}
|
|
218
219
|
|
|
219
|
-
ev
|
|
220
|
+
ev[`__nrNode:${bundleId}`] = state.currentNode
|
|
220
221
|
}, this.featureName, eventsEE)
|
|
221
222
|
|
|
222
223
|
/**
|
|
@@ -10,7 +10,7 @@ export class AgentBase {
|
|
|
10
10
|
* @param {object} [attributes] JSON object with one or more key/value pairs. For example: {key:"value"}. The key is reported as its own PageAction attribute with the specified values.
|
|
11
11
|
*/
|
|
12
12
|
addPageAction (name, attributes) {
|
|
13
|
-
warn('Call to agent api addPageAction failed. The
|
|
13
|
+
warn('Call to agent api addPageAction failed. The page action feature is not currently initialized.')
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -90,13 +90,13 @@ export class AgentBase {
|
|
|
90
90
|
* @param {string} id The ID or version of this release; for example, a version number, build number from your CI environment, GitHub SHA, GUID, or a hash of the contents.
|
|
91
91
|
*/
|
|
92
92
|
addRelease (name, id) {
|
|
93
|
-
warn('Call to agent api addRelease failed. The
|
|
93
|
+
warn('Call to agent api addRelease failed. The js errors feature is not currently initialized.')
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
/**
|
|
97
97
|
* Starts a set of agent features if not running in "autoStart" mode
|
|
98
98
|
* {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/start/}
|
|
99
|
-
* @param {string|string[]
|
|
99
|
+
* @param {string|string[]} [featureNames] The name(s) of the features to start. If no name(s) are passed, all features will be started
|
|
100
100
|
*/
|
|
101
101
|
start (featureNames) {
|
|
102
102
|
warn('Call to agent api addRelease failed. The agent is not currently initialized.')
|
package/src/loaders/agent.js
CHANGED
|
@@ -107,7 +107,7 @@ export class Agent extends AgentBase {
|
|
|
107
107
|
* If you are sending the same event object to New Relic as a PageAction, omit the TYPE attribute. (type is a string to describe what type of event you are marking inside of a session trace.) If included, it will override the event type and cause the PageAction event to be sent incorrectly. Instead, use the name attribute for event information.
|
|
108
108
|
*/
|
|
109
109
|
addToTrace (customAttributes) {
|
|
110
|
-
warn('Call to agent api addToTrace failed. The
|
|
110
|
+
warn('Call to agent api addToTrace failed. The session trace feature is not currently initialized.')
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
/**
|