import type { AbandonBookingSessionV1, AdoptBookingSessionV1, BookingSessionActorKindV1, BookingSessionCapabilityActionV1, BookingSessionOriginV1, BookingSessionOutcomeV1, BookingSessionStateV1, BookingSessionTargetV1, CommitBookingSessionV1, CreateAcceptedProposalBookingSessionV1, CreateBookingSessionV1, PlaceBookingHoldV1, QuoteBookingSessionV1, RenewBookingSessionV1, UpdateBookingSessionV1 } from "@voyant-travel/catalog-contracts/booking-engine/session-contracts"; import type { BookingLifecycleCommitOutcomeV1, PricingBreakdownV1 } from "./contracts.js"; export interface BookingSessionInternalRecord { id: string; createIdempotencyKey: string; createRequestFingerprint: string; capabilityHash?: string; capabilityScopes: BookingSessionCapabilityActionV1[]; target: BookingSessionTargetV1; origin?: BookingSessionOriginV1; actorKind: BookingSessionActorKindV1; ownerPrincipalId?: string; ownerOrganizationId?: string; /** Immutable server-derived public storefront provenance. Never serialized. */ storefrontOrigin?: { storefrontId: string; channelId: string; }; state: BookingSessionStateV1; revision: number; statePayload: Record; expiresAt: Date; createdAt: Date; updatedAt: Date; purgedAt?: Date; } export interface BookingQuoteInternalRecord { id: string; sessionId: string; sessionRevision: number; state: "active" | "superseded" | "consumed" | "expired"; pricing: PricingBreakdownV1; priceFingerprint: string; quotedAt: Date; expiresAt: Date; } export interface BookingHoldInternalRecord { id: string; sessionId: string; quoteId: string; target: BookingSessionTargetV1; quantity: number; state: "active" | "converted" | "released" | "expired"; capacityKey: string; expiresAt: Date; createdAt: Date; } export interface BookingCommitInternalRecord { id: string; sessionId: string; idempotencyKey: string; requestFingerprint: string; outcome: Exclude; bookingId?: string; createdAt: Date; } export interface BookingSessionOperationRecord { id: string; sessionId: string; operation: "update" | "quote" | "hold" | "abandon" | "adopt" | "renew"; idempotencyKey: string; requestFingerprint: string; outcome?: BookingSessionOutcomeV1; createdAt: Date; } export interface BookingSessionAuditRecord { id: string; sessionId: string; action: "read" | "update" | "quote" | "hold" | "commit" | "adopt" | "renew" | "abandon" | "expire" | "purge" | "supplier_reconcile" | "supplier_manual_resolve"; actorKind: BookingSessionActorKindV1 | "system"; principalId?: string; organizationId?: string; authorityReason?: string; metadata: Record; createdAt: Date; } export type BookingSessionOperationClaim = { status: "claimed"; id: string; } | { status: "replay"; outcome: BookingSessionOutcomeV1; } | { status: "conflict"; }; export interface BookingSessionRepository { createSession(record: BookingSessionInternalRecord): Promise; getSession(sessionId: string): Promise; getSessionByCreateIdempotency(idempotencyKey: string): Promise; saveSession(record: BookingSessionInternalRecord): Promise; appendAudit(record: BookingSessionAuditRecord): Promise; listExpiryCandidates(input: { at: Date; limit: number; }): Promise; listPurgeCandidates(input: { before: Date; limit: number; }): Promise; purgeSessionArtifacts(sessionId: string): Promise; listActiveQuotes(sessionId: string): Promise; getQuote(quoteId: string): Promise; saveQuote(record: BookingQuoteInternalRecord): Promise; getHold(holdId: string): Promise; listActiveHolds(sessionId: string): Promise; saveHold(record: BookingHoldInternalRecord): Promise; getCommitByIdempotency(sessionId: string, idempotencyKey: string): Promise; saveCommit(record: BookingCommitInternalRecord): Promise; claimOperation(input: { id: string; sessionId: string; operation: BookingSessionOperationRecord["operation"]; idempotencyKey: string; requestFingerprint: string; now: Date; }): Promise; completeOperation(input: { id: string; outcome: BookingSessionOutcomeV1; }): Promise; consumeCommit(input: { sessionId: string; quoteId: string; holdId?: string; idempotencyKey: string; requestFingerprint: string; outcome: BookingCommitInternalRecord["outcome"]; bookingId: string; now: Date; }): Promise; withTransactionContext(tx: unknown, operation: () => Promise): Promise; withSessionTransaction(sessionId: string, operation: (tx: unknown) => Promise): Promise; } export interface ComposeBookingQuoteInput { session: BookingSessionInternalRecord; now: Date; tx: unknown; } export type BookingSessionQuoteUnavailableReason = "target_not_found" | "target_not_bookable" | "price_unavailable" | "policy_unavailable" | "selection_unavailable"; export type ComposeBookingQuoteResult = { status: "quoted"; pricing: PricingBreakdownV1; } | { status: "unavailable"; reason: BookingSessionQuoteUnavailableReason; nextAction: "select_alternative_inventory" | "contact_operator" | "update_selection"; }; export type PlaceBookingCapacityHoldResult = "held" | "unavailable" | { status: "quantity_mismatch"; expectedQuantity: number; }; export type BookingSessionCommitRejectionReason = "entity_not_found" | "entity_not_bookable" | "incomplete_draft" | "price_changed"; export interface CommitOwnedBookingInput { session: BookingSessionInternalRecord; quote: BookingQuoteInternalRecord; hold: BookingHoldInternalRecord; idempotencyKey: string; requestFingerprint: string; access: BookingSessionAccessContext; now: Date; paymentSessionId?: string; consumeSources(tx: unknown, bookingId: string, allocationIds: string[]): Promise; } export interface CommitSourcedBookingInput { session: BookingSessionInternalRecord; supplierOperationScope?: string; quote: BookingQuoteInternalRecord; hold?: BookingHoldInternalRecord; idempotencyKey: string; requestFingerprint: string; access: BookingSessionAccessContext; now: Date; paymentSessionId?: string; consumeSources(tx: unknown, bookingId: string, allocationIds: string[], supplierOperationId: string): Promise; } export type CommitSourcedBookingResult = { kind: "committed"; bookingId: string; allocationIds: string[]; supplierOperationId: string; } | Extract; export interface CompositeBookingCommitment { componentId: string; bookingId: string; allocationIds: string[]; supplierOperationId?: string; } export interface CommitCompositeBookingInput { session: BookingSessionInternalRecord; quote: BookingQuoteInternalRecord; hold?: BookingHoldInternalRecord; idempotencyKey: string; requestFingerprint: string; access: BookingSessionAccessContext; now: Date; consumeSources(tx: unknown, bookings: CompositeBookingCommitment[]): Promise; } export type CommitCompositeBookingResult = { kind: "committed"; bookings: CompositeBookingCommitment[]; } | Extract; export interface BookingSessionCompositeLeafRuntime { composeQuote(input: ComposeBookingQuoteInput): Promise; placeCapacityHold(input: { session: BookingSessionInternalRecord; quote: BookingQuoteInternalRecord; holdId: string; capacityKey: string; quantity: number; expiresAt: Date; access: BookingSessionAccessContext; now: Date; tx: unknown; }): Promise; releaseCapacityHold(input: { session: BookingSessionInternalRecord; hold: BookingHoldInternalRecord; access: BookingSessionAccessContext; now: Date; tx: unknown; }): Promise; commitOwned(input: CommitOwnedBookingInput): Promise<{ bookingId: string; allocationIds: string[]; }>; commitSourced(input: CommitSourcedBookingInput): Promise; } export interface BookingSessionCompositeHandler { composeQuote(input: ComposeBookingQuoteInput & { leaf: BookingSessionCompositeLeafRuntime; }): Promise; placeCapacityHold(input: { session: BookingSessionInternalRecord; quote: BookingQuoteInternalRecord; holdId: string; capacityKey: string; quantity: number; expiresAt: Date; access: BookingSessionAccessContext; now: Date; tx: unknown; leaf: BookingSessionCompositeLeafRuntime; }): Promise; releaseCapacityHold(input: { session: BookingSessionInternalRecord; hold: BookingHoldInternalRecord; access: BookingSessionAccessContext; now: Date; tx: unknown; leaf: BookingSessionCompositeLeafRuntime; }): Promise; commit(input: CommitCompositeBookingInput & { leaf: BookingSessionCompositeLeafRuntime; db: unknown; }): Promise; } export interface BookingSessionPaymentPorts { prepare(input: { session: BookingSessionInternalRecord; quote: BookingQuoteInternalRecord; hold?: BookingHoldInternalRecord; commit: CommitBookingSessionV1; access: BookingSessionAccessContext; now: Date; }): Promise<{ kind: "not_required"; } | { kind: "established"; paymentSessionId: string; } | { kind: "required"; paymentSession: { id: string; status: "pending" | "requires_redirect" | "processing" | "authorized" | "paid" | "failed" | "cancelled" | "expired"; amountCents: number; currency: string; redirectUrl: string | null; expiresAt: string | null; }; allowedGuarantees: Array<"deposit" | "pre_auth" | "card_on_file" | "agency_letter">; }>; transferToBooking(input: { tx: unknown; paymentSessionId: string; bookingSessionId: string; bookingId: string; }): Promise; expirePending(input: { tx: unknown; bookingSessionId: string; at: Date; }): Promise; } export interface BookingSessionAccessContext { actorKind: BookingSessionActorKindV1; principalId?: string; organizationId?: string; capability?: string; /** Trusted request context resolved by the public transport, never session state. */ storefront?: { storefrontId: string; channelId: string; }; sessionTtlMs?: number; staffAuthority?: { admitted: true; reason: string; }; /** Additional Finance + Bookings authority for operator booking details. */ staffBookingAuthority?: { admitted: true; reason: string; }; } export interface BookingSessionModulePorts { repository: BookingSessionRepository; normalizeSelection(input: { target: BookingSessionTargetV1; selection: Record; access: BookingSessionAccessContext; now: Date; }): Promise>; composeQuote(input: ComposeBookingQuoteInput): Promise; placeCapacityHold(input: { session: BookingSessionInternalRecord; quote: BookingQuoteInternalRecord; holdId: string; capacityKey: string; quantity: number; expiresAt: Date; access: BookingSessionAccessContext; now: Date; tx: unknown; }): Promise; releaseCapacityHold(input: { session: BookingSessionInternalRecord; hold: BookingHoldInternalRecord; access: BookingSessionAccessContext; now: Date; tx: unknown; }): Promise; commitOwnedBooking(input: CommitOwnedBookingInput): Promise<{ bookingId: string; allocationIds: string[]; }>; commitSourcedBooking?(input: CommitSourcedBookingInput): Promise; commitCompositeBooking?(input: CommitCompositeBookingInput): Promise; hasActiveSupplierOperation?(input: { sessionId: string; tx: unknown; }): Promise; payments?: BookingSessionPaymentPorts; } export interface BookingSessionModuleOptions { ports: BookingSessionModulePorts; now?: () => Date; sessionTtlMs?: number; quoteTtlMs?: number; holdTtlMs?: number; maxRenewalExtensionMs?: number; maxSessionLifetimeMs?: number; } export declare class InvalidBookingSessionSelectionError extends Error { readonly reason: "unsupported_target" | "forbidden_field"; readonly path?: string | undefined; constructor(reason: "unsupported_target" | "forbidden_field", path?: string | undefined); } export declare class BookingSessionCommitRejectedError extends Error { readonly reason: BookingSessionCommitRejectionReason; constructor(reason: BookingSessionCommitRejectionReason); } export interface BookingSessionModule { createAcceptedProposalSession(input: CreateAcceptedProposalBookingSessionV1, access: BookingSessionAccessContext): Promise; createSession(input: CreateBookingSessionV1, access: BookingSessionAccessContext): Promise; resumeSession(sessionId: string, access: BookingSessionAccessContext): Promise; adoptSession(sessionId: string, input: AdoptBookingSessionV1, access: BookingSessionAccessContext): Promise; renewSession(sessionId: string, input: RenewBookingSessionV1, access: BookingSessionAccessContext): Promise; updateSession(sessionId: string, input: UpdateBookingSessionV1, access: BookingSessionAccessContext): Promise; quoteSession(sessionId: string, input: QuoteBookingSessionV1, access: BookingSessionAccessContext): Promise; placeHold(sessionId: string, input: PlaceBookingHoldV1, access: BookingSessionAccessContext): Promise; abandonSession(sessionId: string, input: AbandonBookingSessionV1, access: BookingSessionAccessContext): Promise; commitSession(sessionId: string, input: CommitBookingSessionV1, access: BookingSessionAccessContext): Promise; expireDueSessions(input: { limit: number; }, access: BookingSessionAccessContext): Promise<{ expired: number; }>; purgeTerminalSessions(input: { before: Date; limit: number; }, access: BookingSessionAccessContext): Promise<{ purged: number; }>; } export declare function createBookingSessionModule(options: BookingSessionModuleOptions): BookingSessionModule;