• v1.0.0 38a415c2ab

    Rene Nochebuena released this 2026-05-29 09:45:35 -06:00 | 0 commits to main since this release

    v1.0.0

    code.nochebuena.dev/einherjar/core


    Architecture Decisions Resolved

    Decision Outcome
    Four separate modules vs. one module with sub-packages One module — always ship together
    Private duck typing (micro-lib) vs. explicit contracts interfaces contracts/errs — visible, verifiable
    Partial gRPC error code set (micro-lib: 13) vs. complete set 16 codes — complete gRPC coverage
    Convenience constructors for selected codes only One constructor per code — complete coverage
    Startup banner always on vs. env-var toggle EINHERJAR_BANNER=off disables cleanly
    Banner version source runtime/debug.ReadBuildInfo() — no ldflags, no generated files; returns (devel) in local workspaces
    Loaded-module list source Type assertion to observability.Identifiable on registered components — zero coupling
    compliance_test == 1 TypeSpec (contracts rule) vs. <= 1 <= 1 — factory files with 0 types are valid
    Options naming vs. Config naming Config — consistent with all other Einherjar starters
    Env loading in-module vs. tag-only Tags only (caarlos0/env syntax) — application supplies the loader
    4 tags only in message providers vs. full coverage All go-playground/validator built-in tags covered
    No custom validator support vs. WithCustomValidator WithCustomValidator + FieldLevel + OverrideProvider

    API

    launcher

    import "code.nochebuena.dev/einherjar/core/launcher"
    
    // Types
    type Launcher interface {
        Append(components ...lifecycle.Component)
        BeforeStart(hooks ...Hook)
        Run() error
        Shutdown(ctx context.Context) error
    }
    type Hook   func() error
    type Config struct {
        ComponentStopTimeout time.Duration `env:"EINHERJAR_COMPONENT_STOP_TIMEOUT" envDefault:"15s"`
    }
    
    // Constructor
    func New(logger logging.Logger, cfg ...Config) Launcher
    
    // Environment
    // EINHERJAR_BANNER=off|false               — suppress startup banner
    // EINHERJAR_COMPONENT_STOP_TIMEOUT=<dur>   — max time per component OnStop (default 15s)
    
    // Banner format (startup output)
    //
    //   ███████╗ ... Einherjar ASCII art ...
    //   code.nochebuena.dev/einherjar  ·  Chosen warriors. Not for themselves.  ·  v1.0.0
    //
    //   · einherjar/web          v1.0.0
    //   · einherjar/db-postgres  v1.0.0
    //   ...
    //
    // Only components that implement observability.Identifiable appear in the list.
    // Version is read via runtime/debug.ReadBuildInfo(); returns "(devel)" in workspaces.
    

    logz

    import "code.nochebuena.dev/einherjar/core/logz"
    
    // Types
    type Config struct {
        Level      slog.Level `env:"EINHERJAR_LOG_LEVEL" envDefault:"INFO"`
        JSON       bool       `env:"EINHERJAR_LOG_JSON"  envDefault:"false"`
        StaticArgs []any      // programmatic only
        Writer     io.Writer  // programmatic only; defaults to os.Stdout
    }
    
    // Constructor
    func New(cfg Config) logging.Logger   // returns contracts interface
    
    // Context helpers
    func WithRequestID(ctx context.Context, id string) context.Context
    func GetRequestID(ctx context.Context) string
    func WithField(ctx context.Context, key string, value any) context.Context
    func WithFields(ctx context.Context, fields map[string]any) context.Context
    
    // Environment
    // EINHERJAR_LOG_LEVEL=DEBUG|INFO|WARN|ERROR   — minimum log level (default INFO)
    // EINHERJAR_LOG_JSON=true|false               — JSON output (default false)
    

    xerrors

    import "code.nochebuena.dev/einherjar/core/xerrors"
    
    // Code type + 16 constants
    type Code string
    const (
        ErrInvalidInput     Code = "INVALID_ARGUMENT"    // HTTP 400
        ErrOutOfRange       Code = "OUT_OF_RANGE"         // HTTP 400
        ErrUnauthorized     Code = "UNAUTHENTICATED"      // HTTP 401
        ErrPermissionDenied Code = "PERMISSION_DENIED"    // HTTP 403
        ErrNotFound         Code = "NOT_FOUND"            // HTTP 404
        ErrAlreadyExists    Code = "ALREADY_EXISTS"       // HTTP 409
        ErrAborted          Code = "ABORTED"              // HTTP 409
        ErrGone             Code = "GONE"                 // HTTP 410
        ErrPreconditionFailed Code = "FAILED_PRECONDITION" // HTTP 412
        ErrRateLimited      Code = "RESOURCE_EXHAUSTED"   // HTTP 429
        ErrCancelled        Code = "CANCELLED"            // HTTP 499
        ErrInternal         Code = "INTERNAL"             // HTTP 500
        ErrDataLoss         Code = "DATA_LOSS"            // HTTP 500
        ErrNotImplemented   Code = "UNIMPLEMENTED"        // HTTP 501
        ErrUnavailable      Code = "UNAVAILABLE"          // HTTP 503
        ErrDeadlineExceeded Code = "DEADLINE_EXCEEDED"    // HTTP 504
    )
    func (c Code) Description() string
    
    // Err struct
    type Err struct { /* unexported fields */ }
    
    // Base constructors
    func New(code Code, message string) *Err
    func Wrap(code Code, message string, err error) *Err
    
    // Convenience constructors (one per code)
    func InvalidInput(msg string, args ...any) *Err
    func OutOfRange(msg string, args ...any) *Err
    func Unauthorized(msg string, args ...any) *Err
    func PermissionDenied(msg string, args ...any) *Err
    func NotFound(msg string, args ...any) *Err
    func AlreadyExists(msg string, args ...any) *Err
    func Aborted(msg string, args ...any) *Err
    func Gone(msg string, args ...any) *Err
    func PreconditionFailed(msg string, args ...any) *Err
    func RateLimited(msg string, args ...any) *Err
    func Cancelled(msg string, args ...any) *Err
    func Internal(msg string, args ...any) *Err
    func DataLoss(msg string, args ...any) *Err
    func NotImplemented(msg string, args ...any) *Err
    func Unavailable(msg string, args ...any) *Err
    func DeadlineExceeded(msg string, args ...any) *Err
    
    // Builder methods
    func (e *Err) WithContext(key string, value any) *Err
    func (e *Err) WithError(err error) *Err
    func (e *Err) WithPlatformCode(code string) *Err
    
    // Accessors
    func (e *Err) Code() Code
    func (e *Err) Message() string
    func (e *Err) Fields() map[string]any
    func (e *Err) PlatformCode() string
    func (e *Err) Detailed() string
    
    // Standard interfaces
    func (e *Err) Error() string
    func (e *Err) Unwrap() error
    func (e *Err) ErrorCode() string             // errs.CodedError
    func (e *Err) ErrorContext() map[string]any  // errs.ContextualError
    func (e *Err) MarshalJSON() ([]byte, error)
    

    valid

    import "code.nochebuena.dev/einherjar/core/valid"
    
    // Interfaces
    type Validator interface {
        Struct(v any) error
    }
    type MessageProvider interface {
        Message(field, tag, param string) string
    }
    type FieldLevel interface {
        Field()     reflect.Value
        Param()     string
        FieldName() string
    }
    
    // Implementations
    var DefaultMessages MessageProvider  // English — full built-in tag coverage
    var SpanishMessages MessageProvider  // Spanish — full built-in tag coverage
    
    // Constructors / composers
    func New(opts ...Option) Validator
    func OverrideProvider(handlers map[string]func(field, param string) string, base MessageProvider) MessageProvider
    
    // Options
    type Option func(*config)
    func WithMessageProvider(mp MessageProvider) Option
    func WithCustomValidator(tag string, fn func(FieldLevel) bool) Option
    

    Install

    go get code.nochebuena.dev/einherjar/core@v1.0.0
    

    Dependencies

    Module Version Role
    code.nochebuena.dev/einherjar/contracts v1.0.0 Interface definitions
    github.com/go-playground/validator/v10 v10.30.1 Validation backend (hidden)
    Downloads