appium 3.2.2 → 3.3.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 (250) hide show
  1. package/build/lib/appium.d.ts +147 -205
  2. package/build/lib/appium.d.ts.map +1 -1
  3. package/build/lib/appium.js +169 -282
  4. package/build/lib/appium.js.map +1 -1
  5. package/build/lib/bidi-commands.d.ts.map +1 -1
  6. package/build/lib/bidi-commands.js +11 -11
  7. package/build/lib/bidi-commands.js.map +1 -1
  8. package/build/lib/bootstrap/appium-initializer.d.ts +21 -0
  9. package/build/lib/bootstrap/appium-initializer.d.ts.map +1 -0
  10. package/build/lib/bootstrap/appium-initializer.js +146 -0
  11. package/build/lib/bootstrap/appium-initializer.js.map +1 -0
  12. package/build/lib/bootstrap/appium-main-runner.d.ts +22 -0
  13. package/build/lib/bootstrap/appium-main-runner.d.ts.map +1 -0
  14. package/build/lib/bootstrap/appium-main-runner.js +109 -0
  15. package/build/lib/bootstrap/appium-main-runner.js.map +1 -0
  16. package/build/lib/bootstrap/config-file.d.ts +37 -0
  17. package/build/lib/bootstrap/config-file.d.ts.map +1 -0
  18. package/build/lib/{config-file.js → bootstrap/config-file.js} +62 -138
  19. package/build/lib/bootstrap/config-file.js.map +1 -0
  20. package/build/lib/bootstrap/grid-v3-register.d.ts +20 -0
  21. package/build/lib/bootstrap/grid-v3-register.d.ts.map +1 -0
  22. package/build/lib/bootstrap/grid-v3-register.js +185 -0
  23. package/build/lib/bootstrap/grid-v3-register.js.map +1 -0
  24. package/build/lib/bootstrap/init-types.d.ts +16 -0
  25. package/build/lib/bootstrap/init-types.d.ts.map +1 -0
  26. package/build/lib/bootstrap/init-types.js +3 -0
  27. package/build/lib/bootstrap/init-types.js.map +1 -0
  28. package/build/lib/bootstrap/main-helpers.d.ts +55 -0
  29. package/build/lib/bootstrap/main-helpers.d.ts.map +1 -0
  30. package/build/lib/bootstrap/main-helpers.js +187 -0
  31. package/build/lib/bootstrap/main-helpers.js.map +1 -0
  32. package/build/lib/bootstrap/node-helpers.d.ts +32 -0
  33. package/build/lib/bootstrap/node-helpers.d.ts.map +1 -0
  34. package/build/lib/bootstrap/node-helpers.js +201 -0
  35. package/build/lib/bootstrap/node-helpers.js.map +1 -0
  36. package/build/lib/bootstrap/startup-config.d.ts +22 -0
  37. package/build/lib/bootstrap/startup-config.d.ts.map +1 -0
  38. package/build/lib/bootstrap/startup-config.js +111 -0
  39. package/build/lib/bootstrap/startup-config.js.map +1 -0
  40. package/build/lib/cli/args.d.ts +16 -12
  41. package/build/lib/cli/args.d.ts.map +1 -1
  42. package/build/lib/cli/args.js +20 -40
  43. package/build/lib/cli/args.js.map +1 -1
  44. package/build/lib/cli/driver-command.d.ts +51 -93
  45. package/build/lib/cli/driver-command.d.ts.map +1 -1
  46. package/build/lib/cli/driver-command.js +11 -66
  47. package/build/lib/cli/driver-command.js.map +1 -1
  48. package/build/lib/cli/extension-command.d.ts +173 -377
  49. package/build/lib/cli/extension-command.d.ts.map +1 -1
  50. package/build/lib/cli/extension-command.js +387 -656
  51. package/build/lib/cli/extension-command.js.map +1 -1
  52. package/build/lib/cli/extension.d.ts +10 -15
  53. package/build/lib/cli/extension.d.ts.map +1 -1
  54. package/build/lib/cli/extension.js +15 -33
  55. package/build/lib/cli/extension.js.map +1 -1
  56. package/build/lib/cli/parser.d.ts +37 -66
  57. package/build/lib/cli/parser.d.ts.map +1 -1
  58. package/build/lib/cli/parser.js +69 -104
  59. package/build/lib/cli/parser.js.map +1 -1
  60. package/build/lib/cli/plugin-command.d.ts +50 -90
  61. package/build/lib/cli/plugin-command.d.ts.map +1 -1
  62. package/build/lib/cli/plugin-command.js +11 -63
  63. package/build/lib/cli/plugin-command.js.map +1 -1
  64. package/build/lib/cli/setup-command.d.ts +21 -26
  65. package/build/lib/cli/setup-command.d.ts.map +1 -1
  66. package/build/lib/cli/setup-command.js +19 -61
  67. package/build/lib/cli/setup-command.js.map +1 -1
  68. package/build/lib/cli/utils.d.ts +33 -35
  69. package/build/lib/cli/utils.d.ts.map +1 -1
  70. package/build/lib/cli/utils.js +48 -50
  71. package/build/lib/cli/utils.js.map +1 -1
  72. package/build/lib/constants.d.ts +23 -23
  73. package/build/lib/constants.d.ts.map +1 -1
  74. package/build/lib/constants.js +10 -15
  75. package/build/lib/constants.js.map +1 -1
  76. package/build/lib/doctor/doctor.d.ts +40 -57
  77. package/build/lib/doctor/doctor.d.ts.map +1 -1
  78. package/build/lib/doctor/doctor.js +31 -62
  79. package/build/lib/doctor/doctor.js.map +1 -1
  80. package/build/lib/extension/driver-config.d.ts +18 -77
  81. package/build/lib/extension/driver-config.d.ts.map +1 -1
  82. package/build/lib/extension/driver-config.js +37 -125
  83. package/build/lib/extension/driver-config.js.map +1 -1
  84. package/build/lib/extension/extension-config.d.ts +103 -210
  85. package/build/lib/extension/extension-config.d.ts.map +1 -1
  86. package/build/lib/extension/extension-config.js +180 -342
  87. package/build/lib/extension/extension-config.js.map +1 -1
  88. package/build/lib/extension/index.d.ts +12 -29
  89. package/build/lib/extension/index.d.ts.map +1 -1
  90. package/build/lib/extension/index.js +33 -75
  91. package/build/lib/extension/index.js.map +1 -1
  92. package/build/lib/extension/manifest-migrations.d.ts +3 -20
  93. package/build/lib/extension/manifest-migrations.d.ts.map +1 -1
  94. package/build/lib/extension/manifest-migrations.js +20 -101
  95. package/build/lib/extension/manifest-migrations.js.map +1 -1
  96. package/build/lib/extension/manifest.d.ts +61 -107
  97. package/build/lib/extension/manifest.d.ts.map +1 -1
  98. package/build/lib/extension/manifest.js +181 -356
  99. package/build/lib/extension/manifest.js.map +1 -1
  100. package/build/lib/extension/package-changed.d.ts +1 -3
  101. package/build/lib/extension/package-changed.d.ts.map +1 -1
  102. package/build/lib/extension/package-changed.js +8 -15
  103. package/build/lib/extension/package-changed.js.map +1 -1
  104. package/build/lib/extension/plugin-config.d.ts +10 -52
  105. package/build/lib/extension/plugin-config.d.ts.map +1 -1
  106. package/build/lib/extension/plugin-config.js +11 -63
  107. package/build/lib/extension/plugin-config.js.map +1 -1
  108. package/build/lib/helpers/build.d.ts +22 -0
  109. package/build/lib/helpers/build.d.ts.map +1 -0
  110. package/build/lib/helpers/build.js +109 -0
  111. package/build/lib/helpers/build.js.map +1 -0
  112. package/build/lib/helpers/capability.d.ts +38 -0
  113. package/build/lib/helpers/capability.d.ts.map +1 -0
  114. package/build/lib/helpers/capability.js +128 -0
  115. package/build/lib/helpers/capability.js.map +1 -0
  116. package/build/lib/helpers/network.d.ts +14 -0
  117. package/build/lib/helpers/network.d.ts.map +1 -0
  118. package/build/lib/helpers/network.js +35 -0
  119. package/build/lib/helpers/network.js.map +1 -0
  120. package/build/lib/insecure-features.js +6 -6
  121. package/build/lib/insecure-features.js.map +1 -1
  122. package/build/lib/inspector-commands.d.ts +6 -0
  123. package/build/lib/inspector-commands.d.ts.map +1 -1
  124. package/build/lib/inspector-commands.js +6 -0
  125. package/build/lib/inspector-commands.js.map +1 -1
  126. package/build/lib/logger.d.ts +2 -3
  127. package/build/lib/logger.d.ts.map +1 -1
  128. package/build/lib/logger.js +2 -3
  129. package/build/lib/logger.js.map +1 -1
  130. package/build/lib/logsink.d.ts +13 -22
  131. package/build/lib/logsink.d.ts.map +1 -1
  132. package/build/lib/logsink.js +48 -103
  133. package/build/lib/logsink.js.map +1 -1
  134. package/build/lib/main.d.ts +15 -58
  135. package/build/lib/main.d.ts.map +1 -1
  136. package/build/lib/main.js +25 -425
  137. package/build/lib/main.js.map +1 -1
  138. package/build/lib/schema/arg-spec.d.ts +32 -107
  139. package/build/lib/schema/arg-spec.d.ts.map +1 -1
  140. package/build/lib/schema/arg-spec.js +11 -107
  141. package/build/lib/schema/arg-spec.js.map +1 -1
  142. package/build/lib/schema/cli-args-guards.d.ts +34 -0
  143. package/build/lib/schema/cli-args-guards.d.ts.map +1 -0
  144. package/build/lib/schema/cli-args-guards.js +49 -0
  145. package/build/lib/schema/cli-args-guards.js.map +1 -0
  146. package/build/lib/schema/cli-args.d.ts +3 -15
  147. package/build/lib/schema/cli-args.d.ts.map +1 -1
  148. package/build/lib/schema/cli-args.js +17 -107
  149. package/build/lib/schema/cli-args.js.map +1 -1
  150. package/build/lib/schema/cli-transformers.d.ts +15 -12
  151. package/build/lib/schema/cli-transformers.d.ts.map +1 -1
  152. package/build/lib/schema/cli-transformers.js +15 -45
  153. package/build/lib/schema/cli-transformers.js.map +1 -1
  154. package/build/lib/schema/format-errors.d.ts +28 -0
  155. package/build/lib/schema/format-errors.d.ts.map +1 -0
  156. package/build/lib/schema/format-errors.js +29 -0
  157. package/build/lib/schema/format-errors.js.map +1 -0
  158. package/build/lib/schema/index.d.ts +4 -2
  159. package/build/lib/schema/index.d.ts.map +1 -1
  160. package/build/lib/schema/index.js +2 -0
  161. package/build/lib/schema/index.js.map +1 -1
  162. package/build/lib/schema/keywords.d.ts +12 -20
  163. package/build/lib/schema/keywords.d.ts.map +1 -1
  164. package/build/lib/schema/keywords.js +6 -51
  165. package/build/lib/schema/keywords.js.map +1 -1
  166. package/build/lib/schema/schema.d.ts +106 -231
  167. package/build/lib/schema/schema.d.ts.map +1 -1
  168. package/build/lib/schema/schema.js +88 -358
  169. package/build/lib/schema/schema.js.map +1 -1
  170. package/build/lib/utils.d.ts +7 -267
  171. package/build/lib/utils.d.ts.map +1 -1
  172. package/build/lib/utils.js +10 -409
  173. package/build/lib/utils.js.map +1 -1
  174. package/lib/{appium.js → appium.ts} +297 -341
  175. package/lib/bidi-commands.ts +10 -14
  176. package/lib/bootstrap/appium-initializer.ts +212 -0
  177. package/lib/bootstrap/appium-main-runner.ts +172 -0
  178. package/lib/bootstrap/config-file.ts +178 -0
  179. package/lib/bootstrap/grid-v3-register.ts +250 -0
  180. package/lib/bootstrap/init-types.ts +31 -0
  181. package/lib/bootstrap/main-helpers.ts +223 -0
  182. package/lib/bootstrap/node-helpers.ts +180 -0
  183. package/lib/bootstrap/startup-config.ts +143 -0
  184. package/lib/cli/{args.js → args.ts} +45 -56
  185. package/lib/cli/driver-command.ts +122 -0
  186. package/lib/cli/{extension-command.js → extension-command.ts} +827 -906
  187. package/lib/cli/extension.ts +65 -0
  188. package/lib/cli/{parser.js → parser.ts} +93 -116
  189. package/lib/cli/plugin-command.ts +117 -0
  190. package/lib/cli/{setup-command.js → setup-command.ts} +59 -74
  191. package/lib/cli/utils.ts +97 -0
  192. package/lib/{constants.js → constants.ts} +30 -41
  193. package/lib/doctor/{doctor.js → doctor.ts} +82 -92
  194. package/lib/extension/driver-config.ts +165 -0
  195. package/lib/extension/{extension-config.js → extension-config.ts} +291 -405
  196. package/lib/extension/index.ts +143 -0
  197. package/lib/extension/manifest-migrations.ts +57 -0
  198. package/lib/extension/manifest.ts +369 -0
  199. package/lib/extension/{package-changed.js → package-changed.ts} +9 -18
  200. package/lib/extension/plugin-config.ts +62 -0
  201. package/lib/helpers/build.ts +111 -0
  202. package/lib/helpers/capability.ts +171 -0
  203. package/lib/helpers/network.ts +30 -0
  204. package/lib/insecure-features.ts +1 -1
  205. package/lib/inspector-commands.ts +6 -1
  206. package/lib/{logger.js → logger.ts} +1 -2
  207. package/lib/{logsink.js → logsink.ts} +91 -137
  208. package/lib/main.ts +60 -0
  209. package/lib/schema/arg-spec.ts +131 -0
  210. package/lib/schema/cli-args-guards.ts +67 -0
  211. package/lib/schema/cli-args.ts +171 -0
  212. package/lib/schema/cli-transformers.ts +83 -0
  213. package/lib/schema/format-errors.ts +43 -0
  214. package/lib/schema/index.ts +4 -0
  215. package/lib/schema/keywords.ts +96 -0
  216. package/lib/schema/schema.ts +448 -0
  217. package/lib/utils.ts +73 -0
  218. package/package.json +17 -18
  219. package/scripts/autoinstall-extensions.js +3 -0
  220. package/build/lib/config-file.d.ts +0 -100
  221. package/build/lib/config-file.d.ts.map +0 -1
  222. package/build/lib/config-file.js.map +0 -1
  223. package/build/lib/config.d.ts +0 -70
  224. package/build/lib/config.d.ts.map +0 -1
  225. package/build/lib/config.js +0 -390
  226. package/build/lib/config.js.map +0 -1
  227. package/build/lib/grid-register.d.ts +0 -10
  228. package/build/lib/grid-register.d.ts.map +0 -1
  229. package/build/lib/grid-register.js +0 -134
  230. package/build/lib/grid-register.js.map +0 -1
  231. package/lib/cli/driver-command.js +0 -174
  232. package/lib/cli/extension.js +0 -74
  233. package/lib/cli/plugin-command.js +0 -164
  234. package/lib/cli/utils.js +0 -91
  235. package/lib/config-file.js +0 -228
  236. package/lib/config.js +0 -389
  237. package/lib/extension/driver-config.js +0 -245
  238. package/lib/extension/index.js +0 -169
  239. package/lib/extension/manifest-migrations.js +0 -136
  240. package/lib/extension/manifest.js +0 -550
  241. package/lib/extension/plugin-config.js +0 -112
  242. package/lib/grid-register.js +0 -146
  243. package/lib/main.js +0 -545
  244. package/lib/schema/arg-spec.js +0 -229
  245. package/lib/schema/cli-args.js +0 -254
  246. package/lib/schema/cli-transformers.js +0 -113
  247. package/lib/schema/index.js +0 -2
  248. package/lib/schema/keywords.js +0 -136
  249. package/lib/schema/schema.js +0 -725
  250. package/lib/utils.js +0 -512
@@ -1,8 +1,24 @@
1
1
  import _ from 'lodash';
2
- import {getBuildInfo, updateBuildInfo, APPIUM_VER} from './config';
2
+ import type WebSocket from 'ws';
3
+ import type {
4
+ AppiumServer,
5
+ DriverCaps,
6
+ DriverData,
7
+ DriverOpts,
8
+ ExternalDriver,
9
+ Plugin,
10
+ PluginClass,
11
+ PluginCommand,
12
+ Protocol,
13
+ RouteMatcher,
14
+ StringRecord,
15
+ TimestampedMultiSessionData,
16
+ W3CDriverCaps,
17
+ } from '@appium/types';
3
18
  import {
4
19
  BaseDriver,
5
20
  DriverCore,
21
+ type ExtensionCore,
6
22
  errors,
7
23
  isSessionCommand,
8
24
  PROTOCOLS,
@@ -16,16 +32,22 @@ import {
16
32
  generateDriverLogPrefix,
17
33
  isW3cCaps,
18
34
  } from '@appium/base-driver';
19
- import AsyncLock from 'async-lock';
20
- import {parseCapsForInnerDriver, pullSettings, makeNonW3cCapsError} from './utils';
35
+ import {APPIUM_VER, getBuildInfo, updateBuildInfo} from './helpers/build';
36
+ import {
37
+ makeNonW3cCapsError,
38
+ parseCapsForInnerDriver,
39
+ pullSettings,
40
+ type ParsedDriverCaps,
41
+ } from './helpers/capability';
21
42
  import {util} from '@appium/support';
22
43
  import {getDefaultsForExtension} from './schema';
23
44
  import {DRIVER_TYPE, BIDI_BASE_PATH, SESSION_DISCOVERY_FEATURE} from './constants';
24
45
  import * as bidiCommands from './bidi-commands';
25
46
  import * as insecureFeatures from './insecure-features';
26
47
  import * as inspectorCommands from './inspector-commands';
48
+ import type {DriverConfig} from './extension/driver-config';
27
49
 
28
- const desiredCapabilityConstraints = /** @type {const} */ ({
50
+ const desiredCapabilityConstraints = {
29
51
  automationName: {
30
52
  presence: true,
31
53
  isString: true,
@@ -34,81 +56,76 @@ const desiredCapabilityConstraints = /** @type {const} */ ({
34
56
  presence: true,
35
57
  isString: true,
36
58
  },
37
- });
59
+ } as const;
60
+
61
+ export type AppiumDriverConstraints = typeof desiredCapabilityConstraints;
62
+ export type W3CAppiumDriverCaps = W3CDriverCaps<AppiumDriverConstraints>;
63
+
64
+ /** Result shape for umbrella {@link AppiumDriver.createSession} / {@link AppiumDriver.deleteSession}. */
65
+ interface SessionHandlerResult<V = unknown> {
66
+ value?: V;
67
+ error?: Error;
68
+ protocol?: string;
69
+ }
38
70
 
39
- const sessionsListGuard = new AsyncLock();
40
- const pendingDriversGuard = new AsyncLock();
71
+ type SessionHandlerCreateResult = SessionHandlerResult<
72
+ [string, DriverCaps<AppiumDriverConstraints>, string | undefined]
73
+ >;
74
+
75
+ type SessionHandlerDeleteResult = SessionHandlerResult<void>;
41
76
 
42
77
  /**
43
- * @extends {DriverCore<AppiumDriverConstraints>}
78
+ * Umbrella driver: owns the session table, loads platform drivers and plugins, and routes
79
+ * commands to the right session driver or plugin chain.
44
80
  */
45
- class AppiumDriver extends DriverCore {
46
- /**
47
- * Access to sessions list must be guarded with a Semaphore, because
48
- * it might be changed by other async calls at any time
49
- * It is not recommended to access this property directly from the outside
50
- * @type {Record<string,ExternalDriver>}
51
- */
52
- sessions;
81
+ export class AppiumDriver extends DriverCore<AppiumDriverConstraints> {
82
+ readonly sessions: Record<string, ExternalDriver> = {};
53
83
 
54
- /**
55
- * Access to pending drivers list must be guarded with a Semaphore, because
56
- * it might be changed by other async calls at any time
57
- * It is not recommended to access this property directly from the outside
58
- * @type {Record<string,ExternalDriver[]>}
59
- */
60
- pendingDrivers;
84
+ readonly pendingDrivers: Record<string, ExternalDriver[]> = {};
61
85
 
62
86
  /**
63
- * Note that {@linkcode AppiumDriver} has no `newCommandTimeout` method.
64
- * `AppiumDriver` does not set and observe its own timeouts; individual
65
- * sessions (managed drivers) do instead.
87
+ * The umbrella driver does not observe its own command timeout; inner session drivers do.
66
88
  */
67
- newCommandTimeoutMs;
89
+ override newCommandTimeoutMs = 0;
68
90
 
69
- /**
70
- * List of active plugins
71
- * @type {Map<PluginClass,string>}
72
- */
73
- pluginClasses;
91
+ /** Filled during server bootstrap; keep this map instance (do not reassign). */
92
+ readonly pluginClasses = new Map<PluginClass, string>();
74
93
 
75
- /**
76
- * map of sessions to actual plugin instances per session
77
- * @type {Record<string,InstanceType<PluginClass>[]>}
78
- */
79
- sessionPlugins;
94
+ readonly sessionPlugins: Record<string, Plugin[]> = {};
80
95
 
81
- /**
82
- * some commands are sessionless, so we need a set of plugins for them
83
- * @type {InstanceType<PluginClass>[]}
84
- */
85
- sessionlessPlugins;
96
+ sessionlessPlugins: Plugin[] = [];
86
97
 
87
- /** @type {DriverConfig} */
88
- driverConfig;
98
+ /** Set during server init before listen; tests may assign a mock after construction. */
99
+ driverConfig!: DriverConfig;
89
100
 
90
- /** @type {AppiumServer} */
91
- server;
101
+ /** Set when the HTTP server is created in server bootstrap. */
102
+ server!: AppiumServer;
92
103
 
93
- /** @type {Record<string, import('ws').WebSocket[]>} */
94
- bidiSockets;
104
+ readonly bidiSockets: Record<string, WebSocket[]> = {};
95
105
 
96
- /** @type {Record<string, import('ws').WebSocket>} */
97
- bidiProxyClients;
106
+ readonly bidiProxyClients: Record<string, WebSocket> = {};
98
107
 
99
- /**
100
- * @type {AppiumDriverConstraints}
101
- * @readonly
102
- */
103
- desiredCapConstraints;
108
+ readonly desiredCapConstraints = desiredCapabilityConstraints;
104
109
 
105
- /** @type {import('@appium/types').DriverOpts<AppiumDriverConstraints>} */
106
- args;
110
+ readonly args!: DriverOpts<AppiumDriverConstraints>;
111
+
112
+ onBidiConnection = bidiCommands.onBidiConnection;
113
+ onBidiMessage = bidiCommands.onBidiMessage;
114
+ onBidiServerError = bidiCommands.onBidiServerError;
115
+ cleanupBidiSockets = bidiCommands.cleanupBidiSockets;
116
+
117
+ configureGlobalFeatures = insecureFeatures.configureGlobalFeatures;
118
+ configureDriverFeatures = insecureFeatures.configureDriverFeatures;
119
+
120
+ listCommands = inspectorCommands.listCommands;
121
+ listExtensions = inspectorCommands.listExtensions;
122
+
123
+ private _isShuttingDown = false;
107
124
 
108
125
  /**
109
- * @param {import('@appium/types').DriverOpts<AppiumDriverConstraints>} opts
126
+ * @param opts - CLI/server options (address, port, security, default capabilities, etc.)
110
127
  */
111
- constructor(opts) {
128
+ constructor(opts: DriverOpts<AppiumDriverConstraints>) {
112
129
  // It is necessary to set `--tmp` here since it should be set to
113
130
  // process.env.APPIUM_TMP_DIR once at an initial point in the Appium lifecycle.
114
131
  // The process argument will be referenced by BaseDriver.
@@ -120,46 +137,40 @@ class AppiumDriver extends DriverCore {
120
137
  super(opts);
121
138
 
122
139
  this.args = {...opts};
123
- this.sessions = {};
124
- this.pendingDrivers = {};
125
- this.newCommandTimeoutMs = 0;
126
- this.pluginClasses = new Map();
127
- this.sessionPlugins = {};
128
- this.sessionlessPlugins = [];
129
- this.bidiSockets = {};
130
- this.bidiProxyClients = {};
131
- this.desiredCapConstraints = desiredCapabilityConstraints;
132
- this._isShuttingDown = false;
133
140
 
134
141
  // allow this to happen in the background, so no `await`
135
- (async () => {
142
+ void (async () => {
136
143
  try {
137
144
  await updateBuildInfo();
138
- } catch (e) {
145
+ } catch (e: unknown) {
139
146
  // make sure we catch any possible errors to avoid unhandled rejections
140
- this.log.debug(`Cannot fetch Appium build info: ${e.message}`);
147
+ const msg = e instanceof Error ? e.message : String(e);
148
+ this.log.debug(`Cannot fetch Appium build info: ${msg}`);
141
149
  }
142
150
  })();
143
151
  }
144
152
 
145
- /**
146
- * Cancel commands queueing for the umbrella Appium driver
147
- */
148
- get isCommandsQueueEnabled() {
153
+ /** The umbrella driver does not queue commands; inner session drivers may. */
154
+ get isCommandsQueueEnabled(): boolean {
149
155
  return false;
150
156
  }
151
157
 
152
- sessionExists(sessionId) {
158
+ /** Whether a non-null session id is registered on this server. */
159
+ override sessionExists(sessionId: string): boolean {
153
160
  const dstSession = this.sessions[sessionId];
154
- return dstSession && dstSession.sessionId !== null;
161
+ return Boolean(dstSession && dstSession.sessionId !== null);
155
162
  }
156
163
 
157
- driverForSession(sessionId) {
158
- return this.sessions[sessionId];
164
+ /** Active automation driver for the session, or `null` if unknown. */
165
+ override driverForSession(sessionId: string): ExternalDriver | null {
166
+ return this.sessions[sessionId] ?? null;
159
167
  }
160
168
 
161
- async getStatus() {
162
- // https://www.w3.org/TR/webdriver/#dfn-status
169
+ /**
170
+ * WebDriver status payload (readiness message and build metadata).
171
+ * @see https://www.w3.org/TR/webdriver/#dfn-status
172
+ */
173
+ override async getStatus() {
163
174
  const statusObj = this._isShuttingDown
164
175
  ? {
165
176
  ready: false,
@@ -175,32 +186,34 @@ class AppiumDriver extends DriverCore {
175
186
  };
176
187
  }
177
188
 
178
- /**
179
- * @param {string|null} reason An optional shutdown reason
180
- */
181
- async shutdown(reason = null) {
189
+ /** Marks the server as shutting down and ends all sessions (forced unexpected shutdown). */
190
+ async shutdown(reason: string | null = null): Promise<void> {
182
191
  this._isShuttingDown = true;
183
192
  await this.deleteAllSessions({
184
193
  force: true,
185
- reason,
194
+ reason: reason ?? undefined,
186
195
  });
187
196
  }
188
197
 
189
198
  /**
190
199
  * Retrieve information about all active sessions.
191
200
  * Results are returned only if the `session_discovery` insecure feature is enabled.
192
- * @returns {Promise<import('@appium/types').TimestampedMultiSessionData[]>}
193
201
  */
194
- async getAppiumSessions () {
202
+ async getAppiumSessions(): Promise<TimestampedMultiSessionData[]> {
195
203
  this.assertFeatureEnabled(SESSION_DISCOVERY_FEATURE);
196
204
  return _.toPairs(this.sessions).map(([id, driver]) => ({
197
205
  id,
198
206
  created: driver.sessionCreationTimestampMs,
199
- capabilities: /** @type {import('@appium/types').DriverCaps<any>} */ (driver.caps),
207
+ capabilities: driver.caps as DriverCaps<AppiumDriverConstraints>,
200
208
  }));
201
209
  }
202
210
 
203
- printNewSessionAnnouncement(driverName, driverVersion, driverBaseVersion) {
211
+ /** Logs BaseDriver version lines when starting a new inner-driver session. */
212
+ printNewSessionAnnouncement(
213
+ driverName: string,
214
+ driverVersion?: string,
215
+ driverBaseVersion?: string
216
+ ): void {
204
217
  this.log.info(
205
218
  driverVersion
206
219
  ? `Appium v${APPIUM_VER} creating new ${driverName} (v${driverVersion}) session`
@@ -220,48 +233,42 @@ class AppiumDriver extends DriverCore {
220
233
  }
221
234
 
222
235
  /**
223
- * Retrieves all CLI arguments for a specific plugin.
224
- * @param {string} extName - Plugin name
225
- * @returns {StringRecord} Arguments object. If none, an empty object.
236
+ * CLI arguments object for a plugin (from server config). Empty object if none were passed.
226
237
  */
227
- getCliArgsForPlugin(extName) {
228
- return /** @type {StringRecord} */ (this.args.plugin?.[extName] ?? {});
238
+ getCliArgsForPlugin(extName: string): StringRecord {
239
+ return (this.args.plugin?.[extName] ?? {}) as StringRecord;
229
240
  }
230
241
 
231
242
  /**
232
- * Retrieves CLI args for a specific driver.
233
- *
234
- * _Any arg which is equal to its default value will not be present in the returned object._
235
- *
236
- * _Note that this behavior currently (May 18 2022) differs from how plugins are handled_ (see {@linkcode AppiumDriver.getCliArgsForPlugin}).
237
- * @param {string} extName - Driver name
238
- * @returns {StringRecord|undefined} Arguments object. If none, `undefined`
243
+ * CLI arguments for a driver, omitting keys that match schema defaults (unlike
244
+ * {@link AppiumDriver.getCliArgsForPlugin}, which returns defaults explicitly). `undefined` if
245
+ * there is nothing to pass after omitting defaults.
239
246
  */
240
- getCliArgsForDriver(extName) {
241
- const allCliArgsForExt = /** @type {StringRecord|undefined} */ (this.args.driver?.[extName]);
242
-
243
- if (!_.isEmpty(allCliArgsForExt)) {
244
- const defaults = getDefaultsForExtension(DRIVER_TYPE, extName);
245
- const cliArgs = _.isEmpty(defaults)
246
- ? allCliArgsForExt
247
- : _.omitBy(allCliArgsForExt, (value, key) => _.isEqual(defaults[key], value));
248
- if (!_.isEmpty(cliArgs)) {
249
- return cliArgs;
250
- }
247
+ getCliArgsForDriver(extName: string): StringRecord | undefined {
248
+ const allCliArgsForExt = this.args.driver?.[extName] as StringRecord | undefined;
249
+ if (_.isEmpty(allCliArgsForExt)) {
250
+ return undefined;
251
251
  }
252
+
253
+ const defaults = getDefaultsForExtension(DRIVER_TYPE, extName);
254
+ const cliArgs = _.isEmpty(defaults)
255
+ ? allCliArgsForExt
256
+ : _.omitBy(allCliArgsForExt, (value, key) => _.isEqual(defaults[key], value));
257
+ return _.isEmpty(cliArgs) ? undefined : cliArgs;
252
258
  }
253
259
 
254
260
  /**
255
- * Create a new session
256
- *
257
- * @param {W3CAppiumDriverCaps} w3cCapabilities1 W3C capabilities
258
- * @param {W3CAppiumDriverCaps} [w3cCapabilities2] W3C capabilities (legacy)
259
- * @param {W3CAppiumDriverCaps} [w3cCapabilities3] W3C capabilities (legacy)
260
- * @returns {Promise<SessionHandlerCreateResult>}
261
+ * Creates a session: picks an inner driver from caps, runs plugin hooks, and returns a protocol
262
+ * envelope with either `[sessionId, caps, protocol]` or an error. Legacy call sites may pass the
263
+ * same W3C caps in up to three positions; the first W3C-shaped value wins.
261
264
  */
262
- async createSession(w3cCapabilities1, w3cCapabilities2, w3cCapabilities3) {
265
+ async createSession(
266
+ w3cCapabilities1: W3CAppiumDriverCaps,
267
+ w3cCapabilities2?: W3CAppiumDriverCaps,
268
+ w3cCapabilities3?: W3CAppiumDriverCaps
269
+ ): Promise<SessionHandlerCreateResult> {
263
270
  const defaultCapabilities = _.cloneDeep(this.args.defaultCapabilities);
264
- const defaultSettings = pullSettings(defaultCapabilities);
271
+ const defaultSettings = pullSettings((defaultCapabilities ?? {}) as StringRecord);
265
272
  const w3cCapabilities = _.cloneDeep(
266
273
  [w3cCapabilities3, w3cCapabilities2, w3cCapabilities1].find(isW3cCaps)
267
274
  );
@@ -277,24 +284,21 @@ class AppiumDriver extends DriverCore {
277
284
  }
278
285
 
279
286
  const protocol = PROTOCOLS.W3C;
280
- let innerSessionId, dCaps;
287
+ let innerSessionId: string;
288
+ let dCaps: DriverCaps<AppiumDriverConstraints> & {webSocketUrl?: string | boolean};
281
289
  try {
282
290
  // Parse the caps into a format that the InnerDriver will accept
283
- const parsedCaps = parseCapsForInnerDriver(
284
- promoteAppiumOptions(/** @type {W3CAppiumDriverCaps} */ (w3cCapabilities)),
291
+ const parsedCaps = parseCapsForInnerDriver<AppiumDriverConstraints>(
292
+ promoteAppiumOptions(w3cCapabilities),
285
293
  this.desiredCapConstraints,
286
- defaultCapabilities ? promoteAppiumOptionsForObject(defaultCapabilities) : undefined,
294
+ defaultCapabilities ? promoteAppiumOptionsForObject(defaultCapabilities) : undefined
287
295
  );
288
296
 
289
- const {desiredCaps, processedW3CCapabilities} =
290
- /** @type {import('./utils').ParsedDriverCaps<AppiumDriverConstraints>} */ (parsedCaps);
291
- const error = /** @type {import('./utils').InvalidCaps<AppiumDriverConstraints>} */ (
292
- parsedCaps
293
- ).error;
294
- // If the parsing of the caps produced an error, throw it in here
295
- if (error) {
296
- throw error;
297
+ if ('error' in parsedCaps && parsedCaps.error) {
298
+ throw parsedCaps.error;
297
299
  }
300
+ const {desiredCaps, processedW3CCapabilities} =
301
+ parsedCaps as ParsedDriverCaps<AppiumDriverConstraints>;
298
302
 
299
303
  const {
300
304
  driver: InnerDriver,
@@ -307,16 +311,10 @@ class AppiumDriver extends DriverCore {
307
311
  await this.deleteAllSessions();
308
312
  }
309
313
 
310
- /**
311
- * @type {DriverData[]}
312
- */
313
- let runningDriversData = [];
314
- /**
315
- * @type {DriverData[]}
316
- */
317
- let otherPendingDriversData = [];
314
+ let runningDriversData: DriverData[] = [];
315
+ let otherPendingDriversData: DriverData[] = [];
318
316
 
319
- const driverInstance = /** @type {ExternalDriver} */ (new InnerDriver(this.args, true));
317
+ const driverInstance = new InnerDriver(this.args, true) as unknown as ExternalDriver;
320
318
 
321
319
  this.configureDriverFeatures(driverInstance, driverName);
322
320
 
@@ -331,7 +329,7 @@ class AppiumDriver extends DriverCore {
331
329
  // the driver so that they cannot be mimicked by a malicious user sending in capabilities
332
330
  const cliArgs = this.getCliArgsForDriver(driverName);
333
331
  if (!_.isUndefined(cliArgs)) {
334
- driverInstance.cliArgs = cliArgs;
332
+ (driverInstance as ExternalDriver & {cliArgs?: StringRecord}).cliArgs = cliArgs;
335
333
  }
336
334
 
337
335
  // This assignment is required for correct web sockets functionality inside the driver
@@ -345,29 +343,26 @@ class AppiumDriver extends DriverCore {
345
343
 
346
344
  try {
347
345
  runningDriversData = (await this.curSessionDataForDriver(InnerDriver)) ?? [];
348
- } catch (e) {
349
- throw new errors.SessionNotCreatedError(e.message);
346
+ } catch (e: unknown) {
347
+ const msg = e instanceof Error ? e.message : String(e);
348
+ throw new errors.SessionNotCreatedError(msg);
350
349
  }
351
- await pendingDriversGuard.acquire(AppiumDriver.name, () => {
352
- this.pendingDrivers[InnerDriver.name] = this.pendingDrivers[InnerDriver.name] || [];
353
- otherPendingDriversData = _.compact(
354
- this.pendingDrivers[InnerDriver.name].map((drv) => drv.driverData),
355
- );
356
- this.pendingDrivers[InnerDriver.name].push(driverInstance);
357
- });
350
+ this.pendingDrivers[InnerDriver.name] = this.pendingDrivers[InnerDriver.name] || [];
351
+ otherPendingDriversData = _.compact(
352
+ this.pendingDrivers[InnerDriver.name].map((drv) => drv.driverData),
353
+ );
354
+ this.pendingDrivers[InnerDriver.name].push(driverInstance);
358
355
 
359
356
  try {
360
- [innerSessionId, dCaps] = await driverInstance.createSession(
361
- /** @type {any} */ (processedW3CCapabilities),
357
+ [innerSessionId, dCaps] = (await driverInstance.createSession(
358
+ processedW3CCapabilities as never,
362
359
  processedW3CCapabilities,
363
360
  processedW3CCapabilities,
364
- [...runningDriversData, ...otherPendingDriversData],
365
- );
361
+ [...runningDriversData, ...otherPendingDriversData]
362
+ )) as [string, DriverCaps<AppiumDriverConstraints> & {webSocketUrl?: string | boolean}];
366
363
  this.sessions[innerSessionId] = driverInstance;
367
364
  } finally {
368
- await pendingDriversGuard.acquire(AppiumDriver.name, () => {
369
- _.pull(this.pendingDrivers[InnerDriver.name], driverInstance);
370
- });
365
+ _.pull(this.pendingDrivers[InnerDriver.name], driverInstance);
371
366
  }
372
367
 
373
368
  this.attachUnexpectedShutdownHandler(driverInstance, innerSessionId);
@@ -406,10 +401,10 @@ class AppiumDriver extends DriverCore {
406
401
  // capability constraint system
407
402
  dCaps.webSocketUrl = bidiUrl;
408
403
  }
409
- } catch (error) {
404
+ } catch (error: unknown) {
410
405
  return {
411
406
  protocol,
412
- error,
407
+ error: error instanceof Error ? error : new Error(String(error)),
413
408
  };
414
409
  }
415
410
 
@@ -420,12 +415,11 @@ class AppiumDriver extends DriverCore {
420
415
  }
421
416
 
422
417
  /**
423
- *
424
- * @param {ExternalDriver} driver
425
- * @param {string} innerSessionId
418
+ * Subscribes to the inner driver’s unexpected-shutdown hook so Appium can drop the session and
419
+ * notify plugins.
426
420
  */
427
- attachUnexpectedShutdownHandler(driver, innerSessionId) {
428
- const onShutdown = (cause = new Error('Unknown error')) => {
421
+ attachUnexpectedShutdownHandler(driver: ExternalDriver, innerSessionId: string): void {
422
+ const onShutdown = async (cause: Error = new Error('Unknown error')) => {
429
423
  this.log.warn(`Ending session, cause was '${cause.message}'`);
430
424
 
431
425
  if (this.sessionPlugins[innerSessionId]) {
@@ -435,7 +429,7 @@ class AppiumDriver extends DriverCore {
435
429
  `Plugin ${plugin.name} defines an unexpected shutdown handler; calling it now`,
436
430
  );
437
431
  try {
438
- plugin.onUnexpectedShutdown(driver, cause);
432
+ await plugin.onUnexpectedShutdown(driver, cause);
439
433
  } catch (e) {
440
434
  this.log.warn(
441
435
  `Got an error when running plugin ${plugin.name} shutdown handler: ${e}`,
@@ -463,13 +457,11 @@ class AppiumDriver extends DriverCore {
463
457
  }
464
458
 
465
459
  /**
466
- *
467
- * @param {((...args: any[]) => any)|(new(...args: any[]) => any)} InnerDriver
468
- * @returns {Promise<DriverData[]>}}
469
- * @privateRemarks The _intent_ is that `InnerDriver` is the class of a driver, but it only really
470
- * needs to be a function or constructor.
460
+ * Collects `driverData` for every active session whose driver class matches `InnerDriver.name`
461
+ * (used when creating another session of the same driver type).
462
+ * @remarks `InnerDriver` is expected to be the driver class; only `.name` is read.
471
463
  */
472
- async curSessionDataForDriver(InnerDriver) {
464
+ async curSessionDataForDriver(InnerDriver: {name: string}): Promise<DriverData[]> {
473
465
  const data = _.compact(
474
466
  _.values(this.sessions)
475
467
  .filter((s) => s.constructor.name === InnerDriver.name)
@@ -487,23 +479,22 @@ class AppiumDriver extends DriverCore {
487
479
  }
488
480
 
489
481
  /**
490
- * @param {string} sessionId
482
+ * Ends one session: removes it from the master list immediately, then delegates to the inner
483
+ * driver’s `deleteSession` with sibling-session metadata.
491
484
  */
492
- async deleteSession(sessionId) {
493
- let protocol;
485
+ async deleteSession(sessionId: string): Promise<SessionHandlerDeleteResult> {
486
+ let protocol: Protocol | undefined;
494
487
  try {
495
- let otherSessionsData;
496
- const dstSession = await sessionsListGuard.acquire(AppiumDriver.name, () => {
497
- if (!this.sessions[sessionId]) {
498
- return;
499
- }
488
+ let otherSessionsData: DriverData[] | undefined;
489
+ let dstSession: ExternalDriver | undefined;
490
+ if (this.sessions[sessionId]) {
500
491
  const curConstructorName = this.sessions[sessionId].constructor.name;
501
492
  otherSessionsData = _.toPairs(this.sessions)
502
493
  .filter(
503
494
  ([key, value]) => value.constructor.name === curConstructorName && key !== sessionId,
504
495
  )
505
496
  .map(([, value]) => value.driverData);
506
- const dstSession = this.sessions[sessionId];
497
+ dstSession = this.sessions[sessionId];
507
498
  protocol = dstSession.protocol;
508
499
  this.log.info(`Removing session ${sessionId} from our master session list`);
509
500
  // regardless of whether the deleteSession completes successfully or not
@@ -513,9 +504,7 @@ class AppiumDriver extends DriverCore {
513
504
  delete this.sessionPlugins[sessionId];
514
505
 
515
506
  this.cleanupBidiSockets(sessionId);
516
-
517
- return dstSession;
518
- });
507
+ }
519
508
  // this may not be correct, but if `dstSession` was falsy, the call to `deleteSession()` would
520
509
  // throw anyway.
521
510
  if (!dstSession) {
@@ -525,16 +514,21 @@ class AppiumDriver extends DriverCore {
525
514
  protocol,
526
515
  value: await dstSession.deleteSession(sessionId, otherSessionsData),
527
516
  };
528
- } catch (e) {
529
- this.log.error(`Had trouble ending session ${sessionId}: ${e.message}`);
517
+ } catch (e: unknown) {
518
+ const msg = e instanceof Error ? e.message : String(e);
519
+ this.log.error(`Had trouble ending session ${sessionId}: ${msg}`);
530
520
  return {
531
521
  protocol,
532
- error: e,
522
+ error: e instanceof Error ? e : new Error(msg),
533
523
  };
534
524
  }
535
525
  }
536
526
 
537
- async deleteAllSessions(opts = {}) {
527
+ /**
528
+ * Ends every active session, either by normal `deleteSession` or by `startUnexpectedShutdown`
529
+ * when `force` is true.
530
+ */
531
+ async deleteAllSessions(opts: {force?: boolean; reason?: string} = {}): Promise<void> {
538
532
  const sessionsCount = _.size(this.sessions);
539
533
  if (0 === sessionsCount) {
540
534
  this.log.debug('There are no active sessions for cleanup');
@@ -545,7 +539,7 @@ class AppiumDriver extends DriverCore {
545
539
  this.log.debug(`Cleaning up ${util.pluralize('active session', sessionsCount, true)}`);
546
540
  const cleanupPromises = force
547
541
  ? _.values(this.sessions).map((drv) =>
548
- drv.startUnexpectedShutdown(reason && new Error(reason)),
542
+ drv.startUnexpectedShutdown(reason ? new Error(reason) : undefined),
549
543
  )
550
544
  : _.keys(this.sessions).map((id) => this.deleteSession(id));
551
545
  for (const cleanupPromise of cleanupPromises) {
@@ -558,16 +552,22 @@ class AppiumDriver extends DriverCore {
558
552
  }
559
553
 
560
554
  /**
561
- * Get the appropriate plugins for a session (or sessionless plugins)
562
- *
563
- * @param {string|null} [sessionId=null] - the sessionId (or null) to use to find plugins
564
- * @returns {Array<import('@appium/types').Plugin>} - array of plugin instances
555
+ * Plugin instances for a session id, or the shared sessionless list when `sessionId` is null.
556
+ * Lazily builds sessionless instances on first use.
565
557
  */
566
- pluginsForSession(sessionId = null) {
558
+ pluginsForSession(sessionId: string | null = null): Plugin[] {
567
559
  if (sessionId) {
560
+ const existingPlugins = this.sessionPlugins[sessionId];
561
+ if (existingPlugins) {
562
+ return existingPlugins;
563
+ }
568
564
  const driver = this.sessions[sessionId];
569
- return this.sessionPlugins[sessionId]
570
- ?? (driver ? this.createPluginInstances(generateDriverLogPrefix(driver)) : []);
565
+ if (!driver) {
566
+ return [];
567
+ }
568
+ return (this.sessionPlugins[sessionId] = this.createPluginInstances(
569
+ generateDriverLogPrefix(driver),
570
+ ));
571
571
  }
572
572
 
573
573
  if (_.isEmpty(this.sessionlessPlugins)) {
@@ -577,16 +577,10 @@ class AppiumDriver extends DriverCore {
577
577
  }
578
578
 
579
579
  /**
580
- * To get plugins for a command, we either get the plugin instances associated with the
581
- * particular command's session, or in the case of sessionless plugins, pull from the set of
582
- * plugin instances reserved for sessionless commands (and we lazily create plugin instances on
583
- * first use)
584
- *
585
- * @param {string} cmd - the name of the command to find a plugin to handle
586
- * @param {?string} sessionId - the particular session for which to find a plugin, or null if
587
- * sessionless
580
+ * Plugins that declare a method named `cmd` or a generic `handle` method, scoped to the given
581
+ * session (or sessionless when `sessionId` is null).
588
582
  */
589
- pluginsToHandleCmd(cmd, sessionId = null) {
583
+ pluginsToHandleCmd(cmd: string, sessionId: string | null = null): Plugin[] {
590
584
  // to handle a given command, a plugin should either implement that command as a plugin
591
585
  // instance method or it should implement a generic 'handle' method
592
586
  return this.pluginsForSession(sessionId).filter(
@@ -595,19 +589,17 @@ class AppiumDriver extends DriverCore {
595
589
  }
596
590
 
597
591
  /**
598
- * Creates instances of all of the enabled Plugin classes
599
- * @param {string|null} [driverId=null] - ID to use for linking a driver to a plugin in logs
600
- * @returns {Plugin[]}
592
+ * One instance per registered plugin class. `driverId` becomes the plugin log prefix segment
593
+ * when tied to a session driver; use `null` for sessionless plugins.
601
594
  */
602
- createPluginInstances(driverId = null) {
603
- /** @type {Plugin[]} */
604
- const pluginInstances = [];
595
+ createPluginInstances(driverId: string | null = null): Plugin[] {
596
+ const pluginInstances: Plugin[] = [];
605
597
  for (const [PluginClass, name] of this.pluginClasses.entries()) {
606
598
  const cliArgs = this.getCliArgsForPlugin(name);
607
599
  const plugin = new PluginClass(name, cliArgs, driverId);
608
- if (_.isFunction(/** @type {Plugin & ExtensionCore} */(plugin).updateBidiCommands)) {
609
- // some old plugin classes don't have `updateBidiCommands`
610
- /** @type {Plugin & ExtensionCore} */(plugin).updateBidiCommands(PluginClass.newBidiCommands ?? {});
600
+ const extPlugin = plugin as Plugin & ExtensionCore;
601
+ if (_.isFunction(extPlugin.updateBidiCommands)) {
602
+ extPlugin.updateBidiCommands(PluginClass.newBidiCommands ?? {});
611
603
  }
612
604
  pluginInstances.push(plugin);
613
605
  }
@@ -615,12 +607,10 @@ class AppiumDriver extends DriverCore {
615
607
  }
616
608
 
617
609
  /**
618
- *
619
- * @param {string} cmd
620
- * @param {...any} args
621
- * @returns {Promise<{value: any, error?: Error, protocol: string} | import('type-fest').AsyncReturnType<ExternalDriver['executeCommand']>>}
610
+ * Dispatches a WebDriver/Appium command: may run on this driver, a session’s inner driver, or
611
+ * through a plugin chain, and normalizes the return value into a protocol-shaped result.
622
612
  */
623
- async executeCommand(cmd, ...args) {
613
+ async executeCommand(cmd: string, ...args: any[]): Promise<SessionHandlerResult<unknown>> {
624
614
  // We have basically three cases for how to handle commands:
625
615
  // 1. handle getStatus (we do this as a special out of band case so it doesn't get added to an
626
616
  // execution queue, and can be called while e.g. createSession is in progress)
@@ -644,15 +634,14 @@ class AppiumDriver extends DriverCore {
644
634
 
645
635
  // first do some error checking. If we're requesting a session command execution, then make
646
636
  // sure that session actually exists on the session driver, and set the session driver itself
647
- let sessionId = null;
648
- let dstSession = null;
649
- let protocol = null;
650
- /** @type {this | ExternalDriver} */
637
+ let sessionId: string | null = null;
638
+ let dstSession: ExternalDriver | null = null;
639
+ let protocol: string | null | undefined = null;
651
640
  // eslint-disable-next-line @typescript-eslint/no-this-alias
652
- let driver = this;
641
+ let driver: this | ExternalDriver = this;
653
642
  if (isSessionCmd) {
654
- sessionId = _.last(args);
655
- dstSession = this.sessions[sessionId];
643
+ sessionId = _.last(args) as string;
644
+ dstSession = this.sessions[sessionId] ?? null;
656
645
  if (!dstSession) {
657
646
  throw new Error(`The session with id '${sessionId}' does not exist`);
658
647
  }
@@ -682,7 +671,7 @@ class AppiumDriver extends DriverCore {
682
671
  // original command execution. This results in a situation where the command might be handled
683
672
  // by some but not all plugins, or by plugin(s) but not by the default behavior. So start out
684
673
  // this object declaring that the default handler has not been executed.
685
- const cmdHandledBy = {default: false};
674
+ const cmdHandledBy: Record<string, boolean> = {default: false};
686
675
 
687
676
  // now we define an async function which will be passed to plugins, and successively wrapped
688
677
  // if there is more than one plugin that can handle the command. To start off with, the async
@@ -724,8 +713,10 @@ class AppiumDriver extends DriverCore {
724
713
  return await BaseDriver.prototype.executeCommand.call(this, cmd, ...args);
725
714
  }
726
715
 
727
- // here we know that we are executing a session command, and have a valid session driver
728
- return await /** @type {any} */ (dstSession).executeCommand(cmd, ...args);
716
+ if (!dstSession) {
717
+ throw new Error('Internal error: session command without a session driver');
718
+ }
719
+ return await dstSession.executeCommand(cmd, ...args);
729
720
  };
730
721
 
731
722
  // now take our default behavior, wrap it with any number of plugin behaviors, and run it
@@ -763,16 +754,18 @@ class AppiumDriver extends DriverCore {
763
754
  // previously sessionless to use the new sessionId, so that plugins can share state between
764
755
  // their createSession method and other instance methods
765
756
  if (cmd === CREATE_SESSION_COMMAND && this.sessionlessPlugins.length && !res.error) {
766
- const sessionId = _.first(res.value);
757
+ const newSessionId = _.first(res.value as unknown[]) as string;
767
758
  this.log.info(
768
759
  `Promoting ${this.sessionlessPlugins.length} sessionless plugins to be attached ` +
769
- `to session ID ${sessionId}`,
760
+ `to session ID ${newSessionId}`,
770
761
  );
771
- this.sessionPlugins[sessionId] = this.sessionlessPlugins;
772
- for (const p of /** @type {(Plugin & ExtensionCore)[]} */(this.sessionPlugins[sessionId])) {
762
+ this.sessionPlugins[newSessionId] = this.sessionlessPlugins;
763
+ const promoted = this.sessionPlugins[newSessionId] as (Plugin & ExtensionCore)[];
764
+ for (const p of promoted) {
773
765
  if (_.isFunction(p.updateLogPrefix)) {
774
- // some old plugin classes don't have `updateLogPrefix` yet
775
- p.updateLogPrefix(`${generateDriverLogPrefix(p)} <${generateDriverLogPrefix(this.sessions[sessionId])}>`);
766
+ p.updateLogPrefix(
767
+ `${generateDriverLogPrefix(p)} <${generateDriverLogPrefix(this.sessions[newSessionId])}>`
768
+ );
776
769
  }
777
770
  }
778
771
  this.sessionlessPlugins = [];
@@ -781,7 +774,22 @@ class AppiumDriver extends DriverCore {
781
774
  return res;
782
775
  }
783
776
 
784
- wrapCommandWithPlugins({driver, cmd, args, next, cmdHandledBy, plugins}) {
777
+ /** Builds an async chain: each plugin wraps `next` until the default driver behavior runs. */
778
+ wrapCommandWithPlugins({
779
+ driver,
780
+ cmd,
781
+ args,
782
+ next,
783
+ cmdHandledBy,
784
+ plugins,
785
+ }: {
786
+ driver: AppiumDriver | ExternalDriver;
787
+ cmd: string;
788
+ args: any[];
789
+ next: () => Promise<unknown>;
790
+ cmdHandledBy: Record<string, boolean>;
791
+ plugins: Plugin[];
792
+ }): () => Promise<unknown> {
785
793
  if (plugins.length) {
786
794
  this.log.info(`Plugins which can handle cmd '${cmd}': ${plugins.map((p) => p.name)}`);
787
795
  }
@@ -797,18 +805,31 @@ class AppiumDriver extends DriverCore {
797
805
  this.log.info(`Plugin ${plugin.name} is now handling cmd '${cmd}'`);
798
806
  cmdHandledBy[plugin.name] = true; // if we make it here, this plugin has attempted to handle cmd
799
807
  // first attempt to handle the command via a command-specific handler on the plugin
800
- if (plugin[cmd]) {
801
- return await plugin[cmd](_next, driver, ...args);
808
+ const cmdHandler = (plugin as Plugin & Record<string, unknown>)[cmd];
809
+ if (_.isFunction(cmdHandler)) {
810
+ // Command methods must run with plugin as `this` (detached property access drops binding).
811
+ return await (cmdHandler as PluginCommand).call(
812
+ plugin,
813
+ _next,
814
+ driver as ExternalDriver,
815
+ ...args,
816
+ );
817
+ }
818
+ if (!_.isFunction(plugin.handle)) {
819
+ throw new Error(`Plugin ${plugin.name} cannot handle command '${cmd}'`);
802
820
  }
803
- // otherwise, call the generic 'handle' method
804
- return await plugin.handle(_next, driver, cmd, ...args);
821
+ return await plugin.handle(_next, driver as ExternalDriver, cmd, ...args);
805
822
  })(next);
806
823
  }
807
824
 
808
825
  return next;
809
826
  }
810
827
 
811
- logPluginHandlerReport(plugins, {cmd, cmdHandledBy}) {
828
+ /** After a command with plugins, logs which handlers ran vs. were skipped (debugging aid). */
829
+ logPluginHandlerReport(
830
+ plugins: Plugin[],
831
+ {cmd, cmdHandledBy}: {cmd: string; cmdHandledBy: Record<string, boolean>}
832
+ ): void {
812
833
  if (!plugins.length) {
813
834
  return;
814
835
  }
@@ -830,10 +851,17 @@ class AppiumDriver extends DriverCore {
830
851
  }
831
852
  }
832
853
 
833
- async executeWrappedCommand({wrappedCmd, protocol}) {
834
- let cmdRes,
835
- cmdErr,
836
- res = {};
854
+ /** Runs the wrapped plugin chain and merges the result into a `SessionHandlerResult` shape. */
855
+ async executeWrappedCommand({
856
+ wrappedCmd,
857
+ protocol,
858
+ }: {
859
+ wrappedCmd: () => Promise<unknown>;
860
+ protocol: string | null | undefined;
861
+ }): Promise<SessionHandlerResult<unknown>> {
862
+ let cmdRes: unknown;
863
+ let cmdErr: unknown;
864
+ const res: SessionHandlerResult<unknown> = {};
837
865
  try {
838
866
  // At this point, `wrappedCmd` defines a whole sequence of plugin handlers, culminating in
839
867
  // our default handler. Whatever it returns is what we're going to want to send back to the
@@ -847,60 +875,33 @@ class AppiumDriver extends DriverCore {
847
875
  // object, or a protocol-aware object with protocol and error/value keys. So we need to sniff
848
876
  // it and make sure we don't double-wrap it if it's the latter kind.
849
877
  if (_.isPlainObject(cmdRes) && _.has(cmdRes, 'protocol')) {
850
- res = cmdRes;
878
+ Object.assign(res, cmdRes);
851
879
  } else {
852
880
  res.value = cmdRes;
853
- res.error = cmdErr;
854
- res.protocol = protocol;
881
+ res.error = cmdErr instanceof Error ? cmdErr : undefined;
882
+ res.protocol = protocol ?? undefined;
855
883
  }
856
884
  return res;
857
885
  }
858
886
 
859
- proxyActive(sessionId) {
887
+ /** Whether the inner session driver is actively proxying for this session id. */
888
+ override proxyActive(sessionId: string): boolean {
860
889
  const dstSession = this.sessions[sessionId];
861
- return dstSession && _.isFunction(dstSession.proxyActive) && dstSession.proxyActive(sessionId);
890
+ return _.isFunction(dstSession?.proxyActive) && dstSession.proxyActive(sessionId);
862
891
  }
863
892
 
864
- /**
865
- *
866
- * @param {string} sessionId
867
- * @returns {import('@appium/types').RouteMatcher[]}
868
- */
869
- getProxyAvoidList(sessionId) {
893
+ /** URL patterns the session driver does not want proxied; empty if no session or no list. */
894
+ override getProxyAvoidList(sessionId: string): RouteMatcher[] {
870
895
  const dstSession = this.sessions[sessionId];
871
- return dstSession ? dstSession.getProxyAvoidList() : [];
896
+ return _.isFunction(dstSession?.getProxyAvoidList) ? dstSession.getProxyAvoidList() : [];
872
897
  }
873
898
 
874
- canProxy(sessionId) {
899
+ /** Whether the session driver supports proxying for this session. */
900
+ override canProxy(sessionId: string): boolean {
875
901
  const dstSession = this.sessions[sessionId];
876
- return dstSession && dstSession.canProxy(sessionId);
902
+ return _.isFunction(dstSession?.canProxy) && dstSession.canProxy(sessionId);
877
903
  }
878
904
 
879
- onBidiConnection = bidiCommands.onBidiConnection;
880
- onBidiMessage = bidiCommands.onBidiMessage;
881
- onBidiServerError = bidiCommands.onBidiServerError;
882
- cleanupBidiSockets = bidiCommands.cleanupBidiSockets;
883
-
884
- configureGlobalFeatures = insecureFeatures.configureGlobalFeatures;
885
- configureDriverFeatures = insecureFeatures.configureDriverFeatures;
886
-
887
- listCommands = inspectorCommands.listCommands;
888
- listExtensions = inspectorCommands.listExtensions;
889
- }
890
-
891
- /**
892
- * Help decide which commands should be proxied to sub-drivers and which
893
- * should be handled by this, our umbrella driver
894
- * @param {string} cmd
895
- * @returns {boolean}
896
- */
897
- function isAppiumDriverCommand(cmd) {
898
- return !isSessionCommand(cmd)
899
- || _.includes([
900
- DELETE_SESSION_COMMAND,
901
- LIST_DRIVER_COMMANDS_COMMAND,
902
- LIST_DRIVER_EXTENSIONS_COMMAND,
903
- ], cmd);
904
905
  }
905
906
 
906
907
  /**
@@ -908,10 +909,7 @@ function isAppiumDriverCommand(cmd) {
908
909
  * method did not exist
909
910
  */
910
911
  export class NoDriverProxyCommandError extends Error {
911
- /**
912
- * @type {Readonly<string>}
913
- */
914
- code = 'APPIUMERR_NO_DRIVER_PROXYCOMMAND';
912
+ readonly code = 'APPIUMERR_NO_DRIVER_PROXYCOMMAND';
915
913
 
916
914
  constructor() {
917
915
  super(
@@ -923,55 +921,13 @@ export class NoDriverProxyCommandError extends Error {
923
921
  }
924
922
  }
925
923
 
926
- export {AppiumDriver};
927
-
928
- /**
929
- * @typedef {import('@appium/types').DriverData} DriverData
930
- * @typedef {import('@appium/types').ServerArgs} DriverOpts
931
- * @typedef {import('@appium/types').Constraints} Constraints
932
- * @typedef {import('@appium/types').AppiumServer} AppiumServer
933
- * @typedef {import('@appium/types').ExtensionType} ExtensionType
934
- * @typedef {import('./extension/driver-config').DriverConfig} DriverConfig
935
- * @typedef {import('@appium/types').PluginType} PluginType
936
- * @typedef {import('@appium/types').DriverType} DriverType
937
- * @typedef {import('@appium/types').StringRecord} StringRecord
938
- * @typedef {import('@appium/types').ExternalDriver} ExternalDriver
939
- * @typedef {import('@appium/types').PluginClass} PluginClass
940
- * @typedef {import('@appium/types').Plugin} Plugin
941
- * @typedef {import('@appium/base-driver').ExtensionCore} ExtensionCore
942
- * @typedef {import('@appium/types').DriverClass<import('@appium/types').Driver>} DriverClass
943
- */
944
-
945
- /**
946
- * @typedef {import('@appium/types').ISessionHandler<AppiumDriverConstraints,
947
- * SessionHandlerCreateResult, SessionHandlerDeleteResult>} AppiumSessionHandler
948
- */
949
-
950
- /**
951
- * @typedef {SessionHandlerResult<[innerSessionId: string, caps:
952
- * import('@appium/types').DriverCaps<Constraints>, protocol: string|undefined]>} SessionHandlerCreateResult
953
- */
954
-
955
- /**
956
- * @template {Constraints} C
957
- * @typedef {import('@appium/types').Core<C>} Core
958
- */
959
-
960
- /**
961
- * @typedef {SessionHandlerResult<void>} SessionHandlerDeleteResult
962
- */
963
-
964
- /**
965
- * Used by {@linkcode AppiumDriver.createSession} and {@linkcode AppiumDriver.deleteSession} to describe
966
- * result.
967
- * @template V
968
- * @typedef SessionHandlerResult
969
- * @property {V} [value]
970
- * @property {Error} [error]
971
- * @property {string} [protocol]
972
- */
924
+ /** True if `cmd` should run on the umbrella driver instead of only on the session’s inner driver. */
925
+ function isAppiumDriverCommand(cmd: string): boolean {
926
+ return !isSessionCommand(cmd)
927
+ || _.includes([
928
+ DELETE_SESSION_COMMAND,
929
+ LIST_DRIVER_COMMANDS_COMMAND,
930
+ LIST_DRIVER_EXTENSIONS_COMMAND,
931
+ ], cmd);
932
+ }
973
933
 
974
- /**
975
- * @typedef {typeof desiredCapabilityConstraints} AppiumDriverConstraints
976
- * @typedef {import('@appium/types').W3CDriverCaps<AppiumDriverConstraints>} W3CAppiumDriverCaps
977
- */