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

mememage

v0.1.8

Published

The JavaScript SDK for mememage-core — the JS implementation of the Mememage protocol (decode, verify, encode, encrypt/unlock), on parity with the Python reference.

Readme

mememage

The JavaScript SDK for the Mememage protocol. It is the JavaScript implementation of mememage-core. JavaScript developers can work with Mememage natively (decode, verify, and encode), with no Python dependency. It is the counterpart to the Python mememage package. The two are language bindings of one protocol.

Python is the reference. This SDK is never a second source of truth. Every operation mirrors a Python function, and a parity gate validates it against Python's own output. To maintain the code, you fix the Python core. The gate then tells you if the JS side must follow. So there is nothing here to chase down on its own.

Status: complete at core parity

The SDK covers the whole raw-core protocol. The parity gate checks each operation against Python.

  • decode (alias decodeBar) — read the bar and return { identifier, contentHash }. { allBars: true } returns every bar in the image (this mirrors decode(image, all_bars=True)). The SDK finds a bar wherever it sits: at the bottom, at a different height, offset to one side, or pasted into a larger image.
  • verify — check a record against an image and return { match, reason, supported }. This mirrors the api.py Verification result, with the same failure types (no bar, unsupported version, or hash mismatch). verifyWitnessed is the bare-boolean form. isSupportedHashVersion tells UNSUPPORTED apart from tampered.
  • encode — stamp a bar and build the record, byte-identical to mememage.encode. It returns { pixels, record, identifier, contentHash }. Pair it with toPngBytes to write the barred image as a real .png file (Node zlib or browser CompressionStream, still zero dependencies).
  • encryptField / decryptField / encode(…, {password, private}) / unlock / isEncrypted — field encryption (AES-256-GCM and PBKDF2). The envelope is byte-compatible with Python. A record encrypted in either language opens in the other.
  • loadPixels — flexible image input. In the browser, it accepts a File, a Blob, a canvas, an ImageData, or an <img>, decoded by the platform. In Node, it reads PNG directly, and JPEG through the optional jpeg-js peer. See below.
  • CLInpx mememage decode <img> and npx mememage verify <img> <record>.

No network, ever. Like the Python core, every operation is pure math over the values you pass in. There are zero dependencies and zero requests. You resolve an identifier to a record yourself.

The open hash model (schema-agnostic)

The SDK is schema-agnostic. It implements the open hash model. The content hash covers every field a record holds. This is what the raw API and ComfyUI produce. So the SDK verifies any adopter's record, whatever they store, with no knowledge of a particular chain's schema.

A curated integer hash version is a reference-implementation concern (for example the canonical chain's hash_version: 1, with its fixed field list). A record on one of those reads as UNSUPPORTED here. It is not tampered. This is the same verdict that ComfyUI's verifier and the core CLI give. Call isSupportedHashVersion(record) before verifyWitnessed to tell UNSUPPORTED apart from ALTERED.

Deliberately not here. Some features are reference-implementation features, out of raw-core scope: signing and AUTHENTICATED (the raw core's verify() checks integrity only), EMBODIED (dHash and luma grid), and the distributed watermark. These are the canonical chain's specific tamper-evidence technique. They need a record that carries a thumbnail and a luma_grid, and another adopter might use a different technique or none. So they belong to the reference implementation, not the core. They live only in the decoder site (docs/js). This package ships no signing, keychain, or network code. If they are ever ported to JS, they will be a separate surface with its own parity anchor (the reference implementation).

Use

npm install mememage
import { decode, verify } from "mememage";

// 1. decode the bar from image pixels (canvas getImageData().data, flat RGBA)
const bar = decode(pixels, width, height);
if (!bar) { /* NO BAR */ }
// -> { identifier: "mememage-…", contentHash: "…" }
// decode(pixels, w, h, { allBars: true }) -> every bar in the image, as a list

// 2. fetch the record for bar.identifier from wherever it lives (yours to
//    resolve), then produce a verdict — by math alone, no server needed:
const v = await verify(pixels, width, height, record);
if (v.match) {
  // WITNESSED — intact and matched to the image (integrity)
} else if (!v.supported) {
  // UNSUPPORTED — a hash model this SDK doesn't implement (e.g. a canonical-
  // chain record). NOT tampered; v.reason says where to verify it.
} else {
  // ALTERED (or no bar) — v.reason explains exactly what failed
}

Image sources — loadPixels

The codec is pixels-first, because byte-exact math needs raw RGBA. loadPixels(source) turns any reasonable source into { pixels, width, height }. decode and verify accept that object directly:

import { loadPixels, decode, verify } from "mememage";

// Browser — the PLATFORM decodes, so JPEG/WebP/HEIC all work (drop handler,
// <input type=file>, canvas, ImageData, <img>):
const bar = decode(await loadPixels(fileInput.files[0]));

// Node — PNG files natively (every minted original is a PNG), zero deps:
const v = await verify(await loadPixels("art.png"), record);

// Write side, end to end — encode accepts the same object form, and its
// result feeds toPngBytes directly (it carries pixels + width + height):
import { encode, toPngBytes } from "mememage";
import { writeFileSync } from "node:fs";
const r = await encode(await loadPixels("original.png"), { title: "my piece" });
writeFileSync("barred.png", await toPngBytes(r));
writeFileSync("record.json", JSON.stringify(r.record));   // r.identifier names it

// Node pipeline holding other formats — your image library already decodes;
// hand its raw RGBA straight in (e.g. sharp):
const { data, info } = await sharp("photo.jpg").ensureAlpha().raw()
  .toBuffer({ resolveWithObject: true });
const bar2 = decode({ pixels: data, width: info.width, height: info.height });

JPEG in Node needs one opt-in. jpeg-js is an optional peer. The default install stays zero-dependency, and nothing is auto-installed:

npm install mememage jpeg-js   # the npm analog of pip's `mememage[extras]`

With it installed, loadPixels("photo.jpg") and the CLI decode JPEG directly (a pure-JS decoder, MIT, with zero dependencies of its own). Without it, you get an error that names your three options: install jpeg-js, pass raw pixels from your image library (sharp, as above), or convert to PNG once. One note on honesty: JPEG decoding is never bit-exact across implementations. So bar recovery from a JPEG uses the codec's designed noise margin. This is the same guarantee that the browser's platform decoders live under, and a Pillow-anchored q80 vector parity-tests it.

No network, ever: a string source is a filesystem path, never a URL.

CLI

npx mememage encode art.png fields.json --out barred.png --record record.json
npx mememage decode art.png            # -> identifier  content_hash
npx mememage decode art.png --all      # every bar in the image
npx mememage verify art.png record.json  # -> WITNESSED / ALTERED / UNSUPPORTED (+reason)

Exit codes: 0 for verified or found, 1 for a negative verdict, 2 for a usage or input error.

The raw-core exports are available for lower-level use: extractBarScaleAware, extractBars, decodePayload, packPayload, extractIdentifier, normalizeIdentifier, computeContentHash, verifyWitnessed, encode, and contentIdentifier.

Parity

The codec and verify bodies derive from the parity-locked decoder in docs/js (the frozen decoder site), wrapped as ES modules. npm test validates the core layer against the Python core directly. decode-parity decodes bars that the core produced (gen-vectors.py). verify-parity recomputes hashes that the core stamped (gen-hash-vectors.py, with compute_content_hash), and it checks the encode output. The vectors cover sequential and even-fill layouts, custom prefixes, a 2x-resized image (the vertical scan), a bar pasted into a larger canvas (the full-canvas search), a two-bar image (allBars), the real V1 example soul, open-version records, a tamper negative, and the verify() failure taxonomy. Regenerate the vectors from the repo root with python3 test/gen-vectors.py and python3 test/gen-hash-vectors.py.

The parity suite and its Python-generated vectors live in the repo, not the npm tarball. Clone it to run npm test. Parity is against the Python core's code, not a pinned PyPI release. The gate re-proves it on every core change. The npm and PyPI version numbers move independently.

MIT.