@zip.js/zip.js 2.11.4 → 2.12.0
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/deno.json +1 -1
- package/dist/zip-core-external.js +427 -110
- package/dist/zip-core-external.min.js +1 -1
- package/dist/zip-core.js +437 -107
- package/dist/zip-core.min.js +1 -1
- package/dist/zip-fs-core-external.js +492 -230
- package/dist/zip-fs-core-external.min.js +1 -1
- package/dist/zip-fs-core.js +505 -227
- package/dist/zip-fs-core.min.js +1 -1
- package/dist/zip-fs-external.js +492 -230
- package/dist/zip-fs-external.min.js +1 -1
- package/dist/zip-fs-native.js +507 -229
- package/dist/zip-fs-native.min.js +1 -1
- package/dist/zip-fs.js +508 -230
- package/dist/zip-fs.min.js +1 -1
- package/dist/zip-legacy.js +439 -109
- package/dist/zip-legacy.min.js +1 -1
- package/dist/zip-native.js +439 -109
- package/dist/zip-native.min.js +1 -1
- package/dist/zip-web-worker-native.js +1 -1
- package/dist/zip-web-worker.js +1 -1
- package/dist/zip.js +440 -110
- package/dist/zip.min.js +1 -1
- package/index-native.cjs +507 -229
- package/index-native.min.js +1 -1
- package/index.cjs +508 -230
- package/index.d.cts +468 -59
- package/index.d.ts +468 -59
- package/index.min.js +1 -1
- package/lib/core/codec-worker-web.js +10 -2
- package/lib/core/codec-worker.js +4 -3
- package/lib/core/configuration.js +20 -5
- package/lib/core/constants.js +2 -0
- package/lib/core/io.js +13 -5
- package/lib/core/options.js +16 -0
- package/lib/core/streams/codec-stream.js +6 -0
- package/lib/core/util/warnings.js +42 -0
- package/lib/core/version.js +1 -1
- package/lib/core/web-worker-base.js +4 -3
- package/lib/core/web-worker-inline-native.js +1 -1
- package/lib/core/web-worker-inline-wasm.js +1 -1
- package/lib/core/zip-entry.js +49 -1
- package/lib/core/zip-fs.js +43 -132
- package/lib/core/zip-reader.js +106 -48
- package/lib/core/zip-writer.js +236 -54
- package/lib/zip-core-base.js +9 -3
- package/lib/zip-core-reader.js +2 -0
- package/lib/zip-core-writer.js +6 -1
- package/lib/zip-module-wasm-base.js +2 -2
- package/package.json +5 -3
package/lib/core/zip-reader.js
CHANGED
|
@@ -95,6 +95,7 @@ import {
|
|
|
95
95
|
FILE_ATTR_UNIX_STICKY_MASK,
|
|
96
96
|
NUMBER_TYPE,
|
|
97
97
|
EMPTY_UINT8_ARRAY,
|
|
98
|
+
SYMBOL_ASYNC_DISPOSE,
|
|
98
99
|
MIN_DATE
|
|
99
100
|
} from "./constants.js";
|
|
100
101
|
import { getConfiguration } from "./configuration.js";
|
|
@@ -120,6 +121,7 @@ import {
|
|
|
120
121
|
} from "./io.js";
|
|
121
122
|
import { decodeText } from "./util/decode-text.js";
|
|
122
123
|
import { getDataView } from "./util/array.js";
|
|
124
|
+
import { addWarning } from "./util/warnings.js";
|
|
123
125
|
import { toCompatibleReadable, streamToBlob } from "./util/compatible-streams.js";
|
|
124
126
|
import { Crc32 } from "./streams/codecs/crc32.js";
|
|
125
127
|
import {
|
|
@@ -137,7 +139,8 @@ import {
|
|
|
137
139
|
PROPERTY_NAME_RAW_LAST_ACCESS_DATE,
|
|
138
140
|
PROPERTY_NAME_CREATION_DATE,
|
|
139
141
|
PROPERTY_NAME_RAW_CREATION_DATE,
|
|
140
|
-
Entry
|
|
142
|
+
Entry,
|
|
143
|
+
getEncryptionOverhead
|
|
141
144
|
} from "./zip-entry.js";
|
|
142
145
|
import {
|
|
143
146
|
OPTION_FILENAME_ENCODING,
|
|
@@ -154,6 +157,7 @@ import {
|
|
|
154
157
|
OPTION_CHECK_OVERLAPPING_ENTRY,
|
|
155
158
|
OPTION_CHECK_AMBIGUITY,
|
|
156
159
|
OPTION_CHECK_LOCAL_DIRECTORY,
|
|
160
|
+
OPTION_CHECK_LOCAL_FILENAME,
|
|
157
161
|
OPTION_CHECK_SIGNATURE,
|
|
158
162
|
OPTION_CHECK_CRC32,
|
|
159
163
|
OPTION_CHECK_AUTHENTICATION_CODE,
|
|
@@ -175,6 +179,7 @@ import {
|
|
|
175
179
|
checkSignalOption,
|
|
176
180
|
throwIfAborted,
|
|
177
181
|
checkPasswordOption,
|
|
182
|
+
checkPassThroughOption,
|
|
178
183
|
toNumber
|
|
179
184
|
} from "./options.js";
|
|
180
185
|
|
|
@@ -208,6 +213,8 @@ const WARNING_PREPENDED_CENTRAL_DIRECTORY = "prepended central directory";
|
|
|
208
213
|
const WARNING_TRAILING_CENTRAL_DIRECTORY_DATA = "trailing central directory data";
|
|
209
214
|
const WARNING_DUPLICATE_FILENAME = "duplicate filename";
|
|
210
215
|
const WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY = "mismatched zip64 end of central directory record";
|
|
216
|
+
const WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY = "multiple end of central directory records";
|
|
217
|
+
const WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME = "mismatched local file header (filename)";
|
|
211
218
|
const WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG = "mismatched local file header (general purpose bit flag)";
|
|
212
219
|
const WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD = "mismatched local file header (compression method)";
|
|
213
220
|
const WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES = "mismatched local file header (crc32 or sizes)";
|
|
@@ -278,7 +285,7 @@ class ZipReader {
|
|
|
278
285
|
}
|
|
279
286
|
}
|
|
280
287
|
if (rejectAmbiguousEndOfDirectory && endOfDirectoryReachingEndCount > 1) {
|
|
281
|
-
throwAmbiguousArchive(
|
|
288
|
+
throwAmbiguousArchive(WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY);
|
|
282
289
|
}
|
|
283
290
|
const endOfDirectoryView = getDataView(endOfDirectoryInfo);
|
|
284
291
|
let directoryDataLength = getUint32(endOfDirectoryView, 12);
|
|
@@ -576,7 +583,8 @@ class ZipReader {
|
|
|
576
583
|
const writer = new TransformStream();
|
|
577
584
|
const arrayBufferPromise = streamToBlob(writer.readable).then(blob => blob.arrayBuffer());
|
|
578
585
|
arrayBufferPromise.catch(() => { });
|
|
579
|
-
await fileEntry.getData(writer, entry, zipReader.readRanges,
|
|
586
|
+
await fileEntry.getData(writer, entry, zipReader.readRanges,
|
|
587
|
+
Object.assign({}, options, { preventClose: false }));
|
|
580
588
|
return arrayBufferPromise;
|
|
581
589
|
};
|
|
582
590
|
offset = endOffset;
|
|
@@ -657,49 +665,101 @@ class ZipReader {
|
|
|
657
665
|
await reader.readable.cancel();
|
|
658
666
|
}
|
|
659
667
|
}
|
|
668
|
+
|
|
669
|
+
[SYMBOL_ASYNC_DISPOSE]() {
|
|
670
|
+
return this.close();
|
|
671
|
+
}
|
|
660
672
|
}
|
|
661
673
|
|
|
662
674
|
class ZipReaderStream {
|
|
663
675
|
|
|
664
676
|
constructor(options = {}) {
|
|
665
|
-
|
|
666
|
-
const
|
|
677
|
+
let sourceController;
|
|
678
|
+
const { readable, writable } = new TransformStream({
|
|
679
|
+
start(controller) {
|
|
680
|
+
sourceController = controller;
|
|
681
|
+
}
|
|
682
|
+
});
|
|
683
|
+
const zipReader = new ZipReader(readable, options);
|
|
684
|
+
const gen = zipReader.getEntriesGenerator();
|
|
685
|
+
const pendingEntries = new Set();
|
|
667
686
|
this.readable = new ReadableStream({
|
|
668
687
|
async pull(controller) {
|
|
669
688
|
const { done, value } = await gen.next();
|
|
670
689
|
if (done)
|
|
671
690
|
return controller.close();
|
|
672
|
-
const
|
|
673
|
-
const { readable, writable } = new TransformStream();
|
|
674
|
-
if (value.getData) {
|
|
675
|
-
getData();
|
|
676
|
-
return readable;
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
async function getData() {
|
|
680
|
-
try {
|
|
681
|
-
await value.getData(writable);
|
|
682
|
-
} catch (error) {
|
|
683
|
-
try {
|
|
684
|
-
await writable.abort(error);
|
|
685
|
-
} catch {
|
|
686
|
-
// ignored
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
})();
|
|
691
|
+
const entryStream = createEntryStream(value, pendingEntries);
|
|
691
692
|
const chunk = {
|
|
692
693
|
...value,
|
|
693
|
-
readable:
|
|
694
|
+
readable: entryStream.readable
|
|
694
695
|
};
|
|
695
696
|
delete chunk.getData;
|
|
697
|
+
Object.defineProperties(chunk, {
|
|
698
|
+
localDirectory: {
|
|
699
|
+
get: () => value.localDirectory,
|
|
700
|
+
enumerable: true
|
|
701
|
+
},
|
|
702
|
+
warnings: {
|
|
703
|
+
get: () => value.warnings,
|
|
704
|
+
enumerable: true
|
|
705
|
+
}
|
|
706
|
+
});
|
|
696
707
|
controller.enqueue(chunk);
|
|
708
|
+
},
|
|
709
|
+
async cancel(reason) {
|
|
710
|
+
const entryStreams = Array.from(pendingEntries);
|
|
711
|
+
pendingEntries.clear();
|
|
712
|
+
sourceController.error(reason);
|
|
713
|
+
await Promise.allSettled(entryStreams.map(entryStream => entryStream.cancel(reason)));
|
|
714
|
+
await Promise.allSettled([gen.return(), zipReader.close()]);
|
|
697
715
|
}
|
|
698
716
|
});
|
|
699
717
|
this.writable = writable;
|
|
700
718
|
}
|
|
701
719
|
}
|
|
702
720
|
|
|
721
|
+
function createEntryStream(entry, pendingEntries) {
|
|
722
|
+
const { readable, writable } = new TransformStream();
|
|
723
|
+
let dataReader;
|
|
724
|
+
const entryStream = {
|
|
725
|
+
cancel: async reason => {
|
|
726
|
+
pendingEntries.delete(entryStream);
|
|
727
|
+
await (dataReader ? dataReader.cancel(reason) : readable.cancel(reason));
|
|
728
|
+
}
|
|
729
|
+
};
|
|
730
|
+
entryStream.readable = new ReadableStream({
|
|
731
|
+
async pull(controller) {
|
|
732
|
+
if (!dataReader) {
|
|
733
|
+
dataReader = readable.getReader();
|
|
734
|
+
pendingEntries.add(entryStream);
|
|
735
|
+
getData();
|
|
736
|
+
}
|
|
737
|
+
const { done, value } = await dataReader.read();
|
|
738
|
+
if (done) {
|
|
739
|
+
controller.close();
|
|
740
|
+
} else {
|
|
741
|
+
controller.enqueue(value);
|
|
742
|
+
}
|
|
743
|
+
},
|
|
744
|
+
cancel: reason => entryStream.cancel(reason)
|
|
745
|
+
}, { highWaterMark: 0 });
|
|
746
|
+
return entryStream;
|
|
747
|
+
|
|
748
|
+
async function getData() {
|
|
749
|
+
try {
|
|
750
|
+
await entry.getData(writable, { preventClose: false });
|
|
751
|
+
} catch (error) {
|
|
752
|
+
try {
|
|
753
|
+
await writable.abort(error);
|
|
754
|
+
} catch {
|
|
755
|
+
// ignored
|
|
756
|
+
}
|
|
757
|
+
} finally {
|
|
758
|
+
pendingEntries.delete(entryStream);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
703
763
|
async function isZipFile(reader, options = {}) {
|
|
704
764
|
reader = new GenericReader(reader);
|
|
705
765
|
await initStream(reader);
|
|
@@ -763,6 +823,8 @@ export {
|
|
|
763
823
|
WARNING_TRAILING_CENTRAL_DIRECTORY_DATA,
|
|
764
824
|
WARNING_DUPLICATE_FILENAME,
|
|
765
825
|
WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY,
|
|
826
|
+
WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY,
|
|
827
|
+
WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME,
|
|
766
828
|
WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG,
|
|
767
829
|
WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD,
|
|
768
830
|
WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES
|
|
@@ -805,7 +867,9 @@ class ZipEntry {
|
|
|
805
867
|
const dataView = getDataView(dataArray);
|
|
806
868
|
let password = getOptionValue(zipEntry, options, OPTION_PASSWORD);
|
|
807
869
|
let rawPassword = getOptionValue(zipEntry, options, OPTION_RAW_PASSWORD);
|
|
808
|
-
const passThrough = getOptionValue(zipEntry, options, OPTION_PASS_THROUGH);
|
|
870
|
+
const passThrough = checkPassThroughOption(getOptionValue(zipEntry, options, OPTION_PASS_THROUGH));
|
|
871
|
+
const passThroughCompression = Boolean(passThrough);
|
|
872
|
+
const passThroughEncryption = passThrough === true;
|
|
809
873
|
checkPasswordOption(password, rawPassword);
|
|
810
874
|
password = password && password.length && password;
|
|
811
875
|
rawPassword = rawPassword && rawPassword.length && rawPassword;
|
|
@@ -826,7 +890,9 @@ class ZipEntry {
|
|
|
826
890
|
const checkLocalDirectoryOption = getOptionValue(zipEntry, options, OPTION_CHECK_LOCAL_DIRECTORY);
|
|
827
891
|
const entryStrictness = getStrictness(options, zipEntry.options);
|
|
828
892
|
const checkLocalDirectory = getCheckLocalDirectory(checkLocalDirectoryOption, entryStrictness);
|
|
829
|
-
const
|
|
893
|
+
const checkLocalFilenameOption = getOptionValue(zipEntry, options, OPTION_CHECK_LOCAL_FILENAME);
|
|
894
|
+
const checkLocalFilename = getCheckLocalFilename(
|
|
895
|
+
checkLocalFilenameOption === UNDEFINED_VALUE ? checkLocalDirectoryOption : checkLocalFilenameOption, entryStrictness);
|
|
830
896
|
let rawLocalFilename = EMPTY_UINT8_ARRAY;
|
|
831
897
|
if (checkLocalFilename && (filenameLength || extraFieldLength)) {
|
|
832
898
|
const trailingDataArray = await readUint8Array(reader, localHeaderOffset + HEADER_SIZE, filenameLength + extraFieldLength);
|
|
@@ -857,16 +923,17 @@ class ZipEntry {
|
|
|
857
923
|
if (gid !== UNDEFINED_VALUE && fileEntry.gid === UNDEFINED_VALUE) {
|
|
858
924
|
fileEntry.gid = gid;
|
|
859
925
|
}
|
|
860
|
-
const
|
|
926
|
+
const checkPasswordOnly = getOptionValue(zipEntry, options, OPTION_CHECK_PASSWORD_ONLY);
|
|
927
|
+
const encrypted = zipEntry.encrypted && localDirectory.encrypted && (!passThroughEncryption || checkPasswordOnly);
|
|
861
928
|
const zipCrypto = encrypted && !extraFieldAES;
|
|
862
|
-
if (!
|
|
929
|
+
if (!passThroughEncryption) {
|
|
863
930
|
fileEntry.zipCrypto = zipCrypto;
|
|
864
931
|
}
|
|
865
932
|
if (encrypted && (localDirectory.rawBitFlag & BITFLAG_STRONG_ENCRYPTION) == BITFLAG_STRONG_ENCRYPTION) {
|
|
866
933
|
throw new Error(ERR_UNSUPPORTED_ENCRYPTION);
|
|
867
934
|
}
|
|
868
|
-
const registeredCodec =
|
|
869
|
-
if (compressionMethod != COMPRESSION_METHOD_STORE && compressionMethod != COMPRESSION_METHOD_DEFLATE && compressionMethod != COMPRESSION_METHOD_DEFLATE_64 && !registeredCodec && !
|
|
935
|
+
const registeredCodec = passThroughCompression ? UNDEFINED_VALUE : getRegisteredCodec(compressionMethod);
|
|
936
|
+
if (compressionMethod != COMPRESSION_METHOD_STORE && compressionMethod != COMPRESSION_METHOD_DEFLATE && compressionMethod != COMPRESSION_METHOD_DEFLATE_64 && !registeredCodec && !passThroughCompression) {
|
|
870
937
|
throw new Error(ERR_UNSUPPORTED_COMPRESSION);
|
|
871
938
|
}
|
|
872
939
|
if (encrypted) {
|
|
@@ -883,15 +950,16 @@ class ZipEntry {
|
|
|
883
950
|
const readable = toCompatibleReadable(reader.createReadable({ offset: dataOffset, size }));
|
|
884
951
|
const signal = checkSignalOption(getOptionValue(zipEntry, options, OPTION_SIGNAL));
|
|
885
952
|
throwIfAborted(signal);
|
|
886
|
-
const checkPasswordOnly = getOptionValue(zipEntry, options, OPTION_CHECK_PASSWORD_ONLY);
|
|
887
953
|
let checkOverlappingEntry = getOptionValue(zipEntry, options, OPTION_CHECK_OVERLAPPING_ENTRY);
|
|
888
954
|
const checkOverlappingEntryOnly = getOptionValue(zipEntry, options, OPTION_CHECK_OVERLAPPING_ENTRY_ONLY);
|
|
889
955
|
if (checkOverlappingEntryOnly) {
|
|
890
956
|
checkOverlappingEntry = true;
|
|
891
957
|
}
|
|
892
958
|
const { onstart, onprogress, onend } = options;
|
|
893
|
-
const compressed = compressionMethod != COMPRESSION_METHOD_STORE && !
|
|
894
|
-
const outputSize =
|
|
959
|
+
const compressed = compressionMethod != COMPRESSION_METHOD_STORE && !passThroughCompression;
|
|
960
|
+
const outputSize = passThroughCompression ?
|
|
961
|
+
compressedSize - getEncryptionOverhead(encrypted, zipCrypto, extraFieldAES && extraFieldAES.strength) :
|
|
962
|
+
uncompressedSize;
|
|
895
963
|
const deflate64 = compressionMethod == COMPRESSION_METHOD_DEFLATE_64;
|
|
896
964
|
let useCompressionStream = getOptionValue(zipEntry, options, OPTION_USE_COMPRESSION_STREAM);
|
|
897
965
|
if (deflate64) {
|
|
@@ -900,7 +968,7 @@ class ZipEntry {
|
|
|
900
968
|
const checkCrc32Option = getOptionValue(zipEntry, options, OPTION_CHECK_CRC32);
|
|
901
969
|
const checkCrc32 = (checkCrc32Option === UNDEFINED_VALUE ?
|
|
902
970
|
getOptionValue(zipEntry, options, OPTION_CHECK_SIGNATURE) :
|
|
903
|
-
checkCrc32Option) && !
|
|
971
|
+
checkCrc32Option) && !passThroughCompression &&
|
|
904
972
|
(!encrypted || zipCrypto || (extraFieldAES && extraFieldAES.vendorVersion == VENDOR_VERSION_AE_1));
|
|
905
973
|
const workerOptions = {
|
|
906
974
|
options: {
|
|
@@ -954,10 +1022,10 @@ class ZipEntry {
|
|
|
954
1022
|
await initStream(writer, getDecodableOutputSize(outputSize, compressedSize, compressed));
|
|
955
1023
|
({ writable } = writer);
|
|
956
1024
|
const { outputSize: writtenSize } = await runWorker({ readable, writable }, workerOptions);
|
|
957
|
-
writer.size += writtenSize;
|
|
958
1025
|
if (writtenSize != outputSize) {
|
|
959
|
-
throw new Error(ERR_INVALID_UNCOMPRESSED_SIZE);
|
|
1026
|
+
throw Object.assign(new Error(ERR_INVALID_UNCOMPRESSED_SIZE), { outputSize: writtenSize });
|
|
960
1027
|
}
|
|
1028
|
+
writer.size += writtenSize;
|
|
961
1029
|
}
|
|
962
1030
|
} catch (error) {
|
|
963
1031
|
if (error.outputSize !== UNDEFINED_VALUE) {
|
|
@@ -1623,7 +1691,7 @@ function validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, chec
|
|
|
1623
1691
|
if (checkLocalFilename && !maskedLocalDirectory &&
|
|
1624
1692
|
(rawLocalFilename.length != rawFilename.length ||
|
|
1625
1693
|
rawLocalFilename.some((byteValue, indexByte) => byteValue != rawFilename[indexByte]))) {
|
|
1626
|
-
reportAmbiguity(reject, warnings,
|
|
1694
|
+
reportAmbiguity(reject, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME);
|
|
1627
1695
|
}
|
|
1628
1696
|
if ((localDirectory.rawBitFlag & BITFLAG_AMBIGUITY_MASK) != (zipEntry.rawBitFlag & BITFLAG_AMBIGUITY_MASK)) {
|
|
1629
1697
|
reportAmbiguity(reject, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG);
|
|
@@ -1648,16 +1716,6 @@ function reportAmbiguity(reject, warnings, reason) {
|
|
|
1648
1716
|
}
|
|
1649
1717
|
}
|
|
1650
1718
|
|
|
1651
|
-
function addWarning(warnings, reason, filename) {
|
|
1652
|
-
if (!warnings.some(warning => warning.reason == reason)) {
|
|
1653
|
-
const warning = { reason };
|
|
1654
|
-
if (filename !== UNDEFINED_VALUE) {
|
|
1655
|
-
warning.filename = filename;
|
|
1656
|
-
}
|
|
1657
|
-
warnings.push(warning);
|
|
1658
|
-
}
|
|
1659
|
-
}
|
|
1660
|
-
|
|
1661
1719
|
function throwAmbiguousArchive(reason) {
|
|
1662
1720
|
const error = new Error(ERR_AMBIGUOUS_ARCHIVE);
|
|
1663
1721
|
error.reason = reason;
|