@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.
- package/LICENSE +201 -0
- package/build/lib/console.d.ts +42 -88
- package/build/lib/console.d.ts.map +1 -1
- package/build/lib/console.js +20 -80
- 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 +13 -50
- 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 +88 -188
- 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 +53 -76
- 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 +106 -111
- 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 +59 -117
- 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 +46 -11
- 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 +41 -73
- 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 +193 -247
- 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 +210 -258
- 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} +31 -59
- 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} +134 -133
- 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} +341 -296
- package/package.json +20 -22
- 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 -584
package/lib/tempdir.js
DELETED
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
/* This library is originated from temp.js at http://github.com/bruce/node-temp */
|
|
2
|
-
import {fs} from './fs';
|
|
3
|
-
import os from 'node:os';
|
|
4
|
-
import nodePath from 'node:path';
|
|
5
|
-
import {constants} from 'node:fs';
|
|
6
|
-
import log from './logger';
|
|
7
|
-
|
|
8
|
-
const RDWR_EXCL = constants.O_CREAT | constants.O_TRUNC | constants.O_RDWR | constants.O_EXCL;
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Generate a temporary directory in os.tempdir() or process.env.APPIUM_TMP_DIR.
|
|
12
|
-
* e.g.
|
|
13
|
-
* - No `process.env.APPIUM_TMP_DIR`: `/var/folders/34/2222sh8n27d6rcp7jqlkw8km0000gn/T/xxxxxxxx.yyyy`
|
|
14
|
-
* - With `process.env.APPIUM_TMP_DIR = '/path/to/root'`: `/path/to/root/xxxxxxxx.yyyy`
|
|
15
|
-
*
|
|
16
|
-
* @returns {Promise<string>} A path to the temporary directory
|
|
17
|
-
*/
|
|
18
|
-
async function tempDir() {
|
|
19
|
-
const now = new Date();
|
|
20
|
-
const filePath = nodePath.join(
|
|
21
|
-
process.env.APPIUM_TMP_DIR || os.tmpdir(),
|
|
22
|
-
[
|
|
23
|
-
now.getFullYear(),
|
|
24
|
-
now.getMonth(),
|
|
25
|
-
now.getDate(),
|
|
26
|
-
'-',
|
|
27
|
-
process.pid,
|
|
28
|
-
'-',
|
|
29
|
-
(Math.random() * 0x100000000 + 1).toString(36),
|
|
30
|
-
].join('')
|
|
31
|
-
);
|
|
32
|
-
// creates a temp directory using the date and a random string
|
|
33
|
-
|
|
34
|
-
await fs.mkdir(filePath, {recursive: true});
|
|
35
|
-
|
|
36
|
-
return filePath;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* @typedef Affixes
|
|
41
|
-
* @property {string} [prefix] - prefix of the temp directory name
|
|
42
|
-
* @property {string} [suffix] - suffix of the temp directory name
|
|
43
|
-
*/
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Generate a temporary directory in os.tempdir() or process.env.APPIUM_TMP_DIR
|
|
47
|
-
* with arbitrary prefix/suffix for the directory name.
|
|
48
|
-
*
|
|
49
|
-
* @param {string|Affixes} rawAffixes
|
|
50
|
-
* @param {string} [defaultPrefix]
|
|
51
|
-
* @returns {Promise<string>} A path to the temporary directory with rawAffixes and defaultPrefix
|
|
52
|
-
*/
|
|
53
|
-
async function path(rawAffixes, defaultPrefix) {
|
|
54
|
-
const affixes = parseAffixes(rawAffixes, defaultPrefix);
|
|
55
|
-
const name = `${affixes.prefix || ''}${affixes.suffix || ''}`;
|
|
56
|
-
const tempDirectory = await tempDir();
|
|
57
|
-
return nodePath.join(tempDirectory, name);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* @typedef OpenedAffixes
|
|
62
|
-
* @property {string} path - The path to file
|
|
63
|
-
* @property {number} fd - The file descriptor opened
|
|
64
|
-
*/
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Generate a temporary directory in os.tempdir() or process.env.APPIUM_TMP_DIR
|
|
68
|
-
* with arbitrary prefix/suffix for the directory name and return it as open.
|
|
69
|
-
*
|
|
70
|
-
* @param {Affixes} affixes
|
|
71
|
-
* @returns {Promise<OpenedAffixes>}
|
|
72
|
-
*/
|
|
73
|
-
async function open(affixes) {
|
|
74
|
-
const filePath = await path(affixes, 'f-');
|
|
75
|
-
try {
|
|
76
|
-
let fd = await fs.open(filePath, RDWR_EXCL, 0o600);
|
|
77
|
-
// opens the file in mode 384
|
|
78
|
-
return {path: filePath, fd};
|
|
79
|
-
} catch (err) {
|
|
80
|
-
throw log.errorWithException(err);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
*
|
|
86
|
-
* Returns prefix/suffix object
|
|
87
|
-
*
|
|
88
|
-
* @param {string|Affixes} rawAffixes
|
|
89
|
-
* @param {string} [defaultPrefix]
|
|
90
|
-
* @returns {Affixes}
|
|
91
|
-
*/
|
|
92
|
-
function parseAffixes(rawAffixes, defaultPrefix) {
|
|
93
|
-
/** @type {Affixes} */
|
|
94
|
-
let affixes = {};
|
|
95
|
-
if (rawAffixes) {
|
|
96
|
-
switch (typeof rawAffixes) {
|
|
97
|
-
case 'string':
|
|
98
|
-
affixes.prefix = rawAffixes;
|
|
99
|
-
break;
|
|
100
|
-
case 'object':
|
|
101
|
-
affixes = rawAffixes;
|
|
102
|
-
break;
|
|
103
|
-
default:
|
|
104
|
-
throw new Error(`Unknown affix declaration: ${affixes}`);
|
|
105
|
-
}
|
|
106
|
-
} else {
|
|
107
|
-
affixes.prefix = defaultPrefix;
|
|
108
|
-
}
|
|
109
|
-
return affixes;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const _static = tempDir();
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Returns a new path to a temporary directory
|
|
116
|
-
*
|
|
117
|
-
* @returns {string} A new tempDir() if tempRootDirectory is not provided
|
|
118
|
-
*/
|
|
119
|
-
const openDir = tempDir;
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Returns a path to a temporary directory which is defined as static in the same process
|
|
123
|
-
*
|
|
124
|
-
* @returns {Promise<string>} A temp directory path which is defined as static in the same process
|
|
125
|
-
*/
|
|
126
|
-
|
|
127
|
-
async function staticDir() {
|
|
128
|
-
return _static;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
export {open, path, openDir, staticDir};
|
package/lib/util.js
DELETED
|
@@ -1,584 +0,0 @@
|
|
|
1
|
-
import B from 'bluebird';
|
|
2
|
-
import _ from 'lodash';
|
|
3
|
-
import os from 'node:os';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import { fs } from './fs';
|
|
6
|
-
import * as semver from 'semver';
|
|
7
|
-
import {
|
|
8
|
-
// https://www.npmjs.com/package/shell-quote
|
|
9
|
-
quote as shellQuote,
|
|
10
|
-
parse as shellParse,
|
|
11
|
-
} from 'shell-quote';
|
|
12
|
-
import pluralizeLib from 'pluralize';
|
|
13
|
-
import stream from 'node:stream';
|
|
14
|
-
import {Base64Encode} from 'base64-stream';
|
|
15
|
-
import {
|
|
16
|
-
// https://www.npmjs.com/package/uuid
|
|
17
|
-
v1 as uuidV1,
|
|
18
|
-
v3 as uuidV3,
|
|
19
|
-
v4 as uuidV4,
|
|
20
|
-
v5 as uuidV5,
|
|
21
|
-
} from 'uuid';
|
|
22
|
-
import * as _lockfile from 'lockfile';
|
|
23
|
-
|
|
24
|
-
const W3C_WEB_ELEMENT_IDENTIFIER = 'element-6066-11e4-a52e-4f735466cecf';
|
|
25
|
-
const KiB = 1024;
|
|
26
|
-
const MiB = KiB * 1024;
|
|
27
|
-
const GiB = MiB * 1024;
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* @template {string} T
|
|
31
|
-
* @param {T} val
|
|
32
|
-
* @returns {val is NonEmptyString<T>}
|
|
33
|
-
*/
|
|
34
|
-
export function hasContent(val) {
|
|
35
|
-
return _.isString(val) && val !== '';
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* return true if the the value is not `undefined`, `null`, or `NaN`.
|
|
40
|
-
*
|
|
41
|
-
* XXX: `NaN` is not expressible in TypeScript.
|
|
42
|
-
* @template T
|
|
43
|
-
* @param {T} val
|
|
44
|
-
* @returns {val is NonNullable<T>}
|
|
45
|
-
*/
|
|
46
|
-
function hasValue(val) {
|
|
47
|
-
// avoid incorrectly evaluating `0` as false
|
|
48
|
-
if (_.isNumber(val)) {
|
|
49
|
-
return !_.isNaN(val);
|
|
50
|
-
}
|
|
51
|
-
return !_.isUndefined(val) && !_.isNull(val);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Escape spaces in string, for commandline calls
|
|
56
|
-
* @param {string} str - The string to escape spaces in
|
|
57
|
-
* @returns {string} The string with escaped spaces
|
|
58
|
-
*/
|
|
59
|
-
function escapeSpace(str) {
|
|
60
|
-
return str.split(/ /).join('\\ ');
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Escape special characters in string
|
|
65
|
-
* @param {string|any} str - The string to escape special characters in
|
|
66
|
-
* @param {string} quoteEscape - Whether to escape quotes
|
|
67
|
-
* @returns {string|any} The string with escaped special characters, or original value if not a string
|
|
68
|
-
*/
|
|
69
|
-
function escapeSpecialChars(str, quoteEscape) {
|
|
70
|
-
if (typeof str !== 'string') {
|
|
71
|
-
return str;
|
|
72
|
-
}
|
|
73
|
-
if (typeof quoteEscape === 'undefined') {
|
|
74
|
-
// @ts-ignore to set false to mark no quote escaping
|
|
75
|
-
quoteEscape = false;
|
|
76
|
-
}
|
|
77
|
-
str = str
|
|
78
|
-
.replace(/[\\]/g, '\\\\')
|
|
79
|
-
.replace(/[\/]/g, '\\/') // eslint-disable-line no-useless-escape
|
|
80
|
-
.replace(/[\b]/g, '\\b')
|
|
81
|
-
.replace(/[\f]/g, '\\f')
|
|
82
|
-
.replace(/[\n]/g, '\\n')
|
|
83
|
-
.replace(/[\r]/g, '\\r')
|
|
84
|
-
.replace(/[\t]/g, '\\t')
|
|
85
|
-
.replace(/[\"]/g, '\\"') // eslint-disable-line no-useless-escape
|
|
86
|
-
.replace(/\\'/g, "\\'");
|
|
87
|
-
if (quoteEscape) {
|
|
88
|
-
const re = new RegExp(quoteEscape, 'g');
|
|
89
|
-
str = str.replace(re, `\\${quoteEscape}`);
|
|
90
|
-
}
|
|
91
|
-
return str;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Get the local IP address of the machine
|
|
96
|
-
* @returns {string|undefined} The local IP address of the first external IPv4 interface, or undefined if not found
|
|
97
|
-
*/
|
|
98
|
-
function localIp() {
|
|
99
|
-
let ip = _.chain(os.networkInterfaces())
|
|
100
|
-
.values()
|
|
101
|
-
.flatten()
|
|
102
|
-
// @ts-ignore this filter works fine
|
|
103
|
-
.filter(({family, internal}) => family === 'IPv4' && internal === false)
|
|
104
|
-
.map('address')
|
|
105
|
-
.first()
|
|
106
|
-
.value();
|
|
107
|
-
return ip;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/*
|
|
111
|
-
* Creates a promise that is cancellable, and will timeout
|
|
112
|
-
* after `ms` delay
|
|
113
|
-
*/
|
|
114
|
-
function cancellableDelay(ms) {
|
|
115
|
-
let timer;
|
|
116
|
-
let resolve;
|
|
117
|
-
let reject;
|
|
118
|
-
|
|
119
|
-
const delay = new B.Promise((_resolve, _reject) => {
|
|
120
|
-
resolve = _resolve;
|
|
121
|
-
reject = _reject;
|
|
122
|
-
timer = setTimeout(function () {
|
|
123
|
-
resolve();
|
|
124
|
-
}, ms);
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
// override Bluebird's `cancel`, which does not work when using `await` on
|
|
128
|
-
// a promise, since `resolve`/`reject` are never called
|
|
129
|
-
delay.cancel = function () {
|
|
130
|
-
clearTimeout(timer);
|
|
131
|
-
reject(new B.CancellationError());
|
|
132
|
-
};
|
|
133
|
-
return delay;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function multiResolve(roots, ...args) {
|
|
137
|
-
return roots.map((root) => path.resolve(root, ...args));
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Parses an object if possible. Otherwise returns the object without parsing.
|
|
142
|
-
*
|
|
143
|
-
* @param {any} obj
|
|
144
|
-
* @returns {any}
|
|
145
|
-
*/
|
|
146
|
-
function safeJsonParse(obj) {
|
|
147
|
-
try {
|
|
148
|
-
return JSON.parse(obj);
|
|
149
|
-
} catch {
|
|
150
|
-
// ignore: this is not json parsable
|
|
151
|
-
return obj;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Stringifies the object passed in, converting Buffers into Strings for better
|
|
157
|
-
* display. This mimics JSON.stringify (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify)
|
|
158
|
-
* except the `replacer` argument can only be a function.
|
|
159
|
-
*
|
|
160
|
-
* @param {any} obj - the object to be serialized
|
|
161
|
-
* @param {((key:any, value:any) => any)?} replacer - function to transform the properties added to the
|
|
162
|
-
* serialized object
|
|
163
|
-
* @param {number|string|undefined} space - used to insert white space into the output JSON
|
|
164
|
-
* string for readability purposes. Defaults to 2
|
|
165
|
-
* @returns {string} - the JSON object serialized as a string
|
|
166
|
-
*/
|
|
167
|
-
function jsonStringify(obj, replacer = null, space = 2) {
|
|
168
|
-
// if no replacer is passed, or it is not a function, just use a pass-through
|
|
169
|
-
const replacerFunc = _.isFunction(replacer) ? replacer : (k, v) => v;
|
|
170
|
-
|
|
171
|
-
// Buffers cannot be serialized in a readable way
|
|
172
|
-
const bufferToJSON = Buffer.prototype.toJSON;
|
|
173
|
-
delete Buffer.prototype.toJSON;
|
|
174
|
-
try {
|
|
175
|
-
return JSON.stringify(
|
|
176
|
-
obj,
|
|
177
|
-
(key, value) => {
|
|
178
|
-
const updatedValue = Buffer.isBuffer(value) ? value.toString('utf8') : value;
|
|
179
|
-
return replacerFunc(key, updatedValue);
|
|
180
|
-
},
|
|
181
|
-
space
|
|
182
|
-
);
|
|
183
|
-
} finally {
|
|
184
|
-
// restore the function, so as to not break further serialization
|
|
185
|
-
Buffer.prototype.toJSON = bufferToJSON;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Removes the wrapper from element, if it exists.
|
|
191
|
-
* { ELEMENT: 4 } becomes 4
|
|
192
|
-
* { element-6066-11e4-a52e-4f735466cecf: 5 } becomes 5
|
|
193
|
-
* @param {import('@appium/types').Element|string} el
|
|
194
|
-
* @returns {string}
|
|
195
|
-
*/
|
|
196
|
-
function unwrapElement(el) {
|
|
197
|
-
for (const propName of [W3C_WEB_ELEMENT_IDENTIFIER, 'ELEMENT']) {
|
|
198
|
-
if (_.has(el, propName)) {
|
|
199
|
-
return /** @type {string} */ (el[propName]);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
return /** @type {string} */(el);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
*
|
|
207
|
-
* @param {string} elementId
|
|
208
|
-
* @returns {import('@appium/types').Element}
|
|
209
|
-
*/
|
|
210
|
-
function wrapElement(elementId) {
|
|
211
|
-
return {
|
|
212
|
-
ELEMENT: elementId,
|
|
213
|
-
[W3C_WEB_ELEMENT_IDENTIFIER]: elementId,
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/*
|
|
218
|
-
* Returns object consisting of all properties in the original element
|
|
219
|
-
* which were truthy given the predicate.
|
|
220
|
-
* If the predicate is
|
|
221
|
-
* * missing - it will remove all properties whose values are `undefined`
|
|
222
|
-
* * a scalar - it will test all properties' values against that value
|
|
223
|
-
* * a function - it will pass each value and the original object into the function
|
|
224
|
-
*/
|
|
225
|
-
function filterObject(obj, predicate) {
|
|
226
|
-
let newObj = _.clone(obj);
|
|
227
|
-
if (_.isUndefined(predicate)) {
|
|
228
|
-
// remove any element from the object whose value is undefined
|
|
229
|
-
predicate = (v) => !_.isUndefined(v);
|
|
230
|
-
} else if (!_.isFunction(predicate)) {
|
|
231
|
-
// make predicate into a function
|
|
232
|
-
const valuePredicate = predicate;
|
|
233
|
-
predicate = (v) => v === valuePredicate;
|
|
234
|
-
}
|
|
235
|
-
for (const key of Object.keys(obj)) {
|
|
236
|
-
if (!predicate(obj[key], obj)) {
|
|
237
|
-
delete newObj[key];
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
return newObj;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Converts number of bytes to a readable size string.
|
|
245
|
-
*
|
|
246
|
-
* @param {number|string} bytes - The actual number of bytes.
|
|
247
|
-
* @returns {string} The actual string representation, for example
|
|
248
|
-
* '1.00 KB' for '1024 B'
|
|
249
|
-
* @throws {Error} If bytes count cannot be converted to an integer or
|
|
250
|
-
* if it is less than zero.
|
|
251
|
-
*/
|
|
252
|
-
function toReadableSizeString(bytes) {
|
|
253
|
-
const intBytes = parseInt(String(bytes), 10);
|
|
254
|
-
if (isNaN(intBytes) || intBytes < 0) {
|
|
255
|
-
throw new Error(`Cannot convert '${bytes}' to a readable size format`);
|
|
256
|
-
}
|
|
257
|
-
if (intBytes >= GiB) {
|
|
258
|
-
return `${(intBytes / (GiB * 1.0)).toFixed(2)} GB`;
|
|
259
|
-
} else if (intBytes >= MiB) {
|
|
260
|
-
return `${(intBytes / (MiB * 1.0)).toFixed(2)} MB`;
|
|
261
|
-
} else if (intBytes >= KiB) {
|
|
262
|
-
return `${(intBytes / (KiB * 1.0)).toFixed(2)} KB`;
|
|
263
|
-
}
|
|
264
|
-
return `${intBytes} B`;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Checks whether the given path is a subpath of the
|
|
269
|
-
* particular root folder. Both paths can include .. and . specifiers
|
|
270
|
-
*
|
|
271
|
-
* @param {string} originalPath The absolute file/folder path
|
|
272
|
-
* @param {string} root The absolute root folder path
|
|
273
|
-
* @param {?boolean} forcePosix Set it to true if paths must be interpreted in POSIX format
|
|
274
|
-
* @returns {boolean} true if the given original path is the subpath of the root folder
|
|
275
|
-
* @throws {Error} if any of the given paths is not absolute
|
|
276
|
-
*/
|
|
277
|
-
function isSubPath(originalPath, root, forcePosix = null) {
|
|
278
|
-
const pathObj = forcePosix ? path.posix : path;
|
|
279
|
-
for (const p of [originalPath, root]) {
|
|
280
|
-
if (!pathObj.isAbsolute(p)) {
|
|
281
|
-
throw new Error(`'${p}' is expected to be an absolute path`);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
const normalizedRoot = pathObj.normalize(root);
|
|
285
|
-
const normalizedPath = pathObj.normalize(originalPath);
|
|
286
|
-
return normalizedPath.startsWith(normalizedRoot);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* Checks whether the given paths are pointing to the same file system
|
|
291
|
-
* destination.
|
|
292
|
-
*
|
|
293
|
-
* @param {string} path1 - Absolute or relative path to a file/folder
|
|
294
|
-
* @param {string} path2 - Absolute or relative path to a file/folder
|
|
295
|
-
* @param {...string} pathN - Zero or more absolute or relative paths to files/folders
|
|
296
|
-
* @returns {Promise<boolean>} true if all paths are pointing to the same file system item
|
|
297
|
-
*/
|
|
298
|
-
async function isSameDestination(path1, path2, ...pathN) {
|
|
299
|
-
const allPaths = [path1, path2, ...pathN];
|
|
300
|
-
if (!(await B.reduce(allPaths, async (a, b) => a && (await fs.exists(b)), true))) {
|
|
301
|
-
return false;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
const areAllItemsEqual = (arr) => !!arr.reduce((a, b) => (a === b ? a : NaN));
|
|
305
|
-
if (areAllItemsEqual(allPaths)) {
|
|
306
|
-
return true;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
let mapCb = async (x) =>
|
|
310
|
-
(
|
|
311
|
-
await fs.stat(x, {
|
|
312
|
-
bigint: true,
|
|
313
|
-
})
|
|
314
|
-
).ino;
|
|
315
|
-
return areAllItemsEqual(await B.map(allPaths, mapCb));
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
/**
|
|
319
|
-
* Coerces the given number/string to a valid version string
|
|
320
|
-
*
|
|
321
|
-
* @template {boolean} [Strict=true]
|
|
322
|
-
* @param {string} ver - Version string to coerce
|
|
323
|
-
* @param {Strict} [strict] - If `true` then an exception will be thrown
|
|
324
|
-
* if `ver` cannot be coerced
|
|
325
|
-
* @returns {Strict extends true ? string : string|null} Coerced version number or null if the string cannot be
|
|
326
|
-
* coerced and strict mode is disabled
|
|
327
|
-
* @throws {Error} if strict mode is enabled and `ver` cannot be coerced
|
|
328
|
-
*/
|
|
329
|
-
function coerceVersion(ver, strict = /** @type {Strict} */ (true)) {
|
|
330
|
-
// First try to parse as-is, then coerce if needed
|
|
331
|
-
let result = semver.valid(`${ver}`);
|
|
332
|
-
if (!result) {
|
|
333
|
-
result = semver.valid(semver.coerce(`${ver}`));
|
|
334
|
-
}
|
|
335
|
-
if (strict && !result) {
|
|
336
|
-
throw new Error(`'${ver}' cannot be coerced to a valid version number`);
|
|
337
|
-
}
|
|
338
|
-
return /** @type {Strict extends true ? string : string?} */ (result);
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
const SUPPORTED_OPERATORS = ['==', '!=', '>', '<', '>=', '<=', '='];
|
|
342
|
-
|
|
343
|
-
/**
|
|
344
|
-
* Compares two version strings
|
|
345
|
-
*
|
|
346
|
-
* @param {string} ver1 - The first version number to compare. Should be a valid
|
|
347
|
-
* version number supported by semver parser.
|
|
348
|
-
* @param {string} ver2 - The second version number to compare. Should be a valid
|
|
349
|
-
* version number supported by semver parser.
|
|
350
|
-
* @param {string} operator - One of supported version number operators:
|
|
351
|
-
* ==, !=, >, <, <=, >=, =
|
|
352
|
-
* @returns {boolean} true or false depending on the actual comparison result
|
|
353
|
-
* @throws {Error} if an unsupported operator is supplied or any of the supplied
|
|
354
|
-
* version strings cannot be coerced
|
|
355
|
-
*/
|
|
356
|
-
function compareVersions(ver1, operator, ver2) {
|
|
357
|
-
if (!SUPPORTED_OPERATORS.includes(operator)) {
|
|
358
|
-
throw new Error(
|
|
359
|
-
`The '${operator}' comparison operator is not supported. ` +
|
|
360
|
-
`Only '${JSON.stringify(SUPPORTED_OPERATORS)}' operators are supported`
|
|
361
|
-
);
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
const semverOperator = ['==', '!='].includes(operator) ? '=' : operator;
|
|
365
|
-
const result = semver.satisfies(coerceVersion(ver1), `${semverOperator}${coerceVersion(ver2)}`);
|
|
366
|
-
return operator === '!=' ? !result : result;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
/**
|
|
370
|
-
* Add appropriate quotes to command arguments. See https://github.com/substack/node-shell-quote
|
|
371
|
-
* for more details
|
|
372
|
-
*
|
|
373
|
-
* @param {string|string[]} args - The arguments that will be parsed
|
|
374
|
-
* @returns {string} - The arguments, quoted
|
|
375
|
-
*/
|
|
376
|
-
function quote(args) {
|
|
377
|
-
return shellQuote(_.castArray(args));
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
/**
|
|
381
|
-
* @typedef PluralizeOptions
|
|
382
|
-
* @property {boolean} [inclusive=false] - Whether to prefix with the number (e.g., 3 ducks)
|
|
383
|
-
*/
|
|
384
|
-
|
|
385
|
-
/**
|
|
386
|
-
* Get the form of a word appropriate to the count
|
|
387
|
-
*
|
|
388
|
-
* @param {string} word - The word to pluralize
|
|
389
|
-
* @param {number} count - How many of the word exist
|
|
390
|
-
* @param {PluralizeOptions|boolean} options - options for word pluralization,
|
|
391
|
-
* or a boolean indicating the options.inclusive property
|
|
392
|
-
* @returns {string} The word pluralized according to the number
|
|
393
|
-
*/
|
|
394
|
-
function pluralize(word, count, options = {}) {
|
|
395
|
-
let inclusive = false;
|
|
396
|
-
if (_.isBoolean(options)) {
|
|
397
|
-
// if passed in as a boolean
|
|
398
|
-
inclusive = options;
|
|
399
|
-
} else if (_.isBoolean(options?.inclusive)) {
|
|
400
|
-
// if passed in as an options hash
|
|
401
|
-
inclusive = options.inclusive;
|
|
402
|
-
}
|
|
403
|
-
return pluralizeLib(word, count, inclusive);
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
/**
|
|
407
|
-
* @typedef EncodingOptions
|
|
408
|
-
* @property {number} [maxSize=1073741824] The maximum size of
|
|
409
|
-
* the resulting buffer in bytes. This is set to 1GB by default, because
|
|
410
|
-
* Appium limits the maximum HTTP body size to 1GB. Also, the NodeJS heap
|
|
411
|
-
* size must be enough to keep the resulting object (usually this size is
|
|
412
|
-
* limited to 1.4 GB)
|
|
413
|
-
*/
|
|
414
|
-
|
|
415
|
-
/**
|
|
416
|
-
* Converts contents of a local file to an in-memory base-64 encoded buffer.
|
|
417
|
-
* The operation is memory-usage friendly and should be used while encoding
|
|
418
|
-
* large files to base64
|
|
419
|
-
*
|
|
420
|
-
* @param {string} srcPath The full path to the file being encoded
|
|
421
|
-
* @param {EncodingOptions} opts
|
|
422
|
-
* @returns {Promise<Buffer>} base64-encoded content of the source file as memory buffer
|
|
423
|
-
* @throws {Error} if there was an error while reading the source file
|
|
424
|
-
* or the source file is too
|
|
425
|
-
*/
|
|
426
|
-
async function toInMemoryBase64(srcPath, opts = {}) {
|
|
427
|
-
if (!(await fs.exists(srcPath)) || (await fs.stat(srcPath)).isDirectory()) {
|
|
428
|
-
throw new Error(`No such file: ${srcPath}`);
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
const {maxSize = 1 * GiB} = opts;
|
|
432
|
-
const resultBuffers = [];
|
|
433
|
-
let resultBuffersSize = 0;
|
|
434
|
-
const resultWriteStream = new stream.Writable({
|
|
435
|
-
write: (buffer, encoding, next) => {
|
|
436
|
-
resultBuffers.push(buffer);
|
|
437
|
-
resultBuffersSize += buffer.length;
|
|
438
|
-
if (maxSize > 0 && resultBuffersSize > maxSize) {
|
|
439
|
-
resultWriteStream.emit(
|
|
440
|
-
'error',
|
|
441
|
-
new Error(
|
|
442
|
-
`The size of the resulting ` +
|
|
443
|
-
`buffer must not be greater than ${toReadableSizeString(maxSize)}`
|
|
444
|
-
)
|
|
445
|
-
);
|
|
446
|
-
}
|
|
447
|
-
next();
|
|
448
|
-
},
|
|
449
|
-
});
|
|
450
|
-
|
|
451
|
-
const readerStream = fs.createReadStream(srcPath);
|
|
452
|
-
const base64EncoderStream = new Base64Encode();
|
|
453
|
-
const resultWriteStreamPromise = new B((resolve, reject) => {
|
|
454
|
-
resultWriteStream.once('error', (e) => {
|
|
455
|
-
readerStream.unpipe(base64EncoderStream);
|
|
456
|
-
base64EncoderStream.unpipe(resultWriteStream);
|
|
457
|
-
readerStream.destroy();
|
|
458
|
-
reject(e);
|
|
459
|
-
});
|
|
460
|
-
resultWriteStream.once('finish', resolve);
|
|
461
|
-
});
|
|
462
|
-
const readStreamPromise = new B((resolve, reject) => {
|
|
463
|
-
readerStream.once('close', resolve);
|
|
464
|
-
readerStream.once('error', (e) =>
|
|
465
|
-
reject(new Error(`Failed to read '${srcPath}': ${e.message}`))
|
|
466
|
-
);
|
|
467
|
-
});
|
|
468
|
-
readerStream.pipe(base64EncoderStream);
|
|
469
|
-
base64EncoderStream.pipe(resultWriteStream);
|
|
470
|
-
|
|
471
|
-
await B.all([readStreamPromise, resultWriteStreamPromise]);
|
|
472
|
-
return Buffer.concat(resultBuffers);
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
/**
|
|
476
|
-
* @typedef LockFileOptions
|
|
477
|
-
* @property {number} [timeout=120] The max time in seconds to wait for the lock
|
|
478
|
-
* @property {boolean} [tryRecovery=false] Whether to try lock recovery if
|
|
479
|
-
* the first attempt to acquire it timed out.
|
|
480
|
-
*/
|
|
481
|
-
|
|
482
|
-
/**
|
|
483
|
-
* Create an async function which, when called, will not proceed until a certain file is no
|
|
484
|
-
* longer present on the system. This allows for preventing concurrent behavior across processes
|
|
485
|
-
* using a known lockfile path.
|
|
486
|
-
*
|
|
487
|
-
* @template T
|
|
488
|
-
* @param {string} lockFile The full path to the file used for the lock
|
|
489
|
-
* @param {LockFileOptions} opts
|
|
490
|
-
* @returns async function that takes another async function defining the locked
|
|
491
|
-
* behavior
|
|
492
|
-
*/
|
|
493
|
-
function getLockFileGuard(lockFile, opts = {}) {
|
|
494
|
-
const {timeout = 120, tryRecovery = false} = opts;
|
|
495
|
-
|
|
496
|
-
const lock = /** @type {(lockfile: string, opts: import('lockfile').Options)=>B<void>} */ (
|
|
497
|
-
B.promisify(_lockfile.lock)
|
|
498
|
-
);
|
|
499
|
-
const check = B.promisify(_lockfile.check);
|
|
500
|
-
const unlock = B.promisify(_lockfile.unlock);
|
|
501
|
-
|
|
502
|
-
/**
|
|
503
|
-
* @param {(...args: any[]) => T} behavior
|
|
504
|
-
* @returns {Promise<T>}
|
|
505
|
-
*/
|
|
506
|
-
const guard = async (behavior) => {
|
|
507
|
-
let triedRecovery = false;
|
|
508
|
-
do {
|
|
509
|
-
try {
|
|
510
|
-
// if the lockfile doesn't exist, lock it synchronously to make sure no other call
|
|
511
|
-
// on the same spin of the event loop can also initiate a lock. If the lockfile does exist
|
|
512
|
-
// then just use the regular async 'lock' method which will wait on the lock.
|
|
513
|
-
if (_lockfile.checkSync(lockFile)) {
|
|
514
|
-
await lock(lockFile, {wait: timeout * 1000});
|
|
515
|
-
} else {
|
|
516
|
-
_lockfile.lockSync(lockFile);
|
|
517
|
-
}
|
|
518
|
-
break;
|
|
519
|
-
} catch (e) {
|
|
520
|
-
if (_.includes(e.message, 'EEXIST') && tryRecovery && !triedRecovery) {
|
|
521
|
-
// There could be cases where a process has been forcefully terminated
|
|
522
|
-
// without a chance to clean up pending locks: https://github.com/npm/lockfile/issues/26
|
|
523
|
-
_lockfile.unlockSync(lockFile);
|
|
524
|
-
triedRecovery = true;
|
|
525
|
-
continue;
|
|
526
|
-
}
|
|
527
|
-
throw new Error(
|
|
528
|
-
`Could not acquire lock on '${lockFile}' after ${timeout}s. ` +
|
|
529
|
-
`Original error: ${e.message}`
|
|
530
|
-
);
|
|
531
|
-
}
|
|
532
|
-
// eslint-disable-next-line no-constant-condition
|
|
533
|
-
} while (true);
|
|
534
|
-
try {
|
|
535
|
-
return await behavior();
|
|
536
|
-
} finally {
|
|
537
|
-
// whether the behavior succeeded or not, get rid of the lock
|
|
538
|
-
await unlock(lockFile);
|
|
539
|
-
}
|
|
540
|
-
};
|
|
541
|
-
|
|
542
|
-
guard.check = async () => await check(lockFile);
|
|
543
|
-
|
|
544
|
-
return guard;
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
export {
|
|
548
|
-
hasValue,
|
|
549
|
-
escapeSpace,
|
|
550
|
-
escapeSpecialChars,
|
|
551
|
-
localIp,
|
|
552
|
-
cancellableDelay,
|
|
553
|
-
multiResolve,
|
|
554
|
-
safeJsonParse,
|
|
555
|
-
wrapElement,
|
|
556
|
-
unwrapElement,
|
|
557
|
-
filterObject,
|
|
558
|
-
toReadableSizeString,
|
|
559
|
-
isSubPath,
|
|
560
|
-
W3C_WEB_ELEMENT_IDENTIFIER,
|
|
561
|
-
isSameDestination,
|
|
562
|
-
compareVersions,
|
|
563
|
-
coerceVersion,
|
|
564
|
-
quote,
|
|
565
|
-
jsonStringify,
|
|
566
|
-
pluralize,
|
|
567
|
-
GiB,
|
|
568
|
-
MiB,
|
|
569
|
-
KiB,
|
|
570
|
-
toInMemoryBase64,
|
|
571
|
-
uuidV1,
|
|
572
|
-
uuidV3,
|
|
573
|
-
uuidV4,
|
|
574
|
-
uuidV5,
|
|
575
|
-
shellParse,
|
|
576
|
-
getLockFileGuard,
|
|
577
|
-
};
|
|
578
|
-
|
|
579
|
-
/**
|
|
580
|
-
* A `string` which is never `''`.
|
|
581
|
-
*
|
|
582
|
-
* @template {string} T
|
|
583
|
-
* @typedef {T extends '' ? never : T} NonEmptyString
|
|
584
|
-
*/
|