@explorins/pers-sdk-react-native 2.3.7 → 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 (58) 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/useWeb3.js +4 -4
  11. package/dist/index.js +2400 -487
  12. package/dist/index.js.map +1 -1
  13. package/dist/providers/PersSDKProvider.d.ts +4 -0
  14. package/dist/providers/PersSDKProvider.d.ts.map +1 -1
  15. package/dist/providers/PersSDKProvider.js +14 -0
  16. package/dist/providers/react-native-http-client.d.ts +2 -0
  17. package/dist/providers/react-native-http-client.d.ts.map +1 -1
  18. package/dist/providers/react-native-http-client.js +5 -2
  19. package/dist/storage/rn-secure-storage.d.ts.map +1 -1
  20. package/dist/storage/rn-secure-storage.js +21 -8
  21. package/package.json +3 -3
  22. package/src/__tests__/PersSDKProvider.test.tsx +484 -420
  23. package/src/__tests__/setup.ts +40 -40
  24. package/src/hooks/index.ts +60 -60
  25. package/src/hooks/useAnalytics.ts +327 -327
  26. package/src/hooks/useAuth.ts +265 -265
  27. package/src/hooks/useBookings.ts +191 -191
  28. package/src/hooks/useBusiness.ts +251 -251
  29. package/src/hooks/useCampaigns.ts +500 -500
  30. package/src/hooks/useDonations.ts +66 -66
  31. package/src/hooks/useEvents.ts +372 -372
  32. package/src/hooks/useFiles.ts +155 -155
  33. package/src/hooks/useIPFSUrl.ts +192 -192
  34. package/src/hooks/usePurchases.ts +65 -65
  35. package/src/hooks/useRedemptions.ts +467 -467
  36. package/src/hooks/useTenants.ts +72 -72
  37. package/src/hooks/useTokenBalances.ts +385 -385
  38. package/src/hooks/useTokens.ts +313 -313
  39. package/src/hooks/useTransactionSigner.ts +611 -611
  40. package/src/hooks/useTransactions.ts +299 -299
  41. package/src/hooks/useTriggerSources.ts +301 -301
  42. package/src/hooks/useUserStatus.ts +61 -61
  43. package/src/hooks/useUsers.ts +167 -167
  44. package/src/hooks/useWeb3.ts +355 -355
  45. package/src/index.ts +777 -777
  46. package/src/polyfills/index.ts +160 -160
  47. package/src/polyfills/web3-polyfills.ts +97 -97
  48. package/src/providers/PersSDKProvider.tsx +393 -374
  49. package/src/providers/react-native-auth-provider.ts +110 -110
  50. package/src/providers/react-native-http-client.ts +150 -145
  51. package/src/providers/rn-dpop-provider.ts +190 -190
  52. package/src/storage/async-storage-token-storage.ts +185 -185
  53. package/src/storage/rn-secure-storage.ts +182 -170
  54. package/src/types/react-native-globals.d.ts +45 -45
  55. package/src/utils/index.ts +8 -8
  56. package/src/utils/platform-detection.ts +86 -86
  57. package/dist/index-CzLxqePZ.js +0 -7717
  58. 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
+ }