@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/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
- // Use first 16 characters of the hash for visitor ID
211
- return "vis_" + fingerprintHash.substring(0, 16);
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,