@explorins/pers-sdk-react-native 1.5.18 → 1.5.20

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.
Files changed (40) hide show
  1. package/README.md +336 -234
  2. package/dist/hooks/index.d.ts +1 -1
  3. package/dist/hooks/index.d.ts.map +1 -1
  4. package/dist/hooks/useAuth.d.ts +1 -1
  5. package/dist/hooks/useAuth.d.ts.map +1 -1
  6. package/dist/hooks/useAuth.js +7 -7
  7. package/dist/hooks/useRedemptions.d.ts.map +1 -1
  8. package/dist/hooks/useRedemptions.js +4 -4
  9. package/dist/hooks/useTenants.d.ts.map +1 -1
  10. package/dist/hooks/useTenants.js +0 -1
  11. package/dist/hooks/useTransactionSigner.d.ts +186 -53
  12. package/dist/hooks/useTransactionSigner.d.ts.map +1 -1
  13. package/dist/hooks/useTransactionSigner.js +285 -102
  14. package/dist/hooks/useTransactions.d.ts +2 -2
  15. package/dist/hooks/useTransactions.d.ts.map +1 -1
  16. package/dist/hooks/useTransactions.js +9 -10
  17. package/dist/index.d.ts +211 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +20918 -18336
  20. package/dist/index.js.map +1 -1
  21. package/dist/providers/PersSDKProvider.d.ts +2 -8
  22. package/dist/providers/PersSDKProvider.d.ts.map +1 -1
  23. package/dist/providers/PersSDKProvider.js +16 -31
  24. package/dist/providers/react-native-auth-provider.d.ts +25 -36
  25. package/dist/providers/react-native-auth-provider.d.ts.map +1 -1
  26. package/dist/providers/react-native-auth-provider.js +36 -146
  27. package/dist/storage/async-storage-token-storage.d.ts +22 -0
  28. package/dist/storage/async-storage-token-storage.d.ts.map +1 -0
  29. package/dist/storage/async-storage-token-storage.js +113 -0
  30. package/package.json +16 -11
  31. package/src/hooks/index.ts +1 -1
  32. package/src/hooks/useAuth.ts +7 -7
  33. package/src/hooks/useRedemptions.ts +5 -7
  34. package/src/hooks/useTenants.ts +0 -1
  35. package/src/hooks/useTransactionSigner.ts +322 -166
  36. package/src/hooks/useTransactions.ts +12 -11
  37. package/src/index.ts +243 -7
  38. package/src/providers/PersSDKProvider.tsx +21 -40
  39. package/src/providers/react-native-auth-provider.ts +59 -176
  40. package/src/storage/async-storage-token-storage.ts +133 -0
@@ -1,189 +1,72 @@
1
- import type { PersAuthProvider } from '@explorins/pers-sdk/core';
2
- import AsyncStorage from '@react-native-async-storage/async-storage';
1
+ /**
2
+ * React Native Unified Auth Provider
3
+ *
4
+ * Creates a platform-specific auth provider that automatically selects the appropriate storage:
5
+ * - Web: Uses LocalStorageTokenStorage from core SDK
6
+ * - Mobile: Uses AsyncStorage-based storage
7
+ */
8
+
9
+ import { Platform } from 'react-native';
10
+ import { DefaultAuthProvider, AuthApi, AuthType, LocalStorageTokenStorage } from '@explorins/pers-sdk/core';
11
+ import { AsyncStorageTokenStorage } from '../storage/async-storage-token-storage';
12
+ import type { TokenStorage } from '@explorins/pers-sdk/core';
13
+
14
+ // Use React Native's built-in platform detection
15
+ const isWebPlatform = Platform.OS === 'web';
3
16
 
4
17
  /**
5
- * Configuration options for React Native Auth Provider
18
+ * Configuration for React Native Auth Provider
6
19
  */
7
20
  export interface ReactNativeAuthConfig {
8
- /** Use secure storage (Keychain) for tokens when available */
9
- useSecureStorage?: boolean;
10
- /** Custom key prefix for storage */
21
+ /** Custom storage key prefix */
11
22
  keyPrefix?: string;
12
23
  /** Enable debug logging */
13
24
  debug?: boolean;
25
+ /** Custom token storage implementation */
26
+ customStorage?: TokenStorage;
27
+ /** Authentication type (default: 'user') */
28
+ authType?: AuthType;
14
29
  }
15
30
 
16
31
  /**
17
- * React Native Authentication Provider
32
+ * Create a React Native auth provider with platform-appropriate storage
33
+ *
34
+ * Automatically selects storage implementation:
35
+ * - Web: Uses LocalStorageTokenStorage (from core SDK)
36
+ * - Mobile: Uses AsyncStorageTokenStorage (React Native specific)
18
37
  *
19
- * Simple, unified implementation for React Native apps with:
20
- * - AsyncStorage for basic token storage
21
- * - Keychain integration for secure storage (when available)
22
- * - Automatic fallback to AsyncStorage if Keychain is unavailable
23
- * - Cross-platform compatibility (iOS/Android/Expo)
38
+ * @param projectKey - PERS project key
39
+ * @param authApi - Optional auth API instance
40
+ * @param config - Configuration options
41
+ * @returns DefaultAuthProvider configured for the current platform
24
42
  */
25
- export class ReactNativeAuthProvider implements PersAuthProvider {
26
- readonly authType: 'user' = 'user';
27
-
28
- private readonly config: Required<ReactNativeAuthConfig>;
29
- private readonly ACCESS_TOKEN_KEY: string;
30
- private readonly REFRESH_TOKEN_KEY: string;
31
- private readonly projectKey: string;
32
-
33
- // In-memory fallback storage
34
- private memoryStorage: Map<string, string> = new Map();
35
-
36
- constructor(
37
- projectKey: string,
38
- config: ReactNativeAuthConfig = {}
39
- ) {
40
- if (!projectKey || typeof projectKey !== 'string') {
41
- throw new Error('ReactNativeAuthProvider: projectKey is required and must be a string');
42
- }
43
-
44
- this.projectKey = projectKey;
45
-
46
- // Set default configuration
47
- this.config = {
48
- useSecureStorage: true, // Default to secure storage when available
49
- keyPrefix: `pers_${projectKey.slice(0, 8)}`,
50
- debug: false,
51
- ...config
52
- };
53
-
54
- // Create storage keys
55
- this.ACCESS_TOKEN_KEY = `${this.config.keyPrefix}_access_token`;
56
- this.REFRESH_TOKEN_KEY = `${this.config.keyPrefix}_refresh_token`;
57
-
58
- if (this.config.debug) {
59
- console.log('ReactNativeAuthProvider initialized:', {
60
- projectKey: projectKey.slice(0, 8) + '...',
61
- useSecureStorage: this.config.useSecureStorage
62
- });
63
- }
64
- }
65
-
66
- async getProjectKey(): Promise<string | null> {
67
- return this.projectKey;
68
- }
69
-
70
- async getToken(): Promise<string | null> {
71
- return this.getStoredValue(this.ACCESS_TOKEN_KEY);
72
- }
73
-
74
- async setAccessToken(token: string): Promise<void> {
75
- await this.storeValue(this.ACCESS_TOKEN_KEY, token);
76
- if (this.config.debug) {
77
- console.log('Access token stored securely');
78
- }
79
- }
80
-
81
- async setRefreshToken(token: string): Promise<void> {
82
- await this.storeValue(this.REFRESH_TOKEN_KEY, token);
83
- if (this.config.debug) {
84
- console.log('Refresh token stored securely');
85
- }
86
- }
87
-
88
- async getRefreshToken(): Promise<string | null> {
89
- return this.getStoredValue(this.REFRESH_TOKEN_KEY);
90
- }
91
-
92
- async clearTokens(): Promise<void> {
93
- await Promise.all([
94
- this.removeStoredValue(this.ACCESS_TOKEN_KEY),
95
- this.removeStoredValue(this.REFRESH_TOKEN_KEY)
96
- ]);
97
-
98
- if (this.config.debug) {
99
- console.log('All tokens cleared from storage');
100
- }
101
- }
102
-
103
- async onTokenExpired(): Promise<void> {
104
- if (this.config.debug) {
105
- console.warn('ReactNativeAuthProvider: Token expired - implement refresh logic in your app');
106
- }
107
- // Clear expired tokens
108
- await this.clearTokens();
109
- }
110
-
111
- // Token validation methods
112
- hasValidToken(): boolean {
113
- return true; // Actual validation happens in getToken()
114
- }
115
-
116
- hasRefreshToken(): boolean {
117
- return true; // Actual validation happens in getRefreshToken()
118
- }
119
-
120
- // Storage implementation methods
121
- private async storeValue(key: string, value: string): Promise<void> {
122
- try {
123
- if (this.config.useSecureStorage && await this.isKeychainAvailable()) {
124
- await this.storeInKeychain(key, value);
125
- } else {
126
- await AsyncStorage.setItem(key, value);
127
- }
128
- } catch (error) {
129
- console.error(`Failed to store value for key ${key}:`, error);
130
- // Fallback to memory storage
131
- this.memoryStorage.set(key, value);
132
- }
133
- }
134
-
135
- private async getStoredValue(key: string): Promise<string | null> {
136
- try {
137
- if (this.config.useSecureStorage && await this.isKeychainAvailable()) {
138
- return await this.getFromKeychain(key);
139
- } else {
140
- return await AsyncStorage.getItem(key);
141
- }
142
- } catch (error) {
143
- console.warn(`Failed to get value for key ${key}:`, error);
144
- // Fallback to memory storage
145
- return this.memoryStorage.get(key) || null;
146
- }
147
- }
148
-
149
- private async removeStoredValue(key: string): Promise<void> {
150
- try {
151
- if (this.config.useSecureStorage && await this.isKeychainAvailable()) {
152
- await this.removeFromKeychain(key);
153
- } else {
154
- await AsyncStorage.removeItem(key);
155
- }
156
- } catch (error) {
157
- console.error(`Failed to remove value for key ${key}:`, error);
158
- }
159
-
160
- // Ensure memory fallback is also cleared
161
- this.memoryStorage.delete(key);
162
- }
163
-
164
- // Keychain helper methods
165
- private async isKeychainAvailable(): Promise<boolean> {
166
- try {
167
- const Keychain = require('react-native-keychain');
168
- return !!Keychain && typeof Keychain.setInternetCredentials === 'function';
169
- } catch {
170
- return false;
171
- }
172
- }
173
-
174
- private async storeInKeychain(key: string, value: string): Promise<void> {
175
- const Keychain = require('react-native-keychain');
176
- await Keychain.setInternetCredentials(key, 'pers-token', value);
177
- }
178
-
179
- private async getFromKeychain(key: string): Promise<string | null> {
180
- const Keychain = require('react-native-keychain');
181
- const credentials = await Keychain.getInternetCredentials(key);
182
- return credentials ? credentials.password : null;
183
- }
43
+ export function createReactNativeAuthProvider(
44
+ projectKey: string,
45
+ config: ReactNativeAuthConfig = {}
46
+ ): DefaultAuthProvider {
47
+ if (!projectKey || typeof projectKey !== 'string') {
48
+ throw new Error('createReactNativeAuthProvider: projectKey is required and must be a string');
49
+ }
50
+
51
+ const {
52
+ keyPrefix = `pers_${projectKey.slice(0, 8)}_`,
53
+ debug = false,
54
+ customStorage,
55
+ authType = 'user'
56
+ } = config;
57
+
58
+ // Platform-specific storage selection
59
+ const tokenStorage = customStorage || (
60
+ isWebPlatform
61
+ ? new LocalStorageTokenStorage()
62
+ : new AsyncStorageTokenStorage(keyPrefix)
63
+ );
64
+
65
+ // Return DefaultAuthProvider configured with platform-appropriate storage
66
+ return new DefaultAuthProvider({
67
+ authType,
68
+ projectKey,
69
+ storage: tokenStorage
70
+ });
71
+ }
184
72
 
185
- private async removeFromKeychain(key: string): Promise<void> {
186
- const Keychain = require('react-native-keychain');
187
- await Keychain.resetInternetCredentials(key);
188
- }
189
- }
@@ -0,0 +1,133 @@
1
+ /**
2
+ * AsyncStorage Token Storage for React Native Mobile Platforms
3
+ *
4
+ * Bundler-agnostic AsyncStorage implementation for mobile platforms
5
+ */
6
+
7
+ import AsyncStorage from '@react-native-async-storage/async-storage';
8
+ import type { TokenStorage } from '@explorins/pers-sdk/core';
9
+
10
+ /**
11
+ * Bundler-agnostic AsyncStorage wrapper
12
+ * Handles different module resolution patterns across bundlers (Metro, Webpack, Rollup, etc.)
13
+ */
14
+ class BundlerAgnosticAsyncStorage {
15
+ private storage: any;
16
+
17
+ constructor(asyncStorageModule: any) {
18
+ // Try different import patterns to handle various bundlers
19
+ if (asyncStorageModule?.default?.getItem) {
20
+ // Metro/Webpack pattern: { default: { getItem, setItem, ... } }
21
+ this.storage = asyncStorageModule.default;
22
+ } else if (asyncStorageModule?.getItem) {
23
+ // Direct export pattern: { getItem, setItem, ... }
24
+ this.storage = asyncStorageModule;
25
+ } else if (typeof asyncStorageModule === 'function') {
26
+ // Function export pattern (some bundlers)
27
+ this.storage = asyncStorageModule();
28
+ } else {
29
+ // Log the structure for debugging
30
+ console.error('[BundlerAgnosticAsyncStorage] Unknown AsyncStorage structure:', asyncStorageModule);
31
+ throw new Error(
32
+ 'AsyncStorage methods not found. Expected structure with getItem/setItem methods. ' +
33
+ 'Make sure @react-native-async-storage/async-storage is properly installed.'
34
+ );
35
+ }
36
+
37
+ // Validate that we have the required methods
38
+ const requiredMethods = ['getItem', 'setItem', 'removeItem', 'getAllKeys', 'multiRemove'];
39
+ const missingMethods = requiredMethods.filter(method => typeof this.storage[method] !== 'function');
40
+
41
+ if (missingMethods.length > 0) {
42
+ throw new Error(
43
+ `AsyncStorage missing required methods: ${missingMethods.join(', ')}. ` +
44
+ 'Ensure @react-native-async-storage/async-storage is properly installed and compatible.'
45
+ );
46
+ }
47
+ }
48
+
49
+ async getItem(key: string): Promise<string | null> {
50
+ return this.storage.getItem(key);
51
+ }
52
+
53
+ async setItem(key: string, value: string): Promise<void> {
54
+ return this.storage.setItem(key, value);
55
+ }
56
+
57
+ async removeItem(key: string): Promise<void> {
58
+ return this.storage.removeItem(key);
59
+ }
60
+
61
+ async getAllKeys(): Promise<readonly string[]> {
62
+ return this.storage.getAllKeys();
63
+ }
64
+
65
+ async multiRemove(keys: string[]): Promise<void> {
66
+ return this.storage.multiRemove(keys);
67
+ }
68
+ }
69
+
70
+ /**
71
+ * AsyncStorage implementation for mobile platforms
72
+ *
73
+ * This class is only used on mobile platforms (iOS/Android).
74
+ * Web platforms use LocalStorageTokenStorage from core SDK.
75
+ */
76
+ export class AsyncStorageTokenStorage implements TokenStorage {
77
+ private keyPrefix: string;
78
+ private asyncStorage: BundlerAgnosticAsyncStorage;
79
+
80
+ constructor(keyPrefix: string = 'pers_tokens_') {
81
+ this.keyPrefix = keyPrefix;
82
+
83
+ try {
84
+ // Initialize bundler-agnostic AsyncStorage wrapper
85
+ this.asyncStorage = new BundlerAgnosticAsyncStorage(AsyncStorage);
86
+ } catch (error) {
87
+ console.error('[AsyncStorageTokenStorage] Failed to initialize:', error);
88
+ throw new Error(
89
+ 'Failed to initialize AsyncStorage. Make sure @react-native-async-storage/async-storage is installed and ' +
90
+ 'this code is running on a React Native mobile platform (not web).'
91
+ );
92
+ }
93
+ }
94
+
95
+ async set(key: string, value: string): Promise<void> {
96
+ try {
97
+ await this.asyncStorage.setItem(`${this.keyPrefix}${key}`, value);
98
+ } catch (error) {
99
+ console.error(`Failed to store token ${key}:`, error);
100
+ throw new Error(`Token storage failed: ${error}`);
101
+ }
102
+ }
103
+
104
+ async get(key: string): Promise<string | null> {
105
+ try {
106
+ return await this.asyncStorage.getItem(`${this.keyPrefix}${key}`);
107
+ } catch (error) {
108
+ console.error(`Failed to retrieve token ${key}:`, error);
109
+ return null;
110
+ }
111
+ }
112
+
113
+ async remove(key: string): Promise<void> {
114
+ try {
115
+ await this.asyncStorage.removeItem(`${this.keyPrefix}${key}`);
116
+ } catch (error) {
117
+ console.error(`Failed to remove token ${key}:`, error);
118
+ }
119
+ }
120
+
121
+ async clear(): Promise<void> {
122
+ try {
123
+ const allKeys = await this.asyncStorage.getAllKeys();
124
+ const ourKeys = allKeys.filter(key => key.startsWith(this.keyPrefix));
125
+
126
+ if (ourKeys.length > 0) {
127
+ await this.asyncStorage.multiRemove(ourKeys);
128
+ }
129
+ } catch (error) {
130
+ console.error('Failed to clear token storage:', error);
131
+ }
132
+ }
133
+ }