@peektravel/app-utilities 0.7.3 → 0.8.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.d.cts CHANGED
@@ -421,6 +421,64 @@ declare const RENTAL_PRODUCT_TYPE = "RENTAL";
421
421
  */
422
422
  declare const ADD_ON_PRODUCT_TYPE = "ADD-ON";
423
423
 
424
+ /**
425
+ * The clean, transport-agnostic data model for an activity's custom questions.
426
+ *
427
+ * Custom questions are the operator-configured questions attached to a bookable
428
+ * activity (per-guest waiver-style prompts, free-text inputs, multiple-choice
429
+ * pickers, etc.). This is the shape consumers work with — it is decoupled from
430
+ * the underlying Peek GraphQL `Question`/`ChoiceQuestion` schema, which stays
431
+ * internal.
432
+ *
433
+ * Note: this describes the **question definitions** on an activity, not any
434
+ * customer's answers — so there is no PII here and nothing gated behind
435
+ * `fullCustomerAccess`.
436
+ */
437
+ /**
438
+ * A single custom question configured on an activity.
439
+ *
440
+ * The ordering and requiredness come from the activity's question
441
+ * configuration; the remaining fields describe the reusable question itself.
442
+ */
443
+ interface CustomQuestion {
444
+ /** Unique identifier of the question (e.g. `"cq_anq67"`). */
445
+ id: string;
446
+ /** 1-based position of the question within the activity's question list. */
447
+ order: number;
448
+ /** Whether the activity requires an answer to this question. */
449
+ isRequired: boolean;
450
+ /** The question prompt shown to the guest (Peek's `text`). */
451
+ questionText: string;
452
+ /** Helper/hint text shown alongside the prompt, or `null` when none. */
453
+ hintText: string | null;
454
+ /**
455
+ * The question's input type as reported by Peek — e.g. `"CHECK_BOX"`,
456
+ * `"TEXT"`, `"LOCATION"`, `"SELECT_ONE"`. Left as a string so new Peek
457
+ * question types flow through without a package change.
458
+ */
459
+ questionType: string;
460
+ /** Operator-only label for the question, or `null` when none. */
461
+ internalLabel: string | null;
462
+ /** `true` when the question is answered once per guest rather than per booking. */
463
+ perGuest: boolean;
464
+ /** Pre-filled default answer, or `null` when none. */
465
+ defaultValue: string | null;
466
+ /**
467
+ * Selectable options for choice-style questions (`SELECT_ONE`, `LOCATION`,
468
+ * …). Empty for free-text/checkbox questions that carry no options.
469
+ */
470
+ options: CustomQuestionOption[];
471
+ }
472
+ /** A single selectable option on a choice-style {@link CustomQuestion}. */
473
+ interface CustomQuestionOption {
474
+ /** Unique identifier of the option (e.g. `"cqao_mwdgvw"`). */
475
+ id: string;
476
+ /** 1-based position of the option within the question's option list. */
477
+ order: number;
478
+ /** The option's display label (Peek's `text`). */
479
+ value: string;
480
+ }
481
+
424
482
  /** Tuning options for a {@link ProductService}. */
425
483
  interface ProductServiceOptions {
426
484
  /** Page size for cursor-paginated item options. Default: 50. */
@@ -451,6 +509,21 @@ declare class ProductService {
451
509
  getAllRentals(): Promise<Product[]>;
452
510
  /** Returns only add-on products. */
453
511
  getAllAddons(): Promise<Product[]>;
512
+ /**
513
+ * Returns every custom question configured on a single activity, in the order
514
+ * Peek reports them. Choice-style questions (`SELECT_ONE`, `LOCATION`, …)
515
+ * carry their selectable {@link CustomQuestion.options}; free-text/checkbox
516
+ * questions return an empty option list.
517
+ *
518
+ * These are question **definitions**, not customer answers — no PII is
519
+ * involved, so the result is unaffected by `fullCustomerAccess`.
520
+ *
521
+ * @param productId The activity's id.
522
+ * @returns The activity's custom questions, or an empty list when the activity
523
+ * has none or is not found.
524
+ * @throws Error when `productId` is missing or blank.
525
+ */
526
+ getCustomQuestions(productId: string): Promise<CustomQuestion[]>;
454
527
  private fetchActivities;
455
528
  private fetchAllItemOptionNodes;
456
529
  }
@@ -773,6 +846,34 @@ interface CreateBookingGuest {
773
846
  optinMarketing?: boolean;
774
847
  optinSms?: boolean;
775
848
  }
849
+ /**
850
+ * An answer to one of an activity's custom questions, supplied when creating a
851
+ * booking. The question is identified by id **or** text, and the value is
852
+ * resolved against the question's type at create time (see
853
+ * {@link CreateBookingInput.customQuestionAnswers}).
854
+ */
855
+ interface CustomQuestionAnswerInput {
856
+ /**
857
+ * The question to answer — either its id (`cq_…`) or its question text. Text
858
+ * is matched leniently (lowercased, non-alphanumerics stripped); it must match
859
+ * exactly one question or creation fails.
860
+ */
861
+ questionIdOrText: string;
862
+ /**
863
+ * The answer. How it is interpreted depends on the question's type:
864
+ * - **checkbox** — `yes`/`no`/`true`/`false` (case-insensitive).
865
+ * - **text** — used verbatim.
866
+ * - **select-one / location** — an option id (`cqao_…`) or the option's label,
867
+ * matched leniently the same way as `questionIdOrText`.
868
+ *
869
+ * For **location** questions you only select the option — Peek holds each
870
+ * option's configured lat/long and applies it, so no coordinates are (or can
871
+ * be) supplied here.
872
+ *
873
+ * Per-guest questions are **not yet supported**: answering one fails creation.
874
+ */
875
+ value: string;
876
+ }
776
877
  /**
777
878
  * Input for creating a booking. IDs must already be resolved — the package does
778
879
  * not do free-text product/ticket/time matching (that stays in the caller).
@@ -788,6 +889,13 @@ interface CreateBookingInput {
788
889
  guest: CreateBookingGuest;
789
890
  /** Operator notes to attach. */
790
891
  operatorNotes?: string;
892
+ /**
893
+ * Answers to the activity's custom questions. When non-empty, the activity's
894
+ * custom questions are fetched and each answer is validated/resolved before
895
+ * the booking is created — an unmatched question, ambiguous text/option, or
896
+ * invalid checkbox value fails creation before any quote is made.
897
+ */
898
+ customQuestionAnswers?: CustomQuestionAnswerInput[];
791
899
  /** Suppress the customer confirmation email. Default: false. */
792
900
  skipCustomerEmail?: boolean;
793
901
  /** Clone the quote from an existing order. */
@@ -1164,6 +1272,13 @@ declare class BookingService {
1164
1272
  * mutations fail.
1165
1273
  */
1166
1274
  create(input: CreateBookingInput): Promise<CreatedBooking>;
1275
+ /**
1276
+ * Resolves the input's custom-question answers into the quote's
1277
+ * `questionAnswers` payload (each tagged with a fresh `refid`), or `null` when
1278
+ * none were supplied. Fetches the activity's custom questions to validate
1279
+ * against; throws on the first unmatched/ambiguous/invalid answer.
1280
+ */
1281
+ private buildQuestionAnswers;
1167
1282
  private markCreatedBookingPaid;
1168
1283
  /** Finds the parent item id of an add-on by matching its option id. */
1169
1284
  private resolveParentItemId;
@@ -2062,6 +2177,8 @@ declare class PeekAccessService {
2062
2177
  getAllRentals(): Promise<Product[]>;
2063
2178
  /** All add-on products. Delegates to {@link ProductService.getAllAddons}. */
2064
2179
  getAllAddons(): Promise<Product[]>;
2180
+ /** An activity's custom questions. Delegates to {@link ProductService.getCustomQuestions}. */
2181
+ getCustomQuestions(productId: string): Promise<CustomQuestion[]>;
2065
2182
  /** All active account users. Delegates to {@link AccountUserService.getAll}. */
2066
2183
  getAllAccountUsers(): Promise<AccountUser[]>;
2067
2184
  /** Account user by id, or null. Delegates to {@link AccountUserService.getById}. */
@@ -2836,4 +2953,4 @@ declare class AcmeApiError extends Error {
2836
2953
  constructor(statusCode: number, body: unknown, message?: string);
2837
2954
  }
2838
2955
 
2839
- export { ACTIVITY_PRODUCT_TYPE, ADD_ON_PRODUCT_TYPE, type AccessOptions, type AccountUser, AccountUserService, type AccountUserServiceOptions, AcmeAccessService, type AcmeAccessServiceConfig, type AcmeActivity, type AcmeActivityTicket, AcmeApiError, AcmeProductService, type Activity, type ActivityOverrides, type ActivityTicket, type AddAddonInput, AdminAccountRequiredError, type Agent, type AssignGuideResult, type AssignedActivity, type AssignedResource, type Availability, AvailabilityService, type AvailabilityTime, type AvailabilityTimesQuery, type Booking, type BookingAddon, type BookingAddonMoney, type BookingAddonOption, type BookingAddons, type BookingAddonsMutationResult, type BookingPaymentsOnFile, type BookingReadOptions, type BookingSearchBy, BookingService, type BookingServiceOptions, type BookingTimeRangeSearch, type CancelBookingResult, type Channel, type ClearOverridesInput, CngAccessService, type CngAccessServiceConfig, CngApiError, CngProductService, type CreateBookingGuest, type CreateBookingInput, type CreateBookingTicket, type CreateEngineInput, type CreatePromoCodeInput, type CreatedBooking, type CreatedPricingEngine, type CreatedPromoCode, type CustomQuestionAnswer, type DailyNote, DailyNoteService, type Duration, type Guest, type GuestMetadata, type Guide, type GuideAssignment, INSTALL_STATUSES, type InstallAccessConfig, type InstallAccessService, type InstallAccessTarget, type InstallStatus, type InstallWebhook, type InstallWebhookAccount, type InstallWebhookClaims, type InvoiceLinkResult, type Logger, type MakePaymentInput, type MakePaymentResult, type Membership, type MembershipPurchaseInput, MembershipService, type NoteMode, type Payment, type PaymentSource, PeekAccessService, type PeekAccessServiceConfig, type PeekAuthTokenClaims, type PeekAuthTokenUser, PeekGraphQLError, PeekHttpError, type PeekPlatform, PiiAccessDisabledError, type Price, type PricingActivityContext, type PricingEngine, type PricingMoney, type PricingNamedRef, type PricingOverride, type PricingOverrideFilter, PricingService, type Product, type ProductMeetingLocation, ProductService, type ProductServiceOptions, type ProductTicket, type PromoCode, type PromoCodeFixedAmount, PromoCodeService, type PromoCodeServiceOptions, type PurchasedMembership, RENTAL_PRODUCT_TYPE, RateLimitError, type RefundInput, type RefundResult, ResellerService, type ResolvedOverride, type ResolvedPricingOverride, type ResolvedResourceOption, type Resource, type ResourceOptionOverride, type ResourceOptionQuantity, type ResourcePool, type ResourcePoolAccountUser, type ResourcePoolAssignment, type ResourcePoolMode, ResourcePoolService, type Review, ReviewService, type Ticket, type Timeslot, type TimeslotFilter, TimeslotService, type UpdateEngineInput, type UpdateTimeslotResult, type UpsertOverridesInput, type UpsertOverridesResult, type Waiver, createAccessServiceForInstall, noopLogger, parseBookingWebhook, parseInstallWebhook, parseWaiverWebhook, verifyInstallWebhook };
2956
+ export { ACTIVITY_PRODUCT_TYPE, ADD_ON_PRODUCT_TYPE, type AccessOptions, type AccountUser, AccountUserService, type AccountUserServiceOptions, AcmeAccessService, type AcmeAccessServiceConfig, type AcmeActivity, type AcmeActivityTicket, AcmeApiError, AcmeProductService, type Activity, type ActivityOverrides, type ActivityTicket, type AddAddonInput, AdminAccountRequiredError, type Agent, type AssignGuideResult, type AssignedActivity, type AssignedResource, type Availability, AvailabilityService, type AvailabilityTime, type AvailabilityTimesQuery, type Booking, type BookingAddon, type BookingAddonMoney, type BookingAddonOption, type BookingAddons, type BookingAddonsMutationResult, type BookingPaymentsOnFile, type BookingReadOptions, type BookingSearchBy, BookingService, type BookingServiceOptions, type BookingTimeRangeSearch, type CancelBookingResult, type Channel, type ClearOverridesInput, CngAccessService, type CngAccessServiceConfig, CngApiError, CngProductService, type CreateBookingGuest, type CreateBookingInput, type CreateBookingTicket, type CreateEngineInput, type CreatePromoCodeInput, type CreatedBooking, type CreatedPricingEngine, type CreatedPromoCode, type CustomQuestion, type CustomQuestionAnswer, type CustomQuestionAnswerInput, type CustomQuestionOption, type DailyNote, DailyNoteService, type Duration, type Guest, type GuestMetadata, type Guide, type GuideAssignment, INSTALL_STATUSES, type InstallAccessConfig, type InstallAccessService, type InstallAccessTarget, type InstallStatus, type InstallWebhook, type InstallWebhookAccount, type InstallWebhookClaims, type InvoiceLinkResult, type Logger, type MakePaymentInput, type MakePaymentResult, type Membership, type MembershipPurchaseInput, MembershipService, type NoteMode, type Payment, type PaymentSource, PeekAccessService, type PeekAccessServiceConfig, type PeekAuthTokenClaims, type PeekAuthTokenUser, PeekGraphQLError, PeekHttpError, type PeekPlatform, PiiAccessDisabledError, type Price, type PricingActivityContext, type PricingEngine, type PricingMoney, type PricingNamedRef, type PricingOverride, type PricingOverrideFilter, PricingService, type Product, type ProductMeetingLocation, ProductService, type ProductServiceOptions, type ProductTicket, type PromoCode, type PromoCodeFixedAmount, PromoCodeService, type PromoCodeServiceOptions, type PurchasedMembership, RENTAL_PRODUCT_TYPE, RateLimitError, type RefundInput, type RefundResult, ResellerService, type ResolvedOverride, type ResolvedPricingOverride, type ResolvedResourceOption, type Resource, type ResourceOptionOverride, type ResourceOptionQuantity, type ResourcePool, type ResourcePoolAccountUser, type ResourcePoolAssignment, type ResourcePoolMode, ResourcePoolService, type Review, ReviewService, type Ticket, type Timeslot, type TimeslotFilter, TimeslotService, type UpdateEngineInput, type UpdateTimeslotResult, type UpsertOverridesInput, type UpsertOverridesResult, type Waiver, createAccessServiceForInstall, noopLogger, parseBookingWebhook, parseInstallWebhook, parseWaiverWebhook, verifyInstallWebhook };
package/dist/index.d.ts CHANGED
@@ -421,6 +421,64 @@ declare const RENTAL_PRODUCT_TYPE = "RENTAL";
421
421
  */
422
422
  declare const ADD_ON_PRODUCT_TYPE = "ADD-ON";
423
423
 
424
+ /**
425
+ * The clean, transport-agnostic data model for an activity's custom questions.
426
+ *
427
+ * Custom questions are the operator-configured questions attached to a bookable
428
+ * activity (per-guest waiver-style prompts, free-text inputs, multiple-choice
429
+ * pickers, etc.). This is the shape consumers work with — it is decoupled from
430
+ * the underlying Peek GraphQL `Question`/`ChoiceQuestion` schema, which stays
431
+ * internal.
432
+ *
433
+ * Note: this describes the **question definitions** on an activity, not any
434
+ * customer's answers — so there is no PII here and nothing gated behind
435
+ * `fullCustomerAccess`.
436
+ */
437
+ /**
438
+ * A single custom question configured on an activity.
439
+ *
440
+ * The ordering and requiredness come from the activity's question
441
+ * configuration; the remaining fields describe the reusable question itself.
442
+ */
443
+ interface CustomQuestion {
444
+ /** Unique identifier of the question (e.g. `"cq_anq67"`). */
445
+ id: string;
446
+ /** 1-based position of the question within the activity's question list. */
447
+ order: number;
448
+ /** Whether the activity requires an answer to this question. */
449
+ isRequired: boolean;
450
+ /** The question prompt shown to the guest (Peek's `text`). */
451
+ questionText: string;
452
+ /** Helper/hint text shown alongside the prompt, or `null` when none. */
453
+ hintText: string | null;
454
+ /**
455
+ * The question's input type as reported by Peek — e.g. `"CHECK_BOX"`,
456
+ * `"TEXT"`, `"LOCATION"`, `"SELECT_ONE"`. Left as a string so new Peek
457
+ * question types flow through without a package change.
458
+ */
459
+ questionType: string;
460
+ /** Operator-only label for the question, or `null` when none. */
461
+ internalLabel: string | null;
462
+ /** `true` when the question is answered once per guest rather than per booking. */
463
+ perGuest: boolean;
464
+ /** Pre-filled default answer, or `null` when none. */
465
+ defaultValue: string | null;
466
+ /**
467
+ * Selectable options for choice-style questions (`SELECT_ONE`, `LOCATION`,
468
+ * …). Empty for free-text/checkbox questions that carry no options.
469
+ */
470
+ options: CustomQuestionOption[];
471
+ }
472
+ /** A single selectable option on a choice-style {@link CustomQuestion}. */
473
+ interface CustomQuestionOption {
474
+ /** Unique identifier of the option (e.g. `"cqao_mwdgvw"`). */
475
+ id: string;
476
+ /** 1-based position of the option within the question's option list. */
477
+ order: number;
478
+ /** The option's display label (Peek's `text`). */
479
+ value: string;
480
+ }
481
+
424
482
  /** Tuning options for a {@link ProductService}. */
425
483
  interface ProductServiceOptions {
426
484
  /** Page size for cursor-paginated item options. Default: 50. */
@@ -451,6 +509,21 @@ declare class ProductService {
451
509
  getAllRentals(): Promise<Product[]>;
452
510
  /** Returns only add-on products. */
453
511
  getAllAddons(): Promise<Product[]>;
512
+ /**
513
+ * Returns every custom question configured on a single activity, in the order
514
+ * Peek reports them. Choice-style questions (`SELECT_ONE`, `LOCATION`, …)
515
+ * carry their selectable {@link CustomQuestion.options}; free-text/checkbox
516
+ * questions return an empty option list.
517
+ *
518
+ * These are question **definitions**, not customer answers — no PII is
519
+ * involved, so the result is unaffected by `fullCustomerAccess`.
520
+ *
521
+ * @param productId The activity's id.
522
+ * @returns The activity's custom questions, or an empty list when the activity
523
+ * has none or is not found.
524
+ * @throws Error when `productId` is missing or blank.
525
+ */
526
+ getCustomQuestions(productId: string): Promise<CustomQuestion[]>;
454
527
  private fetchActivities;
455
528
  private fetchAllItemOptionNodes;
456
529
  }
@@ -773,6 +846,34 @@ interface CreateBookingGuest {
773
846
  optinMarketing?: boolean;
774
847
  optinSms?: boolean;
775
848
  }
849
+ /**
850
+ * An answer to one of an activity's custom questions, supplied when creating a
851
+ * booking. The question is identified by id **or** text, and the value is
852
+ * resolved against the question's type at create time (see
853
+ * {@link CreateBookingInput.customQuestionAnswers}).
854
+ */
855
+ interface CustomQuestionAnswerInput {
856
+ /**
857
+ * The question to answer — either its id (`cq_…`) or its question text. Text
858
+ * is matched leniently (lowercased, non-alphanumerics stripped); it must match
859
+ * exactly one question or creation fails.
860
+ */
861
+ questionIdOrText: string;
862
+ /**
863
+ * The answer. How it is interpreted depends on the question's type:
864
+ * - **checkbox** — `yes`/`no`/`true`/`false` (case-insensitive).
865
+ * - **text** — used verbatim.
866
+ * - **select-one / location** — an option id (`cqao_…`) or the option's label,
867
+ * matched leniently the same way as `questionIdOrText`.
868
+ *
869
+ * For **location** questions you only select the option — Peek holds each
870
+ * option's configured lat/long and applies it, so no coordinates are (or can
871
+ * be) supplied here.
872
+ *
873
+ * Per-guest questions are **not yet supported**: answering one fails creation.
874
+ */
875
+ value: string;
876
+ }
776
877
  /**
777
878
  * Input for creating a booking. IDs must already be resolved — the package does
778
879
  * not do free-text product/ticket/time matching (that stays in the caller).
@@ -788,6 +889,13 @@ interface CreateBookingInput {
788
889
  guest: CreateBookingGuest;
789
890
  /** Operator notes to attach. */
790
891
  operatorNotes?: string;
892
+ /**
893
+ * Answers to the activity's custom questions. When non-empty, the activity's
894
+ * custom questions are fetched and each answer is validated/resolved before
895
+ * the booking is created — an unmatched question, ambiguous text/option, or
896
+ * invalid checkbox value fails creation before any quote is made.
897
+ */
898
+ customQuestionAnswers?: CustomQuestionAnswerInput[];
791
899
  /** Suppress the customer confirmation email. Default: false. */
792
900
  skipCustomerEmail?: boolean;
793
901
  /** Clone the quote from an existing order. */
@@ -1164,6 +1272,13 @@ declare class BookingService {
1164
1272
  * mutations fail.
1165
1273
  */
1166
1274
  create(input: CreateBookingInput): Promise<CreatedBooking>;
1275
+ /**
1276
+ * Resolves the input's custom-question answers into the quote's
1277
+ * `questionAnswers` payload (each tagged with a fresh `refid`), or `null` when
1278
+ * none were supplied. Fetches the activity's custom questions to validate
1279
+ * against; throws on the first unmatched/ambiguous/invalid answer.
1280
+ */
1281
+ private buildQuestionAnswers;
1167
1282
  private markCreatedBookingPaid;
1168
1283
  /** Finds the parent item id of an add-on by matching its option id. */
1169
1284
  private resolveParentItemId;
@@ -2062,6 +2177,8 @@ declare class PeekAccessService {
2062
2177
  getAllRentals(): Promise<Product[]>;
2063
2178
  /** All add-on products. Delegates to {@link ProductService.getAllAddons}. */
2064
2179
  getAllAddons(): Promise<Product[]>;
2180
+ /** An activity's custom questions. Delegates to {@link ProductService.getCustomQuestions}. */
2181
+ getCustomQuestions(productId: string): Promise<CustomQuestion[]>;
2065
2182
  /** All active account users. Delegates to {@link AccountUserService.getAll}. */
2066
2183
  getAllAccountUsers(): Promise<AccountUser[]>;
2067
2184
  /** Account user by id, or null. Delegates to {@link AccountUserService.getById}. */
@@ -2836,4 +2953,4 @@ declare class AcmeApiError extends Error {
2836
2953
  constructor(statusCode: number, body: unknown, message?: string);
2837
2954
  }
2838
2955
 
2839
- export { ACTIVITY_PRODUCT_TYPE, ADD_ON_PRODUCT_TYPE, type AccessOptions, type AccountUser, AccountUserService, type AccountUserServiceOptions, AcmeAccessService, type AcmeAccessServiceConfig, type AcmeActivity, type AcmeActivityTicket, AcmeApiError, AcmeProductService, type Activity, type ActivityOverrides, type ActivityTicket, type AddAddonInput, AdminAccountRequiredError, type Agent, type AssignGuideResult, type AssignedActivity, type AssignedResource, type Availability, AvailabilityService, type AvailabilityTime, type AvailabilityTimesQuery, type Booking, type BookingAddon, type BookingAddonMoney, type BookingAddonOption, type BookingAddons, type BookingAddonsMutationResult, type BookingPaymentsOnFile, type BookingReadOptions, type BookingSearchBy, BookingService, type BookingServiceOptions, type BookingTimeRangeSearch, type CancelBookingResult, type Channel, type ClearOverridesInput, CngAccessService, type CngAccessServiceConfig, CngApiError, CngProductService, type CreateBookingGuest, type CreateBookingInput, type CreateBookingTicket, type CreateEngineInput, type CreatePromoCodeInput, type CreatedBooking, type CreatedPricingEngine, type CreatedPromoCode, type CustomQuestionAnswer, type DailyNote, DailyNoteService, type Duration, type Guest, type GuestMetadata, type Guide, type GuideAssignment, INSTALL_STATUSES, type InstallAccessConfig, type InstallAccessService, type InstallAccessTarget, type InstallStatus, type InstallWebhook, type InstallWebhookAccount, type InstallWebhookClaims, type InvoiceLinkResult, type Logger, type MakePaymentInput, type MakePaymentResult, type Membership, type MembershipPurchaseInput, MembershipService, type NoteMode, type Payment, type PaymentSource, PeekAccessService, type PeekAccessServiceConfig, type PeekAuthTokenClaims, type PeekAuthTokenUser, PeekGraphQLError, PeekHttpError, type PeekPlatform, PiiAccessDisabledError, type Price, type PricingActivityContext, type PricingEngine, type PricingMoney, type PricingNamedRef, type PricingOverride, type PricingOverrideFilter, PricingService, type Product, type ProductMeetingLocation, ProductService, type ProductServiceOptions, type ProductTicket, type PromoCode, type PromoCodeFixedAmount, PromoCodeService, type PromoCodeServiceOptions, type PurchasedMembership, RENTAL_PRODUCT_TYPE, RateLimitError, type RefundInput, type RefundResult, ResellerService, type ResolvedOverride, type ResolvedPricingOverride, type ResolvedResourceOption, type Resource, type ResourceOptionOverride, type ResourceOptionQuantity, type ResourcePool, type ResourcePoolAccountUser, type ResourcePoolAssignment, type ResourcePoolMode, ResourcePoolService, type Review, ReviewService, type Ticket, type Timeslot, type TimeslotFilter, TimeslotService, type UpdateEngineInput, type UpdateTimeslotResult, type UpsertOverridesInput, type UpsertOverridesResult, type Waiver, createAccessServiceForInstall, noopLogger, parseBookingWebhook, parseInstallWebhook, parseWaiverWebhook, verifyInstallWebhook };
2956
+ export { ACTIVITY_PRODUCT_TYPE, ADD_ON_PRODUCT_TYPE, type AccessOptions, type AccountUser, AccountUserService, type AccountUserServiceOptions, AcmeAccessService, type AcmeAccessServiceConfig, type AcmeActivity, type AcmeActivityTicket, AcmeApiError, AcmeProductService, type Activity, type ActivityOverrides, type ActivityTicket, type AddAddonInput, AdminAccountRequiredError, type Agent, type AssignGuideResult, type AssignedActivity, type AssignedResource, type Availability, AvailabilityService, type AvailabilityTime, type AvailabilityTimesQuery, type Booking, type BookingAddon, type BookingAddonMoney, type BookingAddonOption, type BookingAddons, type BookingAddonsMutationResult, type BookingPaymentsOnFile, type BookingReadOptions, type BookingSearchBy, BookingService, type BookingServiceOptions, type BookingTimeRangeSearch, type CancelBookingResult, type Channel, type ClearOverridesInput, CngAccessService, type CngAccessServiceConfig, CngApiError, CngProductService, type CreateBookingGuest, type CreateBookingInput, type CreateBookingTicket, type CreateEngineInput, type CreatePromoCodeInput, type CreatedBooking, type CreatedPricingEngine, type CreatedPromoCode, type CustomQuestion, type CustomQuestionAnswer, type CustomQuestionAnswerInput, type CustomQuestionOption, type DailyNote, DailyNoteService, type Duration, type Guest, type GuestMetadata, type Guide, type GuideAssignment, INSTALL_STATUSES, type InstallAccessConfig, type InstallAccessService, type InstallAccessTarget, type InstallStatus, type InstallWebhook, type InstallWebhookAccount, type InstallWebhookClaims, type InvoiceLinkResult, type Logger, type MakePaymentInput, type MakePaymentResult, type Membership, type MembershipPurchaseInput, MembershipService, type NoteMode, type Payment, type PaymentSource, PeekAccessService, type PeekAccessServiceConfig, type PeekAuthTokenClaims, type PeekAuthTokenUser, PeekGraphQLError, PeekHttpError, type PeekPlatform, PiiAccessDisabledError, type Price, type PricingActivityContext, type PricingEngine, type PricingMoney, type PricingNamedRef, type PricingOverride, type PricingOverrideFilter, PricingService, type Product, type ProductMeetingLocation, ProductService, type ProductServiceOptions, type ProductTicket, type PromoCode, type PromoCodeFixedAmount, PromoCodeService, type PromoCodeServiceOptions, type PurchasedMembership, RENTAL_PRODUCT_TYPE, RateLimitError, type RefundInput, type RefundResult, ResellerService, type ResolvedOverride, type ResolvedPricingOverride, type ResolvedResourceOption, type Resource, type ResourceOptionOverride, type ResourceOptionQuantity, type ResourcePool, type ResourcePoolAccountUser, type ResourcePoolAssignment, type ResourcePoolMode, ResourcePoolService, type Review, ReviewService, type Ticket, type Timeslot, type TimeslotFilter, TimeslotService, type UpdateEngineInput, type UpdateTimeslotResult, type UpsertOverridesInput, type UpsertOverridesResult, type Waiver, createAccessServiceForInstall, noopLogger, parseBookingWebhook, parseInstallWebhook, parseWaiverWebhook, verifyInstallWebhook };
package/dist/index.js CHANGED
@@ -1039,6 +1039,94 @@ function toBookingAddon(item) {
1039
1039
  return { addonId, addonName, total: item.total, addonOptions };
1040
1040
  }
1041
1041
 
1042
+ // src/internal/peek/bookings/custom-question-answer.ts
1043
+ var QUESTION_TYPE_CHECK_BOX = "CHECK_BOX";
1044
+ var QUESTION_TYPE_TEXT = "TEXT";
1045
+ var QUESTION_TYPE_SELECT_ONE = "SELECT_ONE";
1046
+ var QUESTION_TYPE_LOCATION = "LOCATION";
1047
+ var QUESTION_ID_REGEX = /^cq_[a-z0-9]+$/;
1048
+ var OPTION_ID_REGEX = /^cqao_[a-z0-9]+$/;
1049
+ var CHECKBOX_TRUE = /* @__PURE__ */ new Set(["yes", "true"]);
1050
+ var CHECKBOX_VALID = /* @__PURE__ */ new Set(["yes", "no", "true", "false"]);
1051
+ var CHECKBOX_TEXT_TRUE = "Yes";
1052
+ var CHECKBOX_TEXT_FALSE = "No";
1053
+ var errUnknownQuestionId = (v) => `No custom question matches id "${v}" for this activity`;
1054
+ var errNoTextMatch = (v) => `No custom question matches text "${v}" for this activity`;
1055
+ var errAmbiguousText = (v) => `Custom question text "${v}" matches more than one question`;
1056
+ var errPerGuestUnsupported = (id) => `Per-guest custom question "${id}" is not yet supported`;
1057
+ var errUnsupportedType = (type, id) => `Custom question "${id}" has unsupported type "${type}"`;
1058
+ var errInvalidCheckbox = (v, id) => `Checkbox answer for question "${id}" must be yes/no/true/false, got "${v}"`;
1059
+ var errUnknownOptionId = (v, id) => `No option matches id "${v}" for question "${id}"`;
1060
+ var errNoOptionMatch = (v, id) => `No option matches "${v}" for question "${id}"`;
1061
+ var errAmbiguousOption = (v, id) => `Option "${v}" matches more than one option for question "${id}"`;
1062
+ function normalize(value) {
1063
+ return value.toLowerCase().replace(/[^a-z0-9]/g, "");
1064
+ }
1065
+ function resolveQuestion(idOrText, questions) {
1066
+ if (QUESTION_ID_REGEX.test(idOrText)) {
1067
+ const byId = questions.find((question) => question.id === idOrText);
1068
+ if (!byId) throw new Error(errUnknownQuestionId(idOrText));
1069
+ return byId;
1070
+ }
1071
+ const target = normalize(idOrText);
1072
+ const matches = questions.filter(
1073
+ (question) => normalize(question.questionText) === target
1074
+ );
1075
+ if (matches.length === 0) throw new Error(errNoTextMatch(idOrText));
1076
+ if (matches.length > 1) throw new Error(errAmbiguousText(idOrText));
1077
+ return matches[0];
1078
+ }
1079
+ function resolveOption(value, question) {
1080
+ if (OPTION_ID_REGEX.test(value)) {
1081
+ const byId = question.options.find((option) => option.id === value);
1082
+ if (!byId) throw new Error(errUnknownOptionId(value, question.id));
1083
+ return byId;
1084
+ }
1085
+ const target = normalize(value);
1086
+ const matches = question.options.filter(
1087
+ (option) => normalize(option.value) === target
1088
+ );
1089
+ if (matches.length === 0) throw new Error(errNoOptionMatch(value, question.id));
1090
+ if (matches.length > 1) throw new Error(errAmbiguousOption(value, question.id));
1091
+ return matches[0];
1092
+ }
1093
+ function resolveOne(answer, questions) {
1094
+ const question = resolveQuestion(answer.questionIdOrText, questions);
1095
+ if (question.perGuest) {
1096
+ throw new Error(errPerGuestUnsupported(question.id));
1097
+ }
1098
+ switch (question.questionType) {
1099
+ case QUESTION_TYPE_CHECK_BOX: {
1100
+ const normalized = answer.value.trim().toLowerCase();
1101
+ if (!CHECKBOX_VALID.has(normalized)) {
1102
+ throw new Error(errInvalidCheckbox(answer.value, question.id));
1103
+ }
1104
+ const isChecked = CHECKBOX_TRUE.has(normalized);
1105
+ return {
1106
+ questionId: question.id,
1107
+ isChecked,
1108
+ questionAnswerText: isChecked ? CHECKBOX_TEXT_TRUE : CHECKBOX_TEXT_FALSE
1109
+ };
1110
+ }
1111
+ case QUESTION_TYPE_TEXT:
1112
+ return { questionId: question.id, questionAnswerText: answer.value };
1113
+ case QUESTION_TYPE_SELECT_ONE:
1114
+ case QUESTION_TYPE_LOCATION: {
1115
+ const option = resolveOption(answer.value, question);
1116
+ return {
1117
+ questionId: question.id,
1118
+ questionAnswerOptionId: option.id,
1119
+ questionAnswerText: option.value
1120
+ };
1121
+ }
1122
+ default:
1123
+ throw new Error(errUnsupportedType(question.questionType, question.id));
1124
+ }
1125
+ }
1126
+ function resolveCustomQuestionAnswers(answers, questions) {
1127
+ return answers.map((answer) => resolveOne(answer, questions));
1128
+ }
1129
+
1042
1130
  // src/internal/peek/bookings/booking-service.ts
1043
1131
  var DEFAULT_PAGE_SIZE2 = 50;
1044
1132
  var DEFAULT_CANCEL_NOTE = "Canceled";
@@ -1558,6 +1646,7 @@ ${note}`;
1558
1646
  */
1559
1647
  async create(input) {
1560
1648
  validateCreateInput(input);
1649
+ const questionAnswers = await this.buildQuestionAnswers(input);
1561
1650
  const tickets = input.tickets.flatMap(
1562
1651
  (ticket) => Array.from({ length: ticket.quantity }, () => ({
1563
1652
  resourceOptionId: ticket.resourceOptionId,
@@ -1584,6 +1673,9 @@ ${note}`;
1584
1673
  if (input.operatorNotes) {
1585
1674
  bookingQuote.operatorNotes = input.operatorNotes;
1586
1675
  }
1676
+ if (questionAnswers) {
1677
+ bookingQuote.questionAnswers = questionAnswers;
1678
+ }
1587
1679
  const quoteInput = { bookingQuotes: [bookingQuote] };
1588
1680
  if (input.parentOrderId) {
1589
1681
  quoteInput.source = { clonedFromId: input.parentOrderId };
@@ -1623,6 +1715,25 @@ ${note}`;
1623
1715
  }
1624
1716
  return created;
1625
1717
  }
1718
+ /**
1719
+ * Resolves the input's custom-question answers into the quote's
1720
+ * `questionAnswers` payload (each tagged with a fresh `refid`), or `null` when
1721
+ * none were supplied. Fetches the activity's custom questions to validate
1722
+ * against; throws on the first unmatched/ambiguous/invalid answer.
1723
+ */
1724
+ async buildQuestionAnswers(input) {
1725
+ const answers = input.customQuestionAnswers;
1726
+ if (!answers || answers.length === 0) {
1727
+ return null;
1728
+ }
1729
+ const questions = await this.deps.productService.getCustomQuestions(
1730
+ input.activityId
1731
+ );
1732
+ return resolveCustomQuestionAnswers(answers, questions).map((resolved) => ({
1733
+ ...resolved,
1734
+ refid: randomUUID()
1735
+ }));
1736
+ }
1626
1737
  async markCreatedBookingPaid(booking, input) {
1627
1738
  const parsedPartial = input.markAsPaidAmount ? parseFloat(input.markAsPaidAmount) : NaN;
1628
1739
  const amount = !Number.isNaN(parsedPartial) && parsedPartial > 0 ? parsedPartial.toFixed(2) : booking.balanceAmount;
@@ -2935,6 +3046,33 @@ function fromItemOptionNodes(nodes) {
2935
3046
  return Array.from(grouped.values());
2936
3047
  }
2937
3048
 
3049
+ // src/internal/peek/products/custom-question-converter.ts
3050
+ function fromQuestionConfigurations(configs) {
3051
+ return configs.map(fromQuestionConfiguration);
3052
+ }
3053
+ function fromQuestionConfiguration(config) {
3054
+ const question = config.question;
3055
+ return {
3056
+ id: question.id,
3057
+ order: config.order ?? 0,
3058
+ isRequired: config.isRequired ?? false,
3059
+ questionText: question.text,
3060
+ hintText: question.hint ?? null,
3061
+ questionType: question.questionType,
3062
+ internalLabel: question.internalLabel ?? null,
3063
+ perGuest: question.perGuest ?? false,
3064
+ defaultValue: question.answerDefaultValue ?? null,
3065
+ options: (question.options ?? []).map(fromQuestionOption)
3066
+ };
3067
+ }
3068
+ function fromQuestionOption(option) {
3069
+ return {
3070
+ id: option.id,
3071
+ order: option.order,
3072
+ value: option.text
3073
+ };
3074
+ }
3075
+
2938
3076
  // src/internal/peek/products/product-queries.ts
2939
3077
  var PRODUCTS_QUERY = `
2940
3078
  query Sales {
@@ -2993,7 +3131,38 @@ var ITEM_OPTIONS_QUERY = `
2993
3131
  }
2994
3132
  `;
2995
3133
 
3134
+ // src/internal/peek/products/custom-question-queries.ts
3135
+ var CUSTOM_QUESTIONS_QUERY = `
3136
+ query Sales($id: ID!) {
3137
+ activity(id: $id) {
3138
+ id
3139
+ name
3140
+ questionActivityConfigurations {
3141
+ isRequired
3142
+ order
3143
+ question {
3144
+ id
3145
+ questionType
3146
+ text
3147
+ answerDefaultValue
3148
+ hint
3149
+ internalLabel
3150
+ perGuest
3151
+ ... on ChoiceQuestion {
3152
+ options {
3153
+ id
3154
+ text
3155
+ order
3156
+ }
3157
+ }
3158
+ }
3159
+ }
3160
+ }
3161
+ }
3162
+ `;
3163
+
2996
3164
  // src/internal/peek/products/product-service.ts
3165
+ var ERROR_PRODUCT_ID_REQUIRED2 = "A non-empty product id is required.";
2997
3166
  var DEFAULT_ITEM_OPTIONS_PAGE_SIZE = 50;
2998
3167
  var ProductService = class {
2999
3168
  constructor(client, options = {}) {
@@ -3038,6 +3207,32 @@ var ProductService = class {
3038
3207
  const nodes = await this.fetchAllItemOptionNodes();
3039
3208
  return fromItemOptionNodes(nodes);
3040
3209
  }
3210
+ /**
3211
+ * Returns every custom question configured on a single activity, in the order
3212
+ * Peek reports them. Choice-style questions (`SELECT_ONE`, `LOCATION`, …)
3213
+ * carry their selectable {@link CustomQuestion.options}; free-text/checkbox
3214
+ * questions return an empty option list.
3215
+ *
3216
+ * These are question **definitions**, not customer answers — no PII is
3217
+ * involved, so the result is unaffected by `fullCustomerAccess`.
3218
+ *
3219
+ * @param productId The activity's id.
3220
+ * @returns The activity's custom questions, or an empty list when the activity
3221
+ * has none or is not found.
3222
+ * @throws Error when `productId` is missing or blank.
3223
+ */
3224
+ async getCustomQuestions(productId) {
3225
+ if (!productId?.trim()) {
3226
+ throw new Error(ERROR_PRODUCT_ID_REQUIRED2);
3227
+ }
3228
+ const body = await this.client.request(
3229
+ SALES_ENDPOINT,
3230
+ CUSTOM_QUESTIONS_QUERY,
3231
+ { id: productId }
3232
+ );
3233
+ const configs = body.data?.activity?.questionActivityConfigurations ?? [];
3234
+ return fromQuestionConfigurations(configs);
3235
+ }
3041
3236
  async fetchActivities() {
3042
3237
  const body = await this.client.request(
3043
3238
  SALES_ENDPOINT,
@@ -3751,6 +3946,10 @@ var PeekAccessService = class {
3751
3946
  getAllAddons() {
3752
3947
  return this.getProductService().getAllAddons();
3753
3948
  }
3949
+ /** An activity's custom questions. Delegates to {@link ProductService.getCustomQuestions}. */
3950
+ getCustomQuestions(productId) {
3951
+ return this.getProductService().getCustomQuestions(productId);
3952
+ }
3754
3953
  // ─── Account-user short-forms ─────────────────────────────────────────────
3755
3954
  /** All active account users. Delegates to {@link AccountUserService.getAll}. */
3756
3955
  getAllAccountUsers() {