cryptique-sdk 1.0.4 → 1.0.6

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/cjs/index.js CHANGED
@@ -205,6 +205,11 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
205
205
  * - Easy to add features (encryption, compression, etc.)
206
206
  * - Single place to fix storage bugs
207
207
  */
208
+ // In-memory cache to prevent race conditions in distinct_id generation
209
+ let _cachedDistinctId = null;
210
+ // Synchronous lock flag to prevent concurrent distinct_id generation
211
+ let _isGeneratingDistinctId = false;
212
+
208
213
  const StorageManager = {
209
214
  /**
210
215
  * Load session from sessionStorage with localStorage backup
@@ -511,44 +516,122 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
511
516
 
512
517
  /**
513
518
  * Get or create distinct_id
514
- * Checks sessionStorage first, then generates new anonymous distinct_id if not found
519
+ * All distinct_ids (anonymous and identified) persist in localStorage
520
+ * Uses in-memory cache to prevent race conditions
515
521
  *
516
- * @returns {string} Current distinct_id
522
+ * @returns {string|Promise<string>} Current distinct_id (may be Promise if generation is in progress)
517
523
  */
518
524
  getDistinctId() {
519
525
  try {
520
- // Check sessionStorage first (primary source)
521
- let distinctId = sessionStorage.getItem(CONFIG.STORAGE_KEYS.DISTINCT_ID);
526
+ // Return cached value if we already generated one in this session
527
+ // This prevents race conditions during initialization
528
+ if (_cachedDistinctId) {
529
+ return _cachedDistinctId;
530
+ }
522
531
 
523
- if (!distinctId) {
524
- // Generate new anonymous distinct_id
532
+ // If generation is in progress, return cached or wait briefly and retry
533
+ if (_isGeneratingDistinctId) {
534
+ // Check cache again - it might have been set by the concurrent call
535
+ if (_cachedDistinctId) {
536
+ return _cachedDistinctId;
537
+ }
538
+ // If still generating, return a temporary value (shouldn't happen in practice)
539
+ // This prevents infinite loops
540
+ const tempId = `temp_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
541
+ console.warn('⚠️ [Storage] Concurrent distinctId generation detected, returning temporary ID');
542
+ return tempId;
543
+ }
544
+
545
+ // First check localStorage (persists across sessions for both anonymous and identified)
546
+ let distinctId = localStorage.getItem(CONFIG.STORAGE_KEYS.DISTINCT_ID);
547
+
548
+ if (distinctId) {
549
+ // Found distinct_id in localStorage - use it and sync to sessionStorage
550
+ sessionStorage.setItem(CONFIG.STORAGE_KEYS.DISTINCT_ID, distinctId);
551
+ _cachedDistinctId = distinctId; // Cache it
552
+ return distinctId;
553
+ }
554
+
555
+ // Check sessionStorage as fallback (for current session only)
556
+ distinctId = sessionStorage.getItem(CONFIG.STORAGE_KEYS.DISTINCT_ID);
557
+
558
+ if (distinctId) {
559
+ // Found in sessionStorage - also store in localStorage for persistence
560
+ localStorage.setItem(CONFIG.STORAGE_KEYS.DISTINCT_ID, distinctId);
561
+ _cachedDistinctId = distinctId; // Cache it
562
+ return distinctId;
563
+ }
564
+
565
+ // No distinct_id found - generate new anonymous one
566
+ // Set lock BEFORE generating to prevent concurrent generation
567
+ _isGeneratingDistinctId = true;
568
+ try {
569
+ // Double-check localStorage after acquiring lock (another call might have stored it)
570
+ let checkAgain = localStorage.getItem(CONFIG.STORAGE_KEYS.DISTINCT_ID);
571
+ if (checkAgain) {
572
+ sessionStorage.setItem(CONFIG.STORAGE_KEYS.DISTINCT_ID, checkAgain);
573
+ _cachedDistinctId = checkAgain;
574
+ return checkAgain;
575
+ }
576
+
525
577
  const userId = this.getUserId();
526
578
  distinctId = this.generateAnonymousDistinctId(userId);
579
+
580
+ // Store in both localStorage (persist) and sessionStorage (current session)
581
+ localStorage.setItem(CONFIG.STORAGE_KEYS.DISTINCT_ID, distinctId);
527
582
  sessionStorage.setItem(CONFIG.STORAGE_KEYS.DISTINCT_ID, distinctId);
583
+
584
+ _cachedDistinctId = distinctId; // Cache it
585
+ return distinctId;
586
+ } finally {
587
+ _isGeneratingDistinctId = false;
528
588
  }
529
-
530
- return distinctId;
531
589
  } catch (err) {
532
590
  // Fallback: generate temporary distinct_id
533
- console.warn('Failed to access sessionStorage for distinct_id:', err);
591
+ console.warn('Failed to access storage for distinct_id:', err);
534
592
  const userId = this.getUserId();
535
- return this.generateAnonymousDistinctId(userId);
593
+ const fallbackId = this.generateAnonymousDistinctId(userId);
594
+ _cachedDistinctId = fallbackId; // Cache even fallback
595
+ _isGeneratingDistinctId = false;
596
+ return fallbackId;
536
597
  }
537
598
  },
538
599
 
539
600
  /**
540
- * Set distinct_id in sessionStorage
601
+ * Set distinct_id in storage
602
+ * All distinct_ids (anonymous and identified) are stored in both localStorage and sessionStorage
603
+ * Updates in-memory cache to maintain consistency
541
604
  *
542
605
  * @param {string} distinctId - Distinct ID to set
543
606
  */
544
607
  setDistinctId(distinctId) {
545
608
  try {
609
+ if (!distinctId) {
610
+ console.warn('⚠️ [Storage] setDistinctId called with null/undefined distinctId');
611
+ return;
612
+ }
613
+
614
+ // Store in both localStorage (persists across sessions) and sessionStorage (current session)
615
+ localStorage.setItem(CONFIG.STORAGE_KEYS.DISTINCT_ID, distinctId);
546
616
  sessionStorage.setItem(CONFIG.STORAGE_KEYS.DISTINCT_ID, distinctId);
617
+
618
+ // Update cache
619
+ _cachedDistinctId = distinctId;
620
+
621
+ const type = distinctId.startsWith('anon_') ? 'anonymous' : 'identified';
622
+ console.log(`✅ [Storage] Stored ${type} distinct_id in localStorage and sessionStorage: ${distinctId}`);
547
623
  } catch (err) {
548
624
  console.warn('Failed to set distinct_id:', err);
549
625
  }
550
626
  },
551
627
 
628
+ /**
629
+ * Clear distinct_id cache (used during reset)
630
+ */
631
+ clearDistinctIdCache() {
632
+ _cachedDistinctId = null;
633
+ },
634
+
552
635
  /**
553
636
  * Get user consent status
554
637
  *
@@ -803,6 +886,7 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
803
886
  siteId: getCurrentSiteId(),
804
887
  teamId: null, // May be set externally or from backend
805
888
  userId: null, // Will be set from StorageManager
889
+ distinctId: null, // Will be set from StorageManager.getDistinctId()
806
890
 
807
891
  // Time fields (internal camelCase)
808
892
  startTime: null, // ISO string
@@ -1760,6 +1844,9 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
1760
1844
  if (!sessionData.siteId) sessionData.siteId = currentSiteId;
1761
1845
  if (!sessionData.teamId) sessionData.teamId = null;
1762
1846
  if (!sessionData.userId) sessionData.userId = null;
1847
+ // Always sync distinctId from storage to ensure consistency (like user_id)
1848
+ // This ensures we use distinct_id from past sessions if it exists
1849
+ sessionData.distinctId = StorageManager.getDistinctId();
1763
1850
 
1764
1851
  // Time fields
1765
1852
  if (!sessionData.startTime) sessionData.startTime = nowIso();
@@ -3868,6 +3955,7 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
3868
3955
  site_id: sourceData.site_id || sourceData.siteId || getCurrentSiteId() || null,
3869
3956
  team_id: sourceData.team_id || sourceData.teamId || null,
3870
3957
  user_id: sourceData.user_id || sourceData.userId || null,
3958
+ distinct_id: sourceData.distinct_id || sourceData.distinctId || null,
3871
3959
 
3872
3960
  // Time fields (snake_case)
3873
3961
  start_time: sourceData.start_time || sourceData.startTime || null,
@@ -4152,6 +4240,10 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
4152
4240
  sessionDataToSend.sessionId = SessionIdManager.getOrCreate();
4153
4241
  }
4154
4242
 
4243
+ // Always sync distinctId from storage before sending to backend
4244
+ sessionDataToSend.distinctId = StorageManager.getDistinctId();
4245
+
4246
+
4155
4247
  // Transform to backend format
4156
4248
  const transformedData = DataTransformer.toBackendFormat(sessionDataToSend);
4157
4249
 
@@ -5329,6 +5421,9 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
5329
5421
  // Save session to storage
5330
5422
  StorageManager.saveSession(sessionData);
5331
5423
 
5424
+ // Always sync distinctId from storage before logging/sending to ensure consistency
5425
+ sessionData.distinctId = StorageManager.getDistinctId();
5426
+
5332
5427
  // Console log raw session data intermittently
5333
5428
  console.log('📊 [Session] Raw Session Data:', {
5334
5429
  sessionData: sessionData,
@@ -5975,31 +6070,52 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
5975
6070
 
5976
6071
  const result = await response.json();
5977
6072
 
5978
- // If merge occurred or distinct_id changed, update sessionStorage and session data
5979
- if (result.merged && result.newDistinctId) {
5980
- // Update sessionStorage with new distinct_id
5981
- StorageManager.setDistinctId(result.newDistinctId);
6073
+ // Always update distinct_id if present in response
6074
+ // Prioritize result.distinctId over result.newDistinctId
6075
+ if (result.distinctId) {
6076
+ // Update to identified distinct_id
6077
+ StorageManager.setDistinctId(result.distinctId);
5982
6078
 
5983
6079
  // Update session data
5984
6080
  if (sessionData) {
5985
- sessionData.distinctId = result.newDistinctId;
6081
+ sessionData.distinctId = result.distinctId;
5986
6082
  sessionData.anonymous = false;
5987
6083
  sessionData.identified = true;
5988
6084
  }
5989
6085
 
5990
- console.log(`✅ [Identity] Distinct ID merged: ${currentDistinctId} → ${result.newDistinctId}`);
5991
- } else if (result.distinctId) {
5992
- // Update to identified distinct_id
5993
- StorageManager.setDistinctId(result.distinctId);
6086
+ // Update stored session
6087
+ const session = StorageManager.loadSession();
6088
+ if (session) {
6089
+ session.distinctId = result.distinctId;
6090
+ session.anonymous = false;
6091
+ session.identified = true;
6092
+ StorageManager.saveSession(session);
6093
+ }
6094
+
6095
+ console.log(`✅ [Identity] Distinct ID updated: ${currentDistinctId} → ${result.distinctId}`);
6096
+ } else if (result.merged && result.newDistinctId) {
6097
+ // Fallback: use newDistinctId if distinctId not present
6098
+ StorageManager.setDistinctId(result.newDistinctId);
5994
6099
 
5995
6100
  // Update session data
5996
6101
  if (sessionData) {
5997
- sessionData.distinctId = result.distinctId;
6102
+ sessionData.distinctId = result.newDistinctId;
5998
6103
  sessionData.anonymous = false;
5999
6104
  sessionData.identified = true;
6000
6105
  }
6001
6106
 
6002
- console.log(`✅ [Identity] Distinct ID updated: ${currentDistinctId} → ${result.distinctId}`);
6107
+ // Update stored session
6108
+ const session = StorageManager.loadSession();
6109
+ if (session) {
6110
+ session.distinctId = result.newDistinctId;
6111
+ session.anonymous = false;
6112
+ session.identified = true;
6113
+ StorageManager.saveSession(session);
6114
+ }
6115
+
6116
+ console.log(`✅ [Identity] Distinct ID merged: ${currentDistinctId} → ${result.newDistinctId}`);
6117
+ } else {
6118
+ console.warn('⚠️ [Identity] No distinctId or newDistinctId in response:', result);
6003
6119
  }
6004
6120
 
6005
6121
  return result;
@@ -6012,43 +6128,92 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
6012
6128
  /**
6013
6129
  * Reset identity (logout/anonymous)
6014
6130
  * Generates new anonymous distinct_id and clears identified state
6131
+ * Ensures new anonymous identity exists in backend
6015
6132
  *
6016
6133
  * @returns {Promise<Object>} Result object with new distinct_id
6017
6134
  */
6018
6135
  async reset() {
6019
6136
  try {
6137
+ // Get current session and user data
6138
+ const session = StorageManager.loadSession();
6139
+ if (!session || !session.id) {
6140
+ console.error('❌ [Identity] No active session found');
6141
+ return { success: false, error: 'No active session' };
6142
+ }
6143
+
6144
+ const currentUserId = StorageManager.getUserId();
6145
+ if (!currentUserId) {
6146
+ console.error('❌ [Identity] No user ID found');
6147
+ return { success: false, error: 'No user ID' };
6148
+ }
6149
+
6150
+ const currentSiteId = getCurrentSiteId();
6151
+ if (!currentSiteId) {
6152
+ console.error('❌ [Identity] Site ID not found');
6153
+ return { success: false, error: 'Site ID not found' };
6154
+ }
6155
+
6020
6156
  // Generate new anonymous distinct_id
6021
- const userId = StorageManager.getUserId();
6022
- const newDistinctId = StorageManager.generateAnonymousDistinctId(userId);
6023
-
6024
- // Update sessionStorage
6025
- StorageManager.setDistinctId(newDistinctId);
6157
+ const newDistinctId = StorageManager.generateAnonymousDistinctId(currentUserId);
6026
6158
 
6027
- // Update session data
6028
- const session = StorageManager.loadSession();
6029
- if (session) {
6030
- session.distinctId = newDistinctId;
6031
- session.anonymous = true;
6032
- session.identified = false;
6033
- StorageManager.saveSession(session);
6159
+ // Call backend endpoint to ensure new anonymous identity exists
6160
+ const apiUrl = CONFIG.API.TRACK.replace('/track', '/reset');
6161
+ const response = await fetch(apiUrl, {
6162
+ method: 'POST',
6163
+ headers: {
6164
+ 'Content-Type': 'application/json',
6165
+ 'X-Cryptique-Site-Id': currentSiteId
6166
+ },
6167
+ body: JSON.stringify({
6168
+ siteId: currentSiteId,
6169
+ sessionId: session.id,
6170
+ userId: currentUserId,
6171
+ distinctId: newDistinctId
6172
+ })
6173
+ });
6174
+
6175
+ if (!response.ok) {
6176
+ const errorData = await response.text();
6177
+ console.error('❌ [Identity] Reset API error:', {
6178
+ status: response.status,
6179
+ statusText: response.statusText,
6180
+ error: errorData
6181
+ });
6182
+ throw new Error(`Reset API error: ${response.status} ${response.statusText} - ${errorData}`);
6034
6183
  }
6184
+
6185
+ const result = await response.json();
6186
+
6187
+ // Update local storage after backend confirms
6188
+ StorageManager.setDistinctId(newDistinctId);
6189
+ StorageManager.clearDistinctIdCache();
6035
6190
 
6036
- // Update sessionData if available
6191
+ // Update sessionData
6037
6192
  if (sessionData) {
6038
6193
  sessionData.distinctId = newDistinctId;
6039
6194
  sessionData.anonymous = true;
6040
6195
  sessionData.identified = false;
6041
6196
  }
6042
6197
 
6198
+ // Update stored session
6199
+ const updatedSession = StorageManager.loadSession();
6200
+ if (updatedSession) {
6201
+ updatedSession.distinctId = newDistinctId;
6202
+ updatedSession.anonymous = true;
6203
+ updatedSession.identified = false;
6204
+ StorageManager.saveSession(updatedSession);
6205
+ }
6206
+
6043
6207
  // Clear identify_id from storage (if stored separately)
6044
6208
  try {
6045
6209
  sessionStorage.removeItem('cryptique_identify_id');
6210
+ localStorage.removeItem('cryptique_identify_id');
6046
6211
  } catch (err) {
6047
6212
  // Ignore errors
6048
6213
  }
6049
6214
 
6050
6215
  console.log(`🔄 [Identity] Reset to anonymous: ${newDistinctId}`);
6051
-
6216
+
6052
6217
  return { success: true, distinctId: newDistinctId };
6053
6218
  } catch (error) {
6054
6219
  console.error('❌ [Identity] Error in reset():', error);
package/lib/esm/index.js CHANGED
@@ -203,6 +203,11 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
203
203
  * - Easy to add features (encryption, compression, etc.)
204
204
  * - Single place to fix storage bugs
205
205
  */
206
+ // In-memory cache to prevent race conditions in distinct_id generation
207
+ let _cachedDistinctId = null;
208
+ // Synchronous lock flag to prevent concurrent distinct_id generation
209
+ let _isGeneratingDistinctId = false;
210
+
206
211
  const StorageManager = {
207
212
  /**
208
213
  * Load session from sessionStorage with localStorage backup
@@ -509,44 +514,122 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
509
514
 
510
515
  /**
511
516
  * Get or create distinct_id
512
- * Checks sessionStorage first, then generates new anonymous distinct_id if not found
517
+ * All distinct_ids (anonymous and identified) persist in localStorage
518
+ * Uses in-memory cache to prevent race conditions
513
519
  *
514
- * @returns {string} Current distinct_id
520
+ * @returns {string|Promise<string>} Current distinct_id (may be Promise if generation is in progress)
515
521
  */
516
522
  getDistinctId() {
517
523
  try {
518
- // Check sessionStorage first (primary source)
519
- let distinctId = sessionStorage.getItem(CONFIG.STORAGE_KEYS.DISTINCT_ID);
524
+ // Return cached value if we already generated one in this session
525
+ // This prevents race conditions during initialization
526
+ if (_cachedDistinctId) {
527
+ return _cachedDistinctId;
528
+ }
520
529
 
521
- if (!distinctId) {
522
- // Generate new anonymous distinct_id
530
+ // If generation is in progress, return cached or wait briefly and retry
531
+ if (_isGeneratingDistinctId) {
532
+ // Check cache again - it might have been set by the concurrent call
533
+ if (_cachedDistinctId) {
534
+ return _cachedDistinctId;
535
+ }
536
+ // If still generating, return a temporary value (shouldn't happen in practice)
537
+ // This prevents infinite loops
538
+ const tempId = `temp_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
539
+ console.warn('⚠️ [Storage] Concurrent distinctId generation detected, returning temporary ID');
540
+ return tempId;
541
+ }
542
+
543
+ // First check localStorage (persists across sessions for both anonymous and identified)
544
+ let distinctId = localStorage.getItem(CONFIG.STORAGE_KEYS.DISTINCT_ID);
545
+
546
+ if (distinctId) {
547
+ // Found distinct_id in localStorage - use it and sync to sessionStorage
548
+ sessionStorage.setItem(CONFIG.STORAGE_KEYS.DISTINCT_ID, distinctId);
549
+ _cachedDistinctId = distinctId; // Cache it
550
+ return distinctId;
551
+ }
552
+
553
+ // Check sessionStorage as fallback (for current session only)
554
+ distinctId = sessionStorage.getItem(CONFIG.STORAGE_KEYS.DISTINCT_ID);
555
+
556
+ if (distinctId) {
557
+ // Found in sessionStorage - also store in localStorage for persistence
558
+ localStorage.setItem(CONFIG.STORAGE_KEYS.DISTINCT_ID, distinctId);
559
+ _cachedDistinctId = distinctId; // Cache it
560
+ return distinctId;
561
+ }
562
+
563
+ // No distinct_id found - generate new anonymous one
564
+ // Set lock BEFORE generating to prevent concurrent generation
565
+ _isGeneratingDistinctId = true;
566
+ try {
567
+ // Double-check localStorage after acquiring lock (another call might have stored it)
568
+ let checkAgain = localStorage.getItem(CONFIG.STORAGE_KEYS.DISTINCT_ID);
569
+ if (checkAgain) {
570
+ sessionStorage.setItem(CONFIG.STORAGE_KEYS.DISTINCT_ID, checkAgain);
571
+ _cachedDistinctId = checkAgain;
572
+ return checkAgain;
573
+ }
574
+
523
575
  const userId = this.getUserId();
524
576
  distinctId = this.generateAnonymousDistinctId(userId);
577
+
578
+ // Store in both localStorage (persist) and sessionStorage (current session)
579
+ localStorage.setItem(CONFIG.STORAGE_KEYS.DISTINCT_ID, distinctId);
525
580
  sessionStorage.setItem(CONFIG.STORAGE_KEYS.DISTINCT_ID, distinctId);
581
+
582
+ _cachedDistinctId = distinctId; // Cache it
583
+ return distinctId;
584
+ } finally {
585
+ _isGeneratingDistinctId = false;
526
586
  }
527
-
528
- return distinctId;
529
587
  } catch (err) {
530
588
  // Fallback: generate temporary distinct_id
531
- console.warn('Failed to access sessionStorage for distinct_id:', err);
589
+ console.warn('Failed to access storage for distinct_id:', err);
532
590
  const userId = this.getUserId();
533
- return this.generateAnonymousDistinctId(userId);
591
+ const fallbackId = this.generateAnonymousDistinctId(userId);
592
+ _cachedDistinctId = fallbackId; // Cache even fallback
593
+ _isGeneratingDistinctId = false;
594
+ return fallbackId;
534
595
  }
535
596
  },
536
597
 
537
598
  /**
538
- * Set distinct_id in sessionStorage
599
+ * Set distinct_id in storage
600
+ * All distinct_ids (anonymous and identified) are stored in both localStorage and sessionStorage
601
+ * Updates in-memory cache to maintain consistency
539
602
  *
540
603
  * @param {string} distinctId - Distinct ID to set
541
604
  */
542
605
  setDistinctId(distinctId) {
543
606
  try {
607
+ if (!distinctId) {
608
+ console.warn('⚠️ [Storage] setDistinctId called with null/undefined distinctId');
609
+ return;
610
+ }
611
+
612
+ // Store in both localStorage (persists across sessions) and sessionStorage (current session)
613
+ localStorage.setItem(CONFIG.STORAGE_KEYS.DISTINCT_ID, distinctId);
544
614
  sessionStorage.setItem(CONFIG.STORAGE_KEYS.DISTINCT_ID, distinctId);
615
+
616
+ // Update cache
617
+ _cachedDistinctId = distinctId;
618
+
619
+ const type = distinctId.startsWith('anon_') ? 'anonymous' : 'identified';
620
+ console.log(`✅ [Storage] Stored ${type} distinct_id in localStorage and sessionStorage: ${distinctId}`);
545
621
  } catch (err) {
546
622
  console.warn('Failed to set distinct_id:', err);
547
623
  }
548
624
  },
549
625
 
626
+ /**
627
+ * Clear distinct_id cache (used during reset)
628
+ */
629
+ clearDistinctIdCache() {
630
+ _cachedDistinctId = null;
631
+ },
632
+
550
633
  /**
551
634
  * Get user consent status
552
635
  *
@@ -801,6 +884,7 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
801
884
  siteId: getCurrentSiteId(),
802
885
  teamId: null, // May be set externally or from backend
803
886
  userId: null, // Will be set from StorageManager
887
+ distinctId: null, // Will be set from StorageManager.getDistinctId()
804
888
 
805
889
  // Time fields (internal camelCase)
806
890
  startTime: null, // ISO string
@@ -1758,6 +1842,9 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
1758
1842
  if (!sessionData.siteId) sessionData.siteId = currentSiteId;
1759
1843
  if (!sessionData.teamId) sessionData.teamId = null;
1760
1844
  if (!sessionData.userId) sessionData.userId = null;
1845
+ // Always sync distinctId from storage to ensure consistency (like user_id)
1846
+ // This ensures we use distinct_id from past sessions if it exists
1847
+ sessionData.distinctId = StorageManager.getDistinctId();
1761
1848
 
1762
1849
  // Time fields
1763
1850
  if (!sessionData.startTime) sessionData.startTime = nowIso();
@@ -3866,6 +3953,7 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
3866
3953
  site_id: sourceData.site_id || sourceData.siteId || getCurrentSiteId() || null,
3867
3954
  team_id: sourceData.team_id || sourceData.teamId || null,
3868
3955
  user_id: sourceData.user_id || sourceData.userId || null,
3956
+ distinct_id: sourceData.distinct_id || sourceData.distinctId || null,
3869
3957
 
3870
3958
  // Time fields (snake_case)
3871
3959
  start_time: sourceData.start_time || sourceData.startTime || null,
@@ -4150,6 +4238,10 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
4150
4238
  sessionDataToSend.sessionId = SessionIdManager.getOrCreate();
4151
4239
  }
4152
4240
 
4241
+ // Always sync distinctId from storage before sending to backend
4242
+ sessionDataToSend.distinctId = StorageManager.getDistinctId();
4243
+
4244
+
4153
4245
  // Transform to backend format
4154
4246
  const transformedData = DataTransformer.toBackendFormat(sessionDataToSend);
4155
4247
 
@@ -5327,6 +5419,9 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
5327
5419
  // Save session to storage
5328
5420
  StorageManager.saveSession(sessionData);
5329
5421
 
5422
+ // Always sync distinctId from storage before logging/sending to ensure consistency
5423
+ sessionData.distinctId = StorageManager.getDistinctId();
5424
+
5330
5425
  // Console log raw session data intermittently
5331
5426
  console.log('📊 [Session] Raw Session Data:', {
5332
5427
  sessionData: sessionData,
@@ -5973,31 +6068,52 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
5973
6068
 
5974
6069
  const result = await response.json();
5975
6070
 
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);
6071
+ // Always update distinct_id if present in response
6072
+ // Prioritize result.distinctId over result.newDistinctId
6073
+ if (result.distinctId) {
6074
+ // Update to identified distinct_id
6075
+ StorageManager.setDistinctId(result.distinctId);
5980
6076
 
5981
6077
  // Update session data
5982
6078
  if (sessionData) {
5983
- sessionData.distinctId = result.newDistinctId;
6079
+ sessionData.distinctId = result.distinctId;
5984
6080
  sessionData.anonymous = false;
5985
6081
  sessionData.identified = true;
5986
6082
  }
5987
6083
 
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);
6084
+ // Update stored session
6085
+ const session = StorageManager.loadSession();
6086
+ if (session) {
6087
+ session.distinctId = result.distinctId;
6088
+ session.anonymous = false;
6089
+ session.identified = true;
6090
+ StorageManager.saveSession(session);
6091
+ }
6092
+
6093
+ console.log(`✅ [Identity] Distinct ID updated: ${currentDistinctId} → ${result.distinctId}`);
6094
+ } else if (result.merged && result.newDistinctId) {
6095
+ // Fallback: use newDistinctId if distinctId not present
6096
+ StorageManager.setDistinctId(result.newDistinctId);
5992
6097
 
5993
6098
  // Update session data
5994
6099
  if (sessionData) {
5995
- sessionData.distinctId = result.distinctId;
6100
+ sessionData.distinctId = result.newDistinctId;
5996
6101
  sessionData.anonymous = false;
5997
6102
  sessionData.identified = true;
5998
6103
  }
5999
6104
 
6000
- console.log(`✅ [Identity] Distinct ID updated: ${currentDistinctId} → ${result.distinctId}`);
6105
+ // Update stored session
6106
+ const session = StorageManager.loadSession();
6107
+ if (session) {
6108
+ session.distinctId = result.newDistinctId;
6109
+ session.anonymous = false;
6110
+ session.identified = true;
6111
+ StorageManager.saveSession(session);
6112
+ }
6113
+
6114
+ console.log(`✅ [Identity] Distinct ID merged: ${currentDistinctId} → ${result.newDistinctId}`);
6115
+ } else {
6116
+ console.warn('⚠️ [Identity] No distinctId or newDistinctId in response:', result);
6001
6117
  }
6002
6118
 
6003
6119
  return result;
@@ -6010,43 +6126,92 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
6010
6126
  /**
6011
6127
  * Reset identity (logout/anonymous)
6012
6128
  * Generates new anonymous distinct_id and clears identified state
6129
+ * Ensures new anonymous identity exists in backend
6013
6130
  *
6014
6131
  * @returns {Promise<Object>} Result object with new distinct_id
6015
6132
  */
6016
6133
  async reset() {
6017
6134
  try {
6135
+ // Get current session and user data
6136
+ const session = StorageManager.loadSession();
6137
+ if (!session || !session.id) {
6138
+ console.error('❌ [Identity] No active session found');
6139
+ return { success: false, error: 'No active session' };
6140
+ }
6141
+
6142
+ const currentUserId = StorageManager.getUserId();
6143
+ if (!currentUserId) {
6144
+ console.error('❌ [Identity] No user ID found');
6145
+ return { success: false, error: 'No user ID' };
6146
+ }
6147
+
6148
+ const currentSiteId = getCurrentSiteId();
6149
+ if (!currentSiteId) {
6150
+ console.error('❌ [Identity] Site ID not found');
6151
+ return { success: false, error: 'Site ID not found' };
6152
+ }
6153
+
6018
6154
  // Generate new anonymous distinct_id
6019
- const userId = StorageManager.getUserId();
6020
- const newDistinctId = StorageManager.generateAnonymousDistinctId(userId);
6021
-
6022
- // Update sessionStorage
6023
- StorageManager.setDistinctId(newDistinctId);
6155
+ const newDistinctId = StorageManager.generateAnonymousDistinctId(currentUserId);
6024
6156
 
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);
6157
+ // Call backend endpoint to ensure new anonymous identity exists
6158
+ const apiUrl = CONFIG.API.TRACK.replace('/track', '/reset');
6159
+ const response = await fetch(apiUrl, {
6160
+ method: 'POST',
6161
+ headers: {
6162
+ 'Content-Type': 'application/json',
6163
+ 'X-Cryptique-Site-Id': currentSiteId
6164
+ },
6165
+ body: JSON.stringify({
6166
+ siteId: currentSiteId,
6167
+ sessionId: session.id,
6168
+ userId: currentUserId,
6169
+ distinctId: newDistinctId
6170
+ })
6171
+ });
6172
+
6173
+ if (!response.ok) {
6174
+ const errorData = await response.text();
6175
+ console.error('❌ [Identity] Reset API error:', {
6176
+ status: response.status,
6177
+ statusText: response.statusText,
6178
+ error: errorData
6179
+ });
6180
+ throw new Error(`Reset API error: ${response.status} ${response.statusText} - ${errorData}`);
6032
6181
  }
6182
+
6183
+ const result = await response.json();
6184
+
6185
+ // Update local storage after backend confirms
6186
+ StorageManager.setDistinctId(newDistinctId);
6187
+ StorageManager.clearDistinctIdCache();
6033
6188
 
6034
- // Update sessionData if available
6189
+ // Update sessionData
6035
6190
  if (sessionData) {
6036
6191
  sessionData.distinctId = newDistinctId;
6037
6192
  sessionData.anonymous = true;
6038
6193
  sessionData.identified = false;
6039
6194
  }
6040
6195
 
6196
+ // Update stored session
6197
+ const updatedSession = StorageManager.loadSession();
6198
+ if (updatedSession) {
6199
+ updatedSession.distinctId = newDistinctId;
6200
+ updatedSession.anonymous = true;
6201
+ updatedSession.identified = false;
6202
+ StorageManager.saveSession(updatedSession);
6203
+ }
6204
+
6041
6205
  // Clear identify_id from storage (if stored separately)
6042
6206
  try {
6043
6207
  sessionStorage.removeItem('cryptique_identify_id');
6208
+ localStorage.removeItem('cryptique_identify_id');
6044
6209
  } catch (err) {
6045
6210
  // Ignore errors
6046
6211
  }
6047
6212
 
6048
6213
  console.log(`🔄 [Identity] Reset to anonymous: ${newDistinctId}`);
6049
-
6214
+
6050
6215
  return { success: true, distinctId: newDistinctId };
6051
6216
  } catch (error) {
6052
6217
  console.error('❌ [Identity] Error in reset():', error);
package/lib/umd/index.js CHANGED
@@ -209,6 +209,11 @@
209
209
  * - Easy to add features (encryption, compression, etc.)
210
210
  * - Single place to fix storage bugs
211
211
  */
212
+ // In-memory cache to prevent race conditions in distinct_id generation
213
+ let _cachedDistinctId = null;
214
+ // Synchronous lock flag to prevent concurrent distinct_id generation
215
+ let _isGeneratingDistinctId = false;
216
+
212
217
  const StorageManager = {
213
218
  /**
214
219
  * Load session from sessionStorage with localStorage backup
@@ -515,44 +520,122 @@
515
520
 
516
521
  /**
517
522
  * Get or create distinct_id
518
- * Checks sessionStorage first, then generates new anonymous distinct_id if not found
523
+ * All distinct_ids (anonymous and identified) persist in localStorage
524
+ * Uses in-memory cache to prevent race conditions
519
525
  *
520
- * @returns {string} Current distinct_id
526
+ * @returns {string|Promise<string>} Current distinct_id (may be Promise if generation is in progress)
521
527
  */
522
528
  getDistinctId() {
523
529
  try {
524
- // Check sessionStorage first (primary source)
525
- let distinctId = sessionStorage.getItem(CONFIG.STORAGE_KEYS.DISTINCT_ID);
530
+ // Return cached value if we already generated one in this session
531
+ // This prevents race conditions during initialization
532
+ if (_cachedDistinctId) {
533
+ return _cachedDistinctId;
534
+ }
526
535
 
527
- if (!distinctId) {
528
- // Generate new anonymous distinct_id
536
+ // If generation is in progress, return cached or wait briefly and retry
537
+ if (_isGeneratingDistinctId) {
538
+ // Check cache again - it might have been set by the concurrent call
539
+ if (_cachedDistinctId) {
540
+ return _cachedDistinctId;
541
+ }
542
+ // If still generating, return a temporary value (shouldn't happen in practice)
543
+ // This prevents infinite loops
544
+ const tempId = `temp_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
545
+ console.warn('⚠️ [Storage] Concurrent distinctId generation detected, returning temporary ID');
546
+ return tempId;
547
+ }
548
+
549
+ // First check localStorage (persists across sessions for both anonymous and identified)
550
+ let distinctId = localStorage.getItem(CONFIG.STORAGE_KEYS.DISTINCT_ID);
551
+
552
+ if (distinctId) {
553
+ // Found distinct_id in localStorage - use it and sync to sessionStorage
554
+ sessionStorage.setItem(CONFIG.STORAGE_KEYS.DISTINCT_ID, distinctId);
555
+ _cachedDistinctId = distinctId; // Cache it
556
+ return distinctId;
557
+ }
558
+
559
+ // Check sessionStorage as fallback (for current session only)
560
+ distinctId = sessionStorage.getItem(CONFIG.STORAGE_KEYS.DISTINCT_ID);
561
+
562
+ if (distinctId) {
563
+ // Found in sessionStorage - also store in localStorage for persistence
564
+ localStorage.setItem(CONFIG.STORAGE_KEYS.DISTINCT_ID, distinctId);
565
+ _cachedDistinctId = distinctId; // Cache it
566
+ return distinctId;
567
+ }
568
+
569
+ // No distinct_id found - generate new anonymous one
570
+ // Set lock BEFORE generating to prevent concurrent generation
571
+ _isGeneratingDistinctId = true;
572
+ try {
573
+ // Double-check localStorage after acquiring lock (another call might have stored it)
574
+ let checkAgain = localStorage.getItem(CONFIG.STORAGE_KEYS.DISTINCT_ID);
575
+ if (checkAgain) {
576
+ sessionStorage.setItem(CONFIG.STORAGE_KEYS.DISTINCT_ID, checkAgain);
577
+ _cachedDistinctId = checkAgain;
578
+ return checkAgain;
579
+ }
580
+
529
581
  const userId = this.getUserId();
530
582
  distinctId = this.generateAnonymousDistinctId(userId);
583
+
584
+ // Store in both localStorage (persist) and sessionStorage (current session)
585
+ localStorage.setItem(CONFIG.STORAGE_KEYS.DISTINCT_ID, distinctId);
531
586
  sessionStorage.setItem(CONFIG.STORAGE_KEYS.DISTINCT_ID, distinctId);
587
+
588
+ _cachedDistinctId = distinctId; // Cache it
589
+ return distinctId;
590
+ } finally {
591
+ _isGeneratingDistinctId = false;
532
592
  }
533
-
534
- return distinctId;
535
593
  } catch (err) {
536
594
  // Fallback: generate temporary distinct_id
537
- console.warn('Failed to access sessionStorage for distinct_id:', err);
595
+ console.warn('Failed to access storage for distinct_id:', err);
538
596
  const userId = this.getUserId();
539
- return this.generateAnonymousDistinctId(userId);
597
+ const fallbackId = this.generateAnonymousDistinctId(userId);
598
+ _cachedDistinctId = fallbackId; // Cache even fallback
599
+ _isGeneratingDistinctId = false;
600
+ return fallbackId;
540
601
  }
541
602
  },
542
603
 
543
604
  /**
544
- * Set distinct_id in sessionStorage
605
+ * Set distinct_id in storage
606
+ * All distinct_ids (anonymous and identified) are stored in both localStorage and sessionStorage
607
+ * Updates in-memory cache to maintain consistency
545
608
  *
546
609
  * @param {string} distinctId - Distinct ID to set
547
610
  */
548
611
  setDistinctId(distinctId) {
549
612
  try {
613
+ if (!distinctId) {
614
+ console.warn('⚠️ [Storage] setDistinctId called with null/undefined distinctId');
615
+ return;
616
+ }
617
+
618
+ // Store in both localStorage (persists across sessions) and sessionStorage (current session)
619
+ localStorage.setItem(CONFIG.STORAGE_KEYS.DISTINCT_ID, distinctId);
550
620
  sessionStorage.setItem(CONFIG.STORAGE_KEYS.DISTINCT_ID, distinctId);
621
+
622
+ // Update cache
623
+ _cachedDistinctId = distinctId;
624
+
625
+ const type = distinctId.startsWith('anon_') ? 'anonymous' : 'identified';
626
+ console.log(`✅ [Storage] Stored ${type} distinct_id in localStorage and sessionStorage: ${distinctId}`);
551
627
  } catch (err) {
552
628
  console.warn('Failed to set distinct_id:', err);
553
629
  }
554
630
  },
555
631
 
632
+ /**
633
+ * Clear distinct_id cache (used during reset)
634
+ */
635
+ clearDistinctIdCache() {
636
+ _cachedDistinctId = null;
637
+ },
638
+
556
639
  /**
557
640
  * Get user consent status
558
641
  *
@@ -807,6 +890,7 @@
807
890
  siteId: getCurrentSiteId(),
808
891
  teamId: null, // May be set externally or from backend
809
892
  userId: null, // Will be set from StorageManager
893
+ distinctId: null, // Will be set from StorageManager.getDistinctId()
810
894
 
811
895
  // Time fields (internal camelCase)
812
896
  startTime: null, // ISO string
@@ -1764,6 +1848,9 @@
1764
1848
  if (!sessionData.siteId) sessionData.siteId = currentSiteId;
1765
1849
  if (!sessionData.teamId) sessionData.teamId = null;
1766
1850
  if (!sessionData.userId) sessionData.userId = null;
1851
+ // Always sync distinctId from storage to ensure consistency (like user_id)
1852
+ // This ensures we use distinct_id from past sessions if it exists
1853
+ sessionData.distinctId = StorageManager.getDistinctId();
1767
1854
 
1768
1855
  // Time fields
1769
1856
  if (!sessionData.startTime) sessionData.startTime = nowIso();
@@ -3872,6 +3959,7 @@
3872
3959
  site_id: sourceData.site_id || sourceData.siteId || getCurrentSiteId() || null,
3873
3960
  team_id: sourceData.team_id || sourceData.teamId || null,
3874
3961
  user_id: sourceData.user_id || sourceData.userId || null,
3962
+ distinct_id: sourceData.distinct_id || sourceData.distinctId || null,
3875
3963
 
3876
3964
  // Time fields (snake_case)
3877
3965
  start_time: sourceData.start_time || sourceData.startTime || null,
@@ -4156,6 +4244,10 @@
4156
4244
  sessionDataToSend.sessionId = SessionIdManager.getOrCreate();
4157
4245
  }
4158
4246
 
4247
+ // Always sync distinctId from storage before sending to backend
4248
+ sessionDataToSend.distinctId = StorageManager.getDistinctId();
4249
+
4250
+
4159
4251
  // Transform to backend format
4160
4252
  const transformedData = DataTransformer.toBackendFormat(sessionDataToSend);
4161
4253
 
@@ -5333,6 +5425,9 @@
5333
5425
  // Save session to storage
5334
5426
  StorageManager.saveSession(sessionData);
5335
5427
 
5428
+ // Always sync distinctId from storage before logging/sending to ensure consistency
5429
+ sessionData.distinctId = StorageManager.getDistinctId();
5430
+
5336
5431
  // Console log raw session data intermittently
5337
5432
  console.log('📊 [Session] Raw Session Data:', {
5338
5433
  sessionData: sessionData,
@@ -5979,31 +6074,52 @@
5979
6074
 
5980
6075
  const result = await response.json();
5981
6076
 
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);
6077
+ // Always update distinct_id if present in response
6078
+ // Prioritize result.distinctId over result.newDistinctId
6079
+ if (result.distinctId) {
6080
+ // Update to identified distinct_id
6081
+ StorageManager.setDistinctId(result.distinctId);
5986
6082
 
5987
6083
  // Update session data
5988
6084
  if (sessionData) {
5989
- sessionData.distinctId = result.newDistinctId;
6085
+ sessionData.distinctId = result.distinctId;
5990
6086
  sessionData.anonymous = false;
5991
6087
  sessionData.identified = true;
5992
6088
  }
5993
6089
 
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);
6090
+ // Update stored session
6091
+ const session = StorageManager.loadSession();
6092
+ if (session) {
6093
+ session.distinctId = result.distinctId;
6094
+ session.anonymous = false;
6095
+ session.identified = true;
6096
+ StorageManager.saveSession(session);
6097
+ }
6098
+
6099
+ console.log(`✅ [Identity] Distinct ID updated: ${currentDistinctId} → ${result.distinctId}`);
6100
+ } else if (result.merged && result.newDistinctId) {
6101
+ // Fallback: use newDistinctId if distinctId not present
6102
+ StorageManager.setDistinctId(result.newDistinctId);
5998
6103
 
5999
6104
  // Update session data
6000
6105
  if (sessionData) {
6001
- sessionData.distinctId = result.distinctId;
6106
+ sessionData.distinctId = result.newDistinctId;
6002
6107
  sessionData.anonymous = false;
6003
6108
  sessionData.identified = true;
6004
6109
  }
6005
6110
 
6006
- console.log(`✅ [Identity] Distinct ID updated: ${currentDistinctId} → ${result.distinctId}`);
6111
+ // Update stored session
6112
+ const session = StorageManager.loadSession();
6113
+ if (session) {
6114
+ session.distinctId = result.newDistinctId;
6115
+ session.anonymous = false;
6116
+ session.identified = true;
6117
+ StorageManager.saveSession(session);
6118
+ }
6119
+
6120
+ console.log(`✅ [Identity] Distinct ID merged: ${currentDistinctId} → ${result.newDistinctId}`);
6121
+ } else {
6122
+ console.warn('⚠️ [Identity] No distinctId or newDistinctId in response:', result);
6007
6123
  }
6008
6124
 
6009
6125
  return result;
@@ -6016,43 +6132,92 @@
6016
6132
  /**
6017
6133
  * Reset identity (logout/anonymous)
6018
6134
  * Generates new anonymous distinct_id and clears identified state
6135
+ * Ensures new anonymous identity exists in backend
6019
6136
  *
6020
6137
  * @returns {Promise<Object>} Result object with new distinct_id
6021
6138
  */
6022
6139
  async reset() {
6023
6140
  try {
6141
+ // Get current session and user data
6142
+ const session = StorageManager.loadSession();
6143
+ if (!session || !session.id) {
6144
+ console.error('❌ [Identity] No active session found');
6145
+ return { success: false, error: 'No active session' };
6146
+ }
6147
+
6148
+ const currentUserId = StorageManager.getUserId();
6149
+ if (!currentUserId) {
6150
+ console.error('❌ [Identity] No user ID found');
6151
+ return { success: false, error: 'No user ID' };
6152
+ }
6153
+
6154
+ const currentSiteId = getCurrentSiteId();
6155
+ if (!currentSiteId) {
6156
+ console.error('❌ [Identity] Site ID not found');
6157
+ return { success: false, error: 'Site ID not found' };
6158
+ }
6159
+
6024
6160
  // Generate new anonymous distinct_id
6025
- const userId = StorageManager.getUserId();
6026
- const newDistinctId = StorageManager.generateAnonymousDistinctId(userId);
6027
-
6028
- // Update sessionStorage
6029
- StorageManager.setDistinctId(newDistinctId);
6161
+ const newDistinctId = StorageManager.generateAnonymousDistinctId(currentUserId);
6030
6162
 
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);
6163
+ // Call backend endpoint to ensure new anonymous identity exists
6164
+ const apiUrl = CONFIG.API.TRACK.replace('/track', '/reset');
6165
+ const response = await fetch(apiUrl, {
6166
+ method: 'POST',
6167
+ headers: {
6168
+ 'Content-Type': 'application/json',
6169
+ 'X-Cryptique-Site-Id': currentSiteId
6170
+ },
6171
+ body: JSON.stringify({
6172
+ siteId: currentSiteId,
6173
+ sessionId: session.id,
6174
+ userId: currentUserId,
6175
+ distinctId: newDistinctId
6176
+ })
6177
+ });
6178
+
6179
+ if (!response.ok) {
6180
+ const errorData = await response.text();
6181
+ console.error('❌ [Identity] Reset API error:', {
6182
+ status: response.status,
6183
+ statusText: response.statusText,
6184
+ error: errorData
6185
+ });
6186
+ throw new Error(`Reset API error: ${response.status} ${response.statusText} - ${errorData}`);
6038
6187
  }
6188
+
6189
+ const result = await response.json();
6190
+
6191
+ // Update local storage after backend confirms
6192
+ StorageManager.setDistinctId(newDistinctId);
6193
+ StorageManager.clearDistinctIdCache();
6039
6194
 
6040
- // Update sessionData if available
6195
+ // Update sessionData
6041
6196
  if (sessionData) {
6042
6197
  sessionData.distinctId = newDistinctId;
6043
6198
  sessionData.anonymous = true;
6044
6199
  sessionData.identified = false;
6045
6200
  }
6046
6201
 
6202
+ // Update stored session
6203
+ const updatedSession = StorageManager.loadSession();
6204
+ if (updatedSession) {
6205
+ updatedSession.distinctId = newDistinctId;
6206
+ updatedSession.anonymous = true;
6207
+ updatedSession.identified = false;
6208
+ StorageManager.saveSession(updatedSession);
6209
+ }
6210
+
6047
6211
  // Clear identify_id from storage (if stored separately)
6048
6212
  try {
6049
6213
  sessionStorage.removeItem('cryptique_identify_id');
6214
+ localStorage.removeItem('cryptique_identify_id');
6050
6215
  } catch (err) {
6051
6216
  // Ignore errors
6052
6217
  }
6053
6218
 
6054
6219
  console.log(`🔄 [Identity] Reset to anonymous: ${newDistinctId}`);
6055
-
6220
+
6056
6221
  return { success: true, distinctId: newDistinctId };
6057
6222
  } catch (error) {
6058
6223
  console.error('❌ [Identity] Error in reset():', error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cryptique-sdk",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
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",