109 lines
6.2 KiB
Markdown
109 lines
6.2 KiB
Markdown
|
|
# Changelog — einherjar/core
|
||
|
|
|
||
|
|
All notable changes to this module are documented here.
|
||
|
|
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||
|
|
This module adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## [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](docs/adr/ADR-002-logz-contracts-errs.md).
|
||
|
|
|
||
|
|
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](docs/adr/ADR-001-core-module-composition.md).
|
||
|
|
|
||
|
|
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.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
[1.0.0]: https://code.nochebuena.dev/einherjar/core/releases/tag/v1.0.0
|