@zip.js/zip.js 2.6.26 → 2.6.28

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,7 @@
26
26
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
27
  */
28
28
 
29
- /* global CompressionStream, DecompressionStream, TransformStream */
29
+ /* global TransformStream */
30
30
 
31
31
  import { Crc32Stream } from "./crc32-stream.js";
32
32
  import {
@@ -35,51 +35,36 @@ import {
35
35
  } from "./aes-crypto-stream.js";
36
36
  import {
37
37
  ZipCryptoEncryptionStream,
38
- ZipCryptoDecryptionStream,
38
+ ZipCryptoDecryptionStream
39
39
  } from "./zip-crypto-stream.js";
40
40
  import {
41
- ERR_INVALID_PASSWORD
41
+ ERR_INVALID_PASSWORD,
42
+ ERR_INVALID_SIGNATURE
42
43
  } from "./common-crypto.js";
43
- import { CodecStream } from "./codec-stream.js";
44
44
 
45
- const ERR_INVALID_SIGNATURE = "Invalid signature";
46
45
  const COMPRESSION_FORMAT = "deflate-raw";
47
- const UNDEFINED_TYPE = "undefined";
48
- const COMPRESSION_STREAM_API_SUPPORTED = typeof CompressionStream == UNDEFINED_TYPE;
49
- const DECOMPRESSION_STREAM_API_SUPPORTED = typeof DecompressionStream == UNDEFINED_TYPE;
50
- let INFLATE_RAW_SUPPORTED = true;
51
- let DEFLATE_RAW_SUPPORTED = true;
52
46
 
53
47
  class DeflateStream extends TransformStream {
54
48
 
55
- constructor(codecConstructor, options, { chunkSize }, ...strategies) {
56
- super({}, ...strategies);
49
+ constructor(options, { chunkSize, CompressionStream, CompressionStreamNative }) {
50
+ super({});
57
51
  const { compressed, encrypted, useCompressionStream, password, passwordVerification, encryptionStrength, zipCrypto, signed, level } = options;
58
52
  const stream = this;
59
53
  let crc32Stream, encryptionStream;
60
54
  let readable = filterEmptyChunks(super.readable);
61
55
  if ((!encrypted || zipCrypto) && signed) {
62
56
  [readable, crc32Stream] = readable.tee();
63
- crc32Stream = crc32Stream.pipeThrough(new Crc32Stream());
57
+ crc32Stream = pipeThrough(crc32Stream, new Crc32Stream());
64
58
  }
65
59
  if (compressed) {
66
- if ((useCompressionStream !== undefined && !useCompressionStream) || (COMPRESSION_STREAM_API_SUPPORTED && !DEFLATE_RAW_SUPPORTED)) {
67
- readable = pipeCodecStream(codecConstructor, readable, { chunkSize, level });
68
- } else {
69
- try {
70
- readable = readable.pipeThrough(new CompressionStream(COMPRESSION_FORMAT));
71
- } catch (_error) {
72
- DEFLATE_RAW_SUPPORTED = false;
73
- readable = pipeCodecStream(codecConstructor, readable, { chunkSize, level });
74
- }
75
- }
60
+ readable = pipeThroughCommpressionStream(readable, useCompressionStream, { level, chunkSize }, CompressionStreamNative, CompressionStream);
76
61
  }
77
62
  if (encrypted) {
78
63
  if (zipCrypto) {
79
- readable = readable.pipeThrough(new ZipCryptoEncryptionStream(password, passwordVerification));
64
+ readable = pipeThrough(readable, new ZipCryptoEncryptionStream(password, passwordVerification));
80
65
  } else {
81
66
  encryptionStream = new AESEncryptionStream(password, encryptionStrength);
82
- readable = readable.pipeThrough(encryptionStream);
67
+ readable = pipeThrough(readable, encryptionStream);
83
68
  }
84
69
  }
85
70
  setReadable(stream, readable, async () => {
@@ -98,46 +83,31 @@ class DeflateStream extends TransformStream {
98
83
 
99
84
  class InflateStream extends TransformStream {
100
85
 
101
- constructor(codecConstructor, options, { chunkSize }, ...strategies) {
102
- super({}, ...strategies);
103
- const { zipCrypto, encrypted, password, passwordVerification, signed, encryptionStrength, compressed, useCompressionStream } = options;
104
- const stream = this;
86
+ constructor(options, { chunkSize, DecompressionStream, DecompressionStreamNative }) {
87
+ super({});
88
+ const { zipCrypto, encrypted, password, passwordVerification, signed, signature, encryptionStrength, compressed, useCompressionStream } = options;
105
89
  let crc32Stream, decryptionStream;
106
90
  let readable = filterEmptyChunks(super.readable);
107
91
  if (encrypted) {
108
92
  if (zipCrypto) {
109
- readable = readable.pipeThrough(new ZipCryptoDecryptionStream(password, passwordVerification));
93
+ readable = pipeThrough(readable, new ZipCryptoDecryptionStream(password, passwordVerification));
110
94
  } else {
111
95
  decryptionStream = new AESDecryptionStream(password, signed, encryptionStrength);
112
- readable = readable.pipeThrough(decryptionStream);
96
+ readable = pipeThrough(readable, decryptionStream);
113
97
  }
114
98
  }
115
99
  if (compressed) {
116
- if ((useCompressionStream !== undefined && !useCompressionStream) || (DECOMPRESSION_STREAM_API_SUPPORTED && !INFLATE_RAW_SUPPORTED)) {
117
- readable = pipeCodecStream(codecConstructor, readable, { chunkSize });
118
- } else {
119
- try {
120
- readable = readable.pipeThrough(new DecompressionStream(COMPRESSION_FORMAT));
121
- } catch (_error) {
122
- INFLATE_RAW_SUPPORTED = false;
123
- readable = pipeCodecStream(codecConstructor, readable, { chunkSize });
124
- }
125
- }
100
+ readable = pipeThroughCommpressionStream(readable, useCompressionStream, { chunkSize }, DecompressionStreamNative, DecompressionStream);
126
101
  }
127
102
  if ((!encrypted || zipCrypto) && signed) {
128
103
  [readable, crc32Stream] = readable.tee();
129
- crc32Stream = crc32Stream.pipeThrough(new Crc32Stream());
104
+ crc32Stream = pipeThrough(crc32Stream, new Crc32Stream());
130
105
  }
131
- setReadable(stream, readable, async () => {
132
- if (encrypted && !zipCrypto) {
133
- if (!decryptionStream.valid) {
134
- throw new Error(ERR_INVALID_SIGNATURE);
135
- }
136
- }
106
+ setReadable(this, readable, async () => {
137
107
  if ((!encrypted || zipCrypto) && signed) {
138
- const signature = await crc32Stream.getReader().read();
139
- const dataViewSignature = new DataView(signature.value.buffer);
140
- if (options.signature != dataViewSignature.getUint32(0, false)) {
108
+ const streamSignature = await crc32Stream.getReader().read();
109
+ const dataViewSignature = new DataView(streamSignature.value.buffer);
110
+ if (signature != dataViewSignature.getUint32(0, false)) {
141
111
  throw new Error(ERR_INVALID_SIGNATURE);
142
112
  }
143
113
  }
@@ -152,12 +122,8 @@ export {
152
122
  ERR_INVALID_SIGNATURE
153
123
  };
154
124
 
155
- function pipeCodecStream(codecConstructor, readable, options) {
156
- return readable.pipeThrough(new CodecStream(codecConstructor, options));
157
- }
158
-
159
125
  function filterEmptyChunks(readable) {
160
- return readable.pipeThrough(new TransformStream({
126
+ return pipeThrough(readable, new TransformStream({
161
127
  transform(chunk, controller) {
162
128
  if (chunk && chunk.length) {
163
129
  controller.enqueue(chunk);
@@ -167,10 +133,28 @@ function filterEmptyChunks(readable) {
167
133
  }
168
134
 
169
135
  function setReadable(stream, readable, flush) {
170
- readable = readable.pipeThrough(new TransformStream({ flush }));
136
+ readable = pipeThrough(readable, new TransformStream({ flush }));
171
137
  Object.defineProperty(stream, "readable", {
172
138
  get() {
173
139
  return readable;
174
140
  }
175
141
  });
142
+ }
143
+
144
+ function pipeThroughCommpressionStream(readable, useCompressionStream, options, CodecStreamNative, CodecStream) {
145
+ try {
146
+ const CompressionStream = useCompressionStream && CodecStreamNative ? CodecStreamNative : CodecStream;
147
+ readable = pipeThrough(readable, new CompressionStream(COMPRESSION_FORMAT, options));
148
+ } catch (error) {
149
+ if (useCompressionStream) {
150
+ readable = pipeThrough(readable, new CodecStream(COMPRESSION_FORMAT, options));
151
+ } else {
152
+ throw error;
153
+ }
154
+ }
155
+ return readable;
156
+ }
157
+
158
+ function pipeThrough(readable, transformStream) {
159
+ return readable.pipeThrough(transformStream);
176
160
  }
@@ -30,8 +30,7 @@
30
30
 
31
31
  import {
32
32
  Codec,
33
- CODEC_DEFLATE,
34
- CODEC_INFLATE,
33
+ getCodecStreamClass,
35
34
  MESSAGE_EVENT_TYPE,
36
35
  MESSAGE_START,
37
36
  MESSAGE_PULL,
@@ -70,18 +69,19 @@ addEventListener(MESSAGE_EVENT_TYPE, async event => {
70
69
  async function init(message) {
71
70
  try {
72
71
  const { options, scripts, config } = message;
73
- const { codecType } = options;
74
72
  if (scripts && scripts.length) {
75
73
  importScripts.apply(undefined, scripts);
76
74
  }
77
75
  if (self.initCodec) {
78
76
  self.initCodec();
79
77
  }
80
- let codecConstructor;
81
- if (codecType.startsWith(CODEC_DEFLATE)) {
82
- codecConstructor = self.Deflate;
83
- } else if (codecType.startsWith(CODEC_INFLATE)) {
84
- codecConstructor = self.Inflate;
78
+ config.CompressionStreamNative = self.CompressionStream;
79
+ config.DecompressionStreamNative = self.DecompressionStream;
80
+ if (self.Deflate) {
81
+ config.CompressionStream = getCodecStreamClass(self.Deflate);
82
+ }
83
+ if (self.Inflate) {
84
+ config.DecompressionStream = getCodecStreamClass(self.Inflate);
85
85
  }
86
86
  const strategy = { highWaterMark: 1, size: () => config.chunkSize };
87
87
  const readable = message.readable || new ReadableStream({
@@ -106,7 +106,7 @@ async function init(message) {
106
106
  await ackData;
107
107
  }
108
108
  }, strategy);
109
- codec = new Codec(codecConstructor, readable, writable, options, config);
109
+ codec = new Codec(readable, writable, options, config);
110
110
  const result = await codec.run();
111
111
  if (message.writable && !options.preventClose) {
112
112
  await message.writable.close();
@@ -355,8 +355,7 @@ class ZipEntry {
355
355
  preventClose
356
356
  },
357
357
  config,
358
- streamOptions: { signal, size, onstart, onprogress, onend },
359
- codecConstructor: config.Inflate
358
+ streamOptions: { signal, size, onstart, onprogress, onend }
360
359
  };
361
360
  writable.size += (await runWorker({ readable, writable }, workerOptions)).size;
362
361
  if (!preventClose) {
@@ -181,12 +181,12 @@ async function addFile(zipWriter, name, reader, options) {
181
181
  options.directory = name.endsWith(DIRECTORY_SIGNATURE);
182
182
  }
183
183
  const rawFilename = encodeText(name);
184
- if (rawFilename.length > MAX_16_BITS) {
184
+ if (getLength(rawFilename) > MAX_16_BITS) {
185
185
  throw new Error(ERR_INVALID_ENTRY_NAME);
186
186
  }
187
187
  const comment = options.comment || "";
188
188
  const rawComment = encodeText(comment);
189
- if (rawComment.length > MAX_16_BITS) {
189
+ if (getLength(rawComment) > MAX_16_BITS) {
190
190
  throw new Error(ERR_INVALID_ENTRY_COMMENT);
191
191
  }
192
192
  const version = zipWriter.options.version || options.version || 0;
@@ -211,19 +211,19 @@ async function addFile(zipWriter, name, reader, options) {
211
211
  if (extraField) {
212
212
  let extraFieldSize = 0;
213
213
  let offset = 0;
214
- extraField.forEach(data => extraFieldSize += 4 + data.length);
214
+ extraField.forEach(data => extraFieldSize += 4 + getLength(data));
215
215
  rawExtraField = new Uint8Array(extraFieldSize);
216
216
  extraField.forEach((data, type) => {
217
217
  if (type > MAX_16_BITS) {
218
218
  throw new Error(ERR_INVALID_EXTRAFIELD_TYPE);
219
219
  }
220
- if (data.length > MAX_16_BITS) {
220
+ if (getLength(data) > MAX_16_BITS) {
221
221
  throw new Error(ERR_INVALID_EXTRAFIELD_DATA);
222
222
  }
223
223
  arraySet(rawExtraField, new Uint16Array([type]), offset);
224
- arraySet(rawExtraField, new Uint16Array([data.length]), offset + 2);
224
+ arraySet(rawExtraField, new Uint16Array([getLength(data)]), offset + 2);
225
225
  arraySet(rawExtraField, data, offset + 4);
226
- offset += 4 + data.length;
226
+ offset += 4 + getLength(data);
227
227
  });
228
228
  }
229
229
  let extendedTimestamp = getOptionValue(zipWriter, options, "extendedTimestamp");
@@ -312,7 +312,7 @@ async function addFile(zipWriter, name, reader, options) {
312
312
  });
313
313
  const headerInfo = getHeaderInfo(options);
314
314
  const dataDescriptorInfo = getDataDescriptorInfo(options);
315
- maximumEntrySize = headerInfo.localHeaderArray.length + maximumCompressedSize + dataDescriptorInfo.dataDescriptorArray.length;
315
+ maximumEntrySize = getLength(headerInfo.localHeaderArray, dataDescriptorInfo.dataDescriptorArray) + maximumCompressedSize;
316
316
  zipWriter.pendingEntriesSize += maximumEntrySize;
317
317
  let fileEntry;
318
318
  try {
@@ -530,8 +530,7 @@ async function createFileEntry(reader, writer, pendingFileEntry, entryInfo, conf
530
530
  transferStreams: false
531
531
  },
532
532
  config,
533
- streamOptions: { signal, size, onstart, onprogress, onend },
534
- codecConstructor: config.Deflate
533
+ streamOptions: { signal, size, onstart, onprogress, onend }
535
534
  };
536
535
  const result = await runWorker({ readable, writable }, workerOptions);
537
536
  writable.size += result.size;
@@ -562,7 +561,7 @@ async function createFileEntry(reader, writer, pendingFileEntry, entryInfo, conf
562
561
  creationDate,
563
562
  lastAccessDate,
564
563
  encrypted,
565
- length: localHeaderArray.length + compressedSize + dataDescriptorArray.length,
564
+ length: getLength(localHeaderArray, dataDescriptorArray) + compressedSize,
566
565
  compressionMethod,
567
566
  version,
568
567
  headerArray,
@@ -589,10 +588,10 @@ function getHeaderInfo(options) {
589
588
  } = options;
590
589
  let version = options.version || VERSION_DEFLATE;
591
590
  const compressed = level !== 0 && !directory;
592
- const encrypted = Boolean(password && password.length);
591
+ const encrypted = Boolean(password && getLength(password));
593
592
  let rawExtraFieldAES;
594
593
  if (encrypted && !zipCrypto) {
595
- rawExtraFieldAES = new Uint8Array(EXTRAFIELD_DATA_AES.length + 2);
594
+ rawExtraFieldAES = new Uint8Array(getLength(EXTRAFIELD_DATA_AES) + 2);
596
595
  const extraFieldAESView = getDataView(rawExtraFieldAES);
597
596
  setUint16(extraFieldAESView, 0, EXTRAFIELD_TYPE_AES);
598
597
  arraySet(rawExtraFieldAES, EXTRAFIELD_DATA_AES, 2);
@@ -606,7 +605,7 @@ function getHeaderInfo(options) {
606
605
  rawExtraFieldExtendedTimestamp = new Uint8Array(9 + (lastAccessDate ? 4 : 0) + (creationDate ? 4 : 0));
607
606
  const extraFieldExtendedTimestampView = getDataView(rawExtraFieldExtendedTimestamp);
608
607
  setUint16(extraFieldExtendedTimestampView, 0, EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
609
- setUint16(extraFieldExtendedTimestampView, 2, rawExtraFieldExtendedTimestamp.length - 4);
608
+ setUint16(extraFieldExtendedTimestampView, 2, getLength(rawExtraFieldExtendedTimestamp) - 4);
610
609
  const extraFieldExtendedTimestampFlag = 0x1 + (lastAccessDate ? 0x2 : 0) + (creationDate ? 0x4 : 0);
611
610
  setUint8(extraFieldExtendedTimestampView, 4, extraFieldExtendedTimestampFlag);
612
611
  setUint32(extraFieldExtendedTimestampView, 5, Math.floor(options.lastModDate.getTime() / 1000));
@@ -673,18 +672,18 @@ function getHeaderInfo(options) {
673
672
  setUint16(dateView, 2, ((((lastModDate.getFullYear() - 1980) << 4) | (lastModDate.getMonth() + 1)) << 5) | lastModDate.getDate());
674
673
  const rawLastModDate = dateArray[0];
675
674
  setUint32(headerView, 6, rawLastModDate);
676
- setUint16(headerView, 22, rawFilename.length);
677
- const extraFieldLength = rawExtraFieldAES.length + rawExtraFieldExtendedTimestamp.length + rawExtraFieldNTFS.length + rawExtraField.length;
675
+ setUint16(headerView, 22, getLength(rawFilename));
676
+ const extraFieldLength = getLength(rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawExtraField);
678
677
  setUint16(headerView, 24, extraFieldLength);
679
- const localHeaderArray = new Uint8Array(30 + rawFilename.length + extraFieldLength);
678
+ const localHeaderArray = new Uint8Array(30 + getLength(rawFilename) + extraFieldLength);
680
679
  const localHeaderView = getDataView(localHeaderArray);
681
680
  setUint32(localHeaderView, 0, LOCAL_FILE_HEADER_SIGNATURE);
682
681
  arraySet(localHeaderArray, headerArray, 4);
683
682
  arraySet(localHeaderArray, rawFilename, 30);
684
- arraySet(localHeaderArray, rawExtraFieldAES, 30 + rawFilename.length);
685
- arraySet(localHeaderArray, rawExtraFieldExtendedTimestamp, 30 + rawFilename.length + rawExtraFieldAES.length);
686
- arraySet(localHeaderArray, rawExtraFieldNTFS, 30 + rawFilename.length + rawExtraFieldAES.length + rawExtraFieldExtendedTimestamp.length);
687
- arraySet(localHeaderArray, rawExtraField, 30 + rawFilename.length + rawExtraFieldAES.length + rawExtraFieldExtendedTimestamp.length + rawExtraFieldNTFS.length);
683
+ arraySet(localHeaderArray, rawExtraFieldAES, 30 + getLength(rawFilename));
684
+ arraySet(localHeaderArray, rawExtraFieldExtendedTimestamp, 30 + getLength(rawFilename, rawExtraFieldAES));
685
+ arraySet(localHeaderArray, rawExtraFieldNTFS, 30 + getLength(rawFilename, rawExtraFieldAES, rawExtraFieldExtendedTimestamp));
686
+ arraySet(localHeaderArray, rawExtraField, 30 + getLength(rawFilename, rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS));
688
687
  return {
689
688
  localHeaderArray,
690
689
  headerArray,
@@ -782,13 +781,14 @@ async function closeFile(zipWriter, comment, options) {
782
781
  let filesLength = files.size;
783
782
  for (const [, fileEntry] of files) {
784
783
  directoryDataLength += 46 +
785
- fileEntry.rawFilename.length +
786
- fileEntry.rawComment.length +
787
- fileEntry.rawExtraFieldZip64.length +
788
- fileEntry.rawExtraFieldAES.length +
789
- fileEntry.rawExtraFieldExtendedTimestamp.length +
790
- fileEntry.rawExtraFieldNTFS.length +
791
- fileEntry.rawExtraField.length;
784
+ getLength(
785
+ fileEntry.rawFilename,
786
+ fileEntry.rawComment,
787
+ fileEntry.rawExtraFieldZip64,
788
+ fileEntry.rawExtraFieldAES,
789
+ fileEntry.rawExtraFieldExtendedTimestamp,
790
+ fileEntry.rawExtraFieldNTFS,
791
+ fileEntry.rawExtraField);
792
792
  }
793
793
  let zip64 = options.zip64 || zipWriter.options.zip64 || false;
794
794
  if (directoryOffset >= MAX_32_BITS || directoryDataLength >= MAX_32_BITS || filesLength >= MAX_16_BITS) {
@@ -822,18 +822,18 @@ async function closeFile(zipWriter, comment, options) {
822
822
  rawExtraFieldExtendedTimestamp = new Uint8Array(9);
823
823
  const extraFieldExtendedTimestampView = getDataView(rawExtraFieldExtendedTimestamp);
824
824
  setUint16(extraFieldExtendedTimestampView, 0, EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
825
- setUint16(extraFieldExtendedTimestampView, 2, rawExtraFieldExtendedTimestamp.length - 4);
825
+ setUint16(extraFieldExtendedTimestampView, 2, getLength(rawExtraFieldExtendedTimestamp) - 4);
826
826
  setUint8(extraFieldExtendedTimestampView, 4, 0x1);
827
827
  setUint32(extraFieldExtendedTimestampView, 5, Math.floor(fileEntry.lastModDate.getTime() / 1000));
828
828
  } else {
829
829
  rawExtraFieldNTFS = rawExtraFieldExtendedTimestamp = new Uint8Array();
830
830
  }
831
- const extraFieldLength = rawExtraFieldZip64.length + rawExtraFieldAES.length + rawExtraFieldExtendedTimestamp.length + rawExtraFieldNTFS.length + rawExtraField.length;
831
+ const extraFieldLength = getLength(rawExtraFieldZip64, rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawExtraField);
832
832
  setUint32(directoryView, offset, CENTRAL_FILE_HEADER_SIGNATURE);
833
833
  setUint16(directoryView, offset + 4, versionMadeBy);
834
834
  arraySet(directoryArray, headerArray, offset + 6);
835
835
  setUint16(directoryView, offset + 30, extraFieldLength);
836
- setUint16(directoryView, offset + 32, rawComment.length);
836
+ setUint16(directoryView, offset + 32, getLength(rawComment));
837
837
  setUint16(directoryView, offset + 36, internalFileAttribute);
838
838
  if (externalFileAttribute) {
839
839
  setUint32(directoryView, offset + 38, externalFileAttribute);
@@ -846,13 +846,13 @@ async function closeFile(zipWriter, comment, options) {
846
846
  setUint32(directoryView, offset + 42, fileEntry.offset);
847
847
  }
848
848
  arraySet(directoryArray, rawFilename, offset + 46);
849
- arraySet(directoryArray, rawExtraFieldZip64, offset + 46 + rawFilename.length);
850
- arraySet(directoryArray, rawExtraFieldAES, offset + 46 + rawFilename.length + rawExtraFieldZip64.length);
851
- arraySet(directoryArray, rawExtraFieldExtendedTimestamp, offset + 46 + rawFilename.length + rawExtraFieldZip64.length + rawExtraFieldAES.length);
852
- arraySet(directoryArray, rawExtraFieldNTFS, offset + 46 + rawFilename.length + rawExtraFieldZip64.length + rawExtraFieldAES.length + rawExtraFieldExtendedTimestamp.length);
853
- arraySet(directoryArray, rawExtraField, offset + 46 + rawFilename.length + rawExtraFieldZip64.length + rawExtraFieldAES.length + rawExtraFieldExtendedTimestamp.length + rawExtraFieldNTFS.length);
854
- arraySet(directoryArray, rawComment, offset + 46 + rawFilename.length + extraFieldLength);
855
- offset += 46 + rawFilename.length + extraFieldLength + rawComment.length;
849
+ arraySet(directoryArray, rawExtraFieldZip64, offset + 46 + getLength(rawFilename));
850
+ arraySet(directoryArray, rawExtraFieldAES, offset + 46 + getLength(rawFilename, rawExtraFieldZip64));
851
+ arraySet(directoryArray, rawExtraFieldExtendedTimestamp, offset + 46 + getLength(rawFilename, rawExtraFieldZip64, rawExtraFieldAES));
852
+ arraySet(directoryArray, rawExtraFieldNTFS, offset + 46 + getLength(rawFilename, rawExtraFieldZip64, rawExtraFieldAES, rawExtraFieldExtendedTimestamp));
853
+ arraySet(directoryArray, rawExtraField, offset + 46 + getLength(rawFilename, rawExtraFieldZip64, rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS));
854
+ arraySet(directoryArray, rawComment, offset + 46 + getLength(rawFilename) + extraFieldLength);
855
+ offset += 46 + getLength(rawFilename, rawComment) + extraFieldLength;
856
856
  if (options.onprogress) {
857
857
  try {
858
858
  await options.onprogress(indexFileEntry + 1, files.size, new Entry(fileEntry));
@@ -883,9 +883,10 @@ async function closeFile(zipWriter, comment, options) {
883
883
  setUint16(directoryView, offset + 10, filesLength);
884
884
  setUint32(directoryView, offset + 12, directoryDataLength);
885
885
  setUint32(directoryView, offset + 16, directoryOffset);
886
- if (comment && comment.length) {
887
- if (comment.length <= MAX_16_BITS) {
888
- setUint16(directoryView, offset + 20, comment.length);
886
+ const commentLength = getLength(comment);
887
+ if (commentLength) {
888
+ if (commentLength <= MAX_16_BITS) {
889
+ setUint16(directoryView, offset + 20, commentLength);
889
890
  } else {
890
891
  throw new Error(ERR_INVALID_COMMENT);
891
892
  }
@@ -893,7 +894,7 @@ async function closeFile(zipWriter, comment, options) {
893
894
  const { writable } = zipWriter.writer;
894
895
  await initStream(zipWriter.writer);
895
896
  await writeData(writable, directoryArray);
896
- if (comment && comment.length) {
897
+ if (commentLength) {
897
898
  await writeData(writable, comment);
898
899
  }
899
900
  }
@@ -909,7 +910,7 @@ function sliceAsArrayBuffer(blob, start, end) {
909
910
  async function writeData(writable, array) {
910
911
  const streamWriter = writable.getWriter();
911
912
  await streamWriter.ready;
912
- writable.size += array.length;
913
+ writable.size += getLength(array);
913
914
  await streamWriter.write(array);
914
915
  streamWriter.releaseLock();
915
916
  }
@@ -950,4 +951,10 @@ function arraySet(array, typedArray, offset) {
950
951
 
951
952
  function getDataView(array) {
952
953
  return new DataView(array.buffer);
954
+ }
955
+
956
+ function getLength(...arrayLikes) {
957
+ let result = 0;
958
+ arrayLikes.forEach(arrayLike => arrayLike && (result += arrayLike.length));
959
+ return result;
953
960
  }