cryptique-sdk 1.0.2 → 1.0.4

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/lib/esm/index.js CHANGED
@@ -63,6 +63,7 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
63
63
  SESSION: 'cryptique_session', // Primary session storage (sessionStorage)
64
64
  SESSION_BACKUP: 'cryptique_session_backup', // Backup session (localStorage)
65
65
  USER_ID: 'mtm_user_id', // Persistent user ID (localStorage)
66
+ DISTINCT_ID: 'cryptique_distinct_id', // Primary identity (sessionStorage)
66
67
  CONSENT: 'mtm_consent', // User consent flag (localStorage)
67
68
  REFERRER: 'referrer', // Stored referrer (localStorage)
68
69
  LAST_SESSION: 'cryptique_last_session' // Last session backup (localStorage)
@@ -112,7 +113,18 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
112
113
 
113
114
  // Get Site ID from script tag attribute
114
115
  const analyticsScript = document.currentScript || document.querySelector('script[src*="script.js"]') || document.querySelector('script[src*="cryptique"]');
115
- const SITE_ID = analyticsScript ? analyticsScript.getAttribute("site-id") : null;
116
+ let SITE_ID = analyticsScript ? analyticsScript.getAttribute("site-id") : null;
117
+
118
+ // Fallback: Check window.Cryptique.siteId or global variable (for npm usage)
119
+ if (!SITE_ID) {
120
+ SITE_ID = window.Cryptique?.siteId || window.__CRYPTIQUE_SITE_ID__ || null;
121
+ }
122
+
123
+ // Helper function to get current site ID (checks all sources dynamically)
124
+ // This allows site ID to be set via init() after SDK loads
125
+ function getCurrentSiteId() {
126
+ return SITE_ID || window.Cryptique?.siteId || window.__CRYPTIQUE_SITE_ID__ || null;
127
+ }
116
128
 
117
129
  // Parse auto events configuration from script tag attributes
118
130
  // Default: auto events disabled, empty disabled paths array
@@ -470,6 +482,71 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
470
482
  }
471
483
  },
472
484
 
485
+ /**
486
+ * Generate anonymous distinct_id (long and unique)
487
+ * Format: anon_{timestamp}_{userId}_{uuid}_{random}
488
+ *
489
+ * @param {string} userId - User ID to include in distinct_id
490
+ * @returns {string} Anonymous distinct_id
491
+ */
492
+ generateAnonymousDistinctId(userId) {
493
+ const timestamp = Date.now();
494
+ const random = Math.random().toString(36).substring(2, 18);
495
+ const uuid = this.generateUUIDv4();
496
+ return `anon_${timestamp}_${userId}_${uuid}_${random}`;
497
+ },
498
+
499
+ /**
500
+ * Generate identified distinct_id
501
+ * Format: id_{identifyId}
502
+ *
503
+ * @param {string} identifyId - Identify ID (email, username, etc.)
504
+ * @returns {string} Identified distinct_id
505
+ */
506
+ generateIdentifiedDistinctId(identifyId) {
507
+ return `id_${identifyId}`;
508
+ },
509
+
510
+ /**
511
+ * Get or create distinct_id
512
+ * Checks sessionStorage first, then generates new anonymous distinct_id if not found
513
+ *
514
+ * @returns {string} Current distinct_id
515
+ */
516
+ getDistinctId() {
517
+ try {
518
+ // Check sessionStorage first (primary source)
519
+ let distinctId = sessionStorage.getItem(CONFIG.STORAGE_KEYS.DISTINCT_ID);
520
+
521
+ if (!distinctId) {
522
+ // Generate new anonymous distinct_id
523
+ const userId = this.getUserId();
524
+ distinctId = this.generateAnonymousDistinctId(userId);
525
+ sessionStorage.setItem(CONFIG.STORAGE_KEYS.DISTINCT_ID, distinctId);
526
+ }
527
+
528
+ return distinctId;
529
+ } catch (err) {
530
+ // Fallback: generate temporary distinct_id
531
+ console.warn('Failed to access sessionStorage for distinct_id:', err);
532
+ const userId = this.getUserId();
533
+ return this.generateAnonymousDistinctId(userId);
534
+ }
535
+ },
536
+
537
+ /**
538
+ * Set distinct_id in sessionStorage
539
+ *
540
+ * @param {string} distinctId - Distinct ID to set
541
+ */
542
+ setDistinctId(distinctId) {
543
+ try {
544
+ sessionStorage.setItem(CONFIG.STORAGE_KEYS.DISTINCT_ID, distinctId);
545
+ } catch (err) {
546
+ console.warn('Failed to set distinct_id:', err);
547
+ }
548
+ },
549
+
473
550
  /**
474
551
  * Get user consent status
475
552
  *
@@ -721,7 +798,7 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
721
798
  let sessionData = {
722
799
  // IDs (internal camelCase)
723
800
  sessionId: null, // Will be set by Session ID Management
724
- siteId: SITE_ID,
801
+ siteId: getCurrentSiteId(),
725
802
  teamId: null, // May be set externally or from backend
726
803
  userId: null, // Will be set from StorageManager
727
804
 
@@ -1676,7 +1753,9 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
1676
1753
  initialize() {
1677
1754
  // Ensure all required fields exist (some may already be set)
1678
1755
  if (!sessionData.sessionId) sessionData.sessionId = null;
1679
- if (!sessionData.siteId) sessionData.siteId = SITE_ID;
1756
+ // Get current SITE_ID (may have been set via init() after SDK loaded)
1757
+ const currentSiteId = SITE_ID || window.Cryptique?.siteId || window.__CRYPTIQUE_SITE_ID__ || null;
1758
+ if (!sessionData.siteId) sessionData.siteId = currentSiteId;
1680
1759
  if (!sessionData.teamId) sessionData.teamId = null;
1681
1760
  if (!sessionData.userId) sessionData.userId = null;
1682
1761
 
@@ -3784,7 +3863,7 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
3784
3863
  const transformed = {
3785
3864
  // IDs - only session_id (snake_case), no id (PostgreSQL auto-generates it)
3786
3865
  session_id: sourceData.session_id || sourceData.sessionId,
3787
- site_id: sourceData.site_id || sourceData.siteId || SITE_ID,
3866
+ site_id: sourceData.site_id || sourceData.siteId || getCurrentSiteId() || null,
3788
3867
  team_id: sourceData.team_id || sourceData.teamId || null,
3789
3868
  user_id: sourceData.user_id || sourceData.userId || null,
3790
3869
 
@@ -4104,7 +4183,7 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
4104
4183
  const country = sessionData.locationData?.country || null;
4105
4184
 
4106
4185
  const utmEventPayload = {
4107
- siteId: SITE_ID,
4186
+ siteId: getCurrentSiteId(),
4108
4187
  sessionId: sessionData.sessionId,
4109
4188
  userId: sessionData.userId,
4110
4189
  utm_source: utmData.utm_source,
@@ -5721,7 +5800,7 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
5721
5800
  method: 'POST',
5722
5801
  headers: {
5723
5802
  'Content-Type': 'application/json',
5724
- 'X-Cryptique-Site-Id': SITE_ID
5803
+ 'X-Cryptique-Site-Id': getCurrentSiteId()
5725
5804
  },
5726
5805
  body: JSON.stringify(eventData)
5727
5806
  });
@@ -5856,23 +5935,28 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
5856
5935
  return { success: false, error: 'No user ID' };
5857
5936
  }
5858
5937
 
5859
- if (!SITE_ID) {
5938
+ const currentSiteId = getCurrentSiteId();
5939
+ if (!currentSiteId) {
5860
5940
  console.error('❌ [Identity] Site ID not found');
5861
5941
  return { success: false, error: 'Site ID not found' };
5862
5942
  }
5863
5943
 
5944
+ // Get current distinct_id
5945
+ const currentDistinctId = StorageManager.getDistinctId();
5946
+
5864
5947
  // Call API endpoint
5865
5948
  const apiUrl = CONFIG.API.TRACK.replace('/track', '/identify');
5866
5949
  const response = await fetch(apiUrl, {
5867
5950
  method: 'POST',
5868
5951
  headers: {
5869
5952
  'Content-Type': 'application/json',
5870
- 'X-Cryptique-Site-Id': SITE_ID
5953
+ 'X-Cryptique-Site-Id': currentSiteId
5871
5954
  },
5872
5955
  body: JSON.stringify({
5873
- siteId: SITE_ID,
5956
+ siteId: getCurrentSiteId(),
5874
5957
  sessionId: session.id,
5875
5958
  userId: currentUserId,
5959
+ distinctId: currentDistinctId, // NEW: Pass distinct_id
5876
5960
  identifyId: identifyId.trim()
5877
5961
  })
5878
5962
  });
@@ -5889,20 +5973,31 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
5889
5973
 
5890
5974
  const result = await response.json();
5891
5975
 
5892
- // If merge occurred, update localStorage and session data
5893
- if (result.merged && result.newUserId) {
5894
- // Update localStorage with new user ID
5895
- localStorage.setItem(CONFIG.STORAGE_KEYS.USER_ID, result.newUserId);
5976
+ // If merge occurred or distinct_id changed, update sessionStorage and session data
5977
+ if (result.merged && result.newDistinctId) {
5978
+ // Update sessionStorage with new distinct_id
5979
+ StorageManager.setDistinctId(result.newDistinctId);
5896
5980
 
5897
5981
  // Update session data
5898
- sessionData.userId = result.newUserId;
5899
- session.userId = result.newUserId;
5900
- StorageManager.saveSession(session);
5982
+ if (sessionData) {
5983
+ sessionData.distinctId = result.newDistinctId;
5984
+ sessionData.anonymous = false;
5985
+ sessionData.identified = true;
5986
+ }
5901
5987
 
5902
- // Update userSession
5903
- userSession.userId = result.newUserId;
5988
+ console.log(`✅ [Identity] Distinct ID merged: ${currentDistinctId} → ${result.newDistinctId}`);
5989
+ } else if (result.distinctId) {
5990
+ // Update to identified distinct_id
5991
+ StorageManager.setDistinctId(result.distinctId);
5992
+
5993
+ // Update session data
5994
+ if (sessionData) {
5995
+ sessionData.distinctId = result.distinctId;
5996
+ sessionData.anonymous = false;
5997
+ sessionData.identified = true;
5998
+ }
5904
5999
 
5905
- console.log(`✅ [Identity] User merged: ${currentUserId} → ${result.newUserId}`);
6000
+ console.log(`✅ [Identity] Distinct ID updated: ${currentDistinctId} → ${result.distinctId}`);
5906
6001
  }
5907
6002
 
5908
6003
  return result;
@@ -5912,6 +6007,53 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
5912
6007
  }
5913
6008
  },
5914
6009
 
6010
+ /**
6011
+ * Reset identity (logout/anonymous)
6012
+ * Generates new anonymous distinct_id and clears identified state
6013
+ *
6014
+ * @returns {Promise<Object>} Result object with new distinct_id
6015
+ */
6016
+ async reset() {
6017
+ try {
6018
+ // Generate new anonymous distinct_id
6019
+ const userId = StorageManager.getUserId();
6020
+ const newDistinctId = StorageManager.generateAnonymousDistinctId(userId);
6021
+
6022
+ // Update sessionStorage
6023
+ StorageManager.setDistinctId(newDistinctId);
6024
+
6025
+ // Update session data
6026
+ const session = StorageManager.loadSession();
6027
+ if (session) {
6028
+ session.distinctId = newDistinctId;
6029
+ session.anonymous = true;
6030
+ session.identified = false;
6031
+ StorageManager.saveSession(session);
6032
+ }
6033
+
6034
+ // Update sessionData if available
6035
+ if (sessionData) {
6036
+ sessionData.distinctId = newDistinctId;
6037
+ sessionData.anonymous = true;
6038
+ sessionData.identified = false;
6039
+ }
6040
+
6041
+ // Clear identify_id from storage (if stored separately)
6042
+ try {
6043
+ sessionStorage.removeItem('cryptique_identify_id');
6044
+ } catch (err) {
6045
+ // Ignore errors
6046
+ }
6047
+
6048
+ console.log(`🔄 [Identity] Reset to anonymous: ${newDistinctId}`);
6049
+
6050
+ return { success: true, distinctId: newDistinctId };
6051
+ } catch (error) {
6052
+ console.error('❌ [Identity] Error in reset():', error);
6053
+ return { success: false, error: error.message };
6054
+ }
6055
+ },
6056
+
5915
6057
  /**
5916
6058
  * Set wallet address for current user
5917
6059
  *
@@ -5959,6 +6101,9 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
5959
6101
 
5960
6102
  console.log(`✅ [Identity] Wallet address set in sessionData: ${trimmedWalletAddress}`);
5961
6103
 
6104
+ // Get current distinct_id
6105
+ const currentDistinctId = StorageManager.getDistinctId();
6106
+
5962
6107
  // Call API endpoint to update user_identity and session in database
5963
6108
  // This handles merging and immediate session update if session exists
5964
6109
  const apiUrl = CONFIG.API.TRACK.replace('/track', '/wallet-address');
@@ -5966,12 +6111,13 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
5966
6111
  method: 'POST',
5967
6112
  headers: {
5968
6113
  'Content-Type': 'application/json',
5969
- 'X-Cryptique-Site-Id': SITE_ID
6114
+ 'X-Cryptique-Site-Id': getCurrentSiteId()
5970
6115
  },
5971
6116
  body: JSON.stringify({
5972
- siteId: SITE_ID,
6117
+ siteId: getCurrentSiteId(),
5973
6118
  sessionId: session.id,
5974
6119
  userId: currentUserId,
6120
+ distinctId: currentDistinctId, // NEW: Pass distinct_id
5975
6121
  walletAddress: trimmedWalletAddress
5976
6122
  })
5977
6123
  });
@@ -6440,10 +6586,10 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
6440
6586
  method: 'POST',
6441
6587
  headers: {
6442
6588
  'Content-Type': 'application/json',
6443
- 'X-Cryptique-Site-Id': SITE_ID
6589
+ 'X-Cryptique-Site-Id': getCurrentSiteId()
6444
6590
  },
6445
6591
  body: JSON.stringify({
6446
- siteId: SITE_ID,
6592
+ siteId: getCurrentSiteId(),
6447
6593
  userId: currentUserId,
6448
6594
  properties: properties
6449
6595
  })
@@ -6490,10 +6636,10 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
6490
6636
  method: 'POST',
6491
6637
  headers: {
6492
6638
  'Content-Type': 'application/json',
6493
- 'X-Cryptique-Site-Id': SITE_ID
6639
+ 'X-Cryptique-Site-Id': getCurrentSiteId()
6494
6640
  },
6495
6641
  body: JSON.stringify({
6496
- siteId: SITE_ID,
6642
+ siteId: getCurrentSiteId(),
6497
6643
  userId: currentUserId,
6498
6644
  properties: properties
6499
6645
  })
@@ -6540,10 +6686,10 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
6540
6686
  method: 'POST',
6541
6687
  headers: {
6542
6688
  'Content-Type': 'application/json',
6543
- 'X-Cryptique-Site-Id': SITE_ID
6689
+ 'X-Cryptique-Site-Id': getCurrentSiteId()
6544
6690
  },
6545
6691
  body: JSON.stringify({
6546
- siteId: SITE_ID,
6692
+ siteId: getCurrentSiteId(),
6547
6693
  userId: currentUserId,
6548
6694
  keys: keys
6549
6695
  })
@@ -6596,10 +6742,10 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
6596
6742
  method: 'POST',
6597
6743
  headers: {
6598
6744
  'Content-Type': 'application/json',
6599
- 'X-Cryptique-Site-Id': SITE_ID
6745
+ 'X-Cryptique-Site-Id': getCurrentSiteId()
6600
6746
  },
6601
6747
  body: JSON.stringify({
6602
- siteId: SITE_ID,
6748
+ siteId: getCurrentSiteId(),
6603
6749
  userId: currentUserId,
6604
6750
  key: key,
6605
6751
  amount: amount
@@ -6653,10 +6799,10 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
6653
6799
  method: 'POST',
6654
6800
  headers: {
6655
6801
  'Content-Type': 'application/json',
6656
- 'X-Cryptique-Site-Id': SITE_ID
6802
+ 'X-Cryptique-Site-Id': getCurrentSiteId()
6657
6803
  },
6658
6804
  body: JSON.stringify({
6659
- siteId: SITE_ID,
6805
+ siteId: getCurrentSiteId(),
6660
6806
  userId: currentUserId,
6661
6807
  key: key,
6662
6808
  values: values
@@ -6710,10 +6856,10 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
6710
6856
  method: 'POST',
6711
6857
  headers: {
6712
6858
  'Content-Type': 'application/json',
6713
- 'X-Cryptique-Site-Id': SITE_ID
6859
+ 'X-Cryptique-Site-Id': getCurrentSiteId()
6714
6860
  },
6715
6861
  body: JSON.stringify({
6716
- siteId: SITE_ID,
6862
+ siteId: getCurrentSiteId(),
6717
6863
  userId: currentUserId,
6718
6864
  key: key,
6719
6865
  values: values
@@ -6767,10 +6913,10 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
6767
6913
  method: 'POST',
6768
6914
  headers: {
6769
6915
  'Content-Type': 'application/json',
6770
- 'X-Cryptique-Site-Id': SITE_ID
6916
+ 'X-Cryptique-Site-Id': getCurrentSiteId()
6771
6917
  },
6772
6918
  body: JSON.stringify({
6773
- siteId: SITE_ID,
6919
+ siteId: getCurrentSiteId(),
6774
6920
  userId: currentUserId,
6775
6921
  key: key,
6776
6922
  values: values
@@ -6819,10 +6965,10 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
6819
6965
  method: 'POST',
6820
6966
  headers: {
6821
6967
  'Content-Type': 'application/json',
6822
- 'X-Cryptique-Site-Id': SITE_ID
6968
+ 'X-Cryptique-Site-Id': getCurrentSiteId()
6823
6969
  },
6824
6970
  body: JSON.stringify({
6825
- siteId: SITE_ID,
6971
+ siteId: getCurrentSiteId(),
6826
6972
  userId: currentUserId,
6827
6973
  amount: amount,
6828
6974
  properties: properties
@@ -6864,10 +7010,10 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
6864
7010
  method: 'POST',
6865
7011
  headers: {
6866
7012
  'Content-Type': 'application/json',
6867
- 'X-Cryptique-Site-Id': SITE_ID
7013
+ 'X-Cryptique-Site-Id': getCurrentSiteId()
6868
7014
  },
6869
7015
  body: JSON.stringify({
6870
- siteId: SITE_ID,
7016
+ siteId: getCurrentSiteId(),
6871
7017
  userId: currentUserId
6872
7018
  })
6873
7019
  });
@@ -6907,10 +7053,10 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
6907
7053
  method: 'POST',
6908
7054
  headers: {
6909
7055
  'Content-Type': 'application/json',
6910
- 'X-Cryptique-Site-Id': SITE_ID
7056
+ 'X-Cryptique-Site-Id': getCurrentSiteId()
6911
7057
  },
6912
7058
  body: JSON.stringify({
6913
- siteId: SITE_ID,
7059
+ siteId: getCurrentSiteId(),
6914
7060
  userId: currentUserId
6915
7061
  })
6916
7062
  });
@@ -6960,6 +7106,7 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
6960
7106
  // Identity Functions
6961
7107
  identify: IdentityManager.identify.bind(IdentityManager),
6962
7108
  walletAddress: IdentityManager.walletAddress.bind(IdentityManager),
7109
+ reset: IdentityManager.reset.bind(IdentityManager),
6963
7110
 
6964
7111
  // People Functions (Custom Properties)
6965
7112
  people: {
@@ -6981,7 +7128,7 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
6981
7128
 
6982
7129
  // Session Data Access
6983
7130
  sessionData: sessionData,
6984
- siteId: SITE_ID,
7131
+ siteId: getCurrentSiteId(),
6985
7132
  getSessionData: function() {
6986
7133
  return sessionData;
6987
7134
  },
@@ -7275,6 +7422,22 @@ const CryptiqueSDK = {
7275
7422
  throw new Error('SDK not initialized. Call init() first.');
7276
7423
  },
7277
7424
 
7425
+ /**
7426
+ * Reset user identity to anonymous
7427
+ *
7428
+ * Generates a new anonymous distinct_id and clears identification.
7429
+ * Useful for privacy/GDPR compliance when user wants to reset their identity.
7430
+ *
7431
+ * @returns {Promise<Object>} Result object with success status and new distinctId
7432
+ */
7433
+ async reset() {
7434
+ const instance = this.getInstance();
7435
+ if (instance && instance.reset) {
7436
+ return await instance.reset();
7437
+ }
7438
+ throw new Error('SDK not initialized. Call init() first.');
7439
+ },
7440
+
7278
7441
  /**
7279
7442
  * Track a custom event
7280
7443
  * @param {string} eventName - Name of the event
@@ -149,6 +149,12 @@ export default class CryptiqueSDK {
149
149
  */
150
150
  walletAddress(walletAddress: string): Promise<{ success: boolean; merged?: boolean; newUserId?: string; error?: string }>;
151
151
 
152
+ /**
153
+ * Reset user identity to anonymous
154
+ * Generates a new anonymous distinct_id and clears identification
155
+ */
156
+ reset(): Promise<{ success: boolean; distinctId?: string; error?: string }>;
157
+
152
158
  /**
153
159
  * Set tracking consent
154
160
  */