@tracelog/lib 0.0.8 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -24
- package/dist/browser/tracelog.js +1934 -3226
- package/dist/cjs/api.d.ts +33 -19
- package/dist/cjs/api.js +111 -156
- package/dist/cjs/app.constants.d.ts +80 -1
- package/dist/cjs/app.constants.js +90 -3
- package/dist/cjs/app.d.ts +29 -44
- package/dist/cjs/app.js +114 -212
- package/dist/cjs/app.types.d.ts +2 -7
- package/dist/cjs/app.types.js +10 -21
- package/dist/cjs/constants/api.constants.js +11 -5
- package/dist/cjs/constants/config.constants.d.ts +75 -0
- package/dist/cjs/constants/config.constants.js +178 -0
- package/dist/cjs/constants/error.constants.d.ts +29 -0
- package/dist/cjs/constants/error.constants.js +50 -0
- package/dist/cjs/constants/index.d.ts +3 -6
- package/dist/cjs/constants/index.js +3 -6
- package/dist/cjs/constants/performance.constants.d.ts +28 -0
- package/dist/cjs/constants/performance.constants.js +43 -0
- package/dist/cjs/handlers/click.handler.d.ts +1 -0
- package/dist/cjs/handlers/click.handler.js +30 -49
- package/dist/cjs/handlers/error.handler.d.ts +11 -6
- package/dist/cjs/handlers/error.handler.js +91 -51
- package/dist/cjs/handlers/page-view.handler.js +38 -29
- package/dist/cjs/handlers/performance.handler.d.ts +3 -0
- package/dist/cjs/handlers/performance.handler.js +76 -37
- package/dist/cjs/handlers/scroll.handler.d.ts +15 -0
- package/dist/cjs/handlers/scroll.handler.js +105 -31
- package/dist/cjs/handlers/session.handler.d.ts +6 -20
- package/dist/cjs/handlers/session.handler.js +38 -326
- package/dist/cjs/integrations/google-analytics.integration.d.ts +0 -1
- package/dist/cjs/integrations/google-analytics.integration.js +27 -98
- package/dist/cjs/listeners/input-listener-managers.d.ts +18 -9
- package/dist/cjs/listeners/input-listener-managers.js +24 -33
- package/dist/cjs/listeners/touch-listener-manager.d.ts +1 -3
- package/dist/cjs/listeners/touch-listener-manager.js +1 -23
- package/dist/cjs/listeners/visibility-listener-manager.d.ts +1 -4
- package/dist/cjs/listeners/visibility-listener-manager.js +6 -42
- package/dist/cjs/managers/api.manager.d.ts +13 -3
- package/dist/cjs/managers/api.manager.js +35 -5
- package/dist/cjs/managers/config.manager.d.ts +53 -3
- package/dist/cjs/managers/config.manager.js +131 -62
- package/dist/cjs/managers/event.manager.d.ts +57 -36
- package/dist/cjs/managers/event.manager.js +266 -417
- package/dist/cjs/managers/sender.manager.d.ts +40 -22
- package/dist/cjs/managers/sender.manager.js +200 -198
- package/dist/cjs/managers/session.manager.d.ts +80 -66
- package/dist/cjs/managers/session.manager.js +267 -522
- package/dist/cjs/managers/state.manager.d.ts +33 -0
- package/dist/cjs/managers/state.manager.js +79 -6
- package/dist/cjs/managers/storage.manager.d.ts +26 -2
- package/dist/cjs/managers/storage.manager.js +67 -34
- package/dist/cjs/managers/tags.manager.d.ts +31 -7
- package/dist/cjs/managers/tags.manager.js +123 -241
- package/dist/cjs/managers/user.manager.d.ts +14 -5
- package/dist/cjs/managers/user.manager.js +17 -9
- package/dist/cjs/public-api.d.ts +10 -1
- package/dist/cjs/public-api.js +18 -24
- package/dist/cjs/test-bridge.d.ts +48 -0
- package/dist/cjs/test-bridge.js +110 -0
- package/dist/cjs/types/api.types.d.ts +21 -6
- package/dist/cjs/types/api.types.js +21 -6
- package/dist/cjs/types/config.types.d.ts +22 -84
- package/dist/cjs/types/emitter.types.d.ts +11 -0
- package/dist/cjs/types/emitter.types.js +8 -0
- package/dist/cjs/types/event.types.d.ts +8 -11
- package/dist/cjs/types/index.d.ts +3 -1
- package/dist/cjs/types/index.js +3 -1
- package/dist/cjs/types/queue.types.d.ts +1 -0
- package/dist/cjs/types/session.types.d.ts +0 -64
- package/dist/cjs/types/state.types.d.ts +1 -0
- package/dist/cjs/types/test-bridge.types.d.ts +38 -0
- package/dist/cjs/types/validation-error.types.d.ts +7 -0
- package/dist/cjs/types/validation-error.types.js +11 -1
- package/dist/cjs/types/window.types.d.ts +1 -8
- package/dist/cjs/utils/data/uuid.utils.d.ts +1 -1
- package/dist/cjs/utils/data/uuid.utils.js +7 -5
- package/dist/cjs/utils/emitter.utils.d.ts +8 -0
- package/dist/cjs/utils/emitter.utils.js +33 -0
- package/dist/cjs/utils/index.d.ts +1 -0
- package/dist/cjs/utils/index.js +1 -0
- package/dist/cjs/utils/logging/debug-logger.utils.d.ts +10 -51
- package/dist/cjs/utils/logging/debug-logger.utils.js +36 -127
- package/dist/cjs/utils/network/fetch-with-timeout.utils.d.ts +4 -0
- package/dist/cjs/utils/network/fetch-with-timeout.utils.js +25 -0
- package/dist/cjs/utils/network/index.d.ts +1 -0
- package/dist/cjs/utils/network/index.js +1 -0
- package/dist/cjs/utils/network/url.utils.js +2 -42
- package/dist/cjs/utils/security/sanitize.utils.d.ts +1 -8
- package/dist/cjs/utils/security/sanitize.utils.js +7 -41
- package/dist/cjs/utils/validations/config-validations.utils.d.ts +7 -0
- package/dist/cjs/utils/validations/config-validations.utils.js +77 -22
- package/dist/esm/api.d.ts +33 -19
- package/dist/esm/api.js +105 -118
- package/dist/esm/app.constants.d.ts +80 -1
- package/dist/esm/app.constants.js +89 -1
- package/dist/esm/app.d.ts +29 -44
- package/dist/esm/app.js +115 -213
- package/dist/esm/app.types.d.ts +2 -7
- package/dist/esm/app.types.js +1 -7
- package/dist/esm/constants/api.constants.js +10 -4
- package/dist/esm/constants/config.constants.d.ts +75 -0
- package/dist/esm/constants/config.constants.js +174 -0
- package/dist/esm/constants/error.constants.d.ts +29 -0
- package/dist/esm/constants/error.constants.js +47 -0
- package/dist/esm/constants/index.d.ts +3 -6
- package/dist/esm/constants/index.js +3 -6
- package/dist/esm/constants/performance.constants.d.ts +28 -0
- package/dist/esm/constants/performance.constants.js +40 -0
- package/dist/esm/handlers/click.handler.d.ts +1 -0
- package/dist/esm/handlers/click.handler.js +30 -49
- package/dist/esm/handlers/error.handler.d.ts +11 -6
- package/dist/esm/handlers/error.handler.js +91 -51
- package/dist/esm/handlers/page-view.handler.js +38 -29
- package/dist/esm/handlers/performance.handler.d.ts +3 -0
- package/dist/esm/handlers/performance.handler.js +71 -32
- package/dist/esm/handlers/scroll.handler.d.ts +15 -0
- package/dist/esm/handlers/scroll.handler.js +106 -32
- package/dist/esm/handlers/session.handler.d.ts +6 -20
- package/dist/esm/handlers/session.handler.js +38 -326
- package/dist/esm/integrations/google-analytics.integration.d.ts +0 -1
- package/dist/esm/integrations/google-analytics.integration.js +27 -98
- package/dist/esm/listeners/input-listener-managers.d.ts +18 -9
- package/dist/esm/listeners/input-listener-managers.js +23 -32
- package/dist/esm/listeners/touch-listener-manager.d.ts +1 -3
- package/dist/esm/listeners/touch-listener-manager.js +1 -23
- package/dist/esm/listeners/visibility-listener-manager.d.ts +1 -4
- package/dist/esm/listeners/visibility-listener-manager.js +6 -42
- package/dist/esm/managers/api.manager.d.ts +13 -3
- package/dist/esm/managers/api.manager.js +34 -3
- package/dist/esm/managers/config.manager.d.ts +53 -3
- package/dist/esm/managers/config.manager.js +133 -64
- package/dist/esm/managers/event.manager.d.ts +57 -36
- package/dist/esm/managers/event.manager.js +268 -419
- package/dist/esm/managers/sender.manager.d.ts +40 -22
- package/dist/esm/managers/sender.manager.js +201 -199
- package/dist/esm/managers/session.manager.d.ts +80 -66
- package/dist/esm/managers/session.manager.js +269 -524
- package/dist/esm/managers/state.manager.d.ts +33 -0
- package/dist/esm/managers/state.manager.js +78 -6
- package/dist/esm/managers/storage.manager.d.ts +26 -2
- package/dist/esm/managers/storage.manager.js +66 -33
- package/dist/esm/managers/tags.manager.d.ts +31 -7
- package/dist/esm/managers/tags.manager.js +124 -242
- package/dist/esm/managers/user.manager.d.ts +14 -5
- package/dist/esm/managers/user.manager.js +17 -9
- package/dist/esm/public-api.d.ts +10 -1
- package/dist/esm/public-api.js +14 -1
- package/dist/esm/test-bridge.d.ts +48 -0
- package/dist/esm/test-bridge.js +106 -0
- package/dist/esm/types/api.types.d.ts +21 -6
- package/dist/esm/types/api.types.js +21 -6
- package/dist/esm/types/config.types.d.ts +22 -84
- package/dist/esm/types/emitter.types.d.ts +11 -0
- package/dist/esm/types/emitter.types.js +5 -0
- package/dist/esm/types/event.types.d.ts +8 -11
- package/dist/esm/types/index.d.ts +3 -1
- package/dist/esm/types/index.js +3 -1
- package/dist/esm/types/queue.types.d.ts +1 -0
- package/dist/esm/types/session.types.d.ts +0 -64
- package/dist/esm/types/state.types.d.ts +1 -0
- package/dist/esm/types/test-bridge.types.d.ts +38 -0
- package/dist/esm/types/validation-error.types.d.ts +7 -0
- package/dist/esm/types/validation-error.types.js +9 -0
- package/dist/esm/types/window.types.d.ts +1 -8
- package/dist/esm/utils/data/uuid.utils.d.ts +1 -1
- package/dist/esm/utils/data/uuid.utils.js +7 -5
- package/dist/esm/utils/emitter.utils.d.ts +8 -0
- package/dist/esm/utils/emitter.utils.js +29 -0
- package/dist/esm/utils/index.d.ts +1 -0
- package/dist/esm/utils/index.js +1 -0
- package/dist/esm/utils/logging/debug-logger.utils.d.ts +10 -51
- package/dist/esm/utils/logging/debug-logger.utils.js +36 -127
- package/dist/esm/utils/network/fetch-with-timeout.utils.d.ts +4 -0
- package/dist/esm/utils/network/fetch-with-timeout.utils.js +22 -0
- package/dist/esm/utils/network/index.d.ts +1 -0
- package/dist/esm/utils/network/index.js +1 -0
- package/dist/esm/utils/network/url.utils.js +2 -42
- package/dist/esm/utils/security/sanitize.utils.d.ts +1 -8
- package/dist/esm/utils/security/sanitize.utils.js +6 -39
- package/dist/esm/utils/validations/config-validations.utils.d.ts +7 -0
- package/dist/esm/utils/validations/config-validations.utils.js +76 -22
- package/package.json +23 -16
- package/dist/browser/web-vitals-CCnqwnC8.mjs +0 -198
- package/dist/cjs/constants/browser.constants.d.ts +0 -3
- package/dist/cjs/constants/browser.constants.js +0 -41
- package/dist/cjs/constants/initialization.constants.d.ts +0 -40
- package/dist/cjs/constants/initialization.constants.js +0 -48
- package/dist/cjs/constants/limits.constants.d.ts +0 -25
- package/dist/cjs/constants/limits.constants.js +0 -40
- package/dist/cjs/constants/security.constants.d.ts +0 -1
- package/dist/cjs/constants/security.constants.js +0 -12
- package/dist/cjs/constants/timing.constants.d.ts +0 -22
- package/dist/cjs/constants/timing.constants.js +0 -34
- package/dist/cjs/constants/validation.constants.d.ts +0 -13
- package/dist/cjs/constants/validation.constants.js +0 -31
- package/dist/cjs/handlers/network.handler.d.ts +0 -16
- package/dist/cjs/handlers/network.handler.js +0 -136
- package/dist/cjs/managers/cross-tab-session.manager.d.ts +0 -170
- package/dist/cjs/managers/cross-tab-session.manager.js +0 -730
- package/dist/cjs/managers/sampling.manager.d.ts +0 -8
- package/dist/cjs/managers/sampling.manager.js +0 -53
- package/dist/cjs/managers/session-recovery.manager.d.ts +0 -65
- package/dist/cjs/managers/session-recovery.manager.js +0 -237
- package/dist/cjs/types/web-vitals.types.d.ts +0 -6
- package/dist/esm/constants/browser.constants.d.ts +0 -3
- package/dist/esm/constants/browser.constants.js +0 -38
- package/dist/esm/constants/initialization.constants.d.ts +0 -40
- package/dist/esm/constants/initialization.constants.js +0 -45
- package/dist/esm/constants/limits.constants.d.ts +0 -25
- package/dist/esm/constants/limits.constants.js +0 -37
- package/dist/esm/constants/security.constants.d.ts +0 -1
- package/dist/esm/constants/security.constants.js +0 -9
- package/dist/esm/constants/timing.constants.d.ts +0 -22
- package/dist/esm/constants/timing.constants.js +0 -31
- package/dist/esm/constants/validation.constants.d.ts +0 -13
- package/dist/esm/constants/validation.constants.js +0 -28
- package/dist/esm/handlers/network.handler.d.ts +0 -16
- package/dist/esm/handlers/network.handler.js +0 -132
- package/dist/esm/managers/cross-tab-session.manager.d.ts +0 -170
- package/dist/esm/managers/cross-tab-session.manager.js +0 -726
- package/dist/esm/managers/sampling.manager.d.ts +0 -8
- package/dist/esm/managers/sampling.manager.js +0 -49
- package/dist/esm/managers/session-recovery.manager.d.ts +0 -65
- package/dist/esm/managers/session-recovery.manager.js +0 -233
- package/dist/esm/types/web-vitals.types.d.ts +0 -6
- /package/dist/cjs/types/{web-vitals.types.js → test-bridge.types.js} +0 -0
- /package/dist/esm/types/{web-vitals.types.js → test-bridge.types.js} +0 -0
|
@@ -3,95 +3,135 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ErrorHandler = void 0;
|
|
4
4
|
const state_manager_1 = require("../managers/state.manager");
|
|
5
5
|
const types_1 = require("../types");
|
|
6
|
+
const error_constants_1 = require("../constants/error.constants");
|
|
6
7
|
const logging_1 = require("../utils/logging");
|
|
8
|
+
/**
|
|
9
|
+
* Simplified error handler for tracking JavaScript errors and unhandled promise rejections
|
|
10
|
+
* Includes PII sanitization and sampling support
|
|
11
|
+
*/
|
|
7
12
|
class ErrorHandler extends state_manager_1.StateManager {
|
|
8
13
|
constructor(eventManager) {
|
|
9
14
|
super();
|
|
10
|
-
this.
|
|
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
|
-
];
|
|
15
|
+
this.recentErrors = new Map();
|
|
16
16
|
this.handleError = (event) => {
|
|
17
|
-
|
|
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
|
-
});
|
|
17
|
+
if (!this.shouldSample()) {
|
|
22
18
|
return;
|
|
23
19
|
}
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
const sanitizedMessage = this.sanitize(event.message || 'Unknown error');
|
|
21
|
+
if (this.shouldSuppressError(types_1.ErrorType.JS_ERROR, sanitizedMessage)) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
logging_1.debugLog.warn('ErrorHandler', 'JS error captured', {
|
|
25
|
+
message: sanitizedMessage,
|
|
26
26
|
filename: event.filename,
|
|
27
|
-
|
|
27
|
+
line: event.lineno,
|
|
28
28
|
});
|
|
29
29
|
this.eventManager.track({
|
|
30
30
|
type: types_1.EventType.ERROR,
|
|
31
31
|
error_data: {
|
|
32
32
|
type: types_1.ErrorType.JS_ERROR,
|
|
33
|
-
message:
|
|
33
|
+
message: sanitizedMessage,
|
|
34
|
+
...(event.filename && { filename: event.filename }),
|
|
35
|
+
...(event.lineno && { line: event.lineno }),
|
|
36
|
+
...(event.colno && { column: event.colno }),
|
|
34
37
|
},
|
|
35
38
|
});
|
|
36
39
|
};
|
|
37
|
-
this.
|
|
38
|
-
|
|
39
|
-
if (!this.shouldSample(config?.errorSampling ?? 0.1)) {
|
|
40
|
-
logging_1.debugLog.debug('ErrorHandler', 'Promise rejection not sampled, skipping', {
|
|
41
|
-
errorSampling: config?.errorSampling,
|
|
42
|
-
});
|
|
40
|
+
this.handleRejection = (event) => {
|
|
41
|
+
if (!this.shouldSample()) {
|
|
43
42
|
return;
|
|
44
43
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
}
|
|
44
|
+
const message = this.extractRejectionMessage(event.reason);
|
|
45
|
+
const sanitizedMessage = this.sanitize(message);
|
|
46
|
+
if (this.shouldSuppressError(types_1.ErrorType.PROMISE_REJECTION, sanitizedMessage)) {
|
|
47
|
+
return;
|
|
59
48
|
}
|
|
49
|
+
logging_1.debugLog.warn('ErrorHandler', 'Promise rejection captured', { message: sanitizedMessage });
|
|
60
50
|
this.eventManager.track({
|
|
61
51
|
type: types_1.EventType.ERROR,
|
|
62
52
|
error_data: {
|
|
63
53
|
type: types_1.ErrorType.PROMISE_REJECTION,
|
|
64
|
-
message:
|
|
54
|
+
message: sanitizedMessage,
|
|
65
55
|
},
|
|
66
56
|
});
|
|
67
57
|
};
|
|
68
58
|
this.eventManager = eventManager;
|
|
69
59
|
}
|
|
70
60
|
startTracking() {
|
|
71
|
-
|
|
72
|
-
this.
|
|
73
|
-
this.setupUnhandledRejectionListener();
|
|
61
|
+
window.addEventListener('error', this.handleError);
|
|
62
|
+
window.addEventListener('unhandledrejection', this.handleRejection);
|
|
74
63
|
}
|
|
75
64
|
stopTracking() {
|
|
76
|
-
logging_1.debugLog.debug('ErrorHandler', 'Stopping error tracking');
|
|
77
65
|
window.removeEventListener('error', this.handleError);
|
|
78
|
-
window.removeEventListener('unhandledrejection', this.
|
|
66
|
+
window.removeEventListener('unhandledrejection', this.handleRejection);
|
|
67
|
+
this.recentErrors.clear();
|
|
79
68
|
}
|
|
80
|
-
|
|
81
|
-
|
|
69
|
+
shouldSample() {
|
|
70
|
+
const config = this.get('config');
|
|
71
|
+
const samplingRate = config?.errorSampling ?? 0.1;
|
|
72
|
+
return Math.random() < samplingRate;
|
|
82
73
|
}
|
|
83
|
-
|
|
84
|
-
|
|
74
|
+
extractRejectionMessage(reason) {
|
|
75
|
+
if (!reason)
|
|
76
|
+
return 'Unknown rejection';
|
|
77
|
+
if (typeof reason === 'string')
|
|
78
|
+
return reason;
|
|
79
|
+
if (reason instanceof Error) {
|
|
80
|
+
return reason.stack ?? reason.message ?? reason.toString();
|
|
81
|
+
}
|
|
82
|
+
// Handle objects with message property
|
|
83
|
+
if (typeof reason === 'object' && 'message' in reason) {
|
|
84
|
+
return String(reason.message);
|
|
85
|
+
}
|
|
86
|
+
// Try to stringify objects
|
|
87
|
+
try {
|
|
88
|
+
return JSON.stringify(reason);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return String(reason);
|
|
92
|
+
}
|
|
85
93
|
}
|
|
86
|
-
|
|
87
|
-
let sanitized = text;
|
|
88
|
-
for (const pattern of
|
|
89
|
-
|
|
94
|
+
sanitize(text) {
|
|
95
|
+
let sanitized = text.length > error_constants_1.MAX_ERROR_MESSAGE_LENGTH ? text.slice(0, error_constants_1.MAX_ERROR_MESSAGE_LENGTH) + '...' : text;
|
|
96
|
+
for (const pattern of error_constants_1.PII_PATTERNS) {
|
|
97
|
+
// Create new regex instance to avoid global flag state issues
|
|
98
|
+
const regex = new RegExp(pattern.source, pattern.flags);
|
|
99
|
+
sanitized = sanitized.replace(regex, '[REDACTED]');
|
|
90
100
|
}
|
|
91
101
|
return sanitized;
|
|
92
102
|
}
|
|
93
|
-
|
|
94
|
-
|
|
103
|
+
shouldSuppressError(type, message) {
|
|
104
|
+
const now = Date.now();
|
|
105
|
+
const key = `${type}:${message}`;
|
|
106
|
+
const lastSeenAt = this.recentErrors.get(key);
|
|
107
|
+
if (lastSeenAt && now - lastSeenAt < error_constants_1.ERROR_SUPPRESSION_WINDOW_MS) {
|
|
108
|
+
this.recentErrors.set(key, now);
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
this.recentErrors.set(key, now);
|
|
112
|
+
if (this.recentErrors.size > error_constants_1.MAX_TRACKED_ERRORS) {
|
|
113
|
+
this.pruneOldErrors();
|
|
114
|
+
}
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
pruneOldErrors() {
|
|
118
|
+
const now = Date.now();
|
|
119
|
+
for (const [key, timestamp] of this.recentErrors.entries()) {
|
|
120
|
+
if (now - timestamp > error_constants_1.ERROR_SUPPRESSION_WINDOW_MS) {
|
|
121
|
+
this.recentErrors.delete(key);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (this.recentErrors.size <= error_constants_1.MAX_TRACKED_ERRORS) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const entries = Array.from(this.recentErrors.entries()).sort((a, b) => a[1] - b[1]);
|
|
128
|
+
const excess = this.recentErrors.size - error_constants_1.MAX_TRACKED_ERRORS;
|
|
129
|
+
for (let index = 0; index < excess; index += 1) {
|
|
130
|
+
const entry = entries[index];
|
|
131
|
+
if (entry) {
|
|
132
|
+
this.recentErrors.delete(entry[0]);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
95
135
|
}
|
|
96
136
|
}
|
|
97
137
|
exports.ErrorHandler = ErrorHandler;
|
|
@@ -8,21 +8,23 @@ const logging_1 = require("../utils/logging");
|
|
|
8
8
|
class PageViewHandler extends state_manager_1.StateManager {
|
|
9
9
|
constructor(eventManager, onTrack) {
|
|
10
10
|
super();
|
|
11
|
-
this.trackCurrentPage = () => {
|
|
11
|
+
this.trackCurrentPage = async () => {
|
|
12
12
|
const rawUrl = window.location.href;
|
|
13
13
|
const normalizedUrl = (0, utils_1.normalizeUrl)(rawUrl, this.get('config').sensitiveQueryParams);
|
|
14
|
-
if (this.get('pageUrl')
|
|
15
|
-
|
|
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();
|
|
14
|
+
if (this.get('pageUrl') === normalizedUrl) {
|
|
15
|
+
return;
|
|
25
16
|
}
|
|
17
|
+
this.onTrack();
|
|
18
|
+
const fromUrl = this.get('pageUrl');
|
|
19
|
+
logging_1.debugLog.debug('PageViewHandler', 'Page navigation detected', { from: fromUrl, to: normalizedUrl });
|
|
20
|
+
this.set('pageUrl', normalizedUrl);
|
|
21
|
+
const pageViewData = this.extractPageViewData();
|
|
22
|
+
this.eventManager.track({
|
|
23
|
+
type: types_1.EventType.PAGE_VIEW,
|
|
24
|
+
page_url: this.get('pageUrl'),
|
|
25
|
+
from_page_url: fromUrl,
|
|
26
|
+
...(pageViewData && { page_view: pageViewData }),
|
|
27
|
+
});
|
|
26
28
|
};
|
|
27
29
|
this.eventManager = eventManager;
|
|
28
30
|
this.onTrack = onTrack;
|
|
@@ -30,16 +32,15 @@ class PageViewHandler extends state_manager_1.StateManager {
|
|
|
30
32
|
startTracking() {
|
|
31
33
|
logging_1.debugLog.debug('PageViewHandler', 'Starting page view tracking');
|
|
32
34
|
this.trackInitialPageView();
|
|
33
|
-
this.trackCurrentPage
|
|
34
|
-
window.addEventListener('
|
|
35
|
-
window.addEventListener('hashchange', this.trackCurrentPage);
|
|
35
|
+
window.addEventListener('popstate', this.trackCurrentPage, true);
|
|
36
|
+
window.addEventListener('hashchange', this.trackCurrentPage, true);
|
|
36
37
|
this.patchHistory('pushState');
|
|
37
38
|
this.patchHistory('replaceState');
|
|
38
39
|
}
|
|
39
40
|
stopTracking() {
|
|
40
41
|
logging_1.debugLog.debug('PageViewHandler', 'Stopping page view tracking');
|
|
41
|
-
window.removeEventListener('popstate', this.trackCurrentPage);
|
|
42
|
-
window.removeEventListener('hashchange', this.trackCurrentPage);
|
|
42
|
+
window.removeEventListener('popstate', this.trackCurrentPage, true);
|
|
43
|
+
window.removeEventListener('hashchange', this.trackCurrentPage, true);
|
|
43
44
|
if (this.originalPushState) {
|
|
44
45
|
window.history.pushState = this.originalPushState;
|
|
45
46
|
}
|
|
@@ -48,36 +49,44 @@ class PageViewHandler extends state_manager_1.StateManager {
|
|
|
48
49
|
}
|
|
49
50
|
}
|
|
50
51
|
patchHistory(method) {
|
|
52
|
+
const original = window.history[method];
|
|
51
53
|
if (method === 'pushState' && !this.originalPushState) {
|
|
52
|
-
this.originalPushState =
|
|
54
|
+
this.originalPushState = original;
|
|
53
55
|
}
|
|
54
56
|
else if (method === 'replaceState' && !this.originalReplaceState) {
|
|
55
|
-
this.originalReplaceState =
|
|
57
|
+
this.originalReplaceState = original;
|
|
56
58
|
}
|
|
57
|
-
const original = window.history[method];
|
|
58
59
|
window.history[method] = (...args) => {
|
|
59
60
|
original.apply(window.history, args);
|
|
60
61
|
this.trackCurrentPage();
|
|
61
62
|
};
|
|
62
63
|
}
|
|
63
64
|
trackInitialPageView() {
|
|
65
|
+
const normalizedUrl = (0, utils_1.normalizeUrl)(window.location.href, this.get('config').sensitiveQueryParams);
|
|
66
|
+
const pageViewData = this.extractPageViewData();
|
|
64
67
|
this.eventManager.track({
|
|
65
68
|
type: types_1.EventType.PAGE_VIEW,
|
|
66
|
-
page_url:
|
|
67
|
-
...(
|
|
69
|
+
page_url: normalizedUrl,
|
|
70
|
+
...(pageViewData && { page_view: pageViewData }),
|
|
68
71
|
});
|
|
69
72
|
this.onTrack();
|
|
70
73
|
}
|
|
71
74
|
extractPageViewData() {
|
|
72
|
-
const
|
|
75
|
+
const { pathname, search, hash } = window.location;
|
|
76
|
+
const { referrer } = document;
|
|
77
|
+
const { title } = document;
|
|
78
|
+
// Early return if no meaningful data
|
|
79
|
+
if (!referrer && !title && !pathname && !search && !hash) {
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
73
82
|
const data = {
|
|
74
|
-
...(
|
|
75
|
-
...(
|
|
76
|
-
...(
|
|
77
|
-
...(
|
|
78
|
-
...(
|
|
83
|
+
...(referrer && { referrer }),
|
|
84
|
+
...(title && { title }),
|
|
85
|
+
...(pathname && { pathname }),
|
|
86
|
+
...(search && { search }),
|
|
87
|
+
...(hash && { hash }),
|
|
79
88
|
};
|
|
80
|
-
return
|
|
89
|
+
return data;
|
|
81
90
|
}
|
|
82
91
|
}
|
|
83
92
|
exports.PageViewHandler = PageViewHandler;
|
|
@@ -5,6 +5,7 @@ export declare class PerformanceHandler extends StateManager {
|
|
|
5
5
|
private readonly reportedByNav;
|
|
6
6
|
private readonly observers;
|
|
7
7
|
private lastLongTaskSentAt;
|
|
8
|
+
private readonly vitalThresholds;
|
|
8
9
|
constructor(eventManager: EventManager);
|
|
9
10
|
startTracking(): Promise<void>;
|
|
10
11
|
stopTracking(): void;
|
|
@@ -15,5 +16,7 @@ export declare class PerformanceHandler extends StateManager {
|
|
|
15
16
|
private sendVital;
|
|
16
17
|
private trackWebVital;
|
|
17
18
|
private getNavigationId;
|
|
19
|
+
private isObserverSupported;
|
|
18
20
|
private safeObserve;
|
|
21
|
+
private shouldSendVital;
|
|
19
22
|
}
|
|
@@ -37,7 +37,7 @@ exports.PerformanceHandler = void 0;
|
|
|
37
37
|
const state_manager_1 = require("../managers/state.manager");
|
|
38
38
|
const types_1 = require("../types");
|
|
39
39
|
const constants_1 = require("../constants");
|
|
40
|
-
const
|
|
40
|
+
const performance_constants_1 = require("../constants/performance.constants");
|
|
41
41
|
const logging_1 = require("../utils/logging");
|
|
42
42
|
class PerformanceHandler extends state_manager_1.StateManager {
|
|
43
43
|
constructor(eventManager) {
|
|
@@ -45,16 +45,14 @@ class PerformanceHandler extends state_manager_1.StateManager {
|
|
|
45
45
|
this.reportedByNav = new Map();
|
|
46
46
|
this.observers = [];
|
|
47
47
|
this.lastLongTaskSentAt = 0;
|
|
48
|
+
this.vitalThresholds = performance_constants_1.WEB_VITALS_THRESHOLDS;
|
|
48
49
|
this.eventManager = eventManager;
|
|
49
50
|
}
|
|
50
51
|
async startTracking() {
|
|
51
|
-
logging_1.debugLog.debug('PerformanceHandler', 'Starting performance tracking');
|
|
52
52
|
await this.initWebVitals();
|
|
53
53
|
this.observeLongTasks();
|
|
54
|
-
this.reportTTFB();
|
|
55
54
|
}
|
|
56
55
|
stopTracking() {
|
|
57
|
-
logging_1.debugLog.debug('PerformanceHandler', 'Stopping performance tracking', { observersCount: this.observers.length });
|
|
58
56
|
this.observers.forEach((obs, index) => {
|
|
59
57
|
try {
|
|
60
58
|
obs.disconnect();
|
|
@@ -68,10 +66,6 @@ class PerformanceHandler extends state_manager_1.StateManager {
|
|
|
68
66
|
});
|
|
69
67
|
this.observers.length = 0;
|
|
70
68
|
this.reportedByNav.clear();
|
|
71
|
-
logging_1.debugLog.debug('PerformanceHandler', 'Performance tracking cleanup completed', {
|
|
72
|
-
remainingObservers: this.observers.length,
|
|
73
|
-
clearedNavReports: true,
|
|
74
|
-
});
|
|
75
69
|
}
|
|
76
70
|
observeWebVitalsFallback() {
|
|
77
71
|
// TTFB - should be captured immediately as it's available from navigation timing
|
|
@@ -83,11 +77,18 @@ class PerformanceHandler extends state_manager_1.StateManager {
|
|
|
83
77
|
if (!last) {
|
|
84
78
|
return;
|
|
85
79
|
}
|
|
86
|
-
this.sendVital({ type: 'LCP', value: Number(last.startTime.toFixed(
|
|
80
|
+
this.sendVital({ type: 'LCP', value: Number(last.startTime.toFixed(constants_1.PRECISION_TWO_DECIMALS)) });
|
|
87
81
|
}, { type: 'largest-contentful-paint', buffered: true }, true);
|
|
88
82
|
// CLS (layout-shift)
|
|
89
83
|
let clsValue = 0;
|
|
84
|
+
let currentNavId = this.getNavigationId();
|
|
90
85
|
this.safeObserve('layout-shift', (list) => {
|
|
86
|
+
const navId = this.getNavigationId();
|
|
87
|
+
// Reset CLS on navigation change
|
|
88
|
+
if (navId !== currentNavId) {
|
|
89
|
+
clsValue = 0;
|
|
90
|
+
currentNavId = navId;
|
|
91
|
+
}
|
|
91
92
|
const entries = list.getEntries();
|
|
92
93
|
for (const entry of entries) {
|
|
93
94
|
if (entry.hadRecentInput === true) {
|
|
@@ -96,13 +97,13 @@ class PerformanceHandler extends state_manager_1.StateManager {
|
|
|
96
97
|
const value = typeof entry.value === 'number' ? entry.value : 0;
|
|
97
98
|
clsValue += value;
|
|
98
99
|
}
|
|
99
|
-
this.sendVital({ type: 'CLS', value: Number(clsValue.toFixed(
|
|
100
|
+
this.sendVital({ type: 'CLS', value: Number(clsValue.toFixed(constants_1.PRECISION_TWO_DECIMALS)) });
|
|
100
101
|
}, { type: 'layout-shift', buffered: true });
|
|
101
102
|
// FCP
|
|
102
103
|
this.safeObserve('paint', (list) => {
|
|
103
104
|
for (const entry of list.getEntries()) {
|
|
104
105
|
if (entry.name === 'first-contentful-paint') {
|
|
105
|
-
this.sendVital({ type: 'FCP', value: Number(entry.startTime.toFixed(
|
|
106
|
+
this.sendVital({ type: 'FCP', value: Number(entry.startTime.toFixed(constants_1.PRECISION_TWO_DECIMALS)) });
|
|
106
107
|
}
|
|
107
108
|
}
|
|
108
109
|
}, { type: 'paint', buffered: true }, true);
|
|
@@ -115,7 +116,7 @@ class PerformanceHandler extends state_manager_1.StateManager {
|
|
|
115
116
|
worst = Math.max(worst, dur);
|
|
116
117
|
}
|
|
117
118
|
if (worst > 0) {
|
|
118
|
-
this.sendVital({ type: 'INP', value: Number(worst.toFixed(
|
|
119
|
+
this.sendVital({ type: 'INP', value: Number(worst.toFixed(constants_1.PRECISION_TWO_DECIMALS)) });
|
|
119
120
|
}
|
|
120
121
|
}, { type: 'event', buffered: true });
|
|
121
122
|
}
|
|
@@ -123,7 +124,7 @@ class PerformanceHandler extends state_manager_1.StateManager {
|
|
|
123
124
|
try {
|
|
124
125
|
const { onLCP, onCLS, onFCP, onTTFB, onINP } = await Promise.resolve().then(() => __importStar(require('web-vitals')));
|
|
125
126
|
const report = (type) => (metric) => {
|
|
126
|
-
const value = Number(metric.value.toFixed(
|
|
127
|
+
const value = Number(metric.value.toFixed(constants_1.PRECISION_TWO_DECIMALS));
|
|
127
128
|
this.sendVital({ type, value });
|
|
128
129
|
};
|
|
129
130
|
onLCP(report('LCP'));
|
|
@@ -143,7 +144,6 @@ class PerformanceHandler extends state_manager_1.StateManager {
|
|
|
143
144
|
try {
|
|
144
145
|
const nav = performance.getEntriesByType('navigation')[0];
|
|
145
146
|
if (!nav) {
|
|
146
|
-
logging_1.debugLog.debug('PerformanceHandler', 'Navigation timing not available for TTFB');
|
|
147
147
|
return;
|
|
148
148
|
}
|
|
149
149
|
const ttfb = nav.responseStart;
|
|
@@ -153,10 +153,7 @@ class PerformanceHandler extends state_manager_1.StateManager {
|
|
|
153
153
|
// - Browser cannot determine exact timing
|
|
154
154
|
// We still report it as it's a valid measurement
|
|
155
155
|
if (typeof ttfb === 'number' && Number.isFinite(ttfb)) {
|
|
156
|
-
this.sendVital({ type: 'TTFB', value: Number(ttfb.toFixed(
|
|
157
|
-
}
|
|
158
|
-
else {
|
|
159
|
-
logging_1.debugLog.debug('PerformanceHandler', 'TTFB value is not a valid number', { ttfb });
|
|
156
|
+
this.sendVital({ type: 'TTFB', value: Number(ttfb.toFixed(constants_1.PRECISION_TWO_DECIMALS)) });
|
|
160
157
|
}
|
|
161
158
|
}
|
|
162
159
|
catch (error) {
|
|
@@ -169,32 +166,41 @@ class PerformanceHandler extends state_manager_1.StateManager {
|
|
|
169
166
|
this.safeObserve('longtask', (list) => {
|
|
170
167
|
const entries = list.getEntries();
|
|
171
168
|
for (const entry of entries) {
|
|
172
|
-
const duration = Number(entry.duration.toFixed(
|
|
169
|
+
const duration = Number(entry.duration.toFixed(constants_1.PRECISION_TWO_DECIMALS));
|
|
173
170
|
const now = Date.now();
|
|
174
171
|
if (now - this.lastLongTaskSentAt >= constants_1.LONG_TASK_THROTTLE_MS) {
|
|
175
|
-
this.
|
|
172
|
+
if (this.shouldSendVital('LONG_TASK', duration)) {
|
|
173
|
+
this.trackWebVital('LONG_TASK', duration);
|
|
174
|
+
}
|
|
176
175
|
this.lastLongTaskSentAt = now;
|
|
177
176
|
}
|
|
178
177
|
}
|
|
179
178
|
}, { type: 'longtask', buffered: true });
|
|
180
179
|
}
|
|
181
180
|
sendVital(sample) {
|
|
181
|
+
if (!this.shouldSendVital(sample.type, sample.value)) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
182
184
|
const navId = this.getNavigationId();
|
|
183
|
-
|
|
185
|
+
// Check for duplicates if we have a navigation ID
|
|
184
186
|
if (navId) {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
const sent = this.reportedByNav.get(navId);
|
|
189
|
-
if (sent.has(key)) {
|
|
187
|
+
const reportedForNav = this.reportedByNav.get(navId);
|
|
188
|
+
const isDuplicate = reportedForNav?.has(sample.type);
|
|
189
|
+
if (isDuplicate) {
|
|
190
190
|
return;
|
|
191
191
|
}
|
|
192
|
-
|
|
192
|
+
// Initialize or update reported vitals for this navigation
|
|
193
|
+
if (!reportedForNav) {
|
|
194
|
+
this.reportedByNav.set(navId, new Set([sample.type]));
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
reportedForNav.add(sample.type);
|
|
198
|
+
}
|
|
193
199
|
}
|
|
194
200
|
this.trackWebVital(sample.type, sample.value);
|
|
195
201
|
}
|
|
196
202
|
trackWebVital(type, value) {
|
|
197
|
-
if (
|
|
203
|
+
if (!Number.isFinite(value)) {
|
|
198
204
|
logging_1.debugLog.warn('PerformanceHandler', 'Invalid web vital value', { type, value });
|
|
199
205
|
return;
|
|
200
206
|
}
|
|
@@ -212,7 +218,10 @@ class PerformanceHandler extends state_manager_1.StateManager {
|
|
|
212
218
|
if (!nav) {
|
|
213
219
|
return null;
|
|
214
220
|
}
|
|
215
|
-
|
|
221
|
+
// Use more precise timestamp and add random component to prevent collisions
|
|
222
|
+
const timestamp = nav.startTime || performance.now();
|
|
223
|
+
const random = Math.random().toString(36).substr(2, 5);
|
|
224
|
+
return `${timestamp.toFixed(2)}_${window.location.pathname}_${random}`;
|
|
216
225
|
}
|
|
217
226
|
catch (error) {
|
|
218
227
|
logging_1.debugLog.warn('PerformanceHandler', 'Failed to get navigation ID', {
|
|
@@ -221,35 +230,65 @@ class PerformanceHandler extends state_manager_1.StateManager {
|
|
|
221
230
|
return null;
|
|
222
231
|
}
|
|
223
232
|
}
|
|
233
|
+
isObserverSupported(type) {
|
|
234
|
+
if (typeof PerformanceObserver === 'undefined')
|
|
235
|
+
return false;
|
|
236
|
+
const supported = PerformanceObserver.supportedEntryTypes;
|
|
237
|
+
return !supported || supported.includes(type);
|
|
238
|
+
}
|
|
224
239
|
safeObserve(type, cb, options, once = false) {
|
|
225
240
|
try {
|
|
226
|
-
if (
|
|
227
|
-
return;
|
|
228
|
-
|
|
229
|
-
if (supported && !supported.includes(type))
|
|
230
|
-
return;
|
|
241
|
+
if (!this.isObserverSupported(type)) {
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
231
244
|
const obs = new PerformanceObserver((list, observer) => {
|
|
232
|
-
|
|
245
|
+
try {
|
|
246
|
+
cb(list, observer);
|
|
247
|
+
}
|
|
248
|
+
catch (callbackError) {
|
|
249
|
+
logging_1.debugLog.warn('PerformanceHandler', 'Observer callback failed', {
|
|
250
|
+
type,
|
|
251
|
+
error: callbackError instanceof Error ? callbackError.message : 'Unknown error',
|
|
252
|
+
});
|
|
253
|
+
}
|
|
233
254
|
if (once) {
|
|
234
255
|
try {
|
|
235
256
|
observer.disconnect();
|
|
236
257
|
}
|
|
237
258
|
catch {
|
|
238
|
-
//
|
|
259
|
+
// Disconnect errors are safe to ignore
|
|
239
260
|
}
|
|
240
261
|
}
|
|
241
262
|
});
|
|
242
|
-
obs.observe(
|
|
263
|
+
obs.observe(options ?? { type, buffered: true });
|
|
243
264
|
if (!once) {
|
|
244
265
|
this.observers.push(obs);
|
|
245
266
|
}
|
|
267
|
+
return true;
|
|
246
268
|
}
|
|
247
269
|
catch (error) {
|
|
248
270
|
logging_1.debugLog.warn('PerformanceHandler', 'Failed to create performance observer', {
|
|
249
271
|
type,
|
|
250
272
|
error: error instanceof Error ? error.message : 'Unknown error',
|
|
251
273
|
});
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
shouldSendVital(type, value) {
|
|
278
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
279
|
+
logging_1.debugLog.warn('PerformanceHandler', 'Invalid web vital value', { type, value });
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
const threshold = this.vitalThresholds[type];
|
|
283
|
+
if (typeof threshold === 'number' && value <= threshold) {
|
|
284
|
+
logging_1.debugLog.debug('PerformanceHandler', 'Web vital below threshold, skipping', {
|
|
285
|
+
type,
|
|
286
|
+
value,
|
|
287
|
+
threshold,
|
|
288
|
+
});
|
|
289
|
+
return false;
|
|
252
290
|
}
|
|
291
|
+
return true;
|
|
253
292
|
}
|
|
254
293
|
}
|
|
255
294
|
exports.PerformanceHandler = PerformanceHandler;
|
|
@@ -3,10 +3,25 @@ import { StateManager } from '../managers/state.manager';
|
|
|
3
3
|
export declare class ScrollHandler extends StateManager {
|
|
4
4
|
private readonly eventManager;
|
|
5
5
|
private readonly containers;
|
|
6
|
+
private limitWarningLogged;
|
|
7
|
+
private minDepthChange;
|
|
8
|
+
private minIntervalMs;
|
|
9
|
+
private maxEventsPerSession;
|
|
6
10
|
constructor(eventManager: EventManager);
|
|
7
11
|
startTracking(): void;
|
|
8
12
|
stopTracking(): void;
|
|
9
13
|
private setupScrollContainer;
|
|
14
|
+
private processScrollEvent;
|
|
15
|
+
private shouldEmitScrollEvent;
|
|
16
|
+
private hasReachedSessionLimit;
|
|
17
|
+
private hasElapsedMinimumInterval;
|
|
18
|
+
private hasSignificantDepthChange;
|
|
19
|
+
private logLimitOnce;
|
|
20
|
+
private applyConfigOverrides;
|
|
21
|
+
private isWindowScrollable;
|
|
22
|
+
private clearContainerTimer;
|
|
23
|
+
private getScrollDirection;
|
|
24
|
+
private calculateScrollDepth;
|
|
10
25
|
private calculateScrollData;
|
|
11
26
|
private getScrollTop;
|
|
12
27
|
private getViewportHeight;
|