@explorins/pers-sdk-react-native 2.3.3 → 2.3.5

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/index.js CHANGED
@@ -3445,10 +3445,6 @@ const getOperatorText = (op) => operatorText[op];
3445
3445
  * Does not check cross-field comparisons (use validateFieldComparisons for that).
3446
3446
  */
3447
3447
  const validateFieldValue = (value, rules, fieldType) => {
3448
- // No rules = valid
3449
- if (!rules) {
3450
- return null;
3451
- }
3452
3448
  const stringValue = value !== null && value !== undefined ? String(value) : '';
3453
3449
  const isEmpty = value === null || value === undefined || value === '';
3454
3450
  // Skip validation if empty - required-ness is determined by requiredUserFields
@@ -3456,16 +3452,17 @@ const validateFieldValue = (value, rules, fieldType) => {
3456
3452
  if (isEmpty) {
3457
3453
  return null;
3458
3454
  }
3459
- // Type-specific validation
3455
+ // Type-specific validation (happens regardless of rules)
3456
+ // Rules object only controls additional constraints (min/max, length, etc.)
3460
3457
  switch (fieldType) {
3461
3458
  case 'text':
3462
- if (rules.minLength && stringValue.length < rules.minLength) {
3459
+ if (rules?.minLength && stringValue.length < rules.minLength) {
3463
3460
  return { field: '', message: `Minimum ${rules.minLength} characters required`, code: 'minLength' };
3464
3461
  }
3465
- if (rules.maxLength && stringValue.length > rules.maxLength) {
3462
+ if (rules?.maxLength && stringValue.length > rules.maxLength) {
3466
3463
  return { field: '', message: `Maximum ${rules.maxLength} characters allowed`, code: 'maxLength' };
3467
3464
  }
3468
- if (rules.pattern && !new RegExp(rules.pattern).test(stringValue)) {
3465
+ if (rules?.pattern && !new RegExp(rules.pattern).test(stringValue)) {
3469
3466
  return { field: '', message: rules.patternMessage || 'Invalid format', code: 'pattern' };
3470
3467
  }
3471
3468
  break;
@@ -3473,7 +3470,7 @@ const validateFieldValue = (value, rules, fieldType) => {
3473
3470
  if (stringValue.length > EMAIL_MAX_LENGTH || !EMAIL_REGEX.test(stringValue)) {
3474
3471
  return { field: '', message: 'Invalid email format', code: 'email' };
3475
3472
  }
3476
- if (rules.maxLength && stringValue.length > rules.maxLength) {
3473
+ if (rules?.maxLength && stringValue.length > rules.maxLength) {
3477
3474
  return { field: '', message: `Maximum ${rules.maxLength} characters allowed`, code: 'maxLength' };
3478
3475
  }
3479
3476
  break;
@@ -3481,19 +3478,20 @@ const validateFieldValue = (value, rules, fieldType) => {
3481
3478
  if (!URL_REGEX.test(stringValue)) {
3482
3479
  return { field: '', message: 'Invalid URL format', code: 'url' };
3483
3480
  }
3484
- if (rules.maxLength && stringValue.length > rules.maxLength) {
3481
+ if (rules?.maxLength && stringValue.length > rules.maxLength) {
3485
3482
  return { field: '', message: `Maximum ${rules.maxLength} characters allowed`, code: 'maxLength' };
3486
3483
  }
3487
3484
  break;
3488
3485
  case 'number':
3489
3486
  const numValue = typeof value === 'number' ? value : parseFloat(stringValue);
3490
- if (isNaN(numValue)) {
3487
+ // Check if string contains non-numeric garbage
3488
+ if (isNaN(numValue) || (typeof value === 'string' && !/^-?\d*\.?\d+$/.test(stringValue.trim()))) {
3491
3489
  return { field: '', message: 'Must be a valid number', code: 'type' };
3492
3490
  }
3493
- if (rules.min !== undefined && numValue < rules.min) {
3491
+ if (rules?.min !== undefined && numValue < rules.min) {
3494
3492
  return { field: '', message: `Minimum value is ${rules.min}`, code: 'min' };
3495
3493
  }
3496
- if (rules.max !== undefined && numValue > rules.max) {
3494
+ if (rules?.max !== undefined && numValue > rules.max) {
3497
3495
  return { field: '', message: `Maximum value is ${rules.max}`, code: 'max' };
3498
3496
  }
3499
3497
  break;
@@ -3508,7 +3506,7 @@ const validateFieldValue = (value, rules, fieldType) => {
3508
3506
  return { field: '', message: 'Must be a valid date', code: 'type' };
3509
3507
  }
3510
3508
  const now = new Date();
3511
- if (rules.minDate) {
3509
+ if (rules?.minDate) {
3512
3510
  const minDate = rules.minDate === 'now' ? now : new Date(rules.minDate);
3513
3511
  if (dateValue < minDate) {
3514
3512
  return {
@@ -3518,7 +3516,7 @@ const validateFieldValue = (value, rules, fieldType) => {
3518
3516
  };
3519
3517
  }
3520
3518
  }
3521
- if (rules.maxDate) {
3519
+ if (rules?.maxDate) {
3522
3520
  const maxDate = rules.maxDate === 'now' ? now : new Date(rules.maxDate);
3523
3521
  if (dateValue > maxDate) {
3524
3522
  return {
@@ -3535,10 +3533,10 @@ const validateFieldValue = (value, rules, fieldType) => {
3535
3533
  if (!phoneRegex.test(stringValue)) {
3536
3534
  return { field: '', message: 'Invalid phone number format', code: 'phone' };
3537
3535
  }
3538
- if (rules.minLength && stringValue.replace(/\D/g, '').length < rules.minLength) {
3536
+ if (rules?.minLength && stringValue.replace(/\D/g, '').length < rules.minLength) {
3539
3537
  return { field: '', message: `Phone number too short`, code: 'minLength' };
3540
3538
  }
3541
- if (rules.maxLength && stringValue.length > rules.maxLength) {
3539
+ if (rules?.maxLength && stringValue.length > rules.maxLength) {
3542
3540
  return { field: '', message: `Maximum ${rules.maxLength} characters allowed`, code: 'maxLength' };
3543
3541
  }
3544
3542
  break;
@@ -3552,10 +3550,10 @@ const validateFieldValue = (value, rules, fieldType) => {
3552
3550
  if (!Array.isArray(value)) {
3553
3551
  return { field: '', message: 'Must be an array', code: 'type' };
3554
3552
  }
3555
- if (rules.minItems !== undefined && value.length < rules.minItems) {
3553
+ if (rules?.minItems !== undefined && value.length < rules.minItems) {
3556
3554
  return { field: '', message: `Select at least ${rules.minItems} options`, code: 'minItems' };
3557
3555
  }
3558
- if (rules.maxItems !== undefined && value.length > rules.maxItems) {
3556
+ if (rules?.maxItems !== undefined && value.length > rules.maxItems) {
3559
3557
  return { field: '', message: `Select at most ${rules.maxItems} options`, code: 'maxItems' };
3560
3558
  }
3561
3559
  // Validate each item is a string
@@ -4249,6 +4247,54 @@ function isBookingStatusValid(status, requirement) {
4249
4247
  return false;
4250
4248
  }
4251
4249
  }
4250
+ /**
4251
+ * Compute booking status based on check-in and check-out dates.
4252
+ *
4253
+ * Logic:
4254
+ * - 'past': Check-out date is before today (booking ended yesterday or earlier)
4255
+ * - 'active': Check-in date is today or earlier, AND check-out is today or later (guest is staying)
4256
+ * - 'future': Check-in date is after today (upcoming booking)
4257
+ *
4258
+ * Note: Checkout day itself counts as 'active' since guest is there until end of day.
4259
+ *
4260
+ * @param checkInDate - Booking start date
4261
+ * @param checkOutDate - Booking end date
4262
+ * @param referenceDate - Date to compare against (defaults to current date)
4263
+ * @returns Computed booking status
4264
+ * @throws Error if checkOutDate is not after checkInDate
4265
+ *
4266
+ * @example
4267
+ * // Today is June 5
4268
+ * computeBookingStatus('2026-06-01', '2026-06-04') // 'past' (checked out yesterday)
4269
+ * computeBookingStatus('2026-06-01', '2026-06-05') // 'active' (checking out today)
4270
+ * computeBookingStatus('2026-06-03', '2026-06-08') // 'active' (currently staying)
4271
+ * computeBookingStatus('2026-06-10', '2026-06-15') // 'future' (upcoming)
4272
+ */
4273
+ function computeBookingStatus(checkInDate, checkOutDate, referenceDate = new Date()) {
4274
+ // Normalize all dates to start of day (00:00:00)
4275
+ const now = new Date(referenceDate);
4276
+ const checkIn = new Date(checkInDate);
4277
+ const checkOut = new Date(checkOutDate);
4278
+ now.setHours(0, 0, 0, 0);
4279
+ checkIn.setHours(0, 0, 0, 0);
4280
+ checkOut.setHours(0, 0, 0, 0);
4281
+ // Defensive validation: checkOut must be after checkIn
4282
+ // This catches invalid data from frontend or data integrity issues
4283
+ if (checkOut <= checkIn) {
4284
+ throw new Error(`Invalid booking dates: checkOut (${checkOut.toISOString()}) must be after checkIn (${checkIn.toISOString()})`);
4285
+ }
4286
+ // Checkout day itself counts as still active (guest there until end of day)
4287
+ if (checkOut < now) {
4288
+ return 'past';
4289
+ }
4290
+ else if (checkIn <= now) {
4291
+ // Note: checkOut >= now is implicit here (already ruled out checkOut < now above)
4292
+ return 'active';
4293
+ }
4294
+ else {
4295
+ return 'future';
4296
+ }
4297
+ }
4252
4298
 
4253
4299
  /**
4254
4300
  * Unified data source tracking for analytics across all entities.
@@ -12075,7 +12121,7 @@ class DefaultAuthProvider {
12075
12121
  /** SDK package name */
12076
12122
  const SDK_NAME = "@explorins/pers-sdk";
12077
12123
  /** SDK version - injected from package.json at build time */
12078
- const SDK_VERSION = "2.3.3";
12124
+ const SDK_VERSION = "2.3.10";
12079
12125
  /** Full SDK identifier for headers */
12080
12126
  const SDK_USER_AGENT = `${SDK_NAME}/${SDK_VERSION}`;
12081
12127
 
@@ -50637,6 +50683,7 @@ exports.buildWalletEventsWsUrl = buildWalletEventsWsUrl;
50637
50683
  exports.builtInUserFieldMetadata = builtInUserFieldMetadata;
50638
50684
  exports.builtInUserFields = builtInUserFields;
50639
50685
  exports.compareValues = compareValues;
50686
+ exports.computeBookingStatus = computeBookingStatus;
50640
50687
  exports.createPersEventsClient = createPersEventsClient;
50641
50688
  exports.createPersSDK = createPersSDK;
50642
50689
  exports.createReactNativeAuthProvider = createReactNativeAuthProvider;