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