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

assertcheck

v0.5.5

Published

Negative Space Programming for TypeScript — declare invalid states, fail fast, trust the boundary.

Readme

AssertCheck

Negative Space Programming for TypeScript. Declare what cannot exist. Fail where it matters. Ship with confidence.

npm JSR License TypeScript Bun Documentation


The problem with "defensive" code

Most TypeScript codebases hide bugs behind if (!x) return. Silent failures. No trace. No context. Hours lost in production.

// Before — bad data propagates in silence
function chargeOrder(order: Order) {
  if (!order || !order.amount) return  // swallowed, never caught, never debugged
}
// After — invalid state is declared at the boundary
function chargeOrder(order: Order) {
  assert.notNil(order, "order is required")
  assert.positive(order.amount, "order amount must be positive")
  assert.equal(order.status, "pending", {
    msg:    "order must be pending before charge",
    actual: "order.status",
    note:   "call resetOrder() before retrying",
  })
  // from here: every assumption is verified, every invariant is explicit
}

Assertcheck makes invalid states impossible to ignore. Not a validator. Not a schema library. A contract system — at every boundary, for every assumption.


Why Assertcheck?

| | if/return | node:assert | zod / yup | Assertcheck | |---|---|---|---|---| | Fails loudly in dev | No | Yes | Yes | Yes | | Zero overhead in prod | No | No | No | No | | Type narrowing | No | No | Yes | Yes | | Structured, readable errors | No | Partial | Partial | Yes | | Chainable fluent API | No | No | No | Yes | | Works on functions/purity | No | No | No | Yes | | AI skills included | No | No | No | Yes |


Install

# npm / yarn / pnpm
npm install assertcheck
yarn add assertcheck
pnpm add assertcheck

# Bun
bun add assertcheck

# Deno / JSR
deno add jsr:assertcheck
bunx jsr add assertcheck

Quick start

Single assertions

import { assert } from "assertcheck"

assert.notNil(user, "user is required")
assert.equal(order.status, "pending", {
  msg:    "order must be pending before payment",
  actual: "order.status",
  note:   "call resetOrder() first",
})

Fluent chains

import { check } from "assertcheck"

check(users)
  .notEmpty("users list cannot be empty")
  .noNils("no null users allowed")
  .uniqueBy("id", "duplicate user IDs detected")
  .all(u => u.active, "all users must be active")
  .sortedBy("createdAt")

Error output that actually helps

When an assertion fires, you get a precise, ELM-inspired diagnostic — not a 40-line stack trace.

══════════════════ ● Order status mismatch ════════════════════

── values ──────────────────────────────────────────────────────
  + expected        "pending"
  ✗ order.status    "paid"

── note ────────────────────────────────────────────────────────
  call resetOrder() before retrying

════════════════════════════════════════════════════════════════

Deep equality failures include a structural diff, field by field:

══════════════════ ● Deep equality failed ═════════════════════

── diff ────────────────────────────────────────────────────────
  ·  id           "usr_123"
  ~  status
       expected   "active"
       actual     "banned"
  +  role         "admin"   ← missing

════════════════════════════════════════════════════════════════

Output adapts automatically:

  • Node / Bun / Deno — ANSI colours on TTY, plain text in pipes. Respects NO_COLOR.
  • Browser — collapsible console.groupCollapsed in DevTools.
  • CI — clean plain text, no escape codes.

Full API

Existence

| | | |---|---| | assert.nil(v) | Must be null or undefined | | assert.notNil(v) | Must not be null or undefined — narrows to NonNullable<T> | | assert.empty(v) | Must be empty (string / array / object / Map / Set) | | assert.notEmpty(v) | Must not be empty |

Type guards

| | Narrows to | |---|---| | assert.string(v) | string | | assert.number(v) | number | | assert.integer(v) | number (integer) | | assert.finite(v) | number (finite) | | assert.boolean(v) | boolean | | assert.array<T>(v) | T[] | | assert.object<T>(v) | T | | assert.func<T>(v) | T | | assert.instanceOf(v, Ctor) | Ctor instance |

Equality

| | | |---|---| | assert.equal(a, b) | Strict === | | assert.deepEqual(a, b) | Deep equality with structural diff |

Numerics

positive · negative · zero · greater · greaterOrEqual · less · lessOrEqual · withinRange · inDelta

Arrays (Ruby-inspired)

len · longerThan · shorterThan · includes · all · any · none · one · count · containsAll · containsNone · elementsMatch · subset · unique · uniqueBy · increasing · nonDecreasing · sortedBy · first · last · sumBy · noNils · flat · allInstanceOf · zippedWith · groupedBy · partition

Objects (Ruby Hash-inspired)

hasKey · hasKeys · hasExactKeys · hasOnlyKeys · hasValue · containsSubset · allValuesMatch · noNilValues · dig

Functions (mathematical properties)

returns · pure · idempotent · arity · mapsDistinct · homomorphic

Negation

assert.not(fn, ...args) — the only negation API. Wraps any assertion.

assert.not(assert.equal, user.role, "admin")
assert.not(assert.includes, errors, "FATAL")
assert.not(assert.hasKey, patch, "id")

Chainable API

import { check } from "assertcheck"

// Arrays
check(users)
  .notEmpty()
  .noNils()
  .uniqueBy("id")
  .all(u => u.active)
  .sortedBy("createdAt")
  .len(10)

// Objects
check(config)
  .hasKeys(["host", "port"])
  .noNilValues()
  .dig("database.pool.max", 10)

AssertionError

import { assert, AssertionError } from "assertcheck"

try {
  assert.equal(order.status, "pending")
} catch (err) {
  if (err instanceof AssertionError) {
    err.assertion // "equal"
    err.actual    // "paid"
    err.expected  // "pending"
  }
}

AI Skills — from day one

Assertcheck ships with AI skills so your AI assistant understands and applies the library's patterns automatically.

| Skill | What it does | |---|---| | assertcheck-audit | Audits existing code for missing or weak assertions | | assertcheck-feature | Designs a new assertion following the library's contracts | | assertcheck-refactor | Refactors code toward negative-space programming | | assertcheck-selector | Selects the right assertion for a given scenario | | assertcheck-spec | Writes invariant-driven specs |

# Install all skills
bunx skills add thonymg/assertcheck --skill='*'
npx skills add thonymg/assertcheck --skill='*'

# Or globally
bunx skills add thonymg/assertcheck --skill='*' -g

Learn more at vercel-labs/skills.


Project structure

src/
  index.ts    — barrel export
  types.ts    — shared types and interfaces
  env.ts      — runtime detection (Node / Bun / Deno / browser)
  error.ts    — AssertionError class
  format.ts   — ANSI/browser formatter, diff engine, block builder
  fail.ts     — internal fail() dispatcher
  assert.ts   — all assertion functions
  checker.ts  — chainable wrapper (check())
tests/
  assert.test.ts

Build

bun run build      # compile to ./dist
bun run typecheck  # tsc --noEmit
bun test           # run test suite

License

Apache License 2.0 — free to use, modify, and distribute commercially as long as you retain attribution. See LICENSE.


Built by Vagabond Studio

Vagabond Studio is a fully remote, senior-only collective of engineers and designers. We build TypeScript, Vue.js, Rails, and Django products from greenfield to production — and we stay until it ships right.

Assertcheck is one of the open-source tools we maintain as a demonstration of how we approach software: explicit contracts, zero defensive noise, and code that communicates intent at every boundary.

What we do:

  • Product engineering — TypeScript, Vue.js, Rails, Django. Full-stack from greenfield to production, or embedded in your existing codebase.
  • UI/UX design — Interfaces designed and engineered in the same team. No handoffs. No agency bloat.
  • Technical leadership — Architecture decisions, code reviews, and the kind of senior judgment that prevents six-month rewrites.

Who we work with:

Growing companies between 5 and 200 people who need craft-level output without building a full in-house team. Startups shipping their first real product. Scale-ups that have outgrown their MVP and need the codebase to match their ambitions.

Book a discovery call — 30 minutes, no pitch, just a real conversation about your project.

Or reach us directly: [email protected]

We take on 2–3 new clients per quarter.