appium 3.3.0 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) 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 +13 -13
  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} +11 -28
  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 +182 -344
  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 +10 -17
  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 +17 -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/cli-transformers.js +2 -2
  126. package/build/lib/schema/cli-transformers.js.map +1 -1
  127. package/build/lib/schema/format-errors.d.ts +28 -0
  128. package/build/lib/schema/format-errors.d.ts.map +1 -0
  129. package/build/lib/schema/format-errors.js +29 -0
  130. package/build/lib/schema/format-errors.js.map +1 -0
  131. package/build/lib/schema/index.d.ts +2 -0
  132. package/build/lib/schema/index.d.ts.map +1 -1
  133. package/build/lib/schema/index.js +2 -0
  134. package/build/lib/schema/index.js.map +1 -1
  135. package/build/lib/schema/schema.d.ts +15 -15
  136. package/build/lib/schema/schema.d.ts.map +1 -1
  137. package/build/lib/schema/schema.js +37 -37
  138. package/build/lib/schema/schema.js.map +1 -1
  139. package/build/lib/utils.d.ts +0 -81
  140. package/build/lib/utils.d.ts.map +1 -1
  141. package/build/lib/utils.js +5 -252
  142. package/build/lib/utils.js.map +1 -1
  143. package/lib/{appium.js → appium.ts} +297 -341
  144. package/lib/bidi-commands.ts +12 -15
  145. package/lib/bootstrap/appium-initializer.ts +212 -0
  146. package/lib/bootstrap/appium-main-runner.ts +172 -0
  147. package/lib/{config-file.ts → bootstrap/config-file.ts} +33 -65
  148. package/lib/{grid-register.ts → bootstrap/grid-v3-register.ts} +35 -35
  149. package/lib/bootstrap/init-types.ts +31 -0
  150. package/lib/bootstrap/main-helpers.ts +223 -0
  151. package/lib/bootstrap/node-helpers.ts +184 -0
  152. package/lib/bootstrap/startup-config.ts +143 -0
  153. package/lib/cli/args.ts +10 -10
  154. package/lib/cli/extension-command.ts +132 -132
  155. package/lib/cli/extension.ts +7 -7
  156. package/lib/cli/parser.ts +50 -50
  157. package/lib/cli/setup-command.ts +2 -2
  158. package/lib/cli/utils.ts +33 -33
  159. package/lib/doctor/doctor.ts +8 -8
  160. package/lib/extension/driver-config.ts +165 -0
  161. package/lib/extension/{extension-config.js → extension-config.ts} +295 -407
  162. package/lib/extension/index.ts +143 -0
  163. package/lib/extension/manifest-migrations.ts +57 -0
  164. package/lib/extension/manifest.ts +372 -0
  165. package/lib/extension/{package-changed.js → package-changed.ts} +13 -20
  166. package/lib/extension/plugin-config.ts +62 -0
  167. package/lib/helpers/build.ts +111 -0
  168. package/lib/helpers/capability.ts +171 -0
  169. package/lib/helpers/network.ts +30 -0
  170. package/lib/insecure-features.ts +1 -1
  171. package/lib/inspector-commands.ts +6 -1
  172. package/lib/{logger.js → logger.ts} +1 -2
  173. package/lib/main.ts +60 -0
  174. package/lib/schema/cli-args-guards.ts +67 -0
  175. package/lib/schema/cli-args.ts +1 -1
  176. package/lib/schema/cli-transformers.ts +2 -2
  177. package/lib/schema/format-errors.ts +43 -0
  178. package/lib/schema/index.ts +2 -0
  179. package/lib/schema/schema.ts +51 -52
  180. package/lib/utils.ts +4 -338
  181. package/package.json +16 -17
  182. package/scripts/autoinstall-extensions.js +3 -0
  183. package/build/lib/config-file.d.ts +0 -57
  184. package/build/lib/config-file.d.ts.map +0 -1
  185. package/build/lib/config-file.js.map +0 -1
  186. package/build/lib/config.d.ts +0 -68
  187. package/build/lib/config.d.ts.map +0 -1
  188. package/build/lib/config.js +0 -358
  189. package/build/lib/config.js.map +0 -1
  190. package/build/lib/grid-register.d.ts +0 -35
  191. package/build/lib/grid-register.d.ts.map +0 -1
  192. package/build/lib/grid-register.js.map +0 -1
  193. package/lib/config.ts +0 -377
  194. package/lib/extension/driver-config.js +0 -245
  195. package/lib/extension/index.js +0 -169
  196. package/lib/extension/manifest-migrations.js +0 -136
  197. package/lib/extension/manifest.js +0 -550
  198. package/lib/extension/plugin-config.js +0 -112
  199. package/lib/main.js +0 -545
@@ -0,0 +1,62 @@
1
+ import _ from 'lodash';
2
+ import type {PluginType} from '@appium/types';
3
+ import type {ExtManifest, ExtName, ExtRecord} from 'appium/types';
4
+ import {PLUGIN_TYPE} from '../constants';
5
+ import {log} from '../logger';
6
+ import {ExtensionConfig} from './extension-config';
7
+ import type {Manifest} from './manifest';
8
+
9
+ export class PluginConfig extends ExtensionConfig<PluginType> {
10
+ private static readonly _instances = new WeakMap<Manifest, PluginConfig>();
11
+
12
+ private constructor(manifest: Manifest) {
13
+ super(PLUGIN_TYPE, manifest);
14
+ }
15
+
16
+ static create(manifest: Manifest): PluginConfig {
17
+ const instance = new PluginConfig(manifest);
18
+ if (PluginConfig.getInstance(manifest)) {
19
+ throw new Error(
20
+ `Manifest with APPIUM_HOME ${manifest.appiumHome} already has a PluginConfig; use PluginConfig.getInstance() to retrieve it.`
21
+ );
22
+ }
23
+ PluginConfig._instances.set(manifest, instance);
24
+ return instance;
25
+ }
26
+
27
+ static getInstance(manifest: Manifest): PluginConfig | undefined {
28
+ return PluginConfig._instances.get(manifest);
29
+ }
30
+
31
+ async validate(): Promise<ExtRecord<PluginType>> {
32
+ return await super._validate(this.manifest.getExtensionData(PLUGIN_TYPE));
33
+ }
34
+
35
+ public override extensionDesc(
36
+ pluginName: ExtName<PluginType>,
37
+ {version}: ExtManifest<PluginType>
38
+ ): string {
39
+ return `${String(pluginName)}@${version}`;
40
+ }
41
+
42
+ override print(activeNames: ExtName<PluginType>[] = []): void {
43
+ const pluginNames = Object.keys(this.installedExtensions);
44
+
45
+ if (_.isEmpty(pluginNames)) {
46
+ log.info(
47
+ `No plugins have been installed. Use the "appium plugin" ` + 'command to install the one(s) you want to use.'
48
+ );
49
+ return;
50
+ }
51
+
52
+ log.info(`Available plugins:`);
53
+ for (const [pluginName, pluginData] of _.toPairs(this.installedExtensions)) {
54
+ const activeTxt = _.includes(activeNames, pluginName as ExtName<PluginType>) ? ' (ACTIVE)' : '';
55
+ log.info(` - ${this.extensionDesc(pluginName as ExtName<PluginType>, pluginData)}${activeTxt}`);
56
+ }
57
+
58
+ if (_.isEmpty(activeNames)) {
59
+ log.info('No plugins activated. Use the --use-plugins flag with names of plugins to activate');
60
+ }
61
+ }
62
+ }
@@ -0,0 +1,111 @@
1
+ import _ from 'lodash';
2
+ import axios from 'axios';
3
+ import {exec} from 'teen_process';
4
+ import {system, fs} from '@appium/support';
5
+ import type {BuildInfo} from 'appium/types';
6
+ import {npmPackage} from '../utils';
7
+
8
+ export const APPIUM_VER = npmPackage.version;
9
+ export const rootDir = fs.findRoot(__dirname);
10
+
11
+ const GIT_BINARY = `git${system.isWindows() ? '.exe' : ''}`;
12
+ const GITHUB_API = 'https://api.github.com/repos/appium/appium';
13
+
14
+ const getFullGitPath = _.memoize(async function getFullGitPath(): Promise<string | null> {
15
+ try {
16
+ return await fs.which(GIT_BINARY);
17
+ } catch {
18
+ return null;
19
+ }
20
+ });
21
+
22
+ /**
23
+ * Returns the current git commit SHA for this Appium checkout.
24
+ *
25
+ * Attempts to read from local git first; when unavailable and fallback is enabled,
26
+ * queries the GitHub API for the tag matching the current Appium version.
27
+ */
28
+ export async function getGitRev(useGithubApiFallback = false): Promise<string | null> {
29
+ const fullGitPath = await getFullGitPath();
30
+ if (fullGitPath) {
31
+ try {
32
+ const {stdout} = await exec(fullGitPath, ['rev-parse', 'HEAD'], {
33
+ cwd: __dirname,
34
+ });
35
+ return stdout.trim();
36
+ } catch {}
37
+ }
38
+
39
+ if (!useGithubApiFallback) {
40
+ return null;
41
+ }
42
+
43
+ // If the package folder is not a valid git repository
44
+ // then fetch the corresponding tag info from GitHub
45
+ try {
46
+ return (
47
+ await axios.get(`${GITHUB_API}/git/refs/tags/appium@${APPIUM_VER}`, {
48
+ headers: {
49
+ 'User-Agent': `Appium ${APPIUM_VER}`,
50
+ },
51
+ })
52
+ ).data?.object?.sha;
53
+ } catch {}
54
+ return null;
55
+ }
56
+
57
+ async function getGitTimestamp(commitSha: string, useGithubApiFallback = false): Promise<string | null> {
58
+ const fullGitPath = await getFullGitPath();
59
+ if (fullGitPath) {
60
+ try {
61
+ const {stdout} = await exec(fullGitPath, ['show', '-s', '--format=%ci', commitSha], {
62
+ cwd: __dirname,
63
+ });
64
+ return stdout.trim();
65
+ } catch {}
66
+ }
67
+
68
+ if (!useGithubApiFallback) {
69
+ return null;
70
+ }
71
+
72
+ try {
73
+ return (
74
+ await axios.get(`${GITHUB_API}/git/tags/${commitSha}`, {
75
+ headers: {
76
+ 'User-Agent': `Appium ${APPIUM_VER}`,
77
+ },
78
+ })
79
+ ).data?.tagger?.date;
80
+ } catch {}
81
+ return null;
82
+ }
83
+
84
+ const BUILD_INFO: BuildInfo = {
85
+ version: APPIUM_VER,
86
+ };
87
+
88
+ /**
89
+ * Update mutable build info metadata from local git or GitHub fallback.
90
+ */
91
+ export async function updateBuildInfo(useGithubApiFallback = false): Promise<void> {
92
+ const sha = await getGitRev(useGithubApiFallback);
93
+ if (!sha) {
94
+ return;
95
+ }
96
+ BUILD_INFO['git-sha'] = sha;
97
+ const buildTimestamp = await getGitTimestamp(sha, useGithubApiFallback);
98
+ if (buildTimestamp) {
99
+ BUILD_INFO.built = buildTimestamp;
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Mutable object containing Appium build information. By default it
105
+ * only contains the Appium version, but is updated with the build timestamp
106
+ * and git commit hash asynchronously as soon as `updateBuildInfo` is called
107
+ * and succeeds.
108
+ */
109
+ export function getBuildInfo(): BuildInfo {
110
+ return BUILD_INFO;
111
+ }
@@ -0,0 +1,171 @@
1
+ import type {
2
+ BaseDriverCapConstraints,
3
+ Capabilities,
4
+ Constraints,
5
+ NSCapabilities,
6
+ W3CCapabilities,
7
+ } from '@appium/types';
8
+ import _ from 'lodash';
9
+ import {log as logger} from '../logger';
10
+ import {
11
+ processCapabilities,
12
+ STANDARD_CAPS,
13
+ errors,
14
+ isW3cCaps,
15
+ } from '@appium/base-driver';
16
+
17
+ const W3C_APPIUM_PREFIX = 'appium';
18
+ const STANDARD_CAPS_LOWERCASE = new Set([...STANDARD_CAPS].map((cap) => cap.toLowerCase()));
19
+
20
+ /** Result of successfully parsing W3C capabilities for the inner driver. */
21
+ export interface ParsedDriverCaps<C extends Constraints = BaseDriverCapConstraints> {
22
+ desiredCaps: Capabilities<C>;
23
+ processedW3CCapabilities: W3CCapabilities<C>;
24
+ }
25
+
26
+ /** Result when capability parsing fails or caps are invalid. */
27
+ export interface InvalidCaps<C extends Constraints = BaseDriverCapConstraints> {
28
+ error: Error;
29
+ desiredCaps?: Capabilities<C>;
30
+ processedW3CCapabilities?: W3CCapabilities<C>;
31
+ }
32
+
33
+ /**
34
+ * Creates an error when a session receives non-W3C capabilities.
35
+ */
36
+ export function makeNonW3cCapsError(): Error {
37
+ return new errors.SessionNotCreatedError(
38
+ 'Session capabilities format must comply to the W3C standard. Make sure your client is up to date. ' +
39
+ 'See https://www.w3.org/TR/webdriver/#new-session for more details.'
40
+ );
41
+ }
42
+
43
+ /**
44
+ * Parses W3C capabilities for the inner driver and applies defaults.
45
+ *
46
+ * @returns Parsed caps or an invalid result with an error.
47
+ */
48
+ export function parseCapsForInnerDriver<C extends Constraints = BaseDriverCapConstraints>(
49
+ w3cCapabilities: W3CCapabilities<C>,
50
+ constraints: C = {} as C,
51
+ defaultCapabilities: NSCapabilities<C> = {}
52
+ ): ParsedDriverCaps<C> | InvalidCaps<C> {
53
+ if (!isW3cCaps(w3cCapabilities)) {
54
+ return {error: makeNonW3cCapsError()};
55
+ }
56
+
57
+ let desiredCaps: Capabilities<C> = {} as Capabilities<C>;
58
+ // eslint-disable-next-line prefer-const -- assigned in success path after try
59
+ let processedW3CCapabilities: W3CCapabilities<C> | undefined;
60
+
61
+ w3cCapabilities = _.cloneDeep(w3cCapabilities);
62
+ defaultCapabilities = _.cloneDeep(defaultCapabilities);
63
+
64
+ if (!_.isEmpty(defaultCapabilities)) {
65
+ for (const [defaultCapKey, defaultCapValue] of _.toPairs(defaultCapabilities)) {
66
+ let isCapAlreadySet = false;
67
+ for (const firstMatchEntry of w3cCapabilities.firstMatch ?? []) {
68
+ if (
69
+ _.isPlainObject(firstMatchEntry) &&
70
+ _.has(removeAppiumPrefixes(firstMatchEntry as NSCapabilities<C>), removeAppiumPrefix(defaultCapKey))
71
+ ) {
72
+ isCapAlreadySet = true;
73
+ break;
74
+ }
75
+ }
76
+ isCapAlreadySet =
77
+ isCapAlreadySet ||
78
+ (_.isPlainObject(w3cCapabilities.alwaysMatch) &&
79
+ _.has(
80
+ removeAppiumPrefixes(w3cCapabilities.alwaysMatch),
81
+ removeAppiumPrefix(defaultCapKey)
82
+ ));
83
+ if (isCapAlreadySet) {
84
+ continue;
85
+ }
86
+
87
+ if (_.isEmpty(w3cCapabilities.firstMatch)) {
88
+ w3cCapabilities.firstMatch = [{[defaultCapKey]: defaultCapValue}] as W3CCapabilities<C>['firstMatch'];
89
+ } else {
90
+ (w3cCapabilities.firstMatch[0] as Record<string, unknown>)[defaultCapKey] = defaultCapValue;
91
+ }
92
+ }
93
+ }
94
+
95
+ try {
96
+ desiredCaps = processCapabilities(w3cCapabilities, constraints, true) as Capabilities<C>;
97
+ } catch (error) {
98
+ logger.info(`Could not parse W3C capabilities: ${(error as Error).message}`);
99
+ return {
100
+ desiredCaps,
101
+ processedW3CCapabilities,
102
+ error: error as Error,
103
+ };
104
+ }
105
+
106
+ processedW3CCapabilities = {
107
+ alwaysMatch: {...insertAppiumPrefixes(desiredCaps)},
108
+ firstMatch: [{}],
109
+ } as W3CCapabilities<C>;
110
+
111
+ return {
112
+ desiredCaps,
113
+ processedW3CCapabilities,
114
+ };
115
+ }
116
+
117
+ /**
118
+ * Prefixes capability keys with `appium:` where appropriate.
119
+ */
120
+ export function insertAppiumPrefixes<C extends Constraints = BaseDriverCapConstraints>(
121
+ caps: Capabilities<C>
122
+ ): NSCapabilities<C> {
123
+ return _.mapKeys(caps, (_, key) =>
124
+ STANDARD_CAPS_LOWERCASE.has(key.toLowerCase()) || key.includes(':')
125
+ ? key
126
+ : `${W3C_APPIUM_PREFIX}:${key}`
127
+ ) as NSCapabilities<C>;
128
+ }
129
+
130
+ /**
131
+ * Removes `appium:` prefix from capability keys.
132
+ */
133
+ export function removeAppiumPrefixes<C extends Constraints = BaseDriverCapConstraints>(
134
+ caps: NSCapabilities<C>
135
+ ): Capabilities<C> {
136
+ return _.mapKeys(caps, (_, key) => removeAppiumPrefix(key)) as Capabilities<C>;
137
+ }
138
+
139
+ /**
140
+ * Pulls Appium settings from capabilities (mutates caps). Supports
141
+ * `settings[key]: value` and `settings: { key: value }`.
142
+ *
143
+ * @returns Parsed settings object; empty if none found.
144
+ */
145
+ export function pullSettings(caps: Record<string, unknown> | null | undefined): Record<string, unknown> {
146
+ if (!_.isPlainObject(caps) || _.isEmpty(caps)) {
147
+ return {};
148
+ }
149
+
150
+ const result: Record<string, unknown> = {};
151
+ const singleSettings: Record<string, unknown> = {};
152
+ for (const [key, value] of _.toPairs(caps)) {
153
+ let match: RegExpExecArray | null;
154
+ if (/^(s|appium:s)ettings$/.test(key) && _.isPlainObject(value)) {
155
+ Object.assign(result, value);
156
+ delete caps[key];
157
+ } else if ((match = /^(s|appium:s)ettings\[(\S+)\]$/.exec(key))) {
158
+ singleSettings[match[2]] = value;
159
+ delete caps[key];
160
+ }
161
+ }
162
+ if (!_.isEmpty(singleSettings)) {
163
+ Object.assign(result, singleSettings);
164
+ }
165
+ return result;
166
+ }
167
+
168
+ function removeAppiumPrefix(key: string): string {
169
+ const prefix = `${W3C_APPIUM_PREFIX}:`;
170
+ return _.startsWith(key, prefix) ? key.substring(prefix.length) : key;
171
+ }
@@ -0,0 +1,30 @@
1
+ import _ from 'lodash';
2
+ import os from 'node:os';
3
+
4
+ export const V4_BROADCAST_IP = '0.0.0.0';
5
+ export const V6_BROADCAST_IP = '::';
6
+
7
+ /**
8
+ * Returns network interfaces for the given IP family.
9
+ *
10
+ * @param family - 4 for IPv4, 6 for IPv6, or null for all.
11
+ */
12
+ export function fetchInterfaces(family: 4 | 6 | null = null): os.NetworkInterfaceInfo[] {
13
+ let familyValue: (4 | 6 | string)[] | null = null;
14
+ if (family === 4) {
15
+ familyValue = [4, 'IPv4'];
16
+ } else if (family === 6) {
17
+ familyValue = [6, 'IPv6'];
18
+ }
19
+ const ifaces = _.values(os.networkInterfaces()).filter(Boolean) as os.NetworkInterfaceInfo[][];
20
+ return _.flatMap(ifaces).filter(
21
+ (info) => !familyValue || familyValue.includes(info.family as 4 | 6 | string)
22
+ );
23
+ }
24
+
25
+ /**
26
+ * Returns true if the address is a broadcast IP (0.0.0.0 or ::).
27
+ */
28
+ export function isBroadcastIp(address: string): boolean {
29
+ return [V4_BROADCAST_IP, V6_BROADCAST_IP, `[${V6_BROADCAST_IP}]`].includes(address);
30
+ }
@@ -1,5 +1,5 @@
1
1
  import _ from 'lodash';
2
- import logger from './logger';
2
+ import {log as logger} from './logger';
3
3
 
4
4
  import type {AppiumDriver} from './appium';
5
5
  import type {ExternalDriver} from '@appium/types';
@@ -21,7 +21,9 @@ import type {
21
21
  } from '@appium/types';
22
22
  import type {AppiumDriver} from './appium';
23
23
 
24
-
24
+ /**
25
+ * Returns available REST and BiDi commands for base, driver and plugins.
26
+ */
25
27
  export async function listCommands(this: AppiumDriver, sessionId?: string): Promise<ListCommandsResponse> {
26
28
  let driverRestMethodMap: MethodMap<any> = {};
27
29
  let driverBiDiCommands: BidiModuleMap = {};
@@ -46,6 +48,9 @@ export async function listCommands(this: AppiumDriver, sessionId?: string): Prom
46
48
  };
47
49
  }
48
50
 
51
+ /**
52
+ * Returns available execute methods exposed by driver and plugins.
53
+ */
49
54
  export async function listExtensions(this: AppiumDriver, sessionId?: string): Promise<ListExtensionsResponse> {
50
55
  let driverExecuteMethodMap: ExecuteMethodMap<any> = {};
51
56
  let pluginExecuteMethodMaps: Record<string, ExecuteMethodMap<any>> = {};
@@ -1,6 +1,5 @@
1
1
  import {logger} from '@appium/support';
2
2
 
3
3
  export const APPIUM_LOGGER_NAME = 'Appium';
4
- let log = logger.getLogger(APPIUM_LOGGER_NAME);
5
4
 
6
- export default log;
5
+ export const log = logger.getLogger(APPIUM_LOGGER_NAME);
package/lib/main.ts ADDED
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+
3
+ import './logsink'; // must run first: global npmlog / log sink setup (see logsink module)
4
+ import './logger'; // load Appium logger immediately after logsink (order matters for log wiring)
5
+ import {env} from '@appium/support';
6
+ import type {AppiumServer} from '@appium/types';
7
+ import type {
8
+ Args,
9
+ CliCommand,
10
+ CliCommandServer,
11
+ CliCommandSetupSubcommand,
12
+ CliExtensionSubcommand,
13
+ } from 'appium/types';
14
+ import {AppiumInitializer} from './bootstrap/appium-initializer';
15
+ import {AppiumMainRunner} from './bootstrap/appium-main-runner';
16
+ import type {ExtCommandInitResult, InitResult, ServerInitData} from './bootstrap/init-types';
17
+
18
+ const initializer = new AppiumInitializer();
19
+ const mainRunner = new AppiumMainRunner();
20
+
21
+ /**
22
+ * Initializes Appium, but does not start the server.
23
+ *
24
+ * Use this to get at the configuration schema.
25
+ *
26
+ * If `args` contains a non-empty `subcommand` which is not `server`, this function will return an empty object.
27
+ */
28
+ export async function init<
29
+ Cmd extends CliCommand = CliCommandServer,
30
+ SubCmd extends CliExtensionSubcommand | CliCommandSetupSubcommand | void = void,
31
+ >(args?: Args<Cmd, SubCmd>): Promise<InitResult<Cmd>> {
32
+ return initializer.init(args);
33
+ }
34
+
35
+ /**
36
+ * Initializes Appium's config. Starts server if appropriate and resolves the
37
+ * server instance if so; otherwise resolves with `undefined`.
38
+ */
39
+ export async function main<
40
+ Cmd extends CliCommand = CliCommandServer,
41
+ SubCmd extends CliExtensionSubcommand | CliCommandSetupSubcommand | void = void,
42
+ >(args?: Args<Cmd, SubCmd>): Promise<Cmd extends CliCommandServer ? AppiumServer : void> {
43
+ const initResult = await init(args);
44
+ return mainRunner.run(initResult, args);
45
+ }
46
+
47
+ // NOTE: backwards compat for scripts referencing `build/lib/main.js` directly.
48
+ // The executable is `../index.js`, so that module will typically be `require.main`.
49
+ if (require.main === module) {
50
+ void main();
51
+ }
52
+
53
+ // Re-export helpers from the same package so `import { … } from 'appium'` stays a supported
54
+ // programmatic API (this file is the package `types` entry). The monorepo does not import these
55
+ // from `'appium'`; consumers use local paths or `@appium/support`. Dropping them is semver-major.
56
+ export {readConfigFile} from './bootstrap/config-file';
57
+ export {finalizeSchema, getSchema, validate} from './schema/schema';
58
+ export const resolveAppiumHome = env.resolveAppiumHome;
59
+
60
+ export type {ExtCommandInitResult, InitResult, ServerInitData};
@@ -0,0 +1,67 @@
1
+ import type {
2
+ Args,
3
+ CliCommand,
4
+ CliCommandDriver,
5
+ CliCommandPlugin,
6
+ CliCommandServer,
7
+ CliCommandSetup,
8
+ CliExtensionCommand,
9
+ CliExtensionSubcommand,
10
+ CliCommandSetupSubcommand,
11
+ } from 'appium/types';
12
+ import {SERVER_SUBCOMMAND, DRIVER_TYPE, PLUGIN_TYPE, SETUP_SUBCOMMAND} from '../constants';
13
+
14
+ type AnyArgs = Args<CliCommand, CliExtensionSubcommand | CliCommandSetupSubcommand | void>;
15
+
16
+ /**
17
+ * Type guard: args are for the server command.
18
+ *
19
+ * @param args - Parsed args before full subcommand-specific narrowing
20
+ */
21
+ export function isServerCommandArgs(args: AnyArgs): args is Args<CliCommandServer, void> {
22
+ return args.subcommand === SERVER_SUBCOMMAND;
23
+ }
24
+
25
+ /**
26
+ * Type guard: args are for the setup command.
27
+ *
28
+ * @param args - Parsed args before full subcommand-specific narrowing
29
+ */
30
+ export function isSetupCommandArgs(
31
+ args: AnyArgs
32
+ ): args is Args<CliCommandSetup, CliCommandSetupSubcommand> {
33
+ return args.subcommand === SETUP_SUBCOMMAND;
34
+ }
35
+
36
+ /**
37
+ * Type guard: args are for an extension command (driver or plugin).
38
+ *
39
+ * @param args - Parsed args before full subcommand-specific narrowing
40
+ */
41
+ export function isExtensionCommandArgs(
42
+ args: AnyArgs
43
+ ): args is Args<CliExtensionCommand, CliExtensionSubcommand> {
44
+ return args.subcommand === DRIVER_TYPE || args.subcommand === PLUGIN_TYPE;
45
+ }
46
+
47
+ /**
48
+ * Type guard: args are for a driver extension command.
49
+ *
50
+ * @param args - Parsed args before full subcommand-specific narrowing
51
+ */
52
+ export function isDriverCommandArgs(
53
+ args: AnyArgs
54
+ ): args is Args<CliCommandDriver, CliExtensionSubcommand> {
55
+ return args.subcommand === DRIVER_TYPE;
56
+ }
57
+
58
+ /**
59
+ * Type guard: args are for a plugin extension command.
60
+ *
61
+ * @param args - Parsed args before full subcommand-specific narrowing
62
+ */
63
+ export function isPluginCommandArgs(
64
+ args: AnyArgs
65
+ ): args is Args<CliCommandPlugin, CliExtensionSubcommand> {
66
+ return args.subcommand === PLUGIN_TYPE;
67
+ }
@@ -1,7 +1,7 @@
1
1
  import {ArgumentTypeError, type ArgumentOptions} from 'argparse';
2
2
  import _ from 'lodash';
3
3
  import type {JSONSchema7, JSONSchema7TypeName} from 'json-schema';
4
- import {formatErrors} from '../config-file';
4
+ import {formatErrors} from './format-errors';
5
5
  import {flattenSchema, validate} from './schema';
6
6
  import {transformers, parseCsvLine} from './cli-transformers';
7
7
  import type {ArgSpec} from './arg-spec';
@@ -41,7 +41,7 @@ export const transformers = {
41
41
  const msg = loadedFromFile
42
42
  ? `The provided value of '${csvOrPath}' must be a valid CSV`
43
43
  : `Must be a comma-delimited string, e.g., "foo,bar,baz"`;
44
- throw new TypeError(`${msg}. Original error: ${(err as Error).message}`);
44
+ throw new TypeError(`${msg}. Original error: ${(err as Error).message}`, {cause: err});
45
45
  }
46
46
  },
47
47
 
@@ -66,7 +66,7 @@ export const transformers = {
66
66
  const msg = loadedFromFile
67
67
  ? `'${jsonOrPath}' must be a valid JSON`
68
68
  : `The provided value must be a valid JSON`;
69
- throw new TypeError(`${msg}. Original error: ${(e as Error).message}`);
69
+ throw new TypeError(`${msg}. Original error: ${(e as Error).message}`, {cause: e});
70
70
  }
71
71
  },
72
72
  } as const;
@@ -0,0 +1,43 @@
1
+ import betterAjvErrors, {type IOutputError} from '@sidvind/better-ajv-errors';
2
+ import type {ErrorObject} from 'ajv';
3
+ import type {NormalizedAppiumConfig} from '@appium/types';
4
+ import {getSchema} from './schema';
5
+
6
+ /**
7
+ * The string should be a raw JSON string.
8
+ */
9
+ export type RawJson = string;
10
+
11
+ /**
12
+ * Options for {@link formatErrors}.
13
+ */
14
+ export interface FormatConfigErrorsOptions {
15
+ json?: RawJson;
16
+ pretty?: boolean;
17
+ schemaId?: string;
18
+ }
19
+
20
+ /**
21
+ * Given an array of errors and the result of loading a config file, generate a
22
+ * helpful string for the user.
23
+ *
24
+ * - If `opts` contains a `json` property, this should be the original JSON
25
+ * _string_ of the config file. This is only applicable if the config file
26
+ * was in JSON format. If present, it will associate line numbers with errors.
27
+ * - If `errors` happens to be empty, this will throw.
28
+ *
29
+ * @throws {TypeError} If `errors` is empty
30
+ */
31
+ export function formatErrors(
32
+ errors: ErrorObject[] = [],
33
+ config: NormalizedAppiumConfig | Record<string, unknown> | string | undefined = {},
34
+ opts: FormatConfigErrorsOptions = {}
35
+ ): string | IOutputError[] {
36
+ if (errors && !errors.length) {
37
+ throw new TypeError('Array of errors must be non-empty');
38
+ }
39
+ return betterAjvErrors(getSchema(opts.schemaId), config, errors, {
40
+ json: opts.json,
41
+ format: opts.pretty === false ? 'js' : 'cli',
42
+ });
43
+ }
@@ -1,2 +1,4 @@
1
1
  export * from './schema';
2
+ export * from './format-errors';
2
3
  export * from './cli-args';
4
+ export * from './cli-args-guards';