@appium/base-driver 10.5.1 → 10.6.0

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 (130) hide show
  1. package/build/lib/basedriver/capabilities.d.ts.map +1 -1
  2. package/build/lib/basedriver/capabilities.js +45 -45
  3. package/build/lib/basedriver/capabilities.js.map +1 -1
  4. package/build/lib/basedriver/commands/bidi.d.ts.map +1 -1
  5. package/build/lib/basedriver/commands/bidi.js +9 -13
  6. package/build/lib/basedriver/commands/bidi.js.map +1 -1
  7. package/build/lib/basedriver/commands/event.d.ts.map +1 -1
  8. package/build/lib/basedriver/commands/event.js +4 -7
  9. package/build/lib/basedriver/commands/event.js.map +1 -1
  10. package/build/lib/basedriver/commands/execute.js +3 -6
  11. package/build/lib/basedriver/commands/execute.js.map +1 -1
  12. package/build/lib/basedriver/commands/log.d.ts.map +1 -1
  13. package/build/lib/basedriver/commands/log.js +1 -5
  14. package/build/lib/basedriver/commands/log.js.map +1 -1
  15. package/build/lib/basedriver/commands/timeout.d.ts.map +1 -1
  16. package/build/lib/basedriver/commands/timeout.js +5 -9
  17. package/build/lib/basedriver/commands/timeout.js.map +1 -1
  18. package/build/lib/basedriver/core.js +12 -12
  19. package/build/lib/basedriver/core.js.map +1 -1
  20. package/build/lib/basedriver/device-settings.d.ts.map +1 -1
  21. package/build/lib/basedriver/device-settings.js +3 -7
  22. package/build/lib/basedriver/device-settings.js.map +1 -1
  23. package/build/lib/basedriver/driver.d.ts.map +1 -1
  24. package/build/lib/basedriver/driver.js +13 -16
  25. package/build/lib/basedriver/driver.js.map +1 -1
  26. package/build/lib/basedriver/extension-core.d.ts +4 -1
  27. package/build/lib/basedriver/extension-core.d.ts.map +1 -1
  28. package/build/lib/basedriver/extension-core.js +27 -9
  29. package/build/lib/basedriver/extension-core.js.map +1 -1
  30. package/build/lib/basedriver/helpers.d.ts.map +1 -1
  31. package/build/lib/basedriver/helpers.js +28 -30
  32. package/build/lib/basedriver/helpers.js.map +1 -1
  33. package/build/lib/basedriver/ipc.d.ts +36 -0
  34. package/build/lib/basedriver/ipc.d.ts.map +1 -0
  35. package/build/lib/basedriver/ipc.js +155 -0
  36. package/build/lib/basedriver/ipc.js.map +1 -0
  37. package/build/lib/basedriver/validation.js +25 -28
  38. package/build/lib/basedriver/validation.js.map +1 -1
  39. package/build/lib/express/express-logging.d.ts.map +1 -1
  40. package/build/lib/express/express-logging.js +2 -3
  41. package/build/lib/express/express-logging.js.map +1 -1
  42. package/build/lib/express/idempotency.js +3 -6
  43. package/build/lib/express/idempotency.js.map +1 -1
  44. package/build/lib/express/middleware.d.ts.map +1 -1
  45. package/build/lib/express/middleware.js +6 -10
  46. package/build/lib/express/middleware.js.map +1 -1
  47. package/build/lib/express/server.d.ts.map +1 -1
  48. package/build/lib/express/server.js +64 -54
  49. package/build/lib/express/server.js.map +1 -1
  50. package/build/lib/express/static.d.ts.map +1 -1
  51. package/build/lib/express/static.js +14 -7
  52. package/build/lib/express/static.js.map +1 -1
  53. package/build/lib/express/websocket.d.ts.map +1 -1
  54. package/build/lib/express/websocket.js +6 -9
  55. package/build/lib/express/websocket.js.map +1 -1
  56. package/build/lib/helpers/capabilities.d.ts.map +1 -1
  57. package/build/lib/helpers/capabilities.js +14 -17
  58. package/build/lib/helpers/capabilities.js.map +1 -1
  59. package/build/lib/helpers/extension-command-name.js +2 -5
  60. package/build/lib/helpers/extension-command-name.js.map +1 -1
  61. package/build/lib/helpers/levenshtein-match.d.ts.map +1 -1
  62. package/build/lib/helpers/levenshtein-match.js +2 -6
  63. package/build/lib/helpers/levenshtein-match.js.map +1 -1
  64. package/build/lib/index.d.ts +1 -0
  65. package/build/lib/index.d.ts.map +1 -1
  66. package/build/lib/index.js +3 -14
  67. package/build/lib/index.js.map +1 -1
  68. package/build/lib/jsonwp-proxy/protocol-converter.d.ts.map +1 -1
  69. package/build/lib/jsonwp-proxy/protocol-converter.js +13 -17
  70. package/build/lib/jsonwp-proxy/protocol-converter.js.map +1 -1
  71. package/build/lib/jsonwp-proxy/proxy-request.d.ts +2 -2
  72. package/build/lib/jsonwp-proxy/proxy-request.d.ts.map +1 -1
  73. package/build/lib/jsonwp-proxy/proxy-request.js +25 -21
  74. package/build/lib/jsonwp-proxy/proxy-request.js.map +1 -1
  75. package/build/lib/jsonwp-proxy/proxy.d.ts.map +1 -1
  76. package/build/lib/jsonwp-proxy/proxy.js +29 -26
  77. package/build/lib/jsonwp-proxy/proxy.js.map +1 -1
  78. package/build/lib/protocol/errors.d.ts.map +1 -1
  79. package/build/lib/protocol/errors.js +25 -29
  80. package/build/lib/protocol/errors.js.map +1 -1
  81. package/build/lib/protocol/helpers.d.ts.map +1 -1
  82. package/build/lib/protocol/helpers.js +9 -8
  83. package/build/lib/protocol/helpers.js.map +1 -1
  84. package/build/lib/protocol/protocol.d.ts.map +1 -1
  85. package/build/lib/protocol/protocol.js +43 -48
  86. package/build/lib/protocol/protocol.js.map +1 -1
  87. package/build/lib/protocol/routes.d.ts +1 -1
  88. package/build/lib/protocol/routes.d.ts.map +1 -1
  89. package/build/lib/protocol/routes.js +9 -12
  90. package/build/lib/protocol/routes.js.map +1 -1
  91. package/build/lib/protocol/validators.d.ts.map +1 -1
  92. package/build/lib/protocol/validators.js +1 -5
  93. package/build/lib/protocol/validators.js.map +1 -1
  94. package/build/lib/utils.d.ts +16 -0
  95. package/build/lib/utils.d.ts.map +1 -0
  96. package/build/lib/utils.js +71 -0
  97. package/build/lib/utils.js.map +1 -0
  98. package/lib/basedriver/capabilities.ts +60 -55
  99. package/lib/basedriver/commands/bidi.ts +10 -10
  100. package/lib/basedriver/commands/event.ts +11 -10
  101. package/lib/basedriver/commands/execute.ts +3 -3
  102. package/lib/basedriver/commands/log.ts +3 -2
  103. package/lib/basedriver/commands/timeout.ts +5 -6
  104. package/lib/basedriver/core.ts +12 -12
  105. package/lib/basedriver/device-settings.ts +3 -4
  106. package/lib/basedriver/driver.ts +15 -13
  107. package/lib/basedriver/extension-core.ts +33 -7
  108. package/lib/basedriver/helpers.ts +28 -30
  109. package/lib/basedriver/ipc.ts +179 -0
  110. package/lib/basedriver/validation.ts +26 -26
  111. package/lib/express/express-logging.ts +3 -4
  112. package/lib/express/idempotency.ts +3 -3
  113. package/lib/express/middleware.ts +6 -8
  114. package/lib/express/server.ts +67 -61
  115. package/lib/express/static.ts +15 -7
  116. package/lib/express/websocket.ts +8 -10
  117. package/lib/helpers/capabilities.ts +18 -14
  118. package/lib/helpers/extension-command-name.ts +2 -2
  119. package/lib/helpers/levenshtein-match.ts +2 -5
  120. package/lib/index.js +1 -11
  121. package/lib/jsonwp-proxy/protocol-converter.ts +14 -15
  122. package/lib/jsonwp-proxy/proxy-request.ts +26 -26
  123. package/lib/jsonwp-proxy/proxy.ts +36 -37
  124. package/lib/protocol/errors.ts +29 -28
  125. package/lib/protocol/helpers.ts +9 -5
  126. package/lib/protocol/protocol.ts +44 -46
  127. package/lib/protocol/routes.ts +9 -9
  128. package/lib/protocol/validators.ts +1 -3
  129. package/lib/utils.ts +85 -0
  130. package/package.json +7 -9
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mergePlainObjects = mergePlainObjects;
4
+ exports.omit = omit;
5
+ exports.omitKeys = omitKeys;
6
+ exports.pick = pick;
7
+ exports.pickBy = pickBy;
8
+ exports.compileLodashTemplate = compileLodashTemplate;
9
+ const support_1 = require("@appium/support");
10
+ /**
11
+ * Deep-merge plain objects into a clone of `target`. Skips null/undefined sources.
12
+ * Non-plain values on a key replace the previous value (same as lodash merge for objects).
13
+ */
14
+ function mergePlainObjects(target, ...sources) {
15
+ const result = structuredClone(target);
16
+ for (const source of sources) {
17
+ if (source == null) {
18
+ continue;
19
+ }
20
+ for (const [key, value] of Object.entries(source)) {
21
+ const existing = result[key];
22
+ if (support_1.util.isPlainObject(existing) && support_1.util.isPlainObject(value)) {
23
+ result[key] = mergePlainObjects(existing, value);
24
+ }
25
+ else if (value !== undefined) {
26
+ result[key] = value;
27
+ }
28
+ }
29
+ }
30
+ return result;
31
+ }
32
+ /** Return a shallow copy of `obj` without `key`. Non-objects are returned unchanged. */
33
+ function omit(obj, key) {
34
+ if (!support_1.util.isPlainObject(obj)) {
35
+ return obj;
36
+ }
37
+ return Object.fromEntries(Object.entries(obj).filter(([k]) => k !== key));
38
+ }
39
+ /** Return a shallow copy of `obj` without any of `keys`. */
40
+ function omitKeys(obj, keys) {
41
+ if (!support_1.util.isPlainObject(obj) || keys.length === 0) {
42
+ return obj;
43
+ }
44
+ const keysToOmit = new Set(keys);
45
+ return Object.fromEntries(Object.entries(obj).filter(([k]) => !keysToOmit.has(k)));
46
+ }
47
+ /** Return a shallow copy of `obj` containing only listed keys. */
48
+ function pick(obj, keys) {
49
+ const keysToPick = new Set(keys);
50
+ return Object.fromEntries(Object.entries(obj).filter(([k]) => keysToPick.has(k)));
51
+ }
52
+ /** Return a shallow copy of `obj` whose entries pass `predicate`. */
53
+ function pickBy(obj, predicate) {
54
+ return Object.fromEntries(Object.entries(obj).filter(([key, value]) => predicate(value, key)));
55
+ }
56
+ /** Compile a lodash-style template string (`<%= expression %>`) into a render function. */
57
+ function compileLodashTemplate(template) {
58
+ const parts = [];
59
+ let lastIndex = 0;
60
+ const re = /<%=\s*([\s\S]+?)\s*%>/g;
61
+ let match;
62
+ while ((match = re.exec(template)) !== null) {
63
+ parts.push(JSON.stringify(template.slice(lastIndex, match.index)));
64
+ parts.push(`String(${match[1]})`);
65
+ lastIndex = match.index + match[0].length;
66
+ }
67
+ parts.push(JSON.stringify(template.slice(lastIndex)));
68
+ const fn = new Function('obj', `with (obj) { return ${parts.join(' + ')}; }`);
69
+ return (params) => fn(params);
70
+ }
71
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../lib/utils.ts"],"names":[],"mappings":"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"}
@@ -9,9 +9,9 @@ import type {
9
9
  KeyAsString,
10
10
  MergeExclusive,
11
11
  } from 'type-fest';
12
- import _ from 'lodash';
13
12
  import {validator} from './validation';
14
13
  import {util} from '@appium/support';
14
+ import {omit, pickBy} from '../utils';
15
15
  import {log} from './logger';
16
16
  import {errors} from '../protocol/errors';
17
17
 
@@ -50,7 +50,7 @@ export function mergeCaps<
50
50
 
51
51
  for (const [name, value] of Object.entries(secondary)) {
52
52
  // Overwriting is not allowed. Primary and secondary must have different properties (w3c rule 4.4)
53
- if (!_.isUndefined(primary[name])) {
53
+ if (primary[name] !== undefined) {
54
54
  throw new errors.InvalidArgumentError(
55
55
  `property '${name}' should not exist on both primary (${JSON.stringify(
56
56
  primary
@@ -73,32 +73,31 @@ export function validateCaps<C extends Constraints>(
73
73
  ): Capabilities<C> {
74
74
  const {skipPresenceConstraint} = opts;
75
75
 
76
- if (!_.isPlainObject(caps)) {
76
+ if (!util.isPlainObject(caps)) {
77
77
  throw new errors.InvalidArgumentError(`must be a JSON object`);
78
78
  }
79
79
 
80
80
  // Remove the 'presence' constraint if we're not checking for it
81
- constraints = (
82
- _.mapValues(
83
- constraints,
84
- skipPresenceConstraint
85
- ? /** @param {Constraint} constraint */
86
- (constraint) => _.omit(constraint, 'presence')
87
- : /** @param {Constraint} constraint */
88
- (constraint) => {
89
- if (constraint.presence === true) {
90
- return {..._.omit(constraint, 'presence'), presence: {allowEmpty: false}};
91
- }
92
- return constraint;
93
- }
94
- )
81
+ constraints = Object.fromEntries(
82
+ Object.entries(constraints ?? {}).map(([key, constraint]) => {
83
+ if (skipPresenceConstraint) {
84
+ return [key, omit(constraint as Record<string, unknown>, 'presence')];
85
+ }
86
+ if ((constraint as {presence?: unknown}).presence === true) {
87
+ return [key, {...omit(constraint as Record<string, unknown>, 'presence'), presence: {allowEmpty: false}}];
88
+ }
89
+ return [key, constraint];
90
+ })
95
91
  ) as C;
96
92
 
97
- const validationErrors = validator.validate(_.pickBy(caps, util.hasValue), constraints);
93
+ const validationErrors = validator.validate(
94
+ pickBy(caps, (value) => util.hasValue(value)),
95
+ constraints
96
+ );
98
97
 
99
98
  if (validationErrors) {
100
99
  const message: string[] = [];
101
- for (const [attribute, reasons] of _.toPairs(validationErrors)) {
100
+ for (const [attribute, reasons] of Object.entries(validationErrors)) {
102
101
  for (const reason of (reasons as string[])) {
103
102
  message.push(`'${attribute}' ${reason}`);
104
103
  }
@@ -151,12 +150,14 @@ export function isStandardCap(cap: string): boolean {
151
150
  export function stripAppiumPrefixes<C extends Constraints>(caps: NSCapabilities<C>): Capabilities<C> {
152
151
  // split into prefixed and non-prefixed.
153
152
  // non-prefixed should be standard caps at this point
154
- const [prefixedCaps, nonPrefixedCaps] = _.partition(_.keys(caps), (cap) =>
155
- String(cap).startsWith(APPIUM_VENDOR_PREFIX)
156
- );
153
+ const capKeys = Object.keys(caps);
154
+ const prefixedCaps = capKeys.filter((cap) => String(cap).startsWith(APPIUM_VENDOR_PREFIX));
155
+ const nonPrefixedCaps = capKeys.filter((cap) => !String(cap).startsWith(APPIUM_VENDOR_PREFIX));
157
156
 
158
157
  // initialize this with the k/v pairs of the non-prefixed caps
159
- const strippedCaps = (_.pick(caps, nonPrefixedCaps)) as Capabilities<C>;
158
+ const strippedCaps: Record<string, unknown> = Object.fromEntries(
159
+ nonPrefixedCaps.map((cap) => [cap, caps[cap as keyof typeof caps]])
160
+ ) as Capabilities<C>;
160
161
  const badPrefixedCaps: string[] = [];
161
162
 
162
163
  // Strip out the 'appium:' prefix
@@ -166,16 +167,16 @@ export function stripAppiumPrefixes<C extends Constraints>(caps: NSCapabilities<
166
167
  // If it's standard capability that was prefixed, add it to an array of incorrectly prefixed capabilities
167
168
  if (isStandardCap(strippedCapName)) {
168
169
  badPrefixedCaps.push(strippedCapName);
169
- if (_.isNil(strippedCaps[strippedCapName])) {
170
- strippedCaps[strippedCapName] = caps[prefixedCap];
170
+ if (strippedCaps[strippedCapName] == null) {
171
+ strippedCaps[strippedCapName] = caps[prefixedCap as keyof typeof caps];
171
172
  } else {
172
173
  log.warn(
173
- `Ignoring capability '${prefixedCap}=${caps[prefixedCap]}' and ` +
174
+ `Ignoring capability '${prefixedCap}=${caps[prefixedCap as keyof typeof caps]}' and ` +
174
175
  `using capability '${strippedCapName}=${strippedCaps[strippedCapName]}'`
175
176
  );
176
177
  }
177
178
  } else {
178
- strippedCaps[strippedCapName] = caps[prefixedCap];
179
+ strippedCaps[strippedCapName] = caps[prefixedCap as keyof typeof caps];
179
180
  }
180
181
  }
181
182
 
@@ -187,7 +188,7 @@ export function stripAppiumPrefixes<C extends Constraints>(caps: NSCapabilities<
187
188
  )} are standard capabilities and do not require "appium:" prefix`
188
189
  );
189
190
  }
190
- return strippedCaps;
191
+ return strippedCaps as Capabilities<C>;
191
192
  }
192
193
 
193
194
  /**
@@ -199,16 +200,15 @@ export function findNonPrefixedCaps<C extends Constraints>(
199
200
  firstMatch = []
200
201
  }: W3CCapabilities<C>
201
202
  ): string[] {
202
- return _.chain([alwaysMatch, ...firstMatch])
203
- .reduce(
203
+ return util.uniq(
204
+ [alwaysMatch, ...firstMatch].reduce<string[]>(
204
205
  (unprefixedCaps, caps) => [
205
206
  ...unprefixedCaps,
206
207
  ...Object.keys(caps).filter((cap) => !cap.includes(':') && !isStandardCap(cap)),
207
208
  ],
208
209
  []
209
210
  )
210
- .uniq()
211
- .value();
211
+ );
212
212
  }
213
213
 
214
214
  /**
@@ -221,7 +221,7 @@ export function parseCaps<C extends Constraints>(
221
221
  shouldValidateCaps: boolean | undefined = true
222
222
  ): ParsedCaps<C> {
223
223
  // If capabilities request is not an object, return error (#1.1)
224
- if (!_.isPlainObject(caps)) {
224
+ if (!util.isPlainObject(caps)) {
225
225
  throw new errors.InvalidArgumentError(
226
226
  'The capabilities argument was not valid for the following reason(s): "capabilities" must be a JSON object.'
227
227
  );
@@ -235,7 +235,7 @@ export function parseCaps<C extends Constraints>(
235
235
  } = caps;
236
236
 
237
237
  // Reject 'firstMatch' argument if it's not an array (#3.2)
238
- if (!_.isArray(allFirstMatchCaps)) {
238
+ if (!Array.isArray(allFirstMatchCaps)) {
239
239
  throw new errors.InvalidArgumentError(
240
240
  'The capabilities.firstMatch argument was not valid for the following reason(s): "capabilities.firstMatch" must be a JSON array or undefined'
241
241
  );
@@ -253,7 +253,7 @@ export function parseCaps<C extends Constraints>(
253
253
 
254
254
  // Check for non-prefixed, non-standard capabilities and log warnings if they are found
255
255
  const nonPrefixedCaps = findNonPrefixedCaps(caps);
256
- if (!_.isEmpty(nonPrefixedCaps)) {
256
+ if (!util.isEmpty(nonPrefixedCaps)) {
257
257
  throw new errors.InvalidArgumentError(
258
258
  `All non-standard capabilities should have a vendor prefix. The following capabilities did not have one: ${nonPrefixedCaps}`
259
259
  );
@@ -271,22 +271,24 @@ export function parseCaps<C extends Constraints>(
271
271
  }
272
272
  // Remove the 'presence' constraint for any keys that are already present in 'requiredCaps'
273
273
  // since we know that this constraint has already passed
274
- const filteredConstraints = _.omitBy(constraints, (_, key) => key in strippedRequiredCaps) as C;
274
+ const filteredConstraints = Object.fromEntries(
275
+ Object.entries(constraints ?? {}).filter(([key]) => !(key in strippedRequiredCaps))
276
+ ) as C;
275
277
 
276
278
  // Validate all of the first match capabilities and return an array with only the valid caps (see spec #5)
277
279
  const validationErrors: string[] = [];
278
- const validatedFirstMatchCaps = _.compact(
279
- strippedAllFirstMatchCaps.map((firstMatchCaps) => {
280
+ const validatedFirstMatchCaps = strippedAllFirstMatchCaps
281
+ .map((firstMatchCaps) => {
280
282
  try {
281
283
  // Validate firstMatch caps
282
284
  return shouldValidateCaps
283
285
  ? validateCaps(firstMatchCaps, filteredConstraints)
284
286
  : firstMatchCaps;
285
287
  } catch (e) {
286
- validationErrors.push(e.message);
288
+ validationErrors.push((e as Error).message);
287
289
  }
288
290
  })
289
- ) as Capabilities<C>[];
291
+ .filter(Boolean) as Capabilities<C>[];
290
292
 
291
293
  /**
292
294
  * Try to merge requiredCaps with first match capabilities, break once it finds its first match
@@ -300,8 +302,8 @@ export function parseCaps<C extends Constraints>(
300
302
  break;
301
303
  }
302
304
  } catch (err) {
303
- log.warn(err.message);
304
- validationErrors.push(err.message);
305
+ log.warn((err as Error).message);
306
+ validationErrors.push((err as Error).message);
305
307
  }
306
308
  }
307
309
 
@@ -330,7 +332,7 @@ export function processCapabilities<
330
332
 
331
333
  // If we found an error throw an exception
332
334
  if (!util.hasValue(matchedCaps)) {
333
- if (_.isArray(w3cCaps.firstMatch) && w3cCaps.firstMatch.length > 1) {
335
+ if (Array.isArray(w3cCaps.firstMatch) && w3cCaps.firstMatch.length > 1) {
334
336
  // If there was more than one 'firstMatch' cap, indicate that we couldn't find a matching capabilities set and show all the errors
335
337
  throw new errors.InvalidArgumentError(
336
338
  `Could not find matching capabilities from ${JSON.stringify(
@@ -356,12 +358,12 @@ export function promoteAppiumOptionsForObject<C extends Constraints>(obj: NSCapa
356
358
  return obj;
357
359
  }
358
360
 
359
- if (!_.isPlainObject(appiumOptions)) {
361
+ if (!util.isPlainObject(appiumOptions)) {
360
362
  throw new errors.SessionNotCreatedError(
361
363
  `The ${PREFIXED_APPIUM_OPTS_CAP} capability must be an object`
362
364
  );
363
365
  }
364
- if (_.isEmpty(appiumOptions)) {
366
+ if (util.isEmpty(appiumOptions)) {
365
367
  return obj;
366
368
  }
367
369
 
@@ -374,7 +376,7 @@ export function promoteAppiumOptionsForObject<C extends Constraints>(obj: NSCapa
374
376
  */
375
377
  const shouldAddVendorPrefix = (capName: string) => !capName.startsWith(APPIUM_VENDOR_PREFIX);
376
378
  const verifyIfAcceptable = (capName: string) => {
377
- if (!_.isString(capName)) {
379
+ if (typeof capName !== 'string') {
378
380
  throw new errors.SessionNotCreatedError(
379
381
  `Capability names in ${PREFIXED_APPIUM_OPTS_CAP} must be strings. '${capName}' is unexpected`
380
382
  );
@@ -386,20 +388,23 @@ export function promoteAppiumOptionsForObject<C extends Constraints>(obj: NSCapa
386
388
  }
387
389
  return capName;
388
390
  };
389
- const preprocessedOptions = _(appiumOptions)
390
- .mapKeys((value, key: string) => verifyIfAcceptable(key))
391
- .mapKeys((value, key: string) => (shouldAddVendorPrefix(key) ? `${APPIUM_VENDOR_PREFIX}${key}` : key))
392
- .value();
391
+ const preprocessedOptions: Record<string, unknown> = {};
392
+ for (const [key, value] of Object.entries(appiumOptions as Record<string, unknown>)) {
393
+ const verifiedKey = verifyIfAcceptable(key);
394
+ const finalKey = shouldAddVendorPrefix(verifiedKey) ? `${APPIUM_VENDOR_PREFIX}${verifiedKey}` : verifiedKey;
395
+ preprocessedOptions[finalKey] = value;
396
+ }
393
397
  // warn if we are going to overwrite any keys on the base caps object
394
- const overwrittenKeys = _.intersection(Object.keys(obj), Object.keys(preprocessedOptions));
398
+ const overwrittenKeys = Object.keys(obj).filter((key) => key in preprocessedOptions);
395
399
  if (overwrittenKeys.length > 0) {
396
400
  log.warn(
397
401
  `Found capabilities inside ${PREFIXED_APPIUM_OPTS_CAP} that will overwrite ` +
398
402
  `capabilities at the top level: ${JSON.stringify(overwrittenKeys)}`
399
403
  );
400
404
  }
401
- return _.cloneDeep({
402
- ..._.omit(obj, PREFIXED_APPIUM_OPTS_CAP) as NSCapabilities<C>,
405
+ const restObj = omit(obj, PREFIXED_APPIUM_OPTS_CAP) as NSCapabilities<C>;
406
+ return structuredClone({
407
+ ...restObj,
403
408
  ...preprocessedOptions,
404
409
  });
405
410
  }
@@ -411,12 +416,12 @@ export function promoteAppiumOptionsForObject<C extends Constraints>(obj: NSCapa
411
416
  export function promoteAppiumOptions<C extends Constraints>(originalCaps: W3CCapabilities<C>): W3CCapabilities<C> {
412
417
  const result = {} as W3CCapabilities<C>;
413
418
  const {alwaysMatch, firstMatch} = originalCaps;
414
- if (_.isPlainObject(alwaysMatch)) {
419
+ if (util.isPlainObject(alwaysMatch)) {
415
420
  result.alwaysMatch = promoteAppiumOptionsForObject(alwaysMatch);
416
421
  } else if ('alwaysMatch' in originalCaps) {
417
422
  result.alwaysMatch = alwaysMatch;
418
423
  }
419
- if (_.isArray(firstMatch)) {
424
+ if (Array.isArray(firstMatch)) {
420
425
  result.firstMatch = firstMatch.map(promoteAppiumOptionsForObject);
421
426
  } else if ('firstMatch' in originalCaps) {
422
427
  result.firstMatch = firstMatch;
@@ -1,7 +1,7 @@
1
+ import {util} from '@appium/support';
1
2
  import type {Constraints, DriverStatus, IBidiCommands} from '@appium/types';
2
3
  import type {BaseDriver} from '../driver';
3
4
  import {mixin} from './mixin';
4
- import _ from 'lodash';
5
5
 
6
6
  declare module '../driver' {
7
7
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -36,15 +36,15 @@ const BidiCommands: IBidiCommands = {
36
36
 
37
37
  async bidiStatus<C extends Constraints>(this: BaseDriver<C>): Promise<DriverStatus> {
38
38
  const result = await this.getStatus();
39
- if (!_.has(result, 'ready')) {
40
- //@ts-ignore This is OK
41
- result.ready = true;
42
- }
43
- if (!_.has(result, 'message')) {
44
- //@ts-ignore This is OK
45
- result.message = `${this.constructor.name} is ready to accept commands`;
46
- }
47
- return result as DriverStatus;
39
+ const base: Record<string, unknown> = util.isPlainObject(result) ? {...result} : {};
40
+ return {
41
+ ...base,
42
+ ready: 'ready' in base ? (base.ready as boolean) : true,
43
+ message:
44
+ 'message' in base
45
+ ? (base.message as string)
46
+ : `${this.constructor.name} is ready to accept commands`,
47
+ };
48
48
  }
49
49
  };
50
50
 
@@ -1,5 +1,5 @@
1
- import type {Constraints, IEventCommands} from '@appium/types';
2
- import _ from 'lodash';
1
+ import type {Constraints, EventHistory, IEventCommands} from '@appium/types';
2
+ import {util} from '@appium/support';
3
3
  import type {BaseDriver} from '../driver';
4
4
  import {mixin} from './mixin';
5
5
 
@@ -16,7 +16,7 @@ const EventCommands: IEventCommands = {
16
16
  * separation
17
17
  * @param event - the event name
18
18
  */
19
- async logCustomEvent<C extends Constraints>(this: BaseDriver<C>, vendor: string, event: string) {
19
+ async logCustomEvent<C extends Constraints>(this: BaseDriver<C>, vendor: string, event: string): Promise<void> {
20
20
  this.logEvent(`${vendor}:${event}`);
21
21
  },
22
22
 
@@ -26,23 +26,24 @@ const EventCommands: IEventCommands = {
26
26
  * It returns all events if the type is not provided or empty string/array.
27
27
  * @returns the event history log object
28
28
  */
29
- async getLogEvents<C extends Constraints>(this: BaseDriver<C>, type: string | string[]) {
30
- if (_.isEmpty(type)) {
29
+ async getLogEvents<C extends Constraints>(this: BaseDriver<C>, type: string | string[]): Promise<
30
+ Partial<EventHistory>
31
+ > {
32
+ if (util.isEmpty(type)) {
31
33
  return this.eventHistory;
32
34
  }
33
35
 
34
- const typeList = _.castArray(type);
36
+ const typeList = Array.isArray(type) ? type : [type];
35
37
 
36
- return _.reduce(
37
- this.eventHistory,
38
- (acc, eventTimes, eventType) => {
38
+ return Object.entries(this.eventHistory).reduce<Partial<EventHistory>>(
39
+ (acc, [eventType, eventTimes]) => {
39
40
  if (typeList.includes(eventType)) {
40
41
  acc[eventType] = eventTimes;
41
42
  }
42
43
  return acc;
43
44
  },
44
45
  {}
45
- );
46
+ ) as Record<string, number>;
46
47
  },
47
48
  };
48
49
 
@@ -1,4 +1,4 @@
1
- import _ from 'lodash';
1
+ import {util} from '@appium/support';
2
2
  import {errors, validateExecuteMethodParams} from '../../protocol';
3
3
  import type {
4
4
  Constraints,
@@ -26,8 +26,8 @@ const ExecuteCommands: IExecuteCommands = {
26
26
  const Driver = this.constructor as DriverClass<Driver<C>>;
27
27
  const commandMetadata = {...Driver.executeMethodMap?.[script]};
28
28
  if (!commandMetadata.command) {
29
- const availableScripts = _.keys(Driver.executeMethodMap);
30
- if (_.isEmpty(availableScripts)) {
29
+ const availableScripts = Object.keys(Driver.executeMethodMap ?? {});
30
+ if (util.isEmpty(availableScripts)) {
31
31
  throw new errors.UnsupportedOperationError(
32
32
  `Unsupported execute method '${script}'. ` +
33
33
  `Make sure the installed ${Driver.name} is up-to-date. ` +
@@ -1,5 +1,4 @@
1
1
  import type {Constraints, Driver, ILogCommands} from '@appium/types';
2
- import _ from 'lodash';
3
2
  import type {BaseDriver} from '../driver';
4
3
  import {mixin} from './mixin';
5
4
 
@@ -20,7 +19,9 @@ const LogCommands: ILogCommands = {
20
19
  this.log.debug(`Retrieving '${String(logType)}' logs`);
21
20
 
22
21
  if (!(logType in this.supportedLogTypes)) {
23
- const logsTypesWithDescriptions = _.mapValues(this.supportedLogTypes, 'description');
22
+ const logsTypesWithDescriptions = Object.fromEntries(
23
+ Object.entries(this.supportedLogTypes).map(([key, value]) => [key, value.description])
24
+ );
24
25
  throw new Error(
25
26
  `Unsupported log type '${String(logType)}'. ` +
26
27
  `Supported types: ${JSON.stringify(logsTypesWithDescriptions)}`
@@ -1,5 +1,4 @@
1
1
  import {waitForCondition} from 'asyncbox';
2
- import _ from 'lodash';
3
2
  import {util} from '@appium/support';
4
3
  import {errors} from '../../protocol';
5
4
  import type {BaseDriver} from '../driver';
@@ -15,7 +14,7 @@ const MIN_TIMEOUT = 0;
15
14
 
16
15
  const TimeoutCommands: ITimeoutCommands = {
17
16
  async timeouts<C extends Constraints>(this: BaseDriver<C>, type, ms, script, pageLoad, implicit) {
18
- if (type && _.isString(type) && util.hasValue(ms)) {
17
+ if (type && typeof type === 'string' && util.hasValue(ms)) {
19
18
  // legacy stuff with some Appium-specific additions
20
19
  this.log.debug(`Timeout arguments: ${JSON.stringify({type, ms})}}`);
21
20
  switch (type) {
@@ -33,7 +32,7 @@ const TimeoutCommands: ITimeoutCommands = {
33
32
  }
34
33
 
35
34
  this.log.debug(`W3C timeout argument: ${JSON.stringify({script, pageLoad, implicit})}}`);
36
- if ([script, pageLoad, implicit].every(_.isNil)) {
35
+ if ([script, pageLoad, implicit].every((value) => value == null)) {
37
36
  throw new errors.InvalidArgumentError('W3C protocol expects any of script, pageLoad or implicit to be set');
38
37
  }
39
38
  if (util.hasValue(script)) {
@@ -82,7 +81,7 @@ const TimeoutCommands: ITimeoutCommands = {
82
81
  if (this.managedDrivers?.length) {
83
82
  this.log.debug('Setting implicit wait on managed drivers');
84
83
  for (const driver of this.managedDrivers) {
85
- if (_.isFunction(driver.setImplicitWait)) {
84
+ if (typeof driver.setImplicitWait === 'function') {
86
85
  driver.setImplicitWait(ms);
87
86
  }
88
87
  }
@@ -95,7 +94,7 @@ const TimeoutCommands: ITimeoutCommands = {
95
94
  if (this.managedDrivers?.length) {
96
95
  this.log.debug('Setting new command timeout on managed drivers');
97
96
  for (const driver of this.managedDrivers) {
98
- if (_.isFunction(driver.setNewCommandTimeout)) {
97
+ if (typeof driver.setNewCommandTimeout === 'function') {
99
98
  driver.setNewCommandTimeout(ms);
100
99
  }
101
100
  }
@@ -122,7 +121,7 @@ const TimeoutCommands: ITimeoutCommands = {
122
121
 
123
122
  parseTimeoutArgument<C extends Constraints>(this: BaseDriver<C>, ms: number | string) {
124
123
  const duration = parseInt(String(ms), 10);
125
- if (_.isNaN(duration) || duration < MIN_TIMEOUT) {
124
+ if (Number.isNaN(duration) || duration < MIN_TIMEOUT) {
126
125
  throw new errors.UnknownError(`Invalid timeout value '${ms}'`);
127
126
  }
128
127
  return duration;
@@ -12,7 +12,7 @@ import type {
12
12
  StringRecord,
13
13
  } from '@appium/types';
14
14
  import AsyncLock from 'async-lock';
15
- import _ from 'lodash';
15
+ import {util} from '@appium/support';
16
16
  import os from 'node:os';
17
17
  import {
18
18
  DEFAULT_BASE_PATH,
@@ -116,7 +116,7 @@ export class DriverCore<const C extends Constraints, Settings extends StringReco
116
116
  this.shouldValidateCaps = shouldValidateCaps;
117
117
 
118
118
  // keeping track of initial opts
119
- this.initialOpts = _.cloneDeep(opts);
119
+ this.initialOpts = structuredClone(opts);
120
120
 
121
121
  this.sessionId = null;
122
122
  this.helpers = helpers;
@@ -166,7 +166,7 @@ export class DriverCore<const C extends Constraints, Settings extends StringReco
166
166
  * inadvertently change data outside of logEvent
167
167
  */
168
168
  get eventHistory() {
169
- return _.cloneDeep(this._eventHistory);
169
+ return structuredClone(this._eventHistory);
170
170
  }
171
171
 
172
172
  /**
@@ -263,7 +263,7 @@ export class DriverCore<const C extends Constraints, Settings extends StringReco
263
263
  isFeatureEnabled(name: string): boolean {
264
264
  // automationName comparison is case-insensitive,
265
265
  // while feature name is case-sensitive
266
- const currentAutomationName = _.toLower(this.opts.automationName);
266
+ const currentAutomationName = String(this.opts.automationName).toLowerCase();
267
267
 
268
268
  const parseFullName = (fullName: string) => {
269
269
  const separatorPos = fullName.indexOf(FEATURE_NAME_SEPARATOR);
@@ -279,7 +279,7 @@ export class DriverCore<const C extends Constraints, Settings extends StringReco
279
279
  );
280
280
  }
281
281
  return [
282
- _.toLower(fullName.substring(0, separatorPos)),
282
+ fullName.substring(0, separatorPos).toLowerCase(),
283
283
  fullName.substring(separatorPos + 1)
284
284
  ];
285
285
  };
@@ -288,12 +288,12 @@ export class DriverCore<const C extends Constraints, Settings extends StringReco
288
288
  [currentAutomationName, ALL_DRIVERS_MATCH].includes(automationName) && featureName === name;
289
289
 
290
290
  // if we have explicitly denied this feature, return false immediately
291
- if (!_.isEmpty(this.denyInsecure) && parseFullNames(this.denyInsecure).some(matches)) {
291
+ if (!util.isEmpty(this.denyInsecure) && parseFullNames(this.denyInsecure).some(matches)) {
292
292
  return false;
293
293
  }
294
294
 
295
295
  // if we specifically have allowed the feature, return true
296
- if (!_.isEmpty(this.allowInsecure) && parseFullNames(this.allowInsecure).some(matches)) {
296
+ if (!util.isEmpty(this.allowInsecure) && parseFullNames(this.allowInsecure).some(matches)) {
297
297
  return true;
298
298
  }
299
299
 
@@ -332,7 +332,7 @@ export class DriverCore<const C extends Constraints, Settings extends StringReco
332
332
  validStrategies = validStrategies.concat(this.webLocatorStrategies);
333
333
  }
334
334
 
335
- if (!_.includes(validStrategies, strategy)) {
335
+ if (!validStrategies.includes(strategy)) {
336
336
  throw new errors.InvalidSelectorError(
337
337
  `Locator Strategy '${strategy}' is not supported for this session`,
338
338
  );
@@ -370,17 +370,17 @@ export class DriverCore<const C extends Constraints, Settings extends StringReco
370
370
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
371
371
  proxyRouteIsAvoided(sessionId: string, method: HTTPMethod, url: string, body?: any): boolean {
372
372
  for (const avoidSchema of this.getProxyAvoidList(sessionId)) {
373
- if (!_.isArray(avoidSchema) || avoidSchema.length !== 2) {
373
+ if (!Array.isArray(avoidSchema) || avoidSchema.length !== 2) {
374
374
  throw new Error('Proxy avoidance must be a list of pairs');
375
375
  }
376
376
  const [avoidMethod, avoidPathRegex] = avoidSchema;
377
- if (!_.includes(['GET', 'POST', 'DELETE'], avoidMethod)) {
377
+ if (!['GET', 'POST', 'DELETE'].includes(avoidMethod)) {
378
378
  throw new Error(`Unrecognized proxy avoidance method '${avoidMethod}'`);
379
379
  }
380
- if (!_.isRegExp(avoidPathRegex)) {
380
+ if (!(avoidPathRegex instanceof RegExp)) {
381
381
  throw new Error('Proxy avoidance path must be a regular expression');
382
382
  }
383
- const normalizedUrl = url.replace(new RegExp(`^${_.escapeRegExp(this.basePath)}`), '');
383
+ const normalizedUrl = url.replace(new RegExp(`^${util.escapeRegExp(this.basePath)}`), '');
384
384
  if (avoidMethod === method && avoidPathRegex.test(normalizedUrl)) {
385
385
  return true;
386
386
  }
@@ -1,6 +1,5 @@
1
- import _ from 'lodash';
1
+ import {util, node} from '@appium/support';
2
2
  import {log} from './logger';
3
- import {node, util} from '@appium/support';
4
3
  import {errors} from '../protocol/errors';
5
4
  import type {StringRecord, IDeviceSettings, SettingsUpdateListener} from '@appium/types';
6
5
 
@@ -36,7 +35,7 @@ export class DeviceSettings<T extends StringRecord = StringRecord> implements ID
36
35
  * @param newSettings - New settings to merge (must be plain object; total size remains bounded).
37
36
  */
38
37
  async update(newSettings: T): Promise<void> {
39
- if (!_.isPlainObject(newSettings)) {
38
+ if (!util.isPlainObject(newSettings)) {
40
39
  throw new errors.InvalidArgumentError(
41
40
  `Settings update should be called with valid JSON. Got ` +
42
41
  `${JSON.stringify(newSettings)} instead`
@@ -51,7 +50,7 @@ export class DeviceSettings<T extends StringRecord = StringRecord> implements ID
51
50
  }
52
51
 
53
52
  for (const prop in newSettings) {
54
- if (!_.isUndefined(this._settings[prop])) {
53
+ if (this._settings[prop] !== undefined) {
55
54
  if (this._settings[prop] === newSettings[prop]) {
56
55
  log.debug(`The value of '${prop}' setting did not change. Skipping the update for it`);
57
56
  continue;