/** * Cruise content service — `getCruiseContent` with owned-vs-sourced * dispatch, locale-resolved cache reads, SWR refresh, and synthesizer fallback. * * Mirrors `service-content.ts` in the products package but cruise- * shaped. The cruise content aggregate (§3.2 / §E) is `{ cruise, ship, * sailings[], cabin_categories[], itinerary_stops[], policies[] }` — * one payload returned by a single getContent. The cruise adapter's * existing internal multi-method API * (`fetchCruise/fetchSailing/fetchShip/fetchItinerary`) composes * internally to produce this blob; the public catalog SourceAdapter * contract gets one method, not five. * * See `docs/architecture/catalog-sourced-content.md` §3.3, §3.4, §3.6. */ import { type ContentLocaleResolution, type InvalidateOnDrift, type SourceAdapter, type SourceAdapterContext } from "@voyant-travel/catalog"; import type { SourceAdapterRegistry } from "@voyant-travel/catalog/booking-engine"; import type { AnyDrizzleDb } from "@voyant-travel/db"; import { type CruiseContent } from "./content-shape.js"; export interface CruiseContentScope { preferredLocales: ReadonlyArray; market?: string; currency?: string; acceptMachineTranslated?: boolean; } export interface GetCruiseContentOptions { registry: SourceAdapterRegistry; buildAdapterContext?: (adapter: SourceAdapter) => SourceAdapterContext; onOverlayError?: (event: { field_path: string; reason: string; }) => void; } export interface ResolvedCruiseContent { content: CruiseContent; resolution: ContentLocaleResolution<{ locale: string; payload: CruiseContent; }>; provenance: ResolvedCruiseContentProvenance; source: "sourced-cache" | "sourced-fresh" | "synthesized" | "owned"; served_stale: boolean; synthesized: boolean; machine_translated: boolean; } export interface ResolvedCruiseContentProvenance { source_kind: string; source_provider?: string; source_connection_id?: string; source_ref?: string; } export declare function getCruiseContent(db: AnyDrizzleDb, entityId: string, scope: CruiseContentScope, options: GetCruiseContentOptions): Promise; export interface CruiseSailingPricingRow { /** Upstream cabin-category external id (e.g. `_`). */ cabinExternalId: string; occupancy: number; fareCode: string | null; fareName: string | null; currency: string; /** Major-unit price string as the adapter returns it (e.g. "12959.00"). */ pricePerPerson: string; availability: string; } /** * Live per-sailing cabin pricing for a sourced cruise. Pricing is volatile-live * (architecture §5.4) so it is fetched fresh from the adapter per call rather * than baked into the cached content — the catalog detail sheet calls this * lazily when a departure row is expanded. * * Returns `null` when the entity has no sourced row or the registered adapter * can't price sailings (e.g. a thin adapter without `fetchSailingPricing`). */ export declare function getCruiseSailingPricing(db: AnyDrizzleDb, entityId: string, sailingExternalId: string, options: { registry: SourceAdapterRegistry; }): Promise; /** * Drift event consumer for the cruises content cache. Per sourced- * content §3.4.1. */ export declare const invalidateCruiseContentOnDrift: InvalidateOnDrift;