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

bycontract

v3.0.1

Published

argument validation library based on JSDOC syntax

Readme

ByContract 3

bycontract promo

NPM

Runtime type checking for JavaScript and TypeScript. Declare contracts with JSDoc syntax. No transpilation required, zero production overhead.

Why ByContract?

TypeScript catches type errors at compile time. ByContract validates values at runtime.

This is useful when dealing with:

  • API payloads
  • User input
  • Environment variables
  • Third-party libraries
  • Untyped JavaScript consumers

Contracts can be enabled during development and removed entirely from production builds.

Contents

Quick start

ByContract supports several styles of runtime validation. Most projects will use either contract() or validate() with named helper functions.

Function contracts (recommended)

Wrap a function with parameter and return-value contracts. Contracts are compiled once and cached at definition time.

import { contract, nonNull, optional, typedef } from "bycontract";

const PdfOptionsType = typedef({
  scale: "?number"
});

const pdf = contract(
  [
    "string",
    nonNull( "number" ),
    nonNull( "number" ),
    PdfOptionsType,
    optional( "function" )
  ],
  "Promise",
  ( path, w, h, options, callback ) => {
    return generatePdf( path, w, h, options ).then( callback );
  }
);

pdf( "/tmp/out.pdf", 210, 297, { scale: 2 } );   // ok
pdf( "/tmp/out.pdf", "210", 297, { scale: 2 } );
// ByContractError: pdf: Argument #1: expected non-nullable but got string

Inline validation

A good fit for arrow functions. Property names are included in validation errors.

import { validate, nonNull, optional } from "bycontract";

const pdf = ( path, w, h, options, callback ) => {
  validate( { path, w, h, options, callback }, {
    path: "string",
    w: nonNull( "number" ),
    h: nonNull( "number" ),
    options: { scale: "?number" },
    callback: optional( "function" )
  });

  // ...
};

JSDoc syntax

For projects already using JSDoc-style contracts.

import { validate } from "bycontract";

function pdf( path, w, h, options, callback ) {
  validate(
    arguments,
    [ "string", "!number", "!number", PdfOptionsType, "function=" ]
  );
}

Decorator flavor

Requires Babel decorators in legacy mode.

import { validateJsdoc } from "bycontract";

class Page {
  @validateJsdoc(`
    @param {string} path
    @param {!number} w
    @param {!number} h
    @returns {Promise}
  `)
  pdf( path, w, h ) {
    return Promise.resolve();
  }
}

new Page().pdf( "/tmp/test.pdf", "not-a-number", 297 );
// ByContractError: Method: pdf, parameter w: expected non-nullable but got string

Configuration:

{
  "plugins": [
    ["@babel/plugin-proposal-decorators", { "legacy": true }]
  ]
}

Template literal flavor

For projects that prefer inline JSDoc-style validation without decorators.

import { validateContract } from "bycontract";

const path = "/tmp/out.pdf";
const width = 210;

validateContract`
  @param {string} ${path}
  @param {!number} ${width}
`;

Installation

npm install bycontract
// CommonJS
const { validate } = require( "bycontract" );

// ES module
import { validate } from "bycontract";

// Browser
// <script src="dist/byContract.min.js"></script>
// const { validate } = byContract;

Modifier helpers

Import named helpers instead of memorising JSDoc prefix/suffix characters:

import { optional, nullable, nonNull, arrayOf, union } from "bycontract";

| Helper | JSDoc equivalent | Meaning | |--------|-----------------|---------| | optional("number") | "number=" | Parameter may be omitted | | nullable("number") | "?number" | Value may be null | | nonNull("number") | "!number" | Rejects null and undefined | | arrayOf("string") | "string[]" | Every element must match the type | | union("number","string") | "number\|string" | Accepts any listed type |

Helpers return plain strings, so they compose freely with any contract position:

validate( { name, age, role }, {
  name: nonNull( "string" ),
  age: nonNull( "number" ),
  role: optional( union( "string", "null" ) )
});

validate( ids, arrayOf( "number" ) );
validate( scores, arrayOf( nonNull( "number" ) ) ); // rejects [1, null, 3]

Function contracts

contract( paramContracts, fn ) or contract( paramContracts, returnContract, fn ).

Works with arrow functions. Contracts are pre-compiled at definition time.

import { contract, nonNull, optional } from "bycontract";

// Positional array contracts
const add = contract( [ "number", "number" ], ( a, b ) => a + b );
add( 1, 2 );        // 3
add( 1, "two" );    // ByContractError: add: Argument #1: expected number but got string

// With return-type validation
const parseId = contract( [ "string" ], "number", str => parseInt( str, 10 ) );

// Named-param schema — single destructured argument, best error messages
const render = contract(
  { path: "string", w: nonNull( "number" ), callback: optional( "function" ) },
  ( { path, w, callback } ) => { /* … */ }
);
render( { path: "/", w: "oops" } );
// ByContractError: render: property #w expected non-nullable but got string

Types

Primitives

*, array, boolean, function, nan, null, number, object, regexp, string, undefined

validate( true, "boolean" );   // ok
validate( true, "Boolean" );   // ok — case-insensitive
validate( null, "boolean" );   // ByContractError: expected boolean but got null

Union

validate( 100, "string|number|boolean" );  // ok
validate( [], "string|number|boolean" );
// ByContractError: expected string|number|boolean but failed on each:
// expected string but got array, expected number but got array, expected boolean but got array

Optional

function foo( bar, baz ) {
  validate( arguments, [ "number=", "string=" ] );
}
foo();              // ok
foo( 100 );         // ok
foo( 100, "baz" );  // ok
foo( 100, 100 );    // ByContractError: Argument #1: expected string but got number

Nullable

validate( 100, "?number" );  // ok
validate( null, "?number" ); // ok

Non-nullable

validate( 42, "!number" );   // ok
validate( null, "!number" ); // ByContractError: expected non-nullable but got null

Typed arrays

validate( [ 1, 2 ], "number[]" );          // ok
validate( [ 1, "x" ], "number[]" );        // ByContractError: array element 1: expected number but got string

validate( [ 1, 2 ], "Array.<number>" );    // ok — JSDoc syntax

Typed objects

validate( { a: "foo", b: "bar" }, "Object.<string, string>" );  // ok
validate( { a: "foo", b: 100 }, "Object.<string, string>" );
// ByContractError: object property b: expected string but got number

Object schema

validate( { foo: "foo", bar: 10 }, { foo: "string", bar: "number" } );  // ok

validate( { foo: "foo", bar: { quiz: [10] } }, {
  foo: "string",
  bar: { quiz: "number[]" }
});  // ok — nested schemas work recursively

Class / interface

class MyClass {}
validate( new MyClass(), MyClass );  // ok
validate( new MyClass(), Bar );      // ByContractError: expected instance of Bar but got instance of MyClass

// Global interfaces by string
validate( new Date(), "Date" );         // ok
validate( node, "HTMLElement" );        // ok
validate( [ new Date() ], "Array.<Date>" ); // ok

Custom types

Value-based (recommended)

typedef( schema ) returns the schema directly — no global registry, no string indirection:

import { validate, typedef, contract } from "bycontract";

const HeroType = typedef({
  hasSuperhumanStrength: "boolean",
  hasWaterbreathing: "boolean"
});

validate( superman, HeroType );

const createHero = contract( [ HeroType ], hero => hero );

String registry (legacy)

import { validate, typedef } from "bycontract";

typedef( "#Hero", {
  hasSuperhumanStrength: "boolean",
  hasWaterbreathing: "boolean"
});

validate( superman, "#Hero" );  // ok

validate( { hasSuperhumanStrength: 42, hasWaterbreathing: null }, "#Hero" );
// ByContractError: property #hasSuperhumanStrength expected boolean but got number

validate( { hasWaterbreathing: true }, "#Hero" );
// ByContractError: missing required property #hasSuperhumanStrength

Union typedef:

typedef( "NumberLike", "number|string" );
validate( 10, "NumberLike" );    // ok
validate( null, "NumberLike" );  // ByContractError: expected number|string but got null

Custom validators

Extend the is object with custom predicates:

import { validate, is } from "bycontract";

is.email = ( val ) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test( val );

validate( "[email protected]", "email" );  // ok
validate( "not-an-email", "email" );    // ByContractError: expected email but got string

Exceptions

Every validation failure throws a ByContractError (extends TypeError):

import { validate, Exception } from "bycontract";

try {
  validate( 1, "NaN" );
} catch ( err ) {
  err instanceof Error;      // true
  err instanceof TypeError;  // true
  err instanceof Exception;  // true
  err.name;                  // "ByContractError"
  err.message;               // "expected nan but got number"
  err.code;                  // "EINVALIDTYPE"
}

Combinations

Validate functions that accept several distinct argument signatures:

import { validateCombo } from "bycontract";

const CASE1 = [ "string", TrackerOptions, "function" ];
const CASE2 = [ "string", null, "function" ];
const CASE3 = [ SpecOptions, TrackerOptions, "function" ];
const CASE4 = [ SpecOptions, null, "function" ];

function andLogAndFinish( spec, tracker, done ) {
  validateCombo( [ spec, tracker, done ], [ CASE1, CASE2, CASE3, CASE4 ] );
}

Throws when none of the cases match.

Production

Disable at runtime:

import { validate, config } from "bycontract";

if ( process.env.NODE_ENV === "production" ) {
  config({ enable: false });
}

Or swap the entire module with Webpack (zero-byte production build):

// webpack.config.js
const webpack = require( "webpack" );
const TerserPlugin = require( "terser-webpack-plugin" );

module.exports = {
  mode: process.env.NODE_ENV || "development",
  optimization: {
    minimizer: [
      new TerserPlugin(),
      new webpack.NormalModuleReplacementPlugin(
        /dist\/bycontract\.dev\.js/,
        "./bycontract.prod.js"
      )
    ]
  }
};
NODE_ENV=development npx webpack   # includes validation
NODE_ENV=production npx webpack    # strips validation entirely