@travetto/cli 5.0.0-rc.0 → 5.0.0-rc.10

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/README.md CHANGED
@@ -59,7 +59,7 @@ This module also has a tight integration with the [VSCode plugin](https://market
59
59
  At it's heart, a cli command is the contract defined by what flags, and what arguments the command supports. Within the framework this requires three criteria to be met:
60
60
  * The file must be located in the `support/` folder, and have a name that matches `cli.*.ts`
61
61
  * The file must be a class that has a main method
62
- * The class must use the [@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/decorators.ts#L15) decorator
62
+ * The class must use the [@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/decorators.ts#L85) decorator
63
63
 
64
64
  **Code: Basic Command**
65
65
  ```typescript
@@ -93,7 +93,7 @@ Examples of mappings:
93
93
  The pattern is that underscores(_) translate to colons (:), and the `cli.` prefix, and `.ts` suffix are dropped.
94
94
 
95
95
  ## Binding Flags
96
- [@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/decorators.ts#L15) is a wrapper for [@Schema](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/schema.ts#L14), and so every class that uses the [@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/decorators.ts#L15) decorator is now a full [@Schema](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/schema.ts#L14) class. The fields of the class represent the flags that are available to the command.
96
+ [@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/decorators.ts#L85) is a wrapper for [@Schema](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/schema.ts#L14), and so every class that uses the [@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/decorators.ts#L85) decorator is now a full [@Schema](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/schema.ts#L14) class. The fields of the class represent the flags that are available to the command.
97
97
 
98
98
  **Code: Basic Command with Flag**
99
99
  ```typescript
@@ -130,11 +130,11 @@ $ trv basic:flag --loud
130
130
  HELLO
131
131
  ```
132
132
 
133
- The [@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/decorators.ts#L15) supports the following data types for flags:
133
+ The [@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/decorators.ts#L85) supports the following data types for flags:
134
134
  * Boolean values
135
- * Number values. The [@Integer](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L172), [@Float](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L178), [@Precision](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L166), [@Min](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L107) and [@Max](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L117) decorators help provide additional validation.
136
- * String values. [@MinLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L107), [@MaxLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L117), [@Match](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L99) and [@Enum](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L78) provide additional constraints
137
- * Date values. The [@Min](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L107) and [@Max](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L117) decorators help provide additional validation.
135
+ * Number values. The [@Integer](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L171), [@Float](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L177), [@Precision](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L165), [@Min](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L106) and [@Max](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L116) decorators help provide additional validation.
136
+ * String values. [@MinLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L106), [@MaxLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L116), [@Match](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L98) and [@Enum](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L77) provide additional constraints
137
+ * Date values. The [@Min](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L106) and [@Max](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L116) decorators help provide additional validation.
138
138
  * String lists. Same as String, but allowing multiple values.
139
139
  * Numeric lists. Same as Number, but allowing multiple values.
140
140
 
@@ -388,7 +388,7 @@ npx trv call:db --host localhost --port 3306 --username app --password <custom>
388
388
  ```
389
389
 
390
390
  ## VSCode Integration
391
- By default, cli commands do not expose themselves to the VSCode extension, as the majority of them are not intended for that sort of operation. [RESTful API](https://github.com/travetto/travetto/tree/main/module/rest#readme "Declarative api for RESTful APIs with support for the dependency injection module.") does expose a cli target `run:rest` that will show up, to help run/debug a rest application. Any command can mark itself as being a run target, and will be eligible for running from within the [VSCode plugin](https://marketplace.visualstudio.com/items?itemName=arcsine.travetto-plugin). This is achieved by setting the `runTarget` field on the [@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/decorators.ts#L15) decorator. This means the target will be visible within the editor tooling.
391
+ By default, cli commands do not expose themselves to the VSCode extension, as the majority of them are not intended for that sort of operation. [RESTful API](https://github.com/travetto/travetto/tree/main/module/rest#readme "Declarative api for RESTful APIs with support for the dependency injection module.") does expose a cli target `run:rest` that will show up, to help run/debug a rest application. Any command can mark itself as being a run target, and will be eligible for running from within the [VSCode plugin](https://marketplace.visualstudio.com/items?itemName=arcsine.travetto-plugin). This is achieved by setting the `runTarget` field on the [@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/decorators.ts#L85) decorator. This means the target will be visible within the editor tooling.
392
392
 
393
393
  **Code: Simple Run Target**
394
394
  ```typescript
@@ -411,6 +411,7 @@ export class RunCommand {
411
411
  **Code: Anatomy of a Command**
412
412
  ```typescript
413
413
  export interface CliCommandShape<T extends unknown[] = unknown[]> {
414
+
414
415
  /**
415
416
  * Parsed state
416
417
  */
@@ -459,9 +460,9 @@ If the goal is to run a more complex application, which may include depending on
459
460
 
460
461
  **Code: Simple Run Target**
461
462
  ```typescript
462
- import { Env } from '@travetto/base';
463
+ import { Runtime } from '@travetto/runtime';
463
464
  import { DependencyRegistry } from '@travetto/di';
464
- import { CliCommand, CliCommandShape, CliUtil } from '@travetto/cli';
465
+ import { CliCommand, CliCommandShape } from '@travetto/cli';
465
466
 
466
467
  import { ServerHandle } from '../src/types';
467
468
  import { RestNetUtil } from '../src/util/net';
@@ -469,15 +470,9 @@ import { RestNetUtil } from '../src/util/net';
469
470
  /**
470
471
  * Run a rest server as an application
471
472
  */
472
- @CliCommand({ runTarget: true, addModule: true, addEnv: true })
473
+ @CliCommand({ runTarget: true, with: { debugIpc: true, canRestart: true, module: true, env: true } })
473
474
  export class RunRestCommand implements CliCommandShape {
474
475
 
475
- /** IPC debug is enabled */
476
- debugIpc?: boolean;
477
-
478
- /** Should the server be able to run with restart*/
479
- canRestart?: boolean;
480
-
481
476
  /** Port to run on */
482
477
  port?: number;
483
478
 
@@ -491,14 +486,11 @@ export class RunRestCommand implements CliCommandShape {
491
486
  }
492
487
 
493
488
  async main(): Promise<ServerHandle | void> {
494
- if (await CliUtil.debugIfIpc(this) || await CliUtil.runWithRestart(this)) {
495
- return;
496
- }
497
489
  const { RestApplication } = await import('../src/application/rest');
498
490
  try {
499
491
  return await DependencyRegistry.runInstance(RestApplication);
500
492
  } catch (err) {
501
- if (RestNetUtil.isInuseError(err) && !Env.production && this.killConflict) {
493
+ if (RestNetUtil.isInuseError(err) && !Runtime.production && this.killConflict) {
502
494
  await RestNetUtil.freePort(err.port);
503
495
  return await DependencyRegistry.runInstance(RestApplication);
504
496
  }
@@ -508,7 +500,7 @@ export class RunRestCommand implements CliCommandShape {
508
500
  }
509
501
  ```
510
502
 
511
- As noted in the example above, `fields` is specified in this execution, with support for `module`, and `env`. These env flag is directly tied to the [Runtime](https://github.com/travetto/travetto/tree/main/module/base/src/env.ts#L116) `name` defined in the [Base](https://github.com/travetto/travetto/tree/main/module/base#readme "Environment config and common utilities for travetto applications.") module.
503
+ As noted in the example above, `fields` is specified in this execution, with support for `module`, and `env`. These env flag is directly tied to the [Runtime](https://github.com/travetto/travetto/tree/main/module/runtime/src/env.ts#L109) `name` defined in the [Base](https://github.com/travetto/travetto/tree/main/module/runtime#readme "Environment config and common utilities for travetto applications.") module.
512
504
 
513
505
  The `module` field is slightly more complex, but is geared towards supporting commands within a monorepo context. This flag ensures that a module is specified if running from the root of the monorepo, and that the module provided is real, and can run the desired command. When running from an explicit module folder in the monorepo, the module flag is ignored.
514
506
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/cli",
3
- "version": "5.0.0-rc.0",
3
+ "version": "5.0.0-rc.10",
4
4
  "description": "CLI infrastructure for Travetto framework",
5
5
  "keywords": [
6
6
  "cli",
@@ -29,8 +29,8 @@
29
29
  "directory": "module/cli"
30
30
  },
31
31
  "dependencies": {
32
- "@travetto/schema": "^5.0.0-rc.0",
33
- "@travetto/terminal": "^5.0.0-rc.0"
32
+ "@travetto/schema": "^5.0.0-rc.10",
33
+ "@travetto/terminal": "^5.0.0-rc.9"
34
34
  },
35
35
  "travetto": {
36
36
  "displayName": "Command Line Interface",
package/src/decorators.ts CHANGED
@@ -1,11 +1,81 @@
1
- import { Class, ClassInstance, Env } from '@travetto/base';
2
- import { RuntimeIndex, RuntimeContext } from '@travetto/manifest';
3
- import { SchemaRegistry } from '@travetto/schema';
1
+ import { Class, ClassInstance, Env, Runtime, RuntimeIndex, describeFunction } from '@travetto/runtime';
2
+ import { FieldConfig, SchemaRegistry } from '@travetto/schema';
4
3
 
5
4
  import { CliCommandShape, CliCommandShapeFields } from './types';
6
- import { CliCommandRegistry, CliCommandConfigOptions } from './registry';
5
+ import { CliCommandRegistry } from './registry';
7
6
  import { CliModuleUtil } from './module';
8
7
  import { CliParseUtil } from './parse';
8
+ import { CliUtil } from './util';
9
+
10
+ type Cmd = CliCommandShape & { env?: string };
11
+
12
+ type CliCommandConfigOptions = {
13
+ hidden?: boolean;
14
+ runTarget?: boolean;
15
+ runtimeModule?: 'current' | 'command';
16
+ with?: {
17
+ /** Application environment */
18
+ env?: boolean;
19
+ /** Module to run for */
20
+ module?: boolean;
21
+ /** Should debug invocation trigger via ipc */
22
+ debugIpc?: boolean;
23
+ /** Should the invocation automatically restart on exit */
24
+ canRestart?: boolean;
25
+ };
26
+ };
27
+
28
+ const FIELD_CONFIG: {
29
+ name: keyof Exclude<CliCommandConfigOptions['with'], undefined>;
30
+ field: Partial<FieldConfig>;
31
+ run: (cmd: Cmd) => (Promise<unknown> | unknown);
32
+ }[] =
33
+ [
34
+ {
35
+ name: 'env',
36
+ run: cmd => Env.TRV_ENV.set(cmd.env || Runtime.env),
37
+ field: {
38
+ type: String,
39
+ aliases: ['e', CliParseUtil.toEnvField(Env.TRV_ENV.key)],
40
+ description: 'Application environment',
41
+ required: { active: false },
42
+ },
43
+ },
44
+ {
45
+ name: 'module',
46
+ run: (): void => { },
47
+ field: {
48
+ type: String,
49
+ aliases: ['m', CliParseUtil.toEnvField(Env.TRV_MODULE.key)],
50
+ description: 'Module to run for',
51
+ specifiers: ['module'],
52
+ required: { active: Runtime.monoRoot },
53
+ },
54
+ },
55
+ {
56
+ name: 'debugIpc',
57
+ run: cmd => CliUtil.debugIfIpc(cmd).then((v) => v && process.exit(0)),
58
+ field: {
59
+ type: Boolean,
60
+ aliases: ['di'],
61
+ description: 'Should debug invocation trigger via ipc',
62
+ default: true,
63
+ required: { active: false },
64
+ },
65
+ },
66
+ {
67
+ name: 'canRestart',
68
+ run: cmd => CliUtil.runWithRestart(cmd)?.then((v) => v && process.exit(0)),
69
+ field: {
70
+ type: Boolean,
71
+ aliases: ['cr'],
72
+ description: 'Should the invocation automatically restart on exit',
73
+ default: false,
74
+ required: { active: false },
75
+ },
76
+ }
77
+ ];
78
+
9
79
 
10
80
  /**
11
81
  * Decorator to register a CLI command
@@ -14,54 +84,39 @@ import { CliParseUtil } from './parse';
14
84
  */
15
85
  export function CliCommand(cfg: CliCommandConfigOptions = {}) {
16
86
  return function <T extends CliCommandShape>(target: Class<T>): void {
17
- const meta = RuntimeIndex.getFunctionMetadata(target);
18
- if (!meta || meta.abstract) {
87
+ if (!target.Ⲑid || describeFunction(target)?.abstract) {
19
88
  return;
20
89
  }
21
90
 
22
- const addModule = cfg.addModule === true || cfg.fields?.includes('module');
23
- const runtimeModule = cfg.runtimeModule ?? (cfg.addModule ? 'current' : undefined);
24
- const addEnv = cfg.addEnv ?? cfg.fields?.includes('env');
91
+ const VALID_FIELDS = FIELD_CONFIG.filter(f => !!cfg.with?.[f.name]);
92
+
25
93
  const { commandModule } = CliCommandRegistry.registerClass(target, {
26
94
  hidden: cfg.hidden,
27
95
  runTarget: cfg.runTarget,
28
- preMain: async (cmd: CliCommandShape & { env?: string }) => {
29
- if (addEnv) {
30
- Env.TRV_ENV.set(cmd.env || Env.name);
96
+ preMain: async (cmd: Cmd) => {
97
+ for (const field of VALID_FIELDS) {
98
+ await field.run(cmd);
31
99
  }
32
100
  }
33
101
  });
34
102
 
35
103
  const pendingCls = SchemaRegistry.getOrCreatePending(target);
36
104
 
37
- if (addEnv) {
38
- SchemaRegistry.registerPendingFieldConfig(target, 'env', String, {
39
- aliases: ['e', CliParseUtil.toEnvField(Env.TRV_ENV.key)],
40
- description: 'Application environment',
41
- required: { active: false }
42
- });
105
+ for (const { name, field: { type, ...field } } of VALID_FIELDS) {
106
+ SchemaRegistry.registerPendingFieldConfig(target, name, type!, field);
43
107
  }
44
108
 
45
- if (addModule) {
46
- SchemaRegistry.registerPendingFieldConfig(target, 'module', String, {
47
- aliases: ['m', CliParseUtil.toEnvField(Env.TRV_MODULE.key)],
48
- description: 'Module to run for',
49
- specifiers: ['module'],
50
- required: { active: RuntimeContext.monoRoot }
51
- });
52
- }
109
+ const runtimeModule = cfg.runtimeModule ?? (cfg.with?.module ? 'current' : undefined);
53
110
 
54
111
  if (runtimeModule) { // Validate module
55
- (pendingCls.validators ??= []).push(async item => {
56
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
57
- const { module: mod } = item as CliCommandShapeFields;
58
- const runModule = (runtimeModule === 'command' ? commandModule : mod) || RuntimeContext.main.name;
112
+ (pendingCls.validators ??= []).push(async ({ module: mod }: Partial<CliCommandShapeFields>) => {
113
+ const runModule = (runtimeModule === 'command' ? commandModule : mod) || Runtime.main.name;
59
114
 
60
115
  // If we need to run as a specific module
61
- if (runModule !== RuntimeContext.main.name) {
116
+ if (runModule !== Runtime.main.name) {
62
117
  try {
63
118
  RuntimeIndex.reinitForModule(runModule);
64
- } catch (err) {
119
+ } catch {
65
120
  return { source: 'flag', message: `${runModule} is an unknown module`, kind: 'custom', path: '.' };
66
121
  }
67
122
  }
package/src/error.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { AppError } from '@travetto/base';
2
- import { PackageUtil, RuntimeContext } from '@travetto/manifest';
1
+ import { AppError, Runtime } from '@travetto/runtime';
2
+ import { PackageUtil } from '@travetto/manifest';
3
3
  import { cliTpl } from './color';
4
4
  import { CliValidationError, CliCommandShape } from './types';
5
5
 
@@ -23,7 +23,7 @@ export class CliUnknownCommandError extends Error {
23
23
  const matchedCfg = COMMAND_PACKAGE.find(([re]) => re.test(cmd));
24
24
  if (matchedCfg) {
25
25
  const [, pkg, prod] = matchedCfg;
26
- const install = PackageUtil.getInstallCommand(RuntimeContext, `@travetto/${pkg}`, prod);
26
+ const install = PackageUtil.getInstallCommand(Runtime, `@travetto/${pkg}`, prod);
27
27
  return cliTpl`
28
28
  ${{ title: 'Missing Package' }}\n${'-'.repeat(20)}\nTo use ${{ input: cmd }} please run:\n
29
29
  ${{ identifier: install }}
package/src/execute.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ConsoleManager, Env, ShutdownManager } from '@travetto/base';
1
+ import { ConsoleManager, Runtime, ShutdownManager } from '@travetto/runtime';
2
2
 
3
3
  import { HelpUtil } from './help';
4
4
  import { CliCommandRegistry } from './registry';
@@ -54,7 +54,7 @@ export class ExecutionManager {
54
54
  await command._cfg!.preMain?.(command);
55
55
  await command.preMain?.();
56
56
 
57
- ConsoleManager.debug(Env.debug);
57
+ ConsoleManager.debug(Runtime.debug);
58
58
  const result = await command.main(...boundArgs);
59
59
  await CliUtil.listenForResponse(result);
60
60
  }
package/src/help.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import util from 'node:util';
2
2
 
3
- import { Primitive } from '@travetto/schema';
3
+ import { castKey, castTo, Primitive } from '@travetto/runtime';
4
4
 
5
5
  import { cliTpl } from './color';
6
6
  import { CliCommandShape } from './types';
@@ -41,13 +41,11 @@ export class HelpUtil {
41
41
  }
42
42
 
43
43
  const params: string[] = [];
44
- const descs: string[] = [];
44
+ const descriptions: string[] = [];
45
45
 
46
46
  for (const flag of flags) {
47
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
48
- const key = flag.name as keyof CliCommandShape;
49
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
50
- const flagVal = command[key] as unknown as Primitive;
47
+ const key = castKey<CliCommandShape>(flag.name);
48
+ const flagVal: Primitive = castTo(command[key]);
51
49
 
52
50
  let aliases = flag.flagNames ?? [];
53
51
  if (isBoolFlag(flag)) {
@@ -68,11 +66,11 @@ export class HelpUtil {
68
66
  if (key !== 'help' && flagVal !== null && flagVal !== undefined && flagVal !== '') {
69
67
  desc.push(cliTpl`(default: ${{ input: JSON.stringify(flagVal) }})`);
70
68
  }
71
- descs.push(desc.join(' '));
69
+ descriptions.push(desc.join(' '));
72
70
  }
73
71
 
74
72
  const paramWidths = params.map(x => util.stripVTControlCharacters(x).length);
75
- const descWidths = descs.map(x => util.stripVTControlCharacters(x).length);
73
+ const descWidths = descriptions.map(x => util.stripVTControlCharacters(x).length);
76
74
 
77
75
  const paramWidth = Math.max(...paramWidths);
78
76
  const descWidth = Math.max(...descWidths);
@@ -87,7 +85,7 @@ export class HelpUtil {
87
85
  '',
88
86
  cliTpl`${{ title: 'Options:' }}`,
89
87
  ...params.map((_, i) =>
90
- ` ${params[i]}${' '.repeat((paramWidth - paramWidths[i]))} ${descs[i].padEnd(descWidth)}${' '.repeat((descWidth - descWidths[i]))}`
88
+ ` ${params[i]}${' '.repeat((paramWidth - paramWidths[i]))} ${descriptions[i].padEnd(descWidth)}${' '.repeat((descWidth - descWidths[i]))}`
91
89
  ),
92
90
  '',
93
91
  ...helpText
package/src/module.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { IndexedModule, RuntimeIndex, RuntimeContext } from '@travetto/manifest';
1
+ import { Runtime, RuntimeIndex } from '@travetto/runtime';
2
+ import type { IndexedModule } from '@travetto/manifest';
2
3
 
3
4
  import { CliScmUtil } from './scm';
4
5
 
@@ -43,11 +44,11 @@ export class CliModuleUtil {
43
44
  * @param transitive
44
45
  * @returns
45
46
  */
46
- static async findModules(mode: 'all' | 'changed', fromHash?: string, toHash?: string, emptyOnFail = false): Promise<IndexedModule[]> {
47
+ static async findModules(mode: 'all' | 'changed', fromHash?: string, toHash?: string): Promise<IndexedModule[]> {
47
48
  return (mode === 'changed' ?
48
- await this.findChangedModulesRecursive(fromHash, toHash, emptyOnFail) :
49
+ await this.findChangedModulesRecursive(fromHash, toHash, true) :
49
50
  [...RuntimeIndex.getModuleList('all')].map(x => RuntimeIndex.getModule(x)!)
50
- ).filter(x => x.sourcePath !== RuntimeContext.workspace.path);
51
+ ).filter(x => x.sourcePath !== Runtime.workspace.path);
51
52
  }
52
53
 
53
54
  /**
package/src/parse.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
 
4
- import { RuntimeIndex, RuntimeContext } from '@travetto/manifest';
4
+ import { Runtime } from '@travetto/runtime';
5
5
  import { CliCommandInput, CliCommandSchema, ParsedState } from './types';
6
6
 
7
7
  type ParsedInput = ParsedState['all'][number];
@@ -85,16 +85,8 @@ export class CliParseUtil {
85
85
  const key = flag.replace(CONFIG_PRE, '');
86
86
 
87
87
  // We have a file
88
- const rel = (key.includes('/') ? key : `@/support/pack.${key}.flags`)
89
- .replace('@@/', `${RuntimeContext.workspace.path}/`)
90
- .replace('@/', `${mod}/`)
91
- .replace(/^(@[^\/]+\/[^\/]+)(\/.*)$/, (_, imp, rest) => {
92
- const val = RuntimeIndex.getModule(imp);
93
- if (!val) {
94
- throw new Error(`Unknown module file: ${_}, unable to proceed`);
95
- }
96
- return `${val.sourcePath}${rest}`;
97
- });
88
+ const rel = (key.includes('/') ? key : `@#support/pack.${key}.flags`)
89
+ .replace(/^(@[^#]*)#(.*)$/, (_, imp, rest) => `${Runtime.modulePath(imp)}/${rest}`);
98
90
 
99
91
  const file = path.resolve(rel);
100
92
 
@@ -145,7 +137,7 @@ export class CliParseUtil {
145
137
  const mod = args.reduce(
146
138
  (m, x, i, arr) =>
147
139
  (i < SEP ? check(arr[i - 1], x) ?? check(...x.split('=')) : undefined) ?? m,
148
- process.env[ENV_KEY] || RuntimeContext.main.name
140
+ process.env[ENV_KEY] || Runtime.main.name
149
141
  );
150
142
  return (await Promise.all(args.map((x, i) =>
151
143
  x.startsWith(CONFIG_PRE) && (i < SEP || SEP < 0) ? this.readFlagFile(x, mod) : x))).flat();
@@ -199,7 +191,7 @@ export class CliParseUtil {
199
191
  } else {
200
192
  const field = schema.args[argIdx];
201
193
  out.push(getInput({ field, input, index: argIdx }));
202
- // Move argIdx along if not in a vararg situation
194
+ // Move argIdx along if not in a var arg situation
203
195
  if (!field?.array) {
204
196
  argIdx += 1;
205
197
  }
package/src/registry.ts CHANGED
@@ -1,20 +1,9 @@
1
- import { Class, ConcreteClass, Env } from '@travetto/base';
2
- import { RuntimeIndex } from '@travetto/manifest';
1
+ import { asConstructable, Class, classConstruct, describeFunction, Runtime, RuntimeIndex } from '@travetto/runtime';
3
2
 
4
3
  import { CliCommandConfig, CliCommandShape } from './types';
5
4
  import { CliUnknownCommandError } from './error';
6
5
 
7
- export type CliCommandConfigOptions = {
8
- hidden?: boolean;
9
- runTarget?: boolean;
10
- addModule?: boolean;
11
- addEnv?: boolean;
12
- runtimeModule?: 'current' | 'command';
13
- /** @deprecated */
14
- fields?: ('module' | 'env')[];
15
- };
16
-
17
- const CLI_FILE_REGEX = /\/cli[.](?<name>.*)[.]tsx?$/;
6
+ const CLI_FILE_REGEX = /\/cli[.](?<name>.*?)([.]tsx?)?$/;
18
7
  const getName = (s: string): string => (s.match(CLI_FILE_REGEX)?.groups?.name ?? s).replaceAll('_', ':');
19
8
 
20
9
  class $CliCommandRegistry {
@@ -25,9 +14,8 @@ class $CliCommandRegistry {
25
14
  return this.#commands.get(cls);
26
15
  }
27
16
 
28
- #getClass(cmd: CliCommandShape): Class<CliCommandShape> {
29
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
30
- return cmd.constructor as Class;
17
+ getClass(cmd: CliCommandShape): Class<CliCommandShape> {
18
+ return asConstructable<CliCommandShape>(cmd).constructor;
31
19
  }
32
20
 
33
21
  /**
@@ -37,7 +25,7 @@ class $CliCommandRegistry {
37
25
  if (!this.#fileMapping) {
38
26
  const all = new Map<string, string>();
39
27
  for (const e of RuntimeIndex.find({
40
- module: m => !Env.production || m.prod,
28
+ module: m => !Runtime.production || m.prod,
41
29
  folder: f => f === 'support',
42
30
  file: f => f.role === 'std' && CLI_FILE_REGEX.test(f.sourceFile)
43
31
  })) {
@@ -51,13 +39,12 @@ class $CliCommandRegistry {
51
39
  /**
52
40
  * Registers a cli command
53
41
  */
54
- registerClass(cls: Class, cfg: Partial<CliCommandConfig>): CliCommandConfig {
55
- const source = RuntimeIndex.getFunctionMetadata(cls)!.source;
42
+ registerClass<T extends CliCommandShape>(cls: Class<T>, cfg: Partial<CliCommandConfig>): CliCommandConfig {
43
+ const meta = describeFunction(cls);
56
44
  this.#commands.set(cls, {
57
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
58
- cls: cls as ConcreteClass,
59
- name: getName(source),
60
- commandModule: RuntimeIndex.getModuleFromSource(source)!.name,
45
+ cls,
46
+ name: getName(meta.import),
47
+ commandModule: meta.module,
61
48
  ...cfg,
62
49
  });
63
50
  return this.#commands.get(cls)!;
@@ -67,7 +54,7 @@ class $CliCommandRegistry {
67
54
  * Get config for a given instance
68
55
  */
69
56
  getConfig(cmd: CliCommandShape): CliCommandConfig {
70
- return this.getByClass(this.#getClass(cmd))!;
57
+ return this.getByClass(this.getClass(cmd))!;
71
58
  }
72
59
 
73
60
  /**
@@ -87,11 +74,11 @@ class $CliCommandRegistry {
87
74
  async getInstance(name: string, failOnMissing = false): Promise<CliCommandShape | undefined> {
88
75
  const found = this.getCommandMapping().get(name);
89
76
  if (found) {
90
- const values = Object.values<Class>(await import(found));
77
+ const values = Object.values(await Runtime.importFrom<Record<string, Class>>(found));
91
78
  for (const v of values) {
92
79
  const cfg = this.getByClass(v);
93
80
  if (cfg) {
94
- const inst = new cfg.cls();
81
+ const inst = classConstruct(cfg.cls);
95
82
  if (!inst.isActive || inst.isActive()) {
96
83
  inst._cfg = this.getConfig(inst);
97
84
  return inst;
package/src/schema.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Class } from '@travetto/base';
1
+ import { castKey, castTo, Class } from '@travetto/runtime';
2
2
  import { BindUtil, FieldConfig, SchemaRegistry, SchemaValidator, ValidationResultError } from '@travetto/schema';
3
3
 
4
4
  import { CliCommandRegistry } from './registry';
@@ -52,8 +52,7 @@ export class CliCommandSchemaUtil {
52
52
  * Get schema for a given command
53
53
  */
54
54
  static async getSchema(src: Class | CliCommandShape): Promise<CliCommandSchema> {
55
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
56
- const cls = 'main' in src ? src.constructor as Class : src;
55
+ const cls = 'main' in src ? CliCommandRegistry.getClass(src) : src;
57
56
  if (this.#schemas.has(cls)) {
58
57
  return this.#schemas.get(cls)!;
59
58
  }
@@ -127,22 +126,18 @@ export class CliCommandSchemaUtil {
127
126
  for (const arg of state.all) {
128
127
  switch (arg.type) {
129
128
  case 'flag': {
130
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
131
- const key = arg.fieldName as keyof T;
129
+ const key = castKey<T>(arg.fieldName);
132
130
  const value = arg.value!;
133
131
  if (arg.array) {
134
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
135
- ((template[key] as unknown[]) ??= []).push(value);
132
+ castTo<unknown[]>(template[key] ??= castTo([])).push(value);
136
133
  } else {
137
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
138
- template[key] = value as unknown as T[typeof key];
134
+ template[key] = castTo(value);
139
135
  }
140
136
  break;
141
137
  }
142
138
  case 'arg': {
143
139
  if (arg.array) {
144
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
145
- ((bound[arg.index] ??= []) as unknown[]).push(arg.input);
140
+ castTo<unknown[]>(bound[arg.index] ??= []).push(arg.input);
146
141
  } else {
147
142
  bound[arg.index] = arg.input;
148
143
  }
@@ -150,8 +145,7 @@ export class CliCommandSchemaUtil {
150
145
  }
151
146
  }
152
147
 
153
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
154
- const cls = cmd.constructor as Class<CliCommandShape>;
148
+ const cls = CliCommandRegistry.getClass(cmd);
155
149
  BindUtil.bindSchemaToObject(cls, cmd, template);
156
150
  return BindUtil.coerceMethodParams(cls, 'main', bound);
157
151
  }
@@ -160,9 +154,7 @@ export class CliCommandSchemaUtil {
160
154
  * Validate command shape with the given arguments
161
155
  */
162
156
  static async validate(cmd: CliCommandShape, args: unknown[]): Promise<typeof cmd> {
163
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
164
- const cls = cmd.constructor as Class<CliCommandShape>;
165
-
157
+ const cls = CliCommandRegistry.getClass(cmd);
166
158
  const paramNames = SchemaRegistry.getMethodSchema(cls, 'main').map(x => x.name);
167
159
 
168
160
  const validators = [
package/src/scm.ts CHANGED
@@ -2,8 +2,8 @@ import { spawn } from 'node:child_process';
2
2
  import fs from 'node:fs/promises';
3
3
  import path from 'node:path';
4
4
 
5
- import { ExecUtil } from '@travetto/base';
6
- import { IndexedModule, RuntimeIndex, RuntimeContext } from '@travetto/manifest';
5
+ import { AppError, ExecUtil, Runtime, RuntimeIndex } from '@travetto/runtime';
6
+ import type { IndexedModule } from '@travetto/manifest';
7
7
 
8
8
  export class CliScmUtil {
9
9
  /**
@@ -35,7 +35,7 @@ export class CliScmUtil {
35
35
  * @returns
36
36
  */
37
37
  static async findLastRelease(): Promise<string | undefined> {
38
- const result = await ExecUtil.getResult(spawn('git', ['log', '--pretty=oneline'], { cwd: RuntimeContext.workspace.path }));
38
+ const result = await ExecUtil.getResult(spawn('git', ['log', '--pretty=oneline'], { cwd: Runtime.workspace.path }));
39
39
  return result.stdout
40
40
  .split(/\n/)
41
41
  .find(x => /Publish /.test(x))?.split(/\s+/)?.[0];
@@ -46,12 +46,11 @@ export class CliScmUtil {
46
46
  * @param fromHash
47
47
  * @returns
48
48
  */
49
- static async findChangedFiles(fromHash: string, toHash: string = 'HEAD', emptyOnFail = false): Promise<string[]> {
50
- const ws = RuntimeContext.workspace.path;
49
+ static async findChangedFiles(fromHash: string, toHash: string = 'HEAD'): Promise<string[]> {
50
+ const ws = Runtime.workspace.path;
51
51
  const res = await ExecUtil.getResult(spawn('git', ['diff', '--name-only', `${fromHash}..${toHash}`, ':!**/DOC.*', ':!**/README.*'], { cwd: ws }), { catch: true });
52
- if (!res.valid && emptyOnFail) {
53
- console.warn('Unable to detect changes between', fromHash, toHash, 'with', (res.stderr || res.stdout));
54
- return [];
52
+ if (!res.valid) {
53
+ throw new AppError('Unable to detect changes between', 'data', { fromHash, toHash, output: (res.stderr || res.stdout) });
55
54
  }
56
55
  const out = new Set<string>();
57
56
  for (const line of res.stdout.split(/\n/g)) {
package/src/trv.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import '@travetto/base';
1
+ import '@travetto/runtime';
2
2
 
3
3
  declare global {
4
4
  interface TravettoEnv {
package/src/types.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ConcreteClass } from '@travetto/base';
1
+ import { Class } from '@travetto/runtime';
2
2
 
3
3
  type OrProm<T> = T | Promise<T>;
4
4
 
@@ -19,7 +19,7 @@ export type CliCommandConfig = {
19
19
  name: string;
20
20
  commandModule: string;
21
21
  runTarget?: boolean;
22
- cls: ConcreteClass<CliCommandShape>;
22
+ cls: Class<CliCommandShape>;
23
23
  hidden?: boolean;
24
24
  preMain?: (cmd: CliCommandShape) => void | Promise<void>;
25
25
  };
@@ -49,6 +49,7 @@ export type CliValidationError = {
49
49
  * CLI Command Contract
50
50
  */
51
51
  export interface CliCommandShape<T extends unknown[] = unknown[]> {
52
+
52
53
  /**
53
54
  * Parsed state
54
55
  */
@@ -116,7 +117,7 @@ export type CliCommandShapeFields = {
116
117
  /**
117
118
  * CLI Command argument/flag shape
118
119
  */
119
- export type CliCommandInput = {
120
+ export type CliCommandInput<K extends string = string> = {
120
121
  name: string;
121
122
  description?: string;
122
123
  type: 'string' | 'file' | 'number' | 'boolean' | 'date' | 'regex' | 'module';
@@ -125,19 +126,19 @@ export type CliCommandInput = {
125
126
  required?: boolean;
126
127
  array?: boolean;
127
128
  default?: unknown;
128
- flagNames?: string[];
129
+ flagNames?: K[];
129
130
  envVars?: string[];
130
131
  };
131
132
 
132
133
  /**
133
134
  * CLI Command schema shape
134
135
  */
135
- export type CliCommandSchema = {
136
+ export interface CliCommandSchema<K extends string = string> {
136
137
  name: string;
137
138
  title: string;
138
139
  commandModule: string;
139
140
  runTarget?: boolean;
140
141
  description?: string;
141
142
  args: CliCommandInput[];
142
- flags: CliCommandInput[];
143
- };
143
+ flags: CliCommandInput<K>[];
144
+ }
package/src/util.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { spawn } from 'node:child_process';
2
2
 
3
- import { Env, ExecUtil, ShutdownManager } from '@travetto/base';
4
- import { RuntimeContext } from '@travetto/manifest';
3
+ import { Env, ExecUtil, ShutdownManager, Runtime } from '@travetto/runtime';
5
4
 
6
5
  import { CliCommandShape, CliCommandShapeFields, RunResponse } from './types';
7
6
 
@@ -15,10 +14,10 @@ export class CliUtil {
15
14
  * @returns
16
15
  */
17
16
  static getSimpleModuleName(placeholder: string, module?: string): string {
18
- const simple = (module ?? RuntimeContext.main.name).replace(/[\/]/, '_').replace(/@/, '');
17
+ const simple = (module ?? Runtime.main.name).replace(/[\/]/, '_').replace(/@/, '');
19
18
  if (!simple) {
20
19
  return placeholder;
21
- } else if (!module && RuntimeContext.monoRoot) {
20
+ } else if (!module && Runtime.monoRoot) {
22
21
  return placeholder;
23
22
  } else {
24
23
  return placeholder.replace('<module>', simple);
@@ -32,7 +31,7 @@ export class CliUtil {
32
31
  if (ipc && process.connected) {
33
32
  process.once('disconnect', () => process.exit());
34
33
  }
35
- if (Env.TRV_CAN_RESTART.isFalse || !(cmd.canRestart ?? !Env.production)) {
34
+ if (Env.TRV_CAN_RESTART.isFalse || !(cmd.canRestart ?? !Runtime.production)) {
36
35
  Env.TRV_CAN_RESTART.clear();
37
36
  return;
38
37
  }
@@ -65,7 +64,7 @@ export class CliUtil {
65
64
  data: {
66
65
  name: cmd._cfg!.name, env,
67
66
  commandModule: cmd._cfg!.commandModule,
68
- module: RuntimeContext.main.name,
67
+ module: Runtime.main.name,
69
68
  args: process.argv.slice(3),
70
69
  }
71
70
  };
@@ -80,7 +79,7 @@ export class CliUtil {
80
79
  * Debug if IPC available
81
80
  */
82
81
  static async debugIfIpc<T extends CliCommandShapeFields & CliCommandShape>(cmd: T): Promise<boolean> {
83
- return (cmd.debugIpc ?? !Env.production) && this.triggerIpc('run', cmd);
82
+ return (cmd.debugIpc ?? !Runtime.production) && this.triggerIpc('run', cmd);
84
83
  }
85
84
 
86
85
  /**
@@ -1,4 +1,4 @@
1
- import { Env } from '@travetto/base';
1
+ import { Env } from '@travetto/runtime';
2
2
 
3
3
  import { CliCommand } from '../src/decorators';
4
4
  import { CliCommandSchema, CliCommandShape, CliValidationError } from '../src/types';
@@ -1,8 +1,5 @@
1
- import fs from 'node:fs/promises';
2
- import path from 'node:path';
3
-
1
+ import { Runtime } from '@travetto/runtime';
4
2
  import { CliCommandShape, CliCommand, CliValidationError, ParsedState } from '@travetto/cli';
5
- import { RuntimeIndex, RuntimeContext } from '@travetto/manifest';
6
3
  import { Ignore } from '@travetto/schema';
7
4
 
8
5
  /**
@@ -14,19 +11,10 @@ export class MainCommand implements CliCommandShape {
14
11
  @Ignore()
15
12
  _parsed: ParsedState;
16
13
 
17
- async #getImport(fileOrImport: string): Promise<string | undefined> {
18
- // If referenced file exists
19
- let file = fileOrImport;
20
- if (await (fs.stat(path.resolve(fileOrImport)).then(() => true, () => false))) {
21
- file = path.join(RuntimeContext.main.name, fileOrImport);
22
- }
23
-
24
- return RuntimeIndex.getFromImport(file)?.import;
25
- }
26
-
27
14
  async validate(fileOrImport: string): Promise<CliValidationError | undefined> {
28
- const imp = await this.#getImport(fileOrImport);
29
- if (!imp) {
15
+ try {
16
+ await Runtime.importFrom(fileOrImport);
17
+ } catch {
30
18
  return { message: `Unknown file: ${fileOrImport}` };
31
19
  }
32
20
  }
@@ -34,8 +22,7 @@ export class MainCommand implements CliCommandShape {
34
22
  async main(fileOrImport: string, args: string[] = []): Promise<void> {
35
23
  let res: unknown;
36
24
  try {
37
- const imp = await this.#getImport(fileOrImport);
38
- const mod = await import(imp!);
25
+ const mod = await Runtime.importFrom<{ main(..._: unknown[]): Promise<unknown> }>(fileOrImport);
39
26
  res = await mod.main(...args, ...this._parsed.unknown);
40
27
  } catch (err) {
41
28
  res = err;
@@ -20,16 +20,16 @@ export class CliCommandTransformer {
20
20
  }
21
21
 
22
22
  // Find runnable method
23
- const runMethod = node.members
23
+ const mainMethod = node.members
24
24
  .find((x): x is ts.MethodDeclaration =>
25
25
  ts.isMethodDeclaration(x) && x.name!.getText() === 'main'
26
26
  );
27
27
 
28
- if (!runMethod) {
28
+ if (!mainMethod) {
29
29
  return node;
30
30
  }
31
31
 
32
- const members = node.members.map(x => ts.isMethodDeclaration(x) && x === runMethod ?
32
+ const members = node.members.map(x => ts.isMethodDeclaration(x) && x === mainMethod ?
33
33
  state.factory.updateMethodDeclaration(
34
34
  x,
35
35
  x.modifiers,