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
@@ -36,19 +36,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.AppiumDriver = exports.NoDriverProxyCommandError = void 0;
39
+ exports.NoDriverProxyCommandError = exports.AppiumDriver = void 0;
40
40
  const lodash_1 = __importDefault(require("lodash"));
41
- const config_1 = require("./config");
42
41
  const base_driver_1 = require("@appium/base-driver");
43
- const async_lock_1 = __importDefault(require("async-lock"));
44
- const utils_1 = require("./utils");
42
+ const build_1 = require("./helpers/build");
43
+ const capability_1 = require("./helpers/capability");
45
44
  const support_1 = require("@appium/support");
46
45
  const schema_1 = require("./schema");
47
46
  const constants_1 = require("./constants");
48
47
  const bidiCommands = __importStar(require("./bidi-commands"));
49
48
  const insecureFeatures = __importStar(require("./insecure-features"));
50
49
  const inspectorCommands = __importStar(require("./inspector-commands"));
51
- const desiredCapabilityConstraints = /** @type {const} */ ({
50
+ const desiredCapabilityConstraints = {
52
51
  automationName: {
53
52
  presence: true,
54
53
  isString: true,
@@ -57,65 +56,41 @@ const desiredCapabilityConstraints = /** @type {const} */ ({
57
56
  presence: true,
58
57
  isString: true,
59
58
  },
60
- });
61
- const sessionsListGuard = new async_lock_1.default();
62
- const pendingDriversGuard = new async_lock_1.default();
59
+ };
63
60
  /**
64
- * @extends {DriverCore<AppiumDriverConstraints>}
61
+ * Umbrella driver: owns the session table, loads platform drivers and plugins, and routes
62
+ * commands to the right session driver or plugin chain.
65
63
  */
66
64
  class AppiumDriver extends base_driver_1.DriverCore {
65
+ sessions = {};
66
+ pendingDrivers = {};
67
67
  /**
68
- * Access to sessions list must be guarded with a Semaphore, because
69
- * it might be changed by other async calls at any time
70
- * It is not recommended to access this property directly from the outside
71
- * @type {Record<string,ExternalDriver>}
72
- */
73
- sessions;
74
- /**
75
- * Access to pending drivers list must be guarded with a Semaphore, because
76
- * it might be changed by other async calls at any time
77
- * It is not recommended to access this property directly from the outside
78
- * @type {Record<string,ExternalDriver[]>}
79
- */
80
- pendingDrivers;
81
- /**
82
- * Note that {@linkcode AppiumDriver} has no `newCommandTimeout` method.
83
- * `AppiumDriver` does not set and observe its own timeouts; individual
84
- * sessions (managed drivers) do instead.
68
+ * The umbrella driver does not observe its own command timeout; inner session drivers do.
85
69
  */
86
- newCommandTimeoutMs;
87
- /**
88
- * List of active plugins
89
- * @type {Map<PluginClass,string>}
90
- */
91
- pluginClasses;
92
- /**
93
- * map of sessions to actual plugin instances per session
94
- * @type {Record<string,InstanceType<PluginClass>[]>}
95
- */
96
- sessionPlugins;
97
- /**
98
- * some commands are sessionless, so we need a set of plugins for them
99
- * @type {InstanceType<PluginClass>[]}
100
- */
101
- sessionlessPlugins;
102
- /** @type {DriverConfig} */
70
+ newCommandTimeoutMs = 0;
71
+ /** Filled during server bootstrap; keep this map instance (do not reassign). */
72
+ pluginClasses = new Map();
73
+ sessionPlugins = {};
74
+ sessionlessPlugins = [];
75
+ /** Set during server init before listen; tests may assign a mock after construction. */
103
76
  driverConfig;
104
- /** @type {AppiumServer} */
77
+ /** Set when the HTTP server is created in server bootstrap. */
105
78
  server;
106
- /** @type {Record<string, import('ws').WebSocket[]>} */
107
- bidiSockets;
108
- /** @type {Record<string, import('ws').WebSocket>} */
109
- bidiProxyClients;
110
- /**
111
- * @type {AppiumDriverConstraints}
112
- * @readonly
113
- */
114
- desiredCapConstraints;
115
- /** @type {import('@appium/types').DriverOpts<AppiumDriverConstraints>} */
79
+ bidiSockets = {};
80
+ bidiProxyClients = {};
81
+ desiredCapConstraints = desiredCapabilityConstraints;
116
82
  args;
83
+ onBidiConnection = bidiCommands.onBidiConnection;
84
+ onBidiMessage = bidiCommands.onBidiMessage;
85
+ onBidiServerError = bidiCommands.onBidiServerError;
86
+ cleanupBidiSockets = bidiCommands.cleanupBidiSockets;
87
+ configureGlobalFeatures = insecureFeatures.configureGlobalFeatures;
88
+ configureDriverFeatures = insecureFeatures.configureDriverFeatures;
89
+ listCommands = inspectorCommands.listCommands;
90
+ listExtensions = inspectorCommands.listExtensions;
91
+ _isShuttingDown = false;
117
92
  /**
118
- * @param {import('@appium/types').DriverOpts<AppiumDriverConstraints>} opts
93
+ * @param opts - CLI/server options (address, port, security, default capabilities, etc.)
119
94
  */
120
95
  constructor(opts) {
121
96
  // It is necessary to set `--tmp` here since it should be set to
@@ -127,42 +102,36 @@ class AppiumDriver extends base_driver_1.DriverCore {
127
102
  }
128
103
  super(opts);
129
104
  this.args = { ...opts };
130
- this.sessions = {};
131
- this.pendingDrivers = {};
132
- this.newCommandTimeoutMs = 0;
133
- this.pluginClasses = new Map();
134
- this.sessionPlugins = {};
135
- this.sessionlessPlugins = [];
136
- this.bidiSockets = {};
137
- this.bidiProxyClients = {};
138
- this.desiredCapConstraints = desiredCapabilityConstraints;
139
- this._isShuttingDown = false;
140
105
  // allow this to happen in the background, so no `await`
141
- (async () => {
106
+ void (async () => {
142
107
  try {
143
- await (0, config_1.updateBuildInfo)();
108
+ await (0, build_1.updateBuildInfo)();
144
109
  }
145
110
  catch (e) {
146
111
  // make sure we catch any possible errors to avoid unhandled rejections
147
- this.log.debug(`Cannot fetch Appium build info: ${e.message}`);
112
+ const msg = e instanceof Error ? e.message : String(e);
113
+ this.log.debug(`Cannot fetch Appium build info: ${msg}`);
148
114
  }
149
115
  })();
150
116
  }
151
- /**
152
- * Cancel commands queueing for the umbrella Appium driver
153
- */
117
+ /** The umbrella driver does not queue commands; inner session drivers may. */
154
118
  get isCommandsQueueEnabled() {
155
119
  return false;
156
120
  }
121
+ /** Whether a non-null session id is registered on this server. */
157
122
  sessionExists(sessionId) {
158
123
  const dstSession = this.sessions[sessionId];
159
- return dstSession && dstSession.sessionId !== null;
124
+ return Boolean(dstSession && dstSession.sessionId !== null);
160
125
  }
126
+ /** Active automation driver for the session, or `null` if unknown. */
161
127
  driverForSession(sessionId) {
162
- return this.sessions[sessionId];
128
+ return this.sessions[sessionId] ?? null;
163
129
  }
130
+ /**
131
+ * WebDriver status payload (readiness message and build metadata).
132
+ * @see https://www.w3.org/TR/webdriver/#dfn-status
133
+ */
164
134
  async getStatus() {
165
- // https://www.w3.org/TR/webdriver/#dfn-status
166
135
  const statusObj = this._isShuttingDown
167
136
  ? {
168
137
  ready: false,
@@ -174,36 +143,34 @@ class AppiumDriver extends base_driver_1.DriverCore {
174
143
  };
175
144
  return {
176
145
  ...statusObj,
177
- build: lodash_1.default.clone((0, config_1.getBuildInfo)()),
146
+ build: lodash_1.default.clone((0, build_1.getBuildInfo)()),
178
147
  };
179
148
  }
180
- /**
181
- * @param {string|null} reason An optional shutdown reason
182
- */
149
+ /** Marks the server as shutting down and ends all sessions (forced unexpected shutdown). */
183
150
  async shutdown(reason = null) {
184
151
  this._isShuttingDown = true;
185
152
  await this.deleteAllSessions({
186
153
  force: true,
187
- reason,
154
+ reason: reason ?? undefined,
188
155
  });
189
156
  }
190
157
  /**
191
158
  * Retrieve information about all active sessions.
192
159
  * Results are returned only if the `session_discovery` insecure feature is enabled.
193
- * @returns {Promise<import('@appium/types').TimestampedMultiSessionData[]>}
194
160
  */
195
161
  async getAppiumSessions() {
196
162
  this.assertFeatureEnabled(constants_1.SESSION_DISCOVERY_FEATURE);
197
163
  return lodash_1.default.toPairs(this.sessions).map(([id, driver]) => ({
198
164
  id,
199
165
  created: driver.sessionCreationTimestampMs,
200
- capabilities: /** @type {import('@appium/types').DriverCaps<any>} */ (driver.caps),
166
+ capabilities: driver.caps,
201
167
  }));
202
168
  }
169
+ /** Logs BaseDriver version lines when starting a new inner-driver session. */
203
170
  printNewSessionAnnouncement(driverName, driverVersion, driverBaseVersion) {
204
171
  this.log.info(driverVersion
205
- ? `Appium v${config_1.APPIUM_VER} creating new ${driverName} (v${driverVersion}) session`
206
- : `Appium v${config_1.APPIUM_VER} creating new ${driverName} session`);
172
+ ? `Appium v${build_1.APPIUM_VER} creating new ${driverName} (v${driverVersion}) session`
173
+ : `Appium v${build_1.APPIUM_VER} creating new ${driverName} session`);
207
174
  this.log.info(`Checking BaseDriver versions for Appium and ${driverName}`);
208
175
  this.log.info(AppiumDriver.baseVersion
209
176
  ? `Appium's BaseDriver version is ${AppiumDriver.baseVersion}`
@@ -213,82 +180,64 @@ class AppiumDriver extends base_driver_1.DriverCore {
213
180
  : `Could not determine ${driverName}'s BaseDriver version`);
214
181
  }
215
182
  /**
216
- * Retrieves all CLI arguments for a specific plugin.
217
- * @param {string} extName - Plugin name
218
- * @returns {StringRecord} Arguments object. If none, an empty object.
183
+ * CLI arguments object for a plugin (from server config). Empty object if none were passed.
219
184
  */
220
185
  getCliArgsForPlugin(extName) {
221
- return /** @type {StringRecord} */ (this.args.plugin?.[extName] ?? {});
186
+ return (this.args.plugin?.[extName] ?? {});
222
187
  }
223
188
  /**
224
- * Retrieves CLI args for a specific driver.
225
- *
226
- * _Any arg which is equal to its default value will not be present in the returned object._
227
- *
228
- * _Note that this behavior currently (May 18 2022) differs from how plugins are handled_ (see {@linkcode AppiumDriver.getCliArgsForPlugin}).
229
- * @param {string} extName - Driver name
230
- * @returns {StringRecord|undefined} Arguments object. If none, `undefined`
189
+ * CLI arguments for a driver, omitting keys that match schema defaults (unlike
190
+ * {@link AppiumDriver.getCliArgsForPlugin}, which returns defaults explicitly). `undefined` if
191
+ * there is nothing to pass after omitting defaults.
231
192
  */
232
193
  getCliArgsForDriver(extName) {
233
- const allCliArgsForExt = /** @type {StringRecord|undefined} */ (this.args.driver?.[extName]);
234
- if (!lodash_1.default.isEmpty(allCliArgsForExt)) {
235
- const defaults = (0, schema_1.getDefaultsForExtension)(constants_1.DRIVER_TYPE, extName);
236
- const cliArgs = lodash_1.default.isEmpty(defaults)
237
- ? allCliArgsForExt
238
- : lodash_1.default.omitBy(allCliArgsForExt, (value, key) => lodash_1.default.isEqual(defaults[key], value));
239
- if (!lodash_1.default.isEmpty(cliArgs)) {
240
- return cliArgs;
241
- }
194
+ const allCliArgsForExt = this.args.driver?.[extName];
195
+ if (lodash_1.default.isEmpty(allCliArgsForExt)) {
196
+ return undefined;
242
197
  }
198
+ const defaults = (0, schema_1.getDefaultsForExtension)(constants_1.DRIVER_TYPE, extName);
199
+ const cliArgs = lodash_1.default.isEmpty(defaults)
200
+ ? allCliArgsForExt
201
+ : lodash_1.default.omitBy(allCliArgsForExt, (value, key) => lodash_1.default.isEqual(defaults[key], value));
202
+ return lodash_1.default.isEmpty(cliArgs) ? undefined : cliArgs;
243
203
  }
244
204
  /**
245
- * Create a new session
246
- *
247
- * @param {W3CAppiumDriverCaps} w3cCapabilities1 W3C capabilities
248
- * @param {W3CAppiumDriverCaps} [w3cCapabilities2] W3C capabilities (legacy)
249
- * @param {W3CAppiumDriverCaps} [w3cCapabilities3] W3C capabilities (legacy)
250
- * @returns {Promise<SessionHandlerCreateResult>}
205
+ * Creates a session: picks an inner driver from caps, runs plugin hooks, and returns a protocol
206
+ * envelope with either `[sessionId, caps, protocol]` or an error. Legacy call sites may pass the
207
+ * same W3C caps in up to three positions; the first W3C-shaped value wins.
251
208
  */
252
209
  async createSession(w3cCapabilities1, w3cCapabilities2, w3cCapabilities3) {
253
210
  const defaultCapabilities = lodash_1.default.cloneDeep(this.args.defaultCapabilities);
254
- const defaultSettings = (0, utils_1.pullSettings)(defaultCapabilities);
211
+ const defaultSettings = (0, capability_1.pullSettings)((defaultCapabilities ?? {}));
255
212
  const w3cCapabilities = lodash_1.default.cloneDeep([w3cCapabilities3, w3cCapabilities2, w3cCapabilities1].find(base_driver_1.isW3cCaps));
256
213
  if (!w3cCapabilities) {
257
- throw (0, utils_1.makeNonW3cCapsError)();
214
+ throw (0, capability_1.makeNonW3cCapsError)();
258
215
  }
259
216
  const w3cSettings = {
260
217
  ...defaultSettings,
261
- ...(0, utils_1.pullSettings)(w3cCapabilities.alwaysMatch ?? {}),
218
+ ...(0, capability_1.pullSettings)(w3cCapabilities.alwaysMatch ?? {}),
262
219
  };
263
220
  for (const firstMatchEntry of w3cCapabilities.firstMatch ?? []) {
264
- Object.assign(w3cSettings, (0, utils_1.pullSettings)(firstMatchEntry));
221
+ Object.assign(w3cSettings, (0, capability_1.pullSettings)(firstMatchEntry));
265
222
  }
266
223
  const protocol = base_driver_1.PROTOCOLS.W3C;
267
- let innerSessionId, dCaps;
224
+ let innerSessionId;
225
+ let dCaps;
268
226
  try {
269
227
  // Parse the caps into a format that the InnerDriver will accept
270
- const parsedCaps = (0, utils_1.parseCapsForInnerDriver)((0, base_driver_1.promoteAppiumOptions)(/** @type {W3CAppiumDriverCaps} */ (w3cCapabilities)), this.desiredCapConstraints, defaultCapabilities ? (0, base_driver_1.promoteAppiumOptionsForObject)(defaultCapabilities) : undefined);
271
- const { desiredCaps, processedW3CCapabilities } =
272
- /** @type {import('./utils').ParsedDriverCaps<AppiumDriverConstraints>} */ (parsedCaps);
273
- const error = /** @type {import('./utils').InvalidCaps<AppiumDriverConstraints>} */ (parsedCaps).error;
274
- // If the parsing of the caps produced an error, throw it in here
275
- if (error) {
276
- throw error;
228
+ const parsedCaps = (0, capability_1.parseCapsForInnerDriver)((0, base_driver_1.promoteAppiumOptions)(w3cCapabilities), this.desiredCapConstraints, defaultCapabilities ? (0, base_driver_1.promoteAppiumOptionsForObject)(defaultCapabilities) : undefined);
229
+ if ('error' in parsedCaps && parsedCaps.error) {
230
+ throw parsedCaps.error;
277
231
  }
232
+ const { desiredCaps, processedW3CCapabilities } = parsedCaps;
278
233
  const { driver: InnerDriver, version: driverVersion, driverName, } = await this.driverConfig.findMatchingDriver(desiredCaps);
279
234
  this.printNewSessionAnnouncement(InnerDriver.name, driverVersion, InnerDriver.baseVersion);
280
235
  if (this.args.sessionOverride) {
281
236
  await this.deleteAllSessions();
282
237
  }
283
- /**
284
- * @type {DriverData[]}
285
- */
286
238
  let runningDriversData = [];
287
- /**
288
- * @type {DriverData[]}
289
- */
290
239
  let otherPendingDriversData = [];
291
- const driverInstance = /** @type {ExternalDriver} */ (new InnerDriver(this.args, true));
240
+ const driverInstance = new InnerDriver(this.args, true);
292
241
  this.configureDriverFeatures(driverInstance, driverName);
293
242
  // We also want to assign any new Bidi Commands that the driver has specified, including all
294
243
  // the standard bidi commands. But add a method existence guard since some old driver class
@@ -313,22 +262,18 @@ class AppiumDriver extends base_driver_1.DriverCore {
313
262
  runningDriversData = (await this.curSessionDataForDriver(InnerDriver)) ?? [];
314
263
  }
315
264
  catch (e) {
316
- throw new base_driver_1.errors.SessionNotCreatedError(e.message);
265
+ const msg = e instanceof Error ? e.message : String(e);
266
+ throw new base_driver_1.errors.SessionNotCreatedError(msg);
317
267
  }
318
- await pendingDriversGuard.acquire(AppiumDriver.name, () => {
319
- this.pendingDrivers[InnerDriver.name] = this.pendingDrivers[InnerDriver.name] || [];
320
- otherPendingDriversData = lodash_1.default.compact(this.pendingDrivers[InnerDriver.name].map((drv) => drv.driverData));
321
- this.pendingDrivers[InnerDriver.name].push(driverInstance);
322
- });
268
+ this.pendingDrivers[InnerDriver.name] = this.pendingDrivers[InnerDriver.name] || [];
269
+ otherPendingDriversData = lodash_1.default.compact(this.pendingDrivers[InnerDriver.name].map((drv) => drv.driverData));
270
+ this.pendingDrivers[InnerDriver.name].push(driverInstance);
323
271
  try {
324
- [innerSessionId, dCaps] = await driverInstance.createSession(
325
- /** @type {any} */ (processedW3CCapabilities), processedW3CCapabilities, processedW3CCapabilities, [...runningDriversData, ...otherPendingDriversData]);
272
+ [innerSessionId, dCaps] = (await driverInstance.createSession(processedW3CCapabilities, processedW3CCapabilities, processedW3CCapabilities, [...runningDriversData, ...otherPendingDriversData]));
326
273
  this.sessions[innerSessionId] = driverInstance;
327
274
  }
328
275
  finally {
329
- await pendingDriversGuard.acquire(AppiumDriver.name, () => {
330
- lodash_1.default.pull(this.pendingDrivers[InnerDriver.name], driverInstance);
331
- });
276
+ lodash_1.default.pull(this.pendingDrivers[InnerDriver.name], driverInstance);
332
277
  }
333
278
  this.attachUnexpectedShutdownHandler(driverInstance, innerSessionId);
334
279
  this.log.info(`New ${InnerDriver.name} session created successfully, session ` +
@@ -360,7 +305,7 @@ class AppiumDriver extends base_driver_1.DriverCore {
360
305
  catch (error) {
361
306
  return {
362
307
  protocol,
363
- error,
308
+ error: error instanceof Error ? error : new Error(String(error)),
364
309
  };
365
310
  }
366
311
  return {
@@ -369,19 +314,18 @@ class AppiumDriver extends base_driver_1.DriverCore {
369
314
  };
370
315
  }
371
316
  /**
372
- *
373
- * @param {ExternalDriver} driver
374
- * @param {string} innerSessionId
317
+ * Subscribes to the inner driver’s unexpected-shutdown hook so Appium can drop the session and
318
+ * notify plugins.
375
319
  */
376
320
  attachUnexpectedShutdownHandler(driver, innerSessionId) {
377
- const onShutdown = (cause = new Error('Unknown error')) => {
321
+ const onShutdown = async (cause = new Error('Unknown error')) => {
378
322
  this.log.warn(`Ending session, cause was '${cause.message}'`);
379
323
  if (this.sessionPlugins[innerSessionId]) {
380
324
  for (const plugin of this.sessionPlugins[innerSessionId]) {
381
325
  if (lodash_1.default.isFunction(plugin.onUnexpectedShutdown)) {
382
326
  this.log.debug(`Plugin ${plugin.name} defines an unexpected shutdown handler; calling it now`);
383
327
  try {
384
- plugin.onUnexpectedShutdown(driver, cause);
328
+ await plugin.onUnexpectedShutdown(driver, cause);
385
329
  }
386
330
  catch (e) {
387
331
  this.log.warn(`Got an error when running plugin ${plugin.name} shutdown handler: ${e}`);
@@ -405,11 +349,9 @@ class AppiumDriver extends base_driver_1.DriverCore {
405
349
  }
406
350
  }
407
351
  /**
408
- *
409
- * @param {((...args: any[]) => any)|(new(...args: any[]) => any)} InnerDriver
410
- * @returns {Promise<DriverData[]>}}
411
- * @privateRemarks The _intent_ is that `InnerDriver` is the class of a driver, but it only really
412
- * needs to be a function or constructor.
352
+ * Collects `driverData` for every active session whose driver class matches `InnerDriver.name`
353
+ * (used when creating another session of the same driver type).
354
+ * @remarks `InnerDriver` is expected to be the driver class; only `.name` is read.
413
355
  */
414
356
  async curSessionDataForDriver(InnerDriver) {
415
357
  const data = lodash_1.default.compact(lodash_1.default.values(this.sessions)
@@ -424,21 +366,20 @@ class AppiumDriver extends base_driver_1.DriverCore {
424
366
  return data;
425
367
  }
426
368
  /**
427
- * @param {string} sessionId
369
+ * Ends one session: removes it from the master list immediately, then delegates to the inner
370
+ * driver’s `deleteSession` with sibling-session metadata.
428
371
  */
429
372
  async deleteSession(sessionId) {
430
373
  let protocol;
431
374
  try {
432
375
  let otherSessionsData;
433
- const dstSession = await sessionsListGuard.acquire(AppiumDriver.name, () => {
434
- if (!this.sessions[sessionId]) {
435
- return;
436
- }
376
+ let dstSession;
377
+ if (this.sessions[sessionId]) {
437
378
  const curConstructorName = this.sessions[sessionId].constructor.name;
438
379
  otherSessionsData = lodash_1.default.toPairs(this.sessions)
439
380
  .filter(([key, value]) => value.constructor.name === curConstructorName && key !== sessionId)
440
381
  .map(([, value]) => value.driverData);
441
- const dstSession = this.sessions[sessionId];
382
+ dstSession = this.sessions[sessionId];
442
383
  protocol = dstSession.protocol;
443
384
  this.log.info(`Removing session ${sessionId} from our master session list`);
444
385
  // regardless of whether the deleteSession completes successfully or not
@@ -447,8 +388,7 @@ class AppiumDriver extends base_driver_1.DriverCore {
447
388
  delete this.sessions[sessionId];
448
389
  delete this.sessionPlugins[sessionId];
449
390
  this.cleanupBidiSockets(sessionId);
450
- return dstSession;
451
- });
391
+ }
452
392
  // this may not be correct, but if `dstSession` was falsy, the call to `deleteSession()` would
453
393
  // throw anyway.
454
394
  if (!dstSession) {
@@ -460,13 +400,18 @@ class AppiumDriver extends base_driver_1.DriverCore {
460
400
  };
461
401
  }
462
402
  catch (e) {
463
- this.log.error(`Had trouble ending session ${sessionId}: ${e.message}`);
403
+ const msg = e instanceof Error ? e.message : String(e);
404
+ this.log.error(`Had trouble ending session ${sessionId}: ${msg}`);
464
405
  return {
465
406
  protocol,
466
- error: e,
407
+ error: e instanceof Error ? e : new Error(msg),
467
408
  };
468
409
  }
469
410
  }
411
+ /**
412
+ * Ends every active session, either by normal `deleteSession` or by `startUnexpectedShutdown`
413
+ * when `force` is true.
414
+ */
470
415
  async deleteAllSessions(opts = {}) {
471
416
  const sessionsCount = lodash_1.default.size(this.sessions);
472
417
  if (0 === sessionsCount) {
@@ -476,7 +421,7 @@ class AppiumDriver extends base_driver_1.DriverCore {
476
421
  const { force = false, reason } = opts;
477
422
  this.log.debug(`Cleaning up ${support_1.util.pluralize('active session', sessionsCount, true)}`);
478
423
  const cleanupPromises = force
479
- ? lodash_1.default.values(this.sessions).map((drv) => drv.startUnexpectedShutdown(reason && new Error(reason)))
424
+ ? lodash_1.default.values(this.sessions).map((drv) => drv.startUnexpectedShutdown(reason ? new Error(reason) : undefined))
480
425
  : lodash_1.default.keys(this.sessions).map((id) => this.deleteSession(id));
481
426
  for (const cleanupPromise of cleanupPromises) {
482
427
  try {
@@ -488,16 +433,20 @@ class AppiumDriver extends base_driver_1.DriverCore {
488
433
  }
489
434
  }
490
435
  /**
491
- * Get the appropriate plugins for a session (or sessionless plugins)
492
- *
493
- * @param {string|null} [sessionId=null] - the sessionId (or null) to use to find plugins
494
- * @returns {Array<import('@appium/types').Plugin>} - array of plugin instances
436
+ * Plugin instances for a session id, or the shared sessionless list when `sessionId` is null.
437
+ * Lazily builds sessionless instances on first use.
495
438
  */
496
439
  pluginsForSession(sessionId = null) {
497
440
  if (sessionId) {
441
+ const existingPlugins = this.sessionPlugins[sessionId];
442
+ if (existingPlugins) {
443
+ return existingPlugins;
444
+ }
498
445
  const driver = this.sessions[sessionId];
499
- return this.sessionPlugins[sessionId]
500
- ?? (driver ? this.createPluginInstances((0, base_driver_1.generateDriverLogPrefix)(driver)) : []);
446
+ if (!driver) {
447
+ return [];
448
+ }
449
+ return (this.sessionPlugins[sessionId] = this.createPluginInstances((0, base_driver_1.generateDriverLogPrefix)(driver)));
501
450
  }
502
451
  if (lodash_1.default.isEmpty(this.sessionlessPlugins)) {
503
452
  this.sessionlessPlugins = this.createPluginInstances();
@@ -505,14 +454,8 @@ class AppiumDriver extends base_driver_1.DriverCore {
505
454
  return this.sessionlessPlugins;
506
455
  }
507
456
  /**
508
- * To get plugins for a command, we either get the plugin instances associated with the
509
- * particular command's session, or in the case of sessionless plugins, pull from the set of
510
- * plugin instances reserved for sessionless commands (and we lazily create plugin instances on
511
- * first use)
512
- *
513
- * @param {string} cmd - the name of the command to find a plugin to handle
514
- * @param {?string} sessionId - the particular session for which to find a plugin, or null if
515
- * sessionless
457
+ * Plugins that declare a method named `cmd` or a generic `handle` method, scoped to the given
458
+ * session (or sessionless when `sessionId` is null).
516
459
  */
517
460
  pluginsToHandleCmd(cmd, sessionId = null) {
518
461
  // to handle a given command, a plugin should either implement that command as a plugin
@@ -520,29 +463,25 @@ class AppiumDriver extends base_driver_1.DriverCore {
520
463
  return this.pluginsForSession(sessionId).filter((p) => lodash_1.default.isFunction(p[cmd]) || lodash_1.default.isFunction(p.handle));
521
464
  }
522
465
  /**
523
- * Creates instances of all of the enabled Plugin classes
524
- * @param {string|null} [driverId=null] - ID to use for linking a driver to a plugin in logs
525
- * @returns {Plugin[]}
466
+ * One instance per registered plugin class. `driverId` becomes the plugin log prefix segment
467
+ * when tied to a session driver; use `null` for sessionless plugins.
526
468
  */
527
469
  createPluginInstances(driverId = null) {
528
- /** @type {Plugin[]} */
529
470
  const pluginInstances = [];
530
471
  for (const [PluginClass, name] of this.pluginClasses.entries()) {
531
472
  const cliArgs = this.getCliArgsForPlugin(name);
532
473
  const plugin = new PluginClass(name, cliArgs, driverId);
533
- if (lodash_1.default.isFunction(/** @type {Plugin & ExtensionCore} */ (plugin).updateBidiCommands)) {
534
- // some old plugin classes don't have `updateBidiCommands`
535
- /** @type {Plugin & ExtensionCore} */ (plugin).updateBidiCommands(PluginClass.newBidiCommands ?? {});
474
+ const extPlugin = plugin;
475
+ if (lodash_1.default.isFunction(extPlugin.updateBidiCommands)) {
476
+ extPlugin.updateBidiCommands(PluginClass.newBidiCommands ?? {});
536
477
  }
537
478
  pluginInstances.push(plugin);
538
479
  }
539
480
  return pluginInstances;
540
481
  }
541
482
  /**
542
- *
543
- * @param {string} cmd
544
- * @param {...any} args
545
- * @returns {Promise<{value: any, error?: Error, protocol: string} | import('type-fest').AsyncReturnType<ExternalDriver['executeCommand']>>}
483
+ * Dispatches a WebDriver/Appium command: may run on this driver, a session’s inner driver, or
484
+ * through a plugin chain, and normalizes the return value into a protocol-shaped result.
546
485
  */
547
486
  async executeCommand(cmd, ...args) {
548
487
  // We have basically three cases for how to handle commands:
@@ -568,12 +507,11 @@ class AppiumDriver extends base_driver_1.DriverCore {
568
507
  let sessionId = null;
569
508
  let dstSession = null;
570
509
  let protocol = null;
571
- /** @type {this | ExternalDriver} */
572
510
  // eslint-disable-next-line @typescript-eslint/no-this-alias
573
511
  let driver = this;
574
512
  if (isSessionCmd) {
575
513
  sessionId = lodash_1.default.last(args);
576
- dstSession = this.sessions[sessionId];
514
+ dstSession = this.sessions[sessionId] ?? null;
577
515
  if (!dstSession) {
578
516
  throw new Error(`The session with id '${sessionId}' does not exist`);
579
517
  }
@@ -630,8 +568,10 @@ class AppiumDriver extends base_driver_1.DriverCore {
630
568
  // not the platform driver
631
569
  return await base_driver_1.BaseDriver.prototype.executeCommand.call(this, cmd, ...args);
632
570
  }
633
- // here we know that we are executing a session command, and have a valid session driver
634
- return await /** @type {any} */ (dstSession).executeCommand(cmd, ...args);
571
+ if (!dstSession) {
572
+ throw new Error('Internal error: session command without a session driver');
573
+ }
574
+ return await dstSession.executeCommand(cmd, ...args);
635
575
  };
636
576
  // now take our default behavior, wrap it with any number of plugin behaviors, and run it
637
577
  const wrappedCmd = this.wrapCommandWithPlugins({
@@ -661,21 +601,22 @@ class AppiumDriver extends base_driver_1.DriverCore {
661
601
  // previously sessionless to use the new sessionId, so that plugins can share state between
662
602
  // their createSession method and other instance methods
663
603
  if (cmd === base_driver_1.CREATE_SESSION_COMMAND && this.sessionlessPlugins.length && !res.error) {
664
- const sessionId = lodash_1.default.first(res.value);
604
+ const newSessionId = lodash_1.default.first(res.value);
665
605
  this.log.info(`Promoting ${this.sessionlessPlugins.length} sessionless plugins to be attached ` +
666
- `to session ID ${sessionId}`);
667
- this.sessionPlugins[sessionId] = this.sessionlessPlugins;
668
- for (const p of /** @type {(Plugin & ExtensionCore)[]} */ (this.sessionPlugins[sessionId])) {
606
+ `to session ID ${newSessionId}`);
607
+ this.sessionPlugins[newSessionId] = this.sessionlessPlugins;
608
+ const promoted = this.sessionPlugins[newSessionId];
609
+ for (const p of promoted) {
669
610
  if (lodash_1.default.isFunction(p.updateLogPrefix)) {
670
- // some old plugin classes don't have `updateLogPrefix` yet
671
- p.updateLogPrefix(`${(0, base_driver_1.generateDriverLogPrefix)(p)} <${(0, base_driver_1.generateDriverLogPrefix)(this.sessions[sessionId])}>`);
611
+ p.updateLogPrefix(`${(0, base_driver_1.generateDriverLogPrefix)(p)} <${(0, base_driver_1.generateDriverLogPrefix)(this.sessions[newSessionId])}>`);
672
612
  }
673
613
  }
674
614
  this.sessionlessPlugins = [];
675
615
  }
676
616
  return res;
677
617
  }
678
- wrapCommandWithPlugins({ driver, cmd, args, next, cmdHandledBy, plugins }) {
618
+ /** Builds an async chain: each plugin wraps `next` until the default driver behavior runs. */
619
+ wrapCommandWithPlugins({ driver, cmd, args, next, cmdHandledBy, plugins, }) {
679
620
  if (plugins.length) {
680
621
  this.log.info(`Plugins which can handle cmd '${cmd}': ${plugins.map((p) => p.name)}`);
681
622
  }
@@ -690,15 +631,20 @@ class AppiumDriver extends base_driver_1.DriverCore {
690
631
  this.log.info(`Plugin ${plugin.name} is now handling cmd '${cmd}'`);
691
632
  cmdHandledBy[plugin.name] = true; // if we make it here, this plugin has attempted to handle cmd
692
633
  // first attempt to handle the command via a command-specific handler on the plugin
693
- if (plugin[cmd]) {
694
- return await plugin[cmd](_next, driver, ...args);
634
+ const cmdHandler = plugin[cmd];
635
+ if (lodash_1.default.isFunction(cmdHandler)) {
636
+ // Command methods must run with plugin as `this` (detached property access drops binding).
637
+ return await cmdHandler.call(plugin, _next, driver, ...args);
638
+ }
639
+ if (!lodash_1.default.isFunction(plugin.handle)) {
640
+ throw new Error(`Plugin ${plugin.name} cannot handle command '${cmd}'`);
695
641
  }
696
- // otherwise, call the generic 'handle' method
697
642
  return await plugin.handle(_next, driver, cmd, ...args);
698
643
  })(next);
699
644
  }
700
645
  return next;
701
646
  }
647
+ /** After a command with plugins, logs which handlers ran vs. were skipped (debugging aid). */
702
648
  logPluginHandlerReport(plugins, { cmd, cmdHandledBy }) {
703
649
  if (!plugins.length) {
704
650
  return;
@@ -717,8 +663,11 @@ class AppiumDriver extends base_driver_1.DriverCore {
717
663
  `command *was* handled by these: ${JSON.stringify(didHandle)}.`);
718
664
  }
719
665
  }
720
- async executeWrappedCommand({ wrappedCmd, protocol }) {
721
- let cmdRes, cmdErr, res = {};
666
+ /** Runs the wrapped plugin chain and merges the result into a `SessionHandlerResult` shape. */
667
+ async executeWrappedCommand({ wrappedCmd, protocol, }) {
668
+ let cmdRes;
669
+ let cmdErr;
670
+ const res = {};
722
671
  try {
723
672
  // At this point, `wrappedCmd` defines a whole sequence of plugin handlers, culminating in
724
673
  // our default handler. Whatever it returns is what we're going to want to send back to the
@@ -732,64 +681,37 @@ class AppiumDriver extends base_driver_1.DriverCore {
732
681
  // object, or a protocol-aware object with protocol and error/value keys. So we need to sniff
733
682
  // it and make sure we don't double-wrap it if it's the latter kind.
734
683
  if (lodash_1.default.isPlainObject(cmdRes) && lodash_1.default.has(cmdRes, 'protocol')) {
735
- res = cmdRes;
684
+ Object.assign(res, cmdRes);
736
685
  }
737
686
  else {
738
687
  res.value = cmdRes;
739
- res.error = cmdErr;
740
- res.protocol = protocol;
688
+ res.error = cmdErr instanceof Error ? cmdErr : undefined;
689
+ res.protocol = protocol ?? undefined;
741
690
  }
742
691
  return res;
743
692
  }
693
+ /** Whether the inner session driver is actively proxying for this session id. */
744
694
  proxyActive(sessionId) {
745
695
  const dstSession = this.sessions[sessionId];
746
- return dstSession && lodash_1.default.isFunction(dstSession.proxyActive) && dstSession.proxyActive(sessionId);
696
+ return lodash_1.default.isFunction(dstSession?.proxyActive) && dstSession.proxyActive(sessionId);
747
697
  }
748
- /**
749
- *
750
- * @param {string} sessionId
751
- * @returns {import('@appium/types').RouteMatcher[]}
752
- */
698
+ /** URL patterns the session driver does not want proxied; empty if no session or no list. */
753
699
  getProxyAvoidList(sessionId) {
754
700
  const dstSession = this.sessions[sessionId];
755
- return dstSession ? dstSession.getProxyAvoidList() : [];
701
+ return lodash_1.default.isFunction(dstSession?.getProxyAvoidList) ? dstSession.getProxyAvoidList() : [];
756
702
  }
703
+ /** Whether the session driver supports proxying for this session. */
757
704
  canProxy(sessionId) {
758
705
  const dstSession = this.sessions[sessionId];
759
- return dstSession && dstSession.canProxy(sessionId);
706
+ return lodash_1.default.isFunction(dstSession?.canProxy) && dstSession.canProxy(sessionId);
760
707
  }
761
- onBidiConnection = bidiCommands.onBidiConnection;
762
- onBidiMessage = bidiCommands.onBidiMessage;
763
- onBidiServerError = bidiCommands.onBidiServerError;
764
- cleanupBidiSockets = bidiCommands.cleanupBidiSockets;
765
- configureGlobalFeatures = insecureFeatures.configureGlobalFeatures;
766
- configureDriverFeatures = insecureFeatures.configureDriverFeatures;
767
- listCommands = inspectorCommands.listCommands;
768
- listExtensions = inspectorCommands.listExtensions;
769
708
  }
770
709
  exports.AppiumDriver = AppiumDriver;
771
- /**
772
- * Help decide which commands should be proxied to sub-drivers and which
773
- * should be handled by this, our umbrella driver
774
- * @param {string} cmd
775
- * @returns {boolean}
776
- */
777
- function isAppiumDriverCommand(cmd) {
778
- return !(0, base_driver_1.isSessionCommand)(cmd)
779
- || lodash_1.default.includes([
780
- base_driver_1.DELETE_SESSION_COMMAND,
781
- base_driver_1.LIST_DRIVER_COMMANDS_COMMAND,
782
- base_driver_1.LIST_DRIVER_EXTENSIONS_COMMAND,
783
- ], cmd);
784
- }
785
710
  /**
786
711
  * Thrown when Appium tried to proxy a command using a driver's `proxyCommand` method but the
787
712
  * method did not exist
788
713
  */
789
714
  class NoDriverProxyCommandError extends Error {
790
- /**
791
- * @type {Readonly<string>}
792
- */
793
715
  code = 'APPIUMERR_NO_DRIVER_PROXYCOMMAND';
794
716
  constructor() {
795
717
  super(`The default behavior for this command was to proxy, but the driver ` +
@@ -799,48 +721,13 @@ class NoDriverProxyCommandError extends Error {
799
721
  }
800
722
  }
801
723
  exports.NoDriverProxyCommandError = NoDriverProxyCommandError;
802
- /**
803
- * @typedef {import('@appium/types').DriverData} DriverData
804
- * @typedef {import('@appium/types').ServerArgs} DriverOpts
805
- * @typedef {import('@appium/types').Constraints} Constraints
806
- * @typedef {import('@appium/types').AppiumServer} AppiumServer
807
- * @typedef {import('@appium/types').ExtensionType} ExtensionType
808
- * @typedef {import('./extension/driver-config').DriverConfig} DriverConfig
809
- * @typedef {import('@appium/types').PluginType} PluginType
810
- * @typedef {import('@appium/types').DriverType} DriverType
811
- * @typedef {import('@appium/types').StringRecord} StringRecord
812
- * @typedef {import('@appium/types').ExternalDriver} ExternalDriver
813
- * @typedef {import('@appium/types').PluginClass} PluginClass
814
- * @typedef {import('@appium/types').Plugin} Plugin
815
- * @typedef {import('@appium/base-driver').ExtensionCore} ExtensionCore
816
- * @typedef {import('@appium/types').DriverClass<import('@appium/types').Driver>} DriverClass
817
- */
818
- /**
819
- * @typedef {import('@appium/types').ISessionHandler<AppiumDriverConstraints,
820
- * SessionHandlerCreateResult, SessionHandlerDeleteResult>} AppiumSessionHandler
821
- */
822
- /**
823
- * @typedef {SessionHandlerResult<[innerSessionId: string, caps:
824
- * import('@appium/types').DriverCaps<Constraints>, protocol: string|undefined]>} SessionHandlerCreateResult
825
- */
826
- /**
827
- * @template {Constraints} C
828
- * @typedef {import('@appium/types').Core<C>} Core
829
- */
830
- /**
831
- * @typedef {SessionHandlerResult<void>} SessionHandlerDeleteResult
832
- */
833
- /**
834
- * Used by {@linkcode AppiumDriver.createSession} and {@linkcode AppiumDriver.deleteSession} to describe
835
- * result.
836
- * @template V
837
- * @typedef SessionHandlerResult
838
- * @property {V} [value]
839
- * @property {Error} [error]
840
- * @property {string} [protocol]
841
- */
842
- /**
843
- * @typedef {typeof desiredCapabilityConstraints} AppiumDriverConstraints
844
- * @typedef {import('@appium/types').W3CDriverCaps<AppiumDriverConstraints>} W3CAppiumDriverCaps
845
- */
724
+ /** True if `cmd` should run on the umbrella driver instead of only on the session’s inner driver. */
725
+ function isAppiumDriverCommand(cmd) {
726
+ return !(0, base_driver_1.isSessionCommand)(cmd)
727
+ || lodash_1.default.includes([
728
+ base_driver_1.DELETE_SESSION_COMMAND,
729
+ base_driver_1.LIST_DRIVER_COMMANDS_COMMAND,
730
+ base_driver_1.LIST_DRIVER_EXTENSIONS_COMMAND,
731
+ ], cmd);
732
+ }
846
733
  //# sourceMappingURL=appium.js.map