@explorins/pers-sdk-react-native 2.3.0-alpha.3 → 2.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.
@@ -1 +1 @@
1
- {"version":3,"file":"PersSDKProvider.d.ts","sourceRoot":"","sources":["../../src/providers/PersSDKProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAuC,SAAS,EAA2C,MAAM,OAAO,CAAC;AAEvH,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAIpF,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAkB3D,YAAY,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAE3D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,MAAM,WAAW,cAAc;IAE7B,GAAG,EAAE,OAAO,GAAG,IAAI,CAAC;IAGpB,YAAY,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAGzC,aAAa,EAAE,OAAO,CAAC;IACvB,eAAe,EAAE,OAAO,CAAC;IACzB,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,CAAC;IAGhC,UAAU,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,sBAAsB,EAAE,CAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,EAAE,eAAe,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5F,eAAe,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,cAAc,EAAE,MAAM,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;CAC/C;AAKD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC;IACrC,QAAQ,EAAE,SAAS,CAAC;IACpB,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB,CA6NA,CAAC;AAGF,eAAO,MAAM,UAAU,QAAO,cAQ7B,CAAC"}
1
+ {"version":3,"file":"PersSDKProvider.d.ts","sourceRoot":"","sources":["../../src/providers/PersSDKProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAuC,SAAS,EAA2C,MAAM,OAAO,CAAC;AAEvH,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAIpF,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAmB3D,YAAY,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAE3D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,MAAM,WAAW,cAAc;IAE7B,GAAG,EAAE,OAAO,GAAG,IAAI,CAAC;IAGpB,YAAY,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAGzC,aAAa,EAAE,OAAO,CAAC;IACvB,eAAe,EAAE,OAAO,CAAC;IACzB,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,CAAC;IAGhC,UAAU,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,sBAAsB,EAAE,CAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,EAAE,eAAe,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5F,eAAe,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,cAAc,EAAE,MAAM,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;CAC/C;AAKD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC;IACrC,QAAQ,EAAE,SAAS,CAAC;IACpB,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB,CA+OA,CAAC;AAGF,eAAO,MAAM,UAAU,QAAO,cAQ7B,CAAC"}
@@ -5,6 +5,7 @@ import { PersSDK } from '@explorins/pers-sdk/core';
5
5
  import { ReactNativeHttpClient } from './react-native-http-client';
6
6
  import { createReactNativeAuthProvider } from './react-native-auth-provider';
7
7
  import { ReactNativeDPoPProvider } from './rn-dpop-provider';
8
+ import { detectReactNativePlatform } from '../utils/platform-detection';
8
9
  // Create the context
9
10
  const SDKContext = createContext(null);
10
11
  /**
@@ -92,6 +93,22 @@ export const PersSDKProvider = ({ children, config }) => {
92
93
  };
93
94
  // Initialize PersSDK with platform-aware auth provider
94
95
  const sdkInstance = new PersSDK(httpClient, sdkConfig);
96
+ // Auto-configure data source and platform for React Native
97
+ // Native apps MUST set channel as backend cannot auto-detect without browser User-Agent
98
+ if (Platform.OS !== 'web') {
99
+ sdkInstance.setDataSource({
100
+ channel: 'mobile',
101
+ source: config.dataSource?.source || 'app',
102
+ medium: config.dataSource?.medium,
103
+ campaign: config.dataSource?.campaign,
104
+ });
105
+ // Auto-detect platform info asynchronously
106
+ detectReactNativePlatform().then(platformInfo => {
107
+ sdkInstance.setPlatform(platformInfo);
108
+ }).catch(error => {
109
+ console.debug('[PersSDK] Platform detection failed:', error);
110
+ });
111
+ }
95
112
  // Subscribe to auth events BEFORE exposing SDK to consumers
96
113
  // This ensures no events are missed due to React timing
97
114
  eventUnsubscribeRef.current = sdkInstance.events.subscribe((event) => {
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Utility exports for React Native SDK
3
+ */
4
+ export { detectReactNativePlatform, detectReactNativePlatformSync } from './platform-detection';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,yBAAyB,EACzB,6BAA6B,EAC9B,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Utility exports for React Native SDK
3
+ */
4
+ export { detectReactNativePlatform, detectReactNativePlatformSync } from './platform-detection';
@@ -0,0 +1,35 @@
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
+ import type { Platform } from '@explorins/pers-sdk/core';
9
+ /**
10
+ * Detect platform info for React Native apps
11
+ *
12
+ * Automatically gathers OS, version, app info, and device type.
13
+ * Uses react-native-device-info for enhanced detection if installed.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * import { detectReactNativePlatform } from '@explorins/pers-sdk-react-native';
18
+ *
19
+ * // Get platform info
20
+ * const platform = await detectReactNativePlatform();
21
+ * // { os: 'iOS', osVersion: '17.4', app: 'MyApp', appVersion: '2.3.1', deviceType: 'phone' }
22
+ *
23
+ * // Set in SDK
24
+ * sdk.setPlatform(platform);
25
+ * ```
26
+ */
27
+ export declare function detectReactNativePlatform(): Promise<Platform>;
28
+ /**
29
+ * Synchronous version for immediate use (limited info)
30
+ *
31
+ * Use when you need platform info immediately without async.
32
+ * For full info including device type, use detectReactNativePlatform().
33
+ */
34
+ export declare function detectReactNativePlatformSync(): Platform;
35
+ //# sourceMappingURL=platform-detection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform-detection.d.ts","sourceRoot":"","sources":["../../src/utils/platform-detection.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAWzD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,yBAAyB,IAAI,OAAO,CAAC,QAAQ,CAAC,CAsBnE;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,IAAI,QAAQ,CAiBxD"}
@@ -0,0 +1,79 @@
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
+ import { Platform as RNPlatform } from 'react-native';
9
+ // DeviceInfo is optional - dynamically loaded if available
10
+ let DeviceInfo = null;
11
+ try {
12
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
13
+ DeviceInfo = require('react-native-device-info').default;
14
+ }
15
+ catch {
16
+ // react-native-device-info not installed - will use basic detection
17
+ }
18
+ /**
19
+ * Detect platform info for React Native apps
20
+ *
21
+ * Automatically gathers OS, version, app info, and device type.
22
+ * Uses react-native-device-info for enhanced detection if installed.
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * import { detectReactNativePlatform } from '@explorins/pers-sdk-react-native';
27
+ *
28
+ * // Get platform info
29
+ * const platform = await detectReactNativePlatform();
30
+ * // { os: 'iOS', osVersion: '17.4', app: 'MyApp', appVersion: '2.3.1', deviceType: 'phone' }
31
+ *
32
+ * // Set in SDK
33
+ * sdk.setPlatform(platform);
34
+ * ```
35
+ */
36
+ export async function detectReactNativePlatform() {
37
+ const platform = {
38
+ os: RNPlatform.OS === 'ios' ? 'iOS' : RNPlatform.OS === 'android' ? 'Android' : RNPlatform.OS,
39
+ osVersion: RNPlatform.Version?.toString(),
40
+ deviceType: 'phone', // Default fallback
41
+ };
42
+ if (DeviceInfo) {
43
+ try {
44
+ // App info
45
+ platform.app = await DeviceInfo.getApplicationName();
46
+ platform.appVersion = DeviceInfo.getVersion();
47
+ // Device type detection
48
+ const isTablet = await DeviceInfo.isTablet();
49
+ platform.deviceType = isTablet ? 'tablet' : 'phone';
50
+ }
51
+ catch (error) {
52
+ console.debug('[Platform] DeviceInfo error, using basic detection');
53
+ }
54
+ }
55
+ return platform;
56
+ }
57
+ /**
58
+ * Synchronous version for immediate use (limited info)
59
+ *
60
+ * Use when you need platform info immediately without async.
61
+ * For full info including device type, use detectReactNativePlatform().
62
+ */
63
+ export function detectReactNativePlatformSync() {
64
+ const platform = {
65
+ os: RNPlatform.OS === 'ios' ? 'iOS' : RNPlatform.OS === 'android' ? 'Android' : RNPlatform.OS,
66
+ osVersion: RNPlatform.Version?.toString(),
67
+ deviceType: 'phone', // Default, can't detect tablet sync without DeviceInfo
68
+ };
69
+ if (DeviceInfo) {
70
+ try {
71
+ platform.app = DeviceInfo.getApplicationNameSync?.();
72
+ platform.appVersion = DeviceInfo.getVersion?.();
73
+ }
74
+ catch {
75
+ // Ignore if DeviceInfo methods fail
76
+ }
77
+ }
78
+ return platform;
79
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@explorins/pers-sdk-react-native",
3
- "version": "2.3.0-alpha.3",
3
+ "version": "2.3.2",
4
4
  "description": "React Native SDK for PERS Platform - Tourism Loyalty System with Blockchain Transaction Signing and WebAuthn Authentication",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -37,7 +37,7 @@
37
37
  "author": "eXplorins",
38
38
  "license": "MIT",
39
39
  "dependencies": {
40
- "@explorins/pers-sdk": "^2.2.0-alpha.2",
40
+ "@explorins/pers-sdk": "^2.3.3",
41
41
  "@explorins/pers-signer": "^1.0.34",
42
42
  "buffer": "^6.0.3",
43
43
  "ethers": "^6.15.0",
@@ -94,6 +94,7 @@
94
94
  "@react-native-async-storage/async-storage": ">=1.15.0",
95
95
  "react": ">=16.8.0",
96
96
  "react-native": ">=0.60.0",
97
+ "react-native-device-info": "^15.0.2",
97
98
  "react-native-get-random-values": ">=1.8.0",
98
99
  "react-native-keychain": ">=8.0.0",
99
100
  "react-native-passkey": ">=3.0.0",
@@ -103,6 +104,9 @@
103
104
  "@react-native-async-storage/async-storage": {
104
105
  "optional": false
105
106
  },
107
+ "react-native-device-info": {
108
+ "optional": true
109
+ },
106
110
  "react-native-get-random-values": {
107
111
  "optional": false
108
112
  },
@@ -162,7 +166,7 @@
162
166
  "@ethersproject/scrypt": false
163
167
  },
164
168
  "devDependencies": {
165
- "@explorins/pers-shared": "^2.1.128",
169
+ "@explorins/pers-shared": "^2.1.183",
166
170
  "@react-native-async-storage/async-storage": "^3.0.2",
167
171
  "@rollup/plugin-commonjs": "^25.0.7",
168
172
  "@rollup/plugin-json": "^6.1.0",
@@ -14,11 +14,15 @@ import type {
14
14
  DynamicContext,
15
15
  SortOrder
16
16
  } from '@explorins/pers-sdk';
17
- import type { RedemptionRedeemFilters } from '@explorins/pers-sdk/redemption';
17
+ import type { RedemptionRedeemQueryParams, RedemptionQueryParams } from '@explorins/pers-sdk/redemption';
18
18
  import type { PaginationOptions } from '@explorins/pers-sdk';
19
19
 
20
- // Re-export for consumers
21
- export type { RedemptionRedeemFilters, RedemptionFilterOptions } from '@explorins/pers-sdk/redemption';
20
+ // Re-export for consumers (new names + deprecated aliases)
21
+ export type { RedemptionRedeemQueryParams, RedemptionQueryParams } from '@explorins/pers-sdk/redemption';
22
+ /** @deprecated Use RedemptionRedeemQueryParams instead */
23
+ export type RedemptionRedeemFilters = RedemptionRedeemQueryParams;
24
+ /** @deprecated Use RedemptionQueryParams instead */
25
+ export type RedemptionFilterOptions = RedemptionQueryParams;
22
26
 
23
27
  /**
24
28
  * Options for fetching redemptions with filtering and pagination
@@ -105,7 +109,14 @@ export const useRedemptions = () => {
105
109
  }
106
110
 
107
111
  try {
108
- const result = await sdk.redemptions.getRedemptions(options);
112
+ // Convert Date to ISO string for API compatibility
113
+ const apiOptions = options ? {
114
+ ...options,
115
+ startDate: options.startDate instanceof Date ? options.startDate.toISOString() : options.startDate,
116
+ endDate: options.endDate instanceof Date ? options.endDate.toISOString() : options.endDate,
117
+ } : undefined;
118
+
119
+ const result = await sdk.redemptions.getRedemptions(apiOptions);
109
120
  return result;
110
121
  } catch (error) {
111
122
  console.error('Failed to fetch redemptions:', error);
@@ -327,7 +338,7 @@ export const useRedemptions = () => {
327
338
  * ```
328
339
  */
329
340
  const getRedemptionRedeems = useCallback(async (
330
- filters?: RedemptionRedeemFilters,
341
+ filters?: RedemptionRedeemQueryParams,
331
342
  include?: RedemptionRedeemIncludeRelation[]
332
343
  ): Promise<PaginatedResponseDTO<RedemptionRedeemDTO>> => {
333
344
  if (!isInitialized || !sdk) {
package/src/index.ts CHANGED
@@ -1,3 +1,6 @@
1
+ // Polyfills - must be imported first for Hermes engine compatibility
2
+ import 'react-native-url-polyfill/auto';
3
+
1
4
  /**
2
5
  * @fileoverview PERS SDK for React Native - Tourism Loyalty System
3
6
  *
@@ -566,4 +569,202 @@ export {
566
569
  PersApiError,
567
570
  AuthenticationError,
568
571
  ErrorUtils
569
- } from '@explorins/pers-sdk/core';
572
+ } from '@explorins/pers-sdk/core';
573
+
574
+ // ==============================================================================
575
+ // DATA SOURCE TRACKING
576
+ // ==============================================================================
577
+
578
+ /**
579
+ * Data source tracking utilities for attribution analytics
580
+ *
581
+ * Native apps MUST configure data source and platform info for proper attribution.
582
+ * Web apps benefit from auto-detection, but can still set explicit values.
583
+ *
584
+ * @example
585
+ * **React Native Setup:**
586
+ * ```typescript
587
+ * import { usePersSDK, detectReactNativePlatform } from '@explorins/pers-sdk-react-native';
588
+ *
589
+ * const { sdk } = usePersSDK();
590
+ *
591
+ * // Set platform info (call once on app start)
592
+ * sdk.setPlatform(detectReactNativePlatform());
593
+ *
594
+ * // Set data source (call once, update on deep links/push)
595
+ * sdk.setDataSource({
596
+ * channel: 'mobile',
597
+ * source: 'app_store',
598
+ * });
599
+ * ```
600
+ *
601
+ * @example
602
+ * **Handle Deep Links:**
603
+ * ```typescript
604
+ * import { parseUtmParams } from '@explorins/pers-sdk-react-native';
605
+ *
606
+ * function handleDeepLink(url: string) {
607
+ * const utm = parseUtmParams(url);
608
+ * sdk.updateDataSource({
609
+ * medium: utm.medium,
610
+ * campaign: utm.campaign,
611
+ * source: utm.source || 'deep_link',
612
+ * });
613
+ * }
614
+ * ```
615
+ *
616
+ * @example
617
+ * **Handle Push Notifications:**
618
+ * ```typescript
619
+ * function handlePushNotification(notification: PushNotification) {
620
+ * sdk.updateDataSource({
621
+ * medium: 'push',
622
+ * source: 'push_notification',
623
+ * campaign: notification.campaignId,
624
+ * });
625
+ * }
626
+ * ```
627
+ */
628
+ export {
629
+ // Types
630
+ type Platform,
631
+ type DataSource,
632
+ type UtmParams,
633
+ type WebDataSourceConfig,
634
+ type NativeDataSourceConfig,
635
+
636
+ // Utilities
637
+ parseUtmParams,
638
+ detectSourceFromReferrer,
639
+ detectWebPlatform,
640
+ } from '@explorins/pers-sdk/core';
641
+
642
+ // React Native specific platform detection
643
+ export { detectReactNativePlatform } from './utils/platform-detection';
644
+
645
+ // ==============================================================================
646
+ // FIELD VALIDATION UTILITIES
647
+ // ==============================================================================
648
+
649
+ /**
650
+ * Field validation utilities for custom fields and forms
651
+ *
652
+ * These functions are re-exported from @explorins/pers-shared for convenience.
653
+ * Use them for client-side validation before submitting data to the backend.
654
+ *
655
+ * @example
656
+ * **Single Field Validation:**
657
+ * ```typescript
658
+ * import { validateFieldValue, FieldType, FieldValidationRules } from '@explorins/pers-sdk-react-native';
659
+ *
660
+ * const rules: FieldValidationRules = { minLength: 3 };
661
+ * const error = validateFieldValue('ab', rules, 'text');
662
+ * // error: { message: 'Must be at least 3 characters' }
663
+ * ```
664
+ *
665
+ * @example
666
+ * **Form Validation:**
667
+ * ```typescript
668
+ * import { validateAllFields, FieldDefinition } from '@explorins/pers-sdk-react-native';
669
+ *
670
+ * const definitions: FieldDefinition[] = [
671
+ * { key: 'email', fieldType: 'email', label: 'Email', validation: { minLength: 1 } },
672
+ * { key: 'phone', fieldType: 'phone', label: 'Phone', validation: {} }
673
+ * ];
674
+ *
675
+ * const formData = { email: 'invalid', phone: '123' };
676
+ * const errors = validateAllFields(formData, definitions);
677
+ * // errors: [{ field: 'email', message: 'Invalid email format' }]
678
+ * ```
679
+ *
680
+ * @see {@link validateFieldValue} - Validate a single field value
681
+ * @see {@link validateAllFields} - Validate all fields against definitions
682
+ * @see {@link FieldDefinition} - Field definition structure
683
+ * @see {@link FieldValidationRules} - Available validation rules
684
+ */
685
+ export {
686
+ validateFieldValue,
687
+ validateAllFields,
688
+ validateFieldComparisons,
689
+ compareValues,
690
+ type FieldDefinition,
691
+ type FieldValidationRules,
692
+ type ValidationErrorDetail,
693
+ type FieldType,
694
+ type FieldComparison,
695
+ type ComparisonOperator,
696
+ } from '@explorins/pers-sdk';
697
+
698
+ // ==============================================================================
699
+ // BOOKING STATUS VALIDATION
700
+ // ==============================================================================
701
+
702
+ /**
703
+ * Booking status types and validation utilities for eligibility checks
704
+ *
705
+ * Use these to validate if a user's booking status meets requirements
706
+ * for redemptions, campaigns, or other booking-dependent features.
707
+ *
708
+ * **BookingStatus:** 'past' | 'active' | 'future'
709
+ * - `past`: User's stay has ended (checkOut ≤ now)
710
+ * - `active`: User is currently staying (checkIn ≤ now < checkOut)
711
+ * - `future`: Upcoming booking (checkIn > now)
712
+ *
713
+ * **BookingRequirementType:** 'active' | 'future' | 'past' | 'active_future' | 'any'
714
+ * - `active`: Only active bookings allowed
715
+ * - `future`: Only future bookings allowed
716
+ * - `past`: Only past bookings allowed
717
+ * - `active_future`: Active OR future bookings allowed
718
+ * - `any`: Any booking status allowed
719
+ *
720
+ * @example
721
+ * **Basic Validation:**
722
+ * ```typescript
723
+ * import {
724
+ * isBookingStatusValid,
725
+ * BookingStatus,
726
+ * BookingRequirementType
727
+ * } from '@explorins/pers-sdk-react-native';
728
+ *
729
+ * const userBookingStatus: BookingStatus = booking.status;
730
+ * const requirement: BookingRequirementType = redemption.bookingRequirement;
731
+ * const canRedeem = isBookingStatusValid(userBookingStatus, requirement);
732
+ * ```
733
+ *
734
+ * @example
735
+ * **Redemption Screen:**
736
+ * ```typescript
737
+ * function RedemptionCard({ redemption, userBooking }) {
738
+ * const isEligible = redemption.bookingRequirement
739
+ * ? isBookingStatusValid(userBooking?.status ?? 'past', redemption.bookingRequirement)
740
+ * : true;
741
+ *
742
+ * return (
743
+ * <View style={[styles.card, !isEligible && styles.disabled]}>
744
+ * <Text>{redemption.name}</Text>
745
+ * {!isEligible && (
746
+ * <Text style={styles.warning}>
747
+ * Requires {redemption.bookingRequirement} booking
748
+ * </Text>
749
+ * )}
750
+ * </View>
751
+ * );
752
+ * }
753
+ * ```
754
+ *
755
+ * @example
756
+ * **Validation Results:**
757
+ * ```typescript
758
+ * isBookingStatusValid('active', 'active'); // true
759
+ * isBookingStatusValid('future', 'active'); // false
760
+ * isBookingStatusValid('active', 'active_future'); // true
761
+ * isBookingStatusValid('future', 'active_future'); // true
762
+ * isBookingStatusValid('past', 'active_future'); // false
763
+ * isBookingStatusValid('past', 'any'); // true
764
+ * ```
765
+ */
766
+ export {
767
+ isBookingStatusValid,
768
+ type BookingStatus,
769
+ type BookingRequirementType
770
+ } from '@explorins/pers-sdk';
@@ -5,6 +5,7 @@ import { ReactNativeHttpClient } from './react-native-http-client';
5
5
  import { createReactNativeAuthProvider } from './react-native-auth-provider';
6
6
  import { ReactNativeDPoPProvider } from './rn-dpop-provider';
7
7
  import { UserDTO, AdminDTO } from '@explorins/pers-shared';
8
+ import { detectReactNativePlatform } from '../utils/platform-detection';
8
9
 
9
10
  // Import manager types for TypeScript
10
11
  import type {
@@ -191,6 +192,24 @@ export const PersSDKProvider: React.FC<{
191
192
  // Initialize PersSDK with platform-aware auth provider
192
193
  const sdkInstance = new PersSDK(httpClient, sdkConfig);
193
194
 
195
+ // Auto-configure data source and platform for React Native
196
+ // Native apps MUST set channel as backend cannot auto-detect without browser User-Agent
197
+ if (Platform.OS !== 'web') {
198
+ sdkInstance.setDataSource({
199
+ channel: 'mobile',
200
+ source: config.dataSource?.source || 'app',
201
+ medium: config.dataSource?.medium,
202
+ campaign: config.dataSource?.campaign,
203
+ });
204
+
205
+ // Auto-detect platform info asynchronously
206
+ detectReactNativePlatform().then(platformInfo => {
207
+ sdkInstance.setPlatform(platformInfo);
208
+ }).catch(error => {
209
+ console.debug('[PersSDK] Platform detection failed:', error);
210
+ });
211
+ }
212
+
194
213
  // Subscribe to auth events BEFORE exposing SDK to consumers
195
214
  // This ensures no events are missed due to React timing
196
215
  eventUnsubscribeRef.current = sdkInstance.events.subscribe((event) => {
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Utility exports for React Native SDK
3
+ */
4
+
5
+ export {
6
+ detectReactNativePlatform,
7
+ detectReactNativePlatformSync
8
+ } from './platform-detection';
@@ -0,0 +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
+ }