@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,4 +1,4 @@
1
- import { MAX_RETRY_ATTEMPTS, RETRY_DELAY_MS, SCROLL_DEBOUNCE_TIME_MS, SIGNIFICANT_SCROLL_DELTA } from '../constants';
1
+ import { MAX_SCROLL_EVENTS_PER_SESSION, MIN_SCROLL_DEPTH_CHANGE, SCROLL_DEBOUNCE_TIME_MS, SCROLL_MIN_EVENT_INTERVAL_MS, SIGNIFICANT_SCROLL_DELTA, } from '../constants';
2
2
  import { EventType, ScrollDirection } from '../types';
3
3
  import { StateManager } from '../managers/state.manager';
4
4
  import { debugLog } from '../utils/logging';
@@ -6,51 +6,31 @@ export class ScrollHandler extends StateManager {
6
6
  constructor(eventManager) {
7
7
  super();
8
8
  this.containers = [];
9
- this.pendingSelectors = [];
10
- this.mutationObserver = null;
11
- this.windowFallbackNeeded = false;
9
+ this.limitWarningLogged = false;
10
+ this.minDepthChange = MIN_SCROLL_DEPTH_CHANGE;
11
+ this.minIntervalMs = SCROLL_MIN_EVENT_INTERVAL_MS;
12
+ this.maxEventsPerSession = MAX_SCROLL_EVENTS_PER_SESSION;
12
13
  this.eventManager = eventManager;
13
14
  }
14
15
  startTracking() {
16
+ this.limitWarningLogged = false;
17
+ this.applyConfigOverrides();
18
+ this.set('scrollEventCount', 0);
15
19
  const raw = this.get('config').scrollContainerSelectors;
16
20
  const selectors = Array.isArray(raw) ? raw : typeof raw === 'string' ? [raw] : [];
17
- debugLog.debug('ScrollHandler', 'Starting scroll tracking', { selectorsCount: selectors.length });
18
- // No custom selectors: track window immediately
19
- if (selectors.length === 0) {
20
- this.setupScrollContainer(window);
21
- return;
21
+ const elements = selectors
22
+ .map((sel) => this.safeQuerySelector(sel))
23
+ .filter((element) => element instanceof HTMLElement);
24
+ if (elements.length === 0) {
25
+ elements.push(window);
22
26
  }
23
- const foundElements = [];
24
- const notFoundSelectors = [];
25
- for (const selector of selectors) {
26
- const element = this.safeQuerySelector(selector);
27
- if (element instanceof HTMLElement) {
28
- foundElements.push(element);
29
- }
30
- else if (element === null) {
31
- notFoundSelectors.push(selector);
32
- }
33
- }
34
- // Setup found elements
35
- for (const element of foundElements) {
27
+ for (const element of elements) {
36
28
  this.setupScrollContainer(element);
37
29
  }
38
- // If we have pending selectors, set up retry logic
39
- if (notFoundSelectors.length > 0) {
40
- this.windowFallbackNeeded = true;
41
- this.setupPendingSelectors(notFoundSelectors);
42
- }
43
- else if (this.containers.length === 0) {
44
- // No elements found and none pending: use window fallback immediately
45
- this.setupScrollContainer(window);
46
- }
47
30
  }
48
31
  stopTracking() {
49
- debugLog.debug('ScrollHandler', 'Stopping scroll tracking', { containersCount: this.containers.length });
50
32
  for (const container of this.containers) {
51
- if (container.debounceTimer) {
52
- clearTimeout(container.debounceTimer);
53
- }
33
+ this.clearContainerTimer(container);
54
34
  if (container.element instanceof Window) {
55
35
  window.removeEventListener('scroll', container.listener);
56
36
  }
@@ -59,43 +39,38 @@ export class ScrollHandler extends StateManager {
59
39
  }
60
40
  }
61
41
  this.containers.length = 0;
62
- if (this.mutationObserver) {
63
- this.mutationObserver.disconnect();
64
- this.mutationObserver = null;
65
- }
66
- this.pendingSelectors.length = 0;
42
+ this.set('scrollEventCount', 0);
43
+ this.limitWarningLogged = false;
67
44
  }
68
45
  setupScrollContainer(element) {
69
- // Skip if already tracking this element
70
- if (this.containers.some((c) => c.element === element)) {
46
+ // Skip setup for non-scrollable elements
47
+ if (element !== window && !this.isElementScrollable(element)) {
71
48
  return;
72
49
  }
73
- const container = {
74
- element,
75
- lastScrollPos: this.getScrollTop(element),
76
- debounceTimer: null,
77
- listener: () => { },
78
- };
79
50
  const handleScroll = () => {
80
51
  if (this.get('suppressNextScroll')) {
81
- this.set('suppressNextScroll', false);
82
52
  return;
83
53
  }
84
- if (container.debounceTimer) {
85
- clearTimeout(container.debounceTimer);
86
- }
54
+ this.clearContainerTimer(container);
87
55
  container.debounceTimer = window.setTimeout(() => {
88
56
  const scrollData = this.calculateScrollData(container);
89
57
  if (scrollData) {
90
- this.eventManager.track({
91
- type: EventType.SCROLL,
92
- scroll_data: scrollData,
93
- });
58
+ const now = Date.now();
59
+ this.processScrollEvent(container, scrollData, now);
94
60
  }
95
61
  container.debounceTimer = null;
96
62
  }, SCROLL_DEBOUNCE_TIME_MS);
97
63
  };
98
- container.listener = handleScroll;
64
+ const initialScrollTop = this.getScrollTop(element);
65
+ const container = {
66
+ element,
67
+ lastScrollPos: initialScrollTop,
68
+ lastDepth: this.calculateScrollDepth(initialScrollTop, this.getScrollHeight(element), this.getViewportHeight(element)),
69
+ lastDirection: ScrollDirection.DOWN,
70
+ lastEventTime: 0,
71
+ debounceTimer: null,
72
+ listener: handleScroll,
73
+ };
99
74
  this.containers.push(container);
100
75
  if (element instanceof Window) {
101
76
  window.addEventListener('scroll', handleScroll, { passive: true });
@@ -104,43 +79,95 @@ export class ScrollHandler extends StateManager {
104
79
  element.addEventListener('scroll', handleScroll, { passive: true });
105
80
  }
106
81
  }
82
+ processScrollEvent(container, scrollData, timestamp) {
83
+ if (!this.shouldEmitScrollEvent(container, scrollData, timestamp)) {
84
+ return;
85
+ }
86
+ container.lastEventTime = timestamp;
87
+ container.lastDepth = scrollData.depth;
88
+ container.lastDirection = scrollData.direction;
89
+ const currentCount = this.get('scrollEventCount') ?? 0;
90
+ this.set('scrollEventCount', currentCount + 1);
91
+ this.eventManager.track({
92
+ type: EventType.SCROLL,
93
+ scroll_data: scrollData,
94
+ });
95
+ }
96
+ shouldEmitScrollEvent(container, scrollData, timestamp) {
97
+ if (this.hasReachedSessionLimit()) {
98
+ this.logLimitOnce();
99
+ return false;
100
+ }
101
+ if (!this.hasElapsedMinimumInterval(container, timestamp)) {
102
+ return false;
103
+ }
104
+ if (!this.hasSignificantDepthChange(container, scrollData.depth)) {
105
+ return false;
106
+ }
107
+ return true;
108
+ }
109
+ hasReachedSessionLimit() {
110
+ const currentCount = this.get('scrollEventCount') ?? 0;
111
+ return currentCount >= this.maxEventsPerSession;
112
+ }
113
+ hasElapsedMinimumInterval(container, timestamp) {
114
+ if (container.lastEventTime === 0) {
115
+ return true;
116
+ }
117
+ return timestamp - container.lastEventTime >= this.minIntervalMs;
118
+ }
119
+ hasSignificantDepthChange(container, newDepth) {
120
+ return Math.abs(newDepth - container.lastDepth) >= this.minDepthChange;
121
+ }
122
+ logLimitOnce() {
123
+ if (this.limitWarningLogged) {
124
+ return;
125
+ }
126
+ this.limitWarningLogged = true;
127
+ debugLog.warn('ScrollHandler', 'Max scroll events per session reached', {
128
+ limit: this.maxEventsPerSession,
129
+ });
130
+ }
131
+ applyConfigOverrides() {
132
+ this.minDepthChange = MIN_SCROLL_DEPTH_CHANGE;
133
+ this.minIntervalMs = SCROLL_MIN_EVENT_INTERVAL_MS;
134
+ this.maxEventsPerSession = MAX_SCROLL_EVENTS_PER_SESSION;
135
+ }
136
+ isWindowScrollable() {
137
+ return document.documentElement.scrollHeight > window.innerHeight;
138
+ }
139
+ clearContainerTimer(container) {
140
+ if (container.debounceTimer !== null) {
141
+ clearTimeout(container.debounceTimer);
142
+ container.debounceTimer = null;
143
+ }
144
+ }
145
+ getScrollDirection(current, previous) {
146
+ return current > previous ? ScrollDirection.DOWN : ScrollDirection.UP;
147
+ }
148
+ calculateScrollDepth(scrollTop, scrollHeight, viewportHeight) {
149
+ if (scrollHeight <= viewportHeight) {
150
+ return 0;
151
+ }
152
+ const maxScrollTop = scrollHeight - viewportHeight;
153
+ return Math.min(100, Math.max(0, Math.floor((scrollTop / maxScrollTop) * 100)));
154
+ }
107
155
  calculateScrollData(container) {
108
156
  const { element, lastScrollPos } = container;
109
157
  const scrollTop = this.getScrollTop(element);
110
- const viewportHeight = this.getViewportHeight(element);
111
- const scrollHeight = this.getScrollHeight(element);
112
- const viewportWidth = this.getViewportWidth(element);
113
- const scrollWidth = this.getScrollWidth(element);
114
- // Dynamic validation: check if element is scrollable at runtime
115
- if (element instanceof HTMLElement) {
116
- // Check if element has scrollable overflow style (can change dynamically)
117
- if (!this.hasScrollableOverflow(element)) {
118
- return null;
119
- }
120
- // Check if content exceeds viewport (vertical OR horizontal)
121
- const hasVerticalScroll = scrollHeight > viewportHeight;
122
- const hasHorizontalScroll = scrollWidth > viewportWidth;
123
- if (!hasVerticalScroll && !hasHorizontalScroll) {
124
- return null;
125
- }
126
- }
127
- // For Window: check if content is scrollable (vertical or horizontal)
128
- if (element instanceof Window) {
129
- const hasVerticalScroll = scrollHeight > viewportHeight;
130
- const hasHorizontalScroll = scrollWidth > viewportWidth;
131
- if (!hasVerticalScroll && !hasHorizontalScroll) {
132
- return null;
133
- }
134
- }
135
- const direction = scrollTop > lastScrollPos ? ScrollDirection.DOWN : ScrollDirection.UP;
136
- const depth = scrollHeight > viewportHeight
137
- ? Math.min(100, Math.max(0, Math.floor((scrollTop / (scrollHeight - viewportHeight)) * 100)))
138
- : 0;
139
- // Only update if scroll position changed significantly
158
+ // Early return: check significant movement first (cheapest check)
140
159
  const positionDelta = Math.abs(scrollTop - lastScrollPos);
141
160
  if (positionDelta < SIGNIFICANT_SCROLL_DELTA) {
142
161
  return null;
143
162
  }
163
+ // Early return: check if window is scrollable
164
+ if (element === window && !this.isWindowScrollable()) {
165
+ return null;
166
+ }
167
+ const viewportHeight = this.getViewportHeight(element);
168
+ const scrollHeight = this.getScrollHeight(element);
169
+ const direction = this.getScrollDirection(scrollTop, lastScrollPos);
170
+ const depth = this.calculateScrollDepth(scrollTop, scrollHeight, viewportHeight);
144
171
  container.lastScrollPos = scrollTop;
145
172
  return { depth, direction };
146
173
  }
@@ -153,20 +180,17 @@ export class ScrollHandler extends StateManager {
153
180
  getScrollHeight(element) {
154
181
  return element instanceof Window ? document.documentElement.scrollHeight : element.scrollHeight;
155
182
  }
156
- getViewportWidth(element) {
157
- return element instanceof Window ? window.innerWidth : element.clientWidth;
158
- }
159
- getScrollWidth(element) {
160
- return element instanceof Window ? document.documentElement.scrollWidth : element.scrollWidth;
161
- }
162
- hasScrollableOverflow(element) {
183
+ isElementScrollable(element) {
163
184
  const style = getComputedStyle(element);
164
- return (style.overflowY === 'auto' ||
185
+ const hasScrollableOverflow = style.overflowY === 'auto' ||
165
186
  style.overflowY === 'scroll' ||
166
187
  style.overflowX === 'auto' ||
167
188
  style.overflowX === 'scroll' ||
168
189
  style.overflow === 'auto' ||
169
- style.overflow === 'scroll');
190
+ style.overflow === 'scroll';
191
+ // Element must have scrollable overflow AND content that exceeds the container
192
+ const hasOverflowContent = element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth;
193
+ return hasScrollableOverflow && hasOverflowContent;
170
194
  }
171
195
  safeQuerySelector(selector) {
172
196
  try {
@@ -181,94 +205,4 @@ export class ScrollHandler extends StateManager {
181
205
  return null;
182
206
  }
183
207
  }
184
- setupPendingSelectors(selectors) {
185
- debugLog.debug('ScrollHandler', 'Setting up pending selectors with retry logic', {
186
- selectors,
187
- maxRetries: MAX_RETRY_ATTEMPTS,
188
- });
189
- for (const selector of selectors) {
190
- this.pendingSelectors.push({ selector, retryCount: 0 });
191
- }
192
- this.startMutationObserver();
193
- this.retryPendingSelectors();
194
- }
195
- startMutationObserver() {
196
- if (this.mutationObserver)
197
- return;
198
- this.mutationObserver = new MutationObserver(() => {
199
- this.checkPendingSelectors();
200
- });
201
- this.mutationObserver.observe(document.body, {
202
- childList: true,
203
- subtree: true,
204
- });
205
- debugLog.debug('ScrollHandler', 'MutationObserver started for pending selectors');
206
- }
207
- checkPendingSelectors() {
208
- const remaining = [];
209
- for (const pending of this.pendingSelectors) {
210
- const element = this.safeQuerySelector(pending.selector);
211
- if (element instanceof HTMLElement) {
212
- debugLog.debug('ScrollHandler', 'Found pending selector', { selector: pending.selector });
213
- this.setupScrollContainer(element);
214
- }
215
- else {
216
- remaining.push(pending);
217
- }
218
- }
219
- this.pendingSelectors.length = 0;
220
- this.pendingSelectors.push(...remaining);
221
- if (this.pendingSelectors.length === 0 && this.mutationObserver) {
222
- this.mutationObserver.disconnect();
223
- this.mutationObserver = null;
224
- debugLog.debug('ScrollHandler', 'All pending selectors resolved, MutationObserver stopped');
225
- }
226
- }
227
- retryPendingSelectors() {
228
- setTimeout(() => {
229
- if (this.pendingSelectors.length === 0)
230
- return;
231
- const remaining = [];
232
- for (const pending of this.pendingSelectors) {
233
- pending.retryCount++;
234
- const element = this.safeQuerySelector(pending.selector);
235
- if (element instanceof HTMLElement) {
236
- debugLog.debug('ScrollHandler', 'Retry found pending selector', {
237
- selector: pending.selector,
238
- retryCount: pending.retryCount,
239
- });
240
- this.setupScrollContainer(element);
241
- }
242
- else if (pending.retryCount < MAX_RETRY_ATTEMPTS) {
243
- remaining.push(pending);
244
- }
245
- else {
246
- debugLog.clientWarn('ScrollHandler', 'Selector not found after max retries', {
247
- selector: pending.selector,
248
- maxRetries: MAX_RETRY_ATTEMPTS,
249
- });
250
- }
251
- }
252
- this.pendingSelectors.length = 0;
253
- this.pendingSelectors.push(...remaining);
254
- if (this.pendingSelectors.length > 0) {
255
- this.retryPendingSelectors();
256
- }
257
- else {
258
- // All retries complete
259
- if (this.mutationObserver) {
260
- this.mutationObserver.disconnect();
261
- this.mutationObserver = null;
262
- }
263
- // Apply window fallback if needed and no containers were set up
264
- if (this.windowFallbackNeeded && this.containers.length === 0) {
265
- debugLog.debug('ScrollHandler', 'No scroll containers found, using window fallback');
266
- this.setupScrollContainer(window);
267
- }
268
- debugLog.debug('ScrollHandler', 'All pending selectors resolved or timed out', {
269
- containersCount: this.containers.length,
270
- });
271
- }
272
- }, RETRY_DELAY_MS);
273
- }
274
208
  }
@@ -4,26 +4,12 @@ import { StorageManager } from '../managers/storage.manager';
4
4
  export declare class SessionHandler extends StateManager {
5
5
  private readonly eventManager;
6
6
  private readonly storageManager;
7
- private readonly sessionStorageKey;
8
7
  private sessionManager;
9
- private recoveryManager;
10
- private _crossTabSessionManager;
11
- private heartbeatInterval;
12
- private _isInitializingCrossTab;
13
- private get crossTabSessionManager();
14
- private shouldUseCrossTabs;
8
+ private destroyed;
15
9
  constructor(storageManager: StorageManager, eventManager: EventManager);
16
- startTracking(): void;
17
- stopTracking(): void;
18
- private initializeSessionRecoveryManager;
19
- private initializeCrossTabSessionManager;
20
- private createOrJoinSession;
21
- private forceCleanupSession;
22
- private trackSession;
23
- private startInitialSession;
24
- private checkOrphanedSessions;
25
- private persistSession;
26
- private clearPersistedSession;
27
- private startHeartbeat;
28
- private stopHeartbeat;
10
+ startTracking(): Promise<void>;
11
+ private isActive;
12
+ private cleanupSessionManager;
13
+ stopTracking(): Promise<void>;
14
+ destroy(): void;
29
15
  }