@explorins/pers-sdk-react-native 2.1.2 → 2.1.3
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/dist/hooks/useTokenBalances.d.ts.map +1 -1
- package/dist/hooks/useTokenBalances.js +21 -8
- package/dist/index.js +283 -102
- package/dist/index.js.map +1 -1
- package/dist/providers/PersSDKProvider.d.ts +1 -12
- package/dist/providers/PersSDKProvider.d.ts.map +1 -1
- package/dist/providers/PersSDKProvider.js +50 -25
- package/package.json +2 -2
- package/src/hooks/useTokenBalances.ts +23 -9
- package/src/providers/PersSDKProvider.tsx +58 -39
package/dist/index.js
CHANGED
|
@@ -6828,8 +6828,7 @@ const DEFAULT_PERS_CONFIG = {
|
|
|
6828
6828
|
timeout: 30000,
|
|
6829
6829
|
retries: 3,
|
|
6830
6830
|
tokenRefreshMargin: 60, // Refresh tokens 60 seconds before expiry
|
|
6831
|
-
backgroundRefreshThreshold: 30
|
|
6832
|
-
autoRestoreSession: true // Automatically restore session on initialization
|
|
6831
|
+
backgroundRefreshThreshold: 30 // Use background refresh if >30s remaining
|
|
6833
6832
|
};
|
|
6834
6833
|
/**
|
|
6835
6834
|
* Internal function to construct API root from environment
|
|
@@ -7139,8 +7138,12 @@ class AuthService {
|
|
|
7139
7138
|
await extendedProvider.setProviderToken(providerToken);
|
|
7140
7139
|
}
|
|
7141
7140
|
if (authType && extendedProvider.setAuthType) {
|
|
7141
|
+
console.log('[AuthService] Storing auth type:', authType);
|
|
7142
7142
|
await extendedProvider.setAuthType(authType);
|
|
7143
7143
|
}
|
|
7144
|
+
else {
|
|
7145
|
+
console.warn('[AuthService] Auth type not stored - authType:', authType, 'has setAuthType:', !!extendedProvider.setAuthType);
|
|
7146
|
+
}
|
|
7144
7147
|
// Emit authenticated status on successful token storage
|
|
7145
7148
|
await this.emitAuthStatus(AuthStatus.AUTHENTICATED);
|
|
7146
7149
|
}
|
|
@@ -7159,8 +7162,15 @@ class TokenRefreshManager {
|
|
|
7159
7162
|
this.authService = authService;
|
|
7160
7163
|
this.authProvider = authProvider;
|
|
7161
7164
|
this.tokenRefreshMarginSeconds = 120; // 2 minutes
|
|
7165
|
+
this.lastValidationTime = 0;
|
|
7166
|
+
this.validationCacheDurationMs = 30000; // 30 seconds
|
|
7162
7167
|
}
|
|
7163
7168
|
async ensureValidToken() {
|
|
7169
|
+
const now = Date.now();
|
|
7170
|
+
// Skip validation if we checked recently (within cache duration)
|
|
7171
|
+
if (now - this.lastValidationTime < this.validationCacheDurationMs) {
|
|
7172
|
+
return;
|
|
7173
|
+
}
|
|
7164
7174
|
try {
|
|
7165
7175
|
const token = await this.authProvider.getToken();
|
|
7166
7176
|
if (!token) {
|
|
@@ -7171,6 +7181,14 @@ class TokenRefreshManager {
|
|
|
7171
7181
|
if (!refreshSuccess) {
|
|
7172
7182
|
await this.authService.handleAuthFailure();
|
|
7173
7183
|
}
|
|
7184
|
+
else {
|
|
7185
|
+
// Update validation time on successful refresh
|
|
7186
|
+
this.lastValidationTime = now;
|
|
7187
|
+
}
|
|
7188
|
+
}
|
|
7189
|
+
else {
|
|
7190
|
+
// Token is valid, update validation time
|
|
7191
|
+
this.lastValidationTime = now;
|
|
7174
7192
|
}
|
|
7175
7193
|
}
|
|
7176
7194
|
catch (error) {
|
|
@@ -7254,42 +7272,77 @@ class MemoryTokenStorage {
|
|
|
7254
7272
|
}
|
|
7255
7273
|
}
|
|
7256
7274
|
/**
|
|
7257
|
-
* Token manager for authentication tokens
|
|
7275
|
+
* Token manager for authentication tokens with memory caching
|
|
7258
7276
|
*/
|
|
7259
7277
|
class AuthTokenManager {
|
|
7260
7278
|
constructor(storage = new LocalStorageTokenStorage()) {
|
|
7261
7279
|
this.storage = storage;
|
|
7280
|
+
this.cache = {};
|
|
7262
7281
|
}
|
|
7263
7282
|
async getAccessToken() {
|
|
7283
|
+
// Return cached value if available
|
|
7284
|
+
if (this.cache.accessToken !== undefined) {
|
|
7285
|
+
return this.cache.accessToken;
|
|
7286
|
+
}
|
|
7264
7287
|
const val = await this.storage.get(AUTH_STORAGE_KEYS.ACCESS_TOKEN);
|
|
7265
7288
|
// Ensure we return string for tokens
|
|
7266
|
-
|
|
7289
|
+
const token = typeof val === 'string' ? val : null;
|
|
7290
|
+
this.cache.accessToken = token;
|
|
7291
|
+
return token;
|
|
7267
7292
|
}
|
|
7268
7293
|
async setAccessToken(token) {
|
|
7294
|
+
this.cache.accessToken = token;
|
|
7269
7295
|
await this.storage.set(AUTH_STORAGE_KEYS.ACCESS_TOKEN, token);
|
|
7270
7296
|
}
|
|
7271
7297
|
async getRefreshToken() {
|
|
7298
|
+
// Return cached value if available
|
|
7299
|
+
if (this.cache.refreshToken !== undefined) {
|
|
7300
|
+
return this.cache.refreshToken;
|
|
7301
|
+
}
|
|
7272
7302
|
const val = await this.storage.get(AUTH_STORAGE_KEYS.REFRESH_TOKEN);
|
|
7273
|
-
|
|
7303
|
+
const token = typeof val === 'string' ? val : null;
|
|
7304
|
+
this.cache.refreshToken = token;
|
|
7305
|
+
return token;
|
|
7274
7306
|
}
|
|
7275
7307
|
async setRefreshToken(token) {
|
|
7308
|
+
this.cache.refreshToken = token;
|
|
7276
7309
|
await this.storage.set(AUTH_STORAGE_KEYS.REFRESH_TOKEN, token);
|
|
7277
7310
|
}
|
|
7278
7311
|
async getProviderToken() {
|
|
7312
|
+
// Return cached value if available
|
|
7313
|
+
if (this.cache.providerToken !== undefined) {
|
|
7314
|
+
return this.cache.providerToken;
|
|
7315
|
+
}
|
|
7279
7316
|
const val = await this.storage.get(AUTH_STORAGE_KEYS.PROVIDER_TOKEN);
|
|
7280
|
-
|
|
7317
|
+
const token = typeof val === 'string' ? val : null;
|
|
7318
|
+
this.cache.providerToken = token;
|
|
7319
|
+
return token;
|
|
7281
7320
|
}
|
|
7282
7321
|
async setProviderToken(token) {
|
|
7322
|
+
this.cache.providerToken = token;
|
|
7283
7323
|
await this.storage.set(AUTH_STORAGE_KEYS.PROVIDER_TOKEN, token);
|
|
7284
7324
|
}
|
|
7285
7325
|
async getAuthType() {
|
|
7326
|
+
// Return cached value if available
|
|
7327
|
+
if (this.cache.authType !== undefined) {
|
|
7328
|
+
return this.cache.authType;
|
|
7329
|
+
}
|
|
7286
7330
|
const authType = await this.storage.get(AUTH_STORAGE_KEYS.AUTH_TYPE);
|
|
7287
|
-
|
|
7331
|
+
const type = typeof authType === 'string' ? authType : null;
|
|
7332
|
+
this.cache.authType = type;
|
|
7333
|
+
return type;
|
|
7288
7334
|
}
|
|
7289
7335
|
async setAuthType(authType) {
|
|
7336
|
+
this.cache.authType = authType;
|
|
7290
7337
|
await this.storage.set(AUTH_STORAGE_KEYS.AUTH_TYPE, authType);
|
|
7291
7338
|
}
|
|
7292
7339
|
async setTokens(accessToken, refreshToken, providerToken) {
|
|
7340
|
+
// Update cache for all tokens
|
|
7341
|
+
this.cache.accessToken = accessToken;
|
|
7342
|
+
if (refreshToken)
|
|
7343
|
+
this.cache.refreshToken = refreshToken;
|
|
7344
|
+
if (providerToken)
|
|
7345
|
+
this.cache.providerToken = providerToken;
|
|
7293
7346
|
await this.setAccessToken(accessToken);
|
|
7294
7347
|
if (refreshToken)
|
|
7295
7348
|
await this.setRefreshToken(refreshToken);
|
|
@@ -7297,13 +7350,23 @@ class AuthTokenManager {
|
|
|
7297
7350
|
await this.setProviderToken(providerToken);
|
|
7298
7351
|
}
|
|
7299
7352
|
async clearAllTokens() {
|
|
7353
|
+
// Clear cache
|
|
7354
|
+
this.cache = {};
|
|
7300
7355
|
await this.storage.clear();
|
|
7301
7356
|
}
|
|
7302
7357
|
async hasAccessToken() {
|
|
7358
|
+
// Use cached value if available to avoid storage read
|
|
7359
|
+
if (this.cache.accessToken !== undefined) {
|
|
7360
|
+
return !!this.cache.accessToken;
|
|
7361
|
+
}
|
|
7303
7362
|
const token = await this.getAccessToken();
|
|
7304
7363
|
return !!token;
|
|
7305
7364
|
}
|
|
7306
7365
|
async hasRefreshToken() {
|
|
7366
|
+
// Use cached value if available to avoid storage read
|
|
7367
|
+
if (this.cache.refreshToken !== undefined) {
|
|
7368
|
+
return !!this.cache.refreshToken;
|
|
7369
|
+
}
|
|
7307
7370
|
const token = await this.getRefreshToken();
|
|
7308
7371
|
return !!token;
|
|
7309
7372
|
}
|
|
@@ -7531,6 +7594,7 @@ class DPoPManager {
|
|
|
7531
7594
|
class DefaultAuthProvider {
|
|
7532
7595
|
constructor(config = {}) {
|
|
7533
7596
|
this.config = config;
|
|
7597
|
+
this.authTypeCache = null;
|
|
7534
7598
|
this.authType = config.authType || exports.AccountOwnerType.USER;
|
|
7535
7599
|
const storage = config.storage || this.createStorage();
|
|
7536
7600
|
this.tokenManager = new AuthTokenManager(storage);
|
|
@@ -7591,6 +7655,15 @@ class DefaultAuthProvider {
|
|
|
7591
7655
|
}
|
|
7592
7656
|
async setAccessToken(token) {
|
|
7593
7657
|
await this.tokenManager.setAccessToken(token);
|
|
7658
|
+
// Invalidate cache when token changes
|
|
7659
|
+
this.authTypeCache = null;
|
|
7660
|
+
// Eager DPoP key generation after login
|
|
7661
|
+
if (this.dpopManager) {
|
|
7662
|
+
// Fire and forget - generate keys in background
|
|
7663
|
+
this.dpopManager.ensureKeyPair().catch(err => {
|
|
7664
|
+
console.warn('[DefaultAuthProvider] DPoP key generation failed:', err);
|
|
7665
|
+
});
|
|
7666
|
+
}
|
|
7594
7667
|
}
|
|
7595
7668
|
async setRefreshToken(token) {
|
|
7596
7669
|
await this.tokenManager.setRefreshToken(token);
|
|
@@ -7600,16 +7673,51 @@ class DefaultAuthProvider {
|
|
|
7600
7673
|
}
|
|
7601
7674
|
async clearTokens() {
|
|
7602
7675
|
await this.tokenManager.clearAllTokens();
|
|
7676
|
+
// Invalidate cache on clear
|
|
7677
|
+
this.authTypeCache = null;
|
|
7603
7678
|
if (this.dpopManager) {
|
|
7604
7679
|
await this.dpopManager.clearKeys();
|
|
7605
7680
|
}
|
|
7606
7681
|
}
|
|
7607
7682
|
async setTokens(accessToken, refreshToken, providerToken) {
|
|
7608
7683
|
await this.tokenManager.setTokens(accessToken, refreshToken, providerToken);
|
|
7684
|
+
// Invalidate cache when tokens change
|
|
7685
|
+
this.authTypeCache = null;
|
|
7609
7686
|
}
|
|
7610
7687
|
async hasValidToken() {
|
|
7611
7688
|
return this.tokenManager.hasAccessToken();
|
|
7612
7689
|
}
|
|
7690
|
+
async getAuthType() {
|
|
7691
|
+
// Read from JWT instead of separate storage for single source of truth
|
|
7692
|
+
const token = await this.tokenManager.getAccessToken();
|
|
7693
|
+
if (!token) {
|
|
7694
|
+
this.authTypeCache = null;
|
|
7695
|
+
return null;
|
|
7696
|
+
}
|
|
7697
|
+
// Return cached result if token hasn't changed
|
|
7698
|
+
if (this.authTypeCache && this.authTypeCache.token === token) {
|
|
7699
|
+
return this.authTypeCache.type;
|
|
7700
|
+
}
|
|
7701
|
+
try {
|
|
7702
|
+
// Decode JWT payload (without verification - we just need to read the claim)
|
|
7703
|
+
const parts = token.split('.');
|
|
7704
|
+
if (parts.length !== 3) {
|
|
7705
|
+
console.warn('[DefaultAuthProvider] Invalid JWT format');
|
|
7706
|
+
return null;
|
|
7707
|
+
}
|
|
7708
|
+
const payloadJson = atob(parts[1]);
|
|
7709
|
+
const payload = JSON.parse(payloadJson);
|
|
7710
|
+
// Use accountOwnerType from AuthJWTPayload interface
|
|
7711
|
+
const authType = payload.accountType || null;
|
|
7712
|
+
// Cache result with token signature
|
|
7713
|
+
this.authTypeCache = { token, type: authType };
|
|
7714
|
+
return authType;
|
|
7715
|
+
}
|
|
7716
|
+
catch (error) {
|
|
7717
|
+
console.warn('[DefaultAuthProvider] Failed to parse auth type from JWT:', error);
|
|
7718
|
+
return null;
|
|
7719
|
+
}
|
|
7720
|
+
}
|
|
7613
7721
|
}
|
|
7614
7722
|
|
|
7615
7723
|
/**
|
|
@@ -13351,78 +13459,71 @@ class PersSDK {
|
|
|
13351
13459
|
// Initialize event emitter and wire to API client for error events
|
|
13352
13460
|
this._events = new PersEventEmitter();
|
|
13353
13461
|
this.apiClient.setEvents(this._events);
|
|
13354
|
-
// Initialize domain managers (pass events to managers that emit success events)
|
|
13355
|
-
this._auth = new AuthManager(this.apiClient, this._events);
|
|
13356
|
-
this._users = new UserManager(this.apiClient, this._events);
|
|
13357
|
-
this._userStatus = new UserStatusManager(this.apiClient);
|
|
13358
|
-
this._tokens = new TokenManager(this.apiClient);
|
|
13359
|
-
this._businesses = new BusinessManager(this.apiClient, this._events);
|
|
13360
|
-
this._campaigns = new CampaignManager(this.apiClient, this._events);
|
|
13361
|
-
this._redemptions = new RedemptionManager(this.apiClient, this._events);
|
|
13362
|
-
this._transactions = new TransactionManager(this.apiClient, this._events);
|
|
13363
|
-
this._purchases = new PurchaseManager(this.apiClient);
|
|
13364
|
-
this._files = new FileManager(this.apiClient);
|
|
13365
|
-
this._tenants = new TenantManager(this.apiClient);
|
|
13366
|
-
this._apiKeys = new ApiKeyManager(this.apiClient);
|
|
13367
|
-
this._analytics = new AnalyticsManager(this.apiClient);
|
|
13368
|
-
this._donations = new DonationManager(this.apiClient);
|
|
13369
|
-
// Automatically restore session if enabled and tokens exist
|
|
13370
|
-
if (config.autoRestoreSession !== false) {
|
|
13371
|
-
this.restoreSessionIfTokensExist().catch(err => {
|
|
13372
|
-
console.warn('[PersSDK] Failed to restore session on initialization:', err);
|
|
13373
|
-
});
|
|
13374
|
-
}
|
|
13375
13462
|
}
|
|
13376
13463
|
/**
|
|
13377
|
-
* Restore user session
|
|
13464
|
+
* Restore user session from stored tokens
|
|
13378
13465
|
*
|
|
13379
|
-
*
|
|
13380
|
-
*
|
|
13381
|
-
*
|
|
13466
|
+
* Call this method after SDK initialization to restore the user's session
|
|
13467
|
+
* if valid tokens exist. This is useful for maintaining login state across
|
|
13468
|
+
* app restarts.
|
|
13469
|
+
*
|
|
13470
|
+
* **Important:** Only works for USER and BUSINESS accounts. Admin/tenant
|
|
13471
|
+
* accounts don't support user data fetching, so this will return null for them
|
|
13472
|
+
* (though their tokens remain valid for API calls).
|
|
13382
13473
|
*
|
|
13383
13474
|
* Emits auth domain success event when session is restored.
|
|
13384
13475
|
*
|
|
13385
|
-
* @
|
|
13386
|
-
* @
|
|
13476
|
+
* @returns Promise resolving to User data if session restored, null if no session or admin account
|
|
13477
|
+
* @throws {PersError} If token validation or user fetch fails
|
|
13478
|
+
*
|
|
13479
|
+
* @example
|
|
13480
|
+
* ```typescript
|
|
13481
|
+
* // Call after initial render for better perceived performance
|
|
13482
|
+
* const user = await sdk.restoreSession();
|
|
13483
|
+
* if (user) {
|
|
13484
|
+
* console.log('Welcome back,', user.name);
|
|
13485
|
+
* }
|
|
13486
|
+
* ```
|
|
13387
13487
|
*/
|
|
13388
|
-
async
|
|
13389
|
-
|
|
13390
|
-
|
|
13391
|
-
|
|
13392
|
-
|
|
13393
|
-
|
|
13394
|
-
|
|
13395
|
-
|
|
13396
|
-
|
|
13397
|
-
|
|
13398
|
-
|
|
13399
|
-
|
|
13400
|
-
|
|
13401
|
-
domain: 'authentication',
|
|
13402
|
-
userMessage: 'Session restored successfully',
|
|
13403
|
-
details: { userId: userData.id }
|
|
13404
|
-
});
|
|
13405
|
-
}
|
|
13406
|
-
catch (error) {
|
|
13407
|
-
// Tokens exist but are invalid (expired, revoked, etc.)
|
|
13408
|
-
console.warn('[PersSDK] Failed to restore session, tokens may be invalid:', error);
|
|
13409
|
-
await this._auth.clearAuth();
|
|
13410
|
-
// Emit restoration failed event
|
|
13411
|
-
this._events.emitError({
|
|
13412
|
-
type: 'session_restoration_failed',
|
|
13413
|
-
domain: 'authentication',
|
|
13414
|
-
userMessage: 'Session restoration failed',
|
|
13415
|
-
code: 'SESSION_INVALID',
|
|
13416
|
-
details: { error }
|
|
13417
|
-
});
|
|
13418
|
-
}
|
|
13419
|
-
}
|
|
13420
|
-
else {
|
|
13421
|
-
console.log('[PersSDK] No valid tokens found');
|
|
13488
|
+
async restoreSession() {
|
|
13489
|
+
const hasToken = await this.auth.hasValidAuth();
|
|
13490
|
+
if (!hasToken) {
|
|
13491
|
+
return null;
|
|
13492
|
+
}
|
|
13493
|
+
// Check auth type - only restore session for user and business accounts
|
|
13494
|
+
const authProvider = this.apiClient.getConfig().authProvider;
|
|
13495
|
+
if (authProvider?.getAuthType) {
|
|
13496
|
+
const authType = await authProvider.getAuthType();
|
|
13497
|
+
if (authType === exports.AccountOwnerType.TENANT) {
|
|
13498
|
+
// Admin sessions don't support getCurrentUser(), skip restoration
|
|
13499
|
+
// Tokens are valid and will be used for API calls
|
|
13500
|
+
return null;
|
|
13422
13501
|
}
|
|
13423
13502
|
}
|
|
13503
|
+
try {
|
|
13504
|
+
// Fetch user data to validate tokens and restore session
|
|
13505
|
+
const userData = await this.auth.getCurrentUser();
|
|
13506
|
+
// Emit event through proper event emitter
|
|
13507
|
+
this._events.emitSuccess({
|
|
13508
|
+
type: 'session_restored',
|
|
13509
|
+
domain: 'authentication',
|
|
13510
|
+
userMessage: 'Session restored successfully',
|
|
13511
|
+
details: { userId: userData.id }
|
|
13512
|
+
});
|
|
13513
|
+
return userData;
|
|
13514
|
+
}
|
|
13424
13515
|
catch (error) {
|
|
13425
|
-
|
|
13516
|
+
// Tokens exist but are invalid (expired, revoked, etc.)
|
|
13517
|
+
await this.auth.clearAuth();
|
|
13518
|
+
// Emit restoration failed event
|
|
13519
|
+
this._events.emitError({
|
|
13520
|
+
type: 'session_restoration_failed',
|
|
13521
|
+
domain: 'authentication',
|
|
13522
|
+
userMessage: 'Session restoration failed',
|
|
13523
|
+
code: 'SESSION_INVALID',
|
|
13524
|
+
details: { error }
|
|
13525
|
+
});
|
|
13526
|
+
throw error;
|
|
13426
13527
|
}
|
|
13427
13528
|
}
|
|
13428
13529
|
/**
|
|
@@ -13511,6 +13612,9 @@ class PersSDK {
|
|
|
13511
13612
|
* @see {@link AuthManager} for detailed documentation
|
|
13512
13613
|
*/
|
|
13513
13614
|
get auth() {
|
|
13615
|
+
if (!this._auth) {
|
|
13616
|
+
this._auth = new AuthManager(this.apiClient, this._events);
|
|
13617
|
+
}
|
|
13514
13618
|
return this._auth;
|
|
13515
13619
|
}
|
|
13516
13620
|
/**
|
|
@@ -13524,6 +13628,9 @@ class PersSDK {
|
|
|
13524
13628
|
* ```
|
|
13525
13629
|
*/
|
|
13526
13630
|
get users() {
|
|
13631
|
+
if (!this._users) {
|
|
13632
|
+
this._users = new UserManager(this.apiClient, this._events);
|
|
13633
|
+
}
|
|
13527
13634
|
return this._users;
|
|
13528
13635
|
}
|
|
13529
13636
|
/**
|
|
@@ -13537,6 +13644,9 @@ class PersSDK {
|
|
|
13537
13644
|
* ```
|
|
13538
13645
|
*/
|
|
13539
13646
|
get userStatus() {
|
|
13647
|
+
if (!this._userStatus) {
|
|
13648
|
+
this._userStatus = new UserStatusManager(this.apiClient);
|
|
13649
|
+
}
|
|
13540
13650
|
return this._userStatus;
|
|
13541
13651
|
}
|
|
13542
13652
|
/**
|
|
@@ -13550,6 +13660,9 @@ class PersSDK {
|
|
|
13550
13660
|
* ```
|
|
13551
13661
|
*/
|
|
13552
13662
|
get tokens() {
|
|
13663
|
+
if (!this._tokens) {
|
|
13664
|
+
this._tokens = new TokenManager(this.apiClient);
|
|
13665
|
+
}
|
|
13553
13666
|
return this._tokens;
|
|
13554
13667
|
}
|
|
13555
13668
|
/**
|
|
@@ -13563,6 +13676,9 @@ class PersSDK {
|
|
|
13563
13676
|
* ```
|
|
13564
13677
|
*/
|
|
13565
13678
|
get businesses() {
|
|
13679
|
+
if (!this._businesses) {
|
|
13680
|
+
this._businesses = new BusinessManager(this.apiClient, this._events);
|
|
13681
|
+
}
|
|
13566
13682
|
return this._businesses;
|
|
13567
13683
|
}
|
|
13568
13684
|
/**
|
|
@@ -13576,6 +13692,9 @@ class PersSDK {
|
|
|
13576
13692
|
* ```
|
|
13577
13693
|
*/
|
|
13578
13694
|
get campaigns() {
|
|
13695
|
+
if (!this._campaigns) {
|
|
13696
|
+
this._campaigns = new CampaignManager(this.apiClient, this._events);
|
|
13697
|
+
}
|
|
13579
13698
|
return this._campaigns;
|
|
13580
13699
|
}
|
|
13581
13700
|
/**
|
|
@@ -13589,6 +13708,9 @@ class PersSDK {
|
|
|
13589
13708
|
* ```
|
|
13590
13709
|
*/
|
|
13591
13710
|
get redemptions() {
|
|
13711
|
+
if (!this._redemptions) {
|
|
13712
|
+
this._redemptions = new RedemptionManager(this.apiClient, this._events);
|
|
13713
|
+
}
|
|
13592
13714
|
return this._redemptions;
|
|
13593
13715
|
}
|
|
13594
13716
|
/**
|
|
@@ -13602,6 +13724,9 @@ class PersSDK {
|
|
|
13602
13724
|
* ```
|
|
13603
13725
|
*/
|
|
13604
13726
|
get transactions() {
|
|
13727
|
+
if (!this._transactions) {
|
|
13728
|
+
this._transactions = new TransactionManager(this.apiClient, this._events);
|
|
13729
|
+
}
|
|
13605
13730
|
return this._transactions;
|
|
13606
13731
|
}
|
|
13607
13732
|
/**
|
|
@@ -13615,6 +13740,9 @@ class PersSDK {
|
|
|
13615
13740
|
* ```
|
|
13616
13741
|
*/
|
|
13617
13742
|
get purchases() {
|
|
13743
|
+
if (!this._purchases) {
|
|
13744
|
+
this._purchases = new PurchaseManager(this.apiClient);
|
|
13745
|
+
}
|
|
13618
13746
|
return this._purchases;
|
|
13619
13747
|
}
|
|
13620
13748
|
/**
|
|
@@ -13628,6 +13756,9 @@ class PersSDK {
|
|
|
13628
13756
|
* ```
|
|
13629
13757
|
*/
|
|
13630
13758
|
get files() {
|
|
13759
|
+
if (!this._files) {
|
|
13760
|
+
this._files = new FileManager(this.apiClient);
|
|
13761
|
+
}
|
|
13631
13762
|
return this._files;
|
|
13632
13763
|
}
|
|
13633
13764
|
/**
|
|
@@ -13641,6 +13772,9 @@ class PersSDK {
|
|
|
13641
13772
|
* ```
|
|
13642
13773
|
*/
|
|
13643
13774
|
get tenants() {
|
|
13775
|
+
if (!this._tenants) {
|
|
13776
|
+
this._tenants = new TenantManager(this.apiClient);
|
|
13777
|
+
}
|
|
13644
13778
|
return this._tenants;
|
|
13645
13779
|
}
|
|
13646
13780
|
/**
|
|
@@ -13665,6 +13799,9 @@ class PersSDK {
|
|
|
13665
13799
|
* ```
|
|
13666
13800
|
*/
|
|
13667
13801
|
get apiKeys() {
|
|
13802
|
+
if (!this._apiKeys) {
|
|
13803
|
+
this._apiKeys = new ApiKeyManager(this.apiClient);
|
|
13804
|
+
}
|
|
13668
13805
|
return this._apiKeys;
|
|
13669
13806
|
}
|
|
13670
13807
|
/**
|
|
@@ -13676,6 +13813,9 @@ class PersSDK {
|
|
|
13676
13813
|
* ```
|
|
13677
13814
|
*/
|
|
13678
13815
|
get analytics() {
|
|
13816
|
+
if (!this._analytics) {
|
|
13817
|
+
this._analytics = new AnalyticsManager(this.apiClient);
|
|
13818
|
+
}
|
|
13679
13819
|
return this._analytics;
|
|
13680
13820
|
}
|
|
13681
13821
|
/**
|
|
@@ -13687,6 +13827,9 @@ class PersSDK {
|
|
|
13687
13827
|
* ```
|
|
13688
13828
|
*/
|
|
13689
13829
|
get donations() {
|
|
13830
|
+
if (!this._donations) {
|
|
13831
|
+
this._donations = new DonationManager(this.apiClient);
|
|
13832
|
+
}
|
|
13690
13833
|
return this._donations;
|
|
13691
13834
|
}
|
|
13692
13835
|
/**
|
|
@@ -14581,11 +14724,19 @@ const SDKContext = react.createContext(null);
|
|
|
14581
14724
|
// Provider component
|
|
14582
14725
|
const PersSDKProvider = ({ children, config }) => {
|
|
14583
14726
|
const initializingRef = react.useRef(false);
|
|
14727
|
+
// State refs for stable functions to read current values
|
|
14728
|
+
const sdkRef = react.useRef(null);
|
|
14729
|
+
const isInitializedRef = react.useRef(false);
|
|
14730
|
+
const isAuthenticatedRef = react.useRef(false);
|
|
14584
14731
|
const [sdk, setSdk] = react.useState(null);
|
|
14585
14732
|
const [authProvider, setAuthProvider] = react.useState(null);
|
|
14586
14733
|
const [isInitialized, setIsInitialized] = react.useState(false);
|
|
14587
14734
|
const [isAuthenticated, setIsAuthenticated] = react.useState(false);
|
|
14588
14735
|
const [user, setUser] = react.useState(null);
|
|
14736
|
+
// Keep state refs in sync immediately (not in useEffect to avoid race conditions)
|
|
14737
|
+
sdkRef.current = sdk;
|
|
14738
|
+
isInitializedRef.current = isInitialized;
|
|
14739
|
+
isAuthenticatedRef.current = isAuthenticated;
|
|
14589
14740
|
const initialize = react.useCallback(async (config) => {
|
|
14590
14741
|
// Prevent multiple initializations
|
|
14591
14742
|
if (isInitialized || initializingRef.current) {
|
|
@@ -14652,18 +14803,40 @@ const PersSDKProvider = ({ children, config }) => {
|
|
|
14652
14803
|
}
|
|
14653
14804
|
}, [config, isInitialized, initialize]);
|
|
14654
14805
|
const refreshUserData = react.useCallback(async () => {
|
|
14655
|
-
|
|
14656
|
-
|
|
14806
|
+
// Read from refs to get current values
|
|
14807
|
+
const currentSdk = sdkRef.current;
|
|
14808
|
+
const currentIsInitialized = isInitializedRef.current;
|
|
14809
|
+
if (!currentSdk || !currentIsInitialized) {
|
|
14810
|
+
throw new Error('SDK not initialized. Cannot refresh user data.');
|
|
14657
14811
|
}
|
|
14658
14812
|
try {
|
|
14659
|
-
const freshUserData = await
|
|
14813
|
+
const freshUserData = await currentSdk.users.getCurrentUser();
|
|
14660
14814
|
setUser(freshUserData);
|
|
14661
14815
|
}
|
|
14662
14816
|
catch (error) {
|
|
14663
14817
|
console.error('Failed to refresh user data:', error);
|
|
14664
14818
|
throw error;
|
|
14665
14819
|
}
|
|
14666
|
-
}, [
|
|
14820
|
+
}, []); // No dependencies - reads from refs
|
|
14821
|
+
const restoreSession = react.useCallback(async () => {
|
|
14822
|
+
// Read from refs to get current values
|
|
14823
|
+
const currentSdk = sdkRef.current;
|
|
14824
|
+
const currentIsInitialized = isInitializedRef.current;
|
|
14825
|
+
if (!currentSdk || !currentIsInitialized) {
|
|
14826
|
+
throw new Error('SDK not initialized. Call initialize() first.');
|
|
14827
|
+
}
|
|
14828
|
+
try {
|
|
14829
|
+
const userData = await currentSdk.restoreSession();
|
|
14830
|
+
if (userData) {
|
|
14831
|
+
setAuthenticationState(userData, true);
|
|
14832
|
+
}
|
|
14833
|
+
return userData;
|
|
14834
|
+
}
|
|
14835
|
+
catch (error) {
|
|
14836
|
+
console.error('[PersSDK] Failed to restore session:', error);
|
|
14837
|
+
throw error;
|
|
14838
|
+
}
|
|
14839
|
+
}, [setAuthenticationState]); // Depends on setAuthenticationState
|
|
14667
14840
|
// Listen for authentication events from core SDK
|
|
14668
14841
|
// Set up immediately when SDK is created (don't wait for isInitialized)
|
|
14669
14842
|
// to catch session_restored events that fire during SDK initialization
|
|
@@ -14676,13 +14849,18 @@ const PersSDKProvider = ({ children, config }) => {
|
|
|
14676
14849
|
// Session restored successfully - sync React state
|
|
14677
14850
|
if (event.type === 'session_restored') {
|
|
14678
14851
|
console.log('[PersSDK] Session restoration event received, syncing state...');
|
|
14679
|
-
|
|
14680
|
-
|
|
14681
|
-
|
|
14682
|
-
|
|
14683
|
-
.
|
|
14684
|
-
|
|
14685
|
-
|
|
14852
|
+
// Read user from event details if available, otherwise fetch
|
|
14853
|
+
const userId = event.details?.userId;
|
|
14854
|
+
if (userId) {
|
|
14855
|
+
// User ID available, fetch user data
|
|
14856
|
+
sdk.users.getCurrentUser()
|
|
14857
|
+
.then(userData => {
|
|
14858
|
+
setAuthenticationState(userData, true);
|
|
14859
|
+
})
|
|
14860
|
+
.catch(error => {
|
|
14861
|
+
console.error('[PersSDK] Failed to sync restored session:', error);
|
|
14862
|
+
});
|
|
14863
|
+
}
|
|
14686
14864
|
}
|
|
14687
14865
|
// Session restoration failed or auth error - clear React state
|
|
14688
14866
|
if (event.type === 'session_restoration_failed' || event.code === 'AUTH_FAILED') {
|
|
@@ -14720,28 +14898,17 @@ const PersSDKProvider = ({ children, config }) => {
|
|
|
14720
14898
|
const contextValue = react.useMemo(() => ({
|
|
14721
14899
|
// Main SDK instance
|
|
14722
14900
|
sdk,
|
|
14723
|
-
// Manager shortcuts for convenience
|
|
14724
|
-
auth: sdk?.auth || null,
|
|
14725
|
-
users: sdk?.users || null,
|
|
14726
|
-
tokens: sdk?.tokens || null,
|
|
14727
|
-
businesses: sdk?.businesses || null,
|
|
14728
|
-
campaigns: sdk?.campaigns || null,
|
|
14729
|
-
redemptions: sdk?.redemptions || null,
|
|
14730
|
-
transactions: sdk?.transactions || null,
|
|
14731
|
-
purchases: sdk?.purchases || null,
|
|
14732
|
-
tenants: sdk?.tenants || null,
|
|
14733
|
-
analytics: sdk?.analytics || null,
|
|
14734
|
-
donations: sdk?.donations || null,
|
|
14735
14901
|
// Platform-specific providers
|
|
14736
14902
|
authProvider,
|
|
14737
14903
|
// State
|
|
14738
14904
|
isInitialized,
|
|
14739
14905
|
isAuthenticated,
|
|
14740
14906
|
user,
|
|
14741
|
-
// Methods
|
|
14907
|
+
// Methods - expose functions directly, not through refs
|
|
14742
14908
|
initialize,
|
|
14743
14909
|
setAuthenticationState,
|
|
14744
14910
|
refreshUserData,
|
|
14911
|
+
restoreSession,
|
|
14745
14912
|
}), [
|
|
14746
14913
|
sdk,
|
|
14747
14914
|
authProvider,
|
|
@@ -14750,7 +14917,8 @@ const PersSDKProvider = ({ children, config }) => {
|
|
|
14750
14917
|
user,
|
|
14751
14918
|
initialize,
|
|
14752
14919
|
setAuthenticationState,
|
|
14753
|
-
refreshUserData
|
|
14920
|
+
refreshUserData,
|
|
14921
|
+
restoreSession
|
|
14754
14922
|
]);
|
|
14755
14923
|
return (jsxRuntime.jsx(SDKContext.Provider, { value: contextValue, children: children }));
|
|
14756
14924
|
};
|
|
@@ -36091,7 +36259,7 @@ const useWeb3 = () => {
|
|
|
36091
36259
|
*/
|
|
36092
36260
|
function useTokenBalances(options) {
|
|
36093
36261
|
const { accountAddress, availableTokens = [], autoLoad = true, refreshInterval = 0 } = options;
|
|
36094
|
-
const { isAuthenticated } = usePersSDK();
|
|
36262
|
+
const { isAuthenticated, sdk } = usePersSDK();
|
|
36095
36263
|
const web3 = useWeb3();
|
|
36096
36264
|
const [tokenBalances, setTokenBalances] = react.useState([]);
|
|
36097
36265
|
const [isLoading, setIsLoading] = react.useState(false);
|
|
@@ -36193,15 +36361,28 @@ function useTokenBalances(options) {
|
|
|
36193
36361
|
loadBalances();
|
|
36194
36362
|
}
|
|
36195
36363
|
}, [autoLoad, isAvailable, availableTokens.length, loadBalances]);
|
|
36196
|
-
//
|
|
36364
|
+
// Event-driven refresh: listen for transaction events instead of polling
|
|
36197
36365
|
react.useEffect(() => {
|
|
36198
|
-
if (refreshInterval
|
|
36199
|
-
|
|
36366
|
+
if (!sdk || refreshInterval <= 0 || !isAvailable)
|
|
36367
|
+
return;
|
|
36368
|
+
// Subscribe to transaction domain events
|
|
36369
|
+
const unsubscribe = sdk.events.subscribe((event) => {
|
|
36370
|
+
if (event.domain === 'transaction' && event.type === 'transaction_completed') {
|
|
36371
|
+
console.log('[useTokenBalances] Transaction completed, refreshing balances...');
|
|
36200
36372
|
loadBalances();
|
|
36201
|
-
}
|
|
36202
|
-
|
|
36203
|
-
|
|
36204
|
-
|
|
36373
|
+
}
|
|
36374
|
+
}, { domains: ['transaction'] });
|
|
36375
|
+
// Also set up a fallback polling interval (much longer than before)
|
|
36376
|
+
// This handles cases where events might be missed
|
|
36377
|
+
const fallbackInterval = Math.max(refreshInterval, 60000); // Minimum 1 minute
|
|
36378
|
+
const intervalId = setInterval(() => {
|
|
36379
|
+
loadBalances();
|
|
36380
|
+
}, fallbackInterval);
|
|
36381
|
+
return () => {
|
|
36382
|
+
unsubscribe();
|
|
36383
|
+
clearInterval(intervalId);
|
|
36384
|
+
};
|
|
36385
|
+
}, [sdk, refreshInterval, isAvailable, loadBalances]);
|
|
36205
36386
|
return {
|
|
36206
36387
|
tokenBalances,
|
|
36207
36388
|
isLoading,
|