@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/lib/core/codec-worker.js
CHANGED
|
@@ -194,9 +194,6 @@ async function runWorker({ options, readable, writable, onTaskFinished }, config
|
|
|
194
194
|
try {
|
|
195
195
|
await initModule(config);
|
|
196
196
|
} catch {
|
|
197
|
-
// The WASM module failed to load. Fall back to the native CompressionStream only if the
|
|
198
|
-
// zlib codec that would otherwise run depends on that module; a self-contained codec
|
|
199
|
-
// supplied through config (e.g. the pure-JS port) stays usable and is kept as requested.
|
|
200
197
|
const ZlibStream = options.codecType.startsWith(CODEC_DEFLATE) ?
|
|
201
198
|
config.CompressionStreamZlib :
|
|
202
199
|
config.DecompressionStreamZlib;
|
|
@@ -274,7 +271,6 @@ async function runWebWorker(workerData, config) {
|
|
|
274
271
|
return resultValue;
|
|
275
272
|
} catch (error) {
|
|
276
273
|
await closeWritable();
|
|
277
|
-
// unblock the pipe watcher if the worker died before closing its end of the stream
|
|
278
274
|
abortPipe();
|
|
279
275
|
try {
|
|
280
276
|
await closed;
|
|
@@ -299,8 +295,6 @@ function watchClosedStream(writableSource) {
|
|
|
299
295
|
const abortController = new AbortController();
|
|
300
296
|
const { writable, readable } = new TransformStream();
|
|
301
297
|
const closed = readable.pipeTo(writableSource, { preventClose: true, preventAbort: true, signal: abortController.signal });
|
|
302
|
-
// the pipe can fail before `closed` is awaited (e.g. the destination write fails);
|
|
303
|
-
// mark the rejection as handled so it cannot kill the process in the meantime
|
|
304
298
|
closed.catch(() => { });
|
|
305
299
|
return { writable, closed, abortPipe: () => abortController.abort() };
|
|
306
300
|
}
|
|
@@ -393,8 +387,6 @@ function onWorkerError(event, workerData) {
|
|
|
393
387
|
event.preventDefault();
|
|
394
388
|
}
|
|
395
389
|
const { rejectResult, writer, onTaskFinished } = workerData;
|
|
396
|
-
// the worker is unusable (e.g. its script failed to load); terminate it and
|
|
397
|
-
// fail the current task instead of letting it hang forever
|
|
398
390
|
terminateWorker(workerData);
|
|
399
391
|
if (rejectResult) {
|
|
400
392
|
rejectResult(event.error || new Error(event.message || ERROR_EVENT_TYPE));
|
|
@@ -410,7 +402,7 @@ function sendMessage(message, { worker, writer, transferStreams }) {
|
|
|
410
402
|
const { value, readable, writable } = message;
|
|
411
403
|
const transferables = [];
|
|
412
404
|
if (value) {
|
|
413
|
-
message.value = value;
|
|
405
|
+
message.value = value.byteOffset || value.byteLength != value.buffer.byteLength ? new Uint8Array(value) : value;
|
|
414
406
|
transferables.push(message.value.buffer);
|
|
415
407
|
}
|
|
416
408
|
if (transferStreams && transferStreamsSupported) {
|
|
@@ -446,8 +438,6 @@ function sendMessage(message, { worker, writer, transferStreams }) {
|
|
|
446
438
|
async function onMessage({ data }, workerData) {
|
|
447
439
|
const { type, value, messageId, result, error } = data;
|
|
448
440
|
const { reader, writer, resolveResult, rejectResult, onTaskFinished, generation } = workerData;
|
|
449
|
-
// continuations below can settle after the worker slot has been reassigned to a new
|
|
450
|
-
// task (e.g. a pending pull read failing late); they must not touch the new task
|
|
451
441
|
const stale = () => workerData.generation != generation;
|
|
452
442
|
try {
|
|
453
443
|
if (error) {
|
|
@@ -55,6 +55,22 @@ const DEFAULT_CONFIGURATION = {
|
|
|
55
55
|
DecompressionStream: typeof DecompressionStream != UNDEFINED_TYPE && DecompressionStream
|
|
56
56
|
};
|
|
57
57
|
|
|
58
|
+
const CONFIGURABLE_PROPERTY_NAMES = [
|
|
59
|
+
"baseURI",
|
|
60
|
+
"wasmURI",
|
|
61
|
+
"workerURI",
|
|
62
|
+
"chunkSize",
|
|
63
|
+
"maxWorkers",
|
|
64
|
+
"terminateWorkerTimeout",
|
|
65
|
+
"workerStarvationTimeout",
|
|
66
|
+
"useCompressionStream",
|
|
67
|
+
"useWebWorkers",
|
|
68
|
+
"CompressionStream",
|
|
69
|
+
"DecompressionStream",
|
|
70
|
+
"CompressionStreamZlib",
|
|
71
|
+
"DecompressionStreamZlib"
|
|
72
|
+
];
|
|
73
|
+
|
|
58
74
|
const config = Object.assign({}, DEFAULT_CONFIGURATION);
|
|
59
75
|
|
|
60
76
|
export {
|
|
@@ -72,38 +88,10 @@ function getChunkSize(config) {
|
|
|
72
88
|
}
|
|
73
89
|
|
|
74
90
|
function configure(configuration) {
|
|
75
|
-
const {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
workerStarvationTimeout,
|
|
81
|
-
useCompressionStream,
|
|
82
|
-
useWebWorkers,
|
|
83
|
-
CompressionStream,
|
|
84
|
-
DecompressionStream,
|
|
85
|
-
CompressionStreamZlib,
|
|
86
|
-
DecompressionStreamZlib,
|
|
87
|
-
workerURI,
|
|
88
|
-
wasmURI
|
|
89
|
-
} = configuration;
|
|
90
|
-
setIfDefined("baseURI", baseURI);
|
|
91
|
-
setIfDefined("wasmURI", wasmURI);
|
|
92
|
-
setIfDefined("workerURI", workerURI);
|
|
93
|
-
setIfDefined("chunkSize", chunkSize);
|
|
94
|
-
setIfDefined("maxWorkers", maxWorkers);
|
|
95
|
-
setIfDefined("terminateWorkerTimeout", terminateWorkerTimeout);
|
|
96
|
-
setIfDefined("workerStarvationTimeout", workerStarvationTimeout);
|
|
97
|
-
setIfDefined("useCompressionStream", useCompressionStream);
|
|
98
|
-
setIfDefined("useWebWorkers", useWebWorkers);
|
|
99
|
-
setIfDefined("CompressionStream", CompressionStream);
|
|
100
|
-
setIfDefined("DecompressionStream", DecompressionStream);
|
|
101
|
-
setIfDefined("CompressionStreamZlib", CompressionStreamZlib);
|
|
102
|
-
setIfDefined("DecompressionStreamZlib", DecompressionStreamZlib);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function setIfDefined(propertyName, propertyValue) {
|
|
106
|
-
if (propertyValue !== UNDEFINED_VALUE) {
|
|
107
|
-
config[propertyName] = propertyValue;
|
|
91
|
+
for (const propertyName of CONFIGURABLE_PROPERTY_NAMES) {
|
|
92
|
+
const propertyValue = configuration[propertyName];
|
|
93
|
+
if (propertyValue !== UNDEFINED_VALUE) {
|
|
94
|
+
config[propertyName] = propertyValue;
|
|
95
|
+
}
|
|
108
96
|
}
|
|
109
97
|
}
|
package/lib/core/constants.js
CHANGED
|
@@ -104,7 +104,10 @@ const UNDEFINED_TYPE = "undefined";
|
|
|
104
104
|
const FUNCTION_TYPE = "function";
|
|
105
105
|
const OBJECT_TYPE = "object";
|
|
106
106
|
|
|
107
|
+
const EMPTY_UINT8_ARRAY = new Uint8Array();
|
|
108
|
+
|
|
107
109
|
export {
|
|
110
|
+
EMPTY_UINT8_ARRAY,
|
|
108
111
|
MAX_32_BITS,
|
|
109
112
|
MAX_16_BITS,
|
|
110
113
|
MAX_8_BITS,
|
package/lib/core/io.js
CHANGED
|
@@ -33,7 +33,8 @@ import {
|
|
|
33
33
|
UNDEFINED_VALUE,
|
|
34
34
|
INFINITY_VALUE,
|
|
35
35
|
FUNCTION_TYPE,
|
|
36
|
-
END_OF_CENTRAL_DIR_LENGTH
|
|
36
|
+
END_OF_CENTRAL_DIR_LENGTH,
|
|
37
|
+
EMPTY_UINT8_ARRAY
|
|
37
38
|
} from "./constants.js";
|
|
38
39
|
import { getConfiguration } from "./configuration.js";
|
|
39
40
|
|
|
@@ -71,26 +72,24 @@ class Stream {
|
|
|
71
72
|
class Reader extends Stream {
|
|
72
73
|
|
|
73
74
|
get readable() {
|
|
75
|
+
return this.createReadable();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
createReadable({ offset = 0, size, diskNumberStart, chunkSize = DEFAULT_CHUNK_SIZE } = {}) {
|
|
74
79
|
const reader = this;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
start() {
|
|
78
|
-
this.chunkOffset = 0;
|
|
79
|
-
},
|
|
80
|
+
let chunkOffset = 0;
|
|
81
|
+
return new ReadableStream({
|
|
80
82
|
async pull(controller) {
|
|
81
|
-
const { offset = 0, size, diskNumberStart } = readable;
|
|
82
|
-
const { chunkOffset } = this;
|
|
83
83
|
const dataSize = size === UNDEFINED_VALUE ? chunkSize : Math.min(chunkSize, size - chunkOffset);
|
|
84
84
|
const data = await readUint8Array(reader, offset + chunkOffset, dataSize, diskNumberStart);
|
|
85
85
|
controller.enqueue(data);
|
|
86
86
|
if ((chunkOffset + chunkSize > size) || (size === UNDEFINED_VALUE && !data.length && dataSize)) {
|
|
87
87
|
controller.close();
|
|
88
88
|
} else {
|
|
89
|
-
|
|
89
|
+
chunkOffset += chunkSize;
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
});
|
|
93
|
-
return readable;
|
|
94
93
|
}
|
|
95
94
|
}
|
|
96
95
|
|
|
@@ -229,8 +228,6 @@ class BlobWriter extends Stream {
|
|
|
229
228
|
}
|
|
230
229
|
});
|
|
231
230
|
writer.blob = new Response(transformStream.readable, { headers }).blob();
|
|
232
|
-
// mark early rejections (e.g. aborted pipe) as handled; awaiting getData() still
|
|
233
|
-
// surfaces them
|
|
234
231
|
writer.blob.catch(() => { });
|
|
235
232
|
}
|
|
236
233
|
|
|
@@ -392,7 +389,7 @@ async function readUint8ArrayHttpReader(httpReader, index, length, sendRequest,
|
|
|
392
389
|
return eocdCache;
|
|
393
390
|
}
|
|
394
391
|
if (index >= size || length === 0) {
|
|
395
|
-
return
|
|
392
|
+
return EMPTY_UINT8_ARRAY;
|
|
396
393
|
} else {
|
|
397
394
|
if (index + length > size) {
|
|
398
395
|
length = size - index;
|
|
@@ -449,8 +446,6 @@ async function getXMLHttpRequestData(httpReader) {
|
|
|
449
446
|
async function getRequestData(httpReader, sendRequest) {
|
|
450
447
|
const response = await sendRequest(HTTP_METHOD_GET, httpReader, getHeaders(httpReader));
|
|
451
448
|
httpReader.data = new Uint8Array(await response.arrayBuffer());
|
|
452
|
-
// the downloaded content is authoritative, e.g. the Content-Length response header
|
|
453
|
-
// of the HEAD request can be the encoded size when the content is compressed
|
|
454
449
|
httpReader.size = httpReader.data.length;
|
|
455
450
|
}
|
|
456
451
|
|
|
@@ -515,7 +510,6 @@ class HttpReader extends Reader {
|
|
|
515
510
|
super();
|
|
516
511
|
Object.assign(this, {
|
|
517
512
|
url,
|
|
518
|
-
// an explicit fetch implementation takes precedence over useXHR
|
|
519
513
|
reader: options.useXHR && !options.fetch ? new XHRReader(url, options) : new FetchReader(url, options)
|
|
520
514
|
});
|
|
521
515
|
}
|
|
@@ -654,7 +648,7 @@ class SplitDataReader extends Reader {
|
|
|
654
648
|
}
|
|
655
649
|
}
|
|
656
650
|
} else {
|
|
657
|
-
result =
|
|
651
|
+
result = EMPTY_UINT8_ARRAY;
|
|
658
652
|
}
|
|
659
653
|
reader.lastDiskNumber = Math.max(currentDiskNumber, reader.lastDiskNumber);
|
|
660
654
|
return result;
|
|
@@ -699,8 +693,6 @@ class SplitDataWriter extends Stream {
|
|
|
699
693
|
writer.diskOffset += diskSourceWriter.size;
|
|
700
694
|
writer.diskNumber++;
|
|
701
695
|
diskWriter = null;
|
|
702
|
-
// the next disk is not opened yet but its space is available; leaving a
|
|
703
|
-
// stale value here would force a new disk for every subsequent write
|
|
704
696
|
writer.availableSize = writer.maxSize;
|
|
705
697
|
if (chunk.length > availableSize) {
|
|
706
698
|
await this.write(chunk.subarray(availableSize));
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
/* global crypto, TransformStream */
|
|
30
30
|
// deno-lint-ignore-file no-this-alias
|
|
31
31
|
|
|
32
|
-
import { UNDEFINED_VALUE, UNDEFINED_TYPE, FUNCTION_TYPE } from "../constants.js";
|
|
32
|
+
import { UNDEFINED_VALUE, UNDEFINED_TYPE, FUNCTION_TYPE, EMPTY_UINT8_ARRAY } from "../constants.js";
|
|
33
33
|
import { encodeText } from "./../util/encode-text.js";
|
|
34
34
|
import {
|
|
35
35
|
cipher,
|
|
@@ -73,12 +73,7 @@ class AESDecryptionStream extends TransformStream {
|
|
|
73
73
|
constructor({ password, rawPassword, encryptionStrength, checkPasswordOnly }) {
|
|
74
74
|
super({
|
|
75
75
|
start() {
|
|
76
|
-
|
|
77
|
-
ready: new Promise(resolve => this.resolveReady = resolve),
|
|
78
|
-
password: encodePassword(password, rawPassword),
|
|
79
|
-
strength: encryptionStrength - 1,
|
|
80
|
-
pending: new Uint8Array()
|
|
81
|
-
});
|
|
76
|
+
initAesCrypto(this, password, rawPassword, encryptionStrength);
|
|
82
77
|
},
|
|
83
78
|
async transform(chunk, controller) {
|
|
84
79
|
const aesCrypto = this;
|
|
@@ -113,7 +108,7 @@ class AESDecryptionStream extends TransformStream {
|
|
|
113
108
|
await ready;
|
|
114
109
|
const chunkToDecrypt = subarray(pending, 0, pending.length - SIGNATURE_LENGTH);
|
|
115
110
|
const originalSignature = subarray(pending, pending.length - SIGNATURE_LENGTH);
|
|
116
|
-
let decryptedChunkArray =
|
|
111
|
+
let decryptedChunkArray = EMPTY_UINT8_ARRAY;
|
|
117
112
|
if (chunkToDecrypt.length) {
|
|
118
113
|
const encryptedChunk = toBits(codecBytes, chunkToDecrypt);
|
|
119
114
|
hmac.update(encryptedChunk);
|
|
@@ -121,8 +116,6 @@ class AESDecryptionStream extends TransformStream {
|
|
|
121
116
|
decryptedChunkArray = fromBits(codecBytes, decryptedChunk);
|
|
122
117
|
}
|
|
123
118
|
const signature = subarray(fromBits(codecBytes, hmac.digest()), 0, SIGNATURE_LENGTH);
|
|
124
|
-
// branch-free comparison: accumulate the differences instead of
|
|
125
|
-
// short-circuiting on the first mismatching byte
|
|
126
119
|
let invalidSignature = pending.length < SIGNATURE_LENGTH ? 1 : 0;
|
|
127
120
|
for (let indexSignature = 0; indexSignature < SIGNATURE_LENGTH; indexSignature++) {
|
|
128
121
|
invalidSignature |= signature[indexSignature] ^ originalSignature[indexSignature];
|
|
@@ -144,12 +137,7 @@ class AESEncryptionStream extends TransformStream {
|
|
|
144
137
|
let stream;
|
|
145
138
|
super({
|
|
146
139
|
start() {
|
|
147
|
-
|
|
148
|
-
ready: new Promise(resolve => this.resolveReady = resolve),
|
|
149
|
-
password: encodePassword(password, rawPassword),
|
|
150
|
-
strength: encryptionStrength - 1,
|
|
151
|
-
pending: new Uint8Array()
|
|
152
|
-
});
|
|
140
|
+
initAesCrypto(this, password, rawPassword, encryptionStrength);
|
|
153
141
|
},
|
|
154
142
|
async transform(chunk, controller) {
|
|
155
143
|
const aesCrypto = this;
|
|
@@ -159,7 +147,7 @@ class AESEncryptionStream extends TransformStream {
|
|
|
159
147
|
resolveReady,
|
|
160
148
|
ready
|
|
161
149
|
} = aesCrypto;
|
|
162
|
-
let preamble =
|
|
150
|
+
let preamble = EMPTY_UINT8_ARRAY;
|
|
163
151
|
if (password) {
|
|
164
152
|
preamble = await createEncryptionKeys(aesCrypto, strength, password);
|
|
165
153
|
resolveReady();
|
|
@@ -179,7 +167,7 @@ class AESEncryptionStream extends TransformStream {
|
|
|
179
167
|
} = this;
|
|
180
168
|
if (hmac && ctr) {
|
|
181
169
|
await ready;
|
|
182
|
-
let encryptedChunkArray =
|
|
170
|
+
let encryptedChunkArray = EMPTY_UINT8_ARRAY;
|
|
183
171
|
if (pending.length) {
|
|
184
172
|
const encryptedChunk = ctr.update(toBits(codecBytes, pending));
|
|
185
173
|
hmac.update(encryptedChunk);
|
|
@@ -196,10 +184,18 @@ class AESEncryptionStream extends TransformStream {
|
|
|
196
184
|
|
|
197
185
|
export {
|
|
198
186
|
AESDecryptionStream,
|
|
199
|
-
AESEncryptionStream
|
|
200
|
-
ERR_INVALID_PASSWORD
|
|
187
|
+
AESEncryptionStream
|
|
201
188
|
};
|
|
202
189
|
|
|
190
|
+
function initAesCrypto(aesCrypto, password, rawPassword, encryptionStrength) {
|
|
191
|
+
Object.assign(aesCrypto, {
|
|
192
|
+
ready: new Promise(resolve => aesCrypto.resolveReady = resolve),
|
|
193
|
+
password: encodePassword(password, rawPassword),
|
|
194
|
+
strength: encryptionStrength - 1,
|
|
195
|
+
pending: EMPTY_UINT8_ARRAY
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
203
199
|
function append(aesCrypto, input, output, paddingStart, paddingEnd, verifySignature) {
|
|
204
200
|
const {
|
|
205
201
|
ctr,
|
|
@@ -48,8 +48,6 @@ function getRandomValues(array) {
|
|
|
48
48
|
if (GET_RANDOM_VALUES_SUPPORTED) {
|
|
49
49
|
return crypto.getRandomValues(array);
|
|
50
50
|
} else {
|
|
51
|
-
// all supported runtimes expose crypto.getRandomValues; failing hard beats
|
|
52
|
-
// silently generating predictable encryption salts
|
|
53
51
|
throw new Error(ERR_UNSUPPORTED_CRYPTO_API);
|
|
54
52
|
}
|
|
55
53
|
}
|
|
@@ -43,20 +43,13 @@ class ZipCryptoDecryptionStream extends TransformStream {
|
|
|
43
43
|
constructor({ password, rawPassword, passwordVerification, checkPasswordOnly }) {
|
|
44
44
|
super({
|
|
45
45
|
start() {
|
|
46
|
-
|
|
47
|
-
password,
|
|
48
|
-
rawPassword,
|
|
49
|
-
passwordVerification
|
|
50
|
-
});
|
|
51
|
-
createKeys(this, password, rawPassword);
|
|
46
|
+
initZipCrypto(this, password, rawPassword, passwordVerification);
|
|
52
47
|
},
|
|
53
48
|
transform(chunk, controller) {
|
|
54
49
|
const zipCrypto = this;
|
|
55
50
|
if (zipCrypto.password || zipCrypto.rawPassword) {
|
|
56
51
|
const decryptedHeader = decrypt(zipCrypto, chunk.subarray(0, HEADER_LENGTH));
|
|
57
52
|
zipCrypto.password = zipCrypto.rawPassword = null;
|
|
58
|
-
// verify the password with a branch-free comparison of the verification byte, mirroring
|
|
59
|
-
// the constant-time AES HMAC signature check
|
|
60
53
|
if ((decryptedHeader.at(-1) ^ zipCrypto.passwordVerification) != 0) {
|
|
61
54
|
throw new Error(ERR_INVALID_PASSWORD);
|
|
62
55
|
}
|
|
@@ -77,12 +70,7 @@ class ZipCryptoEncryptionStream extends TransformStream {
|
|
|
77
70
|
constructor({ password, rawPassword, passwordVerification }) {
|
|
78
71
|
super({
|
|
79
72
|
start() {
|
|
80
|
-
|
|
81
|
-
password,
|
|
82
|
-
rawPassword,
|
|
83
|
-
passwordVerification
|
|
84
|
-
});
|
|
85
|
-
createKeys(this, password, rawPassword);
|
|
73
|
+
initZipCrypto(this, password, rawPassword, passwordVerification);
|
|
86
74
|
},
|
|
87
75
|
transform(chunk, controller) {
|
|
88
76
|
const zipCrypto = this;
|
|
@@ -108,10 +96,18 @@ class ZipCryptoEncryptionStream extends TransformStream {
|
|
|
108
96
|
|
|
109
97
|
export {
|
|
110
98
|
ZipCryptoDecryptionStream,
|
|
111
|
-
ZipCryptoEncryptionStream
|
|
112
|
-
ERR_INVALID_PASSWORD
|
|
99
|
+
ZipCryptoEncryptionStream
|
|
113
100
|
};
|
|
114
101
|
|
|
102
|
+
function initZipCrypto(zipCrypto, password, rawPassword, passwordVerification) {
|
|
103
|
+
Object.assign(zipCrypto, {
|
|
104
|
+
password,
|
|
105
|
+
rawPassword,
|
|
106
|
+
passwordVerification
|
|
107
|
+
});
|
|
108
|
+
createKeys(zipCrypto, password, rawPassword);
|
|
109
|
+
}
|
|
110
|
+
|
|
115
111
|
function decrypt(target, input) {
|
|
116
112
|
const output = new Uint8Array(input.length);
|
|
117
113
|
for (let index = 0; index < input.length; index++) {
|
|
@@ -60,14 +60,6 @@ class DeflateStream extends TransformStream {
|
|
|
60
60
|
const stream = this;
|
|
61
61
|
let crc32Stream, encryptionStream, gzipCrc32Stream;
|
|
62
62
|
let readable = super.readable;
|
|
63
|
-
// The gzip trailer carries a CRC-32 of the uncompressed data (same polynomial as zip),
|
|
64
|
-
// computed in native code for free during compression. On the native CompressionStream,
|
|
65
|
-
// harvest it instead of running a separate CRC pass: compress as gzip, then strip the
|
|
66
|
-
// fixed 10-byte header and 8-byte trailer to recover the exact raw-deflate payload
|
|
67
|
-
// (byte-identical to a deflate-raw compression) while capturing the CRC. The CRC becomes
|
|
68
|
-
// available at end-of-stream, exactly like Crc32Stream, so nothing downstream (data
|
|
69
|
-
// descriptor, central directory, ZipCrypto) needs it any earlier. Not applied for the
|
|
70
|
-
// pure-JS/WASM ports (a separate slice-by-8 CRC pass is as fast or faster there).
|
|
71
63
|
const useGzipCrc32 = signed && compressed && !deflate64 && (!encrypted || zipCrypto) &&
|
|
72
64
|
Boolean(useCompressionStream && CompressionStream);
|
|
73
65
|
if ((!encrypted || zipCrypto) && signed && !useGzipCrc32) {
|
|
@@ -104,12 +96,6 @@ class DeflateStream extends TransformStream {
|
|
|
104
96
|
}
|
|
105
97
|
}
|
|
106
98
|
|
|
107
|
-
// Converts a gzip stream into its raw-deflate payload while capturing the CRC-32 from the gzip
|
|
108
|
-
// trailer. The CompressionStream gzip header is always the fixed 10-byte form (FLG=0, no optional
|
|
109
|
-
// fields) per the Compression Streams spec, so it is stripped by length; the trailer is the last
|
|
110
|
-
// 8 bytes (CRC-32 LE, then ISIZE LE). Bounded memory: at most GZIP_TRAILER_LENGTH bytes are held
|
|
111
|
-
// back across chunks. The signature is read big-endian-agnostically as a number, matching the
|
|
112
|
-
// value Crc32Stream produces, so the writer path is unchanged.
|
|
113
99
|
class GzipToRawDeflateStream extends TransformStream {
|
|
114
100
|
|
|
115
101
|
constructor() {
|
|
@@ -135,10 +121,6 @@ class GzipToRawDeflateStream extends TransformStream {
|
|
|
135
121
|
tail = pending;
|
|
136
122
|
return;
|
|
137
123
|
}
|
|
138
|
-
// Emit everything except the trailing GZIP_TRAILER_LENGTH bytes as a standalone,
|
|
139
|
-
// right-sized Uint8Array. Consumers may read chunk.buffer directly (e.g. custom
|
|
140
|
-
// writers), so an aliased subarray of a larger buffer would leak the held-back
|
|
141
|
-
// trailer bytes. Bytes are copied exactly once, into `output` or `tail`.
|
|
142
124
|
const emitLength = available - GZIP_TRAILER_LENGTH;
|
|
143
125
|
const output = new Uint8Array(emitLength);
|
|
144
126
|
const fromTail = Math.min(emitLength, tail.length);
|
|
@@ -237,11 +219,6 @@ function pipeThroughCommpressionStream(readable, useCompressionStream, options,
|
|
|
237
219
|
throw error;
|
|
238
220
|
}
|
|
239
221
|
}
|
|
240
|
-
// The native CompressionStream/DecompressionStream and the pure-JS zlib port do not signal
|
|
241
|
-
// backpressure on their writable side (their `ready` never pends), so a plain pipeThrough drains
|
|
242
|
-
// the whole source into them and peak memory grows with the entry size. Awaiting each write()
|
|
243
|
-
// paces the source to the codec's consumption rate, keeping streaming memory bounded. The WASM
|
|
244
|
-
// codec already backpressures, so this is a no-op cost for it.
|
|
245
222
|
return pipeThroughBackpressured(readable, codecStream);
|
|
246
223
|
}
|
|
247
224
|
|
|
@@ -249,10 +226,6 @@ function pipeThrough(readable, transformStream) {
|
|
|
249
226
|
return readable.pipeThrough(transformStream);
|
|
250
227
|
}
|
|
251
228
|
|
|
252
|
-
// Like readable.pipeThrough(transformStream), but drives the writable side manually and awaits each
|
|
253
|
-
// write() so that a codec which under-reports backpressure cannot pull the whole source into memory.
|
|
254
|
-
// Errors propagate in both directions: a source error aborts the codec; a downstream cancel/error
|
|
255
|
-
// (surfaced through the codec's writable rejecting) cancels the source.
|
|
256
229
|
function pipeThroughBackpressured(readable, transformStream) {
|
|
257
230
|
const writer = transformStream.writable.getWriter();
|
|
258
231
|
const reader = readable.getReader();
|
|
@@ -281,7 +254,7 @@ async function abort(writer, error) {
|
|
|
281
254
|
try {
|
|
282
255
|
await writer.abort(error);
|
|
283
256
|
} catch {
|
|
284
|
-
// the writable may already be errored/closed
|
|
257
|
+
// ignored: the writable may already be errored/closed
|
|
285
258
|
}
|
|
286
259
|
}
|
|
287
260
|
|
|
@@ -289,13 +262,10 @@ async function cancel(reader, error) {
|
|
|
289
262
|
try {
|
|
290
263
|
await reader.cancel(error);
|
|
291
264
|
} catch {
|
|
292
|
-
// the readable may already be errored/closed
|
|
265
|
+
// ignored: the readable may already be errored/closed
|
|
293
266
|
}
|
|
294
267
|
}
|
|
295
268
|
|
|
296
|
-
// DecompressionStream implementations can fail with a message-less TypeError on malformed
|
|
297
|
-
// input; give these errors an identifiable message (errors raised by the other stages of
|
|
298
|
-
// the pipeline, e.g. ERR_INVALID_PASSWORD, always carry a message and pass through as-is)
|
|
299
269
|
function mapInflateStreamError(readable) {
|
|
300
270
|
const reader = readable.getReader();
|
|
301
271
|
return new ReadableStream({
|