@newrelic/browser-agent 1.252.1 → 1.254.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 (252) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +2 -2
  3. package/dist/cjs/cdn/experimental.js +6 -2
  4. package/dist/cjs/cdn/polyfills.js +2 -1
  5. package/dist/cjs/cdn/spa.js +5 -3
  6. package/dist/cjs/common/aggregate/aggregator.js +1 -8
  7. package/dist/cjs/common/config/state/init.js +7 -0
  8. package/dist/cjs/common/config/state/runtime.js +4 -1
  9. package/dist/cjs/common/constants/env.cdn.js +1 -1
  10. package/dist/cjs/common/constants/env.npm.js +1 -1
  11. package/dist/cjs/common/drain/drain.js +41 -27
  12. package/dist/cjs/common/event-emitter/contextual-ee.js +17 -12
  13. package/dist/cjs/common/harvest/harvest.js +5 -1
  14. package/dist/cjs/common/session/constants.js +2 -1
  15. package/dist/cjs/common/timing/nav-timing.js +8 -3
  16. package/dist/cjs/common/timing/now.js +1 -1
  17. package/dist/cjs/common/timing/time-keeper.js +94 -0
  18. package/dist/cjs/common/util/feature-flags.js +14 -31
  19. package/dist/cjs/common/wrap/index.js +0 -7
  20. package/dist/cjs/features/ajax/aggregate/index.js +41 -29
  21. package/dist/cjs/features/jserrors/aggregate/index.js +96 -84
  22. package/dist/cjs/features/metrics/aggregate/index.js +25 -24
  23. package/dist/cjs/features/page_action/aggregate/index.js +6 -4
  24. package/dist/cjs/features/page_view_event/aggregate/index.js +23 -3
  25. package/dist/cjs/features/page_view_event/aggregate/initialized-features.js +1 -0
  26. package/dist/cjs/features/page_view_timing/aggregate/index.js +15 -16
  27. package/dist/cjs/features/session_replay/aggregate/index.js +102 -92
  28. package/dist/cjs/features/session_replay/constants.js +5 -1
  29. package/dist/cjs/features/session_replay/instrument/index.js +24 -8
  30. package/dist/cjs/features/session_replay/shared/utils.js +26 -0
  31. package/dist/cjs/features/session_trace/aggregate/index.js +11 -8
  32. package/dist/cjs/features/soft_navigations/aggregate/ajax-node.js +50 -0
  33. package/dist/cjs/features/soft_navigations/aggregate/bel-node.js +29 -0
  34. package/dist/cjs/features/soft_navigations/aggregate/index.js +268 -0
  35. package/dist/cjs/features/soft_navigations/aggregate/initial-page-load-interaction.js +62 -0
  36. package/dist/cjs/features/soft_navigations/aggregate/interaction.js +146 -0
  37. package/dist/cjs/features/soft_navigations/constants.js +31 -0
  38. package/dist/cjs/features/soft_navigations/index.js +12 -0
  39. package/dist/cjs/features/soft_navigations/instrument/index.js +79 -0
  40. package/dist/cjs/features/spa/aggregate/index.js +23 -18
  41. package/dist/cjs/features/utils/agent-session.js +2 -1
  42. package/dist/cjs/features/utils/aggregate-base.js +18 -5
  43. package/dist/cjs/features/utils/feature-base.js +2 -0
  44. package/dist/cjs/features/utils/instrument-base.js +7 -9
  45. package/dist/cjs/features/utils/lazy-feature-loader.js +2 -0
  46. package/dist/cjs/loaders/agent-base.js +13 -3
  47. package/dist/cjs/loaders/agent.js +19 -22
  48. package/dist/cjs/loaders/api/api-methods.js +2 -1
  49. package/dist/cjs/loaders/api/api.js +15 -12
  50. package/dist/cjs/loaders/configure/configure.js +5 -2
  51. package/dist/cjs/loaders/configure/nonce.cdn.js +13 -0
  52. package/dist/cjs/loaders/configure/nonce.js +2 -13
  53. package/dist/cjs/loaders/configure/public-path.cdn.js +16 -0
  54. package/dist/cjs/loaders/configure/public-path.js +2 -8
  55. package/dist/cjs/loaders/features/enabled-features.js +1 -1
  56. package/dist/cjs/loaders/features/features.js +3 -1
  57. package/dist/esm/cdn/experimental.js +5 -2
  58. package/dist/esm/cdn/polyfills.js +2 -1
  59. package/dist/esm/cdn/spa.js +3 -1
  60. package/dist/esm/common/aggregate/aggregator.js +1 -8
  61. package/dist/esm/common/config/state/init.js +7 -0
  62. package/dist/esm/common/config/state/runtime.js +4 -1
  63. package/dist/esm/common/constants/env.cdn.js +1 -1
  64. package/dist/esm/common/constants/env.npm.js +1 -1
  65. package/dist/esm/common/drain/drain.js +40 -27
  66. package/dist/esm/common/event-emitter/contextual-ee.js +17 -12
  67. package/dist/esm/common/harvest/harvest.js +5 -1
  68. package/dist/esm/common/session/constants.js +1 -0
  69. package/dist/esm/common/timing/nav-timing.js +8 -3
  70. package/dist/esm/common/timing/now.js +1 -1
  71. package/dist/esm/common/timing/time-keeper.js +88 -0
  72. package/dist/esm/common/util/feature-flags.js +14 -31
  73. package/dist/esm/common/wrap/index.js +1 -2
  74. package/dist/esm/features/ajax/aggregate/index.js +43 -31
  75. package/dist/esm/features/jserrors/aggregate/index.js +96 -84
  76. package/dist/esm/features/metrics/aggregate/index.js +25 -24
  77. package/dist/esm/features/page_action/aggregate/index.js +6 -4
  78. package/dist/esm/features/page_view_event/aggregate/index.js +23 -3
  79. package/dist/esm/features/page_view_event/aggregate/initialized-features.js +1 -0
  80. package/dist/esm/features/page_view_timing/aggregate/index.js +15 -16
  81. package/dist/esm/features/session_replay/aggregate/index.js +103 -93
  82. package/dist/esm/features/session_replay/constants.js +4 -0
  83. package/dist/esm/features/session_replay/instrument/index.js +25 -9
  84. package/dist/esm/features/session_replay/shared/utils.js +17 -0
  85. package/dist/esm/features/session_trace/aggregate/index.js +11 -8
  86. package/dist/esm/features/soft_navigations/aggregate/ajax-node.js +43 -0
  87. package/dist/esm/features/soft_navigations/aggregate/bel-node.js +22 -0
  88. package/dist/esm/features/soft_navigations/aggregate/index.js +261 -0
  89. package/dist/esm/features/soft_navigations/aggregate/initial-page-load-interaction.js +55 -0
  90. package/dist/esm/features/soft_navigations/aggregate/interaction.js +140 -0
  91. package/dist/esm/features/soft_navigations/constants.js +25 -0
  92. package/dist/esm/features/soft_navigations/index.js +1 -0
  93. package/dist/esm/features/soft_navigations/instrument/index.js +73 -0
  94. package/dist/esm/features/spa/aggregate/index.js +23 -18
  95. package/dist/esm/features/utils/agent-session.js +2 -1
  96. package/dist/esm/features/utils/aggregate-base.js +18 -5
  97. package/dist/esm/features/utils/feature-base.js +2 -0
  98. package/dist/esm/features/utils/instrument-base.js +8 -10
  99. package/dist/esm/features/utils/lazy-feature-loader.js +2 -0
  100. package/dist/esm/loaders/agent-base.js +13 -3
  101. package/dist/esm/loaders/agent.js +19 -22
  102. package/dist/esm/loaders/api/api-methods.js +2 -1
  103. package/dist/esm/loaders/api/api.js +15 -12
  104. package/dist/esm/loaders/configure/configure.js +5 -2
  105. package/dist/esm/loaders/configure/nonce.cdn.js +11 -0
  106. package/dist/esm/loaders/configure/nonce.js +1 -11
  107. package/dist/esm/loaders/configure/public-path.cdn.js +9 -0
  108. package/dist/esm/loaders/configure/public-path.js +2 -8
  109. package/dist/esm/loaders/features/enabled-features.js +1 -1
  110. package/dist/esm/loaders/features/features.js +3 -1
  111. package/dist/types/common/aggregate/aggregator.d.ts.map +1 -1
  112. package/dist/types/common/config/state/init.d.ts.map +1 -1
  113. package/dist/types/common/config/state/runtime.d.ts.map +1 -1
  114. package/dist/types/common/drain/drain.d.ts +6 -0
  115. package/dist/types/common/drain/drain.d.ts.map +1 -1
  116. package/dist/types/common/event-emitter/contextual-ee.d.ts +2 -1
  117. package/dist/types/common/event-emitter/contextual-ee.d.ts.map +1 -1
  118. package/dist/types/common/harvest/harvest.d.ts.map +1 -1
  119. package/dist/types/common/session/constants.d.ts +1 -0
  120. package/dist/types/common/session/constants.d.ts.map +1 -1
  121. package/dist/types/common/timing/nav-timing.d.ts.map +1 -1
  122. package/dist/types/common/timing/time-keeper.d.ts +31 -0
  123. package/dist/types/common/timing/time-keeper.d.ts.map +1 -0
  124. package/dist/types/common/util/feature-flags.d.ts +11 -2
  125. package/dist/types/common/util/feature-flags.d.ts.map +1 -1
  126. package/dist/types/common/wrap/index.d.ts +1 -2
  127. package/dist/types/common/wrap/index.d.ts.map +1 -1
  128. package/dist/types/features/ajax/aggregate/index.d.ts +5 -5
  129. package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
  130. package/dist/types/features/jserrors/aggregate/index.d.ts +4 -3
  131. package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
  132. package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
  133. package/dist/types/features/page_action/aggregate/index.d.ts.map +1 -1
  134. package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
  135. package/dist/types/features/page_view_event/aggregate/initialized-features.d.ts.map +1 -1
  136. package/dist/types/features/page_view_timing/aggregate/index.d.ts +0 -2
  137. package/dist/types/features/page_view_timing/aggregate/index.d.ts.map +1 -1
  138. package/dist/types/features/session_replay/aggregate/index.d.ts +1 -1
  139. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  140. package/dist/types/features/session_replay/constants.d.ts +4 -0
  141. package/dist/types/features/session_replay/constants.d.ts.map +1 -1
  142. package/dist/types/features/session_replay/instrument/index.d.ts.map +1 -1
  143. package/dist/types/features/session_replay/shared/utils.d.ts +4 -0
  144. package/dist/types/features/session_replay/shared/utils.d.ts.map +1 -0
  145. package/dist/types/features/soft_navigations/aggregate/ajax-node.d.ts +19 -0
  146. package/dist/types/features/soft_navigations/aggregate/ajax-node.d.ts.map +1 -0
  147. package/dist/types/features/soft_navigations/aggregate/bel-node.d.ts +16 -0
  148. package/dist/types/features/soft_navigations/aggregate/bel-node.d.ts.map +1 -0
  149. package/dist/types/features/soft_navigations/aggregate/index.d.ts +34 -0
  150. package/dist/types/features/soft_navigations/aggregate/index.d.ts.map +1 -0
  151. package/dist/types/features/soft_navigations/aggregate/initial-page-load-interaction.d.ts +12 -0
  152. package/dist/types/features/soft_navigations/aggregate/initial-page-load-interaction.d.ts.map +1 -0
  153. package/dist/types/features/soft_navigations/aggregate/interaction.d.ts +50 -0
  154. package/dist/types/features/soft_navigations/aggregate/interaction.d.ts.map +1 -0
  155. package/dist/types/features/soft_navigations/constants.d.ts +20 -0
  156. package/dist/types/features/soft_navigations/constants.d.ts.map +1 -0
  157. package/dist/types/features/soft_navigations/index.d.ts +2 -0
  158. package/dist/types/features/soft_navigations/index.d.ts.map +1 -0
  159. package/dist/types/features/soft_navigations/instrument/index.d.ts +7 -0
  160. package/dist/types/features/soft_navigations/instrument/index.d.ts.map +1 -0
  161. package/dist/types/features/spa/aggregate/index.d.ts +2 -0
  162. package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
  163. package/dist/types/features/utils/agent-session.d.ts.map +1 -1
  164. package/dist/types/features/utils/aggregate-base.d.ts +2 -2
  165. package/dist/types/features/utils/aggregate-base.d.ts.map +1 -1
  166. package/dist/types/features/utils/feature-base.d.ts +1 -0
  167. package/dist/types/features/utils/feature-base.d.ts.map +1 -1
  168. package/dist/types/features/utils/instrument-base.d.ts +1 -7
  169. package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
  170. package/dist/types/features/utils/lazy-feature-loader.d.ts.map +1 -1
  171. package/dist/types/loaders/agent-base.d.ts +5 -1
  172. package/dist/types/loaders/agent-base.d.ts.map +1 -1
  173. package/dist/types/loaders/agent.d.ts +2 -2
  174. package/dist/types/loaders/agent.d.ts.map +1 -1
  175. package/dist/types/loaders/api/api-methods.d.ts.map +1 -1
  176. package/dist/types/loaders/api/api.d.ts +3 -5
  177. package/dist/types/loaders/api/api.d.ts.map +1 -1
  178. package/dist/types/loaders/configure/configure.d.ts.map +1 -1
  179. package/dist/types/loaders/configure/public-path.d.ts +1 -1
  180. package/dist/types/loaders/configure/public-path.d.ts.map +1 -1
  181. package/dist/types/loaders/features/features.d.ts +1 -0
  182. package/dist/types/loaders/features/features.d.ts.map +1 -1
  183. package/dist/types/loaders/micro-agent.d.ts +0 -1
  184. package/dist/types/loaders/micro-agent.d.ts.map +1 -1
  185. package/package.json +1 -1
  186. package/src/cdn/experimental.js +4 -2
  187. package/src/cdn/polyfills.js +1 -0
  188. package/src/cdn/spa.js +3 -1
  189. package/src/common/aggregate/aggregator.js +2 -11
  190. package/src/common/config/state/init.js +3 -1
  191. package/src/common/config/state/runtime.js +4 -1
  192. package/src/common/drain/drain.js +41 -28
  193. package/src/common/event-emitter/contextual-ee.js +21 -13
  194. package/src/common/harvest/harvest.js +4 -1
  195. package/src/common/session/constants.js +1 -0
  196. package/src/common/timing/nav-timing.js +7 -3
  197. package/src/common/timing/now.js +1 -1
  198. package/src/common/timing/time-keeper.js +96 -0
  199. package/src/common/util/feature-flags.js +13 -31
  200. package/src/common/wrap/index.js +1 -2
  201. package/src/features/ajax/aggregate/index.js +43 -33
  202. package/src/features/jserrors/aggregate/index.js +82 -87
  203. package/src/features/metrics/aggregate/index.js +18 -17
  204. package/src/features/page_action/aggregate/index.js +6 -5
  205. package/src/features/page_view_event/aggregate/index.js +19 -3
  206. package/src/features/page_view_event/aggregate/initialized-features.js +1 -0
  207. package/src/features/page_view_timing/aggregate/index.js +15 -15
  208. package/src/features/session_replay/aggregate/index.js +95 -92
  209. package/src/features/session_replay/constants.js +5 -0
  210. package/src/features/session_replay/instrument/index.js +24 -9
  211. package/src/features/session_replay/shared/utils.js +19 -0
  212. package/src/features/session_trace/aggregate/index.js +2 -2
  213. package/src/features/soft_navigations/aggregate/ajax-node.js +57 -0
  214. package/src/features/soft_navigations/aggregate/bel-node.js +26 -0
  215. package/src/features/soft_navigations/aggregate/index.js +256 -0
  216. package/src/features/soft_navigations/aggregate/initial-page-load-interaction.js +53 -0
  217. package/src/features/soft_navigations/aggregate/interaction.js +159 -0
  218. package/src/features/soft_navigations/constants.js +29 -0
  219. package/src/features/soft_navigations/index.js +1 -0
  220. package/src/features/soft_navigations/instrument/index.js +67 -0
  221. package/src/features/spa/aggregate/index.js +20 -17
  222. package/src/features/utils/agent-session.js +2 -1
  223. package/src/features/utils/aggregate-base.js +16 -8
  224. package/src/features/utils/feature-base.js +3 -0
  225. package/src/features/utils/instrument-base.js +8 -10
  226. package/src/features/utils/lazy-feature-loader.js +2 -0
  227. package/src/loaders/agent-base.js +13 -3
  228. package/src/loaders/agent.js +20 -19
  229. package/src/loaders/api/api-methods.js +4 -1
  230. package/src/loaders/api/api.js +15 -13
  231. package/src/loaders/configure/configure.js +4 -1
  232. package/src/loaders/configure/nonce.cdn.js +12 -0
  233. package/src/loaders/configure/nonce.js +1 -12
  234. package/src/loaders/configure/public-path.cdn.js +9 -0
  235. package/src/loaders/configure/public-path.js +2 -8
  236. package/src/loaders/features/enabled-features.js +1 -1
  237. package/src/loaders/features/features.js +3 -1
  238. package/dist/cjs/common/wrap/wrap-raf.js +0 -55
  239. package/dist/cjs/loaders/configure/nonce.npm.js +0 -2
  240. package/dist/cjs/loaders/configure/public-path.npm.js +0 -10
  241. package/dist/esm/common/wrap/wrap-raf.js +0 -48
  242. package/dist/esm/loaders/configure/nonce.npm.js +0 -1
  243. package/dist/esm/loaders/configure/public-path.npm.js +0 -3
  244. package/dist/types/common/wrap/wrap-raf.d.ts +0 -16
  245. package/dist/types/common/wrap/wrap-raf.d.ts.map +0 -1
  246. package/dist/types/loaders/configure/nonce.npm.d.ts +0 -1
  247. package/dist/types/loaders/configure/nonce.npm.d.ts.map +0 -1
  248. package/dist/types/loaders/configure/public-path.npm.d.ts +0 -2
  249. package/dist/types/loaders/configure/public-path.npm.d.ts.map +0 -1
  250. package/src/common/wrap/wrap-raf.js +0 -52
  251. package/src/loaders/configure/nonce.npm.js +0 -1
  252. package/src/loaders/configure/public-path.npm.js +0 -3
@@ -18,6 +18,8 @@ import { globalScope } from '../../../common/constants/runtime';
18
18
  import { FEATURE_NAME } from '../constants';
19
19
  import { FEATURE_NAMES } from '../../../loaders/features/features';
20
20
  import { AggregateBase } from '../../utils/aggregate-base';
21
+ import { getNREUMInitializedAgent } from '../../../common/window/nreum';
22
+ import { deregisterDrain } from '../../../common/drain/drain';
21
23
 
22
24
  /**
23
25
  * @typedef {import('./compute-stack-trace.js').StackInfo} StackInfo
@@ -32,42 +34,41 @@ export class Aggregate extends AggregateBase {
32
34
  this.stackReported = {};
33
35
  this.observedAt = {};
34
36
  this.pageviewReported = {};
35
- this.errorCache = {};
37
+ this.bufferedErrorsUnderSpa = {};
36
38
  this.currentBody = undefined;
37
39
  this.errorOnPage = false;
38
40
 
39
41
  // this will need to change to match whatever ee we use in the instrument
40
- this.ee.on('interactionSaved', interaction => this.onInteractionSaved(interaction));
41
-
42
- // this will need to change to match whatever ee we use in the instrument
43
- this.ee.on('interactionDiscarded', interaction => this.onInteractionDiscarded(interaction));
42
+ this.ee.on('interactionDone', (interaction, wasSaved) => this.onInteractionDone(interaction, wasSaved));
44
43
  register('err', function () {
45
44
  return _this.storeError(...arguments);
46
45
  }, this.featureName, this.ee);
47
46
  register('ierr', function () {
48
47
  return _this.storeError(...arguments);
49
48
  }, this.featureName, this.ee);
49
+ register('softNavFlush', (interactionId, wasFinished, softNavAttrs) => this.onSoftNavNotification(interactionId, wasFinished, softNavAttrs), this.featureName, this.ee); // when an ixn is done or cancelled
50
+
50
51
  const harvestTimeSeconds = getConfigurationValue(this.agentIdentifier, 'jserrors.harvestTimeSeconds') || 10;
51
- const scheduler = new HarvestScheduler('jserrors', {
52
- onFinished: function () {
53
- return _this.onHarvestFinished(...arguments);
54
- }
55
- }, this);
56
- scheduler.harvest.on('jserrors', function () {
57
- return _this.onHarvestStarted(...arguments);
58
- });
59
52
 
60
- // Don't start harvesting until "drain" for this feat has been called (which currently requires RUM response).
61
- this.ee.on("drain-".concat(this.featureName), () => {
62
- if (!this.blocked) scheduler.startTimer(harvestTimeSeconds); // and only if ingest will accept jserror payloads
53
+ // 0 == off, 1 == on
54
+ this.waitForFlags(['err']).then(_ref => {
55
+ let [errFlag] = _ref;
56
+ if (errFlag) {
57
+ const scheduler = new HarvestScheduler('jserrors', {
58
+ onFinished: function () {
59
+ return _this.onHarvestFinished(...arguments);
60
+ }
61
+ }, this);
62
+ scheduler.harvest.on('jserrors', function () {
63
+ return _this.onHarvestStarted(...arguments);
64
+ });
65
+ scheduler.startTimer(harvestTimeSeconds);
66
+ this.drain();
67
+ } else {
68
+ this.blocked = true; // if rum response determines that customer lacks entitlements for spa endpoint, this feature shouldn't harvest
69
+ deregisterDrain(this.agentIdentifier, this.featureName);
70
+ }
63
71
  });
64
-
65
- // If RUM-call's response determines that customer lacks entitlements for the /jserror ingest endpoint, don't harvest at all.
66
- register('block-err', () => {
67
- this.blocked = true;
68
- scheduler.stopTimer(true);
69
- }, this.featureName, this.ee);
70
- this.drain();
71
72
  }
72
73
  onHarvestStarted(options) {
73
74
  // this gets rid of dependency in AJAX module
@@ -188,81 +189,92 @@ export class Aggregate extends AggregateBase {
188
189
  time
189
190
  };
190
191
 
191
- // sr, stn and spa aggregators listen to this event - stn sends the error in its payload,
192
- // and spa annotates the error with interaction info
193
- const msg = [type, bucketHash, params, newMetrics];
194
- handle('errorAgg', msg, undefined, FEATURE_NAMES.sessionTrace, this.ee);
195
- handle('errorAgg', msg, undefined, FEATURE_NAMES.spa, this.ee);
196
- handle('errorAgg', msg, undefined, FEATURE_NAMES.sessionReplay, this.ee);
197
-
192
+ // Trace sends the error in its payload, and both trace & replay simply listens for any error to occur.
193
+ const jsErrorEvent = [type, bucketHash, params, newMetrics, customAttributes];
194
+ handle('errorAgg', jsErrorEvent, undefined, FEATURE_NAMES.sessionTrace, this.ee);
195
+ handle('errorAgg', jsErrorEvent, undefined, FEATURE_NAMES.sessionReplay, this.ee);
198
196
  // still send EE events for other features such as above, but stop this one from aggregating internal data
199
197
  if (this.blocked) return;
200
- var att = getInfo(this.agentIdentifier).jsAttributes;
201
- if (params._interactionId != null) {
202
- // hold on to the error until the interaction finishes
203
- this.errorCache[params._interactionId] = this.errorCache[params._interactionId] || [];
204
- this.errorCache[params._interactionId].push([type, bucketHash, params, newMetrics, att, customAttributes]);
198
+ const softNavInUse = Boolean(getNREUMInitializedAgent(this.agentIdentifier)?.features[FEATURE_NAMES.softNav]);
199
+ // Note: the following are subject to potential race cond wherein if the other feature aren't fully initialized, it'll be treated as there being no associated interaction.
200
+ // They each will also tack on their respective properties to the params object as part of the decision flow.
201
+ if (softNavInUse) handle('jserror', [params, time], undefined, FEATURE_NAMES.softNav, this.ee);else handle('errorAgg', jsErrorEvent, undefined, FEATURE_NAMES.spa, this.ee);
202
+ if (params.browserInteractionId && !params._softNavFinished) {
203
+ // hold onto the error until the in-progress interaction is done, eithered saved or discarded
204
+ this.bufferedErrorsUnderSpa[params.browserInteractionId] ??= [];
205
+ this.bufferedErrorsUnderSpa[params.browserInteractionId].push(jsErrorEvent);
206
+ } else if (params._interactionId != null) {
207
+ // same as above, except tailored for the way old spa does it
208
+ this.bufferedErrorsUnderSpa[params._interactionId] = this.bufferedErrorsUnderSpa[params._interactionId] || [];
209
+ this.bufferedErrorsUnderSpa[params._interactionId].push(jsErrorEvent);
205
210
  } else {
206
- // store custom attributes
207
- var customParams = {};
208
- mapOwn(att, setCustom);
209
- if (customAttributes) {
210
- mapOwn(customAttributes, setCustom);
211
- }
212
- var jsAttributesHash = stringHashCode(stringify(customParams));
213
- var aggregateHash = bucketHash + ':' + jsAttributesHash;
214
- this.aggregator.store(type, aggregateHash, params, newMetrics, customParams);
211
+ // Either there is no interaction (then all these params properties will be undefined) OR there's a related soft navigation that's already completed.
212
+ // The old spa does not look up completed interactions at all, so there's no need to consider it.
213
+ this.#storeJserrorForHarvest(jsErrorEvent, params.browserInteractionId !== undefined, params._softNavAttributes);
215
214
  }
215
+ }
216
+ #storeJserrorForHarvest(errorInfoArr, softNavOccurredFinished) {
217
+ let softNavCustomAttrs = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
218
+ let [type, bucketHash, params, newMetrics, localAttrs] = errorInfoArr;
219
+ const allCustomAttrs = {};
220
+ if (softNavOccurredFinished) {
221
+ Object.entries(softNavCustomAttrs).forEach(_ref2 => {
222
+ let [k, v] = _ref2;
223
+ return setCustom(k, v);
224
+ }); // when an ixn finishes, it'll include stuff in jsAttributes + attrs specific to the ixn
225
+ bucketHash += params.browserInteractionId;
226
+ delete params._softNavAttributes; // cleanup temp properties from synchronous evaluation; this is harmless when async from soft nav (properties DNE)
227
+ delete params._softNavFinished;
228
+ } else {
229
+ // interaction was cancelled -> error should not be associated OR there was no interaction
230
+ Object.entries(getInfo(this.agentIdentifier).jsAttributes).forEach(_ref3 => {
231
+ let [k, v] = _ref3;
232
+ return setCustom(k, v);
233
+ });
234
+ delete params.browserInteractionId;
235
+ }
236
+ if (localAttrs) Object.entries(localAttrs).forEach(_ref4 => {
237
+ let [k, v] = _ref4;
238
+ return setCustom(k, v);
239
+ }); // local custom attrs are applied in either case with the highest precedence
240
+
241
+ const jsAttributesHash = stringHashCode(stringify(allCustomAttrs));
242
+ const aggregateHash = bucketHash + ':' + jsAttributesHash;
243
+ this.aggregator.store(type, aggregateHash, params, newMetrics, allCustomAttrs);
216
244
  function setCustom(key, val) {
217
- customParams[key] = val && typeof val === 'object' ? stringify(val) : val;
245
+ allCustomAttrs[key] = val && typeof val === 'object' ? stringify(val) : val;
218
246
  }
219
247
  }
220
- onInteractionSaved(interaction) {
221
- if (!this.errorCache[interaction.id] || this.blocked) return;
222
- this.errorCache[interaction.id].forEach(item => {
223
- var customParams = {};
224
- var globalCustomParams = item[4];
225
- var localCustomParams = item[5];
226
- mapOwn(globalCustomParams, setCustom);
227
- mapOwn(interaction.root.attrs.custom, setCustom);
228
- mapOwn(localCustomParams, setCustom);
248
+
249
+ // TO-DO: Remove this function when old spa is taken out. #storeJserrorForHarvest handles the work with the softnav feature.
250
+ onInteractionDone(interaction, wasSaved) {
251
+ if (!this.bufferedErrorsUnderSpa[interaction.id] || this.blocked) return;
252
+ this.bufferedErrorsUnderSpa[interaction.id].forEach(item => {
253
+ var allCustomAttrs = {};
254
+ const localCustomAttrs = item[4];
255
+ mapOwn(interaction.root.attrs.custom, setCustom); // tack on custom attrs from the interaction
256
+ mapOwn(localCustomAttrs, setCustom);
229
257
  var params = item[2];
230
- params.browserInteractionId = interaction.root.attrs.id;
231
- delete params._interactionId;
232
- if (params._interactionNodeId) {
233
- params.parentNodeId = params._interactionNodeId.toString();
234
- delete params._interactionNodeId;
235
- }
236
- var hash = item[1] + interaction.root.attrs.id;
237
- var jsAttributesHash = stringHashCode(stringify(customParams));
238
- var aggregateHash = hash + ':' + jsAttributesHash;
239
- this.aggregator.store(item[0], aggregateHash, params, item[3], customParams);
240
- function setCustom(key, val) {
241
- customParams[key] = val && typeof val === 'object' ? stringify(val) : val;
258
+ if (wasSaved) {
259
+ params.browserInteractionId = interaction.root.attrs.id;
260
+ if (params._interactionNodeId) params.parentNodeId = params._interactionNodeId.toString();
242
261
  }
243
- });
244
- delete this.errorCache[interaction.id];
245
- }
246
- onInteractionDiscarded(interaction) {
247
- if (!this.errorCache || !this.errorCache[interaction.id] || this.blocked) return;
248
- this.errorCache[interaction.id].forEach(item => {
249
- var customParams = {};
250
- var globalCustomParams = item[4];
251
- var localCustomParams = item[5];
252
- mapOwn(globalCustomParams, setCustom);
253
- mapOwn(interaction.root.attrs.custom, setCustom);
254
- mapOwn(localCustomParams, setCustom);
255
- var params = item[2];
256
262
  delete params._interactionId;
257
263
  delete params._interactionNodeId;
258
- var hash = item[1];
259
- var jsAttributesHash = stringHashCode(stringify(customParams));
264
+ var hash = wasSaved ? item[1] + interaction.root.attrs.id : item[1];
265
+ var jsAttributesHash = stringHashCode(stringify(allCustomAttrs));
260
266
  var aggregateHash = hash + ':' + jsAttributesHash;
261
- this.aggregator.store(item[0], aggregateHash, item[2], item[3], customParams);
267
+ this.aggregator.store(item[0], aggregateHash, params, item[3], allCustomAttrs);
262
268
  function setCustom(key, val) {
263
- customParams[key] = val && typeof val === 'object' ? stringify(val) : val;
269
+ allCustomAttrs[key] = val && typeof val === 'object' ? stringify(val) : val;
264
270
  }
265
271
  });
266
- delete this.errorCache[interaction.id];
272
+ delete this.bufferedErrorsUnderSpa[interaction.id];
273
+ }
274
+ onSoftNavNotification(interactionId, wasFinished, softNavAttrs) {
275
+ if (this.blocked) return;
276
+ this.bufferedErrorsUnderSpa[interactionId]?.forEach(jsErrorEvent => this.#storeJserrorForHarvest(jsErrorEvent, wasFinished, softNavAttrs) // this should not modify the re-used softNavAttrs contents
277
+ );
278
+ delete this.bufferedErrorsUnderSpa[interactionId]; // wipe the list of jserrors so they aren't duplicated by another call to the same id
267
279
  }
268
280
  }
@@ -9,35 +9,34 @@ import { onDOMContentLoaded } from '../../../common/window/load';
9
9
  import { windowAddEventListener } from '../../../common/event-listener/event-listener-opts';
10
10
  import { isBrowserScope, isWorkerScope } from '../../../common/constants/runtime';
11
11
  import { AggregateBase } from '../../utils/aggregate-base';
12
+ import { deregisterDrain } from '../../../common/drain/drain';
12
13
  export class Aggregate extends AggregateBase {
13
14
  static featureName = FEATURE_NAME;
14
15
  constructor(agentIdentifier, aggregator) {
15
16
  super(agentIdentifier, aggregator, FEATURE_NAME);
16
- let scheduler;
17
-
18
- // If RUM-call's response determines that customer lacks entitlements for the /jserror ingest endpoint, don't harvest at all.
19
- registerHandler('block-err', () => {
20
- this.blocked = true;
21
- if (scheduler) scheduler.aborted = true; // RUM response may or may not have happened already before scheduler initialization below
22
- }, this.featureName, this.ee);
17
+ this.waitForFlags(['err']).then(_ref => {
18
+ let [errFlag] = _ref;
19
+ if (errFlag) {
20
+ // *cli, Mar 23 - Per NR-94597, this feature should only harvest ONCE at the (potential) EoL time of the page.
21
+ const scheduler = new HarvestScheduler('jserrors', {
22
+ onUnload: () => this.unload()
23
+ }, this);
24
+ // this is needed to ensure EoL is "on" and sent
25
+ scheduler.harvest.on('jserrors', () => ({
26
+ body: this.aggregator.take(['cm', 'sm'])
27
+ }));
28
+ this.drain();
29
+ } else {
30
+ this.blocked = true; // if rum response determines that customer lacks entitlements for spa endpoint, this feature shouldn't harvest
31
+ deregisterDrain(this.agentIdentifier, this.featureName);
32
+ }
33
+ });
23
34
 
24
35
  // Allow features external to the metrics feature to capture SMs and CMs through the event emitter
25
36
  registerHandler(SUPPORTABILITY_METRIC_CHANNEL, this.storeSupportabilityMetrics.bind(this), this.featureName, this.ee);
26
37
  registerHandler(CUSTOM_METRIC_CHANNEL, this.storeEventMetrics.bind(this), this.featureName, this.ee);
27
38
  this.singleChecks(); // checks that are run only one time, at script load
28
39
  this.eachSessionChecks(); // the start of every time user engages with page
29
-
30
- this.ee.on("drain-".concat(this.featureName), () => {
31
- // *cli, Mar 23 - Per NR-94597, this feature should only harvest ONCE at the (potential) EoL time of the page.
32
- scheduler = new HarvestScheduler('jserrors', {
33
- onUnload: () => this.unload()
34
- }, this);
35
- scheduler.harvest.on('jserrors', () => ({
36
- body: this.aggregator.take(['cm', 'sm'])
37
- }));
38
- }); // this is needed to ensure EoL is "on" and sent
39
-
40
- this.drain();
41
40
  }
42
41
  storeSupportabilityMetrics(name, value) {
43
42
  if (this.blocked) return;
@@ -61,6 +60,11 @@ export class Aggregate extends AggregateBase {
61
60
  distMethod,
62
61
  loaderType
63
62
  } = getRuntime(this.agentIdentifier);
63
+ const {
64
+ proxy,
65
+ privacy,
66
+ page_view_timing
67
+ } = getConfiguration(this.agentIdentifier);
64
68
  if (loaderType) this.storeSupportabilityMetrics("Generic/LoaderType/".concat(loaderType, "/Detected"));
65
69
  if (distMethod) this.storeSupportabilityMetrics("Generic/DistMethod/".concat(distMethod, "/Detected"));
66
70
  if (isBrowserScope) {
@@ -76,6 +80,8 @@ export class Aggregate extends AggregateBase {
76
80
  this.storeSupportabilityMetrics('Framework/' + framework + '/Detected');
77
81
  });
78
82
  });
83
+ if (!privacy.cookies_enabled) this.storeSupportabilityMetrics('Config/SessionTracking/Disabled');
84
+ if (page_view_timing.long_task) this.storeSupportabilityMetrics('Config/LongTask/Enabled');
79
85
  } else if (isWorkerScope) {
80
86
  this.storeSupportabilityMetrics('Generic/Runtime/Worker/Detected');
81
87
  } else {
@@ -94,13 +100,8 @@ export class Aggregate extends AggregateBase {
94
100
  if (rules.length > 0 && !validateRules(rules)) this.storeSupportabilityMetrics('Generic/Obfuscate/Invalid');
95
101
 
96
102
  // Check if proxy for either chunks or beacon is being used
97
- const {
98
- proxy,
99
- privacy
100
- } = getConfiguration(this.agentIdentifier);
101
103
  if (proxy.assets) this.storeSupportabilityMetrics('Config/AssetsUrl/Changed');
102
104
  if (proxy.beacon) this.storeSupportabilityMetrics('Config/BeaconUrl/Changed');
103
- if (!(isBrowserScope && privacy.cookies_enabled)) this.storeSupportabilityMetrics('Config/SessionTracking/Disabled');
104
105
  }
105
106
  eachSessionChecks() {
106
107
  if (!isBrowserScope) return;
@@ -12,6 +12,7 @@ import { getConfigurationValue, getInfo, getRuntime } from '../../../common/conf
12
12
  import { FEATURE_NAME } from '../constants';
13
13
  import { isBrowserScope } from '../../../common/constants/runtime';
14
14
  import { AggregateBase } from '../../utils/aggregate-base';
15
+ import { deregisterDrain } from '../../../common/drain/drain';
15
16
  export class Aggregate extends AggregateBase {
16
17
  static featureName = FEATURE_NAME;
17
18
  constructor(agentIdentifier, aggregator) {
@@ -31,8 +32,8 @@ export class Aggregate extends AggregateBase {
31
32
  return _this.addPageAction(...arguments);
32
33
  }, this.featureName, this.ee);
33
34
  this.waitForFlags(['ins']).then(_ref => {
34
- let [enabled] = _ref;
35
- if (enabled) {
35
+ let [insFlag] = _ref;
36
+ if (insFlag) {
36
37
  const scheduler = new HarvestScheduler('ins', {
37
38
  onFinished: function () {
38
39
  return _this.onHarvestFinished(...arguments);
@@ -42,11 +43,12 @@ export class Aggregate extends AggregateBase {
42
43
  return _this.onHarvestStarted(...arguments);
43
44
  });
44
45
  scheduler.startTimer(this.harvestTimeSeconds, 0);
46
+ this.drain();
45
47
  } else {
46
- this.blocked = true;
48
+ this.blocked = true; // if rum response determines that customer lacks entitlements for ins endpoint, this feature shouldn't harvest
49
+ deregisterDrain(this.agentIdentifier, this.featureName);
47
50
  }
48
51
  });
49
- this.drain();
50
52
  }
51
53
  onHarvestStarted(options) {
52
54
  const {
@@ -11,6 +11,10 @@ import { AggregateBase } from '../../utils/aggregate-base';
11
11
  import { firstContentfulPaint } from '../../../common/vitals/first-contentful-paint';
12
12
  import { firstPaint } from '../../../common/vitals/first-paint';
13
13
  import { timeToFirstByte } from '../../../common/vitals/time-to-first-byte';
14
+ import { drain } from '../../../common/drain/drain';
15
+ import { FEATURE_NAMES } from '../../../loaders/features/features';
16
+ import { handle } from '../../../common/event-emitter/handle';
17
+ import { SUPPORTABILITY_METRIC_CHANNEL } from '../../metrics/constants';
14
18
  export class Aggregate extends AggregateBase {
15
19
  static featureName = CONSTANTS.FEATURE_NAME;
16
20
  constructor(agentIdentifier, aggregator) {
@@ -117,7 +121,9 @@ export class Aggregate extends AggregateBase {
117
121
  cbFinished: _ref3 => {
118
122
  let {
119
123
  status,
120
- responseText
124
+ responseText,
125
+ xhr,
126
+ fullUrl
121
127
  } = _ref3;
122
128
  if (status >= 400 || status === 0) {
123
129
  // Adding retry logic for the rum call will be a separate change
@@ -125,11 +131,25 @@ export class Aggregate extends AggregateBase {
125
131
  return;
126
132
  }
127
133
  try {
128
- activateFeatures(JSON.parse(responseText), this.agentIdentifier);
134
+ this.timeKeeper.processRumRequest(xhr, fullUrl);
135
+ } catch (error) {
136
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['PVE/NRTime/Calculation/Failed'], undefined, FEATURE_NAMES.metrics, this.ee);
137
+ drain(this.agentIdentifier, FEATURE_NAMES.metrics, true);
138
+ this.ee.abort();
139
+ warn('Could not calculate New Relic server time. Agent shutting down.');
140
+ return;
141
+ }
142
+ try {
143
+ const {
144
+ app,
145
+ ...flags
146
+ } = JSON.parse(responseText);
147
+ agentRuntime.appMetadata = app;
148
+ activateFeatures(flags, this.agentIdentifier);
129
149
  this.drain();
130
150
  } catch (err) {
131
151
  this.ee.abort();
132
- warn('RUM call failed. Agent shutting down.');
152
+ warn('RUM call failed. Agent shutting down.', err);
133
153
  }
134
154
  }
135
155
  });
@@ -25,6 +25,7 @@ export function getActivatedFeaturesFlags(agentId) {
25
25
  case FEATURE_NAMES.sessionTrace:
26
26
  flagArr.push('stn');
27
27
  break;
28
+ case FEATURE_NAMES.softNav:
28
29
  case FEATURE_NAMES.spa:
29
30
  flagArr.push('spa');
30
31
  break;
@@ -37,17 +37,6 @@ export class Aggregate extends AggregateBase {
37
37
  this.timings = [];
38
38
  this.timingsSent = [];
39
39
  this.curSessEndRecorded = false;
40
- firstPaint.subscribe(this.#handleVitalMetric);
41
- firstContentfulPaint.subscribe(this.#handleVitalMetric);
42
- firstInputDelay.subscribe(this.#handleVitalMetric);
43
- largestContentfulPaint.subscribe(this.#handleVitalMetric);
44
- interactionToNextPaint.subscribe(this.#handleVitalMetric);
45
- timeToFirstByte.subscribe(_ref2 => {
46
- let {
47
- entries
48
- } = _ref2;
49
- this.addTiming('load', Math.round(entries[0].loadEventEnd));
50
- });
51
40
  if (getConfigurationValue(this.agentIdentifier, 'page_view_timing.long_task') === true) longTask.subscribe(this.#handleVitalMetric);
52
41
 
53
42
  /* It's important that CWV api, like "onLCP", is called before this scheduler is initialized. The reason is because they listen to the same
@@ -57,9 +46,19 @@ export class Aggregate extends AggregateBase {
57
46
  registerHandler('winPagehide', msTimestamp => this.recordPageUnload(msTimestamp), this.featureName, this.ee);
58
47
  const initialHarvestSeconds = getConfigurationValue(this.agentIdentifier, 'page_view_timing.initialHarvestSeconds') || 10;
59
48
  const harvestTimeSeconds = getConfigurationValue(this.agentIdentifier, 'page_view_timing.harvestTimeSeconds') || 30;
60
- // send initial data sooner, then start regular
61
- this.ee.on("drain-".concat(this.featureName), () => {
62
- this.scheduler = new HarvestScheduler('events', {
49
+ this.waitForFlags([]).then(() => {
50
+ firstPaint.subscribe(this.#handleVitalMetric);
51
+ firstContentfulPaint.subscribe(this.#handleVitalMetric);
52
+ firstInputDelay.subscribe(this.#handleVitalMetric);
53
+ largestContentfulPaint.subscribe(this.#handleVitalMetric);
54
+ interactionToNextPaint.subscribe(this.#handleVitalMetric);
55
+ timeToFirstByte.subscribe(_ref2 => {
56
+ let {
57
+ entries
58
+ } = _ref2;
59
+ this.addTiming('load', Math.round(entries[0].loadEventEnd));
60
+ });
61
+ const scheduler = new HarvestScheduler('events', {
63
62
  onFinished: function () {
64
63
  return _this.onHarvestFinished(...arguments);
65
64
  },
@@ -67,9 +66,9 @@ export class Aggregate extends AggregateBase {
67
66
  return _this.prepareHarvest(...arguments);
68
67
  }
69
68
  }, this);
70
- this.scheduler.startTimer(harvestTimeSeconds, initialHarvestSeconds);
69
+ scheduler.startTimer(harvestTimeSeconds, initialHarvestSeconds);
70
+ this.drain();
71
71
  });
72
- this.drain();
73
72
  }
74
73
 
75
74
  /**