@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,434 @@
1
+ import { LocalMemory } from '../storage/local-memory';
2
+ import { LocalStorage } from '../storage/local-storage';
3
+ import { PREFIX } from './constants';
4
+ import { SessionEntity } from './session-entity';
5
+ const agentIdentifier = 'test_agent_identifier';
6
+ const key = 'test_key';
7
+ const value = 'test_value';
8
+ jest.mock('../timer/timer');
9
+ jest.mock('../timer/interaction-timer');
10
+ jest.useFakeTimers();
11
+ const mockBrowserScope = jest.fn().mockImplementation(() => true);
12
+ jest.mock('../util/global-scope', () => ({
13
+ __esModule: true,
14
+ get isBrowserScope() {
15
+ return mockBrowserScope();
16
+ },
17
+ get globalScope() {
18
+ return global.window;
19
+ }
20
+ }));
21
+ beforeEach(() => {
22
+ jest.restoreAllMocks();
23
+ mockBrowserScope.mockReturnValue(true);
24
+ });
25
+ describe('constructor', () => {
26
+ test('must use required fields', () => {
27
+ try {
28
+ expect(new SessionEntity({})).toThrow(new Error('Missing Required Fields'));
29
+ } catch (err) {}
30
+ });
31
+ test('top-level properties are set and exposed', () => {
32
+ const session = new SessionEntity({
33
+ agentIdentifier,
34
+ key
35
+ });
36
+ expect(session).toMatchObject({
37
+ agentIdentifier: expect.any(String),
38
+ key: expect.any(String),
39
+ value: expect.any(String),
40
+ expiresMs: expect.any(Number),
41
+ expiresAt: expect.any(Number),
42
+ expiresTimer: expect.any(Object),
43
+ inactiveMs: expect.any(Number),
44
+ inactiveAt: expect.any(Number),
45
+ inactiveTimer: expect.any(Object),
46
+ isNew: expect.any(Boolean),
47
+ sessionReplayActive: expect.any(Boolean),
48
+ sessionTraceActive: expect.any(Boolean),
49
+ storage: expect.any(Object)
50
+ });
51
+ });
52
+ test('can use sane defaults', () => {
53
+ const session = new SessionEntity({
54
+ agentIdentifier,
55
+ key
56
+ });
57
+ expect(session).toEqual(expect.objectContaining({
58
+ value: expect.any(String),
59
+ expiresAt: expect.any(Number),
60
+ inactiveAt: expect.any(Number),
61
+ sessionReplayActive: expect.any(Boolean),
62
+ sessionTraceActive: expect.any(Boolean)
63
+ }));
64
+ });
65
+ test('Workers are forced to use local memory', () => {
66
+ mockBrowserScope.mockReturnValueOnce(false);
67
+ const session = new SessionEntity({
68
+ agentIdentifier,
69
+ key,
70
+ storageAPI: new LocalStorage()
71
+ });
72
+ expect(session.storage instanceof LocalMemory).toEqual(true);
73
+ });
74
+ test('expiresAt is the correct future timestamp - new session', () => {
75
+ const now = Date.now();
76
+ jest.setSystemTime(now);
77
+ const session = new SessionEntity({
78
+ agentIdentifier,
79
+ key,
80
+ expiresMs: 100
81
+ });
82
+ expect(session.expiresAt).toEqual(now + 100);
83
+ });
84
+ test('expiresAt is the correct future timestamp - existing session', () => {
85
+ const now = Date.now();
86
+ jest.setSystemTime(now);
87
+ const existingData = new LocalMemory({
88
+ ["".concat(PREFIX, "_").concat(key)]: {
89
+ value,
90
+ expiresAt: now + 5000,
91
+ inactiveAt: Infinity
92
+ }
93
+ });
94
+ const session = new SessionEntity({
95
+ agentIdentifier,
96
+ key,
97
+ expiresMs: 100,
98
+ storageAPI: existingData
99
+ });
100
+ expect(session.expiresAt).toEqual(now + 5000);
101
+ });
102
+ test('expiresAt never expires if 0', () => {
103
+ const session = new SessionEntity({
104
+ agentIdentifier,
105
+ key,
106
+ expiresMs: 0
107
+ });
108
+ expect(session.expiresAt).toEqual(Infinity);
109
+ });
110
+ test('inactiveAt is the correct future timestamp - new session', () => {
111
+ const now = Date.now();
112
+ jest.setSystemTime(now);
113
+ const session = new SessionEntity({
114
+ agentIdentifier,
115
+ key,
116
+ inactiveMs: 100
117
+ });
118
+ expect(session.inactiveAt).toEqual(now + 100);
119
+ });
120
+ test('inactiveAt is the correct future timestamp - existing session', () => {
121
+ const now = Date.now();
122
+ jest.setSystemTime(now);
123
+ const existingData = new LocalMemory({
124
+ ["".concat(PREFIX, "_").concat(key)]: {
125
+ value,
126
+ inactiveAt: now + 5000,
127
+ expiresAt: Infinity
128
+ }
129
+ });
130
+ const session = new SessionEntity({
131
+ agentIdentifier,
132
+ key,
133
+ inactiveMs: 100,
134
+ storageAPI: existingData
135
+ });
136
+ expect(session.inactiveAt).toEqual(now + 5000);
137
+ });
138
+ test('inactiveAt never expires if 0', () => {
139
+ const session = new SessionEntity({
140
+ agentIdentifier,
141
+ key,
142
+ inactiveMs: 0
143
+ });
144
+ expect(session.inactiveAt).toEqual(Infinity);
145
+ });
146
+ test('should handle isNew', () => {
147
+ const newSession = new SessionEntity({
148
+ agentIdentifier,
149
+ key,
150
+ expiresMs: 10
151
+ });
152
+ expect(newSession.isNew).toBeTruthy();
153
+ const storageAPI = new LocalMemory({
154
+ ["".concat(PREFIX, "_").concat(key)]: {
155
+ value,
156
+ expiresAt: Infinity,
157
+ inactiveAt: Infinity
158
+ }
159
+ });
160
+ const existingSession = new SessionEntity({
161
+ agentIdentifier,
162
+ key,
163
+ expiresMs: 10,
164
+ storageAPI
165
+ });
166
+ expect(existingSession.isNew).toBeFalsy();
167
+ });
168
+ test('invalid stored values sets new defaults', () => {
169
+ // missing required fields
170
+ const storageAPI = new LocalMemory({
171
+ ["".concat(PREFIX, "_").concat(key)]: {
172
+ invalid_fields: true
173
+ }
174
+ });
175
+ const session = new SessionEntity({
176
+ agentIdentifier,
177
+ key,
178
+ storageAPI
179
+ });
180
+ expect(session).toEqual(expect.objectContaining({
181
+ value: expect.any(String),
182
+ expiresAt: expect.any(Number),
183
+ inactiveAt: expect.any(Number),
184
+ sessionReplayActive: expect.any(Boolean),
185
+ sessionTraceActive: expect.any(Boolean)
186
+ }));
187
+ });
188
+ test('expired expiresAt value in storage sets new defaults', () => {
189
+ const now = Date.now();
190
+ jest.setSystemTime(now);
191
+ const storageAPI = new LocalMemory({
192
+ ["".concat(PREFIX, "_").concat(key)]: {
193
+ value,
194
+ expiresAt: now - 100,
195
+ inactiveAt: Infinity
196
+ }
197
+ });
198
+ const session = new SessionEntity({
199
+ agentIdentifier,
200
+ key,
201
+ storageAPI
202
+ });
203
+ expect(session).toEqual(expect.objectContaining({
204
+ value: expect.any(String),
205
+ expiresAt: expect.any(Number),
206
+ inactiveAt: expect.any(Number),
207
+ sessionReplayActive: expect.any(Boolean),
208
+ sessionTraceActive: expect.any(Boolean)
209
+ }));
210
+ });
211
+ test('expired inactiveAt value in storage sets new defaults', () => {
212
+ const now = Date.now();
213
+ jest.setSystemTime(now);
214
+ const storageAPI = new LocalMemory({
215
+ ["".concat(PREFIX, "_").concat(key)]: {
216
+ value,
217
+ inactiveAt: now - 100,
218
+ expiresAt: Infinity
219
+ }
220
+ });
221
+ const session = new SessionEntity({
222
+ agentIdentifier,
223
+ key,
224
+ storageAPI
225
+ });
226
+ expect(session).toEqual(expect.objectContaining({
227
+ value: expect.any(String),
228
+ expiresAt: expect.any(Number),
229
+ inactiveAt: expect.any(Number),
230
+ sessionReplayActive: expect.any(Boolean),
231
+ sessionTraceActive: expect.any(Boolean)
232
+ }));
233
+ });
234
+ });
235
+ describe('reset()', () => {
236
+ test('should create new default values when resetting', () => {
237
+ const now = Date.now();
238
+ jest.setSystemTime(now);
239
+ const session = new SessionEntity({
240
+ agentIdentifier,
241
+ key,
242
+ expiresMs: 10
243
+ });
244
+ const sessionVal = session.value;
245
+ expect(session.value).toBeTruthy();
246
+ session.reset();
247
+ expect(session.value).toBeTruthy();
248
+ expect(session.value).not.toEqual(sessionVal);
249
+ });
250
+ test('custom data should be wiped on reset', () => {
251
+ const now = Date.now();
252
+ jest.setSystemTime(now);
253
+ const session = new SessionEntity({
254
+ agentIdentifier,
255
+ key,
256
+ expiresMs: 10
257
+ });
258
+ session.syncCustomAttribute('test', 123);
259
+ expect(session.custom.test).toEqual(123);
260
+ expect(session.read().custom.test).toEqual(123);
261
+
262
+ // simulate a timer expiring
263
+ session.reset();
264
+ expect(session.custom?.test).toEqual(undefined);
265
+ expect(session.read()?.custom?.test).toEqual(undefined);
266
+ });
267
+ });
268
+ describe('read()', () => {
269
+ test('"new" sessions get data from read()', () => {
270
+ const newSession = new SessionEntity({
271
+ agentIdentifier,
272
+ key,
273
+ expiresMs: 10
274
+ });
275
+ expect(newSession.isNew).toBeTruthy();
276
+ expect(newSession.read()).toEqual(expect.objectContaining({
277
+ value: expect.any(String),
278
+ expiresAt: expect.any(Number),
279
+ inactiveAt: expect.any(Number),
280
+ sessionReplayActive: expect.any(Boolean),
281
+ sessionTraceActive: expect.any(Boolean)
282
+ }));
283
+ });
284
+ test('"pre-existing" sessions get data from read()', () => {
285
+ const storageAPI = new LocalMemory({
286
+ ["".concat(PREFIX, "_").concat(key)]: {
287
+ value,
288
+ expiresAt: Infinity,
289
+ inactiveAt: Infinity
290
+ }
291
+ });
292
+ const session = new SessionEntity({
293
+ agentIdentifier,
294
+ key,
295
+ storageAPI
296
+ });
297
+ expect(session.isNew).toBeFalsy();
298
+ expect(session.read()).toEqual(expect.objectContaining({
299
+ value,
300
+ expiresAt: Infinity,
301
+ inactiveAt: Infinity
302
+ }));
303
+ });
304
+ });
305
+ describe('write()', () => {
306
+ test('write() sets data to top-level wrapper', () => {
307
+ const session = new SessionEntity({
308
+ agentIdentifier,
309
+ key
310
+ });
311
+ expect(session.value).not.toEqual(value);
312
+ expect(session.expiresAt).not.toEqual(Infinity);
313
+ expect(session.inactiveAt).not.toEqual(Infinity);
314
+ session.write({
315
+ value,
316
+ expiresAt: Infinity,
317
+ inactiveAt: Infinity
318
+ });
319
+ expect(session.value).toEqual(value);
320
+ expect(session.expiresAt).toEqual(Infinity);
321
+ expect(session.inactiveAt).toEqual(Infinity);
322
+ });
323
+ test('write() sets data that read() can access', () => {
324
+ const now = Date.now();
325
+ jest.setSystemTime(now);
326
+ const session = new SessionEntity({
327
+ agentIdentifier,
328
+ key
329
+ });
330
+ session.write({
331
+ value,
332
+ expiresAt: now + 100,
333
+ inactiveAt: now + 100
334
+ });
335
+ const read = session.read();
336
+ expect(read.value).toEqual(value);
337
+ expect(read.expiresAt).toEqual(now + 100);
338
+ expect(read.inactiveAt).toEqual(now + 100);
339
+ });
340
+ test('write() does not run with invalid data', () => {
341
+ const session = new SessionEntity({
342
+ agentIdentifier,
343
+ key
344
+ });
345
+ let out = session.write();
346
+ expect(out).toEqual(undefined);
347
+ out = session.write('string');
348
+ expect(out).toEqual(undefined);
349
+ out = session.write(123);
350
+ expect(out).toEqual(undefined);
351
+ out = session.write(true);
352
+ expect(out).toEqual(undefined);
353
+ out = session.write(false);
354
+ expect(out).toEqual(undefined);
355
+ });
356
+ });
357
+ describe('refresh()', () => {
358
+ test('refresh sets inactiveAt to future time', () => {
359
+ const now = Date.now();
360
+ jest.setSystemTime(now);
361
+ const session = new SessionEntity({
362
+ agentIdentifier,
363
+ key,
364
+ inactiveMs: 100
365
+ });
366
+ expect(session.inactiveAt).toEqual(now + 100);
367
+ jest.setSystemTime(now + 1000);
368
+ session.refresh();
369
+ expect(session.inactiveAt).toEqual(now + 100 + 1000);
370
+ });
371
+ test('refresh resets the entity if expiresTimer is invalid', () => {
372
+ const now = Date.now();
373
+ jest.setSystemTime(now);
374
+ const session = new SessionEntity({
375
+ agentIdentifier,
376
+ key,
377
+ value
378
+ });
379
+ expect(session.value).toEqual(value);
380
+ session.write({
381
+ ...session.read(),
382
+ expiresAt: now - 1
383
+ });
384
+ session.refresh();
385
+ expect(session.value).not.toEqual(value);
386
+ });
387
+ test('refresh resets the entity if inactiveTimer is invalid', () => {
388
+ const now = Date.now();
389
+ jest.setSystemTime(now);
390
+ const session = new SessionEntity({
391
+ agentIdentifier,
392
+ key,
393
+ value
394
+ });
395
+ expect(session.value).toEqual(value);
396
+ session.write({
397
+ ...session.read(),
398
+ inactiveAt: now - 1
399
+ });
400
+ session.refresh();
401
+ expect(session.value).not.toEqual(value);
402
+ });
403
+ });
404
+ describe('syncCustomAttribute()', () => {
405
+ test('Custom data can be managed by session entity', () => {
406
+ const session = new SessionEntity({
407
+ agentIdentifier,
408
+ key
409
+ });
410
+
411
+ // if custom has never been set, and a "delete" action is triggered, do nothing
412
+ session.syncCustomAttribute('test', null);
413
+ expect(session?.custom?.test).toEqual(undefined);
414
+ session.syncCustomAttribute('test', 1);
415
+ expect(session?.custom?.test).toEqual(1);
416
+ session.syncCustomAttribute('test', 'string');
417
+ expect(session?.custom?.test).toEqual('string');
418
+ session.syncCustomAttribute('test', false);
419
+ expect(session?.custom?.test).toEqual(false);
420
+
421
+ // null specifically deletes the object completely
422
+ session.syncCustomAttribute('test', null);
423
+ expect(session?.custom?.test).toEqual(undefined);
424
+ });
425
+ test('Only runs in browser scope', () => {
426
+ mockBrowserScope.mockReturnValue(false);
427
+ const session = new SessionEntity({
428
+ agentIdentifier,
429
+ key
430
+ });
431
+ session.syncCustomAttribute('test', 1);
432
+ expect(session.read().custom?.test).toEqual(undefined);
433
+ });
434
+ });
@@ -0,0 +1,28 @@
1
+ export class FirstPartyCookies {
2
+ constructor(domain) {
3
+ this.domain = domain;
4
+ }
5
+ get(name) {
6
+ try {
7
+ var match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
8
+ if (match) return match[2];
9
+ } catch (err) {
10
+ return '';
11
+ }
12
+ }
13
+ set(key, value) {
14
+ try {
15
+ const cookie = "".concat(key, "=").concat(value, "; Domain=").concat(domain, "; Path=/");
16
+ document.cookie = cookie;
17
+ } catch (err) {
18
+ return;
19
+ }
20
+ }
21
+ remove(key) {
22
+ try {
23
+ return document.cookie = "".concat(key, "=; expires=Thu, 01 Jan 1970 00:00:00 UTC; Domain=").concat(domain, "; Path=/");
24
+ } catch (err) {
25
+ return;
26
+ }
27
+ }
28
+ }
@@ -0,0 +1,28 @@
1
+ export class LocalMemory {
2
+ constructor() {
3
+ let initialState = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
4
+ this.state = initialState;
5
+ }
6
+ get(key) {
7
+ try {
8
+ return this.state[key];
9
+ } catch (err) {
10
+ return '';
11
+ }
12
+ }
13
+ set(key, value) {
14
+ try {
15
+ if (value === undefined || value === null) return this.remove(key);
16
+ this.state[key] = value;
17
+ } catch (err) {
18
+ return;
19
+ }
20
+ }
21
+ remove(key) {
22
+ try {
23
+ delete this.state[key];
24
+ } catch (err) {
25
+ return;
26
+ }
27
+ }
28
+ }
@@ -0,0 +1,18 @@
1
+ import { LocalMemory } from './local-memory';
2
+ test('Local-memory', () => {
3
+ const LM = new LocalMemory({
4
+ test: 1
5
+ });
6
+ expect(LM.state).toEqual({
7
+ test: 1
8
+ });
9
+ expect(LM.get('test')).toEqual(1);
10
+ LM.set('test', 2);
11
+ expect(LM.get('test')).toEqual(2);
12
+ LM.set('test');
13
+ expect(LM.get('test')).toEqual(undefined);
14
+ LM.set('test', 2);
15
+ expect(LM.get('test')).toEqual(2);
16
+ LM.remove('test');
17
+ expect(LM.get('test')).toEqual(undefined);
18
+ });
@@ -0,0 +1,26 @@
1
+ export class LocalStorage {
2
+ get(key) {
3
+ try {
4
+ // localStorage strangely type-casts non-existing data to "null"...
5
+ // Cast it back to undefined if it doesnt exist
6
+ return localStorage.getItem(key) || undefined;
7
+ } catch (err) {
8
+ return '';
9
+ }
10
+ }
11
+ set(key, value) {
12
+ try {
13
+ if (value === undefined || value === null) return this.remove(key);
14
+ return localStorage.setItem(key, value);
15
+ } catch (err) {
16
+ return;
17
+ }
18
+ }
19
+ remove(key) {
20
+ try {
21
+ localStorage.removeItem(key);
22
+ } catch (err) {
23
+ return;
24
+ }
25
+ }
26
+ }
@@ -0,0 +1,12 @@
1
+ import { LocalStorage } from './local-storage';
2
+ test('Local-memory', () => {
3
+ const LS = new LocalStorage();
4
+ LS.set('test', 1);
5
+ expect(LS.get('test')).toEqual('1');
6
+ LS.set('test');
7
+ expect(LS.get('test')).toEqual(undefined);
8
+ LS.set('test', 2);
9
+ expect(LS.get('test')).toEqual('2');
10
+ LS.remove('test');
11
+ expect(LS.get('test')).toEqual(undefined);
12
+ });
@@ -0,0 +1,71 @@
1
+ import { Timer } from './timer';
2
+ import { subscribeToVisibilityChange } from '../window/page-visibility';
3
+ import { debounce } from '../util/invoke';
4
+ import { isBrowserScope } from '../util/global-scope';
5
+ export class InteractionTimer extends Timer {
6
+ constructor(opts, ms) {
7
+ super(opts, ms);
8
+ this.onRefresh = opts.onRefresh;
9
+ this.onPause = opts.onPause;
10
+
11
+ // used by pause/resume
12
+ this.remainingMs = undefined;
13
+ if (!opts.refreshEvents) opts.refreshEvents = ['click', 'keydown', 'scroll'];
14
+
15
+ // the abort controller is used to "reset" the event listeners and prevent them from duplicating when new sessions are created
16
+ try {
17
+ this.abortController = new AbortController();
18
+ } catch (e) {
19
+ // this try-catch can be removed when IE11 is completely unsupported & gone
20
+ }
21
+ if (isBrowserScope && opts.ee) {
22
+ if (opts.ee) {
23
+ const debouncedRefresh = debounce(this.refresh.bind(this), 500, {
24
+ leading: true
25
+ });
26
+ opts.ee.on('fn-end', evts => {
27
+ if (opts.refreshEvents.includes(evts?.[0]?.type)) {
28
+ debouncedRefresh();
29
+ }
30
+ });
31
+ }
32
+
33
+ // watch for the vis state changing. If the page is hidden, the local inactivity timer should be paused
34
+ // if the page is brought BACK to visibility and the timer hasnt "naturally" expired, refresh the timer...
35
+ // this is to support the concept that other tabs could be experiencing activity. The thought would be that
36
+ // "backgrounded" tabs would pause, while "closed" tabs that "reopen" will just instantiate a new SessionEntity class if restored
37
+ // which will do a "hard" check of the timestamps.
38
+
39
+ // NOTE -- this does not account for 2 browser windows open side by side, blurring/focusing between them
40
+ // IF DEEMED necessary, more event handling would be needed to account for this.
41
+ subscribeToVisibilityChange(state => {
42
+ if (state === 'hidden') this.pause();
43
+ // vis change --> visible is treated like a new interaction with the page
44
+ else this.refresh();
45
+ }, false, false, this.abortController?.signal);
46
+ }
47
+ }
48
+ abort() {
49
+ this.clear();
50
+ this.abortController?.abort();
51
+ }
52
+ pause() {
53
+ this.onPause?.();
54
+ clearTimeout(this.timer);
55
+ this.remainingMs = this.initialMs - (Date.now() - this.startTimestamp);
56
+ }
57
+ refresh(cb, ms) {
58
+ this.clear();
59
+ this.timer = this.create(cb, ms);
60
+ this.startTimestamp = Date.now();
61
+ this.remainingMs = undefined;
62
+ this.onRefresh?.();
63
+ }
64
+
65
+ // NOT CURRENTLY UTILIZED BY ANYTHING
66
+ // resume () {
67
+ // if (!this.remainingMs || !this.isValid()) return
68
+ // this.timer = this.create(this.cb, this.remainingMs)
69
+ // this.remainingMs = undefined
70
+ // }
71
+ }