appium 3.3.0 → 3.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. package/build/lib/appium.d.ts +147 -205
  2. package/build/lib/appium.d.ts.map +1 -1
  3. package/build/lib/appium.js +169 -282
  4. package/build/lib/appium.js.map +1 -1
  5. package/build/lib/bidi-commands.d.ts.map +1 -1
  6. package/build/lib/bidi-commands.js +11 -11
  7. package/build/lib/bidi-commands.js.map +1 -1
  8. package/build/lib/bootstrap/appium-initializer.d.ts +21 -0
  9. package/build/lib/bootstrap/appium-initializer.d.ts.map +1 -0
  10. package/build/lib/bootstrap/appium-initializer.js +146 -0
  11. package/build/lib/bootstrap/appium-initializer.js.map +1 -0
  12. package/build/lib/bootstrap/appium-main-runner.d.ts +22 -0
  13. package/build/lib/bootstrap/appium-main-runner.d.ts.map +1 -0
  14. package/build/lib/bootstrap/appium-main-runner.js +109 -0
  15. package/build/lib/bootstrap/appium-main-runner.js.map +1 -0
  16. package/build/lib/bootstrap/config-file.d.ts +37 -0
  17. package/build/lib/bootstrap/config-file.d.ts.map +1 -0
  18. package/build/lib/{config-file.js → bootstrap/config-file.js} +9 -26
  19. package/build/lib/bootstrap/config-file.js.map +1 -0
  20. package/build/lib/bootstrap/grid-v3-register.d.ts +20 -0
  21. package/build/lib/bootstrap/grid-v3-register.d.ts.map +1 -0
  22. package/build/lib/{grid-register.js → bootstrap/grid-v3-register.js} +28 -13
  23. package/build/lib/bootstrap/grid-v3-register.js.map +1 -0
  24. package/build/lib/bootstrap/init-types.d.ts +16 -0
  25. package/build/lib/bootstrap/init-types.d.ts.map +1 -0
  26. package/build/lib/bootstrap/init-types.js +3 -0
  27. package/build/lib/bootstrap/init-types.js.map +1 -0
  28. package/build/lib/bootstrap/main-helpers.d.ts +55 -0
  29. package/build/lib/bootstrap/main-helpers.d.ts.map +1 -0
  30. package/build/lib/bootstrap/main-helpers.js +187 -0
  31. package/build/lib/bootstrap/main-helpers.js.map +1 -0
  32. package/build/lib/bootstrap/node-helpers.d.ts +32 -0
  33. package/build/lib/bootstrap/node-helpers.d.ts.map +1 -0
  34. package/build/lib/bootstrap/node-helpers.js +201 -0
  35. package/build/lib/bootstrap/node-helpers.js.map +1 -0
  36. package/build/lib/bootstrap/startup-config.d.ts +22 -0
  37. package/build/lib/bootstrap/startup-config.d.ts.map +1 -0
  38. package/build/lib/bootstrap/startup-config.js +111 -0
  39. package/build/lib/bootstrap/startup-config.js.map +1 -0
  40. package/build/lib/cli/args.d.ts.map +1 -1
  41. package/build/lib/cli/args.js +9 -9
  42. package/build/lib/cli/args.js.map +1 -1
  43. package/build/lib/cli/extension-command.d.ts +95 -95
  44. package/build/lib/cli/extension-command.d.ts.map +1 -1
  45. package/build/lib/cli/extension-command.js +18 -18
  46. package/build/lib/cli/extension-command.js.map +1 -1
  47. package/build/lib/cli/extension.d.ts +1 -1
  48. package/build/lib/cli/extension.d.ts.map +1 -1
  49. package/build/lib/cli/extension.js +5 -5
  50. package/build/lib/cli/extension.js.map +1 -1
  51. package/build/lib/cli/parser.d.ts +8 -8
  52. package/build/lib/cli/parser.d.ts.map +1 -1
  53. package/build/lib/cli/parser.js +49 -49
  54. package/build/lib/cli/parser.js.map +1 -1
  55. package/build/lib/cli/setup-command.js +6 -6
  56. package/build/lib/cli/setup-command.js.map +1 -1
  57. package/build/lib/cli/utils.d.ts +17 -17
  58. package/build/lib/cli/utils.d.ts.map +1 -1
  59. package/build/lib/cli/utils.js +29 -29
  60. package/build/lib/cli/utils.js.map +1 -1
  61. package/build/lib/doctor/doctor.d.ts +2 -2
  62. package/build/lib/doctor/doctor.d.ts.map +1 -1
  63. package/build/lib/doctor/doctor.js +6 -6
  64. package/build/lib/doctor/doctor.js.map +1 -1
  65. package/build/lib/extension/driver-config.d.ts +18 -77
  66. package/build/lib/extension/driver-config.d.ts.map +1 -1
  67. package/build/lib/extension/driver-config.js +37 -125
  68. package/build/lib/extension/driver-config.js.map +1 -1
  69. package/build/lib/extension/extension-config.d.ts +103 -210
  70. package/build/lib/extension/extension-config.d.ts.map +1 -1
  71. package/build/lib/extension/extension-config.js +180 -342
  72. package/build/lib/extension/extension-config.js.map +1 -1
  73. package/build/lib/extension/index.d.ts +12 -29
  74. package/build/lib/extension/index.d.ts.map +1 -1
  75. package/build/lib/extension/index.js +33 -75
  76. package/build/lib/extension/index.js.map +1 -1
  77. package/build/lib/extension/manifest-migrations.d.ts +3 -20
  78. package/build/lib/extension/manifest-migrations.d.ts.map +1 -1
  79. package/build/lib/extension/manifest-migrations.js +20 -101
  80. package/build/lib/extension/manifest-migrations.js.map +1 -1
  81. package/build/lib/extension/manifest.d.ts +61 -107
  82. package/build/lib/extension/manifest.d.ts.map +1 -1
  83. package/build/lib/extension/manifest.js +181 -356
  84. package/build/lib/extension/manifest.js.map +1 -1
  85. package/build/lib/extension/package-changed.d.ts +1 -3
  86. package/build/lib/extension/package-changed.d.ts.map +1 -1
  87. package/build/lib/extension/package-changed.js +8 -15
  88. package/build/lib/extension/package-changed.js.map +1 -1
  89. package/build/lib/extension/plugin-config.d.ts +10 -52
  90. package/build/lib/extension/plugin-config.d.ts.map +1 -1
  91. package/build/lib/extension/plugin-config.js +11 -63
  92. package/build/lib/extension/plugin-config.js.map +1 -1
  93. package/build/lib/helpers/build.d.ts +22 -0
  94. package/build/lib/helpers/build.d.ts.map +1 -0
  95. package/build/lib/helpers/build.js +109 -0
  96. package/build/lib/helpers/build.js.map +1 -0
  97. package/build/lib/helpers/capability.d.ts +38 -0
  98. package/build/lib/helpers/capability.d.ts.map +1 -0
  99. package/build/lib/helpers/capability.js +128 -0
  100. package/build/lib/helpers/capability.js.map +1 -0
  101. package/build/lib/helpers/network.d.ts +14 -0
  102. package/build/lib/helpers/network.d.ts.map +1 -0
  103. package/build/lib/helpers/network.js +35 -0
  104. package/build/lib/helpers/network.js.map +1 -0
  105. package/build/lib/insecure-features.js +6 -6
  106. package/build/lib/insecure-features.js.map +1 -1
  107. package/build/lib/inspector-commands.d.ts +6 -0
  108. package/build/lib/inspector-commands.d.ts.map +1 -1
  109. package/build/lib/inspector-commands.js +6 -0
  110. package/build/lib/inspector-commands.js.map +1 -1
  111. package/build/lib/logger.d.ts +2 -3
  112. package/build/lib/logger.d.ts.map +1 -1
  113. package/build/lib/logger.js +2 -3
  114. package/build/lib/logger.js.map +1 -1
  115. package/build/lib/main.d.ts +15 -58
  116. package/build/lib/main.d.ts.map +1 -1
  117. package/build/lib/main.js +25 -425
  118. package/build/lib/main.js.map +1 -1
  119. package/build/lib/schema/cli-args-guards.d.ts +34 -0
  120. package/build/lib/schema/cli-args-guards.d.ts.map +1 -0
  121. package/build/lib/schema/cli-args-guards.js +49 -0
  122. package/build/lib/schema/cli-args-guards.js.map +1 -0
  123. package/build/lib/schema/cli-args.js +2 -2
  124. package/build/lib/schema/cli-args.js.map +1 -1
  125. package/build/lib/schema/format-errors.d.ts +28 -0
  126. package/build/lib/schema/format-errors.d.ts.map +1 -0
  127. package/build/lib/schema/format-errors.js +29 -0
  128. package/build/lib/schema/format-errors.js.map +1 -0
  129. package/build/lib/schema/index.d.ts +2 -0
  130. package/build/lib/schema/index.d.ts.map +1 -1
  131. package/build/lib/schema/index.js +2 -0
  132. package/build/lib/schema/index.js.map +1 -1
  133. package/build/lib/schema/schema.d.ts +15 -15
  134. package/build/lib/schema/schema.d.ts.map +1 -1
  135. package/build/lib/schema/schema.js +37 -37
  136. package/build/lib/schema/schema.js.map +1 -1
  137. package/build/lib/utils.d.ts +0 -81
  138. package/build/lib/utils.d.ts.map +1 -1
  139. package/build/lib/utils.js +1 -248
  140. package/build/lib/utils.js.map +1 -1
  141. package/lib/{appium.js → appium.ts} +297 -341
  142. package/lib/bidi-commands.ts +10 -14
  143. package/lib/bootstrap/appium-initializer.ts +212 -0
  144. package/lib/bootstrap/appium-main-runner.ts +172 -0
  145. package/lib/{config-file.ts → bootstrap/config-file.ts} +29 -63
  146. package/lib/{grid-register.ts → bootstrap/grid-v3-register.ts} +35 -35
  147. package/lib/bootstrap/init-types.ts +31 -0
  148. package/lib/bootstrap/main-helpers.ts +223 -0
  149. package/lib/bootstrap/node-helpers.ts +180 -0
  150. package/lib/bootstrap/startup-config.ts +143 -0
  151. package/lib/cli/args.ts +10 -10
  152. package/lib/cli/extension-command.ts +132 -132
  153. package/lib/cli/extension.ts +7 -7
  154. package/lib/cli/parser.ts +50 -50
  155. package/lib/cli/setup-command.ts +2 -2
  156. package/lib/cli/utils.ts +33 -33
  157. package/lib/doctor/doctor.ts +8 -8
  158. package/lib/extension/driver-config.ts +165 -0
  159. package/lib/extension/{extension-config.js → extension-config.ts} +291 -405
  160. package/lib/extension/index.ts +143 -0
  161. package/lib/extension/manifest-migrations.ts +57 -0
  162. package/lib/extension/manifest.ts +369 -0
  163. package/lib/extension/{package-changed.js → package-changed.ts} +9 -18
  164. package/lib/extension/plugin-config.ts +62 -0
  165. package/lib/helpers/build.ts +111 -0
  166. package/lib/helpers/capability.ts +171 -0
  167. package/lib/helpers/network.ts +30 -0
  168. package/lib/insecure-features.ts +1 -1
  169. package/lib/inspector-commands.ts +6 -1
  170. package/lib/{logger.js → logger.ts} +1 -2
  171. package/lib/main.ts +60 -0
  172. package/lib/schema/cli-args-guards.ts +67 -0
  173. package/lib/schema/cli-args.ts +1 -1
  174. package/lib/schema/format-errors.ts +43 -0
  175. package/lib/schema/index.ts +2 -0
  176. package/lib/schema/schema.ts +51 -52
  177. package/lib/utils.ts +0 -331
  178. package/package.json +12 -13
  179. package/scripts/autoinstall-extensions.js +3 -0
  180. package/build/lib/config-file.d.ts +0 -57
  181. package/build/lib/config-file.d.ts.map +0 -1
  182. package/build/lib/config-file.js.map +0 -1
  183. package/build/lib/config.d.ts +0 -68
  184. package/build/lib/config.d.ts.map +0 -1
  185. package/build/lib/config.js +0 -358
  186. package/build/lib/config.js.map +0 -1
  187. package/build/lib/grid-register.d.ts +0 -35
  188. package/build/lib/grid-register.d.ts.map +0 -1
  189. package/build/lib/grid-register.js.map +0 -1
  190. package/lib/config.ts +0 -377
  191. package/lib/extension/driver-config.js +0 -245
  192. package/lib/extension/index.js +0 -169
  193. package/lib/extension/manifest-migrations.js +0 -136
  194. package/lib/extension/manifest.js +0 -550
  195. package/lib/extension/plugin-config.js +0 -112
  196. package/lib/main.js +0 -545
@@ -0,0 +1,223 @@
1
+ import {inspect as dump, type InspectOptions} from 'node:util';
2
+ import {log as logger} from '../logger';
3
+ import {
4
+ routeConfiguringFunction as makeRouter,
5
+ normalizeBasePath,
6
+ server as baseServer,
7
+ type ServerOpts,
8
+ } from '@appium/base-driver';
9
+ import _ from 'lodash';
10
+ import type {AppiumServer, Driver, MethodMap, UpdateServerCallback} from '@appium/types';
11
+ import {WebSocketServer} from 'ws';
12
+ import type {NetworkInterfaceInfo} from 'node:os';
13
+ import type {AppiumDriver} from '../appium';
14
+ import {APPIUM_VER, getBuildInfo, getGitRev, updateBuildInfo} from '../helpers/build';
15
+ import {checkNodeOk, requireDir} from './node-helpers';
16
+ import {getNonDefaultServerArgs} from './startup-config';
17
+ import {validate as validateSchema} from '../schema/schema';
18
+ import {fetchInterfaces, V4_BROADCAST_IP, isBroadcastIp} from '../helpers/network';
19
+ import {LONG_STACKTRACE_LIMIT, BIDI_BASE_PATH} from '../constants';
20
+ import type {Args, ParsedArgs, CliCommandServer} from 'appium/types';
21
+ import type {DriverNameMap, PluginNameMap} from '../extension';
22
+
23
+ const isStdoutTTY = process.stdout.isTTY;
24
+
25
+ /**
26
+ * Logs a value to the console using the info logger (with util.inspect formatting).
27
+ */
28
+ export const inspect = _.flow(
29
+ _.partialRight(dump as (object: unknown, options: InspectOptions) => string, {
30
+ colors: true,
31
+ depth: null,
32
+ compact: !isStdoutTTY,
33
+ }),
34
+ (...args: unknown[]) => {
35
+ logger.info(...args);
36
+ },
37
+ );
38
+
39
+ /**
40
+ * Prints the current build info JSON to stdout.
41
+ *
42
+ * This updates build metadata first (using GitHub fallback) and then logs the
43
+ * resulting {@link BuildInfo} object.
44
+ */
45
+ export async function showBuildInfo(): Promise<void> {
46
+ await updateBuildInfo(true);
47
+ // eslint-disable-next-line no-console -- CLI output for --build-info
48
+ console.log(JSON.stringify(getBuildInfo()));
49
+ }
50
+
51
+ /**
52
+ * Human-readable label for where Appium resolved `APPIUM_HOME` from (CLI, env, or autodetect).
53
+ */
54
+ export function determineAppiumHomeSource(appiumHomeFromArgs?: string | null): string {
55
+ if (!_.isNil(appiumHomeFromArgs)) {
56
+ return 'appiumHome config value';
57
+ }
58
+ if (process.env.APPIUM_HOME) {
59
+ return 'APPIUM_HOME environment variable';
60
+ }
61
+ return 'autodetected Appium home path';
62
+ }
63
+
64
+ /**
65
+ * Logs the REST listener URL; if the bind address is a broadcast address, lists concrete interface URLs.
66
+ */
67
+ export function logServerAddress(url: string): void {
68
+ const urlObj = new URL(url);
69
+ logger.info(`Appium REST http interface listener started on ${url}`);
70
+ if (!isBroadcastIp(urlObj.hostname)) {
71
+ return;
72
+ }
73
+
74
+ const interfaces = fetchInterfaces(urlObj.hostname === V4_BROADCAST_IP ? 4 : 6);
75
+ const toLabel = (iface: NetworkInterfaceInfo) => {
76
+ const href = urlObj.href.replace(urlObj.hostname, iface.address);
77
+ return iface.internal ? `${href} (only accessible from the same host)` : href;
78
+ };
79
+ logger.info(
80
+ `You can provide the following ${interfaces.length === 1 ? 'URL' : 'URLs'} ` +
81
+ `in your client code to connect to this server:\n` +
82
+ interfaces.map((iface) => `\t${toLabel(iface)}`).join('\n'),
83
+ );
84
+ }
85
+
86
+ /**
87
+ * Validates Node version, optional long stack traces, schema, tmp dir, and `--build-info` early exit.
88
+ *
89
+ * @param args - Parsed server CLI args
90
+ * @param throwInsteadOfExit - When true, rethrows failures instead of calling `process.exit(1)`
91
+ */
92
+ export async function preflightChecks(
93
+ args: ParsedArgs<CliCommandServer>,
94
+ throwInsteadOfExit = false,
95
+ ): Promise<void> {
96
+ try {
97
+ checkNodeOk();
98
+ if (args.longStacktrace) {
99
+ Error.stackTraceLimit = LONG_STACKTRACE_LIMIT;
100
+ }
101
+ if (args.showBuildInfo) {
102
+ await showBuildInfo();
103
+ process.exit(0);
104
+ }
105
+
106
+ validateSchema(args);
107
+
108
+ if (args.tmpDir) {
109
+ await requireDir(args.tmpDir, !args.noPermsCheck, 'tmpDir argument value');
110
+ }
111
+ } catch (err: unknown) {
112
+ const message = err instanceof Error ? err.message : String(err);
113
+ logger.error((message as string & {red: string}).red);
114
+ if (throwInsteadOfExit) {
115
+ throw err;
116
+ }
117
+
118
+ process.exit(1);
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Prints welcome line (version + optional git rev), non-default server args, and default capabilities.
124
+ */
125
+ export async function logStartupInfo(args: ParsedArgs<CliCommandServer>): Promise<void> {
126
+ let welcome = `Welcome to Appium v${APPIUM_VER}`;
127
+ const appiumRev = await getGitRev();
128
+ if (appiumRev) {
129
+ welcome += ` (REV ${appiumRev})`;
130
+ }
131
+ logger.info(welcome);
132
+
133
+ const showArgs = getNonDefaultServerArgs(args);
134
+ if (_.size(showArgs)) {
135
+ logNonDefaultArgsWarning(showArgs);
136
+ }
137
+ if (!_.isEmpty(args.defaultCapabilities)) {
138
+ logDefaultCapabilitiesWarning(args.defaultCapabilities);
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Collects `updateServer` hooks from active driver and plugin classes for HTTP server customization.
144
+ */
145
+ export function getServerUpdaters(
146
+ driverClasses: DriverNameMap,
147
+ pluginClasses: PluginNameMap,
148
+ ): UpdateServerCallback[] {
149
+ return _.compact(_.map([...driverClasses.keys(), ...pluginClasses.keys()], 'updateServer'));
150
+ }
151
+
152
+ /**
153
+ * Merges `newMethodMap` contributions from all active drivers and plugins into one method map.
154
+ */
155
+ export function getExtraMethodMap(driverClasses: DriverNameMap, pluginClasses: PluginNameMap): MethodMap<Driver> {
156
+ return [...driverClasses.keys(), ...pluginClasses.keys()].reduce<MethodMap<Driver>>(
157
+ (map, klass) => ({
158
+ ...map,
159
+ ...(klass.newMethodMap ?? {}),
160
+ }),
161
+ {},
162
+ );
163
+ }
164
+
165
+ /**
166
+ * Builds {@link ServerOpts} and normalized base path for the Appium HTTP server from CLI args and extensions.
167
+ */
168
+ export function buildServerOpts(
169
+ appiumDriver: AppiumDriver,
170
+ parsedArgs: ParsedArgs<CliCommandServer>,
171
+ driverClasses: DriverNameMap,
172
+ pluginClasses: PluginNameMap,
173
+ ): {serverOpts: ServerOpts; normalizedBasePath: string} {
174
+ const routeConfiguringFunction = makeRouter(appiumDriver);
175
+ const serverOpts: ServerOpts = {
176
+ routeConfiguringFunction,
177
+ port: parsedArgs.port,
178
+ hostname: parsedArgs.address,
179
+ allowCors: parsedArgs.allowCors,
180
+ basePath: parsedArgs.basePath,
181
+ serverUpdaters: getServerUpdaters(driverClasses, pluginClasses),
182
+ extraMethodMap: getExtraMethodMap(driverClasses, pluginClasses),
183
+ cliArgs: parsedArgs,
184
+ };
185
+ const normalizedBasePath = normalizeBasePath(parsedArgs.basePath);
186
+ for (const timeoutArgName of ['keepAliveTimeout', 'requestTimeout'] as const) {
187
+ if (_.isInteger(parsedArgs[timeoutArgName])) {
188
+ serverOpts[timeoutArgName] = parsedArgs[timeoutArgName] * 1000;
189
+ }
190
+ }
191
+ return {serverOpts, normalizedBasePath};
192
+ }
193
+
194
+ /**
195
+ * Creates the Appium HTTP server and attaches WebSocket handlers for BiDi under the normalized base path.
196
+ */
197
+ export async function createAppiumServer(
198
+ serverOpts: ServerOpts,
199
+ appiumDriver: AppiumDriver,
200
+ normalizedBasePath: string,
201
+ ): Promise<AppiumServer> {
202
+ const bidiServer = new WebSocketServer({noServer: true});
203
+ bidiServer.on('connection', appiumDriver.onBidiConnection.bind(appiumDriver));
204
+ bidiServer.on('error', appiumDriver.onBidiServerError.bind(appiumDriver));
205
+ const server = await baseServer(serverOpts);
206
+ const bidiBasePath = `${normalizedBasePath}${BIDI_BASE_PATH}`;
207
+ await server.addWebSocketHandler(bidiBasePath, bidiServer);
208
+ await server.addWebSocketHandler(`${bidiBasePath}/:sessionId`, bidiServer);
209
+ return server;
210
+ }
211
+
212
+ function logNonDefaultArgsWarning(args: Args): void {
213
+ logger.info('Non-default server args:');
214
+ inspect(args);
215
+ }
216
+
217
+ function logDefaultCapabilitiesWarning(caps: ParsedArgs<CliCommandServer>['defaultCapabilities']): void {
218
+ logger.info(
219
+ 'Default capabilities, which will be added to each request ' +
220
+ 'unless overridden by desired capabilities:',
221
+ );
222
+ inspect(caps);
223
+ }
@@ -0,0 +1,180 @@
1
+ /* eslint-disable no-console */
2
+ import _ from 'lodash';
3
+ import path from 'node:path';
4
+ import os from 'node:os';
5
+ import * as semver from 'semver';
6
+ import B from 'bluebird';
7
+ import {system, fs, npm} from '@appium/support';
8
+ import {log as logger} from '../logger';
9
+ import {getAppiumModuleRoot, npmPackage} from '../utils';
10
+ import {rootDir, updateBuildInfo, getBuildInfo} from '../helpers/build';
11
+
12
+ const MIN_NODE_VERSION = (npmPackage.engines as Record<string, string>).node;
13
+
14
+ interface NodeModuleWithInitPaths {
15
+ Module: {_initPaths(): void};
16
+ }
17
+
18
+ interface DebugInfoInput {
19
+ driverConfig: {installedExtensions: unknown};
20
+ pluginConfig: {installedExtensions: unknown};
21
+ appiumHome: string;
22
+ }
23
+
24
+ /**
25
+ * @throws {Error} If Node version is outside of the supported range
26
+ */
27
+ export function checkNodeOk(): void {
28
+ const version = getNodeVersion();
29
+ if (!semver.satisfies(version, MIN_NODE_VERSION)) {
30
+ throw new Error(`Node version must be at least ${MIN_NODE_VERSION}; current is ${version.version}`);
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Adjusts NODE_PATH so CJS drivers/plugins can load peer deps. Does not work with ESM.
36
+ */
37
+ export function adjustNodePath(): void {
38
+ let appiumModuleSearchRoot: string;
39
+ try {
40
+ appiumModuleSearchRoot = path.dirname(getAppiumModuleRoot());
41
+ } catch (error) {
42
+ logger.warn((error as Error).message);
43
+ return;
44
+ }
45
+
46
+ const refreshRequirePaths = (): boolean => {
47
+ try {
48
+ // Private API; see https://gist.github.com/branneman/8048520#7-the-hack
49
+ (require('node:module') as NodeModuleWithInitPaths).Module._initPaths();
50
+ return true;
51
+ } catch {
52
+ return false;
53
+ }
54
+ };
55
+
56
+ if (!process.env.NODE_PATH) {
57
+ process.env.NODE_PATH = appiumModuleSearchRoot;
58
+ if (refreshRequirePaths()) {
59
+ process.env.APPIUM_OMIT_PEER_DEPS = '1';
60
+ } else {
61
+ delete process.env.NODE_PATH;
62
+ }
63
+ return;
64
+ }
65
+
66
+ const nodePathParts = process.env.NODE_PATH.split(path.delimiter);
67
+ if (nodePathParts.includes(appiumModuleSearchRoot)) {
68
+ process.env.APPIUM_OMIT_PEER_DEPS = '1';
69
+ return;
70
+ }
71
+
72
+ nodePathParts.push(appiumModuleSearchRoot);
73
+ process.env.NODE_PATH = nodePathParts.join(path.delimiter);
74
+ if (refreshRequirePaths()) {
75
+ process.env.APPIUM_OMIT_PEER_DEPS = '1';
76
+ } else {
77
+ process.env.NODE_PATH = _.without(nodePathParts, appiumModuleSearchRoot).join(path.delimiter);
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Prints JSON debug info (OS, Node/npm, Appium build, installed drivers/plugins) to stdout.
83
+ * Input combines extension config snapshots with the resolved Appium home path.
84
+ */
85
+ export async function showDebugInfo({driverConfig, pluginConfig, appiumHome}: DebugInfoInput): Promise<void> {
86
+ const getNpmVersion = async (): Promise<string> => {
87
+ const {stdout} = await npm.exec('--version', [], {cwd: process.cwd()});
88
+ return _.trim(stdout);
89
+ };
90
+ const findNpmLocation = async (): Promise<string> =>
91
+ await fs.which(system.isWindows() ? 'npm.cmd' : 'npm');
92
+
93
+ const [npmVersion, npmLocation] = await B.all([
94
+ ...[getNpmVersion, findNpmLocation].map((f) => getSafeResult(f, 'unknown')),
95
+ updateBuildInfo() as Promise<unknown>,
96
+ ]);
97
+
98
+ const debugInfo = {
99
+ os: {
100
+ platform: os.platform(),
101
+ release: os.release(),
102
+ arch: os.arch(),
103
+ homedir: os.homedir(),
104
+ username: os.userInfo().username,
105
+ },
106
+ node: {
107
+ version: process.version,
108
+ arch: process.arch,
109
+ cwd: process.cwd(),
110
+ argv: process.argv,
111
+ env: process.env,
112
+ npm: {
113
+ location: npmLocation,
114
+ version: npmVersion,
115
+ },
116
+ },
117
+ appium: {
118
+ location: rootDir,
119
+ homedir: appiumHome,
120
+ build: getBuildInfo(),
121
+ drivers: driverConfig.installedExtensions,
122
+ plugins: pluginConfig.installedExtensions,
123
+ },
124
+ };
125
+ console.log(JSON.stringify(debugInfo, null, 2));
126
+ }
127
+
128
+ /**
129
+ * Ensures a directory exists and (optionally) is writeable.
130
+ *
131
+ * If the directory does not exist, this attempts to create it recursively.
132
+ *
133
+ * @throws {Error}
134
+ */
135
+ export async function requireDir(
136
+ root: string,
137
+ requireWriteable = true,
138
+ displayName = 'folder path'
139
+ ): Promise<void> {
140
+ let stat;
141
+ try {
142
+ stat = await fs.stat(root);
143
+ } catch (e) {
144
+ if ((e as NodeJS.ErrnoException).code === 'ENOENT') {
145
+ try {
146
+ await fs.mkdir(root, {recursive: true});
147
+ return;
148
+ } catch {}
149
+ }
150
+ throw new Error(`The ${displayName} '${root}' must exist and be a valid directory`);
151
+ }
152
+ if (stat && !stat.isDirectory()) {
153
+ throw new Error(`The ${displayName} '${root}' must be a valid directory`);
154
+ }
155
+ if (requireWriteable) {
156
+ try {
157
+ await fs.access(root, fs.constants.W_OK);
158
+ } catch {
159
+ throw new Error(
160
+ `The ${displayName} '${root}' must be writeable for the current user account '${os.userInfo().username}'`
161
+ );
162
+ }
163
+ }
164
+ }
165
+
166
+ function getNodeVersion(): semver.SemVer {
167
+ return semver.coerce(process.version) as semver.SemVer;
168
+ }
169
+
170
+ /**
171
+ * Calculates the result of the given function and return its value
172
+ * or the default one if there was an exception.
173
+ */
174
+ async function getSafeResult<T>(f: () => Promise<T>, defaultValue: T): Promise<T> {
175
+ try {
176
+ return await f();
177
+ } catch {
178
+ return defaultValue;
179
+ }
180
+ }
@@ -0,0 +1,143 @@
1
+ /* eslint-disable no-console */
2
+ import _ from 'lodash';
3
+ import {getDefaultsForSchema, getAllArgSpecs} from '../schema/schema';
4
+ import type {Args} from 'appium/types';
5
+ import type {ReadConfigFileResult} from './config-file';
6
+
7
+ interface FlattenedArg {
8
+ value: unknown;
9
+ argSpec: {dest: string};
10
+ }
11
+
12
+ /**
13
+ * Returns key/value pairs of server arguments that differ from schema defaults (flattened comparison).
14
+ *
15
+ * @param parsedArgs - Fully merged server args (CLI + config + defaults)
16
+ */
17
+ export function getNonDefaultServerArgs(parsedArgs: Args): Args {
18
+ /**
19
+ * Flattens parsed args into a single level object for comparison with
20
+ * flattened defaults across server args and extension args.
21
+ */
22
+ const flatten = (args: Args): Record<string, FlattenedArg> => {
23
+ const argSpecs = getAllArgSpecs();
24
+ const flattened = _.reduce(
25
+ [...argSpecs.values()],
26
+ (acc: Record<string, FlattenedArg>, argSpec: {dest: string}) => {
27
+ if (_.has(args, argSpec.dest)) {
28
+ acc[argSpec.dest] = {value: _.get(args, argSpec.dest), argSpec};
29
+ }
30
+ return acc;
31
+ },
32
+ {}
33
+ );
34
+
35
+ return flattened;
36
+ };
37
+
38
+ const args = flatten(parsedArgs);
39
+
40
+ // hopefully these function names are descriptive enough
41
+ const typesDiffer = (dest: string): boolean =>
42
+ typeof args[dest].value !== typeof defaultsFromSchema[dest];
43
+
44
+ const defaultValueIsArray = (dest: string): boolean => _.isArray(defaultsFromSchema[dest]);
45
+
46
+ const argsValueIsArray = (dest: string): boolean => _.isArray(args[dest].value);
47
+
48
+ const arraysDiffer = (dest: string): boolean =>
49
+ _.gt(_.size(_.difference(args[dest].value as any[], defaultsFromSchema[dest] as any[])), 0);
50
+
51
+ const valuesDiffer = (dest: string): boolean => args[dest].value !== defaultsFromSchema[dest];
52
+
53
+ const defaultIsDefined = (dest: string): boolean => !_.isUndefined(defaultsFromSchema[dest]);
54
+
55
+ // note that `_.overEvery` is like an "AND", and `_.overSome` is like an "OR"
56
+ const argValueNotArrayOrArraysDiffer = _.overSome([_.negate(argsValueIsArray), arraysDiffer]);
57
+
58
+ const defaultValueNotArrayAndValuesDiffer = _.overEvery([
59
+ _.negate(defaultValueIsArray),
60
+ valuesDiffer,
61
+ ]);
62
+
63
+ /**
64
+ * This used to be a hideous conditional, but it's broken up into a hideous function instead.
65
+ * hopefully this makes things a little more understandable.
66
+ * - checks if the default value is defined
67
+ * - if so, and the default is not an array:
68
+ * - ensures the types are the same
69
+ * - ensures the values are equal
70
+ * - if so, and the default is an array:
71
+ * - ensures the args value is an array
72
+ * - ensures the args values do not differ from the default values
73
+ */
74
+ const isNotDefault = _.overEvery([
75
+ defaultIsDefined,
76
+ _.overSome([
77
+ typesDiffer,
78
+ _.overEvery([defaultValueIsArray, argValueNotArrayOrArraysDiffer]),
79
+ defaultValueNotArrayAndValuesDiffer,
80
+ ]),
81
+ ]);
82
+
83
+ const defaultsFromSchema = getDefaultsForSchema(true) as Record<string, unknown>;
84
+
85
+ return _.reduce(
86
+ _.pickBy(args, (_v, key) => isNotDefault(key)),
87
+ // explodes the flattened object back into nested one
88
+ (acc: Args, {value, argSpec}: FlattenedArg) => _.set(acc, argSpec.dest, value),
89
+ {} as Args
90
+ );
91
+ }
92
+
93
+ /**
94
+ * Prints a breakdown of configuration: defaults, config file, CLI/programmatic overrides, and final merged args.
95
+ *
96
+ * The actual shape of `nonDefaultPreConfigParsedArgs` and `defaults` does not matter for the purposes of this
97
+ * function, but it's intended to be called with values of type {@link ParsedArgs} and
98
+ * `DefaultValues<true>`, respectively.
99
+ *
100
+ * @param nonDefaultPreConfigParsedArgs - CLI-only (or programmatic) args that differ from defaults
101
+ * @param configResult - Result of {@link readConfigFile}
102
+ * @param defaults - Schema default values
103
+ * @param parsedArgs - Final merged configuration
104
+ */
105
+ export function showConfig(
106
+ nonDefaultPreConfigParsedArgs: Partial<Args>,
107
+ configResult: ReadConfigFileResult,
108
+ defaults: Partial<Args>,
109
+ parsedArgs: Args
110
+ ): void {
111
+ console.log('Appium Configuration\n');
112
+ console.log('from defaults:\n');
113
+ console.dir(compactConfig(defaults));
114
+ if (configResult.config) {
115
+ console.log(`\nfrom config file at ${configResult.filepath}:\n`);
116
+ console.dir(compactConfig(configResult.config));
117
+ } else {
118
+ console.log(`\n(no configuration file loaded)`);
119
+ }
120
+ const compactedNonDefaultPreConfigArgs = compactConfig(nonDefaultPreConfigParsedArgs);
121
+ if (_.isEmpty(compactedNonDefaultPreConfigArgs)) {
122
+ console.log(`\n(no CLI parameters provided)`);
123
+ } else {
124
+ console.log('\nvia CLI or function call:\n');
125
+ console.dir(compactedNonDefaultPreConfigArgs);
126
+ }
127
+ console.log('\nfinal configuration:\n');
128
+ console.dir(compactConfig(parsedArgs));
129
+ }
130
+
131
+ /**
132
+ * Compacts an object for {@link showConfig}:
133
+ * 1. Removes `subcommand` key/value
134
+ * 2. Removes `undefined` values
135
+ * 3. Removes empty objects (but not `false` values)
136
+ * Does not operate recursively.
137
+ */
138
+ const compactConfig = _.partial(
139
+ _.omitBy,
140
+ _,
141
+ (value: unknown, key: string) =>
142
+ key === 'subcommand' || _.isUndefined(value) || (_.isObject(value) && _.isEmpty(value))
143
+ );
package/lib/cli/args.ts CHANGED
@@ -70,6 +70,16 @@ export const getExtensionArgs = _.memoize(function getExtensionArgs(): Record<
70
70
  return extensionArgs;
71
71
  });
72
72
 
73
+ /**
74
+ * Returns CLI argument definitions for the `server` command.
75
+ *
76
+ * This includes schema-derived options and additional CLI-only options which
77
+ * are intentionally disallowed in config files.
78
+ */
79
+ export function getServerArgs(): ArgumentDefinitions {
80
+ return new Map([...toParserArgs(), ...serverArgsDisallowedInConfig]);
81
+ }
82
+
73
83
  /**
74
84
  * Builds options for the `list` subcommand for an extension type.
75
85
  */
@@ -246,16 +256,6 @@ function makeRunArgs(type: ExtensionType): ArgumentDefinitions {
246
256
  ]);
247
257
  }
248
258
 
249
- /**
250
- * Returns CLI argument definitions for the `server` command.
251
- *
252
- * This includes schema-derived options and additional CLI-only options which
253
- * are intentionally disallowed in config files.
254
- */
255
- export function getServerArgs(): ArgumentDefinitions {
256
- return new Map([...toParserArgs(), ...serverArgsDisallowedInConfig]);
257
- }
258
-
259
259
  /**
260
260
  * These don't make sense in the context of a config file for obvious reasons.
261
261
  */