@appium/support 7.0.5 → 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.
Files changed (114) hide show
  1. package/LICENSE +201 -0
  2. package/build/lib/console.d.ts +42 -88
  3. package/build/lib/console.d.ts.map +1 -1
  4. package/build/lib/console.js +20 -80
  5. package/build/lib/console.js.map +1 -1
  6. package/build/lib/doctor.d.ts +6 -18
  7. package/build/lib/doctor.d.ts.map +1 -1
  8. package/build/lib/doctor.js +0 -15
  9. package/build/lib/doctor.js.map +1 -1
  10. package/build/lib/env.d.ts +14 -20
  11. package/build/lib/env.d.ts.map +1 -1
  12. package/build/lib/env.js +13 -50
  13. package/build/lib/env.js.map +1 -1
  14. package/build/lib/fs.d.ts +109 -148
  15. package/build/lib/fs.d.ts.map +1 -1
  16. package/build/lib/fs.js +88 -188
  17. package/build/lib/fs.js.map +1 -1
  18. package/build/lib/image-util.d.ts +7 -6
  19. package/build/lib/image-util.d.ts.map +1 -1
  20. package/build/lib/image-util.js +9 -6
  21. package/build/lib/image-util.js.map +1 -1
  22. package/build/lib/index.d.ts +19 -17
  23. package/build/lib/index.d.ts.map +1 -1
  24. package/build/lib/logger.d.ts +1 -1
  25. package/build/lib/logger.d.ts.map +1 -1
  26. package/build/lib/logger.js +1 -1
  27. package/build/lib/logger.js.map +1 -1
  28. package/build/lib/logging.d.ts +7 -15
  29. package/build/lib/logging.d.ts.map +1 -1
  30. package/build/lib/logging.js +36 -62
  31. package/build/lib/logging.js.map +1 -1
  32. package/build/lib/mjpeg.d.ts +19 -56
  33. package/build/lib/mjpeg.d.ts.map +1 -1
  34. package/build/lib/mjpeg.js +53 -76
  35. package/build/lib/mjpeg.js.map +1 -1
  36. package/build/lib/mkdirp.d.ts +4 -1
  37. package/build/lib/mkdirp.d.ts.map +1 -1
  38. package/build/lib/mkdirp.js +1 -2
  39. package/build/lib/mkdirp.js.map +1 -1
  40. package/build/lib/net.d.ts +52 -90
  41. package/build/lib/net.d.ts.map +1 -1
  42. package/build/lib/net.js +104 -193
  43. package/build/lib/net.js.map +1 -1
  44. package/build/lib/node.d.ts +16 -17
  45. package/build/lib/node.d.ts.map +1 -1
  46. package/build/lib/node.js +106 -111
  47. package/build/lib/node.js.map +1 -1
  48. package/build/lib/npm.d.ts +65 -86
  49. package/build/lib/npm.d.ts.map +1 -1
  50. package/build/lib/npm.js +59 -117
  51. package/build/lib/npm.js.map +1 -1
  52. package/build/lib/plist.d.ts +36 -29
  53. package/build/lib/plist.d.ts.map +1 -1
  54. package/build/lib/plist.js +62 -59
  55. package/build/lib/plist.js.map +1 -1
  56. package/build/lib/process.d.ts +19 -2
  57. package/build/lib/process.d.ts.map +1 -1
  58. package/build/lib/process.js +24 -7
  59. package/build/lib/process.js.map +1 -1
  60. package/build/lib/system.d.ts +41 -6
  61. package/build/lib/system.d.ts.map +1 -1
  62. package/build/lib/system.js +46 -11
  63. package/build/lib/system.js.map +1 -1
  64. package/build/lib/tempdir.d.ts +26 -49
  65. package/build/lib/tempdir.d.ts.map +1 -1
  66. package/build/lib/tempdir.js +41 -73
  67. package/build/lib/tempdir.js.map +1 -1
  68. package/build/lib/timing.d.ts +28 -22
  69. package/build/lib/timing.d.ts.map +1 -1
  70. package/build/lib/timing.js +16 -17
  71. package/build/lib/timing.js.map +1 -1
  72. package/build/lib/util.d.ts +164 -181
  73. package/build/lib/util.d.ts.map +1 -1
  74. package/build/lib/util.js +193 -247
  75. package/build/lib/util.js.map +1 -1
  76. package/build/lib/zip.d.ts +81 -139
  77. package/build/lib/zip.d.ts.map +1 -1
  78. package/build/lib/zip.js +210 -258
  79. package/build/lib/zip.js.map +1 -1
  80. package/lib/console.ts +139 -0
  81. package/lib/{doctor.js → doctor.ts} +6 -20
  82. package/lib/{env.js → env.ts} +31 -59
  83. package/lib/fs.ts +453 -0
  84. package/lib/image-util.ts +40 -0
  85. package/lib/index.ts +1 -0
  86. package/lib/{logger.js → logger.ts} +1 -1
  87. package/lib/logging.ts +157 -0
  88. package/lib/mjpeg.ts +186 -0
  89. package/lib/{mkdirp.js → mkdirp.ts} +2 -2
  90. package/lib/net.ts +305 -0
  91. package/lib/{node.js → node.ts} +134 -133
  92. package/lib/npm.ts +291 -0
  93. package/lib/plist.ts +187 -0
  94. package/lib/process.ts +62 -0
  95. package/lib/system.ts +95 -0
  96. package/lib/tempdir.ts +115 -0
  97. package/lib/{timing.js → timing.ts} +28 -33
  98. package/lib/util.ts +561 -0
  99. package/lib/{zip.js → zip.ts} +341 -296
  100. package/package.json +20 -22
  101. package/tsconfig.json +3 -5
  102. package/index.js +0 -1
  103. package/lib/console.js +0 -173
  104. package/lib/fs.js +0 -496
  105. package/lib/image-util.js +0 -32
  106. package/lib/logging.js +0 -145
  107. package/lib/mjpeg.js +0 -207
  108. package/lib/net.js +0 -336
  109. package/lib/npm.js +0 -310
  110. package/lib/plist.js +0 -182
  111. package/lib/process.js +0 -46
  112. package/lib/system.js +0 -48
  113. package/lib/tempdir.js +0 -131
  114. package/lib/util.js +0 -584
package/build/lib/zip.js CHANGED
@@ -37,18 +37,19 @@ 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 bluebird_1 = __importDefault(require("bluebird"));
46
+ const node_util_1 = require("node:util");
47
47
  const yauzl = __importStar(require("yauzl"));
48
48
  const archiver_1 = __importDefault(require("archiver"));
49
49
  const node_fs_1 = require("node:fs");
50
50
  const node_path_1 = __importDefault(require("node:path"));
51
51
  const node_stream_1 = __importDefault(require("node:stream"));
52
+ const promises_1 = require("node:stream/promises");
52
53
  const fs_1 = require("./fs");
53
54
  const system_1 = require("./system");
54
55
  const base64_stream_1 = require("base64-stream");
@@ -57,162 +58,19 @@ 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(node_stream_1.default.pipeline);
61
+ const openZip = (0, node_util_1.promisify)(yauzl.open);
68
62
  const ZIP_MAGIC = 'PK';
69
- const IFMT = 61440;
70
- const IFDIR = 16384;
71
- const IFLNK = 40960;
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 = node_path_1.default.dirname(node_path_1.default.join(dir, fileName));
115
- try {
116
- await fs_1.fs.mkdir(destDir, { recursive: true });
117
- const canonicalDestDir = await fs_1.fs.realpath(destDir);
118
- const relativeDestDir = node_path_1.default.relative(dir, canonicalDestDir);
119
- if (relativeDestDir.split(node_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 = node_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 : node_path_1.default.dirname(dest);
153
- const mkdirOptions = { recursive: true };
154
- if (isDir) {
155
- mkdirOptions.mode = procMode;
156
- }
157
- await fs_1.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_1.fs.symlink(link, dest);
168
- }
169
- else {
170
- await pipeline(readStream, fs_1.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 {string} zipFilePath The full path to the source ZIP file
212
- * @param {string} destDir The full path to the destination folder
213
- * @param {ExtractAllOptions} [opts]
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 = /** @type {ExtractAllOptions} */ ({})) {
73
+ async function extractAllTo(zipFilePath, destDir, opts = {}) {
216
74
  if (!node_path_1.default.isAbsolute(destDir)) {
217
75
  throw new Error(`Target path '${destDir}' is expected to be absolute`);
218
76
  }
@@ -233,52 +91,19 @@ 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
- * @param {yauzl.ZipFile} zipFile The source ZIP stream
277
- * @param {yauzl.Entry} entry The entry instance
278
- * @param {string} destDir The full path to the destination folder
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
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
109
  if (!(await fs_1.fs.exists(dstPath))) {
@@ -291,42 +116,33 @@ async function _extractEntryTo(zipFile, entry, destDir) {
291
116
  }
292
117
  // Create a write stream
293
118
  const writeStream = (0, node_fs_1.createWriteStream)(dstPath, { flags: 'w' });
294
- const writeStreamPromise = new bluebird_1.default((resolve, reject) => {
119
+ const writeStreamPromise = new Promise((resolve, reject) => {
295
120
  writeStream.once('finish', resolve);
296
121
  writeStream.once('error', reject);
297
122
  });
298
- // Create zipReadStream and pipe data to the write stream
299
- // (for some odd reason B.promisify doesn't work on zipfile.openReadStream, it causes an error 'closed')
300
- const zipReadStream = await new bluebird_1.default((resolve, reject) => {
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
- return await bluebird_1.default.all([zipReadStreamPromise, writeStreamPromise]);
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 {string} zipFilePath The full path to the source ZIP file
321
- * @param {function} onEntry Callback when entry is read.
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 bi terminated as soon as
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 bluebird_1.default((resolve, reject) => {
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,34 +159,19 @@ async function readEntries(zipFilePath, onEntry) {
343
159
  });
344
160
  zipfile.readEntry();
345
161
  // Wait for the entries to finish being iterated through
346
- return await zipReadStreamPromise;
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 {string} srcPath The full path to the folder or file being zipped
367
- * @param {ZipOptions} opts Zipping options
368
- * @returns {Promise<Buffer>} Zipped (and encoded if `encodeToBase64` is truthy)
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 = /** @type {ZipOptions} */ ({})) {
174
+ async function toInMemoryZip(srcPath, opts = {}) {
374
175
  if (!(await fs_1.fs.exists(srcPath))) {
375
176
  throw new Error(`No such file or folder: ${srcPath}`);
376
177
  }
@@ -379,7 +180,7 @@ async function toInMemoryZip(srcPath, opts = /** @type {ZipOptions} */ ({})) {
379
180
  let resultBuffersSize = 0;
380
181
  // Create a writable stream that zip buffers will be streamed to
381
182
  const resultWriteStream = new node_stream_1.default.Writable({
382
- write: (buffer, encoding, next) => {
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 bluebird_1.default((resolve, reject) => {
199
+ const resultWriteStreamPromise = new Promise((resolve, reject) => {
399
200
  resultWriteStream.once('error', (e) => {
400
201
  if (base64EncoderStream) {
401
202
  archive.unpipe(base64EncoderStream);
@@ -413,7 +214,7 @@ async function toInMemoryZip(srcPath, opts = /** @type {ZipOptions} */ ({})) {
413
214
  resolve();
414
215
  });
415
216
  });
416
- const archiveStreamPromise = new bluebird_1.default((resolve, reject) => {
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
  });
@@ -435,7 +236,7 @@ async function toInMemoryZip(srcPath, opts = /** @type {ZipOptions} */ ({})) {
435
236
  }
436
237
  archive.finalize();
437
238
  // Wait for the streams to finish
438
- await bluebird_1.default.all([archiveStreamPromise, resultWriteStreamPromise]);
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)}' ` +
@@ -449,7 +250,7 @@ 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 {string} filePath - Full path to the file
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) {
@@ -475,42 +276,30 @@ async function assertValidZip(filePath) {
475
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 {string} dstPath - The resulting archive path
494
- * @param {ZipSourceOptions} src - Source options
495
- * @param {ZipCompressionOptions} opts - Compression options
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 = /** @type {ZipSourceOptions} */ ({}), opts = /** @type {ZipCompressionOptions} */ ({})) {
287
+ async function toArchive(dstPath, src = {}, opts = {}) {
499
288
  const { level = 9 } = opts;
500
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 stream = fs_1.fs.createWriteStream(dstPath);
503
- return await new bluebird_1.default((resolve, reject) => {
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(stream);
511
- stream
299
+ .pipe(outStream);
300
+ outStream
512
301
  .on('error', (e) => {
513
- archive.unpipe(stream);
302
+ archive.unpipe(outStream);
514
303
  archive.abort();
515
304
  archive.destroy();
516
305
  reject(e);
@@ -519,13 +308,176 @@ 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 {Promise<string>} Full Path to the executable
480
+ * @returns Full Path to the executable
529
481
  */
530
482
  async function getExecutablePath(binaryName) {
531
483
  const fullPath = await fs_1.fs.which(binaryName);