@appium/support 7.0.4 → 7.0.6
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/build/lib/console.d.ts +42 -88
- package/build/lib/console.d.ts.map +1 -1
- package/build/lib/console.js +25 -85
- package/build/lib/console.js.map +1 -1
- package/build/lib/doctor.d.ts +6 -18
- package/build/lib/doctor.d.ts.map +1 -1
- package/build/lib/doctor.js +0 -15
- package/build/lib/doctor.js.map +1 -1
- package/build/lib/env.d.ts +14 -20
- package/build/lib/env.d.ts.map +1 -1
- package/build/lib/env.js +24 -61
- package/build/lib/env.js.map +1 -1
- package/build/lib/fs.d.ts +109 -148
- package/build/lib/fs.d.ts.map +1 -1
- package/build/lib/fs.js +130 -230
- package/build/lib/fs.js.map +1 -1
- package/build/lib/image-util.d.ts +7 -6
- package/build/lib/image-util.d.ts.map +1 -1
- package/build/lib/image-util.js +9 -6
- package/build/lib/image-util.js.map +1 -1
- package/build/lib/index.d.ts +19 -17
- package/build/lib/index.d.ts.map +1 -1
- package/build/lib/logger.d.ts +1 -1
- package/build/lib/logger.d.ts.map +1 -1
- package/build/lib/logger.js +1 -1
- package/build/lib/logger.js.map +1 -1
- package/build/lib/logging.d.ts +7 -15
- package/build/lib/logging.d.ts.map +1 -1
- package/build/lib/logging.js +36 -62
- package/build/lib/logging.js.map +1 -1
- package/build/lib/mjpeg.d.ts +19 -56
- package/build/lib/mjpeg.d.ts.map +1 -1
- package/build/lib/mjpeg.js +55 -78
- package/build/lib/mjpeg.js.map +1 -1
- package/build/lib/mkdirp.d.ts +4 -1
- package/build/lib/mkdirp.d.ts.map +1 -1
- package/build/lib/mkdirp.js +1 -2
- package/build/lib/mkdirp.js.map +1 -1
- package/build/lib/net.d.ts +52 -90
- package/build/lib/net.d.ts.map +1 -1
- package/build/lib/net.js +104 -193
- package/build/lib/net.js.map +1 -1
- package/build/lib/node.d.ts +16 -17
- package/build/lib/node.d.ts.map +1 -1
- package/build/lib/node.js +115 -120
- package/build/lib/node.js.map +1 -1
- package/build/lib/npm.d.ts +65 -86
- package/build/lib/npm.d.ts.map +1 -1
- package/build/lib/npm.js +64 -122
- package/build/lib/npm.js.map +1 -1
- package/build/lib/plist.d.ts +36 -29
- package/build/lib/plist.d.ts.map +1 -1
- package/build/lib/plist.js +62 -59
- package/build/lib/plist.js.map +1 -1
- package/build/lib/process.d.ts +19 -2
- package/build/lib/process.d.ts.map +1 -1
- package/build/lib/process.js +24 -7
- package/build/lib/process.js.map +1 -1
- package/build/lib/system.d.ts +41 -6
- package/build/lib/system.d.ts.map +1 -1
- package/build/lib/system.js +49 -14
- package/build/lib/system.js.map +1 -1
- package/build/lib/tempdir.d.ts +26 -49
- package/build/lib/tempdir.d.ts.map +1 -1
- package/build/lib/tempdir.js +46 -78
- package/build/lib/tempdir.js.map +1 -1
- package/build/lib/timing.d.ts +28 -22
- package/build/lib/timing.d.ts.map +1 -1
- package/build/lib/timing.js +16 -17
- package/build/lib/timing.js.map +1 -1
- package/build/lib/util.d.ts +164 -181
- package/build/lib/util.d.ts.map +1 -1
- package/build/lib/util.js +198 -253
- package/build/lib/util.js.map +1 -1
- package/build/lib/zip.d.ts +81 -139
- package/build/lib/zip.d.ts.map +1 -1
- package/build/lib/zip.js +235 -283
- package/build/lib/zip.js.map +1 -1
- package/lib/console.ts +139 -0
- package/lib/{doctor.js → doctor.ts} +6 -20
- package/lib/{env.js → env.ts} +34 -62
- package/lib/fs.ts +453 -0
- package/lib/image-util.ts +40 -0
- package/lib/index.ts +1 -0
- package/lib/{logger.js → logger.ts} +1 -1
- package/lib/logging.ts +157 -0
- package/lib/mjpeg.ts +186 -0
- package/lib/{mkdirp.js → mkdirp.ts} +2 -2
- package/lib/net.ts +305 -0
- package/lib/{node.js → node.ts} +136 -135
- package/lib/npm.ts +291 -0
- package/lib/plist.ts +187 -0
- package/lib/process.ts +62 -0
- package/lib/system.ts +95 -0
- package/lib/tempdir.ts +115 -0
- package/lib/{timing.js → timing.ts} +28 -33
- package/lib/util.ts +561 -0
- package/lib/{zip.js → zip.ts} +344 -299
- package/package.json +24 -26
- package/tsconfig.json +3 -5
- package/index.js +0 -1
- package/lib/console.js +0 -173
- package/lib/fs.js +0 -496
- package/lib/image-util.js +0 -32
- package/lib/logging.js +0 -145
- package/lib/mjpeg.js +0 -207
- package/lib/net.js +0 -336
- package/lib/npm.js +0 -310
- package/lib/plist.js +0 -182
- package/lib/process.js +0 -46
- package/lib/system.js +0 -48
- package/lib/tempdir.js +0 -131
- package/lib/util.js +0 -585
package/build/lib/zip.js
CHANGED
|
@@ -37,19 +37,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.extractAllTo = extractAllTo;
|
|
40
|
+
exports._extractEntryTo = _extractEntryTo;
|
|
40
41
|
exports.readEntries = readEntries;
|
|
41
42
|
exports.toInMemoryZip = toInMemoryZip;
|
|
42
|
-
exports._extractEntryTo = _extractEntryTo;
|
|
43
43
|
exports.assertValidZip = assertValidZip;
|
|
44
44
|
exports.toArchive = toArchive;
|
|
45
45
|
const lodash_1 = __importDefault(require("lodash"));
|
|
46
|
-
const
|
|
46
|
+
const node_util_1 = require("node:util");
|
|
47
47
|
const yauzl = __importStar(require("yauzl"));
|
|
48
48
|
const archiver_1 = __importDefault(require("archiver"));
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
const
|
|
49
|
+
const node_fs_1 = require("node:fs");
|
|
50
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
51
|
+
const node_stream_1 = __importDefault(require("node:stream"));
|
|
52
|
+
const promises_1 = require("node:stream/promises");
|
|
53
|
+
const fs_1 = require("./fs");
|
|
53
54
|
const system_1 = require("./system");
|
|
54
55
|
const base64_stream_1 = require("base64-stream");
|
|
55
56
|
const util_1 = require("./util");
|
|
@@ -57,167 +58,24 @@ const timing_1 = require("./timing");
|
|
|
57
58
|
const logger_1 = __importDefault(require("./logger"));
|
|
58
59
|
const get_stream_1 = __importDefault(require("get-stream"));
|
|
59
60
|
const teen_process_1 = require("teen_process");
|
|
60
|
-
|
|
61
|
-
* @type {(path: string, options?: yauzl.Options) => Promise<yauzl.ZipFile>}
|
|
62
|
-
*/
|
|
63
|
-
const openZip = bluebird_1.default.promisify(yauzl.open);
|
|
64
|
-
/**
|
|
65
|
-
* @type {(source: NodeJS.ReadableStream, destination: NodeJS.WritableStream) => Promise<NodeJS.WritableStream>}
|
|
66
|
-
*/
|
|
67
|
-
const pipeline = bluebird_1.default.promisify(stream_1.default.pipeline);
|
|
61
|
+
const openZip = (0, node_util_1.promisify)(yauzl.open);
|
|
68
62
|
const ZIP_MAGIC = 'PK';
|
|
69
|
-
const IFMT =
|
|
70
|
-
const IFDIR =
|
|
71
|
-
const IFLNK =
|
|
72
|
-
// This class is mostly copied from https://github.com/maxogden/extract-zip/blob/master/index.js
|
|
73
|
-
class ZipExtractor {
|
|
74
|
-
/** @type {yauzl.ZipFile} */
|
|
75
|
-
zipfile;
|
|
76
|
-
constructor(sourcePath, opts = {}) {
|
|
77
|
-
this.zipPath = sourcePath;
|
|
78
|
-
this.opts = opts;
|
|
79
|
-
this.canceled = false;
|
|
80
|
-
}
|
|
81
|
-
extractFileName(entry) {
|
|
82
|
-
return lodash_1.default.isBuffer(entry.fileName)
|
|
83
|
-
? entry.fileName.toString(this.opts.fileNamesEncoding)
|
|
84
|
-
: entry.fileName;
|
|
85
|
-
}
|
|
86
|
-
async extract() {
|
|
87
|
-
const { dir, fileNamesEncoding } = this.opts;
|
|
88
|
-
this.zipfile = await openZip(this.zipPath, {
|
|
89
|
-
lazyEntries: true,
|
|
90
|
-
// https://github.com/thejoshwolfe/yauzl/commit/cc7455ac789ba84973184e5ebde0581cdc4c3b39#diff-04c6e90faac2675aa89e2176d2eec7d8R95
|
|
91
|
-
decodeStrings: !fileNamesEncoding,
|
|
92
|
-
});
|
|
93
|
-
this.canceled = false;
|
|
94
|
-
return new bluebird_1.default((resolve, reject) => {
|
|
95
|
-
this.zipfile.on('error', (err) => {
|
|
96
|
-
this.canceled = true;
|
|
97
|
-
reject(err);
|
|
98
|
-
});
|
|
99
|
-
this.zipfile.readEntry();
|
|
100
|
-
this.zipfile.on('close', () => {
|
|
101
|
-
if (!this.canceled) {
|
|
102
|
-
resolve();
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
this.zipfile.on('entry', async (entry) => {
|
|
106
|
-
if (this.canceled) {
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
const fileName = this.extractFileName(entry);
|
|
110
|
-
if (fileName.startsWith('__MACOSX/')) {
|
|
111
|
-
this.zipfile.readEntry();
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
const destDir = path_1.default.dirname(path_1.default.join(dir, fileName));
|
|
115
|
-
try {
|
|
116
|
-
await fs_2.fs.mkdir(destDir, { recursive: true });
|
|
117
|
-
const canonicalDestDir = await fs_2.fs.realpath(destDir);
|
|
118
|
-
const relativeDestDir = path_1.default.relative(dir, canonicalDestDir);
|
|
119
|
-
if (relativeDestDir.split(path_1.default.sep).includes('..')) {
|
|
120
|
-
new Error(`Out of bound path "${canonicalDestDir}" found while processing file ${fileName}`);
|
|
121
|
-
}
|
|
122
|
-
await this.extractEntry(entry);
|
|
123
|
-
this.zipfile.readEntry();
|
|
124
|
-
}
|
|
125
|
-
catch (err) {
|
|
126
|
-
this.canceled = true;
|
|
127
|
-
this.zipfile.close();
|
|
128
|
-
reject(err);
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
async extractEntry(entry) {
|
|
134
|
-
if (this.canceled) {
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
const { dir } = this.opts;
|
|
138
|
-
const fileName = this.extractFileName(entry);
|
|
139
|
-
const dest = path_1.default.join(dir, fileName);
|
|
140
|
-
// convert external file attr int into a fs stat mode int
|
|
141
|
-
const mode = (entry.externalFileAttributes >> 16) & 0xffff;
|
|
142
|
-
// check if it's a symlink or dir (using stat mode constants)
|
|
143
|
-
const isSymlink = (mode & IFMT) === IFLNK;
|
|
144
|
-
const isDir = (mode & IFMT) === IFDIR ||
|
|
145
|
-
// Failsafe, borrowed from jsZip
|
|
146
|
-
fileName.endsWith('/') ||
|
|
147
|
-
// check for windows weird way of specifying a directory
|
|
148
|
-
// https://github.com/maxogden/extract-zip/issues/13#issuecomment-154494566
|
|
149
|
-
(entry.versionMadeBy >> 8 === 0 && entry.externalFileAttributes === 16);
|
|
150
|
-
const procMode = this.getExtractedMode(mode, isDir) & 0o777;
|
|
151
|
-
// always ensure folders are created
|
|
152
|
-
const destDir = isDir ? dest : path_1.default.dirname(dest);
|
|
153
|
-
const mkdirOptions = { recursive: true };
|
|
154
|
-
if (isDir) {
|
|
155
|
-
mkdirOptions.mode = procMode;
|
|
156
|
-
}
|
|
157
|
-
await fs_2.fs.mkdir(destDir, mkdirOptions);
|
|
158
|
-
if (isDir) {
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
/** @type {(entry: yauzl.Entry) => Promise<NodeJS.ReadableStream>} */
|
|
162
|
-
const openReadStream = bluebird_1.default.promisify(this.zipfile.openReadStream.bind(this.zipfile));
|
|
163
|
-
const readStream = await openReadStream(entry);
|
|
164
|
-
if (isSymlink) {
|
|
165
|
-
// @ts-ignore This typecast is ok
|
|
166
|
-
const link = await (0, get_stream_1.default)(readStream);
|
|
167
|
-
await fs_2.fs.symlink(link, dest);
|
|
168
|
-
}
|
|
169
|
-
else {
|
|
170
|
-
await pipeline(readStream, fs_2.fs.createWriteStream(dest, { mode: procMode }));
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
getExtractedMode(entryMode, isDir) {
|
|
174
|
-
const { defaultDirMode, defaultFileMode } = this.opts;
|
|
175
|
-
let mode = entryMode;
|
|
176
|
-
// Set defaults, if necessary
|
|
177
|
-
if (mode === 0) {
|
|
178
|
-
if (isDir) {
|
|
179
|
-
if (defaultDirMode) {
|
|
180
|
-
mode = parseInt(defaultDirMode, 10);
|
|
181
|
-
}
|
|
182
|
-
if (!mode) {
|
|
183
|
-
mode = 0o755;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
else {
|
|
187
|
-
if (defaultFileMode) {
|
|
188
|
-
mode = parseInt(defaultFileMode, 10);
|
|
189
|
-
}
|
|
190
|
-
if (!mode) {
|
|
191
|
-
mode = 0o644;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
return mode;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
/**
|
|
199
|
-
* @typedef ExtractAllOptions
|
|
200
|
-
* @property {string} [fileNamesEncoding] The encoding to use for extracted file names.
|
|
201
|
-
* For ZIP archives created on MacOS it is usually expected to be `utf8`.
|
|
202
|
-
* By default it is autodetected based on the entry metadata and is only needed to be set explicitly
|
|
203
|
-
* if the particular archive does not comply to the standards, which leads to corrupted file names
|
|
204
|
-
* after extraction. Only applicable if system unzip binary is NOT being used.
|
|
205
|
-
* @property {boolean} [useSystemUnzip] If true, attempt to use system unzip; if this fails,
|
|
206
|
-
* fallback to the JS unzip implementation.
|
|
207
|
-
*/
|
|
63
|
+
const IFMT = 0b1111000000000000;
|
|
64
|
+
const IFDIR = 0b0100000000000000;
|
|
65
|
+
const IFLNK = 0b1010000000000000;
|
|
208
66
|
/**
|
|
209
67
|
* Extract zipfile to a directory
|
|
210
68
|
*
|
|
211
|
-
* @param
|
|
212
|
-
* @param
|
|
213
|
-
* @param
|
|
69
|
+
* @param zipFilePath The full path to the source ZIP file
|
|
70
|
+
* @param destDir The full path to the destination folder
|
|
71
|
+
* @param opts Extraction options
|
|
214
72
|
*/
|
|
215
|
-
async function extractAllTo(zipFilePath, destDir, opts =
|
|
216
|
-
if (!
|
|
73
|
+
async function extractAllTo(zipFilePath, destDir, opts = {}) {
|
|
74
|
+
if (!node_path_1.default.isAbsolute(destDir)) {
|
|
217
75
|
throw new Error(`Target path '${destDir}' is expected to be absolute`);
|
|
218
76
|
}
|
|
219
|
-
await
|
|
220
|
-
const dir = await
|
|
77
|
+
await fs_1.fs.mkdir(destDir, { recursive: true });
|
|
78
|
+
const dir = await fs_1.fs.realpath(destDir);
|
|
221
79
|
if (opts.useSystemUnzip) {
|
|
222
80
|
try {
|
|
223
81
|
await extractWithSystemUnzip(zipFilePath, dir);
|
|
@@ -233,100 +91,58 @@ async function extractAllTo(zipFilePath, destDir, opts = /** @type {ExtractAllOp
|
|
|
233
91
|
});
|
|
234
92
|
await extractor.extract();
|
|
235
93
|
}
|
|
236
|
-
/**
|
|
237
|
-
* Executes system unzip (e.g., `/usr/bin/unzip`). If available, it is
|
|
238
|
-
* significantly faster than the JS implementation.
|
|
239
|
-
* By default all files in the destDir get overridden if already exist.
|
|
240
|
-
*
|
|
241
|
-
* @param {string} zipFilePath The full path to the source ZIP file
|
|
242
|
-
* @param {string} destDir The full path to the destination folder.
|
|
243
|
-
* This folder is expected to already exist before extracting the archive.
|
|
244
|
-
*/
|
|
245
|
-
async function extractWithSystemUnzip(zipFilePath, destDir) {
|
|
246
|
-
const isWindowsHost = (0, system_1.isWindows)();
|
|
247
|
-
let executablePath;
|
|
248
|
-
try {
|
|
249
|
-
executablePath = await getExecutablePath(isWindowsHost ? 'powershell.exe' : 'unzip');
|
|
250
|
-
}
|
|
251
|
-
catch {
|
|
252
|
-
throw new Error('Could not find system unzip');
|
|
253
|
-
}
|
|
254
|
-
if (isWindowsHost) {
|
|
255
|
-
// on Windows we use PowerShell to unzip files
|
|
256
|
-
await (0, teen_process_1.exec)(executablePath, [
|
|
257
|
-
'-command',
|
|
258
|
-
'Expand-Archive',
|
|
259
|
-
'-LiteralPath',
|
|
260
|
-
zipFilePath,
|
|
261
|
-
'-DestinationPath',
|
|
262
|
-
destDir,
|
|
263
|
-
'-Force',
|
|
264
|
-
]);
|
|
265
|
-
}
|
|
266
|
-
else {
|
|
267
|
-
// -q means quiet (no stdout)
|
|
268
|
-
// -o means overwrite
|
|
269
|
-
// -d is the dest dir
|
|
270
|
-
await (0, teen_process_1.exec)(executablePath, ['-q', '-o', zipFilePath, '-d', destDir]);
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
94
|
/**
|
|
274
95
|
* Extract a single zip entry to a directory
|
|
275
96
|
*
|
|
276
|
-
* @
|
|
277
|
-
* @param
|
|
278
|
-
* @param
|
|
97
|
+
* @private
|
|
98
|
+
* @param zipFile The source ZIP stream
|
|
99
|
+
* @param entry The entry instance
|
|
100
|
+
* @param destDir The full path to the destination folder
|
|
279
101
|
*/
|
|
280
102
|
async function _extractEntryTo(zipFile, entry, destDir) {
|
|
281
|
-
const dstPath =
|
|
103
|
+
const dstPath = node_path_1.default.resolve(destDir, entry.fileName);
|
|
104
|
+
if (!(0, util_1.isSubPath)(dstPath, destDir)) {
|
|
105
|
+
throw new Error(`Out of bound path "${dstPath}" found while processing file ${entry.fileName}`);
|
|
106
|
+
}
|
|
282
107
|
// Create dest directory if doesn't exist already
|
|
283
108
|
if (entry.fileName.endsWith('/')) {
|
|
284
|
-
if (!(await
|
|
285
|
-
await
|
|
109
|
+
if (!(await fs_1.fs.exists(dstPath))) {
|
|
110
|
+
await fs_1.fs.mkdirp(dstPath);
|
|
286
111
|
}
|
|
287
112
|
return;
|
|
288
113
|
}
|
|
289
|
-
else if (!(await
|
|
290
|
-
await
|
|
114
|
+
else if (!(await fs_1.fs.exists(node_path_1.default.dirname(dstPath)))) {
|
|
115
|
+
await fs_1.fs.mkdirp(node_path_1.default.dirname(dstPath));
|
|
291
116
|
}
|
|
292
117
|
// Create a write stream
|
|
293
|
-
const writeStream = (0,
|
|
294
|
-
const writeStreamPromise = new
|
|
118
|
+
const writeStream = (0, node_fs_1.createWriteStream)(dstPath, { flags: 'w' });
|
|
119
|
+
const writeStreamPromise = new Promise((resolve, reject) => {
|
|
295
120
|
writeStream.once('finish', resolve);
|
|
296
121
|
writeStream.once('error', reject);
|
|
297
122
|
});
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
const
|
|
301
|
-
zipFile.openReadStream(entry, (err, readStream) => (err ? reject(err) : resolve(readStream)));
|
|
302
|
-
});
|
|
303
|
-
const zipReadStreamPromise = new bluebird_1.default((resolve, reject) => {
|
|
123
|
+
const openReadStream = (0, node_util_1.promisify)(zipFile.openReadStream.bind(zipFile));
|
|
124
|
+
const zipReadStream = await openReadStream(entry);
|
|
125
|
+
const zipReadStreamPromise = new Promise((resolve, reject) => {
|
|
304
126
|
zipReadStream.once('end', resolve);
|
|
305
127
|
zipReadStream.once('error', reject);
|
|
306
128
|
});
|
|
307
129
|
zipReadStream.pipe(writeStream);
|
|
308
130
|
// Wait for the zipReadStream and writeStream to end before returning
|
|
309
|
-
|
|
131
|
+
await Promise.all([zipReadStreamPromise, writeStreamPromise]);
|
|
310
132
|
}
|
|
311
|
-
/**
|
|
312
|
-
* @typedef ZipEntry
|
|
313
|
-
* @property {yauzl.Entry} entry The actual entry instance
|
|
314
|
-
* @property {function} extractEntryTo An async function, which accepts one parameter.
|
|
315
|
-
* This parameter contains the destination folder path to which this function is going to extract the entry.
|
|
316
|
-
*/
|
|
317
133
|
/**
|
|
318
134
|
* Get entries for a zip folder
|
|
319
135
|
*
|
|
320
|
-
* @param
|
|
321
|
-
* @param
|
|
136
|
+
* @param zipFilePath The full path to the source ZIP file
|
|
137
|
+
* @param onEntry Callback when entry is read.
|
|
322
138
|
* The callback is expected to accept one argument of ZipEntry type.
|
|
323
|
-
* The iteration through the source zip file will
|
|
139
|
+
* The iteration through the source zip file will be terminated as soon as
|
|
324
140
|
* the result of this function equals to `false`.
|
|
325
141
|
*/
|
|
326
142
|
async function readEntries(zipFilePath, onEntry) {
|
|
327
143
|
// Open a zip file and start reading entries
|
|
328
144
|
const zipfile = await openZip(zipFilePath, { lazyEntries: true });
|
|
329
|
-
const zipReadStreamPromise = new
|
|
145
|
+
const zipReadStreamPromise = new Promise((resolve, reject) => {
|
|
330
146
|
zipfile.once('end', resolve);
|
|
331
147
|
zipfile.once('error', reject);
|
|
332
148
|
// On each entry, call 'onEntry' and then read the next entry
|
|
@@ -343,43 +159,28 @@ async function readEntries(zipFilePath, onEntry) {
|
|
|
343
159
|
});
|
|
344
160
|
zipfile.readEntry();
|
|
345
161
|
// Wait for the entries to finish being iterated through
|
|
346
|
-
|
|
162
|
+
await zipReadStreamPromise;
|
|
347
163
|
}
|
|
348
|
-
/**
|
|
349
|
-
* @typedef ZipOptions
|
|
350
|
-
* @property {boolean} [encodeToBase64=false] Whether to encode
|
|
351
|
-
* the resulting archive to a base64-encoded string
|
|
352
|
-
* @property {boolean} [isMetered=true] Whether to log the actual
|
|
353
|
-
* archiver performance
|
|
354
|
-
* @property {number} [maxSize=1073741824] The maximum size of
|
|
355
|
-
* the resulting archive in bytes. This is set to 1GB by default, because
|
|
356
|
-
* Appium limits the maximum HTTP body size to 1GB. Also, the NodeJS heap
|
|
357
|
-
* size must be enough to keep the resulting object (usually this size is
|
|
358
|
-
* limited to 1.4 GB)
|
|
359
|
-
* @property {number} [level=9] The compression level. The maximum
|
|
360
|
-
* level is 9 (the best compression, worst performance). The minimum
|
|
361
|
-
* compression level is 0 (no compression).
|
|
362
|
-
*/
|
|
363
164
|
/**
|
|
364
165
|
* Converts contents of local directory to an in-memory .zip buffer
|
|
365
166
|
*
|
|
366
|
-
* @param
|
|
367
|
-
* @param
|
|
368
|
-
* @returns
|
|
167
|
+
* @param srcPath The full path to the folder or file being zipped
|
|
168
|
+
* @param opts Zipping options
|
|
169
|
+
* @returns Zipped (and encoded if `encodeToBase64` is truthy)
|
|
369
170
|
* content of the source path as memory buffer
|
|
370
171
|
* @throws {Error} if there was an error while reading the source
|
|
371
172
|
* or the source is too big
|
|
372
173
|
*/
|
|
373
|
-
async function toInMemoryZip(srcPath, opts =
|
|
374
|
-
if (!(await
|
|
174
|
+
async function toInMemoryZip(srcPath, opts = {}) {
|
|
175
|
+
if (!(await fs_1.fs.exists(srcPath))) {
|
|
375
176
|
throw new Error(`No such file or folder: ${srcPath}`);
|
|
376
177
|
}
|
|
377
178
|
const { isMetered = true, encodeToBase64 = false, maxSize = 1 * util_1.GiB, level = 9 } = opts;
|
|
378
179
|
const resultBuffers = [];
|
|
379
180
|
let resultBuffersSize = 0;
|
|
380
181
|
// Create a writable stream that zip buffers will be streamed to
|
|
381
|
-
const resultWriteStream = new
|
|
382
|
-
write
|
|
182
|
+
const resultWriteStream = new node_stream_1.default.Writable({
|
|
183
|
+
write(buffer, _encoding, next) {
|
|
383
184
|
resultBuffers.push(buffer);
|
|
384
185
|
resultBuffersSize += buffer.length;
|
|
385
186
|
if (maxSize > 0 && resultBuffersSize > maxSize) {
|
|
@@ -395,7 +196,7 @@ async function toInMemoryZip(srcPath, opts = /** @type {ZipOptions} */ ({})) {
|
|
|
395
196
|
});
|
|
396
197
|
let srcSize = null;
|
|
397
198
|
const base64EncoderStream = encodeToBase64 ? new base64_stream_1.Base64Encode() : null;
|
|
398
|
-
const resultWriteStreamPromise = new
|
|
199
|
+
const resultWriteStreamPromise = new Promise((resolve, reject) => {
|
|
399
200
|
resultWriteStream.once('error', (e) => {
|
|
400
201
|
if (base64EncoderStream) {
|
|
401
202
|
archive.unpipe(base64EncoderStream);
|
|
@@ -413,17 +214,17 @@ async function toInMemoryZip(srcPath, opts = /** @type {ZipOptions} */ ({})) {
|
|
|
413
214
|
resolve();
|
|
414
215
|
});
|
|
415
216
|
});
|
|
416
|
-
const archiveStreamPromise = new
|
|
217
|
+
const archiveStreamPromise = new Promise((resolve, reject) => {
|
|
417
218
|
archive.once('finish', resolve);
|
|
418
219
|
archive.once('error', (e) => reject(new Error(`Failed to archive '${srcPath}': ${e.message}`)));
|
|
419
220
|
});
|
|
420
221
|
const timer = isMetered ? new timing_1.Timer().start() : null;
|
|
421
|
-
if ((await
|
|
222
|
+
if ((await fs_1.fs.stat(srcPath)).isDirectory()) {
|
|
422
223
|
archive.directory(srcPath, false);
|
|
423
224
|
}
|
|
424
225
|
else {
|
|
425
226
|
archive.file(srcPath, {
|
|
426
|
-
name:
|
|
227
|
+
name: node_path_1.default.basename(srcPath),
|
|
427
228
|
});
|
|
428
229
|
}
|
|
429
230
|
if (base64EncoderStream) {
|
|
@@ -435,10 +236,10 @@ async function toInMemoryZip(srcPath, opts = /** @type {ZipOptions} */ ({})) {
|
|
|
435
236
|
}
|
|
436
237
|
archive.finalize();
|
|
437
238
|
// Wait for the streams to finish
|
|
438
|
-
await
|
|
239
|
+
await Promise.all([archiveStreamPromise, resultWriteStreamPromise]);
|
|
439
240
|
if (timer) {
|
|
440
241
|
logger_1.default.debug(`Zipped ${encodeToBase64 ? 'and base64-encoded ' : ''}` +
|
|
441
|
-
`'${
|
|
242
|
+
`'${node_path_1.default.basename(srcPath)}' ` +
|
|
442
243
|
(srcSize ? `(${(0, util_1.toReadableSizeString)(srcSize)}) ` : '') +
|
|
443
244
|
`in ${timer.getDuration().asSeconds.toFixed(3)}s ` +
|
|
444
245
|
`(compression level: ${level})`);
|
|
@@ -449,21 +250,21 @@ async function toInMemoryZip(srcPath, opts = /** @type {ZipOptions} */ ({})) {
|
|
|
449
250
|
/**
|
|
450
251
|
* Verifies whether the given file is a valid ZIP archive
|
|
451
252
|
*
|
|
452
|
-
* @param
|
|
253
|
+
* @param filePath - Full path to the file
|
|
453
254
|
* @throws {Error} If the file does not exist or is not a valid ZIP archive
|
|
454
255
|
*/
|
|
455
256
|
async function assertValidZip(filePath) {
|
|
456
|
-
if (!(await
|
|
257
|
+
if (!(await fs_1.fs.exists(filePath))) {
|
|
457
258
|
throw new Error(`The file at '${filePath}' does not exist`);
|
|
458
259
|
}
|
|
459
|
-
const { size } = await
|
|
260
|
+
const { size } = await fs_1.fs.stat(filePath);
|
|
460
261
|
if (size < 4) {
|
|
461
262
|
throw new Error(`The file at '${filePath}' is too small to be a ZIP archive`);
|
|
462
263
|
}
|
|
463
|
-
const fd = await
|
|
264
|
+
const fd = await fs_1.fs.open(filePath, 'r');
|
|
464
265
|
try {
|
|
465
266
|
const buffer = Buffer.alloc(ZIP_MAGIC.length);
|
|
466
|
-
await
|
|
267
|
+
await fs_1.fs.read(fd, buffer, 0, ZIP_MAGIC.length, 0);
|
|
467
268
|
const signature = buffer.toString('ascii');
|
|
468
269
|
if (signature !== ZIP_MAGIC) {
|
|
469
270
|
throw new Error(`The file signature '${signature}' of '${filePath}' ` +
|
|
@@ -472,45 +273,33 @@ async function assertValidZip(filePath) {
|
|
|
472
273
|
return true;
|
|
473
274
|
}
|
|
474
275
|
finally {
|
|
475
|
-
await
|
|
276
|
+
await fs_1.fs.close(fd);
|
|
476
277
|
}
|
|
477
278
|
}
|
|
478
|
-
/**
|
|
479
|
-
* @typedef ZipCompressionOptions
|
|
480
|
-
* @property {number} level [9] - Compression level in range 0..9
|
|
481
|
-
* (greater numbers mean better compression, but longer processing time)
|
|
482
|
-
*/
|
|
483
|
-
/**
|
|
484
|
-
* @typedef ZipSourceOptions
|
|
485
|
-
* @property {string} [pattern='**\/*'] - GLOB pattern for compression
|
|
486
|
-
* @property {string} [cwd] - The source root folder (the parent folder of
|
|
487
|
-
* the destination file by default)
|
|
488
|
-
* @property {string[]} [ignore] - The list of ignored patterns
|
|
489
|
-
*/
|
|
490
279
|
/**
|
|
491
280
|
* Creates an archive based on the given glob pattern
|
|
492
281
|
*
|
|
493
|
-
* @param
|
|
494
|
-
* @param
|
|
495
|
-
* @param
|
|
282
|
+
* @param dstPath - The resulting archive path
|
|
283
|
+
* @param src - Source options
|
|
284
|
+
* @param opts - Compression options
|
|
496
285
|
* @throws {Error} If there was an error while creating the archive
|
|
497
286
|
*/
|
|
498
|
-
async function toArchive(dstPath, src =
|
|
287
|
+
async function toArchive(dstPath, src = {}, opts = {}) {
|
|
499
288
|
const { level = 9 } = opts;
|
|
500
|
-
const { pattern = '**/*', cwd =
|
|
289
|
+
const { pattern = '**/*', cwd = node_path_1.default.dirname(dstPath), ignore = [] } = src;
|
|
501
290
|
const archive = (0, archiver_1.default)('zip', { zlib: { level } });
|
|
502
|
-
const
|
|
503
|
-
|
|
291
|
+
const outStream = fs_1.fs.createWriteStream(dstPath);
|
|
292
|
+
await new Promise((resolve, reject) => {
|
|
504
293
|
archive
|
|
505
294
|
.glob(pattern, {
|
|
506
295
|
cwd,
|
|
507
296
|
ignore,
|
|
508
297
|
})
|
|
509
298
|
.on('error', reject)
|
|
510
|
-
.pipe(
|
|
511
|
-
|
|
299
|
+
.pipe(outStream);
|
|
300
|
+
outStream
|
|
512
301
|
.on('error', (e) => {
|
|
513
|
-
archive.unpipe(
|
|
302
|
+
archive.unpipe(outStream);
|
|
514
303
|
archive.abort();
|
|
515
304
|
archive.destroy();
|
|
516
305
|
reject(e);
|
|
@@ -519,16 +308,179 @@ async function toArchive(dstPath, src = /** @type {ZipSourceOptions} */ ({}), op
|
|
|
519
308
|
archive.finalize();
|
|
520
309
|
});
|
|
521
310
|
}
|
|
311
|
+
// This class is mostly copied from https://github.com/maxogden/extract-zip/blob/master/index.js
|
|
312
|
+
class ZipExtractor {
|
|
313
|
+
zipfile;
|
|
314
|
+
zipPath;
|
|
315
|
+
opts;
|
|
316
|
+
canceled = false;
|
|
317
|
+
constructor(sourcePath, opts) {
|
|
318
|
+
this.zipPath = sourcePath;
|
|
319
|
+
this.opts = opts;
|
|
320
|
+
}
|
|
321
|
+
extractFileName(entry) {
|
|
322
|
+
if (Buffer.isBuffer(entry.fileName)) {
|
|
323
|
+
return entry.fileName.toString(this.opts.fileNamesEncoding);
|
|
324
|
+
}
|
|
325
|
+
return entry.fileName;
|
|
326
|
+
}
|
|
327
|
+
async extract() {
|
|
328
|
+
const { dir, fileNamesEncoding } = this.opts;
|
|
329
|
+
this.zipfile = await openZip(this.zipPath, {
|
|
330
|
+
lazyEntries: true,
|
|
331
|
+
// https://github.com/thejoshwolfe/yauzl/commit/cc7455ac789ba84973184e5ebde0581cdc4c3b39#diff-04c6e90faac2675aa89e2176d2eec7d8R95
|
|
332
|
+
decodeStrings: !fileNamesEncoding,
|
|
333
|
+
});
|
|
334
|
+
this.canceled = false;
|
|
335
|
+
return new Promise((resolve, reject) => {
|
|
336
|
+
this.zipfile.on('error', (err) => {
|
|
337
|
+
this.canceled = true;
|
|
338
|
+
reject(err);
|
|
339
|
+
});
|
|
340
|
+
this.zipfile.readEntry();
|
|
341
|
+
this.zipfile.on('close', () => {
|
|
342
|
+
if (!this.canceled) {
|
|
343
|
+
resolve();
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
this.zipfile.on('entry', async (entry) => {
|
|
347
|
+
if (this.canceled) {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
const fileName = this.extractFileName(entry);
|
|
351
|
+
if (fileName.startsWith('__MACOSX/')) {
|
|
352
|
+
this.zipfile.readEntry();
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
const destDir = node_path_1.default.dirname(node_path_1.default.join(dir, fileName));
|
|
356
|
+
try {
|
|
357
|
+
await fs_1.fs.mkdir(destDir, { recursive: true });
|
|
358
|
+
const canonicalDestDir = await fs_1.fs.realpath(destDir);
|
|
359
|
+
const relativeDestDir = node_path_1.default.relative(dir, canonicalDestDir);
|
|
360
|
+
if (relativeDestDir.split(node_path_1.default.sep).includes('..')) {
|
|
361
|
+
throw new Error(`Out of bound path "${canonicalDestDir}" found while processing file ${fileName}`);
|
|
362
|
+
}
|
|
363
|
+
await this.extractEntry(entry);
|
|
364
|
+
this.zipfile.readEntry();
|
|
365
|
+
}
|
|
366
|
+
catch (err) {
|
|
367
|
+
this.canceled = true;
|
|
368
|
+
this.zipfile.close();
|
|
369
|
+
reject(err);
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
async extractEntry(entry) {
|
|
375
|
+
if (this.canceled) {
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
const { dir } = this.opts;
|
|
379
|
+
const fileName = this.extractFileName(entry);
|
|
380
|
+
const dest = node_path_1.default.join(dir, fileName);
|
|
381
|
+
// convert external file attr int into a fs stat mode int
|
|
382
|
+
const mode = (entry.externalFileAttributes >> 16) & 0xffff;
|
|
383
|
+
// check if it's a symlink or dir (using stat mode constants)
|
|
384
|
+
const isSymlink = (mode & IFMT) === IFLNK;
|
|
385
|
+
const isDir = (mode & IFMT) === IFDIR ||
|
|
386
|
+
// Failsafe, borrowed from jsZip
|
|
387
|
+
fileName.endsWith('/') ||
|
|
388
|
+
// check for windows weird way of specifying a directory
|
|
389
|
+
// https://github.com/maxogden/extract-zip/issues/13#issuecomment-154494566
|
|
390
|
+
(entry.versionMadeBy >> 8 === 0 && entry.externalFileAttributes === 16);
|
|
391
|
+
const procMode = this.getExtractedMode(mode, isDir) & 0o777;
|
|
392
|
+
// always ensure folders are created
|
|
393
|
+
const destDir = isDir ? dest : node_path_1.default.dirname(dest);
|
|
394
|
+
const mkdirOptions = { recursive: true };
|
|
395
|
+
if (isDir) {
|
|
396
|
+
mkdirOptions.mode = procMode;
|
|
397
|
+
}
|
|
398
|
+
await fs_1.fs.mkdir(destDir, mkdirOptions);
|
|
399
|
+
if (isDir) {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
const openReadStream = (0, node_util_1.promisify)(this.zipfile.openReadStream.bind(this.zipfile));
|
|
403
|
+
const readStream = await openReadStream(entry);
|
|
404
|
+
if (isSymlink) {
|
|
405
|
+
const link = await (0, get_stream_1.default)(readStream);
|
|
406
|
+
await fs_1.fs.symlink(link, dest);
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
await (0, promises_1.pipeline)(readStream, fs_1.fs.createWriteStream(dest, { mode: procMode }));
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
getExtractedMode(entryMode, isDir) {
|
|
413
|
+
const { defaultDirMode, defaultFileMode } = this.opts;
|
|
414
|
+
let mode = entryMode;
|
|
415
|
+
// Set defaults, if necessary
|
|
416
|
+
if (mode === 0) {
|
|
417
|
+
if (isDir) {
|
|
418
|
+
if (defaultDirMode) {
|
|
419
|
+
mode = parseInt(defaultDirMode, 10);
|
|
420
|
+
}
|
|
421
|
+
if (!mode) {
|
|
422
|
+
mode = 0o755;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
else {
|
|
426
|
+
if (defaultFileMode) {
|
|
427
|
+
mode = parseInt(defaultFileMode, 10);
|
|
428
|
+
}
|
|
429
|
+
if (!mode) {
|
|
430
|
+
mode = 0o644;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
return mode;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Executes system unzip (e.g., `/usr/bin/unzip`). If available, it is
|
|
439
|
+
* significantly faster than the JS implementation.
|
|
440
|
+
* By default all files in the destDir get overridden if already exist.
|
|
441
|
+
*
|
|
442
|
+
* @param zipFilePath The full path to the source ZIP file
|
|
443
|
+
* @param destDir The full path to the destination folder.
|
|
444
|
+
* This folder is expected to already exist before extracting the archive.
|
|
445
|
+
*/
|
|
446
|
+
async function extractWithSystemUnzip(zipFilePath, destDir) {
|
|
447
|
+
const isWindowsHost = (0, system_1.isWindows)();
|
|
448
|
+
let executablePath;
|
|
449
|
+
try {
|
|
450
|
+
executablePath = await getExecutablePath(isWindowsHost ? 'powershell.exe' : 'unzip');
|
|
451
|
+
}
|
|
452
|
+
catch {
|
|
453
|
+
throw new Error('Could not find system unzip');
|
|
454
|
+
}
|
|
455
|
+
if (isWindowsHost) {
|
|
456
|
+
// on Windows we use PowerShell to unzip files
|
|
457
|
+
await (0, teen_process_1.exec)(executablePath, [
|
|
458
|
+
'-command',
|
|
459
|
+
'Expand-Archive',
|
|
460
|
+
'-LiteralPath',
|
|
461
|
+
zipFilePath,
|
|
462
|
+
'-DestinationPath',
|
|
463
|
+
destDir,
|
|
464
|
+
'-Force',
|
|
465
|
+
]);
|
|
466
|
+
}
|
|
467
|
+
else {
|
|
468
|
+
// -q means quiet (no stdout)
|
|
469
|
+
// -o means overwrite
|
|
470
|
+
// -d is the dest dir
|
|
471
|
+
await (0, teen_process_1.exec)(executablePath, ['-q', '-o', zipFilePath, '-d', destDir]);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
522
474
|
/**
|
|
523
475
|
* Finds and memoizes the full path to the given executable.
|
|
524
476
|
* Rejects if it is not found.
|
|
525
477
|
*/
|
|
526
478
|
const getExecutablePath = lodash_1.default.memoize(
|
|
527
479
|
/**
|
|
528
|
-
* @returns
|
|
480
|
+
* @returns Full Path to the executable
|
|
529
481
|
*/
|
|
530
482
|
async function getExecutablePath(binaryName) {
|
|
531
|
-
const fullPath = await
|
|
483
|
+
const fullPath = await fs_1.fs.which(binaryName);
|
|
532
484
|
logger_1.default.debug(`Found '${binaryName}' at '${fullPath}'`);
|
|
533
485
|
return fullPath;
|
|
534
486
|
});
|