appium 2.0.0-beta.5 → 2.0.0-beta.52

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