@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,319 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sanitizeUrl = exports.sanitizeMetadata = exports.sanitizeApiConfig = exports.sanitizePathString = exports.sanitizeString = void 0;
|
|
4
|
+
const constants_1 = require("../../constants");
|
|
5
|
+
const logging_1 = require("../logging");
|
|
6
|
+
/**
|
|
7
|
+
* Sanitizes a string value to prevent XSS attacks
|
|
8
|
+
* @param value - The string to sanitize
|
|
9
|
+
* @returns The sanitized string
|
|
10
|
+
*/
|
|
11
|
+
const sanitizeString = (value) => {
|
|
12
|
+
if (!value || typeof value !== 'string' || value.trim().length === 0) {
|
|
13
|
+
logging_1.debugLog.debug('Sanitize', 'String sanitization skipped - empty or invalid input', { value, type: typeof value });
|
|
14
|
+
return '';
|
|
15
|
+
}
|
|
16
|
+
const originalLength = value.length;
|
|
17
|
+
let sanitized = value;
|
|
18
|
+
// Limit string length
|
|
19
|
+
if (value.length > constants_1.MAX_STRING_LENGTH) {
|
|
20
|
+
sanitized = value.slice(0, Math.max(0, constants_1.MAX_STRING_LENGTH));
|
|
21
|
+
logging_1.debugLog.warn('Sanitize', 'String truncated due to length limit', {
|
|
22
|
+
originalLength,
|
|
23
|
+
maxLength: constants_1.MAX_STRING_LENGTH,
|
|
24
|
+
truncatedLength: sanitized.length,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
// Remove potential XSS patterns
|
|
28
|
+
let xssPatternMatches = 0;
|
|
29
|
+
for (const pattern of constants_1.XSS_PATTERNS) {
|
|
30
|
+
const beforeReplace = sanitized;
|
|
31
|
+
sanitized = sanitized.replace(pattern, '');
|
|
32
|
+
if (beforeReplace !== sanitized) {
|
|
33
|
+
xssPatternMatches++;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (xssPatternMatches > 0) {
|
|
37
|
+
logging_1.debugLog.warn('Sanitize', 'XSS patterns detected and removed', {
|
|
38
|
+
patternMatches: xssPatternMatches,
|
|
39
|
+
originalValue: value.slice(0, 100), // Log first 100 chars for debugging
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
// Basic HTML entity encoding for critical characters
|
|
43
|
+
sanitized = sanitized
|
|
44
|
+
.replaceAll('&', '&')
|
|
45
|
+
.replaceAll('<', '<')
|
|
46
|
+
.replaceAll('>', '>')
|
|
47
|
+
.replaceAll('"', '"')
|
|
48
|
+
.replaceAll("'", ''')
|
|
49
|
+
.replaceAll('/', '/');
|
|
50
|
+
const result = sanitized.trim();
|
|
51
|
+
if (originalLength > 50 || xssPatternMatches > 0) {
|
|
52
|
+
logging_1.debugLog.debug('Sanitize', 'String sanitization completed', {
|
|
53
|
+
originalLength,
|
|
54
|
+
sanitizedLength: result.length,
|
|
55
|
+
xssPatternMatches,
|
|
56
|
+
wasTruncated: originalLength > constants_1.MAX_STRING_LENGTH,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return result;
|
|
60
|
+
};
|
|
61
|
+
exports.sanitizeString = sanitizeString;
|
|
62
|
+
/**
|
|
63
|
+
* Sanitizes a path string for route exclusion checks
|
|
64
|
+
* @param value - The path string to sanitize
|
|
65
|
+
* @returns The sanitized path string
|
|
66
|
+
*/
|
|
67
|
+
const sanitizePathString = (value) => {
|
|
68
|
+
if (typeof value !== 'string') {
|
|
69
|
+
return '';
|
|
70
|
+
}
|
|
71
|
+
if (value.length > constants_1.MAX_STRING_LENGTH) {
|
|
72
|
+
value = value.slice(0, Math.max(0, constants_1.MAX_STRING_LENGTH));
|
|
73
|
+
}
|
|
74
|
+
let sanitized = value;
|
|
75
|
+
for (const pattern of constants_1.XSS_PATTERNS) {
|
|
76
|
+
sanitized = sanitized.replace(pattern, '');
|
|
77
|
+
}
|
|
78
|
+
sanitized = sanitized
|
|
79
|
+
.replaceAll('&', '&')
|
|
80
|
+
.replaceAll('<', '<')
|
|
81
|
+
.replaceAll('>', '>')
|
|
82
|
+
.replaceAll('"', '"')
|
|
83
|
+
.replaceAll("'", ''');
|
|
84
|
+
return sanitized.trim();
|
|
85
|
+
};
|
|
86
|
+
exports.sanitizePathString = sanitizePathString;
|
|
87
|
+
/**
|
|
88
|
+
* Sanitizes any value recursively with depth protection
|
|
89
|
+
* @param value - The value to sanitize
|
|
90
|
+
* @param depth - Current recursion depth
|
|
91
|
+
* @returns The sanitized value
|
|
92
|
+
*/
|
|
93
|
+
const sanitizeValue = (value, depth = 0) => {
|
|
94
|
+
// Prevent infinite recursion
|
|
95
|
+
if (depth > constants_1.MAX_OBJECT_DEPTH) {
|
|
96
|
+
logging_1.debugLog.warn('Sanitize', 'Maximum object depth exceeded during sanitization', {
|
|
97
|
+
depth,
|
|
98
|
+
maxDepth: constants_1.MAX_OBJECT_DEPTH,
|
|
99
|
+
});
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
if (value === null || value === undefined) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
if (typeof value === 'string') {
|
|
106
|
+
return (0, exports.sanitizeString)(value);
|
|
107
|
+
}
|
|
108
|
+
if (typeof value === 'number') {
|
|
109
|
+
if (!Number.isFinite(value) || value < -Number.MAX_SAFE_INTEGER || value > Number.MAX_SAFE_INTEGER) {
|
|
110
|
+
logging_1.debugLog.warn('Sanitize', 'Invalid number sanitized to 0', { value, isFinite: Number.isFinite(value) });
|
|
111
|
+
return 0;
|
|
112
|
+
}
|
|
113
|
+
return value;
|
|
114
|
+
}
|
|
115
|
+
if (typeof value === 'boolean') {
|
|
116
|
+
return value;
|
|
117
|
+
}
|
|
118
|
+
if (Array.isArray(value)) {
|
|
119
|
+
const originalLength = value.length;
|
|
120
|
+
const limitedArray = value.slice(0, constants_1.MAX_ARRAY_LENGTH);
|
|
121
|
+
if (originalLength > constants_1.MAX_ARRAY_LENGTH) {
|
|
122
|
+
logging_1.debugLog.warn('Sanitize', 'Array truncated due to length limit', {
|
|
123
|
+
originalLength,
|
|
124
|
+
maxLength: constants_1.MAX_ARRAY_LENGTH,
|
|
125
|
+
depth,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
const sanitizedArray = limitedArray.map((item) => sanitizeValue(item, depth + 1)).filter((item) => item !== null);
|
|
129
|
+
if (originalLength > 0 && sanitizedArray.length === 0) {
|
|
130
|
+
logging_1.debugLog.warn('Sanitize', 'All array items were filtered out during sanitization', { originalLength, depth });
|
|
131
|
+
}
|
|
132
|
+
return sanitizedArray;
|
|
133
|
+
}
|
|
134
|
+
if (typeof value === 'object') {
|
|
135
|
+
const sanitizedObject = {};
|
|
136
|
+
const entries = Object.entries(value);
|
|
137
|
+
const originalKeysCount = entries.length;
|
|
138
|
+
const limitedEntries = entries.slice(0, 20);
|
|
139
|
+
if (originalKeysCount > 20) {
|
|
140
|
+
logging_1.debugLog.warn('Sanitize', 'Object keys truncated due to limit', {
|
|
141
|
+
originalKeys: originalKeysCount,
|
|
142
|
+
maxKeys: 20,
|
|
143
|
+
depth,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
let filteredKeysCount = 0;
|
|
147
|
+
for (const [key, value_] of limitedEntries) {
|
|
148
|
+
const sanitizedKey = (0, exports.sanitizeString)(key);
|
|
149
|
+
if (sanitizedKey) {
|
|
150
|
+
const sanitizedValue = sanitizeValue(value_, depth + 1);
|
|
151
|
+
if (sanitizedValue !== null) {
|
|
152
|
+
sanitizedObject[sanitizedKey] = sanitizedValue;
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
filteredKeysCount++;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
filteredKeysCount++;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (filteredKeysCount > 0) {
|
|
163
|
+
logging_1.debugLog.debug('Sanitize', 'Object properties filtered during sanitization', {
|
|
164
|
+
filteredKeysCount,
|
|
165
|
+
remainingKeys: Object.keys(sanitizedObject).length,
|
|
166
|
+
depth,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
return sanitizedObject;
|
|
170
|
+
}
|
|
171
|
+
logging_1.debugLog.debug('Sanitize', 'Unknown value type sanitized to null', { type: typeof value, depth });
|
|
172
|
+
return null;
|
|
173
|
+
};
|
|
174
|
+
/**
|
|
175
|
+
* Sanitizes API configuration data with strict validation
|
|
176
|
+
* @param data - The API config data to sanitize
|
|
177
|
+
* @returns The sanitized API config
|
|
178
|
+
*/
|
|
179
|
+
const sanitizeApiConfig = (data) => {
|
|
180
|
+
logging_1.debugLog.debug('Sanitize', 'Starting API config sanitization');
|
|
181
|
+
const safeData = {};
|
|
182
|
+
if (typeof data !== 'object' || data === null) {
|
|
183
|
+
logging_1.debugLog.warn('Sanitize', 'API config data is not an object', { data, type: typeof data });
|
|
184
|
+
return safeData;
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
const originalKeys = Object.keys(data);
|
|
188
|
+
let processedKeys = 0;
|
|
189
|
+
let filteredKeys = 0;
|
|
190
|
+
for (const key of originalKeys) {
|
|
191
|
+
if (constants_1.ALLOWED_API_CONFIG_KEYS.has(key)) {
|
|
192
|
+
const value = data[key];
|
|
193
|
+
if (key === 'excludedUrlPaths') {
|
|
194
|
+
const paths = Array.isArray(value) ? value : typeof value === 'string' ? [value] : [];
|
|
195
|
+
const originalPathsCount = paths.length;
|
|
196
|
+
safeData.excludedUrlPaths = paths.map((path) => (0, exports.sanitizePathString)(String(path))).filter(Boolean);
|
|
197
|
+
const filteredPathsCount = originalPathsCount - safeData.excludedUrlPaths.length;
|
|
198
|
+
if (filteredPathsCount > 0) {
|
|
199
|
+
logging_1.debugLog.warn('Sanitize', 'Some excluded URL paths were filtered during sanitization', {
|
|
200
|
+
originalCount: originalPathsCount,
|
|
201
|
+
filteredCount: filteredPathsCount,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
else if (key === 'tags') {
|
|
206
|
+
if (Array.isArray(value)) {
|
|
207
|
+
safeData.tags = value;
|
|
208
|
+
logging_1.debugLog.debug('Sanitize', 'Tags processed', { count: value.length });
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
logging_1.debugLog.warn('Sanitize', 'Tags value is not an array', { value, type: typeof value });
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
const sanitizedValue = sanitizeValue(value);
|
|
216
|
+
if (sanitizedValue !== null) {
|
|
217
|
+
safeData[key] = sanitizedValue;
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
logging_1.debugLog.warn('Sanitize', 'API config value sanitized to null', { key, originalValue: value });
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
processedKeys++;
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
filteredKeys++;
|
|
227
|
+
logging_1.debugLog.debug('Sanitize', 'API config key not allowed', { key });
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
logging_1.debugLog.info('Sanitize', 'API config sanitization completed', {
|
|
231
|
+
originalKeys: originalKeys.length,
|
|
232
|
+
processedKeys,
|
|
233
|
+
filteredKeys,
|
|
234
|
+
finalKeys: Object.keys(safeData).length,
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
logging_1.debugLog.error('Sanitize', 'API config sanitization failed', {
|
|
239
|
+
error: error instanceof Error ? error.message : error,
|
|
240
|
+
});
|
|
241
|
+
throw new Error(`API config sanitization failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
242
|
+
}
|
|
243
|
+
return safeData;
|
|
244
|
+
};
|
|
245
|
+
exports.sanitizeApiConfig = sanitizeApiConfig;
|
|
246
|
+
/**
|
|
247
|
+
* Sanitizes user metadata for custom events
|
|
248
|
+
* @param metadata - The metadata to sanitize
|
|
249
|
+
* @returns The sanitized metadata
|
|
250
|
+
*/
|
|
251
|
+
const sanitizeMetadata = (metadata) => {
|
|
252
|
+
logging_1.debugLog.debug('Sanitize', 'Starting metadata sanitization', { hasMetadata: metadata != null });
|
|
253
|
+
if (typeof metadata !== 'object' || metadata === null) {
|
|
254
|
+
logging_1.debugLog.debug('Sanitize', 'Metadata is not an object, returning empty object', {
|
|
255
|
+
metadata,
|
|
256
|
+
type: typeof metadata,
|
|
257
|
+
});
|
|
258
|
+
return {};
|
|
259
|
+
}
|
|
260
|
+
try {
|
|
261
|
+
const originalKeys = Object.keys(metadata).length;
|
|
262
|
+
const sanitized = sanitizeValue(metadata);
|
|
263
|
+
const result = typeof sanitized === 'object' && sanitized !== null ? sanitized : {};
|
|
264
|
+
const finalKeys = Object.keys(result).length;
|
|
265
|
+
logging_1.debugLog.debug('Sanitize', 'Metadata sanitization completed', {
|
|
266
|
+
originalKeys,
|
|
267
|
+
finalKeys,
|
|
268
|
+
keysFiltered: originalKeys - finalKeys,
|
|
269
|
+
});
|
|
270
|
+
return result;
|
|
271
|
+
}
|
|
272
|
+
catch (error) {
|
|
273
|
+
logging_1.debugLog.error('Sanitize', 'Metadata sanitization failed', {
|
|
274
|
+
error: error instanceof Error ? error.message : error,
|
|
275
|
+
});
|
|
276
|
+
throw new Error(`Metadata sanitization failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
exports.sanitizeMetadata = sanitizeMetadata;
|
|
280
|
+
/**
|
|
281
|
+
* Sanitizes URL strings for tracking
|
|
282
|
+
* @param url - The URL to sanitize
|
|
283
|
+
* @returns The sanitized URL
|
|
284
|
+
*/
|
|
285
|
+
const sanitizeUrl = (url) => {
|
|
286
|
+
logging_1.debugLog.debug('Sanitize', 'Starting URL sanitization', { urlLength: typeof url === 'string' ? url.length : 0 });
|
|
287
|
+
if (typeof url !== 'string') {
|
|
288
|
+
logging_1.debugLog.warn('Sanitize', 'URL is not a string', { url, type: typeof url });
|
|
289
|
+
return '';
|
|
290
|
+
}
|
|
291
|
+
try {
|
|
292
|
+
// Basic URL validation
|
|
293
|
+
const urlObject = new URL(url);
|
|
294
|
+
// Only allow http/https protocols
|
|
295
|
+
if (!['http:', 'https:'].includes(urlObject.protocol)) {
|
|
296
|
+
logging_1.debugLog.warn('Sanitize', 'URL protocol not allowed', {
|
|
297
|
+
protocol: urlObject.protocol,
|
|
298
|
+
allowedProtocols: ['http:', 'https:'],
|
|
299
|
+
});
|
|
300
|
+
return '';
|
|
301
|
+
}
|
|
302
|
+
// Sanitize the URL string
|
|
303
|
+
const result = (0, exports.sanitizeString)(urlObject.href);
|
|
304
|
+
logging_1.debugLog.debug('Sanitize', 'URL sanitization completed via URL object', {
|
|
305
|
+
originalLength: url.length,
|
|
306
|
+
sanitizedLength: result.length,
|
|
307
|
+
protocol: urlObject.protocol,
|
|
308
|
+
});
|
|
309
|
+
return result;
|
|
310
|
+
}
|
|
311
|
+
catch {
|
|
312
|
+
// If URL parsing fails, sanitize as string
|
|
313
|
+
logging_1.debugLog.warn('Sanitize', 'URL parsing failed, falling back to string sanitization', {
|
|
314
|
+
urlPreview: url.slice(0, 100),
|
|
315
|
+
});
|
|
316
|
+
return (0, exports.sanitizeString)(url);
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
exports.sanitizeUrl = sanitizeUrl;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { AppConfig, Config, ApiConfig } from '../../types';
|
|
2
|
+
/**
|
|
3
|
+
* Validates the app configuration object (before normalization)
|
|
4
|
+
* This validates the structure and basic types but allows for normalization afterward
|
|
5
|
+
* @param config - The app configuration to validate
|
|
6
|
+
* @throws {ProjectIdValidationError} If project ID validation fails
|
|
7
|
+
* @throws {AppConfigValidationError} If other configuration validation fails
|
|
8
|
+
*/
|
|
9
|
+
export declare const validateAppConfig: (config: AppConfig) => void;
|
|
10
|
+
/**
|
|
11
|
+
* Validates and normalizes the app configuration
|
|
12
|
+
* This is the primary validation entry point that ensures consistent behavior
|
|
13
|
+
* @param config - The app configuration to validate and normalize
|
|
14
|
+
* @returns The normalized configuration
|
|
15
|
+
* @throws {ProjectIdValidationError} If project ID validation fails after normalization
|
|
16
|
+
* @throws {AppConfigValidationError} If other configuration validation fails
|
|
17
|
+
*/
|
|
18
|
+
export declare const validateAndNormalizeConfig: (config: AppConfig) => AppConfig;
|
|
19
|
+
/**
|
|
20
|
+
* Validates a complete configuration object
|
|
21
|
+
* @param config - The configuration to validate
|
|
22
|
+
* @returns Validation result with errors and warnings
|
|
23
|
+
*/
|
|
24
|
+
export declare const validateConfig: (config: Config) => {
|
|
25
|
+
errors: string[];
|
|
26
|
+
warnings: string[];
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Validates the final configuration
|
|
30
|
+
* @param config - The configuration to validate
|
|
31
|
+
* @returns Validation result with errors and warnings
|
|
32
|
+
*/
|
|
33
|
+
export declare const validateFinalConfig: (config: Config) => {
|
|
34
|
+
errors: string[];
|
|
35
|
+
warnings: string[];
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Type guard to check if a JSON response is a valid API config
|
|
39
|
+
* @param json - The JSON to validate
|
|
40
|
+
* @returns True if the JSON is a valid API config
|
|
41
|
+
*/
|
|
42
|
+
export declare const isValidConfigApiResponse: (json: unknown) => json is ApiConfig;
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isValidConfigApiResponse = exports.validateFinalConfig = exports.validateConfig = exports.validateAndNormalizeConfig = exports.validateAppConfig = void 0;
|
|
4
|
+
const constants_1 = require("../../constants");
|
|
5
|
+
const types_1 = require("../../types");
|
|
6
|
+
const validation_error_types_1 = require("../../types/validation-error.types");
|
|
7
|
+
const logging_1 = require("../logging");
|
|
8
|
+
/**
|
|
9
|
+
* Validates the app configuration object (before normalization)
|
|
10
|
+
* This validates the structure and basic types but allows for normalization afterward
|
|
11
|
+
* @param config - The app configuration to validate
|
|
12
|
+
* @throws {ProjectIdValidationError} If project ID validation fails
|
|
13
|
+
* @throws {AppConfigValidationError} If other configuration validation fails
|
|
14
|
+
*/
|
|
15
|
+
const validateAppConfig = (config) => {
|
|
16
|
+
// Validate config exists and has id property
|
|
17
|
+
if (!config || typeof config !== 'object') {
|
|
18
|
+
logging_1.debugLog.clientError('ConfigValidation', 'Configuration must be an object', { config });
|
|
19
|
+
throw new validation_error_types_1.AppConfigValidationError('Configuration must be an object', 'config');
|
|
20
|
+
}
|
|
21
|
+
// Check if id property exists (allow falsy values to be handled by normalization)
|
|
22
|
+
if (!('id' in config)) {
|
|
23
|
+
logging_1.debugLog.clientError('ConfigValidation', 'Project ID is missing from configuration');
|
|
24
|
+
throw new validation_error_types_1.ProjectIdValidationError(constants_1.VALIDATION_MESSAGES.MISSING_PROJECT_ID, 'config');
|
|
25
|
+
}
|
|
26
|
+
// Check basic type - null, undefined, or non-string values should fail here
|
|
27
|
+
if (config.id === null || config.id === undefined || typeof config.id !== 'string') {
|
|
28
|
+
logging_1.debugLog.clientError('ConfigValidation', 'Project ID must be a non-empty string', {
|
|
29
|
+
providedId: config.id,
|
|
30
|
+
type: typeof config.id,
|
|
31
|
+
});
|
|
32
|
+
throw new validation_error_types_1.ProjectIdValidationError(constants_1.VALIDATION_MESSAGES.MISSING_PROJECT_ID, 'config');
|
|
33
|
+
}
|
|
34
|
+
if (config.sessionTimeout !== undefined) {
|
|
35
|
+
if (typeof config.sessionTimeout !== 'number' ||
|
|
36
|
+
config.sessionTimeout < constants_1.MIN_SESSION_TIMEOUT_MS ||
|
|
37
|
+
config.sessionTimeout > constants_1.MAX_SESSION_TIMEOUT_MS) {
|
|
38
|
+
logging_1.debugLog.clientError('ConfigValidation', 'Invalid session timeout', {
|
|
39
|
+
provided: config.sessionTimeout,
|
|
40
|
+
min: constants_1.MIN_SESSION_TIMEOUT_MS,
|
|
41
|
+
max: constants_1.MAX_SESSION_TIMEOUT_MS,
|
|
42
|
+
});
|
|
43
|
+
throw new validation_error_types_1.SessionTimeoutValidationError(constants_1.VALIDATION_MESSAGES.INVALID_SESSION_TIMEOUT, 'config');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (config.globalMetadata !== undefined) {
|
|
47
|
+
if (typeof config.globalMetadata !== 'object' || config.globalMetadata === null) {
|
|
48
|
+
logging_1.debugLog.clientError('ConfigValidation', 'Global metadata must be an object', {
|
|
49
|
+
provided: config.globalMetadata,
|
|
50
|
+
type: typeof config.globalMetadata,
|
|
51
|
+
});
|
|
52
|
+
throw new validation_error_types_1.AppConfigValidationError(constants_1.VALIDATION_MESSAGES.INVALID_GLOBAL_METADATA, 'config');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (config.scrollContainerSelectors !== undefined) {
|
|
56
|
+
validateScrollContainerSelectors(config.scrollContainerSelectors);
|
|
57
|
+
}
|
|
58
|
+
if (config.integrations) {
|
|
59
|
+
validateIntegrations(config.integrations);
|
|
60
|
+
}
|
|
61
|
+
if (config.sensitiveQueryParams !== undefined) {
|
|
62
|
+
if (!Array.isArray(config.sensitiveQueryParams)) {
|
|
63
|
+
logging_1.debugLog.clientError('ConfigValidation', 'Sensitive query params must be an array', {
|
|
64
|
+
provided: config.sensitiveQueryParams,
|
|
65
|
+
type: typeof config.sensitiveQueryParams,
|
|
66
|
+
});
|
|
67
|
+
throw new validation_error_types_1.AppConfigValidationError(constants_1.VALIDATION_MESSAGES.INVALID_SENSITIVE_QUERY_PARAMS, 'config');
|
|
68
|
+
}
|
|
69
|
+
for (const param of config.sensitiveQueryParams) {
|
|
70
|
+
if (typeof param !== 'string') {
|
|
71
|
+
logging_1.debugLog.clientError('ConfigValidation', 'All sensitive query params must be strings', {
|
|
72
|
+
param,
|
|
73
|
+
type: typeof param,
|
|
74
|
+
});
|
|
75
|
+
throw new validation_error_types_1.AppConfigValidationError('All sensitive query params must be strings', 'config');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (config.errorSampling !== undefined) {
|
|
80
|
+
if (typeof config.errorSampling !== 'number' || config.errorSampling < 0 || config.errorSampling > 1) {
|
|
81
|
+
logging_1.debugLog.clientError('ConfigValidation', 'Invalid error sampling rate', {
|
|
82
|
+
provided: config.errorSampling,
|
|
83
|
+
expected: '0-1',
|
|
84
|
+
});
|
|
85
|
+
throw new validation_error_types_1.SamplingRateValidationError(constants_1.VALIDATION_MESSAGES.INVALID_ERROR_SAMPLING_RATE, 'config');
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
exports.validateAppConfig = validateAppConfig;
|
|
90
|
+
/**
|
|
91
|
+
* Validates scroll container selectors
|
|
92
|
+
* @param selectors - CSS selectors to validate
|
|
93
|
+
*/
|
|
94
|
+
const validateScrollContainerSelectors = (selectors) => {
|
|
95
|
+
const selectorsArray = Array.isArray(selectors) ? selectors : [selectors];
|
|
96
|
+
for (const selector of selectorsArray) {
|
|
97
|
+
if (typeof selector !== 'string' || selector.trim() === '') {
|
|
98
|
+
logging_1.debugLog.clientError('ConfigValidation', 'Invalid scroll container selector', {
|
|
99
|
+
selector,
|
|
100
|
+
type: typeof selector,
|
|
101
|
+
isEmpty: selector === '' || (typeof selector === 'string' && selector.trim() === ''),
|
|
102
|
+
});
|
|
103
|
+
throw new validation_error_types_1.AppConfigValidationError(constants_1.VALIDATION_MESSAGES.INVALID_SCROLL_CONTAINER_SELECTORS, 'config');
|
|
104
|
+
}
|
|
105
|
+
// Validate CSS selector syntax but handle invalid selectors gracefully
|
|
106
|
+
if (typeof document !== 'undefined') {
|
|
107
|
+
try {
|
|
108
|
+
document.querySelector(selector);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
// Invalid CSS selectors are handled gracefully
|
|
112
|
+
// they will be ignored by the ScrollHandler and it will fall back to window scrolling
|
|
113
|
+
logging_1.debugLog.clientWarn('ConfigValidation', `Invalid CSS selector will be ignored: "${selector}"`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Validates integrations configuration
|
|
120
|
+
* @param integrations - Integrations configuration to validate
|
|
121
|
+
*/
|
|
122
|
+
const validateIntegrations = (integrations) => {
|
|
123
|
+
if (!integrations)
|
|
124
|
+
return;
|
|
125
|
+
if (integrations.googleAnalytics) {
|
|
126
|
+
if (!integrations.googleAnalytics.measurementId ||
|
|
127
|
+
typeof integrations.googleAnalytics.measurementId !== 'string' ||
|
|
128
|
+
integrations.googleAnalytics.measurementId.trim() === '') {
|
|
129
|
+
logging_1.debugLog.clientError('ConfigValidation', 'Invalid Google Analytics measurement ID', {
|
|
130
|
+
provided: integrations.googleAnalytics.measurementId,
|
|
131
|
+
type: typeof integrations.googleAnalytics.measurementId,
|
|
132
|
+
});
|
|
133
|
+
throw new validation_error_types_1.IntegrationValidationError(constants_1.VALIDATION_MESSAGES.INVALID_GOOGLE_ANALYTICS_ID, 'config');
|
|
134
|
+
}
|
|
135
|
+
const measurementId = integrations.googleAnalytics.measurementId.trim();
|
|
136
|
+
if (!measurementId.match(/^(G-|UA-)/)) {
|
|
137
|
+
logging_1.debugLog.clientError('ConfigValidation', 'Google Analytics measurement ID must start with "G-" or "UA-"', {
|
|
138
|
+
provided: measurementId,
|
|
139
|
+
});
|
|
140
|
+
throw new validation_error_types_1.IntegrationValidationError('Google Analytics measurement ID must start with "G-" or "UA-"', 'config');
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
/**
|
|
145
|
+
* Validates and normalizes the app configuration
|
|
146
|
+
* This is the primary validation entry point that ensures consistent behavior
|
|
147
|
+
* @param config - The app configuration to validate and normalize
|
|
148
|
+
* @returns The normalized configuration
|
|
149
|
+
* @throws {ProjectIdValidationError} If project ID validation fails after normalization
|
|
150
|
+
* @throws {AppConfigValidationError} If other configuration validation fails
|
|
151
|
+
*/
|
|
152
|
+
const validateAndNormalizeConfig = (config) => {
|
|
153
|
+
// First validate the structure and basic types
|
|
154
|
+
(0, exports.validateAppConfig)(config);
|
|
155
|
+
// Normalize string values
|
|
156
|
+
const normalizedConfig = {
|
|
157
|
+
...config,
|
|
158
|
+
id: config.id.trim(),
|
|
159
|
+
globalMetadata: config.globalMetadata ?? {},
|
|
160
|
+
sensitiveQueryParams: config.sensitiveQueryParams ?? [],
|
|
161
|
+
};
|
|
162
|
+
// Validate normalized values - this catches whitespace-only IDs
|
|
163
|
+
if (!normalizedConfig.id) {
|
|
164
|
+
logging_1.debugLog.clientError('ConfigValidation', 'Project ID is empty after trimming whitespace', {
|
|
165
|
+
originalId: config.id,
|
|
166
|
+
normalizedId: normalizedConfig.id,
|
|
167
|
+
});
|
|
168
|
+
throw new validation_error_types_1.ProjectIdValidationError(constants_1.VALIDATION_MESSAGES.PROJECT_ID_EMPTY_AFTER_TRIM, 'config');
|
|
169
|
+
}
|
|
170
|
+
return normalizedConfig;
|
|
171
|
+
};
|
|
172
|
+
exports.validateAndNormalizeConfig = validateAndNormalizeConfig;
|
|
173
|
+
/**
|
|
174
|
+
* Validates sampling rate
|
|
175
|
+
* @param samplingRate - The sampling rate to validate
|
|
176
|
+
* @param errors - Array to push errors to
|
|
177
|
+
*/
|
|
178
|
+
const validateSamplingRate = (samplingRate, errors) => {
|
|
179
|
+
if (samplingRate !== undefined) {
|
|
180
|
+
if (typeof samplingRate !== 'number') {
|
|
181
|
+
errors.push('samplingRate must be a number');
|
|
182
|
+
}
|
|
183
|
+
else if (samplingRate < 0 || samplingRate > 1) {
|
|
184
|
+
errors.push('samplingRate must be between 0 and 1');
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
/**
|
|
189
|
+
* Validates excluded URL paths
|
|
190
|
+
* @param excludedUrlPaths - The excluded URL paths to validate
|
|
191
|
+
* @param errors - Array to push errors to
|
|
192
|
+
* @param prefix - Optional prefix for error messages
|
|
193
|
+
*/
|
|
194
|
+
const validateExcludedUrlPaths = (excludedUrlPaths, errors, prefix = '') => {
|
|
195
|
+
if (excludedUrlPaths !== undefined) {
|
|
196
|
+
if (Array.isArray(excludedUrlPaths)) {
|
|
197
|
+
for (const [index, path] of excludedUrlPaths.entries()) {
|
|
198
|
+
if (typeof path === 'string') {
|
|
199
|
+
try {
|
|
200
|
+
new RegExp(path);
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
errors.push(`${prefix}excludedUrlPaths[${index}] is not a valid regex pattern`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
errors.push(`${prefix}excludedUrlPaths[${index}] must be a string`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
errors.push(`${prefix}excludedUrlPaths must be an array`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
/**
|
|
217
|
+
* Validates a complete configuration object
|
|
218
|
+
* @param config - The configuration to validate
|
|
219
|
+
* @returns Validation result with errors and warnings
|
|
220
|
+
*/
|
|
221
|
+
const validateConfig = (config) => {
|
|
222
|
+
const errors = [];
|
|
223
|
+
const warnings = [];
|
|
224
|
+
if (config.sessionTimeout !== undefined) {
|
|
225
|
+
if (typeof config.sessionTimeout !== 'number') {
|
|
226
|
+
errors.push('sessionTimeout must be a number');
|
|
227
|
+
}
|
|
228
|
+
else if (config.sessionTimeout < constants_1.MIN_SESSION_TIMEOUT_MS) {
|
|
229
|
+
errors.push('sessionTimeout must be at least 30 seconds (30000ms)');
|
|
230
|
+
}
|
|
231
|
+
else if (config.sessionTimeout > constants_1.MAX_SESSION_TIMEOUT_MS) {
|
|
232
|
+
warnings.push('sessionTimeout is very long (>24 hours), consider reducing it');
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (config.globalMetadata !== undefined) {
|
|
236
|
+
if (typeof config.globalMetadata !== 'object' || config.globalMetadata === null) {
|
|
237
|
+
errors.push('globalMetadata must be an object');
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
const metadataSize = JSON.stringify(config.globalMetadata).length;
|
|
241
|
+
if (metadataSize > 10240) {
|
|
242
|
+
errors.push('globalMetadata is too large (max 10KB)');
|
|
243
|
+
}
|
|
244
|
+
if (Object.keys(config.globalMetadata).length > 12) {
|
|
245
|
+
errors.push('globalMetadata has too many keys (max 12)');
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
// No custom API endpoints supported
|
|
250
|
+
validateSamplingRate(config.samplingRate, errors);
|
|
251
|
+
if (config.tags !== undefined && !Array.isArray(config.tags)) {
|
|
252
|
+
errors.push('tags must be an array');
|
|
253
|
+
}
|
|
254
|
+
validateExcludedUrlPaths(config.excludedUrlPaths, errors);
|
|
255
|
+
return { errors, warnings };
|
|
256
|
+
};
|
|
257
|
+
exports.validateConfig = validateConfig;
|
|
258
|
+
/**
|
|
259
|
+
* Validates the final configuration
|
|
260
|
+
* @param config - The configuration to validate
|
|
261
|
+
* @returns Validation result with errors and warnings
|
|
262
|
+
*/
|
|
263
|
+
const validateFinalConfig = (config) => {
|
|
264
|
+
const errors = [];
|
|
265
|
+
const warnings = [];
|
|
266
|
+
validateSamplingRate(config.samplingRate, errors);
|
|
267
|
+
validateExcludedUrlPaths(config.excludedUrlPaths, errors);
|
|
268
|
+
// No custom API endpoints supported
|
|
269
|
+
return { errors, warnings };
|
|
270
|
+
};
|
|
271
|
+
exports.validateFinalConfig = validateFinalConfig;
|
|
272
|
+
/**
|
|
273
|
+
* Type guard to check if a JSON response is a valid API config
|
|
274
|
+
* @param json - The JSON to validate
|
|
275
|
+
* @returns True if the JSON is a valid API config
|
|
276
|
+
*/
|
|
277
|
+
const isValidConfigApiResponse = (json) => {
|
|
278
|
+
try {
|
|
279
|
+
if (typeof json !== 'object' || !json) {
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
const response = json;
|
|
283
|
+
const result = {
|
|
284
|
+
mode: response['mode'] === undefined || [types_1.Mode.QA, types_1.Mode.DEBUG].includes(response['mode']),
|
|
285
|
+
samplingRate: response['samplingRate'] === undefined ||
|
|
286
|
+
(typeof response['samplingRate'] === 'number' && response['samplingRate'] > 0 && response['samplingRate'] <= 1),
|
|
287
|
+
tags: response['tags'] === undefined || Array.isArray(response['tags']),
|
|
288
|
+
excludedUrlPaths: response['excludedUrlPaths'] === undefined || Array.isArray(response['excludedUrlPaths']),
|
|
289
|
+
ipExcluded: response['ipExcluded'] === undefined || typeof response['ipExcluded'] === 'boolean',
|
|
290
|
+
};
|
|
291
|
+
return Object.values(result).every(Boolean);
|
|
292
|
+
}
|
|
293
|
+
catch {
|
|
294
|
+
return false;
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
exports.isValidConfigApiResponse = isValidConfigApiResponse;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { MetadataType } from '../../types';
|
|
2
|
+
/**
|
|
3
|
+
* Validates a complete event with name and optional metadata
|
|
4
|
+
* @param eventName - The event name to validate
|
|
5
|
+
* @param metadata - Optional metadata to validate
|
|
6
|
+
* @returns Validation result with sanitized metadata if valid
|
|
7
|
+
*/
|
|
8
|
+
export declare const isEventValid: (eventName: string, metadata?: Record<string, unknown>) => {
|
|
9
|
+
valid: boolean;
|
|
10
|
+
error?: string;
|
|
11
|
+
sanitizedMetadata?: Record<string, MetadataType>;
|
|
12
|
+
};
|