@zip.js/zip.js 2.11.3 → 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 +444 -114
- package/dist/zip-core-external.min.js +1 -1
- package/dist/zip-core.js +454 -111
- package/dist/zip-core.min.js +1 -1
- package/dist/zip-fs-core-external.js +509 -236
- package/dist/zip-fs-core-external.min.js +1 -1
- package/dist/zip-fs-core.js +522 -233
- package/dist/zip-fs-core.min.js +1 -1
- package/dist/zip-fs-external.js +509 -236
- package/dist/zip-fs-external.min.js +1 -1
- package/dist/zip-fs-native.js +524 -235
- package/dist/zip-fs-native.min.js +1 -1
- package/dist/zip-fs.js +525 -236
- package/dist/zip-fs.min.js +1 -1
- package/dist/zip-legacy.js +456 -113
- package/dist/zip-legacy.min.js +1 -1
- package/dist/zip-native.js +456 -113
- 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 +457 -114
- package/dist/zip.min.js +1 -1
- package/index-native.cjs +524 -235
- package/index-native.min.js +1 -1
- package/index.cjs +525 -236
- package/index.d.cts +480 -59
- package/index.d.ts +480 -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 +20 -10
- package/lib/core/options.js +29 -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 -134
- package/lib/core/zip-reader.js +108 -47
- package/lib/core/zip-writer.js +238 -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,
|
|
@@ -173,7 +177,9 @@ import {
|
|
|
173
177
|
OPTION_PREVENT_CLOSE,
|
|
174
178
|
checkFunctionOption,
|
|
175
179
|
checkSignalOption,
|
|
180
|
+
throwIfAborted,
|
|
176
181
|
checkPasswordOption,
|
|
182
|
+
checkPassThroughOption,
|
|
177
183
|
toNumber
|
|
178
184
|
} from "./options.js";
|
|
179
185
|
|
|
@@ -207,6 +213,8 @@ const WARNING_PREPENDED_CENTRAL_DIRECTORY = "prepended central directory";
|
|
|
207
213
|
const WARNING_TRAILING_CENTRAL_DIRECTORY_DATA = "trailing central directory data";
|
|
208
214
|
const WARNING_DUPLICATE_FILENAME = "duplicate filename";
|
|
209
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)";
|
|
210
218
|
const WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG = "mismatched local file header (general purpose bit flag)";
|
|
211
219
|
const WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD = "mismatched local file header (compression method)";
|
|
212
220
|
const WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES = "mismatched local file header (crc32 or sizes)";
|
|
@@ -277,7 +285,7 @@ class ZipReader {
|
|
|
277
285
|
}
|
|
278
286
|
}
|
|
279
287
|
if (rejectAmbiguousEndOfDirectory && endOfDirectoryReachingEndCount > 1) {
|
|
280
|
-
throwAmbiguousArchive(
|
|
288
|
+
throwAmbiguousArchive(WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY);
|
|
281
289
|
}
|
|
282
290
|
const endOfDirectoryView = getDataView(endOfDirectoryInfo);
|
|
283
291
|
let directoryDataLength = getUint32(endOfDirectoryView, 12);
|
|
@@ -575,7 +583,8 @@ class ZipReader {
|
|
|
575
583
|
const writer = new TransformStream();
|
|
576
584
|
const arrayBufferPromise = streamToBlob(writer.readable).then(blob => blob.arrayBuffer());
|
|
577
585
|
arrayBufferPromise.catch(() => { });
|
|
578
|
-
await fileEntry.getData(writer, entry, zipReader.readRanges,
|
|
586
|
+
await fileEntry.getData(writer, entry, zipReader.readRanges,
|
|
587
|
+
Object.assign({}, options, { preventClose: false }));
|
|
579
588
|
return arrayBufferPromise;
|
|
580
589
|
};
|
|
581
590
|
offset = endOffset;
|
|
@@ -656,48 +665,101 @@ class ZipReader {
|
|
|
656
665
|
await reader.readable.cancel();
|
|
657
666
|
}
|
|
658
667
|
}
|
|
668
|
+
|
|
669
|
+
[SYMBOL_ASYNC_DISPOSE]() {
|
|
670
|
+
return this.close();
|
|
671
|
+
}
|
|
659
672
|
}
|
|
660
673
|
|
|
661
674
|
class ZipReaderStream {
|
|
662
675
|
|
|
663
676
|
constructor(options = {}) {
|
|
664
|
-
|
|
665
|
-
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();
|
|
666
686
|
this.readable = new ReadableStream({
|
|
667
687
|
async pull(controller) {
|
|
668
688
|
const { done, value } = await gen.next();
|
|
669
689
|
if (done)
|
|
670
690
|
return controller.close();
|
|
691
|
+
const entryStream = createEntryStream(value, pendingEntries);
|
|
671
692
|
const chunk = {
|
|
672
693
|
...value,
|
|
673
|
-
readable:
|
|
674
|
-
const { readable, writable } = new TransformStream();
|
|
675
|
-
if (value.getData) {
|
|
676
|
-
getData();
|
|
677
|
-
return readable;
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
async function getData() {
|
|
681
|
-
try {
|
|
682
|
-
await value.getData(writable);
|
|
683
|
-
} catch (error) {
|
|
684
|
-
try {
|
|
685
|
-
await writable.abort(error);
|
|
686
|
-
} catch {
|
|
687
|
-
// ignored
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
})()
|
|
694
|
+
readable: entryStream.readable
|
|
692
695
|
};
|
|
693
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
|
+
});
|
|
694
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()]);
|
|
695
715
|
}
|
|
696
716
|
});
|
|
697
717
|
this.writable = writable;
|
|
698
718
|
}
|
|
699
719
|
}
|
|
700
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
|
+
|
|
701
763
|
async function isZipFile(reader, options = {}) {
|
|
702
764
|
reader = new GenericReader(reader);
|
|
703
765
|
await initStream(reader);
|
|
@@ -761,6 +823,8 @@ export {
|
|
|
761
823
|
WARNING_TRAILING_CENTRAL_DIRECTORY_DATA,
|
|
762
824
|
WARNING_DUPLICATE_FILENAME,
|
|
763
825
|
WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY,
|
|
826
|
+
WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY,
|
|
827
|
+
WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME,
|
|
764
828
|
WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG,
|
|
765
829
|
WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD,
|
|
766
830
|
WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES
|
|
@@ -803,7 +867,9 @@ class ZipEntry {
|
|
|
803
867
|
const dataView = getDataView(dataArray);
|
|
804
868
|
let password = getOptionValue(zipEntry, options, OPTION_PASSWORD);
|
|
805
869
|
let rawPassword = getOptionValue(zipEntry, options, OPTION_RAW_PASSWORD);
|
|
806
|
-
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;
|
|
807
873
|
checkPasswordOption(password, rawPassword);
|
|
808
874
|
password = password && password.length && password;
|
|
809
875
|
rawPassword = rawPassword && rawPassword.length && rawPassword;
|
|
@@ -824,7 +890,9 @@ class ZipEntry {
|
|
|
824
890
|
const checkLocalDirectoryOption = getOptionValue(zipEntry, options, OPTION_CHECK_LOCAL_DIRECTORY);
|
|
825
891
|
const entryStrictness = getStrictness(options, zipEntry.options);
|
|
826
892
|
const checkLocalDirectory = getCheckLocalDirectory(checkLocalDirectoryOption, entryStrictness);
|
|
827
|
-
const
|
|
893
|
+
const checkLocalFilenameOption = getOptionValue(zipEntry, options, OPTION_CHECK_LOCAL_FILENAME);
|
|
894
|
+
const checkLocalFilename = getCheckLocalFilename(
|
|
895
|
+
checkLocalFilenameOption === UNDEFINED_VALUE ? checkLocalDirectoryOption : checkLocalFilenameOption, entryStrictness);
|
|
828
896
|
let rawLocalFilename = EMPTY_UINT8_ARRAY;
|
|
829
897
|
if (checkLocalFilename && (filenameLength || extraFieldLength)) {
|
|
830
898
|
const trailingDataArray = await readUint8Array(reader, localHeaderOffset + HEADER_SIZE, filenameLength + extraFieldLength);
|
|
@@ -855,16 +923,17 @@ class ZipEntry {
|
|
|
855
923
|
if (gid !== UNDEFINED_VALUE && fileEntry.gid === UNDEFINED_VALUE) {
|
|
856
924
|
fileEntry.gid = gid;
|
|
857
925
|
}
|
|
858
|
-
const
|
|
926
|
+
const checkPasswordOnly = getOptionValue(zipEntry, options, OPTION_CHECK_PASSWORD_ONLY);
|
|
927
|
+
const encrypted = zipEntry.encrypted && localDirectory.encrypted && (!passThroughEncryption || checkPasswordOnly);
|
|
859
928
|
const zipCrypto = encrypted && !extraFieldAES;
|
|
860
|
-
if (!
|
|
929
|
+
if (!passThroughEncryption) {
|
|
861
930
|
fileEntry.zipCrypto = zipCrypto;
|
|
862
931
|
}
|
|
863
932
|
if (encrypted && (localDirectory.rawBitFlag & BITFLAG_STRONG_ENCRYPTION) == BITFLAG_STRONG_ENCRYPTION) {
|
|
864
933
|
throw new Error(ERR_UNSUPPORTED_ENCRYPTION);
|
|
865
934
|
}
|
|
866
|
-
const registeredCodec =
|
|
867
|
-
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) {
|
|
868
937
|
throw new Error(ERR_UNSUPPORTED_COMPRESSION);
|
|
869
938
|
}
|
|
870
939
|
if (encrypted) {
|
|
@@ -880,15 +949,17 @@ class ZipEntry {
|
|
|
880
949
|
const size = compressedSize;
|
|
881
950
|
const readable = toCompatibleReadable(reader.createReadable({ offset: dataOffset, size }));
|
|
882
951
|
const signal = checkSignalOption(getOptionValue(zipEntry, options, OPTION_SIGNAL));
|
|
883
|
-
|
|
952
|
+
throwIfAborted(signal);
|
|
884
953
|
let checkOverlappingEntry = getOptionValue(zipEntry, options, OPTION_CHECK_OVERLAPPING_ENTRY);
|
|
885
954
|
const checkOverlappingEntryOnly = getOptionValue(zipEntry, options, OPTION_CHECK_OVERLAPPING_ENTRY_ONLY);
|
|
886
955
|
if (checkOverlappingEntryOnly) {
|
|
887
956
|
checkOverlappingEntry = true;
|
|
888
957
|
}
|
|
889
958
|
const { onstart, onprogress, onend } = options;
|
|
890
|
-
const compressed = compressionMethod != COMPRESSION_METHOD_STORE && !
|
|
891
|
-
const outputSize =
|
|
959
|
+
const compressed = compressionMethod != COMPRESSION_METHOD_STORE && !passThroughCompression;
|
|
960
|
+
const outputSize = passThroughCompression ?
|
|
961
|
+
compressedSize - getEncryptionOverhead(encrypted, zipCrypto, extraFieldAES && extraFieldAES.strength) :
|
|
962
|
+
uncompressedSize;
|
|
892
963
|
const deflate64 = compressionMethod == COMPRESSION_METHOD_DEFLATE_64;
|
|
893
964
|
let useCompressionStream = getOptionValue(zipEntry, options, OPTION_USE_COMPRESSION_STREAM);
|
|
894
965
|
if (deflate64) {
|
|
@@ -897,7 +968,7 @@ class ZipEntry {
|
|
|
897
968
|
const checkCrc32Option = getOptionValue(zipEntry, options, OPTION_CHECK_CRC32);
|
|
898
969
|
const checkCrc32 = (checkCrc32Option === UNDEFINED_VALUE ?
|
|
899
970
|
getOptionValue(zipEntry, options, OPTION_CHECK_SIGNATURE) :
|
|
900
|
-
checkCrc32Option) && !
|
|
971
|
+
checkCrc32Option) && !passThroughCompression &&
|
|
901
972
|
(!encrypted || zipCrypto || (extraFieldAES && extraFieldAES.vendorVersion == VENDOR_VERSION_AE_1));
|
|
902
973
|
const workerOptions = {
|
|
903
974
|
options: {
|
|
@@ -951,10 +1022,10 @@ class ZipEntry {
|
|
|
951
1022
|
await initStream(writer, getDecodableOutputSize(outputSize, compressedSize, compressed));
|
|
952
1023
|
({ writable } = writer);
|
|
953
1024
|
const { outputSize: writtenSize } = await runWorker({ readable, writable }, workerOptions);
|
|
954
|
-
writer.size += writtenSize;
|
|
955
1025
|
if (writtenSize != outputSize) {
|
|
956
|
-
throw new Error(ERR_INVALID_UNCOMPRESSED_SIZE);
|
|
1026
|
+
throw Object.assign(new Error(ERR_INVALID_UNCOMPRESSED_SIZE), { outputSize: writtenSize });
|
|
957
1027
|
}
|
|
1028
|
+
writer.size += writtenSize;
|
|
958
1029
|
}
|
|
959
1030
|
} catch (error) {
|
|
960
1031
|
if (error.outputSize !== UNDEFINED_VALUE) {
|
|
@@ -1620,7 +1691,7 @@ function validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, chec
|
|
|
1620
1691
|
if (checkLocalFilename && !maskedLocalDirectory &&
|
|
1621
1692
|
(rawLocalFilename.length != rawFilename.length ||
|
|
1622
1693
|
rawLocalFilename.some((byteValue, indexByte) => byteValue != rawFilename[indexByte]))) {
|
|
1623
|
-
reportAmbiguity(reject, warnings,
|
|
1694
|
+
reportAmbiguity(reject, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME);
|
|
1624
1695
|
}
|
|
1625
1696
|
if ((localDirectory.rawBitFlag & BITFLAG_AMBIGUITY_MASK) != (zipEntry.rawBitFlag & BITFLAG_AMBIGUITY_MASK)) {
|
|
1626
1697
|
reportAmbiguity(reject, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG);
|
|
@@ -1645,16 +1716,6 @@ function reportAmbiguity(reject, warnings, reason) {
|
|
|
1645
1716
|
}
|
|
1646
1717
|
}
|
|
1647
1718
|
|
|
1648
|
-
function addWarning(warnings, reason, filename) {
|
|
1649
|
-
if (!warnings.some(warning => warning.reason == reason)) {
|
|
1650
|
-
const warning = { reason };
|
|
1651
|
-
if (filename !== UNDEFINED_VALUE) {
|
|
1652
|
-
warning.filename = filename;
|
|
1653
|
-
}
|
|
1654
|
-
warnings.push(warning);
|
|
1655
|
-
}
|
|
1656
|
-
}
|
|
1657
|
-
|
|
1658
1719
|
function throwAmbiguousArchive(reason) {
|
|
1659
1720
|
const error = new Error(ERR_AMBIGUOUS_ARCHIVE);
|
|
1660
1721
|
error.reason = reason;
|