@explorins/pers-sdk 2.1.42 → 2.2.0-alpha.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.
Files changed (41) hide show
  1. package/dist/booking/api/booking-api.d.ts +71 -0
  2. package/dist/booking/api/booking-api.d.ts.map +1 -0
  3. package/dist/booking/index.d.ts +9 -0
  4. package/dist/booking/index.d.ts.map +1 -0
  5. package/dist/booking/services/booking-service.d.ts +79 -0
  6. package/dist/booking/services/booking-service.d.ts.map +1 -0
  7. package/dist/chunks/{pers-sdk-DxYmXQcW.js → pers-sdk-C01Z2nmP.js} +923 -4
  8. package/dist/chunks/pers-sdk-C01Z2nmP.js.map +1 -0
  9. package/dist/chunks/{pers-sdk-DeFxjuRB.cjs → pers-sdk-DQ6uC3h0.cjs} +925 -2
  10. package/dist/chunks/pers-sdk-DQ6uC3h0.cjs.map +1 -0
  11. package/dist/core/events/event-types.d.ts +5 -1
  12. package/dist/core/events/event-types.d.ts.map +1 -1
  13. package/dist/core/pers-api-client.d.ts.map +1 -1
  14. package/dist/core/pers-config.d.ts +30 -0
  15. package/dist/core/pers-config.d.ts.map +1 -1
  16. package/dist/core.cjs +3 -1
  17. package/dist/core.cjs.map +1 -1
  18. package/dist/core.js +1 -1
  19. package/dist/custom-field/api/custom-field-api.d.ts +58 -0
  20. package/dist/custom-field/api/custom-field-api.d.ts.map +1 -0
  21. package/dist/custom-field/index.d.ts +2 -0
  22. package/dist/custom-field/index.d.ts.map +1 -0
  23. package/dist/index.cjs +5 -1
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.d.ts +1 -0
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +1 -1
  28. package/dist/managers/booking-manager.d.ts +188 -0
  29. package/dist/managers/booking-manager.d.ts.map +1 -0
  30. package/dist/managers/custom-field-definition-manager.d.ts +317 -0
  31. package/dist/managers/custom-field-definition-manager.d.ts.map +1 -0
  32. package/dist/managers/index.d.ts +2 -0
  33. package/dist/managers/index.d.ts.map +1 -1
  34. package/dist/node.cjs +1 -1
  35. package/dist/node.js +1 -1
  36. package/dist/package.json +2 -2
  37. package/dist/pers-sdk.d.ts +65 -1
  38. package/dist/pers-sdk.d.ts.map +1 -1
  39. package/package.json +2 -2
  40. package/dist/chunks/pers-sdk-DeFxjuRB.cjs.map +0 -1
  41. package/dist/chunks/pers-sdk-DxYmXQcW.js.map +0 -1
@@ -1587,7 +1587,7 @@ class DefaultAuthProvider {
1587
1587
  /** SDK package name */
1588
1588
  const SDK_NAME = "@explorins/pers-sdk";
1589
1589
  /** SDK version - injected from package.json at build time */
1590
- const SDK_VERSION = "2.1.42";
1590
+ const SDK_VERSION = "2.2.0-alpha.2";
1591
1591
  /** Full SDK identifier for headers */
1592
1592
  const SDK_USER_AGENT = `${SDK_NAME}/${SDK_VERSION}`;
1593
1593
 
@@ -1923,6 +1923,24 @@ class PersApiClient {
1923
1923
  else if (this.mergedConfig.apiProjectKey) {
1924
1924
  headers['x-project-key'] = this.mergedConfig.apiProjectKey;
1925
1925
  }
1926
+ // Add data source tracking headers
1927
+ const dataSource = this.mergedConfig.dataSource;
1928
+ if (dataSource) {
1929
+ headers['x-source-channel'] = dataSource.channel;
1930
+ if (dataSource.medium) {
1931
+ headers['x-source-medium'] = dataSource.medium;
1932
+ }
1933
+ if (dataSource.campaign) {
1934
+ headers['x-source-campaign'] = dataSource.campaign;
1935
+ }
1936
+ if (dataSource.source) {
1937
+ headers['x-source'] = dataSource.source;
1938
+ }
1939
+ // Note: referrer is typically set by browser, but can be overridden
1940
+ if (dataSource.referrer) {
1941
+ headers['Referer'] = dataSource.referrer;
1942
+ }
1943
+ }
1926
1944
  return headers;
1927
1945
  }
1928
1946
  // ==========================================
@@ -10108,6 +10126,835 @@ class WalletEventsManager {
10108
10126
  }
10109
10127
  }
10110
10128
 
10129
+ /**
10130
+ * Custom Field Definitions API
10131
+ *
10132
+ * Low-level API for custom field definition CRUD operations.
10133
+ * Used internally by CustomFieldDefinitionManager.
10134
+ *
10135
+ * @internal
10136
+ */
10137
+ class CustomFieldApi {
10138
+ constructor(apiClient) {
10139
+ this.apiClient = apiClient;
10140
+ this.basePath = '/custom-field-definitions';
10141
+ }
10142
+ /**
10143
+ * List all custom field definitions
10144
+ * Backend returns non-paginated array sorted by sortOrder
10145
+ */
10146
+ async getDefinitions(entityType) {
10147
+ const params = new URLSearchParams();
10148
+ if (entityType) {
10149
+ params.append('entityType', entityType);
10150
+ }
10151
+ const query = params.toString();
10152
+ const url = query ? `${this.basePath}?${query}` : this.basePath;
10153
+ return this.apiClient.get(url);
10154
+ }
10155
+ /**
10156
+ * Get a single custom field definition by ID
10157
+ */
10158
+ async getDefinition(id) {
10159
+ return this.apiClient.get(`${this.basePath}/${id}`);
10160
+ }
10161
+ /**
10162
+ * Create a new custom field definition
10163
+ */
10164
+ async createDefinition(data) {
10165
+ return this.apiClient.post(this.basePath, data);
10166
+ }
10167
+ /**
10168
+ * Update an existing custom field definition
10169
+ */
10170
+ async updateDefinition(id, data) {
10171
+ return this.apiClient.put(`${this.basePath}/${id}`, data);
10172
+ }
10173
+ /**
10174
+ * Delete a custom field definition
10175
+ */
10176
+ async deleteDefinition(id) {
10177
+ return this.apiClient.delete(`${this.basePath}/${id}`);
10178
+ }
10179
+ /**
10180
+ * Get usage count for a field definition
10181
+ */
10182
+ async getUsages(id) {
10183
+ return this.apiClient.get(`${this.basePath}/${id}/usages`);
10184
+ }
10185
+ /**
10186
+ * Resolve dynamic select options for a field
10187
+ */
10188
+ async resolveSelectOptions(id) {
10189
+ return this.apiClient.get(`${this.basePath}/${id}/options`);
10190
+ }
10191
+ }
10192
+
10193
+ /**
10194
+ * Custom Field Definition Manager - Manage tenant-specific custom fields
10195
+ *
10196
+ * Provides CRUD operations for custom field definitions that extend the built-in
10197
+ * user profile fields. Custom fields are defined per-tenant and can be used for
10198
+ * additional user data collection, redemption requirements, and form validation.
10199
+ *
10200
+ * @group Managers
10201
+ * @category Custom Fields
10202
+ *
10203
+ * @example Basic Operations
10204
+ * ```typescript
10205
+ * // List all user custom fields
10206
+ * const fields = await sdk.customFields.getDefinitions();
10207
+ *
10208
+ * // Create a new custom field
10209
+ * const field = await sdk.customFields.createDefinition({
10210
+ * key: 'employee_id',
10211
+ * label: 'Employee ID',
10212
+ * fieldType: 'text',
10213
+ * validation: { required: true, pattern: '^E[0-9]{5}$' }
10214
+ * });
10215
+ *
10216
+ * // Update a field
10217
+ * await sdk.customFields.updateDefinition(field.id, {
10218
+ * label: 'Company Employee ID'
10219
+ * });
10220
+ *
10221
+ * // Delete a field
10222
+ * await sdk.customFields.deleteDefinition(field.id);
10223
+ * ```
10224
+ *
10225
+ * @example Validation
10226
+ * ```typescript
10227
+ * // Validate user custom data against definitions
10228
+ * const definitions = await sdk.customFields.getDefinitions();
10229
+ * const errors = sdk.customFields.validateUserData(
10230
+ * { employee_id: 'INVALID' },
10231
+ * definitions
10232
+ * );
10233
+ * if (errors.length > 0) {
10234
+ * console.log('Validation errors:', errors);
10235
+ * }
10236
+ * ```
10237
+ */
10238
+ class CustomFieldDefinitionManager {
10239
+ constructor(apiClient, events) {
10240
+ this.events = events;
10241
+ this.api = new CustomFieldApi(apiClient);
10242
+ }
10243
+ /**
10244
+ * List all custom field definitions for the tenant
10245
+ *
10246
+ * Retrieves all custom field definitions, optionally filtered by entity type.
10247
+ * Results are sorted by sortOrder (ascending).
10248
+ *
10249
+ * @param options - Query options including entity type filter
10250
+ * @returns Array of custom field definitions
10251
+ *
10252
+ * @example List All User Fields
10253
+ * ```typescript
10254
+ * const fields = await sdk.customFields.getDefinitions();
10255
+ * console.log(`Found ${fields.length} custom fields`);
10256
+ *
10257
+ * fields.forEach(field => {
10258
+ * console.log(`${field.key}: ${field.label} (${field.fieldType})`);
10259
+ * });
10260
+ * ```
10261
+ *
10262
+ * @example Filter by Entity Type
10263
+ * ```typescript
10264
+ * // Get only business custom fields
10265
+ * const businessFields = await sdk.customFields.getDefinitions({
10266
+ * entityType: 'business'
10267
+ * });
10268
+ * ```
10269
+ */
10270
+ async getDefinitions(options) {
10271
+ return this.api.getDefinitions(options?.entityType);
10272
+ }
10273
+ /**
10274
+ * Get a single custom field definition by ID
10275
+ *
10276
+ * @param id - The UUID of the custom field definition
10277
+ * @returns The custom field definition
10278
+ * @throws {PersApiError} When definition not found (404)
10279
+ *
10280
+ * @example
10281
+ * ```typescript
10282
+ * try {
10283
+ * const field = await sdk.customFields.getDefinition('uuid-here');
10284
+ * console.log('Field:', field.label);
10285
+ * } catch (error) {
10286
+ * if (error.statusCode === 404) {
10287
+ * console.log('Field not found');
10288
+ * }
10289
+ * }
10290
+ * ```
10291
+ */
10292
+ async getDefinition(id) {
10293
+ return this.api.getDefinition(id);
10294
+ }
10295
+ /**
10296
+ * Create a new custom field definition
10297
+ *
10298
+ * Creates a new custom field for the tenant. The field key must be unique
10299
+ * within the tenant and entity type combination.
10300
+ *
10301
+ * @param data - The field definition data
10302
+ * @returns The created custom field definition
10303
+ * @throws {PersApiError} When key already exists (409) or validation fails (400)
10304
+ *
10305
+ * @example Text Field with Pattern
10306
+ * ```typescript
10307
+ * const employeeIdField = await sdk.customFields.createDefinition({
10308
+ * key: 'employee_id',
10309
+ * label: 'Employee ID',
10310
+ * description: 'Your company employee ID (E + 5 digits)',
10311
+ * fieldType: 'text',
10312
+ * validation: {
10313
+ * required: true,
10314
+ * pattern: '^E[0-9]{5}$',
10315
+ * patternMessage: 'Must be E followed by 5 digits'
10316
+ * },
10317
+ * sortOrder: 1
10318
+ * });
10319
+ * ```
10320
+ *
10321
+ * @example Date Field with Comparison
10322
+ * ```typescript
10323
+ * const checkOutField = await sdk.customFields.createDefinition({
10324
+ * key: 'check_out',
10325
+ * label: 'Check-out Date',
10326
+ * fieldType: 'date',
10327
+ * validation: {
10328
+ * required: true,
10329
+ * comparisons: [
10330
+ * { field: 'check_in', operator: '>', message: 'Must be after check-in' }
10331
+ * ]
10332
+ * },
10333
+ * sortOrder: 2
10334
+ * });
10335
+ * ```
10336
+ *
10337
+ * @example Select Field with Static Options
10338
+ * ```typescript
10339
+ * const departmentField = await sdk.customFields.createDefinition({
10340
+ * key: 'department',
10341
+ * label: 'Department',
10342
+ * fieldType: 'select',
10343
+ * selectOptions: [
10344
+ * { value: 'engineering', label: 'Engineering' },
10345
+ * { value: 'sales', label: 'Sales' },
10346
+ * { value: 'hr', label: 'Human Resources' }
10347
+ * ],
10348
+ * validation: { required: true }
10349
+ * });
10350
+ * ```
10351
+ *
10352
+ * @example Select Field with Dynamic Options
10353
+ * ```typescript
10354
+ * const hotelField = await sdk.customFields.createDefinition({
10355
+ * key: 'preferred_hotel',
10356
+ * label: 'Preferred Hotel',
10357
+ * fieldType: 'select',
10358
+ * selectOptionsSource: {
10359
+ * entity: 'business',
10360
+ * valueField: 'id',
10361
+ * labelField: 'name',
10362
+ * filter: { tags: ['hotel'], isActive: true }
10363
+ * },
10364
+ * validation: { required: true }
10365
+ * });
10366
+ * ```
10367
+ */
10368
+ async createDefinition(data) {
10369
+ const result = await this.api.createDefinition(data);
10370
+ // Using 'user' domain until pers-shared adds 'customField' domain
10371
+ this.events?.emitSuccess({
10372
+ domain: 'user',
10373
+ type: 'definition_created',
10374
+ userMessage: `Custom field "${result.label}" created`,
10375
+ details: { id: result.id, key: result.key }
10376
+ });
10377
+ return result;
10378
+ }
10379
+ /**
10380
+ * Update an existing custom field definition
10381
+ *
10382
+ * Updates a custom field definition. Note that key and entityType cannot
10383
+ * be changed after creation.
10384
+ *
10385
+ * @param id - The UUID of the custom field definition to update
10386
+ * @param data - The fields to update (partial update supported)
10387
+ * @returns The updated custom field definition
10388
+ * @throws {PersApiError} When definition not found (404) or validation fails (400)
10389
+ *
10390
+ * @example Update Label and Validation
10391
+ * ```typescript
10392
+ * const updated = await sdk.customFields.updateDefinition('uuid-here', {
10393
+ * label: 'Employee ID (Required)',
10394
+ * validation: {
10395
+ * required: true,
10396
+ * minLength: 6,
10397
+ * maxLength: 6
10398
+ * }
10399
+ * });
10400
+ * ```
10401
+ *
10402
+ * @example Update Sort Order
10403
+ * ```typescript
10404
+ * await sdk.customFields.updateDefinition('uuid-here', {
10405
+ * sortOrder: 5
10406
+ * });
10407
+ * ```
10408
+ */
10409
+ async updateDefinition(id, data) {
10410
+ const result = await this.api.updateDefinition(id, data);
10411
+ // Using 'user' domain until pers-shared adds 'customField' domain
10412
+ this.events?.emitSuccess({
10413
+ domain: 'user',
10414
+ type: 'definition_updated',
10415
+ userMessage: `Custom field "${result.label}" updated`,
10416
+ details: { id: result.id, key: result.key }
10417
+ });
10418
+ return result;
10419
+ }
10420
+ /**
10421
+ * Delete a custom field definition (soft delete)
10422
+ *
10423
+ * Soft deletes a custom field definition. Existing user data in customData
10424
+ * is preserved, but the field will no longer appear in forms or validation.
10425
+ *
10426
+ * ⚠️ Consider the impact on existing user data before deleting.
10427
+ *
10428
+ * @param id - The UUID of the custom field definition to delete
10429
+ * @throws {PersApiError} When definition not found (404)
10430
+ *
10431
+ * @example
10432
+ * ```typescript
10433
+ * try {
10434
+ * await sdk.customFields.deleteDefinition('uuid-here');
10435
+ * console.log('Field deleted');
10436
+ * } catch (error) {
10437
+ * console.log('Failed to delete:', error.message);
10438
+ * }
10439
+ * ```
10440
+ */
10441
+ async deleteDefinition(id) {
10442
+ await this.api.deleteDefinition(id);
10443
+ // Using 'user' domain until pers-shared adds 'customField' domain
10444
+ this.events?.emitSuccess({
10445
+ domain: 'user',
10446
+ type: 'definition_deleted',
10447
+ userMessage: 'Custom field deleted',
10448
+ details: { id }
10449
+ });
10450
+ }
10451
+ /**
10452
+ * Resolve dynamic select options for a field
10453
+ *
10454
+ * For fields with selectOptionsSource (dynamic options from entities),
10455
+ * this resolves the actual options by querying the source entity.
10456
+ *
10457
+ * @param id - The UUID of the custom field definition
10458
+ * @returns Array of select options
10459
+ *
10460
+ * @example
10461
+ * ```typescript
10462
+ * const field = await sdk.customFields.getDefinition('hotel-field-id');
10463
+ *
10464
+ * if (field.selectOptionsSource) {
10465
+ * // Options come from entity - need to resolve
10466
+ * const options = await sdk.customFields.resolveSelectOptions(field.id);
10467
+ * console.log('Available hotels:', options);
10468
+ * } else {
10469
+ * // Static options
10470
+ * console.log('Options:', field.selectOptions);
10471
+ * }
10472
+ * ```
10473
+ */
10474
+ async resolveSelectOptions(id) {
10475
+ const response = await this.api.resolveSelectOptions(id);
10476
+ return response.options;
10477
+ }
10478
+ /**
10479
+ * Validate user custom data against field definitions
10480
+ *
10481
+ * Client-side validation using the same rules as the backend. Use this
10482
+ * to validate form data before submission.
10483
+ *
10484
+ * @param data - User's custom data (key-value pairs)
10485
+ * @param definitions - Custom field definitions to validate against
10486
+ * @returns Array of validation errors (empty if valid)
10487
+ *
10488
+ * @example Form Validation
10489
+ * ```typescript
10490
+ * const definitions = await sdk.customFields.getDefinitions();
10491
+ * const formData = {
10492
+ * employee_id: 'E123', // Missing a digit
10493
+ * department: 'engineering'
10494
+ * };
10495
+ *
10496
+ * const errors = sdk.customFields.validateUserData(formData, definitions);
10497
+ * if (errors.length > 0) {
10498
+ * errors.forEach(err => {
10499
+ * console.log(`${err.field}: ${err.message}`);
10500
+ * });
10501
+ * return; // Don't submit
10502
+ * }
10503
+ *
10504
+ * // No errors, safe to submit
10505
+ * await sdk.users.updateCurrentUser({ customData: formData });
10506
+ * ```
10507
+ */
10508
+ validateUserData(data, definitions) {
10509
+ // Convert CustomFieldDefinitionDTO to FieldDefinition for validation
10510
+ const fieldDefinitions = definitions.map(def => ({
10511
+ key: def.key,
10512
+ fieldType: def.fieldType,
10513
+ label: def.label,
10514
+ validation: def.validation ?? undefined,
10515
+ }));
10516
+ return persShared.validateAllFields(data, fieldDefinitions);
10517
+ }
10518
+ /**
10519
+ * Validate a single field value
10520
+ *
10521
+ * Validates a single value against a field's rules. Useful for
10522
+ * real-time validation as the user types.
10523
+ *
10524
+ * @param value - The value to validate
10525
+ * @param definition - The field definition
10526
+ * @returns Validation error or null if valid
10527
+ *
10528
+ * @example Real-time Validation
10529
+ * ```typescript
10530
+ * const employeeIdField = definitions.find(d => d.key === 'employee_id');
10531
+ *
10532
+ * const handleBlur = (value: string) => {
10533
+ * const error = sdk.customFields.validateFieldValue(value, employeeIdField);
10534
+ * if (error) {
10535
+ * setFieldError('employee_id', error.message);
10536
+ * } else {
10537
+ * clearFieldError('employee_id');
10538
+ * }
10539
+ * };
10540
+ * ```
10541
+ */
10542
+ validateFieldValue(value, definition) {
10543
+ const error = persShared.validateFieldValue(value, definition.validation ?? null, definition.fieldType);
10544
+ if (error) {
10545
+ // Add field key to error
10546
+ return { ...error, field: definition.key };
10547
+ }
10548
+ return null;
10549
+ }
10550
+ }
10551
+
10552
+ /**
10553
+ * Low-level API for booking operations
10554
+ *
10555
+ * Provides direct HTTP calls to the booking endpoints.
10556
+ * For most use cases, prefer using BookingManager or BookingService.
10557
+ *
10558
+ * @internal
10559
+ */
10560
+ class BookingApi {
10561
+ constructor(apiClient) {
10562
+ this.apiClient = apiClient;
10563
+ this.basePath = '/bookings';
10564
+ }
10565
+ /**
10566
+ * List all bookings with optional filters
10567
+ *
10568
+ * @param options - Filter options (userId, businessId, status)
10569
+ * @returns Promise resolving to array of bookings
10570
+ */
10571
+ async getAll(options) {
10572
+ const params = new URLSearchParams();
10573
+ if (options?.userId) {
10574
+ params.append('userId', options.userId);
10575
+ }
10576
+ if (options?.businessId) {
10577
+ params.append('businessId', options.businessId);
10578
+ }
10579
+ if (options?.status) {
10580
+ params.append('status', options.status);
10581
+ }
10582
+ const queryString = params.toString();
10583
+ const url = queryString ? `${this.basePath}?${queryString}` : this.basePath;
10584
+ return this.apiClient.get(url);
10585
+ }
10586
+ /**
10587
+ * Get a single booking by ID
10588
+ *
10589
+ * @param id - Booking UUID
10590
+ * @returns Promise resolving to booking data
10591
+ */
10592
+ async getById(id) {
10593
+ return this.apiClient.get(`${this.basePath}/${id}`);
10594
+ }
10595
+ /**
10596
+ * Create a new booking
10597
+ *
10598
+ * @param data - Booking creation data
10599
+ * @returns Promise resolving to created booking
10600
+ */
10601
+ async create(data) {
10602
+ return this.apiClient.post(this.basePath, data);
10603
+ }
10604
+ /**
10605
+ * Update an existing booking
10606
+ *
10607
+ * @param id - Booking UUID
10608
+ * @param data - Booking update data
10609
+ * @returns Promise resolving to updated booking
10610
+ */
10611
+ async update(id, data) {
10612
+ return this.apiClient.put(`${this.basePath}/${id}`, data);
10613
+ }
10614
+ /**
10615
+ * Delete a booking
10616
+ *
10617
+ * @param id - Booking UUID
10618
+ * @returns Promise resolving to success status
10619
+ */
10620
+ async delete(id) {
10621
+ return this.apiClient.delete(`${this.basePath}/${id}`);
10622
+ }
10623
+ }
10624
+
10625
+ /**
10626
+ * Booking Service - Business logic layer for booking operations
10627
+ *
10628
+ * Provides booking management functionality with validation and
10629
+ * business rule enforcement.
10630
+ *
10631
+ * @internal
10632
+ */
10633
+ class BookingService {
10634
+ constructor(bookingApi) {
10635
+ this.bookingApi = bookingApi;
10636
+ }
10637
+ /**
10638
+ * Get all bookings with optional filters
10639
+ *
10640
+ * @param options - Filter options
10641
+ * @returns Promise resolving to array of bookings
10642
+ */
10643
+ async getAll(options) {
10644
+ return this.bookingApi.getAll(options);
10645
+ }
10646
+ /**
10647
+ * Get bookings for a specific user
10648
+ *
10649
+ * @param userId - User ID to filter by
10650
+ * @param status - Optional status filter
10651
+ * @returns Promise resolving to user's bookings
10652
+ */
10653
+ async getByUser(userId, status) {
10654
+ return this.bookingApi.getAll({ userId, status });
10655
+ }
10656
+ /**
10657
+ * Get bookings for a specific business
10658
+ *
10659
+ * @param businessId - Business ID to filter by
10660
+ * @param status - Optional status filter
10661
+ * @returns Promise resolving to business's bookings
10662
+ */
10663
+ async getByBusiness(businessId, status) {
10664
+ return this.bookingApi.getAll({ businessId, status });
10665
+ }
10666
+ /**
10667
+ * Get a single booking by ID
10668
+ *
10669
+ * @param id - Booking UUID
10670
+ * @returns Promise resolving to booking data
10671
+ */
10672
+ async getById(id) {
10673
+ return this.bookingApi.getById(id);
10674
+ }
10675
+ /**
10676
+ * Create a new booking
10677
+ *
10678
+ * @param data - Booking creation data
10679
+ * @returns Promise resolving to created booking
10680
+ */
10681
+ async create(data) {
10682
+ // Validate check-out is after check-in
10683
+ if (data.checkInDate && data.checkOutDate) {
10684
+ const checkIn = new Date(data.checkInDate);
10685
+ const checkOut = new Date(data.checkOutDate);
10686
+ if (checkOut <= checkIn) {
10687
+ throw new Error('Check-out date must be after check-in date');
10688
+ }
10689
+ }
10690
+ return this.bookingApi.create(data);
10691
+ }
10692
+ /**
10693
+ * Update an existing booking
10694
+ *
10695
+ * @param id - Booking UUID
10696
+ * @param data - Booking update data
10697
+ * @returns Promise resolving to updated booking
10698
+ */
10699
+ async update(id, data) {
10700
+ // Validate dates if both provided
10701
+ if (data.checkInDate && data.checkOutDate) {
10702
+ const checkIn = new Date(data.checkInDate);
10703
+ const checkOut = new Date(data.checkOutDate);
10704
+ if (checkOut <= checkIn) {
10705
+ throw new Error('Check-out date must be after check-in date');
10706
+ }
10707
+ }
10708
+ return this.bookingApi.update(id, data);
10709
+ }
10710
+ /**
10711
+ * Delete a booking
10712
+ *
10713
+ * @param id - Booking UUID
10714
+ * @returns Promise resolving to success status
10715
+ */
10716
+ async delete(id) {
10717
+ return this.bookingApi.delete(id);
10718
+ }
10719
+ /**
10720
+ * Check if a user has an active or future booking
10721
+ *
10722
+ * Useful for eligibility checks on redemptions.
10723
+ *
10724
+ * @param userId - User ID to check
10725
+ * @param options - Validation options (status filter, business filter)
10726
+ * @returns Promise resolving to true if user has valid booking
10727
+ */
10728
+ async hasValidBooking(userId, options) {
10729
+ const queryOptions = {
10730
+ userId,
10731
+ businessId: options?.businessId
10732
+ };
10733
+ // If a specific status is requested, use it
10734
+ if (options?.status && options.status !== 'active_future') {
10735
+ queryOptions.status = options.status === 'any' ? undefined : options.status;
10736
+ const bookings = await this.bookingApi.getAll(queryOptions);
10737
+ return bookings.length > 0;
10738
+ }
10739
+ // Default: check active+future (active_future or undefined)
10740
+ const activeBookings = await this.bookingApi.getAll({ ...queryOptions, status: 'active' });
10741
+ if (activeBookings.length > 0)
10742
+ return true;
10743
+ const futureBookings = await this.bookingApi.getAll({ ...queryOptions, status: 'future' });
10744
+ return futureBookings.length > 0;
10745
+ }
10746
+ }
10747
+
10748
+ /**
10749
+ * Booking Manager - High-level interface for booking operations
10750
+ *
10751
+ * Provides a simplified API for managing bookings (hotel stays, reservations, etc.)
10752
+ * Used for tracking user stays and eligibility checks on redemptions.
10753
+ *
10754
+ * @group Managers
10755
+ * @category Booking Management
10756
+ *
10757
+ * @example List Bookings
10758
+ * ```typescript
10759
+ * // Get all bookings
10760
+ * const allBookings = await sdk.bookings.getAll();
10761
+ *
10762
+ * // Filter by user
10763
+ * const userBookings = await sdk.bookings.getAll({ userId: 'user-123' });
10764
+ *
10765
+ * // Filter by status
10766
+ * const activeBookings = await sdk.bookings.getAll({ status: 'active' });
10767
+ * ```
10768
+ *
10769
+ * @example Create Booking
10770
+ * ```typescript
10771
+ * const booking = await sdk.bookings.create({
10772
+ * userId: 'user-123',
10773
+ * locationName: 'Grand Hotel',
10774
+ * checkInDate: '2026-06-01',
10775
+ * checkOutDate: '2026-06-05',
10776
+ * guests: 2
10777
+ * });
10778
+ * ```
10779
+ *
10780
+ * @example Check Eligibility
10781
+ * ```typescript
10782
+ * // Check if user has valid (active or future) booking
10783
+ * const hasBooking = await sdk.bookings.hasValidBooking('user-123');
10784
+ * if (hasBooking) {
10785
+ * // User is eligible for booking-required redemption
10786
+ * }
10787
+ * ```
10788
+ */
10789
+ class BookingManager {
10790
+ constructor(apiClient, events) {
10791
+ this.apiClient = apiClient;
10792
+ this.events = events;
10793
+ const bookingApi = new BookingApi(apiClient);
10794
+ this.bookingService = new BookingService(bookingApi);
10795
+ }
10796
+ /**
10797
+ * Get all bookings with optional filters
10798
+ *
10799
+ * @param options - Filter options (userId, businessId, status)
10800
+ * @returns Promise resolving to array of bookings
10801
+ *
10802
+ * @example Basic Usage
10803
+ * ```typescript
10804
+ * // All bookings
10805
+ * const bookings = await sdk.bookings.getAll();
10806
+ *
10807
+ * // Filter by user
10808
+ * const userBookings = await sdk.bookings.getAll({ userId: 'user-123' });
10809
+ *
10810
+ * // Filter by status
10811
+ * const activeBookings = await sdk.bookings.getAll({ status: 'active' });
10812
+ * const futureBookings = await sdk.bookings.getAll({ status: 'future' });
10813
+ * const pastBookings = await sdk.bookings.getAll({ status: 'past' });
10814
+ * ```
10815
+ */
10816
+ async getAll(options) {
10817
+ return this.bookingService.getAll(options);
10818
+ }
10819
+ /**
10820
+ * Get bookings for a specific user
10821
+ *
10822
+ * Convenience method for filtering bookings by user ID.
10823
+ *
10824
+ * @param userId - User ID to filter by
10825
+ * @param status - Optional status filter
10826
+ * @returns Promise resolving to user's bookings
10827
+ */
10828
+ async getByUser(userId, status) {
10829
+ return this.bookingService.getByUser(userId, status);
10830
+ }
10831
+ /**
10832
+ * Get bookings for a specific business
10833
+ *
10834
+ * Convenience method for filtering bookings by business ID.
10835
+ *
10836
+ * @param businessId - Business ID to filter by
10837
+ * @param status - Optional status filter
10838
+ * @returns Promise resolving to business's bookings
10839
+ */
10840
+ async getByBusiness(businessId, status) {
10841
+ return this.bookingService.getByBusiness(businessId, status);
10842
+ }
10843
+ /**
10844
+ * Get a single booking by ID
10845
+ *
10846
+ * @param id - Booking UUID
10847
+ * @returns Promise resolving to booking data
10848
+ * @throws {PersApiError} When booking not found
10849
+ */
10850
+ async getById(id) {
10851
+ return this.bookingService.getById(id);
10852
+ }
10853
+ /**
10854
+ * Create a new booking
10855
+ *
10856
+ * Creates a booking for a user at a specific location. Used for tracking
10857
+ * hotel stays, reservations, etc. for eligibility checks.
10858
+ *
10859
+ * @param data - Booking creation data
10860
+ * @returns Promise resolving to created booking
10861
+ * @throws {Error} When check-out date is not after check-in date
10862
+ *
10863
+ * @example Create Hotel Booking
10864
+ * ```typescript
10865
+ * const booking = await sdk.bookings.create({
10866
+ * userId: 'user-123',
10867
+ * locationName: 'Grand Hotel',
10868
+ * locationType: 'hotel',
10869
+ * checkInDate: '2026-06-01',
10870
+ * checkOutDate: '2026-06-05',
10871
+ * confirmationNumber: 'CONF123',
10872
+ * guests: 2,
10873
+ * notes: 'Ocean view room'
10874
+ * });
10875
+ * ```
10876
+ */
10877
+ async create(data) {
10878
+ const result = await this.bookingService.create(data);
10879
+ // TODO: Add event emission when 'booking' domain is added to pers-shared
10880
+ // this.events?.emitSuccess({ domain: 'booking', type: 'booking_created', ... });
10881
+ return result;
10882
+ }
10883
+ /**
10884
+ * Update an existing booking
10885
+ *
10886
+ * @param id - Booking UUID
10887
+ * @param data - Booking update data (partial update supported)
10888
+ * @returns Promise resolving to updated booking
10889
+ * @throws {PersApiError} When booking not found
10890
+ * @throws {Error} When check-out date is not after check-in date
10891
+ *
10892
+ * @example Update Booking Dates
10893
+ * ```typescript
10894
+ * const updated = await sdk.bookings.update('booking-123', {
10895
+ * checkInDate: '2026-06-02',
10896
+ * checkOutDate: '2026-06-07'
10897
+ * });
10898
+ * ```
10899
+ */
10900
+ async update(id, data) {
10901
+ const result = await this.bookingService.update(id, data);
10902
+ // TODO: Add event emission when 'booking' domain is added to pers-shared
10903
+ // this.events?.emitSuccess({ domain: 'booking', type: 'booking_updated', ... });
10904
+ return result;
10905
+ }
10906
+ /**
10907
+ * Delete a booking
10908
+ *
10909
+ * @param id - Booking UUID
10910
+ * @returns Promise resolving to success status
10911
+ * @throws {PersApiError} When booking not found
10912
+ */
10913
+ async delete(id) {
10914
+ const result = await this.bookingService.delete(id);
10915
+ // TODO: Add event emission when 'booking' domain is added to pers-shared
10916
+ // this.events?.emitSuccess({ domain: 'booking', type: 'booking_deleted', ... });
10917
+ return result;
10918
+ }
10919
+ /**
10920
+ * Check if a user has a valid (active or future) booking
10921
+ *
10922
+ * Useful for eligibility checks on redemptions that require a valid booking.
10923
+ *
10924
+ * @param userId - User ID to check
10925
+ * @param options - Validation options (status filter, business filter)
10926
+ * @returns Promise resolving to true if user has valid booking
10927
+ *
10928
+ * @example Eligibility Check
10929
+ * ```typescript
10930
+ * const hasBooking = await sdk.bookings.hasValidBooking('user-123');
10931
+ *
10932
+ * if (!hasBooking) {
10933
+ * console.log('User must have an active or upcoming booking');
10934
+ * }
10935
+ *
10936
+ * // Check for active booking only
10937
+ * const hasActive = await sdk.bookings.hasValidBooking('user-123', { status: 'active' });
10938
+ *
10939
+ * // Check for booking at specific business
10940
+ * const hasHotelBooking = await sdk.bookings.hasValidBooking('user-123', {
10941
+ * businessId: 'hotel-123'
10942
+ * });
10943
+ * ```
10944
+ */
10945
+ async hasValidBooking(userId, options) {
10946
+ return this.bookingService.hasValidBooking(userId, options);
10947
+ }
10948
+ /**
10949
+ * Get the booking service for advanced operations
10950
+ *
10951
+ * @returns BookingService instance with full API access
10952
+ */
10953
+ getBookingService() {
10954
+ return this.bookingService;
10955
+ }
10956
+ }
10957
+
10111
10958
  /**
10112
10959
  * @fileoverview PERS SDK - Platform-agnostic TypeScript SDK with High-Level Managers
10113
10960
  *
@@ -10796,6 +11643,78 @@ class PersSDK {
10796
11643
  }
10797
11644
  return this._webhooks;
10798
11645
  }
11646
+ /**
11647
+ * Custom Field Definition Manager - Manage tenant-specific custom fields
11648
+ *
11649
+ * Provides CRUD operations for custom field definitions that extend the built-in
11650
+ * user profile fields. Custom fields are tenant-specific and support validation.
11651
+ *
11652
+ * @returns CustomFieldDefinitionManager instance
11653
+ *
11654
+ * @example Basic Operations
11655
+ * ```typescript
11656
+ * // List all custom fields
11657
+ * const fields = await sdk.customFields.getDefinitions();
11658
+ *
11659
+ * // Create a new field
11660
+ * const field = await sdk.customFields.createDefinition({
11661
+ * key: 'employee_id',
11662
+ * label: 'Employee ID',
11663
+ * fieldType: 'text',
11664
+ * validation: { required: true }
11665
+ * });
11666
+ *
11667
+ * // Validate user data
11668
+ * const errors = sdk.customFields.validateUserData(formData, fields.data);
11669
+ * ```
11670
+ */
11671
+ get customFields() {
11672
+ if (!this._customFields) {
11673
+ this._customFields = new CustomFieldDefinitionManager(this.apiClient, this._events);
11674
+ }
11675
+ return this._customFields;
11676
+ }
11677
+ /**
11678
+ * Booking manager - High-level booking operations
11679
+ *
11680
+ * Provides methods for managing user bookings (hotel stays, reservations, etc.)
11681
+ * Used for eligibility checks on redemptions that require valid bookings.
11682
+ *
11683
+ * @returns BookingManager instance
11684
+ *
11685
+ * @example List User Bookings
11686
+ * ```typescript
11687
+ * // Get all bookings for a user
11688
+ * const bookings = await sdk.bookings.getByUser('user-123');
11689
+ *
11690
+ * // Filter by status
11691
+ * const activeBookings = await sdk.bookings.getAll({ status: 'active' });
11692
+ * ```
11693
+ *
11694
+ * @example Create Booking
11695
+ * ```typescript
11696
+ * const booking = await sdk.bookings.create({
11697
+ * userId: 'user-123',
11698
+ * locationName: 'Grand Hotel',
11699
+ * checkInDate: '2026-06-01',
11700
+ * checkOutDate: '2026-06-05'
11701
+ * });
11702
+ * ```
11703
+ *
11704
+ * @example Eligibility Check
11705
+ * ```typescript
11706
+ * const hasBooking = await sdk.bookings.hasValidBooking('user-123');
11707
+ * if (hasBooking) {
11708
+ * // User is eligible for booking-required redemption
11709
+ * }
11710
+ * ```
11711
+ */
11712
+ get bookings() {
11713
+ if (!this._bookings) {
11714
+ this._bookings = new BookingManager(this.apiClient, this._events);
11715
+ }
11716
+ return this._bookings;
11717
+ }
10799
11718
  /**
10800
11719
  * Wallet Events Manager - Real-time blockchain events for user's wallets
10801
11720
  *
@@ -10910,8 +11829,12 @@ exports.AuthApi = AuthApi;
10910
11829
  exports.AuthManager = AuthManager;
10911
11830
  exports.AuthService = AuthService;
10912
11831
  exports.AuthTokenManager = AuthTokenManager;
11832
+ exports.BookingApi = BookingApi;
11833
+ exports.BookingManager = BookingManager;
11834
+ exports.BookingService = BookingService;
10913
11835
  exports.BusinessManager = BusinessManager;
10914
11836
  exports.CampaignManager = CampaignManager;
11837
+ exports.CustomFieldDefinitionManager = CustomFieldDefinitionManager;
10915
11838
  exports.DEFAULT_PERS_CONFIG = DEFAULT_PERS_CONFIG;
10916
11839
  exports.DPOP_STORAGE_KEYS = DPOP_STORAGE_KEYS;
10917
11840
  exports.DPoPManager = DPoPManager;
@@ -10950,4 +11873,4 @@ exports.createPersEventsClient = createPersEventsClient;
10950
11873
  exports.createPersSDK = createPersSDK;
10951
11874
  exports.isFatalAuthErrorInMessage = isFatalAuthErrorInMessage;
10952
11875
  exports.mergeWithDefaults = mergeWithDefaults;
10953
- //# sourceMappingURL=pers-sdk-DeFxjuRB.cjs.map
11876
+ //# sourceMappingURL=pers-sdk-DQ6uC3h0.cjs.map