@appium/support 7.1.0 → 7.1.1

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/lib/zip.ts CHANGED
@@ -43,6 +43,210 @@ export interface ExtractAllOptions {
43
43
  useSystemUnzip?: boolean;
44
44
  }
45
45
 
46
+ export interface ZipEntry {
47
+ /** The actual entry instance */
48
+ entry: yauzl.Entry;
49
+ /**
50
+ * Async function which accepts the destination folder path
51
+ * and extracts this entry into it.
52
+ */
53
+ extractEntryTo: (destDir: string) => Promise<void>;
54
+ }
55
+
56
+ export interface ZipOptions {
57
+ /** Whether to encode the resulting archive to a base64-encoded string */
58
+ encodeToBase64?: boolean;
59
+ /** Whether to log the actual archiver performance */
60
+ isMetered?: boolean;
61
+ /**
62
+ * The maximum size of the resulting archive in bytes.
63
+ * This is set to 1GB by default, because Appium limits the maximum HTTP body size to 1GB.
64
+ * Also, the NodeJS heap size must be enough to keep the resulting object
65
+ * (usually this size is limited to 1.4 GB)
66
+ */
67
+ maxSize?: number;
68
+ /**
69
+ * The compression level.
70
+ * The maximum level is 9 (the best compression, worst performance).
71
+ * The minimum compression level is 0 (no compression).
72
+ */
73
+ level?: number;
74
+ }
75
+
76
+ export interface ZipCompressionOptions {
77
+ /**
78
+ * Compression level in range 0..9
79
+ * (greater numbers mean better compression, but longer processing time)
80
+ */
81
+ level?: number;
82
+ }
83
+
84
+ export interface ZipSourceOptions {
85
+ /** GLOB pattern for compression */
86
+ pattern?: string;
87
+ /** The source root folder (the parent folder of the destination file by default) */
88
+ cwd?: string;
89
+ /** The list of ignored patterns */
90
+ ignore?: string[];
91
+ }
92
+
93
+ interface ZipExtractorOptions {
94
+ dir: string;
95
+ fileNamesEncoding?: BufferEncoding;
96
+ defaultDirMode?: string;
97
+ defaultFileMode?: string;
98
+ }
99
+
100
+ // This class is mostly copied from https://github.com/maxogden/extract-zip/blob/master/index.js
101
+ class ZipExtractor {
102
+ zipfile!: yauzl.ZipFile;
103
+ private readonly zipPath: string;
104
+ private readonly opts: ZipExtractorOptions;
105
+ private canceled = false;
106
+
107
+ constructor(sourcePath: string, opts: ZipExtractorOptions) {
108
+ this.zipPath = sourcePath;
109
+ this.opts = opts;
110
+ }
111
+
112
+ async extract(): Promise<void> {
113
+ const {dir, fileNamesEncoding} = this.opts;
114
+ this.zipfile = await openZip(this.zipPath, {
115
+ lazyEntries: true,
116
+ // https://github.com/thejoshwolfe/yauzl/commit/cc7455ac789ba84973184e5ebde0581cdc4c3b39#diff-04c6e90faac2675aa89e2176d2eec7d8R95
117
+ decodeStrings: !fileNamesEncoding,
118
+ });
119
+ this.canceled = false;
120
+
121
+ return new Promise<void>((resolve, reject) => {
122
+ this.zipfile.on('error', (err: Error) => {
123
+ this.canceled = true;
124
+ reject(err);
125
+ });
126
+ this.zipfile.readEntry();
127
+
128
+ this.zipfile.on('close', () => {
129
+ if (!this.canceled) {
130
+ resolve();
131
+ }
132
+ });
133
+
134
+ this.zipfile.on('entry', async (entry: yauzl.Entry) => {
135
+ if (this.canceled) {
136
+ return;
137
+ }
138
+
139
+ const fileName = this.extractFileName(entry);
140
+ if (fileName.startsWith('__MACOSX/')) {
141
+ this.zipfile.readEntry();
142
+ return;
143
+ }
144
+
145
+ const destDir = path.dirname(path.join(dir, fileName));
146
+ try {
147
+ await fs.mkdir(destDir, {recursive: true});
148
+
149
+ const canonicalDestDir = await fs.realpath(destDir);
150
+ const relativeDestDir = path.relative(dir, canonicalDestDir);
151
+
152
+ if (relativeDestDir.split(path.sep).includes('..')) {
153
+ throw new Error(
154
+ `Out of bound path "${canonicalDestDir}" found while processing file ${fileName}`
155
+ );
156
+ }
157
+
158
+ await this.extractEntry(entry);
159
+ this.zipfile.readEntry();
160
+ } catch (err) {
161
+ this.canceled = true;
162
+ this.zipfile.close();
163
+ reject(err);
164
+ }
165
+ });
166
+ });
167
+ }
168
+
169
+ private extractFileName(entry: yauzl.Entry): string {
170
+ if (Buffer.isBuffer(entry.fileName)) {
171
+ return entry.fileName.toString(this.opts.fileNamesEncoding);
172
+ }
173
+ return entry.fileName;
174
+ }
175
+
176
+ private async extractEntry(entry: yauzl.Entry): Promise<void> {
177
+ if (this.canceled) {
178
+ return;
179
+ }
180
+
181
+ const {dir} = this.opts;
182
+
183
+ const fileName = this.extractFileName(entry);
184
+ const dest = path.join(dir, fileName);
185
+
186
+ // convert external file attr int into a fs stat mode int
187
+ const mode = (entry.externalFileAttributes >> 16) & 0xffff;
188
+ // check if it's a symlink or dir (using stat mode constants)
189
+ const isSymlink = (mode & IFMT) === IFLNK;
190
+ const isDir =
191
+ (mode & IFMT) === IFDIR ||
192
+ // Failsafe, borrowed from jsZip
193
+ fileName.endsWith('/') ||
194
+ // check for windows weird way of specifying a directory
195
+ // https://github.com/maxogden/extract-zip/issues/13#issuecomment-154494566
196
+ (entry.versionMadeBy >> 8 === 0 && entry.externalFileAttributes === 16);
197
+ const procMode = this.getExtractedMode(mode, isDir) & 0o777;
198
+ // always ensure folders are created
199
+ const destDir = isDir ? dest : path.dirname(dest);
200
+ const mkdirOptions: Parameters<typeof fs.mkdir>[1] = {recursive: true};
201
+ if (isDir) {
202
+ mkdirOptions.mode = procMode;
203
+ }
204
+ await fs.mkdir(destDir, mkdirOptions);
205
+ if (isDir) {
206
+ return;
207
+ }
208
+
209
+ const openReadStream = promisify(
210
+ this.zipfile.openReadStream.bind(this.zipfile)
211
+ ) as (entry: yauzl.Entry) => Promise<NodeJS.ReadableStream>;
212
+ const readStream = await openReadStream(entry);
213
+ if (isSymlink) {
214
+ const link = await getStream(readStream);
215
+ await fs.symlink(link, dest);
216
+ } else {
217
+ await pipeline(readStream, fs.createWriteStream(dest, {mode: procMode}));
218
+ }
219
+ }
220
+
221
+ private getExtractedMode(entryMode: number, isDir: boolean): number {
222
+ const {defaultDirMode, defaultFileMode} = this.opts;
223
+
224
+ let mode = entryMode;
225
+ // Set defaults, if necessary
226
+ if (mode === 0) {
227
+ if (isDir) {
228
+ if (defaultDirMode) {
229
+ mode = parseInt(defaultDirMode, 10);
230
+ }
231
+
232
+ if (!mode) {
233
+ mode = 0o755;
234
+ }
235
+ } else {
236
+ if (defaultFileMode) {
237
+ mode = parseInt(defaultFileMode, 10);
238
+ }
239
+
240
+ if (!mode) {
241
+ mode = 0o644;
242
+ }
243
+ }
244
+ }
245
+
246
+ return mode;
247
+ }
248
+ }
249
+
46
250
  /**
47
251
  * Extract zipfile to a directory
48
252
  *
@@ -127,16 +331,6 @@ export async function _extractEntryTo(
127
331
  await Promise.all([zipReadStreamPromise, writeStreamPromise]);
128
332
  }
129
333
 
130
- export interface ZipEntry {
131
- /** The actual entry instance */
132
- entry: yauzl.Entry;
133
- /**
134
- * Async function which accepts the destination folder path
135
- * and extracts this entry into it.
136
- */
137
- extractEntryTo: (destDir: string) => Promise<void>;
138
- }
139
-
140
334
  /**
141
335
  * Get entries for a zip folder
142
336
  *
@@ -174,26 +368,6 @@ export async function readEntries(
174
368
  await zipReadStreamPromise;
175
369
  }
176
370
 
177
- export interface ZipOptions {
178
- /** Whether to encode the resulting archive to a base64-encoded string */
179
- encodeToBase64?: boolean;
180
- /** Whether to log the actual archiver performance */
181
- isMetered?: boolean;
182
- /**
183
- * The maximum size of the resulting archive in bytes.
184
- * This is set to 1GB by default, because Appium limits the maximum HTTP body size to 1GB.
185
- * Also, the NodeJS heap size must be enough to keep the resulting object
186
- * (usually this size is limited to 1.4 GB)
187
- */
188
- maxSize?: number;
189
- /**
190
- * The compression level.
191
- * The maximum level is 9 (the best compression, worst performance).
192
- * The minimum compression level is 0 (no compression).
193
- */
194
- level?: number;
195
- }
196
-
197
371
  /**
198
372
  * Converts contents of local directory to an in-memory .zip buffer
199
373
  *
@@ -276,10 +450,8 @@ export async function toInMemoryZip(
276
450
  } else {
277
451
  archive.pipe(resultWriteStream);
278
452
  }
279
- archive.finalize();
280
453
 
281
- // Wait for the streams to finish
282
- await Promise.all([archiveStreamPromise, resultWriteStreamPromise]);
454
+ await Promise.all([archive.finalize(), archiveStreamPromise, resultWriteStreamPromise]);
283
455
 
284
456
  if (timer) {
285
457
  log.debug(
@@ -326,23 +498,6 @@ export async function assertValidZip(filePath: string): Promise<boolean> {
326
498
  }
327
499
  }
328
500
 
329
- export interface ZipCompressionOptions {
330
- /**
331
- * Compression level in range 0..9
332
- * (greater numbers mean better compression, but longer processing time)
333
- */
334
- level?: number;
335
- }
336
-
337
- export interface ZipSourceOptions {
338
- /** GLOB pattern for compression */
339
- pattern?: string;
340
- /** The source root folder (the parent folder of the destination file by default) */
341
- cwd?: string;
342
- /** The list of ignored patterns */
343
- ignore?: string[];
344
- }
345
-
346
501
  /**
347
502
  * Creates an archive based on the given glob pattern
348
503
  *
@@ -361,6 +516,16 @@ export async function toArchive(
361
516
  const archive = archiver('zip', {zlib: {level}});
362
517
  const outStream = fs.createWriteStream(dstPath);
363
518
  await new Promise<void>((resolve, reject) => {
519
+ const outFinished = new Promise<void>((_resolve, _reject) => {
520
+ outStream.once('error', (e: Error) => {
521
+ archive.unpipe(outStream);
522
+ archive.abort();
523
+ archive.destroy();
524
+ _reject(e);
525
+ });
526
+ outStream.once('finish', () => _resolve());
527
+ });
528
+
364
529
  archive
365
530
  .glob(pattern, {
366
531
  cwd,
@@ -368,173 +533,11 @@ export async function toArchive(
368
533
  })
369
534
  .on('error', reject)
370
535
  .pipe(outStream);
371
- outStream
372
- .on('error', (e: Error) => {
373
- archive.unpipe(outStream);
374
- archive.abort();
375
- archive.destroy();
376
- reject(e);
377
- })
378
- .on('finish', resolve);
379
- archive.finalize();
380
- });
381
- }
382
536
 
383
- interface ZipExtractorOptions {
384
- dir: string;
385
- fileNamesEncoding?: BufferEncoding;
386
- defaultDirMode?: string;
387
- defaultFileMode?: string;
388
- }
389
-
390
- // This class is mostly copied from https://github.com/maxogden/extract-zip/blob/master/index.js
391
- class ZipExtractor {
392
- zipfile!: yauzl.ZipFile;
393
- private readonly zipPath: string;
394
- private readonly opts: ZipExtractorOptions;
395
- private canceled = false;
396
-
397
- constructor(sourcePath: string, opts: ZipExtractorOptions) {
398
- this.zipPath = sourcePath;
399
- this.opts = opts;
400
- }
401
-
402
- private extractFileName(entry: yauzl.Entry): string {
403
- if (Buffer.isBuffer(entry.fileName)) {
404
- return entry.fileName.toString(this.opts.fileNamesEncoding);
405
- }
406
- return entry.fileName;
407
- }
408
-
409
- async extract(): Promise<void> {
410
- const {dir, fileNamesEncoding} = this.opts;
411
- this.zipfile = await openZip(this.zipPath, {
412
- lazyEntries: true,
413
- // https://github.com/thejoshwolfe/yauzl/commit/cc7455ac789ba84973184e5ebde0581cdc4c3b39#diff-04c6e90faac2675aa89e2176d2eec7d8R95
414
- decodeStrings: !fileNamesEncoding,
415
- });
416
- this.canceled = false;
417
-
418
- return new Promise<void>((resolve, reject) => {
419
- this.zipfile.on('error', (err: Error) => {
420
- this.canceled = true;
421
- reject(err);
422
- });
423
- this.zipfile.readEntry();
424
-
425
- this.zipfile.on('close', () => {
426
- if (!this.canceled) {
427
- resolve();
428
- }
429
- });
430
-
431
- this.zipfile.on('entry', async (entry: yauzl.Entry) => {
432
- if (this.canceled) {
433
- return;
434
- }
435
-
436
- const fileName = this.extractFileName(entry);
437
- if (fileName.startsWith('__MACOSX/')) {
438
- this.zipfile.readEntry();
439
- return;
440
- }
441
-
442
- const destDir = path.dirname(path.join(dir, fileName));
443
- try {
444
- await fs.mkdir(destDir, {recursive: true});
445
-
446
- const canonicalDestDir = await fs.realpath(destDir);
447
- const relativeDestDir = path.relative(dir, canonicalDestDir);
448
-
449
- if (relativeDestDir.split(path.sep).includes('..')) {
450
- throw new Error(
451
- `Out of bound path "${canonicalDestDir}" found while processing file ${fileName}`
452
- );
453
- }
454
-
455
- await this.extractEntry(entry);
456
- this.zipfile.readEntry();
457
- } catch (err) {
458
- this.canceled = true;
459
- this.zipfile.close();
460
- reject(err);
461
- }
462
- });
463
- });
464
- }
465
-
466
- private async extractEntry(entry: yauzl.Entry): Promise<void> {
467
- if (this.canceled) {
468
- return;
469
- }
470
-
471
- const {dir} = this.opts;
472
-
473
- const fileName = this.extractFileName(entry);
474
- const dest = path.join(dir, fileName);
475
-
476
- // convert external file attr int into a fs stat mode int
477
- const mode = (entry.externalFileAttributes >> 16) & 0xffff;
478
- // check if it's a symlink or dir (using stat mode constants)
479
- const isSymlink = (mode & IFMT) === IFLNK;
480
- const isDir =
481
- (mode & IFMT) === IFDIR ||
482
- // Failsafe, borrowed from jsZip
483
- fileName.endsWith('/') ||
484
- // check for windows weird way of specifying a directory
485
- // https://github.com/maxogden/extract-zip/issues/13#issuecomment-154494566
486
- (entry.versionMadeBy >> 8 === 0 && entry.externalFileAttributes === 16);
487
- const procMode = this.getExtractedMode(mode, isDir) & 0o777;
488
- // always ensure folders are created
489
- const destDir = isDir ? dest : path.dirname(dest);
490
- const mkdirOptions: Parameters<typeof fs.mkdir>[1] = {recursive: true};
491
- if (isDir) {
492
- mkdirOptions.mode = procMode;
493
- }
494
- await fs.mkdir(destDir, mkdirOptions);
495
- if (isDir) {
496
- return;
497
- }
498
-
499
- const openReadStream = promisify(
500
- this.zipfile.openReadStream.bind(this.zipfile)
501
- ) as (entry: yauzl.Entry) => Promise<NodeJS.ReadableStream>;
502
- const readStream = await openReadStream(entry);
503
- if (isSymlink) {
504
- const link = await getStream(readStream);
505
- await fs.symlink(link, dest);
506
- } else {
507
- await pipeline(readStream, fs.createWriteStream(dest, {mode: procMode}));
508
- }
509
- }
510
-
511
- private getExtractedMode(entryMode: number, isDir: boolean): number {
512
- const {defaultDirMode, defaultFileMode} = this.opts;
513
-
514
- let mode = entryMode;
515
- // Set defaults, if necessary
516
- if (mode === 0) {
517
- if (isDir) {
518
- if (defaultDirMode) {
519
- mode = parseInt(defaultDirMode, 10);
520
- }
521
-
522
- if (!mode) {
523
- mode = 0o755;
524
- }
525
- } else {
526
- if (defaultFileMode) {
527
- mode = parseInt(defaultFileMode, 10);
528
- }
529
-
530
- if (!mode) {
531
- mode = 0o644;
532
- }
533
- }
534
- }
535
-
536
- return mode;
537
- }
537
+ void Promise.all([archive.finalize(), outFinished])
538
+ .then(() => resolve())
539
+ .catch(reject);
540
+ });
538
541
  }
539
542
 
540
543
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appium/support",
3
- "version": "7.1.0",
3
+ "version": "7.1.1",
4
4
  "description": "Support libs used across Appium packages",
5
5
  "keywords": [
6
6
  "automation",
@@ -40,9 +40,9 @@
40
40
  "test:unit": "mocha \"./test/unit/**/*.spec.ts\""
41
41
  },
42
42
  "dependencies": {
43
- "@appium/logger": "2.0.6",
43
+ "@appium/logger": "2.0.7",
44
44
  "@appium/tsconfig": "1.1.2",
45
- "@appium/types": "1.3.0",
45
+ "@appium/types": "1.3.1",
46
46
  "@colors/colors": "1.6.0",
47
47
  "archiver": "7.0.1",
48
48
  "asyncbox": "6.1.0",
@@ -69,9 +69,9 @@
69
69
  "semver": "7.7.4",
70
70
  "shell-quote": "1.8.3",
71
71
  "supports-color": "10.2.2",
72
- "teen_process": "4.1.0",
73
- "type-fest": "5.5.0",
74
- "uuid": "13.0.0",
72
+ "teen_process": "4.1.1",
73
+ "type-fest": "5.6.0",
74
+ "uuid": "14.0.0",
75
75
  "which": "6.0.1",
76
76
  "yauzl": "3.3.0"
77
77
  },
@@ -85,5 +85,5 @@
85
85
  "publishConfig": {
86
86
  "access": "public"
87
87
  },
88
- "gitHead": "7a8965f5c30ffec2ad04ce75903b3960537cef06"
88
+ "gitHead": "17f84265d10944fec06ae7684ae8ad77d1224b50"
89
89
  }
package/tsconfig.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "extends": "@appium/tsconfig/tsconfig.json",
3
3
  "compilerOptions": {
4
+ "rootDir": ".",
4
5
  "outDir": "build",
5
6
  "paths": {"@appium/types": ["../types"]},
6
7
  "types": ["mocha", "chai", "chai-as-promised", "node"]