@peektravel/app-utilities 0.2.9 → 0.3.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/dist/index.js CHANGED
@@ -3,11 +3,11 @@ import { randomUUID } from 'crypto';
3
3
 
4
4
  // src/peek-access-service.ts
5
5
 
6
- // src/internal/gateway-endpoints.ts
6
+ // src/internal/peek/gateway-endpoints.ts
7
7
  var SALES_ENDPOINT = "sales";
8
8
  var V2_EXTENDABLE_SLUG = "peek_backoffice_api-v1";
9
9
 
10
- // src/internal/account-users/account-user-converter.ts
10
+ // src/internal/peek/account-users/account-user-converter.ts
11
11
  var ACTIVE_STATUS = "ACTIVE";
12
12
  function fromAccountUserNode(node) {
13
13
  if (!node || node.status !== ACTIVE_STATUS) {
@@ -35,7 +35,7 @@ function fromAccountUserNodes(nodes) {
35
35
  return users;
36
36
  }
37
37
 
38
- // src/internal/account-users/account-user-queries.ts
38
+ // src/internal/peek/account-users/account-user-queries.ts
39
39
  var USER_QUERY = `
40
40
  query Sales($first: Int, $after: String) {
41
41
  accountUsers(first: $first, after: $after) {
@@ -81,7 +81,7 @@ var USER_BY_FILTER_QUERY = `
81
81
  }
82
82
  `;
83
83
 
84
- // src/internal/account-users/account-user-service.ts
84
+ // src/internal/peek/account-users/account-user-service.ts
85
85
  var DEFAULT_PAGE_SIZE = 50;
86
86
  var AccountUserService = class {
87
87
  constructor(client, options = {}) {
@@ -135,7 +135,7 @@ var AccountUserService = class {
135
135
  }
136
136
  };
137
137
 
138
- // src/internal/availability/availability-queries.ts
138
+ // src/internal/peek/availability/availability-queries.ts
139
139
  var AVAILABILITY_TIMES_QUERY = `
140
140
  query Sales($activityId: ID!, $resourceOptionQuantities: [ResourceOptionQuantityData!]!, $date: Date!) {
141
141
  availabilityTimes(activityId: $activityId, resourceOptionQuantities: $resourceOptionQuantities, date: $date) {
@@ -160,7 +160,7 @@ var AVAILABILITY_TIMES_QUERY = `
160
160
  }
161
161
  `;
162
162
 
163
- // src/internal/availability/availability-service.ts
163
+ // src/internal/peek/availability/availability-service.ts
164
164
  var AvailabilityService = class {
165
165
  constructor(client) {
166
166
  this.client = client;
@@ -181,13 +181,375 @@ var AvailabilityService = class {
181
181
  }
182
182
  };
183
183
 
184
- // src/models/product.ts
184
+ // src/models/peek/product.ts
185
185
  var ACTIVITY_PRODUCT_TYPE = "ACTIVITY";
186
186
  var RENTAL_PRODUCT_TYPE = "RENTAL";
187
187
  var ADD_ON_PRODUCT_TYPE = "ADD-ON";
188
188
 
189
- // src/internal/bookings/booking-converter.ts
189
+ // src/internal/peek/bookings/booking-queries.ts
190
+ var SEARCH_BY_PURCHASE_DATE = "purchaseDate";
191
+ var SEARCH_BY_ACTIVITY_DATE = "activityDate";
192
+ function normalizeBookingId(bookingId) {
193
+ return bookingId.toLowerCase().replace(/-/g, "_");
194
+ }
195
+ var guestFields = `
196
+ id
197
+ name
198
+ country
199
+ dateOfBirth
200
+ email
201
+ isGdpr
202
+ isParticipant
203
+ optinSms
204
+ optinMarketing
205
+ phone
206
+ postalCode
207
+ fieldResponses {
208
+ id
209
+ text
210
+ fieldLocation {
211
+ field {
212
+ name
213
+ }
214
+ }
215
+ }
216
+ `;
217
+ var bookingGuestsFields = `
218
+ bookingGuests {
219
+ ${guestFields}
220
+ }
221
+ primaryGuest {
222
+ ${guestFields}
223
+ }
224
+ `;
225
+ var bookingQueryFields = `
226
+ displayId
227
+ id
228
+ primaryGuest {
229
+ name
230
+ email
231
+ phone
232
+ optinMarketing
233
+ optinSms
234
+ isGdpr
235
+ postalCode
236
+ }
237
+ activitySnapshot {
238
+ type
239
+ name
240
+ id
241
+ }
242
+ ticketQuantities {
243
+ quantity
244
+ resourceOptionSnapshot {
245
+ name
246
+ id
247
+ }
248
+ }
249
+ reservationStatus
250
+ checkinStatus
251
+ returnStatus
252
+ fulfillmentStatusOverride {
253
+ status
254
+ }
255
+ timeSnapshot {
256
+ id
257
+ legacyId
258
+ }
259
+ purchasedAt
260
+ purchasedAtUtc
261
+ startsAt
262
+ startsAtUtc
263
+ endsAt
264
+ endsAtUtc
265
+ availabilityTimeId
266
+ bookingPortalUrl
267
+ operatorNotes
268
+ value {
269
+ total {
270
+ formatted
271
+ amount
272
+ }
273
+ }
274
+ balance {
275
+ total {
276
+ amount
277
+ formatted
278
+ }
279
+ }
280
+ tips {
281
+ price {
282
+ amount
283
+ formatted
284
+ }
285
+ }
286
+ order {
287
+ displayId
288
+ id
289
+ promoCodes {
290
+ code
291
+ }
292
+ channelSnapshot {
293
+ id
294
+ name
295
+ agent {
296
+ name
297
+ }
298
+ }
299
+ initialQuote {
300
+ source {
301
+ actor {
302
+ app
303
+ name
304
+ }
305
+ }
306
+ }
307
+ }
308
+ questionAnswers {
309
+ answer
310
+ questionText
311
+ questionLocationSnapshot {
312
+ latitude
313
+ longitude
314
+ }
315
+ }
316
+ tickets {
317
+ questionAnswers {
318
+ answer
319
+ questionText
320
+ }
321
+ }
322
+ resourcePoolAssignments {
323
+ quantity
324
+ resourcePool {
325
+ name
326
+ shortName
327
+ resources {
328
+ name
329
+ }
330
+ }
331
+ resourceAssignments {
332
+ resource {
333
+ id
334
+ name
335
+ }
336
+ }
337
+ }
338
+ `;
339
+ var PRICE_BREAKDOWN_FIELDS = `
340
+ convenienceFee { amount formatted }
341
+ deposit { amount formatted }
342
+ discount { amount formatted }
343
+ discountedPrice { amount formatted }
344
+ fees { amount formatted }
345
+ flatPartnerFee { amount formatted }
346
+ price { amount formatted }
347
+ retailPrice { amount formatted }
348
+ taxes { amount formatted }
349
+ tips { amount formatted }
350
+ `;
351
+ var TICKET_VALUE_FIELDS = `
352
+ value {
353
+ price { amount formatted }
354
+ total { amount formatted }
355
+ }
356
+ `;
357
+ function buildBookingsListingQuery(includeGuests, includePriceBreakdown) {
358
+ const guestsSection = includeGuests ? bookingGuestsFields : "";
359
+ const fields = includePriceBreakdown ? bookingQueryFields.replace("value {", `value { ${PRICE_BREAKDOWN_FIELDS}`).replace("ticketQuantities {", `ticketQuantities { ${TICKET_VALUE_FIELDS}`) : bookingQueryFields;
360
+ return `
361
+ query Sales($after: String, $first: Int, $filter: SalesFilter!, $orderBy: SalesOrdering) {
362
+ sales(after: $after, first: $first, filter: $filter, orderBy: $orderBy) {
363
+ pageInfo { endCursor hasNextPage }
364
+ edges {
365
+ node {
366
+ ... on Booking {
367
+ ${fields}
368
+ ${guestsSection}
369
+ }
370
+ }
371
+ }
372
+ }
373
+ }
374
+ `;
375
+ }
376
+ var BOOKING_GUESTS_QUERY = `
377
+ query Sales($after: String, $first: Int, $filter: SalesFilter!, $orderBy: SalesOrdering) {
378
+ sales(after: $after, first: $first, filter: $filter, orderBy: $orderBy) {
379
+ pageInfo { endCursor hasNextPage }
380
+ edges {
381
+ node {
382
+ ... on Booking {
383
+ displayId
384
+ id
385
+ ${bookingGuestsFields}
386
+ }
387
+ }
388
+ }
389
+ }
390
+ }
391
+ `;
392
+ var BOOKING_PAYMENTS_ON_FILE_QUERY = `
393
+ query Sales($after: String, $first: Int, $filter: SalesFilter!, $orderBy: SalesOrdering) {
394
+ sales(after: $after, first: $first, filter: $filter, orderBy: $orderBy) {
395
+ pageInfo { endCursor hasNextPage }
396
+ edges {
397
+ node {
398
+ order {
399
+ payments {
400
+ id
401
+ paymentSource { id }
402
+ appliedAt
403
+ currentAmount { amount currency }
404
+ refundableAmount { amount currency }
405
+ }
406
+ id
407
+ displayId
408
+ paymentSources { description type id }
409
+ }
410
+ ... on Booking {
411
+ displayId
412
+ id
413
+ }
414
+ }
415
+ }
416
+ }
417
+ }
418
+ `;
419
+ var UPDATE_OPERATOR_NOTES_MUTATION = `
420
+ mutation Account($input: UpdateOperatorNotesForBookingInput!) {
421
+ updateOperatorNotesForBooking(input: $input) {
422
+ booking { operatorNotes }
423
+ }
424
+ }
425
+ `;
426
+ var UPDATE_BOOKING_CHECKIN_MUTATION = `
427
+ mutation Account($input: UpdateBookingCheckInInput!) {
428
+ updateBookingCheckIn(input: $input) {
429
+ booking { checkinStatus }
430
+ }
431
+ }
432
+ `;
433
+ var CANCEL_BOOKING_MUTATION = `
434
+ mutation Account($input: CancelBookingInput!) {
435
+ cancelBooking(input: $input) {
436
+ booking { id displayId reservationStatus }
437
+ }
438
+ }
439
+ `;
440
+ var APPLY_PAYMENT_TO_ORDER_MUTATION = `
441
+ mutation ApplyPaymentToOrder($input: ApplyPaymentToOrderInput!) {
442
+ applyPaymentToOrder(input: $input) {
443
+ transactionId
444
+ errors { code detail value }
445
+ }
446
+ }
447
+ `;
448
+ var APPLY_REFUND_TO_ORDER_MUTATION = `
449
+ mutation ApplyRefundToOrder($input: ApplyRefundToOrderInput!) {
450
+ applyRefundToOrder(input: $input) {
451
+ transactionId
452
+ errors { code detail value }
453
+ }
454
+ }
455
+ `;
456
+ var CREATE_INVOICE_LINK_MUTATION = `
457
+ mutation CreateInvoiceLink($input: CreateInvoiceLinkInput!) {
458
+ createInvoiceLink(input: $input) {
459
+ invoiceLink { status url }
460
+ errors { code detail value __typename }
461
+ }
462
+ }
463
+ `;
464
+ var CREATE_QUOTE_FROM_ORDER_MUTATION = `
465
+ mutation CreateQuoteFromOrder($input: CreateQuoteFromOrderInput!) {
466
+ createQuoteFromOrder(input: $input) {
467
+ errors { detail value code }
468
+ quote {
469
+ id
470
+ saleQuotes { refid reservationStatus }
471
+ }
472
+ }
473
+ }
474
+ `;
475
+ var UPDATE_QUOTE_V2_MUTATION = `
476
+ mutation UpdateQuoteV2($input: UpdateQuoteV2Input!) {
477
+ updateQuoteV2(input: $input) {
478
+ errors { detail value code }
479
+ quote { id }
480
+ }
481
+ }
482
+ `;
483
+ var AMEND_ORDER_MUTATION = `
484
+ mutation AmendOrder($input: AmendOrderInput!) {
485
+ amendOrder(input: $input) {
486
+ errors { code detail value }
487
+ order { id }
488
+ }
489
+ }
490
+ `;
491
+ var CREATE_QUOTE_V2_MUTATION = `
492
+ mutation CreateQuoteV2($input: CreateQuoteV2Input!) {
493
+ createQuoteV2(input: $input) {
494
+ errors { detail value code }
495
+ quote { id }
496
+ }
497
+ }
498
+ `;
499
+ var CREATE_ORDER_FROM_QUOTE_MUTATION = `
500
+ mutation CreateOrderFromQuote($input: CreateOrderFromQuoteInput!) {
501
+ createOrderFromQuote(input: $input) {
502
+ errors { code detail value }
503
+ order {
504
+ id
505
+ sales {
506
+ id
507
+ displayId
508
+ ... on Booking {
509
+ balance { total { amount currency formatted } }
510
+ }
511
+ }
512
+ }
513
+ }
514
+ }
515
+ `;
516
+ function buildBookingsVariables(params) {
517
+ const filter = {};
518
+ const bookingFilter = {};
519
+ if (params.bookingId) {
520
+ bookingFilter.ids = [normalizeBookingId(params.bookingId)];
521
+ } else if (params.timeslotId) {
522
+ bookingFilter.timeslotRefid = params.timeslotId;
523
+ } else {
524
+ if (params.searchBy === SEARCH_BY_ACTIVITY_DATE) {
525
+ bookingFilter.overlapsRange = `[${params.startDateTime},${params.endDateTime}]`;
526
+ } else if (params.searchBy === SEARCH_BY_PURCHASE_DATE) {
527
+ filter.purchasedAtRangeUtc = `[${params.startDateTime},${params.endDateTime}]`;
528
+ }
529
+ if (params.productId) {
530
+ bookingFilter.activityIds = [params.productId];
531
+ }
532
+ }
533
+ if (params.email) {
534
+ filter.primaryGuestEmail = params.email;
535
+ }
536
+ if (params.searchString && params.searchString.length > 0) {
537
+ filter.searchString = params.searchString;
538
+ }
539
+ if (Object.keys(bookingFilter).length > 0) {
540
+ filter.bookingFilter = bookingFilter;
541
+ }
542
+ return {
543
+ first: params.pageSize,
544
+ after: params.after,
545
+ orderBy: { direction: "ASC", field: "STARTS_AT" },
546
+ filter
547
+ };
548
+ }
549
+
550
+ // src/internal/peek/bookings/booking-converter.ts
190
551
  var UNKNOWN = "unknown";
552
+ var PEEK_PRO_DEEP_LINK_BASE = "http://pro-app.peek.com/-/order";
191
553
  var SOURCE_SOURCE_MAP = {
192
554
  APP_REGISTRY: "app",
193
555
  BOOKING_IMPORTER_FUTURE: "importer",
@@ -303,6 +665,7 @@ function fromBookingNode(node, includeGuests = false, includePriceBreakdown = fa
303
665
  resellerId: data.order?.channelSnapshot?.id || null,
304
666
  resellerName: resellerNameFromChannelSnapshot(data.order?.channelSnapshot),
305
667
  orderId: data.order?.id || "",
668
+ peekProBookingDeepLink: buildPeekProBookingDeepLink(data.order?.id, data.id),
306
669
  convenienceFee: includePriceBreakdown ? mapPrice(data.value?.convenienceFee) : void 0,
307
670
  deposit: includePriceBreakdown ? mapPrice(data.value?.deposit) : void 0,
308
671
  discount: includePriceBreakdown ? mapPrice(data.value?.discount) : void 0,
@@ -329,619 +692,262 @@ function convertGuests(data) {
329
692
  guests.push(mapGuestNode(primaryGuestNode, true));
330
693
  }
331
694
  return guests;
332
- }
333
- function mapGuestNode(guestNode, isPrimary) {
334
- const fieldResponses = Array.isArray(guestNode.fieldResponses) ? guestNode.fieldResponses : [];
335
- const metadata = fieldResponses.map((response) => ({
336
- id: response.id,
337
- name: response.fieldLocation?.field?.name ?? "",
338
- value: response.text ?? ""
339
- }));
340
- return {
341
- id: guestNode.id,
342
- name: guestNode.name ?? null,
343
- country: guestNode.country ?? null,
344
- dateOfBirth: guestNode.dateOfBirth ? new Date(guestNode.dateOfBirth) : null,
345
- phone: guestNode.phone ?? null,
346
- email: guestNode.email ?? null,
347
- isGdpr: Boolean(guestNode.isGdpr),
348
- isParticipant: Boolean(guestNode.isParticipant),
349
- isPrimary,
350
- optinSms: Boolean(guestNode.optinSms),
351
- optinMarketing: Boolean(guestNode.optinMarketing),
352
- postalCode: guestNode.postalCode ?? null,
353
- metadata
354
- };
355
- }
356
- function sourceFromApp(app) {
357
- if (!app) return UNKNOWN;
358
- return SOURCE_SOURCE_MAP[app] ?? UNKNOWN;
359
- }
360
- function sourceDescriptionFromApp(app) {
361
- if (!app) return UNKNOWN;
362
- return SOURCE_DESC_MAP[app] ?? UNKNOWN;
363
- }
364
- function resellerNameFromChannelSnapshot(channelSnapshot) {
365
- if (!channelSnapshot) return null;
366
- let out = channelSnapshot.name ?? "";
367
- if (channelSnapshot.agent?.name) {
368
- out += " - " + channelSnapshot.agent.name;
369
- }
370
- return out;
371
- }
372
- function ticketsToTicketArray(ticketQuantities, includePriceBreakdown) {
373
- if (!ticketQuantities || ticketQuantities.length === 0) return [];
374
- return ticketQuantities.map((ticket) => ({
375
- name: ticket.resourceOptionSnapshot?.name || "Unknown",
376
- quantity: ticket.quantity || 0,
377
- ticketId: ticket.resourceOptionSnapshot?.id || "unknown",
378
- listPrice: includePriceBreakdown ? mapPrice(ticket.value?.price) : void 0,
379
- totalValue: includePriceBreakdown ? mapPrice(ticket.value?.total) : void 0
380
- }));
381
- }
382
- function durationInMin(startsAt, endsAt) {
383
- if (!startsAt || !endsAt) return 0;
384
- const duration = new Date(endsAt).getTime() - new Date(startsAt).getTime();
385
- return Math.floor(duration / 1e3 / 60);
386
- }
387
- function ticketQuantity(ticketQuantities) {
388
- if (!ticketQuantities || ticketQuantities.length === 0) return 0;
389
- return ticketQuantities.reduce((acc, ticket) => acc + (ticket.quantity || 0), 0);
390
- }
391
- function formatTickets(ticketQuantities) {
392
- if (!ticketQuantities || ticketQuantities.length === 0) return "";
393
- return ticketQuantities.map((ticket) => `${ticket.quantity || 0}x ${ticket.resourceOptionSnapshot?.name || "Unknown"}`).join(", ");
394
- }
395
- function mapResourcePoolAssignments(poolAssignments) {
396
- if (!poolAssignments || poolAssignments.length === 0) return [];
397
- return poolAssignments.flatMap(
398
- (pool) => (pool.resourceAssignments ?? []).map((assignment) => ({
399
- id: assignment.resource?.id || "",
400
- name: assignment.resource?.name || ""
401
- }))
402
- );
403
- }
404
- function mapPrice(priceData) {
405
- if (!priceData || !priceData.amount && !priceData.formatted) {
406
- return void 0;
407
- }
408
- return {
409
- amount: priceData.amount || "0",
410
- display: priceData.formatted || ""
411
- };
412
- }
413
-
414
- // src/internal/bookings/booking-guest-converter.ts
415
- function fromBookingGuestsResponse(response) {
416
- const firstEdge = (response?.sales?.edges ?? [])[0];
417
- if (!firstEdge) {
418
- return [];
419
- }
420
- const bookingNode = firstEdge.node;
421
- const primaryGuestNode = bookingNode.primaryGuest;
422
- const bookingGuestsNodes = Array.isArray(bookingNode.bookingGuests) ? bookingNode.bookingGuests : [];
423
- const primaryId = primaryGuestNode?.id;
424
- const guests = [];
425
- for (const guestNode of bookingGuestsNodes) {
426
- guests.push(mapGuestNode(guestNode, primaryId ? guestNode.id === primaryId : false));
427
- }
428
- const hasPrimaryInGuests = primaryId ? bookingGuestsNodes.some((guest) => guest.id === primaryId) : false;
429
- if (!hasPrimaryInGuests && primaryGuestNode) {
430
- guests.push(mapGuestNode(primaryGuestNode, true));
431
- }
432
- return guests;
433
- }
434
-
435
- // src/internal/bookings/payments-on-file-converter.ts
436
- function fromPaymentsOnFileResponse(response, bookingId) {
437
- const firstEdge = (response?.sales?.edges ?? [])[0];
438
- if (!firstEdge) {
439
- return null;
440
- }
441
- const order = firstEdge.node.order;
442
- const orderId = order?.id ?? "";
443
- const rawPaymentSources = order?.paymentSources ?? [];
444
- const rawPayments = order?.payments ?? [];
445
- const paymentsBySourceId = /* @__PURE__ */ new Map();
446
- for (const payment of rawPayments) {
447
- const sourceId = payment.paymentSource?.id;
448
- if (!sourceId) continue;
449
- const mapped = {
450
- id: payment.id,
451
- paidAt: dateOnly(payment.appliedAt),
452
- currentAmount: payment.currentAmount,
453
- refundableAmount: payment.refundableAmount
454
- };
455
- const existing = paymentsBySourceId.get(sourceId);
456
- if (existing) {
457
- existing.push(mapped);
458
- } else {
459
- paymentsBySourceId.set(sourceId, [mapped]);
460
- }
461
- }
462
- const paymentsOnFile = rawPaymentSources.map((source) => {
463
- const payments = paymentsBySourceId.get(source.id);
464
- return {
465
- description: source.description,
466
- id: source.id,
467
- type: source.type,
468
- ...payments ? { payments } : {}
469
- };
470
- });
471
- return { bookingId, orderId, paymentsOnFile };
472
- }
473
- function dateOnly(iso) {
474
- return iso.split("T")[0] ?? iso;
475
- }
476
-
477
- // src/internal/bookings/addon-queries.ts
478
- var RESERVATION_STATUS_CONFIRMED = "CONFIRMED";
479
- var ADDON_OPTION_STATUS_CANCELED = "CANCELED";
480
- var SALES_ADDONS_PAGE_SIZE = 100;
481
- var SALES_ADDONS_QUERY = `
482
- query Sales($after: String, $first: Int, $filter: SalesFilter!, $orderBy: SalesOrdering) {
483
- sales(after: $after, first: $first, filter: $filter, orderBy: $orderBy) {
484
- pageInfo { endCursor hasNextPage }
485
- edges {
486
- node {
487
- order { id displayId }
488
- ... on Booking {
489
- displayId
490
- id
491
- refid
492
- reservationStatus
493
- items {
494
- id
495
- refid
496
- value { total { amount currency formatted } }
497
- reservationStatus
498
- options {
499
- refid
500
- reservationStatus
501
- price { amount currency formatted }
502
- itemOptionSnapshot { id name }
503
- itemSnapshot { id name }
504
- }
505
- }
506
- }
507
- }
508
- }
509
- }
510
- }
511
- `;
512
- function buildSalesAddonsVariables(searchString) {
695
+ }
696
+ function mapGuestNode(guestNode, isPrimary) {
697
+ const fieldResponses = Array.isArray(guestNode.fieldResponses) ? guestNode.fieldResponses : [];
698
+ const metadata = fieldResponses.map((response) => ({
699
+ id: response.id,
700
+ name: response.fieldLocation?.field?.name ?? "",
701
+ value: response.text ?? ""
702
+ }));
513
703
  return {
514
- first: SALES_ADDONS_PAGE_SIZE,
515
- after: null,
516
- orderBy: { direction: "DESC", field: "STARTS_AT" },
517
- filter: { searchString }
704
+ id: guestNode.id,
705
+ name: guestNode.name ?? null,
706
+ country: guestNode.country ?? null,
707
+ dateOfBirth: guestNode.dateOfBirth ? new Date(guestNode.dateOfBirth) : null,
708
+ phone: guestNode.phone ?? null,
709
+ email: guestNode.email ?? null,
710
+ isGdpr: Boolean(guestNode.isGdpr),
711
+ isParticipant: Boolean(guestNode.isParticipant),
712
+ isPrimary,
713
+ optinSms: Boolean(guestNode.optinSms),
714
+ optinMarketing: Boolean(guestNode.optinMarketing),
715
+ postalCode: guestNode.postalCode ?? null,
716
+ metadata
518
717
  };
519
718
  }
520
-
521
- // src/internal/bookings/addon-converter.ts
522
- var ERROR_INCONSISTENT_ADDON_ITEM_ID = "Add-on group contains options with mismatched item IDs";
523
- function parseSaleNode(node) {
524
- const bookingId = node.id || "";
525
- const displayId = node.displayId || "";
526
- const orderId = node.order?.id || "";
527
- const bookingQuoteRefid = node.refid || "";
528
- const bookingQuoteReservationStatus = node.reservationStatus || "";
529
- const items = Array.isArray(node.items) ? node.items : [];
530
- return items.map((item) => {
531
- const options = Array.isArray(item.options) ? item.options : [];
532
- const addonItemOptions = options.map((opt) => ({
533
- itemId: opt.itemSnapshot?.id || "",
534
- optionId: opt.itemOptionSnapshot?.id || "",
535
- itemName: opt.itemSnapshot?.name || "",
536
- optionName: opt.itemOptionSnapshot?.name || "",
537
- optionRefid: opt.refid || "",
538
- optionReservationStatus: opt.reservationStatus || "",
539
- itemReservationStatus: item.reservationStatus || "",
540
- itemRefid: item.refid || ""
541
- }));
542
- return {
543
- bookingId,
544
- displayId,
545
- orderId,
546
- total: item.value?.total ?? null,
547
- bookingQuoteRefid,
548
- bookingQuoteReservationStatus,
549
- addonItemOptions
550
- };
551
- });
719
+ function buildPeekProBookingDeepLink(orderId, bookingId) {
720
+ if (!orderId || !bookingId) return "";
721
+ return `${PEEK_PRO_DEEP_LINK_BASE}%2F${normalizeBookingId(orderId)}%3FsaleId=${normalizeBookingId(bookingId)}`;
552
722
  }
553
- function toBookingAddon(item) {
554
- const options = item.addonItemOptions || [];
555
- if (options.length === 0) {
556
- return null;
557
- }
558
- const addonId = options[0].itemId;
559
- const addonName = options[0].itemName;
560
- if (options.some((opt) => opt.itemId !== addonId)) {
561
- throw new Error(ERROR_INCONSISTENT_ADDON_ITEM_ID);
562
- }
563
- const grouped = /* @__PURE__ */ new Map();
564
- options.filter((opt) => opt.optionReservationStatus !== ADDON_OPTION_STATUS_CANCELED).forEach((opt) => {
565
- const existing = grouped.get(opt.optionId);
566
- if (existing) {
567
- existing.quantity += 1;
568
- } else {
569
- grouped.set(opt.optionId, {
570
- addonOptionId: opt.optionId,
571
- addonOptionName: opt.optionName,
572
- quantity: 1
573
- });
574
- }
575
- });
576
- const addonOptions = Array.from(grouped.values());
577
- if (addonOptions.length === 0) {
578
- return null;
579
- }
580
- return { addonId, addonName, total: item.total, addonOptions };
723
+ function sourceFromApp(app) {
724
+ if (!app) return UNKNOWN;
725
+ return SOURCE_SOURCE_MAP[app] ?? UNKNOWN;
581
726
  }
582
-
583
- // src/internal/bookings/booking-queries.ts
584
- var SEARCH_BY_PURCHASE_DATE = "purchaseDate";
585
- var SEARCH_BY_ACTIVITY_DATE = "activityDate";
586
- function normalizeBookingId(bookingId) {
587
- return bookingId.toLowerCase().replace(/-/g, "_");
727
+ function sourceDescriptionFromApp(app) {
728
+ if (!app) return UNKNOWN;
729
+ return SOURCE_DESC_MAP[app] ?? UNKNOWN;
588
730
  }
589
- var guestFields = `
590
- id
591
- name
592
- country
593
- dateOfBirth
594
- email
595
- isGdpr
596
- isParticipant
597
- optinSms
598
- optinMarketing
599
- phone
600
- postalCode
601
- fieldResponses {
602
- id
603
- text
604
- fieldLocation {
605
- field {
606
- name
607
- }
608
- }
609
- }
610
- `;
611
- var bookingGuestsFields = `
612
- bookingGuests {
613
- ${guestFields}
614
- }
615
- primaryGuest {
616
- ${guestFields}
617
- }
618
- `;
619
- var bookingQueryFields = `
620
- displayId
621
- id
622
- primaryGuest {
623
- name
624
- email
625
- phone
626
- optinMarketing
627
- optinSms
628
- isGdpr
629
- postalCode
630
- }
631
- activitySnapshot {
632
- type
633
- name
634
- id
635
- }
636
- ticketQuantities {
637
- quantity
638
- resourceOptionSnapshot {
639
- name
640
- id
641
- }
642
- }
643
- reservationStatus
644
- checkinStatus
645
- returnStatus
646
- fulfillmentStatusOverride {
647
- status
648
- }
649
- timeSnapshot {
650
- id
651
- legacyId
652
- }
653
- purchasedAt
654
- purchasedAtUtc
655
- startsAt
656
- startsAtUtc
657
- endsAt
658
- endsAtUtc
659
- availabilityTimeId
660
- bookingPortalUrl
661
- operatorNotes
662
- value {
663
- total {
664
- formatted
665
- amount
666
- }
667
- }
668
- balance {
669
- total {
670
- amount
671
- formatted
672
- }
673
- }
674
- tips {
675
- price {
676
- amount
677
- formatted
678
- }
731
+ function resellerNameFromChannelSnapshot(channelSnapshot) {
732
+ if (!channelSnapshot) return null;
733
+ let out = channelSnapshot.name ?? "";
734
+ if (channelSnapshot.agent?.name) {
735
+ out += " - " + channelSnapshot.agent.name;
679
736
  }
680
- order {
681
- displayId
682
- id
683
- promoCodes {
684
- code
685
- }
686
- channelSnapshot {
687
- id
688
- name
689
- agent {
690
- name
691
- }
692
- }
693
- initialQuote {
694
- source {
695
- actor {
696
- app
697
- name
698
- }
699
- }
700
- }
737
+ return out;
738
+ }
739
+ function ticketsToTicketArray(ticketQuantities, includePriceBreakdown) {
740
+ if (!ticketQuantities || ticketQuantities.length === 0) return [];
741
+ return ticketQuantities.map((ticket) => ({
742
+ name: ticket.resourceOptionSnapshot?.name || "Unknown",
743
+ quantity: ticket.quantity || 0,
744
+ ticketId: ticket.resourceOptionSnapshot?.id || "unknown",
745
+ listPrice: includePriceBreakdown ? mapPrice(ticket.value?.price) : void 0,
746
+ totalValue: includePriceBreakdown ? mapPrice(ticket.value?.total) : void 0
747
+ }));
748
+ }
749
+ function durationInMin(startsAt, endsAt) {
750
+ if (!startsAt || !endsAt) return 0;
751
+ const duration = new Date(endsAt).getTime() - new Date(startsAt).getTime();
752
+ return Math.floor(duration / 1e3 / 60);
753
+ }
754
+ function ticketQuantity(ticketQuantities) {
755
+ if (!ticketQuantities || ticketQuantities.length === 0) return 0;
756
+ return ticketQuantities.reduce((acc, ticket) => acc + (ticket.quantity || 0), 0);
757
+ }
758
+ function formatTickets(ticketQuantities) {
759
+ if (!ticketQuantities || ticketQuantities.length === 0) return "";
760
+ return ticketQuantities.map((ticket) => `${ticket.quantity || 0}x ${ticket.resourceOptionSnapshot?.name || "Unknown"}`).join(", ");
761
+ }
762
+ function mapResourcePoolAssignments(poolAssignments) {
763
+ if (!poolAssignments || poolAssignments.length === 0) return [];
764
+ return poolAssignments.flatMap(
765
+ (pool) => (pool.resourceAssignments ?? []).map((assignment) => ({
766
+ id: assignment.resource?.id || "",
767
+ name: assignment.resource?.name || ""
768
+ }))
769
+ );
770
+ }
771
+ function mapPrice(priceData) {
772
+ if (!priceData || !priceData.amount && !priceData.formatted) {
773
+ return void 0;
701
774
  }
702
- questionAnswers {
703
- answer
704
- questionText
705
- questionLocationSnapshot {
706
- latitude
707
- longitude
708
- }
775
+ return {
776
+ amount: priceData.amount || "0",
777
+ display: priceData.formatted || ""
778
+ };
779
+ }
780
+
781
+ // src/internal/peek/bookings/booking-guest-converter.ts
782
+ function fromBookingGuestsResponse(response) {
783
+ const firstEdge = (response?.sales?.edges ?? [])[0];
784
+ if (!firstEdge) {
785
+ return [];
709
786
  }
710
- tickets {
711
- questionAnswers {
712
- answer
713
- questionText
714
- }
787
+ const bookingNode = firstEdge.node;
788
+ const primaryGuestNode = bookingNode.primaryGuest;
789
+ const bookingGuestsNodes = Array.isArray(bookingNode.bookingGuests) ? bookingNode.bookingGuests : [];
790
+ const primaryId = primaryGuestNode?.id;
791
+ const guests = [];
792
+ for (const guestNode of bookingGuestsNodes) {
793
+ guests.push(mapGuestNode(guestNode, primaryId ? guestNode.id === primaryId : false));
715
794
  }
716
- resourcePoolAssignments {
717
- quantity
718
- resourcePool {
719
- name
720
- shortName
721
- resources {
722
- name
723
- }
724
- }
725
- resourceAssignments {
726
- resource {
727
- id
728
- name
729
- }
730
- }
795
+ const hasPrimaryInGuests = primaryId ? bookingGuestsNodes.some((guest) => guest.id === primaryId) : false;
796
+ if (!hasPrimaryInGuests && primaryGuestNode) {
797
+ guests.push(mapGuestNode(primaryGuestNode, true));
731
798
  }
732
- `;
733
- var PRICE_BREAKDOWN_FIELDS = `
734
- convenienceFee { amount formatted }
735
- deposit { amount formatted }
736
- discount { amount formatted }
737
- discountedPrice { amount formatted }
738
- fees { amount formatted }
739
- flatPartnerFee { amount formatted }
740
- price { amount formatted }
741
- retailPrice { amount formatted }
742
- taxes { amount formatted }
743
- tips { amount formatted }
744
- `;
745
- var TICKET_VALUE_FIELDS = `
746
- value {
747
- price { amount formatted }
748
- total { amount formatted }
799
+ return guests;
800
+ }
801
+
802
+ // src/internal/peek/bookings/payments-on-file-converter.ts
803
+ function fromPaymentsOnFileResponse(response, bookingId) {
804
+ const firstEdge = (response?.sales?.edges ?? [])[0];
805
+ if (!firstEdge) {
806
+ return null;
749
807
  }
750
- `;
751
- function buildBookingsListingQuery(includeGuests, includePriceBreakdown) {
752
- const guestsSection = includeGuests ? bookingGuestsFields : "";
753
- const fields = includePriceBreakdown ? bookingQueryFields.replace("value {", `value { ${PRICE_BREAKDOWN_FIELDS}`).replace("ticketQuantities {", `ticketQuantities { ${TICKET_VALUE_FIELDS}`) : bookingQueryFields;
754
- return `
755
- query Sales($after: String, $first: Int, $filter: SalesFilter!, $orderBy: SalesOrdering) {
756
- sales(after: $after, first: $first, filter: $filter, orderBy: $orderBy) {
757
- pageInfo { endCursor hasNextPage }
758
- edges {
759
- node {
760
- ... on Booking {
761
- ${fields}
762
- ${guestsSection}
763
- }
764
- }
765
- }
766
- }
808
+ const order = firstEdge.node.order;
809
+ const orderId = order?.id ?? "";
810
+ const rawPaymentSources = order?.paymentSources ?? [];
811
+ const rawPayments = order?.payments ?? [];
812
+ const paymentsBySourceId = /* @__PURE__ */ new Map();
813
+ for (const payment of rawPayments) {
814
+ const sourceId = payment.paymentSource?.id;
815
+ if (!sourceId) continue;
816
+ const mapped = {
817
+ id: payment.id,
818
+ paidAt: dateOnly(payment.appliedAt),
819
+ currentAmount: payment.currentAmount,
820
+ refundableAmount: payment.refundableAmount
821
+ };
822
+ const existing = paymentsBySourceId.get(sourceId);
823
+ if (existing) {
824
+ existing.push(mapped);
825
+ } else {
826
+ paymentsBySourceId.set(sourceId, [mapped]);
767
827
  }
768
- `;
828
+ }
829
+ const paymentsOnFile = rawPaymentSources.map((source) => {
830
+ const payments = paymentsBySourceId.get(source.id);
831
+ return {
832
+ description: source.description,
833
+ id: source.id,
834
+ type: source.type,
835
+ ...payments ? { payments } : {}
836
+ };
837
+ });
838
+ return { bookingId, orderId, paymentsOnFile };
769
839
  }
770
- var BOOKING_GUESTS_QUERY = `
840
+ function dateOnly(iso) {
841
+ return iso.split("T")[0] ?? iso;
842
+ }
843
+
844
+ // src/internal/peek/bookings/addon-queries.ts
845
+ var RESERVATION_STATUS_CONFIRMED = "CONFIRMED";
846
+ var ADDON_OPTION_STATUS_CANCELED = "CANCELED";
847
+ var SALES_ADDONS_PAGE_SIZE = 100;
848
+ var SALES_ADDONS_QUERY = `
771
849
  query Sales($after: String, $first: Int, $filter: SalesFilter!, $orderBy: SalesOrdering) {
772
850
  sales(after: $after, first: $first, filter: $filter, orderBy: $orderBy) {
773
851
  pageInfo { endCursor hasNextPage }
774
852
  edges {
775
853
  node {
854
+ order { id displayId }
776
855
  ... on Booking {
777
856
  displayId
778
857
  id
779
- ${bookingGuestsFields}
780
- }
781
- }
782
- }
783
- }
784
- }
785
- `;
786
- var BOOKING_PAYMENTS_ON_FILE_QUERY = `
787
- query Sales($after: String, $first: Int, $filter: SalesFilter!, $orderBy: SalesOrdering) {
788
- sales(after: $after, first: $first, filter: $filter, orderBy: $orderBy) {
789
- pageInfo { endCursor hasNextPage }
790
- edges {
791
- node {
792
- order {
793
- payments {
858
+ refid
859
+ reservationStatus
860
+ items {
794
861
  id
795
- paymentSource { id }
796
- appliedAt
797
- currentAmount { amount currency }
798
- refundableAmount { amount currency }
799
- }
800
- id
801
- displayId
802
- paymentSources { description type id }
803
- }
804
- ... on Booking {
805
- displayId
806
- id
807
- }
808
- }
809
- }
810
- }
811
- }
812
- `;
813
- var UPDATE_OPERATOR_NOTES_MUTATION = `
814
- mutation Account($input: UpdateOperatorNotesForBookingInput!) {
815
- updateOperatorNotesForBooking(input: $input) {
816
- booking { operatorNotes }
817
- }
818
- }
819
- `;
820
- var UPDATE_BOOKING_CHECKIN_MUTATION = `
821
- mutation Account($input: UpdateBookingCheckInInput!) {
822
- updateBookingCheckIn(input: $input) {
823
- booking { checkinStatus }
824
- }
825
- }
826
- `;
827
- var CANCEL_BOOKING_MUTATION = `
828
- mutation Account($input: CancelBookingInput!) {
829
- cancelBooking(input: $input) {
830
- booking { id displayId reservationStatus }
831
- }
832
- }
833
- `;
834
- var APPLY_PAYMENT_TO_ORDER_MUTATION = `
835
- mutation ApplyPaymentToOrder($input: ApplyPaymentToOrderInput!) {
836
- applyPaymentToOrder(input: $input) {
837
- transactionId
838
- errors { code detail value }
839
- }
840
- }
841
- `;
842
- var APPLY_REFUND_TO_ORDER_MUTATION = `
843
- mutation ApplyRefundToOrder($input: ApplyRefundToOrderInput!) {
844
- applyRefundToOrder(input: $input) {
845
- transactionId
846
- errors { code detail value }
847
- }
848
- }
849
- `;
850
- var CREATE_INVOICE_LINK_MUTATION = `
851
- mutation CreateInvoiceLink($input: CreateInvoiceLinkInput!) {
852
- createInvoiceLink(input: $input) {
853
- invoiceLink { status url }
854
- errors { code detail value __typename }
855
- }
856
- }
857
- `;
858
- var CREATE_QUOTE_FROM_ORDER_MUTATION = `
859
- mutation CreateQuoteFromOrder($input: CreateQuoteFromOrderInput!) {
860
- createQuoteFromOrder(input: $input) {
861
- errors { detail value code }
862
- quote {
863
- id
864
- saleQuotes { refid reservationStatus }
865
- }
866
- }
867
- }
868
- `;
869
- var UPDATE_QUOTE_V2_MUTATION = `
870
- mutation UpdateQuoteV2($input: UpdateQuoteV2Input!) {
871
- updateQuoteV2(input: $input) {
872
- errors { detail value code }
873
- quote { id }
874
- }
875
- }
876
- `;
877
- var AMEND_ORDER_MUTATION = `
878
- mutation AmendOrder($input: AmendOrderInput!) {
879
- amendOrder(input: $input) {
880
- errors { code detail value }
881
- order { id }
882
- }
883
- }
884
- `;
885
- var CREATE_QUOTE_V2_MUTATION = `
886
- mutation CreateQuoteV2($input: CreateQuoteV2Input!) {
887
- createQuoteV2(input: $input) {
888
- errors { detail value code }
889
- quote { id }
890
- }
891
- }
892
- `;
893
- var CREATE_ORDER_FROM_QUOTE_MUTATION = `
894
- mutation CreateOrderFromQuote($input: CreateOrderFromQuoteInput!) {
895
- createOrderFromQuote(input: $input) {
896
- errors { code detail value }
897
- order {
898
- id
899
- sales {
900
- id
901
- displayId
902
- ... on Booking {
903
- balance { total { amount currency formatted } }
862
+ refid
863
+ value { total { amount currency formatted } }
864
+ reservationStatus
865
+ options {
866
+ refid
867
+ reservationStatus
868
+ price { amount currency formatted }
869
+ itemOptionSnapshot { id name }
870
+ itemSnapshot { id name }
871
+ }
872
+ }
904
873
  }
905
874
  }
906
875
  }
907
876
  }
908
877
  }
909
878
  `;
910
- function buildBookingsVariables(params) {
911
- const filter = {};
912
- const bookingFilter = {};
913
- if (params.bookingId) {
914
- bookingFilter.ids = [normalizeBookingId(params.bookingId)];
915
- } else if (params.timeslotId) {
916
- bookingFilter.timeslotRefid = params.timeslotId;
917
- } else {
918
- if (params.searchBy === SEARCH_BY_ACTIVITY_DATE) {
919
- bookingFilter.overlapsRange = `[${params.startDateTime},${params.endDateTime}]`;
920
- } else if (params.searchBy === SEARCH_BY_PURCHASE_DATE) {
921
- filter.purchasedAtRangeUtc = `[${params.startDateTime},${params.endDateTime}]`;
922
- }
923
- if (params.productId) {
924
- bookingFilter.activityIds = [params.productId];
925
- }
926
- }
927
- if (params.email) {
928
- filter.primaryGuestEmail = params.email;
879
+ function buildSalesAddonsVariables(searchString) {
880
+ return {
881
+ first: SALES_ADDONS_PAGE_SIZE,
882
+ after: null,
883
+ orderBy: { direction: "DESC", field: "STARTS_AT" },
884
+ filter: { searchString }
885
+ };
886
+ }
887
+
888
+ // src/internal/peek/bookings/addon-converter.ts
889
+ var ERROR_INCONSISTENT_ADDON_ITEM_ID = "Add-on group contains options with mismatched item IDs";
890
+ function parseSaleNode(node) {
891
+ const bookingId = node.id || "";
892
+ const displayId = node.displayId || "";
893
+ const orderId = node.order?.id || "";
894
+ const bookingQuoteRefid = node.refid || "";
895
+ const bookingQuoteReservationStatus = node.reservationStatus || "";
896
+ const items = Array.isArray(node.items) ? node.items : [];
897
+ return items.map((item) => {
898
+ const options = Array.isArray(item.options) ? item.options : [];
899
+ const addonItemOptions = options.map((opt) => ({
900
+ itemId: opt.itemSnapshot?.id || "",
901
+ optionId: opt.itemOptionSnapshot?.id || "",
902
+ itemName: opt.itemSnapshot?.name || "",
903
+ optionName: opt.itemOptionSnapshot?.name || "",
904
+ optionRefid: opt.refid || "",
905
+ optionReservationStatus: opt.reservationStatus || "",
906
+ itemReservationStatus: item.reservationStatus || "",
907
+ itemRefid: item.refid || ""
908
+ }));
909
+ return {
910
+ bookingId,
911
+ displayId,
912
+ orderId,
913
+ total: item.value?.total ?? null,
914
+ bookingQuoteRefid,
915
+ bookingQuoteReservationStatus,
916
+ addonItemOptions
917
+ };
918
+ });
919
+ }
920
+ function toBookingAddon(item) {
921
+ const options = item.addonItemOptions || [];
922
+ if (options.length === 0) {
923
+ return null;
929
924
  }
930
- if (params.searchString && params.searchString.length > 0) {
931
- filter.searchString = params.searchString;
925
+ const addonId = options[0].itemId;
926
+ const addonName = options[0].itemName;
927
+ if (options.some((opt) => opt.itemId !== addonId)) {
928
+ throw new Error(ERROR_INCONSISTENT_ADDON_ITEM_ID);
932
929
  }
933
- if (Object.keys(bookingFilter).length > 0) {
934
- filter.bookingFilter = bookingFilter;
930
+ const grouped = /* @__PURE__ */ new Map();
931
+ options.filter((opt) => opt.optionReservationStatus !== ADDON_OPTION_STATUS_CANCELED).forEach((opt) => {
932
+ const existing = grouped.get(opt.optionId);
933
+ if (existing) {
934
+ existing.quantity += 1;
935
+ } else {
936
+ grouped.set(opt.optionId, {
937
+ addonOptionId: opt.optionId,
938
+ addonOptionName: opt.optionName,
939
+ quantity: 1
940
+ });
941
+ }
942
+ });
943
+ const addonOptions = Array.from(grouped.values());
944
+ if (addonOptions.length === 0) {
945
+ return null;
935
946
  }
936
- return {
937
- first: params.pageSize,
938
- after: params.after,
939
- orderBy: { direction: "ASC", field: "STARTS_AT" },
940
- filter
941
- };
947
+ return { addonId, addonName, total: item.total, addonOptions };
942
948
  }
943
949
 
944
- // src/internal/bookings/booking-service.ts
950
+ // src/internal/peek/bookings/booking-service.ts
945
951
  var DEFAULT_PAGE_SIZE2 = 50;
946
952
  var DEFAULT_CANCEL_NOTE = "Canceled";
947
953
  var DEFAULT_CUSTOMER_MESSAGE = "Charge initiated via API";
@@ -1691,7 +1697,7 @@ function buildCancellation(items, addonOptionId, quantity) {
1691
1697
  return { bookingQuotes, canceledCount };
1692
1698
  }
1693
1699
 
1694
- // src/internal/daily-notes/daily-note-converter.ts
1700
+ // src/internal/peek/daily-notes/daily-note-converter.ts
1695
1701
  function toDailyNote(response) {
1696
1702
  const union = response?.dailyNote;
1697
1703
  if (!union) {
@@ -1712,7 +1718,7 @@ function cleanNote(note) {
1712
1718
  return { note: note.note };
1713
1719
  }
1714
1720
 
1715
- // src/internal/daily-notes/daily-note-queries.ts
1721
+ // src/internal/peek/daily-notes/daily-note-queries.ts
1716
1722
  var GLOBAL_NOTE_TYPE = "DASHBOARD";
1717
1723
  var DAILY_NOTE_TODAY_QUERY = `
1718
1724
  query dailyNote($type: GlobalNoteType!) {
@@ -1743,7 +1749,7 @@ function buildUpsertDailyNoteVariables(note) {
1743
1749
  return { input: { type: GLOBAL_NOTE_TYPE, note } };
1744
1750
  }
1745
1751
 
1746
- // src/internal/daily-notes/daily-note-service.ts
1752
+ // src/internal/peek/daily-notes/daily-note-service.ts
1747
1753
  var DailyNoteService = class {
1748
1754
  constructor(client) {
1749
1755
  this.client = client;
@@ -1766,6 +1772,53 @@ var DailyNoteService = class {
1766
1772
  return fromUpsertResponse(body.data);
1767
1773
  }
1768
1774
  };
1775
+ var TokenManager = class {
1776
+ constructor(options) {
1777
+ this.options = options;
1778
+ }
1779
+ options;
1780
+ cached;
1781
+ /**
1782
+ * Returns a valid bearer token, reusing the cached one until it is within
1783
+ * `leewaySeconds` of expiring, at which point a fresh token is minted.
1784
+ */
1785
+ getToken() {
1786
+ const now = Date.now();
1787
+ if (this.cached && now < this.cached.expiresAtMs) {
1788
+ return this.cached.token;
1789
+ }
1790
+ const { secret, issuer, installId, ttlSeconds, leewaySeconds } = this.options;
1791
+ const token = jwt.sign({}, secret, {
1792
+ expiresIn: ttlSeconds,
1793
+ issuer,
1794
+ subject: installId
1795
+ });
1796
+ this.cached = {
1797
+ token,
1798
+ expiresAtMs: now + (ttlSeconds - leewaySeconds) * 1e3
1799
+ };
1800
+ return token;
1801
+ }
1802
+ };
1803
+
1804
+ // src/access-service-config.ts
1805
+ var DEFAULT_TOKEN_TTL_SECONDS = 3600;
1806
+ var DEFAULT_TOKEN_REFRESH_LEEWAY_SECONDS = 60;
1807
+ var DEFAULT_RETRY_DELAYS_MS = [1e3, 2e3, 4e3];
1808
+ function requireNonEmpty(value, name, serviceName) {
1809
+ if (!value) {
1810
+ throw new Error(`${serviceName}: "${name}" is required`);
1811
+ }
1812
+ }
1813
+ function createTokenManager(config) {
1814
+ return new TokenManager({
1815
+ secret: config.jwtSecret,
1816
+ issuer: config.issuer,
1817
+ installId: config.installId,
1818
+ ttlSeconds: config.tokenTtlSeconds ?? DEFAULT_TOKEN_TTL_SECONDS,
1819
+ leewaySeconds: config.tokenRefreshLeewaySeconds ?? DEFAULT_TOKEN_REFRESH_LEEWAY_SECONDS
1820
+ });
1821
+ }
1769
1822
 
1770
1823
  // src/errors.ts
1771
1824
  var AdminAccountRequiredError = class extends Error {
@@ -1793,11 +1846,49 @@ var PeekGraphQLError = class extends Error {
1793
1846
  this.graphqlErrors = graphqlErrors;
1794
1847
  }
1795
1848
  };
1849
+ var CngApiError = class extends Error {
1850
+ /** The HTTP status that triggered this error. */
1851
+ statusCode;
1852
+ /** The raw response body (parsed JSON when possible, otherwise text). */
1853
+ body;
1854
+ constructor(statusCode, body, message) {
1855
+ super(message ?? `CNG request failed with HTTP ${statusCode}`);
1856
+ this.name = "CngApiError";
1857
+ this.statusCode = statusCode;
1858
+ this.body = body;
1859
+ }
1860
+ };
1796
1861
 
1797
- // src/internal/graphql-client.ts
1862
+ // src/internal/http-transport.ts
1798
1863
  var RATE_LIMIT_STATUS = 429;
1799
1864
  var ADMIN_ACCOUNT_REQUIRED_STATUS = 418;
1800
1865
  var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
1866
+ async function requestWithRetry(http, url, init, label, onResponse) {
1867
+ const { retryDelaysMs, logger, fetchFn } = http;
1868
+ for (let attempt = 0; attempt <= retryDelaysMs.length; attempt++) {
1869
+ const response = await fetchFn(url, init);
1870
+ if (response.status === ADMIN_ACCOUNT_REQUIRED_STATUS) {
1871
+ logger.warn(`Admin account required for ${label} (HTTP 418)`, { url });
1872
+ throw new AdminAccountRequiredError();
1873
+ }
1874
+ if (response.status === RATE_LIMIT_STATUS) {
1875
+ const delay = retryDelaysMs[attempt];
1876
+ if (delay !== void 0) {
1877
+ logger.warn(
1878
+ `Rate limited on ${label}, retrying in ${delay}ms (attempt ${attempt + 1}/${retryDelaysMs.length})`
1879
+ );
1880
+ await sleep(delay);
1881
+ continue;
1882
+ }
1883
+ logger.error(`Rate limit exceeded for ${label}`, { url });
1884
+ throw new RateLimitError();
1885
+ }
1886
+ return onResponse(response);
1887
+ }
1888
+ throw new RateLimitError();
1889
+ }
1890
+
1891
+ // src/internal/peek/graphql-client.ts
1801
1892
  var GraphQLClient = class {
1802
1893
  constructor(options) {
1803
1894
  this.options = options;
@@ -1805,50 +1896,39 @@ var GraphQLClient = class {
1805
1896
  options;
1806
1897
  /**
1807
1898
  * Executes a GraphQL query against the named endpoint and returns the raw
1808
- * response body. Retries on HTTP 429 per the configured backoff.
1899
+ * response body. Retries on HTTP 429 per the configured backoff (via the
1900
+ * shared {@link requestWithRetry} loop).
1809
1901
  */
1810
1902
  async request(endpointName, query, variables) {
1811
- const { retryDelaysMs, logger, fetchFn } = this.options;
1903
+ const { logger } = this.options;
1812
1904
  const url = this.endpoint(endpointName);
1813
1905
  const collapsedQuery = query.replace(/\s+/g, " ").trim();
1814
1906
  logger.info("Making GraphQL request", { url, endpointName });
1815
- for (let attempt = 0; attempt <= retryDelaysMs.length; attempt++) {
1816
- const response = await fetchFn(url, {
1907
+ return requestWithRetry(
1908
+ this.options,
1909
+ url,
1910
+ {
1817
1911
  method: "POST",
1818
1912
  headers: this.buildHeaders(),
1819
1913
  body: JSON.stringify({ query: collapsedQuery, variables })
1820
- });
1821
- if (response.status === ADMIN_ACCOUNT_REQUIRED_STATUS) {
1822
- logger.warn(`Admin account required for ${endpointName} (HTTP 418)`, { url });
1823
- throw new AdminAccountRequiredError();
1824
- }
1825
- if (response.status === RATE_LIMIT_STATUS) {
1826
- const delay = retryDelaysMs[attempt];
1827
- if (delay !== void 0) {
1828
- logger.warn(
1829
- `Rate limited on ${endpointName}, retrying in ${delay}ms (attempt ${attempt + 1}/${retryDelaysMs.length})`
1830
- );
1831
- await sleep(delay);
1832
- continue;
1914
+ },
1915
+ endpointName,
1916
+ async (response) => {
1917
+ const body = await response.json();
1918
+ if (body.errors) {
1919
+ logger.error(`GraphQL errors for ${endpointName}`, {
1920
+ url,
1921
+ graphqlErrors: JSON.stringify(body.errors)
1922
+ });
1923
+ throw new PeekGraphQLError(body.errors);
1833
1924
  }
1834
- logger.error(`Rate limit exceeded for ${endpointName}`, { url });
1835
- throw new RateLimitError();
1836
- }
1837
- const body = await response.json();
1838
- if (body.errors) {
1839
- logger.error(`GraphQL errors for ${endpointName}`, {
1840
- url,
1841
- graphqlErrors: JSON.stringify(body.errors)
1842
- });
1843
- throw new PeekGraphQLError(body.errors);
1844
- }
1845
- if (!response.ok) {
1846
- logger.error(`GraphQL request failed with HTTP ${response.status}`, { url });
1847
- throw new Error(`GraphQL request failed with HTTP ${response.status}`);
1925
+ if (!response.ok) {
1926
+ logger.error(`GraphQL request failed with HTTP ${response.status}`, { url });
1927
+ throw new Error(`GraphQL request failed with HTTP ${response.status}`);
1928
+ }
1929
+ return body;
1848
1930
  }
1849
- return body;
1850
- }
1851
- throw new RateLimitError();
1931
+ );
1852
1932
  }
1853
1933
  endpoint(endpointName) {
1854
1934
  const { baseUrl, appId, endpointPathPrefix } = this.options;
@@ -1867,7 +1947,7 @@ var GraphQLClient = class {
1867
1947
  }
1868
1948
  };
1869
1949
 
1870
- // src/internal/memberships/membership-converter.ts
1950
+ // src/internal/peek/memberships/membership-converter.ts
1871
1951
  function fromMembershipsResponse(response) {
1872
1952
  return (response?.memberships ?? []).flatMap(
1873
1953
  (membership) => (membership.membershipVariants ?? []).map((variant) => ({
@@ -1882,7 +1962,7 @@ function fromMembershipsResponse(response) {
1882
1962
  );
1883
1963
  }
1884
1964
 
1885
- // src/internal/memberships/membership-queries.ts
1965
+ // src/internal/peek/memberships/membership-queries.ts
1886
1966
  var MEMBERSHIPS_QUERY = `
1887
1967
  query Sales {
1888
1968
  memberships {
@@ -1927,7 +2007,7 @@ var CREATE_MEMBERSHIP_ORDER_FROM_QUOTE_MUTATION = `
1927
2007
  }
1928
2008
  `;
1929
2009
 
1930
- // src/internal/memberships/membership-service.ts
2010
+ // src/internal/peek/memberships/membership-service.ts
1931
2011
  var QUOTE_STATUS_CONFIRMED = "CONFIRMED";
1932
2012
  var DEFAULT_BALANCE_AMOUNT = "0.00";
1933
2013
  var DEFAULT_BALANCE_CURRENCY = "USD";
@@ -2033,7 +2113,7 @@ function validatePurchaseInput(input) {
2033
2113
  }
2034
2114
  }
2035
2115
 
2036
- // src/internal/resellers/channel-converter.ts
2116
+ // src/internal/peek/resellers/channel-converter.ts
2037
2117
  function fromChannelsResponse(response) {
2038
2118
  return (response?.channels ?? []).map(fromChannelNode);
2039
2119
  }
@@ -2054,7 +2134,7 @@ function fromChannelNode(node) {
2054
2134
  };
2055
2135
  }
2056
2136
 
2057
- // src/internal/resellers/channel-queries.ts
2137
+ // src/internal/peek/resellers/channel-queries.ts
2058
2138
  var CHANNELS_QUERY = `
2059
2139
  query Sales($first: Int) {
2060
2140
  channels {
@@ -2078,7 +2158,7 @@ var CHANNELS_QUERY = `
2078
2158
  }
2079
2159
  `;
2080
2160
 
2081
- // src/internal/resellers/reseller-service.ts
2161
+ // src/internal/peek/resellers/reseller-service.ts
2082
2162
  var DEFAULT_AGENTS_PER_CHANNEL = 10;
2083
2163
  var ResellerService = class {
2084
2164
  constructor(client) {
@@ -2097,7 +2177,7 @@ var ResellerService = class {
2097
2177
  }
2098
2178
  };
2099
2179
 
2100
- // src/internal/resource-pools/resource-pool-converter.ts
2180
+ // src/internal/peek/resource-pools/resource-pool-converter.ts
2101
2181
  function fromResourcePoolsResponse(response) {
2102
2182
  return (response?.resourcePools ?? []).map(fromResourcePoolNode);
2103
2183
  }
@@ -2125,7 +2205,7 @@ function fromAccountUser(accountUser) {
2125
2205
  };
2126
2206
  }
2127
2207
 
2128
- // src/internal/resource-pools/resource-pool-queries.ts
2208
+ // src/internal/peek/resource-pools/resource-pool-queries.ts
2129
2209
  var RESOURCE_POOLS_QUERY = `
2130
2210
  query Sales($filter: ResourcePoolsFilter) {
2131
2211
  resourcePools(filter: $filter) {
@@ -2146,7 +2226,7 @@ var RESOURCE_POOLS_QUERY = `
2146
2226
  }
2147
2227
  `;
2148
2228
 
2149
- // src/internal/resource-pools/resource-pool-service.ts
2229
+ // src/internal/peek/resource-pools/resource-pool-service.ts
2150
2230
  var DEFAULT_MODE = "ALL";
2151
2231
  var ResourcePoolService = class {
2152
2232
  constructor(client) {
@@ -2166,7 +2246,7 @@ var ResourcePoolService = class {
2166
2246
  }
2167
2247
  };
2168
2248
 
2169
- // src/internal/reviews/review-converter.ts
2249
+ // src/internal/peek/reviews/review-converter.ts
2170
2250
  var ISO_DATE_LENGTH = 10;
2171
2251
  function toDateOnly(isoDateTime) {
2172
2252
  return isoDateTime.slice(0, ISO_DATE_LENGTH);
@@ -2190,13 +2270,13 @@ function fromReviewNode(node) {
2190
2270
  };
2191
2271
  }
2192
2272
 
2193
- // src/internal/reviews/review-cursor.ts
2273
+ // src/internal/peek/reviews/review-cursor.ts
2194
2274
  function encodeCursor(offset, pageSize) {
2195
2275
  const start = Math.max(0, offset - pageSize + 1);
2196
2276
  return Buffer.from(`range:${start}..${offset},${offset}`).toString("base64");
2197
2277
  }
2198
2278
 
2199
- // src/internal/reviews/review-queries.ts
2279
+ // src/internal/peek/reviews/review-queries.ts
2200
2280
  var REVIEWS_QUERY = `
2201
2281
  query Reviews($first: Int, $filter: ReviewFilter, $after: String) {
2202
2282
  reviews(first: $first, filter: $filter, after: $after) {
@@ -2231,7 +2311,7 @@ function buildReviewsVariables(params) {
2231
2311
  };
2232
2312
  }
2233
2313
 
2234
- // src/internal/reviews/review-service.ts
2314
+ // src/internal/peek/reviews/review-service.ts
2235
2315
  var DEFAULT_REVIEW_COUNT = 50;
2236
2316
  var MIN_REVIEW_COUNT = 1;
2237
2317
  var MAX_REVIEW_COUNT = 50;
@@ -2292,7 +2372,7 @@ var ReviewService = class {
2292
2372
  }
2293
2373
  };
2294
2374
 
2295
- // src/internal/timeslots/timeslot-converter.ts
2375
+ // src/internal/peek/timeslots/timeslot-converter.ts
2296
2376
  function fromTimeslotNodes(nodes, productId) {
2297
2377
  if (!Array.isArray(nodes) || nodes.length === 0) {
2298
2378
  return [];
@@ -2350,7 +2430,7 @@ function mapAssignedResources(allocations) {
2350
2430
  });
2351
2431
  }
2352
2432
 
2353
- // src/internal/timeslots/guide-matcher.ts
2433
+ // src/internal/peek/timeslots/guide-matcher.ts
2354
2434
  function matchGuideToResourcePool(guideId, guideResourcePools, accountUsers) {
2355
2435
  const directIdMatch = guideResourcePools.find((pool) => pool.id === guideId);
2356
2436
  if (directIdMatch) {
@@ -2384,7 +2464,7 @@ function matchGuideToResourcePool(guideId, guideResourcePools, accountUsers) {
2384
2464
  return null;
2385
2465
  }
2386
2466
 
2387
- // src/internal/timeslots/resource-allocation-queries.ts
2467
+ // src/internal/peek/timeslots/resource-allocation-queries.ts
2388
2468
  var RESOURCE_ALLOCATION_BULK_REQUEST_MUTATION = `
2389
2469
  mutation ResourceAllocationBulkRequest($input: ResourceAllocationBulkRequestInput!) {
2390
2470
  resourceAllocationBulkRequest(input: $input) {
@@ -2403,7 +2483,7 @@ function buildResourceAllocationVariables(timeslotIds, resourcePoolIds, status)
2403
2483
  return { input: { timeslotIds, resourcePoolIds, status } };
2404
2484
  }
2405
2485
 
2406
- // src/internal/timeslots/timeslot-queries.ts
2486
+ // src/internal/peek/timeslots/timeslot-queries.ts
2407
2487
  var TIMESLOTS_QUERY = `
2408
2488
  query Sales($params: TimeslotsFilter!) {
2409
2489
  timeslots(filter: $params) {
@@ -2506,7 +2586,7 @@ function buildTimeslotVariables(productId, date, filter) {
2506
2586
  return { params };
2507
2587
  }
2508
2588
 
2509
- // src/internal/timeslots/timeslot-service.ts
2589
+ // src/internal/peek/timeslots/timeslot-service.ts
2510
2590
  var GUIDE_CATEGORY = "guide";
2511
2591
  var ERROR_MISSING_TIMESLOTS_OR_GUIDES = "At least one timeslot and one guide are required";
2512
2592
  var ERROR_INVALID_ACTION = "Invalid action. Must be either assign or unassign";
@@ -2623,7 +2703,7 @@ function normalizeDate(date) {
2623
2703
  return date.split("T")[0] ?? date;
2624
2704
  }
2625
2705
 
2626
- // src/internal/products/product-converter.ts
2706
+ // src/internal/peek/products/product-converter.ts
2627
2707
  var ADD_ON_COLOR = "#FFFFFF";
2628
2708
  function fromActivities(activities) {
2629
2709
  return activities.map(fromActivity);
@@ -2662,7 +2742,7 @@ function fromItemOptionNodes(nodes) {
2662
2742
  return Array.from(grouped.values());
2663
2743
  }
2664
2744
 
2665
- // src/internal/products/product-queries.ts
2745
+ // src/internal/peek/products/product-queries.ts
2666
2746
  var PRODUCTS_QUERY = `
2667
2747
  query Sales {
2668
2748
  activities {
@@ -2702,7 +2782,7 @@ var ITEM_OPTIONS_QUERY = `
2702
2782
  }
2703
2783
  `;
2704
2784
 
2705
- // src/internal/products/product-service.ts
2785
+ // src/internal/peek/products/product-service.ts
2706
2786
  var DEFAULT_ITEM_OPTIONS_PAGE_SIZE = 50;
2707
2787
  var ProductService = class {
2708
2788
  constructor(client, options = {}) {
@@ -2779,7 +2859,7 @@ var ProductService = class {
2779
2859
  }
2780
2860
  };
2781
2861
 
2782
- // src/internal/promo-codes/promo-code-queries.ts
2862
+ // src/internal/peek/promo-codes/promo-code-queries.ts
2783
2863
  var PROMO_CODES_QUERY = `
2784
2864
  query Sales($first: Int, $after: String) {
2785
2865
  promoCodes(first: $first, after: $after) {
@@ -2818,7 +2898,7 @@ var CREATE_PROMO_CODE_MUTATION = `
2818
2898
  }
2819
2899
  `;
2820
2900
 
2821
- // src/internal/promo-codes/promo-code-service.ts
2901
+ // src/internal/peek/promo-codes/promo-code-service.ts
2822
2902
  var DEFAULT_PAGE_SIZE3 = 50;
2823
2903
  var DEFAULT_CURRENCY = "USD";
2824
2904
  var CURRENCY_PATTERN = /^[A-Z]{3}$/;
@@ -2897,34 +2977,6 @@ var PromoCodeService = class {
2897
2977
  return { id: result.id, name: result.name };
2898
2978
  }
2899
2979
  };
2900
- var TokenManager = class {
2901
- constructor(options) {
2902
- this.options = options;
2903
- }
2904
- options;
2905
- cached;
2906
- /**
2907
- * Returns a valid bearer token, reusing the cached one until it is within
2908
- * `leewaySeconds` of expiring, at which point a fresh token is minted.
2909
- */
2910
- getToken() {
2911
- const now = Date.now();
2912
- if (this.cached && now < this.cached.expiresAtMs) {
2913
- return this.cached.token;
2914
- }
2915
- const { secret, issuer, installId, ttlSeconds, leewaySeconds } = this.options;
2916
- const token = jwt.sign({}, secret, {
2917
- expiresIn: ttlSeconds,
2918
- issuer,
2919
- subject: installId
2920
- });
2921
- this.cached = {
2922
- token,
2923
- expiresAtMs: now + (ttlSeconds - leewaySeconds) * 1e3
2924
- };
2925
- return token;
2926
- }
2927
- };
2928
2980
 
2929
2981
  // src/logger.ts
2930
2982
  var noopLogger = {
@@ -2939,9 +2991,6 @@ var noopLogger = {
2939
2991
  // src/peek-access-service.ts
2940
2992
  var DEFAULT_BASE_URL = "https://apps.peekapis.com/backoffice-gql";
2941
2993
  var DEFAULT_V2_BASE_URL = "https://app-registry.peeklabs.com/installations-api";
2942
- var DEFAULT_TOKEN_TTL_SECONDS = 3600;
2943
- var DEFAULT_TOKEN_REFRESH_LEEWAY_SECONDS = 60;
2944
- var DEFAULT_RETRY_DELAYS_MS = [1e3, 2e3, 4e3];
2945
2994
  var PEEK_TOKEN_ISSUER = "app_registry_v2";
2946
2995
  var PeekAccessService = class {
2947
2996
  client;
@@ -2960,20 +3009,14 @@ var PeekAccessService = class {
2960
3009
  reviewService;
2961
3010
  constructor(config) {
2962
3011
  const isV2 = config.mode === "v2";
2963
- requireNonEmpty(config.installId, "installId");
2964
- requireNonEmpty(config.jwtSecret, "jwtSecret");
2965
- requireNonEmpty(config.issuer, "issuer");
2966
- requireNonEmpty(config.appId, "appId");
2967
- if (!isV2) requireNonEmpty(config.gatewayKey ?? "", "gatewayKey");
3012
+ requireNonEmpty(config.installId, "installId", "PeekAccessService");
3013
+ requireNonEmpty(config.jwtSecret, "jwtSecret", "PeekAccessService");
3014
+ requireNonEmpty(config.issuer, "issuer", "PeekAccessService");
3015
+ requireNonEmpty(config.appId, "appId", "PeekAccessService");
3016
+ if (!isV2) requireNonEmpty(config.gatewayKey ?? "", "gatewayKey", "PeekAccessService");
2968
3017
  this.jwtSecret = config.jwtSecret;
2969
3018
  const logger = config.logger ?? noopLogger;
2970
- const tokens = new TokenManager({
2971
- secret: config.jwtSecret,
2972
- issuer: config.issuer,
2973
- installId: config.installId,
2974
- ttlSeconds: config.tokenTtlSeconds ?? DEFAULT_TOKEN_TTL_SECONDS,
2975
- leewaySeconds: config.tokenRefreshLeewaySeconds ?? DEFAULT_TOKEN_REFRESH_LEEWAY_SECONDS
2976
- });
3019
+ const tokens = createTokenManager(config);
2977
3020
  const defaultBaseUrl = isV2 ? DEFAULT_V2_BASE_URL : DEFAULT_BASE_URL;
2978
3021
  this.client = new GraphQLClient({
2979
3022
  baseUrl: config.baseUrl ?? defaultBaseUrl,
@@ -3299,13 +3342,148 @@ var PeekAccessService = class {
3299
3342
  return this.getReviewService().getReviews(productId, reviewCount, reviewOffset);
3300
3343
  }
3301
3344
  };
3302
- function requireNonEmpty(value, name) {
3303
- if (!value) {
3304
- throw new Error(`PeekAccessService: "${name}" is required`);
3305
- }
3345
+
3346
+ // src/internal/cng/endpoints.ts
3347
+ var CNG_EXTENDABLE_SLUG = "cng_backoffice_api-v1";
3348
+ var PRODUCTS_PATH = "api/v2/commerce-config/products";
3349
+
3350
+ // src/models/cng/product.ts
3351
+ var ACTIVITY_PRODUCT_TYPE2 = "ACTIVITY";
3352
+
3353
+ // src/internal/cng/products/product-converter.ts
3354
+ function fromProductNodes(nodes) {
3355
+ return nodes.map(fromProductNode);
3356
+ }
3357
+ function fromProductNode(node) {
3358
+ return {
3359
+ productId: node.id || "",
3360
+ name: node.name || "",
3361
+ type: node.product_type || ACTIVITY_PRODUCT_TYPE2,
3362
+ color: node.color_hex || "",
3363
+ tickets: (node.tickets ?? []).map((ticket) => ({
3364
+ id: ticket.id,
3365
+ name: ticket.name
3366
+ }))
3367
+ };
3306
3368
  }
3307
3369
 
3308
- // src/internal/bookings/booking-webhook.ts
3370
+ // src/internal/cng/products/product-service.ts
3371
+ var CngProductService = class {
3372
+ constructor(client) {
3373
+ this.client = client;
3374
+ }
3375
+ client;
3376
+ /**
3377
+ * Returns every activity as a single flat list.
3378
+ *
3379
+ * @example
3380
+ * ```ts
3381
+ * const activities = await cng.getProductService().getAllActivities();
3382
+ * ```
3383
+ */
3384
+ async getAllActivities() {
3385
+ const body = await this.client.get(
3386
+ PRODUCTS_PATH
3387
+ );
3388
+ const nodes = Array.isArray(body) ? body : body?.products ?? [];
3389
+ return fromProductNodes(nodes ?? []);
3390
+ }
3391
+ };
3392
+
3393
+ // src/internal/cng/rest-client.ts
3394
+ var RestClient = class {
3395
+ constructor(options) {
3396
+ this.options = options;
3397
+ }
3398
+ options;
3399
+ /**
3400
+ * Issues a GET against the named REST path and returns the parsed JSON body.
3401
+ * Retries on HTTP 429 per the configured backoff (via the shared
3402
+ * {@link requestWithRetry} loop).
3403
+ *
3404
+ * @throws {AdminAccountRequiredError} on HTTP 418
3405
+ * @throws {RateLimitError} on HTTP 429 after retries are exhausted
3406
+ * @throws {CngApiError} on any other non-2xx response
3407
+ */
3408
+ async get(path) {
3409
+ const { logger } = this.options;
3410
+ const url = this.endpoint(path);
3411
+ logger.info("Making CNG request", { url, path });
3412
+ return requestWithRetry(
3413
+ this.options,
3414
+ url,
3415
+ { method: "GET", headers: this.buildHeaders() },
3416
+ path,
3417
+ async (response) => {
3418
+ const body = await this.parseBody(response);
3419
+ if (!response.ok) {
3420
+ logger.error(`CNG request failed with HTTP ${response.status}`, { url });
3421
+ throw new CngApiError(response.status, body);
3422
+ }
3423
+ return body;
3424
+ }
3425
+ );
3426
+ }
3427
+ /** Reads the response body as JSON, falling back to raw text when unparseable. */
3428
+ async parseBody(response) {
3429
+ const text = await response.text();
3430
+ try {
3431
+ return JSON.parse(text);
3432
+ } catch {
3433
+ return text;
3434
+ }
3435
+ }
3436
+ endpoint(path) {
3437
+ const { baseUrl, appId, extendableSlug } = this.options;
3438
+ return `${baseUrl}/${appId}/${extendableSlug}/${path}`;
3439
+ }
3440
+ buildHeaders() {
3441
+ return {
3442
+ "X-Peek-Auth": `Bearer ${this.options.getToken()}`,
3443
+ "Content-Type": "application/json"
3444
+ };
3445
+ }
3446
+ };
3447
+
3448
+ // src/cng-access-service.ts
3449
+ var DEFAULT_BASE_URL2 = "https://app-registry.peeklabs.com/installations-api";
3450
+ var CngAccessService = class {
3451
+ client;
3452
+ productService;
3453
+ constructor(config) {
3454
+ requireNonEmpty(config.installId, "installId", "CngAccessService");
3455
+ requireNonEmpty(config.jwtSecret, "jwtSecret", "CngAccessService");
3456
+ requireNonEmpty(config.issuer, "issuer", "CngAccessService");
3457
+ requireNonEmpty(config.appId, "appId", "CngAccessService");
3458
+ const logger = config.logger ?? noopLogger;
3459
+ const tokens = createTokenManager(config);
3460
+ this.client = new RestClient({
3461
+ baseUrl: config.baseUrl ?? DEFAULT_BASE_URL2,
3462
+ appId: config.appId,
3463
+ extendableSlug: CNG_EXTENDABLE_SLUG,
3464
+ getToken: () => tokens.getToken(),
3465
+ retryDelaysMs: config.retryDelaysMs ?? DEFAULT_RETRY_DELAYS_MS,
3466
+ logger,
3467
+ fetchFn: config.fetch ?? globalThis.fetch
3468
+ });
3469
+ }
3470
+ /**
3471
+ * Returns the {@link CngProductService} for this install, bound to the shared
3472
+ * authenticated transport. The instance is created lazily and reused.
3473
+ */
3474
+ getProductService() {
3475
+ if (!this.productService) {
3476
+ this.productService = new CngProductService(this.client);
3477
+ }
3478
+ return this.productService;
3479
+ }
3480
+ /** All activities. Delegates to {@link CngProductService.getAllActivities}. */
3481
+ getAllActivities() {
3482
+ return this.getProductService().getAllActivities();
3483
+ }
3484
+ };
3485
+
3486
+ // src/internal/peek/bookings/booking-webhook.ts
3309
3487
  var PAYLOAD_BOOKING_KEY = "booking";
3310
3488
  var VALUE_OPEN_TOKEN = "value {";
3311
3489
  var PRICE_BREAKDOWN_KEYS = [
@@ -3355,7 +3533,7 @@ function hasPriceBreakdown(node) {
3355
3533
  return PRICE_BREAKDOWN_KEYS.some((key) => key in value);
3356
3534
  }
3357
3535
 
3358
- // src/internal/waivers/waiver-webhook.ts
3536
+ // src/internal/peek/waivers/waiver-webhook.ts
3359
3537
  var PAYLOAD_WAIVER_KEY = "waiver";
3360
3538
  function parseWaiverWebhook(payload) {
3361
3539
  return fromWaiverNode(extractWaiverNode(payload));
@@ -3390,6 +3568,6 @@ function extractWaiverNode(payload) {
3390
3568
  return record;
3391
3569
  }
3392
3570
 
3393
- export { ACTIVITY_PRODUCT_TYPE, ADD_ON_PRODUCT_TYPE, AccountUserService, AdminAccountRequiredError, AvailabilityService, BookingService, DailyNoteService, MembershipService, PeekAccessService, PeekGraphQLError, ProductService, PromoCodeService, RENTAL_PRODUCT_TYPE, RateLimitError, ResellerService, ResourcePoolService, ReviewService, TimeslotService, noopLogger, parseBookingWebhook, parseWaiverWebhook };
3571
+ export { ACTIVITY_PRODUCT_TYPE, ADD_ON_PRODUCT_TYPE, AccountUserService, AdminAccountRequiredError, AvailabilityService, BookingService, CngAccessService, CngApiError, CngProductService, DailyNoteService, MembershipService, PeekAccessService, PeekGraphQLError, ProductService, PromoCodeService, RENTAL_PRODUCT_TYPE, RateLimitError, ResellerService, ResourcePoolService, ReviewService, TimeslotService, noopLogger, parseBookingWebhook, parseWaiverWebhook };
3394
3572
  //# sourceMappingURL=index.js.map
3395
3573
  //# sourceMappingURL=index.js.map