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