@atzentis/booking-sdk 0.1.3 → 0.1.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
@@ -475,6 +475,267 @@ 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
+
478
739
  // src/services/categories.ts
479
740
  var CategoriesService = class extends BaseService {
480
741
  constructor() {
@@ -613,6 +874,135 @@ var PropertiesService = class extends BaseService {
613
874
  }
614
875
  };
615
876
 
877
+ // src/services/spaces.ts
878
+ var SpacesService = class extends BaseService {
879
+ constructor() {
880
+ super(...arguments);
881
+ this.basePath = "/inventory/v1/spaces";
882
+ }
883
+ /**
884
+ * Create a new space.
885
+ *
886
+ * @example
887
+ * ```typescript
888
+ * const space = await booking.spaces.create({
889
+ * propertyId: "prop_abc123",
890
+ * name: "Room 101",
891
+ * type: "room",
892
+ * floor: "1",
893
+ * capacity: 2,
894
+ * });
895
+ * ```
896
+ */
897
+ create(input) {
898
+ return this._post(this.basePath, input);
899
+ }
900
+ /**
901
+ * List spaces for a property with optional filters and cursor-based pagination.
902
+ *
903
+ * @param params - Must include `propertyId`; supports optional `type`, `status`, `categoryId`, `floor` filters
904
+ *
905
+ * @example
906
+ * ```typescript
907
+ * const page = await booking.spaces.list({
908
+ * propertyId: "prop_abc123",
909
+ * type: "room",
910
+ * status: "available",
911
+ * limit: 20,
912
+ * });
913
+ * ```
914
+ */
915
+ list(params) {
916
+ return this._list(params);
917
+ }
918
+ /**
919
+ * Get a single space by ID.
920
+ *
921
+ * @example
922
+ * ```typescript
923
+ * const space = await booking.spaces.get("spc_abc123");
924
+ * ```
925
+ */
926
+ get(spaceId) {
927
+ return this._get(this._buildPath(spaceId));
928
+ }
929
+ /**
930
+ * Update an existing space.
931
+ *
932
+ * @example
933
+ * ```typescript
934
+ * const updated = await booking.spaces.update("spc_abc123", {
935
+ * name: "Room 101 Deluxe",
936
+ * capacity: 3,
937
+ * });
938
+ * ```
939
+ */
940
+ update(spaceId, input) {
941
+ return this._patch(this._buildPath(spaceId), input);
942
+ }
943
+ /**
944
+ * Delete a space.
945
+ *
946
+ * @example
947
+ * ```typescript
948
+ * await booking.spaces.delete("spc_abc123");
949
+ * ```
950
+ */
951
+ delete(spaceId) {
952
+ return this._delete(this._buildPath(spaceId));
953
+ }
954
+ /**
955
+ * Bulk create multiple spaces for a property in a single API call.
956
+ *
957
+ * @example
958
+ * ```typescript
959
+ * const spaces = await booking.spaces.bulkCreate("prop_abc123", [
960
+ * { propertyId: "prop_abc123", name: "Room 101", type: "room" },
961
+ * { propertyId: "prop_abc123", name: "Room 102", type: "room" },
962
+ * ]);
963
+ * ```
964
+ */
965
+ bulkCreate(propertyId, spaces) {
966
+ return this._post(this._buildPath("bulk"), {
967
+ propertyId,
968
+ spaces
969
+ });
970
+ }
971
+ /**
972
+ * Bulk update multiple spaces for a property in a single API call.
973
+ *
974
+ * @example
975
+ * ```typescript
976
+ * const updated = await booking.spaces.bulkUpdate("prop_abc123", [
977
+ * { id: "spc_1", name: "Room 101 Deluxe" },
978
+ * { id: "spc_2", capacity: 3 },
979
+ * ]);
980
+ * ```
981
+ */
982
+ bulkUpdate(propertyId, updates) {
983
+ return this._patch(this._buildPath("bulk"), {
984
+ propertyId,
985
+ updates
986
+ });
987
+ }
988
+ /**
989
+ * Link a space to a POS table. Tables vertical only.
990
+ *
991
+ * Creates a cross-domain bridge between the inventory service and the POS system,
992
+ * enabling a space to reference its physical POS table.
993
+ *
994
+ * @example
995
+ * ```typescript
996
+ * const linked = await booking.spaces.linkPosTable("spc_abc123", {
997
+ * posTableId: "pos_table_42",
998
+ * });
999
+ * ```
1000
+ */
1001
+ linkPosTable(spaceId, input) {
1002
+ return this._post(this._buildPath(spaceId, "pos-table"), input);
1003
+ }
1004
+ };
1005
+
616
1006
  // src/client.ts
617
1007
  var BookingClient = class {
618
1008
  constructor(config) {
@@ -630,6 +1020,11 @@ var BookingClient = class {
630
1020
  logger: validated.logger
631
1021
  });
632
1022
  }
1023
+ /** Availability service — lazy-initialized on first access */
1024
+ get availability() {
1025
+ this._availability ?? (this._availability = new AvailabilityService(this.httpClient));
1026
+ return this._availability;
1027
+ }
633
1028
  /** Properties service — lazy-initialized on first access */
634
1029
  get properties() {
635
1030
  this._properties ?? (this._properties = new PropertiesService(this.httpClient));
@@ -640,6 +1035,11 @@ var BookingClient = class {
640
1035
  this._categories ?? (this._categories = new CategoriesService(this.httpClient));
641
1036
  return this._categories;
642
1037
  }
1038
+ /** Spaces service — lazy-initialized on first access */
1039
+ get spaces() {
1040
+ this._spaces ?? (this._spaces = new SpacesService(this.httpClient));
1041
+ return this._spaces;
1042
+ }
643
1043
  setApiKey(key) {
644
1044
  if (!key || key.trim().length === 0) {
645
1045
  throw new Error("apiKey must be a non-empty string");
@@ -703,10 +1103,31 @@ var DEFAULT_MODULES = {
703
1103
  pos: false
704
1104
  };
705
1105
 
1106
+ // src/types/spaces.ts
1107
+ var SPACE_TYPES = [
1108
+ "room",
1109
+ "table",
1110
+ "sunbed",
1111
+ "parking",
1112
+ "desk",
1113
+ "meeting_room",
1114
+ "locker",
1115
+ "service",
1116
+ "custom"
1117
+ ];
1118
+ var SPACE_STATUSES = [
1119
+ "available",
1120
+ "occupied",
1121
+ "maintenance",
1122
+ "blocked",
1123
+ "inactive"
1124
+ ];
1125
+
706
1126
  // src/index.ts
707
1127
  var VERSION = "0.1.0";
708
1128
  export {
709
1129
  AuthenticationError,
1130
+ AvailabilityService,
710
1131
  BookingClient,
711
1132
  BookingError,
712
1133
  CategoriesService,
@@ -719,7 +1140,10 @@ export {
719
1140
  PaymentError,
720
1141
  PropertiesService,
721
1142
  RateLimitError,
1143
+ SPACE_STATUSES,
1144
+ SPACE_TYPES,
722
1145
  ServerError,
1146
+ SpacesService,
723
1147
  TimeoutError,
724
1148
  VERSION,
725
1149
  ValidationError,