@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.
Files changed (52) hide show
  1. package/README.md +13 -1
  2. package/deno.json +5 -2
  3. package/dist/README.md +3 -1
  4. package/dist/zip-core.js +472 -220
  5. package/dist/zip-core.min.js +1 -1
  6. package/dist/zip-fs-core.js +449 -220
  7. package/dist/zip-fs-core.min.js +1 -1
  8. package/dist/zip-fs-native.js +476 -226
  9. package/dist/zip-fs-native.min.js +1 -1
  10. package/dist/zip-fs.js +478 -228
  11. package/dist/zip-fs.min.js +1 -1
  12. package/dist/zip-legacy.js +474 -222
  13. package/dist/zip-legacy.min.js +1 -1
  14. package/dist/zip-native.js +475 -223
  15. package/dist/zip-native.min.js +1 -1
  16. package/dist/zip-web-worker-native.js +1 -1
  17. package/dist/zip-web-worker.js +1 -1
  18. package/dist/zip.js +477 -225
  19. package/dist/zip.min.js +1 -1
  20. package/index-native.cjs +476 -226
  21. package/index-native.min.js +1 -1
  22. package/index.cjs +478 -228
  23. package/index.d.cts +53 -0
  24. package/index.d.ts +53 -0
  25. package/index.min.js +1 -1
  26. package/lib/core/codec-pool.js +2 -2
  27. package/lib/core/codec-worker.js +6 -2
  28. package/lib/core/configuration.js +22 -1
  29. package/lib/core/io.js +209 -56
  30. package/lib/core/streams/aes-crypto-stream.js +1 -10
  31. package/lib/core/streams/codec-stream.js +36 -15
  32. package/lib/core/streams/zip-entry-stream.js +19 -28
  33. package/lib/core/util/array.js +48 -0
  34. package/lib/core/web-worker-base.js +3 -3
  35. package/lib/core/web-worker-inline-native.js +1 -1
  36. package/lib/core/web-worker-inline-wasm.js +1 -1
  37. package/lib/core/zip-fs.js +2 -3
  38. package/lib/core/zip-reader.js +37 -40
  39. package/lib/core/zip-writer.js +58 -51
  40. package/lib/core/zlib-streams-inline.js +1 -1
  41. package/lib/zip-core-base.js +5 -3
  42. package/lib/zip-fs-native.js +2 -2
  43. package/lib/zip-fs-wasm.js +2 -2
  44. package/lib/zip-legacy.js +2 -2
  45. package/lib/zip-module-native.js +2 -2
  46. package/lib/zip-module-wasm.js +3 -3
  47. package/lib/zip-native.js +2 -2
  48. package/lib/zip-wasm.js +2 -2
  49. package/package.json +3 -3
  50. package/worker-message-property-names.js +3 -0
  51. package/.github/workflows/publish.yml +0 -30
  52. package/reserved-property-names.js +0 -50
@@ -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 && !workerOptions.transferStreams;
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);
@@ -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.pipeThrough(codecStream).pipeTo(writable, { preventClose: true, preventAbort: true });
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 = value.byteOffset || value.byteLength != value.buffer.byteLength ? new Uint8Array(value) : 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 = Object.assign({}, DEFAULT_CONFIGURATION);
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, diskNumberStart, chunkSize = DEFAULT_CHUNK_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, diskNumberStart);
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 blob = offset || offsetEnd < reader.size ? reader.blob.slice(offset, offsetEnd) : reader.blob;
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
- if (arrayBuffer.byteLength > length) {
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
- let contentSize;
358
- const contentRangeHeader = response.headers.get(HTTP_HEADER_CONTENT_RANGE);
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 new Uint8Array(httpReader.data.subarray(index, index + length));
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
- reader.lastDiskOffset = 0;
611
- await Promise.all(readers.map(async (diskReader, indexDiskReader) => {
612
- await initStream(diskReader);
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
- async readUint8Array(offset, length, diskNumber = 0) {
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 = diskNumber;
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
- result.set(firstPart, 0);
644
- const secondPart = await reader.readUint8Array(offset + chunkLength, length - chunkLength, diskNumber);
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 closeDisk();
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 closeDisk();
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 closeDisk() {
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, diskNumber) {
794
- return reader.readUint8Array(offset, size, diskNumber);
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
- let pendingChunk;
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 (pendingChunk && pendingChunk.length) {
144
- controller.enqueue(pendingChunk);
150
+ if (pendingLength) {
151
+ controller.enqueue(concatChunks(pendingChunks, pendingLength));
145
152
  }
146
153
  }
147
154
  });
148
155
 
149
- function transform(chunk, controller) {
150
- if (pendingChunk) {
151
- const newChunk = new Uint8Array(pendingChunk.length + chunk.length);
152
- newChunk.set(pendingChunk);
153
- newChunk.set(chunk, pendingChunk.length);
154
- chunk = newChunk;
155
- pendingChunk = null;
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
- while (chunk.length - offset > chunkSize) {
159
- controller.enqueue(chunk.slice(offset, offset + chunkSize));
160
- offset += chunkSize;
179
+ for (const chunk of chunks) {
180
+ result.set(chunk, offset);
181
+ offset += chunk.length;
161
182
  }
162
- pendingChunk = offset ? chunk.slice(offset) : chunk;
183
+ return result;
163
184
  }
164
185
  }
165
186
  }