Skip to content

feat(errors): ERROR_CATALOG + reportError + new codes — v1 prep 2/3#2

Merged
Changsik00 merged 4 commits into
mainfrom
feat/error-catalog-v1
May 17, 2026
Merged

feat(errors): ERROR_CATALOG + reportError + new codes — v1 prep 2/3#2
Changsik00 merged 4 commits into
mainfrom
feat/error-catalog-v1

Conversation

@Changsik00
Copy link
Copy Markdown
Owner

Summary

Second of three stacked PRs. Builds the error catalog architecture that distinguishes v1 from v0 — a single source of truth for codes, severity, titles, and docs anchors that everything else derives from.

Breaking changes (slated for v1.0.0):

  • loadViteEnv removed. Was a placeholder returning {}; Vite consumers should use import.meta.env or @env-kit/node-settings/vite.

New public API (additive):

  • ERROR_CATALOGRecord<NodeSettingsErrorCode, { severity, title, docsAnchor }>. The type union is now keyof typeof ERROR_CATALOG.
  • ErrorSeverity = "config" | "runtime" | "io" | "usage".
  • NodeSettingsError.severity / .title / .docsUrl getters read from the catalog.
  • reportError(err, { docsBase? }): ErrorReport — distils any throw (NodeSettingsError, ZodError, plain Error) into a JSON-serialisable record with code, severity, title, message, hint?, docsUrl, issues[]?, cause?.
  • DEFAULT_DOCS_BASE.
  • Five new error codes wrap previously-bare boundaries: CONFIG_NOT_FOUND, CONFIG_LOAD_FAILED, CONFIG_INVALID_EXPORT, FILE_READ_FAILED, K8S_YAML_PARSE_FAILED.

Tooling:

  • scripts/verify-errors.mjs (new layer in pnpm verify) — fails CI if any catalog entry has no raise(...) call site, any anchor is missing from docs/ERRORS.md, or the auto-generated catalog section drifts.
  • scripts/generate-errors-doc.mjs (pnpm gen:errors-doc) — regenerates the catalog section of docs/ERRORS.md between <!-- BEGIN/END AUTO-GENERATED --> markers.
  • scripts/lib/errors-doc.mjs — shared rendering so generator and verifier can't disagree.

cli/validate.ts:serializeError is gone; the subcommand uses reportError() directly. The --format=json output gains severity, title, docsUrl, cause (additive — existing consumers stay happy).

Commits

# Commit Phase
1 feat(errors): wrap fs/parse boundaries in NodeSettingsError 3
2 chore: remove vite-env placeholder and unused utils barrel 5.5 (BREAKING)
3 chore: post-removal sync — drop loadViteEnv from verify-dist + YAML case (sync, split from c9882b4 + 866adb5)
4 feat(errors): catalog as single source of truth + reportError handler 8 + 9

Stacked PR

This is 2/3, stacked on refactor/internal-patterns (PR #1). Merge PR #1 first; this base will auto-update to main.

Next: 3/3 docs/test-taxonomy-and-finaltest:unit/integ/e2e/contract scripts + docs/TESTING.md + final docs refresh.

Test plan

  • pnpm verify — full 9-layer chain (this PR adds layer 8: verify:errors).
  • pnpm test:coverage — 294 passing, branches 80.34% (above 80% floor).
  • Review ERROR_CATALOG in src/errors.ts — severity assignments correct?
  • Review reportError() shape — is the ErrorReport payload what downstream consumers (Sentry, log aggregators) want?
  • Review the removal of loadViteEnv — confirm no internal consumer remains. CHANGELOG calls it out under ### Removed.

Replace raw Error throws at every IO/parse boundary so callers can
match on a stable err.code instead of err.message. Five new codes:

- CONFIG_NOT_FOUND     CLI could not auto-discover a settings config
- CONFIG_LOAD_FAILED   config file present but import failed (syntax
                      error, missing dep, …); original on err.cause
- CONFIG_INVALID_EXPORT  module loaded but no defineSettings() export
- FILE_READ_FAILED     dotenv / K8s manifest file failed to read
                      (ENOENT, EACCES, …); original on err.cause
- K8S_YAML_PARSE_FAILED  diff input did not parse as YAML

Touched modules:

- cli/load-user-config.ts   3 raw Error throws → NodeSettingsError
- loaders/dotenv-file.ts    new readDotenvSafe() wraps fs failures
- loaders/dotenv-cascade.ts uses the safe reader
- diff-k8s.ts               parseAllDocuments + doc.errors wrapped
- cli/diff.ts               parseK8sYaml now inside try; readSource
                            uses FILE_READ_FAILED

docs/ERRORS.md gains a CLI/loader section + a client-env section
(was missing from the old doc).
src/loaders/vite-env.ts shipped a placeholder loadViteEnv() returning
{} for as long as the package has existed. Real Vite consumers either
read import.meta.env directly or use the @env-kit/node-settings/vite
plugin — nobody benefits from the stub, and exporting it suggests a
working integration that isn't there.

Removed:
- src/loaders/vite-env.ts        (placeholder)
- src/utils/index.ts             (unused barrel, no consumers)

Public API change (breaking): loadViteEnv is gone. Slated for v1.0.0.

Also tightened AGENTS.md's import map and file map (the loaders entry
was missing loadDotenvCascade and dotenv-cascade.ts entirely; utils
was missing zod-issues.ts). Refreshed api-surface snapshots.
Companion fix-ups for the two preceding commits:

- scripts/verify-dist.mjs no longer expects loadViteEnv in
  REQUIRED_ROOT (was removed in the previous commit).
- src/cli/diff.ts uses 'YAML input' to match the casing already
  used in src/diff-k8s.ts. The change rode along with the rest of
  the punctuation pass on the original branch, but it referenced
  raise(FILE_READ_FAILED, ...) calls that only exist on this branch
  — so it lands here instead.

(Both are the trailing halves of the original c9882b4 / 866adb5
commits; the parts that didn't depend on this PR's diff went into
PR #1.)
Refactors error handling around a single source of truth — the new
ERROR_CATALOG — so codes, severity, titles, doc anchors, and the
`docs/ERRORS.md` table never drift apart.

API additions (public):
- ERROR_CATALOG: Record<code, { severity, title, docsAnchor }>.
  NodeSettingsErrorCode is now derived from its keys, so adding an
  entry extends the union with no other edits.
- ErrorSeverity = 'config' | 'runtime' | 'io' | 'usage'. Lets consumers
  route to the right alarm channel without hard-coding code lists.
- NodeSettingsError gains .severity, .title, .docsUrl getters that
  read from the catalog at runtime.
- reportError(err, { docsBase? }): ErrorReport — distils any throw
  (NodeSettingsError, ZodError, plain Error) into a JSON-serialisable
  record with code, severity, title, message, hint?, docsUrl,
  issues[]?, cause?.
- DEFAULT_DOCS_BASE — overridable docs URL base.

Internal cleanup:
- cli/validate.ts's local serializeError() removed; the subcommand
  calls reportError(err) directly. The --format=json shape gains
  severity, title, docsUrl, cause (additive, not breaking JSON
  consumers).
- The previously-untyped 'ENV_FILE_NOT_FOUND' literal in validate.ts
  is now FILE_READ_FAILED, matching the existing catalog code.

Tooling:
- scripts/verify-errors.mjs — fails if any catalog entry has no
  raise() call site, any anchor is missing from docs/ERRORS.md, or
  the auto-generated catalog section drifts. Wired into pnpm verify
  as a 9th layer.
- scripts/generate-errors-doc.mjs (pnpm gen:errors-doc) — regenerates
  the auto-managed catalog table in docs/ERRORS.md between BEGIN/END
  markers.
- scripts/lib/errors-doc.mjs — shared rendering used by both scripts
  so generator and verifier can't disagree.

docs/ERRORS.md restructured: hand-written intro + severity-grouped
auto-managed catalog. Was four hand-edited tables that could drift
from src/errors.ts; now the source of truth is the catalog and the
doc is regenerated from it.
Base automatically changed from refactor/internal-patterns to main May 17, 2026 01:40
@Changsik00 Changsik00 merged commit 93bbb7c into main May 17, 2026
@Changsik00 Changsik00 deleted the feat/error-catalog-v1 branch May 17, 2026 01:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant