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

@barter.game/protocol

v0.0.1

Published

Canonical JSON (JCS), ed25519 signing, doc types, and validators for the barter.game federated mutual-credit protocol. Consumed as TypeScript source; no build step. Runs identically under Bun, Node.js, and browser.

Readme

@barter.game/protocol

The shared protocol-primitives library of the reference implementation: canonical JSON, ed25519 signing, content hashing, TypeScript types, and runtime validators for every barter.game v1 document.

Do not confuse this package with the spec. The repo-root protocol/ directory is the normative contract — base.md (identity, canonical JSON, BaseDoc, request signing), bank-schema.md (document schemas and ledger semantics), bank-rpc.md (the RPC API). This package is TypeScript code implementing that contract. When the two disagree, the spec wins.

The entire library is one source file, src/index.ts, with no build step: main, types, and exports all point at the .ts source, and the build script is an echo no-op. Consumers import the TypeScript directly. Dependencies are pure JS and run identically under Bun, Node, and browsers: @noble/ed25519, @noble/hashes, @scure/base, ulid.

API surface

Canonical JSON (RFC 8785 / JCS)

Hand-rolled serializer — object keys sorted by UTF-16 code units, ECMAScript number-to-string formatting, -0 collapses to 0, throws TypeError on NaN/Infinity, undefined-valued keys dropped, arrays keep order, minimal escapes (\" \\ \b \t \n \f \r, \uXXXX for other control chars).

| Export | What it does | | --- | --- | | canonicalize(value) | Canonical JSON string of any JSON value | | canonicalBytes(value) | UTF-8 bytes of the canonical form; a string input is encoded as-is, not re-serialized | | canonicalizeWithoutSig(doc) | Canonical form with the top-level sig field removed (nested sig fields are kept) |

Crypto & hashing

| Export | What it does | | --- | --- | | genKeyPair() / publicKeyOf(priv) | ed25519 keypair generation / public-key derivation, with base58 pubkey | | signBytes(msg, priv) / verifyBytes(msg, sig, pub) | Raw ed25519 over bytes; base58 signatures; verify returns false (never throws) on malformed input | | signDoc(doc, priv) / verifyDoc(doc, sig, pub) | ed25519 over sha256(canonicalizeWithoutSig(doc)) | | hashDoc(doc) | Content address: base58(sha256(canonicalBytes(canonicalizeWithoutSig(doc)))) — the same preimage signDoc commits to, so a doc's hash is stable whether or not it is signed; embedded docs keep their own sig inside the preimage | | sha256Base58(s) | base58(sha256(utf8(s))) | | base58Encode / base58Decode | Bitcoin-alphabet base58 | | newUlid() | Fresh ULID |

Types

| Export | What it covers | | --- | --- | | BaseDoc, DocType, AnyDoc | Common envelope (type, pubkey, ulid, optional sig); DocType is the 10-value union voucher \| account \| credit \| debit \| signature \| order \| offer \| mandate \| address \| post | | Voucher, Account | Issued voucher (incl. optional due, expires, limit, integer, and images — up to 8 content-addressed MediaRefs, images[0] the icon and images[1] the square card by convention, overridable by a later voucher_meta post) and holder account | | BankRecord, RecordDetails | On-ledger credit/debit record and the hashed-away details (pair, deal_id, coordinator, holder, account) | | Order, OrderSide, Offer | Holder trade authorization and its published projection | | Mandate, Signature | Deal settlement mandate; status/ack signature doc (ready \| hold \| settle \| reject) | | Address | Bank address record | | Post | Voucher-anchored post (body_md, media/icon/square MediaRefs, deprecated inline icon_svg/square_svg, voucher_meta release flag); reply_to/repost embed the full parent Post, signatures included — see post-feed.md | | Base58PubKey, Base58Signature, Base58SHA256, ULID | String aliases used throughout |

Media refs

A MediaRef is "<base58(sha256(bytes))>.<ext>" — a content-addressed reference to an image blob in a bank's media vault, with the served Content-Type derived from the extension (post-feed.md §5).

| Export | What it does | | --- | --- | | MediaRef | String alias for the "<hash>.<ext>" form | | MEDIA_EXT_TYPES | Allowed extensions (svg, png, jpg, jpeg, webp, gif) → the Content-Type each serves as | | extForContentType(ct) | Content-Type → canonical extension, or null for anything outside the table | | parseMediaRef(ref) | { hash, ext }, or null when the string is not a well-formed ref (unknown extension, implausible hash); a legacy bare hash parses as null | | mediaRefHash(ref) | The content hash of a ref — or the string itself for a legacy bare hash | | collectMediaRefs(post) | Every ref a post commits to across its whole embedded reply_to/repost tree, de-duplicated — the set a bank checks for presence at intake, and the set a client copies over before a cross-bank repost |

Constants

Protocol-level caps, enforced by the validators at every bank (not bank policy):

| Export | Caps | | --- | --- | | MAX_VOUCHER_IMAGES = 8 | Voucher.images entries (validateVoucher) | | MAX_POST_MEDIA = 12 | media entries per post (validatePost) | | MAX_POST_EMBED_DEPTH = 8 | reply_to/repost nesting a validator will walk | | MAX_POST_SVG_CHARS = 8192 | Each deprecated inline icon_svg/square_svg field, in UTF-16 code units |

The reference bank additionally caps the whole embedded tree of a submitted post at 64 media refs — that is intake policy in packages/bank-core, not a protocol export.

Validators

All validators throw ValidationError on failure and return the narrowed type on success. They check structure and encodings per bank-schema.md — not business state (balances, deal progress), which is the bank's job.

| Export | Notes | | --- | --- | | validateBaseDoc(d) | Shape + base58 pubkey + ULID | | validateVoucher(d, bankPubkey) | Also enforces voucher.bank === bankPubkey | | validateAccount / validateOrder / validateOffer / validateRecord / validateMandate / validateSignature / validateAddress | Per-type required fields, min <= max, positive rate/amount, http(s) URLs, non-empty records, etc. | | validatePost(d, depth?) | Sync shape validation of a Post and, recursively, every embedded ancestor (depth-capped at MAX_POST_EMBED_DEPTH); does not verify embedded signatures | | verifyPostTree(post) | The signature half: verifies the author signature of the post and of every embedded reply_to/repost against each post's own pubkey; a missing sig anywhere fails. A bank must call both this and validatePost (post-feed.md §2) | | isValidBase58(s) / isValidUlid(s) | Boolean predicates | | offerSideFromOrderSide(side) | Helper: project an OrderSide to an Offer side (drops the private account hash) |

How it is consumed

  • apps/bank-aws (Node / Bun): the bank host imports the TypeScript source directly through the workspace (@barter.game/protocol) — no build or sync step, on the Lambda and in the local server alike.
  • apps/web (browser): does not import this package. It ships a hand-compiled vendored copy, apps/web/protocol.js, whose bare imports resolve through the import map in apps/web/index.html (esm.sh). To regenerate after changing src/index.ts: run tsc -p tsconfig.web.json (emits apps/web/index.js per tsconfig.web.json), then manually rename the output to protocol.js. No script automates this — if you change the source and skip this step, the web app silently keeps the old logic. test/web-mirror.test.ts is the tripwire: it imports apps/web/protocol.js and asserts byte-for-byte canonicalize/hash/sign agreement with src/index.ts.

Tests & the parity invariant

| Command (from this directory) | What it runs | | --- | --- | | bun test | test/protocol.test.ts under Bun: golden canonicalization vectors, canonicalizeWithoutSig semantics, non-finite number rejection, ed25519 roundtrips + tamper/wrong-key/malformed-input cases, signDoc/hashDoc determinism, ULID/base58 helpers, and accept/reject cases for every validator | | bun test test/web-mirror.test.ts | test/web-mirror.test.ts (part of the bun test run): imports the vendored browser copy apps/web/protocol.js and asserts it canonicalizes, hashes, signs, and verifies identically to src/index.ts |

From the repo root: bun run test, or bun run test:all for the protocol suite plus the bank's KvStore contract suite.

Cross-runtime canonicalization parity is the load-bearing invariant of the whole system. Every hash and every signature is computed over canonical bytes. The bank canonicalizes under Node, the web client under a browser engine, the tests under Bun. If any two runtimes ever disagree on a single canonical byte, hashes stop matching and signature verification fails across implementations. The golden vectors in test/fixtures/canonical/vectors.json and the web-mirror test are the fences that catch this.

Porting to another language

Treat src/index.ts as the executable reference for the canonicalization rules in base.md:

  1. Port the canonicalizer exactly — UTF-16 code-unit key sort (not locale, not byte-wise UTF-8), the escape table above, -0 → 0, reject non-finite numbers, drop absent/undefined fields. The subtle part is number formatting: RFC 8785 requires ECMAScript Number::toString (shortest round-trip) — a naive printf-style formatter will not match.
  2. Validate against the golden vectors in test/fixtures/canonical/vectors.json — byte-for-byte equality on every canonical string.
  3. Keep the primitives equivalent: ed25519 (RFC 8032) over the SHA-256 of the sig-less canonical form for doc signatures; SHA-256 of the full canonical form for content hashes; Bitcoin-alphabet base58 for all encodings; ULIDs for ids.

Constraints for contributors

  • No runtime-specific APIs in this package: no Node fs/path/Buffer/process.env, no DOM. src/index.ts must run unchanged under Bun, Node, and browsers.
  • Dependencies must stay pure-JS and cross-runtime (the current four are; keep it that way).
  • Any change to the canonicalizer requires new golden vectors in vectors.json and a green bun run test:all.
  • After changing src/index.ts, regenerate the vendored apps/web/protocol.js (see above).
  • Keep it a single source file with no build step; consumers import the .ts directly.