@newrelic/browser-agent 1.238.0 → 1.239.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 (249) hide show
  1. package/dist/cjs/common/config/state/init.js +21 -13
  2. package/dist/cjs/common/config/state/runtime.js +0 -4
  3. package/dist/cjs/common/constants/env.cdn.js +1 -1
  4. package/dist/cjs/common/constants/env.npm.js +1 -1
  5. package/dist/cjs/common/constants/runtime.js +3 -3
  6. package/dist/cjs/common/constants/shared-channel.js +3 -1
  7. package/dist/cjs/common/deny-list/deny-list.js +2 -8
  8. package/dist/cjs/common/drain/drain.js +1 -1
  9. package/dist/cjs/common/event-emitter/contextual-ee.js +6 -6
  10. package/dist/cjs/common/harvest/harvest-scheduler.js +2 -4
  11. package/dist/cjs/common/harvest/harvest.js +2 -6
  12. package/dist/cjs/common/serialize/bel-serializer.js +1 -1
  13. package/dist/cjs/common/session/session-entity.js +16 -12
  14. package/dist/cjs/common/storage/first-party-cookies.js +5 -4
  15. package/dist/cjs/common/storage/local-storage.js +3 -2
  16. package/dist/cjs/common/timer/interaction-timer.js +14 -6
  17. package/dist/cjs/common/timing/nav-timing.js +1 -1
  18. package/dist/cjs/common/unload/eol.js +4 -30
  19. package/dist/cjs/common/util/feature-flags.js +14 -15
  20. package/dist/cjs/common/util/invoke.js +3 -1
  21. package/dist/cjs/common/util/obfuscate.js +3 -5
  22. package/dist/cjs/common/util/traverse.js +1 -0
  23. package/dist/cjs/common/window/page-visibility.js +1 -2
  24. package/dist/cjs/common/wrap/wrap-jsonp.js +1 -1
  25. package/dist/cjs/features/ajax/aggregate/index.js +5 -7
  26. package/dist/cjs/features/ajax/instrument/distributed-tracing.js +8 -8
  27. package/dist/cjs/features/ajax/instrument/index.js +47 -15
  28. package/dist/cjs/features/jserrors/aggregate/canonical-function-name.js +0 -1
  29. package/dist/cjs/features/jserrors/aggregate/compute-stack-trace.js +4 -4
  30. package/dist/cjs/features/jserrors/aggregate/index.js +4 -5
  31. package/dist/cjs/features/metrics/aggregate/framework-detection.js +103 -7
  32. package/dist/cjs/features/metrics/aggregate/index.js +20 -37
  33. package/dist/cjs/features/metrics/instrument/index.js +0 -2
  34. package/dist/cjs/features/page_action/aggregate/index.js +17 -19
  35. package/dist/cjs/features/page_view_event/aggregate/index.js +1 -2
  36. package/dist/cjs/features/page_view_event/instrument/index.js +4 -2
  37. package/dist/cjs/features/page_view_timing/aggregate/index.js +6 -7
  38. package/dist/cjs/features/session_replay/aggregate/index.js +3 -3
  39. package/dist/cjs/features/session_trace/aggregate/index.js +8 -8
  40. package/dist/cjs/features/spa/aggregate/index.js +14 -20
  41. package/dist/cjs/features/spa/aggregate/interaction.js +0 -2
  42. package/dist/cjs/features/spa/aggregate/serializer.js +1 -1
  43. package/dist/cjs/features/utils/aggregate-base.js +4 -0
  44. package/dist/cjs/features/utils/handler-cache.js +3 -1
  45. package/dist/cjs/features/utils/instrument-base.js +24 -6
  46. package/dist/cjs/loaders/agent-base.js +11 -0
  47. package/dist/cjs/loaders/agent.js +7 -7
  48. package/dist/cjs/loaders/api/api.js +21 -4
  49. package/dist/cjs/loaders/api/apiAsync.js +19 -20
  50. package/dist/cjs/loaders/configure/configure.js +3 -0
  51. package/dist/cjs/loaders/micro-agent.js +20 -3
  52. package/dist/esm/common/config/state/init.js +21 -13
  53. package/dist/esm/common/config/state/runtime.js +0 -4
  54. package/dist/esm/common/constants/env.cdn.js +1 -1
  55. package/dist/esm/common/constants/env.npm.js +1 -1
  56. package/dist/esm/common/constants/runtime.js +1 -1
  57. package/dist/esm/common/constants/shared-channel.js +3 -1
  58. package/dist/esm/common/deny-list/deny-list.js +2 -8
  59. package/dist/esm/common/drain/drain.js +1 -1
  60. package/dist/esm/common/event-emitter/contextual-ee.js +6 -6
  61. package/dist/esm/common/harvest/harvest-scheduler.js +2 -4
  62. package/dist/esm/common/harvest/harvest.js +2 -6
  63. package/dist/esm/common/serialize/bel-serializer.js +1 -1
  64. package/dist/esm/common/session/session-entity.js +16 -12
  65. package/dist/esm/common/storage/first-party-cookies.js +5 -4
  66. package/dist/esm/common/storage/local-storage.js +3 -2
  67. package/dist/esm/common/timer/interaction-timer.js +14 -6
  68. package/dist/esm/common/timing/nav-timing.js +1 -1
  69. package/dist/esm/common/unload/eol.js +5 -31
  70. package/dist/esm/common/util/feature-flags.js +14 -15
  71. package/dist/esm/common/util/invoke.js +3 -1
  72. package/dist/esm/common/util/obfuscate.js +3 -5
  73. package/dist/esm/common/util/traverse.js +1 -0
  74. package/dist/esm/common/window/page-visibility.js +1 -2
  75. package/dist/esm/common/wrap/wrap-jsonp.js +1 -1
  76. package/dist/esm/features/ajax/aggregate/index.js +5 -7
  77. package/dist/esm/features/ajax/instrument/distributed-tracing.js +8 -8
  78. package/dist/esm/features/ajax/instrument/index.js +47 -15
  79. package/dist/esm/features/jserrors/aggregate/canonical-function-name.js +0 -1
  80. package/dist/esm/features/jserrors/aggregate/compute-stack-trace.js +4 -4
  81. package/dist/esm/features/jserrors/aggregate/index.js +4 -5
  82. package/dist/esm/features/metrics/aggregate/framework-detection.js +103 -7
  83. package/dist/esm/features/metrics/aggregate/index.js +22 -39
  84. package/dist/esm/features/metrics/instrument/index.js +1 -3
  85. package/dist/esm/features/page_action/aggregate/index.js +17 -19
  86. package/dist/esm/features/page_view_event/aggregate/index.js +1 -2
  87. package/dist/esm/features/page_view_event/instrument/index.js +4 -2
  88. package/dist/esm/features/page_view_timing/aggregate/index.js +7 -8
  89. package/dist/esm/features/session_replay/aggregate/index.js +2 -2
  90. package/dist/esm/features/session_trace/aggregate/index.js +8 -8
  91. package/dist/esm/features/spa/aggregate/index.js +14 -20
  92. package/dist/esm/features/spa/aggregate/interaction.js +0 -2
  93. package/dist/esm/features/spa/aggregate/serializer.js +1 -1
  94. package/dist/esm/features/utils/aggregate-base.js +4 -0
  95. package/dist/esm/features/utils/handler-cache.js +3 -1
  96. package/dist/esm/features/utils/instrument-base.js +24 -6
  97. package/dist/esm/loaders/agent-base.js +11 -0
  98. package/dist/esm/loaders/agent.js +7 -7
  99. package/dist/esm/loaders/api/api.js +21 -4
  100. package/dist/esm/loaders/api/apiAsync.js +19 -20
  101. package/dist/esm/loaders/configure/configure.js +3 -0
  102. package/dist/esm/loaders/features/enabled-features.js +1 -1
  103. package/dist/esm/loaders/micro-agent.js +21 -4
  104. package/dist/types/common/config/state/init.d.ts.map +1 -1
  105. package/dist/types/common/config/state/runtime.d.ts.map +1 -1
  106. package/dist/types/common/constants/runtime.d.ts +1 -1
  107. package/dist/types/common/constants/runtime.d.ts.map +1 -1
  108. package/dist/types/common/harvest/harvest-scheduler.d.ts.map +1 -1
  109. package/dist/types/common/harvest/harvest.d.ts.map +1 -1
  110. package/dist/types/common/session/session-entity.d.ts +6 -9
  111. package/dist/types/common/session/session-entity.d.ts.map +1 -1
  112. package/dist/types/common/storage/first-party-cookies.d.ts +1 -1
  113. package/dist/types/common/storage/first-party-cookies.d.ts.map +1 -1
  114. package/dist/types/common/storage/local-storage.d.ts.map +1 -1
  115. package/dist/types/common/timer/interaction-timer.d.ts +3 -0
  116. package/dist/types/common/timer/interaction-timer.d.ts.map +1 -1
  117. package/dist/types/common/unload/eol.d.ts +1 -2
  118. package/dist/types/common/unload/eol.d.ts.map +1 -1
  119. package/dist/types/common/util/feature-flags.d.ts.map +1 -1
  120. package/dist/types/common/util/obfuscate.d.ts.map +1 -1
  121. package/dist/types/common/util/traverse.d.ts.map +1 -1
  122. package/dist/types/common/window/page-visibility.d.ts.map +1 -1
  123. package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
  124. package/dist/types/features/ajax/instrument/index.d.ts.map +1 -1
  125. package/dist/types/features/jserrors/aggregate/canonical-function-name.d.ts.map +1 -1
  126. package/dist/types/features/jserrors/aggregate/index.d.ts +1 -1
  127. package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
  128. package/dist/types/features/metrics/aggregate/framework-detection.d.ts.map +1 -1
  129. package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
  130. package/dist/types/features/metrics/instrument/index.d.ts.map +1 -1
  131. package/dist/types/features/page_action/aggregate/index.d.ts +2 -2
  132. package/dist/types/features/page_action/aggregate/index.d.ts.map +1 -1
  133. package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
  134. package/dist/types/features/page_view_timing/aggregate/index.d.ts.map +1 -1
  135. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  136. package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
  137. package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
  138. package/dist/types/features/spa/aggregate/interaction.d.ts.map +1 -1
  139. package/dist/types/features/utils/aggregate-base.d.ts +1 -0
  140. package/dist/types/features/utils/aggregate-base.d.ts.map +1 -1
  141. package/dist/types/features/utils/handler-cache.d.ts.map +1 -1
  142. package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
  143. package/dist/types/loaders/agent-base.d.ts +6 -0
  144. package/dist/types/loaders/agent-base.d.ts.map +1 -1
  145. package/dist/types/loaders/agent.d.ts +1 -1
  146. package/dist/types/loaders/agent.d.ts.map +1 -1
  147. package/dist/types/loaders/api/api.d.ts +1 -0
  148. package/dist/types/loaders/api/api.d.ts.map +1 -1
  149. package/dist/types/loaders/api/apiAsync.d.ts.map +1 -1
  150. package/dist/types/loaders/configure/configure.d.ts +1 -0
  151. package/dist/types/loaders/configure/configure.d.ts.map +1 -1
  152. package/dist/types/loaders/micro-agent.d.ts +7 -1
  153. package/dist/types/loaders/micro-agent.d.ts.map +1 -1
  154. package/package.json +41 -12
  155. package/src/common/aggregate/aggregator.test.js +107 -0
  156. package/src/common/config/state/configurable.test.js +73 -0
  157. package/src/common/config/state/info.test.js +31 -0
  158. package/src/common/config/state/init.js +13 -12
  159. package/src/common/config/state/init.test.js +28 -0
  160. package/src/common/config/state/loader-config.test.js +21 -0
  161. package/src/common/config/state/runtime.js +0 -2
  162. package/src/common/config/state/runtime.test.js +21 -0
  163. package/src/common/constants/__mocks__/runtime.js +1 -1
  164. package/src/common/constants/runtime.js +1 -1
  165. package/src/common/constants/runtime.test.js +2 -2
  166. package/src/common/constants/shared-channel.js +1 -1
  167. package/src/common/deny-list/deny-list.js +2 -10
  168. package/src/common/drain/drain.js +1 -1
  169. package/src/common/drain/drain.test.js +74 -0
  170. package/src/common/event-emitter/contextual-ee.js +6 -6
  171. package/src/common/harvest/harvest-scheduler.js +2 -5
  172. package/src/common/harvest/harvest-scheduler.test.js +2 -6
  173. package/src/common/harvest/harvest.js +2 -6
  174. package/src/common/harvest/harvest.test.js +0 -2
  175. package/src/common/ids/id.test.js +6 -6
  176. package/src/common/serialize/bel-serializer.js +1 -1
  177. package/src/common/session/session-entity.component-test.js +3 -2
  178. package/src/common/session/session-entity.js +11 -9
  179. package/src/common/storage/first-party-cookies.js +5 -4
  180. package/src/common/storage/local-storage.js +3 -2
  181. package/src/common/timer/interaction-timer.component-test.js +52 -7
  182. package/src/common/timer/interaction-timer.js +15 -6
  183. package/src/common/timer/timer.test.js +0 -1
  184. package/src/common/timing/nav-timing.js +1 -1
  185. package/src/common/unload/eol.js +5 -31
  186. package/src/common/url/encode.test.js +1 -0
  187. package/src/common/util/__mocks__/invoke.js +1 -0
  188. package/src/common/util/data-size.test.js +0 -1
  189. package/src/common/util/feature-flags.js +15 -17
  190. package/src/common/util/feature-flags.test.js +4 -8
  191. package/src/common/util/invoke.js +1 -1
  192. package/src/common/util/obfuscate.js +3 -5
  193. package/src/common/util/submit-data.test.js +1 -1
  194. package/src/common/util/traverse.js +1 -0
  195. package/src/common/window/__mocks__/nreum.js +3 -1
  196. package/src/common/window/page-visibility.js +1 -2
  197. package/src/common/wrap/wrap-jsonp.js +1 -1
  198. package/src/features/ajax/aggregate/index.js +5 -7
  199. package/src/features/ajax/instrument/distributed-tracing.js +8 -8
  200. package/src/features/ajax/instrument/index.js +40 -17
  201. package/src/features/jserrors/aggregate/canonical-function-name.js +0 -2
  202. package/src/features/jserrors/aggregate/compute-stack-trace.js +4 -4
  203. package/src/features/jserrors/aggregate/compute-stack-trace.test.js +1 -1
  204. package/src/features/jserrors/aggregate/index.js +4 -5
  205. package/src/features/metrics/aggregate/framework-detection.js +129 -8
  206. package/src/features/metrics/aggregate/framework-detection.test.js +213 -82
  207. package/src/features/metrics/aggregate/index.js +22 -45
  208. package/src/features/metrics/instrument/index.js +1 -3
  209. package/src/features/page_action/aggregate/index.js +12 -13
  210. package/src/features/page_view_event/aggregate/index.js +1 -2
  211. package/src/features/page_view_event/instrument/index.js +2 -2
  212. package/src/features/page_view_timing/aggregate/index.js +7 -8
  213. package/src/features/session_replay/aggregate/index.component-test.js +3 -15
  214. package/src/features/session_replay/aggregate/index.js +2 -2
  215. package/src/features/session_trace/aggregate/index.js +6 -9
  216. package/src/features/spa/aggregate/index.js +14 -20
  217. package/src/features/spa/aggregate/interaction.js +0 -2
  218. package/src/features/spa/aggregate/serializer.js +1 -1
  219. package/src/features/utils/aggregate-base.js +5 -0
  220. package/src/features/utils/aggregate-base.test.js +2 -1
  221. package/src/features/utils/handler-cache.js +3 -2
  222. package/src/features/utils/instrument-base.js +25 -6
  223. package/src/features/utils/instrument-base.test.js +2 -2
  224. package/src/loaders/agent-base.js +11 -0
  225. package/src/loaders/agent.js +7 -7
  226. package/src/loaders/api/api.js +21 -4
  227. package/src/loaders/api/api.test.js +85 -0
  228. package/src/loaders/api/apiAsync.js +19 -20
  229. package/src/loaders/api/apiAsync.test.js +17 -0
  230. package/src/loaders/configure/configure.js +3 -0
  231. package/src/loaders/features/enabled-features.js +1 -1
  232. package/src/loaders/micro-agent.js +22 -4
  233. package/dist/cjs/features/metrics/aggregate/endpoint-map.js +0 -14
  234. package/dist/cjs/features/metrics/aggregate/polyfill-detection.es5.js +0 -14
  235. package/dist/cjs/features/metrics/aggregate/polyfill-detection.js +0 -53
  236. package/dist/esm/features/metrics/aggregate/endpoint-map.js +0 -7
  237. package/dist/esm/features/metrics/aggregate/polyfill-detection.es5.js +0 -8
  238. package/dist/esm/features/metrics/aggregate/polyfill-detection.js +0 -47
  239. package/dist/types/features/metrics/aggregate/endpoint-map.d.ts +0 -8
  240. package/dist/types/features/metrics/aggregate/endpoint-map.d.ts.map +0 -1
  241. package/dist/types/features/metrics/aggregate/polyfill-detection.d.ts +0 -6
  242. package/dist/types/features/metrics/aggregate/polyfill-detection.d.ts.map +0 -1
  243. package/dist/types/features/metrics/aggregate/polyfill-detection.es5.d.ts +0 -7
  244. package/dist/types/features/metrics/aggregate/polyfill-detection.es5.d.ts.map +0 -1
  245. package/src/features/metrics/aggregate/endpoint-map.js +0 -7
  246. package/src/features/metrics/aggregate/polyfill-detection.es5.js +0 -8
  247. package/src/features/metrics/aggregate/polyfill-detection.es5.test.js +0 -16
  248. package/src/features/metrics/aggregate/polyfill-detection.js +0 -48
  249. package/src/features/metrics/aggregate/polyfill-detection.test.js +0 -163
@@ -0,0 +1,31 @@
1
+ let isValid, getInfo, setInfo
2
+ beforeEach(async () => {
3
+ jest.resetModules()
4
+ ;({ isValid, getInfo, setInfo } = await import('./info.js'))
5
+ })
6
+
7
+ test('set/getInfo should throw on an invalid agent id', () => {
8
+ // currently only checks if it's a truthy value
9
+ expect(() => setInfo(undefined, {})).toThrow('require an agent id')
10
+ expect(() => getInfo(undefined)).toThrow('require an agent id')
11
+ expect(() => setInfo('', {})).toThrow('require an agent id')
12
+ expect(() => getInfo('')).toThrow('require an agent id')
13
+ })
14
+
15
+ test('set/getInfo works correctly', () => {
16
+ expect(() => setInfo(123, { beacon: 'im here' })).not.toThrow() // notice setInfo accepts numbers
17
+ let cachedObj = getInfo('123')
18
+ expect(Object.keys(cachedObj).length).toBeGreaterThan(1)
19
+ expect(cachedObj.beacon).toEqual('im here')
20
+ expect(cachedObj.licenseKey).toEqual(undefined) // this should mirror default in info.js
21
+ })
22
+
23
+ test('isValid is correct', () => {
24
+ setInfo('ab', { errorBeacon: 'some value' })
25
+ setInfo('cd', { errorBeacon: 'some value', licenseKey: 'cereal box' })
26
+ expect(isValid('ab')).toBeFalsy()
27
+ expect(isValid('cd')).toBeFalsy()
28
+
29
+ setInfo('ef', { errorBeacon: 'some value', licenseKey: 'cereal box', applicationID: '25' })
30
+ expect(isValid('ef')).toBeTruthy()
31
+ })
@@ -8,9 +8,8 @@ const model = () => {
8
8
  maskInputOptions: { password: true }
9
9
  }
10
10
  return {
11
- allow_bfcache: true, // *cli - temporary feature flag for BFCache work
12
11
  privacy: { cookies_enabled: true }, // *cli - per discussion, default should be true
13
- ajax: { deny_list: undefined, block_internal: true, enabled: true, harvestTimeSeconds: 10 },
12
+ ajax: { deny_list: undefined, block_internal: true, enabled: true, harvestTimeSeconds: 10, autoStart: true },
14
13
  distributed_tracing: {
15
14
  enabled: undefined,
16
15
  exclude_newrelic_header: undefined,
@@ -25,15 +24,16 @@ const model = () => {
25
24
  },
26
25
  ssl: undefined,
27
26
  obfuscate: undefined,
28
- jserrors: { enabled: true, harvestTimeSeconds: 10 },
29
- metrics: { enabled: true },
30
- page_action: { enabled: true, harvestTimeSeconds: 30 },
31
- page_view_event: { enabled: true },
32
- page_view_timing: { enabled: true, harvestTimeSeconds: 30, long_task: false },
33
- session_trace: { enabled: true, harvestTimeSeconds: 10 },
27
+ jserrors: { enabled: true, harvestTimeSeconds: 10, autoStart: true },
28
+ metrics: { enabled: true, autoStart: true },
29
+ page_action: { enabled: true, harvestTimeSeconds: 30, autoStart: true },
30
+ page_view_event: { enabled: true, autoStart: true },
31
+ page_view_timing: { enabled: true, harvestTimeSeconds: 30, long_task: false, autoStart: true },
32
+ session_trace: { enabled: true, harvestTimeSeconds: 10, autoStart: true },
34
33
  harvest: { tooManyRequestsDelay: 60 },
35
34
  session_replay: {
36
35
  // feature settings
36
+ autoStart: true,
37
37
  enabled: false,
38
38
  harvestTimeSeconds: 60,
39
39
  sampleRate: 0.1,
@@ -61,26 +61,27 @@ const model = () => {
61
61
  hiddenState.maskInputOptions = { ...val, password: true }
62
62
  }
63
63
  },
64
- spa: { enabled: true, harvestTimeSeconds: 10 }
64
+ spa: { enabled: true, harvestTimeSeconds: 10, autoStart: true }
65
65
  }
66
66
  }
67
67
 
68
68
  const _cache = {}
69
+ const missingAgentIdError = 'All configuration objects require an agent identifier!'
69
70
 
70
71
  export function getConfiguration (id) {
71
- if (!id) throw new Error('All configuration objects require an agent identifier!')
72
+ if (!id) throw new Error(missingAgentIdError)
72
73
  if (!_cache[id]) throw new Error(`Configuration for ${id} was never set`)
73
74
  return _cache[id]
74
75
  }
75
76
 
76
77
  export function setConfiguration (id, obj) {
77
- if (!id) throw new Error('All configuration objects require an agent identifier!')
78
+ if (!id) throw new Error(missingAgentIdError)
78
79
  _cache[id] = getModeledObject(obj, model())
79
80
  gosNREUMInitializedAgents(id, _cache[id], 'config')
80
81
  }
81
82
 
82
83
  export function getConfigurationValue (id, path) {
83
- if (!id) throw new Error('All configuration objects require an agent identifier!')
84
+ if (!id) throw new Error(missingAgentIdError)
84
85
  var val = getConfiguration(id)
85
86
  if (val) {
86
87
  var parts = path.split('.')
@@ -0,0 +1,28 @@
1
+ let getConfiguration, setConfiguration, getConfigurationValue
2
+ beforeEach(async () => {
3
+ jest.resetModules()
4
+ ;({ getConfiguration, setConfiguration, getConfigurationValue } = await import('./init.js'))
5
+ })
6
+
7
+ test('set/getConfiguration should throw on an invalid agent id', () => {
8
+ // currently only checks if it's a truthy value
9
+ expect(() => setConfiguration(undefined, {})).toThrow('require an agent id')
10
+ expect(() => getConfiguration(undefined)).toThrow('require an agent id')
11
+ expect(() => setConfiguration('', {})).toThrow('require an agent id')
12
+ expect(() => getConfiguration('')).toThrow('require an agent id')
13
+ })
14
+
15
+ test('set/getConfiguration works correctly', () => {
16
+ expect(() => setConfiguration(123, { jserrors: { enabled: false } })).not.toThrow() // notice setConfiguration accepts numbers
17
+ let cachedObj = getConfiguration('123')
18
+ expect(Object.keys(cachedObj).length).toBeGreaterThan(1)
19
+ expect(cachedObj.jserrors.enabled).toEqual(false)
20
+ expect(cachedObj.page_action.enabled).toEqual(true) // this should mirror default in init.js
21
+ })
22
+
23
+ test('getConfigurationValue parses path correctly', () => {
24
+ setConfiguration('ab', { page_action: { harvestTimeSeconds: 1000 } })
25
+ expect(getConfigurationValue('ab', '')).toBeUndefined()
26
+ expect(getConfigurationValue('ab', 'page_action')).toEqual({ enabled: true, harvestTimeSeconds: 1000, autoStart: true })
27
+ expect(getConfigurationValue('ab', 'page_action.harvestTimeSeconds')).toEqual(1000)
28
+ })
@@ -0,0 +1,21 @@
1
+ let getLoaderConfig, setLoaderConfig
2
+ beforeEach(async () => {
3
+ jest.resetModules()
4
+ ;({ getLoaderConfig, setLoaderConfig } = await import('./loader-config.js'))
5
+ })
6
+
7
+ test('set/getLoaderConfig should throw on an invalid agent id', () => {
8
+ // currently only checks if it's a truthy value
9
+ expect(() => setLoaderConfig(undefined, {})).toThrow('require an agent id')
10
+ expect(() => getLoaderConfig(undefined)).toThrow('require an agent id')
11
+ expect(() => setLoaderConfig('', {})).toThrow('require an agent id')
12
+ expect(() => getLoaderConfig('')).toThrow('require an agent id')
13
+ })
14
+
15
+ test('set/getLoaderConfig works correctly', () => {
16
+ expect(() => setLoaderConfig(123, { trustKey: 1 })).not.toThrow() // notice setLoaderConfig accepts numbers
17
+ let cachedObj = getLoaderConfig('123')
18
+ expect(Object.keys(cachedObj).length).toBeGreaterThan(1)
19
+ expect(cachedObj.trustKey).toEqual(1)
20
+ expect(cachedObj.xpid).toBeUndefined() // this should mirror default in loader-config.js
21
+ })
@@ -5,8 +5,6 @@ import { BUILD_ENV, DIST_METHOD, VERSION } from '../../constants/env'
5
5
 
6
6
  const model = {
7
7
  buildEnv: BUILD_ENV,
8
- bytesSent: {}, // Used for SM to capture body bytes sent per endpoint
9
- queryBytesSent: {}, // Used for SM to capture query parameter bytes sent per endpoint
10
8
  customTransaction: undefined,
11
9
  disabled: false,
12
10
  distMethod: DIST_METHOD,
@@ -0,0 +1,21 @@
1
+ let getRuntime, setRuntime
2
+ beforeEach(async () => {
3
+ jest.resetModules()
4
+ ;({ getRuntime, setRuntime } = await import('./runtime.js'))
5
+ })
6
+
7
+ test('set/getRuntime should throw on an invalid agent id', () => {
8
+ // currently only checks if it's a truthy value
9
+ expect(() => setRuntime(undefined, {})).toThrow('require an agent id')
10
+ expect(() => getRuntime(undefined)).toThrow('require an agent id')
11
+ expect(() => setRuntime('', {})).toThrow('require an agent id')
12
+ expect(() => getRuntime('')).toThrow('require an agent id')
13
+ })
14
+
15
+ test('set/getRuntime works correctly', () => {
16
+ expect(() => setRuntime(123, { session: 1 })).not.toThrow() // notice setRuntime accepts numbers
17
+ let cachedObj = getRuntime('123')
18
+ expect(Object.keys(cachedObj).length).toBeGreaterThan(1)
19
+ expect(cachedObj.session).toEqual(1)
20
+ expect(cachedObj.maxBytes).toEqual(30000) // this should mirror default in runtime.js
21
+ })
@@ -3,6 +3,6 @@ export const isWorkerScope = false
3
3
  export const globalScope = window
4
4
  export const initialLocation = '' + globalScope?.location
5
5
  export const isiOS = false
6
- export const iOS_below16 = false
6
+ export const iOSBelow16 = false
7
7
  export const ffVersion = 0
8
8
  export const supportsSendBeacon = true
@@ -55,7 +55,7 @@ export const isiOS = /iPad|iPhone|iPod/.test(navigator.userAgent)
55
55
  * did not work. This affects our onFCP metric in particular since web-vitals uses that flag to retrieve paint timing entries.
56
56
  * This was fixed in v16+.
57
57
  */
58
- export const iOS_below16 = (isiOS && typeof SharedWorker === 'undefined')
58
+ export const iOSBelow16 = (isiOS && typeof SharedWorker === 'undefined')
59
59
 
60
60
  export const ffVersion = (() => {
61
61
  const match = navigator.userAgent.match(/Firefox[/\s](\d+\.\d+)/)
@@ -122,7 +122,7 @@ test.each([
122
122
  { userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 13_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.5 Safari/605.1.15', expected: false },
123
123
  { userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/114.0', expected: false },
124
124
  { userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 13.4; rv:109.0) Gecko/20100101 Firefox/114.0', expected: false }
125
- ])('should set iOS_below16 to $expected for $userAgent', async ({ userAgent, expected }) => {
125
+ ])('should set iOSBelow16 to $expected for $userAgent', async ({ userAgent, expected }) => {
126
126
  if (!expected) {
127
127
  global.SharedWorker = class SharedWorker {}
128
128
  }
@@ -132,7 +132,7 @@ test.each([
132
132
 
133
133
  delete global.SharedWorker
134
134
 
135
- expect(runtime.iOS_below16).toEqual(expected)
135
+ expect(runtime.iOSBelow16).toEqual(expected)
136
136
  })
137
137
 
138
138
  test.each([
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  let onReplayReady
8
- const sessionReplayInitialized = new Promise(resolve => onReplayReady = resolve)
8
+ const sessionReplayInitialized = new Promise(resolve => { onReplayReady = resolve })
9
9
 
10
10
  export const sharedChannel = Object.freeze({
11
11
  onReplayReady,
@@ -81,11 +81,7 @@ function domainMatchesPattern (pattern, domain) {
81
81
  return false
82
82
  }
83
83
 
84
- if (domain.indexOf(pattern) === (domain.length - pattern.length)) {
85
- return true
86
- }
87
-
88
- return false
84
+ return domain.indexOf(pattern) === (domain.length - pattern.length)
89
85
  }
90
86
 
91
87
  /**
@@ -108,9 +104,5 @@ function comparePath (pattern, path) {
108
104
  return true
109
105
  }
110
106
 
111
- if (pattern === path) {
112
- return true
113
- }
114
-
115
- return false
107
+ return pattern === path
116
108
  }
@@ -44,7 +44,6 @@ function curateRegistry (agentIdentifier) {
44
44
  */
45
45
  export function drain (agentIdentifier = '', featureName = 'feature') {
46
46
  curateRegistry(agentIdentifier)
47
-
48
47
  // If the feature for the specified agent is not in the registry, that means the instrument file was bypassed.
49
48
  // This could happen in tests, or loaders that directly import the aggregator. In these cases it is safe to
50
49
  // drain the feature group immediately rather than waiting to drain all at once.
@@ -59,6 +58,7 @@ export function drain (agentIdentifier = '', featureName = 'feature') {
59
58
  if (items.every(([key, values]) => values.staged)) {
60
59
  items.sort((a, b) => a[1].priority - b[1].priority)
61
60
  items.forEach(([group]) => {
61
+ registry[agentIdentifier].delete(group)
62
62
  drainGroup(group)
63
63
  })
64
64
  }
@@ -0,0 +1,74 @@
1
+ let registerDrain, drain, ee
2
+ beforeEach(async () => {
3
+ jest.resetModules()
4
+ ;({ registerDrain, drain } = await import('./drain'))
5
+ ;({ ee } = await import('../event-emitter/contextual-ee'))
6
+ })
7
+
8
+ test('can register a feat and drain it', () => {
9
+ registerDrain('abcd', 'page_view_event')
10
+
11
+ let emitSpy = jest.spyOn(ee.get('abcd'), 'emit')
12
+ drain('abcd', 'page_view_event')
13
+ expect(emitSpy).toHaveBeenCalledWith('drain-page_view_event', expect.anything())
14
+ })
15
+
16
+ test('other unregistered drains do not affect feat reg & drain', () => {
17
+ registerDrain('abcd', 'page_view_event')
18
+
19
+ let emitSpy = jest.spyOn(ee.get('abcd'), 'emit')
20
+ drain('abcd', 'timon')
21
+ expect(emitSpy).toHaveBeenCalledWith('drain-timon', expect.anything())
22
+ expect(emitSpy).not.toHaveBeenCalledWith('drain-page_view_event', expect.anything())
23
+
24
+ drain('abcd', 'page_view_event')
25
+ expect(emitSpy).toHaveBeenCalledWith('drain-page_view_event', expect.anything())
26
+ })
27
+
28
+ describe('drain', () => {
29
+ test('does not execute until all registered groups calls it and in order', () => {
30
+ registerDrain('abcd', 'page_view_timing')
31
+ registerDrain('abcd', 'page_view_event')
32
+
33
+ let emitSpy = jest.spyOn(ee.get('abcd'), 'emit')
34
+ drain('abcd', 'page_view_event')
35
+ expect(emitSpy).not.toHaveBeenCalled()
36
+
37
+ drain('abcd', 'page_view_timing')
38
+ // The priority order of features is also checked here, even though the latter was registered first.
39
+ expect(emitSpy).toHaveBeenNthCalledWith(1, 'drain-page_view_event', expect.anything())
40
+ expect(emitSpy).toHaveBeenNthCalledWith(2, 'drain-page_view_timing', expect.anything())
41
+ })
42
+
43
+ test('does not require registration for non-feat groups', () => {
44
+ let emitSpy = jest.spyOn(ee.get('abcd'), 'emit')
45
+ drain('abcd', 'pumbaa')
46
+ expect(emitSpy).toHaveBeenCalledWith('drain-pumbaa', expect.anything())
47
+ })
48
+
49
+ test('defaults to "feature" group when not provided one', () => {
50
+ let emitSpy = jest.spyOn(ee.get('abcd'), 'emit')
51
+ drain('abcd')
52
+ expect(emitSpy).toHaveBeenCalledWith('drain-feature', expect.anything())
53
+ })
54
+
55
+ test('works on the global ee when agent id not provided', () => {
56
+ let emitSpy = jest.spyOn(ee, 'emit')
57
+ drain()
58
+ expect(emitSpy).toHaveBeenCalledWith('drain-feature', expect.anything())
59
+ })
60
+ })
61
+
62
+ test('non-feat groups can register and drain too alongside features', () => {
63
+ registerDrain('abcd', 'page_view_event')
64
+ registerDrain('abcd', 'simba')
65
+
66
+ console.log(JSON.stringify(ee.get('abcd')))
67
+ let emitSpy = jest.spyOn(ee.get('abcd'), 'emit')
68
+ drain('abcd', 'simba')
69
+ expect(emitSpy).not.toHaveBeenCalled()
70
+
71
+ drain('abcd', 'page_view_event')
72
+ expect(emitSpy).toHaveBeenNthCalledWith(1, 'drain-simba', expect.anything()) // non-feat have prio of 0
73
+ expect(emitSpy).toHaveBeenNthCalledWith(2, 'drain-page_view_event', expect.anything())
74
+ })
@@ -41,16 +41,16 @@ function ee (old, debugId) {
41
41
 
42
42
  var emitter = {
43
43
  on: addEventListener,
44
- addEventListener: addEventListener,
45
- removeEventListener: removeEventListener,
46
- emit: emit,
44
+ addEventListener,
45
+ removeEventListener,
46
+ emit,
47
47
  get: getOrCreate,
48
- listeners: listeners,
49
- context: context,
48
+ listeners,
49
+ context,
50
50
  buffer: bufferEventsByGroup,
51
51
  abort,
52
52
  aborted: false,
53
- isBuffering: isBuffering,
53
+ isBuffering,
54
54
  debugId,
55
55
  backlog: isolatedBacklog ? {} : old && typeof old.backlog === 'object' ? old.backlog : {}
56
56
 
@@ -7,7 +7,6 @@ import * as submitData from '../util/submit-data'
7
7
  import { SharedContext } from '../context/shared-context'
8
8
  import { Harvest } from './harvest'
9
9
  import { subscribeToEOL } from '../unload/eol'
10
- import { getConfigurationValue } from '../config/config'
11
10
  import { SESSION_EVENTS } from '../session/session-entity'
12
11
 
13
12
  /**
@@ -36,7 +35,7 @@ export class HarvestScheduler extends SharedContext {
36
35
  this.harvest = new Harvest(this.sharedContext)
37
36
 
38
37
  // unload if EOL mechanism fires
39
- subscribeToEOL(this.unload.bind(this), getConfigurationValue(this.sharedContext.agentIdentifier, 'allow_bfcache')) // TO DO: remove feature flag after rls stable
38
+ subscribeToEOL(this.unload.bind(this))
40
39
 
41
40
  /* Flush all buffered data if session resets and give up retries. This should be synchronous to ensure that the correct `session` value is sent.
42
41
  Since session-reset generates a new session ID and the ID is grabbed at send-time, any delays or retries would cause the payload to be sent under
@@ -103,7 +102,7 @@ export class HarvestScheduler extends SharedContext {
103
102
  if (!submitMethod) return false
104
103
 
105
104
  const retry = !opts?.unload && submitMethod === submitData.xhr
106
- payload = this.opts.getPayload({ retry: retry })
105
+ payload = this.opts.getPayload({ retry })
107
106
 
108
107
  if (!payload) {
109
108
  if (this.started) {
@@ -143,8 +142,6 @@ export class HarvestScheduler extends SharedContext {
143
142
  if (this.started) {
144
143
  this.scheduleHarvest()
145
144
  }
146
-
147
- return
148
145
  }
149
146
 
150
147
  onHarvestFinished (opts, result) {
@@ -2,7 +2,6 @@ import { faker } from '@faker-js/faker'
2
2
 
3
3
  import * as submitData from '../util/submit-data'
4
4
  import { subscribeToEOL } from '../unload/eol'
5
- import { getConfigurationValue } from '../config/config'
6
5
  import { Harvest } from './harvest'
7
6
 
8
7
  import { HarvestScheduler } from './harvest-scheduler'
@@ -31,13 +30,10 @@ describe('unload', () => {
31
30
  jest.spyOn(harvestSchedulerInstance, 'runHarvest').mockImplementation(jest.fn())
32
31
  })
33
32
 
34
- test('should subscribe to eol with allow_bfcache setting', () => {
35
- const mockedBFCacheSetting = faker.datatype.uuid()
36
- jest.mocked(getConfigurationValue).mockReturnValue(mockedBFCacheSetting)
37
-
33
+ test('should subscribe to eol', () => {
38
34
  new HarvestScheduler()
39
35
 
40
- expect(subscribeToEOL).toHaveBeenCalledWith(expect.any(Function), mockedBFCacheSetting)
36
+ expect(subscribeToEOL).toHaveBeenCalledWith(expect.any(Function))
41
37
  })
42
38
 
43
39
  test('should run onUnload callback', () => {
@@ -96,7 +96,8 @@ export class Harvest extends SharedContext {
96
96
  return false
97
97
  }
98
98
 
99
- let url = `${this.getScheme()}://${info.errorBeacon}${endpoint !== 'rum' ? `/${endpoint}` : ''}/1/${info.licenseKey}`
99
+ const endpointURLPart = endpoint !== 'rum' ? `/${endpoint}` : ''
100
+ let url = `${this.getScheme()}://${info.errorBeacon}${endpointURLPart}/1/${info.licenseKey}`
100
101
  if (customUrl) url = customUrl
101
102
  if (raw) url = `${this.getScheme()}://${info.errorBeacon}/${endpoint}`
102
103
 
@@ -127,11 +128,6 @@ export class Harvest extends SharedContext {
127
128
  body = ''
128
129
  }
129
130
 
130
- // Get bytes harvested per endpoint as a supportability metric. See metrics aggregator (on unload).
131
- agentRuntime.bytesSent[endpoint] = (agentRuntime.bytesSent[endpoint] || 0) + body?.length || 0
132
- // Get query bytes harvested per endpoint as a supportability metric. See metrics aggregator (on unload).
133
- agentRuntime.queryBytesSent[endpoint] = (agentRuntime.queryBytesSent[endpoint] || 0) + fullUrl.split('?').slice(-1)[0]?.length || 0
134
-
135
131
  const headers = []
136
132
 
137
133
  headers.push({ key: 'content-type', value: 'text/plain' })
@@ -157,8 +157,6 @@ describe('_send', () => {
157
157
  licenseKey
158
158
  })
159
159
  jest.mocked(configModule.getRuntime).mockReturnValue({
160
- bytesSent: {},
161
- queryBytesSent: {},
162
160
  maxBytes: Infinity
163
161
  })
164
162
 
@@ -67,25 +67,25 @@ test('id is zero on global scope', async () => {
67
67
  })
68
68
 
69
69
  test('id on prototype is correctly inherited', () => {
70
- const fn = jest.fn()
70
+ const Ctor = jest.fn()
71
71
  const a = {}
72
72
 
73
73
  idFn(a)
74
- fn.prototype = a
74
+ Ctor.prototype = a
75
75
 
76
- const b = new fn()
76
+ const b = new Ctor()
77
77
 
78
78
  expect(b['nr@id']).toEqual(a['nr@id'])
79
79
  })
80
80
 
81
81
  test('id on prototype is different from instance', () => {
82
- const fn = jest.fn()
82
+ const Ctor = jest.fn()
83
83
  const a = {}
84
84
 
85
85
  idFn(a)
86
- fn.prototype = a
86
+ Ctor.prototype = a
87
87
 
88
- const b = new fn()
88
+ const b = new Ctor()
89
89
  const result = idFn(b)
90
90
 
91
91
  expect(b['nr@id']).toEqual(result)
@@ -32,7 +32,7 @@ export function getAddStringContext (agentIdentifier) {
32
32
 
33
33
  function addString (str) {
34
34
  if (typeof str === 'undefined' || str === '') return ''
35
- var obfuscator = new Obfuscator({ agentIdentifier: agentIdentifier })
35
+ var obfuscator = new Obfuscator({ agentIdentifier })
36
36
  str = String(str)
37
37
  if (obfuscator.shouldObfuscate()) str = obfuscator.obfuscateString(str)
38
38
  if (hasOwnProp.call(stringTable, str)) {
@@ -13,6 +13,7 @@ class LocalMemory {
13
13
  try {
14
14
  return this.state[key]
15
15
  } catch (err) {
16
+ // Error is ignored
16
17
  return ''
17
18
  }
18
19
  }
@@ -22,7 +23,7 @@ class LocalMemory {
22
23
  if (value === undefined || value === null) return this.remove(key)
23
24
  this.state[key] = value
24
25
  } catch (err) {
25
- return
26
+ // Error is ignored
26
27
  }
27
28
  }
28
29
 
@@ -30,7 +31,7 @@ class LocalMemory {
30
31
  try {
31
32
  delete this.state[key]
32
33
  } catch (err) {
33
- return
34
+ // Error is ignored
34
35
  }
35
36
  }
36
37
  }
@@ -42,10 +42,8 @@ export class SessionEntity {
42
42
  * expiresMs and inactiveMs are used to "expire" the session, but can be overridden in the constructor. Pass 0 to disable expiration timers.
43
43
  */
44
44
  constructor (opts) {
45
- this.setup(opts)
46
- }
45
+ const { agentIdentifier, key, storage } = opts
47
46
 
48
- setup ({ agentIdentifier, key, storage, value = generateRandomHexString(16), expiresMs = DEFAULT_EXPIRES_MS, inactiveMs = DEFAULT_INACTIVE_MS }) {
49
47
  if (!agentIdentifier || !key || !storage) {
50
48
  throw new Error(`Missing required field(s):${!agentIdentifier ? ' agentID' : ''}${!key ? ' key' : ''}${!storage ? ' storage' : ''}`)
51
49
  }
@@ -53,20 +51,24 @@ export class SessionEntity {
53
51
  this.storage = storage
54
52
  this.state = {}
55
53
 
56
- this.sync(model)
57
-
58
54
  // key is intended to act as the k=v pair
59
55
  this.key = key
56
+
57
+ this.ee = ee.get(agentIdentifier)
58
+ wrapEvents(this.ee)
59
+ this.setup(opts)
60
+ }
61
+
62
+ setup ({ value = generateRandomHexString(16), expiresMs = DEFAULT_EXPIRES_MS, inactiveMs = DEFAULT_INACTIVE_MS }) {
63
+ this.state = {}
64
+ this.sync(model)
65
+
60
66
  // value is intended to act as the primary value of the k=v pair
61
67
  this.state.value = value
62
68
 
63
69
  this.expiresMs = expiresMs
64
70
  this.inactiveMs = inactiveMs
65
71
 
66
- this.ee = ee.get(agentIdentifier)
67
-
68
- wrapEvents(this.ee)
69
-
70
72
  // the first time the session entity class is instantiated, we check the storage API for an existing
71
73
  // object. If it exists, the values inside the object are used to inform the timers that run locally.
72
74
  // if the initial read is empty, it allows us to set a "fresh" "new" session immediately.
@@ -8,24 +8,25 @@ export class FirstPartyCookies {
8
8
  var match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'))
9
9
  if (match) return match[2]
10
10
  } catch (err) {
11
+ // Error is ignored
11
12
  return ''
12
13
  }
13
14
  }
14
15
 
15
16
  set (key, value) {
16
17
  try {
17
- const cookie = `${key}=${value}; Domain=${domain}; Path=/`
18
+ const cookie = `${key}=${value}; Domain=${this.domain}; Path=/`
18
19
  document.cookie = cookie
19
20
  } catch (err) {
20
- return
21
+ // Error is ignored
21
22
  }
22
23
  }
23
24
 
24
25
  remove (key) {
25
26
  try {
26
- return document.cookie = `${key}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; Domain=${domain}; Path=/`
27
+ document.cookie = `${key}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; Domain=${this.domain}; Path=/`
27
28
  } catch (err) {
28
- return
29
+ // Error is ignored
29
30
  }
30
31
  }
31
32
  }
@@ -5,6 +5,7 @@ export class LocalStorage {
5
5
  // Cast it back to undefined if it doesnt exist
6
6
  return localStorage.getItem(key) || undefined
7
7
  } catch (err) {
8
+ // Error is ignored
8
9
  return ''
9
10
  }
10
11
  }
@@ -14,7 +15,7 @@ export class LocalStorage {
14
15
  if (value === undefined || value === null) return this.remove(key)
15
16
  return localStorage.setItem(key, value)
16
17
  } catch (err) {
17
- return
18
+ // Error is ignored
18
19
  }
19
20
  }
20
21
 
@@ -22,7 +23,7 @@ export class LocalStorage {
22
23
  try {
23
24
  localStorage.removeItem(key)
24
25
  } catch (err) {
25
- return
26
+ // Error is ignored
26
27
  }
27
28
  }
28
29
  }