Skip to content

About

Local architecture contracts for TypeScript — mechanical, milliseconds-fast checks designed for the age of AI-generated code.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

code-contracts

npm version CI License: MIT

Mechanical TypeScript conventions for testable code: keep IO behind explicit boundaries and make exported free functions composable.

What it guarantees

code-arch checks two things:

  1. IO APIs and packages declared in your catalog only appear in their designated areas.
  2. Exported free functions with input accept one object parameter.

The IO catalog is explicit. No static tool can reliably infer whether every arbitrary package performs IO, so code-arch guarantees confinement for the APIs and packages you declare.

Install and initialize

npm install --save-dev code-arch
npx code-arch init
npx code-arch check

Run npx code-arch readme to print the documentation shipped with the installed package.

init creates code-contracts.config.ts and a short CODE_CONTRACTS.md. A config is required: designated IO areas are application-specific, and the checker will not silently invent them.

Configuration

import { defineCodeContracts } from "code-arch";

export default defineCodeContracts({
  io: {
    time: {
      apis: ["Date.now", "new Date"],
      allowedIn: ["src/infrastructure/time/**"]
    },
    database: {
      packages: ["drizzle-orm", "drizzle-orm/**"],
      allowedIn: ["src/infrastructure/database/**"]
    },
    network: {
      apis: ["fetch"],
      packages: ["axios", "axios/**"],
      allowedIn: ["src/infrastructure/http/**"]
    }
  },
  functions: {
    objectParams: true
  }
});

Top-level fields:

Field Type Default Description
rootDir string config directory Root for discovery and reporting
include string[] ["src/**/*.{ts,tsx}"] Source globs to check
exclude string[] build, dependency, declaration, fixture, and test files Globs to skip
io Record<string, IoCapability> required Named IO capabilities and their owners
functions.objectParams boolean true Enforce object input for exported free functions

Each IO capability accepts:

Field Type Description
apis string[] API expressions such as Date.now, new Date, process.env, or console.*
packages string[] Exact or globbed module specifiers such as drizzle-orm/**
allowedIn string[] Required file globs designating where this IO belongs

A capability must declare at least one API or package. Package checks cover imports, side-effect imports, re-exports, literal dynamic imports, and literal CommonJS require() calls.

Object parameters

Exported top-level functions may take no input or one object input:

export function healthcheck() {}

interface CreateOrderParams {
  customerId: string;
  productId: string;
}

export function createOrder(params: CreateOrderParams) {}

Primitive, positional, array, tuple, callable, rest, any, unknown, and unconstrained generic inputs fail. Methods and non-exported helpers are outside this convention.

Layer boundaries

Layer assignment and dependency direction are deliberately not duplicated here. Use ArchUnitTS for those architecture tests:

const rule = projectFiles()
  .inFolder("src/domain/**")
  .shouldNot()
  .dependOnFiles()
  .inFolder("src/infrastructure/**");

This keeps responsibilities clear: code-arch owns IO confinement and function composition conventions; ArchUnitTS owns dependency graphs, layers, slices, and cycles.

CLI

Usage: code-arch <command> [options]

Commands:
  init              Create a starter config
  check             Run code contract checks
  readme            Print this README
  rules             List available rule IDs
  prompt            Generate a focused AI refactor prompt

Options for check:
  --json            Print machine-readable JSON
  --rule <rule>     Run restrict-io or require-object-params only

Options for prompt:
  --rule <rule>     Rule ID (required)
  --file <file>     File to target (otherwise discover all violations)

Programmatic API

import { loadConfig, runCheck } from "code-arch";

const config = await loadConfig(process.cwd());
const result = await runCheck(config, process.cwd());

Requirements

  • Node.js 20 or newer
  • TypeScript project

License

MIT

About

Local architecture contracts for TypeScript — mechanical, milliseconds-fast checks designed for the age of AI-generated code.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages