npm package: @fuzdev/blake3_wasm
Description: BLAKE3 hashing compiled to WASM
Author: Ryan Atkinson
Latest version: 0.1.1

# @fuzdev/blake3_wasm

[BLAKE3](https://en.wikipedia.org/wiki/BLAKE_(hash_function)#BLAKE3) hashing
for TypeScript/JS, compiled to WASM from the
[`blake3`](https://crates.io/crates/blake3) Rust crate,
[@BLAKE3-team/BLAKE3](https://github.com/BLAKE3-team/BLAKE3).

## Install

Two builds with the same API:

```bash
npm i @fuzdev/blake3_wasm        # SIMD — faster, 45 KB
npm i @fuzdev/blake3_wasm_small  # no SIMD — smaller, 32 KB
```

Use [`@fuzdev/blake3_wasm`](https://www.npmjs.com/package/@fuzdev/blake3_wasm)
unless optimizing bundle size (or using Bun with its current SIMD issue).
It enables [blake3](https://crates.io/crates/blake3)'s WASM SIMD optimizations
and is 2-3x faster at 64KB+ inputs on Deno and Node.
[`@fuzdev/blake3_wasm_small`](https://www.npmjs.com/package/@fuzdev/blake3_wasm_small)
is ~30% smaller but loses the 2-3x SIMD gains
(but it's up to ~2.5x faster on Bun, which has a WASM SIMD problem).
See [benchmarks](#benchmarks) for details
(includes a comparison to the [SIMD-lacking](https://github.com/connor4312/blake3/issues/56)
[@connor4312/blake3](https://github.com/connor4312/blake3),
`blake3-wasm` on npm).

Also available via `npm:` specifier in Deno (jsr coming soon, user requests will speed it up):

```ts
import { hash } from 'npm:@fuzdev/blake3_wasm';
```

Only import one — importing both loads two separate WASM modules.
To swap builds without changing imports, use a bundler alias:

```ts
// vite.config.ts — use the small build everywhere
export default {
	resolve: {
		alias: { '@fuzdev/blake3_wasm': '@fuzdev/blake3_wasm_small' },
	},
};
```

## Usage

```ts
import { Blake3Hasher, derive_key, hash, keyed_hash } from '@fuzdev/blake3_wasm';

// one-shot hash — returns 32-byte Uint8Array
const digest = hash(new TextEncoder().encode('hello'));
console.log(digest.length); // 32

// keyed hash (MAC) — key must be 32 bytes
const key = new Uint8Array(32);
const mac = keyed_hash(key, new TextEncoder().encode('hello'));

// key derivation
const derived = derive_key('my-app', new TextEncoder().encode('secret'));

// streaming hasher — same result as one-shot above
const hasher = new Blake3Hasher();
hasher.update(new TextEncoder().encode('hel'));
hasher.update(new TextEncoder().encode('lo'));
const result = hasher.finalize(); // same bytes as `digest`
hasher.free(); // release WASM memory, or use `using` — see API below
```

### Browser

In browsers, call `init()` once before using any hash functions.
It's a no-op if already initialized, so calling it unconditionally is safe.

```ts
import { hash, init } from '@fuzdev/blake3_wasm';

await init();

const digest = hash(new TextEncoder().encode('hello'));
```

Node and Deno initialize automatically — `init()` is only needed in browsers.
The browser entry uses `new URL('./blake3_wasm_bg.wasm', import.meta.url)` internally,
which Vite and webpack handle automatically. Other bundlers may need a plugin for WASM assets.

**Vite**: Exclude from dependency pre-bundling so the `.wasm` file resolves correctly:

```ts
// vite.config.ts
export default {
	optimizeDeps: {
		exclude: ['@fuzdev/blake3_wasm'],
	},
};
```

Without this, Vite pre-bundles the JS into `.vite/deps/` but doesn't copy the WASM binary alongside it,
causing a 404 at runtime. This is a [common Vite issue](https://vite.dev/guide/dep-pre-bundling)
with WASM packages.

For synchronous initialization in Web Workers, use `init_sync`:

```ts
import { init_sync } from '@fuzdev/blake3_wasm';

const wasm = await fetch('/blake3_wasm_bg.wasm').then((r) => r.arrayBuffer());
init_sync({ module: wasm });
```

### Hashing a `ReadableStream`

```ts
import { derive_key_stream, hash_stream, keyed_hash_stream } from '@fuzdev/blake3_wasm';

// file: File from <input>, drop event, or fetch Response
const digest = await hash_stream(file.stream());
const mac = await keyed_hash_stream(key, file.stream());
const derived = await derive_key_stream('my-app', file.stream());
```

## API

All functions return 32-byte (256-bit) digests. BLAKE3 supports variable-length output (XOF)
but this API does not currently expose it — 32 bytes covers most use cases and XOF may be added
in the future (open a [discussion](https://github.com/fuzdev/blake3/discussions) if you'd like it).

### One-shot functions

```ts
hash(data: Uint8Array): Uint8Array
```

Returns 32-byte digest.

```ts
keyed_hash(key: Uint8Array, data: Uint8Array): Uint8Array
```

Keyed hash (MAC). Throws if key is not exactly 32 bytes.

```ts
derive_key(context: string, key_material: Uint8Array): Uint8Array
```

Key derivation. Returns 32 bytes.

### Streaming hasher

```ts
const hasher = new Blake3Hasher();
```

Create a hasher for plain hashing.

```ts
Blake3Hasher.new_keyed(key: Uint8Array): Blake3Hasher
```

Keyed hasher (MAC). Throws if key is not exactly 32 bytes.

```ts
Blake3Hasher.new_derive_key(context: string): Blake3Hasher
```

Hasher in key derivation mode.

```ts
hasher.update(data: Uint8Array): void
```

Feed data incrementally.

```ts
hasher.finalize(): Uint8Array
```

Returns 32-byte digest. Non-destructive — can call repeatedly.

```ts
hasher.finalize_and_reset(): Uint8Array
```

Finalize and reset in one call.

```ts
hasher.reset(): void
```

Reset to initial state. Preserves keyed/derive mode.

```ts
hasher.free(): void
```

Release WASM memory. Supports [`using`](https://github.com/tc39/proposal-explicit-resource-management)
for automatic cleanup:

```ts
using hasher = new Blake3Hasher();
hasher.update(data);
return hasher.finalize();
// hasher.free() called automatically
```

### Stream functions

```ts
hash_stream(stream: ReadableStream<Uint8Array>): Promise<Uint8Array>
```

Hash a stream. Returns 32-byte digest.

```ts
keyed_hash_stream(key: Uint8Array, stream: ReadableStream<Uint8Array>): Promise<Uint8Array>
```

Keyed hash a stream. Key must be exactly 32 bytes. Returns 32-byte digest.

```ts
derive_key_stream(context: string, stream: ReadableStream<Uint8Array>): Promise<Uint8Array>
```

Derive-key hash a stream. Returns 32-byte digest.

Stream functions handle hasher cleanup automatically, even if the stream throws mid-read.

## Benchmarks

Cross-runtime results (Deno, Node.js, Bun, Wasmtime) are available in the
[GitHub repository](https://github.com/fuzdev/blake3/blob/main/benches/results/report.md).

## License

[MIT](https://github.com/fuzdev/blake3/blob/main/LICENSE)