# `@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` 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">>` 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` 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.