@newrelic/browser-agent 0.1.231 → 1.232.1

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 (256) hide show
  1. package/README.md +2 -2
  2. package/dist/cjs/common/config/state/configurable.js +27 -21
  3. package/dist/cjs/common/config/state/init.js +8 -0
  4. package/dist/cjs/common/config/state/runtime.js +24 -26
  5. package/dist/cjs/common/constants/env.cdn.js +1 -1
  6. package/dist/cjs/common/constants/env.npm.js +1 -1
  7. package/dist/cjs/common/context/shared-context.js +2 -1
  8. package/dist/cjs/common/event-emitter/contextual-ee.test.js +2 -2
  9. package/dist/cjs/common/event-emitter/register-handler.test.js +1 -1
  10. package/dist/cjs/common/event-listener/event-listener-opts.js +4 -2
  11. package/dist/cjs/common/harvest/harvest-scheduler.js +14 -11
  12. package/dist/cjs/common/harvest/harvest.js +3 -1
  13. package/dist/cjs/common/session/constants.js +12 -0
  14. package/dist/cjs/common/session/session-entity.js +278 -0
  15. package/dist/cjs/common/session/session-entity.test.js +436 -0
  16. package/dist/cjs/common/storage/first-party-cookies.js +35 -0
  17. package/dist/cjs/common/storage/local-memory.js +35 -0
  18. package/dist/cjs/common/storage/local-memory.test.js +20 -0
  19. package/dist/cjs/common/storage/local-storage.js +33 -0
  20. package/dist/cjs/common/storage/local-storage.test.js +14 -0
  21. package/dist/cjs/common/timer/interaction-timer.js +78 -0
  22. package/dist/cjs/common/timer/interaction-timer.test.js +216 -0
  23. package/dist/cjs/common/timer/timer.js +32 -0
  24. package/dist/cjs/common/timer/timer.test.js +105 -0
  25. package/dist/cjs/common/unload/eol.js +2 -2
  26. package/dist/cjs/common/url/canonicalize-url.js +32 -0
  27. package/dist/cjs/common/url/canonicalize-url.test.js +42 -0
  28. package/dist/cjs/common/url/clean-url.js +10 -3
  29. package/dist/cjs/common/util/data-size.js +6 -0
  30. package/dist/cjs/common/util/data-size.test.js +47 -0
  31. package/dist/cjs/common/util/global-scope.js +4 -2
  32. package/dist/cjs/common/util/invoke.js +73 -0
  33. package/dist/cjs/common/util/invoke.test.js +49 -0
  34. package/dist/cjs/common/util/obfuscate.js +0 -4
  35. package/dist/cjs/common/window/page-visibility.js +3 -1
  36. package/dist/cjs/common/wrap/wrap-fetch.js +1 -3
  37. package/dist/cjs/common/wrap/wrap-function.js +1 -3
  38. package/dist/cjs/common/wrap/wrap-timer.js +1 -1
  39. package/dist/cjs/features/ajax/aggregate/index.js +2 -2
  40. package/dist/cjs/features/ajax/instrument/index.js +1 -1
  41. package/dist/cjs/features/jserrors/aggregate/canonical-function-name.js +12 -4
  42. package/dist/cjs/features/jserrors/aggregate/compute-stack-trace.js +93 -10
  43. package/dist/cjs/features/jserrors/aggregate/compute-stack-trace.test.js +164 -38
  44. package/dist/cjs/features/jserrors/aggregate/index.js +25 -46
  45. package/dist/cjs/features/jserrors/instrument/index.js +0 -2
  46. package/dist/cjs/features/metrics/aggregate/index.js +13 -2
  47. package/dist/cjs/features/page_action/aggregate/index.js +2 -2
  48. package/dist/cjs/features/page_view_event/aggregate/index.js +6 -3
  49. package/dist/cjs/features/page_view_timing/aggregate/index.js +6 -6
  50. package/dist/cjs/features/session_trace/aggregate/index.js +3 -5
  51. package/dist/cjs/features/spa/aggregate/index.js +6 -5
  52. package/dist/cjs/features/utils/agent-session.js +73 -0
  53. package/dist/cjs/features/utils/feature-base.js +1 -1
  54. package/dist/cjs/features/utils/instrument-base.js +7 -2
  55. package/dist/cjs/features/utils/lazy-loader.js +1 -1
  56. package/dist/cjs/loaders/agent.js +1 -1
  57. package/dist/cjs/loaders/api/api.js +1 -4
  58. package/dist/cjs/loaders/api/apiAsync.js +3 -2
  59. package/dist/cjs/loaders/configure/configure.js +0 -6
  60. package/dist/esm/common/config/state/configurable.js +26 -20
  61. package/dist/esm/common/config/state/init.js +8 -0
  62. package/dist/esm/common/config/state/runtime.js +24 -26
  63. package/dist/esm/common/constants/env.cdn.js +1 -1
  64. package/dist/esm/common/constants/env.npm.js +1 -1
  65. package/dist/esm/common/context/shared-context.js +2 -1
  66. package/dist/esm/common/event-emitter/contextual-ee.test.js +2 -2
  67. package/dist/esm/common/event-emitter/register-handler.test.js +1 -1
  68. package/dist/esm/common/event-listener/event-listener-opts.js +4 -2
  69. package/dist/esm/common/harvest/harvest-scheduler.js +14 -11
  70. package/dist/esm/common/harvest/harvest.js +3 -1
  71. package/dist/esm/common/session/constants.js +3 -0
  72. package/dist/esm/common/session/session-entity.js +271 -0
  73. package/dist/esm/common/session/session-entity.test.js +434 -0
  74. package/dist/esm/common/storage/first-party-cookies.js +28 -0
  75. package/dist/esm/common/storage/local-memory.js +28 -0
  76. package/dist/esm/common/storage/local-memory.test.js +18 -0
  77. package/dist/esm/common/storage/local-storage.js +26 -0
  78. package/dist/esm/common/storage/local-storage.test.js +12 -0
  79. package/dist/esm/common/timer/interaction-timer.js +71 -0
  80. package/dist/esm/common/timer/interaction-timer.test.js +214 -0
  81. package/dist/esm/common/timer/timer.js +25 -0
  82. package/dist/esm/common/timer/timer.test.js +103 -0
  83. package/dist/esm/common/unload/eol.js +1 -1
  84. package/dist/esm/common/url/canonicalize-url.js +27 -0
  85. package/dist/esm/common/url/canonicalize-url.test.js +38 -0
  86. package/dist/esm/common/url/clean-url.js +10 -3
  87. package/dist/esm/common/util/data-size.js +7 -0
  88. package/dist/esm/common/util/data-size.test.js +45 -0
  89. package/dist/esm/common/util/global-scope.js +1 -0
  90. package/dist/esm/common/util/invoke.js +66 -0
  91. package/dist/esm/common/util/invoke.test.js +47 -0
  92. package/dist/esm/common/util/obfuscate.js +0 -4
  93. package/dist/esm/common/window/page-visibility.js +3 -1
  94. package/dist/esm/common/wrap/wrap-fetch.js +1 -2
  95. package/dist/esm/common/wrap/wrap-function.js +1 -2
  96. package/dist/esm/common/wrap/wrap-timer.js +1 -1
  97. package/dist/esm/features/ajax/aggregate/index.js +2 -2
  98. package/dist/esm/features/ajax/instrument/index.js +1 -1
  99. package/dist/esm/features/jserrors/aggregate/canonical-function-name.js +12 -4
  100. package/dist/esm/features/jserrors/aggregate/compute-stack-trace.js +93 -10
  101. package/dist/esm/features/jserrors/aggregate/compute-stack-trace.test.js +149 -25
  102. package/dist/esm/features/jserrors/aggregate/index.js +26 -46
  103. package/dist/esm/features/jserrors/instrument/index.js +0 -1
  104. package/dist/esm/features/metrics/aggregate/index.js +14 -3
  105. package/dist/esm/features/page_action/aggregate/index.js +2 -2
  106. package/dist/esm/features/page_view_event/aggregate/index.js +6 -3
  107. package/dist/esm/features/page_view_timing/aggregate/index.js +6 -6
  108. package/dist/esm/features/session_trace/aggregate/index.js +3 -4
  109. package/dist/esm/features/spa/aggregate/index.js +6 -5
  110. package/dist/esm/features/utils/agent-session.js +67 -0
  111. package/dist/esm/features/utils/feature-base.js +1 -1
  112. package/dist/esm/features/utils/instrument-base.js +7 -2
  113. package/dist/esm/features/utils/lazy-loader.js +1 -1
  114. package/dist/esm/loaders/agent.js +1 -1
  115. package/dist/esm/loaders/api/api.js +2 -5
  116. package/dist/esm/loaders/api/apiAsync.js +2 -1
  117. package/dist/esm/loaders/configure/configure.js +2 -8
  118. package/dist/types/common/config/state/configurable.d.ts.map +1 -1
  119. package/dist/types/common/config/state/init.d.ts.map +1 -1
  120. package/dist/types/common/config/state/runtime.d.ts.map +1 -1
  121. package/dist/types/common/context/shared-context.d.ts.map +1 -1
  122. package/dist/types/common/event-listener/event-listener-opts.d.ts +2 -2
  123. package/dist/types/common/event-listener/event-listener-opts.d.ts.map +1 -1
  124. package/dist/types/common/harvest/harvest-scheduler.d.ts +1 -0
  125. package/dist/types/common/harvest/harvest-scheduler.d.ts.map +1 -1
  126. package/dist/types/common/harvest/harvest.d.ts.map +1 -1
  127. package/dist/types/common/session/constants.d.ts +4 -0
  128. package/dist/types/common/session/constants.d.ts.map +1 -0
  129. package/dist/types/common/session/session-entity.d.ts +72 -0
  130. package/dist/types/common/session/session-entity.d.ts.map +1 -0
  131. package/dist/types/common/storage/first-party-cookies.d.ts +8 -0
  132. package/dist/types/common/storage/first-party-cookies.d.ts.map +1 -0
  133. package/dist/types/common/storage/local-memory.d.ts +8 -0
  134. package/dist/types/common/storage/local-memory.d.ts.map +1 -0
  135. package/dist/types/common/storage/local-storage.d.ts +6 -0
  136. package/dist/types/common/storage/local-storage.d.ts.map +1 -0
  137. package/dist/types/common/timer/interaction-timer.d.ts +11 -0
  138. package/dist/types/common/timer/interaction-timer.d.ts.map +1 -0
  139. package/dist/types/common/timer/timer.d.ts +12 -0
  140. package/dist/types/common/timer/timer.d.ts.map +1 -0
  141. package/dist/types/common/url/canonicalize-url.d.ts +9 -0
  142. package/dist/types/common/url/canonicalize-url.d.ts.map +1 -0
  143. package/dist/types/common/url/clean-url.d.ts +7 -1
  144. package/dist/types/common/url/clean-url.d.ts.map +1 -1
  145. package/dist/types/common/util/data-size.d.ts +7 -1
  146. package/dist/types/common/util/data-size.d.ts.map +1 -1
  147. package/dist/types/common/util/global-scope.d.ts +1 -0
  148. package/dist/types/common/util/global-scope.d.ts.map +1 -1
  149. package/dist/types/common/util/invoke.d.ts +35 -0
  150. package/dist/types/common/util/invoke.d.ts.map +1 -0
  151. package/dist/types/common/util/obfuscate.d.ts.map +1 -1
  152. package/dist/types/common/window/page-visibility.d.ts +1 -1
  153. package/dist/types/common/window/page-visibility.d.ts.map +1 -1
  154. package/dist/types/common/wrap/wrap-fetch.d.ts.map +1 -1
  155. package/dist/types/common/wrap/wrap-function.d.ts.map +1 -1
  156. package/dist/types/features/ajax/aggregate/index.d.ts +2 -2
  157. package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
  158. package/dist/types/features/jserrors/aggregate/canonical-function-name.d.ts +8 -1
  159. package/dist/types/features/jserrors/aggregate/canonical-function-name.d.ts.map +1 -1
  160. package/dist/types/features/jserrors/aggregate/compute-stack-trace.d.ts +48 -19
  161. package/dist/types/features/jserrors/aggregate/compute-stack-trace.d.ts.map +1 -1
  162. package/dist/types/features/jserrors/aggregate/index.d.ts +14 -5
  163. package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
  164. package/dist/types/features/jserrors/instrument/index.d.ts.map +1 -1
  165. package/dist/types/features/metrics/aggregate/index.d.ts +2 -2
  166. package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
  167. package/dist/types/features/page_action/aggregate/index.d.ts +3 -3
  168. package/dist/types/features/page_action/aggregate/index.d.ts.map +1 -1
  169. package/dist/types/features/page_view_event/aggregate/index.d.ts +2 -2
  170. package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
  171. package/dist/types/features/page_view_timing/aggregate/index.d.ts +2 -2
  172. package/dist/types/features/page_view_timing/aggregate/index.d.ts.map +1 -1
  173. package/dist/types/features/session_trace/aggregate/index.d.ts +2 -2
  174. package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
  175. package/dist/types/features/spa/aggregate/index.d.ts +2 -2
  176. package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
  177. package/dist/types/features/utils/agent-session.d.ts +2 -0
  178. package/dist/types/features/utils/agent-session.d.ts.map +1 -0
  179. package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
  180. package/dist/types/features/utils/lazy-loader.d.ts +2 -2
  181. package/dist/types/features/utils/lazy-loader.d.ts.map +1 -1
  182. package/dist/types/loaders/api/api.d.ts.map +1 -1
  183. package/dist/types/loaders/api/apiAsync.d.ts.map +1 -1
  184. package/dist/types/loaders/configure/configure.d.ts.map +1 -1
  185. package/package.json +9 -8
  186. package/src/common/config/state/configurable.js +26 -19
  187. package/src/common/config/state/init.js +7 -0
  188. package/src/common/config/state/runtime.js +22 -27
  189. package/src/common/context/shared-context.js +2 -1
  190. package/src/common/event-emitter/contextual-ee.test.js +2 -2
  191. package/src/common/event-emitter/register-handler.test.js +1 -1
  192. package/src/common/event-listener/event-listener-opts.js +4 -4
  193. package/src/common/harvest/harvest-scheduler.js +12 -8
  194. package/src/common/harvest/harvest.js +3 -1
  195. package/src/common/session/constants.js +3 -0
  196. package/src/common/session/session-entity.js +271 -0
  197. package/src/common/session/session-entity.test.js +317 -0
  198. package/src/common/storage/first-party-cookies.js +31 -0
  199. package/src/common/storage/local-memory.js +30 -0
  200. package/src/common/storage/local-memory.test.js +19 -0
  201. package/src/common/storage/local-storage.js +28 -0
  202. package/src/common/storage/local-storage.test.js +17 -0
  203. package/src/common/timer/interaction-timer.js +75 -0
  204. package/src/common/timer/interaction-timer.test.js +167 -0
  205. package/src/common/timer/timer.js +31 -0
  206. package/src/common/timer/timer.test.js +100 -0
  207. package/src/common/unload/eol.js +1 -1
  208. package/src/common/url/canonicalize-url.js +28 -0
  209. package/src/common/url/canonicalize-url.test.js +34 -0
  210. package/src/common/url/clean-url.js +10 -3
  211. package/src/common/util/data-size.js +6 -0
  212. package/src/common/util/data-size.test.js +50 -0
  213. package/src/common/util/global-scope.js +2 -0
  214. package/src/common/util/invoke.js +55 -0
  215. package/src/common/util/invoke.test.js +65 -0
  216. package/src/common/util/obfuscate.js +0 -4
  217. package/src/common/window/page-visibility.js +2 -2
  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-timer.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 +24 -50
  227. package/src/features/jserrors/instrument/index.js +0 -1
  228. package/src/features/metrics/aggregate/index.js +18 -3
  229. package/src/features/page_action/aggregate/index.js +2 -2
  230. package/src/features/page_view_event/aggregate/index.js +6 -3
  231. package/src/features/page_view_timing/aggregate/index.js +6 -6
  232. package/src/features/session_trace/aggregate/index.js +3 -4
  233. package/src/features/spa/aggregate/index.js +5 -5
  234. package/src/features/utils/agent-session.js +68 -0
  235. package/src/features/utils/feature-base.js +1 -1
  236. package/src/features/utils/instrument-base.js +5 -2
  237. package/src/features/utils/lazy-loader.js +1 -1
  238. package/src/loaders/agent.js +1 -1
  239. package/src/loaders/api/api.js +2 -5
  240. package/src/loaders/api/apiAsync.js +2 -1
  241. package/src/loaders/configure/configure.js +2 -7
  242. package/dist/cjs/common/util/single.js +0 -23
  243. package/dist/cjs/common/window/session-storage.js +0 -87
  244. package/dist/cjs/features/utils/aggregate-base.js +0 -13
  245. package/dist/esm/common/util/single.js +0 -16
  246. package/dist/esm/common/window/session-storage.js +0 -77
  247. package/dist/esm/features/utils/aggregate-base.js +0 -6
  248. package/dist/types/common/util/single.d.ts +0 -2
  249. package/dist/types/common/util/single.d.ts.map +0 -1
  250. package/dist/types/common/window/session-storage.d.ts +0 -18
  251. package/dist/types/common/window/session-storage.d.ts.map +0 -1
  252. package/dist/types/features/utils/aggregate-base.d.ts +0 -4
  253. package/dist/types/features/utils/aggregate-base.d.ts.map +0 -1
  254. package/src/common/util/single.js +0 -18
  255. package/src/common/window/session-storage.js +0 -75
  256. package/src/features/utils/aggregate-base.js +0 -7
@@ -0,0 +1,31 @@
1
+
2
+ export class Timer {
3
+ constructor (opts, ms) {
4
+ if (!opts.onEnd) throw new Error('onEnd handler is required')
5
+ if (!ms) throw new Error('ms duration is required')
6
+ this.onEnd = opts.onEnd
7
+ this.initialMs = ms
8
+ this.startTimestamp = Date.now()
9
+
10
+ this.timer = this.create(this.onEnd, ms)
11
+ }
12
+
13
+ create (cb, ms) {
14
+ if (this.timer) this.clear()
15
+ return setTimeout(() => cb ? cb() : this.onEnd(), ms || this.initialMs)
16
+ }
17
+
18
+ clear () {
19
+ clearTimeout(this.timer)
20
+ this.timer = null
21
+ }
22
+
23
+ end () {
24
+ this.clear()
25
+ this.onEnd()
26
+ }
27
+
28
+ isValid () {
29
+ return this.initialMs - (Date.now() - this.startTimestamp) > 0
30
+ }
31
+ }
@@ -0,0 +1,100 @@
1
+ import { Timer } from './timer'
2
+
3
+ jest.useFakeTimers()
4
+
5
+ let now
6
+
7
+ beforeEach(() => {
8
+ now = Date.now()
9
+ jest.setSystemTime(now)
10
+ })
11
+
12
+ afterEach(() => {
13
+ jest.clearAllMocks()
14
+ })
15
+
16
+ describe('constructor', () => {
17
+ test('appropriate properties are set with valid values -- no refresh', () => {
18
+ const timer = new Timer({ onEnd: jest.fn() }, 100)
19
+ const requiredKeys = ['onEnd', 'initialMs', 'startTimestamp', 'timer']
20
+ expect(requiredKeys.every(rk => !!timer[rk])).toBeTruthy()
21
+ })
22
+
23
+ test('appropriate properties are set with valid values -- with refresh', () => {
24
+ const timer = new Timer({ onEnd: jest.fn() }, 100)
25
+ const requiredKeys = ['onEnd', 'initialMs', 'startTimestamp', 'timer']
26
+ expect(requiredKeys.every(rk => !!timer[rk])).toBeTruthy()
27
+ })
28
+
29
+ test('required keys are enforced', () => {
30
+ try {
31
+ new Timer({}, 100)
32
+ } catch (e) {
33
+ expect(e).toEqual(new Error('onEnd handler is required'))
34
+ }
35
+
36
+ try {
37
+ new Timer({ onEnd: jest.fn() })
38
+ } catch (e) {
39
+ expect(e).toEqual(new Error('ms duration is required'))
40
+ }
41
+ })
42
+ })
43
+
44
+ describe('create()', () => {
45
+ test('Create sets a timeout that can execute a cb', () => {
46
+ const timer = new Timer({ onEnd: jest.fn() }, 100)
47
+ expect(timer.timer).toBeTruthy()
48
+ expect(timer.onEnd).toHaveBeenCalledTimes(0)
49
+ jest.runOnlyPendingTimers()
50
+ expect(timer.onEnd).toHaveBeenCalledTimes(1)
51
+ })
52
+
53
+ test('Create can fallback to use defaults', () => {
54
+ let called = 0
55
+ const timer1 = new Timer({ onEnd: jest.fn() }, 100)
56
+ timer1.create()
57
+
58
+ const timer2 = new Timer({ onEnd: jest.fn() }, 100)
59
+ timer2.create(timer2.onEnd)
60
+
61
+ const timer3 = new Timer({ onEnd: jest.fn() }, 100)
62
+ timer3.create(undefined, 100)
63
+
64
+ jest.runAllTimers(200)
65
+
66
+ expect(timer1.onEnd).toHaveBeenCalledTimes(1)
67
+ expect(timer2.onEnd).toHaveBeenCalledTimes(1)
68
+ expect(timer3.onEnd).toHaveBeenCalledTimes(1)
69
+ })
70
+ })
71
+
72
+ describe('clear()', () => {
73
+ test('clear prevents the callback from firing and deletes the pointer', () => {
74
+ const timer = new Timer({ onEnd: jest.fn() }, 100)
75
+ expect(timer.onEnd).toHaveBeenCalledTimes(0)
76
+ timer.clear()
77
+ jest.advanceTimersByTime(150)
78
+ expect(timer.onEnd).toHaveBeenCalledTimes(0)
79
+ expect(timer.timer).toEqual(null)
80
+ })
81
+ })
82
+
83
+ describe('end()', () => {
84
+ test('end clears the callback and calls the onEnd callback', () => {
85
+ const timer = new Timer({ onEnd: jest.fn() }, 100)
86
+ expect(timer.onEnd).toHaveBeenCalledTimes(0)
87
+ timer.end()
88
+ expect(timer.onEnd).toHaveBeenCalledTimes(1)
89
+ expect(timer.timer).toEqual(null)
90
+ })
91
+ })
92
+
93
+ describe('isValid', () => {
94
+ test('isValid validates timeStamps', () => {
95
+ const timer = new Timer({ onEnd: jest.fn() }, 100)
96
+ expect(timer.isValid()).toEqual(true)
97
+ timer.startTimestamp -= 100
98
+ expect(timer.isValid()).toEqual(false)
99
+ })
100
+ })
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import { ffVersion } from '../browser-version/firefox-version'
6
6
  import { windowAddEventListener } from '../event-listener/event-listener-opts'
7
- import { single } from '../util/single'
7
+ import { single } from '../util/invoke'
8
8
  import { globalScope, isWorkerScope, isBrowserScope } from '../util/global-scope'
9
9
  import { subscribeToVisibilityChange } from '../window/page-visibility'
10
10
 
@@ -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
  }
@@ -5,6 +5,12 @@
5
5
 
6
6
  import { stringify } from './stringify'
7
7
 
8
+ /**
9
+ * Returns the size of the provided data. Designed for measuring XHR responses.
10
+ *
11
+ * @param {*} data - The data to be measured.
12
+ * @returns {(number|undefined)} - The size of the data or undefined if size cannot be determined.
13
+ */
8
14
  export function dataSize (data) {
9
15
  if (typeof data === 'string' && data.length) return data.length
10
16
  if (typeof data !== 'object') return undefined
@@ -0,0 +1,50 @@
1
+ import { dataSize } from './data-size'
2
+
3
+ describe('dataSize', () => {
4
+ test('returns length of string', () => {
5
+ const str = 'Hello, world!'
6
+ expect(dataSize(str)).toBe(str.length)
7
+ })
8
+
9
+ test('returns undefined for non-object, number, or empty string', () => {
10
+ expect(dataSize(Infinity)).toBeUndefined()
11
+ expect(dataSize(12345)).toBeUndefined() // might not actually be by design, but this is how it works today
12
+ expect(dataSize('')).toBeUndefined()
13
+ })
14
+
15
+ test('returns byte length of ArrayBuffer object', () => {
16
+ const buffer = new ArrayBuffer(8)
17
+ expect(dataSize(buffer)).toBe(8)
18
+ })
19
+
20
+ test('returns size of Blob object', () => {
21
+ const blob = new Blob(['Hello, world!'], { type: 'text/plain' })
22
+ expect(dataSize(blob)).toBe(blob.size)
23
+ })
24
+
25
+ test('returns undefined for FormData object', () => {
26
+ const formData = new FormData()
27
+ expect(dataSize(formData)).toBeUndefined()
28
+ })
29
+
30
+ test('returns length of JSON string representation of object', () => {
31
+ const obj = {
32
+ str: 'Hello, world!',
33
+ num: 12345,
34
+ nestedObj: {
35
+ arr: [1, 2, 3]
36
+ }
37
+ }
38
+ const expectedSize = JSON.stringify(obj).length
39
+ expect(dataSize(obj)).toBe(expectedSize)
40
+ })
41
+
42
+ test('returns undefined for object with toJSON method that throws an error', () => {
43
+ const obj = {
44
+ toJSON: () => {
45
+ throw new Error('Error in toJSON')
46
+ }
47
+ }
48
+ expect(dataSize(obj)).toBeUndefined()
49
+ })
50
+ })
@@ -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.
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Reduce the invocation of the supplied function so that it is only invoked
3
+ * once within a given timeout.
4
+ *
5
+ * If `wait` is `0`, the function will be invoked during the next tick.
6
+ * If `options.leading` is false or not provided, the function will be invoked
7
+ * N milliseconds after the last invocation of the returned function where
8
+ * N is the `timeout` value.
9
+ * If `options.leading` is true, the function will be invoked immediately upon
10
+ * the first invocation of the returned function and not again for N milliseconds
11
+ * where N is the `timeout` value.
12
+ * @param {function} func Function whose invocation should be limited so it is only invoked
13
+ * once within a given timeout period.
14
+ * @param {number} timeout Time in milliseconds that the function should only be invoked
15
+ * once within.
16
+ * @param {object} options Debounce options
17
+ * @param {boolean} options.leading Forces the function to be invoked on the first
18
+ * invocation of the returned function instead of N milliseconds after the last
19
+ * invocation.
20
+ * @returns {function} A wrapping function that will ensure the provided function
21
+ * is invoked only once within the given timeout.
22
+ */
23
+ export function debounce (func, timeout = 500, options = {}) {
24
+ const leading = options?.leading || false
25
+ let timer
26
+ return (...args) => {
27
+ if (leading && timer === undefined) {
28
+ func.apply(this, args)
29
+ timer = setTimeout(() => timer = clearTimeout(timer), timeout)
30
+ }
31
+
32
+ if (!leading) {
33
+ clearTimeout(timer)
34
+ timer = setTimeout(() => { func.apply(this, args) }, timeout)
35
+ }
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Reduce the invocation of the supplied function so that it is only invoked
41
+ * once.
42
+ * @param {function} func Function whose invocation should be limited so it is only invoked
43
+ * once.
44
+ * @returns {function} A wrapping function that will ensure the provided function
45
+ * is invoked only once.
46
+ */
47
+ export function single (func) {
48
+ let called = false
49
+ return (...args) => {
50
+ if (!called) {
51
+ called = true
52
+ func.apply(this, args)
53
+ }
54
+ }
55
+ }
@@ -0,0 +1,65 @@
1
+ import { debounce, single } from './invoke'
2
+
3
+ jest.useFakeTimers()
4
+
5
+ describe('debounce', () => {
6
+ test('should run the supplied function after 100ms', () => {
7
+ let mockCallback = jest.fn()
8
+
9
+ let debouncedMethod = debounce(mockCallback, 100)
10
+ execFnTimes(debouncedMethod, 100)
11
+
12
+ expect(mockCallback).not.toHaveBeenCalled()
13
+
14
+ jest.advanceTimersByTime(1000)
15
+ expect(mockCallback).toHaveBeenCalledTimes(1)
16
+ })
17
+
18
+ test('should rerun the supplied function when called again after 100ms', () => {
19
+ let mockCallback = jest.fn()
20
+
21
+ let debouncedMethod = debounce(mockCallback, 100)
22
+
23
+ execFnTimes(debouncedMethod, 100)
24
+ jest.advanceTimersByTime(200)
25
+ execFnTimes(debouncedMethod, 100)
26
+ jest.advanceTimersByTime(2000)
27
+
28
+ expect(mockCallback).toHaveBeenCalledTimes(2)
29
+ })
30
+
31
+ test('should run the supplied function on the first event and debounce subsequent events', () => {
32
+ let mockCallback = jest.fn()
33
+
34
+ let debouncedMethod = debounce(mockCallback, 100, { leading: true })
35
+
36
+ execFnTimes(debouncedMethod, 100)
37
+ expect(mockCallback).toHaveBeenCalledTimes(1)
38
+
39
+ jest.advanceTimersByTime(200)
40
+
41
+ expect(mockCallback).toHaveBeenCalledTimes(1)
42
+
43
+ execFnTimes(debouncedMethod, 100)
44
+ jest.advanceTimersByTime(200)
45
+
46
+ expect(mockCallback).toHaveBeenCalledTimes(2)
47
+ })
48
+ })
49
+
50
+ describe('single', () => {
51
+ test('should run the supplied function only once', () => {
52
+ let mockCallback = jest.fn()
53
+
54
+ let singleMethod = single(mockCallback, 100)
55
+ execFnTimes(singleMethod, 100)
56
+
57
+ expect(mockCallback).toHaveBeenCalledTimes(1)
58
+ })
59
+ })
60
+
61
+ function execFnTimes (fn, count) {
62
+ for (let i = 0; i < count; i++) {
63
+ fn()
64
+ }
65
+ }
@@ -8,10 +8,6 @@ var fileProtocolRule = {
8
8
  replacement: 'file://OBFUSCATED'
9
9
  }
10
10
  export class Obfuscator extends SharedContext {
11
- constructor (parent) {
12
- super(parent) // gets any allowed properties from the parent and stores them in `sharedContext`
13
- }
14
-
15
11
  shouldObfuscate () {
16
12
  return getRules(this.sharedContext.agentIdentifier).length > 0
17
13
  }
@@ -10,8 +10,8 @@ import { documentAddEventListener } from '../event-listener/event-listener-opts'
10
10
  * @param {boolean} [toHiddenOnly=false] - only execute the 'cb' when the vis is changing to the hidden state; no arg is passed to 'cb' if used
11
11
  * @returns void
12
12
  */
13
- export function subscribeToVisibilityChange (cb, toHiddenOnly = false) {
14
- documentAddEventListener('visibilitychange', handleVisibilityChange)
13
+ export function subscribeToVisibilityChange (cb, toHiddenOnly = false, capture, abortSignal) {
14
+ documentAddEventListener('visibilitychange', handleVisibilityChange, capture, abortSignal)
15
15
  return
16
16
 
17
17
  function handleVisibilityChange () {
@@ -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 = Array.from(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 = Array.from(arguments)
81
80
 
82
81
  if (typeof getContext === 'function') {
83
82
  ctx = getContext(args, originalThis)
@@ -26,7 +26,7 @@ const TIMER_FNS = [SET_TIMEOUT, 'setImmediate', SET_INTERVAL, CLEAR_TIMEOUT, 'cl
26
26
  * @param {Object} sharedEE - The shared event emitter on which a new scoped event emitter will be based.
27
27
  * @returns {Object} Scoped event emitter with a debug ID of `timer`.
28
28
  */
29
- //eslint-disable-next-line
29
+ // eslint-disable-next-line
30
30
  export function wrapTimer(sharedEE) {
31
31
  const ee = scopedEE(sharedEE)
32
32
 
@@ -9,13 +9,13 @@ import { handle } from '../../../common/event-emitter/handle'
9
9
  import { getConfigurationValue, getInfo } from '../../../common/config/config'
10
10
  import { HarvestScheduler } from '../../../common/harvest/harvest-scheduler'
11
11
  import { setDenyList, shouldCollectEvent } from '../../../common/deny-list/deny-list'
12
- import { AggregateBase } from '../../utils/aggregate-base'
13
12
  import { FEATURE_NAME } from '../constants'
14
13
  import { drain } from '../../../common/drain/drain'
15
14
  import { FEATURE_NAMES } from '../../../loaders/features/features'
16
15
  import { SUPPORTABILITY_METRIC_CHANNEL } from '../../metrics/constants'
16
+ import { FeatureBase } from '../../utils/feature-base'
17
17
 
18
- export class Aggregate extends AggregateBase {
18
+ export class Aggregate extends FeatureBase {
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