@zip.js/zip.js 2.8.58 → 2.8.60

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.
Files changed (45) hide show
  1. package/deno.json +2 -2
  2. package/dist/zip-core-external.js +295 -57
  3. package/dist/zip-core-external.min.js +1 -1
  4. package/dist/zip-core.js +286 -40
  5. package/dist/zip-core.min.js +1 -1
  6. package/dist/zip-fs-core-external.js +295 -58
  7. package/dist/zip-fs-core-external.min.js +1 -1
  8. package/dist/zip-fs-core.js +5867 -4360
  9. package/dist/zip-fs-core.min.js +1 -1
  10. package/dist/zip-fs-external.js +295 -58
  11. package/dist/zip-fs-external.min.js +1 -1
  12. package/dist/zip-fs-native.js +288 -43
  13. package/dist/zip-fs-native.min.js +1 -1
  14. package/dist/zip-fs.js +313 -58
  15. package/dist/zip-fs.min.js +1 -1
  16. package/dist/zip-legacy.js +288 -42
  17. package/dist/zip-legacy.min.js +1 -1
  18. package/dist/zip-native.js +288 -42
  19. package/dist/zip-native.min.js +1 -1
  20. package/dist/zip-web-worker-native.js +1 -1
  21. package/dist/zip-web-worker.js +1 -1
  22. package/dist/zip.js +313 -57
  23. package/dist/zip.min.js +1 -1
  24. package/index-native.cjs +288 -43
  25. package/index-native.min.js +1 -1
  26. package/index.cjs +313 -58
  27. package/index.d.cts +318 -2
  28. package/index.d.ts +318 -2
  29. package/index.min.js +1 -1
  30. package/lib/core/codec-registry.js +5 -0
  31. package/lib/core/compression-methods.js +93 -0
  32. package/lib/core/constants.js +2 -0
  33. package/lib/core/options.js +2 -0
  34. package/lib/core/streams/zip-entry-stream.js +7 -3
  35. package/lib/core/streams/zlib-wasm/zlib-streams.js +18 -8
  36. package/lib/core/version.js +31 -0
  37. package/lib/core/web-worker-inline-native.js +1 -1
  38. package/lib/core/web-worker-inline-wasm.js +1 -1
  39. package/lib/core/zip-reader.js +143 -33
  40. package/lib/core/zip-writer.js +14 -6
  41. package/lib/zip-core-base.js +7 -0
  42. package/lib/zip-core-reader.js +16 -1
  43. package/lib/zip-fs-core.js +32 -0
  44. package/package.json +3 -3
  45. package/sync-versions.js +26 -0
@@ -68,6 +68,7 @@ import {
68
68
  ZIP64_END_OF_CENTRAL_DIR_LENGTH,
69
69
  BITFLAG_ENCRYPTED,
70
70
  BITFLAG_STRONG_ENCRYPTION,
71
+ BITFLAG_COMPRESSED_PATCHED_DATA,
71
72
  BITFLAG_MASKED_LOCAL_HEADERS,
72
73
  BITFLAG_LEVEL,
73
74
  BITFLAG_DATA_DESCRIPTOR,
@@ -196,6 +197,21 @@ const ERR_INVALID_STRICTNESS = "Invalid strictness (must be 'strict', 'balanced'
196
197
  const ERR_INVALID_FILENAME_VALIDATION = "Invalid filenameValidation (must be 'strict', 'balanced' or 'tolerant')";
197
198
  const ERR_INVALID_MAX_APPENDED_DATA_SIZE = "Invalid maxAppendedDataSize (must be a number greater than or equal to 0)";
198
199
  const ERR_UNSUPPORTED_UINT64 = "64-bit value exceeds Number.MAX_SAFE_INTEGER";
200
+ const WARNING_UNSORTED_CENTRAL_DIRECTORY = "unsorted central directory";
201
+ const WARNING_UNKNOWN_VERSION = "unknown version needed to extract";
202
+ const WARNING_COMPRESSED_PATCHED_DATA = "compressed patched data";
203
+ const WARNING_MALFORMED_EXTRA_FIELD = "malformed extra field";
204
+ const WARNING_UNKNOWN_ZIP64_EXTENSIBLE_DATA = "unknown zip64 extensible data";
205
+ const WARNING_WRAPPED_ENTRIES_COUNT = "wrapped entries count";
206
+ const WARNING_APPENDED_DATA = "appended data";
207
+ const WARNING_PREPENDED_DATA = "prepended data";
208
+ const WARNING_TRAILING_CENTRAL_DIRECTORY_DATA = "trailing central directory data";
209
+ const WARNING_DUPLICATE_FILENAME = "duplicate filename";
210
+ const WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY = "mismatched zip64 end of central directory record";
211
+ const WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG = "mismatched local file header (general purpose bit flag)";
212
+ const WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD = "mismatched local file header (compression method)";
213
+ const WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES = "mismatched local file header (crc32 or sizes)";
214
+ const MAX_KNOWN_VERSION = 63;
199
215
  const DRIVE_LETTER_REGEXP = /^[a-zA-Z]:/;
200
216
  const CHARSET_UTF8 = "utf-8";
201
217
  const PROPERTY_NAME_UTF8_SUFFIX = "UTF8";
@@ -246,6 +262,7 @@ class ZipReader {
246
262
  if (reader.size < END_OF_CENTRAL_DIR_LENGTH) {
247
263
  throw new Error(ERR_BAD_FORMAT);
248
264
  }
265
+ const warnings = zipReader.warnings = [];
249
266
  const strictness = getStrictness(options, zipReader.options);
250
267
  const checkAmbiguity = strictness == STRICTNESS_STRICT;
251
268
  const rejectAmbiguousEndOfDirectory = strictness != STRICTNESS_TOLERANT;
@@ -269,8 +286,12 @@ class ZipReader {
269
286
  const commentOffset = endOfDirectoryInfo.offset;
270
287
  const commentLength = getUint16(endOfDirectoryView, 20);
271
288
  const appendedDataOffset = commentOffset + END_OF_CENTRAL_DIR_LENGTH + commentLength;
272
- if (reader.size - appendedDataOffset > maxAppendedDataSize) {
273
- throwAmbiguousArchive("appended data");
289
+ const appendedDataLength = reader.size - appendedDataOffset;
290
+ if (appendedDataLength > maxAppendedDataSize) {
291
+ throwAmbiguousArchive(WARNING_APPENDED_DATA);
292
+ }
293
+ if (appendedDataLength > 0) {
294
+ addWarning(warnings, WARNING_APPENDED_DATA);
274
295
  }
275
296
  let lastDiskNumber = getUint16(endOfDirectoryView, 4);
276
297
  const expectedLastDiskNumber = reader.lastDiskNumber || 0;
@@ -324,23 +345,23 @@ class ZipReader {
324
345
  }
325
346
  if (lastDiskNumber == MAX_16_BITS) {
326
347
  lastDiskNumber = getUint32(endOfDirectoryView, 16);
327
- } else if (checkAmbiguity && lastDiskNumber != getUint32(endOfDirectoryView, 16)) {
328
- throwAmbiguousArchive("mismatched zip64 end of central directory record");
348
+ } else if (lastDiskNumber != getUint32(endOfDirectoryView, 16)) {
349
+ reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY);
329
350
  }
330
351
  if (diskNumber == MAX_16_BITS) {
331
352
  diskNumber = getUint32(endOfDirectoryView, 20);
332
- } else if (checkAmbiguity && diskNumber != getUint32(endOfDirectoryView, 20)) {
333
- throwAmbiguousArchive("mismatched zip64 end of central directory record");
353
+ } else if (diskNumber != getUint32(endOfDirectoryView, 20)) {
354
+ reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY);
334
355
  }
335
356
  if (filesLength == MAX_16_BITS) {
336
357
  filesLength = getBigUint64(endOfDirectoryView, 32);
337
- } else if (checkAmbiguity && filesLength != getBigUint64(endOfDirectoryView, 32)) {
338
- throwAmbiguousArchive("mismatched zip64 end of central directory record");
358
+ } else if (filesLength != getBigUint64(endOfDirectoryView, 32)) {
359
+ reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY);
339
360
  }
340
361
  if (directoryDataLength == MAX_32_BITS) {
341
362
  directoryDataLength = getBigUint64(endOfDirectoryView, 40);
342
- } else if (checkAmbiguity && directoryDataLength != getBigUint64(endOfDirectoryView, 40)) {
343
- throwAmbiguousArchive("mismatched zip64 end of central directory record");
363
+ } else if (directoryDataLength != getBigUint64(endOfDirectoryView, 40)) {
364
+ reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY);
344
365
  }
345
366
  directoryDataOffset = getDiskOffset(reader, diskNumber) + getBigUint64(endOfDirectoryView, 48) + prependedDataLength;
346
367
  }
@@ -409,14 +430,22 @@ class ZipReader {
409
430
  declaredDirectoryDataLength = directoryArray.length;
410
431
  decryptedDirectory = true;
411
432
  }
433
+ if (directoryEncryptionInfo && !decryptedDirectory &&
434
+ (directoryArray.length < 4 || getUint32(directoryView, 0) == CENTRAL_FILE_HEADER_SIGNATURE)) {
435
+ addWarning(warnings, WARNING_UNKNOWN_ZIP64_EXTENSIBLE_DATA);
436
+ }
412
437
  startOffset = directoryDataOffset;
413
438
  const filenameEncoding = getOptionValue(zipReader, options, OPTION_FILENAME_ENCODING);
414
439
  const commentEncoding = getOptionValue(zipReader, options, OPTION_COMMENT_ENCODING);
415
- const filenames = checkAmbiguity ? new Set() : UNDEFINED_VALUE;
440
+ const filenames = new Set();
416
441
  let duplicateFilename;
442
+ let previousEntryPosition = -1;
417
443
  const recoverWrappedFilesLength = !checkAmbiguity && !zip64EndOfDirectory;
418
444
  if (!filesLength && recoverWrappedFilesLength) {
419
445
  filesLength = getWrappedFilesLength(directoryView, directoryArray, offset);
446
+ if (filesLength) {
447
+ addWarning(warnings, WARNING_WRAPPED_ENTRIES_COUNT);
448
+ }
420
449
  }
421
450
  for (let indexFile = 0; indexFile < filesLength; indexFile++) {
422
451
  const fileEntry = new ZipEntry(reader, zipReader.options);
@@ -495,15 +524,26 @@ class ZipReader {
495
524
  filename,
496
525
  comment
497
526
  });
498
- readCommonFooter(fileEntry, fileEntry, directoryView, offset + 6);
527
+ if (readCommonFooter(fileEntry, fileEntry, directoryView, offset + 6)) {
528
+ addWarning(warnings, WARNING_MALFORMED_EXTRA_FIELD, filename);
529
+ }
499
530
  fileEntry.offset += prependedDataLength;
500
- startOffset = Math.min(getDiskOffset(reader, fileEntry.diskNumberStart) + fileEntry.offset, startOffset);
501
- if (checkAmbiguity) {
502
- if (filenames.has(fileEntry.filename)) {
503
- duplicateFilename = true;
504
- }
505
- filenames.add(fileEntry.filename);
531
+ const entryPosition = getDiskOffset(reader, fileEntry.diskNumberStart) + fileEntry.offset;
532
+ startOffset = Math.min(entryPosition, startOffset);
533
+ if (entryPosition < previousEntryPosition) {
534
+ addWarning(warnings, WARNING_UNSORTED_CENTRAL_DIRECTORY, filename);
535
+ }
536
+ previousEntryPosition = entryPosition;
537
+ if ((fileEntry.version & MAX_8_BITS) > MAX_KNOWN_VERSION) {
538
+ addWarning(warnings, WARNING_UNKNOWN_VERSION, filename);
539
+ }
540
+ if ((fileEntry.rawBitFlag & BITFLAG_COMPRESSED_PATCHED_DATA) == BITFLAG_COMPRESSED_PATCHED_DATA) {
541
+ addWarning(warnings, WARNING_COMPRESSED_PATCHED_DATA, filename);
542
+ }
543
+ if (filenames.has(fileEntry.filename)) {
544
+ duplicateFilename = true;
506
545
  }
546
+ filenames.add(fileEntry.filename);
507
547
  const unixExternalUpper = (fileEntry.externalFileAttributes >> 16) & MAX_16_BITS;
508
548
  if (fileEntry.unixMode === UNDEFINED_VALUE && (unixExternalUpper & (FILE_ATTR_UNIX_DEFAULT_MASK | FILE_ATTR_UNIX_EXECUTABLE_MASK | FILE_ATTR_UNIX_TYPE_DIR)) != 0) {
509
549
  fileEntry.unixMode = unixExternalUpper;
@@ -541,7 +581,11 @@ class ZipReader {
541
581
  };
542
582
  offset = endOffset;
543
583
  if (indexFile == filesLength - 1 && recoverWrappedFilesLength) {
544
- filesLength += getWrappedFilesLength(directoryView, directoryArray, offset);
584
+ const wrappedFilesLength = getWrappedFilesLength(directoryView, directoryArray, offset);
585
+ if (wrappedFilesLength) {
586
+ filesLength += wrappedFilesLength;
587
+ addWarning(warnings, WARNING_WRAPPED_ENTRIES_COUNT);
588
+ }
545
589
  }
546
590
  const { onprogress } = options;
547
591
  if (onprogress) {
@@ -567,18 +611,22 @@ class ZipReader {
567
611
  zipReader.digitalSignature = digitalSignature;
568
612
  offsetAfterSignature = offset + 6 + digitalSignature.length;
569
613
  }
570
- if (checkAmbiguity && offset != declaredDirectoryDataLength && offsetAfterSignature != declaredDirectoryDataLength) {
571
- throwAmbiguousArchive("trailing central directory data");
614
+ if ((offset != declaredDirectoryDataLength && offsetAfterSignature != declaredDirectoryDataLength) ||
615
+ (!decryptedDirectory && offset != directoryDataLength && offsetAfterSignature != directoryDataLength)) {
616
+ reportAmbiguity(checkAmbiguity, warnings, WARNING_TRAILING_CENTRAL_DIRECTORY_DATA);
572
617
  }
573
618
  if (duplicateFilename) {
574
- throwAmbiguousArchive("duplicate filename");
619
+ reportAmbiguity(checkAmbiguity, warnings, WARNING_DUPLICATE_FILENAME);
575
620
  }
576
621
  const extractPrependedData = getOptionValue(zipReader, options, OPTION_EXTRACT_PREPENDED_DATA);
577
622
  const extractAppendedData = getOptionValue(zipReader, options, OPTION_EXTRACT_APPENDED_DATA);
578
623
  const splitZipSignatureLength = (checkAmbiguity || extractPrependedData) && filesLength &&
579
624
  startOffset == SPLIT_ZIP_FILE_SIGNATURE_LENGTH && await startsWithSplitZipMarker(reader) ? SPLIT_ZIP_FILE_SIGNATURE_LENGTH : 0;
580
625
  if (checkAmbiguity && (prependedDataLength || (filesLength && startOffset > splitZipSignatureLength))) {
581
- throwAmbiguousArchive("prepended data");
626
+ throwAmbiguousArchive(WARNING_PREPENDED_DATA);
627
+ }
628
+ if (prependedDataLength || (filesLength && startOffset > SPLIT_ZIP_FILE_SIGNATURE_LENGTH)) {
629
+ addWarning(warnings, WARNING_PREPENDED_DATA);
582
630
  }
583
631
  if (extractPrependedData) {
584
632
  zipReader.prependedData = startOffset > splitZipSignatureLength ?
@@ -648,8 +696,31 @@ class ZipReaderStream {
648
696
  }
649
697
  }
650
698
 
699
+ async function isZipFile(reader, options = {}) {
700
+ reader = new GenericReader(reader);
701
+ await initStream(reader);
702
+ if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
703
+ reader = new BlobReader(await streamToBlob(reader.readable));
704
+ await initStream(reader);
705
+ }
706
+ if (reader.size < END_OF_CENTRAL_DIR_LENGTH) {
707
+ return false;
708
+ }
709
+ const strictness = getStrictness(options, {});
710
+ const rejectAmbiguousEndOfDirectory = strictness != STRICTNESS_TOLERANT;
711
+ const maxAppendedDataSize = getMaxAppendedDataSize(options[OPTION_MAX_APPENDED_DATA_SIZE], strictness);
712
+ const { endOfDirectoryInfo, endOfDirectoryReachingEndCount } = await findEndOfCentralDirectory(reader, rejectAmbiguousEndOfDirectory, maxAppendedDataSize);
713
+ if (!endOfDirectoryInfo || (strictness == STRICTNESS_STRICT && endOfDirectoryReachingEndCount > 1)) {
714
+ return false;
715
+ }
716
+ const commentLength = getUint16(getDataView(endOfDirectoryInfo), 20);
717
+ const appendedDataOffset = endOfDirectoryInfo.offset + END_OF_CENTRAL_DIR_LENGTH + commentLength;
718
+ return reader.size - appendedDataOffset <= maxAppendedDataSize;
719
+ }
720
+
651
721
  export {
652
722
  ZipReader,
723
+ isZipFile,
653
724
  ZipReaderStream,
654
725
  ERR_BAD_FORMAT,
655
726
  ERR_EOCDR_NOT_FOUND,
@@ -676,7 +747,21 @@ export {
676
747
  ERR_INVALID_FILENAME_VALIDATION,
677
748
  ERR_INVALID_MAX_APPENDED_DATA_SIZE,
678
749
  ERR_UNSUPPORTED_UINT64,
679
- ERR_WORKER_STARTUP_TIMEOUT
750
+ ERR_WORKER_STARTUP_TIMEOUT,
751
+ WARNING_UNSORTED_CENTRAL_DIRECTORY,
752
+ WARNING_UNKNOWN_VERSION,
753
+ WARNING_COMPRESSED_PATCHED_DATA,
754
+ WARNING_MALFORMED_EXTRA_FIELD,
755
+ WARNING_UNKNOWN_ZIP64_EXTENSIBLE_DATA,
756
+ WARNING_WRAPPED_ENTRIES_COUNT,
757
+ WARNING_APPENDED_DATA,
758
+ WARNING_PREPENDED_DATA,
759
+ WARNING_TRAILING_CENTRAL_DIRECTORY_DATA,
760
+ WARNING_DUPLICATE_FILENAME,
761
+ WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY,
762
+ WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG,
763
+ WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD,
764
+ WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES
680
765
  };
681
766
 
682
767
  class ZipEntry {
@@ -710,6 +795,7 @@ class ZipEntry {
710
795
  dataDescriptor
711
796
  } = bitFlag;
712
797
  const localDirectory = fileEntry.localDirectory = {};
798
+ const warnings = fileEntry.warnings = [];
713
799
  const localHeaderOffset = getDiskOffset(reader, diskNumberStart) + offset;
714
800
  const dataArray = await readUint8Array(reader, localHeaderOffset, HEADER_SIZE);
715
801
  const dataView = getDataView(dataArray);
@@ -750,10 +836,10 @@ class ZipEntry {
750
836
  if (checkLocalFilename) {
751
837
  localDirectory.rawFilename = rawLocalFilename;
752
838
  }
753
- readCommonFooter(zipEntry, localDirectory, dataView, 4, true);
754
- if (checkLocalDirectory) {
755
- validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename);
839
+ if (readCommonFooter(zipEntry, localDirectory, dataView, 4, true)) {
840
+ addWarning(warnings, WARNING_MALFORMED_EXTRA_FIELD);
756
841
  }
842
+ validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename, checkLocalDirectory ? UNDEFINED_VALUE : warnings);
757
843
  const { lastAccessDate, creationDate, uid, gid } = localDirectory;
758
844
  if (lastAccessDate) {
759
845
  fileEntry.lastAccessDate = lastAccessDate;
@@ -979,6 +1065,7 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
979
1065
  const extraField = directory.extraField = new Map();
980
1066
  const rawExtraFieldView = getDataView(rawExtraField);
981
1067
  let offsetExtraField = 0;
1068
+ let malformedExtraField = false;
982
1069
  try {
983
1070
  while (offsetExtraField < rawExtraField.length) {
984
1071
  const type = getUint16(rawExtraFieldView, offsetExtraField);
@@ -990,7 +1077,10 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
990
1077
  offsetExtraField += 4 + size;
991
1078
  }
992
1079
  } catch {
993
- // ignored
1080
+ malformedExtraField = true;
1081
+ }
1082
+ if (offsetExtraField > rawExtraField.length) {
1083
+ malformedExtraField = true;
994
1084
  }
995
1085
  const compressionMethod = getUint16(dataView, offset + 4);
996
1086
  Object.assign(directory, {
@@ -1058,6 +1148,7 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
1058
1148
  if (extraFieldUSDZ) {
1059
1149
  directory.extraFieldUSDZ = extraFieldUSDZ;
1060
1150
  }
1151
+ return malformedExtraField;
1061
1152
  }
1062
1153
 
1063
1154
  function readExtraFieldZip64(extraFieldZip64, directory) {
@@ -1519,27 +1610,46 @@ async function readSignature(reader, view, anchoredOffset, signatureOffset, size
1519
1610
  return UNDEFINED_VALUE;
1520
1611
  }
1521
1612
 
1522
- function validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename) {
1613
+ function validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename, warnings) {
1523
1614
  const { rawFilename } = zipEntry;
1615
+ const reject = !warnings;
1524
1616
  const maskedLocalDirectory = zipEntry.decryptedDirectory &&
1525
1617
  (localDirectory.rawBitFlag & BITFLAG_MASKED_LOCAL_HEADERS) == BITFLAG_MASKED_LOCAL_HEADERS;
1526
1618
  if (checkLocalFilename && !maskedLocalDirectory &&
1527
1619
  (rawLocalFilename.length != rawFilename.length ||
1528
1620
  rawLocalFilename.some((byteValue, indexByte) => byteValue != rawFilename[indexByte]))) {
1529
- throwAmbiguousArchive("mismatched local file header (filename)");
1621
+ reportAmbiguity(reject, warnings, "mismatched local file header (filename)");
1530
1622
  }
1531
1623
  if ((localDirectory.rawBitFlag & BITFLAG_AMBIGUITY_MASK) != (zipEntry.rawBitFlag & BITFLAG_AMBIGUITY_MASK)) {
1532
- throwAmbiguousArchive("mismatched local file header (general purpose bit flag)");
1624
+ reportAmbiguity(reject, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG);
1533
1625
  }
1534
1626
  if (localDirectory.compressionMethod != zipEntry.compressionMethod) {
1535
- throwAmbiguousArchive("mismatched local file header (compression method)");
1627
+ reportAmbiguity(reject, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD);
1536
1628
  }
1537
1629
  if (!localDirectory.bitFlag.dataDescriptor && !maskedLocalDirectory &&
1538
1630
  (localDirectory.crc32 || localDirectory.compressedSize || localDirectory.uncompressedSize) &&
1539
1631
  (localDirectory.crc32 != zipEntry.crc32 ||
1540
1632
  localDirectory.compressedSize != zipEntry.compressedSize ||
1541
1633
  localDirectory.uncompressedSize != zipEntry.uncompressedSize)) {
1542
- throwAmbiguousArchive("mismatched local file header (crc32 or sizes)");
1634
+ reportAmbiguity(reject, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES);
1635
+ }
1636
+ }
1637
+
1638
+ function reportAmbiguity(reject, warnings, reason) {
1639
+ if (reject) {
1640
+ throwAmbiguousArchive(reason);
1641
+ } else {
1642
+ addWarning(warnings, reason);
1643
+ }
1644
+ }
1645
+
1646
+ function addWarning(warnings, reason, filename) {
1647
+ if (!warnings.some(warning => warning.reason == reason)) {
1648
+ const warning = { reason };
1649
+ if (filename !== UNDEFINED_VALUE) {
1650
+ warning.filename = filename;
1651
+ }
1652
+ warnings.push(warning);
1543
1653
  }
1544
1654
  }
1545
1655
 
@@ -230,6 +230,7 @@ import {
230
230
  OPTION_USDZ,
231
231
  OPTION_UNIX_EXTRA_FIELD_TYPE,
232
232
  OPTION_LOCAL_EXTRA_FIELD,
233
+ OPTION_CENTRAL_EXTRA_FIELD,
233
234
  OPTION_SIGN_CENTRAL_DIRECTORY,
234
235
  TEXT_TYPE_FILENAME,
235
236
  TEXT_TYPE_COMMENT,
@@ -423,6 +424,7 @@ class ZipWriter {
423
424
  rawExtraFieldNTFS: EMPTY_UINT8_ARRAY,
424
425
  rawExtraFieldUnix: EMPTY_UINT8_ARRAY,
425
426
  rawExtraField,
427
+ rawCentralExtraField: EMPTY_UINT8_ARRAY,
426
428
  extendedTimestamp: false,
427
429
  headerArray,
428
430
  headerView
@@ -596,7 +598,6 @@ async function prependZipEntries(zipWriter, reader) {
596
598
 
597
599
  async function addFileEntry(zipWriter, name, reader, options) {
598
600
  options = Object.assign({}, options);
599
- name = name.trim();
600
601
  if (getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY) && !name.endsWith(DIRECTORY_SIGNATURE)) {
601
602
  name += DIRECTORY_SIGNATURE;
602
603
  }
@@ -725,7 +726,6 @@ async function addFile(zipWriter, name, reader, options) {
725
726
  }
726
727
 
727
728
  function resolveAttributes(zipWriter, name, options) {
728
- name = name.trim();
729
729
  let msDosCompatible = getOptionValue(zipWriter, options, PROPERTY_NAME_MS_DOS_COMPATIBLE);
730
730
  let versionMadeBy = getOptionValue(zipWriter, options, PROPERTY_NAME_VERSION_MADE_BY, msDosCompatible ? VERSION_MADE_BY_MSDOS : VERSION_MADE_BY_UNIX);
731
731
  const executable = getOptionValue(zipWriter, options, PROPERTY_NAME_EXECUTABLE);
@@ -930,6 +930,7 @@ function resolveMetadata(zipWriter, name, options) {
930
930
  }
931
931
  const rawExtraField = serializeExtraField(options[PROPERTY_NAME_EXTRA_FIELD]);
932
932
  const rawLocalExtraField = serializeExtraField(options[OPTION_LOCAL_EXTRA_FIELD]);
933
+ const rawCentralExtraField = serializeExtraField(options[OPTION_CENTRAL_EXTRA_FIELD]);
933
934
  return {
934
935
  comment,
935
936
  resolvedOptions: {
@@ -965,7 +966,8 @@ function resolveMetadata(zipWriter, name, options) {
965
966
  dataDescriptor,
966
967
  zip64,
967
968
  rawExtraField,
968
- rawLocalExtraField
969
+ rawLocalExtraField,
970
+ rawCentralExtraField
969
971
  }
970
972
  };
971
973
  }
@@ -1107,7 +1109,6 @@ async function getEntriesSize(writerOptions, entries, writeOrderGuaranteed, comm
1107
1109
  let { name } = entry;
1108
1110
  const { size } = entry;
1109
1111
  const options = Object.assign({}, entry.options);
1110
- name = name.trim();
1111
1112
  if (getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY) && !name.endsWith(DIRECTORY_SIGNATURE)) {
1112
1113
  name += DIRECTORY_SIGNATURE;
1113
1114
  }
@@ -1363,6 +1364,7 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1363
1364
  versionMadeBy,
1364
1365
  rawComment,
1365
1366
  rawExtraField,
1367
+ rawCentralExtraField,
1366
1368
  useWebWorkers,
1367
1369
  transferStreams,
1368
1370
  onstart,
@@ -1406,6 +1408,7 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1406
1408
  rawExtraFieldUnix,
1407
1409
  rawExtraFieldAES,
1408
1410
  rawExtraField,
1411
+ rawCentralExtraField,
1409
1412
  extendedTimestamp,
1410
1413
  msDosCompatible,
1411
1414
  internalFileAttributes,
@@ -1437,6 +1440,7 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1437
1440
  const workerOptions = {
1438
1441
  options: {
1439
1442
  codecType: CODEC_DEFLATE,
1443
+ inputSize: size,
1440
1444
  level,
1441
1445
  rawPassword,
1442
1446
  password,
@@ -1906,6 +1910,7 @@ function createDirectoryRecords(files) {
1906
1910
  rawExtraFieldNTFS,
1907
1911
  rawExtraFieldUnix,
1908
1912
  rawExtraField,
1913
+ rawCentralExtraField,
1909
1914
  extendedTimestamp,
1910
1915
  extraFieldExtendedTimestampFlag,
1911
1916
  lastModDate,
@@ -1960,7 +1965,8 @@ function createDirectoryRecords(files) {
1960
1965
  rawExtraFieldNTFS,
1961
1966
  rawExtraFieldUnix,
1962
1967
  rawExtraFieldTimestamp,
1963
- rawExtraField);
1968
+ rawExtraField,
1969
+ rawCentralExtraField);
1964
1970
  if (extraFieldLength > MAX_16_BITS) {
1965
1971
  throw new Error(ERR_INVALID_EXTRAFIELD_DATA);
1966
1972
  }
@@ -1988,6 +1994,7 @@ async function writeDirectoryRecords(zipWriter, directoryDataLength, options) {
1988
1994
  rawExtraFieldNTFS,
1989
1995
  rawExtraFieldUnix,
1990
1996
  rawExtraField,
1997
+ rawCentralExtraField,
1991
1998
  rawComment,
1992
1999
  versionMadeBy,
1993
2000
  headerArray,
@@ -2002,7 +2009,7 @@ async function writeDirectoryRecords(zipWriter, directoryDataLength, options) {
2002
2009
  uncompressedSize,
2003
2010
  compressedSize
2004
2011
  } = fileEntry;
2005
- const extraFieldLength = getLength(rawExtraFieldZip64, rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawExtraFieldUnix, rawExtraField);
2012
+ const extraFieldLength = getLength(rawExtraFieldZip64, rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawExtraFieldUnix, rawExtraField, rawCentralExtraField);
2006
2013
  const directoryRecordLength = CENTRAL_FILE_HEADER_LENGTH + getLength(rawFilename, rawComment) + extraFieldLength;
2007
2014
  if (exceedsAvailableSize(writer, offset + directoryRecordLength - directoryDiskOffset)) {
2008
2015
  await writeData(writer, directoryArray.slice(directoryDiskOffset, offset));
@@ -2040,6 +2047,7 @@ async function writeDirectoryRecords(zipWriter, directoryDataLength, options) {
2040
2047
  directoryRecord.writeBytes(rawExtraFieldNTFS);
2041
2048
  directoryRecord.writeBytes(rawExtraFieldUnix);
2042
2049
  directoryRecord.writeBytes(rawExtraField);
2050
+ directoryRecord.writeBytes(rawCentralExtraField);
2043
2051
  directoryRecord.writeBytes(rawComment);
2044
2052
  arraySet(directoryArray, directoryRecord.array, offset);
2045
2053
  offset += directoryRecordLength;
@@ -71,10 +71,17 @@ export {
71
71
  export {
72
72
  registerCodec,
73
73
  unregisterCodec,
74
+ getRegisteredCodecs,
74
75
  ERR_INVALID_CODEC_DEFINITION,
75
76
  ERR_RESERVED_COMPRESSION_METHOD,
76
77
  ERR_INVALID_CODEC_MODULE
77
78
  } from "./core/codec-registry.js";
79
+ export {
80
+ getSupportedCompressionMethods
81
+ } from "./core/compression-methods.js";
82
+ export {
83
+ VERSION
84
+ } from "./core/version.js";
78
85
  export {
79
86
  createOPFSTempStream
80
87
  } from "./core/util/opfs-temp-stream.js";
@@ -31,6 +31,7 @@ import "./core/codec-worker-web.js";
31
31
  export {
32
32
  ZipReader,
33
33
  ZipReaderStream,
34
+ isZipFile,
34
35
  ERR_BAD_FORMAT,
35
36
  ERR_EOCDR_NOT_FOUND,
36
37
  ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND,
@@ -56,5 +57,19 @@ export {
56
57
  ERR_INVALID_FILENAME_VALIDATION,
57
58
  ERR_INVALID_MAX_APPENDED_DATA_SIZE,
58
59
  ERR_UNSUPPORTED_UINT64,
59
- ERR_WORKER_STARTUP_TIMEOUT
60
+ ERR_WORKER_STARTUP_TIMEOUT,
61
+ WARNING_UNSORTED_CENTRAL_DIRECTORY,
62
+ WARNING_UNKNOWN_VERSION,
63
+ WARNING_COMPRESSED_PATCHED_DATA,
64
+ WARNING_MALFORMED_EXTRA_FIELD,
65
+ WARNING_UNKNOWN_ZIP64_EXTENSIBLE_DATA,
66
+ WARNING_WRAPPED_ENTRIES_COUNT,
67
+ WARNING_APPENDED_DATA,
68
+ WARNING_PREPENDED_DATA,
69
+ WARNING_TRAILING_CENTRAL_DIRECTORY_DATA,
70
+ WARNING_DUPLICATE_FILENAME,
71
+ WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY,
72
+ WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG,
73
+ WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD,
74
+ WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES
60
75
  } from "./core/zip-reader.js";
@@ -0,0 +1,32 @@
1
+ /// <reference types="../index.d.ts" />
2
+
3
+ /*
4
+ Copyright (c) 2025 Gildas Lormeau. All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright notice,
10
+ this list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright
13
+ notice, this list of conditions and the following disclaimer in
14
+ the documentation and/or other materials provided with the distribution.
15
+
16
+ 3. The names of the authors may not be used to endorse or promote products
17
+ derived from this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
20
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
22
+ INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
23
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
25
+ OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ */
30
+
31
+ export * from "./zip-core.js";
32
+ export * from "./core/zip-fs.js";
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "A JavaScript library to zip and unzip files in the browser, Deno and Node.js",
4
4
  "author": "Gildas Lormeau",
5
5
  "license": "BSD-3-Clause",
6
- "version": "2.8.58",
6
+ "version": "2.8.60",
7
7
  "type": "module",
8
8
  "keywords": [
9
9
  "zip",
@@ -34,11 +34,11 @@
34
34
  "bun": ">=0.7.0"
35
35
  },
36
36
  "scripts": {
37
- "version": "node ./sync-deno-version.js && git add deno.json",
37
+ "version": "node ./sync-versions.js && git add deno.json lib/core/version.js",
38
38
  "bump-patch": "npm version patch --no-git-tag-version && npm run bump-commit",
39
39
  "bump-minor": "npm version minor --no-git-tag-version && npm run bump-commit",
40
40
  "bump-major": "npm version major --no-git-tag-version && npm run bump-commit",
41
- "bump-commit": "git commit -m \"bump up version\" package.json package-lock.json deno.json",
41
+ "bump-commit": "npm run build && git commit -m \"bump up version\" package.json package-lock.json deno.json lib/core/version.js dist index.cjs index-native.cjs index.min.js index-native.min.js lib/core/web-worker-inline-wasm.js lib/core/web-worker-inline-native.js",
42
42
  "lint": "npx eslint . && deno lint",
43
43
  "test": "npm run test-node && npm run test-deno && npm run test-bun && npm run test-node-dist && npm run test-deno-dist && npm run test-bun-dist && npm run test-web-streams-polyfill && npm run test-types && npm run test-api-symmetry && npm run test-api-shape && npm run test-api-mangling",
44
44
  "test-browsers": "npm run test-chrome && npm run test-firefox && npm run test-safari",
@@ -0,0 +1,26 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ const JSON_VERSION_REGEXP = /("version"\s*:\s*")[^"]*(")/;
6
+ const LIB_VERSION_REGEXP = /(const VERSION = ")[^"]*(")/;
7
+
8
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
+ const PACKAGE_PATH = path.resolve(__dirname, "package.json");
10
+ const DENO_PATH = path.resolve(__dirname, "deno.json");
11
+ const LIB_VERSION_PATH = path.resolve(__dirname, "lib", "core", "version.js");
12
+
13
+ const { version } = JSON.parse(fs.readFileSync(PACKAGE_PATH, "utf8"));
14
+ syncVersion(DENO_PATH, JSON_VERSION_REGEXP);
15
+ syncVersion(LIB_VERSION_PATH, LIB_VERSION_REGEXP);
16
+
17
+ function syncVersion(filePath, versionRegExp) {
18
+ const source = fs.readFileSync(filePath, "utf8");
19
+ if (!versionRegExp.test(source)) {
20
+ throw new Error("no version found in " + path.basename(filePath));
21
+ }
22
+ const syncedSource = source.replace(versionRegExp, "$1" + version + "$2");
23
+ if (syncedSource != source) {
24
+ fs.writeFileSync(filePath, syncedSource);
25
+ }
26
+ }