100 lines
3.2 KiB
Markdown
100 lines
3.2 KiB
Markdown
# `@suaveplan/license` — error-codes
|
|
|
|
## Purpose
|
|
|
|
This module owns the stable error-code registry for `@suaveplan/license`. It
|
|
exposes `LICENSE_CODES` (a frozen `defineCodes("LICENSE", [...])` record),
|
|
the `LicenseErrorCode` type union, and the `LicenseError` class. Every
|
|
public failure path in the package raises a `LicenseError` carrying one of
|
|
these twelve namespaced `LICENSE.*` codes, so consumers can dispatch on the
|
|
literal string or `instanceof LicenseError` and let TypeScript narrow the
|
|
union exhaustively.
|
|
|
|
## Features
|
|
|
|
- 12 codes covering input validation, encoding / parsing, cryptographic
|
|
failures, lifecycle (expired / revoked), manager state, feature-mask
|
|
bounds, and `AbortSignal` cancellation.
|
|
- `LicenseError` extends `SuaveplanError<LicenseErrorCode>` so the
|
|
generic `code` type narrows under `instanceof`.
|
|
- Constructor widened to `public` so all modules can construct errors
|
|
directly without trampolines.
|
|
- Codes are namespaced `LICENSE.*` per the suaveplan-wide pattern (see
|
|
`@suaveplan/crypto-keys`'s `CHAIN_KEYS.*`), making programmatic dispatch
|
|
consistent across packages.
|
|
|
|
## Usage: Basic
|
|
|
|
```ts
|
|
import { LicenseError, LICENSE_CODES } from "@suaveplan/license/error-codes/error-codes";
|
|
|
|
throw new LicenseError(
|
|
LICENSE_CODES.INVALID_ENCODING,
|
|
"Invalid Base32 character: !"
|
|
);
|
|
```
|
|
|
|
## Usage: Advanced
|
|
|
|
Attach a Zod `cause` and structured `context` for an input-validation
|
|
boundary failure:
|
|
|
|
```ts
|
|
import { LicenseError, LICENSE_CODES } from "@suaveplan/license/error-codes/error-codes";
|
|
import { LicensePayloadSchema } from "@suaveplan/license/schemas/schemas";
|
|
|
|
try {
|
|
LicensePayloadSchema.parse(input);
|
|
} catch (zodError) {
|
|
throw new LicenseError(
|
|
LICENSE_CODES.INVALID_INPUT,
|
|
"License payload failed schema validation",
|
|
{ cause: zodError, context: { source: "generateLicense" } }
|
|
);
|
|
}
|
|
```
|
|
|
|
Consumer-side narrowing:
|
|
|
|
```ts
|
|
import { LicenseError } from "@suaveplan/license";
|
|
|
|
try {
|
|
await manager.generate({ productId: 1 });
|
|
} catch (error) {
|
|
if (error instanceof LicenseError && error.code === "LICENSE.NO_SIGNING_KEY") {
|
|
// recover or re-prompt
|
|
} else {
|
|
throw error;
|
|
}
|
|
}
|
|
```
|
|
|
|
## API Reference
|
|
|
|
### `LICENSE_CODES`
|
|
|
|
Frozen `Readonly<Record<Reason, "LICENSE.<Reason>">>` where `Reason` is
|
|
one of the 12 keys: `INVALID_INPUT`, `INVALID_ENCODING`, `MALFORMED`,
|
|
`UNSUPPORTED_VERSION`, `UNKNOWN_SIGNING_KEY`, `INVALID_SIGNATURE`,
|
|
`EXPIRED`, `REVOKED`, `NO_SIGNING_KEY`, `FEATURE_INDEX_OUT_OF_RANGE`,
|
|
`PAYLOAD_OUT_OF_RANGE`, `ABORTED`.
|
|
|
|
### `type LicenseErrorCode`
|
|
|
|
String-literal union of all 12 namespaced codes.
|
|
|
|
### `class LicenseError extends SuaveplanError<LicenseErrorCode>`
|
|
|
|
Concrete error class. Constructor: `(code, message, options?)` where
|
|
`options` is `SuaveplanErrorOptions` (carries optional `cause`,
|
|
`context`, `traceId`, `clock`).
|
|
|
|
## Implementation Notes
|
|
|
|
The registry uses `defineCodes` from `@suaveplan/error` so the type of
|
|
`LICENSE_CODES.INVALID_ENCODING` is the literal `"LICENSE.INVALID_ENCODING"`
|
|
(not the broad `string`), giving exhaustiveness checking in `switch`
|
|
statements. The list is declared `as const` so TypeScript captures the
|
|
tuple shape and the helper's mapped-type return picks every key.
|