Promise-friendly, Result-first, fully typed ZIP reader that keeps large archives off disk and outside memory.
@zokugun/yauzl-plus is a TypeScript-first port and fork of overlookmotel/yauzl-promise. The upstream project provides a Promise wrapper over the battle-tested thejoshwolfe/yauzl ZIP reader. This repository rebuilds that API:
- written entirely in TypeScript with strict types;
- shipped as native ES modules.
- Promise + Result-based API – Every helper (
open,fromFd,fromBuffer,fromReader) returns aResultso you handle errors explicitly without exceptions. - Streaming friendly – Iterate entries one at a time via
zip.readEntry()and stream their contents with back-pressure-awareentry.openReadStream(). - Multi-source readers – Open ZIPs from file paths, existing file descriptors, in-memory buffers, or custom
Readersubclasses. - Spec coverage – Supports ZIP64 archives, Mac Archive Utility quirks, Central Directory validation, CRC32 checking, CP437/UTF-8 filename decoding, and DOS timestamp conversion.
- TypeScript ready – Rich
.d.tsdeclarations shipped in the package; strict options help you catch mistakes at compile time.
npm add @zokugun/yauzl-plusimport * as fs from 'node:fs';
import * as path from 'node:path';
import { err, type Result, stringifyError, xtry, xdefer } from '@zokugun/xtry/async';
import { open } from '@zokugun/yauzl-plus';
async function run(): Promise<Result<void, string>> {
const zipResult = await open('fixtures/archive.zip', {
decodeStrings: true,
strictFilenames: false,
});
if(zipResult.fails) {
return zipResult;
}
const zip = zipResult.value;
const defer = xdefer(zip.close)
for await (const entryResult of zip) {
if(entryResult.fails) {
return defer(entryResult);
}
const entry = entryResult.value;
if(!entry) {
break; // No more files
}
const name = entry.filename;
if(name.endsWith('/')) {
continue; // Skip directories
}
const readStreamResult = await entry.openReadStream({ decompress: true });
if(streamResult.fails) {
return defer(streamResult);
}
const readStream = await entry.openReadStream();
const writeStream = fs.createWriteStream(path.join('output', name));
const result = await xtry(pipeline(readStream, writeStream));
if(result.fails) {
return defer(err(stringifyError(result.error)));
}
}
return defer();
}open(path, options?)– open a ZIP from disk.fromFd(fd, options?)– reuse an already opened file descriptor.fromBuffer(buffer, options?)– treat anyBufferas a ZIP binary.fromReader(reader, size, options?)– plug in your ownReadersubclass (e.g., S3, HTTP).Zip– exposesreadEntry(),close(), and metadata about the archive.Entry– read metadata (filename,lastModDate,comment), checkisEncrypted()/isCompressed(), and stream data withopenReadStream().- Utilities –
dosDateTimeToDate,streamToBuffer,streamToString,validateFilenamefor standalone usage.
Refer to the TypeScript definitions in lib/*.d.ts or the source in src/ for the complete surface area.
open, fromFd, fromBuffer, and fromReader accept a ZipOptions object:
decodeStrings– decode CP437 / UTF-8 filenames to strings (defaulttrue).validateEntrySizes– ensure streamed bytes match the header metadata.validateFilenames&strictFilenames– reject unsafe paths or disallow relative components.supportMacArchive– enable heuristics for ZIPs produced by Apple Archive Utility.
All options default to the most defensive settings; pass false when you need raw access.
decompress(boolean | 'auto') Iftrue, only the Deflate method (8) is supported andstart/endcannot be set.decrypt(boolean | 'auto') Decryption is not implemented yet, so enabling it returns an error.validateCrc32(boolean | 'auto') Cannot be combined with partial (start/end) reads.start/end– byte offsets (integers) into the compressed stream.
npm run compile # Build TypeScript -> lib/
npm test # Run Vitest suite
npm run lint # XO lintingPlease read CONTRIBUTING.md for code style, branching, and release guidance. Bug reports, feature proposals, docs tweaks, and test fixtures are all welcome.
Support this project by becoming a financial contributor.
| ko-fi.com/daiyam | |
| liberapay.com/daiyam/donate | |
| paypal.me/daiyam99 |
Copyright © 2025-present Baptiste Augrain
Licensed under the MIT license.