unzipit 2.0.1 → 2.0.3

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 CHANGED
@@ -1,7 +1,7 @@
1
- # unzipit.js
1
+ # unzipit
2
2
 
3
3
  <img src="./unzipit-no-anim.png" style="max-width: 640px">
4
-
4
+ <!-- cut here -->
5
5
  Random access unzip library for browser and node based JavaScript
6
6
 
7
7
  [![Build and Deploy](https://github.com/greggman/unzipit/actions/workflows/build_and_deploy.yml/badge.svg)](https://github.com/greggman/unzipit/actions/workflows/build_and_deploy.yml)
@@ -90,7 +90,7 @@ You can also pass a [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Bl
90
90
  or your own `Reader`
91
91
 
92
92
  For using without a builder/bundler grab `unzipit.min.js` or `unzipit.module.js` from
93
- the [`dist`](https://github.com/greggman/unzipit/tree/master/dist) folder and
93
+ the [`dist`](https://github.com/greggman/unzipit/tree/gh-pages/dist) folder and
94
94
  include with
95
95
 
96
96
  ```js
@@ -106,13 +106,13 @@ or
106
106
  or vs CDN
107
107
 
108
108
  ```js
109
- import * as unzipit from 'https://unpkg.com/unzipit@1.4.0/dist/unzipit.module.js';
109
+ import * as unzipit from 'https://unpkg.com/unzipit@2.0.1/dist/unzipit.module.js';
110
110
  ```
111
111
 
112
112
  or
113
113
 
114
114
  ```html
115
- <script src="https://unpkg.com/unzipit@1.4.0/dist/unzipit.js"></script>
115
+ <script src="https://unpkg.com/unzipit@2.0.1/dist/unzipit.js"></script>
116
116
  ```
117
117
  ## Node
118
118
 
@@ -0,0 +1,15 @@
1
+ export interface InflateRequestData {
2
+ id: number;
3
+ type?: string;
4
+ src: Uint8Array<ArrayBuffer> | Blob;
5
+ uncompressedSize: number;
6
+ }
7
+ export interface InflateRequestMessage {
8
+ type: 'inflate';
9
+ data: InflateRequestData;
10
+ }
11
+ export interface InflateResultMessage {
12
+ id: number;
13
+ data?: ArrayBuffer | Blob;
14
+ error?: string;
15
+ }
@@ -1,4 +1,4 @@
1
- /* unzipit@2.0.1, license MIT */
1
+ /* unzipit@2.0.3, license MIT */
2
2
  (function (factory) {
3
3
  typeof define === 'function' && define.amd ? define(factory) :
4
4
  factory();
@@ -47,18 +47,23 @@
47
47
  // then 50 blobs will be asked to be read at once.
48
48
  // If feels like that should happen at a higher level (user code)
49
49
  // or a lower level (the browser)?
50
- async function decompressRaw(src) {
50
+ async function decompressRaw(src, maxLimit) {
51
51
  const ds = new DecompressionStream('deflate-raw');
52
52
  const writer = ds.writable.getWriter();
53
53
  writer.write(src).then(() => writer.close()).catch(() => { });
54
54
  const chunks = [];
55
55
  const reader = ds.readable.getReader();
56
+ let seen = 0;
56
57
  for (;;) {
57
58
  const { done, value } = await reader.read();
58
59
  if (done) {
59
60
  break;
60
61
  }
61
62
  chunks.push(value);
63
+ seen += value.byteLength;
64
+ if (typeof maxLimit === 'number' && seen > maxLimit) {
65
+ throw new Error(`decompressed size exceeds limit: ${seen} > ${maxLimit}`);
66
+ }
62
67
  }
63
68
  const size = chunks.reduce((s, c) => s + c.byteLength, 0);
64
69
  const result = new Uint8Array(size);
@@ -75,7 +80,9 @@
75
80
  const srcData = isBlob(src)
76
81
  ? await readBlobAsUint8Array(src)
77
82
  : new Uint8Array(src);
78
- const dstData = await decompressRaw(srcData);
83
+ // Enforce declared uncompressedSize as the streaming limit
84
+ const limit = typeof req.uncompressedSize === 'number' ? req.uncompressedSize : undefined;
85
+ const dstData = await decompressRaw(srcData, limit);
79
86
  const transferables = [];
80
87
  let data;
81
88
  if (type) {
@@ -107,13 +114,11 @@
107
114
  const moduleId = 'node:worker_threads';
108
115
  import(moduleId).then(({ parentPort }) => {
109
116
  parentPort.on('message', (msg) => {
110
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
111
117
  handleMessage(msg, (m, t) => parentPort.postMessage(m, t));
112
118
  });
113
119
  });
114
120
  }
115
121
  else {
116
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
117
122
  const workerSelf = self;
118
123
  workerSelf.addEventListener('message', (e) => {
119
124
  handleMessage(e.data, (m, t) => workerSelf.postMessage(m, t));
@@ -1 +1 @@
1
- !function(e){"function"==typeof define&&define.amd?define(e):e()}(function(){"use strict";var e,r;async function n(e){const r=await function(e){return e.arrayBuffer?e.arrayBuffer():new Promise((r,n)=>{const t=new FileReader;t.addEventListener("loadend",()=>{r(t.result)}),t.addEventListener("error",n),t.readAsArrayBuffer(e)})}(e);return new Uint8Array(r)}async function t(e,r){const{id:t,src:o,type:s}=e;try{const e=(a=o,"undefined"!=typeof Blob&&a instanceof Blob?await n(o):new Uint8Array(o)),i=await async function(e){const r=new DecompressionStream("deflate-raw"),n=r.writable.getWriter();n.write(e).then(()=>n.close()).catch(()=>{});const t=[],o=r.readable.getReader();for(;;){const{done:e,value:r}=await o.read();if(e)break;t.push(r)}const s=t.reduce((e,r)=>e+r.byteLength,0),a=new Uint8Array(s);let i=0;for(const e of t)a.set(e,i),i+=e.byteLength;return a}(e),d=[];let c;s?c=new Blob([i],{type:s}):(c=i.buffer,d.push(c)),r({id:t,data:c},d)}catch(e){console.error(e),r({id:t,error:`${e}`})}var a}function o(e,r){const{type:n,data:o}=e;if("inflate"!==n)throw new Error("no handler for type: "+n);t(o,r)}if("undefined"!=typeof process&&!!(null===process||void 0===process?void 0:process.versions)&&void 0!==(null===(e=null===process||void 0===process?void 0:process.versions)||void 0===e?void 0:e.node)&&void 0===(null===(r=null===process||void 0===process?void 0:process.versions)||void 0===r?void 0:r.electron)){import("node:worker_threads").then(({parentPort:e})=>{e.on("message",r=>{o(r,(r,n)=>e.postMessage(r,n))})})}else{const e=self;e.addEventListener("message",r=>{o(r.data,(r,n)=>e.postMessage(r,n))}),e.postMessage("start")}});
1
+ !function(e){"function"==typeof define&&define.amd?define(e):e()}(function(){"use strict";var e,r;async function n(e){const r=await function(e){return e.arrayBuffer?e.arrayBuffer():new Promise((r,n)=>{const o=new FileReader;o.addEventListener("loadend",()=>{r(o.result)}),o.addEventListener("error",n),o.readAsArrayBuffer(e)})}(e);return new Uint8Array(r)}async function o(e,r){const{id:o,src:t,type:s}=e;try{const a=(i=t,"undefined"!=typeof Blob&&i instanceof Blob?await n(t):new Uint8Array(t)),d="number"==typeof e.uncompressedSize?e.uncompressedSize:void 0,c=await async function(e,r){const n=new DecompressionStream("deflate-raw"),o=n.writable.getWriter();o.write(e).then(()=>o.close()).catch(()=>{});const t=[],s=n.readable.getReader();let i=0;for(;;){const{done:e,value:n}=await s.read();if(e)break;if(t.push(n),i+=n.byteLength,"number"==typeof r&&i>r)throw new Error(`decompressed size exceeds limit: ${i} > ${r}`)}const a=t.reduce((e,r)=>e+r.byteLength,0),d=new Uint8Array(a);let c=0;for(const e of t)d.set(e,c),c+=e.byteLength;return d}(a,d),f=[];let u;s?u=new Blob([c],{type:s}):(u=c.buffer,f.push(u)),r({id:o,data:u},f)}catch(e){console.error(e),r({id:o,error:`${e}`})}var i}function t(e,r){const{type:n,data:t}=e;if("inflate"!==n)throw new Error("no handler for type: "+n);o(t,r)}if("undefined"!=typeof process&&!!(null===process||void 0===process?void 0:process.versions)&&void 0!==(null===(e=null===process||void 0===process?void 0:process.versions)||void 0===e?void 0:e.node)&&void 0===(null===(r=null===process||void 0===process?void 0:process.versions)||void 0===r?void 0:r.electron)){import("node:worker_threads").then(({parentPort:e})=>{e.on("message",r=>{t(r,(r,n)=>e.postMessage(r,n))})})}else{const e=self;e.addEventListener("message",r=>{t(r.data,(r,n)=>e.postMessage(r,n))}),e.postMessage("start")}});
@@ -1,4 +1,4 @@
1
- /* unzipit@2.0.1, license MIT */
1
+ /* unzipit@2.0.3, license MIT */
2
2
  var _a, _b;
3
3
  function readBlobAsArrayBuffer(blob) {
4
4
  if (blob.arrayBuffer) {
@@ -42,18 +42,23 @@ const isNode = (typeof process !== 'undefined') &&
42
42
  // then 50 blobs will be asked to be read at once.
43
43
  // If feels like that should happen at a higher level (user code)
44
44
  // or a lower level (the browser)?
45
- async function decompressRaw(src) {
45
+ async function decompressRaw(src, maxLimit) {
46
46
  const ds = new DecompressionStream('deflate-raw');
47
47
  const writer = ds.writable.getWriter();
48
48
  writer.write(src).then(() => writer.close()).catch(() => { });
49
49
  const chunks = [];
50
50
  const reader = ds.readable.getReader();
51
+ let seen = 0;
51
52
  for (;;) {
52
53
  const { done, value } = await reader.read();
53
54
  if (done) {
54
55
  break;
55
56
  }
56
57
  chunks.push(value);
58
+ seen += value.byteLength;
59
+ if (typeof maxLimit === 'number' && seen > maxLimit) {
60
+ throw new Error(`decompressed size exceeds limit: ${seen} > ${maxLimit}`);
61
+ }
57
62
  }
58
63
  const size = chunks.reduce((s, c) => s + c.byteLength, 0);
59
64
  const result = new Uint8Array(size);
@@ -70,7 +75,9 @@ async function inflate(req, postMessage) {
70
75
  const srcData = isBlob(src)
71
76
  ? await readBlobAsUint8Array(src)
72
77
  : new Uint8Array(src);
73
- const dstData = await decompressRaw(srcData);
78
+ // Enforce declared uncompressedSize as the streaming limit
79
+ const limit = typeof req.uncompressedSize === 'number' ? req.uncompressedSize : undefined;
80
+ const dstData = await decompressRaw(srcData, limit);
74
81
  const transferables = [];
75
82
  let data;
76
83
  if (type) {
@@ -102,13 +109,11 @@ if (isNode) {
102
109
  const moduleId = 'node:worker_threads';
103
110
  import(moduleId).then(({ parentPort }) => {
104
111
  parentPort.on('message', (msg) => {
105
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
106
112
  handleMessage(msg, (m, t) => parentPort.postMessage(m, t));
107
113
  });
108
114
  });
109
115
  }
110
116
  else {
111
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
112
117
  const workerSelf = self;
113
118
  workerSelf.addEventListener('message', (e) => {
114
119
  handleMessage(e.data, (m, t) => workerSelf.postMessage(m, t));
package/dist/unzipit.js CHANGED
@@ -1,4 +1,4 @@
1
- /* unzipit@2.0.1, license MIT */
1
+ /* unzipit@2.0.3, license MIT */
2
2
  (function (global, factory) {
3
3
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
4
4
  typeof define === 'function' && define.amd ? define(['exports'], factory) :
@@ -125,24 +125,30 @@
125
125
  // come in before a worker gets added to `workers`
126
126
  let numWorkers = 0;
127
127
  let canUseWorkers = true; // gets set to false if we can't start a worker
128
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
129
128
  const workers = [];
130
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
131
129
  const availableWorkers = [];
132
130
  const waitingForWorkerQueue = [];
133
131
  const currentlyProcessingIdToRequestMap = new Map();
134
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
135
132
  function handleResult(e) {
136
133
  makeWorkerAvailable(e.target);
137
134
  const { id, error, data } = e.data;
138
135
  const request = currentlyProcessingIdToRequestMap.get(id);
139
136
  currentlyProcessingIdToRequestMap.delete(id);
140
137
  if (error) {
141
- request.reject(error);
138
+ // The worker can only structured-clone the error as a string, so wrap it
139
+ // back into an Error to match the rejection type of the non-worker path.
140
+ request.reject(new Error(error));
141
+ return;
142
142
  }
143
- else {
144
- request.resolve(data);
143
+ // Verify that the decompressed size matches the declared uncompressedSize
144
+ // Treat declared size as authoritative metadata that must be verified.
145
+ const expected = request.uncompressedSize;
146
+ const actual = data instanceof ArrayBuffer ? data.byteLength : data === null || data === void 0 ? void 0 : data.size;
147
+ if (typeof expected === 'number' && typeof actual === 'number' && expected !== actual) {
148
+ request.reject(new Error(`decompressed size mismatch. declared: ${expected}, actual: ${actual}`));
149
+ return;
145
150
  }
151
+ request.resolve(data);
146
152
  }
147
153
  // Because Firefox uses non-standard onerror to signal an error.
148
154
  function startWorker(url) {
@@ -164,28 +170,25 @@
164
170
  const workerHelper = (function () {
165
171
  if (isNode) {
166
172
  return {
167
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
168
173
  async createWorker(url) {
169
174
  const moduleId = 'node:worker_threads';
170
175
  const { Worker } = await import(moduleId);
171
176
  return new Worker(url);
172
177
  },
173
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
174
178
  addEventListener(worker, fn) {
175
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
176
- worker.on('message', (data) => {
179
+ var _a;
180
+ (_a = worker.on) === null || _a === void 0 ? void 0 : _a.call(worker, 'message', (data) => {
177
181
  fn({ target: worker, data });
178
182
  });
179
183
  },
180
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
181
184
  async terminate(worker) {
182
- await worker.terminate();
185
+ var _a;
186
+ await ((_a = worker.terminate) === null || _a === void 0 ? void 0 : _a.call(worker));
183
187
  },
184
188
  };
185
189
  }
186
190
  else {
187
191
  return {
188
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
189
192
  async createWorker(url) {
190
193
  // I don't understand this security issue
191
194
  // Apparently there is some iframe setting or http header
@@ -228,23 +231,23 @@
228
231
  console.warn('workers will not be used');
229
232
  throw new Error('can not start workers');
230
233
  },
231
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
232
234
  addEventListener(worker, fn) {
233
- worker.addEventListener('message', fn);
235
+ var _a;
236
+ // The browser delivers a MessageEvent whose `target` is the worker
237
+ // and whose `data` is the InflateResultMessage, matching WorkerResultEvent.
238
+ (_a = worker.addEventListener) === null || _a === void 0 ? void 0 : _a.call(worker, 'message', fn);
234
239
  },
235
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
236
240
  async terminate(worker) {
237
- worker.terminate();
241
+ var _a;
242
+ await ((_a = worker.terminate) === null || _a === void 0 ? void 0 : _a.call(worker));
238
243
  },
239
244
  };
240
245
  }
241
246
  }());
242
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
243
247
  function makeWorkerAvailable(worker) {
244
248
  availableWorkers.push(worker);
245
249
  processWaitingForWorkerQueue();
246
250
  }
247
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
248
251
  async function getAvailableWorker() {
249
252
  if (availableWorkers.length === 0 && numWorkers < config.numWorkers) {
250
253
  ++numWorkers; // see comment at numWorkers declaration
@@ -261,7 +264,7 @@
261
264
  }
262
265
  return availableWorkers.pop();
263
266
  }
264
- async function decompressRaw(src) {
267
+ async function decompressRaw(src, limit) {
265
268
  const ds = new DecompressionStream('deflate-raw');
266
269
  const writer = ds.writable.getWriter();
267
270
  // Do not await the write — doing so before reading causes a deadlock when
@@ -269,12 +272,19 @@
269
272
  writer.write(src).then(() => writer.close()).catch(() => { });
270
273
  const chunks = [];
271
274
  const reader = ds.readable.getReader();
275
+ let seen = 0;
272
276
  for (;;) {
273
277
  const { done, value } = await reader.read();
274
278
  if (done) {
275
279
  break;
276
280
  }
281
+ // If this chunk would push us past the configured/declared limit, abort
282
+ const newSeen = seen + value.byteLength;
283
+ if (typeof limit === 'number' && newSeen > limit) {
284
+ throw new Error(`decompressed size exceeds limit: ${newSeen} > ${limit}`);
285
+ }
277
286
  chunks.push(value);
287
+ seen = newSeen;
278
288
  }
279
289
  const size = chunks.reduce((s, c) => s + c.byteLength, 0);
280
290
  const result = new Uint8Array(size);
@@ -288,9 +298,15 @@
288
298
  // @param {Uint8Array} src
289
299
  // @param {string} [type] mime-type
290
300
  // @returns {ArrayBuffer|Blob} ArrayBuffer if type is falsy or Blob otherwise.
291
- async function inflateRawLocal(src, type, resolve, reject) {
301
+ async function inflateRawLocal(src, uncompressedSize, type, resolve, reject) {
292
302
  try {
293
- const dst = await decompressRaw(src);
303
+ const limit = uncompressedSize;
304
+ const dst = await decompressRaw(src, limit);
305
+ const actual = dst.byteLength;
306
+ if (typeof uncompressedSize === 'number' && actual !== uncompressedSize) {
307
+ reject(new Error(`decompressed size mismatch. declared: ${uncompressedSize}, actual: ${actual}`));
308
+ return;
309
+ }
294
310
  resolve(type ? new Blob([dst], { type }) : dst.buffer);
295
311
  }
296
312
  catch (e) {
@@ -298,6 +314,7 @@
298
314
  }
299
315
  }
300
316
  async function processWaitingForWorkerQueue() {
317
+ var _a;
301
318
  if (waitingForWorkerQueue.length === 0) {
302
319
  return;
303
320
  }
@@ -327,7 +344,7 @@
327
344
  //if (!isBlob(src) && !isSharedArrayBuffer(src)) {
328
345
  // transferables.push(src);
329
346
  //}
330
- worker.postMessage({
347
+ (_a = worker.postMessage) === null || _a === void 0 ? void 0 : _a.call(worker, {
331
348
  type: 'inflate',
332
349
  data: {
333
350
  id,
@@ -346,9 +363,14 @@
346
363
  // will then be on the queue. But if we fail to make workers then there
347
364
  // are pending requests.
348
365
  while (waitingForWorkerQueue.length) {
349
- const { src, type, resolve, reject } = waitingForWorkerQueue.shift();
366
+ const { src, uncompressedSize, type, resolve, reject } = waitingForWorkerQueue.shift();
350
367
  const data = isBlob(src) ? await readBlobAsUint8Array(src) : src;
351
- inflateRawLocal(data, type, resolve, reject);
368
+ try {
369
+ await inflateRawLocal(data, uncompressedSize, type, resolve, reject);
370
+ }
371
+ catch (e) {
372
+ reject(e);
373
+ }
352
374
  }
353
375
  }
354
376
  function setOptions$1(options) {
@@ -1 +1 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).unzipit={})}(this,function(e){"use strict";var t,r;function n(e){return e.arrayBuffer?e.arrayBuffer():new Promise((t,r)=>{const n=new FileReader;n.addEventListener("loadend",()=>{t(n.result)}),n.addEventListener("error",r),n.readAsArrayBuffer(e)})}async function s(e){const t=await n(e);return new Uint8Array(t)}function o(e){return"undefined"!=typeof Blob&&e instanceof Blob}function i(e){return"undefined"!=typeof SharedArrayBuffer&&e instanceof SharedArrayBuffer}const a="undefined"!=typeof process&&!!(null===process||void 0===process?void 0:process.versions)&&void 0!==(null===(t=null===process||void 0===process?void 0:process.versions)||void 0===t?void 0:t.node)&&void 0===(null===(r=null===process||void 0===process?void 0:process.versions)||void 0===r?void 0:r.electron);class c{constructor(e){this.typedArray=e instanceof ArrayBuffer||i(e)?new Uint8Array(e):new Uint8Array(e.buffer,e.byteOffset,e.byteLength)}async getLength(){return this.typedArray.byteLength}async read(e,t){return new Uint8Array(this.typedArray.buffer,this.typedArray.byteOffset+e,t)}}class f{constructor(e){this.blob=e}async getLength(){return this.blob.size}async read(e,t){const r=this.blob.slice(e,e+t),s=await n(r);return new Uint8Array(s)}async sliceAsBlob(e,t,r=""){return this.blob.slice(e,e+t,r)}}const d={numWorkers:1,workerURL:"",useWorkers:!1};let l=0,u=0,h=!0;const w=[],p=[],y=[],m=new Map;function g(e){z(e.target);const{id:t,error:r,data:n}=e.data,s=m.get(t);m.delete(t),r?s.reject(r):s.resolve(n)}function b(e){return new Promise((t,r)=>{const n=new Worker(e);n.onmessage=e=>{"start"===e.data?(n.onerror=null,n.onmessage=null,t(n)):r(new Error(`unexpected message: ${e.data}`))},n.onerror=r})}const v=a?{async createWorker(e){const{Worker:t}=await import("node:worker_threads");return new t(e)},addEventListener(e,t){e.on("message",r=>{t({target:e,data:r})})},async terminate(e){await e.terminate()}}:{async createWorker(e){try{return await b(e)}catch(t){console.warn("could not load worker:",e)}let t;try{const r=await fetch(e,{mode:"cors"});if(!r.ok)throw new Error(`could not load: ${e}`);t=await r.text(),e=URL.createObjectURL(new Blob([t],{type:"application/javascript"}));const n=await b(e);return d.workerURL=e,n}catch(t){console.warn("could not load worker via fetch:",e)}if(void 0!==t)try{e=`data:application/javascript;base64,${btoa(t)}`;const r=await b(e);return d.workerURL=e,r}catch(e){console.warn("could not load worker via dataURI")}throw console.warn("workers will not be used"),new Error("can not start workers")},addEventListener(e,t){e.addEventListener("message",t)},async terminate(e){e.terminate()}};function z(e){p.push(e),E()}async function L(e,t,r,n){try{const n=await async function(e){const t=new DecompressionStream("deflate-raw"),r=t.writable.getWriter();r.write(e).then(()=>r.close()).catch(()=>{});const n=[],s=t.readable.getReader();for(;;){const{done:e,value:t}=await s.read();if(e)break;n.push(t)}const o=n.reduce((e,t)=>e+t.byteLength,0),i=new Uint8Array(o);let a=0;for(const e of n)i.set(e,a),a+=e.byteLength;return i}(e);r(t?new Blob([n],{type:t}):n.buffer)}catch(e){n(e)}}async function E(){if(0!==y.length){if(d.useWorkers&&h){const e=await async function(){if(0===p.length&&u<d.numWorkers){++u;try{const e=await v.createWorker(d.workerURL);w.push(e),p.push(e),v.addEventListener(e,g)}catch(e){h=!1}}return p.pop()}();if(h){if(e){if(0===y.length)return void z(e);const{id:t,src:r,uncompressedSize:n,type:s,resolve:o,reject:i}=y.shift();m.set(t,{id:t,src:r,uncompressedSize:n,type:s,resolve:o,reject:i});const a=[];e.postMessage({type:"inflate",data:{id:t,type:s,src:r,uncompressedSize:n}},a)}return}}for(;y.length;){const{src:e,type:t,resolve:r,reject:n}=y.shift();L(o(e)?await s(e):e,t,r,n)}}}function B(e,t,r){return new Promise((n,s)=>{y.push({src:e,uncompressedSize:t,type:r,resolve:n,reject:s,id:l++}),E()})}function S(e){e.splice(0,e.length)}class x{constructor(e,t){var r,n;this._reader=e,this._rawEntry=t,this.name=t.name,this.nameBytes=t.nameBytes,this.size=t.uncompressedSize,this.compressedSize=t.compressedSize,this.comment=t.comment,this.commentBytes=t.commentBytes,this.compressionMethod=t.compressionMethod,this.lastModDate=(r=t.lastModFileDate,n=t.lastModFileTime,new Date(1980+(r>>9&127),(r>>5&15)-1,31&r,n>>11&31,n>>5&63,2*(31&n),0)),this.isDirectory=0===t.uncompressedSize&&t.name.endsWith("/"),this.encrypted=!!(1&t.generalPurposeBitFlag),this.externalFileAttributes=t.externalFileAttributes,this.versionMadeBy=t.versionMadeBy}async blob(e="application/octet-stream"){return await async function(e,t,r){const{decompress:n,fileDataStart:s}=await D(e,t);if(!n){const n=await A(e,s,t.compressedSize,r);return o(n)?n:new Blob([n],{type:r})}const i=await A(e,s,t.compressedSize),a=await B((Uint8Array,i),t.uncompressedSize,r);return a}(this._reader,this._rawEntry,e)}async arrayBuffer(){return await async function(e,t){const{decompress:r,fileDataStart:n}=await D(e,t);if(!r){const r=await k(e,n,t.compressedSize);return 0===(s=r).byteOffset&&s.byteLength===s.buffer.byteLength?r.buffer:r.slice().buffer}var s;const o=await A(e,n,t.compressedSize);return await B((Uint8Array,o),t.uncompressedSize)}(this._reader,this._rawEntry)}async text(){const e=await this.arrayBuffer();return M(new Uint8Array(e))}async json(){const e=await this.text();return JSON.parse(e)}}async function k(e,t,r){return await e.read(t,r)}async function A(e,t,r,n){return e.sliceAsBlob?await e.sliceAsBlob(t,r,n):await e.read(t,r)}const $={unsigned:()=>0};function U(e,t){return e[t]+256*e[t+1]}function F(e,t){return e[t]+256*e[t+1]+65536*e[t+2]+16777216*e[t+3]}function O(e,t){return F(e,t)+4294967296*F(e,t+4)}const R=new TextDecoder;function M(e,t){return i(e.buffer)&&(e=new Uint8Array(e)),R.decode(e)}const W=117853008;async function N(e,t,r,n){const s=t-20,o=await k(e,s,20);if(F(o,0)!==W)throw new Error("invalid zip64 end of central directory locator signature");const i=O(o,8),a=await k(e,i,56);if(101075792!==F(a,0))throw new Error("invalid zip64 end of central directory record signature");const c=O(a,32),f=O(a,40);return j(e,O(a,48),f,c,r,n)}const T=33639248;async function j(e,t,r,n,s,o){let i=0;const a=await k(e,t,r),c=[];for(let e=0;e<n;++e){const e=a.subarray(i,i+46),t=F(e,0);if(t!==T)throw new Error(`invalid central directory file header signature: 0x${t.toString(16)}`);const r={versionMadeBy:U(e,4),versionNeededToExtract:U(e,6),generalPurposeBitFlag:U(e,8),compressionMethod:U(e,10),lastModFileTime:U(e,12),lastModFileDate:U(e,14),crc32:F(e,16),compressedSize:F(e,20),uncompressedSize:F(e,24),fileNameLength:U(e,28),extraFieldLength:U(e,30),fileCommentLength:U(e,32),internalFileAttributes:U(e,36),externalFileAttributes:F(e,38),relativeOffsetOfLocalHeader:F(e,42)};if(64&r.generalPurposeBitFlag)throw new Error("strong encryption is not supported");i+=46;const n=a.subarray(i,i+r.fileNameLength+r.extraFieldLength+r.fileCommentLength);r.generalPurposeBitFlag,r.nameBytes=n.slice(0,r.fileNameLength),r.name=M(r.nameBytes);const s=r.fileNameLength+r.extraFieldLength,o=n.slice(r.fileNameLength,s);r.extraFields=[];let f=0;for(;f<o.length-3;){const e=U(o,f+0),t=f+4,n=t+U(o,f+2);if(n>o.length)throw new Error("extra field length exceeds extra field buffer size");r.extraFields.push({id:e,data:o.slice(t,n)}),f=n}if(r.commentBytes=n.slice(s,s+r.fileCommentLength),r.comment=M(r.commentBytes),i+=n.length,4294967295===r.uncompressedSize||4294967295===r.compressedSize||4294967295===r.relativeOffsetOfLocalHeader){const e=r.extraFields.find(e=>1===e.id);if(!e)throw new Error("expected zip64 extended information extra field");const t=e.data;let n=0;if(4294967295===r.uncompressedSize){if(n+8>t.length)throw new Error("zip64 extended information extra field does not include uncompressed size");r.uncompressedSize=O(t,n),n+=8}if(4294967295===r.compressedSize){if(n+8>t.length)throw new Error("zip64 extended information extra field does not include compressed size");r.compressedSize=O(t,n),n+=8}if(4294967295===r.relativeOffsetOfLocalHeader){if(n+8>t.length)throw new Error("zip64 extended information extra field does not include relative header offset");r.relativeOffsetOfLocalHeader=O(t,n),n+=8}}const d=r.extraFields.find(e=>28789===e.id&&e.data.length>=6&&1===e.data[0]&&F(e.data,1),$.unsigned());if(d&&(r.fileName=M(d.data.slice(5))),0===r.compressionMethod){let e=r.uncompressedSize;if(1&r.generalPurposeBitFlag&&(e+=12),r.compressedSize!==e)throw new Error(`compressed size mismatch for stored file: ${r.compressedSize} != ${e}`)}c.push(r)}return{zip:{comment:s,commentBytes:o},entries:c.map(t=>new x(e,t))}}async function D(e,t){if(1&t.generalPurposeBitFlag)throw new Error("encrypted entries not supported");const r=await k(e,t.relativeOffsetOfLocalHeader,30),n=await e.getLength(),s=F(r,0);if(67324752!==s)throw new Error(`invalid local file header signature: 0x${s.toString(16)}`);const o=U(r,26),i=U(r,28),a=t.relativeOffsetOfLocalHeader+r.length+o+i;let c;if(0===t.compressionMethod)c=!1;else{if(8!==t.compressionMethod)throw new Error(`unsupported compression method: ${t.compressionMethod}`);c=!0}const f=a,d=f+t.compressedSize;if(0!==t.compressedSize&&d>n)throw new Error(`file data overflows file bounds: ${f} + ${t.compressedSize} > ${n}`);return{decompress:c,fileDataStart:f}}async function P(e){let t;if("undefined"!=typeof Blob&&e instanceof Blob)t=new f(e);else if(e instanceof ArrayBuffer||e&&e.buffer&&e.buffer instanceof ArrayBuffer)t=new c(e);else if(i(e)||i(e.buffer))t=new c(e);else if("string"==typeof e){const r=await fetch(e);if(!r.ok)throw new Error(`failed http request ${e}, status: ${r.status}: ${r.statusText}`);const n=await r.blob();t=new f(n)}else{if("function"!=typeof e.getLength||"function"!=typeof e.read)throw new Error("unsupported source type");t=e}const r=await t.getLength();if(r>Number.MAX_SAFE_INTEGER)throw new Error(`file too large. size: ${r}. Only file sizes up 4503599627370496 bytes are supported`);return await async function(e,t){const r=Math.min(65557,t),n=t-r,s=await k(e,n,r);for(let t=r-22;t>=0;--t){if(101010256!==F(s,t))continue;const r=new Uint8Array(s.buffer,s.byteOffset+t,s.byteLength-t),o=U(r,4);if(0!==o)throw new Error(`multi-volume zip files are not supported. This is volume: ${o}`);const i=U(r,10),a=F(r,12),c=F(r,16),f=U(r,20),d=r.length-22;if(f!==d)throw new Error(`invalid comment length. expected: ${d}, actual: ${f}`);const l=new Uint8Array(r.buffer,r.byteOffset+22,f),u=M(l);return 65535===i||4294967295===c?await N(e,n+t,u,l):await j(e,c,a,i,u,l)}throw new Error("could not find end of central directory. maybe not zip file")}(t,r)}e.ArrayBufferReader=c,e.BlobReader=f,e.HTTPRangeReader=class{constructor(e){this.url=e}async getLength(){if(void 0===this.length){const e=await fetch(this.url,{method:"HEAD"});if(!e.ok)throw new Error(`failed http request ${this.url}, status: ${e.status}: ${e.statusText}`);if(this.length=parseInt(e.headers.get("content-length")),Number.isNaN(this.length))throw Error("could not get length")}return this.length}async read(e,t){if(0===t)return new Uint8Array(0);const r=await fetch(this.url,{headers:{Range:`bytes=${e}-${e+t-1}`}});if(!r.ok)throw new Error(`failed http request ${this.url}, status: ${r.status} offset: ${e} size: ${t}: ${r.statusText}`);const n=await r.arrayBuffer();return new Uint8Array(n)}},e.ZipEntry=x,e.cleanup=function(){!async function(){for(const e of w)await v.terminate(e);S(w),S(p),S(y),m.clear(),u=0,h=!0}()},e.setOptions=function(e){!function(e){d.workerURL=e.workerURL||d.workerURL,e.workerURL&&(d.useWorkers=!0),d.useWorkers=void 0!==e.useWorkers?e.useWorkers:d.useWorkers,d.numWorkers=e.numWorkers||d.numWorkers}(e)},e.unzip=async function(e){const{zip:t,entries:r}=await P(e);return{zip:t,entries:Object.fromEntries(r.map(e=>[e.name,e]))}},e.unzipRaw=P});
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).unzipit={})}(this,function(e){"use strict";var t,r;function n(e){return e.arrayBuffer?e.arrayBuffer():new Promise((t,r)=>{const n=new FileReader;n.addEventListener("loadend",()=>{t(n.result)}),n.addEventListener("error",r),n.readAsArrayBuffer(e)})}async function o(e){const t=await n(e);return new Uint8Array(t)}function s(e){return"undefined"!=typeof Blob&&e instanceof Blob}function i(e){return"undefined"!=typeof SharedArrayBuffer&&e instanceof SharedArrayBuffer}const a="undefined"!=typeof process&&!!(null===process||void 0===process?void 0:process.versions)&&void 0!==(null===(t=null===process||void 0===process?void 0:process.versions)||void 0===t?void 0:t.node)&&void 0===(null===(r=null===process||void 0===process?void 0:process.versions)||void 0===r?void 0:r.electron);class c{constructor(e){this.typedArray=e instanceof ArrayBuffer||i(e)?new Uint8Array(e):new Uint8Array(e.buffer,e.byteOffset,e.byteLength)}async getLength(){return this.typedArray.byteLength}async read(e,t){return new Uint8Array(this.typedArray.buffer,this.typedArray.byteOffset+e,t)}}class d{constructor(e){this.blob=e}async getLength(){return this.blob.size}async read(e,t){const r=this.blob.slice(e,e+t),o=await n(r);return new Uint8Array(o)}async sliceAsBlob(e,t,r=""){return this.blob.slice(e,e+t,r)}}const f={numWorkers:1,workerURL:"",useWorkers:!1};let l=0,u=0,h=!0;const p=[],w=[],m=[],y=new Map;function g(e){z(e.target);const{id:t,error:r,data:n}=e.data,o=y.get(t);if(y.delete(t),r)return void o.reject(new Error(r));const s=o.uncompressedSize,i=n instanceof ArrayBuffer?n.byteLength:null==n?void 0:n.size;"number"!=typeof s||"number"!=typeof i||s===i?o.resolve(n):o.reject(new Error(`decompressed size mismatch. declared: ${s}, actual: ${i}`))}function b(e){return new Promise((t,r)=>{const n=new Worker(e);n.onmessage=e=>{"start"===e.data?(n.onerror=null,n.onmessage=null,t(n)):r(new Error(`unexpected message: ${e.data}`))},n.onerror=r})}const v=a?{async createWorker(e){const{Worker:t}=await import("node:worker_threads");return new t(e)},addEventListener(e,t){var r;null===(r=e.on)||void 0===r||r.call(e,"message",r=>{t({target:e,data:r})})},async terminate(e){var t;await(null===(t=e.terminate)||void 0===t?void 0:t.call(e))}}:{async createWorker(e){try{return await b(e)}catch(t){console.warn("could not load worker:",e)}let t;try{const r=await fetch(e,{mode:"cors"});if(!r.ok)throw new Error(`could not load: ${e}`);t=await r.text(),e=URL.createObjectURL(new Blob([t],{type:"application/javascript"}));const n=await b(e);return f.workerURL=e,n}catch(t){console.warn("could not load worker via fetch:",e)}if(void 0!==t)try{e=`data:application/javascript;base64,${btoa(t)}`;const r=await b(e);return f.workerURL=e,r}catch(e){console.warn("could not load worker via dataURI")}throw console.warn("workers will not be used"),new Error("can not start workers")},addEventListener(e,t){var r;null===(r=e.addEventListener)||void 0===r||r.call(e,"message",t)},async terminate(e){var t;await(null===(t=e.terminate)||void 0===t?void 0:t.call(e))}};function z(e){w.push(e),E()}async function L(e,t,r,n,o){try{const s=t,i=await async function(e,t){const r=new DecompressionStream("deflate-raw"),n=r.writable.getWriter();n.write(e).then(()=>n.close()).catch(()=>{});const o=[],s=r.readable.getReader();let i=0;for(;;){const{done:e,value:r}=await s.read();if(e)break;const n=i+r.byteLength;if("number"==typeof t&&n>t)throw new Error(`decompressed size exceeds limit: ${n} > ${t}`);o.push(r),i=n}const a=o.reduce((e,t)=>e+t.byteLength,0),c=new Uint8Array(a);let d=0;for(const e of o)c.set(e,d),d+=e.byteLength;return c}(e,s),a=i.byteLength;if("number"==typeof t&&a!==t)return void o(new Error(`decompressed size mismatch. declared: ${t}, actual: ${a}`));n(r?new Blob([i],{type:r}):i.buffer)}catch(e){o(e)}}async function E(){var e;if(0!==m.length){if(f.useWorkers&&h){const t=await async function(){if(0===w.length&&u<f.numWorkers){++u;try{const e=await v.createWorker(f.workerURL);p.push(e),w.push(e),v.addEventListener(e,g)}catch(e){h=!1}}return w.pop()}();if(h){if(t){if(0===m.length)return void z(t);const{id:r,src:n,uncompressedSize:o,type:s,resolve:i,reject:a}=m.shift();y.set(r,{id:r,src:n,uncompressedSize:o,type:s,resolve:i,reject:a});const c=[];null===(e=t.postMessage)||void 0===e||e.call(t,{type:"inflate",data:{id:r,type:s,src:n,uncompressedSize:o}},c)}return}}for(;m.length;){const{src:e,uncompressedSize:t,type:r,resolve:n,reject:i}=m.shift(),a=s(e)?await o(e):e;try{await L(a,t,r,n,i)}catch(e){i(e)}}}}function B(e,t,r){return new Promise((n,o)=>{m.push({src:e,uncompressedSize:t,type:r,resolve:n,reject:o,id:l++}),E()})}function S(e){e.splice(0,e.length)}class x{constructor(e,t){var r,n;this._reader=e,this._rawEntry=t,this.name=t.name,this.nameBytes=t.nameBytes,this.size=t.uncompressedSize,this.compressedSize=t.compressedSize,this.comment=t.comment,this.commentBytes=t.commentBytes,this.compressionMethod=t.compressionMethod,this.lastModDate=(r=t.lastModFileDate,n=t.lastModFileTime,new Date(1980+(r>>9&127),(r>>5&15)-1,31&r,n>>11&31,n>>5&63,2*(31&n),0)),this.isDirectory=0===t.uncompressedSize&&t.name.endsWith("/"),this.encrypted=!!(1&t.generalPurposeBitFlag),this.externalFileAttributes=t.externalFileAttributes,this.versionMadeBy=t.versionMadeBy}async blob(e="application/octet-stream"){return await async function(e,t,r){const{decompress:n,fileDataStart:o}=await D(e,t);if(!n){const n=await k(e,o,t.compressedSize,r);return s(n)?n:new Blob([n],{type:r})}const i=await k(e,o,t.compressedSize),a=await B((Uint8Array,i),t.uncompressedSize,r);return a}(this._reader,this._rawEntry,e)}async arrayBuffer(){return await async function(e,t){const{decompress:r,fileDataStart:n}=await D(e,t);if(!r){const r=await A(e,n,t.compressedSize);return 0===(o=r).byteOffset&&o.byteLength===o.buffer.byteLength?r.buffer:r.slice().buffer}var o;const s=await k(e,n,t.compressedSize);return await B((Uint8Array,s),t.uncompressedSize)}(this._reader,this._rawEntry)}async text(){const e=await this.arrayBuffer();return M(new Uint8Array(e))}async json(){const e=await this.text();return JSON.parse(e)}}async function A(e,t,r){return await e.read(t,r)}async function k(e,t,r,n){return e.sliceAsBlob?await e.sliceAsBlob(t,r,n):await e.read(t,r)}const $={unsigned:()=>0};function U(e,t){return e[t]+256*e[t+1]}function F(e,t){return e[t]+256*e[t+1]+65536*e[t+2]+16777216*e[t+3]}function O(e,t){return F(e,t)+4294967296*F(e,t+4)}const R=new TextDecoder;function M(e,t){return i(e.buffer)&&(e=new Uint8Array(e)),R.decode(e)}const W=117853008;async function N(e,t,r,n){const o=t-20,s=await A(e,o,20);if(F(s,0)!==W)throw new Error("invalid zip64 end of central directory locator signature");const i=O(s,8),a=await A(e,i,56);if(101075792!==F(a,0))throw new Error("invalid zip64 end of central directory record signature");const c=O(a,32),d=O(a,40);return j(e,O(a,48),d,c,r,n)}const T=33639248;async function j(e,t,r,n,o,s){let i=0;const a=await A(e,t,r),c=[];for(let e=0;e<n;++e){const e=a.subarray(i,i+46),t=F(e,0);if(t!==T)throw new Error(`invalid central directory file header signature: 0x${t.toString(16)}`);const r={versionMadeBy:U(e,4),versionNeededToExtract:U(e,6),generalPurposeBitFlag:U(e,8),compressionMethod:U(e,10),lastModFileTime:U(e,12),lastModFileDate:U(e,14),crc32:F(e,16),compressedSize:F(e,20),uncompressedSize:F(e,24),fileNameLength:U(e,28),extraFieldLength:U(e,30),fileCommentLength:U(e,32),internalFileAttributes:U(e,36),externalFileAttributes:F(e,38),relativeOffsetOfLocalHeader:F(e,42)};if(64&r.generalPurposeBitFlag)throw new Error("strong encryption is not supported");i+=46;const n=a.subarray(i,i+r.fileNameLength+r.extraFieldLength+r.fileCommentLength);r.generalPurposeBitFlag,r.nameBytes=n.slice(0,r.fileNameLength),r.name=M(r.nameBytes);const o=r.fileNameLength+r.extraFieldLength,s=n.slice(r.fileNameLength,o);r.extraFields=[];let d=0;for(;d<s.length-3;){const e=U(s,d+0),t=d+4,n=t+U(s,d+2);if(n>s.length)throw new Error("extra field length exceeds extra field buffer size");r.extraFields.push({id:e,data:s.slice(t,n)}),d=n}if(r.commentBytes=n.slice(o,o+r.fileCommentLength),r.comment=M(r.commentBytes),i+=n.length,4294967295===r.uncompressedSize||4294967295===r.compressedSize||4294967295===r.relativeOffsetOfLocalHeader){const e=r.extraFields.find(e=>1===e.id);if(!e)throw new Error("expected zip64 extended information extra field");const t=e.data;let n=0;if(4294967295===r.uncompressedSize){if(n+8>t.length)throw new Error("zip64 extended information extra field does not include uncompressed size");r.uncompressedSize=O(t,n),n+=8}if(4294967295===r.compressedSize){if(n+8>t.length)throw new Error("zip64 extended information extra field does not include compressed size");r.compressedSize=O(t,n),n+=8}if(4294967295===r.relativeOffsetOfLocalHeader){if(n+8>t.length)throw new Error("zip64 extended information extra field does not include relative header offset");r.relativeOffsetOfLocalHeader=O(t,n),n+=8}}const f=r.extraFields.find(e=>28789===e.id&&e.data.length>=6&&1===e.data[0]&&F(e.data,1),$.unsigned());if(f&&(r.fileName=M(f.data.slice(5))),0===r.compressionMethod){let e=r.uncompressedSize;if(1&r.generalPurposeBitFlag&&(e+=12),r.compressedSize!==e)throw new Error(`compressed size mismatch for stored file: ${r.compressedSize} != ${e}`)}c.push(r)}return{zip:{comment:o,commentBytes:s},entries:c.map(t=>new x(e,t))}}async function D(e,t){if(1&t.generalPurposeBitFlag)throw new Error("encrypted entries not supported");const r=await A(e,t.relativeOffsetOfLocalHeader,30),n=await e.getLength(),o=F(r,0);if(67324752!==o)throw new Error(`invalid local file header signature: 0x${o.toString(16)}`);const s=U(r,26),i=U(r,28),a=t.relativeOffsetOfLocalHeader+r.length+s+i;let c;if(0===t.compressionMethod)c=!1;else{if(8!==t.compressionMethod)throw new Error(`unsupported compression method: ${t.compressionMethod}`);c=!0}const d=a,f=d+t.compressedSize;if(0!==t.compressedSize&&f>n)throw new Error(`file data overflows file bounds: ${d} + ${t.compressedSize} > ${n}`);return{decompress:c,fileDataStart:d}}async function P(e){let t;if("undefined"!=typeof Blob&&e instanceof Blob)t=new d(e);else if(e instanceof ArrayBuffer||e&&e.buffer&&e.buffer instanceof ArrayBuffer)t=new c(e);else if(i(e)||i(e.buffer))t=new c(e);else if("string"==typeof e){const r=await fetch(e);if(!r.ok)throw new Error(`failed http request ${e}, status: ${r.status}: ${r.statusText}`);const n=await r.blob();t=new d(n)}else{if("function"!=typeof e.getLength||"function"!=typeof e.read)throw new Error("unsupported source type");t=e}const r=await t.getLength();if(r>Number.MAX_SAFE_INTEGER)throw new Error(`file too large. size: ${r}. Only file sizes up 4503599627370496 bytes are supported`);return await async function(e,t){const r=Math.min(65557,t),n=t-r,o=await A(e,n,r);for(let t=r-22;t>=0;--t){if(101010256!==F(o,t))continue;const r=new Uint8Array(o.buffer,o.byteOffset+t,o.byteLength-t),s=U(r,4);if(0!==s)throw new Error(`multi-volume zip files are not supported. This is volume: ${s}`);const i=U(r,10),a=F(r,12),c=F(r,16),d=U(r,20),f=r.length-22;if(d!==f)throw new Error(`invalid comment length. expected: ${f}, actual: ${d}`);const l=new Uint8Array(r.buffer,r.byteOffset+22,d),u=M(l);return 65535===i||4294967295===c?await N(e,n+t,u,l):await j(e,c,a,i,u,l)}throw new Error("could not find end of central directory. maybe not zip file")}(t,r)}e.ArrayBufferReader=c,e.BlobReader=d,e.HTTPRangeReader=class{constructor(e){this.url=e}async getLength(){if(void 0===this.length){const e=await fetch(this.url,{method:"HEAD"});if(!e.ok)throw new Error(`failed http request ${this.url}, status: ${e.status}: ${e.statusText}`);if(this.length=parseInt(e.headers.get("content-length")),Number.isNaN(this.length))throw Error("could not get length")}return this.length}async read(e,t){if(0===t)return new Uint8Array(0);const r=await fetch(this.url,{headers:{Range:`bytes=${e}-${e+t-1}`}});if(!r.ok)throw new Error(`failed http request ${this.url}, status: ${r.status} offset: ${e} size: ${t}: ${r.statusText}`);const n=await r.arrayBuffer();return new Uint8Array(n)}},e.ZipEntry=x,e.cleanup=function(){!async function(){for(const e of p)await v.terminate(e);S(p),S(w),S(m),y.clear(),u=0,h=!0}()},e.setOptions=function(e){!function(e){f.workerURL=e.workerURL||f.workerURL,e.workerURL&&(f.useWorkers=!0),f.useWorkers=void 0!==e.useWorkers?e.useWorkers:f.useWorkers,f.numWorkers=e.numWorkers||f.numWorkers}(e)},e.unzip=async function(e){const{zip:t,entries:r}=await P(e);return{zip:t,entries:Object.fromEntries(r.map(e=>[e.name,e]))}},e.unzipRaw=P});
@@ -1,4 +1,4 @@
1
- /* unzipit@2.0.1, license MIT */
1
+ /* unzipit@2.0.3, license MIT */
2
2
  var _a, _b;
3
3
  function readBlobAsArrayBuffer(blob) {
4
4
  if (blob.arrayBuffer) {
@@ -119,24 +119,30 @@ let nextId = 0;
119
119
  // come in before a worker gets added to `workers`
120
120
  let numWorkers = 0;
121
121
  let canUseWorkers = true; // gets set to false if we can't start a worker
122
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
123
122
  const workers = [];
124
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
125
123
  const availableWorkers = [];
126
124
  const waitingForWorkerQueue = [];
127
125
  const currentlyProcessingIdToRequestMap = new Map();
128
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
129
126
  function handleResult(e) {
130
127
  makeWorkerAvailable(e.target);
131
128
  const { id, error, data } = e.data;
132
129
  const request = currentlyProcessingIdToRequestMap.get(id);
133
130
  currentlyProcessingIdToRequestMap.delete(id);
134
131
  if (error) {
135
- request.reject(error);
132
+ // The worker can only structured-clone the error as a string, so wrap it
133
+ // back into an Error to match the rejection type of the non-worker path.
134
+ request.reject(new Error(error));
135
+ return;
136
136
  }
137
- else {
138
- request.resolve(data);
137
+ // Verify that the decompressed size matches the declared uncompressedSize
138
+ // Treat declared size as authoritative metadata that must be verified.
139
+ const expected = request.uncompressedSize;
140
+ const actual = data instanceof ArrayBuffer ? data.byteLength : data === null || data === void 0 ? void 0 : data.size;
141
+ if (typeof expected === 'number' && typeof actual === 'number' && expected !== actual) {
142
+ request.reject(new Error(`decompressed size mismatch. declared: ${expected}, actual: ${actual}`));
143
+ return;
139
144
  }
145
+ request.resolve(data);
140
146
  }
141
147
  // Because Firefox uses non-standard onerror to signal an error.
142
148
  function startWorker(url) {
@@ -158,28 +164,25 @@ function startWorker(url) {
158
164
  const workerHelper = (function () {
159
165
  if (isNode) {
160
166
  return {
161
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
162
167
  async createWorker(url) {
163
168
  const moduleId = 'node:worker_threads';
164
169
  const { Worker } = await import(moduleId);
165
170
  return new Worker(url);
166
171
  },
167
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
168
172
  addEventListener(worker, fn) {
169
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
170
- worker.on('message', (data) => {
173
+ var _a;
174
+ (_a = worker.on) === null || _a === void 0 ? void 0 : _a.call(worker, 'message', (data) => {
171
175
  fn({ target: worker, data });
172
176
  });
173
177
  },
174
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
175
178
  async terminate(worker) {
176
- await worker.terminate();
179
+ var _a;
180
+ await ((_a = worker.terminate) === null || _a === void 0 ? void 0 : _a.call(worker));
177
181
  },
178
182
  };
179
183
  }
180
184
  else {
181
185
  return {
182
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
183
186
  async createWorker(url) {
184
187
  // I don't understand this security issue
185
188
  // Apparently there is some iframe setting or http header
@@ -222,23 +225,23 @@ const workerHelper = (function () {
222
225
  console.warn('workers will not be used');
223
226
  throw new Error('can not start workers');
224
227
  },
225
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
226
228
  addEventListener(worker, fn) {
227
- worker.addEventListener('message', fn);
229
+ var _a;
230
+ // The browser delivers a MessageEvent whose `target` is the worker
231
+ // and whose `data` is the InflateResultMessage, matching WorkerResultEvent.
232
+ (_a = worker.addEventListener) === null || _a === void 0 ? void 0 : _a.call(worker, 'message', fn);
228
233
  },
229
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
230
234
  async terminate(worker) {
231
- worker.terminate();
235
+ var _a;
236
+ await ((_a = worker.terminate) === null || _a === void 0 ? void 0 : _a.call(worker));
232
237
  },
233
238
  };
234
239
  }
235
240
  }());
236
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
237
241
  function makeWorkerAvailable(worker) {
238
242
  availableWorkers.push(worker);
239
243
  processWaitingForWorkerQueue();
240
244
  }
241
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
242
245
  async function getAvailableWorker() {
243
246
  if (availableWorkers.length === 0 && numWorkers < config.numWorkers) {
244
247
  ++numWorkers; // see comment at numWorkers declaration
@@ -255,7 +258,7 @@ async function getAvailableWorker() {
255
258
  }
256
259
  return availableWorkers.pop();
257
260
  }
258
- async function decompressRaw(src) {
261
+ async function decompressRaw(src, limit) {
259
262
  const ds = new DecompressionStream('deflate-raw');
260
263
  const writer = ds.writable.getWriter();
261
264
  // Do not await the write — doing so before reading causes a deadlock when
@@ -263,12 +266,19 @@ async function decompressRaw(src) {
263
266
  writer.write(src).then(() => writer.close()).catch(() => { });
264
267
  const chunks = [];
265
268
  const reader = ds.readable.getReader();
269
+ let seen = 0;
266
270
  for (;;) {
267
271
  const { done, value } = await reader.read();
268
272
  if (done) {
269
273
  break;
270
274
  }
275
+ // If this chunk would push us past the configured/declared limit, abort
276
+ const newSeen = seen + value.byteLength;
277
+ if (typeof limit === 'number' && newSeen > limit) {
278
+ throw new Error(`decompressed size exceeds limit: ${newSeen} > ${limit}`);
279
+ }
271
280
  chunks.push(value);
281
+ seen = newSeen;
272
282
  }
273
283
  const size = chunks.reduce((s, c) => s + c.byteLength, 0);
274
284
  const result = new Uint8Array(size);
@@ -282,9 +292,15 @@ async function decompressRaw(src) {
282
292
  // @param {Uint8Array} src
283
293
  // @param {string} [type] mime-type
284
294
  // @returns {ArrayBuffer|Blob} ArrayBuffer if type is falsy or Blob otherwise.
285
- async function inflateRawLocal(src, type, resolve, reject) {
295
+ async function inflateRawLocal(src, uncompressedSize, type, resolve, reject) {
286
296
  try {
287
- const dst = await decompressRaw(src);
297
+ const limit = uncompressedSize;
298
+ const dst = await decompressRaw(src, limit);
299
+ const actual = dst.byteLength;
300
+ if (typeof uncompressedSize === 'number' && actual !== uncompressedSize) {
301
+ reject(new Error(`decompressed size mismatch. declared: ${uncompressedSize}, actual: ${actual}`));
302
+ return;
303
+ }
288
304
  resolve(type ? new Blob([dst], { type }) : dst.buffer);
289
305
  }
290
306
  catch (e) {
@@ -292,6 +308,7 @@ async function inflateRawLocal(src, type, resolve, reject) {
292
308
  }
293
309
  }
294
310
  async function processWaitingForWorkerQueue() {
311
+ var _a;
295
312
  if (waitingForWorkerQueue.length === 0) {
296
313
  return;
297
314
  }
@@ -321,7 +338,7 @@ async function processWaitingForWorkerQueue() {
321
338
  //if (!isBlob(src) && !isSharedArrayBuffer(src)) {
322
339
  // transferables.push(src);
323
340
  //}
324
- worker.postMessage({
341
+ (_a = worker.postMessage) === null || _a === void 0 ? void 0 : _a.call(worker, {
325
342
  type: 'inflate',
326
343
  data: {
327
344
  id,
@@ -340,9 +357,14 @@ async function processWaitingForWorkerQueue() {
340
357
  // will then be on the queue. But if we fail to make workers then there
341
358
  // are pending requests.
342
359
  while (waitingForWorkerQueue.length) {
343
- const { src, type, resolve, reject } = waitingForWorkerQueue.shift();
360
+ const { src, uncompressedSize, type, resolve, reject } = waitingForWorkerQueue.shift();
344
361
  const data = isBlob(src) ? await readBlobAsUint8Array(src) : src;
345
- inflateRawLocal(data, type, resolve, reject);
362
+ try {
363
+ await inflateRawLocal(data, uncompressedSize, type, resolve, reject);
364
+ }
365
+ catch (e) {
366
+ reject(e);
367
+ }
346
368
  }
347
369
  }
348
370
  function setOptions$1(options) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unzipit",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "description": "random access unzip library for JavaScript",
5
5
  "type": "module",
6
6
  "main": "dist/unzipit.module.js",