@newrelic/browser-agent 1.290.0 → 1.290.1
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 +7 -0
- package/dist/cjs/common/config/configurable.js +1 -1
- package/dist/cjs/common/config/runtime.js +4 -2
- package/dist/cjs/common/constants/agent-constants.js +3 -2
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/features/jserrors/instrument/index.js +3 -8
- package/dist/cjs/features/session_replay/aggregate/index.js +4 -4
- package/dist/cjs/features/session_replay/constants.js +0 -1
- package/dist/cjs/features/session_replay/instrument/index.js +4 -8
- package/dist/cjs/features/session_replay/shared/recorder.js +29 -30
- package/dist/cjs/features/utils/entity-manager.js +5 -6
- package/dist/cjs/features/utils/event-store-manager.js +8 -4
- package/dist/cjs/loaders/api/constants.js +1 -2
- package/dist/cjs/loaders/api/noticeError.js +3 -9
- package/dist/cjs/loaders/api/sharedHandlers.js +1 -7
- package/dist/esm/common/config/configurable.js +1 -1
- package/dist/esm/common/config/runtime.js +4 -2
- package/dist/esm/common/constants/agent-constants.js +2 -1
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/features/jserrors/instrument/index.js +3 -8
- package/dist/esm/features/session_replay/aggregate/index.js +4 -4
- package/dist/esm/features/session_replay/constants.js +0 -1
- package/dist/esm/features/session_replay/instrument/index.js +4 -8
- package/dist/esm/features/session_replay/shared/recorder.js +30 -31
- package/dist/esm/features/utils/entity-manager.js +5 -6
- package/dist/esm/features/utils/event-store-manager.js +5 -1
- package/dist/esm/loaders/api/constants.js +0 -1
- package/dist/esm/loaders/api/noticeError.js +2 -8
- package/dist/esm/loaders/api/sharedHandlers.js +1 -7
- package/dist/types/common/config/runtime.d.ts.map +1 -1
- package/dist/types/common/constants/agent-constants.d.ts +1 -0
- package/dist/types/common/constants/agent-constants.d.ts.map +1 -1
- package/dist/types/features/jserrors/instrument/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/constants.d.ts +0 -1
- package/dist/types/features/session_replay/constants.d.ts.map +1 -1
- package/dist/types/features/session_replay/instrument/index.d.ts +0 -1
- package/dist/types/features/session_replay/instrument/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/shared/recorder.d.ts +0 -3
- package/dist/types/features/session_replay/shared/recorder.d.ts.map +1 -1
- package/dist/types/features/utils/entity-manager.d.ts +1 -5
- package/dist/types/features/utils/entity-manager.d.ts.map +1 -1
- package/dist/types/features/utils/event-store-manager.d.ts.map +1 -1
- package/dist/types/loaders/api/constants.d.ts +0 -1
- package/dist/types/loaders/api/constants.d.ts.map +1 -1
- package/dist/types/loaders/api/noticeError.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/common/config/configurable.js +1 -1
- package/src/common/config/runtime.js +3 -2
- package/src/common/constants/agent-constants.js +1 -0
- package/src/features/jserrors/instrument/index.js +3 -9
- package/src/features/session_replay/aggregate/index.js +4 -4
- package/src/features/session_replay/constants.js +0 -1
- package/src/features/session_replay/instrument/index.js +4 -8
- package/src/features/session_replay/shared/recorder.js +28 -32
- package/src/features/utils/entity-manager.js +5 -5
- package/src/features/utils/event-store-manager.js +5 -2
- package/src/loaders/api/constants.js +0 -2
- package/src/loaders/api/noticeError.js +2 -10
- package/src/loaders/api/sharedHandlers.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,13 @@
|
|
|
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.290.1](https://github.com/newrelic/newrelic-browser-agent/compare/v1.290.0...v1.290.1) (2025-05-21)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* Silence setting getter only harvestCount message ([#1480](https://github.com/newrelic/newrelic-browser-agent/issues/1480)) ([98b1ab6](https://github.com/newrelic/newrelic-browser-agent/commit/98b1ab69c8a03a41f0cdccf27e410728f389bebb))
|
|
12
|
+
|
|
6
13
|
## [1.290.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.289.0...v1.290.0) (2025-05-02)
|
|
7
14
|
|
|
8
15
|
|
|
@@ -26,7 +26,7 @@ function getModeledObject(obj, model) {
|
|
|
26
26
|
}
|
|
27
27
|
if (Array.isArray(obj[key]) && Array.isArray(model[key])) output[key] = Array.from(new Set([...obj[key], ...model[key]]));else if (typeof obj[key] === 'object' && typeof model[key] === 'object') output[key] = getModeledObject(obj[key], model[key]);else output[key] = obj[key];
|
|
28
28
|
} catch (e) {
|
|
29
|
-
(0, _console.warn)(1, e);
|
|
29
|
+
if (!output[key]) (0, _console.warn)(1, e);
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
return output;
|
|
@@ -16,7 +16,7 @@ var _env = require("../constants/env.npm");
|
|
|
16
16
|
* Module level count of harvests. This property will auto-increment each time it is accessed.
|
|
17
17
|
* @type {number}
|
|
18
18
|
*/
|
|
19
|
-
let
|
|
19
|
+
let _harvestCount = 0;
|
|
20
20
|
const ReadOnly = {
|
|
21
21
|
buildEnv: _env.BUILD_ENV,
|
|
22
22
|
distMethod: _env.DIST_METHOD,
|
|
@@ -32,6 +32,8 @@ const RuntimeModel = {
|
|
|
32
32
|
entityManager: undefined,
|
|
33
33
|
harvester: undefined,
|
|
34
34
|
isolatedBacklog: false,
|
|
35
|
+
isRecording: false,
|
|
36
|
+
// true when actively recording, false when paused or stopped
|
|
35
37
|
loaderType: undefined,
|
|
36
38
|
maxBytes: 30000,
|
|
37
39
|
obfuscator: undefined,
|
|
@@ -41,7 +43,7 @@ const RuntimeModel = {
|
|
|
41
43
|
session: undefined,
|
|
42
44
|
timeKeeper: undefined,
|
|
43
45
|
get harvestCount() {
|
|
44
|
-
return ++
|
|
46
|
+
return ++_harvestCount;
|
|
45
47
|
}
|
|
46
48
|
};
|
|
47
49
|
const mergeRuntime = runtime => {
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.MAX_PAYLOAD_SIZE = exports.IDEAL_PAYLOAD_SIZE = void 0;
|
|
6
|
+
exports.MAX_PAYLOAD_SIZE = exports.IDEAL_PAYLOAD_SIZE = exports.DEFAULT_KEY = void 0;
|
|
7
7
|
/**
|
|
8
8
|
* Copyright 2020-2025 New Relic, Inc. All rights reserved.
|
|
9
9
|
* SPDX-License-Identifier: Apache-2.0
|
|
10
10
|
*/
|
|
11
11
|
const IDEAL_PAYLOAD_SIZE = exports.IDEAL_PAYLOAD_SIZE = 64000;
|
|
12
|
-
const MAX_PAYLOAD_SIZE = exports.MAX_PAYLOAD_SIZE = 1000000;
|
|
12
|
+
const MAX_PAYLOAD_SIZE = exports.MAX_PAYLOAD_SIZE = 1000000;
|
|
13
|
+
const DEFAULT_KEY = exports.DEFAULT_KEY = 'NR_CONTAINER_AGENT';
|
|
@@ -17,7 +17,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_EN
|
|
|
17
17
|
/**
|
|
18
18
|
* Exposes the version of the agent
|
|
19
19
|
*/
|
|
20
|
-
const VERSION = exports.VERSION = "1.290.
|
|
20
|
+
const VERSION = exports.VERSION = "1.290.1";
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Exposes the build type of the agent
|
|
@@ -17,7 +17,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_EN
|
|
|
17
17
|
/**
|
|
18
18
|
* Exposes the version of the agent
|
|
19
19
|
*/
|
|
20
|
-
const VERSION = exports.VERSION = "1.290.
|
|
20
|
+
const VERSION = exports.VERSION = "1.290.1";
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Exposes the build type of the agent
|
|
@@ -10,7 +10,6 @@ var _constants = require("../constants");
|
|
|
10
10
|
var _runtime = require("../../../common/constants/runtime");
|
|
11
11
|
var _eventListenerOpts = require("../../../common/event-listener/event-listener-opts");
|
|
12
12
|
var _now = require("../../../common/timing/now");
|
|
13
|
-
var _constants2 = require("../../session_replay/constants");
|
|
14
13
|
var _castError = require("../shared/cast-error");
|
|
15
14
|
var _noticeError = require("../../../loaders/api/noticeError");
|
|
16
15
|
var _setErrorHandler = require("../../../loaders/api/setErrorHandler");
|
|
@@ -23,7 +22,6 @@ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e;
|
|
|
23
22
|
*/
|
|
24
23
|
class Instrument extends _instrumentBase.InstrumentBase {
|
|
25
24
|
static featureName = _constants.FEATURE_NAME;
|
|
26
|
-
#replayRunning = false;
|
|
27
25
|
constructor(agentRef) {
|
|
28
26
|
super(agentRef, _constants.FEATURE_NAME);
|
|
29
27
|
|
|
@@ -38,20 +36,17 @@ class Instrument extends _instrumentBase.InstrumentBase {
|
|
|
38
36
|
} catch (e) {}
|
|
39
37
|
this.ee.on('internal-error', (error, reason) => {
|
|
40
38
|
if (!this.abortHandler) return;
|
|
41
|
-
(0, _handle.handle)('ierr', [(0, _castError.castError)(error), (0, _now.now)(), true, {},
|
|
42
|
-
});
|
|
43
|
-
this.ee.on(_constants2.SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, isRunning => {
|
|
44
|
-
this.#replayRunning = isRunning;
|
|
39
|
+
(0, _handle.handle)('ierr', [(0, _castError.castError)(error), (0, _now.now)(), true, {}, agentRef.runtime.isRecording, reason], undefined, this.featureName, this.ee);
|
|
45
40
|
});
|
|
46
41
|
_runtime.globalScope.addEventListener('unhandledrejection', promiseRejectionEvent => {
|
|
47
42
|
if (!this.abortHandler) return;
|
|
48
43
|
(0, _handle.handle)('err', [(0, _castError.castPromiseRejectionEvent)(promiseRejectionEvent), (0, _now.now)(), false, {
|
|
49
44
|
unhandledPromiseRejection: 1
|
|
50
|
-
},
|
|
45
|
+
}, agentRef.runtime.isRecording], undefined, this.featureName, this.ee);
|
|
51
46
|
}, (0, _eventListenerOpts.eventListenerOpts)(false, this.removeOnAbort?.signal));
|
|
52
47
|
_runtime.globalScope.addEventListener('error', errorEvent => {
|
|
53
48
|
if (!this.abortHandler) return;
|
|
54
|
-
(0, _handle.handle)('err', [(0, _castError.castErrorEvent)(errorEvent), (0, _now.now)(), false, {},
|
|
49
|
+
(0, _handle.handle)('err', [(0, _castError.castErrorEvent)(errorEvent), (0, _now.now)(), false, {}, agentRef.runtime.isRecording], undefined, this.featureName, this.ee);
|
|
55
50
|
}, (0, _eventListenerOpts.eventListenerOpts)(false, this.removeOnAbort?.signal));
|
|
56
51
|
this.abortHandler = this.#abort; // we also use this as a flag to denote that the feature is active or on and handling errors
|
|
57
52
|
this.importAggregator(agentRef, () => Promise.resolve().then(() => _interopRequireWildcard(require(/* webpackChunkName: "jserrors-aggregate" */'../aggregate'))));
|
|
@@ -95,7 +95,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
95
95
|
this.entitled = !!entitled;
|
|
96
96
|
if (!this.entitled) {
|
|
97
97
|
this.deregisterDrain();
|
|
98
|
-
if (this.
|
|
98
|
+
if (this.agentRef.runtime.isRecording) {
|
|
99
99
|
this.abort(_constants.ABORT_REASONS.ENTITLEMENTS);
|
|
100
100
|
this.reportSupportabilityMetric('SessionReplay/EnabledNotEntitled/Detected');
|
|
101
101
|
}
|
|
@@ -136,7 +136,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
136
136
|
this.mode = _constants2.MODE.FULL;
|
|
137
137
|
// if the error was noticed AFTER the recorder was already imported....
|
|
138
138
|
if (this.recorder && this.initialized) {
|
|
139
|
-
if (!this.
|
|
139
|
+
if (!this.agentRef.runtime.isRecording) this.recorder.startRecording();
|
|
140
140
|
this.syncWithSessionManager({
|
|
141
141
|
sessionReplayMode: this.mode
|
|
142
142
|
});
|
|
@@ -166,7 +166,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
166
166
|
timeKeeper
|
|
167
167
|
} = this.agentRef.runtime;
|
|
168
168
|
this.timeKeeper = timeKeeper;
|
|
169
|
-
if (this.recorder?.parent.trigger === _constants.TRIGGERS.API && this.
|
|
169
|
+
if (this.recorder?.parent.trigger === _constants.TRIGGERS.API && this.agentRef.runtime.isRecording) {
|
|
170
170
|
this.mode = _constants2.MODE.FULL;
|
|
171
171
|
} else if (!session.isNew && !ignoreSession) {
|
|
172
172
|
// inherit the mode of the existing session
|
|
@@ -204,7 +204,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
204
204
|
this.prepUtils().then(() => this.agentRef.runtime.harvester.triggerHarvestFor(this));
|
|
205
205
|
}
|
|
206
206
|
await this.prepUtils();
|
|
207
|
-
if (!this.
|
|
207
|
+
if (!this.agentRef.runtime.isRecording) this.recorder.startRecording();
|
|
208
208
|
this.syncWithSessionManager({
|
|
209
209
|
sessionReplayMode: this.mode
|
|
210
210
|
});
|
|
@@ -15,7 +15,6 @@ const FEATURE_NAME = exports.FEATURE_NAME = _features.FEATURE_NAMES.sessionRepla
|
|
|
15
15
|
const SR_EVENT_EMITTER_TYPES = exports.SR_EVENT_EMITTER_TYPES = {
|
|
16
16
|
RECORD: 'recordReplay',
|
|
17
17
|
PAUSE: 'pauseReplay',
|
|
18
|
-
REPLAY_RUNNING: 'replayRunning',
|
|
19
18
|
ERROR_DURING_REPLAY: 'errorDuringReplay'
|
|
20
19
|
};
|
|
21
20
|
const AVG_COMPRESSION = exports.AVG_COMPRESSION = 0.12;
|
|
@@ -29,7 +29,6 @@ class Instrument extends _instrumentBase.InstrumentBase {
|
|
|
29
29
|
(0, _recordReplay.setupRecordReplayAPI)(agentRef);
|
|
30
30
|
(0, _pauseReplay.setupPauseReplayAPI)(agentRef);
|
|
31
31
|
let session;
|
|
32
|
-
this.replayRunning = false;
|
|
33
32
|
this.#agentRef = agentRef;
|
|
34
33
|
try {
|
|
35
34
|
session = JSON.parse(localStorage.getItem("".concat(_constants.PREFIX, "_").concat(_constants.DEFAULT_KEY)));
|
|
@@ -46,16 +45,11 @@ class Instrument extends _instrumentBase.InstrumentBase {
|
|
|
46
45
|
|
|
47
46
|
/** If the recorder is running, we can pass error events on to the agg to help it switch to full mode later */
|
|
48
47
|
this.ee.on('err', e => {
|
|
49
|
-
if (this.
|
|
48
|
+
if (this.#agentRef.runtime.isRecording) {
|
|
50
49
|
this.errorNoticed = true;
|
|
51
50
|
(0, _handle.handle)(_constants2.SR_EVENT_EMITTER_TYPES.ERROR_DURING_REPLAY, [e], undefined, this.featureName, this.ee);
|
|
52
51
|
}
|
|
53
52
|
});
|
|
54
|
-
|
|
55
|
-
/** Emitted by the recorder when it starts capturing data, used to determine if we should pass errors on to the agg */
|
|
56
|
-
this.ee.on(_constants2.SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, isRunning => {
|
|
57
|
-
this.replayRunning = isRunning;
|
|
58
|
-
});
|
|
59
53
|
}
|
|
60
54
|
|
|
61
55
|
// At this point wherein session state exists already but we haven't init SessionEntity aka verify timers.
|
|
@@ -95,7 +89,9 @@ class Instrument extends _instrumentBase.InstrumentBase {
|
|
|
95
89
|
});
|
|
96
90
|
this.recorder.startRecording();
|
|
97
91
|
this.abortHandler = this.recorder.stopRecording;
|
|
98
|
-
} catch (
|
|
92
|
+
} catch (err) {
|
|
93
|
+
this.parent.ee.emit('internal-error', [err]);
|
|
94
|
+
}
|
|
99
95
|
this.importAggregator(this.#agentRef, () => Promise.resolve().then(() => _interopRequireWildcard(require(/* webpackChunkName: "session_replay-aggregate" */'../aggregate'))), {
|
|
100
96
|
recorder: this.recorder,
|
|
101
97
|
errorNoticed: this.errorNoticed
|
|
@@ -43,8 +43,6 @@ class Recorder {
|
|
|
43
43
|
this.#events = new _recorderEvents.RecorderEvents(this.shouldFix);
|
|
44
44
|
this.#backloggedEvents = new _recorderEvents.RecorderEvents(this.shouldFix);
|
|
45
45
|
this.#preloaded = [new _recorderEvents.RecorderEvents(this.shouldFix)];
|
|
46
|
-
/** True when actively recording, false when paused or stopped */
|
|
47
|
-
this.recording = false;
|
|
48
46
|
/** The pointer to the current bucket holding rrweb events */
|
|
49
47
|
this.currentBufferTarget = this.#events;
|
|
50
48
|
/** Only set to true once a snapshot node has been processed. Used to block preload harvests from sending before we know we have a snapshot */
|
|
@@ -52,7 +50,9 @@ class Recorder {
|
|
|
52
50
|
/** Hold on to the last meta node, so that it can be re-inserted if the meta and snapshot nodes are broken up due to harvesting */
|
|
53
51
|
this.lastMeta = false;
|
|
54
52
|
/** The method to stop recording. This defaults to a noop, but is overwritten once the recording library is imported and initialized */
|
|
55
|
-
this.stopRecording = () => {
|
|
53
|
+
this.stopRecording = () => {
|
|
54
|
+
this.parent.agentRef.runtime.isRecording = false;
|
|
55
|
+
};
|
|
56
56
|
}
|
|
57
57
|
getEvents() {
|
|
58
58
|
if (this.#preloaded[0]?.events.length) {
|
|
@@ -83,7 +83,7 @@ class Recorder {
|
|
|
83
83
|
|
|
84
84
|
/** Begin recording using configured recording lib */
|
|
85
85
|
startRecording() {
|
|
86
|
-
this.
|
|
86
|
+
this.parent.agentRef.runtime.isRecording = true;
|
|
87
87
|
const {
|
|
88
88
|
block_class,
|
|
89
89
|
ignore_class,
|
|
@@ -98,27 +98,30 @@ class Recorder {
|
|
|
98
98
|
|
|
99
99
|
// set up rrweb configurations for maximum privacy --
|
|
100
100
|
// https://newrelic.atlassian.net/wiki/spaces/O11Y/pages/2792293280/2023+02+28+Browser+-+Session+Replay#Configuration-options
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
101
|
+
let stop;
|
|
102
|
+
try {
|
|
103
|
+
stop = (0, _rrweb.record)({
|
|
104
|
+
emit: this.audit.bind(this),
|
|
105
|
+
blockClass: block_class,
|
|
106
|
+
ignoreClass: ignore_class,
|
|
107
|
+
maskTextClass: mask_text_class,
|
|
108
|
+
blockSelector: block_selector,
|
|
109
|
+
maskInputOptions: mask_input_options,
|
|
110
|
+
maskTextSelector: mask_text_selector,
|
|
111
|
+
maskTextFn: _utils.customMasker,
|
|
112
|
+
maskAllInputs: mask_all_inputs,
|
|
113
|
+
maskInputFn: _utils.customMasker,
|
|
114
|
+
inlineStylesheet: true,
|
|
115
|
+
inlineImages: inline_images,
|
|
116
|
+
collectFonts: collect_fonts,
|
|
117
|
+
checkoutEveryNms: _constants.CHECKOUT_MS[this.parent.mode],
|
|
118
|
+
recordAfter: 'DOMContentLoaded'
|
|
119
|
+
});
|
|
120
|
+
} catch (err) {
|
|
121
|
+
this.parent.ee.emit('internal-error', [err]);
|
|
122
|
+
}
|
|
118
123
|
this.stopRecording = () => {
|
|
119
|
-
this.
|
|
120
|
-
this.notified = false;
|
|
121
|
-
this.parent.ee.emit(_constants.SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, [false, this.parent.mode]);
|
|
124
|
+
this.parent.agentRef.runtime.isRecording = false;
|
|
122
125
|
stop?.();
|
|
123
126
|
};
|
|
124
127
|
}
|
|
@@ -165,16 +168,12 @@ class Recorder {
|
|
|
165
168
|
/** Store a payload in the buffer (this.#events). This should be the callback to the recording lib noticing a mutation */
|
|
166
169
|
store(event, isCheckout) {
|
|
167
170
|
if (!event) return;
|
|
168
|
-
if (this.parent.agentRef.runtime
|
|
171
|
+
if (this.parent.agentRef.runtime.session?.isAfterSessionExpiry(event.timestamp)) {
|
|
169
172
|
(0, _handle.handle)(_constants3.SUPPORTABILITY_METRIC_CHANNEL, ['Session/Expired/SessionReplay/Seen'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
170
173
|
return;
|
|
171
174
|
}
|
|
172
175
|
if (!(this.parent instanceof _aggregateBase.AggregateBase) && this.#preloaded.length) this.currentBufferTarget = this.#preloaded[this.#preloaded.length - 1];else this.currentBufferTarget = this.#events;
|
|
173
176
|
if (this.parent.blocked) return;
|
|
174
|
-
if (!this.notified) {
|
|
175
|
-
this.parent.ee.emit(_constants.SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, [true, this.parent.mode]);
|
|
176
|
-
this.notified = true;
|
|
177
|
-
}
|
|
178
177
|
if (this.parent.timeKeeper?.ready && !event.__newrelic) {
|
|
179
178
|
event.__newrelic = (0, _utils.buildNRMetaNode)(event.timestamp, this.parent.timeKeeper);
|
|
180
179
|
event.timestamp = this.parent.timeKeeper.correctAbsoluteTimestamp(event.timestamp);
|
|
@@ -219,7 +218,7 @@ class Recorder {
|
|
|
219
218
|
/** force the recording lib to take a full DOM snapshot. This needs to occur in certain cases, like visibility changes */
|
|
220
219
|
takeFullSnapshot() {
|
|
221
220
|
try {
|
|
222
|
-
if (!this.
|
|
221
|
+
if (!this.parent.agentRef.runtime.isRecording) return;
|
|
223
222
|
_rrweb.record.takeFullSnapshot();
|
|
224
223
|
} catch (err) {
|
|
225
224
|
// in the off chance we think we are recording, but rrweb does not, rrweb's lib will throw an error. This catch is just a precaution
|
|
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.EntityManager = void 0;
|
|
7
|
+
var _agentConstants = require("../../common/constants/agent-constants");
|
|
7
8
|
/**
|
|
8
9
|
* Copyright 2020-2025 New Relic, Inc. All rights reserved.
|
|
9
10
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -12,16 +13,14 @@ exports.EntityManager = void 0;
|
|
|
12
13
|
class EntityManager {
|
|
13
14
|
#entities = new Map();
|
|
14
15
|
#entityGuidLookup = {};
|
|
15
|
-
#defaultEntity = null;
|
|
16
16
|
constructor(agentRef) {
|
|
17
17
|
this.agentRef = agentRef;
|
|
18
|
-
this.#
|
|
18
|
+
this.#entities.set(_agentConstants.DEFAULT_KEY, {
|
|
19
19
|
licenseKey: agentRef.info.licenseKey,
|
|
20
20
|
applicationID: agentRef.info.applicationID
|
|
21
|
-
};
|
|
21
|
+
});
|
|
22
22
|
}
|
|
23
|
-
get(entityGuid) {
|
|
24
|
-
if (!entityGuid) return this.#defaultEntity;
|
|
23
|
+
get(entityGuid = _agentConstants.DEFAULT_KEY) {
|
|
25
24
|
return this.#entities.get(entityGuid);
|
|
26
25
|
}
|
|
27
26
|
getEntityGuidFor(licenseKey, applicationID) {
|
|
@@ -41,7 +40,7 @@ class EntityManager {
|
|
|
41
40
|
this.#entities.clear();
|
|
42
41
|
}
|
|
43
42
|
setDefaultEntity(entity) {
|
|
44
|
-
this.#
|
|
43
|
+
this.#entities.set(_agentConstants.DEFAULT_KEY, entity);
|
|
45
44
|
}
|
|
46
45
|
}
|
|
47
46
|
exports.EntityManager = EntityManager;
|
|
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.EventStoreManager = void 0;
|
|
7
|
+
var _agentConstants = require("../../common/constants/agent-constants");
|
|
7
8
|
var _globalEvent = require("../../common/dispatch/global-event");
|
|
8
9
|
var _featureFlags = require("../../common/util/feature-flags");
|
|
9
10
|
var _target = require("../../common/util/target");
|
|
@@ -12,7 +13,6 @@ var _target = require("../../common/util/target");
|
|
|
12
13
|
* SPDX-License-Identifier: Apache-2.0
|
|
13
14
|
*/
|
|
14
15
|
|
|
15
|
-
const DEFAULT_KEY = 'NR_CONTAINER_AGENT'; // this is the default entity guid used for the default storage instance
|
|
16
16
|
/**
|
|
17
17
|
* This layer allows multiple browser entity apps, or "target", to each have their own segregated storage instance.
|
|
18
18
|
* The purpose is so the harvester can send data to different apps within the same agent. Each feature should have a manager if it needs this capability.
|
|
@@ -28,7 +28,7 @@ class EventStoreManager {
|
|
|
28
28
|
this.agentRef = agentRef;
|
|
29
29
|
this.entityManager = agentRef.runtime.entityManager;
|
|
30
30
|
this.StorageClass = storageClass;
|
|
31
|
-
this.appStorageMap = new Map([[DEFAULT_KEY, new this.StorageClass()]]);
|
|
31
|
+
this.appStorageMap = new Map([[_agentConstants.DEFAULT_KEY, new this.StorageClass()]]);
|
|
32
32
|
this.featureName = featureName;
|
|
33
33
|
this.setEventStore(defaultEntityGuid);
|
|
34
34
|
}
|
|
@@ -38,7 +38,7 @@ class EventStoreManager {
|
|
|
38
38
|
* @param {string=} targetEntityGuid the lookup
|
|
39
39
|
* @returns {*} ALWAYS returns a storage instance
|
|
40
40
|
*/
|
|
41
|
-
#getEventStore(targetEntityGuid = DEFAULT_KEY) {
|
|
41
|
+
#getEventStore(targetEntityGuid = _agentConstants.DEFAULT_KEY) {
|
|
42
42
|
if (!this.appStorageMap.has(targetEntityGuid)) this.setEventStore(targetEntityGuid);
|
|
43
43
|
return this.appStorageMap.get(targetEntityGuid);
|
|
44
44
|
}
|
|
@@ -46,7 +46,7 @@ class EventStoreManager {
|
|
|
46
46
|
/** we should already have an event store for the default */
|
|
47
47
|
if (!targetEntityGuid) return;
|
|
48
48
|
/** if the target is the container agent, SHARE the default storage -- otherwise create a new event store */
|
|
49
|
-
const eventStorage = (0, _target.isContainerAgentTarget)(this.entityManager.get(targetEntityGuid), this.agentRef) ? this.appStorageMap.get(DEFAULT_KEY) : new this.StorageClass();
|
|
49
|
+
const eventStorage = (0, _target.isContainerAgentTarget)(this.entityManager.get(targetEntityGuid), this.agentRef) ? this.appStorageMap.get(_agentConstants.DEFAULT_KEY) : new this.StorageClass();
|
|
50
50
|
this.appStorageMap.set(targetEntityGuid, eventStorage);
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -102,6 +102,10 @@ class EventStoreManager {
|
|
|
102
102
|
}];
|
|
103
103
|
const allPayloads = [];
|
|
104
104
|
this.appStorageMap.forEach((eventStore, targetEntityGuid) => {
|
|
105
|
+
// We shouldnt harvest unless we have a valid entity guid. It was ONLY stored under the default key temporarily
|
|
106
|
+
// until a real key was returned in the RUM call. The real key SHARES the event store with the default key, and
|
|
107
|
+
// should be the key that is honored to get the event store to ensure a valid connection was made.
|
|
108
|
+
if (targetEntityGuid === _agentConstants.DEFAULT_KEY) return;
|
|
105
109
|
const targetApp = this.entityManager.get(targetEntityGuid);
|
|
106
110
|
if (targetApp) allPayloads.push({
|
|
107
111
|
targetApp,
|
|
@@ -3,14 +3,13 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.spaPrefix = exports.
|
|
6
|
+
exports.spaPrefix = exports.prefix = exports.WRAP_LOGGER = exports.START = exports.SET_USER_ID = exports.SET_PAGE_VIEW_NAME = exports.SET_ERROR_HANDLER = exports.SET_CUSTOM_ATTRIBUTE = exports.SET_CURRENT_ROUTE_NAME = exports.SET_APPLICATION_VERSION = exports.REGISTER = exports.RECORD_REPLAY = exports.RECORD_CUSTOM_EVENT = exports.PAUSE_REPLAY = exports.NOTICE_ERROR = exports.LOG = exports.INTERACTION = exports.FINISHED = exports.ADD_TO_TRACE = exports.ADD_RELEASE = exports.ADD_PAGE_ACTION = void 0;
|
|
7
7
|
/**
|
|
8
8
|
* Copyright 2020-2025 New Relic, Inc. All rights reserved.
|
|
9
9
|
* SPDX-License-Identifier: Apache-2.0
|
|
10
10
|
*/
|
|
11
11
|
const prefix = exports.prefix = 'api-';
|
|
12
12
|
const spaPrefix = exports.spaPrefix = prefix + 'ixn-';
|
|
13
|
-
const replayRunning = exports.replayRunning = {};
|
|
14
13
|
const ADD_PAGE_ACTION = exports.ADD_PAGE_ACTION = 'addPageAction';
|
|
15
14
|
const ADD_TO_TRACE = exports.ADD_TO_TRACE = 'addToTrace';
|
|
16
15
|
const ADD_RELEASE = exports.ADD_RELEASE = 'addRelease';
|
|
@@ -6,11 +6,9 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.noticeError = noticeError;
|
|
7
7
|
exports.setupNoticeErrorAPI = setupNoticeErrorAPI;
|
|
8
8
|
var _handle = require("../../common/event-emitter/handle");
|
|
9
|
-
var _constants = require("../../common/session/constants");
|
|
10
9
|
var _now = require("../../common/timing/now");
|
|
11
|
-
var _constants2 = require("../../features/session_replay/constants");
|
|
12
10
|
var _features = require("../features/features");
|
|
13
|
-
var
|
|
11
|
+
var _constants = require("./constants");
|
|
14
12
|
var _sharedHandlers = require("./sharedHandlers");
|
|
15
13
|
/**
|
|
16
14
|
* Copyright 2020-2025 New Relic, Inc. All rights reserved.
|
|
@@ -18,13 +16,9 @@ var _sharedHandlers = require("./sharedHandlers");
|
|
|
18
16
|
*/
|
|
19
17
|
|
|
20
18
|
function setupNoticeErrorAPI(agent) {
|
|
21
|
-
(0, _sharedHandlers.setupAPI)(
|
|
22
|
-
_constants3.replayRunning[agent.agentIdentifier] ??= _constants.MODE.OFF;
|
|
23
|
-
agent.ee.on(_constants2.SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, isRunning => {
|
|
24
|
-
_constants3.replayRunning[agent.agentIdentifier] = isRunning;
|
|
25
|
-
});
|
|
19
|
+
(0, _sharedHandlers.setupAPI)(_constants.NOTICE_ERROR, (err, customAttributes) => noticeError(err, customAttributes, agent), agent);
|
|
26
20
|
}
|
|
27
21
|
function noticeError(err, customAttributes, agentRef, targetEntityGuid, timestamp = (0, _now.now)()) {
|
|
28
22
|
if (typeof err === 'string') err = new Error(err);
|
|
29
|
-
(0, _handle.handle)('err', [err, timestamp, false, customAttributes,
|
|
23
|
+
(0, _handle.handle)('err', [err, timestamp, false, customAttributes, agentRef.runtime.isRecording, undefined, targetEntityGuid], undefined, _features.FEATURE_NAMES.jserrors, agentRef.ee);
|
|
30
24
|
}
|
|
@@ -64,13 +64,7 @@ function appendJsAttribute(agent, key, value, apiName, addToBrowserStorage) {
|
|
|
64
64
|
if (value === null) {
|
|
65
65
|
delete currentInfo.jsAttributes[key];
|
|
66
66
|
} else {
|
|
67
|
-
|
|
68
|
-
...agent.info,
|
|
69
|
-
jsAttributes: {
|
|
70
|
-
...currentInfo.jsAttributes,
|
|
71
|
-
[key]: value
|
|
72
|
-
}
|
|
73
|
-
};
|
|
67
|
+
currentInfo.jsAttributes[key] = value;
|
|
74
68
|
}
|
|
75
69
|
if (addToBrowserStorage || value === null) (0, _handle.handle)(_constants2.prefix + apiName, [(0, _now.now)(), key, value], undefined, 'session', agent.ee);
|
|
76
70
|
}
|
|
@@ -19,7 +19,7 @@ export function getModeledObject(obj, model) {
|
|
|
19
19
|
}
|
|
20
20
|
if (Array.isArray(obj[key]) && Array.isArray(model[key])) output[key] = Array.from(new Set([...obj[key], ...model[key]]));else if (typeof obj[key] === 'object' && typeof model[key] === 'object') output[key] = getModeledObject(obj[key], model[key]);else output[key] = obj[key];
|
|
21
21
|
} catch (e) {
|
|
22
|
-
warn(1, e);
|
|
22
|
+
if (!output[key]) warn(1, e);
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
return output;
|
|
@@ -10,7 +10,7 @@ import { BUILD_ENV, DIST_METHOD, VERSION } from "../constants/env.npm";
|
|
|
10
10
|
* Module level count of harvests. This property will auto-increment each time it is accessed.
|
|
11
11
|
* @type {number}
|
|
12
12
|
*/
|
|
13
|
-
let
|
|
13
|
+
let _harvestCount = 0;
|
|
14
14
|
const ReadOnly = {
|
|
15
15
|
buildEnv: BUILD_ENV,
|
|
16
16
|
distMethod: DIST_METHOD,
|
|
@@ -26,6 +26,8 @@ const RuntimeModel = {
|
|
|
26
26
|
entityManager: undefined,
|
|
27
27
|
harvester: undefined,
|
|
28
28
|
isolatedBacklog: false,
|
|
29
|
+
isRecording: false,
|
|
30
|
+
// true when actively recording, false when paused or stopped
|
|
29
31
|
loaderType: undefined,
|
|
30
32
|
maxBytes: 30000,
|
|
31
33
|
obfuscator: undefined,
|
|
@@ -35,7 +37,7 @@ const RuntimeModel = {
|
|
|
35
37
|
session: undefined,
|
|
36
38
|
timeKeeper: undefined,
|
|
37
39
|
get harvestCount() {
|
|
38
|
-
return ++
|
|
40
|
+
return ++_harvestCount;
|
|
39
41
|
}
|
|
40
42
|
};
|
|
41
43
|
export const mergeRuntime = runtime => {
|
|
@@ -9,7 +9,6 @@ import { FEATURE_NAME } from '../constants';
|
|
|
9
9
|
import { globalScope } from '../../../common/constants/runtime';
|
|
10
10
|
import { eventListenerOpts } from '../../../common/event-listener/event-listener-opts';
|
|
11
11
|
import { now } from '../../../common/timing/now';
|
|
12
|
-
import { SR_EVENT_EMITTER_TYPES } from '../../session_replay/constants';
|
|
13
12
|
import { castError, castErrorEvent, castPromiseRejectionEvent } from '../shared/cast-error';
|
|
14
13
|
import { setupNoticeErrorAPI } from '../../../loaders/api/noticeError';
|
|
15
14
|
import { setupSetErrorHandlerAPI } from '../../../loaders/api/setErrorHandler';
|
|
@@ -17,7 +16,6 @@ import { setupAddReleaseAPI } from '../../../loaders/api/addRelease';
|
|
|
17
16
|
import { setupRegisterAPI } from '../../../loaders/api/register';
|
|
18
17
|
export class Instrument extends InstrumentBase {
|
|
19
18
|
static featureName = FEATURE_NAME;
|
|
20
|
-
#replayRunning = false;
|
|
21
19
|
constructor(agentRef) {
|
|
22
20
|
super(agentRef, FEATURE_NAME);
|
|
23
21
|
|
|
@@ -32,20 +30,17 @@ export class Instrument extends InstrumentBase {
|
|
|
32
30
|
} catch (e) {}
|
|
33
31
|
this.ee.on('internal-error', (error, reason) => {
|
|
34
32
|
if (!this.abortHandler) return;
|
|
35
|
-
handle('ierr', [castError(error), now(), true, {},
|
|
36
|
-
});
|
|
37
|
-
this.ee.on(SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, isRunning => {
|
|
38
|
-
this.#replayRunning = isRunning;
|
|
33
|
+
handle('ierr', [castError(error), now(), true, {}, agentRef.runtime.isRecording, reason], undefined, this.featureName, this.ee);
|
|
39
34
|
});
|
|
40
35
|
globalScope.addEventListener('unhandledrejection', promiseRejectionEvent => {
|
|
41
36
|
if (!this.abortHandler) return;
|
|
42
37
|
handle('err', [castPromiseRejectionEvent(promiseRejectionEvent), now(), false, {
|
|
43
38
|
unhandledPromiseRejection: 1
|
|
44
|
-
},
|
|
39
|
+
}, agentRef.runtime.isRecording], undefined, this.featureName, this.ee);
|
|
45
40
|
}, eventListenerOpts(false, this.removeOnAbort?.signal));
|
|
46
41
|
globalScope.addEventListener('error', errorEvent => {
|
|
47
42
|
if (!this.abortHandler) return;
|
|
48
|
-
handle('err', [castErrorEvent(errorEvent), now(), false, {},
|
|
43
|
+
handle('err', [castErrorEvent(errorEvent), now(), false, {}, agentRef.runtime.isRecording], undefined, this.featureName, this.ee);
|
|
49
44
|
}, eventListenerOpts(false, this.removeOnAbort?.signal));
|
|
50
45
|
this.abortHandler = this.#abort; // we also use this as a flag to denote that the feature is active or on and handling errors
|
|
51
46
|
this.importAggregator(agentRef, () => import(/* webpackChunkName: "jserrors-aggregate" */'../aggregate'));
|
|
@@ -90,7 +90,7 @@ export class Aggregate extends AggregateBase {
|
|
|
90
90
|
this.entitled = !!entitled;
|
|
91
91
|
if (!this.entitled) {
|
|
92
92
|
this.deregisterDrain();
|
|
93
|
-
if (this.
|
|
93
|
+
if (this.agentRef.runtime.isRecording) {
|
|
94
94
|
this.abort(ABORT_REASONS.ENTITLEMENTS);
|
|
95
95
|
this.reportSupportabilityMetric('SessionReplay/EnabledNotEntitled/Detected');
|
|
96
96
|
}
|
|
@@ -131,7 +131,7 @@ export class Aggregate extends AggregateBase {
|
|
|
131
131
|
this.mode = MODE.FULL;
|
|
132
132
|
// if the error was noticed AFTER the recorder was already imported....
|
|
133
133
|
if (this.recorder && this.initialized) {
|
|
134
|
-
if (!this.
|
|
134
|
+
if (!this.agentRef.runtime.isRecording) this.recorder.startRecording();
|
|
135
135
|
this.syncWithSessionManager({
|
|
136
136
|
sessionReplayMode: this.mode
|
|
137
137
|
});
|
|
@@ -161,7 +161,7 @@ export class Aggregate extends AggregateBase {
|
|
|
161
161
|
timeKeeper
|
|
162
162
|
} = this.agentRef.runtime;
|
|
163
163
|
this.timeKeeper = timeKeeper;
|
|
164
|
-
if (this.recorder?.parent.trigger === TRIGGERS.API && this.
|
|
164
|
+
if (this.recorder?.parent.trigger === TRIGGERS.API && this.agentRef.runtime.isRecording) {
|
|
165
165
|
this.mode = MODE.FULL;
|
|
166
166
|
} else if (!session.isNew && !ignoreSession) {
|
|
167
167
|
// inherit the mode of the existing session
|
|
@@ -199,7 +199,7 @@ export class Aggregate extends AggregateBase {
|
|
|
199
199
|
this.prepUtils().then(() => this.agentRef.runtime.harvester.triggerHarvestFor(this));
|
|
200
200
|
}
|
|
201
201
|
await this.prepUtils();
|
|
202
|
-
if (!this.
|
|
202
|
+
if (!this.agentRef.runtime.isRecording) this.recorder.startRecording();
|
|
203
203
|
this.syncWithSessionManager({
|
|
204
204
|
sessionReplayMode: this.mode
|
|
205
205
|
});
|
|
@@ -8,7 +8,6 @@ export const FEATURE_NAME = FEATURE_NAMES.sessionReplay;
|
|
|
8
8
|
export const SR_EVENT_EMITTER_TYPES = {
|
|
9
9
|
RECORD: 'recordReplay',
|
|
10
10
|
PAUSE: 'pauseReplay',
|
|
11
|
-
REPLAY_RUNNING: 'replayRunning',
|
|
12
11
|
ERROR_DURING_REPLAY: 'errorDuringReplay'
|
|
13
12
|
};
|
|
14
13
|
export const AVG_COMPRESSION = 0.12;
|