@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,46 +1,64 @@
1
1
  import { BaseEventsQueueDto } from '../types';
2
2
  import { StorageManager } from './storage.manager';
3
3
  import { StateManager } from './state.manager';
4
+ interface SendCallbacks {
5
+ onSuccess?: (eventCount?: number, events?: any[]) => void;
6
+ onFailure?: () => void;
7
+ }
4
8
  export declare class SenderManager extends StateManager {
5
9
  private readonly storeManager;
6
- private readonly queueStorageKey;
7
- private retryDelay;
8
10
  private retryTimeoutId;
9
- private lastAsyncSend;
10
- private lastSyncSend;
11
+ private retryCount;
12
+ private isRetrying;
11
13
  constructor(storeManager: StorageManager);
12
- sendEventsQueueAsync(body: BaseEventsQueueDto): Promise<boolean>;
14
+ private getQueueStorageKey;
15
+ /**
16
+ * Send events synchronously using sendBeacon or XHR fallback
17
+ * Used primarily for page unload scenarios
18
+ */
13
19
  sendEventsQueueSync(body: BaseEventsQueueDto): boolean;
14
- sendEventsQueue(body: BaseEventsQueueDto): boolean;
15
- recoverPersistedEvents(): void;
16
- stop(): void;
17
20
  /**
18
- * Sends recovered events without re-deduplication since they were already processed
21
+ * Send events asynchronously with persistence and retry logic
22
+ * Main method for sending events during normal operation
23
+ */
24
+ sendEventsQueue(body: BaseEventsQueueDto, callbacks?: SendCallbacks): Promise<boolean>;
25
+ /**
26
+ * Recover and send previously persisted events
27
+ * Called during initialization to handle events from previous session
19
28
  */
20
- private sendRecoveredEvents;
29
+ recoverPersistedEvents(callbacks?: SendCallbacks): Promise<void>;
21
30
  /**
22
- * Schedules retry for recovered events using the specific recovery method
31
+ * Persist events for recovery in case of failure
23
32
  */
24
- private scheduleRetryForRecoveredEvents;
25
- private canSendAsync;
26
- private canSendSync;
27
- private sendQueueAsync;
28
- private sendQueueSync;
29
- private sendQueue;
33
+ persistEventsForRecovery(body: BaseEventsQueueDto): boolean;
34
+ /**
35
+ * Legacy method for backward compatibility
36
+ * @deprecated Use sendEventsQueue instead
37
+ */
38
+ sendEventsQueueAsync(body: BaseEventsQueueDto): Promise<boolean>;
39
+ /**
40
+ * Stop the sender manager and clean up resources
41
+ */
42
+ stop(): void;
43
+ private send;
44
+ private sendWithTimeout;
45
+ private sendQueueSyncInternal;
30
46
  private sendSyncXHR;
31
47
  private prepareRequest;
32
48
  private getPersistedData;
33
49
  private isDataRecent;
34
50
  private createRecoveryBody;
35
- private logQueue;
36
- private handleSendFailure;
37
- private persistFailedEvents;
51
+ private persistEvents;
38
52
  private clearPersistedEvents;
39
53
  private resetRetryState;
40
54
  private scheduleRetry;
41
- private executeSend;
42
- private executeSendSync;
43
55
  private shouldSkipSend;
56
+ /**
57
+ * Simulate a successful send operation for skip mode
58
+ * Provides realistic timing and behavior without making HTTP requests
59
+ */
60
+ private simulateSuccessfulSend;
44
61
  private isSendBeaconAvailable;
45
62
  private clearRetryTimeout;
46
63
  }
64
+ export {};
@@ -3,112 +3,155 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SenderManager = void 0;
4
4
  const constants_1 = require("../constants");
5
5
  const types_1 = require("../types");
6
- const logging_1 = require("../utils/logging");
6
+ const utils_1 = require("../utils");
7
7
  const state_manager_1 = require("./state.manager");
8
8
  class SenderManager extends state_manager_1.StateManager {
9
9
  constructor(storeManager) {
10
10
  super();
11
- this.retryDelay = constants_1.RETRY_BACKOFF_INITIAL;
12
11
  this.retryTimeoutId = null;
13
- this.lastAsyncSend = 0;
14
- this.lastSyncSend = 0;
12
+ this.retryCount = 0;
13
+ this.isRetrying = false;
15
14
  this.storeManager = storeManager;
16
- this.queueStorageKey = `${(0, constants_1.QUEUE_KEY)(this.get('config')?.id)}:${this.get('userId')}`;
17
- this.recoverPersistedEvents();
18
15
  }
19
- async sendEventsQueueAsync(body) {
20
- return this.executeSend(body, () => this.sendQueueAsync(body));
16
+ getQueueStorageKey() {
17
+ const projectId = this.get('config')?.id || 'default';
18
+ const userId = this.get('userId') || 'anonymous';
19
+ return `${(0, constants_1.QUEUE_KEY)(projectId)}:${userId}`;
21
20
  }
21
+ /**
22
+ * Send events synchronously using sendBeacon or XHR fallback
23
+ * Used primarily for page unload scenarios
24
+ */
22
25
  sendEventsQueueSync(body) {
23
- return this.executeSendSync(body, () => this.sendQueueSync(body));
26
+ // For skip mode, simulate success immediately (sync version)
27
+ if (this.shouldSkipSend()) {
28
+ this.clearPersistedEvents();
29
+ this.resetRetryState();
30
+ return true;
31
+ }
32
+ const success = this.sendQueueSyncInternal(body);
33
+ if (success) {
34
+ this.clearPersistedEvents();
35
+ this.resetRetryState();
36
+ }
37
+ return success;
24
38
  }
25
- sendEventsQueue(body) {
26
- return this.executeSendSync(body, () => this.sendQueue(body));
39
+ /**
40
+ * Send events asynchronously with persistence and retry logic
41
+ * Main method for sending events during normal operation
42
+ */
43
+ async sendEventsQueue(body, callbacks) {
44
+ // First, try to persist events for recovery (even in skip mode for consistency)
45
+ const persisted = this.persistEvents(body);
46
+ if (!persisted && !this.shouldSkipSend()) {
47
+ utils_1.debugLog.warn('SenderManager', 'Failed to persist events, attempting immediate send');
48
+ }
49
+ // Attempt to send events (or simulate in skip mode)
50
+ const success = await this.send(body);
51
+ if (success) {
52
+ this.clearPersistedEvents();
53
+ this.resetRetryState();
54
+ callbacks?.onSuccess?.(body.events.length);
55
+ }
56
+ else {
57
+ this.scheduleRetry(body, callbacks);
58
+ callbacks?.onFailure?.();
59
+ }
60
+ return success;
27
61
  }
28
- recoverPersistedEvents() {
62
+ /**
63
+ * Recover and send previously persisted events
64
+ * Called during initialization to handle events from previous session
65
+ */
66
+ async recoverPersistedEvents(callbacks) {
29
67
  try {
30
68
  const persistedData = this.getPersistedData();
31
69
  if (!persistedData || !this.isDataRecent(persistedData) || persistedData.events.length === 0) {
32
70
  this.clearPersistedEvents();
33
71
  return;
34
72
  }
35
- const recoveryBody = this.createRecoveryBody(persistedData);
36
- const success = this.sendRecoveredEvents(recoveryBody);
73
+ const body = this.createRecoveryBody(persistedData);
74
+ const success = await this.send(body);
37
75
  if (success) {
38
- logging_1.debugLog.info('SenderManager', 'Persisted events recovered successfully', {
39
- eventsCount: persistedData.events.length,
40
- sessionId: persistedData.sessionId,
41
- });
42
76
  this.clearPersistedEvents();
77
+ this.resetRetryState();
78
+ callbacks?.onSuccess?.(persistedData.events.length);
43
79
  }
44
80
  else {
45
- logging_1.debugLog.warn('SenderManager', 'Failed to recover persisted events, scheduling retry', {
46
- eventsCount: persistedData.events.length,
47
- });
48
- this.scheduleRetryForRecoveredEvents(recoveryBody);
81
+ this.scheduleRetry(body, callbacks);
82
+ callbacks?.onFailure?.();
49
83
  }
50
84
  }
51
85
  catch (error) {
52
- logging_1.debugLog.error('SenderManager', 'Failed to recover persisted events', { error });
86
+ utils_1.debugLog.error('SenderManager', 'Failed to recover persisted events', { error });
87
+ this.clearPersistedEvents(); // Clean up corrupted data
53
88
  }
54
89
  }
55
- stop() {
56
- this.clearRetryTimeout();
57
- this.resetRetryState();
58
- }
59
90
  /**
60
- * Sends recovered events without re-deduplication since they were already processed
91
+ * Persist events for recovery in case of failure
61
92
  */
62
- sendRecoveredEvents(body) {
63
- return this.executeSendSync(body, () => this.sendQueue(body));
93
+ persistEventsForRecovery(body) {
94
+ return this.persistEvents(body);
64
95
  }
65
96
  /**
66
- * Schedules retry for recovered events using the specific recovery method
97
+ * Legacy method for backward compatibility
98
+ * @deprecated Use sendEventsQueue instead
67
99
  */
68
- scheduleRetryForRecoveredEvents(body) {
69
- if (this.retryTimeoutId !== null) {
70
- return;
71
- }
72
- this.retryTimeoutId = window.setTimeout(() => {
73
- this.retryTimeoutId = null;
74
- this.sendRecoveredEvents(body);
75
- }, this.retryDelay);
76
- this.retryDelay = Math.min(this.retryDelay * 2, constants_1.RETRY_BACKOFF_MAX);
77
- }
78
- canSendAsync() {
79
- return Date.now() - this.lastAsyncSend >= constants_1.RATE_LIMIT_INTERVAL;
100
+ async sendEventsQueueAsync(body) {
101
+ return this.sendEventsQueue(body);
80
102
  }
81
- canSendSync() {
82
- return Date.now() - this.lastSyncSend >= constants_1.RATE_LIMIT_INTERVAL;
103
+ /**
104
+ * Stop the sender manager and clean up resources
105
+ */
106
+ stop() {
107
+ this.clearRetryTimeout();
108
+ this.resetRetryState();
109
+ // Clear any persisted events on shutdown to prevent stale data
110
+ this.clearPersistedEvents();
83
111
  }
84
- async sendQueueAsync(body) {
112
+ async send(body) {
113
+ if (this.shouldSkipSend()) {
114
+ return this.simulateSuccessfulSend();
115
+ }
85
116
  const { url, payload } = this.prepareRequest(body);
86
117
  try {
87
- const response = await fetch(url, {
88
- method: 'POST',
89
- mode: 'cors',
90
- credentials: 'include',
91
- body: payload,
92
- headers: {
93
- 'Content-Type': 'application/json',
94
- Origin: window.location.origin,
95
- Referer: window.location.href,
96
- },
97
- });
118
+ const response = await this.sendWithTimeout(url, payload);
98
119
  return response.ok;
99
120
  }
100
121
  catch (error) {
101
122
  const errorMessage = error instanceof Error ? error.message : String(error);
102
- const isCorsError = errorMessage.includes('CORS') || errorMessage.includes('NotSameOrigin') || errorMessage.includes('blocked');
103
- logging_1.debugLog.error('SenderManager', 'Failed to send events async', {
123
+ utils_1.debugLog.error('SenderManager', 'Send request failed', {
104
124
  error: errorMessage,
105
- isCorsError,
106
- url: url.replace(/\/\/[^/]+/, '//[DOMAIN]'),
125
+ events: body.events.length,
126
+ url: url.replace(/\/\/[^/]+/, '//[DOMAIN]'), // Hide domain for privacy
107
127
  });
108
128
  return false;
109
129
  }
110
130
  }
111
- sendQueueSync(body) {
131
+ async sendWithTimeout(url, payload) {
132
+ const controller = new AbortController();
133
+ const timeoutId = setTimeout(() => controller.abort(), constants_1.REQUEST_TIMEOUT_MS);
134
+ try {
135
+ const response = await fetch(url, {
136
+ method: 'POST',
137
+ headers: {
138
+ 'Content-Type': 'application/json',
139
+ },
140
+ body: payload,
141
+ keepalive: true,
142
+ credentials: 'include',
143
+ signal: controller.signal,
144
+ });
145
+ if (!response.ok) {
146
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
147
+ }
148
+ return response;
149
+ }
150
+ finally {
151
+ clearTimeout(timeoutId);
152
+ }
153
+ }
154
+ sendQueueSyncInternal(body) {
112
155
  const { url, payload } = this.prepareRequest(body);
113
156
  const blob = new Blob([payload], { type: 'application/json' });
114
157
  if (this.isSendBeaconAvailable() && navigator.sendBeacon(url, blob)) {
@@ -116,54 +159,61 @@ class SenderManager extends state_manager_1.StateManager {
116
159
  }
117
160
  return this.sendSyncXHR(url, payload);
118
161
  }
119
- sendQueue(body) {
120
- if (!this.isSendBeaconAvailable()) {
121
- return false;
122
- }
123
- const { url, payload } = this.prepareRequest(body);
124
- const blob = new Blob([payload], { type: 'application/json' });
125
- return navigator.sendBeacon(url, blob);
126
- }
127
162
  sendSyncXHR(url, payload) {
128
163
  const xhr = new XMLHttpRequest();
129
164
  try {
130
165
  xhr.open('POST', url, false);
131
166
  xhr.setRequestHeader('Content-Type', 'application/json');
132
- xhr.setRequestHeader('Origin', window.location.origin);
133
- xhr.setRequestHeader('Referer', window.location.href);
134
167
  xhr.withCredentials = true;
135
168
  xhr.timeout = constants_1.SYNC_XHR_TIMEOUT_MS;
136
169
  xhr.send(payload);
137
- return xhr.status >= 200 && xhr.status < 300;
170
+ const success = xhr.status >= 200 && xhr.status < 300;
171
+ if (!success) {
172
+ utils_1.debugLog.warn('SenderManager', 'Sync XHR failed', {
173
+ status: xhr.status,
174
+ statusText: xhr.statusText || 'Unknown error',
175
+ });
176
+ }
177
+ return success;
138
178
  }
139
179
  catch (error) {
140
180
  const errorMessage = error instanceof Error ? error.message : String(error);
141
- const isCorsError = errorMessage.includes('CORS') || errorMessage.includes('NotSameOrigin') || errorMessage.includes('blocked');
142
- logging_1.debugLog.error('SenderManager', 'Sync XHR failed', {
181
+ utils_1.debugLog.warn('SenderManager', 'Sync XHR error', {
143
182
  error: errorMessage,
144
- isCorsError,
145
- status: xhr.status ?? 'unknown',
146
- url: url.replace(/\/\/[^/]+/, '//[DOMAIN]'),
183
+ status: xhr.status || 'unknown',
147
184
  });
148
185
  return false;
149
186
  }
150
187
  }
151
188
  prepareRequest(body) {
152
- const useLocalServer = this.get('config').id === types_1.SpecialProjectId.HttpLocal;
153
- const baseUrl = useLocalServer ? window.location.origin : this.get('apiUrl');
154
- const url = `${baseUrl}/collect`;
189
+ const url = `${this.get('apiUrl')}/collect`;
155
190
  return {
156
191
  url,
157
192
  payload: JSON.stringify(body),
158
193
  };
159
194
  }
160
195
  getPersistedData() {
161
- const persistedDataString = this.storeManager.getItem(this.queueStorageKey);
162
- return persistedDataString ? JSON.parse(persistedDataString) : null;
196
+ try {
197
+ const storageKey = this.getQueueStorageKey();
198
+ const persistedDataString = this.storeManager.getItem(storageKey);
199
+ if (persistedDataString) {
200
+ return JSON.parse(persistedDataString);
201
+ }
202
+ }
203
+ catch (error) {
204
+ utils_1.debugLog.warn('SenderManager', 'Failed to parse persisted data', { error });
205
+ // Clean up corrupted data
206
+ this.clearPersistedEvents();
207
+ }
208
+ return null;
163
209
  }
164
210
  isDataRecent(data) {
211
+ if (!data.timestamp || typeof data.timestamp !== 'number') {
212
+ return false;
213
+ }
165
214
  const ageInHours = (Date.now() - data.timestamp) / (1000 * 60 * 60);
166
- return ageInHours < constants_1.EVENT_EXPIRY_HOURS;
215
+ const isRecent = ageInHours < constants_1.EVENT_EXPIRY_HOURS;
216
+ return isRecent;
167
217
  }
168
218
  createRecoveryBody(data) {
169
219
  return {
@@ -174,14 +224,7 @@ class SenderManager extends state_manager_1.StateManager {
174
224
  ...(data.global_metadata && { global_metadata: data.global_metadata }),
175
225
  };
176
226
  }
177
- logQueue(queue) {
178
- logging_1.debugLog.info('SenderManager', ` ⏩ Queue snapshot`, queue);
179
- }
180
- handleSendFailure(body) {
181
- this.persistFailedEvents(body);
182
- this.scheduleRetry(body);
183
- }
184
- persistFailedEvents(body) {
227
+ persistEvents(body) {
185
228
  try {
186
229
  const persistedData = {
187
230
  userId: body.user_id,
@@ -191,131 +234,90 @@ class SenderManager extends state_manager_1.StateManager {
191
234
  timestamp: Date.now(),
192
235
  ...(body.global_metadata && { global_metadata: body.global_metadata }),
193
236
  };
194
- this.storeManager.setItem(this.queueStorageKey, JSON.stringify(persistedData));
237
+ const storageKey = this.getQueueStorageKey();
238
+ this.storeManager.setItem(storageKey, JSON.stringify(persistedData));
239
+ return !!this.storeManager.getItem(storageKey);
195
240
  }
196
241
  catch (error) {
197
- logging_1.debugLog.error('SenderManager', 'Failed to persist events', { error });
242
+ utils_1.debugLog.warn('SenderManager', 'Failed to persist events', { error });
243
+ return false;
198
244
  }
199
245
  }
200
246
  clearPersistedEvents() {
201
- this.storeManager.removeItem(this.queueStorageKey);
247
+ try {
248
+ this.storeManager.removeItem(this.getQueueStorageKey());
249
+ }
250
+ catch (error) {
251
+ utils_1.debugLog.warn('SenderManager', 'Failed to clear persisted events', { error });
252
+ }
202
253
  }
203
254
  resetRetryState() {
204
- this.retryDelay = constants_1.RETRY_BACKOFF_INITIAL;
255
+ this.retryCount = 0;
256
+ this.isRetrying = false;
205
257
  this.clearRetryTimeout();
206
258
  }
207
- scheduleRetry(body) {
208
- if (this.retryTimeoutId !== null) {
259
+ scheduleRetry(body, originalCallbacks) {
260
+ if (this.retryTimeoutId !== null || this.isRetrying) {
209
261
  return;
210
262
  }
211
- this.retryTimeoutId = window.setTimeout(() => {
212
- this.retryTimeoutId = null;
213
- this.sendEventsQueue(body);
214
- }, this.retryDelay);
215
- this.retryDelay = Math.min(this.retryDelay * 2, constants_1.RETRY_BACKOFF_MAX);
216
- }
217
- async executeSend(body, sendFn) {
218
- if (this.shouldSkipSend()) {
219
- this.logQueue(body);
220
- return true;
221
- }
222
- if (!this.canSendAsync()) {
223
- logging_1.debugLog.info('SenderManager', `⏱️ Rate limited - skipping async send`, {
224
- eventsCount: body.events.length,
225
- timeSinceLastSend: Date.now() - this.lastAsyncSend,
226
- });
227
- return false;
263
+ if (this.retryCount >= constants_1.MAX_RETRIES) {
264
+ utils_1.debugLog.warn('SenderManager', 'Max retries reached, giving up', { retryCount: this.retryCount });
265
+ this.clearPersistedEvents();
266
+ this.resetRetryState();
267
+ originalCallbacks?.onFailure?.();
268
+ return;
228
269
  }
229
- logging_1.debugLog.info('SenderManager', `🌐 Sending events to server (async)`, {
230
- eventsCount: body.events.length,
231
- sessionId: body.session_id,
232
- userId: body.user_id,
233
- });
234
- this.lastAsyncSend = Date.now();
235
- try {
236
- const success = await sendFn();
237
- if (success) {
238
- logging_1.debugLog.info('SenderManager', `✅ Successfully sent events to server`, {
239
- eventsCount: body.events.length,
240
- method: 'async',
241
- });
242
- this.resetRetryState();
243
- this.clearPersistedEvents();
244
- }
245
- else {
246
- logging_1.debugLog.warn('SenderManager', 'Failed to send events', {
247
- eventsCount: body.events.length,
248
- method: 'async',
249
- });
250
- this.handleSendFailure(body);
270
+ const retryDelay = constants_1.RETRY_DELAY_MS * Math.pow(2, this.retryCount); // Exponential backoff
271
+ this.retryTimeoutId = window.setTimeout(async () => {
272
+ this.retryTimeoutId = null;
273
+ if (this.isRetrying) {
274
+ return;
251
275
  }
252
- return success;
253
- }
254
- catch {
255
- this.handleSendFailure(body);
256
- return false;
257
- }
258
- }
259
- executeSendSync(body, sendFn) {
260
- if (this.shouldSkipSend()) {
261
- this.logQueue(body);
262
- return true;
263
- }
264
- if (!this.canSendSync()) {
265
- logging_1.debugLog.info('SenderManager', `⏱️ Rate limited - skipping sync send`, {
266
- eventsCount: body.events.length,
267
- timeSinceLastSend: Date.now() - this.lastSyncSend,
268
- });
269
- return false;
270
- }
271
- logging_1.debugLog.info('SenderManager', `🌐 Sending events to server (sync)`, {
272
- eventsCount: body.events.length,
273
- sessionId: body.session_id,
274
- userId: body.user_id,
275
- method: 'sendBeacon/XHR',
276
- });
277
- this.lastSyncSend = Date.now();
278
- try {
279
- const success = sendFn();
280
- if (success) {
281
- logging_1.debugLog.info('SenderManager', `✅ Successfully sent events to server`, {
282
- eventsCount: body.events.length,
283
- method: 'sync',
284
- });
285
- this.resetRetryState();
286
- this.clearPersistedEvents();
276
+ this.retryCount++;
277
+ this.isRetrying = true;
278
+ try {
279
+ const success = await this.send(body);
280
+ if (success) {
281
+ this.clearPersistedEvents();
282
+ this.resetRetryState();
283
+ originalCallbacks?.onSuccess?.(body.events.length);
284
+ }
285
+ else if (this.retryCount >= constants_1.MAX_RETRIES) {
286
+ this.clearPersistedEvents();
287
+ this.resetRetryState();
288
+ originalCallbacks?.onFailure?.();
289
+ }
290
+ else {
291
+ this.scheduleRetry(body, originalCallbacks);
292
+ }
287
293
  }
288
- else {
289
- logging_1.debugLog.warn('SenderManager', 'Failed to send events', {
290
- eventsCount: body.events.length,
291
- method: 'sync',
292
- });
293
- this.handleSendFailure(body);
294
+ finally {
295
+ this.isRetrying = false;
294
296
  }
295
- return success;
296
- }
297
- catch {
298
- logging_1.debugLog.info('SenderManager', `💥 Exception during event sending`, {
299
- eventsCount: body.events.length,
300
- method: 'sync',
301
- });
302
- this.handleSendFailure(body);
303
- return false;
304
- }
297
+ }, retryDelay);
298
+ utils_1.debugLog.debug('SenderManager', 'Retry scheduled', {
299
+ attempt: this.retryCount + 1,
300
+ delay: retryDelay,
301
+ events: body.events.length,
302
+ });
305
303
  }
306
304
  shouldSkipSend() {
307
- const { id, mode } = this.get('config');
308
- const specialModes = [types_1.Mode.QA, types_1.Mode.DEBUG];
309
- if (id === types_1.SpecialProjectId.HttpSkip) {
310
- return true;
311
- }
312
- return !!mode && specialModes.includes(mode) && id !== types_1.SpecialProjectId.HttpLocal;
305
+ const config = this.get('config');
306
+ const { id } = config || {};
307
+ return id === types_1.SpecialProjectId.Skip;
308
+ }
309
+ /**
310
+ * Simulate a successful send operation for skip mode
311
+ * Provides realistic timing and behavior without making HTTP requests
312
+ */
313
+ async simulateSuccessfulSend() {
314
+ // Simulate realistic network delay (100-500ms)
315
+ const delay = Math.random() * 400 + 100;
316
+ await new Promise((resolve) => setTimeout(resolve, delay));
317
+ return true; // Always successful in skip mode
313
318
  }
314
319
  isSendBeaconAvailable() {
315
- if (typeof navigator.sendBeacon !== 'function') {
316
- return false;
317
- }
318
- return true;
320
+ return typeof navigator !== 'undefined' && typeof navigator.sendBeacon === 'function';
319
321
  }
320
322
  clearRetryTimeout() {
321
323
  if (this.retryTimeoutId !== null) {