@zip.js/zip.js 2.11.4 → 2.13.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 +549 -139
- package/dist/zip-core-external.min.js +1 -1
- package/dist/zip-core.js +559 -136
- package/dist/zip-core.min.js +1 -1
- package/dist/zip-fs-core-external.js +615 -260
- package/dist/zip-fs-core-external.min.js +1 -1
- package/dist/zip-fs-core.js +628 -257
- package/dist/zip-fs-core.min.js +1 -1
- package/dist/zip-fs-external.js +615 -260
- package/dist/zip-fs-external.min.js +1 -1
- package/dist/zip-fs-native.js +630 -259
- package/dist/zip-fs-native.min.js +1 -1
- package/dist/zip-fs.js +631 -260
- package/dist/zip-fs.min.js +1 -1
- package/dist/zip-legacy.js +561 -138
- package/dist/zip-legacy.min.js +1 -1
- package/dist/zip-native.js +561 -138
- 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 +562 -139
- package/dist/zip.min.js +1 -1
- package/index-native.cjs +630 -259
- package/index-native.min.js +1 -1
- package/index.cjs +631 -260
- package/index.d.cts +474 -59
- package/index.d.ts +474 -59
- package/index.min.js +1 -1
- package/lib/core/codec-worker-web.js +72 -22
- package/lib/core/codec-worker.js +18 -5
- 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/error.js +35 -0
- package/lib/core/util/warnings.js +42 -0
- package/lib/core/version.js +1 -1
- package/lib/core/web-worker-base.js +46 -9
- 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 +45 -133
- package/lib/core/zip-reader.js +115 -52
- package/lib/core/zip-writer.js +244 -57
- 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/worker-message-property-names.js +1 -1
package/lib/core/zip-fs.js
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
|
-
/* global WritableStream, AbortController
|
|
29
|
+
/* global WritableStream, AbortController */
|
|
30
30
|
// deno-lint-ignore-file no-this-alias
|
|
31
31
|
|
|
32
32
|
import {
|
|
@@ -50,39 +50,30 @@ import {
|
|
|
50
50
|
} from "./zip-reader.js";
|
|
51
51
|
import {
|
|
52
52
|
ZipWriter,
|
|
53
|
-
getEntriesSize
|
|
53
|
+
getEntriesSize,
|
|
54
|
+
getSourceEntryOptions
|
|
54
55
|
} from "./zip-writer.js";
|
|
55
56
|
import {
|
|
56
57
|
Entry
|
|
57
58
|
} from "./zip-entry.js";
|
|
58
|
-
import { checkSignalOption,
|
|
59
|
+
import { checkSignalOption, throwIfAborted, checkPassThroughOption, PASS_THROUGH_COMPRESSED } from "./options.js";
|
|
59
60
|
import { ProgressWatcherStream, callHandler } from "./codec-worker.js";
|
|
60
61
|
import {
|
|
61
62
|
UNDEFINED_VALUE,
|
|
62
63
|
FUNCTION_TYPE,
|
|
63
|
-
OBJECT_TYPE
|
|
64
|
-
MAX_8_BITS,
|
|
65
|
-
MIN_DATE,
|
|
66
|
-
MAX_DATE,
|
|
67
|
-
EXTRAFIELD_TYPE_ZIP64,
|
|
68
|
-
EXTRAFIELD_TYPE_AES,
|
|
69
|
-
EXTRAFIELD_TYPE_NTFS,
|
|
70
|
-
EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP,
|
|
71
|
-
EXTRAFIELD_TYPE_UNICODE_PATH,
|
|
72
|
-
EXTRAFIELD_TYPE_UNICODE_COMMENT,
|
|
73
|
-
EXTRAFIELD_TYPE_USDZ,
|
|
74
|
-
EXTRAFIELD_TYPE_INFOZIP,
|
|
75
|
-
EXTRAFIELD_TYPE_UNIX,
|
|
76
|
-
EXTRAFIELD_TYPE_UNIX_TYPE1,
|
|
77
|
-
EXTRAFIELD_TYPE_PKWARE_UNIX
|
|
64
|
+
OBJECT_TYPE
|
|
78
65
|
} from "./constants.js";
|
|
79
66
|
import { toExactUint8Array } from "./util/array.js";
|
|
80
67
|
import { toCompatibleReadable, toCompatibleWritable } from "./util/compatible-streams.js";
|
|
68
|
+
import { isErrorObject } from "./util/error.js";
|
|
81
69
|
|
|
82
70
|
const ERR_ENTRY_EXISTS = "Entry filename already exists";
|
|
83
71
|
const ERR_DUPLICATE_IMPORTED_ENTRY = "Duplicate entry filename in the imported zip file";
|
|
84
72
|
const ERR_INVALID_DUPLICATES = "Invalid duplicates option (must be \"throw\", \"keep-first\" or \"keep-last\")";
|
|
85
|
-
const ERR_ANCESTOR_ENTRY = "Entry is
|
|
73
|
+
const ERR_ANCESTOR_ENTRY = "Entry is an ancestor of target entry";
|
|
74
|
+
const ERR_ROOT_DIRECTORY_NOT_MOVABLE = "Root directory cannot be moved";
|
|
75
|
+
const ERR_TARGET_NOT_DIRECTORY = "Target entry is not a directory";
|
|
76
|
+
const ERR_PARENT_NOT_DIRECTORY = "Parent entry is not a directory";
|
|
86
77
|
const ERR_READABLE_CONSUMED = "Readable stream already consumed";
|
|
87
78
|
const DUPLICATES_THROW = "throw";
|
|
88
79
|
const DUPLICATES_KEEP_FIRST = "keep-first";
|
|
@@ -90,22 +81,8 @@ const DUPLICATES_KEEP_LAST = "keep-last";
|
|
|
90
81
|
const DUPLICATES_VALUES = new Set([DUPLICATES_THROW, DUPLICATES_KEEP_FIRST, DUPLICATES_KEEP_LAST]);
|
|
91
82
|
const ERR_INVALID_PASS_THROUGH = "Invalid passThrough option (use readerOptions.passThrough or set uncompressedSize for each entry)";
|
|
92
83
|
const ERR_INVALID_READER_OPTIONS = "Invalid readerOptions (must be an object)";
|
|
93
|
-
const
|
|
84
|
+
const ERR_UNSUPPORTED_PASS_THROUGH_VALUE = "The 'compressed' passThrough option is only supported by Entry#getData() and ZipWriter#add()";
|
|
94
85
|
const ERR_ABORT_EXPORT = "zipjs-abort-export";
|
|
95
|
-
const INFOZIP_EXTRA_FIELD_TYPE = "infozip";
|
|
96
|
-
const INTERPRETED_EXTRA_FIELD_TYPES = new Set([
|
|
97
|
-
EXTRAFIELD_TYPE_ZIP64,
|
|
98
|
-
EXTRAFIELD_TYPE_AES,
|
|
99
|
-
EXTRAFIELD_TYPE_NTFS,
|
|
100
|
-
EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP,
|
|
101
|
-
EXTRAFIELD_TYPE_UNICODE_PATH,
|
|
102
|
-
EXTRAFIELD_TYPE_UNICODE_COMMENT,
|
|
103
|
-
EXTRAFIELD_TYPE_USDZ,
|
|
104
|
-
EXTRAFIELD_TYPE_INFOZIP,
|
|
105
|
-
EXTRAFIELD_TYPE_UNIX,
|
|
106
|
-
EXTRAFIELD_TYPE_UNIX_TYPE1,
|
|
107
|
-
EXTRAFIELD_TYPE_PKWARE_UNIX
|
|
108
|
-
]);
|
|
109
86
|
|
|
110
87
|
class ZipEntry {
|
|
111
88
|
|
|
@@ -237,7 +214,7 @@ class ZipFileEntry extends ZipEntry {
|
|
|
237
214
|
}));
|
|
238
215
|
return true;
|
|
239
216
|
} catch (error) {
|
|
240
|
-
if (error.message == ERR_INVALID_PASSWORD) {
|
|
217
|
+
if (isErrorObject(error) && error.message == ERR_INVALID_PASSWORD) {
|
|
241
218
|
return false;
|
|
242
219
|
} else {
|
|
243
220
|
throw error;
|
|
@@ -477,6 +454,7 @@ class ZipDirectoryEntry extends ZipEntry {
|
|
|
477
454
|
await initStream(reader);
|
|
478
455
|
zipReader = new ZipReader(reader, options);
|
|
479
456
|
}
|
|
457
|
+
checkPassThroughValue(options.passThrough);
|
|
480
458
|
const duplicates = checkDuplicatesOption(options.duplicates);
|
|
481
459
|
const importedEntries = [];
|
|
482
460
|
const entries = await zipReader.getEntries(options);
|
|
@@ -567,18 +545,25 @@ class ZipDirectoryEntry extends ZipEntry {
|
|
|
567
545
|
|
|
568
546
|
async exportZip(writer, options = {}) {
|
|
569
547
|
const zipEntry = this;
|
|
548
|
+
const zipWriterProvided = Boolean(writer) && typeof writer.add == FUNCTION_TYPE;
|
|
570
549
|
options = Object.assign({}, options);
|
|
571
|
-
if (options.bufferedWrite === UNDEFINED_VALUE) {
|
|
550
|
+
if (!zipWriterProvided && options.bufferedWrite === UNDEFINED_VALUE) {
|
|
572
551
|
options.bufferedWrite = true;
|
|
573
552
|
}
|
|
574
|
-
const [readers] = await Promise.all([
|
|
575
|
-
|
|
553
|
+
const [readers] = await Promise.all([
|
|
554
|
+
initReaders(zipEntry, checkReaderOptions(options.readerOptions)),
|
|
555
|
+
zipWriterProvided ? UNDEFINED_VALUE : initStream(writer)
|
|
556
|
+
]);
|
|
557
|
+
const zipWriter = zipWriterProvided ? writer : new ZipWriter(writer, options);
|
|
576
558
|
await exportZip(zipWriter, zipEntry, getTotalSize([zipEntry], getUncompressedSize), options, readers);
|
|
559
|
+
if (zipWriterProvided) {
|
|
560
|
+
return zipWriter;
|
|
561
|
+
}
|
|
577
562
|
await zipWriter.close(options.globalComment);
|
|
578
563
|
return writer.getData ? writer.getData() : writer.writable;
|
|
579
564
|
}
|
|
580
565
|
|
|
581
|
-
getExportedSize(options = {}) {
|
|
566
|
+
async getExportedSize(options = {}) {
|
|
582
567
|
const zipEntry = this;
|
|
583
568
|
options = Object.assign({}, options);
|
|
584
569
|
checkReaderOptions(options.readerOptions);
|
|
@@ -593,7 +578,7 @@ class ZipDirectoryEntry extends ZipEntry {
|
|
|
593
578
|
const writeOrderGuaranteed = !options.bufferedWrite ||
|
|
594
579
|
(entries.every(entry => entry.options.keepOrder !== false) &&
|
|
595
580
|
children.every(child => isImplicitDirectory(child) || !child.children.length));
|
|
596
|
-
return getEntriesSize(options, entries, writeOrderGuaranteed, options.globalComment);
|
|
581
|
+
return await getEntriesSize(options, entries, writeOrderGuaranteed, options.globalComment);
|
|
597
582
|
}
|
|
598
583
|
|
|
599
584
|
getChildByName(name) {
|
|
@@ -654,7 +639,7 @@ class ZipFS {
|
|
|
654
639
|
|
|
655
640
|
move(entry, destination) {
|
|
656
641
|
if (entry == this.root) {
|
|
657
|
-
throw new Error(
|
|
642
|
+
throw new Error(ERR_ROOT_DIRECTORY_NOT_MOVABLE);
|
|
658
643
|
} else {
|
|
659
644
|
if (destination.directory) {
|
|
660
645
|
if (!destination.isDescendantOf(entry)) {
|
|
@@ -675,7 +660,7 @@ class ZipFS {
|
|
|
675
660
|
throw new Error(ERR_ANCESTOR_ENTRY);
|
|
676
661
|
}
|
|
677
662
|
} else {
|
|
678
|
-
throw new Error(
|
|
663
|
+
throw new Error(ERR_TARGET_NOT_DIRECTORY);
|
|
679
664
|
}
|
|
680
665
|
}
|
|
681
666
|
}
|
|
@@ -807,7 +792,7 @@ class ZipFS {
|
|
|
807
792
|
}
|
|
808
793
|
|
|
809
794
|
const fs = { FS: ZipFS, ZipDirectoryEntry, ZipFileEntry };
|
|
810
|
-
export { ZipFS, ZipEntry, ZipFileEntry, ZipDirectoryEntry, fs, ERR_ENTRY_EXISTS, ERR_DUPLICATE_IMPORTED_ENTRY, ERR_INVALID_DUPLICATES, ERR_READABLE_CONSUMED, ERR_INVALID_PASS_THROUGH, ERR_INVALID_READER_OPTIONS,
|
|
795
|
+
export { ZipFS, ZipEntry, ZipFileEntry, ZipDirectoryEntry, fs, ERR_ENTRY_EXISTS, ERR_DUPLICATE_IMPORTED_ENTRY, ERR_INVALID_DUPLICATES, ERR_READABLE_CONSUMED, ERR_INVALID_PASS_THROUGH, ERR_UNSUPPORTED_PASS_THROUGH_VALUE, ERR_INVALID_READER_OPTIONS, ERR_ANCESTOR_ENTRY, ERR_ROOT_DIRECTORY_NOT_MOVABLE, ERR_TARGET_NOT_DIRECTORY, ERR_PARENT_NOT_DIRECTORY };
|
|
811
796
|
|
|
812
797
|
function getTotalSize(entries, getEntrySize) {
|
|
813
798
|
let size = 0;
|
|
@@ -989,70 +974,11 @@ function getChildEntryOptions(child, selectedEntry, options) {
|
|
|
989
974
|
let zipEntryMetadata = {};
|
|
990
975
|
let passThroughOptions = {};
|
|
991
976
|
if (child.data instanceof Entry) {
|
|
992
|
-
const
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
rawLastModDate,
|
|
998
|
-
creationDate,
|
|
999
|
-
lastAccessDate,
|
|
1000
|
-
uncompressedSize,
|
|
1001
|
-
encrypted,
|
|
1002
|
-
zipCrypto,
|
|
1003
|
-
crc32,
|
|
1004
|
-
compressionMethod,
|
|
1005
|
-
extraFieldAES,
|
|
1006
|
-
internalFileAttributes,
|
|
1007
|
-
extraField,
|
|
1008
|
-
bitFlag,
|
|
1009
|
-
uid,
|
|
1010
|
-
gid
|
|
1011
|
-
} = child.data;
|
|
1012
|
-
zipEntryMetadata = {
|
|
1013
|
-
externalFileAttributes,
|
|
1014
|
-
versionMadeBy,
|
|
1015
|
-
comment,
|
|
1016
|
-
lastModDate,
|
|
1017
|
-
creationDate,
|
|
1018
|
-
lastAccessDate,
|
|
1019
|
-
internalFileAttributes
|
|
1020
|
-
};
|
|
1021
|
-
const userExtraField = getUserExtraField(extraField);
|
|
1022
|
-
if (userExtraField) {
|
|
1023
|
-
zipEntryMetadata.extraField = userExtraField;
|
|
1024
|
-
}
|
|
1025
|
-
if (uid !== UNDEFINED_VALUE || gid !== UNDEFINED_VALUE) {
|
|
1026
|
-
Object.assign(zipEntryMetadata, {
|
|
1027
|
-
uid,
|
|
1028
|
-
gid,
|
|
1029
|
-
unixExtraFieldType: INFOZIP_EXTRA_FIELD_TYPE
|
|
1030
|
-
});
|
|
1031
|
-
}
|
|
1032
|
-
if (isPassThrough(child, options)) {
|
|
1033
|
-
let encryptionStrength;
|
|
1034
|
-
if (extraFieldAES) {
|
|
1035
|
-
encryptionStrength = extraFieldAES.strength;
|
|
1036
|
-
}
|
|
1037
|
-
passThroughOptions = {
|
|
1038
|
-
passThrough: true,
|
|
1039
|
-
encrypted,
|
|
1040
|
-
zipCrypto,
|
|
1041
|
-
crc32,
|
|
1042
|
-
uncompressedSize,
|
|
1043
|
-
encryptionStrength,
|
|
1044
|
-
compressionMethod
|
|
1045
|
-
};
|
|
1046
|
-
if (bitFlag) {
|
|
1047
|
-
passThroughOptions.dataDescriptor = bitFlag.dataDescriptor;
|
|
1048
|
-
}
|
|
1049
|
-
const lastModDateOverride = childOptions.lastModDate === UNDEFINED_VALUE ? options.lastModDate : childOptions.lastModDate;
|
|
1050
|
-
if (lastModDateOverride === UNDEFINED_VALUE) {
|
|
1051
|
-
passThroughOptions.rawLastModDate = rawLastModDate;
|
|
1052
|
-
} else if (zipCrypto && (!bitFlag || bitFlag.dataDescriptor) && lastModDateOverride instanceof Date &&
|
|
1053
|
-
getDosTimeHighByte(lastModDateOverride) != ((rawLastModDate >>> 8) & MAX_8_BITS)) {
|
|
1054
|
-
throw new Error(ERR_ZIP_CRYPTO_LAST_MOD_DATE);
|
|
1055
|
-
}
|
|
977
|
+
const passThrough = isPassThrough(child, options);
|
|
978
|
+
const lastModDateOverride = childOptions.lastModDate === UNDEFINED_VALUE ? options.lastModDate : childOptions.lastModDate;
|
|
979
|
+
({ entryOptions: zipEntryMetadata, passThroughOptions } = getSourceEntryOptions(child.data, passThrough, lastModDateOverride));
|
|
980
|
+
if (passThrough) {
|
|
981
|
+
passThroughOptions.passThrough = true;
|
|
1056
982
|
}
|
|
1057
983
|
}
|
|
1058
984
|
const entryOptions = Object.assign({ lastModDate: child.defaultLastModDate }, zipEntryMetadata, options, childOptions, passThroughOptions, { directory: child.directory });
|
|
@@ -1062,16 +988,6 @@ function getChildEntryOptions(child, selectedEntry, options) {
|
|
|
1062
988
|
return { name, entryOptions };
|
|
1063
989
|
}
|
|
1064
990
|
|
|
1065
|
-
function getDosTimeHighByte(lastModDate) {
|
|
1066
|
-
let dosLastModDate = new Date(Math.ceil(Math.floor(lastModDate.getTime() / 1000) / 2) * 2000);
|
|
1067
|
-
if (dosLastModDate < MIN_DATE) {
|
|
1068
|
-
dosLastModDate = MIN_DATE;
|
|
1069
|
-
} else if (dosLastModDate > MAX_DATE) {
|
|
1070
|
-
dosLastModDate = MAX_DATE;
|
|
1071
|
-
}
|
|
1072
|
-
return ((dosLastModDate.getHours() << 3) | (dosLastModDate.getMinutes() >> 3)) & MAX_8_BITS;
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
991
|
function getDeterminedSize(child, passThrough) {
|
|
1076
992
|
const { reader } = child;
|
|
1077
993
|
if (reader && reader.size !== UNDEFINED_VALUE) {
|
|
@@ -1084,9 +1000,18 @@ function checkReaderOptions(readerOptions) {
|
|
|
1084
1000
|
if (readerOptions && (typeof readerOptions != OBJECT_TYPE || Array.isArray(readerOptions))) {
|
|
1085
1001
|
throw new Error(ERR_INVALID_READER_OPTIONS);
|
|
1086
1002
|
}
|
|
1003
|
+
if (readerOptions) {
|
|
1004
|
+
checkPassThroughValue(readerOptions.passThrough);
|
|
1005
|
+
}
|
|
1087
1006
|
return readerOptions;
|
|
1088
1007
|
}
|
|
1089
1008
|
|
|
1009
|
+
function checkPassThroughValue(passThrough) {
|
|
1010
|
+
if (checkPassThroughOption(passThrough) === PASS_THROUGH_COMPRESSED) {
|
|
1011
|
+
throw new Error(ERR_UNSUPPORTED_PASS_THROUGH_VALUE);
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1090
1015
|
function replaceContent(zipEntry, params) {
|
|
1091
1016
|
const { uncompressedSize } = params;
|
|
1092
1017
|
Object.assign(zipEntry, params, {
|
|
@@ -1115,20 +1040,6 @@ function isImplicitDirectory(child) {
|
|
|
1115
1040
|
return child.directory && child.data === null;
|
|
1116
1041
|
}
|
|
1117
1042
|
|
|
1118
|
-
function getUserExtraField(extraField) {
|
|
1119
|
-
if (extraField) {
|
|
1120
|
-
const userExtraField = new Map();
|
|
1121
|
-
extraField.forEach((field, type) => {
|
|
1122
|
-
if (!INTERPRETED_EXTRA_FIELD_TYPES.has(type)) {
|
|
1123
|
-
userExtraField.set(type, field.data);
|
|
1124
|
-
}
|
|
1125
|
-
});
|
|
1126
|
-
if (userExtraField.size) {
|
|
1127
|
-
return userExtraField;
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
}
|
|
1131
|
-
|
|
1132
1043
|
async function exportZip(zipWriter, entry, totalSize, options, readers) {
|
|
1133
1044
|
const { onstart, onprogress, onend, onentryprogress } = options;
|
|
1134
1045
|
const selectedEntry = entry;
|
|
@@ -1231,6 +1142,7 @@ function addFileSystemHandle(zipEntry, handle, options) {
|
|
|
1231
1142
|
|
|
1232
1143
|
async function exportFileSystemHandle(zipEntry, directoryHandle, options) {
|
|
1233
1144
|
const { onstart, onprogress, onend } = options;
|
|
1145
|
+
checkPassThroughValue(options.passThrough);
|
|
1234
1146
|
const readerOptions = checkReaderOptions(options.readerOptions);
|
|
1235
1147
|
const abortController = new AbortController();
|
|
1236
1148
|
const { signal } = abortController;
|
|
@@ -1306,7 +1218,7 @@ async function exportFileSystemHandle(zipEntry, directoryHandle, options) {
|
|
|
1306
1218
|
if (exportAborted || isExportAborted(signal.reason)) {
|
|
1307
1219
|
return;
|
|
1308
1220
|
}
|
|
1309
|
-
|
|
1221
|
+
throwIfAborted(signal);
|
|
1310
1222
|
}
|
|
1311
1223
|
try {
|
|
1312
1224
|
if (child.directory) {
|
|
@@ -1439,7 +1351,7 @@ function addChild(parent, name, params, directory) {
|
|
|
1439
1351
|
}
|
|
1440
1352
|
return directory ? new ZipDirectoryEntry(parent.fs, name, params, parent) : new ZipFileEntry(parent.fs, name, params, parent);
|
|
1441
1353
|
} else {
|
|
1442
|
-
throw new Error(
|
|
1354
|
+
throw new Error(ERR_PARENT_NOT_DIRECTORY);
|
|
1443
1355
|
}
|
|
1444
1356
|
}
|
|
1445
1357
|
|
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,7 +121,9 @@ 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";
|
|
126
|
+
import { isErrorObject } from "./util/error.js";
|
|
124
127
|
import { Crc32 } from "./streams/codecs/crc32.js";
|
|
125
128
|
import {
|
|
126
129
|
PROPERTY_NAME_RAW_FILENAME,
|
|
@@ -137,7 +140,8 @@ import {
|
|
|
137
140
|
PROPERTY_NAME_RAW_LAST_ACCESS_DATE,
|
|
138
141
|
PROPERTY_NAME_CREATION_DATE,
|
|
139
142
|
PROPERTY_NAME_RAW_CREATION_DATE,
|
|
140
|
-
Entry
|
|
143
|
+
Entry,
|
|
144
|
+
getEncryptionOverhead
|
|
141
145
|
} from "./zip-entry.js";
|
|
142
146
|
import {
|
|
143
147
|
OPTION_FILENAME_ENCODING,
|
|
@@ -154,6 +158,7 @@ import {
|
|
|
154
158
|
OPTION_CHECK_OVERLAPPING_ENTRY,
|
|
155
159
|
OPTION_CHECK_AMBIGUITY,
|
|
156
160
|
OPTION_CHECK_LOCAL_DIRECTORY,
|
|
161
|
+
OPTION_CHECK_LOCAL_FILENAME,
|
|
157
162
|
OPTION_CHECK_SIGNATURE,
|
|
158
163
|
OPTION_CHECK_CRC32,
|
|
159
164
|
OPTION_CHECK_AUTHENTICATION_CODE,
|
|
@@ -175,6 +180,7 @@ import {
|
|
|
175
180
|
checkSignalOption,
|
|
176
181
|
throwIfAborted,
|
|
177
182
|
checkPasswordOption,
|
|
183
|
+
checkPassThroughOption,
|
|
178
184
|
toNumber
|
|
179
185
|
} from "./options.js";
|
|
180
186
|
|
|
@@ -208,6 +214,8 @@ const WARNING_PREPENDED_CENTRAL_DIRECTORY = "prepended central directory";
|
|
|
208
214
|
const WARNING_TRAILING_CENTRAL_DIRECTORY_DATA = "trailing central directory data";
|
|
209
215
|
const WARNING_DUPLICATE_FILENAME = "duplicate filename";
|
|
210
216
|
const WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY = "mismatched zip64 end of central directory record";
|
|
217
|
+
const WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY = "multiple end of central directory records";
|
|
218
|
+
const WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME = "mismatched local file header (filename)";
|
|
211
219
|
const WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG = "mismatched local file header (general purpose bit flag)";
|
|
212
220
|
const WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD = "mismatched local file header (compression method)";
|
|
213
221
|
const WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES = "mismatched local file header (crc32 or sizes)";
|
|
@@ -278,7 +286,7 @@ class ZipReader {
|
|
|
278
286
|
}
|
|
279
287
|
}
|
|
280
288
|
if (rejectAmbiguousEndOfDirectory && endOfDirectoryReachingEndCount > 1) {
|
|
281
|
-
throwAmbiguousArchive(
|
|
289
|
+
throwAmbiguousArchive(WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY);
|
|
282
290
|
}
|
|
283
291
|
const endOfDirectoryView = getDataView(endOfDirectoryInfo);
|
|
284
292
|
let directoryDataLength = getUint32(endOfDirectoryView, 12);
|
|
@@ -576,7 +584,8 @@ class ZipReader {
|
|
|
576
584
|
const writer = new TransformStream();
|
|
577
585
|
const arrayBufferPromise = streamToBlob(writer.readable).then(blob => blob.arrayBuffer());
|
|
578
586
|
arrayBufferPromise.catch(() => { });
|
|
579
|
-
await fileEntry.getData(writer, entry, zipReader.readRanges,
|
|
587
|
+
await fileEntry.getData(writer, entry, zipReader.readRanges,
|
|
588
|
+
Object.assign({}, options, { preventClose: false }));
|
|
580
589
|
return arrayBufferPromise;
|
|
581
590
|
};
|
|
582
591
|
offset = endOffset;
|
|
@@ -657,49 +666,101 @@ class ZipReader {
|
|
|
657
666
|
await reader.readable.cancel();
|
|
658
667
|
}
|
|
659
668
|
}
|
|
669
|
+
|
|
670
|
+
[SYMBOL_ASYNC_DISPOSE]() {
|
|
671
|
+
return this.close();
|
|
672
|
+
}
|
|
660
673
|
}
|
|
661
674
|
|
|
662
675
|
class ZipReaderStream {
|
|
663
676
|
|
|
664
677
|
constructor(options = {}) {
|
|
665
|
-
|
|
666
|
-
const
|
|
678
|
+
let sourceController;
|
|
679
|
+
const { readable, writable } = new TransformStream({
|
|
680
|
+
start(controller) {
|
|
681
|
+
sourceController = controller;
|
|
682
|
+
}
|
|
683
|
+
});
|
|
684
|
+
const zipReader = new ZipReader(readable, options);
|
|
685
|
+
const gen = zipReader.getEntriesGenerator();
|
|
686
|
+
const pendingEntries = new Set();
|
|
667
687
|
this.readable = new ReadableStream({
|
|
668
688
|
async pull(controller) {
|
|
669
689
|
const { done, value } = await gen.next();
|
|
670
690
|
if (done)
|
|
671
691
|
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
|
-
})();
|
|
692
|
+
const entryStream = createEntryStream(value, pendingEntries);
|
|
691
693
|
const chunk = {
|
|
692
694
|
...value,
|
|
693
|
-
readable:
|
|
695
|
+
readable: entryStream.readable
|
|
694
696
|
};
|
|
695
697
|
delete chunk.getData;
|
|
698
|
+
Object.defineProperties(chunk, {
|
|
699
|
+
localDirectory: {
|
|
700
|
+
get: () => value.localDirectory,
|
|
701
|
+
enumerable: true
|
|
702
|
+
},
|
|
703
|
+
warnings: {
|
|
704
|
+
get: () => value.warnings,
|
|
705
|
+
enumerable: true
|
|
706
|
+
}
|
|
707
|
+
});
|
|
696
708
|
controller.enqueue(chunk);
|
|
709
|
+
},
|
|
710
|
+
async cancel(reason) {
|
|
711
|
+
const entryStreams = Array.from(pendingEntries);
|
|
712
|
+
pendingEntries.clear();
|
|
713
|
+
sourceController.error(reason);
|
|
714
|
+
await Promise.allSettled(entryStreams.map(entryStream => entryStream.cancel(reason)));
|
|
715
|
+
await Promise.allSettled([gen.return(), zipReader.close()]);
|
|
697
716
|
}
|
|
698
717
|
});
|
|
699
718
|
this.writable = writable;
|
|
700
719
|
}
|
|
701
720
|
}
|
|
702
721
|
|
|
722
|
+
function createEntryStream(entry, pendingEntries) {
|
|
723
|
+
const { readable, writable } = new TransformStream();
|
|
724
|
+
let dataReader;
|
|
725
|
+
const entryStream = {
|
|
726
|
+
cancel: async reason => {
|
|
727
|
+
pendingEntries.delete(entryStream);
|
|
728
|
+
await (dataReader ? dataReader.cancel(reason) : readable.cancel(reason));
|
|
729
|
+
}
|
|
730
|
+
};
|
|
731
|
+
entryStream.readable = new ReadableStream({
|
|
732
|
+
async pull(controller) {
|
|
733
|
+
if (!dataReader) {
|
|
734
|
+
dataReader = readable.getReader();
|
|
735
|
+
pendingEntries.add(entryStream);
|
|
736
|
+
getData();
|
|
737
|
+
}
|
|
738
|
+
const { done, value } = await dataReader.read();
|
|
739
|
+
if (done) {
|
|
740
|
+
controller.close();
|
|
741
|
+
} else {
|
|
742
|
+
controller.enqueue(value);
|
|
743
|
+
}
|
|
744
|
+
},
|
|
745
|
+
cancel: reason => entryStream.cancel(reason)
|
|
746
|
+
}, { highWaterMark: 0 });
|
|
747
|
+
return entryStream;
|
|
748
|
+
|
|
749
|
+
async function getData() {
|
|
750
|
+
try {
|
|
751
|
+
await entry.getData(writable, { preventClose: false });
|
|
752
|
+
} catch (error) {
|
|
753
|
+
try {
|
|
754
|
+
await writable.abort(error);
|
|
755
|
+
} catch {
|
|
756
|
+
// ignored
|
|
757
|
+
}
|
|
758
|
+
} finally {
|
|
759
|
+
pendingEntries.delete(entryStream);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
703
764
|
async function isZipFile(reader, options = {}) {
|
|
704
765
|
reader = new GenericReader(reader);
|
|
705
766
|
await initStream(reader);
|
|
@@ -763,6 +824,8 @@ export {
|
|
|
763
824
|
WARNING_TRAILING_CENTRAL_DIRECTORY_DATA,
|
|
764
825
|
WARNING_DUPLICATE_FILENAME,
|
|
765
826
|
WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY,
|
|
827
|
+
WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY,
|
|
828
|
+
WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME,
|
|
766
829
|
WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG,
|
|
767
830
|
WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD,
|
|
768
831
|
WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES
|
|
@@ -805,7 +868,9 @@ class ZipEntry {
|
|
|
805
868
|
const dataView = getDataView(dataArray);
|
|
806
869
|
let password = getOptionValue(zipEntry, options, OPTION_PASSWORD);
|
|
807
870
|
let rawPassword = getOptionValue(zipEntry, options, OPTION_RAW_PASSWORD);
|
|
808
|
-
const passThrough = getOptionValue(zipEntry, options, OPTION_PASS_THROUGH);
|
|
871
|
+
const passThrough = checkPassThroughOption(getOptionValue(zipEntry, options, OPTION_PASS_THROUGH));
|
|
872
|
+
const passThroughCompression = Boolean(passThrough);
|
|
873
|
+
const passThroughEncryption = passThrough === true;
|
|
809
874
|
checkPasswordOption(password, rawPassword);
|
|
810
875
|
password = password && password.length && password;
|
|
811
876
|
rawPassword = rawPassword && rawPassword.length && rawPassword;
|
|
@@ -826,7 +891,9 @@ class ZipEntry {
|
|
|
826
891
|
const checkLocalDirectoryOption = getOptionValue(zipEntry, options, OPTION_CHECK_LOCAL_DIRECTORY);
|
|
827
892
|
const entryStrictness = getStrictness(options, zipEntry.options);
|
|
828
893
|
const checkLocalDirectory = getCheckLocalDirectory(checkLocalDirectoryOption, entryStrictness);
|
|
829
|
-
const
|
|
894
|
+
const checkLocalFilenameOption = getOptionValue(zipEntry, options, OPTION_CHECK_LOCAL_FILENAME);
|
|
895
|
+
const checkLocalFilename = getCheckLocalFilename(
|
|
896
|
+
checkLocalFilenameOption === UNDEFINED_VALUE ? checkLocalDirectoryOption : checkLocalFilenameOption, entryStrictness);
|
|
830
897
|
let rawLocalFilename = EMPTY_UINT8_ARRAY;
|
|
831
898
|
if (checkLocalFilename && (filenameLength || extraFieldLength)) {
|
|
832
899
|
const trailingDataArray = await readUint8Array(reader, localHeaderOffset + HEADER_SIZE, filenameLength + extraFieldLength);
|
|
@@ -857,16 +924,17 @@ class ZipEntry {
|
|
|
857
924
|
if (gid !== UNDEFINED_VALUE && fileEntry.gid === UNDEFINED_VALUE) {
|
|
858
925
|
fileEntry.gid = gid;
|
|
859
926
|
}
|
|
860
|
-
const
|
|
927
|
+
const checkPasswordOnly = getOptionValue(zipEntry, options, OPTION_CHECK_PASSWORD_ONLY);
|
|
928
|
+
const encrypted = zipEntry.encrypted && localDirectory.encrypted && (!passThroughEncryption || checkPasswordOnly);
|
|
861
929
|
const zipCrypto = encrypted && !extraFieldAES;
|
|
862
|
-
if (!
|
|
930
|
+
if (!passThroughEncryption) {
|
|
863
931
|
fileEntry.zipCrypto = zipCrypto;
|
|
864
932
|
}
|
|
865
933
|
if (encrypted && (localDirectory.rawBitFlag & BITFLAG_STRONG_ENCRYPTION) == BITFLAG_STRONG_ENCRYPTION) {
|
|
866
934
|
throw new Error(ERR_UNSUPPORTED_ENCRYPTION);
|
|
867
935
|
}
|
|
868
|
-
const registeredCodec =
|
|
869
|
-
if (compressionMethod != COMPRESSION_METHOD_STORE && compressionMethod != COMPRESSION_METHOD_DEFLATE && compressionMethod != COMPRESSION_METHOD_DEFLATE_64 && !registeredCodec && !
|
|
936
|
+
const registeredCodec = passThroughCompression ? UNDEFINED_VALUE : getRegisteredCodec(compressionMethod);
|
|
937
|
+
if (compressionMethod != COMPRESSION_METHOD_STORE && compressionMethod != COMPRESSION_METHOD_DEFLATE && compressionMethod != COMPRESSION_METHOD_DEFLATE_64 && !registeredCodec && !passThroughCompression) {
|
|
870
938
|
throw new Error(ERR_UNSUPPORTED_COMPRESSION);
|
|
871
939
|
}
|
|
872
940
|
if (encrypted) {
|
|
@@ -883,15 +951,16 @@ class ZipEntry {
|
|
|
883
951
|
const readable = toCompatibleReadable(reader.createReadable({ offset: dataOffset, size }));
|
|
884
952
|
const signal = checkSignalOption(getOptionValue(zipEntry, options, OPTION_SIGNAL));
|
|
885
953
|
throwIfAborted(signal);
|
|
886
|
-
const checkPasswordOnly = getOptionValue(zipEntry, options, OPTION_CHECK_PASSWORD_ONLY);
|
|
887
954
|
let checkOverlappingEntry = getOptionValue(zipEntry, options, OPTION_CHECK_OVERLAPPING_ENTRY);
|
|
888
955
|
const checkOverlappingEntryOnly = getOptionValue(zipEntry, options, OPTION_CHECK_OVERLAPPING_ENTRY_ONLY);
|
|
889
956
|
if (checkOverlappingEntryOnly) {
|
|
890
957
|
checkOverlappingEntry = true;
|
|
891
958
|
}
|
|
892
959
|
const { onstart, onprogress, onend } = options;
|
|
893
|
-
const compressed = compressionMethod != COMPRESSION_METHOD_STORE && !
|
|
894
|
-
const outputSize =
|
|
960
|
+
const compressed = compressionMethod != COMPRESSION_METHOD_STORE && !passThroughCompression;
|
|
961
|
+
const outputSize = passThroughCompression ?
|
|
962
|
+
compressedSize - getEncryptionOverhead(encrypted, zipCrypto, extraFieldAES && extraFieldAES.strength) :
|
|
963
|
+
uncompressedSize;
|
|
895
964
|
const deflate64 = compressionMethod == COMPRESSION_METHOD_DEFLATE_64;
|
|
896
965
|
let useCompressionStream = getOptionValue(zipEntry, options, OPTION_USE_COMPRESSION_STREAM);
|
|
897
966
|
if (deflate64) {
|
|
@@ -900,7 +969,7 @@ class ZipEntry {
|
|
|
900
969
|
const checkCrc32Option = getOptionValue(zipEntry, options, OPTION_CHECK_CRC32);
|
|
901
970
|
const checkCrc32 = (checkCrc32Option === UNDEFINED_VALUE ?
|
|
902
971
|
getOptionValue(zipEntry, options, OPTION_CHECK_SIGNATURE) :
|
|
903
|
-
checkCrc32Option) && !
|
|
972
|
+
checkCrc32Option) && !passThroughCompression &&
|
|
904
973
|
(!encrypted || zipCrypto || (extraFieldAES && extraFieldAES.vendorVersion == VENDOR_VERSION_AE_1));
|
|
905
974
|
const workerOptions = {
|
|
906
975
|
options: {
|
|
@@ -944,7 +1013,7 @@ class ZipEntry {
|
|
|
944
1013
|
readRanges
|
|
945
1014
|
});
|
|
946
1015
|
}
|
|
947
|
-
let writable, abortError;
|
|
1016
|
+
let writable, abortError, aborted;
|
|
948
1017
|
try {
|
|
949
1018
|
if (!checkOverlappingEntryOnly) {
|
|
950
1019
|
if (checkPasswordOnly) {
|
|
@@ -954,24 +1023,28 @@ class ZipEntry {
|
|
|
954
1023
|
await initStream(writer, getDecodableOutputSize(outputSize, compressedSize, compressed));
|
|
955
1024
|
({ writable } = writer);
|
|
956
1025
|
const { outputSize: writtenSize } = await runWorker({ readable, writable }, workerOptions);
|
|
957
|
-
writer.size += writtenSize;
|
|
958
1026
|
if (writtenSize != outputSize) {
|
|
959
|
-
throw new Error(ERR_INVALID_UNCOMPRESSED_SIZE);
|
|
1027
|
+
throw Object.assign(new Error(ERR_INVALID_UNCOMPRESSED_SIZE), { outputSize: writtenSize });
|
|
960
1028
|
}
|
|
1029
|
+
writer.size += writtenSize;
|
|
961
1030
|
}
|
|
962
1031
|
} catch (error) {
|
|
963
|
-
|
|
1032
|
+
const { outputSize: failedOutputSize } = workerOptions;
|
|
1033
|
+
if (failedOutputSize !== UNDEFINED_VALUE) {
|
|
1034
|
+
writer.size += failedOutputSize;
|
|
1035
|
+
} else if (isErrorObject(error) && error.outputSize !== UNDEFINED_VALUE) {
|
|
964
1036
|
writer.size += error.outputSize;
|
|
965
1037
|
}
|
|
966
|
-
if (!checkPasswordOnly || error.message != ERR_ABORT_CHECK_PASSWORD) {
|
|
1038
|
+
if (!checkPasswordOnly || !isErrorObject(error) || error.message != ERR_ABORT_CHECK_PASSWORD) {
|
|
967
1039
|
abortError = error;
|
|
1040
|
+
aborted = true;
|
|
968
1041
|
throw error;
|
|
969
1042
|
}
|
|
970
1043
|
} finally {
|
|
971
1044
|
const preventClose = !ownsWritable(writer) && getOptionValue(zipEntry, options, OPTION_PREVENT_CLOSE);
|
|
972
1045
|
if (!preventClose && writable && !writable.locked) {
|
|
973
1046
|
const writableWriter = writable.getWriter();
|
|
974
|
-
if (
|
|
1047
|
+
if (aborted) {
|
|
975
1048
|
try {
|
|
976
1049
|
await writableWriter.abort(abortError);
|
|
977
1050
|
} catch {
|
|
@@ -1623,7 +1696,7 @@ function validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, chec
|
|
|
1623
1696
|
if (checkLocalFilename && !maskedLocalDirectory &&
|
|
1624
1697
|
(rawLocalFilename.length != rawFilename.length ||
|
|
1625
1698
|
rawLocalFilename.some((byteValue, indexByte) => byteValue != rawFilename[indexByte]))) {
|
|
1626
|
-
reportAmbiguity(reject, warnings,
|
|
1699
|
+
reportAmbiguity(reject, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_FILENAME);
|
|
1627
1700
|
}
|
|
1628
1701
|
if ((localDirectory.rawBitFlag & BITFLAG_AMBIGUITY_MASK) != (zipEntry.rawBitFlag & BITFLAG_AMBIGUITY_MASK)) {
|
|
1629
1702
|
reportAmbiguity(reject, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG);
|
|
@@ -1648,16 +1721,6 @@ function reportAmbiguity(reject, warnings, reason) {
|
|
|
1648
1721
|
}
|
|
1649
1722
|
}
|
|
1650
1723
|
|
|
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
1724
|
function throwAmbiguousArchive(reason) {
|
|
1662
1725
|
const error = new Error(ERR_AMBIGUOUS_ARCHIVE);
|
|
1663
1726
|
error.reason = reason;
|