/** * `bookEntity` — the second step in the booking engine lifecycle. * * Validates the supplied quote, calls `adapter.reserve`, and writes a * `booking_catalog_snapshot` row with the frozen view, source pointer, * and pricing breakdown. The snapshot is the audit record; the source * pointer is what subsequent `cancelEntity` / status calls dispatch * against. * * `bookingId` is plain text (no FK), so the engine accepts an existing * id from the caller (when a `bookings` row already exists) or generates * one on demand. This keeps the engine decoupled from `packages/bookings` * — the engine and the bookings module evolve independently and are * joined at read time via the shared `booking_id` text column. */ import type { AnyDrizzleDb } from "@voyantjs/db"; import type { SourceAdapterContext, SourceAdapterRequestScope } from "../adapter/contract.js"; import { type PricingBasis, type SelectBookingCatalogSnapshot } from "../snapshot/schema.js"; /** * `BookingPaymentIntent` now lives in `@voyantjs/catalog-contracts` * (the shared server/client seam, alongside `bookRequestV1` whose * `paymentIntent` field mirrors it). Re-exported here to keep existing * `@voyantjs/catalog/booking-engine` import paths stable. */ import type { BookingPaymentIntent } from "./contracts.js"; import type { OwnedBookingHandlerRegistry } from "./owned-handler.js"; import type { SourceAdapterRegistry } from "./registry.js"; import type { SnapshotContentCapturer } from "./snapshot-content.js"; export type { BookingPaymentIntent }; export interface BookEntityRequest { /** Quote previously returned from `quoteEntity`. */ quoteId: string; /** * Existing or newly-created booking shell id. Generated when omitted, * using the `bookings` typeid prefix so the value is shape-compatible * with `packages/bookings`. */ bookingId?: string; /** Customer / passenger payload echoed to the adapter's reserve call. */ party?: Record; /** Defaults to `{ type: "hold" }` — see `BookingPaymentIntent`. */ paymentIntent?: BookingPaymentIntent; /** Vertical-specific parameters passed to the adapter. */ parameters?: Record; /** * Caller-supplied idempotency key. When set, a duplicate * `bookEntity` call within the snapshot table's lifetime returns * the prior booking instead of creating a new one. Length 8–128. * Per booking-journey-architecture §12.6. */ idempotencyKey?: string; adapterContext: SourceAdapterContext; /** * Locale / market / currency scope for snapshot content capture per * sourced-content §5.1. Required when `deps.captureSnapshotContent` * is wired — the engine refreshes content from the adapter at commit * time using this scope and embeds the result as `content_capture` * in `frozen_payload`. When the deps callback isn't set, this field * is ignored and snapshot behavior is unchanged. */ contentScope?: { locale: string; market?: string; currency?: string; }; /** * Per-request supplier scope forwarded to `SourceAdapter.reserve`. * Kept separate from `contentScope`: this controls upstream write * semantics, while `contentScope` controls snapshot-content capture. */ adapterScope?: SourceAdapterRequestScope; } export interface BookEntityResult { bookingId: string; orderRef: string; status: "held" | "confirmed" | "ticketed" | "failed"; snapshotId: string; pricing?: PricingBasis; upstreamPayload?: Record; } export interface BookEntityDeps { registry: SourceAdapterRegistry; /** * Owned-arm dispatch — when set and the quote's source kind is * `"owned"`, the engine commits via a handler keyed by * `entity_module` instead of the SourceAdapterRegistry. Per * booking-journey-architecture §6. */ ownedHandlers?: OwnedBookingHandlerRegistry; /** * Optional snapshot content capture orchestrator (sourced-content * §5.1). When set, called after `adapter.reserve` succeeds. Returns a * `SnapshotContentCapture` envelope embedded in `frozen_payload` so * audit can later distinguish a fresh capture from a cache fallback. * * Throws `SnapshotContentUnavailableError` when neither a fresh * adapter fetch nor a cache fallback can produce content; the engine * propagates that error and aborts the commit. When the entity is * owned (no sourced-entry row), the orchestrator should return null * and the engine skips the capture. * * Implementations live in templates: each template composes per- * vertical content services into one capturer and threads it through * deps. */ captureSnapshotContent?: SnapshotContentCapturer; } /** * Book the row referenced by `quoteId`. End-to-end: validate quote, * dispatch `adapter.reserve`, capture snapshot, mark quote consumed. * * Throws: * - `BookingEngineError(QUOTE_NOT_FOUND)` if the quote doesn't exist. * - `QuoteExpiredError` if the quote's `expires_at` has passed. * - `QuoteMismatchError` if a future caller tries to book a different * entity than the quote was issued for (defensive — the engine * re-reads quote.entity_*). * - `NoAdapterRegisteredError` if the registry has no adapter for * the quote's `source_kind`. * - `ReserveFailedError` when the adapter returns `status: "failed"`. */ export declare function bookEntity(db: AnyDrizzleDb, deps: BookEntityDeps, request: BookEntityRequest): Promise; export type { SelectBookingCatalogSnapshot }; //# sourceMappingURL=book.d.ts.map