spac
TypeScript DSL for authoring OpenAPI 3.1+ specs. You write TypeScript, OpenAPI is the output.
$
npm install @spec-spac/spac @sinclair/typeboxMerge-friendly by design
Each team owns a TypeScript module. Diffs are line-level — not indentation battles in a 10k-line YAML.
api.group('/pets', g => g.get('/').response(Pet))Compile-time version safety
Api<'3.1'> in function signatures. assertVersion() catches team drift before CI even runs.
function registerPets(api: Api<'3.1'>) {}Share macros, not copy-paste
Auth, audit headers, x- extensions — publish once, import everywhere.
import { authenticated } from '@co/macros'Multi-team composition
index.ts
// Each team owns its own module — monorepo or multi-repo.
import { Api } from '@spec-spac/spac'
import { registerPets } from '@co/api-pets'
import { registerOrders } from '@co/api-orders'
import { registerUsers } from '@co/api-users'
const api = new Api('3.1', 'Company API', { versionPolicy: 'strict' })
registerPets(api) // @co/api-pets
registerOrders(api) // @co/api-orders
registerUsers(api) // @co/api-users
api.versionAudit().compatible // ← fails CI on drift
api.emit() // ← plain OpenAPI 3.1
Each team declares the OpenAPI version they authored against with assertVersion(). Drift is caught at compile time, audit time, or CI — not after deploy. Why spac →