ZipKit runs in the browser using the same Wasm engine as Node/Bun. The only
concern is making sure the bundler ships and resolves the .wasm asset.
The Emscripten module locates its .wasm relative to itself via import.meta.url:
new URL('zipkit-engine.wasm', import.meta.url)Modern bundlers understand this pattern and emit the .wasm as an asset
automatically — no configuration needed with:
- Vite (dev and build)
- webpack 5 (
asyncWebAssembly/ asset modules) - esbuild (with
--loader:.wasm=fileor the default asset handling) - Rollup (
@rollup/plugin-urlor native asset handling)
import { gzip, zstd } from '@myrialabs/zipkit';
const compressed = await zstd(new TextEncoder().encode(text));The engine is instantiated lazily on first use and cached for the page lifetime.
A minimal Vite app is in examples/browser. It depends on
the local repo via file:../.., so build the package once before installing the
example:
bun run build
cd examples/browser
bun install
bun run devOpen the Vite URL it prints, usually http://localhost:5173. Do not open
examples/browser/index.html directly as a file:// URL; browser module loading
and Wasm fetches are blocked by CORS/security rules outside an HTTP dev server.
The example's vite.config.ts allows Vite to serve the local repo's committed
engine/dist/*.wasm file during development.
The combined engine is ~1.4 MB of .wasm because it contains every encoder. It's
loaded once and cached, and await import()-ing ZipKit keeps it off your initial
JS bundle. For size-sensitive apps:
- Lazy-load ZipKit only on the route/interaction that needs it
(
const { gzip } = await import('@myrialabs/zipkit')). - Prefer gzip/zlib/deflate via
@myrialabs/zipkit, which use the browser's nativeCompressionStreamand don't touch the Wasm engine at all.
Roadmap: per-codec Wasm splitting (a small
coremodule plus lazy zstd/brotli/lzma modules) is planned so browser apps can ship only the codecs they use. Until then, the combined engine is the single artifact.
compressionStream('gzip' | 'zlib' | 'deflate') returns the browser's native
CompressionStream — true streaming with zero Wasm. Other codecs buffer and use
the engine. See streaming.md.
@myrialabs/zipkit bridges the browser's FileSystemFileHandle to the streaming ZIP
writer, so you can zip large local files straight to disk without reading them
all into memory:
import { zipToFileHandle, entriesFromFileHandles } from '@myrialabs/zipkit';
const out = await window.showSaveFilePicker({ suggestedName: 'archive.zip' });
const picked = await window.showOpenFilePicker({ multiple: true });
await zipToFileHandle(out, entriesFromFileHandles(picked));The module imports cleanly everywhere (handles are typed structurally); it just
needs real handles at call time, so use it behind a showSaveFilePicker check.
@myrialabs/zipkit targets Node/Bun worker_threads. In the browser it transparently
falls back to inline (main-thread) execution. To compress off the main thread in a
browser, run ZipKit inside your own Web Worker.
The engine is built with -msimd128. All current browsers support Wasm SIMD; very
old versions may not. If you must support them, feature-detect and gate ZipKit
behind a capability check.