@tracelog/lib 0.5.4 → 0.6.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 +157 -180
- package/dist/browser/tracelog.esm.js +1007 -1357
- package/dist/browser/tracelog.js +2 -2
- package/dist/cjs/api.d.ts +12 -2
- package/dist/cjs/api.js +63 -27
- package/dist/cjs/app.d.ts +2 -2
- package/dist/cjs/app.js +26 -32
- package/dist/cjs/constants/config.constants.d.ts +4 -2
- package/dist/cjs/constants/config.constants.js +6 -18
- package/dist/cjs/constants/index.d.ts +0 -1
- package/dist/cjs/constants/index.js +0 -1
- package/dist/cjs/constants/storage.constants.d.ts +3 -2
- package/dist/cjs/constants/storage.constants.js +4 -4
- package/dist/cjs/handlers/click.handler.js +3 -6
- package/dist/cjs/handlers/error.handler.js +1 -11
- package/dist/cjs/handlers/page-view.handler.js +0 -4
- package/dist/cjs/handlers/performance.handler.js +14 -29
- package/dist/cjs/handlers/scroll.handler.js +7 -6
- package/dist/cjs/handlers/session.handler.js +7 -6
- package/dist/cjs/integrations/google-analytics.integration.js +2 -6
- package/dist/cjs/listeners/activity-listener-manager.js +3 -3
- package/dist/cjs/listeners/input-listener-managers.js +3 -3
- package/dist/cjs/listeners/touch-listener-manager.js +3 -3
- package/dist/cjs/listeners/unload-listener-manager.js +3 -3
- package/dist/cjs/listeners/visibility-listener-manager.js +3 -3
- package/dist/cjs/managers/event.manager.d.ts +2 -1
- package/dist/cjs/managers/event.manager.js +60 -38
- package/dist/cjs/managers/sender.manager.js +29 -36
- package/dist/cjs/managers/session.manager.js +5 -13
- package/dist/cjs/managers/state.manager.d.ts +0 -3
- package/dist/cjs/managers/state.manager.js +1 -43
- package/dist/cjs/managers/storage.manager.d.ts +16 -2
- package/dist/cjs/managers/storage.manager.js +73 -19
- package/dist/cjs/managers/user.manager.d.ts +1 -1
- package/dist/cjs/managers/user.manager.js +2 -2
- package/dist/cjs/public-api.d.ts +3 -3
- package/dist/cjs/public-api.js +1 -1
- package/dist/cjs/test-bridge.d.ts +1 -0
- package/dist/cjs/test-bridge.js +37 -2
- package/dist/cjs/types/config.types.d.ts +15 -18
- package/dist/cjs/types/config.types.js +6 -0
- package/dist/cjs/types/event.types.d.ts +1 -13
- package/dist/cjs/types/index.d.ts +0 -2
- package/dist/cjs/types/index.js +0 -2
- package/dist/cjs/types/mode.types.d.ts +1 -2
- package/dist/cjs/types/mode.types.js +0 -1
- package/dist/cjs/types/queue.types.d.ts +0 -6
- package/dist/cjs/types/state.types.d.ts +2 -0
- package/dist/cjs/types/test-bridge.types.d.ts +2 -2
- package/dist/cjs/types/validation-error.types.d.ts +0 -6
- package/dist/cjs/types/validation-error.types.js +1 -10
- package/dist/cjs/utils/browser/device-detector.utils.js +2 -24
- package/dist/cjs/utils/browser/index.d.ts +1 -0
- package/dist/cjs/utils/browser/index.js +1 -0
- package/dist/cjs/utils/browser/qa-mode.utils.d.ts +13 -0
- package/dist/cjs/utils/browser/qa-mode.utils.js +43 -0
- package/dist/cjs/utils/browser/utm-params.utils.js +0 -15
- package/dist/cjs/utils/data/uuid.utils.d.ts +13 -0
- package/dist/cjs/utils/data/uuid.utils.js +37 -1
- package/dist/cjs/utils/index.d.ts +1 -1
- package/dist/cjs/utils/index.js +1 -1
- package/dist/cjs/utils/logging.utils.d.ts +6 -0
- package/dist/cjs/utils/logging.utils.js +25 -0
- package/dist/cjs/utils/network/index.d.ts +0 -1
- package/dist/cjs/utils/network/index.js +0 -1
- package/dist/cjs/utils/network/url.utils.d.ts +2 -8
- package/dist/cjs/utils/network/url.utils.js +46 -90
- package/dist/cjs/utils/security/sanitize.utils.d.ts +1 -13
- package/dist/cjs/utils/security/sanitize.utils.js +15 -178
- package/dist/cjs/utils/validations/config-validations.utils.d.ts +3 -9
- package/dist/cjs/utils/validations/config-validations.utils.js +48 -94
- package/dist/cjs/utils/validations/event-validations.utils.js +11 -5
- package/dist/cjs/utils/validations/index.d.ts +0 -1
- package/dist/cjs/utils/validations/index.js +0 -1
- package/dist/cjs/utils/validations/metadata-validations.utils.js +0 -1
- package/dist/cjs/utils/validations/type-guards.utils.d.ts +2 -2
- package/dist/cjs/utils/validations/type-guards.utils.js +50 -4
- package/dist/esm/api.d.ts +12 -2
- package/dist/esm/api.js +62 -27
- package/dist/esm/app.d.ts +2 -2
- package/dist/esm/app.js +28 -34
- package/dist/esm/constants/config.constants.d.ts +4 -2
- package/dist/esm/constants/config.constants.js +4 -16
- package/dist/esm/constants/index.d.ts +0 -1
- package/dist/esm/constants/index.js +0 -1
- package/dist/esm/constants/storage.constants.d.ts +3 -2
- package/dist/esm/constants/storage.constants.js +3 -2
- package/dist/esm/handlers/click.handler.js +3 -6
- package/dist/esm/handlers/error.handler.js +1 -11
- package/dist/esm/handlers/page-view.handler.js +0 -4
- package/dist/esm/handlers/performance.handler.js +14 -29
- package/dist/esm/handlers/scroll.handler.js +7 -6
- package/dist/esm/handlers/session.handler.js +7 -6
- package/dist/esm/integrations/google-analytics.integration.js +3 -7
- package/dist/esm/listeners/activity-listener-manager.js +3 -3
- package/dist/esm/listeners/input-listener-managers.js +3 -3
- package/dist/esm/listeners/touch-listener-manager.js +3 -3
- package/dist/esm/listeners/unload-listener-manager.js +3 -3
- package/dist/esm/listeners/visibility-listener-manager.js +3 -3
- package/dist/esm/managers/event.manager.d.ts +2 -1
- package/dist/esm/managers/event.manager.js +62 -40
- package/dist/esm/managers/sender.manager.js +31 -38
- package/dist/esm/managers/session.manager.js +5 -13
- package/dist/esm/managers/state.manager.d.ts +0 -3
- package/dist/esm/managers/state.manager.js +1 -43
- package/dist/esm/managers/storage.manager.d.ts +16 -2
- package/dist/esm/managers/storage.manager.js +73 -19
- package/dist/esm/managers/user.manager.d.ts +1 -1
- package/dist/esm/managers/user.manager.js +2 -2
- package/dist/esm/public-api.d.ts +3 -3
- package/dist/esm/public-api.js +1 -1
- package/dist/esm/test-bridge.d.ts +1 -0
- package/dist/esm/test-bridge.js +37 -2
- package/dist/esm/types/config.types.d.ts +15 -18
- package/dist/esm/types/config.types.js +5 -1
- package/dist/esm/types/event.types.d.ts +1 -13
- package/dist/esm/types/index.d.ts +0 -2
- package/dist/esm/types/index.js +0 -2
- package/dist/esm/types/mode.types.d.ts +1 -2
- package/dist/esm/types/mode.types.js +0 -1
- package/dist/esm/types/queue.types.d.ts +0 -6
- package/dist/esm/types/state.types.d.ts +2 -0
- package/dist/esm/types/test-bridge.types.d.ts +2 -2
- package/dist/esm/types/validation-error.types.d.ts +0 -6
- package/dist/esm/types/validation-error.types.js +0 -8
- package/dist/esm/utils/browser/device-detector.utils.js +2 -24
- package/dist/esm/utils/browser/index.d.ts +1 -0
- package/dist/esm/utils/browser/index.js +1 -0
- package/dist/esm/utils/browser/qa-mode.utils.d.ts +13 -0
- package/dist/esm/utils/browser/qa-mode.utils.js +39 -0
- package/dist/esm/utils/browser/utm-params.utils.js +0 -15
- package/dist/esm/utils/data/uuid.utils.d.ts +13 -0
- package/dist/esm/utils/data/uuid.utils.js +35 -0
- package/dist/esm/utils/index.d.ts +1 -1
- package/dist/esm/utils/index.js +1 -1
- package/dist/esm/utils/logging.utils.d.ts +6 -0
- package/dist/esm/utils/logging.utils.js +20 -0
- package/dist/esm/utils/network/index.d.ts +0 -1
- package/dist/esm/utils/network/index.js +0 -1
- package/dist/esm/utils/network/url.utils.d.ts +2 -8
- package/dist/esm/utils/network/url.utils.js +45 -88
- package/dist/esm/utils/security/sanitize.utils.d.ts +1 -13
- package/dist/esm/utils/security/sanitize.utils.js +15 -176
- package/dist/esm/utils/validations/config-validations.utils.d.ts +3 -9
- package/dist/esm/utils/validations/config-validations.utils.js +49 -94
- package/dist/esm/utils/validations/event-validations.utils.js +11 -5
- package/dist/esm/utils/validations/index.d.ts +0 -1
- package/dist/esm/utils/validations/index.js +0 -1
- package/dist/esm/utils/validations/metadata-validations.utils.js +0 -1
- package/dist/esm/utils/validations/type-guards.utils.d.ts +2 -2
- package/dist/esm/utils/validations/type-guards.utils.js +50 -4
- package/package.json +1 -1
- package/dist/cjs/app.types.d.ts +0 -2
- package/dist/cjs/app.types.js +0 -12
- package/dist/cjs/constants/api.constants.d.ts +0 -6
- package/dist/cjs/constants/api.constants.js +0 -14
- package/dist/cjs/managers/api.manager.d.ts +0 -13
- package/dist/cjs/managers/api.manager.js +0 -44
- package/dist/cjs/managers/config.builder.d.ts +0 -33
- package/dist/cjs/managers/config.builder.js +0 -116
- package/dist/cjs/managers/config.manager.d.ts +0 -56
- package/dist/cjs/managers/config.manager.js +0 -157
- package/dist/cjs/managers/tags.manager.d.ts +0 -36
- package/dist/cjs/managers/tags.manager.js +0 -171
- package/dist/cjs/types/api.types.d.ts +0 -52
- package/dist/cjs/types/api.types.js +0 -56
- package/dist/cjs/types/tag.types.d.ts +0 -43
- package/dist/cjs/types/tag.types.js +0 -31
- package/dist/cjs/utils/logging/debug-logger.utils.d.ts +0 -14
- package/dist/cjs/utils/logging/debug-logger.utils.js +0 -47
- package/dist/cjs/utils/logging/index.d.ts +0 -1
- package/dist/cjs/utils/logging/index.js +0 -5
- package/dist/cjs/utils/network/fetch-with-timeout.utils.d.ts +0 -4
- package/dist/cjs/utils/network/fetch-with-timeout.utils.js +0 -25
- package/dist/cjs/utils/validations/url-validations.utils.d.ts +0 -15
- package/dist/cjs/utils/validations/url-validations.utils.js +0 -47
- package/dist/esm/app.types.d.ts +0 -2
- package/dist/esm/app.types.js +0 -1
- package/dist/esm/constants/api.constants.d.ts +0 -6
- package/dist/esm/constants/api.constants.js +0 -11
- package/dist/esm/managers/api.manager.d.ts +0 -13
- package/dist/esm/managers/api.manager.js +0 -41
- package/dist/esm/managers/config.builder.d.ts +0 -33
- package/dist/esm/managers/config.builder.js +0 -112
- package/dist/esm/managers/config.manager.d.ts +0 -56
- package/dist/esm/managers/config.manager.js +0 -153
- package/dist/esm/managers/tags.manager.d.ts +0 -36
- package/dist/esm/managers/tags.manager.js +0 -167
- package/dist/esm/types/api.types.d.ts +0 -52
- package/dist/esm/types/api.types.js +0 -53
- package/dist/esm/types/tag.types.d.ts +0 -43
- package/dist/esm/types/tag.types.js +0 -28
- package/dist/esm/utils/logging/debug-logger.utils.d.ts +0 -14
- package/dist/esm/utils/logging/debug-logger.utils.js +0 -44
- package/dist/esm/utils/logging/index.d.ts +0 -1
- package/dist/esm/utils/logging/index.js +0 -1
- package/dist/esm/utils/network/fetch-with-timeout.utils.d.ts +0 -4
- package/dist/esm/utils/network/fetch-with-timeout.utils.js +0 -22
- package/dist/esm/utils/validations/url-validations.utils.d.ts +0 -15
- package/dist/esm/utils/validations/url-validations.utils.js +0 -42
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { QUEUE_KEY, EVENT_EXPIRY_HOURS, MAX_RETRIES, RETRY_DELAY_MS, REQUEST_TIMEOUT_MS } from '../constants';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { SpecialApiUrl } from '../types';
|
|
3
|
+
import { log } from '../utils';
|
|
4
4
|
import { StateManager } from './state.manager';
|
|
5
5
|
export class SenderManager extends StateManager {
|
|
6
6
|
constructor(storeManager) {
|
|
@@ -11,9 +11,8 @@ export class SenderManager extends StateManager {
|
|
|
11
11
|
this.storeManager = storeManager;
|
|
12
12
|
}
|
|
13
13
|
getQueueStorageKey() {
|
|
14
|
-
const projectId = this.get('config')?.id || 'default';
|
|
15
14
|
const userId = this.get('userId') || 'anonymous';
|
|
16
|
-
return
|
|
15
|
+
return QUEUE_KEY(userId);
|
|
17
16
|
}
|
|
18
17
|
sendEventsQueueSync(body) {
|
|
19
18
|
if (this.shouldSkipSend()) {
|
|
@@ -21,9 +20,9 @@ export class SenderManager extends StateManager {
|
|
|
21
20
|
return true;
|
|
22
21
|
}
|
|
23
22
|
const config = this.get('config');
|
|
24
|
-
if (config?.
|
|
25
|
-
|
|
26
|
-
events: body.events.length,
|
|
23
|
+
if (config?.integrations?.custom?.apiUrl === SpecialApiUrl.Fail) {
|
|
24
|
+
log('warn', 'Fail mode: simulating network failure (sync)', {
|
|
25
|
+
data: { events: body.events.length },
|
|
27
26
|
});
|
|
28
27
|
return false;
|
|
29
28
|
}
|
|
@@ -34,9 +33,11 @@ export class SenderManager extends StateManager {
|
|
|
34
33
|
return success;
|
|
35
34
|
}
|
|
36
35
|
async sendEventsQueue(body, callbacks) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
if (!this.shouldSkipSend()) {
|
|
37
|
+
const persisted = this.persistEvents(body);
|
|
38
|
+
if (!persisted) {
|
|
39
|
+
log('warn', 'Failed to persist events, attempting immediate send');
|
|
40
|
+
}
|
|
40
41
|
}
|
|
41
42
|
const success = await this.send(body);
|
|
42
43
|
if (success) {
|
|
@@ -70,7 +71,7 @@ export class SenderManager extends StateManager {
|
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
73
|
catch (error) {
|
|
73
|
-
|
|
74
|
+
log('error', 'Failed to recover persisted events', { error });
|
|
74
75
|
this.clearPersistedEvents();
|
|
75
76
|
}
|
|
76
77
|
}
|
|
@@ -89,9 +90,9 @@ export class SenderManager extends StateManager {
|
|
|
89
90
|
return this.simulateSuccessfulSend();
|
|
90
91
|
}
|
|
91
92
|
const config = this.get('config');
|
|
92
|
-
if (config?.
|
|
93
|
-
|
|
94
|
-
events: body.events.length,
|
|
93
|
+
if (config?.integrations?.custom?.apiUrl === SpecialApiUrl.Fail) {
|
|
94
|
+
log('warn', 'Fail mode: simulating network failure', {
|
|
95
|
+
data: { events: body.events.length },
|
|
95
96
|
});
|
|
96
97
|
return false;
|
|
97
98
|
}
|
|
@@ -101,11 +102,12 @@ export class SenderManager extends StateManager {
|
|
|
101
102
|
return response.ok;
|
|
102
103
|
}
|
|
103
104
|
catch (error) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
105
|
+
log('error', 'Send request failed', {
|
|
106
|
+
error,
|
|
107
|
+
data: {
|
|
108
|
+
events: body.events.length,
|
|
109
|
+
url: url.replace(/\/\/[^/]+/, '//[DOMAIN]'),
|
|
110
|
+
},
|
|
109
111
|
});
|
|
110
112
|
return false;
|
|
111
113
|
}
|
|
@@ -113,18 +115,16 @@ export class SenderManager extends StateManager {
|
|
|
113
115
|
async sendWithTimeout(url, payload) {
|
|
114
116
|
const controller = new AbortController();
|
|
115
117
|
const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
116
|
-
const config = this.get('config');
|
|
117
118
|
try {
|
|
118
119
|
const response = await fetch(url, {
|
|
119
120
|
method: 'POST',
|
|
120
|
-
headers: {
|
|
121
|
-
'Content-Type': 'application/json',
|
|
122
|
-
'X-TraceLog-Project': config?.id || 'unknown',
|
|
123
|
-
},
|
|
124
121
|
body: payload,
|
|
125
122
|
keepalive: true,
|
|
126
123
|
credentials: 'include',
|
|
127
124
|
signal: controller.signal,
|
|
125
|
+
headers: {
|
|
126
|
+
'Content-Type': 'application/json',
|
|
127
|
+
},
|
|
128
128
|
});
|
|
129
129
|
if (!response.ok) {
|
|
130
130
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
@@ -143,10 +143,10 @@ export class SenderManager extends StateManager {
|
|
|
143
143
|
if (success) {
|
|
144
144
|
return true;
|
|
145
145
|
}
|
|
146
|
-
|
|
146
|
+
log('warn', 'sendBeacon failed, persisting events for recovery');
|
|
147
147
|
}
|
|
148
148
|
else {
|
|
149
|
-
|
|
149
|
+
log('warn', 'sendBeacon not available, persisting events for recovery');
|
|
150
150
|
}
|
|
151
151
|
this.persistEventsForRecovery(body);
|
|
152
152
|
return false;
|
|
@@ -176,7 +176,7 @@ export class SenderManager extends StateManager {
|
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
178
|
catch (error) {
|
|
179
|
-
|
|
179
|
+
log('warn', 'Failed to parse persisted data', { error });
|
|
180
180
|
this.clearPersistedEvents();
|
|
181
181
|
}
|
|
182
182
|
return null;
|
|
@@ -213,7 +213,7 @@ export class SenderManager extends StateManager {
|
|
|
213
213
|
return !!this.storeManager.getItem(storageKey);
|
|
214
214
|
}
|
|
215
215
|
catch (error) {
|
|
216
|
-
|
|
216
|
+
log('warn', 'Failed to persist events', { error });
|
|
217
217
|
return false;
|
|
218
218
|
}
|
|
219
219
|
}
|
|
@@ -223,7 +223,7 @@ export class SenderManager extends StateManager {
|
|
|
223
223
|
this.storeManager.removeItem(key);
|
|
224
224
|
}
|
|
225
225
|
catch (error) {
|
|
226
|
-
|
|
226
|
+
log('warn', 'Failed to clear persisted events', { error });
|
|
227
227
|
}
|
|
228
228
|
}
|
|
229
229
|
resetRetryState() {
|
|
@@ -236,7 +236,7 @@ export class SenderManager extends StateManager {
|
|
|
236
236
|
return;
|
|
237
237
|
}
|
|
238
238
|
if (this.retryCount >= MAX_RETRIES) {
|
|
239
|
-
|
|
239
|
+
log('warn', 'Max retries reached, giving up', { data: { retryCount: this.retryCount } });
|
|
240
240
|
this.clearPersistedEvents();
|
|
241
241
|
this.resetRetryState();
|
|
242
242
|
originalCallbacks?.onFailure?.();
|
|
@@ -267,16 +267,9 @@ export class SenderManager extends StateManager {
|
|
|
267
267
|
this.isRetrying = false;
|
|
268
268
|
}
|
|
269
269
|
}, retryDelay);
|
|
270
|
-
debugLog.debug('SenderManager', 'Retry scheduled', {
|
|
271
|
-
attempt: this.retryCount + 1,
|
|
272
|
-
delay: retryDelay,
|
|
273
|
-
events: body.events.length,
|
|
274
|
-
});
|
|
275
270
|
}
|
|
276
271
|
shouldSkipSend() {
|
|
277
|
-
|
|
278
|
-
const { id } = config || {};
|
|
279
|
-
return id === SpecialProjectId.Skip;
|
|
272
|
+
return !this.get('apiUrl');
|
|
280
273
|
}
|
|
281
274
|
async simulateSuccessfulSend() {
|
|
282
275
|
const delay = Math.random() * 400 + 100;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BROADCAST_CHANNEL_NAME, DEFAULT_SESSION_TIMEOUT, SESSION_STORAGE_KEY } from '../constants';
|
|
2
2
|
import { EventType } from '../types';
|
|
3
|
-
import {
|
|
3
|
+
import { log } from '../utils';
|
|
4
4
|
import { StateManager } from './state.manager';
|
|
5
5
|
export class SessionManager extends StateManager {
|
|
6
6
|
constructor(storageManager, eventManager, projectId) {
|
|
@@ -17,7 +17,7 @@ export class SessionManager extends StateManager {
|
|
|
17
17
|
}
|
|
18
18
|
initCrossTabSync() {
|
|
19
19
|
if (typeof BroadcastChannel === 'undefined') {
|
|
20
|
-
|
|
20
|
+
log('warn', 'BroadcastChannel not supported');
|
|
21
21
|
return;
|
|
22
22
|
}
|
|
23
23
|
const projectId = this.getProjectId();
|
|
@@ -28,7 +28,6 @@ export class SessionManager extends StateManager {
|
|
|
28
28
|
return;
|
|
29
29
|
}
|
|
30
30
|
if (action === 'session_end') {
|
|
31
|
-
debugLog.debug('SessionManager', 'Session end synced from another tab');
|
|
32
31
|
this.resetSessionState();
|
|
33
32
|
return;
|
|
34
33
|
}
|
|
@@ -39,7 +38,6 @@ export class SessionManager extends StateManager {
|
|
|
39
38
|
if (this.isTracking) {
|
|
40
39
|
this.setupSessionTimeout();
|
|
41
40
|
}
|
|
42
|
-
debugLog.debug('SessionManager', 'Session synced from another tab', { sessionId });
|
|
43
41
|
}
|
|
44
42
|
};
|
|
45
43
|
}
|
|
@@ -82,11 +80,9 @@ export class SessionManager extends StateManager {
|
|
|
82
80
|
}
|
|
83
81
|
const sessionTimeout = this.get('config')?.sessionTimeout ?? DEFAULT_SESSION_TIMEOUT;
|
|
84
82
|
if (Date.now() - storedSession.lastActivity > sessionTimeout) {
|
|
85
|
-
debugLog.debug('SessionManager', 'Stored session expired');
|
|
86
83
|
this.clearStoredSession();
|
|
87
84
|
return null;
|
|
88
85
|
}
|
|
89
|
-
debugLog.info('SessionManager', 'Session recovered from storage', { sessionId: storedSession.id });
|
|
90
86
|
return storedSession.id;
|
|
91
87
|
}
|
|
92
88
|
persistSession(sessionId, lastActivity = Date.now()) {
|
|
@@ -129,7 +125,7 @@ export class SessionManager extends StateManager {
|
|
|
129
125
|
}
|
|
130
126
|
async startTracking() {
|
|
131
127
|
if (this.isTracking) {
|
|
132
|
-
|
|
128
|
+
log('warn', 'Session tracking already active');
|
|
133
129
|
return;
|
|
134
130
|
}
|
|
135
131
|
const recoveredSessionId = this.recoverSession();
|
|
@@ -149,7 +145,6 @@ export class SessionManager extends StateManager {
|
|
|
149
145
|
this.setupSessionTimeout();
|
|
150
146
|
this.setupActivityListeners();
|
|
151
147
|
this.setupLifecycleListeners();
|
|
152
|
-
debugLog.info('SessionManager', 'Session tracking started', { sessionId, recovered: isRecovered });
|
|
153
148
|
}
|
|
154
149
|
catch (error) {
|
|
155
150
|
this.isTracking = false;
|
|
@@ -232,11 +227,10 @@ export class SessionManager extends StateManager {
|
|
|
232
227
|
async endSession(reason) {
|
|
233
228
|
const sessionId = this.get('sessionId');
|
|
234
229
|
if (!sessionId) {
|
|
235
|
-
|
|
230
|
+
log('warn', 'endSession called without active session', { data: { reason } });
|
|
236
231
|
this.resetSessionState(reason);
|
|
237
232
|
return;
|
|
238
233
|
}
|
|
239
|
-
debugLog.info('SessionManager', 'Ending session', { sessionId, reason });
|
|
240
234
|
this.eventManager.track({
|
|
241
235
|
type: EventType.SESSION_END,
|
|
242
236
|
session_end_reason: reason,
|
|
@@ -255,9 +249,7 @@ export class SessionManager extends StateManager {
|
|
|
255
249
|
finalize();
|
|
256
250
|
}
|
|
257
251
|
catch (error) {
|
|
258
|
-
|
|
259
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
260
|
-
});
|
|
252
|
+
log('warn', 'Async flush failed during session end', { error });
|
|
261
253
|
finalize();
|
|
262
254
|
}
|
|
263
255
|
}
|
|
@@ -5,7 +5,4 @@ export declare abstract class StateManager {
|
|
|
5
5
|
protected get<T extends keyof State>(key: T): State[T];
|
|
6
6
|
protected set<T extends keyof State>(key: T, value: State[T]): void;
|
|
7
7
|
protected getState(): Readonly<State>;
|
|
8
|
-
private isCriticalStateKey;
|
|
9
|
-
private shouldLog;
|
|
10
|
-
private formatLogValue;
|
|
11
8
|
}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { debugLog } from '../utils/logging';
|
|
2
|
-
import { DEFAULT_SAMPLING_RATE } from '../constants';
|
|
3
1
|
const globalState = {};
|
|
4
2
|
export function getGlobalState() {
|
|
5
3
|
return globalState;
|
|
@@ -14,49 +12,9 @@ export class StateManager {
|
|
|
14
12
|
return globalState[key];
|
|
15
13
|
}
|
|
16
14
|
set(key, value) {
|
|
17
|
-
|
|
18
|
-
if (key === 'config' && value) {
|
|
19
|
-
const configValue = value;
|
|
20
|
-
if (configValue) {
|
|
21
|
-
const samplingRate = configValue.samplingRate ?? DEFAULT_SAMPLING_RATE;
|
|
22
|
-
const normalizedSamplingRate = samplingRate < 0 || samplingRate > 1 ? DEFAULT_SAMPLING_RATE : samplingRate;
|
|
23
|
-
const hasNormalizedSampling = normalizedSamplingRate !== samplingRate;
|
|
24
|
-
if (hasNormalizedSampling) {
|
|
25
|
-
const normalizedConfig = { ...configValue, samplingRate: normalizedSamplingRate };
|
|
26
|
-
globalState[key] = normalizedConfig;
|
|
27
|
-
}
|
|
28
|
-
else {
|
|
29
|
-
globalState[key] = configValue;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
else {
|
|
33
|
-
globalState[key] = value;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
globalState[key] = value;
|
|
38
|
-
}
|
|
39
|
-
if (this.isCriticalStateKey(key) && this.shouldLog(oldValue, globalState[key])) {
|
|
40
|
-
debugLog.debug('StateManager', 'State updated', {
|
|
41
|
-
key,
|
|
42
|
-
oldValue: this.formatLogValue(key, oldValue),
|
|
43
|
-
newValue: this.formatLogValue(key, globalState[key]),
|
|
44
|
-
});
|
|
45
|
-
}
|
|
15
|
+
globalState[key] = value;
|
|
46
16
|
}
|
|
47
17
|
getState() {
|
|
48
18
|
return { ...globalState };
|
|
49
19
|
}
|
|
50
|
-
isCriticalStateKey(key) {
|
|
51
|
-
return key === 'sessionId' || key === 'config' || key === 'hasStartSession';
|
|
52
|
-
}
|
|
53
|
-
shouldLog(oldValue, newValue) {
|
|
54
|
-
return oldValue !== newValue;
|
|
55
|
-
}
|
|
56
|
-
formatLogValue(key, value) {
|
|
57
|
-
if (key === 'config') {
|
|
58
|
-
return value ? '(configured)' : '(not configured)';
|
|
59
|
-
}
|
|
60
|
-
return value;
|
|
61
|
-
}
|
|
62
20
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Manages localStorage with automatic fallback to in-memory storage.
|
|
2
|
+
* Manages localStorage and sessionStorage with automatic fallback to in-memory storage.
|
|
3
3
|
* Provides a consistent interface for storing session data, configuration,
|
|
4
4
|
* and analytics metadata across browser environments.
|
|
5
5
|
*/
|
|
6
6
|
export declare class StorageManager {
|
|
7
7
|
private readonly storage;
|
|
8
|
+
private readonly sessionStorageRef;
|
|
8
9
|
private readonly fallbackStorage;
|
|
10
|
+
private readonly fallbackSessionStorage;
|
|
9
11
|
private hasQuotaExceededError;
|
|
10
12
|
constructor();
|
|
11
13
|
/**
|
|
@@ -34,7 +36,19 @@ export declare class StorageManager {
|
|
|
34
36
|
*/
|
|
35
37
|
hasQuotaError(): boolean;
|
|
36
38
|
/**
|
|
37
|
-
* Initialize localStorage with feature detection
|
|
39
|
+
* Initialize storage (localStorage or sessionStorage) with feature detection
|
|
38
40
|
*/
|
|
39
41
|
private initializeStorage;
|
|
42
|
+
/**
|
|
43
|
+
* Retrieves an item from sessionStorage
|
|
44
|
+
*/
|
|
45
|
+
getSessionItem(key: string): string | null;
|
|
46
|
+
/**
|
|
47
|
+
* Stores an item in sessionStorage
|
|
48
|
+
*/
|
|
49
|
+
setSessionItem(key: string, value: string): void;
|
|
50
|
+
/**
|
|
51
|
+
* Removes an item from sessionStorage
|
|
52
|
+
*/
|
|
53
|
+
removeSessionItem(key: string): void;
|
|
40
54
|
}
|
|
@@ -1,16 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { log } from '../utils';
|
|
2
2
|
/**
|
|
3
|
-
* Manages localStorage with automatic fallback to in-memory storage.
|
|
3
|
+
* Manages localStorage and sessionStorage with automatic fallback to in-memory storage.
|
|
4
4
|
* Provides a consistent interface for storing session data, configuration,
|
|
5
5
|
* and analytics metadata across browser environments.
|
|
6
6
|
*/
|
|
7
7
|
export class StorageManager {
|
|
8
8
|
constructor() {
|
|
9
9
|
this.fallbackStorage = new Map();
|
|
10
|
+
this.fallbackSessionStorage = new Map();
|
|
10
11
|
this.hasQuotaExceededError = false;
|
|
11
|
-
this.storage = this.initializeStorage();
|
|
12
|
+
this.storage = this.initializeStorage('localStorage');
|
|
13
|
+
this.sessionStorageRef = this.initializeStorage('sessionStorage');
|
|
12
14
|
if (!this.storage) {
|
|
13
|
-
|
|
15
|
+
log('warn', 'localStorage not available, using memory fallback');
|
|
16
|
+
}
|
|
17
|
+
if (!this.sessionStorageRef) {
|
|
18
|
+
log('warn', 'sessionStorage not available, using memory fallback');
|
|
14
19
|
}
|
|
15
20
|
}
|
|
16
21
|
/**
|
|
@@ -23,8 +28,8 @@ export class StorageManager {
|
|
|
23
28
|
}
|
|
24
29
|
return this.fallbackStorage.get(key) ?? null;
|
|
25
30
|
}
|
|
26
|
-
catch
|
|
27
|
-
|
|
31
|
+
catch {
|
|
32
|
+
// Silent fallback - user already warned in constructor
|
|
28
33
|
return this.fallbackStorage.get(key) ?? null;
|
|
29
34
|
}
|
|
30
35
|
}
|
|
@@ -41,14 +46,12 @@ export class StorageManager {
|
|
|
41
46
|
catch (error) {
|
|
42
47
|
if (error instanceof DOMException && error.name === 'QuotaExceededError') {
|
|
43
48
|
this.hasQuotaExceededError = true;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
valueSize: value.length,
|
|
49
|
+
log('error', 'localStorage quota exceeded - data will not persist after reload', {
|
|
50
|
+
error,
|
|
51
|
+
data: { key, valueSize: value.length },
|
|
47
52
|
});
|
|
48
53
|
}
|
|
49
|
-
|
|
50
|
-
debugLog.warn('StorageManager', 'Failed to set item, using fallback', { key, error });
|
|
51
|
-
}
|
|
54
|
+
// Else: Silent fallback - user already warned in constructor
|
|
52
55
|
}
|
|
53
56
|
// Always update fallback for consistency
|
|
54
57
|
this.fallbackStorage.set(key, value);
|
|
@@ -62,8 +65,8 @@ export class StorageManager {
|
|
|
62
65
|
this.storage.removeItem(key);
|
|
63
66
|
}
|
|
64
67
|
}
|
|
65
|
-
catch
|
|
66
|
-
|
|
68
|
+
catch {
|
|
69
|
+
// Silent - not critical
|
|
67
70
|
}
|
|
68
71
|
// Always clean fallback
|
|
69
72
|
this.fallbackStorage.delete(key);
|
|
@@ -86,10 +89,9 @@ export class StorageManager {
|
|
|
86
89
|
}
|
|
87
90
|
keysToRemove.forEach((key) => this.storage.removeItem(key));
|
|
88
91
|
this.fallbackStorage.clear();
|
|
89
|
-
debugLog.debug('StorageManager', 'Cleared storage', { itemsRemoved: keysToRemove.length });
|
|
90
92
|
}
|
|
91
93
|
catch (error) {
|
|
92
|
-
|
|
94
|
+
log('error', 'Failed to clear storage', { error });
|
|
93
95
|
this.fallbackStorage.clear();
|
|
94
96
|
}
|
|
95
97
|
}
|
|
@@ -107,14 +109,14 @@ export class StorageManager {
|
|
|
107
109
|
return this.hasQuotaExceededError;
|
|
108
110
|
}
|
|
109
111
|
/**
|
|
110
|
-
* Initialize localStorage with feature detection
|
|
112
|
+
* Initialize storage (localStorage or sessionStorage) with feature detection
|
|
111
113
|
*/
|
|
112
|
-
initializeStorage() {
|
|
114
|
+
initializeStorage(type) {
|
|
113
115
|
if (typeof window === 'undefined') {
|
|
114
116
|
return null;
|
|
115
117
|
}
|
|
116
118
|
try {
|
|
117
|
-
const storage = window.localStorage;
|
|
119
|
+
const storage = type === 'localStorage' ? window.localStorage : window.sessionStorage;
|
|
118
120
|
const testKey = '__tracelog_test__';
|
|
119
121
|
storage.setItem(testKey, 'test');
|
|
120
122
|
storage.removeItem(testKey);
|
|
@@ -124,4 +126,56 @@ export class StorageManager {
|
|
|
124
126
|
return null;
|
|
125
127
|
}
|
|
126
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Retrieves an item from sessionStorage
|
|
131
|
+
*/
|
|
132
|
+
getSessionItem(key) {
|
|
133
|
+
try {
|
|
134
|
+
if (this.sessionStorageRef) {
|
|
135
|
+
return this.sessionStorageRef.getItem(key);
|
|
136
|
+
}
|
|
137
|
+
return this.fallbackSessionStorage.get(key) ?? null;
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// Silent fallback - user already warned in constructor
|
|
141
|
+
return this.fallbackSessionStorage.get(key) ?? null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Stores an item in sessionStorage
|
|
146
|
+
*/
|
|
147
|
+
setSessionItem(key, value) {
|
|
148
|
+
try {
|
|
149
|
+
if (this.sessionStorageRef) {
|
|
150
|
+
this.sessionStorageRef.setItem(key, value);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
if (error instanceof DOMException && error.name === 'QuotaExceededError') {
|
|
156
|
+
log('error', 'sessionStorage quota exceeded - data will not persist', {
|
|
157
|
+
error,
|
|
158
|
+
data: { key, valueSize: value.length },
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
// Else: Silent fallback - user already warned in constructor
|
|
162
|
+
}
|
|
163
|
+
// Always update fallback for consistency
|
|
164
|
+
this.fallbackSessionStorage.set(key, value);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Removes an item from sessionStorage
|
|
168
|
+
*/
|
|
169
|
+
removeSessionItem(key) {
|
|
170
|
+
try {
|
|
171
|
+
if (this.sessionStorageRef) {
|
|
172
|
+
this.sessionStorageRef.removeItem(key);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
// Silent - not critical
|
|
177
|
+
}
|
|
178
|
+
// Always clean fallback
|
|
179
|
+
this.fallbackSessionStorage.delete(key);
|
|
180
|
+
}
|
|
127
181
|
}
|
|
@@ -12,5 +12,5 @@ export declare class UserManager {
|
|
|
12
12
|
* @param projectId - Project identifier for namespacing
|
|
13
13
|
* @returns Persistent unique user ID
|
|
14
14
|
*/
|
|
15
|
-
static getId(storageManager: StorageManager
|
|
15
|
+
static getId(storageManager: StorageManager): string;
|
|
16
16
|
}
|
|
@@ -13,8 +13,8 @@ export class UserManager {
|
|
|
13
13
|
* @param projectId - Project identifier for namespacing
|
|
14
14
|
* @returns Persistent unique user ID
|
|
15
15
|
*/
|
|
16
|
-
static getId(storageManager
|
|
17
|
-
const storageKey = USER_ID_KEY
|
|
16
|
+
static getId(storageManager) {
|
|
17
|
+
const storageKey = USER_ID_KEY;
|
|
18
18
|
const storedUserId = storageManager.getItem(storageKey);
|
|
19
19
|
if (storedUserId) {
|
|
20
20
|
return storedUserId;
|
package/dist/esm/public-api.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export * from './app.constants';
|
|
2
|
-
export * from './
|
|
2
|
+
export * from './types';
|
|
3
3
|
export declare const tracelog: {
|
|
4
|
-
init: (
|
|
5
|
-
event: (name: string, metadata?: Record<string, import("./
|
|
4
|
+
init: (config: import("./types").Config) => Promise<void>;
|
|
5
|
+
event: (name: string, metadata?: Record<string, import("./types").MetadataType> | Record<string, import("./types").MetadataType>[]) => void;
|
|
6
6
|
on: <K extends keyof import("./types").EmitterMap>(event: K, callback: import("./types").EmitterCallback<import("./types").EmitterMap[K]>) => void;
|
|
7
7
|
off: <K extends keyof import("./types").EmitterMap>(event: K, callback: import("./types").EmitterCallback<import("./types").EmitterMap[K]>) => void;
|
|
8
8
|
isInitialized: () => boolean;
|
package/dist/esm/public-api.js
CHANGED
|
@@ -2,7 +2,7 @@ import { init, event, on, off, isInitialized, destroy } from './api';
|
|
|
2
2
|
// Constants
|
|
3
3
|
export * from './app.constants';
|
|
4
4
|
// Types
|
|
5
|
-
export * from './
|
|
5
|
+
export * from './types';
|
|
6
6
|
// TraceLog namespace containing all API methods
|
|
7
7
|
export const tracelog = {
|
|
8
8
|
init,
|
|
@@ -16,6 +16,7 @@ export declare class TestBridge extends App implements TraceLogTestBridge {
|
|
|
16
16
|
private _isInitializing;
|
|
17
17
|
private _isDestroying;
|
|
18
18
|
constructor(isInitializing: boolean, isDestroying: boolean);
|
|
19
|
+
init(config: any): Promise<void>;
|
|
19
20
|
isInitializing(): boolean;
|
|
20
21
|
sendCustomEvent(name: string, data?: Record<string, unknown> | Record<string, unknown>[]): void;
|
|
21
22
|
getSessionData(): Record<string, unknown> | null;
|
package/dist/esm/test-bridge.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { App } from './app';
|
|
2
|
+
import { __setAppInstance } from './api';
|
|
3
|
+
import { STORAGE_BASE_KEY } from './constants';
|
|
2
4
|
/**
|
|
3
5
|
* Test bridge for E2E testing
|
|
4
6
|
*/
|
|
@@ -9,6 +11,34 @@ export class TestBridge extends App {
|
|
|
9
11
|
this._isInitializing = isInitializing;
|
|
10
12
|
this._isDestroying = isDestroying;
|
|
11
13
|
}
|
|
14
|
+
async init(config) {
|
|
15
|
+
// Guard: TestBridge should only be used in development
|
|
16
|
+
if (process.env.NODE_ENV !== 'dev') {
|
|
17
|
+
throw new Error('[TraceLog] TestBridge is only available in development mode');
|
|
18
|
+
}
|
|
19
|
+
// First sync with window.tracelog BEFORE initializing
|
|
20
|
+
// This ensures both APIs point to the same instance from the start
|
|
21
|
+
if (!__setAppInstance) {
|
|
22
|
+
throw new Error('[TraceLog] __setAppInstance is not available (production build?)');
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
__setAppInstance(this);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// If __setAppInstance fails (e.g., already initialized), throw clear error
|
|
29
|
+
throw new Error('[TraceLog] TestBridge cannot sync with existing tracelog instance. Call destroy() first.');
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
await super.init(config);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
// If init fails, clear the sync
|
|
36
|
+
if (__setAppInstance) {
|
|
37
|
+
__setAppInstance(null);
|
|
38
|
+
}
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
12
42
|
isInitializing() {
|
|
13
43
|
return this._isInitializing;
|
|
14
44
|
}
|
|
@@ -43,7 +73,8 @@ export class TestBridge extends App {
|
|
|
43
73
|
if (!storageManager) {
|
|
44
74
|
throw new Error('Storage manager not available');
|
|
45
75
|
}
|
|
46
|
-
const
|
|
76
|
+
const config = this.get('config');
|
|
77
|
+
const projectId = config?.integrations?.tracelog?.projectId ?? config?.integrations?.custom?.apiUrl ?? 'test';
|
|
47
78
|
const userId = this.get('userId');
|
|
48
79
|
const sessionId = this.get('sessionId');
|
|
49
80
|
if (!projectId || !userId) {
|
|
@@ -58,7 +89,7 @@ export class TestBridge extends App {
|
|
|
58
89
|
timestamp: Date.now(),
|
|
59
90
|
};
|
|
60
91
|
// Store in the same format as SenderManager.persistEvents()
|
|
61
|
-
const storageKey =
|
|
92
|
+
const storageKey = `${STORAGE_BASE_KEY}:${projectId}:queue:${userId}`;
|
|
62
93
|
storageManager.setItem(storageKey, JSON.stringify(persistedData));
|
|
63
94
|
}
|
|
64
95
|
get(key) {
|
|
@@ -103,6 +134,10 @@ export class TestBridge extends App {
|
|
|
103
134
|
this._isDestroying = true;
|
|
104
135
|
try {
|
|
105
136
|
await super.destroy(force);
|
|
137
|
+
// Clear window.tracelog API reference (only in dev mode)
|
|
138
|
+
if (__setAppInstance) {
|
|
139
|
+
__setAppInstance(null);
|
|
140
|
+
}
|
|
106
141
|
}
|
|
107
142
|
finally {
|
|
108
143
|
this._isDestroying = false;
|