@zip.js/zip.js 2.8.26 → 2.8.28
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/README.md +2 -2
- package/deno.json +1 -1
- package/dist/zip-core.js +1050 -569
- package/dist/zip-core.min.js +1 -1
- package/dist/zip-fs-core.js +1146 -617
- package/dist/zip-fs-core.min.js +1 -1
- package/dist/zip-fs-native.js +1207 -627
- package/dist/zip-fs-native.min.js +1 -1
- package/dist/zip-fs.js +1279 -681
- package/dist/zip-fs.min.js +1 -1
- package/dist/zip-legacy.js +1051 -570
- package/dist/zip-legacy.min.js +1 -1
- package/dist/zip-module.wasm +0 -0
- package/dist/zip-native.js +1053 -572
- 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 +1125 -626
- package/dist/zip.min.js +1 -1
- package/eslint.config.mjs +6 -1
- package/index-native.cjs +1207 -627
- package/index-native.min.js +1 -1
- package/index.cjs +1279 -681
- package/index.d.ts +2451 -2355
- package/index.min.js +1 -1
- package/lib/core/codec-pool.js +39 -1
- package/lib/core/codec-worker.js +120 -34
- package/lib/core/configuration.js +3 -0
- package/lib/core/constants.js +6 -0
- package/lib/core/io.js +55 -25
- package/lib/core/options.js +2 -0
- package/lib/core/streams/aes-crypto-stream.js +12 -12
- package/lib/core/streams/codec-stream.js +11 -5
- package/lib/core/streams/codecs/sjcl.js +1 -33
- package/lib/core/streams/common-crypto.js +4 -4
- package/lib/core/streams/zip-crypto-stream.js +25 -15
- package/lib/core/streams/zip-entry-stream.js +35 -1
- package/lib/core/streams/zlib-js/zlib-streams.min.js +1 -1
- package/lib/core/streams/zlib-wasm/zlib-streams.js +73 -55
- package/lib/core/streams/zlib-wasm/zlib-streams.wasm +0 -0
- package/lib/core/util/decode-cp437.js +1 -1
- package/lib/core/util/decode-text.js +2 -1
- package/lib/core/web-worker-base.js +12 -4
- package/lib/core/web-worker-inline-native.js +1 -1
- package/lib/core/web-worker-inline-wasm.js +1 -1
- package/lib/core/zip-fs.js +154 -55
- package/lib/core/zip-reader.js +185 -67
- package/lib/core/zip-writer.js +613 -376
- package/lib/core/zlib-streams-inline.js +1 -1
- package/lib/zip-core-reader.js +3 -1
- package/lib/zip-core-writer.js +1 -0
- package/package.json +20 -6
package/lib/core/zip-fs.js
CHANGED
|
@@ -54,12 +54,15 @@ import {
|
|
|
54
54
|
} from "./zip-entry.js";
|
|
55
55
|
import { UNDEFINED_VALUE } from "./constants.js";
|
|
56
56
|
|
|
57
|
+
const ERR_ENTRY_EXISTS = "Entry filename already exists";
|
|
58
|
+
const ERR_READABLE_CONSUMED = "Readable stream already consumed";
|
|
59
|
+
|
|
57
60
|
class ZipEntry {
|
|
58
61
|
|
|
59
62
|
constructor(fs, name, params, parent) {
|
|
60
63
|
const zipEntry = this;
|
|
61
64
|
if (fs.root && parent && parent.getChildByName(name)) {
|
|
62
|
-
throw new Error(
|
|
65
|
+
throw new Error(ERR_ENTRY_EXISTS);
|
|
63
66
|
}
|
|
64
67
|
if (!params) {
|
|
65
68
|
params = {};
|
|
@@ -69,13 +72,17 @@ class ZipEntry {
|
|
|
69
72
|
name,
|
|
70
73
|
data: params.data,
|
|
71
74
|
options: params.options,
|
|
72
|
-
id: fs.
|
|
75
|
+
id: fs.entryIdCounter++,
|
|
73
76
|
parent,
|
|
74
77
|
children: [],
|
|
75
78
|
uncompressedSize: params.uncompressedSize || 0,
|
|
76
79
|
passThrough: params.passThrough
|
|
77
80
|
});
|
|
78
|
-
|
|
81
|
+
// detached entries (e.g. clones) are not registered until they are attached,
|
|
82
|
+
// otherwise they could never be garbage collected
|
|
83
|
+
if (parent || !fs.root) {
|
|
84
|
+
fs.entries[zipEntry.id] = zipEntry;
|
|
85
|
+
}
|
|
79
86
|
if (parent) {
|
|
80
87
|
zipEntry.parent.children.push(zipEntry);
|
|
81
88
|
}
|
|
@@ -113,7 +120,7 @@ class ZipEntry {
|
|
|
113
120
|
rename(name) {
|
|
114
121
|
const parent = this.parent;
|
|
115
122
|
if (parent && parent.getChildByName(name)) {
|
|
116
|
-
throw new Error(
|
|
123
|
+
throw new Error(ERR_ENTRY_EXISTS);
|
|
117
124
|
} else {
|
|
118
125
|
this.name = name;
|
|
119
126
|
}
|
|
@@ -152,16 +159,17 @@ class ZipFileEntry extends ZipEntry {
|
|
|
152
159
|
}
|
|
153
160
|
|
|
154
161
|
isPasswordProtected() {
|
|
155
|
-
return this.data.encrypted;
|
|
162
|
+
return Boolean(this.data && this.data.encrypted);
|
|
156
163
|
}
|
|
157
164
|
|
|
158
165
|
async checkPassword(password, options = {}) {
|
|
159
166
|
const zipEntry = this;
|
|
160
167
|
if (zipEntry.isPasswordProtected()) {
|
|
161
|
-
options.password = password;
|
|
162
|
-
options.checkPasswordOnly = true;
|
|
163
168
|
try {
|
|
164
|
-
await zipEntry.data.getData(null, options
|
|
169
|
+
await zipEntry.data.getData(null, Object.assign({}, options, {
|
|
170
|
+
password,
|
|
171
|
+
checkPasswordOnly: true
|
|
172
|
+
}));
|
|
165
173
|
return true;
|
|
166
174
|
} catch (error) {
|
|
167
175
|
if (error.message == ERR_INVALID_PASSWORD) {
|
|
@@ -195,8 +203,15 @@ class ZipFileEntry extends ZipEntry {
|
|
|
195
203
|
return this.getData({ writable }, options);
|
|
196
204
|
}
|
|
197
205
|
|
|
198
|
-
getArrayBuffer(options) {
|
|
199
|
-
|
|
206
|
+
async getArrayBuffer(options) {
|
|
207
|
+
// this.data may be a string, a Uint8Array, a data URI or a Blob depending on how the entry
|
|
208
|
+
// was created, so retrieve the bytes through getUint8Array rather than assuming a Blob; this
|
|
209
|
+
// also applies the options (password, signal, onprogress...) like the other getData() getters
|
|
210
|
+
const array = await this.getUint8Array(options);
|
|
211
|
+
// return the backing buffer directly when the array spans it, slicing the exact bytes otherwise
|
|
212
|
+
return array.byteOffset || array.byteLength != array.buffer.byteLength
|
|
213
|
+
? array.buffer.slice(array.byteOffset, array.byteOffset + array.byteLength)
|
|
214
|
+
: array.buffer;
|
|
200
215
|
}
|
|
201
216
|
|
|
202
217
|
replaceBlob(blob) {
|
|
@@ -238,7 +253,7 @@ class ZipFileEntry extends ZipEntry {
|
|
|
238
253
|
replaceReadable(readable) {
|
|
239
254
|
Object.assign(this, {
|
|
240
255
|
data: null,
|
|
241
|
-
Reader:
|
|
256
|
+
Reader: getReadableReader(readable),
|
|
242
257
|
Writer: null,
|
|
243
258
|
reader: null
|
|
244
259
|
});
|
|
@@ -254,7 +269,7 @@ class ZipDirectoryEntry extends ZipEntry {
|
|
|
254
269
|
|
|
255
270
|
clone(deepClone) {
|
|
256
271
|
const zipEntry = this;
|
|
257
|
-
const clonedEntry = new ZipDirectoryEntry(zipEntry.fs, zipEntry.name);
|
|
272
|
+
const clonedEntry = new ZipDirectoryEntry(zipEntry.fs, zipEntry.name, zipEntry);
|
|
258
273
|
if (deepClone) {
|
|
259
274
|
clonedEntry.children = zipEntry.children.map(child => {
|
|
260
275
|
const childClone = child.clone(deepClone);
|
|
@@ -328,7 +343,7 @@ class ZipDirectoryEntry extends ZipEntry {
|
|
|
328
343
|
|
|
329
344
|
addReadable(name, readable, options = {}) {
|
|
330
345
|
return addChild(this, name, {
|
|
331
|
-
Reader:
|
|
346
|
+
Reader: getReadableReader(readable),
|
|
332
347
|
options
|
|
333
348
|
});
|
|
334
349
|
}
|
|
@@ -342,6 +357,7 @@ class ZipDirectoryEntry extends ZipEntry {
|
|
|
342
357
|
}
|
|
343
358
|
|
|
344
359
|
addFile(file, options = {}) {
|
|
360
|
+
options = Object.assign({}, options);
|
|
345
361
|
if (!options.lastModDate) {
|
|
346
362
|
options.lastModDate = new Date(file.lastModified);
|
|
347
363
|
}
|
|
@@ -408,11 +424,15 @@ class ZipDirectoryEntry extends ZipEntry {
|
|
|
408
424
|
try {
|
|
409
425
|
const path = entry.filename.split("/");
|
|
410
426
|
const name = path.pop();
|
|
411
|
-
path.forEach(
|
|
427
|
+
path.forEach(pathPart => {
|
|
412
428
|
const previousParent = parent;
|
|
413
429
|
parent = parent.getChildByName(pathPart);
|
|
414
|
-
if (
|
|
415
|
-
|
|
430
|
+
if (parent) {
|
|
431
|
+
if (!parent.directory) {
|
|
432
|
+
throw new Error(ERR_ENTRY_EXISTS);
|
|
433
|
+
}
|
|
434
|
+
} else {
|
|
435
|
+
parent = new ZipDirectoryEntry(this.fs, pathPart, { data: null }, previousParent);
|
|
416
436
|
importedEntries.push(parent);
|
|
417
437
|
}
|
|
418
438
|
});
|
|
@@ -423,6 +443,22 @@ class ZipDirectoryEntry extends ZipEntry {
|
|
|
423
443
|
uncompressedSize: entry.uncompressedSize,
|
|
424
444
|
passThrough: options.passThrough
|
|
425
445
|
}));
|
|
446
|
+
} else {
|
|
447
|
+
let directoryEntry = parent;
|
|
448
|
+
if (name) {
|
|
449
|
+
directoryEntry = parent.getChildByName(name);
|
|
450
|
+
if (directoryEntry) {
|
|
451
|
+
if (!directoryEntry.directory) {
|
|
452
|
+
throw new Error(ERR_ENTRY_EXISTS);
|
|
453
|
+
}
|
|
454
|
+
} else {
|
|
455
|
+
directoryEntry = new ZipDirectoryEntry(this.fs, name, { data: null }, parent);
|
|
456
|
+
importedEntries.push(directoryEntry);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
if (directoryEntry != this && !directoryEntry.data) {
|
|
460
|
+
directoryEntry.data = entry;
|
|
461
|
+
}
|
|
426
462
|
}
|
|
427
463
|
} catch (error) {
|
|
428
464
|
try {
|
|
@@ -438,14 +474,15 @@ class ZipDirectoryEntry extends ZipEntry {
|
|
|
438
474
|
return importedEntries;
|
|
439
475
|
}
|
|
440
476
|
|
|
441
|
-
async exportZip(writer, options) {
|
|
477
|
+
async exportZip(writer, options = {}) {
|
|
442
478
|
const zipEntry = this;
|
|
479
|
+
options = Object.assign({}, options);
|
|
443
480
|
if (options.bufferedWrite === UNDEFINED_VALUE) {
|
|
444
481
|
options.bufferedWrite = true;
|
|
445
482
|
}
|
|
446
|
-
await Promise.all([initReaders(zipEntry, options.readerOptions), initStream(writer)]);
|
|
483
|
+
const [readers] = await Promise.all([initReaders(zipEntry, options.readerOptions), initStream(writer)]);
|
|
447
484
|
const zipWriter = new ZipWriter(writer, options);
|
|
448
|
-
await exportZip(zipWriter, zipEntry, getTotalSize([zipEntry], "uncompressedSize"), options);
|
|
485
|
+
await exportZip(zipWriter, zipEntry, getTotalSize([zipEntry], "uncompressedSize"), options, readers);
|
|
449
486
|
await zipWriter.close();
|
|
450
487
|
return writer.getData ? writer.getData() : writer.writable;
|
|
451
488
|
}
|
|
@@ -491,7 +528,15 @@ class FS {
|
|
|
491
528
|
|
|
492
529
|
remove(entry) {
|
|
493
530
|
detach(entry);
|
|
494
|
-
|
|
531
|
+
const removedEntries = [entry];
|
|
532
|
+
while (removedEntries.length) {
|
|
533
|
+
const removedEntry = removedEntries.pop();
|
|
534
|
+
this.entries[removedEntry.id] = null;
|
|
535
|
+
for (const child of removedEntry.children) {
|
|
536
|
+
removedEntries.push(child);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
entry.parent = UNDEFINED_VALUE;
|
|
495
540
|
}
|
|
496
541
|
|
|
497
542
|
move(entry, destination) {
|
|
@@ -501,12 +546,17 @@ class FS {
|
|
|
501
546
|
if (destination.directory) {
|
|
502
547
|
if (!destination.isDescendantOf(entry)) {
|
|
503
548
|
if (entry != destination) {
|
|
504
|
-
|
|
505
|
-
|
|
549
|
+
const existingChild = destination.getChildByName(entry.name);
|
|
550
|
+
if (existingChild) {
|
|
551
|
+
if (existingChild != entry) {
|
|
552
|
+
throw new Error(ERR_ENTRY_EXISTS);
|
|
553
|
+
}
|
|
554
|
+
} else {
|
|
555
|
+
detach(entry);
|
|
556
|
+
entry.parent = destination;
|
|
557
|
+
destination.children.push(entry);
|
|
558
|
+
registerEntries(this, entry);
|
|
506
559
|
}
|
|
507
|
-
detach(entry);
|
|
508
|
-
entry.parent = destination;
|
|
509
|
-
destination.children.push(entry);
|
|
510
560
|
}
|
|
511
561
|
} else {
|
|
512
562
|
throw new Error("Entry is a ancestor of target entry");
|
|
@@ -523,6 +573,11 @@ class FS {
|
|
|
523
573
|
for (let index = 0; node && index < path.length; index++) {
|
|
524
574
|
node = node.getChildByName(path[index]);
|
|
525
575
|
}
|
|
576
|
+
if (!node) {
|
|
577
|
+
// exclude detached entries, e.g. clones registered in `entries` but not attached
|
|
578
|
+
node = this.entries.find(entry => entry && (entry == this.root || entry.isDescendantOf(this.root)) &&
|
|
579
|
+
entry.getRelativeName() == fullname);
|
|
580
|
+
}
|
|
526
581
|
return node;
|
|
527
582
|
}
|
|
528
583
|
|
|
@@ -604,6 +659,7 @@ class FS {
|
|
|
604
659
|
}
|
|
605
660
|
|
|
606
661
|
importZip(reader, options) {
|
|
662
|
+
resetFS(this);
|
|
607
663
|
return this.root.importZip(reader, options);
|
|
608
664
|
}
|
|
609
665
|
|
|
@@ -623,6 +679,10 @@ class FS {
|
|
|
623
679
|
return this.root.exportWritable(writable, options);
|
|
624
680
|
}
|
|
625
681
|
|
|
682
|
+
exportZip(writer, options) {
|
|
683
|
+
return this.root.exportZip(writer, options);
|
|
684
|
+
}
|
|
685
|
+
|
|
626
686
|
isPasswordProtected() {
|
|
627
687
|
return this.root.isPasswordProtected();
|
|
628
688
|
}
|
|
@@ -637,15 +697,26 @@ export { fs };
|
|
|
637
697
|
|
|
638
698
|
function getTotalSize(entries, propertyName) {
|
|
639
699
|
let size = 0;
|
|
640
|
-
|
|
700
|
+
const pendingEntries = Array.from(entries);
|
|
701
|
+
while (pendingEntries.length) {
|
|
702
|
+
const entry = pendingEntries.pop();
|
|
703
|
+
size += entry[propertyName] || 0;
|
|
704
|
+
for (const child of entry.children) {
|
|
705
|
+
pendingEntries.push(child);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
641
708
|
return size;
|
|
709
|
+
}
|
|
642
710
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
711
|
+
function getReadableReader(readable) {
|
|
712
|
+
let consumed;
|
|
713
|
+
return function () {
|
|
714
|
+
if (consumed) {
|
|
715
|
+
throw new Error(ERR_READABLE_CONSUMED);
|
|
647
716
|
}
|
|
648
|
-
|
|
717
|
+
consumed = true;
|
|
718
|
+
return { readable };
|
|
719
|
+
};
|
|
649
720
|
}
|
|
650
721
|
|
|
651
722
|
function getZipBlobReader(options) {
|
|
@@ -660,7 +731,7 @@ function getZipBlobReader(options) {
|
|
|
660
731
|
async init() {
|
|
661
732
|
const zipBlobReader = this;
|
|
662
733
|
zipBlobReader.size = zipBlobReader.entry.uncompressedSize;
|
|
663
|
-
const data = await zipBlobReader.entry.getData(new BlobWriter(), Object.assign({},
|
|
734
|
+
const data = await zipBlobReader.entry.getData(new BlobWriter(), Object.assign({}, options, zipBlobReader.options));
|
|
664
735
|
zipBlobReader.data = data;
|
|
665
736
|
zipBlobReader.blobReader = new BlobReader(data);
|
|
666
737
|
super.init();
|
|
@@ -673,29 +744,41 @@ function getZipBlobReader(options) {
|
|
|
673
744
|
}
|
|
674
745
|
|
|
675
746
|
async function initReaders(entry, options) {
|
|
676
|
-
|
|
677
|
-
|
|
747
|
+
const fileEntries = [];
|
|
748
|
+
const pendingEntries = [entry];
|
|
749
|
+
// per-export map so concurrent exports do not share (and lock) the same readers
|
|
750
|
+
const readers = new Map();
|
|
751
|
+
while (pendingEntries.length) {
|
|
752
|
+
const pendingEntry = pendingEntries.pop();
|
|
753
|
+
for (const child of pendingEntry.children) {
|
|
678
754
|
if (child.directory) {
|
|
679
|
-
|
|
755
|
+
pendingEntries.push(child);
|
|
680
756
|
} else {
|
|
681
|
-
|
|
682
|
-
try {
|
|
683
|
-
await initStream(reader);
|
|
684
|
-
} catch (error) {
|
|
685
|
-
try {
|
|
686
|
-
error.entryId = child.id;
|
|
687
|
-
error.cause = {
|
|
688
|
-
entry: child
|
|
689
|
-
};
|
|
690
|
-
} catch {
|
|
691
|
-
// ignored
|
|
692
|
-
}
|
|
693
|
-
throw error;
|
|
694
|
-
}
|
|
695
|
-
child.uncompressedSize = reader.size;
|
|
757
|
+
fileEntries.push(child);
|
|
696
758
|
}
|
|
697
|
-
}
|
|
759
|
+
}
|
|
698
760
|
}
|
|
761
|
+
await Promise.all(fileEntries.map(async child => {
|
|
762
|
+
const reader = child.reader = new child.Reader(child.data, options);
|
|
763
|
+
readers.set(child, reader);
|
|
764
|
+
try {
|
|
765
|
+
await initStream(reader);
|
|
766
|
+
} catch (error) {
|
|
767
|
+
try {
|
|
768
|
+
error.entryId = child.id;
|
|
769
|
+
error.cause = {
|
|
770
|
+
entry: child
|
|
771
|
+
};
|
|
772
|
+
} catch {
|
|
773
|
+
// ignored
|
|
774
|
+
}
|
|
775
|
+
throw error;
|
|
776
|
+
}
|
|
777
|
+
if (reader.size !== UNDEFINED_VALUE) {
|
|
778
|
+
child.uncompressedSize = reader.size;
|
|
779
|
+
}
|
|
780
|
+
}));
|
|
781
|
+
return readers;
|
|
699
782
|
}
|
|
700
783
|
|
|
701
784
|
function detach(entry) {
|
|
@@ -709,7 +792,7 @@ function detach(entry) {
|
|
|
709
792
|
}
|
|
710
793
|
}
|
|
711
794
|
|
|
712
|
-
async function exportZip(zipWriter, entry, totalSize, options) {
|
|
795
|
+
async function exportZip(zipWriter, entry, totalSize, options, readers) {
|
|
713
796
|
const selectedEntry = entry;
|
|
714
797
|
const entryOffsets = new Map();
|
|
715
798
|
await process(zipWriter, entry);
|
|
@@ -719,7 +802,11 @@ async function exportZip(zipWriter, entry, totalSize, options) {
|
|
|
719
802
|
|
|
720
803
|
async function exportChild() {
|
|
721
804
|
if (options.bufferedWrite) {
|
|
722
|
-
await Promise.allSettled(entry.children.map(processChild));
|
|
805
|
+
const results = await Promise.allSettled(entry.children.map(processChild));
|
|
806
|
+
const errorResult = results.find(result => result.status == "rejected");
|
|
807
|
+
if (errorResult) {
|
|
808
|
+
throw errorResult.reason;
|
|
809
|
+
}
|
|
723
810
|
} else {
|
|
724
811
|
for (const child of entry.children) {
|
|
725
812
|
await processChild(child);
|
|
@@ -774,7 +861,7 @@ async function exportZip(zipWriter, entry, totalSize, options) {
|
|
|
774
861
|
});
|
|
775
862
|
}
|
|
776
863
|
}
|
|
777
|
-
await zipWriter.add(name, child
|
|
864
|
+
await zipWriter.add(name, readers.get(child), Object.assign({}, options, zipEntryOptions, childOptions, {
|
|
778
865
|
directory: child.directory,
|
|
779
866
|
onprogress: async indexProgress => {
|
|
780
867
|
if (options.onprogress) {
|
|
@@ -823,7 +910,7 @@ function addFileSystemHandle(zipEntry, handle, options) {
|
|
|
823
910
|
}
|
|
824
911
|
} catch (error) {
|
|
825
912
|
const message = error.message + (handle ? " (" + handle.name + ")" : "");
|
|
826
|
-
throw new Error(message);
|
|
913
|
+
throw new Error(message, { cause: error });
|
|
827
914
|
}
|
|
828
915
|
}
|
|
829
916
|
return addedEntries;
|
|
@@ -883,9 +970,21 @@ async function transformToFileSystemhandles(entry) {
|
|
|
883
970
|
|
|
884
971
|
function resetFS(fs) {
|
|
885
972
|
fs.entries = [];
|
|
973
|
+
fs.entryIdCounter = 0;
|
|
886
974
|
fs.root = new ZipDirectoryEntry(fs);
|
|
887
975
|
}
|
|
888
976
|
|
|
977
|
+
function registerEntries(fs, entry) {
|
|
978
|
+
const pendingEntries = [entry];
|
|
979
|
+
while (pendingEntries.length) {
|
|
980
|
+
const pendingEntry = pendingEntries.pop();
|
|
981
|
+
fs.entries[pendingEntry.id] = pendingEntry;
|
|
982
|
+
for (const child of pendingEntry.children) {
|
|
983
|
+
pendingEntries.push(child);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
|
|
889
988
|
function addChild(parent, name, params, directory) {
|
|
890
989
|
if (parent.directory) {
|
|
891
990
|
return directory ? new ZipDirectoryEntry(parent.fs, name, params, parent) : new ZipFileEntry(parent.fs, name, params, parent);
|