@zaplier/sdk 1.7.2 → 1.7.3

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,44 @@ 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
+ // Immediate test of element detection
19582
+ setTimeout(() => {
19583
+ const scrollElements = this.getCachedScrollElements();
19584
+ console.log(`[📜 AutoTracker] Initial scroll elements check: ${scrollElements.length} found`);
19585
+ if (scrollElements.length > 0) {
19586
+ Array.from(scrollElements).forEach((element, index) => {
19587
+ console.log(`[📜 AutoTracker] Element ${index + 1}:`, {
19588
+ id: element.id || 'no-id',
19589
+ trackEvent: element.getAttribute('data-track-scroll'),
19590
+ threshold: element.getAttribute('data-scroll-threshold') || '0.5',
19591
+ tagName: element.tagName
19592
+ });
19593
+ });
19594
+ }
19595
+ }, 100);
19596
+ }
19654
19597
  }
19655
19598
  // Setup debounced mutation observer for dynamic content
19656
19599
  this.setupIntelligentMutationObserver();
19657
19600
  this.isInitialized = true;
19658
19601
  if (this.config.debug) {
19659
- console.log("[✅ AutoTracker] Event delegation setup complete");
19602
+ console.log("[✅ AutoTracker] Event delegation setup complete", {
19603
+ clickHandler: this.delegationHandlers.has('click'),
19604
+ scrollHandler: this.delegationHandlers.has('scroll'),
19605
+ totalHandlers: this.delegationHandlers.size
19606
+ });
19660
19607
  }
19661
19608
  }
19662
19609
  /**
@@ -19795,33 +19742,50 @@ class AutoTracker {
19795
19742
  this.scrollThrottle = now;
19796
19743
  // Enhanced element finding with caching
19797
19744
  const scrollElements = this.getCachedScrollElements();
19798
- if (this.config.debug && scrollElements.length > 0) {
19799
- console.log(`[📜 AutoTracker] Processing ${scrollElements.length} scroll elements`, {
19745
+ if (this.config.debug) {
19746
+ console.log(`[📜 AutoTracker] Enhanced scroll handler triggered`, {
19747
+ scrollElementsFound: scrollElements.length,
19748
+ scrollY: window.scrollY,
19800
19749
  throttleDelay,
19801
- timeDelta,
19802
- scrollY: window.scrollY
19750
+ timeDelta
19803
19751
  });
19804
19752
  }
19805
19753
  // Process elements with enhanced visibility detection
19806
- scrollElements.forEach((element) => {
19807
- this.processScrollElementEnhanced(element);
19808
- });
19754
+ if (scrollElements.length > 0) {
19755
+ scrollElements.forEach((element) => {
19756
+ this.processScrollElementEnhanced(element);
19757
+ });
19758
+ }
19759
+ else if (this.config.debug) {
19760
+ console.log(`[📜 AutoTracker] No scroll elements found to process`);
19761
+ }
19809
19762
  }
19810
19763
  catch (error) {
19811
19764
  if (this.config.debug) {
19812
- console.error('[❌ AutoTracker] Scroll handler error:', error);
19765
+ console.error('[❌ AutoTracker] Enhanced scroll handler error:', error);
19813
19766
  }
19814
19767
  }
19815
19768
  };
19816
19769
  }
19817
19770
  getCachedScrollElements() {
19818
19771
  const now = Date.now();
19819
- const cacheExpiry = 5000; // 5 seconds
19772
+ const cacheExpiry = 2000; // Reduced to 2 seconds for faster detection
19820
19773
  if (!this.cachedScrollElements || (now - this.lastScrollElementsCheck) > cacheExpiry) {
19821
19774
  this.cachedScrollElements = document.querySelectorAll('[data-track-scroll]');
19822
19775
  this.lastScrollElementsCheck = now;
19823
19776
  if (this.config.debug) {
19824
- console.log(`[🔄 AutoTracker] Refreshed scroll elements cache: ${this.cachedScrollElements.length} elements`);
19777
+ console.log(`[🔄 AutoTracker] Refreshed scroll elements cache: ${this.cachedScrollElements.length} elements found`);
19778
+ // Log each element found for debugging
19779
+ Array.from(this.cachedScrollElements).forEach((element, index) => {
19780
+ console.log(`[🔄 AutoTracker] Cached element ${index + 1}:`, {
19781
+ id: element.id || 'no-id',
19782
+ className: element.className || 'no-class',
19783
+ trackEvent: element.getAttribute('data-track-scroll'),
19784
+ threshold: element.getAttribute('data-scroll-threshold') || '0.5',
19785
+ tagName: element.tagName,
19786
+ triggered: element.getAttribute('data-scroll-triggered') === 'true'
19787
+ });
19788
+ });
19825
19789
  }
19826
19790
  }
19827
19791
  return this.cachedScrollElements;
@@ -19832,43 +19796,63 @@ class AutoTracker {
19832
19796
  processScrollElementEnhanced(element) {
19833
19797
  try {
19834
19798
  const hasTriggered = element.getAttribute('data-scroll-triggered') === 'true';
19835
- if (hasTriggered)
19799
+ if (hasTriggered) {
19800
+ if (this.config.debug) {
19801
+ console.log(`[⏭️ AutoTracker] Element already triggered, skipping:`, element.id || element.className || 'no-id');
19802
+ }
19836
19803
  return;
19804
+ }
19837
19805
  const threshold = parseFloat(element.getAttribute('data-scroll-threshold') || '0.5');
19838
19806
  // Enhanced visibility calculation
19839
19807
  const rect = element.getBoundingClientRect();
19840
19808
  const elementHeight = rect.height;
19841
19809
  const windowHeight = window.innerHeight;
19842
19810
  // Handle edge cases
19843
- if (elementHeight <= 0)
19811
+ if (elementHeight <= 0) {
19812
+ if (this.config.debug) {
19813
+ console.log(`[⚠️ AutoTracker] Element has no height, skipping:`, element.id || 'no-id');
19814
+ }
19844
19815
  return;
19816
+ }
19845
19817
  const visibleTop = Math.max(rect.top, 0);
19846
19818
  const visibleBottom = Math.min(rect.bottom, windowHeight);
19847
19819
  const visibleHeight = Math.max(0, visibleBottom - visibleTop);
19848
19820
  const visibilityRatio = visibleHeight / elementHeight;
19821
+ const eventName = element.getAttribute('data-track-scroll');
19822
+ if (!eventName) {
19823
+ if (this.config.debug) {
19824
+ console.log(`[⚠️ AutoTracker] Element missing data-track-scroll attribute:`, element.id || 'no-id');
19825
+ }
19826
+ return;
19827
+ }
19849
19828
  if (this.config.debug) {
19850
- console.log(`[📊 AutoTracker] Enhanced scroll check:`, {
19829
+ console.log(`[📊 AutoTracker] Enhanced scroll check for "${eventName}":`, {
19851
19830
  elementId: element.id || element.className || 'no-id',
19852
19831
  rect: {
19853
19832
  top: Math.round(rect.top),
19854
19833
  bottom: Math.round(rect.bottom),
19855
19834
  height: Math.round(elementHeight)
19856
19835
  },
19836
+ window: {
19837
+ height: windowHeight,
19838
+ scrollY: window.scrollY
19839
+ },
19857
19840
  visibility: {
19841
+ visibleTop,
19842
+ visibleBottom,
19843
+ visibleHeight,
19858
19844
  ratio: Math.round(visibilityRatio * 1000) / 1000,
19859
19845
  threshold,
19860
- triggered: hasTriggered
19861
- },
19862
- scroll: window.scrollY
19846
+ triggered: hasTriggered,
19847
+ shouldTrigger: visibilityRatio >= threshold
19848
+ }
19863
19849
  });
19864
19850
  }
19865
19851
  if (visibilityRatio >= threshold) {
19852
+ // Mark as triggered to prevent duplicate events
19866
19853
  element.setAttribute('data-scroll-triggered', 'true');
19867
- const eventName = element.getAttribute('data-track-scroll');
19868
- if (!eventName)
19869
- return;
19870
19854
  const metadata = this.extractMetadata(element);
19871
- this.trackEvent(eventName, {
19855
+ const eventData = {
19872
19856
  type: 'scroll',
19873
19857
  element: element.tagName.toLowerCase(),
19874
19858
  threshold,
@@ -19876,20 +19860,22 @@ class AutoTracker {
19876
19860
  visibilityRatio: Math.round(visibilityRatio * 1000) / 1000,
19877
19861
  enhanced: true,
19878
19862
  ...metadata,
19879
- });
19863
+ };
19880
19864
  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
- });
19865
+ console.log(`[🎯 AutoTracker] TRIGGERING scroll event "${eventName}":`, eventData);
19866
+ }
19867
+ this.trackEvent(eventName, eventData);
19868
+ if (this.config.debug) {
19869
+ console.log(`[✅ AutoTracker] Scroll event "${eventName}" sent successfully`);
19887
19870
  }
19888
19871
  }
19889
19872
  }
19890
19873
  catch (error) {
19891
19874
  if (this.config.debug) {
19892
- console.error('[❌ AutoTracker] Scroll element processing error:', error);
19875
+ console.error('[❌ AutoTracker] Scroll element processing error:', error, {
19876
+ element: element.id || element.className || 'no-id',
19877
+ tagName: element.tagName
19878
+ });
19893
19879
  }
19894
19880
  }
19895
19881
  }
@@ -20271,6 +20257,8 @@ class AutoTracker {
20271
20257
  errors
20272
20258
  };
20273
20259
  }
20260
+ // Legacy handlers removed - now using enhanced event delegation system
20261
+ // All tracking is now handled by createEnhancedScrollHandler and createReactCompatibleClickHandler
20274
20262
  /**
20275
20263
  * Modern API: Refresh tracking for dynamic content (React/SPA support)
20276
20264
  * Industry best practice for SPA route changes
@@ -20396,6 +20384,33 @@ class AutoTracker {
20396
20384
  }
20397
20385
  }
20398
20386
 
20387
+ /**
20388
+ * Centralized Session Utilities
20389
+ * Standardized session ID generation for consistency across SDK and backend
20390
+ */
20391
+ /**
20392
+ * Generate standardized session ID
20393
+ * Format: visitorId_YYYYMMDD_HH
20394
+ * Example: f697a6e1-1aae-48db-bc6a-afc1d2c01852_20251231_03
20395
+ */
20396
+ function generateSessionId(visitorId, date) {
20397
+ const sessionDate = date || new Date();
20398
+ // Format: YYYYMMDD
20399
+ const year = sessionDate.getFullYear();
20400
+ const month = (sessionDate.getMonth() + 1).toString().padStart(2, '0');
20401
+ const day = sessionDate.getDate().toString().padStart(2, '0');
20402
+ const dateStr = `${year}${month}${day}`;
20403
+ // Format: HH (24-hour format)
20404
+ const hour = sessionDate.getHours().toString().padStart(2, '0');
20405
+ return `${visitorId}_${dateStr}_${hour}`;
20406
+ }
20407
+ /**
20408
+ * Generate session ID for current time
20409
+ */
20410
+ function generateCurrentSessionId(visitorId) {
20411
+ return generateSessionId(visitorId, new Date());
20412
+ }
20413
+
20399
20414
  /**
20400
20415
  * Visitor Persistence Manager for SDK
20401
20416
  * Implements client-side visitor identification with localStorage camouflage
@@ -20555,9 +20570,23 @@ class VisitorIdentityManager {
20555
20570
  // REMOVED: generateVisitorId() and generateVisitorIdFromHash() methods
20556
20571
  // SDK no longer generates visitor IDs - only backend does
20557
20572
  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);
20573
+ // Try to get existing visitor identity from storage
20574
+ const { value: storedData } = PersistenceManager.get(STORAGE_KEYS.prefs);
20575
+ if (storedData) {
20576
+ try {
20577
+ const parsed = JSON.parse(storedData);
20578
+ if (parsed._v && parsed._vb === true) {
20579
+ // We have a valid visitor ID from backend
20580
+ return generateCurrentSessionId(parsed._v);
20581
+ }
20582
+ }
20583
+ catch (e) {
20584
+ // Invalid data, continue to fallback
20585
+ }
20586
+ }
20587
+ // Fallback for cases where visitor ID is not available yet
20588
+ const tempVisitorId = "temp-" + Date.now().toString(36);
20589
+ return generateCurrentSessionId(tempVisitorId);
20561
20590
  }
20562
20591
  createCamouflageData(visitorId, sessionId, stableCoreHash, fromBackend = false) {
20563
20592
  return {
@@ -20567,7 +20596,7 @@ class VisitorIdentityManager {
20567
20596
  analytics: true,
20568
20597
  cookies: true,
20569
20598
  _v: visitorId, // Hidden visitor ID
20570
- _s: sessionId, // Hidden session ID
20599
+ _s: sessionId, // Hidden session ID
20571
20600
  _sc: stableCoreHash, // Hidden stable core
20572
20601
  _vb: fromBackend ? true : undefined, // Flag: visitor ID from backend (v1.7.0+)
20573
20602
  ts: Date.now(),
@@ -20611,7 +20640,9 @@ class VisitorIdentityManager {
20611
20640
  const { value: storedData, method } = PersistenceManager.get(STORAGE_KEYS.prefs);
20612
20641
  if (storedData) {
20613
20642
  const existingIdentity = this.extractFromCamouflage(storedData);
20614
- if (existingIdentity && existingIdentity.stableCoreHash === stableCoreHash && existingIdentity.visitorId) {
20643
+ if (existingIdentity &&
20644
+ existingIdentity.stableCoreHash === stableCoreHash &&
20645
+ existingIdentity.visitorId) {
20615
20646
  // Update last seen time only if we have a valid visitor ID from backend
20616
20647
  const updatedCamouflage = this.createCamouflageData(existingIdentity.visitorId, existingIdentity.sessionId, stableCoreHash);
20617
20648
  PersistenceManager.set(STORAGE_KEYS.prefs, JSON.stringify(updatedCamouflage));
@@ -20631,7 +20662,7 @@ class VisitorIdentityManager {
20631
20662
  stableCoreHash,
20632
20663
  deviceFingerprint: params.deviceFingerprint,
20633
20664
  persistenceMethod: "memory", // Start with memory, upgrade after backend response
20634
- confidence: 0.90,
20665
+ confidence: 0.9,
20635
20666
  createdAt: Date.now(),
20636
20667
  lastSeen: Date.now(),
20637
20668
  reused: false,
@@ -20646,7 +20677,12 @@ class VisitorIdentityManager {
20646
20677
  async updateVisitorIdFromBackend(visitorId, stableCoreHash, sessionId) {
20647
20678
  try {
20648
20679
  // Create or update camouflaged data with backend visitor ID
20649
- const currentSessionId = sessionId || this.getCurrentSessionId() || this.generateSessionId();
20680
+ // Regenerate session ID with proper visitor ID if needed
20681
+ let currentSessionId = sessionId || this.getCurrentSessionId();
20682
+ // If we have a temp session ID or no session ID, generate a new one with the proper visitor ID
20683
+ if (!currentSessionId || currentSessionId.includes("temp-")) {
20684
+ currentSessionId = generateCurrentSessionId(visitorId);
20685
+ }
20650
20686
  const camouflageData = this.createCamouflageData(visitorId, currentSessionId, stableCoreHash, true // Mark as coming from backend
20651
20687
  );
20652
20688
  const { success, method } = PersistenceManager.set(STORAGE_KEYS.prefs, JSON.stringify(camouflageData));
@@ -20658,7 +20694,7 @@ class VisitorIdentityManager {
20658
20694
  return false;
20659
20695
  }
20660
20696
  catch (error) {
20661
- console.error('[VisitorIdentityManager] Failed to update visitor ID from backend:', error);
20697
+ console.error("[VisitorIdentityManager] Failed to update visitor ID from backend:", error);
20662
20698
  return false;
20663
20699
  }
20664
20700
  }
@@ -21112,36 +21148,17 @@ class ZaplierSDK {
21112
21148
  }
21113
21149
  }
21114
21150
  /**
21115
- * Generate session ID
21151
+ * Generate session ID using standardized format
21116
21152
  */
21117
21153
  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;
21154
+ // Use centralized session generation with current visitor ID
21155
+ if (this.backendVisitorId) {
21156
+ return generateCurrentSessionId(this.backendVisitorId);
21143
21157
  }
21144
- return Math.abs(hash).toString(36);
21158
+ // Fallback for cases where backendVisitorId is not available yet
21159
+ // This should be rare and will be updated when visitor ID is available
21160
+ const tempVisitorId = this.visitorId || 'temp-' + Date.now().toString(36);
21161
+ return generateCurrentSessionId(tempVisitorId);
21145
21162
  }
21146
21163
  /**
21147
21164
  * Initialize Anti-Adblock Manager
@@ -21400,6 +21417,10 @@ class ZaplierSDK {
21400
21417
  if (response.sessionId) {
21401
21418
  this.sessionId = response.sessionId;
21402
21419
  }
21420
+ else if (this.backendVisitorId && (!this.sessionId || this.sessionId.includes('temp-'))) {
21421
+ // Generate new session ID now that we have the proper visitor ID
21422
+ this.sessionId = generateCurrentSessionId(this.backendVisitorId);
21423
+ }
21403
21424
  return response;
21404
21425
  }
21405
21426
  catch (error) {