@explorins/pers-sdk-react-native 1.3.2

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 (63) hide show
  1. package/README.md +200 -0
  2. package/dist/hooks/index.d.ts +8 -0
  3. package/dist/hooks/index.d.ts.map +1 -0
  4. package/dist/hooks/index.js +8 -0
  5. package/dist/hooks/useAuth.d.ts +12 -0
  6. package/dist/hooks/useAuth.d.ts.map +1 -0
  7. package/dist/hooks/useAuth.js +16 -0
  8. package/dist/hooks/useBusiness.d.ts +7 -0
  9. package/dist/hooks/useBusiness.d.ts.map +1 -0
  10. package/dist/hooks/useBusiness.js +64 -0
  11. package/dist/hooks/useCampaigns.d.ts +8 -0
  12. package/dist/hooks/useCampaigns.d.ts.map +1 -0
  13. package/dist/hooks/useCampaigns.js +90 -0
  14. package/dist/hooks/useRedemptions.d.ts +9 -0
  15. package/dist/hooks/useRedemptions.d.ts.map +1 -0
  16. package/dist/hooks/useRedemptions.js +121 -0
  17. package/dist/hooks/useTokens.d.ts +6 -0
  18. package/dist/hooks/useTokens.d.ts.map +1 -0
  19. package/dist/hooks/useTokens.js +48 -0
  20. package/dist/hooks/useTransactions.d.ts +7 -0
  21. package/dist/hooks/useTransactions.d.ts.map +1 -0
  22. package/dist/hooks/useTransactions.js +78 -0
  23. package/dist/hooks/useWeb3.d.ts +11 -0
  24. package/dist/hooks/useWeb3.d.ts.map +1 -0
  25. package/dist/hooks/useWeb3.js +63 -0
  26. package/dist/index.d.ts +7 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.esm.js +1057 -0
  29. package/dist/index.esm.js.map +1 -0
  30. package/dist/index.js +1077 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/polyfills/index.d.ts +3 -0
  33. package/dist/polyfills/index.d.ts.map +1 -0
  34. package/dist/polyfills/index.js +13 -0
  35. package/dist/polyfills/index.simple.d.ts +2 -0
  36. package/dist/polyfills/index.simple.d.ts.map +1 -0
  37. package/dist/polyfills/index.simple.js +17 -0
  38. package/dist/providers/PersSDKProvider.d.ts +29 -0
  39. package/dist/providers/PersSDKProvider.d.ts.map +1 -0
  40. package/dist/providers/PersSDKProvider.js +194 -0
  41. package/dist/providers/react-native-auth-provider.d.ts +92 -0
  42. package/dist/providers/react-native-auth-provider.d.ts.map +1 -0
  43. package/dist/providers/react-native-auth-provider.js +268 -0
  44. package/dist/providers/react-native-http-client.d.ts +29 -0
  45. package/dist/providers/react-native-http-client.d.ts.map +1 -0
  46. package/dist/providers/react-native-http-client.js +94 -0
  47. package/package.json +157 -0
  48. package/src/hooks/index.ts +8 -0
  49. package/src/hooks/useAuth.ts +43 -0
  50. package/src/hooks/useBusiness.ts +69 -0
  51. package/src/hooks/useCampaigns.ts +96 -0
  52. package/src/hooks/useRedemptions.ts +129 -0
  53. package/src/hooks/useTokens.ts +53 -0
  54. package/src/hooks/useTransactions.ts +85 -0
  55. package/src/hooks/useWeb3.ts +70 -0
  56. package/src/index.ts +51 -0
  57. package/src/polyfills/index.simple.ts +22 -0
  58. package/src/polyfills/index.ts +16 -0
  59. package/src/providers/PersSDKProvider.tsx +274 -0
  60. package/src/providers/react-native-auth-provider.ts +332 -0
  61. package/src/providers/react-native-http-client.ts +129 -0
  62. package/src/types/external-modules.d.ts +13 -0
  63. package/src/types/react-native-globals.d.ts +46 -0
@@ -0,0 +1,332 @@
1
+ import type { PersAuthProvider } from '@explorins/pers-sdk/core';
2
+ import AsyncStorage from '@react-native-async-storage/async-storage';
3
+
4
+ /**
5
+ * Storage strategy for different types of data
6
+ */
7
+ export enum StorageStrategy {
8
+ /** Use AsyncStorage for general app data */
9
+ ASYNC_STORAGE = 'async_storage',
10
+ /** Use Keychain for sensitive tokens (when available) */
11
+ KEYCHAIN = 'keychain',
12
+ /** Use in-memory storage (fallback) */
13
+ MEMORY = 'memory'
14
+ }
15
+
16
+ /**
17
+ * Configuration options for React Native Auth Provider
18
+ */
19
+ export interface ReactNativeAuthConfig {
20
+ /** Storage strategy for access tokens */
21
+ accessTokenStrategy?: StorageStrategy;
22
+ /** Storage strategy for refresh tokens */
23
+ refreshTokenStrategy?: StorageStrategy;
24
+ /** Custom key prefix for storage */
25
+ keyPrefix?: string;
26
+ /** Enable debug logging */
27
+ debug?: boolean;
28
+ }
29
+
30
+ /**
31
+ * Base React Native Authentication Provider
32
+ *
33
+ * This serves as a blueprint for native implementations and can be extended
34
+ * by consuming applications to integrate with their specific auth systems.
35
+ *
36
+ * Features:
37
+ * - Multiple storage strategies (AsyncStorage, Keychain, Memory)
38
+ * - Configurable security levels per token type
39
+ * - Cross-platform compatibility (iOS/Android/Web)
40
+ * - Blueprint for native iOS/Android implementations
41
+ */
42
+ export abstract class BaseReactNativeAuthProvider implements PersAuthProvider {
43
+ abstract readonly authType: 'admin' | 'user';
44
+ abstract getProjectKey(): Promise<string | null>;
45
+ abstract onTokenExpired(): Promise<void>;
46
+
47
+ protected readonly config: Required<ReactNativeAuthConfig>;
48
+ protected readonly ACCESS_TOKEN_KEY: string;
49
+ protected readonly REFRESH_TOKEN_KEY: string;
50
+ protected readonly PROJECT_KEY_KEY: string;
51
+
52
+ // In-memory fallback storage
53
+ private memoryStorage: Map<string, string> = new Map();
54
+
55
+ constructor(
56
+ projectKey: string,
57
+ config: ReactNativeAuthConfig = {}
58
+ ) {
59
+ // Validate projectKey
60
+ if (!projectKey || typeof projectKey !== 'string') {
61
+ throw new Error('ReactNativeAuthProvider: projectKey is required and must be a string');
62
+ }
63
+
64
+ // Set default configuration
65
+ this.config = {
66
+ accessTokenStrategy: StorageStrategy.ASYNC_STORAGE,
67
+ refreshTokenStrategy: StorageStrategy.KEYCHAIN,
68
+ keyPrefix: `pers_${projectKey.slice(0, 8)}`,
69
+ debug: false,
70
+ ...config
71
+ };
72
+
73
+ // Create storage keys
74
+ this.ACCESS_TOKEN_KEY = `${this.config.keyPrefix}_access_token`;
75
+ this.REFRESH_TOKEN_KEY = `${this.config.keyPrefix}_refresh_token`;
76
+ this.PROJECT_KEY_KEY = `${this.config.keyPrefix}_project_key`;
77
+
78
+ if (this.config.debug) {
79
+ console.log('🔧 ReactNativeAuthProvider initialized with config:', this.config);
80
+ }
81
+ }
82
+
83
+ // Core token management methods
84
+ async getToken(): Promise<string | null> {
85
+ return this.getStoredValue(this.ACCESS_TOKEN_KEY, this.config.accessTokenStrategy);
86
+ }
87
+
88
+ async setAccessToken(token: string): Promise<void> {
89
+ await this.storeValue(this.ACCESS_TOKEN_KEY, token, this.config.accessTokenStrategy);
90
+ if (this.config.debug) {
91
+ console.log('✅ Access token stored securely');
92
+ }
93
+ }
94
+
95
+ async setRefreshToken(token: string): Promise<void> {
96
+ await this.storeValue(this.REFRESH_TOKEN_KEY, token, this.config.refreshTokenStrategy);
97
+ if (this.config.debug) {
98
+ console.log('✅ Refresh token stored securely');
99
+ }
100
+ }
101
+
102
+ async getRefreshToken(): Promise<string | null> {
103
+ return this.getStoredValue(this.REFRESH_TOKEN_KEY, this.config.refreshTokenStrategy);
104
+ }
105
+
106
+ async clearTokens(): Promise<void> {
107
+ await Promise.all([
108
+ this.removeStoredValue(this.ACCESS_TOKEN_KEY, this.config.accessTokenStrategy),
109
+ this.removeStoredValue(this.REFRESH_TOKEN_KEY, this.config.refreshTokenStrategy)
110
+ ]);
111
+
112
+ if (this.config.debug) {
113
+ console.log('✅ All tokens cleared from storage');
114
+ }
115
+ }
116
+
117
+ // Token validation methods
118
+ hasValidToken(): boolean {
119
+ // Synchronous check - for async check use hasValidTokenAsync()
120
+ return true; // Actual validation happens in getToken()
121
+ }
122
+
123
+ hasRefreshToken(): boolean {
124
+ // Synchronous check - for async check use hasRefreshTokenAsync()
125
+ return true; // Actual validation happens in getRefreshToken()
126
+ }
127
+
128
+ // Async token validation methods
129
+ async hasValidTokenAsync(): Promise<boolean> {
130
+ const token = await this.getToken();
131
+ return !!token;
132
+ }
133
+
134
+ async hasRefreshTokenAsync(): Promise<boolean> {
135
+ const refreshToken = await this.getRefreshToken();
136
+ return !!refreshToken;
137
+ }
138
+
139
+ // Legacy methods for backward compatibility
140
+ setToken(token: string): void {
141
+ this.setAccessToken(token).catch(error => {
142
+ console.error('Legacy setToken failed:', error);
143
+ });
144
+ }
145
+
146
+ clearToken(): void {
147
+ this.clearTokens().catch(error => {
148
+ console.error('Legacy clearToken failed:', error);
149
+ });
150
+ }
151
+
152
+ // Protected storage implementation methods
153
+ protected async storeValue(key: string, value: string, strategy: StorageStrategy): Promise<void> {
154
+ try {
155
+ switch (strategy) {
156
+ case StorageStrategy.KEYCHAIN:
157
+ // Try Keychain first, fallback to AsyncStorage
158
+ if (await this.isKeychainAvailable()) {
159
+ await this.storeInKeychain(key, value);
160
+ } else {
161
+ await AsyncStorage.setItem(key, value);
162
+ }
163
+ break;
164
+
165
+ case StorageStrategy.ASYNC_STORAGE:
166
+ await AsyncStorage.setItem(key, value);
167
+ break;
168
+
169
+ case StorageStrategy.MEMORY:
170
+ default:
171
+ this.memoryStorage.set(key, value);
172
+ break;
173
+ }
174
+ } catch (error) {
175
+ console.error(`Failed to store value with strategy ${strategy}:`, error);
176
+ // Fallback to memory storage
177
+ this.memoryStorage.set(key, value);
178
+ }
179
+ }
180
+
181
+ protected async getStoredValue(key: string, strategy: StorageStrategy): Promise<string | null> {
182
+ try {
183
+ switch (strategy) {
184
+ case StorageStrategy.KEYCHAIN:
185
+ // Try Keychain first, fallback to AsyncStorage
186
+ if (await this.isKeychainAvailable()) {
187
+ return await this.getFromKeychain(key);
188
+ } else {
189
+ return await AsyncStorage.getItem(key);
190
+ }
191
+
192
+ case StorageStrategy.ASYNC_STORAGE:
193
+ return await AsyncStorage.getItem(key);
194
+
195
+ case StorageStrategy.MEMORY:
196
+ default:
197
+ return this.memoryStorage.get(key) || null;
198
+ }
199
+ } catch (error) {
200
+ console.warn(`Failed to get value with strategy ${strategy}:`, error);
201
+ // Fallback to memory storage
202
+ return this.memoryStorage.get(key) || null;
203
+ }
204
+ }
205
+
206
+ protected async removeStoredValue(key: string, strategy: StorageStrategy): Promise<void> {
207
+ try {
208
+ switch (strategy) {
209
+ case StorageStrategy.KEYCHAIN:
210
+ // Try Keychain first, fallback to AsyncStorage
211
+ if (await this.isKeychainAvailable()) {
212
+ await this.removeFromKeychain(key);
213
+ } else {
214
+ await AsyncStorage.removeItem(key);
215
+ }
216
+ break;
217
+
218
+ case StorageStrategy.ASYNC_STORAGE:
219
+ await AsyncStorage.removeItem(key);
220
+ break;
221
+
222
+ case StorageStrategy.MEMORY:
223
+ default:
224
+ this.memoryStorage.delete(key);
225
+ break;
226
+ }
227
+ } catch (error) {
228
+ console.error(`Failed to remove value with strategy ${strategy}:`, error);
229
+ // Ensure memory fallback is also cleared
230
+ this.memoryStorage.delete(key);
231
+ }
232
+ }
233
+
234
+ // Keychain helper methods
235
+ private async isKeychainAvailable(): Promise<boolean> {
236
+ try {
237
+ // Check if react-native-keychain is available
238
+ const Keychain = require('react-native-keychain');
239
+ return !!Keychain && typeof Keychain.setInternetCredentials === 'function';
240
+ } catch {
241
+ return false;
242
+ }
243
+ }
244
+
245
+ private async storeInKeychain(key: string, value: string): Promise<void> {
246
+ const Keychain = require('react-native-keychain');
247
+ await Keychain.setInternetCredentials(key, 'pers-token', value);
248
+ }
249
+
250
+ private async getFromKeychain(key: string): Promise<string | null> {
251
+ const Keychain = require('react-native-keychain');
252
+ const credentials = await Keychain.getInternetCredentials(key);
253
+ return credentials ? credentials.password : null;
254
+ }
255
+
256
+ private async removeFromKeychain(key: string): Promise<void> {
257
+ const Keychain = require('react-native-keychain');
258
+ await Keychain.resetInternetCredentials(key);
259
+ }
260
+ }
261
+
262
+ /**
263
+ * React Native Authentication Provider
264
+ *
265
+ * A ready-to-use implementation for basic use cases.
266
+ * Uses AsyncStorage for access tokens and Keychain for refresh tokens (when available).
267
+ */
268
+ export class ReactNativeAuthProvider extends BaseReactNativeAuthProvider {
269
+ readonly authType: 'user' = 'user';
270
+
271
+ constructor(
272
+ private projectKey: string,
273
+ config: ReactNativeAuthConfig = {}
274
+ ) {
275
+ // Validate projectKey
276
+ if (!projectKey || typeof projectKey !== 'string') {
277
+ throw new Error('ReactNativeAuthProvider: projectKey is required and must be a string');
278
+ }
279
+
280
+ super(projectKey, {
281
+ accessTokenStrategy: StorageStrategy.ASYNC_STORAGE,
282
+ refreshTokenStrategy: StorageStrategy.KEYCHAIN,
283
+ debug: false,
284
+ ...config
285
+ });
286
+ }
287
+
288
+ async getProjectKey(): Promise<string | null> {
289
+ return this.projectKey;
290
+ }
291
+
292
+ async onTokenExpired(): Promise<void> {
293
+ if (this.config.debug) {
294
+ console.warn('ReactNativeAuthProvider: Token expired - no refresh logic implemented');
295
+ }
296
+ }
297
+ }
298
+
299
+ /**
300
+ * Secure React Native Authentication Provider
301
+ *
302
+ * Maximum security implementation using Keychain for all sensitive data.
303
+ */
304
+ export class SecureReactNativeAuthProvider extends BaseReactNativeAuthProvider {
305
+ readonly authType: 'user' = 'user';
306
+
307
+ constructor(
308
+ private projectKey: string,
309
+ config: Omit<ReactNativeAuthConfig, 'accessTokenStrategy' | 'refreshTokenStrategy'> = {}
310
+ ) {
311
+ // Validate projectKey
312
+ if (!projectKey || typeof projectKey !== 'string') {
313
+ throw new Error('SecureReactNativeAuthProvider: projectKey is required and must be a string');
314
+ }
315
+
316
+ super(projectKey, {
317
+ ...config,
318
+ accessTokenStrategy: StorageStrategy.KEYCHAIN,
319
+ refreshTokenStrategy: StorageStrategy.KEYCHAIN,
320
+ });
321
+ }
322
+
323
+ async getProjectKey(): Promise<string | null> {
324
+ return this.projectKey;
325
+ }
326
+
327
+ async onTokenExpired(): Promise<void> {
328
+ if (this.config.debug) {
329
+ console.warn('SecureReactNativeAuthProvider: Token expired - implement refresh logic');
330
+ }
331
+ }
332
+ }
@@ -0,0 +1,129 @@
1
+ /**
2
+ * HTTP Client implementation for React Native
3
+ *
4
+ * This implementation properly implements the core SDK's HttpClient interface
5
+ * and uses fetch API which is available in React Native.
6
+ */
7
+
8
+ export interface RequestOptions {
9
+ headers?: Record<string, string>;
10
+ params?: Record<string, string>;
11
+ timeout?: number;
12
+ responseType?: 'json' | 'blob' | 'text' | 'arraybuffer';
13
+ }
14
+
15
+ export interface HttpClient {
16
+ get<T>(url: string, options?: RequestOptions): Promise<T>;
17
+ post<T>(url: string, body: any, options?: RequestOptions): Promise<T>;
18
+ put<T>(url: string, body: any, options?: RequestOptions): Promise<T>;
19
+ delete<T>(url: string, options?: RequestOptions): Promise<T>;
20
+ }
21
+
22
+ export class ReactNativeHttpClient implements HttpClient {
23
+ private baseURL?: string;
24
+
25
+ constructor(baseURL?: string) {
26
+ this.baseURL = baseURL;
27
+ }
28
+
29
+ async get<T = any>(url: string, options?: RequestOptions): Promise<T> {
30
+ return this.request<T>('GET', url, undefined, options);
31
+ }
32
+
33
+ async post<T = any>(url: string, body?: any, options?: RequestOptions): Promise<T> {
34
+ return this.request<T>('POST', url, body, options);
35
+ }
36
+
37
+ async put<T = any>(url: string, body?: any, options?: RequestOptions): Promise<T> {
38
+ return this.request<T>('PUT', url, body, options);
39
+ }
40
+
41
+ async delete<T = any>(url: string, options?: RequestOptions): Promise<T> {
42
+ return this.request<T>('DELETE', url, undefined, options);
43
+ }
44
+
45
+ private async request<T>(
46
+ method: string,
47
+ url: string,
48
+ body?: any,
49
+ options?: RequestOptions
50
+ ): Promise<T> {
51
+ const fullUrl = this.buildUrl(url, options?.params);
52
+
53
+ const config: RequestInit = {
54
+ method,
55
+ headers: {
56
+ 'Content-Type': 'application/json',
57
+ ...options?.headers,
58
+ },
59
+ };
60
+
61
+ // Add timeout if specified
62
+ if (options?.timeout) {
63
+ const controller = new AbortController();
64
+ config.signal = controller.signal;
65
+ setTimeout(() => controller.abort(), options.timeout);
66
+ }
67
+
68
+ // Add body for POST/PUT requests
69
+ if (body && (method === 'POST' || method === 'PUT')) {
70
+ config.body = JSON.stringify(body);
71
+ }
72
+
73
+ try {
74
+ const response = await fetch(fullUrl, config);
75
+
76
+ if (!response.ok) {
77
+ // Try to get error details from response
78
+ let errorMessage = `HTTP ${response.status}: ${response.statusText}`;
79
+ try {
80
+ const errorBody = await response.text();
81
+ if (errorBody) {
82
+ errorMessage += ` - ${errorBody}`;
83
+ }
84
+ } catch (e) {
85
+ // Ignore errors when reading error body
86
+ }
87
+ throw new Error(errorMessage);
88
+ }
89
+
90
+ // Handle different response types
91
+ switch (options?.responseType) {
92
+ case 'text':
93
+ return response.text() as Promise<T>;
94
+ case 'blob':
95
+ return response.blob() as Promise<T>;
96
+ case 'arraybuffer':
97
+ return response.arrayBuffer() as Promise<T>;
98
+ case 'json':
99
+ default:
100
+ // Check if response has content
101
+ const contentLength = response.headers.get('content-length');
102
+ if (contentLength === '0' || response.status === 204) {
103
+ return undefined as T;
104
+ }
105
+ return response.json();
106
+ }
107
+ } catch (error) {
108
+ if (error instanceof Error && error.name === 'AbortError') {
109
+ throw new Error(`Request timeout after ${options?.timeout}ms`);
110
+ }
111
+ throw error;
112
+ }
113
+ }
114
+
115
+ private buildUrl(url: string, params?: Record<string, string>): string {
116
+ const fullUrl = this.baseURL ? `${this.baseURL}${url}` : url;
117
+
118
+ if (!params || Object.keys(params).length === 0) {
119
+ return fullUrl;
120
+ }
121
+
122
+ const urlObj = new URL(fullUrl);
123
+ Object.entries(params).forEach(([key, value]) => {
124
+ urlObj.searchParams.append(key, value);
125
+ });
126
+
127
+ return urlObj.toString();
128
+ }
129
+ }
@@ -0,0 +1,13 @@
1
+ // Type declarations for externalized dependencies
2
+ declare module '@explorins/pers-shared' {
3
+ // Re-export all types from pers-shared
4
+ export * from '@explorins/pers-shared/dist/types';
5
+ }
6
+
7
+ declare module 'ethers/providers' {
8
+ export * from 'ethers/lib/providers';
9
+ }
10
+
11
+ declare module 'ethers/utils' {
12
+ export * from 'ethers/lib/utils';
13
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * React Native Global Type Declarations
3
+ *
4
+ * This file declares global types that are available in React Native
5
+ * but might not be available in TypeScript compilation.
6
+ */
7
+
8
+ // React Native global type declarations
9
+ declare global {
10
+ interface Navigator {
11
+ product?: string;
12
+ }
13
+
14
+ var navigator: Navigator | undefined;
15
+ var localStorage: Storage | undefined;
16
+
17
+ interface Storage {
18
+ getItem(key: string): string | null;
19
+ setItem(key: string, value: string): void;
20
+ removeItem(key: string): void;
21
+ }
22
+
23
+ // Global object - React Native environment
24
+ var global: typeof globalThis & {
25
+ global: typeof globalThis;
26
+ Buffer: typeof Buffer;
27
+ process: NodeJS.Process;
28
+ };
29
+
30
+ // Process object for Node.js compatibility
31
+ namespace NodeJS {
32
+ interface Process {
33
+ env: Record<string, string | undefined>;
34
+ }
35
+ }
36
+
37
+ var process: NodeJS.Process;
38
+
39
+ // Buffer for Node.js compatibility
40
+ var Buffer: {
41
+ from(data: any, encoding?: string): any;
42
+ alloc(size: number): any;
43
+ };
44
+ }
45
+
46
+ export {};