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