import type { FanOutAvailabilityResult } from "@voyant-travel/catalog"; import type { AvailabilityCandidate, AvailabilitySearchRequest } from "@voyant-travel/catalog-contracts"; import type { AnyDrizzleDb } from "@voyant-travel/db"; import type { NewTripCandidate, NewTripComponent, TripCandidate, TripComponent, TripRequirement } from "./schema.js"; /** A candidate is expired once its TTL has elapsed. */ export declare function isTripCandidateExpired(candidate: Pick, asOf: Date): boolean; /** Guard a candidate is still selectable; throws otherwise. */ export declare function assertCandidateSelectable(candidate: TripCandidate, asOf: Date): void; /** * Reserve gate: every `required` requirement must be `selected` before a trip * can reserve. Pure — the caller supplies the envelope's requirements. */ export declare function assertRequiredRequirementsResolved(requirements: TripRequirement[]): void; /** Map a normalized `AvailabilityCandidate` into a persisted candidate row. */ export declare function availabilityCandidateToRow(args: { requirementId: string; envelopeId: string; candidate: AvailabilityCandidate; rank: number; }): NewTripCandidate; /** * Build the draft component a selected candidate pins into. The component is * intentionally `draft`/unpriced: `priceTrip` re-resolves the selection * (`candidateRef` is not replay-safe) and `reserveTrip` re-validates before any * supplier dispatch. * * `sourceKind` is load-bearing: `isCatalogBackedTripComponent` requires it for * the component to be priced via the catalog path (rather than falling through * to placeholder pricing → `unavailable`), and the catalog booking engine * routes on it — `"owned"` (`OWNED_SOURCE_KIND`) to an owned handler keyed by * entityModule, anything else to a sourced adapter keyed by `sourceConnectionId`. * The candidate's `sourceKind` is already `"owned"`/`"sourced"`, which aligns. */ export declare function pinnedComponentValuesFromCandidate(candidate: TripCandidate, sequence: number): NewTripComponent; export interface AddRequirementInput { envelopeId: string; vertical: string; criteria: Record; criteriaVersion: string; sequence?: number; required?: boolean; title?: string; description?: string; metadata?: Record; } export interface SourceRequirementCandidatesInput { requirementId: string; /** Search scope (locale/audience/market/currency), mirrors the catalog request. */ scope: AvailabilitySearchRequest["scope"]; deadlineMs?: number; limit?: number; } /** * The deployment injects the actual fan-out (wired with its adapters + owned * handlers), keeping this service deterministic and provider-agnostic. */ export interface SourceRequirementCandidatesDeps { search: (request: AvailabilitySearchRequest) => Promise; } export interface SelectCandidateInput { requirementId: string; candidateId: string; asOf?: Date; } export declare function addRequirement(db: AnyDrizzleDb, input: AddRequirementInput): Promise; export declare function listEnvelopeRequirements(db: AnyDrizzleDb, envelopeId: string): Promise; /** Reserve gate — throws if any required requirement is unresolved. */ export declare function assertEnvelopeRequirementsSatisfied(db: AnyDrizzleDb, envelopeId: string): Promise; /** * Resolve a requirement by selecting one of its candidates: enforces * selected-uniqueness, pins a draft component, and records the resolution. */ export declare function selectCandidate(db: AnyDrizzleDb, input: SelectCandidateInput): Promise<{ requirement: TripRequirement; candidate: TripCandidate; component: TripComponent; }>; /** * TTL reaper: mark `ranked` candidates whose TTL has elapsed as `expired`. * Returns how many were swept. Intended to be driven by a deployment cron. */ export declare function expireStaleTripCandidates(db: AnyDrizzleDb, asOf: Date): Promise;