npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@molecule/api-travel

v1.0.1

Published

Travel trip-planning aggregator (flights + hotels + cars + activities) core interface for molecule.dev

Downloads

265

Readme

@molecule/api-travel

Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit src/index.ts JSDoc, not this file.

Provider-agnostic travel trip-planning interface for molecule.dev.

Defines the {@link TravelProvider} interface for aggregate trip planning across flights, hotels, cars and activities. Bond packages (Amadeus, Travelport, Sabre, etc.) implement this interface. Application code uses the convenience functions (searchTripOptions, searchActivities, searchCars) which delegate to the bonded provider.

Quick Start

import { setProvider, searchTripOptions } from '@molecule/api-travel'
import { provider as amadeus } from '@molecule/api-travel-amadeus'

setProvider(amadeus)
const trip = await searchTripOptions({
  origin: 'JFK',
  destination: 'PAR',
  departureDate: '2026-07-15',
  returnDate: '2026-07-22',
  travelers: { adults: 2 },
  includeFlights: true,
  includeHotels: true,
})
console.log(trip.flights.length, trip.hotels.length)

Type

core

Installation

npm install @molecule/api-travel @molecule/api-bond @molecule/api-i18n

API

Interfaces

ActivityOffer

Normalized activity / experience offer (e.g. a museum tour, food walk, day trip) surfaced inside a {@link TripSearchResult}.

interface ActivityOffer {
  /**
   * Provider-specific opaque offer / activity identifier.
   */
  id: OfferId

  /**
   * Human-readable activity name (e.g. `'Eiffel Tower skip-the-line'`).
   */
  name: string

  /**
   * Free-form short description, if the provider exposes one.
   */
  description?: string

  /**
   * Per-person or per-booking price (provider-defined). Treat as the
   * starting "from" price unless the provider documents otherwise.
   */
  price: MoneyAmount

  /**
   * Geographic point the activity takes place at, when known.
   */
  location?: GeoLocation

  /**
   * URL of a representative image, if the provider exposes one.
   */
  pictureUrl?: string

  /**
   * URL of the provider's booking / detail page, if exposed.
   */
  bookingUrl?: string

  /**
   * Provider-supplied minimum duration string (e.g. `'PT2H'`), if any.
   */
  minimumDuration?: IsoDuration
}

CarOffer

Normalized car-rental offer surfaced inside a {@link TripSearchResult}.

Most major travel aggregators expose car offers as a separate vertical; the trip facade includes them so itinerary planners can present a unified "ground transport" line item alongside flights and hotels.

interface CarOffer {
  /**
   * Provider-specific opaque offer identifier.
   */
  id: OfferId

  /**
   * Vendor / supplier name (e.g. `'Hertz'`, `'Avis'`).
   */
  vendor: string

  /**
   * Free-form vehicle / category description (e.g.
   * `'Compact SUV or similar'`).
   */
  vehicleDescription: string

  /**
   * Total price for the entire rental.
   */
  price: MoneyAmount

  /**
   * Pickup location (IATA airport / city code or free-form locality).
   */
  pickupLocation: LocationCode

  /**
   * Pickup date / time.
   */
  pickupAt: IsoDateTime

  /**
   * Return date / time.
   */
  returnAt: IsoDateTime

  /**
   * Whether unlimited mileage is included. `undefined` when unknown.
   */
  unlimitedMileage?: boolean
}

FlightOffer

Normalized flight offer surfaced inside a {@link TripSearchResult}.

This is a minimal, travel-core-local shape — not the same TypeScript type as @molecule/api-flights's FlightOffer. Providers that wrap the flights core MAY convert between the two structurally.

interface FlightOffer {
  /**
   * Provider-specific opaque offer identifier.
   */
  id: OfferId

  /**
   * Total grand-total price for ALL travelers.
   */
  price: MoneyAmount

  /**
   * Flight segments in chronological order. For round-trip itineraries
   * outbound segments precede return segments.
   */
  segments: FlightSegment[]

  /**
   * Total elapsed time across all segments (including layovers).
   */
  duration: IsoDuration
}

FlightSegment

One leg of a flight offer — a single take-off / landing pair on a single operated flight.

interface FlightSegment {
  /**
   * Departure airport / instant.
   */
  departure: FlightSegmentEndpoint

  /**
   * Arrival airport / instant.
   */
  arrival: FlightSegmentEndpoint

  /**
   * Marketing carrier IATA code (e.g. `'AA'`, `'BA'`).
   */
  carrier: string

  /**
   * Marketing flight number (e.g. `'100'`, `'1234'`).
   */
  flightNumber: string

  /**
   * Block / total time the segment is in the air. `null` when the
   * upstream does not supply per-segment duration.
   */
  duration?: IsoDuration | null
}

FlightSegmentEndpoint

Departure or arrival point on a {@link FlightSegment}.

interface FlightSegmentEndpoint {
  /**
   * IATA airport code (e.g. `'JFK'`).
   */
  airport: LocationCode

  /**
   * Local-time instant including timezone offset.
   */
  at: IsoDateTime

  /**
   * Terminal designator (e.g. `'4'`, `'2A'`). `null` when not supplied.
   */
  terminal?: string | null
}

GeoLocation

Geographic point used for radius-based search of hotels, activities or car-rental locations.

interface GeoLocation {
  /**
   * Latitude in decimal degrees, WGS-84.
   */
  lat: number

  /**
   * Longitude in decimal degrees, WGS-84.
   */
  lon: number

  /**
   * Search radius around the point. Units are provider-defined but
   * SHOULD default to kilometres if not otherwise specified by the
   * provider.
   */
  radius?: number
}

HotelOffer

Normalized hotel offer surfaced inside a {@link TripSearchResult}.

interface HotelOffer {
  /**
   * Provider-specific opaque offer identifier (room / rate
   * combination).
   */
  id: OfferId

  /**
   * Provider-specific hotel identifier the offer belongs to.
   */
  hotelId: string

  /**
   * Human-readable hotel name (e.g. `'Hotel de Paris'`).
   */
  name: string

  /**
   * Total price for the entire stay (all nights).
   */
  price: MoneyAmount

  /**
   * Check-in date (ISO 8601 calendar date).
   */
  checkInDate: IsoDate

  /**
   * Check-out date (ISO 8601 calendar date).
   */
  checkOutDate: IsoDate

  /**
   * Star rating as an integer (1..5), if the provider exposes it.
   */
  rating?: number

  /**
   * Free-form room name / type description, if supplied.
   */
  roomDescription?: string

  /**
   * Cancellation / refundability hint. `true` = explicitly refundable,
   * `false` = explicitly non-refundable, `undefined` = unknown.
   */
  refundable?: boolean
}

MoneyAmount

Monetary price block. Always carries an explicit currency.

interface MoneyAmount {
  /**
   * Total amount in major units of {@link currency} (e.g. dollars,
   * not cents).
   */
  total: number

  /**
   * ISO 4217 currency code the {@link total} is denominated in.
   */
  currency: CurrencyCode
}

SearchActivitiesOptions

Search criteria for {@link TravelProvider.searchActivities}.

Activities are typically scoped to a destination + date range and filtered by the provider's own catalogue. Most providers expose latitude / longitude search rather than IATA codes, so callers can supply either form.

interface SearchActivitiesOptions {
  /**
   * Destination — either an IATA airport / city code or a geographic
   * point with optional radius. Providers SHOULD prefer
   * {@link GeoLocation} when both are present.
   */
  destination: LocationCode | GeoLocation

  /**
   * Date range as `[start, end]` ISO calendar dates. Providers MAY
   * ignore the range and return their full catalogue if the upstream
   * does not support date-filtered availability.
   */
  dates?: { start: IsoDate; end: IsoDate }

  /**
   * Maximum number of offers to return.
   */
  maxResults?: number
}

SearchCarsOptions

Search criteria for {@link TravelProvider.searchCars}.

interface SearchCarsOptions {
  /**
   * IATA airport / city code or free-form locality string for the
   * pickup location.
   */
  pickupLocation: LocationCode

  /**
   * Date / time the car is collected.
   */
  pickupDate: IsoDate | IsoDateTime

  /**
   * Date / time the car is returned.
   */
  returnDate: IsoDate | IsoDateTime

  /**
   * Optional alternate dropoff location. Defaults to
   * {@link pickupLocation} when omitted.
   */
  dropoffLocation?: LocationCode

  /**
   * Maximum number of offers to return.
   */
  maxResults?: number
}

SearchTripOptions

Search criteria for {@link TravelProvider.searchTripOptions}.

The criteria are deliberately broad: travelers typically want to see flights + hotels + cars + activities all at once when planning a trip, so the same date / origin / destination apply to each. Per- vertical filtering (cabin, hotel rating, etc.) is left to follow-up calls against the per-vertical cores.

interface SearchTripOptions {
  /**
   * Origin IATA airport / city code (e.g. `'JFK'`, `'NYC'`). Used for
   * the flight portion of the trip.
   */
  origin: LocationCode

  /**
   * Destination IATA airport / city code. Used for the flight portion
   * of the trip and as the catalogue lookup for hotels / activities
   * when the provider supports it.
   */
  destination: LocationCode

  /**
   * Outbound departure date (ISO 8601 calendar date). Also serves as
   * the hotel check-in date.
   */
  departureDate: IsoDate

  /**
   * Return date for round-trip searches. Also serves as the hotel
   * check-out date when supplied. Omit for one-way / open-ended trips.
   */
  returnDate?: IsoDate

  /**
   * Traveler-count breakdown. Defaults to a single adult when omitted.
   */
  travelers?: TravelerCounts

  /**
   * Whether to include flight offers in the result. Defaults to
   * `true`.
   */
  includeFlights?: boolean

  /**
   * Whether to include hotel offers in the result. Defaults to
   * `true`.
   */
  includeHotels?: boolean

  /**
   * Whether to include car-rental offers in the result. Defaults to
   * `false` (most providers do not expose a car-rental API; opt in
   * explicitly when you know yours does).
   */
  includeCars?: boolean

  /**
   * Whether to include activity offers in the result. Defaults to
   * `false` for the same reason as {@link includeCars}.
   */
  includeActivities?: boolean

  /**
   * Maximum number of offers per vertical. Implementations MAY clamp
   * this to whatever upper bound their upstream API enforces.
   */
  maxResultsPerCategory?: number
}

TravelerCounts

Traveler-count breakdown supplied to {@link TravelProvider.searchTripOptions}.

interface TravelerCounts {
  /**
   * Adult travelers (>=12 years). Defaults to `1` when omitted.
   */
  adults?: number

  /**
   * Child travelers (2-11 years). Defaults to `0` when omitted.
   */
  children?: number

  /**
   * Infant travelers (<2 years). Defaults to `0` when omitted.
   */
  infants?: number
}

TravelProvider

Travel trip-planning provider interface.

All travel providers (Amadeus, Travelport, Sabre, fixtures, etc.) implement this interface. The interface is deliberately minimal and aggregates across the per-vertical cores (@molecule/api-flights, @molecule/api-hotels) so callers building "search a trip" itinerary UIs can issue a single call and render mixed results.

Providers that lack one of the vertical APIs (e.g. Amadeus does not expose a public cars API as of v22) MUST return an empty array for that vertical rather than throwing — the absence is data, not an error.

interface TravelProvider {
  /**
   * Searches for trip options matching the supplied itinerary. Returns
   * an aggregated {@link TripSearchResult} containing flights,
   * hotels, cars and activities (each opt-in via the corresponding
   * `include*` flag).
   *
   * @param options - Trip search criteria.
   * @returns Aggregated trip search result.
   */
  searchTripOptions(options: SearchTripOptions): Promise<TripSearchResult>

  /**
   * Searches for activity / experience offers at a destination.
   *
   * @param options - Activity search criteria.
   * @returns Array of normalized activity offers, possibly empty.
   */
  searchActivities(options: SearchActivitiesOptions): Promise<ActivityOffer[]>

  /**
   * Searches for car-rental offers.
   *
   * Providers without a car-rental API MUST return an empty array
   * rather than throwing.
   *
   * @param options - Car-rental search criteria.
   * @returns Array of normalized car-rental offers, possibly empty.
   */
  searchCars(options: SearchCarsOptions): Promise<CarOffer[]>
}

TripSearchResult

Aggregated trip-search result returned by {@link TravelProvider.searchTripOptions}.

Each per-vertical array is empty (NOT undefined) when the caller did not opt in to that vertical or when the provider returned no offers — this lets consumers iterate without conditional access checks.

interface TripSearchResult {
  /**
   * Flight offers matching the trip search. Empty when
   * {@link SearchTripOptions.includeFlights} is `false` or the
   * provider returned none.
   */
  flights: FlightOffer[]

  /**
   * Hotel offers matching the trip search. Empty when
   * {@link SearchTripOptions.includeHotels} is `false` or the
   * provider returned none.
   */
  hotels: HotelOffer[]

  /**
   * Car-rental offers matching the trip search. Empty when
   * {@link SearchTripOptions.includeCars} is `false` or the provider
   * does not expose a car-rental API.
   */
  cars: CarOffer[]

  /**
   * Activity offers matching the trip search. Empty when
   * {@link SearchTripOptions.includeActivities} is `false` or the
   * provider returned none.
   */
  activities: ActivityOffer[]
}

Types

CurrencyCode

ISO 4217 three-letter currency code (e.g. 'USD', 'EUR', 'JPY').

type CurrencyCode = string

IsoDate

ISO 8601 calendar date (e.g. '2026-07-15'). Time-of-day MUST NOT be included — this is a date for searching availability, not a timestamp.

type IsoDate = string

IsoDateTime

ISO 8601 instant including timezone offset (e.g. '2026-07-15T08:30:00+02:00').

type IsoDateTime = string

IsoDuration

ISO 8601 duration string (e.g. 'PT2H30M', 'PT11H45M'). Plain string for the same reason as {@link IsoDate} — providers differ on whether they expose seconds-precision, fractional minutes, etc.

type IsoDuration = string

LocationCode

IATA airport / city / metropolitan code (e.g. 'JFK', 'NYC', 'PAR'). Plain string alias — providers map onto whatever upstream catalogue they expose.

type LocationCode = string

OfferId

Provider-specific opaque offer identifier. Identifier scheme is provider-defined and MUST NOT be parsed by consumers.

type OfferId = string

Functions

getProvider()

Retrieves the bonded travel provider, throwing if none is configured.

function getProvider(): TravelProvider

Returns: The bonded travel provider.

hasProvider()

Checks whether a travel provider is currently bonded.

function hasProvider(): boolean

Returns: true if a travel provider is bonded.

searchActivities(options)

Searches for activity / experience offers at a destination using the bonded provider.

function searchActivities(options: SearchActivitiesOptions): Promise<ActivityOffer[]>
  • options — Activity search criteria.

Returns: Array of normalized activity offers, possibly empty.

searchCars(options)

Searches for car-rental offers using the bonded provider.

Providers without a car-rental API return an empty array rather than throwing.

function searchCars(options: SearchCarsOptions): Promise<CarOffer[]>
  • options — Car-rental search criteria.

Returns: Array of normalized car-rental offers, possibly empty.

searchTripOptions(options)

Searches for trip options (flights + hotels + cars + activities) using the bonded provider.

function searchTripOptions(options: SearchTripOptions): Promise<TripSearchResult>
  • options — Trip search criteria.

Returns: Aggregated trip search result.

setProvider(provider)

Registers a travel provider as the active singleton. Called by bond packages (e.g. @molecule/api-travel-amadeus) during application startup.

function setProvider(provider: TravelProvider): void
  • provider — The travel provider implementation to bond.

Available Providers

| Provider | Package | | -------- | ------------------------------ | | Amadeus | @molecule/api-travel-amadeus |

Injection Notes

Requirements

Peer dependencies:

  • @molecule/api-bond ^1.0.1
  • @molecule/api-i18n ^1.0.1

Runtime Dependencies

  • @molecule/api-bond
  • @molecule/api-i18n

E2E Tests

Integration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual trip-planning / results screens, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip:

  • [ ] A real trip search (searchTripOptions with a real origin -> destination, a departureDate weeks out, and returnDate for a round trip — e.g. JFK -> PAR) returns REAL results rendered in the UI: each opted-in vertical (includeFlights / includeHotels / includeCars / includeActivities) shows populated flights / hotels / cars / activities, each an actual offer — never an empty list, a stuck spinner, or a placeholder row presented as a successful search.
  • [ ] Results match the query: each FlightOffer's first segments[].departure.airport is the searched origin and its last arrival.airport the destination, on the requested dates; each HotelOffer is at the destination with checkInDate / checkOutDate equal to the searched departure / return dates — not random routes, cities, or dates.
  • [ ] Each vertical is opt-in and additive: toggling includeFlights, includeHotels, includeCars, or includeActivities adds ONLY that section, and a vertical not requested (or one the provider can't serve, e.g. cars) comes back as an EMPTY array — rendered as "none", never as a failed or blank search. maxResultsPerCategory actually caps how many offers each section shows. Any separate activities / cars screen calls searchActivities / searchCars and renders its own ActivityOffer[] / CarOffer[].
  • [ ] Every offer's price.total shows its ISO 4217 price.currency (formatted with it — never a hardcoded $) and is sane: a flight total is the grand total for ALL travelers, a hotel total covers the whole stay, a car total the whole rental — positive and the right order of magnitude.
  • [ ] An impossible or invalid search — a bad IATA code, a past departureDate, a returnDate before it, or a route with no availability — shows a clear per-section "no results" / "invalid" state, not a crash and not a blank list presented as a successful search.
  • [ ] A provider outage or rate-limit (HTTP 429) surfaces as a graceful, retryable error in the UI — not a hung spinner or a silent empty list.
  • [ ] This contract is search ONLY (there is no book / reserve call). Booking goes OUT-OF-BAND to the vendor (an ActivityOffer.bookingUrl or the app's own checkout) — verify the app RECORDS the SELECTED offer (its priced total + itinerary), since opaque OfferIds are short-lived and can't be replayed later.
  • [ ] The provider API key stays server-side: every search runs through the API bond (the package is SERVER-ONLY), no key or upstream credential reaches the browser, and the endpoint validates origin / destination / dates instead of forwarding arbitrary caller input upstream (an open proxy would leak quota or enable SSRF).