Initial Commit
This commit is contained in:
120
packages/devtools/tooling/cli/src/app/app.md
Normal file
120
packages/devtools/tooling/cli/src/app/app.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# app
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`App` is the top-level entry point for a `@suaveplan/cli` application. It wraps a root `Command` and adds application-level concerns: version printing, help generation, `onError` handling, OTel span instrumentation on `run()`, and `AbortSignal` support. The `cli()` factory function creates an `App` with a name and optional version string in a single expression.
|
||||
|
||||
`App` delegates all routing and parsing to the underlying `Command` hierarchy. Its primary responsibility is to intercept `--version`, `--help`, and abort signals before dispatching, and to wrap any thrown error with `CliError(CLI_CODES.APP.RUN_FAILED)` when an `onError` handler is registered.
|
||||
|
||||
## Features
|
||||
|
||||
- Fluent builder: `.description()`, `.option()`, `.argument()`, `.action()`, `.command()`, `.example()`
|
||||
- `run(argv?, options?)` dispatches to the root command; defaults `argv` to `process.argv.slice(2)`
|
||||
- `--version` / `-V` prints the version string (if set) and exits cleanly
|
||||
- `--help` / `-h` prints `helpText()` and exits cleanly
|
||||
- `options.signal` (AbortSignal) — throws `CliError(CLI_CODES.APP.ABORTED)` immediately when already aborted
|
||||
- `onError` handler — catches thrown errors, wraps non-`CliError` in `CliError(CLI_CODES.APP.RUN_FAILED)`, and calls the handler instead of re-throwing
|
||||
- OTel span on every `run()` call: span name `cli.app.run`, attribute `cli.app` set to the app name; `SpanStatusCode.ERROR` set on failure
|
||||
- `cli(name, version?)` factory for single-expression app creation
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { cli } from "@suaveplan/cli/app/app";
|
||||
|
||||
const app = cli("deploy", "1.0.0")
|
||||
.description("Deployment toolkit");
|
||||
|
||||
app.command("up")
|
||||
.description("Bring services up")
|
||||
.action(async () => {
|
||||
console.log("Services are up");
|
||||
});
|
||||
|
||||
await app.run();
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { App } from "@suaveplan/cli/app/app";
|
||||
|
||||
const app = new App({
|
||||
name: "myapp",
|
||||
version: "2.1.0",
|
||||
description: "My enterprise CLI",
|
||||
writer: (text) => process.stdout.write(text + "\n"),
|
||||
onError: (err) => {
|
||||
console.error(`Error [${err.code}]: ${err.message}`);
|
||||
process.exit(1);
|
||||
},
|
||||
});
|
||||
|
||||
app
|
||||
.option("--verbose", "Enable verbose output")
|
||||
.action(async (ctx) => {
|
||||
if (ctx.options.verbose) console.log("Verbose mode on");
|
||||
});
|
||||
|
||||
const controller = new AbortController();
|
||||
process.on("SIGINT", () => controller.abort());
|
||||
|
||||
await app.run(process.argv.slice(2), { signal: controller.signal });
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `new App(config: AppConfig)`
|
||||
|
||||
Creates a new app. Config fields: `name` (required), `version`, `description`, `enableHelp` (default true), `enableVersion` (default true), `onError`, `writer`.
|
||||
|
||||
### `app.name: string`
|
||||
|
||||
The app's name as provided in config.
|
||||
|
||||
### `app.version: string | undefined`
|
||||
|
||||
The app's version string, or `undefined` if not set.
|
||||
|
||||
### `app.description(text: string): this`
|
||||
|
||||
Sets the app description shown in help text.
|
||||
|
||||
### `app.option(flags, description?, defaultValue?): this`
|
||||
|
||||
Registers a root-level option.
|
||||
|
||||
### `app.argument(spec, description?): this`
|
||||
|
||||
Registers a root-level positional argument.
|
||||
|
||||
### `app.action(fn: ActionFn): this`
|
||||
|
||||
Sets the root action handler.
|
||||
|
||||
### `app.command(name: string): Command`
|
||||
|
||||
Registers and returns a named sub-command.
|
||||
|
||||
### `app.example(text: string): this`
|
||||
|
||||
Adds an example to the help output.
|
||||
|
||||
### `app.helpText(): string`
|
||||
|
||||
Returns formatted multi-line help text for the root command.
|
||||
|
||||
### `app.run(argv?: string[], options?: { signal?: AbortSignal }): Promise<void>`
|
||||
|
||||
Executes the app. If `signal` is already aborted, throws `CliError(ABORTED)` immediately. Intercepts `--version` and `--help` before routing. Delegates to the root `Command.execute()`.
|
||||
|
||||
### `cli(name: string, version?: string): App`
|
||||
|
||||
Factory function. Creates an `App` with `name` and optional `version`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The `writer` config field injects the output sink, defaulting to `process.stdout.write`. This enables full testability without TTY or process coupling. The `onError` hook receives a `CliError` — if the thrown value was a plain `Error`, it is wrapped in `CliError(RUN_FAILED)` before being handed to the hook, so callers always receive a structured error with a stable code.
|
||||
Reference in New Issue
Block a user