@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.
- package/README.md +58 -24
- package/dist/browser/tracelog.js +1928 -3297
- package/dist/cjs/api.d.ts +33 -19
- package/dist/cjs/api.js +111 -156
- package/dist/cjs/app.constants.d.ts +74 -1
- package/dist/cjs/app.constants.js +77 -3
- package/dist/cjs/app.d.ts +29 -44
- package/dist/cjs/app.js +114 -212
- package/dist/cjs/app.types.d.ts +2 -7
- package/dist/cjs/app.types.js +10 -21
- package/dist/cjs/constants/api.constants.js +11 -5
- package/dist/cjs/constants/config.constants.d.ts +75 -0
- package/dist/cjs/constants/config.constants.js +178 -0
- package/dist/cjs/constants/error.constants.d.ts +29 -0
- package/dist/cjs/constants/error.constants.js +50 -0
- package/dist/cjs/constants/index.d.ts +3 -6
- package/dist/cjs/constants/index.js +3 -6
- package/dist/cjs/constants/performance.constants.d.ts +28 -0
- package/dist/cjs/constants/performance.constants.js +43 -0
- package/dist/cjs/handlers/click.handler.d.ts +1 -0
- package/dist/cjs/handlers/click.handler.js +30 -49
- package/dist/cjs/handlers/error.handler.d.ts +11 -6
- package/dist/cjs/handlers/error.handler.js +91 -51
- package/dist/cjs/handlers/page-view.handler.js +38 -29
- package/dist/cjs/handlers/performance.handler.d.ts +3 -0
- package/dist/cjs/handlers/performance.handler.js +76 -37
- package/dist/cjs/handlers/scroll.handler.d.ts +16 -10
- package/dist/cjs/handlers/scroll.handler.js +119 -185
- package/dist/cjs/handlers/session.handler.d.ts +6 -20
- package/dist/cjs/handlers/session.handler.js +38 -326
- package/dist/cjs/integrations/google-analytics.integration.d.ts +0 -1
- package/dist/cjs/integrations/google-analytics.integration.js +27 -98
- package/dist/cjs/listeners/input-listener-managers.d.ts +18 -9
- package/dist/cjs/listeners/input-listener-managers.js +24 -33
- package/dist/cjs/listeners/touch-listener-manager.d.ts +1 -3
- package/dist/cjs/listeners/touch-listener-manager.js +1 -23
- package/dist/cjs/listeners/visibility-listener-manager.d.ts +1 -4
- package/dist/cjs/listeners/visibility-listener-manager.js +6 -42
- package/dist/cjs/managers/api.manager.d.ts +13 -3
- package/dist/cjs/managers/api.manager.js +35 -5
- package/dist/cjs/managers/config.manager.d.ts +53 -3
- package/dist/cjs/managers/config.manager.js +131 -62
- package/dist/cjs/managers/event.manager.d.ts +57 -36
- package/dist/cjs/managers/event.manager.js +266 -417
- package/dist/cjs/managers/sender.manager.d.ts +40 -22
- package/dist/cjs/managers/sender.manager.js +200 -198
- package/dist/cjs/managers/session.manager.d.ts +80 -66
- package/dist/cjs/managers/session.manager.js +267 -522
- package/dist/cjs/managers/state.manager.d.ts +33 -0
- package/dist/cjs/managers/state.manager.js +79 -6
- package/dist/cjs/managers/storage.manager.d.ts +26 -2
- package/dist/cjs/managers/storage.manager.js +67 -34
- package/dist/cjs/managers/tags.manager.d.ts +31 -7
- package/dist/cjs/managers/tags.manager.js +123 -241
- package/dist/cjs/managers/user.manager.d.ts +14 -5
- package/dist/cjs/managers/user.manager.js +17 -9
- package/dist/cjs/public-api.d.ts +10 -1
- package/dist/cjs/public-api.js +18 -24
- package/dist/cjs/test-bridge.d.ts +48 -0
- package/dist/cjs/test-bridge.js +110 -0
- package/dist/cjs/types/api.types.d.ts +21 -6
- package/dist/cjs/types/api.types.js +21 -6
- package/dist/cjs/types/config.types.d.ts +22 -84
- package/dist/cjs/types/emitter.types.d.ts +11 -0
- package/dist/cjs/types/emitter.types.js +8 -0
- package/dist/cjs/types/event.types.d.ts +8 -11
- package/dist/cjs/types/index.d.ts +3 -1
- package/dist/cjs/types/index.js +3 -1
- package/dist/cjs/types/queue.types.d.ts +1 -0
- package/dist/cjs/types/session.types.d.ts +0 -64
- package/dist/cjs/types/state.types.d.ts +1 -0
- package/dist/cjs/types/test-bridge.types.d.ts +38 -0
- package/dist/cjs/types/validation-error.types.d.ts +7 -0
- package/dist/cjs/types/validation-error.types.js +11 -1
- package/dist/cjs/types/window.types.d.ts +1 -8
- package/dist/cjs/utils/data/uuid.utils.d.ts +1 -1
- package/dist/cjs/utils/data/uuid.utils.js +7 -5
- package/dist/cjs/utils/emitter.utils.d.ts +8 -0
- package/dist/cjs/utils/emitter.utils.js +33 -0
- package/dist/cjs/utils/index.d.ts +1 -0
- package/dist/cjs/utils/index.js +1 -0
- package/dist/cjs/utils/logging/debug-logger.utils.d.ts +10 -51
- package/dist/cjs/utils/logging/debug-logger.utils.js +36 -127
- package/dist/cjs/utils/network/fetch-with-timeout.utils.d.ts +4 -0
- package/dist/cjs/utils/network/fetch-with-timeout.utils.js +25 -0
- package/dist/cjs/utils/network/index.d.ts +1 -0
- package/dist/cjs/utils/network/index.js +1 -0
- package/dist/cjs/utils/network/url.utils.js +2 -42
- package/dist/cjs/utils/security/sanitize.utils.d.ts +1 -8
- package/dist/cjs/utils/security/sanitize.utils.js +7 -41
- package/dist/cjs/utils/validations/config-validations.utils.d.ts +7 -0
- package/dist/cjs/utils/validations/config-validations.utils.js +77 -22
- package/dist/esm/api.d.ts +33 -19
- package/dist/esm/api.js +105 -118
- package/dist/esm/app.constants.d.ts +74 -1
- package/dist/esm/app.constants.js +76 -1
- package/dist/esm/app.d.ts +29 -44
- package/dist/esm/app.js +115 -213
- package/dist/esm/app.types.d.ts +2 -7
- package/dist/esm/app.types.js +1 -7
- package/dist/esm/constants/api.constants.js +10 -4
- package/dist/esm/constants/config.constants.d.ts +75 -0
- package/dist/esm/constants/config.constants.js +174 -0
- package/dist/esm/constants/error.constants.d.ts +29 -0
- package/dist/esm/constants/error.constants.js +47 -0
- package/dist/esm/constants/index.d.ts +3 -6
- package/dist/esm/constants/index.js +3 -6
- package/dist/esm/constants/performance.constants.d.ts +28 -0
- package/dist/esm/constants/performance.constants.js +40 -0
- package/dist/esm/handlers/click.handler.d.ts +1 -0
- package/dist/esm/handlers/click.handler.js +30 -49
- package/dist/esm/handlers/error.handler.d.ts +11 -6
- package/dist/esm/handlers/error.handler.js +91 -51
- package/dist/esm/handlers/page-view.handler.js +38 -29
- package/dist/esm/handlers/performance.handler.d.ts +3 -0
- package/dist/esm/handlers/performance.handler.js +71 -32
- package/dist/esm/handlers/scroll.handler.d.ts +16 -10
- package/dist/esm/handlers/scroll.handler.js +120 -186
- package/dist/esm/handlers/session.handler.d.ts +6 -20
- package/dist/esm/handlers/session.handler.js +38 -326
- package/dist/esm/integrations/google-analytics.integration.d.ts +0 -1
- package/dist/esm/integrations/google-analytics.integration.js +27 -98
- package/dist/esm/listeners/input-listener-managers.d.ts +18 -9
- package/dist/esm/listeners/input-listener-managers.js +23 -32
- package/dist/esm/listeners/touch-listener-manager.d.ts +1 -3
- package/dist/esm/listeners/touch-listener-manager.js +1 -23
- package/dist/esm/listeners/visibility-listener-manager.d.ts +1 -4
- package/dist/esm/listeners/visibility-listener-manager.js +6 -42
- package/dist/esm/managers/api.manager.d.ts +13 -3
- package/dist/esm/managers/api.manager.js +34 -3
- package/dist/esm/managers/config.manager.d.ts +53 -3
- package/dist/esm/managers/config.manager.js +133 -64
- package/dist/esm/managers/event.manager.d.ts +57 -36
- package/dist/esm/managers/event.manager.js +268 -419
- package/dist/esm/managers/sender.manager.d.ts +40 -22
- package/dist/esm/managers/sender.manager.js +201 -199
- package/dist/esm/managers/session.manager.d.ts +80 -66
- package/dist/esm/managers/session.manager.js +269 -524
- package/dist/esm/managers/state.manager.d.ts +33 -0
- package/dist/esm/managers/state.manager.js +78 -6
- package/dist/esm/managers/storage.manager.d.ts +26 -2
- package/dist/esm/managers/storage.manager.js +66 -33
- package/dist/esm/managers/tags.manager.d.ts +31 -7
- package/dist/esm/managers/tags.manager.js +124 -242
- package/dist/esm/managers/user.manager.d.ts +14 -5
- package/dist/esm/managers/user.manager.js +17 -9
- package/dist/esm/public-api.d.ts +10 -1
- package/dist/esm/public-api.js +14 -1
- package/dist/esm/test-bridge.d.ts +48 -0
- package/dist/esm/test-bridge.js +106 -0
- package/dist/esm/types/api.types.d.ts +21 -6
- package/dist/esm/types/api.types.js +21 -6
- package/dist/esm/types/config.types.d.ts +22 -84
- package/dist/esm/types/emitter.types.d.ts +11 -0
- package/dist/esm/types/emitter.types.js +5 -0
- package/dist/esm/types/event.types.d.ts +8 -11
- package/dist/esm/types/index.d.ts +3 -1
- package/dist/esm/types/index.js +3 -1
- package/dist/esm/types/queue.types.d.ts +1 -0
- package/dist/esm/types/session.types.d.ts +0 -64
- package/dist/esm/types/state.types.d.ts +1 -0
- package/dist/esm/types/test-bridge.types.d.ts +38 -0
- package/dist/esm/types/validation-error.types.d.ts +7 -0
- package/dist/esm/types/validation-error.types.js +9 -0
- package/dist/esm/types/window.types.d.ts +1 -8
- package/dist/esm/utils/data/uuid.utils.d.ts +1 -1
- package/dist/esm/utils/data/uuid.utils.js +7 -5
- package/dist/esm/utils/emitter.utils.d.ts +8 -0
- package/dist/esm/utils/emitter.utils.js +29 -0
- package/dist/esm/utils/index.d.ts +1 -0
- package/dist/esm/utils/index.js +1 -0
- package/dist/esm/utils/logging/debug-logger.utils.d.ts +10 -51
- package/dist/esm/utils/logging/debug-logger.utils.js +36 -127
- package/dist/esm/utils/network/fetch-with-timeout.utils.d.ts +4 -0
- package/dist/esm/utils/network/fetch-with-timeout.utils.js +22 -0
- package/dist/esm/utils/network/index.d.ts +1 -0
- package/dist/esm/utils/network/index.js +1 -0
- package/dist/esm/utils/network/url.utils.js +2 -42
- package/dist/esm/utils/security/sanitize.utils.d.ts +1 -8
- package/dist/esm/utils/security/sanitize.utils.js +6 -39
- package/dist/esm/utils/validations/config-validations.utils.d.ts +7 -0
- package/dist/esm/utils/validations/config-validations.utils.js +76 -22
- package/package.json +23 -16
- package/dist/browser/web-vitals-CCnqwnC8.mjs +0 -198
- package/dist/cjs/constants/browser.constants.d.ts +0 -3
- package/dist/cjs/constants/browser.constants.js +0 -41
- package/dist/cjs/constants/initialization.constants.d.ts +0 -40
- package/dist/cjs/constants/initialization.constants.js +0 -48
- package/dist/cjs/constants/limits.constants.d.ts +0 -27
- package/dist/cjs/constants/limits.constants.js +0 -43
- package/dist/cjs/constants/security.constants.d.ts +0 -1
- package/dist/cjs/constants/security.constants.js +0 -12
- package/dist/cjs/constants/timing.constants.d.ts +0 -22
- package/dist/cjs/constants/timing.constants.js +0 -34
- package/dist/cjs/constants/validation.constants.d.ts +0 -13
- package/dist/cjs/constants/validation.constants.js +0 -31
- package/dist/cjs/handlers/network.handler.d.ts +0 -16
- package/dist/cjs/handlers/network.handler.js +0 -136
- package/dist/cjs/managers/cross-tab-session.manager.d.ts +0 -170
- package/dist/cjs/managers/cross-tab-session.manager.js +0 -730
- package/dist/cjs/managers/sampling.manager.d.ts +0 -8
- package/dist/cjs/managers/sampling.manager.js +0 -53
- package/dist/cjs/managers/session-recovery.manager.d.ts +0 -65
- package/dist/cjs/managers/session-recovery.manager.js +0 -237
- package/dist/cjs/types/web-vitals.types.d.ts +0 -6
- package/dist/esm/constants/browser.constants.d.ts +0 -3
- package/dist/esm/constants/browser.constants.js +0 -38
- package/dist/esm/constants/initialization.constants.d.ts +0 -40
- package/dist/esm/constants/initialization.constants.js +0 -45
- package/dist/esm/constants/limits.constants.d.ts +0 -27
- package/dist/esm/constants/limits.constants.js +0 -40
- package/dist/esm/constants/security.constants.d.ts +0 -1
- package/dist/esm/constants/security.constants.js +0 -9
- package/dist/esm/constants/timing.constants.d.ts +0 -22
- package/dist/esm/constants/timing.constants.js +0 -31
- package/dist/esm/constants/validation.constants.d.ts +0 -13
- package/dist/esm/constants/validation.constants.js +0 -28
- package/dist/esm/handlers/network.handler.d.ts +0 -16
- package/dist/esm/handlers/network.handler.js +0 -132
- package/dist/esm/managers/cross-tab-session.manager.d.ts +0 -170
- package/dist/esm/managers/cross-tab-session.manager.js +0 -726
- package/dist/esm/managers/sampling.manager.d.ts +0 -8
- package/dist/esm/managers/sampling.manager.js +0 -49
- package/dist/esm/managers/session-recovery.manager.d.ts +0 -65
- package/dist/esm/managers/session-recovery.manager.js +0 -233
- package/dist/esm/types/web-vitals.types.d.ts +0 -6
- /package/dist/cjs/types/{web-vitals.types.js → test-bridge.types.js} +0 -0
- /package/dist/esm/types/{web-vitals.types.js → test-bridge.types.js} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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.
|
|
10
|
-
this.
|
|
11
|
-
this.
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
63
|
-
|
|
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
|
|
70
|
-
if (
|
|
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
|
-
|
|
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
|
-
|
|
91
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
18
|
-
private
|
|
19
|
-
|
|
20
|
-
|
|
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
|
}
|