@newrelic/browser-agent 1.233.0 → 1.233.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/dist/cjs/cdn/experimental.js +27 -0
- package/dist/cjs/common/config/state/init.js +1 -1
- 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-scheduler.js +18 -3
- package/dist/cjs/common/harvest/harvest-scheduler.test.js +39 -0
- package/dist/cjs/common/harvest/harvest.js +11 -24
- package/dist/cjs/features/session_replay/aggregate/index.js +10 -7
- package/dist/esm/cdn/experimental.js +24 -0
- package/dist/esm/common/config/state/init.js +1 -1
- 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-scheduler.js +18 -3
- package/dist/esm/common/harvest/harvest-scheduler.test.js +37 -0
- package/dist/esm/common/harvest/harvest.js +11 -24
- package/dist/esm/features/session_replay/aggregate/index.js +10 -7
- package/dist/types/cdn/experimental.d.ts +2 -0
- package/dist/types/cdn/experimental.d.ts.map +1 -0
- package/dist/types/common/harvest/harvest-scheduler.d.ts +26 -3
- package/dist/types/common/harvest/harvest-scheduler.d.ts.map +1 -1
- package/dist/types/common/harvest/harvest.d.ts +2 -2
- package/dist/types/common/harvest/harvest.d.ts.map +1 -1
- package/dist/types/features/session_replay/aggregate/index.d.ts +4 -0
- package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
- package/package.json +3 -2
- package/src/cdn/experimental.js +36 -0
- package/src/common/config/state/init.js +1 -1
- package/src/common/harvest/harvest-scheduler.js +18 -3
- package/src/common/harvest/harvest-scheduler.test.js +25 -0
- package/src/common/harvest/harvest.js +11 -12
- package/src/features/session_replay/aggregate/index.js +12 -7
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _agent = require("../loaders/agent");
|
|
4
|
+
var _instrument = require("../features/page_view_event/instrument");
|
|
5
|
+
var _instrument2 = require("../features/page_view_timing/instrument");
|
|
6
|
+
var _instrument3 = require("../features/metrics/instrument");
|
|
7
|
+
var _instrument4 = require("../features/jserrors/instrument");
|
|
8
|
+
var _instrument5 = require("../features/ajax/instrument");
|
|
9
|
+
var _instrument6 = require("../features/session_trace/instrument");
|
|
10
|
+
var _instrument7 = require("../features/session_replay/instrument");
|
|
11
|
+
var _instrument8 = require("../features/spa/instrument");
|
|
12
|
+
var _instrument9 = require("../features/page_action/instrument");
|
|
13
|
+
/*
|
|
14
|
+
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
15
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* @file Creates an "EXPERIMENTAL" agent loader bundle composed of the core agent and all available feature modules, including experimental features.
|
|
19
|
+
*
|
|
20
|
+
* NOTE: This loader is ONLY used for internal testing. The code contained within is likely under development and dormant. It will not download to instrumented pages or record any data.
|
|
21
|
+
* It is not production ready, and is not intended to be imported or implemented in any build of the browser agent
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
new _agent.Agent({
|
|
25
|
+
features: [_instrument5.Instrument, _instrument.Instrument, _instrument2.Instrument, _instrument6.Instrument, _instrument7.Instrument, _instrument3.Instrument, _instrument9.Instrument, _instrument4.Instrument, _instrument8.Instrument],
|
|
26
|
+
loaderType: 'experimental'
|
|
27
|
+
});
|
|
@@ -18,6 +18,17 @@ var _config = require("../config/config");
|
|
|
18
18
|
* Periodically invokes harvest calls and handles retries
|
|
19
19
|
*/
|
|
20
20
|
class HarvestScheduler extends _sharedContext.SharedContext {
|
|
21
|
+
/**
|
|
22
|
+
* Create a HarvestScheduler
|
|
23
|
+
* @param {string} endpoint - The base BAM endpoint name -- ex. 'events'
|
|
24
|
+
* @param {object} opts - The options used to configure the HarvestScheduler
|
|
25
|
+
* @param {Function} opts.onFinished - The callback to be fired when a harvest has finished
|
|
26
|
+
* @param {Function} opts.getPayload - A callback which can be triggered to return a payload for harvesting
|
|
27
|
+
* @param {number} opts.retryDelay - The number of seconds to wait before retrying after a network failure
|
|
28
|
+
* @param {boolean} opts.raw - Use a prefabricated payload shape as the harvest payload without the need for formatting
|
|
29
|
+
* @param {string} opts.customUrl - A custom url that falls outside of the shape of the standard BAM harvester url pattern. Will use directly instead of concatenating various pieces
|
|
30
|
+
* @param {*} parent - The parent object, whose state can be passed into SharedContext
|
|
31
|
+
*/
|
|
21
32
|
constructor(endpoint, opts, parent) {
|
|
22
33
|
super(parent); // gets any allowed properties from the parent and stores them in `sharedContext`
|
|
23
34
|
this.endpoint = endpoint;
|
|
@@ -79,7 +90,12 @@ class HarvestScheduler extends _sharedContext.SharedContext {
|
|
|
79
90
|
var payload = this.opts.getPayload({
|
|
80
91
|
retry: retry
|
|
81
92
|
});
|
|
82
|
-
if (!payload)
|
|
93
|
+
if (!payload) {
|
|
94
|
+
if (this.started) {
|
|
95
|
+
this.scheduleHarvest();
|
|
96
|
+
}
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
83
99
|
payload = Object.prototype.toString.call(payload) === '[object Array]' ? payload : [payload];
|
|
84
100
|
harvests.push(...payload);
|
|
85
101
|
}
|
|
@@ -101,9 +117,8 @@ class HarvestScheduler extends _sharedContext.SharedContext {
|
|
|
101
117
|
opts,
|
|
102
118
|
submitMethod,
|
|
103
119
|
cbFinished: onHarvestFinished,
|
|
104
|
-
includeBaseParams: this.opts.includeBaseParams,
|
|
105
120
|
customUrl: this.opts.customUrl,
|
|
106
|
-
|
|
121
|
+
raw: this.opts.raw
|
|
107
122
|
});
|
|
108
123
|
});
|
|
109
124
|
if (this.started) {
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _init = require("../config/state/init");
|
|
4
|
+
var _harvestScheduler = require("./harvest-scheduler");
|
|
5
|
+
describe('runHarvest', () => {
|
|
6
|
+
it('should re-schedule harvest even if there is no accumulated data', () => {
|
|
7
|
+
(0, _init.setConfiguration)('asdf', {});
|
|
8
|
+
const scheduler = new _harvestScheduler.HarvestScheduler('events', {
|
|
9
|
+
getPayload: jest.fn()
|
|
10
|
+
}, {
|
|
11
|
+
agentIdentifier: 'asdf',
|
|
12
|
+
ee: {
|
|
13
|
+
on: jest.fn()
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
scheduler.started = true;
|
|
17
|
+
jest.spyOn(scheduler, 'scheduleHarvest');
|
|
18
|
+
scheduler.runHarvest();
|
|
19
|
+
expect(scheduler.opts.getPayload()).toBeFalsy();
|
|
20
|
+
expect(scheduler.scheduleHarvest).toHaveBeenCalledTimes(1);
|
|
21
|
+
});
|
|
22
|
+
it('should also re-schedule harvest if there is accumulated data', () => {
|
|
23
|
+
(0, _init.setConfiguration)('asdf', {});
|
|
24
|
+
const scheduler = new _harvestScheduler.HarvestScheduler('events', {
|
|
25
|
+
getPayload: jest.fn().mockImplementation(() => 'payload')
|
|
26
|
+
}, {
|
|
27
|
+
agentIdentifier: 'asdf',
|
|
28
|
+
ee: {
|
|
29
|
+
on: jest.fn()
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
scheduler.started = true;
|
|
33
|
+
scheduler.harvest._send = () => {};
|
|
34
|
+
jest.spyOn(scheduler, 'scheduleHarvest');
|
|
35
|
+
scheduler.runHarvest();
|
|
36
|
+
expect(scheduler.opts.getPayload()).toBeTruthy();
|
|
37
|
+
expect(scheduler.scheduleHarvest).toHaveBeenCalledTimes(1);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -125,7 +125,7 @@ class Harvest extends _sharedContext.SharedContext {
|
|
|
125
125
|
submitMethod,
|
|
126
126
|
cbFinished,
|
|
127
127
|
customUrl,
|
|
128
|
-
|
|
128
|
+
raw,
|
|
129
129
|
includeBaseParams = true
|
|
130
130
|
} = _ref;
|
|
131
131
|
var info = (0, _config.getInfo)(this.sharedContext.agentIdentifier);
|
|
@@ -140,16 +140,18 @@ class Harvest extends _sharedContext.SharedContext {
|
|
|
140
140
|
}
|
|
141
141
|
return false;
|
|
142
142
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
var
|
|
143
|
+
let url = '';
|
|
144
|
+
if (customUrl) url = customUrl;else if (raw) url = "".concat(this.getScheme(), "://").concat(info.errorBeacon, "/").concat(endpoint);else url = "".concat(this.getScheme(), "://").concat(info.errorBeacon, "/").concat(endpoint, "/1/").concat(info.licenseKey);
|
|
145
|
+
var baseParams = !raw && includeBaseParams ? this.baseQueryString() : '';
|
|
146
|
+
var payloadParams = payload.qs ? (0, _encode.obj)(payload.qs, agentRuntime.maxBytes) : '';
|
|
146
147
|
if (!submitMethod) {
|
|
147
148
|
submitMethod = getSubmitMethod(endpoint, opts);
|
|
148
149
|
}
|
|
149
150
|
var method = submitMethod.method;
|
|
150
151
|
var useBody = submitMethod.useBody;
|
|
151
152
|
var body;
|
|
152
|
-
var fullUrl = url
|
|
153
|
+
var fullUrl = "".concat(url, "?").concat(baseParams).concat(payloadParams);
|
|
154
|
+
const gzip = payload?.qs?.content_encoding === 'gzip';
|
|
153
155
|
if (!gzip) {
|
|
154
156
|
if (useBody && endpoint === 'events') {
|
|
155
157
|
body = payload.body.e;
|
|
@@ -165,25 +167,10 @@ class Harvest extends _sharedContext.SharedContext {
|
|
|
165
167
|
// Get query bytes harvested per endpoint as a supportability metric. See metrics aggregator (on unload).
|
|
166
168
|
agentRuntime.queryBytesSent[endpoint] = (agentRuntime.queryBytesSent[endpoint] || 0) + fullUrl.split('?').slice(-1)[0]?.length || 0;
|
|
167
169
|
const headers = [];
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
});
|
|
173
|
-
headers.push({
|
|
174
|
-
key: 'X-Browser-Monitoring-Key',
|
|
175
|
-
value: info.licenseKey
|
|
176
|
-
});
|
|
177
|
-
headers.push({
|
|
178
|
-
key: 'Content-Encoding',
|
|
179
|
-
value: 'gzip'
|
|
180
|
-
});
|
|
181
|
-
} else {
|
|
182
|
-
headers.push({
|
|
183
|
-
key: 'content-type',
|
|
184
|
-
value: 'text/plain'
|
|
185
|
-
});
|
|
186
|
-
}
|
|
170
|
+
headers.push({
|
|
171
|
+
key: 'content-type',
|
|
172
|
+
value: 'text/plain'
|
|
173
|
+
});
|
|
187
174
|
|
|
188
175
|
/* Since workers don't support sendBeacon right now, or Image(), they can only use XHR method.
|
|
189
176
|
Because they still do permit synch XHR, the idea is that at final harvest time (worker is closing),
|
|
@@ -95,11 +95,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
95
95
|
onFinished: this.onHarvestFinished.bind(this),
|
|
96
96
|
retryDelay: this.harvestTimeSeconds,
|
|
97
97
|
getPayload: this.prepareHarvest.bind(this),
|
|
98
|
-
|
|
99
|
-
includeBaseParams: false,
|
|
100
|
-
customUrl: 'https://vortex-alb.stg-single-tooth.cell.us.nr-data.net/blob',
|
|
101
|
-
raw: true,
|
|
102
|
-
gzip: true
|
|
98
|
+
raw: true
|
|
103
99
|
}, this);
|
|
104
100
|
|
|
105
101
|
// Wait for an error to be reported. This currently is wrapped around the "Error" feature. This is a feature-feature dependency.
|
|
@@ -159,7 +155,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
159
155
|
if (fullSample) this.mode = MODE.FULL; // full mode has precedence over error mode
|
|
160
156
|
else if (errorSample) this.mode = MODE.ERROR;
|
|
161
157
|
// If neither are selected, then don't record (early return)
|
|
162
|
-
return;
|
|
158
|
+
else return;
|
|
163
159
|
}
|
|
164
160
|
|
|
165
161
|
// FULL mode records AND reports from the beginning, while ERROR mode only records (but does not report).
|
|
@@ -169,6 +165,11 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
169
165
|
// We only report (harvest) in FULL mode
|
|
170
166
|
this.scheduler.startTimer(this.harvestTimeSeconds);
|
|
171
167
|
}
|
|
168
|
+
|
|
169
|
+
// If an error was noticed before the mode could be set (like in the early lifecycle of the page), immediately set to FULL mode
|
|
170
|
+
if (this.mode === MODE.ERROR && this.errorNoticed) {
|
|
171
|
+
this.mode = MODE.FULL;
|
|
172
|
+
}
|
|
172
173
|
// We record in FULL or ERROR mode
|
|
173
174
|
|
|
174
175
|
recorder = (await Promise.resolve().then(() => _interopRequireWildcard(require( /* webpackChunkName: "recorder" */'rrweb')))).record;
|
|
@@ -201,7 +202,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
201
202
|
const info = (0, _config.getInfo)(this.agentIdentifier);
|
|
202
203
|
return {
|
|
203
204
|
qs: {
|
|
204
|
-
protocol_version: '0'
|
|
205
|
+
protocol_version: '0',
|
|
206
|
+
content_encoding: 'gzip',
|
|
207
|
+
browser_monitoring_key: info.licenseKey
|
|
205
208
|
},
|
|
206
209
|
body: {
|
|
207
210
|
type: 'SessionReplay',
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* @file Creates an "EXPERIMENTAL" agent loader bundle composed of the core agent and all available feature modules, including experimental features.
|
|
7
|
+
*
|
|
8
|
+
* NOTE: This loader is ONLY used for internal testing. The code contained within is likely under development and dormant. It will not download to instrumented pages or record any data.
|
|
9
|
+
* It is not production ready, and is not intended to be imported or implemented in any build of the browser agent
|
|
10
|
+
*/
|
|
11
|
+
import { Agent } from '../loaders/agent';
|
|
12
|
+
import { Instrument as InstrumentPageViewEvent } from '../features/page_view_event/instrument';
|
|
13
|
+
import { Instrument as InstrumentPageViewTiming } from '../features/page_view_timing/instrument';
|
|
14
|
+
import { Instrument as InstrumentMetrics } from '../features/metrics/instrument';
|
|
15
|
+
import { Instrument as InstrumentErrors } from '../features/jserrors/instrument';
|
|
16
|
+
import { Instrument as InstrumentXhr } from '../features/ajax/instrument';
|
|
17
|
+
import { Instrument as InstrumentSessionTrace } from '../features/session_trace/instrument';
|
|
18
|
+
import { Instrument as InstrumentSessionReplay } from '../features/session_replay/instrument';
|
|
19
|
+
import { Instrument as InstrumentSpa } from '../features/spa/instrument';
|
|
20
|
+
import { Instrument as InstrumentPageAction } from '../features/page_action/instrument';
|
|
21
|
+
new Agent({
|
|
22
|
+
features: [InstrumentXhr, InstrumentPageViewEvent, InstrumentPageViewTiming, InstrumentSessionTrace, InstrumentSessionReplay, InstrumentMetrics, InstrumentPageAction, InstrumentErrors, InstrumentSpa],
|
|
23
|
+
loaderType: 'experimental'
|
|
24
|
+
});
|
|
@@ -13,6 +13,17 @@ import { getConfigurationValue } from '../config/config';
|
|
|
13
13
|
* Periodically invokes harvest calls and handles retries
|
|
14
14
|
*/
|
|
15
15
|
export class HarvestScheduler extends SharedContext {
|
|
16
|
+
/**
|
|
17
|
+
* Create a HarvestScheduler
|
|
18
|
+
* @param {string} endpoint - The base BAM endpoint name -- ex. 'events'
|
|
19
|
+
* @param {object} opts - The options used to configure the HarvestScheduler
|
|
20
|
+
* @param {Function} opts.onFinished - The callback to be fired when a harvest has finished
|
|
21
|
+
* @param {Function} opts.getPayload - A callback which can be triggered to return a payload for harvesting
|
|
22
|
+
* @param {number} opts.retryDelay - The number of seconds to wait before retrying after a network failure
|
|
23
|
+
* @param {boolean} opts.raw - Use a prefabricated payload shape as the harvest payload without the need for formatting
|
|
24
|
+
* @param {string} opts.customUrl - A custom url that falls outside of the shape of the standard BAM harvester url pattern. Will use directly instead of concatenating various pieces
|
|
25
|
+
* @param {*} parent - The parent object, whose state can be passed into SharedContext
|
|
26
|
+
*/
|
|
16
27
|
constructor(endpoint, opts, parent) {
|
|
17
28
|
super(parent); // gets any allowed properties from the parent and stores them in `sharedContext`
|
|
18
29
|
this.endpoint = endpoint;
|
|
@@ -74,7 +85,12 @@ export class HarvestScheduler extends SharedContext {
|
|
|
74
85
|
var payload = this.opts.getPayload({
|
|
75
86
|
retry: retry
|
|
76
87
|
});
|
|
77
|
-
if (!payload)
|
|
88
|
+
if (!payload) {
|
|
89
|
+
if (this.started) {
|
|
90
|
+
this.scheduleHarvest();
|
|
91
|
+
}
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
78
94
|
payload = Object.prototype.toString.call(payload) === '[object Array]' ? payload : [payload];
|
|
79
95
|
harvests.push(...payload);
|
|
80
96
|
}
|
|
@@ -96,9 +112,8 @@ export class HarvestScheduler extends SharedContext {
|
|
|
96
112
|
opts,
|
|
97
113
|
submitMethod,
|
|
98
114
|
cbFinished: onHarvestFinished,
|
|
99
|
-
includeBaseParams: this.opts.includeBaseParams,
|
|
100
115
|
customUrl: this.opts.customUrl,
|
|
101
|
-
|
|
116
|
+
raw: this.opts.raw
|
|
102
117
|
});
|
|
103
118
|
});
|
|
104
119
|
if (this.started) {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { setConfiguration } from '../config/state/init';
|
|
2
|
+
import { HarvestScheduler } from './harvest-scheduler';
|
|
3
|
+
describe('runHarvest', () => {
|
|
4
|
+
it('should re-schedule harvest even if there is no accumulated data', () => {
|
|
5
|
+
setConfiguration('asdf', {});
|
|
6
|
+
const scheduler = new HarvestScheduler('events', {
|
|
7
|
+
getPayload: jest.fn()
|
|
8
|
+
}, {
|
|
9
|
+
agentIdentifier: 'asdf',
|
|
10
|
+
ee: {
|
|
11
|
+
on: jest.fn()
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
scheduler.started = true;
|
|
15
|
+
jest.spyOn(scheduler, 'scheduleHarvest');
|
|
16
|
+
scheduler.runHarvest();
|
|
17
|
+
expect(scheduler.opts.getPayload()).toBeFalsy();
|
|
18
|
+
expect(scheduler.scheduleHarvest).toHaveBeenCalledTimes(1);
|
|
19
|
+
});
|
|
20
|
+
it('should also re-schedule harvest if there is accumulated data', () => {
|
|
21
|
+
setConfiguration('asdf', {});
|
|
22
|
+
const scheduler = new HarvestScheduler('events', {
|
|
23
|
+
getPayload: jest.fn().mockImplementation(() => 'payload')
|
|
24
|
+
}, {
|
|
25
|
+
agentIdentifier: 'asdf',
|
|
26
|
+
ee: {
|
|
27
|
+
on: jest.fn()
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
scheduler.started = true;
|
|
31
|
+
scheduler.harvest._send = () => {};
|
|
32
|
+
jest.spyOn(scheduler, 'scheduleHarvest');
|
|
33
|
+
scheduler.runHarvest();
|
|
34
|
+
expect(scheduler.opts.getPayload()).toBeTruthy();
|
|
35
|
+
expect(scheduler.scheduleHarvest).toHaveBeenCalledTimes(1);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -119,7 +119,7 @@ export class Harvest extends SharedContext {
|
|
|
119
119
|
submitMethod,
|
|
120
120
|
cbFinished,
|
|
121
121
|
customUrl,
|
|
122
|
-
|
|
122
|
+
raw,
|
|
123
123
|
includeBaseParams = true
|
|
124
124
|
} = _ref;
|
|
125
125
|
var info = getInfo(this.sharedContext.agentIdentifier);
|
|
@@ -134,16 +134,18 @@ export class Harvest extends SharedContext {
|
|
|
134
134
|
}
|
|
135
135
|
return false;
|
|
136
136
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
var
|
|
137
|
+
let url = '';
|
|
138
|
+
if (customUrl) url = customUrl;else if (raw) url = "".concat(this.getScheme(), "://").concat(info.errorBeacon, "/").concat(endpoint);else url = "".concat(this.getScheme(), "://").concat(info.errorBeacon, "/").concat(endpoint, "/1/").concat(info.licenseKey);
|
|
139
|
+
var baseParams = !raw && includeBaseParams ? this.baseQueryString() : '';
|
|
140
|
+
var payloadParams = payload.qs ? encodeObj(payload.qs, agentRuntime.maxBytes) : '';
|
|
140
141
|
if (!submitMethod) {
|
|
141
142
|
submitMethod = getSubmitMethod(endpoint, opts);
|
|
142
143
|
}
|
|
143
144
|
var method = submitMethod.method;
|
|
144
145
|
var useBody = submitMethod.useBody;
|
|
145
146
|
var body;
|
|
146
|
-
var fullUrl = url
|
|
147
|
+
var fullUrl = "".concat(url, "?").concat(baseParams).concat(payloadParams);
|
|
148
|
+
const gzip = payload?.qs?.content_encoding === 'gzip';
|
|
147
149
|
if (!gzip) {
|
|
148
150
|
if (useBody && endpoint === 'events') {
|
|
149
151
|
body = payload.body.e;
|
|
@@ -159,25 +161,10 @@ export class Harvest extends SharedContext {
|
|
|
159
161
|
// Get query bytes harvested per endpoint as a supportability metric. See metrics aggregator (on unload).
|
|
160
162
|
agentRuntime.queryBytesSent[endpoint] = (agentRuntime.queryBytesSent[endpoint] || 0) + fullUrl.split('?').slice(-1)[0]?.length || 0;
|
|
161
163
|
const headers = [];
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
});
|
|
167
|
-
headers.push({
|
|
168
|
-
key: 'X-Browser-Monitoring-Key',
|
|
169
|
-
value: info.licenseKey
|
|
170
|
-
});
|
|
171
|
-
headers.push({
|
|
172
|
-
key: 'Content-Encoding',
|
|
173
|
-
value: 'gzip'
|
|
174
|
-
});
|
|
175
|
-
} else {
|
|
176
|
-
headers.push({
|
|
177
|
-
key: 'content-type',
|
|
178
|
-
value: 'text/plain'
|
|
179
|
-
});
|
|
180
|
-
}
|
|
164
|
+
headers.push({
|
|
165
|
+
key: 'content-type',
|
|
166
|
+
value: 'text/plain'
|
|
167
|
+
});
|
|
181
168
|
|
|
182
169
|
/* Since workers don't support sendBeacon right now, or Image(), they can only use XHR method.
|
|
183
170
|
Because they still do permit synch XHR, the idea is that at final harvest time (worker is closing),
|
|
@@ -90,11 +90,7 @@ export class Aggregate extends AggregateBase {
|
|
|
90
90
|
onFinished: this.onHarvestFinished.bind(this),
|
|
91
91
|
retryDelay: this.harvestTimeSeconds,
|
|
92
92
|
getPayload: this.prepareHarvest.bind(this),
|
|
93
|
-
|
|
94
|
-
includeBaseParams: false,
|
|
95
|
-
customUrl: 'https://vortex-alb.stg-single-tooth.cell.us.nr-data.net/blob',
|
|
96
|
-
raw: true,
|
|
97
|
-
gzip: true
|
|
93
|
+
raw: true
|
|
98
94
|
}, this);
|
|
99
95
|
|
|
100
96
|
// Wait for an error to be reported. This currently is wrapped around the "Error" feature. This is a feature-feature dependency.
|
|
@@ -154,7 +150,7 @@ export class Aggregate extends AggregateBase {
|
|
|
154
150
|
if (fullSample) this.mode = MODE.FULL; // full mode has precedence over error mode
|
|
155
151
|
else if (errorSample) this.mode = MODE.ERROR;
|
|
156
152
|
// If neither are selected, then don't record (early return)
|
|
157
|
-
return;
|
|
153
|
+
else return;
|
|
158
154
|
}
|
|
159
155
|
|
|
160
156
|
// FULL mode records AND reports from the beginning, while ERROR mode only records (but does not report).
|
|
@@ -164,6 +160,11 @@ export class Aggregate extends AggregateBase {
|
|
|
164
160
|
// We only report (harvest) in FULL mode
|
|
165
161
|
this.scheduler.startTimer(this.harvestTimeSeconds);
|
|
166
162
|
}
|
|
163
|
+
|
|
164
|
+
// If an error was noticed before the mode could be set (like in the early lifecycle of the page), immediately set to FULL mode
|
|
165
|
+
if (this.mode === MODE.ERROR && this.errorNoticed) {
|
|
166
|
+
this.mode = MODE.FULL;
|
|
167
|
+
}
|
|
167
168
|
// We record in FULL or ERROR mode
|
|
168
169
|
|
|
169
170
|
recorder = (await import( /* webpackChunkName: "recorder" */'rrweb')).record;
|
|
@@ -196,7 +197,9 @@ export class Aggregate extends AggregateBase {
|
|
|
196
197
|
const info = getInfo(this.agentIdentifier);
|
|
197
198
|
return {
|
|
198
199
|
qs: {
|
|
199
|
-
protocol_version: '0'
|
|
200
|
+
protocol_version: '0',
|
|
201
|
+
content_encoding: 'gzip',
|
|
202
|
+
browser_monitoring_key: info.licenseKey
|
|
200
203
|
},
|
|
201
204
|
body: {
|
|
202
205
|
type: 'SessionReplay',
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"experimental.d.ts","sourceRoot":"","sources":["../../../src/cdn/experimental.js"],"names":[],"mappings":""}
|
|
@@ -2,9 +2,32 @@
|
|
|
2
2
|
* Periodically invokes harvest calls and handles retries
|
|
3
3
|
*/
|
|
4
4
|
export class HarvestScheduler extends SharedContext {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Create a HarvestScheduler
|
|
7
|
+
* @param {string} endpoint - The base BAM endpoint name -- ex. 'events'
|
|
8
|
+
* @param {object} opts - The options used to configure the HarvestScheduler
|
|
9
|
+
* @param {Function} opts.onFinished - The callback to be fired when a harvest has finished
|
|
10
|
+
* @param {Function} opts.getPayload - A callback which can be triggered to return a payload for harvesting
|
|
11
|
+
* @param {number} opts.retryDelay - The number of seconds to wait before retrying after a network failure
|
|
12
|
+
* @param {boolean} opts.raw - Use a prefabricated payload shape as the harvest payload without the need for formatting
|
|
13
|
+
* @param {string} opts.customUrl - A custom url that falls outside of the shape of the standard BAM harvester url pattern. Will use directly instead of concatenating various pieces
|
|
14
|
+
* @param {*} parent - The parent object, whose state can be passed into SharedContext
|
|
15
|
+
*/
|
|
16
|
+
constructor(endpoint: string, opts: {
|
|
17
|
+
onFinished: Function;
|
|
18
|
+
getPayload: Function;
|
|
19
|
+
retryDelay: number;
|
|
20
|
+
raw: boolean;
|
|
21
|
+
customUrl: string;
|
|
22
|
+
}, parent: any);
|
|
23
|
+
endpoint: string;
|
|
24
|
+
opts: {
|
|
25
|
+
onFinished: Function;
|
|
26
|
+
getPayload: Function;
|
|
27
|
+
retryDelay: number;
|
|
28
|
+
raw: boolean;
|
|
29
|
+
customUrl: string;
|
|
30
|
+
};
|
|
8
31
|
started: boolean;
|
|
9
32
|
timeoutHandle: NodeJS.Timeout | null;
|
|
10
33
|
aborted: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"harvest-scheduler.d.ts","sourceRoot":"","sources":["../../../../src/common/harvest/harvest-scheduler.js"],"names":[],"mappings":"AAWA;;GAEG;AACH;IACE,
|
|
1
|
+
{"version":3,"file":"harvest-scheduler.d.ts","sourceRoot":"","sources":["../../../../src/common/harvest/harvest-scheduler.js"],"names":[],"mappings":"AAWA;;GAEG;AACH;IACE;;;;;;;;;;SAUK;IACL,sBATa,MAAM;QAES,UAAU;QACV,UAAU;QACZ,UAAU,EAAvB,MAAM;QACQ,GAAG,EAAjB,OAAO;QACM,SAAS,EAAtB,MAAM;oBAkBlB;IAbC,iBAAwB;IACxB;;;oBARW,MAAM;aACN,OAAO;mBACP,MAAM;MAMK;IACtB,iBAAoB;IACpB,qCAAyB;IACzB,iBAAoB;IAEpB,iBAA8C;IAShD,eAKC;IAED,mDAIC;IAHC,cAAwB;IAK1B,uCAMC;IAED,6CAWC;IAED,yCA0DC;IAED,gDAiBC;CACF;8BArJ6B,2BAA2B;wBAChB,WAAW"}
|
|
@@ -37,14 +37,14 @@ export class Harvest extends SharedContext {
|
|
|
37
37
|
* @param {NetworkSendSpec} spec Specification for sending data
|
|
38
38
|
*/
|
|
39
39
|
obfuscateAndSend(spec: NetworkSendSpec): any;
|
|
40
|
-
_send({ endpoint, payload, opts, submitMethod, cbFinished, customUrl,
|
|
40
|
+
_send({ endpoint, payload, opts, submitMethod, cbFinished, customUrl, raw, includeBaseParams }: {
|
|
41
41
|
endpoint: any;
|
|
42
42
|
payload?: {} | undefined;
|
|
43
43
|
opts?: {} | undefined;
|
|
44
44
|
submitMethod: any;
|
|
45
45
|
cbFinished: any;
|
|
46
46
|
customUrl: any;
|
|
47
|
-
|
|
47
|
+
raw: any;
|
|
48
48
|
includeBaseParams?: boolean | undefined;
|
|
49
49
|
}): any;
|
|
50
50
|
baseQueryString(): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"harvest.d.ts","sourceRoot":"","sources":["../../../../src/common/harvest/harvest.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"harvest.d.ts","sourceRoot":"","sources":["../../../../src/common/harvest/harvest.js"],"names":[],"mappings":"AAmOA;;;;;;;;;;;;;;;;;EAsBC;AAlND;IAII,0BAA2H;IAC3H,uBAAoD;IACpD,kCAAsH;IAEtH,YAAiB;IAGnB;;;;OAIG;IACH,YAFW,eAAe,OAYzB;IAED;;;OAGG;IACH,WAFW,eAAe,OAazB;IAED;;;OAGG;IACH,uBAFW,eAAe,OAMzB;IAED;;;;;;;;;YAiFC;IAGD,0BAmBC;IAED;;;MAYC;IAED,mCAGC;IAED,uBAIC;CACF;8BA1MY,MAAM;8BALW,2BAA2B;2BAF9B,mBAAmB"}
|
|
@@ -37,6 +37,8 @@ export class Aggregate extends AggregateBase {
|
|
|
37
37
|
prepareHarvest(options: any): {
|
|
38
38
|
qs: {
|
|
39
39
|
protocol_version: string;
|
|
40
|
+
content_encoding: string;
|
|
41
|
+
browser_monitoring_key: any;
|
|
40
42
|
};
|
|
41
43
|
body: {
|
|
42
44
|
type: string;
|
|
@@ -56,6 +58,8 @@ export class Aggregate extends AggregateBase {
|
|
|
56
58
|
getHarvestContents(): {
|
|
57
59
|
qs: {
|
|
58
60
|
protocol_version: string;
|
|
61
|
+
content_encoding: string;
|
|
62
|
+
browser_monitoring_key: any;
|
|
59
63
|
};
|
|
60
64
|
body: {
|
|
61
65
|
type: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_replay/aggregate/index.js"],"names":[],"mappings":"AAsBA,4CAA4C;AAiB5C;IACE,2BAAiC;IACjC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_replay/aggregate/index.js"],"names":[],"mappings":"AAsBA,4CAA4C;AAiB5C;IACE,2BAAiC;IACjC,mDAmFC;IAhFC,iHAAiH;IACjH,cAAgB;IAChB,8GAA8G;IAC9G,wBAAgH;IAChH,iFAAiF;IACjF,qBAAwB;IACxB,uDAAuD;IACvD,sBAAyB;IACzB,6GAA6G;IAC7G,aAAoB;IAIpB,gGAAgG;IAChG,sBAAyB;IACzB;;;MAGE;IACF,qBAAwB;IACxB,+HAA+H;IAC/H,kBAAqB;IAErB,qGAAqG;IACrG,+BAA+B;IAE/B,uIAAuI;IACvI,0BAAyE;IAiBzE,4BAKQ;IAiCV;;;;;;OAMG;IACH,kCALW,OAAO,eACP,OAAO,cACP,OAAO,GACL,IAAI,CA8ChB;IAED;;;;;;;;;;;;;;;;;;;;oBAaC;IAED;;;;;;;;;;;;;;;;;;;;MAwBC;IAED,qCAOC;IAED,kFAAkF;IAClF,oBAMC;IAED,qDAAqD;IACrD,uBAoBC;IAED,yHAAyH;IACzH,yCA0BC;IAED,0HAA0H;IAC1H,yBAIC;IAED,gCAAgC;IAChC,0CAGC;IAED,yDAAyD;IACzD,cAKC;IAED;;;SAGK;IACL,uCAEC;CACF;8BA3S6B,4BAA4B;iCALzB,2CAA2C"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@newrelic/browser-agent",
|
|
3
|
-
"version": "1.233.
|
|
3
|
+
"version": "1.233.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"author": "New Relic Browser Agent Team <browser-agent@newrelic.com>",
|
|
6
6
|
"description": "Tests for the New Relic JavaScript agent",
|
|
@@ -126,7 +126,8 @@
|
|
|
126
126
|
"npm:build:esm": "npx babel --env-name npm-esm --out-dir dist/esm --out-file-extension .js ./src",
|
|
127
127
|
"npm:build:cjs": "npx babel --env-name npm-cjs --out-dir dist/cjs --out-file-extension .js ./src",
|
|
128
128
|
"npm:build:types": "npx tsc -b",
|
|
129
|
-
"npm:pack": "mkdir -p temp && export PKG_NAME=$(npm pack --pack-destination temp) && echo ./temp/$PKG_NAME"
|
|
129
|
+
"npm:pack": "mkdir -p temp && export PKG_NAME=$(npm pack --pack-destination temp) && echo ./temp/$PKG_NAME",
|
|
130
|
+
"watch:browser-tests": "jung -r ./src -F '.*\\.test\\.js' --run -- npm run build:browser-tests"
|
|
130
131
|
},
|
|
131
132
|
"config": {
|
|
132
133
|
"unsafe-perm": true
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* @file Creates an "EXPERIMENTAL" agent loader bundle composed of the core agent and all available feature modules, including experimental features.
|
|
7
|
+
*
|
|
8
|
+
* NOTE: This loader is ONLY used for internal testing. The code contained within is likely under development and dormant. It will not download to instrumented pages or record any data.
|
|
9
|
+
* It is not production ready, and is not intended to be imported or implemented in any build of the browser agent
|
|
10
|
+
*/
|
|
11
|
+
import { Agent } from '../loaders/agent'
|
|
12
|
+
|
|
13
|
+
import { Instrument as InstrumentPageViewEvent } from '../features/page_view_event/instrument'
|
|
14
|
+
import { Instrument as InstrumentPageViewTiming } from '../features/page_view_timing/instrument'
|
|
15
|
+
import { Instrument as InstrumentMetrics } from '../features/metrics/instrument'
|
|
16
|
+
import { Instrument as InstrumentErrors } from '../features/jserrors/instrument'
|
|
17
|
+
import { Instrument as InstrumentXhr } from '../features/ajax/instrument'
|
|
18
|
+
import { Instrument as InstrumentSessionTrace } from '../features/session_trace/instrument'
|
|
19
|
+
import { Instrument as InstrumentSessionReplay } from '../features/session_replay/instrument'
|
|
20
|
+
import { Instrument as InstrumentSpa } from '../features/spa/instrument'
|
|
21
|
+
import { Instrument as InstrumentPageAction } from '../features/page_action/instrument'
|
|
22
|
+
|
|
23
|
+
new Agent({
|
|
24
|
+
features: [
|
|
25
|
+
InstrumentXhr,
|
|
26
|
+
InstrumentPageViewEvent,
|
|
27
|
+
InstrumentPageViewTiming,
|
|
28
|
+
InstrumentSessionTrace,
|
|
29
|
+
InstrumentSessionReplay,
|
|
30
|
+
InstrumentMetrics,
|
|
31
|
+
InstrumentPageAction,
|
|
32
|
+
InstrumentErrors,
|
|
33
|
+
InstrumentSpa
|
|
34
|
+
],
|
|
35
|
+
loaderType: 'experimental'
|
|
36
|
+
})
|
|
@@ -13,6 +13,17 @@ import { getConfigurationValue } from '../config/config'
|
|
|
13
13
|
* Periodically invokes harvest calls and handles retries
|
|
14
14
|
*/
|
|
15
15
|
export class HarvestScheduler extends SharedContext {
|
|
16
|
+
/**
|
|
17
|
+
* Create a HarvestScheduler
|
|
18
|
+
* @param {string} endpoint - The base BAM endpoint name -- ex. 'events'
|
|
19
|
+
* @param {object} opts - The options used to configure the HarvestScheduler
|
|
20
|
+
* @param {Function} opts.onFinished - The callback to be fired when a harvest has finished
|
|
21
|
+
* @param {Function} opts.getPayload - A callback which can be triggered to return a payload for harvesting
|
|
22
|
+
* @param {number} opts.retryDelay - The number of seconds to wait before retrying after a network failure
|
|
23
|
+
* @param {boolean} opts.raw - Use a prefabricated payload shape as the harvest payload without the need for formatting
|
|
24
|
+
* @param {string} opts.customUrl - A custom url that falls outside of the shape of the standard BAM harvester url pattern. Will use directly instead of concatenating various pieces
|
|
25
|
+
* @param {*} parent - The parent object, whose state can be passed into SharedContext
|
|
26
|
+
*/
|
|
16
27
|
constructor (endpoint, opts, parent) {
|
|
17
28
|
super(parent) // gets any allowed properties from the parent and stores them in `sharedContext`
|
|
18
29
|
this.endpoint = endpoint
|
|
@@ -78,7 +89,12 @@ export class HarvestScheduler extends SharedContext {
|
|
|
78
89
|
const retry = submitMethod.method === submitData.xhr
|
|
79
90
|
var payload = this.opts.getPayload({ retry: retry })
|
|
80
91
|
|
|
81
|
-
if (!payload)
|
|
92
|
+
if (!payload) {
|
|
93
|
+
if (this.started) {
|
|
94
|
+
this.scheduleHarvest()
|
|
95
|
+
}
|
|
96
|
+
return
|
|
97
|
+
}
|
|
82
98
|
|
|
83
99
|
payload = Object.prototype.toString.call(payload) === '[object Array]' ? payload : [payload]
|
|
84
100
|
harvests.push(...payload)
|
|
@@ -103,9 +119,8 @@ export class HarvestScheduler extends SharedContext {
|
|
|
103
119
|
opts,
|
|
104
120
|
submitMethod,
|
|
105
121
|
cbFinished: onHarvestFinished,
|
|
106
|
-
includeBaseParams: this.opts.includeBaseParams,
|
|
107
122
|
customUrl: this.opts.customUrl,
|
|
108
|
-
|
|
123
|
+
raw: this.opts.raw
|
|
109
124
|
})
|
|
110
125
|
})
|
|
111
126
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { setConfiguration } from '../config/state/init'
|
|
2
|
+
import { HarvestScheduler } from './harvest-scheduler'
|
|
3
|
+
|
|
4
|
+
describe('runHarvest', () => {
|
|
5
|
+
it('should re-schedule harvest even if there is no accumulated data', () => {
|
|
6
|
+
setConfiguration('asdf', {})
|
|
7
|
+
const scheduler = new HarvestScheduler('events', { getPayload: jest.fn() }, { agentIdentifier: 'asdf', ee: { on: jest.fn() } })
|
|
8
|
+
scheduler.started = true
|
|
9
|
+
jest.spyOn(scheduler, 'scheduleHarvest')
|
|
10
|
+
scheduler.runHarvest()
|
|
11
|
+
expect(scheduler.opts.getPayload()).toBeFalsy()
|
|
12
|
+
expect(scheduler.scheduleHarvest).toHaveBeenCalledTimes(1)
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('should also re-schedule harvest if there is accumulated data', () => {
|
|
16
|
+
setConfiguration('asdf', {})
|
|
17
|
+
const scheduler = new HarvestScheduler('events', { getPayload: jest.fn().mockImplementation(() => 'payload') }, { agentIdentifier: 'asdf', ee: { on: jest.fn() } })
|
|
18
|
+
scheduler.started = true
|
|
19
|
+
scheduler.harvest._send = () => {}
|
|
20
|
+
jest.spyOn(scheduler, 'scheduleHarvest')
|
|
21
|
+
scheduler.runHarvest()
|
|
22
|
+
expect(scheduler.opts.getPayload()).toBeTruthy()
|
|
23
|
+
expect(scheduler.scheduleHarvest).toHaveBeenCalledTimes(1)
|
|
24
|
+
})
|
|
25
|
+
})
|
|
@@ -92,7 +92,7 @@ export class Harvest extends SharedContext {
|
|
|
92
92
|
return this._send({ ...spec, payload })
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
_send ({ endpoint, payload = {}, opts = {}, submitMethod, cbFinished, customUrl,
|
|
95
|
+
_send ({ endpoint, payload = {}, opts = {}, submitMethod, cbFinished, customUrl, raw, includeBaseParams = true }) {
|
|
96
96
|
var info = getInfo(this.sharedContext.agentIdentifier)
|
|
97
97
|
if (!info.errorBeacon) return false
|
|
98
98
|
|
|
@@ -105,10 +105,13 @@ export class Harvest extends SharedContext {
|
|
|
105
105
|
return false
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
let url = ''
|
|
109
|
+
if (customUrl) url = customUrl
|
|
110
|
+
else if (raw) url = `${this.getScheme()}://${info.errorBeacon}/${endpoint}`
|
|
111
|
+
else url = `${this.getScheme()}://${info.errorBeacon}/${endpoint}/1/${info.licenseKey}`
|
|
109
112
|
|
|
110
|
-
var baseParams = includeBaseParams ? this.baseQueryString() : ''
|
|
111
|
-
var
|
|
113
|
+
var baseParams = !raw && includeBaseParams ? this.baseQueryString() : ''
|
|
114
|
+
var payloadParams = payload.qs ? encodeObj(payload.qs, agentRuntime.maxBytes) : ''
|
|
112
115
|
if (!submitMethod) {
|
|
113
116
|
submitMethod = getSubmitMethod(endpoint, opts)
|
|
114
117
|
}
|
|
@@ -116,7 +119,9 @@ export class Harvest extends SharedContext {
|
|
|
116
119
|
var useBody = submitMethod.useBody
|
|
117
120
|
|
|
118
121
|
var body
|
|
119
|
-
var fullUrl = url
|
|
122
|
+
var fullUrl = `${url}?${baseParams}${payloadParams}`
|
|
123
|
+
|
|
124
|
+
const gzip = payload?.qs?.content_encoding === 'gzip'
|
|
120
125
|
|
|
121
126
|
if (!gzip) {
|
|
122
127
|
if (useBody && endpoint === 'events') {
|
|
@@ -135,13 +140,7 @@ export class Harvest extends SharedContext {
|
|
|
135
140
|
|
|
136
141
|
const headers = []
|
|
137
142
|
|
|
138
|
-
|
|
139
|
-
headers.push({ key: 'content-type', value: 'application/json' })
|
|
140
|
-
headers.push({ key: 'X-Browser-Monitoring-Key', value: info.licenseKey })
|
|
141
|
-
headers.push({ key: 'Content-Encoding', value: 'gzip' })
|
|
142
|
-
} else {
|
|
143
|
-
headers.push({ key: 'content-type', value: 'text/plain' })
|
|
144
|
-
}
|
|
143
|
+
headers.push({ key: 'content-type', value: 'text/plain' })
|
|
145
144
|
|
|
146
145
|
/* Since workers don't support sendBeacon right now, or Image(), they can only use XHR method.
|
|
147
146
|
Because they still do permit synch XHR, the idea is that at final harvest time (worker is closing),
|
|
@@ -90,11 +90,7 @@ export class Aggregate extends AggregateBase {
|
|
|
90
90
|
onFinished: this.onHarvestFinished.bind(this),
|
|
91
91
|
retryDelay: this.harvestTimeSeconds,
|
|
92
92
|
getPayload: this.prepareHarvest.bind(this),
|
|
93
|
-
|
|
94
|
-
includeBaseParams: false,
|
|
95
|
-
customUrl: 'https://vortex-alb.stg-single-tooth.cell.us.nr-data.net/blob',
|
|
96
|
-
raw: true,
|
|
97
|
-
gzip: true
|
|
93
|
+
raw: true
|
|
98
94
|
}, this)
|
|
99
95
|
|
|
100
96
|
// Wait for an error to be reported. This currently is wrapped around the "Error" feature. This is a feature-feature dependency.
|
|
@@ -153,7 +149,7 @@ export class Aggregate extends AggregateBase {
|
|
|
153
149
|
if (fullSample) this.mode = MODE.FULL // full mode has precedence over error mode
|
|
154
150
|
else if (errorSample) this.mode = MODE.ERROR
|
|
155
151
|
// If neither are selected, then don't record (early return)
|
|
156
|
-
return
|
|
152
|
+
else return
|
|
157
153
|
}
|
|
158
154
|
|
|
159
155
|
// FULL mode records AND reports from the beginning, while ERROR mode only records (but does not report).
|
|
@@ -163,6 +159,11 @@ export class Aggregate extends AggregateBase {
|
|
|
163
159
|
// We only report (harvest) in FULL mode
|
|
164
160
|
this.scheduler.startTimer(this.harvestTimeSeconds)
|
|
165
161
|
}
|
|
162
|
+
|
|
163
|
+
// If an error was noticed before the mode could be set (like in the early lifecycle of the page), immediately set to FULL mode
|
|
164
|
+
if (this.mode === MODE.ERROR && this.errorNoticed) {
|
|
165
|
+
this.mode = MODE.FULL
|
|
166
|
+
}
|
|
166
167
|
// We record in FULL or ERROR mode
|
|
167
168
|
|
|
168
169
|
recorder = (await import(/* webpackChunkName: "recorder" */'rrweb')).record
|
|
@@ -195,7 +196,11 @@ export class Aggregate extends AggregateBase {
|
|
|
195
196
|
const agentRuntime = getRuntime(this.agentIdentifier)
|
|
196
197
|
const info = getInfo(this.agentIdentifier)
|
|
197
198
|
return {
|
|
198
|
-
qs: {
|
|
199
|
+
qs: {
|
|
200
|
+
protocol_version: '0',
|
|
201
|
+
content_encoding: 'gzip',
|
|
202
|
+
browser_monitoring_key: info.licenseKey
|
|
203
|
+
},
|
|
199
204
|
body: {
|
|
200
205
|
type: 'SessionReplay',
|
|
201
206
|
appId: Number(info.applicationID),
|