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

@thomasfosterau/argon2

v0.1.1

Published

argon2id password hashing for edge runtimes (Cloudflare Workers, Deno, browsers) — the RustCrypto argon2 crate compiled to WebAssembly, synchronous and dependency-free.

Readme

@thomasfosterau/argon2

argon2id password hashing that works on Cloudflare Workers — the RustCrypto argon2 crate compiled to wasm32-unknown-unknown, wrapped in a small TypeScript API.

npm install @thomasfosterau/argon2
import { hashPassword, verifyPassword } from "@thomasfosterau/argon2";

const hash = await hashPassword("correct horse battery staple");
// "$argon2id$v=19$m=19456,t=2,p=1$<salt>$<hash>"

// `verifyPassword` takes a single object, not positional arguments.
await verifyPassword({ hash, password: "correct horse battery staple" }); // true
await verifyPassword({ hash, password: "hunter2" }); // false

verifyPassword returns false — it never throws — for a malformed or foreign stored value (a legacy pbkdf2-sha256$… hash, say), so one bad row fails verification instead of failing the request. That is deliberate, but it means a call with the wrong argument shape also returns false silently, which looks identical to a wrong password. Destructure the object.

No dependencies, and nothing to configure. The functions return Promises so they drop into password-hashing contracts that expect async (Better Auth's emailAndPassword.password.{hash,verify}, for one), but the work underneath is synchronous.

Why this exists

Node-native argon2 bindings don't run on workerd, and the pure-JS implementations are slow enough to blow a request's CPU budget. The usual wasm route has its own problem on this runtime, though:

  • The computation must be synchronous. workerd has no async task pool, so anything that wants to spawn a Worker is refused outright. This module does the hashing on the calling thread.
  • Instantiation must happen at startup. Workers permits synchronous WebAssembly.Module construction while a module is being evaluated and forbids it inside a request. The wasm is therefore compiled once at module scope — moving it into a lazy per-call path would break on the platform while still passing every local test.
  • The wasm must not be an asset import. Importing a .wasm file needs a bundler-specific suffix (?url / ?inline for Vite, ?module / ?init for the Cloudflare rolldown plugin). Instead the bytes are embedded as a base64 string constant — plain JavaScript, with no resolution behaviour to get wrong, identical under every bundler and test runner.

The published dist/ already contains that constant, so consumers need no Rust toolchain.

Parameters

OWASP's second recommended argon2id option, for memory-constrained environments: 19 MiB, 2 iterations, 1 degree of parallelism — measured at ~28 ms per hash inside a Workers isolate.

Salts are 16 random bytes from crypto.getRandomValues (the crate is built without rand_core, so the target needs no getrandom syscall surface).

Cost parameters are encoded into every PHC string and verifyPassword reads them from the stored hash rather than from the constants above, so raising them does not invalidate existing hashes.

Development

Needs a Rust toolchain with the wasm target:

rustup target add wasm32-unknown-unknown

From the repository root:

vp run -r build   # cargo build -> src/wasm.generated.ts -> dist/
vp check          # format, lint, type-check
vp run -r test    # Vitest, against the real compiled wasm

src/wasm.generated.ts is not committed: it is regenerated from src/lib.rs by the build:wasm task, which every other task depends on, so the artifact can never drift from the Rust source.

License

MIT