@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
@@ -17,7 +17,8 @@ import { Instrument as InstrumentErrors } from '../features/jserrors/instrument'
17
17
  import { Instrument as InstrumentXhr } from '../features/ajax/instrument'
18
18
  import { Instrument as InstrumentSessionTrace } from '../features/session_trace/instrument'
19
19
  import { Instrument as InstrumentSessionReplay } from '../features/session_replay/instrument'
20
- import { Instrument as InstrumentSpa } from '../features/spa/instrument'
20
+ // import { Instrument as InstrumentSpa } from '../features/spa/instrument'
21
+ import { Instrument as InstrumentSoftNav } from '../features/soft_navigations/instrument'
21
22
  import { Instrument as InstrumentPageAction } from '../features/page_action/instrument'
22
23
 
23
24
  new Agent({
@@ -30,7 +31,8 @@ new Agent({
30
31
  InstrumentMetrics,
31
32
  InstrumentPageAction,
32
33
  InstrumentErrors,
33
- InstrumentSpa
34
+ // InstrumentSpa,
35
+ InstrumentSoftNav
34
36
  ],
35
37
  loaderType: 'experimental'
36
38
  })
@@ -22,3 +22,4 @@ import 'core-js/stable/object/get-own-property-descriptors'
22
22
  import 'core-js/stable/url'
23
23
  import 'core-js/stable/url-search-params'
24
24
  import 'core-js/stable/string/starts-with'
25
+ import 'core-js/stable/number/is-nan'
package/src/cdn/spa.js CHANGED
@@ -11,6 +11,7 @@ import { Instrument as InstrumentErrors } from '../features/jserrors/instrument'
11
11
  import { Instrument as InstrumentXhr } from '../features/ajax/instrument'
12
12
  import { Instrument as InstrumentSessionTrace } from '../features/session_trace/instrument'
13
13
  import { Instrument as InstrumentSessionReplay } from '../features/session_replay/instrument'
14
+ import { Instrument as InstrumentSoftNav } from '../features/soft_navigations/instrument'
14
15
  import { Instrument as InstrumentSpa } from '../features/spa/instrument'
15
16
  import { Instrument as InstrumentPageAction } from '../features/page_action/instrument'
16
17
 
@@ -24,7 +25,8 @@ new Agent({
24
25
  InstrumentMetrics,
25
26
  InstrumentPageAction,
26
27
  InstrumentErrors,
27
- InstrumentSpa
28
+ InstrumentSoftNav,
29
+ InstrumentSpa // either the softnav or the old spa will be used (not both), but we still need to pack both to avoid dynamic import for instrument files
28
30
  ],
29
31
  loaderType: 'spa'
30
32
  })
@@ -82,7 +82,8 @@ export class Aggregator extends SharedContext {
82
82
  var hasData = false
83
83
  for (var i = 0; i < types.length; i++) {
84
84
  type = types[i]
85
- results[type] = toArray(this.aggregatedData[type])
85
+ results[type] = Object.values(this.aggregatedData[type] || {})
86
+
86
87
  if (results[type].length) hasData = true
87
88
  delete this.aggregatedData[type]
88
89
  }
@@ -159,13 +160,3 @@ function createMetricObject (value) {
159
160
  c: 1
160
161
  }
161
162
  }
162
-
163
- function toArray (obj) {
164
- if (typeof obj !== 'object') return []
165
-
166
- return mapOwn(obj, getValue)
167
- }
168
-
169
- function getValue (key, value) {
170
- return value
171
- }
@@ -64,6 +64,7 @@ const model = () => {
64
64
  autoStart: true,
65
65
  enabled: false,
66
66
  harvestTimeSeconds: 60,
67
+ preload: false, // if true, enables the agent to load rrweb immediately instead of waiting to do so after the window.load event
67
68
  sampling_rate: 10, // float from 0 - 100
68
69
  error_sampling_rate: 100, // float from 0 - 100
69
70
  collect_fonts: false, // serialize fonts for collection without public asset url, this is currently broken in RRWeb -- https://github.com/rrweb-io/rrweb/issues/1304. When fixed, revisit with test cases
@@ -100,7 +101,8 @@ const model = () => {
100
101
  else warn('An invalid session_replay.mask_input_option was provided and will not be used', val)
101
102
  }
102
103
  },
103
- spa: { enabled: true, harvestTimeSeconds: 10, autoStart: true }
104
+ spa: { enabled: true, harvestTimeSeconds: 10, autoStart: true },
105
+ soft_navigations: { enabled: true, harvestTimeSeconds: 10, autoStart: true }
104
106
  }
105
107
  }
106
108
 
@@ -18,10 +18,13 @@ const model = {
18
18
  origin: '' + globalScope.location,
19
19
  ptid: undefined,
20
20
  releaseIds: {},
21
+ /** Agent-specific metadata found in the RUM call response. ex. entityGuid */
22
+ appMetadata: {},
21
23
  session: undefined,
22
24
  xhrWrappable: typeof globalScope.XMLHttpRequest?.prototype?.addEventListener === 'function',
23
25
  version: VERSION,
24
- denyList: undefined
26
+ denyList: undefined,
27
+ harvestCount: 0
25
28
  }
26
29
 
27
30
  const _cache = {}
@@ -26,13 +26,24 @@ export function registerDrain (agentIdentifier, group) {
26
26
  if (!registry[agentIdentifier].get(group)) registry[agentIdentifier].set(group, item)
27
27
  }
28
28
 
29
+ /**
30
+ * Removes an item from the registry and immediately re-checks if the registry is ready to "drain all"
31
+ * @param {*} agentIdentifier - A 16 character string uniquely identifying the agent.
32
+ * @param {*} group - The named "bucket" to be removed from the registry
33
+ */
34
+ export function deregisterDrain (agentIdentifier, group) {
35
+ curateRegistry(agentIdentifier)
36
+ if (registry[agentIdentifier].get(group)) registry[agentIdentifier].delete(group)
37
+ if (registry[agentIdentifier].size) checkCanDrainAll(agentIdentifier)
38
+ }
39
+
29
40
  /**
30
41
  * Registers the specified agent with the centralized event buffer registry if it is not already registered.
31
42
  * Agents without an identifier (as in the case of some tests) will be excluded from the registry.
32
43
  * @param {string} agentIdentifier - A 16 character string uniquely identifying an agent.
33
44
  */
34
45
  function curateRegistry (agentIdentifier) {
35
- if (!agentIdentifier) return
46
+ if (!agentIdentifier) throw new Error('agentIdentifier required')
36
47
  if (!registry[agentIdentifier]) registry[agentIdentifier] = new Map()
37
48
  }
38
49
 
@@ -48,54 +59,56 @@ export function drain (agentIdentifier = '', featureName = 'feature', force = fa
48
59
  // If the feature for the specified agent is not in the registry, that means the instrument file was bypassed.
49
60
  // This could happen in tests, or loaders that directly import the aggregator. In these cases it is safe to
50
61
  // drain the feature group immediately rather than waiting to drain all at once.
51
- if (!agentIdentifier || !registry[agentIdentifier].get(featureName) || force) return drainGroup(featureName)
62
+ if (!agentIdentifier || !registry[agentIdentifier].get(featureName) || force) return drainGroup(agentIdentifier, featureName)
52
63
 
53
64
  // When `drain` is called, this feature is ready to drain (staged).
54
65
  registry[agentIdentifier].get(featureName).staged = true
55
66
 
56
- // Only when the event-groups for all features are ready to drain (staged) do we execute the drain. This has the effect
67
+ checkCanDrainAll(agentIdentifier)
68
+ }
69
+
70
+ /** Checks all items in the registry to see if they have been "staged". If ALL items are staged, it will drain all registry items (drainGroup). It not, nothing will happen */
71
+ function checkCanDrainAll (agentIdentifier) {
72
+ // Only when the event-groups for all features are ready to drain (staged) do we execute the drain. This has the effect
57
73
  // that the last feature to call drain triggers drain for all features.
58
74
  const items = [...registry[agentIdentifier]]
59
75
  if (items.every(([key, values]) => values.staged)) {
60
76
  items.sort((a, b) => a[1].priority - b[1].priority)
61
77
  items.forEach(([group]) => {
62
78
  registry[agentIdentifier].delete(group)
63
- drainGroup(group)
79
+ drainGroup(agentIdentifier, group)
64
80
  })
65
81
  }
82
+ }
66
83
 
67
- /**
84
+ /**
68
85
  * Drains all the buffered (backlog) events for a particular feature's event-group by emitting each event to each of
69
86
  * the subscribed handlers for the group.
70
87
  * @param {*} group - The name of a particular feature's event "bucket".
71
88
  */
72
- function drainGroup (group) {
73
- const baseEE = agentIdentifier ? ee.get(agentIdentifier) : ee
74
- const handlers = defaultRegister.handlers
75
- if (!baseEE.backlog || !handlers) return
89
+ function drainGroup (agentIdentifier, group) {
90
+ const baseEE = agentIdentifier ? ee.get(agentIdentifier) : ee
91
+ const handlers = defaultRegister.handlers // other storage in registerHandler
92
+ if (!baseEE.backlog || !handlers) return
76
93
 
77
- var bufferedEventsInGroup = baseEE.backlog[group]
78
- var groupHandlers = handlers[group]
79
- if (groupHandlers) {
80
- // We don't cache the length of the buffer while looping because events might still be added while processing.
81
- for (var i = 0; bufferedEventsInGroup && i < bufferedEventsInGroup.length; ++i) { // eslint-disable-line no-unmodified-loop-condition
82
- emitEvent(bufferedEventsInGroup[i], groupHandlers)
83
- }
84
-
85
- mapOwn(groupHandlers, function (eventType, handlerRegistrationList) {
86
- mapOwn(handlerRegistrationList, function (i, registration) {
87
- // registration is an array of: [targetEE, eventHandler]
88
- registration[0].on(eventType, registration[1])
89
- })
90
- })
94
+ var bufferedEventsInGroup = baseEE.backlog[group]
95
+ var groupHandlers = handlers[group] // each group in the registerHandler storage
96
+ if (groupHandlers) {
97
+ // We don't cache the length of the buffer while looping because events might still be added while processing.
98
+ for (var i = 0; bufferedEventsInGroup && i < bufferedEventsInGroup.length; ++i) { // eslint-disable-line no-unmodified-loop-condition
99
+ emitEvent(bufferedEventsInGroup[i], groupHandlers)
91
100
  }
92
101
 
93
- delete handlers[group]
94
-
95
- // Keep the feature's event-group as a property of the event emitter so we know it was already created and drained.
96
- baseEE.backlog[group] = null
97
- baseEE.emit('drain-' + group, [])
102
+ mapOwn(groupHandlers, function (eventType, handlerRegistrationList) {
103
+ mapOwn(handlerRegistrationList, function (i, registration) {
104
+ // registration is an array of: [targetEE, eventHandler]
105
+ registration[0].on(eventType, registration[1])
106
+ })
107
+ })
98
108
  }
109
+ if (!baseEE.isolatedBacklog) delete handlers[group]
110
+ baseEE.backlog[group] = null
111
+ baseEE.emit('drain-' + group, [])
99
112
  }
100
113
 
101
114
  /**
@@ -49,13 +49,32 @@ function ee (old, debugId) {
49
49
  context,
50
50
  buffer: bufferEventsByGroup,
51
51
  abort,
52
- aborted: false,
53
52
  isBuffering,
54
53
  debugId,
55
- backlog: isolatedBacklog ? {} : old && typeof old.backlog === 'object' ? old.backlog : {}
54
+ backlog: isolatedBacklog ? {} : old && typeof old.backlog === 'object' ? old.backlog : {},
55
+ isolatedBacklog
56
+ }
56
57
 
58
+ function abort () {
59
+ emitter._aborted = true
60
+ Object.keys(emitter.backlog).forEach(key => {
61
+ delete emitter.backlog[key]
62
+ })
57
63
  }
58
64
 
65
+ Object.defineProperty(emitter, 'aborted', {
66
+ get: () => {
67
+ let aborted = emitter._aborted || false
68
+
69
+ if (aborted) return aborted
70
+ else if (old) {
71
+ aborted = old.aborted
72
+ }
73
+
74
+ return aborted
75
+ }
76
+ })
77
+
59
78
  return emitter
60
79
 
61
80
  function context (contextOrStore) {
@@ -141,14 +160,3 @@ function ee (old, debugId) {
141
160
  return emitter.backlog
142
161
  }
143
162
  }
144
-
145
- function abort () {
146
- globalInstance.aborted = true
147
- // The global backlog can be referenced directly by other emitters,
148
- // so we need to delete its contents as opposed to replacing it.
149
- // Otherwise, these references to the old backlog would still exist
150
- // and the keys will not be garbage collected.
151
- Object.keys(globalInstance.backlog).forEach(key => {
152
- delete globalInstance.backlog[key]
153
- })
154
- }
@@ -145,7 +145,7 @@ export class Harvest extends SharedContext {
145
145
  result.addEventListener('loadend', function () {
146
146
  // `this` refers to the XHR object in this scope, do not change this to a fat arrow
147
147
  // status 0 refers to a local error, such as CORS or network failure, or a blocked request by the browser (e.g. adblocker)
148
- const cbResult = { sent: this.status !== 0, status: this.status }
148
+ const cbResult = { sent: this.status !== 0, status: this.status, xhr: this, fullUrl }
149
149
  if (this.status === 429) {
150
150
  cbResult.retry = true
151
151
  cbResult.delay = harvestScope.tooManyRequestsDelay
@@ -160,6 +160,9 @@ export class Harvest extends SharedContext {
160
160
  }, eventListenerOpts(false))
161
161
  }
162
162
 
163
+ const runtime = getRuntime(this.sharedContext.agentIdentifier)
164
+ runtime.harvestCount++
165
+
163
166
  return result
164
167
  }
165
168
 
@@ -1,4 +1,5 @@
1
1
  export const PREFIX = 'NRBA'
2
+ export const DEFAULT_KEY = 'SESSION'
2
3
  export const DEFAULT_EXPIRES_MS = 14400000
3
4
  export const DEFAULT_INACTIVE_MS = 1800000
4
5
 
@@ -25,7 +25,7 @@ var RESPONSE = 'response'
25
25
  var LOAD_EVENT = 'loadEvent'
26
26
  var DOM_CONTENT_LOAD_EVENT = 'domContentLoadedEvent'
27
27
 
28
- export var navTimingValues = []
28
+ export const navTimingValues = []
29
29
 
30
30
  function getPntType (type) {
31
31
  if (typeof type === 'number') return type
@@ -72,6 +72,10 @@ export function addPN (pn, v) {
72
72
  return v
73
73
  }
74
74
 
75
+ /**
76
+ * By side effect, this modifies 'obj' with a mapping of the 'prop' provided to a 'value', and invalid values are not added.
77
+ * On the other hand, the local navTimingValues array gets the value appended if valid and 'undefined' appended if invalid, regardless.
78
+ */
75
79
  function handleValue (value, obj, prop, isOldApi) {
76
80
  /*
77
81
  For L2 Timing API, the value will already be a relative-to-previous-document DOMHighResTimeStamp.
@@ -85,6 +89,6 @@ function handleValue (value, obj, prop, isOldApi) {
85
89
  }
86
90
  value = Math.round(value)
87
91
  obj[prop] = value
88
- }
89
- navTimingValues.push(value)
92
+ navTimingValues.push(value)
93
+ } else navTimingValues.push(undefined)
90
94
  }
@@ -5,5 +5,5 @@
5
5
 
6
6
  // This is our own layer around performance.now. It's not strictly necessary, but we keep it in case of future mod-ing of the value for refactor purpose.
7
7
  export function now () {
8
- return Math.round(performance.now())
8
+ return Math.floor(performance.now())
9
9
  }
@@ -0,0 +1,96 @@
1
+ import { gosNREUM } from '../window/nreum'
2
+ import { globalScope } from '../constants/runtime'
3
+ import { getRuntime } from '../config/config'
4
+
5
+ /**
6
+ * Class used to adjust the timestamp of harvested data to New Relic server time. This
7
+ * is done by tracking the performance timings of the RUM call and applying a calculation
8
+ * to the harvested data event offset time.
9
+ */
10
+ export class TimeKeeper {
11
+ #agent
12
+
13
+ /**
14
+ * Represents the browser origin time corrected to NR server time.
15
+ * @type {number}
16
+ */
17
+ #correctedOriginTime
18
+
19
+ /**
20
+ * Represents the difference in milliseconds between the calculated NR server time and
21
+ * the local time.
22
+ * @type {number}
23
+ */
24
+ #localTimeDiff
25
+
26
+ constructor (agent) {
27
+ this.#agent = agent
28
+ }
29
+
30
+ static getTimeKeeperByAgentIdentifier (agentIdentifier) {
31
+ const nr = gosNREUM()
32
+ return Object.keys(nr?.initializedAgents || {}).indexOf(agentIdentifier) > -1
33
+ ? nr.initializedAgents[agentIdentifier].timeKeeper
34
+ : undefined
35
+ }
36
+
37
+ get correctedPageOriginTime () {
38
+ return this.#correctedOriginTime
39
+ }
40
+
41
+ /**
42
+ * Process a rum request to calculate NR server time.
43
+ * @param rumRequest {XMLHttpRequest} The xhr for the rum request
44
+ * @param rumRequestUrl {string} The full url of the rum request
45
+ */
46
+ processRumRequest (rumRequest, rumRequestUrl) {
47
+ const responseDateHeader = rumRequest.getResponseHeader('Date')
48
+ if (!responseDateHeader) {
49
+ throw new Error('Missing date header on rum response.')
50
+ }
51
+
52
+ const resourceEntries = globalScope.performance.getEntriesByName(rumRequestUrl, 'resource')
53
+ if (!Array.isArray((resourceEntries)) || resourceEntries.length === 0) {
54
+ throw new Error('Missing rum request performance entry.')
55
+ }
56
+
57
+ let medianRumOffset = 0
58
+ let serverOffset = 0
59
+ if (typeof resourceEntries[0].responseStart === 'number' && resourceEntries[0].responseStart !== 0) {
60
+ // Cors is enabled and we can make a more accurate calculation of NR server time
61
+ medianRumOffset = (resourceEntries[0].responseStart - resourceEntries[0].requestStart) / 2
62
+ serverOffset = Math.floor(resourceEntries[0].requestStart + medianRumOffset)
63
+ } else {
64
+ // Cors is disabled or erred, we need to use a less accurate calculation
65
+ medianRumOffset = (resourceEntries[0].responseEnd - resourceEntries[0].fetchStart) / 2
66
+ serverOffset = Math.floor(resourceEntries[0].fetchStart + medianRumOffset)
67
+ }
68
+
69
+ // Corrected page origin time
70
+ this.#correctedOriginTime = Math.floor(Date.parse(responseDateHeader) - serverOffset)
71
+ this.#localTimeDiff = getRuntime(this.#agent.agentIdentifier).offset - this.#correctedOriginTime
72
+
73
+ if (Number.isNaN(this.#correctedOriginTime)) {
74
+ throw new Error('Date header invalid format.')
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Converts a page origin relative time to an absolute timestamp
80
+ * corrected to NR server time.
81
+ * @param relativeTime {number} The relative time of the event in milliseconds
82
+ * @returns {number} The correct timestamp as a unix/epoch timestamp value
83
+ */
84
+ convertRelativeTimestamp (relativeTime) {
85
+ return this.#correctedOriginTime + relativeTime
86
+ }
87
+
88
+ /**
89
+ * Corrects an event timestamp to NR server time.
90
+ * @param timestamp {number} The unix/epoch timestamp of the event with milliseconds
91
+ * @return {number} Corrected unix/epoch timestamp
92
+ */
93
+ correctAbsoluteTimestamp (timestamp) {
94
+ return Math.floor(timestamp - this.#localTimeDiff)
95
+ }
96
+ }
@@ -3,49 +3,31 @@
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
  import { ee } from '../event-emitter/contextual-ee'
6
- import { handle } from '../event-emitter/handle'
7
- import { FEATURE_NAMES } from '../../loaders/features/features'
8
6
  import { dispatchGlobalEvent } from '../dispatch/global-event'
9
7
 
10
- const bucketMap = {
11
- stn: [FEATURE_NAMES.sessionTrace],
12
- err: [FEATURE_NAMES.jserrors, FEATURE_NAMES.metrics],
13
- ins: [FEATURE_NAMES.pageAction],
14
- spa: [FEATURE_NAMES.spa],
15
- sr: [FEATURE_NAMES.sessionReplay, FEATURE_NAMES.sessionTrace]
16
- }
17
-
18
8
  const sentIds = new Set()
19
9
 
20
- /** Note that this function only processes each unique flag ONCE, with the first occurrence of each flag and numeric value determining its switch on/off setting. */
10
+ /** A map of feature flags and their values as provided by the rum call -- scoped by agent ID */
11
+ export const activatedFeatures = {}
12
+
13
+ /**
14
+ * Sets the activatedFeatures object, dispatches the global loaded event,
15
+ * and emits the rumresp flag to features
16
+ * @param {{[key:string]:number}} flags key-val pair of flag names and numeric
17
+ * @param {string} agentIdentifier agent instance identifier
18
+ * @returns {void}
19
+ */
21
20
  export function activateFeatures (flags, agentIdentifier) {
22
21
  const sharedEE = ee.get(agentIdentifier)
22
+ activatedFeatures[agentIdentifier] ??= {}
23
23
  if (!(flags && typeof flags === 'object')) return
24
24
  if (sentIds.has(agentIdentifier)) return
25
25
 
26
- Object.entries(flags).forEach(([flag, num]) => {
27
- if (bucketMap[flag]) {
28
- bucketMap[flag].forEach(feat => {
29
- if (!num) handle('block-' + flag, [], undefined, feat, sharedEE)
30
- else handle('feat-' + flag, [], undefined, feat, sharedEE)
31
- handle('rumresp-' + flag, [Boolean(num)], undefined, feat, sharedEE) // this is a duplicate of feat-/block- but makes awaiting for 1 event easier than 2
32
- })
33
- } else if (num) handle('feat-' + flag, [], undefined, undefined, sharedEE) // not sure what other flags are overlooked, but there's a test for ones not in the map --
34
- activatedFeatures[flag] = Boolean(num)
35
- })
26
+ sharedEE.emit('rumresp', [flags])
27
+ activatedFeatures[agentIdentifier] = flags
36
28
 
37
- // Let the features waiting on their respective flags know that RUM response was received and that any missing flags are interpreted as bad entitlement / "off".
38
- // Hence, those features will not be hanging forever if their flags aren't included in the response.
39
- Object.keys(bucketMap).forEach(flag => {
40
- if (activatedFeatures[flag] === undefined) {
41
- bucketMap[flag]?.forEach(feat => handle('rumresp-' + flag, [false], undefined, feat, sharedEE))
42
- activatedFeatures[flag] = false
43
- }
44
- })
45
29
  sentIds.add(agentIdentifier)
46
30
 
47
31
  // let any window level subscribers know that the agent is running
48
32
  dispatchGlobalEvent({ loaded: true })
49
33
  }
50
-
51
- export const activatedFeatures = {}
@@ -8,10 +8,9 @@ import { wrapHistory } from './wrap-history'
8
8
  import { wrapJsonP } from './wrap-jsonp'
9
9
  import { wrapMutation } from './wrap-mutation'
10
10
  import { wrapPromise } from './wrap-promise'
11
- import { wrapRaf } from './wrap-raf'
12
11
  import { wrapTimer } from './wrap-timer'
13
12
  import { wrapXhr } from './wrap-xhr'
14
13
 
15
14
  export {
16
- wrapEvents, wrapFetch, wrapHistory, wrapJsonP, wrapMutation, wrapPromise, wrapRaf, wrapTimer, wrapXhr
15
+ wrapEvents, wrapFetch, wrapHistory, wrapJsonP, wrapMutation, wrapPromise, wrapTimer, wrapXhr
17
16
  }
@@ -2,7 +2,7 @@
2
2
  * Copyright 2020 New Relic Corporation. All rights reserved.
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
- import { registerHandler as register } from '../../../common/event-emitter/register-handler'
5
+ import { registerHandler } from '../../../common/event-emitter/register-handler'
6
6
  import { stringify } from '../../../common/util/stringify'
7
7
  import { nullable, numeric, getAddStringContext, addCustomAttributes } from '../../../common/serialize/bel-serializer'
8
8
  import { handle } from '../../../common/event-emitter/handle'
@@ -14,19 +14,24 @@ import { FEATURE_NAMES } from '../../../loaders/features/features'
14
14
  import { SUPPORTABILITY_METRIC_CHANNEL } from '../../metrics/constants'
15
15
  import { AggregateBase } from '../../utils/aggregate-base'
16
16
  import { parseGQL } from './gql'
17
+ import { getNREUMInitializedAgent } from '../../../common/window/nreum'
17
18
 
18
19
  export class Aggregate extends AggregateBase {
19
20
  static featureName = FEATURE_NAME
20
21
  constructor (agentIdentifier, aggregator) {
21
22
  super(agentIdentifier, aggregator, FEATURE_NAME)
22
23
  const agentInit = getConfiguration(agentIdentifier)
23
- const allAjaxIsEnabled = agentInit.ajax.enabled !== false
24
24
 
25
- register('xhr', storeXhr, this.featureName, this.ee)
26
- if (!allAjaxIsEnabled) {
25
+ registerHandler('xhr', storeXhr, this.featureName, this.ee)
26
+
27
+ this.waitForFlags(([])).then(() => {
28
+ const scheduler = new HarvestScheduler('events', {
29
+ onFinished: onEventsHarvestFinished,
30
+ getPayload: prepareHarvest
31
+ }, this)
32
+ scheduler.startTimer(harvestTimeSeconds)
27
33
  this.drain()
28
- return // feature will only collect timeslice metrics & ajax trace nodes if it's not fully enabled
29
- }
34
+ })
30
35
 
31
36
  const denyList = getRuntime(agentIdentifier).denyList
32
37
  setDenyList(denyList)
@@ -44,29 +49,21 @@ export class Aggregate extends AggregateBase {
44
49
  this.prepareHarvest = prepareHarvest
45
50
  this.getStoredEvents = function () { return { ajaxEvents, spaAjaxEvents } }
46
51
 
47
- ee.on('interactionSaved', (interaction) => {
48
- if (!spaAjaxEvents[interaction.id]) return
49
- // remove from the spaAjaxEvents buffer, and let spa harvest it
50
- delete spaAjaxEvents[interaction.id]
51
- })
52
- ee.on('interactionDiscarded', (interaction) => {
52
+ // --- v Used by old spa feature
53
+ ee.on('interactionDone', (interaction, wasSaved) => {
53
54
  if (!spaAjaxEvents[interaction.id]) return
54
55
 
55
- spaAjaxEvents[interaction.id].forEach(function (item) {
56
- // move it from the spaAjaxEvents buffer to the ajaxEvents buffer for harvesting here
57
- ajaxEvents.push(item)
58
- })
56
+ if (!wasSaved) { // 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
57
+ spaAjaxEvents[interaction.id].forEach(function (item) {
58
+ ajaxEvents.push(item)
59
+ })
60
+ }
59
61
  delete spaAjaxEvents[interaction.id]
60
62
  })
61
-
62
- const scheduler = new HarvestScheduler('events', {
63
- onFinished: onEventsHarvestFinished,
64
- getPayload: prepareHarvest
65
- }, this)
66
-
67
- ee.on(`drain-${this.featureName}`, () => { scheduler.startTimer(harvestTimeSeconds) })
68
-
69
- this.drain()
63
+ // --- ^
64
+ // --- v Used by new soft nav
65
+ registerHandler('returnAjax', event => ajaxEvents.push(event), this.featureName, this.ee)
66
+ // --- ^
70
67
 
71
68
  const beacon = getInfo(agentIdentifier).errorBeacon
72
69
  const proxyBeacon = agentInit.proxy.beacon
@@ -82,17 +79,28 @@ export class Aggregate extends AggregateBase {
82
79
  hash = stringify([params.status, params.host, params.pathname])
83
80
  }
84
81
 
85
- // store as metric
86
- aggregator.store('xhr', hash, params, metrics)
82
+ const shouldCollect = shouldCollectEvent(params)
83
+ const ajaxMetricDenyListEnabled = agentInit.feature_flags?.includes('ajax_metrics_deny_list')
87
84
 
88
- if (!allAjaxIsEnabled) return
85
+ // store as metric
86
+ if (shouldCollect || !ajaxMetricDenyListEnabled) {
87
+ aggregator.store('xhr', hash, params, metrics)
88
+ }
89
89
 
90
- if (!shouldCollectEvent(params)) {
90
+ if (!shouldCollect) {
91
91
  if (params.hostname === beacon || (proxyBeacon && params.hostname === proxyBeacon)) {
92
92
  // This doesn't make a distinction if the same-domain request is going to a different port or path...
93
93
  handle(SUPPORTABILITY_METRIC_CHANNEL, ['Ajax/Events/Excluded/Agent'], undefined, FEATURE_NAMES.metrics, ee)
94
+
95
+ if (ajaxMetricDenyListEnabled) {
96
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['Ajax/Metrics/Excluded/Agent'], undefined, FEATURE_NAMES.metrics, ee)
97
+ }
94
98
  } else {
95
99
  handle(SUPPORTABILITY_METRIC_CHANNEL, ['Ajax/Events/Excluded/App'], undefined, FEATURE_NAMES.metrics, ee)
100
+
101
+ if (ajaxMetricDenyListEnabled) {
102
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['Ajax/Metrics/Excluded/App'], undefined, FEATURE_NAMES.metrics, ee)
103
+ }
96
104
  }
97
105
  return
98
106
  }
@@ -125,12 +133,13 @@ export class Aggregate extends AggregateBase {
125
133
  body: this.body,
126
134
  query: this?.parsedOrigin?.search
127
135
  })
128
-
129
136
  if (event.gql) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Ajax/Events/GraphQL/Bytes-Added', stringify(event.gql).length], undefined, FEATURE_NAMES.metrics, ee)
130
137
 
131
- // if the ajax happened inside an interaction, hold it until the interaction finishes
132
- if (this.spaNode) {
133
- var interactionId = this.spaNode.interaction.id
138
+ const softNavInUse = Boolean(getNREUMInitializedAgent(agentIdentifier)?.features?.[FEATURE_NAMES.softNav])
139
+ if (softNavInUse) { // For newer soft nav (when running), pass the event to it for evaluation -- either part of an interaction or is given back
140
+ handle('ajax', [event], undefined, FEATURE_NAMES.softNav, ee)
141
+ } else if (this.spaNode) { // For old spa (when running), if the ajax happened inside an interaction, hold it until the interaction finishes
142
+ const interactionId = this.spaNode.interaction.id
134
143
  spaAjaxEvents[interactionId] = spaAjaxEvents[interactionId] || []
135
144
  spaAjaxEvents[interactionId].push(event)
136
145
  } else {
@@ -209,6 +218,7 @@ export class Aggregate extends AggregateBase {
209
218
 
210
219
  for (var i = 0; i < events.length; i++) {
211
220
  var event = events[i]
221
+
212
222
  var fields = [
213
223
  numeric(event.startTime),
214
224
  numeric(event.endTime - event.startTime),