@zip.js/zip.js 2.8.34 → 2.8.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -1
- package/deno.json +5 -2
- package/dist/README.md +3 -1
- package/dist/zip-core.js +472 -220
- package/dist/zip-core.min.js +1 -1
- package/dist/zip-fs-core.js +449 -220
- package/dist/zip-fs-core.min.js +1 -1
- package/dist/zip-fs-native.js +476 -226
- package/dist/zip-fs-native.min.js +1 -1
- package/dist/zip-fs.js +478 -228
- package/dist/zip-fs.min.js +1 -1
- package/dist/zip-legacy.js +474 -222
- package/dist/zip-legacy.min.js +1 -1
- package/dist/zip-native.js +475 -223
- 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 +477 -225
- package/dist/zip.min.js +1 -1
- package/index-native.cjs +476 -226
- package/index-native.min.js +1 -1
- package/index.cjs +478 -228
- package/index.d.cts +53 -0
- package/index.d.ts +53 -0
- package/index.min.js +1 -1
- package/lib/core/codec-pool.js +2 -2
- package/lib/core/codec-worker.js +6 -2
- package/lib/core/configuration.js +22 -1
- package/lib/core/io.js +209 -56
- package/lib/core/streams/aes-crypto-stream.js +1 -10
- package/lib/core/streams/codec-stream.js +36 -15
- package/lib/core/streams/zip-entry-stream.js +19 -28
- package/lib/core/util/array.js +48 -0
- package/lib/core/web-worker-base.js +3 -3
- package/lib/core/web-worker-inline-native.js +1 -1
- package/lib/core/web-worker-inline-wasm.js +1 -1
- package/lib/core/zip-fs.js +2 -3
- package/lib/core/zip-reader.js +37 -40
- package/lib/core/zip-writer.js +58 -51
- package/lib/core/zlib-streams-inline.js +1 -1
- package/lib/zip-core-base.js +5 -3
- package/lib/zip-fs-native.js +2 -2
- package/lib/zip-fs-wasm.js +2 -2
- package/lib/zip-legacy.js +2 -2
- package/lib/zip-module-native.js +2 -2
- package/lib/zip-module-wasm.js +3 -3
- package/lib/zip-native.js +2 -2
- package/lib/zip-wasm.js +2 -2
- package/package.json +3 -3
- package/worker-message-property-names.js +3 -0
- package/.github/workflows/publish.yml +0 -30
- package/reserved-property-names.js +0 -50
package/lib/core/codec-pool.js
CHANGED
|
@@ -63,8 +63,8 @@ async function runWorker(stream, workerOptions) {
|
|
|
63
63
|
const { options, config } = workerOptions;
|
|
64
64
|
const { transferStreams, useWebWorkers, useCompressionStream, compressed, signed, encrypted } = options;
|
|
65
65
|
const { workerURI, maxWorkers } = config;
|
|
66
|
-
workerOptions.transferStreams = transferStreams || transferStreams === UNDEFINED_VALUE;
|
|
67
|
-
const streamCopy = !compressed && !signed && !encrypted
|
|
66
|
+
workerOptions.transferStreams = transferStreams || (transferStreams === UNDEFINED_VALUE && config.transferStreams);
|
|
67
|
+
const streamCopy = !compressed && !signed && !encrypted;
|
|
68
68
|
workerOptions.useWebWorkers = !streamCopy && (useWebWorkers || (useWebWorkers === UNDEFINED_VALUE && config.useWebWorkers));
|
|
69
69
|
workerOptions.workerURI = workerOptions.useWebWorkers && workerURI ? workerURI : UNDEFINED_VALUE;
|
|
70
70
|
options.useCompressionStream = useCompressionStream || (useCompressionStream === UNDEFINED_VALUE && config.useCompressionStream);
|
package/lib/core/codec-worker.js
CHANGED
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
FUNCTION_TYPE
|
|
35
35
|
} from "./constants.js";
|
|
36
36
|
import { getChunkSize } from "./configuration.js";
|
|
37
|
+
import { toExactUint8Array } from "./util/array.js";
|
|
37
38
|
import {
|
|
38
39
|
CODEC_DEFLATE,
|
|
39
40
|
CodecStream,
|
|
@@ -203,7 +204,10 @@ async function runWorker({ options, readable, writable, onTaskFinished }, config
|
|
|
203
204
|
}
|
|
204
205
|
}
|
|
205
206
|
codecStream = new CodecStream(options, config);
|
|
206
|
-
await readable
|
|
207
|
+
await readable
|
|
208
|
+
.pipeThrough(codecStream)
|
|
209
|
+
.pipeThrough(new ChunkStream(getChunkSize(config)))
|
|
210
|
+
.pipeTo(writable, { preventClose: true, preventAbort: true });
|
|
207
211
|
const {
|
|
208
212
|
signature,
|
|
209
213
|
inputSize,
|
|
@@ -402,7 +406,7 @@ function sendMessage(message, { worker, writer, transferStreams }) {
|
|
|
402
406
|
const { value, readable, writable } = message;
|
|
403
407
|
const transferables = [];
|
|
404
408
|
if (value) {
|
|
405
|
-
message.value =
|
|
409
|
+
message.value = toExactUint8Array(value);
|
|
406
410
|
transferables.push(message.value.buffer);
|
|
407
411
|
}
|
|
408
412
|
if (transferStreams && transferStreamsSupported) {
|
|
@@ -51,6 +51,7 @@ const DEFAULT_CONFIGURATION = {
|
|
|
51
51
|
workerStarvationTimeout: 5000,
|
|
52
52
|
useWebWorkers: true,
|
|
53
53
|
useCompressionStream: true,
|
|
54
|
+
transferStreams: true,
|
|
54
55
|
CompressionStream: typeof CompressionStream != UNDEFINED_TYPE && CompressionStream,
|
|
55
56
|
DecompressionStream: typeof DecompressionStream != UNDEFINED_TYPE && DecompressionStream
|
|
56
57
|
};
|
|
@@ -65,16 +66,19 @@ const CONFIGURABLE_PROPERTY_NAMES = [
|
|
|
65
66
|
"workerStarvationTimeout",
|
|
66
67
|
"useCompressionStream",
|
|
67
68
|
"useWebWorkers",
|
|
69
|
+
"transferStreams",
|
|
68
70
|
"CompressionStream",
|
|
69
71
|
"DecompressionStream",
|
|
70
72
|
"CompressionStreamZlib",
|
|
71
73
|
"DecompressionStreamZlib"
|
|
72
74
|
];
|
|
73
75
|
|
|
74
|
-
const config =
|
|
76
|
+
const config = { ...DEFAULT_CONFIGURATION };
|
|
75
77
|
|
|
76
78
|
export {
|
|
77
79
|
configure,
|
|
80
|
+
setDefaultConfiguration,
|
|
81
|
+
resetConfiguration,
|
|
78
82
|
getConfiguration,
|
|
79
83
|
getChunkSize
|
|
80
84
|
};
|
|
@@ -95,3 +99,20 @@ function configure(configuration) {
|
|
|
95
99
|
}
|
|
96
100
|
}
|
|
97
101
|
}
|
|
102
|
+
|
|
103
|
+
function setDefaultConfiguration(configuration) {
|
|
104
|
+
for (const propertyName of CONFIGURABLE_PROPERTY_NAMES) {
|
|
105
|
+
const propertyValue = configuration[propertyName];
|
|
106
|
+
if (propertyValue !== UNDEFINED_VALUE) {
|
|
107
|
+
DEFAULT_CONFIGURATION[propertyName] = propertyValue;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
configure(configuration);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function resetConfiguration() {
|
|
114
|
+
for (const propertyName of CONFIGURABLE_PROPERTY_NAMES) {
|
|
115
|
+
delete config[propertyName];
|
|
116
|
+
}
|
|
117
|
+
Object.assign(config, DEFAULT_CONFIGURATION);
|
|
118
|
+
}
|
package/lib/core/io.js
CHANGED
|
@@ -31,15 +31,16 @@
|
|
|
31
31
|
|
|
32
32
|
import {
|
|
33
33
|
UNDEFINED_VALUE,
|
|
34
|
-
INFINITY_VALUE,
|
|
35
34
|
FUNCTION_TYPE,
|
|
36
35
|
END_OF_CENTRAL_DIR_LENGTH,
|
|
37
36
|
EMPTY_UINT8_ARRAY
|
|
38
37
|
} from "./constants.js";
|
|
39
|
-
import { getConfiguration } from "./configuration.js";
|
|
38
|
+
import { getConfiguration, getChunkSize } from "./configuration.js";
|
|
39
|
+
import { concat, toExactUint8Array } from "./util/array.js";
|
|
40
40
|
|
|
41
41
|
const ERR_HTTP_STATUS = "HTTP error ";
|
|
42
42
|
const ERR_HTTP_RANGE = "HTTP Range not supported";
|
|
43
|
+
const ERR_HTTP_RESOURCE_CHANGED = "HTTP resource changed";
|
|
43
44
|
const ERR_ITERATOR_COMPLETED_TOO_SOON = "Writer iterator completed too soon";
|
|
44
45
|
const ERR_WRITER_NOT_INITIALIZED = "Writer not initialized";
|
|
45
46
|
|
|
@@ -50,13 +51,15 @@ const HTTP_HEADER_CONTENT_RANGE = "Content-Range";
|
|
|
50
51
|
const HTTP_HEADER_ACCEPT_RANGES = "Accept-Ranges";
|
|
51
52
|
const HTTP_HEADER_RANGE = "Range";
|
|
52
53
|
const HTTP_HEADER_CONTENT_TYPE = "Content-Type";
|
|
54
|
+
const HTTP_HEADER_ETAG = "Etag";
|
|
55
|
+
const HTTP_HEADER_LAST_MODIFIED = "Last-Modified";
|
|
53
56
|
const HTTP_METHOD_HEAD = "HEAD";
|
|
54
57
|
const HTTP_METHOD_GET = "GET";
|
|
55
58
|
const HTTP_RANGE_UNIT = "bytes";
|
|
56
|
-
const DEFAULT_CHUNK_SIZE = 64 * 1024;
|
|
57
59
|
const DEFAULT_BUFFER_SIZE = 256 * 1024;
|
|
58
60
|
|
|
59
61
|
const PROPERTY_NAME_WRITABLE = "writable";
|
|
62
|
+
const DISK_BOUNDARY = Symbol();
|
|
60
63
|
|
|
61
64
|
class Stream {
|
|
62
65
|
|
|
@@ -75,13 +78,13 @@ class Reader extends Stream {
|
|
|
75
78
|
return this.createReadable();
|
|
76
79
|
}
|
|
77
80
|
|
|
78
|
-
createReadable({ offset = 0, size,
|
|
81
|
+
createReadable({ offset = 0, size, chunkSize = getChunkSize(getConfiguration()) } = {}) {
|
|
79
82
|
const reader = this;
|
|
80
83
|
let chunkOffset = 0;
|
|
81
84
|
return new ReadableStream({
|
|
82
85
|
async pull(controller) {
|
|
83
86
|
const dataSize = size === UNDEFINED_VALUE ? chunkSize : Math.min(chunkSize, size - chunkOffset);
|
|
84
|
-
const data = await readUint8Array(reader, offset + chunkOffset, dataSize
|
|
87
|
+
const data = await readUint8Array(reader, offset + chunkOffset, dataSize);
|
|
85
88
|
controller.enqueue(data);
|
|
86
89
|
if ((chunkOffset + chunkSize > size) || (size === UNDEFINED_VALUE && !data.length && dataSize)) {
|
|
87
90
|
controller.close();
|
|
@@ -103,7 +106,7 @@ class Writer extends Stream {
|
|
|
103
106
|
if (!writer.initialized) {
|
|
104
107
|
throw new Error(ERR_WRITER_NOT_INITIALIZED);
|
|
105
108
|
}
|
|
106
|
-
return writer.writeUint8Array(chunk);
|
|
109
|
+
return writer.writeUint8Array(toExactUint8Array(chunk));
|
|
107
110
|
}
|
|
108
111
|
});
|
|
109
112
|
Object.defineProperty(writer, PROPERTY_NAME_WRITABLE, {
|
|
@@ -190,6 +193,27 @@ class Data64URIWriter extends Writer {
|
|
|
190
193
|
}
|
|
191
194
|
}
|
|
192
195
|
|
|
196
|
+
let blobSliceReliable;
|
|
197
|
+
let blobSliceProbe;
|
|
198
|
+
|
|
199
|
+
function probeBlobSliceReliability() {
|
|
200
|
+
blobSliceProbe = (async () => {
|
|
201
|
+
try {
|
|
202
|
+
const slicedBlob = new Blob([new Uint8Array(3)]).slice(1, 2);
|
|
203
|
+
const streamReader = slicedBlob.stream().getReader();
|
|
204
|
+
let streamedLength = 0;
|
|
205
|
+
let result = await streamReader.read();
|
|
206
|
+
while (!result.done) {
|
|
207
|
+
streamedLength += result.value.length;
|
|
208
|
+
result = await streamReader.read();
|
|
209
|
+
}
|
|
210
|
+
blobSliceReliable = streamedLength == 1;
|
|
211
|
+
} catch {
|
|
212
|
+
blobSliceReliable = false;
|
|
213
|
+
}
|
|
214
|
+
})();
|
|
215
|
+
}
|
|
216
|
+
|
|
193
217
|
class BlobReader extends Reader {
|
|
194
218
|
|
|
195
219
|
constructor(blob) {
|
|
@@ -198,14 +222,32 @@ class BlobReader extends Reader {
|
|
|
198
222
|
blob,
|
|
199
223
|
size: blob.size
|
|
200
224
|
});
|
|
225
|
+
if (!blobSliceProbe) {
|
|
226
|
+
probeBlobSliceReliability();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
createReadable(options) {
|
|
231
|
+
const reader = this;
|
|
232
|
+
const { blob, size } = reader;
|
|
233
|
+
const { offset = 0, size: readSize = size - offset } = options || {};
|
|
234
|
+
if (!offset && readSize >= size) {
|
|
235
|
+
return blob.stream();
|
|
236
|
+
}
|
|
237
|
+
if (blobSliceReliable) {
|
|
238
|
+
return blob.slice(offset, offset + readSize).stream();
|
|
239
|
+
}
|
|
240
|
+
return super.createReadable(options);
|
|
201
241
|
}
|
|
202
242
|
|
|
203
243
|
async readUint8Array(offset, length) {
|
|
204
244
|
const reader = this;
|
|
205
245
|
const offsetEnd = offset + length;
|
|
206
|
-
const
|
|
246
|
+
const readsWholeBlob = !offset && offsetEnd >= reader.size;
|
|
247
|
+
const blob = readsWholeBlob ? reader.blob : reader.blob.slice(offset, offsetEnd);
|
|
207
248
|
let arrayBuffer = await blob.arrayBuffer();
|
|
208
|
-
|
|
249
|
+
const sliceIgnoredByBuggyImplementation = arrayBuffer.byteLength > length;
|
|
250
|
+
if (sliceIgnoredByBuggyImplementation) {
|
|
209
251
|
arrayBuffer = arrayBuffer.slice(offset, offsetEnd);
|
|
210
252
|
}
|
|
211
253
|
return new Uint8Array(arrayBuffer);
|
|
@@ -286,6 +328,18 @@ class FetchReader extends Reader {
|
|
|
286
328
|
super.init();
|
|
287
329
|
}
|
|
288
330
|
|
|
331
|
+
createReadable(options) {
|
|
332
|
+
const reader = this;
|
|
333
|
+
const { useRangeHeader, forceRangeRequests, size } = reader;
|
|
334
|
+
if ((useRangeHeader || forceRangeRequests) && size !== UNDEFINED_VALUE) {
|
|
335
|
+
const { offset = 0, size: readSize = size - offset } = options || {};
|
|
336
|
+
if (readSize > 0 && offset < size) {
|
|
337
|
+
return createRangeReadable(reader, offset, Math.min(readSize, size - offset));
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return super.createReadable(options);
|
|
341
|
+
}
|
|
342
|
+
|
|
289
343
|
readUint8Array(index, length) {
|
|
290
344
|
return readUint8ArrayHttpReader(this, index, length, sendFetchRequest, getFetchRequestData);
|
|
291
345
|
}
|
|
@@ -314,6 +368,7 @@ function createHttpReader(httpReader, url, options) {
|
|
|
314
368
|
useRangeHeader,
|
|
315
369
|
forceRangeRequests,
|
|
316
370
|
combineSizeEocd,
|
|
371
|
+
checkResourceChanges = true,
|
|
317
372
|
fetch
|
|
318
373
|
} = options;
|
|
319
374
|
options = Object.assign({}, options);
|
|
@@ -321,6 +376,7 @@ function createHttpReader(httpReader, url, options) {
|
|
|
321
376
|
delete options.useRangeHeader;
|
|
322
377
|
delete options.forceRangeRequests;
|
|
323
378
|
delete options.combineSizeEocd;
|
|
379
|
+
delete options.checkResourceChanges;
|
|
324
380
|
delete options.useXHR;
|
|
325
381
|
delete options.fetch;
|
|
326
382
|
Object.assign(httpReader, {
|
|
@@ -330,6 +386,7 @@ function createHttpReader(httpReader, url, options) {
|
|
|
330
386
|
useRangeHeader,
|
|
331
387
|
forceRangeRequests,
|
|
332
388
|
combineSizeEocd,
|
|
389
|
+
checkResourceChanges,
|
|
333
390
|
fetch
|
|
334
391
|
});
|
|
335
392
|
}
|
|
@@ -354,17 +411,8 @@ async function initHttpReader(httpReader, sendRequest, getRequestData) {
|
|
|
354
411
|
httpReader.eocdCache = eocdCache;
|
|
355
412
|
}
|
|
356
413
|
}
|
|
357
|
-
|
|
358
|
-
const
|
|
359
|
-
if (contentRangeHeader) {
|
|
360
|
-
const splitHeader = contentRangeHeader.trim().split(/\s*\/\s*/);
|
|
361
|
-
if (splitHeader.length) {
|
|
362
|
-
const headerValue = splitHeader[1];
|
|
363
|
-
if (headerValue && headerValue != "*") {
|
|
364
|
-
contentSize = Number(headerValue);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
}
|
|
414
|
+
setResourceValidators(httpReader, response);
|
|
415
|
+
const contentSize = getContentRangeSize(response);
|
|
368
416
|
if (contentSize === UNDEFINED_VALUE) {
|
|
369
417
|
await getContentLength(httpReader, sendRequest, getRequestData);
|
|
370
418
|
} else {
|
|
@@ -405,6 +453,8 @@ async function readUint8ArrayHttpReader(httpReader, index, length, sendRequest,
|
|
|
405
453
|
throw new Error(ERR_HTTP_RANGE);
|
|
406
454
|
}
|
|
407
455
|
}
|
|
456
|
+
checkResourceValidators(httpReader, response);
|
|
457
|
+
setResourceValidators(httpReader, response);
|
|
408
458
|
const data = new Uint8Array(await response.arrayBuffer());
|
|
409
459
|
if (data.length != length) {
|
|
410
460
|
throw new Error(ERR_HTTP_RANGE);
|
|
@@ -416,7 +466,97 @@ async function readUint8ArrayHttpReader(httpReader, index, length, sendRequest,
|
|
|
416
466
|
if (!data) {
|
|
417
467
|
await getRequestData(httpReader, options);
|
|
418
468
|
}
|
|
419
|
-
return
|
|
469
|
+
return httpReader.data.subarray(index, index + length);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function createRangeReadable(httpReader, offset, size) {
|
|
474
|
+
let bodyReader;
|
|
475
|
+
let remainingLength = size;
|
|
476
|
+
return new ReadableStream({
|
|
477
|
+
async start() {
|
|
478
|
+
const response = await sendFetchRequest(HTTP_METHOD_GET, httpReader, getRangeHeaders(httpReader, offset, size));
|
|
479
|
+
if (response.status != 206) {
|
|
480
|
+
throw new Error(ERR_HTTP_RANGE);
|
|
481
|
+
}
|
|
482
|
+
const contentRangeHeader = response.headers.get(HTTP_HEADER_CONTENT_RANGE);
|
|
483
|
+
if (contentRangeHeader) {
|
|
484
|
+
const rangeStart = Number(contentRangeHeader.trim().split(/[\s-]+/)[1]);
|
|
485
|
+
if (!Number.isNaN(rangeStart) && rangeStart != offset) {
|
|
486
|
+
throw new Error(ERR_HTTP_RANGE);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
checkResourceValidators(httpReader, response);
|
|
490
|
+
setResourceValidators(httpReader, response);
|
|
491
|
+
bodyReader = response.body.getReader();
|
|
492
|
+
},
|
|
493
|
+
async pull(controller) {
|
|
494
|
+
const { value, done } = await bodyReader.read();
|
|
495
|
+
if (done) {
|
|
496
|
+
if (remainingLength) {
|
|
497
|
+
throw new Error(ERR_HTTP_RANGE);
|
|
498
|
+
}
|
|
499
|
+
controller.close();
|
|
500
|
+
} else {
|
|
501
|
+
const chunk = value.length > remainingLength ? value.subarray(0, remainingLength) : value;
|
|
502
|
+
remainingLength -= chunk.length;
|
|
503
|
+
if (chunk.length) {
|
|
504
|
+
controller.enqueue(chunk);
|
|
505
|
+
}
|
|
506
|
+
if (!remainingLength) {
|
|
507
|
+
controller.close();
|
|
508
|
+
await bodyReader.cancel();
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
},
|
|
512
|
+
cancel(reason) {
|
|
513
|
+
return bodyReader && bodyReader.cancel(reason);
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
function getContentRangeSize(response) {
|
|
519
|
+
const contentRangeHeader = response.headers.get(HTTP_HEADER_CONTENT_RANGE);
|
|
520
|
+
if (contentRangeHeader) {
|
|
521
|
+
const headerValue = contentRangeHeader.trim().split(/\s*\/\s*/)[1];
|
|
522
|
+
if (headerValue && headerValue != "*") {
|
|
523
|
+
const contentSize = Number(headerValue);
|
|
524
|
+
if (!Number.isNaN(contentSize)) {
|
|
525
|
+
return contentSize;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
function getResourceValidators({ headers }) {
|
|
532
|
+
return {
|
|
533
|
+
etag: headers.get(HTTP_HEADER_ETAG) || UNDEFINED_VALUE,
|
|
534
|
+
lastModified: headers.get(HTTP_HEADER_LAST_MODIFIED) || UNDEFINED_VALUE
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
function setResourceValidators(httpReader, response) {
|
|
539
|
+
const { checkResourceChanges, resourceValidators } = httpReader;
|
|
540
|
+
if (checkResourceChanges && !resourceValidators && response.status == 206) {
|
|
541
|
+
httpReader.resourceValidators = getResourceValidators(response);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
function checkResourceValidators(httpReader, response) {
|
|
546
|
+
const { checkResourceChanges, resourceValidators, size } = httpReader;
|
|
547
|
+
if (checkResourceChanges) {
|
|
548
|
+
const contentRangeSize = getContentRangeSize(response);
|
|
549
|
+
if (contentRangeSize !== UNDEFINED_VALUE && size !== UNDEFINED_VALUE && contentRangeSize != size) {
|
|
550
|
+
throw new Error(ERR_HTTP_RESOURCE_CHANGED);
|
|
551
|
+
}
|
|
552
|
+
if (resourceValidators) {
|
|
553
|
+
const validators = getResourceValidators(response);
|
|
554
|
+
const changed = Object.entries(resourceValidators).some(([name, value]) =>
|
|
555
|
+
value !== UNDEFINED_VALUE && validators[name] !== UNDEFINED_VALUE && value != validators[name]);
|
|
556
|
+
if (changed) {
|
|
557
|
+
throw new Error(ERR_HTTP_RESOURCE_CHANGED);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
420
560
|
}
|
|
421
561
|
}
|
|
422
562
|
|
|
@@ -527,6 +667,10 @@ class HttpReader extends Reader {
|
|
|
527
667
|
super.init();
|
|
528
668
|
}
|
|
529
669
|
|
|
670
|
+
createReadable(options) {
|
|
671
|
+
return this.reader.createReadable(options);
|
|
672
|
+
}
|
|
673
|
+
|
|
530
674
|
readUint8Array(index, length) {
|
|
531
675
|
return this.reader.readUint8Array(index, length);
|
|
532
676
|
}
|
|
@@ -607,25 +751,26 @@ class SplitDataReader extends Reader {
|
|
|
607
751
|
const reader = this;
|
|
608
752
|
const { readers } = reader;
|
|
609
753
|
reader.lastDiskNumber = 0;
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
if (indexDiskReader != readers.length - 1) {
|
|
614
|
-
reader.lastDiskOffset += diskReader.size;
|
|
615
|
-
}
|
|
754
|
+
await Promise.all(readers.map(diskReader => initStream(diskReader)));
|
|
755
|
+
reader.diskOffsets = readers.map(diskReader => {
|
|
756
|
+
const diskOffset = reader.size;
|
|
616
757
|
reader.size += diskReader.size;
|
|
617
|
-
|
|
758
|
+
return diskOffset;
|
|
759
|
+
});
|
|
618
760
|
super.init();
|
|
619
761
|
}
|
|
620
762
|
|
|
621
|
-
|
|
763
|
+
getDiskOffset(diskNumber) {
|
|
764
|
+
const { diskOffsets, size } = this;
|
|
765
|
+
const diskOffset = diskOffsets.at(diskNumber);
|
|
766
|
+
return diskOffset === UNDEFINED_VALUE ? size : diskOffset;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
async readUint8Array(offset, length) {
|
|
622
770
|
const reader = this;
|
|
623
771
|
const { readers } = this;
|
|
624
772
|
let result;
|
|
625
|
-
let currentDiskNumber =
|
|
626
|
-
if (currentDiskNumber == -1) {
|
|
627
|
-
currentDiskNumber = readers.length - 1;
|
|
628
|
-
}
|
|
773
|
+
let currentDiskNumber = 0;
|
|
629
774
|
let currentReaderOffset = offset;
|
|
630
775
|
while (readers[currentDiskNumber] && currentReaderOffset >= readers[currentDiskNumber].size) {
|
|
631
776
|
currentReaderOffset -= readers[currentDiskNumber].size;
|
|
@@ -638,14 +783,9 @@ class SplitDataReader extends Reader {
|
|
|
638
783
|
result = await readUint8Array(currentReader, currentReaderOffset, length);
|
|
639
784
|
} else {
|
|
640
785
|
const chunkLength = currentReaderSize - currentReaderOffset;
|
|
641
|
-
result = new Uint8Array(length);
|
|
642
786
|
const firstPart = await readUint8Array(currentReader, currentReaderOffset, chunkLength);
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
result.set(secondPart, chunkLength);
|
|
646
|
-
if (firstPart.length + secondPart.length < length) {
|
|
647
|
-
result = result.subarray(0, firstPart.length + secondPart.length);
|
|
648
|
-
}
|
|
787
|
+
const secondPart = await reader.readUint8Array(offset + chunkLength, length - chunkLength);
|
|
788
|
+
result = concat(firstPart, secondPart);
|
|
649
789
|
}
|
|
650
790
|
} else {
|
|
651
791
|
result = EMPTY_UINT8_ARRAY;
|
|
@@ -670,6 +810,12 @@ class SplitDataWriter extends Stream {
|
|
|
670
810
|
let diskSourceWriter, diskWritable, diskWriter;
|
|
671
811
|
const writable = new WritableStream({
|
|
672
812
|
async write(chunk) {
|
|
813
|
+
if (chunk === DISK_BOUNDARY) {
|
|
814
|
+
if (diskWriter) {
|
|
815
|
+
await endDisk();
|
|
816
|
+
}
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
673
819
|
const { availableSize } = writer;
|
|
674
820
|
if (!diskWriter) {
|
|
675
821
|
const { value, done } = await writerGenerator.next();
|
|
@@ -689,11 +835,7 @@ class SplitDataWriter extends Stream {
|
|
|
689
835
|
await this.write(chunk);
|
|
690
836
|
} else if (chunk.length >= availableSize) {
|
|
691
837
|
await writeChunk(chunk.subarray(0, availableSize));
|
|
692
|
-
await
|
|
693
|
-
writer.diskOffset += diskSourceWriter.size;
|
|
694
|
-
writer.diskNumber++;
|
|
695
|
-
diskWriter = null;
|
|
696
|
-
writer.availableSize = writer.maxSize;
|
|
838
|
+
await endDisk();
|
|
697
839
|
if (chunk.length > availableSize) {
|
|
698
840
|
await this.write(chunk.subarray(availableSize));
|
|
699
841
|
}
|
|
@@ -704,7 +846,7 @@ class SplitDataWriter extends Stream {
|
|
|
704
846
|
async close() {
|
|
705
847
|
if (diskWriter) {
|
|
706
848
|
await diskWriter.ready;
|
|
707
|
-
await
|
|
849
|
+
await closeDiskWriter();
|
|
708
850
|
}
|
|
709
851
|
},
|
|
710
852
|
async abort(reason) {
|
|
@@ -729,10 +871,28 @@ class SplitDataWriter extends Stream {
|
|
|
729
871
|
}
|
|
730
872
|
}
|
|
731
873
|
|
|
732
|
-
async function
|
|
874
|
+
async function endDisk() {
|
|
875
|
+
await closeDiskWriter();
|
|
876
|
+
writer.diskOffset += diskSourceWriter.size;
|
|
877
|
+
writer.diskNumber++;
|
|
878
|
+
diskWriter = null;
|
|
879
|
+
writer.availableSize = writer.maxSize;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
async function closeDiskWriter() {
|
|
733
883
|
await diskWriter.close();
|
|
734
884
|
}
|
|
735
885
|
}
|
|
886
|
+
|
|
887
|
+
async closeDisk() {
|
|
888
|
+
const streamWriter = this.writable.getWriter();
|
|
889
|
+
try {
|
|
890
|
+
await streamWriter.ready;
|
|
891
|
+
await streamWriter.write(DISK_BOUNDARY);
|
|
892
|
+
} finally {
|
|
893
|
+
streamWriter.releaseLock();
|
|
894
|
+
}
|
|
895
|
+
}
|
|
736
896
|
}
|
|
737
897
|
|
|
738
898
|
class GenericReader {
|
|
@@ -764,14 +924,6 @@ class GenericWriter {
|
|
|
764
924
|
if (writer.size === UNDEFINED_VALUE) {
|
|
765
925
|
writer.size = 0;
|
|
766
926
|
}
|
|
767
|
-
if (!(writer instanceof SplitDataWriter)) {
|
|
768
|
-
Object.assign(writer, {
|
|
769
|
-
diskNumber: 0,
|
|
770
|
-
diskOffset: 0,
|
|
771
|
-
availableSize: INFINITY_VALUE,
|
|
772
|
-
maxSize: INFINITY_VALUE
|
|
773
|
-
});
|
|
774
|
-
}
|
|
775
927
|
return writer;
|
|
776
928
|
}
|
|
777
929
|
}
|
|
@@ -790,8 +942,8 @@ async function initStream(stream, initSize) {
|
|
|
790
942
|
}
|
|
791
943
|
}
|
|
792
944
|
|
|
793
|
-
function readUint8Array(reader, offset, size
|
|
794
|
-
return reader.readUint8Array(offset, size
|
|
945
|
+
function readUint8Array(reader, offset, size) {
|
|
946
|
+
return reader.readUint8Array(offset, size);
|
|
795
947
|
}
|
|
796
948
|
|
|
797
949
|
export {
|
|
@@ -814,6 +966,7 @@ export {
|
|
|
814
966
|
SplitDataReader,
|
|
815
967
|
SplitDataWriter,
|
|
816
968
|
ERR_HTTP_RANGE,
|
|
969
|
+
ERR_HTTP_RESOURCE_CHANGED,
|
|
817
970
|
ERR_ITERATOR_COMPLETED_TOO_SOON,
|
|
818
971
|
ERR_WRITER_NOT_INITIALIZED
|
|
819
972
|
};
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
|
|
32
32
|
import { UNDEFINED_VALUE, UNDEFINED_TYPE, FUNCTION_TYPE, EMPTY_UINT8_ARRAY } from "../constants.js";
|
|
33
33
|
import { encodeText } from "./../util/encode-text.js";
|
|
34
|
+
import { concat } from "./../util/array.js";
|
|
34
35
|
import {
|
|
35
36
|
cipher,
|
|
36
37
|
codec,
|
|
@@ -291,16 +292,6 @@ function encodePassword(password, rawPassword) {
|
|
|
291
292
|
}
|
|
292
293
|
}
|
|
293
294
|
|
|
294
|
-
function concat(leftArray, rightArray) {
|
|
295
|
-
let array = leftArray;
|
|
296
|
-
if (leftArray.length + rightArray.length) {
|
|
297
|
-
array = new Uint8Array(leftArray.length + rightArray.length);
|
|
298
|
-
array.set(leftArray, 0);
|
|
299
|
-
array.set(rightArray, leftArray.length);
|
|
300
|
-
}
|
|
301
|
-
return array;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
295
|
function expand(inputArray, length) {
|
|
305
296
|
if (length && length > inputArray.length) {
|
|
306
297
|
const array = inputArray;
|
|
@@ -133,33 +133,54 @@ class CodecStream extends TransformStream {
|
|
|
133
133
|
class ChunkStream extends TransformStream {
|
|
134
134
|
|
|
135
135
|
constructor(chunkSize) {
|
|
136
|
-
|
|
136
|
+
const pendingChunks = [];
|
|
137
|
+
let pendingLength = 0;
|
|
137
138
|
if (!(chunkSize >= 1)) {
|
|
138
139
|
chunkSize = DEFAULT_CHUNK_SIZE;
|
|
139
140
|
}
|
|
140
141
|
super({
|
|
141
|
-
transform,
|
|
142
|
+
transform(chunk, controller) {
|
|
143
|
+
pendingChunks.push(chunk);
|
|
144
|
+
pendingLength += chunk.length;
|
|
145
|
+
while (pendingLength > chunkSize) {
|
|
146
|
+
controller.enqueue(shiftChunk());
|
|
147
|
+
}
|
|
148
|
+
},
|
|
142
149
|
flush(controller) {
|
|
143
|
-
if (
|
|
144
|
-
controller.enqueue(
|
|
150
|
+
if (pendingLength) {
|
|
151
|
+
controller.enqueue(concatChunks(pendingChunks, pendingLength));
|
|
145
152
|
}
|
|
146
153
|
}
|
|
147
154
|
});
|
|
148
155
|
|
|
149
|
-
function
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
+
function shiftChunk() {
|
|
157
|
+
const result = new Uint8Array(chunkSize);
|
|
158
|
+
let resultOffset = 0;
|
|
159
|
+
while (resultOffset < chunkSize) {
|
|
160
|
+
const firstChunk = pendingChunks[0];
|
|
161
|
+
const remainingLength = chunkSize - resultOffset;
|
|
162
|
+
if (firstChunk.length <= remainingLength) {
|
|
163
|
+
result.set(firstChunk, resultOffset);
|
|
164
|
+
resultOffset += firstChunk.length;
|
|
165
|
+
pendingChunks.shift();
|
|
166
|
+
} else {
|
|
167
|
+
result.set(firstChunk.subarray(0, remainingLength), resultOffset);
|
|
168
|
+
pendingChunks[0] = firstChunk.subarray(remainingLength);
|
|
169
|
+
resultOffset += remainingLength;
|
|
170
|
+
}
|
|
156
171
|
}
|
|
172
|
+
pendingLength -= chunkSize;
|
|
173
|
+
return result;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function concatChunks(chunks, length) {
|
|
177
|
+
const result = new Uint8Array(length);
|
|
157
178
|
let offset = 0;
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
offset +=
|
|
179
|
+
for (const chunk of chunks) {
|
|
180
|
+
result.set(chunk, offset);
|
|
181
|
+
offset += chunk.length;
|
|
161
182
|
}
|
|
162
|
-
|
|
183
|
+
return result;
|
|
163
184
|
}
|
|
164
185
|
}
|
|
165
186
|
}
|