appium 3.5.0 → 3.5.2

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 (195) hide show
  1. package/build/lib/appium.d.ts.map +1 -1
  2. package/build/lib/appium.js +43 -45
  3. package/build/lib/appium.js.map +1 -1
  4. package/build/lib/bidi-commands.d.ts.map +1 -1
  5. package/build/lib/bidi-commands.js +34 -29
  6. package/build/lib/bidi-commands.js.map +1 -1
  7. package/build/lib/bootstrap/appium-initializer.d.ts.map +1 -1
  8. package/build/lib/bootstrap/appium-initializer.js +13 -13
  9. package/build/lib/bootstrap/appium-initializer.js.map +1 -1
  10. package/build/lib/bootstrap/appium-main-runner.d.ts.map +1 -1
  11. package/build/lib/bootstrap/appium-main-runner.js +2 -2
  12. package/build/lib/bootstrap/appium-main-runner.js.map +1 -1
  13. package/build/lib/bootstrap/config-file.d.ts.map +1 -1
  14. package/build/lib/bootstrap/config-file.js +11 -11
  15. package/build/lib/bootstrap/config-file.js.map +1 -1
  16. package/build/lib/bootstrap/grid-v3-register.d.ts.map +1 -1
  17. package/build/lib/bootstrap/grid-v3-register.js +9 -6
  18. package/build/lib/bootstrap/grid-v3-register.js.map +1 -1
  19. package/build/lib/bootstrap/main-helpers.d.ts +1 -1
  20. package/build/lib/bootstrap/main-helpers.d.ts.map +1 -1
  21. package/build/lib/bootstrap/main-helpers.js +19 -20
  22. package/build/lib/bootstrap/main-helpers.js.map +1 -1
  23. package/build/lib/bootstrap/node-helpers.d.ts +1 -1
  24. package/build/lib/bootstrap/node-helpers.d.ts.map +1 -1
  25. package/build/lib/bootstrap/node-helpers.js +7 -14
  26. package/build/lib/bootstrap/node-helpers.js.map +1 -1
  27. package/build/lib/bootstrap/startup-config.d.ts.map +1 -1
  28. package/build/lib/bootstrap/startup-config.js +32 -30
  29. package/build/lib/bootstrap/startup-config.js.map +1 -1
  30. package/build/lib/cli/args.d.ts +3 -2
  31. package/build/lib/cli/args.d.ts.map +1 -1
  32. package/build/lib/cli/args.js +2 -5
  33. package/build/lib/cli/args.js.map +1 -1
  34. package/build/lib/cli/driver-command.d.ts +1 -2
  35. package/build/lib/cli/driver-command.d.ts.map +1 -1
  36. package/build/lib/cli/driver-command.js +7 -8
  37. package/build/lib/cli/driver-command.js.map +1 -1
  38. package/build/lib/cli/extension-command.d.ts +2 -2
  39. package/build/lib/cli/extension-command.d.ts.map +1 -1
  40. package/build/lib/cli/extension-command.js +100 -66
  41. package/build/lib/cli/extension-command.js.map +1 -1
  42. package/build/lib/cli/extension.d.ts.map +1 -1
  43. package/build/lib/cli/extension.js.map +1 -1
  44. package/build/lib/cli/parser.d.ts.map +1 -1
  45. package/build/lib/cli/parser.js +21 -14
  46. package/build/lib/cli/parser.js.map +1 -1
  47. package/build/lib/cli/plugin-command.d.ts +1 -1
  48. package/build/lib/cli/plugin-command.d.ts.map +1 -1
  49. package/build/lib/cli/plugin-command.js +5 -5
  50. package/build/lib/cli/plugin-command.js.map +1 -1
  51. package/build/lib/cli/setup-command.d.ts +1 -1
  52. package/build/lib/cli/setup-command.d.ts.map +1 -1
  53. package/build/lib/cli/setup-command.js +14 -19
  54. package/build/lib/cli/setup-command.js.map +1 -1
  55. package/build/lib/cli/utils.d.ts.map +1 -1
  56. package/build/lib/cli/utils.js +3 -2
  57. package/build/lib/cli/utils.js.map +1 -1
  58. package/build/lib/doctor/doctor.d.ts +0 -1
  59. package/build/lib/doctor/doctor.d.ts.map +1 -1
  60. package/build/lib/doctor/doctor.js +12 -17
  61. package/build/lib/doctor/doctor.js.map +1 -1
  62. package/build/lib/extension/driver-config.d.ts.map +1 -1
  63. package/build/lib/extension/driver-config.js +11 -12
  64. package/build/lib/extension/driver-config.js.map +1 -1
  65. package/build/lib/extension/extension-config.d.ts.map +1 -1
  66. package/build/lib/extension/extension-config.js +28 -27
  67. package/build/lib/extension/extension-config.js.map +1 -1
  68. package/build/lib/extension/index.d.ts.map +1 -1
  69. package/build/lib/extension/index.js +13 -16
  70. package/build/lib/extension/index.js.map +1 -1
  71. package/build/lib/extension/manifest.d.ts +3 -2
  72. package/build/lib/extension/manifest.d.ts.map +1 -1
  73. package/build/lib/extension/manifest.js +19 -22
  74. package/build/lib/extension/manifest.js.map +1 -1
  75. package/build/lib/extension/plugin-config.d.ts.map +1 -1
  76. package/build/lib/extension/plugin-config.js +7 -9
  77. package/build/lib/extension/plugin-config.js.map +1 -1
  78. package/build/lib/helpers/build.d.ts +0 -1
  79. package/build/lib/helpers/build.d.ts.map +1 -1
  80. package/build/lib/helpers/build.js +2 -4
  81. package/build/lib/helpers/build.js.map +1 -1
  82. package/build/lib/helpers/capability.d.ts.map +1 -1
  83. package/build/lib/helpers/capability.js +27 -22
  84. package/build/lib/helpers/capability.js.map +1 -1
  85. package/build/lib/helpers/network.d.ts.map +1 -1
  86. package/build/lib/helpers/network.js +4 -3
  87. package/build/lib/helpers/network.js.map +1 -1
  88. package/build/lib/insecure-features.d.ts.map +1 -1
  89. package/build/lib/insecure-features.js +9 -13
  90. package/build/lib/insecure-features.js.map +1 -1
  91. package/build/lib/inspector-commands.d.ts.map +1 -1
  92. package/build/lib/inspector-commands.js +29 -28
  93. package/build/lib/inspector-commands.js.map +1 -1
  94. package/build/lib/logsink.d.ts.map +1 -1
  95. package/build/lib/logsink.js +8 -7
  96. package/build/lib/logsink.js.map +1 -1
  97. package/build/lib/schema/arg-spec.d.ts.map +1 -1
  98. package/build/lib/schema/arg-spec.js +5 -8
  99. package/build/lib/schema/arg-spec.js.map +1 -1
  100. package/build/lib/schema/cli-args.d.ts.map +1 -1
  101. package/build/lib/schema/cli-args.js +20 -19
  102. package/build/lib/schema/cli-args.js.map +1 -1
  103. package/build/lib/schema/cli-transformers.d.ts.map +1 -1
  104. package/build/lib/schema/cli-transformers.js.map +1 -1
  105. package/build/lib/schema/schema.d.ts.map +1 -1
  106. package/build/lib/schema/schema.js +28 -21
  107. package/build/lib/schema/schema.js.map +1 -1
  108. package/build/lib/utils/hash.d.ts +5 -0
  109. package/build/lib/utils/hash.d.ts.map +1 -0
  110. package/build/lib/{utils.js → utils/hash.js} +3 -21
  111. package/build/lib/utils/hash.js.map +1 -0
  112. package/build/lib/utils/index.d.ts +8 -0
  113. package/build/lib/utils/index.d.ts.map +1 -0
  114. package/build/lib/utils/index.js +31 -0
  115. package/build/lib/utils/index.js.map +1 -0
  116. package/build/lib/utils/is-package-changed.d.ts +16 -0
  117. package/build/lib/utils/is-package-changed.d.ts.map +1 -0
  118. package/build/lib/utils/is-package-changed.js +62 -0
  119. package/build/lib/utils/is-package-changed.js.map +1 -0
  120. package/build/lib/utils/object.d.ts +120 -0
  121. package/build/lib/utils/object.d.ts.map +1 -0
  122. package/build/lib/utils/object.js +288 -0
  123. package/build/lib/utils/object.js.map +1 -0
  124. package/build/lib/{extension → utils}/package-changed.d.ts +0 -1
  125. package/build/lib/utils/package-changed.d.ts.map +1 -0
  126. package/build/lib/{extension → utils}/package-changed.js +3 -4
  127. package/build/lib/utils/package-changed.js.map +1 -0
  128. package/build/lib/utils/package-json.d.ts +9 -0
  129. package/build/lib/utils/package-json.d.ts.map +1 -0
  130. package/build/lib/utils/package-json.js +32 -0
  131. package/build/lib/utils/package-json.js.map +1 -0
  132. package/build/lib/utils/resolve-from.d.ts +11 -0
  133. package/build/lib/utils/resolve-from.d.ts.map +1 -0
  134. package/build/lib/utils/resolve-from.js +41 -0
  135. package/build/lib/utils/resolve-from.js.map +1 -0
  136. package/build/types/cli.d.ts.map +1 -1
  137. package/build/types/index.d.ts.map +1 -1
  138. package/build/types/manifest/base.d.ts.map +1 -1
  139. package/build/types/manifest/v3.d.ts.map +1 -1
  140. package/build/types/manifest/v4.d.ts.map +1 -1
  141. package/lib/appium.ts +59 -55
  142. package/lib/bidi-commands.ts +106 -60
  143. package/lib/bootstrap/appium-initializer.ts +27 -19
  144. package/lib/bootstrap/appium-main-runner.ts +8 -4
  145. package/lib/bootstrap/config-file.ts +33 -16
  146. package/lib/bootstrap/grid-v3-register.ts +26 -19
  147. package/lib/bootstrap/main-helpers.ts +25 -19
  148. package/lib/bootstrap/node-helpers.ts +19 -22
  149. package/lib/bootstrap/startup-config.ts +42 -39
  150. package/lib/cli/args.ts +3 -4
  151. package/lib/cli/driver-command.ts +19 -13
  152. package/lib/cli/extension-command.ts +208 -143
  153. package/lib/cli/extension.ts +4 -6
  154. package/lib/cli/parser.ts +32 -30
  155. package/lib/cli/plugin-command.ts +20 -9
  156. package/lib/cli/setup-command.ts +54 -39
  157. package/lib/cli/utils.ts +4 -3
  158. package/lib/doctor/doctor.ts +15 -17
  159. package/lib/extension/driver-config.ts +22 -17
  160. package/lib/extension/extension-config.ts +74 -66
  161. package/lib/extension/index.ts +42 -25
  162. package/lib/extension/manifest.ts +34 -31
  163. package/lib/extension/plugin-config.ts +15 -10
  164. package/lib/helpers/build.ts +6 -5
  165. package/lib/helpers/capability.ts +40 -35
  166. package/lib/helpers/network.ts +6 -5
  167. package/lib/insecure-features.ts +16 -14
  168. package/lib/inspector-commands.ts +72 -40
  169. package/lib/logsink.ts +22 -26
  170. package/lib/schema/arg-spec.ts +9 -8
  171. package/lib/schema/cli-args-guards.ts +4 -4
  172. package/lib/schema/cli-args.ts +25 -21
  173. package/lib/schema/cli-transformers.ts +6 -2
  174. package/lib/schema/format-errors.ts +1 -1
  175. package/lib/schema/schema.ts +52 -38
  176. package/lib/{utils.ts → utils/hash.ts} +2 -19
  177. package/lib/utils/index.ts +23 -0
  178. package/lib/utils/is-package-changed.ts +78 -0
  179. package/lib/utils/object.ts +312 -0
  180. package/lib/{extension → utils}/package-changed.ts +6 -7
  181. package/lib/utils/package-json.ts +36 -0
  182. package/lib/utils/resolve-from.ts +40 -0
  183. package/package.json +13 -16
  184. package/scripts/autoinstall-extensions.js +8 -8
  185. package/tsconfig.json +8 -2
  186. package/types/cli.ts +18 -18
  187. package/types/index.ts +2 -2
  188. package/types/manifest/base.ts +2 -2
  189. package/types/manifest/v3.ts +2 -2
  190. package/types/manifest/v4.ts +2 -2
  191. package/build/lib/extension/package-changed.d.ts.map +0 -1
  192. package/build/lib/extension/package-changed.js.map +0 -1
  193. package/build/lib/utils.d.ts +0 -13
  194. package/build/lib/utils.d.ts.map +0 -1
  195. package/build/lib/utils.js.map +0 -1
@@ -1,5 +1,6 @@
1
- import _ from 'lodash';
1
+ import {util} from '@appium/support';
2
2
  import {promisify} from 'node:util';
3
+ import {capitalize} from './utils';
3
4
  import type {ExtensionCore} from '@appium/base-driver';
4
5
  import {errors} from '@appium/base-driver';
5
6
  import {BIDI_BASE_PATH, BIDI_EVENT_NAME} from './constants';
@@ -14,7 +15,7 @@ import type {
14
15
  ExternalDriver,
15
16
  StringRecord,
16
17
  Plugin,
17
- BiDiResultData
18
+ BiDiResultData,
18
19
  } from '@appium/types';
19
20
 
20
21
  type ExtensionPlugin = Plugin & ExtensionCore;
@@ -30,7 +31,6 @@ interface InitBiDiSocketResult {
30
31
  logSocketErr: LogSocketError;
31
32
  }
32
33
 
33
-
34
34
  const MIN_WS_CODE_VAL = 1000;
35
35
  const MAX_WS_CODE_VAL = 1015;
36
36
  const WS_FALLBACK_CODE = 1011; // server encountered an error while fulfilling request
@@ -50,8 +50,9 @@ export function determineBiDiHost(address: string): string {
50
50
  return address;
51
51
  }
52
52
 
53
- const nonLocalInterfaces = fetchInterfaces(address === V4_BROADCAST_IP ? 4 : 6)
54
- .filter((iface) => !iface.internal);
53
+ const nonLocalInterfaces = fetchInterfaces(address === V4_BROADCAST_IP ? 4 : 6).filter(
54
+ (iface) => !iface.internal,
55
+ );
55
56
  return nonLocalInterfaces.length === 1 ? nonLocalInterfaces[0].address : os.hostname();
56
57
  }
57
58
 
@@ -63,13 +64,11 @@ export function determineBiDiHost(address: string): string {
63
64
  export function onBidiConnection(this: AppiumDriver, ws: WebSocket, req: IncomingMessage): void {
64
65
  try {
65
66
  const initBiDiSocketFunc: OmitThisParameter<typeof initBidiSocket> = initBidiSocket.bind(this);
66
- const {bidiHandlerDriver, bidiHandlerPlugins, proxyClient, send, sendToProxy, logSocketErr} = initBiDiSocketFunc(
67
- ws,
68
- req,
69
- );
67
+ const {bidiHandlerDriver, bidiHandlerPlugins, proxyClient, send, sendToProxy, logSocketErr} =
68
+ initBiDiSocketFunc(ws, req);
70
69
 
71
- const initBidiSocketHandlersFunc: OmitThisParameter<typeof initBidiSocketHandlers> = initBidiSocketHandlers
72
- .bind(this);
70
+ const initBidiSocketHandlersFunc: OmitThisParameter<typeof initBidiSocketHandlers> =
71
+ initBidiSocketHandlers.bind(this);
73
72
  initBidiSocketHandlersFunc(
74
73
  ws,
75
74
  proxyClient,
@@ -80,12 +79,12 @@ export function onBidiConnection(this: AppiumDriver, ws: WebSocket, req: Incomin
80
79
  logSocketErr,
81
80
  );
82
81
  if (proxyClient) {
83
- const initBidiProxyHandlersFunc: OmitThisParameter<typeof initBidiProxyHandlers> = initBidiProxyHandlers
84
- .bind(bidiHandlerDriver);
82
+ const initBidiProxyHandlersFunc: OmitThisParameter<typeof initBidiProxyHandlers> =
83
+ initBidiProxyHandlers.bind(bidiHandlerDriver);
85
84
  initBidiProxyHandlersFunc(proxyClient, ws, send);
86
85
  }
87
- const initBidiEventListenersFunc: OmitThisParameter<typeof initBidiEventListeners> = initBidiEventListeners
88
- .bind(this);
86
+ const initBidiEventListenersFunc: OmitThisParameter<typeof initBidiEventListeners> =
87
+ initBidiEventListeners.bind(this);
89
88
  initBidiEventListenersFunc(ws, bidiHandlerDriver, bidiHandlerPlugins, send);
90
89
  } catch (err) {
91
90
  this.log.error(err);
@@ -104,20 +103,21 @@ export async function onBidiMessage(
104
103
  this: AppiumDriver,
105
104
  data: Buffer,
106
105
  driver: AnyDriver,
107
- plugins: ExtensionPlugin[]
106
+ plugins: ExtensionPlugin[],
108
107
  ): Promise<SuccessBiDiCommandResponse | ErrorBiDiCommandResponse> {
109
108
  let resMessage: SuccessBiDiCommandResponse | ErrorBiDiCommandResponse;
110
109
  let id: number = 0;
111
110
  const driverLog = driver.log;
112
- const dataTruncated = _.truncate(data.toString(), {length: MAX_LOGGED_DATA_LENGTH});
111
+ const dataTruncated = util.truncateString(data.toString(), {length: MAX_LOGGED_DATA_LENGTH});
113
112
  try {
114
113
  let method: string;
115
114
  let params: StringRecord;
116
115
  try {
117
116
  ({id, method, params} = JSON.parse(data.toString('utf8')));
118
117
  } catch (err) {
118
+ const message = err instanceof Error ? err.message : String(err);
119
119
  throw new errors.InvalidArgumentError(
120
- `Could not parse Bidi command '${dataTruncated}': ${err.message}`,
120
+ `Could not parse Bidi command '${dataTruncated}': ${message}`,
121
121
  );
122
122
  }
123
123
  driverLog.info(`--> BIDI message #${id}`);
@@ -131,7 +131,12 @@ export async function onBidiMessage(
131
131
  `Missing params for BiDi operation in '${dataTruncated}`,
132
132
  );
133
133
  }
134
- const executeWrappedCommand = wrapCommandWithPlugins(driver as ExtensionCore, plugins, method, params);
134
+ const executeWrappedCommand = wrapCommandWithPlugins(
135
+ driver as ExtensionCore,
136
+ plugins,
137
+ method,
138
+ params,
139
+ );
135
140
  const result = await executeWrappedCommand();
136
141
  resMessage = {
137
142
  id,
@@ -139,15 +144,24 @@ export async function onBidiMessage(
139
144
  result,
140
145
  };
141
146
  } catch (err) {
142
- resMessage = _.has(err, 'bidiErrObject')
143
- ? err.bidiErrObject(id)
144
- : {
147
+ if (
148
+ err !== null &&
149
+ typeof err === 'object' &&
150
+ 'bidiErrObject' in err &&
151
+ typeof (err as {bidiErrObject: unknown}).bidiErrObject === 'function'
152
+ ) {
153
+ resMessage = (
154
+ err as {bidiErrObject: (msgId: number) => ErrorBiDiCommandResponse}
155
+ ).bidiErrObject(id);
156
+ } else {
157
+ resMessage = {
145
158
  id,
146
159
  type: 'error',
147
160
  error: errors.UnknownError.error(),
148
- message: (err as Error).message,
149
- stacktrace: (err as Error).stack,
161
+ message: err instanceof Error ? err.message : String(err),
162
+ stacktrace: err instanceof Error ? err.stack : undefined,
150
163
  };
164
+ }
151
165
  }
152
166
  driverLog.info(`<-- BIDI message #${id}`);
153
167
  return resMessage;
@@ -191,11 +205,19 @@ export function cleanupBidiSockets(this: AppiumDriver, sessionId: string): void
191
205
  delete this.bidiProxyClients[sessionId];
192
206
  }
193
207
 
194
- function wrapCommandWithPlugins(driver: ExtensionCore, plugins: ExtensionCore[], method: string, params: StringRecord): () => Promise<BiDiResultData> {
208
+ function wrapCommandWithPlugins(
209
+ driver: ExtensionCore,
210
+ plugins: ExtensionCore[],
211
+ method: string,
212
+ params: StringRecord,
213
+ ): () => Promise<BiDiResultData> {
195
214
  const [moduleName, methodName] = method.split('.');
196
215
  let next = async () => await driver.executeBidiCommand(method, params);
197
216
  for (const plugin of plugins.filter((p) => p.doesBidiCommandExist(moduleName, methodName))) {
198
- next = ((_next) => async () => await plugin.executeBidiCommand(method, params, _next, driver))(next);
217
+ next = (
218
+ (_next) => async () =>
219
+ await plugin.executeBidiCommand(method, params, _next, driver)
220
+ )(next);
199
221
  }
200
222
  return next;
201
223
  }
@@ -207,7 +229,11 @@ function wrapCommandWithPlugins(driver: ExtensionCore, plugins: ExtensionCore[],
207
229
  * @param ws The websocket connection object
208
230
  * @param req The connection pathname, which might include the session id
209
231
  */
210
- function initBidiSocket(this: AppiumDriver, ws: WebSocket, req: IncomingMessage): InitBiDiSocketResult {
232
+ function initBidiSocket(
233
+ this: AppiumDriver,
234
+ ws: WebSocket,
235
+ req: IncomingMessage,
236
+ ): InitBiDiSocketResult {
211
237
  const pathname = req.url;
212
238
  if (!pathname) {
213
239
  throw new Error('Invalid connection request: pathname missing from request');
@@ -260,22 +286,24 @@ function initBidiSocket(this: AppiumDriver, ws: WebSocket, req: IncomingMessage)
260
286
  throw new Error(
261
287
  `Got request for ${driverName} to proxy bidi connections to upstream socket with ` +
262
288
  `url ${bidiProxyUrl}, but this was not a valid url`,
263
- {cause: e}
289
+ {cause: e},
264
290
  );
265
291
  }
266
- this.log.info(`Bidi connection for ${driverName} will be proxied to ${bidiProxyUrl}. ` +
267
- `Plugins will not handle bidi commands`);
292
+ this.log.info(
293
+ `Bidi connection for ${driverName} will be proxied to ${bidiProxyUrl}. ` +
294
+ `Plugins will not handle bidi commands`,
295
+ );
268
296
  proxyClient = new WebSocket(bidiProxyUrl);
269
297
  this.bidiProxyClients[sessionId] = proxyClient;
270
298
  } else {
271
- bidiHandlerPlugins.push(...this.pluginsForSession(sessionId) as ExtensionPlugin[]);
299
+ bidiHandlerPlugins.push(...(this.pluginsForSession(sessionId) as ExtensionPlugin[]));
272
300
  }
273
301
  } else {
274
302
  this.log.info('Bidi websocket connection made to main server');
275
303
  // no need to store the socket connection if it's to the main server since it will just
276
304
  // stay open as long as the server itself is and will close when the server closes.
277
305
  bidiHandlerDriver = this; // eslint-disable-line @typescript-eslint/no-this-alias
278
- bidiHandlerPlugins.push(...this.pluginsForSession() as ExtensionPlugin[]);
306
+ bidiHandlerPlugins.push(...(this.pluginsForSession() as ExtensionPlugin[]));
279
307
  }
280
308
 
281
309
  const driverLog = bidiHandlerDriver.log;
@@ -287,13 +315,15 @@ function initBidiSocket(this: AppiumDriver, ws: WebSocket, req: IncomingMessage)
287
315
  // 1. Make it async-await friendly
288
316
  // 2. Do some logging if there's a send error
289
317
  const sendFactory = (socket: WebSocket) => {
290
- const socketSend = promisify(socket.send.bind(socket)) as (data: string | Buffer) => Promise<void>;
318
+ const socketSend = promisify(socket.send.bind(socket)) as (
319
+ data: string | Buffer,
320
+ ) => Promise<void>;
291
321
  return async (data: string | Buffer) => {
292
322
  try {
293
323
  await assertIsOpen(socket);
294
324
  await socketSend(data);
295
325
  } catch (err) {
296
- logSocketErr(err);
326
+ logSocketErr(err instanceof Error ? err : new Error(String(err)));
297
327
  }
298
328
  };
299
329
  };
@@ -338,8 +368,8 @@ function initBidiProxyHandlers(
338
368
  `Upstream bidi socket closed connection (code ${code}, reason: '${reason}'). ` +
339
369
  `Closing proxy connection to client`,
340
370
  );
341
- const intCode: number = _.isNumber(code) ? (code as number) : parseInt(code, 10);
342
- if (_.isNaN(intCode) || intCode < MIN_WS_CODE_VAL || intCode > MAX_WS_CODE_VAL) {
371
+ const intCode: number = typeof code === 'number' ? (code as number) : parseInt(code, 10);
372
+ if (Number.isNaN(intCode) || intCode < MIN_WS_CODE_VAL || intCode > MAX_WS_CODE_VAL) {
343
373
  driverLog.warn(
344
374
  `Received code ${code} from upstream socket, but this is not a valid ` +
345
375
  `websocket code. Rewriting to ${WS_FALLBACK_CODE} for ws compatibility`,
@@ -417,7 +447,7 @@ function initBidiSocketHandlers(
417
447
  }
418
448
 
419
449
  const eventLogCounts = BIDI_EVENTS_MAP.get(bidiHandlerDriver);
420
- if (!_.isEmpty(eventLogCounts)) {
450
+ if (!util.isEmpty(eventLogCounts)) {
421
451
  driverLog.debug(`BiDi events statistics: ${JSON.stringify(eventLogCounts, null, 2)}`);
422
452
  }
423
453
  });
@@ -444,19 +474,31 @@ function initBidiEventListeners(
444
474
  const eventLogCounts: Record<string, number> = BIDI_EVENTS_MAP.get(bidiHandlerDriver) ?? {};
445
475
  BIDI_EVENTS_MAP.set(bidiHandlerDriver, eventLogCounts);
446
476
  const eventListenerFactory = (extType: 'driver' | 'plugin', ext: ExtensionCore) => {
447
- const eventListener = async ({context, method, params = {}}) => {
477
+ const eventListener = async ({
478
+ context,
479
+ method,
480
+ params = {},
481
+ }: {
482
+ context?: string;
483
+ method: string;
484
+ params?: StringRecord;
485
+ }) => {
448
486
  // if the driver didn't specify a context, use the empty context
449
487
  if (!context) {
450
488
  context = '';
451
489
  }
452
490
  if (!method || !params) {
453
- ext.log?.warn( // some old plugins might not have the `log` property
454
- `${_.capitalize(extType)} emitted a bidi event that was malformed. Require method and params keys ` +
455
- `(with optional context). But instead received: ${_.truncate(JSON.stringify({
456
- context,
457
- method,
458
- params,
459
- }), {length: MAX_LOGGED_DATA_LENGTH})}`,
491
+ ext.log?.warn(
492
+ // some old plugins might not have the `log` property
493
+ `${capitalize(extType)} emitted a bidi event that was malformed. Require method and params keys ` +
494
+ `(with optional context). But instead received: ${util.truncateString(
495
+ JSON.stringify({
496
+ context,
497
+ method,
498
+ params,
499
+ }),
500
+ {length: MAX_LOGGED_DATA_LENGTH},
501
+ )}`,
460
502
  );
461
503
  return;
462
504
  }
@@ -472,14 +514,15 @@ function initBidiEventListeners(
472
514
  }
473
515
 
474
516
  const eventSubs = bidiHandlerDriver.bidiEventSubs[method];
475
- if (_.isArray(eventSubs) && eventSubs.includes(context)) {
517
+ if (Array.isArray(eventSubs) && eventSubs.includes(context)) {
476
518
  if (method in eventLogCounts) {
477
519
  ++eventLogCounts[method];
478
520
  } else {
479
- ext.log?.debug( // some old plugins might not have the `log` property
521
+ ext.log?.debug(
522
+ // some old plugins might not have the `log` property
480
523
  `<-- BIDI EVENT ${method} (context: '${context}', ` +
481
- `params: ${_.truncate(JSON.stringify(params), {length: MAX_LOGGED_DATA_LENGTH})}). ` +
482
- `All further similar events won't be logged.`,
524
+ `params: ${util.truncateString(JSON.stringify(params), {length: MAX_LOGGED_DATA_LENGTH})}). ` +
525
+ `All further similar events won't be logged.`,
483
526
  );
484
527
  eventLogCounts[method] = 1;
485
528
  }
@@ -490,17 +533,17 @@ function initBidiEventListeners(
490
533
  };
491
534
  return eventListener;
492
535
  };
493
- bidiHandlerDriver.eventEmitter.on(BIDI_EVENT_NAME, eventListenerFactory('driver', bidiHandlerDriver as ExtensionCore));
536
+ bidiHandlerDriver.eventEmitter.on(
537
+ BIDI_EVENT_NAME,
538
+ eventListenerFactory('driver', bidiHandlerDriver as ExtensionCore),
539
+ );
494
540
  for (const plugin of bidiHandlerPlugins) {
495
541
  // some old plugins might not have the eventEmitter property
496
542
  plugin.eventEmitter?.on(BIDI_EVENT_NAME, eventListenerFactory('plugin', plugin));
497
543
  }
498
544
  }
499
545
 
500
- async function assertIsOpen(
501
- ws: WebSocket,
502
- timeoutMs: number = 5000,
503
- ): Promise<WebSocket> {
546
+ async function assertIsOpen(ws: WebSocket, timeoutMs: number = 5000): Promise<WebSocket> {
504
547
  if (ws.readyState === ws.OPEN) {
505
548
  return ws;
506
549
  }
@@ -513,12 +556,15 @@ async function assertIsOpen(
513
556
  // The socket is in CONNECTING state. Wait up to `timeoutMs` until it is open
514
557
  try {
515
558
  await new Promise((resolve, reject) => {
516
- setTimeout(() => reject(
517
- new Error(
518
- `The BiDi web socket at ${ws.url} did not ` +
519
- `open after ${timeoutMs}ms timeout`
520
- )
521
- ), timeoutMs);
559
+ setTimeout(
560
+ () =>
561
+ reject(
562
+ new Error(
563
+ `The BiDi web socket at ${ws.url} did not ` + `open after ${timeoutMs}ms timeout`,
564
+ ),
565
+ ),
566
+ timeoutMs,
567
+ );
522
568
  ws.once('error', reject);
523
569
  errorListener = reject;
524
570
  ws.once('open', resolve);
@@ -1,8 +1,8 @@
1
1
  import {init as logsinkInit} from '../logsink';
2
2
  import {log as logger} from '../logger';
3
3
  import {util, env} from '@appium/support';
4
- import _ from 'lodash';
5
4
  import type {DriverOpts} from '@appium/types';
5
+ import {defaultsDeep} from '../utils';
6
6
  import {AppiumDriver, type AppiumDriverConstraints} from '../appium';
7
7
  import {runExtensionCommand} from '../cli/extension';
8
8
  import {runSetupCommand} from '../cli/setup-command';
@@ -88,7 +88,9 @@ export class AppiumInitializer {
88
88
  private async parsePreConfigArgs<
89
89
  Cmd extends CliCommand,
90
90
  SubCmd extends CliExtensionSubcommand | CliCommandSetupSubcommand | void,
91
- >(args?: Args<Cmd, SubCmd>): Promise<{preConfigArgs: PreConfigArgs; throwInsteadOfExit: boolean}> {
91
+ >(
92
+ args?: Args<Cmd, SubCmd>,
93
+ ): Promise<{preConfigArgs: PreConfigArgs; throwInsteadOfExit: boolean}> {
92
94
  const parser = await getParser();
93
95
  let throwInsteadOfExit = false;
94
96
 
@@ -97,7 +99,10 @@ export class AppiumInitializer {
97
99
  throwInsteadOfExit = true;
98
100
  delete args.throwInsteadOfExit;
99
101
  }
100
- const preConfigArgs = {...args, subcommand: args.subcommand ?? SERVER_SUBCOMMAND} as PreConfigArgs;
102
+ const preConfigArgs = {
103
+ ...args,
104
+ subcommand: args.subcommand ?? SERVER_SUBCOMMAND,
105
+ } as PreConfigArgs;
101
106
  ArgParser.normalizeServerArgs(preConfigArgs);
102
107
  return {preConfigArgs, throwInsteadOfExit};
103
108
  }
@@ -106,7 +111,7 @@ export class AppiumInitializer {
106
111
  }
107
112
 
108
113
  private assertConfigFileOk(configResult: Awaited<ReturnType<typeof readConfigFile>>): void {
109
- if (!_.isEmpty(configResult.errors)) {
114
+ if (!util.isEmpty(configResult.errors)) {
110
115
  throw new Error(
111
116
  `Errors in config file ${configResult.filepath}:\n ${configResult.reason ?? configResult.errors}`,
112
117
  );
@@ -122,15 +127,20 @@ export class AppiumInitializer {
122
127
  appiumHomeSourceName: string,
123
128
  ): Promise<InitResult<Cmd>> {
124
129
  const defaults = getDefaultsForSchema(false);
125
- const serverArgs = _.defaultsDeep(
130
+ const serverArgs = defaultsDeep(
126
131
  {},
127
- preConfigArgs,
128
- configResult.config?.server,
129
- defaults,
130
- ) as ParsedArgs<CliCommandServer>;
132
+ preConfigArgs as Record<string, unknown>,
133
+ configResult.config?.server as Record<string, unknown> | undefined,
134
+ defaults as Record<string, unknown>,
135
+ ) as unknown as ParsedArgs<CliCommandServer>;
131
136
 
132
137
  if (preConfigArgs.showConfig) {
133
- showConfig(getNonDefaultServerArgs(preConfigArgs as Args), configResult, defaults, serverArgs);
138
+ showConfig(
139
+ getNonDefaultServerArgs(preConfigArgs as Args),
140
+ configResult,
141
+ defaults,
142
+ serverArgs,
143
+ );
134
144
  return {} as InitResult<Cmd>;
135
145
  }
136
146
 
@@ -167,19 +177,17 @@ export class AppiumInitializer {
167
177
  if (!serverArgs.logFilters) {
168
178
  return;
169
179
  }
170
- const {issues, rules} = await logger.unwrap().loadSecureValuesPreprocessingRules(serverArgs.logFilters);
171
- const argToLog = _.truncate(JSON.stringify(serverArgs.logFilters), {
172
- length: 150,
173
- });
174
- if (!_.isEmpty(issues)) {
180
+ const {issues, rules} = await logger
181
+ .unwrap()
182
+ .loadSecureValuesPreprocessingRules(serverArgs.logFilters);
183
+ const argToLog = util.truncateString(JSON.stringify(serverArgs.logFilters), {length: 150});
184
+ if (!util.isEmpty(issues)) {
175
185
  throw new Error(
176
186
  `The log filtering rules config ${argToLog} has issues: ` + JSON.stringify(issues, null, 2),
177
187
  );
178
188
  }
179
- if (_.isEmpty(rules)) {
180
- logger.warn(
181
- `Found no log filtering rules in the ${argToLog} config. ` + `Is that expected?`,
182
- );
189
+ if (util.isEmpty(rules)) {
190
+ logger.warn(`Found no log filtering rules in the ${argToLog} config. ` + `Is that expected?`);
183
191
  } else {
184
192
  logger.info(`Loaded ${util.pluralize('filtering rule', rules.length, true)}`);
185
193
  }
@@ -1,5 +1,5 @@
1
1
  import {log as logger} from '../logger';
2
- import _ from 'lodash';
2
+ import {util} from '@appium/support';
3
3
  import type {AppiumServer} from '@appium/types';
4
4
  import {getActivePlugins, getActiveDrivers} from '../extension';
5
5
  import registerNode from './grid-v3-register';
@@ -37,12 +37,16 @@ export class AppiumMainRunner {
37
37
  async run<
38
38
  Cmd extends CliCommand = CliCommandServer,
39
39
  SubCmd extends CliExtensionSubcommand | CliCommandSetupSubcommand | void = void,
40
- >(initResult: InitResult<Cmd>, args?: Args<Cmd, SubCmd>): Promise<Cmd extends CliCommandServer ? AppiumServer : void> {
41
- if (_.isEmpty(initResult)) {
40
+ >(
41
+ initResult: InitResult<Cmd>,
42
+ args?: Args<Cmd, SubCmd>,
43
+ ): Promise<Cmd extends CliCommandServer ? AppiumServer : void> {
44
+ if (util.isEmpty(initResult)) {
42
45
  return undefined as Cmd extends CliCommandServer ? AppiumServer : void;
43
46
  }
44
47
 
45
- const {appiumDriver, pluginConfig, driverConfig, parsedArgs, appiumHome} = initResult as ServerInitData;
48
+ const {appiumDriver, pluginConfig, driverConfig, parsedArgs, appiumHome} =
49
+ initResult as ServerInitData;
46
50
 
47
51
  const pluginClasses = await getActivePlugins(
48
52
  pluginConfig,
@@ -1,8 +1,9 @@
1
1
  import type {IOutputError} from '@sidvind/better-ajv-errors';
2
2
  import type {ErrorObject, SchemaObject} from 'ajv';
3
3
  import {lilconfig, type LoaderSync, type LilconfigResult} from 'lilconfig';
4
- import _ from 'lodash';
4
+ import {util} from '@appium/support';
5
5
  import * as yaml from 'yaml';
6
+ import {camelCase, getPath, mapKeys, mapValues} from '../utils';
6
7
  import type {AppiumConfig, NormalizedAppiumConfig} from '@appium/types';
7
8
  import {getSchema, validate} from '../schema/schema';
8
9
  import {formatErrors} from '../schema/format-errors';
@@ -48,7 +49,7 @@ type LilconfigAsyncSearcher = ReturnType<typeof lilconfig>;
48
49
  */
49
50
  export async function readConfigFile(
50
51
  filepath?: string,
51
- opts: ReadConfigFileOptions = {}
52
+ opts: ReadConfigFileOptions = {},
52
53
  ): Promise<ReadConfigFileResult> {
53
54
  const lc = lilconfig('appium', {
54
55
  loaders: {
@@ -67,7 +68,7 @@ export async function readConfigFile(
67
68
  try {
68
69
  let configResult: ReadConfigFileResult;
69
70
  const errors = validate(result.config) as ErrorObject[];
70
- if (_.isEmpty(errors)) {
71
+ if (util.isEmpty(errors)) {
71
72
  configResult = {...result, errors};
72
73
  } else {
73
74
  const reason = formatErrors(errors, result.config as Record<string, unknown>, {
@@ -98,19 +99,31 @@ export async function readConfigFile(
98
99
  export function normalizeConfig(config: AppiumConfig): NormalizedAppiumConfig {
99
100
  const schema = getSchema();
100
101
 
101
- const isSchemaTypeObject = (schemaObj: SchemaObject | Record<string, unknown> | undefined): boolean =>
102
- Boolean((schemaObj as SchemaObject | undefined)?.properties || (schemaObj as SchemaObject | undefined)?.type === 'object');
102
+ const isSchemaTypeObject = (
103
+ schemaObj: SchemaObject | Record<string, unknown> | undefined,
104
+ ): boolean =>
105
+ Boolean(
106
+ (schemaObj as SchemaObject | undefined)?.properties ||
107
+ (schemaObj as SchemaObject | undefined)?.type === 'object',
108
+ );
103
109
 
104
110
  const normalize = (rootConfig: AppiumConfig, section?: string): Record<string, unknown> => {
105
- const obj = _.isUndefined(section)
106
- ? rootConfig
107
- : (_.get(rootConfig, section, rootConfig) as Record<string, unknown>);
108
-
109
- const mappedObj = _.mapKeys(obj, (_v, prop) =>
110
- _.get(schema, `properties.server.properties[${prop}].appiumCliDest`, _.camelCase(prop))
111
+ const obj =
112
+ section === undefined
113
+ ? rootConfig
114
+ : (getPath(rootConfig, section, rootConfig) as Record<string, unknown>);
115
+
116
+ const mappedObj = mapKeys(obj as Record<string, unknown>, (_v, prop) =>
117
+ String(
118
+ getPath(
119
+ schema,
120
+ `properties.server.properties.${prop}.appiumCliDest`,
121
+ camelCase(String(prop)),
122
+ ),
123
+ ),
111
124
  );
112
125
 
113
- return _.mapValues(mappedObj, (value, property) => {
126
+ return mapValues(mappedObj, (value, property) => {
114
127
  const nextSection = section ? `${section}.${property}` : property;
115
128
  return isSchemaTypeObject((schema as any).properties?.[property])
116
129
  ? normalize(rootConfig, nextSection)
@@ -130,7 +143,7 @@ function yamlLoader(filepath: string, content: string): unknown {
130
143
  } catch (e) {
131
144
  throw new Error(
132
145
  `The YAML config at '${filepath}' cannot be loaded. Original error: ${(e as Error).message}`,
133
- {cause: e}
146
+ {cause: e},
134
147
  );
135
148
  }
136
149
  }
@@ -147,7 +160,7 @@ function jsonLoader(filepath: string, content: string): unknown {
147
160
  rawConfig.delete(filepath);
148
161
  throw new Error(
149
162
  `The JSON config at '${filepath}' cannot be loaded. Original error: ${(e as Error).message}`,
150
- {cause: e}
163
+ {cause: e},
151
164
  );
152
165
  }
153
166
  }
@@ -155,13 +168,17 @@ function jsonLoader(filepath: string, content: string): unknown {
155
168
  /**
156
169
  * Loads a config file from an explicit path
157
170
  */
158
- async function loadConfigFile(lc: LilconfigAsyncSearcher, filepath: string): Promise<LilconfigResult> {
171
+ async function loadConfigFile(
172
+ lc: LilconfigAsyncSearcher,
173
+ filepath: string,
174
+ ): Promise<LilconfigResult> {
159
175
  try {
160
176
  // removing "await" will cause any rejection to _not_ be caught in this block!
161
177
  return await lc.load(filepath);
162
178
  } catch (err) {
163
179
  if ((err as NodeJS.ErrnoException).code === 'ENOENT') {
164
- (err as NodeJS.ErrnoException).message = `Config file not found at user-provided path: ${filepath}`;
180
+ (err as NodeJS.ErrnoException).message =
181
+ `Config file not found at user-provided path: ${filepath}`;
165
182
  throw err;
166
183
  } else if (err instanceof SyntaxError) {
167
184
  // generally invalid JSON