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-resource-booking

v1.0.2

Published

Booking/reservation resource with availability checking, lifecycle management, rescheduling, and resource-scoped queries.

Readme

@molecule/api-resource-booking

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.

Booking/reservation resource for molecule.dev.

Provides availability checking, booking creation, lifecycle management (confirm, cancel, complete), rescheduling, and resource-scoped queries.

Quick Start

import { routes, requestHandlerMap } from '@molecule/api-resource-booking'

// Wired by mlcl inject (all routes require authenticate):
// GET    /bookings/availability/:resourceType/:resourceId?date=YYYY-MM-DD[&duration=60]
// POST   /bookings                       — create (starts 'pending', 409 on overlap)
// GET    /bookings                       — the caller's bookings
// GET    /bookings/:id
// POST   /bookings/:id/cancel | /confirm | /complete
// PUT    /bookings/:id/reschedule

Type

resource

Installation

npm install @molecule/api-resource-booking @molecule/api-database @molecule/api-i18n @molecule/api-logger @molecule/api-resource

API

Interfaces

Booking

A booking/reservation.

interface Booking {
  /** Unique booking identifier. */
  id: string
  /** The user who created this booking. */
  userId: string
  /** The type of resource being booked (e.g., 'room', 'appointment'). */
  resourceType: string
  /** The specific resource identifier. */
  resourceId: string
  /** Current booking status. */
  status: BookingStatus
  /** Start time of the booking. */
  startTime: string
  /** End time of the booking. */
  endTime: string
  /** Duration in minutes. */
  duration: number
  /** Optional notes. */
  notes?: string
  /** Arbitrary metadata attached to this booking. */
  metadata?: Record<string, unknown>
  /** Creation timestamp. */
  createdAt: string
  /** Last modification timestamp. */
  updatedAt: string
}

BookingQuery

Options for querying bookings.

interface BookingQuery {
  /** Filter by status. */
  status?: BookingStatus
  /** Return only bookings starting after this date. */
  from?: string
  /** Return only bookings starting before this date. */
  to?: string
  /** Page number (1-based). */
  page?: number
  /** Items per page. */
  limit?: number
}

BookingRow

Internal database row for a booking.

interface BookingRow {
  /** Unique booking identifier. */
  id: string
  /** The user who created this booking. */
  userId: string
  /** The type of resource being booked. */
  resourceType: string
  /** The specific resource identifier. */
  resourceId: string
  /** Current booking status. */
  status: string
  /** Start time of the booking. */
  startTime: string
  /** End time of the booking. */
  endTime: string
  /** Duration in minutes. */
  duration: number
  /** Optional notes. */
  notes: string | null
  /** JSON-serialized metadata. */
  metadata: string | null
  /** Creation timestamp. */
  createdAt: string
  /** Last modification timestamp. */
  updatedAt: string
}

CancelBookingInput

Input for cancelling a booking.

interface CancelBookingInput {
  /** Optional cancellation reason. */
  reason?: string
}

CreateBookingInput

Input for creating a booking.

interface CreateBookingInput {
  /** The type of resource being booked. */
  resourceType: string
  /** The specific resource identifier. */
  resourceId: string
  /** Start time of the booking (ISO 8601). */
  startTime: string
  /** Duration in minutes. */
  duration: number
  /** Optional notes. */
  notes?: string
  /** Arbitrary metadata. */
  metadata?: Record<string, unknown>
}

PaginatedResult

A paginated result set.

interface PaginatedResult<T> {
  /** The page of results. */
  data: T[]
  /** Total number of matching records. */
  total: number
  /** Current page number. */
  page: number
  /** Page size. */
  limit: number
}

RescheduleBookingInput

Input for rescheduling a booking.

interface RescheduleBookingInput {
  /** New start time (ISO 8601). */
  startTime: string
  /** New duration in minutes (optional, keeps existing if omitted). */
  duration?: number
}

TimeSlot

An available time slot.

interface TimeSlot {
  /** Start time of the slot. */
  startTime: string
  /** End time of the slot. */
  endTime: string
  /** Whether the slot is available. */
  available: boolean
}

Types

BookingStatus

Possible statuses for a booking lifecycle.

type BookingStatus = 'pending' | 'confirmed' | 'cancelled' | 'completed' | 'no-show'

Functions

book(req, res)

Creates a new booking for the authenticated user. Validates no conflicting bookings exist for the requested time slot.

function book(req: MoleculeRequest, res: MoleculeResponse): Promise<void>
  • req — The request with {@link CreateBookingInput} body.
  • res — The response object.

cancel(req, res)

Cancels a booking. Only the booking owner can cancel, and only from valid states.

function cancel(req: MoleculeRequest, res: MoleculeResponse): Promise<void>
  • req — The request with params.id and optional {@link CancelBookingInput} body.
  • res — The response object.

checkAvailability(req, res)

Checks availability for a resource on a given date. Returns hourly time slots with availability status based on existing bookings.

function checkAvailability(req: MoleculeRequest, res: MoleculeResponse): Promise<void>
  • req — The request with params.resourceType, params.resourceId, and query date and optional duration.
  • res — The response object.

complete(req, res)

Marks a confirmed booking as completed. Only the booking owner can complete.

function complete(req: MoleculeRequest, res: MoleculeResponse): Promise<void>
  • req — The request with params.id.
  • res — The response object.

computeEndTime(startTime, durationMinutes)

Computes the end time given a start time and duration in minutes.

function computeEndTime(startTime: string, durationMinutes: number): string
  • startTime — ISO 8601 start time string.
  • durationMinutes — Duration in minutes.

Returns: ISO 8601 end time string.

confirm(req, res)

Confirms a pending booking. Only the booking owner can confirm.

function confirm(req: MoleculeRequest, res: MoleculeResponse): Promise<void>
  • req — The request with params.id.
  • res — The response object.

generateTimeSlots(date, durationMinutes, existingBookings)

Generates hourly time slots for a given date and checks availability against existing bookings.

function generateTimeSlots(
  date: string,
  durationMinutes: number,
  existingBookings: BookingRow[],
): TimeSlot[]
  • date — The date to generate slots for (ISO 8601).
  • durationMinutes — Requested duration in minutes (default 60).
  • existingBookings — Already-booked rows for the resource on that day.

Returns: An array of time slots with availability.

getBookings(req, res)

Lists bookings for the authenticated user with optional filtering and pagination.

function getBookings(req: MoleculeRequest, res: MoleculeResponse): Promise<void>
  • req — The request with optional query params for status, from, to, page, limit.
  • res — The response object.

getById(req, res)

Retrieves a single booking by ID. Only the booking owner can access it.

function getById(req: MoleculeRequest, res: MoleculeResponse): Promise<void>
  • req — The request with params.id.
  • res — The response object.

reschedule(req, res)

Reschedules a booking to a new time. Only the booking owner can reschedule, and only pending or confirmed bookings can be rescheduled.

function reschedule(req: MoleculeRequest, res: MoleculeResponse): Promise<void>
  • req — The request with params.id and {@link RescheduleBookingInput} body.
  • res — The response object.

toBooking(row)

Converts a database booking row into a typed {@link Booking}.

function toBooking(row: BookingRow): Booking
  • row — The raw database row.

Returns: The deserialized booking.

Constants

BOOKING_STATUSES

All valid booking statuses.

const BOOKING_STATUSES: readonly BookingStatus[]

i18nRegistered

Whether i18n registration has been attempted. Always true; this module is a placeholder for symmetry with locale-bonded resources.

const i18nRegistered: true

requestHandlerMap

Handler map for the booking resource routes.

const requestHandlerMap: {
  readonly checkAvailability: typeof checkAvailability
  readonly book: typeof book
  readonly getBookings: typeof getBookings
  readonly getById: typeof getById
  readonly cancel: typeof cancel
  readonly reschedule: typeof reschedule
  readonly confirm: typeof confirm
  readonly complete: typeof complete
}

routes

Booking routes. Supports availability checking, CRUD, lifecycle transitions, and resource-scoped listing.

const routes: readonly [
  {
    readonly method: 'get'
    readonly path: '/bookings/availability/:resourceType/:resourceId'
    readonly handler: 'checkAvailability'
    readonly middlewares: readonly ['authenticate']
  },
  {
    readonly method: 'post'
    readonly path: '/bookings'
    readonly handler: 'book'
    readonly middlewares: readonly ['authenticate']
  },
  {
    readonly method: 'get'
    readonly path: '/bookings'
    readonly handler: 'getBookings'
    readonly middlewares: readonly ['authenticate']
  },
  {
    readonly method: 'get'
    readonly path: '/bookings/:id'
    readonly handler: 'getById'
    readonly middlewares: readonly ['authenticate']
  },
  {
    readonly method: 'post'
    readonly path: '/bookings/:id/cancel'
    readonly handler: 'cancel'
    readonly middlewares: readonly ['authenticate']
  },
  {
    readonly method: 'put'
    readonly path: '/bookings/:id/reschedule'
    readonly handler: 'reschedule'
    readonly middlewares: readonly ['authenticate']
  },
  {
    readonly method: 'post'
    readonly path: '/bookings/:id/confirm'
    readonly handler: 'confirm'
    readonly middlewares: readonly ['authenticate']
  },
  {
    readonly method: 'post'
    readonly path: '/bookings/:id/complete'
    readonly handler: 'complete'
    readonly middlewares: readonly ['authenticate']
  },
]

STATUS_TRANSITIONS

Allowed status transitions keyed by current status.

const STATUS_TRANSITIONS: Record<BookingStatus, readonly BookingStatus[]>

Injection Notes

Requirements

Peer dependencies:

  • @molecule/api-database ^1.0.1
  • @molecule/api-i18n ^1.0.1
  • @molecule/api-logger ^1.0.1
  • @molecule/api-resource ^1.0.1

Runtime Dependencies

  • @molecule/api-database

  • @molecule/api-i18n

  • @molecule/api-logger

  • @molecule/api-resource

  • Migration required. src/__setup__/bookings.sql ships with this package and must exist in the target database before use (scaffolded apps apply it automatically; existing apps must apply it first).

  • Bookable resources are polymorphic and NOT verified. resourceType / resourceId are free-form — no FK, no existence or capacity check. Your app decides what is bookable and validates the target in domain code.

  • Every lifecycle action is owner-only. cancel/reschedule/confirm/complete all reject when the booking's userId differs from the session user. There is no staff/operator role: if your app needs a provider to confirm bookings, add your own authorizer + handler — do not loosen the ownership checks.

  • The status machine is enforced (STATUS_TRANSITIONS): pending → confirmed → completed/no-show, with cancel allowed from pending/confirmed. Creation always starts pending; book returns 409 when a non-cancelled booking overlaps the requested slot.

  • Availability requires ?date=YYYY-MM-DD (400 without it) and returns hourly slots based on existing bookings — it is a DISPLAY aid; book re-checks conflicts server-side at creation time.

E2E Tests

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

  • [ ] Booking an AVAILABLE slot succeeds (starts 'pending') and then appears in the user's own booking list (GET /bookings) at the exact start time you picked — not shifted, not missing.
  • [ ] The status lifecycle advances in the UI: pending -> confirm -> confirmed, confirmed -> complete -> completed; cancel works from pending or confirmed and shows the booking as cancelled. A terminal booking (completed / cancelled / no-show) rejects any further confirm/complete/cancel/ reschedule with a visible 409 error — the STATUS_TRANSITIONS machine is enforced, not just the happy path.
  • [ ] DOUBLE-BOOKING is prevented — the core reservation invariant. Book a slot, then try to book the SAME resourceType + resourceId for an overlapping time: the second attempt is rejected ('not available', 409), never silently overlapped, and availability now shows that slot as taken. Cancelling the first booking frees the slot — a cancelled booking must no longer block it.
  • [ ] Rescheduling a pending/confirmed booking moves it to the new time AND frees the old slot (the old time reads available again); rescheduling ONTO a time another active booking already holds is rejected (409), not overlapped.
  • [ ] A past / no-show booking is handled sanely — a completed or no-show booking is terminal (cannot be re-confirmed or rescheduled), and any past/no-show UI the app adds acts only on the owner's own bookings.
  • [ ] TIMEZONE is correct: a slot booked for a given local time shows back at that SAME local time in the list and detail — a booking made for 2pm local must not display as 9am or the next day (the stored instant must round-trip).
  • [ ] AUTHORIZATION — a user sees and acts on ONLY their own bookings: the list is scoped to the session user; loading, cancelling, rescheduling, confirming, or completing another user's booking id returns 403 (no id-guessing into someone else's reservation); the caller cannot book on behalf of another user (the owner is the session, never the request body) or bypass the server-side availability re-check by posting an overlapping slot directly.