@explorins/pers-sdk-react-native 2.0.5 → 2.1.1
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/README.md +117 -4
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useTokenBalances.d.ts +140 -0
- package/dist/hooks/useTokenBalances.d.ts.map +1 -0
- package/dist/hooks/useTokenBalances.js +213 -0
- package/dist/hooks/useWeb3.d.ts +2 -3
- package/dist/hooks/useWeb3.d.ts.map +1 -1
- package/dist/hooks/useWeb3.js +42 -32
- package/dist/index.d.ts +80 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +29617 -29260
- package/dist/index.js.map +1 -1
- package/dist/providers/PersSDKProvider.d.ts.map +1 -1
- package/dist/providers/PersSDKProvider.js +31 -7
- package/dist/storage/rn-secure-storage.d.ts.map +1 -1
- package/dist/storage/rn-secure-storage.js +24 -10
- package/package.json +2 -4
- package/src/hooks/index.ts +7 -1
- package/src/hooks/useTokenBalances.ts +290 -0
- package/src/hooks/useWeb3.ts +45 -35
- package/src/index.ts +89 -8
- package/src/providers/PersSDKProvider.tsx +33 -8
- package/src/storage/rn-secure-storage.ts +27 -10
package/src/index.ts
CHANGED
|
@@ -247,6 +247,7 @@ export {
|
|
|
247
247
|
export {
|
|
248
248
|
useAuth,
|
|
249
249
|
useTokens,
|
|
250
|
+
useTokenBalances,
|
|
250
251
|
useTransactions,
|
|
251
252
|
useTransactionSigner,
|
|
252
253
|
SigningStatus,
|
|
@@ -270,6 +271,9 @@ export type { OnStatusUpdateFn, StatusUpdateData, SigningStatusType } from './ho
|
|
|
270
271
|
// Re-export event types for convenience
|
|
271
272
|
export type { EventsHook, PersEvent, EventHandler, EventFilter, Unsubscribe } from './hooks';
|
|
272
273
|
|
|
274
|
+
// Re-export token balance types for convenience
|
|
275
|
+
export type { TokenBalanceWithToken, UseTokenBalancesOptions, UseTokenBalancesResult } from './hooks';
|
|
276
|
+
|
|
273
277
|
// ==============================================================================
|
|
274
278
|
// PLATFORM ADAPTERS
|
|
275
279
|
// ==============================================================================
|
|
@@ -420,31 +424,108 @@ export {
|
|
|
420
424
|
buildTransferRequest,
|
|
421
425
|
buildPOSTransferRequest,
|
|
422
426
|
buildPOSBurnRequest,
|
|
423
|
-
buildSubmissionRequest
|
|
427
|
+
buildSubmissionRequest,
|
|
428
|
+
ClientTransactionType,
|
|
429
|
+
extractDeadlineFromSigningData,
|
|
430
|
+
buildPendingTransactionData
|
|
424
431
|
} from '@explorins/pers-sdk/transaction';
|
|
425
432
|
|
|
426
|
-
export type { POSAuthorizationOptions } from '@explorins/pers-sdk/transaction';
|
|
433
|
+
export type { POSAuthorizationOptions, PendingTransactionParams } from '@explorins/pers-sdk/transaction';
|
|
427
434
|
|
|
428
435
|
// ==============================================================================
|
|
429
436
|
// ERROR CLASSES (for instanceof checks)
|
|
430
437
|
// ==============================================================================
|
|
431
438
|
|
|
432
439
|
/**
|
|
433
|
-
*
|
|
440
|
+
* Structured error handling for PERS SDK
|
|
441
|
+
*
|
|
442
|
+
* The SDK provides comprehensive error classes and utilities for consistent error handling
|
|
443
|
+
* across all operations. Errors follow the StructuredError pattern from @explorins/pers-shared.
|
|
444
|
+
*
|
|
445
|
+
* **Error Classes:**
|
|
446
|
+
* - `PersApiError` - API errors with structured backend details
|
|
447
|
+
* - `AuthenticationError` - Authentication/authorization failures (401)
|
|
448
|
+
*
|
|
449
|
+
* **Error Utilities:**
|
|
450
|
+
* - `ErrorUtils.getMessage(error)` - Extract user-friendly message
|
|
451
|
+
* - `ErrorUtils.getStatus(error)` - Get HTTP status code
|
|
452
|
+
* - `ErrorUtils.isRetryable(error)` - Check if operation should be retried
|
|
453
|
+
* - `ErrorUtils.isTokenExpiredError(error)` - Detect token expiration
|
|
434
454
|
*
|
|
435
455
|
* @example
|
|
456
|
+
* **Basic Error Handling:**
|
|
436
457
|
* ```typescript
|
|
437
|
-
* import { PersApiError } from '@explorins/pers-sdk-react-native';
|
|
458
|
+
* import { PersApiError, ErrorUtils } from '@explorins/pers-sdk-react-native';
|
|
438
459
|
*
|
|
439
460
|
* try {
|
|
440
461
|
* await sdk.campaigns.claimCampaign({ campaignId });
|
|
441
462
|
* } catch (error) {
|
|
442
463
|
* if (error instanceof PersApiError) {
|
|
443
|
-
*
|
|
444
|
-
* console.log(error.code);
|
|
445
|
-
* console.log(error.status);
|
|
464
|
+
* // Structured error with backend details
|
|
465
|
+
* console.log('Error code:', error.code); // 'CAMPAIGN_BUSINESS_REQUIRED'
|
|
466
|
+
* console.log('Status:', error.status); // 400
|
|
467
|
+
* console.log('Message:', error.message); // Backend error message
|
|
468
|
+
* console.log('User message:', error.userMessage); // User-friendly message
|
|
469
|
+
* console.log('Retryable:', error.retryable); // false
|
|
470
|
+
* } else {
|
|
471
|
+
* // Generic error fallback
|
|
472
|
+
* const message = ErrorUtils.getMessage(error);
|
|
473
|
+
* console.error('Operation failed:', message);
|
|
446
474
|
* }
|
|
447
475
|
* }
|
|
448
476
|
* ```
|
|
477
|
+
*
|
|
478
|
+
* @example
|
|
479
|
+
* **Error Utilities:**
|
|
480
|
+
* ```typescript
|
|
481
|
+
* import { ErrorUtils } from '@explorins/pers-sdk-react-native';
|
|
482
|
+
*
|
|
483
|
+
* try {
|
|
484
|
+
* await someOperation();
|
|
485
|
+
* } catch (error) {
|
|
486
|
+
* const status = ErrorUtils.getStatus(error); // Extract status code
|
|
487
|
+
* const message = ErrorUtils.getMessage(error); // Extract message
|
|
488
|
+
* const retryable = ErrorUtils.isRetryable(error); // Check if retryable
|
|
489
|
+
*
|
|
490
|
+
* if (ErrorUtils.isTokenExpiredError(error)) {
|
|
491
|
+
* // Handle token expiration
|
|
492
|
+
* await refreshToken();
|
|
493
|
+
* } else if (retryable) {
|
|
494
|
+
* // Retry operation
|
|
495
|
+
* await retry(someOperation);
|
|
496
|
+
* } else {
|
|
497
|
+
* // Show error to user
|
|
498
|
+
* showError(message);
|
|
499
|
+
* }
|
|
500
|
+
* }
|
|
501
|
+
* ```
|
|
502
|
+
*
|
|
503
|
+
* @example
|
|
504
|
+
* **React Native Error Display:**
|
|
505
|
+
* ```typescript
|
|
506
|
+
* import { PersApiError, ErrorUtils } from '@explorins/pers-sdk-react-native';
|
|
507
|
+
* import { Alert } from 'react-native';
|
|
508
|
+
*
|
|
509
|
+
* const handleError = (error: unknown) => {
|
|
510
|
+
* if (error instanceof PersApiError) {
|
|
511
|
+
* // Show structured error
|
|
512
|
+
* Alert.alert(
|
|
513
|
+
* 'Error',
|
|
514
|
+
* error.userMessage || error.message,
|
|
515
|
+
* [
|
|
516
|
+
* { text: 'OK' },
|
|
517
|
+
* error.retryable && { text: 'Retry', onPress: retry }
|
|
518
|
+
* ].filter(Boolean)
|
|
519
|
+
* );
|
|
520
|
+
* } else {
|
|
521
|
+
* // Show generic error
|
|
522
|
+
* Alert.alert('Error', ErrorUtils.getMessage(error));
|
|
523
|
+
* }
|
|
524
|
+
* };
|
|
525
|
+
* ```
|
|
449
526
|
*/
|
|
450
|
-
export {
|
|
527
|
+
export {
|
|
528
|
+
PersApiError,
|
|
529
|
+
AuthenticationError,
|
|
530
|
+
ErrorUtils
|
|
531
|
+
} from '@explorins/pers-sdk/core';
|
|
@@ -132,19 +132,37 @@ export const PersSDKProvider: React.FC<{
|
|
|
132
132
|
}
|
|
133
133
|
}, [isInitialized]);
|
|
134
134
|
|
|
135
|
+
const setAuthenticationState = useCallback((user: UserDTO | AdminDTO | null, isAuthenticated: boolean) => {
|
|
136
|
+
setUser(user);
|
|
137
|
+
setIsAuthenticated(isAuthenticated);
|
|
138
|
+
}, []);
|
|
139
|
+
|
|
135
140
|
// Auto-initialize if config is provided
|
|
136
141
|
useEffect(() => {
|
|
137
142
|
if (config && !isInitialized && !initializingRef.current) {
|
|
138
|
-
initialize(config).
|
|
143
|
+
initialize(config).then(async () => {
|
|
144
|
+
// Validate stored tokens on startup
|
|
145
|
+
// SDK's initialize() already calls ensureValidToken() which handles expired tokens
|
|
146
|
+
// This provides an additional safety layer for missing/corrupted tokens
|
|
147
|
+
if (authProvider && sdk) {
|
|
148
|
+
try {
|
|
149
|
+
const hasToken = await sdk.auth.hasValidAuth();
|
|
150
|
+
if (!hasToken) {
|
|
151
|
+
console.log('[PersSDK] No tokens found on startup, ensuring clean state');
|
|
152
|
+
await authProvider.clearTokens();
|
|
153
|
+
setAuthenticationState(null, false);
|
|
154
|
+
}
|
|
155
|
+
// Note: Token expiration validation happens automatically in SDK's initialize()
|
|
156
|
+
// which calls ensureValidToken() → checks expiration → triggers AUTH_FAILED if needed
|
|
157
|
+
} catch (error) {
|
|
158
|
+
console.warn('[PersSDK] Token validation on startup failed:', error);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}).catch(err => {
|
|
139
162
|
console.error('Auto-initialization failed:', err);
|
|
140
163
|
});
|
|
141
164
|
}
|
|
142
|
-
}, [config, isInitialized, initialize]);
|
|
143
|
-
|
|
144
|
-
const setAuthenticationState = useCallback((user: UserDTO | AdminDTO | null, isAuthenticated: boolean) => {
|
|
145
|
-
setUser(user);
|
|
146
|
-
setIsAuthenticated(isAuthenticated);
|
|
147
|
-
}, []);
|
|
165
|
+
}, [config, isInitialized, initialize, authProvider, sdk, setAuthenticationState]);
|
|
148
166
|
|
|
149
167
|
const refreshUserData = useCallback(async (): Promise<void> => {
|
|
150
168
|
if (!sdk || !isAuthenticated || !isInitialized) {
|
|
@@ -190,8 +208,15 @@ export const PersSDKProvider: React.FC<{
|
|
|
190
208
|
}
|
|
191
209
|
|
|
192
210
|
// If authentication failed, clear state
|
|
211
|
+
// Frontend app can observe isAuthenticated state change to show custom UI
|
|
193
212
|
if (status === 'auth_failed') {
|
|
194
|
-
console.log('[PersSDK] Authentication failed
|
|
213
|
+
console.log('[PersSDK] Authentication failed - session expired');
|
|
214
|
+
|
|
215
|
+
// Note: Token clearing already handled by SDK's handleAuthFailure()
|
|
216
|
+
// which calls authProvider.clearTokens() with robust retry logic
|
|
217
|
+
|
|
218
|
+
// Clear React state to sync with SDK
|
|
219
|
+
// This triggers re-render, allowing app to show login screen
|
|
195
220
|
setAuthenticationState(null, false);
|
|
196
221
|
}
|
|
197
222
|
};
|
|
@@ -117,22 +117,39 @@ export class ReactNativeSecureStorage implements TokenStorage {
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
async clear(): Promise<void> {
|
|
120
|
-
|
|
120
|
+
const clearPromises: Promise<void>[] = [];
|
|
121
|
+
|
|
122
|
+
// Clear all known secure keys with retry logic
|
|
121
123
|
for (const key of this.SECURE_KEYS) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
124
|
+
clearPromises.push(
|
|
125
|
+
(async () => {
|
|
126
|
+
try {
|
|
127
|
+
if (Keychain) {
|
|
128
|
+
await Keychain.resetGenericPassword({ service: this.getKeyName(key) });
|
|
129
|
+
}
|
|
130
|
+
} catch (e) {
|
|
131
|
+
console.warn(`[ReactNativeSecureStorage] Retry clearing ${key}`);
|
|
132
|
+
// Retry once
|
|
133
|
+
try {
|
|
134
|
+
if (Keychain) {
|
|
135
|
+
await Keychain.resetGenericPassword({ service: this.getKeyName(key) });
|
|
136
|
+
}
|
|
137
|
+
} catch (retryError) {
|
|
138
|
+
console.error(`[ReactNativeSecureStorage] Failed to clear ${key} after retry:`, retryError);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
})()
|
|
142
|
+
);
|
|
129
143
|
}
|
|
130
144
|
|
|
131
|
-
//
|
|
145
|
+
// Wait for all Keychain clearing to complete (or fail)
|
|
146
|
+
await Promise.allSettled(clearPromises);
|
|
147
|
+
|
|
148
|
+
// Always clear AsyncStorage fallback
|
|
132
149
|
try {
|
|
133
150
|
await this.fallbackStorage.clear();
|
|
134
151
|
} catch (e) {
|
|
135
|
-
console.
|
|
152
|
+
console.error('[ReactNativeSecureStorage] Failed to clear AsyncStorage:', e);
|
|
136
153
|
}
|
|
137
154
|
}
|
|
138
155
|
|