@zip.js/zip.js 2.7.63 → 2.7.65

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.
@@ -57,7 +57,7 @@ class CodecWorker {
57
57
  busy: true,
58
58
  readable: readable
59
59
  .pipeThrough(new ChunkStream(config.chunkSize))
60
- .pipeThrough(new ProgressWatcherStream(readable, streamOptions), { signal }),
60
+ .pipeThrough(new ProgressWatcherStream(streamOptions), { signal }),
61
61
  writable,
62
62
  options: Object.assign({}, options),
63
63
  scripts,
@@ -96,7 +96,7 @@ class CodecWorker {
96
96
 
97
97
  class ProgressWatcherStream extends TransformStream {
98
98
 
99
- constructor(readableSource, { onstart, onprogress, size, onend }) {
99
+ constructor({ onstart, onprogress, size, onend }) {
100
100
  let chunkOffset = 0;
101
101
  super({
102
102
  async start() {
@@ -112,7 +112,6 @@ class ProgressWatcherStream extends TransformStream {
112
112
  controller.enqueue(chunk);
113
113
  },
114
114
  async flush() {
115
- readableSource.size = chunkOffset;
116
115
  if (onend) {
117
116
  await callHandler(onend, chunkOffset);
118
117
  }
@@ -158,8 +157,9 @@ function createWebWorkerInterface(workerData, config) {
158
157
  }
159
158
 
160
159
  async function runWorker({ options, readable, writable, onTaskFinished }, config) {
160
+ let codecStream;
161
161
  try {
162
- const codecStream = new CodecStream(options, config);
162
+ codecStream = new CodecStream(options, config);
163
163
  await readable.pipeThrough(codecStream).pipeTo(writable, { preventClose: true, preventAbort: true });
164
164
  const {
165
165
  signature,
@@ -171,6 +171,11 @@ async function runWorker({ options, readable, writable, onTaskFinished }, config
171
171
  inputSize,
172
172
  outputSize
173
173
  };
174
+ } catch (error) {
175
+ if (codecStream) {
176
+ error.outputSize = codecStream.outputSize;
177
+ }
178
+ throw error;
174
179
  } finally {
175
180
  onTaskFinished();
176
181
  }
@@ -314,9 +319,9 @@ async function onMessage({ data }, workerData) {
314
319
  const { reader, writer, resolveResult, rejectResult, onTaskFinished } = workerData;
315
320
  try {
316
321
  if (error) {
317
- const { message, stack, code, name } = error;
322
+ const { message, stack, code, name, outputSize } = error;
318
323
  const responseError = new Error(message);
319
- Object.assign(responseError, { stack, code, name });
324
+ Object.assign(responseError, { stack, code, name, outputSize });
320
325
  close(responseError);
321
326
  } else {
322
327
  if (type == MESSAGE_PULL) {
@@ -53,13 +53,16 @@ const EXTRAFIELD_TYPE_UNICODE_PATH = 0x7075;
53
53
  const EXTRAFIELD_TYPE_UNICODE_COMMENT = 0x6375;
54
54
  const EXTRAFIELD_TYPE_USDZ = 0x1986;
55
55
 
56
- const BITFLAG_ENCRYPTED = 0x01;
57
- const BITFLAG_LEVEL = 0x06;
58
- const BITFLAG_DATA_DESCRIPTOR = 0x0008;
59
- const BITFLAG_LANG_ENCODING_FLAG = 0x0800;
60
- const FILE_ATTR_MSDOS_DIR_MASK = 0x10;
61
- const FILE_ATTR_UNIX_TYPE_MASK = 0o170000;
62
- const FILE_ATTR_UNIX_TYPE_DIR = 0o040000;
56
+ const BITFLAG_ENCRYPTED = 0b1;
57
+ const BITFLAG_LEVEL = 0b0110;
58
+ const BITFLAG_LEVEL_MAX_MASK = 0b010;
59
+ const BITFLAG_LEVEL_FAST_MASK = 0b100;
60
+ const BITFLAG_LEVEL_SUPER_FAST_MASK = 0b110;
61
+ const BITFLAG_DATA_DESCRIPTOR = 0b1000;
62
+ const BITFLAG_LANG_ENCODING_FLAG = 0b100000000000;
63
+ const FILE_ATTR_MSDOS_DIR_MASK = 0b10000;
64
+ const FILE_ATTR_UNIX_TYPE_MASK = 0o170000;
65
+ const FILE_ATTR_UNIX_TYPE_DIR = 0o040000;
63
66
  const FILE_ATTR_UNIX_EXECUTABLE_MASK = 0o111;
64
67
  const FILE_ATTR_UNIX_DEFAULT_MASK = 0o644;
65
68
 
@@ -69,6 +72,11 @@ const VERSION_AES = 0x33;
69
72
 
70
73
  const DIRECTORY_SIGNATURE = "/";
71
74
 
75
+ const HEADER_SIZE = 26;
76
+ const HEADER_OFFSET_SIGNATURE = 10;
77
+ const HEADER_OFFSET_COMPRESSED_SIZE = 14;
78
+ const HEADER_OFFSET_UNCOMPRESSED_SIZE = 18;
79
+
72
80
  const MAX_DATE = new Date(2107, 11, 31);
73
81
  const MIN_DATE = new Date(1980, 0, 1);
74
82
 
@@ -103,6 +111,9 @@ export {
103
111
  ZIP64_END_OF_CENTRAL_DIR_TOTAL_LENGTH,
104
112
  BITFLAG_ENCRYPTED,
105
113
  BITFLAG_LEVEL,
114
+ BITFLAG_LEVEL_MAX_MASK,
115
+ BITFLAG_LEVEL_FAST_MASK,
116
+ BITFLAG_LEVEL_SUPER_FAST_MASK,
106
117
  BITFLAG_DATA_DESCRIPTOR,
107
118
  BITFLAG_LANG_ENCODING_FLAG,
108
119
  FILE_ATTR_MSDOS_DIR_MASK,
@@ -113,6 +124,10 @@ export {
113
124
  VERSION_DEFLATE,
114
125
  VERSION_ZIP64,
115
126
  VERSION_AES,
127
+ HEADER_SIZE,
128
+ HEADER_OFFSET_SIGNATURE,
129
+ HEADER_OFFSET_COMPRESSED_SIZE,
130
+ HEADER_OFFSET_UNCOMPRESSED_SIZE,
116
131
  DIRECTORY_SIGNATURE,
117
132
  MIN_DATE,
118
133
  MAX_DATE,
package/lib/core/io.js CHANGED
@@ -141,10 +141,16 @@ class Data64URIReader extends Reader {
141
141
  const start = Math.floor(offset / 3) * 4;
142
142
  const bytes = atob(dataURI.substring(start + dataStart, Math.ceil((offset + length) / 3) * 4 + dataStart));
143
143
  const delta = offset - Math.floor(start / 4) * 3;
144
- for (let indexByte = delta; indexByte < delta + length; indexByte++) {
144
+ let effectiveLength = 0;
145
+ for (let indexByte = delta; indexByte < delta + length && indexByte < bytes.length; indexByte++) {
145
146
  dataArray[indexByte - delta] = bytes.charCodeAt(indexByte);
147
+ effectiveLength++;
148
+ }
149
+ if (effectiveLength < dataArray.length) {
150
+ return dataArray.subarray(0, effectiveLength);
151
+ } else {
152
+ return dataArray;
146
153
  }
147
- return dataArray;
148
154
  }
149
155
  }
150
156
 
@@ -513,6 +519,7 @@ class Uint8ArrayReader extends Reader {
513
519
 
514
520
  constructor(array) {
515
521
  super();
522
+ array = new Uint8Array(array.buffer, array.byteOffset, array.byteLength);
516
523
  Object.assign(this, {
517
524
  array,
518
525
  size: array.length
@@ -581,19 +588,28 @@ class SplitDataReader extends Reader {
581
588
  currentDiskNumber = readers.length - 1;
582
589
  }
583
590
  let currentReaderOffset = offset;
584
- while (currentReaderOffset >= readers[currentDiskNumber].size) {
591
+ while (readers[currentDiskNumber] && currentReaderOffset >= readers[currentDiskNumber].size) {
585
592
  currentReaderOffset -= readers[currentDiskNumber].size;
586
593
  currentDiskNumber++;
587
594
  }
588
595
  const currentReader = readers[currentDiskNumber];
589
- const currentReaderSize = currentReader.size;
590
- if (currentReaderOffset + length <= currentReaderSize) {
591
- result = await readUint8Array(currentReader, currentReaderOffset, length);
596
+ if (currentReader) {
597
+ const currentReaderSize = currentReader.size;
598
+ if (currentReaderOffset + length <= currentReaderSize) {
599
+ result = await readUint8Array(currentReader, currentReaderOffset, length);
600
+ } else {
601
+ const chunkLength = currentReaderSize - currentReaderOffset;
602
+ result = new Uint8Array(length);
603
+ const firstPart = await readUint8Array(currentReader, currentReaderOffset, chunkLength);
604
+ result.set(firstPart, 0);
605
+ const secondPart = await reader.readUint8Array(offset + chunkLength, length - chunkLength, diskNumber);
606
+ result.set(secondPart, chunkLength);
607
+ if (firstPart.length + secondPart.length < length) {
608
+ result = result.subarray(0, firstPart.length + secondPart.length);
609
+ }
610
+ }
592
611
  } else {
593
- const chunkLength = currentReaderSize - currentReaderOffset;
594
- result = new Uint8Array(length);
595
- result.set(await readUint8Array(currentReader, currentReaderOffset, chunkLength));
596
- result.set(await reader.readUint8Array(offset + chunkLength, length - chunkLength, diskNumber), chunkLength);
612
+ result = new Uint8Array();
597
613
  }
598
614
  reader.lastDiskNumber = Math.max(currentDiskNumber, reader.lastDiskNumber);
599
615
  return result;
@@ -633,12 +649,12 @@ class SplitDataWriter extends Stream {
633
649
  }
634
650
  await this.write(chunk);
635
651
  } else if (chunk.length >= availableSize) {
636
- await writeChunk(chunk.slice(0, availableSize));
652
+ await writeChunk(chunk.subarray(0, availableSize));
637
653
  await closeDisk();
638
654
  writer.diskOffset += diskSourceWriter.size;
639
655
  writer.diskNumber++;
640
656
  diskWriter = null;
641
- await this.write(chunk.slice(availableSize));
657
+ await this.write(chunk.subarray(availableSize));
642
658
  } else {
643
659
  await writeChunk(chunk);
644
660
  }
@@ -666,12 +682,52 @@ class SplitDataWriter extends Stream {
666
682
  }
667
683
 
668
684
  async function closeDisk() {
669
- diskWritable.size = diskSourceWriter.size;
670
685
  await diskWriter.close();
671
686
  }
672
687
  }
673
688
  }
674
689
 
690
+ class GenericReader {
691
+
692
+ constructor(reader) {
693
+ if (Array.isArray(reader)) {
694
+ reader = new SplitDataReader(reader);
695
+ }
696
+ if (reader instanceof ReadableStream) {
697
+ reader = {
698
+ readable: reader
699
+ };
700
+ }
701
+ return reader;
702
+ }
703
+ }
704
+
705
+ class GenericWriter {
706
+
707
+ constructor(writer) {
708
+ if (writer.writable === UNDEFINED_VALUE && typeof writer.next == FUNCTION_TYPE) {
709
+ writer = new SplitDataWriter(writer);
710
+ }
711
+ if (writer instanceof WritableStream) {
712
+ writer = {
713
+ writable: writer
714
+ };
715
+ }
716
+ if (writer.size === UNDEFINED_VALUE) {
717
+ writer.size = 0;
718
+ }
719
+ if (!(writer instanceof SplitDataWriter)) {
720
+ Object.assign(writer, {
721
+ diskNumber: 0,
722
+ diskOffset: 0,
723
+ availableSize: Infinity,
724
+ maxSize: Infinity
725
+ });
726
+ }
727
+ return writer;
728
+ }
729
+ }
730
+
675
731
  function isHttpFamily(url) {
676
732
  const { baseURL } = getConfiguration();
677
733
  const { protocol } = new URL(url, baseURL);
@@ -686,42 +742,6 @@ async function initStream(stream, initSize) {
686
742
  }
687
743
  }
688
744
 
689
- function initReader(reader) {
690
- if (Array.isArray(reader)) {
691
- reader = new SplitDataReader(reader);
692
- }
693
- if (reader instanceof ReadableStream) {
694
- reader = {
695
- readable: reader
696
- };
697
- }
698
- return reader;
699
- }
700
-
701
- function initWriter(writer) {
702
- if (writer.writable === UNDEFINED_VALUE && typeof writer.next == FUNCTION_TYPE) {
703
- writer = new SplitDataWriter(writer);
704
- }
705
- if (writer instanceof WritableStream) {
706
- writer = {
707
- writable: writer
708
- };
709
- }
710
- const { writable } = writer;
711
- if (writable.size === UNDEFINED_VALUE) {
712
- writable.size = 0;
713
- }
714
- if (!(writer instanceof SplitDataWriter)) {
715
- Object.assign(writer, {
716
- diskNumber: 0,
717
- diskOffset: 0,
718
- availableSize: Infinity,
719
- maxSize: Infinity
720
- });
721
- }
722
- return writer;
723
- }
724
-
725
745
  function readUint8Array(reader, offset, size, diskNumber) {
726
746
  return reader.readUint8Array(offset, size, diskNumber);
727
747
  }
@@ -731,8 +751,8 @@ const SplitZipWriter = SplitDataWriter;
731
751
 
732
752
  export {
733
753
  initStream,
734
- initReader,
735
- initWriter,
754
+ GenericReader,
755
+ GenericWriter,
736
756
  readUint8Array,
737
757
  Reader,
738
758
  Writer,
@@ -81,7 +81,7 @@ class CodecStream extends TransformStream {
81
81
  } else if (codecType.startsWith(CODEC_INFLATE)) {
82
82
  Stream = InflateStream;
83
83
  }
84
- let outputSize = 0;
84
+ codec.outputSize = 0;
85
85
  let inputSize = 0;
86
86
  const stream = new Stream(options, config);
87
87
  const readable = super.readable;
@@ -101,15 +101,14 @@ class CodecStream extends TransformStream {
101
101
  const outputSizeStream = new TransformStream({
102
102
  transform(chunk, controller) {
103
103
  if (chunk && chunk.length) {
104
- outputSize += chunk.length;
105
104
  controller.enqueue(chunk);
105
+ codec.outputSize += chunk.length;
106
106
  }
107
107
  },
108
108
  flush() {
109
109
  const { signature } = stream;
110
110
  Object.assign(codec, {
111
111
  signature,
112
- outputSize,
113
112
  inputSize
114
113
  });
115
114
  }
@@ -71,6 +71,7 @@ addEventListener(MESSAGE_EVENT_TYPE, ({ data }) => {
71
71
  });
72
72
 
73
73
  async function init(message) {
74
+ let codecStream;
74
75
  try {
75
76
  const { options, scripts, config } = message;
76
77
  if (scripts && scripts.length) {
@@ -142,6 +143,7 @@ async function init(message) {
142
143
  }
143
144
  });
144
145
  } catch (error) {
146
+ error.outputSize = codecStream ? codecStream.outputSize : 0;
145
147
  sendErrorMessage(error);
146
148
  }
147
149
  }
@@ -173,6 +175,6 @@ function sendMessage(message) {
173
175
  }
174
176
 
175
177
  function sendErrorMessage(error = new Error("Unknown error")) {
176
- const { message, stack, code, name } = error;
177
- postMessage({ error: { message, stack, code, name } });
178
+ const { message, stack, code, name, outputSize } = error;
179
+ postMessage({ error: { message, stack, code, name, outputSize } });
178
180
  }
@@ -144,8 +144,8 @@ class ZipFileEntry extends ZipEntry {
144
144
  const reader = zipEntry.reader = new zipEntry.Reader(zipEntry.data, options);
145
145
  const uncompressedSize = zipEntry.data ? zipEntry.data.uncompressedSize : reader.size;
146
146
  await Promise.all([initStream(reader), initStream(writer, uncompressedSize)]);
147
- const readable = reader.readable;
148
- readable.size = zipEntry.uncompressedSize = reader.size;
147
+ const { readable } = reader;
148
+ zipEntry.uncompressedSize = reader.size;
149
149
  await readable.pipeTo(writer.writable);
150
150
  return writer.getData ? writer.getData() : writer.writable;
151
151
  }
@@ -195,6 +195,10 @@ class ZipFileEntry extends ZipEntry {
195
195
  return this.getData({ writable }, options);
196
196
  }
197
197
 
198
+ getArrayBuffer(options) {
199
+ return this.data.arrayBuffer(options);
200
+ }
201
+
198
202
  replaceBlob(blob) {
199
203
  Object.assign(this, {
200
204
  data: blob,
@@ -772,9 +776,8 @@ async function exportZip(zipWriter, entry, totalSize, options) {
772
776
  });
773
777
  }
774
778
  }
775
- await zipWriter.add(name, child.reader, Object.assign({
776
- directory: child.directory
777
- }, Object.assign({}, options, zipEntryOptions, childOptions, {
779
+ await zipWriter.add(name, child.reader, Object.assign({}, options, zipEntryOptions, childOptions, {
780
+ directory: child.directory,
778
781
  onprogress: async indexProgress => {
779
782
  if (options.onprogress) {
780
783
  entryOffsets.set(name, indexProgress);
@@ -786,7 +789,7 @@ async function exportZip(zipWriter, entry, totalSize, options) {
786
789
  }
787
790
  }
788
791
  }
789
- })));
792
+ }));
790
793
  await process(zipWriter, child);
791
794
  }
792
795
  }
@@ -61,6 +61,10 @@ import {
61
61
  FILE_ATTR_UNIX_TYPE_DIR,
62
62
  FILE_ATTR_UNIX_EXECUTABLE_MASK,
63
63
  DIRECTORY_SIGNATURE,
64
+ HEADER_SIZE,
65
+ HEADER_OFFSET_SIGNATURE,
66
+ HEADER_OFFSET_COMPRESSED_SIZE,
67
+ HEADER_OFFSET_UNCOMPRESSED_SIZE,
64
68
  UNDEFINED_VALUE
65
69
  } from "./constants.js";
66
70
  import {
@@ -76,10 +80,10 @@ import {
76
80
  } from "./codec-pool.js";
77
81
  import {
78
82
  initStream,
79
- initReader,
80
- initWriter,
81
83
  readUint8Array,
82
- BlobReader
84
+ BlobReader,
85
+ GenericReader,
86
+ GenericWriter
83
87
  } from "./io.js";
84
88
  import { decodeText } from "./util/decode-text.js";
85
89
  import { Crc32 } from "./streams/codecs/crc32.js";
@@ -134,7 +138,7 @@ class ZipReader {
134
138
 
135
139
  constructor(reader, options = {}) {
136
140
  Object.assign(this, {
137
- reader: initReader(reader),
141
+ reader: new GenericReader(reader),
138
142
  options,
139
143
  config: getConfiguration()
140
144
  });
@@ -309,6 +313,13 @@ class ZipReader {
309
313
  fileEntry.zipCrypto = fileEntry.encrypted && !fileEntry.extraFieldAES;
310
314
  const entry = new Entry(fileEntry);
311
315
  entry.getData = (writer, options) => fileEntry.getData(writer, entry, options);
316
+ entry.arrayBuffer = async options => {
317
+ const writer = new TransformStream();
318
+ const [arrayBuffer] = await Promise.all([
319
+ new Response(writer.readable).arrayBuffer(),
320
+ fileEntry.getData(writer, entry, options)]);
321
+ return arrayBuffer;
322
+ };
312
323
  offset = endOffset;
313
324
  const { onprogress } = options;
314
325
  if (onprogress) {
@@ -416,7 +427,8 @@ class ZipEntry {
416
427
  compressedSize
417
428
  } = zipEntry;
418
429
  const localDirectory = fileEntry.localDirectory = {};
419
- const dataArray = await readUint8Array(reader, offset, 30, diskNumberStart);
430
+ const dataArraySize = HEADER_SIZE + 4;
431
+ const dataArray = await readUint8Array(reader, offset, dataArraySize + 4, diskNumberStart);
420
432
  const dataView = getDataView(dataArray);
421
433
  let password = getOptionValue(zipEntry, options, "password");
422
434
  let rawPassword = getOptionValue(zipEntry, options, "rawPassword");
@@ -436,7 +448,7 @@ class ZipEntry {
436
448
  }
437
449
  readCommonHeader(localDirectory, dataView, 4);
438
450
  localDirectory.rawExtraField = localDirectory.extraFieldLength ?
439
- await readUint8Array(reader, offset + 30 + localDirectory.filenameLength, localDirectory.extraFieldLength, diskNumberStart) :
451
+ await readUint8Array(reader, offset + dataArraySize + localDirectory.filenameLength, localDirectory.extraFieldLength, diskNumberStart) :
440
452
  new Uint8Array();
441
453
  readCommonFooter(zipEntry, localDirectory, dataView, 4, true);
442
454
  Object.assign(fileEntry, {
@@ -455,7 +467,7 @@ class ZipEntry {
455
467
  throw new Error(ERR_ENCRYPTED);
456
468
  }
457
469
  }
458
- const dataOffset = offset + 30 + localDirectory.filenameLength + localDirectory.extraFieldLength;
470
+ const dataOffset = offset + dataArraySize + localDirectory.filenameLength + localDirectory.extraFieldLength;
459
471
  const size = compressedSize;
460
472
  const readable = reader.readable;
461
473
  Object.assign(readable, {
@@ -468,7 +480,7 @@ class ZipEntry {
468
480
  if (checkPasswordOnly) {
469
481
  writer = new WritableStream();
470
482
  }
471
- writer = initWriter(writer);
483
+ writer = new GenericWriter(writer);
472
484
  await initStream(writer, passThrough ? compressedSize : uncompressedSize);
473
485
  const { writable } = writer;
474
486
  const { onstart, onprogress, onend } = options;
@@ -492,16 +504,18 @@ class ZipEntry {
492
504
  config,
493
505
  streamOptions: { signal, size, onstart, onprogress, onend }
494
506
  };
495
- let outputSize = 0;
496
507
  try {
497
- ({ outputSize } = (await runWorker({ readable, writable }, workerOptions)));
508
+ const { outputSize } = await runWorker({ readable, writable }, workerOptions);
509
+ writer.size += outputSize;
498
510
  } catch (error) {
511
+ if (error.outputSize !== UNDEFINED_VALUE) {
512
+ writer.size += error.outputSize;
513
+ }
499
514
  if (!checkPasswordOnly || error.message != ERR_ABORT_CHECK_PASSWORD) {
500
515
  throw error;
501
516
  }
502
517
  } finally {
503
518
  const preventClose = getOptionValue(zipEntry, options, "preventClose");
504
- writable.size += outputSize;
505
519
  if (!preventClose && !writable.locked) {
506
520
  await writable.getWriter().close();
507
521
  }
@@ -550,9 +564,9 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
550
564
  }
551
565
  const compressionMethod = getUint16(dataView, offset + 4);
552
566
  Object.assign(directory, {
553
- signature: getUint32(dataView, offset + 10),
554
- uncompressedSize: getUint32(dataView, offset + 18),
555
- compressedSize: getUint32(dataView, offset + 14)
567
+ signature: getUint32(dataView, offset + HEADER_OFFSET_SIGNATURE),
568
+ compressedSize: getUint32(dataView, offset + HEADER_OFFSET_COMPRESSED_SIZE),
569
+ uncompressedSize: getUint32(dataView, offset + HEADER_OFFSET_UNCOMPRESSED_SIZE)
556
570
  });
557
571
  const extraFieldZip64 = extraField.get(EXTRAFIELD_TYPE_ZIP64);
558
572
  if (extraFieldZip64) {