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

3.1 KiB

TENANT_PROVIDER_CODES — stable error registry

(no submodules)

Purpose

@suaveplan/api-tenant-provider raises every failure through a single SuaveplanError subclass — TenantProviderError — and every instance carries a code from the frozen TENANT_PROVIDER_CODES registry. The registry exists so consumers can branch on a stable string literal (error.code === "TENANT_PROVIDER.INVALID_CURSOR") without coupling to the package's class hierarchy or sniffing message text.

Because tenant enumeration is consumed by very different concerns (per-tenant cron fan-out, admin dashboards, background reconciliation, cache warmers), a small set of well-named codes is more useful than a large catalogue: callers either retry on SOURCE_UNAVAILABLE, fix caller bugs on INVALID_CURSOR, or treat the absent record as expected on TENANT_NOT_FOUND.

Features

  • defineCodes("TENANT_PROVIDER", [...]) produces a frozen registry; new entries are an OpenSpec change.
  • TenantProviderError extends SuaveplanError<TenantProviderErrorCode> so instanceof TenantProviderError narrows the code field.
  • Three codes: TENANT_NOT_FOUND, SOURCE_UNAVAILABLE, INVALID_CURSOR.
  • Inherits cause-chain, redaction, OTel span-event adapter from @suaveplan/error.

Usage: Basic

import {
    TENANT_PROVIDER_CODES,
    TenantProviderError,
} from "@suaveplan/api-tenant-provider/error-codes";

throw new TenantProviderError(
    TENANT_PROVIDER_CODES.TENANT_NOT_FOUND,
    "tenant t-42 is not registered",
);

Usage: Advanced

import {
    TenantProviderError,
    TENANT_PROVIDER_CODES,
} from "@suaveplan/api-tenant-provider/error-codes";

try {
    for await (const t of provider.list({ cursor })) {
        // ...
    }
} catch (err) {
    if (
        err instanceof TenantProviderError &&
        err.code === TENANT_PROVIDER_CODES.INVALID_CURSOR
    ) {
        // The caller passed a cursor not produced by `list()` — reset
        // and start a fresh iteration instead of crashing the job.
        for await (const t of provider.list()) {
            // ...
        }
    } else {
        throw err;
    }
}

API Reference

  • TENANT_PROVIDER_CODES: Readonly<{ TENANT_NOT_FOUND: "TENANT_PROVIDER.TENANT_NOT_FOUND"; SOURCE_UNAVAILABLE: "TENANT_PROVIDER.SOURCE_UNAVAILABLE"; INVALID_CURSOR: "TENANT_PROVIDER.INVALID_CURSOR" }>.
  • TenantProviderErrorCode: the union of those three string literals.
  • class TenantProviderError extends SuaveplanError<TenantProviderErrorCode> with constructor(code, message, options?) and public readonly tenantProviderCode.

Implementation Notes

The codes are defined via defineCodes("TENANT_PROVIDER", [...] as const) so the TENANT_PROVIDER. prefix is forwarded by the registry helper — the array entries are bare names. The resulting object is frozen at construction and the type is widened to the union of every namespaced literal. The tenantProviderCode field on the error mirrors code for ergonomic narrowing in consumer code that already imports the class but not the code union.