tape-six-invariant
v1.0.0
Published
Zero-dependency invariant checks that materialize into real tape-six assertions under test, and stay inert (or configurable) in production.
Maintainers
Readme
tape-six-invariant 
tape-six-invariant lets you embed assert-style invariant checks in production or library code that materialize into real tape-six assertions when a tape-six run exercises that code — and stay inert (or do whatever you configure) otherwise.
Zero runtime dependencies. Works in Node, Deno, Bun, and browsers. ES modules, TypeScript bindings included. The library never imports tape-six — the two coordinate through a single global slot.
import check from 'tape-six-invariant';
export function transfer(from, to, amount) {
check(amount > 0, 'amount must be positive');
check(from.currency === to.currency, 'currencies must match');
// …
}- Under a tape-six run (the code is exercised by a test): each
check()becomes a counted assertion on the current test — in the plan, in TAP output, with its source location pointing at thecheck()call site. - In production (no tape-six): a configurable behavior. Default inert (does nothing). Overridable to throw, warn, defer to your own
node:assert, or anything you like.
The same call site means the same thing — an invariant — and is paid for only when something is listening.
Install
npm install --save tape-six-invarianttape-six is needed only to run the invariants as assertions (a dev/test concern), so it is not a dependency of this package.
Usage
Materialize under test
Any code reached — directly or transitively — from a tape-six test body routes its check() calls to the current test:
import test from 'tape-six';
import {transfer} from '../src/bank.js';
test('transfer enforces its invariants', t => {
transfer({currency: 'USD'}, {currency: 'USD'}, 10); // the two checks pass as assertions
t.pass('done');
});Configure production behavior
By default invariants are inert in production — they cost a single global-property read. Opt into enforcement at startup:
import {setAbsentBehavior, throwOnFail} from 'tape-six-invariant';
setAbsentBehavior(throwOnFail); // a failing check now throws InvariantErrorWith no behavior set — the default — a failing check is a no-op. Canned behaviors (exported functions, not magic strings):
| Behavior | Effect on a failing check |
| ------------- | ------------------------- |
| throwOnFail | throw InvariantError |
| warnOnFail | console.warn |
setAbsentBehavior(null) (or any non-function) clears a previously set behavior. Or pass your own (ok, message) => void — for instance, to defer to node:assert, bring your own import so it stays your dependency, not the package's:
import assert from 'node:assert';
setAbsentBehavior((ok, message) => assert.ok(ok, message));Skip expensive pre-check work
hasHost is an import-time snapshot — use it to avoid computing an argument that is only worth checking under a run:
import {check, hasHost} from 'tape-six-invariant';
if (hasHost) check(expensiveToCompute(), 'invariant holds');Correctness never depends on the snapshot — check() always reads the live slot.
Lazy messages
Pass a () => string thunk to build an expensive message only when it is needed:
check(list.every(valid), () => `invalid items: ${list.filter(x => !valid(x)).join(', ')}`);API
See the wiki for full docs. check is the default export (and a named export); everything else is named-only.
check(cond, message?)— record an invariant.messagemay be a string or a() => stringthunk. TypeScript signature isasserts cond.hasHost— import-time boolean: was a tape-six host present at load?setAbsentBehavior(fn)— set the no-host failure behavior;nullclears it (default: none, a no-op).throwOnFail,warnOnFail— canned absent behaviors.InvariantError— thrown bythrowOnFail.
Related packages
- tape-six — the test library these invariants materialize into.
- tape-six-proc — process-isolated test execution.
- tape-six-puppeteer / tape-six-playwright — browser automation.
Release notes
- 1.0.0 — Initial release:
check,hasHost,setAbsentBehavior, canned behaviors (throwOnFail/warnOnFail),InvariantError.
License
BSD-3-Clause © Eugene Lazutkin
