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-inventory

v1.0.1

Published

Inventory resource with stock tracking, reservations, low-stock alerts, movement history, and bulk updates.

Downloads

523

Readme

@molecule/api-resource-inventory

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.

Inventory resource for molecule.dev.

Provides stock tracking with reservations, low-stock alerts, movement history, and bulk update support.

Quick Start

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

Type

resource

Installation

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

API

Interfaces

BulkStockAdjustment

Input for a single item in a bulk stock update.

interface BulkStockAdjustment {
  /** The product to adjust. */
  productId: string
  /** Optional variant identifier. */
  variantId?: string
  /** Quantity to adjust. */
  quantity: number
  /** Type of adjustment. */
  type: StockAdjustmentType
  /** Reason for the adjustment. */
  reason?: string
}

BulkUpdateItemResult

Result of a single item in a bulk update.

interface BulkUpdateItemResult {
  /** The product that was adjusted. */
  productId: string
  /** Optional variant identifier. */
  variantId?: string
  /** Whether this individual adjustment succeeded. */
  success: boolean
  /** Error message if the adjustment failed. */
  error?: string
  /** Updated stock info if successful. */
  stock?: StockInfo
}

BulkUpdateResult

Result of a bulk stock update operation.

interface BulkUpdateResult {
  /** Total number of adjustments attempted. */
  total: number
  /** Number of successful adjustments. */
  succeeded: number
  /** Number of failed adjustments. */
  failed: number
  /** Per-item results. */
  results: BulkUpdateItemResult[]
}

InventorySession

Structural view of the fields on res.locals.session this resource inspects to decide authorization. All fields are optional — a standard molecule session carries only userId; apps that model roles may also set isAdmin/role/ roles/permissions claims, which are honored here.

interface InventorySession {
  /** Authenticated user id (set by the global auth middleware). */
  userId?: string
  /** Optional boolean admin claim. */
  isAdmin?: boolean
  /** Optional single-role claim. */
  role?: string
  /** Optional multi-role claim. */
  roles?: string[]
  /** Optional permission strings claim. */
  permissions?: string[]
}

LowStockAlert

A low-stock alert entry.

interface LowStockAlert {
  /** The product that is low on stock. */
  productId: string
  /** Optional variant identifier. */
  variantId?: string
  /** Current available quantity. */
  available: number
  /** The low-stock threshold. */
  threshold: number
}

PaginatedResult

A paginated result set.

interface PaginatedResult<T> {
  /** The items in this page. */
  data: T[]
  /** Total number of items matching the query. */
  total: number
  /** Current page number (1-based). */
  page: number
  /** Number of items per page. */
  limit: number
}

PaginationOptions

Pagination options for list queries.

interface PaginationOptions {
  /** Page number (1-based). */
  page?: number
  /** Number of items per page. */
  limit?: number
}

Reservation

A stock reservation tied to an order.

interface Reservation {
  /** Unique reservation identifier. */
  id: string
  /** The product being reserved. */
  productId: string
  /** Optional variant identifier. */
  variantId?: string
  /** Quantity reserved. */
  quantity: number
  /** The order this reservation belongs to. */
  orderId: string
  /** The authenticated user who created this reservation, if known. */
  userId?: string
  /** When the reservation was created. */
  createdAt: string
}

ReservationRow

Internal database row for a stock reservation.

interface ReservationRow {
  /** Unique reservation identifier. */
  id: string
  /** The product being reserved. */
  productId: string
  /** Optional variant identifier. */
  variantId: string | null
  /** Quantity reserved. */
  quantity: number
  /** The order this reservation belongs to. */
  orderId: string
  /** The authenticated user who created this reservation (null for legacy rows). */
  userId: string | null
  /** Creation timestamp. */
  createdAt: string
}

ReserveStockInput

Input for creating a stock reservation.

interface ReserveStockInput {
  /** Quantity to reserve. */
  quantity: number
  /** The order to associate this reservation with. */
  orderId: string
}

StockAdjustment

Input for adjusting stock levels.

interface StockAdjustment {
  /** Optional variant identifier. */
  variantId?: string
  /** Quantity to adjust by (or absolute value for 'set'). */
  quantity: number
  /** Type of adjustment: add, remove, or set to absolute value. */
  type: StockAdjustmentType
  /** Human-readable reason for the adjustment. */
  reason?: string
}

StockInfo

Current stock information for a product or variant.

interface StockInfo {
  /** The product this stock belongs to. */
  productId: string
  /** Optional variant identifier (size, color, etc.). */
  variantId?: string
  /** Quantity available for purchase (total − reserved). */
  available: number
  /** Quantity currently reserved by pending orders. */
  reserved: number
  /** Total quantity in stock (available + reserved). */
  total: number
  /** Threshold below which the product is considered low-stock. */
  lowStockThreshold: number
  /** Whether current available stock is at or below the threshold. */
  isLowStock: boolean
}

StockMovement

A record of a stock movement (adjustment, reservation, release, or confirmation).

interface StockMovement {
  /** Unique movement identifier. */
  id: string
  /** The product affected. */
  productId: string
  /** Optional variant identifier. */
  variantId?: string
  /** Type of movement. */
  type: StockMovementType
  /** Quantity change (positive for additions, negative for removals). */
  quantity: number
  /** Reason or description for this movement. */
  reason?: string
  /** Associated order or reservation identifier. */
  referenceId?: string
  /** When this movement occurred. */
  createdAt: string
}

StockMovementRow

Internal database row for a stock movement.

interface StockMovementRow {
  /** Unique movement identifier. */
  id: string
  /** The product affected. */
  productId: string
  /** Optional variant identifier. */
  variantId: string | null
  /** Type of movement. */
  type: StockMovementType
  /** Quantity change. */
  quantity: number
  /** Reason for this movement. */
  reason: string | null
  /** Associated reference identifier. */
  referenceId: string | null
  /** Creation timestamp. */
  createdAt: string
}

StockRow

Internal database row for an inventory stock record.

interface StockRow {
  /** Unique stock record identifier. */
  id: string
  /** The product this stock belongs to. */
  productId: string
  /** Optional variant identifier. */
  variantId: string | null
  /** Total quantity in stock. */
  total: number
  /** Quantity currently reserved. */
  reserved: number
  /** Low-stock threshold. */
  lowStockThreshold: number
  /** Creation timestamp. */
  createdAt: string
  /** Last modification timestamp. */
  updatedAt: string
}

Types

StockAdjustmentType

Types of stock adjustment operations.

type StockAdjustmentType = 'add' | 'remove' | 'set'

StockMovementType

Types of stock movements recorded in the movement history.

type StockMovementType = 'adjustment' | 'reservation' | 'release' | 'confirmation'

Functions

assertInventoryAdmin(res)

In-handler admin guard for the admin-only inventory mutations. Writes the appropriate JSON error and returns false when the caller is not an authorized admin — 401 when unauthenticated, 403 when authenticated but not an admin. Returns true (and writes nothing) when the caller is an admin.

Call this at the top of every admin handler so protection holds independently of the route middleware (defense-in-depth, fail-closed).

function assertInventoryAdmin(res: MoleculeResponse): boolean
  • res — The response, whose locals.session is inspected and onto which an error is written when access is denied.

Returns: true when the caller is an authorized admin, otherwise false.

assertReservationActor(res, reservationUserId)

In-handler guard for reservation-lifecycle mutations (release, confirm). A reservation is owned by the user who created it; only that owner — or an inventory admin — may release or confirm it. Writes the appropriate JSON error and returns false when access is denied (401 when unauthenticated, 403 when authenticated but neither the owner nor an admin); returns true (writing nothing) when the caller may act.

Fail-closed: a reservation with no recorded owner (null — e.g. a legacy row created before ownership binding) is accessible only to admins.

function assertReservationActor(res: MoleculeResponse, reservationUserId: string | null): boolean
  • res — The response, whose locals.session is inspected and onto which an error is written when access is denied.
  • reservationUserId — The userId recorded on the reservation, or null.

Returns: true when the caller is the owner or an admin, otherwise false.

bulkUpdate(req, res)

Processes multiple stock adjustments in a single request.

function bulkUpdate(req: MoleculeRequest, res: MoleculeResponse): Promise<void>
  • req — The request with adjustments array body.
  • res — The response object.

confirm(req, res)

Confirms a reservation, permanently removing the reserved quantity from total stock.

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

getAlerts(req, res)

Returns all products whose available stock is at or below their low-stock threshold. Accepts an optional threshold query parameter to override the per-product threshold.

function getAlerts(req: MoleculeRequest, res: MoleculeResponse): Promise<void>
  • req — The request with optional threshold query parameter.
  • res — The response object.

getInventorySession(res)

Reads the structural session off res.locals.

function getInventorySession(res: MoleculeResponse): InventorySession | undefined
  • res — The response whose locals.session is inspected.

Returns: The session, or undefined when unauthenticated.

getMovements(req, res)

Returns paginated stock movement history for the given product.

function getMovements(req: MoleculeRequest, res: MoleculeResponse): Promise<void>
  • req — The request with productId param and optional pagination query params.
  • res — The response object.

getStock(req, res)

Returns stock information for the given product (and optional variant).

function getStock(req: MoleculeRequest, res: MoleculeResponse): Promise<void>
  • req — The request with productId param and optional variantId query.
  • res — The response object.

isInventoryAdmin(res)

Resolves whether the current request's session belongs to an actor authorized to administer inventory (rewrite/bulk-update stock, release/confirm any reservation). Fail-closed: returns false when there is no authenticated session, and otherwise only true when the session carries an admin claim — isAdmin === true, role === 'admin', roles containing 'admin', or permissions containing 'admin' / 'inventory:manage'.

Use this for in-handler defense-in-depth (it does not depend on the route middleware being preserved by the injector).

function isInventoryAdmin(res: MoleculeResponse): boolean
  • res — The response whose locals.session is inspected.

Returns: true when the session is an authorized inventory admin.

release(req, res)

Releases a stock reservation, returning the reserved quantity to available stock.

function release(req: MoleculeRequest, res: MoleculeResponse): Promise<void>
  • req — The request with reservationId param.
  • res — The response object.

requireInventoryAdmin()

Route middleware that gates the admin-only inventory routes (updateStock, bulkUpdate). Calls next() only for an authenticated admin; otherwise forwards an error to the framework error handler — Unauthorized when no session is present, Forbidden when the session is authenticated but not an admin.

Exposed as a requestHandlerMap key so the injector's route scanner keeps it (unlike the inert global 'authenticate' string, which is dropped).

function requireInventoryAdmin(): MoleculeRequestHandler

Returns: An Express-compatible middleware function.

reserve(req, res)

Reserves stock for the given product and order.

function reserve(req: MoleculeRequest, res: MoleculeResponse): Promise<void>
  • req — The request with productId param and {@link ReserveStockInput} body.
  • res — The response object.

toLowStockAlert(row)

Converts a database stock row into a typed {@link LowStockAlert}.

function toLowStockAlert(row: StockRow): LowStockAlert
  • row — The raw database row.

Returns: The low-stock alert.

toReservation(row)

Converts a database reservation row into a typed {@link Reservation}.

function toReservation(row: ReservationRow): Reservation
  • row — The raw database row.

Returns: The deserialized reservation.

toStockInfo(row)

Converts a database stock row into a typed {@link StockInfo}.

function toStockInfo(row: StockRow): StockInfo
  • row — The raw database row.

Returns: The deserialized stock info.

toStockMovement(row)

Converts a database stock movement row into a typed {@link StockMovement}.

function toStockMovement(row: StockMovementRow): StockMovement
  • row — The raw database row.

Returns: The deserialized stock movement.

updateStock(req, res)

Updates stock for the given product. Creates the stock record if it doesn't exist.

function updateStock(req: MoleculeRequest, res: MoleculeResponse): Promise<void>
  • req — The request with productId param and {@link StockAdjustment} body.
  • res — The response object.

Constants

i18nRegistered

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

const i18nRegistered: true

INVENTORY_ADMIN_PERMISSION

Session-claim permission string ('inventory:manage') that, when present in a session's permissions array, grants inventory administration.

const INVENTORY_ADMIN_PERMISSION: 'inventory:manage'

INVENTORY_PERMISSION_ACTION

Permission action describing inventory administration, e.g. for an app's own @molecule/api-permissions wiring.

const INVENTORY_PERMISSION_ACTION: 'manage'

INVENTORY_PERMISSION_RESOURCE

Permission resource describing inventory administration, e.g. for an app's own @molecule/api-permissions wiring.

const INVENTORY_PERMISSION_RESOURCE: 'inventory'

requestHandlerMap

Handler map for the inventory resource routes.

requireInventoryAdmin is the admin authorizer middleware referenced by the updateStock/bulkUpdate routes. It must live here (as a real handler-map key) so the mlcl injector's route scanner preserves it — a bare middleware string that isn't a handler-map key is silently dropped, which is exactly how the previous bare 'authenticate' gate became inert.

const requestHandlerMap: {
  readonly getStock: typeof getStock
  readonly updateStock: typeof updateStock
  readonly reserve: typeof reserve
  readonly release: typeof release
  readonly confirm: typeof confirm
  readonly getAlerts: typeof getAlerts
  readonly getMovements: typeof getMovements
  readonly bulkUpdate: typeof bulkUpdate
  readonly requireInventoryAdmin: MoleculeRequestHandler
}

routes

Inventory resource routes.

All routes require authentication. The destructive admin-side mutations (updateStock, bulkUpdate) are additionally gated by the requireInventoryAdmin middleware — a real requestHandlerMap key (see {@link requireInventoryAdmin}) so the injector preserves it; the previously declared bare 'authenticate' string was silently dropped by the route scanner, leaving stock open to rewrite by any authenticated user. Each admin/reservation handler additionally re-checks authorization internally, so the gate holds even if a consumer wires the routes without these middlewares.

const routes: readonly [
  {
    readonly method: 'get'
    readonly path: '/inventory/:productId'
    readonly handler: 'getStock'
    readonly middlewares: readonly ['authenticate']
  },
  {
    readonly method: 'put'
    readonly path: '/inventory/:productId'
    readonly handler: 'updateStock'
    readonly middlewares: readonly ['requireInventoryAdmin']
  },
  {
    readonly method: 'post'
    readonly path: '/inventory/:productId/reserve'
    readonly handler: 'reserve'
    readonly middlewares: readonly ['authenticate']
  },
  {
    readonly method: 'delete'
    readonly path: '/inventory/reservations/:reservationId'
    readonly handler: 'release'
    readonly middlewares: readonly ['authenticate']
  },
  {
    readonly method: 'post'
    readonly path: '/inventory/reservations/:reservationId/confirm'
    readonly handler: 'confirm'
    readonly middlewares: readonly ['authenticate']
  },
  {
    readonly method: 'get'
    readonly path: '/inventory/alerts'
    readonly handler: 'getAlerts'
    readonly middlewares: readonly ['authenticate']
  },
  {
    readonly method: 'get'
    readonly path: '/inventory/:productId/movements'
    readonly handler: 'getMovements'
    readonly middlewares: readonly ['authenticate']
  },
  {
    readonly method: 'post'
    readonly path: '/inventory/bulk'
    readonly handler: 'bulkUpdate'
    readonly middlewares: readonly ['requireInventoryAdmin']
  },
]

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

Tables: src/__setup__/inventory.sql creates inventory_stock, inventory_reservations, and inventory_movements. An mlcl-scaffolded API replays __setup__/*.sql automatically on migrate; anywhere else run it once — nothing at runtime creates them.

Stock rows are keyed by productId — SHARED app-wide state, not per-user rows. Writing stock (PUT /inventory/:productId) and POST /inventory/bulk are role-gated and DENY BY DEFAULT (admin session claim or an @molecule/api-permissions grant), enforced both as the requireInventoryAdmin route middleware and inside the handlers (fail-closed). Out of the box no one can mutate stock — grant the role first; never "fix" the 403 by removing the gate.

Reservation flow: POST /inventory/:productId/reserve holds quantity → POST /inventory/reservations/:id/confirm deducts it, DELETE /inventory/reservations/:id releases the hold. All handlers read the authenticated user from res.locals.session (mount behind your global auth middleware; 401 otherwise).

E2E Tests

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

  • [ ] Setting stock (PUT /inventory/:productId, type:'set') then reloading GET /inventory/:productId shows the exact total/available you set — it persisted, not just optimistic UI. available = total - reserved.
  • [ ] type:'add' N raises total by exactly N and type:'remove' N lowers it by exactly N — verify the arithmetic on the specific productId/variantId; variant stock is tracked independently, so adjusting one variant must not move another.
  • [ ] Stock never goes negative: removing or setting below the currently reserved quantity is rejected with a visible 409 error (inventory.error.insufficientStock) and the stored total is unchanged — never persisted as a negative; reserving more than available is likewise rejected (409 insufficientAvailable).
  • [ ] Low-stock crossing flags the item: when available falls to or below lowStockThreshold (default 10) it reads isLowStock:true and appears in GET /inventory/alerts; raising stock back above the threshold clears it.
  • [ ] Every mutation appends an inventory_movements row shown in GET /inventory/:productId/movements with the signed delta (+N/-N), type (adjustment/reservation/confirmation), timestamp, and referenceId (orderId/reservationId); the acting user is recorded on the reservation. The movement log must reconstruct the current total.
  • [ ] Concurrency: two reservations or removals fired at once that together exceed available don't double-spend the last unit — exactly one succeeds, and final total/reserved stay consistent (available never goes negative).
  • [ ] AUTHORIZATION — stock mutation is admin-only and denies by default: PUT /inventory/:productId and POST /inventory/bulk return 403 for a normal signed-in user (no isAdmin/role:'admin'/roles/permissions claim) and 401 when signed out; only an admin session can change stock. A customer cannot mutate the shared catalog stock through any endpoint, and one user cannot release/confirm another user's reservation (403 reservationForbidden).