@interfere/next 0.0.14 → 0.0.15-alpha.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 (232) hide show
  1. package/README.md +1 -6
  2. package/dist/build/env-config.d.mts +7 -0
  3. package/dist/build/env-config.d.mts.map +1 -0
  4. package/dist/build/env-config.mjs +17 -0
  5. package/dist/build/env-config.mjs.map +1 -0
  6. package/dist/build/logger.d.mts +11 -0
  7. package/dist/build/logger.d.mts.map +1 -0
  8. package/dist/build/logger.mjs +155 -0
  9. package/dist/build/logger.mjs.map +1 -0
  10. package/dist/build/release-program.d.mts +19 -0
  11. package/dist/build/release-program.d.mts.map +1 -0
  12. package/dist/build/release-program.mjs +92 -0
  13. package/dist/build/release-program.mjs.map +1 -0
  14. package/dist/build/secret-key.d.mts +10 -0
  15. package/dist/build/secret-key.d.mts.map +1 -0
  16. package/dist/build/secret-key.mjs +16 -0
  17. package/dist/build/secret-key.mjs.map +1 -0
  18. package/dist/build/services/config.service.d.mts +9 -0
  19. package/dist/build/services/config.service.d.mts.map +1 -0
  20. package/dist/build/services/config.service.mjs +8 -0
  21. package/dist/build/services/config.service.mjs.map +1 -0
  22. package/dist/build/services/preflight.service.d.mts +19 -0
  23. package/dist/build/services/preflight.service.d.mts.map +1 -0
  24. package/dist/build/services/preflight.service.mjs +76 -0
  25. package/dist/build/services/preflight.service.mjs.map +1 -0
  26. package/dist/build/services/release-identity.service.d.mts +22 -0
  27. package/dist/build/services/release-identity.service.d.mts.map +1 -0
  28. package/dist/build/services/release-identity.service.mjs +48 -0
  29. package/dist/build/services/release-identity.service.mjs.map +1 -0
  30. package/dist/build/services/source-map.service.d.mts +24 -0
  31. package/dist/build/services/source-map.service.d.mts.map +1 -0
  32. package/dist/build/services/source-map.service.mjs +58 -0
  33. package/dist/build/services/source-map.service.mjs.map +1 -0
  34. package/dist/build/source-maps/api.d.mts +35 -0
  35. package/dist/build/source-maps/api.d.mts.map +1 -0
  36. package/dist/build/source-maps/api.mjs +61 -0
  37. package/dist/build/source-maps/api.mjs.map +1 -0
  38. package/dist/build/source-maps/client.d.mts +73 -0
  39. package/dist/build/source-maps/client.d.mts.map +1 -0
  40. package/dist/build/source-maps/client.mjs +228 -0
  41. package/dist/build/source-maps/client.mjs.map +1 -0
  42. package/dist/build/source-maps/errors.d.mts +109 -0
  43. package/dist/build/source-maps/errors.d.mts.map +1 -0
  44. package/dist/build/source-maps/errors.mjs +22 -0
  45. package/dist/build/source-maps/errors.mjs.map +1 -0
  46. package/dist/build/source-maps/files.d.mts +35 -0
  47. package/dist/build/source-maps/files.d.mts.map +1 -0
  48. package/dist/build/source-maps/files.mjs +222 -0
  49. package/dist/build/source-maps/files.mjs.map +1 -0
  50. package/dist/build/source-maps/providers/deployment/detector.d.mts +26 -0
  51. package/dist/build/source-maps/providers/deployment/detector.d.mts.map +1 -0
  52. package/dist/build/source-maps/providers/deployment/detector.mjs +22 -0
  53. package/dist/build/source-maps/providers/deployment/detector.mjs.map +1 -0
  54. package/dist/build/source-maps/providers/deployment/types.d.mts +12 -0
  55. package/dist/build/source-maps/providers/deployment/types.d.mts.map +1 -0
  56. package/dist/build/source-maps/providers/deployment/types.mjs +3 -0
  57. package/dist/build/source-maps/providers/deployment/vercel.d.mts +6 -0
  58. package/dist/build/source-maps/providers/deployment/vercel.d.mts.map +1 -0
  59. package/dist/build/source-maps/providers/deployment/vercel.mjs +44 -0
  60. package/dist/build/source-maps/providers/deployment/vercel.mjs.map +1 -0
  61. package/dist/build/source-maps/providers/source-control/detector.d.mts +15 -0
  62. package/dist/build/source-maps/providers/source-control/detector.d.mts.map +1 -0
  63. package/dist/build/source-maps/providers/source-control/detector.mjs +22 -0
  64. package/dist/build/source-maps/providers/source-control/detector.mjs.map +1 -0
  65. package/dist/build/source-maps/providers/source-control/git.d.mts +6 -0
  66. package/dist/build/source-maps/providers/source-control/git.d.mts.map +1 -0
  67. package/dist/build/source-maps/providers/source-control/git.mjs +50 -0
  68. package/dist/build/source-maps/providers/source-control/git.mjs.map +1 -0
  69. package/dist/build/source-maps/providers/source-control/types.d.mts +12 -0
  70. package/dist/build/source-maps/providers/source-control/types.d.mts.map +1 -0
  71. package/dist/build/source-maps/providers/source-control/types.mjs +3 -0
  72. package/dist/build/with-interfere.d.mts +49 -0
  73. package/dist/build/with-interfere.d.mts.map +1 -0
  74. package/dist/build/with-interfere.mjs +75 -0
  75. package/dist/build/with-interfere.mjs.map +1 -0
  76. package/dist/client/client.d.mts +3 -0
  77. package/dist/client/client.mjs +5 -0
  78. package/dist/client/provider.d.mts +22 -0
  79. package/dist/client/provider.d.mts.map +1 -0
  80. package/dist/client/provider.mjs +33 -0
  81. package/dist/client/provider.mjs.map +1 -0
  82. package/dist/lib/env.d.mts +12 -0
  83. package/dist/lib/env.d.mts.map +1 -0
  84. package/dist/lib/env.mjs +17 -0
  85. package/dist/lib/env.mjs.map +1 -0
  86. package/dist/lib/test-utils/make-next-request.d.mts +6 -0
  87. package/dist/lib/test-utils/make-next-request.d.mts.map +1 -0
  88. package/dist/lib/test-utils/make-next-request.mjs +12 -0
  89. package/dist/lib/test-utils/make-next-request.mjs.map +1 -0
  90. package/dist/lib/types.d.mts +22 -0
  91. package/dist/lib/types.d.mts.map +1 -0
  92. package/dist/lib/types.mjs +7 -0
  93. package/dist/lib/types.mjs.map +1 -0
  94. package/dist/server/middleware.d.mts +11 -0
  95. package/dist/server/middleware.d.mts.map +1 -0
  96. package/dist/server/middleware.mjs +84 -0
  97. package/dist/server/middleware.mjs.map +1 -0
  98. package/dist/server/proxy.d.mts +6 -0
  99. package/dist/server/proxy.d.mts.map +1 -0
  100. package/dist/server/proxy.mjs +29 -0
  101. package/dist/server/proxy.mjs.map +1 -0
  102. package/dist/server/route-handler.d.mts +9 -0
  103. package/dist/server/route-handler.d.mts.map +1 -0
  104. package/dist/server/route-handler.mjs +172 -0
  105. package/dist/server/route-handler.mjs.map +1 -0
  106. package/dist/server/services/config.service.d.mts +21 -0
  107. package/dist/server/services/config.service.d.mts.map +1 -0
  108. package/dist/server/services/config.service.mjs +43 -0
  109. package/dist/server/services/config.service.mjs.map +1 -0
  110. package/dist/server/services/error-tracking.service.d.mts +19 -0
  111. package/dist/server/services/error-tracking.service.d.mts.map +1 -0
  112. package/dist/server/services/error-tracking.service.mjs +31 -0
  113. package/dist/server/services/error-tracking.service.mjs.map +1 -0
  114. package/package.json +67 -36
  115. package/dist/__tests__/build/with-interfere-coverage.test.d.ts +0 -2
  116. package/dist/__tests__/build/with-interfere-coverage.test.d.ts.map +0 -1
  117. package/dist/__tests__/build/with-interfere-coverage.test.js +0 -295
  118. package/dist/__tests__/build/with-interfere-coverage.test.js.map +0 -1
  119. package/dist/__tests__/build/with-interfere.test.d.ts +0 -2
  120. package/dist/__tests__/build/with-interfere.test.d.ts.map +0 -1
  121. package/dist/__tests__/build/with-interfere.test.js +0 -363
  122. package/dist/__tests__/build/with-interfere.test.js.map +0 -1
  123. package/dist/__tests__/core/client.test.d.ts +0 -2
  124. package/dist/__tests__/core/client.test.d.ts.map +0 -1
  125. package/dist/__tests__/core/client.test.js +0 -373
  126. package/dist/__tests__/core/client.test.js.map +0 -1
  127. package/dist/__tests__/core/encoders.test.d.ts +0 -2
  128. package/dist/__tests__/core/encoders.test.d.ts.map +0 -1
  129. package/dist/__tests__/core/encoders.test.js +0 -56
  130. package/dist/__tests__/core/encoders.test.js.map +0 -1
  131. package/dist/__tests__/core/rage-click.test.d.ts +0 -2
  132. package/dist/__tests__/core/rage-click.test.d.ts.map +0 -1
  133. package/dist/__tests__/core/rage-click.test.js +0 -121
  134. package/dist/__tests__/core/rage-click.test.js.map +0 -1
  135. package/dist/__tests__/core/session-manager.test.d.ts +0 -2
  136. package/dist/__tests__/core/session-manager.test.d.ts.map +0 -1
  137. package/dist/__tests__/core/session-manager.test.js +0 -1168
  138. package/dist/__tests__/core/session-manager.test.js.map +0 -1
  139. package/dist/__tests__/integration/release-upload.test.d.ts +0 -2
  140. package/dist/__tests__/integration/release-upload.test.d.ts.map +0 -1
  141. package/dist/__tests__/integration/release-upload.test.js +0 -153
  142. package/dist/__tests__/integration/release-upload.test.js.map +0 -1
  143. package/dist/__tests__/provider.test.d.ts +0 -2
  144. package/dist/__tests__/provider.test.d.ts.map +0 -1
  145. package/dist/__tests__/provider.test.js +0 -84
  146. package/dist/__tests__/provider.test.js.map +0 -1
  147. package/dist/__tests__/session/persistence.test.d.ts +0 -2
  148. package/dist/__tests__/session/persistence.test.d.ts.map +0 -1
  149. package/dist/__tests__/session/persistence.test.js +0 -129
  150. package/dist/__tests__/session/persistence.test.js.map +0 -1
  151. package/dist/__tests__/session/session-summary.test.d.ts +0 -2
  152. package/dist/__tests__/session/session-summary.test.d.ts.map +0 -1
  153. package/dist/__tests__/session/session-summary.test.js +0 -763
  154. package/dist/__tests__/session/session-summary.test.js.map +0 -1
  155. package/dist/client.d.ts +0 -75
  156. package/dist/client.d.ts.map +0 -1
  157. package/dist/client.js +0 -123
  158. package/dist/client.js.map +0 -1
  159. package/dist/config.d.ts +0 -40
  160. package/dist/config.d.ts.map +0 -1
  161. package/dist/config.js +0 -340
  162. package/dist/config.js.map +0 -1
  163. package/dist/index.d.ts +0 -37
  164. package/dist/index.d.ts.map +0 -1
  165. package/dist/index.js +0 -49
  166. package/dist/index.js.map +0 -1
  167. package/dist/index.jsx +0 -87
  168. package/dist/index.jsx.map +0 -1
  169. package/dist/lib/core/client-core.d.ts +0 -27
  170. package/dist/lib/core/client-core.d.ts.map +0 -1
  171. package/dist/lib/core/client-core.js +0 -152
  172. package/dist/lib/core/client-core.js.map +0 -1
  173. package/dist/lib/core/constants.d.ts +0 -12
  174. package/dist/lib/core/constants.d.ts.map +0 -1
  175. package/dist/lib/core/constants.js +0 -17
  176. package/dist/lib/core/constants.js.map +0 -1
  177. package/dist/lib/core/debug.d.ts +0 -47
  178. package/dist/lib/core/debug.d.ts.map +0 -1
  179. package/dist/lib/core/debug.js +0 -79
  180. package/dist/lib/core/debug.js.map +0 -1
  181. package/dist/lib/core/encoders.d.ts +0 -3
  182. package/dist/lib/core/encoders.d.ts.map +0 -1
  183. package/dist/lib/core/encoders.js +0 -5
  184. package/dist/lib/core/encoders.js.map +0 -1
  185. package/dist/lib/core/error-handlers.d.ts +0 -14
  186. package/dist/lib/core/error-handlers.d.ts.map +0 -1
  187. package/dist/lib/core/error-handlers.js +0 -191
  188. package/dist/lib/core/error-handlers.js.map +0 -1
  189. package/dist/lib/core/runtime.d.ts +0 -7
  190. package/dist/lib/core/runtime.d.ts.map +0 -1
  191. package/dist/lib/core/runtime.js +0 -16
  192. package/dist/lib/core/runtime.js.map +0 -1
  193. package/dist/lib/persistence/storage.d.ts +0 -5
  194. package/dist/lib/persistence/storage.d.ts.map +0 -1
  195. package/dist/lib/persistence/storage.js +0 -67
  196. package/dist/lib/persistence/storage.js.map +0 -1
  197. package/dist/lib/session/constants.d.ts +0 -19
  198. package/dist/lib/session/constants.d.ts.map +0 -1
  199. package/dist/lib/session/constants.js +0 -34
  200. package/dist/lib/session/constants.js.map +0 -1
  201. package/dist/lib/session/persistence.d.ts +0 -58
  202. package/dist/lib/session/persistence.d.ts.map +0 -1
  203. package/dist/lib/session/persistence.js +0 -179
  204. package/dist/lib/session/persistence.js.map +0 -1
  205. package/dist/lib/session/rage-click.d.ts +0 -17
  206. package/dist/lib/session/rage-click.d.ts.map +0 -1
  207. package/dist/lib/session/rage-click.js +0 -104
  208. package/dist/lib/session/rage-click.js.map +0 -1
  209. package/dist/lib/session/replay.d.ts +0 -3
  210. package/dist/lib/session/replay.d.ts.map +0 -1
  211. package/dist/lib/session/replay.js +0 -109
  212. package/dist/lib/session/replay.js.map +0 -1
  213. package/dist/lib/session/session-manager.d.ts +0 -126
  214. package/dist/lib/session/session-manager.d.ts.map +0 -1
  215. package/dist/lib/session/session-manager.js +0 -635
  216. package/dist/lib/session/session-manager.js.map +0 -1
  217. package/dist/lib/session/session-summary.d.ts +0 -3
  218. package/dist/lib/session/session-summary.d.ts.map +0 -1
  219. package/dist/lib/session/session-summary.js +0 -214
  220. package/dist/lib/session/session-summary.js.map +0 -1
  221. package/dist/middleware.d.ts +0 -8
  222. package/dist/middleware.d.ts.map +0 -1
  223. package/dist/middleware.js +0 -139
  224. package/dist/middleware.js.map +0 -1
  225. package/dist/types/storage.d.ts +0 -7
  226. package/dist/types/storage.d.ts.map +0 -1
  227. package/dist/types/storage.js +0 -2
  228. package/dist/types/storage.js.map +0 -1
  229. package/dist/types.d.ts +0 -6
  230. package/dist/types.d.ts.map +0 -1
  231. package/dist/types.js +0 -4
  232. package/dist/types.js.map +0 -1
@@ -1,763 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
- import { capture as captureFn } from "../../client.js";
3
- import { setupSessionSummary, stopSessionSummary, } from "../../lib/session/session-summary.js";
4
- // Mock the client module
5
- vi.mock("../../client.js", () => ({
6
- capture: vi.fn(),
7
- }));
8
- // Mock constants
9
- vi.mock("../../lib/session/constants.js", () => ({
10
- ALLOW_BROWSER_AI: true,
11
- SESSION_SUMMARY_ENABLED: true,
12
- SESSION_SUMMARY_MAX_EVENTS: 100,
13
- SESSION_SUMMARY_WINDOW_MS: 30_000,
14
- }));
15
- // Constants for tests
16
- const SUMMARY_WINDOW_MS = 30_000;
17
- const MAX_EVENTS_TO_GENERATE = 150;
18
- const EVENTS_TO_GENERATE = 30;
19
- const SAMPLE_SIZE = 20;
20
- const MAX_SUMMARY_LENGTH = 2000;
21
- const LONGER_THAN_MAX_LENGTH = 3000;
22
- describe("session-summary", () => {
23
- let originalWindow;
24
- let originalDocument;
25
- let mockAddEventListener;
26
- let mockRemoveEventListener;
27
- let mockDocAddEventListener;
28
- let mockDocRemoveEventListener;
29
- let mockSetInterval;
30
- let mockClearInterval;
31
- let eventListeners;
32
- let docEventListeners;
33
- let intervalCallbacks;
34
- let intervalId = 1;
35
- beforeEach(() => {
36
- vi.clearAllMocks();
37
- // Reset environment variables using vi.stubEnv
38
- vi.stubEnv("NODE_ENV", "test");
39
- // Store original globals
40
- originalWindow = global.window;
41
- originalDocument = global.document;
42
- // Create event listener tracking
43
- eventListeners = new Map();
44
- docEventListeners = new Map();
45
- intervalCallbacks = new Map();
46
- // Mock window addEventListener/removeEventListener
47
- mockAddEventListener = vi.fn((event, handler) => {
48
- if (!eventListeners.has(event)) {
49
- eventListeners.set(event, new Set());
50
- }
51
- eventListeners.get(event)?.add(handler);
52
- });
53
- mockRemoveEventListener = vi.fn((event, handler) => {
54
- eventListeners.get(event)?.delete(handler);
55
- });
56
- // Mock document addEventListener/removeEventListener
57
- mockDocAddEventListener = vi.fn((event, handler) => {
58
- if (!docEventListeners.has(event)) {
59
- docEventListeners.set(event, new Set());
60
- }
61
- docEventListeners.get(event)?.add(handler);
62
- });
63
- mockDocRemoveEventListener = vi.fn((event, handler) => {
64
- docEventListeners.get(event)?.delete(handler);
65
- });
66
- // Mock setInterval/clearInterval
67
- mockSetInterval = vi.fn((callback, _ms) => {
68
- const id = intervalId++;
69
- intervalCallbacks.set(id, callback);
70
- return id;
71
- });
72
- mockClearInterval = vi.fn((id) => {
73
- intervalCallbacks.delete(id);
74
- });
75
- // Setup global window and document mocks
76
- global.window = {
77
- addEventListener: mockAddEventListener,
78
- removeEventListener: mockRemoveEventListener,
79
- setInterval: mockSetInterval,
80
- clearInterval: mockClearInterval,
81
- ai: undefined,
82
- };
83
- global.document = {
84
- addEventListener: mockDocAddEventListener,
85
- removeEventListener: mockDocRemoveEventListener,
86
- visibilityState: "visible",
87
- };
88
- // Mock history API
89
- global.history = {
90
- pushState: vi.fn(),
91
- replaceState: vi.fn(),
92
- };
93
- });
94
- afterEach(() => {
95
- stopSessionSummary();
96
- global.window = originalWindow;
97
- global.document = originalDocument;
98
- vi.resetAllMocks();
99
- });
100
- describe("setupSessionSummary", () => {
101
- it("should initialize event listeners when in browser environment", () => {
102
- setupSessionSummary();
103
- // Check that event listeners were added
104
- expect(mockDocAddEventListener).toHaveBeenCalledWith("click", expect.any(Function), { capture: true });
105
- expect(mockDocAddEventListener).toHaveBeenCalledWith("keydown", expect.any(Function), { capture: true });
106
- expect(mockAddEventListener).toHaveBeenCalledWith("popstate", expect.any(Function));
107
- expect(mockAddEventListener).toHaveBeenCalledWith("error", expect.any(Function));
108
- expect(mockAddEventListener).toHaveBeenCalledWith("unhandledrejection", expect.any(Function));
109
- expect(mockAddEventListener).toHaveBeenCalledWith("beforeunload", expect.any(Function));
110
- expect(mockDocAddEventListener).toHaveBeenCalledWith("visibilitychange", expect.any(Function));
111
- // Check that interval was set
112
- expect(mockSetInterval).toHaveBeenCalledWith(expect.any(Function), SUMMARY_WINDOW_MS);
113
- });
114
- it("should not initialize twice", () => {
115
- setupSessionSummary();
116
- const firstCallCount = mockAddEventListener.mock.calls.length;
117
- setupSessionSummary();
118
- expect(mockAddEventListener).toHaveBeenCalledTimes(firstCallCount);
119
- });
120
- it("should not initialize when not in browser environment", () => {
121
- // Remove window to simulate non-browser environment
122
- // @ts-expect-error - intentionally setting window to undefined for test
123
- global.window = undefined;
124
- setupSessionSummary();
125
- expect(mockAddEventListener).not.toHaveBeenCalled();
126
- // Restore window
127
- global.window = {
128
- addEventListener: mockAddEventListener,
129
- removeEventListener: mockRemoveEventListener,
130
- };
131
- });
132
- it("should not initialize when SESSION_SUMMARY_ENABLED is false", () => {
133
- // This test would require re-mocking the module which is complex in vitest
134
- // Skipping for now as SESSION_SUMMARY_ENABLED is always true in current implementation
135
- expect(true).toBe(true);
136
- });
137
- });
138
- describe("event tracking", () => {
139
- beforeEach(() => {
140
- setupSessionSummary();
141
- });
142
- it("should track click events", () => {
143
- const clickHandler = docEventListeners
144
- .get("click")
145
- ?.values()
146
- .next().value;
147
- expect(clickHandler).toBeDefined();
148
- if (clickHandler) {
149
- // Simulate click with aria-label
150
- const event1 = {
151
- target: {
152
- getAttribute: vi.fn((attr) => attr === "aria-label" ? "Submit Button" : null),
153
- textContent: "Click me",
154
- },
155
- };
156
- clickHandler(event1);
157
- // Simulate click with text content
158
- const event2 = {
159
- target: {
160
- getAttribute: vi.fn(() => null),
161
- textContent: "Another button with very long text that should be truncated after 60 characters",
162
- },
163
- };
164
- clickHandler(event2);
165
- // Simulate click with no label
166
- const event3 = {
167
- target: {
168
- getAttribute: vi.fn(() => null),
169
- textContent: null,
170
- },
171
- };
172
- clickHandler(event3);
173
- }
174
- });
175
- it("should track keydown events", () => {
176
- const keyHandler = docEventListeners
177
- .get("keydown")
178
- ?.values()
179
- .next().value;
180
- expect(keyHandler).toBeDefined();
181
- if (keyHandler) {
182
- // Simulate keydown event
183
- keyHandler({});
184
- }
185
- });
186
- it("should track navigation events", () => {
187
- const originalPushState = vi.fn();
188
- const originalReplaceState = vi.fn();
189
- global.history.pushState = originalPushState;
190
- global.history.replaceState = originalReplaceState;
191
- setupSessionSummary();
192
- // The wrapped functions should be called
193
- global.history.pushState({}, "", "/new-page");
194
- expect(originalPushState).toHaveBeenCalled();
195
- global.history.replaceState({}, "", "/replaced-page");
196
- expect(originalReplaceState).toHaveBeenCalled();
197
- // Test popstate
198
- const popstateHandler = eventListeners
199
- .get("popstate")
200
- ?.values()
201
- .next().value;
202
- expect(popstateHandler).toBeDefined();
203
- if (popstateHandler) {
204
- popstateHandler({});
205
- }
206
- });
207
- it("should track error events", () => {
208
- const errorHandler = eventListeners.get("error")?.values().next().value;
209
- expect(errorHandler).toBeDefined();
210
- if (errorHandler) {
211
- // Simulate error event
212
- errorHandler({
213
- message: "Test error message",
214
- });
215
- // Simulate error event without message
216
- errorHandler({});
217
- }
218
- });
219
- it("should track unhandled rejection events", () => {
220
- const rejectionHandler = eventListeners
221
- .get("unhandledrejection")
222
- ?.values()
223
- .next().value;
224
- expect(rejectionHandler).toBeDefined();
225
- if (rejectionHandler) {
226
- // Simulate rejection with Error object
227
- rejectionHandler({
228
- reason: new Error("Promise rejection error"),
229
- });
230
- // Simulate rejection with string reason
231
- rejectionHandler({
232
- reason: "String rejection",
233
- });
234
- // Simulate rejection with object reason without message
235
- rejectionHandler({
236
- reason: { someProperty: "value" },
237
- });
238
- // Simulate rejection without reason
239
- rejectionHandler({});
240
- }
241
- });
242
- });
243
- describe("event buffer management", () => {
244
- it("should limit buffer size to max events", () => {
245
- setupSessionSummary();
246
- const clickHandler = docEventListeners
247
- .get("click")
248
- ?.values()
249
- .next().value;
250
- if (clickHandler) {
251
- // Generate more events than max (assuming max is 100)
252
- for (let i = 0; i < MAX_EVENTS_TO_GENERATE; i++) {
253
- clickHandler({
254
- target: {
255
- getAttribute: vi.fn(() => null),
256
- textContent: `Click ${i}`,
257
- },
258
- });
259
- }
260
- }
261
- // Buffer should be limited to max events
262
- // We can't directly check buffer size, but we can verify behavior
263
- // by checking that old events are removed
264
- });
265
- });
266
- describe("summary emission", () => {
267
- beforeEach(() => {
268
- vi.useFakeTimers();
269
- setupSessionSummary();
270
- });
271
- afterEach(() => {
272
- vi.useRealTimers();
273
- });
274
- it("should emit summary on interval", async () => {
275
- const clickHandler = docEventListeners
276
- .get("click")
277
- ?.values()
278
- .next().value;
279
- if (clickHandler) {
280
- // Add some events
281
- clickHandler({
282
- target: {
283
- getAttribute: vi.fn(() => "Test Button"),
284
- textContent: "Click",
285
- },
286
- });
287
- }
288
- // Get the interval callback
289
- const intervalCallback = Array.from(intervalCallbacks.values())[0];
290
- expect(intervalCallback).toBeDefined();
291
- if (intervalCallback) {
292
- // Execute the interval callback
293
- await intervalCallback();
294
- // Check that capture was called
295
- expect(vi.mocked(captureFn)).toHaveBeenCalledWith("session_summary", expect.objectContaining({
296
- version: 1,
297
- eventCount: expect.any(Number),
298
- sample: expect.any(Array),
299
- summary: null, // No AI available in test
300
- }));
301
- }
302
- });
303
- it("should emit summary on beforeunload", async () => {
304
- const clickHandler = docEventListeners
305
- .get("click")
306
- ?.values()
307
- .next().value;
308
- if (clickHandler) {
309
- // Add some events
310
- clickHandler({
311
- target: {
312
- getAttribute: vi.fn(() => null),
313
- textContent: "Test",
314
- },
315
- });
316
- }
317
- // Trigger beforeunload
318
- const beforeUnloadHandler = eventListeners
319
- .get("beforeunload")
320
- ?.values()
321
- .next().value;
322
- expect(beforeUnloadHandler).toBeDefined();
323
- if (beforeUnloadHandler) {
324
- await beforeUnloadHandler();
325
- expect(vi.mocked(captureFn)).toHaveBeenCalled();
326
- }
327
- });
328
- it("should emit summary on visibility change to hidden", async () => {
329
- const clickHandler = docEventListeners
330
- .get("click")
331
- ?.values()
332
- .next().value;
333
- if (clickHandler) {
334
- // Add some events
335
- clickHandler({
336
- target: {
337
- getAttribute: vi.fn(() => null),
338
- textContent: "Test",
339
- },
340
- });
341
- }
342
- // Change visibility to hidden
343
- Object.defineProperty(global.document, "visibilityState", {
344
- value: "hidden",
345
- writable: true,
346
- configurable: true,
347
- });
348
- const visibilityHandler = docEventListeners
349
- .get("visibilitychange")
350
- ?.values()
351
- .next().value;
352
- expect(visibilityHandler).toBeDefined();
353
- if (visibilityHandler) {
354
- await visibilityHandler();
355
- expect(vi.mocked(captureFn)).toHaveBeenCalled();
356
- }
357
- });
358
- it("should not emit summary when no events", async () => {
359
- // Get the interval callback
360
- const intervalCallback = Array.from(intervalCallbacks.values())[0];
361
- if (intervalCallback) {
362
- // Execute without any events
363
- await intervalCallback();
364
- expect(vi.mocked(captureFn)).not.toHaveBeenCalled();
365
- }
366
- });
367
- it("should handle capture errors gracefully", async () => {
368
- vi.mocked(captureFn).mockImplementation(() => {
369
- throw new Error("Capture failed");
370
- });
371
- const clickHandler = docEventListeners
372
- .get("click")
373
- ?.values()
374
- .next().value;
375
- if (clickHandler) {
376
- clickHandler({
377
- target: {
378
- getAttribute: vi.fn(() => null),
379
- textContent: "Test",
380
- },
381
- });
382
- }
383
- const intervalCallback = Array.from(intervalCallbacks.values())[0];
384
- if (intervalCallback) {
385
- // Should not throw
386
- await expect(Promise.resolve(intervalCallback())).resolves.not.toThrow();
387
- }
388
- });
389
- });
390
- describe("browser AI summarization", () => {
391
- it("should use browser AI when available", async () => {
392
- // Note: Browser AI is mocked but ALLOW_BROWSER_AI constant determines if it's used
393
- // Since we can't easily change the constant after module import, we'll verify
394
- // the behavior with the current setting
395
- const mockPrompt = vi.fn().mockResolvedValue(JSON.stringify({
396
- summary: "User clicked buttons",
397
- keyActions: ["click"],
398
- errors: [],
399
- }));
400
- const mockCreateTextSession = vi.fn().mockResolvedValue({
401
- prompt: mockPrompt,
402
- });
403
- const mockCanCreateTextSession = vi.fn().mockResolvedValue("readily");
404
- global.window.ai = {
405
- canCreateTextSession: mockCanCreateTextSession,
406
- createTextSession: mockCreateTextSession,
407
- };
408
- setupSessionSummary();
409
- // Add events
410
- const clickHandler = docEventListeners
411
- .get("click")
412
- ?.values()
413
- .next().value;
414
- if (clickHandler) {
415
- clickHandler({
416
- target: {
417
- getAttribute: vi.fn(() => "Submit"),
418
- textContent: "Submit",
419
- },
420
- });
421
- }
422
- // Trigger summary
423
- const intervalCallback = Array.from(intervalCallbacks.values())[0];
424
- if (intervalCallback) {
425
- await intervalCallback();
426
- // With ALLOW_BROWSER_AI = true (from mock), AI should be called
427
- if (mockCanCreateTextSession.mock.calls.length > 0) {
428
- expect(mockCreateTextSession).toHaveBeenCalledWith({
429
- temperature: 0.2,
430
- topK: 40,
431
- topP: 0.95,
432
- });
433
- expect(mockPrompt).toHaveBeenCalled();
434
- expect(vi.mocked(captureFn)).toHaveBeenCalledWith("session_summary", expect.objectContaining({
435
- summary: expect.stringContaining("User clicked buttons"),
436
- }));
437
- }
438
- else {
439
- // If AI wasn't called, verify capture was still called without summary
440
- expect(vi.mocked(captureFn)).toHaveBeenCalledWith("session_summary", expect.objectContaining({
441
- summary: null,
442
- }));
443
- }
444
- }
445
- });
446
- it("should handle AI not ready", async () => {
447
- global.window.ai = {
448
- canCreateTextSession: vi.fn().mockResolvedValue("no"),
449
- createTextSession: vi.fn(),
450
- };
451
- setupSessionSummary();
452
- // Add events
453
- const clickHandler = docEventListeners
454
- .get("click")
455
- ?.values()
456
- .next().value;
457
- if (clickHandler) {
458
- clickHandler({
459
- target: {
460
- getAttribute: vi.fn(() => null),
461
- textContent: "Test",
462
- },
463
- });
464
- }
465
- // Trigger summary
466
- const intervalCallback = Array.from(intervalCallbacks.values())[0];
467
- if (intervalCallback) {
468
- await intervalCallback();
469
- expect(vi.mocked(captureFn)).toHaveBeenCalledWith("session_summary", expect.objectContaining({
470
- summary: null,
471
- }));
472
- }
473
- });
474
- it("should handle AI after-download state", async () => {
475
- const mockPrompt = vi.fn().mockResolvedValue("AI summary");
476
- global.window.ai = {
477
- canCreateTextSession: vi.fn().mockResolvedValue("after-download"),
478
- createTextSession: vi.fn().mockResolvedValue({
479
- prompt: mockPrompt,
480
- }),
481
- };
482
- setupSessionSummary();
483
- // Add events
484
- const clickHandler = docEventListeners
485
- .get("click")
486
- ?.values()
487
- .next().value;
488
- if (clickHandler) {
489
- clickHandler({
490
- target: {
491
- getAttribute: vi.fn(() => null),
492
- textContent: "Test",
493
- },
494
- });
495
- }
496
- // Trigger summary
497
- const intervalCallback = Array.from(intervalCallbacks.values())[0];
498
- if (intervalCallback) {
499
- await intervalCallback();
500
- // AI may or may not be called depending on ALLOW_BROWSER_AI constant
501
- expect(vi.mocked(captureFn)).toHaveBeenCalled();
502
- }
503
- });
504
- it("should handle AI errors gracefully", async () => {
505
- global.window.ai = {
506
- canCreateTextSession: vi.fn().mockRejectedValue(new Error("AI failed")),
507
- createTextSession: vi.fn(),
508
- };
509
- setupSessionSummary();
510
- // Add events
511
- const clickHandler = docEventListeners
512
- .get("click")
513
- ?.values()
514
- .next().value;
515
- if (clickHandler) {
516
- clickHandler({
517
- target: {
518
- getAttribute: vi.fn(() => null),
519
- textContent: "Test",
520
- },
521
- });
522
- }
523
- // Trigger summary
524
- const intervalCallback = Array.from(intervalCallbacks.values())[0];
525
- if (intervalCallback) {
526
- await intervalCallback();
527
- expect(vi.mocked(captureFn)).toHaveBeenCalledWith("session_summary", expect.objectContaining({
528
- summary: null,
529
- }));
530
- }
531
- });
532
- it("should truncate long AI responses", async () => {
533
- const longResponse = "x".repeat(LONGER_THAN_MAX_LENGTH); // Longer than MAX_LENGTH (2000)
534
- global.window.ai = {
535
- canCreateTextSession: vi.fn().mockResolvedValue("readily"),
536
- createTextSession: vi.fn().mockResolvedValue({
537
- prompt: vi.fn().mockResolvedValue(longResponse),
538
- }),
539
- };
540
- setupSessionSummary();
541
- // Add events
542
- const clickHandler = docEventListeners
543
- .get("click")
544
- ?.values()
545
- .next().value;
546
- if (clickHandler) {
547
- clickHandler({
548
- target: {
549
- getAttribute: vi.fn(() => null),
550
- textContent: "Test",
551
- },
552
- });
553
- }
554
- // Trigger summary
555
- const intervalCallback = Array.from(intervalCallbacks.values())[0];
556
- if (intervalCallback) {
557
- await intervalCallback();
558
- expect(vi.mocked(captureFn)).toHaveBeenCalled();
559
- const call = vi.mocked(captureFn).mock.calls[0];
560
- // If AI was used, check truncation
561
- if (call?.[1]) {
562
- const payload = call[1];
563
- if (payload.summary && payload.summary !== null) {
564
- expect(payload.summary.length).toBeLessThanOrEqual(MAX_SUMMARY_LENGTH);
565
- }
566
- }
567
- }
568
- });
569
- it("should handle empty AI response", async () => {
570
- global.window.ai = {
571
- canCreateTextSession: vi.fn().mockResolvedValue("readily"),
572
- createTextSession: vi.fn().mockResolvedValue({
573
- prompt: vi.fn().mockResolvedValue(""),
574
- }),
575
- };
576
- setupSessionSummary();
577
- // Add events
578
- const clickHandler = docEventListeners
579
- .get("click")
580
- ?.values()
581
- .next().value;
582
- if (clickHandler) {
583
- clickHandler({
584
- target: {
585
- getAttribute: vi.fn(() => null),
586
- textContent: "Test",
587
- },
588
- });
589
- }
590
- // Trigger summary
591
- const intervalCallback = Array.from(intervalCallbacks.values())[0];
592
- if (intervalCallback) {
593
- await intervalCallback();
594
- expect(vi.mocked(captureFn)).toHaveBeenCalledWith("session_summary", expect.objectContaining({
595
- summary: null,
596
- }));
597
- }
598
- });
599
- });
600
- describe("stopSessionSummary", () => {
601
- it("should remove all event listeners", () => {
602
- setupSessionSummary();
603
- // Verify listeners were added
604
- expect(mockAddEventListener).toHaveBeenCalled();
605
- expect(mockDocAddEventListener).toHaveBeenCalled();
606
- expect(mockSetInterval).toHaveBeenCalled();
607
- stopSessionSummary();
608
- // Verify listeners were removed
609
- expect(mockRemoveEventListener).toHaveBeenCalled();
610
- expect(mockDocRemoveEventListener).toHaveBeenCalled();
611
- expect(mockClearInterval).toHaveBeenCalled();
612
- });
613
- it("should reset initialized state", () => {
614
- setupSessionSummary();
615
- const firstCallCount = mockAddEventListener.mock.calls.length;
616
- stopSessionSummary();
617
- // Should be able to initialize again
618
- setupSessionSummary();
619
- expect(mockAddEventListener.mock.calls.length).toBeGreaterThan(firstCallCount);
620
- });
621
- it("should clear buffer", () => {
622
- setupSessionSummary();
623
- // Add some events
624
- const clickHandler = docEventListeners
625
- .get("click")
626
- ?.values()
627
- .next().value;
628
- if (clickHandler) {
629
- clickHandler({
630
- target: {
631
- getAttribute: vi.fn(() => null),
632
- textContent: "Test",
633
- },
634
- });
635
- }
636
- stopSessionSummary();
637
- // Re-initialize and check that buffer was cleared
638
- setupSessionSummary();
639
- // Trigger summary immediately - should have no events
640
- const intervalCallback = Array.from(intervalCallbacks.values())[0];
641
- if (intervalCallback) {
642
- intervalCallback();
643
- expect(vi.mocked(captureFn)).not.toHaveBeenCalled();
644
- }
645
- });
646
- it("should handle teardown errors gracefully", () => {
647
- setupSessionSummary();
648
- // Make removeEventListener throw
649
- mockRemoveEventListener.mockImplementation(() => {
650
- throw new Error("Remove failed");
651
- });
652
- // Should not throw
653
- expect(() => stopSessionSummary()).not.toThrow();
654
- });
655
- it("should restore original history methods", () => {
656
- const originalPushState = vi.fn();
657
- const originalReplaceState = vi.fn();
658
- global.history.pushState = originalPushState;
659
- global.history.replaceState = originalReplaceState;
660
- setupSessionSummary();
661
- // History methods should be wrapped
662
- expect(global.history.pushState).not.toBe(originalPushState);
663
- expect(global.history.replaceState).not.toBe(originalReplaceState);
664
- stopSessionSummary();
665
- // History methods should be restored (they are bound functions)
666
- // We can verify they work correctly by calling them
667
- global.history.pushState({}, "", "/test");
668
- global.history.replaceState({}, "", "/test2");
669
- expect(originalPushState).toHaveBeenCalledWith({}, "", "/test");
670
- expect(originalReplaceState).toHaveBeenCalledWith({}, "", "/test2");
671
- });
672
- });
673
- describe("sample events", () => {
674
- it("should include last 20 events in sample", async () => {
675
- setupSessionSummary();
676
- const clickHandler = docEventListeners
677
- .get("click")
678
- ?.values()
679
- .next().value;
680
- if (clickHandler) {
681
- // Add 30 events
682
- for (let i = 0; i < EVENTS_TO_GENERATE; i++) {
683
- clickHandler({
684
- target: {
685
- getAttribute: vi.fn(() => null),
686
- textContent: `Click ${i}`,
687
- },
688
- });
689
- }
690
- }
691
- // Trigger summary
692
- const intervalCallback = Array.from(intervalCallbacks.values())[0];
693
- if (intervalCallback) {
694
- await intervalCallback();
695
- expect(vi.mocked(captureFn)).toHaveBeenCalledWith("session_summary", expect.objectContaining({
696
- eventCount: EVENTS_TO_GENERATE,
697
- sample: expect.arrayContaining([
698
- expect.objectContaining({
699
- kind: "ui",
700
- action: "click",
701
- }),
702
- ]),
703
- }));
704
- const call = vi.mocked(captureFn).mock.calls[0];
705
- if (call?.[1]) {
706
- const payload = call[1];
707
- expect(payload.sample.length).toBeLessThanOrEqual(SAMPLE_SIZE);
708
- }
709
- }
710
- });
711
- });
712
- describe("ALLOW_BROWSER_AI flag", () => {
713
- it("should not attempt AI summarization when ALLOW_BROWSER_AI is false", async () => {
714
- // Re-mock constants with ALLOW_BROWSER_AI = false
715
- vi.doUnmock("../../lib/session/constants.js");
716
- vi.mock("../../lib/session/constants.js", () => ({
717
- ALLOW_BROWSER_AI: false,
718
- SESSION_SUMMARY_ENABLED: true,
719
- SESSION_SUMMARY_MAX_EVENTS: 100,
720
- SESSION_SUMMARY_WINDOW_MS: 30_000,
721
- }));
722
- // Setup AI mock
723
- const mockPrompt = vi.fn();
724
- global.window.ai = {
725
- canCreateTextSession: vi.fn(),
726
- createTextSession: vi.fn().mockResolvedValue({
727
- prompt: mockPrompt,
728
- }),
729
- };
730
- // Re-import to get new mock
731
- const module = await import("../../lib/session/session-summary.js");
732
- module.setupSessionSummary();
733
- // Add events
734
- const clickHandler = docEventListeners
735
- .get("click")
736
- ?.values()
737
- .next().value;
738
- if (clickHandler) {
739
- clickHandler({
740
- target: {
741
- getAttribute: vi.fn(() => null),
742
- textContent: "Test",
743
- },
744
- });
745
- }
746
- // Trigger summary
747
- const intervalCallback = Array.from(intervalCallbacks.values())[0];
748
- if (intervalCallback) {
749
- await intervalCallback();
750
- // AI should not be called
751
- const windowWithAI = global.window;
752
- if (windowWithAI.ai) {
753
- expect(windowWithAI.ai.canCreateTextSession).not.toHaveBeenCalled();
754
- }
755
- expect(mockPrompt).not.toHaveBeenCalled();
756
- expect(vi.mocked(captureFn)).toHaveBeenCalledWith("session_summary", expect.objectContaining({
757
- summary: null,
758
- }));
759
- }
760
- });
761
- });
762
- });
763
- //# sourceMappingURL=session-summary.test.js.map