@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,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MIN_SESSION_RECOVERY_WINDOW_MS = exports.MAX_SESSION_RECOVERY_WINDOW_MS = exports.MAX_SESSION_RECOVERY_ATTEMPTS = exports.SESSION_RECOVERY_WINDOW_MULTIPLIER = exports.TAB_CLEANUP_DELAY_MS = exports.TAB_ELECTION_TIMEOUT_MS = exports.TAB_HEARTBEAT_INTERVAL_MS = exports.SESSION_END_PRIORITY_DELAY_MS = exports.LONG_TASK_THROTTLE_MS = exports.EVENT_PERSISTENCE_MAX_AGE_MS = exports.EVENT_EXPIRY_HOURS = exports.RATE_LIMIT_INTERVAL = exports.RETRY_BACKOFF_MAX = exports.RETRY_BACKOFF_INITIAL = exports.EVENT_SENT_INTERVAL_TEST_MS = exports.EVENT_SENT_INTERVAL_MS = exports.DUPLICATE_EVENT_THRESHOLD_MS = exports.DEFAULT_VISIBILITY_TIMEOUT_MS = exports.SCROLL_DEBOUNCE_TIME_MS = exports.DEFAULT_THROTTLE_DELAY_MS = exports.SESSION_HEARTBEAT_INTERVAL_MS = exports.DEFAULT_SESSION_TIMEOUT_MS = void 0;
|
|
4
|
+
// Session and timeout constants
|
|
5
|
+
exports.DEFAULT_SESSION_TIMEOUT_MS = 15 * 60 * 1000;
|
|
6
|
+
exports.SESSION_HEARTBEAT_INTERVAL_MS = 30000;
|
|
7
|
+
// Throttling and debouncing constants
|
|
8
|
+
exports.DEFAULT_THROTTLE_DELAY_MS = 1000;
|
|
9
|
+
exports.SCROLL_DEBOUNCE_TIME_MS = 250;
|
|
10
|
+
exports.DEFAULT_VISIBILITY_TIMEOUT_MS = 2000;
|
|
11
|
+
exports.DUPLICATE_EVENT_THRESHOLD_MS = 1000;
|
|
12
|
+
// Event processing intervals
|
|
13
|
+
exports.EVENT_SENT_INTERVAL_MS = 10000;
|
|
14
|
+
exports.EVENT_SENT_INTERVAL_TEST_MS = 2500;
|
|
15
|
+
// Network timing
|
|
16
|
+
exports.RETRY_BACKOFF_INITIAL = 1000; // 1 second
|
|
17
|
+
exports.RETRY_BACKOFF_MAX = 30000; // 30 seconds
|
|
18
|
+
exports.RATE_LIMIT_INTERVAL = 1000; // 1 second
|
|
19
|
+
// Event expiry
|
|
20
|
+
exports.EVENT_EXPIRY_HOURS = 24;
|
|
21
|
+
exports.EVENT_PERSISTENCE_MAX_AGE_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
22
|
+
// Performance metrics
|
|
23
|
+
exports.LONG_TASK_THROTTLE_MS = exports.DEFAULT_THROTTLE_DELAY_MS;
|
|
24
|
+
// Session end coordination
|
|
25
|
+
exports.SESSION_END_PRIORITY_DELAY_MS = 100;
|
|
26
|
+
// Cross-tab session management
|
|
27
|
+
exports.TAB_HEARTBEAT_INTERVAL_MS = 5000; // 5 seconds
|
|
28
|
+
exports.TAB_ELECTION_TIMEOUT_MS = 2000; // 2 seconds
|
|
29
|
+
exports.TAB_CLEANUP_DELAY_MS = 1000; // 1 second
|
|
30
|
+
// Session recovery
|
|
31
|
+
exports.SESSION_RECOVERY_WINDOW_MULTIPLIER = 2; // 2x session timeout
|
|
32
|
+
exports.MAX_SESSION_RECOVERY_ATTEMPTS = 3;
|
|
33
|
+
exports.MAX_SESSION_RECOVERY_WINDOW_MS = 24 * 60 * 60 * 1000; // 24 hours max
|
|
34
|
+
exports.MIN_SESSION_RECOVERY_WINDOW_MS = 2 * 60 * 1000; // 2 minutes minimum
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ApiConfig } from '../types';
|
|
2
|
+
export declare const ALLOWED_API_CONFIG_KEYS: Set<keyof ApiConfig>;
|
|
3
|
+
export declare const VALIDATION_MESSAGES: {
|
|
4
|
+
readonly MISSING_PROJECT_ID: "Project ID is required";
|
|
5
|
+
readonly PROJECT_ID_EMPTY_AFTER_TRIM: "Project ID is required";
|
|
6
|
+
readonly INVALID_SESSION_TIMEOUT: "Session timeout must be between 30000ms (30 seconds) and 86400000ms (24 hours)";
|
|
7
|
+
readonly INVALID_SAMPLING_RATE: "Sampling rate must be between 0 and 1";
|
|
8
|
+
readonly INVALID_ERROR_SAMPLING_RATE: "Error sampling must be between 0 and 1";
|
|
9
|
+
readonly INVALID_GOOGLE_ANALYTICS_ID: "Google Analytics measurement ID is required when integration is enabled";
|
|
10
|
+
readonly INVALID_SCROLL_CONTAINER_SELECTORS: "Scroll container selectors must be valid CSS selectors";
|
|
11
|
+
readonly INVALID_GLOBAL_METADATA: "Global metadata must be an object";
|
|
12
|
+
readonly INVALID_SENSITIVE_QUERY_PARAMS: "Sensitive query params must be an array of strings";
|
|
13
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VALIDATION_MESSAGES = exports.ALLOWED_API_CONFIG_KEYS = void 0;
|
|
4
|
+
const limits_constants_1 = require("./limits.constants");
|
|
5
|
+
// Allowed API config keys for runtime validation
|
|
6
|
+
exports.ALLOWED_API_CONFIG_KEYS = new Set([
|
|
7
|
+
'mode',
|
|
8
|
+
'tags',
|
|
9
|
+
'samplingRate',
|
|
10
|
+
'excludedUrlPaths',
|
|
11
|
+
'ipExcluded',
|
|
12
|
+
]);
|
|
13
|
+
// Validation error messages - standardized across all layers
|
|
14
|
+
exports.VALIDATION_MESSAGES = {
|
|
15
|
+
// Project ID validation - consistent message across all layers
|
|
16
|
+
MISSING_PROJECT_ID: 'Project ID is required',
|
|
17
|
+
PROJECT_ID_EMPTY_AFTER_TRIM: 'Project ID is required',
|
|
18
|
+
// Session timeout validation
|
|
19
|
+
INVALID_SESSION_TIMEOUT: `Session timeout must be between ${limits_constants_1.MIN_SESSION_TIMEOUT_MS}ms (30 seconds) and ${limits_constants_1.MAX_SESSION_TIMEOUT_MS}ms (24 hours)`,
|
|
20
|
+
// Sampling rate validation
|
|
21
|
+
INVALID_SAMPLING_RATE: `Sampling rate must be between ${limits_constants_1.MIN_SAMPLING_RATE} and ${limits_constants_1.MAX_SAMPLING_RATE}`,
|
|
22
|
+
INVALID_ERROR_SAMPLING_RATE: 'Error sampling must be between 0 and 1',
|
|
23
|
+
// Integration validation
|
|
24
|
+
INVALID_GOOGLE_ANALYTICS_ID: 'Google Analytics measurement ID is required when integration is enabled',
|
|
25
|
+
// UI validation
|
|
26
|
+
INVALID_SCROLL_CONTAINER_SELECTORS: 'Scroll container selectors must be valid CSS selectors',
|
|
27
|
+
// Global metadata validation
|
|
28
|
+
INVALID_GLOBAL_METADATA: 'Global metadata must be an object',
|
|
29
|
+
// Array validation
|
|
30
|
+
INVALID_SENSITIVE_QUERY_PARAMS: 'Sensitive query params must be an array of strings',
|
|
31
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { EventManager } from '../managers/event.manager';
|
|
2
|
+
import { StateManager } from '../managers/state.manager';
|
|
3
|
+
export declare class ClickHandler extends StateManager {
|
|
4
|
+
private readonly eventManager;
|
|
5
|
+
private clickHandler?;
|
|
6
|
+
constructor(eventManager: EventManager);
|
|
7
|
+
startTracking(): void;
|
|
8
|
+
stopTracking(): void;
|
|
9
|
+
private findTrackingElement;
|
|
10
|
+
private getRelevantClickElement;
|
|
11
|
+
private calculateClickCoordinates;
|
|
12
|
+
private extractTrackingData;
|
|
13
|
+
private generateClickData;
|
|
14
|
+
private getRelevantText;
|
|
15
|
+
private extractElementAttributes;
|
|
16
|
+
private createCustomEventData;
|
|
17
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ClickHandler = void 0;
|
|
4
|
+
const constants_1 = require("../constants");
|
|
5
|
+
const types_1 = require("../types");
|
|
6
|
+
const state_manager_1 = require("../managers/state.manager");
|
|
7
|
+
const logging_1 = require("../utils/logging");
|
|
8
|
+
class ClickHandler extends state_manager_1.StateManager {
|
|
9
|
+
constructor(eventManager) {
|
|
10
|
+
super();
|
|
11
|
+
this.eventManager = eventManager;
|
|
12
|
+
}
|
|
13
|
+
startTracking() {
|
|
14
|
+
if (this.clickHandler) {
|
|
15
|
+
logging_1.debugLog.debug('ClickHandler', 'Click tracking already active');
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
logging_1.debugLog.debug('ClickHandler', 'Starting click tracking');
|
|
19
|
+
this.clickHandler = (event) => {
|
|
20
|
+
const mouseEvent = event;
|
|
21
|
+
const target = mouseEvent.target;
|
|
22
|
+
const clickedElement = target instanceof HTMLElement
|
|
23
|
+
? target
|
|
24
|
+
: target instanceof Node && target.parentElement instanceof HTMLElement
|
|
25
|
+
? target.parentElement
|
|
26
|
+
: null;
|
|
27
|
+
if (!clickedElement) {
|
|
28
|
+
logging_1.debugLog.warn('ClickHandler', 'Click target not found or not an element');
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
logging_1.debugLog.info('ClickHandler', '🖱️ Click detected on element', {
|
|
32
|
+
tagName: clickedElement.tagName,
|
|
33
|
+
className: clickedElement.className || 'none',
|
|
34
|
+
textContent: clickedElement.textContent?.slice(0, 50) ?? 'empty',
|
|
35
|
+
});
|
|
36
|
+
const trackingElement = this.findTrackingElement(clickedElement);
|
|
37
|
+
const relevantClickElement = this.getRelevantClickElement(clickedElement);
|
|
38
|
+
const coordinates = this.calculateClickCoordinates(mouseEvent, clickedElement);
|
|
39
|
+
if (trackingElement) {
|
|
40
|
+
const trackingData = this.extractTrackingData(trackingElement);
|
|
41
|
+
if (trackingData) {
|
|
42
|
+
const attributeData = this.createCustomEventData(trackingData);
|
|
43
|
+
this.eventManager.track({
|
|
44
|
+
type: types_1.EventType.CUSTOM,
|
|
45
|
+
custom_event: {
|
|
46
|
+
name: attributeData.name,
|
|
47
|
+
...(attributeData.value && { metadata: { value: attributeData.value } }),
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const clickData = this.generateClickData(clickedElement, relevantClickElement, coordinates);
|
|
53
|
+
this.eventManager.track({
|
|
54
|
+
type: types_1.EventType.CLICK,
|
|
55
|
+
click_data: clickData,
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
window.addEventListener('click', this.clickHandler, true);
|
|
59
|
+
}
|
|
60
|
+
stopTracking() {
|
|
61
|
+
if (this.clickHandler) {
|
|
62
|
+
window.removeEventListener('click', this.clickHandler, true);
|
|
63
|
+
this.clickHandler = undefined;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
findTrackingElement(element) {
|
|
67
|
+
if (element.hasAttribute(`${constants_1.HTML_DATA_ATTR_PREFIX}-name`)) {
|
|
68
|
+
return element;
|
|
69
|
+
}
|
|
70
|
+
const closest = element.closest(`[${constants_1.HTML_DATA_ATTR_PREFIX}-name]`);
|
|
71
|
+
return closest || undefined;
|
|
72
|
+
}
|
|
73
|
+
getRelevantClickElement(element) {
|
|
74
|
+
for (const selector of constants_1.INTERACTIVE_SELECTORS) {
|
|
75
|
+
try {
|
|
76
|
+
if (element.matches(selector)) {
|
|
77
|
+
return element;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
logging_1.debugLog.warn('ClickHandler', 'Invalid selector in interactive elements check', {
|
|
82
|
+
selector,
|
|
83
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
84
|
+
});
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
for (const selector of constants_1.INTERACTIVE_SELECTORS) {
|
|
89
|
+
try {
|
|
90
|
+
const parent = element.closest(selector);
|
|
91
|
+
if (parent) {
|
|
92
|
+
return parent;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
logging_1.debugLog.warn('ClickHandler', 'Invalid selector in parent element search', {
|
|
97
|
+
selector,
|
|
98
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
99
|
+
});
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return element;
|
|
104
|
+
}
|
|
105
|
+
calculateClickCoordinates(event, element) {
|
|
106
|
+
const rect = element.getBoundingClientRect();
|
|
107
|
+
const x = event.clientX;
|
|
108
|
+
const y = event.clientY;
|
|
109
|
+
const relativeX = rect.width > 0 ? Math.max(0, Math.min(1, Number(((x - rect.left) / rect.width).toFixed(3)))) : 0;
|
|
110
|
+
const relativeY = rect.height > 0 ? Math.max(0, Math.min(1, Number(((y - rect.top) / rect.height).toFixed(3)))) : 0;
|
|
111
|
+
return { x, y, relativeX, relativeY };
|
|
112
|
+
}
|
|
113
|
+
extractTrackingData(trackingElement) {
|
|
114
|
+
const name = trackingElement.getAttribute(`${constants_1.HTML_DATA_ATTR_PREFIX}-name`);
|
|
115
|
+
const value = trackingElement.getAttribute(`${constants_1.HTML_DATA_ATTR_PREFIX}-value`);
|
|
116
|
+
if (!name) {
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
element: trackingElement,
|
|
121
|
+
name,
|
|
122
|
+
...(value && { value }),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
generateClickData(clickedElement, relevantElement, coordinates) {
|
|
126
|
+
const { x, y, relativeX, relativeY } = coordinates;
|
|
127
|
+
const text = this.getRelevantText(clickedElement, relevantElement);
|
|
128
|
+
const attributes = this.extractElementAttributes(relevantElement);
|
|
129
|
+
const href = relevantElement.getAttribute('href');
|
|
130
|
+
const title = relevantElement.getAttribute('title');
|
|
131
|
+
const alt = relevantElement.getAttribute('alt');
|
|
132
|
+
const role = relevantElement.getAttribute('role');
|
|
133
|
+
const ariaLabel = relevantElement.getAttribute('aria-label');
|
|
134
|
+
const className = typeof relevantElement.className === 'string' ? relevantElement.className : String(relevantElement.className);
|
|
135
|
+
return {
|
|
136
|
+
x,
|
|
137
|
+
y,
|
|
138
|
+
relativeX,
|
|
139
|
+
relativeY,
|
|
140
|
+
tag: relevantElement.tagName.toLowerCase(),
|
|
141
|
+
...(relevantElement.id && { id: relevantElement.id }),
|
|
142
|
+
...(relevantElement.className && { class: className }),
|
|
143
|
+
...(text && { text }),
|
|
144
|
+
...(href && { href }),
|
|
145
|
+
...(title && { title }),
|
|
146
|
+
...(alt && { alt }),
|
|
147
|
+
...(role && { role }),
|
|
148
|
+
...(ariaLabel && { ariaLabel }),
|
|
149
|
+
...(Object.keys(attributes).length > 0 && { dataAttributes: attributes }),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
getRelevantText(clickedElement, relevantElement) {
|
|
153
|
+
const LARGE_CONTAINER_TAGS = ['main', 'section', 'article', 'body', 'html', 'header', 'footer', 'aside', 'nav'];
|
|
154
|
+
const clickedText = clickedElement.textContent?.trim() ?? '';
|
|
155
|
+
const relevantText = relevantElement.textContent?.trim() ?? '';
|
|
156
|
+
if (!clickedText && !relevantText) {
|
|
157
|
+
return '';
|
|
158
|
+
}
|
|
159
|
+
// Strategy 1: If clicked element has reasonable text, use it
|
|
160
|
+
if (clickedText && clickedText.length <= constants_1.MAX_TEXT_LENGTH) {
|
|
161
|
+
return clickedText;
|
|
162
|
+
}
|
|
163
|
+
// Strategy 2: For large containers with excessive text, avoid using container text
|
|
164
|
+
const isLargeContainer = LARGE_CONTAINER_TAGS.includes(relevantElement.tagName.toLowerCase());
|
|
165
|
+
const hasExcessiveText = relevantText.length > constants_1.MAX_TEXT_LENGTH * 2; // 510 chars
|
|
166
|
+
if (isLargeContainer && hasExcessiveText) {
|
|
167
|
+
// Use clicked element text if available and reasonable, otherwise empty
|
|
168
|
+
return clickedText && clickedText.length <= constants_1.MAX_TEXT_LENGTH ? clickedText : '';
|
|
169
|
+
}
|
|
170
|
+
// Strategy 3: Use relevant text but truncate if needed
|
|
171
|
+
if (relevantText.length <= constants_1.MAX_TEXT_LENGTH) {
|
|
172
|
+
return relevantText;
|
|
173
|
+
}
|
|
174
|
+
// Strategy 4: If clicked text is much shorter than relevant text, prefer clicked text
|
|
175
|
+
if (clickedText && clickedText.length < relevantText.length * 0.1) {
|
|
176
|
+
return clickedText.length <= constants_1.MAX_TEXT_LENGTH ? clickedText : clickedText.slice(0, constants_1.MAX_TEXT_LENGTH - 3) + '...';
|
|
177
|
+
}
|
|
178
|
+
// Fallback: truncate relevant text to exactly MAX_TEXT_LENGTH including ellipsis
|
|
179
|
+
return relevantText.slice(0, constants_1.MAX_TEXT_LENGTH - 3) + '...';
|
|
180
|
+
}
|
|
181
|
+
extractElementAttributes(element) {
|
|
182
|
+
const commonAttributes = ['id', 'class', 'data-testid', 'aria-label', 'title', 'href', 'type', 'name'];
|
|
183
|
+
const result = {};
|
|
184
|
+
for (const attributeName of commonAttributes) {
|
|
185
|
+
const value = element.getAttribute(attributeName);
|
|
186
|
+
if (value) {
|
|
187
|
+
result[attributeName] = value;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return result;
|
|
191
|
+
}
|
|
192
|
+
createCustomEventData(trackingData) {
|
|
193
|
+
return {
|
|
194
|
+
name: trackingData.name,
|
|
195
|
+
...(trackingData.value && { value: trackingData.value }),
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
exports.ClickHandler = ClickHandler;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { EventManager } from '../managers/event.manager';
|
|
2
|
+
import { StateManager } from '../managers/state.manager';
|
|
3
|
+
export declare class ErrorHandler extends StateManager {
|
|
4
|
+
private readonly eventManager;
|
|
5
|
+
private readonly piiPatterns;
|
|
6
|
+
constructor(eventManager: EventManager);
|
|
7
|
+
startTracking(): void;
|
|
8
|
+
stopTracking(): void;
|
|
9
|
+
private setupErrorListener;
|
|
10
|
+
private setupUnhandledRejectionListener;
|
|
11
|
+
private readonly handleError;
|
|
12
|
+
private readonly handleUnhandledRejection;
|
|
13
|
+
private sanitizeText;
|
|
14
|
+
private shouldSample;
|
|
15
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ErrorHandler = void 0;
|
|
4
|
+
const state_manager_1 = require("../managers/state.manager");
|
|
5
|
+
const types_1 = require("../types");
|
|
6
|
+
const logging_1 = require("../utils/logging");
|
|
7
|
+
class ErrorHandler extends state_manager_1.StateManager {
|
|
8
|
+
constructor(eventManager) {
|
|
9
|
+
super();
|
|
10
|
+
this.piiPatterns = [
|
|
11
|
+
/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g,
|
|
12
|
+
/\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/g,
|
|
13
|
+
/\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/g,
|
|
14
|
+
/\b[A-Z]{2}\d{2}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/g,
|
|
15
|
+
];
|
|
16
|
+
this.handleError = (event) => {
|
|
17
|
+
const config = this.get('config');
|
|
18
|
+
if (!this.shouldSample(config?.errorSampling ?? 0.1)) {
|
|
19
|
+
logging_1.debugLog.debug('ErrorHandler', `Error not sampled, skipping (errorSampling: ${config?.errorSampling})`, {
|
|
20
|
+
errorSampling: config?.errorSampling,
|
|
21
|
+
});
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
logging_1.debugLog.warn('ErrorHandler', `JavaScript error captured: ${event.message} (filename: ${event.filename}, lineno: ${event.lineno})`, {
|
|
25
|
+
message: event.message,
|
|
26
|
+
filename: event.filename,
|
|
27
|
+
lineno: event.lineno,
|
|
28
|
+
});
|
|
29
|
+
this.eventManager.track({
|
|
30
|
+
type: types_1.EventType.ERROR,
|
|
31
|
+
error_data: {
|
|
32
|
+
type: types_1.ErrorType.JS_ERROR,
|
|
33
|
+
message: this.sanitizeText(event.message || 'Unknown error'),
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
this.handleUnhandledRejection = (event) => {
|
|
38
|
+
const config = this.get('config');
|
|
39
|
+
if (!this.shouldSample(config?.errorSampling ?? 0.1)) {
|
|
40
|
+
logging_1.debugLog.debug('ErrorHandler', 'Promise rejection not sampled, skipping', {
|
|
41
|
+
errorSampling: config?.errorSampling,
|
|
42
|
+
});
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
logging_1.debugLog.warn('ErrorHandler', `Unhandled promise rejection captured (reason: ${typeof event.reason})`, {
|
|
46
|
+
reason: typeof event.reason,
|
|
47
|
+
});
|
|
48
|
+
let reason = 'Unknown rejection';
|
|
49
|
+
if (event.reason) {
|
|
50
|
+
if (typeof event.reason === 'string') {
|
|
51
|
+
reason = event.reason;
|
|
52
|
+
}
|
|
53
|
+
else if (event.reason instanceof Error) {
|
|
54
|
+
reason = event.reason.message || event.reason.toString();
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
reason = String(event.reason);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
this.eventManager.track({
|
|
61
|
+
type: types_1.EventType.ERROR,
|
|
62
|
+
error_data: {
|
|
63
|
+
type: types_1.ErrorType.PROMISE_REJECTION,
|
|
64
|
+
message: this.sanitizeText(reason),
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
this.eventManager = eventManager;
|
|
69
|
+
}
|
|
70
|
+
startTracking() {
|
|
71
|
+
logging_1.debugLog.debug('ErrorHandler', 'Starting error tracking');
|
|
72
|
+
this.setupErrorListener();
|
|
73
|
+
this.setupUnhandledRejectionListener();
|
|
74
|
+
}
|
|
75
|
+
stopTracking() {
|
|
76
|
+
logging_1.debugLog.debug('ErrorHandler', 'Stopping error tracking');
|
|
77
|
+
window.removeEventListener('error', this.handleError);
|
|
78
|
+
window.removeEventListener('unhandledrejection', this.handleUnhandledRejection);
|
|
79
|
+
}
|
|
80
|
+
setupErrorListener() {
|
|
81
|
+
window.addEventListener('error', this.handleError);
|
|
82
|
+
}
|
|
83
|
+
setupUnhandledRejectionListener() {
|
|
84
|
+
window.addEventListener('unhandledrejection', this.handleUnhandledRejection);
|
|
85
|
+
}
|
|
86
|
+
sanitizeText(text) {
|
|
87
|
+
let sanitized = text;
|
|
88
|
+
for (const pattern of this.piiPatterns) {
|
|
89
|
+
sanitized = sanitized.replace(pattern, '[REDACTED]');
|
|
90
|
+
}
|
|
91
|
+
return sanitized;
|
|
92
|
+
}
|
|
93
|
+
shouldSample(rate) {
|
|
94
|
+
return Math.random() < rate;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
exports.ErrorHandler = ErrorHandler;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { EventManager } from '../managers/event.manager';
|
|
2
|
+
import { StateManager } from '../managers/state.manager';
|
|
3
|
+
export declare class NetworkHandler extends StateManager {
|
|
4
|
+
private readonly eventManager;
|
|
5
|
+
private readonly originalFetch;
|
|
6
|
+
private readonly originalXHROpen;
|
|
7
|
+
private readonly originalXHRSend;
|
|
8
|
+
constructor(eventManager: EventManager);
|
|
9
|
+
startTracking(): void;
|
|
10
|
+
stopTracking(): void;
|
|
11
|
+
private interceptFetch;
|
|
12
|
+
private interceptXHR;
|
|
13
|
+
private trackNetworkError;
|
|
14
|
+
private normalizeUrlForTracking;
|
|
15
|
+
private shouldSample;
|
|
16
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NetworkHandler = void 0;
|
|
4
|
+
const state_manager_1 = require("../managers/state.manager");
|
|
5
|
+
const types_1 = require("../types");
|
|
6
|
+
const utils_1 = require("../utils");
|
|
7
|
+
const logging_1 = require("../utils/logging");
|
|
8
|
+
class NetworkHandler extends state_manager_1.StateManager {
|
|
9
|
+
constructor(eventManager) {
|
|
10
|
+
super();
|
|
11
|
+
this.eventManager = eventManager;
|
|
12
|
+
this.originalFetch = window.fetch;
|
|
13
|
+
this.originalXHROpen = XMLHttpRequest.prototype.open;
|
|
14
|
+
this.originalXHRSend = XMLHttpRequest.prototype.send;
|
|
15
|
+
}
|
|
16
|
+
startTracking() {
|
|
17
|
+
logging_1.debugLog.debug('NetworkHandler', 'Starting network error tracking');
|
|
18
|
+
this.interceptFetch();
|
|
19
|
+
this.interceptXHR();
|
|
20
|
+
}
|
|
21
|
+
stopTracking() {
|
|
22
|
+
logging_1.debugLog.debug('NetworkHandler', 'Stopping network error tracking');
|
|
23
|
+
window.fetch = this.originalFetch;
|
|
24
|
+
XMLHttpRequest.prototype.open = this.originalXHROpen;
|
|
25
|
+
XMLHttpRequest.prototype.send = this.originalXHRSend;
|
|
26
|
+
}
|
|
27
|
+
interceptFetch() {
|
|
28
|
+
window.fetch = async (input, init) => {
|
|
29
|
+
const startTime = Date.now();
|
|
30
|
+
const url = typeof input === 'string' ? input : input.toString();
|
|
31
|
+
const method = init?.method ?? 'GET';
|
|
32
|
+
try {
|
|
33
|
+
const response = await this.originalFetch(input, init);
|
|
34
|
+
const duration = Date.now() - startTime;
|
|
35
|
+
if (!response.ok) {
|
|
36
|
+
logging_1.debugLog.debug('NetworkHandler', 'Fetch error detected', {
|
|
37
|
+
method,
|
|
38
|
+
url: this.normalizeUrlForTracking(url),
|
|
39
|
+
status: response.status,
|
|
40
|
+
statusText: response.statusText,
|
|
41
|
+
});
|
|
42
|
+
this.trackNetworkError(method.toUpperCase(), this.normalizeUrlForTracking(url), response.status, response.statusText, duration);
|
|
43
|
+
}
|
|
44
|
+
return response;
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
const duration = Date.now() - startTime;
|
|
48
|
+
const errorMessage = error instanceof Error ? error.message : 'Network Error';
|
|
49
|
+
logging_1.debugLog.debug('NetworkHandler', 'Fetch exception caught', {
|
|
50
|
+
method,
|
|
51
|
+
url: this.normalizeUrlForTracking(url),
|
|
52
|
+
error: errorMessage,
|
|
53
|
+
});
|
|
54
|
+
this.trackNetworkError(method.toUpperCase(), this.normalizeUrlForTracking(url), undefined, errorMessage, duration);
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
interceptXHR() {
|
|
60
|
+
const trackNetworkError = this.trackNetworkError.bind(this);
|
|
61
|
+
const normalizeUrlForTracking = this.normalizeUrlForTracking.bind(this);
|
|
62
|
+
const originalXHROpen = this.originalXHROpen;
|
|
63
|
+
const originalXHRSend = this.originalXHRSend;
|
|
64
|
+
XMLHttpRequest.prototype.open = function (method, url, async, user, password) {
|
|
65
|
+
const asyncMode = async ?? true;
|
|
66
|
+
const extendedThis = this;
|
|
67
|
+
extendedThis._tracelogStartTime = Date.now();
|
|
68
|
+
extendedThis._tracelogMethod = method.toUpperCase();
|
|
69
|
+
extendedThis._tracelogUrl = url.toString();
|
|
70
|
+
return originalXHROpen.call(this, method, url, asyncMode, user, password);
|
|
71
|
+
};
|
|
72
|
+
XMLHttpRequest.prototype.send = function (body) {
|
|
73
|
+
const xhr = this;
|
|
74
|
+
const startTime = xhr._tracelogStartTime ?? Date.now();
|
|
75
|
+
const method = xhr._tracelogMethod ?? 'GET';
|
|
76
|
+
const url = xhr._tracelogUrl ?? '';
|
|
77
|
+
const originalOnReadyStateChange = xhr.onreadystatechange;
|
|
78
|
+
xhr.onreadystatechange = (ev) => {
|
|
79
|
+
if (xhr.readyState === XMLHttpRequest.DONE) {
|
|
80
|
+
const duration = Date.now() - startTime;
|
|
81
|
+
if (xhr.status === 0 || xhr.status >= 400) {
|
|
82
|
+
const statusText = xhr.statusText || 'Request Failed';
|
|
83
|
+
logging_1.debugLog.debug('NetworkHandler', 'XHR error detected', {
|
|
84
|
+
method,
|
|
85
|
+
url: normalizeUrlForTracking(url),
|
|
86
|
+
status: xhr.status,
|
|
87
|
+
statusText,
|
|
88
|
+
});
|
|
89
|
+
trackNetworkError(method, normalizeUrlForTracking(url), xhr.status, statusText, duration);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (originalOnReadyStateChange) {
|
|
93
|
+
return originalOnReadyStateChange.call(xhr, ev);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
return originalXHRSend.call(this, body);
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
trackNetworkError(method, url, status, statusText, duration) {
|
|
100
|
+
const config = this.get('config');
|
|
101
|
+
if (!this.shouldSample(config?.errorSampling ?? 0.1)) {
|
|
102
|
+
logging_1.debugLog.debug('NetworkHandler', `Network error not sampled, skipping (errorSampling: ${config?.errorSampling}, method: ${method}, url: ${url})`, {
|
|
103
|
+
errorSampling: config?.errorSampling,
|
|
104
|
+
method,
|
|
105
|
+
url,
|
|
106
|
+
});
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
logging_1.debugLog.warn('NetworkHandler', `Network error tracked: ${method} ${url} (status: ${status}, statusText: ${statusText}, duration: ${duration}ms)`, { method, url, status, statusText, duration });
|
|
110
|
+
this.eventManager.track({
|
|
111
|
+
type: types_1.EventType.ERROR,
|
|
112
|
+
error_data: {
|
|
113
|
+
type: types_1.ErrorType.NETWORK_ERROR,
|
|
114
|
+
message: statusText,
|
|
115
|
+
method,
|
|
116
|
+
url,
|
|
117
|
+
status,
|
|
118
|
+
statusText,
|
|
119
|
+
duration,
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
normalizeUrlForTracking(url) {
|
|
124
|
+
try {
|
|
125
|
+
const config = this.get('config');
|
|
126
|
+
return (0, utils_1.normalizeUrl)(url, config?.sensitiveQueryParams);
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return url;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
shouldSample(rate) {
|
|
133
|
+
return Math.random() < rate;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
exports.NetworkHandler = NetworkHandler;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { StateManager } from '../managers/state.manager';
|
|
2
|
+
import { EventManager } from '../managers/event.manager';
|
|
3
|
+
export declare class PageViewHandler extends StateManager {
|
|
4
|
+
private readonly eventManager;
|
|
5
|
+
private readonly onTrack;
|
|
6
|
+
private originalPushState?;
|
|
7
|
+
private originalReplaceState?;
|
|
8
|
+
constructor(eventManager: EventManager, onTrack: () => void);
|
|
9
|
+
startTracking(): void;
|
|
10
|
+
stopTracking(): void;
|
|
11
|
+
private patchHistory;
|
|
12
|
+
private readonly trackCurrentPage;
|
|
13
|
+
private trackInitialPageView;
|
|
14
|
+
private extractPageViewData;
|
|
15
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PageViewHandler = void 0;
|
|
4
|
+
const types_1 = require("../types");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
const state_manager_1 = require("../managers/state.manager");
|
|
7
|
+
const logging_1 = require("../utils/logging");
|
|
8
|
+
class PageViewHandler extends state_manager_1.StateManager {
|
|
9
|
+
constructor(eventManager, onTrack) {
|
|
10
|
+
super();
|
|
11
|
+
this.trackCurrentPage = () => {
|
|
12
|
+
const rawUrl = window.location.href;
|
|
13
|
+
const normalizedUrl = (0, utils_1.normalizeUrl)(rawUrl, this.get('config').sensitiveQueryParams);
|
|
14
|
+
if (this.get('pageUrl') !== normalizedUrl) {
|
|
15
|
+
const fromUrl = this.get('pageUrl');
|
|
16
|
+
logging_1.debugLog.debug('PageViewHandler', 'Page navigation detected', { from: fromUrl, to: normalizedUrl });
|
|
17
|
+
this.set('pageUrl', normalizedUrl);
|
|
18
|
+
this.eventManager.track({
|
|
19
|
+
type: types_1.EventType.PAGE_VIEW,
|
|
20
|
+
page_url: this.get('pageUrl'),
|
|
21
|
+
from_page_url: fromUrl,
|
|
22
|
+
...(this.extractPageViewData() && { page_view: this.extractPageViewData() }),
|
|
23
|
+
});
|
|
24
|
+
this.onTrack();
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
this.eventManager = eventManager;
|
|
28
|
+
this.onTrack = onTrack;
|
|
29
|
+
}
|
|
30
|
+
startTracking() {
|
|
31
|
+
logging_1.debugLog.debug('PageViewHandler', 'Starting page view tracking');
|
|
32
|
+
this.trackInitialPageView();
|
|
33
|
+
this.trackCurrentPage();
|
|
34
|
+
window.addEventListener('popstate', this.trackCurrentPage);
|
|
35
|
+
window.addEventListener('hashchange', this.trackCurrentPage);
|
|
36
|
+
this.patchHistory('pushState');
|
|
37
|
+
this.patchHistory('replaceState');
|
|
38
|
+
}
|
|
39
|
+
stopTracking() {
|
|
40
|
+
logging_1.debugLog.debug('PageViewHandler', 'Stopping page view tracking');
|
|
41
|
+
window.removeEventListener('popstate', this.trackCurrentPage);
|
|
42
|
+
window.removeEventListener('hashchange', this.trackCurrentPage);
|
|
43
|
+
if (this.originalPushState) {
|
|
44
|
+
window.history.pushState = this.originalPushState;
|
|
45
|
+
}
|
|
46
|
+
if (this.originalReplaceState) {
|
|
47
|
+
window.history.replaceState = this.originalReplaceState;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
patchHistory(method) {
|
|
51
|
+
if (method === 'pushState' && !this.originalPushState) {
|
|
52
|
+
this.originalPushState = window.history.pushState;
|
|
53
|
+
}
|
|
54
|
+
else if (method === 'replaceState' && !this.originalReplaceState) {
|
|
55
|
+
this.originalReplaceState = window.history.replaceState;
|
|
56
|
+
}
|
|
57
|
+
const original = window.history[method];
|
|
58
|
+
window.history[method] = (...args) => {
|
|
59
|
+
original.apply(window.history, args);
|
|
60
|
+
this.trackCurrentPage();
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
trackInitialPageView() {
|
|
64
|
+
this.eventManager.track({
|
|
65
|
+
type: types_1.EventType.PAGE_VIEW,
|
|
66
|
+
page_url: this.get('pageUrl'),
|
|
67
|
+
...(this.extractPageViewData() && { page_view: this.extractPageViewData() }),
|
|
68
|
+
});
|
|
69
|
+
this.onTrack();
|
|
70
|
+
}
|
|
71
|
+
extractPageViewData() {
|
|
72
|
+
const location = window.location;
|
|
73
|
+
const data = {
|
|
74
|
+
...(document.referrer && { referrer: document.referrer }),
|
|
75
|
+
...(document.title && { title: document.title }),
|
|
76
|
+
...(location.pathname && { pathname: location.pathname }),
|
|
77
|
+
...(location.search && { search: location.search }),
|
|
78
|
+
...(location.hash && { hash: location.hash }),
|
|
79
|
+
};
|
|
80
|
+
return Object.values(data).some((value) => !!value) ? data : undefined;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
exports.PageViewHandler = PageViewHandler;
|