@newrelic/browser-agent 1.295.0 → 1.296.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/CHANGELOG.md +12 -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/wrap/wrap-events.js +2 -1
  5. package/dist/cjs/features/session_trace/aggregate/index.js +20 -21
  6. package/dist/cjs/features/session_trace/aggregate/trace/storage.js +198 -185
  7. package/dist/cjs/features/session_trace/aggregate/trace/utils.js +41 -0
  8. package/dist/cjs/features/session_trace/constants.js +3 -2
  9. package/dist/cjs/features/utils/aggregate-base.js +1 -2
  10. package/dist/cjs/features/utils/event-buffer.js +34 -3
  11. package/dist/cjs/features/utils/event-store-manager.js +18 -11
  12. package/dist/esm/common/constants/env.cdn.js +1 -1
  13. package/dist/esm/common/constants/env.npm.js +1 -1
  14. package/dist/esm/common/wrap/wrap-events.js +2 -1
  15. package/dist/esm/features/session_trace/aggregate/index.js +21 -21
  16. package/dist/esm/features/session_trace/aggregate/trace/storage.js +199 -186
  17. package/dist/esm/features/session_trace/aggregate/trace/utils.js +34 -0
  18. package/dist/esm/features/session_trace/constants.js +2 -1
  19. package/dist/esm/features/utils/aggregate-base.js +1 -2
  20. package/dist/esm/features/utils/event-buffer.js +34 -3
  21. package/dist/esm/features/utils/event-store-manager.js +18 -11
  22. package/dist/tsconfig.tsbuildinfo +1 -1
  23. package/dist/types/common/wrap/wrap-events.d.ts.map +1 -1
  24. package/dist/types/features/session_trace/aggregate/index.d.ts +5 -10
  25. package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
  26. package/dist/types/features/session_trace/aggregate/trace/storage.d.ts +81 -39
  27. package/dist/types/features/session_trace/aggregate/trace/storage.d.ts.map +1 -1
  28. package/dist/types/features/session_trace/aggregate/trace/utils.d.ts +7 -0
  29. package/dist/types/features/session_trace/aggregate/trace/utils.d.ts.map +1 -0
  30. package/dist/types/features/session_trace/constants.d.ts +1 -0
  31. package/dist/types/features/session_trace/constants.d.ts.map +1 -1
  32. package/dist/types/features/utils/aggregate-base.d.ts.map +1 -1
  33. package/dist/types/features/utils/event-buffer.d.ts +18 -1
  34. package/dist/types/features/utils/event-buffer.d.ts.map +1 -1
  35. package/dist/types/features/utils/event-store-manager.d.ts +12 -0
  36. package/dist/types/features/utils/event-store-manager.d.ts.map +1 -1
  37. package/package.json +2 -2
  38. package/src/common/wrap/wrap-events.js +2 -1
  39. package/src/features/session_trace/aggregate/index.js +23 -15
  40. package/src/features/session_trace/aggregate/trace/storage.js +186 -189
  41. package/src/features/session_trace/aggregate/trace/utils.js +35 -0
  42. package/src/features/session_trace/constants.js +1 -0
  43. package/src/features/utils/aggregate-base.js +1 -2
  44. package/src/features/utils/event-buffer.js +35 -3
  45. package/src/features/utils/event-store-manager.js +18 -11
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.evtName = evtName;
7
+ exports.isTrivial = isTrivial;
8
+ /**
9
+ * Copyright 2020-2025 New Relic, Inc. All rights reserved.
10
+ * SPDX-License-Identifier: Apache-2.0
11
+ */
12
+ function evtName(type) {
13
+ switch (type) {
14
+ case 'keydown':
15
+ case 'keyup':
16
+ case 'keypress':
17
+ return 'typing';
18
+ case 'mousemove':
19
+ case 'mouseenter':
20
+ case 'mouseleave':
21
+ case 'mouseover':
22
+ case 'mouseout':
23
+ return 'mousing';
24
+ case 'touchstart':
25
+ case 'touchmove':
26
+ case 'touchend':
27
+ case 'touchcancel':
28
+ case 'touchenter':
29
+ case 'touchleave':
30
+ return 'touching';
31
+ case 'scroll':
32
+ case 'scrollend':
33
+ return 'scrolling';
34
+ default:
35
+ return type;
36
+ }
37
+ }
38
+ function isTrivial(node) {
39
+ const limit = 4;
40
+ return !!(node && typeof node.e === 'number' && typeof node.s === 'number' && node.e - node.s < limit);
41
+ }
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.START = exports.RESOURCE = exports.PUSH_STATE = exports.MAX_NODES_PER_HARVEST = exports.FN_START = exports.FN_END = exports.FEATURE_NAME = exports.END = exports.BST_RESOURCE = void 0;
6
+ exports.START = exports.RESOURCE = exports.PUSH_STATE = exports.MAX_NODES_PER_HARVEST = exports.FN_START = exports.FN_END = exports.FEATURE_NAME = exports.ERROR_MODE_SECONDS_WINDOW = exports.END = exports.BST_RESOURCE = void 0;
7
7
  var _features = require("../../loaders/features/features");
8
8
  /**
9
9
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
@@ -18,4 +18,5 @@ const END = exports.END = '-end';
18
18
  const FN_START = exports.FN_START = 'fn' + START;
19
19
  const FN_END = exports.FN_END = 'fn' + END;
20
20
  const PUSH_STATE = exports.PUSH_STATE = 'pushState';
21
- const MAX_NODES_PER_HARVEST = exports.MAX_NODES_PER_HARVEST = 1000;
21
+ const MAX_NODES_PER_HARVEST = exports.MAX_NODES_PER_HARVEST = 1000;
22
+ const ERROR_MODE_SECONDS_WINDOW = exports.ERROR_MODE_SECONDS_WINDOW = 30 * 1000; // sliding window of nodes to track when simply monitoring (but not harvesting) in error mode
@@ -65,8 +65,7 @@ class AggregateBase extends _featureBase.FeatureBase {
65
65
  #setupEventStore(entityGuid) {
66
66
  if (this.events) return;
67
67
  switch (this.featureName) {
68
- // SessionTrace + Replay have their own storage mechanisms.
69
- case _features.FEATURE_NAMES.sessionTrace:
68
+ // SessionReplay has its own storage mechanisms.
70
69
  case _features.FEATURE_NAMES.sessionReplay:
71
70
  break;
72
71
  // Jserror and Metric features uses a singleton EventAggregator instead of a regular EventBuffer.
@@ -26,6 +26,9 @@ class EventBuffer {
26
26
  this.maxPayloadSize = maxPayloadSize;
27
27
  this.featureAgg = featureAgg;
28
28
  }
29
+ get length() {
30
+ return this.#buffer.length;
31
+ }
29
32
  isEmpty() {
30
33
  return this.#buffer.length === 0;
31
34
  }
@@ -58,12 +61,40 @@ class EventBuffer {
58
61
  return true;
59
62
  }
60
63
 
64
+ /**
65
+ * Merges events in the buffer that match the given criteria.
66
+ * @param {Function} matcher - A function that takes an event and returns true if it should be merged.
67
+ * @param {Object} data - The data to merge into the matching events.
68
+ * @returns {boolean} true if a match was found and merged; false otherwise.
69
+ */
70
+ merge(matcher, data) {
71
+ if (this.isEmpty() || !matcher) return false;
72
+ const matchIdx = this.#buffer.findIndex(matcher);
73
+ if (matchIdx < 0) return false;
74
+ this.#buffer[matchIdx] = {
75
+ ...this.#buffer[matchIdx],
76
+ ...data
77
+ };
78
+ return true;
79
+ }
80
+
61
81
  /**
62
82
  * Wipes the main buffer
83
+ * @param {Object} [opts] - options for clearing the buffer
84
+ * @param {Number} [opts.clearBeforeTime] - timestamp before which all events should be cleared
85
+ * @param {String} [opts.timestampKey] - the key in the event object that contains the timestamp to compare against `clearBefore`
86
+ * @param {Number} [opts.clearBeforeIndex] - index before which all events should be cleared
87
+ * @returns {void}
63
88
  */
64
- clear() {
65
- this.#buffer = [];
66
- this.#rawBytes = 0;
89
+ clear(opts = {}) {
90
+ if (opts.clearBeforeTime !== undefined && opts.timestampKey) {
91
+ this.#buffer = this.#buffer.filter(event => event[opts.timestampKey] >= opts.clearBeforeTime);
92
+ } else if (opts.clearBeforeIndex !== undefined) {
93
+ this.#buffer = this.#buffer.slice(opts.clearBeforeIndex);
94
+ } else {
95
+ this.#buffer = [];
96
+ }
97
+ this.#rawBytes = this.#buffer.length ? (0, _stringify.stringify)(this.#buffer)?.length || 0 : 0; // recalculate raw bytes after clearing
67
98
  }
68
99
 
69
100
  /**
@@ -5,8 +5,6 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.EventStoreManager = void 0;
7
7
  var _agentConstants = require("../../common/constants/agent-constants");
8
- var _globalEvent = require("../../common/dispatch/global-event");
9
- var _featureFlags = require("../../common/util/feature-flags");
10
8
  var _target = require("../../common/util/target");
11
9
  /**
12
10
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
@@ -50,7 +48,24 @@ class EventStoreManager {
50
48
  this.appStorageMap.set(targetEntityGuid, eventStorage);
51
49
  }
52
50
 
53
- // This class must contain an union of all methods from all supported storage classes and conceptualize away the target app argument.
51
+ /** IMPORTANT
52
+ * This class must contain an union of all methods from all supported storage classes and conceptualize away the target app argument.
53
+ */
54
+
55
+ get length() {
56
+ return this.#getEventStore().length;
57
+ }
58
+
59
+ /**
60
+ * Calls the merge method on the underlying storage class.
61
+ * @param {*} matcher
62
+ * @param {*} data
63
+ * @param {*} targetEntityGuid
64
+ * @returns {boolean} True if the merge was successful
65
+ */
66
+ merge(matcher, data, targetEntityGuid) {
67
+ return this.#getEventStore(targetEntityGuid).merge(matcher, data);
68
+ }
54
69
 
55
70
  /**
56
71
  * Calls the isEmpty method on the underlying storage class. If target is provided, runs just for the target, otherwise runs for all apps.
@@ -73,14 +88,6 @@ class EventStoreManager {
73
88
  * @returns {boolean} True if the event was successfully added
74
89
  */
75
90
  add(event, targetEntityGuid) {
76
- (0, _globalEvent.dispatchGlobalEvent)({
77
- agentIdentifier: this.agentRef.agentIdentifier,
78
- drained: !!_featureFlags.activatedFeatures?.[this.agentRef.agentIdentifier],
79
- type: 'data',
80
- name: 'buffer',
81
- feature: this.featureAgg.featureName,
82
- data: event
83
- });
84
91
  return this.#getEventStore(targetEntityGuid).add(event);
85
92
  }
86
93
 
@@ -11,7 +11,7 @@
11
11
  /**
12
12
  * Exposes the version of the agent
13
13
  */
14
- export const VERSION = "1.295.0";
14
+ export const VERSION = "1.296.0-rc.1";
15
15
 
16
16
  /**
17
17
  * Exposes the build type of the agent
@@ -11,7 +11,7 @@
11
11
  /**
12
12
  * Exposes the version of the agent
13
13
  */
14
- export const VERSION = "1.295.0";
14
+ export const VERSION = "1.296.0-rc.1";
15
15
 
16
16
  /**
17
17
  * Exposes the build type of the agent
@@ -42,7 +42,8 @@ export function wrapEvents(sharedEE) {
42
42
  }
43
43
  ee.on(ADD_EVENT_LISTENER + '-start', function (args, target) {
44
44
  var originalListener = args[1];
45
- if (originalListener === null || typeof originalListener !== 'function' && typeof originalListener !== 'object') {
45
+ if (originalListener === null || typeof originalListener !== 'function' && typeof originalListener !== 'object' || args[0] === 'newrelic' // ignore our own window events
46
+ ) {
46
47
  return;
47
48
  }
48
49
  var wrapped = getOrSet(originalListener, flag, function () {
@@ -12,7 +12,7 @@ import { MODE, SESSION_EVENTS } from '../../../common/session/constants';
12
12
  import { applyFnToProps } from '../../../common/util/traverse';
13
13
  import { cleanURL } from '../../../common/url/clean-url';
14
14
  import { warn } from '../../../common/util/console';
15
- const ERROR_MODE_SECONDS_WINDOW = 30 * 1000; // sliding window of nodes to track when simply monitoring (but not harvesting) in error mode
15
+
16
16
  /** Reserved room for query param attrs */
17
17
  const QUERY_PARAM_PADDING = 5000;
18
18
  export class Aggregate extends AggregateBase {
@@ -27,8 +27,8 @@ export class Aggregate extends AggregateBase {
27
27
  this.everHarvested = false;
28
28
  /** If the harvest module is harvesting */
29
29
  this.harvesting = false;
30
- /** TraceStorage is the mechanism that holds, normalizes and aggregates ST nodes. It will be accessed and purged when harvests occur */
31
- this.events = new TraceStorage(this);
30
+ /** TraceStorage is a middleware that decides how to format data before passing events to `this.events` */
31
+ this.traceStorage = new TraceStorage(this);
32
32
 
33
33
  /* This agg needs information about sampling (sts) and entitlements (st) to make the appropriate decisions on running */
34
34
  this.waitForFlags(['sts', 'st']).then(([stMode, stEntitled]) => this.initialize(stMode, stEntitled));
@@ -59,9 +59,9 @@ export class Aggregate extends AggregateBase {
59
59
  if (this.sessionId !== sessionState.value || eventType === 'cross-tab' && sessionState.sessionTraceMode === MODE.OFF) this.abort(2);
60
60
  });
61
61
  if (typeof PerformanceNavigationTiming !== 'undefined') {
62
- this.events.storeTiming(globalScope.performance?.getEntriesByType?.('navigation')[0]);
62
+ this.traceStorage.storeTiming(globalScope.performance?.getEntriesByType?.('navigation')[0]);
63
63
  } else {
64
- this.events.storeTiming(globalScope.performance?.timing, true);
64
+ this.traceStorage.storeTiming(globalScope.performance?.timing, true);
65
65
  }
66
66
  }
67
67
 
@@ -75,13 +75,13 @@ export class Aggregate extends AggregateBase {
75
75
  this.timeKeeper ??= this.agentRef.runtime.timeKeeper;
76
76
 
77
77
  /** The handlers set up by the Inst file */
78
- registerHandler('bst', (...args) => this.events.storeEvent(...args), this.featureName, this.ee);
79
- registerHandler('bstResource', (...args) => this.events.storeResources(...args), this.featureName, this.ee);
80
- registerHandler('bstHist', (...args) => this.events.storeHist(...args), this.featureName, this.ee);
81
- registerHandler('bstXhrAgg', (...args) => this.events.storeXhrAgg(...args), this.featureName, this.ee);
82
- registerHandler('bstApi', (...args) => this.events.storeNode(...args), this.featureName, this.ee);
83
- registerHandler('trace-jserror', (...args) => this.events.storeErrorAgg(...args), this.featureName, this.ee);
84
- registerHandler('pvtAdded', (...args) => this.events.processPVT(...args), this.featureName, this.ee);
78
+ registerHandler('bst', (...args) => this.traceStorage.storeEvent(...args), this.featureName, this.ee);
79
+ registerHandler('bstResource', (...args) => this.traceStorage.storeResources(...args), this.featureName, this.ee);
80
+ registerHandler('bstHist', (...args) => this.traceStorage.storeHist(...args), this.featureName, this.ee);
81
+ registerHandler('bstXhrAgg', (...args) => this.traceStorage.storeXhrAgg(...args), this.featureName, this.ee);
82
+ registerHandler('bstApi', (...args) => this.traceStorage.storeNode(...args), this.featureName, this.ee);
83
+ registerHandler('trace-jserror', (...args) => this.traceStorage.storeErrorAgg(...args), this.featureName, this.ee);
84
+ registerHandler('pvtAdded', (...args) => this.traceStorage.processPVT(...args), this.featureName, this.ee);
85
85
  if (this.mode !== MODE.FULL) {
86
86
  /** A separate handler for noticing errors, and switching to "full" mode if running in "error" mode */
87
87
  registerHandler('trace-jserror', () => {
@@ -106,18 +106,12 @@ export class Aggregate extends AggregateBase {
106
106
  }
107
107
  return true;
108
108
  }
109
- serializer({
110
- stns
111
- }) {
109
+ serializer(stns) {
112
110
  if (!stns.length) return; // there are no processed nodes
113
111
  this.everHarvested = true;
114
112
  return applyFnToProps(stns, this.obfuscator.obfuscateString.bind(this.obfuscator), 'string');
115
113
  }
116
- queryStringsBuilder({
117
- stns,
118
- earliestTimeStamp,
119
- latestTimeStamp
120
- }) {
114
+ queryStringsBuilder(stns) {
121
115
  const firstSessionHarvest = !this.agentRef.runtime.session.state.traceHarvestStarted;
122
116
  if (firstSessionHarvest) this.agentRef.runtime.session.write({
123
117
  traceHarvestStarted: true
@@ -125,6 +119,8 @@ export class Aggregate extends AggregateBase {
125
119
  const hasReplay = this.agentRef.runtime.session.state.sessionReplayMode === 1;
126
120
  const endUserId = this.agentRef.info.jsAttributes['enduser.id'];
127
121
  const entityGuid = this.agentRef.runtime.appMetadata.agents?.[0]?.entityGuid;
122
+ const earliestTimeStamp = stns.reduce((earliest, stn) => Math.min(earliest, stn.s), Infinity);
123
+ const latestTimeStamp = stns.reduce((latest, stn) => Math.max(latest, stn.s), -Infinity);
128
124
 
129
125
  /* The blob consumer expects the following and will reject if not supplied:
130
126
  * browser_monitoring_key
@@ -184,7 +180,7 @@ export class Aggregate extends AggregateBase {
184
180
  });
185
181
  if (prevMode === MODE.OFF || !this.initialized) return this.initialize(this.mode, this.entitled);
186
182
  if (this.initialized) {
187
- this.events.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
183
+ this.traceStorage.trimSTNsByTime(); // up until now, Trace would've been just buffering nodes up to max, which needs to be trimmed to last X seconds
188
184
  this.agentRef.runtime.harvester.triggerHarvestFor(this);
189
185
  }
190
186
  }
@@ -199,4 +195,8 @@ export class Aggregate extends AggregateBase {
199
195
  });
200
196
  this.events.clear();
201
197
  }
198
+ postHarvestCleanup(result) {
199
+ this.traceStorage.clear(); // clear the trace storage state
200
+ super.postHarvestCleanup(result);
201
+ }
202
202
  }