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

@pwngh/economy-lab

v0.4.0

Published

A provably-solvent credits economy: wallets, payouts, subscriptions, and a marketplace, on one double-entry ledger.

Readme

economy-lab

A provably-solvent credits economy: wallets, payouts, subscriptions, and a marketplace, on one double-entry ledger.

ci runtime deps node license

On this page


Read the full documentation at economy-lab-docs.pages.dev, or try the live console.

Highlights

  • Zero runtime dependencies — pure TypeScript; the whole economy runs in-memory with no infrastructure.
  • Provably solventread.health() snapshots every solvency and integrity invariant, and proveEconomy re-derives them thoroughly for CI; make prove and make fuzz surface any leak or drift.
  • Tamper-evident by construction — every balance folds from an append-only, hash-chained, double-entry log under signed Merkle checkpoints.
  • Five backends — in-memory, Postgres, MySQL, Redis, and SQS run identical logic, pinned by a single conformance suite.
  • Safe by defaultsubmit returns an Outcome and never throws for a "no"; every request is idempotent.
  • Ops supervisor built in — the ./ops entry point watches the meter/logger ports for twelve incident signatures, remediates under guardrails, and writes a hash-chained audit trail; leaving it out of the composition is the off switch.

Quick start

Build an Economy, drive it through a single submit, and read derived state through read. boot() with no arguments wires a complete in-memory build — the store, a dev signer, a dev rate table, a flat fee policy, an in-memory processor, and a worker — so there is nothing to configure:

import { boot, topUp, toAmount, spendable } from '@pwngh/economy-lab';

const { economy, worker } = await boot();

// Credit a wallet after a card charge clears.
const outcome = await economy.submit(
  topUp({
    idempotencyKey: 'ord_ada_1',
    actor: { kind: 'system', service: 'payments' },
    userId: 'usr_ada',
    amount: toAmount('CREDIT', 5_000n),
    source: 'card',
  }),
);
outcome.status; // 'committed'

const balance = await economy.read.balance(spendable('usr_ada'));
balance.minor; // 5_000n — 50.00 CREDIT in minor units

await worker?.sweep(); // payouts, fees, checkpoints — one background pass

const report = await economy.read.health(); // every solvency & integrity invariant
report.conserved; // true

await economy.close();

Architecture

flowchart TB
    subgraph sync["request path, synchronous"]
        Client(["`**submit(operation)**`"]):::entry
        Ops["`**Operations**
        validate, authorize, post`"]:::proc
        Client --> Ops
    end

    Ledger[("`**Ledger**
    the book of record
    append-only, hash-chained, double-entry`")]:::truth
    Reads["`**Projections**
    balances, statements, derived`"]:::derived
    Outbox[["`**Outbox**
    domain events`"]]:::evt
    Worker{{"`**Worker**, asynchronous
    payouts, subscriptions, fees, checkpoints, relay, inbox`"}}:::worker

    Ops -- "debit = credit" --> Ledger
    Ledger -- "re-folded on read" --> Reads
    Ledger -- "same transaction" --> Outbox
    Worker -. "submit, sweep, checkpoint, apply" .-> Ledger

    classDef entry fill:#ffffff,stroke:#1f6feb,stroke-width:1.5px,color:#1f2328;
    classDef proc fill:#f6f8fa,stroke:#57606a,stroke-width:1px,color:#1f2328;
    classDef truth fill:#1f6feb,stroke:#0b4fc4,stroke-width:2px,color:#ffffff;
    classDef derived fill:#eaf6ec,stroke:#3fb950,stroke-width:1.5px,color:#1f2328;
    classDef evt fill:#f3eefb,stroke:#8957e5,stroke-width:1.5px,color:#1f2328;
    classDef worker fill:#f3eefb,stroke:#8957e5,stroke-width:1.5px,color:#1f2328;
    style sync fill:#f0f6ff,stroke:#1f6feb,stroke-width:1px,color:#57606a;

The same logic runs in-memory and on Postgres or MySQL through swappable engines (databases that enforce the invariants natively) and adapters (pluggable cache and event transports). One conformance suite holds them to identical behavior.

Guarantees

| Capability | Where | What it guarantees | | ---------------------------- | -------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | | Double-entry ledger | ledger.ts | A posting is rejected unless its debit and credit lines net to zero per currency; a balance is the sum of its lines. | | Tamper-evident history | chain.ts, integrity.ts | Each posting is hash-chained per account; a signed Merkle checkpoint anchors the whole ledger; proveChain locates any altered entry. | | Idempotent requests + outbox | economy.ts, worker/relay.ts | A retried request runs once: key, postings, and outbound event all commit in one transaction; duplicates replay. | | Marketplace + fee policy | operations/spend.ts, pricing.ts | A sale charges the buyer and pays the sellers in one balanced transaction; shares sum to 100%; the fee is injected policy. | | Payout saga + retries | operations/requestPayout.ts, worker/payouts.ts | The worker submits a payout; the settlement webhook settles it once (deduped); a stuck payout re-drives then reverses. | | Recurring subscriptions | operations/subscribe.ts, worker/subscriptions.ts | Each period bills once; an underfunded renewal lapses instead of overdrawing. | | Refunds & clawback | operations/refund.ts, operations/clawback.ts | A reversal restores each account the sale touched, booking any uncollectable remainder to a receivable. | | Settlement maturity gate | maturity.ts | Payouts release only funds settled past the chargeback window; fresh credits are held back until they mature. | | Spend-velocity risk gate | trust.ts | Recent spend is summed over a sliding window and checked against a limit before any money moves. | | Swappable storage | engines/, adapters/ | The same logic runs in-memory and on Postgres, MySQL, Redis, and SQS; one conformance suite runs against every backend. |

make prove and make fuzz attack these invariants after every operation and across every backend.

Ecosystem

One sibling package composes around the ledger at runtime, behind a stable seam — the lab runs unchanged without it. See the packages for where everything plugs in.

  • @pwngh/economy-edge — the runtime sibling: a real payout rail (Tilia) behind the same Processor port; an optional peer the lab composes without.

Two more packages serve the ledger without composing as siblings. @pwngh/money is vendored into src/ as the ledger's arithmetic, pinned by embedded conformance vectors — each boot proves the live database computes the same semantics before any posting trusts it. @pwngh/taskq is an optional relay backend: with TASKQ=1 the outbox relay rides taskq's task table beside the ledger, one at-least-once task per event, keyed by event id. The ops supervisor is not a package at all: it ships built in as the ./ops entry point.

Run it

make dev         # local HTTP server — in-memory, dev secrets, hot reload; zero setup
make start       # HTTP service against the configured environment
make worker      # background sweep loop
make test        # the full suite, zero infra, all in-memory
make check       # typecheck + eslint + prettier + test + golden trace (the CI gate)
make demo        # compose an economy and run a sample money flow
make demo-ops    # ops supervisor closed loop: a stuck payout is detected, swept, verified
make prove       # randomized invariant proof; exits non-zero on any leak or drift
make fuzz        # cross-backend differential — every backend must produce identical results
make backup      # data-only dump of every configured engine into backups/
make restore-drill # restore the newest dump into a scratch namespace and prove it
make db-clean    # drop the orphaned throwaway namespaces a killed run left behind

The bundled host process runs as an HTTP service (POST /submit, POST /webhooks/:provider, plus /healthz and /readyz) and a background worker (eleven sweeps on an interval). Every backend is selected by an environment variable; OPS=1 composes the ops supervisor around the worker, and Ops & runbooks holds one runbook per incident signature.

Performance

make bench measures submit throughput per backend and integrity cost as the ledger grows. In memory it runs tens of thousands of submits per second; on Postgres or MySQL each submit is its own transaction, so throughput is commit-bound (hundreds per second). Those are the general-path numbers: where a workload allows it, instance netting and the entitlement bitset remove the transaction and the round-trip respectively, which is worth orders of magnitude on the SQL backends. See Performance for the measured tables and how to read them.

Apps

  • apps/console — a demo admin UI driven by the live engine: accounts, the ledger feed, the payout board, and an integrity page, with a simulation panel to advance time and run the background jobs. Try it live.
  • apps/docs — the source of the documentation site.

Documentation

License

MIT — see LICENSE.md.