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,113 @@
# Error codes
## Purpose
This module defines the stable, namespaced error-code registry for
`@suaveplan/api-plugin-banking`. Every failure path in the plugin throws
a `BankingError` carrying one of fourteen `BANKING.*` codes, so callers
can branch on `error.code` without parsing free-form messages and the
HTTP layer can map a code → status with a one-line lookup.
## Features
- 14 typed string-literal codes registered via `defineCodes("BANKING", ...)`
- `BankingError` class extending `SuaveplanError`, generic over the
code literal type so `instanceof` narrows the `code` field
- Stable JSDoc with HTTP-status mapping for each code
- Re-exported under the package's `./error-codes/error-codes` subpath
## Usage: Basic
```ts
import { BankingError, BANKING_CODES } from "@suaveplan/api-plugin-banking";
throw new BankingError(
BANKING_CODES.ACCOUNT_NOT_FOUND,
`Account ${id} not found`
);
```
## Usage: Advanced
Codes carry no behaviour — they are documentation. The expected pattern
is to catch at the route boundary, narrow on `instanceof BankingError`,
and map `error.code` to an HTTP status:
```ts
import {
BankingError,
BANKING_CODES,
type BankingErrorCode,
} from "@suaveplan/api-plugin-banking";
const STATUS: Record<BankingErrorCode, number> = {
INVALID_OPTIONS: 500,
SERVICE_NOT_AVAILABLE: 500,
BANK_NOT_FOUND: 404,
BANK_NAME_TAKEN: 400,
BANK_HAS_ACTIVE_ACCOUNTS: 400,
ACCOUNT_NOT_FOUND: 404,
ACCOUNT_LIMIT_REACHED: 400,
INSUFFICIENT_FUNDS: 400,
INVALID_AMOUNT: 400,
INVALID_FIELD_NAME: 500,
INVALID_ACCOUNT_NUMBER: 400,
INVALID_SORT_CODE: 400,
TENANT_CLEANUP_FAILED: 500,
DATABASE_ERROR: 500,
};
try {
await service.transferFunds(from, to, amount);
} catch (err) {
if (err instanceof BankingError) {
const status = STATUS[err.code];
return { status, body: { error: err.message, code: err.code } };
}
throw err;
}
```
## API Reference
### `BANKING_CODES`
`const BANKING_CODES: { readonly INVALID_OPTIONS: "BANKING.INVALID_OPTIONS" } & {...}`
Frozen, namespaced map of every code in the registry. Indexing it
yields the canonical `"BANKING.XYZ"` string passed to `BankingError`.
### `BankingErrorCode`
`type BankingErrorCode = (typeof BANKING_CODES)[keyof typeof BANKING_CODES]`
Discriminated union of all literal code values.
### `class BankingError extends SuaveplanError<BankingErrorCode>`
`constructor(code: BankingErrorCode, message: string, options?: SuaveplanErrorOptions)`
`error.code: BankingErrorCode` — the literal code string.
`error.bankingCode: BankingErrorCode` — alias for ergonomic exhaustive
switches without re-typing the union annotation.
`error.cause`, `error.context`, `error.toJSON()` — inherited from
`SuaveplanError`.
## Implementation Notes
The registry is intentionally exhaustive: every error code that
appears here is thrown by at least one production code path inside the
package. The pre-archive checklist (AGENTS.md §13) gates this — codes
registered but never thrown are removed before the change ships, so the
list reflects the real fault surface a caller can branch on.
`BankingError` keeps the `bankingCode` alias so consumers writing
exhaustive switches do not need to re-type the union annotation:
`switch (err.bankingCode) { case "BANKING.ACCOUNT_NOT_FOUND": ... }`
type-checks against `BankingErrorCode` directly.
When throwing from a SurrealDB call site, always set
`{ cause: originalError, context: { ... } }` — the wire-format JSON
adapter (`SuaveplanError.toJSON`) preserves both. `DATABASE_ERROR` is
the catch-all wrapper used by repositories when SurrealDB rejects a
query for a reason outside the typed code list.