4.1 KiB
error-codes
(no submodules)
Purpose
@suaveplan/linter/error-codes owns the stable error-code registry and the typed
LinterError class that every other module in the package throws. The registry is
expressed through defineCodes("LINTER", [...]) from @suaveplan/error so each code
carries the LINTER. namespace prefix and survives across module boundaries, worker
threads, RPC envelopes, and serialised error transports without losing its identity.
Consumers of the linter — pre-commit hooks, CI gates, IDE integrations — discriminate
on error.code to make routing decisions (retry, suppress, surface to the user) without
parsing free-form messages, so the registry is a public contract, not an implementation
detail.
Features
- Closed code set declared once via
defineCodes— typo-safe, no string literals at call sites. LinterError extends SuaveplanError<LinterCode>— interoperable with every observability adapter shipped in@suaveplan/error(OTel span events, redaction, i18n hooks).- Optional
detailspayload preserved on the instance and on the underlyingSuaveplanError.contextfield so structured diagnostics travel with the error. - Optional
causeforwarded to the nativeError.causechain — preserves the underlying failure (e.g. anEACCESfromfs.writeFile) for debugging.
Usage: Basic
import { LINTER_CODES, LinterError } from "@suaveplan/linter/error-codes";
throw new LinterError(LINTER_CODES.NO_TARGETS_FOUND, "no .md files matched", {
details: { pattern: "docs/**/*.md" },
});
Usage: Advanced
Route errors by code in a CI gate:
import { LinterError } from "@suaveplan/linter/error-codes";
import { runUnifiedLint } from "@suaveplan/linter/unified-lint";
try {
const report = await runUnifiedLint({ fix: false });
if (!report.success) {
process.exitCode = 1;
}
} catch (err) {
if (err instanceof LinterError) {
switch (err.code) {
case "LINTER.BIOME_UNAVAILABLE":
console.warn("biome not installed — skipping JS/TS lane");
break;
case "LINTER.NO_TARGETS_FOUND":
process.exitCode = 0; // empty repo, not a failure
break;
default:
console.error(err.code, err.message, err.details);
process.exitCode = 2;
}
} else {
throw err;
}
}
API Reference
LINTER_CODES
A frozen object whose values are the registered LINTER.<REASON> strings. Members:
INVALID_OPTIONS, FILE_NOT_FOUND, NO_TARGETS_FOUND, READ_FAILED,
WRITE_FAILED, MARKDOWN_PARSE_FAILED, BIOME_UNAVAILABLE, BIOME_FAILED,
RUNNER_FAILED, UNKNOWN_FLAG.
type LinterCode
Union of every value in LINTER_CODES.
class LinterError extends SuaveplanError<LinterCode>
Constructor: new LinterError(code, message, options?).
code: LinterCode— one of the registered codes.message: string— human-readable summary.options.cause?: unknown— forwarded toError.cause.options.details?: Record<string, unknown>— stored as bothinstance.detailsANDinstance.context(viaSuaveplanError).
instance.name is set to "LinterError".
Implementation Notes
- The registry is enumerated by tests (
Object.values(LINTER_CODES).sort()) to guarantee the public set matches the spec delta exactly. Adding a code without updating the test fails the gate. detailsdefaults to{}rather thanundefinedso consumers can safely spread or read fields without a null-check; the spec scenario "defaults details to empty record when omitted" pins this contract.SuaveplanErroracceptscauseonly when defined — passingundefinedwould setcause: undefinedon the chain, which differs from "no cause." The constructor guards this with theoptions?.cause === undefined ? {} : { cause: options.cause }ternary.- The class extends
SuaveplanError<LinterCode>rather thanSuaveplanErrorso thecodegetter is narrowed to the linter's registered union at the type level — IDE autocompletion lists exactly the ten codes.