/** * Does a selection satisfy the Booking Requirements the server published? * * `BookingRequirementsV1` is what a booking of a target *requires*; * `bookingSelectionPublicV1` is what has been *picked*. Until this module * existed the pairing was advisory: nothing checked that the selection a host * collected answered the descriptor the server published, so a host that * rendered the wrong field set did not error — it collected a plausible-looking * set and failed at commit, or committed something incomplete. That is the * voyant#4113 class of bug, and it cost one operator nine days. * * This is the one validator. The Booking Session calls it at quote time (so a * host learns what is missing while it can still fix it) and again at commit * (server-authoritative — never trust that the client quoted first). Two * validators would recreate the exact defect this closes, so it lives in the * contracts package where a third-party vertical, the conformance suite, and * the engine all reach the same function. * * It only checks what the requirements DECLARE. A descriptor that asks for * nothing rejects nothing; a descriptor that marks a step required is enforced. * Every finding is machine-readable — a stable `requirementKey` and an enum * `reason`, never prose a client would have to parse. */ import { z } from "zod"; import type { BookingRequirementsV1 } from "./requirements-contracts.js"; /** * Why a declared requirement is not satisfied. An enum rather than a message: * the host renders the copy, the server names the fact. */ export declare const unsatisfiedRequirementReasonV1: z.ZodEnum<{ pax_band_below_min: "pax_band_below_min"; pax_band_above_max: "pax_band_above_max"; pax_total_below_min: "pax_total_below_min"; pax_total_above_max: "pax_total_above_max"; pax_band_master_required: "pax_band_master_required"; pax_band_excluded: "pax_band_excluded"; pax_band_per_master_exceeded: "pax_band_per_master_exceeded"; pax_band_sum_exceeded: "pax_band_sum_exceeded"; departure_required: "departure_required"; option_units_required: "option_units_required"; cabin_category_required: "cabin_category_required"; cabin_number_required: "cabin_number_required"; date_range_required: "date_range_required"; date_range_too_short: "date_range_too_short"; date_range_too_long: "date_range_too_long"; occupancy_required: "occupancy_required"; air_arrangement_required: "air_arrangement_required"; traveler_field_required: "traveler_field_required"; booking_field_required: "booking_field_required"; }>; export type UnsatisfiedRequirementReasonV1 = z.infer; /** * One declared requirement the selection does not answer. * * `requirementKey` addresses the requirement inside the descriptor and is * stable across derivations of the same product: `paxBands.adult`, * `configureSubSteps.departure`, `travelerFields.passport.travelers.1`, * `bookingFields.buyerType`. A host maps it back to the control it drew. */ export declare const unsatisfiedRequirementV1: z.ZodObject<{ requirementKey: z.ZodString; reason: z.ZodEnum<{ pax_band_below_min: "pax_band_below_min"; pax_band_above_max: "pax_band_above_max"; pax_total_below_min: "pax_total_below_min"; pax_total_above_max: "pax_total_above_max"; pax_band_master_required: "pax_band_master_required"; pax_band_excluded: "pax_band_excluded"; pax_band_per_master_exceeded: "pax_band_per_master_exceeded"; pax_band_sum_exceeded: "pax_band_sum_exceeded"; departure_required: "departure_required"; option_units_required: "option_units_required"; cabin_category_required: "cabin_category_required"; cabin_number_required: "cabin_number_required"; date_range_required: "date_range_required"; date_range_too_short: "date_range_too_short"; date_range_too_long: "date_range_too_long"; occupancy_required: "occupancy_required"; air_arrangement_required: "air_arrangement_required"; traveler_field_required: "traveler_field_required"; booking_field_required: "booking_field_required"; }>; }, z.core.$strip>; export type UnsatisfiedRequirementV1 = z.infer; /** * Validate a selection against the requirements that were published with it. * * Pure and side-effect free. `selection` is deliberately `unknown`: the Booking * Session stores a normalized record, hosts hold a * `BookingSelectionPublicV1`, and neither is worth a parse here — every read * below is defensive, and a shape the validator cannot read is treated as * absent, which is what it is. * * Returns every unsatisfied requirement, not the first one: a host that has to * round-trip once per missing field is a host that gives up. */ export declare function validateSelectionAgainstRequirements(requirements: BookingRequirementsV1, selection: unknown): UnsatisfiedRequirementV1[]; /** * Every requirement key the descriptor declares as required — the checkable * surface of a descriptor, independent of any selection. * * The conformance suite uses it to hold a third-party vertical to the same * contract: a descriptor may not mark something required that this validator * would never look at, because a requirement nothing checks is exactly the * advisory state #4188 removes. */ export declare function requiredRequirementKeysV1(requirements: BookingRequirementsV1): string[]; /** * Traveler-field keys the Booking Session can actually carry. * * `normalizeBookingSelection` projects a stored traveler down to this set * (`sessions-production.ts`, `normalizeTraveler`), deliberately: the richer * fields a wizard collects — `documents`, `dateOfBirth`, `preferredLanguage`, * `specialRequests` — are plaintext PII that the v1 posture does not keep on * an open Session. * * That makes them unsatisfiable, not merely unchecked. A vertical marking one * of them `required` would publish a descriptor its own commit path can never * satisfy, and the buyer would be told to supply something the pipeline drops * on the way in — voyant#4113's shape, arriving through the enforcement added * to prevent it. * * So the mismatch is a contract error, surfaced by the conformance suite at * development time rather than as a dead-ended quote in production. Widening * this set means widening the selection projection first, and deciding where * the PII lands. */ export declare const REPRESENTABLE_TRAVELER_FIELD_KEYS_V1: ReadonlySet; /** Booking-field groups the public selection has a bucket for. */ export declare const REPRESENTABLE_BOOKING_FIELD_GROUPS_V1: ReadonlySet; /** * Required requirements the Session could never satisfy, whatever the buyer * enters. Empty is the only acceptable value; a non-empty result is a defect * in the descriptor, not in the selection. */ export declare function unsatisfiableRequiredRequirementsV1(requirements: BookingRequirementsV1): UnsatisfiedRequirementV1[];