@zip.js/zip.js 2.11.4 → 2.13.0

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/deno.json +1 -1
  2. package/dist/zip-core-external.js +549 -139
  3. package/dist/zip-core-external.min.js +1 -1
  4. package/dist/zip-core.js +559 -136
  5. package/dist/zip-core.min.js +1 -1
  6. package/dist/zip-fs-core-external.js +615 -260
  7. package/dist/zip-fs-core-external.min.js +1 -1
  8. package/dist/zip-fs-core.js +628 -257
  9. package/dist/zip-fs-core.min.js +1 -1
  10. package/dist/zip-fs-external.js +615 -260
  11. package/dist/zip-fs-external.min.js +1 -1
  12. package/dist/zip-fs-native.js +630 -259
  13. package/dist/zip-fs-native.min.js +1 -1
  14. package/dist/zip-fs.js +631 -260
  15. package/dist/zip-fs.min.js +1 -1
  16. package/dist/zip-legacy.js +561 -138
  17. package/dist/zip-legacy.min.js +1 -1
  18. package/dist/zip-native.js +561 -138
  19. package/dist/zip-native.min.js +1 -1
  20. package/dist/zip-web-worker-native.js +1 -1
  21. package/dist/zip-web-worker.js +1 -1
  22. package/dist/zip.js +562 -139
  23. package/dist/zip.min.js +1 -1
  24. package/index-native.cjs +630 -259
  25. package/index-native.min.js +1 -1
  26. package/index.cjs +631 -260
  27. package/index.d.cts +474 -59
  28. package/index.d.ts +474 -59
  29. package/index.min.js +1 -1
  30. package/lib/core/codec-worker-web.js +72 -22
  31. package/lib/core/codec-worker.js +18 -5
  32. package/lib/core/configuration.js +20 -5
  33. package/lib/core/constants.js +2 -0
  34. package/lib/core/io.js +13 -5
  35. package/lib/core/options.js +16 -0
  36. package/lib/core/streams/codec-stream.js +6 -0
  37. package/lib/core/util/error.js +35 -0
  38. package/lib/core/util/warnings.js +42 -0
  39. package/lib/core/version.js +1 -1
  40. package/lib/core/web-worker-base.js +46 -9
  41. package/lib/core/web-worker-inline-native.js +1 -1
  42. package/lib/core/web-worker-inline-wasm.js +1 -1
  43. package/lib/core/zip-entry.js +49 -1
  44. package/lib/core/zip-fs.js +45 -133
  45. package/lib/core/zip-reader.js +115 -52
  46. package/lib/core/zip-writer.js +244 -57
  47. package/lib/zip-core-base.js +9 -3
  48. package/lib/zip-core-reader.js +2 -0
  49. package/lib/zip-core-writer.js +6 -1
  50. package/lib/zip-module-wasm-base.js +2 -2
  51. package/package.json +5 -3
  52. package/worker-message-property-names.js +1 -1
@@ -33,6 +33,7 @@ import {
33
33
  FUNCTION_TYPE
34
34
  } from "./constants.js";
35
35
  import { toExactUint8Array } from "./util/array.js";
36
+ import { isErrorObject } from "./util/error.js";
36
37
  import {
37
38
  MESSAGE_EVENT_TYPE,
38
39
  MESSAGE_START,
@@ -121,11 +122,23 @@ async function runWebWorker(workerData, config) {
121
122
  let resolveResult, rejectResult;
122
123
  const result = new Promise((resolve, reject) => {
123
124
  resolveResult = resolve;
124
- rejectResult = reject;
125
+ rejectResult = error => {
126
+ const { outputSize, workerOptions } = workerData;
127
+ workerOptions.outputSize = outputSize;
128
+ if (isErrorObject(error) && error.outputSize === UNDEFINED_VALUE) {
129
+ try {
130
+ error.outputSize = outputSize;
131
+ } catch {
132
+ // ignored
133
+ }
134
+ }
135
+ reject(error);
136
+ };
125
137
  });
126
138
  Object.assign(workerData, {
127
139
  reader: null,
128
140
  writer: null,
141
+ outputSize: 0,
129
142
  resolveResult,
130
143
  rejectResult,
131
144
  result
@@ -386,21 +399,15 @@ function sendMessage(message, { worker, writer, transferStreams, workerAlive })
386
399
  }
387
400
 
388
401
  async function onMessage({ data }, workerData) {
389
- const { type, value, messageId, result, error } = data;
402
+ const { type, value, messageId, result, error, errorValue } = data;
390
403
  const { reader, writer, resolveResult, rejectResult, onTaskFinished, generation } = workerData;
391
404
  const stale = () => workerData.generation != generation;
392
405
  try {
393
406
  if (error) {
394
- const { message, stack, code, name, outputSize, cause, codecImportFailed } = error;
395
- const responseError = new Error(message);
396
- Object.assign(responseError, { stack, code, name, outputSize });
397
- if (cause) {
398
- responseError.cause = Object.assign(new Error(cause.message), { name: cause.name });
399
- }
400
- if (codecImportFailed) {
401
- responseError.codecImportFailed = true;
407
+ if (error.outputSize !== UNDEFINED_VALUE) {
408
+ workerData.outputSize = error.outputSize;
402
409
  }
403
- close(responseError);
410
+ fail(getResponseError(error, errorValue));
404
411
  } else {
405
412
  if (type == MESSAGE_PULL) {
406
413
  const { value, done } = await reader.read();
@@ -409,37 +416,80 @@ async function onMessage({ data }, workerData) {
409
416
  }
410
417
  }
411
418
  if (type == MESSAGE_DATA) {
419
+ const chunk = new Uint8Array(value);
412
420
  await writer.ready;
413
- await writer.write(new Uint8Array(value));
421
+ await writer.write(chunk);
422
+ workerData.outputSize += chunk.length;
414
423
  if (!stale()) {
415
424
  sendMessage({ type: MESSAGE_ACK_DATA, messageId }, workerData);
416
425
  }
417
426
  }
418
427
  if (type == MESSAGE_CLOSE) {
419
- close(null, result);
428
+ succeed(result);
420
429
  }
421
430
  }
422
431
  } catch (error) {
423
432
  if (!stale()) {
424
433
  terminateWorker(workerData);
425
- close(error);
434
+ fail(error);
426
435
  }
427
436
  }
428
437
 
429
- function close(error, result) {
430
- if (stale()) {
431
- return;
432
- }
433
- if (error) {
438
+ function fail(error) {
439
+ if (!stale()) {
434
440
  rejectResult(error);
435
- } else {
441
+ releaseWriter();
442
+ if (!(isErrorObject(error) && error.codecImportFailed)) {
443
+ onTaskFinished();
444
+ }
445
+ }
446
+ }
447
+
448
+ function succeed(result) {
449
+ if (!stale()) {
436
450
  resolveResult(result);
451
+ releaseWriter();
452
+ onTaskFinished();
437
453
  }
454
+ }
455
+
456
+ function releaseWriter() {
438
457
  if (writer) {
439
458
  writer.releaseLock();
440
459
  }
441
- if (!(error && error.codecImportFailed)) {
442
- onTaskFinished();
460
+ }
461
+ }
462
+
463
+ function getResponseError(errorData, errorValue) {
464
+ const { message, stack, code, name, outputSize, cause, codecImportFailed } = errorData;
465
+ let responseError;
466
+ if (errorValue) {
467
+ responseError = errorValue.value;
468
+ } else {
469
+ responseError = Object.assign(new Error(message), { stack, code, name });
470
+ if (cause) {
471
+ responseError.cause = Object.assign(new Error(cause.message), { name: cause.name });
472
+ }
473
+ }
474
+ if (isErrorObject(responseError)) {
475
+ try {
476
+ if (outputSize !== UNDEFINED_VALUE) {
477
+ responseError.outputSize = outputSize;
478
+ }
479
+ if (codecImportFailed) {
480
+ responseError.codecImportFailed = true;
481
+ }
482
+ if (errorValue) {
483
+ if (responseError.name !== name) {
484
+ responseError.name = name;
485
+ }
486
+ if (responseError.code !== code) {
487
+ responseError.code = code;
488
+ }
489
+ }
490
+ } catch {
491
+ // ignored
443
492
  }
444
493
  }
494
+ return responseError;
445
495
  }
@@ -33,6 +33,7 @@ import {
33
33
  UNDEFINED_TYPE
34
34
  } from "./constants.js";
35
35
  import { getChunkSize } from "./configuration.js";
36
+ import { isErrorObject } from "./util/error.js";
36
37
  import {
37
38
  CODEC_DEFLATE,
38
39
  CodecStream,
@@ -102,7 +103,9 @@ function disableWebWorker(workerData) {
102
103
 
103
104
  class CodecWorker {
104
105
 
105
- constructor(workerData, { readable, writable }, { options, config, streamOptions, useWebWorkers, transferStreams, workerURI, createWorker }, onTaskFinished) {
106
+ constructor(workerData, { readable, writable }, workerOptions, onTaskFinished) {
107
+ const { options, config, streamOptions, useWebWorkers, transferStreams, workerURI } = workerOptions;
108
+ let { createWorker } = workerOptions;
106
109
  const { signal } = streamOptions;
107
110
  if (createWorkerFailed) {
108
111
  createWorker = UNDEFINED_VALUE;
@@ -115,6 +118,7 @@ class CodecWorker {
115
118
  .pipeThrough(new ProgressWatcherStream(streamOptions), { signal }),
116
119
  writable,
117
120
  options: Object.assign({}, options),
121
+ workerOptions,
118
122
  workerURI,
119
123
  createWorker,
120
124
  transferStreams,
@@ -196,8 +200,8 @@ function createWorkerInterface(workerData, config) {
196
200
  };
197
201
  }
198
202
 
199
- async function runWorker({ options, readable, writable, onTaskFinished }, config) {
200
- let codecStream;
203
+ async function runWorker({ options, readable, writable, onTaskFinished, workerOptions }, config) {
204
+ let codecStream, chunkStream;
201
205
  try {
202
206
  if (options.compressed && !options.format) {
203
207
  const deflate = options.codecType.startsWith(CODEC_DEFLATE);
@@ -220,9 +224,10 @@ async function runWorker({ options, readable, writable, onTaskFinished }, config
220
224
  }
221
225
  }
222
226
  codecStream = new CodecStream(options, config);
227
+ chunkStream = new ChunkStream(getChunkSize(config));
223
228
  await readable
224
229
  .pipeThrough(codecStream)
225
- .pipeThrough(new ChunkStream(getChunkSize(config)))
230
+ .pipeThrough(chunkStream)
226
231
  .pipeTo(writable, { preventClose: true, preventAbort: true });
227
232
  const {
228
233
  crc32,
@@ -236,7 +241,15 @@ async function runWorker({ options, readable, writable, onTaskFinished }, config
236
241
  };
237
242
  } catch (error) {
238
243
  if (codecStream) {
239
- error.outputSize = codecStream.outputSize;
244
+ const outputSize = chunkStream ? chunkStream.outputSize : 0;
245
+ workerOptions.outputSize = outputSize;
246
+ if (isErrorObject(error)) {
247
+ try {
248
+ error.outputSize = outputSize;
249
+ } catch {
250
+ // ignored
251
+ }
252
+ }
240
253
  }
241
254
  throw error;
242
255
  } finally {
@@ -30,7 +30,9 @@
30
30
 
31
31
  import {
32
32
  UNDEFINED_VALUE,
33
- UNDEFINED_TYPE
33
+ UNDEFINED_TYPE,
34
+ FUNCTION_TYPE,
35
+ STRING_TYPE
34
36
  } from "./constants.js";
35
37
  import {
36
38
  checkFunctionOption,
@@ -41,6 +43,8 @@ const DEFAULT_CHUNK_SIZE = 64 * 1024;
41
43
  const MINIMUM_CHUNK_SIZE = 64;
42
44
  const MINIMUM_PROPERTY_VALUE = 1;
43
45
  const ERR_INVALID_MAX_WORKERS = "Invalid maxWorkers (must be an integer greater than 0)";
46
+ const ERR_INVALID_BASE_URI = "Invalid baseURI (must be a string)";
47
+ const ERR_INVALID_URI = "Invalid URI (must be a string or a function returning a string)";
44
48
  let maxWorkers = 2;
45
49
  try {
46
50
  if (typeof navigator != UNDEFINED_TYPE && navigator.hardwareConcurrency) {
@@ -65,9 +69,9 @@ const DEFAULT_CONFIGURATION = {
65
69
  };
66
70
 
67
71
  const PROPERTY_NAME_MAX_WORKERS = "maxWorkers";
72
+ const PROPERTY_NAME_BASE_URI = "baseURI";
68
73
 
69
- const STRING_PROPERTY_NAMES = [
70
- "baseURI",
74
+ const URI_PROPERTY_NAMES = [
71
75
  "wasmURI",
72
76
  "workerURI"
73
77
  ];
@@ -91,7 +95,8 @@ const FUNCTION_PROPERTY_NAMES = [
91
95
  "DecompressionStreamFallback"
92
96
  ];
93
97
  const CONFIGURABLE_PROPERTY_NAMES = [
94
- ...STRING_PROPERTY_NAMES,
98
+ PROPERTY_NAME_BASE_URI,
99
+ ...URI_PROPERTY_NAMES,
95
100
  ...BOOLEAN_PROPERTY_NAMES,
96
101
  ...NUMBER_PROPERTY_NAMES,
97
102
  ...FUNCTION_PROPERTY_NAMES
@@ -106,7 +111,9 @@ export {
106
111
  getConfiguration,
107
112
  getChunkSize,
108
113
  normalizeChunkSize,
109
- ERR_INVALID_MAX_WORKERS
114
+ ERR_INVALID_MAX_WORKERS,
115
+ ERR_INVALID_BASE_URI,
116
+ ERR_INVALID_URI
110
117
  };
111
118
 
112
119
  function getConfiguration() {
@@ -145,6 +152,14 @@ function checkPropertyValue(propertyName, propertyValue) {
145
152
  }
146
153
  } else if (FUNCTION_PROPERTY_NAMES.includes(propertyName)) {
147
154
  checkFunctionOption(propertyValue);
155
+ } else if (propertyName == PROPERTY_NAME_BASE_URI) {
156
+ if (propertyValue && typeof propertyValue != STRING_TYPE) {
157
+ throw new Error(ERR_INVALID_BASE_URI);
158
+ }
159
+ } else if (URI_PROPERTY_NAMES.includes(propertyName)) {
160
+ if (propertyValue && typeof propertyValue != STRING_TYPE && typeof propertyValue != FUNCTION_TYPE) {
161
+ throw new Error(ERR_INVALID_URI);
162
+ }
148
163
  }
149
164
  return propertyValue;
150
165
  }
@@ -125,9 +125,11 @@ const NUMBER_TYPE = "number";
125
125
  const BOOLEAN_TYPE = "boolean";
126
126
 
127
127
  const EMPTY_UINT8_ARRAY = new Uint8Array();
128
+ const SYMBOL_ASYNC_DISPOSE = Symbol.asyncDispose || Symbol();
128
129
 
129
130
  export {
130
131
  EMPTY_UINT8_ARRAY,
132
+ SYMBOL_ASYNC_DISPOSE,
131
133
  MAX_32_BITS,
132
134
  MAX_16_BITS,
133
135
  MAX_8_BITS,
package/lib/core/io.js CHANGED
@@ -42,10 +42,13 @@ import { toCompatibleReadable, toCompatibleWritable, streamToBlob } from "./util
42
42
  import { decodeTextRemovingBOM } from "./util/decode-text.js";
43
43
 
44
44
  const ERR_HTTP_STATUS = "HTTP error ";
45
+ const MIN_SUCCESS_HTTP_STATUS = 200;
46
+ const MAX_SUCCESS_HTTP_STATUS = 299;
45
47
  const ERR_HTTP_RANGE = "HTTP Range not supported";
46
48
  const ERR_HTTP_RESOURCE_CHANGED = "HTTP resource changed";
47
49
  const ERR_ITERATOR_COMPLETED_TOO_SOON = "Writer iterator completed too soon";
48
50
  const ERR_WRITER_NOT_INITIALIZED = "Writer not initialized";
51
+ const ERR_WRITER_SIZE_NOT_WRITABLE = "Invalid writer (size must be writable)";
49
52
 
50
53
  const CONTENT_TYPE_TEXT_PLAIN = "text/plain";
51
54
  const HTTP_HEADER_CONTENT_LENGTH = "Content-Length";
@@ -237,6 +240,7 @@ class BlobReader extends Reader {
237
240
  const reader = this;
238
241
  const { sourceBlob, size } = reader;
239
242
  const { offset = 0, size: readSize = size - offset } = options || {};
243
+ // deno-lint-ignore valid-typeof
240
244
  if (typeof sourceBlob.stream == FUNCTION_TYPE) {
241
245
  if (!offset && readSize >= size) {
242
246
  return toCompatibleReadable(sourceBlob.stream());
@@ -643,7 +647,7 @@ async function getContentLength(httpReader, sendRequest, getRequestData) {
643
647
 
644
648
  async function sendFetchRequest(method, { fetch: fetchFunction = fetch, options, url }, headers) {
645
649
  const response = await fetchFunction(url, Object.assign({}, options, { method, headers }));
646
- if (response.status < 400) {
650
+ if (response.status >= MIN_SUCCESS_HTTP_STATUS && response.status <= MAX_SUCCESS_HTTP_STATUS) {
647
651
  return response;
648
652
  } else {
649
653
  throw response.status == 416 ? new Error(ERR_HTTP_RANGE) : new Error(ERR_HTTP_STATUS + (response.statusText || response.status));
@@ -654,7 +658,7 @@ function sendXMLHttpRequest(method, { url }, headers) {
654
658
  return new Promise((resolve, reject) => {
655
659
  const request = new XMLHttpRequest();
656
660
  request.addEventListener("load", () => {
657
- if (request.status < 400) {
661
+ if (request.status >= MIN_SUCCESS_HTTP_STATUS && request.status <= MAX_SUCCESS_HTTP_STATUS) {
658
662
  const headers = [];
659
663
  request.getAllResponseHeaders().trim().split(/[\r\n]+/).forEach(header => {
660
664
  const splitHeader = header.trim().split(/\s*:\s*/);
@@ -958,8 +962,10 @@ class GenericWriter {
958
962
  writable: toCompatibleWritable(writer)
959
963
  };
960
964
  }
961
- if (writer.size === UNDEFINED_VALUE) {
962
- writer.size = 0;
965
+ try {
966
+ writer.size = writer.size === UNDEFINED_VALUE ? 0 : writer.size;
967
+ } catch {
968
+ throw new Error(ERR_WRITER_SIZE_NOT_WRITABLE);
963
969
  }
964
970
  return writer;
965
971
  }
@@ -1030,7 +1036,9 @@ export {
1030
1036
  SplitDataReader,
1031
1037
  SplitDataWriter,
1032
1038
  ERR_HTTP_RANGE,
1039
+ ERR_HTTP_STATUS,
1033
1040
  ERR_HTTP_RESOURCE_CHANGED,
1034
1041
  ERR_ITERATOR_COMPLETED_TOO_SOON,
1035
- ERR_WRITER_NOT_INITIALIZED
1042
+ ERR_WRITER_NOT_INITIALIZED,
1043
+ ERR_WRITER_SIZE_NOT_WRITABLE
1036
1044
  };
@@ -49,6 +49,7 @@ const OPTION_CHECK_OVERLAPPING_ENTRY_ONLY = "checkOverlappingEntryOnly";
49
49
  const OPTION_CHECK_OVERLAPPING_ENTRY = "checkOverlappingEntry";
50
50
  const OPTION_CHECK_AMBIGUITY = "checkAmbiguity";
51
51
  const OPTION_CHECK_LOCAL_DIRECTORY = "checkLocalDirectory";
52
+ const OPTION_CHECK_LOCAL_FILENAME = "checkLocalFilename";
52
53
  const OPTION_CHECK_SIGNATURE = "checkSignature";
53
54
  const OPTION_CHECK_CRC32 = "checkCrc32";
54
55
  const OPTION_CHECK_AUTHENTICATION_CODE = "checkAuthenticationCode";
@@ -79,15 +80,18 @@ const OPTION_NORMALIZE_FILENAME = "normalizeFilename";
79
80
  const OPTION_MAX_APPENDED_DATA_SIZE = "maxAppendedDataSize";
80
81
  const OPTION_DECRYPT_CENTRAL_DIRECTORY = "decryptCentralDirectory";
81
82
  const OPTION_SIGN_CENTRAL_DIRECTORY = "signCentralDirectory";
83
+ const OPTION_ENTRY = "entry";
82
84
  const TEXT_TYPE_FILENAME = "filename";
83
85
  const TEXT_TYPE_COMMENT = "comment";
84
86
  const STRICTNESS_STRICT = "strict";
85
87
  const STRICTNESS_BALANCED = "balanced";
86
88
  const STRICTNESS_TOLERANT = "tolerant";
89
+ const PASS_THROUGH_COMPRESSED = "compressed";
87
90
 
88
91
  const ERR_INVALID_FUNCTION_OPTION = "Invalid option (must be a function)";
89
92
  const ERR_INVALID_SIGNAL = "Invalid signal (must be an AbortSignal instance)";
90
93
  const ERR_INVALID_PASSWORD_TYPE = "Invalid password (password must be a string, rawPassword must be a Uint8Array)";
94
+ const ERR_INVALID_PASS_THROUGH_VALUE = "Invalid passThrough option (must be a boolean or 'compressed')";
91
95
  const ERR_ABORTED = "The operation was aborted";
92
96
  const ABORT_ERROR_NAME = "AbortError";
93
97
 
@@ -106,6 +110,7 @@ export {
106
110
  OPTION_CHECK_OVERLAPPING_ENTRY,
107
111
  OPTION_CHECK_AMBIGUITY,
108
112
  OPTION_CHECK_LOCAL_DIRECTORY,
113
+ OPTION_CHECK_LOCAL_FILENAME,
109
114
  OPTION_CHECK_SIGNATURE,
110
115
  OPTION_CHECK_CRC32,
111
116
  OPTION_CHECK_AUTHENTICATION_CODE,
@@ -136,20 +141,24 @@ export {
136
141
  OPTION_MAX_APPENDED_DATA_SIZE,
137
142
  OPTION_DECRYPT_CENTRAL_DIRECTORY,
138
143
  OPTION_SIGN_CENTRAL_DIRECTORY,
144
+ OPTION_ENTRY,
139
145
  TEXT_TYPE_FILENAME,
140
146
  TEXT_TYPE_COMMENT,
141
147
  STRICTNESS_STRICT,
142
148
  STRICTNESS_BALANCED,
143
149
  STRICTNESS_TOLERANT,
150
+ PASS_THROUGH_COMPRESSED,
144
151
  ERR_INVALID_FUNCTION_OPTION,
145
152
  ERR_INVALID_SIGNAL,
146
153
  ERR_INVALID_PASSWORD_TYPE,
154
+ ERR_INVALID_PASS_THROUGH_VALUE,
147
155
  ERR_ABORTED,
148
156
  ABORT_ERROR_NAME,
149
157
  checkFunctionOption,
150
158
  checkSignalOption,
151
159
  throwIfAborted,
152
160
  checkPasswordOption,
161
+ checkPassThroughOption,
153
162
  checkInteger,
154
163
  checkIntegerOption,
155
164
  toNumber
@@ -181,6 +190,13 @@ function checkPasswordOption(password, rawPassword) {
181
190
  }
182
191
  }
183
192
 
193
+ function checkPassThroughOption(passThrough) {
194
+ if (passThrough !== UNDEFINED_VALUE && typeof passThrough != BOOLEAN_TYPE && passThrough !== PASS_THROUGH_COMPRESSED) {
195
+ throw new Error(ERR_INVALID_PASS_THROUGH_VALUE);
196
+ }
197
+ return passThrough;
198
+ }
199
+
184
200
  function checkInteger(value, maxValue, errorMessage) {
185
201
  if (!Number.isInteger(value) || value < 0 || value > maxValue) {
186
202
  throw new Error(errorMessage);
@@ -145,6 +145,7 @@ class ChunkStream extends TransformStream {
145
145
  constructor(chunkSize) {
146
146
  const pendingChunks = [];
147
147
  let pendingLength = 0;
148
+ let outputSize = 0;
148
149
  if (!Number.isFinite(chunkSize) || chunkSize < 1) {
149
150
  chunkSize = DEFAULT_CHUNK_SIZE;
150
151
  }
@@ -153,15 +154,20 @@ class ChunkStream extends TransformStream {
153
154
  pendingChunks.push(chunk);
154
155
  pendingLength += chunk.length;
155
156
  while (pendingLength > chunkSize) {
157
+ outputSize += chunkSize;
156
158
  controller.enqueue(shiftChunk());
157
159
  }
158
160
  },
159
161
  flush(controller) {
160
162
  if (pendingLength) {
163
+ outputSize += pendingLength;
161
164
  controller.enqueue(concatChunks(pendingChunks, pendingLength));
162
165
  }
163
166
  }
164
167
  });
168
+ Object.defineProperty(this, "outputSize", {
169
+ get: () => outputSize
170
+ });
165
171
 
166
172
  function shiftChunk() {
167
173
  const result = new Uint8Array(chunkSize);
@@ -0,0 +1,35 @@
1
+ /*
2
+ Copyright (c) 2026 Gildas Lormeau. All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice,
8
+ this list of conditions and the following disclaimer.
9
+
10
+ 2. Redistributions in binary form must reproduce the above copyright
11
+ notice, this list of conditions and the following disclaimer in
12
+ the documentation and/or other materials provided with the distribution.
13
+
14
+ 3. The names of the authors may not be used to endorse or promote products
15
+ derived from this software without specific prior written permission.
16
+
17
+ THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
18
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
19
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
20
+ INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
21
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
23
+ OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
26
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+ */
28
+
29
+ export {
30
+ isErrorObject
31
+ };
32
+
33
+ function isErrorObject(error) {
34
+ return Boolean(error) && typeof error == "object";
35
+ }
@@ -0,0 +1,42 @@
1
+ /*
2
+ Copyright (c) 2026 Gildas Lormeau. All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice,
8
+ this list of conditions and the following disclaimer.
9
+
10
+ 2. Redistributions in binary form must reproduce the above copyright
11
+ notice, this list of conditions and the following disclaimer in
12
+ the documentation and/or other materials provided with the distribution.
13
+
14
+ 3. The names of the authors may not be used to endorse or promote products
15
+ derived from this software without specific prior written permission.
16
+
17
+ THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
18
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
19
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
20
+ INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
21
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
23
+ OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
26
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+ */
28
+
29
+
30
+ import { UNDEFINED_VALUE } from "../constants.js";
31
+
32
+ export { addWarning };
33
+
34
+ function addWarning(warnings, reason, filename) {
35
+ if (!warnings.some(warning => warning.reason == reason)) {
36
+ const warning = { reason };
37
+ if (filename !== UNDEFINED_VALUE) {
38
+ warning.filename = filename;
39
+ }
40
+ warnings.push(warning);
41
+ }
42
+ }
@@ -26,6 +26,6 @@
26
26
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
27
  */
28
28
 
29
- const VERSION = "2.11.4";
29
+ const VERSION = "2.13.0";
30
30
 
31
31
  export { VERSION };
@@ -28,7 +28,7 @@
28
28
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
29
  */
30
30
 
31
- /* global self, addEventListener, postMessage, ReadableStream, WritableStream, AbortController */
31
+ /* global self, addEventListener, postMessage, ReadableStream, WritableStream, AbortController, structuredClone */
32
32
 
33
33
  import {
34
34
  CODEC_DEFLATE,
@@ -46,10 +46,13 @@ import {
46
46
  import { getChunkSize } from "./configuration.js";
47
47
  import { toCompatibleReadable, toCompatibleWritable } from "./util/compatible-streams.js";
48
48
  import { toExactUint8Array } from "./util/array.js";
49
+ import { isErrorObject } from "./util/error.js";
50
+ import { UNDEFINED_VALUE } from "./constants.js";
49
51
  import { ensureCodecStreams } from "./codec-registry.js";
50
52
 
51
53
  const pendingPullMessages = new Map();
52
54
  const pendingDataMessages = new Map();
55
+ const errorValueSerializable = testErrorValueSerializable();
53
56
 
54
57
  let abortController, messageId = 0;
55
58
 
@@ -99,14 +102,20 @@ addEventListener(MESSAGE_EVENT_TYPE, ({ data }) => {
99
102
  postMessage({ type: MESSAGE_READY });
100
103
 
101
104
  async function init(message) {
102
- let codecStream, writable;
105
+ let codecStream, chunkStream, writable;
103
106
  try {
104
107
  const { options, config } = message;
105
108
  if (options.format) {
106
109
  try {
107
110
  await ensureCodecStreams(options.format, options.codecURI);
108
111
  } catch (error) {
109
- error.codecImportFailed = true;
112
+ if (isErrorObject(error)) {
113
+ try {
114
+ error.codecImportFailed = true;
115
+ } catch {
116
+ // ignored
117
+ }
118
+ }
110
119
  throw error;
111
120
  }
112
121
  }
@@ -162,11 +171,12 @@ async function init(message) {
162
171
  }
163
172
  }, strategy);
164
173
  codecStream = new CodecStream(options, config);
174
+ chunkStream = new ChunkStream(getChunkSize(config));
165
175
  abortController = new AbortController();
166
176
  const { signal } = abortController;
167
177
  await readable
168
178
  .pipeThrough(codecStream)
169
- .pipeThrough(new ChunkStream(getChunkSize(config)))
179
+ .pipeThrough(chunkStream)
170
180
  .pipeTo(writable, { signal, preventClose: true, preventAbort: true });
171
181
  await writable.getWriter().close();
172
182
  const {
@@ -183,7 +193,14 @@ async function init(message) {
183
193
  }
184
194
  });
185
195
  } catch (error) {
186
- error.outputSize = codecStream ? codecStream.outputSize : 0;
196
+ const outputSize = chunkStream ? chunkStream.outputSize : 0;
197
+ if (isErrorObject(error)) {
198
+ try {
199
+ error.outputSize = outputSize;
200
+ } catch {
201
+ // ignored
202
+ }
203
+ }
187
204
  if (writable && !writable.locked) {
188
205
  try {
189
206
  await writable.getWriter().close();
@@ -191,7 +208,7 @@ async function init(message) {
191
208
  // ignored
192
209
  }
193
210
  }
194
- sendErrorMessage(error);
211
+ sendErrorMessage(error, outputSize);
195
212
  }
196
213
  }
197
214
 
@@ -213,14 +230,34 @@ function sendMessage(message) {
213
230
  }
214
231
  }
215
232
 
216
- function sendErrorMessage(error = new Error("Unknown error")) {
217
- const { message, stack, code, name, outputSize, cause, codecImportFailed } = error;
218
- const errorData = { message, stack, code, name, outputSize };
233
+ function sendErrorMessage(errorValue, writtenSize) {
234
+ const { message, stack, code, name, outputSize, cause, codecImportFailed } = getErrorObject(errorValue);
235
+ const errorData = { message, stack, code, name, outputSize: outputSize === UNDEFINED_VALUE ? writtenSize : outputSize };
219
236
  if (cause) {
220
237
  errorData.cause = { name: cause.name, message: cause.message };
221
238
  }
222
239
  if (codecImportFailed) {
223
240
  errorData.codecImportFailed = true;
224
241
  }
242
+ if (errorValueSerializable) {
243
+ try {
244
+ postMessage({ error: errorData, errorValue: { value: errorValue } });
245
+ return;
246
+ } catch {
247
+ // ignored
248
+ }
249
+ }
225
250
  postMessage({ error: errorData });
226
251
  }
252
+
253
+ function getErrorObject(error = new Error("Unknown error")) {
254
+ return isErrorObject(error) ? error : new Error(String(error));
255
+ }
256
+
257
+ function testErrorValueSerializable() {
258
+ try {
259
+ return structuredClone(new Error()) instanceof Error;
260
+ } catch {
261
+ return false;
262
+ }
263
+ }