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/utils.js CHANGED
@@ -1,65 +1,80 @@
1
1
  import _ from 'lodash';
2
2
  import logger from './logger';
3
- import { processCapabilities, PROTOCOLS } from 'appium-base-driver';
4
- import findRoot from 'find-root';
3
+ import {processCapabilities, PROTOCOLS, STANDARD_CAPS} from '@appium/base-driver';
4
+ import {inspect as dump} from 'util';
5
+ import {node} from '@appium/support';
6
+ import path from 'path';
7
+ import {SERVER_SUBCOMMAND, DRIVER_TYPE, PLUGIN_TYPE} from './constants';
8
+ import os from 'node:os';
5
9
 
6
10
  const W3C_APPIUM_PREFIX = 'appium';
11
+ const STANDARD_CAPS_LOWERCASE = new Set([...STANDARD_CAPS].map((cap) => cap.toLowerCase()));
12
+ export const V4_BROADCAST_IP = '0.0.0.0';
13
+ export const V6_BROADCAST_IP = '::';
7
14
 
8
- function inspectObject (args) {
9
- function getValueArray (obj, indent = ' ') {
10
- if (!_.isObject(obj)) {
11
- return [obj];
12
- }
13
15
 
14
- let strArr = ['{'];
15
- for (let [arg, value] of _.toPairs(obj)) {
16
- if (!_.isObject(value)) {
17
- strArr.push(`${indent} ${arg}: ${value}`);
18
- } else {
19
- value = getValueArray(value, `${indent} `);
20
- strArr.push(`${indent} ${arg}: ${value.shift()}`);
21
- strArr.push(...value);
22
- }
23
- }
24
- strArr.push(`${indent}}`);
25
- return strArr;
26
- }
27
- for (let [arg, value] of _.toPairs(args)) {
28
- value = getValueArray(value);
29
- logger.info(` ${arg}: ${value.shift()}`);
30
- for (let val of value) {
31
- logger.info(val);
32
- }
16
+ /**
17
+ *
18
+ * If `stdout` is a TTY, this is `true`.
19
+ *
20
+ * Used for tighter control over log output.
21
+ * @type {boolean}
22
+ */
23
+ const isStdoutTTY = process.stdout.isTTY;
24
+
25
+ /**
26
+ * Dumps to value to the console using `info` logger.
27
+ *
28
+ * @todo May want to force color to be `false` if {@link isStdoutTTY} is `false`.
29
+ */
30
+ const inspect = _.flow(
31
+ _.partialRight(
32
+ /** @type {(object: any, options: import('util').InspectOptions) => string} */ (dump),
33
+ {colors: true, depth: null, compact: !isStdoutTTY}
34
+ ),
35
+ (...args) => {
36
+ logger.info(...args);
33
37
  }
34
- }
38
+ );
35
39
 
36
40
  /**
37
41
  * Takes the caps that were provided in the request and translates them
38
42
  * into caps that can be used by the inner drivers.
39
43
  *
40
- * @param {Object} jsonwpCapabilities
41
- * @param {Object} w3cCapabilities
42
- * @param {Object} constraints
43
- * @param {Object} defaultCapabilities
44
+ * @template {Constraints} C
45
+ * @template [J=any]
46
+ * @param {J} jsonwpCapabilities
47
+ * @param {W3CCapabilities<C>} w3cCapabilities
48
+ * @param {C} constraints
49
+ * @param {NSCapabilities<C>} [defaultCapabilities]
50
+ * @returns {ParsedDriverCaps<C,J>|InvalidCaps<C,J>}
44
51
  */
45
- function parseCapsForInnerDriver (jsonwpCapabilities, w3cCapabilities, constraints = {}, defaultCapabilities = {}) {
52
+ function parseCapsForInnerDriver(
53
+ jsonwpCapabilities,
54
+ w3cCapabilities,
55
+ constraints = /** @type {C} */ ({}),
56
+ defaultCapabilities = {}
57
+ ) {
46
58
  // Check if the caller sent JSONWP caps, W3C caps, or both
47
- const hasW3CCaps = _.isPlainObject(w3cCapabilities) &&
59
+ const hasW3CCaps =
60
+ _.isPlainObject(w3cCapabilities) &&
48
61
  (_.has(w3cCapabilities, 'alwaysMatch') || _.has(w3cCapabilities, 'firstMatch'));
49
62
  const hasJSONWPCaps = _.isPlainObject(jsonwpCapabilities);
50
- let protocol = null;
51
- let desiredCaps = {};
52
- let processedW3CCapabilities = null;
53
- let processedJsonwpCapabilities = null;
63
+ let desiredCaps = /** @type {ParsedDriverCaps<C>['desiredCaps']} */ ({});
64
+ /** @type {ParsedDriverCaps<C>['processedW3CCapabilities']} */
65
+ let processedW3CCapabilities;
66
+ /** @type {ParsedDriverCaps<C>['processedJsonwpCapabilities']} */
67
+ let processedJsonwpCapabilities;
54
68
 
55
- if (!hasJSONWPCaps && !hasW3CCaps) {
56
- return {
69
+ if (!hasW3CCaps) {
70
+ return /** @type {InvalidCaps<C>} */ ({
57
71
  protocol: PROTOCOLS.W3C,
58
- error: new Error('Either JSONWP or W3C capabilities should be provided'),
59
- };
72
+ error: new Error('W3C capabilities should be provided'),
73
+ });
60
74
  }
61
75
 
62
- const {W3C, MJSONWP} = PROTOCOLS;
76
+ const {W3C} = PROTOCOLS;
77
+ const protocol = W3C;
63
78
 
64
79
  // Make sure we don't mutate the original arguments
65
80
  jsonwpCapabilities = _.cloneDeep(jsonwpCapabilities);
@@ -71,16 +86,23 @@ function parseCapsForInnerDriver (jsonwpCapabilities, w3cCapabilities, constrain
71
86
  for (const [defaultCapKey, defaultCapValue] of _.toPairs(defaultCapabilities)) {
72
87
  let isCapAlreadySet = false;
73
88
  // Check if the key is already present in firstMatch entries
74
- for (const firstMatchEntry of (w3cCapabilities.firstMatch || [])) {
75
- if (_.isPlainObject(firstMatchEntry)
76
- && _.has(removeAppiumPrefixes(firstMatchEntry), removeAppiumPrefix(defaultCapKey))) {
89
+ for (const firstMatchEntry of w3cCapabilities.firstMatch ?? []) {
90
+ if (
91
+ _.isPlainObject(firstMatchEntry) &&
92
+ _.has(removeAppiumPrefixes(firstMatchEntry), removeAppiumPrefix(defaultCapKey))
93
+ ) {
77
94
  isCapAlreadySet = true;
78
95
  break;
79
96
  }
80
97
  }
81
98
  // Check if the key is already present in alwaysMatch entries
82
- isCapAlreadySet = isCapAlreadySet || (_.isPlainObject(w3cCapabilities.alwaysMatch)
83
- && _.has(removeAppiumPrefixes(w3cCapabilities.alwaysMatch), removeAppiumPrefix(defaultCapKey)));
99
+ isCapAlreadySet =
100
+ isCapAlreadySet ||
101
+ (_.isPlainObject(w3cCapabilities.alwaysMatch) &&
102
+ _.has(
103
+ removeAppiumPrefixes(w3cCapabilities.alwaysMatch),
104
+ removeAppiumPrefix(defaultCapKey)
105
+ ));
84
106
  if (isCapAlreadySet) {
85
107
  // Skip if the key is already present in the provided caps
86
108
  continue;
@@ -88,69 +110,42 @@ function parseCapsForInnerDriver (jsonwpCapabilities, w3cCapabilities, constrain
88
110
 
89
111
  // Only add the default capability if it is not overridden
90
112
  if (_.isEmpty(w3cCapabilities.firstMatch)) {
91
- w3cCapabilities.firstMatch = [{[defaultCapKey]: defaultCapValue}];
113
+ w3cCapabilities.firstMatch = /** @type {W3CCapabilities<C>['firstMatch']} */ ([
114
+ {[defaultCapKey]: defaultCapValue},
115
+ ]);
92
116
  } else {
93
117
  w3cCapabilities.firstMatch[0][defaultCapKey] = defaultCapValue;
94
118
  }
95
119
  }
96
120
  }
97
121
  if (hasJSONWPCaps) {
98
- jsonwpCapabilities = Object.assign({}, removeAppiumPrefixes(defaultCapabilities), jsonwpCapabilities);
122
+ jsonwpCapabilities = {
123
+ ...removeAppiumPrefixes(defaultCapabilities),
124
+ ...jsonwpCapabilities,
125
+ };
99
126
  }
100
127
  }
101
128
 
102
129
  // Get MJSONWP caps
103
130
  if (hasJSONWPCaps) {
104
- protocol = MJSONWP;
105
- desiredCaps = jsonwpCapabilities;
106
131
  processedJsonwpCapabilities = {...jsonwpCapabilities};
107
132
  }
108
133
 
109
134
  // Get W3C caps
110
135
  if (hasW3CCaps) {
111
- protocol = W3C;
112
136
  // Call the process capabilities algorithm to find matching caps on the W3C
113
137
  // (see: https://github.com/jlipps/simple-wd-spec#processing-capabilities)
114
- let isFixingNeededForW3cCaps = false;
115
138
  try {
116
139
  desiredCaps = processCapabilities(w3cCapabilities, constraints, true);
117
140
  } catch (error) {
118
- if (!hasJSONWPCaps) {
119
- return {
120
- desiredCaps,
121
- processedJsonwpCapabilities,
122
- processedW3CCapabilities,
123
- protocol,
124
- error,
125
- };
126
- }
127
141
  logger.info(`Could not parse W3C capabilities: ${error.message}`);
128
- isFixingNeededForW3cCaps = true;
129
- }
130
-
131
- if (hasJSONWPCaps && !isFixingNeededForW3cCaps) {
132
- const differingKeys = _.difference(_.keys(removeAppiumPrefixes(processedJsonwpCapabilities)), _.keys(removeAppiumPrefixes(desiredCaps)));
133
- if (!_.isEmpty(differingKeys)) {
134
- logger.info(`The following capabilities were provided in the JSONWP desired capabilities that are missing ` +
135
- `in W3C capabilities: ${JSON.stringify(differingKeys)}`);
136
- isFixingNeededForW3cCaps = true;
137
- }
138
- }
139
-
140
- if (isFixingNeededForW3cCaps && hasJSONWPCaps) {
141
- logger.info('Trying to fix W3C capabilities by merging them with JSONWP caps');
142
- w3cCapabilities = fixW3cCapabilities(w3cCapabilities, jsonwpCapabilities);
143
- try {
144
- desiredCaps = processCapabilities(w3cCapabilities, constraints, true);
145
- } catch (error) {
146
- logger.warn(`Could not parse fixed W3C capabilities: ${error.message}. Falling back to JSONWP protocol`);
147
- return {
148
- desiredCaps: processedJsonwpCapabilities,
149
- processedJsonwpCapabilities,
150
- processedW3CCapabilities: null,
151
- protocol: MJSONWP,
152
- };
153
- }
142
+ return /** @type {InvalidCaps<C,J>} */ ({
143
+ desiredCaps,
144
+ processedJsonwpCapabilities,
145
+ processedW3CCapabilities,
146
+ protocol,
147
+ error,
148
+ });
154
149
  }
155
150
 
156
151
  // Create a new w3c capabilities payload that contains only the matching caps in `alwaysMatch`
@@ -160,99 +155,110 @@ function parseCapsForInnerDriver (jsonwpCapabilities, w3cCapabilities, constrain
160
155
  };
161
156
  }
162
157
 
163
- return {desiredCaps, processedJsonwpCapabilities, processedW3CCapabilities, protocol};
158
+ return /** @type {ParsedDriverCaps<C,J>} */ ({
159
+ desiredCaps,
160
+ processedJsonwpCapabilities,
161
+ processedW3CCapabilities,
162
+ protocol,
163
+ });
164
164
  }
165
165
 
166
166
  /**
167
- * This helper method tries to fix corrupted W3C capabilities by
168
- * merging them to existing JSONWP capabilities.
169
- *
170
- * @param {Object} w3cCaps W3C capabilities
171
- * @param {Object} jsonwpCaps JSONWP capabilities
172
- * @return {Object} Fixed W3C capabilities
167
+ * Takes a capabilities objects and prefixes capabilities with `appium:`
168
+ * @template {Constraints} [C={}]
169
+ * @param {Capabilities<C>} caps - Desired capabilities object
170
+ * @returns {NSCapabilities<C>}
173
171
  */
174
- function fixW3cCapabilities (w3cCaps, jsonwpCaps) {
175
- const result = {
176
- firstMatch: w3cCaps.firstMatch || [],
177
- alwaysMatch: w3cCaps.alwaysMatch || {},
178
- };
179
- const keysToInsert = _.keys(jsonwpCaps);
180
- const removeMatchingKeys = (match) => {
181
- _.pull(keysToInsert, match);
182
- const colonIndex = match.indexOf(':');
183
- if (colonIndex >= 0 && match.length > colonIndex) {
184
- _.pull(keysToInsert, match.substring(colonIndex + 1));
185
- }
186
- if (keysToInsert.includes(`${W3C_APPIUM_PREFIX}:${match}`)) {
187
- _.pull(keysToInsert, `${W3C_APPIUM_PREFIX}:${match}`);
188
- }
189
- };
172
+ function insertAppiumPrefixes(caps) {
173
+ return /** @type {NSCapabilities<C>} */ (
174
+ _.mapKeys(caps, (_, key) =>
175
+ STANDARD_CAPS_LOWERCASE.has(key.toLowerCase()) || key.includes(':')
176
+ ? key
177
+ : `${W3C_APPIUM_PREFIX}:${key}`
178
+ )
179
+ );
180
+ }
190
181
 
191
- for (const firstMatchEntry of result.firstMatch) {
192
- for (const pair of _.toPairs(firstMatchEntry)) {
193
- removeMatchingKeys(pair[0]);
194
- }
195
- }
182
+ /**
183
+ * @template {Constraints} [C={}]
184
+ * @param {NSCapabilities<C>} caps
185
+ * @returns {Capabilities<C>}
186
+ */
187
+ function removeAppiumPrefixes(caps) {
188
+ return /** @type {Capabilities<C>} */ (_.mapKeys(caps, (_, key) => removeAppiumPrefix(key)));
189
+ }
196
190
 
197
- for (const pair of _.toPairs(result.alwaysMatch)) {
198
- removeMatchingKeys(pair[0]);
199
- }
191
+ /**
192
+ * @param {string} key
193
+ * @returns {string}
194
+ */
195
+ function removeAppiumPrefix(key) {
196
+ const prefix = `${W3C_APPIUM_PREFIX}:`;
197
+ return _.startsWith(key, prefix) ? key.substring(prefix.length) : key;
198
+ }
200
199
 
201
- for (const key of keysToInsert) {
202
- result.alwaysMatch[key] = jsonwpCaps[key];
203
- }
204
- return result;
200
+ /**
201
+ *
202
+ * @param {string} pkgName
203
+ * @returns {string|undefined}
204
+ */
205
+ function getPackageVersion(pkgName) {
206
+ const pkgInfo = require(`${pkgName}/package.json`) || {};
207
+ return pkgInfo.version;
205
208
  }
206
209
 
207
210
  /**
208
- * Takes a capabilities objects and prefixes capabilities with `appium:`
209
- * @param {Object} caps Desired capabilities object
211
+ * Adjusts NODE_PATH environment variable,
212
+ * so drivers and plugins could load their peer dependencies.
213
+ * Read https://nodejs.org/api/modules.html#loading-from-the-global-folders
214
+ * for more details.
215
+ * @returns {void}
210
216
  */
211
- function insertAppiumPrefixes (caps) {
212
- // Standard, non-prefixed capabilities (see https://www.w3.org/TR/webdriver/#dfn-table-of-standard-capabilities)
213
- const STANDARD_CAPS = [
214
- 'browserName',
215
- 'browserVersion',
216
- 'platformName',
217
- 'acceptInsecureCerts',
218
- 'pageLoadStrategy',
219
- 'proxy',
220
- 'setWindowRect',
221
- 'timeouts',
222
- 'unhandledPromptBehavior'
223
- ];
224
-
225
- let prefixedCaps = {};
226
- for (let [name, value] of _.toPairs(caps)) {
227
- if (STANDARD_CAPS.includes(name) || name.includes(':')) {
228
- prefixedCaps[name] = value;
217
+ function adjustNodePath() {
218
+ const selfRoot = node.getModuleRootSync('appium', __filename);
219
+ if (!selfRoot || path.dirname(selfRoot).length >= selfRoot.length) {
220
+ return;
221
+ }
222
+ const nodeModulesRoot = path.dirname(selfRoot);
223
+
224
+ const refreshRequirePaths = () => {
225
+ try {
226
+ // ! This hack allows us to avoid modification of import
227
+ // ! statements in client modules. It uses a private API though,
228
+ // ! so it could break (maybe, eventually).
229
+ // See https://gist.github.com/branneman/8048520#7-the-hack
230
+ // @ts-ignore see above comment
231
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
232
+ require('module').Module._initPaths();
233
+ return true;
234
+ } catch (e) {
235
+ return false;
236
+ }
237
+ };
238
+
239
+ if (!process.env.NODE_PATH) {
240
+ process.env.NODE_PATH = nodeModulesRoot;
241
+ if (refreshRequirePaths()) {
242
+ process.env.APPIUM_OMIT_PEER_DEPS = '1';
229
243
  } else {
230
- prefixedCaps[`${W3C_APPIUM_PREFIX}:${name}`] = value;
244
+ delete process.env.NODE_PATH;
231
245
  }
246
+ return;
232
247
  }
233
- return prefixedCaps;
234
- }
235
248
 
236
- function removeAppiumPrefixes (caps) {
237
- if (!_.isPlainObject(caps)) {
238
- return caps;
249
+ const nodePathParts = process.env.NODE_PATH.split(path.delimiter);
250
+ if (nodePathParts.includes(nodeModulesRoot)) {
251
+ process.env.APPIUM_OMIT_PEER_DEPS = '1';
252
+ return;
239
253
  }
240
254
 
241
- const fixedCaps = {};
242
- for (let [name, value] of _.toPairs(caps)) {
243
- fixedCaps[removeAppiumPrefix(name)] = value;
255
+ nodePathParts.push(nodeModulesRoot);
256
+ process.env.NODE_PATH = nodePathParts.join(path.delimiter);
257
+ if (refreshRequirePaths()) {
258
+ process.env.APPIUM_OMIT_PEER_DEPS = '1';
259
+ } else {
260
+ process.env.NODE_PATH = _.without(nodePathParts, nodeModulesRoot).join(path.delimiter);
244
261
  }
245
- return fixedCaps;
246
- }
247
-
248
- function removeAppiumPrefix (key) {
249
- const prefix = `${W3C_APPIUM_PREFIX}:`;
250
- return _.startsWith(key, prefix) ? key.substring(prefix.length) : key;
251
- }
252
-
253
- function getPackageVersion (pkgName) {
254
- const pkgInfo = require(`${pkgName}/package.json`) || {};
255
- return pkgInfo.version;
256
262
  }
257
263
 
258
264
  /**
@@ -271,7 +277,7 @@ function getPackageVersion (pkgName) {
271
277
  * setting items or a dictionary containing parsed Appium setting names along with
272
278
  * their values.
273
279
  */
274
- function pullSettings (caps) {
280
+ function pullSettings(caps) {
275
281
  if (!_.isPlainObject(caps) || _.isEmpty(caps)) {
276
282
  return {};
277
283
  }
@@ -289,9 +295,151 @@ function pullSettings (caps) {
289
295
  return result;
290
296
  }
291
297
 
292
- const rootDir = findRoot(__dirname);
298
+ /**
299
+ * @template {CliCommand} [Cmd=ServerCommand]
300
+ * @template {CliExtensionSubcommand|void} [SubCmd=void]
301
+ * @param {Args<Cmd, SubCmd>} args
302
+ * @returns {args is Args<ServerCommand>}
303
+ */
304
+ export function isServerCommandArgs(args) {
305
+ return args.subcommand === SERVER_SUBCOMMAND;
306
+ }
307
+
308
+ /**
309
+ * @template {CliCommand} [Cmd=ServerCommand]
310
+ * @template {CliExtensionSubcommand|void} [SubCmd=void]
311
+ * @param {Args<Cmd, SubCmd>} args
312
+ * @returns {args is Args<CliExtensionCommand, SubCmd>}
313
+ */
314
+ export function isExtensionCommandArgs(args) {
315
+ return args.subcommand === DRIVER_TYPE || args.subcommand === PLUGIN_TYPE;
316
+ }
317
+
318
+ /**
319
+ * @template {CliCommand} Cmd
320
+ * @template {CliExtensionSubcommand} SubCmd
321
+ * @param {Args<Cmd, SubCmd>} args
322
+ * @returns {args is Args<DriverCommand, SubCmd>}
323
+ */
324
+ export function isDriverCommandArgs(args) {
325
+ return args.subcommand === DRIVER_TYPE;
326
+ }
327
+
328
+ /**
329
+ * @template {CliCommand} Cmd
330
+ * @template {CliExtensionSubcommand} SubCmd
331
+ * @param {Args<Cmd, SubCmd>} args
332
+ * @returns {args is Args<PluginCommand, SubCmd>}
333
+ */
334
+ export function isPluginCommandArgs(args) {
335
+ return args.subcommand === PLUGIN_TYPE;
336
+ }
337
+
338
+ /**
339
+ * Fetches the list of IP addresses of the current host.
340
+ *
341
+ * @param {4|6|null} family Either 4 to include ipv4 addresses only,
342
+ * 6 to include ipv6 addresses only, or null to include all of them
343
+ * @returns {string[]} The list of matched IP addresses
344
+ */
345
+ export function fetchIpAddresses (family = null) {
346
+ let familyValue = null;
347
+ // 'IPv4' is in Node <= 17, from 18 it's a number 4 or 6
348
+ if (family === 4) {
349
+ familyValue = [4, 'IPv4'];
350
+ } else if (family === 6) {
351
+ familyValue = [6, 'IPv6'];
352
+ }
353
+ return _.flatMap(_.values(os.networkInterfaces()).filter(Boolean))
354
+ // @ts-ignore The linter does not understand the above filter
355
+ .filter(({family}) => !familyValue || familyValue && familyValue.includes(family))
356
+ // @ts-ignore The linter does not understand the above filter
357
+ .map(({address}) => address);
358
+ }
293
359
 
294
360
  export {
295
- inspectObject, parseCapsForInnerDriver, insertAppiumPrefixes, rootDir,
296
- getPackageVersion, pullSettings, removeAppiumPrefixes,
361
+ inspect,
362
+ parseCapsForInnerDriver,
363
+ insertAppiumPrefixes,
364
+ getPackageVersion,
365
+ pullSettings,
366
+ removeAppiumPrefixes,
367
+ adjustNodePath,
297
368
  };
369
+
370
+ /**
371
+ * @typedef {import('@appium/types').StringRecord} StringRecord
372
+ * @typedef {import('@appium/types').BaseDriverCapConstraints} BaseDriverCapConstraints
373
+ */
374
+
375
+ /**
376
+ * @template {Constraints} [C=BaseDriverCapConstraints]
377
+ * @template [J=any]
378
+ * @typedef ParsedDriverCaps
379
+ * @property {Capabilities<C>} desiredCaps
380
+ * @property {string} protocol
381
+ * @property {J} [processedJsonwpCapabilities]
382
+ * @property {W3CCapabilities<C>} [processedW3CCapabilities]
383
+ */
384
+
385
+ /**
386
+ * @todo protocol is more specific
387
+ * @template {Constraints} [C=BaseDriverCapConstraints]
388
+ * @template [J=any]
389
+ * @typedef InvalidCaps
390
+ * @property {Error} error
391
+ * @property {string} protocol
392
+ * @property {Capabilities<C>} [desiredCaps]
393
+ * @property {J} [processedJsonwpCapabilities]
394
+ * @property {W3CCapabilities<C>} [processedW3CCapabilities]
395
+ */
396
+
397
+ /**
398
+ * @template {Constraints} C
399
+ * @typedef {import('@appium/types').Capabilities<C>} Capabilities
400
+ */
401
+
402
+ /**
403
+ * @template {Constraints} C
404
+ * @typedef {import('@appium/types').W3CCapabilities<C>} W3CCapabilities
405
+ */
406
+
407
+ /**
408
+ * @template {Constraints} C
409
+ * @typedef {import('@appium/types').NSCapabilities<C>} NSCapabilities
410
+ */
411
+
412
+ /**
413
+ * @template {Constraints} C
414
+ * @typedef {import('@appium/types').ConstraintsToCaps<C>} ConstraintsToCaps
415
+ */
416
+
417
+ /**
418
+ * @template T
419
+ * @typedef {import('type-fest').StringKeyOf<T>} StringKeyOf
420
+ */
421
+
422
+ /**
423
+ * @typedef {import('@appium/types').Constraints} Constraints
424
+ */
425
+
426
+ /**
427
+ * @typedef {import('appium/types').CliCommand} CliCommand
428
+ * @typedef {import('appium/types').CliExtensionSubcommand} CliExtensionSubcommand
429
+ * @typedef {import('appium/types').CliExtensionCommand} CliExtensionCommand
430
+ * @typedef {import('appium/types').CliCommandServer} ServerCommand
431
+ * @typedef {import('appium/types').CliCommandDriver} DriverCommand
432
+ * @typedef {import('appium/types').CliCommandPlugin} PluginCommand
433
+ */
434
+
435
+ /**
436
+ * @template {CliCommand} [Cmd=ServerCommand]
437
+ * @template {CliExtensionSubcommand|void} [SubCmd=void]
438
+ * @typedef {import('appium/types').Args<Cmd, SubCmd>} Args
439
+ */
440
+
441
+ /**
442
+ * @template {CliCommand} [Cmd=ServerCommand]
443
+ * @template {CliExtensionSubcommand|void} [SubCmd=void]
444
+ * @typedef {import('appium/types').ParsedArgs<Cmd, SubCmd>} ParsedArgs
445
+ */