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.
- package/build/lib/appium.d.ts.map +1 -1
- package/build/lib/appium.js +43 -45
- package/build/lib/appium.js.map +1 -1
- package/build/lib/bidi-commands.d.ts.map +1 -1
- package/build/lib/bidi-commands.js +34 -29
- package/build/lib/bidi-commands.js.map +1 -1
- package/build/lib/bootstrap/appium-initializer.d.ts.map +1 -1
- package/build/lib/bootstrap/appium-initializer.js +13 -13
- package/build/lib/bootstrap/appium-initializer.js.map +1 -1
- package/build/lib/bootstrap/appium-main-runner.d.ts.map +1 -1
- package/build/lib/bootstrap/appium-main-runner.js +2 -2
- package/build/lib/bootstrap/appium-main-runner.js.map +1 -1
- package/build/lib/bootstrap/config-file.d.ts.map +1 -1
- package/build/lib/bootstrap/config-file.js +11 -11
- package/build/lib/bootstrap/config-file.js.map +1 -1
- package/build/lib/bootstrap/grid-v3-register.d.ts.map +1 -1
- package/build/lib/bootstrap/grid-v3-register.js +9 -6
- package/build/lib/bootstrap/grid-v3-register.js.map +1 -1
- package/build/lib/bootstrap/main-helpers.d.ts +1 -1
- package/build/lib/bootstrap/main-helpers.d.ts.map +1 -1
- package/build/lib/bootstrap/main-helpers.js +19 -20
- package/build/lib/bootstrap/main-helpers.js.map +1 -1
- package/build/lib/bootstrap/node-helpers.d.ts +1 -1
- package/build/lib/bootstrap/node-helpers.d.ts.map +1 -1
- package/build/lib/bootstrap/node-helpers.js +7 -14
- package/build/lib/bootstrap/node-helpers.js.map +1 -1
- package/build/lib/bootstrap/startup-config.d.ts.map +1 -1
- package/build/lib/bootstrap/startup-config.js +32 -30
- package/build/lib/bootstrap/startup-config.js.map +1 -1
- package/build/lib/cli/args.d.ts +3 -2
- package/build/lib/cli/args.d.ts.map +1 -1
- package/build/lib/cli/args.js +2 -5
- package/build/lib/cli/args.js.map +1 -1
- package/build/lib/cli/driver-command.d.ts +1 -2
- package/build/lib/cli/driver-command.d.ts.map +1 -1
- package/build/lib/cli/driver-command.js +7 -8
- package/build/lib/cli/driver-command.js.map +1 -1
- package/build/lib/cli/extension-command.d.ts +2 -2
- package/build/lib/cli/extension-command.d.ts.map +1 -1
- package/build/lib/cli/extension-command.js +100 -66
- package/build/lib/cli/extension-command.js.map +1 -1
- package/build/lib/cli/extension.d.ts.map +1 -1
- package/build/lib/cli/extension.js.map +1 -1
- package/build/lib/cli/parser.d.ts.map +1 -1
- package/build/lib/cli/parser.js +21 -14
- package/build/lib/cli/parser.js.map +1 -1
- package/build/lib/cli/plugin-command.d.ts +1 -1
- package/build/lib/cli/plugin-command.d.ts.map +1 -1
- package/build/lib/cli/plugin-command.js +5 -5
- package/build/lib/cli/plugin-command.js.map +1 -1
- package/build/lib/cli/setup-command.d.ts +1 -1
- package/build/lib/cli/setup-command.d.ts.map +1 -1
- package/build/lib/cli/setup-command.js +14 -19
- package/build/lib/cli/setup-command.js.map +1 -1
- package/build/lib/cli/utils.d.ts.map +1 -1
- package/build/lib/cli/utils.js +3 -2
- package/build/lib/cli/utils.js.map +1 -1
- package/build/lib/doctor/doctor.d.ts +0 -1
- package/build/lib/doctor/doctor.d.ts.map +1 -1
- package/build/lib/doctor/doctor.js +12 -17
- package/build/lib/doctor/doctor.js.map +1 -1
- package/build/lib/extension/driver-config.d.ts.map +1 -1
- package/build/lib/extension/driver-config.js +11 -12
- package/build/lib/extension/driver-config.js.map +1 -1
- package/build/lib/extension/extension-config.d.ts.map +1 -1
- package/build/lib/extension/extension-config.js +28 -27
- package/build/lib/extension/extension-config.js.map +1 -1
- package/build/lib/extension/index.d.ts.map +1 -1
- package/build/lib/extension/index.js +13 -16
- package/build/lib/extension/index.js.map +1 -1
- package/build/lib/extension/manifest.d.ts +3 -2
- package/build/lib/extension/manifest.d.ts.map +1 -1
- package/build/lib/extension/manifest.js +19 -22
- package/build/lib/extension/manifest.js.map +1 -1
- package/build/lib/extension/plugin-config.d.ts.map +1 -1
- package/build/lib/extension/plugin-config.js +7 -9
- package/build/lib/extension/plugin-config.js.map +1 -1
- package/build/lib/helpers/build.d.ts +0 -1
- package/build/lib/helpers/build.d.ts.map +1 -1
- package/build/lib/helpers/build.js +2 -4
- package/build/lib/helpers/build.js.map +1 -1
- package/build/lib/helpers/capability.d.ts.map +1 -1
- package/build/lib/helpers/capability.js +27 -22
- package/build/lib/helpers/capability.js.map +1 -1
- package/build/lib/helpers/network.d.ts.map +1 -1
- package/build/lib/helpers/network.js +4 -3
- package/build/lib/helpers/network.js.map +1 -1
- package/build/lib/insecure-features.d.ts.map +1 -1
- package/build/lib/insecure-features.js +9 -13
- package/build/lib/insecure-features.js.map +1 -1
- package/build/lib/inspector-commands.d.ts.map +1 -1
- package/build/lib/inspector-commands.js +29 -28
- package/build/lib/inspector-commands.js.map +1 -1
- package/build/lib/logsink.d.ts.map +1 -1
- package/build/lib/logsink.js +8 -7
- package/build/lib/logsink.js.map +1 -1
- package/build/lib/schema/arg-spec.d.ts.map +1 -1
- package/build/lib/schema/arg-spec.js +5 -8
- package/build/lib/schema/arg-spec.js.map +1 -1
- package/build/lib/schema/cli-args.d.ts.map +1 -1
- package/build/lib/schema/cli-args.js +20 -19
- package/build/lib/schema/cli-args.js.map +1 -1
- package/build/lib/schema/cli-transformers.d.ts.map +1 -1
- package/build/lib/schema/cli-transformers.js.map +1 -1
- package/build/lib/schema/schema.d.ts.map +1 -1
- package/build/lib/schema/schema.js +28 -21
- package/build/lib/schema/schema.js.map +1 -1
- package/build/lib/utils/hash.d.ts +5 -0
- package/build/lib/utils/hash.d.ts.map +1 -0
- package/build/lib/{utils.js → utils/hash.js} +3 -21
- package/build/lib/utils/hash.js.map +1 -0
- package/build/lib/utils/index.d.ts +8 -0
- package/build/lib/utils/index.d.ts.map +1 -0
- package/build/lib/utils/index.js +31 -0
- package/build/lib/utils/index.js.map +1 -0
- package/build/lib/utils/is-package-changed.d.ts +16 -0
- package/build/lib/utils/is-package-changed.d.ts.map +1 -0
- package/build/lib/utils/is-package-changed.js +62 -0
- package/build/lib/utils/is-package-changed.js.map +1 -0
- package/build/lib/utils/object.d.ts +120 -0
- package/build/lib/utils/object.d.ts.map +1 -0
- package/build/lib/utils/object.js +288 -0
- package/build/lib/utils/object.js.map +1 -0
- package/build/lib/{extension → utils}/package-changed.d.ts +0 -1
- package/build/lib/utils/package-changed.d.ts.map +1 -0
- package/build/lib/{extension → utils}/package-changed.js +3 -4
- package/build/lib/utils/package-changed.js.map +1 -0
- package/build/lib/utils/package-json.d.ts +9 -0
- package/build/lib/utils/package-json.d.ts.map +1 -0
- package/build/lib/utils/package-json.js +32 -0
- package/build/lib/utils/package-json.js.map +1 -0
- package/build/lib/utils/resolve-from.d.ts +11 -0
- package/build/lib/utils/resolve-from.d.ts.map +1 -0
- package/build/lib/utils/resolve-from.js +41 -0
- package/build/lib/utils/resolve-from.js.map +1 -0
- package/build/types/cli.d.ts.map +1 -1
- package/build/types/index.d.ts.map +1 -1
- package/build/types/manifest/base.d.ts.map +1 -1
- package/build/types/manifest/v3.d.ts.map +1 -1
- package/build/types/manifest/v4.d.ts.map +1 -1
- package/lib/appium.ts +59 -55
- package/lib/bidi-commands.ts +106 -60
- package/lib/bootstrap/appium-initializer.ts +27 -19
- package/lib/bootstrap/appium-main-runner.ts +8 -4
- package/lib/bootstrap/config-file.ts +33 -16
- package/lib/bootstrap/grid-v3-register.ts +26 -19
- package/lib/bootstrap/main-helpers.ts +25 -19
- package/lib/bootstrap/node-helpers.ts +19 -22
- package/lib/bootstrap/startup-config.ts +42 -39
- package/lib/cli/args.ts +3 -4
- package/lib/cli/driver-command.ts +19 -13
- package/lib/cli/extension-command.ts +208 -143
- package/lib/cli/extension.ts +4 -6
- package/lib/cli/parser.ts +32 -30
- package/lib/cli/plugin-command.ts +20 -9
- package/lib/cli/setup-command.ts +54 -39
- package/lib/cli/utils.ts +4 -3
- package/lib/doctor/doctor.ts +15 -17
- package/lib/extension/driver-config.ts +22 -17
- package/lib/extension/extension-config.ts +74 -66
- package/lib/extension/index.ts +42 -25
- package/lib/extension/manifest.ts +34 -31
- package/lib/extension/plugin-config.ts +15 -10
- package/lib/helpers/build.ts +6 -5
- package/lib/helpers/capability.ts +40 -35
- package/lib/helpers/network.ts +6 -5
- package/lib/insecure-features.ts +16 -14
- package/lib/inspector-commands.ts +72 -40
- package/lib/logsink.ts +22 -26
- package/lib/schema/arg-spec.ts +9 -8
- package/lib/schema/cli-args-guards.ts +4 -4
- package/lib/schema/cli-args.ts +25 -21
- package/lib/schema/cli-transformers.ts +6 -2
- package/lib/schema/format-errors.ts +1 -1
- package/lib/schema/schema.ts +52 -38
- package/lib/{utils.ts → utils/hash.ts} +2 -19
- package/lib/utils/index.ts +23 -0
- package/lib/utils/is-package-changed.ts +78 -0
- package/lib/utils/object.ts +312 -0
- package/lib/{extension → utils}/package-changed.ts +6 -7
- package/lib/utils/package-json.ts +36 -0
- package/lib/utils/resolve-from.ts +40 -0
- package/package.json +13 -16
- package/scripts/autoinstall-extensions.js +8 -8
- package/tsconfig.json +8 -2
- package/types/cli.ts +18 -18
- package/types/index.ts +2 -2
- package/types/manifest/base.ts +2 -2
- package/types/manifest/v3.ts +2 -2
- package/types/manifest/v4.ts +2 -2
- package/build/lib/extension/package-changed.d.ts.map +0 -1
- package/build/lib/extension/package-changed.js.map +0 -1
- package/build/lib/utils.d.ts +0 -13
- package/build/lib/utils.d.ts.map +0 -1
- package/build/lib/utils.js.map +0 -1
package/lib/schema/schema.ts
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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(
|
|
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] = {
|
|
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(
|
|
211
|
-
|
|
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 =
|
|
225
|
+
const normalizedExtName = kebabCase(extName);
|
|
218
226
|
if (this.hasRegisteredSchema(extType, normalizedExtName)) {
|
|
219
|
-
if (
|
|
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 (
|
|
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 (
|
|
261
|
-
|
|
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 =
|
|
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({
|
|
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:
|
|
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) &&
|
|
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 (
|
|
425
|
+
if (typeof schema === 'boolean') {
|
|
412
426
|
return `${msg} schema cannot be a boolean`;
|
|
413
427
|
}
|
|
414
|
-
if (
|
|
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
|
-
|
|
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
|
+
}
|