Skip to main content

What We Learned

blake3-wasm has exactly two published versions: 2.1.5 and 3.0.0. Version 3.0.0 introduced a mandatory await load() call before any hashing — the WASM module loads asynchronously. Version 2.1.5 loads the WASM module synchronously at import time. This seemingly minor API difference has cascading consequences:
  • 2.1.5 (sync): fingerprint() and verify() are synchronous functions. Callers use them inline: const fp = fingerprint(unit). Batch fingerprinting is a simple loop. The SPUH header can be computed in the same synchronous pipeline as encoding.
  • 3.0.0 (async): Every function that touches Blake3 becomes async. Callers need await. Batch fingerprinting requires Promise.all() or sequential await in a loop. The entire stratt publish pipeline becomes async. Test setup needs beforeAll(async () => { await load(); }).

The Decision

Pin to blake3-wasm@2.1.5 (exact version, no caret, no tilde) per DR-05. The sync API is the right default for a hashing library — the operation is CPU-bound, not I/O-bound. The async ceremony in 3.0.0 is an artefact of the WASM loading mechanism, not a performance benefit. The FALLBACK.md documents the SHA-256 migration path if blake3-wasm is abandoned. Web Crypto’s crypto.subtle.digest() IS async, so the fallback path would require the API to become async — but that’s a bridge to cross only if needed, and it would be a major version bump.

Applicability

When pinning WASM-based dependencies: check whether the latest version forces an async initialisation step. If the operation is fundamentally synchronous (hashing, encoding, compression), prefer the synchronous API version. The async wrapper can always be added by the caller; the sync API cannot be recovered from an async-only library.