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

pruvon

v0.6.0

Published

A simple behavior driven development test tool

Readme

Pruvon is a Concordion-style BDD tool: you write a spec describing expected behavior as a plain table (HTML or Markdown), pair it with a small fixture that wires each row to your real code, and Pruvon runs it — producing the same document back with every example colored green or red.

The spec is the documentation, the test, and the report. Product owners can read it; your CI can gate on it.

Why senior developers reach for it

  • Zero test-framework ceremony. No describe/it, no assertion DSL, no config file. A table and a function. The engine is ~40 lines of readable async ESM (src/run-table.js).
  • Specs are the source of truth. Examples live in prose your domain experts can review in a PR, not buried in expect(...) calls only engineers read.
  • Drops into any Node project. Plain ESM, TypeScript, or a NestJS app resolving services through its DI container — the fixture is just a function, so it doesn't care how your code is wired.
  • CI-native by design. One process-exit contract, no reporter plugins to install.
  • Tiny, auditable surface. Three dependencies (cheerio, glob, markdown-it), no build step, no runtime magic.

Install

npm install pruvon

Requires Node.js ≥ 20.

Quick start

1. Write a specbasket.pruvon.md. Each row is one call: every column but the last is an argument, the last is the expected result. The fence tag names the fixture function to run.

# Shopping basket

```pruvon:total
| Item  | Qty | Unit | Total |
|-------|-----|------|-------|
| Apple | 3   | 2    | 6     |
| Pear  | 2   | 5    | 10    |
```

2. Pair a fixturebasket.pruvon.fixture.js. One export per fence tag. Cells arrive as strings; functions may be async.

export function total(args) {
  const [, qty, unit] = args;
  return Number(qty) * Number(unit);
}

3. Run it.

npx pruvon

Pruvon writes a *.pruvon.result.html report next to each spec, with every result cell colored green (pass) or red (fail, annotated expected X but was Y). Result files are generated output — they're .gitignored and never hand-edited.

Features

| | | |---|---| | Markdown or HTML specs | Author in GFM Markdown (```pruvon:<fn> fenced tables) or raw HTML (<table data-execute="<fn>">). Both run through the exact same engine. | | Async fixtures | Fixture functions are awaited, so a row can hit a database, call a service, or resolve a DI container before comparing. | | CI gate exit code | 0 when every row of every spec passes, 1 on any failure, thrown fixture, or missing pairing — drop npx pruvon straight into a pipeline. | | Framework-agnostic | Ships as pure ESM; works from plain Node, TypeScript, or CommonJS apps. See the NestJS demo resolving a service via NestFactory.createApplicationContext. | | Isolated rows | Each row runs in its own try/catch — one throwing or unmatched fixture fails only that row, not the whole spec. | | Lifecycle hooks | Optional beforeExample/afterExample, beforeSpecification/afterSpecification, and beforeSuite/afterSuite — reset state between examples, share a resource across one spec, or set up/tear down once for the whole run. | | Zero build | No transpile step, no linter config, no plugins. |

How it works

 Spec (.pruvon.html / .pruvon.md)  +  Fixture (.pruvon.fixture.js)
                          │
                          ▼
                   pruvon engine
                          │
                          ▼
        Result (.pruvon.result.html) — green / red

Discovery globs **/*.pruvon.{html,md}, resolving each spec's fixture by naming convention (<name>.pruvon.html and/or .md both pair with <name>.pruvon.fixture.js). Markdown is rendered to HTML first; from there HTML and Markdown specs share one execution path.

Writing specs in detail

HTML specs (*.pruvon.html)

A table's data-execute attribute names the fixture function called for each of its rows.

<table data-execute="sum">
  <tr><th>Operand 1</th><th>Operand 2</th><th>Result</th></tr>
  <tr><td>0</td><td>1</td><td>1</td></tr>
</table>

Markdown specs (*.pruvon.md)

GFM tables can't carry a data-execute attribute, so wrap the table in a fenced block tagged pruvon:<fnName>:

```pruvon:sum
| Operand 1 | Operand 2 | Result |
|---|---|---|
| 0 | 1 | 1 |
```

Everything else in the file (headings, paragraphs, other code blocks) renders as normal Markdown, so a spec doubles as living documentation.

Fixtures (*.pruvon.fixture.js)

An ES module exporting one function per data-execute / fence name. Each function receives the row's argument cells as an array of strings and returns (or resolves to) the value compared against the last cell:

export function sum(args) {
  return args.map(Number).reduce((a, b) => a + b, 0);
}

Lifecycle hooks

A fixture (or, for the suite scope, a dedicated file) can export optional hook functions that run around your examples. All of them may be async; none are required — an undeclared hook is simply skipped.

| Hook | Runs | Declared in | Example | |---|---|---|---| | beforeExample / afterExample | Once per table row | the spec's fixture | examples/before-after-example/ | | beforeSpecification / afterSpecification | Once per spec file, around all of its tables | the spec's fixture | examples/before-after-specification/ | | beforeSuite / afterSuite | Once for the whole run, around every spec | pruvon.suite.js at the --cwd root | examples/before-after-suite/ |

Per-example: beforeExample / afterExample

Called before/after each row's fixture function, useful for resetting state so examples don't leak into one another. afterExample always runs, even if the row's function throws.

export function beforeExample({ fnName, args }) {
  cache.clear();
}

export function afterExample({ fnName, args, actual, passed, error }) {
  // e.g. tear down a per-row stub
}

export function record(args) { /* ... */ }

If either hook throws, only that row fails — the same isolation as a throwing fixture function.

Per-specification: beforeSpecification / afterSpecification

Called once for a whole spec file, before its first table runs and after its last one finishes — handy for a resource that's expensive to set up but safe to share across every table in that file (e.g. seeding an in-memory catalog once instead of per table).

export function beforeSpecification() {
  db.open();
  db.seed(fixtures);
}

export function afterSpecification() {
  db.close();
}

If either hook throws, the whole spec is reported as errored (like a missing fixture pairing), and none of its rows are counted as passed or failed.

Per-suite: beforeSuite / afterSuite

A run's "suite" spans every spec pruvon discovers — potentially many independent fixture files — so this hook isn't declared on a fixture. Instead, drop an optional pruvon.suite.js at the directory passed as --cwd:

// pruvon.suite.js
export function beforeSuite() {
  server.start();
}

export function afterSuite() {
  server.stop();
}

It's loaded once, before any spec is discovered. If beforeSuite throws, no spec runs at all; pruvon prints ✗ suite hook failed: <message> and exits 1 without writing a report. afterSuite always runs after every spec finishes, even if one of them failed.

CLI

npx pruvon [options]

| Option | Description | |---|---| | --cwd <dir> | Directory to discover specs from (default: current directory). | | --pattern <glob> | Glob for spec files (default: **/*.pruvon.{html,md}). | | --help | Show usage. |

Exit code is 0 if every discovered spec's every row passed (or no specs were found), and 1 if any row failed, any fixture threw, or any spec had no paired fixture — making it a drop-in CI gate.

Examples & demos

  • Tutorial — a full illustrated walkthrough of the discuss → document → instrument → code loop.
  • examples/basket/ (arithmetic, one fixture paired to both an HTML and a Markdown spec), name-splitter/ (the Concordion getting-started example), and the three lifecycle hooks demos (before-after-example/, before-after-specification/, before-after-suite/) — all running against this repo's engine source directly.
  • demos/standard-project — a minimal plain-ESM Node project that installs pruvon from npm like a real consumer would.
  • demos/nestjs — a CommonJS NestJS app whose fixture resolves a service through Nest's DI container, showing Pruvon works regardless of the host project's module system.

Contributing

Run the engine's own test suite with npm test (Node's built-in test runner — no external framework).

Reports

Every run writes a pruvon-report.html in <cwd>, listing every discovered spec with its pass/fail count and a link to that spec's own *.pruvon.result.html — one file to open instead of one per spec. Like the per-spec result files, it's generated output (.gitignored), regenerated on every run.

Tracking results in git

By default, *.pruvon.result.html and pruvon-report.html are generated output — you're expected to .gitignore them and regenerate on every run. If you'd rather have the latest pass/fail state visible just by browsing the repo on main (no CI run needed), commit specific result files instead: remove them from your .gitignore (or add a ! negation for just the ones you want tracked), then run pruvon --track-results to get a warning listing any result file that's still being caught by .gitignore. See examples/tracked-results/ — its tracked-results.pruvon.result.html is committed, so the colored cells are visible straight from GitHub's file view.

Continuous Integration

Running under GitHub Actions (i.e. when $GITHUB_STEP_SUMMARY is set) also appends a pass/fail table to the run's Job Summary, visible directly on the run's page — no flag needed. GitHub sanitizes raw HTML/inline styles in Job Summaries, so this is a plain Markdown table (✅/❌ per spec) rather than the colored pruvon-report.html; upload pruvon-report.html and the *.pruvon.result.html files as a build artifact (e.g. via actions/upload-artifact) alongside it to keep the full colored report browsable from the run too.

Contributors

@francoispetitit

License

MIT