appium 2.3.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/README.md +129 -158
  2. package/build/lib/appium.d.ts +72 -0
  3. package/build/lib/appium.d.ts.map +1 -1
  4. package/build/lib/appium.js +326 -0
  5. package/build/lib/appium.js.map +1 -1
  6. package/build/lib/cli/args.d.ts.map +1 -1
  7. package/build/lib/cli/args.js +24 -2
  8. package/build/lib/cli/args.js.map +1 -1
  9. package/build/lib/cli/driver-command.d.ts +16 -0
  10. package/build/lib/cli/driver-command.d.ts.map +1 -1
  11. package/build/lib/cli/driver-command.js +16 -0
  12. package/build/lib/cli/driver-command.js.map +1 -1
  13. package/build/lib/cli/extension-command.d.ts +26 -2
  14. package/build/lib/cli/extension-command.d.ts.map +1 -1
  15. package/build/lib/cli/extension-command.js +126 -1
  16. package/build/lib/cli/extension-command.js.map +1 -1
  17. package/build/lib/cli/parser.d.ts.map +1 -1
  18. package/build/lib/cli/parser.js +11 -7
  19. package/build/lib/cli/parser.js.map +1 -1
  20. package/build/lib/cli/plugin-command.d.ts +16 -0
  21. package/build/lib/cli/plugin-command.d.ts.map +1 -1
  22. package/build/lib/cli/plugin-command.js +16 -0
  23. package/build/lib/cli/plugin-command.js.map +1 -1
  24. package/build/lib/cli/utils.d.ts.map +1 -1
  25. package/build/lib/cli/utils.js +3 -4
  26. package/build/lib/cli/utils.js.map +1 -1
  27. package/build/lib/config.d.ts +1 -1
  28. package/build/lib/config.d.ts.map +1 -1
  29. package/build/lib/config.js +25 -29
  30. package/build/lib/config.js.map +1 -1
  31. package/build/lib/constants.d.ts +10 -0
  32. package/build/lib/constants.d.ts.map +1 -1
  33. package/build/lib/constants.js +11 -1
  34. package/build/lib/constants.js.map +1 -1
  35. package/build/lib/doctor/doctor.d.ts +55 -0
  36. package/build/lib/doctor/doctor.d.ts.map +1 -0
  37. package/build/lib/doctor/doctor.js +201 -0
  38. package/build/lib/doctor/doctor.js.map +1 -0
  39. package/build/lib/extension/index.d.ts.map +1 -1
  40. package/build/lib/extension/index.js +10 -4
  41. package/build/lib/extension/index.js.map +1 -1
  42. package/build/lib/grid-register.d.ts.map +1 -1
  43. package/build/lib/grid-register.js +1 -2
  44. package/build/lib/grid-register.js.map +1 -1
  45. package/build/lib/main.d.ts.map +1 -1
  46. package/build/lib/main.js +6 -0
  47. package/build/lib/main.js.map +1 -1
  48. package/build/lib/utils.d.ts +1 -0
  49. package/build/lib/utils.d.ts.map +1 -1
  50. package/build/lib/utils.js +2 -1
  51. package/build/lib/utils.js.map +1 -1
  52. package/build/types/cli.d.ts +1 -1
  53. package/build/types/cli.d.ts.map +1 -1
  54. package/lib/appium.js +387 -1
  55. package/lib/cli/args.js +27 -2
  56. package/lib/cli/driver-command.js +18 -0
  57. package/lib/cli/extension-command.js +148 -2
  58. package/lib/cli/parser.js +22 -8
  59. package/lib/cli/plugin-command.js +18 -0
  60. package/lib/cli/utils.js +5 -7
  61. package/lib/config.js +18 -24
  62. package/lib/constants.js +12 -0
  63. package/lib/doctor/doctor.js +209 -0
  64. package/lib/extension/index.js +10 -4
  65. package/lib/grid-register.js +1 -2
  66. package/lib/main.js +6 -0
  67. package/lib/utils.js +2 -1
  68. package/package.json +13 -13
  69. package/types/cli.ts +1 -1
@@ -66,6 +66,18 @@ export default class DriverCliCommand extends ExtensionCliCommand {
66
66
  });
67
67
  }
68
68
 
69
+ /**
70
+ * Runs doctor checks for the given driver
71
+ *
72
+ * @param {DriverDoctorOptions} opts
73
+ * @returns {Promise<import('@appium/types').IDoctorCheck[]>}
74
+ */
75
+ async doctor({driver}) {
76
+ return await super._doctor({
77
+ installSpec: driver,
78
+ });
79
+ }
80
+
69
81
  /**
70
82
  *
71
83
  * @param {import('./extension-command').ExtensionArgs} opts
@@ -151,3 +163,9 @@ export default class DriverCliCommand extends ExtensionCliCommand {
151
163
  * @property {string} scriptName - name of the script to run
152
164
  * @property {string[]} [extraArgs] - arguments to pass to the script
153
165
  */
166
+
167
+ /**
168
+ * Options for {@linkcode DriverCliCommand.doctor}.
169
+ * @typedef DriverDoctorOptions
170
+ * @property {string} driver - name of the driver to run doctor checks for
171
+ */
@@ -2,7 +2,7 @@
2
2
  import B from 'bluebird';
3
3
  import _ from 'lodash';
4
4
  import path from 'path';
5
- import {npm, util, env, console} from '@appium/support';
5
+ import {npm, util, env, console, fs, system} from '@appium/support';
6
6
  import {spinWith, RingBuffer} from './utils';
7
7
  import {
8
8
  INSTALL_TYPE_NPM,
@@ -15,6 +15,10 @@ import {SubProcess} from 'teen_process';
15
15
  import {packageDidChange} from '../extension/package-changed';
16
16
  import {spawn} from 'child_process';
17
17
  import {inspect} from 'node:util';
18
+ import {pathToFileURL} from 'url';
19
+ import {Doctor} from '../doctor/doctor';
20
+ import {npmPackage} from '../utils';
21
+ import semver from 'semver';
18
22
 
19
23
  const UPDATE_ALL = 'installed';
20
24
 
@@ -31,6 +35,19 @@ function receiptToManifest(receipt) {
31
35
  return /** @type {ExtManifest<ExtType>} */ (_.omit(receipt, 'driverName', 'pluginName'));
32
36
  }
33
37
 
38
+ /**
39
+ * Fetches the remote extension version requirement
40
+ *
41
+ * @param {string} installSpec Extension name (could also contain the version suffix)
42
+ * @returns {Promise<[string, string|null]>}
43
+ */
44
+ async function getRemoteExtensionVersionReq(installSpec) {
45
+ const allDeps = await npm.getPackageInfo(installSpec, ['peerDependencies', 'dependencies']);
46
+ const requiredVersionPair = _.flatMap(_.values(allDeps).map(_.toPairs))
47
+ .find(([name]) => name === 'appium');
48
+ return [npmPackage.version, requiredVersionPair ? requiredVersionPair[1] : null];
49
+ }
50
+
34
51
  /**
35
52
  * @template {ExtensionType} ExtType
36
53
  */
@@ -223,6 +240,29 @@ class ExtensionCliCommand {
223
240
  return listData;
224
241
  }
225
242
 
243
+ /**
244
+ * Checks whether the given extension is compatible with the currently installed server
245
+ *
246
+ * @param {InstallViaNpmArgs} installViaNpmOpts
247
+ * @returns {Promise<void>}
248
+ */
249
+ async _checkInstallCompatibility({installSpec, pkgName, installType}) {
250
+ if (INSTALL_TYPE_NPM !== installType) {
251
+ return;
252
+ }
253
+
254
+ await spinWith(this.isJsonOutput, `Checking if '${pkgName}' is compatible`, async () => {
255
+ const [serverVersion, extVersionRequirement] = await getRemoteExtensionVersionReq(installSpec);
256
+ if (serverVersion && extVersionRequirement && !semver.satisfies(serverVersion, extVersionRequirement)) {
257
+ throw this._createFatalError(
258
+ `'${installSpec}' cannot be installed because the server version it requires (${extVersionRequirement}) ` +
259
+ `does not meet the currently installed one (${serverVersion}). Please install ` +
260
+ `a compatible server version first.`
261
+ );
262
+ }
263
+ });
264
+ }
265
+
226
266
  /**
227
267
  * Install an extension
228
268
  *
@@ -332,6 +372,8 @@ class ExtensionCliCommand {
332
372
  );
333
373
  }
334
374
 
375
+ await this._checkInstallCompatibility(installViaNpmOpts);
376
+
335
377
  receipt = await this.installViaNpm(installViaNpmOpts);
336
378
 
337
379
  // this _should_ be the same as `probablyExtName` as the one derived above unless
@@ -695,6 +737,85 @@ class ExtensionCliCommand {
695
737
  });
696
738
  }
697
739
 
740
+ /**
741
+ * Runs doctor checks for the given extension
742
+ *
743
+ * @param {DoctorOptions} opts
744
+ * @returns {Promise<import('@appium/types').IDoctorCheck[]>}
745
+ */
746
+ async _doctor({installSpec}) {
747
+ if (!this.config.isInstalled(installSpec)) {
748
+ throw this._createFatalError(`The ${this.type} "${installSpec}" is not installed`);
749
+ }
750
+
751
+ const moduleRoot = this.config.getInstallPath(installSpec);
752
+ const packageJsonPath = path.join(moduleRoot, 'package.json');
753
+ if (!await fs.exists(packageJsonPath)) {
754
+ throw this._createFatalError(
755
+ `No package.json could be found for "${installSpec}" ${this.type}`
756
+ );
757
+ }
758
+ let doctorSpec;
759
+ try {
760
+ doctorSpec = JSON.parse(await fs.readFile(packageJsonPath, 'utf8')).appium?.doctor;
761
+ } catch (e) {
762
+ throw this._createFatalError(
763
+ `The manifest at '${packageJsonPath}' cannot be parsed: ${e.message}`
764
+ );
765
+ }
766
+ if (!doctorSpec) {
767
+ this.log.info(`The ${this.type} "${installSpec}" does not export any doctor checks`);
768
+ return [];
769
+ }
770
+ if (!_.isPlainObject(doctorSpec) || !_.isArray(doctorSpec.checks)) {
771
+ throw this._createFatalError(
772
+ `The 'doctor' entry in the package manifest '${packageJsonPath}' must be a proper object ` +
773
+ `containing the 'checks' key with the array of script paths`
774
+ );
775
+ }
776
+ const paths = doctorSpec.checks.map((/** @type {string} */ p) => {
777
+ const scriptPath = path.resolve(moduleRoot, p);
778
+ if (!path.normalize(scriptPath).startsWith(path.normalize(moduleRoot))) {
779
+ this.log.error(
780
+ `The doctor check script '${p}' from the package manifest '${packageJsonPath}' must be located ` +
781
+ `in the '${moduleRoot}' root folder. It will be skipped`
782
+ );
783
+ return null;
784
+ }
785
+ return scriptPath;
786
+ }).filter(Boolean);
787
+ /** @type {Promise[]} */
788
+ const loadChecksPromises = [];
789
+ for (const p of paths) {
790
+ const promise = (async () => {
791
+ // https://github.com/nodejs/node/issues/31710
792
+ const scriptPath = system.isWindows() ? pathToFileURL(p).href : p;
793
+ try {
794
+ return await import(scriptPath);
795
+ } catch (e) {
796
+ this.log.warn(`Unable to load doctor checks from '${p}': ${e.message}`);
797
+ }
798
+ })();
799
+ loadChecksPromises.push(promise);
800
+ }
801
+ const isDoctorCheck = (/** @type {any} */ x) =>
802
+ ['diagnose', 'fix', 'hasAutofix', 'isOptional'].every((method) => _.isFunction(x?.[method]));
803
+ /** @type {import('@appium/types').IDoctorCheck[]} */
804
+ const checks = _.flatMap((await B.all(loadChecksPromises)).filter(Boolean).map(_.toPairs))
805
+ .map(([, value]) => value)
806
+ .filter(isDoctorCheck);
807
+ if (_.isEmpty(checks)) {
808
+ this.log.info(`The ${this.type} "${installSpec}" exports no valid doctor checks`);
809
+ return [];
810
+ }
811
+ this.log.debug(
812
+ `Running ${util.pluralize('doctor check', checks.length, true)} ` +
813
+ `for the "${installSpec}" ${this.type}`
814
+ );
815
+ await new Doctor(checks).run();
816
+ return checks;
817
+ }
818
+
698
819
  /**
699
820
  * Runs a script cached inside the `scripts` field under `appium`
700
821
  * inside of the extension's `package.json` file. Will throw
@@ -729,6 +850,24 @@ class ExtensionCliCommand {
729
850
  );
730
851
  }
731
852
 
853
+ if (!scriptName) {
854
+ const allScripts = _.toPairs(extScripts);
855
+ const root = this.config.getInstallPath(installSpec);
856
+ const existingScripts = await B.filter(
857
+ allScripts,
858
+ async ([, p]) => await fs.exists(path.join(root, p))
859
+ );
860
+ if (_.isEmpty(existingScripts)) {
861
+ this.log.info(`The ${this.type} named '${installSpec}' does not contain any scripts`);
862
+ } else {
863
+ this.log.info(`The ${this.type} named '${installSpec}' contains ` +
864
+ `${util.pluralize('script', existingScripts.length, true)}:`);
865
+ existingScripts.forEach(([name]) => this.log.info(` - ${name}`));
866
+ }
867
+ this.log.ok(`Successfully retrieved the list of scripts`.green);
868
+ return {};
869
+ }
870
+
732
871
  if (!(scriptName in /** @type {Record<string,string>} */ (extScripts))) {
733
872
  throw this._createFatalError(
734
873
  `The ${this.type} named '${installSpec}' does not support the script: '${scriptName}'`
@@ -874,11 +1013,18 @@ export {ExtensionCliCommand as ExtensionCommand};
874
1013
  * Options for {@linkcode ExtensionCliCommand._run}.
875
1014
  * @typedef RunOptions
876
1015
  * @property {string} installSpec - name of the extension to run a script from
877
- * @property {string} scriptName - name of the script to run
1016
+ * @property {string} [scriptName] - name of the script to run. If not provided
1017
+ * then all available script names will be printed
878
1018
  * @property {string[]} [extraArgs] - arguments to pass to the script
879
1019
  * @property {boolean} [bufferOutput] - if true, will buffer the output of the script and return it
880
1020
  */
881
1021
 
1022
+ /**
1023
+ * Options for {@linkcode ExtensionCliCommand.doctor}.
1024
+ * @typedef DoctorOptions
1025
+ * @property {string} installSpec - name of the extension to run doctor checks for
1026
+ */
1027
+
882
1028
  /**
883
1029
  * Return value of {@linkcode ExtensionCliCommand._run}
884
1030
  *
package/lib/cli/parser.js CHANGED
@@ -2,7 +2,17 @@ import {fs} from '@appium/support';
2
2
  import {ArgumentParser} from 'argparse';
3
3
  import _ from 'lodash';
4
4
  import path from 'path';
5
- import {DRIVER_TYPE, PLUGIN_TYPE, SERVER_SUBCOMMAND} from '../constants';
5
+ import {
6
+ DRIVER_TYPE,
7
+ EXT_SUBCOMMAND_DOCTOR,
8
+ EXT_SUBCOMMAND_INSTALL,
9
+ EXT_SUBCOMMAND_LIST,
10
+ EXT_SUBCOMMAND_RUN,
11
+ EXT_SUBCOMMAND_UNINSTALL,
12
+ EXT_SUBCOMMAND_UPDATE,
13
+ PLUGIN_TYPE,
14
+ SERVER_SUBCOMMAND
15
+ } from '../constants';
6
16
  import {finalizeSchema, getArgSpec, hasArgSpec} from '../schema';
7
17
  import {rootDir} from '../config';
8
18
  import {getExtensionArgs, getServerArgs} from './args';
@@ -191,8 +201,7 @@ class ArgParser {
191
201
 
192
202
  const serverArgs = getServerArgs();
193
203
  for (const [flagsOrNames, opts] of serverArgs) {
194
- // TS doesn't like the spread operator here.
195
- // @ts-ignore
204
+ // @ts-ignore TS doesn't like the spread operator here.
196
205
  serverParser.add_argument(...flagsOrNames, {...opts});
197
206
  }
198
207
 
@@ -221,33 +230,38 @@ class ArgParser {
221
230
  */
222
231
  const parserSpecs = [
223
232
  {
224
- command: 'list',
233
+ command: EXT_SUBCOMMAND_LIST,
225
234
  args: extensionArgs[type].list,
226
235
  help: `List available and installed ${type}s`,
227
236
  aliases: ['ls'],
228
237
  },
229
238
  {
230
- command: 'install',
239
+ command: EXT_SUBCOMMAND_INSTALL,
231
240
  args: extensionArgs[type].install,
232
241
  help: `Install a ${type}`,
233
242
  },
234
243
  {
235
- command: 'uninstall',
244
+ command: EXT_SUBCOMMAND_UNINSTALL,
236
245
  args: extensionArgs[type].uninstall,
237
246
  help: `Uninstall a ${type}`,
238
247
  },
239
248
  {
240
- command: 'update',
249
+ command: EXT_SUBCOMMAND_UPDATE,
241
250
  args: extensionArgs[type].update,
242
251
  help: `Update installed ${type}s to the latest version`,
243
252
  },
244
253
  {
245
- command: 'run',
254
+ command: EXT_SUBCOMMAND_RUN,
246
255
  args: extensionArgs[type].run,
247
256
  help:
248
257
  `Run a script (defined inside the ${type}'s package.json under the ` +
249
258
  `“scripts” field inside the “appium” field) from an installed ${type}`,
250
259
  },
260
+ {
261
+ command: EXT_SUBCOMMAND_DOCTOR,
262
+ args: extensionArgs[type].doctor,
263
+ help: `Run doctor checks (if any defined) for the given ${type}`,
264
+ },
251
265
  ];
252
266
 
253
267
  for (const {command, args, help, aliases} of parserSpecs) {
@@ -65,6 +65,18 @@ export default class PluginCliCommand extends ExtensionCliCommand {
65
65
  });
66
66
  }
67
67
 
68
+ /**
69
+ * Runs doctor checks for the given plugin
70
+ *
71
+ * @param {PluginDoctorOptions} opts
72
+ * @returns {Promise<import('@appium/types').IDoctorCheck[]>}
73
+ */
74
+ async doctor({plugin}) {
75
+ return await super._doctor({
76
+ installSpec: plugin,
77
+ });
78
+ }
79
+
68
80
  /**
69
81
  *
70
82
  * @param {import('./extension-command').ExtensionArgs} opts
@@ -141,3 +153,9 @@ export default class PluginCliCommand extends ExtensionCliCommand {
141
153
  * @property {string} scriptName - name of the script to run
142
154
  * @property {string[]} [extraArgs] - arguments to pass to the script
143
155
  */
156
+
157
+ /**
158
+ * Options for {@linkcode PluginCliCommand.doctor}.
159
+ * @typedef PluginDoctorOptions
160
+ * @property {string} plugin - name of the plugin to run doctor checks for
161
+ */
package/lib/cli/utils.js CHANGED
@@ -2,14 +2,14 @@
2
2
 
3
3
  import ora from 'ora';
4
4
 
5
- const JSON_SPACES = 4;
5
+ export const JSON_SPACES = 4;
6
6
 
7
7
  /***
8
8
  * Log an error to the console and exit the process.
9
9
  * @param {boolean} json - whether we should log json or text
10
10
  * @param {any} msg - error message, object, Error instance, etc.
11
11
  */
12
- function errAndQuit(json, msg) {
12
+ export function errAndQuit(json, msg) {
13
13
  if (json) {
14
14
  console.log(JSON.stringify({error: `${msg}`}, null, JSON_SPACES));
15
15
  } else {
@@ -26,7 +26,7 @@ function errAndQuit(json, msg) {
26
26
  * @param {boolean} json - whether we are in json mode (and should therefore not log)
27
27
  * @param {string} msg - string to log
28
28
  */
29
- function log(json, msg) {
29
+ export function log(json, msg) {
30
30
  !json && console.log(msg);
31
31
  }
32
32
 
@@ -36,7 +36,7 @@ function log(json, msg) {
36
36
  * @param {string} msg - string to log
37
37
  * @param {function} fn - function to wrap with spinning
38
38
  */
39
- async function spinWith(json, msg, fn) {
39
+ export async function spinWith(json, msg, fn) {
40
40
  if (json) {
41
41
  return await fn();
42
42
  }
@@ -52,7 +52,7 @@ async function spinWith(json, msg, fn) {
52
52
  }
53
53
  }
54
54
 
55
- class RingBuffer {
55
+ export class RingBuffer {
56
56
  constructor(size = 50) {
57
57
  this.size = size;
58
58
  this.buffer = [];
@@ -70,5 +70,3 @@ class RingBuffer {
70
70
  this.buffer.push(item);
71
71
  }
72
72
  }
73
-
74
- export {errAndQuit, log, spinWith, JSON_SPACES, RingBuffer};
package/lib/config.js CHANGED
@@ -4,17 +4,14 @@ import {system, fs} from '@appium/support';
4
4
  import axios from 'axios';
5
5
  import {exec} from 'teen_process';
6
6
  import semver from 'semver';
7
- import findUp from 'find-up';
8
7
  import os from 'node:os';
8
+ import {npmPackage} from './utils';
9
9
  import {getDefaultsForSchema, getAllArgSpecs} from './schema/schema';
10
10
 
11
- const npmPackage = fs.readPackageJsonFrom(__dirname);
12
-
13
- const APPIUM_VER = npmPackage.version;
11
+ export const APPIUM_VER = npmPackage.version;
14
12
  const ENGINES = /** @type {Record<string,string>} */ (npmPackage.engines);
15
13
  const MIN_NODE_VERSION = ENGINES.node;
16
14
 
17
- const GIT_META_ROOT = '.git';
18
15
  const GIT_BINARY = `git${system.isWindows() ? '.exe' : ''}`;
19
16
  const GITHUB_API = 'https://api.github.com/repos/appium/appium';
20
17
 
@@ -44,27 +41,25 @@ async function updateBuildInfo(useGithubApiFallback = false) {
44
41
  }
45
42
  }
46
43
 
47
- /**
48
- * Finds the Git metadata dir (see `GIT_META_ROOT`)
49
- *
50
- * This is needed because Appium cannot assume `package.json` and `.git` are in the same
51
- * directory. Monorepos, see?
52
- * @returns {Promise<string|undefined>} Path to dir or `undefined` if not found
53
- */
54
- async function findGitRoot() {
55
- return await findUp(GIT_META_ROOT, {cwd: rootDir, type: 'directory'});
56
- }
44
+ /** @type {() => Promise<string?>} */
45
+ const getFullGitPath = _.memoize(async function getFullGitPath() {
46
+ try {
47
+ return await fs.which(GIT_BINARY);
48
+ } catch {
49
+ return null;
50
+ }
51
+ });
57
52
 
58
53
  /**
59
54
  * @param {boolean} [useGithubApiFallback]
60
55
  * @returns {Promise<string?>}
61
56
  */
62
57
  async function getGitRev(useGithubApiFallback = false) {
63
- const gitRoot = await findGitRoot();
64
- if (gitRoot) {
58
+ const fullGitPath = await getFullGitPath();
59
+ if (fullGitPath) {
65
60
  try {
66
- const {stdout} = await exec(GIT_BINARY, ['rev-parse', 'HEAD'], {
67
- cwd: gitRoot,
61
+ const {stdout} = await exec(fullGitPath, ['rev-parse', 'HEAD'], {
62
+ cwd: __dirname,
68
63
  });
69
64
  return stdout.trim();
70
65
  } catch (ign) {}
@@ -94,11 +89,11 @@ async function getGitRev(useGithubApiFallback = false) {
94
89
  * @returns {Promise<string?>}
95
90
  */
96
91
  async function getGitTimestamp(commitSha, useGithubApiFallback = false) {
97
- const gitRoot = await findGitRoot();
98
- if (gitRoot) {
92
+ const fullGitPath = await getFullGitPath();
93
+ if (fullGitPath) {
99
94
  try {
100
- const {stdout} = await exec(GIT_BINARY, ['show', '-s', '--format=%ci', commitSha], {
101
- cwd: gitRoot,
95
+ const {stdout} = await exec(fullGitPath, ['show', '-s', '--format=%ci', commitSha], {
96
+ cwd: __dirname,
102
97
  });
103
98
  return stdout.trim();
104
99
  } catch (ign) {}
@@ -321,7 +316,6 @@ export {
321
316
  showBuildInfo,
322
317
  getNonDefaultServerArgs,
323
318
  getGitRev,
324
- APPIUM_VER,
325
319
  updateBuildInfo,
326
320
  showConfig,
327
321
  rootDir,
package/lib/constants.js CHANGED
@@ -64,6 +64,7 @@ export const EXT_SUBCOMMAND_INSTALL = 'install';
64
64
  export const EXT_SUBCOMMAND_UNINSTALL = 'uninstall';
65
65
  export const EXT_SUBCOMMAND_UPDATE = 'update';
66
66
  export const EXT_SUBCOMMAND_RUN = 'run';
67
+ export const EXT_SUBCOMMAND_DOCTOR = 'doctor';
67
68
 
68
69
  /**
69
70
  * Current revision of the manifest (`extensions.yaml`) schema
@@ -77,3 +78,14 @@ export const CURRENT_SCHEMA_REV = 4;
77
78
  * memory usage, perf, and/or log output, and higher limits may be difficult to scan.
78
79
  */
79
80
  export const LONG_STACKTRACE_LIMIT = 100;
81
+
82
+ /**
83
+ * Where should the bidi websocket handler live on the server?
84
+ */
85
+ export const BIDI_BASE_PATH = '/bidi';
86
+
87
+ /**
88
+ * The name of the event for drivers to emit when they want to send bidi events to a client over
89
+ * a bidi socket
90
+ */
91
+ export const BIDI_EVENT_NAME = 'bidiEvent';