/** * `OwnedBookingHandler` + `OwnedBookingHandlerRegistry` — the * dispatch seam for owned-arm bookings. * * Per `docs/architecture/booking-journey-architecture.md` §6, the * catalog booking engine has two dispatch axes: * * - Sourced rows go through `SourceAdapterRegistry` keyed by * `connection_id` (one adapter per upstream connection). * - Owned rows go through `OwnedBookingHandlerRegistry` keyed by * `entity_module` (one handler per vertical — products, * accommodations, cruises, etc.). * * The two registries live side-by-side, never wrap each other. The * dispatch direction inverts: instead of `@voyantjs/catalog` * importing every vertical, each vertical imports this interface * and provides an implementation. Catalog stays a contract package; * verticals stay self-contained. * * Phase A (per doc §10) ships the registry + interface + the first * vertical handler (products). Subsequent phases land accommodations, * cruises, etc. against the same interface without re-architecting * the dispatch. */ import type { AnyDrizzleDb } from "@voyantjs/db"; import type { SourceAdapterContext } from "../adapter/contract.js"; import type { PricingBasis } from "../snapshot/schema.js"; import type { BookingDraftShape } from "./draft-shape.js"; export interface OwnedHandlerContext { db: AnyDrizzleDb; /** Echoed through from the engine — handlers may use it for audit. */ adapterContext: SourceAdapterContext; } export interface OwnedQuoteScope { locale: string; audience: string; market: string; currency?: string; } export interface ComputeQuoteRequest { entityModule: string; entityId: string; scope: OwnedQuoteScope; /** * Vertical-specific parameters echoed by callers. Free-form today; * Phase B adds a typed `BookingDraft` payload that supersedes this. */ parameters?: Record; /** * Optional partial draft state — used when the wizard re-quotes * mid-journey. Owned handlers read pax counts, addons, accommodation * picks, billing country off this to compute the correct price. * * Typed as `unknown` here so Phase A doesn't pin the schema in the * catalog package; Phase B replaces with a Zod-validated * `BookingDraftV1` (see §8.5 of the doc). */ draft?: unknown; } export interface ComputeQuoteResult { available: boolean; invalidReason?: string; pricing?: PricingBasis; /** * Per-quote journey descriptor. Owned handlers return this when * they have enough context (always, for products in Phase A; the * journey falls back to defaults when omitted). */ shape?: BookingDraftShape; /** Echoed back into `catalog_quotes.upstream_payload` for audit. */ upstreamPayload?: Record; } export interface CommitOwnedRequest { entityModule: string; entityId: string; /** Booking shell id — generated by the engine when the caller * doesn't pass one. Always defined here. */ bookingId: string; /** * Free-form party / passenger payload echoed from the caller. Phase * B supersedes with a typed draft. */ party?: Record; parameters?: Record; /** Pre-validated quote pricing — handlers MAY trust this for cost * basis or recompute on their own (some verticals re-price at * commit). */ pricing?: PricingBasis; /** Optional draft payload — Phase B routes the full draft here. */ draft?: unknown; } export interface CommitOwnedResult { status: "held" | "confirmed" | "ticketed" | "failed"; orderRef: string; /** * The id of the booking row the handler actually created. The engine adopts * this as the canonical booking id (the handler mints its own rather than use * the provided `request.bookingId`), so the response + snapshot + quote- * consumed marker all point at the real row. */ bookingId?: string; /** Re-priced or echoed pricing — written into the snapshot. */ pricing?: PricingBasis; /** Free-form payload preserved in the snapshot's frozen_payload. */ upstreamPayload?: Record; } export interface HoldRequest { entityModule: string; entityId: string; /** Caller-supplied draft id; handlers tie holds to the draft so * abandonment is observable. */ draftId?: string; ttlMs: number; parameters?: Record; } export interface HoldResult { holdToken: string; expiresAt: Date; } export interface OwnedBookingHandler { /** Vertical this handler claims. One handler per `entity_module`. */ readonly entityModule: string; /** * Per-vertical hold-release grace period in milliseconds. The * reaper defers calling `releaseHold` for `grace` past a draft's * expiry. Default `0` (immediate release). * * Mirrors `AdapterCapabilities.holdReleaseGraceMs` for sourced * holds. Per booking-journey-architecture §12.9. */ readonly holdReleaseGraceMs?: number; /** * Live-quote an owned row for a draft. The engine calls this on * every meaningful input change. Returns shape + pricing + * availability. * * Implementations should be idempotent — the same input must * produce the same output (modulo time-sensitive fields like * promo windows, which are explicitly OK to vary). */ computeQuote(ctx: OwnedHandlerContext, request: ComputeQuoteRequest): Promise; /** * Commit the draft to a booking row. Phase A handlers map the * draft into `bookingsCreate`'s input shape; later phases * extend this to model extras, accommodation stays, encrypted * travel details, tax lines, snapshot graphs. */ commit(ctx: OwnedHandlerContext, request: CommitOwnedRequest): Promise; /** Optional: place / extend / release a soft hold on the row. */ placeHold?(ctx: OwnedHandlerContext, request: HoldRequest): Promise; extendHold?(ctx: OwnedHandlerContext, holdToken: string, request?: Pick): Promise; releaseHold?(ctx: OwnedHandlerContext, holdToken: string): Promise; } export interface OwnedBookingHandlerRegistry { /** Register a handler. Re-registering the same `entityModule` * replaces the previous handler. */ register(handler: OwnedBookingHandler): void; resolve(entityModule: string): OwnedBookingHandler | undefined; resolveOrThrow(entityModule: string): OwnedBookingHandler; has(entityModule: string): boolean; modules(): ReadonlyArray; } export declare function createOwnedBookingHandlerRegistry(): OwnedBookingHandlerRegistry; /** Stable string the engine inspects when deciding which arm to use. */ export declare const OWNED_SOURCE_KIND: "owned"; //# sourceMappingURL=owned-handler.d.ts.map