@newrelic/browser-agent 1.232.0 → 1.233.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 (273) hide show
  1. package/dist/cjs/cdn/polyfills.js +5 -2
  2. package/dist/cjs/common/config/state/configurable.js +15 -26
  3. package/dist/cjs/common/config/state/info.js +1 -1
  4. package/dist/cjs/common/config/state/init.js +101 -56
  5. package/dist/cjs/common/config/state/loader-config.js +1 -1
  6. package/dist/cjs/common/config/state/runtime.js +1 -5
  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/drain/drain.js +1 -1
  10. package/dist/cjs/common/harvest/harvest-scheduler.js +30 -10
  11. package/dist/cjs/common/harvest/harvest.js +119 -55
  12. package/dist/cjs/common/session/session-entity.js +35 -22
  13. package/dist/cjs/common/session/session-entity.test.js +73 -49
  14. package/dist/cjs/common/timer/interaction-timer.js +9 -12
  15. package/dist/cjs/common/url/canonicalize-url.js +32 -0
  16. package/dist/cjs/common/url/canonicalize-url.test.js +42 -0
  17. package/dist/cjs/common/url/clean-url.js +10 -3
  18. package/dist/cjs/common/url/protocol.test.js +0 -1
  19. package/dist/cjs/common/util/feature-flags.js +2 -1
  20. package/dist/cjs/common/util/global-scope.js +4 -2
  21. package/dist/cjs/common/util/submit-data.js +57 -18
  22. package/dist/cjs/common/wrap/wrap-fetch.js +1 -3
  23. package/dist/cjs/common/wrap/wrap-function.js +1 -3
  24. package/dist/cjs/common/wrap/wrap-promise.js +1 -1
  25. package/dist/cjs/features/ajax/aggregate/index.js +2 -2
  26. package/dist/cjs/features/ajax/instrument/index.js +1 -1
  27. package/dist/cjs/features/jserrors/aggregate/canonical-function-name.js +12 -4
  28. package/dist/cjs/features/jserrors/aggregate/compute-stack-trace.js +93 -10
  29. package/dist/cjs/features/jserrors/aggregate/compute-stack-trace.test.js +164 -38
  30. package/dist/cjs/features/jserrors/aggregate/index.js +29 -48
  31. package/dist/cjs/features/jserrors/instrument/index.js +0 -2
  32. package/dist/cjs/features/metrics/aggregate/framework-detection.js +67 -0
  33. package/dist/cjs/features/metrics/aggregate/framework-detection.test.js +137 -0
  34. package/dist/cjs/features/metrics/aggregate/index.js +7 -3
  35. package/dist/cjs/features/metrics/aggregate/polyfill-detection.es5.js +14 -0
  36. package/dist/cjs/features/metrics/aggregate/polyfill-detection.es5.test.js +17 -0
  37. package/dist/cjs/features/metrics/aggregate/polyfill-detection.js +53 -0
  38. package/dist/cjs/features/metrics/aggregate/polyfill-detection.test.js +165 -0
  39. package/dist/cjs/features/page_action/aggregate/index.js +2 -2
  40. package/dist/cjs/features/page_view_event/aggregate/index.js +6 -3
  41. package/dist/cjs/features/page_view_timing/aggregate/index.js +2 -2
  42. package/dist/cjs/features/session_replay/aggregate/index.js +333 -0
  43. package/dist/cjs/features/session_replay/constants.js +9 -0
  44. package/dist/cjs/features/session_replay/index.js +12 -0
  45. package/dist/cjs/features/session_replay/instrument/index.js +29 -0
  46. package/dist/cjs/features/session_trace/aggregate/index.js +163 -164
  47. package/dist/cjs/features/session_trace/constants.js +2 -9
  48. package/dist/cjs/features/session_trace/instrument/index.js +24 -66
  49. package/dist/cjs/features/spa/aggregate/index.js +2 -2
  50. package/dist/cjs/features/utils/agent-session.js +1 -2
  51. package/dist/cjs/features/utils/aggregate-base.js +64 -0
  52. package/dist/cjs/features/utils/feature-base.js +0 -31
  53. package/dist/cjs/features/utils/handler-cache.js +3 -4
  54. package/dist/cjs/features/utils/instrument-base.js +42 -10
  55. package/dist/cjs/features/utils/{lazy-loader.js → lazy-feature-loader.js} +4 -2
  56. package/dist/cjs/loaders/agent.js +1 -1
  57. package/dist/cjs/loaders/api/apiAsync.js +3 -1
  58. package/dist/cjs/loaders/configure/configure.js +3 -3
  59. package/dist/cjs/loaders/features/featureDependencies.js +0 -12
  60. package/dist/cjs/loaders/features/features.js +3 -1
  61. package/dist/cjs/loaders/micro-agent.js +6 -6
  62. package/dist/esm/cdn/polyfills.js +5 -2
  63. package/dist/esm/common/config/state/configurable.js +14 -24
  64. package/dist/esm/common/config/state/info.js +2 -2
  65. package/dist/esm/common/config/state/init.js +102 -57
  66. package/dist/esm/common/config/state/loader-config.js +2 -2
  67. package/dist/esm/common/config/state/runtime.js +2 -4
  68. package/dist/esm/common/constants/env.cdn.js +1 -1
  69. package/dist/esm/common/constants/env.npm.js +1 -1
  70. package/dist/esm/common/drain/drain.js +1 -1
  71. package/dist/esm/common/harvest/harvest-scheduler.js +30 -10
  72. package/dist/esm/common/harvest/harvest.js +121 -56
  73. package/dist/esm/common/session/session-entity.js +35 -22
  74. package/dist/esm/common/session/session-entity.test.js +73 -49
  75. package/dist/esm/common/timer/interaction-timer.js +9 -12
  76. package/dist/esm/common/url/canonicalize-url.js +27 -0
  77. package/dist/esm/common/url/canonicalize-url.test.js +38 -0
  78. package/dist/esm/common/url/clean-url.js +10 -3
  79. package/dist/esm/common/url/protocol.test.js +0 -1
  80. package/dist/esm/common/util/feature-flags.js +2 -1
  81. package/dist/esm/common/util/global-scope.js +1 -0
  82. package/dist/esm/common/util/submit-data.js +57 -18
  83. package/dist/esm/common/wrap/wrap-fetch.js +1 -2
  84. package/dist/esm/common/wrap/wrap-function.js +1 -2
  85. package/dist/esm/common/wrap/wrap-promise.js +1 -1
  86. package/dist/esm/features/ajax/aggregate/index.js +2 -2
  87. package/dist/esm/features/ajax/instrument/index.js +1 -1
  88. package/dist/esm/features/jserrors/aggregate/canonical-function-name.js +12 -4
  89. package/dist/esm/features/jserrors/aggregate/compute-stack-trace.js +93 -10
  90. package/dist/esm/features/jserrors/aggregate/compute-stack-trace.test.js +149 -25
  91. package/dist/esm/features/jserrors/aggregate/index.js +30 -48
  92. package/dist/esm/features/jserrors/instrument/index.js +0 -1
  93. package/dist/esm/features/metrics/aggregate/framework-detection.js +61 -0
  94. package/dist/esm/features/metrics/aggregate/framework-detection.test.js +133 -0
  95. package/dist/esm/features/metrics/aggregate/index.js +7 -3
  96. package/dist/esm/features/metrics/aggregate/polyfill-detection.es5.js +8 -0
  97. package/dist/esm/features/metrics/aggregate/polyfill-detection.es5.test.js +15 -0
  98. package/dist/esm/features/metrics/aggregate/polyfill-detection.js +47 -0
  99. package/dist/esm/features/metrics/aggregate/polyfill-detection.test.js +163 -0
  100. package/dist/esm/features/page_action/aggregate/index.js +2 -2
  101. package/dist/esm/features/page_view_event/aggregate/index.js +6 -3
  102. package/dist/esm/features/page_view_timing/aggregate/index.js +2 -2
  103. package/dist/esm/features/session_replay/aggregate/index.js +327 -0
  104. package/dist/esm/features/session_replay/constants.js +2 -0
  105. package/dist/esm/features/session_replay/index.js +12 -0
  106. package/dist/esm/features/session_replay/instrument/index.js +21 -0
  107. package/dist/esm/features/session_trace/aggregate/index.js +163 -163
  108. package/dist/esm/features/session_trace/constants.js +1 -5
  109. package/dist/esm/features/session_trace/instrument/index.js +24 -66
  110. package/dist/esm/features/spa/aggregate/index.js +2 -2
  111. package/dist/esm/features/utils/agent-session.js +1 -2
  112. package/dist/esm/features/utils/aggregate-base.js +57 -0
  113. package/dist/esm/features/utils/feature-base.js +1 -32
  114. package/dist/esm/features/utils/handler-cache.js +3 -4
  115. package/dist/esm/features/utils/instrument-base.js +42 -10
  116. package/dist/esm/features/utils/{lazy-loader.js → lazy-feature-loader.js} +3 -1
  117. package/dist/esm/loaders/agent.js +1 -1
  118. package/dist/esm/loaders/api/apiAsync.js +3 -1
  119. package/dist/esm/loaders/configure/configure.js +3 -3
  120. package/dist/esm/loaders/features/featureDependencies.js +0 -11
  121. package/dist/esm/loaders/features/features.js +3 -1
  122. package/dist/esm/loaders/micro-agent.js +6 -6
  123. package/dist/types/common/config/state/configurable.d.ts +1 -3
  124. package/dist/types/common/config/state/configurable.d.ts.map +1 -1
  125. package/dist/types/common/config/state/init.d.ts.map +1 -1
  126. package/dist/types/common/config/state/runtime.d.ts.map +1 -1
  127. package/dist/types/common/harvest/harvest-scheduler.d.ts.map +1 -1
  128. package/dist/types/common/harvest/harvest.d.ts +37 -34
  129. package/dist/types/common/harvest/harvest.d.ts.map +1 -1
  130. package/dist/types/common/session/session-entity.d.ts +6 -3
  131. package/dist/types/common/session/session-entity.d.ts.map +1 -1
  132. package/dist/types/common/timer/interaction-timer.d.ts +2 -1
  133. package/dist/types/common/timer/interaction-timer.d.ts.map +1 -1
  134. package/dist/types/common/url/canonicalize-url.d.ts +9 -0
  135. package/dist/types/common/url/canonicalize-url.d.ts.map +1 -0
  136. package/dist/types/common/url/clean-url.d.ts +7 -1
  137. package/dist/types/common/url/clean-url.d.ts.map +1 -1
  138. package/dist/types/common/util/feature-flags.d.ts.map +1 -1
  139. package/dist/types/common/util/global-scope.d.ts +1 -0
  140. package/dist/types/common/util/global-scope.d.ts.map +1 -1
  141. package/dist/types/common/util/submit-data.d.ts +40 -14
  142. package/dist/types/common/util/submit-data.d.ts.map +1 -1
  143. package/dist/types/common/wrap/wrap-fetch.d.ts.map +1 -1
  144. package/dist/types/common/wrap/wrap-function.d.ts.map +1 -1
  145. package/dist/types/features/ajax/aggregate/index.d.ts +2 -2
  146. package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
  147. package/dist/types/features/jserrors/aggregate/canonical-function-name.d.ts +8 -1
  148. package/dist/types/features/jserrors/aggregate/canonical-function-name.d.ts.map +1 -1
  149. package/dist/types/features/jserrors/aggregate/compute-stack-trace.d.ts +48 -19
  150. package/dist/types/features/jserrors/aggregate/compute-stack-trace.d.ts.map +1 -1
  151. package/dist/types/features/jserrors/aggregate/index.d.ts +14 -5
  152. package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
  153. package/dist/types/features/jserrors/instrument/index.d.ts.map +1 -1
  154. package/dist/types/features/metrics/aggregate/framework-detection.d.ts.map +1 -0
  155. package/dist/types/features/metrics/aggregate/index.d.ts +2 -2
  156. package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
  157. package/dist/types/features/metrics/aggregate/polyfill-detection.d.ts +6 -0
  158. package/dist/types/features/metrics/aggregate/polyfill-detection.d.ts.map +1 -0
  159. package/dist/types/features/metrics/aggregate/polyfill-detection.es5.d.ts +7 -0
  160. package/dist/types/features/metrics/aggregate/polyfill-detection.es5.d.ts.map +1 -0
  161. package/dist/types/features/page_action/aggregate/index.d.ts +3 -3
  162. package/dist/types/features/page_action/aggregate/index.d.ts.map +1 -1
  163. package/dist/types/features/page_view_event/aggregate/index.d.ts +2 -2
  164. package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
  165. package/dist/types/features/page_view_timing/aggregate/index.d.ts +2 -2
  166. package/dist/types/features/page_view_timing/aggregate/index.d.ts.map +1 -1
  167. package/dist/types/features/session_replay/aggregate/index.d.ts +96 -0
  168. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -0
  169. package/dist/types/features/session_replay/constants.d.ts +2 -0
  170. package/dist/types/features/session_replay/constants.d.ts.map +1 -0
  171. package/dist/types/features/session_replay/index.d.ts +2 -0
  172. package/dist/types/features/session_replay/index.d.ts.map +1 -0
  173. package/dist/types/features/session_replay/instrument/index.d.ts +6 -0
  174. package/dist/types/features/session_replay/instrument/index.d.ts.map +1 -0
  175. package/dist/types/features/session_trace/aggregate/index.d.ts +8 -57
  176. package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
  177. package/dist/types/features/session_trace/constants.d.ts +0 -3
  178. package/dist/types/features/session_trace/constants.d.ts.map +1 -1
  179. package/dist/types/features/session_trace/instrument/index.d.ts +1 -3
  180. package/dist/types/features/session_trace/instrument/index.d.ts.map +1 -1
  181. package/dist/types/features/spa/aggregate/index.d.ts +2 -2
  182. package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
  183. package/dist/types/features/utils/agent-session.d.ts.map +1 -1
  184. package/dist/types/features/utils/aggregate-base.d.ts +11 -0
  185. package/dist/types/features/utils/aggregate-base.d.ts.map +1 -0
  186. package/dist/types/features/utils/feature-base.d.ts +0 -5
  187. package/dist/types/features/utils/feature-base.d.ts.map +1 -1
  188. package/dist/types/features/utils/handler-cache.d.ts.map +1 -1
  189. package/dist/types/features/utils/instrument-base.d.ts +3 -1
  190. package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
  191. package/dist/types/features/utils/{lazy-loader.d.ts → lazy-feature-loader.d.ts} +2 -2
  192. package/dist/types/features/utils/lazy-feature-loader.d.ts.map +1 -0
  193. package/dist/types/loaders/configure/configure.d.ts.map +1 -1
  194. package/dist/types/loaders/features/featureDependencies.d.ts +0 -1
  195. package/dist/types/loaders/features/featureDependencies.d.ts.map +1 -1
  196. package/dist/types/loaders/features/features.d.ts +1 -0
  197. package/dist/types/loaders/features/features.d.ts.map +1 -1
  198. package/package.json +31 -22
  199. package/src/cdn/polyfills.js +4 -1
  200. package/src/common/config/state/configurable.js +18 -24
  201. package/src/common/config/state/info.js +2 -2
  202. package/src/common/config/state/init.js +62 -28
  203. package/src/common/config/state/loader-config.js +2 -2
  204. package/src/common/config/state/runtime.js +2 -4
  205. package/src/common/drain/drain.js +1 -1
  206. package/src/common/harvest/harvest-scheduler.js +35 -10
  207. package/src/common/harvest/harvest.js +73 -50
  208. package/src/common/session/session-entity.js +34 -23
  209. package/src/common/session/session-entity.test.js +57 -51
  210. package/src/common/timer/interaction-timer.js +9 -12
  211. package/src/common/url/canonicalize-url.js +28 -0
  212. package/src/common/url/canonicalize-url.test.js +34 -0
  213. package/src/common/url/clean-url.js +10 -3
  214. package/src/common/url/protocol.test.js +0 -1
  215. package/src/common/util/feature-flags.js +2 -2
  216. package/src/common/util/global-scope.js +2 -0
  217. package/src/common/util/submit-data.js +28 -17
  218. package/src/common/wrap/wrap-fetch.js +1 -2
  219. package/src/common/wrap/wrap-function.js +1 -2
  220. package/src/common/wrap/wrap-promise.js +1 -1
  221. package/src/features/ajax/aggregate/index.js +2 -2
  222. package/src/features/ajax/instrument/index.js +1 -1
  223. package/src/features/jserrors/aggregate/canonical-function-name.js +12 -4
  224. package/src/features/jserrors/aggregate/compute-stack-trace.js +85 -11
  225. package/src/features/jserrors/aggregate/compute-stack-trace.test.js +141 -24
  226. package/src/features/jserrors/aggregate/index.js +28 -52
  227. package/src/features/jserrors/instrument/index.js +0 -1
  228. package/src/features/metrics/aggregate/framework-detection.js +73 -0
  229. package/src/features/metrics/aggregate/framework-detection.test.js +201 -0
  230. package/src/features/metrics/aggregate/index.js +8 -3
  231. package/src/features/metrics/aggregate/polyfill-detection.es5.js +9 -0
  232. package/src/features/metrics/aggregate/polyfill-detection.es5.test.js +16 -0
  233. package/src/features/metrics/aggregate/polyfill-detection.js +48 -0
  234. package/src/features/metrics/aggregate/polyfill-detection.test.js +163 -0
  235. package/src/features/page_action/aggregate/index.js +2 -2
  236. package/src/features/page_view_event/aggregate/index.js +5 -5
  237. package/src/features/page_view_timing/aggregate/index.js +2 -2
  238. package/src/features/session_replay/aggregate/index.js +314 -0
  239. package/src/features/session_replay/constants.js +3 -0
  240. package/src/features/session_replay/index.js +12 -0
  241. package/src/features/session_replay/instrument/index.js +22 -0
  242. package/src/features/session_trace/aggregate/index.js +148 -188
  243. package/src/features/session_trace/constants.js +0 -4
  244. package/src/features/session_trace/instrument/index.js +17 -69
  245. package/src/features/spa/aggregate/index.js +2 -2
  246. package/src/features/utils/agent-session.js +1 -2
  247. package/src/features/utils/aggregate-base.js +51 -0
  248. package/src/features/utils/feature-base.js +1 -31
  249. package/src/features/utils/handler-cache.js +3 -4
  250. package/src/features/utils/instrument-base.js +40 -8
  251. package/src/features/utils/{lazy-loader.js → lazy-feature-loader.js} +3 -1
  252. package/src/loaders/agent.js +1 -1
  253. package/src/loaders/api/apiAsync.js +1 -1
  254. package/src/loaders/configure/configure.js +4 -3
  255. package/src/loaders/features/featureDependencies.js +0 -12
  256. package/src/loaders/features/features.js +3 -1
  257. package/src/loaders/micro-agent.js +4 -4
  258. package/dist/cjs/common/metrics/framework-detection.js +0 -72
  259. package/dist/cjs/common/util/user-agent.js +0 -57
  260. package/dist/cjs/common/window/supports-performance-observer.js +0 -15
  261. package/dist/esm/common/metrics/framework-detection.js +0 -66
  262. package/dist/esm/common/util/user-agent.js +0 -48
  263. package/dist/esm/common/window/supports-performance-observer.js +0 -9
  264. package/dist/types/common/metrics/framework-detection.d.ts.map +0 -1
  265. package/dist/types/common/util/user-agent.d.ts +0 -5
  266. package/dist/types/common/util/user-agent.d.ts.map +0 -1
  267. package/dist/types/common/window/supports-performance-observer.d.ts +0 -2
  268. package/dist/types/common/window/supports-performance-observer.d.ts.map +0 -1
  269. package/dist/types/features/utils/lazy-loader.d.ts.map +0 -1
  270. package/src/common/metrics/framework-detection.js +0 -71
  271. package/src/common/util/user-agent.js +0 -56
  272. package/src/common/window/supports-performance-observer.js +0 -10
  273. /package/dist/types/{common/metrics → features/metrics/aggregate}/framework-detection.d.ts +0 -0
@@ -41,27 +41,30 @@ describe('constructor', () => {
41
41
  expect(session).toMatchObject({
42
42
  agentIdentifier: expect.any(String),
43
43
  key: expect.any(String),
44
- value: expect.any(String),
45
44
  expiresMs: expect.any(Number),
46
- expiresAt: expect.any(Number),
47
45
  expiresTimer: expect.any(Object),
48
46
  inactiveMs: expect.any(Number),
49
- inactiveAt: expect.any(Number),
50
47
  inactiveTimer: expect.any(Object),
51
48
  isNew: expect.any(Boolean),
52
- sessionReplayActive: expect.any(Boolean),
53
- sessionTraceActive: expect.any(Boolean),
54
- storage: expect.any(Object)
49
+ storage: expect.any(Object),
50
+ state: expect.objectContaining({
51
+ value: expect.any(String),
52
+ expiresAt: expect.any(Number),
53
+ inactiveAt: expect.any(Number),
54
+ sessionReplay: expect.any(Number),
55
+ sessionTraceActive: expect.any(Boolean)
56
+ })
55
57
  })
56
58
  })
57
59
 
58
60
  test('can use sane defaults', () => {
59
61
  const session = new SessionEntity({ agentIdentifier, key })
60
- expect(session).toEqual(expect.objectContaining({
62
+ expect(session.state).toEqual(expect.objectContaining({
61
63
  value: expect.any(String),
62
64
  expiresAt: expect.any(Number),
63
65
  inactiveAt: expect.any(Number),
64
- sessionReplayActive: expect.any(Boolean),
66
+ updatedAt: expect.any(Number),
67
+ sessionReplay: expect.any(Number),
65
68
  sessionTraceActive: expect.any(Boolean)
66
69
  }))
67
70
  })
@@ -76,47 +79,47 @@ describe('constructor', () => {
76
79
  const now = Date.now()
77
80
  jest.setSystemTime(now)
78
81
  const session = new SessionEntity({ agentIdentifier, key, expiresMs: 100 })
79
- expect(session.expiresAt).toEqual(now + 100)
82
+ expect(session.state.expiresAt).toEqual(now + 100)
80
83
  })
81
84
 
82
85
  test('expiresAt is the correct future timestamp - existing session', () => {
83
86
  const now = Date.now()
84
87
  jest.setSystemTime(now)
85
- const existingData = new LocalMemory({ [`${PREFIX}_${key}`]: { value, expiresAt: now + 5000, inactiveAt: Infinity } })
88
+ const existingData = new LocalMemory({ [`${PREFIX}_${key}`]: { value, expiresAt: now + 5000, inactiveAt: Infinity, updatedAt: now, sessionReplay: 0, sessionTraceActive: false, custom: {} } })
86
89
  const session = new SessionEntity({ agentIdentifier, key, expiresMs: 100, storageAPI: existingData })
87
- expect(session.expiresAt).toEqual(now + 5000)
90
+ expect(session.state.expiresAt).toEqual(now + 5000)
88
91
  })
89
92
 
90
93
  test('expiresAt never expires if 0', () => {
91
94
  const session = new SessionEntity({ agentIdentifier, key, expiresMs: 0 })
92
- expect(session.expiresAt).toEqual(Infinity)
95
+ expect(session.state.expiresAt).toEqual(Infinity)
93
96
  })
94
97
 
95
98
  test('inactiveAt is the correct future timestamp - new session', () => {
96
99
  const now = Date.now()
97
100
  jest.setSystemTime(now)
98
101
  const session = new SessionEntity({ agentIdentifier, key, inactiveMs: 100 })
99
- expect(session.inactiveAt).toEqual(now + 100)
102
+ expect(session.state.inactiveAt).toEqual(now + 100)
100
103
  })
101
104
 
102
105
  test('inactiveAt is the correct future timestamp - existing session', () => {
103
106
  const now = Date.now()
104
107
  jest.setSystemTime(now)
105
- const existingData = new LocalMemory({ [`${PREFIX}_${key}`]: { value, inactiveAt: now + 5000, expiresAt: Infinity } })
108
+ const existingData = new LocalMemory({ [`${PREFIX}_${key}`]: { value, inactiveAt: now + 5000, expiresAt: Infinity, updatedAt: now, sessionReplay: 0, sessionTraceActive: false, custom: {} } })
106
109
  const session = new SessionEntity({ agentIdentifier, key, inactiveMs: 100, storageAPI: existingData })
107
- expect(session.inactiveAt).toEqual(now + 5000)
110
+ expect(session.state.inactiveAt).toEqual(now + 5000)
108
111
  })
109
112
 
110
113
  test('inactiveAt never expires if 0', () => {
111
114
  const session = new SessionEntity({ agentIdentifier, key, inactiveMs: 0 })
112
- expect(session.inactiveAt).toEqual(Infinity)
115
+ expect(session.state.inactiveAt).toEqual(Infinity)
113
116
  })
114
117
 
115
118
  test('should handle isNew', () => {
116
119
  const newSession = new SessionEntity({ agentIdentifier, key, expiresMs: 10 })
117
120
  expect(newSession.isNew).toBeTruthy()
118
121
 
119
- const storageAPI = new LocalMemory({ [`${PREFIX}_${key}`]: { value, expiresAt: Infinity, inactiveAt: Infinity } })
122
+ const storageAPI = new LocalMemory({ [`${PREFIX}_${key}`]: { value, expiresAt: Infinity, inactiveAt: Infinity, updatedAt: Date.now(), sessionReplay: 0, sessionTraceActive: false, custom: {} } })
120
123
  const existingSession = new SessionEntity({ agentIdentifier, key, expiresMs: 10, storageAPI })
121
124
  expect(existingSession.isNew).toBeFalsy()
122
125
  })
@@ -125,11 +128,12 @@ describe('constructor', () => {
125
128
  // missing required fields
126
129
  const storageAPI = new LocalMemory({ [`${PREFIX}_${key}`]: { invalid_fields: true } })
127
130
  const session = new SessionEntity({ agentIdentifier, key, storageAPI })
128
- expect(session).toEqual(expect.objectContaining({
131
+ expect(session.state).toEqual(expect.objectContaining({
129
132
  value: expect.any(String),
130
133
  expiresAt: expect.any(Number),
131
134
  inactiveAt: expect.any(Number),
132
- sessionReplayActive: expect.any(Boolean),
135
+ updatedAt: expect.any(Number),
136
+ sessionReplay: expect.any(Number),
133
137
  sessionTraceActive: expect.any(Boolean)
134
138
  }))
135
139
  })
@@ -139,11 +143,12 @@ describe('constructor', () => {
139
143
  jest.setSystemTime(now)
140
144
  const storageAPI = new LocalMemory({ [`${PREFIX}_${key}`]: { value, expiresAt: now - 100, inactiveAt: Infinity } })
141
145
  const session = new SessionEntity({ agentIdentifier, key, storageAPI })
142
- expect(session).toEqual(expect.objectContaining({
146
+ expect(session.state).toEqual(expect.objectContaining({
143
147
  value: expect.any(String),
144
148
  expiresAt: expect.any(Number),
145
149
  inactiveAt: expect.any(Number),
146
- sessionReplayActive: expect.any(Boolean),
150
+ updatedAt: expect.any(Number),
151
+ sessionReplay: expect.any(Number),
147
152
  sessionTraceActive: expect.any(Boolean)
148
153
  }))
149
154
  })
@@ -153,11 +158,12 @@ describe('constructor', () => {
153
158
  jest.setSystemTime(now)
154
159
  const storageAPI = new LocalMemory({ [`${PREFIX}_${key}`]: { value, inactiveAt: now - 100, expiresAt: Infinity } })
155
160
  const session = new SessionEntity({ agentIdentifier, key, storageAPI })
156
- expect(session).toEqual(expect.objectContaining({
161
+ expect(session.state).toEqual(expect.objectContaining({
157
162
  value: expect.any(String),
158
163
  expiresAt: expect.any(Number),
159
164
  inactiveAt: expect.any(Number),
160
- sessionReplayActive: expect.any(Boolean),
165
+ updatedAt: expect.any(Number),
166
+ sessionReplay: expect.any(Number),
161
167
  sessionTraceActive: expect.any(Boolean)
162
168
  }))
163
169
  })
@@ -169,10 +175,10 @@ describe('reset()', () => {
169
175
  jest.setSystemTime(now)
170
176
  const session = new SessionEntity({ agentIdentifier, key, expiresMs: 10 })
171
177
  const sessionVal = session.value
172
- expect(session.value).toBeTruthy()
178
+ expect(session.state.value).toBeTruthy()
173
179
  session.reset()
174
- expect(session.value).toBeTruthy()
175
- expect(session.value).not.toEqual(sessionVal)
180
+ expect(session.state.value).toBeTruthy()
181
+ expect(session.state.value).not.toEqual(sessionVal)
176
182
  })
177
183
 
178
184
  test('custom data should be wiped on reset', () => {
@@ -180,12 +186,12 @@ describe('reset()', () => {
180
186
  jest.setSystemTime(now)
181
187
  const session = new SessionEntity({ agentIdentifier, key, expiresMs: 10 })
182
188
  session.syncCustomAttribute('test', 123)
183
- expect(session.custom.test).toEqual(123)
189
+ expect(session.state.custom.test).toEqual(123)
184
190
  expect(session.read().custom.test).toEqual(123)
185
191
 
186
192
  // simulate a timer expiring
187
193
  session.reset()
188
- expect(session.custom?.test).toEqual(undefined)
194
+ expect(session.state.custom?.test).toEqual(undefined)
189
195
  expect(session.read()?.custom?.test).toEqual(undefined)
190
196
  })
191
197
  })
@@ -199,13 +205,13 @@ describe('read()', () => {
199
205
  value: expect.any(String),
200
206
  expiresAt: expect.any(Number),
201
207
  inactiveAt: expect.any(Number),
202
- sessionReplayActive: expect.any(Boolean),
208
+ sessionReplay: expect.any(Number),
203
209
  sessionTraceActive: expect.any(Boolean)
204
210
  }))
205
211
  })
206
212
 
207
213
  test('"pre-existing" sessions get data from read()', () => {
208
- const storageAPI = new LocalMemory({ [`${PREFIX}_${key}`]: { value, expiresAt: Infinity, inactiveAt: Infinity } })
214
+ const storageAPI = new LocalMemory({ [`${PREFIX}_${key}`]: { value, expiresAt: Infinity, inactiveAt: Infinity, updatedAt: Date.now(), sessionReplay: 0, sessionTraceActive: false, custom: {} } })
209
215
  const session = new SessionEntity({ agentIdentifier, key, storageAPI })
210
216
  expect(session.isNew).toBeFalsy()
211
217
  expect(session.read()).toEqual(expect.objectContaining({
@@ -219,20 +225,20 @@ describe('read()', () => {
219
225
  describe('write()', () => {
220
226
  test('write() sets data to top-level wrapper', () => {
221
227
  const session = new SessionEntity({ agentIdentifier, key })
222
- expect(session.value).not.toEqual(value)
223
- expect(session.expiresAt).not.toEqual(Infinity)
224
- expect(session.inactiveAt).not.toEqual(Infinity)
225
- session.write({ value, expiresAt: Infinity, inactiveAt: Infinity })
226
- expect(session.value).toEqual(value)
227
- expect(session.expiresAt).toEqual(Infinity)
228
- expect(session.inactiveAt).toEqual(Infinity)
228
+ expect(session.state.value).not.toEqual(value)
229
+ expect(session.state.expiresAt).not.toEqual(Infinity)
230
+ expect(session.state.inactiveAt).not.toEqual(Infinity)
231
+ session.write({ ...session.state, value, expiresAt: Infinity, inactiveAt: Infinity })
232
+ expect(session.state.value).toEqual(value)
233
+ expect(session.state.expiresAt).toEqual(Infinity)
234
+ expect(session.state.inactiveAt).toEqual(Infinity)
229
235
  })
230
236
 
231
237
  test('write() sets data that read() can access', () => {
232
238
  const now = Date.now()
233
239
  jest.setSystemTime(now)
234
240
  const session = new SessionEntity({ agentIdentifier, key })
235
- session.write({ value, expiresAt: now + 100, inactiveAt: now + 100 })
241
+ session.write({ ...session.state, value, expiresAt: now + 100, inactiveAt: now + 100 })
236
242
  const read = session.read()
237
243
  expect(read.value).toEqual(value)
238
244
  expect(read.expiresAt).toEqual(now + 100)
@@ -259,30 +265,30 @@ describe('refresh()', () => {
259
265
  const now = Date.now()
260
266
  jest.setSystemTime(now)
261
267
  const session = new SessionEntity({ agentIdentifier, key, inactiveMs: 100 })
262
- expect(session.inactiveAt).toEqual(now + 100)
268
+ expect(session.state.inactiveAt).toEqual(now + 100)
263
269
  jest.setSystemTime(now + 1000)
264
270
  session.refresh()
265
- expect(session.inactiveAt).toEqual(now + 100 + 1000)
271
+ expect(session.state.inactiveAt).toEqual(now + 100 + 1000)
266
272
  })
267
273
 
268
274
  test('refresh resets the entity if expiresTimer is invalid', () => {
269
275
  const now = Date.now()
270
276
  jest.setSystemTime(now)
271
277
  const session = new SessionEntity({ agentIdentifier, key, value })
272
- expect(session.value).toEqual(value)
273
- session.write({ ...session.read(), expiresAt: now - 1 })
278
+ expect(session.state.value).toEqual(value)
279
+ session.write({ ...session.state, expiresAt: now - 1 })
274
280
  session.refresh()
275
- expect(session.value).not.toEqual(value)
281
+ expect(session.state.value).not.toEqual(value)
276
282
  })
277
283
 
278
284
  test('refresh resets the entity if inactiveTimer is invalid', () => {
279
285
  const now = Date.now()
280
286
  jest.setSystemTime(now)
281
287
  const session = new SessionEntity({ agentIdentifier, key, value })
282
- expect(session.value).toEqual(value)
283
- session.write({ ...session.read(), inactiveAt: now - 1 })
288
+ expect(session.state.value).toEqual(value)
289
+ session.write({ ...session.state, inactiveAt: now - 1 })
284
290
  session.refresh()
285
- expect(session.value).not.toEqual(value)
291
+ expect(session.state.value).not.toEqual(value)
286
292
  })
287
293
  })
288
294
 
@@ -292,20 +298,20 @@ describe('syncCustomAttribute()', () => {
292
298
 
293
299
  // if custom has never been set, and a "delete" action is triggered, do nothing
294
300
  session.syncCustomAttribute('test', null)
295
- expect(session?.custom?.test).toEqual(undefined)
301
+ expect(session?.state?.custom?.test).toEqual(undefined)
296
302
 
297
303
  session.syncCustomAttribute('test', 1)
298
- expect(session?.custom?.test).toEqual(1)
304
+ expect(session?.state?.custom?.test).toEqual(1)
299
305
 
300
306
  session.syncCustomAttribute('test', 'string')
301
- expect(session?.custom?.test).toEqual('string')
307
+ expect(session?.state?.custom?.test).toEqual('string')
302
308
 
303
309
  session.syncCustomAttribute('test', false)
304
- expect(session?.custom?.test).toEqual(false)
310
+ expect(session?.state?.custom?.test).toEqual(false)
305
311
 
306
312
  // null specifically deletes the object completely
307
313
  session.syncCustomAttribute('test', null)
308
- expect(session?.custom?.test).toEqual(undefined)
314
+ expect(session?.state?.custom?.test).toEqual(undefined)
309
315
  })
310
316
 
311
317
  test('Only runs in browser scope', () => {
@@ -6,8 +6,9 @@ import { isBrowserScope } from '../util/global-scope'
6
6
  export class InteractionTimer extends Timer {
7
7
  constructor (opts, ms) {
8
8
  super(opts, ms)
9
- this.onRefresh = opts.onRefresh
10
- this.onPause = opts.onPause
9
+ this.onPause = typeof opts.onPause === 'function' ? opts.onPause : () => { /* noop */ }
10
+ this.onRefresh = typeof opts.onRefresh === 'function' ? opts.onRefresh : () => { /* noop */ }
11
+ this.onResume = typeof opts.onResume === 'function' ? opts.onResume : () => { /* noop */ }
11
12
 
12
13
  // used by pause/resume
13
14
  this.remainingMs = undefined
@@ -42,7 +43,10 @@ export class InteractionTimer extends Timer {
42
43
  subscribeToVisibilityChange((state) => {
43
44
  if (state === 'hidden') this.pause()
44
45
  // vis change --> visible is treated like a new interaction with the page
45
- else this.refresh()
46
+ else {
47
+ this.onResume()
48
+ this.refresh()
49
+ }
46
50
  }, false, false, this.abortController?.signal)
47
51
  }
48
52
  }
@@ -53,7 +57,7 @@ export class InteractionTimer extends Timer {
53
57
  }
54
58
 
55
59
  pause () {
56
- this.onPause?.()
60
+ this.onPause()
57
61
  clearTimeout(this.timer)
58
62
  this.remainingMs = this.initialMs - (Date.now() - this.startTimestamp)
59
63
  }
@@ -63,13 +67,6 @@ export class InteractionTimer extends Timer {
63
67
  this.timer = this.create(cb, ms)
64
68
  this.startTimestamp = Date.now()
65
69
  this.remainingMs = undefined
66
- this.onRefresh?.()
70
+ this.onRefresh()
67
71
  }
68
-
69
- // NOT CURRENTLY UTILIZED BY ANYTHING
70
- // resume () {
71
- // if (!this.remainingMs || !this.isValid()) return
72
- // this.timer = this.create(this.cb, this.remainingMs)
73
- // this.remainingMs = undefined
74
- // }
75
72
  }
@@ -0,0 +1,28 @@
1
+ /*
2
+ * Copyright 2020 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import { initialLocation } from '../util/global-scope'
7
+ import { cleanURL } from './clean-url'
8
+
9
+ /**
10
+ * Converts a URL to its basic form without a query string or fragment. If the resulting URL is the same as the
11
+ * loader's origin URL, returns '<inline>'.
12
+ * @param {string} url - The URL to be canonicalized.
13
+ * @param {string} loaderOriginUrl - The origin URL of the agent loader, used for inline detection.
14
+ * @returns {string} The canonicalized URL, or '<inline>' if the URL matches the loader origin URL.
15
+ */
16
+ export function canonicalizeUrl (url) {
17
+ if (typeof url !== 'string') return ''
18
+
19
+ const cleanedUrl = cleanURL(url)
20
+ const cleanedGlobalScopeUrl = cleanURL(initialLocation)
21
+
22
+ // If the URL matches the origin URL of the loader, we assume it originated within an inline script.
23
+ if (cleanedUrl === cleanedGlobalScopeUrl) {
24
+ return '<inline>'
25
+ } else {
26
+ return cleanedUrl
27
+ }
28
+ }
@@ -0,0 +1,34 @@
1
+ afterEach(() => {
2
+ jest.resetModules()
3
+ })
4
+
5
+ test.each([null, undefined, 34])('returns empty string when url argument is %s', async (url) => {
6
+ const { canonicalizeUrl } = await import('./canonicalize-url')
7
+ expect(canonicalizeUrl(url)).toEqual('')
8
+ })
9
+
10
+ test('strips URLs of query strings and fragments', async () => {
11
+ jest.doMock('../util/global-scope', () => ({
12
+ initialLocation: 'http://different-domain.com/'
13
+ }))
14
+ const { canonicalizeUrl } = await import('./canonicalize-url')
15
+ expect(canonicalizeUrl('http://example.com/path?query=string#fragment')).toBe('http://example.com/path')
16
+ expect(canonicalizeUrl('https://www.example.com/path/to/file.html?param=value')).toBe('https://www.example.com/path/to/file.html')
17
+ expect(canonicalizeUrl('https://www.example.com/?param=value#fragment')).toBe('https://www.example.com/')
18
+ })
19
+
20
+ test('returns <inline> when matching the page URL of the loader', async () => {
21
+ jest.doMock('../util/global-scope', () => ({
22
+ initialLocation: 'http://example.com/'
23
+ }))
24
+ const { canonicalizeUrl } = await import('./canonicalize-url')
25
+ expect(canonicalizeUrl('http://example.com/')).toEqual('<inline>')
26
+ })
27
+
28
+ test('does not identify sub-paths of the loader origin as <inline>', async () => {
29
+ jest.doMock('../util/global-scope', () => ({
30
+ initialLocation: 'http://example.com/'
31
+ }))
32
+ const { canonicalizeUrl } = await import('./canonicalize-url')
33
+ expect(canonicalizeUrl('http://example.com/path/to/script.js')).not.toEqual('<inline>')
34
+ })
@@ -3,8 +3,15 @@
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
 
6
- var withHash = /([^?#]*)[^#]*(#[^?]*|$).*/
7
- var withoutHash = /([^?#]*)().*/
6
+ var patternWithHash = /([^?#]*)[^#]*(#[^?]*|$).*/
7
+ var patternWithoutHash = /([^?#]*)().*/
8
+
9
+ /**
10
+ * Cleans a URL by removing the query string and fragment (hash portion).
11
+ * @param {string} url - The original URL to be cleaned.
12
+ * @param {boolean} [keepHash=false] - Whether to preserve the hash portion of the URL.
13
+ * @returns {string} The cleaned URL.
14
+ */
8
15
  export function cleanURL (url, keepHash) {
9
- return url.replace(keepHash ? withHash : withoutHash, '$1$2')
16
+ return url.replace(keepHash ? patternWithHash : patternWithoutHash, '$1$2')
10
17
  }
@@ -1,4 +1,3 @@
1
- import { faker } from '@faker-js/faker'
2
1
  import { isFileProtocol } from './protocol'
3
2
 
4
3
  test('should return true when location url contains file protocol', () => {
@@ -12,7 +12,8 @@ const bucketMap = {
12
12
  stn: [FEATURE_NAMES.sessionTrace],
13
13
  err: [FEATURE_NAMES.jserrors, FEATURE_NAMES.metrics],
14
14
  ins: [FEATURE_NAMES.pageAction],
15
- spa: [FEATURE_NAMES.spa]
15
+ spa: [FEATURE_NAMES.spa],
16
+ sr: [FEATURE_NAMES.sessionReplay]
16
17
  }
17
18
 
18
19
  export function activateFeatures (flags, agentIdentifier) {
@@ -28,7 +29,6 @@ export function activateFeatures (flags, agentIdentifier) {
28
29
  if (activatedFeatures[flag]) {
29
30
  return
30
31
  }
31
-
32
32
  handle('feat-' + flag, [], undefined, bucketMap[flag], sharedEE)
33
33
  activatedFeatures[flag] = true
34
34
  })
@@ -20,6 +20,8 @@ export let globalScope = (() => {
20
20
  throw new Error('New Relic browser agent shutting down due to error: Unable to locate global scope. This is possibly due to code redefining browser global variables like "self" and "window".')
21
21
  })()
22
22
 
23
+ export const initialLocation = '' + globalScope.location
24
+
23
25
  /**
24
26
  * The below methods are only used for testing and should be removed once the
25
27
  * reliant tests are moved to Jest.
@@ -7,11 +7,13 @@ export const submitData = {}
7
7
 
8
8
  /**
9
9
  * Send via JSONP. Do NOT call this function outside of a guaranteed web window environment.
10
- * @param {string} url
11
- * @param {string} jsonp
10
+ * @param {Object} args - The args
11
+ * @param {string} args.url - The URL to send to
12
+ * @param {string} args.jsonp - The string name of the jsonp cb method
13
+ * @returns {XMLHttpRequest}
12
14
  * @returns {Element}
13
15
  */
14
- submitData.jsonp = function jsonp (url, jsonp) {
16
+ submitData.jsonp = function jsonp ({ url, jsonp }) {
15
17
  try {
16
18
  if (isWorkerScope) {
17
19
  try {
@@ -19,7 +21,7 @@ submitData.jsonp = function jsonp (url, jsonp) {
19
21
  } catch (e) {
20
22
  // for now theres no other way to execute the callback from ingest without jsonp, or unsafe eval / new Function calls
21
23
  // future work needs to be conducted to allow ingest to return a more traditional JSON API-like experience for the entitlement flags
22
- submitData.xhrGet(url + '&jsonp=' + jsonp)
24
+ submitData.xhrGet({ url: url + '&jsonp=' + jsonp })
23
25
  return false
24
26
  }
25
27
  } else {
@@ -35,18 +37,21 @@ submitData.jsonp = function jsonp (url, jsonp) {
35
37
  }
36
38
  }
37
39
 
38
- submitData.xhrGet = function xhrGet (url) {
39
- return submitData.xhr(url, undefined, false, 'GET')
40
+ submitData.xhrGet = function xhrGet ({ url }) {
41
+ return submitData.xhr({ url, sync: false, method: 'GET' })
40
42
  }
41
43
 
42
44
  /**
43
45
  * Send via XHR
44
- * @param {string} url
45
- * @param {string} body
46
- * @param {boolean} sync
46
+ * @param {Object} args - The args
47
+ * @param {string} args.url - The URL to send to
48
+ * @param {string=} args.body - The Stringified body
49
+ * @param {boolean=} args.sync - Run XHR as Synchronous
50
+ * @param {string=} [args.method=POST] - The XHR method to use
51
+ * @param {{key: string, value: string}[]} [args.headers] - The headers to attach
47
52
  * @returns {XMLHttpRequest}
48
53
  */
49
- submitData.xhr = function xhr (url, body, sync, method = 'POST') {
54
+ submitData.xhr = function xhr ({ url, body, sync, method = 'POST', headers = [{ key: 'content-type', value: 'text/plain' }] }) {
50
55
  var request = new XMLHttpRequest()
51
56
 
52
57
  request.open(method, url, !sync)
@@ -57,7 +62,10 @@ submitData.xhr = function xhr (url, body, sync, method = 'POST') {
57
62
  // do nothing
58
63
  }
59
64
 
60
- request.setRequestHeader('content-type', 'text/plain')
65
+ headers.forEach(header => {
66
+ request.setRequestHeader(header.key, header.value)
67
+ })
68
+
61
69
  request.send(body)
62
70
  return request
63
71
  }
@@ -71,10 +79,12 @@ submitData.xhr = function xhr (url, body, sync, method = 'POST') {
71
79
 
72
80
  /**
73
81
  * Send by appending an <img> element to the page. Do NOT call this function outside of a guaranteed web window environment.
74
- * @param {string} url
75
- * @returns {Element}
82
+ * @param {Object} args - The args
83
+ * @param {string} args.url - The URL to send to
84
+ * @returns {HTMLImageElement}
76
85
  */
77
- submitData.img = function img (url) {
86
+ submitData.img = function img ({ url }) {
87
+ console.log('img url', url)
78
88
  var element = new Image()
79
89
  element.src = url
80
90
  return element
@@ -82,11 +92,12 @@ submitData.img = function img (url) {
82
92
 
83
93
  /**
84
94
  * Send via sendBeacon. Do NOT call this function outside of a guaranteed web window environment.
85
- * @param {string} url
86
- * @param {string} body
95
+ * @param {Object} args - The args
96
+ * @param {string} args.url - The URL to send to
97
+ * @param {string=} args.body - The Stringified body
87
98
  * @returns {boolean}
88
99
  */
89
- submitData.beacon = function (url, body) {
100
+ submitData.beacon = function ({ url, body }) {
90
101
  // Navigator has to be bound to ensure it does not error in some browsers
91
102
  // https://xgwang.me/posts/you-may-not-know-beacon/#it-may-throw-error%2C-be-sure-to-catch
92
103
  const send = window.navigator.sendBeacon.bind(window.navigator)
@@ -7,7 +7,6 @@
7
7
  * This module is used by: ajax, spa.
8
8
  */
9
9
  import { ee as baseEE } from '../event-emitter/contextual-ee'
10
- import slice from 'lodash._slice'
11
10
  import { globalScope } from '../util/global-scope'
12
11
  import { flag } from './wrap-function'
13
12
 
@@ -71,7 +70,7 @@ export function wrapFetch (sharedEE) {
71
70
  var fn = target[name]
72
71
  if (typeof fn === 'function') {
73
72
  target[name] = function () {
74
- var args = slice(arguments)
73
+ var args = [...arguments]
75
74
 
76
75
  var ctx = {}
77
76
  // we are wrapping args in an array so we can preserve the reference
@@ -7,7 +7,6 @@
7
7
  */
8
8
 
9
9
  import { ee } from '../event-emitter/contextual-ee'
10
- import slice from 'lodash._slice'
11
10
  export const flag = 'nr@original'
12
11
 
13
12
  /**
@@ -77,7 +76,7 @@ export function createWrapperWithEmitter (emitter, always) {
77
76
 
78
77
  try {
79
78
  originalThis = this
80
- args = slice(arguments)
79
+ args = [...arguments]
81
80
 
82
81
  if (typeof getContext === 'function') {
83
82
  ctx = getContext(args, originalThis)
@@ -71,7 +71,7 @@ export function wrapPromise (sharedEE) {
71
71
  WrappedPromise[method] = function (subPromises) { // use our own wrapped version of "Promise.all" and ".race" static fns
72
72
  let finalized = false
73
73
 
74
- Array.from(subPromises || []).forEach(sub => {
74
+ ;[...subPromises || []].forEach(sub => {
75
75
  this.resolve(sub).then(setNrId(method === 'all'), setNrId(false))
76
76
  })
77
77
 
@@ -13,9 +13,9 @@ import { FEATURE_NAME } from '../constants'
13
13
  import { drain } from '../../../common/drain/drain'
14
14
  import { FEATURE_NAMES } from '../../../loaders/features/features'
15
15
  import { SUPPORTABILITY_METRIC_CHANNEL } from '../../metrics/constants'
16
- import { FeatureBase } from '../../utils/feature-base'
16
+ import { AggregateBase } from '../../utils/aggregate-base'
17
17
 
18
- export class Aggregate extends FeatureBase {
18
+ export class Aggregate extends AggregateBase {
19
19
  static featureName = FEATURE_NAME
20
20
  constructor (agentIdentifier, aggregator) {
21
21
  super(agentIdentifier, aggregator, FEATURE_NAME)
@@ -99,7 +99,7 @@ function subscribeToEvents (agentIdentifier, ee, handler, dt) {
99
99
 
100
100
  function onOpenXhrEnd (args, xhr) {
101
101
  var loader_config = getLoaderConfig(agentIdentifier)
102
- if ('xpid' in loader_config && this.sameOrigin) {
102
+ if (loader_config.xpid && this.sameOrigin) {
103
103
  xhr.setRequestHeader('X-NewRelic-ID', loader_config.xpid)
104
104
  }
105
105
 
@@ -3,11 +3,19 @@
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
 
6
- var canonicalFunctionNameRe = /([a-z0-9]+)$/i
7
- export function canonicalFunctionName (orig) {
8
- if (!orig) return
6
+ const canonicalFunctionNameRe = /([a-z0-9]+)$/i
9
7
 
10
- var match = orig.match(canonicalFunctionNameRe)
8
+ /**
9
+ * Given a function name string, extracts only an alphanumeric segment at the end of the string (if one exists).
10
+ * This is useful for stack traces, where functions might not be named (e.g., anonymous, computed).
11
+ *
12
+ * @param {string} functionNameString - The original function name string.
13
+ * @returns {string|undefined} The canonical function name, or undefined if the input is falsy or no alphanumeric segments are found.
14
+ */
15
+ export function canonicalFunctionName (functionNameString) {
16
+ if (!functionNameString) return
17
+
18
+ const match = functionNameString.match(canonicalFunctionNameRe)
11
19
  if (match) return match[1]
12
20
 
13
21
  return