@peektravel/app-utilities 0.9.2 → 0.9.4
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/README.md +5 -1
- package/dist/index.cjs +557 -70
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +287 -5
- package/dist/index.d.ts +287 -5
- package/dist/index.js +557 -70
- package/dist/index.js.map +1 -1
- package/docs/webhooks.md +1 -1
- package/llms.txt +26 -4
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -338,6 +338,20 @@ var SEARCH_BY_ACTIVITY_DATE = "activityDate";
|
|
|
338
338
|
function normalizeBookingId(bookingId) {
|
|
339
339
|
return bookingId.toLowerCase().replace(/-/g, "_");
|
|
340
340
|
}
|
|
341
|
+
function buildSalesQuery(nodeBody) {
|
|
342
|
+
return `
|
|
343
|
+
query Sales($after: String, $first: Int, $filter: SalesFilter!, $orderBy: SalesOrdering) {
|
|
344
|
+
sales(after: $after, first: $first, filter: $filter, orderBy: $orderBy) {
|
|
345
|
+
pageInfo { endCursor hasNextPage }
|
|
346
|
+
edges {
|
|
347
|
+
node {
|
|
348
|
+
${nodeBody}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
`;
|
|
354
|
+
}
|
|
341
355
|
var GUEST_IDENTITY_PII_FIELDS = "name country dateOfBirth email isGdpr";
|
|
342
356
|
var GUEST_CONTACT_PII_FIELDS = `phone
|
|
343
357
|
postalCode
|
|
@@ -431,6 +445,7 @@ function buildBookingQueryFields(fullCustomerAccess) {
|
|
|
431
445
|
availabilityTimeId
|
|
432
446
|
${fullCustomerAccess ? "bookingPortalUrl" : ""}
|
|
433
447
|
operatorNotes
|
|
448
|
+
operatorStatus
|
|
434
449
|
value {
|
|
435
450
|
total {
|
|
436
451
|
formatted
|
|
@@ -513,67 +528,40 @@ function buildBookingsListingQuery(includeGuests, includePriceBreakdown, fullCus
|
|
|
513
528
|
const guestsSection = includeGuests ? buildBookingGuestsFields(fullCustomerAccess) : "";
|
|
514
529
|
const baseFields = buildBookingQueryFields(fullCustomerAccess);
|
|
515
530
|
const fields = includePriceBreakdown ? baseFields.replace("value {", `value { ${PRICE_BREAKDOWN_FIELDS}`).replace("ticketQuantities {", `ticketQuantities { ${TICKET_VALUE_FIELDS}`) : baseFields;
|
|
516
|
-
return `
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
edges {
|
|
521
|
-
node {
|
|
522
|
-
... on Booking {
|
|
523
|
-
${fields}
|
|
524
|
-
${guestsSection}
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
}
|
|
531
|
+
return buildSalesQuery(`
|
|
532
|
+
... on Booking {
|
|
533
|
+
${fields}
|
|
534
|
+
${guestsSection}
|
|
529
535
|
}
|
|
530
|
-
|
|
536
|
+
`);
|
|
531
537
|
}
|
|
532
538
|
function buildBookingGuestsQuery(fullCustomerAccess) {
|
|
533
|
-
return `
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
node {
|
|
539
|
-
... on Booking {
|
|
540
|
-
displayId
|
|
541
|
-
id
|
|
542
|
-
${buildBookingGuestsFields(fullCustomerAccess)}
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
}
|
|
539
|
+
return buildSalesQuery(`
|
|
540
|
+
... on Booking {
|
|
541
|
+
displayId
|
|
542
|
+
id
|
|
543
|
+
${buildBookingGuestsFields(fullCustomerAccess)}
|
|
546
544
|
}
|
|
547
|
-
|
|
548
|
-
`;
|
|
545
|
+
`);
|
|
549
546
|
}
|
|
550
|
-
var BOOKING_PAYMENTS_ON_FILE_QUERY = `
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
id
|
|
559
|
-
paymentSource { id }
|
|
560
|
-
appliedAt
|
|
561
|
-
currentAmount { amount currency }
|
|
562
|
-
refundableAmount { amount currency }
|
|
563
|
-
}
|
|
564
|
-
id
|
|
565
|
-
displayId
|
|
566
|
-
paymentSources { description type id }
|
|
567
|
-
}
|
|
568
|
-
... on Booking {
|
|
569
|
-
displayId
|
|
570
|
-
id
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
}
|
|
547
|
+
var BOOKING_PAYMENTS_ON_FILE_QUERY = buildSalesQuery(`
|
|
548
|
+
order {
|
|
549
|
+
payments {
|
|
550
|
+
id
|
|
551
|
+
paymentSource { id }
|
|
552
|
+
appliedAt
|
|
553
|
+
currentAmount { amount currency }
|
|
554
|
+
refundableAmount { amount currency }
|
|
574
555
|
}
|
|
556
|
+
id
|
|
557
|
+
displayId
|
|
558
|
+
paymentSources { description type id }
|
|
575
559
|
}
|
|
576
|
-
|
|
560
|
+
... on Booking {
|
|
561
|
+
displayId
|
|
562
|
+
id
|
|
563
|
+
}
|
|
564
|
+
`);
|
|
577
565
|
var UPDATE_OPERATOR_NOTES_MUTATION = `
|
|
578
566
|
mutation Account($input: UpdateOperatorNotesForBookingInput!) {
|
|
579
567
|
updateOperatorNotesForBooking(input: $input) {
|
|
@@ -581,6 +569,13 @@ var UPDATE_OPERATOR_NOTES_MUTATION = `
|
|
|
581
569
|
}
|
|
582
570
|
}
|
|
583
571
|
`;
|
|
572
|
+
var UPDATE_OPERATOR_STATUS_MUTATION = `
|
|
573
|
+
mutation Account($input: UpdateOperatorStatusForBookingInput!) {
|
|
574
|
+
updateOperatorStatusForBooking(input: $input) {
|
|
575
|
+
booking { operatorStatus }
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
`;
|
|
584
579
|
var UPDATE_BOOKING_CHECKIN_MUTATION = `
|
|
585
580
|
mutation Account($input: UpdateBookingCheckInInput!) {
|
|
586
581
|
updateBookingCheckIn(input: $input) {
|
|
@@ -803,6 +798,7 @@ function fromBookingNode(node, includeGuests = false, includePriceBreakdown = fa
|
|
|
803
798
|
availabilityTimeId: data.availabilityTimeId || null,
|
|
804
799
|
portalUrl: data.bookingPortalUrl || null,
|
|
805
800
|
notes: data.operatorNotes || "",
|
|
801
|
+
customStatus: data.operatorStatus || null,
|
|
806
802
|
valueDisplay: data.value?.total?.formatted || "",
|
|
807
803
|
valueAmount: data.value?.total?.amount || "",
|
|
808
804
|
outstandingBalanceAmount: data.balance?.total?.amount || "",
|
|
@@ -957,6 +953,261 @@ function fromBookingGuestsResponse(response) {
|
|
|
957
953
|
return guests;
|
|
958
954
|
}
|
|
959
955
|
|
|
956
|
+
// src/internal/peek/bookings/metadata-converter.ts
|
|
957
|
+
var UPSERT_SUCCESS_TYPENAME = "UpsertBookingFieldResponsesSuccess";
|
|
958
|
+
function fromBookingMetaDataNode(node) {
|
|
959
|
+
return (node?.fieldResponses ?? []).map(fromFieldResponseNode);
|
|
960
|
+
}
|
|
961
|
+
function fromFieldResponseNode(response) {
|
|
962
|
+
const field = response.fieldLocation?.field;
|
|
963
|
+
const prompt = response.fieldLocation?.prompt;
|
|
964
|
+
const { value, displayValue } = fromValueNode(response.value);
|
|
965
|
+
return {
|
|
966
|
+
id: field?.id || "",
|
|
967
|
+
name: field?.name || "",
|
|
968
|
+
slug: field?.slug || "",
|
|
969
|
+
type: field?.type || "",
|
|
970
|
+
prompt: prompt?.label ?? null,
|
|
971
|
+
promptHint: prompt?.hint ?? null,
|
|
972
|
+
isRequired: Boolean(prompt?.isRequired),
|
|
973
|
+
refid: response.refid || "",
|
|
974
|
+
displayValue,
|
|
975
|
+
value
|
|
976
|
+
};
|
|
977
|
+
}
|
|
978
|
+
function fromValueNode(value) {
|
|
979
|
+
if (!value || !value.__typename) return { value: null, displayValue: "" };
|
|
980
|
+
switch (value.__typename) {
|
|
981
|
+
case "AgeFieldResponseValue": {
|
|
982
|
+
const age = value.age ?? 0;
|
|
983
|
+
return { value: { kind: "age", age }, displayValue: String(age) };
|
|
984
|
+
}
|
|
985
|
+
case "AttachmentFieldResponseValue": {
|
|
986
|
+
const attachmentUrl = value.attachmentUrl ?? "";
|
|
987
|
+
const name = value.name ?? null;
|
|
988
|
+
return {
|
|
989
|
+
value: {
|
|
990
|
+
kind: "attachment",
|
|
991
|
+
attachmentUrl,
|
|
992
|
+
mime: value.mime ?? null,
|
|
993
|
+
name,
|
|
994
|
+
size: value.size ?? null
|
|
995
|
+
},
|
|
996
|
+
displayValue: name || attachmentUrl
|
|
997
|
+
};
|
|
998
|
+
}
|
|
999
|
+
case "BarcodeFieldResponseValue": {
|
|
1000
|
+
const barcodeValue = value.barcodeValue ?? "";
|
|
1001
|
+
return {
|
|
1002
|
+
value: {
|
|
1003
|
+
kind: "barcode",
|
|
1004
|
+
barcodeType: value.barcodeType ?? null,
|
|
1005
|
+
barcodeValue
|
|
1006
|
+
},
|
|
1007
|
+
displayValue: barcodeValue
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
1010
|
+
case "BooleanFieldResponseValue": {
|
|
1011
|
+
const bool = Boolean(value.boolean);
|
|
1012
|
+
return { value: { kind: "boolean", boolean: bool }, displayValue: yesNo(bool) };
|
|
1013
|
+
}
|
|
1014
|
+
case "CheckboxFieldResponseValue": {
|
|
1015
|
+
const isChecked = Boolean(value.isChecked);
|
|
1016
|
+
return { value: { kind: "checkbox", isChecked }, displayValue: yesNo(isChecked) };
|
|
1017
|
+
}
|
|
1018
|
+
case "CurrencyFieldResponseValue": {
|
|
1019
|
+
const currency = value.currency ?? "";
|
|
1020
|
+
return { value: { kind: "currency", currency }, displayValue: currency };
|
|
1021
|
+
}
|
|
1022
|
+
case "DateFieldResponseValue": {
|
|
1023
|
+
const date = value.date ?? "";
|
|
1024
|
+
return { value: { kind: "date", date }, displayValue: date };
|
|
1025
|
+
}
|
|
1026
|
+
case "DecimalFieldResponseValue": {
|
|
1027
|
+
const decimal = value.decimal ?? null;
|
|
1028
|
+
return { value: { kind: "decimal", decimal }, displayValue: decimal ?? "" };
|
|
1029
|
+
}
|
|
1030
|
+
case "DurationFieldResponseValue": {
|
|
1031
|
+
const amount = value.duration?.amount ?? 0;
|
|
1032
|
+
const unit = value.duration?.unit ?? null;
|
|
1033
|
+
return { value: { kind: "duration", amount, unit }, displayValue: amountUnit(amount, unit) };
|
|
1034
|
+
}
|
|
1035
|
+
case "EmailFieldResponseValue": {
|
|
1036
|
+
const email = value.email ?? null;
|
|
1037
|
+
return { value: { kind: "email", email }, displayValue: email ?? "" };
|
|
1038
|
+
}
|
|
1039
|
+
case "GuestFieldResponseValue": {
|
|
1040
|
+
const name = value.name ?? null;
|
|
1041
|
+
return {
|
|
1042
|
+
value: {
|
|
1043
|
+
kind: "guest",
|
|
1044
|
+
name,
|
|
1045
|
+
email: value.email ?? null,
|
|
1046
|
+
dateOfBirth: value.dateOfBirth ?? null,
|
|
1047
|
+
notes: value.notes ?? null,
|
|
1048
|
+
waiverSigned: value.waiverSigned ?? null
|
|
1049
|
+
},
|
|
1050
|
+
displayValue: name ?? ""
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
1053
|
+
case "HtmlFieldResponseValue": {
|
|
1054
|
+
const html = value.html ?? "";
|
|
1055
|
+
return { value: { kind: "html", html }, displayValue: escapeHtml(html) };
|
|
1056
|
+
}
|
|
1057
|
+
case "ImageUrlFieldResponseValue": {
|
|
1058
|
+
const imageUrl = value.imageUrl ?? "";
|
|
1059
|
+
return { value: { kind: "imageUrl", imageUrl }, displayValue: imageUrl };
|
|
1060
|
+
}
|
|
1061
|
+
case "IntegerFieldResponseValue": {
|
|
1062
|
+
const integer = value.integer ?? 0;
|
|
1063
|
+
return { value: { kind: "integer", integer }, displayValue: String(integer) };
|
|
1064
|
+
}
|
|
1065
|
+
case "LocationFieldResponseValue": {
|
|
1066
|
+
const streetAddress = value.streetAddress ?? null;
|
|
1067
|
+
const locality = value.locality ?? null;
|
|
1068
|
+
const region = value.region ?? null;
|
|
1069
|
+
const postalCode = value.postalCode ?? null;
|
|
1070
|
+
const country = value.country ?? null;
|
|
1071
|
+
return {
|
|
1072
|
+
value: {
|
|
1073
|
+
kind: "location",
|
|
1074
|
+
country,
|
|
1075
|
+
county: value.county ?? null,
|
|
1076
|
+
locality,
|
|
1077
|
+
postalCode,
|
|
1078
|
+
region,
|
|
1079
|
+
streetAddress
|
|
1080
|
+
},
|
|
1081
|
+
displayValue: [streetAddress, locality, region, postalCode, country].filter((part) => part).join(", ")
|
|
1082
|
+
};
|
|
1083
|
+
}
|
|
1084
|
+
case "LongTextFieldResponseValue": {
|
|
1085
|
+
const longText = value.longText ?? "";
|
|
1086
|
+
return { value: { kind: "longText", longText }, displayValue: longText };
|
|
1087
|
+
}
|
|
1088
|
+
case "MetaFieldResponseValue": {
|
|
1089
|
+
const meta = value.meta ?? null;
|
|
1090
|
+
return { value: { kind: "meta", meta }, displayValue: meta == null ? "" : JSON.stringify(meta) };
|
|
1091
|
+
}
|
|
1092
|
+
case "PercentFieldResponseValue": {
|
|
1093
|
+
const percent = value.percent ?? null;
|
|
1094
|
+
return { value: { kind: "percent", percent }, displayValue: percent == null ? "" : `${percent}%` };
|
|
1095
|
+
}
|
|
1096
|
+
case "PhoneFieldResponseValue": {
|
|
1097
|
+
const phone = value.phone ?? "";
|
|
1098
|
+
return { value: { kind: "phone", phone }, displayValue: phone };
|
|
1099
|
+
}
|
|
1100
|
+
case "ShortTextFieldResponseValue": {
|
|
1101
|
+
const shortText = value.shortText ?? "";
|
|
1102
|
+
return { value: { kind: "shortText", shortText }, displayValue: shortText };
|
|
1103
|
+
}
|
|
1104
|
+
case "TimeFieldResponseValue": {
|
|
1105
|
+
const time = value.time ?? "";
|
|
1106
|
+
return { value: { kind: "time", time }, displayValue: time };
|
|
1107
|
+
}
|
|
1108
|
+
case "UrlFieldResponseValue": {
|
|
1109
|
+
const url = value.url ?? null;
|
|
1110
|
+
return { value: { kind: "url", url }, displayValue: url ?? "" };
|
|
1111
|
+
}
|
|
1112
|
+
case "VolumeFieldResponseValue": {
|
|
1113
|
+
const amount = value.volume?.amount ?? "0";
|
|
1114
|
+
const unit = value.volume?.unit ?? null;
|
|
1115
|
+
return { value: { kind: "volume", amount, unit }, displayValue: amountUnit(amount, unit) };
|
|
1116
|
+
}
|
|
1117
|
+
case "WeightFieldResponseValue": {
|
|
1118
|
+
const amount = value.weight?.amount ?? "0";
|
|
1119
|
+
const unit = value.weight?.unit ?? null;
|
|
1120
|
+
return { value: { kind: "weight", amount, unit }, displayValue: amountUnit(amount, unit) };
|
|
1121
|
+
}
|
|
1122
|
+
default:
|
|
1123
|
+
return { value: null, displayValue: "" };
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
function yesNo(value) {
|
|
1127
|
+
return value ? "Yes" : "No";
|
|
1128
|
+
}
|
|
1129
|
+
function amountUnit(amount, unit) {
|
|
1130
|
+
return unit ? `${amount} ${unit.toLowerCase()}` : String(amount);
|
|
1131
|
+
}
|
|
1132
|
+
function escapeHtml(value) {
|
|
1133
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
1134
|
+
}
|
|
1135
|
+
function toSetMetaDataResult(node, bookingId) {
|
|
1136
|
+
return {
|
|
1137
|
+
success: node?.__typename === UPSERT_SUCCESS_TYPENAME,
|
|
1138
|
+
bookingId: node?.bookingId || bookingId,
|
|
1139
|
+
message: node?.message || ""
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
function filterMetaDataByIntegrator(metaData, integrator) {
|
|
1143
|
+
const prefix = `integrators:${integrator}:`;
|
|
1144
|
+
return metaData.filter((entry) => entry.slug.startsWith(prefix)).map((entry) => ({ ...entry, slug: entry.slug.slice(prefix.length) }));
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
// src/internal/peek/bookings/metadata-queries.ts
|
|
1148
|
+
var GUEST_VALUE_PII_FIELDS = "dateOfBirth\n email\n name\n ";
|
|
1149
|
+
function buildGuestValueFields(fullCustomerAccess) {
|
|
1150
|
+
return `... on GuestFieldResponseValue {
|
|
1151
|
+
${fullCustomerAccess ? GUEST_VALUE_PII_FIELDS : ""}notes
|
|
1152
|
+
waiverSigned
|
|
1153
|
+
}`;
|
|
1154
|
+
}
|
|
1155
|
+
function buildMetaDataValueSelection(fullCustomerAccess) {
|
|
1156
|
+
return `value {
|
|
1157
|
+
__typename
|
|
1158
|
+
... on AgeFieldResponseValue { age }
|
|
1159
|
+
... on AttachmentFieldResponseValue { attachmentUrl mime name size }
|
|
1160
|
+
... on BarcodeFieldResponseValue { barcodeType barcodeValue }
|
|
1161
|
+
... on BooleanFieldResponseValue { boolean }
|
|
1162
|
+
... on CheckboxFieldResponseValue { isChecked }
|
|
1163
|
+
... on CurrencyFieldResponseValue { currency }
|
|
1164
|
+
... on DateFieldResponseValue { date }
|
|
1165
|
+
... on DecimalFieldResponseValue { decimal }
|
|
1166
|
+
... on DurationFieldResponseValue { duration { amount unit } }
|
|
1167
|
+
... on EmailFieldResponseValue { email }
|
|
1168
|
+
${buildGuestValueFields(fullCustomerAccess)}
|
|
1169
|
+
... on HtmlFieldResponseValue { html }
|
|
1170
|
+
... on ImageUrlFieldResponseValue { imageUrl }
|
|
1171
|
+
... on IntegerFieldResponseValue { integer }
|
|
1172
|
+
... on LocationFieldResponseValue { country county locality postalCode region streetAddress }
|
|
1173
|
+
... on LongTextFieldResponseValue { longText }
|
|
1174
|
+
... on MetaFieldResponseValue { meta }
|
|
1175
|
+
... on PercentFieldResponseValue { percent }
|
|
1176
|
+
... on PhoneFieldResponseValue { phone }
|
|
1177
|
+
... on ShortTextFieldResponseValue { shortText }
|
|
1178
|
+
... on TimeFieldResponseValue { time }
|
|
1179
|
+
... on UrlFieldResponseValue { url }
|
|
1180
|
+
... on VolumeFieldResponseValue { volume { amount unit } }
|
|
1181
|
+
... on WeightFieldResponseValue { weight { amount unit } }
|
|
1182
|
+
}`;
|
|
1183
|
+
}
|
|
1184
|
+
function buildBookingMetaDataQuery(fullCustomerAccess) {
|
|
1185
|
+
return buildSalesQuery(`
|
|
1186
|
+
... on Booking {
|
|
1187
|
+
displayId
|
|
1188
|
+
id
|
|
1189
|
+
fieldResponses {
|
|
1190
|
+
fieldLocation {
|
|
1191
|
+
field { id name slug type }
|
|
1192
|
+
prompt { label hint isRequired }
|
|
1193
|
+
}
|
|
1194
|
+
refid
|
|
1195
|
+
${buildMetaDataValueSelection(fullCustomerAccess)}
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
`);
|
|
1199
|
+
}
|
|
1200
|
+
var UPSERT_BOOKING_FIELD_RESPONSES_MUTATION = `
|
|
1201
|
+
mutation UpsertBookingFieldResponses($input: UpsertBookingFieldResponsesInput!) {
|
|
1202
|
+
upsertBookingFieldResponses(input: $input) {
|
|
1203
|
+
__typename
|
|
1204
|
+
... on UpsertBookingFieldResponsesSuccess { bookingId message }
|
|
1205
|
+
... on BookingNotFoundError { message bookingId }
|
|
1206
|
+
... on GenericError { message }
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
`;
|
|
1210
|
+
|
|
960
1211
|
// src/internal/peek/bookings/payments-on-file-converter.ts
|
|
961
1212
|
function fromPaymentsOnFileResponse(response, bookingId) {
|
|
962
1213
|
const firstEdge = (response?.sales?.edges ?? [])[0];
|
|
@@ -1216,10 +1467,41 @@ function assertRequiredAnswered(resolved, questions) {
|
|
|
1216
1467
|
}
|
|
1217
1468
|
}
|
|
1218
1469
|
|
|
1470
|
+
// src/internal/peek/bookings/list-price.ts
|
|
1471
|
+
var ERROR_LIST_PRICE_INVALID = "listPrice must be a positive number string, e.g. '100', '1,000', or '10.00'";
|
|
1472
|
+
function parseListPriceToCents(listPrice) {
|
|
1473
|
+
if (typeof listPrice !== "string") {
|
|
1474
|
+
throw new Error(ERROR_LIST_PRICE_INVALID);
|
|
1475
|
+
}
|
|
1476
|
+
const cleaned = listPrice.replace(/,/g, "").trim();
|
|
1477
|
+
if (!/^\d+(\.\d+)?$/.test(cleaned)) {
|
|
1478
|
+
throw new Error(ERROR_LIST_PRICE_INVALID);
|
|
1479
|
+
}
|
|
1480
|
+
const cents = Math.round(Number(cleaned) * 100);
|
|
1481
|
+
if (!Number.isFinite(cents) || cents <= 0) {
|
|
1482
|
+
throw new Error(ERROR_LIST_PRICE_INVALID);
|
|
1483
|
+
}
|
|
1484
|
+
return cents;
|
|
1485
|
+
}
|
|
1486
|
+
function centsToAmount(cents) {
|
|
1487
|
+
return (cents / 100).toFixed(2);
|
|
1488
|
+
}
|
|
1489
|
+
function distributeListPrice(listPrice, ticketCount) {
|
|
1490
|
+
const totalCents = parseListPriceToCents(listPrice);
|
|
1491
|
+
const perTicketCents = Math.floor(totalCents / ticketCount);
|
|
1492
|
+
const amounts = [];
|
|
1493
|
+
for (let i = 0; i < ticketCount - 1; i += 1) {
|
|
1494
|
+
amounts.push(centsToAmount(perTicketCents));
|
|
1495
|
+
}
|
|
1496
|
+
amounts.push(centsToAmount(totalCents - perTicketCents * (ticketCount - 1)));
|
|
1497
|
+
return amounts;
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1219
1500
|
// src/internal/peek/bookings/booking-service.ts
|
|
1220
1501
|
var DEFAULT_PAGE_SIZE2 = 50;
|
|
1221
1502
|
var DEFAULT_CANCEL_NOTE = "Canceled";
|
|
1222
1503
|
var DEFAULT_CUSTOMER_MESSAGE = "Charge initiated via API";
|
|
1504
|
+
var DEFAULT_CURRENCY = "USD";
|
|
1223
1505
|
var BOOKING_DB_ID_REGEX = /^b_[a-z0-9]+$/;
|
|
1224
1506
|
var BOOKING_DISPLAY_ID_REGEX = /^B-[A-Z0-9]+$/;
|
|
1225
1507
|
var ORDER_DB_ID_REGEX = /^o_[a-z0-9]+$/;
|
|
@@ -1232,6 +1514,17 @@ var ERROR_ADDON_OPTION_ID_REQUIRED = "addonOptionId is required";
|
|
|
1232
1514
|
var ERROR_QUANTITY_INVALID = "quantity must be a positive integer string";
|
|
1233
1515
|
var ERROR_INVALID_BOOKING_ID = "bookingId is required and must be a valid booking id, e.g. 'b_abc123' or 'B-ABC123'";
|
|
1234
1516
|
var ERROR_INVALID_ORDER_ID = "orderId is required and must be a valid order id, e.g. 'o_abc123' or 'O-ABC123'";
|
|
1517
|
+
var ERROR_CUSTOM_STATUS_REQUIRED = "customStatus is required and must be a non-empty string";
|
|
1518
|
+
var ERROR_GUESTS_REQUIRED = "at least one guest is required";
|
|
1519
|
+
var ERROR_ATTACHMENTS_REQUIRED = "at least one attachment is required";
|
|
1520
|
+
var META_FIELD_GUEST = "guest";
|
|
1521
|
+
var META_FIELD_ATTACHMENT = "attachment";
|
|
1522
|
+
var META_FIELD_MANIFEST_URL = "manifest_url";
|
|
1523
|
+
var META_FIELD_RESERVATION_ID = "reservation_id";
|
|
1524
|
+
var META_FIELD_BOOKING_STATUS = "booking_status";
|
|
1525
|
+
var META_FIELD_ASSIGNED_PRODUCT = "assigned_product";
|
|
1526
|
+
var META_FIELD_ASSIGNED_EMPLOYEE = "assigned_employee";
|
|
1527
|
+
var META_FIELD_INSURANCE_PURCHASED = "insurance_purchased";
|
|
1235
1528
|
var ERROR_BOOKING_NOT_FOUND = "Booking not found";
|
|
1236
1529
|
var ERROR_MULTIPLE_BOOKINGS_FOUND = "Expected exactly one booking for the provided bookingId";
|
|
1237
1530
|
var ERROR_NO_ADDON_OPTION_TO_REMOVE = "No confirmed add-on option matching addonOptionId was found on the booking";
|
|
@@ -1244,12 +1537,15 @@ var BookingService = class {
|
|
|
1244
1537
|
this.deps = deps;
|
|
1245
1538
|
this.pageSize = options.pageSize ?? DEFAULT_PAGE_SIZE2;
|
|
1246
1539
|
this.fullCustomerAccess = resolveAccessOptions(options.accessOptions).fullCustomerAccess;
|
|
1540
|
+
this.integrator = options.integrator ?? "";
|
|
1247
1541
|
}
|
|
1248
1542
|
client;
|
|
1249
1543
|
deps;
|
|
1250
1544
|
pageSize;
|
|
1251
1545
|
/** Whether customer PII is requested and payment operations are allowed. */
|
|
1252
1546
|
fullCustomerAccess;
|
|
1547
|
+
/** Integrator id scoping `getMetaData` results. */
|
|
1548
|
+
integrator;
|
|
1253
1549
|
/**
|
|
1254
1550
|
* Throws a {@link PiiAccessDisabledError} for `operation` when this service was
|
|
1255
1551
|
* created without `fullCustomerAccess` — used to gate payment and booking-modification
|
|
@@ -1337,6 +1633,131 @@ var BookingService = class {
|
|
|
1337
1633
|
);
|
|
1338
1634
|
return fromBookingGuestsResponse(body.data);
|
|
1339
1635
|
}
|
|
1636
|
+
/**
|
|
1637
|
+
* Returns a booking's custom-field metadata (its `fieldResponses`), scoped to
|
|
1638
|
+
* this service's integrator. Only fields whose slug starts with
|
|
1639
|
+
* `integrators:<integrator>:` are returned, and that prefix is stripped from
|
|
1640
|
+
* each returned `slug`. Returns `[]` when the booking is not found or has no
|
|
1641
|
+
* matching fields.
|
|
1642
|
+
*
|
|
1643
|
+
* @example
|
|
1644
|
+
* ```ts
|
|
1645
|
+
* const meta = await peek.getBookingService().getMetaData("b_abc123");
|
|
1646
|
+
* for (const field of meta) {
|
|
1647
|
+
* if (field.value?.kind === "url") console.log(field.slug, field.value.url);
|
|
1648
|
+
* }
|
|
1649
|
+
* ```
|
|
1650
|
+
*/
|
|
1651
|
+
async getMetaData(bookingId) {
|
|
1652
|
+
assertBookingId(bookingId);
|
|
1653
|
+
const body = await this.client.request(
|
|
1654
|
+
SALES_ENDPOINT,
|
|
1655
|
+
buildBookingMetaDataQuery(this.fullCustomerAccess),
|
|
1656
|
+
buildBookingsVariables({
|
|
1657
|
+
pageSize: this.pageSize,
|
|
1658
|
+
after: null,
|
|
1659
|
+
bookingId: normalizeBookingId(bookingId)
|
|
1660
|
+
})
|
|
1661
|
+
);
|
|
1662
|
+
const firstEdge = (body.data?.sales?.edges ?? [])[0];
|
|
1663
|
+
if (!firstEdge) {
|
|
1664
|
+
return [];
|
|
1665
|
+
}
|
|
1666
|
+
return filterMetaDataByIntegrator(fromBookingMetaDataNode(firstEdge.node), this.integrator);
|
|
1667
|
+
}
|
|
1668
|
+
/**
|
|
1669
|
+
* Sets the booking's `guest` metadata field. Each guest requires a `name` and
|
|
1670
|
+
* `email`; `dateOfBirth`/`waiverSigned`/`notes` are optional.
|
|
1671
|
+
*/
|
|
1672
|
+
async setMetaDataGuest(bookingId, guests) {
|
|
1673
|
+
if (!Array.isArray(guests) || guests.length === 0) {
|
|
1674
|
+
throw new Error(ERROR_GUESTS_REQUIRED);
|
|
1675
|
+
}
|
|
1676
|
+
const built = guests.map((guest) => {
|
|
1677
|
+
assertNonEmptyString(guest?.name, "guest name");
|
|
1678
|
+
assertNonEmptyString(guest?.email, "guest email");
|
|
1679
|
+
const out = { name: guest.name, email: guest.email };
|
|
1680
|
+
if (guest.dateOfBirth !== void 0) out.dateOfBirth = guest.dateOfBirth;
|
|
1681
|
+
if (guest.waiverSigned !== void 0) out.waiverSigned = guest.waiverSigned;
|
|
1682
|
+
if (guest.notes !== void 0) out.notes = guest.notes;
|
|
1683
|
+
return out;
|
|
1684
|
+
});
|
|
1685
|
+
return this.upsertFieldResponse(bookingId, { fieldName: META_FIELD_GUEST, guests: built });
|
|
1686
|
+
}
|
|
1687
|
+
/**
|
|
1688
|
+
* Sets the booking's `attachment` metadata field. Each attachment requires a
|
|
1689
|
+
* `name` and `attachmentUrl`.
|
|
1690
|
+
*/
|
|
1691
|
+
async setMetaDataAttachment(bookingId, attachments) {
|
|
1692
|
+
if (!Array.isArray(attachments) || attachments.length === 0) {
|
|
1693
|
+
throw new Error(ERROR_ATTACHMENTS_REQUIRED);
|
|
1694
|
+
}
|
|
1695
|
+
const built = attachments.map((attachment) => {
|
|
1696
|
+
assertNonEmptyString(attachment?.name, "attachment name");
|
|
1697
|
+
assertNonEmptyString(attachment?.attachmentUrl, "attachment attachmentUrl");
|
|
1698
|
+
return { name: attachment.name, attachmentUrl: attachment.attachmentUrl };
|
|
1699
|
+
});
|
|
1700
|
+
return this.upsertFieldResponse(bookingId, {
|
|
1701
|
+
fieldName: META_FIELD_ATTACHMENT,
|
|
1702
|
+
attachments: built
|
|
1703
|
+
});
|
|
1704
|
+
}
|
|
1705
|
+
/** Sets the booking's `manifest_url` metadata field. */
|
|
1706
|
+
async setMetaDataManifestUrl(bookingId, url) {
|
|
1707
|
+
assertNonEmptyString(url, "url");
|
|
1708
|
+
return this.upsertFieldResponse(bookingId, { fieldName: META_FIELD_MANIFEST_URL, url });
|
|
1709
|
+
}
|
|
1710
|
+
/** Sets the booking's `reservation_id` metadata field. */
|
|
1711
|
+
async setMetaDataReservationId(bookingId, reservationId) {
|
|
1712
|
+
return this.setShortTextField(bookingId, META_FIELD_RESERVATION_ID, reservationId, "reservationId");
|
|
1713
|
+
}
|
|
1714
|
+
/** Sets the booking's `booking_status` metadata field. */
|
|
1715
|
+
async setMetaDataBookingStatus(bookingId, status) {
|
|
1716
|
+
return this.setShortTextField(bookingId, META_FIELD_BOOKING_STATUS, status, "status");
|
|
1717
|
+
}
|
|
1718
|
+
/** Sets the booking's `assigned_product` metadata field. */
|
|
1719
|
+
async setMetaDataAssignedProduct(bookingId, productName) {
|
|
1720
|
+
return this.setShortTextField(bookingId, META_FIELD_ASSIGNED_PRODUCT, productName, "productName");
|
|
1721
|
+
}
|
|
1722
|
+
/** Sets the booking's `assigned_employee` metadata field. */
|
|
1723
|
+
async setMetaDataAssignedEmployee(bookingId, employeeName) {
|
|
1724
|
+
return this.setShortTextField(bookingId, META_FIELD_ASSIGNED_EMPLOYEE, employeeName, "employeeName");
|
|
1725
|
+
}
|
|
1726
|
+
/** Sets the booking's `insurance_purchased` metadata field. */
|
|
1727
|
+
async setMetaDataInsurancePurchased(bookingId, insurancePurchased) {
|
|
1728
|
+
if (typeof insurancePurchased !== "boolean") {
|
|
1729
|
+
throw new Error("insurancePurchased is required and must be a boolean");
|
|
1730
|
+
}
|
|
1731
|
+
return this.upsertFieldResponse(bookingId, {
|
|
1732
|
+
fieldName: META_FIELD_INSURANCE_PURCHASED,
|
|
1733
|
+
boolean: insurancePurchased
|
|
1734
|
+
});
|
|
1735
|
+
}
|
|
1736
|
+
/** Validates a non-empty `shortText` value and upserts it under `fieldName`. */
|
|
1737
|
+
setShortTextField(bookingId, fieldName, value, argName) {
|
|
1738
|
+
assertNonEmptyString(value, argName);
|
|
1739
|
+
return this.upsertFieldResponse(bookingId, { fieldName, shortText: value });
|
|
1740
|
+
}
|
|
1741
|
+
/**
|
|
1742
|
+
* Validates the booking id and upserts a single field response (scoped to this
|
|
1743
|
+
* service's integrator), mapping the result union into a {@link SetMetaDataResult}.
|
|
1744
|
+
*/
|
|
1745
|
+
async upsertFieldResponse(bookingId, fieldResponse) {
|
|
1746
|
+
assertBookingId(bookingId);
|
|
1747
|
+
const normalized = normalizeBookingId(bookingId);
|
|
1748
|
+
const body = await this.client.request(
|
|
1749
|
+
SALES_ENDPOINT,
|
|
1750
|
+
UPSERT_BOOKING_FIELD_RESPONSES_MUTATION,
|
|
1751
|
+
{
|
|
1752
|
+
input: {
|
|
1753
|
+
bookingId: normalized,
|
|
1754
|
+
integrator: this.integrator,
|
|
1755
|
+
fieldResponses: [fieldResponse]
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
);
|
|
1759
|
+
return toSetMetaDataResult(body.data?.upsertBookingFieldResponses, normalized);
|
|
1760
|
+
}
|
|
1340
1761
|
/** Returns the payments on file for a booking, or null when not found. */
|
|
1341
1762
|
async getPaymentsOnFile(bookingId) {
|
|
1342
1763
|
this.assertPiiEnabled("getPaymentsOnFile");
|
|
@@ -1381,6 +1802,30 @@ ${note}`;
|
|
|
1381
1802
|
});
|
|
1382
1803
|
return this.getById(normalized);
|
|
1383
1804
|
}
|
|
1805
|
+
/**
|
|
1806
|
+
* Sets a booking's operator-facing custom status. Returns `true` when the
|
|
1807
|
+
* gateway confirms the stored `operatorStatus` equals the requested
|
|
1808
|
+
* `customStatus`, `false` otherwise.
|
|
1809
|
+
*
|
|
1810
|
+
* @example
|
|
1811
|
+
* ```ts
|
|
1812
|
+
* const ok = await peek.getBookingService().updateCustomStatus("b_abc123", "Awaiting deposit");
|
|
1813
|
+
* ```
|
|
1814
|
+
*
|
|
1815
|
+
* @throws {Error} when `bookingId` is not a valid booking id (`b_…`/`B-…`) or
|
|
1816
|
+
* when `customStatus` is not a non-empty string.
|
|
1817
|
+
*/
|
|
1818
|
+
async updateCustomStatus(bookingId, customStatus) {
|
|
1819
|
+
assertBookingId(bookingId);
|
|
1820
|
+
assertCustomStatus(customStatus);
|
|
1821
|
+
const normalized = normalizeBookingId(bookingId);
|
|
1822
|
+
const body = await this.client.request(
|
|
1823
|
+
SALES_ENDPOINT,
|
|
1824
|
+
UPDATE_OPERATOR_STATUS_MUTATION,
|
|
1825
|
+
{ input: { id: normalized, operatorStatus: customStatus } }
|
|
1826
|
+
);
|
|
1827
|
+
return body.data?.updateOperatorStatusForBooking?.booking?.operatorStatus === customStatus;
|
|
1828
|
+
}
|
|
1384
1829
|
/** Cancels a booking and returns its id/displayId/status. */
|
|
1385
1830
|
async cancel(bookingId, notes = DEFAULT_CANCEL_NOTE) {
|
|
1386
1831
|
assertBookingId(bookingId);
|
|
@@ -1736,13 +2181,26 @@ ${note}`;
|
|
|
1736
2181
|
*/
|
|
1737
2182
|
async create(input) {
|
|
1738
2183
|
validateCreateInput(input);
|
|
1739
|
-
const
|
|
2184
|
+
const ticketCount = input.tickets.reduce((count, ticket) => count + ticket.quantity, 0);
|
|
2185
|
+
const listPriceAmounts = input.listPrice === void 0 ? null : distributeListPrice(input.listPrice, ticketCount);
|
|
2186
|
+
const [questionAnswers, currency] = await Promise.all([
|
|
2187
|
+
this.buildQuestionAnswers(input),
|
|
2188
|
+
listPriceAmounts ? this.resolveActivityCurrency(input.activityId) : Promise.resolve("")
|
|
2189
|
+
]);
|
|
2190
|
+
let seatIndex = 0;
|
|
1740
2191
|
const tickets = input.tickets.flatMap(
|
|
1741
|
-
(ticket) => Array.from({ length: ticket.quantity }, () =>
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
2192
|
+
(ticket) => Array.from({ length: ticket.quantity }, () => {
|
|
2193
|
+
const seat = {
|
|
2194
|
+
resourceOptionId: ticket.resourceOptionId,
|
|
2195
|
+
reservationStatus: "CONFIRMED",
|
|
2196
|
+
refid: crypto.randomUUID()
|
|
2197
|
+
};
|
|
2198
|
+
if (listPriceAmounts) {
|
|
2199
|
+
seat.price = { amount: listPriceAmounts[seatIndex], currency };
|
|
2200
|
+
}
|
|
2201
|
+
seatIndex += 1;
|
|
2202
|
+
return seat;
|
|
2203
|
+
})
|
|
1746
2204
|
);
|
|
1747
2205
|
const bookingQuote = {
|
|
1748
2206
|
tickets,
|
|
@@ -1857,6 +2315,15 @@ ${note}`;
|
|
|
1857
2315
|
}
|
|
1858
2316
|
return result.transactionId;
|
|
1859
2317
|
}
|
|
2318
|
+
/**
|
|
2319
|
+
* Resolves an activity's ISO currency via the product service, falling back to
|
|
2320
|
+
* {@link DEFAULT_CURRENCY} when the activity (or its currency) can't be found.
|
|
2321
|
+
*/
|
|
2322
|
+
async resolveActivityCurrency(activityId) {
|
|
2323
|
+
const products = await this.deps.productService.getAllProducts();
|
|
2324
|
+
const match = products.find((product) => product.productId === activityId);
|
|
2325
|
+
return match?.currency || DEFAULT_CURRENCY;
|
|
2326
|
+
}
|
|
1860
2327
|
/** Finds the parent item id of an add-on by matching its option id. */
|
|
1861
2328
|
async resolveParentItemId(addonOptionId) {
|
|
1862
2329
|
const products = await this.deps.productService.getAllProducts();
|
|
@@ -1931,6 +2398,16 @@ function assertBookingId(bookingId) {
|
|
|
1931
2398
|
throw new Error(ERROR_INVALID_BOOKING_ID);
|
|
1932
2399
|
}
|
|
1933
2400
|
}
|
|
2401
|
+
function assertCustomStatus(customStatus) {
|
|
2402
|
+
if (typeof customStatus !== "string" || customStatus.trim().length === 0) {
|
|
2403
|
+
throw new Error(ERROR_CUSTOM_STATUS_REQUIRED);
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
function assertNonEmptyString(value, field) {
|
|
2407
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
2408
|
+
throw new Error(`${field} is required and must be a non-empty string`);
|
|
2409
|
+
}
|
|
2410
|
+
}
|
|
1934
2411
|
function assertOrderId(orderId) {
|
|
1935
2412
|
if (!(ORDER_DB_ID_REGEX.test(orderId) || ORDER_DISPLAY_ID_REGEX.test(orderId))) {
|
|
1936
2413
|
throw new Error(ERROR_INVALID_ORDER_ID);
|
|
@@ -2197,7 +2674,7 @@ async function requestWithRetry(http, url, init, label, onResponse) {
|
|
|
2197
2674
|
// src/internal/sdk-headers.ts
|
|
2198
2675
|
var SDK_HEADER_NAME = "x-peek-sdk";
|
|
2199
2676
|
var SDK_PLATFORM = "js";
|
|
2200
|
-
var SDK_VERSION = "0.9.
|
|
2677
|
+
var SDK_VERSION = "0.9.4";
|
|
2201
2678
|
var SDK_HEADER_VALUE = `${SDK_PLATFORM}-${SDK_VERSION}`;
|
|
2202
2679
|
|
|
2203
2680
|
// src/internal/peek/graphql-client.ts
|
|
@@ -3483,7 +3960,7 @@ var CREATE_PROMO_CODE_MUTATION = `
|
|
|
3483
3960
|
|
|
3484
3961
|
// src/internal/peek/promo-codes/promo-code-service.ts
|
|
3485
3962
|
var DEFAULT_PAGE_SIZE3 = 50;
|
|
3486
|
-
var
|
|
3963
|
+
var DEFAULT_CURRENCY2 = "USD";
|
|
3487
3964
|
var CURRENCY_PATTERN = /^[A-Z]{3}$/;
|
|
3488
3965
|
var PromoCodeService = class {
|
|
3489
3966
|
constructor(client, options = {}) {
|
|
@@ -3529,7 +4006,7 @@ var PromoCodeService = class {
|
|
|
3529
4006
|
if (Number.isNaN(parseFloat(input.amount))) {
|
|
3530
4007
|
throw new Error("amount must be a valid number");
|
|
3531
4008
|
}
|
|
3532
|
-
const currency = input.currency ||
|
|
4009
|
+
const currency = input.currency || DEFAULT_CURRENCY2;
|
|
3533
4010
|
if (!CURRENCY_PATTERN.test(currency)) {
|
|
3534
4011
|
throw new Error("currency must be 3 uppercase letters");
|
|
3535
4012
|
}
|
|
@@ -3903,6 +4380,7 @@ var PeekAccessService = class {
|
|
|
3903
4380
|
productServiceOptions;
|
|
3904
4381
|
accessOptions;
|
|
3905
4382
|
jwtSecret;
|
|
4383
|
+
issuer;
|
|
3906
4384
|
productService;
|
|
3907
4385
|
accountUserService;
|
|
3908
4386
|
resourcePoolService;
|
|
@@ -3913,7 +4391,7 @@ var PeekAccessService = class {
|
|
|
3913
4391
|
dailyNoteService;
|
|
3914
4392
|
availabilityService;
|
|
3915
4393
|
membershipService;
|
|
3916
|
-
|
|
4394
|
+
bookingServices = /* @__PURE__ */ new Map();
|
|
3917
4395
|
reviewService;
|
|
3918
4396
|
constructor(config) {
|
|
3919
4397
|
const hasApiUrl = !!config.apiUrl;
|
|
@@ -3924,6 +4402,7 @@ var PeekAccessService = class {
|
|
|
3924
4402
|
if (!hasApiUrl) requireNonEmpty(config.appId ?? "", "appId", "PeekAccessService");
|
|
3925
4403
|
if (!hasApiUrl && !isV2) requireNonEmpty(config.gatewayKey ?? "", "gatewayKey", "PeekAccessService");
|
|
3926
4404
|
this.jwtSecret = config.jwtSecret;
|
|
4405
|
+
this.issuer = config.issuer;
|
|
3927
4406
|
const logger = config.logger ?? noopLogger;
|
|
3928
4407
|
const tokens = createTokenManager(config);
|
|
3929
4408
|
const useRegistrySlug = hasApiUrl || isV2;
|
|
@@ -4084,17 +4563,25 @@ var PeekAccessService = class {
|
|
|
4084
4563
|
}
|
|
4085
4564
|
/**
|
|
4086
4565
|
* Returns the {@link BookingService} for this install, bound to the shared
|
|
4087
|
-
* authenticated transport.
|
|
4566
|
+
* authenticated transport. Instances are created lazily and reused per
|
|
4567
|
+
* `integrator`.
|
|
4568
|
+
*
|
|
4569
|
+
* `integrator` scopes `getMetaData` results to a custom integrator id. **Do
|
|
4570
|
+
* not set it** unless Peek engineering created a custom integrator id for this
|
|
4571
|
+
* integration — omit it and the correct default is used.
|
|
4088
4572
|
*/
|
|
4089
|
-
getBookingService() {
|
|
4090
|
-
|
|
4091
|
-
|
|
4573
|
+
getBookingService(integrator) {
|
|
4574
|
+
const resolved = integrator ?? this.issuer;
|
|
4575
|
+
let service = this.bookingServices.get(resolved);
|
|
4576
|
+
if (!service) {
|
|
4577
|
+
service = new BookingService(
|
|
4092
4578
|
this.client,
|
|
4093
4579
|
{ productService: this.getProductService() },
|
|
4094
|
-
{ accessOptions: this.accessOptions }
|
|
4580
|
+
{ accessOptions: this.accessOptions, integrator: resolved }
|
|
4095
4581
|
);
|
|
4582
|
+
this.bookingServices.set(resolved, service);
|
|
4096
4583
|
}
|
|
4097
|
-
return
|
|
4584
|
+
return service;
|
|
4098
4585
|
}
|
|
4099
4586
|
/**
|
|
4100
4587
|
* Returns the {@link ReviewService} for this install, bound to the shared
|