@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.
- package/README.md +58 -24
- package/dist/browser/tracelog.js +1934 -3226
- package/dist/cjs/api.d.ts +33 -19
- package/dist/cjs/api.js +111 -156
- package/dist/cjs/app.constants.d.ts +80 -1
- package/dist/cjs/app.constants.js +90 -3
- package/dist/cjs/app.d.ts +29 -44
- package/dist/cjs/app.js +114 -212
- package/dist/cjs/app.types.d.ts +2 -7
- package/dist/cjs/app.types.js +10 -21
- package/dist/cjs/constants/api.constants.js +11 -5
- package/dist/cjs/constants/config.constants.d.ts +75 -0
- package/dist/cjs/constants/config.constants.js +178 -0
- package/dist/cjs/constants/error.constants.d.ts +29 -0
- package/dist/cjs/constants/error.constants.js +50 -0
- package/dist/cjs/constants/index.d.ts +3 -6
- package/dist/cjs/constants/index.js +3 -6
- package/dist/cjs/constants/performance.constants.d.ts +28 -0
- package/dist/cjs/constants/performance.constants.js +43 -0
- package/dist/cjs/handlers/click.handler.d.ts +1 -0
- package/dist/cjs/handlers/click.handler.js +30 -49
- package/dist/cjs/handlers/error.handler.d.ts +11 -6
- package/dist/cjs/handlers/error.handler.js +91 -51
- package/dist/cjs/handlers/page-view.handler.js +38 -29
- package/dist/cjs/handlers/performance.handler.d.ts +3 -0
- package/dist/cjs/handlers/performance.handler.js +76 -37
- package/dist/cjs/handlers/scroll.handler.d.ts +15 -0
- package/dist/cjs/handlers/scroll.handler.js +105 -31
- package/dist/cjs/handlers/session.handler.d.ts +6 -20
- package/dist/cjs/handlers/session.handler.js +38 -326
- package/dist/cjs/integrations/google-analytics.integration.d.ts +0 -1
- package/dist/cjs/integrations/google-analytics.integration.js +27 -98
- package/dist/cjs/listeners/input-listener-managers.d.ts +18 -9
- package/dist/cjs/listeners/input-listener-managers.js +24 -33
- package/dist/cjs/listeners/touch-listener-manager.d.ts +1 -3
- package/dist/cjs/listeners/touch-listener-manager.js +1 -23
- package/dist/cjs/listeners/visibility-listener-manager.d.ts +1 -4
- package/dist/cjs/listeners/visibility-listener-manager.js +6 -42
- package/dist/cjs/managers/api.manager.d.ts +13 -3
- package/dist/cjs/managers/api.manager.js +35 -5
- package/dist/cjs/managers/config.manager.d.ts +53 -3
- package/dist/cjs/managers/config.manager.js +131 -62
- package/dist/cjs/managers/event.manager.d.ts +57 -36
- package/dist/cjs/managers/event.manager.js +266 -417
- package/dist/cjs/managers/sender.manager.d.ts +40 -22
- package/dist/cjs/managers/sender.manager.js +200 -198
- package/dist/cjs/managers/session.manager.d.ts +80 -66
- package/dist/cjs/managers/session.manager.js +267 -522
- package/dist/cjs/managers/state.manager.d.ts +33 -0
- package/dist/cjs/managers/state.manager.js +79 -6
- package/dist/cjs/managers/storage.manager.d.ts +26 -2
- package/dist/cjs/managers/storage.manager.js +67 -34
- package/dist/cjs/managers/tags.manager.d.ts +31 -7
- package/dist/cjs/managers/tags.manager.js +123 -241
- package/dist/cjs/managers/user.manager.d.ts +14 -5
- package/dist/cjs/managers/user.manager.js +17 -9
- package/dist/cjs/public-api.d.ts +10 -1
- package/dist/cjs/public-api.js +18 -24
- package/dist/cjs/test-bridge.d.ts +48 -0
- package/dist/cjs/test-bridge.js +110 -0
- package/dist/cjs/types/api.types.d.ts +21 -6
- package/dist/cjs/types/api.types.js +21 -6
- package/dist/cjs/types/config.types.d.ts +22 -84
- package/dist/cjs/types/emitter.types.d.ts +11 -0
- package/dist/cjs/types/emitter.types.js +8 -0
- package/dist/cjs/types/event.types.d.ts +8 -11
- package/dist/cjs/types/index.d.ts +3 -1
- package/dist/cjs/types/index.js +3 -1
- package/dist/cjs/types/queue.types.d.ts +1 -0
- package/dist/cjs/types/session.types.d.ts +0 -64
- package/dist/cjs/types/state.types.d.ts +1 -0
- package/dist/cjs/types/test-bridge.types.d.ts +38 -0
- package/dist/cjs/types/validation-error.types.d.ts +7 -0
- package/dist/cjs/types/validation-error.types.js +11 -1
- package/dist/cjs/types/window.types.d.ts +1 -8
- package/dist/cjs/utils/data/uuid.utils.d.ts +1 -1
- package/dist/cjs/utils/data/uuid.utils.js +7 -5
- package/dist/cjs/utils/emitter.utils.d.ts +8 -0
- package/dist/cjs/utils/emitter.utils.js +33 -0
- package/dist/cjs/utils/index.d.ts +1 -0
- package/dist/cjs/utils/index.js +1 -0
- package/dist/cjs/utils/logging/debug-logger.utils.d.ts +10 -51
- package/dist/cjs/utils/logging/debug-logger.utils.js +36 -127
- package/dist/cjs/utils/network/fetch-with-timeout.utils.d.ts +4 -0
- package/dist/cjs/utils/network/fetch-with-timeout.utils.js +25 -0
- package/dist/cjs/utils/network/index.d.ts +1 -0
- package/dist/cjs/utils/network/index.js +1 -0
- package/dist/cjs/utils/network/url.utils.js +2 -42
- package/dist/cjs/utils/security/sanitize.utils.d.ts +1 -8
- package/dist/cjs/utils/security/sanitize.utils.js +7 -41
- package/dist/cjs/utils/validations/config-validations.utils.d.ts +7 -0
- package/dist/cjs/utils/validations/config-validations.utils.js +77 -22
- package/dist/esm/api.d.ts +33 -19
- package/dist/esm/api.js +105 -118
- package/dist/esm/app.constants.d.ts +80 -1
- package/dist/esm/app.constants.js +89 -1
- package/dist/esm/app.d.ts +29 -44
- package/dist/esm/app.js +115 -213
- package/dist/esm/app.types.d.ts +2 -7
- package/dist/esm/app.types.js +1 -7
- package/dist/esm/constants/api.constants.js +10 -4
- package/dist/esm/constants/config.constants.d.ts +75 -0
- package/dist/esm/constants/config.constants.js +174 -0
- package/dist/esm/constants/error.constants.d.ts +29 -0
- package/dist/esm/constants/error.constants.js +47 -0
- package/dist/esm/constants/index.d.ts +3 -6
- package/dist/esm/constants/index.js +3 -6
- package/dist/esm/constants/performance.constants.d.ts +28 -0
- package/dist/esm/constants/performance.constants.js +40 -0
- package/dist/esm/handlers/click.handler.d.ts +1 -0
- package/dist/esm/handlers/click.handler.js +30 -49
- package/dist/esm/handlers/error.handler.d.ts +11 -6
- package/dist/esm/handlers/error.handler.js +91 -51
- package/dist/esm/handlers/page-view.handler.js +38 -29
- package/dist/esm/handlers/performance.handler.d.ts +3 -0
- package/dist/esm/handlers/performance.handler.js +71 -32
- package/dist/esm/handlers/scroll.handler.d.ts +15 -0
- package/dist/esm/handlers/scroll.handler.js +106 -32
- package/dist/esm/handlers/session.handler.d.ts +6 -20
- package/dist/esm/handlers/session.handler.js +38 -326
- package/dist/esm/integrations/google-analytics.integration.d.ts +0 -1
- package/dist/esm/integrations/google-analytics.integration.js +27 -98
- package/dist/esm/listeners/input-listener-managers.d.ts +18 -9
- package/dist/esm/listeners/input-listener-managers.js +23 -32
- package/dist/esm/listeners/touch-listener-manager.d.ts +1 -3
- package/dist/esm/listeners/touch-listener-manager.js +1 -23
- package/dist/esm/listeners/visibility-listener-manager.d.ts +1 -4
- package/dist/esm/listeners/visibility-listener-manager.js +6 -42
- package/dist/esm/managers/api.manager.d.ts +13 -3
- package/dist/esm/managers/api.manager.js +34 -3
- package/dist/esm/managers/config.manager.d.ts +53 -3
- package/dist/esm/managers/config.manager.js +133 -64
- package/dist/esm/managers/event.manager.d.ts +57 -36
- package/dist/esm/managers/event.manager.js +268 -419
- package/dist/esm/managers/sender.manager.d.ts +40 -22
- package/dist/esm/managers/sender.manager.js +201 -199
- package/dist/esm/managers/session.manager.d.ts +80 -66
- package/dist/esm/managers/session.manager.js +269 -524
- package/dist/esm/managers/state.manager.d.ts +33 -0
- package/dist/esm/managers/state.manager.js +78 -6
- package/dist/esm/managers/storage.manager.d.ts +26 -2
- package/dist/esm/managers/storage.manager.js +66 -33
- package/dist/esm/managers/tags.manager.d.ts +31 -7
- package/dist/esm/managers/tags.manager.js +124 -242
- package/dist/esm/managers/user.manager.d.ts +14 -5
- package/dist/esm/managers/user.manager.js +17 -9
- package/dist/esm/public-api.d.ts +10 -1
- package/dist/esm/public-api.js +14 -1
- package/dist/esm/test-bridge.d.ts +48 -0
- package/dist/esm/test-bridge.js +106 -0
- package/dist/esm/types/api.types.d.ts +21 -6
- package/dist/esm/types/api.types.js +21 -6
- package/dist/esm/types/config.types.d.ts +22 -84
- package/dist/esm/types/emitter.types.d.ts +11 -0
- package/dist/esm/types/emitter.types.js +5 -0
- package/dist/esm/types/event.types.d.ts +8 -11
- package/dist/esm/types/index.d.ts +3 -1
- package/dist/esm/types/index.js +3 -1
- package/dist/esm/types/queue.types.d.ts +1 -0
- package/dist/esm/types/session.types.d.ts +0 -64
- package/dist/esm/types/state.types.d.ts +1 -0
- package/dist/esm/types/test-bridge.types.d.ts +38 -0
- package/dist/esm/types/validation-error.types.d.ts +7 -0
- package/dist/esm/types/validation-error.types.js +9 -0
- package/dist/esm/types/window.types.d.ts +1 -8
- package/dist/esm/utils/data/uuid.utils.d.ts +1 -1
- package/dist/esm/utils/data/uuid.utils.js +7 -5
- package/dist/esm/utils/emitter.utils.d.ts +8 -0
- package/dist/esm/utils/emitter.utils.js +29 -0
- package/dist/esm/utils/index.d.ts +1 -0
- package/dist/esm/utils/index.js +1 -0
- package/dist/esm/utils/logging/debug-logger.utils.d.ts +10 -51
- package/dist/esm/utils/logging/debug-logger.utils.js +36 -127
- package/dist/esm/utils/network/fetch-with-timeout.utils.d.ts +4 -0
- package/dist/esm/utils/network/fetch-with-timeout.utils.js +22 -0
- package/dist/esm/utils/network/index.d.ts +1 -0
- package/dist/esm/utils/network/index.js +1 -0
- package/dist/esm/utils/network/url.utils.js +2 -42
- package/dist/esm/utils/security/sanitize.utils.d.ts +1 -8
- package/dist/esm/utils/security/sanitize.utils.js +6 -39
- package/dist/esm/utils/validations/config-validations.utils.d.ts +7 -0
- package/dist/esm/utils/validations/config-validations.utils.js +76 -22
- package/package.json +23 -16
- package/dist/browser/web-vitals-CCnqwnC8.mjs +0 -198
- package/dist/cjs/constants/browser.constants.d.ts +0 -3
- package/dist/cjs/constants/browser.constants.js +0 -41
- package/dist/cjs/constants/initialization.constants.d.ts +0 -40
- package/dist/cjs/constants/initialization.constants.js +0 -48
- package/dist/cjs/constants/limits.constants.d.ts +0 -25
- package/dist/cjs/constants/limits.constants.js +0 -40
- package/dist/cjs/constants/security.constants.d.ts +0 -1
- package/dist/cjs/constants/security.constants.js +0 -12
- package/dist/cjs/constants/timing.constants.d.ts +0 -22
- package/dist/cjs/constants/timing.constants.js +0 -34
- package/dist/cjs/constants/validation.constants.d.ts +0 -13
- package/dist/cjs/constants/validation.constants.js +0 -31
- package/dist/cjs/handlers/network.handler.d.ts +0 -16
- package/dist/cjs/handlers/network.handler.js +0 -136
- package/dist/cjs/managers/cross-tab-session.manager.d.ts +0 -170
- package/dist/cjs/managers/cross-tab-session.manager.js +0 -730
- package/dist/cjs/managers/sampling.manager.d.ts +0 -8
- package/dist/cjs/managers/sampling.manager.js +0 -53
- package/dist/cjs/managers/session-recovery.manager.d.ts +0 -65
- package/dist/cjs/managers/session-recovery.manager.js +0 -237
- package/dist/cjs/types/web-vitals.types.d.ts +0 -6
- package/dist/esm/constants/browser.constants.d.ts +0 -3
- package/dist/esm/constants/browser.constants.js +0 -38
- package/dist/esm/constants/initialization.constants.d.ts +0 -40
- package/dist/esm/constants/initialization.constants.js +0 -45
- package/dist/esm/constants/limits.constants.d.ts +0 -25
- package/dist/esm/constants/limits.constants.js +0 -37
- package/dist/esm/constants/security.constants.d.ts +0 -1
- package/dist/esm/constants/security.constants.js +0 -9
- package/dist/esm/constants/timing.constants.d.ts +0 -22
- package/dist/esm/constants/timing.constants.js +0 -31
- package/dist/esm/constants/validation.constants.d.ts +0 -13
- package/dist/esm/constants/validation.constants.js +0 -28
- package/dist/esm/handlers/network.handler.d.ts +0 -16
- package/dist/esm/handlers/network.handler.js +0 -132
- package/dist/esm/managers/cross-tab-session.manager.d.ts +0 -170
- package/dist/esm/managers/cross-tab-session.manager.js +0 -726
- package/dist/esm/managers/sampling.manager.d.ts +0 -8
- package/dist/esm/managers/sampling.manager.js +0 -49
- package/dist/esm/managers/session-recovery.manager.d.ts +0 -65
- package/dist/esm/managers/session-recovery.manager.js +0 -233
- package/dist/esm/types/web-vitals.types.d.ts +0 -6
- /package/dist/cjs/types/{web-vitals.types.js → test-bridge.types.js} +0 -0
- /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
|
|
10
|
-
private
|
|
11
|
+
private retryCount;
|
|
12
|
+
private isRetrying;
|
|
11
13
|
constructor(storeManager: StorageManager);
|
|
12
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
29
|
+
recoverPersistedEvents(callbacks?: SendCallbacks): Promise<void>;
|
|
21
30
|
/**
|
|
22
|
-
*
|
|
31
|
+
* Persist events for recovery in case of failure
|
|
23
32
|
*/
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
|
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
|
|
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.
|
|
14
|
-
this.
|
|
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
|
-
|
|
20
|
-
|
|
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
|
-
|
|
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
|
-
|
|
26
|
-
|
|
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
|
-
|
|
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
|
|
36
|
-
const success = this.
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
});
|
|
48
|
-
this.scheduleRetryForRecoveredEvents(recoveryBody);
|
|
81
|
+
this.scheduleRetry(body, callbacks);
|
|
82
|
+
callbacks?.onFailure?.();
|
|
49
83
|
}
|
|
50
84
|
}
|
|
51
85
|
catch (error) {
|
|
52
|
-
|
|
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
|
-
*
|
|
91
|
+
* Persist events for recovery in case of failure
|
|
61
92
|
*/
|
|
62
|
-
|
|
63
|
-
return this.
|
|
93
|
+
persistEventsForRecovery(body) {
|
|
94
|
+
return this.persistEvents(body);
|
|
64
95
|
}
|
|
65
96
|
/**
|
|
66
|
-
*
|
|
97
|
+
* Legacy method for backward compatibility
|
|
98
|
+
* @deprecated Use sendEventsQueue instead
|
|
67
99
|
*/
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
82
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
142
|
-
logging_1.debugLog.error('SenderManager', 'Sync XHR failed', {
|
|
181
|
+
utils_1.debugLog.warn('SenderManager', 'Sync XHR error', {
|
|
143
182
|
error: errorMessage,
|
|
144
|
-
|
|
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
|
|
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
|
-
|
|
162
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
242
|
+
utils_1.debugLog.warn('SenderManager', 'Failed to persist events', { error });
|
|
243
|
+
return false;
|
|
198
244
|
}
|
|
199
245
|
}
|
|
200
246
|
clearPersistedEvents() {
|
|
201
|
-
|
|
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.
|
|
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.
|
|
212
|
-
this.
|
|
213
|
-
this.
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
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
|
-
|
|
289
|
-
|
|
290
|
-
eventsCount: body.events.length,
|
|
291
|
-
method: 'sync',
|
|
292
|
-
});
|
|
293
|
-
this.handleSendFailure(body);
|
|
294
|
+
finally {
|
|
295
|
+
this.isRetrying = false;
|
|
294
296
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
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
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
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
|
-
|
|
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) {
|