@newrelic/browser-agent 1.252.1 → 1.253.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 (203) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +1 -1
  3. package/dist/cjs/cdn/experimental.js +6 -2
  4. package/dist/cjs/cdn/spa.js +5 -3
  5. package/dist/cjs/common/aggregate/aggregator.js +1 -8
  6. package/dist/cjs/common/config/state/init.js +7 -0
  7. package/dist/cjs/common/constants/env.cdn.js +1 -1
  8. package/dist/cjs/common/constants/env.npm.js +1 -1
  9. package/dist/cjs/common/context/observation-context-manager.js +56 -0
  10. package/dist/cjs/common/event-emitter/contextual-ee.js +12 -9
  11. package/dist/cjs/common/session/constants.js +2 -1
  12. package/dist/cjs/common/timing/nav-timing.js +8 -3
  13. package/dist/cjs/common/timing/now.js +1 -1
  14. package/dist/cjs/common/util/feature-flags.js +1 -1
  15. package/dist/cjs/common/wrap/index.js +0 -7
  16. package/dist/cjs/common/wrap/wrap-events.js +2 -2
  17. package/dist/cjs/common/wrap/wrap-fetch.js +2 -1
  18. package/dist/cjs/common/wrap/wrap-function.js +5 -7
  19. package/dist/cjs/common/wrap/wrap-promise.js +2 -1
  20. package/dist/cjs/features/ajax/aggregate/index.js +34 -16
  21. package/dist/cjs/features/jserrors/aggregate/index.js +77 -66
  22. package/dist/cjs/features/page_view_event/aggregate/index.js +1 -1
  23. package/dist/cjs/features/page_view_event/aggregate/initialized-features.js +1 -0
  24. package/dist/cjs/features/session_replay/aggregate/index.js +96 -94
  25. package/dist/cjs/features/session_replay/constants.js +5 -1
  26. package/dist/cjs/features/session_replay/instrument/index.js +24 -8
  27. package/dist/cjs/features/session_replay/shared/utils.js +26 -0
  28. package/dist/cjs/features/soft_navigations/aggregate/ajax-node.js +50 -0
  29. package/dist/cjs/features/soft_navigations/aggregate/bel-node.js +29 -0
  30. package/dist/cjs/features/soft_navigations/aggregate/index.js +263 -0
  31. package/dist/cjs/features/soft_navigations/aggregate/initial-page-load-interaction.js +62 -0
  32. package/dist/cjs/features/soft_navigations/aggregate/interaction.js +146 -0
  33. package/dist/cjs/features/soft_navigations/constants.js +31 -0
  34. package/dist/cjs/features/soft_navigations/index.js +12 -0
  35. package/dist/cjs/features/soft_navigations/instrument/index.js +79 -0
  36. package/dist/cjs/features/spa/aggregate/index.js +4 -4
  37. package/dist/cjs/features/utils/agent-session.js +2 -1
  38. package/dist/cjs/features/utils/instrument-base.js +6 -9
  39. package/dist/cjs/features/utils/lazy-feature-loader.js +2 -0
  40. package/dist/cjs/loaders/agent-base.js +18 -3
  41. package/dist/cjs/loaders/agent.js +15 -18
  42. package/dist/cjs/loaders/api/api-methods.js +2 -1
  43. package/dist/cjs/loaders/api/api.js +14 -11
  44. package/dist/cjs/loaders/configure/configure.js +4 -1
  45. package/dist/cjs/loaders/features/enabled-features.js +1 -1
  46. package/dist/cjs/loaders/features/features.js +3 -1
  47. package/dist/esm/cdn/experimental.js +5 -2
  48. package/dist/esm/cdn/spa.js +3 -1
  49. package/dist/esm/common/aggregate/aggregator.js +1 -8
  50. package/dist/esm/common/config/state/init.js +7 -0
  51. package/dist/esm/common/constants/env.cdn.js +1 -1
  52. package/dist/esm/common/constants/env.npm.js +1 -1
  53. package/dist/esm/common/context/observation-context-manager.js +49 -0
  54. package/dist/esm/common/event-emitter/contextual-ee.js +12 -9
  55. package/dist/esm/common/session/constants.js +1 -0
  56. package/dist/esm/common/timing/nav-timing.js +8 -3
  57. package/dist/esm/common/timing/now.js +1 -1
  58. package/dist/esm/common/util/feature-flags.js +1 -1
  59. package/dist/esm/common/wrap/index.js +1 -2
  60. package/dist/esm/common/wrap/wrap-events.js +3 -3
  61. package/dist/esm/common/wrap/wrap-fetch.js +3 -2
  62. package/dist/esm/common/wrap/wrap-function.js +4 -5
  63. package/dist/esm/common/wrap/wrap-promise.js +3 -2
  64. package/dist/esm/features/ajax/aggregate/index.js +36 -18
  65. package/dist/esm/features/jserrors/aggregate/index.js +77 -66
  66. package/dist/esm/features/page_view_event/aggregate/index.js +1 -1
  67. package/dist/esm/features/page_view_event/aggregate/initialized-features.js +1 -0
  68. package/dist/esm/features/session_replay/aggregate/index.js +97 -95
  69. package/dist/esm/features/session_replay/constants.js +4 -0
  70. package/dist/esm/features/session_replay/instrument/index.js +25 -9
  71. package/dist/esm/features/session_replay/shared/utils.js +17 -0
  72. package/dist/esm/features/soft_navigations/aggregate/ajax-node.js +43 -0
  73. package/dist/esm/features/soft_navigations/aggregate/bel-node.js +22 -0
  74. package/dist/esm/features/soft_navigations/aggregate/index.js +256 -0
  75. package/dist/esm/features/soft_navigations/aggregate/initial-page-load-interaction.js +55 -0
  76. package/dist/esm/features/soft_navigations/aggregate/interaction.js +140 -0
  77. package/dist/esm/features/soft_navigations/constants.js +25 -0
  78. package/dist/esm/features/soft_navigations/index.js +1 -0
  79. package/dist/esm/features/soft_navigations/instrument/index.js +73 -0
  80. package/dist/esm/features/spa/aggregate/index.js +4 -4
  81. package/dist/esm/features/utils/agent-session.js +2 -1
  82. package/dist/esm/features/utils/instrument-base.js +7 -10
  83. package/dist/esm/features/utils/lazy-feature-loader.js +2 -0
  84. package/dist/esm/loaders/agent-base.js +18 -3
  85. package/dist/esm/loaders/agent.js +15 -18
  86. package/dist/esm/loaders/api/api-methods.js +2 -1
  87. package/dist/esm/loaders/api/api.js +14 -11
  88. package/dist/esm/loaders/configure/configure.js +4 -1
  89. package/dist/esm/loaders/features/enabled-features.js +1 -1
  90. package/dist/esm/loaders/features/features.js +3 -1
  91. package/dist/types/common/aggregate/aggregator.d.ts.map +1 -1
  92. package/dist/types/common/config/state/init.d.ts.map +1 -1
  93. package/dist/types/common/context/event-context.d.ts.map +1 -0
  94. package/dist/types/common/context/observation-context-manager.d.ts +28 -0
  95. package/dist/types/common/context/observation-context-manager.d.ts.map +1 -0
  96. package/dist/types/common/event-emitter/contextual-ee.d.ts +2 -2
  97. package/dist/types/common/event-emitter/contextual-ee.d.ts.map +1 -1
  98. package/dist/types/common/session/constants.d.ts +1 -0
  99. package/dist/types/common/session/constants.d.ts.map +1 -1
  100. package/dist/types/common/timing/nav-timing.d.ts.map +1 -1
  101. package/dist/types/common/wrap/index.d.ts +1 -2
  102. package/dist/types/common/wrap/index.d.ts.map +1 -1
  103. package/dist/types/common/wrap/wrap-fetch.d.ts.map +1 -1
  104. package/dist/types/common/wrap/wrap-function.d.ts +0 -1
  105. package/dist/types/common/wrap/wrap-function.d.ts.map +1 -1
  106. package/dist/types/common/wrap/wrap-promise.d.ts.map +1 -1
  107. package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
  108. package/dist/types/features/jserrors/aggregate/index.d.ts +4 -3
  109. package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
  110. package/dist/types/features/page_view_event/aggregate/initialized-features.d.ts.map +1 -1
  111. package/dist/types/features/session_replay/aggregate/index.d.ts +1 -1
  112. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  113. package/dist/types/features/session_replay/constants.d.ts +4 -0
  114. package/dist/types/features/session_replay/constants.d.ts.map +1 -1
  115. package/dist/types/features/session_replay/instrument/index.d.ts.map +1 -1
  116. package/dist/types/features/session_replay/shared/utils.d.ts +4 -0
  117. package/dist/types/features/session_replay/shared/utils.d.ts.map +1 -0
  118. package/dist/types/features/soft_navigations/aggregate/ajax-node.d.ts +19 -0
  119. package/dist/types/features/soft_navigations/aggregate/ajax-node.d.ts.map +1 -0
  120. package/dist/types/features/soft_navigations/aggregate/bel-node.d.ts +16 -0
  121. package/dist/types/features/soft_navigations/aggregate/bel-node.d.ts.map +1 -0
  122. package/dist/types/features/soft_navigations/aggregate/index.d.ts +36 -0
  123. package/dist/types/features/soft_navigations/aggregate/index.d.ts.map +1 -0
  124. package/dist/types/features/soft_navigations/aggregate/initial-page-load-interaction.d.ts +12 -0
  125. package/dist/types/features/soft_navigations/aggregate/initial-page-load-interaction.d.ts.map +1 -0
  126. package/dist/types/features/soft_navigations/aggregate/interaction.d.ts +50 -0
  127. package/dist/types/features/soft_navigations/aggregate/interaction.d.ts.map +1 -0
  128. package/dist/types/features/soft_navigations/constants.d.ts +20 -0
  129. package/dist/types/features/soft_navigations/constants.d.ts.map +1 -0
  130. package/dist/types/features/soft_navigations/index.d.ts +2 -0
  131. package/dist/types/features/soft_navigations/index.d.ts.map +1 -0
  132. package/dist/types/features/soft_navigations/instrument/index.d.ts +7 -0
  133. package/dist/types/features/soft_navigations/instrument/index.d.ts.map +1 -0
  134. package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
  135. package/dist/types/features/utils/agent-session.d.ts.map +1 -1
  136. package/dist/types/features/utils/instrument-base.d.ts +1 -7
  137. package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
  138. package/dist/types/features/utils/lazy-feature-loader.d.ts.map +1 -1
  139. package/dist/types/loaders/agent-base.d.ts +5 -1
  140. package/dist/types/loaders/agent-base.d.ts.map +1 -1
  141. package/dist/types/loaders/agent.d.ts +2 -2
  142. package/dist/types/loaders/agent.d.ts.map +1 -1
  143. package/dist/types/loaders/api/api-methods.d.ts.map +1 -1
  144. package/dist/types/loaders/api/api.d.ts +3 -5
  145. package/dist/types/loaders/api/api.d.ts.map +1 -1
  146. package/dist/types/loaders/configure/configure.d.ts.map +1 -1
  147. package/dist/types/loaders/features/features.d.ts +1 -0
  148. package/dist/types/loaders/features/features.d.ts.map +1 -1
  149. package/dist/types/loaders/micro-agent.d.ts +0 -1
  150. package/dist/types/loaders/micro-agent.d.ts.map +1 -1
  151. package/package.json +1 -1
  152. package/src/cdn/experimental.js +4 -2
  153. package/src/cdn/spa.js +3 -1
  154. package/src/common/aggregate/aggregator.js +2 -11
  155. package/src/common/config/state/init.js +3 -1
  156. package/src/common/context/observation-context-manager.js +55 -0
  157. package/src/common/event-emitter/contextual-ee.js +20 -10
  158. package/src/common/session/constants.js +1 -0
  159. package/src/common/timing/nav-timing.js +7 -3
  160. package/src/common/timing/now.js +1 -1
  161. package/src/common/util/feature-flags.js +1 -1
  162. package/src/common/wrap/index.js +1 -2
  163. package/src/common/wrap/wrap-events.js +3 -3
  164. package/src/common/wrap/wrap-fetch.js +3 -2
  165. package/src/common/wrap/wrap-function.js +4 -6
  166. package/src/common/wrap/wrap-promise.js +3 -2
  167. package/src/features/ajax/aggregate/index.js +36 -18
  168. package/src/features/jserrors/aggregate/index.js +70 -73
  169. package/src/features/page_view_event/aggregate/index.js +1 -1
  170. package/src/features/page_view_event/aggregate/initialized-features.js +1 -0
  171. package/src/features/session_replay/aggregate/index.js +92 -95
  172. package/src/features/session_replay/constants.js +5 -0
  173. package/src/features/session_replay/instrument/index.js +24 -9
  174. package/src/features/session_replay/shared/utils.js +19 -0
  175. package/src/features/soft_navigations/aggregate/ajax-node.js +57 -0
  176. package/src/features/soft_navigations/aggregate/bel-node.js +26 -0
  177. package/src/features/soft_navigations/aggregate/index.js +254 -0
  178. package/src/features/soft_navigations/aggregate/initial-page-load-interaction.js +53 -0
  179. package/src/features/soft_navigations/aggregate/interaction.js +159 -0
  180. package/src/features/soft_navigations/constants.js +29 -0
  181. package/src/features/soft_navigations/index.js +1 -0
  182. package/src/features/soft_navigations/instrument/index.js +67 -0
  183. package/src/features/spa/aggregate/index.js +5 -4
  184. package/src/features/utils/agent-session.js +2 -1
  185. package/src/features/utils/instrument-base.js +7 -10
  186. package/src/features/utils/lazy-feature-loader.js +2 -0
  187. package/src/loaders/agent-base.js +18 -3
  188. package/src/loaders/agent.js +18 -17
  189. package/src/loaders/api/api-methods.js +4 -1
  190. package/src/loaders/api/api.js +14 -12
  191. package/src/loaders/configure/configure.js +4 -1
  192. package/src/loaders/features/enabled-features.js +1 -1
  193. package/src/loaders/features/features.js +3 -1
  194. package/dist/cjs/common/wrap/wrap-raf.js +0 -55
  195. package/dist/esm/common/wrap/wrap-raf.js +0 -48
  196. package/dist/types/common/event-emitter/event-context.d.ts.map +0 -1
  197. package/dist/types/common/wrap/wrap-raf.d.ts +0 -16
  198. package/dist/types/common/wrap/wrap-raf.d.ts.map +0 -1
  199. package/src/common/wrap/wrap-raf.js +0 -52
  200. /package/dist/cjs/common/{event-emitter → context}/event-context.js +0 -0
  201. /package/dist/esm/common/{event-emitter → context}/event-context.js +0 -0
  202. /package/dist/types/common/{event-emitter → context}/event-context.d.ts +0 -0
  203. /package/src/common/{event-emitter → context}/event-context.js +0 -0
@@ -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
  }
@@ -11,7 +11,7 @@ const bucketMap = {
11
11
  stn: [FEATURE_NAMES.sessionTrace],
12
12
  err: [FEATURE_NAMES.jserrors, FEATURE_NAMES.metrics],
13
13
  ins: [FEATURE_NAMES.pageAction],
14
- spa: [FEATURE_NAMES.spa],
14
+ spa: [FEATURE_NAMES.spa, FEATURE_NAMES.softNav],
15
15
  sr: [FEATURE_NAMES.sessionReplay, FEATURE_NAMES.sessionTrace]
16
16
  }
17
17
 
@@ -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
  }
@@ -7,16 +7,16 @@
7
7
  * This module is used directly by: session_trace.
8
8
  * It is also called by -> wrapXhr <-, so see "wrap-xhr.js" for features that use this indirectly.
9
9
  */
10
- import { ee as baseEE, contextId } from '../event-emitter/contextual-ee'
10
+ import { ee as baseEE } from '../event-emitter/contextual-ee'
11
11
  import { createWrapperWithEmitter as wfn } from './wrap-function'
12
12
  import { getOrSet } from '../util/get-or-set'
13
13
  import { globalScope, isBrowserScope } from '../constants/runtime'
14
+ import { ObservationContextManager } from '../context/observation-context-manager'
14
15
 
15
16
  const wrapped = {}
16
17
  const XHR = globalScope.XMLHttpRequest
17
18
  const ADD_EVENT_LISTENER = 'addEventListener'
18
19
  const REMOVE_EVENT_LISTENER = 'removeEventListener'
19
- const flag = `nr@wrapped:${contextId}`
20
20
 
21
21
  /**
22
22
  * Wraps `addEventListener` and `removeEventListener` on: global scope; the prototype of `XMLHttpRequest`, and
@@ -49,7 +49,7 @@ export function wrapEvents (sharedEE) {
49
49
  return
50
50
  }
51
51
 
52
- var wrapped = getOrSet(originalListener, flag, function () {
52
+ var wrapped = getOrSet(originalListener, ObservationContextManager.contextWrappedId, function () {
53
53
  var listener = {
54
54
  object: wrapHandleEvent,
55
55
  function: originalListener
@@ -6,8 +6,9 @@
6
6
  * @file Wraps `fetch` and related methods for instrumentation.
7
7
  * This module is used by: ajax, spa.
8
8
  */
9
- import { ee as baseEE, contextId } from '../event-emitter/contextual-ee'
9
+ import { ee as baseEE } from '../event-emitter/contextual-ee'
10
10
  import { globalScope } from '../constants/runtime'
11
+ import { ObservationContextManager } from '../context/observation-context-manager'
11
12
 
12
13
  var prefix = 'fetch-'
13
14
  var bodyPrefix = prefix + 'body-'
@@ -74,7 +75,7 @@ export function wrapFetch (sharedEE) {
74
75
  // we are wrapping args in an array so we can preserve the reference
75
76
  ee.emit(prefix + 'before-start', [args], ctx)
76
77
  var dtPayload
77
- if (ctx[contextId] && ctx[contextId].dt) dtPayload = ctx[contextId].dt
78
+ if (ctx[ObservationContextManager.contextId] && ctx[ObservationContextManager.contextId].dt) dtPayload = ctx[ObservationContextManager.contextId].dt
78
79
 
79
80
  var origPromiseFromFetch = fn.apply(this, args)
80
81
 
@@ -7,9 +7,7 @@
7
7
  */
8
8
 
9
9
  import { ee } from '../event-emitter/contextual-ee'
10
- import { bundleId } from '../ids/bundle-id'
11
-
12
- export const flag = `nr@original:${bundleId}`
10
+ import { ObservationContextManager } from '../context/observation-context-manager'
13
11
 
14
12
  /**
15
13
  * A convenience alias of `hasOwnProperty`.
@@ -42,7 +40,7 @@ export function createWrapperWithEmitter (emitter, always) {
42
40
  * As a property on a wrapped function, contains the original function.
43
41
  * @type {string}
44
42
  */
45
- wrapFn.flag = flag
43
+ wrapFn.flag = ObservationContextManager.contextOriginalId
46
44
 
47
45
  return wrapFn
48
46
 
@@ -61,7 +59,7 @@ export function createWrapperWithEmitter (emitter, always) {
61
59
 
62
60
  if (!prefix) prefix = ''
63
61
 
64
- nrWrapper[flag] = fn
62
+ nrWrapper[ObservationContextManager.contextOriginalId] = fn
65
63
  copy(fn, nrWrapper, emitter)
66
64
  return nrWrapper
67
65
 
@@ -215,5 +213,5 @@ function copy (from, to, emitter) {
215
213
  * @returns {boolean} Whether the passed function is ineligible to be wrapped.
216
214
  */
217
215
  function notWrappable (fn) {
218
- return !(fn && typeof fn === 'function' && fn.apply && !fn[flag])
216
+ return !(fn && typeof fn === 'function' && fn.apply && !fn[ObservationContextManager.contextOriginalId])
219
217
  }
@@ -7,9 +7,10 @@
7
7
  * This module is used by: spa.
8
8
  */
9
9
 
10
- import { createWrapperWithEmitter as wrapFn, flag } from './wrap-function'
10
+ import { createWrapperWithEmitter as wrapFn } from './wrap-function'
11
11
  import { ee as baseEE } from '../event-emitter/contextual-ee'
12
12
  import { globalScope } from '../constants/runtime'
13
+ import { ObservationContextManager } from '../context/observation-context-manager'
13
14
 
14
15
  const wrapped = {}
15
16
 
@@ -122,7 +123,7 @@ export function wrapPromise (sharedEE) {
122
123
 
123
124
  return origFnCallWithThis
124
125
  }
125
- prevPromiseObj.prototype.then[flag] = prevPromiseOrigThen
126
+ prevPromiseObj.prototype.then[ObservationContextManager.contextOriginalId] = prevPromiseOrigThen
126
127
 
127
128
  promiseEE.on('executor-start', function (args) {
128
129
  args[0] = promiseWrapper(args[0], 'resolve-', this, null, false)
@@ -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,6 +14,7 @@ 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
@@ -22,7 +23,7 @@ export class Aggregate extends AggregateBase {
22
23
  const agentInit = getConfiguration(agentIdentifier)
23
24
  const allAjaxIsEnabled = agentInit.ajax.enabled !== false
24
25
 
25
- register('xhr', storeXhr, this.featureName, this.ee)
26
+ registerHandler('xhr', storeXhr, this.featureName, this.ee)
26
27
  if (!allAjaxIsEnabled) {
27
28
  this.drain()
28
29
  return // feature will only collect timeslice metrics & ajax trace nodes if it's not fully enabled
@@ -44,20 +45,21 @@ export class Aggregate extends AggregateBase {
44
45
  this.prepareHarvest = prepareHarvest
45
46
  this.getStoredEvents = function () { return { ajaxEvents, spaAjaxEvents } }
46
47
 
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) => {
48
+ // --- v Used by old spa feature
49
+ ee.on('interactionDone', (interaction, wasSaved) => {
53
50
  if (!spaAjaxEvents[interaction.id]) return
54
51
 
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
- })
52
+ 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
53
+ spaAjaxEvents[interaction.id].forEach(function (item) {
54
+ ajaxEvents.push(item)
55
+ })
56
+ }
59
57
  delete spaAjaxEvents[interaction.id]
60
58
  })
59
+ // --- ^
60
+ // --- v Used by new soft nav
61
+ registerHandler('returnAjax', event => ajaxEvents.push(event), this.featureName, this.ee)
62
+ // --- ^
61
63
 
62
64
  const scheduler = new HarvestScheduler('events', {
63
65
  onFinished: onEventsHarvestFinished,
@@ -82,17 +84,30 @@ export class Aggregate extends AggregateBase {
82
84
  hash = stringify([params.status, params.host, params.pathname])
83
85
  }
84
86
 
87
+ const shouldCollect = shouldCollectEvent(params)
88
+ const ajaxMetricDenyListEnabled = agentInit.feature_flags?.includes('ajax_metrics_deny_list')
89
+
85
90
  // store as metric
86
- aggregator.store('xhr', hash, params, metrics)
91
+ if (shouldCollect || !ajaxMetricDenyListEnabled) {
92
+ aggregator.store('xhr', hash, params, metrics)
93
+ }
87
94
 
88
95
  if (!allAjaxIsEnabled) return
89
96
 
90
- if (!shouldCollectEvent(params)) {
97
+ if (!shouldCollect) {
91
98
  if (params.hostname === beacon || (proxyBeacon && params.hostname === proxyBeacon)) {
92
99
  // This doesn't make a distinction if the same-domain request is going to a different port or path...
93
100
  handle(SUPPORTABILITY_METRIC_CHANNEL, ['Ajax/Events/Excluded/Agent'], undefined, FEATURE_NAMES.metrics, ee)
101
+
102
+ if (ajaxMetricDenyListEnabled) {
103
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['Ajax/Metrics/Excluded/Agent'], undefined, FEATURE_NAMES.metrics, ee)
104
+ }
94
105
  } else {
95
106
  handle(SUPPORTABILITY_METRIC_CHANNEL, ['Ajax/Events/Excluded/App'], undefined, FEATURE_NAMES.metrics, ee)
107
+
108
+ if (ajaxMetricDenyListEnabled) {
109
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['Ajax/Metrics/Excluded/App'], undefined, FEATURE_NAMES.metrics, ee)
110
+ }
96
111
  }
97
112
  return
98
113
  }
@@ -125,12 +140,14 @@ export class Aggregate extends AggregateBase {
125
140
  body: this.body,
126
141
  query: this?.parsedOrigin?.search
127
142
  })
128
-
129
143
  if (event.gql) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Ajax/Events/GraphQL/Bytes-Added', stringify(event.gql).length], undefined, FEATURE_NAMES.metrics, ee)
130
144
 
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
145
+ const softNavInUse = Boolean(getNREUMInitializedAgent(agentIdentifier)?.features?.[FEATURE_NAMES.softNav])
146
+
147
+ if (softNavInUse) { // For newer soft nav (when running), pass the event to it for evaluation -- either part of an interaction or is given back
148
+ handle('ajax', [event], undefined, FEATURE_NAMES.softNav, ee)
149
+ } else if (this.spaNode) { // For old spa (when running), if the ajax happened inside an interaction, hold it until the interaction finishes
150
+ const interactionId = this.spaNode.interaction.id
134
151
  spaAjaxEvents[interactionId] = spaAjaxEvents[interactionId] || []
135
152
  spaAjaxEvents[interactionId].push(event)
136
153
  } else {
@@ -209,6 +226,7 @@ export class Aggregate extends AggregateBase {
209
226
 
210
227
  for (var i = 0; i < events.length; i++) {
211
228
  var event = events[i]
229
+
212
230
  var fields = [
213
231
  numeric(event.startTime),
214
232
  numeric(event.endTime - event.startTime),
@@ -20,6 +20,7 @@ import { globalScope } from '../../../common/constants/runtime'
20
20
  import { FEATURE_NAME } from '../constants'
21
21
  import { FEATURE_NAMES } from '../../../loaders/features/features'
22
22
  import { AggregateBase } from '../../utils/aggregate-base'
23
+ import { getNREUMInitializedAgent } from '../../../common/window/nreum'
23
24
 
24
25
  /**
25
26
  * @typedef {import('./compute-stack-trace.js').StackInfo} StackInfo
@@ -33,18 +34,17 @@ export class Aggregate extends AggregateBase {
33
34
  this.stackReported = {}
34
35
  this.observedAt = {}
35
36
  this.pageviewReported = {}
36
- this.errorCache = {}
37
+ this.bufferedErrorsUnderSpa = {}
37
38
  this.currentBody = undefined
38
39
  this.errorOnPage = false
39
40
 
40
41
  // this will need to change to match whatever ee we use in the instrument
41
- this.ee.on('interactionSaved', (interaction) => this.onInteractionSaved(interaction))
42
-
43
- // this will need to change to match whatever ee we use in the instrument
44
- this.ee.on('interactionDiscarded', (interaction) => this.onInteractionDiscarded(interaction))
42
+ this.ee.on('interactionDone', (interaction, wasSaved) => this.onInteractionDone(interaction, wasSaved))
45
43
 
46
44
  register('err', (...args) => this.storeError(...args), this.featureName, this.ee)
47
45
  register('ierr', (...args) => this.storeError(...args), this.featureName, this.ee)
46
+ register('softNavFlush', (interactionId, wasFinished, softNavAttrs) =>
47
+ this.onSoftNavNotification(interactionId, wasFinished, softNavAttrs), this.featureName, this.ee) // when an ixn is done or cancelled
48
48
 
49
49
  const harvestTimeSeconds = getConfigurationValue(this.agentIdentifier, 'jserrors.harvestTimeSeconds') || 10
50
50
 
@@ -197,98 +197,95 @@ export class Aggregate extends AggregateBase {
197
197
  var type = internal ? 'ierr' : 'err'
198
198
  var newMetrics = { time }
199
199
 
200
- // sr, stn and spa aggregators listen to this event - stn sends the error in its payload,
201
- // and spa annotates the error with interaction info
202
- const msg = [type, bucketHash, params, newMetrics]
203
- handle('errorAgg', msg, undefined, FEATURE_NAMES.sessionTrace, this.ee)
204
- handle('errorAgg', msg, undefined, FEATURE_NAMES.spa, this.ee)
205
- handle('errorAgg', msg, undefined, FEATURE_NAMES.sessionReplay, this.ee)
206
-
200
+ // Trace sends the error in its payload, and both trace & replay simply listens for any error to occur.
201
+ const jsErrorEvent = [type, bucketHash, params, newMetrics, customAttributes]
202
+ handle('errorAgg', jsErrorEvent, undefined, FEATURE_NAMES.sessionTrace, this.ee)
203
+ handle('errorAgg', jsErrorEvent, undefined, FEATURE_NAMES.sessionReplay, this.ee)
207
204
  // still send EE events for other features such as above, but stop this one from aggregating internal data
208
205
  if (this.blocked) return
209
- var att = getInfo(this.agentIdentifier).jsAttributes
210
- if (params._interactionId != null) {
211
- // hold on to the error until the interaction finishes
212
- this.errorCache[params._interactionId] = this.errorCache[params._interactionId] || []
213
- this.errorCache[params._interactionId].push([type, bucketHash, params, newMetrics, att, customAttributes])
214
- } else {
215
- // store custom attributes
216
- var customParams = {}
217
- mapOwn(att, setCustom)
218
- if (customAttributes) {
219
- mapOwn(customAttributes, setCustom)
220
- }
221
-
222
- var jsAttributesHash = stringHashCode(stringify(customParams))
223
- var aggregateHash = bucketHash + ':' + jsAttributesHash
224
- this.aggregator.store(type, aggregateHash, params, newMetrics, customParams)
225
- }
226
206
 
227
- function setCustom (key, val) {
228
- customParams[key] = (val && typeof val === 'object' ? stringify(val) : val)
207
+ const softNavInUse = Boolean(getNREUMInitializedAgent(this.agentIdentifier)?.features[FEATURE_NAMES.softNav])
208
+ // 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.
209
+ // They each will also tack on their respective properties to the params object as part of the decision flow.
210
+ if (softNavInUse) handle('jserror', [params, time], undefined, FEATURE_NAMES.softNav, this.ee)
211
+ else handle('errorAgg', jsErrorEvent, undefined, FEATURE_NAMES.spa, this.ee)
212
+
213
+ if (params.browserInteractionId && !params._softNavFinished) { // hold onto the error until the in-progress interaction is done, eithered saved or discarded
214
+ this.bufferedErrorsUnderSpa[params.browserInteractionId] ??= []
215
+ this.bufferedErrorsUnderSpa[params.browserInteractionId].push(jsErrorEvent)
216
+ } else if (params._interactionId != null) { // same as above, except tailored for the way old spa does it
217
+ this.bufferedErrorsUnderSpa[params._interactionId] = this.bufferedErrorsUnderSpa[params._interactionId] || []
218
+ this.bufferedErrorsUnderSpa[params._interactionId].push(jsErrorEvent)
219
+ } else {
220
+ // Either there is no interaction (then all these params properties will be undefined) OR there's a related soft navigation that's already completed.
221
+ // The old spa does not look up completed interactions at all, so there's no need to consider it.
222
+ this.#storeJserrorForHarvest(jsErrorEvent, params.browserInteractionId !== undefined, params._softNavAttributes)
229
223
  }
230
224
  }
231
225
 
232
- onInteractionSaved (interaction) {
233
- if (!this.errorCache[interaction.id] || this.blocked) return
226
+ #storeJserrorForHarvest (errorInfoArr, softNavOccurredFinished, softNavCustomAttrs = {}) {
227
+ let [type, bucketHash, params, newMetrics, localAttrs] = errorInfoArr
228
+ const allCustomAttrs = {}
234
229
 
235
- this.errorCache[interaction.id].forEach((item) => {
236
- var customParams = {}
237
- var globalCustomParams = item[4]
238
- var localCustomParams = item[5]
230
+ if (softNavOccurredFinished) {
231
+ Object.entries(softNavCustomAttrs).forEach(([k, v]) => setCustom(k, v)) // when an ixn finishes, it'll include stuff in jsAttributes + attrs specific to the ixn
232
+ bucketHash += params.browserInteractionId
239
233
 
240
- mapOwn(globalCustomParams, setCustom)
241
- mapOwn(interaction.root.attrs.custom, setCustom)
242
- mapOwn(localCustomParams, setCustom)
243
-
244
- var params = item[2]
245
- params.browserInteractionId = interaction.root.attrs.id
246
- delete params._interactionId
247
-
248
- if (params._interactionNodeId) {
249
- params.parentNodeId = params._interactionNodeId.toString()
250
- delete params._interactionNodeId
251
- }
252
-
253
- var hash = item[1] + interaction.root.attrs.id
254
- var jsAttributesHash = stringHashCode(stringify(customParams))
255
- var aggregateHash = hash + ':' + jsAttributesHash
234
+ delete params._softNavAttributes // cleanup temp properties from synchronous evaluation; this is harmless when async from soft nav (properties DNE)
235
+ delete params._softNavFinished
236
+ } else { // interaction was cancelled -> error should not be associated OR there was no interaction
237
+ Object.entries(getInfo(this.agentIdentifier).jsAttributes).forEach(([k, v]) => setCustom(k, v))
238
+ delete params.browserInteractionId
239
+ }
240
+ if (localAttrs) Object.entries(localAttrs).forEach(([k, v]) => setCustom(k, v)) // local custom attrs are applied in either case with the highest precedence
256
241
 
257
- this.aggregator.store(item[0], aggregateHash, params, item[3], customParams)
242
+ const jsAttributesHash = stringHashCode(stringify(allCustomAttrs))
243
+ const aggregateHash = bucketHash + ':' + jsAttributesHash
244
+ this.aggregator.store(type, aggregateHash, params, newMetrics, allCustomAttrs)
258
245
 
259
- function setCustom (key, val) {
260
- customParams[key] = (val && typeof val === 'object' ? stringify(val) : val)
261
- }
262
- })
263
- delete this.errorCache[interaction.id]
246
+ function setCustom (key, val) {
247
+ allCustomAttrs[key] = (val && typeof val === 'object' ? stringify(val) : val)
248
+ }
264
249
  }
265
250
 
266
- onInteractionDiscarded (interaction) {
267
- if (!this.errorCache || !this.errorCache[interaction.id] || this.blocked) return
251
+ // TO-DO: Remove this function when old spa is taken out. #storeJserrorForHarvest handles the work with the softnav feature.
252
+ onInteractionDone (interaction, wasSaved) {
253
+ if (!this.bufferedErrorsUnderSpa[interaction.id] || this.blocked) return
268
254
 
269
- this.errorCache[interaction.id].forEach((item) => {
270
- var customParams = {}
271
- var globalCustomParams = item[4]
272
- var localCustomParams = item[5]
255
+ this.bufferedErrorsUnderSpa[interaction.id].forEach((item) => {
256
+ var allCustomAttrs = {}
257
+ const localCustomAttrs = item[4]
273
258
 
274
- mapOwn(globalCustomParams, setCustom)
275
- mapOwn(interaction.root.attrs.custom, setCustom)
276
- mapOwn(localCustomParams, setCustom)
259
+ mapOwn(interaction.root.attrs.custom, setCustom) // tack on custom attrs from the interaction
260
+ mapOwn(localCustomAttrs, setCustom)
277
261
 
278
262
  var params = item[2]
263
+ if (wasSaved) {
264
+ params.browserInteractionId = interaction.root.attrs.id
265
+ if (params._interactionNodeId) params.parentNodeId = params._interactionNodeId.toString()
266
+ }
279
267
  delete params._interactionId
280
268
  delete params._interactionNodeId
281
269
 
282
- var hash = item[1]
283
- var jsAttributesHash = stringHashCode(stringify(customParams))
270
+ var hash = wasSaved ? item[1] + interaction.root.attrs.id : item[1]
271
+ var jsAttributesHash = stringHashCode(stringify(allCustomAttrs))
284
272
  var aggregateHash = hash + ':' + jsAttributesHash
285
273
 
286
- this.aggregator.store(item[0], aggregateHash, item[2], item[3], customParams)
274
+ this.aggregator.store(item[0], aggregateHash, params, item[3], allCustomAttrs)
287
275
 
288
276
  function setCustom (key, val) {
289
- customParams[key] = (val && typeof val === 'object' ? stringify(val) : val)
277
+ allCustomAttrs[key] = (val && typeof val === 'object' ? stringify(val) : val)
290
278
  }
291
279
  })
292
- delete this.errorCache[interaction.id]
280
+ delete this.bufferedErrorsUnderSpa[interaction.id]
281
+ }
282
+
283
+ onSoftNavNotification (interactionId, wasFinished, softNavAttrs) {
284
+ if (this.blocked) return
285
+
286
+ this.bufferedErrorsUnderSpa[interactionId]?.forEach(jsErrorEvent =>
287
+ this.#storeJserrorForHarvest(jsErrorEvent, wasFinished, softNavAttrs) // this should not modify the re-used softNavAttrs contents
288
+ )
289
+ delete this.bufferedErrorsUnderSpa[interactionId] // wipe the list of jserrors so they aren't duplicated by another call to the same id
293
290
  }
294
291
  }
@@ -112,7 +112,7 @@ export class Aggregate extends AggregateBase {
112
112
  this.drain()
113
113
  } catch (err) {
114
114
  this.ee.abort()
115
- warn('RUM call failed. Agent shutting down.')
115
+ warn('RUM call failed. Agent shutting down.', err)
116
116
  }
117
117
  }
118
118
  })
@@ -22,6 +22,7 @@ export function getActivatedFeaturesFlags (agentId) {
22
22
  flagArr.push('ins'); break
23
23
  case FEATURE_NAMES.sessionTrace:
24
24
  flagArr.push('stn'); break
25
+ case FEATURE_NAMES.softNav:
25
26
  case FEATURE_NAMES.spa:
26
27
  flagArr.push('spa'); break
27
28
  }