@newrelic/browser-agent 1.290.0 → 1.291.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 (105) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/cjs/common/config/configurable.js +1 -1
  3. package/dist/cjs/common/config/runtime.js +4 -2
  4. package/dist/cjs/common/constants/agent-constants.js +3 -2
  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/harvest/harvester.js +0 -7
  8. package/dist/cjs/common/session/session-entity.js +5 -5
  9. package/dist/cjs/common/util/console.js +12 -0
  10. package/dist/cjs/features/generic_events/aggregate/index.js +16 -0
  11. package/dist/cjs/features/generic_events/instrument/index.js +2 -0
  12. package/dist/cjs/features/jserrors/instrument/index.js +3 -8
  13. package/dist/cjs/features/session_replay/aggregate/index.js +7 -4
  14. package/dist/cjs/features/session_replay/constants.js +0 -1
  15. package/dist/cjs/features/session_replay/instrument/index.js +4 -8
  16. package/dist/cjs/features/session_replay/shared/recorder.js +28 -33
  17. package/dist/cjs/features/session_trace/aggregate/index.js +6 -2
  18. package/dist/cjs/features/session_trace/aggregate/trace/storage.js +0 -7
  19. package/dist/cjs/features/utils/entity-manager.js +5 -6
  20. package/dist/cjs/features/utils/event-store-manager.js +8 -4
  21. package/dist/cjs/loaders/api/constants.js +3 -3
  22. package/dist/cjs/loaders/api/measure.js +60 -0
  23. package/dist/cjs/loaders/api/noticeError.js +3 -9
  24. package/dist/cjs/loaders/api/sharedHandlers.js +1 -7
  25. package/dist/cjs/loaders/api-base.js +11 -0
  26. package/dist/esm/common/config/configurable.js +1 -1
  27. package/dist/esm/common/config/runtime.js +4 -2
  28. package/dist/esm/common/constants/agent-constants.js +2 -1
  29. package/dist/esm/common/constants/env.cdn.js +1 -1
  30. package/dist/esm/common/constants/env.npm.js +1 -1
  31. package/dist/esm/common/harvest/harvester.js +0 -7
  32. package/dist/esm/common/session/session-entity.js +5 -5
  33. package/dist/esm/common/util/console.js +13 -0
  34. package/dist/esm/features/generic_events/aggregate/index.js +16 -0
  35. package/dist/esm/features/generic_events/instrument/index.js +2 -0
  36. package/dist/esm/features/jserrors/instrument/index.js +3 -8
  37. package/dist/esm/features/session_replay/aggregate/index.js +7 -4
  38. package/dist/esm/features/session_replay/constants.js +0 -1
  39. package/dist/esm/features/session_replay/instrument/index.js +4 -8
  40. package/dist/esm/features/session_replay/shared/recorder.js +29 -34
  41. package/dist/esm/features/session_trace/aggregate/index.js +6 -2
  42. package/dist/esm/features/session_trace/aggregate/trace/storage.js +0 -7
  43. package/dist/esm/features/utils/entity-manager.js +5 -6
  44. package/dist/esm/features/utils/event-store-manager.js +5 -1
  45. package/dist/esm/loaders/api/constants.js +2 -2
  46. package/dist/esm/loaders/api/measure.js +53 -0
  47. package/dist/esm/loaders/api/noticeError.js +2 -8
  48. package/dist/esm/loaders/api/sharedHandlers.js +1 -7
  49. package/dist/esm/loaders/api-base.js +12 -1
  50. package/dist/tsconfig.tsbuildinfo +1 -1
  51. package/dist/types/common/config/runtime.d.ts.map +1 -1
  52. package/dist/types/common/constants/agent-constants.d.ts +1 -0
  53. package/dist/types/common/constants/agent-constants.d.ts.map +1 -1
  54. package/dist/types/common/harvest/harvester.d.ts.map +1 -1
  55. package/dist/types/common/session/session-entity.d.ts +0 -1
  56. package/dist/types/common/session/session-entity.d.ts.map +1 -1
  57. package/dist/types/common/util/console.d.ts +0 -4
  58. package/dist/types/common/util/console.d.ts.map +1 -1
  59. package/dist/types/features/generic_events/aggregate/index.d.ts.map +1 -1
  60. package/dist/types/features/generic_events/instrument/index.d.ts.map +1 -1
  61. package/dist/types/features/jserrors/instrument/index.d.ts.map +1 -1
  62. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  63. package/dist/types/features/session_replay/constants.d.ts +0 -1
  64. package/dist/types/features/session_replay/constants.d.ts.map +1 -1
  65. package/dist/types/features/session_replay/instrument/index.d.ts +0 -1
  66. package/dist/types/features/session_replay/instrument/index.d.ts.map +1 -1
  67. package/dist/types/features/session_replay/shared/recorder.d.ts +0 -3
  68. package/dist/types/features/session_replay/shared/recorder.d.ts.map +1 -1
  69. package/dist/types/features/session_trace/aggregate/index.d.ts +1 -1
  70. package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
  71. package/dist/types/features/session_trace/aggregate/trace/storage.d.ts +0 -1
  72. package/dist/types/features/session_trace/aggregate/trace/storage.d.ts.map +1 -1
  73. package/dist/types/features/utils/entity-manager.d.ts +1 -5
  74. package/dist/types/features/utils/entity-manager.d.ts.map +1 -1
  75. package/dist/types/features/utils/event-store-manager.d.ts.map +1 -1
  76. package/dist/types/loaders/api/constants.d.ts +1 -1
  77. package/dist/types/loaders/api/constants.d.ts.map +1 -1
  78. package/dist/types/loaders/api/measure.d.ts +2 -0
  79. package/dist/types/loaders/api/measure.d.ts.map +1 -0
  80. package/dist/types/loaders/api/noticeError.d.ts.map +1 -1
  81. package/dist/types/loaders/api-base.d.ts +13 -0
  82. package/dist/types/loaders/api-base.d.ts.map +1 -1
  83. package/package.json +1 -1
  84. package/src/common/config/configurable.js +1 -1
  85. package/src/common/config/runtime.js +3 -2
  86. package/src/common/constants/agent-constants.js +1 -0
  87. package/src/common/harvest/harvester.js +0 -5
  88. package/src/common/session/session-entity.js +5 -6
  89. package/src/common/util/console.js +13 -0
  90. package/src/features/generic_events/aggregate/index.js +15 -0
  91. package/src/features/generic_events/instrument/index.js +2 -0
  92. package/src/features/jserrors/instrument/index.js +3 -9
  93. package/src/features/session_replay/aggregate/index.js +8 -4
  94. package/src/features/session_replay/constants.js +0 -1
  95. package/src/features/session_replay/instrument/index.js +4 -8
  96. package/src/features/session_replay/shared/recorder.js +27 -35
  97. package/src/features/session_trace/aggregate/index.js +6 -2
  98. package/src/features/session_trace/aggregate/trace/storage.js +0 -8
  99. package/src/features/utils/entity-manager.js +5 -5
  100. package/src/features/utils/event-store-manager.js +5 -2
  101. package/src/loaders/api/constants.js +1 -2
  102. package/src/loaders/api/measure.js +53 -0
  103. package/src/loaders/api/noticeError.js +2 -10
  104. package/src/loaders/api/sharedHandlers.js +1 -1
  105. package/src/loaders/api-base.js +12 -1
package/CHANGELOG.md CHANGED
@@ -3,6 +3,26 @@
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.291.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.290.1...v1.291.0) (2025-05-30)
7
+
8
+
9
+ ### Features
10
+
11
+ * Create the measure API ([#1476](https://github.com/newrelic/newrelic-browser-agent/issues/1476)) ([f944b76](https://github.com/newrelic/newrelic-browser-agent/commit/f944b76c2137e6d75e47e692ded4ba5f04bb4b6d))
12
+
13
+
14
+ ### Bug Fixes
15
+
16
+ * Fix race between end of session and features aborting ([#1487](https://github.com/newrelic/newrelic-browser-agent/issues/1487)) ([531f8d4](https://github.com/newrelic/newrelic-browser-agent/commit/531f8d4228f6c7ead8e2342c8a8dd25c651e9ec6))
17
+ * Harvest first session trace payload immediately ([#1483](https://github.com/newrelic/newrelic-browser-agent/issues/1483)) ([50f4ace](https://github.com/newrelic/newrelic-browser-agent/commit/50f4acea3e27c6b5e65d02ffb46af02b351e4500))
18
+
19
+ ## [1.290.1](https://github.com/newrelic/newrelic-browser-agent/compare/v1.290.0...v1.290.1) (2025-05-21)
20
+
21
+
22
+ ### Bug Fixes
23
+
24
+ * Silence setting getter only harvestCount message ([#1480](https://github.com/newrelic/newrelic-browser-agent/issues/1480)) ([98b1ab6](https://github.com/newrelic/newrelic-browser-agent/commit/98b1ab69c8a03a41f0cdccf27e410728f389bebb))
25
+
6
26
  ## [1.290.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.289.0...v1.290.0) (2025-05-02)
7
27
 
8
28
 
@@ -26,7 +26,7 @@ function getModeledObject(obj, model) {
26
26
  }
27
27
  if (Array.isArray(obj[key]) && Array.isArray(model[key])) output[key] = Array.from(new Set([...obj[key], ...model[key]]));else if (typeof obj[key] === 'object' && typeof model[key] === 'object') output[key] = getModeledObject(obj[key], model[key]);else output[key] = obj[key];
28
28
  } catch (e) {
29
- (0, _console.warn)(1, e);
29
+ if (!output[key]) (0, _console.warn)(1, e);
30
30
  }
31
31
  }
32
32
  return output;
@@ -16,7 +16,7 @@ var _env = require("../constants/env.npm");
16
16
  * Module level count of harvests. This property will auto-increment each time it is accessed.
17
17
  * @type {number}
18
18
  */
19
- let harvestCount = 0;
19
+ let _harvestCount = 0;
20
20
  const ReadOnly = {
21
21
  buildEnv: _env.BUILD_ENV,
22
22
  distMethod: _env.DIST_METHOD,
@@ -32,6 +32,8 @@ const RuntimeModel = {
32
32
  entityManager: undefined,
33
33
  harvester: undefined,
34
34
  isolatedBacklog: false,
35
+ isRecording: false,
36
+ // true when actively recording, false when paused or stopped
35
37
  loaderType: undefined,
36
38
  maxBytes: 30000,
37
39
  obfuscator: undefined,
@@ -41,7 +43,7 @@ const RuntimeModel = {
41
43
  session: undefined,
42
44
  timeKeeper: undefined,
43
45
  get harvestCount() {
44
- return ++harvestCount;
46
+ return ++_harvestCount;
45
47
  }
46
48
  };
47
49
  const mergeRuntime = runtime => {
@@ -3,10 +3,11 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.MAX_PAYLOAD_SIZE = exports.IDEAL_PAYLOAD_SIZE = void 0;
6
+ exports.MAX_PAYLOAD_SIZE = exports.IDEAL_PAYLOAD_SIZE = exports.DEFAULT_KEY = void 0;
7
7
  /**
8
8
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
9
9
  * SPDX-License-Identifier: Apache-2.0
10
10
  */
11
11
  const IDEAL_PAYLOAD_SIZE = exports.IDEAL_PAYLOAD_SIZE = 64000;
12
- const MAX_PAYLOAD_SIZE = exports.MAX_PAYLOAD_SIZE = 1000000;
12
+ const MAX_PAYLOAD_SIZE = exports.MAX_PAYLOAD_SIZE = 1000000;
13
+ const DEFAULT_KEY = exports.DEFAULT_KEY = 'NR_CONTAINER_AGENT';
@@ -17,7 +17,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_EN
17
17
  /**
18
18
  * Exposes the version of the agent
19
19
  */
20
- const VERSION = exports.VERSION = "1.290.0";
20
+ const VERSION = exports.VERSION = "1.291.0";
21
21
 
22
22
  /**
23
23
  * Exposes the build type of the agent
@@ -17,7 +17,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_EN
17
17
  /**
18
18
  * Exposes the version of the agent
19
19
  */
20
- const VERSION = exports.VERSION = "1.290.0";
20
+ const VERSION = exports.VERSION = "1.291.0";
21
21
 
22
22
  /**
23
23
  * Exposes the build type of the agent
@@ -10,7 +10,6 @@ var _env = require("../constants/env.npm");
10
10
  var _runtime = require("../constants/runtime");
11
11
  var _handle = require("../event-emitter/handle");
12
12
  var _eventListenerOpts = require("../event-listener/event-listener-opts");
13
- var _constants2 = require("../session/constants");
14
13
  var _now = require("../timing/now");
15
14
  var _eol = require("../unload/eol");
16
15
  var _cleanUrl = require("../url/clean-url");
@@ -43,12 +42,6 @@ class Harvester {
43
42
  }));
44
43
  /* This callback should run in bubble phase, so that that CWV api, like "onLCP", is called before the final harvest so that emitted timings are part of last outgoing. */
45
44
  }, false);
46
-
47
- /* Flush all buffered data if session resets and give up retries. This should be synchronous to ensure that the correct `session` value is sent.
48
- Since session-reset generates a new session ID and the ID is grabbed at send-time, any delays or retries would cause the payload to be sent under the wrong session ID. */
49
- agentRef.ee.on(_constants2.SESSION_EVENTS.RESET, () => this.initializedAggregates.forEach(aggregateInst => this.triggerHarvestFor(aggregateInst, {
50
- forceNoRetry: true
51
- })));
52
45
  }
53
46
  startTimer(harvestInterval = this.agentRef.init.harvest.interval) {
54
47
  if (this.#started) return;
@@ -240,7 +240,10 @@ class SessionEntity {
240
240
  // * stop recording (stn and sr)...
241
241
  // * delete the session and start over
242
242
  try {
243
- if (this.initialized) this.ee.emit(_constants.SESSION_EVENTS.RESET);
243
+ if (this.initialized) {
244
+ this.ee.emit(_constants.SESSION_EVENTS.RESET);
245
+ this.state.numOfResets++;
246
+ }
244
247
  this.storage.remove(this.lookupKey);
245
248
  this.inactiveTimer?.abort?.();
246
249
  this.expiresTimer?.clear?.();
@@ -251,7 +254,7 @@ class SessionEntity {
251
254
  storage: this.storage,
252
255
  expiresMs: this.expiresMs,
253
256
  inactiveMs: this.inactiveMs,
254
- numOfResets: ++this.state.numOfResets
257
+ numOfResets: this.state.numOfResets
255
258
  });
256
259
  return this.read();
257
260
  } catch (e) {
@@ -270,9 +273,6 @@ class SessionEntity {
270
273
  inactiveAt: this.getFutureTimestamp(this.inactiveMs)
271
274
  });
272
275
  }
273
- isAfterSessionExpiry(timestamp) {
274
- return this.state.numOfResets > 0 || typeof timestamp === 'number' && typeof this.state.expiresAt === 'number' && timestamp >= this.state.expiresAt;
275
- }
276
276
 
277
277
  /**
278
278
  * @param {number} timestamp
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.warn = warn;
7
+ var _globalEvent = require("../dispatch/global-event");
7
8
  /**
8
9
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
9
10
  * SPDX-License-Identifier: Apache-2.0
@@ -20,4 +21,15 @@ exports.warn = warn;
20
21
  function warn(code, secondary) {
21
22
  if (typeof console.debug !== 'function') return;
22
23
  console.debug("New Relic Warning: https://github.com/newrelic/newrelic-browser-agent/blob/main/docs/warning-codes.md#".concat(code), secondary);
24
+ (0, _globalEvent.dispatchGlobalEvent)({
25
+ agentIdentifier: null,
26
+ drained: null,
27
+ type: 'data',
28
+ name: 'warn',
29
+ feature: 'warn',
30
+ data: {
31
+ code,
32
+ secondary
33
+ }
34
+ });
23
35
  }
@@ -224,6 +224,22 @@ class Aggregate extends _aggregateBase.AggregateBase {
224
224
  }
225
225
  }, this.featureName, this.ee);
226
226
  }
227
+ (0, _registerHandler.registerHandler)('api-measure', (args, n) => {
228
+ const {
229
+ start,
230
+ duration,
231
+ customAttributes
232
+ } = args;
233
+ const event = {
234
+ ...customAttributes,
235
+ eventType: 'BrowserPerformance',
236
+ timestamp: Math.floor(agentRef.runtime.timeKeeper.correctRelativeTimestamp(start)),
237
+ entryName: n,
238
+ entryDuration: duration,
239
+ entryType: 'measure'
240
+ };
241
+ this.addEvent(event);
242
+ }, this.featureName, this.ee);
227
243
  agentRef.runtime.harvester.triggerHarvestFor(this);
228
244
  this.drain();
229
245
  });
@@ -12,6 +12,7 @@ var _addPageAction = require("../../../loaders/api/addPageAction");
12
12
  var _finished = require("../../../loaders/api/finished");
13
13
  var _recordCustomEvent = require("../../../loaders/api/recordCustomEvent");
14
14
  var _register = require("../../../loaders/api/register");
15
+ var _measure = require("../../../loaders/api/measure");
15
16
  var _instrumentBase = require("../../utils/instrument-base");
16
17
  var _constants = require("../constants");
17
18
  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); }
@@ -31,6 +32,7 @@ class Instrument extends _instrumentBase.InstrumentBase {
31
32
  (0, _recordCustomEvent.setupRecordCustomEventAPI)(agentRef);
32
33
  (0, _finished.setupFinishedAPI)(agentRef);
33
34
  (0, _register.setupRegisterAPI)(agentRef);
35
+ (0, _measure.setupMeasureAPI)(agentRef);
34
36
  if (_runtime.isBrowserScope) {
35
37
  if (agentRef.init.user_actions.enabled) {
36
38
  _constants.OBSERVED_EVENTS.forEach(eventType => (0, _eventListenerOpts.windowAddEventListener)(eventType, evt => (0, _handle.handle)('ua', [evt], undefined, this.featureName, this.ee), true));
@@ -10,7 +10,6 @@ var _constants = require("../constants");
10
10
  var _runtime = require("../../../common/constants/runtime");
11
11
  var _eventListenerOpts = require("../../../common/event-listener/event-listener-opts");
12
12
  var _now = require("../../../common/timing/now");
13
- var _constants2 = require("../../session_replay/constants");
14
13
  var _castError = require("../shared/cast-error");
15
14
  var _noticeError = require("../../../loaders/api/noticeError");
16
15
  var _setErrorHandler = require("../../../loaders/api/setErrorHandler");
@@ -23,7 +22,6 @@ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e;
23
22
  */
24
23
  class Instrument extends _instrumentBase.InstrumentBase {
25
24
  static featureName = _constants.FEATURE_NAME;
26
- #replayRunning = false;
27
25
  constructor(agentRef) {
28
26
  super(agentRef, _constants.FEATURE_NAME);
29
27
 
@@ -38,20 +36,17 @@ class Instrument extends _instrumentBase.InstrumentBase {
38
36
  } catch (e) {}
39
37
  this.ee.on('internal-error', (error, reason) => {
40
38
  if (!this.abortHandler) return;
41
- (0, _handle.handle)('ierr', [(0, _castError.castError)(error), (0, _now.now)(), true, {}, this.#replayRunning, reason], undefined, this.featureName, this.ee);
42
- });
43
- this.ee.on(_constants2.SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, isRunning => {
44
- this.#replayRunning = isRunning;
39
+ (0, _handle.handle)('ierr', [(0, _castError.castError)(error), (0, _now.now)(), true, {}, agentRef.runtime.isRecording, reason], undefined, this.featureName, this.ee);
45
40
  });
46
41
  _runtime.globalScope.addEventListener('unhandledrejection', promiseRejectionEvent => {
47
42
  if (!this.abortHandler) return;
48
43
  (0, _handle.handle)('err', [(0, _castError.castPromiseRejectionEvent)(promiseRejectionEvent), (0, _now.now)(), false, {
49
44
  unhandledPromiseRejection: 1
50
- }, this.#replayRunning], undefined, this.featureName, this.ee);
45
+ }, agentRef.runtime.isRecording], undefined, this.featureName, this.ee);
51
46
  }, (0, _eventListenerOpts.eventListenerOpts)(false, this.removeOnAbort?.signal));
52
47
  _runtime.globalScope.addEventListener('error', errorEvent => {
53
48
  if (!this.abortHandler) return;
54
- (0, _handle.handle)('err', [(0, _castError.castErrorEvent)(errorEvent), (0, _now.now)(), false, {}, this.#replayRunning], undefined, this.featureName, this.ee);
49
+ (0, _handle.handle)('err', [(0, _castError.castErrorEvent)(errorEvent), (0, _now.now)(), false, {}, agentRef.runtime.isRecording], undefined, this.featureName, this.ee);
55
50
  }, (0, _eventListenerOpts.eventListenerOpts)(false, this.removeOnAbort?.signal));
56
51
  this.abortHandler = this.#abort; // we also use this as a flag to denote that the feature is active or on and handling errors
57
52
  this.importAggregator(agentRef, () => Promise.resolve().then(() => _interopRequireWildcard(require(/* webpackChunkName: "jserrors-aggregate" */'../aggregate'))));
@@ -95,7 +95,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
95
95
  this.entitled = !!entitled;
96
96
  if (!this.entitled) {
97
97
  this.deregisterDrain();
98
- if (this.recorder?.recording) {
98
+ if (this.agentRef.runtime.isRecording) {
99
99
  this.abort(_constants.ABORT_REASONS.ENTITLEMENTS);
100
100
  this.reportSupportabilityMetric('SessionReplay/EnabledNotEntitled/Detected');
101
101
  }
@@ -136,7 +136,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
136
136
  this.mode = _constants2.MODE.FULL;
137
137
  // if the error was noticed AFTER the recorder was already imported....
138
138
  if (this.recorder && this.initialized) {
139
- if (!this.recorder.recording) this.recorder.startRecording();
139
+ if (!this.agentRef.runtime.isRecording) this.recorder.startRecording();
140
140
  this.syncWithSessionManager({
141
141
  sessionReplayMode: this.mode
142
142
  });
@@ -166,7 +166,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
166
166
  timeKeeper
167
167
  } = this.agentRef.runtime;
168
168
  this.timeKeeper = timeKeeper;
169
- if (this.recorder?.parent.trigger === _constants.TRIGGERS.API && this.recorder?.recording) {
169
+ if (this.recorder?.parent.trigger === _constants.TRIGGERS.API && this.agentRef.runtime.isRecording) {
170
170
  this.mode = _constants2.MODE.FULL;
171
171
  } else if (!session.isNew && !ignoreSession) {
172
172
  // inherit the mode of the existing session
@@ -204,7 +204,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
204
204
  this.prepUtils().then(() => this.agentRef.runtime.harvester.triggerHarvestFor(this));
205
205
  }
206
206
  await this.prepUtils();
207
- if (!this.recorder.recording) this.recorder.startRecording();
207
+ if (!this.agentRef.runtime.isRecording) this.recorder.startRecording();
208
208
  this.syncWithSessionManager({
209
209
  sessionReplayMode: this.mode
210
210
  });
@@ -281,6 +281,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
281
281
  this.recorder.clearBuffer();
282
282
  if (recorderEvents.type === 'preloaded') this.agentRef.runtime.harvester.triggerHarvestFor(this);
283
283
  payloadOutput.payload = payload;
284
+ if (!this.agentRef.runtime.session.state.traceHarvestStarted) {
285
+ (0, _console.warn)(59, JSON.stringify(this.agentRef.runtime.session.state));
286
+ }
284
287
  return [payloadOutput];
285
288
  }
286
289
  getCorrectedTimestamp(node) {
@@ -15,7 +15,6 @@ const FEATURE_NAME = exports.FEATURE_NAME = _features.FEATURE_NAMES.sessionRepla
15
15
  const SR_EVENT_EMITTER_TYPES = exports.SR_EVENT_EMITTER_TYPES = {
16
16
  RECORD: 'recordReplay',
17
17
  PAUSE: 'pauseReplay',
18
- REPLAY_RUNNING: 'replayRunning',
19
18
  ERROR_DURING_REPLAY: 'errorDuringReplay'
20
19
  };
21
20
  const AVG_COMPRESSION = exports.AVG_COMPRESSION = 0.12;
@@ -29,7 +29,6 @@ class Instrument extends _instrumentBase.InstrumentBase {
29
29
  (0, _recordReplay.setupRecordReplayAPI)(agentRef);
30
30
  (0, _pauseReplay.setupPauseReplayAPI)(agentRef);
31
31
  let session;
32
- this.replayRunning = false;
33
32
  this.#agentRef = agentRef;
34
33
  try {
35
34
  session = JSON.parse(localStorage.getItem("".concat(_constants.PREFIX, "_").concat(_constants.DEFAULT_KEY)));
@@ -46,16 +45,11 @@ class Instrument extends _instrumentBase.InstrumentBase {
46
45
 
47
46
  /** If the recorder is running, we can pass error events on to the agg to help it switch to full mode later */
48
47
  this.ee.on('err', e => {
49
- if (this.replayRunning) {
48
+ if (this.#agentRef.runtime.isRecording) {
50
49
  this.errorNoticed = true;
51
50
  (0, _handle.handle)(_constants2.SR_EVENT_EMITTER_TYPES.ERROR_DURING_REPLAY, [e], undefined, this.featureName, this.ee);
52
51
  }
53
52
  });
54
-
55
- /** Emitted by the recorder when it starts capturing data, used to determine if we should pass errors on to the agg */
56
- this.ee.on(_constants2.SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, isRunning => {
57
- this.replayRunning = isRunning;
58
- });
59
53
  }
60
54
 
61
55
  // At this point wherein session state exists already but we haven't init SessionEntity aka verify timers.
@@ -95,7 +89,9 @@ class Instrument extends _instrumentBase.InstrumentBase {
95
89
  });
96
90
  this.recorder.startRecording();
97
91
  this.abortHandler = this.recorder.stopRecording;
98
- } catch (e) {} // TODO add internal error handling
92
+ } catch (err) {
93
+ this.parent.ee.emit('internal-error', [err]);
94
+ }
99
95
  this.importAggregator(this.#agentRef, () => Promise.resolve().then(() => _interopRequireWildcard(require(/* webpackChunkName: "session_replay-aggregate" */'../aggregate'))), {
100
96
  recorder: this.recorder,
101
97
  errorNoticed: this.errorNoticed
@@ -43,8 +43,6 @@ class Recorder {
43
43
  this.#events = new _recorderEvents.RecorderEvents(this.shouldFix);
44
44
  this.#backloggedEvents = new _recorderEvents.RecorderEvents(this.shouldFix);
45
45
  this.#preloaded = [new _recorderEvents.RecorderEvents(this.shouldFix)];
46
- /** True when actively recording, false when paused or stopped */
47
- this.recording = false;
48
46
  /** The pointer to the current bucket holding rrweb events */
49
47
  this.currentBufferTarget = this.#events;
50
48
  /** Only set to true once a snapshot node has been processed. Used to block preload harvests from sending before we know we have a snapshot */
@@ -52,7 +50,9 @@ class Recorder {
52
50
  /** Hold on to the last meta node, so that it can be re-inserted if the meta and snapshot nodes are broken up due to harvesting */
53
51
  this.lastMeta = false;
54
52
  /** The method to stop recording. This defaults to a noop, but is overwritten once the recording library is imported and initialized */
55
- this.stopRecording = () => {/* no-op until set by rrweb initializer */};
53
+ this.stopRecording = () => {
54
+ this.parent.agentRef.runtime.isRecording = false;
55
+ };
56
56
  }
57
57
  getEvents() {
58
58
  if (this.#preloaded[0]?.events.length) {
@@ -83,7 +83,7 @@ class Recorder {
83
83
 
84
84
  /** Begin recording using configured recording lib */
85
85
  startRecording() {
86
- this.recording = true;
86
+ this.parent.agentRef.runtime.isRecording = true;
87
87
  const {
88
88
  block_class,
89
89
  ignore_class,
@@ -98,27 +98,30 @@ class Recorder {
98
98
 
99
99
  // set up rrweb configurations for maximum privacy --
100
100
  // https://newrelic.atlassian.net/wiki/spaces/O11Y/pages/2792293280/2023+02+28+Browser+-+Session+Replay#Configuration-options
101
- const stop = (0, _rrweb.record)({
102
- emit: this.audit.bind(this),
103
- blockClass: block_class,
104
- ignoreClass: ignore_class,
105
- maskTextClass: mask_text_class,
106
- blockSelector: block_selector,
107
- maskInputOptions: mask_input_options,
108
- maskTextSelector: mask_text_selector,
109
- maskTextFn: _utils.customMasker,
110
- maskAllInputs: mask_all_inputs,
111
- maskInputFn: _utils.customMasker,
112
- inlineStylesheet: true,
113
- inlineImages: inline_images,
114
- collectFonts: collect_fonts,
115
- checkoutEveryNms: _constants.CHECKOUT_MS[this.parent.mode],
116
- recordAfter: 'DOMContentLoaded'
117
- });
101
+ let stop;
102
+ try {
103
+ stop = (0, _rrweb.record)({
104
+ emit: this.audit.bind(this),
105
+ blockClass: block_class,
106
+ ignoreClass: ignore_class,
107
+ maskTextClass: mask_text_class,
108
+ blockSelector: block_selector,
109
+ maskInputOptions: mask_input_options,
110
+ maskTextSelector: mask_text_selector,
111
+ maskTextFn: _utils.customMasker,
112
+ maskAllInputs: mask_all_inputs,
113
+ maskInputFn: _utils.customMasker,
114
+ inlineStylesheet: true,
115
+ inlineImages: inline_images,
116
+ collectFonts: collect_fonts,
117
+ checkoutEveryNms: _constants.CHECKOUT_MS[this.parent.mode],
118
+ recordAfter: 'DOMContentLoaded'
119
+ });
120
+ } catch (err) {
121
+ this.parent.ee.emit('internal-error', [err]);
122
+ }
118
123
  this.stopRecording = () => {
119
- this.recording = false;
120
- this.notified = false;
121
- this.parent.ee.emit(_constants.SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, [false, this.parent.mode]);
124
+ this.parent.agentRef.runtime.isRecording = false;
122
125
  stop?.();
123
126
  };
124
127
  }
@@ -165,16 +168,8 @@ class Recorder {
165
168
  /** Store a payload in the buffer (this.#events). This should be the callback to the recording lib noticing a mutation */
166
169
  store(event, isCheckout) {
167
170
  if (!event) return;
168
- if (this.parent.agentRef.runtime?.session?.isAfterSessionExpiry(event.timestamp)) {
169
- (0, _handle.handle)(_constants3.SUPPORTABILITY_METRIC_CHANNEL, ['Session/Expired/SessionReplay/Seen'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
170
- return;
171
- }
172
171
  if (!(this.parent instanceof _aggregateBase.AggregateBase) && this.#preloaded.length) this.currentBufferTarget = this.#preloaded[this.#preloaded.length - 1];else this.currentBufferTarget = this.#events;
173
172
  if (this.parent.blocked) return;
174
- if (!this.notified) {
175
- this.parent.ee.emit(_constants.SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, [true, this.parent.mode]);
176
- this.notified = true;
177
- }
178
173
  if (this.parent.timeKeeper?.ready && !event.__newrelic) {
179
174
  event.__newrelic = (0, _utils.buildNRMetaNode)(event.timestamp, this.parent.timeKeeper);
180
175
  event.timestamp = this.parent.timeKeeper.correctAbsoluteTimestamp(event.timestamp);
@@ -219,7 +214,7 @@ class Recorder {
219
214
  /** force the recording lib to take a full DOM snapshot. This needs to occur in certain cases, like visibility changes */
220
215
  takeFullSnapshot() {
221
216
  try {
222
- if (!this.recording) return;
217
+ if (!this.parent.agentRef.runtime.isRecording) return;
223
218
  _rrweb.record.takeFullSnapshot();
224
219
  } catch (err) {
225
220
  // in the off chance we think we are recording, but rrweb does not, rrweb's lib will throw an error. This catch is just a precaution
@@ -13,6 +13,7 @@ var _runtime = require("../../../common/constants/runtime");
13
13
  var _constants2 = require("../../../common/session/constants");
14
14
  var _traverse = require("../../../common/util/traverse");
15
15
  var _cleanUrl = require("../../../common/url/clean-url");
16
+ var _console = require("../../../common/util/console");
16
17
  /**
17
18
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
18
19
  * SPDX-License-Identifier: Apache-2.0
@@ -98,9 +99,11 @@ class Aggregate extends _aggregateBase.AggregateBase {
98
99
  sessionTraceMode: this.mode
99
100
  });
100
101
  this.drain();
102
+ /** try to harvest immediately. This will not send if the trace is not running in FULL mode due to the pre-harvest checks. */
103
+ this.agentRef.runtime.harvester.triggerHarvestFor(this);
101
104
  }
102
105
  preHarvestChecks() {
103
- if (this.mode !== _constants2.MODE.FULL) return; // only allow harvest if running in full mode
106
+ if (this.blocked || this.mode !== _constants2.MODE.FULL) return; // only allow harvest if running in full mode
104
107
  if (!this.timeKeeper?.ready) return; // this should likely never happen, but just to be safe, we should never harvest if we cant correct time
105
108
  if (!this.agentRef.runtime.session) return; // session entity is required for trace to run and continue running
106
109
  if (this.sessionId !== this.agentRef.runtime.session.state.value || this.ptid !== this.agentRef.runtime.ptid) {
@@ -194,7 +197,8 @@ class Aggregate extends _aggregateBase.AggregateBase {
194
197
  }
195
198
 
196
199
  /** Stop running for the remainder of the page lifecycle */
197
- abort() {
200
+ abort(code) {
201
+ (0, _console.warn)(60, code);
198
202
  this.blocked = true;
199
203
  this.mode = _constants2.MODE.OFF;
200
204
  this.agentRef.runtime.session.write({
@@ -53,9 +53,6 @@ class TraceStorage {
53
53
  constructor(parent) {
54
54
  this.parent = parent;
55
55
  }
56
- isAfterSessionExpiry(entryTimestamp) {
57
- return this.parent.agentRef.runtime?.session?.isAfterSessionExpiry((this.parent.timeKeeper?.ready && this.parent.timeKeeper.convertRelativeTimestamp(entryTimestamp)) ?? undefined);
58
- }
59
56
 
60
57
  /** Central function called by all the other store__ & addToTrace API to append a trace node. */
61
58
  storeSTN(stn) {
@@ -66,10 +63,6 @@ class TraceStorage {
66
63
  const openedSpace = this.trimSTNs(ERROR_MODE_SECONDS_WINDOW); // but maybe we could make some space by discarding irrelevant nodes if we're in sessioned Error mode
67
64
  if (openedSpace === 0) return;
68
65
  }
69
- if (this.isAfterSessionExpiry(stn.s)) {
70
- this.parent.reportSupportabilityMetric('Session/Expired/SessionTrace/Seen');
71
- return;
72
- }
73
66
  if (this.trace[stn.n]) this.trace[stn.n].push(stn);else this.trace[stn.n] = [stn];
74
67
  if (stn.s < this.earliestTimeStamp) this.earliestTimeStamp = stn.s;
75
68
  if (stn.s > this.latestTimeStamp) this.latestTimeStamp = stn.s;
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.EntityManager = void 0;
7
+ var _agentConstants = require("../../common/constants/agent-constants");
7
8
  /**
8
9
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
9
10
  * SPDX-License-Identifier: Apache-2.0
@@ -12,16 +13,14 @@ exports.EntityManager = void 0;
12
13
  class EntityManager {
13
14
  #entities = new Map();
14
15
  #entityGuidLookup = {};
15
- #defaultEntity = null;
16
16
  constructor(agentRef) {
17
17
  this.agentRef = agentRef;
18
- this.#defaultEntity = {
18
+ this.#entities.set(_agentConstants.DEFAULT_KEY, {
19
19
  licenseKey: agentRef.info.licenseKey,
20
20
  applicationID: agentRef.info.applicationID
21
- };
21
+ });
22
22
  }
23
- get(entityGuid) {
24
- if (!entityGuid) return this.#defaultEntity;
23
+ get(entityGuid = _agentConstants.DEFAULT_KEY) {
25
24
  return this.#entities.get(entityGuid);
26
25
  }
27
26
  getEntityGuidFor(licenseKey, applicationID) {
@@ -41,7 +40,7 @@ class EntityManager {
41
40
  this.#entities.clear();
42
41
  }
43
42
  setDefaultEntity(entity) {
44
- this.#defaultEntity = entity;
43
+ this.#entities.set(_agentConstants.DEFAULT_KEY, entity);
45
44
  }
46
45
  }
47
46
  exports.EntityManager = EntityManager;
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.EventStoreManager = void 0;
7
+ var _agentConstants = require("../../common/constants/agent-constants");
7
8
  var _globalEvent = require("../../common/dispatch/global-event");
8
9
  var _featureFlags = require("../../common/util/feature-flags");
9
10
  var _target = require("../../common/util/target");
@@ -12,7 +13,6 @@ var _target = require("../../common/util/target");
12
13
  * SPDX-License-Identifier: Apache-2.0
13
14
  */
14
15
 
15
- const DEFAULT_KEY = 'NR_CONTAINER_AGENT'; // this is the default entity guid used for the default storage instance
16
16
  /**
17
17
  * This layer allows multiple browser entity apps, or "target", to each have their own segregated storage instance.
18
18
  * The purpose is so the harvester can send data to different apps within the same agent. Each feature should have a manager if it needs this capability.
@@ -28,7 +28,7 @@ class EventStoreManager {
28
28
  this.agentRef = agentRef;
29
29
  this.entityManager = agentRef.runtime.entityManager;
30
30
  this.StorageClass = storageClass;
31
- this.appStorageMap = new Map([[DEFAULT_KEY, new this.StorageClass()]]);
31
+ this.appStorageMap = new Map([[_agentConstants.DEFAULT_KEY, new this.StorageClass()]]);
32
32
  this.featureName = featureName;
33
33
  this.setEventStore(defaultEntityGuid);
34
34
  }
@@ -38,7 +38,7 @@ class EventStoreManager {
38
38
  * @param {string=} targetEntityGuid the lookup
39
39
  * @returns {*} ALWAYS returns a storage instance
40
40
  */
41
- #getEventStore(targetEntityGuid = DEFAULT_KEY) {
41
+ #getEventStore(targetEntityGuid = _agentConstants.DEFAULT_KEY) {
42
42
  if (!this.appStorageMap.has(targetEntityGuid)) this.setEventStore(targetEntityGuid);
43
43
  return this.appStorageMap.get(targetEntityGuid);
44
44
  }
@@ -46,7 +46,7 @@ class EventStoreManager {
46
46
  /** we should already have an event store for the default */
47
47
  if (!targetEntityGuid) return;
48
48
  /** if the target is the container agent, SHARE the default storage -- otherwise create a new event store */
49
- const eventStorage = (0, _target.isContainerAgentTarget)(this.entityManager.get(targetEntityGuid), this.agentRef) ? this.appStorageMap.get(DEFAULT_KEY) : new this.StorageClass();
49
+ const eventStorage = (0, _target.isContainerAgentTarget)(this.entityManager.get(targetEntityGuid), this.agentRef) ? this.appStorageMap.get(_agentConstants.DEFAULT_KEY) : new this.StorageClass();
50
50
  this.appStorageMap.set(targetEntityGuid, eventStorage);
51
51
  }
52
52
 
@@ -102,6 +102,10 @@ class EventStoreManager {
102
102
  }];
103
103
  const allPayloads = [];
104
104
  this.appStorageMap.forEach((eventStore, targetEntityGuid) => {
105
+ // We shouldnt harvest unless we have a valid entity guid. It was ONLY stored under the default key temporarily
106
+ // until a real key was returned in the RUM call. The real key SHARES the event store with the default key, and
107
+ // should be the key that is honored to get the event store to ensure a valid connection was made.
108
+ if (targetEntityGuid === _agentConstants.DEFAULT_KEY) return;
105
109
  const targetApp = this.entityManager.get(targetEntityGuid);
106
110
  if (targetApp) allPayloads.push({
107
111
  targetApp,
@@ -3,14 +3,13 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.spaPrefix = exports.replayRunning = exports.prefix = exports.WRAP_LOGGER = exports.START = exports.SET_USER_ID = exports.SET_PAGE_VIEW_NAME = exports.SET_ERROR_HANDLER = exports.SET_CUSTOM_ATTRIBUTE = exports.SET_CURRENT_ROUTE_NAME = exports.SET_APPLICATION_VERSION = exports.REGISTER = exports.RECORD_REPLAY = exports.RECORD_CUSTOM_EVENT = exports.PAUSE_REPLAY = exports.NOTICE_ERROR = exports.LOG = exports.INTERACTION = exports.FINISHED = exports.ADD_TO_TRACE = exports.ADD_RELEASE = exports.ADD_PAGE_ACTION = void 0;
6
+ exports.spaPrefix = exports.prefix = exports.WRAP_LOGGER = exports.START = exports.SET_USER_ID = exports.SET_PAGE_VIEW_NAME = exports.SET_ERROR_HANDLER = exports.SET_CUSTOM_ATTRIBUTE = exports.SET_CURRENT_ROUTE_NAME = exports.SET_APPLICATION_VERSION = exports.REGISTER = exports.RECORD_REPLAY = exports.RECORD_CUSTOM_EVENT = exports.PAUSE_REPLAY = exports.NOTICE_ERROR = exports.MEASURE = exports.LOG = exports.INTERACTION = exports.FINISHED = exports.ADD_TO_TRACE = exports.ADD_RELEASE = exports.ADD_PAGE_ACTION = void 0;
7
7
  /**
8
8
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
9
9
  * SPDX-License-Identifier: Apache-2.0
10
10
  */
11
11
  const prefix = exports.prefix = 'api-';
12
12
  const spaPrefix = exports.spaPrefix = prefix + 'ixn-';
13
- const replayRunning = exports.replayRunning = {};
14
13
  const ADD_PAGE_ACTION = exports.ADD_PAGE_ACTION = 'addPageAction';
15
14
  const ADD_TO_TRACE = exports.ADD_TO_TRACE = 'addToTrace';
16
15
  const ADD_RELEASE = exports.ADD_RELEASE = 'addRelease';
@@ -29,4 +28,5 @@ const SET_ERROR_HANDLER = exports.SET_ERROR_HANDLER = 'setErrorHandler';
29
28
  const SET_PAGE_VIEW_NAME = exports.SET_PAGE_VIEW_NAME = 'setPageViewName';
30
29
  const SET_USER_ID = exports.SET_USER_ID = 'setUserId';
31
30
  const START = exports.START = 'start';
32
- const WRAP_LOGGER = exports.WRAP_LOGGER = 'wrapLogger';
31
+ const WRAP_LOGGER = exports.WRAP_LOGGER = 'wrapLogger';
32
+ const MEASURE = exports.MEASURE = 'measure';