@appium/support 2.55.4 → 2.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/lib/env.d.ts +54 -0
- package/build/lib/env.d.ts.map +1 -0
- package/build/lib/env.js +102 -0
- package/build/lib/fs.d.ts +221 -0
- package/build/lib/fs.d.ts.map +1 -0
- package/build/lib/fs.js +74 -56
- package/build/lib/image-util.d.ts +56 -0
- package/build/lib/image-util.d.ts.map +1 -0
- package/build/lib/image-util.js +3 -6
- package/build/lib/index.d.ts +38 -0
- package/build/lib/index.d.ts.map +1 -0
- package/build/lib/index.js +36 -18
- package/build/lib/log-internal.d.ts +74 -0
- package/build/lib/log-internal.d.ts.map +1 -0
- package/build/lib/log-internal.js +11 -21
- package/build/lib/logger.d.ts +3 -0
- package/build/lib/logger.d.ts.map +1 -0
- package/build/lib/logger.js +2 -4
- package/build/lib/logging.d.ts +45 -0
- package/build/lib/logging.d.ts.map +1 -0
- package/build/lib/logging.js +12 -16
- package/build/lib/mjpeg.d.ts +65 -0
- package/build/lib/mjpeg.d.ts.map +1 -0
- package/build/lib/mjpeg.js +12 -7
- package/build/lib/mkdirp.d.ts +3 -0
- package/build/lib/mkdirp.d.ts.map +1 -0
- package/build/lib/mkdirp.js +7 -11
- package/build/lib/net.d.ts +95 -0
- package/build/lib/net.d.ts.map +1 -0
- package/build/lib/net.js +42 -26
- package/build/lib/node.d.ts +26 -0
- package/build/lib/node.d.ts.map +1 -0
- package/build/lib/node.js +102 -3
- package/build/lib/npm.d.ts +123 -0
- package/build/lib/npm.d.ts.map +1 -0
- package/build/lib/npm.js +217 -0
- package/build/lib/plist.d.ts +43 -0
- package/build/lib/plist.d.ts.map +1 -0
- package/build/lib/plist.js +2 -4
- package/build/lib/process.d.ts +3 -0
- package/build/lib/process.d.ts.map +1 -0
- package/build/lib/process.js +2 -4
- package/build/lib/system.d.ts +7 -0
- package/build/lib/system.d.ts.map +1 -0
- package/build/lib/system.js +2 -4
- package/build/lib/tempdir.d.ts +63 -0
- package/build/lib/tempdir.d.ts.map +1 -0
- package/build/lib/tempdir.js +4 -9
- package/build/lib/timing.d.ts +46 -0
- package/build/lib/timing.d.ts.map +1 -0
- package/build/lib/timing.js +2 -4
- package/build/lib/util.d.ts +183 -0
- package/build/lib/util.d.ts.map +1 -0
- package/build/lib/util.js +9 -15
- package/build/lib/zip.d.ts +180 -0
- package/build/lib/zip.d.ts.map +1 -0
- package/build/lib/zip.js +9 -9
- package/build/tsconfig.tsbuildinfo +1 -0
- package/lib/env.js +162 -0
- package/lib/fs.js +198 -69
- package/lib/image-util.js +23 -7
- package/lib/index.js +6 -6
- package/lib/log-internal.js +31 -38
- package/lib/logging.js +41 -17
- package/lib/mjpeg.js +14 -5
- package/lib/mkdirp.js +3 -6
- package/lib/net.js +116 -60
- package/lib/node.js +107 -4
- package/lib/npm.js +278 -0
- package/lib/plist.js +3 -1
- package/lib/tempdir.js +14 -13
- package/lib/util.js +36 -33
- package/lib/zip.js +31 -21
- package/package.json +31 -13
- package/build/test/assets/sample_binary.plist +0 -0
- package/build/test/assets/sample_text.plist +0 -28
- package/build/test/fs-specs.js +0 -264
- package/build/test/helpers.js +0 -35
- package/build/test/image-util-e2e-specs.js +0 -78
- package/build/test/index-specs.js +0 -49
- package/build/test/log-internals-specs.js +0 -97
- package/build/test/logger/helpers.js +0 -71
- package/build/test/logger/logger-force-specs.js +0 -41
- package/build/test/logger/logger-normal-specs.js +0 -113
- package/build/test/logger/logger-test-specs.js +0 -40
- package/build/test/mjpeg-e2e-specs.js +0 -96
- package/build/test/net-e2e-specs.js +0 -32
- package/build/test/node-e2e-specs.js +0 -22
- package/build/test/plist-specs.js +0 -54
- package/build/test/process-specs.js +0 -104
- package/build/test/system-specs.js +0 -136
- package/build/test/tempdir-specs.js +0 -86
- package/build/test/timing-specs.js +0 -125
- package/build/test/util-e2e-specs.js +0 -136
- package/build/test/util-specs.js +0 -537
- package/build/test/zip-e2e-specs.js +0 -233
package/lib/npm.js
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import semver from 'semver';
|
|
5
|
+
import { hasAppiumDependency } from './env';
|
|
6
|
+
import { exec } from 'teen_process';
|
|
7
|
+
import { fs } from './fs';
|
|
8
|
+
import * as util from './util';
|
|
9
|
+
import * as system from './system';
|
|
10
|
+
import resolveFrom from 'resolve-from';
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Relative path to directory containing any Appium internal files
|
|
15
|
+
* XXX: this is duplicated in `appium/lib/constants.js`.
|
|
16
|
+
*/
|
|
17
|
+
export const CACHE_DIR_RELATIVE_PATH = path.join(
|
|
18
|
+
'node_modules',
|
|
19
|
+
'.cache',
|
|
20
|
+
'appium',
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Relative path to lockfile used when installing an extension via `appium`
|
|
25
|
+
*/
|
|
26
|
+
export const INSTALL_LOCKFILE_RELATIVE_PATH = path.join(
|
|
27
|
+
CACHE_DIR_RELATIVE_PATH,
|
|
28
|
+
'.install.lock',
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* XXX: This should probably be a singleton, but it isn't. Maybe this module should just export functions?
|
|
33
|
+
*/
|
|
34
|
+
export class NPM {
|
|
35
|
+
/**
|
|
36
|
+
* Returns path to "install" lockfile
|
|
37
|
+
* @private
|
|
38
|
+
* @param {string} cwd
|
|
39
|
+
*/
|
|
40
|
+
_getInstallLockfilePath (cwd) {
|
|
41
|
+
return path.join(cwd, INSTALL_LOCKFILE_RELATIVE_PATH);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Execute `npm` with given args.
|
|
46
|
+
*
|
|
47
|
+
* If the process exits with a nonzero code, the contents of `STDOUT` and `STDERR` will be in the
|
|
48
|
+
* `message` of the {@link TeenProcessExecError} rejected.
|
|
49
|
+
* @param {string} cmd
|
|
50
|
+
* @param {string[]} args
|
|
51
|
+
* @param {ExecOpts} opts
|
|
52
|
+
* @param {ExecOpts} [execOpts]
|
|
53
|
+
*/
|
|
54
|
+
async exec (cmd, args, opts, execOpts = /** @type {ExecOpts} */({})) {
|
|
55
|
+
let { cwd, json, lockFile } = opts;
|
|
56
|
+
|
|
57
|
+
// make sure we perform the current operation in cwd
|
|
58
|
+
execOpts = {...execOpts, cwd};
|
|
59
|
+
|
|
60
|
+
args.unshift(cmd);
|
|
61
|
+
if (json) {
|
|
62
|
+
args.push('--json');
|
|
63
|
+
}
|
|
64
|
+
const npmCmd = system.isWindows() ? 'npm.cmd' : 'npm';
|
|
65
|
+
let runner = async () => await exec(npmCmd, args, execOpts);
|
|
66
|
+
if (lockFile) {
|
|
67
|
+
const acquireLock = util.getLockFileGuard(lockFile);
|
|
68
|
+
const _runner = runner;
|
|
69
|
+
runner = async () => await acquireLock(_runner);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** @type {import('teen_process').ExecResult<string> & {json?: any}} */
|
|
73
|
+
let ret;
|
|
74
|
+
try {
|
|
75
|
+
const {stdout, stderr, code} = await runner();
|
|
76
|
+
ret = {stdout, stderr, code};
|
|
77
|
+
// if possible, parse NPM's json output. During NPM install 3rd-party
|
|
78
|
+
// packages can write to stdout, so sometimes the json output can't be
|
|
79
|
+
// guaranteed to be parseable
|
|
80
|
+
try {
|
|
81
|
+
ret.json = JSON.parse(stdout);
|
|
82
|
+
} catch (ign) {}
|
|
83
|
+
} catch (e) {
|
|
84
|
+
const {stdout = '', stderr = '', code = null} = /** @type {TeenProcessExecError} */(e);
|
|
85
|
+
const err = new Error(`npm command '${args.join(' ')}' failed with code ${code}.\n\nSTDOUT:\n${stdout.trim()}\n\nSTDERR:\n${stderr.trim()}`);
|
|
86
|
+
throw err;
|
|
87
|
+
}
|
|
88
|
+
return ret;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @param {string} cwd
|
|
93
|
+
* @param {string} pkg
|
|
94
|
+
*/
|
|
95
|
+
async getLatestVersion (cwd, pkg) {
|
|
96
|
+
return (await this.exec('view', [pkg, 'dist-tags'], {
|
|
97
|
+
json: true,
|
|
98
|
+
cwd
|
|
99
|
+
})).json?.latest;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @param {string} cwd
|
|
104
|
+
* @param {string} pkg
|
|
105
|
+
* @param {string} curVersion
|
|
106
|
+
*/
|
|
107
|
+
async getLatestSafeUpgradeVersion (cwd, pkg, curVersion) {
|
|
108
|
+
const allVersions = (await this.exec('view', [pkg, 'versions'], {
|
|
109
|
+
json: true,
|
|
110
|
+
cwd
|
|
111
|
+
})).json;
|
|
112
|
+
return this.getLatestSafeUpgradeFromVersions(curVersion, allVersions);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Runs `npm ls`, optionally for a particular package.
|
|
117
|
+
* @param {string} cwd
|
|
118
|
+
* @param {string} [pkg]
|
|
119
|
+
*/
|
|
120
|
+
async list (cwd, pkg) {
|
|
121
|
+
return (await this.exec('list', pkg ? [pkg] : [], {cwd, json: true})).json;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Given a current version and a list of all versions for a package, return the version which is
|
|
126
|
+
* the highest safely-upgradable version (meaning not crossing any major revision boundaries, and
|
|
127
|
+
* not including any alpha/beta/rc versions)
|
|
128
|
+
*
|
|
129
|
+
* @param {string} curVersion - the current version of a package
|
|
130
|
+
* @param {Array<string>} allVersions - a list of version strings
|
|
131
|
+
*
|
|
132
|
+
* @return {string|null} - the highest safely-upgradable version, or null if there isn't one
|
|
133
|
+
*/
|
|
134
|
+
getLatestSafeUpgradeFromVersions (curVersion, allVersions) {
|
|
135
|
+
let safeUpgradeVer = null;
|
|
136
|
+
const curSemver = semver.parse(curVersion);
|
|
137
|
+
if (curSemver === null) {
|
|
138
|
+
throw new Error(`Could not parse current version '${curVersion}'`);
|
|
139
|
+
}
|
|
140
|
+
for (const testVer of allVersions) {
|
|
141
|
+
const testSemver = semver.parse(testVer);
|
|
142
|
+
if (testSemver === null) {
|
|
143
|
+
throw new Error(`Could not parse version to test against: '${testVer}'`);
|
|
144
|
+
}
|
|
145
|
+
// if the test version is a prerelease, ignore it
|
|
146
|
+
if (testSemver.prerelease.length > 0) {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
// if the current version is later than the test version, skip this test version
|
|
150
|
+
if (curSemver.compare(testSemver) === 1) {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
// if the test version is newer, but crosses a major revision boundary, also skip it
|
|
154
|
+
if (testSemver.major > curSemver.major) {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
// otherwise this version is safe to upgrade to. But there might be multiple ones of this
|
|
158
|
+
// kind, so keep iterating and keeping the highest
|
|
159
|
+
if (safeUpgradeVer === null || testSemver.compare(safeUpgradeVer) === 1) {
|
|
160
|
+
safeUpgradeVer = testSemver;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (safeUpgradeVer) {
|
|
164
|
+
safeUpgradeVer = safeUpgradeVer.format();
|
|
165
|
+
}
|
|
166
|
+
return safeUpgradeVer;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Installs a package w/ `npm`
|
|
171
|
+
* @param {string} cwd
|
|
172
|
+
* @param {string} pkgName
|
|
173
|
+
* @param {InstallPackageOpts} [opts]
|
|
174
|
+
* @returns {Promise<import('type-fest').PackageJson>}
|
|
175
|
+
*/
|
|
176
|
+
async installPackage (cwd, pkgName, {pkgVer} = {}) {
|
|
177
|
+
/** @type {any} */
|
|
178
|
+
let dummyPkgJson;
|
|
179
|
+
const dummyPkgPath = path.join(cwd, 'package.json');
|
|
180
|
+
try {
|
|
181
|
+
dummyPkgJson = JSON.parse(await fs.readFile(dummyPkgPath, 'utf8'));
|
|
182
|
+
} catch (err) {
|
|
183
|
+
if (err.code === 'ENOENT') {
|
|
184
|
+
dummyPkgJson = {};
|
|
185
|
+
await fs.writeFile(dummyPkgPath, JSON.stringify(dummyPkgJson, null, 2), 'utf8');
|
|
186
|
+
} else {
|
|
187
|
+
throw err;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* If we've found a `package.json` containined the `appiumCreated` property,
|
|
193
|
+
* then we can do whatever we please with it, since we created it. This is
|
|
194
|
+
* likely when `APPIUM_HOME` is the default (in `~/.appium`). In that case,
|
|
195
|
+
* we want `--global-style` to avoid deduping, and we also do not need a
|
|
196
|
+
* `package-lock.json`.
|
|
197
|
+
*
|
|
198
|
+
* If we _haven't_ found such a key, then this `package.json` isn't a
|
|
199
|
+
* "dummy" and is controlled by the user. So we'll just add it as a dev
|
|
200
|
+
* dep; whatever else it does is up to the user's npm config.
|
|
201
|
+
*/
|
|
202
|
+
const installOpts = await hasAppiumDependency(cwd) ?
|
|
203
|
+
['--save-dev'] :
|
|
204
|
+
['--save-dev', '--save-exact', '--global-style', '--no-package-lock'];
|
|
205
|
+
|
|
206
|
+
const res = await this.exec('install', [
|
|
207
|
+
...installOpts,
|
|
208
|
+
pkgVer ? `${pkgName}@${pkgVer}` : pkgName
|
|
209
|
+
], {
|
|
210
|
+
cwd,
|
|
211
|
+
json: true,
|
|
212
|
+
lockFile: this._getInstallLockfilePath(cwd)
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
if (res.json) {
|
|
216
|
+
// we parsed a valid json response, so if we got an error here, return that
|
|
217
|
+
// message straightaway
|
|
218
|
+
if (res.json.error) {
|
|
219
|
+
throw new Error(res.json.error);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Now read package data from the installed package to return, and make sure
|
|
224
|
+
// everything got installed ok. Remember, pkgName might end up with a / in it due to an npm
|
|
225
|
+
// org, so if so, that will get correctly exploded into multiple directories, by path.resolve here
|
|
226
|
+
// (even on Windows!)
|
|
227
|
+
const pkgJsonPath = resolveFrom(cwd, `${pkgName}/package.json`);
|
|
228
|
+
try {
|
|
229
|
+
return require(pkgJsonPath);
|
|
230
|
+
} catch {
|
|
231
|
+
throw new Error('The package was not downloaded correctly; its package.json ' +
|
|
232
|
+
'did not exist or was unreadable. We looked for it at ' +
|
|
233
|
+
pkgJsonPath);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* @param {string} cwd
|
|
239
|
+
* @param {string} pkg
|
|
240
|
+
*/
|
|
241
|
+
async uninstallPackage (cwd, pkg) {
|
|
242
|
+
await this.exec('uninstall', [pkg], {
|
|
243
|
+
cwd,
|
|
244
|
+
lockFile: this._getInstallLockfilePath(cwd)
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export const npm = new NPM();
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Options for {@link NPM.installPackage}
|
|
253
|
+
* @typedef InstallPackageOpts
|
|
254
|
+
* @property {string} [pkgVer] - the version of the package to install
|
|
255
|
+
*/
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Options for {@link NPM.exec}
|
|
259
|
+
* @typedef ExecOpts
|
|
260
|
+
* @property {string} cwd - Current working directory
|
|
261
|
+
* @property {boolean} [json] - If `true`, supply `--json` flag to npm and resolve w/ parsed JSON
|
|
262
|
+
* @property {string} [lockFile] - Path to lockfile to use
|
|
263
|
+
*/
|
|
264
|
+
|
|
265
|
+
// THESE TYPES SHOULD BE IN TEEN PROCESS, NOT HERE
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Extra props `teen_process.exec` adds to its error objects
|
|
269
|
+
* @typedef TeenProcessExecErrorProps
|
|
270
|
+
* @property {string} stdout - STDOUT
|
|
271
|
+
* @property {string} stderr - STDERR
|
|
272
|
+
* @property {number?} code - Exit code
|
|
273
|
+
*/
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Error thrown by `teen_process.exec`
|
|
277
|
+
* @typedef {Error & TeenProcessExecErrorProps} TeenProcessExecError
|
|
278
|
+
*/
|
package/lib/plist.js
CHANGED
|
@@ -26,7 +26,7 @@ async function parseXmlPlistFile (plistFilename) {
|
|
|
26
26
|
* @param {string} plist The plist file path
|
|
27
27
|
* @param {boolean} mustExist If set to false, this method will return an empty object when the file doesn't exist
|
|
28
28
|
* @param {boolean} quiet If set to false, the plist path will be logged in debug level
|
|
29
|
-
* @returns {
|
|
29
|
+
* @returns {Promise<any>} parsed plist JS Object
|
|
30
30
|
*/
|
|
31
31
|
async function parsePlistFile (plist, mustExist = true, quiet = true) {
|
|
32
32
|
// handle nonexistant file
|
|
@@ -103,6 +103,8 @@ function createBinaryPlist (data) {
|
|
|
103
103
|
* @param {Buffer} data The beffer of a binary plist
|
|
104
104
|
*/
|
|
105
105
|
function parseBinaryPlist (data) {
|
|
106
|
+
// this function exists, but is not in the type declarations.
|
|
107
|
+
// @ts-expect-error
|
|
106
108
|
return bplistParse.parseBuffer(data);
|
|
107
109
|
}
|
|
108
110
|
|
package/lib/tempdir.js
CHANGED
|
@@ -13,7 +13,7 @@ const RDWR_EXCL = cnst.O_CREAT | cnst.O_TRUNC | cnst.O_RDWR | cnst.O_EXCL;
|
|
|
13
13
|
* - No `process.env.APPIUM_TMP_DIR`: `/var/folders/34/2222sh8n27d6rcp7jqlkw8km0000gn/T/xxxxxxxx.yyyy`
|
|
14
14
|
* - With `process.env.APPIUM_TMP_DIR = '/path/to/root'`: `/path/to/root/xxxxxxxx.yyyy`
|
|
15
15
|
*
|
|
16
|
-
* @returns {string} A path to the temporary directory
|
|
16
|
+
* @returns {Promise<string>} A path to the temporary directory
|
|
17
17
|
*/
|
|
18
18
|
async function tempDir () {
|
|
19
19
|
const now = new Date();
|
|
@@ -33,9 +33,9 @@ async function tempDir () {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
* @typedef
|
|
37
|
-
* @property {string} prefix - prefix of the temp directory name
|
|
38
|
-
* @property {string} suffix - suffix of the temp directory name
|
|
36
|
+
* @typedef Affixes
|
|
37
|
+
* @property {string} [prefix] - prefix of the temp directory name
|
|
38
|
+
* @property {string} [suffix] - suffix of the temp directory name
|
|
39
39
|
*/
|
|
40
40
|
|
|
41
41
|
/**
|
|
@@ -43,8 +43,8 @@ async function tempDir () {
|
|
|
43
43
|
* with arbitrary prefix/suffix for the directory name.
|
|
44
44
|
*
|
|
45
45
|
* @param {string|Affixes} rawAffixes
|
|
46
|
-
* @param {
|
|
47
|
-
* @returns {string} A path to the temporary directory with rawAffixes and defaultPrefix
|
|
46
|
+
* @param {string} [defaultPrefix]
|
|
47
|
+
* @returns {Promise<string>} A path to the temporary directory with rawAffixes and defaultPrefix
|
|
48
48
|
*/
|
|
49
49
|
async function path (rawAffixes, defaultPrefix) {
|
|
50
50
|
const affixes = parseAffixes(rawAffixes, defaultPrefix);
|
|
@@ -54,9 +54,9 @@ async function path (rawAffixes, defaultPrefix) {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
|
-
* @typedef
|
|
57
|
+
* @typedef OpenedAffixes
|
|
58
58
|
* @property {string} path - The path to file
|
|
59
|
-
* @property {
|
|
59
|
+
* @property {number} fd - The file descriptor opened
|
|
60
60
|
*/
|
|
61
61
|
|
|
62
62
|
/**
|
|
@@ -64,7 +64,7 @@ async function path (rawAffixes, defaultPrefix) {
|
|
|
64
64
|
* with arbitrary prefix/suffix for the directory name and return it as open.
|
|
65
65
|
*
|
|
66
66
|
* @param {Affixes} affixes
|
|
67
|
-
* @returns {OpenedAffixes}
|
|
67
|
+
* @returns {Promise<OpenedAffixes>}
|
|
68
68
|
*/
|
|
69
69
|
async function open (affixes) {
|
|
70
70
|
const filePath = await path(affixes, 'f-');
|
|
@@ -73,7 +73,7 @@ async function open (affixes) {
|
|
|
73
73
|
// opens the file in mode 384
|
|
74
74
|
return {path: filePath, fd};
|
|
75
75
|
} catch (err) {
|
|
76
|
-
log.errorAndThrow(err);
|
|
76
|
+
return log.errorAndThrow(err);
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
|
|
@@ -82,11 +82,12 @@ async function open (affixes) {
|
|
|
82
82
|
* Returns prefix/suffix object
|
|
83
83
|
*
|
|
84
84
|
* @param {string|Affixes} rawAffixes
|
|
85
|
-
* @param {
|
|
85
|
+
* @param {string} [defaultPrefix]
|
|
86
86
|
* @returns {Affixes}
|
|
87
87
|
*/
|
|
88
88
|
function parseAffixes (rawAffixes, defaultPrefix) {
|
|
89
|
-
|
|
89
|
+
/** @type {Affixes} */
|
|
90
|
+
let affixes = {};
|
|
90
91
|
if (rawAffixes) {
|
|
91
92
|
switch (typeof rawAffixes) {
|
|
92
93
|
case 'string':
|
|
@@ -116,7 +117,7 @@ const openDir = tempDir;
|
|
|
116
117
|
/**
|
|
117
118
|
* Returns a path to a temporary directory whcih is defined as static in the same process
|
|
118
119
|
*
|
|
119
|
-
* @returns {string} A temp directory path whcih is defined as static in the same process
|
|
120
|
+
* @returns {Promise<string>} A temp directory path whcih is defined as static in the same process
|
|
120
121
|
*/
|
|
121
122
|
async function staticDir () { // eslint-disable-line require-await
|
|
122
123
|
return _static;
|
package/lib/util.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
1
3
|
import B from 'bluebird';
|
|
2
4
|
import _ from 'lodash';
|
|
3
5
|
import os from 'os';
|
|
@@ -75,6 +77,7 @@ function localIp () {
|
|
|
75
77
|
let ip = _.chain(os.networkInterfaces())
|
|
76
78
|
.values()
|
|
77
79
|
.flatten()
|
|
80
|
+
// @ts-ignore
|
|
78
81
|
.filter(function (val) {
|
|
79
82
|
return (val.family === 'IPv4' && val.internal === false);
|
|
80
83
|
})
|
|
@@ -217,16 +220,16 @@ function filterObject (obj, predicate) {
|
|
|
217
220
|
* if it is less than zero.
|
|
218
221
|
*/
|
|
219
222
|
function toReadableSizeString (bytes) {
|
|
220
|
-
const intBytes = parseInt(bytes, 10);
|
|
223
|
+
const intBytes = parseInt(String(bytes), 10);
|
|
221
224
|
if (isNaN(intBytes) || intBytes < 0) {
|
|
222
225
|
throw new Error(`Cannot convert '${bytes}' to a readable size format`);
|
|
223
226
|
}
|
|
224
227
|
if (intBytes >= GiB) {
|
|
225
|
-
return `${
|
|
228
|
+
return `${(intBytes / (GiB * 1.0)).toFixed(2)} GB`;
|
|
226
229
|
} else if (intBytes >= MiB) {
|
|
227
|
-
return `${
|
|
230
|
+
return `${(intBytes / (MiB * 1.0)).toFixed(2)} MB`;
|
|
228
231
|
} else if (intBytes >= KiB) {
|
|
229
|
-
return `${
|
|
232
|
+
return `${(intBytes / (KiB * 1.0)).toFixed(2)} KB`;
|
|
230
233
|
}
|
|
231
234
|
return `${intBytes} B`;
|
|
232
235
|
}
|
|
@@ -260,7 +263,7 @@ function isSubPath (originalPath, root, forcePosix = null) {
|
|
|
260
263
|
* @param {string} path1 - Absolute or relative path to a file/folder
|
|
261
264
|
* @param {string} path2 - Absolute or relative path to a file/folder
|
|
262
265
|
* @param {...string} pathN - Zero or more absolute or relative paths to files/folders
|
|
263
|
-
* @returns {boolean} true if all paths are pointing to the same file system item
|
|
266
|
+
* @returns {Promise<boolean>} true if all paths are pointing to the same file system item
|
|
264
267
|
*/
|
|
265
268
|
async function isSameDestination (path1, path2, ...pathN) {
|
|
266
269
|
const allPaths = [path1, path2, ...pathN];
|
|
@@ -273,34 +276,29 @@ async function isSameDestination (path1, path2, ...pathN) {
|
|
|
273
276
|
return true;
|
|
274
277
|
}
|
|
275
278
|
|
|
276
|
-
|
|
277
|
-
// however below that the options get interpreted as the callback
|
|
278
|
-
// TODO: remove when Node 10 is no longer supported
|
|
279
|
-
let mapCb = async (x) => await fs.stat(x, {
|
|
279
|
+
let mapCb = async (x) => (await fs.stat(x, {
|
|
280
280
|
bigint: true,
|
|
281
|
-
}).ino;
|
|
282
|
-
if (semver.lt(process.version, '10.5.0')) {
|
|
283
|
-
mapCb = async (x) => await fs.stat(x).ino;
|
|
284
|
-
}
|
|
281
|
+
})).ino;
|
|
285
282
|
return areAllItemsEqual(await B.map(allPaths, mapCb));
|
|
286
283
|
}
|
|
287
284
|
|
|
288
285
|
/**
|
|
289
286
|
* Coerces the given number/string to a valid version string
|
|
290
287
|
*
|
|
291
|
-
* @
|
|
292
|
-
* @param {
|
|
288
|
+
* @template {boolean} [Strict=true]
|
|
289
|
+
* @param {string} ver - Version string to coerce
|
|
290
|
+
* @param {Strict} [strict] - If `true` then an exception will be thrown
|
|
293
291
|
* if `ver` cannot be coerced
|
|
294
|
-
* @returns {string} Coerced version number or null if the string cannot be
|
|
292
|
+
* @returns {Strict extends true ? string : string|null} Coerced version number or null if the string cannot be
|
|
295
293
|
* coerced and strict mode is disabled
|
|
296
294
|
* @throws {Error} if strict mode is enabled and `ver` cannot be coerced
|
|
297
295
|
*/
|
|
298
|
-
function coerceVersion (ver, strict = true) {
|
|
296
|
+
function coerceVersion (ver, strict = /** @type {Strict} */(true)) {
|
|
299
297
|
const result = semver.valid(semver.coerce(`${ver}`));
|
|
300
298
|
if (strict && !result) {
|
|
301
299
|
throw new Error(`'${ver}' cannot be coerced to a valid version number`);
|
|
302
300
|
}
|
|
303
|
-
return result;
|
|
301
|
+
return /** @type {Strict extends true ? string : string?} */(result);
|
|
304
302
|
}
|
|
305
303
|
|
|
306
304
|
const SUPPORTED_OPERATORS = ['==', '!=', '>', '<', '>=', '<=', '='];
|
|
@@ -308,9 +306,9 @@ const SUPPORTED_OPERATORS = ['==', '!=', '>', '<', '>=', '<=', '='];
|
|
|
308
306
|
/**
|
|
309
307
|
* Compares two version strings
|
|
310
308
|
*
|
|
311
|
-
* @param {string
|
|
309
|
+
* @param {string} ver1 - The first version number to compare. Should be a valid
|
|
312
310
|
* version number supported by semver parser.
|
|
313
|
-
* @param {string
|
|
311
|
+
* @param {string} ver2 - The second version number to compare. Should be a valid
|
|
314
312
|
* version number supported by semver parser.
|
|
315
313
|
* @param {string} operator - One of supported version number operators:
|
|
316
314
|
* ==, !=, >, <, <=, >=, =
|
|
@@ -333,11 +331,11 @@ function compareVersions (ver1, operator, ver2) {
|
|
|
333
331
|
* Add appropriate quotes to command arguments. See https://github.com/substack/node-shell-quote
|
|
334
332
|
* for more details
|
|
335
333
|
*
|
|
336
|
-
* @param {string|
|
|
334
|
+
* @param {string|string[]} args - The arguments that will be parsed
|
|
337
335
|
* @returns {string} - The arguments, quoted
|
|
338
336
|
*/
|
|
339
337
|
function quote (args) {
|
|
340
|
-
return shellQuote(args);
|
|
338
|
+
return shellQuote(_.castArray(args));
|
|
341
339
|
}
|
|
342
340
|
|
|
343
341
|
/**
|
|
@@ -354,8 +352,8 @@ function unleakString (s) {
|
|
|
354
352
|
|
|
355
353
|
|
|
356
354
|
/**
|
|
357
|
-
* @typedef
|
|
358
|
-
* @property {
|
|
355
|
+
* @typedef PluralizeOptions
|
|
356
|
+
* @property {boolean} [inclusive=false] - Whether to prefix with the number (e.g., 3 ducks)
|
|
359
357
|
*/
|
|
360
358
|
|
|
361
359
|
/**
|
|
@@ -363,7 +361,7 @@ function unleakString (s) {
|
|
|
363
361
|
*
|
|
364
362
|
* @param {string} word - The word to pluralize
|
|
365
363
|
* @param {number} count - How many of the word exist
|
|
366
|
-
* @param {
|
|
364
|
+
* @param {PluralizeOptions|boolean} options - options for word pluralization,
|
|
367
365
|
* or a boolean indicating the options.inclusive property
|
|
368
366
|
* @returns {string} The word pluralized according to the number
|
|
369
367
|
*/
|
|
@@ -380,8 +378,8 @@ function pluralize (word, count, options = {}) {
|
|
|
380
378
|
}
|
|
381
379
|
|
|
382
380
|
/**
|
|
383
|
-
* @typedef
|
|
384
|
-
* @property {number} maxSize
|
|
381
|
+
* @typedef EncodingOptions
|
|
382
|
+
* @property {number} [maxSize=1073741824] The maximum size of
|
|
385
383
|
* the resulting buffer in bytes. This is set to 1GB by default, because
|
|
386
384
|
* Appium limits the maximum HTTP body size to 1GB. Also, the NodeJS heap
|
|
387
385
|
* size must be enough to keep the resulting object (usually this size is
|
|
@@ -395,7 +393,7 @@ function pluralize (word, count, options = {}) {
|
|
|
395
393
|
*
|
|
396
394
|
* @param {string} srcPath The full path to the file being encoded
|
|
397
395
|
* @param {EncodingOptions} opts
|
|
398
|
-
* @returns {Buffer} base64-encoded content of the source file as memory buffer
|
|
396
|
+
* @returns {Promise<Buffer>} base64-encoded content of the source file as memory buffer
|
|
399
397
|
* @throws {Error} if there was an error while reading the source file
|
|
400
398
|
* or the source file is too
|
|
401
399
|
*/
|
|
@@ -445,9 +443,9 @@ async function toInMemoryBase64 (srcPath, opts = {}) {
|
|
|
445
443
|
}
|
|
446
444
|
|
|
447
445
|
/**
|
|
448
|
-
* @typedef
|
|
449
|
-
* @property {number} timeout
|
|
450
|
-
* @property {boolean} tryRecovery
|
|
446
|
+
* @typedef LockFileOptions
|
|
447
|
+
* @property {number} [timeout=120] The max time in seconds to wait for the lock
|
|
448
|
+
* @property {boolean} [tryRecovery=false] Whether to try lock recovery if
|
|
451
449
|
* the first attempt to acquire it timed out.
|
|
452
450
|
*/
|
|
453
451
|
|
|
@@ -456,9 +454,10 @@ async function toInMemoryBase64 (srcPath, opts = {}) {
|
|
|
456
454
|
* longer present on the system. This allows for preventing concurrent behavior across processes
|
|
457
455
|
* using a known lockfile path.
|
|
458
456
|
*
|
|
457
|
+
* @template T
|
|
459
458
|
* @param {string} lockFile The full path to the file used for the lock
|
|
460
459
|
* @param {LockFileOptions} opts
|
|
461
|
-
* @returns
|
|
460
|
+
* @returns async function that takes another async function defining the locked
|
|
462
461
|
* behavior
|
|
463
462
|
*/
|
|
464
463
|
function getLockFileGuard (lockFile, opts = {}) {
|
|
@@ -467,10 +466,14 @@ function getLockFileGuard (lockFile, opts = {}) {
|
|
|
467
466
|
tryRecovery = false,
|
|
468
467
|
} = opts;
|
|
469
468
|
|
|
470
|
-
const lock = B.promisify(_lockfile.lock);
|
|
469
|
+
const lock = /** @type {(lockfile: string, opts: import('lockfile').Options)=>B<void>} */(B.promisify(_lockfile.lock));
|
|
471
470
|
const check = B.promisify(_lockfile.check);
|
|
472
471
|
const unlock = B.promisify(_lockfile.unlock);
|
|
473
472
|
|
|
473
|
+
/**
|
|
474
|
+
* @param {(...args: any[]) => T} behavior
|
|
475
|
+
* @returns {Promise<T>}
|
|
476
|
+
*/
|
|
474
477
|
const guard = async (behavior) => {
|
|
475
478
|
let triedRecovery = false;
|
|
476
479
|
do {
|