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