first commit
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
# error-codes
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Declares the stable, namespaced error registry for `@suaveplan/cryptocurrency` and the typed `CryptocurrencyError` class that wraps every failure path. The registry uses `defineCodes("CRYPTOCURRENCY", [...])` to produce a frozen, literal-typed map (`{ readonly UNKNOWN_CHAIN: "CRYPTOCURRENCY.UNKNOWN_CHAIN", ... }`) so consumers can dispatch programmatically on `err.code === CRYPTOCURRENCY_CODES.UNKNOWN_CHAIN` with full TypeScript narrowing.
|
||||
|
||||
Centralising the registry guarantees the error contract is single-sourced — every throw site in the package picks from the same five-code set, and the exposed `CryptocurrencyErrorCode` union travels with consumers' code so a future code addition triggers an exhaustiveness check at every switch site.
|
||||
|
||||
## Features
|
||||
|
||||
- Five codes covering every distinct failure mode in the migrated surface: `UNKNOWN_CHAIN`, `UNKNOWN_CURRENCY`, `UNKNOWN_UNIT`, `INVALID_ADDRESS`, `INVALID_HASH`.
|
||||
- `CryptocurrencyError extends SuaveplanError<CryptocurrencyErrorCode>` with optional `chainId` / `currency` / `unit` discriminants for diagnostics.
|
||||
- `CryptocurrencyErrorCode` literal union exported for consumer narrowing.
|
||||
- `CryptocurrencyErrorOptions` extends `SuaveplanErrorOptions` so `cause`, `context`, `traceId`, and `clock` are all available.
|
||||
- The registry is `Object.freeze`d so consumers cannot mutate the code map.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import {
|
||||
CRYPTOCURRENCY_CODES,
|
||||
CryptocurrencyError,
|
||||
} from "@suaveplan/cryptocurrency";
|
||||
|
||||
throw new CryptocurrencyError(
|
||||
CRYPTOCURRENCY_CODES.UNKNOWN_CHAIN,
|
||||
"Unknown chain: ethereum",
|
||||
{ chainId: "ethereum" }
|
||||
);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import {
|
||||
CRYPTOCURRENCY_CODES,
|
||||
CryptocurrencyError,
|
||||
type CryptocurrencyErrorCode,
|
||||
} from "@suaveplan/cryptocurrency";
|
||||
|
||||
function describeFailure(err: unknown): string {
|
||||
if (!(err instanceof CryptocurrencyError)) return "non-crypto error";
|
||||
const code: CryptocurrencyErrorCode = err.code;
|
||||
switch (code) {
|
||||
case CRYPTOCURRENCY_CODES.UNKNOWN_CHAIN:
|
||||
return `unknown chain: ${err.chainId ?? "<unset>"}`;
|
||||
case CRYPTOCURRENCY_CODES.UNKNOWN_CURRENCY:
|
||||
return `unknown currency: ${err.currency ?? "<unset>"}`;
|
||||
case CRYPTOCURRENCY_CODES.UNKNOWN_UNIT:
|
||||
return `unknown unit '${err.unit}' for ${err.currency}`;
|
||||
case CRYPTOCURRENCY_CODES.INVALID_ADDRESS:
|
||||
return `invalid address${err.chainId ? ` for ${err.chainId}` : ""}`;
|
||||
case CRYPTOCURRENCY_CODES.INVALID_HASH:
|
||||
return `invalid hash${err.chainId ? ` for ${err.chainId}` : ""}`;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// some package call
|
||||
} catch (err) {
|
||||
console.error(describeFailure(err));
|
||||
}
|
||||
```
|
||||
|
||||
The exhaustive switch above will fail to compile if a new code is added without a matching arm — that is the point of exporting `CryptocurrencyErrorCode`.
|
||||
|
||||
## API Reference
|
||||
|
||||
| Export | Kind | Notes |
|
||||
|---|---|---|
|
||||
| `CRYPTOCURRENCY_CODES` | const | frozen `{ readonly <REASON>: "CRYPTOCURRENCY.<REASON>" }` map |
|
||||
| `CryptocurrencyErrorCode` | type | union of every registered code's string literal |
|
||||
| `CryptocurrencyError` | class | `extends SuaveplanError<CryptocurrencyErrorCode>`; constructor `(code, message, options?)` |
|
||||
| `CryptocurrencyErrorOptions` | interface | `extends SuaveplanErrorOptions`; adds optional `chainId`, `currency`, `unit` |
|
||||
|
||||
### `CryptocurrencyError` discriminants
|
||||
|
||||
- `chainId?: ChainId` — set on `UNKNOWN_CHAIN`, `INVALID_ADDRESS`, `INVALID_HASH` raises.
|
||||
- `currency?: string` — set on `UNKNOWN_CURRENCY`, `UNKNOWN_UNIT` raises.
|
||||
- `unit?: string` — set on `UNKNOWN_UNIT` raises.
|
||||
|
||||
All discriminants are read-only and `undefined` when not supplied — consumers should narrow with `if (err.chainId)` rather than asserting non-null.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- `INVALID_ADDRESS` and `INVALID_HASH` are exposed in the registry even though the package's own validators return structured `AddressValidation` / `TxHashValidation` records rather than throwing. The codes are there for application-tier consumers that want to wrap a failed validation in a typed throw. They are exercised by direct construction in `error-codes.test.ts`, satisfying the conformance rule (§13 step 4 in `AGENTS.md`) that every registered code must be reachable from a call site somewhere in `src/`.
|
||||
- `defineCodes` produces a `Object.freeze`d record — mutating `CRYPTOCURRENCY_CODES` throws in strict mode. Consumers must rely on the literal types, not runtime extension.
|
||||
- `CryptocurrencyError` extends `SuaveplanError` (rule §63 of `CLAUDE.md`) so every error in the package participates in cause-chain traversal, OTel span events, redaction, and i18n message resolution.
|
||||
- The error's `name` field is automatically set to `"CryptocurrencyError"` by `SuaveplanError`'s use of `new.target.name`.
|
||||
- The optional discriminants are assigned via `if (options.X !== undefined)` so the field stays `undefined` (and serialises away from JSON) when not provided.
|
||||
Reference in New Issue
Block a user