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

xdbc

v1.0.220

Published

A Typescript Design by Contract Framework

Readme

XDBC — eXplicit Design by Contract for TypeScript

A decorator-based Design by Contract framework that enforces preconditions, postconditions, and invariants through TypeScript metadata — delivering precise, self-documenting, and verifiable component contracts.


At a Glance

| Approach | With XDBC | Without XDBC | |---|---|---| | Parameter validation | @DBC.ParamvalueProvidermethod(@REGEX.PRE(/^.*XDBC.*$/i) input: string[]) { ...} | method(input: string[]) { input.forEach((el, i) => { console.assert(/^.*XDBC.*$/i.test(el), "error"); }); ...} | | Field invariant | @REGEX.INVARIANT(/^.*XDBC.*$/i)public field = "XDBC"; | get field(): string { return this._field; }set field(v: string) { console.assert(/^.*XDBC.*$/i.test(v), "error"); this._field = v;} | | Return validation | @REGEX.POST(/^XDBC$/i)method(input: unknown): string { ... return result;} | method(input: unknown): string { ... if (!/^XDBC$/i.test(result)) { throw new Error("error"); } return result;} |

Contract violations produce structured, actionable diagnostics:

[ XDBC Infringement [ From "method" in "MyClass": [ Parameter-value "+1d,+5d,-x10y"
of the 1st parameter did not fulfill one of it's contracts: Violating-Arrayelement at
index 2. Value has to comply to regular expression "/^(?i:(NOW)|([+-]\d+[dmy]))$/i"]]]

Table of Contents


What is Design by Contract?

Design by Contract (DbC) is a software engineering methodology that defines formal, precise, and verifiable interface specifications for software components. Each component's contract comprises:

| Element | Purpose | |---|---| | Preconditions | Conditions that must hold true before a method executes | | Postconditions | Guarantees that must hold true after a method returns | | Invariants | Properties that must remain true throughout an object's lifetime |

DbC vs. Assertions

| Aspect | XDBC Decorators | Manual Assertions | |---|---|---| | Formality | Formal, declarative, co-located with signatures | Informal, scattered through method bodies | | Integration | TypeScript metadata and decorators | Built-in console.assert / throw | | Expressiveness | Composable, parameterized contract objects | Simple boolean checks | | Readability | Contracts are visible at the API surface | Validation logic obscures business logic | | Maintainability | Contracts are localized and reusable | Duplicated checks are hard to track | | Production control | Selectively enable/disable by contract type | Typically all-or-nothing |


Why XDBC?

  • Declarative — contracts live as decorators alongside type signatures, not buried in method bodies
  • Composable — combine contracts with AE, OR, and IF for expressive validation
  • Configurable — toggle preconditions, postconditions, and invariants independently
  • Diagnostic — structured error messages pinpoint the exact violation, parameter, and context
  • Extensible — 16 built-in contracts, with support for custom contracts and Zod schema integration
  • Zero runtime overhead — disable contract checking in production with a single flag

Installation

npm install xdbc

Requirements: TypeScript 5.x with experimentalDecorators and emitDecoratorMetadata enabled in tsconfig.json.

// tsconfig.json
{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

Decorator API

XDBC is built on TypeScript's legacy (experimentalDecorators) decorator API — not the TC39 Stage 3 decorator API.

Why? Stage 3 decorators deliberately excluded parameter decorators from their scope. Parameter decorators are the foundation of XDBC's contract syntax: @DEFINED.PRE(), @GREATER.PRE(0), and every other PRE contract applied per-parameter depends on them. There is no equivalent in Stage 3, and no workaround that preserves the same ergonomics.

Is this unusual? No. Some of the most widely adopted TypeScript frameworks in the industry require experimentalDecorators for exactly the same reason and have no near-term plans to migrate: on the backend, NestJS (used at thousands of companies, tens of millions of weekly downloads), TypeORM, class-validator, and class-transformer; on the frontend, vue-class-component (Vue's official class-based API) and MobX (the dominant React state management library for class-based stores). experimentalDecorators: true is compatible with Angular, Vue, and React — it is a TypeScript compiler flag, not a framework-level constraint, and does not conflict with any framework's runtime behavior. Any project already using these libraries has experimentalDecorators: true in its tsconfig and can adopt XDBC with zero additional configuration. For projects that don't, enabling it requires adding two lines to tsconfig.json — see Installation.

Is it risky? No. TypeScript explicitly supports both APIs simultaneously and has made no announcement about removing experimentalDecorators. Any project already using the frameworks above already has experimentalDecorators: true in its tsconfig, meaning XDBC requires zero additional configuration in those environments.

What about the future? TC39 has an active Stage 1 proposal — Class Method Parameter Decorators (Ron Buckton, 2023) — that would close this gap. When parameter decorators reach a stable stage, XDBC will migrate to Stage 3. Because all decorator wiring is contained in DBC.ts and the 17 contract classes are completely insulated from it, that migration will be localized and non-breaking at the API level.


Quick Start

import { DBC, REGEX, TYPE, EQ } from "xdbc";

class UserService {

  // Invariant: email must always match pattern
  @REGEX.INVARIANT(/^[^@]+@[^@]+\.[^@]+$/)
  public email = "[email protected]";

  // Precondition: name must be a string; Postcondition: return must match pattern
  @REGEX.POST(/^Hello, .+$/)
  @DBC.ParamvalueProvider
  public greet(@TYPE.PRE("string") name: string): string {
    return `Hello, ${name}`;
  }

  // Precondition: age must be >= 0
  @DBC.ParamvalueProvider
  public setAge(@GREATER_OR_EQUAL.PRE(0) age: number) {
    // ...
  }
}

Contracts Reference

XDBC ships with 16 contracts organized into core validators and derived specializations:

Core Contracts

| Contract | Description | Constructor | |---|---|---| | REGEX | Value must match a regular expression | new REGEX(expression: RegExp) | | TYPE | Value must be of a specified type (supports pipe-separated: "string\|number") | new TYPE(type: string) | | EQ | Value must equal (or not equal) a reference value | new EQ(equivalent: any, invert?: boolean) | | COMPARISON | Numeric comparison against a reference value | new COMPARISON(equivalent, equalityPermitted, invert) | | INSTANCE | Value must be an instance of a specified class | new INSTANCE(reference: any \| any[]) | | AE | Every element in an array must satisfy a set of contracts | new AE(conditions, index?, idxEnd?) | | OR | At least one of a set of contracts must be satisfied | new OR(conditions: DBC[]) | | IF | Conditional contract: if A holds, then B must also hold | IF.PRE(condition, inCase, path?, invert?) | | JSON_OP | Object must contain specific properties of specific types | new JSON_OP(properties: {name, type}[], checkElements?) | | JSON_Parse | String must be valid JSON; optionally forwards parsed result | new JSON_Parse(receptor?: (json) => void) | | DEFINED | Value must not be null or undefined | — | | UNDEFINED | Value must be undefined | — | | ARRAY | Value must be an array | — | | HasAttribute | HTMLElement must possess a named attribute | HasAttribute.PRE(attrName, invert?) | | ZOD | Value must validate against a Zod schema | new ZOD(schema: z.ZodType) |

Derived Contracts

| Contract | Derives From | Semantics | |---|---|---| | GREATER | COMPARISON | value > reference | | GREATER_OR_EQUAL | COMPARISON | value >= reference | | LESS | COMPARISON | value < reference | | LESS_OR_EQUAL | COMPARISON | value <= reference | | DIFFERENT | EQ | value !== reference | | PLAIN_OBJECT | ARRAY | Value must be a non-null, non-array object |

Built-in Regular Expressions

REGEX.stdExp provides ready-to-use patterns:

| Key | Validates | |---|---| | htmlAttributeName | HTML attribute names | | eMail | Email addresses | | property | Property identifiers | | url | URLs | | keyPath | Key paths | | date | Date strings | | dateFormat | Date format patterns | | cssSelector | CSS selectors | | boolean | Boolean string literals | | colorCodeHEX | Hex color codes | | simpleHotkey | Keyboard shortcuts | | bcp47 | BCP 47 language tags |


Core Concepts

Decorator Types

Every contract exposes three decorator factories:

| Decorator | Applies To | Validates | |---|---|---| | Contract.PRE(...) | Method parameters | Input values before method execution | | Contract.POST(...) | Methods | Return value after method execution | | Contract.INVARIANT(...) | Fields / Properties | Value on every assignment (including initialization) |

The ParamvalueProvider Decorator

TypeScript parameter decorators do not natively receive parameter values. Any method using PRE parameter contracts must be decorated with @DBC.ParamvalueProvider:

@DBC.ParamvalueProvider
public process(
  @TYPE.PRE("string") name: string,
  @REGEX.PRE(/^\d{4}$/) code: string
) { ... }

Path Resolution

All PRE, POST, and INVARIANT decorators accept an optional path parameter — a dotted path that specifies a nested property of the value to validate instead of the value itself:

// Validate that element.tagName === "SELECT"
@DBC.ParamvalueProvider
public handleElement(@EQ.PRE("SELECT", false, "tagName") el: HTMLElement) { }

// Validate that value.length === 1
@EQ.INVARIANT(1, false, "length")
public singleChar = "X";

Path resolution supports:

  • Dot notation: "user.address.city"
  • Array indices: "items[0]"
  • Method calls: "getName()"
  • HTML attributes: "@data-id"

Custom Hints

Add context to error messages with the optional hint parameter:

@DBC.ParamvalueProvider
public setAge(
  @GREATER_OR_EQUAL.PRE(0, undefined, "Age must be non-negative") age: number
) { }

Advanced Features

Composing Contracts with AE

Validate array elements against one or more contracts, optionally targeting a specific index range:

// All elements must be non-empty strings matching a date pattern
@AE.PRE([new TYPE("string"), new REGEX(/^\d{4}-\d{2}-\d{2}$/)])
public processDates(dates: string[]) { }

// Only elements at indices 1 through 3
@AE.PRE([new REGEX(/^[A-Z]+$/)], 1, 3)
public processRange(items: string[]) { }

// Single element at index 0
@AE.PRE([new TYPE("number")], 0)
public processFirst(values: unknown[]) { }

Logical Composition with OR

At least one contract must pass:

@DBC.ParamvalueProvider
public setStatus(@OR.PRE([new EQ("active"), new EQ("inactive"), new EQ("pending")]) status: string) { }

Conditional Contracts with IF

Apply a contract only when a precondition holds:

// If the value is a string, it must also match digits-only
@IF.PRE(new TYPE("string"), new REGEX(/^\d+$/))
public processInput(value: unknown) { }

Zod Schema Integration

Leverage Zod schemas for complex structural validation:

import { z } from "zod";

const UserSchema = z.object({
  name: z.string().min(1),
  email: z.string().email(),
  age: z.number().int().positive()
});

@DBC.ParamvalueProvider
public createUser( @ZOD.PRE(UserSchema) data: unknown) { }

Type-Safe Static Checks

Several contracts offer static tsCheck methods for imperative validation outside of decorators:

// Throws if value is not a string
const name = TYPE.tsCheck<string>(input, "string", "Expected a string");

// Throws if value doesn't match regex
const code = REGEX.tsCheck<string>(input, /^\d{4}$/, "Invalid code format");

// Throws if not an instance of Date
const date = INSTANCE.tsCheck<Date>(input, Date, "Expected a Date");

// Throws if none of the conditions pass
const result = OR.tsCheck<string>(input, [new EQ("a"), new EQ("b")]);

DOM / HTML Input Binding

XDBC can enforce contracts directly on <input> and <textarea> elements using HTML data attributes — no JavaScript wiring required per element.

Setup

import { scanDOM } from "xdbc/DBC/DOM";

// Call once after the DOM is ready. Returns a cleanup function.
const cleanup = scanDOM();

// Optionally scope to a subtree:
const cleanup = scanDOM(document.getElementById("my-form"));

// Remove all listeners (e.g. on component unmount):
cleanup();

Opting an element in

Any data-xdbc-* attribute is sufficient to enroll an element — no data-xdbc marker is required:

<input data-xdbc-regex="^\d*$" />

The optional data-xdbc attribute specifies a custom DBC instance path (default: "WaXCode.DBC"):

<input data-xdbc="MyApp.DBC" data-xdbc-regex="^\d*$" />

Built-in contract attributes

| Attribute | Example value | Contract | |---|---|---| | data-xdbc-regex | ^\d*$ | REGEX | | data-xdbc-type | string\|number | TYPE | | data-xdbc-eq | hello | EQ | | data-xdbc-different | forbidden | EQ (inverted) | | data-xdbc-defined | (no value needed) | DEFINED | | data-xdbc-undefined | (no value needed) | UNDEFINED | | data-xdbc-greater | 5 | COMPARISON | | data-xdbc-greater-or-equal | 5 | COMPARISON | | data-xdbc-less | 100 | COMPARISON | | data-xdbc-less-or-equal | 100 | COMPARISON | | data-xdbc-or | regex:^\d+$;;eq:N/A | OR combinator (see below) |

Multiple attributes on one element are all enforced — the first failure blocks and reports.

OR fragment syntax

Use data-xdbc-or to express that the value must satisfy at least one of several contracts. Fragments are separated by ;;; each fragment is <contract-key>:<value>, where the split is on the first : only (so colons inside regex patterns are safe):

<!-- digits, OR exactly the string "N/A" -->
<input data-xdbc-or="regex:^\d+$;;eq:N/A" />

<!-- http or https URL, OR the literal "N/A" -->
<input data-xdbc-or="regex:^https?://;;eq:N/A" />

Keystroke vs. blur validation

By default, contracts fire on blur (when the element loses focus), so partially typed values are never rejected mid-entry.

To switch an element to keystroke-time validation, set data-xdbc-validate-on="input":

<!-- validates after every keystroke -->
<input data-xdbc-validate-on="input" data-xdbc-regex="^\d*$" />

Every built-in contract also has an -input twin that always fires on keystroke, regardless of data-xdbc-validate-on. Use it alongside the base contract to apply a permissive pattern while the user is typing and a strict pattern on blur:

| Base attribute | -input twin | When it fires | |---|---|---| | data-xdbc-regex | data-xdbc-regex-input | every keystroke | | data-xdbc-type | data-xdbc-type-input | every keystroke | | data-xdbc-eq | data-xdbc-eq-input | every keystroke | | data-xdbc-different | data-xdbc-different-input | every keystroke | | data-xdbc-defined | data-xdbc-defined-input | every keystroke | | data-xdbc-undefined | data-xdbc-undefined-input | every keystroke | | data-xdbc-greater | data-xdbc-greater-input | every keystroke | | data-xdbc-greater-or-equal | data-xdbc-greater-or-equal-input | every keystroke | | data-xdbc-less | data-xdbc-less-input | every keystroke | | data-xdbc-less-or-equal | data-xdbc-less-or-equal-input | every keystroke | | data-xdbc-or | data-xdbc-or-input | every keystroke |

This is the recommended pattern for fields where the fully valid value can only be determined once typing is complete — such as email addresses, domain names, or structured codes:

<!-- strict LDAP DN on blur; only legal characters allowed on every keystroke -->
<input
  data-xdbc-regex="^[A-Za-z]+=.+(,[A-Za-z]+=.+)*$"
  data-xdbc-regex-input="^[a-zA-Z0-9=,. _\-]*$"
/>

Real-world example — Angular with toast notifications

In production, two or three attributes on an ordinary <input> — combined with a single global onInfringement callback — are enough to guarantee correctness and surface precise, human-readable error messages through your application's own notification system.

The following field is from a school Active Directory management suite. It binds a full LDAP Distinguished Name contract to a PrimeNG input with zero custom validator code:

<!-- ldap-settings.component.html -->
<input
  pInputText
  [(ngModel)]="form.base_dn"
  placeholder="DC=school,DC=local"
  data-xdbc-regex="^[A-Za-z]+=.+(,[A-Za-z]+=.+)*$"
  data-xdbc-regex-input="^[a-zA-Z0-9=,. _\-]*$"
/>

Two attributes do all the work:

  • data-xdbc-regex-input — permits only the characters that can legally appear in an LDAP DN while the user is still typing, so the field never blocks a partial entry.
  • data-xdbc-regex — enforces the full key=value(,key=value)* DN structure on blur, giving the user a complete value to correct.

Route violations to PrimeNG's MessageService (or any toast library) once at startup:

// app.component.ts
import { scanDOM } from "xdbc/DBC/DOM";

export class AppComponent implements OnInit {
  constructor(private messages: MessageService) {}

  ngOnInit() {
    const dbc = (globalThis as any).WaXCode.DBC;
    dbc.infringementSettings.onInfringement = (infringement: Error) => {
      this.messages.add({
        severity: "error",
        summary:  "Invalid input",
        detail:   infringement.message,
      });
    };
    scanDOM();
  }
}

No per-element wiring. No Angular validators. Three attributes on an HTML element and one global callback are enough to enforce correctness and deliver structured diagnostics directly to your users.

Behaviour on infringement

  1. The element's value is reverted to the last accepted state, blocking the invalid input.
  2. The DBC instance's onInfringement, logToConsole, and throwException settings are all honoured. Any throw is swallowed inside the event handler so it cannot propagate unhandled.

IME / composition awareness

Validation is suspended during IME composition (e.g. CJK on-screen keyboards) and runs once on compositionend, so partially composed characters are never incorrectly rejected.

Registering custom contracts

Use registerDOMContract to add any contract — including future ones — without modifying the library:

import { registerDOMContract } from "xdbc/DBC/DOM";
import { MY_CONTRACT } from "./MY_CONTRACT";

// Register once, before scanDOM():
registerDOMContract("my-contract", (value, attrValue) =>
    MY_CONTRACT.checkAlgorithm(value, attrValue),
);
<input data-xdbc-my-contract="someConfig" />

The attrValue string is whatever appears in the attribute — parse it however your contract needs.


Configuration

DBC Instance Settings

A default DBC instance is automatically registered at WaXCode.DBC when the module is imported. You can access and configure it:

import { DBC } from "xdbc";

// Access the default DBC instance
const dbc = (globalThis as any).WaXCode.DBC as DBC;

// Toggle contract checking
dbc.executionSettings.checkPreconditions = true;
dbc.executionSettings.checkPostconditions = true;
dbc.executionSettings.checkInvariants = true;

// Configure infringement handling
dbc.infringementSettings.throwException = true;   // throw DBC.Infringement on violation
dbc.infringementSettings.logToConsole = false;     // log to console instead

# React to infringements programmatically
dbc.infringementSettings.onInfringement = (infringement, context) => {
    // infringement — DBC.Infringement instance (extends Error, has .message and .stack)
    // context.type — "precondition" | "postcondition" | "invariant"
    // context.value — the raw value that violated the contract
    Sentry.captureException(infringement, { extra: context });
};

The callback fires before throwException, so it always runs even when an exception is thrown. All three settings are independent and can be combined freely.

Multiple DBC Instances

Create isolated DBC instances with separate configurations using DBC.register():

// Register a vendor-specific instance at a custom path
const vendorDbc = new DBC(
  { throwException: false, logToConsole: true },
);
DBC.register(vendorDbc, "MyVendor.DBC");

// Route a contract to the custom instance via its path
@REGEX.INVARIANT(/^[A-Z]+$/, undefined, undefined, "MyVendor.DBC")
public code = "ABC";

Note: new DBC() does not automatically mount onto globalThis. Call DBC.register(instance, path) to make an instance available for decorator resolution.

Test Isolation

Use DBC.isolated() to run tests with a temporary DBC instance that doesn't affect other tests:

DBC.isolated((tempDbc) => {
  // tempDbc is registered at "WaXCode.DBC" for the duration of this callback
  tempDbc.executionSettings.checkPreconditions = false;

  // ... run tests with contracts disabled ...
});
// Original DBC instance is automatically restored here

Disabling Contracts in Production

const dbc = (globalThis as any).WaXCode.DBC as DBC;
dbc.executionSettings.checkPreconditions = false;
dbc.executionSettings.checkPostconditions = false;
dbc.executionSettings.checkInvariants = false;

API Documentation

Full generated API documentation is available at callaris.github.io/XDBC.

See Demo.ts for annotated usage examples.


Built With XDBC

XDBC is actively used in production across the following projects:

| Project | Context | |---|---| | CodBi | Low-code engine plugin for XIMA Formcycle | | tinymce-multicloud-plugin | multiCloud plugin for TinyMCE | | (internal) | Comprehensive Active Directory management suite for schools, deployed at a German public administration |

XDBC is used in the Angular frontends of the above projects.


Contributing

Participation is highly valued and warmly welcomed. The ultimate goal is to create a tool that proves genuinely useful and empowers a wide range of developers to build more robust and reliable applications.

Please see CONTRIBUTING.md for guidelines, and CODE_OF_CONDUCT.md for community standards.


License

MIT © Callari, Salvatore


"Design by Contract" is a registered trademark of Eiffel Software. XDBC is an independent project and is not affiliated with or endorsed by Eiffel Software.