@explorins/pers-sdk-react-native 2.3.0-alpha.1 → 2.3.0-alpha.3
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/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useBookings.d.ts +55 -0
- package/dist/hooks/useBookings.d.ts.map +1 -0
- package/dist/hooks/useBookings.js +153 -0
- package/dist/index.js +592 -81
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/hooks/index.ts +5 -1
- package/src/hooks/useBookings.ts +191 -0
package/dist/index.js
CHANGED
|
@@ -3517,15 +3517,38 @@ const validateFieldValue = (value, rules, fieldType) => {
|
|
|
3517
3517
|
}
|
|
3518
3518
|
}
|
|
3519
3519
|
break;
|
|
3520
|
-
case '
|
|
3520
|
+
case 'phone':
|
|
3521
|
+
// Basic phone validation - allows digits, spaces, dashes, parentheses, plus
|
|
3522
|
+
const phoneRegex = /^[\d\s\-\(\)\+]+$/;
|
|
3523
|
+
if (!phoneRegex.test(stringValue)) {
|
|
3524
|
+
return { field: '', message: 'Invalid phone number format', code: 'phone' };
|
|
3525
|
+
}
|
|
3526
|
+
if (rules.minLength && stringValue.replace(/\D/g, '').length < rules.minLength) {
|
|
3527
|
+
return { field: '', message: `Phone number too short`, code: 'minLength' };
|
|
3528
|
+
}
|
|
3529
|
+
if (rules.maxLength && stringValue.length > rules.maxLength) {
|
|
3530
|
+
return { field: '', message: `Maximum ${rules.maxLength} characters allowed`, code: 'maxLength' };
|
|
3531
|
+
}
|
|
3532
|
+
break;
|
|
3533
|
+
case 'select':
|
|
3534
|
+
// Single selection - just validate it's a string (option validation done separately)
|
|
3535
|
+
if (typeof value !== 'string') {
|
|
3536
|
+
return { field: '', message: 'Must be a string', code: 'type' };
|
|
3537
|
+
}
|
|
3538
|
+
break;
|
|
3539
|
+
case 'multiselect':
|
|
3521
3540
|
if (!Array.isArray(value)) {
|
|
3522
3541
|
return { field: '', message: 'Must be an array', code: 'type' };
|
|
3523
3542
|
}
|
|
3524
3543
|
if (rules.minItems !== undefined && value.length < rules.minItems) {
|
|
3525
|
-
return { field: '', message: `
|
|
3544
|
+
return { field: '', message: `Select at least ${rules.minItems} options`, code: 'minItems' };
|
|
3526
3545
|
}
|
|
3527
3546
|
if (rules.maxItems !== undefined && value.length > rules.maxItems) {
|
|
3528
|
-
return { field: '', message: `
|
|
3547
|
+
return { field: '', message: `Select at most ${rules.maxItems} options`, code: 'maxItems' };
|
|
3548
|
+
}
|
|
3549
|
+
// Validate each item is a string
|
|
3550
|
+
if (!value.every(item => typeof item === 'string')) {
|
|
3551
|
+
return { field: '', message: 'All selections must be strings', code: 'type' };
|
|
3529
3552
|
}
|
|
3530
3553
|
break;
|
|
3531
3554
|
}
|
|
@@ -3597,75 +3620,6 @@ const validateFieldComparisons = (fieldKey, value, comparisons, allData, fieldTy
|
|
|
3597
3620
|
return errors;
|
|
3598
3621
|
};
|
|
3599
3622
|
// ============================================================================
|
|
3600
|
-
// Object Array Item Validation
|
|
3601
|
-
// ============================================================================
|
|
3602
|
-
/**
|
|
3603
|
-
* Validate each item in an object array against the object schema.
|
|
3604
|
-
* Checks required properties, type validation, and validates each property.
|
|
3605
|
-
*
|
|
3606
|
-
* @param fieldKey - Parent field key (for error reporting)
|
|
3607
|
-
* @param items - Array of objects to validate
|
|
3608
|
-
* @param schema - Schema defining expected properties
|
|
3609
|
-
* @returns Array of validation errors with item index in field path (e.g., "contacts[0].phone")
|
|
3610
|
-
*/
|
|
3611
|
-
const validateObjectArrayItems = (fieldKey, items, schema) => {
|
|
3612
|
-
const errors = [];
|
|
3613
|
-
if (!Array.isArray(items))
|
|
3614
|
-
return errors;
|
|
3615
|
-
items.forEach((item, index) => {
|
|
3616
|
-
if (typeof item !== 'object' || item === null) {
|
|
3617
|
-
errors.push({
|
|
3618
|
-
field: `${fieldKey}[${index}]`,
|
|
3619
|
-
code: 'type',
|
|
3620
|
-
message: 'Item must be an object',
|
|
3621
|
-
});
|
|
3622
|
-
return;
|
|
3623
|
-
}
|
|
3624
|
-
const obj = item;
|
|
3625
|
-
// Check each schema property
|
|
3626
|
-
for (const prop of schema) {
|
|
3627
|
-
const value = obj[prop.key];
|
|
3628
|
-
const propPath = `${fieldKey}[${index}].${prop.key}`;
|
|
3629
|
-
const isEmpty = value === null || value === undefined || value === '';
|
|
3630
|
-
// Required check for nested properties (ObjectFieldSchema.required)
|
|
3631
|
-
if (prop.required && isEmpty) {
|
|
3632
|
-
errors.push({
|
|
3633
|
-
field: propPath,
|
|
3634
|
-
code: 'required',
|
|
3635
|
-
message: `${prop.label} is required`,
|
|
3636
|
-
});
|
|
3637
|
-
continue;
|
|
3638
|
-
}
|
|
3639
|
-
if (isEmpty)
|
|
3640
|
-
continue;
|
|
3641
|
-
// Convert ObjectFieldSchema inline rules to FieldValidationRules
|
|
3642
|
-
const rules = {
|
|
3643
|
-
min: prop.min,
|
|
3644
|
-
max: prop.max,
|
|
3645
|
-
minLength: prop.minLength,
|
|
3646
|
-
maxLength: prop.maxLength,
|
|
3647
|
-
pattern: prop.pattern,
|
|
3648
|
-
patternMessage: prop.patternMessage,
|
|
3649
|
-
};
|
|
3650
|
-
// Validate the property value
|
|
3651
|
-
const error = validateFieldValue(value, rules, prop.fieldType);
|
|
3652
|
-
if (error) {
|
|
3653
|
-
errors.push({ ...error, field: propPath });
|
|
3654
|
-
}
|
|
3655
|
-
}
|
|
3656
|
-
// Cross-property comparisons within the same object item
|
|
3657
|
-
// e.g., checkOutDate > checkInDate within each reservation
|
|
3658
|
-
for (const prop of schema) {
|
|
3659
|
-
const value = obj[prop.key];
|
|
3660
|
-
if (value === null || value === undefined || value === '')
|
|
3661
|
-
continue;
|
|
3662
|
-
// ObjectFieldSchema doesn't currently have comparisons, but we could
|
|
3663
|
-
// validate date fields against 'now' using minDate/maxDate if we extend the schema
|
|
3664
|
-
}
|
|
3665
|
-
});
|
|
3666
|
-
return errors;
|
|
3667
|
-
};
|
|
3668
|
-
// ============================================================================
|
|
3669
3623
|
// Full Data Validation
|
|
3670
3624
|
// ============================================================================
|
|
3671
3625
|
/**
|
|
@@ -3693,11 +3647,6 @@ const validateAllFields = (data, definitions) => {
|
|
|
3693
3647
|
const comparisonErrors = validateFieldComparisons(key, value, def.validation.comparisons, data, def.fieldType);
|
|
3694
3648
|
errors.push(...comparisonErrors);
|
|
3695
3649
|
}
|
|
3696
|
-
// Object array item validation (validate each item's properties against objectSchema)
|
|
3697
|
-
if (def.fieldType === 'objectArray' && def.objectSchema && Array.isArray(value)) {
|
|
3698
|
-
const itemErrors = validateObjectArrayItems(key, value, def.objectSchema);
|
|
3699
|
-
errors.push(...itemErrors);
|
|
3700
|
-
}
|
|
3701
3650
|
}
|
|
3702
3651
|
return errors;
|
|
3703
3652
|
};
|
|
@@ -3780,7 +3729,6 @@ const getBuiltInFieldDefinition = (field) => {
|
|
|
3780
3729
|
label: metadata.label,
|
|
3781
3730
|
description: metadata.description,
|
|
3782
3731
|
fieldType: metadata.fieldType,
|
|
3783
|
-
objectSchema: null,
|
|
3784
3732
|
selectOptions: null,
|
|
3785
3733
|
selectOptionsSource: null,
|
|
3786
3734
|
validation: null,
|
|
@@ -3808,6 +3756,14 @@ const getAllBuiltInFieldDefinitions = () => {
|
|
|
3808
3756
|
const customFieldEntityTypesArray = ['user', 'business', 'campaign', 'redemption'];
|
|
3809
3757
|
/** All supported custom field entity types for iteration/validation */
|
|
3810
3758
|
const customFieldEntityTypes = [...customFieldEntityTypesArray];
|
|
3759
|
+
/**
|
|
3760
|
+
* Entity types available for select options source (includes static sources)
|
|
3761
|
+
* - Database entities: user, business, campaign, redemption
|
|
3762
|
+
* - Static sources: country (ISO 3166-1 country list)
|
|
3763
|
+
*/
|
|
3764
|
+
const selectOptionsSourceEntityArray = [...customFieldEntityTypesArray, 'country'];
|
|
3765
|
+
/** All supported select options source entities for iteration/validation */
|
|
3766
|
+
const selectOptionsSourceEntities = [...selectOptionsSourceEntityArray];
|
|
3811
3767
|
|
|
3812
3768
|
/**
|
|
3813
3769
|
* Core Entity Types
|
|
@@ -4123,7 +4079,8 @@ function isValidCampaignClaimRelation(value) {
|
|
|
4123
4079
|
const VALID_REDEMPTION_RELATIONS = [
|
|
4124
4080
|
'redeemCount',
|
|
4125
4081
|
'ownerBusiness',
|
|
4126
|
-
'
|
|
4082
|
+
'requiredUserInfo',
|
|
4083
|
+
'booking'
|
|
4127
4084
|
];
|
|
4128
4085
|
/**
|
|
4129
4086
|
* Type guard to validate redemption include relation at runtime
|
|
@@ -4221,6 +4178,88 @@ function normalizeFilters(filters) {
|
|
|
4221
4178
|
};
|
|
4222
4179
|
}
|
|
4223
4180
|
|
|
4181
|
+
/**
|
|
4182
|
+
* Location types for bookings.
|
|
4183
|
+
* Broad categories for different booking contexts.
|
|
4184
|
+
*/
|
|
4185
|
+
const LOCATION_TYPES = [
|
|
4186
|
+
'property', // Physical property (hotel, resort, vacation rental)
|
|
4187
|
+
'service', // Service-based booking (spa, salon, restaurant)
|
|
4188
|
+
'event', // Event/activity booking (concert, tour, class)
|
|
4189
|
+
'experience', // Experience package (multi-activity, bundle)
|
|
4190
|
+
'destination', // Destination/location-based (city pass, area access)
|
|
4191
|
+
];
|
|
4192
|
+
|
|
4193
|
+
/**
|
|
4194
|
+
* Unified data source tracking for analytics across all entities.
|
|
4195
|
+
*
|
|
4196
|
+
* Use this type to track the origin/channel of data for:
|
|
4197
|
+
* - Users (registration source)
|
|
4198
|
+
* - Claims (how the claim was initiated)
|
|
4199
|
+
* - Redeems (redemption channel)
|
|
4200
|
+
* - Bookings (booking source)
|
|
4201
|
+
* - Purchases (transaction origin)
|
|
4202
|
+
*
|
|
4203
|
+
* ## HTTP Headers for SDK/API
|
|
4204
|
+
*
|
|
4205
|
+
* | Header | Purpose | Examples |
|
|
4206
|
+
* |---------------------|----------------------------|-------------------------------------------------------|
|
|
4207
|
+
* | `x-source-channel` | How the request arrived | `web`, `mobile`, `api`, `admin`, `webhook`, `import` |
|
|
4208
|
+
* | `x-source-medium` | Marketing channel type | `email`, `referral`, `direct`, `organic`, `paid` |
|
|
4209
|
+
* | `x-source-campaign` | Campaign ID for attribution| `summer_promo_2026`, `welcome_series`, `partner_launch`|
|
|
4210
|
+
* | `x-source` | Traffic origin reference | `google`, `facebook`, `partner_xyz`, `qr_code_lobby` |
|
|
4211
|
+
*
|
|
4212
|
+
* ## Example Scenarios
|
|
4213
|
+
*
|
|
4214
|
+
* | Scenario | channel | medium | campaign | source |
|
|
4215
|
+
* |-----------------------------|-----------|------------|--------------|--------------|
|
|
4216
|
+
* | User clicks email link | `web` | `email` | `summer_promo`| `mailchimp` |
|
|
4217
|
+
* | Mobile app registration | `mobile` | `app` | - | - |
|
|
4218
|
+
* | Partner API integration | `api` | `affiliate`| - | `partner_xyz`|
|
|
4219
|
+
* | QR code scan in hotel | `mobile` | `referral` | `lobby_qr` | `qr_code` |
|
|
4220
|
+
* | Admin creates user | `admin` | - | - | - |
|
|
4221
|
+
* | Webhook from Stripe | `webhook` | - | - | `stripe` |
|
|
4222
|
+
*
|
|
4223
|
+
* @example
|
|
4224
|
+
* // Simple usage - just channel
|
|
4225
|
+
* const source: DataSource = { channel: 'web' };
|
|
4226
|
+
*
|
|
4227
|
+
* // Full attribution tracking
|
|
4228
|
+
* const source: DataSource = {
|
|
4229
|
+
* channel: 'web',
|
|
4230
|
+
* medium: 'email',
|
|
4231
|
+
* campaign: 'summer_promo_2026',
|
|
4232
|
+
* source: 'mailchimp',
|
|
4233
|
+
* referrer: 'https://email.example.com/campaign/123'
|
|
4234
|
+
* };
|
|
4235
|
+
*/
|
|
4236
|
+
/**
|
|
4237
|
+
* Primary ingestion channels for data source tracking.
|
|
4238
|
+
*/
|
|
4239
|
+
const DATA_SOURCE_CHANNELS = [
|
|
4240
|
+
'web', // Web application/browser
|
|
4241
|
+
'mobile', // Mobile app (iOS/Android)
|
|
4242
|
+
'api', // Direct API integration
|
|
4243
|
+
'admin', // Admin panel/back-office
|
|
4244
|
+
'system', // System-generated (automated)
|
|
4245
|
+
'import', // Bulk import/CSV upload
|
|
4246
|
+
'webhook', // External webhook callback
|
|
4247
|
+
'sync', // Synchronization from external system
|
|
4248
|
+
'migration', // Data migration
|
|
4249
|
+
];
|
|
4250
|
+
/**
|
|
4251
|
+
* Type guard to check if a value is a valid DataSourceChannel.
|
|
4252
|
+
*/
|
|
4253
|
+
function isDataSourceChannel(value) {
|
|
4254
|
+
return typeof value === 'string' && DATA_SOURCE_CHANNELS.includes(value);
|
|
4255
|
+
}
|
|
4256
|
+
/**
|
|
4257
|
+
* Default data source for manual/admin operations.
|
|
4258
|
+
*/
|
|
4259
|
+
const DEFAULT_DATA_SOURCE = {
|
|
4260
|
+
channel: 'admin',
|
|
4261
|
+
};
|
|
4262
|
+
|
|
4224
4263
|
/**
|
|
4225
4264
|
* Analytics Constants
|
|
4226
4265
|
*
|
|
@@ -11657,7 +11696,7 @@ class DefaultAuthProvider {
|
|
|
11657
11696
|
/** SDK package name */
|
|
11658
11697
|
const SDK_NAME = "@explorins/pers-sdk";
|
|
11659
11698
|
/** SDK version - injected from package.json at build time */
|
|
11660
|
-
const SDK_VERSION = "2.2.0-alpha.
|
|
11699
|
+
const SDK_VERSION = "2.2.0-alpha.2";
|
|
11661
11700
|
/** Full SDK identifier for headers */
|
|
11662
11701
|
const SDK_USER_AGENT = `${SDK_NAME}/${SDK_VERSION}`;
|
|
11663
11702
|
|
|
@@ -11993,6 +12032,24 @@ class PersApiClient {
|
|
|
11993
12032
|
else if (this.mergedConfig.apiProjectKey) {
|
|
11994
12033
|
headers['x-project-key'] = this.mergedConfig.apiProjectKey;
|
|
11995
12034
|
}
|
|
12035
|
+
// Add data source tracking headers
|
|
12036
|
+
const dataSource = this.mergedConfig.dataSource;
|
|
12037
|
+
if (dataSource) {
|
|
12038
|
+
headers['x-source-channel'] = dataSource.channel;
|
|
12039
|
+
if (dataSource.medium) {
|
|
12040
|
+
headers['x-source-medium'] = dataSource.medium;
|
|
12041
|
+
}
|
|
12042
|
+
if (dataSource.campaign) {
|
|
12043
|
+
headers['x-source-campaign'] = dataSource.campaign;
|
|
12044
|
+
}
|
|
12045
|
+
if (dataSource.source) {
|
|
12046
|
+
headers['x-source'] = dataSource.source;
|
|
12047
|
+
}
|
|
12048
|
+
// Note: referrer is typically set by browser, but can be overridden
|
|
12049
|
+
if (dataSource.referrer) {
|
|
12050
|
+
headers['Referer'] = dataSource.referrer;
|
|
12051
|
+
}
|
|
12052
|
+
}
|
|
11996
12053
|
return headers;
|
|
11997
12054
|
}
|
|
11998
12055
|
// ==========================================
|
|
@@ -20601,6 +20658,412 @@ class CustomFieldDefinitionManager {
|
|
|
20601
20658
|
}
|
|
20602
20659
|
}
|
|
20603
20660
|
|
|
20661
|
+
/**
|
|
20662
|
+
* Low-level API for booking operations
|
|
20663
|
+
*
|
|
20664
|
+
* Provides direct HTTP calls to the booking endpoints.
|
|
20665
|
+
* For most use cases, prefer using BookingManager or BookingService.
|
|
20666
|
+
*
|
|
20667
|
+
* @internal
|
|
20668
|
+
*/
|
|
20669
|
+
class BookingApi {
|
|
20670
|
+
constructor(apiClient) {
|
|
20671
|
+
this.apiClient = apiClient;
|
|
20672
|
+
this.basePath = '/bookings';
|
|
20673
|
+
}
|
|
20674
|
+
/**
|
|
20675
|
+
* List all bookings with optional filters
|
|
20676
|
+
*
|
|
20677
|
+
* @param options - Filter options (userId, businessId, status)
|
|
20678
|
+
* @returns Promise resolving to array of bookings
|
|
20679
|
+
*/
|
|
20680
|
+
async getAll(options) {
|
|
20681
|
+
const params = new URLSearchParams();
|
|
20682
|
+
if (options?.userId) {
|
|
20683
|
+
params.append('userId', options.userId);
|
|
20684
|
+
}
|
|
20685
|
+
if (options?.businessId) {
|
|
20686
|
+
params.append('businessId', options.businessId);
|
|
20687
|
+
}
|
|
20688
|
+
if (options?.status) {
|
|
20689
|
+
params.append('status', options.status);
|
|
20690
|
+
}
|
|
20691
|
+
const queryString = params.toString();
|
|
20692
|
+
const url = queryString ? `${this.basePath}?${queryString}` : this.basePath;
|
|
20693
|
+
return this.apiClient.get(url);
|
|
20694
|
+
}
|
|
20695
|
+
/**
|
|
20696
|
+
* Get a single booking by ID
|
|
20697
|
+
*
|
|
20698
|
+
* @param id - Booking UUID
|
|
20699
|
+
* @returns Promise resolving to booking data
|
|
20700
|
+
*/
|
|
20701
|
+
async getById(id) {
|
|
20702
|
+
return this.apiClient.get(`${this.basePath}/${id}`);
|
|
20703
|
+
}
|
|
20704
|
+
/**
|
|
20705
|
+
* Create a new booking
|
|
20706
|
+
*
|
|
20707
|
+
* @param data - Booking creation data
|
|
20708
|
+
* @returns Promise resolving to created booking
|
|
20709
|
+
*/
|
|
20710
|
+
async create(data) {
|
|
20711
|
+
return this.apiClient.post(this.basePath, data);
|
|
20712
|
+
}
|
|
20713
|
+
/**
|
|
20714
|
+
* Update an existing booking
|
|
20715
|
+
*
|
|
20716
|
+
* @param id - Booking UUID
|
|
20717
|
+
* @param data - Booking update data
|
|
20718
|
+
* @returns Promise resolving to updated booking
|
|
20719
|
+
*/
|
|
20720
|
+
async update(id, data) {
|
|
20721
|
+
return this.apiClient.put(`${this.basePath}/${id}`, data);
|
|
20722
|
+
}
|
|
20723
|
+
/**
|
|
20724
|
+
* Delete a booking
|
|
20725
|
+
*
|
|
20726
|
+
* @param id - Booking UUID
|
|
20727
|
+
* @returns Promise resolving to success status
|
|
20728
|
+
*/
|
|
20729
|
+
async delete(id) {
|
|
20730
|
+
return this.apiClient.delete(`${this.basePath}/${id}`);
|
|
20731
|
+
}
|
|
20732
|
+
}
|
|
20733
|
+
|
|
20734
|
+
/**
|
|
20735
|
+
* Booking Service - Business logic layer for booking operations
|
|
20736
|
+
*
|
|
20737
|
+
* Provides booking management functionality with validation and
|
|
20738
|
+
* business rule enforcement.
|
|
20739
|
+
*
|
|
20740
|
+
* @internal
|
|
20741
|
+
*/
|
|
20742
|
+
class BookingService {
|
|
20743
|
+
constructor(bookingApi) {
|
|
20744
|
+
this.bookingApi = bookingApi;
|
|
20745
|
+
}
|
|
20746
|
+
/**
|
|
20747
|
+
* Get all bookings with optional filters
|
|
20748
|
+
*
|
|
20749
|
+
* @param options - Filter options
|
|
20750
|
+
* @returns Promise resolving to array of bookings
|
|
20751
|
+
*/
|
|
20752
|
+
async getAll(options) {
|
|
20753
|
+
return this.bookingApi.getAll(options);
|
|
20754
|
+
}
|
|
20755
|
+
/**
|
|
20756
|
+
* Get bookings for a specific user
|
|
20757
|
+
*
|
|
20758
|
+
* @param userId - User ID to filter by
|
|
20759
|
+
* @param status - Optional status filter
|
|
20760
|
+
* @returns Promise resolving to user's bookings
|
|
20761
|
+
*/
|
|
20762
|
+
async getByUser(userId, status) {
|
|
20763
|
+
return this.bookingApi.getAll({ userId, status });
|
|
20764
|
+
}
|
|
20765
|
+
/**
|
|
20766
|
+
* Get bookings for a specific business
|
|
20767
|
+
*
|
|
20768
|
+
* @param businessId - Business ID to filter by
|
|
20769
|
+
* @param status - Optional status filter
|
|
20770
|
+
* @returns Promise resolving to business's bookings
|
|
20771
|
+
*/
|
|
20772
|
+
async getByBusiness(businessId, status) {
|
|
20773
|
+
return this.bookingApi.getAll({ businessId, status });
|
|
20774
|
+
}
|
|
20775
|
+
/**
|
|
20776
|
+
* Get a single booking by ID
|
|
20777
|
+
*
|
|
20778
|
+
* @param id - Booking UUID
|
|
20779
|
+
* @returns Promise resolving to booking data
|
|
20780
|
+
*/
|
|
20781
|
+
async getById(id) {
|
|
20782
|
+
return this.bookingApi.getById(id);
|
|
20783
|
+
}
|
|
20784
|
+
/**
|
|
20785
|
+
* Create a new booking
|
|
20786
|
+
*
|
|
20787
|
+
* @param data - Booking creation data
|
|
20788
|
+
* @returns Promise resolving to created booking
|
|
20789
|
+
*/
|
|
20790
|
+
async create(data) {
|
|
20791
|
+
// Validate check-out is after check-in
|
|
20792
|
+
if (data.checkInDate && data.checkOutDate) {
|
|
20793
|
+
const checkIn = new Date(data.checkInDate);
|
|
20794
|
+
const checkOut = new Date(data.checkOutDate);
|
|
20795
|
+
if (checkOut <= checkIn) {
|
|
20796
|
+
throw new Error('Check-out date must be after check-in date');
|
|
20797
|
+
}
|
|
20798
|
+
}
|
|
20799
|
+
return this.bookingApi.create(data);
|
|
20800
|
+
}
|
|
20801
|
+
/**
|
|
20802
|
+
* Update an existing booking
|
|
20803
|
+
*
|
|
20804
|
+
* @param id - Booking UUID
|
|
20805
|
+
* @param data - Booking update data
|
|
20806
|
+
* @returns Promise resolving to updated booking
|
|
20807
|
+
*/
|
|
20808
|
+
async update(id, data) {
|
|
20809
|
+
// Validate dates if both provided
|
|
20810
|
+
if (data.checkInDate && data.checkOutDate) {
|
|
20811
|
+
const checkIn = new Date(data.checkInDate);
|
|
20812
|
+
const checkOut = new Date(data.checkOutDate);
|
|
20813
|
+
if (checkOut <= checkIn) {
|
|
20814
|
+
throw new Error('Check-out date must be after check-in date');
|
|
20815
|
+
}
|
|
20816
|
+
}
|
|
20817
|
+
return this.bookingApi.update(id, data);
|
|
20818
|
+
}
|
|
20819
|
+
/**
|
|
20820
|
+
* Delete a booking
|
|
20821
|
+
*
|
|
20822
|
+
* @param id - Booking UUID
|
|
20823
|
+
* @returns Promise resolving to success status
|
|
20824
|
+
*/
|
|
20825
|
+
async delete(id) {
|
|
20826
|
+
return this.bookingApi.delete(id);
|
|
20827
|
+
}
|
|
20828
|
+
/**
|
|
20829
|
+
* Check if a user has an active or future booking
|
|
20830
|
+
*
|
|
20831
|
+
* Useful for eligibility checks on redemptions.
|
|
20832
|
+
*
|
|
20833
|
+
* @param userId - User ID to check
|
|
20834
|
+
* @param options - Validation options (status filter, business filter)
|
|
20835
|
+
* @returns Promise resolving to true if user has valid booking
|
|
20836
|
+
*/
|
|
20837
|
+
async hasValidBooking(userId, options) {
|
|
20838
|
+
const queryOptions = {
|
|
20839
|
+
userId,
|
|
20840
|
+
businessId: options?.businessId
|
|
20841
|
+
};
|
|
20842
|
+
// If a specific status is requested, use it
|
|
20843
|
+
if (options?.status && options.status !== 'active_future') {
|
|
20844
|
+
queryOptions.status = options.status === 'any' ? undefined : options.status;
|
|
20845
|
+
const bookings = await this.bookingApi.getAll(queryOptions);
|
|
20846
|
+
return bookings.length > 0;
|
|
20847
|
+
}
|
|
20848
|
+
// Default: check active+future (active_future or undefined)
|
|
20849
|
+
const activeBookings = await this.bookingApi.getAll({ ...queryOptions, status: 'active' });
|
|
20850
|
+
if (activeBookings.length > 0)
|
|
20851
|
+
return true;
|
|
20852
|
+
const futureBookings = await this.bookingApi.getAll({ ...queryOptions, status: 'future' });
|
|
20853
|
+
return futureBookings.length > 0;
|
|
20854
|
+
}
|
|
20855
|
+
}
|
|
20856
|
+
|
|
20857
|
+
/**
|
|
20858
|
+
* Booking Manager - High-level interface for booking operations
|
|
20859
|
+
*
|
|
20860
|
+
* Provides a simplified API for managing bookings (hotel stays, reservations, etc.)
|
|
20861
|
+
* Used for tracking user stays and eligibility checks on redemptions.
|
|
20862
|
+
*
|
|
20863
|
+
* @group Managers
|
|
20864
|
+
* @category Booking Management
|
|
20865
|
+
*
|
|
20866
|
+
* @example List Bookings
|
|
20867
|
+
* ```typescript
|
|
20868
|
+
* // Get all bookings
|
|
20869
|
+
* const allBookings = await sdk.bookings.getAll();
|
|
20870
|
+
*
|
|
20871
|
+
* // Filter by user
|
|
20872
|
+
* const userBookings = await sdk.bookings.getAll({ userId: 'user-123' });
|
|
20873
|
+
*
|
|
20874
|
+
* // Filter by status
|
|
20875
|
+
* const activeBookings = await sdk.bookings.getAll({ status: 'active' });
|
|
20876
|
+
* ```
|
|
20877
|
+
*
|
|
20878
|
+
* @example Create Booking
|
|
20879
|
+
* ```typescript
|
|
20880
|
+
* const booking = await sdk.bookings.create({
|
|
20881
|
+
* userId: 'user-123',
|
|
20882
|
+
* locationName: 'Grand Hotel',
|
|
20883
|
+
* checkInDate: '2026-06-01',
|
|
20884
|
+
* checkOutDate: '2026-06-05',
|
|
20885
|
+
* guests: 2
|
|
20886
|
+
* });
|
|
20887
|
+
* ```
|
|
20888
|
+
*
|
|
20889
|
+
* @example Check Eligibility
|
|
20890
|
+
* ```typescript
|
|
20891
|
+
* // Check if user has valid (active or future) booking
|
|
20892
|
+
* const hasBooking = await sdk.bookings.hasValidBooking('user-123');
|
|
20893
|
+
* if (hasBooking) {
|
|
20894
|
+
* // User is eligible for booking-required redemption
|
|
20895
|
+
* }
|
|
20896
|
+
* ```
|
|
20897
|
+
*/
|
|
20898
|
+
class BookingManager {
|
|
20899
|
+
constructor(apiClient, events) {
|
|
20900
|
+
this.apiClient = apiClient;
|
|
20901
|
+
this.events = events;
|
|
20902
|
+
const bookingApi = new BookingApi(apiClient);
|
|
20903
|
+
this.bookingService = new BookingService(bookingApi);
|
|
20904
|
+
}
|
|
20905
|
+
/**
|
|
20906
|
+
* Get all bookings with optional filters
|
|
20907
|
+
*
|
|
20908
|
+
* @param options - Filter options (userId, businessId, status)
|
|
20909
|
+
* @returns Promise resolving to array of bookings
|
|
20910
|
+
*
|
|
20911
|
+
* @example Basic Usage
|
|
20912
|
+
* ```typescript
|
|
20913
|
+
* // All bookings
|
|
20914
|
+
* const bookings = await sdk.bookings.getAll();
|
|
20915
|
+
*
|
|
20916
|
+
* // Filter by user
|
|
20917
|
+
* const userBookings = await sdk.bookings.getAll({ userId: 'user-123' });
|
|
20918
|
+
*
|
|
20919
|
+
* // Filter by status
|
|
20920
|
+
* const activeBookings = await sdk.bookings.getAll({ status: 'active' });
|
|
20921
|
+
* const futureBookings = await sdk.bookings.getAll({ status: 'future' });
|
|
20922
|
+
* const pastBookings = await sdk.bookings.getAll({ status: 'past' });
|
|
20923
|
+
* ```
|
|
20924
|
+
*/
|
|
20925
|
+
async getAll(options) {
|
|
20926
|
+
return this.bookingService.getAll(options);
|
|
20927
|
+
}
|
|
20928
|
+
/**
|
|
20929
|
+
* Get bookings for a specific user
|
|
20930
|
+
*
|
|
20931
|
+
* Convenience method for filtering bookings by user ID.
|
|
20932
|
+
*
|
|
20933
|
+
* @param userId - User ID to filter by
|
|
20934
|
+
* @param status - Optional status filter
|
|
20935
|
+
* @returns Promise resolving to user's bookings
|
|
20936
|
+
*/
|
|
20937
|
+
async getByUser(userId, status) {
|
|
20938
|
+
return this.bookingService.getByUser(userId, status);
|
|
20939
|
+
}
|
|
20940
|
+
/**
|
|
20941
|
+
* Get bookings for a specific business
|
|
20942
|
+
*
|
|
20943
|
+
* Convenience method for filtering bookings by business ID.
|
|
20944
|
+
*
|
|
20945
|
+
* @param businessId - Business ID to filter by
|
|
20946
|
+
* @param status - Optional status filter
|
|
20947
|
+
* @returns Promise resolving to business's bookings
|
|
20948
|
+
*/
|
|
20949
|
+
async getByBusiness(businessId, status) {
|
|
20950
|
+
return this.bookingService.getByBusiness(businessId, status);
|
|
20951
|
+
}
|
|
20952
|
+
/**
|
|
20953
|
+
* Get a single booking by ID
|
|
20954
|
+
*
|
|
20955
|
+
* @param id - Booking UUID
|
|
20956
|
+
* @returns Promise resolving to booking data
|
|
20957
|
+
* @throws {PersApiError} When booking not found
|
|
20958
|
+
*/
|
|
20959
|
+
async getById(id) {
|
|
20960
|
+
return this.bookingService.getById(id);
|
|
20961
|
+
}
|
|
20962
|
+
/**
|
|
20963
|
+
* Create a new booking
|
|
20964
|
+
*
|
|
20965
|
+
* Creates a booking for a user at a specific location. Used for tracking
|
|
20966
|
+
* hotel stays, reservations, etc. for eligibility checks.
|
|
20967
|
+
*
|
|
20968
|
+
* @param data - Booking creation data
|
|
20969
|
+
* @returns Promise resolving to created booking
|
|
20970
|
+
* @throws {Error} When check-out date is not after check-in date
|
|
20971
|
+
*
|
|
20972
|
+
* @example Create Hotel Booking
|
|
20973
|
+
* ```typescript
|
|
20974
|
+
* const booking = await sdk.bookings.create({
|
|
20975
|
+
* userId: 'user-123',
|
|
20976
|
+
* locationName: 'Grand Hotel',
|
|
20977
|
+
* locationType: 'hotel',
|
|
20978
|
+
* checkInDate: '2026-06-01',
|
|
20979
|
+
* checkOutDate: '2026-06-05',
|
|
20980
|
+
* confirmationNumber: 'CONF123',
|
|
20981
|
+
* guests: 2,
|
|
20982
|
+
* notes: 'Ocean view room'
|
|
20983
|
+
* });
|
|
20984
|
+
* ```
|
|
20985
|
+
*/
|
|
20986
|
+
async create(data) {
|
|
20987
|
+
const result = await this.bookingService.create(data);
|
|
20988
|
+
// TODO: Add event emission when 'booking' domain is added to pers-shared
|
|
20989
|
+
// this.events?.emitSuccess({ domain: 'booking', type: 'booking_created', ... });
|
|
20990
|
+
return result;
|
|
20991
|
+
}
|
|
20992
|
+
/**
|
|
20993
|
+
* Update an existing booking
|
|
20994
|
+
*
|
|
20995
|
+
* @param id - Booking UUID
|
|
20996
|
+
* @param data - Booking update data (partial update supported)
|
|
20997
|
+
* @returns Promise resolving to updated booking
|
|
20998
|
+
* @throws {PersApiError} When booking not found
|
|
20999
|
+
* @throws {Error} When check-out date is not after check-in date
|
|
21000
|
+
*
|
|
21001
|
+
* @example Update Booking Dates
|
|
21002
|
+
* ```typescript
|
|
21003
|
+
* const updated = await sdk.bookings.update('booking-123', {
|
|
21004
|
+
* checkInDate: '2026-06-02',
|
|
21005
|
+
* checkOutDate: '2026-06-07'
|
|
21006
|
+
* });
|
|
21007
|
+
* ```
|
|
21008
|
+
*/
|
|
21009
|
+
async update(id, data) {
|
|
21010
|
+
const result = await this.bookingService.update(id, data);
|
|
21011
|
+
// TODO: Add event emission when 'booking' domain is added to pers-shared
|
|
21012
|
+
// this.events?.emitSuccess({ domain: 'booking', type: 'booking_updated', ... });
|
|
21013
|
+
return result;
|
|
21014
|
+
}
|
|
21015
|
+
/**
|
|
21016
|
+
* Delete a booking
|
|
21017
|
+
*
|
|
21018
|
+
* @param id - Booking UUID
|
|
21019
|
+
* @returns Promise resolving to success status
|
|
21020
|
+
* @throws {PersApiError} When booking not found
|
|
21021
|
+
*/
|
|
21022
|
+
async delete(id) {
|
|
21023
|
+
const result = await this.bookingService.delete(id);
|
|
21024
|
+
// TODO: Add event emission when 'booking' domain is added to pers-shared
|
|
21025
|
+
// this.events?.emitSuccess({ domain: 'booking', type: 'booking_deleted', ... });
|
|
21026
|
+
return result;
|
|
21027
|
+
}
|
|
21028
|
+
/**
|
|
21029
|
+
* Check if a user has a valid (active or future) booking
|
|
21030
|
+
*
|
|
21031
|
+
* Useful for eligibility checks on redemptions that require a valid booking.
|
|
21032
|
+
*
|
|
21033
|
+
* @param userId - User ID to check
|
|
21034
|
+
* @param options - Validation options (status filter, business filter)
|
|
21035
|
+
* @returns Promise resolving to true if user has valid booking
|
|
21036
|
+
*
|
|
21037
|
+
* @example Eligibility Check
|
|
21038
|
+
* ```typescript
|
|
21039
|
+
* const hasBooking = await sdk.bookings.hasValidBooking('user-123');
|
|
21040
|
+
*
|
|
21041
|
+
* if (!hasBooking) {
|
|
21042
|
+
* console.log('User must have an active or upcoming booking');
|
|
21043
|
+
* }
|
|
21044
|
+
*
|
|
21045
|
+
* // Check for active booking only
|
|
21046
|
+
* const hasActive = await sdk.bookings.hasValidBooking('user-123', { status: 'active' });
|
|
21047
|
+
*
|
|
21048
|
+
* // Check for booking at specific business
|
|
21049
|
+
* const hasHotelBooking = await sdk.bookings.hasValidBooking('user-123', {
|
|
21050
|
+
* businessId: 'hotel-123'
|
|
21051
|
+
* });
|
|
21052
|
+
* ```
|
|
21053
|
+
*/
|
|
21054
|
+
async hasValidBooking(userId, options) {
|
|
21055
|
+
return this.bookingService.hasValidBooking(userId, options);
|
|
21056
|
+
}
|
|
21057
|
+
/**
|
|
21058
|
+
* Get the booking service for advanced operations
|
|
21059
|
+
*
|
|
21060
|
+
* @returns BookingService instance with full API access
|
|
21061
|
+
*/
|
|
21062
|
+
getBookingService() {
|
|
21063
|
+
return this.bookingService;
|
|
21064
|
+
}
|
|
21065
|
+
}
|
|
21066
|
+
|
|
20604
21067
|
/**
|
|
20605
21068
|
* @fileoverview PERS SDK - Platform-agnostic TypeScript SDK with High-Level Managers
|
|
20606
21069
|
*
|
|
@@ -21320,6 +21783,47 @@ class PersSDK {
|
|
|
21320
21783
|
}
|
|
21321
21784
|
return this._customFields;
|
|
21322
21785
|
}
|
|
21786
|
+
/**
|
|
21787
|
+
* Booking manager - High-level booking operations
|
|
21788
|
+
*
|
|
21789
|
+
* Provides methods for managing user bookings (hotel stays, reservations, etc.)
|
|
21790
|
+
* Used for eligibility checks on redemptions that require valid bookings.
|
|
21791
|
+
*
|
|
21792
|
+
* @returns BookingManager instance
|
|
21793
|
+
*
|
|
21794
|
+
* @example List User Bookings
|
|
21795
|
+
* ```typescript
|
|
21796
|
+
* // Get all bookings for a user
|
|
21797
|
+
* const bookings = await sdk.bookings.getByUser('user-123');
|
|
21798
|
+
*
|
|
21799
|
+
* // Filter by status
|
|
21800
|
+
* const activeBookings = await sdk.bookings.getAll({ status: 'active' });
|
|
21801
|
+
* ```
|
|
21802
|
+
*
|
|
21803
|
+
* @example Create Booking
|
|
21804
|
+
* ```typescript
|
|
21805
|
+
* const booking = await sdk.bookings.create({
|
|
21806
|
+
* userId: 'user-123',
|
|
21807
|
+
* locationName: 'Grand Hotel',
|
|
21808
|
+
* checkInDate: '2026-06-01',
|
|
21809
|
+
* checkOutDate: '2026-06-05'
|
|
21810
|
+
* });
|
|
21811
|
+
* ```
|
|
21812
|
+
*
|
|
21813
|
+
* @example Eligibility Check
|
|
21814
|
+
* ```typescript
|
|
21815
|
+
* const hasBooking = await sdk.bookings.hasValidBooking('user-123');
|
|
21816
|
+
* if (hasBooking) {
|
|
21817
|
+
* // User is eligible for booking-required redemption
|
|
21818
|
+
* }
|
|
21819
|
+
* ```
|
|
21820
|
+
*/
|
|
21821
|
+
get bookings() {
|
|
21822
|
+
if (!this._bookings) {
|
|
21823
|
+
this._bookings = new BookingManager(this.apiClient, this._events);
|
|
21824
|
+
}
|
|
21825
|
+
return this._bookings;
|
|
21826
|
+
}
|
|
21323
21827
|
/**
|
|
21324
21828
|
* Wallet Events Manager - Real-time blockchain events for user's wallets
|
|
21325
21829
|
*
|
|
@@ -48896,6 +49400,9 @@ exports.AuthService = AuthService;
|
|
|
48896
49400
|
exports.AuthTokenManager = AuthTokenManager;
|
|
48897
49401
|
exports.AuthenticationError = AuthenticationError;
|
|
48898
49402
|
exports.BaseTokenService = BaseTokenService;
|
|
49403
|
+
exports.BookingApi = BookingApi;
|
|
49404
|
+
exports.BookingManager = BookingManager;
|
|
49405
|
+
exports.BookingService = BookingService;
|
|
48899
49406
|
exports.BrowserFetchClientAdapter = BrowserFetchClientAdapter;
|
|
48900
49407
|
exports.BusinessApi = BusinessApi;
|
|
48901
49408
|
exports.BusinessManager = BusinessManager;
|
|
@@ -48911,6 +49418,8 @@ exports.ChainTypes = ChainTypes;
|
|
|
48911
49418
|
exports.ClientTransactionType = ClientTransactionType;
|
|
48912
49419
|
exports.CommonErrorCodes = CommonErrorCodes;
|
|
48913
49420
|
exports.CustomFieldDefinitionManager = CustomFieldDefinitionManager;
|
|
49421
|
+
exports.DATA_SOURCE_CHANNELS = DATA_SOURCE_CHANNELS;
|
|
49422
|
+
exports.DEFAULT_DATA_SOURCE = DEFAULT_DATA_SOURCE;
|
|
48914
49423
|
exports.DEFAULT_PERS_CONFIG = DEFAULT_PERS_CONFIG;
|
|
48915
49424
|
exports.DPOP_STORAGE_KEYS = DPOP_STORAGE_KEYS;
|
|
48916
49425
|
exports.DPoPManager = DPoPManager;
|
|
@@ -48938,6 +49447,7 @@ exports.IMAGE_CDN_MAX_INPUT_SIZE = IMAGE_CDN_MAX_INPUT_SIZE;
|
|
|
48938
49447
|
exports.IMAGE_CDN_PRESETS = IMAGE_CDN_PRESETS;
|
|
48939
49448
|
exports.IMAGE_CDN_SUPPORTED_FORMATS = IMAGE_CDN_SUPPORTED_FORMATS;
|
|
48940
49449
|
exports.IndexedDBTokenStorage = IndexedDBTokenStorage;
|
|
49450
|
+
exports.LOCATION_TYPES = LOCATION_TYPES;
|
|
48941
49451
|
exports.LocalStorageTokenStorage = LocalStorageTokenStorage;
|
|
48942
49452
|
exports.LogoutRequired = LogoutRequired;
|
|
48943
49453
|
exports.MEMBERSHIP_ROLE_HIERARCHY = MEMBERSHIP_ROLE_HIERARCHY;
|
|
@@ -49054,6 +49564,7 @@ exports.hasMinimumRole = hasMinimumRole;
|
|
|
49054
49564
|
exports.initializeReactNativePolyfills = initializeReactNativePolyfills;
|
|
49055
49565
|
exports.isBuiltInUserField = isBuiltInUserField;
|
|
49056
49566
|
exports.isConnectedMessage = isConnectedMessage;
|
|
49567
|
+
exports.isDataSourceChannel = isDataSourceChannel;
|
|
49057
49568
|
exports.isErrorMessage = isErrorMessage;
|
|
49058
49569
|
exports.isEventMessage = isEventMessage;
|
|
49059
49570
|
exports.isFatalAuthErrorInMessage = isFatalAuthErrorInMessage;
|
|
@@ -49088,6 +49599,7 @@ exports.normalizeFilters = normalizeFilters;
|
|
|
49088
49599
|
exports.normalizeToPaginated = normalizeToPaginated;
|
|
49089
49600
|
exports.operatorText = operatorText;
|
|
49090
49601
|
exports.registerIdentifierType = registerIdentifierType;
|
|
49602
|
+
exports.selectOptionsSourceEntities = selectOptionsSourceEntities;
|
|
49091
49603
|
exports.testnetPrefix = testnetPrefix;
|
|
49092
49604
|
exports.testnetShortPrefix = testnetShortPrefix;
|
|
49093
49605
|
exports.uniqueUserIdentifierKeys = uniqueUserIdentifierKeys;
|
|
@@ -49118,6 +49630,5 @@ exports.userModelFields = userModelFields;
|
|
|
49118
49630
|
exports.validateAllFields = validateAllFields;
|
|
49119
49631
|
exports.validateFieldComparisons = validateFieldComparisons;
|
|
49120
49632
|
exports.validateFieldValue = validateFieldValue;
|
|
49121
|
-
exports.validateObjectArrayItems = validateObjectArrayItems;
|
|
49122
49633
|
exports.warnIfProblematicEnvironment = warnIfProblematicEnvironment;
|
|
49123
49634
|
//# sourceMappingURL=index.js.map
|