@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.cjs
CHANGED
|
@@ -256,6 +256,7 @@ function fromBookingNode(node, includeGuests = false, includePriceBreakdown = fa
|
|
|
256
256
|
const data = node ?? {};
|
|
257
257
|
const ticketQuantities = data.ticketQuantities ?? [];
|
|
258
258
|
const app = data.order?.initialQuote?.source?.actor?.app ?? null;
|
|
259
|
+
const sourceDetails = data.order?.initialQuote?.source?.actor?.name ?? null;
|
|
259
260
|
const customQuestionAnswers = (data.questionAnswers ?? []).map(
|
|
260
261
|
(answer) => {
|
|
261
262
|
const base = {
|
|
@@ -279,6 +280,7 @@ function fromBookingNode(node, includeGuests = false, includePriceBreakdown = fa
|
|
|
279
280
|
source: sourceFromApp(app),
|
|
280
281
|
sourceApp: app || UNKNOWN,
|
|
281
282
|
sourceDescription: sourceDescriptionFromApp(app),
|
|
283
|
+
sourceDetails,
|
|
282
284
|
customerName: data.primaryGuest?.name || "",
|
|
283
285
|
customerEmail: data.primaryGuest?.email || null,
|
|
284
286
|
customerPhone: data.primaryGuest?.phone || null,
|
|
@@ -288,7 +290,7 @@ function fromBookingNode(node, includeGuests = false, includePriceBreakdown = fa
|
|
|
288
290
|
timeslotId: data.timeSnapshot?.legacyId || null,
|
|
289
291
|
totalTickets: ticketQuantity(ticketQuantities),
|
|
290
292
|
ticketDescription: formatTickets(ticketQuantities),
|
|
291
|
-
tickets: ticketsToTicketArray(ticketQuantities),
|
|
293
|
+
tickets: ticketsToTicketArray(ticketQuantities, includePriceBreakdown),
|
|
292
294
|
isCanceled: data.reservationStatus === "CANCELED",
|
|
293
295
|
isNoShow: data.fulfillmentStatusOverride?.status === "NO_SHOW",
|
|
294
296
|
isCheckedIn: data.checkinStatus !== "NONE",
|
|
@@ -389,12 +391,14 @@ function resellerNameFromChannelSnapshot(channelSnapshot) {
|
|
|
389
391
|
}
|
|
390
392
|
return out;
|
|
391
393
|
}
|
|
392
|
-
function ticketsToTicketArray(ticketQuantities) {
|
|
394
|
+
function ticketsToTicketArray(ticketQuantities, includePriceBreakdown) {
|
|
393
395
|
if (!ticketQuantities || ticketQuantities.length === 0) return [];
|
|
394
396
|
return ticketQuantities.map((ticket) => ({
|
|
395
397
|
name: ticket.resourceOptionSnapshot?.name || "Unknown",
|
|
396
398
|
quantity: ticket.quantity || 0,
|
|
397
|
-
ticketId: ticket.resourceOptionSnapshot?.id || "unknown"
|
|
399
|
+
ticketId: ticket.resourceOptionSnapshot?.id || "unknown",
|
|
400
|
+
listPrice: includePriceBreakdown ? mapPrice(ticket.value?.price) : void 0,
|
|
401
|
+
totalValue: includePriceBreakdown ? mapPrice(ticket.value?.total) : void 0
|
|
398
402
|
}));
|
|
399
403
|
}
|
|
400
404
|
function durationInMin(startsAt, endsAt) {
|
|
@@ -712,6 +716,7 @@ var bookingQueryFields = `
|
|
|
712
716
|
source {
|
|
713
717
|
actor {
|
|
714
718
|
app
|
|
719
|
+
name
|
|
715
720
|
}
|
|
716
721
|
}
|
|
717
722
|
}
|
|
@@ -759,9 +764,15 @@ var PRICE_BREAKDOWN_FIELDS = `
|
|
|
759
764
|
taxes { amount formatted }
|
|
760
765
|
tips { amount formatted }
|
|
761
766
|
`;
|
|
767
|
+
var TICKET_VALUE_FIELDS = `
|
|
768
|
+
value {
|
|
769
|
+
price { amount formatted }
|
|
770
|
+
total { amount formatted }
|
|
771
|
+
}
|
|
772
|
+
`;
|
|
762
773
|
function buildBookingsListingQuery(includeGuests, includePriceBreakdown) {
|
|
763
774
|
const guestsSection = includeGuests ? bookingGuestsFields : "";
|
|
764
|
-
const fields = includePriceBreakdown ? bookingQueryFields.replace("value {", `value { ${PRICE_BREAKDOWN_FIELDS}`) : bookingQueryFields;
|
|
775
|
+
const fields = includePriceBreakdown ? bookingQueryFields.replace("value {", `value { ${PRICE_BREAKDOWN_FIELDS}`).replace("ticketQuantities {", `ticketQuantities { ${TICKET_VALUE_FIELDS}`) : bookingQueryFields;
|
|
765
776
|
return `
|
|
766
777
|
query Sales($after: String, $first: Int, $filter: SalesFilter!, $orderBy: SalesOrdering) {
|
|
767
778
|
sales(after: $after, first: $first, filter: $filter, orderBy: $orderBy) {
|
|
@@ -956,14 +967,18 @@ function buildBookingsVariables(params) {
|
|
|
956
967
|
var DEFAULT_PAGE_SIZE2 = 50;
|
|
957
968
|
var DEFAULT_CANCEL_NOTE = "Canceled";
|
|
958
969
|
var DEFAULT_CUSTOMER_MESSAGE = "Charge initiated via API";
|
|
959
|
-
var
|
|
970
|
+
var BOOKING_DB_ID_REGEX = /^b_[a-z0-9]+$/;
|
|
971
|
+
var BOOKING_DISPLAY_ID_REGEX = /^B-[A-Z0-9]+$/;
|
|
972
|
+
var ORDER_DB_ID_REGEX = /^o_[a-z0-9]+$/;
|
|
973
|
+
var ORDER_DISPLAY_ID_REGEX = /^O-[A-Z0-9]+$/;
|
|
960
974
|
var PAYMENT_SOURCE_PREFIX = "ps_";
|
|
961
975
|
var PAYMENT_ID_PREFIX = "pmt_";
|
|
962
976
|
var ALLOWED_PAYMENT_SOURCE_IDS = ["cash/cash", "custom/other", "custom/voucher"];
|
|
963
977
|
var CURRENCY_CODE_REGEX = /^[A-Z]{3}$/;
|
|
964
978
|
var ERROR_ADDON_OPTION_ID_REQUIRED = "addonOptionId is required";
|
|
965
979
|
var ERROR_QUANTITY_INVALID = "quantity must be a positive integer string";
|
|
966
|
-
var
|
|
980
|
+
var ERROR_INVALID_BOOKING_ID = "bookingId is required and must be a valid booking id, e.g. 'b_abc123' or 'B-ABC123'";
|
|
981
|
+
var ERROR_INVALID_ORDER_ID = "orderId is required and must be a valid order id, e.g. 'o_abc123' or 'O-ABC123'";
|
|
967
982
|
var ERROR_BOOKING_NOT_FOUND = "Booking not found";
|
|
968
983
|
var ERROR_MULTIPLE_BOOKINGS_FOUND = "Expected exactly one booking for the provided bookingId";
|
|
969
984
|
var ERROR_NO_ADDON_OPTION_TO_REMOVE = "No confirmed add-on option matching addonOptionId was found on the booking";
|
|
@@ -995,6 +1010,7 @@ var BookingService = class {
|
|
|
995
1010
|
* ```
|
|
996
1011
|
*/
|
|
997
1012
|
async getById(bookingId, options = {}) {
|
|
1013
|
+
assertBookingId(bookingId);
|
|
998
1014
|
const includeGuests = options.includeGuests ?? false;
|
|
999
1015
|
const includePriceBreakdown = options.includePriceBreakdown ?? false;
|
|
1000
1016
|
const body = await this.client.request(
|
|
@@ -1043,6 +1059,7 @@ var BookingService = class {
|
|
|
1043
1059
|
}
|
|
1044
1060
|
/** Returns the guests on a booking (primary guest included). */
|
|
1045
1061
|
async getGuests(bookingId) {
|
|
1062
|
+
assertBookingId(bookingId);
|
|
1046
1063
|
const body = await this.client.request(
|
|
1047
1064
|
SALES_ENDPOINT,
|
|
1048
1065
|
BOOKING_GUESTS_QUERY,
|
|
@@ -1056,6 +1073,7 @@ var BookingService = class {
|
|
|
1056
1073
|
}
|
|
1057
1074
|
/** Returns the payments on file for a booking, or null when not found. */
|
|
1058
1075
|
async getPaymentsOnFile(bookingId) {
|
|
1076
|
+
assertBookingId(bookingId);
|
|
1059
1077
|
const normalized = normalizeBookingId(bookingId);
|
|
1060
1078
|
const body = await this.client.request(
|
|
1061
1079
|
SALES_ENDPOINT,
|
|
@@ -1069,6 +1087,7 @@ var BookingService = class {
|
|
|
1069
1087
|
* booking, or null when the booking is not found.
|
|
1070
1088
|
*/
|
|
1071
1089
|
async appendNote(bookingId, note, mode = "append") {
|
|
1090
|
+
assertBookingId(bookingId);
|
|
1072
1091
|
const normalized = normalizeBookingId(bookingId);
|
|
1073
1092
|
const booking = await this.getById(normalized);
|
|
1074
1093
|
if (!booking) {
|
|
@@ -1087,6 +1106,7 @@ ${note}`;
|
|
|
1087
1106
|
* when not found.
|
|
1088
1107
|
*/
|
|
1089
1108
|
async setCheckinStatus(bookingId, checkedIn) {
|
|
1109
|
+
assertBookingId(bookingId);
|
|
1090
1110
|
const normalized = normalizeBookingId(bookingId);
|
|
1091
1111
|
const checkedInAt = checkedIn ? (/* @__PURE__ */ new Date()).toISOString() : null;
|
|
1092
1112
|
await this.client.request(SALES_ENDPOINT, UPDATE_BOOKING_CHECKIN_MUTATION, {
|
|
@@ -1096,6 +1116,7 @@ ${note}`;
|
|
|
1096
1116
|
}
|
|
1097
1117
|
/** Cancels a booking and returns its id/displayId/status. */
|
|
1098
1118
|
async cancel(bookingId, notes = DEFAULT_CANCEL_NOTE) {
|
|
1119
|
+
assertBookingId(bookingId);
|
|
1099
1120
|
const body = await this.client.request(
|
|
1100
1121
|
SALES_ENDPOINT,
|
|
1101
1122
|
CANCEL_BOOKING_MUTATION,
|
|
@@ -1131,12 +1152,13 @@ ${note}`;
|
|
|
1131
1152
|
* @throws {Error} when `paymentSourceId` is missing or not a `ps_…` id / one
|
|
1132
1153
|
* of `cash/cash`, `custom/other`, `custom/voucher`; when `amount` is not a
|
|
1133
1154
|
* valid number; when `currency` is not a 3-letter uppercase code; when
|
|
1134
|
-
* `idempotencyKey` is empty; when `bookingId`
|
|
1135
|
-
* when the booking or payment source is not found; or when the
|
|
1155
|
+
* `idempotencyKey` is empty; when `bookingId` is not a valid booking id
|
|
1156
|
+
* (`b_…`/`B-…`); when the booking or payment source is not found; or when the
|
|
1157
|
+
* charge fails.
|
|
1136
1158
|
*/
|
|
1137
1159
|
async makePayment(input) {
|
|
1160
|
+
this.validatePaymentInput(input);
|
|
1138
1161
|
const normalized = normalizeBookingId(input.bookingId);
|
|
1139
|
-
this.validatePaymentInput(input, normalized);
|
|
1140
1162
|
const onFile = await this.getPaymentsOnFile(normalized);
|
|
1141
1163
|
if (!onFile) {
|
|
1142
1164
|
throw new Error("Booking not found");
|
|
@@ -1177,8 +1199,8 @@ ${note}`;
|
|
|
1177
1199
|
* payments-on-file, then applies the refund.
|
|
1178
1200
|
*/
|
|
1179
1201
|
async refund(input) {
|
|
1202
|
+
this.validateRefundInput(input);
|
|
1180
1203
|
const normalized = normalizeBookingId(input.bookingId);
|
|
1181
|
-
this.validateRefundInput(input, normalized);
|
|
1182
1204
|
const onFile = await this.getPaymentsOnFile(normalized);
|
|
1183
1205
|
if (!onFile) {
|
|
1184
1206
|
throw new Error("Booking not found");
|
|
@@ -1217,9 +1239,7 @@ ${note}`;
|
|
|
1217
1239
|
}
|
|
1218
1240
|
/** Creates an invoice link for a booking's order. */
|
|
1219
1241
|
async createInvoiceLink(bookingId) {
|
|
1220
|
-
|
|
1221
|
-
throw new Error("bookingId is required");
|
|
1222
|
-
}
|
|
1242
|
+
assertBookingId(bookingId);
|
|
1223
1243
|
const normalized = normalizeBookingId(bookingId);
|
|
1224
1244
|
const booking = await this.getById(normalized);
|
|
1225
1245
|
if (!booking || !booking.orderId) {
|
|
@@ -1239,6 +1259,7 @@ ${note}`;
|
|
|
1239
1259
|
}
|
|
1240
1260
|
/** Returns the add-ons on a booking, grouped by add-on item (clean model). */
|
|
1241
1261
|
async listAddons(bookingId) {
|
|
1262
|
+
assertBookingId(bookingId);
|
|
1242
1263
|
const { items, displayId, orderId, normalizedBookingId } = await this.fetchBookingSale(bookingId);
|
|
1243
1264
|
const addons = items.map((item) => toBookingAddon(item)).filter((addon) => addon !== null);
|
|
1244
1265
|
return {
|
|
@@ -1273,6 +1294,7 @@ ${note}`;
|
|
|
1273
1294
|
* the underlying quote/order mutations fail.
|
|
1274
1295
|
*/
|
|
1275
1296
|
async addAddon(bookingId, input) {
|
|
1297
|
+
assertBookingId(bookingId);
|
|
1276
1298
|
const addonOptionId = (input?.addonOptionId || input?.addonId || "").trim();
|
|
1277
1299
|
if (!addonOptionId) {
|
|
1278
1300
|
throw new Error(ERROR_ADDON_OPTION_ID_REQUIRED);
|
|
@@ -1325,6 +1347,7 @@ ${note}`;
|
|
|
1325
1347
|
* variables. Returns the booking's add-ons after the change.
|
|
1326
1348
|
*/
|
|
1327
1349
|
async removeAddon(bookingId, input) {
|
|
1350
|
+
assertBookingId(bookingId);
|
|
1328
1351
|
const addonOptionId = (input?.addonOptionId || input?.addonId || "").trim();
|
|
1329
1352
|
if (!addonOptionId) {
|
|
1330
1353
|
throw new Error(ERROR_ADDON_OPTION_ID_REQUIRED);
|
|
@@ -1353,14 +1376,10 @@ ${note}`;
|
|
|
1353
1376
|
* booking matched, and parses the node into the internal model.
|
|
1354
1377
|
*/
|
|
1355
1378
|
async fetchBookingSale(bookingId) {
|
|
1356
|
-
const searchString = (bookingId || "").trim();
|
|
1357
|
-
if (!searchString) {
|
|
1358
|
-
throw new Error(ERROR_BOOKING_ID_REQUIRED);
|
|
1359
|
-
}
|
|
1360
1379
|
const body = await this.client.request(
|
|
1361
1380
|
SALES_ENDPOINT,
|
|
1362
1381
|
SALES_ADDONS_QUERY,
|
|
1363
|
-
buildSalesAddonsVariables(
|
|
1382
|
+
buildSalesAddonsVariables(bookingId)
|
|
1364
1383
|
);
|
|
1365
1384
|
const edges = body.data?.sales?.edges ?? [];
|
|
1366
1385
|
if (edges.length === 0) {
|
|
@@ -1543,7 +1562,7 @@ ${note}`;
|
|
|
1543
1562
|
}
|
|
1544
1563
|
return matched.productId;
|
|
1545
1564
|
}
|
|
1546
|
-
validatePaymentInput(input
|
|
1565
|
+
validatePaymentInput(input) {
|
|
1547
1566
|
if (!input.paymentSourceId || !input.paymentSourceId.startsWith(PAYMENT_SOURCE_PREFIX) && !ALLOWED_PAYMENT_SOURCE_IDS.includes(input.paymentSourceId)) {
|
|
1548
1567
|
throw new Error(
|
|
1549
1568
|
"paymentSourceId is required and must start with 'ps_' or be one of 'cash/cash', 'custom/other', 'custom/voucher'"
|
|
@@ -1552,16 +1571,16 @@ ${note}`;
|
|
|
1552
1571
|
assertAmount(input.amount);
|
|
1553
1572
|
assertCurrency(input.currency);
|
|
1554
1573
|
assertIdempotencyKey(input.idempotencyKey);
|
|
1555
|
-
assertBookingId(
|
|
1574
|
+
assertBookingId(input.bookingId);
|
|
1556
1575
|
}
|
|
1557
|
-
validateRefundInput(input
|
|
1576
|
+
validateRefundInput(input) {
|
|
1558
1577
|
if (!input.paymentId || !input.paymentId.startsWith(PAYMENT_ID_PREFIX)) {
|
|
1559
1578
|
throw new Error("paymentId is required and must start with 'pmt_'");
|
|
1560
1579
|
}
|
|
1561
1580
|
assertAmount(input.amount);
|
|
1562
1581
|
assertCurrency(input.currency);
|
|
1563
1582
|
assertIdempotencyKey(input.idempotencyKey);
|
|
1564
|
-
assertBookingId(
|
|
1583
|
+
assertBookingId(input.bookingId);
|
|
1565
1584
|
}
|
|
1566
1585
|
async fetchPaginated(query, baseParams, includeGuests, includePriceBreakdown) {
|
|
1567
1586
|
const bookings = [];
|
|
@@ -1601,9 +1620,14 @@ function assertIdempotencyKey(idempotencyKey) {
|
|
|
1601
1620
|
throw new Error("idempotencyKey is required");
|
|
1602
1621
|
}
|
|
1603
1622
|
}
|
|
1604
|
-
function assertBookingId(
|
|
1605
|
-
if (!
|
|
1606
|
-
throw new Error(
|
|
1623
|
+
function assertBookingId(bookingId) {
|
|
1624
|
+
if (!(BOOKING_DB_ID_REGEX.test(bookingId) || BOOKING_DISPLAY_ID_REGEX.test(bookingId))) {
|
|
1625
|
+
throw new Error(ERROR_INVALID_BOOKING_ID);
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
function assertOrderId(orderId) {
|
|
1629
|
+
if (!(ORDER_DB_ID_REGEX.test(orderId) || ORDER_DISPLAY_ID_REGEX.test(orderId))) {
|
|
1630
|
+
throw new Error(ERROR_INVALID_ORDER_ID);
|
|
1607
1631
|
}
|
|
1608
1632
|
}
|
|
1609
1633
|
function validateCreateInput(input) {
|
|
@@ -1621,6 +1645,9 @@ function validateCreateInput(input) {
|
|
|
1621
1645
|
if (input.markAsPaid && !input.idempotencyKey) {
|
|
1622
1646
|
throw new Error("idempotencyKey is required when markAsPaid is set");
|
|
1623
1647
|
}
|
|
1648
|
+
if (input.parentOrderId) {
|
|
1649
|
+
assertOrderId(input.parentOrderId);
|
|
1650
|
+
}
|
|
1624
1651
|
}
|
|
1625
1652
|
function parseQuantity(value) {
|
|
1626
1653
|
if (typeof value !== "string" && typeof value !== "number") {
|
|
@@ -2325,7 +2352,7 @@ function fromTimeslotNode(node, productId) {
|
|
|
2325
2352
|
durationMin: node.minuteLength ?? 0,
|
|
2326
2353
|
date: node.date || "",
|
|
2327
2354
|
startTime: node.start ?? null,
|
|
2328
|
-
assignedResources: mapAssignedResources(node.
|
|
2355
|
+
assignedResources: mapAssignedResources(node.inheritedResourceAllocations)
|
|
2329
2356
|
};
|
|
2330
2357
|
}
|
|
2331
2358
|
function mapAssignedResources(allocations) {
|
|
@@ -2335,11 +2362,11 @@ function mapAssignedResources(allocations) {
|
|
|
2335
2362
|
return allocations.map((allocation) => {
|
|
2336
2363
|
const pool = allocation.resourcePool;
|
|
2337
2364
|
return {
|
|
2338
|
-
id: pool?.id || "",
|
|
2339
2365
|
name: pool?.name || "",
|
|
2340
2366
|
capacity: pool?.capacity ?? 0,
|
|
2341
2367
|
category: pool?.category || "",
|
|
2342
2368
|
quantity: allocation.quantity ?? 0,
|
|
2369
|
+
status: allocation.status || "",
|
|
2343
2370
|
accountUserId: pool?.accountUser?.id ?? null
|
|
2344
2371
|
};
|
|
2345
2372
|
});
|
|
@@ -2414,10 +2441,10 @@ var TIMESLOTS_QUERY = `
|
|
|
2414
2441
|
minuteLength
|
|
2415
2442
|
status
|
|
2416
2443
|
date
|
|
2417
|
-
|
|
2444
|
+
inheritedResourceAllocations {
|
|
2418
2445
|
quantity
|
|
2446
|
+
status
|
|
2419
2447
|
resourcePool {
|
|
2420
|
-
id
|
|
2421
2448
|
name
|
|
2422
2449
|
category
|
|
2423
2450
|
capacity
|
|
@@ -2452,10 +2479,10 @@ var TIMESLOT_BY_ID_QUERY = `
|
|
|
2452
2479
|
minuteLength
|
|
2453
2480
|
status
|
|
2454
2481
|
date
|
|
2455
|
-
|
|
2482
|
+
inheritedResourceAllocations {
|
|
2456
2483
|
quantity
|
|
2484
|
+
status
|
|
2457
2485
|
resourcePool {
|
|
2458
|
-
id
|
|
2459
2486
|
name
|
|
2460
2487
|
category
|
|
2461
2488
|
capacity
|