@tracelog/lib 0.0.8 → 0.2.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 (228) hide show
  1. package/README.md +58 -24
  2. package/dist/browser/tracelog.js +1934 -3226
  3. package/dist/cjs/api.d.ts +33 -19
  4. package/dist/cjs/api.js +111 -156
  5. package/dist/cjs/app.constants.d.ts +80 -1
  6. package/dist/cjs/app.constants.js +90 -3
  7. package/dist/cjs/app.d.ts +29 -44
  8. package/dist/cjs/app.js +114 -212
  9. package/dist/cjs/app.types.d.ts +2 -7
  10. package/dist/cjs/app.types.js +10 -21
  11. package/dist/cjs/constants/api.constants.js +11 -5
  12. package/dist/cjs/constants/config.constants.d.ts +75 -0
  13. package/dist/cjs/constants/config.constants.js +178 -0
  14. package/dist/cjs/constants/error.constants.d.ts +29 -0
  15. package/dist/cjs/constants/error.constants.js +50 -0
  16. package/dist/cjs/constants/index.d.ts +3 -6
  17. package/dist/cjs/constants/index.js +3 -6
  18. package/dist/cjs/constants/performance.constants.d.ts +28 -0
  19. package/dist/cjs/constants/performance.constants.js +43 -0
  20. package/dist/cjs/handlers/click.handler.d.ts +1 -0
  21. package/dist/cjs/handlers/click.handler.js +30 -49
  22. package/dist/cjs/handlers/error.handler.d.ts +11 -6
  23. package/dist/cjs/handlers/error.handler.js +91 -51
  24. package/dist/cjs/handlers/page-view.handler.js +38 -29
  25. package/dist/cjs/handlers/performance.handler.d.ts +3 -0
  26. package/dist/cjs/handlers/performance.handler.js +76 -37
  27. package/dist/cjs/handlers/scroll.handler.d.ts +15 -0
  28. package/dist/cjs/handlers/scroll.handler.js +105 -31
  29. package/dist/cjs/handlers/session.handler.d.ts +6 -20
  30. package/dist/cjs/handlers/session.handler.js +38 -326
  31. package/dist/cjs/integrations/google-analytics.integration.d.ts +0 -1
  32. package/dist/cjs/integrations/google-analytics.integration.js +27 -98
  33. package/dist/cjs/listeners/input-listener-managers.d.ts +18 -9
  34. package/dist/cjs/listeners/input-listener-managers.js +24 -33
  35. package/dist/cjs/listeners/touch-listener-manager.d.ts +1 -3
  36. package/dist/cjs/listeners/touch-listener-manager.js +1 -23
  37. package/dist/cjs/listeners/visibility-listener-manager.d.ts +1 -4
  38. package/dist/cjs/listeners/visibility-listener-manager.js +6 -42
  39. package/dist/cjs/managers/api.manager.d.ts +13 -3
  40. package/dist/cjs/managers/api.manager.js +35 -5
  41. package/dist/cjs/managers/config.manager.d.ts +53 -3
  42. package/dist/cjs/managers/config.manager.js +131 -62
  43. package/dist/cjs/managers/event.manager.d.ts +57 -36
  44. package/dist/cjs/managers/event.manager.js +266 -417
  45. package/dist/cjs/managers/sender.manager.d.ts +40 -22
  46. package/dist/cjs/managers/sender.manager.js +200 -198
  47. package/dist/cjs/managers/session.manager.d.ts +80 -66
  48. package/dist/cjs/managers/session.manager.js +267 -522
  49. package/dist/cjs/managers/state.manager.d.ts +33 -0
  50. package/dist/cjs/managers/state.manager.js +79 -6
  51. package/dist/cjs/managers/storage.manager.d.ts +26 -2
  52. package/dist/cjs/managers/storage.manager.js +67 -34
  53. package/dist/cjs/managers/tags.manager.d.ts +31 -7
  54. package/dist/cjs/managers/tags.manager.js +123 -241
  55. package/dist/cjs/managers/user.manager.d.ts +14 -5
  56. package/dist/cjs/managers/user.manager.js +17 -9
  57. package/dist/cjs/public-api.d.ts +10 -1
  58. package/dist/cjs/public-api.js +18 -24
  59. package/dist/cjs/test-bridge.d.ts +48 -0
  60. package/dist/cjs/test-bridge.js +110 -0
  61. package/dist/cjs/types/api.types.d.ts +21 -6
  62. package/dist/cjs/types/api.types.js +21 -6
  63. package/dist/cjs/types/config.types.d.ts +22 -84
  64. package/dist/cjs/types/emitter.types.d.ts +11 -0
  65. package/dist/cjs/types/emitter.types.js +8 -0
  66. package/dist/cjs/types/event.types.d.ts +8 -11
  67. package/dist/cjs/types/index.d.ts +3 -1
  68. package/dist/cjs/types/index.js +3 -1
  69. package/dist/cjs/types/queue.types.d.ts +1 -0
  70. package/dist/cjs/types/session.types.d.ts +0 -64
  71. package/dist/cjs/types/state.types.d.ts +1 -0
  72. package/dist/cjs/types/test-bridge.types.d.ts +38 -0
  73. package/dist/cjs/types/validation-error.types.d.ts +7 -0
  74. package/dist/cjs/types/validation-error.types.js +11 -1
  75. package/dist/cjs/types/window.types.d.ts +1 -8
  76. package/dist/cjs/utils/data/uuid.utils.d.ts +1 -1
  77. package/dist/cjs/utils/data/uuid.utils.js +7 -5
  78. package/dist/cjs/utils/emitter.utils.d.ts +8 -0
  79. package/dist/cjs/utils/emitter.utils.js +33 -0
  80. package/dist/cjs/utils/index.d.ts +1 -0
  81. package/dist/cjs/utils/index.js +1 -0
  82. package/dist/cjs/utils/logging/debug-logger.utils.d.ts +10 -51
  83. package/dist/cjs/utils/logging/debug-logger.utils.js +36 -127
  84. package/dist/cjs/utils/network/fetch-with-timeout.utils.d.ts +4 -0
  85. package/dist/cjs/utils/network/fetch-with-timeout.utils.js +25 -0
  86. package/dist/cjs/utils/network/index.d.ts +1 -0
  87. package/dist/cjs/utils/network/index.js +1 -0
  88. package/dist/cjs/utils/network/url.utils.js +2 -42
  89. package/dist/cjs/utils/security/sanitize.utils.d.ts +1 -8
  90. package/dist/cjs/utils/security/sanitize.utils.js +7 -41
  91. package/dist/cjs/utils/validations/config-validations.utils.d.ts +7 -0
  92. package/dist/cjs/utils/validations/config-validations.utils.js +77 -22
  93. package/dist/esm/api.d.ts +33 -19
  94. package/dist/esm/api.js +105 -118
  95. package/dist/esm/app.constants.d.ts +80 -1
  96. package/dist/esm/app.constants.js +89 -1
  97. package/dist/esm/app.d.ts +29 -44
  98. package/dist/esm/app.js +115 -213
  99. package/dist/esm/app.types.d.ts +2 -7
  100. package/dist/esm/app.types.js +1 -7
  101. package/dist/esm/constants/api.constants.js +10 -4
  102. package/dist/esm/constants/config.constants.d.ts +75 -0
  103. package/dist/esm/constants/config.constants.js +174 -0
  104. package/dist/esm/constants/error.constants.d.ts +29 -0
  105. package/dist/esm/constants/error.constants.js +47 -0
  106. package/dist/esm/constants/index.d.ts +3 -6
  107. package/dist/esm/constants/index.js +3 -6
  108. package/dist/esm/constants/performance.constants.d.ts +28 -0
  109. package/dist/esm/constants/performance.constants.js +40 -0
  110. package/dist/esm/handlers/click.handler.d.ts +1 -0
  111. package/dist/esm/handlers/click.handler.js +30 -49
  112. package/dist/esm/handlers/error.handler.d.ts +11 -6
  113. package/dist/esm/handlers/error.handler.js +91 -51
  114. package/dist/esm/handlers/page-view.handler.js +38 -29
  115. package/dist/esm/handlers/performance.handler.d.ts +3 -0
  116. package/dist/esm/handlers/performance.handler.js +71 -32
  117. package/dist/esm/handlers/scroll.handler.d.ts +15 -0
  118. package/dist/esm/handlers/scroll.handler.js +106 -32
  119. package/dist/esm/handlers/session.handler.d.ts +6 -20
  120. package/dist/esm/handlers/session.handler.js +38 -326
  121. package/dist/esm/integrations/google-analytics.integration.d.ts +0 -1
  122. package/dist/esm/integrations/google-analytics.integration.js +27 -98
  123. package/dist/esm/listeners/input-listener-managers.d.ts +18 -9
  124. package/dist/esm/listeners/input-listener-managers.js +23 -32
  125. package/dist/esm/listeners/touch-listener-manager.d.ts +1 -3
  126. package/dist/esm/listeners/touch-listener-manager.js +1 -23
  127. package/dist/esm/listeners/visibility-listener-manager.d.ts +1 -4
  128. package/dist/esm/listeners/visibility-listener-manager.js +6 -42
  129. package/dist/esm/managers/api.manager.d.ts +13 -3
  130. package/dist/esm/managers/api.manager.js +34 -3
  131. package/dist/esm/managers/config.manager.d.ts +53 -3
  132. package/dist/esm/managers/config.manager.js +133 -64
  133. package/dist/esm/managers/event.manager.d.ts +57 -36
  134. package/dist/esm/managers/event.manager.js +268 -419
  135. package/dist/esm/managers/sender.manager.d.ts +40 -22
  136. package/dist/esm/managers/sender.manager.js +201 -199
  137. package/dist/esm/managers/session.manager.d.ts +80 -66
  138. package/dist/esm/managers/session.manager.js +269 -524
  139. package/dist/esm/managers/state.manager.d.ts +33 -0
  140. package/dist/esm/managers/state.manager.js +78 -6
  141. package/dist/esm/managers/storage.manager.d.ts +26 -2
  142. package/dist/esm/managers/storage.manager.js +66 -33
  143. package/dist/esm/managers/tags.manager.d.ts +31 -7
  144. package/dist/esm/managers/tags.manager.js +124 -242
  145. package/dist/esm/managers/user.manager.d.ts +14 -5
  146. package/dist/esm/managers/user.manager.js +17 -9
  147. package/dist/esm/public-api.d.ts +10 -1
  148. package/dist/esm/public-api.js +14 -1
  149. package/dist/esm/test-bridge.d.ts +48 -0
  150. package/dist/esm/test-bridge.js +106 -0
  151. package/dist/esm/types/api.types.d.ts +21 -6
  152. package/dist/esm/types/api.types.js +21 -6
  153. package/dist/esm/types/config.types.d.ts +22 -84
  154. package/dist/esm/types/emitter.types.d.ts +11 -0
  155. package/dist/esm/types/emitter.types.js +5 -0
  156. package/dist/esm/types/event.types.d.ts +8 -11
  157. package/dist/esm/types/index.d.ts +3 -1
  158. package/dist/esm/types/index.js +3 -1
  159. package/dist/esm/types/queue.types.d.ts +1 -0
  160. package/dist/esm/types/session.types.d.ts +0 -64
  161. package/dist/esm/types/state.types.d.ts +1 -0
  162. package/dist/esm/types/test-bridge.types.d.ts +38 -0
  163. package/dist/esm/types/validation-error.types.d.ts +7 -0
  164. package/dist/esm/types/validation-error.types.js +9 -0
  165. package/dist/esm/types/window.types.d.ts +1 -8
  166. package/dist/esm/utils/data/uuid.utils.d.ts +1 -1
  167. package/dist/esm/utils/data/uuid.utils.js +7 -5
  168. package/dist/esm/utils/emitter.utils.d.ts +8 -0
  169. package/dist/esm/utils/emitter.utils.js +29 -0
  170. package/dist/esm/utils/index.d.ts +1 -0
  171. package/dist/esm/utils/index.js +1 -0
  172. package/dist/esm/utils/logging/debug-logger.utils.d.ts +10 -51
  173. package/dist/esm/utils/logging/debug-logger.utils.js +36 -127
  174. package/dist/esm/utils/network/fetch-with-timeout.utils.d.ts +4 -0
  175. package/dist/esm/utils/network/fetch-with-timeout.utils.js +22 -0
  176. package/dist/esm/utils/network/index.d.ts +1 -0
  177. package/dist/esm/utils/network/index.js +1 -0
  178. package/dist/esm/utils/network/url.utils.js +2 -42
  179. package/dist/esm/utils/security/sanitize.utils.d.ts +1 -8
  180. package/dist/esm/utils/security/sanitize.utils.js +6 -39
  181. package/dist/esm/utils/validations/config-validations.utils.d.ts +7 -0
  182. package/dist/esm/utils/validations/config-validations.utils.js +76 -22
  183. package/package.json +23 -16
  184. package/dist/browser/web-vitals-CCnqwnC8.mjs +0 -198
  185. package/dist/cjs/constants/browser.constants.d.ts +0 -3
  186. package/dist/cjs/constants/browser.constants.js +0 -41
  187. package/dist/cjs/constants/initialization.constants.d.ts +0 -40
  188. package/dist/cjs/constants/initialization.constants.js +0 -48
  189. package/dist/cjs/constants/limits.constants.d.ts +0 -25
  190. package/dist/cjs/constants/limits.constants.js +0 -40
  191. package/dist/cjs/constants/security.constants.d.ts +0 -1
  192. package/dist/cjs/constants/security.constants.js +0 -12
  193. package/dist/cjs/constants/timing.constants.d.ts +0 -22
  194. package/dist/cjs/constants/timing.constants.js +0 -34
  195. package/dist/cjs/constants/validation.constants.d.ts +0 -13
  196. package/dist/cjs/constants/validation.constants.js +0 -31
  197. package/dist/cjs/handlers/network.handler.d.ts +0 -16
  198. package/dist/cjs/handlers/network.handler.js +0 -136
  199. package/dist/cjs/managers/cross-tab-session.manager.d.ts +0 -170
  200. package/dist/cjs/managers/cross-tab-session.manager.js +0 -730
  201. package/dist/cjs/managers/sampling.manager.d.ts +0 -8
  202. package/dist/cjs/managers/sampling.manager.js +0 -53
  203. package/dist/cjs/managers/session-recovery.manager.d.ts +0 -65
  204. package/dist/cjs/managers/session-recovery.manager.js +0 -237
  205. package/dist/cjs/types/web-vitals.types.d.ts +0 -6
  206. package/dist/esm/constants/browser.constants.d.ts +0 -3
  207. package/dist/esm/constants/browser.constants.js +0 -38
  208. package/dist/esm/constants/initialization.constants.d.ts +0 -40
  209. package/dist/esm/constants/initialization.constants.js +0 -45
  210. package/dist/esm/constants/limits.constants.d.ts +0 -25
  211. package/dist/esm/constants/limits.constants.js +0 -37
  212. package/dist/esm/constants/security.constants.d.ts +0 -1
  213. package/dist/esm/constants/security.constants.js +0 -9
  214. package/dist/esm/constants/timing.constants.d.ts +0 -22
  215. package/dist/esm/constants/timing.constants.js +0 -31
  216. package/dist/esm/constants/validation.constants.d.ts +0 -13
  217. package/dist/esm/constants/validation.constants.js +0 -28
  218. package/dist/esm/handlers/network.handler.d.ts +0 -16
  219. package/dist/esm/handlers/network.handler.js +0 -132
  220. package/dist/esm/managers/cross-tab-session.manager.d.ts +0 -170
  221. package/dist/esm/managers/cross-tab-session.manager.js +0 -726
  222. package/dist/esm/managers/sampling.manager.d.ts +0 -8
  223. package/dist/esm/managers/sampling.manager.js +0 -49
  224. package/dist/esm/managers/session-recovery.manager.d.ts +0 -65
  225. package/dist/esm/managers/session-recovery.manager.js +0 -233
  226. package/dist/esm/types/web-vitals.types.d.ts +0 -6
  227. /package/dist/cjs/types/{web-vitals.types.js → test-bridge.types.js} +0 -0
  228. /package/dist/esm/types/{web-vitals.types.js → test-bridge.types.js} +0 -0
@@ -1,507 +1,356 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.EventManager = void 0;
4
- const constants_1 = require("../constants");
4
+ const config_constants_1 = require("../constants/config.constants");
5
5
  const types_1 = require("../types");
6
6
  const utils_1 = require("../utils");
7
- const logging_1 = require("../utils/logging");
8
7
  const sender_manager_1 = require("./sender.manager");
9
- const sampling_manager_1 = require("./sampling.manager");
10
8
  const state_manager_1 = require("./state.manager");
11
- const tags_manager_1 = require("./tags.manager");
9
+ /**
10
+ * EventManager - Core event tracking and queue management
11
+ *
12
+ * Responsibilities:
13
+ * - Track user events (clicks, scrolls, page views, custom events)
14
+ * - Queue events and batch send them to the analytics API
15
+ * - Handle deduplication of similar events
16
+ * - Manage event sending intervals and retry logic
17
+ * - Integrate with Google Analytics when configured
18
+ */
12
19
  class EventManager extends state_manager_1.StateManager {
13
- constructor(storeManager, googleAnalytics = null) {
20
+ constructor(storeManager, googleAnalytics = null, emitter = null) {
14
21
  super();
15
22
  this.eventsQueue = [];
16
- this.lastEvent = null;
17
- this.eventsQueueIntervalId = null;
18
- this.intervalActive = false;
19
- // Circuit breaker properties
20
- this.failureCount = 0;
21
- this.MAX_FAILURES = constants_1.CIRCUIT_BREAKER_CONSTANTS.MAX_FAILURES;
22
- this.circuitOpen = false;
23
- this.circuitOpenTime = 0;
24
- this.backoffDelay = constants_1.CIRCUIT_BREAKER_CONSTANTS.INITIAL_BACKOFF_DELAY_MS;
25
- this.circuitResetTimeoutId = null;
26
- // Event deduplication properties
27
- this.eventFingerprints = new Map();
28
- // Persistence storage key
29
- this.PERSISTENCE_KEY = 'tl:circuit_breaker_events';
30
- this.storageManager = storeManager;
23
+ this.lastEventFingerprint = null;
24
+ this.lastEventTime = 0;
25
+ this.sendIntervalId = null;
31
26
  this.googleAnalytics = googleAnalytics;
32
- this.samplingManager = new sampling_manager_1.SamplingManager();
33
- this.tagsManager = new tags_manager_1.TagsManager();
34
27
  this.dataSender = new sender_manager_1.SenderManager(storeManager);
35
- // Restore any persisted events on initialization
36
- this.restoreEventsFromStorage();
37
- logging_1.debugLog.debug('EventManager', 'EventManager initialized', {
38
- hasGoogleAnalytics: !!googleAnalytics,
39
- restoredEventsCount: this.eventsQueue.length,
40
- });
28
+ this.emitter = emitter;
41
29
  }
42
- track({ type, page_url, from_page_url, scroll_data, click_data, custom_event, web_vitals, session_end_reason, session_start_recovered, }) {
43
- logging_1.debugLog.info('EventManager', `📥 Event captured: ${type}`, {
44
- type,
45
- page_url,
46
- hasCustomEvent: !!custom_event,
47
- hasClickData: !!click_data,
48
- hasScrollData: !!scroll_data,
49
- hasWebVitals: !!web_vitals,
30
+ /**
31
+ * Recovers persisted events from localStorage
32
+ * Should be called after initialization to recover any events that failed to send
33
+ */
34
+ async recoverPersistedEvents() {
35
+ await this.dataSender.recoverPersistedEvents({
36
+ onSuccess: (_eventCount, recoveredEvents) => {
37
+ if (recoveredEvents && recoveredEvents.length > 0) {
38
+ const eventIds = recoveredEvents.map((e) => e.timestamp + '_' + e.type);
39
+ this.removeProcessedEvents(eventIds);
40
+ }
41
+ },
42
+ onFailure: async () => {
43
+ utils_1.debugLog.warn('EventManager', 'Failed to recover persisted events');
44
+ },
50
45
  });
51
- if (!this.samplingManager.shouldSampleEvent(type, web_vitals)) {
52
- logging_1.debugLog.debug('EventManager', 'Event filtered by sampling', { type, samplingActive: true });
46
+ }
47
+ /**
48
+ * Track user events with automatic deduplication and queueing
49
+ */
50
+ track({ type, page_url, from_page_url, scroll_data, click_data, custom_event, web_vitals, error_data, session_end_reason, session_start_recovered, }) {
51
+ if (!type) {
52
+ utils_1.debugLog.warn('EventManager', 'Event type is required');
53
53
  return;
54
54
  }
55
- const isDuplicatedEvent = this.isDuplicatedEvent({
56
- type,
57
- page_url,
55
+ const eventType = type;
56
+ const isSessionStart = eventType === types_1.EventType.SESSION_START;
57
+ const isSessionEnd = eventType === types_1.EventType.SESSION_END;
58
+ const isCriticalEvent = isSessionStart || isSessionEnd;
59
+ // Build event payload
60
+ const currentPageUrl = page_url || this.get('pageUrl');
61
+ const payload = this.buildEventPayload({
62
+ type: eventType,
63
+ page_url: currentPageUrl,
64
+ from_page_url,
58
65
  scroll_data,
59
66
  click_data,
60
67
  custom_event,
61
68
  web_vitals,
69
+ error_data,
62
70
  session_end_reason,
63
71
  session_start_recovered,
64
72
  });
65
- if (isDuplicatedEvent) {
66
- const now = Date.now();
67
- if (this.eventsQueue && this.eventsQueue.length > 0) {
68
- const lastEvent = this.eventsQueue.at(-1);
69
- if (lastEvent) {
70
- lastEvent.timestamp = now;
71
- }
72
- }
73
- if (this.lastEvent) {
74
- this.lastEvent.timestamp = now;
75
- }
76
- logging_1.debugLog.debug('EventManager', 'Duplicate event detected, timestamp updated', {
77
- type,
78
- queueLength: this.eventsQueue.length,
79
- });
73
+ // Check URL exclusions
74
+ if (this.isEventExcluded(payload)) {
80
75
  return;
81
76
  }
82
- const effectivePageUrl = page_url || this.get('pageUrl');
83
- const isRouteExcluded = (0, utils_1.isUrlPathExcluded)(effectivePageUrl, this.get('config').excludedUrlPaths);
84
- const hasStartSession = this.get('hasStartSession');
85
- const isSessionEndEvent = type == types_1.EventType.SESSION_END;
86
- if (isRouteExcluded && (!isSessionEndEvent || (isSessionEndEvent && !hasStartSession))) {
87
- if (this.get('config')?.mode === 'qa' || this.get('config')?.mode === 'debug') {
88
- logging_1.debugLog.debug('EventManager', `Event ${type} on excluded route: ${page_url}`);
89
- }
77
+ // Skip sampling for critical events, apply for the rest
78
+ if (!isCriticalEvent && !this.shouldSample()) {
90
79
  return;
91
80
  }
92
- const isSessionStartEvent = type === types_1.EventType.SESSION_START;
93
- if (isSessionStartEvent) {
81
+ if (isSessionStart) {
82
+ const currentSessionId = this.get('sessionId');
83
+ if (!currentSessionId) {
84
+ utils_1.debugLog.warn('EventManager', 'Session start event ignored: missing sessionId');
85
+ return;
86
+ }
87
+ if (this.get('hasStartSession')) {
88
+ utils_1.debugLog.warn('EventManager', 'Duplicate session_start detected', {
89
+ sessionId: currentSessionId,
90
+ });
91
+ return;
92
+ }
94
93
  this.set('hasStartSession', true);
95
94
  }
96
- const utmParams = isSessionStartEvent ? (0, utils_1.getUTMParameters)() : undefined;
97
- const payload = {
98
- type: type,
99
- page_url: isRouteExcluded ? 'excluded' : effectivePageUrl,
100
- timestamp: Date.now(),
101
- ...(isSessionStartEvent && { referrer: document.referrer || 'Direct' }),
102
- ...(from_page_url && !isRouteExcluded ? { from_page_url } : {}),
103
- ...(scroll_data && { scroll_data }),
104
- ...(click_data && { click_data }),
105
- ...(custom_event && { custom_event }),
106
- ...(utmParams && { utm: utmParams }),
107
- ...(web_vitals && { web_vitals }),
108
- ...(session_end_reason && { session_end_reason }),
109
- ...(session_start_recovered && { session_start_recovered }),
110
- };
111
- if (this.get('config')?.tags?.length) {
112
- const matchedTags = this.tagsManager.getEventTagsIds(payload, this.get('device'));
113
- if (matchedTags?.length) {
114
- payload.tags =
115
- this.get('config')?.mode === 'qa' || this.get('config')?.mode === 'debug'
116
- ? matchedTags.map((id) => ({
117
- id,
118
- key: this.get('config')?.tags?.find((t) => t.id === id)?.key ?? '',
119
- }))
120
- : matchedTags;
121
- }
95
+ // Check for duplicates
96
+ if (this.isDuplicateEvent(payload)) {
97
+ return;
122
98
  }
123
- this.lastEvent = payload;
124
- this.processAndSend(payload);
99
+ // Add to queue and schedule sending
100
+ this.addToQueue(payload);
125
101
  }
126
102
  stop() {
127
- // Clear interval and reset interval state
128
- if (this.eventsQueueIntervalId) {
129
- clearInterval(this.eventsQueueIntervalId);
130
- this.eventsQueueIntervalId = null;
131
- this.intervalActive = false;
132
- }
133
- // Clean up circuit breaker timeout
134
- if (this.circuitResetTimeoutId) {
135
- clearTimeout(this.circuitResetTimeoutId);
136
- this.circuitResetTimeoutId = null;
103
+ // Clear interval
104
+ if (this.sendIntervalId) {
105
+ clearInterval(this.sendIntervalId);
106
+ this.sendIntervalId = null;
137
107
  }
138
- // Persist any remaining events before stopping
139
- if (this.eventsQueue.length > 0) {
140
- this.persistEventsToStorage();
141
- }
142
- // Clean up all state variables
143
- this.eventFingerprints.clear();
144
- this.circuitOpen = false;
145
- this.circuitOpenTime = 0;
146
- this.failureCount = 0;
147
- this.backoffDelay = constants_1.CIRCUIT_BREAKER_CONSTANTS.INITIAL_BACKOFF_DELAY_MS;
148
- this.lastEvent = null;
149
- // Stop the data sender to clean up retry timeouts
108
+ // Reset state
109
+ this.eventsQueue = [];
110
+ this.lastEventFingerprint = null;
111
+ this.lastEventTime = 0;
112
+ // Stop sender
150
113
  this.dataSender.stop();
151
114
  }
152
- processAndSend(payload) {
153
- logging_1.debugLog.info('EventManager', `🔄 Event processed and queued: ${payload.type}`, {
154
- type: payload.type,
155
- timestamp: payload.timestamp,
156
- page_url: payload.page_url,
157
- queueLengthBefore: this.eventsQueue.length,
158
- });
159
- if (this.get('config').ipExcluded) {
160
- logging_1.debugLog.info('EventManager', `❌ Event blocked: IP excluded`);
161
- return;
162
- }
163
- this.eventsQueue.push(payload);
164
- if (this.eventsQueue.length > constants_1.MAX_EVENTS_QUEUE_LENGTH) {
165
- const removedEvent = this.eventsQueue.shift();
166
- logging_1.debugLog.warn('EventManager', 'Event queue overflow, oldest event removed', {
167
- maxLength: constants_1.MAX_EVENTS_QUEUE_LENGTH,
168
- currentLength: this.eventsQueue.length,
169
- removedEventType: removedEvent?.type,
170
- });
171
- }
172
- if (!this.eventsQueueIntervalId) {
173
- this.initEventsQueueInterval();
174
- logging_1.debugLog.info('EventManager', `⏰ Event sender initialized - queue will be sent periodically`, {
175
- queueLength: this.eventsQueue.length,
176
- });
177
- }
178
- if (this.googleAnalytics && payload.type === types_1.EventType.CUSTOM) {
179
- const customEvent = payload.custom_event;
180
- this.trackGoogleAnalyticsEvent(customEvent);
181
- }
182
- }
183
- trackGoogleAnalyticsEvent(customEvent) {
184
- if (this.get('config').mode === 'qa' || this.get('config').mode === 'debug') {
185
- logging_1.debugLog.debug('EventManager', `Google Analytics event: ${JSON.stringify(customEvent)}`);
186
- }
187
- else if (this.googleAnalytics) {
188
- this.googleAnalytics.trackEvent(customEvent.name, customEvent.metadata ?? {});
189
- }
190
- }
191
- initEventsQueueInterval() {
192
- if (this.eventsQueueIntervalId || this.intervalActive) {
193
- return;
194
- }
195
- const interval = this.get('config')?.id === 'test' ? constants_1.EVENT_SENT_INTERVAL_TEST_MS : constants_1.EVENT_SENT_INTERVAL_MS;
196
- this.eventsQueueIntervalId = window.setInterval(() => {
197
- if (this.eventsQueue.length > 0) {
198
- this.sendEventsQueue();
199
- }
200
- }, interval);
201
- this.intervalActive = true;
202
- }
115
+ /**
116
+ * Flush all queued events immediately (async)
117
+ */
203
118
  async flushImmediately() {
204
- if (this.eventsQueue.length === 0) {
205
- return true;
206
- }
207
- const body = this.buildEventsPayload();
208
- const success = await this.dataSender.sendEventsQueueAsync(body);
209
- if (success) {
210
- this.eventsQueue = [];
211
- this.clearQueueInterval();
212
- }
213
- return success;
119
+ return this.flushEvents(false);
214
120
  }
121
+ /**
122
+ * Flush all queued events immediately (sync)
123
+ */
215
124
  flushImmediatelySync() {
216
- if (this.eventsQueue.length === 0) {
217
- return true;
218
- }
219
- const body = this.buildEventsPayload();
220
- const success = this.dataSender.sendEventsQueueSync(body);
221
- if (success) {
222
- this.eventsQueue = [];
223
- this.clearQueueInterval();
224
- }
225
- return success;
125
+ return this.flushEvents(true);
226
126
  }
127
+ /**
128
+ * Queue management and sending intervals
129
+ */
227
130
  getQueueLength() {
228
131
  return this.eventsQueue.length;
229
132
  }
230
- sendEventsQueue() {
231
- if (this.eventsQueue.length === 0) {
232
- return;
233
- }
234
- logging_1.debugLog.info('EventManager', `📤 Preparing to send event queue`, {
235
- queueLength: this.eventsQueue.length,
236
- hasSessionId: !!this.get('sessionId'),
237
- circuitOpen: this.circuitOpen,
238
- });
239
- // Circuit breaker: check if it should be reset or continue blocking
240
- if (this.circuitOpen) {
241
- const timeSinceOpen = Date.now() - this.circuitOpenTime;
242
- if (timeSinceOpen >= constants_1.CIRCUIT_BREAKER_CONSTANTS.RECOVERY_TIME_MS) {
243
- this.resetCircuitBreaker();
244
- logging_1.debugLog.info('EventManager', 'Circuit breaker reset after timeout', {
245
- timeSinceOpen,
246
- recoveryTime: constants_1.CIRCUIT_BREAKER_CONSTANTS.RECOVERY_TIME_MS,
247
- });
248
- }
249
- else {
250
- logging_1.debugLog.debug('EventManager', 'Circuit breaker is open - skipping event sending', {
251
- queueLength: this.eventsQueue.length,
252
- failureCount: this.failureCount,
253
- timeSinceOpen,
254
- recoveryTime: constants_1.CIRCUIT_BREAKER_CONSTANTS.RECOVERY_TIME_MS,
255
- });
256
- return;
257
- }
133
+ clearSendInterval() {
134
+ if (this.sendIntervalId) {
135
+ clearInterval(this.sendIntervalId);
136
+ this.sendIntervalId = null;
258
137
  }
259
- if (!this.get('sessionId')) {
260
- logging_1.debugLog.info('EventManager', `⏳ Queue waiting: ${this.eventsQueue.length} events waiting for active session`);
261
- return;
138
+ }
139
+ /**
140
+ * Shared flush implementation for both sync and async modes
141
+ */
142
+ flushEvents(isSync) {
143
+ if (this.eventsQueue.length === 0) {
144
+ return isSync ? true : Promise.resolve(true);
262
145
  }
263
146
  const body = this.buildEventsPayload();
264
- const success = this.dataSender.sendEventsQueue(body);
265
- if (success) {
266
- logging_1.debugLog.info('EventManager', `✅ Event queue sent successfully`, {
267
- eventsCount: body.events.length,
268
- sessionId: body.session_id,
269
- uniqueEventsAfterDedup: body.events.length,
270
- });
271
- this.eventsQueue = [];
272
- this.failureCount = 0;
273
- this.backoffDelay = constants_1.CIRCUIT_BREAKER_CONSTANTS.INITIAL_BACKOFF_DELAY_MS;
274
- // Clear any persisted events on successful send
275
- this.clearPersistedEvents();
147
+ const eventsToSend = [...this.eventsQueue];
148
+ const eventIds = eventsToSend.map((e) => `${e.timestamp}_${e.type}`);
149
+ if (isSync) {
150
+ const success = this.dataSender.sendEventsQueueSync(body);
151
+ if (success) {
152
+ this.removeProcessedEvents(eventIds);
153
+ this.clearSendInterval();
154
+ }
155
+ return success;
276
156
  }
277
157
  else {
278
- logging_1.debugLog.info('EventManager', `❌ Failed to send event queue`, {
279
- eventsCount: body.events.length,
280
- failureCount: this.failureCount + 1,
281
- willOpenCircuit: this.failureCount + 1 >= this.MAX_FAILURES,
158
+ return this.dataSender.sendEventsQueue(body, {
159
+ onSuccess: () => {
160
+ this.removeProcessedEvents(eventIds);
161
+ this.clearSendInterval();
162
+ this.emitEventsQueue(body);
163
+ },
164
+ onFailure: () => {
165
+ utils_1.debugLog.warn('EventManager', 'Async flush failed', {
166
+ eventCount: eventsToSend.length,
167
+ });
168
+ },
282
169
  });
283
- // Persist failed events for recovery instead of restoring queue to prevent duplicates
284
- this.persistEventsToStorage();
285
- this.eventsQueue = [];
286
- this.failureCount++;
287
- if (this.failureCount >= this.MAX_FAILURES) {
288
- this.openCircuitBreaker();
289
- }
290
170
  }
291
171
  }
172
+ /**
173
+ * Send queued events to the API
174
+ */
175
+ async sendEventsQueue() {
176
+ if (!this.get('sessionId') || this.eventsQueue.length === 0) {
177
+ return;
178
+ }
179
+ const body = this.buildEventsPayload();
180
+ const eventsToSend = [...this.eventsQueue];
181
+ const eventIds = eventsToSend.map((e) => `${e.timestamp}_${e.type}`);
182
+ await this.dataSender.sendEventsQueue(body, {
183
+ onSuccess: () => {
184
+ this.removeProcessedEvents(eventIds);
185
+ this.emitEventsQueue(body);
186
+ },
187
+ onFailure: async () => {
188
+ utils_1.debugLog.warn('EventManager', 'Events send failed, keeping in queue', {
189
+ eventCount: eventsToSend.length,
190
+ });
191
+ },
192
+ });
193
+ }
194
+ /**
195
+ * Build the payload for sending events to the API
196
+ * Includes basic deduplication and sorting
197
+ */
292
198
  buildEventsPayload() {
293
- const uniqueEvents = new Map();
199
+ const eventMap = new Map();
200
+ const order = [];
294
201
  for (const event of this.eventsQueue) {
295
- let key = `${event.type}_${event.page_url}`;
296
- if (event.click_data) {
297
- key += `_${event.click_data.x}_${event.click_data.y}`;
298
- }
299
- if (event.scroll_data) {
300
- key += `_${event.scroll_data.depth}_${event.scroll_data.direction}`;
301
- }
302
- if (event.custom_event) {
303
- key += `_${event.custom_event.name}`;
304
- }
305
- if (event.web_vitals) {
306
- key += `_${event.web_vitals.type}`;
307
- }
308
- if (!uniqueEvents.has(key)) {
309
- uniqueEvents.set(key, event);
202
+ const signature = this.createEventSignature(event);
203
+ if (!eventMap.has(signature)) {
204
+ order.push(signature);
310
205
  }
206
+ eventMap.set(signature, event);
311
207
  }
312
- const deduplicatedEvents = [...uniqueEvents.values()];
313
- deduplicatedEvents.sort((a, b) => a.timestamp - b.timestamp);
208
+ const events = order
209
+ .map((signature) => eventMap.get(signature))
210
+ .filter((event) => Boolean(event))
211
+ .sort((a, b) => a.timestamp - b.timestamp);
314
212
  return {
315
213
  user_id: this.get('userId'),
316
214
  session_id: this.get('sessionId'),
317
215
  device: this.get('device'),
318
- events: deduplicatedEvents,
216
+ events,
319
217
  ...(this.get('config')?.globalMetadata && { global_metadata: this.get('config')?.globalMetadata }),
320
218
  };
321
219
  }
322
- clearQueueInterval() {
323
- if (this.eventsQueueIntervalId) {
324
- clearInterval(this.eventsQueueIntervalId);
325
- this.eventsQueueIntervalId = null;
326
- this.intervalActive = false;
220
+ /**
221
+ * Helper methods for event processing
222
+ */
223
+ buildEventPayload(data) {
224
+ const isSessionStart = data.type === types_1.EventType.SESSION_START;
225
+ const currentPageUrl = data.page_url ?? this.get('pageUrl');
226
+ const payload = {
227
+ type: data.type,
228
+ page_url: currentPageUrl,
229
+ timestamp: Date.now(),
230
+ ...(isSessionStart && { referrer: document.referrer || 'Direct' }),
231
+ ...(data.from_page_url && { from_page_url: data.from_page_url }),
232
+ ...(data.scroll_data && { scroll_data: data.scroll_data }),
233
+ ...(data.click_data && { click_data: data.click_data }),
234
+ ...(data.custom_event && { custom_event: data.custom_event }),
235
+ ...(data.web_vitals && { web_vitals: data.web_vitals }),
236
+ ...(data.error_data && { error_data: data.error_data }),
237
+ ...(data.session_end_reason && { session_end_reason: data.session_end_reason }),
238
+ ...(data.session_start_recovered && { session_start_recovered: data.session_start_recovered }),
239
+ ...(isSessionStart && (0, utils_1.getUTMParameters)() && { utm: (0, utils_1.getUTMParameters)() }),
240
+ };
241
+ // Add project tags
242
+ const projectTags = this.get('config')?.tags;
243
+ if (projectTags?.length) {
244
+ payload.tags = projectTags;
327
245
  }
246
+ return payload;
328
247
  }
329
- getEventFingerprint(event) {
330
- const key = `${event.type}_${event.page_url}`;
331
- if (event.click_data) {
332
- // Round coordinates to reduce false positives
333
- const x = Math.round((event.click_data.x || 0) / constants_1.CLICK_COORDINATE_PRECISION) * constants_1.CLICK_COORDINATE_PRECISION;
334
- const y = Math.round((event.click_data.y || 0) / constants_1.CLICK_COORDINATE_PRECISION) * constants_1.CLICK_COORDINATE_PRECISION;
335
- return `${key}_${x}_${y}_${event.click_data.tag}_${event.click_data.id}`;
336
- }
337
- if (event.scroll_data) {
338
- return `${key}_${event.scroll_data.depth}_${event.scroll_data.direction}`;
339
- }
340
- if (event.custom_event) {
341
- return `${key}_${event.custom_event.name}`;
342
- }
343
- if (event.web_vitals) {
344
- return `${key}_${event.web_vitals.type}`;
345
- }
346
- if (event.session_end_reason) {
347
- return `${key}_${event.session_end_reason}`;
348
- }
349
- if (event.session_start_recovered !== undefined) {
350
- return `${key}_${event.session_start_recovered}`;
248
+ isEventExcluded(event) {
249
+ const config = this.get('config');
250
+ const isRouteExcluded = (0, utils_1.isUrlPathExcluded)(event.page_url, config?.excludedUrlPaths ?? []);
251
+ const hasStartSession = this.get('hasStartSession');
252
+ const isSessionEndEvent = event.type === types_1.EventType.SESSION_END;
253
+ const isSessionStartEvent = event.type === types_1.EventType.SESSION_START;
254
+ if (isRouteExcluded && !isSessionStartEvent && !(isSessionEndEvent && hasStartSession)) {
255
+ return true;
351
256
  }
352
- return key;
257
+ return config?.ipExcluded === true;
353
258
  }
354
- isDuplicatedEvent(event) {
355
- const fingerprint = this.getEventFingerprint(event);
356
- const lastTime = this.eventFingerprints.get(fingerprint) ?? 0;
259
+ isDuplicateEvent(event) {
357
260
  const now = Date.now();
358
- if (now - lastTime < constants_1.DUPLICATE_EVENT_THRESHOLD_MS) {
261
+ const fingerprint = this.createEventFingerprint(event);
262
+ // Check if this is a duplicate within the threshold
263
+ if (this.lastEventFingerprint === fingerprint && now - this.lastEventTime < config_constants_1.DUPLICATE_EVENT_THRESHOLD_MS) {
359
264
  return true;
360
265
  }
361
- this.eventFingerprints.set(fingerprint, now);
362
- // Clean up old fingerprints to prevent memory leaks
363
- this.cleanupOldFingerprints();
266
+ // Update tracking
267
+ this.lastEventFingerprint = fingerprint;
268
+ this.lastEventTime = now;
364
269
  return false;
365
270
  }
366
- /**
367
- * Cleans up old fingerprints to prevent memory leaks
368
- */
369
- cleanupOldFingerprints() {
370
- if (this.eventFingerprints.size <= constants_1.MAX_FINGERPRINTS) {
371
- return;
271
+ createEventFingerprint(event) {
272
+ let fingerprint = `${event.type}_${event.page_url}`;
273
+ if (event.click_data) {
274
+ // Round coordinates to reduce false duplicates
275
+ const x = Math.round((event.click_data.x || 0) / 10) * 10;
276
+ const y = Math.round((event.click_data.y || 0) / 10) * 10;
277
+ fingerprint += `_click_${x}_${y}`;
372
278
  }
373
- const now = Date.now();
374
- const cleanupThreshold = constants_1.DUPLICATE_EVENT_THRESHOLD_MS * constants_1.FINGERPRINT_CLEANUP_MULTIPLIER;
375
- const keysToDelete = [];
376
- for (const [key, timestamp] of this.eventFingerprints) {
377
- if (now - timestamp > cleanupThreshold) {
378
- keysToDelete.push(key);
379
- }
279
+ if (event.scroll_data) {
280
+ fingerprint += `_scroll_${event.scroll_data.depth}_${event.scroll_data.direction}`;
380
281
  }
381
- // Delete old entries
382
- for (const key of keysToDelete) {
383
- this.eventFingerprints.delete(key);
282
+ if (event.custom_event) {
283
+ fingerprint += `_custom_${event.custom_event.name}`;
384
284
  }
385
- logging_1.debugLog.debug('EventManager', 'Cleaned up old event fingerprints', {
386
- totalFingerprints: this.eventFingerprints.size + keysToDelete.length,
387
- cleanedCount: keysToDelete.length,
388
- remainingCount: this.eventFingerprints.size,
389
- cleanupThreshold,
390
- });
391
- }
392
- /**
393
- * Opens the circuit breaker with time-based recovery and event persistence
394
- */
395
- openCircuitBreaker() {
396
- this.circuitOpen = true;
397
- this.circuitOpenTime = Date.now();
398
- // Persist events before clearing queue to prevent data loss
399
- this.persistEventsToStorage();
400
- const eventsCount = this.eventsQueue.length;
401
- this.eventsQueue = []; // Clear memory queue
402
- logging_1.debugLog.warn('EventManager', 'Circuit breaker opened with time-based recovery', {
403
- maxFailures: this.MAX_FAILURES,
404
- persistedEvents: eventsCount,
405
- failureCount: this.failureCount,
406
- recoveryTime: constants_1.CIRCUIT_BREAKER_CONSTANTS.RECOVERY_TIME_MS,
407
- openTime: this.circuitOpenTime,
408
- });
409
- // Increase backoff for next failure
410
- this.backoffDelay = Math.min(this.backoffDelay * constants_1.CIRCUIT_BREAKER_CONSTANTS.BACKOFF_MULTIPLIER, constants_1.CIRCUIT_BREAKER_CONSTANTS.MAX_BACKOFF_DELAY_MS);
285
+ if (event.web_vitals) {
286
+ fingerprint += `_vitals_${event.web_vitals.type}`;
287
+ }
288
+ return fingerprint;
411
289
  }
412
- /**
413
- * Resets the circuit breaker and attempts to restore persisted events
414
- */
415
- resetCircuitBreaker() {
416
- this.circuitOpen = false;
417
- this.circuitOpenTime = 0;
418
- this.failureCount = 0;
419
- this.circuitResetTimeoutId = null;
420
- logging_1.debugLog.info('EventManager', 'Circuit breaker reset - attempting to restore events', {
421
- currentQueueLength: this.eventsQueue.length,
422
- });
423
- // Restore persisted events
424
- this.restoreEventsFromStorage();
425
- logging_1.debugLog.info('EventManager', 'Circuit breaker reset completed', {
426
- restoredQueueLength: this.eventsQueue.length,
427
- backoffDelay: this.backoffDelay,
428
- });
290
+ createEventSignature(event) {
291
+ return this.createEventFingerprint(event);
429
292
  }
430
- /**
431
- * Persists current events queue to localStorage for recovery
432
- */
433
- persistEventsToStorage() {
434
- try {
435
- if (this.eventsQueue.length === 0) {
436
- return;
437
- }
438
- const persistData = {
439
- events: this.eventsQueue,
440
- timestamp: Date.now(),
441
- failureCount: this.failureCount,
442
- };
443
- this.storageManager.setItem(this.PERSISTENCE_KEY, JSON.stringify(persistData));
444
- logging_1.debugLog.debug('EventManager', 'Events persisted to storage for recovery', {
445
- eventsCount: this.eventsQueue.length,
446
- failureCount: this.failureCount,
293
+ addToQueue(event) {
294
+ this.eventsQueue.push(event);
295
+ utils_1.debugLog.info('EventManager', 'Event added to queue', event);
296
+ this.emitEvent(event);
297
+ // Prevent queue overflow
298
+ if (this.eventsQueue.length > config_constants_1.MAX_EVENTS_QUEUE_LENGTH) {
299
+ const removedEvent = this.eventsQueue.shift();
300
+ utils_1.debugLog.warn('EventManager', 'Event queue overflow, oldest event removed', {
301
+ maxLength: config_constants_1.MAX_EVENTS_QUEUE_LENGTH,
302
+ currentLength: this.eventsQueue.length,
303
+ removedEventType: removedEvent?.type,
447
304
  });
448
305
  }
449
- catch (error) {
450
- logging_1.debugLog.warn('EventManager', 'Failed to persist events to storage', {
451
- error: error instanceof Error ? error.message : 'Unknown error',
452
- eventsCount: this.eventsQueue.length,
453
- });
306
+ if (!this.sendIntervalId) {
307
+ this.startSendInterval();
454
308
  }
309
+ // Google Analytics integration
310
+ this.handleGoogleAnalyticsIntegration(event);
455
311
  }
456
- /**
457
- * Restores events from localStorage if available and not expired
458
- */
459
- restoreEventsFromStorage() {
460
- try {
461
- const persistedData = this.storageManager.getItem(this.PERSISTENCE_KEY);
462
- if (!persistedData) {
463
- return;
312
+ startSendInterval() {
313
+ this.sendIntervalId = window.setInterval(() => {
314
+ if (this.eventsQueue.length > 0) {
315
+ this.sendEventsQueue();
464
316
  }
465
- const parsed = JSON.parse(persistedData);
466
- const now = Date.now();
467
- const maxAge = constants_1.EVENT_PERSISTENCE_MAX_AGE_MS;
468
- // Check if persisted data is not too old
469
- if (now - parsed.timestamp > maxAge) {
470
- this.clearPersistedEvents();
471
- logging_1.debugLog.debug('EventManager', 'Cleared expired persisted events', {
472
- age: now - parsed.timestamp,
473
- maxAge,
474
- });
475
- return;
317
+ }, config_constants_1.EVENT_SENT_INTERVAL_MS);
318
+ }
319
+ handleGoogleAnalyticsIntegration(event) {
320
+ if (this.googleAnalytics && event.type === types_1.EventType.CUSTOM && event.custom_event) {
321
+ if (this.get('config')?.mode === 'qa' || this.get('config')?.mode === 'debug') {
322
+ // Skip GA tracking in QA/debug modes
476
323
  }
477
- // Restore events if we don't already have events in queue
478
- if (Array.isArray(parsed.events) && parsed.events.length > 0 && this.eventsQueue.length === 0) {
479
- this.eventsQueue = parsed.events;
480
- logging_1.debugLog.info('EventManager', 'Restored events from storage', {
481
- restoredCount: parsed.events.length,
482
- originalFailureCount: parsed.failureCount ?? 0,
483
- });
324
+ else {
325
+ this.googleAnalytics.trackEvent(event.custom_event.name, event.custom_event.metadata ?? {});
484
326
  }
485
327
  }
486
- catch (error) {
487
- logging_1.debugLog.warn('EventManager', 'Failed to restore events from storage', {
488
- error: error instanceof Error ? error.message : 'Unknown error',
489
- });
490
- this.clearPersistedEvents();
491
- }
328
+ }
329
+ shouldSample() {
330
+ const samplingRate = this.get('config')?.samplingRate ?? 1;
331
+ return Math.random() < samplingRate;
332
+ }
333
+ removeProcessedEvents(eventIds) {
334
+ const eventIdSet = new Set(eventIds);
335
+ this.eventsQueue = this.eventsQueue.filter((event) => {
336
+ const eventId = `${event.timestamp}_${event.type}`;
337
+ return !eventIdSet.has(eventId);
338
+ });
492
339
  }
493
340
  /**
494
- * Clears persisted events from localStorage
341
+ * Emit event for external listeners
495
342
  */
496
- clearPersistedEvents() {
497
- try {
498
- this.storageManager.removeItem(this.PERSISTENCE_KEY);
499
- logging_1.debugLog.debug('EventManager', 'Cleared persisted events from storage');
343
+ emitEvent(eventData) {
344
+ if (this.emitter) {
345
+ this.emitter.emit(types_1.EmitterEvent.EVENT, eventData);
500
346
  }
501
- catch (error) {
502
- logging_1.debugLog.warn('EventManager', 'Failed to clear persisted events', {
503
- error: error instanceof Error ? error.message : 'Unknown error',
504
- });
347
+ }
348
+ /**
349
+ * Emit events queue for external listeners
350
+ */
351
+ emitEventsQueue(queue) {
352
+ if (this.emitter) {
353
+ this.emitter.emit(types_1.EmitterEvent.QUEUE, queue);
505
354
  }
506
355
  }
507
356
  }