@zaplier/sdk 1.0.5 → 1.0.7
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 +112 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.esm.js +112 -27
- package/dist/index.esm.js.map +1 -1
- package/dist/sdk.js +112 -27
- package/dist/sdk.js.map +1 -1
- package/dist/sdk.min.js +1 -1
- package/dist/src/modules/fingerprint/hashing.d.ts +2 -2
- package/dist/src/modules/fingerprint/hashing.d.ts.map +1 -1
- package/dist/src/modules/fingerprint.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/sdk.js
CHANGED
|
@@ -157,8 +157,12 @@
|
|
|
157
157
|
h1 = x64Add(h1, h2);
|
|
158
158
|
h2 = x64Add(h2, h1);
|
|
159
159
|
// Convert to hex string
|
|
160
|
-
|
|
161
|
-
|
|
160
|
+
// CRITICAL: Use >>> 0 to convert to unsigned 32-bit integer before converting to hex
|
|
161
|
+
// This prevents negative numbers from generating hashes with minus signs (e.g., "-6b43973f")
|
|
162
|
+
const hex1 = (h1[0] >>> 0).toString(16).padStart(8, "0") +
|
|
163
|
+
(h1[1] >>> 0).toString(16).padStart(8, "0");
|
|
164
|
+
const hex2 = (h2[0] >>> 0).toString(16).padStart(8, "0") +
|
|
165
|
+
(h2[1] >>> 0).toString(16).padStart(8, "0");
|
|
162
166
|
return hex1 + hex2;
|
|
163
167
|
}
|
|
164
168
|
/**
|
|
@@ -178,13 +182,26 @@
|
|
|
178
182
|
/**
|
|
179
183
|
* Hash fingerprint components into a stable identifier
|
|
180
184
|
*/
|
|
181
|
-
function hashFingerprint(components) {
|
|
185
|
+
function hashFingerprint(components, debug = false) {
|
|
182
186
|
// Convert components to canonical string representation using deep sorting
|
|
183
187
|
// CRITICAL: Do NOT use JSON.stringify(obj, keys) as it filters out nested properties!
|
|
184
188
|
// Use canonicalizeStable instead which handles deep sorting and normalization.
|
|
185
|
-
const
|
|
189
|
+
const canonicalized = canonicalizeStable(components);
|
|
190
|
+
const canonical = JSON.stringify(canonicalized);
|
|
191
|
+
if (debug) {
|
|
192
|
+
console.log("[RabbitTracker] hashFingerprint debug:", {
|
|
193
|
+
originalKeys: Object.keys(components),
|
|
194
|
+
canonicalizedKeys: Object.keys(canonicalized),
|
|
195
|
+
canonicalLength: canonical.length,
|
|
196
|
+
sample: canonical.substring(0, 200) + "...",
|
|
197
|
+
});
|
|
198
|
+
}
|
|
186
199
|
// Use MurmurHash3 x64 for consistent hashing
|
|
187
|
-
|
|
200
|
+
const hash = x64hash128(canonical);
|
|
201
|
+
if (debug) {
|
|
202
|
+
console.log("[RabbitTracker] Generated fingerprintHash:", hash.substring(0, 32));
|
|
203
|
+
}
|
|
204
|
+
return hash;
|
|
188
205
|
}
|
|
189
206
|
/**
|
|
190
207
|
* Generate a visitor ID from fingerprint hash
|
|
@@ -193,8 +210,33 @@
|
|
|
193
210
|
// Use first 16 characters of the hash for visitor ID
|
|
194
211
|
return "vis_" + fingerprintHash.substring(0, 16);
|
|
195
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* Check if a key represents an unstable component that should be excluded from hashing
|
|
215
|
+
*/
|
|
216
|
+
function isUnstableKey(key) {
|
|
217
|
+
// Unstable keys that change between requests:
|
|
218
|
+
// - duration: collection time varies
|
|
219
|
+
// - timestamp: changes every request
|
|
220
|
+
// - error: error messages can vary
|
|
221
|
+
// - errors: error arrays can vary
|
|
222
|
+
// - collectionTime: varies per request
|
|
223
|
+
const unstableKeys = [
|
|
224
|
+
"duration",
|
|
225
|
+
"timestamp",
|
|
226
|
+
"error",
|
|
227
|
+
"errors",
|
|
228
|
+
"collectionTime",
|
|
229
|
+
"startTime",
|
|
230
|
+
"endTime",
|
|
231
|
+
"_timestamp", // internal timestamp fields
|
|
232
|
+
];
|
|
233
|
+
return (unstableKeys.includes(key) ||
|
|
234
|
+
key.endsWith("_timestamp") ||
|
|
235
|
+
key.endsWith("Timestamp"));
|
|
236
|
+
}
|
|
196
237
|
/**
|
|
197
238
|
* Canonicalize an object deeply with sorted keys and normalized primitives
|
|
239
|
+
* CRITICAL: Filters out unstable components that vary between requests
|
|
198
240
|
*/
|
|
199
241
|
function canonicalizeStable(input) {
|
|
200
242
|
if (input === null || input === undefined)
|
|
@@ -209,6 +251,10 @@
|
|
|
209
251
|
const keys = Object.keys(input).sort();
|
|
210
252
|
const out = {};
|
|
211
253
|
for (const k of keys) {
|
|
254
|
+
// CRITICAL: Skip unstable keys that cause hash instability
|
|
255
|
+
if (isUnstableKey(k)) {
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
212
258
|
const v = canonicalizeStable(input[k]);
|
|
213
259
|
if (v !== null)
|
|
214
260
|
out[k] = v;
|
|
@@ -229,9 +275,22 @@
|
|
|
229
275
|
/**
|
|
230
276
|
* Compute stable core hash from a minimal, invariant vector
|
|
231
277
|
*/
|
|
232
|
-
function hashStableCore(coreVector) {
|
|
233
|
-
const
|
|
234
|
-
|
|
278
|
+
function hashStableCore(coreVector, debug = false) {
|
|
279
|
+
const canonicalized = canonicalizeStable(coreVector);
|
|
280
|
+
const canonical = JSON.stringify(canonicalized);
|
|
281
|
+
if (debug) {
|
|
282
|
+
console.log("[RabbitTracker] hashStableCore debug:", {
|
|
283
|
+
originalKeys: Object.keys(coreVector),
|
|
284
|
+
canonicalizedKeys: Object.keys(canonicalized),
|
|
285
|
+
canonicalLength: canonical.length,
|
|
286
|
+
sample: canonical.substring(0, 200) + "...",
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
const hash = x64hash128(canonical);
|
|
290
|
+
if (debug) {
|
|
291
|
+
console.log("[RabbitTracker] Generated stableCoreHash:", hash.substring(0, 32));
|
|
292
|
+
}
|
|
293
|
+
return hash;
|
|
235
294
|
}
|
|
236
295
|
|
|
237
296
|
/**
|
|
@@ -4942,7 +5001,7 @@
|
|
|
4942
5001
|
"system",
|
|
4943
5002
|
].includes(componentType);
|
|
4944
5003
|
// Reduce retries for WebGL to prevent context leaks
|
|
4945
|
-
const maxRetries = isWebGLComponent ? 2 :
|
|
5004
|
+
const maxRetries = isWebGLComponent ? 2 : isCriticalComponent ? 3 : 1;
|
|
4946
5005
|
const retryDelays = isWebGLComponent ? [200, 500] : [100, 250, 500]; // Longer delays for WebGL
|
|
4947
5006
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
4948
5007
|
try {
|
|
@@ -5574,7 +5633,9 @@
|
|
|
5574
5633
|
// Fallback to legacy detection if imports fail
|
|
5575
5634
|
const ua = uaStr;
|
|
5576
5635
|
const m = ua.match(/(chrome|safari|firefox|edge|edg|brave|opera|opr|chromium)/i);
|
|
5577
|
-
const family = (m?.[1] ||
|
|
5636
|
+
const family = (m?.[1] ||
|
|
5637
|
+
ua.split(" ")[0] ||
|
|
5638
|
+
"unknown").toLowerCase();
|
|
5578
5639
|
return family;
|
|
5579
5640
|
}
|
|
5580
5641
|
})(),
|
|
@@ -5582,8 +5643,10 @@
|
|
|
5582
5643
|
timezone: browser?.timezone || "unknown",
|
|
5583
5644
|
// PRIORITY 3: Primary language only (most stable language preference)
|
|
5584
5645
|
// Remove secondary languages as they can change more frequently
|
|
5585
|
-
primaryLanguage: Array.isArray(browser?.languages) &&
|
|
5586
|
-
|
|
5646
|
+
primaryLanguage: Array.isArray(browser?.languages) &&
|
|
5647
|
+
browser.languages.length > 0 &&
|
|
5648
|
+
browser.languages[0]
|
|
5649
|
+
? browser.languages[0].toLowerCase().split("-")[0] // Only language code (en, pt, fr), not region
|
|
5587
5650
|
: "unknown",
|
|
5588
5651
|
// PRIORITY 4: Platform (ultra-stable - OS doesn't change)
|
|
5589
5652
|
platform: (browser?.platform || "unknown").toLowerCase(),
|
|
@@ -5691,11 +5754,24 @@
|
|
|
5691
5754
|
// Simplified ratio for matching (rounded to prevent micro-variations)
|
|
5692
5755
|
ratio: height > 0 ? Math.round((width / height) * 100) / 100 : 0,
|
|
5693
5756
|
// Size category for general device classification
|
|
5694
|
-
sizeCategory: width >= 2560
|
|
5695
|
-
|
|
5757
|
+
sizeCategory: width >= 2560
|
|
5758
|
+
? "large"
|
|
5759
|
+
: width >= 1920
|
|
5760
|
+
? "desktop"
|
|
5761
|
+
: width >= 1024
|
|
5762
|
+
? "laptop"
|
|
5763
|
+
: width >= 768
|
|
5764
|
+
? "tablet"
|
|
5765
|
+
: "mobile",
|
|
5696
5766
|
};
|
|
5697
5767
|
})()
|
|
5698
|
-
: {
|
|
5768
|
+
: {
|
|
5769
|
+
bucket: "unknown",
|
|
5770
|
+
aspectClass: "unknown",
|
|
5771
|
+
densityClass: "unknown",
|
|
5772
|
+
ratio: 0,
|
|
5773
|
+
sizeCategory: "unknown",
|
|
5774
|
+
},
|
|
5699
5775
|
// CRITICAL: Do NOT include availability flags (canvas/webgl/audio) in stableCoreVector
|
|
5700
5776
|
// These flags can change between requests due to timeouts, permissions, or hardware issues
|
|
5701
5777
|
// and would cause the stableCoreHash to change, breaking visitor identification
|
|
@@ -5806,10 +5882,13 @@
|
|
|
5806
5882
|
arch: archBucket,
|
|
5807
5883
|
class: getHardwareClass(coresBucket, memoryBucket),
|
|
5808
5884
|
// Touch capability for mobile/desktop differentiation
|
|
5809
|
-
touch: deviceSignals$1.touchSupport?.maxTouchPoints
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
|
|
5885
|
+
touch: deviceSignals$1.touchSupport?.maxTouchPoints
|
|
5886
|
+
? deviceSignals$1.touchSupport.maxTouchPoints >= 10
|
|
5887
|
+
? "multi"
|
|
5888
|
+
: deviceSignals$1.touchSupport.maxTouchPoints >= 5
|
|
5889
|
+
? "standard"
|
|
5890
|
+
: "basic"
|
|
5891
|
+
: "none",
|
|
5813
5892
|
};
|
|
5814
5893
|
})(),
|
|
5815
5894
|
// PRIORITY 8: Enhanced color and display capabilities bucketing
|
|
@@ -5835,10 +5914,14 @@
|
|
|
5835
5914
|
if (!gamut)
|
|
5836
5915
|
return "unknown";
|
|
5837
5916
|
switch (gamut) {
|
|
5838
|
-
case "rec2020":
|
|
5839
|
-
|
|
5840
|
-
case "
|
|
5841
|
-
|
|
5917
|
+
case "rec2020":
|
|
5918
|
+
return "professional"; // Professional/HDR displays
|
|
5919
|
+
case "p3":
|
|
5920
|
+
return "enhanced"; // Modern displays (Apple, etc.)
|
|
5921
|
+
case "srgb":
|
|
5922
|
+
return "standard"; // Standard displays
|
|
5923
|
+
default:
|
|
5924
|
+
return "unknown";
|
|
5842
5925
|
}
|
|
5843
5926
|
};
|
|
5844
5927
|
// Combined display quality classification
|
|
@@ -5862,7 +5945,7 @@
|
|
|
5862
5945
|
return {
|
|
5863
5946
|
depth: depthClass,
|
|
5864
5947
|
gamut: gamutClass,
|
|
5865
|
-
class: getDisplayClass(depthClass, gamutClass)
|
|
5948
|
+
class: getDisplayClass(depthClass, gamutClass),
|
|
5866
5949
|
};
|
|
5867
5950
|
})(),
|
|
5868
5951
|
};
|
|
@@ -5881,7 +5964,9 @@
|
|
|
5881
5964
|
// Add a simple checksum for additional uniqueness (deterministic)
|
|
5882
5965
|
let checksum = 0;
|
|
5883
5966
|
for (let i = 0; i < entropyString.length; i++) {
|
|
5884
|
-
checksum =
|
|
5967
|
+
checksum =
|
|
5968
|
+
((checksum << 5) - checksum + entropyString.charCodeAt(i)) &
|
|
5969
|
+
0xffffffff;
|
|
5885
5970
|
}
|
|
5886
5971
|
coreVector._entropy = Math.abs(checksum).toString(36).substring(0, 8);
|
|
5887
5972
|
}
|
|
@@ -5897,7 +5982,7 @@
|
|
|
5897
5982
|
// - Vendor flavors (can change)
|
|
5898
5983
|
// - Exact hardware values (use buckets)
|
|
5899
5984
|
// Generate ultra-stable hash with additional collision resistance
|
|
5900
|
-
const stableCoreHash = hashStableCore(coreVector);
|
|
5985
|
+
const stableCoreHash = hashStableCore(coreVector, opts.debug);
|
|
5901
5986
|
// CRITICAL: Generate enhanced fingerprint hash combining unstable + stable components
|
|
5902
5987
|
// This prevents collisions by adding stable entropy to the fingerprint hash
|
|
5903
5988
|
const enhancedComponentValues = {
|
|
@@ -5906,7 +5991,7 @@
|
|
|
5906
5991
|
// REMOVED: _hourlyEntropy - was causing visitor ID instability (same user = different IDs)
|
|
5907
5992
|
};
|
|
5908
5993
|
// Generate collision-resistant fingerprint hash
|
|
5909
|
-
const enhancedHash = hashFingerprint(enhancedComponentValues);
|
|
5994
|
+
const enhancedHash = hashFingerprint(enhancedComponentValues, opts.debug);
|
|
5910
5995
|
// Update finalData with the enhanced hash
|
|
5911
5996
|
finalData.hash = enhancedHash;
|
|
5912
5997
|
// Log stable core components for debugging collision issues
|