@travetto/cli 6.0.0-rc.1 → 6.0.0-rc.3
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 +34 -27
- package/__index__.ts +14 -14
- package/package.json +3 -3
- package/src/decorators.ts +5 -5
- package/src/error.ts +4 -3
- package/src/execute.ts +7 -9
- package/src/help.ts +7 -7
- package/src/module.ts +2 -2
- package/src/parse.ts +21 -12
- package/src/registry.ts +2 -2
- package/src/schema.ts +3 -3
- package/src/scm.ts +6 -6
- package/src/service.ts +3 -3
- package/src/types.ts +4 -8
- package/src/util.ts +2 -20
- package/support/cli.cli_schema.ts +5 -5
- package/support/cli.main.ts +6 -6
- package/support/cli.service.ts +2 -2
- package/support/transformer.cli.ts +1 -1
package/README.md
CHANGED
|
@@ -43,14 +43,13 @@ Commands:
|
|
|
43
43
|
repo:publish Publish all pending modules
|
|
44
44
|
repo:version Version all changed dependencies
|
|
45
45
|
repo:version-sync Enforces all packages to write out their versions and dependencies
|
|
46
|
-
rest:client Run client rest operation
|
|
47
|
-
rest:rpc Run client rest operation
|
|
48
46
|
run:double Doubles a number
|
|
49
|
-
run:rest Run a rest server as an application
|
|
50
47
|
scaffold Command to run scaffolding
|
|
51
48
|
service Allows for running services
|
|
52
49
|
test Launch test framework and execute tests
|
|
53
50
|
test:watch Invoke the test watcher
|
|
51
|
+
web:http Run a web server
|
|
52
|
+
web:rpc-client Generate the web-rpc client
|
|
54
53
|
```
|
|
55
54
|
|
|
56
55
|
This listing is from the [Travetto](https://travetto.dev) monorepo, and represents the majority of tools that can be invoked from the command line.
|
|
@@ -91,10 +90,11 @@ Examples of mappings:
|
|
|
91
90
|
* `cli.test.ts` maps to `test`
|
|
92
91
|
* `cli.pack_docker.ts` maps to `pack:docker`
|
|
93
92
|
* `cli.email_template.ts` maps to `email:template`
|
|
93
|
+
|
|
94
94
|
The pattern is that underscores(_) translate to colons (:), and the `cli.` prefix, and `.ts` suffix are dropped.
|
|
95
95
|
|
|
96
96
|
## Binding Flags
|
|
97
|
-
[@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/decorators.ts#L84) is a wrapper for [@Schema](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/schema.ts#
|
|
97
|
+
[@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/decorators.ts#L84) is a wrapper for [@Schema](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/schema.ts#L13), and so every class that uses the [@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/decorators.ts#L84) decorator is now a full [@Schema](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/schema.ts#L13) class. The fields of the class represent the flags that are available to the command.
|
|
98
98
|
|
|
99
99
|
**Code: Basic Command with Flag**
|
|
100
100
|
```typescript
|
|
@@ -133,9 +133,9 @@ HELLO
|
|
|
133
133
|
|
|
134
134
|
The [@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/decorators.ts#L84) supports the following data types for flags:
|
|
135
135
|
* Boolean values
|
|
136
|
-
* Number values. The [@Integer](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
137
|
-
* String values. [@MinLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
138
|
-
* Date values. The [@Min](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
136
|
+
* Number values. The [@Integer](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L164), [@Float](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L170), [@Precision](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L158), [@Min](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L99) and [@Max](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L109) decorators help provide additional validation.
|
|
137
|
+
* String values. [@MinLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L99), [@MaxLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L109), [@Match](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L91) and [@Enum](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L70) provide additional constraints
|
|
138
|
+
* Date values. The [@Min](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L99) and [@Max](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L109) decorators help provide additional validation.
|
|
139
139
|
* String lists. Same as String, but allowing multiple values.
|
|
140
140
|
* Numeric lists. Same as Number, but allowing multiple values.
|
|
141
141
|
|
|
@@ -206,7 +206,7 @@ export class BasicCommand {
|
|
|
206
206
|
reverse?: boolean;
|
|
207
207
|
|
|
208
208
|
main(@Min(1) @Max(10) volumes: number[]) {
|
|
209
|
-
console.log(volumes.
|
|
209
|
+
console.log(volumes.toSorted((a, b) => (a - b) * (this.reverse ? -1 : 1)).join(' '));
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
212
|
```
|
|
@@ -251,7 +251,7 @@ $ trv basic:arglist -r 10 5 3 9 8 1
|
|
|
251
251
|
```
|
|
252
252
|
|
|
253
253
|
## Customization
|
|
254
|
-
By default, all fields are treated as flags and all parameters of `main()` are treated as arguments within the validation process. Like the standard [@Schema](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/schema.ts#
|
|
254
|
+
By default, all fields are treated as flags and all parameters of `main()` are treated as arguments within the validation process. Like the standard [@Schema](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/schema.ts#L13) behavior, we can leverage the metadata of the fields/parameters to help provide additional customization/context for the users of the commands.
|
|
255
255
|
|
|
256
256
|
**Code: Custom Command with Metadata**
|
|
257
257
|
```typescript
|
|
@@ -381,6 +381,7 @@ The flag files can be included in one of a few ways:
|
|
|
381
381
|
* `+=<name>` - This translates into `<mod>/support/<name>.flags`, which is a convenient shorthand.
|
|
382
382
|
* `+=<mod>/path/file.flags` - This is a path-related file that will be resolved from the module's location.
|
|
383
383
|
* `+=/path/file.flags` - This is an absolute path that will be read from the root of the file system.
|
|
384
|
+
|
|
384
385
|
Ultimately, after resolution, the content of these files will be injected inline within the location.
|
|
385
386
|
|
|
386
387
|
**Code: Final arguments after Flag File resolution**
|
|
@@ -389,7 +390,7 @@ npx trv call:db --host localhost --port 3306 --username app --password <custom>
|
|
|
389
390
|
```
|
|
390
391
|
|
|
391
392
|
## VSCode Integration
|
|
392
|
-
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. [
|
|
393
|
+
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. [Web API](https://github.com/travetto/travetto/tree/main/module/web#readme "Declarative api for Web Applications with support for the dependency injection.") does expose a cli target `web:http` that will show up, to help run/debug a web 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#L84) decorator. This means the target will be visible within the editor tooling.
|
|
393
394
|
|
|
394
395
|
**Code: Simple Run Target**
|
|
395
396
|
```typescript
|
|
@@ -424,7 +425,7 @@ export interface CliCommandShape<T extends unknown[] = unknown[]> {
|
|
|
424
425
|
/**
|
|
425
426
|
* Action target of the command
|
|
426
427
|
*/
|
|
427
|
-
main(...args: T): OrProm<
|
|
428
|
+
main(...args: T): OrProm<undefined | void>;
|
|
428
429
|
/**
|
|
429
430
|
* Run before main runs
|
|
430
431
|
*/
|
|
@@ -457,22 +458,23 @@ export interface CliCommandShape<T extends unknown[] = unknown[]> {
|
|
|
457
458
|
```
|
|
458
459
|
|
|
459
460
|
### Dependency Injection
|
|
460
|
-
If the goal is to run a more complex application, which may include depending on [Dependency Injection](https://github.com/travetto/travetto/tree/main/module/di#readme "Dependency registration/management and injection support."), we can take a look at [
|
|
461
|
+
If the goal is to run a more complex application, which may include depending on [Dependency Injection](https://github.com/travetto/travetto/tree/main/module/di#readme "Dependency registration/management and injection support."), we can take a look at [Web API](https://github.com/travetto/travetto/tree/main/module/web#readme "Declarative api for Web Applications with support for the dependency injection.")'s target:
|
|
461
462
|
|
|
462
463
|
**Code: Simple Run Target**
|
|
463
464
|
```typescript
|
|
464
|
-
import { Runtime } from '@travetto/runtime';
|
|
465
|
+
import { Runtime, ShutdownManager, toConcrete } from '@travetto/runtime';
|
|
465
466
|
import { DependencyRegistry } from '@travetto/di';
|
|
466
467
|
import { CliCommand, CliCommandShape } from '@travetto/cli';
|
|
468
|
+
import { NetUtil } from '@travetto/web';
|
|
469
|
+
import { RootRegistry } from '@travetto/registry';
|
|
467
470
|
|
|
468
|
-
import {
|
|
469
|
-
import { RestNetUtil } from '../src/util/net';
|
|
471
|
+
import type { WebHttpServer } from '../src/types.ts';
|
|
470
472
|
|
|
471
473
|
/**
|
|
472
|
-
* Run a
|
|
474
|
+
* Run a web server
|
|
473
475
|
*/
|
|
474
476
|
@CliCommand({ runTarget: true, with: { debugIpc: true, canRestart: true, module: true, env: true } })
|
|
475
|
-
export class
|
|
477
|
+
export class WebHttpCommand implements CliCommandShape {
|
|
476
478
|
|
|
477
479
|
/** Port to run on */
|
|
478
480
|
port?: number;
|
|
@@ -482,35 +484,40 @@ export class RunRestCommand implements CliCommandShape {
|
|
|
482
484
|
|
|
483
485
|
preMain(): void {
|
|
484
486
|
if (this.port) {
|
|
485
|
-
process.env.
|
|
487
|
+
process.env.WEB_HTTP_PORT = `${this.port}`;
|
|
486
488
|
}
|
|
487
489
|
}
|
|
488
490
|
|
|
489
|
-
async main(): Promise<
|
|
490
|
-
|
|
491
|
+
async main(): Promise<void> {
|
|
492
|
+
await RootRegistry.init();
|
|
493
|
+
const instance = await DependencyRegistry.getInstance(toConcrete<WebHttpServer>());
|
|
494
|
+
|
|
495
|
+
let res;
|
|
491
496
|
try {
|
|
492
|
-
|
|
497
|
+
res = await instance.serve();
|
|
493
498
|
} catch (err) {
|
|
494
|
-
if (
|
|
495
|
-
await
|
|
496
|
-
|
|
499
|
+
if (NetUtil.isPortUsedError(err) && !Runtime.production && this.killConflict) {
|
|
500
|
+
await NetUtil.freePort(err.port);
|
|
501
|
+
res = await instance.serve();
|
|
497
502
|
}
|
|
498
503
|
throw err;
|
|
499
504
|
}
|
|
505
|
+
ShutdownManager.onGracefulShutdown(res.kill, this);
|
|
506
|
+
return res.wait;
|
|
500
507
|
}
|
|
501
508
|
}
|
|
502
509
|
```
|
|
503
510
|
|
|
504
|
-
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/
|
|
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/runtime/src/context.ts#L12) `name` defined in the [Runtime](https://github.com/travetto/travetto/tree/main/module/runtime#readme "Runtime for travetto applications.") module.
|
|
505
512
|
|
|
506
513
|
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.
|
|
507
514
|
|
|
508
515
|
### Custom Validation
|
|
509
|
-
In addition to dependency injection, the command contract also allows for a custom validation function, which will have access to bound command (flags, and args) as well as the unknown arguments. When a command implements this method, any [CliValidationError](https://github.com/travetto/travetto/tree/main/module/cli/src/types.ts#
|
|
516
|
+
In addition to dependency injection, the command contract also allows for a custom validation function, which will have access to bound command (flags, and args) as well as the unknown arguments. When a command implements this method, any [CliValidationError](https://github.com/travetto/travetto/tree/main/module/cli/src/types.ts#L32) errors that are returned will be shared with the user, and fail to invoke the `main` method.
|
|
510
517
|
|
|
511
518
|
**Code: CliValidationError**
|
|
512
519
|
```typescript
|
|
513
|
-
export
|
|
520
|
+
export interface CliValidationError {
|
|
514
521
|
/**
|
|
515
522
|
* The error message
|
|
516
523
|
*/
|
package/__index__.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import type { } from './src/trv';
|
|
2
|
-
export * from './src/types';
|
|
3
|
-
export * from './src/decorators';
|
|
4
|
-
export * from './src/execute';
|
|
5
|
-
export * from './src/error';
|
|
6
|
-
export * from './src/schema';
|
|
7
|
-
export * from './src/registry';
|
|
8
|
-
export * from './src/help';
|
|
9
|
-
export * from './src/color';
|
|
10
|
-
export * from './src/module';
|
|
11
|
-
export * from './src/scm';
|
|
12
|
-
export * from './src/parse';
|
|
13
|
-
export * from './src/service';
|
|
14
|
-
export * from './src/util';
|
|
1
|
+
import type { } from './src/trv.d.ts';
|
|
2
|
+
export * from './src/types.ts';
|
|
3
|
+
export * from './src/decorators.ts';
|
|
4
|
+
export * from './src/execute.ts';
|
|
5
|
+
export * from './src/error.ts';
|
|
6
|
+
export * from './src/schema.ts';
|
|
7
|
+
export * from './src/registry.ts';
|
|
8
|
+
export * from './src/help.ts';
|
|
9
|
+
export * from './src/color.ts';
|
|
10
|
+
export * from './src/module.ts';
|
|
11
|
+
export * from './src/scm.ts';
|
|
12
|
+
export * from './src/parse.ts';
|
|
13
|
+
export * from './src/service.ts';
|
|
14
|
+
export * from './src/util.ts';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/cli",
|
|
3
|
-
"version": "6.0.0-rc.
|
|
3
|
+
"version": "6.0.0-rc.3",
|
|
4
4
|
"description": "CLI infrastructure for Travetto framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"directory": "module/cli"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@travetto/schema": "^6.0.0-rc.
|
|
32
|
-
"@travetto/terminal": "^6.0.0-rc.
|
|
31
|
+
"@travetto/schema": "^6.0.0-rc.2",
|
|
32
|
+
"@travetto/terminal": "^6.0.0-rc.2"
|
|
33
33
|
},
|
|
34
34
|
"travetto": {
|
|
35
35
|
"displayName": "Command Line Interface",
|
package/src/decorators.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Class, ClassInstance, Env, Runtime, RuntimeIndex, describeFunction } from '@travetto/runtime';
|
|
2
2
|
import { FieldConfig, SchemaRegistry } from '@travetto/schema';
|
|
3
3
|
|
|
4
|
-
import { CliCommandShape, CliCommandShapeFields } from './types';
|
|
5
|
-
import { CliCommandRegistry } from './registry';
|
|
6
|
-
import { CliModuleUtil } from './module';
|
|
7
|
-
import { CliParseUtil } from './parse';
|
|
8
|
-
import { CliUtil } from './util';
|
|
4
|
+
import { CliCommandShape, CliCommandShapeFields } from './types.ts';
|
|
5
|
+
import { CliCommandRegistry } from './registry.ts';
|
|
6
|
+
import { CliModuleUtil } from './module.ts';
|
|
7
|
+
import { CliParseUtil } from './parse.ts';
|
|
8
|
+
import { CliUtil } from './util.ts';
|
|
9
9
|
|
|
10
10
|
type Cmd = CliCommandShape & { env?: string };
|
|
11
11
|
|
package/src/error.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { AppError, Runtime } from '@travetto/runtime';
|
|
2
2
|
import { PackageUtil } from '@travetto/manifest';
|
|
3
3
|
|
|
4
|
-
import { cliTpl } from './color';
|
|
5
|
-
import { CliValidationError, CliCommandShape } from './types';
|
|
4
|
+
import { cliTpl } from './color.ts';
|
|
5
|
+
import { CliValidationError, CliCommandShape } from './types.ts';
|
|
6
6
|
|
|
7
7
|
const COMMAND_PACKAGE = [
|
|
8
8
|
[/^test(:watch)?$/, 'test', false],
|
|
@@ -12,7 +12,8 @@ const COMMAND_PACKAGE = [
|
|
|
12
12
|
[/^openapi:(spec|client)$/, 'openapi', true],
|
|
13
13
|
[/^email:(compile|editor)$/, 'email-compiler', false],
|
|
14
14
|
[/^pack(:zip|:docker)?$/, 'pack', false],
|
|
15
|
-
[/^
|
|
15
|
+
[/^web:http$/, 'web-http-server', false],
|
|
16
|
+
[/^web:rpc-client$/, 'web-rpc', false],
|
|
16
17
|
] as const;
|
|
17
18
|
|
|
18
19
|
/**
|
package/src/execute.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { ConsoleManager, Runtime, ShutdownManager } from '@travetto/runtime';
|
|
2
2
|
|
|
3
|
-
import { HelpUtil } from './help';
|
|
4
|
-
import { CliCommandRegistry } from './registry';
|
|
5
|
-
import { CliCommandSchemaUtil } from './schema';
|
|
6
|
-
import { CliUnknownCommandError, CliValidationResultError } from './error';
|
|
7
|
-
import { CliParseUtil } from './parse';
|
|
8
|
-
import {
|
|
9
|
-
import { CliCommandShape } from './types';
|
|
3
|
+
import { HelpUtil } from './help.ts';
|
|
4
|
+
import { CliCommandRegistry } from './registry.ts';
|
|
5
|
+
import { CliCommandSchemaUtil } from './schema.ts';
|
|
6
|
+
import { CliUnknownCommandError, CliValidationResultError } from './error.ts';
|
|
7
|
+
import { CliParseUtil } from './parse.ts';
|
|
8
|
+
import { CliCommandShape } from './types.ts';
|
|
10
9
|
|
|
11
10
|
/**
|
|
12
11
|
* Execution manager
|
|
@@ -55,8 +54,7 @@ export class ExecutionManager {
|
|
|
55
54
|
await command.preMain?.();
|
|
56
55
|
|
|
57
56
|
ConsoleManager.debug(Runtime.debug);
|
|
58
|
-
|
|
59
|
-
await CliUtil.listenForResponse(result);
|
|
57
|
+
await command.main(...boundArgs);
|
|
60
58
|
}
|
|
61
59
|
|
|
62
60
|
/**
|
package/src/help.ts
CHANGED
|
@@ -2,12 +2,12 @@ import util from 'node:util';
|
|
|
2
2
|
|
|
3
3
|
import { castKey, castTo, Primitive } from '@travetto/runtime';
|
|
4
4
|
|
|
5
|
-
import { cliTpl } from './color';
|
|
6
|
-
import { CliCommandShape } from './types';
|
|
7
|
-
import { CliCommandRegistry } from './registry';
|
|
8
|
-
import { CliCommandSchemaUtil } from './schema';
|
|
9
|
-
import { CliValidationResultError } from './error';
|
|
10
|
-
import { isBoolFlag } from './parse';
|
|
5
|
+
import { cliTpl } from './color.ts';
|
|
6
|
+
import { CliCommandShape } from './types.ts';
|
|
7
|
+
import { CliCommandRegistry } from './registry.ts';
|
|
8
|
+
import { CliCommandSchemaUtil } from './schema.ts';
|
|
9
|
+
import { CliValidationResultError } from './error.ts';
|
|
10
|
+
import { isBoolFlag } from './parse.ts';
|
|
11
11
|
|
|
12
12
|
const validationSourceMap = {
|
|
13
13
|
custom: '',
|
|
@@ -97,7 +97,7 @@ export class HelpUtil {
|
|
|
97
97
|
*/
|
|
98
98
|
static async renderAllHelp(title?: string): Promise<string> {
|
|
99
99
|
const rows: string[] = [];
|
|
100
|
-
const keys = [...CliCommandRegistry.getCommandMapping().keys()].
|
|
100
|
+
const keys = [...CliCommandRegistry.getCommandMapping().keys()].toSorted((a, b) => a.localeCompare(b));
|
|
101
101
|
const maxWidth = keys.reduce((a, b) => Math.max(a, util.stripVTControlCharacters(b).length), 0);
|
|
102
102
|
|
|
103
103
|
for (const cmd of keys) {
|
package/src/module.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Runtime, RuntimeIndex } from '@travetto/runtime';
|
|
2
2
|
import type { IndexedModule } from '@travetto/manifest';
|
|
3
3
|
|
|
4
|
-
import { CliScmUtil } from './scm';
|
|
4
|
+
import { CliScmUtil } from './scm.ts';
|
|
5
5
|
|
|
6
6
|
type ModuleGraphEntry = { children: Set<string>, name: string, active: Set<string>, parents?: string[] };
|
|
7
7
|
|
|
@@ -35,7 +35,7 @@ export class CliModuleUtil {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
return [...out.values()]
|
|
38
|
-
.
|
|
38
|
+
.toSorted((a, b) => a.name.localeCompare(b.name));
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
/**
|
package/src/parse.ts
CHANGED
|
@@ -2,7 +2,7 @@ import fs from 'node:fs/promises';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
|
|
4
4
|
import { Runtime } from '@travetto/runtime';
|
|
5
|
-
import { CliCommandInput, CliCommandSchema, ParsedState } from './types';
|
|
5
|
+
import { CliCommandInput, CliCommandSchema, ParsedState } from './types.ts';
|
|
6
6
|
|
|
7
7
|
type ParsedInput = ParsedState['all'][number];
|
|
8
8
|
|
|
@@ -77,15 +77,32 @@ export class CliParseUtil {
|
|
|
77
77
|
return { next: i, value: collected.length ? String.fromCharCode(...collected) : undefined };
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Get a user-specified module if present
|
|
82
|
+
*/
|
|
83
|
+
static getSpecifiedModule(schema: CliCommandSchema, args: string[]): string | undefined {
|
|
84
|
+
const SEP = args.includes(RAW_SEP) ? args.indexOf(RAW_SEP) : args.length;
|
|
85
|
+
const input = schema.flags.find(x => x.type === 'module');
|
|
86
|
+
const ENV_KEY = input?.flagNames?.filter(x => x.startsWith(ENV_PRE)).map(x => x.replace(ENV_PRE, ''))[0] ?? '';
|
|
87
|
+
const flags = new Set(input?.flagNames ?? []);
|
|
88
|
+
const check = (k?: string, v?: string): string | undefined => flags.has(k!) ? v : undefined;
|
|
89
|
+
return args.reduce(
|
|
90
|
+
(m, x, i, arr) =>
|
|
91
|
+
(i < SEP ? check(arr[i - 1], x) ?? check(...x.split('=')) : undefined) ?? m,
|
|
92
|
+
process.env[ENV_KEY]
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
80
96
|
/**
|
|
81
97
|
* Read configuration file given flag
|
|
82
98
|
*/
|
|
83
|
-
static async readFlagFile(flag: string, mod
|
|
99
|
+
static async readFlagFile(flag: string, mod?: string): Promise<string[]> {
|
|
84
100
|
const key = flag.replace(CONFIG_PRE, '');
|
|
101
|
+
const overrides = { '@': mod ?? Runtime.main.name };
|
|
85
102
|
|
|
86
103
|
// We have a file
|
|
87
104
|
const rel = (key.includes('/') ? key : `@#support/pack.${key}.flags`)
|
|
88
|
-
.replace(/^(@[^#]*)#(.*)$/, (_, imp, rest) => `${Runtime.modulePath(imp)}/${rest}`);
|
|
105
|
+
.replace(/^(@[^#]*)#(.*)$/, (_, imp, rest) => `${Runtime.modulePath(imp, overrides)}/${rest}`);
|
|
89
106
|
|
|
90
107
|
const file = path.resolve(rel);
|
|
91
108
|
|
|
@@ -129,15 +146,7 @@ export class CliParseUtil {
|
|
|
129
146
|
*/
|
|
130
147
|
static async expandArgs(schema: CliCommandSchema, args: string[]): Promise<string[]> {
|
|
131
148
|
const SEP = args.includes(RAW_SEP) ? args.indexOf(RAW_SEP) : args.length;
|
|
132
|
-
const
|
|
133
|
-
const ENV_KEY = input?.flagNames?.filter(x => x.startsWith(ENV_PRE)).map(x => x.replace(ENV_PRE, ''))[0] ?? '';
|
|
134
|
-
const flags = new Set(input?.flagNames ?? []);
|
|
135
|
-
const check = (k?: string, v?: string): string | undefined => flags.has(k!) ? v : undefined;
|
|
136
|
-
const mod = args.reduce(
|
|
137
|
-
(m, x, i, arr) =>
|
|
138
|
-
(i < SEP ? check(arr[i - 1], x) ?? check(...x.split('=')) : undefined) ?? m,
|
|
139
|
-
process.env[ENV_KEY] || Runtime.main.name
|
|
140
|
-
);
|
|
149
|
+
const mod = this.getSpecifiedModule(schema, args);
|
|
141
150
|
return (await Promise.all(args.map((x, i) =>
|
|
142
151
|
x.startsWith(CONFIG_PRE) && (i < SEP || SEP < 0) ? this.readFlagFile(x, mod) : x))).flat();
|
|
143
152
|
}
|
package/src/registry.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { asConstructable, Class, classConstruct, describeFunction, Runtime, RuntimeIndex } from '@travetto/runtime';
|
|
2
2
|
|
|
3
|
-
import { CliCommandConfig, CliCommandShape } from './types';
|
|
4
|
-
import { CliUnknownCommandError } from './error';
|
|
3
|
+
import { CliCommandConfig, CliCommandShape } from './types.ts';
|
|
4
|
+
import { CliUnknownCommandError } from './error.ts';
|
|
5
5
|
|
|
6
6
|
const CLI_FILE_REGEX = /\/cli[.](?<name>.{0,100}?)([.]tsx?)?$/;
|
|
7
7
|
const getName = (s: string): string => (s.match(CLI_FILE_REGEX)?.groups?.name ?? s).replaceAll('_', ':');
|
package/src/schema.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { castKey, castTo, Class } from '@travetto/runtime';
|
|
2
2
|
import { BindUtil, FieldConfig, SchemaRegistry, SchemaValidator, ValidationResultError } from '@travetto/schema';
|
|
3
3
|
|
|
4
|
-
import { CliCommandRegistry } from './registry';
|
|
5
|
-
import { ParsedState, CliCommandInput, CliCommandSchema, CliCommandShape } from './types';
|
|
6
|
-
import { CliValidationResultError } from './error';
|
|
4
|
+
import { CliCommandRegistry } from './registry.ts';
|
|
5
|
+
import { ParsedState, CliCommandInput, CliCommandSchema, CliCommandShape } from './types.ts';
|
|
6
|
+
import { CliValidationResultError } from './error.ts';
|
|
7
7
|
|
|
8
8
|
const LONG_FLAG = /^--[a-z][^= ]+/i;
|
|
9
9
|
const SHORT_FLAG = /^-[a-z]/i;
|
package/src/scm.ts
CHANGED
|
@@ -48,18 +48,18 @@ export class CliScmUtil {
|
|
|
48
48
|
*/
|
|
49
49
|
static async findChangedFiles(fromHash: string, toHash: string = 'HEAD'): Promise<string[]> {
|
|
50
50
|
const ws = Runtime.workspace.path;
|
|
51
|
-
const
|
|
52
|
-
if (!
|
|
53
|
-
throw new AppError('Unable to detect changes between', { category: 'data', details: { fromHash, toHash, output: (
|
|
51
|
+
const result = await ExecUtil.getResult(spawn('git', ['diff', '--name-only', `${fromHash}..${toHash}`, ':!**/DOC.*', ':!**/README.*'], { cwd: ws }), { catch: true });
|
|
52
|
+
if (!result.valid) {
|
|
53
|
+
throw new AppError('Unable to detect changes between', { category: 'data', details: { fromHash, toHash, output: (result.stderr || result.stdout) } });
|
|
54
54
|
}
|
|
55
55
|
const out = new Set<string>();
|
|
56
|
-
for (const line of
|
|
56
|
+
for (const line of result.stdout.split(/\n/g)) {
|
|
57
57
|
const entry = RuntimeIndex.getEntry(path.resolve(ws, line));
|
|
58
58
|
if (entry) {
|
|
59
59
|
out.add(entry.sourceFile);
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
-
return [...out].
|
|
62
|
+
return [...out].toSorted((a, b) => a.localeCompare(b));
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
/**
|
|
@@ -77,7 +77,7 @@ export class CliScmUtil {
|
|
|
77
77
|
.filter(x => !!x);
|
|
78
78
|
|
|
79
79
|
return [...new Set(mods)]
|
|
80
|
-
.
|
|
80
|
+
.toSorted((a, b) => a.name.localeCompare(b.name));
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
/**
|
package/src/service.ts
CHANGED
|
@@ -53,9 +53,9 @@ export class ServiceRunner {
|
|
|
53
53
|
if (!this.svc.ready?.url || !full) {
|
|
54
54
|
return true;
|
|
55
55
|
} else {
|
|
56
|
-
const
|
|
57
|
-
const text = await
|
|
58
|
-
if (
|
|
56
|
+
const response = await fetch(this.svc.ready.url, { method: 'GET' });
|
|
57
|
+
const text = await response.text();
|
|
58
|
+
if (response.ok && (this.svc.ready.test?.(text) ?? true)) {
|
|
59
59
|
return true;
|
|
60
60
|
}
|
|
61
61
|
}
|
package/src/types.ts
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
import { Class } from '@travetto/runtime';
|
|
2
2
|
|
|
3
3
|
type OrProm<T> = T | Promise<T>;
|
|
4
|
-
|
|
5
|
-
export type RunResponse =
|
|
6
|
-
{ wait(): Promise<unknown> } |
|
|
7
|
-
{ on(event: 'close', cb: Function): unknown } |
|
|
8
|
-
{ close: () => (void | Promise<void>) } | void | undefined;
|
|
9
|
-
|
|
10
4
|
type ParsedFlag = { type: 'flag', input: string, array?: boolean, fieldName: string, value?: unknown };
|
|
11
5
|
type ParsedArg = { type: 'arg', input: string, array?: boolean, index: number };
|
|
12
6
|
type ParsedUnknown = { type: 'unknown', input: string };
|
|
@@ -33,8 +27,9 @@ export type ParsedState = {
|
|
|
33
27
|
|
|
34
28
|
/**
|
|
35
29
|
* Constrained version of Schema's Validation Error
|
|
30
|
+
* @concrete
|
|
36
31
|
*/
|
|
37
|
-
export
|
|
32
|
+
export interface CliValidationError {
|
|
38
33
|
/**
|
|
39
34
|
* The error message
|
|
40
35
|
*/
|
|
@@ -47,6 +42,7 @@ export type CliValidationError = {
|
|
|
47
42
|
|
|
48
43
|
/**
|
|
49
44
|
* CLI Command Contract
|
|
45
|
+
* @concrete
|
|
50
46
|
*/
|
|
51
47
|
export interface CliCommandShape<T extends unknown[] = unknown[]> {
|
|
52
48
|
|
|
@@ -61,7 +57,7 @@ export interface CliCommandShape<T extends unknown[] = unknown[]> {
|
|
|
61
57
|
/**
|
|
62
58
|
* Action target of the command
|
|
63
59
|
*/
|
|
64
|
-
main(...args: T): OrProm<
|
|
60
|
+
main(...args: T): OrProm<undefined | void>;
|
|
65
61
|
/**
|
|
66
62
|
* Run before main runs
|
|
67
63
|
*/
|
package/src/util.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
|
|
3
|
-
import { Env, ExecUtil,
|
|
3
|
+
import { Env, ExecUtil, Runtime } from '@travetto/runtime';
|
|
4
4
|
|
|
5
|
-
import { CliCommandShape, CliCommandShapeFields
|
|
5
|
+
import { CliCommandShape, CliCommandShapeFields } from './types.ts';
|
|
6
6
|
|
|
7
7
|
const IPC_ALLOWED_ENV = new Set(['NODE_OPTIONS']);
|
|
8
8
|
const IPC_INVALID_ENV = new Set(['PS1', 'INIT_CWD', 'COLOR', 'LANGUAGE', 'PROFILEHOME', '_']);
|
|
@@ -11,7 +11,6 @@ const validEnv = (k: string): boolean => IPC_ALLOWED_ENV.has(k) || (!IPC_INVALID
|
|
|
11
11
|
export class CliUtil {
|
|
12
12
|
/**
|
|
13
13
|
* Get a simplified version of a module name
|
|
14
|
-
* @returns
|
|
15
14
|
*/
|
|
16
15
|
static getSimpleModuleName(placeholder: string, module?: string): string {
|
|
17
16
|
const simple = (module ?? Runtime.main.name).replace(/[\/]/, '_').replace(/@/, '');
|
|
@@ -88,21 +87,4 @@ export class CliUtil {
|
|
|
88
87
|
static async writeAndEnsureComplete(data: unknown, channel: 'stdout' | 'stderr' = 'stdout'): Promise<void> {
|
|
89
88
|
return await new Promise(r => process[channel].write(typeof data === 'string' ? data : JSON.stringify(data, null, 2), () => r()));
|
|
90
89
|
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Listen for a run response to finish
|
|
94
|
-
*/
|
|
95
|
-
static async listenForResponse(result: RunResponse): Promise<void> {
|
|
96
|
-
// Listen to result if non-empty
|
|
97
|
-
if (result !== undefined && result !== null) {
|
|
98
|
-
if ('close' in result) {
|
|
99
|
-
ShutdownManager.onGracefulShutdown(async () => result.close()); // Tie shutdown into app close
|
|
100
|
-
}
|
|
101
|
-
if ('wait' in result) {
|
|
102
|
-
await result.wait(); // Wait for close signal
|
|
103
|
-
} else if ('on' in result) {
|
|
104
|
-
await new Promise<void>(res => result.on('close', res)); // Wait for callback
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
90
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Env } from '@travetto/runtime';
|
|
2
2
|
|
|
3
|
-
import { CliCommand } from '../src/decorators';
|
|
4
|
-
import { CliCommandSchema, CliCommandShape, CliValidationError } from '../src/types';
|
|
5
|
-
import { CliCommandRegistry } from '../src/registry';
|
|
6
|
-
import { CliCommandSchemaUtil } from '../src/schema';
|
|
7
|
-
import { CliUtil } from '../src/util';
|
|
3
|
+
import { CliCommand } from '../src/decorators.ts';
|
|
4
|
+
import { CliCommandSchema, CliCommandShape, CliValidationError } from '../src/types.ts';
|
|
5
|
+
import { CliCommandRegistry } from '../src/registry.ts';
|
|
6
|
+
import { CliCommandSchemaUtil } from '../src/schema.ts';
|
|
7
|
+
import { CliUtil } from '../src/util.ts';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Generates the schema for all CLI operations
|
package/support/cli.main.ts
CHANGED
|
@@ -20,18 +20,18 @@ export class MainCommand implements CliCommandShape {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
async main(fileOrImport: string, args: string[] = []): Promise<void> {
|
|
23
|
-
let
|
|
23
|
+
let result: unknown;
|
|
24
24
|
try {
|
|
25
25
|
const mod = await Runtime.importFrom<{ main(..._: unknown[]): Promise<unknown> }>(fileOrImport);
|
|
26
|
-
|
|
26
|
+
result = await mod.main(...args, ...this._parsed.unknown);
|
|
27
27
|
} catch (err) {
|
|
28
|
-
|
|
28
|
+
result = err;
|
|
29
29
|
process.exitCode = Math.max(process.exitCode ? +process.exitCode : 1, 1);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
if (
|
|
33
|
-
if (process.connected) { process.send?.(
|
|
34
|
-
const payload = typeof
|
|
32
|
+
if (result !== undefined) {
|
|
33
|
+
if (process.connected) { process.send?.(result); }
|
|
34
|
+
const payload = typeof result === 'string' ? result : (result instanceof Error ? result.stack : JSON.stringify(result));
|
|
35
35
|
process[process.exitCode ? 'stderr' : 'stdout'].write(`${payload}\n`);
|
|
36
36
|
}
|
|
37
37
|
}
|
package/support/cli.service.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { CliCommandShape, CliCommand, cliTpl, CliValidationError } from '@travet
|
|
|
2
2
|
import { Terminal } from '@travetto/terminal';
|
|
3
3
|
import { AsyncQueue, Runtime, RuntimeIndex, Util } from '@travetto/runtime';
|
|
4
4
|
|
|
5
|
-
import { ServiceRunner, ServiceDescriptor, ServiceAction } from '../src/service';
|
|
5
|
+
import { ServiceRunner, ServiceDescriptor, ServiceAction } from '../src/service.ts';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Allows for running services
|
|
@@ -21,7 +21,7 @@ export class CliServiceCommand implements CliCommandShape {
|
|
|
21
21
|
))
|
|
22
22
|
.filter(x => !!x)
|
|
23
23
|
.filter(x => services?.length ? services.includes(x.name) : true)
|
|
24
|
-
.
|
|
24
|
+
.toSorted((a, b) => a.name.localeCompare(b.name));
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
async validate(action: ServiceAction, services: string[]): Promise<CliValidationError | undefined> {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
2
|
|
|
3
3
|
import { TransformerState, DecoratorMeta, AfterClass } from '@travetto/transformer';
|
|
4
|
-
import { SchemaTransformUtil } from '@travetto/schema/support/transformer/util';
|
|
4
|
+
import { SchemaTransformUtil } from '@travetto/schema/support/transformer/util.ts';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Converts classes with `@CliCommand` to `@Schema` and maps the main method
|