Skip to content

jonloucks/contracts-ts

@jonloucks/contracts-ts

Badges

CI npm version Coverage Badge Typedoc Badge

Typescript Dependency Contracts for dependency inversion

Documentation

Installation

npm install @jonloucks/contracts-ts

v2.0.0 Migration Notes

v2.0.0 introduces a smaller root export surface, removes deprecated APIs, and finalizes ESM packaging.

Root package imports are intentionally minimal

Use root imports only for core exports:

import {
    CONTRACTS,
    Contract,
    ContractConfig,
    ContractException,
    Contracts,
    ContractsConfig,
    createContract,
    createContracts,
    VERSION
} from "@jonloucks/contracts-ts";

For broader helper APIs, use convenience or explicit subpath imports:

import { bind, enforce, createSingleton } from "@jonloucks/contracts-ts/api/Convenience";
import { createAtomicBoolean } from "@jonloucks/contracts-ts/auxiliary/Convenience";
Transform types moved out of api/Types
// v1.x (removed)
// import { TransformType, typeToTransform } from "@jonloucks/contracts-ts/api/Types";

// v2.0.0
import { type Type as TransformType } from "@jonloucks/contracts-ts/auxiliary/Transform";
Functional barrel removed
// v1.x (removed)
// import { TransformType } from "@jonloucks/contracts-ts/auxiliary/Functional";

// v2.0.0
import {
    type TransformType,
    transformFromType,
    transformGuard
} from "@jonloucks/contracts-ts/auxiliary/Convenience";

Usage - code fragments from Example.test.ts

Importing the Package
import { CONTRACTS, createContract } from '@jonloucks/contracts-ts';
Importing the Convenience Package
import {
    type AutoClose,
  bind,
  claim,
    type Contract,
  createExtractor,
  createLifeCycle,
  createRepository,
  createSingleton,
  createValue,
  createContract,
  enforce,
  guardFunctions,
  isBound
} from "@jonloucks/contracts-ts/api/Convenience";
Creating a Contract
// Define a service interface
interface Logger {
    log(message: string): void;
}

// Create a contract for the service
const LOGGER_CONTRACT: Contract<Logger> = createContract<Logger>({
    name: "Logger",
    test: (obj: unknown): obj is Logger => { // example of duck-typing check
    return guardFunctions(obj, 'log'); // example of using guardFunctions helper
    }
});
Binding a Contract
bind<Logger>(LOGGER_CONTRACT,
    createSingleton<Logger>(
        () => ({
            log: (message: string) => {
                console.log("LOG:", message);
            }
        })));
Using the Contract
const logger : Logger = enforce<Logger>(LOGGER_CONTRACT);
logger.log("Using the service in the test.");

Development

Install dependencies
npm install
Build the project
npm run build
Run tests
npm test
Run tests in watch mode
npm run test:watch
Run test coverage
npm run test:coverage
Lint the code
npm run lint
Fix linting issues
npm run lint:fix
Generate documents
npm run docs
Generate badges
npm run badges
Project Structure
  • All tests must have suffix of -test.ts or -spec.ts
  • Tests that validate supported APIs go in src/test
  • Tests that validate internal implementation details go in src/impl
contracts-ts
├── .github
│   ├── ISSUE_TEMPLATE
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   └── workflows
│       ├── main-pull-request-matrix.yml
│       ├── main-pull-request.yml
│       ├── main-push.yml
│       └── main-release.yml
├── CODE_OF_CONDUCT.md
├── CODING_STANDARDS.md
├── CONTRIBUTING.md
├── DOCUMENTATION.md
├── .editorconfig
├── eslint.config.mjs
├── LICENSE
├── package-lock.json
├── package.json
├── PULL_REQUEST_TEMPLATE.md
├── README.md
├── SECURITY.md
├── src
│   ├── index.ts
│   ├── version.ts
│   ├── api
│   │   └── *.ts
│   ├── auxiliary
│   │   └── *.ts
│   ├── impl
│   │   ├── *.ts
│   │   └── *.test.ts    // internal implementation specific
│   ├── test
│   │   └── *.test.ts
│   └── never-publish             // non shippable development scripts (if present)
├── tsconfig.json
└── typedoc.json

GitHub Workflows

CI Workflow

The CI workflow runs on every push and pull request to main branch. It:

  • Tests against supported Node.js versions 20.19.0+, 22.13.0+, and 24.x
  • Runs linting
  • Builds the project
  • Runs tests with coverage
  • Uploads coverage to Codecov (optional)

Compatibility note:

  • Production AutoClose implementations intentionally support both Symbol.dispose and Symbol.for("Symbol.dispose") keys, so disposal remains stable in environments that do not provide Symbol.dispose.
Publish Workflow

The GitHub publishings workflows are run to make an official release.

  • If all scanning and tests pass it is published. There is no other way allowed.
  • Publishing authentication is done using (OIDC trusted publishing)

To set up your own publishing:

  1. Publishing this project as is intentionally disabled
  2. You are welcome to fork this repository and publish where you want.
  3. Run npm pkg delete private to remove the private flag from the package.
  4. Change the name field in package.json to your desired package name.

License

MIT