Files
core/CHANGELOG.md
Rene Nochebuena 38a415c2ab feat(core): initial implementation — launcher, logz, xerrors, valid
Introduces `code.nochebuena.dev/einherjar/core` — the foundational implementation
module of the Einherjar framework. Provides four sub-packages that together cover
every service's baseline needs: lifecycle management, structured logging, typed
errors, and struct validation.

- launcher: Launcher interface — three-phase managed lifecycle (OnInit → BeforeStart
  hooks → OnStart → OS signal wait → OnStop in reverse). Accepts
  lifecycle.Component and logging.Logger from contracts. Prints an ASCII art banner
  at startup (EINHERJAR_BANNER=off to suppress). Banner includes core version via
  runtime/debug.ReadBuildInfo() and a loaded-module list for every registered
  component that implements observability.Identifiable. Config struct with
  EINHERJAR_COMPONENT_STOP_TIMEOUT env tag (caarlos0/env syntax, default 15s).

- logz: Logger implementation backed by log/slog. Returns contracts/logging.Logger.
  Detects errs.CodedError and errs.ContextualError (from contracts/errs) to enrich
  log records automatically — replaces the private duck-typed bridge from micro-lib.
  Context helpers: WithRequestID, WithField, WithFields, GetRequestID. Config struct
  with EINHERJAR_LOG_LEVEL (default INFO) and EINHERJAR_LOG_JSON (default false) env
  tags (caarlos0/env syntax); programmatic-only fields StaticArgs and Writer carry no
  tags.

- xerrors: Typed error codes with context enrichment. Complete gRPC canonical set
  (16 codes) plus HTTP 410 ErrGone. Adds ErrOutOfRange, ErrAborted, ErrDataLoss
  over micro-lib. One convenience constructor per code. *Err declares compile-time
  satisfaction of errs.CodedError and errs.ContextualError.

- valid: Struct validation wrapping go-playground/validator/v10. Validator interface
  + MessageProvider interface with full built-in tag coverage (~150 tags) in both
  DefaultMessages (English) and SpanishMessages (Spanish). Backend fully hidden;
  returns *xerrors.Err with ErrInvalidInput or ErrInternal. FieldLevel interface
  abstracts the backend's field-level access for custom validators.
  WithCustomValidator registers custom validation tags at construction time;
  OverrideProvider chains a tag→handler map with a fallback MessageProvider for
  custom tag messages without re-implementing built-ins.

Compliance test enforces CT-6 (at most one exported TypeSpec per file via AST) and
verifies behavioural correctness of all four sub-packages, including custom validator
registration and OverrideProvider composition. Compile-time var _ assertions prove
interface satisfaction.

docs: ADR-001 (core module composition), ADR-002 (logz contracts/errs adoption),
ADR-003 (Config naming convention and caarlos0/env tag standard)
2026-05-29 15:45:12 +00:00

6.2 KiB

Changelog — einherjar/core

All notable changes to this module are documented here. Format follows Keep a Changelog. This module adheres to Semantic Versioning.


1.0.0 — 2026-05-28

Added

launcher

  • Launcher interface — Append, BeforeStart, Run, Shutdown
  • New(logger logging.Logger, opts ...Options) Launcher — constructs the lifecycle orchestrator; takes contracts/logging.Logger (not the concrete logz type)
  • Hook type (func() error) — assembly-phase callback registered via BeforeStart
  • Config struct — ComponentStopTimeout time.Duration with env:"EINHERJAR_COMPONENT_STOP_TIMEOUT" envDefault:"15s" (caarlos0/env syntax)
  • Startup banner — printed to stdout before any slog output; disabled via EINHERJAR_BANNER=off or EINHERJAR_BANNER=false
  • Components accepted as lifecycle.Component from contracts — not a locally defined interface; any type that satisfies contracts/lifecycle.Component is directly compatible

logz

  • Config struct — Level slog.Level, JSON bool, StaticArgs []any, Writer io.Writer
    • Level and JSON carry env and envDefault tags (caarlos0/env syntax); StaticArgs and Writer are programmatic-only fields without tags
    • Env vars: EINHERJAR_LOG_LEVEL (default INFO), EINHERJAR_LOG_JSON (default false)
  • New(cfg Config) logging.Logger — returns contracts/logging.Logger; the concrete slogLogger struct is unexported
  • Error enrichment — when the error passed to Logger.Error satisfies errs.CodedError or errs.ContextualError (from contracts/errs), the corresponding error_code field and context key-value pairs are automatically appended to the log record; this replaces the private duck-typed bridge used in micro-lib's logz
  • Context helpers — WithRequestID, GetRequestID, WithField, WithFields
  • Logger.WithContext(ctx) — extracts request_id and extra fields from context and attaches them to every subsequent log record

xerrors

  • Code type (stable string wire values, gRPC-aligned)
  • 16 error code constants — complete gRPC canonical set plus ErrGone (HTTP 410)
    • New over micro-lib: ErrOutOfRange (gRPC OUT_OF_RANGE, HTTP 400), ErrAborted (gRPC ABORTED, HTTP 409), ErrDataLoss (gRPC DATA_LOSS, HTTP 500)
  • Code.Description() — human-readable description for each code
  • Err struct — code, message, err (cause), fields (context), platformCode
  • Base constructors: New(code, message), Wrap(code, message, err)
  • 16 convenience constructors (one per code): InvalidInput, OutOfRange, Unauthorized, PermissionDenied, NotFound, AlreadyExists, Aborted, Gone, PreconditionFailed, RateLimited, Cancelled, Internal, DataLoss, NotImplemented, Unavailable, DeadlineExceeded
  • Builder methods: WithContext, WithError, WithPlatformCode
  • Accessors: Code(), Message(), Fields(), PlatformCode(), Detailed()
  • Standard interfaces: error, Unwrap, json.Marshaler
  • Compile-time assertions: var _ errs.CodedError = (*Err)(nil) and var _ errs.ContextualError = (*Err)(nil) — formalises the duck-type bridge from micro-lib into an explicit, verifiable contract

valid

  • Validator interface — Struct(v any) error
  • New(opts ...Option) Validator — constructs a validator backed by go-playground/validator/v10 (backend is hidden; never exposed in the public API)
  • MessageProvider interface — Message(field, tag, param string) string
  • DefaultMessages — built-in English message provider
  • SpanishMessages — opt-in Spanish message provider
  • Option type and WithMessageProvider(mp MessageProvider) Option
  • FieldLevel interface — passed to custom validator functions; exposes Field() reflect.Value, Param() string, FieldName() string; go-playground backend never visible
  • WithCustomValidator(tag string, fn func(FieldLevel) bool) Option — registers a custom validation tag at construction time; panics on empty or conflicting tag
  • OverrideProvider(handlers map[string]func(field, param string) string, base MessageProvider) MessageProvider — composes a tag→message handler map with a fallback; use for custom tag messages without re-implementing built-ins
  • Full built-in tag coverage in DefaultMessages and SpanishMessages — all go-playground/validator tags have specific messages (fields, network, strings, format, comparisons, other; ~150 tags total)
  • Field names in error context prefer the json struct tag, falling back to the Go field name
  • Error codes: ErrInvalidInput for constraint failures, ErrInternal for non-struct arguments — both returned as *xerrors.Err

Design Notes

  1. Contracts as the source of truth. launcher accepts lifecycle.Component and logging.Logger from contracts. It does not define its own lifecycle interface. Any type that satisfies the contracts interface is directly compatible — no adapters needed.

  2. Duck typing replaced by explicit interfaces. micro-lib's logz detected enrichable errors via private errorWithCode/errorWithContext interfaces. core/logz detects them via contracts/errs.CodedError and contracts/errs.ContextualError. The decoupling (logz does not import xerrors) is preserved; the contract is now visible. See ADR-002.

  3. Complete gRPC error code set. micro-lib's xerrors had 13 codes. core/xerrors adds the three missing gRPC codes (OUT_OF_RANGE, ABORTED, DATA_LOSS) and provides a named convenience constructor for every code — New() and Wrap() are reserved for edge cases.

  4. One module, four sub-packages. The consolidation eliminates four-way version coordination for a set of packages that always ship and upgrade together. See ADR-001.

  5. Startup banner. The launcher prints an ASCII art banner to stdout before any structured log output. It is disabled via EINHERJAR_BANNER=off, not via code changes, so production deployments can suppress it without modifying the service.