cryptique-sdk 1.1.5 → 1.1.7
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 +128 -23
- package/lib/esm/index.js +128 -23
- package/lib/types/index.d.ts +7 -1
- package/lib/umd/index.js +128 -23
- package/package.json +1 -1
package/lib/cjs/index.js
CHANGED
|
@@ -164,6 +164,21 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
164
164
|
eip6963Providers: [] // EIP-6963 discovered wallet providers
|
|
165
165
|
};
|
|
166
166
|
|
|
167
|
+
// Ready promise - resolves when SDK is fully initialized (allows consumers to await init)
|
|
168
|
+
let _readyPromiseResolve;
|
|
169
|
+
const _readyPromise = new Promise((resolve) => {
|
|
170
|
+
_readyPromiseResolve = resolve;
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Helper to wait for SDK ready with timeout (avoids hanging if init fails)
|
|
174
|
+
const _waitForReady = (timeoutMs = 5000) => {
|
|
175
|
+
if (runtimeState.isInitialized) return Promise.resolve();
|
|
176
|
+
return Promise.race([
|
|
177
|
+
_readyPromise,
|
|
178
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('SDK init timeout')), timeoutMs))
|
|
179
|
+
]);
|
|
180
|
+
};
|
|
181
|
+
|
|
167
182
|
// Initialize EIP-6963 wallet provider detection (if enabled)
|
|
168
183
|
{
|
|
169
184
|
// Listen for providers being announced
|
|
@@ -3770,18 +3785,6 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
3770
3785
|
|
|
3771
3786
|
// Save updated session
|
|
3772
3787
|
StorageManager.saveSession(session);
|
|
3773
|
-
|
|
3774
|
-
// Fire page_view auto event on every navigation (single source of truth with session tracking)
|
|
3775
|
-
// This ensures SPAs get page_view on route changes without a separate history override
|
|
3776
|
-
if (typeof CONFIG !== 'undefined' && CONFIG.AUTO_EVENTS && CONFIG.AUTO_EVENTS.enabled && typeof shouldTrackAutoEvents === 'function' && shouldTrackAutoEvents()) {
|
|
3777
|
-
if (typeof AutoEventsTracker !== 'undefined') {
|
|
3778
|
-
if (!AutoEventsTracker.isSetup) {
|
|
3779
|
-
AutoEventsTracker.setup();
|
|
3780
|
-
} else {
|
|
3781
|
-
AutoEventsTracker.trackPageView();
|
|
3782
|
-
}
|
|
3783
|
-
}
|
|
3784
|
-
}
|
|
3785
3788
|
} catch (error) {
|
|
3786
3789
|
console.error("Error tracking page visit:", error);
|
|
3787
3790
|
}
|
|
@@ -4311,6 +4314,10 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
4311
4314
|
// - Proper use of sendBeacon vs fetch
|
|
4312
4315
|
// ============================================================================
|
|
4313
4316
|
|
|
4317
|
+
// Throttle network error logs (e.g. "Failed to fetch" from ad blockers) - log at most once per 30s
|
|
4318
|
+
let _lastNetworkErrorLog = 0;
|
|
4319
|
+
const _NETWORK_ERROR_THROTTLE_MS = 30000;
|
|
4320
|
+
|
|
4314
4321
|
/**
|
|
4315
4322
|
* APIClient - Unified API communication
|
|
4316
4323
|
*
|
|
@@ -4491,8 +4498,14 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
4491
4498
|
// Don't retry aborted requests - they were intentionally cancelled
|
|
4492
4499
|
return; // Silently return, don't throw
|
|
4493
4500
|
}
|
|
4494
|
-
|
|
4495
|
-
|
|
4501
|
+
|
|
4502
|
+
// Throttle "Failed to fetch" logs (common with ad blockers, CORS, network issues)
|
|
4503
|
+
const isNetworkError = error.name === 'TypeError' && (error.message === 'Failed to fetch' || error.message?.includes('fetch'));
|
|
4504
|
+
const now = Date.now();
|
|
4505
|
+
if (isNetworkError && (now - _lastNetworkErrorLog) < _NETWORK_ERROR_THROTTLE_MS) ; else {
|
|
4506
|
+
if (isNetworkError) _lastNetworkErrorLog = now;
|
|
4507
|
+
console.error('❌ Error sending data:', error);
|
|
4508
|
+
}
|
|
4496
4509
|
|
|
4497
4510
|
// Retry if retries > 0 (but not for abort errors)
|
|
4498
4511
|
if (retries > 0) {
|
|
@@ -5899,6 +5912,12 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
5899
5912
|
|
|
5900
5913
|
} catch (error) {
|
|
5901
5914
|
console.error('Error initializing Cryptique SDK:', error);
|
|
5915
|
+
} finally {
|
|
5916
|
+
// Always resolve ready promise so awaiters don't hang (even if init failed)
|
|
5917
|
+
if (typeof _readyPromiseResolve === 'function') {
|
|
5918
|
+
_readyPromiseResolve();
|
|
5919
|
+
_readyPromiseResolve = null; // Prevent multiple resolves
|
|
5920
|
+
}
|
|
5902
5921
|
}
|
|
5903
5922
|
}
|
|
5904
5923
|
};
|
|
@@ -6111,9 +6130,17 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
6111
6130
|
}
|
|
6112
6131
|
}
|
|
6113
6132
|
|
|
6133
|
+
// Wait for SDK init if not ready (handles race conditions)
|
|
6114
6134
|
if (!runtimeState.isInitialized) {
|
|
6115
|
-
|
|
6116
|
-
|
|
6135
|
+
try {
|
|
6136
|
+
await _waitForReady();
|
|
6137
|
+
} catch (e) {
|
|
6138
|
+
// Init timeout or failed - silently skip
|
|
6139
|
+
return;
|
|
6140
|
+
}
|
|
6141
|
+
if (!runtimeState.isInitialized) {
|
|
6142
|
+
return;
|
|
6143
|
+
}
|
|
6117
6144
|
}
|
|
6118
6145
|
|
|
6119
6146
|
// Get session from storage - it returns { id, userId, ... }
|
|
@@ -6688,8 +6715,9 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
6688
6715
|
|
|
6689
6716
|
/**
|
|
6690
6717
|
* Setup auto events tracking
|
|
6718
|
+
* Waits for SDK initialization before proceeding (handles SPA navigation race)
|
|
6691
6719
|
*/
|
|
6692
|
-
setup() {
|
|
6720
|
+
async setup() {
|
|
6693
6721
|
// Check if auto events are enabled
|
|
6694
6722
|
if (!CONFIG.AUTO_EVENTS.enabled) {
|
|
6695
6723
|
return;
|
|
@@ -6705,6 +6733,17 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
6705
6733
|
}
|
|
6706
6734
|
|
|
6707
6735
|
try {
|
|
6736
|
+
// Wait for SDK to be fully initialized (handles SPA nav before init completes)
|
|
6737
|
+
await _waitForReady().catch(() => {
|
|
6738
|
+
// Init failed or timed out - silently skip setup
|
|
6739
|
+
return;
|
|
6740
|
+
});
|
|
6741
|
+
|
|
6742
|
+
// Double-check after wait (init may have failed)
|
|
6743
|
+
if (!runtimeState.isInitialized) {
|
|
6744
|
+
return;
|
|
6745
|
+
}
|
|
6746
|
+
|
|
6708
6747
|
// Track page views
|
|
6709
6748
|
this.trackPageView();
|
|
6710
6749
|
|
|
@@ -7687,6 +7726,9 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
7687
7726
|
...window.Cryptique,
|
|
7688
7727
|
version: CONFIG.VERSION,
|
|
7689
7728
|
|
|
7729
|
+
// Wait for SDK to be fully initialized (returns Promise)
|
|
7730
|
+
ready: () => _readyPromise,
|
|
7731
|
+
|
|
7690
7732
|
// Events System
|
|
7691
7733
|
track: EventsManager.trackEvent.bind(EventsManager),
|
|
7692
7734
|
trackEvent: EventsManager.trackEvent.bind(EventsManager),
|
|
@@ -7817,6 +7859,46 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
7817
7859
|
// Initialize SDK when DOM is ready
|
|
7818
7860
|
// ============================================================================
|
|
7819
7861
|
|
|
7862
|
+
/**
|
|
7863
|
+
* Monitor path changes for Single Page Applications (SPAs)
|
|
7864
|
+
* Re-checks auto events configuration when navigation occurs
|
|
7865
|
+
*/
|
|
7866
|
+
function setupSPANavigationMonitoring() {
|
|
7867
|
+
let lastPath = window.location.pathname;
|
|
7868
|
+
|
|
7869
|
+
// Check path changes
|
|
7870
|
+
function checkPathChange() {
|
|
7871
|
+
const currentPath = window.location.pathname;
|
|
7872
|
+
if (currentPath !== lastPath) {
|
|
7873
|
+
lastPath = currentPath;
|
|
7874
|
+
|
|
7875
|
+
// If auto events were disabled on previous page but enabled on new page, setup
|
|
7876
|
+
if (CONFIG.AUTO_EVENTS.enabled && shouldTrackAutoEvents() && !AutoEventsTracker.isSetup) {
|
|
7877
|
+
AutoEventsTracker.setup();
|
|
7878
|
+
}
|
|
7879
|
+
// Note: If auto events should be disabled on new page, trackAutoEvent will check
|
|
7880
|
+
// shouldTrackAutoEvents() before tracking, so no need to remove listeners
|
|
7881
|
+
}
|
|
7882
|
+
}
|
|
7883
|
+
|
|
7884
|
+
// Check on popstate (browser back/forward)
|
|
7885
|
+
window.addEventListener('popstate', checkPathChange);
|
|
7886
|
+
|
|
7887
|
+
// For SPAs using history.pushState/replaceState, monitor them
|
|
7888
|
+
const originalPushState = history.pushState;
|
|
7889
|
+
const originalReplaceState = history.replaceState;
|
|
7890
|
+
|
|
7891
|
+
history.pushState = function(...args) {
|
|
7892
|
+
originalPushState.apply(history, args);
|
|
7893
|
+
setTimeout(checkPathChange, 0);
|
|
7894
|
+
};
|
|
7895
|
+
|
|
7896
|
+
history.replaceState = function(...args) {
|
|
7897
|
+
originalReplaceState.apply(history, args);
|
|
7898
|
+
setTimeout(checkPathChange, 0);
|
|
7899
|
+
};
|
|
7900
|
+
}
|
|
7901
|
+
|
|
7820
7902
|
// Initialize SDK
|
|
7821
7903
|
async function initializeSDK() {
|
|
7822
7904
|
await Initializer.initialize();
|
|
@@ -7826,6 +7908,9 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
7826
7908
|
setTimeout(() => {
|
|
7827
7909
|
AutoEventsTracker.setup();
|
|
7828
7910
|
}, 2000); // Wait 2 seconds for initial session to be saved
|
|
7911
|
+
|
|
7912
|
+
// Setup SPA navigation monitoring for auto events
|
|
7913
|
+
setupSPANavigationMonitoring();
|
|
7829
7914
|
}
|
|
7830
7915
|
|
|
7831
7916
|
if (document.readyState === 'loading') {
|
|
@@ -7868,7 +7953,7 @@ const CryptiqueSDK = {
|
|
|
7868
7953
|
init(options = {}) {
|
|
7869
7954
|
if (typeof window === 'undefined') {
|
|
7870
7955
|
console.warn('Cryptique SDK requires a browser environment');
|
|
7871
|
-
return null;
|
|
7956
|
+
return Promise.resolve(null);
|
|
7872
7957
|
}
|
|
7873
7958
|
|
|
7874
7959
|
if (!options.siteId) {
|
|
@@ -7909,21 +7994,28 @@ const CryptiqueSDK = {
|
|
|
7909
7994
|
return null;
|
|
7910
7995
|
};
|
|
7911
7996
|
|
|
7912
|
-
// If SDK is already loaded, configure
|
|
7997
|
+
// If SDK is already loaded, configure and return ready promise
|
|
7913
7998
|
if (window.Cryptique) {
|
|
7914
|
-
|
|
7999
|
+
checkSDK();
|
|
8000
|
+
// Return ready() so await Cryptique.init() waits for full initialization
|
|
8001
|
+
return window.Cryptique.ready ? window.Cryptique.ready() : Promise.resolve(window.Cryptique);
|
|
7915
8002
|
}
|
|
7916
8003
|
|
|
7917
|
-
// Otherwise, wait
|
|
8004
|
+
// Otherwise, wait for SDK to load then configure and wait for ready
|
|
7918
8005
|
return new Promise((resolve) => {
|
|
7919
8006
|
const maxAttempts = 50;
|
|
7920
8007
|
let attempts = 0;
|
|
7921
8008
|
const interval = setInterval(() => {
|
|
7922
8009
|
attempts++;
|
|
7923
8010
|
const sdk = checkSDK();
|
|
7924
|
-
if (sdk
|
|
8011
|
+
if (sdk) {
|
|
8012
|
+
clearInterval(interval);
|
|
8013
|
+
// Wait for full initialization
|
|
8014
|
+
const readyPromise = sdk.ready ? sdk.ready() : Promise.resolve(sdk);
|
|
8015
|
+
resolve(readyPromise);
|
|
8016
|
+
} else if (attempts >= maxAttempts) {
|
|
7925
8017
|
clearInterval(interval);
|
|
7926
|
-
resolve(
|
|
8018
|
+
resolve(null);
|
|
7927
8019
|
}
|
|
7928
8020
|
}, 100);
|
|
7929
8021
|
});
|
|
@@ -7940,6 +8032,19 @@ const CryptiqueSDK = {
|
|
|
7940
8032
|
return null;
|
|
7941
8033
|
},
|
|
7942
8034
|
|
|
8035
|
+
/**
|
|
8036
|
+
* Wait for SDK to be fully initialized
|
|
8037
|
+
* Returns a Promise that resolves when initialization is complete
|
|
8038
|
+
* @returns {Promise<void>}
|
|
8039
|
+
*/
|
|
8040
|
+
ready() {
|
|
8041
|
+
const instance = this.getInstance();
|
|
8042
|
+
if (instance && instance.ready) {
|
|
8043
|
+
return instance.ready();
|
|
8044
|
+
}
|
|
8045
|
+
return Promise.resolve();
|
|
8046
|
+
},
|
|
8047
|
+
|
|
7943
8048
|
/**
|
|
7944
8049
|
* Identify a user with a unique identifier
|
|
7945
8050
|
*
|
package/lib/esm/index.js
CHANGED
|
@@ -162,6 +162,21 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
162
162
|
eip6963Providers: [] // EIP-6963 discovered wallet providers
|
|
163
163
|
};
|
|
164
164
|
|
|
165
|
+
// Ready promise - resolves when SDK is fully initialized (allows consumers to await init)
|
|
166
|
+
let _readyPromiseResolve;
|
|
167
|
+
const _readyPromise = new Promise((resolve) => {
|
|
168
|
+
_readyPromiseResolve = resolve;
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Helper to wait for SDK ready with timeout (avoids hanging if init fails)
|
|
172
|
+
const _waitForReady = (timeoutMs = 5000) => {
|
|
173
|
+
if (runtimeState.isInitialized) return Promise.resolve();
|
|
174
|
+
return Promise.race([
|
|
175
|
+
_readyPromise,
|
|
176
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('SDK init timeout')), timeoutMs))
|
|
177
|
+
]);
|
|
178
|
+
};
|
|
179
|
+
|
|
165
180
|
// Initialize EIP-6963 wallet provider detection (if enabled)
|
|
166
181
|
{
|
|
167
182
|
// Listen for providers being announced
|
|
@@ -3768,18 +3783,6 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
3768
3783
|
|
|
3769
3784
|
// Save updated session
|
|
3770
3785
|
StorageManager.saveSession(session);
|
|
3771
|
-
|
|
3772
|
-
// Fire page_view auto event on every navigation (single source of truth with session tracking)
|
|
3773
|
-
// This ensures SPAs get page_view on route changes without a separate history override
|
|
3774
|
-
if (typeof CONFIG !== 'undefined' && CONFIG.AUTO_EVENTS && CONFIG.AUTO_EVENTS.enabled && typeof shouldTrackAutoEvents === 'function' && shouldTrackAutoEvents()) {
|
|
3775
|
-
if (typeof AutoEventsTracker !== 'undefined') {
|
|
3776
|
-
if (!AutoEventsTracker.isSetup) {
|
|
3777
|
-
AutoEventsTracker.setup();
|
|
3778
|
-
} else {
|
|
3779
|
-
AutoEventsTracker.trackPageView();
|
|
3780
|
-
}
|
|
3781
|
-
}
|
|
3782
|
-
}
|
|
3783
3786
|
} catch (error) {
|
|
3784
3787
|
console.error("Error tracking page visit:", error);
|
|
3785
3788
|
}
|
|
@@ -4309,6 +4312,10 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
4309
4312
|
// - Proper use of sendBeacon vs fetch
|
|
4310
4313
|
// ============================================================================
|
|
4311
4314
|
|
|
4315
|
+
// Throttle network error logs (e.g. "Failed to fetch" from ad blockers) - log at most once per 30s
|
|
4316
|
+
let _lastNetworkErrorLog = 0;
|
|
4317
|
+
const _NETWORK_ERROR_THROTTLE_MS = 30000;
|
|
4318
|
+
|
|
4312
4319
|
/**
|
|
4313
4320
|
* APIClient - Unified API communication
|
|
4314
4321
|
*
|
|
@@ -4489,8 +4496,14 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
4489
4496
|
// Don't retry aborted requests - they were intentionally cancelled
|
|
4490
4497
|
return; // Silently return, don't throw
|
|
4491
4498
|
}
|
|
4492
|
-
|
|
4493
|
-
|
|
4499
|
+
|
|
4500
|
+
// Throttle "Failed to fetch" logs (common with ad blockers, CORS, network issues)
|
|
4501
|
+
const isNetworkError = error.name === 'TypeError' && (error.message === 'Failed to fetch' || error.message?.includes('fetch'));
|
|
4502
|
+
const now = Date.now();
|
|
4503
|
+
if (isNetworkError && (now - _lastNetworkErrorLog) < _NETWORK_ERROR_THROTTLE_MS) ; else {
|
|
4504
|
+
if (isNetworkError) _lastNetworkErrorLog = now;
|
|
4505
|
+
console.error('❌ Error sending data:', error);
|
|
4506
|
+
}
|
|
4494
4507
|
|
|
4495
4508
|
// Retry if retries > 0 (but not for abort errors)
|
|
4496
4509
|
if (retries > 0) {
|
|
@@ -5897,6 +5910,12 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
5897
5910
|
|
|
5898
5911
|
} catch (error) {
|
|
5899
5912
|
console.error('Error initializing Cryptique SDK:', error);
|
|
5913
|
+
} finally {
|
|
5914
|
+
// Always resolve ready promise so awaiters don't hang (even if init failed)
|
|
5915
|
+
if (typeof _readyPromiseResolve === 'function') {
|
|
5916
|
+
_readyPromiseResolve();
|
|
5917
|
+
_readyPromiseResolve = null; // Prevent multiple resolves
|
|
5918
|
+
}
|
|
5900
5919
|
}
|
|
5901
5920
|
}
|
|
5902
5921
|
};
|
|
@@ -6109,9 +6128,17 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
6109
6128
|
}
|
|
6110
6129
|
}
|
|
6111
6130
|
|
|
6131
|
+
// Wait for SDK init if not ready (handles race conditions)
|
|
6112
6132
|
if (!runtimeState.isInitialized) {
|
|
6113
|
-
|
|
6114
|
-
|
|
6133
|
+
try {
|
|
6134
|
+
await _waitForReady();
|
|
6135
|
+
} catch (e) {
|
|
6136
|
+
// Init timeout or failed - silently skip
|
|
6137
|
+
return;
|
|
6138
|
+
}
|
|
6139
|
+
if (!runtimeState.isInitialized) {
|
|
6140
|
+
return;
|
|
6141
|
+
}
|
|
6115
6142
|
}
|
|
6116
6143
|
|
|
6117
6144
|
// Get session from storage - it returns { id, userId, ... }
|
|
@@ -6686,8 +6713,9 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
6686
6713
|
|
|
6687
6714
|
/**
|
|
6688
6715
|
* Setup auto events tracking
|
|
6716
|
+
* Waits for SDK initialization before proceeding (handles SPA navigation race)
|
|
6689
6717
|
*/
|
|
6690
|
-
setup() {
|
|
6718
|
+
async setup() {
|
|
6691
6719
|
// Check if auto events are enabled
|
|
6692
6720
|
if (!CONFIG.AUTO_EVENTS.enabled) {
|
|
6693
6721
|
return;
|
|
@@ -6703,6 +6731,17 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
6703
6731
|
}
|
|
6704
6732
|
|
|
6705
6733
|
try {
|
|
6734
|
+
// Wait for SDK to be fully initialized (handles SPA nav before init completes)
|
|
6735
|
+
await _waitForReady().catch(() => {
|
|
6736
|
+
// Init failed or timed out - silently skip setup
|
|
6737
|
+
return;
|
|
6738
|
+
});
|
|
6739
|
+
|
|
6740
|
+
// Double-check after wait (init may have failed)
|
|
6741
|
+
if (!runtimeState.isInitialized) {
|
|
6742
|
+
return;
|
|
6743
|
+
}
|
|
6744
|
+
|
|
6706
6745
|
// Track page views
|
|
6707
6746
|
this.trackPageView();
|
|
6708
6747
|
|
|
@@ -7685,6 +7724,9 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
7685
7724
|
...window.Cryptique,
|
|
7686
7725
|
version: CONFIG.VERSION,
|
|
7687
7726
|
|
|
7727
|
+
// Wait for SDK to be fully initialized (returns Promise)
|
|
7728
|
+
ready: () => _readyPromise,
|
|
7729
|
+
|
|
7688
7730
|
// Events System
|
|
7689
7731
|
track: EventsManager.trackEvent.bind(EventsManager),
|
|
7690
7732
|
trackEvent: EventsManager.trackEvent.bind(EventsManager),
|
|
@@ -7815,6 +7857,46 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
7815
7857
|
// Initialize SDK when DOM is ready
|
|
7816
7858
|
// ============================================================================
|
|
7817
7859
|
|
|
7860
|
+
/**
|
|
7861
|
+
* Monitor path changes for Single Page Applications (SPAs)
|
|
7862
|
+
* Re-checks auto events configuration when navigation occurs
|
|
7863
|
+
*/
|
|
7864
|
+
function setupSPANavigationMonitoring() {
|
|
7865
|
+
let lastPath = window.location.pathname;
|
|
7866
|
+
|
|
7867
|
+
// Check path changes
|
|
7868
|
+
function checkPathChange() {
|
|
7869
|
+
const currentPath = window.location.pathname;
|
|
7870
|
+
if (currentPath !== lastPath) {
|
|
7871
|
+
lastPath = currentPath;
|
|
7872
|
+
|
|
7873
|
+
// If auto events were disabled on previous page but enabled on new page, setup
|
|
7874
|
+
if (CONFIG.AUTO_EVENTS.enabled && shouldTrackAutoEvents() && !AutoEventsTracker.isSetup) {
|
|
7875
|
+
AutoEventsTracker.setup();
|
|
7876
|
+
}
|
|
7877
|
+
// Note: If auto events should be disabled on new page, trackAutoEvent will check
|
|
7878
|
+
// shouldTrackAutoEvents() before tracking, so no need to remove listeners
|
|
7879
|
+
}
|
|
7880
|
+
}
|
|
7881
|
+
|
|
7882
|
+
// Check on popstate (browser back/forward)
|
|
7883
|
+
window.addEventListener('popstate', checkPathChange);
|
|
7884
|
+
|
|
7885
|
+
// For SPAs using history.pushState/replaceState, monitor them
|
|
7886
|
+
const originalPushState = history.pushState;
|
|
7887
|
+
const originalReplaceState = history.replaceState;
|
|
7888
|
+
|
|
7889
|
+
history.pushState = function(...args) {
|
|
7890
|
+
originalPushState.apply(history, args);
|
|
7891
|
+
setTimeout(checkPathChange, 0);
|
|
7892
|
+
};
|
|
7893
|
+
|
|
7894
|
+
history.replaceState = function(...args) {
|
|
7895
|
+
originalReplaceState.apply(history, args);
|
|
7896
|
+
setTimeout(checkPathChange, 0);
|
|
7897
|
+
};
|
|
7898
|
+
}
|
|
7899
|
+
|
|
7818
7900
|
// Initialize SDK
|
|
7819
7901
|
async function initializeSDK() {
|
|
7820
7902
|
await Initializer.initialize();
|
|
@@ -7824,6 +7906,9 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
7824
7906
|
setTimeout(() => {
|
|
7825
7907
|
AutoEventsTracker.setup();
|
|
7826
7908
|
}, 2000); // Wait 2 seconds for initial session to be saved
|
|
7909
|
+
|
|
7910
|
+
// Setup SPA navigation monitoring for auto events
|
|
7911
|
+
setupSPANavigationMonitoring();
|
|
7827
7912
|
}
|
|
7828
7913
|
|
|
7829
7914
|
if (document.readyState === 'loading') {
|
|
@@ -7866,7 +7951,7 @@ const CryptiqueSDK = {
|
|
|
7866
7951
|
init(options = {}) {
|
|
7867
7952
|
if (typeof window === 'undefined') {
|
|
7868
7953
|
console.warn('Cryptique SDK requires a browser environment');
|
|
7869
|
-
return null;
|
|
7954
|
+
return Promise.resolve(null);
|
|
7870
7955
|
}
|
|
7871
7956
|
|
|
7872
7957
|
if (!options.siteId) {
|
|
@@ -7907,21 +7992,28 @@ const CryptiqueSDK = {
|
|
|
7907
7992
|
return null;
|
|
7908
7993
|
};
|
|
7909
7994
|
|
|
7910
|
-
// If SDK is already loaded, configure
|
|
7995
|
+
// If SDK is already loaded, configure and return ready promise
|
|
7911
7996
|
if (window.Cryptique) {
|
|
7912
|
-
|
|
7997
|
+
checkSDK();
|
|
7998
|
+
// Return ready() so await Cryptique.init() waits for full initialization
|
|
7999
|
+
return window.Cryptique.ready ? window.Cryptique.ready() : Promise.resolve(window.Cryptique);
|
|
7913
8000
|
}
|
|
7914
8001
|
|
|
7915
|
-
// Otherwise, wait
|
|
8002
|
+
// Otherwise, wait for SDK to load then configure and wait for ready
|
|
7916
8003
|
return new Promise((resolve) => {
|
|
7917
8004
|
const maxAttempts = 50;
|
|
7918
8005
|
let attempts = 0;
|
|
7919
8006
|
const interval = setInterval(() => {
|
|
7920
8007
|
attempts++;
|
|
7921
8008
|
const sdk = checkSDK();
|
|
7922
|
-
if (sdk
|
|
8009
|
+
if (sdk) {
|
|
8010
|
+
clearInterval(interval);
|
|
8011
|
+
// Wait for full initialization
|
|
8012
|
+
const readyPromise = sdk.ready ? sdk.ready() : Promise.resolve(sdk);
|
|
8013
|
+
resolve(readyPromise);
|
|
8014
|
+
} else if (attempts >= maxAttempts) {
|
|
7923
8015
|
clearInterval(interval);
|
|
7924
|
-
resolve(
|
|
8016
|
+
resolve(null);
|
|
7925
8017
|
}
|
|
7926
8018
|
}, 100);
|
|
7927
8019
|
});
|
|
@@ -7938,6 +8030,19 @@ const CryptiqueSDK = {
|
|
|
7938
8030
|
return null;
|
|
7939
8031
|
},
|
|
7940
8032
|
|
|
8033
|
+
/**
|
|
8034
|
+
* Wait for SDK to be fully initialized
|
|
8035
|
+
* Returns a Promise that resolves when initialization is complete
|
|
8036
|
+
* @returns {Promise<void>}
|
|
8037
|
+
*/
|
|
8038
|
+
ready() {
|
|
8039
|
+
const instance = this.getInstance();
|
|
8040
|
+
if (instance && instance.ready) {
|
|
8041
|
+
return instance.ready();
|
|
8042
|
+
}
|
|
8043
|
+
return Promise.resolve();
|
|
8044
|
+
},
|
|
8045
|
+
|
|
7941
8046
|
/**
|
|
7942
8047
|
* Identify a user with a unique identifier
|
|
7943
8048
|
*
|
package/lib/types/index.d.ts
CHANGED
|
@@ -99,14 +99,20 @@ export interface PeopleManager {
|
|
|
99
99
|
export default class CryptiqueSDK {
|
|
100
100
|
/**
|
|
101
101
|
* Initialize the SDK programmatically
|
|
102
|
+
* Returns a Promise that resolves when the SDK is fully initialized
|
|
102
103
|
*/
|
|
103
|
-
init(options: CryptiqueOptions): Promise<any
|
|
104
|
+
init(options: CryptiqueOptions): Promise<any>;
|
|
104
105
|
|
|
105
106
|
/**
|
|
106
107
|
* Get the SDK instance
|
|
107
108
|
*/
|
|
108
109
|
getInstance(): any | null;
|
|
109
110
|
|
|
111
|
+
/**
|
|
112
|
+
* Wait for SDK to be fully initialized
|
|
113
|
+
*/
|
|
114
|
+
ready(): Promise<void>;
|
|
115
|
+
|
|
110
116
|
/**
|
|
111
117
|
* Track a custom event
|
|
112
118
|
*/
|
package/lib/umd/index.js
CHANGED
|
@@ -168,6 +168,21 @@
|
|
|
168
168
|
eip6963Providers: [] // EIP-6963 discovered wallet providers
|
|
169
169
|
};
|
|
170
170
|
|
|
171
|
+
// Ready promise - resolves when SDK is fully initialized (allows consumers to await init)
|
|
172
|
+
let _readyPromiseResolve;
|
|
173
|
+
const _readyPromise = new Promise((resolve) => {
|
|
174
|
+
_readyPromiseResolve = resolve;
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Helper to wait for SDK ready with timeout (avoids hanging if init fails)
|
|
178
|
+
const _waitForReady = (timeoutMs = 5000) => {
|
|
179
|
+
if (runtimeState.isInitialized) return Promise.resolve();
|
|
180
|
+
return Promise.race([
|
|
181
|
+
_readyPromise,
|
|
182
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('SDK init timeout')), timeoutMs))
|
|
183
|
+
]);
|
|
184
|
+
};
|
|
185
|
+
|
|
171
186
|
// Initialize EIP-6963 wallet provider detection (if enabled)
|
|
172
187
|
{
|
|
173
188
|
// Listen for providers being announced
|
|
@@ -3774,18 +3789,6 @@
|
|
|
3774
3789
|
|
|
3775
3790
|
// Save updated session
|
|
3776
3791
|
StorageManager.saveSession(session);
|
|
3777
|
-
|
|
3778
|
-
// Fire page_view auto event on every navigation (single source of truth with session tracking)
|
|
3779
|
-
// This ensures SPAs get page_view on route changes without a separate history override
|
|
3780
|
-
if (typeof CONFIG !== 'undefined' && CONFIG.AUTO_EVENTS && CONFIG.AUTO_EVENTS.enabled && typeof shouldTrackAutoEvents === 'function' && shouldTrackAutoEvents()) {
|
|
3781
|
-
if (typeof AutoEventsTracker !== 'undefined') {
|
|
3782
|
-
if (!AutoEventsTracker.isSetup) {
|
|
3783
|
-
AutoEventsTracker.setup();
|
|
3784
|
-
} else {
|
|
3785
|
-
AutoEventsTracker.trackPageView();
|
|
3786
|
-
}
|
|
3787
|
-
}
|
|
3788
|
-
}
|
|
3789
3792
|
} catch (error) {
|
|
3790
3793
|
console.error("Error tracking page visit:", error);
|
|
3791
3794
|
}
|
|
@@ -4315,6 +4318,10 @@
|
|
|
4315
4318
|
// - Proper use of sendBeacon vs fetch
|
|
4316
4319
|
// ============================================================================
|
|
4317
4320
|
|
|
4321
|
+
// Throttle network error logs (e.g. "Failed to fetch" from ad blockers) - log at most once per 30s
|
|
4322
|
+
let _lastNetworkErrorLog = 0;
|
|
4323
|
+
const _NETWORK_ERROR_THROTTLE_MS = 30000;
|
|
4324
|
+
|
|
4318
4325
|
/**
|
|
4319
4326
|
* APIClient - Unified API communication
|
|
4320
4327
|
*
|
|
@@ -4495,8 +4502,14 @@
|
|
|
4495
4502
|
// Don't retry aborted requests - they were intentionally cancelled
|
|
4496
4503
|
return; // Silently return, don't throw
|
|
4497
4504
|
}
|
|
4498
|
-
|
|
4499
|
-
|
|
4505
|
+
|
|
4506
|
+
// Throttle "Failed to fetch" logs (common with ad blockers, CORS, network issues)
|
|
4507
|
+
const isNetworkError = error.name === 'TypeError' && (error.message === 'Failed to fetch' || error.message?.includes('fetch'));
|
|
4508
|
+
const now = Date.now();
|
|
4509
|
+
if (isNetworkError && (now - _lastNetworkErrorLog) < _NETWORK_ERROR_THROTTLE_MS) ; else {
|
|
4510
|
+
if (isNetworkError) _lastNetworkErrorLog = now;
|
|
4511
|
+
console.error('❌ Error sending data:', error);
|
|
4512
|
+
}
|
|
4500
4513
|
|
|
4501
4514
|
// Retry if retries > 0 (but not for abort errors)
|
|
4502
4515
|
if (retries > 0) {
|
|
@@ -5903,6 +5916,12 @@
|
|
|
5903
5916
|
|
|
5904
5917
|
} catch (error) {
|
|
5905
5918
|
console.error('Error initializing Cryptique SDK:', error);
|
|
5919
|
+
} finally {
|
|
5920
|
+
// Always resolve ready promise so awaiters don't hang (even if init failed)
|
|
5921
|
+
if (typeof _readyPromiseResolve === 'function') {
|
|
5922
|
+
_readyPromiseResolve();
|
|
5923
|
+
_readyPromiseResolve = null; // Prevent multiple resolves
|
|
5924
|
+
}
|
|
5906
5925
|
}
|
|
5907
5926
|
}
|
|
5908
5927
|
};
|
|
@@ -6115,9 +6134,17 @@
|
|
|
6115
6134
|
}
|
|
6116
6135
|
}
|
|
6117
6136
|
|
|
6137
|
+
// Wait for SDK init if not ready (handles race conditions)
|
|
6118
6138
|
if (!runtimeState.isInitialized) {
|
|
6119
|
-
|
|
6120
|
-
|
|
6139
|
+
try {
|
|
6140
|
+
await _waitForReady();
|
|
6141
|
+
} catch (e) {
|
|
6142
|
+
// Init timeout or failed - silently skip
|
|
6143
|
+
return;
|
|
6144
|
+
}
|
|
6145
|
+
if (!runtimeState.isInitialized) {
|
|
6146
|
+
return;
|
|
6147
|
+
}
|
|
6121
6148
|
}
|
|
6122
6149
|
|
|
6123
6150
|
// Get session from storage - it returns { id, userId, ... }
|
|
@@ -6692,8 +6719,9 @@
|
|
|
6692
6719
|
|
|
6693
6720
|
/**
|
|
6694
6721
|
* Setup auto events tracking
|
|
6722
|
+
* Waits for SDK initialization before proceeding (handles SPA navigation race)
|
|
6695
6723
|
*/
|
|
6696
|
-
setup() {
|
|
6724
|
+
async setup() {
|
|
6697
6725
|
// Check if auto events are enabled
|
|
6698
6726
|
if (!CONFIG.AUTO_EVENTS.enabled) {
|
|
6699
6727
|
return;
|
|
@@ -6709,6 +6737,17 @@
|
|
|
6709
6737
|
}
|
|
6710
6738
|
|
|
6711
6739
|
try {
|
|
6740
|
+
// Wait for SDK to be fully initialized (handles SPA nav before init completes)
|
|
6741
|
+
await _waitForReady().catch(() => {
|
|
6742
|
+
// Init failed or timed out - silently skip setup
|
|
6743
|
+
return;
|
|
6744
|
+
});
|
|
6745
|
+
|
|
6746
|
+
// Double-check after wait (init may have failed)
|
|
6747
|
+
if (!runtimeState.isInitialized) {
|
|
6748
|
+
return;
|
|
6749
|
+
}
|
|
6750
|
+
|
|
6712
6751
|
// Track page views
|
|
6713
6752
|
this.trackPageView();
|
|
6714
6753
|
|
|
@@ -7691,6 +7730,9 @@
|
|
|
7691
7730
|
...window.Cryptique,
|
|
7692
7731
|
version: CONFIG.VERSION,
|
|
7693
7732
|
|
|
7733
|
+
// Wait for SDK to be fully initialized (returns Promise)
|
|
7734
|
+
ready: () => _readyPromise,
|
|
7735
|
+
|
|
7694
7736
|
// Events System
|
|
7695
7737
|
track: EventsManager.trackEvent.bind(EventsManager),
|
|
7696
7738
|
trackEvent: EventsManager.trackEvent.bind(EventsManager),
|
|
@@ -7821,6 +7863,46 @@
|
|
|
7821
7863
|
// Initialize SDK when DOM is ready
|
|
7822
7864
|
// ============================================================================
|
|
7823
7865
|
|
|
7866
|
+
/**
|
|
7867
|
+
* Monitor path changes for Single Page Applications (SPAs)
|
|
7868
|
+
* Re-checks auto events configuration when navigation occurs
|
|
7869
|
+
*/
|
|
7870
|
+
function setupSPANavigationMonitoring() {
|
|
7871
|
+
let lastPath = window.location.pathname;
|
|
7872
|
+
|
|
7873
|
+
// Check path changes
|
|
7874
|
+
function checkPathChange() {
|
|
7875
|
+
const currentPath = window.location.pathname;
|
|
7876
|
+
if (currentPath !== lastPath) {
|
|
7877
|
+
lastPath = currentPath;
|
|
7878
|
+
|
|
7879
|
+
// If auto events were disabled on previous page but enabled on new page, setup
|
|
7880
|
+
if (CONFIG.AUTO_EVENTS.enabled && shouldTrackAutoEvents() && !AutoEventsTracker.isSetup) {
|
|
7881
|
+
AutoEventsTracker.setup();
|
|
7882
|
+
}
|
|
7883
|
+
// Note: If auto events should be disabled on new page, trackAutoEvent will check
|
|
7884
|
+
// shouldTrackAutoEvents() before tracking, so no need to remove listeners
|
|
7885
|
+
}
|
|
7886
|
+
}
|
|
7887
|
+
|
|
7888
|
+
// Check on popstate (browser back/forward)
|
|
7889
|
+
window.addEventListener('popstate', checkPathChange);
|
|
7890
|
+
|
|
7891
|
+
// For SPAs using history.pushState/replaceState, monitor them
|
|
7892
|
+
const originalPushState = history.pushState;
|
|
7893
|
+
const originalReplaceState = history.replaceState;
|
|
7894
|
+
|
|
7895
|
+
history.pushState = function(...args) {
|
|
7896
|
+
originalPushState.apply(history, args);
|
|
7897
|
+
setTimeout(checkPathChange, 0);
|
|
7898
|
+
};
|
|
7899
|
+
|
|
7900
|
+
history.replaceState = function(...args) {
|
|
7901
|
+
originalReplaceState.apply(history, args);
|
|
7902
|
+
setTimeout(checkPathChange, 0);
|
|
7903
|
+
};
|
|
7904
|
+
}
|
|
7905
|
+
|
|
7824
7906
|
// Initialize SDK
|
|
7825
7907
|
async function initializeSDK() {
|
|
7826
7908
|
await Initializer.initialize();
|
|
@@ -7830,6 +7912,9 @@
|
|
|
7830
7912
|
setTimeout(() => {
|
|
7831
7913
|
AutoEventsTracker.setup();
|
|
7832
7914
|
}, 2000); // Wait 2 seconds for initial session to be saved
|
|
7915
|
+
|
|
7916
|
+
// Setup SPA navigation monitoring for auto events
|
|
7917
|
+
setupSPANavigationMonitoring();
|
|
7833
7918
|
}
|
|
7834
7919
|
|
|
7835
7920
|
if (document.readyState === 'loading') {
|
|
@@ -7872,7 +7957,7 @@
|
|
|
7872
7957
|
init(options = {}) {
|
|
7873
7958
|
if (typeof window === 'undefined') {
|
|
7874
7959
|
console.warn('Cryptique SDK requires a browser environment');
|
|
7875
|
-
return null;
|
|
7960
|
+
return Promise.resolve(null);
|
|
7876
7961
|
}
|
|
7877
7962
|
|
|
7878
7963
|
if (!options.siteId) {
|
|
@@ -7913,21 +7998,28 @@
|
|
|
7913
7998
|
return null;
|
|
7914
7999
|
};
|
|
7915
8000
|
|
|
7916
|
-
// If SDK is already loaded, configure
|
|
8001
|
+
// If SDK is already loaded, configure and return ready promise
|
|
7917
8002
|
if (window.Cryptique) {
|
|
7918
|
-
|
|
8003
|
+
checkSDK();
|
|
8004
|
+
// Return ready() so await Cryptique.init() waits for full initialization
|
|
8005
|
+
return window.Cryptique.ready ? window.Cryptique.ready() : Promise.resolve(window.Cryptique);
|
|
7919
8006
|
}
|
|
7920
8007
|
|
|
7921
|
-
// Otherwise, wait
|
|
8008
|
+
// Otherwise, wait for SDK to load then configure and wait for ready
|
|
7922
8009
|
return new Promise((resolve) => {
|
|
7923
8010
|
const maxAttempts = 50;
|
|
7924
8011
|
let attempts = 0;
|
|
7925
8012
|
const interval = setInterval(() => {
|
|
7926
8013
|
attempts++;
|
|
7927
8014
|
const sdk = checkSDK();
|
|
7928
|
-
if (sdk
|
|
8015
|
+
if (sdk) {
|
|
8016
|
+
clearInterval(interval);
|
|
8017
|
+
// Wait for full initialization
|
|
8018
|
+
const readyPromise = sdk.ready ? sdk.ready() : Promise.resolve(sdk);
|
|
8019
|
+
resolve(readyPromise);
|
|
8020
|
+
} else if (attempts >= maxAttempts) {
|
|
7929
8021
|
clearInterval(interval);
|
|
7930
|
-
resolve(
|
|
8022
|
+
resolve(null);
|
|
7931
8023
|
}
|
|
7932
8024
|
}, 100);
|
|
7933
8025
|
});
|
|
@@ -7944,6 +8036,19 @@
|
|
|
7944
8036
|
return null;
|
|
7945
8037
|
},
|
|
7946
8038
|
|
|
8039
|
+
/**
|
|
8040
|
+
* Wait for SDK to be fully initialized
|
|
8041
|
+
* Returns a Promise that resolves when initialization is complete
|
|
8042
|
+
* @returns {Promise<void>}
|
|
8043
|
+
*/
|
|
8044
|
+
ready() {
|
|
8045
|
+
const instance = this.getInstance();
|
|
8046
|
+
if (instance && instance.ready) {
|
|
8047
|
+
return instance.ready();
|
|
8048
|
+
}
|
|
8049
|
+
return Promise.resolve();
|
|
8050
|
+
},
|
|
8051
|
+
|
|
7947
8052
|
/**
|
|
7948
8053
|
* Identify a user with a unique identifier
|
|
7949
8054
|
*
|
package/package.json
CHANGED