@zaplier/sdk 1.1.1 → 1.1.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
@@ -5137,46 +5137,47 @@ async function collectFingerprint(options = {}) {
5137
5137
  const parallelTasks = [];
5138
5138
  // Group 1: Independent rendering components (can run in parallel)
5139
5139
  if (shouldIncludeComponent("canvas", opts) && isCanvasAvailable$1()) {
5140
- parallelTasks.push(collectComponent("canvas", getCanvasFingerprint, opts).then(result => ({
5140
+ parallelTasks.push(collectComponent("canvas", getCanvasFingerprint, opts).then((result) => ({
5141
5141
  component: "canvas",
5142
- result
5142
+ result,
5143
5143
  })));
5144
5144
  }
5145
5145
  if (shouldIncludeComponent("webgl", opts) && isWebGLAvailable()) {
5146
- parallelTasks.push(collectComponent("webgl", getWebGLFingerprint, opts).then(result => ({
5146
+ parallelTasks.push(collectComponent("webgl", getWebGLFingerprint, opts).then((result) => ({
5147
5147
  component: "webgl",
5148
- result
5148
+ result,
5149
5149
  })));
5150
5150
  }
5151
5151
  if (shouldIncludeComponent("audio", opts) && isAudioAvailable()) {
5152
- parallelTasks.push(collectComponent("audio", getAudioFingerprint, opts).then(result => ({
5152
+ parallelTasks.push(collectComponent("audio", getAudioFingerprint, opts).then((result) => ({
5153
5153
  component: "audio",
5154
- result
5154
+ result,
5155
5155
  })));
5156
5156
  }
5157
5157
  // Group 2: Fast synchronous components (can run in parallel)
5158
5158
  if (shouldIncludeComponent("fonts", opts) && isFontDetectionAvailable()) {
5159
- parallelTasks.push(collectComponent("fonts", () => getFontFingerprint(!opts.gdprMode), opts).then(result => ({
5159
+ parallelTasks.push(collectComponent("fonts", () => getFontFingerprint(!opts.gdprMode), opts).then((result) => ({
5160
5160
  component: "fonts",
5161
- result
5161
+ result,
5162
5162
  })));
5163
5163
  }
5164
5164
  if (shouldIncludeComponent("screen", opts) && isScreenAvailable()) {
5165
- parallelTasks.push(collectComponent("screen", getScreenFingerprint, opts).then(result => ({
5165
+ parallelTasks.push(collectComponent("screen", getScreenFingerprint, opts).then((result) => ({
5166
5166
  component: "screen",
5167
- result
5167
+ result,
5168
5168
  })));
5169
5169
  }
5170
- if (shouldIncludeComponent("browser", opts) && isBrowserFingerprintingAvailable()) {
5171
- parallelTasks.push(collectComponent("browser", getBrowserFingerprint, opts).then(result => ({
5170
+ if (shouldIncludeComponent("browser", opts) &&
5171
+ isBrowserFingerprintingAvailable()) {
5172
+ parallelTasks.push(collectComponent("browser", getBrowserFingerprint, opts).then((result) => ({
5172
5173
  component: "browser",
5173
- result
5174
+ result,
5174
5175
  })));
5175
5176
  }
5176
5177
  // Execute all parallel tasks and handle results
5177
5178
  const parallelResults = await Promise.allSettled(parallelTasks);
5178
5179
  parallelResults.forEach((promiseResult, index) => {
5179
- if (promiseResult.status === 'fulfilled' && promiseResult.value.result) {
5180
+ if (promiseResult.status === "fulfilled" && promiseResult.value.result) {
5180
5181
  const { component, result } = promiseResult.value;
5181
5182
  fingerprintData[component] = result;
5182
5183
  collectedComponents.push(component);
@@ -5184,10 +5185,10 @@ async function collectFingerprint(options = {}) {
5184
5185
  else {
5185
5186
  // Extract component name from the corresponding task
5186
5187
  const taskComponent = parallelTasks[index];
5187
- const componentName = taskComponent?.toString().match(/component: "(\w+)"/)?.[1] || 'unknown';
5188
+ const componentName = taskComponent?.toString().match(/component: "(\w+)"/)?.[1] || "unknown";
5188
5189
  failedComponents.push({
5189
5190
  component: componentName,
5190
- error: promiseResult.status === 'rejected'
5191
+ error: promiseResult.status === "rejected"
5191
5192
  ? promiseResult.reason?.message || "Promise rejected"
5192
5193
  : "Collection failed or timeout",
5193
5194
  });
@@ -5235,7 +5236,7 @@ async function collectFingerprint(options = {}) {
5235
5236
  const fallbackHardware = {
5236
5237
  hardwareConcurrency: navigator.hardwareConcurrency || 0,
5237
5238
  deviceMemory: navigator.deviceMemory || 0,
5238
- platform: navigator.platform || 'unknown'
5239
+ platform: navigator.platform || "unknown",
5239
5240
  };
5240
5241
  fingerprintData.hardware = {
5241
5242
  value: fallbackHardware,
@@ -5702,146 +5703,31 @@ async function collectFingerprint(options = {}) {
5702
5703
  platform: (browser?.platform || "unknown").toLowerCase(),
5703
5704
  // PRIORITY 5: Device type (critical for differentiation, ultra-stable)
5704
5705
  deviceType,
5705
- // PRIORITY 6: Enhanced screen resolution with exact dimensions for mobile differentiation
5706
- // For mobile devices, we NEED exact dimensions to differentiate between similar models (S22 vs A54)
5707
- // For desktop, bucketing is sufficient as hardware varies more
5708
- screen: screen
5709
- ? (() => {
5710
- // Use enhanced screen buckets for modern display landscape
5711
- const dims = [screen.width, screen.height].sort((a, b) => b - a);
5712
- const w = dims[0];
5713
- const h = dims[1];
5714
- // Enhanced screen bucketing based on 2024 display standards
5715
- const getScreenBucket = (width, height) => {
5716
- // Ultra-high resolution displays (2024: 8K, 5K, etc.)
5717
- if (width >= 7680)
5718
- return "8k"; // 8K displays
5719
- if (width >= 5120)
5720
- return "5k"; // 5K displays (iMac, etc.)
5721
- if (width >= 4096)
5722
- return "4k_cinema"; // Cinema 4K
5723
- if (width >= 3840)
5724
- return "4k_uhd"; // 4K UHD standard
5725
- if (width >= 3440)
5726
- return "ultrawide"; // Ultrawide QHD
5727
- if (width >= 2880)
5728
- return "retina_4k"; // Retina 4K/5K scaled
5729
- if (width >= 2560)
5730
- return "qhd"; // QHD/WQHD
5731
- // Standard high-resolution displays
5732
- if (width >= 2048)
5733
- return "qxga"; // QXGA
5734
- if (width >= 1920)
5735
- return "fhd"; // Full HD 1080p
5736
- if (width >= 1680)
5737
- return "wsxga+"; // WSXGA+
5738
- if (width >= 1600)
5739
- return "uxga"; // UXGA
5740
- if (width >= 1440)
5741
- return "wxga++"; // WXGA++
5742
- if (width >= 1366)
5743
- return "hd"; // HD 720p
5744
- if (width >= 1280)
5745
- return "sxga"; // SXGA
5746
- // Tablet and mobile displays
5747
- if (width >= 1024)
5748
- return "xga"; // XGA (tablets)
5749
- if (width >= 768)
5750
- return "svga"; // SVGA (small tablets)
5751
- if (width >= 640)
5752
- return "vga"; // VGA (phones)
5753
- if (width >= 480)
5754
- return "hvga"; // HVGA (older phones)
5755
- if (width >= 320)
5756
- return "qvga"; // QVGA (legacy phones)
5757
- return "minimal"; // Sub-QVGA
5758
- };
5759
- // Enhanced aspect ratio classification for device type hints
5760
- const getAspectRatioClass = (width, height) => {
5761
- if (height === 0)
5762
- return "unknown";
5763
- const ratio = width / height;
5764
- // Ultra-wide displays (gaming, productivity)
5765
- if (ratio >= 3.0)
5766
- return "super_ultrawide"; // 32:9+
5767
- if (ratio >= 2.3)
5768
- return "ultrawide"; // 21:9
5769
- if (ratio >= 2.0)
5770
- return "wide"; // 18:9
5771
- // Standard desktop ratios
5772
- if (ratio >= 1.7 && ratio <= 1.8)
5773
- return "standard"; // 16:9
5774
- if (ratio >= 1.6 && ratio <= 1.67)
5775
- return "classic"; // 16:10, 5:3
5776
- if (ratio >= 1.3 && ratio <= 1.35)
5777
- return "traditional"; // 4:3
5778
- // Mobile/portrait ratios
5779
- if (ratio >= 0.5 && ratio <= 0.8)
5780
- return "mobile_portrait"; // Phones in portrait
5781
- if (ratio >= 0.4 && ratio <= 0.5)
5782
- return "tall_mobile"; // Modern tall phones
5783
- return "custom"; // Non-standard ratio
5784
- };
5785
- // Ensure w and h are valid numbers
5786
- const width = w || 0;
5787
- const height = h || 0;
5788
- // Screen density class based on common DPR patterns
5789
- const getDensityClass = (pixelRatio) => {
5790
- if (!pixelRatio)
5791
- return "standard";
5792
- if (pixelRatio >= 3.0)
5793
- return "ultra_high"; // iPhone Plus, high-end Android
5794
- if (pixelRatio >= 2.0)
5795
- return "high"; // Retina, standard high-DPI
5796
- if (pixelRatio >= 1.5)
5797
- return "medium"; // Mid-range displays
5798
- if (pixelRatio >= 1.25)
5799
- return "enhanced"; // Slightly enhanced DPI
5800
- return "standard"; // Standard 1x displays
5801
- };
5802
- // CRITICAL: Do NOT include exact dimensions in stableCoreVector for mobile!
5803
- // Reason: Screen dimensions change when:
5804
- // - Chrome address bar appears/disappears (scroll behavior)
5805
- // - Virtual keyboard opens/closes
5806
- // - Screen rotates (portrait ↔ landscape)
5807
- // - Browser zoom changes
5808
- // These changes would cause stableCoreHash to change, breaking visitor identification
5809
- //
5810
- // INSTEAD: Use ONLY stable screen characteristics (bucket, aspect, density)
5811
- // For device differentiation (S22 vs A54), use:
5812
- // - screenFrame (bezel measurements)
5813
- // - Combination of bucket + hardware + display
5814
- // - Vector similarity in IdentityResolver
5815
- return {
5816
- bucket: getScreenBucket(width),
5817
- aspectClass: getAspectRatioClass(width, height),
5818
- densityClass: getDensityClass(screen.pixelRatio || window.devicePixelRatio),
5819
- // Simplified ratio for matching (rounded to prevent micro-variations)
5820
- ratio: height > 0 ? Math.round((width / height) * 100) / 100 : 0,
5821
- // Size category for general device classification
5822
- sizeCategory: width >= 2560
5823
- ? "large"
5824
- : width >= 1920
5825
- ? "desktop"
5826
- : width >= 1024
5827
- ? "laptop"
5828
- : width >= 768
5829
- ? "tablet"
5830
- : "mobile",
5831
- // ❌ REMOVED: exact dimensions (causes instability on mobile)
5832
- // Device differentiation is now handled by:
5833
- // 1. screenFrame (bezel measurements - stable)
5834
- // 2. hardware + display combination
5835
- // 3. Vector similarity in IdentityResolver
5836
- };
5837
- })()
5838
- : {
5839
- bucket: "unknown",
5840
- aspectClass: "unknown",
5841
- densityClass: "unknown",
5842
- ratio: 0,
5843
- sizeCategory: "unknown",
5844
- },
5706
+ // PRIORITY 6: Screen - REMOVED FROM stableCoreVector
5707
+ // REASON: Screen properties are UNSTABLE across all device types:
5708
+ //
5709
+ // Desktop:
5710
+ // - DevTools changes viewport (lateral vs bottom)
5711
+ // - Browser zoom changes dimensions
5712
+ // - Window resize changes available space
5713
+ //
5714
+ // Mobile:
5715
+ // - Address bar appears/disappears on scroll
5716
+ // - Virtual keyboard opens/closes
5717
+ // - Screen rotation (portrait landscape)
5718
+ // - Pull-to-refresh changes viewport
5719
+ //
5720
+ // RESULT: Even "bucket" detection is unreliable due to viewport changes
5721
+ //
5722
+ // SOLUTION: Device differentiation now relies ONLY on:
5723
+ // 1. screenFrame (bezel measurements - MORE stable)
5724
+ // 2. hardware (cores, memory, arch - STABLE)
5725
+ // 3. display (color depth, gamut - STABLE)
5726
+ // 4. platform + deviceType + ua (ULTRA-STABLE)
5727
+ // 5. timezone + primaryLanguage (USER-STABLE)
5728
+ //
5729
+ // Note: screen is still collected in full fingerprint for analytics,
5730
+ // but excluded from stableCoreHash to ensure visitor identification stability
5845
5731
  // CRITICAL: Do NOT include availability flags (canvas/webgl/audio) in stableCoreVector
5846
5732
  // These flags can change between requests due to timeouts, permissions, or hardware issues
5847
5733
  // and would cause the stableCoreHash to change, breaking visitor identification
@@ -6032,12 +5918,13 @@ async function collectFingerprint(options = {}) {
6032
5918
  };
6033
5919
  // ADDITIONAL ENTROPY: Add a deterministic but unique component based on ultra-stable signals
6034
5920
  // This helps prevent collisions while maintaining deterministic behavior
5921
+ // Note: screen removed as it's unstable (viewport changes)
6035
5922
  const entropyComponents = [
6036
5923
  coreVector.ua || "",
6037
5924
  coreVector.platform || "",
6038
5925
  coreVector.deviceType || "",
6039
- coreVector.screen?.bucket || "",
6040
5926
  coreVector.timezone || "",
5927
+ coreVector.primaryLanguage || "",
6041
5928
  ].filter(Boolean);
6042
5929
  if (entropyComponents.length >= 3) {
6043
5930
  // Create a stable entropy string from the most stable components
@@ -6062,15 +5949,19 @@ async function collectFingerprint(options = {}) {
6062
5949
  // - Screen frame (can vary slightly)
6063
5950
  // - Vendor flavors (can change)
6064
5951
  // - Exact hardware values (use buckets)
6065
- // DEBUG: Log coreVector components to identify DevTools instability
6066
- if (opts.debug || (typeof window !== 'undefined' && window._rabbitTrackerDebug)) {
6067
- console.log('[RabbitTracker DEBUG] coreVector components:', {
5952
+ // DEBUG: Log coreVector components for monitoring
5953
+ if (opts.debug ||
5954
+ (typeof window !== "undefined" && window._rabbitTrackerDebug)) {
5955
+ console.log("[RabbitTracker DEBUG] coreVector components:", {
6068
5956
  ua: coreVector.ua,
6069
5957
  platform: coreVector.platform,
6070
5958
  deviceType: coreVector.deviceType,
6071
- screenBucket: coreVector.screen?.bucket,
6072
5959
  timezone: coreVector.timezone,
6073
- _entropy: coreVector._entropy
5960
+ primaryLanguage: coreVector.primaryLanguage,
5961
+ hasHardware: !!coreVector.hardware,
5962
+ hasDisplay: !!coreVector.display,
5963
+ hasScreenFrame: !!coreVector.screenFrame,
5964
+ _entropy: coreVector._entropy,
6074
5965
  });
6075
5966
  }
6076
5967
  // Generate ultra-stable hash with additional collision resistance
@@ -6115,7 +6006,10 @@ async function collectFingerprint(options = {}) {
6115
6006
  ua: coreVector.ua,
6116
6007
  deviceType: coreVector.deviceType,
6117
6008
  platform: coreVector.platform,
6118
- screenBucket: coreVector.screen?.bucket,
6009
+ timezone: coreVector.timezone,
6010
+ hasScreenFrame: !!coreVector.screenFrame,
6011
+ hardwareClass: coreVector.hardware?.class,
6012
+ displayClass: coreVector.display?.class,
6119
6013
  totalComponentsInHash: Object.keys(enhancedComponentValues).length,
6120
6014
  });
6121
6015
  }