Files
skills/docs/persistence/database/seed/src/error-codes/error-codes.md
2026-05-29 08:20:56 +01:00

5.2 KiB

error-codes

(no submodules)

Purpose

The @suaveplan/seed package centralises every failure mode it can produce behind a single registry built with defineCodes("SEED", [...]) from @suaveplan/error. The registry is the stable contract callers depend on: code strings are documented, frozen at module load, and never renamed without an explicit migration. This module exports the SEED_CODES registry, the derived SeedErrorCode type, and the SeedError class that every public failure path raises.

SeedError extends SuaveplanError<SeedErrorCode> so instanceof SeedError narrows code to the exact union of SEED.* strings. It adds optional seedName and dependency discriminants that diagnose which seed (or which missing dependency) triggered the error, while leaving the base context map free for caller-supplied structured data. The class threads cause straight through SuaveplanError, so store-layer exceptions wrapped by the runner remain inspectable through error.cause.

Features

  • Frozen SEED_CODES registry with seven namespaced codes
  • SeedErrorCode literal-union type derived from the registry
  • SeedError class extending SuaveplanError<SeedErrorCode>
  • Optional seedName and dependency diagnostic discriminants
  • Full cause-chain preservation via SuaveplanError
  • Compatible with the OTel span-event adapter shipped by @suaveplan/error

Usage: Basic

import { SEED_CODES, SeedError } from "@suaveplan/seed";

if (registry.has(seed.name)) {
 throw new SeedError(SEED_CODES.DUPLICATE_NAME, `seed "${seed.name}" already registered`, {
  seedName: seed.name,
 });
}

Usage: Advanced

import { SEED_CODES, SeedError } from "@suaveplan/seed";
import { SuaveplanError } from "@suaveplan/error";

try {
 await store.insert(seed.table, batch);
} catch (cause) {
 throw new SeedError(SEED_CODES.STORE_ERROR, `store rejected batch for ${seed.table}`, {
  cause: cause instanceof Error ? cause : undefined,
  seedName: seed.name,
  context: { table: seed.table, batchSize: batch.length },
 });
}

// Downstream:
catch (err) {
 if (err instanceof SeedError && err.code === SEED_CODES.STORE_ERROR) {
  logger.error("seed.store.failure", {
   seedName: err.seedName,
   rootMessage: err.cause instanceof Error ? err.cause.message : String(err.cause),
  });
 }
}

API Reference

SEED_CODES

A frozen object literal of seven namespaced codes:

Property Value
DUPLICATE_NAME "SEED.DUPLICATE_NAME"
UNREGISTERED_DEPENDENCY "SEED.UNREGISTERED_DEPENDENCY"
CIRCULAR_DEPENDENCY "SEED.CIRCULAR_DEPENDENCY"
DEPENDENCY_DEPTH_EXCEEDED "SEED.DEPENDENCY_DEPTH_EXCEEDED"
INVALID_CONFIG "SEED.INVALID_CONFIG"
STORE_ERROR "SEED.STORE_ERROR"
ABORTED "SEED.ABORTED"

type SeedErrorCode

The literal union of every value in SEED_CODES.

class SeedError extends SuaveplanError<SeedErrorCode>

Member Type
code SeedErrorCode (narrowed)
seedName? string
dependency? string
cause? unknown (inherited)
timestamp number (inherited; Date.now() at construction)
constructor(...) (code, message, options?: SeedErrorOptions)

SeedErrorOptions extends SuaveplanErrorOptions (cause, context, clock, traceId) with the additional optional seedName and dependency fields.

Implementation Notes

defineCodes returns a Object.freezed record with null prototype, so the registry cannot be mutated, reassigned, or extended at runtime — any attempt fails in strict mode (which TypeScript modules enforce). The literal type machinery preserves the exact ${Namespace}.${Reason} string template across every consumer, so switch (err.code) enjoys exhaustive narrowing without runtime cost.

SeedError's constructor passes message straight through to SuaveplanError's messageKey parameter. The base class resolves the key via the i18n resolver if one has been registered with setMessageResolver, falling back to the raw string otherwise — so this package works without i18n wiring and lights up automatically when @suaveplan/i18n is present.

Optional seedName / dependency fields are stored on the error instance directly (not on context) so they survive structured-cloning and serialisation via the toJSON helper from @suaveplan/error. Consumers that programmatically dispatch on a missing dependency can read err.dependency without first parsing err.context.