@appium/base-driver 10.2.0 → 10.2.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 (127) hide show
  1. package/LICENSE +201 -0
  2. package/build/lib/basedriver/capabilities.js +7 -7
  3. package/build/lib/basedriver/capabilities.js.map +1 -1
  4. package/build/lib/basedriver/commands/event.d.ts +1 -1
  5. package/build/lib/basedriver/commands/event.d.ts.map +1 -1
  6. package/build/lib/basedriver/commands/execute.d.ts +1 -1
  7. package/build/lib/basedriver/commands/execute.d.ts.map +1 -1
  8. package/build/lib/basedriver/commands/find.d.ts +1 -1
  9. package/build/lib/basedriver/commands/find.d.ts.map +1 -1
  10. package/build/lib/basedriver/commands/mixin.d.ts +1 -1
  11. package/build/lib/basedriver/commands/mixin.d.ts.map +1 -1
  12. package/build/lib/basedriver/commands/timeout.d.ts +1 -1
  13. package/build/lib/basedriver/commands/timeout.d.ts.map +1 -1
  14. package/build/lib/basedriver/device-settings.d.ts +14 -23
  15. package/build/lib/basedriver/device-settings.d.ts.map +1 -1
  16. package/build/lib/basedriver/device-settings.js +11 -26
  17. package/build/lib/basedriver/device-settings.js.map +1 -1
  18. package/build/lib/basedriver/helpers.d.ts +36 -57
  19. package/build/lib/basedriver/helpers.d.ts.map +1 -1
  20. package/build/lib/basedriver/helpers.js +148 -239
  21. package/build/lib/basedriver/helpers.js.map +1 -1
  22. package/build/lib/basedriver/logger.d.ts +1 -2
  23. package/build/lib/basedriver/logger.d.ts.map +1 -1
  24. package/build/lib/basedriver/logger.js +2 -2
  25. package/build/lib/basedriver/logger.js.map +1 -1
  26. package/build/lib/basedriver/validation.d.ts.map +1 -1
  27. package/build/lib/basedriver/validation.js +3 -3
  28. package/build/lib/basedriver/validation.js.map +1 -1
  29. package/build/lib/constants.d.ts +1 -1
  30. package/build/lib/constants.d.ts.map +1 -1
  31. package/build/lib/express/crash.d.ts +8 -2
  32. package/build/lib/express/crash.d.ts.map +1 -1
  33. package/build/lib/express/crash.js +6 -0
  34. package/build/lib/express/crash.js.map +1 -1
  35. package/build/lib/express/express-logging.d.ts +12 -2
  36. package/build/lib/express/express-logging.d.ts.map +1 -1
  37. package/build/lib/express/express-logging.js +34 -26
  38. package/build/lib/express/express-logging.js.map +1 -1
  39. package/build/lib/express/idempotency.d.ts +4 -10
  40. package/build/lib/express/idempotency.d.ts.map +1 -1
  41. package/build/lib/express/idempotency.js +69 -73
  42. package/build/lib/express/idempotency.js.map +1 -1
  43. package/build/lib/express/logger.d.ts +1 -2
  44. package/build/lib/express/logger.d.ts.map +1 -1
  45. package/build/lib/express/logger.js +2 -2
  46. package/build/lib/express/logger.js.map +1 -1
  47. package/build/lib/express/middleware.d.ts +37 -41
  48. package/build/lib/express/middleware.d.ts.map +1 -1
  49. package/build/lib/express/middleware.js +48 -60
  50. package/build/lib/express/middleware.js.map +1 -1
  51. package/build/lib/express/server.d.ts +57 -101
  52. package/build/lib/express/server.d.ts.map +1 -1
  53. package/build/lib/express/server.js +51 -128
  54. package/build/lib/express/server.js.map +1 -1
  55. package/build/lib/express/static.d.ts +10 -5
  56. package/build/lib/express/static.d.ts.map +1 -1
  57. package/build/lib/express/static.js +32 -42
  58. package/build/lib/express/static.js.map +1 -1
  59. package/build/lib/express/websocket.d.ts +22 -6
  60. package/build/lib/express/websocket.d.ts.map +1 -1
  61. package/build/lib/express/websocket.js +10 -15
  62. package/build/lib/express/websocket.js.map +1 -1
  63. package/build/lib/helpers/capabilities.d.ts +4 -16
  64. package/build/lib/helpers/capabilities.d.ts.map +1 -1
  65. package/build/lib/helpers/capabilities.js +36 -48
  66. package/build/lib/helpers/capabilities.js.map +1 -1
  67. package/build/lib/jsonwp-status/status.d.ts +113 -158
  68. package/build/lib/jsonwp-status/status.d.ts.map +1 -1
  69. package/build/lib/jsonwp-status/status.js +10 -14
  70. package/build/lib/jsonwp-status/status.js.map +1 -1
  71. package/build/lib/protocol/bidi-commands.d.ts +31 -36
  72. package/build/lib/protocol/bidi-commands.d.ts.map +1 -1
  73. package/build/lib/protocol/bidi-commands.js +5 -5
  74. package/build/lib/protocol/bidi-commands.js.map +1 -1
  75. package/build/lib/protocol/errors.d.ts.map +1 -1
  76. package/build/lib/protocol/helpers.d.ts +7 -11
  77. package/build/lib/protocol/helpers.d.ts.map +1 -1
  78. package/build/lib/protocol/helpers.js +5 -9
  79. package/build/lib/protocol/helpers.js.map +1 -1
  80. package/build/lib/protocol/index.d.ts +4 -21
  81. package/build/lib/protocol/index.d.ts.map +1 -1
  82. package/build/lib/protocol/index.js.map +1 -1
  83. package/build/lib/protocol/protocol.d.ts +15 -1
  84. package/build/lib/protocol/protocol.d.ts.map +1 -1
  85. package/build/lib/protocol/protocol.js +50 -20
  86. package/build/lib/protocol/protocol.js.map +1 -1
  87. package/build/lib/protocol/routes.d.ts +8 -15
  88. package/build/lib/protocol/routes.d.ts.map +1 -1
  89. package/build/lib/protocol/routes.js +18 -33
  90. package/build/lib/protocol/routes.js.map +1 -1
  91. package/lib/basedriver/capabilities.ts +1 -1
  92. package/lib/basedriver/commands/event.ts +2 -2
  93. package/lib/basedriver/commands/execute.ts +2 -2
  94. package/lib/basedriver/commands/find.ts +2 -2
  95. package/lib/basedriver/commands/mixin.ts +1 -1
  96. package/lib/basedriver/commands/timeout.ts +2 -2
  97. package/lib/basedriver/{device-settings.js → device-settings.ts} +24 -35
  98. package/lib/basedriver/{helpers.js → helpers.ts} +208 -266
  99. package/lib/basedriver/logger.ts +3 -0
  100. package/lib/basedriver/validation.ts +2 -2
  101. package/lib/constants.ts +1 -1
  102. package/lib/express/crash.ts +15 -0
  103. package/lib/express/express-logging.ts +84 -0
  104. package/lib/express/{idempotency.js → idempotency.ts} +105 -89
  105. package/lib/express/logger.ts +3 -0
  106. package/lib/express/middleware.ts +187 -0
  107. package/lib/express/{server.js → server.ts} +175 -167
  108. package/lib/express/static.ts +77 -0
  109. package/lib/express/websocket.ts +81 -0
  110. package/lib/helpers/capabilities.ts +83 -0
  111. package/lib/jsonwp-status/{status.js → status.ts} +12 -15
  112. package/lib/protocol/{bidi-commands.js → bidi-commands.ts} +7 -5
  113. package/lib/protocol/errors.ts +1 -1
  114. package/lib/protocol/{helpers.js → helpers.ts} +8 -11
  115. package/lib/protocol/protocol.ts +57 -26
  116. package/lib/protocol/{routes.js → routes.ts} +29 -40
  117. package/package.json +11 -11
  118. package/tsconfig.json +3 -1
  119. package/lib/basedriver/logger.js +0 -4
  120. package/lib/express/crash.js +0 -11
  121. package/lib/express/express-logging.js +0 -60
  122. package/lib/express/logger.js +0 -4
  123. package/lib/express/middleware.js +0 -171
  124. package/lib/express/static.js +0 -76
  125. package/lib/express/websocket.js +0 -79
  126. package/lib/helpers/capabilities.js +0 -93
  127. /package/lib/protocol/{index.js → index.ts} +0 -0
@@ -0,0 +1,83 @@
1
+ import type {Constraints, W3CCapabilities, Capabilities, AppiumLogger} from '@appium/types';
2
+ import _ from 'lodash';
3
+
4
+ /**
5
+ * Determine whether the given argument is valid
6
+ * W3C capabilities instance.
7
+ */
8
+ export function isW3cCaps(caps: unknown): caps is W3CCapabilities<Constraints> {
9
+ if (!_.isPlainObject(caps)) {
10
+ return false;
11
+ }
12
+
13
+ const c = caps as Record<string, unknown>;
14
+ const isFirstMatchValid = () =>
15
+ _.isArray(c.firstMatch) &&
16
+ !_.isEmpty(c.firstMatch) &&
17
+ _.every(c.firstMatch, _.isPlainObject);
18
+ const isAlwaysMatchValid = () => _.isPlainObject(c.alwaysMatch);
19
+ if (_.has(c, 'firstMatch') && _.has(c, 'alwaysMatch')) {
20
+ return isFirstMatchValid() && isAlwaysMatchValid();
21
+ }
22
+ if (_.has(c, 'firstMatch')) {
23
+ return isFirstMatchValid();
24
+ }
25
+ if (_.has(c, 'alwaysMatch')) {
26
+ return isAlwaysMatchValid();
27
+ }
28
+ return false;
29
+ }
30
+
31
+ /**
32
+ * Normalize capability values according to constraints (e.g. string 'true' → boolean).
33
+ */
34
+ export function fixCaps<C extends Constraints>(
35
+ oldCaps: Record<string, unknown>,
36
+ desiredCapConstraints: C,
37
+ log: AppiumLogger
38
+ ): Capabilities<C> {
39
+ const caps = _.clone(oldCaps) as Record<string, unknown>;
40
+
41
+ const logCastWarning = (prefix: string) => log.warn(`${prefix}. This may cause unexpected behavior`);
42
+
43
+ // boolean capabilities can be passed in as strings 'false' and 'true'
44
+ // which we want to translate into boolean values
45
+ const booleanCaps = _.keys(_.pickBy(desiredCapConstraints, (k) => k.isBoolean === true));
46
+ for (const cap of booleanCaps) {
47
+ const value = oldCaps[cap];
48
+ if (!_.isString(value)) {
49
+ continue;
50
+ }
51
+
52
+ if (!['true', 'false'].includes(value.toLowerCase())) {
53
+ logCastWarning(`String capability '${cap}' ('${value}') cannot be converted to a boolean`);
54
+ continue;
55
+ }
56
+
57
+ logCastWarning(`Capability '${cap}' changed from string '${value}' to boolean`);
58
+ caps[cap] = value.toLowerCase() === 'true';
59
+ }
60
+
61
+ // int capabilities are often sent in as strings by frameworks
62
+ const intCaps = _.keys(_.pickBy(desiredCapConstraints, (k) => k.isNumber === true));
63
+ for (const cap of intCaps) {
64
+ const value = oldCaps[cap];
65
+ if (!_.isString(value)) {
66
+ continue;
67
+ }
68
+
69
+ const intValue = parseInt(value as string, 10);
70
+ const floatValue = parseFloat(value as string);
71
+ const newValue = floatValue !== intValue ? floatValue : intValue;
72
+
73
+ if (Number.isNaN(newValue)) {
74
+ logCastWarning(`String capability '${cap}' ('${value}') cannot be converted to a number`);
75
+ continue;
76
+ }
77
+
78
+ logCastWarning(`Capability '${cap}' changed from string '${value}' to number ${newValue}`);
79
+ caps[cap] = newValue;
80
+ }
81
+
82
+ return caps as Capabilities<C>;
83
+ }
@@ -1,6 +1,4 @@
1
- import _ from 'lodash';
2
-
3
- const codes = {
1
+ export const codes = {
4
2
  Success: {
5
3
  code: 0,
6
4
  summary: 'The command executed successfully.',
@@ -112,17 +110,16 @@ const codes = {
112
110
  code: 35,
113
111
  summary: 'No such context found.',
114
112
  },
115
- };
113
+ } as const;
116
114
 
117
- function getSummaryByCode(code) {
118
- code = parseInt(code, 10);
119
- for (let obj of _.values(codes)) {
120
- if (!_.isUndefined(obj.code) && obj.code === code) {
121
- return obj.summary;
122
- }
123
- }
124
- return 'An error occurred';
115
+ /**
116
+ * Returns the summary message for a JSONWP status code.
117
+ *
118
+ * @param code - Numeric status code (e.g. 0, 7) or string representation
119
+ * @returns Human-readable summary for the code, or 'An error occurred' if unknown
120
+ */
121
+ export function getSummaryByCode(code: number | string): string {
122
+ const parsed = parseInt(String(code), 10);
123
+ const match = Object.values(codes).find((obj) => obj.code === parsed);
124
+ return match?.summary ?? 'An error occurred';
125
125
  }
126
-
127
- export default codes;
128
- export {codes, getSummaryByCode};
@@ -1,9 +1,11 @@
1
- const SUBSCRIPTION_REQUEST_PARAMS = /** @type {const} */ ({
1
+ import type {BidiModuleMap} from '@appium/types';
2
+
3
+ const SUBSCRIPTION_REQUEST_PARAMS = {
2
4
  required: ['events'],
3
5
  optional: ['contexts'],
4
- });
6
+ } as const;
5
7
 
6
- export const BIDI_COMMANDS = /** @type {const} */ ({
8
+ export const BIDI_COMMANDS = {
7
9
  session: {
8
10
  subscribe: {
9
11
  command: 'bidiSubscribe',
@@ -16,7 +18,7 @@ export const BIDI_COMMANDS = /** @type {const} */ ({
16
18
  status: {
17
19
  command: 'bidiStatus',
18
20
  params: {},
19
- }
21
+ },
20
22
  },
21
23
  browsingContext: {
22
24
  navigate: {
@@ -27,7 +29,7 @@ export const BIDI_COMMANDS = /** @type {const} */ ({
27
29
  },
28
30
  },
29
31
  },
30
- });
32
+ } as const satisfies BidiModuleMap;
31
33
 
32
34
  // TODO add definitions for all bidi commands.
33
35
  // spec link: https://w3c.github.io/webdriver-bidi/
@@ -1,7 +1,7 @@
1
1
  import _ from 'lodash';
2
2
  import {util, logger} from '@appium/support';
3
3
  import {StatusCodes as HTTPStatusCodes} from 'http-status-codes';
4
- import type { ErrorBiDiCommandResponse, Class } from '@appium/types';
4
+ import type {ErrorBiDiCommandResponse, Class} from '@appium/types';
5
5
 
6
6
  const mjsonwpLog = logger.getLogger('MJSONWP');
7
7
  const w3cLog = logger.getLogger('W3C');
@@ -5,13 +5,12 @@ import {MJSONWP_ELEMENT_KEY, W3C_ELEMENT_KEY} from '../constants';
5
5
  /**
6
6
  * Preprocesses the resulting value for API responses,
7
7
  * so they have keys for both W3C and JSONWP protocols.
8
- * The argument value is NOT mutated
8
+ * The argument value is NOT mutated.
9
9
  *
10
- * @param {Object | undefined} resValue The actual response value
11
- * @returns {Object | null} Either modified value or the same one if
12
- * nothing has been modified
10
+ * @param resValue - The actual response value
11
+ * @returns Either modified value or the same one if nothing has been modified
13
12
  */
14
- export function formatResponseValue(resValue) {
13
+ export function formatResponseValue(resValue: object | undefined): object | null {
15
14
  if (_.isUndefined(resValue)) {
16
15
  // convert undefined to null
17
16
  return null;
@@ -27,13 +26,11 @@ export function formatResponseValue(resValue) {
27
26
  * Properly formats the status for API responses,
28
27
  * so they are correct for the W3C protocol.
29
28
  *
30
- * @param {Object} responseBody
31
- * @returns {Object} The fixed response body
29
+ * @param responseBody - The response body
30
+ * @returns The fixed response body
32
31
  */
33
- export function ensureW3cResponse(responseBody) {
32
+ export function ensureW3cResponse(responseBody: Record<string, unknown>): Record<string, unknown> {
34
33
  return _.isPlainObject(responseBody)
35
- ? _.omit(responseBody, ['status', 'sessionId'])
34
+ ? (_.omit(responseBody, ['status', 'sessionId']) as Record<string, unknown>)
36
35
  : responseBody;
37
36
  }
38
-
39
- export {MJSONWP_ELEMENT_KEY, W3C_ELEMENT_KEY};
@@ -14,13 +14,13 @@ import B from 'bluebird';
14
14
  import {formatResponseValue, ensureW3cResponse} from './helpers';
15
15
  import {MAX_LOG_BODY_LENGTH, PROTOCOLS, DEFAULT_BASE_PATH} from '../constants';
16
16
  import {isW3cCaps} from '../helpers/capabilities';
17
- import log from '../basedriver/logger';
18
- import { generateDriverLogPrefix } from '../basedriver/helpers';
19
- import type { Core, AppiumLogger, PayloadParams, MethodMap, Driver, DriverMethodDef } from '@appium/types';
20
- import type { BaseDriver } from '../basedriver/driver';
21
- import type { Request, Response, Application } from 'express';
22
- import type { MultidimensionalReadonlyArray } from 'type-fest';
23
- import type { RouteConfiguringFunction } from '../express/server';
17
+ import {log} from '../basedriver/logger';
18
+ import {generateDriverLogPrefix} from '../basedriver/helpers';
19
+ import type {Core, AppiumLogger, PayloadParams, MethodMap, Driver, DriverMethodDef} from '@appium/types';
20
+ import type {BaseDriver} from '../basedriver/driver';
21
+ import type {Request, Response, Application} from 'express';
22
+ import type {MultidimensionalReadonlyArray} from 'type-fest';
23
+ import type {RouteConfiguringFunction} from '../express/server';
24
24
 
25
25
  export const CREATE_SESSION_COMMAND = 'createSession';
26
26
  export const DELETE_SESSION_COMMAND = 'deleteSession';
@@ -34,6 +34,34 @@ export function determineProtocol(createSessionArgs: any[]): keyof typeof PROTOC
34
34
  return _.some(createSessionArgs, isW3cCaps) ? PROTOCOLS.W3C : PROTOCOLS.MJSONWP;
35
35
  }
36
36
 
37
+ /**
38
+ * Extract and validate the sessionId from the Express route parameter.
39
+ * Express may return route params as string | string[] | undefined.
40
+ * Appium uses standard routes (e.g., /session/:sessionId) which should always be strings.
41
+ * Only `*` such as `/session/*sessionId` can return `string[]`.
42
+ * Then, this method will return the first element as the session id.
43
+ * It may break existing appium routing handling also, thus this method will log
44
+ * received parameters as well to help debugging.
45
+ * @param driver Running driver
46
+ * @param req The request in Express
47
+ * @returns The normalized sessionId (string or undefined)
48
+ */
49
+ export function getSessionId(driver: Core<any>, req: Request): string | undefined {
50
+ if (Array.isArray(req.params.sessionId)) {
51
+ const sessionId = req.params.sessionId[0];
52
+ getLogger(driver, sessionId).warn(
53
+ `Received malformed sessionId as array from the route: ${req.originalUrl}. ` +
54
+ `This indicates the route definition issue. The route should start with '/session/:sessionId' (named parameter) ` +
55
+ `instead of '/session/*sessionId' (wildcard). ` +
56
+ `Using the first element as session id: ${sessionId}. ` +
57
+ `Please fix the route definition to prevent this error.`
58
+ );
59
+ // This is to not log the message multiple times.
60
+ req.params.sessionId = sessionId;
61
+ return sessionId;
62
+ }
63
+ return req.params.sessionId;
64
+ }
37
65
 
38
66
  function extractProtocol(driver: Core<any>, sessionId: string | null = null): keyof typeof PROTOCOLS {
39
67
  const dstDriver = _.isFunction(driver.driverForSession) && sessionId
@@ -310,13 +338,14 @@ function buildHandler(
310
338
  let httpResBody = {} as any;
311
339
  let httpStatus = 200;
312
340
  let newSessionId: string | undefined;
313
- let currentProtocol = extractProtocol(driver, req.params.sessionId);
341
+ const sessionId = getSessionId(driver, req);
342
+ let currentProtocol = extractProtocol(driver, sessionId);
314
343
 
315
344
  try {
316
345
  // if the route accessed is deprecated, log a warning
317
346
  if (spec.deprecated && spec.command && !deprecatedCommandsLogged.has(spec.command)) {
318
347
  deprecatedCommandsLogged.add(spec.command);
319
- getLogger(driver, req.params.sessionId).warn(
348
+ getLogger(driver, sessionId).warn(
320
349
  `The ${method} ${path} endpoint has been deprecated and will be removed in a future ` +
321
350
  `version of Appium or your driver/plugin. Please use a different endpoint or contact the ` +
322
351
  `driver/plugin author to add explicit support for the endpoint before it is removed`
@@ -325,7 +354,7 @@ function buildHandler(
325
354
 
326
355
  // if this is a session command but we don't have a session,
327
356
  // error out early (especially before proxying)
328
- if (isSessCmd && !driver.sessionExists(req.params.sessionId)) {
357
+ if (isSessCmd && !driver.sessionExists(sessionId)) {
329
358
  throw new errors.NoSuchDriverError();
330
359
  }
331
360
 
@@ -341,12 +370,12 @@ function buildHandler(
341
370
  if (isSessCmd && !spec.neverProxy && spec.command && driverShouldDoJwpProxy(driver, req, spec.command)) {
342
371
  if (
343
372
  !('pluginsToHandleCmd' in driver) || !_.isFunction(driver.pluginsToHandleCmd) ||
344
- driver.pluginsToHandleCmd(spec.command, req.params.sessionId).length === 0
373
+ driver.pluginsToHandleCmd(spec.command, sessionId).length === 0
345
374
  ) {
346
375
  await doJwpProxy(driver as BaseDriver<any>, req, res);
347
376
  return;
348
377
  }
349
- getLogger(driver, req.params.sessionId).debug(
378
+ getLogger(driver, sessionId).debug(
350
379
  `Would have proxied ` +
351
380
  `command directly, but a plugin exists which might require its value, so will let ` +
352
381
  `its value be collected internally and made part of plugin chain`
@@ -393,7 +422,7 @@ function buildHandler(
393
422
  }
394
423
 
395
424
  // run the driver command wrapped inside the argument validators
396
- getLogger(driver, req.params.sessionId).debug(
425
+ getLogger(driver, sessionId).debug(
397
426
  `Calling %s.%s() with args: %s`,
398
427
  driver.constructor.name, spec.command,
399
428
  logger.markSensitive(_.truncate(JSON.stringify(args), {length: MAX_LOG_BODY_LENGTH}))
@@ -409,7 +438,7 @@ function buildHandler(
409
438
  driverRes = await (driver as BaseDriver<any>).executeCommand(spec.command, ...args);
410
439
 
411
440
  // Get the protocol after executeCommand
412
- currentProtocol = extractProtocol(driver, req.params.sessionId) || currentProtocol;
441
+ currentProtocol = extractProtocol(driver, sessionId) || currentProtocol;
413
442
 
414
443
  // If `executeCommand` was overridden and the method returns an object
415
444
  // with a protocol and value/error property, re-assign the protocol
@@ -440,12 +469,12 @@ function buildHandler(
440
469
 
441
470
  // delete should not return anything even if successful
442
471
  if (spec.command === DELETE_SESSION_COMMAND) {
443
- getLogger(driver, req.params.sessionId).debug(
472
+ getLogger(driver, sessionId).debug(
444
473
  `Received response: ${_.truncate(JSON.stringify(driverRes), {
445
474
  length: MAX_LOG_BODY_LENGTH,
446
475
  })}`
447
476
  );
448
- getLogger(driver, req.params.sessionId).debug('But deleting session, so not returning');
477
+ getLogger(driver, sessionId).debug('But deleting session, so not returning');
449
478
  driverRes = null;
450
479
  }
451
480
 
@@ -467,7 +496,7 @@ function buildHandler(
467
496
  }
468
497
 
469
498
  httpResBody.value = driverRes;
470
- getLogger(driver, req.params.sessionId || newSessionId).debug(
499
+ getLogger(driver, sessionId || newSessionId).debug(
471
500
  `Responding ` +
472
501
  `to client with driver.${spec.command}() result: ${_.truncate(JSON.stringify(driverRes), {
473
502
  length: MAX_LOG_BODY_LENGTH,
@@ -480,7 +509,7 @@ function buildHandler(
480
509
  if (err instanceof Error || (_.has(err, 'stack') && _.has(err, 'message'))) {
481
510
  actualErr = err;
482
511
  } else {
483
- getLogger(driver, req.params.sessionId || newSessionId).warn(
512
+ getLogger(driver, sessionId || newSessionId).warn(
484
513
  'The thrown error object does not seem to be a valid instance of the Error class. This ' +
485
514
  'might be a genuine bug of a driver or a plugin.'
486
515
  );
@@ -488,7 +517,7 @@ function buildHandler(
488
517
  }
489
518
 
490
519
  currentProtocol =
491
- currentProtocol || extractProtocol(driver, req.params.sessionId || newSessionId);
520
+ currentProtocol || extractProtocol(driver, sessionId || newSessionId);
492
521
 
493
522
  let errMsg = err.stacktrace || err.stack;
494
523
  if (!_.includes(errMsg, err.message)) {
@@ -499,7 +528,7 @@ function buildHandler(
499
528
  if (isErrorType(err, errors.ProxyRequestError)) {
500
529
  actualErr = err.getActualError();
501
530
  } else {
502
- getLogger(driver, req.params.sessionId || newSessionId).debug(
531
+ getLogger(driver, sessionId || newSessionId).debug(
503
532
  `Encountered internal error running command: ${errMsg}`
504
533
  );
505
534
  }
@@ -525,9 +554,10 @@ function buildHandler(
525
554
  });
526
555
  }
527
556
 
528
- export function driverShouldDoJwpProxy(driver: Core<any>, req: import('express').Request, command: string): boolean {
557
+ export function driverShouldDoJwpProxy(driver: Core<any>, req: Request, command: string): boolean {
558
+ const sessionId = getSessionId(driver, req);
529
559
  // drivers need to explicitly say when the proxy is active
530
- if (!driver.proxyActive(req.params.sessionId)) {
560
+ if (!driver.proxyActive(sessionId)) {
531
561
  return false;
532
562
  }
533
563
 
@@ -539,7 +569,7 @@ export function driverShouldDoJwpProxy(driver: Core<any>, req: import('express')
539
569
 
540
570
  // validate avoidance schema, and say we shouldn't proxy if anything in the
541
571
  // avoid list matches our req
542
- if (driver.proxyRouteIsAvoided(req.params.sessionId, req.method, req.originalUrl, req.body)) {
572
+ if (driver.proxyRouteIsAvoided(sessionId as string, req.method, req.originalUrl, req.body)) {
543
573
  return false;
544
574
  }
545
575
 
@@ -547,16 +577,17 @@ export function driverShouldDoJwpProxy(driver: Core<any>, req: import('express')
547
577
  }
548
578
 
549
579
  async function doJwpProxy(driver: BaseDriver<any>, req: Request, res: Response): Promise<void> {
550
- getLogger(driver, req.params.sessionId).info(
580
+ const sessionId = getSessionId(driver, req) as string;
581
+ getLogger(driver, sessionId).info(
551
582
  'Driver proxy active, passing request on via HTTP proxy'
552
583
  );
553
584
 
554
585
  // check that the inner driver has a proxy function
555
- if (!driver.canProxy(req.params.sessionId)) {
586
+ if (!driver.canProxy(sessionId)) {
556
587
  throw new Error('Trying to proxy to a server but the driver is unable to proxy');
557
588
  }
558
589
  try {
559
- await driver.executeCommand('proxyReqRes', req, res, req.params.sessionId);
590
+ await driver.executeCommand('proxyReqRes', req, res, sessionId);
560
591
  } catch (err) {
561
592
  if (isErrorType(err, errors.ProxyRequestError)) {
562
593
  throw err;
@@ -1,20 +1,19 @@
1
+ import type {Driver, DriverMethodDef, HTTPMethod, MethodMap} from '@appium/types';
1
2
  import _ from 'lodash';
2
3
  import {DEFAULT_BASE_PATH} from '../constants';
3
4
  import {match} from 'path-to-regexp';
4
5
  import {LRUCache} from 'lru-cache';
5
6
 
6
- /** @type {LRUCache<string, string>} */
7
- const COMMAND_NAMES_CACHE = new LRUCache({
7
+ const COMMAND_NAMES_CACHE = new LRUCache<string, string>({
8
8
  max: 1024,
9
9
  });
10
10
 
11
11
  /**
12
- * define the routes, mapping of HTTP methods to particular driver commands, and
13
- * any parameters that are expected in a request parameters can be `required` or
14
- * `optional`
15
- * @satisfies {import('@appium/types').MethodMap<import('../basedriver/driver').BaseDriver>}
12
+ * Define the routes: mapping of HTTP methods to particular driver commands, and
13
+ * any parameters that are expected in a request. Parameters can be `required` or
14
+ * `optional`.
16
15
  */
17
- export const METHOD_MAP = /** @type {const} */ ({
16
+ export const METHOD_MAP = {
18
17
 
19
18
  // #region W3C WebDriver
20
19
  // https://www.w3.org/TR/webdriver2/
@@ -566,42 +565,40 @@ export const METHOD_MAP = /** @type {const} */ ({
566
565
  DELETE: {command: 'deleteVirtualPressureSource'},
567
566
  },
568
567
  // #endregion
569
- });
568
+ } as const satisfies MethodMap<Driver>;
570
569
 
571
570
  // driver command names
572
571
  export const ALL_COMMANDS = _.flatMap(_.values(METHOD_MAP).map(_.values))
573
572
  .filter((m) => Boolean(m.command))
574
573
  .map((m) => m.command);
575
574
 
576
- /**
577
- *
578
- * @param {string} endpoint
579
- * @param {import('@appium/types').HTTPMethod} [method]
580
- * @param {string} [basePath=DEFAULT_BASE_PATH]
581
- * @returns {string|undefined}
582
- */
583
- export function routeToCommandName(endpoint, method, basePath = DEFAULT_BASE_PATH) {
584
- let normalizedEndpoint = basePath
585
- ? endpoint.replace(new RegExp(`^${_.escapeRegExp(basePath)}`), '')
575
+ export function routeToCommandName(
576
+ endpoint: string,
577
+ method?: HTTPMethod,
578
+ basePath?: string
579
+ ): string | undefined {
580
+ const resolvedBasePath = basePath ?? DEFAULT_BASE_PATH;
581
+ let normalizedEndpoint = resolvedBasePath
582
+ ? endpoint.replace(new RegExp(`^${_.escapeRegExp(resolvedBasePath)}`), '')
586
583
  : endpoint;
587
584
  normalizedEndpoint = `${_.startsWith(normalizedEndpoint, '/') ? '' : '/'}${normalizedEndpoint}`;
588
- /** @type {string} */
589
- let normalizedPathname;
585
+ let normalizedPathname: string;
590
586
  try {
591
587
  // we could use any prefix there as we anyway need to only extract the pathname
592
588
  normalizedPathname = new URL(`https://appium.io${normalizedEndpoint}`).pathname;
593
- } catch (err) {
594
- throw new Error(`'${endpoint}' cannot be translated to a command name: ${err.message}`);
589
+ } catch (err: unknown) {
590
+ const msg = err instanceof Error ? err.message : String(err);
591
+ throw new Error(`'${endpoint}' cannot be translated to a command name: ${msg}`);
595
592
  }
596
593
 
597
- const normalizedMethod = _.toUpper(method);
594
+ const normalizedMethod = _.toUpper(method ?? '');
598
595
  const cacheKey = toCommandNameCacheKey(normalizedPathname, normalizedMethod);
599
- if (COMMAND_NAMES_CACHE.has(cacheKey)) {
600
- return COMMAND_NAMES_CACHE.get(cacheKey) || undefined;
596
+ const cached = COMMAND_NAMES_CACHE.get(cacheKey);
597
+ if (cached !== undefined) {
598
+ return cached || undefined;
601
599
  }
602
600
 
603
- /** @type {string[]} */
604
- const possiblePathnames = [];
601
+ const possiblePathnames: string[] = [];
605
602
  if (!normalizedPathname.startsWith('/session/')) {
606
603
  possiblePathnames.push(`/session/any-session-id${normalizedPathname}`);
607
604
  }
@@ -609,12 +606,10 @@ export function routeToCommandName(endpoint, method, basePath = DEFAULT_BASE_PAT
609
606
  for (const [routePath, routeSpec] of _.toPairs(METHOD_MAP)) {
610
607
  const routeMatcher = match(routePath);
611
608
  if (possiblePathnames.some((pp) => routeMatcher(pp))) {
612
- const commandForAnyMethod = () => _.first(
613
- (_.keys(routeSpec) ?? []).map((key) => routeSpec[key]?.command)
614
- );
615
- const commandName = normalizedMethod
616
- ? routeSpec?.[normalizedMethod]?.command
617
- : commandForAnyMethod();
609
+ const spec = routeSpec as Record<string, DriverMethodDef<Driver>>;
610
+ const commandForAnyMethod = () =>
611
+ _.first(_.keys(spec).map((key) => spec[key]?.command));
612
+ const commandName = normalizedMethod ? spec[normalizedMethod]?.command : commandForAnyMethod();
618
613
  if (commandName) {
619
614
  COMMAND_NAMES_CACHE.set(cacheKey, commandName);
620
615
  return commandName;
@@ -626,13 +621,7 @@ export function routeToCommandName(endpoint, method, basePath = DEFAULT_BASE_PAT
626
621
  COMMAND_NAMES_CACHE.set(cacheKey, '');
627
622
  }
628
623
 
629
- /**
630
- *
631
- * @param {string} endpoint
632
- * @param {string} [method]
633
- * @returns {string}
634
- */
635
- function toCommandNameCacheKey(endpoint, method) {
624
+ function toCommandNameCacheKey(endpoint: string, method?: string): string {
636
625
  return `${endpoint}:${method ?? ''}`;
637
626
  }
638
627
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appium/base-driver",
3
- "version": "10.2.0",
3
+ "version": "10.2.1",
4
4
  "description": "Base driver class for Appium drivers",
5
5
  "keywords": [
6
6
  "automation",
@@ -39,30 +39,30 @@
39
39
  ],
40
40
  "scripts": {
41
41
  "test": "run-p test:unit test:types",
42
- "test:e2e": "mocha --exit --timeout 20s --slow 10s \"./test/e2e/**/*.spec.js\"",
42
+ "test:e2e": "mocha --exit --timeout 20s --slow 10s \"./test/e2e/**/*.spec.ts\"",
43
43
  "test:smoke": "node ./index.js",
44
- "test:unit": "mocha \"./test/unit/**/*.spec.js\"",
45
- "test:types": "tsd"
44
+ "test:unit": "mocha \"./test/unit/**/*.spec.ts\"",
45
+ "test:types": "tsd && tsc -p tsconfig.test.json"
46
46
  },
47
47
  "dependencies": {
48
- "@appium/support": "^7.0.5",
49
- "@appium/types": "^1.2.0",
48
+ "@appium/support": "^7.0.6",
49
+ "@appium/types": "^1.2.1",
50
50
  "@colors/colors": "1.6.0",
51
51
  "async-lock": "1.4.1",
52
- "asyncbox": "6.0.1",
53
- "axios": "1.13.3",
52
+ "asyncbox": "6.1.0",
53
+ "axios": "1.13.6",
54
54
  "bluebird": "3.7.2",
55
55
  "body-parser": "2.2.2",
56
56
  "express": "5.2.1",
57
57
  "fastest-levenshtein": "1.0.16",
58
58
  "http-status-codes": "2.3.0",
59
59
  "lodash": "4.17.23",
60
- "lru-cache": "11.2.5",
60
+ "lru-cache": "11.2.6",
61
61
  "method-override": "3.0.0",
62
62
  "morgan": "1.10.1",
63
63
  "path-to-regexp": "8.3.0",
64
64
  "serve-favicon": "2.5.1",
65
- "type-fest": "5.4.1"
65
+ "type-fest": "5.4.4"
66
66
  },
67
67
  "optionalDependencies": {
68
68
  "spdy": "4.0.2"
@@ -74,7 +74,7 @@
74
74
  "publishConfig": {
75
75
  "access": "public"
76
76
  },
77
- "gitHead": "f7b20335eab4022e5cbbb627ec86866a994444f8",
77
+ "gitHead": "980a121804ae006db879fb6860f627ac36174c15",
78
78
  "tsd": {
79
79
  "directory": "test/types"
80
80
  }
package/tsconfig.json CHANGED
@@ -7,8 +7,10 @@
7
7
  "@appium/types": ["../types"],
8
8
  "@appium/driver-test-support": ["../driver-test-support"]
9
9
  },
10
- "checkJs": true
10
+ "checkJs": true,
11
+ "types": ["node", "mocha"]
11
12
  },
12
13
  "include": ["lib"],
14
+ "exclude": ["build"],
13
15
  "references": [{"path": "../support"}, {"path": "../types"}]
14
16
  }
@@ -1,4 +0,0 @@
1
- import {logger} from '@appium/support';
2
-
3
- const log = logger.getLogger('BaseDriver');
4
- export default log;
@@ -1,11 +0,0 @@
1
- import {errors} from '../protocol';
2
-
3
- function produceError() {
4
- throw new errors.UnknownCommandError('Produced generic error for testing');
5
- }
6
-
7
- function produceCrash() {
8
- throw new Error('We just tried to crash Appium!');
9
- }
10
-
11
- export {produceError, produceCrash};