@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/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
- */