@zip.js/zip.js 2.18.2 → 2.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/BENCHMARKS.md +122 -118
  2. package/deno.json +1 -1
  3. package/dist/zip-core-external.js +54 -81
  4. package/dist/zip-core-external.min.js +1 -1
  5. package/dist/zip-core.js +54 -81
  6. package/dist/zip-core.min.js +1 -1
  7. package/dist/zip-fs-core-external.js +54 -81
  8. package/dist/zip-fs-core-external.min.js +1 -1
  9. package/dist/zip-fs-core.js +54 -81
  10. package/dist/zip-fs-core.min.js +1 -1
  11. package/dist/zip-fs-external.js +54 -81
  12. package/dist/zip-fs-external.min.js +1 -1
  13. package/dist/zip-fs-native.js +56 -83
  14. package/dist/zip-fs-native.min.js +1 -1
  15. package/dist/zip-fs.js +55 -82
  16. package/dist/zip-fs.min.js +1 -1
  17. package/dist/zip-legacy.js +56 -83
  18. package/dist/zip-legacy.min.js +1 -1
  19. package/dist/zip-native.js +56 -83
  20. package/dist/zip-native.min.js +1 -1
  21. package/dist/zip-web-worker-native.js +1 -1
  22. package/dist/zip-web-worker.js +1 -1
  23. package/dist/zip.js +55 -82
  24. package/dist/zip.min.js +1 -1
  25. package/index-native.cjs +56 -83
  26. package/index-native.min.js +1 -1
  27. package/index.cjs +55 -82
  28. package/index.d.cts +27 -18
  29. package/index.d.ts +27 -18
  30. package/index.min.js +1 -1
  31. package/lib/core/codec-pool.js +1 -2
  32. package/lib/core/codec-worker-web.js +11 -39
  33. package/lib/core/codec-worker.js +1 -2
  34. package/lib/core/configuration.js +1 -1
  35. package/lib/core/options.js +0 -2
  36. package/lib/core/streams/codec-stream.js +1 -1
  37. package/lib/core/version.js +1 -1
  38. package/lib/core/web-worker-base.js +2 -3
  39. package/lib/core/web-worker-inline-native.js +1 -1
  40. package/lib/core/web-worker-inline-wasm.js +1 -1
  41. package/lib/core/zip-reader.js +21 -23
  42. package/lib/core/zip-writer.js +18 -14
  43. package/package.json +1 -1
  44. package/worker-message-property-names.js +1 -1
package/BENCHMARKS.md CHANGED
@@ -22,14 +22,14 @@ The page answers four questions, each from one or two scripts of the harness:
22
22
  | | |
23
23
  |---|---|
24
24
  | Machine | Apple M2, 8 cores (4 performance + 4 efficiency), 16 GB RAM |
25
- | OS | macOS 26.6.2 (arm64) |
26
- | Runtimes | Node.js v26.7.0, Bun 1.4.2, Deno 2.9.6 |
27
- | Browsers | Firefox 154, Chrome 153 (the browser encryption table only) |
28
- | zip.js | 2.14.0 |
25
+ | OS | macOS 27.0 (arm64) |
26
+ | Runtimes | Node.js v26.7.0, Bun 1.4.2, Deno 2.9.7 |
27
+ | Browsers | Firefox 156, Chrome 153, headless (the browser encryption table only) |
28
+ | zip.js | 2.19.0, measured at 96084cc8; the later commits of the release change no codec or stream path |
29
29
  | jszip | 3.10.2 |
30
30
  | fflate | 0.8.3 |
31
31
  | archiver | 8.0.0 |
32
- | Measured | 2026-09-10; the two encryption tables on 2026-09-12, on the engines that follow 2.14.0 |
32
+ | Measured | 2026-09-26 |
33
33
 
34
34
  ## Method
35
35
 
@@ -82,31 +82,31 @@ size each row produced:
82
82
 
83
83
  | Library | Codec | Compressible text (20 MB) | Incompressible data (20 MB) | 5,000 files × ~2 KB |
84
84
  |---|---|--:|--:|--:|
85
- | @zip.js/zip.js | `CompressionStream` | **700 ms** / 6.1 MB | 365 ms / 21.0 MB | 888 ms / 4.9 MB |
86
- | @zip.js/zip.js | WASM zlib | 1057 ms / 6.2 MB | 475 ms / 21.0 MB | 679 ms / 4.9 MB |
87
- | @zip.js/zip.js | pure-JS zlib | 1238 ms / 6.2 MB | 802 ms / 21.0 MB | 1125 ms / 4.9 MB |
88
- | jszip | pako (JavaScript) | 1723 ms / 6.2 MB | 862 ms / 21.0 MB | 843 ms / 4.7 MB |
89
- | fflate | fflate (JavaScript) | 909 ms / 6.3 MB | **297 ms** / 21.0 MB | **281 ms** / 4.8 MB |
90
- | archiver | Node `zlib` (C) | 702 ms / 6.1 MB | 359 ms / 21.0 MB | 424 ms / 4.8 MB |
85
+ | @zip.js/zip.js | `CompressionStream` | **698 ms** / 6.1 MB | 350 ms / 21.0 MB | 864 ms / 4.9 MB |
86
+ | @zip.js/zip.js | WASM zlib | 1049 ms / 6.2 MB | 461 ms / 21.0 MB | 666 ms / 4.9 MB |
87
+ | @zip.js/zip.js | pure-JS zlib | 1218 ms / 6.2 MB | 817 ms / 21.0 MB | 1107 ms / 4.9 MB |
88
+ | jszip | pako (JavaScript) | 1702 ms / 6.2 MB | 855 ms / 21.0 MB | 834 ms / 4.7 MB |
89
+ | fflate | fflate (JavaScript) | 908 ms / 6.3 MB | **296 ms** / 21.0 MB | **276 ms** / 4.8 MB |
90
+ | archiver | Node `zlib` (C) | 702 ms / 6.1 MB | 355 ms / 21.0 MB | 421 ms / 4.8 MB |
91
91
 
92
92
  Peak memory for the same runs (Δ over baseline):
93
93
 
94
94
  | Library | Codec | Compressible text (20 MB) | Incompressible data (20 MB) | 5,000 files × ~2 KB |
95
95
  |---|---|--:|--:|--:|
96
- | @zip.js/zip.js | `CompressionStream` | 91 MB | 153 MB | 245 MB |
97
- | @zip.js/zip.js | WASM zlib | 119 MB | 170 MB | 250 MB |
98
- | @zip.js/zip.js | pure-JS zlib | 114 MB | 172 MB | 276 MB |
99
- | jszip | pako (JavaScript) | 69 MB | 93 MB | 269 MB |
100
- | fflate | fflate (JavaScript) | **62 MB** | 108 MB | **102 MB** |
101
- | archiver | Node `zlib` (C) | 72 MB | **74 MB** | 137 MB |
102
-
103
- On the text file zip.js with `CompressionStream` and archiver take the same time, 700 and
96
+ | @zip.js/zip.js | `CompressionStream` | 108 MB | 160 MB | 251 MB |
97
+ | @zip.js/zip.js | WASM zlib | 115 MB | 187 MB | 260 MB |
98
+ | @zip.js/zip.js | pure-JS zlib | 113 MB | 167 MB | 281 MB |
99
+ | jszip | pako (JavaScript) | 69 MB | 93 MB | 261 MB |
100
+ | fflate | fflate (JavaScript) | **62 MB** | 108 MB | **103 MB** |
101
+ | archiver | Node `zlib` (C) | 71 MB | **74 MB** | 136 MB |
102
+
103
+ On the text file zip.js with `CompressionStream` and archiver take the same time, 698 and
104
104
  702 ms, for the same output size: both run Node's zlib. With the codecs that ship with each
105
- library, fflate takes 909 ms for 2 % more bytes, zip.js's WebAssembly zlib 1057 ms, its
106
- pure-JavaScript port 1238 ms and jszip 1723 ms. On incompressible data fflate is the fastest row
105
+ library, fflate takes 908 ms for 2 % more bytes, zip.js's WebAssembly zlib 1049 ms, its
106
+ pure-JavaScript port 1218 ms and jszip 1702 ms. On incompressible data fflate is the fastest row
107
107
  and archiver the smallest in memory. On 5,000 small files the WebAssembly backend is 23 % faster
108
108
  than `CompressionStream`, so a native stream costs more per entry than a WebAssembly one; fflate
109
- takes 41 % of the WebAssembly row's time there against 86 % on the 20 MB file, and the
109
+ takes 41 % of the WebAssembly row's time there against 87 % on the 20 MB file, and the
110
110
  difference is the work zip.js does per entry, a stream and a header each. The zip.js rows use
111
111
  the most memory on the two 20 MB files.
112
112
 
@@ -117,31 +117,31 @@ excluded.
117
117
 
118
118
  | Library | Codec | Compressible text (20 MB) | 5,000 files × ~2 KB |
119
119
  |---|---|--:|--:|
120
- | @zip.js/zip.js | `DecompressionStream` | **66 ms** | 628 ms |
121
- | @zip.js/zip.js | WASM zlib | 78 ms | 476 ms |
122
- | @zip.js/zip.js | pure-JS zlib | 180 ms | 615 ms |
123
- | jszip | pako (JavaScript) | 137 ms | 444 ms |
124
- | fflate | fflate (JavaScript) | 116 ms | **102 ms** |
120
+ | @zip.js/zip.js | `DecompressionStream` | **61 ms** | 629 ms |
121
+ | @zip.js/zip.js | WASM zlib | 72 ms | 474 ms |
122
+ | @zip.js/zip.js | pure-JS zlib | 154 ms | 613 ms |
123
+ | jszip | pako (JavaScript) | 133 ms | 440 ms |
124
+ | fflate | fflate (JavaScript) | 123 ms | **102 ms** |
125
125
 
126
126
  Peak memory for the same runs (Δ over baseline):
127
127
 
128
128
  | Library | Codec | Compressible text (20 MB) | 5,000 files × ~2 KB |
129
129
  |---|---|--:|--:|
130
- | @zip.js/zip.js | `DecompressionStream` | 116 MB | 350 MB |
131
- | @zip.js/zip.js | WASM zlib | 163 MB | 484 MB |
132
- | @zip.js/zip.js | pure-JS zlib | 154 MB | 451 MB |
133
- | jszip | pako (JavaScript) | 103 MB | 306 MB |
134
- | fflate | fflate (JavaScript) | **92 MB** | **120 MB** |
135
-
136
- On the 20 MB stream `DecompressionStream` takes 66 ms and the WebAssembly inflate 78 ms, then
137
- fflate 116 ms, jszip 137 ms and the pure-JavaScript port 180 ms. The WebAssembly row includes
138
- the module instantiation a fresh process pays once, about 20 ms. Warm, the two inflates are
139
- within 6 % of each other in the [Codecs](#codecs-at-equal-output-size) table. On 5,000 small
140
- files the order reverses: fflate takes 102 ms, jszip 444 ms and the three zip.js rows 476 to
141
- 628 ms, with `DecompressionStream` the slowest of them, the same per-entry cost as in
142
- compression. fflate uses the least memory on both workloads, 34 % of the `DecompressionStream`
143
- row on the small files, and the WebAssembly and pure-JavaScript rows peak higher than
144
- `DecompressionStream`. The three zip.js rows allocate the same 800 MB of stream objects over the
130
+ | @zip.js/zip.js | `DecompressionStream` | 135 MB | 348 MB |
131
+ | @zip.js/zip.js | WASM zlib | 165 MB | 453 MB |
132
+ | @zip.js/zip.js | pure-JS zlib | 155 MB | 467 MB |
133
+ | jszip | pako (JavaScript) | 103 MB | 303 MB |
134
+ | fflate | fflate (JavaScript) | **92 MB** | **119 MB** |
135
+
136
+ On the 20 MB stream `DecompressionStream` takes 61 ms and the WebAssembly inflate 72 ms, then
137
+ fflate 123 ms, jszip 133 ms and the pure-JavaScript port 154 ms. The WebAssembly row includes
138
+ the module instantiation a fresh process pays once, about 20 ms. Warm, the WebAssembly inflate
139
+ is 24 % faster than `DecompressionStream` in the [Codecs](#codecs-at-equal-output-size) table.
140
+ On 5,000 small files the order reverses: fflate takes 102 ms, jszip 440 ms and the three zip.js
141
+ rows 474 to 629 ms, with `DecompressionStream` the slowest of them, the same per-entry cost as
142
+ in compression. fflate uses the least memory on both workloads, 34 % of the
143
+ `DecompressionStream` row on the small files, and the WebAssembly and pure-JavaScript rows peak
144
+ higher than `DecompressionStream`. The three zip.js rows allocate the same 800 MB of stream objects over the
145
145
  5,000 entries, about 160 KB per entry. The WebAssembly and pure-JavaScript codecs run on the
146
146
  JavaScript thread, so V8's incremental marking keeps more of that garbage alive between
147
147
  collections, and that is the difference between the rows.
@@ -155,16 +155,16 @@ the Node streams directly.
155
155
 
156
156
  | Library | Codec | Median time | Peak memory (Δ) | Output |
157
157
  |---|---|--:|--:|--:|
158
- | @zip.js/zip.js | `CompressionStream` | **9055 ms** | 62 MB | 78.3 MB |
159
- | archiver | Node `zlib` (C) | 9115 ms | 71 MB | 78.3 MB |
160
- | fflate | fflate (JavaScript) | 12456 ms | **41 MB** | 80.2 MB |
161
- | @zip.js/zip.js | WASM zlib | 13629 ms | 88 MB | 78.9 MB |
162
- | @zip.js/zip.js | pure-JS zlib | 15485 ms | 120 MB | 78.9 MB |
163
- | jszip | pako (JavaScript) | 22539 ms | 74 MB | 78.9 MB |
164
-
165
- Every row streams: the peaks stay between 41 MB (fflate) and 120 MB (the pure-JavaScript port)
158
+ | @zip.js/zip.js | `CompressionStream` | **8927 ms** | 85 MB | 78.3 MB |
159
+ | archiver | Node `zlib` (C) | 9131 ms | 75 MB | 78.3 MB |
160
+ | fflate | fflate (JavaScript) | 12394 ms | **42 MB** | 80.2 MB |
161
+ | @zip.js/zip.js | WASM zlib | 13320 ms | 122 MB | 78.9 MB |
162
+ | @zip.js/zip.js | pure-JS zlib | 15082 ms | 116 MB | 78.9 MB |
163
+ | jszip | pako (JavaScript) | 22243 ms | 74 MB | 78.9 MB |
164
+
165
+ Every row streams: the peaks stay between 42 MB (fflate) and 122 MB (the WebAssembly zlib)
166
166
  over baseline on the 256 MB input. zip.js with `CompressionStream` and archiver take the same
167
- time within 1 %; jszip takes 2.5× that. With its WebAssembly zlib zip.js takes 1.5× the
167
+ time within 3 %; jszip takes 2.5× that. With its WebAssembly zlib zip.js takes 1.5× the
168
168
  `CompressionStream` time and 1.1× fflate's, for 1.6 % fewer bytes; the pure-JavaScript port
169
169
  takes 1.2× fflate's time.
170
170
 
@@ -187,17 +187,17 @@ fastest such row. A row with an empty last column is beaten by nothing on the ta
187
187
 
188
188
  | Codec | Setting | Output | Ratio | Time | Throughput | Beaten by |
189
189
  |---|---|--:|--:|--:|--:|---|
190
- | WASM zlib | level 8 | 6,115,980 | 3.429 | 1504 ms | 13.3 MB/s | |
191
- | `CompressionStream` | no level control | 6,120,503 | 3.426 | 716 ms | 27.9 MB/s | |
192
- | WASM zlib | level 6 | 6,165,103 | 3.402 | 1047 ms | 19.1 MB/s | `CompressionStream` |
193
- | fflate | level 8, its best | 6,239,025 | 3.361 | 767 ms | 26.1 MB/s | `CompressionStream` |
194
- | fflate | level 6 | 6,257,881 | 3.351 | 815 ms | 24.6 MB/s | `CompressionStream` |
195
- | WASM zlib | level 5 | 6,562,182 | 3.196 | 505 ms | 39.6 MB/s | |
196
- | fflate | level 5 | 6,648,574 | 3.154 | 541 ms | 36.9 MB/s | WASM zlib level 5 |
197
- | WASM zlib | level 4 | 6,907,987 | 3.036 | 278 ms | 71.9 MB/s | |
198
- | fflate | level 1 | 7,261,700 | 2.888 | 318 ms | 62.9 MB/s | WASM zlib level 4 |
199
- | WASM zlib | level 2 | 7,359,387 | 2.850 | 195 ms | 102.5 MB/s | |
200
- | WASM zlib | level 1 | 7,531,042 | 2.785 | 175 ms | 114.1 MB/s | |
190
+ | WASM zlib | level 8 | 6,115,980 | 3.429 | 1479 ms | 13.5 MB/s | |
191
+ | `CompressionStream` | no level control | 6,120,503 | 3.426 | 693 ms | 28.9 MB/s | |
192
+ | WASM zlib | level 6 | 6,165,103 | 3.402 | 1026 ms | 19.5 MB/s | `CompressionStream` |
193
+ | fflate | level 8, its best | 6,239,025 | 3.361 | 755 ms | 26.5 MB/s | `CompressionStream` |
194
+ | fflate | level 6 | 6,257,881 | 3.351 | 732 ms | 27.3 MB/s | `CompressionStream` |
195
+ | WASM zlib | level 5 | 6,562,182 | 3.196 | 494 ms | 40.5 MB/s | |
196
+ | fflate | level 5 | 6,648,574 | 3.154 | 532 ms | 37.6 MB/s | WASM zlib level 5 |
197
+ | WASM zlib | level 4 | 6,907,987 | 3.036 | 275 ms | 72.7 MB/s | |
198
+ | fflate | level 1 | 7,261,700 | 2.888 | 315 ms | 63.6 MB/s | WASM zlib level 4 |
199
+ | WASM zlib | level 2 | 7,359,387 | 2.850 | 195 ms | 102.8 MB/s | |
200
+ | WASM zlib | level 1 | 7,531,042 | 2.785 | 171 ms | 116.7 MB/s | |
201
201
 
202
202
  The pure-JavaScript port produces the same size as the WebAssembly codec at every level and
203
203
  takes 1.0 to 1.8× its time, the gap closing at the higher levels; the full 28-row table is in
@@ -212,16 +212,16 @@ Decompression of the same stream:
212
212
 
213
213
  | Codec | Time | Throughput |
214
214
  |---|--:|--:|
215
- | WASM zlib | **54 ms** | 369.5 MB/s |
216
- | `DecompressionStream` | 57 ms | 352.6 MB/s |
217
- | pure-JS zlib | 111 ms | 180.6 MB/s |
218
- | fflate | 128 ms | 156.0 MB/s |
215
+ | WASM zlib | **42 ms** | 479.8 MB/s |
216
+ | `DecompressionStream` | 55 ms | 365.1 MB/s |
217
+ | pure-JS zlib | 109 ms | 182.7 MB/s |
218
+ | fflate | 121 ms | 165.6 MB/s |
219
219
 
220
- The WebAssembly inflate and Node's `DecompressionStream` are within 6 % of each other; fflate's
221
- inflate takes 2.4× the time of the WebAssembly one, the pure-JavaScript port 2.1×.
220
+ The WebAssembly inflate is 24 % faster than Node's `DecompressionStream`; fflate's inflate takes
221
+ 2.9× the time of the WebAssembly one, the pure-JavaScript port 2.6×.
222
222
 
223
- Alone, the WebAssembly backend deflates at level 6 in 1.46× the time of `CompressionStream` for
224
- 0.7 % more bytes and inflates within 6 % of `DecompressionStream`. zip.js uses it by itself
223
+ Alone, the WebAssembly backend deflates at level 6 in 1.48× the time of `CompressionStream` for
224
+ 0.7 % more bytes and inflates 24 % faster than `DecompressionStream`. zip.js uses it by itself
225
225
  when no `CompressionStream` exists or when a level other than the default is requested;
226
226
  `useCompressionStream: false` selects it on every host, for an output that does not depend on
227
227
  the host's zlib.
@@ -234,21 +234,21 @@ that buys with each backend.
234
234
 
235
235
  | Configuration | Median time | Output | vs jszip |
236
236
  |---|--:|--:|--:|
237
- | **zip.js — `CompressionStream`, concurrent `add()`** | **676 ms** | 19.6 MB | **8.6×** |
238
- | fflate — async (its own worker pool) | 768 ms | 20.0 MB | 7.5× |
239
- | archiver — Node zlib | 2324 ms | 19.6 MB | 2.5× |
240
- | zip.js — `CompressionStream`, sequential `add()` | 2421 ms | 19.6 MB | 2.4× |
241
- | fflate — `zipSync` (single thread) | 3165 ms | 20.0 MB | 1.8× |
242
- | zip.js — WASM zlib, sequential `add()` | 3553 ms | 19.7 MB | 1.6× |
243
- | zip.js — WASM zlib, concurrent `add()` | 3577 ms | 19.7 MB | 1.6× |
244
- | zip.js — pure-JS zlib, concurrent `add()` | 3954 ms | 19.7 MB | 1.5× |
245
- | zip.js — pure-JS zlib, sequential `add()` | 4008 ms | 19.7 MB | 1.4× |
246
- | jszip (pako, single thread) | 5788 ms | 19.7 MB | 1.0× |
247
-
248
- With the host's `CompressionStream`, zip.js goes from 2421 ms with sequential `add()` calls to
249
- 676 ms with concurrent ones, 3.6×, without Web Workers: Node runs `CompressionStream` off the
237
+ | **zip.js — `CompressionStream`, concurrent `add()`** | **598 ms** | 19.6 MB | **9.4×** |
238
+ | fflate — async (its own worker pool) | 764 ms | 20.0 MB | 7.4× |
239
+ | zip.js — `CompressionStream`, sequential `add()` | 2245 ms | 19.6 MB | 2.5× |
240
+ | archiver — Node zlib | 2270 ms | 19.6 MB | 2.5× |
241
+ | fflate — `zipSync` (single thread) | 3083 ms | 20.0 MB | 1.8× |
242
+ | zip.js — WASM zlib, concurrent `add()` | 3369 ms | 19.7 MB | 1.7× |
243
+ | zip.js — WASM zlib, sequential `add()` | 3389 ms | 19.7 MB | 1.7× |
244
+ | zip.js — pure-JS zlib, concurrent `add()` | 3822 ms | 19.7 MB | 1.5× |
245
+ | zip.js — pure-JS zlib, sequential `add()` | 3872 ms | 19.7 MB | 1.5× |
246
+ | jszip (pako, single thread) | 5629 ms | 19.7 MB | 1.0× |
247
+
248
+ With the host's `CompressionStream`, zip.js goes from 2245 ms with sequential `add()` calls to
249
+ 598 ms with concurrent ones, 3.8×, without Web Workers: Node runs `CompressionStream` off the
250
250
  main thread, so the entries compress on several cores while the JavaScript thread only feeds
251
- them. fflate's async API, which runs its own worker pool, takes 768 ms on the same input; its
251
+ them. fflate's async API, which runs its own worker pool, takes 764 ms on the same input; its
252
252
  output is 2 % larger than the zlib rows, so it does slightly less work. The WebAssembly and
253
253
  pure-JavaScript backends run on the main thread, and concurrent `add()` changes nothing for them.
254
254
 
@@ -269,22 +269,23 @@ JavaScript thread. Same 8 × 8 MB workload as the backends table, level 6. That
269
269
  process per run and this one measures in-process after a warmup, which is why their Node numbers
270
270
  differ:
271
271
 
272
- | Runtime | sequential `add()` | concurrent `add()` | concurrent `add()`, `chunkSize` 256 KB | concurrent `add()` + `useWebWorkers` |
272
+ | Runtime | sequential `add()` | concurrent `add()` | concurrent `add()`, `chunkSize` 64 KB | concurrent `add()` + `useWebWorkers` |
273
273
  |---|--:|--:|--:|--:|
274
- | Node.js v26.7.0 | 2.25 s | **0.59 s** | 0.59 s | 0.59 s |
275
- | Bun 1.4.2 | 1.19 s | 1.19 s | **0.24 s** | 0.25 s |
276
- | Deno 2.9.6 | 1.31 s | 1.31 s | 1.33 s | **0.34 s** |
274
+ | Node.js v26.7.0 | 2.23 s | **0.59 s** | 0.59 s | 0.61 s |
275
+ | Bun 1.4.2 | 1.20 s | **0.24 s** | 1.20 s | 0.25 s |
276
+ | Deno 2.9.7 | 1.39 s | 1.30 s | 1.31 s | **0.34 s** |
277
277
 
278
278
  - **Node** runs `CompressionStream` off the JavaScript thread, on the libuv threadpool, four
279
279
  threads by default (`UV_THREADPOOL_SIZE`): concurrent `add()` alone is 3.8× faster than
280
280
  sequential, and the chunk size changes nothing. Node has no `Worker` global, so
281
281
  `useWebWorkers` spawns nothing there and the entries run in-process, in the same time.
282
282
  - **Bun** runs `CompressionStream` off the JavaScript thread only for writes larger than 128 KB
283
- (its native implementation, since Bun 1.4 in August 2026). zip.js writes 64 KB chunks by
284
- default, so concurrent `add()` alone gains nothing on Bun; `chunkSize: 256 * 1024` makes it
285
- 4.9× faster, and `useWebWorkers: true` 4.7×.
283
+ (its native implementation, since Bun 1.4 in August 2026). With the 256 KB chunks zip.js
284
+ writes by default, concurrent `add()` alone is 5.0× faster than sequential; with the 64 KB
285
+ chunks that were the default up to version 2.18.2 it gains nothing, and `useWebWorkers: true`
286
+ is 4.8× faster.
286
287
  - **Deno** runs `CompressionStream` on the JavaScript thread whatever the write size: concurrent
287
- `add()` alone gains nothing, `useWebWorkers: true` is 3.9× faster.
288
+ `add()` alone gains 7 %, `useWebWorkers: true` is 4.1× faster.
288
289
 
289
290
  ### Compression levels
290
291
 
@@ -294,13 +295,13 @@ column runs the same JavaScript on each engine. One 20 MB text entry, one thread
294
295
 
295
296
  | Runtime | level 6, `CompressionStream` | level 5, WASM zlib | level 5, pure-JS zlib | level 1, WASM zlib |
296
297
  |---|--:|--:|--:|--:|
297
- | Node.js v26.7.0 | 686 ms / 6.12 MB | 521 ms / 6.56 MB | 780 ms / 6.56 MB | 192 ms / 7.53 MB |
298
- | Bun 1.4.2 | 371 ms / 6.20 MB | 464 ms / 6.56 MB | 665 ms / 6.56 MB | 168 ms / 7.53 MB |
299
- | Deno 2.9.6 | 403 ms / 6.21 MB | 590 ms / 6.56 MB | 689 ms / 6.56 MB | 228 ms / 7.53 MB |
298
+ | Node.js v26.7.0 | 694 ms / 6.12 MB | 509 ms / 6.56 MB | 773 ms / 6.56 MB | 185 ms / 7.53 MB |
299
+ | Bun 1.4.2 | 368 ms / 6.20 MB | 459 ms / 6.56 MB | 661 ms / 6.56 MB | 177 ms / 7.53 MB |
300
+ | Deno 2.9.7 | 408 ms / 6.20 MB | 574 ms / 6.56 MB | 666 ms / 6.56 MB | 219 ms / 7.53 MB |
300
301
 
301
- On Node, level 5 on the WebAssembly codec is 24 % faster than the default for 7 % more bytes.
302
+ On Node, level 5 on the WebAssembly codec is 27 % faster than the default for 7 % more bytes.
302
303
  On Bun and Deno the default is already faster than level 5, so level 5 there is slower and
303
- larger; only level 1 buys speed on them, 55 % on Bun and 44 % on Deno, at 21 % more bytes. The
304
+ larger; only level 1 buys speed on them, 52 % on Bun and 46 % on Deno, at 21 % more bytes. The
304
305
  pure-JavaScript port at level 5 takes 1.5× the WebAssembly time on Node, 1.4× on Bun and 1.2×
305
306
  on Deno, and is slower than the default on every runtime. The two bundled codecs produce the
306
307
  same size on every runtime; the level-6 sizes differ because they come from three different
@@ -310,17 +311,17 @@ zlib builds.
310
311
 
311
312
  On bulk data zip.js adds almost nothing over the host's `CompressionStream`, so its throughput is
312
313
  that of the zlib the runtime ships, and they differ. One 256 MB text file, one thread, in
313
- memory, fed in 64 KB writes as zip.js does; gzip is Apple's, timed as a child process:
314
+ memory, fed in 256 KB writes as zip.js does; gzip is Apple's, timed as a child process:
314
315
 
315
316
  | Runtime | `CompressionStream` alone | zip.js, one entry | Output |
316
317
  |---|--:|--:|--:|
317
- | Node.js v26.7.0 | 8.99 s | 8.78 s | 78.3 MB |
318
- | Bun 1.4.2 | 4.73 s | 4.77 s | 79.4 MB |
319
- | Deno 2.9.6 | 5.13 s | 5.23 s | 79.5 MB |
320
- | Apple `gzip -6`, classic zlib | 12.3 to 12.4 s | | 78.9 MB |
318
+ | Node.js v26.7.0 | 8.85 s | 8.91 s | 78.3 MB |
319
+ | Bun 1.4.2 | 4.72 s | 4.75 s | 79.4 MB |
320
+ | Deno 2.9.7 | 5.05 s | 5.20 s | 79.4 MB |
321
+ | Apple `gzip -6`, classic zlib | 12.2 to 12.6 s | | 78.9 MB |
321
322
 
322
- zip.js is within 2 % of the raw stream on every runtime. On this file Bun's `CompressionStream`
323
- takes 53 % of Node's time and Deno's 57 %, for about 1.5 % more bytes; Apple's gzip takes 1.4×
323
+ zip.js is within 3 % of the raw stream on every runtime. On this file Bun's `CompressionStream`
324
+ takes 53 % of Node's time and Deno's 57 %, for about 1.4 % more bytes; Apple's gzip takes 1.4×
324
325
  Node's time. Which zlib each runtime ships is not verified here; the table measures the result.
325
326
 
326
327
  ## Encryption: AES-256 on a stored entry
@@ -334,29 +335,29 @@ one archive written, then read back.
334
335
 
335
336
  | Engine | Node.js | Bun | Deno |
336
337
  |---|--:|--:|--:|
337
- | WebAssembly, linked into the module of the WebAssembly builds | **131 / 130 MB/s** | **118 / 121** | 123 / 122 |
338
- | JavaScript, the fallback of those builds and the engine of the native and core builds | 114 / 114 | 112 / 111 | **142 / 129** |
339
- | 2.13.1, sjcl | 25 / 25 | 27 / 27 | 19 / 19 |
338
+ | WebAssembly, linked into the module of the WebAssembly builds | **138 / 139 MB/s** | **122 / 124** | 128 / 128 |
339
+ | JavaScript, the fallback of those builds and the engine of the native and core builds | 118 / 107 | 115 / 115 | **145 / 145** |
340
+ | 2.13.1, sjcl | 26 / 26 | 28 / 28 | 20 / 19 |
340
341
 
341
342
  The same measurement in a browser, through the Web Worker pool, on 32 MB of bytes generated in
342
343
  the page (the corpus is not served to the browser), median of 3 passes
343
344
  ([`benchmarks/bench-aes.html`](benchmarks/bench-aes.html), which prints its result and writes no
344
345
  file):
345
346
 
346
- | Engine | Firefox 154 | Chrome 153 |
347
+ | Engine | Firefox 156 | Chrome 153 |
347
348
  |---|--:|--:|
348
- | WebAssembly | **96 / 101 MB/s** | **118 / 121** |
349
- | JavaScript | 68 / 69 | 100 / 98 |
349
+ | WebAssembly | **125 / 125 MB/s** | **136 / 138** |
350
+ | JavaScript | 79 / 79 | 104 / 104 |
350
351
  | 2.13.1, sjcl | 17 / 17 | 22 / 22 |
351
352
 
352
- - The JavaScript engine is 4.0 to 4.6× sjcl on Node, Bun and in the two browsers, and 6.8 to
353
- 7.5× on Deno, where sjcl was slowest. sjcl ran the cipher 16 bytes
353
+ - The JavaScript engine is 4.1 to 4.7× sjcl on Node, Bun and in the two browsers, and 7.3 to
354
+ 7.6× on Deno, where sjcl was slowest. sjcl ran the cipher 16 bytes
354
355
  at a time through a bit-array layer; the new engine works on typed arrays and is fed whole
355
356
  chunks, with the AES rounds and the SHA-1 steps written out. Every build has it.
356
357
  - The WebAssembly kernel is the same C code on every host, with the same rounds written out:
357
- 118 to 131 MB/s on Node, Bun and Deno, 96 to 121 MB/s in the two browsers. The JavaScript
358
- engine is within 13 % of it on Node and Bun, ahead of it on Deno, and 1.2 to 1.4× slower in
359
- the browsers. The WebAssembly builds use
358
+ 122 to 139 MB/s on Node, Bun and Deno, 125 to 138 MB/s in the two browsers. The JavaScript
359
+ engine is within 8 % of it on Bun and 23 % on Node, ahead of it on Deno, and 1.3 to 1.6×
360
+ slower in the browsers. The WebAssembly builds use
360
361
  the kernel whenever their module loads and fall back to the JavaScript engine when it cannot,
361
362
  for instance under a Content Security Policy that does not allow WebAssembly
362
363
  (`'wasm-unsafe-eval'`).
@@ -369,8 +370,8 @@ file):
369
370
  bytes in 2.13.1 to 67,820 in 2.14.0, `dist/zip-native.min.js` from 73,798 to 72,060 and
370
371
  `dist/zip-core.min.js` from 36,405 to 35,493 (`git show <tag>:<file> | gzip -9 | wc -c`):
371
372
  sjcl's removal outweighs the JavaScript engine and the kernel. Writing the rounds out after
372
- 2.14.0 gives some of it back: 71,238, 73,339 and 36,235 bytes for the three files behind the
373
- encryption tables above.
373
+ 2.14.0 gave some of it back, and the three files behind the encryption tables above gzip to
374
+ 73,184, 75,050 and 37,264 bytes.
374
375
 
375
376
  ## Reproduce
376
377
 
@@ -390,7 +391,10 @@ node bench-runtimes.js # the runtime tables; also: bun bench-runtimes.js, deno
390
391
 
391
392
  Each script writes its JSON to `benchmarks/results/`; the `*-run.log` files there are the
392
393
  console output of the runs behind this page. `RUNS=<n>` sets the number of repetitions (default
393
- 3, 5 in `bench-aes.js`). `ZIPJS_BUNDLE=<path to a previous index.min.js> node bench-aes.js` adds
394
+ 3, 5 in `bench-aes.js`). `QUICK=1` divides every workload by 8, runs each measurement once and
395
+ writes under `benchmarks/results/quick/`; the scripts then finish in seconds instead of minutes,
396
+ which is enough to check that they run and not enough to read a number from.
397
+ `ZIPJS_BUNDLE=<path to a previous index.min.js> node bench-aes.js` adds
394
398
  the row of a previous release; `git show v2.13.1:index.min.js` gave the sjcl row. The browser
395
399
  encryption table comes from `benchmarks/bench-aes.html`, served from the repository root
396
400
  (`npx http-server -p 8080`, then `http://localhost:8080/benchmarks/bench-aes.html`), with
package/deno.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zip-js/zip-js",
3
- "version": "2.18.2",
3
+ "version": "2.19.0",
4
4
  "exports": {
5
5
  ".": "./index.js",
6
6
  "./external": "./lib/zip-fs-external.js",