@zaplier/sdk 1.7.2 โ†’ 1.7.4

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/dist/index.cjs CHANGED
@@ -19276,85 +19276,6 @@ class AutoTracker {
19276
19276
  */
19277
19277
  this.cachedScrollElements = null;
19278
19278
  this.lastScrollElementsCheck = 0;
19279
- this.handleScroll = () => {
19280
- if (!this.config.trackScrolls)
19281
- return;
19282
- // Throttle scroll events for performance
19283
- const now = Date.now();
19284
- if (now - this.scrollThrottle < 100)
19285
- return; // 100ms throttle
19286
- this.scrollThrottle = now;
19287
- const scrollElements = document.querySelectorAll('[data-track-scroll]');
19288
- if (this.config.debug && scrollElements.length > 0) {
19289
- console.log(`[AutoTracker] Checking ${scrollElements.length} scroll elements`, {
19290
- scrollY: window.scrollY,
19291
- innerHeight: window.innerHeight
19292
- });
19293
- }
19294
- scrollElements.forEach((element) => {
19295
- let hasTriggered = element.getAttribute('data-scroll-triggered') === 'true';
19296
- if (hasTriggered)
19297
- return;
19298
- const threshold = parseFloat(element.getAttribute("data-scroll-threshold") || "0.5");
19299
- const rect = element.getBoundingClientRect();
19300
- const elementHeight = rect.height;
19301
- const visibleHeight = Math.min(rect.bottom, window.innerHeight) - Math.max(rect.top, 0);
19302
- const visibilityRatio = Math.max(0, visibleHeight) / elementHeight;
19303
- if (this.config.debug) {
19304
- console.log(`[AutoTracker] Element check:`, {
19305
- elementId: element.id || element.className,
19306
- visibilityRatio: Math.round(visibilityRatio * 100) / 100,
19307
- threshold,
19308
- triggered: hasTriggered
19309
- });
19310
- }
19311
- if (visibilityRatio >= threshold) {
19312
- element.setAttribute('data-scroll-triggered', 'true');
19313
- const eventName = element.getAttribute("data-track-scroll");
19314
- if (!eventName)
19315
- return;
19316
- const metadata = this.extractMetadata(element);
19317
- this.trackEvent(eventName, {
19318
- type: "scroll",
19319
- element: element.tagName.toLowerCase(),
19320
- threshold,
19321
- scrollDepth: window.scrollY,
19322
- visibilityRatio: Math.round(visibilityRatio * 100) / 100,
19323
- ...metadata,
19324
- });
19325
- if (this.config.debug) {
19326
- console.log(`[AutoTracker] Scroll tracked: ${eventName}`, {
19327
- threshold,
19328
- visibilityRatio,
19329
- scrollDepth: window.scrollY,
19330
- ...metadata
19331
- });
19332
- }
19333
- }
19334
- });
19335
- };
19336
- this.handleClick = (event) => {
19337
- if (!this.config.trackClicks)
19338
- return;
19339
- const target = event.target;
19340
- if (!target || !target.hasAttribute("data-track-click"))
19341
- return;
19342
- const eventName = target.getAttribute("data-track-click");
19343
- if (!eventName)
19344
- return;
19345
- const metadata = this.extractMetadata(target);
19346
- this.trackEvent(eventName, {
19347
- type: "click",
19348
- element: target.tagName.toLowerCase(),
19349
- ...metadata,
19350
- });
19351
- if (this.config.debug) {
19352
- console.log(`[AutoTracker] Click tracked: ${eventName}`, {
19353
- element: target.tagName.toLowerCase(),
19354
- ...metadata
19355
- });
19356
- }
19357
- };
19358
19279
  this.sdkInstance = sdkInstance;
19359
19280
  this.config = {
19360
19281
  enabled: true,
@@ -19645,18 +19566,73 @@ class AutoTracker {
19645
19566
  const clickHandler = this.createReactCompatibleClickHandler();
19646
19567
  this.delegationHandlers.set('click', clickHandler);
19647
19568
  delegationTarget.addEventListener('click', clickHandler, this.getEventOptions('click'));
19569
+ if (this.config.debug) {
19570
+ console.log("[๐Ÿ‘† AutoTracker] Click delegation handler added");
19571
+ }
19648
19572
  }
19649
- // Scroll delegation with enhanced detection
19573
+ // CRITICAL FIX: Scroll delegation with enhanced detection
19650
19574
  if (this.config.trackScrolls) {
19651
19575
  const scrollHandler = this.createEnhancedScrollHandler();
19652
19576
  this.delegationHandlers.set('scroll', scrollHandler);
19577
+ // Always use document for scroll events (window doesn't work with removeEventListener)
19653
19578
  document.addEventListener('scroll', scrollHandler, this.getEventOptions('scroll'));
19579
+ if (this.config.debug) {
19580
+ console.log("[๐Ÿ“œ AutoTracker] โœ… Scroll delegation handler added to document");
19581
+ console.log("[๐Ÿ“œ AutoTracker] ๐Ÿ“‹ Event listener details:", {
19582
+ target: 'document',
19583
+ eventType: 'scroll',
19584
+ handlerType: typeof scrollHandler,
19585
+ options: this.getEventOptions('scroll'),
19586
+ handlerStored: this.delegationHandlers.has('scroll')
19587
+ });
19588
+ // Test if handler is actually working
19589
+ const testHandler = () => {
19590
+ console.log("[๐Ÿ“œ AutoTracker] ๐Ÿงช MANUAL SCROLL TEST - Handler should trigger");
19591
+ scrollHandler(new Event('scroll'));
19592
+ };
19593
+ // Expose test function globally for manual testing
19594
+ if (typeof window !== 'undefined') {
19595
+ window.testScrollHandler = testHandler;
19596
+ console.log("[๐Ÿ“œ AutoTracker] ๐Ÿงช Manual test available: window.testScrollHandler()");
19597
+ }
19598
+ // Immediate test of element detection
19599
+ setTimeout(() => {
19600
+ const scrollElements = this.getCachedScrollElements();
19601
+ console.log(`[๐Ÿ“œ AutoTracker] ๐Ÿ“ Initial scroll elements check: ${scrollElements.length} found`);
19602
+ if (scrollElements.length > 0) {
19603
+ Array.from(scrollElements).forEach((element, index) => {
19604
+ console.log(`[๐Ÿ“œ AutoTracker] Element ${index + 1}:`, {
19605
+ id: element.id || 'no-id',
19606
+ trackEvent: element.getAttribute('data-track-scroll'),
19607
+ threshold: element.getAttribute('data-scroll-threshold') || '0.5',
19608
+ tagName: element.tagName,
19609
+ rect: element.getBoundingClientRect(),
19610
+ isVisible: element.getBoundingClientRect().height > 0
19611
+ });
19612
+ });
19613
+ }
19614
+ // Also test manually trigger one element
19615
+ if (scrollElements.length > 0) {
19616
+ const firstElement = scrollElements[0];
19617
+ if (firstElement) {
19618
+ console.log(`[๐Ÿ“œ AutoTracker] ๐Ÿงช Testing manual trigger on first element:`, firstElement.getAttribute('data-track-scroll'));
19619
+ setTimeout(() => {
19620
+ console.log("[๐Ÿ“œ AutoTracker] ๐Ÿงช Manual trigger test executed");
19621
+ }, 500);
19622
+ }
19623
+ }
19624
+ }, 100);
19625
+ }
19654
19626
  }
19655
19627
  // Setup debounced mutation observer for dynamic content
19656
19628
  this.setupIntelligentMutationObserver();
19657
19629
  this.isInitialized = true;
19658
19630
  if (this.config.debug) {
19659
- console.log("[โœ… AutoTracker] Event delegation setup complete");
19631
+ console.log("[โœ… AutoTracker] Event delegation setup complete", {
19632
+ clickHandler: this.delegationHandlers.has('click'),
19633
+ scrollHandler: this.delegationHandlers.has('scroll'),
19634
+ totalHandlers: this.delegationHandlers.size
19635
+ });
19660
19636
  }
19661
19637
  }
19662
19638
  /**
@@ -19784,44 +19760,98 @@ class AutoTracker {
19784
19760
  createEnhancedScrollHandler() {
19785
19761
  return () => {
19786
19762
  try {
19787
- if (!this.config.trackScrolls)
19763
+ if (this.config.debug) {
19764
+ console.log(`[๐Ÿ“œ AutoTracker] ๐Ÿš€ SCROLL HANDLER CALLED`, {
19765
+ trackScrolls: this.config.trackScrolls,
19766
+ scrollY: window.scrollY,
19767
+ timestamp: Date.now()
19768
+ });
19769
+ }
19770
+ if (!this.config.trackScrolls) {
19771
+ if (this.config.debug) {
19772
+ console.log(`[๐Ÿ“œ AutoTracker] โญ๏ธ Scroll tracking disabled, skipping`);
19773
+ }
19788
19774
  return;
19775
+ }
19789
19776
  // Adaptive throttling based on scroll frequency
19790
19777
  const now = Date.now();
19791
19778
  const timeDelta = now - this.scrollThrottle;
19792
19779
  const throttleDelay = this.diagnostics.environment?.isMobile ? 150 : 100;
19793
- if (timeDelta < throttleDelay)
19780
+ if (this.config.debug) {
19781
+ console.log(`[๐Ÿ“œ AutoTracker] โฑ๏ธ Throttle check:`, {
19782
+ timeDelta,
19783
+ throttleDelay,
19784
+ willProcess: timeDelta >= throttleDelay,
19785
+ lastScrollThrottle: this.scrollThrottle
19786
+ });
19787
+ }
19788
+ if (timeDelta < throttleDelay) {
19789
+ if (this.config.debug) {
19790
+ console.log(`[๐Ÿ“œ AutoTracker] โธ๏ธ Throttled - skipping (${timeDelta}ms < ${throttleDelay}ms)`);
19791
+ }
19794
19792
  return;
19793
+ }
19795
19794
  this.scrollThrottle = now;
19796
19795
  // Enhanced element finding with caching
19797
19796
  const scrollElements = this.getCachedScrollElements();
19798
- if (this.config.debug && scrollElements.length > 0) {
19799
- console.log(`[๐Ÿ“œ AutoTracker] Processing ${scrollElements.length} scroll elements`, {
19797
+ if (this.config.debug) {
19798
+ console.log(`[๐Ÿ“œ AutoTracker] ๐Ÿ” Processing scroll elements:`, {
19799
+ scrollElementsFound: scrollElements.length,
19800
+ scrollY: window.scrollY,
19801
+ windowHeight: window.innerHeight,
19800
19802
  throttleDelay,
19801
- timeDelta,
19802
- scrollY: window.scrollY
19803
+ timeDelta
19803
19804
  });
19804
19805
  }
19805
19806
  // Process elements with enhanced visibility detection
19806
- scrollElements.forEach((element) => {
19807
- this.processScrollElementEnhanced(element);
19808
- });
19807
+ if (scrollElements.length > 0) {
19808
+ if (this.config.debug) {
19809
+ console.log(`[๐Ÿ“œ AutoTracker] ๐ŸŽฏ Starting to process ${scrollElements.length} elements`);
19810
+ }
19811
+ scrollElements.forEach((element, index) => {
19812
+ if (this.config.debug) {
19813
+ console.log(`[๐Ÿ“œ AutoTracker] ๐Ÿ”„ Processing element ${index + 1}/${scrollElements.length}:`, {
19814
+ elementId: element.id || 'no-id',
19815
+ trackEvent: element.getAttribute('data-track-scroll'),
19816
+ hasTriggered: element.getAttribute('data-scroll-triggered') === 'true'
19817
+ });
19818
+ }
19819
+ this.processScrollElementEnhanced(element);
19820
+ });
19821
+ if (this.config.debug) {
19822
+ console.log(`[๐Ÿ“œ AutoTracker] โœ… Finished processing all ${scrollElements.length} elements`);
19823
+ }
19824
+ }
19825
+ else if (this.config.debug) {
19826
+ console.log(`[๐Ÿ“œ AutoTracker] โš ๏ธ No scroll elements found to process - cache might be empty`);
19827
+ }
19809
19828
  }
19810
19829
  catch (error) {
19811
19830
  if (this.config.debug) {
19812
- console.error('[โŒ AutoTracker] Scroll handler error:', error);
19831
+ console.error('[โŒ AutoTracker] CRITICAL: Enhanced scroll handler error:', error);
19813
19832
  }
19814
19833
  }
19815
19834
  };
19816
19835
  }
19817
19836
  getCachedScrollElements() {
19818
19837
  const now = Date.now();
19819
- const cacheExpiry = 5000; // 5 seconds
19838
+ const cacheExpiry = 2000; // Reduced to 2 seconds for faster detection
19820
19839
  if (!this.cachedScrollElements || (now - this.lastScrollElementsCheck) > cacheExpiry) {
19821
19840
  this.cachedScrollElements = document.querySelectorAll('[data-track-scroll]');
19822
19841
  this.lastScrollElementsCheck = now;
19823
19842
  if (this.config.debug) {
19824
- console.log(`[๐Ÿ”„ AutoTracker] Refreshed scroll elements cache: ${this.cachedScrollElements.length} elements`);
19843
+ console.log(`[๐Ÿ”„ AutoTracker] Refreshed scroll elements cache: ${this.cachedScrollElements.length} elements found`);
19844
+ // Log each element found for debugging
19845
+ Array.from(this.cachedScrollElements).forEach((element, index) => {
19846
+ console.log(`[๐Ÿ”„ AutoTracker] Cached element ${index + 1}:`, {
19847
+ id: element.id || 'no-id',
19848
+ className: element.className || 'no-class',
19849
+ trackEvent: element.getAttribute('data-track-scroll'),
19850
+ threshold: element.getAttribute('data-scroll-threshold') || '0.5',
19851
+ tagName: element.tagName,
19852
+ triggered: element.getAttribute('data-scroll-triggered') === 'true'
19853
+ });
19854
+ });
19825
19855
  }
19826
19856
  }
19827
19857
  return this.cachedScrollElements;
@@ -19832,43 +19862,71 @@ class AutoTracker {
19832
19862
  processScrollElementEnhanced(element) {
19833
19863
  try {
19834
19864
  const hasTriggered = element.getAttribute('data-scroll-triggered') === 'true';
19835
- if (hasTriggered)
19865
+ if (hasTriggered) {
19866
+ if (this.config.debug) {
19867
+ console.log(`[โญ๏ธ AutoTracker] Element already triggered, skipping:`, element.id || element.className || 'no-id');
19868
+ }
19836
19869
  return;
19870
+ }
19837
19871
  const threshold = parseFloat(element.getAttribute('data-scroll-threshold') || '0.5');
19838
19872
  // Enhanced visibility calculation
19839
19873
  const rect = element.getBoundingClientRect();
19840
19874
  const elementHeight = rect.height;
19841
19875
  const windowHeight = window.innerHeight;
19842
19876
  // Handle edge cases
19843
- if (elementHeight <= 0)
19877
+ if (elementHeight <= 0) {
19878
+ if (this.config.debug) {
19879
+ console.log(`[โš ๏ธ AutoTracker] Element has no height, skipping:`, element.id || 'no-id');
19880
+ }
19844
19881
  return;
19882
+ }
19845
19883
  const visibleTop = Math.max(rect.top, 0);
19846
19884
  const visibleBottom = Math.min(rect.bottom, windowHeight);
19847
19885
  const visibleHeight = Math.max(0, visibleBottom - visibleTop);
19848
19886
  const visibilityRatio = visibleHeight / elementHeight;
19887
+ const eventName = element.getAttribute('data-track-scroll');
19888
+ if (!eventName) {
19889
+ if (this.config.debug) {
19890
+ console.log(`[โš ๏ธ AutoTracker] Element missing data-track-scroll attribute:`, element.id || 'no-id');
19891
+ }
19892
+ return;
19893
+ }
19849
19894
  if (this.config.debug) {
19850
- console.log(`[๐Ÿ“Š AutoTracker] Enhanced scroll check:`, {
19851
- elementId: element.id || element.className || 'no-id',
19852
- rect: {
19853
- top: Math.round(rect.top),
19854
- bottom: Math.round(rect.bottom),
19855
- height: Math.round(elementHeight)
19895
+ const shouldTrigger = visibilityRatio >= threshold;
19896
+ console.log(`[๐Ÿ“Š AutoTracker] ๐Ÿ” DETAILED visibility check for "${eventName}":`, {
19897
+ elementInfo: {
19898
+ id: element.id || 'no-id',
19899
+ className: element.className || 'no-class',
19900
+ tagName: element.tagName,
19901
+ trackEvent: eventName,
19902
+ threshold: threshold
19903
+ },
19904
+ positioning: {
19905
+ rect_top: Math.round(rect.top),
19906
+ rect_bottom: Math.round(rect.bottom),
19907
+ rect_height: Math.round(elementHeight),
19908
+ window_height: windowHeight,
19909
+ scroll_y: window.scrollY
19856
19910
  },
19857
- visibility: {
19858
- ratio: Math.round(visibilityRatio * 1000) / 1000,
19859
- threshold,
19860
- triggered: hasTriggered
19911
+ visibilityCalculation: {
19912
+ step1_visibleTop: `Math.max(${Math.round(rect.top)}, 0) = ${visibleTop}`,
19913
+ step2_visibleBottom: `Math.min(${Math.round(rect.bottom)}, ${windowHeight}) = ${visibleBottom}`,
19914
+ step3_visibleHeight: `Math.max(0, ${visibleBottom} - ${visibleTop}) = ${visibleHeight}`,
19915
+ step4_visibilityRatio: `${visibleHeight} / ${Math.round(elementHeight)} = ${Math.round(visibilityRatio * 1000) / 1000}`,
19916
+ step5_comparison: `${Math.round(visibilityRatio * 1000) / 1000} >= ${threshold} = ${shouldTrigger}`
19861
19917
  },
19862
- scroll: window.scrollY
19918
+ status: {
19919
+ hasTriggered: hasTriggered,
19920
+ willTrigger: shouldTrigger && !hasTriggered,
19921
+ skipReason: hasTriggered ? 'already_triggered' : (!shouldTrigger ? 'not_visible_enough' : 'none')
19922
+ }
19863
19923
  });
19864
19924
  }
19865
19925
  if (visibilityRatio >= threshold) {
19926
+ // Mark as triggered to prevent duplicate events
19866
19927
  element.setAttribute('data-scroll-triggered', 'true');
19867
- const eventName = element.getAttribute('data-track-scroll');
19868
- if (!eventName)
19869
- return;
19870
19928
  const metadata = this.extractMetadata(element);
19871
- this.trackEvent(eventName, {
19929
+ const eventData = {
19872
19930
  type: 'scroll',
19873
19931
  element: element.tagName.toLowerCase(),
19874
19932
  threshold,
@@ -19876,20 +19934,22 @@ class AutoTracker {
19876
19934
  visibilityRatio: Math.round(visibilityRatio * 1000) / 1000,
19877
19935
  enhanced: true,
19878
19936
  ...metadata,
19879
- });
19937
+ };
19880
19938
  if (this.config.debug) {
19881
- console.log(`[๐ŸŽฏ AutoTracker] Scroll tracked (enhanced): ${eventName}`, {
19882
- threshold,
19883
- visibilityRatio: Math.round(visibilityRatio * 1000) / 1000,
19884
- scrollDepth: window.scrollY,
19885
- ...metadata
19886
- });
19939
+ console.log(`[๐ŸŽฏ AutoTracker] TRIGGERING scroll event "${eventName}":`, eventData);
19940
+ }
19941
+ this.trackEvent(eventName, eventData);
19942
+ if (this.config.debug) {
19943
+ console.log(`[โœ… AutoTracker] Scroll event "${eventName}" sent successfully`);
19887
19944
  }
19888
19945
  }
19889
19946
  }
19890
19947
  catch (error) {
19891
19948
  if (this.config.debug) {
19892
- console.error('[โŒ AutoTracker] Scroll element processing error:', error);
19949
+ console.error('[โŒ AutoTracker] Scroll element processing error:', error, {
19950
+ element: element.id || element.className || 'no-id',
19951
+ tagName: element.tagName
19952
+ });
19893
19953
  }
19894
19954
  }
19895
19955
  }
@@ -20195,17 +20255,18 @@ class AutoTracker {
20195
20255
  trackEvent(eventName, metadata) {
20196
20256
  const trackingStart = performance.now();
20197
20257
  if (this.config.debug) {
20198
- console.log("[๐Ÿš€ AutoTracker] Enhanced trackEvent called:", {
20258
+ console.log("[๐Ÿš€ AutoTracker] ๐ŸŽฏ TRACK EVENT CALLED:", {
20199
20259
  eventName,
20200
20260
  metadata,
20201
- timestamp: new Date().toISOString()
20261
+ timestamp: new Date().toISOString(),
20262
+ stackTrace: new Error().stack?.split('\n').slice(1, 4) // Show call stack
20202
20263
  });
20203
20264
  }
20204
20265
  // Comprehensive validation
20205
20266
  const validation = this.validateTrackingCall(eventName, metadata);
20206
20267
  if (!validation.isValid) {
20207
20268
  if (this.config.debug) {
20208
- console.error("[โŒ AutoTracker] Validation failed:", validation.errors);
20269
+ console.error("[โŒ AutoTracker] ๐Ÿšซ VALIDATION FAILED:", validation.errors);
20209
20270
  }
20210
20271
  return;
20211
20272
  }
@@ -20226,12 +20287,29 @@ class AutoTracker {
20226
20287
  ...metadata,
20227
20288
  };
20228
20289
  if (this.config.debug) {
20229
- console.log("[๐Ÿ“ก AutoTracker] Sending enhanced event:", { eventName, eventData });
20290
+ console.log("[๐Ÿ“ก AutoTracker] ๐Ÿ“ค SENDING EVENT TO SDK:", {
20291
+ eventName,
20292
+ eventData,
20293
+ sdkInstanceExists: !!this.sdkInstance,
20294
+ trackCustomEventExists: typeof this.sdkInstance?.trackCustomEvent === 'function'
20295
+ });
20296
+ }
20297
+ if (!this.sdkInstance) {
20298
+ if (this.config.debug) {
20299
+ console.error("[โŒ AutoTracker] ๐Ÿšซ SDK INSTANCE IS NULL - CANNOT SEND EVENT");
20300
+ }
20301
+ return;
20302
+ }
20303
+ if (typeof this.sdkInstance.trackCustomEvent !== 'function') {
20304
+ if (this.config.debug) {
20305
+ console.error("[โŒ AutoTracker] ๐Ÿšซ trackCustomEvent IS NOT A FUNCTION:", typeof this.sdkInstance.trackCustomEvent);
20306
+ }
20307
+ return;
20230
20308
  }
20231
20309
  const result = this.sdkInstance.trackCustomEvent(eventName, eventData);
20232
20310
  if (this.config.debug) {
20233
20311
  const trackingEnd = performance.now();
20234
- console.log("[โœ… AutoTracker] Event sent successfully:", {
20312
+ console.log("[โœ… AutoTracker] ๐ŸŽ‰ EVENT SENT SUCCESSFULLY:", {
20235
20313
  eventName,
20236
20314
  result,
20237
20315
  totalLatency: Math.round((trackingEnd - trackingStart) * 100) / 100 + 'ms'
@@ -20240,11 +20318,13 @@ class AutoTracker {
20240
20318
  }
20241
20319
  catch (error) {
20242
20320
  if (this.config.debug) {
20243
- console.error("[๐Ÿ’ฅ AutoTracker] Critical tracking error:", {
20321
+ console.error("[๐Ÿ’ฅ AutoTracker] ๐Ÿ’€ CRITICAL TRACKING ERROR:", {
20244
20322
  eventName,
20245
20323
  error: error instanceof Error ? error.message : error,
20246
20324
  stack: error instanceof Error ? error.stack : undefined,
20247
- metadata
20325
+ metadata,
20326
+ sdkInstance: !!this.sdkInstance,
20327
+ sdkInstanceType: typeof this.sdkInstance
20248
20328
  });
20249
20329
  }
20250
20330
  }
@@ -20271,6 +20351,8 @@ class AutoTracker {
20271
20351
  errors
20272
20352
  };
20273
20353
  }
20354
+ // Legacy handlers removed - now using enhanced event delegation system
20355
+ // All tracking is now handled by createEnhancedScrollHandler and createReactCompatibleClickHandler
20274
20356
  /**
20275
20357
  * Modern API: Refresh tracking for dynamic content (React/SPA support)
20276
20358
  * Industry best practice for SPA route changes
@@ -20303,6 +20385,50 @@ class AutoTracker {
20303
20385
  * Get comprehensive diagnostic information for debugging
20304
20386
  */
20305
20387
  getDiagnostics() {
20388
+ // Create manual test functions
20389
+ const manualTests = {
20390
+ triggerScrollHandler: () => {
20391
+ if (this.config.debug) {
20392
+ console.log("[๐Ÿงช AutoTracker] Manual scroll handler trigger");
20393
+ }
20394
+ const handler = this.delegationHandlers.get('scroll');
20395
+ if (handler) {
20396
+ handler(new Event('scroll'));
20397
+ }
20398
+ else {
20399
+ console.error("[โŒ AutoTracker] No scroll handler found");
20400
+ }
20401
+ },
20402
+ forceScrollEvent: (elementSelector) => {
20403
+ const element = document.querySelector(elementSelector);
20404
+ if (!element) {
20405
+ console.error(`[โŒ AutoTracker] Element not found: ${elementSelector}`);
20406
+ return;
20407
+ }
20408
+ const eventName = element.getAttribute('data-track-scroll');
20409
+ if (!eventName) {
20410
+ console.error(`[โŒ AutoTracker] Element has no data-track-scroll: ${elementSelector}`);
20411
+ return;
20412
+ }
20413
+ console.log(`[๐Ÿงช AutoTracker] Force triggering scroll event: ${eventName}`);
20414
+ // Reset trigger state
20415
+ element.removeAttribute('data-scroll-triggered');
20416
+ // Force trigger
20417
+ this.processScrollElementEnhanced(element);
20418
+ },
20419
+ testTrackEvent: (eventName = 'test_event') => {
20420
+ console.log(`[๐Ÿงช AutoTracker] Testing trackEvent: ${eventName}`);
20421
+ this.trackEvent(eventName, {
20422
+ type: 'test',
20423
+ source: 'manual_diagnostic',
20424
+ timestamp: Date.now()
20425
+ });
20426
+ }
20427
+ };
20428
+ // Expose tests globally for easy access
20429
+ if (typeof window !== 'undefined') {
20430
+ window.autoTrackerTests = manualTests;
20431
+ }
20306
20432
  return {
20307
20433
  // Initialization status
20308
20434
  initialization: {
@@ -20344,7 +20470,17 @@ class AutoTracker {
20344
20470
  // Validation status
20345
20471
  validation: this.validateTrackingCall('test', {}),
20346
20472
  // Debug recommendations
20347
- recommendations: this.generateRecommendations()
20473
+ recommendations: this.generateRecommendations(),
20474
+ // Manual testing functions
20475
+ manualTests: {
20476
+ available: true,
20477
+ usage: {
20478
+ triggerScrollHandler: 'window.autoTrackerTests.triggerScrollHandler()',
20479
+ forceScrollEvent: 'window.autoTrackerTests.forceScrollEvent("[data-track-scroll]")',
20480
+ testTrackEvent: 'window.autoTrackerTests.testTrackEvent("my_event")'
20481
+ },
20482
+ description: 'Use these functions in the browser console to manually test the AutoTracker'
20483
+ }
20348
20484
  };
20349
20485
  }
20350
20486
  /**
@@ -20396,6 +20532,33 @@ class AutoTracker {
20396
20532
  }
20397
20533
  }
20398
20534
 
20535
+ /**
20536
+ * Centralized Session Utilities
20537
+ * Standardized session ID generation for consistency across SDK and backend
20538
+ */
20539
+ /**
20540
+ * Generate standardized session ID
20541
+ * Format: visitorId_YYYYMMDD_HH
20542
+ * Example: f697a6e1-1aae-48db-bc6a-afc1d2c01852_20251231_03
20543
+ */
20544
+ function generateSessionId(visitorId, date) {
20545
+ const sessionDate = date || new Date();
20546
+ // Format: YYYYMMDD
20547
+ const year = sessionDate.getFullYear();
20548
+ const month = (sessionDate.getMonth() + 1).toString().padStart(2, '0');
20549
+ const day = sessionDate.getDate().toString().padStart(2, '0');
20550
+ const dateStr = `${year}${month}${day}`;
20551
+ // Format: HH (24-hour format)
20552
+ const hour = sessionDate.getHours().toString().padStart(2, '0');
20553
+ return `${visitorId}_${dateStr}_${hour}`;
20554
+ }
20555
+ /**
20556
+ * Generate session ID for current time
20557
+ */
20558
+ function generateCurrentSessionId(visitorId) {
20559
+ return generateSessionId(visitorId, new Date());
20560
+ }
20561
+
20399
20562
  /**
20400
20563
  * Visitor Persistence Manager for SDK
20401
20564
  * Implements client-side visitor identification with localStorage camouflage
@@ -20555,9 +20718,23 @@ class VisitorIdentityManager {
20555
20718
  // REMOVED: generateVisitorId() and generateVisitorIdFromHash() methods
20556
20719
  // SDK no longer generates visitor IDs - only backend does
20557
20720
  generateSessionId() {
20558
- return "ses_" + Array.from(crypto.getRandomValues(new Uint8Array(8)))
20559
- .map(b => b.toString(16).padStart(2, '0'))
20560
- .join('') + "_" + Date.now().toString(36);
20721
+ // Try to get existing visitor identity from storage
20722
+ const { value: storedData } = PersistenceManager.get(STORAGE_KEYS.prefs);
20723
+ if (storedData) {
20724
+ try {
20725
+ const parsed = JSON.parse(storedData);
20726
+ if (parsed._v && parsed._vb === true) {
20727
+ // We have a valid visitor ID from backend
20728
+ return generateCurrentSessionId(parsed._v);
20729
+ }
20730
+ }
20731
+ catch (e) {
20732
+ // Invalid data, continue to fallback
20733
+ }
20734
+ }
20735
+ // Fallback for cases where visitor ID is not available yet
20736
+ const tempVisitorId = "temp-" + Date.now().toString(36);
20737
+ return generateCurrentSessionId(tempVisitorId);
20561
20738
  }
20562
20739
  createCamouflageData(visitorId, sessionId, stableCoreHash, fromBackend = false) {
20563
20740
  return {
@@ -20567,7 +20744,7 @@ class VisitorIdentityManager {
20567
20744
  analytics: true,
20568
20745
  cookies: true,
20569
20746
  _v: visitorId, // Hidden visitor ID
20570
- _s: sessionId, // Hidden session ID
20747
+ _s: sessionId, // Hidden session ID
20571
20748
  _sc: stableCoreHash, // Hidden stable core
20572
20749
  _vb: fromBackend ? true : undefined, // Flag: visitor ID from backend (v1.7.0+)
20573
20750
  ts: Date.now(),
@@ -20611,7 +20788,9 @@ class VisitorIdentityManager {
20611
20788
  const { value: storedData, method } = PersistenceManager.get(STORAGE_KEYS.prefs);
20612
20789
  if (storedData) {
20613
20790
  const existingIdentity = this.extractFromCamouflage(storedData);
20614
- if (existingIdentity && existingIdentity.stableCoreHash === stableCoreHash && existingIdentity.visitorId) {
20791
+ if (existingIdentity &&
20792
+ existingIdentity.stableCoreHash === stableCoreHash &&
20793
+ existingIdentity.visitorId) {
20615
20794
  // Update last seen time only if we have a valid visitor ID from backend
20616
20795
  const updatedCamouflage = this.createCamouflageData(existingIdentity.visitorId, existingIdentity.sessionId, stableCoreHash);
20617
20796
  PersistenceManager.set(STORAGE_KEYS.prefs, JSON.stringify(updatedCamouflage));
@@ -20631,7 +20810,7 @@ class VisitorIdentityManager {
20631
20810
  stableCoreHash,
20632
20811
  deviceFingerprint: params.deviceFingerprint,
20633
20812
  persistenceMethod: "memory", // Start with memory, upgrade after backend response
20634
- confidence: 0.90,
20813
+ confidence: 0.9,
20635
20814
  createdAt: Date.now(),
20636
20815
  lastSeen: Date.now(),
20637
20816
  reused: false,
@@ -20646,7 +20825,12 @@ class VisitorIdentityManager {
20646
20825
  async updateVisitorIdFromBackend(visitorId, stableCoreHash, sessionId) {
20647
20826
  try {
20648
20827
  // Create or update camouflaged data with backend visitor ID
20649
- const currentSessionId = sessionId || this.getCurrentSessionId() || this.generateSessionId();
20828
+ // Regenerate session ID with proper visitor ID if needed
20829
+ let currentSessionId = sessionId || this.getCurrentSessionId();
20830
+ // If we have a temp session ID or no session ID, generate a new one with the proper visitor ID
20831
+ if (!currentSessionId || currentSessionId.includes("temp-")) {
20832
+ currentSessionId = generateCurrentSessionId(visitorId);
20833
+ }
20650
20834
  const camouflageData = this.createCamouflageData(visitorId, currentSessionId, stableCoreHash, true // Mark as coming from backend
20651
20835
  );
20652
20836
  const { success, method } = PersistenceManager.set(STORAGE_KEYS.prefs, JSON.stringify(camouflageData));
@@ -20658,7 +20842,7 @@ class VisitorIdentityManager {
20658
20842
  return false;
20659
20843
  }
20660
20844
  catch (error) {
20661
- console.error('[VisitorIdentityManager] Failed to update visitor ID from backend:', error);
20845
+ console.error("[VisitorIdentityManager] Failed to update visitor ID from backend:", error);
20662
20846
  return false;
20663
20847
  }
20664
20848
  }
@@ -21112,36 +21296,17 @@ class ZaplierSDK {
21112
21296
  }
21113
21297
  }
21114
21298
  /**
21115
- * Generate session ID
21299
+ * Generate session ID using standardized format
21116
21300
  */
21117
21301
  generateSessionId() {
21118
- // Create deterministic session ID based on page load time and stable browser characteristics
21119
- const pageLoadTime = performance.timeOrigin || Date.now();
21120
- const browserFingerprint = this.getBrowserFingerprint();
21121
- // Use page load time + browser fingerprint for session uniqueness within same page load
21122
- return pageLoadTime.toString(36) + browserFingerprint.substring(0, 8);
21123
- }
21124
- /**
21125
- * Get stable browser characteristics for session ID generation
21126
- * Uses only stable, privacy-safe values that don't change during session
21127
- */
21128
- getBrowserFingerprint() {
21129
- const components = [
21130
- navigator.userAgent || "",
21131
- navigator.platform || "",
21132
- screen.width || 0,
21133
- screen.height || 0,
21134
- new Date(2024, 0, 1).getTimezoneOffset(),
21135
- navigator.language || "",
21136
- navigator.hardwareConcurrency || 0,
21137
- ];
21138
- const combined = components.join("|");
21139
- let hash = 0;
21140
- for (let i = 0; i < combined.length; i++) {
21141
- const char = combined.charCodeAt(i);
21142
- hash = ((hash << 5) - hash + char) & 0xffffffff;
21302
+ // Use centralized session generation with current visitor ID
21303
+ if (this.backendVisitorId) {
21304
+ return generateCurrentSessionId(this.backendVisitorId);
21143
21305
  }
21144
- return Math.abs(hash).toString(36);
21306
+ // Fallback for cases where backendVisitorId is not available yet
21307
+ // This should be rare and will be updated when visitor ID is available
21308
+ const tempVisitorId = this.visitorId || 'temp-' + Date.now().toString(36);
21309
+ return generateCurrentSessionId(tempVisitorId);
21145
21310
  }
21146
21311
  /**
21147
21312
  * Initialize Anti-Adblock Manager
@@ -21400,6 +21565,10 @@ class ZaplierSDK {
21400
21565
  if (response.sessionId) {
21401
21566
  this.sessionId = response.sessionId;
21402
21567
  }
21568
+ else if (this.backendVisitorId && (!this.sessionId || this.sessionId.includes('temp-'))) {
21569
+ // Generate new session ID now that we have the proper visitor ID
21570
+ this.sessionId = generateCurrentSessionId(this.backendVisitorId);
21571
+ }
21403
21572
  return response;
21404
21573
  }
21405
21574
  catch (error) {