@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,8 +1,7 @@
|
|
|
1
1
|
import { StateManager } from '../managers/state.manager';
|
|
2
2
|
import { EventType } from '../types';
|
|
3
|
-
import { LONG_TASK_THROTTLE_MS, PRECISION_TWO_DECIMALS } from '../constants';
|
|
4
|
-
import {
|
|
5
|
-
import { debugLog } from '../utils/logging';
|
|
3
|
+
import { LONG_TASK_THROTTLE_MS, PRECISION_TWO_DECIMALS, WEB_VITALS_THRESHOLDS } from '../constants';
|
|
4
|
+
import { log } from '@/utils';
|
|
6
5
|
export class PerformanceHandler extends StateManager {
|
|
7
6
|
constructor(eventManager) {
|
|
8
7
|
super();
|
|
@@ -22,10 +21,7 @@ export class PerformanceHandler extends StateManager {
|
|
|
22
21
|
obs.disconnect();
|
|
23
22
|
}
|
|
24
23
|
catch (error) {
|
|
25
|
-
|
|
26
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
27
|
-
observerIndex: index,
|
|
28
|
-
});
|
|
24
|
+
log('warn', 'Failed to disconnect performance observer', { error, data: { observerIndex: index } });
|
|
29
25
|
}
|
|
30
26
|
});
|
|
31
27
|
this.observers.length = 0;
|
|
@@ -98,9 +94,7 @@ export class PerformanceHandler extends StateManager {
|
|
|
98
94
|
onINP(report('INP'));
|
|
99
95
|
}
|
|
100
96
|
catch (error) {
|
|
101
|
-
|
|
102
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
103
|
-
});
|
|
97
|
+
log('warn', 'Failed to load web-vitals library, using fallback', { error });
|
|
104
98
|
this.observeWebVitalsFallback();
|
|
105
99
|
}
|
|
106
100
|
}
|
|
@@ -121,9 +115,7 @@ export class PerformanceHandler extends StateManager {
|
|
|
121
115
|
}
|
|
122
116
|
}
|
|
123
117
|
catch (error) {
|
|
124
|
-
|
|
125
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
126
|
-
});
|
|
118
|
+
log('warn', 'Failed to report TTFB', { error });
|
|
127
119
|
}
|
|
128
120
|
}
|
|
129
121
|
observeLongTasks() {
|
|
@@ -165,7 +157,7 @@ export class PerformanceHandler extends StateManager {
|
|
|
165
157
|
}
|
|
166
158
|
trackWebVital(type, value) {
|
|
167
159
|
if (!Number.isFinite(value)) {
|
|
168
|
-
|
|
160
|
+
log('warn', 'Invalid web vital value', { data: { type, value } });
|
|
169
161
|
return;
|
|
170
162
|
}
|
|
171
163
|
this.eventManager.track({
|
|
@@ -188,9 +180,7 @@ export class PerformanceHandler extends StateManager {
|
|
|
188
180
|
return `${timestamp.toFixed(2)}_${window.location.pathname}_${random}`;
|
|
189
181
|
}
|
|
190
182
|
catch (error) {
|
|
191
|
-
|
|
192
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
193
|
-
});
|
|
183
|
+
log('warn', 'Failed to get navigation ID', { error });
|
|
194
184
|
return null;
|
|
195
185
|
}
|
|
196
186
|
}
|
|
@@ -210,9 +200,9 @@ export class PerformanceHandler extends StateManager {
|
|
|
210
200
|
cb(list, observer);
|
|
211
201
|
}
|
|
212
202
|
catch (callbackError) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
203
|
+
log('warn', 'Observer callback failed', {
|
|
204
|
+
error: callbackError,
|
|
205
|
+
data: { type },
|
|
216
206
|
});
|
|
217
207
|
}
|
|
218
208
|
if (once) {
|
|
@@ -231,25 +221,20 @@ export class PerformanceHandler extends StateManager {
|
|
|
231
221
|
return true;
|
|
232
222
|
}
|
|
233
223
|
catch (error) {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
224
|
+
log('warn', 'Failed to create performance observer', {
|
|
225
|
+
error,
|
|
226
|
+
data: { type },
|
|
237
227
|
});
|
|
238
228
|
return false;
|
|
239
229
|
}
|
|
240
230
|
}
|
|
241
231
|
shouldSendVital(type, value) {
|
|
242
232
|
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
243
|
-
|
|
233
|
+
log('warn', 'Invalid web vital value', { data: { type, value } });
|
|
244
234
|
return false;
|
|
245
235
|
}
|
|
246
236
|
const threshold = this.vitalThresholds[type];
|
|
247
237
|
if (typeof threshold === 'number' && value <= threshold) {
|
|
248
|
-
debugLog.debug('PerformanceHandler', 'Web vital below threshold, skipping', {
|
|
249
|
-
type,
|
|
250
|
-
value,
|
|
251
|
-
threshold,
|
|
252
|
-
});
|
|
253
238
|
return false;
|
|
254
239
|
}
|
|
255
240
|
return true;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { MAX_SCROLL_EVENTS_PER_SESSION, MIN_SCROLL_DEPTH_CHANGE, SCROLL_DEBOUNCE_TIME_MS, SCROLL_MIN_EVENT_INTERVAL_MS, SIGNIFICANT_SCROLL_DELTA, } from '../constants';
|
|
2
2
|
import { EventType, ScrollDirection } from '../types';
|
|
3
3
|
import { StateManager } from '../managers/state.manager';
|
|
4
|
-
import {
|
|
4
|
+
import { log } from '../utils';
|
|
5
5
|
export class ScrollHandler extends StateManager {
|
|
6
6
|
constructor(eventManager) {
|
|
7
7
|
super();
|
|
@@ -141,8 +141,8 @@ export class ScrollHandler extends StateManager {
|
|
|
141
141
|
return;
|
|
142
142
|
}
|
|
143
143
|
this.limitWarningLogged = true;
|
|
144
|
-
|
|
145
|
-
limit: this.maxEventsPerSession,
|
|
144
|
+
log('warn', 'Max scroll events per session reached', {
|
|
145
|
+
data: { limit: this.maxEventsPerSession },
|
|
146
146
|
});
|
|
147
147
|
}
|
|
148
148
|
applyConfigOverrides() {
|
|
@@ -211,9 +211,10 @@ export class ScrollHandler extends StateManager {
|
|
|
211
211
|
return document.querySelector(selector);
|
|
212
212
|
}
|
|
213
213
|
catch (error) {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
214
|
+
log('warn', 'Invalid CSS selector', {
|
|
215
|
+
error,
|
|
216
|
+
data: { selector },
|
|
217
|
+
showToClient: true,
|
|
217
218
|
});
|
|
218
219
|
return null;
|
|
219
220
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SessionManager } from '../managers/session.manager';
|
|
2
2
|
import { StateManager } from '../managers/state.manager';
|
|
3
|
-
import {
|
|
3
|
+
import { log } from '../utils';
|
|
4
4
|
export class SessionHandler extends StateManager {
|
|
5
5
|
constructor(storageManager, eventManager) {
|
|
6
6
|
super();
|
|
@@ -14,16 +14,19 @@ export class SessionHandler extends StateManager {
|
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
16
|
if (this.destroyed) {
|
|
17
|
-
|
|
17
|
+
log('warn', 'Cannot start tracking on destroyed handler');
|
|
18
18
|
return;
|
|
19
19
|
}
|
|
20
|
-
const
|
|
20
|
+
const config = this.get('config');
|
|
21
|
+
const projectId = config?.integrations?.tracelog?.projectId ?? config?.integrations?.custom?.apiUrl ?? 'default';
|
|
21
22
|
if (!projectId) {
|
|
22
23
|
throw new Error('Cannot start session tracking: config not available');
|
|
23
24
|
}
|
|
24
25
|
try {
|
|
25
26
|
this.sessionManager = new SessionManager(this.storageManager, this.eventManager, projectId);
|
|
26
27
|
await this.sessionManager.startTracking();
|
|
28
|
+
// Flush any events that were buffered during initialization
|
|
29
|
+
this.eventManager.flushPendingEvents();
|
|
27
30
|
}
|
|
28
31
|
catch (error) {
|
|
29
32
|
if (this.sessionManager) {
|
|
@@ -35,9 +38,7 @@ export class SessionHandler extends StateManager {
|
|
|
35
38
|
}
|
|
36
39
|
this.sessionManager = null;
|
|
37
40
|
}
|
|
38
|
-
|
|
39
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
40
|
-
});
|
|
41
|
+
log('error', 'Failed to start session tracking', { error });
|
|
41
42
|
throw error;
|
|
42
43
|
}
|
|
43
44
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { log } from '../utils';
|
|
2
2
|
import { StateManager } from '../managers/state.manager';
|
|
3
3
|
export class GoogleAnalyticsIntegration extends StateManager {
|
|
4
4
|
constructor() {
|
|
@@ -24,9 +24,7 @@ export class GoogleAnalyticsIntegration extends StateManager {
|
|
|
24
24
|
this.isInitialized = true;
|
|
25
25
|
}
|
|
26
26
|
catch (error) {
|
|
27
|
-
|
|
28
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
29
|
-
});
|
|
27
|
+
log('error', 'Google Analytics initialization failed', { error });
|
|
30
28
|
}
|
|
31
29
|
}
|
|
32
30
|
trackEvent(eventName, metadata) {
|
|
@@ -38,9 +36,7 @@ export class GoogleAnalyticsIntegration extends StateManager {
|
|
|
38
36
|
window.gtag('event', eventName, normalizedMetadata);
|
|
39
37
|
}
|
|
40
38
|
catch (error) {
|
|
41
|
-
|
|
42
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
43
|
-
});
|
|
39
|
+
log('error', 'Google Analytics event tracking failed', { error });
|
|
44
40
|
}
|
|
45
41
|
}
|
|
46
42
|
cleanup() {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { log } from '../utils';
|
|
2
2
|
export class ActivityListenerManager {
|
|
3
3
|
constructor(onActivity) {
|
|
4
4
|
this.options = { passive: true };
|
|
@@ -11,7 +11,7 @@ export class ActivityListenerManager {
|
|
|
11
11
|
window.addEventListener('focus', this.onActivity, this.options);
|
|
12
12
|
}
|
|
13
13
|
catch (error) {
|
|
14
|
-
|
|
14
|
+
log('error', 'Failed to setup activity listeners', { error });
|
|
15
15
|
throw error;
|
|
16
16
|
}
|
|
17
17
|
}
|
|
@@ -22,7 +22,7 @@ export class ActivityListenerManager {
|
|
|
22
22
|
window.removeEventListener('focus', this.onActivity);
|
|
23
23
|
}
|
|
24
24
|
catch (error) {
|
|
25
|
-
|
|
25
|
+
log('warn', 'Error during activity listeners cleanup', { error });
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { log } from '../utils';
|
|
2
2
|
/**
|
|
3
3
|
* Base class for input listener managers to reduce code duplication
|
|
4
4
|
*/
|
|
@@ -14,7 +14,7 @@ class BaseInputListenerManager {
|
|
|
14
14
|
});
|
|
15
15
|
}
|
|
16
16
|
catch (error) {
|
|
17
|
-
|
|
17
|
+
log('error', `Failed to setup ${this.logPrefix.toLowerCase()} listeners`, { error });
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
cleanup() {
|
|
@@ -24,7 +24,7 @@ class BaseInputListenerManager {
|
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
26
|
catch (error) {
|
|
27
|
-
|
|
27
|
+
log('warn', `Error during ${this.logPrefix.toLowerCase()} listeners cleanup`, { error });
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { log } from '../utils';
|
|
2
2
|
export class TouchListenerManager {
|
|
3
3
|
constructor(onActivity) {
|
|
4
4
|
this.options = { passive: true };
|
|
@@ -12,7 +12,7 @@ export class TouchListenerManager {
|
|
|
12
12
|
window.addEventListener('orientationchange', this.onActivity, this.options);
|
|
13
13
|
}
|
|
14
14
|
catch (error) {
|
|
15
|
-
|
|
15
|
+
log('error', 'Failed to setup touch listeners', { error });
|
|
16
16
|
throw error;
|
|
17
17
|
}
|
|
18
18
|
}
|
|
@@ -24,7 +24,7 @@ export class TouchListenerManager {
|
|
|
24
24
|
window.removeEventListener('orientationchange', this.onActivity);
|
|
25
25
|
}
|
|
26
26
|
catch (error) {
|
|
27
|
-
|
|
27
|
+
log('warn', 'Error during touch listeners cleanup', { error });
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { log } from '../utils';
|
|
2
2
|
export class UnloadListenerManager {
|
|
3
3
|
constructor(onInactivity) {
|
|
4
4
|
this.options = { passive: true };
|
|
@@ -10,7 +10,7 @@ export class UnloadListenerManager {
|
|
|
10
10
|
window.addEventListener('pagehide', this.onInactivity, this.options);
|
|
11
11
|
}
|
|
12
12
|
catch (error) {
|
|
13
|
-
|
|
13
|
+
log('error', 'Failed to setup unload listeners', { error });
|
|
14
14
|
throw error;
|
|
15
15
|
}
|
|
16
16
|
}
|
|
@@ -20,7 +20,7 @@ export class UnloadListenerManager {
|
|
|
20
20
|
window.removeEventListener('pagehide', this.onInactivity);
|
|
21
21
|
}
|
|
22
22
|
catch (error) {
|
|
23
|
-
|
|
23
|
+
log('warn', 'Error during unload listeners cleanup', { error });
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { log } from '../utils';
|
|
2
2
|
export class VisibilityListenerManager {
|
|
3
3
|
constructor(onActivity, onVisibilityChange) {
|
|
4
4
|
this.options = { passive: true };
|
|
@@ -21,7 +21,7 @@ export class VisibilityListenerManager {
|
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
catch (error) {
|
|
24
|
-
|
|
24
|
+
log('error', 'Failed to setup visibility listeners', { error });
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
cleanup() {
|
|
@@ -37,7 +37,7 @@ export class VisibilityListenerManager {
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
catch (error) {
|
|
40
|
-
|
|
40
|
+
log('warn', 'Error during visibility listeners cleanup', { error });
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
}
|
|
@@ -8,6 +8,7 @@ export declare class EventManager extends StateManager {
|
|
|
8
8
|
private readonly dataSender;
|
|
9
9
|
private readonly emitter;
|
|
10
10
|
private eventsQueue;
|
|
11
|
+
private pendingEventsBuffer;
|
|
11
12
|
private lastEventFingerprint;
|
|
12
13
|
private lastEventTime;
|
|
13
14
|
private sendIntervalId;
|
|
@@ -18,12 +19,12 @@ export declare class EventManager extends StateManager {
|
|
|
18
19
|
flushImmediately(): Promise<boolean>;
|
|
19
20
|
flushImmediatelySync(): boolean;
|
|
20
21
|
getQueueLength(): number;
|
|
22
|
+
flushPendingEvents(): void;
|
|
21
23
|
private clearSendInterval;
|
|
22
24
|
private flushEvents;
|
|
23
25
|
private sendEventsQueue;
|
|
24
26
|
private buildEventsPayload;
|
|
25
27
|
private buildEventPayload;
|
|
26
|
-
private isEventExcluded;
|
|
27
28
|
private isDuplicateEvent;
|
|
28
29
|
private createEventFingerprint;
|
|
29
30
|
private createEventSignature;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { EVENT_SENT_INTERVAL_MS, MAX_EVENTS_QUEUE_LENGTH, DUPLICATE_EVENT_THRESHOLD_MS, } from '../constants/config.constants';
|
|
2
|
-
import { EmitterEvent, EventType } from '../types';
|
|
3
|
-
import { getUTMParameters,
|
|
2
|
+
import { EmitterEvent, EventType, Mode } from '../types';
|
|
3
|
+
import { getUTMParameters, log, generateEventId } from '../utils';
|
|
4
4
|
import { SenderManager } from './sender.manager';
|
|
5
5
|
import { StateManager } from './state.manager';
|
|
6
6
|
export class EventManager extends StateManager {
|
|
7
7
|
constructor(storeManager, googleAnalytics = null, emitter = null) {
|
|
8
8
|
super();
|
|
9
9
|
this.eventsQueue = [];
|
|
10
|
+
this.pendingEventsBuffer = [];
|
|
10
11
|
this.lastEventFingerprint = null;
|
|
11
12
|
this.lastEventTime = 0;
|
|
12
13
|
this.sendIntervalId = null;
|
|
@@ -18,7 +19,7 @@ export class EventManager extends StateManager {
|
|
|
18
19
|
await this.dataSender.recoverPersistedEvents({
|
|
19
20
|
onSuccess: (_eventCount, recoveredEvents, body) => {
|
|
20
21
|
if (recoveredEvents && recoveredEvents.length > 0) {
|
|
21
|
-
const eventIds = recoveredEvents.map((e) => e.
|
|
22
|
+
const eventIds = recoveredEvents.map((e) => e.id);
|
|
22
23
|
this.removeProcessedEvents(eventIds);
|
|
23
24
|
if (body) {
|
|
24
25
|
this.emitEventsQueue(body);
|
|
@@ -26,13 +27,27 @@ export class EventManager extends StateManager {
|
|
|
26
27
|
}
|
|
27
28
|
},
|
|
28
29
|
onFailure: async () => {
|
|
29
|
-
|
|
30
|
+
log('warn', 'Failed to recover persisted events');
|
|
30
31
|
},
|
|
31
32
|
});
|
|
32
33
|
}
|
|
33
34
|
track({ type, page_url, from_page_url, scroll_data, click_data, custom_event, web_vitals, error_data, session_end_reason, }) {
|
|
34
35
|
if (!type) {
|
|
35
|
-
|
|
36
|
+
log('warn', 'Event type is required');
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (!this.get('sessionId')) {
|
|
40
|
+
this.pendingEventsBuffer.push({
|
|
41
|
+
type,
|
|
42
|
+
page_url,
|
|
43
|
+
from_page_url,
|
|
44
|
+
scroll_data,
|
|
45
|
+
click_data,
|
|
46
|
+
custom_event,
|
|
47
|
+
web_vitals,
|
|
48
|
+
error_data,
|
|
49
|
+
session_end_reason,
|
|
50
|
+
});
|
|
36
51
|
return;
|
|
37
52
|
}
|
|
38
53
|
const eventType = type;
|
|
@@ -51,21 +66,18 @@ export class EventManager extends StateManager {
|
|
|
51
66
|
error_data,
|
|
52
67
|
session_end_reason,
|
|
53
68
|
});
|
|
54
|
-
if (this.isEventExcluded(payload)) {
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
69
|
if (!isCriticalEvent && !this.shouldSample()) {
|
|
58
70
|
return;
|
|
59
71
|
}
|
|
60
72
|
if (isSessionStart) {
|
|
61
73
|
const currentSessionId = this.get('sessionId');
|
|
62
74
|
if (!currentSessionId) {
|
|
63
|
-
|
|
75
|
+
log('warn', 'Session start event ignored: missing sessionId');
|
|
64
76
|
return;
|
|
65
77
|
}
|
|
66
78
|
if (this.get('hasStartSession')) {
|
|
67
|
-
|
|
68
|
-
sessionId: currentSessionId,
|
|
79
|
+
log('warn', 'Duplicate session_start detected', {
|
|
80
|
+
data: { sessionId: currentSessionId },
|
|
69
81
|
});
|
|
70
82
|
return;
|
|
71
83
|
}
|
|
@@ -74,6 +86,14 @@ export class EventManager extends StateManager {
|
|
|
74
86
|
if (this.isDuplicateEvent(payload)) {
|
|
75
87
|
return;
|
|
76
88
|
}
|
|
89
|
+
if (this.get('mode') === Mode.QA && eventType === EventType.CUSTOM && custom_event) {
|
|
90
|
+
console.log('[TraceLog] Event', {
|
|
91
|
+
name: custom_event.name,
|
|
92
|
+
...(custom_event.metadata && { metadata: custom_event.metadata }),
|
|
93
|
+
});
|
|
94
|
+
this.emitEvent(payload);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
77
97
|
this.addToQueue(payload);
|
|
78
98
|
}
|
|
79
99
|
stop() {
|
|
@@ -82,6 +102,7 @@ export class EventManager extends StateManager {
|
|
|
82
102
|
this.sendIntervalId = null;
|
|
83
103
|
}
|
|
84
104
|
this.eventsQueue = [];
|
|
105
|
+
this.pendingEventsBuffer = [];
|
|
85
106
|
this.lastEventFingerprint = null;
|
|
86
107
|
this.lastEventTime = 0;
|
|
87
108
|
this.dataSender.stop();
|
|
@@ -95,6 +116,20 @@ export class EventManager extends StateManager {
|
|
|
95
116
|
getQueueLength() {
|
|
96
117
|
return this.eventsQueue.length;
|
|
97
118
|
}
|
|
119
|
+
flushPendingEvents() {
|
|
120
|
+
if (this.pendingEventsBuffer.length === 0) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (!this.get('sessionId')) {
|
|
124
|
+
log('warn', 'Cannot flush pending events: session not initialized');
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const bufferedEvents = [...this.pendingEventsBuffer];
|
|
128
|
+
this.pendingEventsBuffer = [];
|
|
129
|
+
bufferedEvents.forEach((event) => {
|
|
130
|
+
this.track(event);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
98
133
|
clearSendInterval() {
|
|
99
134
|
if (this.sendIntervalId) {
|
|
100
135
|
clearInterval(this.sendIntervalId);
|
|
@@ -107,7 +142,7 @@ export class EventManager extends StateManager {
|
|
|
107
142
|
}
|
|
108
143
|
const body = this.buildEventsPayload();
|
|
109
144
|
const eventsToSend = [...this.eventsQueue];
|
|
110
|
-
const eventIds = eventsToSend.map((e) =>
|
|
145
|
+
const eventIds = eventsToSend.map((e) => e.id);
|
|
111
146
|
if (isSync) {
|
|
112
147
|
const success = this.dataSender.sendEventsQueueSync(body);
|
|
113
148
|
if (success) {
|
|
@@ -125,8 +160,8 @@ export class EventManager extends StateManager {
|
|
|
125
160
|
this.emitEventsQueue(body);
|
|
126
161
|
},
|
|
127
162
|
onFailure: () => {
|
|
128
|
-
|
|
129
|
-
eventCount: eventsToSend.length,
|
|
163
|
+
log('warn', 'Async flush failed', {
|
|
164
|
+
data: { eventCount: eventsToSend.length },
|
|
130
165
|
});
|
|
131
166
|
},
|
|
132
167
|
});
|
|
@@ -138,15 +173,15 @@ export class EventManager extends StateManager {
|
|
|
138
173
|
}
|
|
139
174
|
const body = this.buildEventsPayload();
|
|
140
175
|
const eventsToSend = [...this.eventsQueue];
|
|
141
|
-
const eventIds = eventsToSend.map((e) =>
|
|
176
|
+
const eventIds = eventsToSend.map((e) => e.id);
|
|
142
177
|
await this.dataSender.sendEventsQueue(body, {
|
|
143
178
|
onSuccess: () => {
|
|
144
179
|
this.removeProcessedEvents(eventIds);
|
|
145
180
|
this.emitEventsQueue(body);
|
|
146
181
|
},
|
|
147
182
|
onFailure: async () => {
|
|
148
|
-
|
|
149
|
-
eventCount: eventsToSend.length,
|
|
183
|
+
log('warn', 'Events send failed, keeping in queue', {
|
|
184
|
+
data: { eventCount: eventsToSend.length },
|
|
150
185
|
});
|
|
151
186
|
},
|
|
152
187
|
});
|
|
@@ -177,6 +212,7 @@ export class EventManager extends StateManager {
|
|
|
177
212
|
const isSessionStart = data.type === EventType.SESSION_START;
|
|
178
213
|
const currentPageUrl = data.page_url ?? this.get('pageUrl');
|
|
179
214
|
const payload = {
|
|
215
|
+
id: generateEventId(),
|
|
180
216
|
type: data.type,
|
|
181
217
|
page_url: currentPageUrl,
|
|
182
218
|
timestamp: Date.now(),
|
|
@@ -190,23 +226,8 @@ export class EventManager extends StateManager {
|
|
|
190
226
|
...(data.session_end_reason && { session_end_reason: data.session_end_reason }),
|
|
191
227
|
...(isSessionStart && getUTMParameters() && { utm: getUTMParameters() }),
|
|
192
228
|
};
|
|
193
|
-
const projectTags = this.get('config')?.tags;
|
|
194
|
-
if (projectTags?.length) {
|
|
195
|
-
payload.tags = projectTags;
|
|
196
|
-
}
|
|
197
229
|
return payload;
|
|
198
230
|
}
|
|
199
|
-
isEventExcluded(event) {
|
|
200
|
-
const config = this.get('config');
|
|
201
|
-
const isRouteExcluded = isUrlPathExcluded(event.page_url, config?.excludedUrlPaths ?? []);
|
|
202
|
-
const hasStartSession = this.get('hasStartSession');
|
|
203
|
-
const isSessionEndEvent = event.type === EventType.SESSION_END;
|
|
204
|
-
const isSessionStartEvent = event.type === EventType.SESSION_START;
|
|
205
|
-
if (isRouteExcluded && !isSessionStartEvent && !(isSessionEndEvent && hasStartSession)) {
|
|
206
|
-
return true;
|
|
207
|
-
}
|
|
208
|
-
return config?.ipExcluded === true;
|
|
209
|
-
}
|
|
210
231
|
isDuplicateEvent(event) {
|
|
211
232
|
const now = Date.now();
|
|
212
233
|
const fingerprint = this.createEventFingerprint(event);
|
|
@@ -247,11 +268,13 @@ export class EventManager extends StateManager {
|
|
|
247
268
|
if (this.eventsQueue.length > MAX_EVENTS_QUEUE_LENGTH) {
|
|
248
269
|
const nonCriticalIndex = this.eventsQueue.findIndex((e) => e.type !== EventType.SESSION_START && e.type !== EventType.SESSION_END);
|
|
249
270
|
const removedEvent = nonCriticalIndex >= 0 ? this.eventsQueue.splice(nonCriticalIndex, 1)[0] : this.eventsQueue.shift();
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
271
|
+
log('warn', 'Event queue overflow, oldest non-critical event removed', {
|
|
272
|
+
data: {
|
|
273
|
+
maxLength: MAX_EVENTS_QUEUE_LENGTH,
|
|
274
|
+
currentLength: this.eventsQueue.length,
|
|
275
|
+
removedEventType: removedEvent?.type,
|
|
276
|
+
wasCritical: removedEvent?.type === EventType.SESSION_START || removedEvent?.type === EventType.SESSION_END,
|
|
277
|
+
},
|
|
255
278
|
});
|
|
256
279
|
}
|
|
257
280
|
if (!this.sendIntervalId) {
|
|
@@ -268,7 +291,7 @@ export class EventManager extends StateManager {
|
|
|
268
291
|
}
|
|
269
292
|
handleGoogleAnalyticsIntegration(event) {
|
|
270
293
|
if (this.googleAnalytics && event.type === EventType.CUSTOM && event.custom_event) {
|
|
271
|
-
if (this.get('
|
|
294
|
+
if (this.get('mode') === Mode.QA) {
|
|
272
295
|
return;
|
|
273
296
|
}
|
|
274
297
|
this.googleAnalytics.trackEvent(event.custom_event.name, event.custom_event.metadata ?? {});
|
|
@@ -281,8 +304,7 @@ export class EventManager extends StateManager {
|
|
|
281
304
|
removeProcessedEvents(eventIds) {
|
|
282
305
|
const eventIdSet = new Set(eventIds);
|
|
283
306
|
this.eventsQueue = this.eventsQueue.filter((event) => {
|
|
284
|
-
|
|
285
|
-
return !eventIdSet.has(eventId);
|
|
307
|
+
return !eventIdSet.has(event.id);
|
|
286
308
|
});
|
|
287
309
|
}
|
|
288
310
|
emitEvent(eventData) {
|