@newrelic/browser-agent 1.271.0 → 1.273.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 (123) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/cjs/common/aggregate/aggregator.js +23 -30
  3. package/dist/cjs/common/aggregate/event-aggregator.js +84 -0
  4. package/dist/cjs/common/config/init.js +8 -4
  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/harvest-scheduler.js +1 -1
  8. package/dist/cjs/common/harvest/harvest.js +1 -5
  9. package/dist/cjs/common/harvest/types.js +0 -1
  10. package/dist/cjs/features/ajax/aggregate/index.js +52 -62
  11. package/dist/cjs/features/generic_events/aggregate/index.js +57 -36
  12. package/dist/cjs/features/generic_events/instrument/index.js +1 -1
  13. package/dist/cjs/features/jserrors/aggregate/index.js +23 -69
  14. package/dist/cjs/features/logging/aggregate/index.js +52 -59
  15. package/dist/cjs/features/metrics/aggregate/index.js +8 -5
  16. package/dist/cjs/features/page_view_timing/aggregate/index.js +8 -25
  17. package/dist/cjs/features/session_replay/aggregate/index.js +11 -10
  18. package/dist/cjs/features/session_replay/shared/recorder-events.js +2 -2
  19. package/dist/cjs/features/session_trace/aggregate/index.js +77 -88
  20. package/dist/cjs/features/session_trace/aggregate/trace/storage.js +22 -13
  21. package/dist/cjs/features/soft_navigations/aggregate/index.js +10 -20
  22. package/dist/cjs/features/soft_navigations/instrument/index.js +5 -9
  23. package/dist/cjs/features/spa/aggregate/index.js +10 -26
  24. package/dist/cjs/features/utils/aggregate-base.js +37 -0
  25. package/dist/cjs/features/utils/event-buffer.js +36 -87
  26. package/dist/cjs/features/utils/instrument-base.js +3 -3
  27. package/dist/cjs/loaders/features/features.js +13 -1
  28. package/dist/esm/common/aggregate/aggregator.js +23 -30
  29. package/dist/esm/common/aggregate/event-aggregator.js +78 -0
  30. package/dist/esm/common/config/init.js +8 -4
  31. package/dist/esm/common/constants/env.cdn.js +1 -1
  32. package/dist/esm/common/constants/env.npm.js +1 -1
  33. package/dist/esm/common/harvest/harvest-scheduler.js +1 -1
  34. package/dist/esm/common/harvest/harvest.js +1 -5
  35. package/dist/esm/common/harvest/types.js +0 -1
  36. package/dist/esm/features/ajax/aggregate/index.js +53 -62
  37. package/dist/esm/features/generic_events/aggregate/index.js +57 -36
  38. package/dist/esm/features/generic_events/instrument/index.js +1 -1
  39. package/dist/esm/features/jserrors/aggregate/index.js +24 -70
  40. package/dist/esm/features/logging/aggregate/index.js +52 -59
  41. package/dist/esm/features/metrics/aggregate/index.js +8 -5
  42. package/dist/esm/features/page_view_timing/aggregate/index.js +9 -26
  43. package/dist/esm/features/session_replay/aggregate/index.js +12 -11
  44. package/dist/esm/features/session_replay/shared/recorder-events.js +2 -2
  45. package/dist/esm/features/session_trace/aggregate/index.js +77 -88
  46. package/dist/esm/features/session_trace/aggregate/trace/storage.js +22 -13
  47. package/dist/esm/features/soft_navigations/aggregate/index.js +11 -21
  48. package/dist/esm/features/soft_navigations/instrument/index.js +5 -9
  49. package/dist/esm/features/spa/aggregate/index.js +11 -27
  50. package/dist/esm/features/utils/aggregate-base.js +37 -0
  51. package/dist/esm/features/utils/event-buffer.js +36 -88
  52. package/dist/esm/features/utils/instrument-base.js +3 -3
  53. package/dist/esm/loaders/features/features.js +12 -0
  54. package/dist/types/common/aggregate/aggregator.d.ts +4 -6
  55. package/dist/types/common/aggregate/aggregator.d.ts.map +1 -1
  56. package/dist/types/common/aggregate/event-aggregator.d.ts +26 -0
  57. package/dist/types/common/aggregate/event-aggregator.d.ts.map +1 -0
  58. package/dist/types/common/config/init.d.ts.map +1 -1
  59. package/dist/types/common/harvest/harvest.d.ts.map +1 -1
  60. package/dist/types/common/harvest/types.d.ts +1 -4
  61. package/dist/types/common/harvest/types.d.ts.map +1 -1
  62. package/dist/types/features/ajax/aggregate/index.d.ts +2 -10
  63. package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
  64. package/dist/types/features/generic_events/aggregate/index.d.ts +5 -11
  65. package/dist/types/features/generic_events/aggregate/index.d.ts.map +1 -1
  66. package/dist/types/features/generic_events/instrument/index.d.ts.map +1 -1
  67. package/dist/types/features/jserrors/aggregate/index.d.ts +4 -7
  68. package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
  69. package/dist/types/features/logging/aggregate/index.d.ts +10 -28
  70. package/dist/types/features/logging/aggregate/index.d.ts.map +1 -1
  71. package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
  72. package/dist/types/features/page_view_timing/aggregate/index.d.ts +1 -9
  73. package/dist/types/features/page_view_timing/aggregate/index.d.ts.map +1 -1
  74. package/dist/types/features/session_replay/aggregate/index.d.ts +3 -4
  75. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  76. package/dist/types/features/session_replay/shared/recorder-events.d.ts +1 -1
  77. package/dist/types/features/session_replay/shared/recorder-events.d.ts.map +1 -1
  78. package/dist/types/features/session_replay/shared/recorder.d.ts +1 -1
  79. package/dist/types/features/session_trace/aggregate/index.d.ts +17 -19
  80. package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
  81. package/dist/types/features/session_trace/aggregate/trace/storage.d.ts +10 -6
  82. package/dist/types/features/session_trace/aggregate/trace/storage.d.ts.map +1 -1
  83. package/dist/types/features/soft_navigations/aggregate/index.d.ts +3 -9
  84. package/dist/types/features/soft_navigations/aggregate/index.d.ts.map +1 -1
  85. package/dist/types/features/soft_navigations/instrument/index.d.ts.map +1 -1
  86. package/dist/types/features/spa/aggregate/index.d.ts +2 -3
  87. package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
  88. package/dist/types/features/utils/aggregate-base.d.ts +14 -0
  89. package/dist/types/features/utils/aggregate-base.d.ts.map +1 -1
  90. package/dist/types/features/utils/event-buffer.d.ts +19 -56
  91. package/dist/types/features/utils/event-buffer.d.ts.map +1 -1
  92. package/dist/types/loaders/features/features.d.ts +3 -0
  93. package/dist/types/loaders/features/features.d.ts.map +1 -1
  94. package/package.json +3 -2
  95. package/src/common/aggregate/aggregator.js +22 -32
  96. package/src/common/aggregate/event-aggregator.js +76 -0
  97. package/src/common/config/init.js +6 -2
  98. package/src/common/harvest/harvest-scheduler.js +1 -1
  99. package/src/common/harvest/harvest.js +1 -5
  100. package/src/common/harvest/types.js +0 -1
  101. package/src/features/ajax/aggregate/index.js +60 -67
  102. package/src/features/generic_events/aggregate/index.js +48 -38
  103. package/src/features/generic_events/instrument/index.js +2 -0
  104. package/src/features/jserrors/aggregate/index.js +21 -77
  105. package/src/features/logging/aggregate/index.js +46 -60
  106. package/src/features/metrics/aggregate/index.js +6 -4
  107. package/src/features/page_view_timing/aggregate/index.js +9 -30
  108. package/src/features/session_replay/aggregate/index.js +10 -14
  109. package/src/features/session_replay/shared/recorder-events.js +2 -2
  110. package/src/features/session_trace/aggregate/index.js +64 -73
  111. package/src/features/session_trace/aggregate/trace/storage.js +25 -14
  112. package/src/features/soft_navigations/aggregate/index.js +11 -22
  113. package/src/features/soft_navigations/instrument/index.js +6 -9
  114. package/src/features/spa/aggregate/index.js +12 -27
  115. package/src/features/utils/aggregate-base.js +39 -0
  116. package/src/features/utils/event-buffer.js +36 -83
  117. package/src/features/utils/instrument-base.js +3 -3
  118. package/src/loaders/features/features.js +13 -0
  119. package/dist/cjs/features/ajax/aggregate/chunk.js +0 -51
  120. package/dist/esm/features/ajax/aggregate/chunk.js +0 -44
  121. package/dist/types/features/ajax/aggregate/chunk.d.ts +0 -8
  122. package/dist/types/features/ajax/aggregate/chunk.d.ts.map +0 -1
  123. package/src/features/ajax/aggregate/chunk.js +0 -52
package/CHANGELOG.md CHANGED
@@ -3,6 +3,21 @@
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.273.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.272.0...v1.273.0) (2024-11-11)
7
+
8
+
9
+ ### Features
10
+
11
+ * Refactor feature storages ([#1241](https://github.com/newrelic/newrelic-browser-agent/issues/1241)) ([f77380b](https://github.com/newrelic/newrelic-browser-agent/commit/f77380b01e9b57db0b5c782d6d512431229bcd79))
12
+ * Remove wrap-events usage from soft nav ([#1244](https://github.com/newrelic/newrelic-browser-agent/issues/1244)) ([911d8d1](https://github.com/newrelic/newrelic-browser-agent/commit/911d8d1d78b49fa4252257596e013806885ace9b))
13
+
14
+ ## [1.272.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.271.0...v1.272.0) (2024-11-07)
15
+
16
+
17
+ ### Features
18
+
19
+ * Marks and measures ([#1189](https://github.com/newrelic/newrelic-browser-agent/issues/1189)) ([1a58409](https://github.com/newrelic/newrelic-browser-agent/commit/1a58409cda8b7b264e58fe284041540941dccd1c))
20
+
6
21
  ## [1.271.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.270.3...v1.271.0) (2024-11-01)
7
22
 
8
23
 
@@ -4,15 +4,12 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.Aggregator = void 0;
7
- var _sharedContext = require("../context/shared-context");
8
7
  /*
9
8
  * Copyright 2020 New Relic Corporation. All rights reserved.
10
9
  * SPDX-License-Identifier: Apache-2.0
11
10
  */
12
-
13
- class Aggregator extends _sharedContext.SharedContext {
14
- constructor(parent) {
15
- super(parent);
11
+ class Aggregator {
12
+ constructor() {
16
13
  this.aggregatedData = {};
17
14
  }
18
15
 
@@ -21,12 +18,14 @@ class Aggregator extends _sharedContext.SharedContext {
21
18
  // metrics are the numeric values to be aggregated
22
19
 
23
20
  store(type, name, params, newMetrics, customParams) {
24
- var bucket = this.getBucket(type, name, params, customParams);
21
+ var bucket = this.#getBucket(type, name, params, customParams);
25
22
  bucket.metrics = aggregateMetrics(newMetrics, bucket.metrics);
26
23
  return bucket;
27
24
  }
28
- merge(type, name, metrics, params, customParams) {
29
- var bucket = this.getBucket(type, name, params, customParams);
25
+ merge(type, name, metrics, params, customParams, overwriteParams = false) {
26
+ var bucket = this.#getBucket(type, name, params, customParams);
27
+ if (overwriteParams) bucket.params = params; // replace current params with incoming params obj
28
+
30
29
  if (!bucket.metrics) {
31
30
  bucket.metrics = metrics;
32
31
  return;
@@ -51,11 +50,25 @@ class Aggregator extends _sharedContext.SharedContext {
51
50
  });
52
51
  }
53
52
  storeMetric(type, name, params, value) {
54
- var bucket = this.getBucket(type, name, params);
53
+ var bucket = this.#getBucket(type, name, params);
55
54
  bucket.stats = updateMetric(value, bucket.stats);
56
55
  return bucket;
57
56
  }
58
- getBucket(type, name, params, customParams) {
57
+
58
+ // Get all listed types buckets and it deletes the retrieved content from the aggregatedData
59
+ take(types, deleteWhenRetrieved = true) {
60
+ var results = {};
61
+ var type = '';
62
+ var hasData = false;
63
+ for (var i = 0; i < types.length; i++) {
64
+ type = types[i];
65
+ results[type] = Object.values(this.aggregatedData[type] || {});
66
+ if (results[type].length) hasData = true;
67
+ if (deleteWhenRetrieved) delete this.aggregatedData[type];
68
+ }
69
+ return hasData ? results : null;
70
+ }
71
+ #getBucket(type, name, params, customParams) {
59
72
  if (!this.aggregatedData[type]) this.aggregatedData[type] = {};
60
73
  var bucket = this.aggregatedData[type][name];
61
74
  if (!bucket) {
@@ -68,26 +81,6 @@ class Aggregator extends _sharedContext.SharedContext {
68
81
  }
69
82
  return bucket;
70
83
  }
71
- get(type, name) {
72
- // if name is passed, get a single bucket
73
- if (name) return this.aggregatedData[type] && this.aggregatedData[type][name];
74
- // else, get all buckets of that type
75
- return this.aggregatedData[type];
76
- }
77
-
78
- // Like get, but for many types and it deletes the retrieved content from the aggregatedData
79
- take(types) {
80
- var results = {};
81
- var type = '';
82
- var hasData = false;
83
- for (var i = 0; i < types.length; i++) {
84
- type = types[i];
85
- results[type] = Object.values(this.aggregatedData[type] || {});
86
- if (results[type].length) hasData = true;
87
- delete this.aggregatedData[type];
88
- }
89
- return hasData ? results : null;
90
- }
91
84
  }
92
85
  exports.Aggregator = Aggregator;
93
86
  function aggregateMetrics(newMetrics, oldMetrics) {
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.EventAggregator = void 0;
7
+ var _aggregator = require("./aggregator");
8
+ /**
9
+ * An extension of the Aggregator class that provides an interface similar to that of EventBuffer class.
10
+ * This typecasting allow features that uses Aggregator as their event handler to share the same AggregateBase.events utilization by those features.
11
+ */
12
+ class EventAggregator {
13
+ #aggregator = new _aggregator.Aggregator();
14
+ #savedNamesToBuckets = {};
15
+ isEmpty({
16
+ aggregatorTypes
17
+ }) {
18
+ if (!aggregatorTypes) return Object.keys(this.#aggregator.aggregatedData).length === 0;
19
+ return aggregatorTypes.every(type => !this.#aggregator.aggregatedData[type]); // no bucket exist for any of the types we're looking for
20
+ }
21
+ add(type, name, params, newMetrics, customParams) {
22
+ // Do we need to track byte size here like EventBuffer?
23
+ this.#aggregator.store(type, name, params, newMetrics, customParams);
24
+ return true;
25
+ }
26
+ addMetric(type, name, params, value) {
27
+ this.#aggregator.storeMetric(type, name, params, value);
28
+ return true;
29
+ }
30
+ save({
31
+ aggregatorTypes
32
+ }) {
33
+ const key = aggregatorTypes.toString(); // the stringified types serve as the key to each save call, e.g. ['err', 'ierr', 'xhr'] => 'err,ierr,xhr'
34
+ const backupAggregatedDataSubset = {};
35
+ aggregatorTypes.forEach(type => backupAggregatedDataSubset[type] = this.#aggregator.aggregatedData[type]); // make a subset of the aggregatedData for each of the types we want to save
36
+ this.#savedNamesToBuckets[key] = backupAggregatedDataSubset;
37
+ /*
38
+ { 'err,ierr,xhr': {
39
+ 'err': {
40
+ <aggregateHash>: { metrics: { count: 1, time, ... }, params: {}, custom: {} },
41
+ <otherHashName>: { metrics: { count: 1, ... }, ... }
42
+ },
43
+ 'ierr': { ... },
44
+ 'xhr': { ... }
45
+ }
46
+ }
47
+ */
48
+ }
49
+ get(opts) {
50
+ const aggregatorTypes = Array.isArray(opts) ? opts : opts.aggregatorTypes;
51
+ return this.#aggregator.take(aggregatorTypes, false);
52
+ }
53
+ clear({
54
+ aggregatorTypes
55
+ } = {}) {
56
+ if (!aggregatorTypes) {
57
+ this.#aggregator.aggregatedData = {};
58
+ return;
59
+ }
60
+ aggregatorTypes.forEach(type => delete this.#aggregator.aggregatedData[type]);
61
+ }
62
+ reloadSave({
63
+ aggregatorTypes
64
+ }) {
65
+ const key = aggregatorTypes.toString();
66
+ const backupAggregatedDataSubset = this.#savedNamesToBuckets[key];
67
+ // Grabs the previously stored subset and merge it back into aggregatedData.
68
+ aggregatorTypes.forEach(type => {
69
+ Object.keys(backupAggregatedDataSubset[type] || {}).forEach(name => {
70
+ const bucket = backupAggregatedDataSubset[type][name];
71
+ // The older aka saved params take effect over the newer one. This is especially important when merging back for a failed harvest retry if, for example,
72
+ // the first-ever occurrence of an error is in the retry: it contains the params.stack_trace whereas the newer or current bucket.params would not.
73
+ this.#aggregator.merge(type, name, bucket.metrics, bucket.params, bucket.custom, true);
74
+ });
75
+ });
76
+ }
77
+ clearSave({
78
+ aggregatorTypes
79
+ }) {
80
+ const key = aggregatorTypes.toString();
81
+ delete this.#savedNamesToBuckets[key];
82
+ }
83
+ }
84
+ exports.EventAggregator = EventAggregator;
@@ -80,9 +80,6 @@ const model = () => {
80
80
  page_action: {
81
81
  enabled: true
82
82
  },
83
- user_actions: {
84
- enabled: true
85
- },
86
83
  page_view_event: {
87
84
  enabled: true,
88
85
  autoStart: true
@@ -92,6 +89,10 @@ const model = () => {
92
89
  harvestTimeSeconds: 30,
93
90
  autoStart: true
94
91
  },
92
+ performance: {
93
+ capture_marks: false,
94
+ capture_measures: false // false by default through experimental phase, but flipped to true once GA'd
95
+ },
95
96
  privacy: {
96
97
  cookies_enabled: true
97
98
  },
@@ -175,7 +176,10 @@ const model = () => {
175
176
  harvestTimeSeconds: 10,
176
177
  autoStart: true
177
178
  },
178
- ssl: undefined
179
+ ssl: undefined,
180
+ user_actions: {
181
+ enabled: true
182
+ }
179
183
  };
180
184
  };
181
185
  const _cache = {};
@@ -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.271.0";
15
+ const VERSION = exports.VERSION = "1.273.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.271.0";
15
+ const VERSION = exports.VERSION = "1.273.0";
16
16
 
17
17
  /**
18
18
  * Exposes the build type of the agent
@@ -102,7 +102,7 @@ class HarvestScheduler extends _sharedContext.SharedContext {
102
102
  let submitMethod;
103
103
  let payload;
104
104
  if (this.opts.getPayload) {
105
- // Ajax & PVT & SR features provide a callback function to get data for harvesting
105
+ // Ajax, PVT, Softnav, Logging, SR & ST features provide a single callback function to get data for harvesting
106
106
  submitMethod = submitData.getSubmitMethod({
107
107
  isFinalHarvest: opts?.unload
108
108
  });
@@ -128,11 +128,7 @@ class Harvest extends _sharedContext.SharedContext {
128
128
  const fullUrl = "".concat(url, "?").concat(baseParams).concat(payloadParams);
129
129
  const gzip = !!qs?.attributes?.includes('gzip');
130
130
  if (!gzip) {
131
- if (endpoint === 'events') {
132
- body = body.e;
133
- } else {
134
- body = (0, _stringify.stringify)(body);
135
- }
131
+ if (endpoint !== 'events') body = (0, _stringify.stringify)(body); // all features going to /events/ endpoint should already be serialized & stringified
136
132
  /** Warn --once per endpoint-- if the agent tries to send large payloads */
137
133
  if (body.length > 750000 && (warnings[endpoint] = (warnings?.[endpoint] || 0) + 1) === 1) (0, _console.warn)(28, endpoint);
138
134
  }
@@ -18,7 +18,6 @@ exports.unused = void 0;
18
18
  * @typedef {object} HarvestPayload
19
19
  * @property {object} qs Map of values that should be sent as part of the request query string.
20
20
  * @property {object} body Map of values that should be sent as the body of the request.
21
- * @property {string} body.e Special case of body used for browser interactions.
22
21
  */
23
22
 
24
23
  /**
@@ -14,10 +14,7 @@ var _features = require("../../../loaders/features/features");
14
14
  var _constants2 = require("../../metrics/constants");
15
15
  var _aggregateBase = require("../../utils/aggregate-base");
16
16
  var _gql = require("./gql");
17
- var _nreum = require("../../../common/window/nreum");
18
- var _chunk = _interopRequireDefault(require("./chunk"));
19
- var _eventBuffer = require("../../utils/event-buffer");
20
- function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
17
+ var _belSerializer = require("../../../common/serialize/bel-serializer");
21
18
  /*
22
19
  * Copyright 2020 New Relic Corporation. All rights reserved.
23
20
  * SPDX-License-Identifier: Apache-2.0
@@ -29,31 +26,30 @@ class Aggregate extends _aggregateBase.AggregateBase {
29
26
  super(agentRef, _constants.FEATURE_NAME);
30
27
  const harvestTimeSeconds = agentRef.init.ajax.harvestTimeSeconds || 10;
31
28
  (0, _denyList.setDenyList)(agentRef.runtime.denyList);
32
- this.ajaxEvents = new _eventBuffer.EventBuffer();
33
- this.spaAjaxEvents = {};
29
+ this.underSpaEvents = {};
34
30
  const classThis = this;
35
31
 
36
32
  // --- v Used by old spa feature
37
33
  this.ee.on('interactionDone', (interaction, wasSaved) => {
38
- if (!this.spaAjaxEvents[interaction.id]?.hasData) return;
34
+ if (!this.underSpaEvents[interaction.id]) return;
39
35
  if (!wasSaved) {
40
36
  // if the ixn was saved, then its ajax reqs are part of the payload whereas if it was discarded, it should still be harvested in the ajax feature itself
41
- this.ajaxEvents.merge(this.spaAjaxEvents[interaction.id]);
37
+ this.underSpaEvents[interaction.id].forEach(item => this.events.add(item));
42
38
  }
43
- delete this.spaAjaxEvents[interaction.id];
39
+ delete this.underSpaEvents[interaction.id];
44
40
  });
45
41
  // --- ^
46
42
  // --- v Used by new soft nav
47
- (0, _registerHandler.registerHandler)('returnAjax', event => this.ajaxEvents.add(event), this.featureName, this.ee);
43
+ (0, _registerHandler.registerHandler)('returnAjax', event => this.events.add(event), this.featureName, this.ee);
48
44
  // --- ^
49
45
  (0, _registerHandler.registerHandler)('xhr', function () {
50
46
  // the EE-drain system not only switches "this" but also passes a new EventContext with info. Should consider platform refactor to another system which passes a mutable context around separately and predictably to avoid problems like this.
51
47
  classThis.storeXhr(...arguments, this); // this switches the context back to the class instance while passing the NR context as an argument -- see "ctx" in storeXhr
52
48
  }, this.featureName, this.ee);
53
49
  this.waitForFlags([]).then(() => {
54
- const scheduler = new _harvestScheduler.HarvestScheduler('events', {
55
- onFinished: this.onEventsHarvestFinished.bind(this),
56
- getPayload: this.prepareHarvest.bind(this)
50
+ const scheduler = new _harvestScheduler.HarvestScheduler(_features.FEATURE_TO_ENDPOINT[this.featureName], {
51
+ onFinished: result => this.postHarvestCleanup(result.sent && result.retry),
52
+ getPayload: options => this.makeHarvestPayload(options.retry)
57
53
  }, this);
58
54
  scheduler.startTimer(harvestTimeSeconds);
59
55
  this.drain();
@@ -71,10 +67,11 @@ class Aggregate extends _aggregateBase.AggregateBase {
71
67
  }
72
68
  const shouldCollect = (0, _denyList.shouldCollectEvent)(params);
73
69
  const shouldOmitAjaxMetrics = this.agentRef.init.feature_flags?.includes('ajax_metrics_deny_list');
70
+ const jserrorsInUse = Boolean(this.agentRef.features?.[_features.FEATURE_NAMES.jserrors]);
74
71
 
75
- // store for timeslice metric (harvested by jserrors feature)
76
- if (shouldCollect || !shouldOmitAjaxMetrics) {
77
- this.agentRef.sharedAggregator.store('xhr', hash, params, metrics);
72
+ // Report ajax timeslice metric (to be harvested by jserrors feature, but only if it's running).
73
+ if (jserrorsInUse && (shouldCollect || !shouldOmitAjaxMetrics)) {
74
+ this.agentRef.sharedAggregator.add('xhr', hash, params, metrics);
78
75
  }
79
76
  if (!shouldCollect) {
80
77
  if (params.hostname === this.agentRef.info.errorBeacon || this.agentRef.init.proxy?.beacon && params.hostname === this.agentRef.init.proxy.beacon) {
@@ -113,63 +110,56 @@ class Aggregate extends _aggregateBase.AggregateBase {
113
110
  query: ctx.parsedOrigin?.search
114
111
  });
115
112
  if (event.gql) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Ajax/Events/GraphQL/Bytes-Added', (0, _stringify.stringify)(event.gql).length], undefined, _features.FEATURE_NAMES.metrics, this.ee);
116
- const softNavInUse = Boolean((0, _nreum.getNREUMInitializedAgent)(this.agentIdentifier)?.features?.[_features.FEATURE_NAMES.softNav]);
113
+ const softNavInUse = Boolean(this.agentRef.features?.[_features.FEATURE_NAMES.softNav]);
117
114
  if (softNavInUse) {
118
115
  // For newer soft nav (when running), pass the event to it for evaluation -- either part of an interaction or is given back
119
116
  (0, _handle.handle)('ajax', [event], undefined, _features.FEATURE_NAMES.softNav, this.ee);
120
117
  } else if (ctx.spaNode) {
121
118
  // For old spa (when running), if the ajax happened inside an interaction, hold it until the interaction finishes
122
119
  const interactionId = ctx.spaNode.interaction.id;
123
- this.spaAjaxEvents[interactionId] ??= new _eventBuffer.EventBuffer();
124
- this.spaAjaxEvents[interactionId].add(event);
120
+ this.underSpaEvents[interactionId] ??= [];
121
+ this.underSpaEvents[interactionId].push(event);
125
122
  } else {
126
- this.ajaxEvents.add(event);
123
+ this.events.add(event);
127
124
  }
128
125
  }
129
- prepareHarvest(options) {
130
- options = options || {};
131
- if (this.ajaxEvents.buffer.length === 0) return null;
132
- const payload = this.#getPayload(this.ajaxEvents.buffer);
133
- const payloadObjs = [];
134
- for (let i = 0; i < payload.length; i++) payloadObjs.push({
135
- body: {
136
- e: payload[i]
137
- }
138
- });
139
- if (options.retry) this.ajaxEvents.hold();else this.ajaxEvents.clear();
140
- return payloadObjs;
141
- }
142
- onEventsHarvestFinished(result) {
143
- if (result.retry && this.ajaxEvents.held.hasData) this.ajaxEvents.unhold();else this.ajaxEvents.held.clear();
144
- }
145
- #getPayload(events, numberOfChunks) {
146
- numberOfChunks = numberOfChunks || 1;
147
- const payload = [];
148
- const chunkSize = events.length / numberOfChunks;
149
- const eventChunks = splitChunks.call(this, events, chunkSize);
150
- let tooBig = false;
151
- for (let i = 0; i < eventChunks.length; i++) {
152
- const currentChunk = eventChunks[i];
153
- if (currentChunk.tooBig) {
154
- if (currentChunk.events.length > 1) {
155
- tooBig = true;
156
- break; // if the payload is too big BUT is made of more than 1 event, we can split it down again
157
- }
158
- // Otherwise, if it consists of one sole event, we do not send it (discarded) since we cannot break it apart any further.
159
- } else {
160
- payload.push(currentChunk.payload);
161
- }
162
- }
163
- // Check if the current payload string is too big, if so then run getPayload again with more buckets.
164
- return tooBig ? this.#getPayload(events, ++numberOfChunks) : payload;
165
- function splitChunks(arr, chunkSize) {
166
- chunkSize = chunkSize || arr.length;
167
- const chunks = [];
168
- for (let i = 0, len = arr.length; i < len; i += chunkSize) {
169
- chunks.push(new _chunk.default(arr.slice(i, i + chunkSize), this));
126
+ serializer(eventBuffer) {
127
+ const addString = (0, _belSerializer.getAddStringContext)(this.agentIdentifier);
128
+ let payload = 'bel.7;';
129
+ for (let i = 0; i < eventBuffer.length; i++) {
130
+ const event = eventBuffer[i];
131
+ const fields = [(0, _belSerializer.numeric)(event.startTime), (0, _belSerializer.numeric)(event.endTime - event.startTime), (0, _belSerializer.numeric)(0),
132
+ // callbackEnd
133
+ (0, _belSerializer.numeric)(0),
134
+ // no callbackDuration for non-SPA events
135
+ addString(event.method), (0, _belSerializer.numeric)(event.status), addString(event.domain), addString(event.path), (0, _belSerializer.numeric)(event.requestSize), (0, _belSerializer.numeric)(event.responseSize), event.type === 'fetch' ? 1 : '', addString(0),
136
+ // nodeId
137
+ (0, _belSerializer.nullable)(event.spanId, addString, true) +
138
+ // guid
139
+ (0, _belSerializer.nullable)(event.traceId, addString, true) +
140
+ // traceId
141
+ (0, _belSerializer.nullable)(event.spanTimestamp, _belSerializer.numeric, false) // timestamp
142
+ ];
143
+ let insert = '2,';
144
+
145
+ // Since configuration objects (like info) are created new each time they are set, we have to grab the current pointer to the attr object here.
146
+ const jsAttributes = this.agentRef.info.jsAttributes;
147
+
148
+ // add custom attributes
149
+ // gql decorators are added as custom attributes to alleviate need for new BEL schema
150
+ const attrParts = (0, _belSerializer.addCustomAttributes)({
151
+ ...(jsAttributes || {}),
152
+ ...(event.gql || {})
153
+ }, addString);
154
+ fields.unshift((0, _belSerializer.numeric)(attrParts.length));
155
+ insert += fields.join(',');
156
+ if (attrParts && attrParts.length > 0) {
157
+ insert += ';' + attrParts.join(';');
170
158
  }
171
- return chunks;
159
+ if (i + 1 < eventBuffer.length) insert += ';';
160
+ payload += insert;
172
161
  }
162
+ return payload;
173
163
  }
174
164
  }
175
165
  exports.Aggregate = Aggregate;
@@ -14,9 +14,9 @@ var _console = require("../../../common/util/console");
14
14
  var _now = require("../../../common/timing/now");
15
15
  var _registerHandler = require("../../../common/event-emitter/register-handler");
16
16
  var _constants2 = require("../../metrics/constants");
17
- var _eventBuffer = require("../../utils/event-buffer");
18
17
  var _traverse = require("../../../common/util/traverse");
19
18
  var _agentConstants = require("../../../common/constants/agent-constants");
19
+ var _features = require("../../../loaders/features/features");
20
20
  var _userActionsAggregator = require("./user-actions/user-actions-aggregator");
21
21
  var _iframe = require("../../../common/dom/iframe");
22
22
  /*
@@ -31,14 +31,12 @@ class Aggregate extends _aggregateBase.AggregateBase {
31
31
  this.eventsPerHarvest = 1000;
32
32
  this.harvestTimeSeconds = agentRef.init.generic_events.harvestTimeSeconds;
33
33
  this.referrerUrl = _runtime.isBrowserScope && document.referrer ? (0, _cleanUrl.cleanURL)(document.referrer) : undefined;
34
- this.events = new _eventBuffer.EventBuffer();
35
34
  this.waitForFlags(['ins']).then(([ins]) => {
36
35
  if (!ins) {
37
36
  this.blocked = true;
38
37
  this.deregisterDrain();
39
38
  return;
40
39
  }
41
- const preHarvestMethods = [];
42
40
  if (agentRef.init.page_action.enabled) {
43
41
  (0, _registerHandler.registerHandler)('api-addPageAction', (timestamp, name, attributes) => {
44
42
  this.addEvent({
@@ -55,9 +53,10 @@ class Aggregate extends _aggregateBase.AggregateBase {
55
53
  });
56
54
  }, this.featureName, this.ee);
57
55
  }
56
+ let addUserAction;
58
57
  if (_runtime.isBrowserScope && agentRef.init.user_actions.enabled) {
59
58
  this.userActionAggregator = new _userActionsAggregator.UserActionsAggregator();
60
- this.addUserAction = aggregatedUserAction => {
59
+ addUserAction = aggregatedUserAction => {
61
60
  try {
62
61
  /** The aggregator process only returns an event when it is "done" aggregating -
63
62
  * so we still need to validate that an event was given to this method before we try to add */
@@ -99,21 +98,53 @@ class Aggregate extends _aggregateBase.AggregateBase {
99
98
  };
100
99
  (0, _registerHandler.registerHandler)('ua', evt => {
101
100
  /** the processor will return the previously aggregated event if it has been completed by processing the current event */
102
- this.addUserAction(this.userActionAggregator.process(evt));
101
+ addUserAction(this.userActionAggregator.process(evt));
103
102
  }, this.featureName, this.ee);
104
- preHarvestMethods.push((options = {}) => {
105
- /** send whatever UserActions have been aggregated up to this point
106
- * if we are in a final harvest. By accessing the aggregationEvent, the aggregation is then force-cleared */
107
- if (options.isFinalHarvest) this.addUserAction(this.userActionAggregator.aggregationEvent);
108
- });
109
103
  }
110
- this.harvestScheduler = new _harvestScheduler.HarvestScheduler('ins', {
111
- onFinished: (...args) => this.onHarvestFinished(...args)
104
+
105
+ /**
106
+ * is it worth complicating the agent and skipping the POs for single repeating queries? maybe,
107
+ * but right now it was less desirable simply because it is a nice benefit of populating the event buffer
108
+ * immediately as events happen for payload evaluation purposes and that becomes a little more chaotic
109
+ * with an arbitrary query method. note: eventTypes: [...types] does not support the 'buffered' flag so we have
110
+ * to create up to two PO's here.
111
+ */
112
+ const performanceTypesToCapture = [...(agentRef.init.performance.capture_marks ? ['mark'] : []), ...(agentRef.init.performance.capture_measures ? ['measure'] : [])];
113
+ if (performanceTypesToCapture.length) {
114
+ try {
115
+ performanceTypesToCapture.forEach(type => {
116
+ if (PerformanceObserver.supportedEntryTypes.includes(type)) {
117
+ const observer = new PerformanceObserver(list => {
118
+ list.getEntries().forEach(entry => {
119
+ try {
120
+ this.addEvent({
121
+ eventType: 'BrowserPerformance',
122
+ timestamp: Math.floor(agentRef.runtime.timeKeeper.correctRelativeTimestamp(entry.startTime)),
123
+ entryName: entry.name,
124
+ entryDuration: entry.duration,
125
+ entryType: type,
126
+ ...(entry.detail && {
127
+ entryDetail: entry.detail
128
+ })
129
+ });
130
+ } catch (err) {}
131
+ });
132
+ });
133
+ observer.observe({
134
+ buffered: true,
135
+ type
136
+ });
137
+ }
138
+ });
139
+ } catch (err) {
140
+ // Something failed in our set up, likely the browser does not support PO's... do nothing
141
+ }
142
+ }
143
+ this.harvestScheduler = new _harvestScheduler.HarvestScheduler(_features.FEATURE_TO_ENDPOINT[this.featureName], {
144
+ onFinished: result => this.postHarvestCleanup(result.sent && result.retry),
145
+ onUnload: () => addUserAction?.(this.userActionAggregator.aggregationEvent)
112
146
  }, this);
113
- this.harvestScheduler.harvest.on('ins', (...args) => {
114
- preHarvestMethods.forEach(fn => fn(...args));
115
- return this.onHarvestStarted(...args);
116
- });
147
+ this.harvestScheduler.harvest.on(_features.FEATURE_TO_ENDPOINT[this.featureName], options => this.makeHarvestPayload(options.retry));
117
148
  this.harvestScheduler.startTimer(this.harvestTimeSeconds, 0);
118
149
  this.drain();
119
150
  });
@@ -160,30 +191,20 @@ class Aggregate extends _aggregateBase.AggregateBase {
160
191
  this.events.add(eventAttributes);
161
192
  this.checkEventLimits();
162
193
  }
163
- onHarvestStarted(options) {
164
- const {
165
- userAttributes,
166
- atts
167
- } = this.agentRef.info;
168
- if (!this.events.hasData) return;
169
- var payload = {
170
- qs: {
171
- ua: userAttributes,
172
- at: atts
173
- },
174
- body: (0, _traverse.applyFnToProps)({
175
- ins: this.events.buffer
176
- }, this.obfuscator.obfuscateString.bind(this.obfuscator), 'string')
177
- };
178
- if (options.retry) this.events.hold();else this.events.clear();
179
- return payload;
194
+ serializer(eventBuffer) {
195
+ return (0, _traverse.applyFnToProps)({
196
+ ins: eventBuffer
197
+ }, this.obfuscator.obfuscateString.bind(this.obfuscator), 'string');
180
198
  }
181
- onHarvestFinished(result) {
182
- if (result && result?.sent && result?.retry && this.events.held.hasData) this.events.unhold();else this.events.held.clear();
199
+ queryStringsBuilder() {
200
+ return {
201
+ ua: this.agentRef.info.userAttributes,
202
+ at: this.agentRef.info.atts
203
+ };
183
204
  }
184
205
  checkEventLimits() {
185
206
  // check if we've reached any harvest limits...
186
- if (this.events.bytes > _agentConstants.IDEAL_PAYLOAD_SIZE) {
207
+ if (this.events.byteSize() > _agentConstants.IDEAL_PAYLOAD_SIZE) {
187
208
  this.ee.emit(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['GenericEvents/Harvest/Max/Seen']);
188
209
  this.harvestScheduler.runHarvest();
189
210
  }
@@ -17,7 +17,7 @@ class Instrument extends _instrumentBase.InstrumentBase {
17
17
  static featureName = _constants.FEATURE_NAME;
18
18
  constructor(agentRef, auto = true) {
19
19
  super(agentRef, _constants.FEATURE_NAME, auto);
20
- const genericEventSourceConfigs = [agentRef.init.page_action.enabled, agentRef.init.user_actions.enabled
20
+ const genericEventSourceConfigs = [agentRef.init.page_action.enabled, agentRef.init.performance.capture_marks, agentRef.init.performance.capture_measures, agentRef.init.user_actions.enabled
21
21
  // other future generic event source configs to go here, like M&Ms, PageResouce, etc.
22
22
  ];
23
23
  if (_runtime.isBrowserScope && agentRef.init.user_actions.enabled) {