@zip.js/zip.js 2.8.34 → 2.8.36
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 +494 -221
- package/dist/zip-core.min.js +1 -1
- package/dist/zip-fs-core.js +471 -221
- package/dist/zip-fs-core.min.js +1 -1
- package/dist/zip-fs-native.js +498 -227
- package/dist/zip-fs-native.min.js +1 -1
- package/dist/zip-fs.js +500 -229
- package/dist/zip-fs.min.js +1 -1
- package/dist/zip-legacy.js +496 -223
- package/dist/zip-legacy.min.js +1 -1
- package/dist/zip-native.js +497 -224
- 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 +499 -226
- package/dist/zip.min.js +1 -1
- package/index-native.cjs +498 -227
- package/index-native.min.js +1 -1
- package/index.cjs +500 -229
- package/index.d.cts +61 -0
- package/index.d.ts +61 -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 +230 -56
- package/lib/core/streams/aes-crypto-stream.js +1 -10
- package/lib/core/streams/codec-stream.js +37 -16
- 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,16 @@ 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;
|
|
60
|
+
const DEFAULT_MAXIMUM_RANGE_SIZE = 16 * 1024 * 1024;
|
|
58
61
|
|
|
59
62
|
const PROPERTY_NAME_WRITABLE = "writable";
|
|
63
|
+
const DISK_BOUNDARY = Symbol();
|
|
60
64
|
|
|
61
65
|
class Stream {
|
|
62
66
|
|
|
@@ -75,13 +79,13 @@ class Reader extends Stream {
|
|
|
75
79
|
return this.createReadable();
|
|
76
80
|
}
|
|
77
81
|
|
|
78
|
-
createReadable({ offset = 0, size,
|
|
82
|
+
createReadable({ offset = 0, size, chunkSize = getChunkSize(getConfiguration()) } = {}) {
|
|
79
83
|
const reader = this;
|
|
80
84
|
let chunkOffset = 0;
|
|
81
85
|
return new ReadableStream({
|
|
82
86
|
async pull(controller) {
|
|
83
87
|
const dataSize = size === UNDEFINED_VALUE ? chunkSize : Math.min(chunkSize, size - chunkOffset);
|
|
84
|
-
const data = await readUint8Array(reader, offset + chunkOffset, dataSize
|
|
88
|
+
const data = await readUint8Array(reader, offset + chunkOffset, dataSize);
|
|
85
89
|
controller.enqueue(data);
|
|
86
90
|
if ((chunkOffset + chunkSize > size) || (size === UNDEFINED_VALUE && !data.length && dataSize)) {
|
|
87
91
|
controller.close();
|
|
@@ -103,7 +107,7 @@ class Writer extends Stream {
|
|
|
103
107
|
if (!writer.initialized) {
|
|
104
108
|
throw new Error(ERR_WRITER_NOT_INITIALIZED);
|
|
105
109
|
}
|
|
106
|
-
return writer.writeUint8Array(chunk);
|
|
110
|
+
return writer.writeUint8Array(toExactUint8Array(chunk));
|
|
107
111
|
}
|
|
108
112
|
});
|
|
109
113
|
Object.defineProperty(writer, PROPERTY_NAME_WRITABLE, {
|
|
@@ -190,6 +194,27 @@ class Data64URIWriter extends Writer {
|
|
|
190
194
|
}
|
|
191
195
|
}
|
|
192
196
|
|
|
197
|
+
let blobSliceReliable;
|
|
198
|
+
let blobSliceProbe;
|
|
199
|
+
|
|
200
|
+
function probeBlobSliceReliability() {
|
|
201
|
+
blobSliceProbe = (async () => {
|
|
202
|
+
try {
|
|
203
|
+
const slicedBlob = new Blob([new Uint8Array(3)]).slice(1, 2);
|
|
204
|
+
const streamReader = slicedBlob.stream().getReader();
|
|
205
|
+
let streamedLength = 0;
|
|
206
|
+
let result = await streamReader.read();
|
|
207
|
+
while (!result.done) {
|
|
208
|
+
streamedLength += result.value.length;
|
|
209
|
+
result = await streamReader.read();
|
|
210
|
+
}
|
|
211
|
+
blobSliceReliable = streamedLength == 1;
|
|
212
|
+
} catch {
|
|
213
|
+
blobSliceReliable = false;
|
|
214
|
+
}
|
|
215
|
+
})();
|
|
216
|
+
}
|
|
217
|
+
|
|
193
218
|
class BlobReader extends Reader {
|
|
194
219
|
|
|
195
220
|
constructor(blob) {
|
|
@@ -198,14 +223,32 @@ class BlobReader extends Reader {
|
|
|
198
223
|
blob,
|
|
199
224
|
size: blob.size
|
|
200
225
|
});
|
|
226
|
+
if (!blobSliceProbe) {
|
|
227
|
+
probeBlobSliceReliability();
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
createReadable(options) {
|
|
232
|
+
const reader = this;
|
|
233
|
+
const { blob, size } = reader;
|
|
234
|
+
const { offset = 0, size: readSize = size - offset } = options || {};
|
|
235
|
+
if (!offset && readSize >= size) {
|
|
236
|
+
return blob.stream();
|
|
237
|
+
}
|
|
238
|
+
if (blobSliceReliable) {
|
|
239
|
+
return blob.slice(offset, offset + readSize).stream();
|
|
240
|
+
}
|
|
241
|
+
return super.createReadable(options);
|
|
201
242
|
}
|
|
202
243
|
|
|
203
244
|
async readUint8Array(offset, length) {
|
|
204
245
|
const reader = this;
|
|
205
246
|
const offsetEnd = offset + length;
|
|
206
|
-
const
|
|
247
|
+
const readsWholeBlob = !offset && offsetEnd >= reader.size;
|
|
248
|
+
const blob = readsWholeBlob ? reader.blob : reader.blob.slice(offset, offsetEnd);
|
|
207
249
|
let arrayBuffer = await blob.arrayBuffer();
|
|
208
|
-
|
|
250
|
+
const sliceIgnoredByBuggyImplementation = arrayBuffer.byteLength > length;
|
|
251
|
+
if (sliceIgnoredByBuggyImplementation) {
|
|
209
252
|
arrayBuffer = arrayBuffer.slice(offset, offsetEnd);
|
|
210
253
|
}
|
|
211
254
|
return new Uint8Array(arrayBuffer);
|
|
@@ -286,6 +329,18 @@ class FetchReader extends Reader {
|
|
|
286
329
|
super.init();
|
|
287
330
|
}
|
|
288
331
|
|
|
332
|
+
createReadable(options) {
|
|
333
|
+
const reader = this;
|
|
334
|
+
const { useRangeHeader, forceRangeRequests, size } = reader;
|
|
335
|
+
if ((useRangeHeader || forceRangeRequests) && size !== UNDEFINED_VALUE) {
|
|
336
|
+
const { offset = 0, size: readSize = size - offset } = options || {};
|
|
337
|
+
if (readSize > 0 && offset < size) {
|
|
338
|
+
return createRangeReadable(reader, offset, Math.min(readSize, size - offset));
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return super.createReadable(options);
|
|
342
|
+
}
|
|
343
|
+
|
|
289
344
|
readUint8Array(index, length) {
|
|
290
345
|
return readUint8ArrayHttpReader(this, index, length, sendFetchRequest, getFetchRequestData);
|
|
291
346
|
}
|
|
@@ -314,6 +369,8 @@ function createHttpReader(httpReader, url, options) {
|
|
|
314
369
|
useRangeHeader,
|
|
315
370
|
forceRangeRequests,
|
|
316
371
|
combineSizeEocd,
|
|
372
|
+
checkResourceChanges = true,
|
|
373
|
+
maximumRangeSize = DEFAULT_MAXIMUM_RANGE_SIZE,
|
|
317
374
|
fetch
|
|
318
375
|
} = options;
|
|
319
376
|
options = Object.assign({}, options);
|
|
@@ -321,6 +378,8 @@ function createHttpReader(httpReader, url, options) {
|
|
|
321
378
|
delete options.useRangeHeader;
|
|
322
379
|
delete options.forceRangeRequests;
|
|
323
380
|
delete options.combineSizeEocd;
|
|
381
|
+
delete options.checkResourceChanges;
|
|
382
|
+
delete options.maximumRangeSize;
|
|
324
383
|
delete options.useXHR;
|
|
325
384
|
delete options.fetch;
|
|
326
385
|
Object.assign(httpReader, {
|
|
@@ -330,6 +389,8 @@ function createHttpReader(httpReader, url, options) {
|
|
|
330
389
|
useRangeHeader,
|
|
331
390
|
forceRangeRequests,
|
|
332
391
|
combineSizeEocd,
|
|
392
|
+
checkResourceChanges,
|
|
393
|
+
maximumRangeSize,
|
|
333
394
|
fetch
|
|
334
395
|
});
|
|
335
396
|
}
|
|
@@ -354,17 +415,8 @@ async function initHttpReader(httpReader, sendRequest, getRequestData) {
|
|
|
354
415
|
httpReader.eocdCache = eocdCache;
|
|
355
416
|
}
|
|
356
417
|
}
|
|
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
|
-
}
|
|
418
|
+
setResourceValidators(httpReader, response);
|
|
419
|
+
const contentSize = getContentRangeSize(response);
|
|
368
420
|
if (contentSize === UNDEFINED_VALUE) {
|
|
369
421
|
await getContentLength(httpReader, sendRequest, getRequestData);
|
|
370
422
|
} else {
|
|
@@ -405,6 +457,8 @@ async function readUint8ArrayHttpReader(httpReader, index, length, sendRequest,
|
|
|
405
457
|
throw new Error(ERR_HTTP_RANGE);
|
|
406
458
|
}
|
|
407
459
|
}
|
|
460
|
+
checkResourceValidators(httpReader, response);
|
|
461
|
+
setResourceValidators(httpReader, response);
|
|
408
462
|
const data = new Uint8Array(await response.arrayBuffer());
|
|
409
463
|
if (data.length != length) {
|
|
410
464
|
throw new Error(ERR_HTTP_RANGE);
|
|
@@ -416,7 +470,114 @@ async function readUint8ArrayHttpReader(httpReader, index, length, sendRequest,
|
|
|
416
470
|
if (!data) {
|
|
417
471
|
await getRequestData(httpReader, options);
|
|
418
472
|
}
|
|
419
|
-
return
|
|
473
|
+
return httpReader.data.subarray(index, index + length);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function createRangeReadable(httpReader, offset, size) {
|
|
478
|
+
let bodyReader;
|
|
479
|
+
let windowOffset = offset;
|
|
480
|
+
let windowRemainingLength = 0;
|
|
481
|
+
let remainingLength = size;
|
|
482
|
+
return new ReadableStream({
|
|
483
|
+
start() {
|
|
484
|
+
return openWindow();
|
|
485
|
+
},
|
|
486
|
+
async pull(controller) {
|
|
487
|
+
if (!bodyReader) {
|
|
488
|
+
await openWindow();
|
|
489
|
+
}
|
|
490
|
+
const { value, done } = await bodyReader.read();
|
|
491
|
+
if (done) {
|
|
492
|
+
throw new Error(ERR_HTTP_RANGE);
|
|
493
|
+
}
|
|
494
|
+
const chunk = value.length > windowRemainingLength ? value.subarray(0, windowRemainingLength) : value;
|
|
495
|
+
windowRemainingLength -= chunk.length;
|
|
496
|
+
remainingLength -= chunk.length;
|
|
497
|
+
if (chunk.length) {
|
|
498
|
+
controller.enqueue(chunk);
|
|
499
|
+
}
|
|
500
|
+
if (!windowRemainingLength) {
|
|
501
|
+
await closeWindow();
|
|
502
|
+
if (!remainingLength) {
|
|
503
|
+
controller.close();
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
},
|
|
507
|
+
cancel(reason) {
|
|
508
|
+
return bodyReader && bodyReader.cancel(reason);
|
|
509
|
+
}
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
async function openWindow() {
|
|
513
|
+
const windowLength = Math.min(httpReader.maximumRangeSize, remainingLength);
|
|
514
|
+
const response = await sendFetchRequest(HTTP_METHOD_GET, httpReader, getRangeHeaders(httpReader, windowOffset, windowLength));
|
|
515
|
+
if (response.status != 206) {
|
|
516
|
+
throw new Error(ERR_HTTP_RANGE);
|
|
517
|
+
}
|
|
518
|
+
const contentRangeHeader = response.headers.get(HTTP_HEADER_CONTENT_RANGE);
|
|
519
|
+
if (contentRangeHeader) {
|
|
520
|
+
const rangeStart = Number(contentRangeHeader.trim().split(/[\s-]+/)[1]);
|
|
521
|
+
if (!Number.isNaN(rangeStart) && rangeStart != windowOffset) {
|
|
522
|
+
throw new Error(ERR_HTTP_RANGE);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
checkResourceValidators(httpReader, response);
|
|
526
|
+
setResourceValidators(httpReader, response);
|
|
527
|
+
windowOffset += windowLength;
|
|
528
|
+
windowRemainingLength = windowLength;
|
|
529
|
+
bodyReader = response.body.getReader();
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
async function closeWindow() {
|
|
533
|
+
const currentBodyReader = bodyReader;
|
|
534
|
+
bodyReader = UNDEFINED_VALUE;
|
|
535
|
+
await currentBodyReader.cancel();
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
function getContentRangeSize(response) {
|
|
540
|
+
const contentRangeHeader = response.headers.get(HTTP_HEADER_CONTENT_RANGE);
|
|
541
|
+
if (contentRangeHeader) {
|
|
542
|
+
const headerValue = contentRangeHeader.trim().split(/\s*\/\s*/)[1];
|
|
543
|
+
if (headerValue && headerValue != "*") {
|
|
544
|
+
const contentSize = Number(headerValue);
|
|
545
|
+
if (!Number.isNaN(contentSize)) {
|
|
546
|
+
return contentSize;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
function getResourceValidators({ headers }) {
|
|
553
|
+
return {
|
|
554
|
+
etag: headers.get(HTTP_HEADER_ETAG) || UNDEFINED_VALUE,
|
|
555
|
+
lastModified: headers.get(HTTP_HEADER_LAST_MODIFIED) || UNDEFINED_VALUE
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
function setResourceValidators(httpReader, response) {
|
|
560
|
+
const { checkResourceChanges, resourceValidators } = httpReader;
|
|
561
|
+
if (checkResourceChanges && !resourceValidators && response.status == 206) {
|
|
562
|
+
httpReader.resourceValidators = getResourceValidators(response);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
function checkResourceValidators(httpReader, response) {
|
|
567
|
+
const { checkResourceChanges, resourceValidators, size } = httpReader;
|
|
568
|
+
if (checkResourceChanges) {
|
|
569
|
+
const contentRangeSize = getContentRangeSize(response);
|
|
570
|
+
if (contentRangeSize !== UNDEFINED_VALUE && size !== UNDEFINED_VALUE && contentRangeSize != size) {
|
|
571
|
+
throw new Error(ERR_HTTP_RESOURCE_CHANGED);
|
|
572
|
+
}
|
|
573
|
+
if (resourceValidators) {
|
|
574
|
+
const validators = getResourceValidators(response);
|
|
575
|
+
const changed = Object.entries(resourceValidators).some(([name, value]) =>
|
|
576
|
+
value !== UNDEFINED_VALUE && validators[name] !== UNDEFINED_VALUE && value != validators[name]);
|
|
577
|
+
if (changed) {
|
|
578
|
+
throw new Error(ERR_HTTP_RESOURCE_CHANGED);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
420
581
|
}
|
|
421
582
|
}
|
|
422
583
|
|
|
@@ -527,6 +688,10 @@ class HttpReader extends Reader {
|
|
|
527
688
|
super.init();
|
|
528
689
|
}
|
|
529
690
|
|
|
691
|
+
createReadable(options) {
|
|
692
|
+
return this.reader.createReadable(options);
|
|
693
|
+
}
|
|
694
|
+
|
|
530
695
|
readUint8Array(index, length) {
|
|
531
696
|
return this.reader.readUint8Array(index, length);
|
|
532
697
|
}
|
|
@@ -607,25 +772,26 @@ class SplitDataReader extends Reader {
|
|
|
607
772
|
const reader = this;
|
|
608
773
|
const { readers } = reader;
|
|
609
774
|
reader.lastDiskNumber = 0;
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
if (indexDiskReader != readers.length - 1) {
|
|
614
|
-
reader.lastDiskOffset += diskReader.size;
|
|
615
|
-
}
|
|
775
|
+
await Promise.all(readers.map(diskReader => initStream(diskReader)));
|
|
776
|
+
reader.diskOffsets = readers.map(diskReader => {
|
|
777
|
+
const diskOffset = reader.size;
|
|
616
778
|
reader.size += diskReader.size;
|
|
617
|
-
|
|
779
|
+
return diskOffset;
|
|
780
|
+
});
|
|
618
781
|
super.init();
|
|
619
782
|
}
|
|
620
783
|
|
|
621
|
-
|
|
784
|
+
getDiskOffset(diskNumber) {
|
|
785
|
+
const { diskOffsets, size } = this;
|
|
786
|
+
const diskOffset = diskOffsets.at(diskNumber);
|
|
787
|
+
return diskOffset === UNDEFINED_VALUE ? size : diskOffset;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
async readUint8Array(offset, length) {
|
|
622
791
|
const reader = this;
|
|
623
792
|
const { readers } = this;
|
|
624
793
|
let result;
|
|
625
|
-
let currentDiskNumber =
|
|
626
|
-
if (currentDiskNumber == -1) {
|
|
627
|
-
currentDiskNumber = readers.length - 1;
|
|
628
|
-
}
|
|
794
|
+
let currentDiskNumber = 0;
|
|
629
795
|
let currentReaderOffset = offset;
|
|
630
796
|
while (readers[currentDiskNumber] && currentReaderOffset >= readers[currentDiskNumber].size) {
|
|
631
797
|
currentReaderOffset -= readers[currentDiskNumber].size;
|
|
@@ -638,14 +804,9 @@ class SplitDataReader extends Reader {
|
|
|
638
804
|
result = await readUint8Array(currentReader, currentReaderOffset, length);
|
|
639
805
|
} else {
|
|
640
806
|
const chunkLength = currentReaderSize - currentReaderOffset;
|
|
641
|
-
result = new Uint8Array(length);
|
|
642
807
|
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
|
-
}
|
|
808
|
+
const secondPart = await reader.readUint8Array(offset + chunkLength, length - chunkLength);
|
|
809
|
+
result = concat(firstPart, secondPart);
|
|
649
810
|
}
|
|
650
811
|
} else {
|
|
651
812
|
result = EMPTY_UINT8_ARRAY;
|
|
@@ -670,6 +831,12 @@ class SplitDataWriter extends Stream {
|
|
|
670
831
|
let diskSourceWriter, diskWritable, diskWriter;
|
|
671
832
|
const writable = new WritableStream({
|
|
672
833
|
async write(chunk) {
|
|
834
|
+
if (chunk === DISK_BOUNDARY) {
|
|
835
|
+
if (diskWriter) {
|
|
836
|
+
await endDisk();
|
|
837
|
+
}
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
673
840
|
const { availableSize } = writer;
|
|
674
841
|
if (!diskWriter) {
|
|
675
842
|
const { value, done } = await writerGenerator.next();
|
|
@@ -689,11 +856,7 @@ class SplitDataWriter extends Stream {
|
|
|
689
856
|
await this.write(chunk);
|
|
690
857
|
} else if (chunk.length >= availableSize) {
|
|
691
858
|
await writeChunk(chunk.subarray(0, availableSize));
|
|
692
|
-
await
|
|
693
|
-
writer.diskOffset += diskSourceWriter.size;
|
|
694
|
-
writer.diskNumber++;
|
|
695
|
-
diskWriter = null;
|
|
696
|
-
writer.availableSize = writer.maxSize;
|
|
859
|
+
await endDisk();
|
|
697
860
|
if (chunk.length > availableSize) {
|
|
698
861
|
await this.write(chunk.subarray(availableSize));
|
|
699
862
|
}
|
|
@@ -704,7 +867,7 @@ class SplitDataWriter extends Stream {
|
|
|
704
867
|
async close() {
|
|
705
868
|
if (diskWriter) {
|
|
706
869
|
await diskWriter.ready;
|
|
707
|
-
await
|
|
870
|
+
await closeDiskWriter();
|
|
708
871
|
}
|
|
709
872
|
},
|
|
710
873
|
async abort(reason) {
|
|
@@ -729,10 +892,28 @@ class SplitDataWriter extends Stream {
|
|
|
729
892
|
}
|
|
730
893
|
}
|
|
731
894
|
|
|
732
|
-
async function
|
|
895
|
+
async function endDisk() {
|
|
896
|
+
await closeDiskWriter();
|
|
897
|
+
writer.diskOffset += diskSourceWriter.size;
|
|
898
|
+
writer.diskNumber++;
|
|
899
|
+
diskWriter = null;
|
|
900
|
+
writer.availableSize = writer.maxSize;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
async function closeDiskWriter() {
|
|
733
904
|
await diskWriter.close();
|
|
734
905
|
}
|
|
735
906
|
}
|
|
907
|
+
|
|
908
|
+
async closeDisk() {
|
|
909
|
+
const streamWriter = this.writable.getWriter();
|
|
910
|
+
try {
|
|
911
|
+
await streamWriter.ready;
|
|
912
|
+
await streamWriter.write(DISK_BOUNDARY);
|
|
913
|
+
} finally {
|
|
914
|
+
streamWriter.releaseLock();
|
|
915
|
+
}
|
|
916
|
+
}
|
|
736
917
|
}
|
|
737
918
|
|
|
738
919
|
class GenericReader {
|
|
@@ -764,14 +945,6 @@ class GenericWriter {
|
|
|
764
945
|
if (writer.size === UNDEFINED_VALUE) {
|
|
765
946
|
writer.size = 0;
|
|
766
947
|
}
|
|
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
948
|
return writer;
|
|
776
949
|
}
|
|
777
950
|
}
|
|
@@ -790,8 +963,8 @@ async function initStream(stream, initSize) {
|
|
|
790
963
|
}
|
|
791
964
|
}
|
|
792
965
|
|
|
793
|
-
function readUint8Array(reader, offset, size
|
|
794
|
-
return reader.readUint8Array(offset, size
|
|
966
|
+
function readUint8Array(reader, offset, size) {
|
|
967
|
+
return reader.readUint8Array(offset, size);
|
|
795
968
|
}
|
|
796
969
|
|
|
797
970
|
export {
|
|
@@ -814,6 +987,7 @@ export {
|
|
|
814
987
|
SplitDataReader,
|
|
815
988
|
SplitDataWriter,
|
|
816
989
|
ERR_HTTP_RANGE,
|
|
990
|
+
ERR_HTTP_RESOURCE_CHANGED,
|
|
817
991
|
ERR_ITERATOR_COMPLETED_TOO_SOON,
|
|
818
992
|
ERR_WRITER_NOT_INITIALIZED
|
|
819
993
|
};
|
|
@@ -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
|
-
|
|
137
|
-
|
|
136
|
+
const pendingChunks = [];
|
|
137
|
+
let pendingLength = 0;
|
|
138
|
+
if (!Number.isFinite(chunkSize) || 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
|
}
|