@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.
- package/README.md +200 -0
- package/dist/hooks/index.d.ts +8 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +8 -0
- package/dist/hooks/useAuth.d.ts +12 -0
- package/dist/hooks/useAuth.d.ts.map +1 -0
- package/dist/hooks/useAuth.js +16 -0
- package/dist/hooks/useBusiness.d.ts +7 -0
- package/dist/hooks/useBusiness.d.ts.map +1 -0
- package/dist/hooks/useBusiness.js +64 -0
- package/dist/hooks/useCampaigns.d.ts +8 -0
- package/dist/hooks/useCampaigns.d.ts.map +1 -0
- package/dist/hooks/useCampaigns.js +90 -0
- package/dist/hooks/useRedemptions.d.ts +9 -0
- package/dist/hooks/useRedemptions.d.ts.map +1 -0
- package/dist/hooks/useRedemptions.js +121 -0
- package/dist/hooks/useTokens.d.ts +6 -0
- package/dist/hooks/useTokens.d.ts.map +1 -0
- package/dist/hooks/useTokens.js +48 -0
- package/dist/hooks/useTransactions.d.ts +7 -0
- package/dist/hooks/useTransactions.d.ts.map +1 -0
- package/dist/hooks/useTransactions.js +78 -0
- package/dist/hooks/useWeb3.d.ts +11 -0
- package/dist/hooks/useWeb3.d.ts.map +1 -0
- package/dist/hooks/useWeb3.js +63 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.esm.js +1057 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +1077 -0
- package/dist/index.js.map +1 -0
- package/dist/polyfills/index.d.ts +3 -0
- package/dist/polyfills/index.d.ts.map +1 -0
- package/dist/polyfills/index.js +13 -0
- package/dist/polyfills/index.simple.d.ts +2 -0
- package/dist/polyfills/index.simple.d.ts.map +1 -0
- package/dist/polyfills/index.simple.js +17 -0
- package/dist/providers/PersSDKProvider.d.ts +29 -0
- package/dist/providers/PersSDKProvider.d.ts.map +1 -0
- package/dist/providers/PersSDKProvider.js +194 -0
- package/dist/providers/react-native-auth-provider.d.ts +92 -0
- package/dist/providers/react-native-auth-provider.d.ts.map +1 -0
- package/dist/providers/react-native-auth-provider.js +268 -0
- package/dist/providers/react-native-http-client.d.ts +29 -0
- package/dist/providers/react-native-http-client.d.ts.map +1 -0
- package/dist/providers/react-native-http-client.js +94 -0
- package/package.json +157 -0
- package/src/hooks/index.ts +8 -0
- package/src/hooks/useAuth.ts +43 -0
- package/src/hooks/useBusiness.ts +69 -0
- package/src/hooks/useCampaigns.ts +96 -0
- package/src/hooks/useRedemptions.ts +129 -0
- package/src/hooks/useTokens.ts +53 -0
- package/src/hooks/useTransactions.ts +85 -0
- package/src/hooks/useWeb3.ts +70 -0
- package/src/index.ts +51 -0
- package/src/polyfills/index.simple.ts +22 -0
- package/src/polyfills/index.ts +16 -0
- package/src/providers/PersSDKProvider.tsx +274 -0
- package/src/providers/react-native-auth-provider.ts +332 -0
- package/src/providers/react-native-http-client.ts +129 -0
- package/src/types/external-modules.d.ts +13 -0
- 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 {};
|