@zip.js/zip.js 2.7.69 → 2.7.71

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.
@@ -36,6 +36,10 @@ import {
36
36
  COMPRESSION_METHOD_STORE,
37
37
  COMPRESSION_METHOD_AES,
38
38
  SPLIT_ZIP_FILE_SIGNATURE,
39
+ DATA_DESCRIPTOR_RECORD_SIGNATURE,
40
+ DATA_DESCRIPTOR_RECORD_LENGTH,
41
+ DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH,
42
+ DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH,
39
43
  LOCAL_FILE_HEADER_SIGNATURE,
40
44
  CENTRAL_FILE_HEADER_SIGNATURE,
41
45
  END_OF_CENTRAL_DIR_SIGNATURE,
@@ -76,6 +80,7 @@ import {
76
80
  CODEC_INFLATE,
77
81
  ERR_INVALID_SIGNATURE,
78
82
  ERR_INVALID_PASSWORD,
83
+ ERR_INVALID_UNCOMPRESSED_SIZE,
79
84
  ERR_ABORT_CHECK_PASSWORD
80
85
  } from "./codec-pool.js";
81
86
  import {
@@ -92,8 +97,8 @@ import {
92
97
  PROPERTY_NAME_FILENAME,
93
98
  PROPERTY_NAME_RAW_COMMENT,
94
99
  PROPERTY_NAME_COMMENT,
95
- PROPERTY_NAME_UNCOMPPRESSED_SIZE,
96
- PROPERTY_NAME_COMPPRESSED_SIZE,
100
+ PROPERTY_NAME_UNCOMPRESSED_SIZE,
101
+ PROPERTY_NAME_COMPRESSED_SIZE,
97
102
  PROPERTY_NAME_OFFSET,
98
103
  PROPERTY_NAME_DISK_NUMBER_START,
99
104
  PROPERTY_NAME_LAST_MODIFICATION_DATE,
@@ -104,6 +109,25 @@ import {
104
109
  PROPERTY_NAME_RAW_CREATION_DATE,
105
110
  Entry
106
111
  } from "./zip-entry.js";
112
+ import {
113
+ OPTION_FILENAME_ENCODING,
114
+ OPTION_COMMENT_ENCODING,
115
+ OPTION_DECODE_TEXT,
116
+ OPTION_EXTRACT_PREPENDED_DATA,
117
+ OPTION_EXTRACT_APPENDED_DATA,
118
+ OPTION_PASSWORD,
119
+ OPTION_RAW_PASSWORD,
120
+ OPTION_PASS_THROUGH,
121
+ OPTION_SIGNAL,
122
+ OPTION_CHECK_PASSWORD_ONLY,
123
+ OPTION_CHECK_OVERLAPPING_ENTRY_ONLY,
124
+ OPTION_CHECK_OVERLAPPING_ENTRY,
125
+ OPTION_CHECK_SIGNATURE,
126
+ OPTION_USE_WEB_WORKERS,
127
+ OPTION_USE_COMPRESSION_STREAM,
128
+ OPTION_TRANSFER_STREAMS,
129
+ OPTION_PREVENT_CLOSE
130
+ } from "./options.js";
107
131
 
108
132
  const ERR_BAD_FORMAT = "File format is not recognized";
109
133
  const ERR_EOCDR_NOT_FOUND = "End of central directory not found";
@@ -115,11 +139,12 @@ const ERR_ENCRYPTED = "File contains encrypted entry";
115
139
  const ERR_UNSUPPORTED_ENCRYPTION = "Encryption method not supported";
116
140
  const ERR_UNSUPPORTED_COMPRESSION = "Compression method not supported";
117
141
  const ERR_SPLIT_ZIP_FILE = "Split zip file";
142
+ const ERR_OVERLAPPING_ENTRY = "Overlapping entry found";
118
143
  const CHARSET_UTF8 = "utf-8";
119
144
  const CHARSET_CP437 = "cp437";
120
145
  const ZIP64_PROPERTIES = [
121
- [PROPERTY_NAME_UNCOMPPRESSED_SIZE, MAX_32_BITS],
122
- [PROPERTY_NAME_COMPPRESSED_SIZE, MAX_32_BITS],
146
+ [PROPERTY_NAME_UNCOMPRESSED_SIZE, MAX_32_BITS],
147
+ [PROPERTY_NAME_COMPRESSED_SIZE, MAX_32_BITS],
123
148
  [PROPERTY_NAME_OFFSET, MAX_32_BITS],
124
149
  [PROPERTY_NAME_DISK_NUMBER_START, MAX_16_BITS]
125
150
  ];
@@ -140,7 +165,8 @@ class ZipReader {
140
165
  Object.assign(this, {
141
166
  reader: new GenericReader(reader),
142
167
  options,
143
- config: getConfiguration()
168
+ config: getConfiguration(),
169
+ readRanges: []
144
170
  });
145
171
  }
146
172
 
@@ -190,7 +216,9 @@ class ZipReader {
190
216
  if (getUint32(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE && directoryDataOffset != expectedDirectoryDataOffset) {
191
217
  const originalDirectoryDataOffset = directoryDataOffset;
192
218
  directoryDataOffset = expectedDirectoryDataOffset;
193
- prependedDataLength = directoryDataOffset - originalDirectoryDataOffset;
219
+ if (directoryDataOffset > originalDirectoryDataOffset) {
220
+ prependedDataLength = directoryDataOffset - originalDirectoryDataOffset;
221
+ }
194
222
  endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH, -1);
195
223
  endOfDirectoryView = getDataView(endOfDirectoryArray);
196
224
  }
@@ -230,7 +258,9 @@ class ZipReader {
230
258
  if (getUint32(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE && directoryDataOffset != expectedDirectoryDataOffset) {
231
259
  const originalDirectoryDataOffset = directoryDataOffset;
232
260
  directoryDataOffset = expectedDirectoryDataOffset;
233
- prependedDataLength += directoryDataOffset - originalDirectoryDataOffset;
261
+ if (directoryDataOffset > originalDirectoryDataOffset) {
262
+ prependedDataLength += directoryDataOffset - originalDirectoryDataOffset;
263
+ }
234
264
  directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength, diskNumber);
235
265
  directoryView = getDataView(directoryArray);
236
266
  }
@@ -244,8 +274,8 @@ class ZipReader {
244
274
  if (directoryDataOffset < 0 || directoryDataOffset >= reader.size) {
245
275
  throw new Error(ERR_BAD_FORMAT);
246
276
  }
247
- const filenameEncoding = getOptionValue(zipReader, options, "filenameEncoding");
248
- const commentEncoding = getOptionValue(zipReader, options, "commentEncoding");
277
+ const filenameEncoding = getOptionValue(zipReader, options, OPTION_FILENAME_ENCODING);
278
+ const commentEncoding = getOptionValue(zipReader, options, OPTION_COMMENT_ENCODING);
249
279
  for (let indexFile = 0; indexFile < filesLength; indexFile++) {
250
280
  const fileEntry = new ZipEntry(reader, config, zipReader.options);
251
281
  if (getUint32(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE) {
@@ -291,7 +321,7 @@ class ZipReader {
291
321
  });
292
322
  fileEntry.internalFileAttribute = fileEntry.internalFileAttributes;
293
323
  fileEntry.externalFileAttribute = fileEntry.externalFileAttributes;
294
- const decode = getOptionValue(zipReader, options, "decodeText") || decodeText;
324
+ const decode = getOptionValue(zipReader, options, OPTION_DECODE_TEXT) || decodeText;
295
325
  const rawFilenameEncoding = filenameUTF8 ? CHARSET_UTF8 : filenameEncoding || CHARSET_CP437;
296
326
  const rawCommentEncoding = commentUTF8 ? CHARSET_UTF8 : commentEncoding || CHARSET_CP437;
297
327
  let filename = decode(rawFilename, rawFilenameEncoding);
@@ -312,12 +342,12 @@ class ZipReader {
312
342
  readCommonFooter(fileEntry, fileEntry, directoryView, offset + 6);
313
343
  fileEntry.zipCrypto = fileEntry.encrypted && !fileEntry.extraFieldAES;
314
344
  const entry = new Entry(fileEntry);
315
- entry.getData = (writer, options) => fileEntry.getData(writer, entry, options);
345
+ entry.getData = (writer, options) => fileEntry.getData(writer, entry, zipReader.readRanges, options);
316
346
  entry.arrayBuffer = async options => {
317
347
  const writer = new TransformStream();
318
348
  const [arrayBuffer] = await Promise.all([
319
349
  new Response(writer.readable).arrayBuffer(),
320
- fileEntry.getData(writer, entry, options)]);
350
+ fileEntry.getData(writer, entry, zipReader.readRanges, options)]);
321
351
  return arrayBuffer;
322
352
  };
323
353
  offset = endOffset;
@@ -332,8 +362,8 @@ class ZipReader {
332
362
  }
333
363
  yield entry;
334
364
  }
335
- const extractPrependedData = getOptionValue(zipReader, options, "extractPrependedData");
336
- const extractAppendedData = getOptionValue(zipReader, options, "extractAppendedData");
365
+ const extractPrependedData = getOptionValue(zipReader, options, OPTION_EXTRACT_PREPENDED_DATA);
366
+ const extractAppendedData = getOptionValue(zipReader, options, OPTION_EXTRACT_APPENDED_DATA);
337
367
  if (extractPrependedData) {
338
368
  zipReader.prependedData = startOffset > 0 ? await readUint8Array(reader, 0, startOffset) : new Uint8Array();
339
369
  }
@@ -398,7 +428,9 @@ export {
398
428
  ERR_UNSUPPORTED_COMPRESSION,
399
429
  ERR_INVALID_SIGNATURE,
400
430
  ERR_INVALID_PASSWORD,
401
- ERR_SPLIT_ZIP_FILE
431
+ ERR_INVALID_UNCOMPRESSED_SIZE,
432
+ ERR_SPLIT_ZIP_FILE,
433
+ ERR_OVERLAPPING_ENTRY
402
434
  };
403
435
 
404
436
  class ZipEntry {
@@ -411,13 +443,14 @@ class ZipEntry {
411
443
  });
412
444
  }
413
445
 
414
- async getData(writer, fileEntry, options = {}) {
446
+ async getData(writer, fileEntry, readRanges, options = {}) {
415
447
  const zipEntry = this;
416
448
  const {
417
449
  reader,
418
450
  offset,
419
451
  diskNumberStart,
420
452
  extraFieldAES,
453
+ extraFieldZip64,
421
454
  compressionMethod,
422
455
  config,
423
456
  bitFlag,
@@ -426,13 +459,15 @@ class ZipEntry {
426
459
  uncompressedSize,
427
460
  compressedSize
428
461
  } = zipEntry;
462
+ const {
463
+ dataDescriptor
464
+ } = bitFlag;
429
465
  const localDirectory = fileEntry.localDirectory = {};
430
- const dataArraySize = HEADER_SIZE + 4;
431
- const dataArray = await readUint8Array(reader, offset, dataArraySize + 4, diskNumberStart);
466
+ const dataArray = await readUint8Array(reader, offset, HEADER_SIZE, diskNumberStart);
432
467
  const dataView = getDataView(dataArray);
433
- let password = getOptionValue(zipEntry, options, "password");
434
- let rawPassword = getOptionValue(zipEntry, options, "rawPassword");
435
- const passThrough = getOptionValue(zipEntry, options, "passThrough");
468
+ let password = getOptionValue(zipEntry, options, OPTION_PASSWORD);
469
+ let rawPassword = getOptionValue(zipEntry, options, OPTION_RAW_PASSWORD);
470
+ const passThrough = getOptionValue(zipEntry, options, OPTION_PASS_THROUGH);
436
471
  password = password && password.length && password;
437
472
  rawPassword = rawPassword && rawPassword.length && rawPassword;
438
473
  if (extraFieldAES) {
@@ -447,14 +482,17 @@ class ZipEntry {
447
482
  throw new Error(ERR_LOCAL_FILE_HEADER_NOT_FOUND);
448
483
  }
449
484
  readCommonHeader(localDirectory, dataView, 4);
450
- localDirectory.rawExtraField = localDirectory.extraFieldLength ?
451
- await readUint8Array(reader, offset + dataArraySize + localDirectory.filenameLength, localDirectory.extraFieldLength, diskNumberStart) :
485
+ const {
486
+ extraFieldLength,
487
+ filenameLength,
488
+ lastAccessDate,
489
+ creationDate
490
+ } = localDirectory;
491
+ localDirectory.rawExtraField = extraFieldLength ?
492
+ await readUint8Array(reader, offset + HEADER_SIZE + filenameLength, extraFieldLength, diskNumberStart) :
452
493
  new Uint8Array();
453
494
  readCommonFooter(zipEntry, localDirectory, dataView, 4, true);
454
- Object.assign(fileEntry, {
455
- lastAccessDate: localDirectory.lastAccessDate,
456
- creationDate: localDirectory.creationDate
457
- });
495
+ Object.assign(fileEntry, { lastAccessDate, creationDate });
458
496
  const encrypted = zipEntry.encrypted && localDirectory.encrypted && !passThrough;
459
497
  const zipCrypto = encrypted && !extraFieldAES;
460
498
  if (!passThrough) {
@@ -467,7 +505,7 @@ class ZipEntry {
467
505
  throw new Error(ERR_ENCRYPTED);
468
506
  }
469
507
  }
470
- const dataOffset = offset + dataArraySize + localDirectory.filenameLength + localDirectory.extraFieldLength;
508
+ const dataOffset = offset + HEADER_SIZE + filenameLength + extraFieldLength;
471
509
  const size = compressedSize;
472
510
  const readable = reader.readable;
473
511
  Object.assign(readable, {
@@ -475,14 +513,13 @@ class ZipEntry {
475
513
  offset: dataOffset,
476
514
  size
477
515
  });
478
- const signal = getOptionValue(zipEntry, options, "signal");
479
- const checkPasswordOnly = getOptionValue(zipEntry, options, "checkPasswordOnly");
480
- if (checkPasswordOnly) {
481
- writer = new WritableStream();
482
- }
483
- writer = new GenericWriter(writer);
484
- await initStream(writer, passThrough ? compressedSize : uncompressedSize);
485
- const { writable } = writer;
516
+ const signal = getOptionValue(zipEntry, options, OPTION_SIGNAL);
517
+ const checkPasswordOnly = getOptionValue(zipEntry, options, OPTION_CHECK_PASSWORD_ONLY);
518
+ let checkOverlappingEntry = getOptionValue(zipEntry, options, OPTION_CHECK_OVERLAPPING_ENTRY);
519
+ const checkOverlappingEntryOnly = getOptionValue(zipEntry, options, OPTION_CHECK_OVERLAPPING_ENTRY_ONLY);
520
+ if (checkOverlappingEntryOnly) {
521
+ checkOverlappingEntry = true;
522
+ }
486
523
  const { onstart, onprogress, onend } = options;
487
524
  const workerOptions = {
488
525
  options: {
@@ -491,22 +528,50 @@ class ZipEntry {
491
528
  rawPassword,
492
529
  zipCrypto,
493
530
  encryptionStrength: extraFieldAES && extraFieldAES.strength,
494
- signed: getOptionValue(zipEntry, options, "checkSignature") && !passThrough,
495
- passwordVerification: zipCrypto && (bitFlag.dataDescriptor ? ((rawLastModDate >>> 8) & 0xFF) : ((signature >>> 24) & 0xFF)),
531
+ signed: getOptionValue(zipEntry, options, OPTION_CHECK_SIGNATURE) && !passThrough,
532
+ passwordVerification: zipCrypto && (dataDescriptor ? ((rawLastModDate >>> 8) & 0xFF) : ((signature >>> 24) & 0xFF)),
533
+ outputSize: uncompressedSize,
496
534
  signature,
497
535
  compressed: compressionMethod != 0 && !passThrough,
498
536
  encrypted: zipEntry.encrypted && !passThrough,
499
- useWebWorkers: getOptionValue(zipEntry, options, "useWebWorkers"),
500
- useCompressionStream: getOptionValue(zipEntry, options, "useCompressionStream"),
501
- transferStreams: getOptionValue(zipEntry, options, "transferStreams"),
537
+ useWebWorkers: getOptionValue(zipEntry, options, OPTION_USE_WEB_WORKERS),
538
+ useCompressionStream: getOptionValue(zipEntry, options, OPTION_USE_COMPRESSION_STREAM),
539
+ transferStreams: getOptionValue(zipEntry, options, OPTION_TRANSFER_STREAMS),
502
540
  checkPasswordOnly
503
541
  },
504
542
  config,
505
543
  streamOptions: { signal, size, onstart, onprogress, onend }
506
544
  };
545
+ if (checkOverlappingEntry) {
546
+ await detectOverlappingEntry({
547
+ reader,
548
+ fileEntry,
549
+ offset,
550
+ diskNumberStart,
551
+ signature,
552
+ compressedSize,
553
+ uncompressedSize,
554
+ dataOffset,
555
+ dataDescriptor: dataDescriptor || localDirectory.bitFlag.dataDescriptor,
556
+ extraFieldZip64: extraFieldZip64 || localDirectory.extraFieldZip64,
557
+ readRanges
558
+ });
559
+ }
560
+ let writable;
507
561
  try {
508
- const { outputSize } = await runWorker({ readable, writable }, workerOptions);
509
- writer.size += outputSize;
562
+ if (!checkOverlappingEntryOnly) {
563
+ if (checkPasswordOnly) {
564
+ writer = new WritableStream();
565
+ }
566
+ writer = new GenericWriter(writer);
567
+ await initStream(writer, passThrough ? compressedSize : uncompressedSize);
568
+ ({ writable } = writer);
569
+ const { outputSize } = await runWorker({ readable, writable }, workerOptions);
570
+ writer.size += outputSize;
571
+ if (outputSize != (passThrough ? compressedSize : uncompressedSize)) {
572
+ throw new Error(ERR_INVALID_UNCOMPRESSED_SIZE);
573
+ }
574
+ }
510
575
  } catch (error) {
511
576
  if (error.outputSize !== UNDEFINED_VALUE) {
512
577
  writer.size += error.outputSize;
@@ -515,12 +580,12 @@ class ZipEntry {
515
580
  throw error;
516
581
  }
517
582
  } finally {
518
- const preventClose = getOptionValue(zipEntry, options, "preventClose");
519
- if (!preventClose && !writable.locked) {
583
+ const preventClose = getOptionValue(zipEntry, options, OPTION_PREVENT_CLOSE);
584
+ if (!preventClose && writable && !writable.locked) {
520
585
  await writable.getWriter().close();
521
586
  }
522
587
  }
523
- return checkPasswordOnly ? UNDEFINED_VALUE : writer.getData ? writer.getData() : writable;
588
+ return checkPasswordOnly || checkOverlappingEntryOnly ? UNDEFINED_VALUE : writer.getData ? writer.getData() : writable;
524
589
  }
525
590
  }
526
591
 
@@ -728,6 +793,71 @@ function readExtraFieldExtendedTimestamp(extraFieldExtendedTimestamp, directory,
728
793
  });
729
794
  }
730
795
 
796
+ async function detectOverlappingEntry({
797
+ reader,
798
+ fileEntry,
799
+ offset,
800
+ diskNumberStart,
801
+ signature,
802
+ compressedSize,
803
+ uncompressedSize,
804
+ dataOffset,
805
+ dataDescriptor,
806
+ extraFieldZip64,
807
+ readRanges
808
+ }) {
809
+ let diskOffset = 0;
810
+ if (diskNumberStart) {
811
+ for (let indexReader = 0; indexReader < diskNumberStart; indexReader++) {
812
+ const diskReader = reader.readers[indexReader];
813
+ diskOffset += diskReader.size;
814
+ }
815
+ }
816
+ let dataDescriptorLength = 0;
817
+ if (dataDescriptor) {
818
+ if (extraFieldZip64) {
819
+ dataDescriptorLength = DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH;
820
+ } else {
821
+ dataDescriptorLength = DATA_DESCRIPTOR_RECORD_LENGTH;
822
+ }
823
+ }
824
+ if (dataDescriptorLength) {
825
+ const dataDescriptorArray = await readUint8Array(reader, dataOffset + compressedSize, dataDescriptorLength + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH, diskNumberStart);
826
+ const dataDescriptorSignature = getUint32(getDataView(dataDescriptorArray), 0) == DATA_DESCRIPTOR_RECORD_SIGNATURE;
827
+ if (dataDescriptorSignature) {
828
+ const readSignature = getUint32(getDataView(dataDescriptorArray), 4);
829
+ let readCompressedSize;
830
+ let readUncompressedSize;
831
+ if (extraFieldZip64) {
832
+ readCompressedSize = getBigUint64(getDataView(dataDescriptorArray), 8);
833
+ readUncompressedSize = getBigUint64(getDataView(dataDescriptorArray), 16);
834
+ } else {
835
+ readCompressedSize = getUint32(getDataView(dataDescriptorArray), 8);
836
+ readUncompressedSize = getUint32(getDataView(dataDescriptorArray), 12);
837
+ }
838
+ const matchSignature = (fileEntry.encrypted && !fileEntry.zipCrypto) || readSignature == signature;
839
+ if (matchSignature &&
840
+ readCompressedSize == compressedSize &&
841
+ readUncompressedSize == uncompressedSize) {
842
+ dataDescriptorLength += DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH;
843
+ }
844
+ }
845
+ }
846
+ const range = {
847
+ start: diskOffset + offset,
848
+ end: diskOffset + dataOffset + compressedSize + dataDescriptorLength,
849
+ fileEntry
850
+ };
851
+ for (const otherRange of readRanges) {
852
+ if (otherRange.fileEntry != fileEntry && range.start >= otherRange.start && range.start < otherRange.end) {
853
+ const error = new Error(ERR_OVERLAPPING_ENTRY);
854
+ error.overlappingEntry = otherRange.fileEntry;
855
+ throw error;
856
+ }
857
+ }
858
+ readRanges.push(range);
859
+ }
860
+
731
861
  async function seekSignature(reader, signature, startOffset, minimumBytes, maximumLength) {
732
862
  const signatureArray = new Uint8Array(4);
733
863
  const signatureView = getDataView(signatureArray);
@@ -43,6 +43,9 @@ import {
43
43
  END_OF_CENTRAL_DIR_SIGNATURE,
44
44
  ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIGNATURE,
45
45
  ZIP64_END_OF_CENTRAL_DIR_SIGNATURE,
46
+ DATA_DESCRIPTOR_RECORD_LENGTH,
47
+ DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH,
48
+ DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH,
46
49
  EXTRAFIELD_TYPE_AES,
47
50
  EXTRAFIELD_TYPE_ZIP64,
48
51
  EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP,
@@ -105,8 +108,34 @@ import {
105
108
  PROPERTY_NAME_ZIPCRYPTO,
106
109
  PROPERTY_NAME_DIRECTORY,
107
110
  PROPERTY_NAME_EXECUTABLE,
111
+ PROPERTY_NAME_COMPRESSION_METHOD,
112
+ PROPERTY_NAME_SIGNATURE,
113
+ PROPERTY_NAME_COMMENT,
114
+ PROPERTY_NAME_UNCOMPRESSED_SIZE,
115
+ PROPERTY_NAME_EXTRA_FIELD,
108
116
  Entry
109
117
  } from "./zip-entry.js";
118
+ import {
119
+ OPTION_PASSWORD,
120
+ OPTION_RAW_PASSWORD,
121
+ OPTION_PASS_THROUGH,
122
+ OPTION_SIGNAL,
123
+ OPTION_USE_WEB_WORKERS,
124
+ OPTION_USE_COMPRESSION_STREAM,
125
+ OPTION_PREVENT_CLOSE,
126
+ OPTION_ENCRYPTION_STRENGTH,
127
+ OPTION_EXTENDED_TIMESTAMP,
128
+ OPTION_KEEP_ORDER,
129
+ OPTION_LEVEL,
130
+ OPTION_BUFFERED_WRITE,
131
+ OPTION_DATA_DESCRIPTOR_SIGNATURE,
132
+ OPTION_USE_UNICODE_FILE_NAMES,
133
+ OPTION_DATA_DESCRIPTOR,
134
+ OPTION_SUPPORT_ZIP64_SPLIT_FILE,
135
+ OPTION_ENCODE_TEXT,
136
+ OPTION_OFFSET,
137
+ OPTION_USDZ
138
+ } from "./options.js";
110
139
  import {
111
140
  ZipReader
112
141
  } from "./zip-reader.js";
@@ -142,7 +171,7 @@ class ZipWriter {
142
171
  config: getConfiguration(),
143
172
  files: new Map(),
144
173
  filenames: new Set(),
145
- offset: options.offset === UNDEFINED_VALUE ? writer.size || writer.writable.size || 0 : options.offset,
174
+ offset: options[OPTION_OFFSET] === UNDEFINED_VALUE ? writer.size || writer.writable.size || 0 : options[OPTION_OFFSET],
146
175
  pendingEntriesSize: 0,
147
176
  pendingAddFileCalls: new Set(),
148
177
  bufferedWrites: 0
@@ -181,7 +210,7 @@ class ZipWriter {
181
210
  rawExtraFieldAES,
182
211
  rawExtraFieldExtendedTimestamp,
183
212
  rawExtraFieldNTFS,
184
- rawExtraField,
213
+ rawExtraField
185
214
  } = entry;
186
215
  const { level, languageEncodingFlag, dataDescriptor } = bitFlag;
187
216
  rawExtraFieldZip64 = rawExtraFieldZip64 || new Uint8Array();
@@ -285,7 +314,7 @@ class ZipWriter {
285
314
  await Promise.allSettled(Array.from(pendingAddFileCalls));
286
315
  }
287
316
  await closeFile(this, comment, options);
288
- const preventClose = getOptionValue(zipWriter, options, "preventClose");
317
+ const preventClose = getOptionValue(zipWriter, options, OPTION_PREVENT_CLOSE);
289
318
  if (!preventClose) {
290
319
  await writable.getWriter().close();
291
320
  }
@@ -348,8 +377,8 @@ async function addFile(zipWriter, name, reader, options) {
348
377
  if (externalFileAttributes === 0) {
349
378
  externalFileAttributes = getOptionValue(zipWriter, options, PROPERTY_NAME_EXTERNAL_FILE_ATTRIBUTE, 0);
350
379
  }
351
- if (!options.directory && name.endsWith(DIRECTORY_SIGNATURE)) {
352
- options.directory = true;
380
+ if (!options[PROPERTY_NAME_DIRECTORY] && name.endsWith(DIRECTORY_SIGNATURE)) {
381
+ options[PROPERTY_NAME_DIRECTORY] = true;
353
382
  }
354
383
  const directory = getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY);
355
384
  if (directory) {
@@ -369,7 +398,7 @@ async function addFile(zipWriter, name, reader, options) {
369
398
  externalFileAttributes = FILE_ATTR_UNIX_DEFAULT_MASK << 16;
370
399
  }
371
400
  }
372
- const encode = getOptionValue(zipWriter, options, "encodeText", encodeText);
401
+ const encode = getOptionValue(zipWriter, options, OPTION_ENCODE_TEXT, encodeText);
373
402
  let rawFilename = encode(name);
374
403
  if (rawFilename === UNDEFINED_VALUE) {
375
404
  rawFilename = encodeText(name);
@@ -377,7 +406,7 @@ async function addFile(zipWriter, name, reader, options) {
377
406
  if (getLength(rawFilename) > MAX_16_BITS) {
378
407
  throw new Error(ERR_INVALID_ENTRY_NAME);
379
408
  }
380
- const comment = options.comment || "";
409
+ const comment = options[PROPERTY_NAME_COMMENT] || "";
381
410
  let rawComment = encode(comment);
382
411
  if (rawComment === UNDEFINED_VALUE) {
383
412
  rawComment = encodeText(comment);
@@ -396,25 +425,25 @@ async function addFile(zipWriter, name, reader, options) {
396
425
  if (internalFileAttributes === 0) {
397
426
  internalFileAttributes = getOptionValue(zipWriter, options, PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTE, 0);
398
427
  }
399
- const passThrough = getOptionValue(zipWriter, options, "passThrough");
428
+ const passThrough = getOptionValue(zipWriter, options, OPTION_PASS_THROUGH);
400
429
  let password, rawPassword;
401
430
  if (!passThrough) {
402
- password = getOptionValue(zipWriter, options, "password");
403
- rawPassword = getOptionValue(zipWriter, options, "rawPassword");
431
+ password = getOptionValue(zipWriter, options, OPTION_PASSWORD);
432
+ rawPassword = getOptionValue(zipWriter, options, OPTION_RAW_PASSWORD);
404
433
  }
405
- const encryptionStrength = getOptionValue(zipWriter, options, "encryptionStrength", 3);
434
+ const encryptionStrength = getOptionValue(zipWriter, options, OPTION_ENCRYPTION_STRENGTH, 3);
406
435
  const zipCrypto = getOptionValue(zipWriter, options, PROPERTY_NAME_ZIPCRYPTO);
407
- const extendedTimestamp = getOptionValue(zipWriter, options, "extendedTimestamp", true);
408
- const keepOrder = getOptionValue(zipWriter, options, "keepOrder", true);
409
- const level = getOptionValue(zipWriter, options, "level");
410
- const useWebWorkers = getOptionValue(zipWriter, options, "useWebWorkers");
411
- const bufferedWrite = getOptionValue(zipWriter, options, "bufferedWrite");
412
- const dataDescriptorSignature = getOptionValue(zipWriter, options, "dataDescriptorSignature", false);
413
- const signal = getOptionValue(zipWriter, options, "signal");
414
- const useUnicodeFileNames = getOptionValue(zipWriter, options, "useUnicodeFileNames", true);
415
- const useCompressionStream = getOptionValue(zipWriter, options, "useCompressionStream");
416
- const compressionMethod = getOptionValue(zipWriter, options, "compressionMethod");
417
- let dataDescriptor = getOptionValue(zipWriter, options, "dataDescriptor");
436
+ const extendedTimestamp = getOptionValue(zipWriter, options, OPTION_EXTENDED_TIMESTAMP, true);
437
+ const keepOrder = getOptionValue(zipWriter, options, OPTION_KEEP_ORDER, true);
438
+ const level = getOptionValue(zipWriter, options, OPTION_LEVEL);
439
+ const useWebWorkers = getOptionValue(zipWriter, options, OPTION_USE_WEB_WORKERS);
440
+ const bufferedWrite = getOptionValue(zipWriter, options, OPTION_BUFFERED_WRITE);
441
+ const dataDescriptorSignature = getOptionValue(zipWriter, options, OPTION_DATA_DESCRIPTOR_SIGNATURE, false);
442
+ const signal = getOptionValue(zipWriter, options, OPTION_SIGNAL);
443
+ const useUnicodeFileNames = getOptionValue(zipWriter, options, OPTION_USE_UNICODE_FILE_NAMES, true);
444
+ const useCompressionStream = getOptionValue(zipWriter, options, OPTION_USE_COMPRESSION_STREAM);
445
+ const compressionMethod = getOptionValue(zipWriter, options, PROPERTY_NAME_COMPRESSION_METHOD);
446
+ let dataDescriptor = getOptionValue(zipWriter, options, OPTION_DATA_DESCRIPTOR);
418
447
  if (bufferedWrite && dataDescriptor === UNDEFINED_VALUE) {
419
448
  dataDescriptor = false;
420
449
  }
@@ -426,7 +455,7 @@ async function addFile(zipWriter, name, reader, options) {
426
455
  throw new Error(ERR_INVALID_ENCRYPTION_STRENGTH);
427
456
  }
428
457
  let rawExtraField = new Uint8Array();
429
- const { extraField } = options;
458
+ const extraField = options[PROPERTY_NAME_EXTRA_FIELD];
430
459
  if (extraField) {
431
460
  let extraFieldSize = 0;
432
461
  let offset = 0;
@@ -449,7 +478,7 @@ async function addFile(zipWriter, name, reader, options) {
449
478
  let maximumEntrySize = 0;
450
479
  let uncompressedSize = 0;
451
480
  if (passThrough) {
452
- ({ uncompressedSize } = options);
481
+ uncompressedSize = options[PROPERTY_NAME_UNCOMPRESSED_SIZE];
453
482
  if (uncompressedSize === UNDEFINED_VALUE) {
454
483
  throw new Error(ERR_UNDEFINED_UNCOMPRESSED_SIZE);
455
484
  }
@@ -478,7 +507,7 @@ async function addFile(zipWriter, name, reader, options) {
478
507
  const zip64UncompressedSize = zip64Enabled || uncompressedSize > MAX_32_BITS;
479
508
  const zip64CompressedSize = zip64Enabled || maximumCompressedSize > MAX_32_BITS;
480
509
  const zip64Offset = zip64Enabled || zipWriter.offset + zipWriter.pendingEntriesSize - diskOffset > MAX_32_BITS;
481
- const supportZip64SplitFile = getOptionValue(zipWriter, options, "supportZip64SplitFile", true);
510
+ const supportZip64SplitFile = getOptionValue(zipWriter, options, OPTION_SUPPORT_ZIP64_SPLIT_FILE, true);
482
511
  const zip64DiskNumberStart = (supportZip64SplitFile && zip64Enabled) || diskNumber + Math.ceil(zipWriter.pendingEntriesSize / maxSize) > MAX_16_BITS;
483
512
  if (zip64Offset || zip64UncompressedSize || zip64CompressedSize || zip64DiskNumberStart) {
484
513
  if (zip64 === false || !keepOrder) {
@@ -489,7 +518,6 @@ async function addFile(zipWriter, name, reader, options) {
489
518
  }
490
519
  zip64 = zip64 || false;
491
520
  const encrypted = getOptionValue(zipWriter, options, PROPERTY_NAME_ENCRYPTED);
492
- const { signature } = options;
493
521
  options = Object.assign({}, options, {
494
522
  rawFilename,
495
523
  rawComment,
@@ -525,17 +553,17 @@ async function addFile(zipWriter, name, reader, options) {
525
553
  useCompressionStream,
526
554
  passThrough,
527
555
  encrypted: Boolean((password && getLength(password)) || (rawPassword && getLength(rawPassword))) || (passThrough && encrypted),
528
- signature,
556
+ signature: options[PROPERTY_NAME_SIGNATURE],
529
557
  compressionMethod,
530
558
  uncompressedSize,
531
559
  offset: zipWriter.offset - diskOffset,
532
- diskNumberStart: diskNumber,
560
+ diskNumberStart: diskNumber
533
561
  });
534
562
  const headerInfo = getHeaderInfo(options);
535
563
  const dataDescriptorInfo = getDataDescriptorInfo(options);
536
564
  const metadataSize = getLength(headerInfo.localHeaderArray, dataDescriptorInfo.dataDescriptorArray);
537
565
  maximumEntrySize = metadataSize + maximumCompressedSize;
538
- if (zipWriter.options.usdz) {
566
+ if (zipWriter.options[OPTION_USDZ]) {
539
567
  maximumEntrySize += maximumEntrySize + 64;
540
568
  }
541
569
  zipWriter.pendingEntriesSize += maximumEntrySize;
@@ -562,7 +590,7 @@ async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
562
590
  const {
563
591
  headerInfo
564
592
  } = entryInfo;
565
- const { usdz } = zipWriter.options;
593
+ const usdz = zipWriter.options[OPTION_USDZ];
566
594
  const previousFileEntry = Array.from(files.values()).pop();
567
595
  let fileEntry = {};
568
596
  let bufferedWrite;
@@ -1008,7 +1036,7 @@ function getHeaderInfo(options) {
1008
1036
  zip64UncompressedSize,
1009
1037
  extraFieldLength
1010
1038
  });
1011
- let localHeaderOffset = HEADER_SIZE + 4;
1039
+ let localHeaderOffset = HEADER_SIZE;
1012
1040
  const localHeaderArray = new Uint8Array(localHeaderOffset + getLength(rawFilename) + extraFieldLength);
1013
1041
  const localHeaderView = getDataView(localHeaderArray);
1014
1042
  setUint32(localHeaderView, 0, LOCAL_FILE_HEADER_SIGNATURE);
@@ -1079,11 +1107,15 @@ function getDataDescriptorInfo({
1079
1107
  }) {
1080
1108
  let dataDescriptorArray = new Uint8Array();
1081
1109
  let dataDescriptorView, dataDescriptorOffset = 0;
1110
+ let dataDescriptorLength = zip64 ? DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH : DATA_DESCRIPTOR_RECORD_LENGTH;
1111
+ if (dataDescriptorSignature) {
1112
+ dataDescriptorLength += DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH;
1113
+ }
1082
1114
  if (dataDescriptor) {
1083
- dataDescriptorArray = new Uint8Array(zip64 ? (dataDescriptorSignature ? 24 : 20) : (dataDescriptorSignature ? 16 : 12));
1115
+ dataDescriptorArray = new Uint8Array(dataDescriptorLength);
1084
1116
  dataDescriptorView = getDataView(dataDescriptorArray);
1085
1117
  if (dataDescriptorSignature) {
1086
- dataDescriptorOffset = 4;
1118
+ dataDescriptorOffset = DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH;
1087
1119
  setUint32(dataDescriptorView, 0, DATA_DESCRIPTOR_RECORD_SIGNATURE);
1088
1120
  }
1089
1121
  }
@@ -1160,7 +1192,7 @@ function updateLocalHeader({
1160
1192
  }
1161
1193
  if (zip64) {
1162
1194
  if (localExtraFieldZip64Length) {
1163
- let localHeaderOffset = HEADER_SIZE + 4 + getLength(rawFilename) + 4;
1195
+ let localHeaderOffset = HEADER_SIZE + getLength(rawFilename) + 4;
1164
1196
  if (zip64UncompressedSize) {
1165
1197
  setBigUint64(localHeaderView, localHeaderOffset, BigInt(uncompressedSize));
1166
1198
  localHeaderOffset += 8;
@@ -1290,7 +1322,7 @@ async function closeFile(zipWriter, comment, options) {
1290
1322
  setUint32(headerView, HEADER_OFFSET_COMPRESSED_SIZE, compressedSize);
1291
1323
  }
1292
1324
  arraySet(directoryArray, headerArray, offset + 6);
1293
- let directoryOffset = offset + HEADER_SIZE + 4;
1325
+ let directoryOffset = offset + HEADER_SIZE;
1294
1326
  setUint16(directoryView, directoryOffset, extraFieldLength);
1295
1327
  directoryOffset += 2;
1296
1328
  setUint16(directoryView, directoryOffset, getLength(rawComment));
@@ -1364,7 +1396,7 @@ async function closeFile(zipWriter, comment, options) {
1364
1396
  setUint32(endOfdirectoryView, 56, ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIGNATURE);
1365
1397
  setBigUint64(endOfdirectoryView, 64, BigInt(directoryOffset) + BigInt(directoryDataLength));
1366
1398
  setUint32(endOfdirectoryView, 72, lastDiskNumber + 1);
1367
- const supportZip64SplitFile = getOptionValue(zipWriter, options, "supportZip64SplitFile", true);
1399
+ const supportZip64SplitFile = getOptionValue(zipWriter, options, OPTION_SUPPORT_ZIP64_SPLIT_FILE, true);
1368
1400
  if (supportZip64SplitFile) {
1369
1401
  lastDiskNumber = MAX_16_BITS;
1370
1402
  diskNumber = MAX_16_BITS;
@@ -1464,7 +1496,7 @@ function getHeaderArrayData({
1464
1496
  zip64UncompressedSize,
1465
1497
  extraFieldLength
1466
1498
  }) {
1467
- const headerArray = new Uint8Array(HEADER_SIZE);
1499
+ const headerArray = new Uint8Array(HEADER_SIZE - 4);
1468
1500
  const headerView = getDataView(headerArray);
1469
1501
  setUint16(headerView, 0, version);
1470
1502
  setUint16(headerView, 2, bitFlag);