@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.
package/dist/zip-core.js CHANGED
@@ -101,6 +101,7 @@
101
101
  const FILE_ATTR_UNIX_SETGID_MASK = 0o2000;
102
102
  const FILE_ATTR_UNIX_STICKY_MASK = 0o1000;
103
103
 
104
+ const VERSION_STORE = 0x0A;
104
105
  const VERSION_DEFLATE = 0x14;
105
106
  const VERSION_ZIP64 = 0x2D;
106
107
  const VERSION_AES = 0x33;
@@ -4926,6 +4927,10 @@
4926
4927
  const commentEncoding = getOptionValue$1(zipReader, options, OPTION_COMMENT_ENCODING);
4927
4928
  const filenames = checkAmbiguity ? new Set() : UNDEFINED_VALUE;
4928
4929
  let duplicateFilename;
4930
+ const recoverWrappedFilesLength = !checkAmbiguity && !zip64EndOfDirectory;
4931
+ if (!filesLength && recoverWrappedFilesLength) {
4932
+ filesLength = getWrappedFilesLength(directoryView, directoryArray, offset);
4933
+ }
4929
4934
  for (let indexFile = 0; indexFile < filesLength; indexFile++) {
4930
4935
  const fileEntry = new ZipEntry(reader, zipReader.options);
4931
4936
  if (offset + CENTRAL_FILE_HEADER_LENGTH > directoryArray.length || getUint32$1(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE) {
@@ -5035,7 +5040,7 @@
5035
5040
  internalFileAttribute: fileEntry.internalFileAttributes,
5036
5041
  externalFileAttribute: fileEntry.externalFileAttributes,
5037
5042
  executable,
5038
- directory: modeIsDir || upperIsDir || (msDosCompatible && msdosAttributes.directory) || (fileEntry.filename.endsWith(DIRECTORY_SIGNATURE) && !fileEntry.uncompressedSize),
5043
+ directory: modeIsDir || upperIsDir || (msDosCompatible && msdosAttributes.directory) || fileEntry.filename.endsWith(DIRECTORY_SIGNATURE),
5039
5044
  zipCrypto: fileEntry.encrypted && !fileEntry.extraFieldAES
5040
5045
  });
5041
5046
  const entry = new Entry(fileEntry);
@@ -5048,6 +5053,9 @@
5048
5053
  return arrayBufferPromise;
5049
5054
  };
5050
5055
  offset = endOffset;
5056
+ if (indexFile == filesLength - 1 && recoverWrappedFilesLength) {
5057
+ filesLength += getWrappedFilesLength(directoryView, directoryArray, offset);
5058
+ }
5051
5059
  const { onprogress } = options;
5052
5060
  if (onprogress) {
5053
5061
  try {
@@ -5206,6 +5214,7 @@
5206
5214
  extraFieldLength,
5207
5215
  filenameLength
5208
5216
  } = localDirectory;
5217
+ const dataOffset = localDirectory.dataOffset = localHeaderOffset + HEADER_SIZE + filenameLength + extraFieldLength;
5209
5218
  const checkLocalDirectoryOption = getOptionValue$1(zipEntry, options, OPTION_CHECK_LOCAL_DIRECTORY);
5210
5219
  const entryStrictness = getStrictness(options, zipEntry.options);
5211
5220
  const checkLocalDirectory = getCheckLocalDirectory(checkLocalDirectoryOption, entryStrictness);
@@ -5259,7 +5268,6 @@
5259
5268
  throw new Error(ERR_ENCRYPTED);
5260
5269
  }
5261
5270
  }
5262
- const dataOffset = localHeaderOffset + HEADER_SIZE + filenameLength + extraFieldLength;
5263
5271
  if (dataOffset + compressedSize > reader.size) {
5264
5272
  throw new Error(ERR_ENTRY_DATA_OUT_OF_BOUNDS);
5265
5273
  }
@@ -5379,6 +5387,16 @@
5379
5387
  return false;
5380
5388
  }
5381
5389
 
5390
+ function getWrappedFilesLength(directoryView, directoryArray, offset) {
5391
+ let wrappedFilesLength = 0;
5392
+ while (offset + CENTRAL_FILE_HEADER_LENGTH <= directoryArray.length && getUint32$1(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE) {
5393
+ offset += CENTRAL_FILE_HEADER_LENGTH +
5394
+ getUint16$1(directoryView, offset + 28) + getUint16$1(directoryView, offset + 30) + getUint16$1(directoryView, offset + 32);
5395
+ wrappedFilesLength++;
5396
+ }
5397
+ return wrappedFilesLength % (MAX_16_BITS + 1) ? 0 : wrappedFilesLength;
5398
+ }
5399
+
5382
5400
  function readDigitalSignature(signatureRecordArray) {
5383
5401
  if (signatureRecordArray.length >= 6) {
5384
5402
  const signatureRecordView = getDataView(signatureRecordArray);
@@ -5551,10 +5569,11 @@
5551
5569
  const computedCrc32View = getDataView(new Uint8Array(4));
5552
5570
  computedCrc32View.setUint32(0, computedCrc32.get(), true);
5553
5571
  const nameCrc32 = getUint32$1(extraFieldView, 1);
5572
+ const version = getUint8(extraFieldView, 0);
5554
5573
  Object.assign(extraFieldUnicode, {
5555
- version: getUint8(extraFieldView, 0),
5574
+ version,
5556
5575
  [propertyName]: decodeText(extraFieldUnicode.data.subarray(5)),
5557
- valid: !fileEntry.bitFlag.languageEncodingFlag && nameCrc32 == getUint32$1(computedCrc32View, 0)
5576
+ valid: version == 1 && !fileEntry.bitFlag.languageEncodingFlag && nameCrc32 == getUint32$1(computedCrc32View, 0)
5558
5577
  });
5559
5578
  if (extraFieldUnicode.valid) {
5560
5579
  directory[propertyName] = extraFieldUnicode[propertyName];
@@ -6023,7 +6042,8 @@
6023
6042
 
6024
6043
  function getDate(timeRaw) {
6025
6044
  const date = (timeRaw & 0xffff0000) >> 16, time = timeRaw & MAX_16_BITS;
6026
- return new Date(1980 + ((date & 0xFE00) >> 9), ((date & 0x01E0) >> 5) - 1, date & 0x001F, (time & 0xF800) >> 11, (time & 0x07E0) >> 5, (time & 0x001F) * 2, 0);
6045
+ const result = new Date(1980 + ((date & 0xFE00) >> 9), ((date & 0x01E0) >> 5) - 1, date & 0x001F, (time & 0xF800) >> 11, (time & 0x07E0) >> 5, (time & 0x001F) * 2, 0);
6046
+ return result < MIN_DATE ? MIN_DATE : result;
6027
6047
  }
6028
6048
 
6029
6049
  function getDateNTFS(timeRaw) {
@@ -6176,153 +6196,150 @@
6176
6196
  offset: options[OPTION_OFFSET] === UNDEFINED_VALUE ? writer.size || writer.writable.size || 0 : options[OPTION_OFFSET],
6177
6197
  initialOffset: options[OPTION_OFFSET] === UNDEFINED_VALUE ? 0 : options[OPTION_OFFSET] - (writer.size || writer.writable.size || 0),
6178
6198
  pendingAddFileCalls: new Set(),
6199
+ pendingErrors: [],
6179
6200
  bufferedWrites: 0,
6180
6201
  lastFileEntry: UNDEFINED_VALUE
6181
6202
  });
6182
6203
  }
6183
6204
 
6184
- async prependZip(reader) {
6185
- if (this.filenames.size) {
6186
- throw new Error(ERR_ZIP_NOT_EMPTY);
6187
- }
6188
- reader = new GenericReader(reader);
6189
- await initStream(reader);
6190
- if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
6191
- reader = new BlobReader(await streamToBlob(reader.readable));
6192
- await initStream(reader);
6193
- }
6194
- const { ZipReader } = await Promise.resolve().then(function () { return zipReader; });
6195
- const zipReader$1 = new ZipReader(reader);
6196
- const entries = await zipReader$1.getEntries();
6197
- await zipReader$1.close();
6198
- await initStream(this.writer);
6199
- const { directoryOffset } = zipReader$1;
6200
- if (this.addSplitZipSignature) {
6201
- delete this.addSplitZipSignature;
6202
- if (!await startsWithSplitZipSignature(reader)) {
6203
- await writeData(this.writer, getSplitZipSignatureArray());
6204
- this.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
6205
- }
6206
- }
6207
- const entryPositions = await copyZipData(this, reader, entries, directoryOffset);
6208
- this.filenames = new Set(entries.map(entry => entry.filename));
6209
- this.fileEntries = new Map(entries.map(entry => {
6210
- const {
6211
- version,
6212
- rawLastModDate,
6213
- lastAccessDate,
6214
- creationDate,
6215
- rawFilename,
6216
- bitFlag,
6217
- encrypted,
6218
- uncompressedSize,
6219
- compressedSize,
6220
- extraFieldZip64
6221
- } = entry;
6222
- let {
6223
- compressionMethod,
6224
- rawExtraFieldZip64,
6225
- rawExtraFieldAES,
6226
- rawExtraFieldExtendedTimestamp,
6227
- rawExtraFieldNTFS,
6228
- rawExtraFieldUnix,
6229
- rawExtraField,
6230
- } = entry;
6231
- const { level, languageEncodingFlag, dataDescriptor } = bitFlag;
6232
- rawExtraFieldZip64 = rawExtraFieldZip64 || EMPTY_UINT8_ARRAY;
6233
- rawExtraFieldAES = rawExtraFieldAES || EMPTY_UINT8_ARRAY;
6234
- rawExtraFieldExtendedTimestamp = rawExtraFieldExtendedTimestamp || EMPTY_UINT8_ARRAY;
6235
- rawExtraFieldNTFS = rawExtraFieldNTFS || EMPTY_UINT8_ARRAY;
6236
- rawExtraFieldUnix = rawExtraFieldUnix || EMPTY_UINT8_ARRAY;
6237
- rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
6238
- if (entry.extraFieldAES) {
6239
- compressionMethod = COMPRESSION_METHOD_AES;
6240
- }
6241
- const extraFieldLength = getLength(rawExtraFieldZip64, rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawExtraFieldUnix, rawExtraField);
6242
- const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
6243
- const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
6244
- const bitFlagValue = (getBitFlag(level, languageEncodingFlag, dataDescriptor, encrypted, compressionMethod) & ~BITFLAG_LEVEL) | (level << 1);
6245
- const {
6246
- headerArray,
6247
- headerView
6248
- } = getHeaderArrayData({
6249
- version,
6250
- bitFlag: bitFlagValue,
6251
- compressionMethod,
6252
- uncompressedSize,
6253
- compressedSize,
6254
- rawLastModDate,
6255
- rawFilename,
6256
- zip64CompressedSize,
6257
- zip64UncompressedSize,
6258
- extraFieldLength
6259
- });
6260
- const { crc32 } = entry;
6261
- if (crc32 !== UNDEFINED_VALUE) {
6262
- setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
6263
- }
6264
- const { offset, diskNumberStart } = entryPositions.get(entry);
6265
- Object.assign(entry, {
6266
- zip64UncompressedSize,
6267
- zip64CompressedSize,
6268
- offset,
6269
- diskNumberStart,
6270
- zip64DiskNumberStart: false,
6271
- rawExtraFieldZip64,
6272
- rawExtraFieldAES,
6273
- rawExtraFieldExtendedTimestamp,
6274
- rawExtraFieldNTFS,
6275
- rawExtraFieldUnix,
6276
- rawExtraField,
6277
- extendedTimestamp: rawExtraFieldExtendedTimestamp.length > 0 || rawExtraFieldNTFS.length > 0,
6278
- extraFieldExtendedTimestampFlag: 0x1 + (lastAccessDate ? 0x2 : 0) + (creationDate ? 0x4 : 0),
6279
- headerArray,
6280
- headerView
6281
- });
6282
- return [entry.filename, entry];
6283
- }));
6205
+ prependZip(reader) {
6206
+ return watchPromiseError(this, prependZipEntries(this, reader));
6284
6207
  }
6285
6208
 
6286
- async add(name = "", reader, options = {}) {
6209
+ appendZip(reader) {
6210
+ return watchPromiseError(this, this.appendZipEntries(reader));
6211
+ }
6212
+
6213
+ async appendZipEntries(reader) {
6287
6214
  const zipWriter = this;
6288
- options = Object.assign({}, options);
6289
- const { pendingAddFileCalls } = zipWriter;
6290
- if (workers < getConfiguration().maxWorkers) {
6291
- workers++;
6292
- } else {
6293
- await new Promise(resolve => pendingEntries.push(resolve));
6215
+ const { pendingAddFileCalls, filenames, fileEntries } = zipWriter;
6216
+ while (pendingAddFileCalls.size) {
6217
+ await Promise.allSettled(Array.from(pendingAddFileCalls));
6294
6218
  }
6295
- let promiseAddFile;
6296
- let nameAdded;
6219
+ let resolveAppendZip;
6220
+ const promiseAppendZip = new Promise(resolve => resolveAppendZip = resolve);
6221
+ pendingAddFileCalls.add(promiseAppendZip);
6222
+ const appendedFilenames = [];
6223
+ let releaseLockWriter;
6297
6224
  try {
6298
- name = name.trim();
6299
- if (getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY) && !name.endsWith(DIRECTORY_SIGNATURE)) {
6300
- name += DIRECTORY_SIGNATURE;
6301
- }
6302
- if (zipWriter.filenames.has(name)) {
6303
- throw new Error(ERR_DUPLICATED_NAME);
6304
- }
6305
- zipWriter.filenames.add(name);
6306
- nameAdded = true;
6307
- promiseAddFile = addFile(zipWriter, name, reader, options);
6308
- pendingAddFileCalls.add(promiseAddFile);
6309
- return await promiseAddFile;
6310
- } catch (error) {
6311
- if (nameAdded) {
6312
- zipWriter.filenames.delete(name);
6225
+ reader = new GenericReader(reader);
6226
+ await initStream(reader);
6227
+ if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
6228
+ reader = new BlobReader(await streamToBlob(reader.readable));
6229
+ await initStream(reader);
6230
+ }
6231
+ const { ZipReader } = await Promise.resolve().then(function () { return zipReader; });
6232
+ const zipReader$1 = new ZipReader(reader);
6233
+ const entries = await zipReader$1.getEntries();
6234
+ await zipReader$1.close();
6235
+ await initStream(zipWriter.writer);
6236
+ const { directoryOffset } = zipReader$1;
6237
+ entries.forEach(({ filename }) => {
6238
+ if (filenames.has(filename)) {
6239
+ throw new Error(ERR_DUPLICATED_NAME);
6240
+ }
6241
+ filenames.add(filename);
6242
+ appendedFilenames.push(filename);
6243
+ });
6244
+ zipWriter.writerLocked = true;
6245
+ const { lockWriter } = zipWriter;
6246
+ zipWriter.lockWriter = new Promise(resolve => releaseLockWriter = () => {
6247
+ zipWriter.writerLocked = false;
6248
+ resolve();
6249
+ });
6250
+ await lockWriter;
6251
+ if (zipWriter.addSplitZipSignature) {
6252
+ delete zipWriter.addSplitZipSignature;
6253
+ if (!await startsWithSplitZipSignature(reader)) {
6254
+ await writeData(zipWriter.writer, getSplitZipSignatureArray());
6255
+ zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
6256
+ }
6313
6257
  }
6258
+ const entryPositions = await copyZipData(zipWriter, reader, entries, directoryOffset);
6259
+ entries.forEach(entry => {
6260
+ const {
6261
+ version,
6262
+ rawLastModDate,
6263
+ rawFilename,
6264
+ bitFlag,
6265
+ encrypted,
6266
+ uncompressedSize,
6267
+ compressedSize,
6268
+ extraFieldZip64
6269
+ } = entry;
6270
+ let {
6271
+ compressionMethod,
6272
+ rawExtraField,
6273
+ } = entry;
6274
+ const { level, languageEncodingFlag, dataDescriptor } = bitFlag;
6275
+ rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
6276
+ if (entry.extraFieldAES) {
6277
+ compressionMethod = COMPRESSION_METHOD_AES;
6278
+ }
6279
+ const extraFieldLength = getLength(rawExtraField);
6280
+ const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
6281
+ const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
6282
+ const bitFlagValue = (getBitFlag(level, languageEncodingFlag, dataDescriptor, encrypted, compressionMethod) & ~BITFLAG_LEVEL) | (level << 1);
6283
+ const {
6284
+ headerArray,
6285
+ headerView
6286
+ } = getHeaderArrayData({
6287
+ version,
6288
+ bitFlag: bitFlagValue,
6289
+ compressionMethod,
6290
+ uncompressedSize,
6291
+ compressedSize,
6292
+ rawLastModDate,
6293
+ rawFilename,
6294
+ zip64CompressedSize,
6295
+ zip64UncompressedSize,
6296
+ extraFieldLength
6297
+ });
6298
+ const { crc32 } = entry;
6299
+ if (crc32 !== UNDEFINED_VALUE) {
6300
+ setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
6301
+ }
6302
+ const { offset, diskNumberStart } = entryPositions.get(entry);
6303
+ Object.assign(entry, {
6304
+ zip64UncompressedSize,
6305
+ zip64CompressedSize,
6306
+ offset,
6307
+ diskNumberStart,
6308
+ zip64DiskNumberStart: false,
6309
+ rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
6310
+ rawExtraFieldAES: EMPTY_UINT8_ARRAY,
6311
+ rawExtraFieldExtendedTimestamp: EMPTY_UINT8_ARRAY,
6312
+ rawExtraFieldNTFS: EMPTY_UINT8_ARRAY,
6313
+ rawExtraFieldUnix: EMPTY_UINT8_ARRAY,
6314
+ rawExtraField,
6315
+ extendedTimestamp: false,
6316
+ headerArray,
6317
+ headerView
6318
+ });
6319
+ fileEntries.set(entry.filename, entry);
6320
+ });
6321
+ } catch (error) {
6322
+ appendedFilenames.forEach(filename => filenames.delete(filename));
6314
6323
  throw error;
6315
6324
  } finally {
6316
- pendingAddFileCalls.delete(promiseAddFile);
6317
- const pendingEntry = pendingEntries.shift();
6318
- if (pendingEntry) {
6319
- pendingEntry();
6320
- } else {
6321
- workers--;
6325
+ resolveAppendZip();
6326
+ pendingAddFileCalls.delete(promiseAppendZip);
6327
+ if (releaseLockWriter) {
6328
+ releaseLockWriter();
6322
6329
  }
6323
6330
  }
6324
6331
  }
6325
6332
 
6333
+ add(name = "", reader, options = {}) {
6334
+ const zipWriter = this;
6335
+ const { pendingAddFileCalls } = zipWriter;
6336
+ const promiseAddFile = addFileEntry(zipWriter, name, reader, options);
6337
+ pendingAddFileCalls.add(promiseAddFile);
6338
+ const deletePendingAddFileCall = () => pendingAddFileCalls.delete(promiseAddFile);
6339
+ Promise.prototype.then.call(promiseAddFile, deletePendingAddFileCall, deletePendingAddFileCall);
6340
+ return watchPromiseError(zipWriter, promiseAddFile);
6341
+ }
6342
+
6326
6343
  remove(entry) {
6327
6344
  const { filenames, fileEntries } = this;
6328
6345
  // deno-lint-ignore valid-typeof
@@ -6353,6 +6370,19 @@
6353
6370
  while (pendingAddFileCalls.size) {
6354
6371
  await Promise.allSettled(Array.from(pendingAddFileCalls));
6355
6372
  }
6373
+ await Promise.allSettled(zipWriter.pendingErrors.map(watcher => watcher.recorded));
6374
+ const unobservedWatchers = zipWriter.pendingErrors.filter(watcher => watcher.error && !watcher.observed);
6375
+ if (unobservedWatchers.length) {
6376
+ const unobservedErrors = unobservedWatchers.map(watcher => watcher.error);
6377
+ unobservedWatchers.forEach(watcher => watcher.observed = true);
6378
+ const [error] = unobservedErrors;
6379
+ try {
6380
+ error.entryErrors = unobservedErrors;
6381
+ } catch {
6382
+ // ignored
6383
+ }
6384
+ throw error;
6385
+ }
6356
6386
  await closeFile(zipWriter, comment, options);
6357
6387
  const preventClose = !ownsWritable(writer) && getOptionValue(zipWriter, options, OPTION_PREVENT_CLOSE);
6358
6388
  if (!preventClose) {
@@ -6387,11 +6417,7 @@
6387
6417
  try {
6388
6418
  await zipWriter.close();
6389
6419
  } catch (error) {
6390
- try {
6391
- await zipWriter.writer.writable.abort(error);
6392
- } catch {
6393
- // ignored
6394
- }
6420
+ await abortWritable(zipWriter, error);
6395
6421
  }
6396
6422
  }
6397
6423
  }
@@ -6408,8 +6434,90 @@
6408
6434
  }
6409
6435
 
6410
6436
  async close(comment = UNDEFINED_VALUE, options = {}) {
6411
- await Promise.all(Array.from(this.pendingAddFileCalls));
6412
- return this.zipWriter.close(comment, options);
6437
+ const { zipWriter } = this;
6438
+ const results = await Promise.allSettled(Array.from(this.pendingAddFileCalls));
6439
+ const entryErrors = results.filter(result => result.status == "rejected").map(result => result.reason);
6440
+ if (entryErrors.length) {
6441
+ const [error] = entryErrors;
6442
+ try {
6443
+ error.entryErrors = entryErrors;
6444
+ } catch {
6445
+ // ignored
6446
+ }
6447
+ await abortWritable(zipWriter, error);
6448
+ throw error;
6449
+ }
6450
+ try {
6451
+ return await zipWriter.close(comment, options);
6452
+ } catch (error) {
6453
+ await abortWritable(zipWriter, error);
6454
+ throw error;
6455
+ }
6456
+ }
6457
+ }
6458
+
6459
+ class WatchedPromise extends Promise {
6460
+
6461
+ then(onFulfilled, onRejected) {
6462
+ const { watcher } = this;
6463
+ if (watcher) {
6464
+ watcher.observed = true;
6465
+ }
6466
+ return super.then(onFulfilled, onRejected);
6467
+ }
6468
+ }
6469
+
6470
+ function watchPromiseError(zipWriter, promise) {
6471
+ const watchedPromise = new WatchedPromise((resolve, reject) => Promise.prototype.then.call(promise, resolve, reject));
6472
+ const watcher = {};
6473
+ watchedPromise.watcher = watcher;
6474
+ watcher.recorded = Promise.prototype.then.call(watchedPromise, UNDEFINED_VALUE, error => watcher.error = error);
6475
+ zipWriter.pendingErrors.push(watcher);
6476
+ return watchedPromise;
6477
+ }
6478
+
6479
+ async function prependZipEntries(zipWriter, reader) {
6480
+ if (zipWriter.filenames.size) {
6481
+ throw new Error(ERR_ZIP_NOT_EMPTY);
6482
+ }
6483
+ await zipWriter.appendZipEntries(reader);
6484
+ }
6485
+
6486
+ async function addFileEntry(zipWriter, name, reader, options) {
6487
+ options = Object.assign({}, options);
6488
+ name = name.trim();
6489
+ if (getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY) && !name.endsWith(DIRECTORY_SIGNATURE)) {
6490
+ name += DIRECTORY_SIGNATURE;
6491
+ }
6492
+ if (zipWriter.filenames.has(name)) {
6493
+ throw new Error(ERR_DUPLICATED_NAME);
6494
+ }
6495
+ zipWriter.filenames.add(name);
6496
+ if (workers < getConfiguration().maxWorkers) {
6497
+ workers++;
6498
+ } else {
6499
+ await new Promise(resolve => pendingEntries.push(resolve));
6500
+ }
6501
+ try {
6502
+ return await addFile(zipWriter, name, reader, options);
6503
+ } catch (error) {
6504
+ zipWriter.filenames.delete(name);
6505
+ throw error;
6506
+ } finally {
6507
+ const pendingEntry = pendingEntries.shift();
6508
+ if (pendingEntry) {
6509
+ pendingEntry();
6510
+ } else {
6511
+ workers--;
6512
+ }
6513
+ }
6514
+ }
6515
+
6516
+ async function abortWritable(zipWriter, error) {
6517
+ try {
6518
+ await zipWriter.writer.writable.abort(error);
6519
+ } catch {
6520
+ // ignored
6413
6521
  }
6414
6522
  }
6415
6523
 
@@ -6612,11 +6720,12 @@
6612
6720
  if (getLength(rawComment) > MAX_16_BITS) {
6613
6721
  throw new Error(ERR_INVALID_ENTRY_COMMENT);
6614
6722
  }
6615
- const version = getOptionValue(zipWriter, options, PROPERTY_NAME_VERSION, VERSION_DEFLATE);
6616
- if (version > MAX_16_BITS) {
6723
+ const version = getOptionValue(zipWriter, options, PROPERTY_NAME_VERSION);
6724
+ if (version !== UNDEFINED_VALUE && version > MAX_16_BITS) {
6617
6725
  throw new Error(ERR_INVALID_VERSION);
6618
6726
  }
6619
6727
  const lastModDate = getDateOptionValue(zipWriter, options, PROPERTY_NAME_LAST_MODIFICATION_DATE, new Date());
6728
+ const rawLastModDate = getOptionValue(zipWriter, options, PROPERTY_NAME_RAW_LAST_MODIFICATION_DATE);
6620
6729
  const lastAccessDate = getDateOptionValue(zipWriter, options, PROPERTY_NAME_LAST_ACCESS_DATE);
6621
6730
  const creationDate = getDateOptionValue(zipWriter, options, PROPERTY_NAME_CREATION_DATE);
6622
6731
  const internalFileAttributes = getAliasedOptionValue(zipWriter, options, PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTES, PROPERTY_NAME_DEPRECATED_INTERNAL_FILE_ATTRIBUTES, 0);
@@ -6660,7 +6769,7 @@
6660
6769
  if (bufferedWrite && dataDescriptor === UNDEFINED_VALUE) {
6661
6770
  dataDescriptor = false;
6662
6771
  }
6663
- if (dataDescriptor === UNDEFINED_VALUE || zipCrypto) {
6772
+ if (dataDescriptor === UNDEFINED_VALUE || (zipCrypto && !passThrough)) {
6664
6773
  dataDescriptor = true;
6665
6774
  }
6666
6775
  if (level !== UNDEFINED_VALUE && level != 6) {
@@ -6679,6 +6788,7 @@
6679
6788
  rawComment,
6680
6789
  version,
6681
6790
  lastModDate,
6791
+ rawLastModDate,
6682
6792
  lastAccessDate,
6683
6793
  creationDate,
6684
6794
  internalFileAttributes,
@@ -6875,10 +6985,8 @@
6875
6985
  }
6876
6986
  await initStream(fileWriter);
6877
6987
  const diskOffset = getDiskOffset(writer);
6878
- if (zipWriter.addSplitZipSignature) {
6879
- delete zipWriter.addSplitZipSignature;
6880
- await writeData(writer, getSplitZipSignatureArray());
6881
- zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
6988
+ if (zipWriter.addSplitZipSignature && !bufferedWrite) {
6989
+ await writeSplitZipSignature(zipWriter, writer);
6882
6990
  }
6883
6991
  if (usdz && !bufferedWrite) {
6884
6992
  appendExtraFieldUSDZ(entryInfo, zipWriter.offset - diskOffset);
@@ -6905,6 +7013,9 @@
6905
7013
  if (bufferedWrite) {
6906
7014
  await Promise.all([fileWriter.writable.getWriter().close(), lockPreviousFileEntry]);
6907
7015
  await requestLockWriter();
7016
+ if (zipWriter.addSplitZipSignature) {
7017
+ await writeSplitZipSignature(zipWriter, writer);
7018
+ }
6908
7019
  writingBufferedEntryData = true;
6909
7020
  writerSizeBeforeEntry = writer.size;
6910
7021
  await skipDiskIfNeeded();
@@ -7176,6 +7287,7 @@
7176
7287
  const {
7177
7288
  rawFilename,
7178
7289
  lastModDate,
7290
+ rawLastModDate: rawLastModDateOption,
7179
7291
  lastAccessDate,
7180
7292
  creationDate,
7181
7293
  level,
@@ -7309,6 +7421,9 @@
7309
7421
  if (compressionMethod === UNDEFINED_VALUE) {
7310
7422
  compressionMethod = compressed ? COMPRESSION_METHOD_DEFLATE : COMPRESSION_METHOD_STORE;
7311
7423
  }
7424
+ if (version === UNDEFINED_VALUE) {
7425
+ version = compressionMethod == COMPRESSION_METHOD_STORE && !directory && !encrypted ? VERSION_STORE : VERSION_DEFLATE;
7426
+ }
7312
7427
  const { codecVersionNeeded } = options;
7313
7428
  if (compressed && codecVersionNeeded !== UNDEFINED_VALUE) {
7314
7429
  version = version > codecVersionNeeded ? version : codecVersionNeeded;
@@ -7341,6 +7456,7 @@
7341
7456
  compressionMethod,
7342
7457
  uncompressedSize,
7343
7458
  lastModDate: dosLastModDate < MIN_DATE ? MIN_DATE : dosLastModDate > MAX_DATE ? MAX_DATE : dosLastModDate,
7459
+ rawLastModDate: rawLastModDateOption,
7344
7460
  rawFilename,
7345
7461
  zip64CompressedSize,
7346
7462
  zip64UncompressedSize,
@@ -7547,9 +7663,9 @@
7547
7663
 
7548
7664
  async function closeFile(zipWriter, comment, options) {
7549
7665
  const directoryDataLength = createDirectoryRecords(zipWriter.fileEntries);
7550
- const { directoryStart, directoryArray } = await writeDirectoryRecords(zipWriter, directoryDataLength, options);
7666
+ const { directoryStart, directoryEnd, directoryArray } = await writeDirectoryRecords(zipWriter, directoryDataLength, options);
7551
7667
  const signatureLength = await writeDigitalSignatureRecord(zipWriter, directoryArray, options);
7552
- await writeEndOfDirectoryRecord(zipWriter, comment, options, { directoryStart, directoryDataLength, signatureLength });
7668
+ await writeEndOfDirectoryRecord(zipWriter, comment, options, { directoryStart, directoryEnd, directoryDataLength, signatureLength });
7553
7669
  }
7554
7670
 
7555
7671
  function createDirectoryRecords(files) {
@@ -7633,6 +7749,7 @@
7633
7749
  let directoryDiskOffset = 0;
7634
7750
  let directoryStartDiskNumber = getDiskNumber(writer);
7635
7751
  let directoryStartDiskOffset = getDiskOffset(writer);
7752
+ let directoryEndDiskEntriesLength = 0;
7636
7753
  for (const [indexFileEntry, fileEntry] of Array.from(fileEntries.values()).entries()) {
7637
7754
  const {
7638
7755
  offset: fileEntryOffset,
@@ -7662,6 +7779,7 @@
7662
7779
  if (exceedsAvailableSize(writer, offset + directoryRecordLength - directoryDiskOffset)) {
7663
7780
  await writeData(writer, directoryArray.slice(directoryDiskOffset, offset));
7664
7781
  directoryDiskOffset = offset;
7782
+ directoryEndDiskEntriesLength = 0;
7665
7783
  await writer.closeDisk();
7666
7784
  }
7667
7785
  if (indexFileEntry == 0) {
@@ -7697,6 +7815,7 @@
7697
7815
  directoryRecord.writeBytes(rawComment);
7698
7816
  arraySet(directoryArray, directoryRecord.array, offset);
7699
7817
  offset += directoryRecordLength;
7818
+ directoryEndDiskEntriesLength++;
7700
7819
  if (options.onprogress) {
7701
7820
  try {
7702
7821
  await options.onprogress(indexFileEntry + 1, fileEntries.size, new Entry(fileEntry));
@@ -7708,6 +7827,7 @@
7708
7827
  await writeData(writer, directoryDiskOffset ? directoryArray.slice(directoryDiskOffset) : directoryArray);
7709
7828
  return {
7710
7829
  directoryStart: { diskNumber: directoryStartDiskNumber, diskOffset: directoryStartDiskOffset },
7830
+ directoryEnd: { diskNumber: getDiskNumber(writer), entriesLength: directoryEndDiskEntriesLength },
7711
7831
  directoryArray
7712
7832
  };
7713
7833
  }
@@ -7736,16 +7856,20 @@
7736
7856
 
7737
7857
  async function writeEndOfDirectoryRecord(zipWriter, comment, options, cdInfo) {
7738
7858
  const { writer } = zipWriter;
7739
- const { directoryStart, signatureLength } = cdInfo;
7859
+ const { directoryStart, directoryEnd, signatureLength } = cdInfo;
7740
7860
  let { directoryDataLength } = cdInfo;
7741
7861
  let fileEntriesLength = zipWriter.fileEntries.size;
7742
7862
  let diskNumber = directoryStart.diskNumber;
7743
7863
  let directoryOffset = getSegmentOffset(zipWriter, directoryStart);
7864
+ const commentLength = getLength(comment);
7865
+ if (commentLength > MAX_16_BITS) {
7866
+ throw new Error(ERR_INVALID_COMMENT);
7867
+ }
7868
+ let zip64 = getOptionValue(zipWriter, options, PROPERTY_NAME_ZIP64);
7744
7869
  let lastDiskNumber = getDiskNumber(writer);
7745
- if (exceedsAvailableSize(writer, END_OF_CENTRAL_DIR_LENGTH)) {
7870
+ if (exceedsAvailableSize(writer, (zip64 ? ZIP64_END_OF_CENTRAL_DIR_TOTAL_LENGTH : END_OF_CENTRAL_DIR_LENGTH) + commentLength)) {
7746
7871
  lastDiskNumber++;
7747
7872
  }
7748
- let zip64 = getOptionValue(zipWriter, options, PROPERTY_NAME_ZIP64);
7749
7873
  if (directoryOffset >= MAX_32_BITS || directoryDataLength >= MAX_32_BITS || fileEntriesLength >= MAX_16_BITS || lastDiskNumber >= MAX_16_BITS) {
7750
7874
  if (zip64 === false) {
7751
7875
  throw new Error(ERR_UNSUPPORTED_FORMAT);
@@ -7753,15 +7877,12 @@
7753
7877
  zip64 = true;
7754
7878
  }
7755
7879
  }
7756
- const commentLength = getLength(comment);
7757
- if (commentLength > MAX_16_BITS) {
7758
- throw new Error(ERR_INVALID_COMMENT);
7759
- }
7760
7880
  const endOfdirectoryRecord = createRecordWriter(zip64 ? ZIP64_END_OF_CENTRAL_DIR_TOTAL_LENGTH : END_OF_CENTRAL_DIR_LENGTH);
7761
7881
  if (exceedsAvailableSize(writer, getLength(endOfdirectoryRecord.array) + commentLength)) {
7762
7882
  await writer.closeDisk();
7763
7883
  }
7764
7884
  lastDiskNumber = getDiskNumber(writer);
7885
+ let diskFileEntriesLength = lastDiskNumber == directoryEnd.diskNumber ? directoryEnd.entriesLength : 0;
7765
7886
  if (zip64) {
7766
7887
  endOfdirectoryRecord.writeUint32(ZIP64_END_OF_CENTRAL_DIR_SIGNATURE);
7767
7888
  endOfdirectoryRecord.writeUint64(44);
@@ -7769,7 +7890,7 @@
7769
7890
  endOfdirectoryRecord.writeUint16(45);
7770
7891
  endOfdirectoryRecord.writeUint32(lastDiskNumber);
7771
7892
  endOfdirectoryRecord.writeUint32(diskNumber);
7772
- endOfdirectoryRecord.writeUint64(fileEntriesLength);
7893
+ endOfdirectoryRecord.writeUint64(diskFileEntriesLength);
7773
7894
  endOfdirectoryRecord.writeUint64(fileEntriesLength);
7774
7895
  endOfdirectoryRecord.writeUint64(directoryDataLength);
7775
7896
  endOfdirectoryRecord.writeUint64(directoryOffset);
@@ -7782,6 +7903,7 @@
7782
7903
  lastDiskNumber = MAX_16_BITS;
7783
7904
  diskNumber = MAX_16_BITS;
7784
7905
  }
7906
+ diskFileEntriesLength = MAX_16_BITS;
7785
7907
  fileEntriesLength = MAX_16_BITS;
7786
7908
  directoryOffset = MAX_32_BITS;
7787
7909
  directoryDataLength = MAX_32_BITS;
@@ -7789,7 +7911,7 @@
7789
7911
  endOfdirectoryRecord.writeUint32(END_OF_CENTRAL_DIR_SIGNATURE);
7790
7912
  endOfdirectoryRecord.writeUint16(lastDiskNumber);
7791
7913
  endOfdirectoryRecord.writeUint16(diskNumber);
7792
- endOfdirectoryRecord.writeUint16(fileEntriesLength);
7914
+ endOfdirectoryRecord.writeUint16(diskFileEntriesLength);
7793
7915
  endOfdirectoryRecord.writeUint16(fileEntriesLength);
7794
7916
  endOfdirectoryRecord.writeUint32(directoryDataLength);
7795
7917
  endOfdirectoryRecord.writeUint32(directoryOffset);
@@ -7888,9 +8010,21 @@
7888
8010
  async function copyData(zipWriter, reader, offset, size) {
7889
8011
  if (size > 0) {
7890
8012
  const { writer } = zipWriter;
7891
- await createReadable(reader, { offset, size }).pipeTo(writer.writable, { preventClose: true, preventAbort: true });
7892
- writer.size += size;
7893
- zipWriter.offset += size;
8013
+ let copiedLength = 0;
8014
+ try {
8015
+ await flushBufferedData(createReadable(reader, { offset, size }), writer, UNDEFINED_VALUE, chunkLength => copiedLength += chunkLength);
8016
+ } catch (error) {
8017
+ zipWriter.hasCorruptedEntries = true;
8018
+ try {
8019
+ error.corruptedEntry = true;
8020
+ } catch {
8021
+ // ignored
8022
+ }
8023
+ throw error;
8024
+ } finally {
8025
+ writer.size += copiedLength;
8026
+ zipWriter.offset += copiedLength;
8027
+ }
7894
8028
  }
7895
8029
  }
7896
8030
 
@@ -7915,6 +8049,12 @@
7915
8049
  return signatureArray;
7916
8050
  }
7917
8051
 
8052
+ async function writeSplitZipSignature(zipWriter, writer) {
8053
+ delete zipWriter.addSplitZipSignature;
8054
+ await writeData(writer, getSplitZipSignatureArray());
8055
+ zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
8056
+ }
8057
+
7918
8058
  async function writeData(writer, array) {
7919
8059
  const { writable } = writer;
7920
8060
  const streamWriter = writable.getWriter();