appium 2.0.0-beta.8 → 2.0.0-rc.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 (206) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +149 -58
  3. package/build/lib/appium.d.ts +229 -0
  4. package/build/lib/appium.d.ts.map +1 -0
  5. package/build/lib/appium.js +678 -441
  6. package/build/lib/appium.js.map +1 -0
  7. package/build/lib/cli/args.d.ts +17 -0
  8. package/build/lib/cli/args.d.ts.map +1 -0
  9. package/build/lib/cli/args.js +263 -300
  10. package/build/lib/cli/args.js.map +1 -0
  11. package/build/lib/cli/driver-command.d.ts +102 -0
  12. package/build/lib/cli/driver-command.d.ts.map +1 -0
  13. package/build/lib/cli/driver-command.js +131 -81
  14. package/build/lib/cli/driver-command.js.map +1 -0
  15. package/build/lib/cli/extension-command.d.ts +402 -0
  16. package/build/lib/cli/extension-command.d.ts.map +1 -0
  17. package/build/lib/cli/extension-command.js +799 -383
  18. package/build/lib/cli/extension-command.js.map +1 -0
  19. package/build/lib/cli/extension.d.ts +23 -0
  20. package/build/lib/cli/extension.d.ts.map +1 -0
  21. package/build/lib/cli/extension.js +71 -60
  22. package/build/lib/cli/extension.js.map +1 -0
  23. package/build/lib/cli/parser.d.ts +84 -0
  24. package/build/lib/cli/parser.d.ts.map +1 -0
  25. package/build/lib/cli/parser.js +252 -148
  26. package/build/lib/cli/parser.js.map +1 -0
  27. package/build/lib/cli/plugin-command.d.ts +99 -0
  28. package/build/lib/cli/plugin-command.d.ts.map +1 -0
  29. package/build/lib/cli/plugin-command.js +125 -81
  30. package/build/lib/cli/plugin-command.js.map +1 -0
  31. package/build/lib/cli/utils.d.ts +29 -0
  32. package/build/lib/cli/utils.d.ts.map +1 -0
  33. package/build/lib/cli/utils.js +72 -51
  34. package/build/lib/cli/utils.js.map +1 -0
  35. package/build/lib/config-file.d.ts +100 -0
  36. package/build/lib/config-file.d.ts.map +1 -0
  37. package/build/lib/config-file.js +207 -0
  38. package/build/lib/config-file.js.map +1 -0
  39. package/build/lib/config.d.ts +49 -0
  40. package/build/lib/config.d.ts.map +1 -0
  41. package/build/lib/config.js +262 -223
  42. package/build/lib/config.js.map +1 -0
  43. package/build/lib/constants.d.ts +56 -0
  44. package/build/lib/constants.d.ts.map +1 -0
  45. package/build/lib/constants.js +73 -0
  46. package/build/lib/constants.js.map +1 -0
  47. package/build/lib/extension/driver-config.d.ts +82 -0
  48. package/build/lib/extension/driver-config.d.ts.map +1 -0
  49. package/build/lib/extension/driver-config.js +210 -0
  50. package/build/lib/extension/driver-config.js.map +1 -0
  51. package/build/lib/extension/extension-config.d.ts +270 -0
  52. package/build/lib/extension/extension-config.d.ts.map +1 -0
  53. package/build/lib/extension/extension-config.js +601 -0
  54. package/build/lib/extension/extension-config.js.map +1 -0
  55. package/build/lib/extension/index.d.ts +48 -0
  56. package/build/lib/extension/index.d.ts.map +1 -0
  57. package/build/lib/extension/index.js +105 -0
  58. package/build/lib/extension/index.js.map +1 -0
  59. package/build/lib/extension/manifest-migrations.d.ts +27 -0
  60. package/build/lib/extension/manifest-migrations.d.ts.map +1 -0
  61. package/build/lib/extension/manifest-migrations.js +134 -0
  62. package/build/lib/extension/manifest-migrations.js.map +1 -0
  63. package/build/lib/extension/manifest.d.ts +145 -0
  64. package/build/lib/extension/manifest.d.ts.map +1 -0
  65. package/build/lib/extension/manifest.js +528 -0
  66. package/build/lib/extension/manifest.js.map +1 -0
  67. package/build/lib/extension/package-changed.d.ts +11 -0
  68. package/build/lib/extension/package-changed.d.ts.map +1 -0
  69. package/build/lib/extension/package-changed.js +62 -0
  70. package/build/lib/extension/package-changed.js.map +1 -0
  71. package/build/lib/extension/plugin-config.d.ts +56 -0
  72. package/build/lib/extension/plugin-config.d.ts.map +1 -0
  73. package/build/lib/extension/plugin-config.js +102 -0
  74. package/build/lib/extension/plugin-config.js.map +1 -0
  75. package/build/lib/grid-register.d.ts +10 -0
  76. package/build/lib/grid-register.d.ts.map +1 -0
  77. package/build/lib/grid-register.js +122 -144
  78. package/build/lib/grid-register.js.map +1 -0
  79. package/build/lib/logger.d.ts +3 -0
  80. package/build/lib/logger.d.ts.map +1 -0
  81. package/build/lib/logger.js +5 -17
  82. package/build/lib/logger.js.map +1 -0
  83. package/build/lib/logsink.d.ts +4 -0
  84. package/build/lib/logsink.d.ts.map +1 -0
  85. package/build/lib/logsink.js +189 -184
  86. package/build/lib/logsink.js.map +1 -0
  87. package/build/lib/main.d.ts +62 -0
  88. package/build/lib/main.d.ts.map +1 -0
  89. package/build/lib/main.js +406 -234
  90. package/build/lib/main.js.map +1 -0
  91. package/build/lib/schema/arg-spec.d.ts +143 -0
  92. package/build/lib/schema/arg-spec.d.ts.map +1 -0
  93. package/build/lib/schema/arg-spec.js +164 -0
  94. package/build/lib/schema/arg-spec.js.map +1 -0
  95. package/build/lib/schema/cli-args.d.ts +19 -0
  96. package/build/lib/schema/cli-args.d.ts.map +1 -0
  97. package/build/lib/schema/cli-args.js +220 -0
  98. package/build/lib/schema/cli-args.js.map +1 -0
  99. package/build/lib/schema/cli-transformers.d.ts +5 -0
  100. package/build/lib/schema/cli-transformers.d.ts.map +1 -0
  101. package/build/lib/schema/cli-transformers.js +124 -0
  102. package/build/lib/schema/cli-transformers.js.map +1 -0
  103. package/build/lib/schema/index.d.ts +3 -0
  104. package/build/lib/schema/index.d.ts.map +1 -0
  105. package/build/lib/schema/index.js +19 -0
  106. package/build/lib/schema/index.js.map +1 -0
  107. package/build/lib/schema/keywords.d.ts +24 -0
  108. package/build/lib/schema/keywords.d.ts.map +1 -0
  109. package/build/lib/schema/keywords.js +128 -0
  110. package/build/lib/schema/keywords.js.map +1 -0
  111. package/build/lib/schema/schema.d.ts +260 -0
  112. package/build/lib/schema/schema.d.ts.map +1 -0
  113. package/build/lib/schema/schema.js +640 -0
  114. package/build/lib/schema/schema.js.map +1 -0
  115. package/build/lib/utils.d.ts +276 -0
  116. package/build/lib/utils.d.ts.map +1 -0
  117. package/build/lib/utils.js +373 -271
  118. package/build/lib/utils.js.map +1 -0
  119. package/build/types/cli.d.ts +134 -0
  120. package/build/types/cli.d.ts.map +1 -0
  121. package/build/types/cli.js +3 -0
  122. package/build/types/cli.js.map +1 -0
  123. package/build/types/index.d.ts +15 -0
  124. package/build/types/index.d.ts.map +1 -0
  125. package/build/types/index.js +19 -0
  126. package/build/types/index.js.map +1 -0
  127. package/build/types/manifest/base.d.ts +135 -0
  128. package/build/types/manifest/base.d.ts.map +1 -0
  129. package/build/types/manifest/base.js +3 -0
  130. package/build/types/manifest/base.js.map +1 -0
  131. package/build/types/manifest/index.d.ts +21 -0
  132. package/build/types/manifest/index.d.ts.map +1 -0
  133. package/build/types/manifest/index.js +42 -0
  134. package/build/types/manifest/index.js.map +1 -0
  135. package/build/types/manifest/v3.d.ts +139 -0
  136. package/build/types/manifest/v3.d.ts.map +1 -0
  137. package/build/types/manifest/v3.js +3 -0
  138. package/build/types/manifest/v3.js.map +1 -0
  139. package/build/types/manifest/v4.d.ts +139 -0
  140. package/build/types/manifest/v4.d.ts.map +1 -0
  141. package/build/types/manifest/v4.js +3 -0
  142. package/build/types/manifest/v4.js.map +1 -0
  143. package/driver.d.ts +1 -0
  144. package/driver.js +14 -0
  145. package/index.js +11 -0
  146. package/lib/appium.js +555 -185
  147. package/lib/cli/args.js +275 -407
  148. package/lib/cli/driver-command.js +132 -24
  149. package/lib/cli/extension-command.js +751 -272
  150. package/lib/cli/extension.js +47 -20
  151. package/lib/cli/parser.js +267 -95
  152. package/lib/cli/plugin-command.js +122 -22
  153. package/lib/cli/utils.js +24 -10
  154. package/lib/config-file.js +220 -0
  155. package/lib/config.js +243 -132
  156. package/lib/constants.js +79 -0
  157. package/lib/extension/driver-config.js +247 -0
  158. package/lib/extension/extension-config.js +709 -0
  159. package/lib/extension/index.js +116 -0
  160. package/lib/extension/manifest-migrations.js +136 -0
  161. package/lib/extension/manifest.js +580 -0
  162. package/lib/extension/package-changed.js +64 -0
  163. package/lib/extension/plugin-config.js +112 -0
  164. package/lib/grid-register.js +49 -35
  165. package/lib/logger.js +1 -2
  166. package/lib/logsink.js +59 -36
  167. package/lib/main.js +392 -104
  168. package/lib/schema/arg-spec.js +229 -0
  169. package/lib/schema/cli-args.js +241 -0
  170. package/lib/schema/cli-transformers.js +119 -0
  171. package/lib/schema/index.js +2 -0
  172. package/lib/schema/keywords.js +136 -0
  173. package/lib/schema/schema.js +725 -0
  174. package/lib/utils.js +315 -167
  175. package/package.json +84 -82
  176. package/plugin.d.ts +1 -0
  177. package/plugin.js +13 -0
  178. package/scripts/autoinstall-extensions.js +243 -0
  179. package/support.d.ts +1 -0
  180. package/support.js +13 -0
  181. package/tsconfig.json +25 -0
  182. package/types/cli.ts +193 -0
  183. package/types/index.ts +20 -0
  184. package/types/manifest/README.md +30 -0
  185. package/types/manifest/base.ts +158 -0
  186. package/types/manifest/index.ts +28 -0
  187. package/types/manifest/v3.ts +161 -0
  188. package/types/manifest/v4.ts +161 -0
  189. package/CHANGELOG.md +0 -3669
  190. package/bin/ios-webkit-debug-proxy-launcher.js +0 -71
  191. package/build/lib/cli/argparse-actions.js +0 -104
  192. package/build/lib/cli/npm.js +0 -207
  193. package/build/lib/cli/parser-helpers.js +0 -93
  194. package/build/lib/driver-config.js +0 -77
  195. package/build/lib/drivers.js +0 -99
  196. package/build/lib/extension-config.js +0 -253
  197. package/build/lib/plugin-config.js +0 -59
  198. package/build/lib/plugins.js +0 -14
  199. package/lib/cli/argparse-actions.js +0 -77
  200. package/lib/cli/npm.js +0 -183
  201. package/lib/cli/parser-helpers.js +0 -91
  202. package/lib/driver-config.js +0 -46
  203. package/lib/drivers.js +0 -84
  204. package/lib/extension-config.js +0 -209
  205. package/lib/plugin-config.js +0 -34
  206. package/lib/plugins.js +0 -10
package/lib/main.js CHANGED
@@ -1,42 +1,65 @@
1
1
  #!/usr/bin/env node
2
- // transpile:main
3
2
 
4
- import { init as logsinkInit } from './logsink';
5
- import logger from './logger'; // logger needs to remain first of imports
3
+ import {init as logsinkInit} from './logsink'; // this import needs to come first since it sets up global npmlog
4
+ import logger from './logger'; // logger needs to remain second
5
+ import {routeConfiguringFunction as makeRouter, server as baseServer} from '@appium/base-driver';
6
+ import {logger as logFactory, util, env, fs} from '@appium/support';
7
+ import {asyncify} from 'asyncbox';
6
8
  import _ from 'lodash';
7
- import { server as baseServer, routeConfiguringFunction as makeRouter } from 'appium-base-driver';
8
- import { asyncify } from 'asyncbox';
9
- import { default as getParser, getDefaultServerArgs } from './cli/parser';
10
- import { USE_ALL_PLUGINS } from './cli/args';
11
- import { logger as logFactory, util } from 'appium-support';
9
+ import {AppiumDriver} from './appium';
10
+ import {runExtensionCommand} from './cli/extension';
11
+ import {getParser} from './cli/parser';
12
12
  import {
13
- showConfig, checkNodeOk, validateServerArgs,
14
- warnNodeDeprecations, validateTmpDir, getNonDefaultArgs,
15
- getGitRev, APPIUM_VER
13
+ APPIUM_VER,
14
+ checkNodeOk,
15
+ getGitRev,
16
+ getNonDefaultServerArgs,
17
+ showConfig,
18
+ showBuildInfo,
19
+ validateTmpDir,
20
+ warnNodeDeprecations,
21
+ checkNpmOk,
16
22
  } from './config';
17
- import DriverConfig from './driver-config';
18
- import PluginConfig from './plugin-config';
19
- import { DRIVER_TYPE, PLUGIN_TYPE } from './extension-config';
20
- import { runExtensionCommand } from './cli/extension';
21
- import { AppiumDriver } from './appium';
23
+ import {readConfigFile} from './config-file';
24
+ import {loadExtensions, getActivePlugins, getActiveDrivers} from './extension';
25
+ import {SERVER_SUBCOMMAND, LONG_STACKTRACE_LIMIT} from './constants';
22
26
  import registerNode from './grid-register';
23
- import { inspectObject } from './utils';
27
+ import {getDefaultsForSchema, validate} from './schema/schema';
28
+ import {
29
+ inspect,
30
+ adjustNodePath,
31
+ isDriverCommandArgs,
32
+ isExtensionCommandArgs,
33
+ isPluginCommandArgs,
34
+ isServerCommandArgs,
35
+ fetchIpAddresses,
36
+ V4_BROADCAST_IP,
37
+ V6_BROADCAST_IP,
38
+ } from './utils';
39
+ import os from 'node:os';
24
40
 
41
+ const {resolveAppiumHome} = env;
25
42
 
26
- async function preflightChecks ({parser, args, driverConfig, pluginConfig, throwInsteadOfExit = false}) {
43
+ /**
44
+ *
45
+ * @param {ParsedArgs} args
46
+ * @param {boolean} [throwInsteadOfExit]
47
+ */
48
+ async function preflightChecks(args, throwInsteadOfExit = false) {
27
49
  try {
28
50
  checkNodeOk();
51
+ await checkNpmOk();
29
52
  if (args.longStacktrace) {
30
- require('longjohn').async_trace_limit = -1;
53
+ Error.stackTraceLimit = LONG_STACKTRACE_LIMIT;
31
54
  }
32
- if (args.showConfig) {
33
- await showConfig();
55
+ if (args.showBuildInfo) {
56
+ await showBuildInfo();
34
57
  process.exit(0);
35
58
  }
36
59
  warnNodeDeprecations();
37
- validateServerArgs(parser, args);
38
- await driverConfig.read();
39
- await pluginConfig.read();
60
+
61
+ validate(args);
62
+
40
63
  if (args.tmpDir) {
41
64
  await validateTmpDir(args.tmpDir);
42
65
  }
@@ -50,18 +73,29 @@ async function preflightChecks ({parser, args, driverConfig, pluginConfig, throw
50
73
  }
51
74
  }
52
75
 
53
- function logNonDefaultArgsWarning (args) {
76
+ /**
77
+ * @param {Args} args
78
+ */
79
+ function logNonDefaultArgsWarning(args) {
54
80
  logger.info('Non-default server args:');
55
- inspectObject(args);
81
+ inspect(args);
56
82
  }
57
83
 
58
- function logDefaultCapabilitiesWarning (caps) {
59
- logger.info('Default capabilities, which will be added to each request ' +
60
- 'unless overridden by desired capabilities:');
61
- inspectObject(caps);
84
+ /**
85
+ * @param {Args['defaultCapabilities']} caps
86
+ */
87
+ function logDefaultCapabilitiesWarning(caps) {
88
+ logger.info(
89
+ 'Default capabilities, which will be added to each request ' +
90
+ 'unless overridden by desired capabilities:'
91
+ );
92
+ inspect(caps);
62
93
  }
63
94
 
64
- async function logStartupInfo (parser, args) {
95
+ /**
96
+ * @param {ParsedArgs} args
97
+ */
98
+ async function logStartupInfo(args) {
65
99
  let welcome = `Welcome to Appium v${APPIUM_VER}`;
66
100
  let appiumRev = await getGitRev();
67
101
  if (appiumRev) {
@@ -69,7 +103,7 @@ async function logStartupInfo (parser, args) {
69
103
  }
70
104
  logger.info(welcome);
71
105
 
72
- let showArgs = getNonDefaultArgs(parser, args);
106
+ let showArgs = getNonDefaultServerArgs(args);
73
107
  if (_.size(showArgs)) {
74
108
  logNonDefaultArgsWarning(showArgs);
75
109
  }
@@ -83,20 +117,114 @@ async function logStartupInfo (parser, args) {
83
117
  // }
84
118
  }
85
119
 
86
- function logServerPort (address, port) {
87
- let logMessage = `Appium REST http interface listener started on ` +
88
- `${address}:${port}`;
89
- logger.info(logMessage);
120
+ /**
121
+ * Gets a list of `updateServer` functions from all extensions
122
+ * @param {DriverNameMap} driverClasses
123
+ * @param {PluginNameMap} pluginClasses
124
+ * @returns {import('@appium/types').UpdateServerCallback[]}
125
+ */
126
+ function getServerUpdaters(driverClasses, pluginClasses) {
127
+ return _.compact(_.map([...driverClasses.keys(), ...pluginClasses.keys()], 'updateServer'));
128
+ }
129
+
130
+ /**
131
+ * Makes a big `MethodMap` from all the little `MethodMap`s in the extensions
132
+ * @param {DriverNameMap} driverClasses
133
+ * @param {PluginNameMap} pluginClasses
134
+ * @returns {import('@appium/types').MethodMap<import('@appium/types').Driver>}
135
+ */
136
+ function getExtraMethodMap(driverClasses, pluginClasses) {
137
+ return [...driverClasses.keys(), ...pluginClasses.keys()].reduce(
138
+ (map, klass) => ({
139
+ ...map,
140
+ ...(klass.newMethodMap ?? {}),
141
+ }),
142
+ {}
143
+ );
144
+ }
145
+
146
+ /**
147
+ * Prepares and validates appium home path folder
148
+ *
149
+ * @param {string} name The name of the appium home source (needed for error messages)
150
+ * @param {string} appiumHome The actual value to be verified
151
+ * @returns {Promise<string>} Same appiumHome value
152
+ * @throws {Error} If the validation has failed
153
+ */
154
+ async function prepareAppiumHome(name, appiumHome) {
155
+ let stat;
156
+ try {
157
+ stat = await fs.stat(appiumHome);
158
+ } catch (e) {
159
+ let err = e;
160
+ if (e.code === 'ENOENT') {
161
+ try {
162
+ await fs.mkdir(appiumHome, {recursive: true});
163
+ return appiumHome;
164
+ } catch (e1) {
165
+ err = e1;
166
+ }
167
+ }
168
+ throw new Error(
169
+ `The path '${appiumHome}' provided in the ${name} must point ` +
170
+ `to a valid folder writeable for the current user account '${os.userInfo().username}'. ` +
171
+ `Original error: ${err.message}`
172
+ );
173
+ }
174
+ if (!stat.isDirectory()) {
175
+ throw new Error(
176
+ `The path '${appiumHome}' provided in the ${name} must point to a valid folder`
177
+ );
178
+ }
179
+ try {
180
+ await fs.access(appiumHome, fs.constants.W_OK);
181
+ } catch (e) {
182
+ throw new Error(
183
+ `The folder path '${appiumHome}' provided in the ${name} must be ` +
184
+ `writeable for the current user account '${os.userInfo().username}. ` +
185
+ `Original error: ${e.message}`
186
+ );
187
+ }
188
+ return appiumHome;
90
189
  }
91
190
 
92
- async function main (args = null) {
93
- let parser = getParser();
191
+ /**
192
+ * Initializes Appium, but does not start the server.
193
+ *
194
+ * Use this to get at the configuration schema.
195
+ *
196
+ * If `args` contains a non-empty `subcommand` which is not `server`, this function will return an empty object.
197
+ *
198
+ * @template {CliCommand} [Cmd=ServerCommand]
199
+ * @template {CliExtensionSubcommand|void} [SubCmd=void]
200
+ * @param {Args<Cmd, SubCmd>} [args] - Partial args (progammatic usage only)
201
+ * @returns {Promise<InitResult<Cmd>>}
202
+ * @example
203
+ * import {init, getSchema} from 'appium';
204
+ * const options = {}; // config object
205
+ * await init(options);
206
+ * const schema = getSchema(); // entire config schema including plugins and drivers
207
+ */
208
+ async function init(args) {
209
+ const appiumHome = args?.appiumHome ?? (await resolveAppiumHome());
210
+ let appiumHomeSourceName = 'autodetected appium home path';
211
+ if (!_.isNil(args?.appiumHome)) {
212
+ appiumHomeSourceName = 'appiumHome config value';
213
+ } else if (process.env.APPIUM_HOME) {
214
+ appiumHomeSourceName = 'APPIUM_HOME environment variable';
215
+ }
216
+ await prepareAppiumHome(appiumHomeSourceName, appiumHome);
217
+
218
+ adjustNodePath();
219
+
220
+ const {driverConfig, pluginConfig} = await loadExtensions(appiumHome);
221
+
222
+ const parser = getParser();
94
223
  let throwInsteadOfExit = false;
95
- if (args) {
96
- // a containing package passed in their own args, let's fill them out
97
- // with defaults
98
- args = Object.assign({}, getDefaultServerArgs(), args);
224
+ /** @type {Args<Cmd, SubCmd>} */
225
+ let preConfigArgs;
99
226
 
227
+ if (args) {
100
228
  // if we have a containing package instead of running as a CLI process,
101
229
  // that package might not appreciate us calling 'process.exit' willy-
102
230
  // nilly, so give it the option to have us throw instead of exit
@@ -105,84 +233,192 @@ async function main (args = null) {
105
233
  // but remove it since it's not a real server arg per se
106
234
  delete args.throwInsteadOfExit;
107
235
  }
236
+ preConfigArgs = {...args, subcommand: args.subcommand ?? SERVER_SUBCOMMAND};
108
237
  } else {
109
238
  // otherwise parse from CLI
110
- args = parser.parse_args();
239
+ preConfigArgs = /** @type {Args<Cmd, SubCmd>} */ (parser.parseArgs());
111
240
  }
112
- await logsinkInit(args);
113
241
 
114
- // if the user has requested the 'driver' CLI, don't run the normal server,
115
- // but instead pass control to the driver CLI
116
- if (args.subcommand === DRIVER_TYPE || args.subcommand === PLUGIN_TYPE) {
117
- await runExtensionCommand(args, args.subcommand);
118
- process.exit();
242
+ const configResult = await readConfigFile(preConfigArgs.configFile);
243
+
244
+ if (!_.isEmpty(configResult.errors)) {
245
+ throw new Error(
246
+ `Errors in config file ${configResult.filepath}:\n ${
247
+ configResult.reason ?? configResult.errors
248
+ }`
249
+ );
119
250
  }
120
251
 
121
- if (args.logFilters) {
122
- const {issues, rules} = await logFactory.loadSecureValuesPreprocessingRules(args.logFilters);
123
- if (!_.isEmpty(issues)) {
124
- throw new Error(`The log filtering rules config '${args.logFilters}' has issues: ` +
125
- JSON.stringify(issues, null, 2));
252
+ // merge config and apply defaults.
253
+ // the order of precendece is:
254
+ // 1. command line args
255
+ // 2. config file
256
+ // 3. defaults from config file.
257
+ if (isServerCommandArgs(preConfigArgs)) {
258
+ const defaults = getDefaultsForSchema(false);
259
+
260
+ /** @type {ParsedArgs} */
261
+ const serverArgs = _.defaultsDeep({}, preConfigArgs, configResult.config?.server, defaults);
262
+
263
+ if (preConfigArgs.showConfig) {
264
+ showConfig(getNonDefaultServerArgs(preConfigArgs), configResult, defaults, serverArgs);
265
+ return /** @type {InitResult<Cmd>} */ ({});
266
+ }
267
+
268
+ await logsinkInit(serverArgs);
269
+
270
+ if (serverArgs.logFilters) {
271
+ const {issues, rules} = await logFactory.loadSecureValuesPreprocessingRules(
272
+ serverArgs.logFilters
273
+ );
274
+ if (!_.isEmpty(issues)) {
275
+ throw new Error(
276
+ `The log filtering rules config '${serverArgs.logFilters}' has issues: ` +
277
+ JSON.stringify(issues, null, 2)
278
+ );
279
+ }
280
+ if (_.isEmpty(rules)) {
281
+ logger.warn(
282
+ `Found no log filtering rules in '${serverArgs.logFilters}'. Is that expected?`
283
+ );
284
+ } else {
285
+ logger.info(
286
+ `Loaded ${util.pluralize('filtering rule', rules.length, true)} from '${
287
+ serverArgs.logFilters
288
+ }'`
289
+ );
290
+ }
126
291
  }
127
- if (_.isEmpty(rules)) {
128
- logger.warn(`Found no log filtering rules in '${args.logFilters}'. Is that expected?`);
129
- } else {
130
- logger.info(`Loaded ${util.pluralize('filtering rule', rules.length, true)} from '${args.logFilters}'`);
292
+
293
+ const appiumDriver = new AppiumDriver(
294
+ /** @type {import('@appium/types').DriverOpts<import('./appium').AppiumDriverConstraints>} */ (
295
+ serverArgs
296
+ )
297
+ );
298
+ // set the config on the umbrella driver so it can match drivers to caps
299
+ appiumDriver.driverConfig = driverConfig;
300
+ await preflightChecks(serverArgs, throwInsteadOfExit);
301
+
302
+ return /** @type {InitResult<Cmd>} */ ({
303
+ appiumDriver,
304
+ parsedArgs: serverArgs,
305
+ driverConfig,
306
+ pluginConfig,
307
+ });
308
+ } else {
309
+ if (isExtensionCommandArgs(preConfigArgs)) {
310
+ // if the user has requested the 'driver' CLI, don't run the normal server,
311
+ // but instead pass control to the driver CLI
312
+ if (isDriverCommandArgs(preConfigArgs)) {
313
+ await runExtensionCommand(preConfigArgs, driverConfig);
314
+ }
315
+ if (isPluginCommandArgs(preConfigArgs)) {
316
+ await runExtensionCommand(preConfigArgs, pluginConfig);
317
+ }
131
318
  }
319
+ return /** @type {InitResult<Cmd>} */ ({});
132
320
  }
321
+ }
322
+
323
+ /**
324
+ * Prints the actual server address and the list of URLs that
325
+ * could be used to connect to the current server.
326
+ * Properly replaces broadcast addresses in client URLs.
327
+ *
328
+ * @param {string} url The URL the server is listening on
329
+ */
330
+ function logServerAddress(url) {
331
+ logger.info(`Appium REST http interface listener started on ${url}`);
332
+ const urlObj = new URL(url);
333
+ if (![V4_BROADCAST_IP, V6_BROADCAST_IP].includes(urlObj.hostname)) {
334
+ return;
335
+ }
336
+
337
+ const ips = fetchIpAddresses(urlObj.hostname === V4_BROADCAST_IP ? 4 : 6);
338
+ logger.info(
339
+ `You can provide the following ${util.pluralize('URL', ips.length, false)} ` +
340
+ `in your client code to connect to this server:\n` +
341
+ ips.map((x) => `\t${urlObj.href.replace(urlObj.hostname, x)}`).join('\n')
342
+ );
343
+ }
344
+
345
+ /**
346
+ * Initializes Appium's config. Starts server if appropriate and resolves the
347
+ * server instance if so; otherwise resolves w/ `undefined`.
348
+ * @template {CliCommand} [Cmd=ServerCommand]
349
+ * @template {CliExtensionSubcommand|void} [SubCmd=void]
350
+ * @param {Args<Cmd, SubCmd>} [args] - Arguments from CLI or otherwise
351
+ * @returns {Promise<Cmd extends ServerCommand ? import('@appium/types').AppiumServer : void>}
352
+ */
353
+ async function main(args) {
354
+ const initResult = await init(args);
355
+
356
+ if (_.isEmpty(initResult)) {
357
+ // if this branch is taken, we've run a different subcommand, so there's nothing
358
+ // left to do here.
359
+ return /** @type {Cmd extends ServerCommand ? import('@appium/types').AppiumServer : void} */ (
360
+ undefined
361
+ );
362
+ }
363
+
364
+ const {appiumDriver, pluginConfig, driverConfig, parsedArgs} =
365
+ /** @type {InitResult<ServerCommand>} */ (initResult);
133
366
 
134
- let appiumDriver = new AppiumDriver(args);
135
- const driverConfig = new DriverConfig(args.appiumHome);
136
- const pluginConfig = new PluginConfig(args.appiumHome);
137
- appiumDriver.driverConfig = driverConfig;
138
- await preflightChecks({parser, args, driverConfig, pluginConfig, throwInsteadOfExit});
139
- await logStartupInfo(parser, args);
367
+ const pluginClasses = getActivePlugins(pluginConfig, parsedArgs.usePlugins);
368
+ // set the active plugins on the umbrella driver so it can use them for commands
369
+ appiumDriver.pluginClasses = pluginClasses;
370
+
371
+ await logStartupInfo(parsedArgs);
140
372
  let routeConfiguringFunction = makeRouter(appiumDriver);
141
373
 
142
- // find any plugin name which has been installed, and which has been requested for activation by
143
- // using the --plugins flag, and turn each one into an instantiated plugin object, so we can send
144
- // them as objects to the server init. we also want to send/assign them to the umbrella driver so
145
- // it can use them to wrap command execution
146
- const plugins = Object.keys(pluginConfig.installedExtensions).filter((pluginName) =>
147
- _.includes(args.plugins, pluginName) ||
148
- (args.plugins.length === 1 && args.plugins[0] === USE_ALL_PLUGINS)
149
- ).map((pluginName) => {
150
- try {
151
- const PluginClass = pluginConfig.require(pluginName);
152
- return new PluginClass(pluginName);
153
- } catch (err) {
154
- logger.error(`Could not load plugin '${pluginName}', so it will not be available. Error ` +
155
- `in loading the plugin was: ${err}`);
156
- return false;
157
- }
158
- }).filter(Boolean);
159
- appiumDriver.plugins = plugins;
374
+ const driverClasses = getActiveDrivers(driverConfig, parsedArgs.useDrivers);
375
+ const serverUpdaters = getServerUpdaters(driverClasses, pluginClasses);
376
+ const extraMethodMap = getExtraMethodMap(driverClasses, pluginClasses);
160
377
 
378
+ /** @type {import('@appium/base-driver').ServerOpts} */
161
379
  const serverOpts = {
162
380
  routeConfiguringFunction,
163
- port: args.port,
164
- hostname: args.address,
165
- allowCors: args.allowCors,
166
- basePath: args.basePath,
167
- plugins,
381
+ port: parsedArgs.port,
382
+ hostname: parsedArgs.address,
383
+ allowCors: parsedArgs.allowCors,
384
+ basePath: parsedArgs.basePath,
385
+ serverUpdaters,
386
+ extraMethodMap,
387
+ cliArgs: parsedArgs,
168
388
  };
169
- if (args.keepAliveTimeout) {
170
- serverOpts.keepAliveTimeout = args.keepAliveTimeout * 1000;
389
+ if (parsedArgs.keepAliveTimeout) {
390
+ serverOpts.keepAliveTimeout = parsedArgs.keepAliveTimeout * 1000;
171
391
  }
172
- let server = await baseServer(serverOpts);
173
- if (args.allowCors) {
174
- logger.warn('You have enabled CORS requests from any host. Be careful not ' +
175
- 'to visit sites which could maliciously try to start Appium ' +
176
- 'sessions on your machine');
392
+ let server;
393
+ try {
394
+ server = await baseServer(serverOpts);
395
+ } catch (err) {
396
+ logger.error(
397
+ `Could not configure Appium server. It's possible that a driver or plugin tried ` +
398
+ `to update the server and failed. Original error: ${err.message}`
399
+ );
400
+ logger.debug(err.stack);
401
+ return process.exit(1);
402
+ }
403
+
404
+ if (parsedArgs.allowCors) {
405
+ logger.warn(
406
+ 'You have enabled CORS requests from any host. Be careful not ' +
407
+ 'to visit sites which could maliciously try to start Appium ' +
408
+ 'sessions on your machine'
409
+ );
177
410
  }
178
411
  appiumDriver.server = server;
179
412
  try {
180
- // TODO prelaunch if args.launch is set
181
- // TODO: startAlertSocket(server, appiumServer);
182
-
183
413
  // configure as node on grid, if necessary
184
- if (args.nodeconfig !== null) {
185
- await registerNode(args.nodeconfig, args.address, args.port, args.basePath);
414
+ // falsy values should not cause this to run
415
+ if (parsedArgs.nodeconfig) {
416
+ await registerNode(
417
+ parsedArgs.nodeconfig,
418
+ parsedArgs.address,
419
+ parsedArgs.port,
420
+ parsedArgs.basePath
421
+ );
186
422
  }
187
423
  } catch (err) {
188
424
  await server.close();
@@ -190,7 +426,7 @@ async function main (args = null) {
190
426
  }
191
427
 
192
428
  for (const signal of ['SIGINT', 'SIGTERM']) {
193
- process.once(signal, async function onSignal () {
429
+ process.once(signal, async function onSignal() {
194
430
  logger.info(`Received ${signal} - shutting down`);
195
431
  try {
196
432
  await appiumDriver.deleteAllSessions({
@@ -206,15 +442,67 @@ async function main (args = null) {
206
442
  });
207
443
  }
208
444
 
209
- logServerPort(args.address, args.port);
445
+ logServerAddress(`http://${parsedArgs.address}:${parsedArgs.port}${parsedArgs.basePath}`);
446
+
210
447
  driverConfig.print();
211
- pluginConfig.print(plugins);
448
+ pluginConfig.print([...pluginClasses.values()]);
212
449
 
213
- return server;
450
+ return /** @type {Cmd extends ServerCommand ? import('@appium/types').AppiumServer : void} */ (
451
+ server
452
+ );
214
453
  }
215
454
 
455
+ // NOTE: this is here for backwards compat for any scripts referencing `main.js` directly
456
+ // (more specifically, `build/lib/main.js`)
457
+ // the executable is now `../index.js`, so that module will typically be `require.main`.
216
458
  if (require.main === module) {
217
459
  asyncify(main);
218
460
  }
219
461
 
220
- export { main };
462
+ // everything below here is intended to be a public API.
463
+ export {readConfigFile} from './config-file';
464
+ export {finalizeSchema, getSchema, validate} from './schema/schema';
465
+ export {main, init, resolveAppiumHome};
466
+
467
+ /**
468
+ * @typedef {import('@appium/types').DriverType} DriverType
469
+ * @typedef {import('@appium/types').PluginType} PluginType
470
+ * @typedef {import('@appium/types').DriverClass} DriverClass
471
+ * @typedef {import('@appium/types').PluginClass} PluginClass
472
+ * @typedef {import('appium/types').CliCommand} CliCommand
473
+ * @typedef {import('appium/types').CliExtensionSubcommand} CliExtensionSubcommand
474
+ * @typedef {import('appium/types').CliExtensionCommand} CliExtensionCommand
475
+ * @typedef {import('appium/types').CliCommandServer} ServerCommand
476
+ * @typedef {import('appium/types').CliCommandDriver} DriverCommand
477
+ * @typedef {import('appium/types').CliCommandPlugin} PluginCommand
478
+ * @typedef {import('./extension').DriverNameMap} DriverNameMap
479
+ * @typedef {import('./extension').PluginNameMap} PluginNameMap
480
+ */
481
+
482
+ /**
483
+ * Literally an empty object
484
+ * @typedef { {} } ExtCommandInitResult
485
+ */
486
+
487
+ /**
488
+ * @typedef ServerInitData
489
+ * @property {import('./appium').AppiumDriver} appiumDriver - The Appium driver
490
+ * @property {import('appium/types').ParsedArgs} parsedArgs - The parsed arguments
491
+ */
492
+
493
+ /**
494
+ * @template {CliCommand} Cmd
495
+ * @typedef {Cmd extends ServerCommand ? ServerInitData & import('./extension').ExtensionConfigs : ExtCommandInitResult} InitResult
496
+ */
497
+
498
+ /**
499
+ * @template {CliCommand} [Cmd=ServerCommand]
500
+ * @template {CliExtensionSubcommand|void} [SubCmd=void]
501
+ * @typedef {import('appium/types').Args<Cmd, SubCmd>} Args
502
+ */
503
+
504
+ /**
505
+ * @template {CliCommand} [Cmd=ServerCommand]
506
+ * @template {CliExtensionSubcommand|void} [SubCmd=void]
507
+ * @typedef {import('appium/types').ParsedArgs<Cmd, SubCmd>} ParsedArgs
508
+ */