@voyant-travel/cruises is the cruise vertical for OTA, tour-operator, and DMC deployments. It models cruise inventory the way the industry actually describes it (sailings, ships, decks, cabin grades, fare codes, occupancy, onboard credit) instead of bending cruises into the per-person, per-departure shape of the products module.
Cruises are an inventory and operations capability inside those scenarios, not a separate cruise-line implementation. The module ships the canonical schema, services, admin and storefront routes, and a booking extension, plus a provider-neutral adapter contract so external cruise inventory can flow through the same surfaces as cruises an operator owns.
The package is strictly opt-in. Deployments that do not sell cruises get no cruise tables, no cruise routes, and no cruise types in their TypeScript surface.
Key concepts
These terms come from the shared glossary. Cruises uses them natively rather than inventing synonyms.- Cruise catalog. A cruise is the route template (the voyage, itinerary, or holiday a line publishes). It carries the name, ship, nights, embark and disembark ports, and merchandising copy. The cruise is canonical, module-owned truth for self-managed inventory, and a derived read model for sourced inventory.
- Sailings. A sailing is a dated departure of a cruise on a specific ship, with departure and return dates, a sales status (open, on request, wait list, sold out, closed), and its own pricing. It is the equivalent of a Slot in the products world, but cruise availability lives per price row rather than per product day.
- Cabin categories. A cabin category is the archetype (Veranda Suite, Oceanview) on a ship, with a canonical room type, occupancy bounds, amenities, and the grade codes that map to it. Most operators sell at the category level. Specific cabins (cabin numbers) are an optional second layer for “guarantee a forward-facing balcony” inventory.
- Itinerary stops. The day-by-day itinerary lives at two levels: a per-cruise template and per-sailing overrides for sailings that skip a port or reroute. Reading the effective itinerary merges the two. Ports reference shared Places (facility rows) plus canonical place ids for faceting.
- Cruise extensions. A Cruise Extension is a cruise-specific pre or post-cruise hotel or land program. Its offer definition can be reused across many sailings. When the extension is cruise-owned and lifecycle-dependent it travels as an Extra under the cruise booking; when it is independently supplied, confirmed, cancelled, taxed, or supported it splits into a sibling Component Booking under the same Trip Envelope.
- The cruise adapter contract. External cruises reach the module through a registered
CruiseAdapter. The adapter is swappable, not baked in. A deployment picks the adapter package at startup; the framework never imports a concrete adapter. This is how Connect cruises plug in: a thin adapter over the Connect SDK satisfies the same contract a self-managed cruise resolves against.
Provenance: self-managed and external
Every cruise the operator’s customer sees comes from one of two places, side by side in the same admin experience.- Self-managed cruises live in the operator’s own database under the full canonical schema, with full admin CRUD. Bookings against them never touch an upstream.
- External cruises live in an upstream system reached through an adapter. The local database stores nothing about the cruise itself; reads resolve live through the adapter, and bookings snapshot the upstream state into the local booking row.
External badge. Bookings, payments, CRM, and finance plumbing is identical across the two.
What it owns
- The canonical cruise schema: cruises, sailings, ships, decks, cabin categories, specific cabins, prices, price components, itinerary days, per-sailing day overrides, media, inclusions, voyage groups (back-to-back, grand, world, and cruise-tour composites), and an opt-in storefront search index.
- A pricing grid keyed by (sailing, cabin category, occupancy) with fare codes, onboard credit, gratuities, port charges, taxes, and NCF modeled as composable price components.
cruisesServicefor cruise, sailing, ship, cabin, and itinerary reads and writes, andpricingServicefor quote assembly and lowest-price aggregates.- Admin routes (
/v1/admin/cruises/*) that are provenance-aware, and storefront routes (/v1/public/cruises/*) that read from the search-index projection. - A booking extension (
booking_cruise_details) plus a multi-cabin party-booking path that composes the bookings module’s group primitives. - The
CruiseAdaptercontract, the adapter registry, a memoizing decorator, aMockCruiseAdapterfor tests, andassertCruiseAdapterCompatibilityfor adapter authors.
Working with it
Create and price a self-managed cruise
Assemble a quote
assembleQuote is the single place quote math happens. The booking extension, the storefront, and the admin UI all call it, so there is one source of truth. Quotes return the native currency of the underlying price rows; the module never converts at ingest time.
Read the effective itinerary
Book a cabin
A cruise booking is a booking. The extension wraps the bookings module so the quote, the booking row, and the cruise detail snapshot all land in one transaction. Themode flag chooses between lead-gen inquiry and a payment-capable reserve, per tenant and per line.
createCruisePartyBooking composes the bookings group primitives: one shared confirmation number, atomic cancellation across cabins, a single deposit and invoice, with each cabin keeping its own travelers and quote snapshot.
Register an external adapter
The adapter is wired at the deployment edge. The framework package stays provider-neutral.adapter.createBooking() first, then snapshots the result. The caller does not branch; the helper does.
Links to other modules and Connect
- bookings. The
booking_cruise_detailsextension and thebooking_group_cruise_detailsparty extension plug into the bookings state machine, PII handling, and financial linkage. - pricing. Dated promotions and onboard-credit overlays reuse
pricing.price_catalogsandpricing.price_schedulesvia soft foreign keys on cruise price rows. There is no cruise-local promotions table. - finance. Bookings become invoices through the finance module; cruise line items live in the booking quote snapshot, not in finance.
- suppliers and Places. Cruise lines are supplier rows linked from
cruises.lineSupplierId; ports are facility (Place) rows referenced by soft foreign key. - products. Pre and post-cruise extensions that are reusable sellable products link through a template-level link rather than a cross-package foreign key.
- Connect. Connect cruises is the default source of external cruise inventory. A thin adapter over the Connect SDK implements the
CruiseAdaptercontract; from the module’s point of view it is just another registered adapter.
React package
@voyant-travel/cruises-react is the cruises client tier: headless data hooks and a Zod-validated fetch client at the root and ./hooks, ./client, and ./query-keys subpaths, plus styled UI under ./ui and ./components/*. The headless subpaths pull no styling peers; the styled subpaths add @voyant-travel/ui.
CruisesUiMessagesProvider and import only the locales you support (for example ./i18n/en and ./i18n/ro) to localize.
Next steps
Connect cruises
How sourced cruise inventory reaches the adapter contract.
Connect adapter
The contract external inventory implements to plug in.
Charters
The sister module for per-suite and whole-yacht voyages.
Glossary
The cruise vocabulary in the shared domain language.