first commit
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
# `error-codes/error-codes.ts`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Owns the stable `SITE.*` error-code registry and the `SiteError` class used by every public API path in `@suaveplan/api-plugin-site`. Each code is a stable string that consumers can pattern-match without relying on message text — important for cross-version compatibility, log aggregation, alert routing, and HTTP-status mapping. Centralising the codes in one file makes the contract auditable in a single grep and prevents new code paths from inventing ad-hoc strings.
|
||||
|
||||
The registry is built with `defineCodes("SITE", [...])` from `@suaveplan/error`, which freezes the array into a readonly object whose values are namespaced `"SITE.<KEY>"`. `SiteError extends SuaveplanError<SiteErrorCode>` so `instanceof SiteError` narrows `error.code` to the literal-union `SiteErrorCode`.
|
||||
|
||||
## Features
|
||||
|
||||
- 14 canonical codes covering input validation, lifecycle wiring, site CRUD, domain CRUD, resolution failures, and bootstrap failures.
|
||||
- `SiteError` constructor accepts `SuaveplanErrorOptions` so callers can attach `cause`, `context`, and structured fields.
|
||||
- Stable string codes — `error.code === SITE_CODES.SITE_NOT_FOUND` survives minification and i18n.
|
||||
- Per-code doc comment naming the HTTP status the route layer maps to.
|
||||
- Zero runtime dependencies beyond `@suaveplan/error`.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { SITE_CODES, SiteError } from "@suaveplan/api-plugin-site/error-codes/error-codes";
|
||||
|
||||
if (site === null) {
|
||||
throw new SiteError(SITE_CODES.SITE_NOT_FOUND, `site '${id}' not found`);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { SITE_CODES, SiteError } from "@suaveplan/api-plugin-site/error-codes/error-codes";
|
||||
|
||||
try {
|
||||
await db.query("SELECT * FROM site WHERE id = $p0", [id]);
|
||||
} catch (cause) {
|
||||
throw new SiteError(SITE_CODES.STORAGE_ERROR, "SurrealDB query failed", {
|
||||
cause: cause instanceof Error ? cause : undefined,
|
||||
context: { statusCode: 502, id },
|
||||
});
|
||||
}
|
||||
|
||||
// Discrimination at a boundary:
|
||||
if (err instanceof SiteError && err.code === SITE_CODES.DOMAIN_ALREADY_CLAIMED) {
|
||||
return c.json({ error: err.message }, 409);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `SITE_CODES` — readonly object of 14 keys mapped to namespaced `"SITE.<KEY>"` literals.
|
||||
- `SiteErrorCode` — string-literal union of every value in `SITE_CODES`.
|
||||
- `class SiteError extends SuaveplanError<SiteErrorCode>` — constructor `(code, message, options?)`. Exposes `siteCode` as a typed alias of `code`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The registry is the contract: deleting a code to satisfy a coverage gate is forbidden by `AGENTS.md §2c`. Every code MUST be thrown by at least one production path, asserted by `error-codes.test.ts`.
|
||||
- HTTP statuses are documented per-code but the mapping is performed by the route layer (`routes/`) via `error.context.statusCode` — the registry itself is transport-agnostic.
|
||||
- New codes require a registry edit, an OpenSpec change, and a test that drives the new throw path. There are no silent additions.
|
||||
Reference in New Issue
Block a user