@zaplier/sdk 1.6.1 → 1.6.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 +324 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.esm.js +324 -3
- package/dist/index.esm.js.map +1 -1
- package/dist/sdk.js +324 -3
- package/dist/sdk.js.map +1 -1
- package/dist/sdk.min.js +1 -1
- package/dist/src/modules/fingerprint/hashing.d.ts +1 -0
- package/dist/src/modules/fingerprint/hashing.d.ts.map +1 -1
- package/dist/src/modules/visitor-persistence.d.ts +21 -88
- package/dist/src/modules/visitor-persistence.d.ts.map +1 -1
- package/dist/src/sdk.d.ts +2 -1
- package/dist/src/sdk.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -203,10 +203,19 @@ function hashFingerprint(components, debug = false) {
|
|
|
203
203
|
}
|
|
204
204
|
/**
|
|
205
205
|
* Generate a visitor ID from fingerprint hash
|
|
206
|
+
* Returns a proper UUID v4 format for database compatibility
|
|
206
207
|
*/
|
|
207
208
|
function generateVisitorId(fingerprintHash) {
|
|
208
|
-
//
|
|
209
|
-
|
|
209
|
+
// Generate UUID v4 format from fingerprint hash
|
|
210
|
+
const hash = fingerprintHash.length >= 32 ? fingerprintHash : (fingerprintHash + fingerprintHash + fingerprintHash + fingerprintHash).substring(0, 32);
|
|
211
|
+
// Extract bytes for UUID construction
|
|
212
|
+
const hex = hash.substring(0, 32);
|
|
213
|
+
// Set version 4 and variant bits according to RFC 4122
|
|
214
|
+
const chars = hex.split('');
|
|
215
|
+
chars[12] = '4'; // Version 4
|
|
216
|
+
chars[16] = (parseInt(chars[16] || '0', 16) & 0x3 | 0x8).toString(16); // Variant 10
|
|
217
|
+
// Format as UUID: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
|
|
218
|
+
return `${chars.slice(0, 8).join('')}-${chars.slice(8, 12).join('')}-${chars.slice(12, 16).join('')}-${chars.slice(16, 20).join('')}-${chars.slice(20, 32).join('')}`;
|
|
210
219
|
}
|
|
211
220
|
/**
|
|
212
221
|
* Check if a key represents an unstable component that should be excluded from hashing
|
|
@@ -19612,6 +19621,292 @@ class AutoTracker {
|
|
|
19612
19621
|
}
|
|
19613
19622
|
}
|
|
19614
19623
|
|
|
19624
|
+
/**
|
|
19625
|
+
* Visitor Persistence Manager for SDK
|
|
19626
|
+
* Implements client-side visitor identification with localStorage camouflage
|
|
19627
|
+
*/
|
|
19628
|
+
// Camuflaged storage keys to avoid detection/blocking
|
|
19629
|
+
const STORAGE_KEYS = {
|
|
19630
|
+
session: "__zp_s", // Session identifier
|
|
19631
|
+
visitor: "__zp_v", // Visitor identifier (camuflado)
|
|
19632
|
+
device: "__zp_d", // Device fingerprint cache
|
|
19633
|
+
prefs: "__zp_p", // User preferences (decoy storage)
|
|
19634
|
+
analytics: "__zp_a", // Analytics preferences (additional decoy)
|
|
19635
|
+
};
|
|
19636
|
+
/**
|
|
19637
|
+
* Multi-layer persistence manager with fallbacks
|
|
19638
|
+
*/
|
|
19639
|
+
class PersistenceManager {
|
|
19640
|
+
// 1. LocalStorage (primary) - Most persistent
|
|
19641
|
+
static setLocal(key, value) {
|
|
19642
|
+
try {
|
|
19643
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
19644
|
+
localStorage.setItem(key, value);
|
|
19645
|
+
return true;
|
|
19646
|
+
}
|
|
19647
|
+
}
|
|
19648
|
+
catch (e) {
|
|
19649
|
+
// Storage disabled/private mode
|
|
19650
|
+
}
|
|
19651
|
+
return false;
|
|
19652
|
+
}
|
|
19653
|
+
static getLocal(key) {
|
|
19654
|
+
try {
|
|
19655
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
19656
|
+
return localStorage.getItem(key);
|
|
19657
|
+
}
|
|
19658
|
+
}
|
|
19659
|
+
catch (e) {
|
|
19660
|
+
// Storage disabled/private mode
|
|
19661
|
+
}
|
|
19662
|
+
return null;
|
|
19663
|
+
}
|
|
19664
|
+
// 2. SessionStorage (secondary) - Session-only
|
|
19665
|
+
static setSession(key, value) {
|
|
19666
|
+
try {
|
|
19667
|
+
if (typeof window !== "undefined" && window.sessionStorage) {
|
|
19668
|
+
sessionStorage.setItem(key, value);
|
|
19669
|
+
return true;
|
|
19670
|
+
}
|
|
19671
|
+
}
|
|
19672
|
+
catch (e) {
|
|
19673
|
+
// Storage disabled/private mode
|
|
19674
|
+
}
|
|
19675
|
+
return false;
|
|
19676
|
+
}
|
|
19677
|
+
static getSession(key) {
|
|
19678
|
+
try {
|
|
19679
|
+
if (typeof window !== "undefined" && window.sessionStorage) {
|
|
19680
|
+
return sessionStorage.getItem(key);
|
|
19681
|
+
}
|
|
19682
|
+
}
|
|
19683
|
+
catch (e) {
|
|
19684
|
+
// Storage disabled/private mode
|
|
19685
|
+
}
|
|
19686
|
+
return null;
|
|
19687
|
+
}
|
|
19688
|
+
// 3. Cookie (tertiary) - Cross-session with expiration
|
|
19689
|
+
static setCookie(key, value, days = 365) {
|
|
19690
|
+
try {
|
|
19691
|
+
if (typeof document !== "undefined") {
|
|
19692
|
+
const expires = new Date(Date.now() + days * 24 * 60 * 60 * 1000).toUTCString();
|
|
19693
|
+
document.cookie = `${key}=${value}; expires=${expires}; path=/; SameSite=Lax`;
|
|
19694
|
+
return true;
|
|
19695
|
+
}
|
|
19696
|
+
}
|
|
19697
|
+
catch (e) {
|
|
19698
|
+
// Cookies disabled
|
|
19699
|
+
}
|
|
19700
|
+
return false;
|
|
19701
|
+
}
|
|
19702
|
+
static getCookie(key) {
|
|
19703
|
+
try {
|
|
19704
|
+
if (typeof document !== "undefined") {
|
|
19705
|
+
const name = key + "=";
|
|
19706
|
+
const decodedCookie = decodeURIComponent(document.cookie);
|
|
19707
|
+
const ca = decodedCookie.split(";");
|
|
19708
|
+
for (let i = 0; i < ca.length; i++) {
|
|
19709
|
+
let c = ca[i];
|
|
19710
|
+
if (c) {
|
|
19711
|
+
while (c.charAt(0) === " ") {
|
|
19712
|
+
c = c.substring(1);
|
|
19713
|
+
}
|
|
19714
|
+
if (c.indexOf(name) === 0) {
|
|
19715
|
+
return c.substring(name.length, c.length);
|
|
19716
|
+
}
|
|
19717
|
+
}
|
|
19718
|
+
}
|
|
19719
|
+
}
|
|
19720
|
+
}
|
|
19721
|
+
catch (e) {
|
|
19722
|
+
// Cookies disabled
|
|
19723
|
+
}
|
|
19724
|
+
return null;
|
|
19725
|
+
}
|
|
19726
|
+
// 4. Memory (fallback) - Current session only
|
|
19727
|
+
static setMemory(key, value) {
|
|
19728
|
+
this.memoryStore.set(key, value);
|
|
19729
|
+
return true;
|
|
19730
|
+
}
|
|
19731
|
+
static getMemory(key) {
|
|
19732
|
+
return this.memoryStore.get(key) || null;
|
|
19733
|
+
}
|
|
19734
|
+
// Multi-layer get with fallbacks
|
|
19735
|
+
static get(key) {
|
|
19736
|
+
// Try localStorage first (most persistent)
|
|
19737
|
+
let value = this.getLocal(key);
|
|
19738
|
+
if (value)
|
|
19739
|
+
return { value, method: "localStorage" };
|
|
19740
|
+
// Try sessionStorage (session-only)
|
|
19741
|
+
value = this.getSession(key);
|
|
19742
|
+
if (value)
|
|
19743
|
+
return { value, method: "sessionStorage" };
|
|
19744
|
+
// Try cookies (cross-session)
|
|
19745
|
+
value = this.getCookie(key);
|
|
19746
|
+
if (value)
|
|
19747
|
+
return { value, method: "cookie" };
|
|
19748
|
+
// Try memory (current session)
|
|
19749
|
+
value = this.getMemory(key);
|
|
19750
|
+
if (value)
|
|
19751
|
+
return { value, method: "memory" };
|
|
19752
|
+
return { value: null, method: "none" };
|
|
19753
|
+
}
|
|
19754
|
+
// Multi-layer set with fallbacks
|
|
19755
|
+
static set(key, value) {
|
|
19756
|
+
// Try localStorage first (most persistent)
|
|
19757
|
+
if (this.setLocal(key, value)) {
|
|
19758
|
+
return { success: true, method: "localStorage" };
|
|
19759
|
+
}
|
|
19760
|
+
// Try sessionStorage (session-only)
|
|
19761
|
+
if (this.setSession(key, value)) {
|
|
19762
|
+
return { success: true, method: "sessionStorage" };
|
|
19763
|
+
}
|
|
19764
|
+
// Try cookies (cross-session)
|
|
19765
|
+
if (this.setCookie(key, value)) {
|
|
19766
|
+
return { success: true, method: "cookie" };
|
|
19767
|
+
}
|
|
19768
|
+
// Fallback to memory (current session)
|
|
19769
|
+
this.setMemory(key, value);
|
|
19770
|
+
return { success: true, method: "memory" };
|
|
19771
|
+
}
|
|
19772
|
+
}
|
|
19773
|
+
PersistenceManager.memoryStore = new Map();
|
|
19774
|
+
/**
|
|
19775
|
+
* Advanced Visitor Identity Manager with Camouflaged Storage
|
|
19776
|
+
*/
|
|
19777
|
+
class VisitorIdentityManager {
|
|
19778
|
+
generateVisitorId() {
|
|
19779
|
+
// Generate UUID v4 format instead of vis_ prefix
|
|
19780
|
+
const bytes = crypto.getRandomValues(new Uint8Array(16));
|
|
19781
|
+
// Set version 4
|
|
19782
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
|
19783
|
+
// Set variant
|
|
19784
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
19785
|
+
const hex = Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
19786
|
+
return `${hex.substr(0, 8)}-${hex.substr(8, 4)}-${hex.substr(12, 4)}-${hex.substr(16, 4)}-${hex.substr(20, 12)}`;
|
|
19787
|
+
}
|
|
19788
|
+
generateVisitorIdFromHash(hash) {
|
|
19789
|
+
// Generate deterministic UUID v4 format from fingerprint hash
|
|
19790
|
+
// This ensures same fingerprint = same visitor ID
|
|
19791
|
+
const hashExtended = hash.length >= 32 ? hash : (hash + hash + hash + hash).substring(0, 32);
|
|
19792
|
+
// Extract hex characters for UUID construction
|
|
19793
|
+
const hex = hashExtended.substring(0, 32);
|
|
19794
|
+
const chars = hex.split('');
|
|
19795
|
+
// Set version 4 and variant bits according to RFC 4122
|
|
19796
|
+
chars[12] = '4'; // Version 4
|
|
19797
|
+
chars[16] = (parseInt(chars[16] || '0', 16) & 0x3 | 0x8).toString(16); // Variant 10
|
|
19798
|
+
// Format as UUID: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
|
|
19799
|
+
return `${chars.slice(0, 8).join('')}-${chars.slice(8, 12).join('')}-${chars.slice(12, 16).join('')}-${chars.slice(16, 20).join('')}-${chars.slice(20, 32).join('')}`;
|
|
19800
|
+
}
|
|
19801
|
+
generateSessionId() {
|
|
19802
|
+
return "ses_" + Array.from(crypto.getRandomValues(new Uint8Array(8)))
|
|
19803
|
+
.map(b => b.toString(16).padStart(2, '0'))
|
|
19804
|
+
.join('') + "_" + Date.now().toString(36);
|
|
19805
|
+
}
|
|
19806
|
+
createCamouflageData(visitorId, sessionId, stableCoreHash) {
|
|
19807
|
+
return {
|
|
19808
|
+
theme: "auto",
|
|
19809
|
+
lang: (navigator.language || "en-US").substring(0, 2),
|
|
19810
|
+
tz: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
19811
|
+
analytics: true,
|
|
19812
|
+
cookies: true,
|
|
19813
|
+
_v: visitorId, // Hidden visitor ID
|
|
19814
|
+
_s: sessionId, // Hidden session ID
|
|
19815
|
+
_sc: stableCoreHash, // Hidden stable core
|
|
19816
|
+
ts: Date.now(),
|
|
19817
|
+
};
|
|
19818
|
+
}
|
|
19819
|
+
extractFromCamouflage(data) {
|
|
19820
|
+
try {
|
|
19821
|
+
const parsed = JSON.parse(data);
|
|
19822
|
+
if (parsed._v && parsed._s && parsed._sc) {
|
|
19823
|
+
return {
|
|
19824
|
+
visitorId: parsed._v,
|
|
19825
|
+
sessionId: parsed._s,
|
|
19826
|
+
stableCoreHash: parsed._sc,
|
|
19827
|
+
persistenceMethod: "localStorage",
|
|
19828
|
+
confidence: 0.95,
|
|
19829
|
+
createdAt: parsed.ts || Date.now(),
|
|
19830
|
+
lastSeen: Date.now(),
|
|
19831
|
+
reused: true,
|
|
19832
|
+
};
|
|
19833
|
+
}
|
|
19834
|
+
}
|
|
19835
|
+
catch (e) {
|
|
19836
|
+
// Invalid data
|
|
19837
|
+
}
|
|
19838
|
+
return null;
|
|
19839
|
+
}
|
|
19840
|
+
async getOrCreateVisitorIdentity(params) {
|
|
19841
|
+
const { stableCoreHash } = params;
|
|
19842
|
+
// Try to recover existing identity from camouflaged storage
|
|
19843
|
+
const { value: storedData, method } = PersistenceManager.get(STORAGE_KEYS.prefs);
|
|
19844
|
+
if (storedData) {
|
|
19845
|
+
const existingIdentity = this.extractFromCamouflage(storedData);
|
|
19846
|
+
if (existingIdentity && existingIdentity.stableCoreHash === stableCoreHash) {
|
|
19847
|
+
// Update last seen time
|
|
19848
|
+
const updatedCamouflage = this.createCamouflageData(existingIdentity.visitorId, existingIdentity.sessionId, stableCoreHash);
|
|
19849
|
+
PersistenceManager.set(STORAGE_KEYS.prefs, JSON.stringify(updatedCamouflage));
|
|
19850
|
+
return {
|
|
19851
|
+
...existingIdentity,
|
|
19852
|
+
persistenceMethod: method,
|
|
19853
|
+
lastSeen: Date.now(),
|
|
19854
|
+
reused: true,
|
|
19855
|
+
};
|
|
19856
|
+
}
|
|
19857
|
+
}
|
|
19858
|
+
// Create new visitor identity (deterministic from fingerprint)
|
|
19859
|
+
// Use stableCoreHash for deterministic visitor ID generation
|
|
19860
|
+
// This ensures same fingerprint = same visitor ID across devices and sessions
|
|
19861
|
+
const deterministicVisitorId = this.generateVisitorIdFromHash(params.stableCoreHash);
|
|
19862
|
+
const newSessionId = params.sessionId || this.generateSessionId();
|
|
19863
|
+
const newIdentity = {
|
|
19864
|
+
visitorId: deterministicVisitorId,
|
|
19865
|
+
sessionId: newSessionId,
|
|
19866
|
+
stableCoreHash,
|
|
19867
|
+
deviceFingerprint: params.deviceFingerprint,
|
|
19868
|
+
persistenceMethod: "localStorage",
|
|
19869
|
+
confidence: 0.90,
|
|
19870
|
+
createdAt: Date.now(),
|
|
19871
|
+
lastSeen: Date.now(),
|
|
19872
|
+
reused: false,
|
|
19873
|
+
};
|
|
19874
|
+
// Store in camouflaged format
|
|
19875
|
+
const camouflageData = this.createCamouflageData(deterministicVisitorId, newSessionId, stableCoreHash);
|
|
19876
|
+
const { method: storageMethod } = PersistenceManager.set(STORAGE_KEYS.prefs, JSON.stringify(camouflageData));
|
|
19877
|
+
newIdentity.persistenceMethod = storageMethod;
|
|
19878
|
+
// Also store device fingerprint cache for faster lookups
|
|
19879
|
+
PersistenceManager.set(STORAGE_KEYS.device, params.deviceFingerprint);
|
|
19880
|
+
return newIdentity;
|
|
19881
|
+
}
|
|
19882
|
+
// Get current visitor ID without creating new one
|
|
19883
|
+
getCurrentVisitorId() {
|
|
19884
|
+
const { value: storedData } = PersistenceManager.get(STORAGE_KEYS.prefs);
|
|
19885
|
+
if (storedData) {
|
|
19886
|
+
const identity = this.extractFromCamouflage(storedData);
|
|
19887
|
+
return identity?.visitorId || null;
|
|
19888
|
+
}
|
|
19889
|
+
return null;
|
|
19890
|
+
}
|
|
19891
|
+
// Clear all stored identity data
|
|
19892
|
+
clearIdentity() {
|
|
19893
|
+
// Remove from all storage layers
|
|
19894
|
+
try {
|
|
19895
|
+
PersistenceManager.setLocal(STORAGE_KEYS.prefs, "");
|
|
19896
|
+
PersistenceManager.setLocal(STORAGE_KEYS.device, "");
|
|
19897
|
+
PersistenceManager.setSession(STORAGE_KEYS.prefs, "");
|
|
19898
|
+
PersistenceManager.setSession(STORAGE_KEYS.device, "");
|
|
19899
|
+
PersistenceManager.setCookie(STORAGE_KEYS.prefs, "", -1); // Expire immediately
|
|
19900
|
+
PersistenceManager.setCookie(STORAGE_KEYS.device, "", -1);
|
|
19901
|
+
PersistenceManager.setMemory(STORAGE_KEYS.prefs, "");
|
|
19902
|
+
PersistenceManager.setMemory(STORAGE_KEYS.device, "");
|
|
19903
|
+
}
|
|
19904
|
+
catch (e) {
|
|
19905
|
+
// Silent fail
|
|
19906
|
+
}
|
|
19907
|
+
}
|
|
19908
|
+
}
|
|
19909
|
+
|
|
19615
19910
|
/**
|
|
19616
19911
|
* Zaplier SDK v1.0.0
|
|
19617
19912
|
* 100% Cookieless Analytics Tracking
|
|
@@ -20047,10 +20342,12 @@ class ZaplierSDK {
|
|
|
20047
20342
|
}
|
|
20048
20343
|
/**
|
|
20049
20344
|
* Collect fingerprint and generate visitor ID (IMPROVED - 2024)
|
|
20050
|
-
* Enhanced with better incognito detection
|
|
20345
|
+
* Enhanced with better incognito detection and localStorage persistence
|
|
20051
20346
|
*/
|
|
20052
20347
|
async collectFingerprint() {
|
|
20053
20348
|
try {
|
|
20349
|
+
// Initialize visitor identity manager
|
|
20350
|
+
this.visitorIdentityManager = new VisitorIdentityManager();
|
|
20054
20351
|
// Use appropriate fingerprinting based on GDPR mode
|
|
20055
20352
|
const result = this.config.gdprMode
|
|
20056
20353
|
? await getLightweightFingerprint()
|
|
@@ -20068,6 +20365,26 @@ class ZaplierSDK {
|
|
|
20068
20365
|
sdkVersion: this.version,
|
|
20069
20366
|
};
|
|
20070
20367
|
}
|
|
20368
|
+
// Generate or retrieve visitor identity using persistent storage
|
|
20369
|
+
const visitorIdentity = await this.visitorIdentityManager.getOrCreateVisitorIdentity({
|
|
20370
|
+
fingerprintHash: result.data.hash,
|
|
20371
|
+
stableCoreHash: result.data.stableCoreHash,
|
|
20372
|
+
deviceFingerprint: result.data.hash,
|
|
20373
|
+
sessionId: this.sessionId,
|
|
20374
|
+
userAgent: navigator.userAgent,
|
|
20375
|
+
ipAddress: undefined, // Will be determined server-side
|
|
20376
|
+
});
|
|
20377
|
+
// Set the persistent visitor ID
|
|
20378
|
+
this.visitorId = visitorIdentity.visitorId;
|
|
20379
|
+
if (this.config.debug) {
|
|
20380
|
+
console.log("[Zaplier] Visitor identity resolved:", {
|
|
20381
|
+
visitorId: this.visitorId,
|
|
20382
|
+
sessionId: this.sessionId,
|
|
20383
|
+
persistenceMethod: visitorIdentity.persistenceMethod,
|
|
20384
|
+
confidence: visitorIdentity.confidence,
|
|
20385
|
+
isNewVisitor: !visitorIdentity.reused,
|
|
20386
|
+
});
|
|
20387
|
+
}
|
|
20071
20388
|
if (this.config.debug) {
|
|
20072
20389
|
console.log("[Zaplier] Fingerprint collected:", {
|
|
20073
20390
|
components: result.collectedComponents,
|
|
@@ -20174,6 +20491,10 @@ class ZaplierSDK {
|
|
|
20174
20491
|
try {
|
|
20175
20492
|
const payload = {
|
|
20176
20493
|
// workspaceId moved to query parameter
|
|
20494
|
+
// Visitor ID (persistido via localStorage camuflado)
|
|
20495
|
+
visitorId: this.visitorId,
|
|
20496
|
+
// Session ID (gerado por sessão)
|
|
20497
|
+
sessionId: this.sessionId,
|
|
20177
20498
|
fingerprintHash: this.fingerprint?.hash,
|
|
20178
20499
|
stableCoreHash: this.fingerprint?.stableCoreHash,
|
|
20179
20500
|
stableCoreVector: this.fingerprint?.stableCoreVector,
|