Initial Commit
This commit is contained in:
113
packages/api/api-plugin-banking/src/error-codes/error-codes.md
Normal file
113
packages/api/api-plugin-banking/src/error-codes/error-codes.md
Normal 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.
|
||||
Reference in New Issue
Block a user