Initial Commit
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
# `error-codes` — `SECRETS_CODES` and `SecretsError`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`SECRETS_CODES` is the frozen registry of fully-qualified error codes the API plugin system throws when a plugin tries to read a secret the source cannot supply, or when the host detects a missing required secret during install. Pinning the registry in one place keeps the wire format stable for clients, lets the `@suaveplan/error` i18n resolver translate codes to human messages, and gives every concrete source (env, Bun.secrets, vault) a single canonical set of identifiers to throw.
|
||||
|
||||
`SecretsError` is the concrete `SuaveplanError` subclass for the secrets domain. It widens the abstract base's `protected` constructor to public so callers can throw without defining a per-call subclass, while still benefiting from `instanceof SecretsError` narrowing the code union to `SecretsCode`.
|
||||
|
||||
## Features
|
||||
|
||||
- Four canonical codes: `SECRET_NOT_FOUND`, `SECRET_REQUIRED_MISSING_AT_START`, `SECRET_INVALID_FORMAT`, `SOURCE_UNAVAILABLE`.
|
||||
- All codes fully-qualified at the `SECRETS.` namespace.
|
||||
- `Object.freeze`d registry — mutation attempts throw in strict mode.
|
||||
- `SecretsError` class with type-level code narrowing.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { SECRETS_CODES, SecretsError } from "@suaveplan/api-secrets/error-codes";
|
||||
|
||||
throw new SecretsError(
|
||||
SECRETS_CODES.SECRET_NOT_FOUND,
|
||||
"error.secrets.not_found",
|
||||
{ context: { name: "STRIPE_KEY" } }
|
||||
);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
Pattern-matching on the code union in a host-side error mapper:
|
||||
|
||||
```ts
|
||||
import { SECRETS_CODES, type SecretsCode, SecretsError } from "@suaveplan/api-secrets/error-codes";
|
||||
|
||||
function toHttpStatus(err: SecretsError): number {
|
||||
const code: SecretsCode = err.code;
|
||||
switch (code) {
|
||||
case SECRETS_CODES.SECRET_NOT_FOUND:
|
||||
return 404;
|
||||
case SECRETS_CODES.SECRET_REQUIRED_MISSING_AT_START:
|
||||
return 503; // install incomplete
|
||||
case SECRETS_CODES.SECRET_INVALID_FORMAT:
|
||||
return 422;
|
||||
case SECRETS_CODES.SOURCE_UNAVAILABLE:
|
||||
return 503;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `const SECRETS_CODES` — `Readonly<{ [K in CodeKey]: \`SECRETS.${K}\` }>`; frozen.
|
||||
- `type SecretsCode` — the literal-string union derived from `SECRETS_CODES`.
|
||||
- `class SecretsError extends SuaveplanError<SecretsCode>` — public constructor `(code, messageKey, options?)`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The registry is built via `defineCodes("SECRETS", [...] as const)` from `@suaveplan/error`. `defineCodes` returns a frozen record so the registry is immutable at runtime; the `as const` on the keys array gives TypeScript the literal-string view it needs to derive `SecretsCode`. The `SecretsError` constructor delegates straight to the `SuaveplanError` base, including the trace-id, context, and clock injection points — every error thrown through `SecretsError` participates in `walkCauses`, `toSpanEvent`, and the redacted wire format. Note that `SuaveplanError` is abstract specifically because every domain should have its own `instanceof`-distinct subclass; do not import `SuaveplanError` directly to throw from this package.
|
||||
Reference in New Issue
Block a user