@zip.js/zip.js 2.8.57 → 2.8.58

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.
@@ -92,6 +92,7 @@ const FILE_ATTR_UNIX_SETUID_MASK = 0o4000;
92
92
  const FILE_ATTR_UNIX_SETGID_MASK = 0o2000;
93
93
  const FILE_ATTR_UNIX_STICKY_MASK = 0o1000;
94
94
 
95
+ const VERSION_STORE = 0x0A;
95
96
  const VERSION_DEFLATE = 0x14;
96
97
  const VERSION_ZIP64 = 0x2D;
97
98
  const VERSION_AES = 0x33;
@@ -4957,6 +4958,10 @@ class ZipReader {
4957
4958
  const commentEncoding = getOptionValue$1(zipReader, options, OPTION_COMMENT_ENCODING);
4958
4959
  const filenames = checkAmbiguity ? new Set() : UNDEFINED_VALUE;
4959
4960
  let duplicateFilename;
4961
+ const recoverWrappedFilesLength = !checkAmbiguity && !zip64EndOfDirectory;
4962
+ if (!filesLength && recoverWrappedFilesLength) {
4963
+ filesLength = getWrappedFilesLength(directoryView, directoryArray, offset);
4964
+ }
4960
4965
  for (let indexFile = 0; indexFile < filesLength; indexFile++) {
4961
4966
  const fileEntry = new ZipEntry(reader, zipReader.options);
4962
4967
  if (offset + CENTRAL_FILE_HEADER_LENGTH > directoryArray.length || getUint32$1(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE) {
@@ -5066,7 +5071,7 @@ class ZipReader {
5066
5071
  internalFileAttribute: fileEntry.internalFileAttributes,
5067
5072
  externalFileAttribute: fileEntry.externalFileAttributes,
5068
5073
  executable,
5069
- directory: modeIsDir || upperIsDir || (msDosCompatible && msdosAttributes.directory) || (fileEntry.filename.endsWith(DIRECTORY_SIGNATURE) && !fileEntry.uncompressedSize),
5074
+ directory: modeIsDir || upperIsDir || (msDosCompatible && msdosAttributes.directory) || fileEntry.filename.endsWith(DIRECTORY_SIGNATURE),
5070
5075
  zipCrypto: fileEntry.encrypted && !fileEntry.extraFieldAES
5071
5076
  });
5072
5077
  const entry = new Entry(fileEntry);
@@ -5079,6 +5084,9 @@ class ZipReader {
5079
5084
  return arrayBufferPromise;
5080
5085
  };
5081
5086
  offset = endOffset;
5087
+ if (indexFile == filesLength - 1 && recoverWrappedFilesLength) {
5088
+ filesLength += getWrappedFilesLength(directoryView, directoryArray, offset);
5089
+ }
5082
5090
  const { onprogress } = options;
5083
5091
  if (onprogress) {
5084
5092
  try {
@@ -5237,6 +5245,7 @@ class ZipEntry {
5237
5245
  extraFieldLength,
5238
5246
  filenameLength
5239
5247
  } = localDirectory;
5248
+ const dataOffset = localDirectory.dataOffset = localHeaderOffset + HEADER_SIZE + filenameLength + extraFieldLength;
5240
5249
  const checkLocalDirectoryOption = getOptionValue$1(zipEntry, options, OPTION_CHECK_LOCAL_DIRECTORY);
5241
5250
  const entryStrictness = getStrictness(options, zipEntry.options);
5242
5251
  const checkLocalDirectory = getCheckLocalDirectory(checkLocalDirectoryOption, entryStrictness);
@@ -5290,7 +5299,6 @@ class ZipEntry {
5290
5299
  throw new Error(ERR_ENCRYPTED);
5291
5300
  }
5292
5301
  }
5293
- const dataOffset = localHeaderOffset + HEADER_SIZE + filenameLength + extraFieldLength;
5294
5302
  if (dataOffset + compressedSize > reader.size) {
5295
5303
  throw new Error(ERR_ENTRY_DATA_OUT_OF_BOUNDS);
5296
5304
  }
@@ -5410,6 +5418,16 @@ function detectEncryptedCentralDirectory(directoryView) {
5410
5418
  return false;
5411
5419
  }
5412
5420
 
5421
+ function getWrappedFilesLength(directoryView, directoryArray, offset) {
5422
+ let wrappedFilesLength = 0;
5423
+ while (offset + CENTRAL_FILE_HEADER_LENGTH <= directoryArray.length && getUint32$1(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE) {
5424
+ offset += CENTRAL_FILE_HEADER_LENGTH +
5425
+ getUint16$1(directoryView, offset + 28) + getUint16$1(directoryView, offset + 30) + getUint16$1(directoryView, offset + 32);
5426
+ wrappedFilesLength++;
5427
+ }
5428
+ return wrappedFilesLength % (MAX_16_BITS + 1) ? 0 : wrappedFilesLength;
5429
+ }
5430
+
5413
5431
  function readDigitalSignature(signatureRecordArray) {
5414
5432
  if (signatureRecordArray.length >= 6) {
5415
5433
  const signatureRecordView = getDataView(signatureRecordArray);
@@ -5582,10 +5600,11 @@ function readExtraFieldUnicode(extraFieldUnicode, propertyName, rawPropertyName,
5582
5600
  const computedCrc32View = getDataView(new Uint8Array(4));
5583
5601
  computedCrc32View.setUint32(0, computedCrc32.get(), true);
5584
5602
  const nameCrc32 = getUint32$1(extraFieldView, 1);
5603
+ const version = getUint8(extraFieldView, 0);
5585
5604
  Object.assign(extraFieldUnicode, {
5586
- version: getUint8(extraFieldView, 0),
5605
+ version,
5587
5606
  [propertyName]: decodeText(extraFieldUnicode.data.subarray(5)),
5588
- valid: !fileEntry.bitFlag.languageEncodingFlag && nameCrc32 == getUint32$1(computedCrc32View, 0)
5607
+ valid: version == 1 && !fileEntry.bitFlag.languageEncodingFlag && nameCrc32 == getUint32$1(computedCrc32View, 0)
5589
5608
  });
5590
5609
  if (extraFieldUnicode.valid) {
5591
5610
  directory[propertyName] = extraFieldUnicode[propertyName];
@@ -6054,7 +6073,8 @@ function getFunctionOptionValue$1(zipReader, options, name) {
6054
6073
 
6055
6074
  function getDate(timeRaw) {
6056
6075
  const date = (timeRaw & 0xffff0000) >> 16, time = timeRaw & MAX_16_BITS;
6057
- return new Date(1980 + ((date & 0xFE00) >> 9), ((date & 0x01E0) >> 5) - 1, date & 0x001F, (time & 0xF800) >> 11, (time & 0x07E0) >> 5, (time & 0x001F) * 2, 0);
6076
+ const result = new Date(1980 + ((date & 0xFE00) >> 9), ((date & 0x01E0) >> 5) - 1, date & 0x001F, (time & 0xF800) >> 11, (time & 0x07E0) >> 5, (time & 0x001F) * 2, 0);
6077
+ return result < MIN_DATE ? MIN_DATE : result;
6058
6078
  }
6059
6079
 
6060
6080
  function getDateNTFS(timeRaw) {
@@ -6207,153 +6227,150 @@ class ZipWriter {
6207
6227
  offset: options[OPTION_OFFSET] === UNDEFINED_VALUE ? writer.size || writer.writable.size || 0 : options[OPTION_OFFSET],
6208
6228
  initialOffset: options[OPTION_OFFSET] === UNDEFINED_VALUE ? 0 : options[OPTION_OFFSET] - (writer.size || writer.writable.size || 0),
6209
6229
  pendingAddFileCalls: new Set(),
6230
+ pendingErrors: [],
6210
6231
  bufferedWrites: 0,
6211
6232
  lastFileEntry: UNDEFINED_VALUE
6212
6233
  });
6213
6234
  }
6214
6235
 
6215
- async prependZip(reader) {
6216
- if (this.filenames.size) {
6217
- throw new Error(ERR_ZIP_NOT_EMPTY);
6218
- }
6219
- reader = new GenericReader(reader);
6220
- await initStream(reader);
6221
- if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
6222
- reader = new BlobReader(await streamToBlob(reader.readable));
6223
- await initStream(reader);
6224
- }
6225
- const { ZipReader } = await Promise.resolve().then(function () { return zipReader; });
6226
- const zipReader$1 = new ZipReader(reader);
6227
- const entries = await zipReader$1.getEntries();
6228
- await zipReader$1.close();
6229
- await initStream(this.writer);
6230
- const { directoryOffset } = zipReader$1;
6231
- if (this.addSplitZipSignature) {
6232
- delete this.addSplitZipSignature;
6233
- if (!await startsWithSplitZipSignature(reader)) {
6234
- await writeData(this.writer, getSplitZipSignatureArray());
6235
- this.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
6236
- }
6237
- }
6238
- const entryPositions = await copyZipData(this, reader, entries, directoryOffset);
6239
- this.filenames = new Set(entries.map(entry => entry.filename));
6240
- this.fileEntries = new Map(entries.map(entry => {
6241
- const {
6242
- version,
6243
- rawLastModDate,
6244
- lastAccessDate,
6245
- creationDate,
6246
- rawFilename,
6247
- bitFlag,
6248
- encrypted,
6249
- uncompressedSize,
6250
- compressedSize,
6251
- extraFieldZip64
6252
- } = entry;
6253
- let {
6254
- compressionMethod,
6255
- rawExtraFieldZip64,
6256
- rawExtraFieldAES,
6257
- rawExtraFieldExtendedTimestamp,
6258
- rawExtraFieldNTFS,
6259
- rawExtraFieldUnix,
6260
- rawExtraField,
6261
- } = entry;
6262
- const { level, languageEncodingFlag, dataDescriptor } = bitFlag;
6263
- rawExtraFieldZip64 = rawExtraFieldZip64 || EMPTY_UINT8_ARRAY;
6264
- rawExtraFieldAES = rawExtraFieldAES || EMPTY_UINT8_ARRAY;
6265
- rawExtraFieldExtendedTimestamp = rawExtraFieldExtendedTimestamp || EMPTY_UINT8_ARRAY;
6266
- rawExtraFieldNTFS = rawExtraFieldNTFS || EMPTY_UINT8_ARRAY;
6267
- rawExtraFieldUnix = rawExtraFieldUnix || EMPTY_UINT8_ARRAY;
6268
- rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
6269
- if (entry.extraFieldAES) {
6270
- compressionMethod = COMPRESSION_METHOD_AES;
6271
- }
6272
- const extraFieldLength = getLength(rawExtraFieldZip64, rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawExtraFieldUnix, rawExtraField);
6273
- const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
6274
- const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
6275
- const bitFlagValue = (getBitFlag(level, languageEncodingFlag, dataDescriptor, encrypted, compressionMethod) & ~BITFLAG_LEVEL) | (level << 1);
6276
- const {
6277
- headerArray,
6278
- headerView
6279
- } = getHeaderArrayData({
6280
- version,
6281
- bitFlag: bitFlagValue,
6282
- compressionMethod,
6283
- uncompressedSize,
6284
- compressedSize,
6285
- rawLastModDate,
6286
- rawFilename,
6287
- zip64CompressedSize,
6288
- zip64UncompressedSize,
6289
- extraFieldLength
6290
- });
6291
- const { crc32 } = entry;
6292
- if (crc32 !== UNDEFINED_VALUE) {
6293
- setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
6294
- }
6295
- const { offset, diskNumberStart } = entryPositions.get(entry);
6296
- Object.assign(entry, {
6297
- zip64UncompressedSize,
6298
- zip64CompressedSize,
6299
- offset,
6300
- diskNumberStart,
6301
- zip64DiskNumberStart: false,
6302
- rawExtraFieldZip64,
6303
- rawExtraFieldAES,
6304
- rawExtraFieldExtendedTimestamp,
6305
- rawExtraFieldNTFS,
6306
- rawExtraFieldUnix,
6307
- rawExtraField,
6308
- extendedTimestamp: rawExtraFieldExtendedTimestamp.length > 0 || rawExtraFieldNTFS.length > 0,
6309
- extraFieldExtendedTimestampFlag: 0x1 + (lastAccessDate ? 0x2 : 0) + (creationDate ? 0x4 : 0),
6310
- headerArray,
6311
- headerView
6312
- });
6313
- return [entry.filename, entry];
6314
- }));
6236
+ prependZip(reader) {
6237
+ return watchPromiseError(this, prependZipEntries(this, reader));
6315
6238
  }
6316
6239
 
6317
- async add(name = "", reader, options = {}) {
6240
+ appendZip(reader) {
6241
+ return watchPromiseError(this, this.appendZipEntries(reader));
6242
+ }
6243
+
6244
+ async appendZipEntries(reader) {
6318
6245
  const zipWriter = this;
6319
- options = Object.assign({}, options);
6320
- const { pendingAddFileCalls } = zipWriter;
6321
- if (workers < getConfiguration().maxWorkers) {
6322
- workers++;
6323
- } else {
6324
- await new Promise(resolve => pendingEntries.push(resolve));
6246
+ const { pendingAddFileCalls, filenames, fileEntries } = zipWriter;
6247
+ while (pendingAddFileCalls.size) {
6248
+ await Promise.allSettled(Array.from(pendingAddFileCalls));
6325
6249
  }
6326
- let promiseAddFile;
6327
- let nameAdded;
6250
+ let resolveAppendZip;
6251
+ const promiseAppendZip = new Promise(resolve => resolveAppendZip = resolve);
6252
+ pendingAddFileCalls.add(promiseAppendZip);
6253
+ const appendedFilenames = [];
6254
+ let releaseLockWriter;
6328
6255
  try {
6329
- name = name.trim();
6330
- if (getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY) && !name.endsWith(DIRECTORY_SIGNATURE)) {
6331
- name += DIRECTORY_SIGNATURE;
6332
- }
6333
- if (zipWriter.filenames.has(name)) {
6334
- throw new Error(ERR_DUPLICATED_NAME);
6335
- }
6336
- zipWriter.filenames.add(name);
6337
- nameAdded = true;
6338
- promiseAddFile = addFile(zipWriter, name, reader, options);
6339
- pendingAddFileCalls.add(promiseAddFile);
6340
- return await promiseAddFile;
6341
- } catch (error) {
6342
- if (nameAdded) {
6343
- zipWriter.filenames.delete(name);
6256
+ reader = new GenericReader(reader);
6257
+ await initStream(reader);
6258
+ if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
6259
+ reader = new BlobReader(await streamToBlob(reader.readable));
6260
+ await initStream(reader);
6261
+ }
6262
+ const { ZipReader } = await Promise.resolve().then(function () { return zipReader; });
6263
+ const zipReader$1 = new ZipReader(reader);
6264
+ const entries = await zipReader$1.getEntries();
6265
+ await zipReader$1.close();
6266
+ await initStream(zipWriter.writer);
6267
+ const { directoryOffset } = zipReader$1;
6268
+ entries.forEach(({ filename }) => {
6269
+ if (filenames.has(filename)) {
6270
+ throw new Error(ERR_DUPLICATED_NAME);
6271
+ }
6272
+ filenames.add(filename);
6273
+ appendedFilenames.push(filename);
6274
+ });
6275
+ zipWriter.writerLocked = true;
6276
+ const { lockWriter } = zipWriter;
6277
+ zipWriter.lockWriter = new Promise(resolve => releaseLockWriter = () => {
6278
+ zipWriter.writerLocked = false;
6279
+ resolve();
6280
+ });
6281
+ await lockWriter;
6282
+ if (zipWriter.addSplitZipSignature) {
6283
+ delete zipWriter.addSplitZipSignature;
6284
+ if (!await startsWithSplitZipSignature(reader)) {
6285
+ await writeData(zipWriter.writer, getSplitZipSignatureArray());
6286
+ zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
6287
+ }
6344
6288
  }
6289
+ const entryPositions = await copyZipData(zipWriter, reader, entries, directoryOffset);
6290
+ entries.forEach(entry => {
6291
+ const {
6292
+ version,
6293
+ rawLastModDate,
6294
+ rawFilename,
6295
+ bitFlag,
6296
+ encrypted,
6297
+ uncompressedSize,
6298
+ compressedSize,
6299
+ extraFieldZip64
6300
+ } = entry;
6301
+ let {
6302
+ compressionMethod,
6303
+ rawExtraField,
6304
+ } = entry;
6305
+ const { level, languageEncodingFlag, dataDescriptor } = bitFlag;
6306
+ rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
6307
+ if (entry.extraFieldAES) {
6308
+ compressionMethod = COMPRESSION_METHOD_AES;
6309
+ }
6310
+ const extraFieldLength = getLength(rawExtraField);
6311
+ const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
6312
+ const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
6313
+ const bitFlagValue = (getBitFlag(level, languageEncodingFlag, dataDescriptor, encrypted, compressionMethod) & ~BITFLAG_LEVEL) | (level << 1);
6314
+ const {
6315
+ headerArray,
6316
+ headerView
6317
+ } = getHeaderArrayData({
6318
+ version,
6319
+ bitFlag: bitFlagValue,
6320
+ compressionMethod,
6321
+ uncompressedSize,
6322
+ compressedSize,
6323
+ rawLastModDate,
6324
+ rawFilename,
6325
+ zip64CompressedSize,
6326
+ zip64UncompressedSize,
6327
+ extraFieldLength
6328
+ });
6329
+ const { crc32 } = entry;
6330
+ if (crc32 !== UNDEFINED_VALUE) {
6331
+ setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
6332
+ }
6333
+ const { offset, diskNumberStart } = entryPositions.get(entry);
6334
+ Object.assign(entry, {
6335
+ zip64UncompressedSize,
6336
+ zip64CompressedSize,
6337
+ offset,
6338
+ diskNumberStart,
6339
+ zip64DiskNumberStart: false,
6340
+ rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
6341
+ rawExtraFieldAES: EMPTY_UINT8_ARRAY,
6342
+ rawExtraFieldExtendedTimestamp: EMPTY_UINT8_ARRAY,
6343
+ rawExtraFieldNTFS: EMPTY_UINT8_ARRAY,
6344
+ rawExtraFieldUnix: EMPTY_UINT8_ARRAY,
6345
+ rawExtraField,
6346
+ extendedTimestamp: false,
6347
+ headerArray,
6348
+ headerView
6349
+ });
6350
+ fileEntries.set(entry.filename, entry);
6351
+ });
6352
+ } catch (error) {
6353
+ appendedFilenames.forEach(filename => filenames.delete(filename));
6345
6354
  throw error;
6346
6355
  } finally {
6347
- pendingAddFileCalls.delete(promiseAddFile);
6348
- const pendingEntry = pendingEntries.shift();
6349
- if (pendingEntry) {
6350
- pendingEntry();
6351
- } else {
6352
- workers--;
6356
+ resolveAppendZip();
6357
+ pendingAddFileCalls.delete(promiseAppendZip);
6358
+ if (releaseLockWriter) {
6359
+ releaseLockWriter();
6353
6360
  }
6354
6361
  }
6355
6362
  }
6356
6363
 
6364
+ add(name = "", reader, options = {}) {
6365
+ const zipWriter = this;
6366
+ const { pendingAddFileCalls } = zipWriter;
6367
+ const promiseAddFile = addFileEntry(zipWriter, name, reader, options);
6368
+ pendingAddFileCalls.add(promiseAddFile);
6369
+ const deletePendingAddFileCall = () => pendingAddFileCalls.delete(promiseAddFile);
6370
+ Promise.prototype.then.call(promiseAddFile, deletePendingAddFileCall, deletePendingAddFileCall);
6371
+ return watchPromiseError(zipWriter, promiseAddFile);
6372
+ }
6373
+
6357
6374
  remove(entry) {
6358
6375
  const { filenames, fileEntries } = this;
6359
6376
  // deno-lint-ignore valid-typeof
@@ -6384,6 +6401,19 @@ class ZipWriter {
6384
6401
  while (pendingAddFileCalls.size) {
6385
6402
  await Promise.allSettled(Array.from(pendingAddFileCalls));
6386
6403
  }
6404
+ await Promise.allSettled(zipWriter.pendingErrors.map(watcher => watcher.recorded));
6405
+ const unobservedWatchers = zipWriter.pendingErrors.filter(watcher => watcher.error && !watcher.observed);
6406
+ if (unobservedWatchers.length) {
6407
+ const unobservedErrors = unobservedWatchers.map(watcher => watcher.error);
6408
+ unobservedWatchers.forEach(watcher => watcher.observed = true);
6409
+ const [error] = unobservedErrors;
6410
+ try {
6411
+ error.entryErrors = unobservedErrors;
6412
+ } catch {
6413
+ // ignored
6414
+ }
6415
+ throw error;
6416
+ }
6387
6417
  await closeFile(zipWriter, comment, options);
6388
6418
  const preventClose = !ownsWritable(writer) && getOptionValue(zipWriter, options, OPTION_PREVENT_CLOSE);
6389
6419
  if (!preventClose) {
@@ -6418,11 +6448,7 @@ class ZipWriterStream {
6418
6448
  try {
6419
6449
  await zipWriter.close();
6420
6450
  } catch (error) {
6421
- try {
6422
- await zipWriter.writer.writable.abort(error);
6423
- } catch {
6424
- // ignored
6425
- }
6451
+ await abortWritable(zipWriter, error);
6426
6452
  }
6427
6453
  }
6428
6454
  }
@@ -6439,8 +6465,90 @@ class ZipWriterStream {
6439
6465
  }
6440
6466
 
6441
6467
  async close(comment = UNDEFINED_VALUE, options = {}) {
6442
- await Promise.all(Array.from(this.pendingAddFileCalls));
6443
- return this.zipWriter.close(comment, options);
6468
+ const { zipWriter } = this;
6469
+ const results = await Promise.allSettled(Array.from(this.pendingAddFileCalls));
6470
+ const entryErrors = results.filter(result => result.status == "rejected").map(result => result.reason);
6471
+ if (entryErrors.length) {
6472
+ const [error] = entryErrors;
6473
+ try {
6474
+ error.entryErrors = entryErrors;
6475
+ } catch {
6476
+ // ignored
6477
+ }
6478
+ await abortWritable(zipWriter, error);
6479
+ throw error;
6480
+ }
6481
+ try {
6482
+ return await zipWriter.close(comment, options);
6483
+ } catch (error) {
6484
+ await abortWritable(zipWriter, error);
6485
+ throw error;
6486
+ }
6487
+ }
6488
+ }
6489
+
6490
+ class WatchedPromise extends Promise {
6491
+
6492
+ then(onFulfilled, onRejected) {
6493
+ const { watcher } = this;
6494
+ if (watcher) {
6495
+ watcher.observed = true;
6496
+ }
6497
+ return super.then(onFulfilled, onRejected);
6498
+ }
6499
+ }
6500
+
6501
+ function watchPromiseError(zipWriter, promise) {
6502
+ const watchedPromise = new WatchedPromise((resolve, reject) => Promise.prototype.then.call(promise, resolve, reject));
6503
+ const watcher = {};
6504
+ watchedPromise.watcher = watcher;
6505
+ watcher.recorded = Promise.prototype.then.call(watchedPromise, UNDEFINED_VALUE, error => watcher.error = error);
6506
+ zipWriter.pendingErrors.push(watcher);
6507
+ return watchedPromise;
6508
+ }
6509
+
6510
+ async function prependZipEntries(zipWriter, reader) {
6511
+ if (zipWriter.filenames.size) {
6512
+ throw new Error(ERR_ZIP_NOT_EMPTY);
6513
+ }
6514
+ await zipWriter.appendZipEntries(reader);
6515
+ }
6516
+
6517
+ async function addFileEntry(zipWriter, name, reader, options) {
6518
+ options = Object.assign({}, options);
6519
+ name = name.trim();
6520
+ if (getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY) && !name.endsWith(DIRECTORY_SIGNATURE)) {
6521
+ name += DIRECTORY_SIGNATURE;
6522
+ }
6523
+ if (zipWriter.filenames.has(name)) {
6524
+ throw new Error(ERR_DUPLICATED_NAME);
6525
+ }
6526
+ zipWriter.filenames.add(name);
6527
+ if (workers < getConfiguration().maxWorkers) {
6528
+ workers++;
6529
+ } else {
6530
+ await new Promise(resolve => pendingEntries.push(resolve));
6531
+ }
6532
+ try {
6533
+ return await addFile(zipWriter, name, reader, options);
6534
+ } catch (error) {
6535
+ zipWriter.filenames.delete(name);
6536
+ throw error;
6537
+ } finally {
6538
+ const pendingEntry = pendingEntries.shift();
6539
+ if (pendingEntry) {
6540
+ pendingEntry();
6541
+ } else {
6542
+ workers--;
6543
+ }
6544
+ }
6545
+ }
6546
+
6547
+ async function abortWritable(zipWriter, error) {
6548
+ try {
6549
+ await zipWriter.writer.writable.abort(error);
6550
+ } catch {
6551
+ // ignored
6444
6552
  }
6445
6553
  }
6446
6554
 
@@ -6643,11 +6751,12 @@ function resolveMetadata(zipWriter, name, options) {
6643
6751
  if (getLength(rawComment) > MAX_16_BITS) {
6644
6752
  throw new Error(ERR_INVALID_ENTRY_COMMENT);
6645
6753
  }
6646
- const version = getOptionValue(zipWriter, options, PROPERTY_NAME_VERSION, VERSION_DEFLATE);
6647
- if (version > MAX_16_BITS) {
6754
+ const version = getOptionValue(zipWriter, options, PROPERTY_NAME_VERSION);
6755
+ if (version !== UNDEFINED_VALUE && version > MAX_16_BITS) {
6648
6756
  throw new Error(ERR_INVALID_VERSION);
6649
6757
  }
6650
6758
  const lastModDate = getDateOptionValue(zipWriter, options, PROPERTY_NAME_LAST_MODIFICATION_DATE, new Date());
6759
+ const rawLastModDate = getOptionValue(zipWriter, options, PROPERTY_NAME_RAW_LAST_MODIFICATION_DATE);
6651
6760
  const lastAccessDate = getDateOptionValue(zipWriter, options, PROPERTY_NAME_LAST_ACCESS_DATE);
6652
6761
  const creationDate = getDateOptionValue(zipWriter, options, PROPERTY_NAME_CREATION_DATE);
6653
6762
  const internalFileAttributes = getAliasedOptionValue(zipWriter, options, PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTES, PROPERTY_NAME_DEPRECATED_INTERNAL_FILE_ATTRIBUTES, 0);
@@ -6691,7 +6800,7 @@ function resolveMetadata(zipWriter, name, options) {
6691
6800
  if (bufferedWrite && dataDescriptor === UNDEFINED_VALUE) {
6692
6801
  dataDescriptor = false;
6693
6802
  }
6694
- if (dataDescriptor === UNDEFINED_VALUE || zipCrypto) {
6803
+ if (dataDescriptor === UNDEFINED_VALUE || (zipCrypto && !passThrough)) {
6695
6804
  dataDescriptor = true;
6696
6805
  }
6697
6806
  if (level !== UNDEFINED_VALUE && level != 6) {
@@ -6710,6 +6819,7 @@ function resolveMetadata(zipWriter, name, options) {
6710
6819
  rawComment,
6711
6820
  version,
6712
6821
  lastModDate,
6822
+ rawLastModDate,
6713
6823
  lastAccessDate,
6714
6824
  creationDate,
6715
6825
  internalFileAttributes,
@@ -6906,10 +7016,8 @@ async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
6906
7016
  }
6907
7017
  await initStream(fileWriter);
6908
7018
  const diskOffset = getDiskOffset(writer);
6909
- if (zipWriter.addSplitZipSignature) {
6910
- delete zipWriter.addSplitZipSignature;
6911
- await writeData(writer, getSplitZipSignatureArray());
6912
- zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
7019
+ if (zipWriter.addSplitZipSignature && !bufferedWrite) {
7020
+ await writeSplitZipSignature(zipWriter, writer);
6913
7021
  }
6914
7022
  if (usdz && !bufferedWrite) {
6915
7023
  appendExtraFieldUSDZ(entryInfo, zipWriter.offset - diskOffset);
@@ -6936,6 +7044,9 @@ async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
6936
7044
  if (bufferedWrite) {
6937
7045
  await Promise.all([fileWriter.writable.getWriter().close(), lockPreviousFileEntry]);
6938
7046
  await requestLockWriter();
7047
+ if (zipWriter.addSplitZipSignature) {
7048
+ await writeSplitZipSignature(zipWriter, writer);
7049
+ }
6939
7050
  writingBufferedEntryData = true;
6940
7051
  writerSizeBeforeEntry = writer.size;
6941
7052
  await skipDiskIfNeeded();
@@ -7207,6 +7318,7 @@ function getHeaderInfo(options) {
7207
7318
  const {
7208
7319
  rawFilename,
7209
7320
  lastModDate,
7321
+ rawLastModDate: rawLastModDateOption,
7210
7322
  lastAccessDate,
7211
7323
  creationDate,
7212
7324
  level,
@@ -7340,6 +7452,9 @@ function getHeaderInfo(options) {
7340
7452
  if (compressionMethod === UNDEFINED_VALUE) {
7341
7453
  compressionMethod = compressed ? COMPRESSION_METHOD_DEFLATE : COMPRESSION_METHOD_STORE;
7342
7454
  }
7455
+ if (version === UNDEFINED_VALUE) {
7456
+ version = compressionMethod == COMPRESSION_METHOD_STORE && !directory && !encrypted ? VERSION_STORE : VERSION_DEFLATE;
7457
+ }
7343
7458
  const { codecVersionNeeded } = options;
7344
7459
  if (compressed && codecVersionNeeded !== UNDEFINED_VALUE) {
7345
7460
  version = version > codecVersionNeeded ? version : codecVersionNeeded;
@@ -7372,6 +7487,7 @@ function getHeaderInfo(options) {
7372
7487
  compressionMethod,
7373
7488
  uncompressedSize,
7374
7489
  lastModDate: dosLastModDate < MIN_DATE ? MIN_DATE : dosLastModDate > MAX_DATE ? MAX_DATE : dosLastModDate,
7490
+ rawLastModDate: rawLastModDateOption,
7375
7491
  rawFilename,
7376
7492
  zip64CompressedSize,
7377
7493
  zip64UncompressedSize,
@@ -7578,9 +7694,9 @@ function updateLocalHeader({
7578
7694
 
7579
7695
  async function closeFile(zipWriter, comment, options) {
7580
7696
  const directoryDataLength = createDirectoryRecords(zipWriter.fileEntries);
7581
- const { directoryStart, directoryArray } = await writeDirectoryRecords(zipWriter, directoryDataLength, options);
7697
+ const { directoryStart, directoryEnd, directoryArray } = await writeDirectoryRecords(zipWriter, directoryDataLength, options);
7582
7698
  const signatureLength = await writeDigitalSignatureRecord(zipWriter, directoryArray, options);
7583
- await writeEndOfDirectoryRecord(zipWriter, comment, options, { directoryStart, directoryDataLength, signatureLength });
7699
+ await writeEndOfDirectoryRecord(zipWriter, comment, options, { directoryStart, directoryEnd, directoryDataLength, signatureLength });
7584
7700
  }
7585
7701
 
7586
7702
  function createDirectoryRecords(files) {
@@ -7664,6 +7780,7 @@ async function writeDirectoryRecords(zipWriter, directoryDataLength, options) {
7664
7780
  let directoryDiskOffset = 0;
7665
7781
  let directoryStartDiskNumber = getDiskNumber(writer);
7666
7782
  let directoryStartDiskOffset = getDiskOffset(writer);
7783
+ let directoryEndDiskEntriesLength = 0;
7667
7784
  for (const [indexFileEntry, fileEntry] of Array.from(fileEntries.values()).entries()) {
7668
7785
  const {
7669
7786
  offset: fileEntryOffset,
@@ -7693,6 +7810,7 @@ async function writeDirectoryRecords(zipWriter, directoryDataLength, options) {
7693
7810
  if (exceedsAvailableSize(writer, offset + directoryRecordLength - directoryDiskOffset)) {
7694
7811
  await writeData(writer, directoryArray.slice(directoryDiskOffset, offset));
7695
7812
  directoryDiskOffset = offset;
7813
+ directoryEndDiskEntriesLength = 0;
7696
7814
  await writer.closeDisk();
7697
7815
  }
7698
7816
  if (indexFileEntry == 0) {
@@ -7728,6 +7846,7 @@ async function writeDirectoryRecords(zipWriter, directoryDataLength, options) {
7728
7846
  directoryRecord.writeBytes(rawComment);
7729
7847
  arraySet(directoryArray, directoryRecord.array, offset);
7730
7848
  offset += directoryRecordLength;
7849
+ directoryEndDiskEntriesLength++;
7731
7850
  if (options.onprogress) {
7732
7851
  try {
7733
7852
  await options.onprogress(indexFileEntry + 1, fileEntries.size, new Entry(fileEntry));
@@ -7739,6 +7858,7 @@ async function writeDirectoryRecords(zipWriter, directoryDataLength, options) {
7739
7858
  await writeData(writer, directoryDiskOffset ? directoryArray.slice(directoryDiskOffset) : directoryArray);
7740
7859
  return {
7741
7860
  directoryStart: { diskNumber: directoryStartDiskNumber, diskOffset: directoryStartDiskOffset },
7861
+ directoryEnd: { diskNumber: getDiskNumber(writer), entriesLength: directoryEndDiskEntriesLength },
7742
7862
  directoryArray
7743
7863
  };
7744
7864
  }
@@ -7767,16 +7887,20 @@ async function writeDigitalSignatureRecord(zipWriter, directoryArray, options) {
7767
7887
 
7768
7888
  async function writeEndOfDirectoryRecord(zipWriter, comment, options, cdInfo) {
7769
7889
  const { writer } = zipWriter;
7770
- const { directoryStart, signatureLength } = cdInfo;
7890
+ const { directoryStart, directoryEnd, signatureLength } = cdInfo;
7771
7891
  let { directoryDataLength } = cdInfo;
7772
7892
  let fileEntriesLength = zipWriter.fileEntries.size;
7773
7893
  let diskNumber = directoryStart.diskNumber;
7774
7894
  let directoryOffset = getSegmentOffset(zipWriter, directoryStart);
7895
+ const commentLength = getLength(comment);
7896
+ if (commentLength > MAX_16_BITS) {
7897
+ throw new Error(ERR_INVALID_COMMENT);
7898
+ }
7899
+ let zip64 = getOptionValue(zipWriter, options, PROPERTY_NAME_ZIP64);
7775
7900
  let lastDiskNumber = getDiskNumber(writer);
7776
- if (exceedsAvailableSize(writer, END_OF_CENTRAL_DIR_LENGTH)) {
7901
+ if (exceedsAvailableSize(writer, (zip64 ? ZIP64_END_OF_CENTRAL_DIR_TOTAL_LENGTH : END_OF_CENTRAL_DIR_LENGTH) + commentLength)) {
7777
7902
  lastDiskNumber++;
7778
7903
  }
7779
- let zip64 = getOptionValue(zipWriter, options, PROPERTY_NAME_ZIP64);
7780
7904
  if (directoryOffset >= MAX_32_BITS || directoryDataLength >= MAX_32_BITS || fileEntriesLength >= MAX_16_BITS || lastDiskNumber >= MAX_16_BITS) {
7781
7905
  if (zip64 === false) {
7782
7906
  throw new Error(ERR_UNSUPPORTED_FORMAT);
@@ -7784,15 +7908,12 @@ async function writeEndOfDirectoryRecord(zipWriter, comment, options, cdInfo) {
7784
7908
  zip64 = true;
7785
7909
  }
7786
7910
  }
7787
- const commentLength = getLength(comment);
7788
- if (commentLength > MAX_16_BITS) {
7789
- throw new Error(ERR_INVALID_COMMENT);
7790
- }
7791
7911
  const endOfdirectoryRecord = createRecordWriter(zip64 ? ZIP64_END_OF_CENTRAL_DIR_TOTAL_LENGTH : END_OF_CENTRAL_DIR_LENGTH);
7792
7912
  if (exceedsAvailableSize(writer, getLength(endOfdirectoryRecord.array) + commentLength)) {
7793
7913
  await writer.closeDisk();
7794
7914
  }
7795
7915
  lastDiskNumber = getDiskNumber(writer);
7916
+ let diskFileEntriesLength = lastDiskNumber == directoryEnd.diskNumber ? directoryEnd.entriesLength : 0;
7796
7917
  if (zip64) {
7797
7918
  endOfdirectoryRecord.writeUint32(ZIP64_END_OF_CENTRAL_DIR_SIGNATURE);
7798
7919
  endOfdirectoryRecord.writeUint64(44);
@@ -7800,7 +7921,7 @@ async function writeEndOfDirectoryRecord(zipWriter, comment, options, cdInfo) {
7800
7921
  endOfdirectoryRecord.writeUint16(45);
7801
7922
  endOfdirectoryRecord.writeUint32(lastDiskNumber);
7802
7923
  endOfdirectoryRecord.writeUint32(diskNumber);
7803
- endOfdirectoryRecord.writeUint64(fileEntriesLength);
7924
+ endOfdirectoryRecord.writeUint64(diskFileEntriesLength);
7804
7925
  endOfdirectoryRecord.writeUint64(fileEntriesLength);
7805
7926
  endOfdirectoryRecord.writeUint64(directoryDataLength);
7806
7927
  endOfdirectoryRecord.writeUint64(directoryOffset);
@@ -7813,6 +7934,7 @@ async function writeEndOfDirectoryRecord(zipWriter, comment, options, cdInfo) {
7813
7934
  lastDiskNumber = MAX_16_BITS;
7814
7935
  diskNumber = MAX_16_BITS;
7815
7936
  }
7937
+ diskFileEntriesLength = MAX_16_BITS;
7816
7938
  fileEntriesLength = MAX_16_BITS;
7817
7939
  directoryOffset = MAX_32_BITS;
7818
7940
  directoryDataLength = MAX_32_BITS;
@@ -7820,7 +7942,7 @@ async function writeEndOfDirectoryRecord(zipWriter, comment, options, cdInfo) {
7820
7942
  endOfdirectoryRecord.writeUint32(END_OF_CENTRAL_DIR_SIGNATURE);
7821
7943
  endOfdirectoryRecord.writeUint16(lastDiskNumber);
7822
7944
  endOfdirectoryRecord.writeUint16(diskNumber);
7823
- endOfdirectoryRecord.writeUint16(fileEntriesLength);
7945
+ endOfdirectoryRecord.writeUint16(diskFileEntriesLength);
7824
7946
  endOfdirectoryRecord.writeUint16(fileEntriesLength);
7825
7947
  endOfdirectoryRecord.writeUint32(directoryDataLength);
7826
7948
  endOfdirectoryRecord.writeUint32(directoryOffset);
@@ -7919,9 +8041,21 @@ async function copyZipData(zipWriter, reader, entries, directoryOffset) {
7919
8041
  async function copyData(zipWriter, reader, offset, size) {
7920
8042
  if (size > 0) {
7921
8043
  const { writer } = zipWriter;
7922
- await createReadable(reader, { offset, size }).pipeTo(writer.writable, { preventClose: true, preventAbort: true });
7923
- writer.size += size;
7924
- zipWriter.offset += size;
8044
+ let copiedLength = 0;
8045
+ try {
8046
+ await flushBufferedData(createReadable(reader, { offset, size }), writer, UNDEFINED_VALUE, chunkLength => copiedLength += chunkLength);
8047
+ } catch (error) {
8048
+ zipWriter.hasCorruptedEntries = true;
8049
+ try {
8050
+ error.corruptedEntry = true;
8051
+ } catch {
8052
+ // ignored
8053
+ }
8054
+ throw error;
8055
+ } finally {
8056
+ writer.size += copiedLength;
8057
+ zipWriter.offset += copiedLength;
8058
+ }
7925
8059
  }
7926
8060
  }
7927
8061
 
@@ -7946,6 +8080,12 @@ function getSplitZipSignatureArray() {
7946
8080
  return signatureArray;
7947
8081
  }
7948
8082
 
8083
+ async function writeSplitZipSignature(zipWriter, writer) {
8084
+ delete zipWriter.addSplitZipSignature;
8085
+ await writeData(writer, getSplitZipSignatureArray());
8086
+ zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
8087
+ }
8088
+
7949
8089
  async function writeData(writer, array) {
7950
8090
  const { writable } = writer;
7951
8091
  const streamWriter = writable.getWriter();