@explorins/pers-sdk-react-native 2.3.6 → 2.3.8

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 (62) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1266 -1244
  3. package/dist/__tests__/PersSDKProvider.test.js +33 -0
  4. package/dist/hooks/useBusiness.d.ts +2 -2
  5. package/dist/hooks/useBusiness.d.ts.map +1 -1
  6. package/dist/hooks/useBusiness.js +2 -2
  7. package/dist/hooks/useIPFSUrl.d.ts +3 -3
  8. package/dist/hooks/useIPFSUrl.d.ts.map +1 -1
  9. package/dist/hooks/useIPFSUrl.js +7 -7
  10. package/dist/hooks/useTransactions.d.ts.map +1 -1
  11. package/dist/hooks/useTransactions.js +13 -5
  12. package/dist/hooks/useWeb3.js +4 -4
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +2412 -492
  16. package/dist/index.js.map +1 -1
  17. package/dist/providers/PersSDKProvider.d.ts +4 -0
  18. package/dist/providers/PersSDKProvider.d.ts.map +1 -1
  19. package/dist/providers/PersSDKProvider.js +14 -0
  20. package/dist/providers/react-native-http-client.d.ts +2 -0
  21. package/dist/providers/react-native-http-client.d.ts.map +1 -1
  22. package/dist/providers/react-native-http-client.js +5 -2
  23. package/dist/storage/rn-secure-storage.d.ts.map +1 -1
  24. package/dist/storage/rn-secure-storage.js +21 -8
  25. package/package.json +3 -3
  26. package/src/__tests__/PersSDKProvider.test.tsx +484 -420
  27. package/src/__tests__/setup.ts +40 -40
  28. package/src/hooks/index.ts +60 -60
  29. package/src/hooks/useAnalytics.ts +327 -327
  30. package/src/hooks/useAuth.ts +265 -265
  31. package/src/hooks/useBookings.ts +191 -191
  32. package/src/hooks/useBusiness.ts +251 -251
  33. package/src/hooks/useCampaigns.ts +500 -500
  34. package/src/hooks/useDonations.ts +66 -66
  35. package/src/hooks/useEvents.ts +372 -372
  36. package/src/hooks/useFiles.ts +155 -155
  37. package/src/hooks/useIPFSUrl.ts +192 -192
  38. package/src/hooks/usePurchases.ts +65 -65
  39. package/src/hooks/useRedemptions.ts +467 -467
  40. package/src/hooks/useTenants.ts +72 -72
  41. package/src/hooks/useTokenBalances.ts +385 -385
  42. package/src/hooks/useTokens.ts +313 -313
  43. package/src/hooks/useTransactionSigner.ts +611 -611
  44. package/src/hooks/useTransactions.ts +299 -290
  45. package/src/hooks/useTriggerSources.ts +301 -301
  46. package/src/hooks/useUserStatus.ts +61 -61
  47. package/src/hooks/useUsers.ts +167 -167
  48. package/src/hooks/useWeb3.ts +355 -355
  49. package/src/index.ts +777 -774
  50. package/src/polyfills/index.ts +160 -160
  51. package/src/polyfills/web3-polyfills.ts +97 -97
  52. package/src/providers/PersSDKProvider.tsx +393 -374
  53. package/src/providers/react-native-auth-provider.ts +110 -110
  54. package/src/providers/react-native-http-client.ts +150 -145
  55. package/src/providers/rn-dpop-provider.ts +190 -190
  56. package/src/storage/async-storage-token-storage.ts +185 -185
  57. package/src/storage/rn-secure-storage.ts +182 -170
  58. package/src/types/react-native-globals.d.ts +45 -45
  59. package/src/utils/index.ts +8 -8
  60. package/src/utils/platform-detection.ts +86 -86
  61. package/dist/index-CzLxqePZ.js +0 -7717
  62. package/dist/index-CzLxqePZ.js.map +0 -1
@@ -1,170 +1,182 @@
1
- import { Platform } from 'react-native';
2
- import {
3
- TokenStorage,
4
- DPOP_STORAGE_KEYS,
5
- AUTH_STORAGE_KEYS
6
- } from '@explorins/pers-sdk/core';
7
- import { AsyncStorageTokenStorage } from './async-storage-token-storage';
8
-
9
- // Conditionally require Keychain to avoid Web bundler errors
10
- let Keychain: any;
11
- if (Platform.OS !== 'web') {
12
- try {
13
- Keychain = require('react-native-keychain');
14
- } catch (e) {
15
- console.warn('ReactNativeSecureStorage: Failed to load react-native-keychain', e);
16
- }
17
- }
18
-
19
- /**
20
- * Secure Storage implementation for React Native
21
- * Uses Keychain/Keystore for sensitive data and AsyncStorage for non-sensitive data.
22
- */
23
- export class ReactNativeSecureStorage implements TokenStorage {
24
- // Set to false because Keychain stores strings, so we need extractable (serializable) keys
25
- readonly supportsObjects = false;
26
-
27
- // Define which keys MUST go to secure hardware storage
28
- private readonly SECURE_KEYS = new Set([
29
- DPOP_STORAGE_KEYS.PRIVATE,
30
- AUTH_STORAGE_KEYS.ACCESS_TOKEN,
31
- AUTH_STORAGE_KEYS.REFRESH_TOKEN,
32
- AUTH_STORAGE_KEYS.PROVIDER_TOKEN
33
- ]);
34
-
35
- private fallbackStorage: AsyncStorageTokenStorage;
36
-
37
- constructor(private keyPrefix: string = 'pers_tokens_') {
38
- this.fallbackStorage = new AsyncStorageTokenStorage(keyPrefix);
39
- }
40
-
41
- async set(key: string, value: unknown): Promise<void> {
42
- const prefixedKey = this.getKeyName(key);
43
- const stringValue = typeof value === 'string' ? value : JSON.stringify(value);
44
-
45
- // Cast key to any to bypass strict literal type checking of the Set.has method
46
- // In runtime, 'key' is just a string, so this is safe.
47
- if (this.SECURE_KEYS.has(key as any)) {
48
- // Store in Keychain/Keystore
49
- try {
50
- if (Keychain) {
51
- // Service name acts as the key identifier in simple usage
52
- await Keychain.setGenericPassword(prefixedKey, stringValue, { service: prefixedKey });
53
- } else {
54
- throw new Error('RN Keychain not available');
55
- }
56
- } catch (e) {
57
- console.warn(`[ReactNativeSecureStorage] Keychain set failed for ${key}, falling back to AsyncStorage`, e);
58
- // Fallback to AsyncStorage if Keychain fails
59
- await this.fallbackStorage.set(key, stringValue);
60
- }
61
- } else {
62
- // Store standard config/metadata in AsyncStorage
63
- await this.fallbackStorage.set(key, stringValue);
64
- }
65
- }
66
-
67
- async get(key: string): Promise<unknown | null> {
68
- const prefixedKey = this.getKeyName(key);
69
-
70
- if (this.SECURE_KEYS.has(key as any)) {
71
- try {
72
- if (Keychain) {
73
- const credentials = await Keychain.getGenericPassword({ service: prefixedKey });
74
- if (credentials) {
75
- return this.tryParse(credentials.password);
76
- }
77
- }
78
- } catch (e) {
79
- console.warn(`[ReactNativeSecureStorage] Failed to access keychain for ${key}`, e);
80
- // Continue to fallback check...
81
- }
82
-
83
- // Fallback: Check AsyncStorage if not found in Keychain or Keychain failed
84
- try {
85
- const val = await this.fallbackStorage.get(key);
86
- return val ? this.tryParse(val) : null;
87
- } catch (e) {
88
- return null;
89
- }
90
- } else {
91
- try {
92
- const val = await this.fallbackStorage.get(key);
93
- return val ? this.tryParse(val) : null;
94
- } catch (e) {
95
- console.warn(`[ReactNativeSecureStorage] Failed to access AsyncStorage for ${key}`, e);
96
- return null;
97
- }
98
- }
99
- }
100
-
101
- async remove(key: string): Promise<void> {
102
- const prefixedKey = this.getKeyName(key);
103
-
104
- if (this.SECURE_KEYS.has(key as any)) {
105
- try {
106
- if (Keychain) {
107
- await Keychain.resetGenericPassword({ service: prefixedKey });
108
- }
109
- } catch (e) {
110
- console.warn(`[ReactNativeSecureStorage] Failed to reset keychain for ${key}`, e);
111
- }
112
- // Always remove from fallback storage too, just in case
113
- await this.fallbackStorage.remove(key);
114
- } else {
115
- await this.fallbackStorage.remove(key);
116
- }
117
- }
118
-
119
- async clear(): Promise<void> {
120
- const clearPromises: Promise<void>[] = [];
121
-
122
- // Clear all known secure keys with retry logic
123
- for (const key of this.SECURE_KEYS) {
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
- );
143
- }
144
-
145
- // Wait for all Keychain clearing to complete (or fail)
146
- await Promise.allSettled(clearPromises);
147
-
148
- // Always clear AsyncStorage fallback
149
- try {
150
- await this.fallbackStorage.clear();
151
- } catch (e) {
152
- console.error('[ReactNativeSecureStorage] Failed to clear AsyncStorage:', e);
153
- }
154
- }
155
-
156
- private getKeyName(key: string): string {
157
- // For Keychain, we might want to avoid prefixes if we want to share across apps,
158
- // but for simple isolation, prefix is fine to avoid collisions if multiple instances exist.
159
- // However, Keychain services are global to the app bundle ID usually.
160
- return `${this.keyPrefix}${key}`;
161
- }
162
-
163
- private tryParse(val: string): unknown {
164
- try {
165
- return JSON.parse(val);
166
- } catch {
167
- return val;
168
- }
169
- }
170
- }
1
+ import { Platform } from 'react-native';
2
+ import {
3
+ TokenStorage,
4
+ DPOP_STORAGE_KEYS,
5
+ AUTH_STORAGE_KEYS
6
+ } from '@explorins/pers-sdk/core';
7
+ import { AsyncStorageTokenStorage } from './async-storage-token-storage';
8
+
9
+ // Conditionally require Keychain to avoid Web bundler errors
10
+ let Keychain: any;
11
+ if (Platform.OS !== 'web') {
12
+ try {
13
+ Keychain = require('react-native-keychain');
14
+ } catch (e) {
15
+ console.warn('ReactNativeSecureStorage: Failed to load react-native-keychain', e);
16
+ }
17
+ }
18
+
19
+ /**
20
+ * Secure Storage implementation for React Native
21
+ * Uses Keychain/Keystore for sensitive data and AsyncStorage for non-sensitive data.
22
+ */
23
+ export class ReactNativeSecureStorage implements TokenStorage {
24
+ // Set to false because Keychain stores strings, so we need extractable (serializable) keys
25
+ readonly supportsObjects = false;
26
+
27
+ // Define which keys MUST go to secure hardware storage
28
+ private readonly SECURE_KEYS = new Set([
29
+ DPOP_STORAGE_KEYS.PRIVATE,
30
+ AUTH_STORAGE_KEYS.ACCESS_TOKEN,
31
+ AUTH_STORAGE_KEYS.REFRESH_TOKEN,
32
+ AUTH_STORAGE_KEYS.PROVIDER_TOKEN
33
+ ]);
34
+
35
+ private fallbackStorage: AsyncStorageTokenStorage;
36
+
37
+ constructor(private keyPrefix: string = 'pers_tokens_') {
38
+ this.fallbackStorage = new AsyncStorageTokenStorage(keyPrefix);
39
+ }
40
+
41
+ async set(key: string, value: unknown): Promise<void> {
42
+ const prefixedKey = this.getKeyName(key);
43
+ const stringValue = typeof value === 'string' ? value : JSON.stringify(value);
44
+
45
+ // Cast key to any to bypass strict literal type checking of the Set.has method
46
+ // In runtime, 'key' is just a string, so this is safe.
47
+ if (this.SECURE_KEYS.has(key as any)) {
48
+ // Store in Keychain/Keystore
49
+ try {
50
+ if (Keychain) {
51
+ // Service name acts as the key identifier in simple usage
52
+ await Keychain.setGenericPassword(prefixedKey, stringValue, { service: prefixedKey });
53
+ } else {
54
+ throw new Error('RN Keychain not available');
55
+ }
56
+ } catch (e) {
57
+ console.warn(`[ReactNativeSecureStorage] Keychain set failed for ${key}, falling back to AsyncStorage`, e);
58
+ // Fallback to AsyncStorage if Keychain fails
59
+ await this.fallbackStorage.set(key, stringValue);
60
+ }
61
+ } else {
62
+ // Store standard config/metadata in AsyncStorage
63
+ await this.fallbackStorage.set(key, stringValue);
64
+ }
65
+ }
66
+
67
+ async get(key: string): Promise<unknown | null> {
68
+ const prefixedKey = this.getKeyName(key);
69
+
70
+ if (this.SECURE_KEYS.has(key as any)) {
71
+ // The iOS Keychain can be briefly unreadable (device locked, background launch), so retry before giving up
72
+ let keychainError: unknown;
73
+ if (Keychain) {
74
+ for (let attempt = 0; attempt < 3; attempt++) {
75
+ try {
76
+ const credentials = await Keychain.getGenericPassword({ service: prefixedKey });
77
+ if (credentials) {
78
+ return this.tryParse(credentials.password);
79
+ }
80
+ keychainError = undefined;
81
+ break;
82
+ } catch (e) {
83
+ keychainError = e;
84
+ if (attempt < 2) await new Promise(resolve => setTimeout(resolve, 150 * (attempt + 1)));
85
+ }
86
+ }
87
+ }
88
+
89
+ if (keychainError) {
90
+ console.warn(`[ReactNativeSecureStorage] Failed to access keychain for ${key}`, keychainError);
91
+ // Returning "missing" here would make the DPoP manager regenerate the key pair and log the user out
92
+ if (key === DPOP_STORAGE_KEYS.PRIVATE) throw keychainError;
93
+ }
94
+
95
+ // Fallback: Check AsyncStorage if not found in Keychain or Keychain failed
96
+ try {
97
+ const val = await this.fallbackStorage.get(key);
98
+ return val ? this.tryParse(val) : null;
99
+ } catch (e) {
100
+ return null;
101
+ }
102
+ } else {
103
+ try {
104
+ const val = await this.fallbackStorage.get(key);
105
+ return val ? this.tryParse(val) : null;
106
+ } catch (e) {
107
+ console.warn(`[ReactNativeSecureStorage] Failed to access AsyncStorage for ${key}`, e);
108
+ return null;
109
+ }
110
+ }
111
+ }
112
+
113
+ async remove(key: string): Promise<void> {
114
+ const prefixedKey = this.getKeyName(key);
115
+
116
+ if (this.SECURE_KEYS.has(key as any)) {
117
+ try {
118
+ if (Keychain) {
119
+ await Keychain.resetGenericPassword({ service: prefixedKey });
120
+ }
121
+ } catch (e) {
122
+ console.warn(`[ReactNativeSecureStorage] Failed to reset keychain for ${key}`, e);
123
+ }
124
+ // Always remove from fallback storage too, just in case
125
+ await this.fallbackStorage.remove(key);
126
+ } else {
127
+ await this.fallbackStorage.remove(key);
128
+ }
129
+ }
130
+
131
+ async clear(): Promise<void> {
132
+ const clearPromises: Promise<void>[] = [];
133
+
134
+ // Clear all known secure keys with retry logic
135
+ for (const key of this.SECURE_KEYS) {
136
+ clearPromises.push(
137
+ (async () => {
138
+ try {
139
+ if (Keychain) {
140
+ await Keychain.resetGenericPassword({ service: this.getKeyName(key) });
141
+ }
142
+ } catch (e) {
143
+ console.warn(`[ReactNativeSecureStorage] Retry clearing ${key}`);
144
+ // Retry once
145
+ try {
146
+ if (Keychain) {
147
+ await Keychain.resetGenericPassword({ service: this.getKeyName(key) });
148
+ }
149
+ } catch (retryError) {
150
+ console.error(`[ReactNativeSecureStorage] Failed to clear ${key} after retry:`, retryError);
151
+ }
152
+ }
153
+ })()
154
+ );
155
+ }
156
+
157
+ // Wait for all Keychain clearing to complete (or fail)
158
+ await Promise.allSettled(clearPromises);
159
+
160
+ // Always clear AsyncStorage fallback
161
+ try {
162
+ await this.fallbackStorage.clear();
163
+ } catch (e) {
164
+ console.error('[ReactNativeSecureStorage] Failed to clear AsyncStorage:', e);
165
+ }
166
+ }
167
+
168
+ private getKeyName(key: string): string {
169
+ // For Keychain, we might want to avoid prefixes if we want to share across apps,
170
+ // but for simple isolation, prefix is fine to avoid collisions if multiple instances exist.
171
+ // However, Keychain services are global to the app bundle ID usually.
172
+ return `${this.keyPrefix}${key}`;
173
+ }
174
+
175
+ private tryParse(val: string): unknown {
176
+ try {
177
+ return JSON.parse(val);
178
+ } catch {
179
+ return val;
180
+ }
181
+ }
182
+ }
@@ -1,46 +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
-
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
46
  export {};
@@ -1,8 +1,8 @@
1
- /**
2
- * Utility exports for React Native SDK
3
- */
4
-
5
- export {
6
- detectReactNativePlatform,
7
- detectReactNativePlatformSync
8
- } from './platform-detection';
1
+ /**
2
+ * Utility exports for React Native SDK
3
+ */
4
+
5
+ export {
6
+ detectReactNativePlatform,
7
+ detectReactNativePlatformSync
8
+ } from './platform-detection';
@@ -1,86 +1,86 @@
1
- /**
2
- * React Native Platform Detection Utilities
3
- *
4
- * Provides automatic platform info detection for React Native apps.
5
- *
6
- * @module @explorins/pers-sdk-react-native/utils
7
- */
8
-
9
- import { Platform as RNPlatform } from 'react-native';
10
- import type { Platform } from '@explorins/pers-sdk/core';
11
-
12
- // DeviceInfo is optional - dynamically loaded if available
13
- let DeviceInfo: any = null;
14
- try {
15
- // eslint-disable-next-line @typescript-eslint/no-require-imports
16
- DeviceInfo = require('react-native-device-info').default;
17
- } catch {
18
- // react-native-device-info not installed - will use basic detection
19
- }
20
-
21
- /**
22
- * Detect platform info for React Native apps
23
- *
24
- * Automatically gathers OS, version, app info, and device type.
25
- * Uses react-native-device-info for enhanced detection if installed.
26
- *
27
- * @example
28
- * ```typescript
29
- * import { detectReactNativePlatform } from '@explorins/pers-sdk-react-native';
30
- *
31
- * // Get platform info
32
- * const platform = await detectReactNativePlatform();
33
- * // { os: 'iOS', osVersion: '17.4', app: 'MyApp', appVersion: '2.3.1', deviceType: 'phone' }
34
- *
35
- * // Set in SDK
36
- * sdk.setPlatform(platform);
37
- * ```
38
- */
39
- export async function detectReactNativePlatform(): Promise<Platform> {
40
- const platform: Platform = {
41
- os: RNPlatform.OS === 'ios' ? 'iOS' : RNPlatform.OS === 'android' ? 'Android' : RNPlatform.OS,
42
- osVersion: RNPlatform.Version?.toString(),
43
- deviceType: 'phone', // Default fallback
44
- };
45
-
46
- if (DeviceInfo) {
47
- try {
48
- // App info
49
- platform.app = await DeviceInfo.getApplicationName();
50
- platform.appVersion = DeviceInfo.getVersion();
51
-
52
- // Device type detection
53
- const isTablet = await DeviceInfo.isTablet();
54
- platform.deviceType = isTablet ? 'tablet' : 'phone';
55
- } catch (error) {
56
- console.debug('[Platform] DeviceInfo error, using basic detection');
57
- }
58
- }
59
-
60
- return platform;
61
- }
62
-
63
- /**
64
- * Synchronous version for immediate use (limited info)
65
- *
66
- * Use when you need platform info immediately without async.
67
- * For full info including device type, use detectReactNativePlatform().
68
- */
69
- export function detectReactNativePlatformSync(): Platform {
70
- const platform: Platform = {
71
- os: RNPlatform.OS === 'ios' ? 'iOS' : RNPlatform.OS === 'android' ? 'Android' : RNPlatform.OS,
72
- osVersion: RNPlatform.Version?.toString(),
73
- deviceType: 'phone', // Default, can't detect tablet sync without DeviceInfo
74
- };
75
-
76
- if (DeviceInfo) {
77
- try {
78
- platform.app = DeviceInfo.getApplicationNameSync?.();
79
- platform.appVersion = DeviceInfo.getVersion?.();
80
- } catch {
81
- // Ignore if DeviceInfo methods fail
82
- }
83
- }
84
-
85
- return platform;
86
- }
1
+ /**
2
+ * React Native Platform Detection Utilities
3
+ *
4
+ * Provides automatic platform info detection for React Native apps.
5
+ *
6
+ * @module @explorins/pers-sdk-react-native/utils
7
+ */
8
+
9
+ import { Platform as RNPlatform } from 'react-native';
10
+ import type { Platform } from '@explorins/pers-sdk/core';
11
+
12
+ // DeviceInfo is optional - dynamically loaded if available
13
+ let DeviceInfo: any = null;
14
+ try {
15
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
16
+ DeviceInfo = require('react-native-device-info').default;
17
+ } catch {
18
+ // react-native-device-info not installed - will use basic detection
19
+ }
20
+
21
+ /**
22
+ * Detect platform info for React Native apps
23
+ *
24
+ * Automatically gathers OS, version, app info, and device type.
25
+ * Uses react-native-device-info for enhanced detection if installed.
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * import { detectReactNativePlatform } from '@explorins/pers-sdk-react-native';
30
+ *
31
+ * // Get platform info
32
+ * const platform = await detectReactNativePlatform();
33
+ * // { os: 'iOS', osVersion: '17.4', app: 'MyApp', appVersion: '2.3.1', deviceType: 'phone' }
34
+ *
35
+ * // Set in SDK
36
+ * sdk.setPlatform(platform);
37
+ * ```
38
+ */
39
+ export async function detectReactNativePlatform(): Promise<Platform> {
40
+ const platform: Platform = {
41
+ os: RNPlatform.OS === 'ios' ? 'iOS' : RNPlatform.OS === 'android' ? 'Android' : RNPlatform.OS,
42
+ osVersion: RNPlatform.Version?.toString(),
43
+ deviceType: 'phone', // Default fallback
44
+ };
45
+
46
+ if (DeviceInfo) {
47
+ try {
48
+ // App info
49
+ platform.app = await DeviceInfo.getApplicationName();
50
+ platform.appVersion = DeviceInfo.getVersion();
51
+
52
+ // Device type detection
53
+ const isTablet = await DeviceInfo.isTablet();
54
+ platform.deviceType = isTablet ? 'tablet' : 'phone';
55
+ } catch (error) {
56
+ console.debug('[Platform] DeviceInfo error, using basic detection');
57
+ }
58
+ }
59
+
60
+ return platform;
61
+ }
62
+
63
+ /**
64
+ * Synchronous version for immediate use (limited info)
65
+ *
66
+ * Use when you need platform info immediately without async.
67
+ * For full info including device type, use detectReactNativePlatform().
68
+ */
69
+ export function detectReactNativePlatformSync(): Platform {
70
+ const platform: Platform = {
71
+ os: RNPlatform.OS === 'ios' ? 'iOS' : RNPlatform.OS === 'android' ? 'Android' : RNPlatform.OS,
72
+ osVersion: RNPlatform.Version?.toString(),
73
+ deviceType: 'phone', // Default, can't detect tablet sync without DeviceInfo
74
+ };
75
+
76
+ if (DeviceInfo) {
77
+ try {
78
+ platform.app = DeviceInfo.getApplicationNameSync?.();
79
+ platform.appVersion = DeviceInfo.getVersion?.();
80
+ } catch {
81
+ // Ignore if DeviceInfo methods fail
82
+ }
83
+ }
84
+
85
+ return platform;
86
+ }