@peektravel/app-utilities 0.2.5 → 0.2.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.cjs +58 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +219 -25
- package/dist/index.d.ts +219 -25
- package/dist/index.js +58 -31
- package/dist/index.js.map +1 -1
- package/dist/ui/index.cjs +39 -8
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.d.cts +9 -3
- package/dist/ui/index.d.ts +9 -3
- package/dist/ui/index.js +39 -8
- package/dist/ui/index.js.map +1 -1
- package/docs/webhooks.md +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -234,6 +234,7 @@ function fromBookingNode(node, includeGuests = false, includePriceBreakdown = fa
|
|
|
234
234
|
const data = node ?? {};
|
|
235
235
|
const ticketQuantities = data.ticketQuantities ?? [];
|
|
236
236
|
const app = data.order?.initialQuote?.source?.actor?.app ?? null;
|
|
237
|
+
const sourceDetails = data.order?.initialQuote?.source?.actor?.name ?? null;
|
|
237
238
|
const customQuestionAnswers = (data.questionAnswers ?? []).map(
|
|
238
239
|
(answer) => {
|
|
239
240
|
const base = {
|
|
@@ -257,6 +258,7 @@ function fromBookingNode(node, includeGuests = false, includePriceBreakdown = fa
|
|
|
257
258
|
source: sourceFromApp(app),
|
|
258
259
|
sourceApp: app || UNKNOWN,
|
|
259
260
|
sourceDescription: sourceDescriptionFromApp(app),
|
|
261
|
+
sourceDetails,
|
|
260
262
|
customerName: data.primaryGuest?.name || "",
|
|
261
263
|
customerEmail: data.primaryGuest?.email || null,
|
|
262
264
|
customerPhone: data.primaryGuest?.phone || null,
|
|
@@ -266,7 +268,7 @@ function fromBookingNode(node, includeGuests = false, includePriceBreakdown = fa
|
|
|
266
268
|
timeslotId: data.timeSnapshot?.legacyId || null,
|
|
267
269
|
totalTickets: ticketQuantity(ticketQuantities),
|
|
268
270
|
ticketDescription: formatTickets(ticketQuantities),
|
|
269
|
-
tickets: ticketsToTicketArray(ticketQuantities),
|
|
271
|
+
tickets: ticketsToTicketArray(ticketQuantities, includePriceBreakdown),
|
|
270
272
|
isCanceled: data.reservationStatus === "CANCELED",
|
|
271
273
|
isNoShow: data.fulfillmentStatusOverride?.status === "NO_SHOW",
|
|
272
274
|
isCheckedIn: data.checkinStatus !== "NONE",
|
|
@@ -367,12 +369,14 @@ function resellerNameFromChannelSnapshot(channelSnapshot) {
|
|
|
367
369
|
}
|
|
368
370
|
return out;
|
|
369
371
|
}
|
|
370
|
-
function ticketsToTicketArray(ticketQuantities) {
|
|
372
|
+
function ticketsToTicketArray(ticketQuantities, includePriceBreakdown) {
|
|
371
373
|
if (!ticketQuantities || ticketQuantities.length === 0) return [];
|
|
372
374
|
return ticketQuantities.map((ticket) => ({
|
|
373
375
|
name: ticket.resourceOptionSnapshot?.name || "Unknown",
|
|
374
376
|
quantity: ticket.quantity || 0,
|
|
375
|
-
ticketId: ticket.resourceOptionSnapshot?.id || "unknown"
|
|
377
|
+
ticketId: ticket.resourceOptionSnapshot?.id || "unknown",
|
|
378
|
+
listPrice: includePriceBreakdown ? mapPrice(ticket.value?.price) : void 0,
|
|
379
|
+
totalValue: includePriceBreakdown ? mapPrice(ticket.value?.total) : void 0
|
|
376
380
|
}));
|
|
377
381
|
}
|
|
378
382
|
function durationInMin(startsAt, endsAt) {
|
|
@@ -690,6 +694,7 @@ var bookingQueryFields = `
|
|
|
690
694
|
source {
|
|
691
695
|
actor {
|
|
692
696
|
app
|
|
697
|
+
name
|
|
693
698
|
}
|
|
694
699
|
}
|
|
695
700
|
}
|
|
@@ -737,9 +742,15 @@ var PRICE_BREAKDOWN_FIELDS = `
|
|
|
737
742
|
taxes { amount formatted }
|
|
738
743
|
tips { amount formatted }
|
|
739
744
|
`;
|
|
745
|
+
var TICKET_VALUE_FIELDS = `
|
|
746
|
+
value {
|
|
747
|
+
price { amount formatted }
|
|
748
|
+
total { amount formatted }
|
|
749
|
+
}
|
|
750
|
+
`;
|
|
740
751
|
function buildBookingsListingQuery(includeGuests, includePriceBreakdown) {
|
|
741
752
|
const guestsSection = includeGuests ? bookingGuestsFields : "";
|
|
742
|
-
const fields = includePriceBreakdown ? bookingQueryFields.replace("value {", `value { ${PRICE_BREAKDOWN_FIELDS}`) : bookingQueryFields;
|
|
753
|
+
const fields = includePriceBreakdown ? bookingQueryFields.replace("value {", `value { ${PRICE_BREAKDOWN_FIELDS}`).replace("ticketQuantities {", `ticketQuantities { ${TICKET_VALUE_FIELDS}`) : bookingQueryFields;
|
|
743
754
|
return `
|
|
744
755
|
query Sales($after: String, $first: Int, $filter: SalesFilter!, $orderBy: SalesOrdering) {
|
|
745
756
|
sales(after: $after, first: $first, filter: $filter, orderBy: $orderBy) {
|
|
@@ -934,14 +945,18 @@ function buildBookingsVariables(params) {
|
|
|
934
945
|
var DEFAULT_PAGE_SIZE2 = 50;
|
|
935
946
|
var DEFAULT_CANCEL_NOTE = "Canceled";
|
|
936
947
|
var DEFAULT_CUSTOMER_MESSAGE = "Charge initiated via API";
|
|
937
|
-
var
|
|
948
|
+
var BOOKING_DB_ID_REGEX = /^b_[a-z0-9]+$/;
|
|
949
|
+
var BOOKING_DISPLAY_ID_REGEX = /^B-[A-Z0-9]+$/;
|
|
950
|
+
var ORDER_DB_ID_REGEX = /^o_[a-z0-9]+$/;
|
|
951
|
+
var ORDER_DISPLAY_ID_REGEX = /^O-[A-Z0-9]+$/;
|
|
938
952
|
var PAYMENT_SOURCE_PREFIX = "ps_";
|
|
939
953
|
var PAYMENT_ID_PREFIX = "pmt_";
|
|
940
954
|
var ALLOWED_PAYMENT_SOURCE_IDS = ["cash/cash", "custom/other", "custom/voucher"];
|
|
941
955
|
var CURRENCY_CODE_REGEX = /^[A-Z]{3}$/;
|
|
942
956
|
var ERROR_ADDON_OPTION_ID_REQUIRED = "addonOptionId is required";
|
|
943
957
|
var ERROR_QUANTITY_INVALID = "quantity must be a positive integer string";
|
|
944
|
-
var
|
|
958
|
+
var ERROR_INVALID_BOOKING_ID = "bookingId is required and must be a valid booking id, e.g. 'b_abc123' or 'B-ABC123'";
|
|
959
|
+
var ERROR_INVALID_ORDER_ID = "orderId is required and must be a valid order id, e.g. 'o_abc123' or 'O-ABC123'";
|
|
945
960
|
var ERROR_BOOKING_NOT_FOUND = "Booking not found";
|
|
946
961
|
var ERROR_MULTIPLE_BOOKINGS_FOUND = "Expected exactly one booking for the provided bookingId";
|
|
947
962
|
var ERROR_NO_ADDON_OPTION_TO_REMOVE = "No confirmed add-on option matching addonOptionId was found on the booking";
|
|
@@ -973,6 +988,7 @@ var BookingService = class {
|
|
|
973
988
|
* ```
|
|
974
989
|
*/
|
|
975
990
|
async getById(bookingId, options = {}) {
|
|
991
|
+
assertBookingId(bookingId);
|
|
976
992
|
const includeGuests = options.includeGuests ?? false;
|
|
977
993
|
const includePriceBreakdown = options.includePriceBreakdown ?? false;
|
|
978
994
|
const body = await this.client.request(
|
|
@@ -1021,6 +1037,7 @@ var BookingService = class {
|
|
|
1021
1037
|
}
|
|
1022
1038
|
/** Returns the guests on a booking (primary guest included). */
|
|
1023
1039
|
async getGuests(bookingId) {
|
|
1040
|
+
assertBookingId(bookingId);
|
|
1024
1041
|
const body = await this.client.request(
|
|
1025
1042
|
SALES_ENDPOINT,
|
|
1026
1043
|
BOOKING_GUESTS_QUERY,
|
|
@@ -1034,6 +1051,7 @@ var BookingService = class {
|
|
|
1034
1051
|
}
|
|
1035
1052
|
/** Returns the payments on file for a booking, or null when not found. */
|
|
1036
1053
|
async getPaymentsOnFile(bookingId) {
|
|
1054
|
+
assertBookingId(bookingId);
|
|
1037
1055
|
const normalized = normalizeBookingId(bookingId);
|
|
1038
1056
|
const body = await this.client.request(
|
|
1039
1057
|
SALES_ENDPOINT,
|
|
@@ -1047,6 +1065,7 @@ var BookingService = class {
|
|
|
1047
1065
|
* booking, or null when the booking is not found.
|
|
1048
1066
|
*/
|
|
1049
1067
|
async appendNote(bookingId, note, mode = "append") {
|
|
1068
|
+
assertBookingId(bookingId);
|
|
1050
1069
|
const normalized = normalizeBookingId(bookingId);
|
|
1051
1070
|
const booking = await this.getById(normalized);
|
|
1052
1071
|
if (!booking) {
|
|
@@ -1065,6 +1084,7 @@ ${note}`;
|
|
|
1065
1084
|
* when not found.
|
|
1066
1085
|
*/
|
|
1067
1086
|
async setCheckinStatus(bookingId, checkedIn) {
|
|
1087
|
+
assertBookingId(bookingId);
|
|
1068
1088
|
const normalized = normalizeBookingId(bookingId);
|
|
1069
1089
|
const checkedInAt = checkedIn ? (/* @__PURE__ */ new Date()).toISOString() : null;
|
|
1070
1090
|
await this.client.request(SALES_ENDPOINT, UPDATE_BOOKING_CHECKIN_MUTATION, {
|
|
@@ -1074,6 +1094,7 @@ ${note}`;
|
|
|
1074
1094
|
}
|
|
1075
1095
|
/** Cancels a booking and returns its id/displayId/status. */
|
|
1076
1096
|
async cancel(bookingId, notes = DEFAULT_CANCEL_NOTE) {
|
|
1097
|
+
assertBookingId(bookingId);
|
|
1077
1098
|
const body = await this.client.request(
|
|
1078
1099
|
SALES_ENDPOINT,
|
|
1079
1100
|
CANCEL_BOOKING_MUTATION,
|
|
@@ -1109,12 +1130,13 @@ ${note}`;
|
|
|
1109
1130
|
* @throws {Error} when `paymentSourceId` is missing or not a `ps_…` id / one
|
|
1110
1131
|
* of `cash/cash`, `custom/other`, `custom/voucher`; when `amount` is not a
|
|
1111
1132
|
* valid number; when `currency` is not a 3-letter uppercase code; when
|
|
1112
|
-
* `idempotencyKey` is empty; when `bookingId`
|
|
1113
|
-
* when the booking or payment source is not found; or when the
|
|
1133
|
+
* `idempotencyKey` is empty; when `bookingId` is not a valid booking id
|
|
1134
|
+
* (`b_…`/`B-…`); when the booking or payment source is not found; or when the
|
|
1135
|
+
* charge fails.
|
|
1114
1136
|
*/
|
|
1115
1137
|
async makePayment(input) {
|
|
1138
|
+
this.validatePaymentInput(input);
|
|
1116
1139
|
const normalized = normalizeBookingId(input.bookingId);
|
|
1117
|
-
this.validatePaymentInput(input, normalized);
|
|
1118
1140
|
const onFile = await this.getPaymentsOnFile(normalized);
|
|
1119
1141
|
if (!onFile) {
|
|
1120
1142
|
throw new Error("Booking not found");
|
|
@@ -1155,8 +1177,8 @@ ${note}`;
|
|
|
1155
1177
|
* payments-on-file, then applies the refund.
|
|
1156
1178
|
*/
|
|
1157
1179
|
async refund(input) {
|
|
1180
|
+
this.validateRefundInput(input);
|
|
1158
1181
|
const normalized = normalizeBookingId(input.bookingId);
|
|
1159
|
-
this.validateRefundInput(input, normalized);
|
|
1160
1182
|
const onFile = await this.getPaymentsOnFile(normalized);
|
|
1161
1183
|
if (!onFile) {
|
|
1162
1184
|
throw new Error("Booking not found");
|
|
@@ -1195,9 +1217,7 @@ ${note}`;
|
|
|
1195
1217
|
}
|
|
1196
1218
|
/** Creates an invoice link for a booking's order. */
|
|
1197
1219
|
async createInvoiceLink(bookingId) {
|
|
1198
|
-
|
|
1199
|
-
throw new Error("bookingId is required");
|
|
1200
|
-
}
|
|
1220
|
+
assertBookingId(bookingId);
|
|
1201
1221
|
const normalized = normalizeBookingId(bookingId);
|
|
1202
1222
|
const booking = await this.getById(normalized);
|
|
1203
1223
|
if (!booking || !booking.orderId) {
|
|
@@ -1217,6 +1237,7 @@ ${note}`;
|
|
|
1217
1237
|
}
|
|
1218
1238
|
/** Returns the add-ons on a booking, grouped by add-on item (clean model). */
|
|
1219
1239
|
async listAddons(bookingId) {
|
|
1240
|
+
assertBookingId(bookingId);
|
|
1220
1241
|
const { items, displayId, orderId, normalizedBookingId } = await this.fetchBookingSale(bookingId);
|
|
1221
1242
|
const addons = items.map((item) => toBookingAddon(item)).filter((addon) => addon !== null);
|
|
1222
1243
|
return {
|
|
@@ -1251,6 +1272,7 @@ ${note}`;
|
|
|
1251
1272
|
* the underlying quote/order mutations fail.
|
|
1252
1273
|
*/
|
|
1253
1274
|
async addAddon(bookingId, input) {
|
|
1275
|
+
assertBookingId(bookingId);
|
|
1254
1276
|
const addonOptionId = (input?.addonOptionId || input?.addonId || "").trim();
|
|
1255
1277
|
if (!addonOptionId) {
|
|
1256
1278
|
throw new Error(ERROR_ADDON_OPTION_ID_REQUIRED);
|
|
@@ -1303,6 +1325,7 @@ ${note}`;
|
|
|
1303
1325
|
* variables. Returns the booking's add-ons after the change.
|
|
1304
1326
|
*/
|
|
1305
1327
|
async removeAddon(bookingId, input) {
|
|
1328
|
+
assertBookingId(bookingId);
|
|
1306
1329
|
const addonOptionId = (input?.addonOptionId || input?.addonId || "").trim();
|
|
1307
1330
|
if (!addonOptionId) {
|
|
1308
1331
|
throw new Error(ERROR_ADDON_OPTION_ID_REQUIRED);
|
|
@@ -1331,14 +1354,10 @@ ${note}`;
|
|
|
1331
1354
|
* booking matched, and parses the node into the internal model.
|
|
1332
1355
|
*/
|
|
1333
1356
|
async fetchBookingSale(bookingId) {
|
|
1334
|
-
const searchString = (bookingId || "").trim();
|
|
1335
|
-
if (!searchString) {
|
|
1336
|
-
throw new Error(ERROR_BOOKING_ID_REQUIRED);
|
|
1337
|
-
}
|
|
1338
1357
|
const body = await this.client.request(
|
|
1339
1358
|
SALES_ENDPOINT,
|
|
1340
1359
|
SALES_ADDONS_QUERY,
|
|
1341
|
-
buildSalesAddonsVariables(
|
|
1360
|
+
buildSalesAddonsVariables(bookingId)
|
|
1342
1361
|
);
|
|
1343
1362
|
const edges = body.data?.sales?.edges ?? [];
|
|
1344
1363
|
if (edges.length === 0) {
|
|
@@ -1521,7 +1540,7 @@ ${note}`;
|
|
|
1521
1540
|
}
|
|
1522
1541
|
return matched.productId;
|
|
1523
1542
|
}
|
|
1524
|
-
validatePaymentInput(input
|
|
1543
|
+
validatePaymentInput(input) {
|
|
1525
1544
|
if (!input.paymentSourceId || !input.paymentSourceId.startsWith(PAYMENT_SOURCE_PREFIX) && !ALLOWED_PAYMENT_SOURCE_IDS.includes(input.paymentSourceId)) {
|
|
1526
1545
|
throw new Error(
|
|
1527
1546
|
"paymentSourceId is required and must start with 'ps_' or be one of 'cash/cash', 'custom/other', 'custom/voucher'"
|
|
@@ -1530,16 +1549,16 @@ ${note}`;
|
|
|
1530
1549
|
assertAmount(input.amount);
|
|
1531
1550
|
assertCurrency(input.currency);
|
|
1532
1551
|
assertIdempotencyKey(input.idempotencyKey);
|
|
1533
|
-
assertBookingId(
|
|
1552
|
+
assertBookingId(input.bookingId);
|
|
1534
1553
|
}
|
|
1535
|
-
validateRefundInput(input
|
|
1554
|
+
validateRefundInput(input) {
|
|
1536
1555
|
if (!input.paymentId || !input.paymentId.startsWith(PAYMENT_ID_PREFIX)) {
|
|
1537
1556
|
throw new Error("paymentId is required and must start with 'pmt_'");
|
|
1538
1557
|
}
|
|
1539
1558
|
assertAmount(input.amount);
|
|
1540
1559
|
assertCurrency(input.currency);
|
|
1541
1560
|
assertIdempotencyKey(input.idempotencyKey);
|
|
1542
|
-
assertBookingId(
|
|
1561
|
+
assertBookingId(input.bookingId);
|
|
1543
1562
|
}
|
|
1544
1563
|
async fetchPaginated(query, baseParams, includeGuests, includePriceBreakdown) {
|
|
1545
1564
|
const bookings = [];
|
|
@@ -1579,9 +1598,14 @@ function assertIdempotencyKey(idempotencyKey) {
|
|
|
1579
1598
|
throw new Error("idempotencyKey is required");
|
|
1580
1599
|
}
|
|
1581
1600
|
}
|
|
1582
|
-
function assertBookingId(
|
|
1583
|
-
if (!
|
|
1584
|
-
throw new Error(
|
|
1601
|
+
function assertBookingId(bookingId) {
|
|
1602
|
+
if (!(BOOKING_DB_ID_REGEX.test(bookingId) || BOOKING_DISPLAY_ID_REGEX.test(bookingId))) {
|
|
1603
|
+
throw new Error(ERROR_INVALID_BOOKING_ID);
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
function assertOrderId(orderId) {
|
|
1607
|
+
if (!(ORDER_DB_ID_REGEX.test(orderId) || ORDER_DISPLAY_ID_REGEX.test(orderId))) {
|
|
1608
|
+
throw new Error(ERROR_INVALID_ORDER_ID);
|
|
1585
1609
|
}
|
|
1586
1610
|
}
|
|
1587
1611
|
function validateCreateInput(input) {
|
|
@@ -1599,6 +1623,9 @@ function validateCreateInput(input) {
|
|
|
1599
1623
|
if (input.markAsPaid && !input.idempotencyKey) {
|
|
1600
1624
|
throw new Error("idempotencyKey is required when markAsPaid is set");
|
|
1601
1625
|
}
|
|
1626
|
+
if (input.parentOrderId) {
|
|
1627
|
+
assertOrderId(input.parentOrderId);
|
|
1628
|
+
}
|
|
1602
1629
|
}
|
|
1603
1630
|
function parseQuantity(value) {
|
|
1604
1631
|
if (typeof value !== "string" && typeof value !== "number") {
|
|
@@ -2303,7 +2330,7 @@ function fromTimeslotNode(node, productId) {
|
|
|
2303
2330
|
durationMin: node.minuteLength ?? 0,
|
|
2304
2331
|
date: node.date || "",
|
|
2305
2332
|
startTime: node.start ?? null,
|
|
2306
|
-
assignedResources: mapAssignedResources(node.
|
|
2333
|
+
assignedResources: mapAssignedResources(node.inheritedResourceAllocations)
|
|
2307
2334
|
};
|
|
2308
2335
|
}
|
|
2309
2336
|
function mapAssignedResources(allocations) {
|
|
@@ -2313,11 +2340,11 @@ function mapAssignedResources(allocations) {
|
|
|
2313
2340
|
return allocations.map((allocation) => {
|
|
2314
2341
|
const pool = allocation.resourcePool;
|
|
2315
2342
|
return {
|
|
2316
|
-
id: pool?.id || "",
|
|
2317
2343
|
name: pool?.name || "",
|
|
2318
2344
|
capacity: pool?.capacity ?? 0,
|
|
2319
2345
|
category: pool?.category || "",
|
|
2320
2346
|
quantity: allocation.quantity ?? 0,
|
|
2347
|
+
status: allocation.status || "",
|
|
2321
2348
|
accountUserId: pool?.accountUser?.id ?? null
|
|
2322
2349
|
};
|
|
2323
2350
|
});
|
|
@@ -2392,10 +2419,10 @@ var TIMESLOTS_QUERY = `
|
|
|
2392
2419
|
minuteLength
|
|
2393
2420
|
status
|
|
2394
2421
|
date
|
|
2395
|
-
|
|
2422
|
+
inheritedResourceAllocations {
|
|
2396
2423
|
quantity
|
|
2424
|
+
status
|
|
2397
2425
|
resourcePool {
|
|
2398
|
-
id
|
|
2399
2426
|
name
|
|
2400
2427
|
category
|
|
2401
2428
|
capacity
|
|
@@ -2430,10 +2457,10 @@ var TIMESLOT_BY_ID_QUERY = `
|
|
|
2430
2457
|
minuteLength
|
|
2431
2458
|
status
|
|
2432
2459
|
date
|
|
2433
|
-
|
|
2460
|
+
inheritedResourceAllocations {
|
|
2434
2461
|
quantity
|
|
2462
|
+
status
|
|
2435
2463
|
resourcePool {
|
|
2436
|
-
id
|
|
2437
2464
|
name
|
|
2438
2465
|
category
|
|
2439
2466
|
capacity
|