@peektravel/app-utilities 0.4.1 → 0.5.0
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 +36 -2
- package/dist/index.cjs +164 -103
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +64 -3
- package/dist/index.d.ts +64 -3
- package/dist/index.js +164 -104
- package/dist/index.js.map +1 -1
- package/docs/webhooks.md +9 -1
- package/llms.txt +19 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -70,6 +70,34 @@ add-on pages for you.
|
|
|
70
70
|
| `logger` | no-op | Inject a `Logger` for diagnostics |
|
|
71
71
|
| `fetch` | global `fetch` | Custom fetch (e.g. for tests) |
|
|
72
72
|
| `itemOptionsPageSize` | `50` | Add-on pagination page size |
|
|
73
|
+
| `accessOptions` | `{ fullCustomerAccess: false }` | PII exposure — see [Access options / PII](#access-options--pii) |
|
|
74
|
+
|
|
75
|
+
### Access options / PII
|
|
76
|
+
|
|
77
|
+
Every access service accepts `accessOptions?: AccessOptions` — today a single
|
|
78
|
+
flag, `fullCustomerAccess` (default `false`). It's an object so future cross-cutting
|
|
79
|
+
flags can be added without breaking signatures.
|
|
80
|
+
|
|
81
|
+
With `fullCustomerAccess` **off** (the default):
|
|
82
|
+
|
|
83
|
+
- **Customer PII is never requested** (the GraphQL queries omit the fields, so
|
|
84
|
+
they come back `null`/empty): booking guest identity (name/email/phone/DOB/
|
|
85
|
+
postal code/GDPR + custom field responses — guests keep only ids and
|
|
86
|
+
participation/opt-in flags), booking custom question answers, the customer
|
|
87
|
+
`portalUrl`, and review reviewer `customerName`/`customerEmail`. Waiver
|
|
88
|
+
webhooks (a fixed payload with no query to trim) instead have `guestName` and
|
|
89
|
+
`fileUrl` redacted at parse time.
|
|
90
|
+
- **Payment / booking-modification operations are disabled** —
|
|
91
|
+
`getPaymentsOnFile`, `makePayment`, `refund`, `createInvoiceLink`, `addAddon`,
|
|
92
|
+
and `removeAddon` throw `PiiAccessDisabledError`. `create` (including
|
|
93
|
+
`markAsPaid`) and non-payment reads/mutations remain available.
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
const peek = new PeekAccessService({
|
|
97
|
+
installId, jwtSecret, issuer, appId, gatewayKey,
|
|
98
|
+
accessOptions: { fullCustomerAccess: true }, // opt into PII + payment operations
|
|
99
|
+
});
|
|
100
|
+
```
|
|
73
101
|
|
|
74
102
|
### Errors
|
|
75
103
|
|
|
@@ -83,6 +111,9 @@ Two kinds of failures surface as exceptions:
|
|
|
83
111
|
exhausted. Carries `.statusCode === 429`.
|
|
84
112
|
- `PeekGraphQLError` — the response contained a GraphQL `errors` array, preserved
|
|
85
113
|
on `.graphqlErrors`.
|
|
114
|
+
- `PiiAccessDisabledError` — a payment / booking-modification operation was
|
|
115
|
+
called on an access service created without `fullCustomerAccess` (see [Access options
|
|
116
|
+
/ PII](#access-options--pii)). Carries `.operation` (the blocked method name).
|
|
86
117
|
|
|
87
118
|
**Plain `Error` validation/precondition failures** thrown by the service layer
|
|
88
119
|
*before* any network call — e.g. an empty config field, a `bookingId` that
|
|
@@ -216,13 +247,16 @@ app.post("/booking-webhook", (req, res) => {
|
|
|
216
247
|
});
|
|
217
248
|
|
|
218
249
|
app.post("/waiver-webhook", (req, res) => {
|
|
219
|
-
|
|
250
|
+
// guestName/fileUrl are redacted unless you opt into PII:
|
|
251
|
+
const waiver: Waiver = parseWaiverWebhook(req.body, { fullCustomerAccess: true });
|
|
220
252
|
res.sendStatus(200);
|
|
221
253
|
});
|
|
222
254
|
```
|
|
223
255
|
|
|
224
256
|
Both tolerate the delivery envelope / a bare node / a JSON string and never throw
|
|
225
|
-
on malformed input.
|
|
257
|
+
on malformed input. `parseWaiverWebhook` also takes an optional `AccessOptions`
|
|
258
|
+
(`{ fullCustomerAccess }`) and redacts the participant `guestName` + document `fileUrl`
|
|
259
|
+
by default — see [Access options / PII](#access-options--pii) below. They differ on registration: a **booking** webhook's payload
|
|
226
260
|
shape is set by a GraphQL query configured **once in an external system** (the
|
|
227
261
|
App Store `broadcast_to_url` config) — this package documents and drift-guards
|
|
228
262
|
the exact query to paste there — whereas a **waiver** webhook has a fixed payload,
|
package/dist/index.cjs
CHANGED
|
@@ -203,6 +203,73 @@ var AvailabilityService = class {
|
|
|
203
203
|
}
|
|
204
204
|
};
|
|
205
205
|
|
|
206
|
+
// src/access-options.ts
|
|
207
|
+
function resolveAccessOptions(options) {
|
|
208
|
+
return { fullCustomerAccess: options?.fullCustomerAccess ?? false };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// src/errors.ts
|
|
212
|
+
var AdminAccountRequiredError = class extends Error {
|
|
213
|
+
/** The HTTP status that triggered this error. */
|
|
214
|
+
statusCode = 418;
|
|
215
|
+
constructor(message = "Admin account required") {
|
|
216
|
+
super(message);
|
|
217
|
+
this.name = "AdminAccountRequiredError";
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
var RateLimitError = class extends Error {
|
|
221
|
+
/** The HTTP status that triggered this error. */
|
|
222
|
+
statusCode = 429;
|
|
223
|
+
constructor(message = "Rate limit exceeded") {
|
|
224
|
+
super(message);
|
|
225
|
+
this.name = "RateLimitError";
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
var PeekGraphQLError = class extends Error {
|
|
229
|
+
/** The raw `errors` array returned by the GraphQL endpoint. */
|
|
230
|
+
graphqlErrors;
|
|
231
|
+
constructor(graphqlErrors, message = "GraphQL request failed") {
|
|
232
|
+
super(message);
|
|
233
|
+
this.name = "PeekGraphQLError";
|
|
234
|
+
this.graphqlErrors = graphqlErrors;
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
var PiiAccessDisabledError = class extends Error {
|
|
238
|
+
/** The name of the operation that was blocked (e.g. `"makePayment"`). */
|
|
239
|
+
operation;
|
|
240
|
+
constructor(operation) {
|
|
241
|
+
super(
|
|
242
|
+
`"${operation}" is disabled because this access service was created without "fullCustomerAccess"; enable it to allow payment and booking-modification operations`
|
|
243
|
+
);
|
|
244
|
+
this.name = "PiiAccessDisabledError";
|
|
245
|
+
this.operation = operation;
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
var CngApiError = class extends Error {
|
|
249
|
+
/** The HTTP status that triggered this error. */
|
|
250
|
+
statusCode;
|
|
251
|
+
/** The raw response body (parsed JSON when possible, otherwise text). */
|
|
252
|
+
body;
|
|
253
|
+
constructor(statusCode, body, message) {
|
|
254
|
+
super(message ?? `CNG request failed with HTTP ${statusCode}`);
|
|
255
|
+
this.name = "CngApiError";
|
|
256
|
+
this.statusCode = statusCode;
|
|
257
|
+
this.body = body;
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
var AcmeApiError = class extends Error {
|
|
261
|
+
/** The HTTP status that triggered this error. */
|
|
262
|
+
statusCode;
|
|
263
|
+
/** The raw response body (parsed JSON when possible, otherwise text). */
|
|
264
|
+
body;
|
|
265
|
+
constructor(statusCode, body, message) {
|
|
266
|
+
super(message ?? `ACME request failed with HTTP ${statusCode}`);
|
|
267
|
+
this.name = "AcmeApiError";
|
|
268
|
+
this.statusCode = statusCode;
|
|
269
|
+
this.body = body;
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
|
|
206
273
|
// src/models/peek/product.ts
|
|
207
274
|
var ACTIVITY_PRODUCT_TYPE = "ACTIVITY";
|
|
208
275
|
var RENTAL_PRODUCT_TYPE = "RENTAL";
|
|
@@ -214,17 +281,8 @@ var SEARCH_BY_ACTIVITY_DATE = "activityDate";
|
|
|
214
281
|
function normalizeBookingId(bookingId) {
|
|
215
282
|
return bookingId.toLowerCase().replace(/-/g, "_");
|
|
216
283
|
}
|
|
217
|
-
var
|
|
218
|
-
|
|
219
|
-
name
|
|
220
|
-
country
|
|
221
|
-
dateOfBirth
|
|
222
|
-
email
|
|
223
|
-
isGdpr
|
|
224
|
-
isParticipant
|
|
225
|
-
optinSms
|
|
226
|
-
optinMarketing
|
|
227
|
-
phone
|
|
284
|
+
var GUEST_IDENTITY_PII_FIELDS = "name country dateOfBirth email isGdpr";
|
|
285
|
+
var GUEST_CONTACT_PII_FIELDS = `phone
|
|
228
286
|
postalCode
|
|
229
287
|
fieldResponses {
|
|
230
288
|
id
|
|
@@ -234,9 +292,20 @@ var guestFields = `
|
|
|
234
292
|
name
|
|
235
293
|
}
|
|
236
294
|
}
|
|
237
|
-
}
|
|
295
|
+
}`;
|
|
296
|
+
function buildGuestFields(fullCustomerAccess) {
|
|
297
|
+
return `
|
|
298
|
+
id
|
|
299
|
+
${fullCustomerAccess ? GUEST_IDENTITY_PII_FIELDS : ""}
|
|
300
|
+
isParticipant
|
|
301
|
+
optinSms
|
|
302
|
+
optinMarketing
|
|
303
|
+
${fullCustomerAccess ? GUEST_CONTACT_PII_FIELDS : ""}
|
|
238
304
|
`;
|
|
239
|
-
|
|
305
|
+
}
|
|
306
|
+
function buildBookingGuestsFields(fullCustomerAccess) {
|
|
307
|
+
const guestFields = buildGuestFields(fullCustomerAccess);
|
|
308
|
+
return `
|
|
240
309
|
bookingGuests {
|
|
241
310
|
${guestFields}
|
|
242
311
|
}
|
|
@@ -244,10 +313,9 @@ var bookingGuestsFields = `
|
|
|
244
313
|
${guestFields}
|
|
245
314
|
}
|
|
246
315
|
`;
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
primaryGuest {
|
|
316
|
+
}
|
|
317
|
+
var bookingGuestsFields = buildBookingGuestsFields(true);
|
|
318
|
+
var BOOKING_PRIMARY_GUEST_PII_FIELDS = `primaryGuest {
|
|
251
319
|
name
|
|
252
320
|
email
|
|
253
321
|
phone
|
|
@@ -255,7 +323,26 @@ var bookingQueryFields = `
|
|
|
255
323
|
optinSms
|
|
256
324
|
isGdpr
|
|
257
325
|
postalCode
|
|
326
|
+
}`;
|
|
327
|
+
var BOOKING_QUESTION_ANSWERS_PII_FIELDS = `questionAnswers {
|
|
328
|
+
answer
|
|
329
|
+
questionText
|
|
330
|
+
questionLocationSnapshot {
|
|
331
|
+
latitude
|
|
332
|
+
longitude
|
|
333
|
+
}
|
|
258
334
|
}
|
|
335
|
+
tickets {
|
|
336
|
+
questionAnswers {
|
|
337
|
+
answer
|
|
338
|
+
questionText
|
|
339
|
+
}
|
|
340
|
+
}`;
|
|
341
|
+
function buildBookingQueryFields(fullCustomerAccess) {
|
|
342
|
+
return `
|
|
343
|
+
displayId
|
|
344
|
+
id
|
|
345
|
+
${fullCustomerAccess ? BOOKING_PRIMARY_GUEST_PII_FIELDS : ""}
|
|
259
346
|
activitySnapshot {
|
|
260
347
|
type
|
|
261
348
|
name
|
|
@@ -285,7 +372,7 @@ var bookingQueryFields = `
|
|
|
285
372
|
endsAt
|
|
286
373
|
endsAtUtc
|
|
287
374
|
availabilityTimeId
|
|
288
|
-
bookingPortalUrl
|
|
375
|
+
${fullCustomerAccess ? "bookingPortalUrl" : ""}
|
|
289
376
|
operatorNotes
|
|
290
377
|
value {
|
|
291
378
|
total {
|
|
@@ -327,20 +414,7 @@ var bookingQueryFields = `
|
|
|
327
414
|
}
|
|
328
415
|
}
|
|
329
416
|
}
|
|
330
|
-
|
|
331
|
-
answer
|
|
332
|
-
questionText
|
|
333
|
-
questionLocationSnapshot {
|
|
334
|
-
latitude
|
|
335
|
-
longitude
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
tickets {
|
|
339
|
-
questionAnswers {
|
|
340
|
-
answer
|
|
341
|
-
questionText
|
|
342
|
-
}
|
|
343
|
-
}
|
|
417
|
+
${fullCustomerAccess ? BOOKING_QUESTION_ANSWERS_PII_FIELDS : ""}
|
|
344
418
|
resourcePoolAssignments {
|
|
345
419
|
quantity
|
|
346
420
|
resourcePool {
|
|
@@ -358,6 +432,8 @@ var bookingQueryFields = `
|
|
|
358
432
|
}
|
|
359
433
|
}
|
|
360
434
|
`;
|
|
435
|
+
}
|
|
436
|
+
var bookingQueryFields = buildBookingQueryFields(true);
|
|
361
437
|
var PRICE_BREAKDOWN_FIELDS = `
|
|
362
438
|
convenienceFee { amount formatted }
|
|
363
439
|
deposit { amount formatted }
|
|
@@ -376,9 +452,10 @@ var TICKET_VALUE_FIELDS = `
|
|
|
376
452
|
total { amount formatted }
|
|
377
453
|
}
|
|
378
454
|
`;
|
|
379
|
-
function buildBookingsListingQuery(includeGuests, includePriceBreakdown) {
|
|
380
|
-
const guestsSection = includeGuests ?
|
|
381
|
-
const
|
|
455
|
+
function buildBookingsListingQuery(includeGuests, includePriceBreakdown, fullCustomerAccess = true) {
|
|
456
|
+
const guestsSection = includeGuests ? buildBookingGuestsFields(fullCustomerAccess) : "";
|
|
457
|
+
const baseFields = buildBookingQueryFields(fullCustomerAccess);
|
|
458
|
+
const fields = includePriceBreakdown ? baseFields.replace("value {", `value { ${PRICE_BREAKDOWN_FIELDS}`).replace("ticketQuantities {", `ticketQuantities { ${TICKET_VALUE_FIELDS}`) : baseFields;
|
|
382
459
|
return `
|
|
383
460
|
query Sales($after: String, $first: Int, $filter: SalesFilter!, $orderBy: SalesOrdering) {
|
|
384
461
|
sales(after: $after, first: $first, filter: $filter, orderBy: $orderBy) {
|
|
@@ -395,7 +472,8 @@ function buildBookingsListingQuery(includeGuests, includePriceBreakdown) {
|
|
|
395
472
|
}
|
|
396
473
|
`;
|
|
397
474
|
}
|
|
398
|
-
|
|
475
|
+
function buildBookingGuestsQuery(fullCustomerAccess) {
|
|
476
|
+
return `
|
|
399
477
|
query Sales($after: String, $first: Int, $filter: SalesFilter!, $orderBy: SalesOrdering) {
|
|
400
478
|
sales(after: $after, first: $first, filter: $filter, orderBy: $orderBy) {
|
|
401
479
|
pageInfo { endCursor hasNextPage }
|
|
@@ -404,13 +482,14 @@ var BOOKING_GUESTS_QUERY = `
|
|
|
404
482
|
... on Booking {
|
|
405
483
|
displayId
|
|
406
484
|
id
|
|
407
|
-
${
|
|
485
|
+
${buildBookingGuestsFields(fullCustomerAccess)}
|
|
408
486
|
}
|
|
409
487
|
}
|
|
410
488
|
}
|
|
411
489
|
}
|
|
412
490
|
}
|
|
413
491
|
`;
|
|
492
|
+
}
|
|
414
493
|
var BOOKING_PAYMENTS_ON_FILE_QUERY = `
|
|
415
494
|
query Sales($after: String, $first: Int, $filter: SalesFilter!, $orderBy: SalesOrdering) {
|
|
416
495
|
sales(after: $after, first: $first, filter: $filter, orderBy: $orderBy) {
|
|
@@ -996,10 +1075,23 @@ var BookingService = class {
|
|
|
996
1075
|
this.client = client;
|
|
997
1076
|
this.deps = deps;
|
|
998
1077
|
this.pageSize = options.pageSize ?? DEFAULT_PAGE_SIZE2;
|
|
1078
|
+
this.fullCustomerAccess = resolveAccessOptions(options.accessOptions).fullCustomerAccess;
|
|
999
1079
|
}
|
|
1000
1080
|
client;
|
|
1001
1081
|
deps;
|
|
1002
1082
|
pageSize;
|
|
1083
|
+
/** Whether customer PII is requested and payment operations are allowed. */
|
|
1084
|
+
fullCustomerAccess;
|
|
1085
|
+
/**
|
|
1086
|
+
* Throws a {@link PiiAccessDisabledError} for `operation` when this service was
|
|
1087
|
+
* created without `fullCustomerAccess` — used to gate payment and booking-modification
|
|
1088
|
+
* operations that touch customer financial data.
|
|
1089
|
+
*/
|
|
1090
|
+
assertPiiEnabled(operation) {
|
|
1091
|
+
if (!this.fullCustomerAccess) {
|
|
1092
|
+
throw new PiiAccessDisabledError(operation);
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1003
1095
|
/**
|
|
1004
1096
|
* Returns a single booking by id, or null when not found. The `bookingId` is
|
|
1005
1097
|
* normalized internally (lowercased, `-` → `_`), so `B-ABC123` and `b_abc123`
|
|
@@ -1021,7 +1113,7 @@ var BookingService = class {
|
|
|
1021
1113
|
const includePriceBreakdown = options.includePriceBreakdown ?? false;
|
|
1022
1114
|
const body = await this.client.request(
|
|
1023
1115
|
SALES_ENDPOINT,
|
|
1024
|
-
buildBookingsListingQuery(includeGuests, includePriceBreakdown),
|
|
1116
|
+
buildBookingsListingQuery(includeGuests, includePriceBreakdown, this.fullCustomerAccess),
|
|
1025
1117
|
buildBookingsVariables({
|
|
1026
1118
|
pageSize: this.pageSize,
|
|
1027
1119
|
after: null,
|
|
@@ -1039,7 +1131,7 @@ var BookingService = class {
|
|
|
1039
1131
|
const includeGuests = input.includeGuests ?? false;
|
|
1040
1132
|
const includePriceBreakdown = input.includePriceBreakdown ?? false;
|
|
1041
1133
|
return this.fetchPaginated(
|
|
1042
|
-
buildBookingsListingQuery(includeGuests, includePriceBreakdown),
|
|
1134
|
+
buildBookingsListingQuery(includeGuests, includePriceBreakdown, this.fullCustomerAccess),
|
|
1043
1135
|
{
|
|
1044
1136
|
startDateTime: input.start,
|
|
1045
1137
|
endDateTime: input.end,
|
|
@@ -1057,7 +1149,7 @@ var BookingService = class {
|
|
|
1057
1149
|
const includeGuests = options.includeGuests ?? false;
|
|
1058
1150
|
const includePriceBreakdown = options.includePriceBreakdown ?? false;
|
|
1059
1151
|
return this.fetchPaginated(
|
|
1060
|
-
buildBookingsListingQuery(includeGuests, includePriceBreakdown),
|
|
1152
|
+
buildBookingsListingQuery(includeGuests, includePriceBreakdown, this.fullCustomerAccess),
|
|
1061
1153
|
{ timeslotId },
|
|
1062
1154
|
includeGuests,
|
|
1063
1155
|
includePriceBreakdown
|
|
@@ -1068,7 +1160,7 @@ var BookingService = class {
|
|
|
1068
1160
|
assertBookingId(bookingId);
|
|
1069
1161
|
const body = await this.client.request(
|
|
1070
1162
|
SALES_ENDPOINT,
|
|
1071
|
-
|
|
1163
|
+
buildBookingGuestsQuery(this.fullCustomerAccess),
|
|
1072
1164
|
buildBookingsVariables({
|
|
1073
1165
|
pageSize: this.pageSize,
|
|
1074
1166
|
after: null,
|
|
@@ -1079,6 +1171,7 @@ var BookingService = class {
|
|
|
1079
1171
|
}
|
|
1080
1172
|
/** Returns the payments on file for a booking, or null when not found. */
|
|
1081
1173
|
async getPaymentsOnFile(bookingId) {
|
|
1174
|
+
this.assertPiiEnabled("getPaymentsOnFile");
|
|
1082
1175
|
assertBookingId(bookingId);
|
|
1083
1176
|
const normalized = normalizeBookingId(bookingId);
|
|
1084
1177
|
const body = await this.client.request(
|
|
@@ -1163,6 +1256,7 @@ ${note}`;
|
|
|
1163
1256
|
* charge fails.
|
|
1164
1257
|
*/
|
|
1165
1258
|
async makePayment(input) {
|
|
1259
|
+
this.assertPiiEnabled("makePayment");
|
|
1166
1260
|
this.validatePaymentInput(input);
|
|
1167
1261
|
const normalized = normalizeBookingId(input.bookingId);
|
|
1168
1262
|
const onFile = await this.getPaymentsOnFile(normalized);
|
|
@@ -1205,6 +1299,7 @@ ${note}`;
|
|
|
1205
1299
|
* payments-on-file, then applies the refund.
|
|
1206
1300
|
*/
|
|
1207
1301
|
async refund(input) {
|
|
1302
|
+
this.assertPiiEnabled("refund");
|
|
1208
1303
|
this.validateRefundInput(input);
|
|
1209
1304
|
const normalized = normalizeBookingId(input.bookingId);
|
|
1210
1305
|
const onFile = await this.getPaymentsOnFile(normalized);
|
|
@@ -1245,6 +1340,7 @@ ${note}`;
|
|
|
1245
1340
|
}
|
|
1246
1341
|
/** Creates an invoice link for a booking's order. */
|
|
1247
1342
|
async createInvoiceLink(bookingId) {
|
|
1343
|
+
this.assertPiiEnabled("createInvoiceLink");
|
|
1248
1344
|
assertBookingId(bookingId);
|
|
1249
1345
|
const normalized = normalizeBookingId(bookingId);
|
|
1250
1346
|
const booking = await this.getById(normalized);
|
|
@@ -1300,6 +1396,7 @@ ${note}`;
|
|
|
1300
1396
|
* the underlying quote/order mutations fail.
|
|
1301
1397
|
*/
|
|
1302
1398
|
async addAddon(bookingId, input) {
|
|
1399
|
+
this.assertPiiEnabled("addAddon");
|
|
1303
1400
|
assertBookingId(bookingId);
|
|
1304
1401
|
const addonOptionId = (input?.addonOptionId || input?.addonId || "").trim();
|
|
1305
1402
|
if (!addonOptionId) {
|
|
@@ -1353,6 +1450,7 @@ ${note}`;
|
|
|
1353
1450
|
* variables. Returns the booking's add-ons after the change.
|
|
1354
1451
|
*/
|
|
1355
1452
|
async removeAddon(bookingId, input) {
|
|
1453
|
+
this.assertPiiEnabled("removeAddon");
|
|
1356
1454
|
assertBookingId(bookingId);
|
|
1357
1455
|
const addonOptionId = (input?.addonOptionId || input?.addonId || "").trim();
|
|
1358
1456
|
if (!addonOptionId) {
|
|
@@ -1842,57 +1940,6 @@ function createTokenManager(config) {
|
|
|
1842
1940
|
});
|
|
1843
1941
|
}
|
|
1844
1942
|
|
|
1845
|
-
// src/errors.ts
|
|
1846
|
-
var AdminAccountRequiredError = class extends Error {
|
|
1847
|
-
/** The HTTP status that triggered this error. */
|
|
1848
|
-
statusCode = 418;
|
|
1849
|
-
constructor(message = "Admin account required") {
|
|
1850
|
-
super(message);
|
|
1851
|
-
this.name = "AdminAccountRequiredError";
|
|
1852
|
-
}
|
|
1853
|
-
};
|
|
1854
|
-
var RateLimitError = class extends Error {
|
|
1855
|
-
/** The HTTP status that triggered this error. */
|
|
1856
|
-
statusCode = 429;
|
|
1857
|
-
constructor(message = "Rate limit exceeded") {
|
|
1858
|
-
super(message);
|
|
1859
|
-
this.name = "RateLimitError";
|
|
1860
|
-
}
|
|
1861
|
-
};
|
|
1862
|
-
var PeekGraphQLError = class extends Error {
|
|
1863
|
-
/** The raw `errors` array returned by the GraphQL endpoint. */
|
|
1864
|
-
graphqlErrors;
|
|
1865
|
-
constructor(graphqlErrors, message = "GraphQL request failed") {
|
|
1866
|
-
super(message);
|
|
1867
|
-
this.name = "PeekGraphQLError";
|
|
1868
|
-
this.graphqlErrors = graphqlErrors;
|
|
1869
|
-
}
|
|
1870
|
-
};
|
|
1871
|
-
var CngApiError = class extends Error {
|
|
1872
|
-
/** The HTTP status that triggered this error. */
|
|
1873
|
-
statusCode;
|
|
1874
|
-
/** The raw response body (parsed JSON when possible, otherwise text). */
|
|
1875
|
-
body;
|
|
1876
|
-
constructor(statusCode, body, message) {
|
|
1877
|
-
super(message ?? `CNG request failed with HTTP ${statusCode}`);
|
|
1878
|
-
this.name = "CngApiError";
|
|
1879
|
-
this.statusCode = statusCode;
|
|
1880
|
-
this.body = body;
|
|
1881
|
-
}
|
|
1882
|
-
};
|
|
1883
|
-
var AcmeApiError = class extends Error {
|
|
1884
|
-
/** The HTTP status that triggered this error. */
|
|
1885
|
-
statusCode;
|
|
1886
|
-
/** The raw response body (parsed JSON when possible, otherwise text). */
|
|
1887
|
-
body;
|
|
1888
|
-
constructor(statusCode, body, message) {
|
|
1889
|
-
super(message ?? `ACME request failed with HTTP ${statusCode}`);
|
|
1890
|
-
this.name = "AcmeApiError";
|
|
1891
|
-
this.statusCode = statusCode;
|
|
1892
|
-
this.body = body;
|
|
1893
|
-
}
|
|
1894
|
-
};
|
|
1895
|
-
|
|
1896
1943
|
// src/internal/http-transport.ts
|
|
1897
1944
|
var RATE_LIMIT_STATUS = 429;
|
|
1898
1945
|
var ADMIN_ACCOUNT_REQUIRED_STATUS = 418;
|
|
@@ -2311,7 +2358,8 @@ function encodeCursor(offset, pageSize) {
|
|
|
2311
2358
|
}
|
|
2312
2359
|
|
|
2313
2360
|
// src/internal/peek/reviews/review-queries.ts
|
|
2314
|
-
|
|
2361
|
+
function buildReviewsQuery(fullCustomerAccess) {
|
|
2362
|
+
return `
|
|
2315
2363
|
query Reviews($first: Int, $filter: ReviewFilter, $after: String) {
|
|
2316
2364
|
reviews(first: $first, filter: $filter, after: $after) {
|
|
2317
2365
|
edges {
|
|
@@ -2325,8 +2373,7 @@ var REVIEWS_QUERY = `
|
|
|
2325
2373
|
name
|
|
2326
2374
|
}
|
|
2327
2375
|
id
|
|
2328
|
-
name
|
|
2329
|
-
email
|
|
2376
|
+
${fullCustomerAccess ? "name\n email" : ""}
|
|
2330
2377
|
rating
|
|
2331
2378
|
comment
|
|
2332
2379
|
reviewedAt
|
|
@@ -2337,6 +2384,7 @@ var REVIEWS_QUERY = `
|
|
|
2337
2384
|
}
|
|
2338
2385
|
}
|
|
2339
2386
|
`;
|
|
2387
|
+
}
|
|
2340
2388
|
function buildReviewsVariables(params) {
|
|
2341
2389
|
return {
|
|
2342
2390
|
first: params.first,
|
|
@@ -2353,10 +2401,13 @@ var ERROR_PRODUCT_ID_REQUIRED = "productId is required";
|
|
|
2353
2401
|
var ERROR_INVALID_REVIEW_COUNT = `reviewCount must be an integer between ${MIN_REVIEW_COUNT} and ${MAX_REVIEW_COUNT}`;
|
|
2354
2402
|
var ERROR_INVALID_REVIEW_OFFSET = "reviewOffset must be a non-negative integer";
|
|
2355
2403
|
var ReviewService = class {
|
|
2356
|
-
constructor(client) {
|
|
2404
|
+
constructor(client, accessOptions) {
|
|
2357
2405
|
this.client = client;
|
|
2406
|
+
this.fullCustomerAccess = resolveAccessOptions(accessOptions).fullCustomerAccess;
|
|
2358
2407
|
}
|
|
2359
2408
|
client;
|
|
2409
|
+
/** Whether reviewer PII (name/email) is requested. */
|
|
2410
|
+
fullCustomerAccess;
|
|
2360
2411
|
/**
|
|
2361
2412
|
* Returns up to `reviewCount` reviews for an activity in **descending order
|
|
2362
2413
|
* by review date (newest first)**, skipping the `reviewOffset` newest reviews
|
|
@@ -2387,7 +2438,7 @@ var ReviewService = class {
|
|
|
2387
2438
|
const after = reviewOffset > 0 ? encodeCursor(reviewOffset - 1, reviewCount) : null;
|
|
2388
2439
|
const body = await this.client.request(
|
|
2389
2440
|
SALES_ENDPOINT,
|
|
2390
|
-
|
|
2441
|
+
buildReviewsQuery(this.fullCustomerAccess),
|
|
2391
2442
|
buildReviewsVariables({ activityId: productId, first: reviewCount, after })
|
|
2392
2443
|
);
|
|
2393
2444
|
const edges = body.data?.reviews?.edges ?? [];
|
|
@@ -3029,6 +3080,7 @@ var PEEK_TOKEN_ISSUER = "app_registry_v2";
|
|
|
3029
3080
|
var PeekAccessService = class {
|
|
3030
3081
|
client;
|
|
3031
3082
|
productServiceOptions;
|
|
3083
|
+
accessOptions;
|
|
3032
3084
|
jwtSecret;
|
|
3033
3085
|
productService;
|
|
3034
3086
|
accountUserService;
|
|
@@ -3065,6 +3117,7 @@ var PeekAccessService = class {
|
|
|
3065
3117
|
this.productServiceOptions = {
|
|
3066
3118
|
itemOptionsPageSize: config.itemOptionsPageSize
|
|
3067
3119
|
};
|
|
3120
|
+
this.accessOptions = resolveAccessOptions(config.accessOptions);
|
|
3068
3121
|
}
|
|
3069
3122
|
/**
|
|
3070
3123
|
* Verifies a Peek auth token issued by the app registry and returns the
|
|
@@ -3205,9 +3258,11 @@ var PeekAccessService = class {
|
|
|
3205
3258
|
*/
|
|
3206
3259
|
getBookingService() {
|
|
3207
3260
|
if (!this.bookingService) {
|
|
3208
|
-
this.bookingService = new BookingService(
|
|
3209
|
-
|
|
3210
|
-
|
|
3261
|
+
this.bookingService = new BookingService(
|
|
3262
|
+
this.client,
|
|
3263
|
+
{ productService: this.getProductService() },
|
|
3264
|
+
{ accessOptions: this.accessOptions }
|
|
3265
|
+
);
|
|
3211
3266
|
}
|
|
3212
3267
|
return this.bookingService;
|
|
3213
3268
|
}
|
|
@@ -3217,7 +3272,7 @@ var PeekAccessService = class {
|
|
|
3217
3272
|
*/
|
|
3218
3273
|
getReviewService() {
|
|
3219
3274
|
if (!this.reviewService) {
|
|
3220
|
-
this.reviewService = new ReviewService(this.client);
|
|
3275
|
+
this.reviewService = new ReviewService(this.client, this.accessOptions);
|
|
3221
3276
|
}
|
|
3222
3277
|
return this.reviewService;
|
|
3223
3278
|
}
|
|
@@ -3710,8 +3765,13 @@ function hasPriceBreakdown(node) {
|
|
|
3710
3765
|
|
|
3711
3766
|
// src/internal/peek/waivers/waiver-webhook.ts
|
|
3712
3767
|
var PAYLOAD_WAIVER_KEY = "waiver";
|
|
3713
|
-
function parseWaiverWebhook(payload) {
|
|
3714
|
-
|
|
3768
|
+
function parseWaiverWebhook(payload, options) {
|
|
3769
|
+
const waiver = fromWaiverNode(extractWaiverNode(payload));
|
|
3770
|
+
if (!resolveAccessOptions(options).fullCustomerAccess) {
|
|
3771
|
+
waiver.guestName = null;
|
|
3772
|
+
waiver.fileUrl = "";
|
|
3773
|
+
}
|
|
3774
|
+
return waiver;
|
|
3715
3775
|
}
|
|
3716
3776
|
function fromWaiverNode(node) {
|
|
3717
3777
|
const data = node ?? {};
|
|
@@ -3759,6 +3819,7 @@ exports.DailyNoteService = DailyNoteService;
|
|
|
3759
3819
|
exports.MembershipService = MembershipService;
|
|
3760
3820
|
exports.PeekAccessService = PeekAccessService;
|
|
3761
3821
|
exports.PeekGraphQLError = PeekGraphQLError;
|
|
3822
|
+
exports.PiiAccessDisabledError = PiiAccessDisabledError;
|
|
3762
3823
|
exports.ProductService = ProductService;
|
|
3763
3824
|
exports.PromoCodeService = PromoCodeService;
|
|
3764
3825
|
exports.RENTAL_PRODUCT_TYPE = RENTAL_PRODUCT_TYPE;
|