first commit
This commit is contained in:
99
docs/security-stack/security/license/src/error-codes.md
Normal file
99
docs/security-stack/security/license/src/error-codes.md
Normal file
@@ -0,0 +1,99 @@
|
||||
# `@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.
|
||||
Reference in New Issue
Block a user