appium 3.5.0 → 3.5.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 (195) hide show
  1. package/build/lib/appium.d.ts.map +1 -1
  2. package/build/lib/appium.js +43 -45
  3. package/build/lib/appium.js.map +1 -1
  4. package/build/lib/bidi-commands.d.ts.map +1 -1
  5. package/build/lib/bidi-commands.js +34 -29
  6. package/build/lib/bidi-commands.js.map +1 -1
  7. package/build/lib/bootstrap/appium-initializer.d.ts.map +1 -1
  8. package/build/lib/bootstrap/appium-initializer.js +13 -13
  9. package/build/lib/bootstrap/appium-initializer.js.map +1 -1
  10. package/build/lib/bootstrap/appium-main-runner.d.ts.map +1 -1
  11. package/build/lib/bootstrap/appium-main-runner.js +2 -2
  12. package/build/lib/bootstrap/appium-main-runner.js.map +1 -1
  13. package/build/lib/bootstrap/config-file.d.ts.map +1 -1
  14. package/build/lib/bootstrap/config-file.js +11 -11
  15. package/build/lib/bootstrap/config-file.js.map +1 -1
  16. package/build/lib/bootstrap/grid-v3-register.d.ts.map +1 -1
  17. package/build/lib/bootstrap/grid-v3-register.js +9 -6
  18. package/build/lib/bootstrap/grid-v3-register.js.map +1 -1
  19. package/build/lib/bootstrap/main-helpers.d.ts +1 -1
  20. package/build/lib/bootstrap/main-helpers.d.ts.map +1 -1
  21. package/build/lib/bootstrap/main-helpers.js +19 -20
  22. package/build/lib/bootstrap/main-helpers.js.map +1 -1
  23. package/build/lib/bootstrap/node-helpers.d.ts +1 -1
  24. package/build/lib/bootstrap/node-helpers.d.ts.map +1 -1
  25. package/build/lib/bootstrap/node-helpers.js +7 -14
  26. package/build/lib/bootstrap/node-helpers.js.map +1 -1
  27. package/build/lib/bootstrap/startup-config.d.ts.map +1 -1
  28. package/build/lib/bootstrap/startup-config.js +32 -30
  29. package/build/lib/bootstrap/startup-config.js.map +1 -1
  30. package/build/lib/cli/args.d.ts +3 -2
  31. package/build/lib/cli/args.d.ts.map +1 -1
  32. package/build/lib/cli/args.js +2 -5
  33. package/build/lib/cli/args.js.map +1 -1
  34. package/build/lib/cli/driver-command.d.ts +1 -2
  35. package/build/lib/cli/driver-command.d.ts.map +1 -1
  36. package/build/lib/cli/driver-command.js +7 -8
  37. package/build/lib/cli/driver-command.js.map +1 -1
  38. package/build/lib/cli/extension-command.d.ts +2 -2
  39. package/build/lib/cli/extension-command.d.ts.map +1 -1
  40. package/build/lib/cli/extension-command.js +100 -66
  41. package/build/lib/cli/extension-command.js.map +1 -1
  42. package/build/lib/cli/extension.d.ts.map +1 -1
  43. package/build/lib/cli/extension.js.map +1 -1
  44. package/build/lib/cli/parser.d.ts.map +1 -1
  45. package/build/lib/cli/parser.js +21 -14
  46. package/build/lib/cli/parser.js.map +1 -1
  47. package/build/lib/cli/plugin-command.d.ts +1 -1
  48. package/build/lib/cli/plugin-command.d.ts.map +1 -1
  49. package/build/lib/cli/plugin-command.js +5 -5
  50. package/build/lib/cli/plugin-command.js.map +1 -1
  51. package/build/lib/cli/setup-command.d.ts +1 -1
  52. package/build/lib/cli/setup-command.d.ts.map +1 -1
  53. package/build/lib/cli/setup-command.js +14 -19
  54. package/build/lib/cli/setup-command.js.map +1 -1
  55. package/build/lib/cli/utils.d.ts.map +1 -1
  56. package/build/lib/cli/utils.js +3 -2
  57. package/build/lib/cli/utils.js.map +1 -1
  58. package/build/lib/doctor/doctor.d.ts +0 -1
  59. package/build/lib/doctor/doctor.d.ts.map +1 -1
  60. package/build/lib/doctor/doctor.js +12 -17
  61. package/build/lib/doctor/doctor.js.map +1 -1
  62. package/build/lib/extension/driver-config.d.ts.map +1 -1
  63. package/build/lib/extension/driver-config.js +11 -12
  64. package/build/lib/extension/driver-config.js.map +1 -1
  65. package/build/lib/extension/extension-config.d.ts.map +1 -1
  66. package/build/lib/extension/extension-config.js +28 -27
  67. package/build/lib/extension/extension-config.js.map +1 -1
  68. package/build/lib/extension/index.d.ts.map +1 -1
  69. package/build/lib/extension/index.js +13 -16
  70. package/build/lib/extension/index.js.map +1 -1
  71. package/build/lib/extension/manifest.d.ts +3 -2
  72. package/build/lib/extension/manifest.d.ts.map +1 -1
  73. package/build/lib/extension/manifest.js +19 -22
  74. package/build/lib/extension/manifest.js.map +1 -1
  75. package/build/lib/extension/plugin-config.d.ts.map +1 -1
  76. package/build/lib/extension/plugin-config.js +7 -9
  77. package/build/lib/extension/plugin-config.js.map +1 -1
  78. package/build/lib/helpers/build.d.ts +0 -1
  79. package/build/lib/helpers/build.d.ts.map +1 -1
  80. package/build/lib/helpers/build.js +2 -4
  81. package/build/lib/helpers/build.js.map +1 -1
  82. package/build/lib/helpers/capability.d.ts.map +1 -1
  83. package/build/lib/helpers/capability.js +27 -22
  84. package/build/lib/helpers/capability.js.map +1 -1
  85. package/build/lib/helpers/network.d.ts.map +1 -1
  86. package/build/lib/helpers/network.js +4 -3
  87. package/build/lib/helpers/network.js.map +1 -1
  88. package/build/lib/insecure-features.d.ts.map +1 -1
  89. package/build/lib/insecure-features.js +9 -13
  90. package/build/lib/insecure-features.js.map +1 -1
  91. package/build/lib/inspector-commands.d.ts.map +1 -1
  92. package/build/lib/inspector-commands.js +29 -28
  93. package/build/lib/inspector-commands.js.map +1 -1
  94. package/build/lib/logsink.d.ts.map +1 -1
  95. package/build/lib/logsink.js +8 -7
  96. package/build/lib/logsink.js.map +1 -1
  97. package/build/lib/schema/arg-spec.d.ts.map +1 -1
  98. package/build/lib/schema/arg-spec.js +5 -8
  99. package/build/lib/schema/arg-spec.js.map +1 -1
  100. package/build/lib/schema/cli-args.d.ts.map +1 -1
  101. package/build/lib/schema/cli-args.js +20 -19
  102. package/build/lib/schema/cli-args.js.map +1 -1
  103. package/build/lib/schema/cli-transformers.d.ts.map +1 -1
  104. package/build/lib/schema/cli-transformers.js.map +1 -1
  105. package/build/lib/schema/schema.d.ts.map +1 -1
  106. package/build/lib/schema/schema.js +28 -21
  107. package/build/lib/schema/schema.js.map +1 -1
  108. package/build/lib/utils/hash.d.ts +5 -0
  109. package/build/lib/utils/hash.d.ts.map +1 -0
  110. package/build/lib/{utils.js → utils/hash.js} +3 -21
  111. package/build/lib/utils/hash.js.map +1 -0
  112. package/build/lib/utils/index.d.ts +8 -0
  113. package/build/lib/utils/index.d.ts.map +1 -0
  114. package/build/lib/utils/index.js +31 -0
  115. package/build/lib/utils/index.js.map +1 -0
  116. package/build/lib/utils/is-package-changed.d.ts +16 -0
  117. package/build/lib/utils/is-package-changed.d.ts.map +1 -0
  118. package/build/lib/utils/is-package-changed.js +62 -0
  119. package/build/lib/utils/is-package-changed.js.map +1 -0
  120. package/build/lib/utils/object.d.ts +120 -0
  121. package/build/lib/utils/object.d.ts.map +1 -0
  122. package/build/lib/utils/object.js +288 -0
  123. package/build/lib/utils/object.js.map +1 -0
  124. package/build/lib/{extension → utils}/package-changed.d.ts +0 -1
  125. package/build/lib/utils/package-changed.d.ts.map +1 -0
  126. package/build/lib/{extension → utils}/package-changed.js +3 -4
  127. package/build/lib/utils/package-changed.js.map +1 -0
  128. package/build/lib/utils/package-json.d.ts +9 -0
  129. package/build/lib/utils/package-json.d.ts.map +1 -0
  130. package/build/lib/utils/package-json.js +32 -0
  131. package/build/lib/utils/package-json.js.map +1 -0
  132. package/build/lib/utils/resolve-from.d.ts +11 -0
  133. package/build/lib/utils/resolve-from.d.ts.map +1 -0
  134. package/build/lib/utils/resolve-from.js +41 -0
  135. package/build/lib/utils/resolve-from.js.map +1 -0
  136. package/build/types/cli.d.ts.map +1 -1
  137. package/build/types/index.d.ts.map +1 -1
  138. package/build/types/manifest/base.d.ts.map +1 -1
  139. package/build/types/manifest/v3.d.ts.map +1 -1
  140. package/build/types/manifest/v4.d.ts.map +1 -1
  141. package/lib/appium.ts +59 -55
  142. package/lib/bidi-commands.ts +106 -60
  143. package/lib/bootstrap/appium-initializer.ts +27 -19
  144. package/lib/bootstrap/appium-main-runner.ts +8 -4
  145. package/lib/bootstrap/config-file.ts +33 -16
  146. package/lib/bootstrap/grid-v3-register.ts +26 -19
  147. package/lib/bootstrap/main-helpers.ts +25 -19
  148. package/lib/bootstrap/node-helpers.ts +19 -22
  149. package/lib/bootstrap/startup-config.ts +42 -39
  150. package/lib/cli/args.ts +3 -4
  151. package/lib/cli/driver-command.ts +19 -13
  152. package/lib/cli/extension-command.ts +208 -143
  153. package/lib/cli/extension.ts +4 -6
  154. package/lib/cli/parser.ts +32 -30
  155. package/lib/cli/plugin-command.ts +20 -9
  156. package/lib/cli/setup-command.ts +54 -39
  157. package/lib/cli/utils.ts +4 -3
  158. package/lib/doctor/doctor.ts +15 -17
  159. package/lib/extension/driver-config.ts +22 -17
  160. package/lib/extension/extension-config.ts +74 -66
  161. package/lib/extension/index.ts +42 -25
  162. package/lib/extension/manifest.ts +34 -31
  163. package/lib/extension/plugin-config.ts +15 -10
  164. package/lib/helpers/build.ts +6 -5
  165. package/lib/helpers/capability.ts +40 -35
  166. package/lib/helpers/network.ts +6 -5
  167. package/lib/insecure-features.ts +16 -14
  168. package/lib/inspector-commands.ts +72 -40
  169. package/lib/logsink.ts +22 -26
  170. package/lib/schema/arg-spec.ts +9 -8
  171. package/lib/schema/cli-args-guards.ts +4 -4
  172. package/lib/schema/cli-args.ts +25 -21
  173. package/lib/schema/cli-transformers.ts +6 -2
  174. package/lib/schema/format-errors.ts +1 -1
  175. package/lib/schema/schema.ts +52 -38
  176. package/lib/{utils.ts → utils/hash.ts} +2 -19
  177. package/lib/utils/index.ts +23 -0
  178. package/lib/utils/is-package-changed.ts +78 -0
  179. package/lib/utils/object.ts +312 -0
  180. package/lib/{extension → utils}/package-changed.ts +6 -7
  181. package/lib/utils/package-json.ts +36 -0
  182. package/lib/utils/resolve-from.ts +40 -0
  183. package/package.json +13 -16
  184. package/scripts/autoinstall-extensions.js +8 -8
  185. package/tsconfig.json +8 -2
  186. package/types/cli.ts +18 -18
  187. package/types/index.ts +2 -2
  188. package/types/manifest/base.ts +2 -2
  189. package/types/manifest/v3.ts +2 -2
  190. package/types/manifest/v4.ts +2 -2
  191. package/build/lib/extension/package-changed.d.ts.map +0 -1
  192. package/build/lib/extension/package-changed.js.map +0 -1
  193. package/build/lib/utils.d.ts +0 -13
  194. package/build/lib/utils.d.ts.map +0 -1
  195. package/build/lib/utils.js.map +0 -1
@@ -1,7 +1,8 @@
1
1
  import Ajv, {type ErrorObject, type SchemaObject, type ValidateFunction} from 'ajv';
2
2
  import addFormats from 'ajv-formats';
3
- import _ from 'lodash';
3
+ import {util} from '@appium/support';
4
4
  import path from 'node:path';
5
+ import {bindAll, kebabCase, omitKeys, setPath} from '../utils';
5
6
  import {AppiumConfigJsonSchema} from '@appium/schema';
6
7
  import type {ExtensionType} from '@appium/types';
7
8
  import {DRIVER_TYPE, PLUGIN_TYPE} from '../constants';
@@ -40,7 +41,7 @@ export class RoachHotelMap<K, V> extends Map<K, V> {
40
41
  }
41
42
 
42
43
  export const ALLOWED_SCHEMA_EXTENSIONS = Object.freeze(
43
- new Set<AllowedSchemaExtension>(['.json', '.js', '.cjs'])
44
+ new Set<AllowedSchemaExtension>(['.json', '.js', '.cjs']),
44
45
  );
45
46
 
46
47
  const SCHEMA_KEY = '$schema';
@@ -66,7 +67,7 @@ class AppiumSchema {
66
67
  if (!AppiumSchema.#instance) {
67
68
  const instance = new AppiumSchema();
68
69
  AppiumSchema.#instance = instance;
69
- _.bindAll(instance, [
70
+ bindAll(instance, [
70
71
  'finalize',
71
72
  'flatten',
72
73
  'getAllArgSpecs',
@@ -96,7 +97,7 @@ class AppiumSchema {
96
97
  * Returns `true` if schema is a plain object and not async.
97
98
  */
98
99
  static isSupportedSchemaType(schema: any): schema is SchemaObject {
99
- return _.isPlainObject(schema) && (schema as any).$async !== true;
100
+ return util.isPlainObject(schema) && (schema as any).$async !== true;
100
101
  }
101
102
 
102
103
  /**
@@ -107,11 +108,11 @@ class AppiumSchema {
107
108
  new Ajv({
108
109
  // without this not much validation actually happens
109
110
  allErrors: true,
110
- })
111
+ }),
111
112
  );
112
- _.forEach(keywords, (keyword) => {
113
+ for (const keyword of Object.values(keywords)) {
113
114
  ajv.addKeyword(keyword);
114
- });
115
+ }
115
116
  return ajv;
116
117
  }
117
118
 
@@ -145,12 +146,12 @@ class AppiumSchema {
145
146
  }
146
147
 
147
148
  const ajv = this.#ajv;
148
- const baseSchema = _.cloneDeep(AppiumConfigJsonSchema) as any;
149
+ const baseSchema = structuredClone(AppiumConfigJsonSchema) as any;
149
150
 
150
151
  const addArgSpecs = (
151
152
  schema: Record<string, any>,
152
153
  extType?: ExtensionType,
153
- extName?: string
154
+ extName?: string,
154
155
  ): void => {
155
156
  for (const [propName, propSchema] of Object.entries(schema)) {
156
157
  const argSpec = ArgSpec.create(propName, {
@@ -163,7 +164,7 @@ class AppiumSchema {
163
164
  }
164
165
  };
165
166
 
166
- addArgSpecs(_.omit(baseSchema.properties.server.properties, [DRIVER_TYPE, PLUGIN_TYPE]));
167
+ addArgSpecs(omitKeys(baseSchema.properties.server.properties, [DRIVER_TYPE, PLUGIN_TYPE]));
167
168
 
168
169
  const finalizedSchemas: Record<string, StrictSchemaObject> = {};
169
170
 
@@ -174,7 +175,10 @@ class AppiumSchema {
174
175
  const $ref = ArgSpec.toSchemaBaseRef(extType, extName);
175
176
  (schema as any).$id = $ref;
176
177
  (schema as any).additionalProperties = false;
177
- baseSchema.properties.server.properties[extType].properties[extName] = {$ref, $comment: extName};
178
+ baseSchema.properties.server.properties[extType].properties[extName] = {
179
+ $ref,
180
+ $comment: extName,
181
+ };
178
182
  await ajv.validateSchema(schema, true);
179
183
  addArgSpecs((schema as any).properties, extType, extName);
180
184
  ajv.addSchema(schema, $ref);
@@ -207,16 +211,20 @@ class AppiumSchema {
207
211
  /**
208
212
  * Registers an extension schema.
209
213
  */
210
- async registerSchema(extType: ExtensionType, extName: string, schema: SchemaObject): Promise<void> {
211
- if (!(extType && extName) || _.isUndefined(schema)) {
214
+ async registerSchema(
215
+ extType: ExtensionType,
216
+ extName: string,
217
+ schema: SchemaObject,
218
+ ): Promise<void> {
219
+ if (!(extType && extName) || schema === undefined) {
212
220
  throw new TypeError('Expected extension type, extension name, and a defined schema');
213
221
  }
214
222
  if (!AppiumSchema.isSupportedSchemaType(schema)) {
215
223
  throw new SchemaUnsupportedSchemaError(schema, extType, extName);
216
224
  }
217
- const normalizedExtName = _.kebabCase(extName);
225
+ const normalizedExtName = kebabCase(extName);
218
226
  if (this.hasRegisteredSchema(extType, normalizedExtName)) {
219
- if (_.isEqual(this.#registeredSchemas[extType].get(normalizedExtName), schema)) {
227
+ if (util.isEqual(this.#registeredSchemas[extType].get(normalizedExtName), schema)) {
220
228
  return;
221
229
  }
222
230
  throw new SchemaNameConflictError(extType, extName);
@@ -243,7 +251,7 @@ class AppiumSchema {
243
251
  * Returns default values for all args, flattened or nested.
244
252
  */
245
253
  getDefaults<Flattened extends boolean | undefined = true>(
246
- flatten = true as Flattened
254
+ flatten = true as Flattened,
247
255
  ): DefaultValues<Flattened> {
248
256
  if (!this.isFinalized()) {
249
257
  throw new SchemaFinalizationError();
@@ -251,14 +259,14 @@ class AppiumSchema {
251
259
 
252
260
  const reducer = flatten
253
261
  ? (defaults: any, {defaultValue, dest}: ArgSpec) => {
254
- if (!_.isUndefined(defaultValue)) {
262
+ if (defaultValue !== undefined) {
255
263
  defaults[dest] = defaultValue;
256
264
  }
257
265
  return defaults;
258
266
  }
259
267
  : (defaults: any, {defaultValue, dest}: ArgSpec) => {
260
- if (!_.isUndefined(defaultValue)) {
261
- _.set(defaults, dest, defaultValue);
268
+ if (defaultValue !== undefined) {
269
+ setPath(defaults as Record<string, unknown>, dest, defaultValue);
262
270
  }
263
271
  return defaults;
264
272
  };
@@ -272,20 +280,23 @@ class AppiumSchema {
272
280
  */
273
281
  getDefaultsForExtension(
274
282
  extType: ExtensionType,
275
- extName: string
283
+ extName: string,
276
284
  ): Record<string, ArgSpecDefaultValue> {
277
285
  if (!this.isFinalized()) {
278
286
  throw new SchemaFinalizationError();
279
287
  }
280
288
  const specs = [...this.#argSpecs.values()].filter(
281
- (spec) => spec.extType === extType && spec.extName === extName
289
+ (spec) => spec.extType === extType && spec.extName === extName,
290
+ );
291
+ return specs.reduce(
292
+ (defaults, {defaultValue, rawDest}) => {
293
+ if (defaultValue !== undefined) {
294
+ defaults[rawDest] = defaultValue;
295
+ }
296
+ return defaults;
297
+ },
298
+ {} as Record<string, ArgSpecDefaultValue>,
282
299
  );
283
- return specs.reduce((defaults, {defaultValue, rawDest}) => {
284
- if (!_.isUndefined(defaultValue)) {
285
- defaults[rawDest] = defaultValue;
286
- }
287
- return defaults;
288
- }, {} as Record<string, ArgSpecDefaultValue>);
289
300
  }
290
301
 
291
302
  /**
@@ -299,7 +310,7 @@ class AppiumSchema {
299
310
  const flattened: FlattenedSchema = [];
300
311
 
301
312
  for (const {properties, prefix} of stack) {
302
- const pairs = _.toPairs(properties);
313
+ const pairs = Object.entries(properties);
303
314
  for (const [key, value] of pairs) {
304
315
  if (key === SCHEMA_KEY) {
305
316
  continue;
@@ -317,19 +328,22 @@ class AppiumSchema {
317
328
  const {normalizedExtName} = ArgSpec.extensionInfoFromRootSchemaId($ref);
318
329
  if (!normalizedExtName) {
319
330
  throw new ReferenceError(
320
- `Could not determine extension name from schema ID ${$ref}. This is a bug.`
331
+ `Could not determine extension name from schema ID ${$ref}. This is a bug.`,
321
332
  );
322
333
  }
323
- stack.push({properties: refSchema.properties, prefix: [...prefix, key, normalizedExtName]});
334
+ stack.push({
335
+ properties: refSchema.properties,
336
+ prefix: [...prefix, key, normalizedExtName],
337
+ });
324
338
  } else if (key !== DRIVER_TYPE && key !== PLUGIN_TYPE) {
325
339
  const [extType, extName] = prefix;
326
340
  const argSpec = this.getArgSpec(key, extType as ExtensionType, extName);
327
341
  if (!argSpec) {
328
342
  throw new ReferenceError(
329
- `Unknown argument with key ${key}, extType ${extType} and extName ${extName}. This is a bug.`
343
+ `Unknown argument with key ${key}, extType ${extType} and extName ${extName}. This is a bug.`,
330
344
  );
331
345
  }
332
- flattened.push({schema: _.cloneDeep(value as SchemaObject), argSpec});
346
+ flattened.push({schema: structuredClone(value as SchemaObject), argSpec});
333
347
  }
334
348
  }
335
349
  }
@@ -348,7 +362,7 @@ class AppiumSchema {
348
362
  */
349
363
  validate(value: any, ref = APPIUM_CONFIG_SCHEMA_ID): ErrorObject[] {
350
364
  const validator = this._getValidator(ref);
351
- return !validator(value) && _.isArray(validator.errors) ? [...validator.errors] : [];
365
+ return !validator(value) && Array.isArray(validator.errors) ? [...validator.errors] : [];
352
366
  }
353
367
 
354
368
  /**
@@ -408,21 +422,21 @@ export class SchemaUnsupportedSchemaError extends TypeError {
408
422
  super(
409
423
  (() => {
410
424
  const msg = `Unsupported schema from ${extType} "${extName}":`;
411
- if (_.isBoolean(schema)) {
425
+ if (typeof schema === 'boolean') {
412
426
  return `${msg} schema cannot be a boolean`;
413
427
  }
414
- if (_.isPlainObject(schema)) {
428
+ if (util.isPlainObject(schema)) {
415
429
  if ((schema as any).$async) {
416
430
  return `${msg} schema cannot be an async schema`;
417
431
  }
418
432
  throw new TypeError(
419
433
  `schema IS supported; this error should not be thrown (this is a bug). value of schema: ${JSON.stringify(
420
- schema
421
- )}`
434
+ schema,
435
+ )}`,
422
436
  );
423
437
  }
424
438
  return `${msg} schema must be a plain object without a true "$async" property`;
425
- })()
439
+ })(),
426
440
  );
427
441
  this.data = {schema, extType, extName};
428
442
  }
@@ -1,21 +1,3 @@
1
- import _ from 'lodash';
2
- import {node, fs} from '@appium/support';
3
-
4
- export const npmPackage = fs.readPackageJsonFrom(__dirname);
5
-
6
- /**
7
- * Returns the root directory of the Appium module (memoized).
8
- *
9
- * @throws {Error} If the appium module root cannot be determined.
10
- */
11
- export const getAppiumModuleRoot = _.memoize(function getAppiumModuleRoot(): string {
12
- const selfRoot = node.getModuleRootSync('appium', __filename);
13
- if (!selfRoot) {
14
- throw new Error('Cannot find the appium module root. This is likely a bug in Appium.');
15
- }
16
- return selfRoot;
17
- });
18
-
19
1
  /**
20
2
  * Adler-32 checksum (see https://github.com/SheetJS/js-adler32).
21
3
  */
@@ -27,7 +9,8 @@ export function adler32(str: string, seed: number | null = null): number {
27
9
  a = seed & 0xffff;
28
10
  b = seed >>> 16;
29
11
  }
30
- for (let i = 0; i < L;) {
12
+ let i = 0;
13
+ while (i < L) {
31
14
  let M = Math.min(L - i, 2918);
32
15
  while (M > 0) {
33
16
  let c = str.charCodeAt(i++);
@@ -0,0 +1,23 @@
1
+ export {adler32} from './hash';
2
+ export {isPackageChanged} from './is-package-changed';
3
+ export type {IsPackageChangedOptions, IsPackageChangedResult} from './is-package-changed';
4
+ export {appiumPackageRoot, npmPackage} from './package-json';
5
+ export {packageDidChange} from './package-changed';
6
+ export {resolveFrom} from './resolve-from';
7
+ export {
8
+ bindAll,
9
+ camelCase,
10
+ capitalize,
11
+ compact,
12
+ defaultsDeep,
13
+ difference,
14
+ getPath,
15
+ kebabCase,
16
+ mapKeys,
17
+ mapValues,
18
+ omitKeys,
19
+ pickBy,
20
+ pull,
21
+ setPath,
22
+ zip,
23
+ } from './object';
@@ -0,0 +1,78 @@
1
+ import {fs} from '@appium/support';
2
+ import crypto from 'node:crypto';
3
+ import path from 'node:path';
4
+
5
+ export interface IsPackageChangedOptions {
6
+ cwd?: string;
7
+ hashFilename?: string;
8
+ }
9
+
10
+ export interface IsPackageChangedResult {
11
+ hash: string;
12
+ isChanged: boolean;
13
+ oldHash?: string;
14
+ writeHash: () => Promise<void>;
15
+ }
16
+
17
+ /**
18
+ * Detects whether `package.json` dependencies changed since the last hash write.
19
+ * Inlined from the `package-changed` package (lockfile support omitted; unused by Appium).
20
+ */
21
+ export async function isPackageChanged(
22
+ options: IsPackageChangedOptions = {},
23
+ ): Promise<IsPackageChangedResult> {
24
+ const {hashFilename = '.packagehash', cwd = process.cwd()} = options;
25
+ const packagePath = await findPackageJson(cwd);
26
+ if (!packagePath) {
27
+ throw new Error(`Cannot find package.json travelling up from "${cwd}".`);
28
+ }
29
+
30
+ const packageHashPath = path.join(cwd, hashFilename);
31
+ const packageHashPathExists = await fs.exists(packageHashPath);
32
+ const recentDigest = await getPackageHash(packagePath);
33
+ const previousDigest = packageHashPathExists
34
+ ? await fs.readFile(packageHashPath, 'utf-8')
35
+ : undefined;
36
+ const isChanged = !packageHashPathExists || previousDigest !== recentDigest;
37
+
38
+ const writeHash = async () => {
39
+ await fs.writeFile(packageHashPath, recentDigest, 'utf-8');
40
+ };
41
+
42
+ return {
43
+ hash: recentDigest,
44
+ isChanged,
45
+ oldHash: previousDigest || undefined,
46
+ writeHash,
47
+ };
48
+ }
49
+
50
+ async function findPackageJson(cwd: string): Promise<string | undefined> {
51
+ let current = cwd;
52
+ while (true) {
53
+ const search = path.join(current, 'package.json');
54
+ if (await fs.exists(search)) {
55
+ return search;
56
+ }
57
+ const parent = path.dirname(current);
58
+ if (parent === current) {
59
+ return;
60
+ }
61
+ current = parent;
62
+ }
63
+ }
64
+
65
+ async function getPackageHash(packagePath: string): Promise<string> {
66
+ const contents = await fs.readFile(packagePath, 'utf-8');
67
+ const packageBlob = JSON.parse(contents) as {
68
+ dependencies?: Record<string, string>;
69
+ devDependencies?: Record<string, string>;
70
+ };
71
+ const dependencies = {
72
+ dependencies: packageBlob.dependencies ?? {},
73
+ devDependencies: packageBlob.devDependencies ?? {},
74
+ };
75
+ const hashSum = crypto.createHash('md5');
76
+ hashSum.update(Buffer.from(JSON.stringify(dependencies)));
77
+ return hashSum.digest('hex');
78
+ }
@@ -0,0 +1,312 @@
1
+ import {util} from '@appium/support';
2
+
3
+ const UNSAFE_PATH_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
4
+
5
+ /**
6
+ * Converts a string to kebab-case (for Appium CLI and schema property names).
7
+ *
8
+ * @param value - Input string
9
+ * @returns Kebab-cased string
10
+ */
11
+ export function kebabCase(value: string): string {
12
+ return splitWords(value).join('-').toLowerCase();
13
+ }
14
+
15
+ /**
16
+ * Converts a string to camelCase.
17
+ *
18
+ * @param value - Input string
19
+ * @returns camelCased string
20
+ */
21
+ export function camelCase(value: string): string {
22
+ const words = splitWords(value);
23
+ if (words.length === 0) {
24
+ return '';
25
+ }
26
+ return (
27
+ words[0].toLowerCase() +
28
+ words
29
+ .slice(1)
30
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())
31
+ .join('')
32
+ );
33
+ }
34
+
35
+ /**
36
+ * Uppercases the first character of a string.
37
+ *
38
+ * @param value - Input string
39
+ * @returns Capitalized string, or empty string when input is empty
40
+ */
41
+ export function capitalize(value: string): string {
42
+ return value ? value.charAt(0).toUpperCase() + value.slice(1) : value;
43
+ }
44
+
45
+ /**
46
+ * Returns a shallow copy of `obj` without any of `keys`. Non-plain objects are returned unchanged.
47
+ *
48
+ * @param obj - Source object
49
+ * @param keys - Property names to omit
50
+ * @returns Shallow copy without the listed keys
51
+ */
52
+ export function omitKeys<T extends Record<string, unknown>>(obj: T, keys: readonly string[]): T {
53
+ if (!util.isPlainObject(obj) || keys.length === 0) {
54
+ return obj;
55
+ }
56
+ const omit = new Set(keys);
57
+ return Object.fromEntries(Object.entries(obj).filter(([k]) => !omit.has(k))) as T;
58
+ }
59
+
60
+ /**
61
+ * Returns a shallow copy of `obj` whose entries pass `predicate`.
62
+ *
63
+ * @param obj - Source object
64
+ * @param predicate - Filter invoked with each value and key
65
+ * @returns Shallow copy containing only matching entries
66
+ */
67
+ export function pickBy<T extends Record<string, unknown>>(
68
+ obj: T,
69
+ predicate: (value: T[keyof T], key: keyof T) => boolean,
70
+ ): Partial<T> {
71
+ return Object.fromEntries(
72
+ Object.entries(obj).filter(([key, value]) => predicate(value as T[keyof T], key as keyof T)),
73
+ ) as Partial<T>;
74
+ }
75
+
76
+ /**
77
+ * Returns an object with the same keys as `obj` and values transformed by `fn`.
78
+ *
79
+ * @param obj - Source object
80
+ * @param fn - Mapper invoked with each value and key
81
+ * @returns Object with transformed values
82
+ */
83
+ export function mapValues<T extends Record<string, unknown>, R>(
84
+ obj: T,
85
+ fn: (value: T[keyof T], key: keyof T) => R,
86
+ ): Record<string, R> {
87
+ return Object.fromEntries(
88
+ Object.entries(obj).map(([key, value]) => [key, fn(value as T[keyof T], key as keyof T)]),
89
+ );
90
+ }
91
+
92
+ /**
93
+ * Returns an object with keys renamed by `fn` and original values preserved.
94
+ *
95
+ * @param obj - Source object
96
+ * @param fn - Key mapper invoked with each value and key
97
+ * @returns Object with renamed keys
98
+ */
99
+ export function mapKeys<T extends Record<string, unknown>>(
100
+ obj: T,
101
+ fn: (value: T[keyof T], key: keyof T) => string,
102
+ ): Record<string, unknown> {
103
+ return Object.fromEntries(
104
+ Object.entries(obj).map(([key, value]) => [fn(value as T[keyof T], key as keyof T), value]),
105
+ );
106
+ }
107
+
108
+ /**
109
+ * Reads a dot-separated path on `obj`, returning `defaultValue` when any segment is missing.
110
+ *
111
+ * @param obj - Object to read from
112
+ * @param path - Dot-separated property path
113
+ * @param defaultValue - Value returned when the path cannot be resolved
114
+ * @returns Value at the path, or `defaultValue`
115
+ */
116
+ export function getPath(obj: unknown, path: string, defaultValue?: unknown): unknown {
117
+ const parts = path.split('.');
118
+ let current: unknown = obj;
119
+ for (const part of parts) {
120
+ if (current == null || (typeof current !== 'object' && typeof current !== 'function')) {
121
+ return defaultValue;
122
+ }
123
+ current = (current as Record<string, unknown>)[part];
124
+ }
125
+ return current === undefined ? defaultValue : current;
126
+ }
127
+
128
+ /**
129
+ * Assigns `value` at a dot-separated path on `obj`, creating plain object segments as needed.
130
+ *
131
+ * No-ops when the path contains empty segments or keys that could trigger prototype pollution
132
+ * (`__proto__`, `constructor`, `prototype`).
133
+ *
134
+ * @param obj - Object to mutate
135
+ * @param path - Dot-separated property path
136
+ * @param value - Value to assign at the path
137
+ */
138
+ export function setPath(obj: Record<string, unknown>, path: string, value: unknown): void {
139
+ const parts = path.split('.');
140
+ if (!parts.every(isSafePathKey)) {
141
+ return;
142
+ }
143
+ let current = obj;
144
+ for (let i = 0; i < parts.length - 1; i++) {
145
+ const part = parts[i];
146
+ const next = current[part];
147
+ if (!util.isPlainObject(next)) {
148
+ current[part] = {};
149
+ }
150
+ current = current[part] as Record<string, unknown>;
151
+ }
152
+ current[parts[parts.length - 1]] = value;
153
+ }
154
+
155
+ /**
156
+ * Binds listed methods on `obj` so they keep the correct `this` when passed as callbacks.
157
+ *
158
+ * @param obj - Target object
159
+ * @param methodNames - Method names to bind
160
+ * @returns `obj` (mutated in place)
161
+ */
162
+ export function bindAll<T extends object>(obj: T, methodNames: readonly string[]): T {
163
+ for (const name of methodNames) {
164
+ const method = (obj as Record<string, unknown>)[name];
165
+ if (typeof method === 'function') {
166
+ (obj as Record<string, unknown>)[name] = method.bind(obj);
167
+ }
168
+ }
169
+ return obj;
170
+ }
171
+
172
+ /**
173
+ * Returns a copy of `arr` with falsy entries removed.
174
+ *
175
+ * @param arr - Input array
176
+ * @returns Array containing only truthy elements
177
+ */
178
+ export function compact<T>(arr: Array<T | null | undefined | false | '' | 0>): T[] {
179
+ return arr.filter((x): x is T => Boolean(x));
180
+ }
181
+
182
+ /**
183
+ * Removes all occurrences of `values` from `arr` (mutates `arr`).
184
+ *
185
+ * @param arr - Array to modify
186
+ * @param values - Values to remove
187
+ * @returns `arr`
188
+ */
189
+ export function pull<T>(arr: T[], ...values: T[]): T[] {
190
+ for (const v of values) {
191
+ let idx: number;
192
+ while ((idx = arr.indexOf(v)) !== -1) {
193
+ arr.splice(idx, 1);
194
+ }
195
+ }
196
+ return arr;
197
+ }
198
+
199
+ /**
200
+ * Zips two arrays into tuples `[a[i], b[i]]`.
201
+ *
202
+ * @param a - First array
203
+ * @param b - Second array
204
+ * @returns Array of zipped pairs
205
+ */
206
+ export function zip<A, B>(a: readonly A[], b: readonly B[]): Array<[A, B | undefined]> {
207
+ return a.map((item, i) => [item, b[i]]);
208
+ }
209
+
210
+ /**
211
+ * Returns elements in `a` that are not present in `b`.
212
+ *
213
+ * @param a - Source array
214
+ * @param b - Values to exclude
215
+ * @returns Filtered array
216
+ */
217
+ export function difference<T>(a: readonly T[], b: readonly T[]): T[] {
218
+ const bSet = new Set(b);
219
+ return a.filter((x) => !bSet.has(x));
220
+ }
221
+
222
+ /**
223
+ * Deep-defaults merge: each source fills only `undefined` properties in the result (recursive for plain objects).
224
+ *
225
+ * @param sources - Objects merged in order; null/undefined sources are skipped
226
+ * @returns Merged object
227
+ */
228
+ export function defaultsDeep<T extends Record<string, unknown>>(
229
+ ...sources: Array<Partial<T> | undefined>
230
+ ): T {
231
+ let result: Record<string, unknown> = {};
232
+ for (const source of sources) {
233
+ if (source == null) {
234
+ continue;
235
+ }
236
+ result = fillUndefinedDeep(result, source as Record<string, unknown>);
237
+ }
238
+ return result as T;
239
+ }
240
+
241
+ /**
242
+ * Splits a string into words (lodash-compatible), including acronym boundaries.
243
+ *
244
+ * @param value - Input string
245
+ * @returns Word tokens
246
+ */
247
+ function splitWords(value: string): string[] {
248
+ return value
249
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
250
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
251
+ .replace(/[-_\s]+/g, ' ')
252
+ .trim()
253
+ .split(/\s+/)
254
+ .filter(Boolean);
255
+ }
256
+
257
+ function isSafePathKey(key: string): boolean {
258
+ return key.length > 0 && !UNSAFE_PATH_KEYS.has(key);
259
+ }
260
+
261
+ /**
262
+ * Copies a value for {@link defaultsDeep}: plain objects are copied recursively;
263
+ * other values (including functions) are reused by reference.
264
+ *
265
+ * @param value - Value to copy
266
+ * @returns Copy safe for further defaults merging
267
+ */
268
+ function copyForDefaultsDeep(value: unknown): unknown {
269
+ if (!util.isPlainObject(value)) {
270
+ return value;
271
+ }
272
+ const out: Record<string, unknown> = {};
273
+ for (const [key, val] of Object.entries(value)) {
274
+ out[key] = copyForDefaultsDeep(val);
275
+ }
276
+ return out;
277
+ }
278
+
279
+ /**
280
+ * Fills `undefined` keys in a clone of `target` from `source` (recursive for plain objects).
281
+ *
282
+ * @param target - Destination object
283
+ * @param source - Default values
284
+ * @returns Merged clone
285
+ */
286
+ function fillUndefinedDeep(
287
+ target: Record<string, unknown>,
288
+ source: Record<string, unknown>,
289
+ ): Record<string, unknown> {
290
+ const out = copyForDefaultsDeep(target) as Record<string, unknown>;
291
+ const stack: Array<{dest: Record<string, unknown>; src: Record<string, unknown>}> = [
292
+ {dest: out, src: source},
293
+ ];
294
+ while (stack.length) {
295
+ const next = stack.pop();
296
+ if (!next) {
297
+ continue;
298
+ }
299
+ const {dest, src} = next;
300
+ for (const [key, srcVal] of Object.entries(src)) {
301
+ const destVal = dest[key];
302
+ if (destVal === undefined) {
303
+ dest[key] = copyForDefaultsDeep(srcVal);
304
+ continue;
305
+ }
306
+ if (util.isPlainObject(destVal) && util.isPlainObject(srcVal)) {
307
+ stack.push({dest: destVal, src: srcVal});
308
+ }
309
+ }
310
+ }
311
+ return out;
312
+ }