@zip.js/zip.js 2.8.36 → 2.8.38

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 (42) hide show
  1. package/deno.json +4 -2
  2. package/dist/zip-core.js +356 -63
  3. package/dist/zip-core.min.js +1 -1
  4. package/dist/zip-fs-core.js +301 -63
  5. package/dist/zip-fs-core.min.js +1 -1
  6. package/dist/zip-fs-native.js +359 -66
  7. package/dist/zip-fs-native.min.js +1 -1
  8. package/dist/zip-fs.js +357 -64
  9. package/dist/zip-fs.min.js +1 -1
  10. package/dist/zip-legacy.js +358 -65
  11. package/dist/zip-legacy.min.js +1 -1
  12. package/dist/zip-native.js +359 -66
  13. package/dist/zip-native.min.js +1 -1
  14. package/dist/zip-web-worker-native.js +1 -1
  15. package/dist/zip-web-worker.js +1 -1
  16. package/dist/zip.js +357 -64
  17. package/dist/zip.min.js +1 -1
  18. package/index-native.cjs +359 -66
  19. package/index-native.min.js +1 -1
  20. package/index.cjs +357 -64
  21. package/index.d.cts +126 -4
  22. package/index.d.ts +126 -4
  23. package/index.min.js +1 -1
  24. package/lib/core/codec-pool.js +21 -4
  25. package/lib/core/codec-registry.js +116 -0
  26. package/lib/core/codec-worker.js +94 -18
  27. package/lib/core/configuration.js +2 -0
  28. package/lib/core/constants.js +5 -1
  29. package/lib/core/options.js +2 -0
  30. package/lib/core/streams/codec-stream.js +6 -2
  31. package/lib/core/streams/zip-entry-stream.js +50 -16
  32. package/lib/core/streams/zlib-js/zlib-streams.min.js +1 -1
  33. package/lib/core/web-worker-base.js +30 -8
  34. package/lib/core/web-worker-inline-native.js +1 -1
  35. package/lib/core/web-worker-inline-wasm.js +1 -1
  36. package/lib/core/zip-reader.js +29 -3
  37. package/lib/core/zip-writer.js +68 -25
  38. package/lib/zip-core-base.js +7 -0
  39. package/lib/zip-core-reader.js +2 -1
  40. package/lib/zip-core-writer.js +1 -0
  41. package/package.json +1 -1
  42. package/worker-message-property-names.js +1 -1
@@ -26,7 +26,7 @@
26
26
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
27
  */
28
28
 
29
- /* global setTimeout, clearTimeout */
29
+ /* global setTimeout, clearTimeout, URL */
30
30
 
31
31
  import { UNDEFINED_VALUE } from "./constants.js";
32
32
  import {
@@ -38,7 +38,8 @@ import {
38
38
  ERR_INVALID_COMPRESSED_DATA,
39
39
  ERR_ABORT_CHECK_PASSWORD
40
40
  } from "./streams/codec-stream.js";
41
- import { CodecWorker } from "./codec-worker.js";
41
+ import { ensureCodecStreams } from "./codec-registry.js";
42
+ import { CodecWorker, resetWebWorkerSupport } from "./codec-worker.js";
42
43
 
43
44
  let pool = [];
44
45
  const pendingRequests = [];
@@ -61,11 +62,18 @@ let indexWorker = 0;
61
62
 
62
63
  async function runWorker(stream, workerOptions) {
63
64
  const { options, config } = workerOptions;
64
- const { transferStreams, useWebWorkers, useCompressionStream, compressed, signed, encrypted } = options;
65
+ const { transferStreams, useWebWorkers, useCompressionStream, compressed, signed, encrypted, format, codecURI } = options;
65
66
  const { workerURI, maxWorkers } = config;
67
+ if (format) {
68
+ if (codecURI) {
69
+ options.codecURI = resolveCodecURI(codecURI, config.baseURI);
70
+ }
71
+ await ensureCodecStreams(format, options.codecURI);
72
+ }
66
73
  workerOptions.transferStreams = transferStreams || (transferStreams === UNDEFINED_VALUE && config.transferStreams);
67
74
  const streamCopy = !compressed && !signed && !encrypted;
68
- workerOptions.useWebWorkers = !streamCopy && (useWebWorkers || (useWebWorkers === UNDEFINED_VALUE && config.useWebWorkers));
75
+ const workerSupported = format === UNDEFINED_VALUE || Boolean(options.codecURI);
76
+ workerOptions.useWebWorkers = !streamCopy && workerSupported && (useWebWorkers || (useWebWorkers === UNDEFINED_VALUE && config.useWebWorkers));
69
77
  workerOptions.workerURI = workerOptions.useWebWorkers && workerURI ? workerURI : UNDEFINED_VALUE;
70
78
  options.useCompressionStream = useCompressionStream || (useCompressionStream === UNDEFINED_VALUE && config.useCompressionStream);
71
79
  return (await getWorker()).run();
@@ -105,6 +113,14 @@ async function runWorker(stream, workerOptions) {
105
113
  }
106
114
  }
107
115
 
116
+ function resolveCodecURI(codecURI, baseURI) {
117
+ try {
118
+ return new URL(codecURI, baseURI).toString();
119
+ } catch {
120
+ return codecURI;
121
+ }
122
+ }
123
+
108
124
  function armStarvationTimeout() {
109
125
  if (!starvationTimeout && pendingRequests.length && Number.isFinite(starvationDelay) && starvationDelay >= 0) {
110
126
  starvationTimeout = setTimeout(onWorkerStarvation, starvationDelay);
@@ -165,4 +181,5 @@ async function terminateWorkers() {
165
181
  clearTerminateTimeout(workerData);
166
182
  return workerData.terminate();
167
183
  }));
184
+ resetWebWorkerSupport();
168
185
  }
@@ -0,0 +1,116 @@
1
+ /*
2
+ Copyright (c) 2025 Gildas Lormeau. All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice,
8
+ this list of conditions and the following disclaimer.
9
+
10
+ 2. Redistributions in binary form must reproduce the above copyright
11
+ notice, this list of conditions and the following disclaimer in
12
+ the documentation and/or other materials provided with the distribution.
13
+
14
+ 3. The names of the authors may not be used to endorse or promote products
15
+ derived from this software without specific prior written permission.
16
+
17
+ THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
18
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
19
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
20
+ INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
21
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
23
+ OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
26
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+ */
28
+
29
+ import {
30
+ MAX_16_BITS,
31
+ COMPRESSION_METHOD_STORE,
32
+ COMPRESSION_METHOD_DEFLATE,
33
+ COMPRESSION_METHOD_DEFLATE_64,
34
+ COMPRESSION_METHOD_AES,
35
+ FUNCTION_TYPE
36
+ } from "./constants.js";
37
+
38
+ const ERR_INVALID_CODEC_DEFINITION = "Invalid codec definition";
39
+ const ERR_RESERVED_COMPRESSION_METHOD = "Reserved compression method";
40
+ const ERR_INVALID_CODEC_MODULE = "Invalid codec module";
41
+
42
+ const STRING_TYPE = "string";
43
+ const RESERVED_COMPRESSION_METHODS = [
44
+ COMPRESSION_METHOD_STORE,
45
+ COMPRESSION_METHOD_DEFLATE,
46
+ COMPRESSION_METHOD_DEFLATE_64,
47
+ COMPRESSION_METHOD_AES
48
+ ];
49
+
50
+ const registeredCodecs = new Map();
51
+ const codecStreams = new Map();
52
+
53
+ export {
54
+ ERR_INVALID_CODEC_DEFINITION,
55
+ ERR_RESERVED_COMPRESSION_METHOD,
56
+ ERR_INVALID_CODEC_MODULE,
57
+ registerCodec,
58
+ unregisterCodec,
59
+ getRegisteredCodec,
60
+ getCodecStreams,
61
+ setCodecStreams,
62
+ ensureCodecStreams
63
+ };
64
+
65
+ function registerCodec(codec = {}) {
66
+ const { compressionMethod, format, codecURI, CompressionStream, DecompressionStream, versionNeeded } = codec;
67
+ if (!Number.isInteger(compressionMethod) || compressionMethod < 0 || compressionMethod > MAX_16_BITS ||
68
+ typeof format != STRING_TYPE || !format.length) {
69
+ throw new Error(ERR_INVALID_CODEC_DEFINITION);
70
+ }
71
+ if (RESERVED_COMPRESSION_METHODS.includes(compressionMethod)) {
72
+ throw new Error(ERR_RESERVED_COMPRESSION_METHOD);
73
+ }
74
+ const hasStreams = typeof CompressionStream == FUNCTION_TYPE || typeof DecompressionStream == FUNCTION_TYPE;
75
+ if (!hasStreams && (typeof codecURI != STRING_TYPE || !codecURI.length)) {
76
+ throw new Error(ERR_INVALID_CODEC_DEFINITION);
77
+ }
78
+ registeredCodecs.set(compressionMethod, { compressionMethod, format, codecURI, versionNeeded });
79
+ if (hasStreams) {
80
+ setCodecStreams(format, { CompressionStream, DecompressionStream });
81
+ }
82
+ }
83
+
84
+ function unregisterCodec(compressionMethod) {
85
+ const codec = registeredCodecs.get(compressionMethod);
86
+ if (codec) {
87
+ registeredCodecs.delete(compressionMethod);
88
+ let formatUsed;
89
+ registeredCodecs.forEach(otherCodec => formatUsed = formatUsed || otherCodec.format == codec.format);
90
+ if (!formatUsed) {
91
+ codecStreams.delete(codec.format);
92
+ }
93
+ }
94
+ }
95
+
96
+ function getRegisteredCodec(compressionMethod) {
97
+ return registeredCodecs.get(compressionMethod);
98
+ }
99
+
100
+ function getCodecStreams(format) {
101
+ return codecStreams.get(format);
102
+ }
103
+
104
+ function setCodecStreams(format, streams) {
105
+ const { CompressionStream, DecompressionStream } = streams;
106
+ if (typeof CompressionStream != FUNCTION_TYPE && typeof DecompressionStream != FUNCTION_TYPE) {
107
+ throw new Error(ERR_INVALID_CODEC_MODULE);
108
+ }
109
+ codecStreams.set(format, { CompressionStream, DecompressionStream });
110
+ }
111
+
112
+ async function ensureCodecStreams(format, codecURI) {
113
+ if (!codecStreams.has(format) && codecURI) {
114
+ setCodecStreams(format, await import(codecURI));
115
+ }
116
+ }
@@ -39,6 +39,7 @@ import {
39
39
  CODEC_DEFLATE,
40
40
  CodecStream,
41
41
  ChunkStream,
42
+ supportsDeflateRaw,
42
43
  MESSAGE_EVENT_TYPE,
43
44
  MESSAGE_START,
44
45
  MESSAGE_PULL,
@@ -50,6 +51,7 @@ import {
50
51
  const MODULE_WORKER_OPTIONS = { type: "module" };
51
52
  const ERROR_EVENT_TYPE = "error";
52
53
  const MESSAGE_ERROR_EVENT_TYPE = "messageerror";
54
+ const ERR_WORKER_STARTUP_TIMEOUT = "Worker startup timeout";
53
55
 
54
56
  let webWorkerSupported, webWorkerSource, webWorkerURI, webWorkerOptions;
55
57
  let transferStreamsSupported = true;
@@ -62,13 +64,18 @@ let initModule = () => { };
62
64
 
63
65
  export {
64
66
  CodecWorker,
65
- configureWorker
67
+ configureWorker,
68
+ resetWebWorkerSupport
66
69
  };
67
70
 
68
71
  function configureWorker({ initModule: initModuleFunction }) {
69
72
  initModule = initModuleFunction;
70
73
  }
71
74
 
75
+ function resetWebWorkerSupport() {
76
+ webWorkerSupported = UNDEFINED_VALUE;
77
+ }
78
+
72
79
  class CodecWorker {
73
80
 
74
81
  constructor(workerData, { readable, writable }, { options, config, streamOptions, useWebWorkers, transferStreams, workerURI }, onTaskFinished) {
@@ -162,7 +169,7 @@ function createWorkerInterface(workerData, config) {
162
169
  }
163
170
 
164
171
  function createWebWorkerInterface(workerData, config) {
165
- const { baseURI, chunkSize } = config;
172
+ const { baseURI, chunkSize, workerStartupTimeout } = config;
166
173
  let { wasmURI } = config;
167
174
 
168
175
  if (!workerData.interface) {
@@ -179,9 +186,26 @@ function createWebWorkerInterface(workerData, config) {
179
186
  }
180
187
  Object.assign(workerData, {
181
188
  worker,
189
+ workerAlive: false,
182
190
  terminated: false,
183
191
  interface: {
184
- run: () => runWebWorker(workerData, { chunkSize, wasmURI, baseURI })
192
+ run: async () => {
193
+ try {
194
+ return await runWebWorker(workerData, { chunkSize, wasmURI, baseURI, workerStartupTimeout });
195
+ } catch (error) {
196
+ if (error && error.workerStartupFailed) {
197
+ webWorkerSupported = false;
198
+ const { reader } = workerData;
199
+ if (reader) {
200
+ reader.releaseLock();
201
+ }
202
+ workerData.reader = null;
203
+ workerData.writer = null;
204
+ return runWorker(workerData, config);
205
+ }
206
+ throw error;
207
+ }
208
+ }
185
209
  }
186
210
  });
187
211
  }
@@ -191,15 +215,23 @@ function createWebWorkerInterface(workerData, config) {
191
215
  async function runWorker({ options, readable, writable, onTaskFinished }, config) {
192
216
  let codecStream;
193
217
  try {
194
- if (!options.useCompressionStream) {
195
- try {
196
- await initModule(config);
197
- } catch {
198
- const ZlibStream = options.codecType.startsWith(CODEC_DEFLATE) ?
199
- config.CompressionStreamZlib :
200
- config.DecompressionStreamZlib;
201
- if (!ZlibStream || ZlibStream.requiresModule) {
202
- options.useCompressionStream = true;
218
+ if (options.compressed && !options.format) {
219
+ const deflate = options.codecType.startsWith(CODEC_DEFLATE);
220
+ const ZlibStream = deflate ? config.CompressionStreamZlib : config.DecompressionStreamZlib;
221
+ const NativeStream = deflate ? config.CompressionStream : config.DecompressionStream;
222
+ if (!options.useCompressionStream) {
223
+ try {
224
+ await initModule(config);
225
+ } catch {
226
+ if (!ZlibStream || ZlibStream.requiresModule) {
227
+ options.useCompressionStream = true;
228
+ }
229
+ }
230
+ } else if (ZlibStream && ZlibStream.requiresModule && !supportsDeflateRaw(NativeStream)) {
231
+ try {
232
+ await initModule(config);
233
+ } catch {
234
+ // ignored
203
235
  }
204
236
  }
205
237
  }
@@ -268,6 +300,10 @@ async function runWebWorker(workerData, config) {
268
300
  writer: writable.getWriter()
269
301
  });
270
302
  }
303
+ const { workerStartupTimeout } = config;
304
+ if (!workerData.workerAlive && Number.isFinite(workerStartupTimeout) && workerStartupTimeout >= 0) {
305
+ workerData.startupTimeout = setTimeout(() => onStartupTimeout(workerData), workerStartupTimeout);
306
+ }
271
307
  try {
272
308
  const resultValue = await result;
273
309
  await closeWritable();
@@ -380,28 +416,68 @@ function getWebWorker(url, baseURI, workerData, isModuleType, useBlobURI = true)
380
416
  } else {
381
417
  worker = new Worker(webWorkerURI, webWorkerOptions);
382
418
  }
383
- worker.addEventListener(MESSAGE_EVENT_TYPE, event => onMessage(event, workerData));
419
+ worker.addEventListener(MESSAGE_EVENT_TYPE, event => {
420
+ workerData.workerAlive = true;
421
+ clearStartupTimeout(workerData);
422
+ onMessage(event, workerData);
423
+ });
384
424
  worker.addEventListener(ERROR_EVENT_TYPE, event => onWorkerError(event, workerData));
385
425
  worker.addEventListener(MESSAGE_ERROR_EVENT_TYPE, event => onWorkerError(event, workerData));
386
426
  return worker;
387
427
  }
388
428
 
429
+ function onStartupTimeout(workerData) {
430
+ workerData.startupTimeout = null;
431
+ if (workerData.workerAlive) {
432
+ return;
433
+ }
434
+ const { rejectResult, writer } = workerData;
435
+ terminateWorker(workerData);
436
+ workerData.worker = null;
437
+ if (rejectResult) {
438
+ const error = new Error(ERR_WORKER_STARTUP_TIMEOUT);
439
+ error.workerStartupFailed = true;
440
+ rejectResult(error);
441
+ if (writer) {
442
+ writer.releaseLock();
443
+ }
444
+ }
445
+ }
446
+
447
+ function clearStartupTimeout(workerData) {
448
+ const { startupTimeout } = workerData;
449
+ if (startupTimeout) {
450
+ clearTimeout(startupTimeout);
451
+ workerData.startupTimeout = null;
452
+ }
453
+ }
454
+
389
455
  function onWorkerError(event, workerData) {
390
456
  if (event.preventDefault) {
391
457
  event.preventDefault();
392
458
  }
393
- const { rejectResult, writer, onTaskFinished } = workerData;
459
+ clearStartupTimeout(workerData);
460
+ const { workerAlive, rejectResult, writer, onTaskFinished } = workerData;
394
461
  terminateWorker(workerData);
462
+ if (!workerAlive) {
463
+ workerData.worker = null;
464
+ }
395
465
  if (rejectResult) {
396
- rejectResult(event.error || new Error(event.message || ERROR_EVENT_TYPE));
466
+ let error = event.error || new Error(event.message || ERROR_EVENT_TYPE);
467
+ if (!workerAlive) {
468
+ error = Object.assign(new Error(error.message || ERROR_EVENT_TYPE), { workerStartupFailed: true });
469
+ }
470
+ rejectResult(error);
397
471
  if (writer) {
398
472
  writer.releaseLock();
399
473
  }
400
- onTaskFinished();
474
+ if (workerAlive) {
475
+ onTaskFinished();
476
+ }
401
477
  }
402
478
  }
403
479
 
404
- function sendMessage(message, { worker, writer, transferStreams }) {
480
+ function sendMessage(message, { worker, writer, transferStreams, workerAlive }) {
405
481
  try {
406
482
  const { value, readable, writable } = message;
407
483
  const transferables = [];
@@ -409,7 +485,7 @@ function sendMessage(message, { worker, writer, transferStreams }) {
409
485
  message.value = toExactUint8Array(value);
410
486
  transferables.push(message.value.buffer);
411
487
  }
412
- if (transferStreams && transferStreamsSupported) {
488
+ if (transferStreams && transferStreamsSupported && workerAlive) {
413
489
  if (readable) {
414
490
  transferables.push(readable);
415
491
  }
@@ -49,6 +49,7 @@ const DEFAULT_CONFIGURATION = {
49
49
  maxWorkers,
50
50
  terminateWorkerTimeout: 5000,
51
51
  workerStarvationTimeout: 5000,
52
+ workerStartupTimeout: 5000,
52
53
  useWebWorkers: true,
53
54
  useCompressionStream: true,
54
55
  transferStreams: true,
@@ -64,6 +65,7 @@ const CONFIGURABLE_PROPERTY_NAMES = [
64
65
  "maxWorkers",
65
66
  "terminateWorkerTimeout",
66
67
  "workerStarvationTimeout",
68
+ "workerStartupTimeout",
67
69
  "useCompressionStream",
68
70
  "useWebWorkers",
69
71
  "transferStreams",
@@ -37,6 +37,7 @@ const COMPRESSION_METHOD_AES = 0x63;
37
37
  const LOCAL_FILE_HEADER_SIGNATURE = 0x04034b50;
38
38
  const SPLIT_ZIP_FILE_SIGNATURE = 0x08074b50;
39
39
  const DATA_DESCRIPTOR_RECORD_SIGNATURE = SPLIT_ZIP_FILE_SIGNATURE;
40
+ const ARCHIVE_EXTRA_DATA_SIGNATURE = 0x08064b50;
40
41
  const CENTRAL_FILE_HEADER_SIGNATURE = 0x02014b50;
41
42
  const END_OF_CENTRAL_DIR_SIGNATURE = 0x06054b50;
42
43
  const ZIP64_END_OF_CENTRAL_DIR_SIGNATURE = 0x06064b50;
@@ -68,6 +69,7 @@ const BITFLAG_LEVEL_MAX_MASK = 0b010;
68
69
  const BITFLAG_LEVEL_FAST_MASK = 0b100;
69
70
  const BITFLAG_LEVEL_SUPER_FAST_MASK = 0b110;
70
71
  const BITFLAG_DATA_DESCRIPTOR = 0b1000;
72
+ const BITFLAG_STRONG_ENCRYPTION = 0b1000000;
71
73
  const BITFLAG_LANG_ENCODING_FLAG = 0b100000000000;
72
74
  const FILE_ATTR_MSDOS_DIR_MASK = 0b10000;
73
75
  const FILE_ATTR_MSDOS_READONLY_MASK = 0x01;
@@ -95,7 +97,7 @@ const HEADER_OFFSET_COMPRESSED_SIZE = 14;
95
97
  const HEADER_OFFSET_UNCOMPRESSED_SIZE = 18;
96
98
  const LOCAL_HEADER_COMMON_OFFSET = 4;
97
99
 
98
- const MAX_DATE = new Date(2107, 11, 31);
100
+ const MAX_DATE = new Date(2107, 11, 31, 23, 59, 58);
99
101
  const MIN_DATE = new Date(1980, 0, 1);
100
102
 
101
103
  const UNDEFINED_VALUE = undefined;
@@ -116,6 +118,7 @@ export {
116
118
  COMPRESSION_METHOD_STORE,
117
119
  COMPRESSION_METHOD_AES,
118
120
  SPLIT_ZIP_FILE_SIGNATURE,
121
+ ARCHIVE_EXTRA_DATA_SIGNATURE,
119
122
  LOCAL_FILE_HEADER_SIGNATURE,
120
123
  DATA_DESCRIPTOR_RECORD_SIGNATURE,
121
124
  CENTRAL_FILE_HEADER_SIGNATURE,
@@ -146,6 +149,7 @@ export {
146
149
  BITFLAG_LEVEL_FAST_MASK,
147
150
  BITFLAG_LEVEL_SUPER_FAST_MASK,
148
151
  BITFLAG_DATA_DESCRIPTOR,
152
+ BITFLAG_STRONG_ENCRYPTION,
149
153
  BITFLAG_LANG_ENCODING_FLAG,
150
154
  FILE_ATTR_MSDOS_DIR_MASK,
151
155
  FILE_ATTR_MSDOS_READONLY_MASK,
@@ -46,6 +46,7 @@ const OPTION_TRANSFER_STREAMS = "transferStreams";
46
46
  const OPTION_PREVENT_CLOSE = "preventClose";
47
47
  const OPTION_ENCRYPTION_STRENGTH = "encryptionStrength";
48
48
  const OPTION_EXTENDED_TIMESTAMP = "extendedTimestamp";
49
+ const OPTION_NTFS_TIMESTAMP = "ntfsTimestamp";
49
50
  const OPTION_KEEP_ORDER = "keepOrder";
50
51
  const OPTION_LEVEL = "level";
51
52
  const OPTION_BUFFERED_WRITE = "bufferedWrite";
@@ -85,6 +86,7 @@ export {
85
86
  OPTION_PREVENT_CLOSE,
86
87
  OPTION_ENCRYPTION_STRENGTH,
87
88
  OPTION_EXTENDED_TIMESTAMP,
89
+ OPTION_NTFS_TIMESTAMP,
88
90
  OPTION_KEEP_ORDER,
89
91
  OPTION_LEVEL,
90
92
  OPTION_BUFFERED_WRITE,
@@ -44,7 +44,8 @@ import {
44
44
  ERR_INVALID_COMPRESSED_DATA,
45
45
  ERR_ABORT_CHECK_PASSWORD,
46
46
  InflateStream,
47
- DeflateStream
47
+ DeflateStream,
48
+ supportsDeflateRaw
48
49
  } from "./zip-entry-stream.js";
49
50
 
50
51
  const DEFAULT_CHUNK_SIZE = 64 * 1024;
@@ -54,6 +55,7 @@ const MESSAGE_PULL = "pull";
54
55
  const MESSAGE_DATA = "data";
55
56
  const MESSAGE_ACK_DATA = "ack";
56
57
  const MESSAGE_CLOSE = "close";
58
+ const MESSAGE_READY = "ready";
57
59
  const CODEC_DEFLATE = "deflate";
58
60
  const CODEC_INFLATE = "inflate";
59
61
 
@@ -66,13 +68,15 @@ export {
66
68
  MESSAGE_DATA,
67
69
  MESSAGE_ACK_DATA,
68
70
  MESSAGE_CLOSE,
71
+ MESSAGE_READY,
69
72
  ERR_INVALID_PASSWORD,
70
73
  ERR_INVALID_SIGNATURE,
71
74
  ERR_INVALID_UNCOMPRESSED_SIZE,
72
75
  ERR_INVALID_COMPRESSED_DATA,
73
76
  ERR_ABORT_CHECK_PASSWORD,
74
77
  CodecStream,
75
- ChunkStream
78
+ ChunkStream,
79
+ supportsDeflateRaw
76
80
  };
77
81
 
78
82
  class CodecStream extends TransformStream {
@@ -43,10 +43,13 @@ import {
43
43
  ERR_INVALID_SIGNATURE,
44
44
  ERR_ABORT_CHECK_PASSWORD
45
45
  } from "./common-crypto.js";
46
+ import { UNDEFINED_VALUE } from "../constants.js";
46
47
  import { concat, getDataView } from "../util/array.js";
48
+ import { getCodecStreams } from "../codec-registry.js";
47
49
 
48
50
  const ERR_INVALID_UNCOMPRESSED_SIZE = "Invalid uncompressed size";
49
51
  const ERR_INVALID_COMPRESSED_DATA = "Invalid compressed data";
52
+ const ERR_UNSUPPORTED_COMPRESSION = "Compression method not supported";
50
53
  const FORMAT_DEFLATE_RAW = "deflate-raw";
51
54
  const FORMAT_DEFLATE64_RAW = "deflate64-raw";
52
55
  const FORMAT_GZIP = "gzip";
@@ -57,23 +60,26 @@ class DeflateStream extends TransformStream {
57
60
 
58
61
  constructor(options, { chunkSize, CompressionStreamZlib, CompressionStream }) {
59
62
  super({});
60
- const { compressed, encrypted, useCompressionStream, zipCrypto, signed, level, deflate64 } = options;
63
+ const { compressed, encrypted, useCompressionStream, zipCrypto, signed, level, deflate64, format } = options;
61
64
  const stream = this;
62
65
  let crc32Stream, encryptionStream, gzipCrc32Stream;
63
66
  let readable = super.readable;
64
- const useGzipCrc32 = signed && compressed && !deflate64 && (!encrypted || zipCrypto) &&
67
+ const codecStreams = format && getCodecStreams(format);
68
+ const useGzipCrc32 = signed && compressed && !deflate64 && !codecStreams && (!encrypted || zipCrypto) &&
65
69
  Boolean(useCompressionStream && CompressionStream);
66
70
  if ((!encrypted || zipCrypto) && signed && !useGzipCrc32) {
67
71
  crc32Stream = new Crc32Stream();
68
72
  readable = pipeThrough(readable, crc32Stream);
69
73
  }
70
74
  if (compressed) {
71
- if (useGzipCrc32) {
75
+ if (codecStreams) {
76
+ readable = pipeThroughBackpressured(readable, createCodecStream(codecStreams.CompressionStream, format, { level, chunkSize }));
77
+ } else if (useGzipCrc32) {
72
78
  gzipCrc32Stream = new GzipToRawDeflateStream();
73
79
  readable = pipeThroughBackpressured(readable, new CompressionStream(FORMAT_GZIP));
74
80
  readable = pipeThrough(readable, gzipCrc32Stream);
75
81
  } else {
76
- readable = pipeThroughCommpressionStream(readable, useCompressionStream, { level, chunkSize }, CompressionStream, CompressionStreamZlib, CompressionStream);
82
+ readable = pipeThroughCompressionStream(readable, useCompressionStream, { level, chunkSize }, CompressionStream, CompressionStreamZlib);
77
83
  }
78
84
  }
79
85
  if (encrypted) {
@@ -142,7 +148,7 @@ class InflateStream extends TransformStream {
142
148
 
143
149
  constructor(options, { chunkSize, DecompressionStreamZlib, DecompressionStream }) {
144
150
  super({});
145
- const { zipCrypto, encrypted, signed, signature, compressed, useCompressionStream, deflate64 } = options;
151
+ const { zipCrypto, encrypted, signed, signature, compressed, useCompressionStream, deflate64, format } = options;
146
152
  let crc32Stream, decryptionStream;
147
153
  let readable = super.readable;
148
154
  if (encrypted) {
@@ -154,7 +160,12 @@ class InflateStream extends TransformStream {
154
160
  }
155
161
  }
156
162
  if (compressed) {
157
- readable = pipeThroughCommpressionStream(readable, useCompressionStream, { chunkSize, deflate64 }, DecompressionStream, DecompressionStreamZlib, DecompressionStream);
163
+ const codecStreams = format && getCodecStreams(format);
164
+ if (codecStreams) {
165
+ readable = pipeThroughBackpressured(readable, createCodecStream(codecStreams.DecompressionStream, format, { chunkSize }));
166
+ } else {
167
+ readable = pipeThroughCompressionStream(readable, useCompressionStream, { chunkSize, deflate64 }, DecompressionStream, DecompressionStreamZlib);
168
+ }
158
169
  readable = mapInflateStreamError(readable);
159
170
  }
160
171
  if ((!encrypted || zipCrypto) && signed) {
@@ -172,9 +183,29 @@ class InflateStream extends TransformStream {
172
183
  }
173
184
  }
174
185
 
186
+ const deflateRawSupportByStream = new Map();
187
+
188
+ function supportsDeflateRaw(StreamClass) {
189
+ if (!StreamClass) {
190
+ return false;
191
+ }
192
+ let supported = deflateRawSupportByStream.get(StreamClass);
193
+ if (supported === UNDEFINED_VALUE) {
194
+ try {
195
+ new StreamClass(FORMAT_DEFLATE_RAW);
196
+ supported = true;
197
+ } catch {
198
+ supported = false;
199
+ }
200
+ deflateRawSupportByStream.set(StreamClass, supported);
201
+ }
202
+ return supported;
203
+ }
204
+
175
205
  export {
176
206
  DeflateStream,
177
207
  InflateStream,
208
+ supportsDeflateRaw,
178
209
  ERR_INVALID_PASSWORD,
179
210
  ERR_INVALID_SIGNATURE,
180
211
  ERR_INVALID_UNCOMPRESSED_SIZE,
@@ -191,21 +222,24 @@ function setReadable(stream, readable, flush) {
191
222
  });
192
223
  }
193
224
 
194
- function pipeThroughCommpressionStream(readable, useCompressionStream, options, CompressionStreamNative, CompressionStreamZlib, CompressionStream) {
195
- const Stream = useCompressionStream && CompressionStreamNative ? CompressionStreamNative : CompressionStreamZlib || CompressionStream;
225
+ function createCodecStream(CodecStreamClass, format, options) {
226
+ if (!CodecStreamClass) {
227
+ throw new Error(ERR_UNSUPPORTED_COMPRESSION);
228
+ }
229
+ return new CodecStreamClass(format, options);
230
+ }
231
+
232
+ function pipeThroughCompressionStream(readable, useCompressionStream, options, CompressionStreamNative, CompressionStreamZlib) {
233
+ const Stream = useCompressionStream && CompressionStreamNative ?
234
+ CompressionStreamNative :
235
+ CompressionStreamZlib || CompressionStreamNative;
196
236
  const format = options.deflate64 ? FORMAT_DEFLATE64_RAW : FORMAT_DEFLATE_RAW;
197
237
  let codecStream;
198
238
  try {
199
239
  codecStream = new Stream(format, options);
200
240
  } catch (error) {
201
- if (useCompressionStream) {
202
- if (CompressionStreamZlib) {
203
- codecStream = new CompressionStreamZlib(format, options);
204
- } else if (CompressionStream) {
205
- codecStream = new CompressionStream(format, options);
206
- } else {
207
- throw error;
208
- }
241
+ if (useCompressionStream && CompressionStreamZlib && Stream != CompressionStreamZlib) {
242
+ codecStream = new CompressionStreamZlib(format, options);
209
243
  } else {
210
244
  throw error;
211
245
  }