@tracelog/lib 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. package/README.md +58 -24
  2. package/dist/browser/tracelog.js +1928 -3297
  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 +74 -1
  6. package/dist/cjs/app.constants.js +77 -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 +16 -10
  28. package/dist/cjs/handlers/scroll.handler.js +119 -185
  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 +74 -1
  96. package/dist/esm/app.constants.js +76 -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 +16 -10
  118. package/dist/esm/handlers/scroll.handler.js +120 -186
  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 -27
  190. package/dist/cjs/constants/limits.constants.js +0 -43
  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 -27
  211. package/dist/esm/constants/limits.constants.js +0 -40
  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,53 +1,44 @@
1
1
  import { debugLog } from '../utils/logging';
2
- export class MouseListenerManager {
2
+ /**
3
+ * Base class for input listener managers to reduce code duplication
4
+ */
5
+ class BaseInputListenerManager {
3
6
  constructor(onActivity) {
4
7
  this.options = { passive: true };
5
8
  this.onActivity = onActivity;
6
9
  }
7
10
  setup() {
8
11
  try {
9
- window.addEventListener('mousemove', this.onActivity, this.options);
10
- window.addEventListener('mousedown', this.onActivity, this.options);
11
- window.addEventListener('wheel', this.onActivity, this.options);
12
+ this.events.forEach((event) => {
13
+ window.addEventListener(event, this.onActivity, this.options);
14
+ });
12
15
  }
13
16
  catch (error) {
14
- debugLog.error('MouseListenerManager', 'Failed to setup mouse listeners', { error });
15
- throw error;
17
+ debugLog.error(this.logPrefix, `Failed to setup ${this.logPrefix.toLowerCase()} listeners`, { error });
16
18
  }
17
19
  }
18
20
  cleanup() {
19
21
  try {
20
- window.removeEventListener('mousemove', this.onActivity);
21
- window.removeEventListener('mousedown', this.onActivity);
22
- window.removeEventListener('wheel', this.onActivity);
22
+ this.events.forEach((event) => {
23
+ window.removeEventListener(event, this.onActivity);
24
+ });
23
25
  }
24
26
  catch (error) {
25
- debugLog.warn('MouseListenerManager', 'Error during mouse listeners cleanup', { error });
27
+ debugLog.warn(this.logPrefix, `Error during ${this.logPrefix.toLowerCase()} listeners cleanup`, { error });
26
28
  }
27
29
  }
28
30
  }
29
- export class KeyboardListenerManager {
30
- constructor(onActivity) {
31
- this.options = { passive: true };
32
- this.onActivity = onActivity;
31
+ export class MouseListenerManager extends BaseInputListenerManager {
32
+ constructor() {
33
+ super(...arguments);
34
+ this.events = ['mousemove', 'mousedown', 'wheel'];
35
+ this.logPrefix = 'MouseListenerManager';
33
36
  }
34
- setup() {
35
- try {
36
- window.addEventListener('keydown', this.onActivity, this.options);
37
- window.addEventListener('keypress', this.onActivity, this.options);
38
- }
39
- catch (error) {
40
- debugLog.error('KeyboardListenerManager', 'Failed to setup keyboard listeners', { error });
41
- throw error;
42
- }
43
- }
44
- cleanup() {
45
- try {
46
- window.removeEventListener('keydown', this.onActivity);
47
- window.removeEventListener('keypress', this.onActivity);
48
- }
49
- catch (error) {
50
- debugLog.warn('KeyboardListenerManager', 'Error during keyboard listeners cleanup', { error });
51
- }
37
+ }
38
+ export class KeyboardListenerManager extends BaseInputListenerManager {
39
+ constructor() {
40
+ super(...arguments);
41
+ this.events = ['keydown'];
42
+ this.logPrefix = 'KeyboardListenerManager';
52
43
  }
53
44
  }
@@ -2,9 +2,7 @@ import { EventListenerManager } from './listeners.types';
2
2
  export declare class TouchListenerManager implements EventListenerManager {
3
3
  private readonly onActivity;
4
4
  private readonly options;
5
- private readonly motionThreshold;
6
- constructor(onActivity: () => void, motionThreshold: number);
5
+ constructor(onActivity: () => void);
7
6
  setup(): void;
8
7
  cleanup(): void;
9
- private readonly handleDeviceMotion;
10
8
  }
@@ -1,23 +1,8 @@
1
1
  import { debugLog } from '../utils/logging';
2
2
  export class TouchListenerManager {
3
- constructor(onActivity, motionThreshold) {
3
+ constructor(onActivity) {
4
4
  this.options = { passive: true };
5
- this.handleDeviceMotion = (event) => {
6
- try {
7
- const acceleration = event.acceleration;
8
- if (acceleration) {
9
- const totalAcceleration = Math.abs(acceleration.x ?? 0) + Math.abs(acceleration.y ?? 0) + Math.abs(acceleration.z ?? 0);
10
- if (totalAcceleration > this.motionThreshold) {
11
- this.onActivity();
12
- }
13
- }
14
- }
15
- catch (error) {
16
- debugLog.warn('TouchListenerManager', 'Error handling device motion event', { error });
17
- }
18
- };
19
5
  this.onActivity = onActivity;
20
- this.motionThreshold = motionThreshold;
21
6
  }
22
7
  setup() {
23
8
  try {
@@ -25,10 +10,6 @@ export class TouchListenerManager {
25
10
  window.addEventListener('touchmove', this.onActivity, this.options);
26
11
  window.addEventListener('touchend', this.onActivity, this.options);
27
12
  window.addEventListener('orientationchange', this.onActivity, this.options);
28
- const hasDeviceMotion = 'DeviceMotionEvent' in window;
29
- if (hasDeviceMotion) {
30
- window.addEventListener('devicemotion', this.handleDeviceMotion, this.options);
31
- }
32
13
  }
33
14
  catch (error) {
34
15
  debugLog.error('TouchListenerManager', 'Failed to setup touch listeners', { error });
@@ -41,9 +22,6 @@ export class TouchListenerManager {
41
22
  window.removeEventListener('touchmove', this.onActivity);
42
23
  window.removeEventListener('touchend', this.onActivity);
43
24
  window.removeEventListener('orientationchange', this.onActivity);
44
- if ('DeviceMotionEvent' in window) {
45
- window.removeEventListener('devicemotion', this.handleDeviceMotion);
46
- }
47
25
  }
48
26
  catch (error) {
49
27
  debugLog.warn('TouchListenerManager', 'Error during touch listeners cleanup', { error });
@@ -2,11 +2,8 @@ import { EventListenerManager } from './listeners.types';
2
2
  export declare class VisibilityListenerManager implements EventListenerManager {
3
3
  private readonly onActivity;
4
4
  private readonly onVisibilityChange;
5
- private readonly isMobile;
6
5
  private readonly options;
7
- constructor(onActivity: () => void, onVisibilityChange: () => void, isMobile: boolean);
6
+ constructor(onActivity: () => void, onVisibilityChange: () => void);
8
7
  setup(): void;
9
8
  cleanup(): void;
10
- private setupMobileEvents;
11
- private cleanupMobileEvents;
12
9
  }
@@ -1,31 +1,27 @@
1
1
  import { debugLog } from '../utils/logging';
2
2
  export class VisibilityListenerManager {
3
- constructor(onActivity, onVisibilityChange, isMobile) {
3
+ constructor(onActivity, onVisibilityChange) {
4
4
  this.options = { passive: true };
5
5
  this.onActivity = onActivity;
6
6
  this.onVisibilityChange = onVisibilityChange;
7
- this.isMobile = isMobile;
8
7
  }
9
8
  setup() {
10
9
  try {
11
- const hasVisibilityAPI = 'visibilityState' in document;
12
- if (hasVisibilityAPI) {
10
+ // Core visibility API support
11
+ if ('visibilityState' in document) {
13
12
  document.addEventListener('visibilitychange', this.onVisibilityChange, this.options);
14
13
  }
14
+ // Window focus/blur events
15
15
  window.addEventListener('blur', this.onVisibilityChange, this.options);
16
16
  window.addEventListener('focus', this.onActivity, this.options);
17
- const hasNetworkAPI = 'onLine' in navigator;
18
- if (hasNetworkAPI) {
17
+ // Basic network status detection
18
+ if ('onLine' in navigator) {
19
19
  window.addEventListener('online', this.onActivity, this.options);
20
20
  window.addEventListener('offline', this.onVisibilityChange, this.options);
21
21
  }
22
- if (this.isMobile) {
23
- this.setupMobileEvents();
24
- }
25
22
  }
26
23
  catch (error) {
27
24
  debugLog.error('VisibilityListenerManager', 'Failed to setup visibility listeners', { error });
28
- throw error;
29
25
  }
30
26
  }
31
27
  cleanup() {
@@ -39,41 +35,9 @@ export class VisibilityListenerManager {
39
35
  window.removeEventListener('online', this.onActivity);
40
36
  window.removeEventListener('offline', this.onVisibilityChange);
41
37
  }
42
- if (this.isMobile) {
43
- this.cleanupMobileEvents();
44
- }
45
38
  }
46
39
  catch (error) {
47
40
  debugLog.warn('VisibilityListenerManager', 'Error during visibility listeners cleanup', { error });
48
41
  }
49
42
  }
50
- setupMobileEvents() {
51
- try {
52
- document.addEventListener('pause', this.onVisibilityChange, this.options);
53
- document.addEventListener('resume', this.onActivity, this.options);
54
- const hasOrientationAPI = 'orientation' in screen;
55
- if (hasOrientationAPI) {
56
- screen.orientation.addEventListener('change', this.onActivity, this.options);
57
- }
58
- window.addEventListener('pageshow', this.onActivity, this.options);
59
- window.addEventListener('pagehide', this.onActivity, this.options);
60
- }
61
- catch (error) {
62
- debugLog.warn('VisibilityListenerManager', 'Failed to setup mobile listeners', { error });
63
- }
64
- }
65
- cleanupMobileEvents() {
66
- try {
67
- document.removeEventListener('pause', this.onVisibilityChange);
68
- document.removeEventListener('resume', this.onActivity);
69
- if ('orientation' in screen) {
70
- screen.orientation.removeEventListener('change', this.onActivity);
71
- }
72
- window.removeEventListener('pageshow', this.onActivity);
73
- window.removeEventListener('pagehide', this.onActivity);
74
- }
75
- catch (error) {
76
- debugLog.warn('VisibilityListenerManager', 'Error during mobile listeners cleanup', { error });
77
- }
78
- }
79
43
  }
@@ -1,3 +1,13 @@
1
- export declare class ApiManager {
2
- getUrl(id: string, allowHttp?: boolean): string;
3
- }
1
+ /**
2
+ * Generates API URL for TraceLog service based on project ID
3
+ *
4
+ * Handles two special cases:
5
+ * - 'localhost:PORT' - for local development (generates http://localhost:PORT)
6
+ * - Regular project IDs - generates subdomain URLs via getApiUrl utility
7
+ *
8
+ * @param id Project ID or localhost address
9
+ * @param allowHttp Whether to allow HTTP protocol (default: false)
10
+ * @returns Generated API URL
11
+ * @throws Error if URL generation or validation fails
12
+ */
13
+ export declare function getApiUrlForProject(id: string, allowHttp?: boolean): string;
@@ -1,10 +1,41 @@
1
1
  import { getApiUrl, isValidUrl } from '../utils';
2
- export class ApiManager {
3
- getUrl(id, allowHttp = false) {
2
+ import { SpecialProjectId } from '../types';
3
+ import { debugLog } from '../utils/logging';
4
+ /**
5
+ * Generates API URL for TraceLog service based on project ID
6
+ *
7
+ * Handles two special cases:
8
+ * - 'localhost:PORT' - for local development (generates http://localhost:PORT)
9
+ * - Regular project IDs - generates subdomain URLs via getApiUrl utility
10
+ *
11
+ * @param id Project ID or localhost address
12
+ * @param allowHttp Whether to allow HTTP protocol (default: false)
13
+ * @returns Generated API URL
14
+ * @throws Error if URL generation or validation fails
15
+ */
16
+ export function getApiUrlForProject(id, allowHttp = false) {
17
+ try {
18
+ // Handle localhost development case
19
+ if (id.startsWith(SpecialProjectId.Localhost)) {
20
+ const url = `http://${id}`;
21
+ if (!isValidUrl(url, true)) {
22
+ throw new Error(`Invalid localhost URL format: ${id}`);
23
+ }
24
+ return url;
25
+ }
26
+ // Handle regular project ID case
4
27
  const url = getApiUrl(id, allowHttp);
5
28
  if (!isValidUrl(url, allowHttp)) {
6
- throw new Error('Invalid URL');
29
+ throw new Error(`Generated API URL failed validation: ${url}`);
7
30
  }
8
31
  return url;
9
32
  }
33
+ catch (error) {
34
+ debugLog.error('ApiManager', 'API URL generation failed', {
35
+ projectId: id,
36
+ allowHttp,
37
+ error: error instanceof Error ? error.message : error,
38
+ });
39
+ throw error;
40
+ }
10
41
  }
@@ -1,7 +1,57 @@
1
1
  import { AppConfig, Config } from '../types';
2
+ /**
3
+ * Configuration manager responsible for loading and merging application configuration.
4
+ *
5
+ * Handles three configuration sources:
6
+ * 1. Default configuration (fallback values)
7
+ * 2. API configuration (server-side settings)
8
+ * 3. App configuration (client initialization settings)
9
+ *
10
+ * Supports special project IDs for development and testing:
11
+ * - 'skip': Bypasses all network calls, uses defaults
12
+ * - 'localhost:PORT': Loads config from local development server
13
+ */
2
14
  export declare class ConfigManager {
15
+ private static readonly LOCALHOST_PATTERN;
16
+ private static readonly PRODUCTION_DOMAINS;
17
+ /**
18
+ * Gets complete configuration by merging default, API, and app configurations.
19
+ *
20
+ * @param apiUrl - Base URL for the configuration API
21
+ * @param appConfig - Client-side configuration from init()
22
+ * @returns Promise<Config> - Merged configuration object
23
+ */
3
24
  get(apiUrl: string, appConfig: AppConfig): Promise<Config>;
4
- private load;
5
- private getUrl;
6
- private getDefaultConfig;
25
+ /**
26
+ * Loads configuration from API and merges with app config.
27
+ */
28
+ private loadFromApi;
29
+ /**
30
+ * Builds the configuration URL based on project type and QA mode.
31
+ */
32
+ private buildConfigUrl;
33
+ /**
34
+ * Builds request headers based on project configuration.
35
+ */
36
+ private buildHeaders;
37
+ /**
38
+ * Parses and validates JSON response from config API.
39
+ */
40
+ private parseJsonResponse;
41
+ /**
42
+ * Validates localhost project ID format and port range.
43
+ */
44
+ private validateLocalhostProjectId;
45
+ /**
46
+ * Checks if QA mode is enabled via URL parameter.
47
+ */
48
+ private isQaModeEnabled;
49
+ /**
50
+ * Merges API configuration with app configuration and applies mode-specific settings.
51
+ */
52
+ private mergeConfigurations;
53
+ /**
54
+ * Creates default configuration for skip mode and fallback scenarios.
55
+ */
56
+ private createDefaultConfig;
7
57
  }
@@ -1,90 +1,159 @@
1
- import { DEFAULT_API_CONFIG, DEFAULT_CONFIG } from '../constants';
1
+ import { DEFAULT_API_CONFIG, DEFAULT_CONFIG, REQUEST_TIMEOUT_MS } from '../constants';
2
2
  import { Mode, SpecialProjectId } from '../types';
3
- import { isValidUrl, sanitizeApiConfig } from '../utils';
3
+ import { sanitizeApiConfig, fetchWithTimeout, normalizeConfig } from '../utils';
4
4
  import { debugLog } from '../utils/logging';
5
+ /**
6
+ * Configuration manager responsible for loading and merging application configuration.
7
+ *
8
+ * Handles three configuration sources:
9
+ * 1. Default configuration (fallback values)
10
+ * 2. API configuration (server-side settings)
11
+ * 3. App configuration (client initialization settings)
12
+ *
13
+ * Supports special project IDs for development and testing:
14
+ * - 'skip': Bypasses all network calls, uses defaults
15
+ * - 'localhost:PORT': Loads config from local development server
16
+ */
5
17
  export class ConfigManager {
18
+ /**
19
+ * Gets complete configuration by merging default, API, and app configurations.
20
+ *
21
+ * @param apiUrl - Base URL for the configuration API
22
+ * @param appConfig - Client-side configuration from init()
23
+ * @returns Promise<Config> - Merged configuration object
24
+ */
6
25
  async get(apiUrl, appConfig) {
7
- if (appConfig.id === SpecialProjectId.HttpSkip) {
8
- debugLog.debug('ConfigManager', 'Using special project id');
9
- return this.getDefaultConfig(appConfig);
26
+ // Handle skip mode - no network calls
27
+ if (appConfig.id === SpecialProjectId.Skip) {
28
+ return this.createDefaultConfig(appConfig);
10
29
  }
11
- debugLog.debug('ConfigManager', 'Loading config from API', { apiUrl, projectId: appConfig.id });
12
- const config = await this.load(apiUrl, appConfig, appConfig.id === SpecialProjectId.HttpLocal);
13
- debugLog.info('ConfigManager', 'Config loaded successfully', {
30
+ const config = await this.loadFromApi(apiUrl, appConfig);
31
+ const { config: normalizedConfig } = normalizeConfig(config);
32
+ debugLog.info('ConfigManager', 'Configuration loaded', {
14
33
  projectId: appConfig.id,
15
- mode: config.mode,
16
- hasExcludedPaths: !!config.excludedUrlPaths?.length,
17
- hasGlobalMetadata: !!config.globalMetadata,
34
+ mode: normalizedConfig.mode,
35
+ hasTags: !!normalizedConfig.tags?.length,
36
+ hasExclusions: !!normalizedConfig.excludedUrlPaths?.length,
18
37
  });
19
- return config;
38
+ return normalizedConfig;
20
39
  }
21
- async load(apiUrl, appConfig, useLocalServer) {
40
+ /**
41
+ * Loads configuration from API and merges with app config.
42
+ */
43
+ async loadFromApi(apiUrl, appConfig) {
22
44
  try {
23
- const configUrl = useLocalServer ? `${window.location.origin}/config` : this.getUrl(apiUrl);
24
- if (!configUrl) {
25
- throw new Error('Config URL is not valid or not allowed');
26
- }
27
- const response = await fetch(configUrl, {
45
+ const configUrl = this.buildConfigUrl(apiUrl, appConfig);
46
+ const headers = this.buildHeaders(appConfig);
47
+ const response = await fetchWithTimeout(configUrl, {
28
48
  method: 'GET',
29
- headers: { 'Content-Type': 'application/json' },
49
+ headers,
50
+ timeout: REQUEST_TIMEOUT_MS,
30
51
  });
31
52
  if (!response.ok) {
32
- const error = `HTTP ${response.status}: ${response.statusText}`;
33
- debugLog.error('ConfigManager', 'Config API request failed', {
34
- status: response.status,
35
- statusText: response.statusText,
36
- configUrl,
37
- });
38
- throw new Error(error);
39
- }
40
- const rawData = await response.json();
41
- if (rawData === undefined || rawData === null || typeof rawData !== 'object' || Array.isArray(rawData)) {
42
- debugLog.error('ConfigManager', 'Invalid config API response format', {
43
- responseType: typeof rawData,
44
- isArray: Array.isArray(rawData),
45
- });
46
- throw new Error('Invalid config API response: expected object');
47
- }
48
- const safeApiConfig = sanitizeApiConfig(rawData);
49
- const apiConfig = { ...DEFAULT_API_CONFIG, ...safeApiConfig };
50
- const mergedConfig = { ...apiConfig, ...appConfig };
51
- // Check if qaMode=true is in URL and automatically set mode to 'qa'
52
- const urlParameters = new URLSearchParams(window.location.search);
53
- const isQaMode = urlParameters.get('qaMode') === 'true';
54
- if (isQaMode && !mergedConfig.mode) {
55
- mergedConfig.mode = Mode.QA;
56
- debugLog.info('ConfigManager', 'QA mode enabled via URL parameter');
53
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
57
54
  }
58
- const errorSampling = Object.values(Mode).includes(mergedConfig.mode)
59
- ? 1
60
- : (mergedConfig.errorSampling ?? 0.1);
61
- const finalConfig = { ...mergedConfig, errorSampling };
62
- return finalConfig;
55
+ const rawData = await this.parseJsonResponse(response);
56
+ return this.mergeConfigurations(rawData, appConfig);
63
57
  }
64
58
  catch (error) {
65
59
  const errorMessage = error instanceof Error ? error.message : 'Unknown error';
66
- debugLog.error('ConfigManager', 'Failed to load config', { error: errorMessage, apiUrl });
67
- throw new Error(`Failed to load config: ${errorMessage}`);
60
+ debugLog.error('ConfigManager', 'Failed to load configuration', {
61
+ error: errorMessage,
62
+ apiUrl,
63
+ projectId: appConfig.id,
64
+ });
65
+ throw new Error(`Configuration load failed: ${errorMessage}`);
66
+ }
67
+ }
68
+ /**
69
+ * Builds the configuration URL based on project type and QA mode.
70
+ */
71
+ buildConfigUrl(apiUrl, appConfig) {
72
+ const isLocalhost = appConfig.id.startsWith(SpecialProjectId.Localhost);
73
+ if (isLocalhost) {
74
+ this.validateLocalhostProjectId(appConfig.id);
75
+ return `http://${appConfig.id}/config`;
76
+ }
77
+ const baseUrl = `${apiUrl}/config`;
78
+ const isQaMode = this.isQaModeEnabled();
79
+ return isQaMode ? `${baseUrl}?qaMode=true` : baseUrl;
80
+ }
81
+ /**
82
+ * Builds request headers based on project configuration.
83
+ */
84
+ buildHeaders(appConfig) {
85
+ const headers = {
86
+ 'Content-Type': 'application/json',
87
+ };
88
+ if (appConfig.id.startsWith(SpecialProjectId.Localhost)) {
89
+ headers['X-TraceLog-Project'] = appConfig.id;
90
+ }
91
+ return headers;
92
+ }
93
+ /**
94
+ * Parses and validates JSON response from config API.
95
+ */
96
+ async parseJsonResponse(response) {
97
+ const contentType = response.headers.get('content-type');
98
+ if (!contentType?.includes('application/json')) {
99
+ throw new Error('Invalid response content-type, expected JSON');
100
+ }
101
+ const rawData = await response.json();
102
+ if (!rawData || typeof rawData !== 'object' || Array.isArray(rawData)) {
103
+ throw new Error('Invalid response format, expected object');
68
104
  }
105
+ return rawData;
69
106
  }
70
- getUrl(apiUrl) {
71
- const urlParameters = new URLSearchParams(window.location.search);
72
- const isQaMode = urlParameters.get('qaMode') === 'true';
73
- let configUrl = `${apiUrl}/config`;
74
- if (isQaMode) {
75
- configUrl += '?qaMode=true';
107
+ /**
108
+ * Validates localhost project ID format and port range.
109
+ */
110
+ validateLocalhostProjectId(projectId) {
111
+ if (!ConfigManager.LOCALHOST_PATTERN.test(projectId)) {
112
+ throw new Error(`Invalid localhost format. Expected 'localhost:PORT', got '${projectId}'`);
76
113
  }
77
- if (!isValidUrl(configUrl)) {
78
- debugLog.clientError('ConfigManager', 'Invalid config URL provided', { configUrl });
79
- throw new Error('Config URL is not valid or not allowed');
114
+ const port = parseInt(projectId.split(':')[1], 10);
115
+ if (port < 1 || port > 65535) {
116
+ throw new Error(`Port must be between 1 and 65535, got ${port}`);
80
117
  }
81
- return configUrl;
82
118
  }
83
- getDefaultConfig(appConfig) {
84
- return DEFAULT_CONFIG({
119
+ /**
120
+ * Checks if QA mode is enabled via URL parameter.
121
+ */
122
+ isQaModeEnabled() {
123
+ const params = new URLSearchParams(window.location.search);
124
+ return params.get('qaMode') === 'true';
125
+ }
126
+ /**
127
+ * Merges API configuration with app configuration and applies mode-specific settings.
128
+ */
129
+ mergeConfigurations(rawApiConfig, appConfig) {
130
+ const safeApiConfig = sanitizeApiConfig(rawApiConfig);
131
+ const apiConfig = { ...DEFAULT_API_CONFIG, ...safeApiConfig };
132
+ const mergedConfig = DEFAULT_CONFIG({ ...appConfig, ...apiConfig });
133
+ const { config: normalizedConfig } = normalizeConfig(mergedConfig);
134
+ // Apply QA mode if enabled via URL parameter
135
+ if (this.isQaModeEnabled() && !normalizedConfig.mode) {
136
+ normalizedConfig.mode = Mode.QA;
137
+ debugLog.info('ConfigManager', 'QA mode enabled via URL parameter');
138
+ }
139
+ // Set error sampling based on mode
140
+ const errorSampling = Object.values(Mode).includes(normalizedConfig.mode)
141
+ ? 1 // Full sampling for debug/qa modes
142
+ : (normalizedConfig.errorSampling ?? 0.1); // Default sampling for production
143
+ return { ...normalizedConfig, errorSampling };
144
+ }
145
+ /**
146
+ * Creates default configuration for skip mode and fallback scenarios.
147
+ */
148
+ createDefaultConfig(appConfig) {
149
+ const defaultConfig = DEFAULT_CONFIG({
85
150
  ...appConfig,
86
151
  errorSampling: 1,
87
- ...(Object.values(SpecialProjectId).includes(appConfig.id) && { mode: Mode.DEBUG }),
152
+ ...(appConfig.id === SpecialProjectId.Skip && { mode: Mode.DEBUG }),
88
153
  });
154
+ const { config } = normalizeConfig(defaultConfig);
155
+ return config;
89
156
  }
90
157
  }
158
+ ConfigManager.LOCALHOST_PATTERN = /^localhost:\d{1,5}$/;
159
+ ConfigManager.PRODUCTION_DOMAINS = [/^https:\/\/.*\.tracelog\.app$/, /^https:\/\/.*\.tracelog\.dev$/];