@travetto/cli 6.0.0-rc.1 → 6.0.0-rc.2
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 +32 -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.
|
|
@@ -94,7 +93,7 @@ Examples of mappings:
|
|
|
94
93
|
The pattern is that underscores(_) translate to colons (:), and the `cli.` prefix, and `.ts` suffix are dropped.
|
|
95
94
|
|
|
96
95
|
## 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#
|
|
96
|
+
[@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
97
|
|
|
99
98
|
**Code: Basic Command with Flag**
|
|
100
99
|
```typescript
|
|
@@ -133,9 +132,9 @@ HELLO
|
|
|
133
132
|
|
|
134
133
|
The [@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/decorators.ts#L84) supports the following data types for flags:
|
|
135
134
|
* 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#
|
|
135
|
+
* 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.
|
|
136
|
+
* 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
|
|
137
|
+
* 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
138
|
* String lists. Same as String, but allowing multiple values.
|
|
140
139
|
* Numeric lists. Same as Number, but allowing multiple values.
|
|
141
140
|
|
|
@@ -206,7 +205,7 @@ export class BasicCommand {
|
|
|
206
205
|
reverse?: boolean;
|
|
207
206
|
|
|
208
207
|
main(@Min(1) @Max(10) volumes: number[]) {
|
|
209
|
-
console.log(volumes.
|
|
208
|
+
console.log(volumes.toSorted((a, b) => (a - b) * (this.reverse ? -1 : 1)).join(' '));
|
|
210
209
|
}
|
|
211
210
|
}
|
|
212
211
|
```
|
|
@@ -251,7 +250,7 @@ $ trv basic:arglist -r 10 5 3 9 8 1
|
|
|
251
250
|
```
|
|
252
251
|
|
|
253
252
|
## 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#
|
|
253
|
+
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
254
|
|
|
256
255
|
**Code: Custom Command with Metadata**
|
|
257
256
|
```typescript
|
|
@@ -389,7 +388,7 @@ npx trv call:db --host localhost --port 3306 --username app --password <custom>
|
|
|
389
388
|
```
|
|
390
389
|
|
|
391
390
|
## 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. [
|
|
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. [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
392
|
|
|
394
393
|
**Code: Simple Run Target**
|
|
395
394
|
```typescript
|
|
@@ -424,7 +423,7 @@ export interface CliCommandShape<T extends unknown[] = unknown[]> {
|
|
|
424
423
|
/**
|
|
425
424
|
* Action target of the command
|
|
426
425
|
*/
|
|
427
|
-
main(...args: T): OrProm<
|
|
426
|
+
main(...args: T): OrProm<undefined | void>;
|
|
428
427
|
/**
|
|
429
428
|
* Run before main runs
|
|
430
429
|
*/
|
|
@@ -457,22 +456,23 @@ export interface CliCommandShape<T extends unknown[] = unknown[]> {
|
|
|
457
456
|
```
|
|
458
457
|
|
|
459
458
|
### 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 [
|
|
459
|
+
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
460
|
|
|
462
461
|
**Code: Simple Run Target**
|
|
463
462
|
```typescript
|
|
464
|
-
import { Runtime } from '@travetto/runtime';
|
|
463
|
+
import { Runtime, ShutdownManager, toConcrete } from '@travetto/runtime';
|
|
465
464
|
import { DependencyRegistry } from '@travetto/di';
|
|
466
465
|
import { CliCommand, CliCommandShape } from '@travetto/cli';
|
|
466
|
+
import { NetUtil } from '@travetto/web';
|
|
467
|
+
import { RootRegistry } from '@travetto/registry';
|
|
467
468
|
|
|
468
|
-
import {
|
|
469
|
-
import { RestNetUtil } from '../src/util/net';
|
|
469
|
+
import type { WebHttpServer } from '../src/types.ts';
|
|
470
470
|
|
|
471
471
|
/**
|
|
472
|
-
* Run a
|
|
472
|
+
* Run a web server
|
|
473
473
|
*/
|
|
474
474
|
@CliCommand({ runTarget: true, with: { debugIpc: true, canRestart: true, module: true, env: true } })
|
|
475
|
-
export class
|
|
475
|
+
export class WebHttpCommand implements CliCommandShape {
|
|
476
476
|
|
|
477
477
|
/** Port to run on */
|
|
478
478
|
port?: number;
|
|
@@ -482,35 +482,40 @@ export class RunRestCommand implements CliCommandShape {
|
|
|
482
482
|
|
|
483
483
|
preMain(): void {
|
|
484
484
|
if (this.port) {
|
|
485
|
-
process.env.
|
|
485
|
+
process.env.WEB_HTTP_PORT = `${this.port}`;
|
|
486
486
|
}
|
|
487
487
|
}
|
|
488
488
|
|
|
489
|
-
async main(): Promise<
|
|
490
|
-
|
|
489
|
+
async main(): Promise<void> {
|
|
490
|
+
await RootRegistry.init();
|
|
491
|
+
const instance = await DependencyRegistry.getInstance(toConcrete<WebHttpServer>());
|
|
492
|
+
|
|
493
|
+
let res;
|
|
491
494
|
try {
|
|
492
|
-
|
|
495
|
+
res = await instance.serve();
|
|
493
496
|
} catch (err) {
|
|
494
|
-
if (
|
|
495
|
-
await
|
|
496
|
-
|
|
497
|
+
if (NetUtil.isPortUsedError(err) && !Runtime.production && this.killConflict) {
|
|
498
|
+
await NetUtil.freePort(err.port);
|
|
499
|
+
res = await instance.serve();
|
|
497
500
|
}
|
|
498
501
|
throw err;
|
|
499
502
|
}
|
|
503
|
+
ShutdownManager.onGracefulShutdown(res.kill);
|
|
504
|
+
return res.wait;
|
|
500
505
|
}
|
|
501
506
|
}
|
|
502
507
|
```
|
|
503
508
|
|
|
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/
|
|
509
|
+
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
510
|
|
|
506
511
|
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
512
|
|
|
508
513
|
### 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#
|
|
514
|
+
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
515
|
|
|
511
516
|
**Code: CliValidationError**
|
|
512
517
|
```typescript
|
|
513
|
-
export
|
|
518
|
+
export interface CliValidationError {
|
|
514
519
|
/**
|
|
515
520
|
* The error message
|
|
516
521
|
*/
|
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.2",
|
|
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', 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
|