Initial Commit
This commit is contained in:
171
packages/devtools/tooling/cli/src/errors/errors.md
Normal file
171
packages/devtools/tooling/cli/src/errors/errors.md
Normal file
@@ -0,0 +1,171 @@
|
||||
# errors
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
The `errors` module defines the complete error taxonomy for `@suaveplan/cli`. It
|
||||
provides a stable, machine-readable code registry (`CLI_CODES`) and a strongly-typed
|
||||
error class (`CliError`) so that every failure surface in the CLI framework is
|
||||
discriminable at runtime without string-matching on messages. Callers can switch
|
||||
exhaustively on `err.code`, log structured records with stable identifiers, and
|
||||
forward typed errors across process boundaries without losing fidelity.
|
||||
|
||||
## Features
|
||||
|
||||
- **Three sub-namespaces** — `CLI.PARSE.*` covers argument/option parsing failures;
|
||||
`CLI.COMMAND.*` covers command-dispatch failures; `CLI.APP.*` covers top-level
|
||||
application lifecycle failures.
|
||||
- **Nine stable error codes** — each code is a dot-delimited string constant produced
|
||||
by `defineCodes()` from `@suaveplan/error`, guaranteeing uniqueness and structural
|
||||
consistency across the codebase.
|
||||
- **`CliCode` union type** — narrows the full set of valid codes for use in generic
|
||||
`SuaveplanError<CliCode>` type parameters and exhaustive switch statements.
|
||||
- **`CliError` class** — extends `SuaveplanError` with a public constructor, cause
|
||||
chaining, and structured `context` attachment for diagnostic metadata.
|
||||
- **Zero runtime overhead** — the `CLI_CODES` object is `as const`, so all string
|
||||
values are inlined at the call site by the TypeScript compiler.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { CliError, CLI_CODES } from "@suaveplan/cli/errors/errors";
|
||||
|
||||
function assertKnownOption(flag: string, known: Set<string>): void {
|
||||
if (!known.has(flag)) {
|
||||
throw new CliError(
|
||||
CLI_CODES.PARSE.UNKNOWN_OPTION,
|
||||
"cli.parse.unknown_option"
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { CliError, CLI_CODES } from "@suaveplan/cli/errors/errors";
|
||||
import { SuaveplanError } from "@suaveplan/error";
|
||||
|
||||
async function runApp(signal: AbortSignal): Promise<void> {
|
||||
try {
|
||||
await doWork(signal);
|
||||
} catch (err) {
|
||||
if (signal.aborted) {
|
||||
throw new CliError(CLI_CODES.APP.ABORTED, "cli.app.aborted", {
|
||||
cause: err instanceof Error ? err : undefined,
|
||||
context: { reason: signal.reason },
|
||||
});
|
||||
}
|
||||
throw new CliError(CLI_CODES.APP.RUN_FAILED, "cli.app.run_failed", {
|
||||
cause: err instanceof Error ? err : undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function handleError(err: unknown): void {
|
||||
if (err instanceof CliError) {
|
||||
// Narrow on the stable code — no message-string matching needed.
|
||||
switch (err.code) {
|
||||
case CLI_CODES.APP.ABORTED:
|
||||
process.exitCode = 130; // standard SIGINT exit code
|
||||
break;
|
||||
case CLI_CODES.PARSE.UNKNOWN_OPTION:
|
||||
console.error(`Unknown option: ${String(err.context?.["option"])}`);
|
||||
process.exitCode = 1;
|
||||
break;
|
||||
default:
|
||||
console.error(err.message);
|
||||
process.exitCode = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `CLI_CODES`
|
||||
|
||||
```ts
|
||||
const CLI_CODES: {
|
||||
PARSE: {
|
||||
UNKNOWN_OPTION: "CLI.PARSE.UNKNOWN_OPTION";
|
||||
MISSING_VALUE: "CLI.PARSE.MISSING_VALUE";
|
||||
INVALID_VALUE: "CLI.PARSE.INVALID_VALUE";
|
||||
MISSING_ARGUMENT: "CLI.PARSE.MISSING_ARGUMENT";
|
||||
MISSING_REQUIRED_OPTION: "CLI.PARSE.MISSING_REQUIRED_OPTION";
|
||||
};
|
||||
COMMAND: {
|
||||
NO_ACTION: "CLI.COMMAND.NO_ACTION";
|
||||
UNKNOWN_SUBCOMMAND: "CLI.COMMAND.UNKNOWN_SUBCOMMAND";
|
||||
};
|
||||
APP: {
|
||||
RUN_FAILED: "CLI.APP.RUN_FAILED";
|
||||
ABORTED: "CLI.APP.ABORTED";
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Immutable registry of all error codes used by `@suaveplan/cli`. Each value is a
|
||||
dot-delimited string in the form `<NAMESPACE>.<SUBCATEGORY>.<NAME>`.
|
||||
|
||||
### `CliCode`
|
||||
|
||||
```ts
|
||||
type CliCode =
|
||||
| "CLI.PARSE.UNKNOWN_OPTION"
|
||||
| "CLI.PARSE.MISSING_VALUE"
|
||||
| "CLI.PARSE.INVALID_VALUE"
|
||||
| "CLI.PARSE.MISSING_ARGUMENT"
|
||||
| "CLI.PARSE.MISSING_REQUIRED_OPTION"
|
||||
| "CLI.COMMAND.NO_ACTION"
|
||||
| "CLI.COMMAND.UNKNOWN_SUBCOMMAND"
|
||||
| "CLI.APP.RUN_FAILED"
|
||||
| "CLI.APP.ABORTED";
|
||||
```
|
||||
|
||||
Union of all values in `CLI_CODES`. Use as the type parameter for
|
||||
`SuaveplanError<CliCode>` or in exhaustive switch statements.
|
||||
|
||||
### `CliError`
|
||||
|
||||
```ts
|
||||
class CliError extends SuaveplanError<CliCode> {
|
||||
public constructor(
|
||||
code: CliCode,
|
||||
messageKey: string,
|
||||
options?: SuaveplanErrorOptions
|
||||
): CliError;
|
||||
}
|
||||
```
|
||||
|
||||
Typed error for all `@suaveplan/cli` failures.
|
||||
|
||||
- **`code`** — one of the `CliCode` values; accessible as `err.code` via the base
|
||||
class.
|
||||
- **`messageKey`** — i18n message key passed to the base class for message resolver
|
||||
integration.
|
||||
- **`options.cause`** — optional upstream `Error` for cause chaining; accessible as
|
||||
`err.cause`.
|
||||
- **`options.context`** — optional `Record<string, unknown>` with structured
|
||||
diagnostic metadata; accessible as `err.context`.
|
||||
|
||||
Throws: nothing — this is an Error constructor.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
`CLI_CODES` is built from three separate `defineCodes()` calls rather than one flat
|
||||
call so that each sub-namespace is isolated. This mirrors the pattern established in
|
||||
`@suaveplan/readline` and keeps future code-set additions within a namespace
|
||||
non-breaking for callers who already destructure `CLI_CODES.PARSE`.
|
||||
|
||||
The `CliError` constructor comment (`biome-ignore`) documents why the constructor is
|
||||
not "useless" from Biome's perspective: the base `SuaveplanError` constructor is
|
||||
`protected`, so widening it to `public` here is a meaningful override even though
|
||||
the body only calls `super()`. This is the standard pattern across all
|
||||
`@suaveplan/*` packages.
|
||||
|
||||
No runtime dependencies beyond `@suaveplan/error` are introduced. The `defineCodes`
|
||||
helper produces plain string literals, so the entire module tree-shakes to near
|
||||
zero bytes when errors are not thrown on a given code path.
|
||||
Reference in New Issue
Block a user