unzipit 2.0.0 → 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 +10 -6
- package/dist/inflate-types.d.ts +15 -0
- package/dist/unzipit-worker.js +12 -6
- package/dist/unzipit-worker.min.js +1 -1
- package/dist/unzipit-worker.module.js +12 -6
- package/dist/unzipit.js +50 -27
- package/dist/unzipit.min.js +1 -1
- package/dist/unzipit.module.js +50 -27
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
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
|
-
](https://github.com/greggman/unzipit/actions/workflows/build_and_deploy.yml)
|
|
8
8
|
[[Live Tests](https://greggman.github.io/unzipit/test/)]
|
|
9
9
|
|
|
10
10
|
* Less than 5k gzipped without workers, Less than 6k with.
|
|
@@ -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
|
|
|
@@ -279,6 +279,7 @@ entries.forEach(entry => {
|
|
|
279
279
|
Some libraries both zip and unzip.
|
|
280
280
|
IMO those should be separate libraries as there is little if any code to share between
|
|
281
281
|
both. Plenty of projects only need to do one or the other.
|
|
282
|
+
[Here's the sister library for zip](https://greggman.github.io/zipup/).
|
|
282
283
|
|
|
283
284
|
Similarly inflate and deflate libraries should be separate from zip, unzip libraries.
|
|
284
285
|
You need one or the other not both. See zlib as an example.
|
|
@@ -336,6 +337,9 @@ manages them. They don't count as part of the JavaScript heap.
|
|
|
336
337
|
In node, the examples with the file readers will only read the header and whatever entries' contents
|
|
337
338
|
you ask for so similarly you can avoid having everything in memory except the things you read.
|
|
338
339
|
|
|
340
|
+
# Zip Creation
|
|
341
|
+
|
|
342
|
+
see: [zipup](https://greggman.github.io/zipup/)
|
|
339
343
|
|
|
340
344
|
# API
|
|
341
345
|
|
|
@@ -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) {
|
|
@@ -104,15 +111,14 @@
|
|
|
104
111
|
if (isNode) {
|
|
105
112
|
// Use dynamic import so this works in both CJS and ESM contexts.
|
|
106
113
|
// The import of a built-in resolves before any messages can arrive.
|
|
107
|
-
|
|
114
|
+
const moduleId = 'node:worker_threads';
|
|
115
|
+
import(moduleId).then(({ parentPort }) => {
|
|
108
116
|
parentPort.on('message', (msg) => {
|
|
109
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
110
117
|
handleMessage(msg, (m, t) => parentPort.postMessage(m, t));
|
|
111
118
|
});
|
|
112
119
|
});
|
|
113
120
|
}
|
|
114
121
|
else {
|
|
115
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
116
122
|
const workerSelf = self;
|
|
117
123
|
workerSelf.addEventListener('message', (e) => {
|
|
118
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) {
|
|
@@ -99,15 +106,14 @@ function handleMessage(msg, postMessage) {
|
|
|
99
106
|
if (isNode) {
|
|
100
107
|
// Use dynamic import so this works in both CJS and ESM contexts.
|
|
101
108
|
// The import of a built-in resolves before any messages can arrive.
|
|
102
|
-
|
|
109
|
+
const moduleId = 'node:worker_threads';
|
|
110
|
+
import(moduleId).then(({ parentPort }) => {
|
|
103
111
|
parentPort.on('message', (msg) => {
|
|
104
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
105
112
|
handleMessage(msg, (m, t) => parentPort.postMessage(m, t));
|
|
106
113
|
});
|
|
107
114
|
});
|
|
108
115
|
}
|
|
109
116
|
else {
|
|
110
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
111
117
|
const workerSelf = self;
|
|
112
118
|
workerSelf.addEventListener('message', (e) => {
|
|
113
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,27 +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
|
-
const
|
|
174
|
+
const moduleId = 'node:worker_threads';
|
|
175
|
+
const { Worker } = await import(moduleId);
|
|
170
176
|
return new Worker(url);
|
|
171
177
|
},
|
|
172
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
173
178
|
addEventListener(worker, fn) {
|
|
174
|
-
|
|
175
|
-
worker.on('message', (data) => {
|
|
179
|
+
var _a;
|
|
180
|
+
(_a = worker.on) === null || _a === void 0 ? void 0 : _a.call(worker, 'message', (data) => {
|
|
176
181
|
fn({ target: worker, data });
|
|
177
182
|
});
|
|
178
183
|
},
|
|
179
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
180
184
|
async terminate(worker) {
|
|
181
|
-
|
|
185
|
+
var _a;
|
|
186
|
+
await ((_a = worker.terminate) === null || _a === void 0 ? void 0 : _a.call(worker));
|
|
182
187
|
},
|
|
183
188
|
};
|
|
184
189
|
}
|
|
185
190
|
else {
|
|
186
191
|
return {
|
|
187
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
188
192
|
async createWorker(url) {
|
|
189
193
|
// I don't understand this security issue
|
|
190
194
|
// Apparently there is some iframe setting or http header
|
|
@@ -227,23 +231,23 @@
|
|
|
227
231
|
console.warn('workers will not be used');
|
|
228
232
|
throw new Error('can not start workers');
|
|
229
233
|
},
|
|
230
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
231
234
|
addEventListener(worker, fn) {
|
|
232
|
-
|
|
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);
|
|
233
239
|
},
|
|
234
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
235
240
|
async terminate(worker) {
|
|
236
|
-
|
|
241
|
+
var _a;
|
|
242
|
+
await ((_a = worker.terminate) === null || _a === void 0 ? void 0 : _a.call(worker));
|
|
237
243
|
},
|
|
238
244
|
};
|
|
239
245
|
}
|
|
240
246
|
}());
|
|
241
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
242
247
|
function makeWorkerAvailable(worker) {
|
|
243
248
|
availableWorkers.push(worker);
|
|
244
249
|
processWaitingForWorkerQueue();
|
|
245
250
|
}
|
|
246
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
247
251
|
async function getAvailableWorker() {
|
|
248
252
|
if (availableWorkers.length === 0 && numWorkers < config.numWorkers) {
|
|
249
253
|
++numWorkers; // see comment at numWorkers declaration
|
|
@@ -260,7 +264,7 @@
|
|
|
260
264
|
}
|
|
261
265
|
return availableWorkers.pop();
|
|
262
266
|
}
|
|
263
|
-
async function decompressRaw(src) {
|
|
267
|
+
async function decompressRaw(src, limit) {
|
|
264
268
|
const ds = new DecompressionStream('deflate-raw');
|
|
265
269
|
const writer = ds.writable.getWriter();
|
|
266
270
|
// Do not await the write — doing so before reading causes a deadlock when
|
|
@@ -268,12 +272,19 @@
|
|
|
268
272
|
writer.write(src).then(() => writer.close()).catch(() => { });
|
|
269
273
|
const chunks = [];
|
|
270
274
|
const reader = ds.readable.getReader();
|
|
275
|
+
let seen = 0;
|
|
271
276
|
for (;;) {
|
|
272
277
|
const { done, value } = await reader.read();
|
|
273
278
|
if (done) {
|
|
274
279
|
break;
|
|
275
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
|
+
}
|
|
276
286
|
chunks.push(value);
|
|
287
|
+
seen = newSeen;
|
|
277
288
|
}
|
|
278
289
|
const size = chunks.reduce((s, c) => s + c.byteLength, 0);
|
|
279
290
|
const result = new Uint8Array(size);
|
|
@@ -287,9 +298,15 @@
|
|
|
287
298
|
// @param {Uint8Array} src
|
|
288
299
|
// @param {string} [type] mime-type
|
|
289
300
|
// @returns {ArrayBuffer|Blob} ArrayBuffer if type is falsy or Blob otherwise.
|
|
290
|
-
async function inflateRawLocal(src, type, resolve, reject) {
|
|
301
|
+
async function inflateRawLocal(src, uncompressedSize, type, resolve, reject) {
|
|
291
302
|
try {
|
|
292
|
-
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
|
+
}
|
|
293
310
|
resolve(type ? new Blob([dst], { type }) : dst.buffer);
|
|
294
311
|
}
|
|
295
312
|
catch (e) {
|
|
@@ -297,6 +314,7 @@
|
|
|
297
314
|
}
|
|
298
315
|
}
|
|
299
316
|
async function processWaitingForWorkerQueue() {
|
|
317
|
+
var _a;
|
|
300
318
|
if (waitingForWorkerQueue.length === 0) {
|
|
301
319
|
return;
|
|
302
320
|
}
|
|
@@ -326,7 +344,7 @@
|
|
|
326
344
|
//if (!isBlob(src) && !isSharedArrayBuffer(src)) {
|
|
327
345
|
// transferables.push(src);
|
|
328
346
|
//}
|
|
329
|
-
worker.postMessage({
|
|
347
|
+
(_a = worker.postMessage) === null || _a === void 0 ? void 0 : _a.call(worker, {
|
|
330
348
|
type: 'inflate',
|
|
331
349
|
data: {
|
|
332
350
|
id,
|
|
@@ -345,9 +363,14 @@
|
|
|
345
363
|
// will then be on the queue. But if we fail to make workers then there
|
|
346
364
|
// are pending requests.
|
|
347
365
|
while (waitingForWorkerQueue.length) {
|
|
348
|
-
const { src, type, resolve, reject } = waitingForWorkerQueue.shift();
|
|
366
|
+
const { src, uncompressedSize, type, resolve, reject } = waitingForWorkerQueue.shift();
|
|
349
367
|
const data = isBlob(src) ? await readBlobAsUint8Array(src) : src;
|
|
350
|
-
|
|
368
|
+
try {
|
|
369
|
+
await inflateRawLocal(data, uncompressedSize, type, resolve, reject);
|
|
370
|
+
}
|
|
371
|
+
catch (e) {
|
|
372
|
+
reject(e);
|
|
373
|
+
}
|
|
351
374
|
}
|
|
352
375
|
}
|
|
353
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("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,27 +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
|
-
const
|
|
168
|
+
const moduleId = 'node:worker_threads';
|
|
169
|
+
const { Worker } = await import(moduleId);
|
|
164
170
|
return new Worker(url);
|
|
165
171
|
},
|
|
166
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
167
172
|
addEventListener(worker, fn) {
|
|
168
|
-
|
|
169
|
-
worker.on('message', (data) => {
|
|
173
|
+
var _a;
|
|
174
|
+
(_a = worker.on) === null || _a === void 0 ? void 0 : _a.call(worker, 'message', (data) => {
|
|
170
175
|
fn({ target: worker, data });
|
|
171
176
|
});
|
|
172
177
|
},
|
|
173
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
174
178
|
async terminate(worker) {
|
|
175
|
-
|
|
179
|
+
var _a;
|
|
180
|
+
await ((_a = worker.terminate) === null || _a === void 0 ? void 0 : _a.call(worker));
|
|
176
181
|
},
|
|
177
182
|
};
|
|
178
183
|
}
|
|
179
184
|
else {
|
|
180
185
|
return {
|
|
181
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
182
186
|
async createWorker(url) {
|
|
183
187
|
// I don't understand this security issue
|
|
184
188
|
// Apparently there is some iframe setting or http header
|
|
@@ -221,23 +225,23 @@ const workerHelper = (function () {
|
|
|
221
225
|
console.warn('workers will not be used');
|
|
222
226
|
throw new Error('can not start workers');
|
|
223
227
|
},
|
|
224
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
225
228
|
addEventListener(worker, fn) {
|
|
226
|
-
|
|
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);
|
|
227
233
|
},
|
|
228
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
229
234
|
async terminate(worker) {
|
|
230
|
-
|
|
235
|
+
var _a;
|
|
236
|
+
await ((_a = worker.terminate) === null || _a === void 0 ? void 0 : _a.call(worker));
|
|
231
237
|
},
|
|
232
238
|
};
|
|
233
239
|
}
|
|
234
240
|
}());
|
|
235
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
236
241
|
function makeWorkerAvailable(worker) {
|
|
237
242
|
availableWorkers.push(worker);
|
|
238
243
|
processWaitingForWorkerQueue();
|
|
239
244
|
}
|
|
240
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
241
245
|
async function getAvailableWorker() {
|
|
242
246
|
if (availableWorkers.length === 0 && numWorkers < config.numWorkers) {
|
|
243
247
|
++numWorkers; // see comment at numWorkers declaration
|
|
@@ -254,7 +258,7 @@ async function getAvailableWorker() {
|
|
|
254
258
|
}
|
|
255
259
|
return availableWorkers.pop();
|
|
256
260
|
}
|
|
257
|
-
async function decompressRaw(src) {
|
|
261
|
+
async function decompressRaw(src, limit) {
|
|
258
262
|
const ds = new DecompressionStream('deflate-raw');
|
|
259
263
|
const writer = ds.writable.getWriter();
|
|
260
264
|
// Do not await the write — doing so before reading causes a deadlock when
|
|
@@ -262,12 +266,19 @@ async function decompressRaw(src) {
|
|
|
262
266
|
writer.write(src).then(() => writer.close()).catch(() => { });
|
|
263
267
|
const chunks = [];
|
|
264
268
|
const reader = ds.readable.getReader();
|
|
269
|
+
let seen = 0;
|
|
265
270
|
for (;;) {
|
|
266
271
|
const { done, value } = await reader.read();
|
|
267
272
|
if (done) {
|
|
268
273
|
break;
|
|
269
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
|
+
}
|
|
270
280
|
chunks.push(value);
|
|
281
|
+
seen = newSeen;
|
|
271
282
|
}
|
|
272
283
|
const size = chunks.reduce((s, c) => s + c.byteLength, 0);
|
|
273
284
|
const result = new Uint8Array(size);
|
|
@@ -281,9 +292,15 @@ async function decompressRaw(src) {
|
|
|
281
292
|
// @param {Uint8Array} src
|
|
282
293
|
// @param {string} [type] mime-type
|
|
283
294
|
// @returns {ArrayBuffer|Blob} ArrayBuffer if type is falsy or Blob otherwise.
|
|
284
|
-
async function inflateRawLocal(src, type, resolve, reject) {
|
|
295
|
+
async function inflateRawLocal(src, uncompressedSize, type, resolve, reject) {
|
|
285
296
|
try {
|
|
286
|
-
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
|
+
}
|
|
287
304
|
resolve(type ? new Blob([dst], { type }) : dst.buffer);
|
|
288
305
|
}
|
|
289
306
|
catch (e) {
|
|
@@ -291,6 +308,7 @@ async function inflateRawLocal(src, type, resolve, reject) {
|
|
|
291
308
|
}
|
|
292
309
|
}
|
|
293
310
|
async function processWaitingForWorkerQueue() {
|
|
311
|
+
var _a;
|
|
294
312
|
if (waitingForWorkerQueue.length === 0) {
|
|
295
313
|
return;
|
|
296
314
|
}
|
|
@@ -320,7 +338,7 @@ async function processWaitingForWorkerQueue() {
|
|
|
320
338
|
//if (!isBlob(src) && !isSharedArrayBuffer(src)) {
|
|
321
339
|
// transferables.push(src);
|
|
322
340
|
//}
|
|
323
|
-
worker.postMessage({
|
|
341
|
+
(_a = worker.postMessage) === null || _a === void 0 ? void 0 : _a.call(worker, {
|
|
324
342
|
type: 'inflate',
|
|
325
343
|
data: {
|
|
326
344
|
id,
|
|
@@ -339,9 +357,14 @@ async function processWaitingForWorkerQueue() {
|
|
|
339
357
|
// will then be on the queue. But if we fail to make workers then there
|
|
340
358
|
// are pending requests.
|
|
341
359
|
while (waitingForWorkerQueue.length) {
|
|
342
|
-
const { src, type, resolve, reject } = waitingForWorkerQueue.shift();
|
|
360
|
+
const { src, uncompressedSize, type, resolve, reject } = waitingForWorkerQueue.shift();
|
|
343
361
|
const data = isBlob(src) ? await readBlobAsUint8Array(src) : src;
|
|
344
|
-
|
|
362
|
+
try {
|
|
363
|
+
await inflateRawLocal(data, uncompressedSize, type, resolve, reject);
|
|
364
|
+
}
|
|
365
|
+
catch (e) {
|
|
366
|
+
reject(e);
|
|
367
|
+
}
|
|
345
368
|
}
|
|
346
369
|
}
|
|
347
370
|
function setOptions$1(options) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "unzipit",
|
|
3
|
-
"version": "2.0.
|
|
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",
|
|
@@ -10,11 +10,12 @@
|
|
|
10
10
|
"node": ">=18"
|
|
11
11
|
},
|
|
12
12
|
"scripts": {
|
|
13
|
-
"build": "npm run build-js && npm run build-types && node build/copy.js node_modules/chai/index.js test/chai.js && node build/copy.js node_modules/mocha/mocha.js test/mocha.js && node build/copy.js node_modules/mocha/mocha.css test/mocha.css",
|
|
13
|
+
"build": "npm run build-js && npm run build-types && npm run build-ts-test && node build/copy.js node_modules/chai/index.js test/chai.js && node build/copy.js node_modules/mocha/mocha.js test/mocha.js && node build/copy.js node_modules/mocha/mocha.css test/mocha.css",
|
|
14
14
|
"build-ci": "npm run build && node build/prep-for-deploy.js",
|
|
15
15
|
"build-normal": "rollup -c",
|
|
16
16
|
"build-js": "rollup -c",
|
|
17
17
|
"build-types": "tsc -p tsconfig.build.json",
|
|
18
|
+
"build-ts-test": "rollup -c test/ts/rollup.test.config.js",
|
|
18
19
|
"eslint": "eslint src/**/*.ts test/index.js test/node-test.js test/puppeteer.js test/tests/**/*.js",
|
|
19
20
|
"test": "npm run test-node && npm run test-browser",
|
|
20
21
|
"test-node": "mocha test/node-test.js",
|