@atzentis/booking-sdk 0.1.4 → 0.1.6

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
@@ -475,6 +475,505 @@ var BaseService = class {
475
475
  }
476
476
  };
477
477
 
478
+ // src/services/availability.ts
479
+ var AvailabilityService = class extends BaseService {
480
+ constructor() {
481
+ super(...arguments);
482
+ this.basePath = "/booking/v1/availability";
483
+ }
484
+ /**
485
+ * Check availability for a property and date range.
486
+ *
487
+ * @param params - Must include `propertyId`, `checkIn`, `checkOut`; supports optional filters
488
+ *
489
+ * @example
490
+ * ```typescript
491
+ * const result = await booking.availability.check({
492
+ * propertyId: "prop_abc123",
493
+ * checkIn: "2025-06-01",
494
+ * checkOut: "2025-06-05",
495
+ * type: "room",
496
+ * guests: 2,
497
+ * });
498
+ * ```
499
+ */
500
+ check(params) {
501
+ const { propertyId, checkIn, checkOut, categoryId, spaceId, guests, type } = params;
502
+ return this._get(this.basePath, {
503
+ propertyId,
504
+ checkIn,
505
+ checkOut,
506
+ categoryId,
507
+ spaceId,
508
+ guests,
509
+ type
510
+ });
511
+ }
512
+ /**
513
+ * Search availability across multiple properties.
514
+ *
515
+ * @param params - Must include `propertyIds`, `checkIn`, `checkOut`; supports filtering and pagination
516
+ *
517
+ * @example
518
+ * ```typescript
519
+ * const results = await booking.availability.search({
520
+ * propertyIds: ["prop_abc", "prop_def"],
521
+ * checkIn: "2025-06-01",
522
+ * checkOut: "2025-06-05",
523
+ * guests: 2,
524
+ * priceRange: { min: 5000, max: 20000 },
525
+ * });
526
+ * ```
527
+ */
528
+ search(params) {
529
+ return this._post(this.basePath, params);
530
+ }
531
+ /**
532
+ * Generate a calendar view of daily availability for a property.
533
+ *
534
+ * Client-side helper that calls `check()` internally and transforms the result
535
+ * into a `CalendarDay[]` suitable for calendar UI rendering.
536
+ *
537
+ * @param params - Extends `AvailabilityCheckParams` with optional `months` (default: 1)
538
+ *
539
+ * @example
540
+ * ```typescript
541
+ * const calendar = await booking.availability.getCalendar({
542
+ * propertyId: "prop_abc123",
543
+ * checkIn: "2025-06-01",
544
+ * checkOut: "2025-06-30",
545
+ * });
546
+ * ```
547
+ */
548
+ async getCalendar(params) {
549
+ const { months, ...checkParams } = params;
550
+ let endDate;
551
+ if (checkParams.checkOut !== checkParams.checkIn) {
552
+ endDate = checkParams.checkOut;
553
+ } else if (months != null) {
554
+ endDate = addDays(checkParams.checkIn, months * 30);
555
+ } else {
556
+ return [];
557
+ }
558
+ const dates = generateDateRange(checkParams.checkIn, endDate, 365);
559
+ if (dates.length === 0) return [];
560
+ const result = await this.check({ ...checkParams, checkOut: endDate });
561
+ const spacesAvailable = result.spaces.filter((s) => s.available).length;
562
+ return dates.map((date) => ({
563
+ date,
564
+ available: result.available,
565
+ price: result.available && result.pricing ? result.pricing.total : null,
566
+ minStay: result.restrictions?.minStay ?? null,
567
+ closedToArrival: result.restrictions?.closedToArrival ?? false,
568
+ closedToDeparture: result.restrictions?.closedToDeparture ?? false,
569
+ spacesAvailable: result.available ? spacesAvailable : 0
570
+ }));
571
+ }
572
+ /**
573
+ * Get available table reservation time slots for a specific date.
574
+ *
575
+ * Client-side helper for the Tables vertical. Calls `check()` internally
576
+ * with `type: "table"` and reshapes the response into `TimeSlot[]`.
577
+ *
578
+ * @param params - Must include `propertyId` and `date`
579
+ *
580
+ * @example
581
+ * ```typescript
582
+ * const slots = await booking.availability.getTableSlots({
583
+ * propertyId: "prop_abc123",
584
+ * date: "2025-06-15",
585
+ * guests: 4,
586
+ * });
587
+ * ```
588
+ */
589
+ async getTableSlots(params) {
590
+ const { date, guests, categoryId, duration, ...rest } = params;
591
+ const result = await this.check({
592
+ ...rest,
593
+ checkIn: date,
594
+ checkOut: date,
595
+ type: "table",
596
+ guests,
597
+ categoryId
598
+ });
599
+ if (!result.available || result.spaces.length === 0) {
600
+ return [];
601
+ }
602
+ const slotDuration = duration ?? 90;
603
+ const availableSpaces = result.spaces.filter((s) => s.available);
604
+ return availableSpaces.map((space, index) => ({
605
+ startTime: minutesToTime(11 * 60 + index * slotDuration),
606
+ endTime: minutesToTime(11 * 60 + index * slotDuration + slotDuration),
607
+ available: true,
608
+ tablesAvailable: availableSpaces.length,
609
+ price: space.price
610
+ }));
611
+ }
612
+ /**
613
+ * Get available service appointment slots for a specific date.
614
+ *
615
+ * Client-side helper for the Services vertical. Calls `check()` internally
616
+ * with `type: "service"` and reshapes the response into `ServiceTimeSlot[]`.
617
+ *
618
+ * @param params - Must include `propertyId` and `date`
619
+ *
620
+ * @example
621
+ * ```typescript
622
+ * const slots = await booking.availability.getServiceSlots({
623
+ * propertyId: "prop_abc123",
624
+ * date: "2025-06-15",
625
+ * providerId: "staff_001",
626
+ * duration: 60,
627
+ * });
628
+ * ```
629
+ */
630
+ async getServiceSlots(params) {
631
+ const { date, providerId, categoryId, duration, ...rest } = params;
632
+ const result = await this.check({
633
+ ...rest,
634
+ checkIn: date,
635
+ checkOut: date,
636
+ type: "service",
637
+ categoryId
638
+ });
639
+ if (!result.available || result.spaces.length === 0) {
640
+ return [];
641
+ }
642
+ const slotDuration = duration ?? 60;
643
+ const availableSpaces = result.spaces.filter((s) => s.available);
644
+ return availableSpaces.map((space, index) => ({
645
+ startTime: minutesToTime(9 * 60 + index * slotDuration),
646
+ endTime: minutesToTime(9 * 60 + index * slotDuration + slotDuration),
647
+ available: true,
648
+ tablesAvailable: availableSpaces.length,
649
+ price: space.price,
650
+ providerId: providerId ?? space.spaceId,
651
+ providerName: space.name
652
+ }));
653
+ }
654
+ /**
655
+ * Get aggregated pricing statistics for a property and date range.
656
+ *
657
+ * Client-side helper that calls `check()` internally and computes
658
+ * min/max/average/total pricing from available spaces.
659
+ *
660
+ * @param params - Extends `AvailabilityCheckParams` with optional `currency`
661
+ *
662
+ * @example
663
+ * ```typescript
664
+ * const pricing = await booking.availability.getPricing({
665
+ * propertyId: "prop_abc123",
666
+ * checkIn: "2025-06-01",
667
+ * checkOut: "2025-06-05",
668
+ * });
669
+ * console.log(`From €${pricing.minPrice / 100} to €${pricing.maxPrice / 100}`);
670
+ * ```
671
+ */
672
+ async getPricing(params) {
673
+ const { currency: requestedCurrency, ...checkParams } = params;
674
+ const result = await this.check(checkParams);
675
+ const availableSpaces = result.spaces.filter((s) => s.available);
676
+ const nights = dateDiffDays(params.checkIn, params.checkOut);
677
+ const currency = requestedCurrency ?? result.pricing?.currency ?? "EUR";
678
+ if (availableSpaces.length === 0) {
679
+ return {
680
+ minPrice: 0,
681
+ maxPrice: 0,
682
+ averagePrice: 0,
683
+ totalPrice: 0,
684
+ perNight: null,
685
+ nights,
686
+ currency
687
+ };
688
+ }
689
+ const prices = availableSpaces.map((s) => s.price);
690
+ const minPrice = Math.min(...prices);
691
+ const maxPrice = Math.max(...prices);
692
+ const averagePrice = Math.round(prices.reduce((sum, p) => sum + p, 0) / prices.length);
693
+ const totalPrice = result.pricing?.total ?? averagePrice * Math.max(nights, 1);
694
+ const perNight = nights > 0 ? Math.round(totalPrice / nights) : null;
695
+ return { minPrice, maxPrice, averagePrice, totalPrice, perNight, nights, currency };
696
+ }
697
+ };
698
+ function parseDateStr(dateStr) {
699
+ const parts = dateStr.split("-");
700
+ return [Number(parts[0]), Number(parts[1]), Number(parts[2])];
701
+ }
702
+ function addDays(dateStr, days) {
703
+ const [year, month, day] = parseDateStr(dateStr);
704
+ return formatDate(new Date(Date.UTC(year, month - 1, day + days)));
705
+ }
706
+ function generateDateRange(start, end, maxDays) {
707
+ const [sy, sm, sd] = parseDateStr(start);
708
+ const [ey, em, ed] = parseDateStr(end);
709
+ const startMs = Date.UTC(sy, sm - 1, sd);
710
+ const endMs = Date.UTC(ey, em - 1, ed);
711
+ if (endMs <= startMs) return [];
712
+ const msPerDay = 864e5;
713
+ const totalDays = Math.min(Math.floor((endMs - startMs) / msPerDay), maxDays);
714
+ const dates = [];
715
+ for (let i = 0; i < totalDays; i++) {
716
+ dates.push(formatDate(new Date(startMs + i * msPerDay)));
717
+ }
718
+ return dates;
719
+ }
720
+ function dateDiffDays(start, end) {
721
+ const [sy, sm, sd] = parseDateStr(start);
722
+ const [ey, em, ed] = parseDateStr(end);
723
+ const startMs = Date.UTC(sy, sm - 1, sd);
724
+ const endMs = Date.UTC(ey, em - 1, ed);
725
+ return Math.max(0, Math.floor((endMs - startMs) / 864e5));
726
+ }
727
+ function formatDate(d) {
728
+ const y = d.getUTCFullYear();
729
+ const m = String(d.getUTCMonth() + 1).padStart(2, "0");
730
+ const day = String(d.getUTCDate()).padStart(2, "0");
731
+ return `${y}-${m}-${day}`;
732
+ }
733
+ function minutesToTime(totalMinutes) {
734
+ const hours = Math.floor(totalMinutes / 60) % 24;
735
+ const minutes = totalMinutes % 60;
736
+ return `${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}`;
737
+ }
738
+
739
+ // src/services/bookings.ts
740
+ var BookingsService = class extends BaseService {
741
+ constructor() {
742
+ super(...arguments);
743
+ this.basePath = "/booking/v1/bookings";
744
+ }
745
+ // ---------------------------------------------------------------------------
746
+ // CRUD
747
+ // ---------------------------------------------------------------------------
748
+ /**
749
+ * Create a new booking.
750
+ *
751
+ * @example
752
+ * ```typescript
753
+ * const booking = await client.bookings.create({
754
+ * propertyId: "prop_abc123",
755
+ * categoryId: "cat_deluxe",
756
+ * guestId: "guest_42",
757
+ * checkIn: "2025-07-01",
758
+ * checkOut: "2025-07-05",
759
+ * type: "stay",
760
+ * guests: 2,
761
+ * autoConfirm: true,
762
+ * });
763
+ * ```
764
+ */
765
+ create(input) {
766
+ return this._post(this.basePath, input);
767
+ }
768
+ /**
769
+ * Get a single booking by ID.
770
+ *
771
+ * @example
772
+ * ```typescript
773
+ * const booking = await client.bookings.get("bk_abc123");
774
+ * ```
775
+ */
776
+ get(bookingId) {
777
+ return this._get(this._buildPath(bookingId));
778
+ }
779
+ /**
780
+ * List bookings for a property with optional filters and cursor-based pagination.
781
+ *
782
+ * Supports filtering by status, type, guest, date ranges, search term,
783
+ * and custom sort order. Array values for `status` and `type` are serialized
784
+ * as comma-separated strings.
785
+ *
786
+ * @example
787
+ * ```typescript
788
+ * // List all confirmed stay bookings
789
+ * const page = await client.bookings.list({
790
+ * propertyId: "prop_abc123",
791
+ * status: "confirmed",
792
+ * type: "stay",
793
+ * });
794
+ *
795
+ * // Multi-status filter with date range
796
+ * const page2 = await client.bookings.list({
797
+ * propertyId: "prop_abc123",
798
+ * status: ["confirmed", "checked_in"],
799
+ * checkInFrom: "2025-07-01",
800
+ * checkInTo: "2025-07-31",
801
+ * sort: { field: "checkIn", direction: "asc" },
802
+ * limit: 20,
803
+ * });
804
+ * ```
805
+ */
806
+ list(params) {
807
+ const query = {
808
+ propertyId: params.propertyId
809
+ };
810
+ if (params.status !== void 0) {
811
+ query.status = Array.isArray(params.status) ? params.status.join(",") : params.status;
812
+ }
813
+ if (params.type !== void 0) {
814
+ query.type = Array.isArray(params.type) ? params.type.join(",") : params.type;
815
+ }
816
+ if (params.guestId !== void 0) query.guestId = params.guestId;
817
+ if (params.checkInFrom !== void 0) query.checkInFrom = params.checkInFrom;
818
+ if (params.checkInTo !== void 0) query.checkInTo = params.checkInTo;
819
+ if (params.checkOutFrom !== void 0) query.checkOutFrom = params.checkOutFrom;
820
+ if (params.checkOutTo !== void 0) query.checkOutTo = params.checkOutTo;
821
+ if (params.search !== void 0) query.search = params.search;
822
+ if (params.sort !== void 0) {
823
+ query.sort = `${params.sort.field}:${params.sort.direction}`;
824
+ }
825
+ if (params.limit !== void 0) query.limit = params.limit;
826
+ if (params.cursor !== void 0) query.cursor = params.cursor;
827
+ return this._get(this.basePath, query);
828
+ }
829
+ /**
830
+ * Update an existing booking.
831
+ *
832
+ * @example
833
+ * ```typescript
834
+ * const updated = await client.bookings.update("bk_abc123", {
835
+ * guests: 3,
836
+ * notes: "Extra bed requested",
837
+ * });
838
+ * ```
839
+ */
840
+ update(bookingId, input) {
841
+ return this._patch(this._buildPath(bookingId), input);
842
+ }
843
+ /**
844
+ * Delete a booking.
845
+ *
846
+ * @example
847
+ * ```typescript
848
+ * await client.bookings.delete("bk_abc123");
849
+ * ```
850
+ */
851
+ delete(bookingId) {
852
+ return this._delete(this._buildPath(bookingId));
853
+ }
854
+ // ---------------------------------------------------------------------------
855
+ // Lifecycle — happy path
856
+ // ---------------------------------------------------------------------------
857
+ /**
858
+ * Confirm a pending booking.
859
+ *
860
+ * Transitions: `pending` → `confirmed`
861
+ *
862
+ * @throws {ConflictError} 409 if the booking is not in `pending` status
863
+ *
864
+ * @example
865
+ * ```typescript
866
+ * const confirmed = await client.bookings.confirm("bk_abc123");
867
+ * // confirmed.status === "confirmed"
868
+ * // confirmed.confirmedAt is populated
869
+ * ```
870
+ */
871
+ confirm(bookingId) {
872
+ return this._post(this._buildPath(bookingId, "confirm"), {});
873
+ }
874
+ /**
875
+ * Check in a guest.
876
+ *
877
+ * Transitions: `confirmed` → `checked_in`
878
+ *
879
+ * @throws {ConflictError} 409 if the booking is not in `confirmed` status
880
+ *
881
+ * @example
882
+ * ```typescript
883
+ * const checkedIn = await client.bookings.checkIn("bk_abc123", {
884
+ * actualArrival: "2025-07-01T14:30:00Z",
885
+ * });
886
+ * // checkedIn.status === "checked_in"
887
+ * // checkedIn.checkedInAt is populated
888
+ * ```
889
+ */
890
+ checkIn(bookingId, input) {
891
+ return this._post(this._buildPath(bookingId, "check-in"), input ?? {});
892
+ }
893
+ /**
894
+ * Check out a guest.
895
+ *
896
+ * Transitions: `checked_in` → `checked_out`
897
+ *
898
+ * @throws {ConflictError} 409 if the booking is not in `checked_in` status
899
+ *
900
+ * @example
901
+ * ```typescript
902
+ * const checkedOut = await client.bookings.checkOut("bk_abc123", {
903
+ * actualDeparture: "2025-07-05T11:00:00Z",
904
+ * });
905
+ * // checkedOut.status === "checked_out"
906
+ * // checkedOut.checkedOutAt is populated
907
+ * ```
908
+ */
909
+ checkOut(bookingId, input) {
910
+ return this._post(this._buildPath(bookingId, "check-out"), input ?? {});
911
+ }
912
+ // ---------------------------------------------------------------------------
913
+ // Lifecycle — alternate endings
914
+ // ---------------------------------------------------------------------------
915
+ /**
916
+ * Cancel a booking.
917
+ *
918
+ * Transitions: `pending` | `confirmed` | `checked_in` → `cancelled`
919
+ *
920
+ * @throws {ConflictError} 409 if the booking is already cancelled, checked out, or no-show
921
+ *
922
+ * @example
923
+ * ```typescript
924
+ * const cancelled = await client.bookings.cancel("bk_abc123", {
925
+ * reason: "Guest requested cancellation",
926
+ * refundRequested: true,
927
+ * });
928
+ * // cancelled.status === "cancelled"
929
+ * // cancelled.cancelledAt is populated
930
+ * // cancelled.cancellationReason === "Guest requested cancellation"
931
+ * ```
932
+ */
933
+ cancel(bookingId, input) {
934
+ return this._post(this._buildPath(bookingId, "cancel"), input);
935
+ }
936
+ /**
937
+ * Mark a booking as no-show.
938
+ *
939
+ * Transitions: `pending` | `confirmed` | `checked_in` → `no_show`
940
+ *
941
+ * @throws {ConflictError} 409 if the booking is already checked out, cancelled, or no-show
942
+ *
943
+ * @example
944
+ * ```typescript
945
+ * const noShow = await client.bookings.noShow("bk_abc123", {
946
+ * chargeNoShowFee: true,
947
+ * });
948
+ * // noShow.status === "no_show"
949
+ * // noShow.noShowAt is populated
950
+ * ```
951
+ */
952
+ noShow(bookingId, input) {
953
+ return this._post(this._buildPath(bookingId, "no-show"), input ?? {});
954
+ }
955
+ /**
956
+ * Assign or reassign a space to a booking.
957
+ *
958
+ * Can be called on any active booking (pending, confirmed, checked_in).
959
+ *
960
+ * @throws {ConflictError} 409 if the booking is in a terminal status
961
+ *
962
+ * @example
963
+ * ```typescript
964
+ * const assigned = await client.bookings.assignSpace("bk_abc123", {
965
+ * spaceId: "spc_room101",
966
+ * notes: "Upgraded to deluxe",
967
+ * });
968
+ * // assigned.spaceId === "spc_room101"
969
+ * // assigned.space is populated
970
+ * ```
971
+ */
972
+ assignSpace(bookingId, input) {
973
+ return this._post(this._buildPath(bookingId, "assign-space"), input);
974
+ }
975
+ };
976
+
478
977
  // src/services/categories.ts
479
978
  var CategoriesService = class extends BaseService {
480
979
  constructor() {
@@ -759,6 +1258,16 @@ var BookingClient = class {
759
1258
  logger: validated.logger
760
1259
  });
761
1260
  }
1261
+ /** Availability service — lazy-initialized on first access */
1262
+ get availability() {
1263
+ this._availability ?? (this._availability = new AvailabilityService(this.httpClient));
1264
+ return this._availability;
1265
+ }
1266
+ /** Bookings service — lazy-initialized on first access */
1267
+ get bookings() {
1268
+ this._bookings ?? (this._bookings = new BookingsService(this.httpClient));
1269
+ return this._bookings;
1270
+ }
762
1271
  /** Properties service — lazy-initialized on first access */
763
1272
  get properties() {
764
1273
  this._properties ?? (this._properties = new PropertiesService(this.httpClient));
@@ -813,6 +1322,27 @@ async function firstPage(fetcher, options) {
813
1322
  return fetcher({ ...options ?? {} });
814
1323
  }
815
1324
 
1325
+ // src/types/bookings.ts
1326
+ var BOOKING_TYPES = [
1327
+ "stay",
1328
+ "table",
1329
+ "service",
1330
+ "parking",
1331
+ "desk",
1332
+ "meeting",
1333
+ "dayuse",
1334
+ "custom"
1335
+ ];
1336
+ var BOOKING_STATUSES = [
1337
+ "pending",
1338
+ "confirmed",
1339
+ "checked_in",
1340
+ "checked_out",
1341
+ "cancelled",
1342
+ "no_show",
1343
+ "waitlist"
1344
+ ];
1345
+
816
1346
  // src/types/properties.ts
817
1347
  var PROPERTY_MODULES = [
818
1348
  "booking",
@@ -861,8 +1391,12 @@ var SPACE_STATUSES = [
861
1391
  var VERSION = "0.1.0";
862
1392
  export {
863
1393
  AuthenticationError,
1394
+ AvailabilityService,
1395
+ BOOKING_STATUSES,
1396
+ BOOKING_TYPES,
864
1397
  BookingClient,
865
1398
  BookingError,
1399
+ BookingsService,
866
1400
  CategoriesService,
867
1401
  ConflictError,
868
1402
  DEFAULT_MODULES,