CLI
The package exposes two identical binaries — rivet-ts and the legacy alias rivet-reflect-ts — plus these subpath exports:
@maxanstey-meridian/rivet-ts— authoring types,brand(), handler and runtime types. It loads no other package at runtime, so contracts, handlers and browser-run mocks can import it@maxanstey-meridian/rivet-ts/tooling— the reflection pipeline (lowerContracts, the contract document classes), which loads the TypeScript compiler@maxanstey-meridian/rivet-ts/vite— the Vite plugin (only here: the root does not re-export it, so importing the root needs novite)@maxanstey-meridian/rivet-ts/hono— the Hono server runtime@maxanstey-meridian/rivet-ts/package.json
Usage (from rivet-ts --help):
rivet-ts --entry <path> [--out <file>] [--tsconfig <file>]
rivet-ts scaffold --out <dir> [--name <project-name>] [--no-api] [--force]
rivet-ts scaffold-mock --entry <file> --out <dir> [--name <project-name>] [--tsconfig <file>] [--spec <openapi.json>] [--force]
rivet-ts generate --generated-root <dir>
rivet-ts rivet [--] <args passed to the Rivet binary>--help/-h prints usage; --version prints the package version. After rivet, both are passed to the Rivet binary instead. Unknown flags and valued flags missing their value are loud errors, never silently ignored.
Reflect (bare form)
Reflect a TypeScript contract entry into contract JSON — the internal IR consumed by the Rivet binary and the Hono runtime.
pnpm exec rivet-ts --entry ./contracts.ts --out ./contract.json- If
--outis omitted, JSON is written to stdout. --tsconfignames the TypeScript project file; without it the nearesttsconfig.jsonabove the entry is used, if there is one.--outcreates missing parent directories.- An entry containing zero contracts produces an
ENTRY_NO_CONTRACTSwarning instead of silently emitting an empty document. - Exit code is 1 when any error diagnostic was produced.
rivet-ts scaffold
Scaffold a fresh workspace with a worked example module (quotes):
pnpm exec rivet-ts scaffold --out ./myapp --name myappOptions:
--out: output directory (required)--name: project name; defaults to the output directory's basename--no-api: emit onlyapps/ui+packages/contracts, for repos whose API lives elsewhere--force: scaffold into a non-empty directory (refused otherwise)
The command lowers its own example contract through the real reflection pipeline before emitting, so the bootstrap api.contract.json matches the emitted contracts.ts by construction.
rivet-ts scaffold-mock
Scaffold the same workspace shape from an existing contract entry; api modules return synthesized mock values until you replace them.
pnpm exec rivet-ts scaffold-mock --entry ./contracts.ts --out ./myapp
cd ./myapp
task install
task devOptions:
--entry: TypeScript contract entrypoint (required)--out: output directory (required)--name: optional project name--tsconfig: optional explicit TypeScript project file for reflection--spec: optional generatedopenapi.json; its JSON Schema constraints (minLength/maxLength/pattern, numeric bounds,multipleOf,minItems/maxItems/uniqueItems) are chained onto the emitted Zod schemas. Without it the schemas carry shape only. Reading the file fails loudly if it is missing or not valid JSON--force: scaffold into a non-empty directory (refused otherwise)
Body-carrying endpoints get a Zod schema in apps/api/src/modules/<module>/<module>-validation.ts (exported from the api package as ./validation, user-owned after emission) and a handler wrapper that rejects invalid bodies with 422 { code: "validation_failed" }.
The entry and its local imports are copied into apps/api/src/ preserving their relative layout. A copied file that would collide with a scaffold-emitted file (app.ts, local.ts, main.ts, validation.ts, http-errors.ts) is an error, not a silent overwrite. The api package's #contract import alias points at the copied entry.
rivet-ts generate
Derive schema.d.ts from the OpenAPI spec the Rivet binary wrote:
pnpm exec rivet-ts generate --generated-root ./packages/contracts/generatedThis reads <generated-root>/openapi.json and writes <generated-root>/schema.d.ts (openapi-typescript). It fails loudly when openapi.json is missing. It does not write a client facade — the facade is emitted once at scaffold time into packages/contracts/src/index.ts and is hand-owned afterwards; the generated directory holds exactly openapi.json + schema.d.ts. (A stale pre-facade index.ts left in the generated directory by an older scaffold is removed.)
rivet-ts rivet
Resolve the cached Rivet binary — auto-installing on first use, exactly as the Vite plugin does — and pass the remaining arguments through verbatim:
pnpm exec rivet-ts rivet -- --from ./contract.json --output ./packages/contracts/generatedThe RIVET_VERSION environment variable overrides the pinned default version (the Vite plugin honours it too, below an explicit rivet.version). A release asset without a published sha256 digest is refused. Scaffolded task generate pipelines call this instead of a bare rivet that is never on PATH.
Diagnostics
Diagnostics are written to stderr as severity: [CODE] file:line:col message.
- unsupported constructs produce explicit errors or warnings
- the reflector does not silently widen unsupported TS types into fake JSON shapes
- scaffold generation prefers visible TODO stubs over lying handlers;
scaffold-mockreports each// TODO:in a use case as aSCAFFOLD_*warning naming that file, and still exits 0