Files
skills/docs/security-stack/security/license/src/error-codes.md
2026-05-29 08:20:56 +01:00

3.2 KiB

@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

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:

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:

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.