@tracelog/lib 0.0.7 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -23,3 +23,5 @@ export declare const SYNC_XHR_TIMEOUT_MS = 2000;
23
23
  export declare const MAX_FINGERPRINTS = 1000;
24
24
  export declare const FINGERPRINT_CLEANUP_MULTIPLIER = 10;
25
25
  export declare const CLICK_COORDINATE_PRECISION = 10;
26
+ export declare const MAX_RETRY_ATTEMPTS = 3;
27
+ export declare const RETRY_DELAY_MS = 1000;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CLICK_COORDINATE_PRECISION = exports.FINGERPRINT_CLEANUP_MULTIPLIER = exports.MAX_FINGERPRINTS = exports.SYNC_XHR_TIMEOUT_MS = exports.WEB_VITALS_LONG_TASK_SAMPLING = exports.WEB_VITALS_SAMPLING = exports.PRECISION_FOUR_DECIMALS = exports.PRECISION_TWO_DECIMALS = exports.MAX_OBJECT_DEPTH = exports.MAX_ARRAY_LENGTH = exports.MAX_STRING_LENGTH = exports.MAX_TEXT_LENGTH = exports.MAX_CUSTOM_EVENT_ARRAY_SIZE = exports.MAX_CUSTOM_EVENT_KEYS = exports.MAX_CUSTOM_EVENT_STRING_SIZE = exports.MAX_CUSTOM_EVENT_NAME_LENGTH = exports.MAX_SESSION_TIMEOUT_MS = exports.MIN_SESSION_TIMEOUT_MS = exports.MAX_EVENTS_QUEUE_LENGTH = exports.BATCH_SIZE_THRESHOLD = exports.MAX_SAMPLING_RATE = exports.MIN_SAMPLING_RATE = exports.DEFAULT_SAMPLING_RATE = exports.SIGNIFICANT_SCROLL_DELTA = exports.DEFAULT_MOTION_THRESHOLD = void 0;
3
+ exports.RETRY_DELAY_MS = exports.MAX_RETRY_ATTEMPTS = exports.CLICK_COORDINATE_PRECISION = exports.FINGERPRINT_CLEANUP_MULTIPLIER = exports.MAX_FINGERPRINTS = exports.SYNC_XHR_TIMEOUT_MS = exports.WEB_VITALS_LONG_TASK_SAMPLING = exports.WEB_VITALS_SAMPLING = exports.PRECISION_FOUR_DECIMALS = exports.PRECISION_TWO_DECIMALS = exports.MAX_OBJECT_DEPTH = exports.MAX_ARRAY_LENGTH = exports.MAX_STRING_LENGTH = exports.MAX_TEXT_LENGTH = exports.MAX_CUSTOM_EVENT_ARRAY_SIZE = exports.MAX_CUSTOM_EVENT_KEYS = exports.MAX_CUSTOM_EVENT_STRING_SIZE = exports.MAX_CUSTOM_EVENT_NAME_LENGTH = exports.MAX_SESSION_TIMEOUT_MS = exports.MIN_SESSION_TIMEOUT_MS = exports.MAX_EVENTS_QUEUE_LENGTH = exports.BATCH_SIZE_THRESHOLD = exports.MAX_SAMPLING_RATE = exports.MIN_SAMPLING_RATE = exports.DEFAULT_SAMPLING_RATE = exports.SIGNIFICANT_SCROLL_DELTA = exports.DEFAULT_MOTION_THRESHOLD = void 0;
4
4
  // Motion and interaction thresholds
5
5
  exports.DEFAULT_MOTION_THRESHOLD = 2;
6
6
  exports.SIGNIFICANT_SCROLL_DELTA = 10;
@@ -38,3 +38,6 @@ exports.MAX_FINGERPRINTS = 1000; // Maximum fingerprints stored before cleanup
38
38
  exports.FINGERPRINT_CLEANUP_MULTIPLIER = 10; // Cleanup fingerprints older than 10x threshold
39
39
  // Click coordinate precision
40
40
  exports.CLICK_COORDINATE_PRECISION = 10; // Round click coordinates to nearest 10px
41
+ // Retry limits
42
+ exports.MAX_RETRY_ATTEMPTS = 3;
43
+ exports.RETRY_DELAY_MS = 1000;
@@ -3,6 +3,9 @@ import { StateManager } from '../managers/state.manager';
3
3
  export declare class ScrollHandler extends StateManager {
4
4
  private readonly eventManager;
5
5
  private readonly containers;
6
+ private readonly pendingSelectors;
7
+ private mutationObserver;
8
+ private windowFallbackNeeded;
6
9
  constructor(eventManager: EventManager);
7
10
  startTracking(): void;
8
11
  stopTracking(): void;
@@ -11,6 +14,12 @@ export declare class ScrollHandler extends StateManager {
11
14
  private getScrollTop;
12
15
  private getViewportHeight;
13
16
  private getScrollHeight;
14
- private isElementScrollable;
17
+ private getViewportWidth;
18
+ private getScrollWidth;
19
+ private hasScrollableOverflow;
15
20
  private safeQuerySelector;
21
+ private setupPendingSelectors;
22
+ private startMutationObserver;
23
+ private checkPendingSelectors;
24
+ private retryPendingSelectors;
16
25
  }
@@ -9,21 +9,44 @@ class ScrollHandler extends state_manager_1.StateManager {
9
9
  constructor(eventManager) {
10
10
  super();
11
11
  this.containers = [];
12
+ this.pendingSelectors = [];
13
+ this.mutationObserver = null;
14
+ this.windowFallbackNeeded = false;
12
15
  this.eventManager = eventManager;
13
16
  }
14
17
  startTracking() {
15
18
  const raw = this.get('config').scrollContainerSelectors;
16
19
  const selectors = Array.isArray(raw) ? raw : typeof raw === 'string' ? [raw] : [];
17
20
  logging_1.debugLog.debug('ScrollHandler', 'Starting scroll tracking', { selectorsCount: selectors.length });
18
- const elements = selectors
19
- .map((sel) => this.safeQuerySelector(sel))
20
- .filter((element) => element instanceof HTMLElement);
21
- if (elements.length === 0) {
22
- elements.push(window);
21
+ // No custom selectors: track window immediately
22
+ if (selectors.length === 0) {
23
+ this.setupScrollContainer(window);
24
+ return;
23
25
  }
24
- for (const element of elements) {
26
+ const foundElements = [];
27
+ const notFoundSelectors = [];
28
+ for (const selector of selectors) {
29
+ const element = this.safeQuerySelector(selector);
30
+ if (element instanceof HTMLElement) {
31
+ foundElements.push(element);
32
+ }
33
+ else if (element === null) {
34
+ notFoundSelectors.push(selector);
35
+ }
36
+ }
37
+ // Setup found elements
38
+ for (const element of foundElements) {
25
39
  this.setupScrollContainer(element);
26
40
  }
41
+ // If we have pending selectors, set up retry logic
42
+ if (notFoundSelectors.length > 0) {
43
+ this.windowFallbackNeeded = true;
44
+ this.setupPendingSelectors(notFoundSelectors);
45
+ }
46
+ else if (this.containers.length === 0) {
47
+ // No elements found and none pending: use window fallback immediately
48
+ this.setupScrollContainer(window);
49
+ }
27
50
  }
28
51
  stopTracking() {
29
52
  logging_1.debugLog.debug('ScrollHandler', 'Stopping scroll tracking', { containersCount: this.containers.length });
@@ -39,10 +62,15 @@ class ScrollHandler extends state_manager_1.StateManager {
39
62
  }
40
63
  }
41
64
  this.containers.length = 0;
65
+ if (this.mutationObserver) {
66
+ this.mutationObserver.disconnect();
67
+ this.mutationObserver = null;
68
+ }
69
+ this.pendingSelectors.length = 0;
42
70
  }
43
71
  setupScrollContainer(element) {
44
- // Skip setup for non-scrollable elements
45
- if (element !== window && !this.isElementScrollable(element)) {
72
+ // Skip if already tracking this element
73
+ if (this.containers.some((c) => c.element === element)) {
46
74
  return;
47
75
  }
48
76
  const container = {
@@ -84,9 +112,28 @@ class ScrollHandler extends state_manager_1.StateManager {
84
112
  const scrollTop = this.getScrollTop(element);
85
113
  const viewportHeight = this.getViewportHeight(element);
86
114
  const scrollHeight = this.getScrollHeight(element);
87
- // Check if the page is actually scrollable at runtime
88
- if (element === window && scrollHeight <= viewportHeight) {
89
- return null;
115
+ const viewportWidth = this.getViewportWidth(element);
116
+ const scrollWidth = this.getScrollWidth(element);
117
+ // Dynamic validation: check if element is scrollable at runtime
118
+ if (element instanceof HTMLElement) {
119
+ // Check if element has scrollable overflow style (can change dynamically)
120
+ if (!this.hasScrollableOverflow(element)) {
121
+ return null;
122
+ }
123
+ // Check if content exceeds viewport (vertical OR horizontal)
124
+ const hasVerticalScroll = scrollHeight > viewportHeight;
125
+ const hasHorizontalScroll = scrollWidth > viewportWidth;
126
+ if (!hasVerticalScroll && !hasHorizontalScroll) {
127
+ return null;
128
+ }
129
+ }
130
+ // For Window: check if content is scrollable (vertical or horizontal)
131
+ if (element instanceof Window) {
132
+ const hasVerticalScroll = scrollHeight > viewportHeight;
133
+ const hasHorizontalScroll = scrollWidth > viewportWidth;
134
+ if (!hasVerticalScroll && !hasHorizontalScroll) {
135
+ return null;
136
+ }
90
137
  }
91
138
  const direction = scrollTop > lastScrollPos ? types_1.ScrollDirection.DOWN : types_1.ScrollDirection.UP;
92
139
  const depth = scrollHeight > viewportHeight
@@ -109,17 +156,20 @@ class ScrollHandler extends state_manager_1.StateManager {
109
156
  getScrollHeight(element) {
110
157
  return element instanceof Window ? document.documentElement.scrollHeight : element.scrollHeight;
111
158
  }
112
- isElementScrollable(element) {
159
+ getViewportWidth(element) {
160
+ return element instanceof Window ? window.innerWidth : element.clientWidth;
161
+ }
162
+ getScrollWidth(element) {
163
+ return element instanceof Window ? document.documentElement.scrollWidth : element.scrollWidth;
164
+ }
165
+ hasScrollableOverflow(element) {
113
166
  const style = getComputedStyle(element);
114
- const hasScrollableOverflow = style.overflowY === 'auto' ||
167
+ return (style.overflowY === 'auto' ||
115
168
  style.overflowY === 'scroll' ||
116
169
  style.overflowX === 'auto' ||
117
170
  style.overflowX === 'scroll' ||
118
171
  style.overflow === 'auto' ||
119
- style.overflow === 'scroll';
120
- // Element must have scrollable overflow AND content that exceeds the container
121
- const hasOverflowContent = element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth;
122
- return hasScrollableOverflow && hasOverflowContent;
172
+ style.overflow === 'scroll');
123
173
  }
124
174
  safeQuerySelector(selector) {
125
175
  try {
@@ -134,5 +184,95 @@ class ScrollHandler extends state_manager_1.StateManager {
134
184
  return null;
135
185
  }
136
186
  }
187
+ setupPendingSelectors(selectors) {
188
+ logging_1.debugLog.debug('ScrollHandler', 'Setting up pending selectors with retry logic', {
189
+ selectors,
190
+ maxRetries: constants_1.MAX_RETRY_ATTEMPTS,
191
+ });
192
+ for (const selector of selectors) {
193
+ this.pendingSelectors.push({ selector, retryCount: 0 });
194
+ }
195
+ this.startMutationObserver();
196
+ this.retryPendingSelectors();
197
+ }
198
+ startMutationObserver() {
199
+ if (this.mutationObserver)
200
+ return;
201
+ this.mutationObserver = new MutationObserver(() => {
202
+ this.checkPendingSelectors();
203
+ });
204
+ this.mutationObserver.observe(document.body, {
205
+ childList: true,
206
+ subtree: true,
207
+ });
208
+ logging_1.debugLog.debug('ScrollHandler', 'MutationObserver started for pending selectors');
209
+ }
210
+ checkPendingSelectors() {
211
+ const remaining = [];
212
+ for (const pending of this.pendingSelectors) {
213
+ const element = this.safeQuerySelector(pending.selector);
214
+ if (element instanceof HTMLElement) {
215
+ logging_1.debugLog.debug('ScrollHandler', 'Found pending selector', { selector: pending.selector });
216
+ this.setupScrollContainer(element);
217
+ }
218
+ else {
219
+ remaining.push(pending);
220
+ }
221
+ }
222
+ this.pendingSelectors.length = 0;
223
+ this.pendingSelectors.push(...remaining);
224
+ if (this.pendingSelectors.length === 0 && this.mutationObserver) {
225
+ this.mutationObserver.disconnect();
226
+ this.mutationObserver = null;
227
+ logging_1.debugLog.debug('ScrollHandler', 'All pending selectors resolved, MutationObserver stopped');
228
+ }
229
+ }
230
+ retryPendingSelectors() {
231
+ setTimeout(() => {
232
+ if (this.pendingSelectors.length === 0)
233
+ return;
234
+ const remaining = [];
235
+ for (const pending of this.pendingSelectors) {
236
+ pending.retryCount++;
237
+ const element = this.safeQuerySelector(pending.selector);
238
+ if (element instanceof HTMLElement) {
239
+ logging_1.debugLog.debug('ScrollHandler', 'Retry found pending selector', {
240
+ selector: pending.selector,
241
+ retryCount: pending.retryCount,
242
+ });
243
+ this.setupScrollContainer(element);
244
+ }
245
+ else if (pending.retryCount < constants_1.MAX_RETRY_ATTEMPTS) {
246
+ remaining.push(pending);
247
+ }
248
+ else {
249
+ logging_1.debugLog.clientWarn('ScrollHandler', 'Selector not found after max retries', {
250
+ selector: pending.selector,
251
+ maxRetries: constants_1.MAX_RETRY_ATTEMPTS,
252
+ });
253
+ }
254
+ }
255
+ this.pendingSelectors.length = 0;
256
+ this.pendingSelectors.push(...remaining);
257
+ if (this.pendingSelectors.length > 0) {
258
+ this.retryPendingSelectors();
259
+ }
260
+ else {
261
+ // All retries complete
262
+ if (this.mutationObserver) {
263
+ this.mutationObserver.disconnect();
264
+ this.mutationObserver = null;
265
+ }
266
+ // Apply window fallback if needed and no containers were set up
267
+ if (this.windowFallbackNeeded && this.containers.length === 0) {
268
+ logging_1.debugLog.debug('ScrollHandler', 'No scroll containers found, using window fallback');
269
+ this.setupScrollContainer(window);
270
+ }
271
+ logging_1.debugLog.debug('ScrollHandler', 'All pending selectors resolved or timed out', {
272
+ containersCount: this.containers.length,
273
+ });
274
+ }
275
+ }, constants_1.RETRY_DELAY_MS);
276
+ }
137
277
  }
138
278
  exports.ScrollHandler = ScrollHandler;
@@ -87,7 +87,7 @@ class SenderManager extends state_manager_1.StateManager {
87
87
  const response = await fetch(url, {
88
88
  method: 'POST',
89
89
  mode: 'cors',
90
- credentials: 'omit',
90
+ credentials: 'include',
91
91
  body: payload,
92
92
  headers: {
93
93
  'Content-Type': 'application/json',
@@ -131,7 +131,7 @@ class SenderManager extends state_manager_1.StateManager {
131
131
  xhr.setRequestHeader('Content-Type', 'application/json');
132
132
  xhr.setRequestHeader('Origin', window.location.origin);
133
133
  xhr.setRequestHeader('Referer', window.location.href);
134
- xhr.withCredentials = false;
134
+ xhr.withCredentials = true;
135
135
  xhr.timeout = constants_1.SYNC_XHR_TIMEOUT_MS;
136
136
  xhr.send(payload);
137
137
  return xhr.status >= 200 && xhr.status < 300;
@@ -23,3 +23,5 @@ export declare const SYNC_XHR_TIMEOUT_MS = 2000;
23
23
  export declare const MAX_FINGERPRINTS = 1000;
24
24
  export declare const FINGERPRINT_CLEANUP_MULTIPLIER = 10;
25
25
  export declare const CLICK_COORDINATE_PRECISION = 10;
26
+ export declare const MAX_RETRY_ATTEMPTS = 3;
27
+ export declare const RETRY_DELAY_MS = 1000;
@@ -35,3 +35,6 @@ export const MAX_FINGERPRINTS = 1000; // Maximum fingerprints stored before clea
35
35
  export const FINGERPRINT_CLEANUP_MULTIPLIER = 10; // Cleanup fingerprints older than 10x threshold
36
36
  // Click coordinate precision
37
37
  export const CLICK_COORDINATE_PRECISION = 10; // Round click coordinates to nearest 10px
38
+ // Retry limits
39
+ export const MAX_RETRY_ATTEMPTS = 3;
40
+ export const RETRY_DELAY_MS = 1000;
@@ -3,6 +3,9 @@ import { StateManager } from '../managers/state.manager';
3
3
  export declare class ScrollHandler extends StateManager {
4
4
  private readonly eventManager;
5
5
  private readonly containers;
6
+ private readonly pendingSelectors;
7
+ private mutationObserver;
8
+ private windowFallbackNeeded;
6
9
  constructor(eventManager: EventManager);
7
10
  startTracking(): void;
8
11
  stopTracking(): void;
@@ -11,6 +14,12 @@ export declare class ScrollHandler extends StateManager {
11
14
  private getScrollTop;
12
15
  private getViewportHeight;
13
16
  private getScrollHeight;
14
- private isElementScrollable;
17
+ private getViewportWidth;
18
+ private getScrollWidth;
19
+ private hasScrollableOverflow;
15
20
  private safeQuerySelector;
21
+ private setupPendingSelectors;
22
+ private startMutationObserver;
23
+ private checkPendingSelectors;
24
+ private retryPendingSelectors;
16
25
  }
@@ -1,4 +1,4 @@
1
- import { SCROLL_DEBOUNCE_TIME_MS, SIGNIFICANT_SCROLL_DELTA } from '../constants';
1
+ import { MAX_RETRY_ATTEMPTS, RETRY_DELAY_MS, SCROLL_DEBOUNCE_TIME_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,21 +6,44 @@ 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
12
  this.eventManager = eventManager;
10
13
  }
11
14
  startTracking() {
12
15
  const raw = this.get('config').scrollContainerSelectors;
13
16
  const selectors = Array.isArray(raw) ? raw : typeof raw === 'string' ? [raw] : [];
14
17
  debugLog.debug('ScrollHandler', 'Starting scroll tracking', { selectorsCount: selectors.length });
15
- const elements = selectors
16
- .map((sel) => this.safeQuerySelector(sel))
17
- .filter((element) => element instanceof HTMLElement);
18
- if (elements.length === 0) {
19
- elements.push(window);
18
+ // No custom selectors: track window immediately
19
+ if (selectors.length === 0) {
20
+ this.setupScrollContainer(window);
21
+ return;
20
22
  }
21
- for (const element of elements) {
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) {
22
36
  this.setupScrollContainer(element);
23
37
  }
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
+ }
24
47
  }
25
48
  stopTracking() {
26
49
  debugLog.debug('ScrollHandler', 'Stopping scroll tracking', { containersCount: this.containers.length });
@@ -36,10 +59,15 @@ export class ScrollHandler extends StateManager {
36
59
  }
37
60
  }
38
61
  this.containers.length = 0;
62
+ if (this.mutationObserver) {
63
+ this.mutationObserver.disconnect();
64
+ this.mutationObserver = null;
65
+ }
66
+ this.pendingSelectors.length = 0;
39
67
  }
40
68
  setupScrollContainer(element) {
41
- // Skip setup for non-scrollable elements
42
- if (element !== window && !this.isElementScrollable(element)) {
69
+ // Skip if already tracking this element
70
+ if (this.containers.some((c) => c.element === element)) {
43
71
  return;
44
72
  }
45
73
  const container = {
@@ -81,9 +109,28 @@ export class ScrollHandler extends StateManager {
81
109
  const scrollTop = this.getScrollTop(element);
82
110
  const viewportHeight = this.getViewportHeight(element);
83
111
  const scrollHeight = this.getScrollHeight(element);
84
- // Check if the page is actually scrollable at runtime
85
- if (element === window && scrollHeight <= viewportHeight) {
86
- return null;
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
+ }
87
134
  }
88
135
  const direction = scrollTop > lastScrollPos ? ScrollDirection.DOWN : ScrollDirection.UP;
89
136
  const depth = scrollHeight > viewportHeight
@@ -106,17 +153,20 @@ export class ScrollHandler extends StateManager {
106
153
  getScrollHeight(element) {
107
154
  return element instanceof Window ? document.documentElement.scrollHeight : element.scrollHeight;
108
155
  }
109
- isElementScrollable(element) {
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) {
110
163
  const style = getComputedStyle(element);
111
- const hasScrollableOverflow = style.overflowY === 'auto' ||
164
+ return (style.overflowY === 'auto' ||
112
165
  style.overflowY === 'scroll' ||
113
166
  style.overflowX === 'auto' ||
114
167
  style.overflowX === 'scroll' ||
115
168
  style.overflow === 'auto' ||
116
- style.overflow === 'scroll';
117
- // Element must have scrollable overflow AND content that exceeds the container
118
- const hasOverflowContent = element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth;
119
- return hasScrollableOverflow && hasOverflowContent;
169
+ style.overflow === 'scroll');
120
170
  }
121
171
  safeQuerySelector(selector) {
122
172
  try {
@@ -131,4 +181,94 @@ export class ScrollHandler extends StateManager {
131
181
  return null;
132
182
  }
133
183
  }
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
+ }
134
274
  }
@@ -84,7 +84,7 @@ export class SenderManager extends StateManager {
84
84
  const response = await fetch(url, {
85
85
  method: 'POST',
86
86
  mode: 'cors',
87
- credentials: 'omit',
87
+ credentials: 'include',
88
88
  body: payload,
89
89
  headers: {
90
90
  'Content-Type': 'application/json',
@@ -128,7 +128,7 @@ export class SenderManager extends StateManager {
128
128
  xhr.setRequestHeader('Content-Type', 'application/json');
129
129
  xhr.setRequestHeader('Origin', window.location.origin);
130
130
  xhr.setRequestHeader('Referer', window.location.href);
131
- xhr.withCredentials = false;
131
+ xhr.withCredentials = true;
132
132
  xhr.timeout = SYNC_XHR_TIMEOUT_MS;
133
133
  xhr.send(payload);
134
134
  return xhr.status >= 200 && xhr.status < 300;
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@tracelog/lib",
3
3
  "description": "JavaScript library for web analytics and real-time event tracking",
4
4
  "license": "MIT",
5
- "version": "0.0.7",
5
+ "version": "0.1.0",
6
6
  "main": "./dist/cjs/public-api.js",
7
7
  "module": "./dist/esm/public-api.js",
8
8
  "types": "./dist/esm/public-api.d.ts",