@zip.js/zip.js 2.5.17 → 2.5.20

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.
@@ -26,7 +26,6 @@
26
26
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
27
  */
28
28
 
29
- /* global AbortController */
30
29
  // deno-lint-ignore-file no-this-alias
31
30
 
32
31
  import {
@@ -46,7 +45,7 @@ const MESSAGE_CLOSE = "close";
46
45
  const ERR_ABORT = "AbortError";
47
46
 
48
47
  export {
49
- createCodec,
48
+ Codec,
50
49
  CODEC_DEFLATE,
51
50
  CODEC_INFLATE,
52
51
  ERR_INVALID_SIGNATURE,
@@ -59,29 +58,21 @@ export {
59
58
  ERR_ABORT
60
59
  };
61
60
 
62
- function createCodec(codecConstructor, readable, writable, options, config) {
63
- const controller = new AbortController();
64
- const { signal } = controller;
65
- if (options.codecType.startsWith(CODEC_DEFLATE)) {
66
- return {
67
- run: () => run(DeflateStream),
68
- abort
69
- };
70
- } else if (options.codecType.startsWith(CODEC_INFLATE)) {
71
- return {
72
- run: () => run(InflateStream),
73
- abort
74
- };
75
- }
61
+ class Codec {
76
62
 
77
- async function run(StreamConstructor) {
78
- const stream = new StreamConstructor(codecConstructor, options, config);
79
- await readable.pipeThrough(stream, { signal }).pipeTo(writable, { preventClose: true });
80
- const { length, signature } = stream;
81
- return { length, signature };
82
- }
63
+ constructor(codecConstructor, readable, writable, options, config) {
64
+ const { codecType } = options;
65
+ if (codecType.startsWith(CODEC_DEFLATE)) {
66
+ this.run = () => run(DeflateStream);
67
+ } else if (codecType.startsWith(CODEC_INFLATE)) {
68
+ this.run = () => run(InflateStream);
69
+ }
83
70
 
84
- function abort() {
85
- controller.abort();
71
+ async function run(StreamConstructor) {
72
+ const stream = new StreamConstructor(codecConstructor, options, config);
73
+ await readable.pipeThrough(stream).pipeTo(writable, { preventClose: true });
74
+ const { length, signature } = stream;
75
+ return { length, signature };
76
+ }
86
77
  }
87
78
  }
@@ -29,7 +29,7 @@
29
29
  /* global Worker, URL, TransformStream */
30
30
 
31
31
  import {
32
- createCodec,
32
+ Codec,
33
33
  MESSAGE_EVENT_TYPE,
34
34
  MESSAGE_START,
35
35
  MESSAGE_PULL,
@@ -40,50 +40,54 @@ import {
40
40
  let classicWorkersSupported = true;
41
41
 
42
42
  export {
43
- getWorker
43
+ CodecWorker
44
44
  };
45
45
 
46
- function getWorker(workerData, stream, workerOptions, onTaskFinished) {
47
- const { readable, writable } = stream;
48
- const { options, config, streamOptions, webWorker, scripts, codecConstructor } = workerOptions;
49
- Object.assign(workerData, {
50
- busy: true,
51
- codecConstructor,
52
- readable,
53
- writable,
54
- options: Object.assign({}, options),
55
- scripts,
56
- terminate() {
57
- if (workerData.worker && !workerData.busy) {
58
- workerData.worker.terminate();
59
- workerData.interface = null;
46
+ class CodecWorker {
47
+
48
+ constructor(workerData, stream, workerOptions, onTaskFinished) {
49
+ const { readable, writable } = stream;
50
+ const { options, config, streamOptions, webWorker, scripts, codecConstructor } = workerOptions;
51
+ Object.assign(workerData, {
52
+ busy: true,
53
+ codecConstructor,
54
+ readable,
55
+ writable,
56
+ options: Object.assign({}, options),
57
+ scripts,
58
+ terminate() {
59
+ if (workerData.worker && !workerData.busy) {
60
+ workerData.worker.terminate();
61
+ workerData.interface = null;
62
+ }
63
+ },
64
+ onTaskFinished() {
65
+ workerData.busy = false;
66
+ onTaskFinished(workerData);
60
67
  }
61
- },
62
- onTaskFinished() {
63
- workerData.busy = false;
64
- onTaskFinished(workerData);
65
- }
66
- });
67
- const { signal, size, onstart, onprogress, onend } = streamOptions;
68
- if (signal || onstart || onprogress || onend) {
68
+ });
69
+ const { signal, onstart, onprogress, size, onend } = streamOptions;
69
70
  let chunkOffset = 0;
70
71
  const transformer = {};
71
72
  if (onstart) {
72
73
  transformer.start = () => callHandler(onstart, size());
73
74
  }
74
- if (onprogress) {
75
- transformer.transform = async (chunk, controller) => {
76
- chunkOffset += chunk.length;
75
+ transformer.transform = async (chunk, controller) => {
76
+ chunkOffset += chunk.length;
77
+ if (onprogress) {
77
78
  await callHandler(onprogress, chunkOffset, size());
78
- controller.enqueue(chunk);
79
- };
80
- }
81
- if (onend) {
82
- transformer.flush = () => callHandler(onend);
83
- }
79
+ }
80
+ controller.enqueue(chunk);
81
+ };
82
+ transformer.flush = () => {
83
+ readable.size = () => chunkOffset;
84
+ if (onend) {
85
+ callHandler(onend, chunkOffset);
86
+ }
87
+ };
84
88
  workerData.readable = readable.pipeThrough(new TransformStream(transformer, { highWaterMark: 1, size: () => config.chunkSize }), { signal });
89
+ return webWorker ? createWebWorkerInterface(workerData, config) : createWorkerInterface(workerData, config);
85
90
  }
86
- return webWorker ? createWebWorkerInterface(workerData, config) : createWorkerInterface(workerData, config);
87
91
  }
88
92
 
89
93
  async function callHandler(handler, ...parameters) {
@@ -95,17 +99,15 @@ async function callHandler(handler, ...parameters) {
95
99
  }
96
100
 
97
101
  function createWorkerInterface(workerData, config) {
98
- const interfaceCodec = createCodec(workerData.codecConstructor, workerData.readable, workerData.writable, workerData.options, config);
102
+ const interfaceCodec = new Codec(workerData.codecConstructor, workerData.readable, workerData.writable, workerData.options, config);
99
103
  const { onTaskFinished } = workerData;
100
104
  const codec = {
101
- abort() {
102
- interfaceCodec.abort();
103
- onTaskFinished();
104
- },
105
105
  async run() {
106
- const result = await interfaceCodec.run();
107
- onTaskFinished();
108
- return result;
106
+ try {
107
+ return await interfaceCodec.run();
108
+ } finally {
109
+ onTaskFinished();
110
+ }
109
111
  }
110
112
  };
111
113
  return codec;
@@ -135,10 +137,6 @@ function createWebWorkerInterface(workerData, { baseURL, chunkSize }) {
135
137
  }
136
138
  workerData.worker.addEventListener(MESSAGE_EVENT_TYPE, onMessage, false);
137
139
  workerData.interface = {
138
- abort() {
139
- workerData.writer.releaseLock();
140
- workerData.onTaskFinished();
141
- },
142
140
  run() {
143
141
  const { options } = workerData;
144
142
  const scripts = workerData.scripts.slice(1);
@@ -35,7 +35,7 @@ import {
35
35
  ERR_INVALID_PASSWORD,
36
36
  ERR_ABORT
37
37
  } from "./codec-interface.js";
38
- import { getWorker } from "./codec-pool-worker.js";
38
+ import { CodecWorker } from "./codec-pool-worker.js";
39
39
 
40
40
  let pool = [];
41
41
  const pendingRequests = [];
@@ -60,27 +60,26 @@ async function runWorker(stream, workerOptions) {
60
60
  workerOptions.webWorker = !streamCopy && (useWebWorkers || (useWebWorkers === undefined && config.useWebWorkers));
61
61
  workerOptions.scripts = workerOptions.webWorker && workerScripts ? workerScripts[codecType] : [];
62
62
  options.useCompressionStream = useCompressionStream === undefined ? config.useCompressionStream : useCompressionStream;
63
- let codec;
63
+ let worker;
64
64
  if (pool.length < config.maxWorkers) {
65
65
  const workerData = { indexWorker };
66
66
  indexWorker++;
67
67
  pool.push(workerData);
68
- codec = getWorker(workerData, stream, workerOptions, onTaskFinished);
68
+ worker = new CodecWorker(workerData, stream, workerOptions, onTaskFinished);
69
69
  } else {
70
70
  const workerData = pool.find(workerData => !workerData.busy);
71
71
  if (workerData) {
72
72
  clearTerminateTimeout(workerData);
73
- codec = getWorker(workerData, stream, workerOptions, onTaskFinished);
73
+ worker = new CodecWorker(workerData, stream, workerOptions, onTaskFinished);
74
74
  } else {
75
- codec = await new Promise(resolve => pendingRequests.push({ resolve, stream, workerOptions }));
75
+ worker = await new Promise(resolve => pendingRequests.push({ resolve, stream, workerOptions }));
76
76
  }
77
77
  }
78
- const { signal } = streamOptions;
79
78
  try {
80
- return await codec.run(signal);
79
+ return await worker.run();
81
80
  } catch (error) {
81
+ const { signal } = streamOptions;
82
82
  if (signal && signal.aborted && (signal.reason == error || error.name == ERR_ABORT)) {
83
- codec.abort();
84
83
  throw new Error(ERR_ABORT);
85
84
  } else {
86
85
  throw error;
@@ -90,7 +89,7 @@ async function runWorker(stream, workerOptions) {
90
89
  function onTaskFinished(workerData) {
91
90
  if (pendingRequests.length) {
92
91
  const [{ resolve, stream, workerOptions }] = pendingRequests.splice(0, 1);
93
- resolve(getWorker(workerData, stream, workerOptions, onTaskFinished));
92
+ resolve(new CodecWorker(workerData, stream, workerOptions, onTaskFinished));
94
93
  } else if (workerData.worker) {
95
94
  const { terminateWorkerTimeout } = config;
96
95
  clearTerminateTimeout(workerData);
package/lib/core/io.js CHANGED
@@ -26,7 +26,7 @@
26
26
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
27
  */
28
28
 
29
- /* global Blob, atob, btoa, XMLHttpRequest, document, fetch, ReadableStream, WritableStream, TransformStream */
29
+ /* global Blob, atob, btoa, XMLHttpRequest, URL, fetch, ReadableStream, WritableStream */
30
30
 
31
31
  const ERR_HTTP_STATUS = "HTTP error ";
32
32
  const ERR_HTTP_RANGE = "HTTP Range not supported";
@@ -42,6 +42,8 @@ const HTTP_METHOD_GET = "GET";
42
42
  const HTTP_RANGE_UNIT = "bytes";
43
43
  const DEFAULT_CHUNK_SIZE = 64 * 1024;
44
44
 
45
+ import { getConfiguration } from "./configuration.js";
46
+
45
47
  class Stream {
46
48
 
47
49
  constructor() {
@@ -55,13 +57,14 @@ class Stream {
55
57
 
56
58
  class Reader extends Stream {
57
59
 
58
- getReadable({ offset, size, chunkSize } = { offset: 0, chunkSize: DEFAULT_CHUNK_SIZE }) {
60
+ get readable() {
59
61
  const reader = this;
60
- return new ReadableStream({
62
+ const readable = new ReadableStream({
61
63
  start() {
62
64
  this.chunkOffset = 0;
63
65
  },
64
66
  async pull(controller) {
67
+ const { offset = 0, chunkSize = DEFAULT_CHUNK_SIZE, size } = readable;
65
68
  const { chunkOffset } = this;
66
69
  controller.enqueue(await reader.readUint8Array(offset + chunkOffset, Math.min(chunkSize, size() - chunkOffset)));
67
70
  if (chunkOffset + chunkSize > size()) {
@@ -71,18 +74,25 @@ class Reader extends Stream {
71
74
  }
72
75
  }
73
76
  });
77
+ return readable;
74
78
  }
75
79
  }
76
80
 
77
81
  class WriterStream extends Stream {
78
82
 
79
- get writable() {
83
+ constructor() {
84
+ super();
80
85
  const writer = this;
81
- return new WritableStream({
86
+ const writable = new WritableStream({
82
87
  write(chunk) {
83
88
  return writer.writeUint8Array(chunk);
84
89
  }
85
90
  });
91
+ Object.defineProperty(this, "writable", {
92
+ get() {
93
+ return writable;
94
+ }
95
+ });
86
96
  }
87
97
  }
88
98
 
@@ -247,30 +257,13 @@ function flushArrayBuffers(blobWriter) {
247
257
  blobWriter.arrayBuffers = [];
248
258
  }
249
259
 
250
- class ReadableStreamReader {
260
+ class ReadableStreamReader extends Stream {
251
261
 
252
262
  constructor(readable) {
253
- const reader = this;
263
+ super();
254
264
  if (!readable) {
255
- readable = new ReadableStream({});
265
+ this.readable = new ReadableStream({});
256
266
  }
257
- reader.readable = readable.pipeThrough(new TransformStream({
258
- transform(chunk, controller) {
259
- if (chunk) {
260
- reader.size += chunk.length;
261
- controller.enqueue(chunk);
262
- }
263
- }
264
- }));
265
- reader.size = 0;
266
- }
267
-
268
- init() {
269
- this.initialized = true;
270
- }
271
-
272
- getReadable() {
273
- return this.readable;
274
267
  }
275
268
  }
276
269
 
@@ -288,15 +281,6 @@ class WritableStreamWriter extends Writer {
288
281
  }
289
282
  });
290
283
  }
291
-
292
- async getData() {
293
- const writer = this;
294
- const { writable } = writer;
295
- if (!writer.preventClose) {
296
- await writable.getWriter().close();
297
- }
298
- return writable;
299
- }
300
284
  }
301
285
 
302
286
  class FetchReader extends Reader {
@@ -547,13 +531,9 @@ class Uint8ArrayWriter extends Writer {
547
531
  }
548
532
 
549
533
  function isHttpFamily(url) {
550
- if (typeof document != "undefined") {
551
- const anchor = document.createElement("a");
552
- anchor.href = url;
553
- return anchor.protocol == "http:" || anchor.protocol == "https:";
554
- } else {
555
- return /^https?:\/\//i.test(url);
556
- }
534
+ const { baseURL } = getConfiguration();
535
+ const { protocol } = new URL(url, baseURL);
536
+ return protocol == "http:" || protocol == "https:";
557
537
  }
558
538
 
559
539
  export {
@@ -52,11 +52,10 @@ class DeflateStream extends TransformStream {
52
52
 
53
53
  constructor(codecConstructor, options, { chunkSize }, ...strategies) {
54
54
  super({}, ...strategies);
55
- const { compressed, useCompressionStream, password, passwordVerification, encryptionStrength, zipCrypto, signed } = options;
55
+ const { compressed, encrypted, useCompressionStream, password, passwordVerification, encryptionStrength, zipCrypto, signed } = options;
56
56
  const stream = this;
57
57
  let crc32Stream, encryptionStream;
58
58
  let readable = filterEmptyChunks(super.readable);
59
- const encrypted = Boolean(password);
60
59
  if ((!encrypted || zipCrypto) && signed) {
61
60
  [readable, crc32Stream] = readable.tee();
62
61
  crc32Stream = crc32Stream.pipeThrough(new Crc32Stream());
@@ -99,11 +98,10 @@ class InflateStream extends TransformStream {
99
98
 
100
99
  constructor(codecConstructor, options, { chunkSize }, ...strategies) {
101
100
  super({}, ...strategies);
102
- const { zipCrypto, password, passwordVerification, signed, encryptionStrength, compressed, useCompressionStream } = options;
101
+ const { zipCrypto, encrypted, password, passwordVerification, signed, encryptionStrength, compressed, useCompressionStream } = options;
103
102
  const stream = this;
104
103
  let crc32Stream, decryptionStream;
105
104
  let readable = filterEmptyChunks(super.readable);
106
- const encrypted = Boolean(password);
107
105
  if (encrypted) {
108
106
  if (zipCrypto) {
109
107
  readable = readable.pipeThrough(new ZipCryptoDecryptionStream(password, passwordVerification));
@@ -29,7 +29,7 @@
29
29
  /* global self, importScripts, addEventListener, postMessage, ReadableStream, WritableStream */
30
30
 
31
31
  import {
32
- createCodec,
32
+ Codec,
33
33
  CODEC_DEFLATE,
34
34
  CODEC_INFLATE,
35
35
  MESSAGE_EVENT_TYPE,
@@ -94,7 +94,7 @@ async function init(message) {
94
94
  sendMessage({ type: MESSAGE_DATA, data });
95
95
  }
96
96
  }, strategy);
97
- codec = createCodec(codecConstructor, readable, writable, options, config);
97
+ codec = new Codec(codecConstructor, readable, writable, options, config);
98
98
  const result = await codec.run();
99
99
  sendMessage({ type: MESSAGE_CLOSE, result });
100
100
 
@@ -117,7 +117,7 @@ class ZipFileEntry extends ZipEntry {
117
117
  } else {
118
118
  zipEntry.reader = new zipEntry.Reader(zipEntry.data, options);
119
119
  await zipEntry.reader.init();
120
- if (!writer.initialized) {
120
+ if (writer.init && !writer.initialized) {
121
121
  await writer.init();
122
122
  }
123
123
  zipEntry.uncompressedSize = zipEntry.reader.size;
@@ -266,9 +266,6 @@ class ZipEntry {
266
266
  compressedSize
267
267
  } = zipEntry;
268
268
  const localDirectory = zipEntry.localDirectory = {};
269
- if (!reader.initialized) {
270
- await reader.init();
271
- }
272
269
  let dataArray = await readUint8Array(reader, offset, 30);
273
270
  const dataView = getDataView(dataArray);
274
271
  let password = getOptionValue(zipEntry, options, "password");
@@ -301,10 +298,13 @@ class ZipEntry {
301
298
  }
302
299
  const dataOffset = offset + 30 + localDirectory.filenameLength + localDirectory.extraFieldLength;
303
300
  const size = () => compressedSize;
304
- const readable = reader.getReadable({ offset: dataOffset, size, chunkSize: getChunkSize(config) });
301
+ const readable = reader.readable;
302
+ readable.offset = dataOffset;
303
+ readable.size = size;
304
+ readable.chunkSize = getChunkSize(config);
305
305
  const { writable } = writer;
306
306
  const signal = getOptionValue(zipEntry, options, "signal");
307
- if (!writer.initialized) {
307
+ if (writer.init && !writer.initialized) {
308
308
  await writer.init();
309
309
  }
310
310
  const { onstart, onprogress, onend } = options;
@@ -327,7 +327,10 @@ class ZipEntry {
327
327
  codecConstructor: config.Inflate
328
328
  };
329
329
  await runWorker({ readable, writable }, workerOptions);
330
- return writer.getData();
330
+ if (!writer.preventClose) {
331
+ await writable.getWriter().close();
332
+ }
333
+ return writer.getData ? writer.getData() : writable;
331
334
  }
332
335
  }
333
336
 
@@ -94,7 +94,7 @@ class ZipWriter {
94
94
  options,
95
95
  config: getConfiguration(),
96
96
  files: new Map(),
97
- offset: writer.size,
97
+ offset: writer.size || 0,
98
98
  pendingCompressedSize: 0,
99
99
  pendingEntries: [],
100
100
  pendingAddFileCalls: new Set()
@@ -126,11 +126,16 @@ class ZipWriter {
126
126
  }
127
127
 
128
128
  async close(comment = new Uint8Array(), options = {}) {
129
- while (this.pendingAddFileCalls.size) {
130
- await Promise.all(Array.from(this.pendingAddFileCalls));
129
+ const { pendingAddFileCalls, writer } = this;
130
+ const { writable } = writer;
131
+ while (pendingAddFileCalls.size) {
132
+ await Promise.all(Array.from(pendingAddFileCalls));
131
133
  }
132
134
  await closeFile(this, comment, options);
133
- return this.writer.getData();
135
+ if (!writer.preventClose) {
136
+ await writable.getWriter().close();
137
+ }
138
+ return writer.getData ? writer.getData() : writable;
134
139
  }
135
140
  }
136
141
 
@@ -220,10 +225,14 @@ async function addFile(zipWriter, name, reader, options) {
220
225
  const internalFileAttribute = getOptionValue(zipWriter, options, "internalFileAttribute") || 0;
221
226
  const externalFileAttribute = getOptionValue(zipWriter, options, "externalFileAttribute") || 0;
222
227
  if (reader) {
223
- if (!reader.initialized) {
228
+ if (reader.init && !reader.initialized) {
224
229
  await reader.init();
225
230
  }
226
- uncompressedSize = reader.size;
231
+ if (reader.size === undefined) {
232
+ options.dataDescriptor = true;
233
+ } else {
234
+ uncompressedSize = reader.size;
235
+ }
227
236
  maximumCompressedSize = getMaximumCompressedSize(uncompressedSize);
228
237
  }
229
238
  let zip64 = options.zip64 || zipWriter.options.zip64 || false;
@@ -304,8 +313,8 @@ async function getFileEntry(zipWriter, name, reader, options) {
304
313
  fileWriter.init();
305
314
  bufferedWrite = true;
306
315
  } else {
307
- zipWriter.lockWrite = new Promise(resolve => resolveLockUnbufferedWrite = resolve);
308
- if (!writer.initialized) {
316
+ zipWriter.lockWrite = Promise.resolve();
317
+ if (writer.init && !writer.initialized) {
309
318
  await writer.init();
310
319
  }
311
320
  fileWriter = writer;
@@ -316,14 +325,8 @@ async function getFileEntry(zipWriter, name, reader, options) {
316
325
  if (bufferedWrite) {
317
326
  let indexWrittenData = 0;
318
327
  const blob = fileWriter.getData();
319
- await Promise.all([zipWriter.lockWrite, lockPreviousFileEntry]);
320
- let pendingFileEntry;
321
- do {
322
- pendingFileEntry = Array.from(files.values()).find(fileEntry => fileEntry.writingBufferedData);
323
- if (pendingFileEntry) {
324
- await pendingFileEntry.lock;
325
- }
326
- } while (pendingFileEntry && pendingFileEntry.lock);
328
+ await lockPreviousFileEntry;
329
+ await lockWriterAccess();
327
330
  fileEntry.writingBufferedData = true;
328
331
  const { writable } = writer;
329
332
  if (!options.dataDescriptor) {
@@ -370,6 +373,15 @@ async function getFileEntry(zipWriter, name, reader, options) {
370
373
  resolveLockUnbufferedWrite();
371
374
  }
372
375
  }
376
+
377
+ async function lockWriterAccess() {
378
+ if (zipWriter.lockWrite) {
379
+ await zipWriter.lockWrite.then(() => delete zipWriter.lockWrite);
380
+ await lockWriterAccess();
381
+ } else {
382
+ zipWriter.lockWrite = new Promise(resolve => resolveLockUnbufferedWrite = resolve);
383
+ }
384
+ }
373
385
  }
374
386
 
375
387
  async function createFileEntry(reader, writable, config, options) {
@@ -524,7 +536,9 @@ async function createFileEntry(reader, writable, config, options) {
524
536
  await writeUint8Array(writable, localHeaderArray);
525
537
  fileEntry.dataWritten = true;
526
538
  const size = () => reader.size;
527
- const readable = reader.getReadable({ offset: 0, size, chunkSize: getChunkSize(config) });
539
+ const readable = reader.readable;
540
+ readable.size = size;
541
+ readable.chunkSize = getChunkSize(config);
528
542
  const workerOptions = {
529
543
  options: {
530
544
  codecType: CODEC_DEFLATE,
@@ -544,7 +558,7 @@ async function createFileEntry(reader, writable, config, options) {
544
558
  codecConstructor: config.Deflate
545
559
  };
546
560
  result = await runWorker({ readable, writable }, workerOptions);
547
- uncompressedSize = fileEntry.uncompressedSize = reader.size;
561
+ uncompressedSize = fileEntry.uncompressedSize = reader.size = readable.size();
548
562
  compressedSize = result.length;
549
563
  } else {
550
564
  await writeUint8Array(writable, localHeaderArray);