4.9 KiB
error-codes
(no submodules)
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 optionalchainId/currency/unitdiscriminants for diagnostics.CryptocurrencyErrorCodeliteral union exported for consumer narrowing.CryptocurrencyErrorOptionsextendsSuaveplanErrorOptionssocause,context,traceId, andclockare all available.- The registry is
Object.freezed so consumers cannot mutate the code map.
Usage: Basic
import {
CRYPTOCURRENCY_CODES,
CryptocurrencyError,
} from "@suaveplan/cryptocurrency";
throw new CryptocurrencyError(
CRYPTOCURRENCY_CODES.UNKNOWN_CHAIN,
"Unknown chain: ethereum",
{ chainId: "ethereum" }
);
Usage: Advanced
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 onUNKNOWN_CHAIN,INVALID_ADDRESS,INVALID_HASHraises.currency?: string— set onUNKNOWN_CURRENCY,UNKNOWN_UNITraises.unit?: string— set onUNKNOWN_UNITraises.
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_ADDRESSandINVALID_HASHare exposed in the registry even though the package's own validators return structuredAddressValidation/TxHashValidationrecords 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 inerror-codes.test.ts, satisfying the conformance rule (§13 step 4 inAGENTS.md) that every registered code must be reachable from a call site somewhere insrc/.defineCodesproduces aObject.freezed record — mutatingCRYPTOCURRENCY_CODESthrows in strict mode. Consumers must rely on the literal types, not runtime extension.CryptocurrencyErrorextendsSuaveplanError(rule §63 ofCLAUDE.md) so every error in the package participates in cause-chain traversal, OTel span events, redaction, and i18n message resolution.- The error's
namefield is automatically set to"CryptocurrencyError"bySuaveplanError's use ofnew.target.name. - The optional discriminants are assigned via
if (options.X !== undefined)so the field staysundefined(and serialises away from JSON) when not provided.