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 +5 -5
- package/dist/inflate-types.d.ts +15 -0
- package/dist/unzipit-worker.js +10 -5
- package/dist/unzipit-worker.min.js +1 -1
- package/dist/unzipit-worker.module.js +10 -5
- package/dist/unzipit.js +48 -26
- package/dist/unzipit.min.js +1 -1
- package/dist/unzipit.module.js +48 -26
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
# unzipit
|
|
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
|
[](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/
|
|
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@
|
|
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@
|
|
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
|
+
}
|
package/dist/unzipit-worker.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* unzipit@2.0.
|
|
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
|
-
|
|
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
|
|
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
|
+
/* 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
|
-
|
|
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
|
+
/* 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
|
-
|
|
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
|
-
|
|
144
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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) {
|
package/dist/unzipit.min.js
CHANGED
|
@@ -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});
|
package/dist/unzipit.module.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* unzipit@2.0.
|
|
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
|
-
|
|
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
|
-
|
|
138
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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) {
|