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