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 +201 -36
- package/lib/esm/index.js +201 -36
- package/lib/umd/index.js +201 -36
- package/package.json +1 -1
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
|
-
*
|
|
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
|
-
//
|
|
521
|
-
|
|
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
|
-
|
|
524
|
-
|
|
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
|
|
591
|
+
console.warn('Failed to access storage for distinct_id:', err);
|
|
534
592
|
const userId = this.getUserId();
|
|
535
|
-
|
|
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
|
|
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
|
-
//
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
|
|
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.
|
|
6081
|
+
sessionData.distinctId = result.distinctId;
|
|
5986
6082
|
sessionData.anonymous = false;
|
|
5987
6083
|
sessionData.identified = true;
|
|
5988
6084
|
}
|
|
5989
6085
|
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
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.
|
|
6102
|
+
sessionData.distinctId = result.newDistinctId;
|
|
5998
6103
|
sessionData.anonymous = false;
|
|
5999
6104
|
sessionData.identified = true;
|
|
6000
6105
|
}
|
|
6001
6106
|
|
|
6002
|
-
|
|
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
|
|
6022
|
-
const newDistinctId = StorageManager.generateAnonymousDistinctId(userId);
|
|
6023
|
-
|
|
6024
|
-
// Update sessionStorage
|
|
6025
|
-
StorageManager.setDistinctId(newDistinctId);
|
|
6157
|
+
const newDistinctId = StorageManager.generateAnonymousDistinctId(currentUserId);
|
|
6026
6158
|
|
|
6027
|
-
//
|
|
6028
|
-
const
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
|
|
6032
|
-
|
|
6033
|
-
|
|
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
|
|
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
|
-
*
|
|
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
|
-
//
|
|
519
|
-
|
|
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
|
-
|
|
522
|
-
|
|
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
|
|
589
|
+
console.warn('Failed to access storage for distinct_id:', err);
|
|
532
590
|
const userId = this.getUserId();
|
|
533
|
-
|
|
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
|
|
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
|
-
//
|
|
5977
|
-
|
|
5978
|
-
|
|
5979
|
-
|
|
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.
|
|
6079
|
+
sessionData.distinctId = result.distinctId;
|
|
5984
6080
|
sessionData.anonymous = false;
|
|
5985
6081
|
sessionData.identified = true;
|
|
5986
6082
|
}
|
|
5987
6083
|
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
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.
|
|
6100
|
+
sessionData.distinctId = result.newDistinctId;
|
|
5996
6101
|
sessionData.anonymous = false;
|
|
5997
6102
|
sessionData.identified = true;
|
|
5998
6103
|
}
|
|
5999
6104
|
|
|
6000
|
-
|
|
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
|
|
6020
|
-
const newDistinctId = StorageManager.generateAnonymousDistinctId(userId);
|
|
6021
|
-
|
|
6022
|
-
// Update sessionStorage
|
|
6023
|
-
StorageManager.setDistinctId(newDistinctId);
|
|
6155
|
+
const newDistinctId = StorageManager.generateAnonymousDistinctId(currentUserId);
|
|
6024
6156
|
|
|
6025
|
-
//
|
|
6026
|
-
const
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
|
|
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
|
|
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
|
-
*
|
|
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
|
-
//
|
|
525
|
-
|
|
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
|
-
|
|
528
|
-
|
|
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
|
|
595
|
+
console.warn('Failed to access storage for distinct_id:', err);
|
|
538
596
|
const userId = this.getUserId();
|
|
539
|
-
|
|
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
|
|
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
|
-
//
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
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.
|
|
6085
|
+
sessionData.distinctId = result.distinctId;
|
|
5990
6086
|
sessionData.anonymous = false;
|
|
5991
6087
|
sessionData.identified = true;
|
|
5992
6088
|
}
|
|
5993
6089
|
|
|
5994
|
-
|
|
5995
|
-
|
|
5996
|
-
|
|
5997
|
-
|
|
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.
|
|
6106
|
+
sessionData.distinctId = result.newDistinctId;
|
|
6002
6107
|
sessionData.anonymous = false;
|
|
6003
6108
|
sessionData.identified = true;
|
|
6004
6109
|
}
|
|
6005
6110
|
|
|
6006
|
-
|
|
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
|
|
6026
|
-
const newDistinctId = StorageManager.generateAnonymousDistinctId(userId);
|
|
6027
|
-
|
|
6028
|
-
// Update sessionStorage
|
|
6029
|
-
StorageManager.setDistinctId(newDistinctId);
|
|
6161
|
+
const newDistinctId = StorageManager.generateAnonymousDistinctId(currentUserId);
|
|
6030
6162
|
|
|
6031
|
-
//
|
|
6032
|
-
const
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
|
|
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
|
|
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