@newrelic/browser-agent 1.240.0 → 1.242.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.
Files changed (48) hide show
  1. package/dist/cjs/cdn/polyfills/lite.js +13 -1
  2. package/dist/cjs/cdn/polyfills/pro.js +17 -1
  3. package/dist/cjs/cdn/polyfills/spa.js +18 -1
  4. package/dist/cjs/common/config/state/init.js +48 -19
  5. package/dist/cjs/common/constants/env.cdn.js +1 -1
  6. package/dist/cjs/common/constants/env.npm.js +1 -1
  7. package/dist/cjs/common/dom/query-selector.js +16 -0
  8. package/dist/cjs/features/metrics/aggregate/index.js +10 -7
  9. package/dist/cjs/features/page_view_timing/aggregate/index.js +9 -9
  10. package/dist/cjs/features/session_replay/aggregate/index.js +90 -40
  11. package/dist/cjs/features/utils/instrument-base.js +1 -0
  12. package/dist/cjs/loaders/browser-agent.js +2 -1
  13. package/dist/esm/cdn/polyfills/lite.js +8 -1
  14. package/dist/esm/cdn/polyfills/pro.js +13 -2
  15. package/dist/esm/cdn/polyfills/spa.js +13 -1
  16. package/dist/esm/common/config/state/init.js +48 -19
  17. package/dist/esm/common/constants/env.cdn.js +1 -1
  18. package/dist/esm/common/constants/env.npm.js +1 -1
  19. package/dist/esm/common/dom/query-selector.js +9 -0
  20. package/dist/esm/features/metrics/aggregate/index.js +10 -7
  21. package/dist/esm/features/page_view_timing/aggregate/index.js +9 -9
  22. package/dist/esm/features/session_replay/aggregate/index.js +90 -40
  23. package/dist/esm/features/utils/instrument-base.js +1 -0
  24. package/dist/esm/loaders/browser-agent.js +2 -1
  25. package/dist/types/common/config/state/init.d.ts.map +1 -1
  26. package/dist/types/common/dom/query-selector.d.ts +2 -0
  27. package/dist/types/common/dom/query-selector.d.ts.map +1 -0
  28. package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
  29. package/dist/types/features/page_view_timing/aggregate/index.d.ts.map +1 -1
  30. package/dist/types/features/session_replay/aggregate/index.d.ts +17 -5
  31. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  32. package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
  33. package/dist/types/loaders/browser-agent.d.ts.map +1 -1
  34. package/package.json +2 -3
  35. package/src/cdn/polyfills/lite.js +14 -1
  36. package/src/cdn/polyfills/pro.js +23 -2
  37. package/src/cdn/polyfills/spa.js +24 -1
  38. package/src/common/config/state/init.js +46 -17
  39. package/src/common/config/state/init.test.js +40 -0
  40. package/src/common/dom/query-selector.js +9 -0
  41. package/src/common/dom/query-selector.test.js +24 -0
  42. package/src/common/harvest/harvest-scheduler.test.js +2 -2
  43. package/src/features/metrics/aggregate/index.js +5 -3
  44. package/src/features/page_view_timing/aggregate/index.js +7 -6
  45. package/src/features/session_replay/aggregate/index.component-test.js +10 -10
  46. package/src/features/session_replay/aggregate/index.js +71 -34
  47. package/src/features/utils/instrument-base.js +1 -0
  48. package/src/loaders/browser-agent.js +3 -1
@@ -1,4 +1,16 @@
1
1
  "use strict";
2
2
 
3
3
  require("../polyfills.js");
4
- require("../lite");
4
+ var _agent = require("../../loaders/agent");
5
+ var _instrument = require("../../features/page_view_event/instrument");
6
+ var _instrument2 = require("../../features/page_view_timing/instrument");
7
+ var _instrument3 = require("../../features/metrics/instrument");
8
+ /**
9
+ * @file Creates a version of the "Lite" agent loader with [core-js]{@link https://github.com/zloirock/core-js}
10
+ * polyfills for pre-ES6 browsers and IE 11.
11
+ */
12
+
13
+ new _agent.Agent({
14
+ features: [_instrument.Instrument, _instrument2.Instrument, _instrument3.Instrument],
15
+ loaderType: 'lite-polyfills'
16
+ });
@@ -1,4 +1,20 @@
1
1
  "use strict";
2
2
 
3
3
  require("../polyfills.js");
4
- require("../pro");
4
+ var _agent = require("../../loaders/agent");
5
+ var _instrument = require("../../features/page_view_event/instrument");
6
+ var _instrument2 = require("../../features/page_view_timing/instrument");
7
+ var _instrument3 = require("../../features/metrics/instrument");
8
+ var _instrument4 = require("../../features/jserrors/instrument");
9
+ var _instrument5 = require("../../features/ajax/instrument");
10
+ var _instrument6 = require("../../features/session_trace/instrument");
11
+ var _instrument7 = require("../../features/page_action/instrument");
12
+ /**
13
+ * @file Creates a version of the "PRO" agent loader with [core-js]{@link https://github.com/zloirock/core-js}
14
+ * polyfills for pre-ES6 browsers and IE 11.
15
+ */
16
+
17
+ new _agent.Agent({
18
+ features: [_instrument.Instrument, _instrument2.Instrument, _instrument6.Instrument, _instrument5.Instrument, _instrument3.Instrument, _instrument7.Instrument, _instrument4.Instrument],
19
+ loaderType: 'pro-polyfills'
20
+ });
@@ -1,4 +1,21 @@
1
1
  "use strict";
2
2
 
3
3
  require("../polyfills.js");
4
- require("../spa");
4
+ var _agent = require("../../loaders/agent");
5
+ var _instrument = require("../../features/page_view_event/instrument");
6
+ var _instrument2 = require("../../features/page_view_timing/instrument");
7
+ var _instrument3 = require("../../features/metrics/instrument");
8
+ var _instrument4 = require("../../features/jserrors/instrument");
9
+ var _instrument5 = require("../../features/ajax/instrument");
10
+ var _instrument6 = require("../../features/session_trace/instrument");
11
+ var _instrument7 = require("../../features/spa/instrument");
12
+ var _instrument8 = require("../../features/page_action/instrument");
13
+ /**
14
+ * @file Creates a version of the "SPA" agent loader with [core-js]{@link https://github.com/zloirock/core-js}
15
+ * polyfills for pre-ES6 browsers and IE 11.
16
+ */
17
+
18
+ new _agent.Agent({
19
+ features: [_instrument5.Instrument, _instrument.Instrument, _instrument2.Instrument, _instrument6.Instrument, _instrument3.Instrument, _instrument8.Instrument, _instrument4.Instrument, _instrument7.Instrument],
20
+ loaderType: 'spa-polyfills'
21
+ });
@@ -6,16 +6,36 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.getConfiguration = getConfiguration;
7
7
  exports.getConfigurationValue = getConfigurationValue;
8
8
  exports.setConfiguration = setConfiguration;
9
+ var _querySelector = require("../../dom/query-selector");
9
10
  var _constants = require("../../session/constants");
11
+ var _console = require("../../util/console");
10
12
  var _nreum = require("../../window/nreum");
11
13
  var _configurable = require("./configurable");
12
14
  const model = () => {
13
15
  const hiddenState = {
14
- blockSelector: '[data-nr-block]',
15
- maskInputOptions: {
16
- password: true
16
+ mask_selector: '*',
17
+ block_selector: '[data-nr-block]',
18
+ mask_input_options: {
19
+ color: false,
20
+ date: false,
21
+ 'datetime-local': false,
22
+ email: false,
23
+ month: false,
24
+ number: false,
25
+ range: false,
26
+ search: false,
27
+ tel: false,
28
+ text: false,
29
+ time: false,
30
+ url: false,
31
+ week: false,
32
+ // unify textarea and select element with text input
33
+ textarea: false,
34
+ select: false,
35
+ password: true // This will be enforced to always be true in the setter
17
36
  }
18
37
  };
38
+
19
39
  return {
20
40
  proxy: {
21
41
  assets: undefined,
@@ -86,38 +106,47 @@ const model = () => {
86
106
  autoStart: true,
87
107
  enabled: false,
88
108
  harvestTimeSeconds: 60,
89
- sampleRate: 0.1,
90
- errorSampleRate: 0.1,
109
+ sampling_rate: 50,
110
+ // float from 0 - 100
111
+ error_sampling_rate: 50,
112
+ // float from 0 - 100
91
113
  // recording config settings
92
- maskTextSelector: '*',
93
- maskAllInputs: true,
114
+ mask_all_inputs: true,
115
+ // this has a getter/setter to facilitate validation of the selectors
116
+ get mask_text_selector() {
117
+ return hiddenState.mask_selector;
118
+ },
119
+ set mask_text_selector(val) {
120
+ if ((0, _querySelector.isValidSelector)(val)) hiddenState.mask_selector = val + ',[data-nr-mask]';else if (val === null) hiddenState.mask_selector = val; // null is acceptable, which completely disables the behavior
121
+ else (0, _console.warn)('An invalid session_replay.mask_selector was provided and will not be used', val);
122
+ },
94
123
  // these properties only have getters because they are enforcable constants and should error if someone tries to override them
95
- get blockClass() {
124
+ get block_class() {
96
125
  return 'nr-block';
97
126
  },
98
- get ignoreClass() {
127
+ get ignore_class() {
99
128
  return 'nr-ignore';
100
129
  },
101
- get maskTextClass() {
130
+ get mask_text_class() {
102
131
  return 'nr-mask';
103
132
  },
104
133
  // props with a getter and setter are used to extend enforcable constants with customer input
105
134
  // we must preserve data-nr-block no matter what else the customer sets
106
- get blockSelector() {
107
- return hiddenState.blockSelector;
135
+ get block_selector() {
136
+ return hiddenState.block_selector;
108
137
  },
109
- set blockSelector(val) {
110
- hiddenState.blockSelector += ",".concat(val);
138
+ set block_selector(val) {
139
+ if ((0, _querySelector.isValidSelector)(val)) hiddenState.block_selector += ",".concat(val);else if (val !== '') (0, _console.warn)('An invalid session_replay.block_selector was provided and will not be used', val);
111
140
  },
112
141
  // password: must always be present and true no matter what customer sets
113
- get maskInputOptions() {
114
- return hiddenState.maskInputOptions;
142
+ get mask_input_options() {
143
+ return hiddenState.mask_input_options;
115
144
  },
116
- set maskInputOptions(val) {
117
- hiddenState.maskInputOptions = {
145
+ set mask_input_options(val) {
146
+ if (val && typeof val === 'object') hiddenState.mask_input_options = {
118
147
  ...val,
119
148
  password: true
120
- };
149
+ };else (0, _console.warn)('An invalid session_replay.mask_input_option was provided and will not be used', val);
121
150
  }
122
151
  },
123
152
  spa: {
@@ -12,7 +12,7 @@ exports.VERSION = exports.DIST_METHOD = exports.BUILD_ENV = void 0;
12
12
  /**
13
13
  * Exposes the version of the agent
14
14
  */
15
- const VERSION = "1.240.0";
15
+ const VERSION = "1.242.0";
16
16
 
17
17
  /**
18
18
  * Exposes the build type of the agent
@@ -12,7 +12,7 @@ exports.VERSION = exports.DIST_METHOD = exports.BUILD_ENV = void 0;
12
12
  /**
13
13
  * Exposes the version of the agent
14
14
  */
15
- const VERSION = "1.240.0";
15
+ const VERSION = "1.242.0";
16
16
 
17
17
  /**
18
18
  * Exposes the build type of the agent
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.isValidSelector = void 0;
7
+ const isValidSelector = selector => {
8
+ if (!selector || typeof selector !== 'string') return false;
9
+ try {
10
+ document.createDocumentFragment().querySelector(selector);
11
+ } catch {
12
+ return false;
13
+ }
14
+ return true;
15
+ };
16
+ exports.isValidSelector = isValidSelector;
@@ -33,13 +33,16 @@ class Aggregate extends _aggregateBase.AggregateBase {
33
33
  this.singleChecks(); // checks that are run only one time, at script load
34
34
  this.eachSessionChecks(); // the start of every time user engages with page
35
35
 
36
- // *cli, Mar 23 - Per NR-94597, this feature should only harvest ONCE at the (potential) EoL time of the page.
37
- scheduler = new _harvestScheduler.HarvestScheduler('jserrors', {
38
- onUnload: () => this.unload()
39
- }, this);
40
- scheduler.harvest.on('jserrors', () => ({
41
- body: this.aggregator.take(['cm', 'sm'])
42
- }));
36
+ this.ee.on("drain-".concat(this.featureName), () => {
37
+ // *cli, Mar 23 - Per NR-94597, this feature should only harvest ONCE at the (potential) EoL time of the page.
38
+ scheduler = new _harvestScheduler.HarvestScheduler('jserrors', {
39
+ onUnload: () => this.unload()
40
+ }, this);
41
+ scheduler.harvest.on('jserrors', () => ({
42
+ body: this.aggregator.take(['cm', 'sm'])
43
+ }));
44
+ }); // this is needed to ensure EoL is "on" and sent
45
+
43
46
  this.drain();
44
47
  }
45
48
  storeSupportabilityMetrics(name, value) {
@@ -58,21 +58,21 @@ class Aggregate extends _aggregateBase.AggregateBase {
58
58
 
59
59
  /* It's important that CWV api, like "onLCP", is called before this scheduler is initialized. The reason is because they listen to the same
60
60
  on vis change or pagehide events, and we'd want ex. onLCP to record the timing (win the race) before we try to send "final harvest". */
61
- this.scheduler = new _harvestScheduler.HarvestScheduler('events', {
62
- onFinished: function () {
63
- return _this.onHarvestFinished(...arguments);
64
- },
65
- getPayload: function () {
66
- return _this.prepareHarvest(...arguments);
67
- }
68
- }, this);
69
- (0, _registerHandler.registerHandler)('timing', (name, value, attrs) => this.addTiming(name, value, attrs), this.featureName, this.ee); // notice CLS is added to all timings via 4th param
61
+
70
62
  (0, _registerHandler.registerHandler)('docHidden', msTimestamp => this.endCurrentSession(msTimestamp), this.featureName, this.ee);
71
63
  (0, _registerHandler.registerHandler)('winPagehide', msTimestamp => this.recordPageUnload(msTimestamp), this.featureName, this.ee);
72
64
  const initialHarvestSeconds = (0, _config.getConfigurationValue)(this.agentIdentifier, 'page_view_timing.initialHarvestSeconds') || 10;
73
65
  const harvestTimeSeconds = (0, _config.getConfigurationValue)(this.agentIdentifier, 'page_view_timing.harvestTimeSeconds') || 30;
74
66
  // send initial data sooner, then start regular
75
67
  this.ee.on("drain-".concat(this.featureName), () => {
68
+ this.scheduler = new _harvestScheduler.HarvestScheduler('events', {
69
+ onFinished: function () {
70
+ return _this.onHarvestFinished(...arguments);
71
+ },
72
+ getPayload: function () {
73
+ return _this.prepareHarvest(...arguments);
74
+ }
75
+ }, this);
76
76
  this.scheduler.startTimer(harvestTimeSeconds, initialHarvestSeconds);
77
77
  });
78
78
  this.drain();
@@ -14,6 +14,9 @@ var _aggregateBase = require("../../utils/aggregate-base");
14
14
  var _sharedChannel = require("../../../common/constants/shared-channel");
15
15
  var _encode = require("../../../common/url/encode");
16
16
  var _console = require("../../../common/util/console");
17
+ var _runtime = require("../../../common/constants/runtime");
18
+ var _constants2 = require("../../metrics/constants");
19
+ var _features = require("../../../loaders/features/features");
17
20
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
18
21
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } /*
19
22
  * Copyright 2023 New Relic Corporation. All rights reserved.
@@ -72,17 +75,30 @@ class Aggregate extends _aggregateBase.AggregateBase {
72
75
  * -- When visibility changes from "hidden" -> "visible", it must capture a full snapshot for the replay to work correctly across tabs
73
76
  */
74
77
  this.hasSnapshot = false;
78
+ /** Payload metadata -- Should indicate that the payload being sent has a meta node. The meta node should always precede a snapshot node. */
79
+ this.hasMeta = false;
75
80
  /** Payload metadata -- Should indicate that the payload being sent contains an error. Used for query/filter purposes in UI */
76
81
  this.hasError = false;
77
82
 
78
- /** Payload metadata -- Should indicate when a replay blob started recording. Resets each time a harvest occurs. */
83
+ /** Payload metadata -- Should indicate when a replay blob started recording. Resets each time a harvest occurs.
84
+ * cycle timestamps are used as fallbacks if event timestamps cannot be used
85
+ */
79
86
  this.timestamp = {
80
- first: undefined,
81
- last: undefined
87
+ event: {
88
+ first: undefined,
89
+ last: undefined
90
+ },
91
+ cycle: {
92
+ first: undefined,
93
+ last: undefined
94
+ }
82
95
  };
83
96
 
84
97
  /** A value which increments with every new mutation node reported. Resets after a harvest is sent */
85
98
  this.payloadBytesEstimation = 0;
99
+
100
+ /** 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 */
101
+ this.lastMeta = undefined;
86
102
  const shouldSetup = (0, _config.getConfigurationValue)(agentIdentifier, 'privacy.cookies_enabled') === true && (0, _config.getConfigurationValue)(agentIdentifier, 'session_trace.enabled') === true;
87
103
 
88
104
  /** The method to stop recording. This defaults to a noop, but is overwritten once the recording library is imported and initialized */
@@ -90,7 +106,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
90
106
  if (shouldSetup) {
91
107
  // 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.
92
108
  this.ee.on(_sessionEntity.SESSION_EVENTS.RESET, () => {
93
- this.abort();
109
+ this.abort('Session Reset');
94
110
  });
95
111
 
96
112
  // The SessionEntity class can emit a message indicating the session was paused (visibility change). This feature must stop recording if that occurs.
@@ -132,7 +148,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
132
148
  }, this.featureName, this.ee);
133
149
  this.waitForFlags(['sr']).then(_ref => {
134
150
  let [flagOn] = _ref;
135
- return this.initializeRecording(flagOn, Math.random() < (0, _config.getConfigurationValue)(this.agentIdentifier, 'session_replay.errorSampleRate'), Math.random() < (0, _config.getConfigurationValue)(this.agentIdentifier, 'session_replay.sampleRate'));
151
+ return this.initializeRecording(flagOn, Math.random() * 100 < (0, _config.getConfigurationValue)(this.agentIdentifier, 'session_replay.error_sampling_rate'), Math.random() * 100 < (0, _config.getConfigurationValue)(this.agentIdentifier, 'session_replay.sampling_rate'));
136
152
  }).then(() => _sharedChannel.sharedChannel.onReplayReady(this.mode)); // notify watchers that replay started with the mode
137
153
 
138
154
  this.drain();
@@ -173,6 +189,12 @@ class Aggregate extends _aggregateBase.AggregateBase {
173
189
  if (this.mode === _sessionEntity.MODE.ERROR && this.errorNoticed) {
174
190
  this.mode = _sessionEntity.MODE.FULL;
175
191
  }
192
+ try {
193
+ // Do not change the webpackChunkName or it will break the webpack nrba-chunking plugin
194
+ recorder = (await Promise.resolve().then(() => _interopRequireWildcard(require( /* webpackChunkName: "recorder" */'rrweb')))).record;
195
+ } catch (err) {
196
+ return this.abort('Recorder failed to import');
197
+ }
176
198
 
177
199
  // FULL mode records AND reports from the beginning, while ERROR mode only records (but does not report).
178
200
  // ERROR mode will do this until an error is thrown, and then switch into FULL mode.
@@ -181,12 +203,6 @@ class Aggregate extends _aggregateBase.AggregateBase {
181
203
  // We only report (harvest) in FULL mode
182
204
  this.scheduler.startTimer(this.harvestTimeSeconds);
183
205
  }
184
- try {
185
- // Do not change the webpackChunkName or it will break the webpack nrba-chunking plugin
186
- recorder = (await Promise.resolve().then(() => _interopRequireWildcard(require( /* webpackChunkName: "recorder" */'rrweb')))).record;
187
- } catch (err) {
188
- return this.abort();
189
- }
190
206
  try {
191
207
  // Do not change the webpackChunkName or it will break the webpack nrba-chunking plugin
192
208
  const {
@@ -221,6 +237,8 @@ class Aggregate extends _aggregateBase.AggregateBase {
221
237
  getHarvestContents() {
222
238
  const agentRuntime = (0, _config.getRuntime)(this.agentIdentifier);
223
239
  const info = (0, _config.getInfo)(this.agentIdentifier);
240
+ const firstTimestamp = this.timestamp.event.first || this.timestamp.cycle.first;
241
+ const lastTimestamp = this.timestamp.event.last || this.timestamp.cycle.last;
224
242
  return {
225
243
  qs: {
226
244
  browser_monitoring_key: info.licenseKey,
@@ -231,11 +249,12 @@ class Aggregate extends _aggregateBase.AggregateBase {
231
249
  ...(this.shouldCompress && {
232
250
  content_encoding: 'gzip'
233
251
  }),
234
- 'replay.firstTimestamp': this.timestamp.first,
235
- 'replay.lastTimestamp': this.timestamp.last,
236
- 'replay.durationMs': this.timestamp.last - this.timestamp.first,
252
+ 'replay.firstTimestamp': firstTimestamp,
253
+ 'replay.lastTimestamp': lastTimestamp,
254
+ 'replay.durationMs': lastTimestamp - firstTimestamp,
237
255
  agentVersion: agentRuntime.version,
238
256
  session: agentRuntime.session.state.value,
257
+ hasMeta: this.hasMeta,
239
258
  hasSnapshot: this.hasSnapshot,
240
259
  hasError: this.hasError,
241
260
  isFirstChunk: this.isFirstChunk,
@@ -250,7 +269,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
250
269
  onHarvestFinished(result) {
251
270
  // The mutual decision for now is to stop recording and clear buffers if ingest is experiencing 429 rate limiting
252
271
  if (result.status === 429) {
253
- this.abort();
272
+ this.abort('429: Too many requests');
254
273
  }
255
274
  if (this.blocked) this.scheduler.stopTimer(true);
256
275
  }
@@ -260,6 +279,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
260
279
  this.events = [];
261
280
  this.isFirstChunk = false;
262
281
  this.hasSnapshot = false;
282
+ this.hasMeta = false;
263
283
  this.hasError = false;
264
284
  this.payloadBytesEstimation = 0;
265
285
  this.clearTimestamps();
@@ -269,29 +289,32 @@ class Aggregate extends _aggregateBase.AggregateBase {
269
289
  startRecording() {
270
290
  if (!recorder) {
271
291
  (0, _console.warn)('Recording library was never imported');
272
- return this.abort();
292
+ return this.abort('Recorder was never imported');
273
293
  }
294
+ this.clearTimestamps();
295
+ // set the fallbacks as early as possible
296
+ this.setTimestamps();
274
297
  this.recording = true;
275
298
  const {
276
- blockClass,
277
- ignoreClass,
278
- maskTextClass,
279
- blockSelector,
280
- maskInputOptions,
281
- maskTextSelector,
282
- maskAllInputs
299
+ block_class,
300
+ ignore_class,
301
+ mask_text_class,
302
+ block_selector,
303
+ mask_input_options,
304
+ mask_text_selector,
305
+ mask_all_inputs
283
306
  } = (0, _config.getConfigurationValue)(this.agentIdentifier, 'session_replay');
284
307
  // set up rrweb configurations for maximum privacy --
285
308
  // https://newrelic.atlassian.net/wiki/spaces/O11Y/pages/2792293280/2023+02+28+Browser+-+Session+Replay#Configuration-options
286
309
  const stop = recorder({
287
310
  emit: this.store.bind(this),
288
- blockClass,
289
- ignoreClass,
290
- maskTextClass,
291
- blockSelector,
292
- maskInputOptions,
293
- maskTextSelector,
294
- maskAllInputs,
311
+ blockClass: block_class,
312
+ ignoreClass: ignore_class,
313
+ maskTextClass: mask_text_class,
314
+ blockSelector: block_selector,
315
+ maskInputOptions: mask_input_options,
316
+ maskTextSelector: mask_text_selector,
317
+ maskAllInputs: mask_all_inputs,
295
318
  checkoutEveryNms: CHECKOUT_MS[this.mode]
296
319
  });
297
320
  this.stopRecording = () => {
@@ -302,6 +325,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
302
325
 
303
326
  /** Store a payload in the buffer (this.events). This should be the callback to the recording lib noticing a mutation */
304
327
  store(event, isCheckout) {
328
+ this.setTimestamps(event);
305
329
  if (this.blocked) return;
306
330
  const eventBytes = (0, _stringify.stringify)(event).length;
307
331
  /** The estimated size of the payload after compression */
@@ -309,7 +333,8 @@ class Aggregate extends _aggregateBase.AggregateBase {
309
333
  // Vortex will block payloads at a certain size, we might as well not send.
310
334
  if (payloadSize > MAX_PAYLOAD_SIZE) {
311
335
  this.clearBuffer();
312
- return this.abort();
336
+ this.ee.emit(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['SessionReplay/Too-Big/Seen'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
337
+ return this.abort('Payload too big');
313
338
  }
314
339
  // Checkout events are flags by the recording lib that indicate a fullsnapshot was taken every n ms. These are important
315
340
  // to help reconstruct the replay later and must be included. While waiting and buffering for errors to come through,
@@ -318,8 +343,22 @@ class Aggregate extends _aggregateBase.AggregateBase {
318
343
  // we are still waiting for an error to throw, so keep wiping the buffer over time
319
344
  this.clearBuffer();
320
345
  }
321
- this.setTimestamps(event);
322
- if (event.type === 2) this.hasSnapshot = true;
346
+
347
+ // meta event
348
+ if (event.type === 4) {
349
+ this.hasMeta = true;
350
+ this.lastMeta = event;
351
+ }
352
+ // snapshot event
353
+ if (event.type === 2) {
354
+ this.hasSnapshot = true;
355
+ // small chance that the meta event got separated from its matching snapshot across payload harvests
356
+ // it needs to precede the snapshot, so shove it in first.
357
+ if (!this.hasMeta) {
358
+ this.events.push(this.lastMeta);
359
+ this.hasMeta = true;
360
+ }
361
+ }
323
362
  this.events.push(event);
324
363
  this.payloadBytesEstimation += eventBytes;
325
364
 
@@ -336,15 +375,25 @@ class Aggregate extends _aggregateBase.AggregateBase {
336
375
  if (!recorder) return;
337
376
  recorder.takeFullSnapshot();
338
377
  }
339
- setTimestamps(rrwebEvent) {
340
- if (!rrwebEvent) return;
341
- if (!this.timestamp.first) this.timestamp.first = rrwebEvent.timestamp;
342
- this.timestamp.last = rrwebEvent.timestamp;
378
+ setTimestamps(event) {
379
+ // fallbacks if timestamps cannot be derived from rrweb events
380
+ this.timestamp.cycle.last = (0, _config.getRuntime)(this.agentIdentifier).offset + _runtime.globalScope.performance.now();
381
+ if (!this.timestamp.cycle.first) this.timestamp.cycle.first = this.timestamp.cycle.last;
382
+ // timestamps based on rrweb events
383
+ if (!event || !event.timestamp) return;
384
+ if (!this.timestamp.event.first) this.timestamp.event.first = event.timestamp;
385
+ this.timestamp.event.last = event.timestamp;
343
386
  }
344
387
  clearTimestamps() {
345
388
  this.timestamp = {
346
- first: undefined,
347
- last: undefined
389
+ event: {
390
+ first: undefined,
391
+ last: undefined
392
+ },
393
+ cycle: {
394
+ first: undefined,
395
+ last: undefined
396
+ }
348
397
  };
349
398
  }
350
399
 
@@ -356,7 +405,8 @@ class Aggregate extends _aggregateBase.AggregateBase {
356
405
  }
357
406
 
358
407
  /** Abort the feature, once aborted it will not resume */
359
- abort() {
408
+ abort(reason) {
409
+ (0, _console.warn)("SR aborted -- ".concat(reason));
360
410
  this.blocked = true;
361
411
  this.mode = _sessionEntity.MODE.OFF;
362
412
  this.stopRecording();
@@ -119,6 +119,7 @@ class InstrumentBase extends _featureBase.FeatureBase {
119
119
  (0, _console.warn)("Downloading and initializing ".concat(this.featureName, " failed..."), e);
120
120
  this.abortHandler?.(); // undo any important alterations made to the page
121
121
  // not supported yet but nice to do: "abort" this agent's EE for this feature specifically
122
+ (0, _drain.drain)(this.agentIdentifier, this.featureName);
122
123
  loadedSuccessfully(false);
123
124
  }
124
125
  };
@@ -13,6 +13,7 @@ var _instrument5 = require("../features/ajax/instrument");
13
13
  var _instrument6 = require("../features/session_trace/instrument");
14
14
  var _instrument7 = require("../features/spa/instrument");
15
15
  var _instrument8 = require("../features/page_action/instrument");
16
+ var _instrument9 = require("../features/session_replay/instrument");
16
17
  /**
17
18
  * An agent class with all feature modules available. Features may be disabled and enabled via runtime configuration.
18
19
  * The BrowserAgent class is the most convenient and reliable option for most use cases.
@@ -21,7 +22,7 @@ class BrowserAgent extends _agent.Agent {
21
22
  constructor(args) {
22
23
  super({
23
24
  ...args,
24
- features: [_instrument5.Instrument, _instrument.Instrument, _instrument2.Instrument, _instrument6.Instrument, _instrument3.Instrument, _instrument8.Instrument, _instrument4.Instrument, _instrument7.Instrument],
25
+ features: [_instrument5.Instrument, _instrument.Instrument, _instrument2.Instrument, _instrument6.Instrument, _instrument3.Instrument, _instrument8.Instrument, _instrument4.Instrument, _instrument7.Instrument, _instrument9.Instrument],
25
26
  loaderType: 'browser-agent'
26
27
  });
27
28
  }
@@ -4,4 +4,11 @@
4
4
  */
5
5
 
6
6
  import '../polyfills.js';
7
- import '../lite';
7
+ import { Agent } from '../../loaders/agent';
8
+ import { Instrument as InstrumentPageViewEvent } from '../../features/page_view_event/instrument';
9
+ import { Instrument as InstrumentPageViewTiming } from '../../features/page_view_timing/instrument';
10
+ import { Instrument as InstrumentMetrics } from '../../features/metrics/instrument';
11
+ new Agent({
12
+ features: [InstrumentPageViewEvent, InstrumentPageViewTiming, InstrumentMetrics],
13
+ loaderType: 'lite-polyfills'
14
+ });
@@ -1,7 +1,18 @@
1
1
  /**
2
- * @file Creates a version of the "Pro" agent loader with [core-js]{@link https://github.com/zloirock/core-js}
2
+ * @file Creates a version of the "PRO" agent loader with [core-js]{@link https://github.com/zloirock/core-js}
3
3
  * polyfills for pre-ES6 browsers and IE 11.
4
4
  */
5
5
 
6
6
  import '../polyfills.js';
7
- import '../pro';
7
+ import { Agent } from '../../loaders/agent';
8
+ import { Instrument as InstrumentPageViewEvent } from '../../features/page_view_event/instrument';
9
+ import { Instrument as InstrumentPageViewTiming } from '../../features/page_view_timing/instrument';
10
+ import { Instrument as InstrumentMetrics } from '../../features/metrics/instrument';
11
+ import { Instrument as InstrumentErrors } from '../../features/jserrors/instrument';
12
+ import { Instrument as InstrumentXhr } from '../../features/ajax/instrument';
13
+ import { Instrument as InstrumentSessionTrace } from '../../features/session_trace/instrument';
14
+ import { Instrument as InstrumentPageAction } from '../../features/page_action/instrument';
15
+ new Agent({
16
+ features: [InstrumentPageViewEvent, InstrumentPageViewTiming, InstrumentSessionTrace, InstrumentXhr, InstrumentMetrics, InstrumentPageAction, InstrumentErrors],
17
+ loaderType: 'pro-polyfills'
18
+ });
@@ -4,4 +4,16 @@
4
4
  */
5
5
 
6
6
  import '../polyfills.js';
7
- import '../spa';
7
+ import { Agent } from '../../loaders/agent';
8
+ import { Instrument as InstrumentPageViewEvent } from '../../features/page_view_event/instrument';
9
+ import { Instrument as InstrumentPageViewTiming } from '../../features/page_view_timing/instrument';
10
+ import { Instrument as InstrumentMetrics } from '../../features/metrics/instrument';
11
+ import { Instrument as InstrumentErrors } from '../../features/jserrors/instrument';
12
+ import { Instrument as InstrumentXhr } from '../../features/ajax/instrument';
13
+ import { Instrument as InstrumentSessionTrace } from '../../features/session_trace/instrument';
14
+ import { Instrument as InstrumentSpa } from '../../features/spa/instrument';
15
+ import { Instrument as InstrumentPageAction } from '../../features/page_action/instrument';
16
+ new Agent({
17
+ features: [InstrumentXhr, InstrumentPageViewEvent, InstrumentPageViewTiming, InstrumentSessionTrace, InstrumentMetrics, InstrumentPageAction, InstrumentErrors, InstrumentSpa],
18
+ loaderType: 'spa-polyfills'
19
+ });