5.8 KiB
errors
(no submodules)
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. CliCodeunion type — narrows the full set of valid codes for use in genericSuaveplanError<CliCode>type parameters and exhaustive switch statements.CliErrorclass — extendsSuaveplanErrorwith a public constructor, cause chaining, and structuredcontextattachment for diagnostic metadata.- Zero runtime overhead — the
CLI_CODESobject isas const, so all string values are inlined at the call site by the TypeScript compiler.
Usage: Basic Example
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
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
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
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
class CliError extends SuaveplanError<CliCode> {
public constructor(
code: CliCode,
messageKey: string,
options?: SuaveplanErrorOptions
): CliError;
}
Typed error for all @suaveplan/cli failures.
code— one of theCliCodevalues; accessible aserr.codevia the base class.messageKey— i18n message key passed to the base class for message resolver integration.options.cause— optional upstreamErrorfor cause chaining; accessible aserr.cause.options.context— optionalRecord<string, unknown>with structured diagnostic metadata; accessible aserr.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.