Skip to content

Latest commit

 

History

History
139 lines (108 loc) · 4.59 KB

File metadata and controls

139 lines (108 loc) · 4.59 KB

ZIP archives

@myrialabs/zipkit reads and writes standard ZIP archives, with an optional zstd method for much denser archives between ZipKit-aware peers.

import { zip, unzip, listEntries } from '@myrialabs/zipkit';

Creating an archive

const archive = await zip([
  { name: 'index.html', data: html },
  { name: 'app.js', data: js, method: 'zstd', level: 19 },
  { name: 'logo.png', data: png, method: 'store' },
  {
    name: 'run.sh',
    data: script,
    unixPermissions: 0o755,
    mtime: new Date('2026-01-01'),
    comment: 'entrypoint'
  }
]);

ZipEntryInput

Field Type Default Notes
name string path within the archive (/ separators)
data Uint8Array uncompressed contents
method 'store' | 'deflate' | 'zstd' 'deflate' compression method
level number codec default deflate/zstd level
mtime Date | number now last-modified time
unixPermissions number e.g. 0o644, stored in external attrs
comment string per-entry comment

Options

zip(entries, opts?) accepts:

Field Type Default Notes
parallel boolean auto compress entries across the worker pool
onProgress (done, total) => void fires as each entry finishes compressing
password string encrypt every entry with WinZip AES-256 (AE-2)

CRC-32 is computed in Wasm (libdeflate's SIMD path). Entry compression fans out over the worker pool — parallel defaults to on once there are at least two entries totalling ≥256 KB, off otherwise (the hand-off would cost more than it saves on tiny archives). The container is assembled in order, so a parallel archive is byte-identical to a single-threaded one; pass parallel: true/false to force it either way.

Reading an archive

const entries = await unzip(archive);
for (const e of entries) {
  console.log(e.name, e.size, e.mtime, e.unixPermissions);
  await Bun.write(e.name, e.data);
}

Encryption

const enc = await zip(entries, { password: 'secret' });   // WinZip AES-256 (AE-2)
const out = await unzip(enc, { password: 'secret' });     // also reads legacy ZipCrypto

New archives use WinZip AES-256 (AE-2), interoperable with 7-Zip and WinZip. On read, both WinZip AES and legacy PKWARE ZipCrypto are decrypted; a wrong password throws ZipKitError before any plaintext is produced. PBKDF2/HMAC-SHA1 come from WebCrypto, so encryption needs crypto.subtle (present in Node 18+, Bun, and browsers).

Integrity check

const out = await unzip(archive, { verify: true });  // re-checks every entry's CRC-32

Streaming writer

For archives too large to hold in memory, zipStream() emits the archive incrementally through a ReadableStream<Uint8Array> — peak memory is one entry:

import { zipStream } from '@myrialabs/zipkit';
await zipStream(entriesIterable).pipeTo(destinationWritable);

Filtering

Skip decompressing entries you don't need — the filter runs on metadata, before any decompression work:

const jsFiles = await unzip(archive, { filter: (e) => e.name.endsWith('.js') });

Listing without extracting

const list = await listEntries(archive);   // ZipEntryInfo[] — names, sizes, methods

ZipEntry

Field Type Notes
name string
data Uint8Array decompressed contents
method number 0 = store, 8 = deflate, 93 = zstd
mtime Date 2-second resolution (DOS time)
size / compressedSize number
crc32 number stored checksum of the original data
unixPermissions number? if the archive recorded any
comment string?

Compatibility

  • store and deflate entries interoperate with every standard ZIP tool (unzip, Explorer, macOS Archive Utility, fflate, …).
  • zstd entries use method 93; they're readable by ZipKit and other zstd-aware ZIP readers, but not by tools that only support deflate.
  • ZIP64 is engaged automatically when any size/offset exceeds 4 GB or there are more than 65 535 entries — both on write and on read.
  • Filenames are stored UTF-8 (the language-encoding flag is set).

Limitations

  • zip() / unzip() build and read fully in memory (async because the codecs are Wasm). For multi-gigabyte archives, use zipStream(), whose peak memory is the single largest entry.
  • Encrypted reading covers WinZip AES (AE-1/AE-2) and legacy ZipCrypto; ZipKit only ever writes WinZip AES (the weak ZipCrypto cipher is read-only).