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

@stackbilt/contracts

v0.2.1

Published

Stackbilt Contract Ontology Layer — ODD-driven code generation from TypeScript+Zod contracts

Downloads

326

Readme

@stackbilt/contracts

Pragmatic Ontology-Driven Design for TypeScript. Define your domain once, generate everything.

TypeScript + Zod instead of RDF/OWL/SHACL. Same goals: centralized knowledge, grounded reasoning, zero inference. One contract definition produces D1 migrations, Hono route handlers, typed SDK clients, OpenAPI specs, and test fixtures.

Install

npm install @stackbilt/contracts zod

Quick Start

Define a contract:

import { z } from 'zod';
import { defineContract } from '@stackbilt/contracts';

const TaskContract = defineContract({
  name: 'Task',
  version: '1.0.0',
  description: 'A work item with lifecycle management',

  schema: z.object({
    id: z.string().uuid(),
    userId: z.string(),
    title: z.string().min(1).max(200),
    status: z.enum(['open', 'in_progress', 'done']),
    createdAt: z.string().datetime(),
  }),

  operations: {
    create: {
      input: z.object({
        userId: z.string(),
        title: z.string().min(1).max(200),
      }),
      output: 'self',
      emits: ['task.created'],
    },
    complete: {
      input: z.object({ id: z.string().uuid() }),
      output: 'self',
      transition: { from: 'in_progress', to: 'done' },
      emits: ['task.completed'],
    },
  },

  states: {
    field: 'status',
    initial: 'open',
    transitions: {
      open:        { start: 'in_progress' },
      in_progress: { complete: 'done' },
      done:        {},
    },
  },

  surfaces: {
    api: {
      basePath: '/api/tasks',
      routes: {
        create:   { method: 'POST', path: '/' },
        list:     { method: 'GET',  path: '/' },
        get:      { method: 'GET',  path: '/:id' },
        complete: { method: 'POST', path: '/:id/complete' },
      },
    },
    db: {
      table: 'tasks',
      indexes: ['idx_task_user(user_id, status)'],
    },
  },

  authority: {
    create:   { requires: 'authenticated' },
    list:     { requires: 'public' },
    get:      { requires: 'public' },
    complete: { requires: 'owner', ownerField: 'userId' },
  },
});

Generate everything from that single definition:

import {
  generateSQL,
  generateRoutes,
  generateSDK,
  generateOpenAPI,
  generateTests,
} from '@stackbilt/contracts/generators';

const sql     = generateSQL(TaskContract);       // CREATE TABLE DDL
const routes  = generateRoutes(TaskContract);    // Hono handler bodies
const sdk     = generateSDK(TaskContract);       // Typed fetch client
const openapi = generateOpenAPI(TaskContract);   // OpenAPI 3.1 spec
const tests   = generateTests(TaskContract);     // Vitest fixtures

What a Contract Defines

A contract is a single source of truth for a domain entity:

| Field | Purpose | |-------|---------| | schema | Entity shape (Zod schema) | | operations | Valid actions with typed input/output | | states | State machine transitions (optional) | | surfaces | API routes + DB table mapping | | authority | Per-operation access control | | invariants | Runtime business rules (optional) | | version | Semver for schema evolution |

Core API

defineContract(definition)

Pure declaration factory. No side effects. Returns the definition with full type inference.

import { defineContract } from '@stackbilt/contracts';

const MyContract = defineContract({
  name: 'MyEntity',
  version: '1.0.0',
  description: '...',
  schema: z.object({ ... }),
  operations: { ... },
  surfaces: { ... },
  authority: { ... },
});

ref(contract, field)

Cross-contract foreign key reference. Generators use the metadata for JOIN clauses and referential integrity.

import { ref } from '@stackbilt/contracts';

schema: z.object({
  userId: ref(UserContract, 'id'),  // FK to users.id
})

extend(base, extension)

Contract inheritance. Creates a new contract with merged fields. Base contract remains untouched.

import { extend } from '@stackbilt/contracts';

const AdminTaskContract = extend(TaskContract, {
  name: 'AdminTask',
  operations: {
    forceClose: {
      input: z.object({ id: z.string().uuid(), reason: z.string() }),
      output: 'self',
      transition: { from: ['open', 'in_progress'], to: 'done' },
    },
  },
  authority: {
    forceClose: { requires: 'role', roles: ['admin'] },
  },
});

Generators

All generators take a ContractDefinition and return a string (or object for OpenAPI).

generateSQL(contract, options?)

Emits CREATE TABLE DDL for D1 (SQLite).

Options:

  • dropFirst — prepend DROP TABLE IF EXISTS
  • ifNotExists — add IF NOT EXISTS (default: true)
  • tableName — override table name

Output includes:

  • Column types mapped from Zod (string -> TEXT, number.int -> INTEGER, number -> REAL, boolean -> INTEGER, enum -> TEXT with CHECK, array/object -> TEXT as JSON)
  • NOT NULL for non-optional fields
  • PRIMARY KEY detection
  • CHECK constraints for enum fields
  • UNIQUE constraints from surfaces.db.uniqueColumns
  • DEFAULT values from surfaces.db.columnOverrides
  • Foreign key comments from ref() calls
  • Index definitions from surfaces.db.indexes

generateRoutes(contract, options?)

Emits Hono HTTP handler bodies with D1 operations.

Options:

  • contractImport — import path for the contract
  • appVar — Hono app variable name
  • includeAuthImports — include auth middleware imports

Output includes:

  • Per-route handlers with try/catch error handling
  • Input validation via safeParse()
  • CRUD operations (INSERT, SELECT, UPDATE, DELETE)
  • State transition guards (checks current state before allowing transition, returns 409 on invalid state)
  • Authority middleware (requireAuth, requireOwner, requireRole)
  • Proper HTTP status codes (201, 400, 404, 409, 500)
  • Response envelope: { data } or { error: { code, message } }

generateSDK(contract, options?)

Emits a typed fetch client class.

Options:

  • contractImport — import path
  • className — class name override

Output includes:

  • Client class with baseUrl + headers constructor
  • Per-operation methods with typed input/output
  • Path parameter interpolation
  • Error handling with descriptive messages

generateOpenAPI(contract, options?)

Returns an OpenAPI 3.1.0 specification object.

Options:

  • title — API title override
  • version — API version override
  • serverUrl — server URL

Output includes:

  • Paths from surfaces.api.routes (:id -> {id} conversion)
  • Request/response schemas from operations
  • Security schemes for authenticated routes
  • Component schemas for entity + operation inputs

generateTests(contract, options?)

Emits Vitest test fixtures and state machine validation tests.

Options:

  • contractImport — import path

Output includes:

  • Valid fixture generation with sensible defaults per type
  • Enum validation tests (pass/fail for each value)
  • State transition tests (allowed and blocked transitions)
  • Missing field rejection tests

Introspection

The Zod schema walker extracts metadata for code generation. Works with both Zod v3 and v4.

import { extractColumns, extractEnums, toSnakeCase } from '@stackbilt/contracts/introspect';

const columns = extractColumns(MyContract.schema);
// [{ name: 'id', sqlType: 'TEXT', nullable: false, isPrimaryKey: true, ... }, ...]

const enums = extractEnums(MyContract.schema);
// { status: ['open', 'in_progress', 'done'] }

toSnakeCase('createdAt'); // 'created_at'

ColumnDef

interface ColumnDef {
  name: string;           // snake_case column name
  sqlType: string;        // TEXT | INTEGER | REAL
  nullable: boolean;
  defaultValue: string | null;
  checkConstraint: string | null;  // SQL CHECK for enums
  isPrimaryKey: boolean;
  isRef: boolean;         // foreign key via ref()
  refTable: string | null;
  refField: string | null;
}

State Machines

Contracts can declare state machines that enforce valid transitions:

states: {
  field: 'status',        // which schema field holds state
  initial: 'draft',       // state for new entities
  transitions: {
    draft:     { publish: 'published', archive: 'archived', delete: null },
    published: { archive: 'archived' },
    archived:  {},         // terminal state
  },
}
  • null as a target means deletion (terminal)
  • Empty {} means no valid transitions (terminal state)
  • Route generator emits guards that check current state and return 409 on invalid transitions

Authority

Four authorization levels per operation:

authority: {
  list:    { requires: 'public' },                        // no auth
  create:  { requires: 'authenticated' },                 // any logged-in user
  update:  { requires: 'owner', ownerField: 'userId' },  // entity owner only
  admin:   { requires: 'role', roles: ['admin'] },        // specific roles
}

Route generator emits the corresponding middleware calls.

Invariants

Runtime business rules that guard operations:

invariants: [
  {
    name: 'min_ingredients',
    description: 'Published recipes need at least one ingredient',
    check: (entity) => {
      const e = entity as { status: string; ingredients: string[] };
      if (e.status === 'published' && e.ingredients.length === 0) {
        return 'Published recipes need at least one ingredient';
      }
      return true;
    },
    appliesTo: ['publish'],
  },
]
  • Return true to pass, or a string error message to reject
  • appliesTo lists which operations trigger the check
  • Enforcement is delegated to your application code

Examples

See src/examples/recipe.contract.ts for a complete contract demonstrating schema, operations, state machine, surfaces, authority, and invariants.

import { RecipeContract } from '@stackbilt/contracts/examples';
import { generateSQL, generateRoutes } from '@stackbilt/contracts/generators';

console.log(generateSQL(RecipeContract));
console.log(generateRoutes(RecipeContract));

Design Principles

  1. Contracts are the source of truth. Schema, API, database, auth, and business rules in one place.
  2. Generate, don't handwrite. CRUD handlers, SQL migrations, SDKs, and tests derived from contracts.
  3. Zod over RDF. Same ontological rigor, developer-friendly, no external tooling required.
  4. Extend over modify. extend() creates new contracts from existing ones without mutation.
  5. Surfaces decouple shape from deployment. Same contract serves different API frameworks and databases.
  6. Authority is declarative. Per-operation access rules, generated as middleware.
  7. State machines are optional but powerful. Transitions enforce valid lifecycle changes at the route level.

License

Apache-2.0