@zklogic/unzipper 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitattributes +1 -0
- package/LICENSE +25 -0
- package/README.md +407 -0
- package/eslint.config.mjs +29 -0
- package/lib/BufferStream.js +19 -0
- package/lib/Decrypt.js +90 -0
- package/lib/NoopStream.js +14 -0
- package/lib/Open/directory.js +243 -0
- package/lib/Open/index.js +141 -0
- package/lib/Open/unzip.js +120 -0
- package/lib/PullStream.js +139 -0
- package/lib/extract.js +62 -0
- package/lib/parse.js +288 -0
- package/lib/parseBuffer.js +55 -0
- package/lib/parseDateTime.js +13 -0
- package/lib/parseExtraField.js +40 -0
- package/lib/parseOne.js +54 -0
- package/package.json +65 -0
- package/unzip.js +5 -0
package/lib/parse.js
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
const util = require('util');
|
|
2
|
+
const zlib = require('zlib');
|
|
3
|
+
const Stream = require('stream');
|
|
4
|
+
const PullStream = require('./PullStream');
|
|
5
|
+
const NoopStream = require('./NoopStream');
|
|
6
|
+
const BufferStream = require('./BufferStream');
|
|
7
|
+
const parseExtraField = require('./parseExtraField');
|
|
8
|
+
const parseDateTime = require('./parseDateTime');
|
|
9
|
+
const pipeline = Stream.pipeline;
|
|
10
|
+
const parseBuffer = require('./parseBuffer');
|
|
11
|
+
|
|
12
|
+
const endDirectorySignature = Buffer.alloc(4);
|
|
13
|
+
endDirectorySignature.writeUInt32LE(0x06054b50, 0);
|
|
14
|
+
|
|
15
|
+
function Parse(opts) {
|
|
16
|
+
if (!(this instanceof Parse)) {
|
|
17
|
+
return new Parse(opts);
|
|
18
|
+
}
|
|
19
|
+
const self = this;
|
|
20
|
+
self._opts = opts || { verbose: false };
|
|
21
|
+
|
|
22
|
+
PullStream.call(self, self._opts);
|
|
23
|
+
self.on('finish', function() {
|
|
24
|
+
self.emit('end');
|
|
25
|
+
self.emit('close');
|
|
26
|
+
});
|
|
27
|
+
self._readRecord().catch(function(e) {
|
|
28
|
+
if (!self.__emittedError || self.__emittedError !== e)
|
|
29
|
+
self.emit('error', e);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
util.inherits(Parse, PullStream);
|
|
34
|
+
|
|
35
|
+
Parse.prototype._readRecord = function () {
|
|
36
|
+
const self = this;
|
|
37
|
+
|
|
38
|
+
return self.pull(4).then(function(data) {
|
|
39
|
+
if (data.length === 0)
|
|
40
|
+
return;
|
|
41
|
+
|
|
42
|
+
const signature = data.readUInt32LE(0);
|
|
43
|
+
|
|
44
|
+
if (signature === 0x34327243) {
|
|
45
|
+
return self._readCrxHeader();
|
|
46
|
+
}
|
|
47
|
+
if (signature === 0x04034b50) {
|
|
48
|
+
return self._readFile();
|
|
49
|
+
}
|
|
50
|
+
else if (signature === 0x02014b50) {
|
|
51
|
+
self.reachedCD = true;
|
|
52
|
+
return self._readCentralDirectoryFileHeader();
|
|
53
|
+
}
|
|
54
|
+
else if (signature === 0x06054b50) {
|
|
55
|
+
return self._readEndOfCentralDirectoryRecord();
|
|
56
|
+
}
|
|
57
|
+
else if (self.reachedCD) {
|
|
58
|
+
// _readEndOfCentralDirectoryRecord expects the EOCD
|
|
59
|
+
// signature to be consumed so set includeEof=true
|
|
60
|
+
const includeEof = true;
|
|
61
|
+
return self.pull(endDirectorySignature, includeEof).then(function() {
|
|
62
|
+
return self._readEndOfCentralDirectoryRecord();
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
else
|
|
66
|
+
self.emit('error', new Error('invalid signature: 0x' + signature.toString(16)));
|
|
67
|
+
}).then((function(loop) {
|
|
68
|
+
if(loop) {
|
|
69
|
+
return self._readRecord();
|
|
70
|
+
}
|
|
71
|
+
}));
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
Parse.prototype._readCrxHeader = function() {
|
|
75
|
+
const self = this;
|
|
76
|
+
return self.pull(12).then(function(data) {
|
|
77
|
+
self.crxHeader = parseBuffer.parse(data, [
|
|
78
|
+
['version', 4],
|
|
79
|
+
['pubKeyLength', 4],
|
|
80
|
+
['signatureLength', 4],
|
|
81
|
+
]);
|
|
82
|
+
return self.pull(self.crxHeader.pubKeyLength + self.crxHeader.signatureLength);
|
|
83
|
+
}).then(function(data) {
|
|
84
|
+
self.crxHeader.publicKey = data.slice(0, self.crxHeader.pubKeyLength);
|
|
85
|
+
self.crxHeader.signature = data.slice(self.crxHeader.pubKeyLength);
|
|
86
|
+
self.emit('crx-header', self.crxHeader);
|
|
87
|
+
return true;
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
Parse.prototype._readFile = function () {
|
|
92
|
+
const self = this;
|
|
93
|
+
return self.pull(26).then(function(data) {
|
|
94
|
+
const vars = parseBuffer.parse(data, [
|
|
95
|
+
['versionsNeededToExtract', 2],
|
|
96
|
+
['flags', 2],
|
|
97
|
+
['compressionMethod', 2],
|
|
98
|
+
['lastModifiedTime', 2],
|
|
99
|
+
['lastModifiedDate', 2],
|
|
100
|
+
['crc32', 4],
|
|
101
|
+
['compressedSize', 4],
|
|
102
|
+
['uncompressedSize', 4],
|
|
103
|
+
['fileNameLength', 2],
|
|
104
|
+
['extraFieldLength', 2],
|
|
105
|
+
]);
|
|
106
|
+
|
|
107
|
+
vars.lastModifiedDateTime = parseDateTime(vars.lastModifiedDate, vars.lastModifiedTime);
|
|
108
|
+
|
|
109
|
+
if (self.crxHeader) vars.crxHeader = self.crxHeader;
|
|
110
|
+
|
|
111
|
+
return self.pull(vars.fileNameLength).then(function(fileNameBuffer) {
|
|
112
|
+
const fileName = fileNameBuffer.toString('utf8');
|
|
113
|
+
const entry = Stream.PassThrough();
|
|
114
|
+
let __autodraining = false;
|
|
115
|
+
|
|
116
|
+
entry.autodrain = function() {
|
|
117
|
+
__autodraining = true;
|
|
118
|
+
const draining = entry.pipe(NoopStream());
|
|
119
|
+
draining.promise = function() {
|
|
120
|
+
return new Promise(function(resolve, reject) {
|
|
121
|
+
draining.on('finish', resolve);
|
|
122
|
+
draining.on('error', reject);
|
|
123
|
+
});
|
|
124
|
+
};
|
|
125
|
+
return draining;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
entry.buffer = function() {
|
|
129
|
+
return BufferStream(entry);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
entry.path = fileName;
|
|
133
|
+
entry.props = {};
|
|
134
|
+
entry.props.path = fileName;
|
|
135
|
+
entry.props.pathBuffer = fileNameBuffer;
|
|
136
|
+
entry.props.flags = {
|
|
137
|
+
"isUnicode": (vars.flags & 0x800) != 0
|
|
138
|
+
};
|
|
139
|
+
entry.type = (vars.uncompressedSize === 0 && /[/\\]$/.test(fileName)) ? 'Directory' : 'File';
|
|
140
|
+
|
|
141
|
+
if (self._opts.verbose) {
|
|
142
|
+
if (entry.type === 'Directory') {
|
|
143
|
+
console.log(' creating:', fileName);
|
|
144
|
+
} else if (entry.type === 'File') {
|
|
145
|
+
if (vars.compressionMethod === 0) {
|
|
146
|
+
console.log(' extracting:', fileName);
|
|
147
|
+
} else {
|
|
148
|
+
console.log(' inflating:', fileName);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return self.pull(vars.extraFieldLength).then(function(extraField) {
|
|
154
|
+
const extra = parseExtraField(extraField, vars);
|
|
155
|
+
|
|
156
|
+
entry.vars = vars;
|
|
157
|
+
entry.extra = extra;
|
|
158
|
+
|
|
159
|
+
if (self._opts.forceStream) {
|
|
160
|
+
self.push(entry);
|
|
161
|
+
} else {
|
|
162
|
+
self.emit('entry', entry);
|
|
163
|
+
|
|
164
|
+
if (self._readableState.pipesCount || (self._readableState.pipes && self._readableState.pipes.length))
|
|
165
|
+
self.push(entry);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (self._opts.verbose)
|
|
169
|
+
console.log({
|
|
170
|
+
filename:fileName,
|
|
171
|
+
vars: vars,
|
|
172
|
+
extra: extra
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
const fileSizeKnown = !(vars.flags & 0x08) || vars.compressedSize > 0;
|
|
176
|
+
let eof;
|
|
177
|
+
|
|
178
|
+
entry.__autodraining = __autodraining; // expose __autodraining for test purposes
|
|
179
|
+
const inflater = (vars.compressionMethod && !__autodraining) ? zlib.createInflateRaw() : Stream.PassThrough();
|
|
180
|
+
|
|
181
|
+
if (fileSizeKnown) {
|
|
182
|
+
entry.size = vars.uncompressedSize;
|
|
183
|
+
eof = vars.compressedSize;
|
|
184
|
+
} else {
|
|
185
|
+
eof = Buffer.alloc(4);
|
|
186
|
+
eof.writeUInt32LE(0x08074b50, 0);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return new Promise(function(resolve, reject) {
|
|
190
|
+
pipeline(
|
|
191
|
+
self.stream(eof),
|
|
192
|
+
inflater,
|
|
193
|
+
entry,
|
|
194
|
+
function (err) {
|
|
195
|
+
if (err) {
|
|
196
|
+
return reject(err);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return fileSizeKnown ? resolve(fileSizeKnown) : self._processDataDescriptor(entry).then(resolve).catch(reject);
|
|
200
|
+
}
|
|
201
|
+
);
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
Parse.prototype._processDataDescriptor = function (entry) {
|
|
209
|
+
const self = this;
|
|
210
|
+
return self.pull(16).then(function(data) {
|
|
211
|
+
const vars = parseBuffer.parse(data, [
|
|
212
|
+
['dataDescriptorSignature', 4],
|
|
213
|
+
['crc32', 4],
|
|
214
|
+
['compressedSize', 4],
|
|
215
|
+
['uncompressedSize', 4],
|
|
216
|
+
]);
|
|
217
|
+
|
|
218
|
+
entry.size = vars.uncompressedSize;
|
|
219
|
+
return true;
|
|
220
|
+
});
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
Parse.prototype._readCentralDirectoryFileHeader = function () {
|
|
224
|
+
const self = this;
|
|
225
|
+
return self.pull(42).then(function(data) {
|
|
226
|
+
const vars = parseBuffer.parse(data, [
|
|
227
|
+
['versionMadeBy', 2],
|
|
228
|
+
['versionsNeededToExtract', 2],
|
|
229
|
+
['flags', 2],
|
|
230
|
+
['compressionMethod', 2],
|
|
231
|
+
['lastModifiedTime', 2],
|
|
232
|
+
['lastModifiedDate', 2],
|
|
233
|
+
['crc32', 4],
|
|
234
|
+
['compressedSize', 4],
|
|
235
|
+
['uncompressedSize', 4],
|
|
236
|
+
['fileNameLength', 2],
|
|
237
|
+
['extraFieldLength', 2],
|
|
238
|
+
['fileCommentLength', 2],
|
|
239
|
+
['diskNumber', 2],
|
|
240
|
+
['internalFileAttributes', 2],
|
|
241
|
+
['externalFileAttributes', 4],
|
|
242
|
+
['offsetToLocalFileHeader', 4],
|
|
243
|
+
]);
|
|
244
|
+
|
|
245
|
+
return self.pull(vars.fileNameLength).then(function(fileName) {
|
|
246
|
+
vars.fileName = fileName.toString('utf8');
|
|
247
|
+
return self.pull(vars.extraFieldLength);
|
|
248
|
+
})
|
|
249
|
+
.then(function() {
|
|
250
|
+
return self.pull(vars.fileCommentLength);
|
|
251
|
+
})
|
|
252
|
+
.then(function() {
|
|
253
|
+
return true;
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
Parse.prototype._readEndOfCentralDirectoryRecord = function() {
|
|
259
|
+
const self = this;
|
|
260
|
+
return self.pull(18).then(function(data) {
|
|
261
|
+
|
|
262
|
+
const vars = parseBuffer.parse(data, [
|
|
263
|
+
['diskNumber', 2],
|
|
264
|
+
['diskStart', 2],
|
|
265
|
+
['numberOfRecordsOnDisk', 2],
|
|
266
|
+
['numberOfRecords', 2],
|
|
267
|
+
['sizeOfCentralDirectory', 4],
|
|
268
|
+
['offsetToStartOfCentralDirectory', 4],
|
|
269
|
+
['commentLength', 2],
|
|
270
|
+
]);
|
|
271
|
+
|
|
272
|
+
return self.pull(vars.commentLength).then(function() {
|
|
273
|
+
self.end();
|
|
274
|
+
self.push(null);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
});
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
Parse.prototype.promise = function() {
|
|
281
|
+
const self = this;
|
|
282
|
+
return new Promise(function(resolve, reject) {
|
|
283
|
+
self.on('finish', resolve);
|
|
284
|
+
self.on('error', reject);
|
|
285
|
+
});
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
module.exports = Parse;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const parseUIntLE = function(buffer, offset, size) {
|
|
2
|
+
let result;
|
|
3
|
+
switch(size) {
|
|
4
|
+
case 1:
|
|
5
|
+
result = buffer.readUInt8(offset);
|
|
6
|
+
break;
|
|
7
|
+
case 2:
|
|
8
|
+
result = buffer.readUInt16LE(offset);
|
|
9
|
+
break;
|
|
10
|
+
case 4:
|
|
11
|
+
result = buffer.readUInt32LE(offset);
|
|
12
|
+
break;
|
|
13
|
+
case 8:
|
|
14
|
+
result = Number(buffer.readBigUInt64LE(offset));
|
|
15
|
+
break;
|
|
16
|
+
default:
|
|
17
|
+
throw new Error('Unsupported UInt LE size!');
|
|
18
|
+
}
|
|
19
|
+
return result;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Parses sequential unsigned little endian numbers from the head of the passed buffer according to
|
|
24
|
+
* the specified format passed. If the buffer is not large enough to satisfy the full format,
|
|
25
|
+
* null values will be assigned to the remaining keys.
|
|
26
|
+
* @param {*} buffer The buffer to sequentially extract numbers from.
|
|
27
|
+
* @param {*} format Expected format to follow when extrcting values from the buffer. A list of list entries
|
|
28
|
+
* with the following structure:
|
|
29
|
+
* [
|
|
30
|
+
* [
|
|
31
|
+
* <key>, // Name of the key to assign the extracted number to.
|
|
32
|
+
* <size> // The size in bytes of the number to extract. possible values are 1, 2, 4, 8.
|
|
33
|
+
* ],
|
|
34
|
+
* ...
|
|
35
|
+
* ]
|
|
36
|
+
* @returns An object with keys set to their associated extracted values.
|
|
37
|
+
*/
|
|
38
|
+
const parse = function(buffer, format) {
|
|
39
|
+
const result = {};
|
|
40
|
+
let offset = 0;
|
|
41
|
+
for(const [key, size] of format) {
|
|
42
|
+
if(buffer.length >= offset + size) {
|
|
43
|
+
result[key] = parseUIntLE(buffer, offset, size);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
result[key] = null;
|
|
47
|
+
}
|
|
48
|
+
offset += size;
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
module.exports = {
|
|
54
|
+
parse
|
|
55
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Dates in zip file entries are stored as DosDateTime
|
|
2
|
+
// Spec is here: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-dosdatetimetofiletime
|
|
3
|
+
|
|
4
|
+
module.exports = function parseDateTime(date, time) {
|
|
5
|
+
const day = date & 0x1F;
|
|
6
|
+
const month = date >> 5 & 0x0F;
|
|
7
|
+
const year = (date >> 9 & 0x7F) + 1980;
|
|
8
|
+
const seconds = time ? (time & 0x1F) * 2 : 0;
|
|
9
|
+
const minutes = time ? (time >> 5) & 0x3F : 0;
|
|
10
|
+
const hours = time ? (time >> 11): 0;
|
|
11
|
+
|
|
12
|
+
return new Date(Date.UTC(year, month-1, day, hours, minutes, seconds));
|
|
13
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const parseBuffer = require('./parseBuffer');
|
|
2
|
+
|
|
3
|
+
module.exports = function(extraField, vars) {
|
|
4
|
+
let extra;
|
|
5
|
+
// Find the ZIP64 header, if present.
|
|
6
|
+
while(!extra && extraField && extraField.length) {
|
|
7
|
+
const candidateExtra = parseBuffer.parse(extraField, [
|
|
8
|
+
['signature', 2],
|
|
9
|
+
['partSize', 2],
|
|
10
|
+
]);
|
|
11
|
+
|
|
12
|
+
if(candidateExtra.signature === 0x0001) {
|
|
13
|
+
// parse buffer based on data in ZIP64 central directory; order is important!
|
|
14
|
+
const fieldsToExpect = [];
|
|
15
|
+
if (vars.uncompressedSize === 0xffffffff) fieldsToExpect.push(['uncompressedSize', 8]);
|
|
16
|
+
if (vars.compressedSize === 0xffffffff) fieldsToExpect.push(['compressedSize', 8]);
|
|
17
|
+
if (vars.offsetToLocalFileHeader === 0xffffffff) fieldsToExpect.push(['offsetToLocalFileHeader', 8]);
|
|
18
|
+
|
|
19
|
+
// slice off the 4 bytes for signature and partSize
|
|
20
|
+
extra = parseBuffer.parse(extraField.slice(4), fieldsToExpect);
|
|
21
|
+
} else {
|
|
22
|
+
// Advance the buffer to the next part.
|
|
23
|
+
// The total size of this part is the 4 byte header + partsize.
|
|
24
|
+
extraField = extraField.slice(candidateExtra.partSize + 4);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
extra = extra || {};
|
|
29
|
+
|
|
30
|
+
if (vars.compressedSize === 0xffffffff)
|
|
31
|
+
vars.compressedSize = extra.compressedSize;
|
|
32
|
+
|
|
33
|
+
if (vars.uncompressedSize === 0xffffffff)
|
|
34
|
+
vars.uncompressedSize= extra.uncompressedSize;
|
|
35
|
+
|
|
36
|
+
if (vars.offsetToLocalFileHeader === 0xffffffff)
|
|
37
|
+
vars.offsetToLocalFileHeader = extra.offsetToLocalFileHeader;
|
|
38
|
+
|
|
39
|
+
return extra;
|
|
40
|
+
};
|
package/lib/parseOne.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const Stream = require('stream');
|
|
2
|
+
const Parse = require('./parse');
|
|
3
|
+
const duplexer2 = require('duplexer2');
|
|
4
|
+
const BufferStream = require('./BufferStream');
|
|
5
|
+
|
|
6
|
+
function parseOne(match, opts) {
|
|
7
|
+
const inStream = Stream.PassThrough({objectMode:true});
|
|
8
|
+
const outStream = Stream.PassThrough();
|
|
9
|
+
const transform = Stream.Transform({objectMode:true});
|
|
10
|
+
const re = match instanceof RegExp ? match : (match && new RegExp(match));
|
|
11
|
+
let found;
|
|
12
|
+
|
|
13
|
+
transform._transform = function(entry, e, cb) {
|
|
14
|
+
if (found || (re && !re.exec(entry.path))) {
|
|
15
|
+
entry.autodrain();
|
|
16
|
+
return cb();
|
|
17
|
+
} else {
|
|
18
|
+
found = true;
|
|
19
|
+
out.emit('entry', entry);
|
|
20
|
+
entry.on('error', function(e) {
|
|
21
|
+
outStream.emit('error', e);
|
|
22
|
+
});
|
|
23
|
+
entry.pipe(outStream)
|
|
24
|
+
.on('error', function(err) {
|
|
25
|
+
cb(err);
|
|
26
|
+
})
|
|
27
|
+
.on('finish', function(d) {
|
|
28
|
+
cb(null, d);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
inStream.pipe(Parse(opts))
|
|
34
|
+
.on('error', function(err) {
|
|
35
|
+
outStream.emit('error', err);
|
|
36
|
+
})
|
|
37
|
+
.pipe(transform)
|
|
38
|
+
.on('error', Object) // Silence error as its already addressed in transform
|
|
39
|
+
.on('finish', function() {
|
|
40
|
+
if (!found)
|
|
41
|
+
outStream.emit('error', new Error('PATTERN_NOT_FOUND'));
|
|
42
|
+
else
|
|
43
|
+
outStream.end();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const out = duplexer2(inStream, outStream);
|
|
47
|
+
out.buffer = function() {
|
|
48
|
+
return BufferStream(outStream);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
return out;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = parseOne;
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zklogic/unzipper",
|
|
3
|
+
"version": "0.13.0",
|
|
4
|
+
"description": "Unzip cross-platform streaming API ",
|
|
5
|
+
"author": {
|
|
6
|
+
"email": "arfankhalil@hotmail.com",
|
|
7
|
+
"name": "Arfan Khalil Mughal",
|
|
8
|
+
"url": "https://github.com/ArfanKhalilMughal"
|
|
9
|
+
},
|
|
10
|
+
"contributors": [
|
|
11
|
+
{
|
|
12
|
+
"name": "Ziggy Jonsson",
|
|
13
|
+
"email": "ziggy.jonsson.nyc@gmail.com"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"name": "Evan Oxfeld",
|
|
17
|
+
"email": "eoxfeld@gmail.com"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"name": "Joe Ferner",
|
|
21
|
+
"email": "joe.ferner@nearinfinity.com"
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "https://github.com/ArfanKhalilMughal/node-unzipper.git"
|
|
27
|
+
},
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"bluebird": "~3.7.2",
|
|
31
|
+
"duplexer2": "~0.1.4",
|
|
32
|
+
"fs-extra": "^11.2.0",
|
|
33
|
+
"graceful-fs": "^4.2.2"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@eslint/js": "^9.2.0",
|
|
37
|
+
"aws-sdk": "^2.1636.0",
|
|
38
|
+
"@aws-sdk/client-s3": "^3.0.0",
|
|
39
|
+
"dirdiff": ">= 0.0.1 < 1",
|
|
40
|
+
"eslint": "^9.2.0",
|
|
41
|
+
"globals": "^15.2.0",
|
|
42
|
+
"iconv-lite": "^0.4.24",
|
|
43
|
+
"request": "^2.88.0",
|
|
44
|
+
"stream-buffers": ">= 0.2.5 < 1",
|
|
45
|
+
"tap": "^16.3.10",
|
|
46
|
+
"temp": ">= 0.4.0 < 1"
|
|
47
|
+
},
|
|
48
|
+
"directories": {
|
|
49
|
+
"example": "examples",
|
|
50
|
+
"test": "test"
|
|
51
|
+
},
|
|
52
|
+
"keywords": [
|
|
53
|
+
"zip",
|
|
54
|
+
"unzip",
|
|
55
|
+
"zlib",
|
|
56
|
+
"uncompress",
|
|
57
|
+
"archive",
|
|
58
|
+
"stream",
|
|
59
|
+
"extract"
|
|
60
|
+
],
|
|
61
|
+
"main": "unzip.js",
|
|
62
|
+
"scripts": {
|
|
63
|
+
"test": "npx tap test/*.js --coverage-report=html --lines=90 --functions=85 --statements=90 --branches=80 --reporter=dot"
|
|
64
|
+
}
|
|
65
|
+
}
|