@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/plist.ts
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import xmlplist from 'plist';
|
|
2
|
+
import bplistCreate from 'bplist-creator';
|
|
3
|
+
import {parseFile, parseBuffer} from 'bplist-parser';
|
|
4
|
+
import {fs} from './fs';
|
|
5
|
+
import log from './logger';
|
|
6
|
+
import _ from 'lodash';
|
|
7
|
+
|
|
8
|
+
const BPLIST_IDENTIFIER = {
|
|
9
|
+
BUFFER: Buffer.from('bplist00'),
|
|
10
|
+
TEXT: 'bplist00',
|
|
11
|
+
};
|
|
12
|
+
const PLIST_IDENTIFIER = {
|
|
13
|
+
BUFFER: Buffer.from('<'),
|
|
14
|
+
TEXT: '<',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Parses a file in xml or binary format of plist
|
|
19
|
+
*
|
|
20
|
+
* @param plist - The plist file path
|
|
21
|
+
* @param mustExist - If set to false, this method will return an empty object when the file doesn't exist
|
|
22
|
+
* @param quiet - If set to false, the plist path will be logged in debug level
|
|
23
|
+
* @returns Parsed plist as a JS object
|
|
24
|
+
*/
|
|
25
|
+
export async function parsePlistFile(
|
|
26
|
+
plist: string,
|
|
27
|
+
mustExist = true,
|
|
28
|
+
quiet = true
|
|
29
|
+
): Promise<object> {
|
|
30
|
+
if (!(await fs.exists(plist))) {
|
|
31
|
+
if (mustExist) {
|
|
32
|
+
throw log.errorWithException(`Plist file doesn't exist: '${plist}'`);
|
|
33
|
+
}
|
|
34
|
+
if (!quiet) {
|
|
35
|
+
log.debug(`Plist file '${plist}' does not exist. Returning an empty plist.`);
|
|
36
|
+
}
|
|
37
|
+
return {};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let obj: object = {};
|
|
41
|
+
let type = 'binary';
|
|
42
|
+
try {
|
|
43
|
+
const parsed = await parseFile(plist);
|
|
44
|
+
if (parsed.length) {
|
|
45
|
+
obj = parsed[0];
|
|
46
|
+
} else {
|
|
47
|
+
throw new Error(`Binary file '${plist}' appears to be empty`);
|
|
48
|
+
}
|
|
49
|
+
} catch {
|
|
50
|
+
try {
|
|
51
|
+
obj = await parseXmlPlistFile(plist);
|
|
52
|
+
type = 'xml';
|
|
53
|
+
} catch (err) {
|
|
54
|
+
throw log.errorWithException(
|
|
55
|
+
`Could not parse plist file '${plist}' as XML: ${(err as Error).message}`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!quiet) {
|
|
61
|
+
log.debug(`Parsed plist file '${plist}' as ${type}`);
|
|
62
|
+
}
|
|
63
|
+
return obj;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Updates a plist file with the given fields
|
|
68
|
+
*
|
|
69
|
+
* @param plist - The plist file path
|
|
70
|
+
* @param updatedFields - The updated fields-value pairs
|
|
71
|
+
* @param binary - If set to false, the file will be created as a xml plist
|
|
72
|
+
* @param mustExist - If set to false, this method will update an empty plist
|
|
73
|
+
* @param quiet - If set to false, the plist path will be logged in debug level
|
|
74
|
+
*/
|
|
75
|
+
export async function updatePlistFile(
|
|
76
|
+
plist: string,
|
|
77
|
+
updatedFields: object,
|
|
78
|
+
binary = true,
|
|
79
|
+
mustExist = true,
|
|
80
|
+
quiet = true
|
|
81
|
+
): Promise<void> {
|
|
82
|
+
let obj: object;
|
|
83
|
+
try {
|
|
84
|
+
obj = await parsePlistFile(plist, mustExist);
|
|
85
|
+
} catch (err) {
|
|
86
|
+
throw log.errorWithException(`Could not update plist: ${(err as Error).message}`);
|
|
87
|
+
}
|
|
88
|
+
_.extend(obj, updatedFields);
|
|
89
|
+
const newPlist = binary ? bplistCreate(obj) : xmlplist.build(obj);
|
|
90
|
+
try {
|
|
91
|
+
await fs.writeFile(plist, newPlist);
|
|
92
|
+
} catch (err) {
|
|
93
|
+
throw log.errorWithException(`Could not save plist: ${(err as Error).message}`);
|
|
94
|
+
}
|
|
95
|
+
if (!quiet) {
|
|
96
|
+
log.debug(`Wrote plist file '${plist}'`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Creates a binary plist Buffer from an object
|
|
102
|
+
*
|
|
103
|
+
* @param data - The object to be turned into a binary plist
|
|
104
|
+
* @returns Plist in the form of a binary buffer
|
|
105
|
+
*/
|
|
106
|
+
export function createBinaryPlist(data: object): Buffer {
|
|
107
|
+
return bplistCreate(data);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Parses a Buffer into an Object
|
|
112
|
+
*
|
|
113
|
+
* @param data - The buffer of a binary plist
|
|
114
|
+
* @returns Array of parsed root objects (typically one element)
|
|
115
|
+
*/
|
|
116
|
+
export function parseBinaryPlist(data: Buffer): object[] {
|
|
117
|
+
return parseBuffer(data);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Creates a plist from an object
|
|
122
|
+
*
|
|
123
|
+
* @param object - The JS object to be turned into a plist
|
|
124
|
+
* @param binary - Set it to true for a binary plist
|
|
125
|
+
* @returns A buffer or a string depending on the binary parameter
|
|
126
|
+
*/
|
|
127
|
+
export function createPlist(object: object, binary = false): Buffer | string {
|
|
128
|
+
if (binary) {
|
|
129
|
+
return createBinaryPlist(object);
|
|
130
|
+
}
|
|
131
|
+
return xmlplist.build(object);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Parses a buffer or string into a JS object
|
|
136
|
+
*
|
|
137
|
+
* @param data - The plist as a string or Buffer
|
|
138
|
+
* @returns Parsed plist as a JS object
|
|
139
|
+
* @throws Will throw an error if the plist type is unknown
|
|
140
|
+
*/
|
|
141
|
+
export function parsePlist(data: string | Buffer): object {
|
|
142
|
+
const textPlist = getXmlPlist(data);
|
|
143
|
+
if (textPlist) {
|
|
144
|
+
return xmlplist.parse(textPlist);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const binaryPlist = getBinaryPlist(data);
|
|
148
|
+
if (binaryPlist) {
|
|
149
|
+
return parseBinaryPlist(binaryPlist)[0];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
throw new Error(
|
|
153
|
+
`Unknown type of plist, data: ${_.truncate(data.toString(), {length: 200})}`
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async function parseXmlPlistFile(plistFilename: string): Promise<object> {
|
|
158
|
+
const xmlContent = await fs.readFile(plistFilename, 'utf8');
|
|
159
|
+
return xmlplist.parse(xmlContent);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function getXmlPlist(data: string | Buffer): string | null {
|
|
163
|
+
if (_.isString(data) && data.startsWith(PLIST_IDENTIFIER.TEXT)) {
|
|
164
|
+
return data;
|
|
165
|
+
}
|
|
166
|
+
if (
|
|
167
|
+
Buffer.isBuffer(data) &&
|
|
168
|
+
PLIST_IDENTIFIER.BUFFER.compare(data, 0, PLIST_IDENTIFIER.BUFFER.length) === 0
|
|
169
|
+
) {
|
|
170
|
+
return data.toString();
|
|
171
|
+
}
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function getBinaryPlist(data: string | Buffer): Buffer | null {
|
|
176
|
+
if (_.isString(data) && data.startsWith(BPLIST_IDENTIFIER.TEXT)) {
|
|
177
|
+
return Buffer.from(data);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (
|
|
181
|
+
Buffer.isBuffer(data) &&
|
|
182
|
+
BPLIST_IDENTIFIER.BUFFER.compare(data, 0, BPLIST_IDENTIFIER.BUFFER.length) === 0
|
|
183
|
+
) {
|
|
184
|
+
return data;
|
|
185
|
+
}
|
|
186
|
+
return null;
|
|
187
|
+
}
|
package/lib/process.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import {exec} from 'teen_process';
|
|
2
|
+
import type {ExecError} from 'teen_process';
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* Exit Status for pgrep and pkill (`man pkill`)
|
|
6
|
+
* 0. One or more processes matched the criteria.
|
|
7
|
+
* 1. No processes matched.
|
|
8
|
+
* 2. Syntax error in the command line.
|
|
9
|
+
* 3. Fatal error: out of memory etc.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Get PIDs of processes whose executable name matches the given name (exact match via pgrep -x).
|
|
14
|
+
*
|
|
15
|
+
* @param appName - Executable name to match (e.g. 'tail', 'node').
|
|
16
|
+
* @returns Promise resolving to an array of process IDs. Empty if no processes matched.
|
|
17
|
+
* @throws {Error} If pgrep fails for any reason other than "no processes matched" (exit 1).
|
|
18
|
+
* @deprecated Use a process-management API or package that fits your platform instead.
|
|
19
|
+
*/
|
|
20
|
+
export async function getProcessIds(appName: string): Promise<number[]> {
|
|
21
|
+
let pids: number[];
|
|
22
|
+
try {
|
|
23
|
+
const {stdout} = await exec('pgrep', ['-x', appName]);
|
|
24
|
+
pids = stdout
|
|
25
|
+
.trim()
|
|
26
|
+
.split('\n')
|
|
27
|
+
.map((pid) => parseInt(pid, 10));
|
|
28
|
+
} catch (err) {
|
|
29
|
+
const code = (err as ExecError).code;
|
|
30
|
+
if (code !== 1) {
|
|
31
|
+
throw new Error(`Error getting process ids for app '${appName}': ${(err as Error).message}`);
|
|
32
|
+
}
|
|
33
|
+
pids = [];
|
|
34
|
+
}
|
|
35
|
+
return pids;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Kill all processes whose executable name matches the given name (via pkill -x).
|
|
40
|
+
*
|
|
41
|
+
* @param appName - Executable name to match (e.g. 'tail', 'node').
|
|
42
|
+
* @param force - If true, use SIGKILL (-9); otherwise use default pkill signal.
|
|
43
|
+
* @returns Promise that resolves when done, or when no matching processes were running.
|
|
44
|
+
* @throws {Error} If pkill fails for any reason other than "no processes matched" (exit 1).
|
|
45
|
+
* @deprecated Use a process-management API or package that fits your platform instead.
|
|
46
|
+
*/
|
|
47
|
+
export async function killProcess(appName: string, force = false): Promise<void> {
|
|
48
|
+
const pids = await getProcessIds(appName);
|
|
49
|
+
if (pids.length === 0) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
const args = force ? ['-9', '-x', appName] : ['-x', appName];
|
|
55
|
+
await exec('pkill', args);
|
|
56
|
+
} catch (err) {
|
|
57
|
+
const code = (err as ExecError).code;
|
|
58
|
+
if (code !== 1) {
|
|
59
|
+
throw new Error(`Error killing app '${appName}' with pkill: ${(err as Error).message}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
package/lib/system.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import {exec} from 'teen_process';
|
|
2
|
+
import _ from 'lodash';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
|
|
5
|
+
const VERSION_PATTERN = /^(\d+\.\d+)/m;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Whether the current OS is Windows.
|
|
9
|
+
*/
|
|
10
|
+
export function isWindows(): boolean {
|
|
11
|
+
return os.type() === 'Windows_NT';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Whether the current OS is macOS (Darwin).
|
|
16
|
+
*/
|
|
17
|
+
export function isMac(): boolean {
|
|
18
|
+
return os.type() === 'Darwin';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Whether the current OS is Linux (i.e. not Windows and not macOS).
|
|
23
|
+
*/
|
|
24
|
+
export function isLinux(): boolean {
|
|
25
|
+
return !isWindows() && !isMac();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Whether the current Windows process is 64-bit (or WOW64).
|
|
30
|
+
*/
|
|
31
|
+
export function isOSWin64(): boolean {
|
|
32
|
+
return process.arch === 'x64' || _.has(process.env, 'PROCESSOR_ARCHITEW6432');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Detects the major.minor macOS version (e.g. "10.12") via `sw_vers -productVersion`.
|
|
37
|
+
*
|
|
38
|
+
* @returns The major.minor version string.
|
|
39
|
+
* @throws {Error} If `sw_vers` fails or output cannot be parsed.
|
|
40
|
+
*/
|
|
41
|
+
export async function macOsxVersion(): Promise<string> {
|
|
42
|
+
let stdout: string;
|
|
43
|
+
try {
|
|
44
|
+
stdout = (await exec('sw_vers', ['-productVersion'])).stdout.trim();
|
|
45
|
+
} catch (err) {
|
|
46
|
+
throw new Error(`Could not detect Mac OS X Version: ${err}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const versionMatch = VERSION_PATTERN.exec(stdout);
|
|
50
|
+
if (!versionMatch) {
|
|
51
|
+
throw new Error(`Could not detect Mac OS X Version from sw_vers output: '${stdout}'`);
|
|
52
|
+
}
|
|
53
|
+
return versionMatch[1];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* System detection helpers (platform, architecture, macOS version).
|
|
58
|
+
* Use this object when you need `arch()` to call other helpers via `this` (e.g. for testing).
|
|
59
|
+
*/
|
|
60
|
+
export const system: System = {
|
|
61
|
+
isWindows,
|
|
62
|
+
isMac,
|
|
63
|
+
isLinux,
|
|
64
|
+
isOSWin64,
|
|
65
|
+
arch: archImpl,
|
|
66
|
+
macOsxVersion,
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Resolves the process architecture as `'32'` or `'64'` (uname on Unix, process.arch/env on Windows).
|
|
71
|
+
*/
|
|
72
|
+
export const arch = system.arch;
|
|
73
|
+
|
|
74
|
+
// #region Private
|
|
75
|
+
|
|
76
|
+
interface System {
|
|
77
|
+
isWindows(): boolean;
|
|
78
|
+
isMac(): boolean;
|
|
79
|
+
isLinux(): boolean;
|
|
80
|
+
isOSWin64(): boolean;
|
|
81
|
+
arch(): Promise<string>;
|
|
82
|
+
macOsxVersion(): Promise<string>;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function archImpl(this: System): Promise<string> {
|
|
86
|
+
if (this.isLinux() || this.isMac()) {
|
|
87
|
+
const {stdout} = await exec('uname', ['-m']);
|
|
88
|
+
return stdout.trim() === 'i686' ? '32' : '64';
|
|
89
|
+
} else if (this.isWindows()) {
|
|
90
|
+
return this.isOSWin64() ? '64' : '32';
|
|
91
|
+
}
|
|
92
|
+
return '64';
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// #endregion
|
package/lib/tempdir.ts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
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 _ from 'lodash';
|
|
6
|
+
import {constants} from 'node:fs';
|
|
7
|
+
import log from './logger';
|
|
8
|
+
|
|
9
|
+
const RDWR_EXCL = constants.O_CREAT | constants.O_TRUNC | constants.O_RDWR | constants.O_EXCL;
|
|
10
|
+
|
|
11
|
+
/** Prefix/suffix for temp directory or file names */
|
|
12
|
+
export interface Affixes {
|
|
13
|
+
prefix?: string;
|
|
14
|
+
suffix?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Result of opening a temp file */
|
|
18
|
+
export interface OpenedAffixes {
|
|
19
|
+
path: string;
|
|
20
|
+
fd: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Generate a temporary directory with arbitrary prefix/suffix for the directory name.
|
|
25
|
+
*
|
|
26
|
+
* @param rawAffixes - Prefix string, or object with prefix/suffix, or omitted.
|
|
27
|
+
* @param defaultPrefix - Default prefix when rawAffixes is omitted.
|
|
28
|
+
* @returns A path to the temporary directory.
|
|
29
|
+
*/
|
|
30
|
+
export async function path(
|
|
31
|
+
rawAffixes?: string | Affixes,
|
|
32
|
+
defaultPrefix?: string
|
|
33
|
+
): Promise<string> {
|
|
34
|
+
const affixes = parseAffixes(rawAffixes, defaultPrefix);
|
|
35
|
+
const name = `${affixes.prefix ?? ''}${affixes.suffix ?? ''}`;
|
|
36
|
+
const tempDirectory = await tempDir();
|
|
37
|
+
return nodePath.join(tempDirectory, name);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Generate a temp file path with prefix/suffix, open the file, and return path and fd.
|
|
42
|
+
*
|
|
43
|
+
* @param affixes - Prefix/suffix for the file name.
|
|
44
|
+
* @returns The opened file path and descriptor.
|
|
45
|
+
*/
|
|
46
|
+
export async function open(affixes: Affixes): Promise<OpenedAffixes> {
|
|
47
|
+
const filePath = await path(affixes, 'f-');
|
|
48
|
+
try {
|
|
49
|
+
const fd = await fs.open(filePath, RDWR_EXCL, 0o600);
|
|
50
|
+
return {path: filePath, fd};
|
|
51
|
+
} catch (err) {
|
|
52
|
+
throw log.errorWithException(err as Error);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Returns a new path to a temporary directory (alias for the tempDir implementation). */
|
|
57
|
+
export const openDir = tempDir;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Returns a path to a temporary directory which is reused for the life of the process.
|
|
61
|
+
*
|
|
62
|
+
* @returns The same temp directory path on every call.
|
|
63
|
+
*/
|
|
64
|
+
export const staticDir = _.memoize(async function staticDir (): Promise<string> {
|
|
65
|
+
return tempDir();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// #region Private
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Generate a temporary directory in os.tmpdir() or process.env.APPIUM_TMP_DIR.
|
|
72
|
+
*/
|
|
73
|
+
async function tempDir(): Promise<string> {
|
|
74
|
+
const now = new Date();
|
|
75
|
+
const filePath = nodePath.join(
|
|
76
|
+
process.env.APPIUM_TMP_DIR || os.tmpdir(),
|
|
77
|
+
[
|
|
78
|
+
now.getFullYear(),
|
|
79
|
+
now.getMonth(),
|
|
80
|
+
now.getDate(),
|
|
81
|
+
'-',
|
|
82
|
+
process.pid,
|
|
83
|
+
'-',
|
|
84
|
+
(Math.random() * 0x100000000 + 1).toString(36),
|
|
85
|
+
].join('')
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
await fs.mkdir(filePath, {recursive: true});
|
|
89
|
+
|
|
90
|
+
return filePath;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function parseAffixes(
|
|
94
|
+
rawAffixes?: string | Affixes,
|
|
95
|
+
defaultPrefix?: string
|
|
96
|
+
): Affixes {
|
|
97
|
+
let affixes: Affixes = {};
|
|
98
|
+
if (rawAffixes !== undefined && rawAffixes !== null) {
|
|
99
|
+
switch (typeof rawAffixes) {
|
|
100
|
+
case 'string':
|
|
101
|
+
affixes = {prefix: rawAffixes};
|
|
102
|
+
break;
|
|
103
|
+
case 'object':
|
|
104
|
+
affixes = rawAffixes;
|
|
105
|
+
break;
|
|
106
|
+
default:
|
|
107
|
+
throw new Error(`Unknown affix declaration: ${String(rawAffixes)}`);
|
|
108
|
+
}
|
|
109
|
+
} else {
|
|
110
|
+
affixes.prefix = defaultPrefix;
|
|
111
|
+
}
|
|
112
|
+
return affixes;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// #endregion
|
|
@@ -3,15 +3,16 @@ import _ from 'lodash';
|
|
|
3
3
|
const NS_PER_S = 1e9;
|
|
4
4
|
const NS_PER_MS = 1e6;
|
|
5
5
|
|
|
6
|
+
/** High-resolution start time: tuple from process.hrtime() or bigint from process.hrtime.bigint() */
|
|
7
|
+
type HrTime = [number, number] | bigint;
|
|
8
|
+
|
|
6
9
|
/**
|
|
7
10
|
* Class representing a duration, encapsulating the number and units.
|
|
8
11
|
*/
|
|
9
|
-
class Duration {
|
|
10
|
-
constructor(
|
|
11
|
-
this._nanos = nanos;
|
|
12
|
-
}
|
|
12
|
+
export class Duration {
|
|
13
|
+
constructor(private _nanos: number) {}
|
|
13
14
|
|
|
14
|
-
get nanos() {
|
|
15
|
+
get nanos(): number {
|
|
15
16
|
return this._nanos;
|
|
16
17
|
}
|
|
17
18
|
|
|
@@ -20,7 +21,7 @@ class Duration {
|
|
|
20
21
|
*
|
|
21
22
|
* @returns {number} The duration as nanoseconds
|
|
22
23
|
*/
|
|
23
|
-
get asNanoSeconds() {
|
|
24
|
+
get asNanoSeconds(): number {
|
|
24
25
|
return this.nanos;
|
|
25
26
|
}
|
|
26
27
|
|
|
@@ -29,7 +30,7 @@ class Duration {
|
|
|
29
30
|
*
|
|
30
31
|
* @returns {number} The duration as milliseconds
|
|
31
32
|
*/
|
|
32
|
-
get asMilliSeconds() {
|
|
33
|
+
get asMilliSeconds(): number {
|
|
33
34
|
return this.nanos / NS_PER_MS;
|
|
34
35
|
}
|
|
35
36
|
|
|
@@ -38,25 +39,23 @@ class Duration {
|
|
|
38
39
|
*
|
|
39
40
|
* @returns {number} The duration fas seconds
|
|
40
41
|
*/
|
|
41
|
-
get asSeconds() {
|
|
42
|
+
get asSeconds(): number {
|
|
42
43
|
return this.nanos / NS_PER_S;
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
toString() {
|
|
46
|
+
toString(): string {
|
|
46
47
|
// default to milliseconds, rounded
|
|
47
48
|
return this.asMilliSeconds.toFixed(0);
|
|
48
49
|
}
|
|
49
50
|
}
|
|
50
51
|
|
|
51
|
-
class Timer {
|
|
52
|
+
export class Timer {
|
|
52
53
|
/**
|
|
53
54
|
* Creates a timer
|
|
54
55
|
*/
|
|
55
|
-
constructor() {
|
|
56
|
-
this._startTime = null;
|
|
57
|
-
}
|
|
56
|
+
constructor(private _startTime: HrTime | null = null) {}
|
|
58
57
|
|
|
59
|
-
get startTime() {
|
|
58
|
+
get startTime(): HrTime | null {
|
|
60
59
|
return this._startTime;
|
|
61
60
|
}
|
|
62
61
|
|
|
@@ -65,14 +64,11 @@ class Timer {
|
|
|
65
64
|
*
|
|
66
65
|
* @return {Timer} The current instance, for chaining
|
|
67
66
|
*/
|
|
68
|
-
start() {
|
|
69
|
-
if (!_.isNull(this.
|
|
67
|
+
start(): this {
|
|
68
|
+
if (!_.isNull(this._startTime)) {
|
|
70
69
|
throw new Error('Timer has already been started.');
|
|
71
70
|
}
|
|
72
|
-
|
|
73
|
-
this._startTime = _.isFunction(process.hrtime.bigint)
|
|
74
|
-
? process.hrtime.bigint()
|
|
75
|
-
: process.hrtime();
|
|
71
|
+
this._startTime = process.hrtime.bigint();
|
|
76
72
|
return this;
|
|
77
73
|
}
|
|
78
74
|
|
|
@@ -81,36 +77,35 @@ class Timer {
|
|
|
81
77
|
*
|
|
82
78
|
* @return {Duration} the duration
|
|
83
79
|
*/
|
|
84
|
-
getDuration() {
|
|
85
|
-
if (_.isNull(this.
|
|
86
|
-
throw new Error(
|
|
80
|
+
getDuration(): Duration {
|
|
81
|
+
if (_.isNull(this._startTime)) {
|
|
82
|
+
throw new Error('Unable to get duration. Timer was not started');
|
|
87
83
|
}
|
|
88
84
|
|
|
89
|
-
let nanoDuration;
|
|
90
|
-
if (_.isArray(this.
|
|
85
|
+
let nanoDuration: number;
|
|
86
|
+
if (_.isArray(this._startTime)) {
|
|
91
87
|
// startTime was created using process.hrtime()
|
|
92
|
-
const [seconds, nanos] = process.hrtime(this.
|
|
88
|
+
const [seconds, nanos] = process.hrtime(this._startTime as [number, number]);
|
|
93
89
|
nanoDuration = seconds * NS_PER_S + nanos;
|
|
94
|
-
} else if (typeof this.
|
|
90
|
+
} else if (typeof this._startTime === 'bigint') {
|
|
95
91
|
// startTime was created using process.hrtime.bigint()
|
|
96
92
|
const endTime = process.hrtime.bigint();
|
|
97
93
|
// get the difference, and convert to number
|
|
98
|
-
nanoDuration = Number(endTime - this.
|
|
94
|
+
nanoDuration = Number(endTime - this._startTime);
|
|
99
95
|
} else {
|
|
100
|
-
throw new Error(`Unable to get duration. Start time '${this.
|
|
96
|
+
throw new Error(`Unable to get duration. Start time '${this._startTime}' cannot be parsed`);
|
|
101
97
|
}
|
|
102
98
|
|
|
103
99
|
return new Duration(nanoDuration);
|
|
104
100
|
}
|
|
105
101
|
|
|
106
|
-
toString() {
|
|
102
|
+
toString(): string {
|
|
107
103
|
try {
|
|
108
104
|
return this.getDuration().toString();
|
|
109
105
|
} catch (err) {
|
|
110
|
-
return `<err: ${err.message}>`;
|
|
106
|
+
return `<err: ${(err as Error).message}>`;
|
|
111
107
|
}
|
|
112
108
|
}
|
|
113
109
|
}
|
|
114
110
|
|
|
115
|
-
export {Timer
|
|
116
|
-
export default Timer;
|
|
111
|
+
export {Timer as default};
|