@tracelog/lib 0.0.1
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/LICENSE +21 -0
- package/README.md +217 -0
- package/dist/browser/tracelog.js +4040 -0
- package/dist/browser/web-vitals-CCnqwnC8.mjs +198 -0
- package/dist/cjs/api.d.ts +46 -0
- package/dist/cjs/api.js +224 -0
- package/dist/cjs/app.constants.d.ts +1 -0
- package/dist/cjs/app.constants.js +5 -0
- package/dist/cjs/app.d.ts +59 -0
- package/dist/cjs/app.js +272 -0
- package/dist/cjs/app.types.d.ts +6 -0
- package/dist/cjs/app.types.js +22 -0
- package/dist/cjs/constants/api.constants.d.ts +4 -0
- package/dist/cjs/constants/api.constants.js +18 -0
- package/dist/cjs/constants/browser.constants.d.ts +3 -0
- package/dist/cjs/constants/browser.constants.js +41 -0
- package/dist/cjs/constants/index.d.ts +8 -0
- package/dist/cjs/constants/index.js +24 -0
- package/dist/cjs/constants/initialization.constants.d.ts +40 -0
- package/dist/cjs/constants/initialization.constants.js +48 -0
- package/dist/cjs/constants/limits.constants.d.ts +25 -0
- package/dist/cjs/constants/limits.constants.js +40 -0
- package/dist/cjs/constants/security.constants.d.ts +1 -0
- package/dist/cjs/constants/security.constants.js +12 -0
- package/dist/cjs/constants/storage.constants.d.ts +9 -0
- package/dist/cjs/constants/storage.constants.js +22 -0
- package/dist/cjs/constants/timing.constants.d.ts +22 -0
- package/dist/cjs/constants/timing.constants.js +34 -0
- package/dist/cjs/constants/validation.constants.d.ts +13 -0
- package/dist/cjs/constants/validation.constants.js +31 -0
- package/dist/cjs/handlers/click.handler.d.ts +17 -0
- package/dist/cjs/handlers/click.handler.js +199 -0
- package/dist/cjs/handlers/error.handler.d.ts +15 -0
- package/dist/cjs/handlers/error.handler.js +97 -0
- package/dist/cjs/handlers/network.handler.d.ts +16 -0
- package/dist/cjs/handlers/network.handler.js +136 -0
- package/dist/cjs/handlers/page-view.handler.d.ts +15 -0
- package/dist/cjs/handlers/page-view.handler.js +83 -0
- package/dist/cjs/handlers/performance.handler.d.ts +19 -0
- package/dist/cjs/handlers/performance.handler.js +255 -0
- package/dist/cjs/handlers/scroll.handler.d.ts +16 -0
- package/dist/cjs/handlers/scroll.handler.js +138 -0
- package/dist/cjs/handlers/session.handler.d.ts +29 -0
- package/dist/cjs/handlers/session.handler.js +357 -0
- package/dist/cjs/integrations/google-analytics.integration.d.ts +18 -0
- package/dist/cjs/integrations/google-analytics.integration.js +159 -0
- package/dist/cjs/listeners/activity-listener-manager.d.ts +8 -0
- package/dist/cjs/listeners/activity-listener-manager.js +32 -0
- package/dist/cjs/listeners/index.d.ts +6 -0
- package/dist/cjs/listeners/index.js +14 -0
- package/dist/cjs/listeners/input-listener-managers.d.ts +15 -0
- package/dist/cjs/listeners/input-listener-managers.js +58 -0
- package/dist/cjs/listeners/listeners.types.d.ts +4 -0
- package/dist/cjs/listeners/listeners.types.js +2 -0
- package/dist/cjs/listeners/touch-listener-manager.d.ts +10 -0
- package/dist/cjs/listeners/touch-listener-manager.js +56 -0
- package/dist/cjs/listeners/unload-listener-manager.d.ts +8 -0
- package/dist/cjs/listeners/unload-listener-manager.js +30 -0
- package/dist/cjs/listeners/visibility-listener-manager.d.ts +12 -0
- package/dist/cjs/listeners/visibility-listener-manager.js +83 -0
- package/dist/cjs/managers/api.manager.d.ts +3 -0
- package/dist/cjs/managers/api.manager.js +14 -0
- package/dist/cjs/managers/config.manager.d.ts +7 -0
- package/dist/cjs/managers/config.manager.js +94 -0
- package/dist/cjs/managers/cross-tab-session.manager.d.ts +170 -0
- package/dist/cjs/managers/cross-tab-session.manager.js +730 -0
- package/dist/cjs/managers/event.manager.d.ts +61 -0
- package/dist/cjs/managers/event.manager.js +508 -0
- package/dist/cjs/managers/sampling.manager.d.ts +8 -0
- package/dist/cjs/managers/sampling.manager.js +53 -0
- package/dist/cjs/managers/sender.manager.d.ts +46 -0
- package/dist/cjs/managers/sender.manager.js +304 -0
- package/dist/cjs/managers/session-recovery.manager.d.ts +65 -0
- package/dist/cjs/managers/session-recovery.manager.js +237 -0
- package/dist/cjs/managers/session.manager.d.ts +72 -0
- package/dist/cjs/managers/session.manager.js +587 -0
- package/dist/cjs/managers/state.manager.d.ts +5 -0
- package/dist/cjs/managers/state.manager.js +23 -0
- package/dist/cjs/managers/storage.manager.d.ts +10 -0
- package/dist/cjs/managers/storage.manager.js +81 -0
- package/dist/cjs/managers/tags.manager.d.ts +12 -0
- package/dist/cjs/managers/tags.manager.js +289 -0
- package/dist/cjs/managers/user.manager.d.ts +7 -0
- package/dist/cjs/managers/user.manager.js +22 -0
- package/dist/cjs/public-api.d.ts +1 -0
- package/dist/cjs/public-api.js +37 -0
- package/dist/cjs/types/api.types.d.ts +21 -0
- package/dist/cjs/types/api.types.js +25 -0
- package/dist/cjs/types/common.types.d.ts +1 -0
- package/dist/cjs/types/common.types.js +2 -0
- package/dist/cjs/types/config.types.d.ts +104 -0
- package/dist/cjs/types/config.types.js +2 -0
- package/dist/cjs/types/device.types.d.ts +6 -0
- package/dist/cjs/types/device.types.js +10 -0
- package/dist/cjs/types/event.types.d.ts +104 -0
- package/dist/cjs/types/event.types.js +25 -0
- package/dist/cjs/types/index.d.ts +13 -0
- package/dist/cjs/types/index.js +29 -0
- package/dist/cjs/types/log.types.d.ts +4 -0
- package/dist/cjs/types/log.types.js +2 -0
- package/dist/cjs/types/mode.types.d.ts +7 -0
- package/dist/cjs/types/mode.types.js +11 -0
- package/dist/cjs/types/queue.types.d.ts +23 -0
- package/dist/cjs/types/queue.types.js +2 -0
- package/dist/cjs/types/session.types.d.ts +65 -0
- package/dist/cjs/types/session.types.js +2 -0
- package/dist/cjs/types/state.types.d.ts +12 -0
- package/dist/cjs/types/state.types.js +2 -0
- package/dist/cjs/types/tag.types.d.ts +43 -0
- package/dist/cjs/types/tag.types.js +31 -0
- package/dist/cjs/types/validation-error.types.d.ts +42 -0
- package/dist/cjs/types/validation-error.types.js +68 -0
- package/dist/cjs/types/web-vitals.types.d.ts +6 -0
- package/dist/cjs/types/web-vitals.types.js +2 -0
- package/dist/cjs/types/window.types.d.ts +17 -0
- package/dist/cjs/types/window.types.js +2 -0
- package/dist/cjs/utils/browser/device-detector.utils.d.ts +6 -0
- package/dist/cjs/utils/browser/device-detector.utils.js +71 -0
- package/dist/cjs/utils/browser/index.d.ts +2 -0
- package/dist/cjs/utils/browser/index.js +18 -0
- package/dist/cjs/utils/browser/utm-params.utils.d.ts +6 -0
- package/dist/cjs/utils/browser/utm-params.utils.js +37 -0
- package/dist/cjs/utils/data/index.d.ts +1 -0
- package/dist/cjs/utils/data/index.js +17 -0
- package/dist/cjs/utils/data/uuid.utils.d.ts +5 -0
- package/dist/cjs/utils/data/uuid.utils.js +18 -0
- package/dist/cjs/utils/index.d.ts +6 -0
- package/dist/cjs/utils/index.js +22 -0
- package/dist/cjs/utils/logging/debug-logger.utils.d.ts +56 -0
- package/dist/cjs/utils/logging/debug-logger.utils.js +139 -0
- package/dist/cjs/utils/logging/index.d.ts +1 -0
- package/dist/cjs/utils/logging/index.js +5 -0
- package/dist/cjs/utils/network/index.d.ts +1 -0
- package/dist/cjs/utils/network/index.js +17 -0
- package/dist/cjs/utils/network/url.utils.d.ts +20 -0
- package/dist/cjs/utils/network/url.utils.js +172 -0
- package/dist/cjs/utils/security/index.d.ts +1 -0
- package/dist/cjs/utils/security/index.js +17 -0
- package/dist/cjs/utils/security/sanitize.utils.d.ts +32 -0
- package/dist/cjs/utils/security/sanitize.utils.js +319 -0
- package/dist/cjs/utils/validations/config-validations.utils.d.ts +42 -0
- package/dist/cjs/utils/validations/config-validations.utils.js +297 -0
- package/dist/cjs/utils/validations/event-validations.utils.d.ts +12 -0
- package/dist/cjs/utils/validations/event-validations.utils.js +30 -0
- package/dist/cjs/utils/validations/index.d.ts +5 -0
- package/dist/cjs/utils/validations/index.js +21 -0
- package/dist/cjs/utils/validations/metadata-validations.utils.d.ts +22 -0
- package/dist/cjs/utils/validations/metadata-validations.utils.js +115 -0
- package/dist/cjs/utils/validations/type-guards.utils.d.ts +6 -0
- package/dist/cjs/utils/validations/type-guards.utils.js +31 -0
- package/dist/cjs/utils/validations/url-validations.utils.d.ts +15 -0
- package/dist/cjs/utils/validations/url-validations.utils.js +47 -0
- package/dist/esm/api.d.ts +46 -0
- package/dist/esm/api.js +183 -0
- package/dist/esm/app.constants.d.ts +1 -0
- package/dist/esm/app.constants.js +1 -0
- package/dist/esm/app.d.ts +59 -0
- package/dist/esm/app.js +268 -0
- package/dist/esm/app.types.d.ts +6 -0
- package/dist/esm/app.types.js +6 -0
- package/dist/esm/constants/api.constants.d.ts +4 -0
- package/dist/esm/constants/api.constants.js +14 -0
- package/dist/esm/constants/browser.constants.d.ts +3 -0
- package/dist/esm/constants/browser.constants.js +38 -0
- package/dist/esm/constants/index.d.ts +8 -0
- package/dist/esm/constants/index.js +8 -0
- package/dist/esm/constants/initialization.constants.d.ts +40 -0
- package/dist/esm/constants/initialization.constants.js +45 -0
- package/dist/esm/constants/limits.constants.d.ts +25 -0
- package/dist/esm/constants/limits.constants.js +37 -0
- package/dist/esm/constants/security.constants.d.ts +1 -0
- package/dist/esm/constants/security.constants.js +9 -0
- package/dist/esm/constants/storage.constants.d.ts +9 -0
- package/dist/esm/constants/storage.constants.js +11 -0
- package/dist/esm/constants/timing.constants.d.ts +22 -0
- package/dist/esm/constants/timing.constants.js +31 -0
- package/dist/esm/constants/validation.constants.d.ts +13 -0
- package/dist/esm/constants/validation.constants.js +28 -0
- package/dist/esm/handlers/click.handler.d.ts +17 -0
- package/dist/esm/handlers/click.handler.js +195 -0
- package/dist/esm/handlers/error.handler.d.ts +15 -0
- package/dist/esm/handlers/error.handler.js +93 -0
- package/dist/esm/handlers/network.handler.d.ts +16 -0
- package/dist/esm/handlers/network.handler.js +132 -0
- package/dist/esm/handlers/page-view.handler.d.ts +15 -0
- package/dist/esm/handlers/page-view.handler.js +79 -0
- package/dist/esm/handlers/performance.handler.d.ts +19 -0
- package/dist/esm/handlers/performance.handler.js +218 -0
- package/dist/esm/handlers/scroll.handler.d.ts +16 -0
- package/dist/esm/handlers/scroll.handler.js +134 -0
- package/dist/esm/handlers/session.handler.d.ts +29 -0
- package/dist/esm/handlers/session.handler.js +353 -0
- package/dist/esm/integrations/google-analytics.integration.d.ts +18 -0
- package/dist/esm/integrations/google-analytics.integration.js +155 -0
- package/dist/esm/listeners/activity-listener-manager.d.ts +8 -0
- package/dist/esm/listeners/activity-listener-manager.js +28 -0
- package/dist/esm/listeners/index.d.ts +6 -0
- package/dist/esm/listeners/index.js +5 -0
- package/dist/esm/listeners/input-listener-managers.d.ts +15 -0
- package/dist/esm/listeners/input-listener-managers.js +53 -0
- package/dist/esm/listeners/listeners.types.d.ts +4 -0
- package/dist/esm/listeners/listeners.types.js +1 -0
- package/dist/esm/listeners/touch-listener-manager.d.ts +10 -0
- package/dist/esm/listeners/touch-listener-manager.js +52 -0
- package/dist/esm/listeners/unload-listener-manager.d.ts +8 -0
- package/dist/esm/listeners/unload-listener-manager.js +26 -0
- package/dist/esm/listeners/visibility-listener-manager.d.ts +12 -0
- package/dist/esm/listeners/visibility-listener-manager.js +79 -0
- package/dist/esm/managers/api.manager.d.ts +3 -0
- package/dist/esm/managers/api.manager.js +10 -0
- package/dist/esm/managers/config.manager.d.ts +7 -0
- package/dist/esm/managers/config.manager.js +90 -0
- package/dist/esm/managers/cross-tab-session.manager.d.ts +170 -0
- package/dist/esm/managers/cross-tab-session.manager.js +726 -0
- package/dist/esm/managers/event.manager.d.ts +61 -0
- package/dist/esm/managers/event.manager.js +504 -0
- package/dist/esm/managers/sampling.manager.d.ts +8 -0
- package/dist/esm/managers/sampling.manager.js +49 -0
- package/dist/esm/managers/sender.manager.d.ts +46 -0
- package/dist/esm/managers/sender.manager.js +300 -0
- package/dist/esm/managers/session-recovery.manager.d.ts +65 -0
- package/dist/esm/managers/session-recovery.manager.js +233 -0
- package/dist/esm/managers/session.manager.d.ts +72 -0
- package/dist/esm/managers/session.manager.js +583 -0
- package/dist/esm/managers/state.manager.d.ts +5 -0
- package/dist/esm/managers/state.manager.js +19 -0
- package/dist/esm/managers/storage.manager.d.ts +10 -0
- package/dist/esm/managers/storage.manager.js +77 -0
- package/dist/esm/managers/tags.manager.d.ts +12 -0
- package/dist/esm/managers/tags.manager.js +285 -0
- package/dist/esm/managers/user.manager.d.ts +7 -0
- package/dist/esm/managers/user.manager.js +18 -0
- package/dist/esm/public-api.d.ts +1 -0
- package/dist/esm/public-api.js +1 -0
- package/dist/esm/types/api.types.d.ts +21 -0
- package/dist/esm/types/api.types.js +22 -0
- package/dist/esm/types/common.types.d.ts +1 -0
- package/dist/esm/types/common.types.js +1 -0
- package/dist/esm/types/config.types.d.ts +104 -0
- package/dist/esm/types/config.types.js +1 -0
- package/dist/esm/types/device.types.d.ts +6 -0
- package/dist/esm/types/device.types.js +7 -0
- package/dist/esm/types/event.types.d.ts +104 -0
- package/dist/esm/types/event.types.js +22 -0
- package/dist/esm/types/index.d.ts +13 -0
- package/dist/esm/types/index.js +13 -0
- package/dist/esm/types/log.types.d.ts +4 -0
- package/dist/esm/types/log.types.js +1 -0
- package/dist/esm/types/mode.types.d.ts +7 -0
- package/dist/esm/types/mode.types.js +8 -0
- package/dist/esm/types/queue.types.d.ts +23 -0
- package/dist/esm/types/queue.types.js +1 -0
- package/dist/esm/types/session.types.d.ts +65 -0
- package/dist/esm/types/session.types.js +1 -0
- package/dist/esm/types/state.types.d.ts +12 -0
- package/dist/esm/types/state.types.js +1 -0
- package/dist/esm/types/tag.types.d.ts +43 -0
- package/dist/esm/types/tag.types.js +28 -0
- package/dist/esm/types/validation-error.types.d.ts +42 -0
- package/dist/esm/types/validation-error.types.js +59 -0
- package/dist/esm/types/web-vitals.types.d.ts +6 -0
- package/dist/esm/types/web-vitals.types.js +1 -0
- package/dist/esm/types/window.types.d.ts +17 -0
- package/dist/esm/types/window.types.js +1 -0
- package/dist/esm/utils/browser/device-detector.utils.d.ts +6 -0
- package/dist/esm/utils/browser/device-detector.utils.js +67 -0
- package/dist/esm/utils/browser/index.d.ts +2 -0
- package/dist/esm/utils/browser/index.js +2 -0
- package/dist/esm/utils/browser/utm-params.utils.d.ts +6 -0
- package/dist/esm/utils/browser/utm-params.utils.js +33 -0
- package/dist/esm/utils/data/index.d.ts +1 -0
- package/dist/esm/utils/data/index.js +1 -0
- package/dist/esm/utils/data/uuid.utils.d.ts +5 -0
- package/dist/esm/utils/data/uuid.utils.js +14 -0
- package/dist/esm/utils/index.d.ts +6 -0
- package/dist/esm/utils/index.js +6 -0
- package/dist/esm/utils/logging/debug-logger.utils.d.ts +56 -0
- package/dist/esm/utils/logging/debug-logger.utils.js +136 -0
- package/dist/esm/utils/logging/index.d.ts +1 -0
- package/dist/esm/utils/logging/index.js +1 -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.d.ts +20 -0
- package/dist/esm/utils/network/url.utils.js +166 -0
- package/dist/esm/utils/security/index.d.ts +1 -0
- package/dist/esm/utils/security/index.js +1 -0
- package/dist/esm/utils/security/sanitize.utils.d.ts +32 -0
- package/dist/esm/utils/security/sanitize.utils.js +311 -0
- package/dist/esm/utils/validations/config-validations.utils.d.ts +42 -0
- package/dist/esm/utils/validations/config-validations.utils.js +289 -0
- package/dist/esm/utils/validations/event-validations.utils.d.ts +12 -0
- package/dist/esm/utils/validations/event-validations.utils.js +26 -0
- package/dist/esm/utils/validations/index.d.ts +5 -0
- package/dist/esm/utils/validations/index.js +5 -0
- package/dist/esm/utils/validations/metadata-validations.utils.d.ts +22 -0
- package/dist/esm/utils/validations/metadata-validations.utils.js +110 -0
- package/dist/esm/utils/validations/type-guards.utils.d.ts +6 -0
- package/dist/esm/utils/validations/type-guards.utils.js +27 -0
- package/dist/esm/utils/validations/url-validations.utils.d.ts +15 -0
- package/dist/esm/utils/validations/url-validations.utils.js +42 -0
- package/package.json +80 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { StateManager } from '../managers/state.manager';
|
|
2
|
+
import { EventType } from '../types';
|
|
3
|
+
import { LONG_TASK_THROTTLE_MS } from '../constants';
|
|
4
|
+
import { PRECISION_FOUR_DECIMALS, PRECISION_TWO_DECIMALS } from '../constants';
|
|
5
|
+
import { debugLog } from '../utils/logging';
|
|
6
|
+
export class PerformanceHandler extends StateManager {
|
|
7
|
+
constructor(eventManager) {
|
|
8
|
+
super();
|
|
9
|
+
this.reportedByNav = new Map();
|
|
10
|
+
this.observers = [];
|
|
11
|
+
this.lastLongTaskSentAt = 0;
|
|
12
|
+
this.eventManager = eventManager;
|
|
13
|
+
}
|
|
14
|
+
async startTracking() {
|
|
15
|
+
debugLog.debug('PerformanceHandler', 'Starting performance tracking');
|
|
16
|
+
await this.initWebVitals();
|
|
17
|
+
this.observeLongTasks();
|
|
18
|
+
this.reportTTFB();
|
|
19
|
+
}
|
|
20
|
+
stopTracking() {
|
|
21
|
+
debugLog.debug('PerformanceHandler', 'Stopping performance tracking', { observersCount: this.observers.length });
|
|
22
|
+
this.observers.forEach((obs, index) => {
|
|
23
|
+
try {
|
|
24
|
+
obs.disconnect();
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
debugLog.warn('PerformanceHandler', 'Failed to disconnect performance observer', {
|
|
28
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
29
|
+
observerIndex: index,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
this.observers.length = 0;
|
|
34
|
+
this.reportedByNav.clear();
|
|
35
|
+
debugLog.debug('PerformanceHandler', 'Performance tracking cleanup completed', {
|
|
36
|
+
remainingObservers: this.observers.length,
|
|
37
|
+
clearedNavReports: true,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
observeWebVitalsFallback() {
|
|
41
|
+
// TTFB - should be captured immediately as it's available from navigation timing
|
|
42
|
+
this.reportTTFB();
|
|
43
|
+
// LCP
|
|
44
|
+
this.safeObserve('largest-contentful-paint', (list) => {
|
|
45
|
+
const entries = list.getEntries();
|
|
46
|
+
const last = entries[entries.length - 1];
|
|
47
|
+
if (!last) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
this.sendVital({ type: 'LCP', value: Number(last.startTime.toFixed(PRECISION_TWO_DECIMALS)) });
|
|
51
|
+
}, { type: 'largest-contentful-paint', buffered: true }, true);
|
|
52
|
+
// CLS (layout-shift)
|
|
53
|
+
let clsValue = 0;
|
|
54
|
+
this.safeObserve('layout-shift', (list) => {
|
|
55
|
+
const entries = list.getEntries();
|
|
56
|
+
for (const entry of entries) {
|
|
57
|
+
if (entry.hadRecentInput === true) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const value = typeof entry.value === 'number' ? entry.value : 0;
|
|
61
|
+
clsValue += value;
|
|
62
|
+
}
|
|
63
|
+
this.sendVital({ type: 'CLS', value: Number(clsValue.toFixed(PRECISION_FOUR_DECIMALS)) });
|
|
64
|
+
}, { type: 'layout-shift', buffered: true });
|
|
65
|
+
// FCP
|
|
66
|
+
this.safeObserve('paint', (list) => {
|
|
67
|
+
for (const entry of list.getEntries()) {
|
|
68
|
+
if (entry.name === 'first-contentful-paint') {
|
|
69
|
+
this.sendVital({ type: 'FCP', value: Number(entry.startTime.toFixed(PRECISION_TWO_DECIMALS)) });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}, { type: 'paint', buffered: true }, true);
|
|
73
|
+
// INP via performance event timing (where supported)
|
|
74
|
+
this.safeObserve('event', (list) => {
|
|
75
|
+
let worst = 0;
|
|
76
|
+
const entries = list.getEntries();
|
|
77
|
+
for (const entry of entries) {
|
|
78
|
+
const dur = (entry.processingEnd ?? 0) - (entry.startTime ?? 0);
|
|
79
|
+
worst = Math.max(worst, dur);
|
|
80
|
+
}
|
|
81
|
+
if (worst > 0) {
|
|
82
|
+
this.sendVital({ type: 'INP', value: Number(worst.toFixed(PRECISION_TWO_DECIMALS)) });
|
|
83
|
+
}
|
|
84
|
+
}, { type: 'event', buffered: true });
|
|
85
|
+
}
|
|
86
|
+
async initWebVitals() {
|
|
87
|
+
try {
|
|
88
|
+
const { onLCP, onCLS, onFCP, onTTFB, onINP } = await import('web-vitals');
|
|
89
|
+
const report = (type) => (metric) => {
|
|
90
|
+
const value = Number(metric.value.toFixed(PRECISION_TWO_DECIMALS));
|
|
91
|
+
this.sendVital({ type, value });
|
|
92
|
+
};
|
|
93
|
+
onLCP(report('LCP'));
|
|
94
|
+
onCLS(report('CLS'));
|
|
95
|
+
onFCP(report('FCP'));
|
|
96
|
+
onTTFB(report('TTFB'));
|
|
97
|
+
onINP(report('INP'));
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
debugLog.warn('PerformanceHandler', 'Failed to load web-vitals library, using fallback', {
|
|
101
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
102
|
+
});
|
|
103
|
+
this.observeWebVitalsFallback();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
reportTTFB() {
|
|
107
|
+
try {
|
|
108
|
+
const nav = performance.getEntriesByType('navigation')[0];
|
|
109
|
+
if (!nav) {
|
|
110
|
+
debugLog.debug('PerformanceHandler', 'Navigation timing not available for TTFB');
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const ttfb = nav.responseStart;
|
|
114
|
+
// TTFB can be 0 in some browsers (especially Mobile Safari) when:
|
|
115
|
+
// - Response is served from cache
|
|
116
|
+
// - Connection is reused
|
|
117
|
+
// - Browser cannot determine exact timing
|
|
118
|
+
// We still report it as it's a valid measurement
|
|
119
|
+
if (typeof ttfb === 'number' && Number.isFinite(ttfb)) {
|
|
120
|
+
this.sendVital({ type: 'TTFB', value: Number(ttfb.toFixed(PRECISION_TWO_DECIMALS)) });
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
debugLog.debug('PerformanceHandler', 'TTFB value is not a valid number', { ttfb });
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
debugLog.warn('PerformanceHandler', 'Failed to report TTFB', {
|
|
128
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
observeLongTasks() {
|
|
133
|
+
this.safeObserve('longtask', (list) => {
|
|
134
|
+
const entries = list.getEntries();
|
|
135
|
+
for (const entry of entries) {
|
|
136
|
+
const duration = Number(entry.duration.toFixed(PRECISION_TWO_DECIMALS));
|
|
137
|
+
const now = Date.now();
|
|
138
|
+
if (now - this.lastLongTaskSentAt >= LONG_TASK_THROTTLE_MS) {
|
|
139
|
+
this.trackWebVital('LONG_TASK', duration);
|
|
140
|
+
this.lastLongTaskSentAt = now;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}, { type: 'longtask', buffered: true });
|
|
144
|
+
}
|
|
145
|
+
sendVital(sample) {
|
|
146
|
+
const navId = this.getNavigationId();
|
|
147
|
+
const key = `${sample.type}`;
|
|
148
|
+
if (navId) {
|
|
149
|
+
if (!this.reportedByNav.has(navId)) {
|
|
150
|
+
this.reportedByNav.set(navId, new Set());
|
|
151
|
+
}
|
|
152
|
+
const sent = this.reportedByNav.get(navId);
|
|
153
|
+
if (sent.has(key)) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
sent.add(key);
|
|
157
|
+
}
|
|
158
|
+
this.trackWebVital(sample.type, sample.value);
|
|
159
|
+
}
|
|
160
|
+
trackWebVital(type, value) {
|
|
161
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
162
|
+
debugLog.warn('PerformanceHandler', 'Invalid web vital value', { type, value });
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
this.eventManager.track({
|
|
166
|
+
type: EventType.WEB_VITALS,
|
|
167
|
+
web_vitals: {
|
|
168
|
+
type,
|
|
169
|
+
value,
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
getNavigationId() {
|
|
174
|
+
try {
|
|
175
|
+
const nav = performance.getEntriesByType('navigation')[0];
|
|
176
|
+
if (!nav) {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
return `${Math.round(nav.startTime)}_${window.location.pathname}`;
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
debugLog.warn('PerformanceHandler', 'Failed to get navigation ID', {
|
|
183
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
184
|
+
});
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
safeObserve(type, cb, options, once = false) {
|
|
189
|
+
try {
|
|
190
|
+
if (typeof PerformanceObserver === 'undefined')
|
|
191
|
+
return;
|
|
192
|
+
const supported = PerformanceObserver.supportedEntryTypes;
|
|
193
|
+
if (supported && !supported.includes(type))
|
|
194
|
+
return;
|
|
195
|
+
const obs = new PerformanceObserver((list, observer) => {
|
|
196
|
+
cb(list, observer);
|
|
197
|
+
if (once) {
|
|
198
|
+
try {
|
|
199
|
+
observer.disconnect();
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
// Intentionally ignored
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
obs.observe((options ?? { type, buffered: true }));
|
|
207
|
+
if (!once) {
|
|
208
|
+
this.observers.push(obs);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
debugLog.warn('PerformanceHandler', 'Failed to create performance observer', {
|
|
213
|
+
type,
|
|
214
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { EventManager } from '../managers/event.manager';
|
|
2
|
+
import { StateManager } from '../managers/state.manager';
|
|
3
|
+
export declare class ScrollHandler extends StateManager {
|
|
4
|
+
private readonly eventManager;
|
|
5
|
+
private readonly containers;
|
|
6
|
+
constructor(eventManager: EventManager);
|
|
7
|
+
startTracking(): void;
|
|
8
|
+
stopTracking(): void;
|
|
9
|
+
private setupScrollContainer;
|
|
10
|
+
private calculateScrollData;
|
|
11
|
+
private getScrollTop;
|
|
12
|
+
private getViewportHeight;
|
|
13
|
+
private getScrollHeight;
|
|
14
|
+
private isElementScrollable;
|
|
15
|
+
private safeQuerySelector;
|
|
16
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { SCROLL_DEBOUNCE_TIME_MS, SIGNIFICANT_SCROLL_DELTA } from '../constants';
|
|
2
|
+
import { EventType, ScrollDirection } from '../types';
|
|
3
|
+
import { StateManager } from '../managers/state.manager';
|
|
4
|
+
import { debugLog } from '../utils/logging';
|
|
5
|
+
export class ScrollHandler extends StateManager {
|
|
6
|
+
constructor(eventManager) {
|
|
7
|
+
super();
|
|
8
|
+
this.containers = [];
|
|
9
|
+
this.eventManager = eventManager;
|
|
10
|
+
}
|
|
11
|
+
startTracking() {
|
|
12
|
+
const raw = this.get('config').scrollContainerSelectors;
|
|
13
|
+
const selectors = Array.isArray(raw) ? raw : typeof raw === 'string' ? [raw] : [];
|
|
14
|
+
debugLog.debug('ScrollHandler', 'Starting scroll tracking', { selectorsCount: selectors.length });
|
|
15
|
+
const elements = selectors
|
|
16
|
+
.map((sel) => this.safeQuerySelector(sel))
|
|
17
|
+
.filter((element) => element instanceof HTMLElement);
|
|
18
|
+
if (elements.length === 0) {
|
|
19
|
+
elements.push(window);
|
|
20
|
+
}
|
|
21
|
+
for (const element of elements) {
|
|
22
|
+
this.setupScrollContainer(element);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
stopTracking() {
|
|
26
|
+
debugLog.debug('ScrollHandler', 'Stopping scroll tracking', { containersCount: this.containers.length });
|
|
27
|
+
for (const container of this.containers) {
|
|
28
|
+
if (container.debounceTimer) {
|
|
29
|
+
clearTimeout(container.debounceTimer);
|
|
30
|
+
}
|
|
31
|
+
if (container.element instanceof Window) {
|
|
32
|
+
window.removeEventListener('scroll', container.listener);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
container.element.removeEventListener('scroll', container.listener);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
this.containers.length = 0;
|
|
39
|
+
}
|
|
40
|
+
setupScrollContainer(element) {
|
|
41
|
+
// Skip setup for non-scrollable elements
|
|
42
|
+
if (element !== window && !this.isElementScrollable(element)) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const container = {
|
|
46
|
+
element,
|
|
47
|
+
lastScrollPos: this.getScrollTop(element),
|
|
48
|
+
debounceTimer: null,
|
|
49
|
+
listener: () => { },
|
|
50
|
+
};
|
|
51
|
+
const handleScroll = () => {
|
|
52
|
+
if (this.get('suppressNextScroll')) {
|
|
53
|
+
this.set('suppressNextScroll', false);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (container.debounceTimer) {
|
|
57
|
+
clearTimeout(container.debounceTimer);
|
|
58
|
+
}
|
|
59
|
+
container.debounceTimer = window.setTimeout(() => {
|
|
60
|
+
const scrollData = this.calculateScrollData(container);
|
|
61
|
+
if (scrollData) {
|
|
62
|
+
this.eventManager.track({
|
|
63
|
+
type: EventType.SCROLL,
|
|
64
|
+
scroll_data: scrollData,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
container.debounceTimer = null;
|
|
68
|
+
}, SCROLL_DEBOUNCE_TIME_MS);
|
|
69
|
+
};
|
|
70
|
+
container.listener = handleScroll;
|
|
71
|
+
this.containers.push(container);
|
|
72
|
+
if (element instanceof Window) {
|
|
73
|
+
window.addEventListener('scroll', handleScroll, { passive: true });
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
element.addEventListener('scroll', handleScroll, { passive: true });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
calculateScrollData(container) {
|
|
80
|
+
const { element, lastScrollPos } = container;
|
|
81
|
+
const scrollTop = this.getScrollTop(element);
|
|
82
|
+
const viewportHeight = this.getViewportHeight(element);
|
|
83
|
+
const scrollHeight = this.getScrollHeight(element);
|
|
84
|
+
// Check if the page is actually scrollable at runtime
|
|
85
|
+
if (element === window && scrollHeight <= viewportHeight) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
const direction = scrollTop > lastScrollPos ? ScrollDirection.DOWN : ScrollDirection.UP;
|
|
89
|
+
const depth = scrollHeight > viewportHeight
|
|
90
|
+
? Math.min(100, Math.max(0, Math.floor((scrollTop / (scrollHeight - viewportHeight)) * 100)))
|
|
91
|
+
: 0;
|
|
92
|
+
// Only update if scroll position changed significantly
|
|
93
|
+
const positionDelta = Math.abs(scrollTop - lastScrollPos);
|
|
94
|
+
if (positionDelta < SIGNIFICANT_SCROLL_DELTA) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
container.lastScrollPos = scrollTop;
|
|
98
|
+
return { depth, direction };
|
|
99
|
+
}
|
|
100
|
+
getScrollTop(element) {
|
|
101
|
+
return element instanceof Window ? window.scrollY : element.scrollTop;
|
|
102
|
+
}
|
|
103
|
+
getViewportHeight(element) {
|
|
104
|
+
return element instanceof Window ? window.innerHeight : element.clientHeight;
|
|
105
|
+
}
|
|
106
|
+
getScrollHeight(element) {
|
|
107
|
+
return element instanceof Window ? document.documentElement.scrollHeight : element.scrollHeight;
|
|
108
|
+
}
|
|
109
|
+
isElementScrollable(element) {
|
|
110
|
+
const style = getComputedStyle(element);
|
|
111
|
+
const hasScrollableOverflow = style.overflowY === 'auto' ||
|
|
112
|
+
style.overflowY === 'scroll' ||
|
|
113
|
+
style.overflowX === 'auto' ||
|
|
114
|
+
style.overflowX === 'scroll' ||
|
|
115
|
+
style.overflow === 'auto' ||
|
|
116
|
+
style.overflow === 'scroll';
|
|
117
|
+
// Element must have scrollable overflow AND content that exceeds the container
|
|
118
|
+
const hasOverflowContent = element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth;
|
|
119
|
+
return hasScrollableOverflow && hasOverflowContent;
|
|
120
|
+
}
|
|
121
|
+
safeQuerySelector(selector) {
|
|
122
|
+
try {
|
|
123
|
+
return document.querySelector(selector);
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
// Invalid CSS selector - log warning and continue
|
|
127
|
+
debugLog.clientWarn('ScrollHandler', 'Invalid CSS selector', {
|
|
128
|
+
selector,
|
|
129
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
130
|
+
});
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { EventManager } from '../managers/event.manager';
|
|
2
|
+
import { StateManager } from '../managers/state.manager';
|
|
3
|
+
import { StorageManager } from '../managers/storage.manager';
|
|
4
|
+
export declare class SessionHandler extends StateManager {
|
|
5
|
+
private readonly eventManager;
|
|
6
|
+
private readonly storageManager;
|
|
7
|
+
private readonly sessionStorageKey;
|
|
8
|
+
private sessionManager;
|
|
9
|
+
private recoveryManager;
|
|
10
|
+
private _crossTabSessionManager;
|
|
11
|
+
private heartbeatInterval;
|
|
12
|
+
private _isInitializingCrossTab;
|
|
13
|
+
private get crossTabSessionManager();
|
|
14
|
+
private shouldUseCrossTabs;
|
|
15
|
+
constructor(storageManager: StorageManager, eventManager: EventManager);
|
|
16
|
+
startTracking(): void;
|
|
17
|
+
stopTracking(): void;
|
|
18
|
+
private initializeSessionRecoveryManager;
|
|
19
|
+
private initializeCrossTabSessionManager;
|
|
20
|
+
private createOrJoinSession;
|
|
21
|
+
private forceCleanupSession;
|
|
22
|
+
private trackSession;
|
|
23
|
+
private startInitialSession;
|
|
24
|
+
private checkOrphanedSessions;
|
|
25
|
+
private persistSession;
|
|
26
|
+
private clearPersistedSession;
|
|
27
|
+
private startHeartbeat;
|
|
28
|
+
private stopHeartbeat;
|
|
29
|
+
}
|