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