@voyant-travel/catalog) is the plane every sellable thing in Voyant projects into. An operator runs their own tours, resells a wholesaler’s packages, books hotels through a bedbank, and pulls cruises from a cruise line, and all of it has to appear as one normalized catalog in admin search, on the storefront, and in the booking flow. Catalog is the contract and the shared infrastructure that makes that possible without forcing every vertical into one table.
It is deliberately a contract, not a polymorphic root. There is no shared catalog_entries table and no kind discriminator. Each vertical module (inventory, cruises, accommodations, charters, extras) keeps its own schema and operational logic, and adopts a small set of cross-cutting catalog primitives: a field-policy contract, a provenance shape, an overlay store, a booking snapshot table, a search index projection, and a drift event stream. Catalog sits upstream of the commitment chain. It answers “what is sellable, in what shape, projected from where, with which editorial overrides applied”; the bookings module answers “how a specific sale is committed”.
Key concepts
A normalized, sellable discovery and booking record used by admin search, the storefront, the composer, and CMS sync, regardless of provenance. It may resolve to a local Product or to sourced inventory. See Catalog Item.
The derived read model that interleaves Operated Inventory and Sourced Inventory into one search-and-sync surface. The same query returns the operator’s own tours next to a resold Viking sailing; one resolves to a Product, the other to a sourced row, but both share the same card shape, facets, and merchandising.
The uniform
source_kind / source_ref / source_freshness shape carried by every projection. source_kind: "owned" for the operator’s own inventory; "voyant-connect", "direct:tui", "bedbank:hotelbeds", "gds:amadeus", and similar for sourced inventory. Provenance is also the durable callback handle for post-book operations against the upstream.The load-bearing schema decision. Every field on every projection, in every vertical, is declared with a policy row carrying twelve attributes: its class (
managed, structural, merchandisable, volatile-indexed, volatile-live), merge rule, drift severity, reindex scope, snapshot mode, queryability, localization, visibility, edit role, override friction, and source freshness. The registry decides what is overrideable, what gets indexed, what is frozen at booking, and what triggers a reindex.An editorial override keyed by
(entity_module, entity_id, field_path, locale, audience, market). Marketing can rewrite a sourced product’s title, swap its hero image, and add SEO copy without mutating the upstream source. Overlays resolve through a most-specific-to-least-specific fallback chain across the three variant axes.dynamic or scheduled: the mechanic axis that forks the search and booking experience. Dynamic inventory is composed live for the customer’s dates (TUI flight + hotel, bedbanks); it is search-first with a calendar of prices and no local inventory. Scheduled inventory is a seat in a fixed dated departure drawn from a finite allotment (escorted tours, cruises, owned series); it is departures-first. Supply model is explicit and indexed, separate from category (package, tour, cruise, hotel, and so on).The contract any external feed implements to project into the plane:
discover (emit projections), liveResolve (fetch volatile-live values), reserve / cancel (forward booking writes), optional getContent (rich detail content), and optional outbound push methods. Voyant Connect is one adapter among many; a wholesaler, cruise line, or operator can build their own against the same contract.A frozen view captured at booking commit, one
booking_catalog_snapshot row per Catalog Item that participates in the booking. It records what the customer saw, what they paid, and the cancellation terms, even if the upstream source mutates or disappears later.What it owns
Catalog owns the cross-cutting infrastructure, not any vertical’s operational truth.- The field-policy contract (
./contract): theFieldPolicytype, the governance enums, and the inheritance loader. Each vertical writes its own policy file. - The provenance shape (
./provenance) and the durable sourced-entry store that records what is known locally about each sourced row. - The overlay store (
./overlay): the Drizzle schema and the resolver-merge logic with the full locale x audience x market fallback chain. It accepts writes from the admin UI, external CMS plugins, bulk import, and AI copy pipelines, all governed by the same field policy. - The booking snapshot graph (
./snapshot): thebooking_catalog_snapshottable and capture helpers, including structuredpricing_basiscolumns finance and refund engines query. - The search index projection (
./indexer): the engine-agnosticIndexerAdaptercontract and a native Typesense implementation (the v1 default), plus per-(locale, audience, market) document materialization and the admin-vs-storefront denormalization topology. Browse-time price sort and filter is first-class through indexed price summaries, with an optional two-stage live rerank helper (./search/rerank). - The drift event stream (
./drift): typed events emitted when an upstream field changes materially, severity-graded by the policy registry. - The catalog event taxonomy (
./events): visibility-filtered payload builders emitted through@voyant-travel/core/eventsand consumed by the existing webhook pipeline for near-real-time cross-deployment freshness. - The booking engine (
./booking-engine): the vertical-agnosticquoteEntity/bookEntity/cancelEntitylifecycle and theSourceAdapterRegistrythat dispatches bysource_kind.
Working with it
A vertical declares its field policy. Every field it wants to project is a row, so the plane knows how to govern it.supplyModel through the vertical slice; the public route defaults to the customer projection and can return a compact storefront-card shape.
source_kind.
getContent capability: per-vertical, per-locale content caches serve detail pages with stale-while-revalidate refresh, and an operator can curate a ro-RO overlay before the upstream even publishes one. Adapters that do not implement getContent fall through to a synthesizer that builds the most complete blob it legitimately can from the projection, overlay, and plane metadata.
Volatile-live fields (the exact room-level total with taxes and fees, live inventory count) are never indexed. They are always fetched on demand through the adapter at quote and checkout time. The index holds only
volatile-indexed price summaries for browse.Links to other modules
- inventory is the operated-inventory authoring side. It writes its own catalog policy file and projects Products into the plane; catalog never depends on inventory.
- commerce reads the catalog plane: a commercial decision takes a Catalog Item reference and resolves price, availability, and sellability through the matching adapter.
- bookings is where the booking engine lands its rows. The engine reuses bookings as the shared parent so an itinerary can mix owned and sourced lines, and stamps the source pointer and snapshot on each.
- distribution is the outbound inverse: when a booking commits on Voyant, distribution pushes it upstream through the same
SourceAdaptercontract, extended withpushBooking/pushAvailability/pushContent. - finance queries the snapshot’s structured
pricing_basisfor invoicing and refunds.
React package
The@voyant-travel/catalog-react family provides the UI. @voyant-travel/catalog-react/booking-engine backs the quote, draft, hold, and book surfaces, and pairs with @voyant-travel/bookings-react/journey for the customer-facing booking journey. Search, card grids, and merchandising surfaces consume the same HTTP routes the headless package mounts.
Next steps
Inventory
Operated product authoring, availability rules, slots, and allotments that feed the plane.
Commerce
The commercial decision that turns a Catalog Item into a buyable price.
Distribution
Channels, commission rules, channel push, and reconciliation for outbound resale.
Glossary
The shared vocabulary: Product, Catalog Item, Provenance, Channel, and more.