@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
|
@@ -1,136 +1,45 @@
|
|
|
1
1
|
import { StateManager } from '../../managers/state.manager';
|
|
2
|
-
/**
|
|
3
|
-
* Debug logger class that extends StateManager for clean access to global state
|
|
4
|
-
*/
|
|
5
2
|
class DebugLogger extends StateManager {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
this.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
this.
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* General operational information
|
|
22
|
-
* Console: qa and debug modes | Events: NODE_ENV=dev
|
|
23
|
-
*/
|
|
24
|
-
info(namespace, message, data) {
|
|
25
|
-
this.logMessage('INFO', namespace, message, data);
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Internal library errors
|
|
29
|
-
* Console: debug mode only | Events: NODE_ENV=dev
|
|
30
|
-
*/
|
|
31
|
-
error(namespace, message, data) {
|
|
32
|
-
this.logMessage('ERROR', namespace, message, data);
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Internal library warnings
|
|
36
|
-
* Console: debug mode only | Events: NODE_ENV=dev
|
|
37
|
-
*/
|
|
38
|
-
warn(namespace, message, data) {
|
|
39
|
-
this.logMessage('WARN', namespace, message, data);
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Strategic debug information
|
|
43
|
-
* Console: debug mode only | Events: NODE_ENV=dev
|
|
44
|
-
*/
|
|
45
|
-
debug(namespace, message, data) {
|
|
46
|
-
this.logMessage('DEBUG', namespace, message, data);
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Detailed trace information
|
|
50
|
-
* Console: debug mode only | Events: NODE_ENV=dev
|
|
51
|
-
*/
|
|
52
|
-
verbose(namespace, message, data) {
|
|
53
|
-
this.logMessage('VERBOSE', namespace, message, data);
|
|
54
|
-
}
|
|
55
|
-
getCurrentMode() {
|
|
56
|
-
try {
|
|
57
|
-
return this.get('config')?.mode;
|
|
58
|
-
}
|
|
59
|
-
catch {
|
|
60
|
-
return undefined;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
shouldShowLog(level) {
|
|
64
|
-
const mode = this.getCurrentMode();
|
|
65
|
-
switch (mode) {
|
|
66
|
-
case 'qa':
|
|
67
|
-
return ['INFO', 'CLIENT_ERROR', 'CLIENT_WARN'].includes(level);
|
|
68
|
-
case 'debug':
|
|
69
|
-
return true;
|
|
70
|
-
default:
|
|
71
|
-
return false;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
formatMessage(namespace, message) {
|
|
75
|
-
return `[TraceLog:${namespace}] ${message}`;
|
|
76
|
-
}
|
|
77
|
-
getConsoleMethod(level) {
|
|
78
|
-
switch (level) {
|
|
79
|
-
case 'CLIENT_ERROR':
|
|
80
|
-
case 'ERROR':
|
|
81
|
-
return 'error';
|
|
82
|
-
case 'CLIENT_WARN':
|
|
83
|
-
case 'WARN':
|
|
84
|
-
return 'warn';
|
|
85
|
-
case 'INFO':
|
|
86
|
-
case 'DEBUG':
|
|
87
|
-
case 'VERBOSE':
|
|
88
|
-
default:
|
|
89
|
-
return 'log';
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
logMessage(level, namespace, message, data) {
|
|
93
|
-
if (!this.shouldShowLog(level)) {
|
|
3
|
+
constructor() {
|
|
4
|
+
super(...arguments);
|
|
5
|
+
this.clientError = (ns, msg, data) => this.log('CLIENT_ERROR', ns, msg, data);
|
|
6
|
+
this.clientWarn = (ns, msg, data) => this.log('CLIENT_WARN', ns, msg, data);
|
|
7
|
+
this.info = (ns, msg, data) => this.log('INFO', ns, msg, data);
|
|
8
|
+
this.error = (ns, msg, data) => this.log('ERROR', ns, msg, data);
|
|
9
|
+
this.warn = (ns, msg, data) => this.log('WARN', ns, msg, data);
|
|
10
|
+
this.debug = (ns, msg, data) => this.log('DEBUG', ns, msg, data);
|
|
11
|
+
this.verbose = (ns, msg, data) => this.log('VERBOSE', ns, msg, data);
|
|
12
|
+
}
|
|
13
|
+
log(level, ns, msg, data) {
|
|
14
|
+
const mode = this.get('config')?.mode;
|
|
15
|
+
if (!this.shouldShow(level, mode))
|
|
94
16
|
return;
|
|
95
|
-
}
|
|
96
|
-
const
|
|
97
|
-
const consoleMethod = this.getConsoleMethod(level);
|
|
17
|
+
const formattedMsg = `[TraceLog:${ns}] ${msg}`;
|
|
18
|
+
const method = this.getMethod(level);
|
|
98
19
|
if (data !== undefined) {
|
|
99
|
-
console[
|
|
20
|
+
console[method](formattedMsg, data);
|
|
100
21
|
}
|
|
101
22
|
else {
|
|
102
|
-
console[
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if (
|
|
113
|
-
return;
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
data,
|
|
123
|
-
},
|
|
124
|
-
});
|
|
125
|
-
window.dispatchEvent(event);
|
|
126
|
-
}
|
|
127
|
-
catch {
|
|
128
|
-
console.log(`[TraceLog:${namespace}] ${message}`, data);
|
|
129
|
-
}
|
|
23
|
+
console[method](formattedMsg);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
shouldShow(level, mode) {
|
|
27
|
+
if (['CLIENT_ERROR', 'ERROR'].includes(level))
|
|
28
|
+
return true;
|
|
29
|
+
if (!mode)
|
|
30
|
+
return level === 'CLIENT_WARN';
|
|
31
|
+
if (mode === 'qa')
|
|
32
|
+
return ['INFO', 'CLIENT_ERROR', 'CLIENT_WARN'].includes(level);
|
|
33
|
+
if (mode === 'debug')
|
|
34
|
+
return true; // Debug mode shows all logs
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
getMethod(level) {
|
|
38
|
+
if (['CLIENT_ERROR', 'ERROR'].includes(level))
|
|
39
|
+
return 'error';
|
|
40
|
+
if (['CLIENT_WARN', 'WARN'].includes(level))
|
|
41
|
+
return 'warn';
|
|
42
|
+
return 'log';
|
|
130
43
|
}
|
|
131
44
|
}
|
|
132
|
-
/**
|
|
133
|
-
* Singleton debug logger instance
|
|
134
|
-
* Provides the same API as before: debugLog.clientError(), debugLog.info(), etc.
|
|
135
|
-
*/
|
|
136
45
|
export const debugLog = new DebugLogger();
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export async function fetchWithTimeout(url, options = {}) {
|
|
2
|
+
const { timeout = 10000, ...fetchOptions } = options;
|
|
3
|
+
const controller = new AbortController();
|
|
4
|
+
const timeoutId = setTimeout(() => {
|
|
5
|
+
controller.abort();
|
|
6
|
+
}, timeout);
|
|
7
|
+
try {
|
|
8
|
+
const response = await fetch(url, {
|
|
9
|
+
...fetchOptions,
|
|
10
|
+
signal: controller.signal,
|
|
11
|
+
});
|
|
12
|
+
clearTimeout(timeoutId);
|
|
13
|
+
return response;
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
clearTimeout(timeoutId);
|
|
17
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
18
|
+
throw new Error(`Request timeout after ${timeout}ms`);
|
|
19
|
+
}
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -6,7 +6,6 @@ import { debugLog } from '../logging';
|
|
|
6
6
|
* @returns The generated API URL
|
|
7
7
|
*/
|
|
8
8
|
export const getApiUrl = (id, allowHttp = false) => {
|
|
9
|
-
debugLog.debug('URLUtils', 'Generating API URL', { projectId: id, allowHttp });
|
|
10
9
|
const url = new URL(window.location.href);
|
|
11
10
|
const host = url.hostname;
|
|
12
11
|
const parts = host.split('.');
|
|
@@ -17,14 +16,6 @@ export const getApiUrl = (id, allowHttp = false) => {
|
|
|
17
16
|
const cleanDomain = parts.slice(-2).join('.');
|
|
18
17
|
const protocol = allowHttp && url.protocol === 'http:' ? 'http' : 'https';
|
|
19
18
|
const apiUrl = `${protocol}://${id}.${cleanDomain}`;
|
|
20
|
-
debugLog.debug('URLUtils', 'Generated API URL', {
|
|
21
|
-
originalUrl: window.location.href,
|
|
22
|
-
hostname: host,
|
|
23
|
-
domainParts: parts.length,
|
|
24
|
-
cleanDomain,
|
|
25
|
-
protocol,
|
|
26
|
-
generatedUrl: apiUrl,
|
|
27
|
-
});
|
|
28
19
|
const isValid = isValidUrl(apiUrl, allowHttp);
|
|
29
20
|
if (!isValid) {
|
|
30
21
|
debugLog.clientError('URLUtils', 'Generated API URL failed validation', {
|
|
@@ -33,7 +24,6 @@ export const getApiUrl = (id, allowHttp = false) => {
|
|
|
33
24
|
});
|
|
34
25
|
throw new Error('Invalid URL');
|
|
35
26
|
}
|
|
36
|
-
debugLog.debug('URLUtils', 'API URL generation completed successfully', { apiUrl });
|
|
37
27
|
return apiUrl;
|
|
38
28
|
};
|
|
39
29
|
/**
|
|
@@ -43,10 +33,6 @@ export const getApiUrl = (id, allowHttp = false) => {
|
|
|
43
33
|
* @returns The normalized URL
|
|
44
34
|
*/
|
|
45
35
|
export const normalizeUrl = (url, sensitiveQueryParams = []) => {
|
|
46
|
-
debugLog.debug('URLUtils', 'Normalizing URL', {
|
|
47
|
-
urlLength: url.length,
|
|
48
|
-
sensitiveParamsCount: sensitiveQueryParams.length,
|
|
49
|
-
});
|
|
50
36
|
try {
|
|
51
37
|
const urlObject = new URL(url);
|
|
52
38
|
const searchParams = urlObject.searchParams;
|
|
@@ -68,16 +54,10 @@ export const normalizeUrl = (url, sensitiveQueryParams = []) => {
|
|
|
68
54
|
});
|
|
69
55
|
}
|
|
70
56
|
if (!hasChanged && url.includes('?')) {
|
|
71
|
-
debugLog.debug('URLUtils', 'URL normalization - no changes needed');
|
|
72
57
|
return url;
|
|
73
58
|
}
|
|
74
59
|
urlObject.search = searchParams.toString();
|
|
75
60
|
const result = urlObject.toString();
|
|
76
|
-
debugLog.debug('URLUtils', 'URL normalization completed', {
|
|
77
|
-
hasChanged,
|
|
78
|
-
originalLength: url.length,
|
|
79
|
-
normalizedLength: result.length,
|
|
80
|
-
});
|
|
81
61
|
return result;
|
|
82
62
|
}
|
|
83
63
|
catch (error) {
|
|
@@ -95,18 +75,13 @@ export const normalizeUrl = (url, sensitiveQueryParams = []) => {
|
|
|
95
75
|
* @returns True if the URL should be excluded
|
|
96
76
|
*/
|
|
97
77
|
export const isUrlPathExcluded = (url, excludedPaths = []) => {
|
|
98
|
-
debugLog.debug('URLUtils', 'Checking if URL path is excluded', {
|
|
99
|
-
urlLength: url.length,
|
|
100
|
-
excludedPathsCount: excludedPaths.length,
|
|
101
|
-
});
|
|
102
78
|
if (excludedPaths.length === 0) {
|
|
103
|
-
debugLog.debug('URLUtils', 'No excluded paths configured');
|
|
104
79
|
return false;
|
|
105
80
|
}
|
|
106
81
|
let path;
|
|
107
82
|
try {
|
|
108
|
-
|
|
109
|
-
|
|
83
|
+
const parsedUrl = new URL(url, window.location.origin);
|
|
84
|
+
path = parsedUrl.pathname + (parsedUrl.hash ?? '');
|
|
110
85
|
}
|
|
111
86
|
catch (error) {
|
|
112
87
|
debugLog.warn('URLUtils', 'Failed to parse URL for path exclusion check', {
|
|
@@ -127,23 +102,14 @@ export const isUrlPathExcluded = (url, excludedPaths = []) => {
|
|
|
127
102
|
try {
|
|
128
103
|
if (isRegularExpression(pattern)) {
|
|
129
104
|
const matches = pattern.test(path);
|
|
130
|
-
if (matches) {
|
|
131
|
-
debugLog.debug('URLUtils', 'Path matched regex pattern', { path, pattern: pattern.toString() });
|
|
132
|
-
}
|
|
133
105
|
return matches;
|
|
134
106
|
}
|
|
135
107
|
if (pattern.includes('*')) {
|
|
136
108
|
const regex = wildcardToRegex(pattern);
|
|
137
109
|
const matches = regex.test(path);
|
|
138
|
-
if (matches) {
|
|
139
|
-
debugLog.debug('URLUtils', 'Path matched wildcard pattern', { path, pattern, regex: regex.toString() });
|
|
140
|
-
}
|
|
141
110
|
return matches;
|
|
142
111
|
}
|
|
143
112
|
const matches = pattern === path;
|
|
144
|
-
if (matches) {
|
|
145
|
-
debugLog.debug('URLUtils', 'Path matched exact pattern', { path, pattern });
|
|
146
|
-
}
|
|
147
113
|
return matches;
|
|
148
114
|
}
|
|
149
115
|
catch (error) {
|
|
@@ -156,11 +122,5 @@ export const isUrlPathExcluded = (url, excludedPaths = []) => {
|
|
|
156
122
|
}
|
|
157
123
|
});
|
|
158
124
|
const isExcluded = !!matchedPattern;
|
|
159
|
-
debugLog.debug('URLUtils', 'URL path exclusion check completed', {
|
|
160
|
-
path,
|
|
161
|
-
isExcluded,
|
|
162
|
-
matchedPattern: matchedPattern ?? null,
|
|
163
|
-
totalPatternsChecked: excludedPaths.length,
|
|
164
|
-
});
|
|
165
125
|
return isExcluded;
|
|
166
126
|
};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { MetadataType } from '../../types
|
|
2
|
-
import { ApiConfig } from '../../types/config.types';
|
|
1
|
+
import { MetadataType, ApiConfig } from '../../types';
|
|
3
2
|
/**
|
|
4
3
|
* Sanitizes a string value to prevent XSS attacks
|
|
5
4
|
* @param value - The string to sanitize
|
|
@@ -24,9 +23,3 @@ export declare const sanitizeApiConfig: (data: unknown) => ApiConfig;
|
|
|
24
23
|
* @returns The sanitized metadata
|
|
25
24
|
*/
|
|
26
25
|
export declare const sanitizeMetadata: (metadata: unknown) => Record<string, MetadataType>;
|
|
27
|
-
/**
|
|
28
|
-
* Sanitizes URL strings for tracking
|
|
29
|
-
* @param url - The URL to sanitize
|
|
30
|
-
* @returns The sanitized URL
|
|
31
|
-
*/
|
|
32
|
-
export declare const sanitizeUrl: (url: string) => string;
|
|
@@ -206,6 +206,12 @@ export const sanitizeApiConfig = (data) => {
|
|
|
206
206
|
debugLog.warn('Sanitize', 'Tags value is not an array', { value, type: typeof value });
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
|
+
else if (key === 'samplingRate') {
|
|
210
|
+
const sanitizedValue = sanitizeValue(value);
|
|
211
|
+
if (typeof sanitizedValue === 'number') {
|
|
212
|
+
safeData.samplingRate = sanitizedValue;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
209
215
|
else {
|
|
210
216
|
const sanitizedValue = sanitizeValue(value);
|
|
211
217
|
if (sanitizedValue !== null) {
|
|
@@ -270,42 +276,3 @@ export const sanitizeMetadata = (metadata) => {
|
|
|
270
276
|
throw new Error(`Metadata sanitization failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
271
277
|
}
|
|
272
278
|
};
|
|
273
|
-
/**
|
|
274
|
-
* Sanitizes URL strings for tracking
|
|
275
|
-
* @param url - The URL to sanitize
|
|
276
|
-
* @returns The sanitized URL
|
|
277
|
-
*/
|
|
278
|
-
export const sanitizeUrl = (url) => {
|
|
279
|
-
debugLog.debug('Sanitize', 'Starting URL sanitization', { urlLength: typeof url === 'string' ? url.length : 0 });
|
|
280
|
-
if (typeof url !== 'string') {
|
|
281
|
-
debugLog.warn('Sanitize', 'URL is not a string', { url, type: typeof url });
|
|
282
|
-
return '';
|
|
283
|
-
}
|
|
284
|
-
try {
|
|
285
|
-
// Basic URL validation
|
|
286
|
-
const urlObject = new URL(url);
|
|
287
|
-
// Only allow http/https protocols
|
|
288
|
-
if (!['http:', 'https:'].includes(urlObject.protocol)) {
|
|
289
|
-
debugLog.warn('Sanitize', 'URL protocol not allowed', {
|
|
290
|
-
protocol: urlObject.protocol,
|
|
291
|
-
allowedProtocols: ['http:', 'https:'],
|
|
292
|
-
});
|
|
293
|
-
return '';
|
|
294
|
-
}
|
|
295
|
-
// Sanitize the URL string
|
|
296
|
-
const result = sanitizeString(urlObject.href);
|
|
297
|
-
debugLog.debug('Sanitize', 'URL sanitization completed via URL object', {
|
|
298
|
-
originalLength: url.length,
|
|
299
|
-
sanitizedLength: result.length,
|
|
300
|
-
protocol: urlObject.protocol,
|
|
301
|
-
});
|
|
302
|
-
return result;
|
|
303
|
-
}
|
|
304
|
-
catch {
|
|
305
|
-
// If URL parsing fails, sanitize as string
|
|
306
|
-
debugLog.warn('Sanitize', 'URL parsing failed, falling back to string sanitization', {
|
|
307
|
-
urlPreview: url.slice(0, 100),
|
|
308
|
-
});
|
|
309
|
-
return sanitizeString(url);
|
|
310
|
-
}
|
|
311
|
-
};
|
|
@@ -24,6 +24,12 @@ export declare const validateAndNormalizeConfig: (config: AppConfig) => AppConfi
|
|
|
24
24
|
export declare const validateConfig: (config: Config) => {
|
|
25
25
|
errors: string[];
|
|
26
26
|
warnings: string[];
|
|
27
|
+
samplingRate: number;
|
|
28
|
+
};
|
|
29
|
+
export declare const normalizeConfig: (config: Config) => {
|
|
30
|
+
config: Config;
|
|
31
|
+
errors: string[];
|
|
32
|
+
warnings: string[];
|
|
27
33
|
};
|
|
28
34
|
/**
|
|
29
35
|
* Validates the final configuration
|
|
@@ -33,6 +39,7 @@ export declare const validateConfig: (config: Config) => {
|
|
|
33
39
|
export declare const validateFinalConfig: (config: Config) => {
|
|
34
40
|
errors: string[];
|
|
35
41
|
warnings: string[];
|
|
42
|
+
samplingRate: number;
|
|
36
43
|
};
|
|
37
44
|
/**
|
|
38
45
|
* Type guard to check if a JSON response is a valid API config
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MAX_SESSION_TIMEOUT_MS, MIN_SESSION_TIMEOUT_MS, VALIDATION_MESSAGES } from '../../constants';
|
|
1
|
+
import { DEFAULT_SAMPLING_RATE, MAX_SESSION_TIMEOUT_MS, MIN_SESSION_TIMEOUT_MS, VALIDATION_MESSAGES, } from '../../constants';
|
|
2
2
|
import { Mode } from '../../types';
|
|
3
3
|
import { ProjectIdValidationError, AppConfigValidationError, SessionTimeoutValidationError, SamplingRateValidationError, IntegrationValidationError, } from '../../types/validation-error.types';
|
|
4
4
|
import { debugLog } from '../logging';
|
|
@@ -83,6 +83,47 @@ export const validateAppConfig = (config) => {
|
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
};
|
|
86
|
+
/**
|
|
87
|
+
* Validates CSS selector syntax without executing querySelector (XSS prevention)
|
|
88
|
+
* @param selector - CSS selector to validate
|
|
89
|
+
* @returns True if the selector syntax is valid
|
|
90
|
+
*/
|
|
91
|
+
const isValidCssSelectorSyntax = (selector) => {
|
|
92
|
+
// Prevent dangerous characters that could indicate XSS attempts
|
|
93
|
+
if (selector.includes('<') || selector.includes('>') || /on\w+\s*=/i.test(selector)) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
// Safe CSS selector pattern - allows common selector syntax
|
|
97
|
+
const safePattern = /^[a-zA-Z0-9\-_#.[\]="':, >+~*()]+$/;
|
|
98
|
+
if (!safePattern.test(selector)) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
// Check for balanced parentheses
|
|
102
|
+
let parenthesesCount = 0;
|
|
103
|
+
for (const char of selector) {
|
|
104
|
+
if (char === '(')
|
|
105
|
+
parenthesesCount++;
|
|
106
|
+
if (char === ')')
|
|
107
|
+
parenthesesCount--;
|
|
108
|
+
if (parenthesesCount < 0)
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
if (parenthesesCount !== 0)
|
|
112
|
+
return false;
|
|
113
|
+
// Check for balanced square brackets
|
|
114
|
+
let bracketsCount = 0;
|
|
115
|
+
for (const char of selector) {
|
|
116
|
+
if (char === '[')
|
|
117
|
+
bracketsCount++;
|
|
118
|
+
if (char === ']')
|
|
119
|
+
bracketsCount--;
|
|
120
|
+
if (bracketsCount < 0)
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
if (bracketsCount !== 0)
|
|
124
|
+
return false;
|
|
125
|
+
return true;
|
|
126
|
+
};
|
|
86
127
|
/**
|
|
87
128
|
* Validates scroll container selectors
|
|
88
129
|
* @param selectors - CSS selectors to validate
|
|
@@ -98,16 +139,14 @@ const validateScrollContainerSelectors = (selectors) => {
|
|
|
98
139
|
});
|
|
99
140
|
throw new AppConfigValidationError(VALIDATION_MESSAGES.INVALID_SCROLL_CONTAINER_SELECTORS, 'config');
|
|
100
141
|
}
|
|
101
|
-
// Validate CSS selector syntax
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
debugLog.clientWarn('ConfigValidation', `Invalid CSS selector will be ignored: "${selector}"`);
|
|
110
|
-
}
|
|
142
|
+
// Validate CSS selector syntax using regex-based validation (XSS prevention)
|
|
143
|
+
// This validates syntax WITHOUT executing document.querySelector()
|
|
144
|
+
if (!isValidCssSelectorSyntax(selector)) {
|
|
145
|
+
debugLog.clientError('ConfigValidation', 'Invalid or potentially unsafe CSS selector', {
|
|
146
|
+
selector,
|
|
147
|
+
reason: 'Failed security validation',
|
|
148
|
+
});
|
|
149
|
+
throw new AppConfigValidationError('Invalid or potentially unsafe CSS selector', 'config');
|
|
111
150
|
}
|
|
112
151
|
}
|
|
113
152
|
};
|
|
@@ -171,14 +210,18 @@ export const validateAndNormalizeConfig = (config) => {
|
|
|
171
210
|
* @param errors - Array to push errors to
|
|
172
211
|
*/
|
|
173
212
|
const validateSamplingRate = (samplingRate, errors) => {
|
|
174
|
-
if (samplingRate
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
213
|
+
if (samplingRate === undefined) {
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
216
|
+
if (typeof samplingRate !== 'number') {
|
|
217
|
+
errors.push('samplingRate must be a number');
|
|
218
|
+
return DEFAULT_SAMPLING_RATE;
|
|
219
|
+
}
|
|
220
|
+
if (Number.isNaN(samplingRate) || samplingRate <= 0 || samplingRate > 1) {
|
|
221
|
+
errors.push(VALIDATION_MESSAGES.INVALID_SAMPLING_RATE);
|
|
222
|
+
return DEFAULT_SAMPLING_RATE;
|
|
181
223
|
}
|
|
224
|
+
return samplingRate;
|
|
182
225
|
};
|
|
183
226
|
/**
|
|
184
227
|
* Validates excluded URL paths
|
|
@@ -242,12 +285,23 @@ export const validateConfig = (config) => {
|
|
|
242
285
|
}
|
|
243
286
|
}
|
|
244
287
|
// No custom API endpoints supported
|
|
245
|
-
validateSamplingRate(config.samplingRate, errors);
|
|
288
|
+
const validatedSamplingRate = validateSamplingRate(config.samplingRate, errors) ?? DEFAULT_SAMPLING_RATE;
|
|
246
289
|
if (config.tags !== undefined && !Array.isArray(config.tags)) {
|
|
247
290
|
errors.push('tags must be an array');
|
|
248
291
|
}
|
|
249
292
|
validateExcludedUrlPaths(config.excludedUrlPaths, errors);
|
|
250
|
-
return { errors, warnings };
|
|
293
|
+
return { errors, warnings, samplingRate: validatedSamplingRate };
|
|
294
|
+
};
|
|
295
|
+
export const normalizeConfig = (config) => {
|
|
296
|
+
const { errors, warnings, samplingRate } = validateConfig(config);
|
|
297
|
+
return {
|
|
298
|
+
config: {
|
|
299
|
+
...config,
|
|
300
|
+
samplingRate,
|
|
301
|
+
},
|
|
302
|
+
errors,
|
|
303
|
+
warnings,
|
|
304
|
+
};
|
|
251
305
|
};
|
|
252
306
|
/**
|
|
253
307
|
* Validates the final configuration
|
|
@@ -257,10 +311,10 @@ export const validateConfig = (config) => {
|
|
|
257
311
|
export const validateFinalConfig = (config) => {
|
|
258
312
|
const errors = [];
|
|
259
313
|
const warnings = [];
|
|
260
|
-
validateSamplingRate(config.samplingRate, errors);
|
|
314
|
+
const validatedSamplingRate = validateSamplingRate(config.samplingRate, errors) ?? DEFAULT_SAMPLING_RATE;
|
|
261
315
|
validateExcludedUrlPaths(config.excludedUrlPaths, errors);
|
|
262
316
|
// No custom API endpoints supported
|
|
263
|
-
return { errors, warnings };
|
|
317
|
+
return { errors, warnings, samplingRate: validatedSamplingRate };
|
|
264
318
|
};
|
|
265
319
|
/**
|
|
266
320
|
* Type guard to check if a JSON response is a valid API config
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@tracelog/lib",
|
|
3
3
|
"description": "JavaScript library for web analytics and real-time event tracking",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"version": "0.0
|
|
5
|
+
"version": "0.2.0",
|
|
6
6
|
"main": "./dist/cjs/public-api.js",
|
|
7
7
|
"module": "./dist/esm/public-api.js",
|
|
8
8
|
"types": "./dist/esm/public-api.d.ts",
|
|
@@ -20,13 +20,13 @@
|
|
|
20
20
|
},
|
|
21
21
|
"scripts": {
|
|
22
22
|
"build": "npx tsc",
|
|
23
|
+
"type-check": "npx tsc --noEmit",
|
|
24
|
+
"type-check:watch": "npx tsc --noEmit --watch",
|
|
23
25
|
"build:esm": "tsc -p tsconfig.esm.json",
|
|
24
26
|
"build:cjs": "tsc -p tsconfig.cjs.json",
|
|
25
27
|
"build:browser": "NODE_ENV=production vite build",
|
|
26
|
-
"build:browser:
|
|
27
|
-
"build:
|
|
28
|
-
"build:browser:playground": "NODE_ENV=dev vite build",
|
|
29
|
-
"build:all": "npm run build:esm && npm run build:cjs && npm run build:browser:production",
|
|
28
|
+
"build:browser:dev": "NODE_ENV=dev vite build",
|
|
29
|
+
"build:all": "npm run build:esm && npm run build:cjs && npm run build:browser",
|
|
30
30
|
"build-ugly": "npm run build:all && find dist -name '*.js' -exec npx uglify-js {} -o {} --compress --mangle --toplevel \\;",
|
|
31
31
|
"build-ugly:win": "npm run build:all && Get-ChildItem -Path dist -Recurse -Filter *.js | ForEach-Object { npx uglify-js $_.FullName -o $_.FullName --compress --mangle --toplevel }",
|
|
32
32
|
"lint": "npx eslint \"src/**/*.ts\" \"tests/**/*.ts\"",
|
|
@@ -35,16 +35,16 @@
|
|
|
35
35
|
"format:check": "prettier --check \"src/**/*.ts\" \"tests/**/*{.ts,.js,.html,.css,.json,.md}\" \"scripts/**/*.js\"",
|
|
36
36
|
"check": "npm run lint && npm run format:check",
|
|
37
37
|
"fix": "npm run lint:fix && npm run format",
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"test:
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"ci:
|
|
38
|
+
"test:unit": "vitest run",
|
|
39
|
+
"test:unit:ci": "NODE_OPTIONS=\"--max-old-space-size=4096 --no-experimental-fetch\" vitest run --pool=forks --poolOptions.forks.singleFork=true --poolOptions.forks.isolate=false",
|
|
40
|
+
"test:unit:watch": "vitest",
|
|
41
|
+
"test:coverage": "vitest run --coverage",
|
|
42
|
+
"test:integration": "vitest run --config vitest.integration.config.mjs",
|
|
43
|
+
"serve": "http-server playground -p 3000 --cors",
|
|
44
|
+
"playground:setup": "npm run build:browser:dev && cp dist/browser/tracelog.js playground/tracelog.js",
|
|
45
|
+
"playground:dev": "npm run playground:setup && npm run serve",
|
|
46
|
+
"test:e2e": "npm run build:browser:dev && cp dist/browser/tracelog.js playground/tracelog.js && NODE_ENV=dev playwright test",
|
|
47
|
+
"ci:build": "npm run build:all",
|
|
48
48
|
"prepare": "husky",
|
|
49
49
|
"release": "node scripts/release.js",
|
|
50
50
|
"release:patch": "node scripts/release.js --force-version $(node -p \"const v=require('./package.json').version.split('.').map(Number); v[2]++; v.join('.')\")",
|
|
@@ -61,20 +61,27 @@
|
|
|
61
61
|
"@commitlint/config-conventional": "^19.8.1",
|
|
62
62
|
"@eslint/js": "^9.30.1",
|
|
63
63
|
"@playwright/test": "^1.54.0",
|
|
64
|
+
"@types/jest": "^30.0.0",
|
|
65
|
+
"@types/node": "^24.5.2",
|
|
64
66
|
"@typescript-eslint/eslint-plugin": "^8.36.0",
|
|
65
67
|
"@typescript-eslint/parser": "^8.36.0",
|
|
68
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
66
69
|
"commitlint": "^19.8.1",
|
|
70
|
+
"cross-env": "^10.0.0",
|
|
67
71
|
"eslint": "^8.57.1",
|
|
68
72
|
"eslint-config-prettier": "^10.1.5",
|
|
69
73
|
"eslint-plugin-prettier": "^5.5.1",
|
|
70
74
|
"globals": "^16.3.0",
|
|
71
75
|
"http-server": "^14.1.1",
|
|
72
76
|
"husky": "^9.1.6",
|
|
77
|
+
"jsdom": "^27.0.0",
|
|
73
78
|
"lint-staged": "^15.2.10",
|
|
74
79
|
"prettier": "^3.4.2",
|
|
80
|
+
"terser": "^5.44.0",
|
|
75
81
|
"typescript": "^5.7.3",
|
|
76
82
|
"typescript-eslint": "^8.36.0",
|
|
77
83
|
"uglify-js": "^3.19.3",
|
|
78
|
-
"vite": "^7.0.4"
|
|
84
|
+
"vite": "^7.0.4",
|
|
85
|
+
"vitest": "^3.2.4"
|
|
79
86
|
}
|
|
80
87
|
}
|