@newrelic/browser-agent 1.256.1 → 1.257.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 +16 -0
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/harvest/harvest.js +7 -5
- package/dist/cjs/features/jserrors/aggregate/index.js +25 -12
- package/dist/cjs/features/session_replay/aggregate/index.js +11 -5
- package/dist/cjs/features/session_replay/constants.js +2 -1
- package/dist/cjs/features/session_replay/instrument/index.js +15 -5
- package/dist/cjs/features/session_replay/shared/recorder.js +6 -3
- package/dist/cjs/features/session_replay/shared/utils.js +6 -5
- package/dist/cjs/features/soft_navigations/aggregate/index.js +2 -2
- package/dist/cjs/features/utils/instrument-base.js +11 -14
- package/dist/cjs/features/utils/nr1-debugger.js +27 -0
- package/dist/cjs/loaders/agent.js +4 -0
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/common/harvest/harvest.js +7 -5
- package/dist/esm/features/jserrors/aggregate/index.js +25 -12
- package/dist/esm/features/session_replay/aggregate/index.js +11 -5
- package/dist/esm/features/session_replay/constants.js +2 -1
- package/dist/esm/features/session_replay/instrument/index.js +16 -6
- package/dist/esm/features/session_replay/shared/recorder.js +6 -3
- package/dist/esm/features/session_replay/shared/utils.js +5 -3
- package/dist/esm/features/soft_navigations/aggregate/index.js +2 -2
- package/dist/esm/features/utils/instrument-base.js +11 -14
- package/dist/esm/features/utils/nr1-debugger.js +21 -0
- package/dist/esm/loaders/agent.js +4 -0
- package/dist/types/common/harvest/harvest.d.ts +1 -1
- package/dist/types/common/harvest/harvest.d.ts.map +1 -1
- package/dist/types/features/jserrors/aggregate/index.d.ts +0 -1
- package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/aggregate/index.d.ts +0 -1
- package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/constants.d.ts +1 -0
- package/dist/types/features/session_replay/constants.d.ts.map +1 -1
- package/dist/types/features/session_replay/instrument/index.d.ts +1 -0
- package/dist/types/features/session_replay/instrument/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/shared/recorder.d.ts +1 -0
- package/dist/types/features/session_replay/shared/recorder.d.ts.map +1 -1
- package/dist/types/features/session_replay/shared/utils.d.ts +2 -2
- package/dist/types/features/session_replay/shared/utils.d.ts.map +1 -1
- package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
- package/dist/types/features/utils/nr1-debugger.d.ts +2 -0
- package/dist/types/features/utils/nr1-debugger.d.ts.map +1 -0
- package/dist/types/loaders/agent.d.ts +5 -1
- package/dist/types/loaders/agent.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/common/drain/__mocks__/drain.js +2 -0
- package/src/common/harvest/harvest.js +8 -6
- package/src/common/window/__mocks__/load.js +3 -0
- package/src/features/jserrors/aggregate/index.js +26 -10
- package/src/features/session_replay/aggregate/index.js +17 -7
- package/src/features/session_replay/constants.js +2 -1
- package/src/features/session_replay/instrument/index.js +16 -6
- package/src/features/session_replay/shared/__mocks__/utils.js +2 -0
- package/src/features/session_replay/shared/recorder.js +7 -4
- package/src/features/session_replay/shared/utils.js +5 -3
- package/src/features/soft_navigations/aggregate/index.js +2 -2
- package/src/features/utils/__mocks__/agent-session.js +1 -0
- package/src/features/utils/__mocks__/feature-base.js +11 -0
- package/src/features/utils/instrument-base.js +11 -14
- package/src/features/utils/nr1-debugger.js +22 -0
- package/src/loaders/agent.js +4 -0
|
@@ -9,9 +9,10 @@
|
|
|
9
9
|
* It is not production ready, and is not intended to be imported or implemented in any build of the browser agent until
|
|
10
10
|
* functionality is validated and a full user experience is curated.
|
|
11
11
|
*/
|
|
12
|
+
import { handle } from '../../../common/event-emitter/handle';
|
|
12
13
|
import { DEFAULT_KEY, MODE, PREFIX } from '../../../common/session/constants';
|
|
13
14
|
import { InstrumentBase } from '../../utils/instrument-base';
|
|
14
|
-
import { FEATURE_NAME } from '../constants';
|
|
15
|
+
import { FEATURE_NAME, SR_EVENT_EMITTER_TYPES } from '../constants';
|
|
15
16
|
import { isPreloadAllowed } from '../shared/utils';
|
|
16
17
|
export class Instrument extends InstrumentBase {
|
|
17
18
|
static featureName = FEATURE_NAME;
|
|
@@ -19,19 +20,28 @@ export class Instrument extends InstrumentBase {
|
|
|
19
20
|
let auto = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
|
|
20
21
|
super(agentIdentifier, aggregator, FEATURE_NAME, auto);
|
|
21
22
|
let session;
|
|
23
|
+
this.replayRunning = false;
|
|
22
24
|
try {
|
|
23
25
|
session = JSON.parse(localStorage.getItem("".concat(PREFIX, "_").concat(DEFAULT_KEY)));
|
|
24
26
|
} catch (err) {}
|
|
25
27
|
if (this.#canPreloadRecorder(session)) {
|
|
26
|
-
/** If this is preloaded, set up a buffer, if not, later when sampling we will set up a .on for live events */
|
|
27
|
-
this.ee.on('err', e => {
|
|
28
|
-
this.errorNoticed = true;
|
|
29
|
-
if (this.featAggregate) this.featAggregate.handleError();
|
|
30
|
-
});
|
|
31
28
|
this.#startRecording(session?.sessionReplayMode);
|
|
32
29
|
} else {
|
|
33
30
|
this.importAggregator();
|
|
34
31
|
}
|
|
32
|
+
|
|
33
|
+
/** If the recorder is running, we can pass error events on to the agg to help it switch to full mode later */
|
|
34
|
+
this.ee.on('err', e => {
|
|
35
|
+
if (this.replayRunning) {
|
|
36
|
+
this.errorNoticed = true;
|
|
37
|
+
handle(SR_EVENT_EMITTER_TYPES.ERROR_DURING_REPLAY, [e], undefined, this.featureName, this.ee);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
/** Emitted by the recorder when it starts capturing data, used to determine if we should pass errors on to the agg */
|
|
42
|
+
this.ee.on(SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, isRunning => {
|
|
43
|
+
this.replayRunning = isRunning;
|
|
44
|
+
});
|
|
35
45
|
}
|
|
36
46
|
|
|
37
47
|
// At this point wherein session state exists already but we haven't init SessionEntity aka verify timers.
|
|
@@ -97,11 +97,10 @@ export class Recorder {
|
|
|
97
97
|
collectFonts: collect_fonts,
|
|
98
98
|
checkoutEveryNms: CHECKOUT_MS[this.parent.mode]
|
|
99
99
|
});
|
|
100
|
-
this.parent.ee.emit(SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, [true, this.parent.mode]);
|
|
101
100
|
this.stopRecording = () => {
|
|
102
101
|
this.recording = false;
|
|
103
102
|
this.parent.ee.emit(SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, [false, this.parent.mode]);
|
|
104
|
-
stop();
|
|
103
|
+
stop?.();
|
|
105
104
|
};
|
|
106
105
|
}
|
|
107
106
|
|
|
@@ -144,6 +143,10 @@ export class Recorder {
|
|
|
144
143
|
if (!event) return;
|
|
145
144
|
if (!this.parent.scheduler && this.#preloaded.length) this.currentBufferTarget = this.#preloaded[this.#preloaded.length - 1];else this.currentBufferTarget = this.#events;
|
|
146
145
|
if (this.parent.blocked) return;
|
|
146
|
+
if (!this.notified) {
|
|
147
|
+
this.parent.ee.emit(SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, [true, this.parent.mode]);
|
|
148
|
+
this.notified = true;
|
|
149
|
+
}
|
|
147
150
|
if (this.parent.timeKeeper?.ready && !event.__newrelic) {
|
|
148
151
|
event.__newrelic = buildNRMetaNode(event.timestamp, this.parent.timeKeeper);
|
|
149
152
|
event.timestamp = this.parent.timeKeeper.correctAbsoluteTimestamp(event.timestamp);
|
|
@@ -174,7 +177,7 @@ export class Recorder {
|
|
|
174
177
|
|
|
175
178
|
// We are making an effort to try to keep payloads manageable for unloading. If they reach the unload limit before their interval,
|
|
176
179
|
// it will send immediately. This often happens on the first snapshot, which can be significantly larger than the other payloads.
|
|
177
|
-
if (payloadSize > IDEAL_PAYLOAD_SIZE && this.parent.mode
|
|
180
|
+
if ((event.type === RRWEB_EVENT_TYPES.FullSnapshot && this.currentBufferTarget.hasMeta || payloadSize > IDEAL_PAYLOAD_SIZE) && this.parent.mode === MODE.FULL) {
|
|
178
181
|
// if we've made it to the ideal size of ~64kb before the interval timer, we should send early.
|
|
179
182
|
if (this.parent.scheduler) {
|
|
180
183
|
this.parent.scheduler.runHarvest();
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { getConfigurationValue, originals } from '../../../common/config/config';
|
|
2
2
|
import { isBrowserScope } from '../../../common/constants/runtime';
|
|
3
|
-
export
|
|
3
|
+
export function enableSessionTracking(agentId) {
|
|
4
|
+
return isBrowserScope && getConfigurationValue(agentId, 'privacy.cookies_enabled') === true;
|
|
5
|
+
}
|
|
4
6
|
function hasReplayPrerequisite(agentId) {
|
|
5
|
-
return originals.MO &&
|
|
7
|
+
return !!originals.MO &&
|
|
6
8
|
// Session Replay cannot work without Mutation Observer
|
|
7
|
-
enableSessionTracking &&
|
|
9
|
+
enableSessionTracking(agentId) &&
|
|
8
10
|
// requires session tracking to be running (hence "session" replay...)
|
|
9
11
|
getConfigurationValue(agentId, 'session_trace.enabled') === true; // Session Replay as of now is tightly coupled with Session Trace in the UI
|
|
10
12
|
}
|
|
@@ -82,7 +82,7 @@ export class Aggregate extends AggregateBase {
|
|
|
82
82
|
if (!firstIxnStartTime) firstIxnStartTime = Math.floor(interaction.start);
|
|
83
83
|
}
|
|
84
84
|
const payload = "bel.7;".concat(serializedIxnList.join(';'));
|
|
85
|
-
if (options.retry) this.interactionsAwaitingRetry
|
|
85
|
+
if (options.retry) this.interactionsAwaitingRetry = this.interactionsToHarvest;
|
|
86
86
|
this.interactionsToHarvest = [];
|
|
87
87
|
return {
|
|
88
88
|
body: {
|
|
@@ -93,8 +93,8 @@ export class Aggregate extends AggregateBase {
|
|
|
93
93
|
onHarvestFinished(result) {
|
|
94
94
|
if (result.sent && result.retry && this.interactionsAwaitingRetry.length > 0) {
|
|
95
95
|
this.interactionsToHarvest = [...this.interactionsAwaitingRetry, ...this.interactionsToHarvest];
|
|
96
|
-
this.interactionsAwaitingRetry = [];
|
|
97
96
|
}
|
|
97
|
+
this.interactionsAwaitingRetry = [];
|
|
98
98
|
}
|
|
99
99
|
startUIInteraction(eventName, startedAt, sourceElem) {
|
|
100
100
|
// this is throttled by instrumentation so that it isn't excessively called
|
|
@@ -12,6 +12,7 @@ import { warn } from '../../common/util/console';
|
|
|
12
12
|
import { FEATURE_NAMES } from '../../loaders/features/features';
|
|
13
13
|
import { getConfigurationValue } from '../../common/config/config';
|
|
14
14
|
import { canImportReplayAgg, enableSessionTracking } from '../session_replay/shared/utils';
|
|
15
|
+
import { single } from '../../common/util/invoke';
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* Base class for instrumenting a feature.
|
|
@@ -50,7 +51,15 @@ export class InstrumentBase extends FeatureBase {
|
|
|
50
51
|
/** used in conjunction with newrelic.start() to defer harvesting in features */
|
|
51
52
|
if (getConfigurationValue(this.agentIdentifier, "".concat(this.featureName, ".autoStart")) === false) this.auto = false;
|
|
52
53
|
/** if the feature requires opt-in (!auto-start), it will get registered once the api has been called */
|
|
53
|
-
if (this.auto) registerDrain(agentIdentifier, featureName);
|
|
54
|
+
if (this.auto) registerDrain(agentIdentifier, featureName);else {
|
|
55
|
+
this.ee.on("".concat(this.featureName, "-opt-in"), single(() => {
|
|
56
|
+
// register the feature to drain only once the API has been called, it will drain when importAggregator finishes for all the features
|
|
57
|
+
// called by the api in that cycle
|
|
58
|
+
registerDrain(this.agentIdentifier, this.featureName);
|
|
59
|
+
this.auto = true;
|
|
60
|
+
this.importAggregator();
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
54
63
|
}
|
|
55
64
|
|
|
56
65
|
/**
|
|
@@ -61,19 +70,7 @@ export class InstrumentBase extends FeatureBase {
|
|
|
61
70
|
*/
|
|
62
71
|
importAggregator() {
|
|
63
72
|
let argsObjFromInstrument = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
64
|
-
if (this.featAggregate) return;
|
|
65
|
-
if (!this.auto) {
|
|
66
|
-
// this feature requires an opt in...
|
|
67
|
-
// wait for API to be called
|
|
68
|
-
this.ee.on("".concat(this.featureName, "-opt-in"), () => {
|
|
69
|
-
// register the feature to drain only once the API has been called, it will drain when importAggregator finishes for all the features
|
|
70
|
-
// called by the api in that cycle
|
|
71
|
-
registerDrain(this.agentIdentifier, this.featureName);
|
|
72
|
-
this.auto = true;
|
|
73
|
-
this.importAggregator();
|
|
74
|
-
});
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
73
|
+
if (this.featAggregate || !this.auto) return;
|
|
77
74
|
let loadedSuccessfully;
|
|
78
75
|
this.onAggregateImported = new Promise(resolve => {
|
|
79
76
|
loadedSuccessfully = resolve;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { gosCDN } from '../../common/window/nreum';
|
|
2
|
+
const debugId = 1;
|
|
3
|
+
const newrelic = gosCDN();
|
|
4
|
+
export function debugNR1(agentIdentifier, location, event) {
|
|
5
|
+
let otherprops = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
|
|
6
|
+
let debugName = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 'SR';
|
|
7
|
+
const api = agentIdentifier ? newrelic.initializedAgents[agentIdentifier].api.addPageAction : newrelic.addPageAction;
|
|
8
|
+
let url;
|
|
9
|
+
try {
|
|
10
|
+
const locURL = new URL(window.location);
|
|
11
|
+
url = locURL.pathname;
|
|
12
|
+
} catch (err) {}
|
|
13
|
+
api(debugName, {
|
|
14
|
+
debugId,
|
|
15
|
+
url,
|
|
16
|
+
location,
|
|
17
|
+
event,
|
|
18
|
+
now: performance.now(),
|
|
19
|
+
...otherprops
|
|
20
|
+
});
|
|
21
|
+
}
|
|
@@ -21,6 +21,10 @@ import { globalScope } from '../common/constants/runtime';
|
|
|
21
21
|
* sensitive to network load, this may result in smaller builds with slightly lower performance impact.
|
|
22
22
|
*/
|
|
23
23
|
export class Agent extends AgentBase {
|
|
24
|
+
/**
|
|
25
|
+
* @param {Object} options Options to initialize agent with
|
|
26
|
+
* @param {string} [agentIdentifier] Optional identifier of agent
|
|
27
|
+
*/
|
|
24
28
|
constructor(options, agentIdentifier) {
|
|
25
29
|
super(agentIdentifier);
|
|
26
30
|
if (!globalScope) {
|
|
@@ -34,7 +34,7 @@ export class Harvest extends SharedContext {
|
|
|
34
34
|
* value should not be relied upon because network calls will be made asynchronously.
|
|
35
35
|
*/
|
|
36
36
|
_send({ endpoint, payload, opts, submitMethod, cbFinished, customUrl, raw, includeBaseParams }: NetworkSendSpec): boolean;
|
|
37
|
-
baseQueryString(qs: any): string;
|
|
37
|
+
baseQueryString(qs: any, endpoint: any): string;
|
|
38
38
|
/**
|
|
39
39
|
* Calls and accumulates data from registered harvesting functions based on
|
|
40
40
|
* the endpoint being harvested.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"harvest.d.ts","sourceRoot":"","sources":["../../../../src/common/harvest/harvest.js"],"names":[],"mappings":"AAsBA;;;;;;GAMG;AACH;IAII,0BAA2H;IAC3H,uBAAoD;IAEpD,YAAiB;IAGnB;;;;;OAKG;IACH,aAFW,eAAe,WAWzB;IAED;;;OAGG;IACH,YAFW,eAAe,WAMzB;IAED;;;OAGG;IACH,wBAFW,eAAe,WAMzB;IAED;;;;;;OAMG;IACH,gGAJW,eAAe,GACb,OAAO,CAsFnB;IAGD,
|
|
1
|
+
{"version":3,"file":"harvest.d.ts","sourceRoot":"","sources":["../../../../src/common/harvest/harvest.js"],"names":[],"mappings":"AAsBA;;;;;;GAMG;AACH;IAII,0BAA2H;IAC3H,uBAAoD;IAEpD,YAAiB;IAGnB;;;;;OAKG;IACH,aAFW,eAAe,WAWzB;IAED;;;OAGG;IACH,YAFW,eAAe,WAMzB;IAED;;;OAGG;IACH,wBAFW,eAAe,WAMzB;IAED;;;;;;OAMG;IACH,gGAJW,eAAe,GACb,OAAO,CAsFnB;IAGD,gDAsBC;IAED;;;;;;;OAOG;IACH,wBALW,yBAAyB,WACzB,6BAA6B,GAE3B,cAAc,CA2B1B;IAED;;;;;;;OAOG;IACH,uBAHW,cAAc,GACZ,cAAc,CAuB1B;IAED;;;;;OAKG;IACH,aAHW,yBAAyB,YACzB,sBAAsB,QAQhC;CACF;8BAzPY,OAAO,YAAY,EAAE,eAAe;wCACpC,OAAO,YAAY,EAAE,yBAAyB;6BAC9C,OAAO,YAAY,EAAE,cAAc;qCACnC,OAAO,YAAY,EAAE,sBAAsB;4CAC3C,OAAO,YAAY,EAAE,6BAA6B;8BAbjC,2BAA2B;2BAF9B,mBAAmB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/jserrors/aggregate/index.js"],"names":[],"mappings":"AAyBA;;GAEG;AAEH;IACE,2BAAiC;IACjC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/jserrors/aggregate/index.js"],"names":[],"mappings":"AAyBA;;GAEG;AAEH;IACE,2BAAiC;IACjC,mDAgCC;IA7BC,kBAAuB;IACvB,eAAoB;IACpB,qBAA0B;IAC1B,2BAAgC;IAChC,mCAA4B;IAC5B,qBAAwB;IA0B1B;;;MAuBC;IAED,qCAWC;IAED,8BAEC;IAED,oEAMC;IAED;;;;;;OAMG;IACH,qCAHW,SAAS,GACP,MAAM,CAgBlB;IAED,4FAsFC;IA4BD,yDA6BC;IAED,qFAOC;;CAwBF;wBAhSY,OAAO,0BAA0B,EAAE,SAAS;8BAN3B,4BAA4B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_replay/aggregate/index.js"],"names":[],"mappings":"AAgCA;IACE,2BAAiC;IAIjC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_replay/aggregate/index.js"],"names":[],"mappings":"AAgCA;IACE,2BAAiC;IAIjC,8DAiHC;IApHD,aAAe;IAKb,8GAA8G;IAC9G,wBAAgH;IAChH,iFAAiF;IACjF,qBAAwB;IAGxB,2CAA2C;IAC3C,sDAAwB;IACxB,6CAA6C;IAC7C,gDAAmB;IAEnB,0BAA0B;IAC1B,kBAAqB;IACrB,6CAA6C;IAC7C,gBAA2B;IAE3B,cAA8B;IAC9B,kBAA+C;IAoC/C,4BAKQ;IAuDV,0BAMC;IAED,qBAWC;IAED;;;;;;;OAOG;IACH,iCALW,OAAO,cACP,OAAO,iBACP,OAAO,GACL,IAAI,CA6DhB;IAED,2BASC;IAED;;;;;;;;;;;;oBAiDC;IAED,sCAIC;IAED;;;;;;;;;;MAoEC;IAED,qCAOC;IAED;;;;OAIG;IACH,mCAKC;IAED,yDAAyD;IACzD,yBAUC;IAED,yCAGC;CACF;8BAlZ6B,4BAA4B;iCAHzB,2CAA2C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/features/session_replay/constants.js"],"names":[],"mappings":"AAGA,kCAAuD
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/features/session_replay/constants.js"],"names":[],"mappings":"AAGA,kCAAuD;;;;;;;AASvD,mCAAmC;;;;;;;;;AASnC,uCAAuC;AACvC,uCAAuC;AACvC,iCAAiC;AACjC,uCAAuC;AACvC,2GAA2G;AAC3G;;EAAsF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BtF,0CAA0C;AAC1C,uCAAuC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export class Instrument extends InstrumentBase {
|
|
2
2
|
static featureName: string;
|
|
3
3
|
constructor(agentIdentifier: any, aggregator: any, auto?: boolean);
|
|
4
|
+
replayRunning: boolean;
|
|
4
5
|
errorNoticed: boolean;
|
|
5
6
|
recorder: import("../shared/recorder").Recorder | undefined;
|
|
6
7
|
#private;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_replay/instrument/index.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_replay/instrument/index.js"],"names":[],"mappings":"AAiBA;IACE,2BAAiC;IACjC,mEA0BC;IAvBC,uBAA0B;IActB,sBAAwB;IA0B5B,4DAA0F;;CAK7F;+BAtD8B,6BAA6B"}
|
|
@@ -37,6 +37,7 @@ export class Recorder {
|
|
|
37
37
|
audit(event: any, isCheckout: any): void;
|
|
38
38
|
/** Store a payload in the buffer (this.#events). This should be the callback to the recording lib noticing a mutation */
|
|
39
39
|
store(event: any, isCheckout: any): void;
|
|
40
|
+
notified: boolean | undefined;
|
|
40
41
|
/** force the recording lib to take a full DOM snapshot. This needs to occur in certain cases, like visibility changes */
|
|
41
42
|
takeFullSnapshot(): void;
|
|
42
43
|
clearTimestamps(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_replay/shared/recorder.js"],"names":[],"mappings":"AAYA;IAUE,yBAkBC;IAdC,iEAAiE;IACjE,mBAAsB;IACtB,6DAA6D;IAC7D,oCAAuC;IACvC,kIAAkI;IAClI,kBAAqB;IACrB,sDAAsD;IACtD,YAAoB;IACpB,oEAAoE;IACpE,6BAAqH;IACrH,0FAA0F;IAC1F,eAA6C;IAC7C,uIAAuI;IACvI,0BAAyE;IAG3E;;;;;;;;;MAYC;IAED,mFAAmF;IACnF,oBAKC;IAED,qDAAqD;IACrD,
|
|
1
|
+
{"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_replay/shared/recorder.js"],"names":[],"mappings":"AAYA;IAUE,yBAkBC;IAdC,iEAAiE;IACjE,mBAAsB;IACtB,6DAA6D;IAC7D,oCAAuC;IACvC,kIAAkI;IAClI,kBAAqB;IACrB,sDAAsD;IACtD,YAAoB;IACpB,oEAAoE;IACpE,6BAAqH;IACrH,0FAA0F;IAC1F,eAA6C;IAC7C,uIAAuI;IACvI,0BAAyE;IAG3E;;;;;;;;;MAYC;IAED,mFAAmF;IACnF,oBAKC;IAED,qDAAqD;IACrD,uBA+BC;IAED;;;;;OAKG;IACH,yCA0BC;IAED,0HAA0H;IAC1H,yCAqDC;IA3CG,8BAAoB;IA6CxB,0HAA0H;IAC1H,yBAOC;IAED,wBAEC;IAED,gCAAgC;IAChC,uCAGC;IAED;;;SAGK;IACL,oCAGC;;CACF;+BApN8B,mBAAmB"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export function
|
|
1
|
+
export function enableSessionTracking(agentId: any): boolean;
|
|
2
|
+
export function isPreloadAllowed(agentId: any): boolean;
|
|
2
3
|
export function canImportReplayAgg(agentId: any, sessionMgr: any): boolean;
|
|
3
4
|
export function buildNRMetaNode(timestamp: any, timeKeeper: any): {
|
|
4
5
|
originalTimestamp: any;
|
|
@@ -8,5 +9,4 @@ export function buildNRMetaNode(timestamp: any, timeKeeper: any): {
|
|
|
8
9
|
timeKeeperCorrectedOriginTime: any;
|
|
9
10
|
timeKeeperDiff: number;
|
|
10
11
|
};
|
|
11
|
-
export function enableSessionTracking(agentId: any): boolean;
|
|
12
12
|
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_replay/shared/utils.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_replay/shared/utils.js"],"names":[],"mappings":"AAGA,6DAEC;AAQD,wDAEC;AAED,2EAGC;AAED;;;;;;;EAUC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrument-base.d.ts","sourceRoot":"","sources":["../../../../src/features/utils/instrument-base.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"instrument-base.d.ts","sourceRoot":"","sources":["../../../../src/features/utils/instrument-base.js"],"names":[],"mappings":"AAgBA;;;GAGG;AACH;IACE;;;;;;;;OAQG;IACH,6BAPW,MAAM,cACN,OAAO,mCAAmC,EAAE,UAAU,eACtD,MAAM,8BAqChB;IA9BC,cAAgB;IAEhB,8IAA8I;IAC9I,cADW,WAAW,SAAS,CACF;IAE7B;;;MAGE;IACF,eAHU,OAAO,kBAAkB,EAAE,aAAa,CAGpB;IAE9B;;;MAGE;IACF,kCAAoC;IAiBtC;;;;;OAKG;IACH,mEAgDC;;CAYF;4BA3H2B,gBAAgB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nr1-debugger.d.ts","sourceRoot":"","sources":["../../../../src/features/utils/nr1-debugger.js"],"names":[],"mappings":"AAIA,qHAiBC"}
|
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
* sensitive to network load, this may result in smaller builds with slightly lower performance impact.
|
|
4
4
|
*/
|
|
5
5
|
export class Agent extends AgentBase {
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* @param {Object} options Options to initialize agent with
|
|
8
|
+
* @param {string} [agentIdentifier] Optional identifier of agent
|
|
9
|
+
*/
|
|
10
|
+
constructor(options: Object, agentIdentifier?: string | undefined);
|
|
7
11
|
sharedAggregator: Aggregator | undefined;
|
|
8
12
|
features: {} | undefined;
|
|
9
13
|
desiredFeatures: Set<any> | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../../src/loaders/agent.js"],"names":[],"mappings":"AAkBA;;;GAGG;AACH;IACE,
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../../src/loaders/agent.js"],"names":[],"mappings":"AAkBA;;;GAGG;AACH;IACE;;;OAGG;IACH,qBAHW,MAAM,wCA2BhB;IAdC,yCAAiF;IACjF,yBAAkB;IAGlB,sCAAsD;IAMtD,uCAA6G;IAM/G;;;;;MAOC;IAED,yBAiCC;CACF;0BA5FyB,cAAc;2BAQb,gCAAgC"}
|
package/package.json
CHANGED
|
@@ -103,7 +103,7 @@ export class Harvest extends SharedContext {
|
|
|
103
103
|
if (customUrl) url = customUrl
|
|
104
104
|
if (raw) url = `${protocol}://${perceviedBeacon}/${endpoint}`
|
|
105
105
|
|
|
106
|
-
const baseParams = !raw && includeBaseParams ? this.baseQueryString(qs) : ''
|
|
106
|
+
const baseParams = !raw && includeBaseParams ? this.baseQueryString(qs, endpoint) : ''
|
|
107
107
|
let payloadParams = encodeObj(qs, agentRuntime.maxBytes)
|
|
108
108
|
if (!submitMethod) {
|
|
109
109
|
submitMethod = submitData.getSubmitMethod({ isFinalHarvest: opts.unload })
|
|
@@ -167,14 +167,15 @@ export class Harvest extends SharedContext {
|
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
// The stuff that gets sent every time.
|
|
170
|
-
baseQueryString (qs) {
|
|
170
|
+
baseQueryString (qs, endpoint) {
|
|
171
171
|
const runtime = getRuntime(this.sharedContext.agentIdentifier)
|
|
172
172
|
const info = getInfo(this.sharedContext.agentIdentifier)
|
|
173
173
|
|
|
174
174
|
const location = cleanURL(getLocation())
|
|
175
175
|
const ref = this.obfuscator.shouldObfuscate() ? this.obfuscator.obfuscateString(location) : location
|
|
176
|
+
const hr = runtime?.session?.state.sessionReplayMode === 1 && endpoint !== 'jserrors'
|
|
176
177
|
|
|
177
|
-
|
|
178
|
+
const qps = [
|
|
178
179
|
'a=' + info.applicationID,
|
|
179
180
|
encodeParam('sa', (info.sa ? '' + info.sa : '')),
|
|
180
181
|
encodeParam('v', VERSION),
|
|
@@ -184,9 +185,10 @@ export class Harvest extends SharedContext {
|
|
|
184
185
|
'&ck=0', // ck param DEPRECATED - still expected by backend
|
|
185
186
|
'&s=' + (runtime.session?.state.value || '0'), // the 0 id encaps all untrackable and default traffic
|
|
186
187
|
encodeParam('ref', ref),
|
|
187
|
-
encodeParam('ptid', (runtime.ptid ? '' + runtime.ptid : ''))
|
|
188
|
-
|
|
189
|
-
|
|
188
|
+
encodeParam('ptid', (runtime.ptid ? '' + runtime.ptid : ''))
|
|
189
|
+
]
|
|
190
|
+
if (hr) qps.push(encodeParam('hr', '1', qs))
|
|
191
|
+
return qps.join('')
|
|
190
192
|
}
|
|
191
193
|
|
|
192
194
|
/**
|
|
@@ -38,13 +38,10 @@ export class Aggregate extends AggregateBase {
|
|
|
38
38
|
this.bufferedErrorsUnderSpa = {}
|
|
39
39
|
this.currentBody = undefined
|
|
40
40
|
this.errorOnPage = false
|
|
41
|
-
this.replayAborted = false
|
|
42
41
|
|
|
43
42
|
// this will need to change to match whatever ee we use in the instrument
|
|
44
43
|
this.ee.on('interactionDone', (interaction, wasSaved) => this.onInteractionDone(interaction, wasSaved))
|
|
45
44
|
|
|
46
|
-
this.ee.on('REPLAY_ABORTED', () => { this.replayAborted = true })
|
|
47
|
-
|
|
48
45
|
register('err', (...args) => this.storeError(...args), this.featureName, this.ee)
|
|
49
46
|
register('ierr', (...args) => this.storeError(...args), this.featureName, this.ee)
|
|
50
47
|
register('softNavFlush', (interactionId, wasFinished, softNavAttrs) =>
|
|
@@ -82,11 +79,7 @@ export class Aggregate extends AggregateBase {
|
|
|
82
79
|
}
|
|
83
80
|
|
|
84
81
|
if (body && body.err && body.err.length) {
|
|
85
|
-
|
|
86
|
-
body.err.forEach((e) => {
|
|
87
|
-
delete e.params?.hasReplay
|
|
88
|
-
})
|
|
89
|
-
}
|
|
82
|
+
this.#runCrossFeatureChecks(body.err)
|
|
90
83
|
if (!this.errorOnPage) {
|
|
91
84
|
payload.qs.pve = '1'
|
|
92
85
|
this.errorOnPage = true
|
|
@@ -172,13 +165,14 @@ export class Aggregate extends AggregateBase {
|
|
|
172
165
|
// Do not modify the name ('errorGroup') of params without DEM approval!
|
|
173
166
|
if (filterOutput?.group) params.errorGroup = filterOutput.group
|
|
174
167
|
|
|
168
|
+
if (hasReplay) params.hasReplay = hasReplay
|
|
175
169
|
/**
|
|
176
170
|
* The bucketHash is different from the params.stackHash because the params.stackHash is based on the canonicalized
|
|
177
171
|
* stack trace and is used downstream in NR1 to attempt to group the same errors across different browsers. However,
|
|
178
172
|
* the canonical stack trace excludes items like the column number increasing the hit-rate of different errors potentially
|
|
179
173
|
* bucketing and ultimately resulting in the loss of data in NR1.
|
|
180
174
|
*/
|
|
181
|
-
var bucketHash = stringHashCode(`${stackInfo.name}_${stackInfo.message}_${stackInfo.stackString}`)
|
|
175
|
+
var bucketHash = stringHashCode(`${stackInfo.name}_${stackInfo.message}_${stackInfo.stackString}_${params.hasReplay ? 1 : 0}`)
|
|
182
176
|
|
|
183
177
|
if (!this.stackReported[bucketHash]) {
|
|
184
178
|
this.stackReported[bucketHash] = true
|
|
@@ -199,7 +193,6 @@ export class Aggregate extends AggregateBase {
|
|
|
199
193
|
this.pageviewReported[bucketHash] = true
|
|
200
194
|
}
|
|
201
195
|
|
|
202
|
-
if (hasReplay && !this.replayAborted) params.hasReplay = hasReplay
|
|
203
196
|
params.firstOccurrenceTimestamp = this.observedAt[bucketHash]
|
|
204
197
|
params.timestamp = this.observedAt[bucketHash]
|
|
205
198
|
|
|
@@ -296,4 +289,27 @@ export class Aggregate extends AggregateBase {
|
|
|
296
289
|
)
|
|
297
290
|
delete this.bufferedErrorsUnderSpa[interactionId] // wipe the list of jserrors so they aren't duplicated by another call to the same id
|
|
298
291
|
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Dispatches a cross-feature communication event to allow other
|
|
295
|
+
* features to provide flags and data that can be used to mutation
|
|
296
|
+
* to the payload and to allow features to know about a feature
|
|
297
|
+
* harvest happening.
|
|
298
|
+
* @param {any[]} errors Array of errors from the payload body
|
|
299
|
+
*/
|
|
300
|
+
#runCrossFeatureChecks (errors) {
|
|
301
|
+
const errorHashes = errors.map(error => error.params.stackHash)
|
|
302
|
+
const crossFeatureData = {
|
|
303
|
+
errorHashes
|
|
304
|
+
}
|
|
305
|
+
this.ee.emit(`cfc.${this.featureName}`, [crossFeatureData])
|
|
306
|
+
|
|
307
|
+
let hasReplayFlag = errors.find(err => err.params.hasReplay)
|
|
308
|
+
if (hasReplayFlag && !crossFeatureData.hasReplay) {
|
|
309
|
+
// Some errors have `hasReplay` and a replay is not being recorded
|
|
310
|
+
errors.forEach(error => {
|
|
311
|
+
delete error.params.hasReplay
|
|
312
|
+
})
|
|
313
|
+
}
|
|
314
|
+
}
|
|
299
315
|
}
|
|
@@ -54,11 +54,18 @@ export class Aggregate extends AggregateBase {
|
|
|
54
54
|
this.timeKeeper = undefined
|
|
55
55
|
|
|
56
56
|
this.recorder = args?.recorder
|
|
57
|
-
this.preloaded = !!this.recorder
|
|
58
57
|
this.errorNoticed = args?.errorNoticed || false
|
|
59
58
|
|
|
60
59
|
handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/Enabled'], undefined, FEATURE_NAMES.metrics, this.ee)
|
|
61
60
|
|
|
61
|
+
this.ee.on(`cfc.${FEATURE_NAMES.jserrors}`, (crossFeatureData) => {
|
|
62
|
+
crossFeatureData.hasReplay = !!(this.scheduler?.started &&
|
|
63
|
+
this.recorder &&
|
|
64
|
+
this.mode === MODE.FULL &&
|
|
65
|
+
!this.blocked &&
|
|
66
|
+
this.entitled)
|
|
67
|
+
})
|
|
68
|
+
|
|
62
69
|
// The SessionEntity class can emit a message indicating the session was cleared and reset (expiry, inactivity). This feature must abort and never resume if that occurs.
|
|
63
70
|
this.ee.on(SESSION_EVENTS.RESET, () => {
|
|
64
71
|
this.abort(ABORT_REASONS.RESET)
|
|
@@ -104,6 +111,10 @@ export class Aggregate extends AggregateBase {
|
|
|
104
111
|
this.forceStop(this.mode !== MODE.ERROR)
|
|
105
112
|
}, this.featureName, this.ee)
|
|
106
113
|
|
|
114
|
+
registerHandler(SR_EVENT_EMITTER_TYPES.ERROR_DURING_REPLAY, e => {
|
|
115
|
+
this.handleError(e)
|
|
116
|
+
}, this.featureName, this.ee)
|
|
117
|
+
|
|
107
118
|
const { error_sampling_rate, sampling_rate, autoStart, block_selector, mask_text_selector, mask_all_inputs, inline_stylesheet, inline_images, collect_fonts } = getConfigurationValue(this.agentIdentifier, 'session_replay')
|
|
108
119
|
|
|
109
120
|
this.waitForFlags(['sr']).then(([flagOn]) => {
|
|
@@ -148,15 +159,15 @@ export class Aggregate extends AggregateBase {
|
|
|
148
159
|
}
|
|
149
160
|
|
|
150
161
|
switchToFull () {
|
|
162
|
+
if (!this.entitled || this.blocked) return
|
|
151
163
|
this.mode = MODE.FULL
|
|
152
164
|
// if the error was noticed AFTER the recorder was already imported....
|
|
153
165
|
if (this.recorder && this.initialized) {
|
|
154
|
-
this.recorder.
|
|
155
|
-
this.recorder.startRecording()
|
|
156
|
-
|
|
166
|
+
if (!this.recorder.recording) this.recorder.startRecording()
|
|
157
167
|
this.scheduler.startTimer(this.harvestTimeSeconds)
|
|
158
|
-
|
|
159
168
|
this.syncWithSessionManager({ sessionReplayMode: this.mode })
|
|
169
|
+
} else {
|
|
170
|
+
this.initializeRecording(false, true, true)
|
|
160
171
|
}
|
|
161
172
|
}
|
|
162
173
|
|
|
@@ -213,7 +224,6 @@ export class Aggregate extends AggregateBase {
|
|
|
213
224
|
|
|
214
225
|
// If an error was noticed before the mode could be set (like in the early lifecycle of the page), immediately set to FULL mode
|
|
215
226
|
if (this.mode === MODE.ERROR && this.errorNoticed) this.mode = MODE.FULL
|
|
216
|
-
if (!this.preloaded) this.ee.on('err', e => this.handleError(e))
|
|
217
227
|
|
|
218
228
|
// FULL mode records AND reports from the beginning, while ERROR mode only records (but does not report).
|
|
219
229
|
// ERROR mode will do this until an error is thrown, and then switch into FULL mode.
|
|
@@ -293,7 +303,7 @@ export class Aggregate extends AggregateBase {
|
|
|
293
303
|
}
|
|
294
304
|
|
|
295
305
|
getCorrectedTimestamp (node) {
|
|
296
|
-
if (!node
|
|
306
|
+
if (!node?.timestamp) return
|
|
297
307
|
if (node.__newrelic) return node.timestamp
|
|
298
308
|
return this.timeKeeper.correctAbsoluteTimestamp(node.timestamp)
|
|
299
309
|
}
|
|
@@ -6,7 +6,8 @@ export const FEATURE_NAME = FEATURE_NAMES.sessionReplay
|
|
|
6
6
|
export const SR_EVENT_EMITTER_TYPES = {
|
|
7
7
|
RECORD: 'recordReplay',
|
|
8
8
|
PAUSE: 'pauseReplay',
|
|
9
|
-
REPLAY_RUNNING: 'replayRunning'
|
|
9
|
+
REPLAY_RUNNING: 'replayRunning',
|
|
10
|
+
ERROR_DURING_REPLAY: 'errorDuringReplay'
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
export const AVG_COMPRESSION = 0.12
|
|
@@ -9,9 +9,10 @@
|
|
|
9
9
|
* It is not production ready, and is not intended to be imported or implemented in any build of the browser agent until
|
|
10
10
|
* functionality is validated and a full user experience is curated.
|
|
11
11
|
*/
|
|
12
|
+
import { handle } from '../../../common/event-emitter/handle'
|
|
12
13
|
import { DEFAULT_KEY, MODE, PREFIX } from '../../../common/session/constants'
|
|
13
14
|
import { InstrumentBase } from '../../utils/instrument-base'
|
|
14
|
-
import { FEATURE_NAME } from '../constants'
|
|
15
|
+
import { FEATURE_NAME, SR_EVENT_EMITTER_TYPES } from '../constants'
|
|
15
16
|
import { isPreloadAllowed } from '../shared/utils'
|
|
16
17
|
|
|
17
18
|
export class Instrument extends InstrumentBase {
|
|
@@ -19,20 +20,29 @@ export class Instrument extends InstrumentBase {
|
|
|
19
20
|
constructor (agentIdentifier, aggregator, auto = true) {
|
|
20
21
|
super(agentIdentifier, aggregator, FEATURE_NAME, auto)
|
|
21
22
|
let session
|
|
23
|
+
this.replayRunning = false
|
|
22
24
|
try {
|
|
23
25
|
session = JSON.parse(localStorage.getItem(`${PREFIX}_${DEFAULT_KEY}`))
|
|
24
26
|
} catch (err) { }
|
|
25
27
|
|
|
26
28
|
if (this.#canPreloadRecorder(session)) {
|
|
27
|
-
/** If this is preloaded, set up a buffer, if not, later when sampling we will set up a .on for live events */
|
|
28
|
-
this.ee.on('err', (e) => {
|
|
29
|
-
this.errorNoticed = true
|
|
30
|
-
if (this.featAggregate) this.featAggregate.handleError()
|
|
31
|
-
})
|
|
32
29
|
this.#startRecording(session?.sessionReplayMode)
|
|
33
30
|
} else {
|
|
34
31
|
this.importAggregator()
|
|
35
32
|
}
|
|
33
|
+
|
|
34
|
+
/** If the recorder is running, we can pass error events on to the agg to help it switch to full mode later */
|
|
35
|
+
this.ee.on('err', (e) => {
|
|
36
|
+
if (this.replayRunning) {
|
|
37
|
+
this.errorNoticed = true
|
|
38
|
+
handle(SR_EVENT_EMITTER_TYPES.ERROR_DURING_REPLAY, [e], undefined, this.featureName, this.ee)
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
/** Emitted by the recorder when it starts capturing data, used to determine if we should pass errors on to the agg */
|
|
43
|
+
this.ee.on(SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, (isRunning) => {
|
|
44
|
+
this.replayRunning = isRunning
|
|
45
|
+
})
|
|
36
46
|
}
|
|
37
47
|
|
|
38
48
|
// At this point wherein session state exists already but we haven't init SessionEntity aka verify timers.
|