@atzentis/booking-sdk 0.1.5 → 0.1.7

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
@@ -736,6 +736,244 @@ function minutesToTime(totalMinutes) {
736
736
  return `${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}`;
737
737
  }
738
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
+
739
977
  // src/services/categories.ts
740
978
  var CategoriesService = class extends BaseService {
741
979
  constructor() {
@@ -801,6 +1039,285 @@ var CategoriesService = class extends BaseService {
801
1039
  }
802
1040
  };
803
1041
 
1042
+ // src/services/guests.ts
1043
+ var GuestsService = class extends BaseService {
1044
+ constructor() {
1045
+ super(...arguments);
1046
+ this.basePath = "/guest/v1/profiles";
1047
+ }
1048
+ // ---------------------------------------------------------------------------
1049
+ // CRUD
1050
+ // ---------------------------------------------------------------------------
1051
+ /**
1052
+ * Create a new guest profile.
1053
+ *
1054
+ * @example
1055
+ * ```typescript
1056
+ * const guest = await client.guests.create({
1057
+ * firstName: "Maria",
1058
+ * lastName: "Papadopoulou",
1059
+ * email: "maria@example.com",
1060
+ * phone: "+30 210 1234567",
1061
+ * nationality: "GR",
1062
+ * });
1063
+ * ```
1064
+ */
1065
+ create(input) {
1066
+ return this._post(this.basePath, input);
1067
+ }
1068
+ /**
1069
+ * Get a single guest by ID.
1070
+ *
1071
+ * @example
1072
+ * ```typescript
1073
+ * const guest = await client.guests.get("guest_42");
1074
+ * ```
1075
+ */
1076
+ get(guestId) {
1077
+ return this._get(this._buildPath(guestId));
1078
+ }
1079
+ /**
1080
+ * List guests with optional filters and cursor-based pagination.
1081
+ *
1082
+ * @example
1083
+ * ```typescript
1084
+ * const page = await client.guests.list({
1085
+ * tags: ["vip", "returning"],
1086
+ * sort: { field: "lastName", direction: "asc" },
1087
+ * limit: 20,
1088
+ * });
1089
+ * ```
1090
+ */
1091
+ list(params) {
1092
+ if (!params) {
1093
+ return this._get(this.basePath);
1094
+ }
1095
+ const query = {};
1096
+ if (params.tags !== void 0 && params.tags.length > 0) {
1097
+ query.tags = params.tags.join(",");
1098
+ }
1099
+ if (params.source !== void 0) query.source = params.source;
1100
+ if (params.createdFrom !== void 0) query.createdFrom = params.createdFrom;
1101
+ if (params.createdTo !== void 0) query.createdTo = params.createdTo;
1102
+ if (params.sort !== void 0) {
1103
+ query.sort = `${params.sort.field}:${params.sort.direction}`;
1104
+ }
1105
+ if (params.limit !== void 0) query.limit = params.limit;
1106
+ if (params.cursor !== void 0) query.cursor = params.cursor;
1107
+ return this._get(this.basePath, query);
1108
+ }
1109
+ /**
1110
+ * Update an existing guest profile.
1111
+ *
1112
+ * @example
1113
+ * ```typescript
1114
+ * const updated = await client.guests.update("guest_42", {
1115
+ * email: "maria.new@example.com",
1116
+ * tags: ["vip"],
1117
+ * });
1118
+ * ```
1119
+ */
1120
+ update(guestId, input) {
1121
+ return this._patch(this._buildPath(guestId), input);
1122
+ }
1123
+ /**
1124
+ * Delete a guest profile (soft-delete).
1125
+ *
1126
+ * @example
1127
+ * ```typescript
1128
+ * await client.guests.delete("guest_42");
1129
+ * ```
1130
+ */
1131
+ delete(guestId) {
1132
+ return this._delete(this._buildPath(guestId));
1133
+ }
1134
+ // ---------------------------------------------------------------------------
1135
+ // Search and Duplicate Detection
1136
+ // ---------------------------------------------------------------------------
1137
+ /**
1138
+ * Search guests by name, email, phone, or passport number.
1139
+ *
1140
+ * Returns results sorted by relevance score (highest first).
1141
+ *
1142
+ * @param query - Search query string
1143
+ * @param params - Optional search parameters (limit, fields)
1144
+ *
1145
+ * @example
1146
+ * ```typescript
1147
+ * // Search by name
1148
+ * const results = await client.guests.search("Papadopoulos");
1149
+ *
1150
+ * // Search by email with field filter
1151
+ * const results2 = await client.guests.search("maria@", {
1152
+ * fields: ["email"],
1153
+ * limit: 5,
1154
+ * });
1155
+ * ```
1156
+ */
1157
+ search(query, params) {
1158
+ const searchQuery = {
1159
+ q: query
1160
+ };
1161
+ if (params?.limit !== void 0) searchQuery.limit = params.limit;
1162
+ if (params?.fields !== void 0 && params.fields.length > 0) {
1163
+ searchQuery.fields = params.fields.join(",");
1164
+ }
1165
+ return this._get(this._buildPath("search"), searchQuery);
1166
+ }
1167
+ /**
1168
+ * Find potential duplicate profiles for a guest.
1169
+ *
1170
+ * Returns candidates sorted by confidence score (highest first).
1171
+ *
1172
+ * @throws {NotFoundError} 404 if the guest is not found
1173
+ *
1174
+ * @example
1175
+ * ```typescript
1176
+ * const duplicates = await client.guests.findDuplicates("guest_42");
1177
+ * for (const dup of duplicates.data) {
1178
+ * console.log(`${dup.guest.firstName} — ${dup.confidence} (${dup.matchedFields})`);
1179
+ * }
1180
+ * ```
1181
+ */
1182
+ findDuplicates(guestId) {
1183
+ return this._get(this._buildPath(guestId, "duplicates"));
1184
+ }
1185
+ // ---------------------------------------------------------------------------
1186
+ // Merge
1187
+ // ---------------------------------------------------------------------------
1188
+ /**
1189
+ * Merge duplicate guest profiles into a primary profile.
1190
+ *
1191
+ * **Warning: This operation is irreversible.** All history from duplicate
1192
+ * profiles is transferred to the primary. Duplicate profiles are soft-deleted.
1193
+ *
1194
+ * @throws {NotFoundError} 404 if primary or any duplicate is not found
1195
+ * @throws {ConflictError} 409 if attempting to merge a guest into itself
1196
+ * @throws {ValidationError} 400 if duplicateIds is empty
1197
+ *
1198
+ * @example
1199
+ * ```typescript
1200
+ * const merged = await client.guests.merge("guest_primary", {
1201
+ * duplicateIds: ["guest_dup1", "guest_dup2"],
1202
+ * });
1203
+ * ```
1204
+ */
1205
+ merge(primaryId, input) {
1206
+ return this._post(this._buildPath(primaryId, "merge"), input);
1207
+ }
1208
+ // ---------------------------------------------------------------------------
1209
+ // Preferences
1210
+ // ---------------------------------------------------------------------------
1211
+ /**
1212
+ * Get guest preferences.
1213
+ *
1214
+ * Returns default/empty preferences if none have been set (does not throw 404).
1215
+ *
1216
+ * @throws {NotFoundError} 404 if the guest is not found
1217
+ *
1218
+ * @example
1219
+ * ```typescript
1220
+ * const prefs = await client.guests.getPreferences("guest_42");
1221
+ * console.log(prefs.dietary, prefs.roomType);
1222
+ * ```
1223
+ */
1224
+ getPreferences(guestId) {
1225
+ return this._get(this._buildPath(guestId, "preferences"));
1226
+ }
1227
+ /**
1228
+ * Update guest preferences (partial merge).
1229
+ *
1230
+ * Only provided fields are updated; unspecified fields retain their values.
1231
+ *
1232
+ * @throws {NotFoundError} 404 if the guest is not found
1233
+ *
1234
+ * @example
1235
+ * ```typescript
1236
+ * const updated = await client.guests.updatePreferences("guest_42", {
1237
+ * dietary: ["vegetarian"],
1238
+ * roomType: "suite",
1239
+ * custom: { pillow: "firm" },
1240
+ * });
1241
+ * ```
1242
+ */
1243
+ updatePreferences(guestId, input) {
1244
+ return this._patch(this._buildPath(guestId, "preferences"), input);
1245
+ }
1246
+ // ---------------------------------------------------------------------------
1247
+ // Cross-Domain History
1248
+ // ---------------------------------------------------------------------------
1249
+ /**
1250
+ * Get a guest's booking history.
1251
+ *
1252
+ * Returns lightweight booking summaries with pagination support.
1253
+ *
1254
+ * @throws {NotFoundError} 404 if the guest is not found
1255
+ *
1256
+ * @example
1257
+ * ```typescript
1258
+ * const bookings = await client.guests.getBookings("guest_42", {
1259
+ * from: "2025-01-01",
1260
+ * to: "2025-12-31",
1261
+ * limit: 10,
1262
+ * });
1263
+ * ```
1264
+ */
1265
+ getBookings(guestId, params) {
1266
+ return this._get(
1267
+ this._buildPath(guestId, "bookings"),
1268
+ params ? this._buildHistoryQuery(params) : void 0
1269
+ );
1270
+ }
1271
+ /**
1272
+ * Get a guest's folio history.
1273
+ *
1274
+ * Returns lightweight folio summaries with pagination support.
1275
+ *
1276
+ * @throws {NotFoundError} 404 if the guest is not found
1277
+ *
1278
+ * @example
1279
+ * ```typescript
1280
+ * const folios = await client.guests.getFolios("guest_42");
1281
+ * ```
1282
+ */
1283
+ getFolios(guestId, params) {
1284
+ return this._get(
1285
+ this._buildPath(guestId, "folios"),
1286
+ params ? this._buildHistoryQuery(params) : void 0
1287
+ );
1288
+ }
1289
+ /**
1290
+ * Get a guest's order history.
1291
+ *
1292
+ * Returns lightweight order summaries with pagination support.
1293
+ *
1294
+ * @throws {NotFoundError} 404 if the guest is not found
1295
+ *
1296
+ * @example
1297
+ * ```typescript
1298
+ * const orders = await client.guests.getOrders("guest_42", {
1299
+ * limit: 5,
1300
+ * cursor: "next_page",
1301
+ * });
1302
+ * ```
1303
+ */
1304
+ getOrders(guestId, params) {
1305
+ return this._get(
1306
+ this._buildPath(guestId, "orders"),
1307
+ params ? this._buildHistoryQuery(params) : void 0
1308
+ );
1309
+ }
1310
+ /** Build query params from GuestHistoryParams, omitting undefined values */
1311
+ _buildHistoryQuery(params) {
1312
+ const query = {};
1313
+ if (params.limit !== void 0) query.limit = params.limit;
1314
+ if (params.cursor !== void 0) query.cursor = params.cursor;
1315
+ if (params.from !== void 0) query.from = params.from;
1316
+ if (params.to !== void 0) query.to = params.to;
1317
+ return query;
1318
+ }
1319
+ };
1320
+
804
1321
  // src/services/properties.ts
805
1322
  var PropertiesService = class extends BaseService {
806
1323
  constructor() {
@@ -1025,6 +1542,16 @@ var BookingClient = class {
1025
1542
  this._availability ?? (this._availability = new AvailabilityService(this.httpClient));
1026
1543
  return this._availability;
1027
1544
  }
1545
+ /** Bookings service — lazy-initialized on first access */
1546
+ get bookings() {
1547
+ this._bookings ?? (this._bookings = new BookingsService(this.httpClient));
1548
+ return this._bookings;
1549
+ }
1550
+ /** Guests service — lazy-initialized on first access */
1551
+ get guests() {
1552
+ this._guests ?? (this._guests = new GuestsService(this.httpClient));
1553
+ return this._guests;
1554
+ }
1028
1555
  /** Properties service — lazy-initialized on first access */
1029
1556
  get properties() {
1030
1557
  this._properties ?? (this._properties = new PropertiesService(this.httpClient));
@@ -1079,6 +1606,27 @@ async function firstPage(fetcher, options) {
1079
1606
  return fetcher({ ...options ?? {} });
1080
1607
  }
1081
1608
 
1609
+ // src/types/bookings.ts
1610
+ var BOOKING_TYPES = [
1611
+ "stay",
1612
+ "table",
1613
+ "service",
1614
+ "parking",
1615
+ "desk",
1616
+ "meeting",
1617
+ "dayuse",
1618
+ "custom"
1619
+ ];
1620
+ var BOOKING_STATUSES = [
1621
+ "pending",
1622
+ "confirmed",
1623
+ "checked_in",
1624
+ "checked_out",
1625
+ "cancelled",
1626
+ "no_show",
1627
+ "waitlist"
1628
+ ];
1629
+
1082
1630
  // src/types/properties.ts
1083
1631
  var PROPERTY_MODULES = [
1084
1632
  "booking",
@@ -1128,12 +1676,16 @@ var VERSION = "0.1.0";
1128
1676
  export {
1129
1677
  AuthenticationError,
1130
1678
  AvailabilityService,
1679
+ BOOKING_STATUSES,
1680
+ BOOKING_TYPES,
1131
1681
  BookingClient,
1132
1682
  BookingError,
1683
+ BookingsService,
1133
1684
  CategoriesService,
1134
1685
  ConflictError,
1135
1686
  DEFAULT_MODULES,
1136
1687
  ForbiddenError,
1688
+ GuestsService,
1137
1689
  HttpClient,
1138
1690
  NotFoundError,
1139
1691
  PROPERTY_MODULES,