@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.
Files changed (228) hide show
  1. package/README.md +58 -24
  2. package/dist/browser/tracelog.js +1934 -3226
  3. package/dist/cjs/api.d.ts +33 -19
  4. package/dist/cjs/api.js +111 -156
  5. package/dist/cjs/app.constants.d.ts +80 -1
  6. package/dist/cjs/app.constants.js +90 -3
  7. package/dist/cjs/app.d.ts +29 -44
  8. package/dist/cjs/app.js +114 -212
  9. package/dist/cjs/app.types.d.ts +2 -7
  10. package/dist/cjs/app.types.js +10 -21
  11. package/dist/cjs/constants/api.constants.js +11 -5
  12. package/dist/cjs/constants/config.constants.d.ts +75 -0
  13. package/dist/cjs/constants/config.constants.js +178 -0
  14. package/dist/cjs/constants/error.constants.d.ts +29 -0
  15. package/dist/cjs/constants/error.constants.js +50 -0
  16. package/dist/cjs/constants/index.d.ts +3 -6
  17. package/dist/cjs/constants/index.js +3 -6
  18. package/dist/cjs/constants/performance.constants.d.ts +28 -0
  19. package/dist/cjs/constants/performance.constants.js +43 -0
  20. package/dist/cjs/handlers/click.handler.d.ts +1 -0
  21. package/dist/cjs/handlers/click.handler.js +30 -49
  22. package/dist/cjs/handlers/error.handler.d.ts +11 -6
  23. package/dist/cjs/handlers/error.handler.js +91 -51
  24. package/dist/cjs/handlers/page-view.handler.js +38 -29
  25. package/dist/cjs/handlers/performance.handler.d.ts +3 -0
  26. package/dist/cjs/handlers/performance.handler.js +76 -37
  27. package/dist/cjs/handlers/scroll.handler.d.ts +15 -0
  28. package/dist/cjs/handlers/scroll.handler.js +105 -31
  29. package/dist/cjs/handlers/session.handler.d.ts +6 -20
  30. package/dist/cjs/handlers/session.handler.js +38 -326
  31. package/dist/cjs/integrations/google-analytics.integration.d.ts +0 -1
  32. package/dist/cjs/integrations/google-analytics.integration.js +27 -98
  33. package/dist/cjs/listeners/input-listener-managers.d.ts +18 -9
  34. package/dist/cjs/listeners/input-listener-managers.js +24 -33
  35. package/dist/cjs/listeners/touch-listener-manager.d.ts +1 -3
  36. package/dist/cjs/listeners/touch-listener-manager.js +1 -23
  37. package/dist/cjs/listeners/visibility-listener-manager.d.ts +1 -4
  38. package/dist/cjs/listeners/visibility-listener-manager.js +6 -42
  39. package/dist/cjs/managers/api.manager.d.ts +13 -3
  40. package/dist/cjs/managers/api.manager.js +35 -5
  41. package/dist/cjs/managers/config.manager.d.ts +53 -3
  42. package/dist/cjs/managers/config.manager.js +131 -62
  43. package/dist/cjs/managers/event.manager.d.ts +57 -36
  44. package/dist/cjs/managers/event.manager.js +266 -417
  45. package/dist/cjs/managers/sender.manager.d.ts +40 -22
  46. package/dist/cjs/managers/sender.manager.js +200 -198
  47. package/dist/cjs/managers/session.manager.d.ts +80 -66
  48. package/dist/cjs/managers/session.manager.js +267 -522
  49. package/dist/cjs/managers/state.manager.d.ts +33 -0
  50. package/dist/cjs/managers/state.manager.js +79 -6
  51. package/dist/cjs/managers/storage.manager.d.ts +26 -2
  52. package/dist/cjs/managers/storage.manager.js +67 -34
  53. package/dist/cjs/managers/tags.manager.d.ts +31 -7
  54. package/dist/cjs/managers/tags.manager.js +123 -241
  55. package/dist/cjs/managers/user.manager.d.ts +14 -5
  56. package/dist/cjs/managers/user.manager.js +17 -9
  57. package/dist/cjs/public-api.d.ts +10 -1
  58. package/dist/cjs/public-api.js +18 -24
  59. package/dist/cjs/test-bridge.d.ts +48 -0
  60. package/dist/cjs/test-bridge.js +110 -0
  61. package/dist/cjs/types/api.types.d.ts +21 -6
  62. package/dist/cjs/types/api.types.js +21 -6
  63. package/dist/cjs/types/config.types.d.ts +22 -84
  64. package/dist/cjs/types/emitter.types.d.ts +11 -0
  65. package/dist/cjs/types/emitter.types.js +8 -0
  66. package/dist/cjs/types/event.types.d.ts +8 -11
  67. package/dist/cjs/types/index.d.ts +3 -1
  68. package/dist/cjs/types/index.js +3 -1
  69. package/dist/cjs/types/queue.types.d.ts +1 -0
  70. package/dist/cjs/types/session.types.d.ts +0 -64
  71. package/dist/cjs/types/state.types.d.ts +1 -0
  72. package/dist/cjs/types/test-bridge.types.d.ts +38 -0
  73. package/dist/cjs/types/validation-error.types.d.ts +7 -0
  74. package/dist/cjs/types/validation-error.types.js +11 -1
  75. package/dist/cjs/types/window.types.d.ts +1 -8
  76. package/dist/cjs/utils/data/uuid.utils.d.ts +1 -1
  77. package/dist/cjs/utils/data/uuid.utils.js +7 -5
  78. package/dist/cjs/utils/emitter.utils.d.ts +8 -0
  79. package/dist/cjs/utils/emitter.utils.js +33 -0
  80. package/dist/cjs/utils/index.d.ts +1 -0
  81. package/dist/cjs/utils/index.js +1 -0
  82. package/dist/cjs/utils/logging/debug-logger.utils.d.ts +10 -51
  83. package/dist/cjs/utils/logging/debug-logger.utils.js +36 -127
  84. package/dist/cjs/utils/network/fetch-with-timeout.utils.d.ts +4 -0
  85. package/dist/cjs/utils/network/fetch-with-timeout.utils.js +25 -0
  86. package/dist/cjs/utils/network/index.d.ts +1 -0
  87. package/dist/cjs/utils/network/index.js +1 -0
  88. package/dist/cjs/utils/network/url.utils.js +2 -42
  89. package/dist/cjs/utils/security/sanitize.utils.d.ts +1 -8
  90. package/dist/cjs/utils/security/sanitize.utils.js +7 -41
  91. package/dist/cjs/utils/validations/config-validations.utils.d.ts +7 -0
  92. package/dist/cjs/utils/validations/config-validations.utils.js +77 -22
  93. package/dist/esm/api.d.ts +33 -19
  94. package/dist/esm/api.js +105 -118
  95. package/dist/esm/app.constants.d.ts +80 -1
  96. package/dist/esm/app.constants.js +89 -1
  97. package/dist/esm/app.d.ts +29 -44
  98. package/dist/esm/app.js +115 -213
  99. package/dist/esm/app.types.d.ts +2 -7
  100. package/dist/esm/app.types.js +1 -7
  101. package/dist/esm/constants/api.constants.js +10 -4
  102. package/dist/esm/constants/config.constants.d.ts +75 -0
  103. package/dist/esm/constants/config.constants.js +174 -0
  104. package/dist/esm/constants/error.constants.d.ts +29 -0
  105. package/dist/esm/constants/error.constants.js +47 -0
  106. package/dist/esm/constants/index.d.ts +3 -6
  107. package/dist/esm/constants/index.js +3 -6
  108. package/dist/esm/constants/performance.constants.d.ts +28 -0
  109. package/dist/esm/constants/performance.constants.js +40 -0
  110. package/dist/esm/handlers/click.handler.d.ts +1 -0
  111. package/dist/esm/handlers/click.handler.js +30 -49
  112. package/dist/esm/handlers/error.handler.d.ts +11 -6
  113. package/dist/esm/handlers/error.handler.js +91 -51
  114. package/dist/esm/handlers/page-view.handler.js +38 -29
  115. package/dist/esm/handlers/performance.handler.d.ts +3 -0
  116. package/dist/esm/handlers/performance.handler.js +71 -32
  117. package/dist/esm/handlers/scroll.handler.d.ts +15 -0
  118. package/dist/esm/handlers/scroll.handler.js +106 -32
  119. package/dist/esm/handlers/session.handler.d.ts +6 -20
  120. package/dist/esm/handlers/session.handler.js +38 -326
  121. package/dist/esm/integrations/google-analytics.integration.d.ts +0 -1
  122. package/dist/esm/integrations/google-analytics.integration.js +27 -98
  123. package/dist/esm/listeners/input-listener-managers.d.ts +18 -9
  124. package/dist/esm/listeners/input-listener-managers.js +23 -32
  125. package/dist/esm/listeners/touch-listener-manager.d.ts +1 -3
  126. package/dist/esm/listeners/touch-listener-manager.js +1 -23
  127. package/dist/esm/listeners/visibility-listener-manager.d.ts +1 -4
  128. package/dist/esm/listeners/visibility-listener-manager.js +6 -42
  129. package/dist/esm/managers/api.manager.d.ts +13 -3
  130. package/dist/esm/managers/api.manager.js +34 -3
  131. package/dist/esm/managers/config.manager.d.ts +53 -3
  132. package/dist/esm/managers/config.manager.js +133 -64
  133. package/dist/esm/managers/event.manager.d.ts +57 -36
  134. package/dist/esm/managers/event.manager.js +268 -419
  135. package/dist/esm/managers/sender.manager.d.ts +40 -22
  136. package/dist/esm/managers/sender.manager.js +201 -199
  137. package/dist/esm/managers/session.manager.d.ts +80 -66
  138. package/dist/esm/managers/session.manager.js +269 -524
  139. package/dist/esm/managers/state.manager.d.ts +33 -0
  140. package/dist/esm/managers/state.manager.js +78 -6
  141. package/dist/esm/managers/storage.manager.d.ts +26 -2
  142. package/dist/esm/managers/storage.manager.js +66 -33
  143. package/dist/esm/managers/tags.manager.d.ts +31 -7
  144. package/dist/esm/managers/tags.manager.js +124 -242
  145. package/dist/esm/managers/user.manager.d.ts +14 -5
  146. package/dist/esm/managers/user.manager.js +17 -9
  147. package/dist/esm/public-api.d.ts +10 -1
  148. package/dist/esm/public-api.js +14 -1
  149. package/dist/esm/test-bridge.d.ts +48 -0
  150. package/dist/esm/test-bridge.js +106 -0
  151. package/dist/esm/types/api.types.d.ts +21 -6
  152. package/dist/esm/types/api.types.js +21 -6
  153. package/dist/esm/types/config.types.d.ts +22 -84
  154. package/dist/esm/types/emitter.types.d.ts +11 -0
  155. package/dist/esm/types/emitter.types.js +5 -0
  156. package/dist/esm/types/event.types.d.ts +8 -11
  157. package/dist/esm/types/index.d.ts +3 -1
  158. package/dist/esm/types/index.js +3 -1
  159. package/dist/esm/types/queue.types.d.ts +1 -0
  160. package/dist/esm/types/session.types.d.ts +0 -64
  161. package/dist/esm/types/state.types.d.ts +1 -0
  162. package/dist/esm/types/test-bridge.types.d.ts +38 -0
  163. package/dist/esm/types/validation-error.types.d.ts +7 -0
  164. package/dist/esm/types/validation-error.types.js +9 -0
  165. package/dist/esm/types/window.types.d.ts +1 -8
  166. package/dist/esm/utils/data/uuid.utils.d.ts +1 -1
  167. package/dist/esm/utils/data/uuid.utils.js +7 -5
  168. package/dist/esm/utils/emitter.utils.d.ts +8 -0
  169. package/dist/esm/utils/emitter.utils.js +29 -0
  170. package/dist/esm/utils/index.d.ts +1 -0
  171. package/dist/esm/utils/index.js +1 -0
  172. package/dist/esm/utils/logging/debug-logger.utils.d.ts +10 -51
  173. package/dist/esm/utils/logging/debug-logger.utils.js +36 -127
  174. package/dist/esm/utils/network/fetch-with-timeout.utils.d.ts +4 -0
  175. package/dist/esm/utils/network/fetch-with-timeout.utils.js +22 -0
  176. package/dist/esm/utils/network/index.d.ts +1 -0
  177. package/dist/esm/utils/network/index.js +1 -0
  178. package/dist/esm/utils/network/url.utils.js +2 -42
  179. package/dist/esm/utils/security/sanitize.utils.d.ts +1 -8
  180. package/dist/esm/utils/security/sanitize.utils.js +6 -39
  181. package/dist/esm/utils/validations/config-validations.utils.d.ts +7 -0
  182. package/dist/esm/utils/validations/config-validations.utils.js +76 -22
  183. package/package.json +23 -16
  184. package/dist/browser/web-vitals-CCnqwnC8.mjs +0 -198
  185. package/dist/cjs/constants/browser.constants.d.ts +0 -3
  186. package/dist/cjs/constants/browser.constants.js +0 -41
  187. package/dist/cjs/constants/initialization.constants.d.ts +0 -40
  188. package/dist/cjs/constants/initialization.constants.js +0 -48
  189. package/dist/cjs/constants/limits.constants.d.ts +0 -25
  190. package/dist/cjs/constants/limits.constants.js +0 -40
  191. package/dist/cjs/constants/security.constants.d.ts +0 -1
  192. package/dist/cjs/constants/security.constants.js +0 -12
  193. package/dist/cjs/constants/timing.constants.d.ts +0 -22
  194. package/dist/cjs/constants/timing.constants.js +0 -34
  195. package/dist/cjs/constants/validation.constants.d.ts +0 -13
  196. package/dist/cjs/constants/validation.constants.js +0 -31
  197. package/dist/cjs/handlers/network.handler.d.ts +0 -16
  198. package/dist/cjs/handlers/network.handler.js +0 -136
  199. package/dist/cjs/managers/cross-tab-session.manager.d.ts +0 -170
  200. package/dist/cjs/managers/cross-tab-session.manager.js +0 -730
  201. package/dist/cjs/managers/sampling.manager.d.ts +0 -8
  202. package/dist/cjs/managers/sampling.manager.js +0 -53
  203. package/dist/cjs/managers/session-recovery.manager.d.ts +0 -65
  204. package/dist/cjs/managers/session-recovery.manager.js +0 -237
  205. package/dist/cjs/types/web-vitals.types.d.ts +0 -6
  206. package/dist/esm/constants/browser.constants.d.ts +0 -3
  207. package/dist/esm/constants/browser.constants.js +0 -38
  208. package/dist/esm/constants/initialization.constants.d.ts +0 -40
  209. package/dist/esm/constants/initialization.constants.js +0 -45
  210. package/dist/esm/constants/limits.constants.d.ts +0 -25
  211. package/dist/esm/constants/limits.constants.js +0 -37
  212. package/dist/esm/constants/security.constants.d.ts +0 -1
  213. package/dist/esm/constants/security.constants.js +0 -9
  214. package/dist/esm/constants/timing.constants.d.ts +0 -22
  215. package/dist/esm/constants/timing.constants.js +0 -31
  216. package/dist/esm/constants/validation.constants.d.ts +0 -13
  217. package/dist/esm/constants/validation.constants.js +0 -28
  218. package/dist/esm/handlers/network.handler.d.ts +0 -16
  219. package/dist/esm/handlers/network.handler.js +0 -132
  220. package/dist/esm/managers/cross-tab-session.manager.d.ts +0 -170
  221. package/dist/esm/managers/cross-tab-session.manager.js +0 -726
  222. package/dist/esm/managers/sampling.manager.d.ts +0 -8
  223. package/dist/esm/managers/sampling.manager.js +0 -49
  224. package/dist/esm/managers/session-recovery.manager.d.ts +0 -65
  225. package/dist/esm/managers/session-recovery.manager.js +0 -233
  226. package/dist/esm/types/web-vitals.types.d.ts +0 -6
  227. /package/dist/cjs/types/{web-vitals.types.js → test-bridge.types.js} +0 -0
  228. /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
- * Client-facing error - Configuration/usage errors by the client
8
- * Console: qa and debug modes | Events: NODE_ENV=dev
9
- */
10
- clientError(namespace, message, data) {
11
- this.logMessage('CLIENT_ERROR', namespace, message, data);
12
- }
13
- /**
14
- * Client-facing warning - Configuration/usage warnings by the client
15
- * Console: qa and debug modes | Events: NODE_ENV=dev
16
- */
17
- clientWarn(namespace, message, data) {
18
- this.logMessage('CLIENT_WARN', namespace, message, data);
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 formattedMessage = this.formatMessage(namespace, message);
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[consoleMethod](formattedMessage, data);
20
+ console[method](formattedMsg, data);
100
21
  }
101
22
  else {
102
- console[consoleMethod](formattedMessage);
103
- }
104
- if (process.env.NODE_ENV === 'dev') {
105
- this.dispatchEvent(level, namespace, message, data);
106
- }
107
- }
108
- /**
109
- * Dispatches tracelog:log events for E2E testing and development debugging
110
- */
111
- dispatchEvent(level, namespace, message, data) {
112
- if (typeof window === 'undefined' || typeof CustomEvent === 'undefined') {
113
- return;
114
- }
115
- try {
116
- const event = new CustomEvent('tracelog:log', {
117
- detail: {
118
- timestamp: new Date().toISOString(),
119
- level,
120
- namespace,
121
- message,
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,4 @@
1
+ export interface FetchWithTimeoutOptions extends RequestInit {
2
+ timeout?: number;
3
+ }
4
+ export declare function fetchWithTimeout(url: string, options?: FetchWithTimeoutOptions): Promise<Response>;
@@ -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
+ }
@@ -1 +1,2 @@
1
1
  export * from './url.utils';
2
+ export * from './fetch-with-timeout.utils';
@@ -1 +1,2 @@
1
1
  export * from './url.utils';
2
+ export * from './fetch-with-timeout.utils';
@@ -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
- path = new URL(url, window.location.origin).pathname;
109
- debugLog.debug('URLUtils', 'Extracted path from URL', { path });
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/common.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 but handle invalid selectors gracefully
102
- if (typeof document !== 'undefined') {
103
- try {
104
- document.querySelector(selector);
105
- }
106
- catch {
107
- // Invalid CSS selectors are handled gracefully
108
- // they will be ignored by the ScrollHandler and it will fall back to window scrolling
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 !== undefined) {
175
- if (typeof samplingRate !== 'number') {
176
- errors.push('samplingRate must be a number');
177
- }
178
- else if (samplingRate < 0 || samplingRate > 1) {
179
- errors.push('samplingRate must be between 0 and 1');
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.8",
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:production": "NODE_ENV=production vite build",
27
- "build:browser:testing": "NODE_ENV=dev vite 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
- "serve:test": "http-server tests/fixtures -p 3000 --cors",
39
- "serve:playground": "http-server playground -p 3001 --cors",
40
- "playground:setup": "npm run build:browser:playground && cp dist/browser/tracelog.js playground/tracelog.js",
41
- "playground:dev": "npm run playground:setup && npm run serve:playground",
42
- "test:e2e": "npm run build:browser:testing && cp dist/browser/tracelog.js tests/fixtures/tracelog.js && NODE_ENV=dev playwright test",
43
- "test:anomaly-report": "node scripts/test-anomaly-report.js",
44
- "ci:health-check": "node scripts/ci-health-check.js",
45
- "ci:health-strict": "TRACELOG_STRICT_MODE=true node scripts/ci-health-check.js",
46
- "ci:pre-deploy": "npm run build:all && npm run ci:health-check",
47
- "ci:pre-release": "npm run build:all && npm run ci:health-strict",
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
  }