@newrelic/browser-agent 1.256.0 → 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.
Files changed (72) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/cjs/common/constants/env.cdn.js +1 -1
  3. package/dist/cjs/common/constants/env.npm.js +1 -1
  4. package/dist/cjs/common/harvest/harvest.js +7 -6
  5. package/dist/cjs/features/jserrors/aggregate/index.js +25 -12
  6. package/dist/cjs/features/session_replay/aggregate/index.js +11 -5
  7. package/dist/cjs/features/session_replay/constants.js +2 -1
  8. package/dist/cjs/features/session_replay/instrument/index.js +15 -5
  9. package/dist/cjs/features/session_replay/shared/recorder.js +6 -3
  10. package/dist/cjs/features/session_replay/shared/utils.js +6 -5
  11. package/dist/cjs/features/session_trace/aggregate/index.js +22 -19
  12. package/dist/cjs/features/soft_navigations/aggregate/index.js +2 -2
  13. package/dist/cjs/features/utils/instrument-base.js +11 -14
  14. package/dist/cjs/features/utils/nr1-debugger.js +27 -0
  15. package/dist/cjs/loaders/agent.js +4 -0
  16. package/dist/cjs/loaders/configure/configure.js +0 -1
  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/harvest/harvest.js +7 -6
  20. package/dist/esm/features/jserrors/aggregate/index.js +25 -12
  21. package/dist/esm/features/session_replay/aggregate/index.js +11 -5
  22. package/dist/esm/features/session_replay/constants.js +2 -1
  23. package/dist/esm/features/session_replay/instrument/index.js +16 -6
  24. package/dist/esm/features/session_replay/shared/recorder.js +6 -3
  25. package/dist/esm/features/session_replay/shared/utils.js +5 -3
  26. package/dist/esm/features/session_trace/aggregate/index.js +22 -19
  27. package/dist/esm/features/soft_navigations/aggregate/index.js +2 -2
  28. package/dist/esm/features/utils/instrument-base.js +11 -14
  29. package/dist/esm/features/utils/nr1-debugger.js +21 -0
  30. package/dist/esm/loaders/agent.js +4 -0
  31. package/dist/esm/loaders/configure/configure.js +0 -1
  32. package/dist/types/common/harvest/harvest.d.ts +1 -1
  33. package/dist/types/common/harvest/harvest.d.ts.map +1 -1
  34. package/dist/types/features/jserrors/aggregate/index.d.ts +0 -1
  35. package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
  36. package/dist/types/features/session_replay/aggregate/index.d.ts +0 -1
  37. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  38. package/dist/types/features/session_replay/constants.d.ts +1 -0
  39. package/dist/types/features/session_replay/constants.d.ts.map +1 -1
  40. package/dist/types/features/session_replay/instrument/index.d.ts +1 -0
  41. package/dist/types/features/session_replay/instrument/index.d.ts.map +1 -1
  42. package/dist/types/features/session_replay/shared/recorder.d.ts +1 -0
  43. package/dist/types/features/session_replay/shared/recorder.d.ts.map +1 -1
  44. package/dist/types/features/session_replay/shared/utils.d.ts +2 -2
  45. package/dist/types/features/session_replay/shared/utils.d.ts.map +1 -1
  46. package/dist/types/features/session_trace/aggregate/index.d.ts +1 -2
  47. package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
  48. package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
  49. package/dist/types/features/utils/nr1-debugger.d.ts +2 -0
  50. package/dist/types/features/utils/nr1-debugger.d.ts.map +1 -0
  51. package/dist/types/loaders/agent.d.ts +5 -1
  52. package/dist/types/loaders/agent.d.ts.map +1 -1
  53. package/dist/types/loaders/configure/configure.d.ts.map +1 -1
  54. package/package.json +1 -1
  55. package/src/common/drain/__mocks__/drain.js +2 -0
  56. package/src/common/harvest/harvest.js +9 -7
  57. package/src/common/window/__mocks__/load.js +3 -0
  58. package/src/features/jserrors/aggregate/index.js +26 -10
  59. package/src/features/session_replay/aggregate/index.js +17 -7
  60. package/src/features/session_replay/constants.js +2 -1
  61. package/src/features/session_replay/instrument/index.js +16 -6
  62. package/src/features/session_replay/shared/__mocks__/utils.js +2 -0
  63. package/src/features/session_replay/shared/recorder.js +7 -4
  64. package/src/features/session_replay/shared/utils.js +5 -3
  65. package/src/features/session_trace/aggregate/index.js +16 -18
  66. package/src/features/soft_navigations/aggregate/index.js +2 -2
  67. package/src/features/utils/__mocks__/agent-session.js +1 -0
  68. package/src/features/utils/__mocks__/feature-base.js +11 -0
  69. package/src/features/utils/instrument-base.js +11 -14
  70. package/src/features/utils/nr1-debugger.js +22 -0
  71. package/src/loaders/agent.js +4 -0
  72. package/src/loaders/configure/configure.js +0 -1
package/CHANGELOG.md CHANGED
@@ -3,6 +3,29 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [1.257.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.256.1...v1.257.0) (2024-04-18)
7
+
8
+
9
+ ### Features
10
+
11
+ * Decorate errors with hasReplay individually ([#983](https://github.com/newrelic/newrelic-browser-agent/issues/983)) ([b6a7a3e](https://github.com/newrelic/newrelic-browser-agent/commit/b6a7a3ebcf2a69b9cbe9888208bb62330918cdf7))
12
+ * Session Replay preload optimizations ([#982](https://github.com/newrelic/newrelic-browser-agent/issues/982)) ([fa20693](https://github.com/newrelic/newrelic-browser-agent/commit/fa20693d746bed2fa0b8ff972e4b9bee4bbe6956))
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * Agent class type declarations ([#987](https://github.com/newrelic/newrelic-browser-agent/issues/987)) ([b682c88](https://github.com/newrelic/newrelic-browser-agent/commit/b682c880bfb149b61f6c00bf821459ea55a37ae8))
18
+ * JSEerrors harvest hasReplay decoration ([#986](https://github.com/newrelic/newrelic-browser-agent/issues/986)) ([6dd09c5](https://github.com/newrelic/newrelic-browser-agent/commit/6dd09c505af87b3a1b08330362eca46951ea22ed))
19
+ * Session replay preload without autoStart ([#985](https://github.com/newrelic/newrelic-browser-agent/issues/985)) ([f50351a](https://github.com/newrelic/newrelic-browser-agent/commit/f50351acb08b65b03e7f4b5530a001a80fc04ece))
20
+ * Soft navigations memory leak on harvest ([#979](https://github.com/newrelic/newrelic-browser-agent/issues/979)) ([53bb120](https://github.com/newrelic/newrelic-browser-agent/commit/53bb1209cb66fe1a52385b2863e35a93fb29afae))
21
+
22
+ ## [1.256.1](https://github.com/newrelic/newrelic-browser-agent/compare/v1.256.0...v1.256.1) (2024-04-15)
23
+
24
+
25
+ ### Bug Fixes
26
+
27
+ * Revert "Generate PTID in Agent" ([#976](https://github.com/newrelic/newrelic-browser-agent/issues/976)) ([34b317f](https://github.com/newrelic/newrelic-browser-agent/commit/34b317fe577487af56d48861b7f256ec8d644d69))
28
+
6
29
  ## [1.256.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.255.0...v1.256.0) (2024-04-11)
7
30
 
8
31
 
@@ -12,7 +12,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_EN
12
12
  /**
13
13
  * Exposes the version of the agent
14
14
  */
15
- const VERSION = exports.VERSION = "1.256.0";
15
+ const VERSION = exports.VERSION = "1.257.0";
16
16
 
17
17
  /**
18
18
  * Exposes the build type of the agent
@@ -12,7 +12,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_EN
12
12
  /**
13
13
  * Exposes the version of the agent
14
14
  */
15
- const VERSION = exports.VERSION = "1.256.0";
15
+ const VERSION = exports.VERSION = "1.257.0";
16
16
 
17
17
  /**
18
18
  * Exposes the build type of the agent
@@ -137,7 +137,7 @@ class Harvest extends _sharedContext.SharedContext {
137
137
  let url = "".concat(protocol, "://").concat(perceviedBeacon).concat(endpointURLPart, "/1/").concat(info.licenseKey);
138
138
  if (customUrl) url = customUrl;
139
139
  if (raw) url = "".concat(protocol, "://").concat(perceviedBeacon, "/").concat(endpoint);
140
- const baseParams = !raw && includeBaseParams ? this.baseQueryString(qs) : '';
140
+ const baseParams = !raw && includeBaseParams ? this.baseQueryString(qs, endpoint) : '';
141
141
  let payloadParams = (0, _encode.obj)(qs, agentRuntime.maxBytes);
142
142
  if (!submitMethod) {
143
143
  submitMethod = submitData.getSubmitMethod({
@@ -186,7 +186,6 @@ class Harvest extends _sharedContext.SharedContext {
186
186
  const cbResult = {
187
187
  sent: this.status !== 0,
188
188
  status: this.status,
189
- failed: this.status === 0 || this.status >= 400,
190
189
  xhr: this,
191
190
  fullUrl
192
191
  };
@@ -208,17 +207,19 @@ class Harvest extends _sharedContext.SharedContext {
208
207
  }
209
208
 
210
209
  // The stuff that gets sent every time.
211
- baseQueryString(qs) {
210
+ baseQueryString(qs, endpoint) {
212
211
  const runtime = (0, _config.getRuntime)(this.sharedContext.agentIdentifier);
213
212
  const info = (0, _config.getInfo)(this.sharedContext.agentIdentifier);
214
213
  const location = (0, _cleanUrl.cleanURL)((0, _location.getLocation)());
215
214
  const ref = this.obfuscator.shouldObfuscate() ? this.obfuscator.obfuscateString(location) : location;
216
- return ['a=' + info.applicationID, (0, _encode.param)('sa', info.sa ? '' + info.sa : ''), (0, _encode.param)('v', _env.VERSION), transactionNameParam(info), (0, _encode.param)('ct', runtime.customTransaction), '&rst=' + (0, _now.now)(), '&ck=0',
215
+ const hr = runtime?.session?.state.sessionReplayMode === 1 && endpoint !== 'jserrors';
216
+ const qps = ['a=' + info.applicationID, (0, _encode.param)('sa', info.sa ? '' + info.sa : ''), (0, _encode.param)('v', _env.VERSION), transactionNameParam(info), (0, _encode.param)('ct', runtime.customTransaction), '&rst=' + (0, _now.now)(), '&ck=0',
217
217
  // ck param DEPRECATED - still expected by backend
218
218
  '&s=' + (runtime.session?.state.value || '0'),
219
219
  // the 0 id encaps all untrackable and default traffic
220
- (0, _encode.param)('ref', ref), (0, _encode.param)('ptid', runtime.ptid ? '' + runtime.ptid : ''), (0, _encode.param)('hr', runtime?.session?.state.sessionReplayMode === 1 ? '1' : '0', qs) // hasReplay
221
- ].join('');
220
+ (0, _encode.param)('ref', ref), (0, _encode.param)('ptid', runtime.ptid ? '' + runtime.ptid : '')];
221
+ if (hr) qps.push((0, _encode.param)('hr', '1', qs));
222
+ return qps.join('');
222
223
  }
223
224
 
224
225
  /**
@@ -42,13 +42,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
42
42
  this.bufferedErrorsUnderSpa = {};
43
43
  this.currentBody = undefined;
44
44
  this.errorOnPage = false;
45
- this.replayAborted = false;
46
45
 
47
46
  // this will need to change to match whatever ee we use in the instrument
48
47
  this.ee.on('interactionDone', (interaction, wasSaved) => this.onInteractionDone(interaction, wasSaved));
49
- this.ee.on('REPLAY_ABORTED', () => {
50
- this.replayAborted = true;
51
- });
52
48
  (0, _registerHandler.registerHandler)('err', function () {
53
49
  return _this.storeError(...arguments);
54
50
  }, this.featureName, this.ee);
@@ -94,11 +90,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
94
90
  payload.qs.ri = releaseIds;
95
91
  }
96
92
  if (body && body.err && body.err.length) {
97
- if (this.replayAborted) {
98
- body.err.forEach(e => {
99
- delete e.params?.hasReplay;
100
- });
101
- }
93
+ this.#runCrossFeatureChecks(body.err);
102
94
  if (!this.errorOnPage) {
103
95
  payload.qs.pve = '1';
104
96
  this.errorOnPage = true;
@@ -172,14 +164,14 @@ class Aggregate extends _aggregateBase.AggregateBase {
172
164
  // Notice if filterOutput isn't false|undefined OR our specified object, this func would've returned already (so it's unnecessary to req-check group).
173
165
  // Do not modify the name ('errorGroup') of params without DEM approval!
174
166
  if (filterOutput?.group) params.errorGroup = filterOutput.group;
175
-
167
+ if (hasReplay) params.hasReplay = hasReplay;
176
168
  /**
177
169
  * The bucketHash is different from the params.stackHash because the params.stackHash is based on the canonicalized
178
170
  * stack trace and is used downstream in NR1 to attempt to group the same errors across different browsers. However,
179
171
  * the canonical stack trace excludes items like the column number increasing the hit-rate of different errors potentially
180
172
  * bucketing and ultimately resulting in the loss of data in NR1.
181
173
  */
182
- var bucketHash = (0, _stringHashCode.stringHashCode)("".concat(stackInfo.name, "_").concat(stackInfo.message, "_").concat(stackInfo.stackString));
174
+ var bucketHash = (0, _stringHashCode.stringHashCode)("".concat(stackInfo.name, "_").concat(stackInfo.message, "_").concat(stackInfo.stackString, "_").concat(params.hasReplay ? 1 : 0));
183
175
  if (!this.stackReported[bucketHash]) {
184
176
  this.stackReported[bucketHash] = true;
185
177
  params.stack_trace = (0, _formatStackTrace.truncateSize)(stackInfo.stackString);
@@ -198,7 +190,6 @@ class Aggregate extends _aggregateBase.AggregateBase {
198
190
  params.pageview = 1;
199
191
  this.pageviewReported[bucketHash] = true;
200
192
  }
201
- if (hasReplay && !this.replayAborted) params.hasReplay = hasReplay;
202
193
  params.firstOccurrenceTimestamp = this.observedAt[bucketHash];
203
194
  params.timestamp = this.observedAt[bucketHash];
204
195
  var type = internal ? 'ierr' : 'err';
@@ -293,5 +284,27 @@ class Aggregate extends _aggregateBase.AggregateBase {
293
284
  );
294
285
  delete this.bufferedErrorsUnderSpa[interactionId]; // wipe the list of jserrors so they aren't duplicated by another call to the same id
295
286
  }
287
+
288
+ /**
289
+ * Dispatches a cross-feature communication event to allow other
290
+ * features to provide flags and data that can be used to mutation
291
+ * to the payload and to allow features to know about a feature
292
+ * harvest happening.
293
+ * @param {any[]} errors Array of errors from the payload body
294
+ */
295
+ #runCrossFeatureChecks(errors) {
296
+ const errorHashes = errors.map(error => error.params.stackHash);
297
+ const crossFeatureData = {
298
+ errorHashes
299
+ };
300
+ this.ee.emit("cfc.".concat(this.featureName), [crossFeatureData]);
301
+ let hasReplayFlag = errors.find(err => err.params.hasReplay);
302
+ if (hasReplayFlag && !crossFeatureData.hasReplay) {
303
+ // Some errors have `hasReplay` and a replay is not being recorded
304
+ errors.forEach(error => {
305
+ delete error.params.hasReplay;
306
+ });
307
+ }
308
+ }
296
309
  }
297
310
  exports.Aggregate = Aggregate;
@@ -57,9 +57,11 @@ class Aggregate extends _aggregateBase.AggregateBase {
57
57
  /** set at BCS response, stored in runtime */
58
58
  this.timeKeeper = undefined;
59
59
  this.recorder = args?.recorder;
60
- this.preloaded = !!this.recorder;
61
60
  this.errorNoticed = args?.errorNoticed || false;
62
61
  (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/Enabled'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
62
+ this.ee.on("cfc.".concat(_features.FEATURE_NAMES.jserrors), crossFeatureData => {
63
+ crossFeatureData.hasReplay = !!(this.scheduler?.started && this.recorder && this.mode === _constants3.MODE.FULL && !this.blocked && this.entitled);
64
+ });
63
65
 
64
66
  // 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.
65
67
  this.ee.on(_constants3.SESSION_EVENTS.RESET, () => {
@@ -106,6 +108,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
106
108
  (0, _registerHandler.registerHandler)(_constants.SR_EVENT_EMITTER_TYPES.PAUSE, () => {
107
109
  this.forceStop(this.mode !== _constants3.MODE.ERROR);
108
110
  }, this.featureName, this.ee);
111
+ (0, _registerHandler.registerHandler)(_constants.SR_EVENT_EMITTER_TYPES.ERROR_DURING_REPLAY, e => {
112
+ this.handleError(e);
113
+ }, this.featureName, this.ee);
109
114
  const {
110
115
  error_sampling_rate,
111
116
  sampling_rate,
@@ -154,15 +159,17 @@ class Aggregate extends _aggregateBase.AggregateBase {
154
159
  }
155
160
  }
156
161
  switchToFull() {
162
+ if (!this.entitled || this.blocked) return;
157
163
  this.mode = _constants3.MODE.FULL;
158
164
  // if the error was noticed AFTER the recorder was already imported....
159
165
  if (this.recorder && this.initialized) {
160
- this.recorder.stopRecording();
161
- this.recorder.startRecording();
166
+ if (!this.recorder.recording) this.recorder.startRecording();
162
167
  this.scheduler.startTimer(this.harvestTimeSeconds);
163
168
  this.syncWithSessionManager({
164
169
  sessionReplayMode: this.mode
165
170
  });
171
+ } else {
172
+ this.initializeRecording(false, true, true);
166
173
  }
167
174
  }
168
175
 
@@ -223,7 +230,6 @@ class Aggregate extends _aggregateBase.AggregateBase {
223
230
 
224
231
  // If an error was noticed before the mode could be set (like in the early lifecycle of the page), immediately set to FULL mode
225
232
  if (this.mode === _constants3.MODE.ERROR && this.errorNoticed) this.mode = _constants3.MODE.FULL;
226
- if (!this.preloaded) this.ee.on('err', e => this.handleError(e));
227
233
 
228
234
  // FULL mode records AND reports from the beginning, while ERROR mode only records (but does not report).
229
235
  // ERROR mode will do this until an error is thrown, and then switch into FULL mode.
@@ -317,7 +323,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
317
323
  return [payload];
318
324
  }
319
325
  getCorrectedTimestamp(node) {
320
- if (!node.timestamp) return;
326
+ if (!node?.timestamp) return;
321
327
  if (node.__newrelic) return node.timestamp;
322
328
  return this.timeKeeper.correctAbsoluteTimestamp(node.timestamp);
323
329
  }
@@ -10,7 +10,8 @@ const FEATURE_NAME = exports.FEATURE_NAME = _features.FEATURE_NAMES.sessionRepla
10
10
  const SR_EVENT_EMITTER_TYPES = exports.SR_EVENT_EMITTER_TYPES = {
11
11
  RECORD: 'recordReplay',
12
12
  PAUSE: 'pauseReplay',
13
- REPLAY_RUNNING: 'replayRunning'
13
+ REPLAY_RUNNING: 'replayRunning',
14
+ ERROR_DURING_REPLAY: 'errorDuringReplay'
14
15
  };
15
16
  const AVG_COMPRESSION = exports.AVG_COMPRESSION = 0.12;
16
17
  const RRWEB_EVENT_TYPES = exports.RRWEB_EVENT_TYPES = {
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.Instrument = void 0;
7
+ var _handle = require("../../../common/event-emitter/handle");
7
8
  var _constants = require("../../../common/session/constants");
8
9
  var _instrumentBase = require("../../utils/instrument-base");
9
10
  var _constants2 = require("../constants");
@@ -25,19 +26,28 @@ class Instrument extends _instrumentBase.InstrumentBase {
25
26
  let auto = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
26
27
  super(agentIdentifier, aggregator, _constants2.FEATURE_NAME, auto);
27
28
  let session;
29
+ this.replayRunning = false;
28
30
  try {
29
31
  session = JSON.parse(localStorage.getItem("".concat(_constants.PREFIX, "_").concat(_constants.DEFAULT_KEY)));
30
32
  } catch (err) {}
31
33
  if (this.#canPreloadRecorder(session)) {
32
- /** If this is preloaded, set up a buffer, if not, later when sampling we will set up a .on for live events */
33
- this.ee.on('err', e => {
34
- this.errorNoticed = true;
35
- if (this.featAggregate) this.featAggregate.handleError();
36
- });
37
34
  this.#startRecording(session?.sessionReplayMode);
38
35
  } else {
39
36
  this.importAggregator();
40
37
  }
38
+
39
+ /** If the recorder is running, we can pass error events on to the agg to help it switch to full mode later */
40
+ this.ee.on('err', e => {
41
+ if (this.replayRunning) {
42
+ this.errorNoticed = true;
43
+ (0, _handle.handle)(_constants2.SR_EVENT_EMITTER_TYPES.ERROR_DURING_REPLAY, [e], undefined, this.featureName, this.ee);
44
+ }
45
+ });
46
+
47
+ /** Emitted by the recorder when it starts capturing data, used to determine if we should pass errors on to the agg */
48
+ this.ee.on(_constants2.SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, isRunning => {
49
+ this.replayRunning = isRunning;
50
+ });
41
51
  }
42
52
 
43
53
  // At this point wherein session state exists already but we haven't init SessionEntity aka verify timers.
@@ -103,11 +103,10 @@ class Recorder {
103
103
  collectFonts: collect_fonts,
104
104
  checkoutEveryNms: _constants.CHECKOUT_MS[this.parent.mode]
105
105
  });
106
- this.parent.ee.emit(_constants.SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, [true, this.parent.mode]);
107
106
  this.stopRecording = () => {
108
107
  this.recording = false;
109
108
  this.parent.ee.emit(_constants.SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, [false, this.parent.mode]);
110
- stop();
109
+ stop?.();
111
110
  };
112
111
  }
113
112
 
@@ -150,6 +149,10 @@ class Recorder {
150
149
  if (!event) return;
151
150
  if (!this.parent.scheduler && this.#preloaded.length) this.currentBufferTarget = this.#preloaded[this.#preloaded.length - 1];else this.currentBufferTarget = this.#events;
152
151
  if (this.parent.blocked) return;
152
+ if (!this.notified) {
153
+ this.parent.ee.emit(_constants.SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, [true, this.parent.mode]);
154
+ this.notified = true;
155
+ }
153
156
  if (this.parent.timeKeeper?.ready && !event.__newrelic) {
154
157
  event.__newrelic = (0, _utils.buildNRMetaNode)(event.timestamp, this.parent.timeKeeper);
155
158
  event.timestamp = this.parent.timeKeeper.correctAbsoluteTimestamp(event.timestamp);
@@ -180,7 +183,7 @@ class Recorder {
180
183
 
181
184
  // We are making an effort to try to keep payloads manageable for unloading. If they reach the unload limit before their interval,
182
185
  // it will send immediately. This often happens on the first snapshot, which can be significantly larger than the other payloads.
183
- if (payloadSize > _constants.IDEAL_PAYLOAD_SIZE && this.parent.mode !== _constants2.MODE.ERROR) {
186
+ if ((event.type === _constants.RRWEB_EVENT_TYPES.FullSnapshot && this.currentBufferTarget.hasMeta || payloadSize > _constants.IDEAL_PAYLOAD_SIZE) && this.parent.mode === _constants2.MODE.FULL) {
184
187
  // if we've made it to the ideal size of ~64kb before the interval timer, we should send early.
185
188
  if (this.parent.scheduler) {
186
189
  this.parent.scheduler.runHarvest();
@@ -5,16 +5,17 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.buildNRMetaNode = buildNRMetaNode;
7
7
  exports.canImportReplayAgg = canImportReplayAgg;
8
- exports.enableSessionTracking = void 0;
8
+ exports.enableSessionTracking = enableSessionTracking;
9
9
  exports.isPreloadAllowed = isPreloadAllowed;
10
10
  var _config = require("../../../common/config/config");
11
11
  var _runtime = require("../../../common/constants/runtime");
12
- const enableSessionTracking = agentId => _runtime.isBrowserScope && (0, _config.getConfigurationValue)(agentId, 'privacy.cookies_enabled') === true;
13
- exports.enableSessionTracking = enableSessionTracking;
12
+ function enableSessionTracking(agentId) {
13
+ return _runtime.isBrowserScope && (0, _config.getConfigurationValue)(agentId, 'privacy.cookies_enabled') === true;
14
+ }
14
15
  function hasReplayPrerequisite(agentId) {
15
- return _config.originals.MO &&
16
+ return !!_config.originals.MO &&
16
17
  // Session Replay cannot work without Mutation Observer
17
- enableSessionTracking &&
18
+ enableSessionTracking(agentId) &&
18
19
  // requires session tracking to be running (hence "session" replay...)
19
20
  (0, _config.getConfigurationValue)(agentId, 'session_trace.enabled') === true; // Session Replay as of now is tightly coupled with Session Trace in the UI
20
21
  }
@@ -57,11 +57,10 @@ class Aggregate extends _aggregateBase.AggregateBase {
57
57
  // Very unlikely, but in case the existing XMLHttpRequest.prototype object on the page couldn't be wrapped.
58
58
  if (!this.agentRuntime.xhrWrappable) return;
59
59
  this.resourceObserver = argsObj?.resourceObserver; // undefined if observer couldn't be created
60
- this.ptid = this.agentRuntime.ptid;
60
+ this.ptid = '';
61
61
  this.trace = {};
62
62
  this.nodeCount = 0;
63
63
  this.sentTrace = null;
64
- this.everSent = false;
65
64
  this.harvestTimeSeconds = (0, _config.getConfigurationValue)(agentIdentifier, 'session_trace.harvestTimeSeconds') || 10;
66
65
  this.maxNodesPerHarvest = (0, _config.getConfigurationValue)(agentIdentifier, 'session_trace.maxNodesPerHarvest') || 1000;
67
66
  /**
@@ -113,7 +112,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
113
112
  this.isStandalone = false;
114
113
  if (prevMode === _constants2.MODE.ERROR && this.#scheduler) {
115
114
  this.trimSTNs(ERROR_MODE_SECONDS_WINDOW); // up until now, Trace would've been just buffering nodes up to max, which needs to be trimmed to last X seconds
116
- this.#scheduler.runHarvest({});
115
+ this.#scheduler.runHarvest({
116
+ needResponse: true
117
+ });
117
118
  } else {
118
119
  controlTraceOp(_constants2.MODE.FULL);
119
120
  }
@@ -136,7 +137,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
136
137
  sessionTraceMode: _constants2.MODE.OFF
137
138
  });
138
139
  operationalGate.permanentlyDecide(false);
139
- if (mostRecentModeKnown === _constants2.MODE.FULL) this.#scheduler?.runHarvest({}); // allow queued nodes (past opGate) to final harvest, unless they were buffered in other modes
140
+ if (mostRecentModeKnown === _constants2.MODE.FULL) this.#scheduler?.runHarvest(); // allow queued nodes (past opGate) to final harvest, unless they were buffered in other modes
140
141
  this.#scheduler?.stopTimer(true); // the 'true' arg here will forcibly block any future call to runHarvest, so the last runHarvest above must be prior
141
142
  this.#scheduler = null;
142
143
  };
@@ -155,7 +156,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
155
156
  - if trace switches to Full mode, harvest should start (prev: Error) if not already running (prev: Full). */
156
157
  this.ee.on(_constants2.SESSION_EVENTS.RESUME, () => {
157
158
  const updatedTraceMode = sessionEntity.state.sessionTraceMode;
158
- if (updatedTraceMode === _constants2.MODE.OFF) stopTracePerm();else if (updatedTraceMode === _constants2.MODE.FULL && this.#scheduler && !this.#scheduler.started) this.#scheduler.runHarvest({});
159
+ if (updatedTraceMode === _constants2.MODE.OFF) stopTracePerm();else if (updatedTraceMode === _constants2.MODE.FULL && this.#scheduler && !this.#scheduler.started) this.#scheduler.runHarvest({
160
+ needResponse: true
161
+ });
159
162
  mostRecentModeKnown = updatedTraceMode;
160
163
  });
161
164
  this.ee.on(_constants2.SESSION_EVENTS.PAUSE, () => {
@@ -246,12 +249,15 @@ class Aggregate extends _aggregateBase.AggregateBase {
246
249
  retryDelay: this.harvestTimeSeconds
247
250
  }, this);
248
251
  this.#scheduler.harvest.on('resources', this.#prepareHarvest.bind(this));
249
- if (dontStartHarvestYet === false) this.#scheduler.runHarvest({}); // sends first stn harvest immediately
252
+ if (dontStartHarvestYet === false) this.#scheduler.runHarvest({
253
+ needResponse: true
254
+ }); // sends first stn harvest immediately
250
255
  startupBuffer.decide(true); // signal to ALLOW & process data in EE's buffer into internal nodes queued for next harvest
251
256
  }
252
257
  #onHarvestFinished(result) {
253
- if (result.sent && !result.failed && !this.#scheduler.started) {
254
- // continue interval harvest only after first call
258
+ if (result.sent && result.responseText && !this.ptid) {
259
+ // continue interval harvest only if ptid was returned by server on the first
260
+ this.agentRuntime.ptid = this.ptid = result.responseText;
255
261
  this.#scheduler.startTimer(this.harvestTimeSeconds);
256
262
  }
257
263
  if (result.sent && result.retry && this.sentTrace) {
@@ -267,18 +273,15 @@ class Aggregate extends _aggregateBase.AggregateBase {
267
273
  }
268
274
  #prepareHarvest(options) {
269
275
  if (this.isStandalone) {
270
- if (this.#scheduler.started) {
271
- if ((0, _now.now)() >= MAX_TRACE_DURATION) {
272
- // Perform a final harvest once we hit or exceed the max session trace time
273
- options.isFinalHarvest = true;
274
- this.operationalGate.permanentlyDecide(false);
275
- this.#scheduler.stopTimer(true);
276
- } else if (this.nodeCount <= REQ_THRESHOLD_TO_SEND && !options.isFinalHarvest) {
277
- // Only harvest when more than some threshold of nodes are pending, after the very first harvest, with the exception of the last outgoing harvest.
278
- return;
279
- }
276
+ if (this.ptid && (0, _now.now)() >= MAX_TRACE_DURATION) {
277
+ // Perform a final harvest once we hit or exceed the max session trace time
278
+ options.isFinalHarvest = true;
279
+ this.operationalGate.permanentlyDecide(false);
280
+ this.#scheduler.stopTimer(true);
281
+ } else if (this.ptid && this.nodeCount <= REQ_THRESHOLD_TO_SEND && !options.isFinalHarvest) {
282
+ // Only harvest when more than some threshold of nodes are pending, after the very first harvest, with the exception of the last outgoing harvest.
283
+ return;
280
284
  }
281
- // else, we must be on the very first harvest (standalone mode), so go to next square
282
285
  } else {
283
286
  // -- *cli May '26 - Update: Not rate limiting backgrounded pages either for now.
284
287
  // if (this.ptid && document.visibilityState === 'hidden' && this.nodeCount <= REQ_THRESHOLD_TO_SEND) return
@@ -88,7 +88,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
88
88
  if (!firstIxnStartTime) firstIxnStartTime = Math.floor(interaction.start);
89
89
  }
90
90
  const payload = "bel.7;".concat(serializedIxnList.join(';'));
91
- if (options.retry) this.interactionsAwaitingRetry.push(...this.interactionsToHarvest);
91
+ if (options.retry) this.interactionsAwaitingRetry = this.interactionsToHarvest;
92
92
  this.interactionsToHarvest = [];
93
93
  return {
94
94
  body: {
@@ -99,8 +99,8 @@ class Aggregate extends _aggregateBase.AggregateBase {
99
99
  onHarvestFinished(result) {
100
100
  if (result.sent && result.retry && this.interactionsAwaitingRetry.length > 0) {
101
101
  this.interactionsToHarvest = [...this.interactionsAwaitingRetry, ...this.interactionsToHarvest];
102
- this.interactionsAwaitingRetry = [];
103
102
  }
103
+ this.interactionsAwaitingRetry = [];
104
104
  }
105
105
  startUIInteraction(eventName, startedAt, sourceElem) {
106
106
  // this is throttled by instrumentation so that it isn't excessively called
@@ -12,6 +12,7 @@ var _console = require("../../common/util/console");
12
12
  var _features = require("../../loaders/features/features");
13
13
  var _config = require("../../common/config/config");
14
14
  var _utils = require("../session_replay/shared/utils");
15
+ var _invoke = require("../../common/util/invoke");
15
16
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
16
17
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } /**
17
18
  * @file Defines `InstrumentBase` to be used as the super of the Instrument classes implemented by each feature.
@@ -55,7 +56,15 @@ class InstrumentBase extends _featureBase.FeatureBase {
55
56
  /** used in conjunction with newrelic.start() to defer harvesting in features */
56
57
  if ((0, _config.getConfigurationValue)(this.agentIdentifier, "".concat(this.featureName, ".autoStart")) === false) this.auto = false;
57
58
  /** if the feature requires opt-in (!auto-start), it will get registered once the api has been called */
58
- if (this.auto) (0, _drain.registerDrain)(agentIdentifier, featureName);
59
+ if (this.auto) (0, _drain.registerDrain)(agentIdentifier, featureName);else {
60
+ this.ee.on("".concat(this.featureName, "-opt-in"), (0, _invoke.single)(() => {
61
+ // register the feature to drain only once the API has been called, it will drain when importAggregator finishes for all the features
62
+ // called by the api in that cycle
63
+ (0, _drain.registerDrain)(this.agentIdentifier, this.featureName);
64
+ this.auto = true;
65
+ this.importAggregator();
66
+ }));
67
+ }
59
68
  }
60
69
 
61
70
  /**
@@ -66,19 +75,7 @@ class InstrumentBase extends _featureBase.FeatureBase {
66
75
  */
67
76
  importAggregator() {
68
77
  let argsObjFromInstrument = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
69
- if (this.featAggregate) return;
70
- if (!this.auto) {
71
- // this feature requires an opt in...
72
- // wait for API to be called
73
- this.ee.on("".concat(this.featureName, "-opt-in"), () => {
74
- // register the feature to drain only once the API has been called, it will drain when importAggregator finishes for all the features
75
- // called by the api in that cycle
76
- (0, _drain.registerDrain)(this.agentIdentifier, this.featureName);
77
- this.auto = true;
78
- this.importAggregator();
79
- });
80
- return;
81
- }
78
+ if (this.featAggregate || !this.auto) return;
82
79
  let loadedSuccessfully;
83
80
  this.onAggregateImported = new Promise(resolve => {
84
81
  loadedSuccessfully = resolve;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.debugNR1 = debugNR1;
7
+ var _nreum = require("../../common/window/nreum");
8
+ const debugId = 1;
9
+ const newrelic = (0, _nreum.gosCDN)();
10
+ function debugNR1(agentIdentifier, location, event) {
11
+ let otherprops = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
12
+ let debugName = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 'SR';
13
+ const api = agentIdentifier ? newrelic.initializedAgents[agentIdentifier].api.addPageAction : newrelic.addPageAction;
14
+ let url;
15
+ try {
16
+ const locURL = new URL(window.location);
17
+ url = locURL.pathname;
18
+ } catch (err) {}
19
+ api(debugName, {
20
+ debugId,
21
+ url,
22
+ location,
23
+ event,
24
+ now: performance.now(),
25
+ ...otherprops
26
+ });
27
+ }
@@ -30,6 +30,10 @@ var _runtime = require("../common/constants/runtime");
30
30
  * sensitive to network load, this may result in smaller builds with slightly lower performance impact.
31
31
  */
32
32
  class Agent extends _agentBase.AgentBase {
33
+ /**
34
+ * @param {Object} options Options to initialize agent with
35
+ * @param {string} [agentIdentifier] Optional identifier of agent
36
+ */
33
37
  constructor(options, agentIdentifier) {
34
38
  super(agentIdentifier);
35
39
  if (!_runtime.globalScope) {
@@ -60,7 +60,6 @@ function configure(agent) {
60
60
  agent.runSoftNavOverSpa &&= updatedInit.soft_navigations.enabled === true && updatedInit.feature_flags.includes('soft_nav');
61
61
  }
62
62
  runtime.denyList = [...(updatedInit.ajax.deny_list || []), ...(updatedInit.ajax.block_internal ? internalTrafficList : [])];
63
- runtime.ptid = agent.agentIdentifier;
64
63
  (0, _config.setRuntime)(agent.agentIdentifier, runtime);
65
64
  if (agent.api === undefined) agent.api = (0, _api.setAPI)(agent.agentIdentifier, forceDrain, agent.runSoftNavOverSpa);
66
65
  if (agent.exposed === undefined) agent.exposed = exposed;
@@ -6,7 +6,7 @@
6
6
  /**
7
7
  * Exposes the version of the agent
8
8
  */
9
- export const VERSION = "1.256.0";
9
+ export const VERSION = "1.257.0";
10
10
 
11
11
  /**
12
12
  * Exposes the build type of the agent
@@ -6,7 +6,7 @@
6
6
  /**
7
7
  * Exposes the version of the agent
8
8
  */
9
- export const VERSION = "1.256.0";
9
+ export const VERSION = "1.257.0";
10
10
 
11
11
  /**
12
12
  * Exposes the build type of the agent
@@ -129,7 +129,7 @@ export class Harvest extends SharedContext {
129
129
  let url = "".concat(protocol, "://").concat(perceviedBeacon).concat(endpointURLPart, "/1/").concat(info.licenseKey);
130
130
  if (customUrl) url = customUrl;
131
131
  if (raw) url = "".concat(protocol, "://").concat(perceviedBeacon, "/").concat(endpoint);
132
- const baseParams = !raw && includeBaseParams ? this.baseQueryString(qs) : '';
132
+ const baseParams = !raw && includeBaseParams ? this.baseQueryString(qs, endpoint) : '';
133
133
  let payloadParams = encodeObj(qs, agentRuntime.maxBytes);
134
134
  if (!submitMethod) {
135
135
  submitMethod = submitData.getSubmitMethod({
@@ -178,7 +178,6 @@ export class Harvest extends SharedContext {
178
178
  const cbResult = {
179
179
  sent: this.status !== 0,
180
180
  status: this.status,
181
- failed: this.status === 0 || this.status >= 400,
182
181
  xhr: this,
183
182
  fullUrl
184
183
  };
@@ -200,17 +199,19 @@ export class Harvest extends SharedContext {
200
199
  }
201
200
 
202
201
  // The stuff that gets sent every time.
203
- baseQueryString(qs) {
202
+ baseQueryString(qs, endpoint) {
204
203
  const runtime = getRuntime(this.sharedContext.agentIdentifier);
205
204
  const info = getInfo(this.sharedContext.agentIdentifier);
206
205
  const location = cleanURL(getLocation());
207
206
  const ref = this.obfuscator.shouldObfuscate() ? this.obfuscator.obfuscateString(location) : location;
208
- return ['a=' + info.applicationID, encodeParam('sa', info.sa ? '' + info.sa : ''), encodeParam('v', VERSION), transactionNameParam(info), encodeParam('ct', runtime.customTransaction), '&rst=' + now(), '&ck=0',
207
+ const hr = runtime?.session?.state.sessionReplayMode === 1 && endpoint !== 'jserrors';
208
+ const qps = ['a=' + info.applicationID, encodeParam('sa', info.sa ? '' + info.sa : ''), encodeParam('v', VERSION), transactionNameParam(info), encodeParam('ct', runtime.customTransaction), '&rst=' + now(), '&ck=0',
209
209
  // ck param DEPRECATED - still expected by backend
210
210
  '&s=' + (runtime.session?.state.value || '0'),
211
211
  // the 0 id encaps all untrackable and default traffic
212
- encodeParam('ref', ref), encodeParam('ptid', runtime.ptid ? '' + runtime.ptid : ''), encodeParam('hr', runtime?.session?.state.sessionReplayMode === 1 ? '1' : '0', qs) // hasReplay
213
- ].join('');
212
+ encodeParam('ref', ref), encodeParam('ptid', runtime.ptid ? '' + runtime.ptid : '')];
213
+ if (hr) qps.push(encodeParam('hr', '1', qs));
214
+ return qps.join('');
214
215
  }
215
216
 
216
217
  /**