appium 3.3.0 → 3.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. package/build/lib/appium.d.ts +147 -205
  2. package/build/lib/appium.d.ts.map +1 -1
  3. package/build/lib/appium.js +169 -282
  4. package/build/lib/appium.js.map +1 -1
  5. package/build/lib/bidi-commands.d.ts.map +1 -1
  6. package/build/lib/bidi-commands.js +11 -11
  7. package/build/lib/bidi-commands.js.map +1 -1
  8. package/build/lib/bootstrap/appium-initializer.d.ts +21 -0
  9. package/build/lib/bootstrap/appium-initializer.d.ts.map +1 -0
  10. package/build/lib/bootstrap/appium-initializer.js +146 -0
  11. package/build/lib/bootstrap/appium-initializer.js.map +1 -0
  12. package/build/lib/bootstrap/appium-main-runner.d.ts +22 -0
  13. package/build/lib/bootstrap/appium-main-runner.d.ts.map +1 -0
  14. package/build/lib/bootstrap/appium-main-runner.js +109 -0
  15. package/build/lib/bootstrap/appium-main-runner.js.map +1 -0
  16. package/build/lib/bootstrap/config-file.d.ts +37 -0
  17. package/build/lib/bootstrap/config-file.d.ts.map +1 -0
  18. package/build/lib/{config-file.js → bootstrap/config-file.js} +9 -26
  19. package/build/lib/bootstrap/config-file.js.map +1 -0
  20. package/build/lib/bootstrap/grid-v3-register.d.ts +20 -0
  21. package/build/lib/bootstrap/grid-v3-register.d.ts.map +1 -0
  22. package/build/lib/{grid-register.js → bootstrap/grid-v3-register.js} +28 -13
  23. package/build/lib/bootstrap/grid-v3-register.js.map +1 -0
  24. package/build/lib/bootstrap/init-types.d.ts +16 -0
  25. package/build/lib/bootstrap/init-types.d.ts.map +1 -0
  26. package/build/lib/bootstrap/init-types.js +3 -0
  27. package/build/lib/bootstrap/init-types.js.map +1 -0
  28. package/build/lib/bootstrap/main-helpers.d.ts +55 -0
  29. package/build/lib/bootstrap/main-helpers.d.ts.map +1 -0
  30. package/build/lib/bootstrap/main-helpers.js +187 -0
  31. package/build/lib/bootstrap/main-helpers.js.map +1 -0
  32. package/build/lib/bootstrap/node-helpers.d.ts +32 -0
  33. package/build/lib/bootstrap/node-helpers.d.ts.map +1 -0
  34. package/build/lib/bootstrap/node-helpers.js +201 -0
  35. package/build/lib/bootstrap/node-helpers.js.map +1 -0
  36. package/build/lib/bootstrap/startup-config.d.ts +22 -0
  37. package/build/lib/bootstrap/startup-config.d.ts.map +1 -0
  38. package/build/lib/bootstrap/startup-config.js +111 -0
  39. package/build/lib/bootstrap/startup-config.js.map +1 -0
  40. package/build/lib/cli/args.d.ts.map +1 -1
  41. package/build/lib/cli/args.js +9 -9
  42. package/build/lib/cli/args.js.map +1 -1
  43. package/build/lib/cli/extension-command.d.ts +95 -95
  44. package/build/lib/cli/extension-command.d.ts.map +1 -1
  45. package/build/lib/cli/extension-command.js +18 -18
  46. package/build/lib/cli/extension-command.js.map +1 -1
  47. package/build/lib/cli/extension.d.ts +1 -1
  48. package/build/lib/cli/extension.d.ts.map +1 -1
  49. package/build/lib/cli/extension.js +5 -5
  50. package/build/lib/cli/extension.js.map +1 -1
  51. package/build/lib/cli/parser.d.ts +8 -8
  52. package/build/lib/cli/parser.d.ts.map +1 -1
  53. package/build/lib/cli/parser.js +49 -49
  54. package/build/lib/cli/parser.js.map +1 -1
  55. package/build/lib/cli/setup-command.js +6 -6
  56. package/build/lib/cli/setup-command.js.map +1 -1
  57. package/build/lib/cli/utils.d.ts +17 -17
  58. package/build/lib/cli/utils.d.ts.map +1 -1
  59. package/build/lib/cli/utils.js +29 -29
  60. package/build/lib/cli/utils.js.map +1 -1
  61. package/build/lib/doctor/doctor.d.ts +2 -2
  62. package/build/lib/doctor/doctor.d.ts.map +1 -1
  63. package/build/lib/doctor/doctor.js +6 -6
  64. package/build/lib/doctor/doctor.js.map +1 -1
  65. package/build/lib/extension/driver-config.d.ts +18 -77
  66. package/build/lib/extension/driver-config.d.ts.map +1 -1
  67. package/build/lib/extension/driver-config.js +37 -125
  68. package/build/lib/extension/driver-config.js.map +1 -1
  69. package/build/lib/extension/extension-config.d.ts +103 -210
  70. package/build/lib/extension/extension-config.d.ts.map +1 -1
  71. package/build/lib/extension/extension-config.js +180 -342
  72. package/build/lib/extension/extension-config.js.map +1 -1
  73. package/build/lib/extension/index.d.ts +12 -29
  74. package/build/lib/extension/index.d.ts.map +1 -1
  75. package/build/lib/extension/index.js +33 -75
  76. package/build/lib/extension/index.js.map +1 -1
  77. package/build/lib/extension/manifest-migrations.d.ts +3 -20
  78. package/build/lib/extension/manifest-migrations.d.ts.map +1 -1
  79. package/build/lib/extension/manifest-migrations.js +20 -101
  80. package/build/lib/extension/manifest-migrations.js.map +1 -1
  81. package/build/lib/extension/manifest.d.ts +61 -107
  82. package/build/lib/extension/manifest.d.ts.map +1 -1
  83. package/build/lib/extension/manifest.js +181 -356
  84. package/build/lib/extension/manifest.js.map +1 -1
  85. package/build/lib/extension/package-changed.d.ts +1 -3
  86. package/build/lib/extension/package-changed.d.ts.map +1 -1
  87. package/build/lib/extension/package-changed.js +8 -15
  88. package/build/lib/extension/package-changed.js.map +1 -1
  89. package/build/lib/extension/plugin-config.d.ts +10 -52
  90. package/build/lib/extension/plugin-config.d.ts.map +1 -1
  91. package/build/lib/extension/plugin-config.js +11 -63
  92. package/build/lib/extension/plugin-config.js.map +1 -1
  93. package/build/lib/helpers/build.d.ts +22 -0
  94. package/build/lib/helpers/build.d.ts.map +1 -0
  95. package/build/lib/helpers/build.js +109 -0
  96. package/build/lib/helpers/build.js.map +1 -0
  97. package/build/lib/helpers/capability.d.ts +38 -0
  98. package/build/lib/helpers/capability.d.ts.map +1 -0
  99. package/build/lib/helpers/capability.js +128 -0
  100. package/build/lib/helpers/capability.js.map +1 -0
  101. package/build/lib/helpers/network.d.ts +14 -0
  102. package/build/lib/helpers/network.d.ts.map +1 -0
  103. package/build/lib/helpers/network.js +35 -0
  104. package/build/lib/helpers/network.js.map +1 -0
  105. package/build/lib/insecure-features.js +6 -6
  106. package/build/lib/insecure-features.js.map +1 -1
  107. package/build/lib/inspector-commands.d.ts +6 -0
  108. package/build/lib/inspector-commands.d.ts.map +1 -1
  109. package/build/lib/inspector-commands.js +6 -0
  110. package/build/lib/inspector-commands.js.map +1 -1
  111. package/build/lib/logger.d.ts +2 -3
  112. package/build/lib/logger.d.ts.map +1 -1
  113. package/build/lib/logger.js +2 -3
  114. package/build/lib/logger.js.map +1 -1
  115. package/build/lib/main.d.ts +15 -58
  116. package/build/lib/main.d.ts.map +1 -1
  117. package/build/lib/main.js +25 -425
  118. package/build/lib/main.js.map +1 -1
  119. package/build/lib/schema/cli-args-guards.d.ts +34 -0
  120. package/build/lib/schema/cli-args-guards.d.ts.map +1 -0
  121. package/build/lib/schema/cli-args-guards.js +49 -0
  122. package/build/lib/schema/cli-args-guards.js.map +1 -0
  123. package/build/lib/schema/cli-args.js +2 -2
  124. package/build/lib/schema/cli-args.js.map +1 -1
  125. package/build/lib/schema/format-errors.d.ts +28 -0
  126. package/build/lib/schema/format-errors.d.ts.map +1 -0
  127. package/build/lib/schema/format-errors.js +29 -0
  128. package/build/lib/schema/format-errors.js.map +1 -0
  129. package/build/lib/schema/index.d.ts +2 -0
  130. package/build/lib/schema/index.d.ts.map +1 -1
  131. package/build/lib/schema/index.js +2 -0
  132. package/build/lib/schema/index.js.map +1 -1
  133. package/build/lib/schema/schema.d.ts +15 -15
  134. package/build/lib/schema/schema.d.ts.map +1 -1
  135. package/build/lib/schema/schema.js +37 -37
  136. package/build/lib/schema/schema.js.map +1 -1
  137. package/build/lib/utils.d.ts +0 -81
  138. package/build/lib/utils.d.ts.map +1 -1
  139. package/build/lib/utils.js +1 -248
  140. package/build/lib/utils.js.map +1 -1
  141. package/lib/{appium.js → appium.ts} +297 -341
  142. package/lib/bidi-commands.ts +10 -14
  143. package/lib/bootstrap/appium-initializer.ts +212 -0
  144. package/lib/bootstrap/appium-main-runner.ts +172 -0
  145. package/lib/{config-file.ts → bootstrap/config-file.ts} +29 -63
  146. package/lib/{grid-register.ts → bootstrap/grid-v3-register.ts} +35 -35
  147. package/lib/bootstrap/init-types.ts +31 -0
  148. package/lib/bootstrap/main-helpers.ts +223 -0
  149. package/lib/bootstrap/node-helpers.ts +180 -0
  150. package/lib/bootstrap/startup-config.ts +143 -0
  151. package/lib/cli/args.ts +10 -10
  152. package/lib/cli/extension-command.ts +132 -132
  153. package/lib/cli/extension.ts +7 -7
  154. package/lib/cli/parser.ts +50 -50
  155. package/lib/cli/setup-command.ts +2 -2
  156. package/lib/cli/utils.ts +33 -33
  157. package/lib/doctor/doctor.ts +8 -8
  158. package/lib/extension/driver-config.ts +165 -0
  159. package/lib/extension/{extension-config.js → extension-config.ts} +291 -405
  160. package/lib/extension/index.ts +143 -0
  161. package/lib/extension/manifest-migrations.ts +57 -0
  162. package/lib/extension/manifest.ts +369 -0
  163. package/lib/extension/{package-changed.js → package-changed.ts} +9 -18
  164. package/lib/extension/plugin-config.ts +62 -0
  165. package/lib/helpers/build.ts +111 -0
  166. package/lib/helpers/capability.ts +171 -0
  167. package/lib/helpers/network.ts +30 -0
  168. package/lib/insecure-features.ts +1 -1
  169. package/lib/inspector-commands.ts +6 -1
  170. package/lib/{logger.js → logger.ts} +1 -2
  171. package/lib/main.ts +60 -0
  172. package/lib/schema/cli-args-guards.ts +67 -0
  173. package/lib/schema/cli-args.ts +1 -1
  174. package/lib/schema/format-errors.ts +43 -0
  175. package/lib/schema/index.ts +2 -0
  176. package/lib/schema/schema.ts +51 -52
  177. package/lib/utils.ts +0 -331
  178. package/package.json +12 -13
  179. package/scripts/autoinstall-extensions.js +3 -0
  180. package/build/lib/config-file.d.ts +0 -57
  181. package/build/lib/config-file.d.ts.map +0 -1
  182. package/build/lib/config-file.js.map +0 -1
  183. package/build/lib/config.d.ts +0 -68
  184. package/build/lib/config.d.ts.map +0 -1
  185. package/build/lib/config.js +0 -358
  186. package/build/lib/config.js.map +0 -1
  187. package/build/lib/grid-register.d.ts +0 -35
  188. package/build/lib/grid-register.d.ts.map +0 -1
  189. package/build/lib/grid-register.js.map +0 -1
  190. package/lib/config.ts +0 -377
  191. package/lib/extension/driver-config.js +0 -245
  192. package/lib/extension/index.js +0 -169
  193. package/lib/extension/manifest-migrations.js +0 -136
  194. package/lib/extension/manifest.js +0 -550
  195. package/lib/extension/plugin-config.js +0 -112
  196. package/lib/main.js +0 -545
@@ -1,18 +1,28 @@
1
+ import type {ExtensionType} from '@appium/types';
2
+ import type {ExtClass, ExtManifest, ExtName, ExtRecord, InstallType} from 'appium/types';
3
+ import type {SchemaObject} from 'ajv';
1
4
  import {util, fs, system} from '@appium/support';
2
5
  import B from 'bluebird';
3
6
  import _ from 'lodash';
4
7
  import path from 'node:path';
8
+ import {pathToFileURL} from 'node:url';
5
9
  import resolveFrom from 'resolve-from';
6
10
  import {satisfies} from 'semver';
7
11
  import {commandClasses} from '../cli/extension';
8
- import {APPIUM_VER} from '../config';
9
- import log from '../logger';
12
+ import type {
13
+ ExtensionList,
14
+ ExtensionListData,
15
+ InstalledExtensionListData,
16
+ } from '../cli/extension-command';
17
+ import type {ExtCommand} from '../cli/extension';
18
+ import {APPIUM_VER} from '../helpers/build';
19
+ import {log} from '../logger';
10
20
  import {
11
21
  ALLOWED_SCHEMA_EXTENSIONS,
12
22
  isAllowedSchemaFileExtension,
13
23
  registerSchema,
14
24
  } from '../schema/schema';
15
- import { pathToFileURL } from 'node:url';
25
+ import type {Manifest} from './manifest';
16
26
 
17
27
  const DEFAULT_ENTRY_POINT = 'index.js';
18
28
  /**
@@ -43,8 +53,7 @@ export const INSTALL_TYPE_GIT = 'git';
43
53
  */
44
54
  export const INSTALL_TYPE_DEV = 'dev';
45
55
 
46
- /** @type {Set<InstallType>} */
47
- export const INSTALL_TYPES = new Set([
56
+ export const INSTALL_TYPES = new Set<InstallType>([
48
57
  INSTALL_TYPE_GIT,
49
58
  INSTALL_TYPE_GITHUB,
50
59
  INSTALL_TYPE_LOCAL,
@@ -52,80 +61,98 @@ export const INSTALL_TYPES = new Set([
52
61
  INSTALL_TYPE_DEV,
53
62
  ]);
54
63
 
55
- /**
56
- * This class is abstract. It should not be instantiated directly.
57
- *
58
- * Subclasses should provide the generic parameter to implement.
59
- * @template {ExtensionType} ExtType
60
- */
61
- export class ExtensionConfig {
62
- /**
63
- * The type of extension this class is responsible for.
64
- * @type {ExtType}
65
- */
66
- extensionType;
67
-
68
- /**
69
- * Manifest data for the extensions of this type.
70
- *
71
- * This data should _not_ be written to by anything but {@linkcode Manifest}.
72
- * @type {Readonly<ExtRecord<ExtType>>}
73
- */
74
- installedExtensions;
64
+ export type ExtManifestProblem = {err: string; val: unknown};
75
65
 
76
- /** @type {import('@appium/types').AppiumLogger} */
77
- log;
66
+ export type ExtManifestWithSchema<E extends ExtensionType> = ExtManifest<E> & {
67
+ schema: NonNullable<ExtManifest<E>['schema']>;
68
+ };
78
69
 
79
- /** @type {Manifest} */
80
- manifest;
70
+ export type ExtensionConfigMutationOpts = {write?: boolean};
81
71
 
82
- /**
83
- * @type {import('../cli/extension-command').ExtensionList<ExtType>|undefined}
84
- */
85
- #listDataCache;
72
+ /**
73
+ * Shared configuration and validation for installed Appium extensions (drivers or plugins).
74
+ * Subclasses fix the extension kind; do not instantiate this class directly.
75
+ */
76
+ export abstract class ExtensionConfig<ExtType extends ExtensionType> {
77
+ readonly extensionType: ExtType;
78
+ readonly manifest: Manifest;
79
+ installedExtensions: ExtRecord<ExtType>;
80
+ #listDataCache: ExtensionList<ExtType> | undefined;
86
81
 
87
- /**
88
- * @protected
89
- * @param {ExtType} extensionType - Type of extension
90
- * @param {Manifest} manifest - `Manifest` instance
91
- */
92
- constructor(extensionType, manifest) {
82
+ protected constructor(extensionType: ExtType, manifest: Manifest) {
93
83
  this.extensionType = extensionType;
94
- this.installedExtensions = manifest.getExtensionData(extensionType);
95
84
  this.manifest = manifest;
85
+ this.installedExtensions = manifest.getExtensionData(extensionType);
96
86
  }
97
87
 
98
- get manifestPath() {
88
+ /** Path to `extensions.yaml` after the manifest has been read; otherwise undefined. */
89
+ get manifestPath(): string | undefined {
99
90
  return this.manifest.manifestPath;
100
91
  }
101
92
 
102
- get appiumHome() {
93
+ /** `APPIUM_HOME` directory this config is tied to. */
94
+ get appiumHome(): string {
103
95
  return this.manifest.appiumHome;
104
96
  }
105
97
 
106
98
  /**
107
- * Returns a list of errors for a given extension.
99
+ * Type guard: manifest entry includes a `schema` path or inline schema object.
100
+ *
101
+ * @param extManifest - Parsed extension metadata
102
+ */
103
+ static extDataHasSchema<E extends ExtensionType>(
104
+ extManifest: ExtManifest<E>
105
+ ): extManifest is ExtManifestWithSchema<E> {
106
+ return _.isString(extManifest?.schema) || _.isObject(extManifest?.schema);
107
+ }
108
+
109
+ private static async _readExtensionSchema<E extends ExtensionType>(
110
+ appiumHome: string,
111
+ extType: E,
112
+ extName: string,
113
+ extManifest: ExtManifestWithSchema<E>
114
+ ): Promise<SchemaObject | undefined> {
115
+ const {pkgName, schema: argSchemaPath} = extManifest;
116
+ if (!argSchemaPath) {
117
+ throw new TypeError(
118
+ `No \`schema\` property found in config for ${extType} ${pkgName} -- why is this function being called?`
119
+ );
120
+ }
121
+ let moduleObject: any;
122
+ if (_.isString(argSchemaPath)) {
123
+ const schemaPath = resolveFrom(appiumHome, path.join(pkgName, argSchemaPath));
124
+ moduleObject = require(schemaPath);
125
+ } else {
126
+ moduleObject = argSchemaPath;
127
+ }
128
+ // this sucks. default exports should be destroyed
129
+ const schema = moduleObject.__esModule ? moduleObject.default : moduleObject;
130
+ await registerSchema(extType, extName, schema as SchemaObject);
131
+ return schema;
132
+ }
133
+
134
+ /**
135
+ * Collects blocking validation issues for one extension (generic fields, type-specific rules, and schema).
108
136
  *
109
- * @param {ExtName<ExtType>} extName
110
- * @param {ExtManifest<ExtType>} extManifest
111
- * @returns {ExtManifestProblem[]}
137
+ * @param extName - Extension key as stored in the manifest
138
+ * @param extManifest - Manifest entry for that extension
112
139
  */
113
- getProblems(extName, extManifest) {
140
+ async getProblems(extName: string, extManifest: ExtManifest<ExtType>): Promise<ExtManifestProblem[]> {
114
141
  return [
115
142
  ...this.getGenericConfigProblems(extManifest, extName),
116
143
  ...this.getConfigProblems(extManifest, extName),
117
- ...this.getSchemaProblems(extManifest, extName),
144
+ ...(await this.getSchemaProblems(extManifest, extName)),
118
145
  ];
119
146
  }
120
147
 
121
148
  /**
122
- * Returns a list of warnings for a given extension.
149
+ * Collects non-fatal issues for one extension (e.g. manifest quirks, peer dependency mismatches).
150
+ * Warnings do not by themselves prevent loading.
123
151
  *
124
- * @param {ExtName<ExtType>} extName
125
- * @param {ExtManifest<ExtType>} extManifest
126
- * @returns {Promise<string[]>}
152
+ * @param extName - Extension key as stored in the manifest
153
+ * @param extManifest - Manifest entry for that extension
127
154
  */
128
- async getWarnings(extName, extManifest) {
155
+ async getWarnings(extName: string, extManifest: ExtManifest<ExtType>): Promise<string[]> {
129
156
  const [genericConfigWarnings, configWarnings] = await B.all([
130
157
  this.getGenericConfigWarnings(extManifest, extName),
131
158
  this.getConfigWarnings(extManifest, extName),
@@ -135,28 +162,16 @@ export class ExtensionConfig {
135
162
  }
136
163
 
137
164
  /**
138
- * Returns a list of extension-type-specific issues. To be implemented by subclasses.
139
- * @abstract
140
- * @param {ExtManifest<ExtType>} extManifest
141
- * @param {ExtName<ExtType>} extName
142
- * @returns {Promise<string[]>}
143
- */
144
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
145
- async getConfigWarnings(extManifest, extName) {
146
- return [];
147
- }
148
-
149
- /**
165
+ * Turns per-extension errors and warnings into human-readable log lines for console output.
150
166
  *
151
- * @param {Map<ExtName<ExtType>,ExtManifestProblem[]>} [errorMap]
152
- * @param {Map<ExtName<ExtType>,string[]>} [warningMap]
167
+ * @param errorMap - Extension name to list of blocking problems
168
+ * @param warningMap - Extension name to list of warning strings
153
169
  */
154
- getValidationResultSummaries(errorMap = new Map(), warningMap = new Map()) {
155
- /**
156
- * Array of computed strings
157
- * @type {string[]}
158
- */
159
- const errorSummaries = [];
170
+ getValidationResultSummaries(
171
+ errorMap: Map<string, ExtManifestProblem[]> = new Map(),
172
+ warningMap: Map<string, string[]> = new Map()
173
+ ): {errorSummaries: string[]; warningSummaries: string[]} {
174
+ const errorSummaries: string[] = [];
160
175
  for (const [extName, problems] of errorMap.entries()) {
161
176
  if (_.isEmpty(problems)) {
162
177
  continue;
@@ -174,8 +189,7 @@ export class ExtensionConfig {
174
189
  );
175
190
  }
176
191
  }
177
- /** @type {string[]} */
178
- const warningSummaries = [];
192
+ const warningSummaries: string[] = [];
179
193
  for (const [extName, warnings] of warningMap.entries()) {
180
194
  if (_.isEmpty(warnings)) {
181
195
  continue;
@@ -192,27 +206,157 @@ export class ExtensionConfig {
192
206
  }
193
207
 
194
208
  /**
195
- * Checks extensions for problems. To be called by subclasses' `validate` method.
209
+ * Records a new installed extension in the manifest and optionally persists immediately.
196
210
  *
197
- * Errors and warnings will be displayed to the user.
211
+ * @param extName - Manifest key for the extension
212
+ * @param extManifest - Full manifest payload
198
213
  *
199
- * This method mutates `exts`.
214
+ * Pass `{ write: false }` to defer flushing until a later manifest write.
215
+ */
216
+ async addExtension(
217
+ extName: string,
218
+ extManifest: ExtManifest<ExtType>,
219
+ {write = true}: ExtensionConfigMutationOpts = {}
220
+ ): Promise<void> {
221
+ this.manifest.setExtension(this.extensionType, extName, extManifest);
222
+ if (write) {
223
+ await this.manifest.write();
224
+ }
225
+ }
226
+
227
+ /**
228
+ * Merges new metadata into an existing extension entry and optionally writes the manifest.
200
229
  *
201
- * @protected
202
- * @param {ExtRecord<ExtType>} exts - Lookup of extension names to {@linkcode ExtManifest} objects
203
- * @returns {Promise<ExtRecord<ExtType>>} The same lookup, but picking only error-free extensions
230
+ * @param extName - Installed extension to update
231
+ * @param extManifest - Fields to merge over the current entry
232
+ *
233
+ * Pass `{ write: false }` to defer flushing until a later manifest write.
204
234
  */
205
- async _validate(exts) {
206
- /**
207
- * Lookup of extension names to {@linkcode ExtManifestProblem ExtManifestProblems}
208
- * @type {Map<ExtName<ExtType>,ExtManifestProblem[]>}
209
- */
210
- const errorMap = new Map();
211
- /**
212
- * Lookup of extension names to warnings.
213
- * @type {Map<ExtName<ExtType>,string[]>}
214
- */
215
- const warningMap = new Map();
235
+ async updateExtension(
236
+ extName: ExtName<ExtType>,
237
+ extManifest: ExtManifest<ExtType>,
238
+ {write = true}: ExtensionConfigMutationOpts = {}
239
+ ): Promise<void> {
240
+ const existing = this.installedExtensions[extName];
241
+ this.manifest.setExtension(this.extensionType, extName as string, {
242
+ ...existing,
243
+ ...extManifest,
244
+ });
245
+ if (write) {
246
+ await this.manifest.write();
247
+ }
248
+ }
249
+
250
+ /**
251
+ * Drops an extension from the manifest and optionally persists immediately.
252
+ *
253
+ * @param extName - Installed extension to remove
254
+ *
255
+ * Pass `{ write: false }` to defer flushing until a later manifest write.
256
+ */
257
+ async removeExtension(
258
+ extName: ExtName<ExtType>,
259
+ {write = true}: ExtensionConfigMutationOpts = {}
260
+ ): Promise<void> {
261
+ this.manifest.deleteExtension(this.extensionType, extName);
262
+ if (write) {
263
+ await this.manifest.write();
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Logs installed extensions to the console. Subclasses may use `activeNames` to annotate active plugins.
269
+ */
270
+ print(_activeNames?: ExtName<ExtType>[]): void {
271
+ void _activeNames;
272
+ if (_.isEmpty(this.installedExtensions)) {
273
+ log.info(
274
+ `No ${this.extensionType}s have been installed in ${this.appiumHome}. Use the "appium ${this.extensionType}" ` +
275
+ 'command to install the one(s) you want to use.'
276
+ );
277
+ return;
278
+ }
279
+
280
+ log.info(`Available ${this.extensionType}s:`);
281
+ for (const [extName, extManifest] of _.toPairs(this.installedExtensions) as Array<
282
+ [string, ExtManifest<ExtType>]
283
+ >) {
284
+ log.info(` - ${this.extensionDesc(extName, extManifest)}`);
285
+ }
286
+ }
287
+
288
+ /**
289
+ * Root directory of an installed extension, preferring `installPath` and falling back to `node_modules/<pkgName>`.
290
+ *
291
+ * @param extName - Installed extension key
292
+ */
293
+ getInstallPath(extName: keyof ExtRecord<ExtType> & string): string {
294
+ return (
295
+ this.installedExtensions[extName]?.installPath ??
296
+ path.join(this.appiumHome, 'node_modules', this.installedExtensions[extName].pkgName)
297
+ );
298
+ }
299
+
300
+ /**
301
+ * Dynamically imports the extension entry point and returns the exported main class constructor.
302
+ *
303
+ * @param extName - Installed extension to load
304
+ */
305
+ async requireAsync(extName: ExtName<ExtType>): Promise<ExtClass<ExtType>> {
306
+ const [reqPath, mainClass] = await this._resolveExtension(extName);
307
+ log.debug(`Requiring ${this.extensionType} at ${reqPath}`);
308
+ // https://github.com/nodejs/node/issues/31710
309
+ const importPath = system.isWindows() ? pathToFileURL(reqPath).href : reqPath;
310
+ const mod = (await import(importPath)) as Record<string, ExtClass<ExtType>>;
311
+ const MainClass = mod[mainClass];
312
+ if (!MainClass) {
313
+ throw new ReferenceError(
314
+ `Could not find a class named "${mainClass}" exported by ${this.extensionType} "${extName}"`
315
+ );
316
+ }
317
+ return MainClass;
318
+ }
319
+
320
+ /** Whether the manifest lists an extension under the given name. */
321
+ isInstalled(extName: string): boolean {
322
+ return extName in this.installedExtensions;
323
+ }
324
+
325
+ /**
326
+ * Loads the extension’s config schema from disk or inline JSON and registers it for CLI/config validation.
327
+ *
328
+ * @param extName - Extension key
329
+ * @param extManifest - Manifest entry that includes `schema`
330
+ */
331
+ async readExtensionSchema(
332
+ extName: string,
333
+ extManifest: ExtManifestWithSchema<ExtType>
334
+ ): Promise<SchemaObject | undefined> {
335
+ return await ExtensionConfig._readExtensionSchema(
336
+ this.appiumHome,
337
+ this.extensionType,
338
+ extName,
339
+ extManifest
340
+ );
341
+ }
342
+
343
+ /** Optional async warnings for this extension kind; override in subclasses when needed. */
344
+ protected async getConfigWarnings(
345
+ _extManifest: ExtManifest<ExtType>,
346
+ _extName: string
347
+ ): Promise<string[]> {
348
+ void _extManifest;
349
+ void _extName;
350
+ return [];
351
+ }
352
+
353
+ /**
354
+ * Validates all entries in `exts`, logs summaries, and removes keys that have blocking errors.
355
+ * Intended for subclasses’ `validate` implementation.
356
+ */
357
+ protected async _validate(exts: ExtRecord<ExtType>): Promise<ExtRecord<ExtType>> {
358
+ const errorMap = new Map<string, ExtManifestProblem[]>();
359
+ const warningMap = new Map<string, string[]>();
216
360
 
217
361
  for (const [extName, extManifest] of _.toPairs(exts)) {
218
362
  const [errors, warnings] = await B.all([
@@ -257,18 +401,13 @@ export class ExtensionConfig {
257
401
  }
258
402
 
259
403
  /**
260
- * Retrieves listing data for extensions via command class.
261
- *
262
- * This is an expensive operation, so the result is cached. Currently, there is no
263
- * use case for invalidating the cache.
264
- * @protected
265
- * @returns {Promise<import('../cli/extension-command').ExtensionList<ExtType>>}
404
+ * Fetches `appium driver|plugin list`-style data via the CLI command class; result is cached.
266
405
  */
267
- async getListData() {
406
+ protected async getListData(): Promise<ExtensionList<ExtType>> {
268
407
  if (this.#listDataCache) {
269
408
  return this.#listDataCache;
270
409
  }
271
- const CommandClass = /** @type {ExtCommand<ExtType>} */ (commandClasses[this.extensionType]);
410
+ const CommandClass = commandClasses[this.extensionType] as ExtCommand<ExtType>;
272
411
  const cmd = new CommandClass({config: this, json: true});
273
412
  const listData = await cmd.list({showInstalled: true, showUpdates: true});
274
413
  this.#listDataCache = listData;
@@ -276,20 +415,16 @@ export class ExtensionConfig {
276
415
  }
277
416
 
278
417
  /**
279
- * Returns a list of warnings for a particular extension.
280
- *
281
- * By definition, a non-empty list of warnings does _not_ imply the extension cannot be loaded,
282
- * but it may not work as expected or otherwise throw an exception at runtime.
283
- *
284
- * @param {ExtManifest<ExtType>} extManifest
285
- * @param {ExtName<ExtType>} extName
286
- * @returns {Promise<string[]>}
418
+ * Warnings about manifest install fields and Appium peer dependency compatibility for one extension.
287
419
  */
288
- async getGenericConfigWarnings(extManifest, extName) {
420
+ protected async getGenericConfigWarnings(
421
+ extManifest: ExtManifest<ExtType>,
422
+ extName: string
423
+ ): Promise<string[]> {
289
424
  const {appiumVersion, installSpec, installType, pkgName} = extManifest;
290
- const warnings = [];
425
+ const warnings: string[] = [];
291
426
 
292
- const invalidFields = [];
427
+ const invalidFields: string[] = [];
293
428
  if (!_.isString(installSpec)) {
294
429
  invalidFields.push('installSpec');
295
430
  }
@@ -315,20 +450,12 @@ export class ExtensionConfig {
315
450
  );
316
451
  }
317
452
 
318
- /**
319
- * Helps concatenate warning messages related to peer dependencies
320
- * @param {string} reason
321
- * @returns string
322
- */
323
- const createPeerWarning = (reason) =>
453
+ const createPeerWarning = (reason: string): string =>
324
454
  `${extTypeText} "${extName}" (package \`${pkgName}\`) may be incompatible with the current version of Appium (v${APPIUM_VER}) due to ${reason}`;
325
455
 
326
456
  if (_.isString(appiumVersion) && !satisfies(APPIUM_VER, appiumVersion)) {
327
457
  const listData = await this.getListData();
328
- const extListData =
329
- /** @type {import('../cli/extension-command').ExtensionListData<ExtType>} */ (
330
- listData[extName]
331
- );
458
+ const extListData = listData[extName] as ExtensionListData<ExtType> | undefined;
332
459
  if (extListData?.installed) {
333
460
  const {updateVersion, upToDate} = extListData;
334
461
  if (!upToDate && updateVersion) {
@@ -347,10 +474,7 @@ export class ExtensionConfig {
347
474
  }
348
475
  } else if (!_.isString(appiumVersion)) {
349
476
  const listData = await this.getListData();
350
- const extListData =
351
- /** @type {import('../cli/extension-command').InstalledExtensionListData<ExtType>} */ (
352
- listData[extName]
353
- );
477
+ const extListData = listData[extName] as InstalledExtensionListData<ExtType> | undefined;
354
478
  if (!extListData?.upToDate && extListData?.updateVersion) {
355
479
  warnings.push(
356
480
  createPeerWarning(
@@ -369,23 +493,20 @@ export class ExtensionConfig {
369
493
  }
370
494
  return warnings;
371
495
  }
372
- /**
373
- * Returns list of unrecoverable errors (if any) for the given extension _if_ it has a `schema` property.
374
- *
375
- * @param {ExtManifest<ExtType>} extManifest - Extension data (from manifest)
376
- * @param {ExtName<ExtType>} extName - Extension name (from manifest)
377
- * @returns {ExtManifestProblem[]}
378
- */
379
- getSchemaProblems(extManifest, extName) {
380
- /** @type {ExtManifestProblem[]} */
381
- const problems = [];
496
+
497
+ /** Validates and registers extension CLI/config schema when the manifest defines a `schema` field. */
498
+ protected async getSchemaProblems(
499
+ extManifest: ExtManifest<ExtType>,
500
+ extName: string
501
+ ): Promise<ExtManifestProblem[]> {
502
+ const problems: ExtManifestProblem[] = [];
382
503
  const {schema: argSchemaPath} = extManifest;
383
504
  if (ExtensionConfig.extDataHasSchema(extManifest)) {
384
505
  if (_.isString(argSchemaPath)) {
385
506
  if (isAllowedSchemaFileExtension(argSchemaPath)) {
386
507
  try {
387
- this.readExtensionSchema(extName, extManifest);
388
- } catch (err) {
508
+ await this.readExtensionSchema(extName, extManifest);
509
+ } catch (err: any) {
389
510
  problems.push({
390
511
  err: `Unable to register schema at path ${argSchemaPath}; ${err.message}`,
391
512
  val: argSchemaPath,
@@ -401,8 +522,8 @@ export class ExtensionConfig {
401
522
  }
402
523
  } else if (_.isPlainObject(argSchemaPath)) {
403
524
  try {
404
- this.readExtensionSchema(extName, extManifest);
405
- } catch (err) {
525
+ await this.readExtensionSchema(extName, extManifest);
526
+ } catch (err: any) {
406
527
  problems.push({
407
528
  err: `Unable to register embedded schema; ${err.message}`,
408
529
  val: argSchemaPath,
@@ -418,16 +539,14 @@ export class ExtensionConfig {
418
539
  return problems;
419
540
  }
420
541
 
421
- /**
422
- * Return a list of generic unrecoverable errors for the given extension
423
- * @param {ExtManifest<ExtType>} extManifest - Extension data (from manifest)
424
- * @param {ExtName<ExtType>} extName - Extension name (from manifest)
425
- * @returns {ExtManifestProblem[]}
426
- */
427
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
428
- getGenericConfigProblems(extManifest, extName) {
542
+ /** Blocking issues for required manifest fields shared by all extensions (version, package name, main class). */
543
+ protected getGenericConfigProblems(
544
+ extManifest: ExtManifest<ExtType>,
545
+ extName: string
546
+ ): ExtManifestProblem[] {
547
+ void extName;
429
548
  const {version, pkgName, mainClass} = extManifest;
430
- const problems = [];
549
+ const problems: ExtManifestProblem[] = [];
431
550
 
432
551
  if (!_.isString(version)) {
433
552
  problems.push({
@@ -453,139 +572,40 @@ export class ExtensionConfig {
453
572
  return problems;
454
573
  }
455
574
 
456
- /**
457
- * @abstract
458
- * @param {ExtManifest<ExtType>} extManifest
459
- * @param {ExtName<ExtType>} extName
460
- * @returns {ExtManifestProblem[]}
461
- */
462
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
463
- getConfigProblems(extManifest, extName) {
464
- // should override this method if special validation is necessary for this extension type
575
+ /** Driver- or plugin-specific blocking validation; override in subclasses when needed. */
576
+ protected getConfigProblems(
577
+ _extManifest: ExtManifest<ExtType>,
578
+ _extName: string
579
+ ): ExtManifestProblem[] {
580
+ void _extManifest;
581
+ void _extName;
465
582
  return [];
466
583
  }
467
584
 
468
- /**
469
- * @param {string} extName
470
- * @param {ExtManifest<ExtType>} extManifest
471
- * @param {ExtensionConfigMutationOpts} opts
472
- * @returns {Promise<void>}
473
- */
474
- async addExtension(extName, extManifest, {write = true} = {}) {
475
- this.manifest.setExtension(this.extensionType, extName, extManifest);
476
- if (write) {
477
- await this.manifest.write();
478
- }
479
- }
480
-
481
- /**
482
- * @param {ExtName<ExtType>} extName
483
- * @param {ExtManifest<ExtType>} extManifest
484
- * @param {ExtensionConfigMutationOpts} opts
485
- * @returns {Promise<void>}
486
- */
487
- async updateExtension(extName, extManifest, {write = true} = {}) {
488
- this.manifest.setExtension(this.extensionType, extName, {
489
- ...this.installedExtensions[extName],
490
- ...extManifest,
491
- });
492
- if (write) {
493
- await this.manifest.write();
494
- }
495
- }
496
-
497
- /**
498
- * Remove an extension from the list of installed extensions, and optionally avoid a write to the manifest file.
499
- *
500
- * @param {ExtName<ExtType>} extName
501
- * @param {ExtensionConfigMutationOpts} opts
502
- * @returns {Promise<void>}
503
- */
504
- async removeExtension(extName, {write = true} = {}) {
505
- this.manifest.deleteExtension(this.extensionType, extName);
506
- if (write) {
507
- await this.manifest.write();
508
- }
509
- }
510
-
511
- /**
512
- * @param {ExtName<ExtType>[]} [activeNames]
513
- * @returns {void}
514
- */
515
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
516
- print(activeNames) {
517
- if (_.isEmpty(this.installedExtensions)) {
518
- log.info(
519
- `No ${this.extensionType}s have been installed in ${this.appiumHome}. Use the "appium ${this.extensionType}" ` +
520
- 'command to install the one(s) you want to use.'
521
- );
522
- return;
523
- }
524
-
525
- log.info(`Available ${this.extensionType}s:`);
526
- for (const [extName, extManifest] of /** @type {[string, ExtManifest<ExtType>][]} */ (
527
- _.toPairs(this.installedExtensions)
528
- )) {
529
- log.info(` - ${this.extensionDesc(extName, extManifest)}`);
530
- }
531
- }
532
-
533
- /**
534
- * Returns a string describing the extension. Subclasses must implement.
535
- * @param {ExtName<ExtType>} extName - Extension name
536
- * @param {ExtManifest<ExtType>} extManifest - Extension data
537
- * @returns {string}
538
- * @abstract
539
- */
540
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
541
- extensionDesc(extName, extManifest) {
542
- throw new Error('This must be implemented in a subclass');
543
- }
544
-
545
- /**
546
- * Returns--with reasonable accuracy--the path on disk to the extension.
547
- *
548
- * If `installPath` is present in the manifest, then it is used; otherwise we just guess.
549
- * @param {keyof typeof this.installedExtensions} extName
550
- * @returns {string}
551
- */
552
- getInstallPath(extName) {
553
- return (
554
- this.installedExtensions[extName]?.installPath ??
555
- path.join(this.appiumHome, 'node_modules', this.installedExtensions[extName].pkgName)
556
- );
557
- }
558
-
559
- /**
560
- *
561
- * @param {ExtName<ExtType>} extName
562
- * @returns {Promise<[string, string]>}
563
- */
564
- async _resolveExtension(extName) {
585
+ private async _resolveExtension(extName: ExtName<ExtType>): Promise<[string, string]> {
565
586
  const {mainClass} = this.installedExtensions[extName];
566
587
  const moduleRoot = this.getInstallPath(extName);
567
588
  const packageJsonPath = path.join(moduleRoot, 'package.json');
568
- let extensionManifest;
589
+ let extensionManifest: Record<string, any>;
569
590
  try {
570
591
  extensionManifest = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
571
- } catch (e) {
592
+ } catch (e: any) {
572
593
  throw new ReferenceError(
573
594
  `Could not read the ${this.extensionType} manifest at ${packageJsonPath}: ${e.message}`
574
595
  );
575
596
  }
576
- /** @type {string | undefined} */
577
- let entryPointRelativePath;
597
+ let entryPointRelativePath: string | undefined;
578
598
  try {
579
599
  if (extensionManifest.type === 'module' && extensionManifest.exports) {
580
600
  entryPointRelativePath = resolveEsmEntryPoint(extensionManifest.exports);
581
601
  }
582
602
  entryPointRelativePath = entryPointRelativePath ?? extensionManifest.main ?? DEFAULT_ENTRY_POINT;
583
- } catch (e) {
603
+ } catch (e: any) {
584
604
  throw new ReferenceError(
585
605
  `Could not find the ${this.extensionType} installed at ${moduleRoot}: ${e.message}`
586
606
  );
587
607
  }
588
- const entryPointFullPath = path.resolve(moduleRoot, /** @type {string} */ (entryPointRelativePath));
608
+ const entryPointFullPath = path.resolve(moduleRoot, entryPointRelativePath as string);
589
609
  if (!await fs.exists(entryPointFullPath)) {
590
610
  throw new ReferenceError(
591
611
  `Cannot find a valid ${this.extensionType} main entry point in '${packageJsonPath}'. ` +
@@ -601,98 +621,21 @@ export class ExtensionConfig {
601
621
  }
602
622
 
603
623
  /**
604
- * Loads extension asynchronously and returns its main class (constructor)
624
+ * One-line human description for list output; implemented per extension kind.
605
625
  *
606
- * @param {ExtName<ExtType>} extName
607
- * @returns {Promise<ExtClass<ExtType>>}
608
- */
609
- async requireAsync(extName) {
610
- const [reqPath, mainClass] = await this._resolveExtension(extName);
611
- log.debug(`Requiring ${this.extensionType} at ${reqPath}`);
612
- // https://github.com/nodejs/node/issues/31710
613
- const importPath = system.isWindows() ? pathToFileURL(reqPath).href : reqPath;
614
- const MainClass = (await import(importPath))[mainClass];
615
- if (!MainClass) {
616
- throw new ReferenceError(
617
- `Could not find a class named "${mainClass}" exported by ${this.extensionType} "${extName}"`
618
- );
619
- }
620
- return MainClass;
621
- }
622
-
623
- /**
624
- * @param {string} extName
625
- * @returns {boolean}
626
+ * @param extName - Manifest key
627
+ * @param extManifest - Entry used for version and kind-specific labels
626
628
  */
627
- isInstalled(extName) {
628
- return extName in this.installedExtensions;
629
- }
629
+ public abstract extensionDesc(extName: ExtName<ExtType>, extManifest: ExtManifest<ExtType>): string;
630
630
 
631
- /**
632
- * Intended to be called by corresponding instance methods of subclass.
633
- * @private
634
- * @template {ExtensionType} ExtType
635
- * @param {string} appiumHome
636
- * @param {ExtType} extType
637
- * @param {ExtName<ExtType>} extName - Extension name (unique to its type)
638
- * @param {ExtManifestWithSchema<ExtType>} extManifest - Extension config
639
- * @returns {import('ajv').SchemaObject|undefined}
640
- */
641
- static _readExtensionSchema(appiumHome, extType, extName, extManifest) {
642
- const {pkgName, schema: argSchemaPath} = extManifest;
643
- if (!argSchemaPath) {
644
- throw new TypeError(
645
- `No \`schema\` property found in config for ${extType} ${pkgName} -- why is this function being called?`
646
- );
647
- }
648
- let moduleObject;
649
- if (_.isString(argSchemaPath)) {
650
- const schemaPath = resolveFrom(appiumHome, path.join(pkgName, argSchemaPath));
651
- moduleObject = require(schemaPath);
652
- } else {
653
- moduleObject = argSchemaPath;
654
- }
655
- // this sucks. default exports should be destroyed
656
- const schema = moduleObject.__esModule ? moduleObject.default : moduleObject;
657
- registerSchema(extType, extName, schema);
658
- return schema;
659
- }
660
-
661
- /**
662
- * Returns `true` if a specific {@link ExtManifest} object has a `schema` prop.
663
- * The {@link ExtManifest} object becomes a {@link ExtManifestWithSchema} object.
664
- * @template {ExtensionType} ExtType
665
- * @param {ExtManifest<ExtType>} extManifest
666
- * @returns {extManifest is ExtManifestWithSchema<ExtType>}
667
- */
668
- static extDataHasSchema(extManifest) {
669
- return _.isString(extManifest?.schema) || _.isObject(extManifest?.schema);
670
- }
671
-
672
- /**
673
- * If an extension provides a schema, this will load the schema and attempt to
674
- * register it with the schema registrar.
675
- * @param {ExtName<ExtType>} extName - Name of extension
676
- * @param {ExtManifestWithSchema<ExtType>} extManifest - Extension data
677
- * @returns {import('ajv').SchemaObject|undefined}
678
- */
679
- readExtensionSchema(extName, extManifest) {
680
- return ExtensionConfig._readExtensionSchema(
681
- this.appiumHome,
682
- this.extensionType,
683
- extName,
684
- extManifest
685
- );
686
- }
687
631
  }
688
632
 
689
633
  /**
690
- * https://nodejs.org/api/packages.html#package-entry-points
634
+ * Resolves a package `exports` field (string, `"."`, or `"import"`) to a relative entry path for ESM packages.
691
635
  *
692
- * @param {any} exportsValue
693
- * @returns {string | undefined}
636
+ * @param exportsValue - `package.json` `exports` value or nested fragment
694
637
  */
695
- export function resolveEsmEntryPoint(exportsValue) {
638
+ export function resolveEsmEntryPoint(exportsValue: unknown): string | undefined {
696
639
  if (_.isString(exportsValue) && exportsValue) {
697
640
  return exportsValue;
698
641
  }
@@ -700,67 +643,10 @@ export function resolveEsmEntryPoint(exportsValue) {
700
643
  return;
701
644
  }
702
645
 
703
- for (const key of ['.', 'import']) {
704
- if (exportsValue[key]) {
705
- return resolveEsmEntryPoint(exportsValue[key]);
646
+ const obj = exportsValue as Record<string, unknown>;
647
+ for (const key of ['.', 'import'] as const) {
648
+ if (obj[key]) {
649
+ return resolveEsmEntryPoint(obj[key]);
706
650
  }
707
651
  }
708
652
  }
709
-
710
- /**
711
- * An issue with the {@linkcode ExtManifest} for a particular extension.
712
- *
713
- * The existance of such an object implies that the extension cannot be loaded.
714
- * @typedef ExtManifestProblem
715
- * @property {string} err - Error message
716
- * @property {any} val - Associated value
717
- */
718
-
719
- /**
720
- * An optional logging function provided to an {@link ExtensionConfig} subclass.
721
- * @callback ExtensionLogFn
722
- * @param {...any} args
723
- * @returns {void}
724
- */
725
-
726
- /**
727
- * @typedef {import('@appium/types').ExtensionType} ExtensionType
728
- * @typedef {import('./manifest').Manifest} Manifest
729
- * @typedef {import('appium/types').InstallType} InstallType
730
- */
731
-
732
- /**
733
- * @template {ExtensionType} ExtType
734
- * @typedef {import('appium/types').ExtManifest<ExtType>} ExtManifest
735
- */
736
-
737
- /**
738
- * @template {ExtensionType} ExtType
739
- * @typedef {ExtManifest<ExtType> & {schema: NonNullable<ExtManifest<ExtType>['schema']>}} ExtManifestWithSchema
740
- */
741
-
742
- /**
743
- * @template {ExtensionType} ExtType
744
- * @typedef {import('appium/types').ExtName<ExtType>} ExtName
745
- */
746
-
747
- /**
748
- * @template {ExtensionType} ExtType
749
- * @typedef {import('appium/types').ExtClass<ExtType>} ExtClass
750
- */
751
-
752
- /**
753
- * @template {ExtensionType} ExtType
754
- * @typedef {import('appium/types').ExtRecord<ExtType>} ExtRecord
755
- */
756
-
757
- /**
758
- * @template {ExtensionType} ExtType
759
- * @typedef {import('../cli/extension').ExtCommand<ExtType>} ExtCommand
760
- */
761
-
762
- /**
763
- * Options for various methods in {@link ExtensionConfig}
764
- * @typedef ExtensionConfigMutationOpts
765
- * @property {boolean} [write=true] Whether or not to write the manifest to disk after a mutation operation
766
- */