Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View 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.