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

archiver-wasm

v0.2.0

Published

node-archiver ported to Rust and WebAssembly, with ZIP encryption built in

Readme

archiver-wasm

node-archiver ported to Rust and WebAssembly for Node.js, with the ZIP encryption of archiver-zip-encrypted built in and TypeScript types included.

This is an independent port. It is not published or endorsed by the archiver maintainers.

  • Same API as archiver 8: ZipArchive, TarArchive, JsonArchive, append, file, directory, glob, symlink, finalize, abort, pointer, and the same events.
  • The archiver 7 factory is also there: archiver('zip', options), create, registerFormat, isRegisteredFormat.
  • ZipEncrypted adds AES-256 (WinZip AE-1) and legacy ZipCrypto.
  • The bytes of every archive are produced in Rust (crates/archiver-core) and compiled to WebAssembly. Streams, the file system and callbacks stay in TypeScript.
  • ESM and CommonJS, strict types, no native addon, Node.js 18 or newer.

Install

npm install archiver-wasm

Usage

import { createWriteStream } from "node:fs";
import { ZipArchive } from "archiver-wasm";

const archive = new ZipArchive({ zlib: { level: 9 } });
archive.pipe(createWriteStream("out.zip"));

archive.append("hello", { name: "hello.txt" });
archive.file("README.md", { name: "docs/README.md" });
archive.directory("src/", "src");
archive.glob("**/*.json", { cwd: "config" });

await archive.finalize();

Tar and gzipped tar:

import { TarArchive } from "archiver-wasm";

const archive = new TarArchive({ gzip: true, gzipOptions: { level: 6 } });

Encrypted ZIP:

import archiver, { ZipEncrypted } from "archiver-wasm";

archiver.registerFormat("zip-encrypted", ZipEncrypted);

const archive = archiver.create("zip-encrypted", {
  zlib: { level: 8 },
  encryptionMethod: "aes256", // or "zip20"
  password: "secret",
});

CommonJS works the same way: const { ZipArchive } = require("archiver-wasm").

import and require share one implementation. A format registered through one is visible through the other.

Compatibility with upstream

The port follows upstream statement by statement, so behavior matches archiver 8.0.0 and archiver-zip-encrypted 2.0.0.

  • Tar, JSON and stored ZIP output is byte-identical to upstream for the same input.
  • Deflated data is produced by miniz_oxide, not zlib, so compressed bytes can differ. The data always inflates to the same content and every other field of the archive matches.
  • At zlib level 1, which is the ZIP default, miniz_oxide compresses less than zlib. Measured archives were 9 to 19 percent larger than upstream's (15.4 MB of JavaScript source: 3,499,632 bytes upstream, 4,127,264 bytes here). At levels 2, 6 and 9 the sizes were within about 1 percent of zlib's or smaller. Pass zlib: { level: 2 } or higher if size matters.
  • Known upstream defects are preserved on purpose. They are listed in section 3.3 of PORTING.md.

Security notes

archiver-wasm reproduces upstream here too. It does not validate what you pass in. If any of these values come from an untrusted source, check them before you call archiver-wasm.

  • Entry names and prefixes. Only a leading ../, a leading / and a leading drive letter are stripped. An interior .. stays (a/../../b is written as is), and a drive letter after a non-empty prefix stays. Reject names that contain a .. segment.
  • symlink() names and targets. The name passed to symlink() is not sanitized in tar and JSON output, and no link target is ever checked. Reject absolute paths and .. segments in both. directory() and glob() store the links they find with their targets unchanged, including targets outside the tree.
  • NUL characters. A NUL in a tar entry name or link name is written as is. Readers cut the name there, so the name you validated is not the name that gets extracted. Reject names that contain \0.
  • Length of names and comments in ZIP. A name, entry comment or archive comment longer than 65535 bytes gets a length field that wraps around while all the bytes are still written. The archive is then inconsistent and no error is raised. Limit these to 65535 UTF-8 bytes.
  • Tar uname and gname. They have no length limit. More than 32 bytes spill into the next header fields and can set the path prefix that readers put in front of the entry name. Limit both to 32 bytes.
  • Very long tar names. A name with many thousands of / separators takes quadratic time to encode and blocks the event loop. Limit the length of names.
  • glob() with follow: true. Symlink cycles are not detected. A few links that point back into the tree make the walk and the archive grow without bound. Do not use follow: true on a tree that others can write to.
  • Format names. archiver.create(), registerFormat() and isRegisteredFormat() treat constructor, toString and other Object.prototype keys as registered formats. Do not pass a format name from user input without an allow list.

Other inherited behavior to know about:

  • AES entries are WinZip AE-1, as in archiver-zip-encrypted. AE-1 stores the CRC-32 of each file's plaintext unencrypted in the ZIP headers. Someone who holds the archive can confirm a guessed content, or recover a very short file, without the password.
  • ZipCrypto (zip20) is a legacy cipher and is weak. With it, directories and empty stored files are written in a way that 7z and unzip report as a wrong password.
  • In a tar archive, a file whose size changes between stat and read fails with Size mismatch, and the error is also thrown outside any handler. Do not archive files that are being written.
  • archive.destroy() while a file is being read leaves that file descriptor open. Use archive.abort() to stop early.
  • abort() called one macrotask after an entry completed, for example from a setImmediate callback, can leave the archive without 'end' and 'close'. Call archive.destroy() after abort() if you wait for those events.

How it was built

The port copies the method described in the Bun write-up of its port to Rust: a porting guide written before any code, a mechanical all-at-once port unit by unit, one implementer with two adversarial reviewers and one fixer per unit, compiler errors used as a work queue, and the upstream test suites as the oracle with no test skipped or deleted.

  • PORTING.md is the guide.
  • BEHAVIORS.tsv is the inventory of upstream behavior.
  • PORTMAP.tsv maps every upstream file to its Rust or TypeScript target.

Development

Requires the Rust toolchain of rust-toolchain.toml, the wasm32-unknown-unknown target, wasm-pack 0.15.0, Node.js 18 or newer and 7z.

npm ci
npm run build
npm test

npm test runs the Rust tests, the unmodified test suites of both upstream packages against this package, and differential tests that compare its output with the upstream packages.

License

MIT. Portions derive from node-archiver, archiver-zip-encrypted, zip-stream, compress-commons, tar-stream, crc32-stream and readdir-glob. See THIRD_PARTY_NOTICES.md.