@zip.js/zip.js 2.8.30 → 2.8.32
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/BENCHMARKS.md +82 -1
- package/deno.json +5 -4
- package/dist/zip-core.js +403 -339
- package/dist/zip-core.min.js +1 -1
- package/dist/zip-fs-core.js +116 -278
- package/dist/zip-fs-core.min.js +1 -1
- package/dist/zip-fs-native.js +440 -378
- package/dist/zip-fs-native.min.js +1 -1
- package/dist/zip-fs.js +439 -377
- package/dist/zip-fs.min.js +1 -1
- package/dist/zip-legacy.js +408 -344
- package/dist/zip-legacy.min.js +1 -1
- package/dist/zip-module.wasm +0 -0
- package/dist/zip-native.js +411 -347
- 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 +410 -346
- package/dist/zip.min.js +1 -1
- package/index-native.cjs +440 -378
- package/index-native.min.js +1 -1
- package/index.cjs +439 -377
- package/index.d.cts +2853 -0
- package/index.d.ts +278 -1
- package/index.min.js +1 -1
- package/lib/core/codec-worker.js +1 -11
- package/lib/core/configuration.js +21 -33
- package/lib/core/constants.js +3 -0
- package/lib/core/io.js +11 -19
- package/lib/core/streams/aes-crypto-stream.js +16 -20
- package/lib/core/streams/common-crypto.js +0 -2
- package/lib/core/streams/zip-crypto-stream.js +12 -16
- package/lib/core/streams/zip-entry-stream.js +2 -32
- package/lib/core/streams/zlib-js/zlib-streams.min.js +1 -1
- package/lib/core/streams/zlib-wasm/zlib-streams-loader.js +1 -3
- package/lib/core/streams/zlib-wasm/zlib-streams.wasm +0 -0
- package/lib/core/util/base64.js +68 -0
- package/lib/core/util/blob-temp-stream.js +124 -0
- package/lib/core/util/decode-text.js +0 -1
- package/lib/core/util/default-mime-type.js +0 -3
- package/lib/core/util/inflate.js +194 -0
- package/lib/core/util/mime-type.js +26 -22
- package/lib/core/util/opfs-temp-stream.js +1 -37
- package/lib/core/util/sync-access-handle-temp-stream.js +147 -0
- package/lib/core/web-worker-inline-native.js +1 -1
- package/lib/core/web-worker-inline-template-native.js +5 -5
- package/lib/core/web-worker-inline-wasm.js +1 -1
- package/lib/core/zip-fs.js +6 -13
- package/lib/core/zip-reader.js +48 -127
- package/lib/core/zip-writer.js +27 -45
- package/lib/core/zlib-streams-inline-template.js +3 -2
- package/lib/core/zlib-streams-inline.js +1 -1
- package/lib/zip-core-base.js +10 -2
- package/package.json +9 -6
- package/reserved-property-names.js +50 -0
- package/lib/core/util/mini-lz.js +0 -198
package/dist/zip-core.js
CHANGED
|
@@ -113,6 +113,8 @@
|
|
|
113
113
|
const FUNCTION_TYPE = "function";
|
|
114
114
|
const OBJECT_TYPE = "object";
|
|
115
115
|
|
|
116
|
+
const EMPTY_UINT8_ARRAY = new Uint8Array();
|
|
117
|
+
|
|
116
118
|
/*
|
|
117
119
|
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
|
118
120
|
|
|
@@ -164,6 +166,22 @@
|
|
|
164
166
|
DecompressionStream: typeof DecompressionStream != UNDEFINED_TYPE && DecompressionStream
|
|
165
167
|
};
|
|
166
168
|
|
|
169
|
+
const CONFIGURABLE_PROPERTY_NAMES = [
|
|
170
|
+
"baseURI",
|
|
171
|
+
"wasmURI",
|
|
172
|
+
"workerURI",
|
|
173
|
+
"chunkSize",
|
|
174
|
+
"maxWorkers",
|
|
175
|
+
"terminateWorkerTimeout",
|
|
176
|
+
"workerStarvationTimeout",
|
|
177
|
+
"useCompressionStream",
|
|
178
|
+
"useWebWorkers",
|
|
179
|
+
"CompressionStream",
|
|
180
|
+
"DecompressionStream",
|
|
181
|
+
"CompressionStreamZlib",
|
|
182
|
+
"DecompressionStreamZlib"
|
|
183
|
+
];
|
|
184
|
+
|
|
167
185
|
const config = Object.assign({}, DEFAULT_CONFIGURATION);
|
|
168
186
|
|
|
169
187
|
function getConfiguration() {
|
|
@@ -175,39 +193,11 @@
|
|
|
175
193
|
}
|
|
176
194
|
|
|
177
195
|
function configure(configuration) {
|
|
178
|
-
const {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
workerStarvationTimeout,
|
|
184
|
-
useCompressionStream,
|
|
185
|
-
useWebWorkers,
|
|
186
|
-
CompressionStream,
|
|
187
|
-
DecompressionStream,
|
|
188
|
-
CompressionStreamZlib,
|
|
189
|
-
DecompressionStreamZlib,
|
|
190
|
-
workerURI,
|
|
191
|
-
wasmURI
|
|
192
|
-
} = configuration;
|
|
193
|
-
setIfDefined("baseURI", baseURI);
|
|
194
|
-
setIfDefined("wasmURI", wasmURI);
|
|
195
|
-
setIfDefined("workerURI", workerURI);
|
|
196
|
-
setIfDefined("chunkSize", chunkSize);
|
|
197
|
-
setIfDefined("maxWorkers", maxWorkers);
|
|
198
|
-
setIfDefined("terminateWorkerTimeout", terminateWorkerTimeout);
|
|
199
|
-
setIfDefined("workerStarvationTimeout", workerStarvationTimeout);
|
|
200
|
-
setIfDefined("useCompressionStream", useCompressionStream);
|
|
201
|
-
setIfDefined("useWebWorkers", useWebWorkers);
|
|
202
|
-
setIfDefined("CompressionStream", CompressionStream);
|
|
203
|
-
setIfDefined("DecompressionStream", DecompressionStream);
|
|
204
|
-
setIfDefined("CompressionStreamZlib", CompressionStreamZlib);
|
|
205
|
-
setIfDefined("DecompressionStreamZlib", DecompressionStreamZlib);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
function setIfDefined(propertyName, propertyValue) {
|
|
209
|
-
if (propertyValue !== UNDEFINED_VALUE) {
|
|
210
|
-
config[propertyName] = propertyValue;
|
|
196
|
+
for (const propertyName of CONFIGURABLE_PROPERTY_NAMES) {
|
|
197
|
+
const propertyValue = configuration[propertyName];
|
|
198
|
+
if (propertyValue !== UNDEFINED_VALUE) {
|
|
199
|
+
config[propertyName] = propertyValue;
|
|
200
|
+
}
|
|
211
201
|
}
|
|
212
202
|
}
|
|
213
203
|
|
|
@@ -1222,8 +1212,6 @@
|
|
|
1222
1212
|
if (GET_RANDOM_VALUES_SUPPORTED) {
|
|
1223
1213
|
return crypto.getRandomValues(array);
|
|
1224
1214
|
} else {
|
|
1225
|
-
// all supported runtimes expose crypto.getRandomValues; failing hard beats
|
|
1226
|
-
// silently generating predictable encryption salts
|
|
1227
1215
|
throw new Error(ERR_UNSUPPORTED_CRYPTO_API);
|
|
1228
1216
|
}
|
|
1229
1217
|
}
|
|
@@ -1286,12 +1274,7 @@
|
|
|
1286
1274
|
constructor({ password, rawPassword, encryptionStrength, checkPasswordOnly }) {
|
|
1287
1275
|
super({
|
|
1288
1276
|
start() {
|
|
1289
|
-
|
|
1290
|
-
ready: new Promise(resolve => this.resolveReady = resolve),
|
|
1291
|
-
password: encodePassword(password, rawPassword),
|
|
1292
|
-
strength: encryptionStrength - 1,
|
|
1293
|
-
pending: new Uint8Array()
|
|
1294
|
-
});
|
|
1277
|
+
initAesCrypto(this, password, rawPassword, encryptionStrength);
|
|
1295
1278
|
},
|
|
1296
1279
|
async transform(chunk, controller) {
|
|
1297
1280
|
const aesCrypto = this;
|
|
@@ -1326,7 +1309,7 @@
|
|
|
1326
1309
|
await ready;
|
|
1327
1310
|
const chunkToDecrypt = subarray(pending, 0, pending.length - SIGNATURE_LENGTH);
|
|
1328
1311
|
const originalSignature = subarray(pending, pending.length - SIGNATURE_LENGTH);
|
|
1329
|
-
let decryptedChunkArray =
|
|
1312
|
+
let decryptedChunkArray = EMPTY_UINT8_ARRAY;
|
|
1330
1313
|
if (chunkToDecrypt.length) {
|
|
1331
1314
|
const encryptedChunk = toBits(codecBytes, chunkToDecrypt);
|
|
1332
1315
|
hmac.update(encryptedChunk);
|
|
@@ -1334,8 +1317,6 @@
|
|
|
1334
1317
|
decryptedChunkArray = fromBits(codecBytes, decryptedChunk);
|
|
1335
1318
|
}
|
|
1336
1319
|
const signature = subarray(fromBits(codecBytes, hmac.digest()), 0, SIGNATURE_LENGTH);
|
|
1337
|
-
// branch-free comparison: accumulate the differences instead of
|
|
1338
|
-
// short-circuiting on the first mismatching byte
|
|
1339
1320
|
let invalidSignature = pending.length < SIGNATURE_LENGTH ? 1 : 0;
|
|
1340
1321
|
for (let indexSignature = 0; indexSignature < SIGNATURE_LENGTH; indexSignature++) {
|
|
1341
1322
|
invalidSignature |= signature[indexSignature] ^ originalSignature[indexSignature];
|
|
@@ -1357,12 +1338,7 @@
|
|
|
1357
1338
|
let stream;
|
|
1358
1339
|
super({
|
|
1359
1340
|
start() {
|
|
1360
|
-
|
|
1361
|
-
ready: new Promise(resolve => this.resolveReady = resolve),
|
|
1362
|
-
password: encodePassword(password, rawPassword),
|
|
1363
|
-
strength: encryptionStrength - 1,
|
|
1364
|
-
pending: new Uint8Array()
|
|
1365
|
-
});
|
|
1341
|
+
initAesCrypto(this, password, rawPassword, encryptionStrength);
|
|
1366
1342
|
},
|
|
1367
1343
|
async transform(chunk, controller) {
|
|
1368
1344
|
const aesCrypto = this;
|
|
@@ -1372,7 +1348,7 @@
|
|
|
1372
1348
|
resolveReady,
|
|
1373
1349
|
ready
|
|
1374
1350
|
} = aesCrypto;
|
|
1375
|
-
let preamble =
|
|
1351
|
+
let preamble = EMPTY_UINT8_ARRAY;
|
|
1376
1352
|
if (password) {
|
|
1377
1353
|
preamble = await createEncryptionKeys(aesCrypto, strength, password);
|
|
1378
1354
|
resolveReady();
|
|
@@ -1392,7 +1368,7 @@
|
|
|
1392
1368
|
} = this;
|
|
1393
1369
|
if (hmac && ctr) {
|
|
1394
1370
|
await ready;
|
|
1395
|
-
let encryptedChunkArray =
|
|
1371
|
+
let encryptedChunkArray = EMPTY_UINT8_ARRAY;
|
|
1396
1372
|
if (pending.length) {
|
|
1397
1373
|
const encryptedChunk = ctr.update(toBits(codecBytes, pending));
|
|
1398
1374
|
hmac.update(encryptedChunk);
|
|
@@ -1407,6 +1383,15 @@
|
|
|
1407
1383
|
}
|
|
1408
1384
|
}
|
|
1409
1385
|
|
|
1386
|
+
function initAesCrypto(aesCrypto, password, rawPassword, encryptionStrength) {
|
|
1387
|
+
Object.assign(aesCrypto, {
|
|
1388
|
+
ready: new Promise(resolve => aesCrypto.resolveReady = resolve),
|
|
1389
|
+
password: encodePassword(password, rawPassword),
|
|
1390
|
+
strength: encryptionStrength - 1,
|
|
1391
|
+
pending: EMPTY_UINT8_ARRAY
|
|
1392
|
+
});
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1410
1395
|
function append(aesCrypto, input, output, paddingStart, paddingEnd, verifySignature) {
|
|
1411
1396
|
const {
|
|
1412
1397
|
ctr,
|
|
@@ -1568,20 +1553,13 @@
|
|
|
1568
1553
|
constructor({ password, rawPassword, passwordVerification, checkPasswordOnly }) {
|
|
1569
1554
|
super({
|
|
1570
1555
|
start() {
|
|
1571
|
-
|
|
1572
|
-
password,
|
|
1573
|
-
rawPassword,
|
|
1574
|
-
passwordVerification
|
|
1575
|
-
});
|
|
1576
|
-
createKeys(this, password, rawPassword);
|
|
1556
|
+
initZipCrypto(this, password, rawPassword, passwordVerification);
|
|
1577
1557
|
},
|
|
1578
1558
|
transform(chunk, controller) {
|
|
1579
1559
|
const zipCrypto = this;
|
|
1580
1560
|
if (zipCrypto.password || zipCrypto.rawPassword) {
|
|
1581
1561
|
const decryptedHeader = decrypt(zipCrypto, chunk.subarray(0, HEADER_LENGTH));
|
|
1582
1562
|
zipCrypto.password = zipCrypto.rawPassword = null;
|
|
1583
|
-
// verify the password with a branch-free comparison of the verification byte, mirroring
|
|
1584
|
-
// the constant-time AES HMAC signature check
|
|
1585
1563
|
if ((decryptedHeader.at(-1) ^ zipCrypto.passwordVerification) != 0) {
|
|
1586
1564
|
throw new Error(ERR_INVALID_PASSWORD);
|
|
1587
1565
|
}
|
|
@@ -1602,12 +1580,7 @@
|
|
|
1602
1580
|
constructor({ password, rawPassword, passwordVerification }) {
|
|
1603
1581
|
super({
|
|
1604
1582
|
start() {
|
|
1605
|
-
|
|
1606
|
-
password,
|
|
1607
|
-
rawPassword,
|
|
1608
|
-
passwordVerification
|
|
1609
|
-
});
|
|
1610
|
-
createKeys(this, password, rawPassword);
|
|
1583
|
+
initZipCrypto(this, password, rawPassword, passwordVerification);
|
|
1611
1584
|
},
|
|
1612
1585
|
transform(chunk, controller) {
|
|
1613
1586
|
const zipCrypto = this;
|
|
@@ -1631,6 +1604,15 @@
|
|
|
1631
1604
|
}
|
|
1632
1605
|
}
|
|
1633
1606
|
|
|
1607
|
+
function initZipCrypto(zipCrypto, password, rawPassword, passwordVerification) {
|
|
1608
|
+
Object.assign(zipCrypto, {
|
|
1609
|
+
password,
|
|
1610
|
+
rawPassword,
|
|
1611
|
+
passwordVerification
|
|
1612
|
+
});
|
|
1613
|
+
createKeys(zipCrypto, password, rawPassword);
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1634
1616
|
function decrypt(target, input) {
|
|
1635
1617
|
const output = new Uint8Array(input.length);
|
|
1636
1618
|
for (let index = 0; index < input.length; index++) {
|
|
@@ -1735,14 +1717,6 @@
|
|
|
1735
1717
|
const stream = this;
|
|
1736
1718
|
let crc32Stream, encryptionStream, gzipCrc32Stream;
|
|
1737
1719
|
let readable = super.readable;
|
|
1738
|
-
// The gzip trailer carries a CRC-32 of the uncompressed data (same polynomial as zip),
|
|
1739
|
-
// computed in native code for free during compression. On the native CompressionStream,
|
|
1740
|
-
// harvest it instead of running a separate CRC pass: compress as gzip, then strip the
|
|
1741
|
-
// fixed 10-byte header and 8-byte trailer to recover the exact raw-deflate payload
|
|
1742
|
-
// (byte-identical to a deflate-raw compression) while capturing the CRC. The CRC becomes
|
|
1743
|
-
// available at end-of-stream, exactly like Crc32Stream, so nothing downstream (data
|
|
1744
|
-
// descriptor, central directory, ZipCrypto) needs it any earlier. Not applied for the
|
|
1745
|
-
// pure-JS/WASM ports (a separate slice-by-8 CRC pass is as fast or faster there).
|
|
1746
1720
|
const useGzipCrc32 = signed && compressed && !deflate64 && (!encrypted || zipCrypto) &&
|
|
1747
1721
|
Boolean(useCompressionStream && CompressionStream);
|
|
1748
1722
|
if ((!encrypted || zipCrypto) && signed && !useGzipCrc32) {
|
|
@@ -1779,12 +1753,6 @@
|
|
|
1779
1753
|
}
|
|
1780
1754
|
}
|
|
1781
1755
|
|
|
1782
|
-
// Converts a gzip stream into its raw-deflate payload while capturing the CRC-32 from the gzip
|
|
1783
|
-
// trailer. The CompressionStream gzip header is always the fixed 10-byte form (FLG=0, no optional
|
|
1784
|
-
// fields) per the Compression Streams spec, so it is stripped by length; the trailer is the last
|
|
1785
|
-
// 8 bytes (CRC-32 LE, then ISIZE LE). Bounded memory: at most GZIP_TRAILER_LENGTH bytes are held
|
|
1786
|
-
// back across chunks. The signature is read big-endian-agnostically as a number, matching the
|
|
1787
|
-
// value Crc32Stream produces, so the writer path is unchanged.
|
|
1788
1756
|
class GzipToRawDeflateStream extends TransformStream {
|
|
1789
1757
|
|
|
1790
1758
|
constructor() {
|
|
@@ -1810,10 +1778,6 @@
|
|
|
1810
1778
|
tail = pending;
|
|
1811
1779
|
return;
|
|
1812
1780
|
}
|
|
1813
|
-
// Emit everything except the trailing GZIP_TRAILER_LENGTH bytes as a standalone,
|
|
1814
|
-
// right-sized Uint8Array. Consumers may read chunk.buffer directly (e.g. custom
|
|
1815
|
-
// writers), so an aliased subarray of a larger buffer would leak the held-back
|
|
1816
|
-
// trailer bytes. Bytes are copied exactly once, into `output` or `tail`.
|
|
1817
1781
|
const emitLength = available - GZIP_TRAILER_LENGTH;
|
|
1818
1782
|
const output = new Uint8Array(emitLength);
|
|
1819
1783
|
const fromTail = Math.min(emitLength, tail.length);
|
|
@@ -1902,11 +1866,6 @@
|
|
|
1902
1866
|
throw error;
|
|
1903
1867
|
}
|
|
1904
1868
|
}
|
|
1905
|
-
// The native CompressionStream/DecompressionStream and the pure-JS zlib port do not signal
|
|
1906
|
-
// backpressure on their writable side (their `ready` never pends), so a plain pipeThrough drains
|
|
1907
|
-
// the whole source into them and peak memory grows with the entry size. Awaiting each write()
|
|
1908
|
-
// paces the source to the codec's consumption rate, keeping streaming memory bounded. The WASM
|
|
1909
|
-
// codec already backpressures, so this is a no-op cost for it.
|
|
1910
1869
|
return pipeThroughBackpressured(readable, codecStream);
|
|
1911
1870
|
}
|
|
1912
1871
|
|
|
@@ -1914,10 +1873,6 @@
|
|
|
1914
1873
|
return readable.pipeThrough(transformStream);
|
|
1915
1874
|
}
|
|
1916
1875
|
|
|
1917
|
-
// Like readable.pipeThrough(transformStream), but drives the writable side manually and awaits each
|
|
1918
|
-
// write() so that a codec which under-reports backpressure cannot pull the whole source into memory.
|
|
1919
|
-
// Errors propagate in both directions: a source error aborts the codec; a downstream cancel/error
|
|
1920
|
-
// (surfaced through the codec's writable rejecting) cancels the source.
|
|
1921
1876
|
function pipeThroughBackpressured(readable, transformStream) {
|
|
1922
1877
|
const writer = transformStream.writable.getWriter();
|
|
1923
1878
|
const reader = readable.getReader();
|
|
@@ -1946,7 +1901,7 @@
|
|
|
1946
1901
|
try {
|
|
1947
1902
|
await writer.abort(error);
|
|
1948
1903
|
} catch {
|
|
1949
|
-
// the writable may already be errored/closed
|
|
1904
|
+
// ignored: the writable may already be errored/closed
|
|
1950
1905
|
}
|
|
1951
1906
|
}
|
|
1952
1907
|
|
|
@@ -1954,13 +1909,10 @@
|
|
|
1954
1909
|
try {
|
|
1955
1910
|
await reader.cancel(error);
|
|
1956
1911
|
} catch {
|
|
1957
|
-
// the readable may already be errored/closed
|
|
1912
|
+
// ignored: the readable may already be errored/closed
|
|
1958
1913
|
}
|
|
1959
1914
|
}
|
|
1960
1915
|
|
|
1961
|
-
// DecompressionStream implementations can fail with a message-less TypeError on malformed
|
|
1962
|
-
// input; give these errors an identifiable message (errors raised by the other stages of
|
|
1963
|
-
// the pipeline, e.g. ERR_INVALID_PASSWORD, always carry a message and pass through as-is)
|
|
1964
1916
|
function mapInflateStreamError(readable) {
|
|
1965
1917
|
const reader = readable.getReader();
|
|
1966
1918
|
return new ReadableStream({
|
|
@@ -2285,9 +2237,6 @@
|
|
|
2285
2237
|
try {
|
|
2286
2238
|
await initModule(config);
|
|
2287
2239
|
} catch {
|
|
2288
|
-
// The WASM module failed to load. Fall back to the native CompressionStream only if the
|
|
2289
|
-
// zlib codec that would otherwise run depends on that module; a self-contained codec
|
|
2290
|
-
// supplied through config (e.g. the pure-JS port) stays usable and is kept as requested.
|
|
2291
2240
|
const ZlibStream = options.codecType.startsWith(CODEC_DEFLATE) ?
|
|
2292
2241
|
config.CompressionStreamZlib :
|
|
2293
2242
|
config.DecompressionStreamZlib;
|
|
@@ -2365,7 +2314,6 @@
|
|
|
2365
2314
|
return resultValue;
|
|
2366
2315
|
} catch (error) {
|
|
2367
2316
|
await closeWritable();
|
|
2368
|
-
// unblock the pipe watcher if the worker died before closing its end of the stream
|
|
2369
2317
|
abortPipe();
|
|
2370
2318
|
try {
|
|
2371
2319
|
await closed;
|
|
@@ -2390,8 +2338,6 @@
|
|
|
2390
2338
|
const abortController = new AbortController();
|
|
2391
2339
|
const { writable, readable } = new TransformStream();
|
|
2392
2340
|
const closed = readable.pipeTo(writableSource, { preventClose: true, preventAbort: true, signal: abortController.signal });
|
|
2393
|
-
// the pipe can fail before `closed` is awaited (e.g. the destination write fails);
|
|
2394
|
-
// mark the rejection as handled so it cannot kill the process in the meantime
|
|
2395
2341
|
closed.catch(() => { });
|
|
2396
2342
|
return { writable, closed, abortPipe: () => abortController.abort() };
|
|
2397
2343
|
}
|
|
@@ -2484,8 +2430,6 @@
|
|
|
2484
2430
|
event.preventDefault();
|
|
2485
2431
|
}
|
|
2486
2432
|
const { rejectResult, writer, onTaskFinished } = workerData;
|
|
2487
|
-
// the worker is unusable (e.g. its script failed to load); terminate it and
|
|
2488
|
-
// fail the current task instead of letting it hang forever
|
|
2489
2433
|
terminateWorker$1(workerData);
|
|
2490
2434
|
if (rejectResult) {
|
|
2491
2435
|
rejectResult(event.error || new Error(event.message || ERROR_EVENT_TYPE));
|
|
@@ -2501,7 +2445,7 @@
|
|
|
2501
2445
|
const { value, readable, writable } = message;
|
|
2502
2446
|
const transferables = [];
|
|
2503
2447
|
if (value) {
|
|
2504
|
-
message.value = value;
|
|
2448
|
+
message.value = value.byteOffset || value.byteLength != value.buffer.byteLength ? new Uint8Array(value) : value;
|
|
2505
2449
|
transferables.push(message.value.buffer);
|
|
2506
2450
|
}
|
|
2507
2451
|
if (transferStreams && transferStreamsSupported) {
|
|
@@ -2537,8 +2481,6 @@
|
|
|
2537
2481
|
async function onMessage({ data }, workerData) {
|
|
2538
2482
|
const { type, value, messageId, result, error } = data;
|
|
2539
2483
|
const { reader, writer, resolveResult, rejectResult, onTaskFinished, generation } = workerData;
|
|
2540
|
-
// continuations below can settle after the worker slot has been reassigned to a new
|
|
2541
|
-
// task (e.g. a pending pull read failing late); they must not touch the new task
|
|
2542
2484
|
const stale = () => workerData.generation != generation;
|
|
2543
2485
|
try {
|
|
2544
2486
|
if (error) {
|
|
@@ -2794,26 +2736,24 @@
|
|
|
2794
2736
|
class Reader extends Stream {
|
|
2795
2737
|
|
|
2796
2738
|
get readable() {
|
|
2739
|
+
return this.createReadable();
|
|
2740
|
+
}
|
|
2741
|
+
|
|
2742
|
+
createReadable({ offset = 0, size, diskNumberStart, chunkSize = DEFAULT_CHUNK_SIZE } = {}) {
|
|
2797
2743
|
const reader = this;
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
start() {
|
|
2801
|
-
this.chunkOffset = 0;
|
|
2802
|
-
},
|
|
2744
|
+
let chunkOffset = 0;
|
|
2745
|
+
return new ReadableStream({
|
|
2803
2746
|
async pull(controller) {
|
|
2804
|
-
const { offset = 0, size, diskNumberStart } = readable;
|
|
2805
|
-
const { chunkOffset } = this;
|
|
2806
2747
|
const dataSize = size === UNDEFINED_VALUE ? chunkSize : Math.min(chunkSize, size - chunkOffset);
|
|
2807
2748
|
const data = await readUint8Array(reader, offset + chunkOffset, dataSize, diskNumberStart);
|
|
2808
2749
|
controller.enqueue(data);
|
|
2809
2750
|
if ((chunkOffset + chunkSize > size) || (size === UNDEFINED_VALUE && !data.length && dataSize)) {
|
|
2810
2751
|
controller.close();
|
|
2811
2752
|
} else {
|
|
2812
|
-
|
|
2753
|
+
chunkOffset += chunkSize;
|
|
2813
2754
|
}
|
|
2814
2755
|
}
|
|
2815
2756
|
});
|
|
2816
|
-
return readable;
|
|
2817
2757
|
}
|
|
2818
2758
|
}
|
|
2819
2759
|
|
|
@@ -2952,8 +2892,6 @@
|
|
|
2952
2892
|
}
|
|
2953
2893
|
});
|
|
2954
2894
|
writer.blob = new Response(transformStream.readable, { headers }).blob();
|
|
2955
|
-
// mark early rejections (e.g. aborted pipe) as handled; awaiting getData() still
|
|
2956
|
-
// surfaces them
|
|
2957
2895
|
writer.blob.catch(() => { });
|
|
2958
2896
|
}
|
|
2959
2897
|
|
|
@@ -3115,7 +3053,7 @@
|
|
|
3115
3053
|
return eocdCache;
|
|
3116
3054
|
}
|
|
3117
3055
|
if (index >= size || length === 0) {
|
|
3118
|
-
return
|
|
3056
|
+
return EMPTY_UINT8_ARRAY;
|
|
3119
3057
|
} else {
|
|
3120
3058
|
if (index + length > size) {
|
|
3121
3059
|
length = size - index;
|
|
@@ -3172,8 +3110,6 @@
|
|
|
3172
3110
|
async function getRequestData(httpReader, sendRequest) {
|
|
3173
3111
|
const response = await sendRequest(HTTP_METHOD_GET, httpReader, getHeaders(httpReader));
|
|
3174
3112
|
httpReader.data = new Uint8Array(await response.arrayBuffer());
|
|
3175
|
-
// the downloaded content is authoritative, e.g. the Content-Length response header
|
|
3176
|
-
// of the HEAD request can be the encoded size when the content is compressed
|
|
3177
3113
|
httpReader.size = httpReader.data.length;
|
|
3178
3114
|
}
|
|
3179
3115
|
|
|
@@ -3238,7 +3174,6 @@
|
|
|
3238
3174
|
super();
|
|
3239
3175
|
Object.assign(this, {
|
|
3240
3176
|
url,
|
|
3241
|
-
// an explicit fetch implementation takes precedence over useXHR
|
|
3242
3177
|
reader: options.useXHR && !options.fetch ? new XHRReader(url, options) : new FetchReader(url, options)
|
|
3243
3178
|
});
|
|
3244
3179
|
}
|
|
@@ -3377,7 +3312,7 @@
|
|
|
3377
3312
|
}
|
|
3378
3313
|
}
|
|
3379
3314
|
} else {
|
|
3380
|
-
result =
|
|
3315
|
+
result = EMPTY_UINT8_ARRAY;
|
|
3381
3316
|
}
|
|
3382
3317
|
reader.lastDiskNumber = Math.max(currentDiskNumber, reader.lastDiskNumber);
|
|
3383
3318
|
return result;
|
|
@@ -3422,8 +3357,6 @@
|
|
|
3422
3357
|
writer.diskOffset += diskSourceWriter.size;
|
|
3423
3358
|
writer.diskNumber++;
|
|
3424
3359
|
diskWriter = null;
|
|
3425
|
-
// the next disk is not opened yet but its space is available; leaving a
|
|
3426
|
-
// stale value here would force a new disk for every subsequent write
|
|
3427
3360
|
writer.availableSize = writer.maxSize;
|
|
3428
3361
|
if (chunk.length > availableSize) {
|
|
3429
3362
|
await this.write(chunk.subarray(availableSize));
|
|
@@ -3603,7 +3536,6 @@
|
|
|
3603
3536
|
if (encoding && encoding.trim().toLowerCase() == "cp437") {
|
|
3604
3537
|
return decodeCP437(value);
|
|
3605
3538
|
} else {
|
|
3606
|
-
// ignoreBOM keeps a leading U+FEFF in filenames/comments instead of dropping it
|
|
3607
3539
|
return new TextDecoder(encoding, { ignoreBOM: true }).decode(value);
|
|
3608
3540
|
}
|
|
3609
3541
|
}
|
|
@@ -3851,8 +3783,6 @@
|
|
|
3851
3783
|
const CHARSET_UTF8 = "utf-8";
|
|
3852
3784
|
const PROPERTY_NAME_UTF8_SUFFIX = "UTF8";
|
|
3853
3785
|
const CHARSET_CP437 = "cp437";
|
|
3854
|
-
// bits of the general purpose flag that change how an entry is interpreted; the level bits (1-2) are excluded
|
|
3855
|
-
// because they are informative only
|
|
3856
3786
|
const BITFLAG_AMBIGUITY_MASK = BITFLAG_ENCRYPTED | BITFLAG_DATA_DESCRIPTOR | BITFLAG_LANG_ENCODING_FLAG;
|
|
3857
3787
|
const ZIP64_PROPERTIES = [
|
|
3858
3788
|
[PROPERTY_NAME_UNCOMPRESSED_SIZE, MAX_32_BITS],
|
|
@@ -3894,7 +3824,6 @@
|
|
|
3894
3824
|
if (reader.size < END_OF_CENTRAL_DIR_LENGTH) {
|
|
3895
3825
|
throw new Error(ERR_BAD_FORMAT);
|
|
3896
3826
|
}
|
|
3897
|
-
reader.chunkSize = getChunkSize(config);
|
|
3898
3827
|
const strictness = getStrictness(getOptionValue$1(zipReader, options, OPTION_STRICTNESS), getOptionValue$1(zipReader, options, OPTION_CHECK_AMBIGUITY));
|
|
3899
3828
|
const checkAmbiguity = strictness == STRICTNESS_STRICT;
|
|
3900
3829
|
const rejectAmbiguousEndOfDirectory = strictness != STRICTNESS_TOLERANT;
|
|
@@ -3909,8 +3838,6 @@
|
|
|
3909
3838
|
throw new Error(ERR_EOCDR_NOT_FOUND);
|
|
3910
3839
|
}
|
|
3911
3840
|
}
|
|
3912
|
-
// two or more end-anchored records that each dereference to a valid central directory cannot be told
|
|
3913
|
-
// apart (see comment_length ⟺ EOF): the archive is genuinely ambiguous, so refuse rather than guess
|
|
3914
3841
|
if (rejectAmbiguousEndOfDirectory && endOfDirectoryReachingEndCount > 1) {
|
|
3915
3842
|
throwAmbiguousArchive("multiple end of central directory records");
|
|
3916
3843
|
}
|
|
@@ -3933,7 +3860,7 @@
|
|
|
3933
3860
|
if (directoryDataOffset == MAX_32_BITS || directoryDataLength == MAX_32_BITS || filesLength == MAX_16_BITS || diskNumber == MAX_16_BITS) {
|
|
3934
3861
|
const endOfDirectoryLocatorArray = endOfDirectoryInfo.offset >= ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH ?
|
|
3935
3862
|
await readUint8Array(reader, endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH, ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH) :
|
|
3936
|
-
|
|
3863
|
+
EMPTY_UINT8_ARRAY;
|
|
3937
3864
|
const endOfDirectoryLocatorView = getDataView$1(endOfDirectoryLocatorArray);
|
|
3938
3865
|
if (endOfDirectoryLocatorArray.length == ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH &&
|
|
3939
3866
|
getUint32(endOfDirectoryLocatorView, 0) == ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIGNATURE) {
|
|
@@ -4000,12 +3927,6 @@
|
|
|
4000
3927
|
}
|
|
4001
3928
|
const expectedDirectoryDataOffset = centralDirectoryEndOffset - directoryDataLength - (reader.lastDiskOffset || 0);
|
|
4002
3929
|
if (directoryDataOffset != expectedDirectoryDataOffset && diskNumber == lastDiskNumber) {
|
|
4003
|
-
// the reconciled offset (the directory ends exactly where the canonical record begins) is anchored
|
|
4004
|
-
// to the unforgeable end of the archive; the stored offset is not. Prefer the reconciled offset
|
|
4005
|
-
// unless the stored one points at a directory and the reconciled one does not — that exception
|
|
4006
|
-
// keeps a corrupt declared directory length from sending the read astray, while still moving off a
|
|
4007
|
-
// stored offset that only looks valid because an append remnant left the previous (identically
|
|
4008
|
-
// laid out) directory sitting there.
|
|
4009
3930
|
const storedPointsAtDirectory = getUint32(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE;
|
|
4010
3931
|
let reconcile = !storedPointsAtDirectory;
|
|
4011
3932
|
if (!reconcile && expectedDirectoryDataOffset >= 0 && expectedDirectoryDataOffset + 4 <= reader.size) {
|
|
@@ -4162,11 +4083,11 @@
|
|
|
4162
4083
|
const extractPrependedData = getOptionValue$1(zipReader, options, OPTION_EXTRACT_PREPENDED_DATA);
|
|
4163
4084
|
const extractAppendedData = getOptionValue$1(zipReader, options, OPTION_EXTRACT_APPENDED_DATA);
|
|
4164
4085
|
if (extractPrependedData) {
|
|
4165
|
-
zipReader.prependedData = startOffset > 0 ? await readUint8Array(reader, 0, startOffset) :
|
|
4086
|
+
zipReader.prependedData = startOffset > 0 ? await readUint8Array(reader, 0, startOffset) : EMPTY_UINT8_ARRAY;
|
|
4166
4087
|
}
|
|
4167
|
-
zipReader.comment = commentLength ? await readUint8Array(reader, commentOffset + END_OF_CENTRAL_DIR_LENGTH, commentLength) :
|
|
4088
|
+
zipReader.comment = commentLength ? await readUint8Array(reader, commentOffset + END_OF_CENTRAL_DIR_LENGTH, commentLength) : EMPTY_UINT8_ARRAY;
|
|
4168
4089
|
if (extractAppendedData) {
|
|
4169
|
-
zipReader.appendedData = appendedDataOffset < reader.size ? await readUint8Array(reader, appendedDataOffset, reader.size - appendedDataOffset) :
|
|
4090
|
+
zipReader.appendedData = appendedDataOffset < reader.size ? await readUint8Array(reader, appendedDataOffset, reader.size - appendedDataOffset) : EMPTY_UINT8_ARRAY;
|
|
4170
4091
|
}
|
|
4171
4092
|
return true;
|
|
4172
4093
|
}
|
|
@@ -4278,7 +4199,7 @@
|
|
|
4278
4199
|
filenameLength
|
|
4279
4200
|
} = localDirectory;
|
|
4280
4201
|
const checkAmbiguity = getStrictness(getOptionValue$1(zipEntry, options, OPTION_STRICTNESS), getOptionValue$1(zipEntry, options, OPTION_CHECK_AMBIGUITY)) == STRICTNESS_STRICT;
|
|
4281
|
-
let rawLocalFilename =
|
|
4202
|
+
let rawLocalFilename = EMPTY_UINT8_ARRAY;
|
|
4282
4203
|
if (checkAmbiguity && (filenameLength || extraFieldLength)) {
|
|
4283
4204
|
const trailingDataArray = await readUint8Array(reader, offset + HEADER_SIZE, filenameLength + extraFieldLength, diskNumberStart);
|
|
4284
4205
|
rawLocalFilename = trailingDataArray.subarray(0, filenameLength);
|
|
@@ -4286,7 +4207,7 @@
|
|
|
4286
4207
|
} else {
|
|
4287
4208
|
localDirectory.rawExtraField = extraFieldLength ?
|
|
4288
4209
|
await readUint8Array(reader, offset + HEADER_SIZE + filenameLength, extraFieldLength, diskNumberStart) :
|
|
4289
|
-
|
|
4210
|
+
EMPTY_UINT8_ARRAY;
|
|
4290
4211
|
}
|
|
4291
4212
|
readCommonFooter(zipEntry, localDirectory, dataView, 4, true);
|
|
4292
4213
|
if (checkAmbiguity) {
|
|
@@ -4313,12 +4234,7 @@
|
|
|
4313
4234
|
}
|
|
4314
4235
|
const dataOffset = offset + HEADER_SIZE + filenameLength + extraFieldLength;
|
|
4315
4236
|
const size = compressedSize;
|
|
4316
|
-
const readable = reader.
|
|
4317
|
-
Object.assign(readable, {
|
|
4318
|
-
diskNumberStart,
|
|
4319
|
-
offset: dataOffset,
|
|
4320
|
-
size
|
|
4321
|
-
});
|
|
4237
|
+
const readable = reader.createReadable({ offset: dataOffset, size, diskNumberStart, chunkSize: getChunkSize(config) });
|
|
4322
4238
|
const signal = getOptionValue$1(zipEntry, options, OPTION_SIGNAL);
|
|
4323
4239
|
const checkPasswordOnly = getOptionValue$1(zipEntry, options, OPTION_CHECK_PASSWORD_ONLY);
|
|
4324
4240
|
let checkOverlappingEntry = getOptionValue$1(zipEntry, options, OPTION_CHECK_OVERLAPPING_ENTRY);
|
|
@@ -4589,7 +4505,6 @@
|
|
|
4589
4505
|
const view = getDataView$1(new Uint8Array(extraField.data));
|
|
4590
4506
|
let uid, gid;
|
|
4591
4507
|
if (isInfoZip) {
|
|
4592
|
-
// Info-ZIP New Unix "ux" field (0x7875): version + variable-length uid/gid
|
|
4593
4508
|
let offset = 0;
|
|
4594
4509
|
const version = getUint8(view, offset++);
|
|
4595
4510
|
const uidSize = getUint8(view, offset++);
|
|
@@ -4599,8 +4514,6 @@
|
|
|
4599
4514
|
gid = unpackUnixId(extraField.data.subarray(offset, offset + gidSize));
|
|
4600
4515
|
Object.assign(extraField, { version, uid, gid });
|
|
4601
4516
|
} else if (extraField.data.length >= 4) {
|
|
4602
|
-
// Info-ZIP Unix "Ux" field (0x7855): fixed 2-byte uid + gid (the file mode, if any, is
|
|
4603
|
-
// carried in the external file attributes, not here)
|
|
4604
4517
|
uid = getUint16(view, 0);
|
|
4605
4518
|
gid = getUint16(view, 2);
|
|
4606
4519
|
Object.assign(extraField, { uid, gid });
|
|
@@ -4652,7 +4565,6 @@
|
|
|
4652
4565
|
timeProperties.forEach((propertyName, indexProperty) => {
|
|
4653
4566
|
if (extraFieldExtendedTimestamp.data.length >= offset + 4) {
|
|
4654
4567
|
const time = getUint32(extraFieldView, offset);
|
|
4655
|
-
// the extended timestamp is a signed 32-bit time_t value
|
|
4656
4568
|
directory[propertyName] = extraFieldExtendedTimestamp[propertyName] = new Date((time | 0) * 1000);
|
|
4657
4569
|
const rawPropertyName = timeRawProperties[indexProperty];
|
|
4658
4570
|
extraFieldExtendedTimestamp[rawPropertyName] = time;
|
|
@@ -4729,7 +4641,6 @@
|
|
|
4729
4641
|
|
|
4730
4642
|
function getStrictness(strictness, checkAmbiguity) {
|
|
4731
4643
|
if (strictness === UNDEFINED_VALUE) {
|
|
4732
|
-
// `checkAmbiguity: true` is kept as a backward-compatible alias for the strictest mode
|
|
4733
4644
|
return checkAmbiguity ? STRICTNESS_STRICT : STRICTNESS_BALANCED;
|
|
4734
4645
|
}
|
|
4735
4646
|
return strictness;
|
|
@@ -4740,81 +4651,45 @@
|
|
|
4740
4651
|
return maxAppendedDataSize;
|
|
4741
4652
|
}
|
|
4742
4653
|
if (strictness == STRICTNESS_STRICT) {
|
|
4743
|
-
// the comment must reach the end of the file: no trailing data tolerated
|
|
4744
4654
|
return 0;
|
|
4745
4655
|
}
|
|
4746
4656
|
if (strictness == STRICTNESS_TOLERANT) {
|
|
4747
4657
|
return Infinity;
|
|
4748
4658
|
}
|
|
4749
|
-
// balanced: tolerate up to a legal comment's worth of trailing data (a 16-bit length); beyond that the
|
|
4750
|
-
// trailing bytes cannot hide inside a comment, so they are treated as a second thing bolted onto the file
|
|
4751
4659
|
return MAX_16_BITS;
|
|
4752
4660
|
}
|
|
4753
4661
|
|
|
4754
|
-
// cap on how many out-of-window central directory probes the scan below may issue. A legitimate archive needs
|
|
4755
|
-
// at most a couple (the canonical record, plus one more to detect genuine ambiguity); every other reachability
|
|
4756
|
-
// check is served from the tail window already in memory. The cap keeps an archive whose comment is stuffed
|
|
4757
|
-
// with unreachable end-anchored records from forcing an unbounded number of (potentially remote) reads.
|
|
4758
4662
|
const MAX_END_OF_CENTRAL_DIR_PROBES = 64;
|
|
4759
4663
|
|
|
4760
|
-
// reachability rankings for an end-anchored candidate record (see getCentralDirectoryReachability)
|
|
4761
4664
|
const CENTRAL_DIRECTORY_UNREACHABLE = 0;
|
|
4762
4665
|
const CENTRAL_DIRECTORY_PLAUSIBLE = 1;
|
|
4763
4666
|
const CENTRAL_DIRECTORY_REACHABLE = 2;
|
|
4764
4667
|
|
|
4765
|
-
// Locates the authoritative end of central directory record. The canonical record is the last one whose
|
|
4766
|
-
// declared comment reaches exactly the end of the file ("end-anchored") and that points to a central directory
|
|
4767
|
-
// ("reachable"); earlier signatures (stale append remnants, bytes embedded in a comment) are ignored. When two
|
|
4768
|
-
// or more end-anchored records point to a central directory the archive is ambiguous, and the count is returned
|
|
4769
|
-
// so the caller can refuse it. A record that reaches the end of the file but points to no central directory (an
|
|
4770
|
-
// empty archive) is only "plausible": it cannot be corroborated, so it never counts towards ambiguity and is
|
|
4771
|
-
// chosen only when no reachable record exists — otherwise an empty record forged in a comment could outrank the
|
|
4772
|
-
// genuine directory. When no record is end-anchored (appended data or an oversized comment), falls back to the
|
|
4773
|
-
// last record within the tolerated window that points to a directory (see seekEndOfCentralDirectory).
|
|
4774
4668
|
async function findEndOfCentralDirectory(reader, rejectAmbiguous, maxAppendedDataSize) {
|
|
4775
4669
|
const { size } = reader;
|
|
4776
|
-
// an end-anchored record can only live within the last END_OF_CENTRAL_DIR_LENGTH + MAX_16_BITS bytes,
|
|
4777
|
-
// since the comment length that ties it to the end of the file is a 16-bit field
|
|
4778
4670
|
const anchoredLength = Math.min(size, END_OF_CENTRAL_DIR_LENGTH + MAX_16_BITS);
|
|
4779
|
-
const anchoredOffset = size - anchoredLength;
|
|
4780
|
-
const anchoredArray = await readUint8Array(reader, anchoredOffset, anchoredLength);
|
|
4781
|
-
const anchoredView = getDataView$1(anchoredArray);
|
|
4782
4671
|
const remoteProbeBudget = { count: MAX_END_OF_CENTRAL_DIR_PROBES };
|
|
4783
4672
|
let endOfDirectoryInfo;
|
|
4784
4673
|
let plausibleEndOfDirectoryInfo;
|
|
4785
4674
|
let endOfDirectoryReachingEndCount = 0;
|
|
4786
|
-
for (
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
const
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
const reachability = await getCentralDirectoryReachability(reader, anchoredView, anchoredOffset, indexByte, offset, size, remoteProbeBudget);
|
|
4794
|
-
if (reachability == CENTRAL_DIRECTORY_REACHABLE) {
|
|
4795
|
-
if (!endOfDirectoryInfo) {
|
|
4796
|
-
endOfDirectoryInfo = {
|
|
4797
|
-
offset,
|
|
4798
|
-
buffer: anchoredArray.slice(indexByte, indexByte + END_OF_CENTRAL_DIR_LENGTH).buffer
|
|
4799
|
-
};
|
|
4800
|
-
}
|
|
4801
|
-
endOfDirectoryReachingEndCount++;
|
|
4802
|
-
// the canonical record (highest offset) is found first; a second one is enough to flag ambiguity
|
|
4803
|
-
if (!rejectAmbiguous || endOfDirectoryReachingEndCount > 1) {
|
|
4804
|
-
break;
|
|
4805
|
-
}
|
|
4806
|
-
} else if (reachability == CENTRAL_DIRECTORY_PLAUSIBLE && !plausibleEndOfDirectoryInfo) {
|
|
4807
|
-
plausibleEndOfDirectoryInfo = {
|
|
4808
|
-
offset,
|
|
4809
|
-
buffer: anchoredArray.slice(indexByte, indexByte + END_OF_CENTRAL_DIR_LENGTH).buffer
|
|
4810
|
-
};
|
|
4675
|
+
for await (const [anchoredView, anchoredOffset, anchoredArray, indexByte, offset] of scanEndOfCentralDirectory(reader, anchoredLength)) {
|
|
4676
|
+
const commentLength = getUint16(anchoredView, indexByte + 20);
|
|
4677
|
+
if (offset + END_OF_CENTRAL_DIR_LENGTH + commentLength == size) {
|
|
4678
|
+
const reachability = await getCentralDirectoryReachability(reader, anchoredView, anchoredOffset, indexByte, offset, size, remoteProbeBudget);
|
|
4679
|
+
if (reachability == CENTRAL_DIRECTORY_REACHABLE) {
|
|
4680
|
+
if (!endOfDirectoryInfo) {
|
|
4681
|
+
endOfDirectoryInfo = getEndOfCentralDirectoryInfo(anchoredArray, indexByte, offset);
|
|
4811
4682
|
}
|
|
4683
|
+
endOfDirectoryReachingEndCount++;
|
|
4684
|
+
if (!rejectAmbiguous || endOfDirectoryReachingEndCount > 1) {
|
|
4685
|
+
break;
|
|
4686
|
+
}
|
|
4687
|
+
} else if (reachability == CENTRAL_DIRECTORY_PLAUSIBLE && !plausibleEndOfDirectoryInfo) {
|
|
4688
|
+
plausibleEndOfDirectoryInfo = getEndOfCentralDirectoryInfo(anchoredArray, indexByte, offset);
|
|
4812
4689
|
}
|
|
4813
4690
|
}
|
|
4814
4691
|
}
|
|
4815
4692
|
if (!endOfDirectoryInfo) {
|
|
4816
|
-
// no record points to a directory: prefer a plausible (empty) end-anchored record before scanning the
|
|
4817
|
-
// tolerated window for a bare signature
|
|
4818
4693
|
endOfDirectoryInfo = plausibleEndOfDirectoryInfo;
|
|
4819
4694
|
}
|
|
4820
4695
|
if (!endOfDirectoryInfo) {
|
|
@@ -4823,69 +4698,53 @@
|
|
|
4823
4698
|
return { endOfDirectoryInfo, endOfDirectoryReachingEndCount };
|
|
4824
4699
|
}
|
|
4825
4700
|
|
|
4826
|
-
// Fallback for findEndOfCentralDirectory when no record is end-anchored: appended data or an oversized comment
|
|
4827
|
-
// pushed the record's declared comment past the end of the file, so its position can no longer be reconciled
|
|
4828
|
-
// against the end of the file. Scans the tolerated window from the end and returns the first record that points
|
|
4829
|
-
// to a central directory, so a stray signature embedded in appended data cannot hijack discovery (which the
|
|
4830
|
-
// anchored scan already prevents for anchored records). Falls back to the first plausible (empty) record, then
|
|
4831
|
-
// to the last bare signature, so a degenerate archive still opens as before. The window ends at the file, so the
|
|
4832
|
-
// same in-window / metered reads as the anchored scan apply.
|
|
4833
4701
|
async function seekEndOfCentralDirectory(reader, maxAppendedDataSize, remoteProbeBudget) {
|
|
4834
4702
|
const { size } = reader;
|
|
4835
4703
|
const searchLength = Math.min(size, maxAppendedDataSize == Infinity ? size :
|
|
4836
4704
|
END_OF_CENTRAL_DIR_LENGTH + MAX_16_BITS + maxAppendedDataSize);
|
|
4837
|
-
const searchOffset = size - searchLength;
|
|
4838
|
-
const searchArray = await readUint8Array(reader, searchOffset, searchLength);
|
|
4839
|
-
const searchView = getDataView$1(searchArray);
|
|
4840
4705
|
let firstSignatureInfo, plausibleInfo;
|
|
4841
|
-
for (
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
if (reachability == CENTRAL_DIRECTORY_PLAUSIBLE && !plausibleInfo) {
|
|
4853
|
-
plausibleInfo = record;
|
|
4854
|
-
}
|
|
4706
|
+
for await (const [searchView, searchOffset, searchArray, indexByte, offset] of scanEndOfCentralDirectory(reader, searchLength)) {
|
|
4707
|
+
const record = getEndOfCentralDirectoryInfo(searchArray, indexByte, offset);
|
|
4708
|
+
if (!firstSignatureInfo) {
|
|
4709
|
+
firstSignatureInfo = record;
|
|
4710
|
+
}
|
|
4711
|
+
const reachability = await getCentralDirectoryReachability(reader, searchView, searchOffset, indexByte, offset, size, remoteProbeBudget);
|
|
4712
|
+
if (reachability == CENTRAL_DIRECTORY_REACHABLE) {
|
|
4713
|
+
return record;
|
|
4714
|
+
}
|
|
4715
|
+
if (reachability == CENTRAL_DIRECTORY_PLAUSIBLE && !plausibleInfo) {
|
|
4716
|
+
plausibleInfo = record;
|
|
4855
4717
|
}
|
|
4856
4718
|
}
|
|
4857
4719
|
return plausibleInfo || firstSignatureInfo;
|
|
4858
4720
|
}
|
|
4859
4721
|
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4722
|
+
async function* scanEndOfCentralDirectory(reader, scanLength) {
|
|
4723
|
+
const scanOffset = reader.size - scanLength;
|
|
4724
|
+
const scanArray = await readUint8Array(reader, scanOffset, scanLength);
|
|
4725
|
+
const scanView = getDataView$1(scanArray);
|
|
4726
|
+
for (let indexByte = scanArray.length - END_OF_CENTRAL_DIR_LENGTH; indexByte >= 0; indexByte--) {
|
|
4727
|
+
if (getUint32(scanView, indexByte) == END_OF_CENTRAL_DIR_SIGNATURE) {
|
|
4728
|
+
yield [scanView, scanOffset, scanArray, indexByte, scanOffset + indexByte];
|
|
4729
|
+
}
|
|
4730
|
+
}
|
|
4731
|
+
}
|
|
4732
|
+
|
|
4733
|
+
function getEndOfCentralDirectoryInfo(scanArray, indexByte, offset) {
|
|
4734
|
+
return { offset, buffer: scanArray.slice(indexByte, indexByte + END_OF_CENTRAL_DIR_LENGTH).buffer };
|
|
4735
|
+
}
|
|
4736
|
+
|
|
4873
4737
|
async function getCentralDirectoryReachability(reader, view, anchoredOffset, indexByte, offset, size, remoteProbeBudget) {
|
|
4874
4738
|
const filesLength = getUint16(view, indexByte + 10);
|
|
4875
4739
|
const directoryDataLength = getUint32(view, indexByte + 12);
|
|
4876
4740
|
const directoryDataOffset = getUint32(view, indexByte + 16);
|
|
4877
4741
|
if (filesLength == MAX_16_BITS || directoryDataLength == MAX_32_BITS || directoryDataOffset == MAX_32_BITS) {
|
|
4878
|
-
// saturated fields imply a zip64 record; corroborate it with the zip64 end of central directory locator
|
|
4879
|
-
// that sits immediately before it, so saturated bytes inside a comment cannot masquerade as one
|
|
4880
4742
|
const locatorSignature = await readSignature(reader, view, anchoredOffset, offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH, size, remoteProbeBudget);
|
|
4881
4743
|
return locatorSignature == ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIGNATURE ? CENTRAL_DIRECTORY_REACHABLE : CENTRAL_DIRECTORY_UNREACHABLE;
|
|
4882
4744
|
}
|
|
4883
4745
|
if (!filesLength && !directoryDataLength) {
|
|
4884
|
-
// a valid empty archive has no central directory to dereference
|
|
4885
4746
|
return CENTRAL_DIRECTORY_PLAUSIBLE;
|
|
4886
4747
|
}
|
|
4887
|
-
// the central directory ends where this record starts; reconcile the stored offset against that actual
|
|
4888
|
-
// position (see stored_cd_offset ⟺ eocd_pos − cd_size) so prepended data does not hide the directory
|
|
4889
4748
|
for (const centralDirectoryOffset of [offset - directoryDataLength, directoryDataOffset]) {
|
|
4890
4749
|
if (await readSignature(reader, view, anchoredOffset, centralDirectoryOffset, size, remoteProbeBudget) == CENTRAL_FILE_HEADER_SIGNATURE) {
|
|
4891
4750
|
return CENTRAL_DIRECTORY_REACHABLE;
|
|
@@ -4894,15 +4753,11 @@
|
|
|
4894
4753
|
return CENTRAL_DIRECTORY_UNREACHABLE;
|
|
4895
4754
|
}
|
|
4896
4755
|
|
|
4897
|
-
// Reads a 4-byte little-endian signature at `signatureOffset`, served from the tail window (`view`, spanning
|
|
4898
|
-
// `[anchoredOffset, size)`) when it falls inside it and otherwise from a metered (possibly remote) read. Returns
|
|
4899
|
-
// UNDEFINED_VALUE when the position is out of range or the out-of-window read budget is exhausted.
|
|
4900
4756
|
async function readSignature(reader, view, anchoredOffset, signatureOffset, size, remoteProbeBudget) {
|
|
4901
4757
|
if (signatureOffset < 0 || signatureOffset + 4 > size) {
|
|
4902
4758
|
return UNDEFINED_VALUE;
|
|
4903
4759
|
}
|
|
4904
4760
|
if (signatureOffset >= anchoredOffset) {
|
|
4905
|
-
// the target sits inside the tail window already in memory: no extra read needed
|
|
4906
4761
|
return getUint32(view, signatureOffset - anchoredOffset);
|
|
4907
4762
|
}
|
|
4908
4763
|
if (remoteProbeBudget.count > 0) {
|
|
@@ -4910,7 +4765,6 @@
|
|
|
4910
4765
|
const signatureArray = await readUint8Array(reader, signatureOffset, 4);
|
|
4911
4766
|
return getUint32(getDataView$1(signatureArray), 0);
|
|
4912
4767
|
}
|
|
4913
|
-
// out of budget: leave this candidate unverified rather than issue another read
|
|
4914
4768
|
return UNDEFINED_VALUE;
|
|
4915
4769
|
}
|
|
4916
4770
|
|
|
@@ -4926,8 +4780,6 @@
|
|
|
4926
4780
|
if (localDirectory.compressionMethod != zipEntry.compressionMethod) {
|
|
4927
4781
|
throwAmbiguousArchive("mismatched local file header (compression method)");
|
|
4928
4782
|
}
|
|
4929
|
-
// when the data descriptor flag is set, the local signature and sizes are zeroed out; all-zero values are
|
|
4930
|
-
// also tolerated when the flag is clear because some tools produce them (streaming writers predating bit 3)
|
|
4931
4783
|
if (!localDirectory.bitFlag.dataDescriptor &&
|
|
4932
4784
|
(localDirectory.signature || localDirectory.compressedSize || localDirectory.uncompressedSize) &&
|
|
4933
4785
|
(localDirectory.signature != zipEntry.signature ||
|
|
@@ -5017,7 +4869,6 @@
|
|
|
5017
4869
|
const ERR_INVALID_ENCRYPTION_STRENGTH = "The strength must equal 1, 2, or 3";
|
|
5018
4870
|
const ERR_INVALID_EXTRAFIELD_TYPE = "Extra field type exceeds 65535";
|
|
5019
4871
|
const ERR_INVALID_EXTRAFIELD_DATA = "Extra field data exceeds 64KB";
|
|
5020
|
-
// same message as the constant exported by zip-reader.js
|
|
5021
4872
|
const ERR_UNSUPPORTED_COMPRESSION = "Compression method not supported";
|
|
5022
4873
|
const MIN_UNIX_TIME = -2147483648;
|
|
5023
4874
|
const MAX_UNIX_TIME = 2147483647;
|
|
@@ -5100,12 +4951,12 @@
|
|
|
5100
4951
|
rawExtraField,
|
|
5101
4952
|
} = entry;
|
|
5102
4953
|
const { level, languageEncodingFlag, dataDescriptor } = bitFlag;
|
|
5103
|
-
rawExtraFieldZip64 = rawExtraFieldZip64 ||
|
|
5104
|
-
rawExtraFieldAES = rawExtraFieldAES ||
|
|
5105
|
-
rawExtraFieldExtendedTimestamp = rawExtraFieldExtendedTimestamp ||
|
|
5106
|
-
rawExtraFieldNTFS = rawExtraFieldNTFS ||
|
|
5107
|
-
rawExtraFieldUnix = rawExtraFieldUnix ||
|
|
5108
|
-
rawExtraField = rawExtraField ||
|
|
4954
|
+
rawExtraFieldZip64 = rawExtraFieldZip64 || EMPTY_UINT8_ARRAY;
|
|
4955
|
+
rawExtraFieldAES = rawExtraFieldAES || EMPTY_UINT8_ARRAY;
|
|
4956
|
+
rawExtraFieldExtendedTimestamp = rawExtraFieldExtendedTimestamp || EMPTY_UINT8_ARRAY;
|
|
4957
|
+
rawExtraFieldNTFS = rawExtraFieldNTFS || EMPTY_UINT8_ARRAY;
|
|
4958
|
+
rawExtraFieldUnix = rawExtraFieldUnix || EMPTY_UINT8_ARRAY;
|
|
4959
|
+
rawExtraField = rawExtraField || EMPTY_UINT8_ARRAY;
|
|
5109
4960
|
if (entry.extraFieldAES) {
|
|
5110
4961
|
compressionMethod = COMPRESSION_METHOD_AES;
|
|
5111
4962
|
}
|
|
@@ -5211,7 +5062,7 @@
|
|
|
5211
5062
|
return false;
|
|
5212
5063
|
}
|
|
5213
5064
|
|
|
5214
|
-
async close(comment =
|
|
5065
|
+
async close(comment = EMPTY_UINT8_ARRAY, options = {}) {
|
|
5215
5066
|
const zipWriter = this;
|
|
5216
5067
|
const { pendingAddFileCalls, writer } = this;
|
|
5217
5068
|
const { writable } = writer;
|
|
@@ -5510,7 +5361,7 @@
|
|
|
5510
5361
|
if (!zipCrypto && (password !== UNDEFINED_VALUE || rawPassword !== UNDEFINED_VALUE) && !(encryptionStrength >= 1 && encryptionStrength <= 3)) {
|
|
5511
5362
|
throw new Error(ERR_INVALID_ENCRYPTION_STRENGTH);
|
|
5512
5363
|
}
|
|
5513
|
-
let rawExtraField =
|
|
5364
|
+
let rawExtraField = EMPTY_UINT8_ARRAY;
|
|
5514
5365
|
const extraField = options[PROPERTY_NAME_EXTRA_FIELD];
|
|
5515
5366
|
if (extraField) {
|
|
5516
5367
|
let extraFieldSize = 0;
|
|
@@ -5571,8 +5422,6 @@
|
|
|
5571
5422
|
let maximumCompressedSize = 0;
|
|
5572
5423
|
let uncompressedSize = 0;
|
|
5573
5424
|
if (passThrough) {
|
|
5574
|
-
// the headers of a passThrough entry describe the payload verbatim: without a
|
|
5575
|
-
// reader they would declare content that is not there
|
|
5576
5425
|
if (!reader) {
|
|
5577
5426
|
throw new Error(ERR_UNDEFINED_READER);
|
|
5578
5427
|
}
|
|
@@ -5583,8 +5432,6 @@
|
|
|
5583
5432
|
}
|
|
5584
5433
|
const zip64Enabled = zip64 === true;
|
|
5585
5434
|
const encrypted = getOptionValue(zipWriter, options, PROPERTY_NAME_ENCRYPTED);
|
|
5586
|
-
// entries without a reader have no payload: write them as empty stored files instead of
|
|
5587
|
-
// declaring a compressed or encrypted content that does not exist
|
|
5588
5435
|
const encryptedEntry = Boolean(reader) && (Boolean((password && getLength(password)) || (rawPassword && getLength(rawPassword))) || (passThrough && encrypted));
|
|
5589
5436
|
if (!reader) {
|
|
5590
5437
|
level = 0;
|
|
@@ -5722,7 +5569,6 @@
|
|
|
5722
5569
|
fileEntry.diskNumberStart = writer.diskNumber;
|
|
5723
5570
|
fileEntry.offset = getSegmentOffset(zipWriter, writer);
|
|
5724
5571
|
if (usdz) {
|
|
5725
|
-
// the padding depends on the entry offset, only known now that the writer is locked
|
|
5726
5572
|
const previousMetadataSize = entryInfo.metadataSize;
|
|
5727
5573
|
appendExtraFieldUSDZ(entryInfo, zipWriter.offset - writer.diskOffset);
|
|
5728
5574
|
fileEntry.size += entryInfo.metadataSize - previousMetadataSize;
|
|
@@ -5765,10 +5611,6 @@
|
|
|
5765
5611
|
if (releaseLockWriter) {
|
|
5766
5612
|
releaseLockWriter();
|
|
5767
5613
|
}
|
|
5768
|
-
// A buffered entry uses a temporary stream (see `createTempStream`). When that stream is
|
|
5769
|
-
// backed by a resource (a file, an OPFS handle, ...) it must be released on every exit path,
|
|
5770
|
-
// including the error path where the stream is otherwise abandoned without being closed or
|
|
5771
|
-
// cancelled. `dispose()` is optional and best-effort so it never masks the original outcome.
|
|
5772
5614
|
if (bufferedWrite && fileWriter && fileWriter.dispose) {
|
|
5773
5615
|
try {
|
|
5774
5616
|
await fileWriter.dispose();
|
|
@@ -5795,7 +5637,7 @@
|
|
|
5795
5637
|
async function skipDiskIfNeeded() {
|
|
5796
5638
|
if (getLength(headerInfo.localHeaderArray) > writer.availableSize) {
|
|
5797
5639
|
writer.availableSize = 0;
|
|
5798
|
-
await writeData(writer,
|
|
5640
|
+
await writeData(writer, EMPTY_UINT8_ARRAY);
|
|
5799
5641
|
}
|
|
5800
5642
|
}
|
|
5801
5643
|
}
|
|
@@ -5906,8 +5748,7 @@
|
|
|
5906
5748
|
}
|
|
5907
5749
|
const { writable } = writer;
|
|
5908
5750
|
if (reader) {
|
|
5909
|
-
reader.chunkSize
|
|
5910
|
-
const readable = reader.readable;
|
|
5751
|
+
const readable = reader.createReadable ? reader.createReadable({ chunkSize: getChunkSize(config) }) : reader.readable;
|
|
5911
5752
|
const size = reader.size;
|
|
5912
5753
|
const workerOptions = {
|
|
5913
5754
|
options: {
|
|
@@ -6007,13 +5848,9 @@
|
|
|
6007
5848
|
: compressionMethod !== COMPRESSION_METHOD_STORE);
|
|
6008
5849
|
let rawLocalExtraFieldZip64;
|
|
6009
5850
|
const uncompressedFile = passThrough || !compressed;
|
|
6010
|
-
// entries without a data descriptor are always buffered (cf. getFileEntry), so the
|
|
6011
|
-
// zip64 extra field values can be patched into the local header before flushing it
|
|
6012
5851
|
const zip64ExtraFieldComplete = zip64 && (options.bufferedWrite || !dataDescriptor || ((!zip64UncompressedSize && !zip64CompressedSize) || uncompressedFile));
|
|
6013
5852
|
const writeLocalExtraFieldZip64 = zip64ExtraFieldComplete || (zip64 && dataDescriptor && (zip64UncompressedSize || zip64CompressedSize));
|
|
6014
5853
|
if (zip64 && (zip64UncompressedSize || zip64CompressedSize)) {
|
|
6015
|
-
// APPNOTE 4.5.3: the zip64 extra field of a local header must include both the
|
|
6016
|
-
// uncompressed and compressed size fields
|
|
6017
5854
|
const length = 4 + 16;
|
|
6018
5855
|
const extraFieldZip64 = createRecordWriter(length);
|
|
6019
5856
|
extraFieldZip64.uint16(EXTRAFIELD_TYPE_ZIP64);
|
|
@@ -6027,7 +5864,7 @@
|
|
|
6027
5864
|
}
|
|
6028
5865
|
}
|
|
6029
5866
|
} else {
|
|
6030
|
-
rawLocalExtraFieldZip64 =
|
|
5867
|
+
rawLocalExtraFieldZip64 = EMPTY_UINT8_ARRAY;
|
|
6031
5868
|
}
|
|
6032
5869
|
let rawExtraFieldAES;
|
|
6033
5870
|
if (encrypted && !zipCrypto) {
|
|
@@ -6037,7 +5874,7 @@
|
|
|
6037
5874
|
rawExtraFieldAES = extraFieldAES.array;
|
|
6038
5875
|
rawExtraFieldAES[8] = encryptionStrength;
|
|
6039
5876
|
} else {
|
|
6040
|
-
rawExtraFieldAES =
|
|
5877
|
+
rawExtraFieldAES = EMPTY_UINT8_ARRAY;
|
|
6041
5878
|
}
|
|
6042
5879
|
let rawExtraFieldNTFS;
|
|
6043
5880
|
let rawExtraFieldExtendedTimestamp;
|
|
@@ -6060,8 +5897,7 @@
|
|
|
6060
5897
|
}
|
|
6061
5898
|
rawExtraFieldExtendedTimestamp = extraFieldTimestamp.array;
|
|
6062
5899
|
} else {
|
|
6063
|
-
|
|
6064
|
-
rawExtraFieldExtendedTimestamp = new Uint8Array();
|
|
5900
|
+
rawExtraFieldExtendedTimestamp = EMPTY_UINT8_ARRAY;
|
|
6065
5901
|
}
|
|
6066
5902
|
try {
|
|
6067
5903
|
const lastModTimeNTFS = getTimeNTFS(lastModDate);
|
|
@@ -6076,10 +5912,10 @@
|
|
|
6076
5912
|
extraFieldNTFS.uint64(getTimeNTFS(creationDate) || lastModTimeNTFS);
|
|
6077
5913
|
rawExtraFieldNTFS = extraFieldNTFS.array;
|
|
6078
5914
|
} catch {
|
|
6079
|
-
rawExtraFieldNTFS =
|
|
5915
|
+
rawExtraFieldNTFS = EMPTY_UINT8_ARRAY;
|
|
6080
5916
|
}
|
|
6081
5917
|
} else {
|
|
6082
|
-
rawExtraFieldNTFS = rawExtraFieldExtendedTimestamp =
|
|
5918
|
+
rawExtraFieldNTFS = rawExtraFieldExtendedTimestamp = EMPTY_UINT8_ARRAY;
|
|
6083
5919
|
}
|
|
6084
5920
|
let rawExtraFieldUnix;
|
|
6085
5921
|
try {
|
|
@@ -6105,10 +5941,10 @@
|
|
|
6105
5941
|
extraFieldUnix.uint16((gid === UNDEFINED_VALUE ? 0 : gid) & MAX_16_BITS);
|
|
6106
5942
|
rawExtraFieldUnix = extraFieldUnix.array;
|
|
6107
5943
|
} else {
|
|
6108
|
-
rawExtraFieldUnix =
|
|
5944
|
+
rawExtraFieldUnix = EMPTY_UINT8_ARRAY;
|
|
6109
5945
|
}
|
|
6110
5946
|
} catch {
|
|
6111
|
-
rawExtraFieldUnix =
|
|
5947
|
+
rawExtraFieldUnix = EMPTY_UINT8_ARRAY;
|
|
6112
5948
|
}
|
|
6113
5949
|
if (compressionMethod === UNDEFINED_VALUE) {
|
|
6114
5950
|
compressionMethod = compressed ? COMPRESSION_METHOD_DEFLATE : COMPRESSION_METHOD_STORE;
|
|
@@ -6175,7 +6011,7 @@
|
|
|
6175
6011
|
version,
|
|
6176
6012
|
compressionMethod,
|
|
6177
6013
|
extraFieldExtendedTimestampFlag,
|
|
6178
|
-
rawExtraFieldZip64:
|
|
6014
|
+
rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
|
|
6179
6015
|
localExtraFieldZip64Length,
|
|
6180
6016
|
rawExtraFieldExtendedTimestamp,
|
|
6181
6017
|
rawExtraFieldNTFS,
|
|
@@ -6188,7 +6024,6 @@
|
|
|
6188
6024
|
function appendExtraFieldUSDZ(entryInfo, zipWriterOffset) {
|
|
6189
6025
|
const { headerInfo } = entryInfo;
|
|
6190
6026
|
let { localHeaderArray, extraFieldLength } = headerInfo;
|
|
6191
|
-
let localHeaderArrayView;
|
|
6192
6027
|
let extraBytesLength = 64 - ((zipWriterOffset + getLength(localHeaderArray)) % 64);
|
|
6193
6028
|
if (extraBytesLength < 4) {
|
|
6194
6029
|
extraBytesLength += 64;
|
|
@@ -6201,7 +6036,7 @@
|
|
|
6201
6036
|
headerInfo.localHeaderArray = localHeaderArray = new Uint8Array(getLength(previousLocalHeaderArray) + extraBytesLength);
|
|
6202
6037
|
arraySet(localHeaderArray, previousLocalHeaderArray);
|
|
6203
6038
|
arraySet(localHeaderArray, rawExtraFieldUSDZ, getLength(previousLocalHeaderArray));
|
|
6204
|
-
localHeaderArrayView = getDataView(localHeaderArray);
|
|
6039
|
+
const localHeaderArrayView = getDataView(localHeaderArray);
|
|
6205
6040
|
setUint16(localHeaderArrayView, 28, extraFieldLength + extraBytesLength);
|
|
6206
6041
|
headerInfo.localHeaderView = localHeaderArrayView;
|
|
6207
6042
|
entryInfo.metadataSize += extraBytesLength;
|
|
@@ -6209,7 +6044,7 @@
|
|
|
6209
6044
|
|
|
6210
6045
|
function packUnixId(id) {
|
|
6211
6046
|
if (id === UNDEFINED_VALUE) {
|
|
6212
|
-
return
|
|
6047
|
+
return EMPTY_UINT8_ARRAY;
|
|
6213
6048
|
} else {
|
|
6214
6049
|
const dataArray = new Uint8Array(4);
|
|
6215
6050
|
const dataView = getDataView(dataArray);
|
|
@@ -6252,7 +6087,7 @@
|
|
|
6252
6087
|
dataDescriptor,
|
|
6253
6088
|
dataDescriptorSignature
|
|
6254
6089
|
}) {
|
|
6255
|
-
let dataDescriptorArray =
|
|
6090
|
+
let dataDescriptorArray = EMPTY_UINT8_ARRAY;
|
|
6256
6091
|
let dataDescriptorView, dataDescriptorOffset = 0;
|
|
6257
6092
|
let dataDescriptorLength = zip64 ? DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH : DATA_DESCRIPTOR_RECORD_LENGTH;
|
|
6258
6093
|
if (dataDescriptorSignature) {
|
|
@@ -6336,7 +6171,6 @@
|
|
|
6336
6171
|
}
|
|
6337
6172
|
}
|
|
6338
6173
|
if (zip64 && localExtraFieldZip64Length) {
|
|
6339
|
-
// the local zip64 extra field always contains both size slots
|
|
6340
6174
|
const localHeaderOffset = HEADER_SIZE + getLength(rawFilename) + 4;
|
|
6341
6175
|
setBigUint64(localHeaderView, localHeaderOffset, BigInt(uncompressedSize));
|
|
6342
6176
|
setBigUint64(localHeaderView, localHeaderOffset + 8, BigInt(compressedSize));
|
|
@@ -6390,7 +6224,7 @@
|
|
|
6390
6224
|
}
|
|
6391
6225
|
rawExtraFieldZip64 = extraFieldZip64.array;
|
|
6392
6226
|
} else {
|
|
6393
|
-
rawExtraFieldZip64 =
|
|
6227
|
+
rawExtraFieldZip64 = EMPTY_UINT8_ARRAY;
|
|
6394
6228
|
}
|
|
6395
6229
|
fileEntry.rawExtraFieldZip64 = rawExtraFieldZip64;
|
|
6396
6230
|
fileEntry.zip64Offset = zip64Offset;
|
|
@@ -6405,7 +6239,7 @@
|
|
|
6405
6239
|
extraFieldTimestamp.uint32(lastModTimeUnix);
|
|
6406
6240
|
rawExtraFieldTimestamp = extraFieldTimestamp.array;
|
|
6407
6241
|
} else {
|
|
6408
|
-
rawExtraFieldTimestamp =
|
|
6242
|
+
rawExtraFieldTimestamp = EMPTY_UINT8_ARRAY;
|
|
6409
6243
|
}
|
|
6410
6244
|
fileEntry.rawExtraFieldExtendedTimestamp = rawExtraFieldTimestamp;
|
|
6411
6245
|
const extraFieldLength = getLength(
|
|
@@ -6461,7 +6295,7 @@
|
|
|
6461
6295
|
await writeData(writer, directoryArray.slice(directoryDiskOffset, offset));
|
|
6462
6296
|
directoryDiskOffset = offset;
|
|
6463
6297
|
writer.availableSize = 0;
|
|
6464
|
-
await writeData(writer,
|
|
6298
|
+
await writeData(writer, EMPTY_UINT8_ARRAY);
|
|
6465
6299
|
}
|
|
6466
6300
|
if (indexFileEntry == 0) {
|
|
6467
6301
|
cdStartDiskNumber = writer.diskNumber;
|
|
@@ -6534,7 +6368,7 @@
|
|
|
6534
6368
|
const endOfdirectoryRecord = createRecordWriter(zip64 ? ZIP64_END_OF_CENTRAL_DIR_TOTAL_LENGTH : END_OF_CENTRAL_DIR_LENGTH);
|
|
6535
6369
|
if (getLength(endOfdirectoryRecord.array) + commentLength > writer.availableSize) {
|
|
6536
6370
|
writer.availableSize = 0;
|
|
6537
|
-
await writeData(writer,
|
|
6371
|
+
await writeData(writer, EMPTY_UINT8_ARRAY);
|
|
6538
6372
|
}
|
|
6539
6373
|
lastDiskNumber = writer.diskNumber;
|
|
6540
6374
|
if (zip64) {
|
|
@@ -6631,7 +6465,6 @@
|
|
|
6631
6465
|
return Math.floor(date.getTime() / 1000);
|
|
6632
6466
|
}
|
|
6633
6467
|
|
|
6634
|
-
// the extended timestamp extra field stores dates as signed 32-bit time_t values
|
|
6635
6468
|
function inUnixTimeRange(timeUnix) {
|
|
6636
6469
|
return timeUnix >= MIN_UNIX_TIME && timeUnix <= MAX_UNIX_TIME;
|
|
6637
6470
|
}
|
|
@@ -6813,39 +6646,15 @@
|
|
|
6813
6646
|
*/
|
|
6814
6647
|
|
|
6815
6648
|
|
|
6816
|
-
const DEFAULT_THRESHOLD = 1024 * 1024;
|
|
6817
|
-
const DEFAULT_DIRECTORY_NAME = ".zip.js-temp";
|
|
6649
|
+
const DEFAULT_THRESHOLD$2 = 1024 * 1024;
|
|
6650
|
+
const DEFAULT_DIRECTORY_NAME$1 = ".zip.js-temp";
|
|
6818
6651
|
|
|
6819
|
-
// Builds a `createTempStream` factory (see `ZipWriter`'s option of the same name) that spills the
|
|
6820
|
-
// data of a buffered entry to the Origin Private File System (OPFS) instead of keeping it in memory.
|
|
6821
|
-
//
|
|
6822
|
-
// It is meant for the buffered-write path (keep-order concurrent `add()`, non-seekable output, ...),
|
|
6823
|
-
// where the compressed data of an entry has to be held until the writer is free. In memory that data
|
|
6824
|
-
// grows with the entry size; here it is written to an OPFS file and streamed back, so peak memory
|
|
6825
|
-
// stays bounded.
|
|
6826
|
-
//
|
|
6827
|
-
// It is hybrid on purpose: an entry stays fully in memory until it exceeds `thresholdBytes`, and only
|
|
6828
|
-
// then spills to a file. Small entries never touch the disk, so a workload made of many tiny entries
|
|
6829
|
-
// keeps the speed of the in-memory default; only genuinely large entries pay for (and benefit from)
|
|
6830
|
-
// the file round-trip.
|
|
6831
|
-
//
|
|
6832
|
-
// OPFS is a browser/worker feature; there is no fallback here. Feature-detect
|
|
6833
|
-
// `navigator.storage.getDirectory` (or inject `getDirectory`) before using it, and let the writer use
|
|
6834
|
-
// its in-memory default elsewhere.
|
|
6835
|
-
//
|
|
6836
|
-
// Options:
|
|
6837
|
-
// thresholdBytes spill to a file once a buffered entry exceeds this size (default 1 MiB).
|
|
6838
|
-
// directoryName name of the OPFS sub-directory holding the temp files (default ".zip.js-temp").
|
|
6839
|
-
// getDirectory returns (or resolves to) the root `FileSystemDirectoryHandle`. Defaults to
|
|
6840
|
-
// `navigator.storage.getDirectory()`. Inject it to run inside a worker with a
|
|
6841
|
-
// pre-obtained handle, or to test against a mock.
|
|
6842
6652
|
function createOPFSTempStream(options = {}) {
|
|
6843
6653
|
const {
|
|
6844
|
-
thresholdBytes = DEFAULT_THRESHOLD,
|
|
6845
|
-
directoryName = DEFAULT_DIRECTORY_NAME,
|
|
6654
|
+
thresholdBytes = DEFAULT_THRESHOLD$2,
|
|
6655
|
+
directoryName = DEFAULT_DIRECTORY_NAME$1,
|
|
6846
6656
|
getDirectory = () => navigator.storage.getDirectory()
|
|
6847
6657
|
} = options;
|
|
6848
|
-
// The temp directory is resolved once and shared by every entry of this factory.
|
|
6849
6658
|
let directoryHandlePromise;
|
|
6850
6659
|
function getTempDirectory() {
|
|
6851
6660
|
if (!directoryHandlePromise) {
|
|
@@ -6854,15 +6663,12 @@
|
|
|
6854
6663
|
}
|
|
6855
6664
|
return directoryHandlePromise;
|
|
6856
6665
|
}
|
|
6857
|
-
return
|
|
6666
|
+
return function () {
|
|
6858
6667
|
const memoryChunks = [];
|
|
6859
6668
|
let bufferedSize = 0;
|
|
6860
6669
|
let spilled = false;
|
|
6861
6670
|
let fileName, fileHandle, fileWriter, fileReader;
|
|
6862
6671
|
|
|
6863
|
-
// Move whatever is buffered in memory to a fresh OPFS file, then keep writing to that file.
|
|
6864
|
-
// `FileSystemWritableFileStream` is a `WritableStream`, so it is driven through the standard
|
|
6865
|
-
// writer API rather than its non-standard `write()`/`seek()` convenience methods.
|
|
6866
6672
|
async function spillToFile() {
|
|
6867
6673
|
const directoryHandle = await getTempDirectory();
|
|
6868
6674
|
fileName = crypto.randomUUID();
|
|
@@ -6872,7 +6678,6 @@
|
|
|
6872
6678
|
for (const chunk of memoryChunks) {
|
|
6873
6679
|
await fileWriter.write(chunk);
|
|
6874
6680
|
}
|
|
6875
|
-
// Release the in-memory copy; from now on data lives in the file.
|
|
6876
6681
|
memoryChunks.length = 0;
|
|
6877
6682
|
}
|
|
6878
6683
|
|
|
@@ -6896,9 +6701,6 @@
|
|
|
6896
6701
|
}
|
|
6897
6702
|
});
|
|
6898
6703
|
|
|
6899
|
-
// The writer always closes `writable` before reading `readable`, so `spilled` is final by the
|
|
6900
|
-
// time `pull` runs. It is read lazily here (not in `start`, which runs at construction time,
|
|
6901
|
-
// before anything is written and before `spilled` is known).
|
|
6902
6704
|
let memoryIndex = 0;
|
|
6903
6705
|
const readable = new ReadableStream({
|
|
6904
6706
|
async pull(controller) {
|
|
@@ -6925,11 +6727,6 @@
|
|
|
6925
6727
|
}
|
|
6926
6728
|
}
|
|
6927
6729
|
}, { highWaterMark: 0 });
|
|
6928
|
-
// highWaterMark 0: do not pull (and possibly close on an empty buffer) until the consumer
|
|
6929
|
-
// actually reads, which the writer only does once the data has been fully written.
|
|
6930
|
-
|
|
6931
|
-
// Called by the writer on every exit path (success, error, abort). Best-effort: it must never
|
|
6932
|
-
// throw. Closes any still-open file writer, then deletes the temp file.
|
|
6933
6730
|
async function dispose() {
|
|
6934
6731
|
if (fileWriter) {
|
|
6935
6732
|
try {
|
|
@@ -6984,6 +6781,269 @@
|
|
|
6984
6781
|
*/
|
|
6985
6782
|
|
|
6986
6783
|
|
|
6784
|
+
const DEFAULT_THRESHOLD$1 = 1024 * 1024;
|
|
6785
|
+
|
|
6786
|
+
function createBlobTempStream(options = {}) {
|
|
6787
|
+
const {
|
|
6788
|
+
thresholdBytes = DEFAULT_THRESHOLD$1
|
|
6789
|
+
} = options;
|
|
6790
|
+
return function () {
|
|
6791
|
+
const memoryChunks = [];
|
|
6792
|
+
let bufferedSize = 0;
|
|
6793
|
+
let spilled = false;
|
|
6794
|
+
let blobWriter, blobPromise, blobReader;
|
|
6795
|
+
|
|
6796
|
+
async function spillToBlob() {
|
|
6797
|
+
const transformStream = new TransformStream();
|
|
6798
|
+
blobPromise = new Response(transformStream.readable).blob();
|
|
6799
|
+
blobWriter = transformStream.writable.getWriter();
|
|
6800
|
+
spilled = true;
|
|
6801
|
+
for (const chunk of memoryChunks) {
|
|
6802
|
+
await blobWriter.write(chunk);
|
|
6803
|
+
}
|
|
6804
|
+
memoryChunks.length = 0;
|
|
6805
|
+
}
|
|
6806
|
+
|
|
6807
|
+
const writable = new WritableStream({
|
|
6808
|
+
async write(chunk) {
|
|
6809
|
+
if (spilled) {
|
|
6810
|
+
await blobWriter.write(chunk);
|
|
6811
|
+
} else {
|
|
6812
|
+
memoryChunks.push(chunk);
|
|
6813
|
+
bufferedSize += chunk.length;
|
|
6814
|
+
if (bufferedSize > thresholdBytes) {
|
|
6815
|
+
await spillToBlob();
|
|
6816
|
+
}
|
|
6817
|
+
}
|
|
6818
|
+
},
|
|
6819
|
+
async close() {
|
|
6820
|
+
if (blobWriter) {
|
|
6821
|
+
await blobWriter.close();
|
|
6822
|
+
blobWriter = null;
|
|
6823
|
+
}
|
|
6824
|
+
}
|
|
6825
|
+
});
|
|
6826
|
+
|
|
6827
|
+
let memoryIndex = 0;
|
|
6828
|
+
const readable = new ReadableStream({
|
|
6829
|
+
async pull(controller) {
|
|
6830
|
+
if (spilled) {
|
|
6831
|
+
if (!blobReader) {
|
|
6832
|
+
const blob = await blobPromise;
|
|
6833
|
+
blobReader = blob.stream().getReader();
|
|
6834
|
+
}
|
|
6835
|
+
const { value, done } = await blobReader.read();
|
|
6836
|
+
if (done) {
|
|
6837
|
+
controller.close();
|
|
6838
|
+
} else {
|
|
6839
|
+
controller.enqueue(value);
|
|
6840
|
+
}
|
|
6841
|
+
} else if (memoryIndex < memoryChunks.length) {
|
|
6842
|
+
controller.enqueue(memoryChunks[memoryIndex++]);
|
|
6843
|
+
} else {
|
|
6844
|
+
controller.close();
|
|
6845
|
+
}
|
|
6846
|
+
},
|
|
6847
|
+
async cancel(reason) {
|
|
6848
|
+
if (blobReader) {
|
|
6849
|
+
await blobReader.cancel(reason);
|
|
6850
|
+
}
|
|
6851
|
+
}
|
|
6852
|
+
}, { highWaterMark: 0 });
|
|
6853
|
+
async function dispose() {
|
|
6854
|
+
if (blobWriter) {
|
|
6855
|
+
try {
|
|
6856
|
+
await blobWriter.abort();
|
|
6857
|
+
} catch {
|
|
6858
|
+
// ignored
|
|
6859
|
+
}
|
|
6860
|
+
blobWriter = null;
|
|
6861
|
+
}
|
|
6862
|
+
if (blobPromise) {
|
|
6863
|
+
blobPromise.catch(() => {
|
|
6864
|
+
// ignored
|
|
6865
|
+
});
|
|
6866
|
+
blobPromise = null;
|
|
6867
|
+
}
|
|
6868
|
+
memoryChunks.length = 0;
|
|
6869
|
+
}
|
|
6870
|
+
|
|
6871
|
+
return { writable, readable, dispose };
|
|
6872
|
+
};
|
|
6873
|
+
}
|
|
6874
|
+
|
|
6875
|
+
/*
|
|
6876
|
+
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
|
6877
|
+
|
|
6878
|
+
Redistribution and use in source and binary forms, with or without
|
|
6879
|
+
modification, are permitted provided that the following conditions are met:
|
|
6880
|
+
|
|
6881
|
+
1. Redistributions of source code must retain the above copyright notice,
|
|
6882
|
+
this list of conditions and the following disclaimer.
|
|
6883
|
+
|
|
6884
|
+
2. Redistributions in binary form must reproduce the above copyright
|
|
6885
|
+
notice, this list of conditions and the following disclaimer in
|
|
6886
|
+
the documentation and/or other materials provided with the distribution.
|
|
6887
|
+
|
|
6888
|
+
3. The names of the authors may not be used to endorse or promote products
|
|
6889
|
+
derived from this software without specific prior written permission.
|
|
6890
|
+
|
|
6891
|
+
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
|
6892
|
+
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
6893
|
+
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
|
6894
|
+
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
6895
|
+
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
6896
|
+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
6897
|
+
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
6898
|
+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
6899
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
6900
|
+
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
6901
|
+
*/
|
|
6902
|
+
|
|
6903
|
+
|
|
6904
|
+
const DEFAULT_THRESHOLD = 1024 * 1024;
|
|
6905
|
+
const DEFAULT_DIRECTORY_NAME = ".zip.js-temp";
|
|
6906
|
+
const READ_CHUNK_SIZE = 512 * 1024;
|
|
6907
|
+
const ERR_UNSUPPORTED_CONTEXT = "createSyncAccessHandle is only available in dedicated workers";
|
|
6908
|
+
|
|
6909
|
+
function createSyncAccessHandleTempStream(options = {}) {
|
|
6910
|
+
const {
|
|
6911
|
+
thresholdBytes = DEFAULT_THRESHOLD,
|
|
6912
|
+
directoryName = DEFAULT_DIRECTORY_NAME,
|
|
6913
|
+
getDirectory
|
|
6914
|
+
} = options;
|
|
6915
|
+
if (!getDirectory &&
|
|
6916
|
+
(typeof FileSystemFileHandle == "undefined" || !FileSystemFileHandle.prototype.createSyncAccessHandle)) {
|
|
6917
|
+
throw new Error(ERR_UNSUPPORTED_CONTEXT);
|
|
6918
|
+
}
|
|
6919
|
+
const getRootDirectory = getDirectory || (() => navigator.storage.getDirectory());
|
|
6920
|
+
let directoryHandlePromise;
|
|
6921
|
+
function getTempDirectory() {
|
|
6922
|
+
if (!directoryHandlePromise) {
|
|
6923
|
+
directoryHandlePromise = Promise.resolve(getRootDirectory())
|
|
6924
|
+
.then(root => root.getDirectoryHandle(directoryName, { create: true }));
|
|
6925
|
+
}
|
|
6926
|
+
return directoryHandlePromise;
|
|
6927
|
+
}
|
|
6928
|
+
return function () {
|
|
6929
|
+
const memoryChunks = [];
|
|
6930
|
+
let bufferedSize = 0;
|
|
6931
|
+
let spilled = false;
|
|
6932
|
+
let fileName, accessHandle;
|
|
6933
|
+
let writeOffset = 0;
|
|
6934
|
+
let readOffset = 0;
|
|
6935
|
+
|
|
6936
|
+
async function spillToFile() {
|
|
6937
|
+
const directoryHandle = await getTempDirectory();
|
|
6938
|
+
fileName = crypto.randomUUID();
|
|
6939
|
+
const fileHandle = await directoryHandle.getFileHandle(fileName, { create: true });
|
|
6940
|
+
accessHandle = await fileHandle.createSyncAccessHandle();
|
|
6941
|
+
spilled = true;
|
|
6942
|
+
for (const chunk of memoryChunks) {
|
|
6943
|
+
accessHandle.write(chunk, { at: writeOffset });
|
|
6944
|
+
writeOffset += chunk.length;
|
|
6945
|
+
}
|
|
6946
|
+
memoryChunks.length = 0;
|
|
6947
|
+
}
|
|
6948
|
+
|
|
6949
|
+
const writable = new WritableStream({
|
|
6950
|
+
async write(chunk) {
|
|
6951
|
+
if (spilled) {
|
|
6952
|
+
accessHandle.write(chunk, { at: writeOffset });
|
|
6953
|
+
writeOffset += chunk.length;
|
|
6954
|
+
} else {
|
|
6955
|
+
memoryChunks.push(chunk);
|
|
6956
|
+
bufferedSize += chunk.length;
|
|
6957
|
+
if (bufferedSize > thresholdBytes) {
|
|
6958
|
+
await spillToFile();
|
|
6959
|
+
}
|
|
6960
|
+
}
|
|
6961
|
+
},
|
|
6962
|
+
close() {
|
|
6963
|
+
if (accessHandle) {
|
|
6964
|
+
accessHandle.flush();
|
|
6965
|
+
}
|
|
6966
|
+
}
|
|
6967
|
+
});
|
|
6968
|
+
|
|
6969
|
+
let memoryIndex = 0;
|
|
6970
|
+
const readable = new ReadableStream({
|
|
6971
|
+
pull(controller) {
|
|
6972
|
+
if (spilled) {
|
|
6973
|
+
const remaining = writeOffset - readOffset;
|
|
6974
|
+
if (remaining <= 0) {
|
|
6975
|
+
controller.close();
|
|
6976
|
+
return;
|
|
6977
|
+
}
|
|
6978
|
+
const buffer = new Uint8Array(Math.min(READ_CHUNK_SIZE, remaining));
|
|
6979
|
+
const read = accessHandle.read(buffer, { at: readOffset });
|
|
6980
|
+
if (read) {
|
|
6981
|
+
readOffset += read;
|
|
6982
|
+
controller.enqueue(buffer.subarray(0, read));
|
|
6983
|
+
} else {
|
|
6984
|
+
controller.close();
|
|
6985
|
+
}
|
|
6986
|
+
} else if (memoryIndex < memoryChunks.length) {
|
|
6987
|
+
controller.enqueue(memoryChunks[memoryIndex++]);
|
|
6988
|
+
} else {
|
|
6989
|
+
controller.close();
|
|
6990
|
+
}
|
|
6991
|
+
}
|
|
6992
|
+
}, { highWaterMark: 0 });
|
|
6993
|
+
async function dispose() {
|
|
6994
|
+
if (accessHandle) {
|
|
6995
|
+
try {
|
|
6996
|
+
accessHandle.close();
|
|
6997
|
+
} catch {
|
|
6998
|
+
// ignored
|
|
6999
|
+
}
|
|
7000
|
+
accessHandle = null;
|
|
7001
|
+
}
|
|
7002
|
+
if (fileName) {
|
|
7003
|
+
try {
|
|
7004
|
+
const directoryHandle = await getTempDirectory();
|
|
7005
|
+
await directoryHandle.removeEntry(fileName);
|
|
7006
|
+
} catch {
|
|
7007
|
+
// ignored
|
|
7008
|
+
}
|
|
7009
|
+
fileName = null;
|
|
7010
|
+
}
|
|
7011
|
+
memoryChunks.length = 0;
|
|
7012
|
+
}
|
|
7013
|
+
|
|
7014
|
+
return { writable, readable, dispose };
|
|
7015
|
+
};
|
|
7016
|
+
}
|
|
7017
|
+
|
|
7018
|
+
/*
|
|
7019
|
+
Copyright (c) 2025 Gildas Lormeau. All rights reserved.
|
|
7020
|
+
|
|
7021
|
+
Redistribution and use in source and binary forms, with or without
|
|
7022
|
+
modification, are permitted provided that the following conditions are met:
|
|
7023
|
+
|
|
7024
|
+
1. Redistributions of source code must retain the above copyright notice,
|
|
7025
|
+
this list of conditions and the following disclaimer.
|
|
7026
|
+
|
|
7027
|
+
2. Redistributions in binary form must reproduce the above copyright
|
|
7028
|
+
notice, this list of conditions and the following disclaimer in
|
|
7029
|
+
the documentation and/or other materials provided with the distribution.
|
|
7030
|
+
|
|
7031
|
+
3. The names of the authors may not be used to endorse or promote products
|
|
7032
|
+
derived from this software without specific prior written permission.
|
|
7033
|
+
|
|
7034
|
+
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
|
7035
|
+
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
7036
|
+
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
|
7037
|
+
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
7038
|
+
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
7039
|
+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
7040
|
+
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
7041
|
+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
7042
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
7043
|
+
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
7044
|
+
*/
|
|
7045
|
+
|
|
7046
|
+
|
|
6987
7047
|
try {
|
|
6988
7048
|
configure({ baseURI: (typeof document === 'undefined' && typeof location === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : typeof document === 'undefined' ? location.href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('zip-core.js', document.baseURI).href)) });
|
|
6989
7049
|
} catch {
|
|
@@ -7014,6 +7074,7 @@
|
|
|
7014
7074
|
exports.ERR_INVALID_SIGNATURE = ERR_INVALID_SIGNATURE;
|
|
7015
7075
|
exports.ERR_INVALID_UNCOMPRESSED_SIZE = ERR_INVALID_UNCOMPRESSED_SIZE;
|
|
7016
7076
|
exports.ERR_INVALID_VERSION = ERR_INVALID_VERSION;
|
|
7077
|
+
exports.ERR_ITERATOR_COMPLETED_TOO_SOON = ERR_ITERATOR_COMPLETED_TOO_SOON;
|
|
7017
7078
|
exports.ERR_LOCAL_FILE_HEADER_NOT_FOUND = ERR_LOCAL_FILE_HEADER_NOT_FOUND;
|
|
7018
7079
|
exports.ERR_OVERLAPPING_ENTRY = ERR_OVERLAPPING_ENTRY;
|
|
7019
7080
|
exports.ERR_SPLIT_ZIP_FILE = ERR_SPLIT_ZIP_FILE;
|
|
@@ -7022,6 +7083,7 @@
|
|
|
7022
7083
|
exports.ERR_UNSUPPORTED_COMPRESSION = ERR_UNSUPPORTED_COMPRESSION$1;
|
|
7023
7084
|
exports.ERR_UNSUPPORTED_ENCRYPTION = ERR_UNSUPPORTED_ENCRYPTION;
|
|
7024
7085
|
exports.ERR_UNSUPPORTED_FORMAT = ERR_UNSUPPORTED_FORMAT;
|
|
7086
|
+
exports.ERR_WRITER_NOT_INITIALIZED = ERR_WRITER_NOT_INITIALIZED;
|
|
7025
7087
|
exports.ERR_ZIP_NOT_EMPTY = ERR_ZIP_NOT_EMPTY;
|
|
7026
7088
|
exports.HttpRangeReader = HttpRangeReader;
|
|
7027
7089
|
exports.HttpReader = HttpReader;
|
|
@@ -7038,7 +7100,9 @@
|
|
|
7038
7100
|
exports.ZipWriter = ZipWriter;
|
|
7039
7101
|
exports.ZipWriterStream = ZipWriterStream;
|
|
7040
7102
|
exports.configure = configure;
|
|
7103
|
+
exports.createBlobTempStream = createBlobTempStream;
|
|
7041
7104
|
exports.createOPFSTempStream = createOPFSTempStream;
|
|
7105
|
+
exports.createSyncAccessHandleTempStream = createSyncAccessHandleTempStream;
|
|
7042
7106
|
exports.getMimeType = getMimeType;
|
|
7043
7107
|
exports.terminateWorkers = terminateWorkers;
|
|
7044
7108
|
|