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/umd/index.js CHANGED
@@ -69,6 +69,7 @@
69
69
  SESSION: 'cryptique_session', // Primary session storage (sessionStorage)
70
70
  SESSION_BACKUP: 'cryptique_session_backup', // Backup session (localStorage)
71
71
  USER_ID: 'mtm_user_id', // Persistent user ID (localStorage)
72
+ DISTINCT_ID: 'cryptique_distinct_id', // Primary identity (sessionStorage)
72
73
  CONSENT: 'mtm_consent', // User consent flag (localStorage)
73
74
  REFERRER: 'referrer', // Stored referrer (localStorage)
74
75
  LAST_SESSION: 'cryptique_last_session' // Last session backup (localStorage)
@@ -118,7 +119,18 @@
118
119
 
119
120
  // Get Site ID from script tag attribute
120
121
  const analyticsScript = document.currentScript || document.querySelector('script[src*="script.js"]') || document.querySelector('script[src*="cryptique"]');
121
- const SITE_ID = analyticsScript ? analyticsScript.getAttribute("site-id") : null;
122
+ let SITE_ID = analyticsScript ? analyticsScript.getAttribute("site-id") : null;
123
+
124
+ // Fallback: Check window.Cryptique.siteId or global variable (for npm usage)
125
+ if (!SITE_ID) {
126
+ SITE_ID = window.Cryptique?.siteId || window.__CRYPTIQUE_SITE_ID__ || null;
127
+ }
128
+
129
+ // Helper function to get current site ID (checks all sources dynamically)
130
+ // This allows site ID to be set via init() after SDK loads
131
+ function getCurrentSiteId() {
132
+ return SITE_ID || window.Cryptique?.siteId || window.__CRYPTIQUE_SITE_ID__ || null;
133
+ }
122
134
 
123
135
  // Parse auto events configuration from script tag attributes
124
136
  // Default: auto events disabled, empty disabled paths array
@@ -476,6 +488,71 @@
476
488
  }
477
489
  },
478
490
 
491
+ /**
492
+ * Generate anonymous distinct_id (long and unique)
493
+ * Format: anon_{timestamp}_{userId}_{uuid}_{random}
494
+ *
495
+ * @param {string} userId - User ID to include in distinct_id
496
+ * @returns {string} Anonymous distinct_id
497
+ */
498
+ generateAnonymousDistinctId(userId) {
499
+ const timestamp = Date.now();
500
+ const random = Math.random().toString(36).substring(2, 18);
501
+ const uuid = this.generateUUIDv4();
502
+ return `anon_${timestamp}_${userId}_${uuid}_${random}`;
503
+ },
504
+
505
+ /**
506
+ * Generate identified distinct_id
507
+ * Format: id_{identifyId}
508
+ *
509
+ * @param {string} identifyId - Identify ID (email, username, etc.)
510
+ * @returns {string} Identified distinct_id
511
+ */
512
+ generateIdentifiedDistinctId(identifyId) {
513
+ return `id_${identifyId}`;
514
+ },
515
+
516
+ /**
517
+ * Get or create distinct_id
518
+ * Checks sessionStorage first, then generates new anonymous distinct_id if not found
519
+ *
520
+ * @returns {string} Current distinct_id
521
+ */
522
+ getDistinctId() {
523
+ try {
524
+ // Check sessionStorage first (primary source)
525
+ let distinctId = sessionStorage.getItem(CONFIG.STORAGE_KEYS.DISTINCT_ID);
526
+
527
+ if (!distinctId) {
528
+ // Generate new anonymous distinct_id
529
+ const userId = this.getUserId();
530
+ distinctId = this.generateAnonymousDistinctId(userId);
531
+ sessionStorage.setItem(CONFIG.STORAGE_KEYS.DISTINCT_ID, distinctId);
532
+ }
533
+
534
+ return distinctId;
535
+ } catch (err) {
536
+ // Fallback: generate temporary distinct_id
537
+ console.warn('Failed to access sessionStorage for distinct_id:', err);
538
+ const userId = this.getUserId();
539
+ return this.generateAnonymousDistinctId(userId);
540
+ }
541
+ },
542
+
543
+ /**
544
+ * Set distinct_id in sessionStorage
545
+ *
546
+ * @param {string} distinctId - Distinct ID to set
547
+ */
548
+ setDistinctId(distinctId) {
549
+ try {
550
+ sessionStorage.setItem(CONFIG.STORAGE_KEYS.DISTINCT_ID, distinctId);
551
+ } catch (err) {
552
+ console.warn('Failed to set distinct_id:', err);
553
+ }
554
+ },
555
+
479
556
  /**
480
557
  * Get user consent status
481
558
  *
@@ -727,7 +804,7 @@
727
804
  let sessionData = {
728
805
  // IDs (internal camelCase)
729
806
  sessionId: null, // Will be set by Session ID Management
730
- siteId: SITE_ID,
807
+ siteId: getCurrentSiteId(),
731
808
  teamId: null, // May be set externally or from backend
732
809
  userId: null, // Will be set from StorageManager
733
810
 
@@ -1682,7 +1759,9 @@
1682
1759
  initialize() {
1683
1760
  // Ensure all required fields exist (some may already be set)
1684
1761
  if (!sessionData.sessionId) sessionData.sessionId = null;
1685
- if (!sessionData.siteId) sessionData.siteId = SITE_ID;
1762
+ // Get current SITE_ID (may have been set via init() after SDK loaded)
1763
+ const currentSiteId = SITE_ID || window.Cryptique?.siteId || window.__CRYPTIQUE_SITE_ID__ || null;
1764
+ if (!sessionData.siteId) sessionData.siteId = currentSiteId;
1686
1765
  if (!sessionData.teamId) sessionData.teamId = null;
1687
1766
  if (!sessionData.userId) sessionData.userId = null;
1688
1767
 
@@ -3790,7 +3869,7 @@
3790
3869
  const transformed = {
3791
3870
  // IDs - only session_id (snake_case), no id (PostgreSQL auto-generates it)
3792
3871
  session_id: sourceData.session_id || sourceData.sessionId,
3793
- site_id: sourceData.site_id || sourceData.siteId || SITE_ID,
3872
+ site_id: sourceData.site_id || sourceData.siteId || getCurrentSiteId() || null,
3794
3873
  team_id: sourceData.team_id || sourceData.teamId || null,
3795
3874
  user_id: sourceData.user_id || sourceData.userId || null,
3796
3875
 
@@ -4110,7 +4189,7 @@
4110
4189
  const country = sessionData.locationData?.country || null;
4111
4190
 
4112
4191
  const utmEventPayload = {
4113
- siteId: SITE_ID,
4192
+ siteId: getCurrentSiteId(),
4114
4193
  sessionId: sessionData.sessionId,
4115
4194
  userId: sessionData.userId,
4116
4195
  utm_source: utmData.utm_source,
@@ -5727,7 +5806,7 @@
5727
5806
  method: 'POST',
5728
5807
  headers: {
5729
5808
  'Content-Type': 'application/json',
5730
- 'X-Cryptique-Site-Id': SITE_ID
5809
+ 'X-Cryptique-Site-Id': getCurrentSiteId()
5731
5810
  },
5732
5811
  body: JSON.stringify(eventData)
5733
5812
  });
@@ -5862,23 +5941,28 @@
5862
5941
  return { success: false, error: 'No user ID' };
5863
5942
  }
5864
5943
 
5865
- if (!SITE_ID) {
5944
+ const currentSiteId = getCurrentSiteId();
5945
+ if (!currentSiteId) {
5866
5946
  console.error('❌ [Identity] Site ID not found');
5867
5947
  return { success: false, error: 'Site ID not found' };
5868
5948
  }
5869
5949
 
5950
+ // Get current distinct_id
5951
+ const currentDistinctId = StorageManager.getDistinctId();
5952
+
5870
5953
  // Call API endpoint
5871
5954
  const apiUrl = CONFIG.API.TRACK.replace('/track', '/identify');
5872
5955
  const response = await fetch(apiUrl, {
5873
5956
  method: 'POST',
5874
5957
  headers: {
5875
5958
  'Content-Type': 'application/json',
5876
- 'X-Cryptique-Site-Id': SITE_ID
5959
+ 'X-Cryptique-Site-Id': currentSiteId
5877
5960
  },
5878
5961
  body: JSON.stringify({
5879
- siteId: SITE_ID,
5962
+ siteId: getCurrentSiteId(),
5880
5963
  sessionId: session.id,
5881
5964
  userId: currentUserId,
5965
+ distinctId: currentDistinctId, // NEW: Pass distinct_id
5882
5966
  identifyId: identifyId.trim()
5883
5967
  })
5884
5968
  });
@@ -5895,20 +5979,31 @@
5895
5979
 
5896
5980
  const result = await response.json();
5897
5981
 
5898
- // If merge occurred, update localStorage and session data
5899
- if (result.merged && result.newUserId) {
5900
- // Update localStorage with new user ID
5901
- localStorage.setItem(CONFIG.STORAGE_KEYS.USER_ID, result.newUserId);
5982
+ // If merge occurred or distinct_id changed, update sessionStorage and session data
5983
+ if (result.merged && result.newDistinctId) {
5984
+ // Update sessionStorage with new distinct_id
5985
+ StorageManager.setDistinctId(result.newDistinctId);
5902
5986
 
5903
5987
  // Update session data
5904
- sessionData.userId = result.newUserId;
5905
- session.userId = result.newUserId;
5906
- StorageManager.saveSession(session);
5988
+ if (sessionData) {
5989
+ sessionData.distinctId = result.newDistinctId;
5990
+ sessionData.anonymous = false;
5991
+ sessionData.identified = true;
5992
+ }
5907
5993
 
5908
- // Update userSession
5909
- userSession.userId = result.newUserId;
5994
+ console.log(`✅ [Identity] Distinct ID merged: ${currentDistinctId} → ${result.newDistinctId}`);
5995
+ } else if (result.distinctId) {
5996
+ // Update to identified distinct_id
5997
+ StorageManager.setDistinctId(result.distinctId);
5998
+
5999
+ // Update session data
6000
+ if (sessionData) {
6001
+ sessionData.distinctId = result.distinctId;
6002
+ sessionData.anonymous = false;
6003
+ sessionData.identified = true;
6004
+ }
5910
6005
 
5911
- console.log(`✅ [Identity] User merged: ${currentUserId} → ${result.newUserId}`);
6006
+ console.log(`✅ [Identity] Distinct ID updated: ${currentDistinctId} → ${result.distinctId}`);
5912
6007
  }
5913
6008
 
5914
6009
  return result;
@@ -5918,6 +6013,53 @@
5918
6013
  }
5919
6014
  },
5920
6015
 
6016
+ /**
6017
+ * Reset identity (logout/anonymous)
6018
+ * Generates new anonymous distinct_id and clears identified state
6019
+ *
6020
+ * @returns {Promise<Object>} Result object with new distinct_id
6021
+ */
6022
+ async reset() {
6023
+ try {
6024
+ // Generate new anonymous distinct_id
6025
+ const userId = StorageManager.getUserId();
6026
+ const newDistinctId = StorageManager.generateAnonymousDistinctId(userId);
6027
+
6028
+ // Update sessionStorage
6029
+ StorageManager.setDistinctId(newDistinctId);
6030
+
6031
+ // Update session data
6032
+ const session = StorageManager.loadSession();
6033
+ if (session) {
6034
+ session.distinctId = newDistinctId;
6035
+ session.anonymous = true;
6036
+ session.identified = false;
6037
+ StorageManager.saveSession(session);
6038
+ }
6039
+
6040
+ // Update sessionData if available
6041
+ if (sessionData) {
6042
+ sessionData.distinctId = newDistinctId;
6043
+ sessionData.anonymous = true;
6044
+ sessionData.identified = false;
6045
+ }
6046
+
6047
+ // Clear identify_id from storage (if stored separately)
6048
+ try {
6049
+ sessionStorage.removeItem('cryptique_identify_id');
6050
+ } catch (err) {
6051
+ // Ignore errors
6052
+ }
6053
+
6054
+ console.log(`🔄 [Identity] Reset to anonymous: ${newDistinctId}`);
6055
+
6056
+ return { success: true, distinctId: newDistinctId };
6057
+ } catch (error) {
6058
+ console.error('❌ [Identity] Error in reset():', error);
6059
+ return { success: false, error: error.message };
6060
+ }
6061
+ },
6062
+
5921
6063
  /**
5922
6064
  * Set wallet address for current user
5923
6065
  *
@@ -5965,6 +6107,9 @@
5965
6107
 
5966
6108
  console.log(`✅ [Identity] Wallet address set in sessionData: ${trimmedWalletAddress}`);
5967
6109
 
6110
+ // Get current distinct_id
6111
+ const currentDistinctId = StorageManager.getDistinctId();
6112
+
5968
6113
  // Call API endpoint to update user_identity and session in database
5969
6114
  // This handles merging and immediate session update if session exists
5970
6115
  const apiUrl = CONFIG.API.TRACK.replace('/track', '/wallet-address');
@@ -5972,12 +6117,13 @@
5972
6117
  method: 'POST',
5973
6118
  headers: {
5974
6119
  'Content-Type': 'application/json',
5975
- 'X-Cryptique-Site-Id': SITE_ID
6120
+ 'X-Cryptique-Site-Id': getCurrentSiteId()
5976
6121
  },
5977
6122
  body: JSON.stringify({
5978
- siteId: SITE_ID,
6123
+ siteId: getCurrentSiteId(),
5979
6124
  sessionId: session.id,
5980
6125
  userId: currentUserId,
6126
+ distinctId: currentDistinctId, // NEW: Pass distinct_id
5981
6127
  walletAddress: trimmedWalletAddress
5982
6128
  })
5983
6129
  });
@@ -6446,10 +6592,10 @@
6446
6592
  method: 'POST',
6447
6593
  headers: {
6448
6594
  'Content-Type': 'application/json',
6449
- 'X-Cryptique-Site-Id': SITE_ID
6595
+ 'X-Cryptique-Site-Id': getCurrentSiteId()
6450
6596
  },
6451
6597
  body: JSON.stringify({
6452
- siteId: SITE_ID,
6598
+ siteId: getCurrentSiteId(),
6453
6599
  userId: currentUserId,
6454
6600
  properties: properties
6455
6601
  })
@@ -6496,10 +6642,10 @@
6496
6642
  method: 'POST',
6497
6643
  headers: {
6498
6644
  'Content-Type': 'application/json',
6499
- 'X-Cryptique-Site-Id': SITE_ID
6645
+ 'X-Cryptique-Site-Id': getCurrentSiteId()
6500
6646
  },
6501
6647
  body: JSON.stringify({
6502
- siteId: SITE_ID,
6648
+ siteId: getCurrentSiteId(),
6503
6649
  userId: currentUserId,
6504
6650
  properties: properties
6505
6651
  })
@@ -6546,10 +6692,10 @@
6546
6692
  method: 'POST',
6547
6693
  headers: {
6548
6694
  'Content-Type': 'application/json',
6549
- 'X-Cryptique-Site-Id': SITE_ID
6695
+ 'X-Cryptique-Site-Id': getCurrentSiteId()
6550
6696
  },
6551
6697
  body: JSON.stringify({
6552
- siteId: SITE_ID,
6698
+ siteId: getCurrentSiteId(),
6553
6699
  userId: currentUserId,
6554
6700
  keys: keys
6555
6701
  })
@@ -6602,10 +6748,10 @@
6602
6748
  method: 'POST',
6603
6749
  headers: {
6604
6750
  'Content-Type': 'application/json',
6605
- 'X-Cryptique-Site-Id': SITE_ID
6751
+ 'X-Cryptique-Site-Id': getCurrentSiteId()
6606
6752
  },
6607
6753
  body: JSON.stringify({
6608
- siteId: SITE_ID,
6754
+ siteId: getCurrentSiteId(),
6609
6755
  userId: currentUserId,
6610
6756
  key: key,
6611
6757
  amount: amount
@@ -6659,10 +6805,10 @@
6659
6805
  method: 'POST',
6660
6806
  headers: {
6661
6807
  'Content-Type': 'application/json',
6662
- 'X-Cryptique-Site-Id': SITE_ID
6808
+ 'X-Cryptique-Site-Id': getCurrentSiteId()
6663
6809
  },
6664
6810
  body: JSON.stringify({
6665
- siteId: SITE_ID,
6811
+ siteId: getCurrentSiteId(),
6666
6812
  userId: currentUserId,
6667
6813
  key: key,
6668
6814
  values: values
@@ -6716,10 +6862,10 @@
6716
6862
  method: 'POST',
6717
6863
  headers: {
6718
6864
  'Content-Type': 'application/json',
6719
- 'X-Cryptique-Site-Id': SITE_ID
6865
+ 'X-Cryptique-Site-Id': getCurrentSiteId()
6720
6866
  },
6721
6867
  body: JSON.stringify({
6722
- siteId: SITE_ID,
6868
+ siteId: getCurrentSiteId(),
6723
6869
  userId: currentUserId,
6724
6870
  key: key,
6725
6871
  values: values
@@ -6773,10 +6919,10 @@
6773
6919
  method: 'POST',
6774
6920
  headers: {
6775
6921
  'Content-Type': 'application/json',
6776
- 'X-Cryptique-Site-Id': SITE_ID
6922
+ 'X-Cryptique-Site-Id': getCurrentSiteId()
6777
6923
  },
6778
6924
  body: JSON.stringify({
6779
- siteId: SITE_ID,
6925
+ siteId: getCurrentSiteId(),
6780
6926
  userId: currentUserId,
6781
6927
  key: key,
6782
6928
  values: values
@@ -6825,10 +6971,10 @@
6825
6971
  method: 'POST',
6826
6972
  headers: {
6827
6973
  'Content-Type': 'application/json',
6828
- 'X-Cryptique-Site-Id': SITE_ID
6974
+ 'X-Cryptique-Site-Id': getCurrentSiteId()
6829
6975
  },
6830
6976
  body: JSON.stringify({
6831
- siteId: SITE_ID,
6977
+ siteId: getCurrentSiteId(),
6832
6978
  userId: currentUserId,
6833
6979
  amount: amount,
6834
6980
  properties: properties
@@ -6870,10 +7016,10 @@
6870
7016
  method: 'POST',
6871
7017
  headers: {
6872
7018
  'Content-Type': 'application/json',
6873
- 'X-Cryptique-Site-Id': SITE_ID
7019
+ 'X-Cryptique-Site-Id': getCurrentSiteId()
6874
7020
  },
6875
7021
  body: JSON.stringify({
6876
- siteId: SITE_ID,
7022
+ siteId: getCurrentSiteId(),
6877
7023
  userId: currentUserId
6878
7024
  })
6879
7025
  });
@@ -6913,10 +7059,10 @@
6913
7059
  method: 'POST',
6914
7060
  headers: {
6915
7061
  'Content-Type': 'application/json',
6916
- 'X-Cryptique-Site-Id': SITE_ID
7062
+ 'X-Cryptique-Site-Id': getCurrentSiteId()
6917
7063
  },
6918
7064
  body: JSON.stringify({
6919
- siteId: SITE_ID,
7065
+ siteId: getCurrentSiteId(),
6920
7066
  userId: currentUserId
6921
7067
  })
6922
7068
  });
@@ -6966,6 +7112,7 @@
6966
7112
  // Identity Functions
6967
7113
  identify: IdentityManager.identify.bind(IdentityManager),
6968
7114
  walletAddress: IdentityManager.walletAddress.bind(IdentityManager),
7115
+ reset: IdentityManager.reset.bind(IdentityManager),
6969
7116
 
6970
7117
  // People Functions (Custom Properties)
6971
7118
  people: {
@@ -6987,7 +7134,7 @@
6987
7134
 
6988
7135
  // Session Data Access
6989
7136
  sessionData: sessionData,
6990
- siteId: SITE_ID,
7137
+ siteId: getCurrentSiteId(),
6991
7138
  getSessionData: function() {
6992
7139
  return sessionData;
6993
7140
  },
@@ -7281,6 +7428,22 @@
7281
7428
  throw new Error('SDK not initialized. Call init() first.');
7282
7429
  },
7283
7430
 
7431
+ /**
7432
+ * Reset user identity to anonymous
7433
+ *
7434
+ * Generates a new anonymous distinct_id and clears identification.
7435
+ * Useful for privacy/GDPR compliance when user wants to reset their identity.
7436
+ *
7437
+ * @returns {Promise<Object>} Result object with success status and new distinctId
7438
+ */
7439
+ async reset() {
7440
+ const instance = this.getInstance();
7441
+ if (instance && instance.reset) {
7442
+ return await instance.reset();
7443
+ }
7444
+ throw new Error('SDK not initialized. Call init() first.');
7445
+ },
7446
+
7284
7447
  /**
7285
7448
  * Track a custom event
7286
7449
  * @param {string} eventName - Name of the event
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cryptique-sdk",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "type": "module",
5
5
  "description": "Cryptique Analytics SDK - Comprehensive web analytics and user tracking for modern web applications",
6
6
  "main": "lib/cjs/index.js",