API
JSZipp's public surface is intentionally small and stream-native. There is no mutable
archive object: you write with ZipWriter (or
ZipTransformStream) and read with openZip (or
readZipStream). Everything else is an option or a returned entry.
Which API should I use?
Pick the entry point by what you are doing and what shape of output or iteration you want.
The two writers share the same encoder options; the two readers share the same
ZipReadOptions.
| Need | Use | Why |
|---|---|---|
Create a ZIP Blob | new ZipWriter({ outputAs: "blob" }) | Easiest path for downloads and uploads. |
Create a ZIP HTTP Response | new ZipWriter({ outputAs: "response" }) | Returns a native Response with Content-Type: application/zip. |
| Create a byte stream | new ZipWriter() | Default output is ReadableStream<Uint8Array>. |
| Create raw bytes synchronously | writer.writeSync() / closeSync() | In-memory inputs only; no await. |
| Plug into a Web Streams pipeline | new ZipTransformStream() | TransformStream of entries in, bytes out. |
| Open a user-supplied ZIP | openZip(source, options) | Random-access reader for Blob, File, Uint8Array, or ArrayBuffer. |
| Iterate archive order once | readZipStream(stream, options) | Async iterator with single-use entry payloads. |
Importing
Every runtime value is a named export. A default JSZipp namespace bundles the
runtime API for callers who prefer one object.
import JSZipp, {
ZipWriter,
ZipTransformStream,
openZip,
readZipStream,
TimestampMode
} from "web-jszipp";
// Named imports and the namespace are equivalent:
const a = new ZipWriter();
const b = new JSZipp.ZipWriter();
new ZipWriter(options?)
The primary way to build an archive. Append entries with add() and finish with
close(); the value close() resolves to is determined by
outputAs. For in-memory inputs that do not need await, use the
synchronous writeSync() / closeSync() pair instead.
const writer = new ZipWriter({
level: 6, // 0–9, default 6
zip64: "auto", // "auto" | "force" | "off"
outputAs: "blob", // "stream" | "blob" | "response" | "uint8array" | "arraybuffer"
pathMode: "strict", // "unsafe" (default) | "strict" | "sanitize" | "strict-package"
timestamps: TimestampMode.Dos | TimestampMode.Unix, // default; OR in TimestampMode.Ntfs for NTFS
comment: "built with jszipp",
onProgress(p) { console.log(p.phase, p.loaded); }
});
await writer.add({ path: "hello.txt", data: "Hello World\n" });
await writer.add({ path: "nested/data.json", data: JSON.stringify({ ok: true }) });
const zipBlob = await writer.close(); // Blob, because outputAs === "blob"
Constructor options
ZipWriterOptions extends the shared ZipEncoderOptions with two
output-only fields. All options are optional. Every option that affects the ZIP file
specification itself (level, zip64, comment,
timestamps, pathMode, explicitDirectoryEntries) lives
on ZipEncoderOptions, so ZipWriter and
ZipTransformStream share it; only outputAs and
mimeType are writer-specific.
| Option | Type | Default | Description |
|---|---|---|---|
outputAs | "stream" | "blob" | "response" | "uint8array" | "arraybuffer" | "stream" | Shape returned by close() / closeSync(). |
mimeType | string | "application/zip" | Content-Type used by outputAs: "response". |
level | number | 6 | DEFLATE level. Integer 0–9. |
zip64 | "auto" | "force" | "off" | "auto" | When to emit ZIP64 records. |
comment | string | "" | Archive-level (EOCD) comment. |
timestamps | number (bitmask of TimestampMode) | Dos | Unix | Which modification-time fields to write. |
pathMode | "unsafe" | "strict" | "sanitize" | "strict-package" | "unsafe" | Write-side path policy. See path modes. |
explicitDirectoryEntries | boolean | false | Materializes parent directory entries. |
signal | AbortSignal | — | Cancels between stream reads and structure steps. |
onProgress | (p: ZipProgress) => void | — | Coarse progress callback. See progress. |
outputAs — choosing the output shape
The default output is a byte stream. The other modes are conveniences built on top of
it: close() drives the same encoder and then wraps the result. The return
type is inferred from the type argument, so TypeScript knows that
new ZipWriter({ outputAs: "blob" }) resolves close() to a
Blob.
- "stream" (default)
ReadableStream<Uint8Array>.close()resolves immediately; bytes are pulled as the consumer reads.- "blob"
Blob. Ideal for object-URL downloads andFormDatauploads.- "response"
- A native
Responsewhose body is the stream, tagged withmimeType. - "uint8array"
- A single
Uint8Arraywith the whole archive. - "arraybuffer"
- The backing
ArrayBufferof that byte array.
// Stream straight into an upload without buffering the whole archive first.
const writer = new ZipWriter(); // default "stream"
const upload = fetch("/upload", { method: "POST", body: writer.output });
await writer.add({ path: "log.txt", data: bigLogBlob });
await writer.close();
await upload;
level & per-entry method — compression control
level sets the default DEFLATE effort for the writer; an individual entry
may override it with its own level. The value must be an integer from 0 to
9, or a RangeError is thrown.
level: 0, directory entries, andmethod: "store"are written uncompressed (ZIP method0x0000).- With no explicit method, JSZipp attempts DEFLATE but stores the entry instead when the compressed payload would be no smaller than the source.
- An explicit
method: "deflate"always writes a DEFLATE entry (method0x0008), even if that is slightly larger.
The ZIP compression-method field is per entry. A DEFLATE entry may still contain internal
stored blocks for incompressible runs; that does not change the entry method from
0x0008 to 0x0000. ZIP metadata does not record the DEFLATE
level, so a reader cannot report the level an archive was written with.
const writer = new ZipWriter({ level: 6 });
await writer.add({ path: "report.txt", data: text }); // DEFLATE or store fallback
await writer.add({ path: "already.png", data: png, method: "store" }); // never recompress
await writer.add({ path: "huge.log", data: log, level: 9 }); // override the writer level
zip64 — large-archive framing
ZIP64 records remove the classic 4 GiB / 65 535-entry ceilings. JSZipp implements ZIP64 framing itself rather than delegating to a ZIP dependency.
- "auto" (default)
- Emit ZIP64 records only when entry sizes, the entry count, or Central Directory placement exceed standard limits.
- "force"
- Always emit ZIP64 records.
- "off"
- Never emit ZIP64. If the archive would need it,
close()throws aRangeError.
When ZIP64 is emitted, only the saturated 32-bit slots are carried in the extra field:
the local header holds the uncompressed and compressed sizes (16 data bytes); the
Central Directory header additionally carries the local-header offset (24 data bytes).
64-bit values beyond Number.MAX_SAFE_INTEGER are rejected.
timestamps & reproducible output
Each entry's modifiedAt is written to the legacy DOS date/time fields as
local wall-clock time. When timestamps includes TimestampMode.Unix
(the default), the UTC mtime is also written to the Extended Timestamp extra field
(0x5455) so timezone is preserved. Readers prefer 0x5455 and
fall back to the DOS fields.
Pass a bitmask that ORs TimestampMode.Dos (1),
TimestampMode.Unix (2), and TimestampMode.Ntfs (4). Values
outside 0–7 are rejected.
Every ZIP entry already has a local file header before the file data and a Central Directory header near the end of the archive. The timestamp byte counts below are additional extra-field bytes written into those existing per-entry metadata locations; they do not include the base headers, filename bytes, comments, ZIP64 records, EOCD records, or compressed file data. In many-small-file archives this can make JSZipp's total larger than libraries that omit UTC timestamp extras, even when the compressed payloads are comparable.
timestamps |
Extra timestamp bytes/entry | mtime precision | createdAt / lastAccess | unixPermissions |
dosAttributes |
|---|---|---|---|---|---|
Dos | 0 | 2 s, local | not stored | rejected | allowed |
Dos | Unix (default) | +18 | 1 s, UTC | not stored | allowed | rejected |
Dos | Ntfs | +72 | 100 ns, UTC | stored; defaults to mtime | rejected | allowed |
Dos | Unix | Ntfs | +90 | 100 ns, UTC | stored; defaults to mtime | allowed | allowed |
TimestampMode.Unix writes a 9-byte 0x5455 record in both the
local and central headers. TimestampMode.Ntfs writes a 36-byte
0x000a record in both headers for modification, access, and creation
FILETIMEs. dosAttributes is rejected for Dos | Unix because a
Unix-host archive carrying DOS attribute bits would confuse Unix-oriented tools.
For byte-identical (reproducible) archives, supply an explicit modifiedAt
for every entry. When omitted it defaults to new Date() at write
time, which is not reproducible.
const epoch = new Date("2020-01-01T00:00:00Z");
const writer = new ZipWriter({ outputAs: "uint8array" });
await writer.add({ path: "a.txt", data: "A", meta: { modifiedAt: epoch } });
await writer.add({ path: "b.txt", data: "B", meta: { modifiedAt: epoch } });
const bytes = await writer.close(); // byte-identical across runs
explicitDirectoryEntries — parent directory entries
When true, each parent directory implied by an entry's path is written as
a standalone entry. For example, a/b/c.txt also emits a/ and
a/b/.
The default keeps the historical behavior: only caller-added directory entries are written. JSZipp never scans for empty directories; add those explicitly.
Methods & properties
| Member | Signature | Description |
|---|---|---|
add | (entry: ZipInputEntry) => Promise<void> | Append one entry. Accepts string, bytes, Blob, or stream data. |
close | () => Promise<ZipWriterCloseResult<T>> | Finish the archive and resolve to the outputAs shape. |
writeSync | (entry: ZipSyncInputEntry) => void | Synchronous add() for in-memory data only. |
closeSync | () => ZipWriterCloseResult<T> | Synchronous close(); returns the same shape without awaiting. |
output | ReadableStream<Uint8Array> | The live byte stream, available before close(). |
Async mode vs sync mode (and why mixing throws)
A writer runs in exactly one mode. add() / close() form the
async mode and accept any supported data, including Blob
and ReadableStream. writeSync() / closeSync() form
the sync mode and accept only string,
Uint8Array, and ArrayBuffer — values that can be read without
awaiting.
Mixing the two would route some chunks to the stream and others to the sync buffer,
silently dropping entries, so the second mode throws an InvalidStateError
(DOMException). Re-using a closed writer also throws
InvalidStateError.
// Sync path — no awaits, returns immediately.
const writer = new ZipWriter({ outputAs: "uint8array" });
writer.writeSync({ path: "a.txt", data: "A" });
writer.writeSync({ path: "b.txt", data: "B" });
const bytes = writer.closeSync(); // Uint8Array
// writer.add(...) here would throw InvalidStateError: modes cannot mix.
Path normalization & duplicate rejection
Before pathMode runs, writer paths are normalized: backslashes become
/, leading / characters are stripped, and a path ending in
/ is treated as a directory entry. After normalization, writing the same
normalized path twice is rejected with a SecurityError in every path mode.
await writer.add({ path: "docs\\guide.md", data: md }); // stored as docs/guide.md
await writer.add({ path: "/photos/", data: "" }); // directory entry "photos/"
// await writer.add({ path: "photos/" }) again -> SecurityError (duplicate)
Examples
// Downloadable archive.
const writer = new ZipWriter({ outputAs: "blob" });
await writer.add({ path: "hello.txt", data: "Hello from JSZipp\n" });
const blob = await writer.close();
const url = URL.createObjectURL(blob);
Object.assign(document.createElement("a"), { href: url, download: "hello.zip" }).click();
URL.revokeObjectURL(url);
// HTTP response (e.g. inside a service worker or server handler).
const writer = new ZipWriter({ outputAs: "response", mimeType: "application/zip" });
await writer.add({ path: "data.csv", data: csvText });
return writer.close(); // a native Response, streamed to the client
new ZipTransformStream(options?)
A TransformStream whose writable side accepts
ZipInputEntry objects and whose
readable side produces archive bytes. Use it when a Web Streams pipeline is
more natural than ZipWriter. It takes the same
ZipEncoderOptions as the writer (no outputAs / mimeType).
const zipper = new ZipTransformStream({ level: 6 });
const writer = zipper.writable.getWriter();
const upload = fetch("/upload", {
method: "POST",
body: zipper.readable,
headers: { "Content-Type": "application/zip" }
});
await writer.write({ path: "hello.txt", data: "Hello from a stream\n" });
await writer.close();
await upload;
openZip(source, options?)
Reads a complete archive and returns a random-access reader. The source is materialized as bytes, the End of Central Directory is located by content, and the Central Directory is used as the source of truth. Entries can then be listed, looked up by path, and read repeatedly.
const zip = await openZip(file, {
pathMode: "strict-package", // strict + cross-entry checks for untrusted input
maxArchiveSize: 50 * 1024 * 1024, // reject archives larger than 50 MiB
maxEntrySize: 10 * 1024 * 1024 // cap each decompressed entry at 10 MiB
});
console.log(zip.entries.map((e) => e.path));
const text = await zip.get("README.md")?.text();
await zip.close();
Arguments
| Name | Type | Description |
|---|---|---|
source | Blob | File | Uint8Array | ArrayBuffer | The archive to read. Materialized as bytes before parsing. |
options | ZipReadOptions | Reader options, shared with readZipStream. |
ZipReadOptions
| Option | Type | Default | Description |
|---|---|---|---|
pathMode | "strict" | "sanitize" | "unsafe" | "strict-package" | "strict" | Read-side path policy. See path modes. |
filenameEncoding | "cp437" | charset label | TextDecoder-like | "utf-8" | Fallback decoder for legacy (non-UTF-8-flagged) names. |
maxArchiveSize | number | — | Maximum input archive byte length. Exceeding it throws RangeError. |
maxEntrySize | number | — | Maximum decompressed size per entry, enforced during inflate. Exceeding it throws RangeError. |
signal | AbortSignal | — | Cancellation between structure steps. |
onProgress | (p: ZipProgress) => void | — | Coarse progress callback. |
filenameEncoding — decoding legacy filenames
JSZipp follows ZIP metadata first: if a valid Info-ZIP Unicode Path extra field
(0x7075) is present and its embedded CRC-32 matches the header name bytes,
its UTF-8 name is used; otherwise, if the ZIP UTF-8 flag is set, names decode as UTF-8.
filenameEncoding only applies when the UTF-8 flag is absent, and the default
fallback is "utf-8". It affects entry paths, entry comments, and the archive
comment — never file contents.
It accepts "cp437", any charset label the runtime's TextDecoder supports, or a custom TextDecoder-shaped object:
// A built-in label:
const a = await openZip(file, { filenameEncoding: "shift_jis" });
// A custom decoder-shaped object:
const b = await openZip(file, {
filenameEncoding: {
encoding: "cp866",
fatal: false,
ignoreBOM: true,
decode: (bytes) => iconv.decode(bytes, "cp866")
}
});
maxArchiveSize / maxEntrySize — zip-bomb caps
These caps are for untrusted input. maxArchiveSize bounds the input byte
length. maxEntrySize bounds each entry's decompressed size and is enforced
during inflate (bounded reading), so a header that misreports its uncompressed
size cannot expand past the cap. Breaching either throws a RangeError.
try {
const zip = await openZip(untrusted, {
maxArchiveSize: 20 * 1024 * 1024,
maxEntrySize: 5 * 1024 * 1024
});
} catch (err) {
if (err instanceof RangeError) {
// archive or an entry exceeded the configured limit
}
}
Integrity & tamper checks strict by default
openZip requires the Central Directory to be internally consistent: the
number of records must equal the EOCD (or ZIP64 EOCD) entry count and must consume
exactly the declared directory size, or parsing throws. The EOCD is chosen by content,
so an EOCD-like sequence hidden in the archive comment, or a fake EOCD appended after a
complete archive, cannot mask the real entries.
Each entry is also cross-checked against its local file header. A local/central filename mismatch, a disagreement in the security-relevant flag bits (encryption, data descriptor), and two central entries reusing the same local-header offset all throw. These checks do not affect archives whose local and central metadata agree.
Duplicate paths are preserved in append order; the default reader does not reject them.
Encrypted entries and unsupported compression methods throw NotSupportedError.
ZipRandomAccessReader
The object openZip resolves to.
| Member | Type / Signature | Description |
|---|---|---|
entries | readonly ZipRandomAccessEntry[] | All entries in archive (append) order, duplicates preserved. |
comment | string | undefined | The archive-level EOCD comment, when present. |
get | (path: string) => ZipRandomAccessEntry | undefined | The most recently appended matching entry; also tries the writer-style normalized form of the path. |
close | () => Promise<void> | Marks the reader closed and clears lookup state; later payload access throws InvalidStateError. |
readZipStream(stream, options?)
An async iterator over archive entries in order. It accepts the same
ZipReadOptions as openZip. Each yielded
entry's payload methods are single-use: read it, or call skip(),
before moving on.
const response = await fetch("/archives/content.zip");
for await (const entry of readZipStream(response.body, { maxEntrySize: 5 * 1024 * 1024 })) {
if (entry.isDirectory) continue;
if (entry.path.endsWith(".txt")) {
console.log(entry.path, await entry.text());
} else {
await entry.skip(); // consume the payload you do not need
}
}
ZipInputEntry
The object passed to ZipWriter.add() and written to a ZipTransformStream.
| Field | Type | Description |
|---|---|---|
path | string | Entry name. Normalized, then checked against pathMode. |
data | string | Uint8Array | ArrayBuffer | Blob | ReadableStream<Uint8Array> | Entry contents. Strings are encoded as UTF-8. |
method | "store" | "deflate" | Optional per-entry compression method override. |
level | number | Optional per-entry DEFLATE level (0–9), overriding the writer's. |
meta | ZipEntryMeta | Optional comment, timestamp, and attribute metadata. |
ZipSyncInputEntry (used by writeSync()) is identical except that
data is limited to string | Uint8Array | ArrayBuffer — the values
that can be read without awaiting.
ZipEntryMeta
Optional metadata supplied on write and exposed on read.
| Field | Type | Description |
|---|---|---|
comment | string | Per-entry comment (informational; does not affect extraction). |
modifiedAt | Date | Modification time. |
createdAt | Date | Creation time. |
lastAccess | Date | Last-access time. |
extraField | Uint8Array | Advanced, unchecked extra-field override. |
unixPermissions | number | Unix store permission bits. |
dosAttributes | number | MS-DOS attribute byte. |
externalAttributes | number | Escape-hatch external attributes override. |
modifiedAt, createdAt, lastAccess — timestamp validation
modifiedAt, createdAt, and lastAccess must be
valid Date values on or after the Unix epoch. modifiedAt
defaults to write time when omitted.
When createdAt is supplied, neither modifiedAt nor
lastAccess may be earlier than it; an inconsistent entry throws
RangeError. When timestamps includes
TimestampMode.Ntfs, omitted createdAt and
lastAccess values each default to modifiedAt.
unixPermissions & dosAttributes — permission metadata
To set permissions, prefer unixPermissions over
externalAttributes. Use three octal digits 0o000–0o777
(e.g. 0o644 or 0o755); file-type bits are added automatically.
Out-of-range or non-integer values are rejected.
unixPermissions is accepted only when the timestamps mode
includes TimestampMode.Unix (the default does); setting it without that
flag throws RangeError. When an entry records a Unix store permission
— via unixPermissions, a written Unix Extended Timestamp, or raw
externalAttributes bits — its Central Directory header advertises
the Unix host OS (3) in "version made by" so external tools apply those permissions;
otherwise the DOS host (0) is used.
dosAttributes is the DOS-attribute counterpart: the MS-DOS attribute byte
(0x00–0xff) written into the low 8 bits of the external
attributes. The directory bit 0x10 encodes the file/folder distinction
and must match the entry kind; a mismatch throws RangeError.
DOS attributes suit a DOS or NTFS host but would confuse Unix tools, so they are
accepted for the Dos, Dos | Ntfs, and
Dos | Unix | Ntfs modes and rejected when the Unix flag is set without
the NTFS flag (Dos | Unix). With both Unix and
Ntfs set, the external attributes carry the Unix mode (high 16 bits) and
the DOS attributes (low byte) together. dosAttributes does not affect
"version made by".
extraField & externalAttributes — unchecked overrides
Caution: extraField and
externalAttributes are unchecked manual overrides. JSZipp writes them as
supplied and cannot detect every conflict with the entry kind or with the metadata it
generates, so an inconsistent value can corrupt the archive.
extraField must contain raw, well-formed ZIP extra-field bytes
(id(2)+size(2)+payload records). JSZipp writes those raw records before
its own generated extras such as ZIP64, Extended Timestamp, and NTFS. It will not add a
generated timestamp extra whose id already appears in the supplied bytes, so callers
are responsible for emitting well-formed, non-conflicting records.
externalAttributes writes raw 32-bit external attributes verbatim
(high 16 bits = Unix mode, low 8 bits = DOS attribute flags). It overrides
unixPermissions and dosAttributes and is not reconciled with
the entry's directory bit. Use it only to round-trip an exact value read from another
archive.
On read, JSZipp exposes only the raw externalAttributes; derive the Unix
mode with externalAttributes >>> 16 and the MS-DOS attribute byte
with externalAttributes & 0xff.
Reader entries
Reading yields entries with metadata plus four payload helpers:
stream(), text(), bytes(), and
arrayBuffer(). Both entry types extend
ZipEntryMeta.
ZipRandomAccessEntry (from openZip)
Payload helpers are reusable — read the same entry as many times as you like until the reader is closed.
| Member | Type / Signature | Description |
|---|---|---|
path | string | Decoded entry name. |
size | number | Uncompressed size in bytes. |
compressedSize | number | Stored (compressed) size in bytes. |
crc32 | number | CRC-32 of the uncompressed data. |
isDirectory | boolean | Whether the entry is a directory. |
stream | () => ReadableStream<Uint8Array> | A fresh byte stream of the decompressed contents. |
text | () => Promise<string> | Decode the contents as UTF-8 text. |
bytes | () => Promise<Uint8Array> | The decompressed bytes. |
arrayBuffer | () => Promise<ArrayBuffer> | The decompressed bytes as an ArrayBuffer. |
ZipStreamEntry (from readZipStream)
The same shape, with two differences: size, compressedSize, and
crc32 may be null when unknown up front, and the payload helpers are
single-use. After any of stream(), text(),
bytes(), arrayBuffer(), or skip() is called, calling
another throws InvalidStateError.
| Member | Type / Signature | Description |
|---|---|---|
size | number | null | Uncompressed size, when known. |
compressedSize | number | null | Compressed size, when known. |
crc32 | number | null | CRC-32, when known. |
isDirectory | boolean | Whether the entry is a directory. |
skip | () => Promise<void> | Discard this entry's payload so iteration can advance. |
stream() · text() · bytes() · arrayBuffer() | As above, but each entry's payload may be read only once. | |
Path modes
pathMode governs how unsafe entry names are handled. It runs on both the writer
(after path normalization) and the reader. The defaults differ on purpose: writing defaults
to "unsafe" to preserve legacy behavior, while reading defaults to
"strict" so that untrusted archives are safe out of the box.
| Mode | Behavior |
|---|---|
"strict" | Reject unsafe paths with SecurityError. |
"sanitize" | Strip the unsafe components instead of rejecting; a path that sanitizes to empty is rejected. |
"unsafe" | Opt out of path rejection. The writer still applies normalization; the reader exposes raw archive names. |
"strict-package" | "strict" plus package-level checks. |
"strict" / "sanitize" — unsafe path handling
Unsafe paths include .. segments, absolute paths, drive-letter paths
(including drive-relative names like C:name), backslash-separated paths
on read, and any path containing a NUL byte.
"strict" rejects these paths with SecurityError.
"sanitize" strips unsafe components instead, but rejects the path if the
result is empty.
What "strict-package" adds uploads · packages · CI
This is an opt-in profile for archives crossing a trust boundary. Beyond strict per-path safety, it adds two cross-entry checks the default deliberately omits to preserve documented behavior:
- Local/central size cross-check. With general-purpose bit 3 clear, the local and central sizes must agree. Data-descriptor entries (bit 3 set) are exempt.
- Collision rejection. Entry paths that collide after Unicode (NFC) and case normalization are rejected — exact duplicates, case-only twins like
Readme.txt/README.TXT, and NFC/NFD twins.
The default reader still preserves duplicate paths and defers size integrity to read time; only "strict-package" rejects them.
Progress & cancellation
All four entry points accept a signal (AbortSignal) and a coarse
onProgress callback. Cancellation is checked between stream reads and ZIP
structure steps, not inside synchronous DEFLATE work, so it is best-effort rather than
instantaneous.
ZipProgress
| Field | Type | Description |
|---|---|---|
phase | "read" | "compress" | "write" | "parse" | Which stage produced the update. |
loaded | number | Bytes processed so far in this phase. |
total | number | undefined | Expected total, when known. |
entries | number | undefined | Entries processed so far, when applicable. |
path | string | undefined | The entry path being processed, when applicable. |
const controller = new AbortController();
const writer = new ZipWriter({
signal: controller.signal,
onProgress: ({ phase, loaded, total }) => {
if (total) console.log(`${phase}: ${Math.round((loaded / total) * 100)}%`);
}
});
// controller.abort() rejects the in-flight add()/close() with an AbortError.
Errors & diagnostics
JSZipp keeps the category of an exception stable across builds, even though the
human-readable message differs between development and production. Branch on the exception
type and the DOMException name — never on exact message text.
| Condition | Throws |
|---|---|
| Unsafe or duplicate entry path | DOMException name "SecurityError" |
| Encrypted entry or unsupported compression method | DOMException name "NotSupportedError" |
Runtime missing deflate-raw support | DOMException name "NotSupportedError" |
| Re-using a closed writer/reader, or mixing writer modes, or re-reading a single-use stream entry | DOMException name "InvalidStateError" |
maxArchiveSize / maxEntrySize breach, ZIP64 disabled but required, or a 64-bit value beyond safe-integer range | RangeError |
Invalid level (not an integer 0–9) | RangeError |
| Corrupt structure, inconsistent Central Directory, or inflated-size mismatch | Error (corrupt-stream / structure error) |
In production builds, error.message is shortened to stable codes such as
E_PATH, E_LIMIT, E_STRUCTURE, E_ZIP64,
and E_UNSUPPORTED to keep the bundle small. Development builds keep the detailed
messages used by the tests. Because the message text is build-dependent, robust handling
looks at the type and name:
try {
const zip = await openZip(file, { maxEntrySize: 5 * 1024 * 1024 });
} catch (err) {
if (err instanceof DOMException && err.name === "SecurityError") {
// unsafe path under the active pathMode
} else if (err instanceof DOMException && err.name === "NotSupportedError") {
// encrypted or unsupported method
} else if (err instanceof RangeError) {
// exceeded a configured limit
} else {
// corrupt or invalid archive
}
}
Default namespace
The default export is a JSZipp object bundling the runtime API values, for
callers who prefer a single namespace over named imports.
import JSZipp from "web-jszipp";
JSZipp.ZipWriter; // class
JSZipp.ZipTransformStream; // class
JSZipp.openZip; // function
JSZipp.readZipStream; // function
JSZipp.TimestampMode; // timestamp bit flags
ZipSyncInputEntry is exported as a named type for the synchronous writer path
but is not part of the default namespace object because TypeScript types do not exist at
runtime.