node-zip-stream

A streaming ZIP archive generator for Node.js that pipes archives out as they're built, without buffering the whole thing in memory.

Library
npm
v7.0.5
168stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
56/100Fair
Development Activity48
Maintenance32
Community64
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
64/100Good
Architecture78
Code Quality62
Innovation55
Learning Curve60

zip-stream is a low-level Node.js library for generating ZIP archives as a readable stream, built on top of compress-commons’ ZipArchiveOutputStream. Rather than assembling an entire archive in memory before writing it out, it lets you append entries — strings, buffers, or streams — one at a time and pipe the result directly to a file, an HTTP response, or any other writable destination as it is produced.

It is intentionally minimal: there is no built-in queueing or glob-based directory support, so callers must wait for one entry to finish before appending the next. This makes it best suited as a building block inside higher-level tools — most notably the archiver package, which wraps zip-stream (and its sibling tar-stream) to add queue management, glob patterns, and a unified interface across archive formats.

What You Get

  • A ZipStream class you can pipe() anywhere a writable stream is accepted — filesystem, HTTP response, or another stream.
  • An entry() API for appending files, directories, and symlinks from strings, Buffers, or readable streams.
  • Support for ZIP64 headers, configurable zlib compression levels, STORE-only (uncompressed) entries, and archive- or entry-level comments.
  • Automatic path sanitization that strips drive letters and directory traversal from entry names before they are written.

Common Use Cases

  • Streaming a ZIP of dynamically generated files straight to an HTTP response without writing a temp file first.
  • Acting as the ZIP backend inside a higher-level archiving tool, as the archiver package does internally.
  • Building backup or export bundles from data produced incrementally by another process (logs, reports, generated files).
  • Packaging build artifacts into a ZIP entry-by-entry as each file finishes processing in a build pipeline.

Under The Hood

Architecture The entire package is a thin subclass: ZipStream (index.js) extends compress-commons’ ZipArchiveOutputStream, overriding the constructor to normalize options (zlib level, forceZip64, store, comment), overriding entry() to translate a plain data object into a ZipArchiveEntry before delegating to the parent class, and adding a finalize() alias for finish(). A small utils.js holds two pure helper functions (dateify, sanitizePath) used during entry normalization. There is no internal queueing, worker pool, or event bus — the module relies entirely on Node’s native stream backpressure, and the README explicitly documents that callers must serialize entry() calls themselves. Because nearly every method is a direct translation layer, the real abstraction being protected is compress-commons’ ZipArchiveOutputStream/ZipArchiveEntry contract; a breaking change there would ripple through almost all of index.js.

Tech Stack The package ships as pure ESM (“type”: “module”) targeting Node >=18. Runtime dependencies are compress-commons (the underlying ZIP writer), normalize-path (path sanitization), and readable-stream (a consistent Readable/Writable implementation across Node versions). Dev tooling covers mocha and chai for testing, prettier for formatting, and jsdoc (with archiver-jsdoc-theme/minami themes) to generate the published API docs from source comments. There is no bundler or build step — index.js and utils.js ship as-is via the package’s “files” allowlist — and a GitHub Actions workflow is present under .github/ for CI.

Code Quality Tests live under test/ (pack.js and utils.js) written with mocha and chai’s assert style, covering entry() with buffer, stream, and stream-like sources, several date edge cases including DOS date range overflow/underflow boundaries, and the two utils.js helpers directly. Error handling is Node-style: explicit Error objects are passed to err-first callbacks for invalid entry types, names, or symlink data rather than being swallowed silently. There is no TypeScript — types are documented only via JSDoc annotations — and no ESLint config was found, though prettier formatting and CI are in place. Overall this is a modest but genuine test suite focused on observable output rather than exhaustive internal coverage.

API Design The public surface is deliberately tiny: one class extending a Node stream, with entry(source, data, callback) as effectively the only method most callers need, plus finalize(). Getting started takes three lines — construct, pipe, call entry() — and the library composes naturally with anything in Node’s stream ecosystem rather than inventing its own I/O model. The tradeoff, stated plainly in the README, is that there is no queue management: callers (or wrapper libraries like archiver) must serialize entry() calls themselves. That is a legible, honest design choice rather than a novel one — the ZIP format handling itself lives in compress-commons, not here.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search