core

package
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 27, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package core provides the dependency injection container and lifecycle management for the Helix framework.

Index

Constants

View Source
const DefaultShutdownTimeout = 30 * time.Second

DefaultShutdownTimeout is the lifecycle shutdown budget used when no custom timeout is configured by the application bootstrap.

Variables

View Source
var (
	ErrNotFound        = errors.New("helix: not found")
	ErrCyclicDep       = errors.New("helix: cyclic dependency")
	ErrUnresolvable    = errors.New("helix: cannot resolve component")
	ErrShutdownTimeout = errors.New("helix: shutdown timeout exceeded")
)

Sentinel errors returned by the Helix DI container.

Functions

func ResolveAll

func ResolveAll[T any](container *Container) ([]T, error)

ResolveAll resolves every registered component assignable to T.

Resolver implementations may opt into this capability without expanding the base Resolver interface used by both reflection and generated modes.

Types

type ComponentRegistration

type ComponentRegistration struct {
	// Component is the registered value (pointer to struct).
	Component any
	// Scope controls how instances are created. Defaults to ScopeSingleton.
	Scope Scope
	// Lazy defers instantiation until the first Resolve call.
	Lazy bool
}

ComponentRegistration holds the metadata for a registered component. It is used internally by Resolver implementations.

func NewComponentRegistration

func NewComponentRegistration(component any) ComponentRegistration

NewComponentRegistration creates a ComponentRegistration with safe defaults (ScopeSingleton, Lazy = false). Use this instead of a struct literal to avoid the zero-value trap where Scope would be "" instead of ScopeSingleton.

type Container

type Container struct {
	// contains filtered or unexported fields
}

Container is the Helix dependency injection container. It delegates all registration and resolution to a pluggable Resolver. Use NewContainer with WithResolver to configure a concrete resolver.

func NewContainer

func NewContainer(opts ...Option) *Container

NewContainer creates a new Container and applies the provided options. Without WithResolver, Register and Resolve will return ErrUnresolvable.

func (*Container) Register

func (c *Container) Register(component any) error

Register adds a component to the container's resolver registry. Returns ErrUnresolvable if no Resolver has been configured or component is nil.

func (*Container) Resolve

func (c *Container) Resolve(target any) error

Resolve populates target with the registered component matching its type. Returns ErrUnresolvable if no Resolver has been configured or target is nil.

func (*Container) Shutdown

func (c *Container) Shutdown() error

Shutdown stops only the lifecycle components that successfully completed OnStart, in reverse startup order.

func (*Container) Start

func (c *Container) Start() error

Start resolves lifecycle-aware singleton components and invokes OnStart in dependency order. Lazy components are intentionally ignored until a future bootstrap phase decides to resolve them explicitly.

type CyclicDepError

type CyclicDepError struct {
	Path []string
}

CyclicDepError is returned when a cyclic dependency is detected. It wraps ErrCyclicDep and includes the full dependency path.

func (*CyclicDepError) Error

func (e *CyclicDepError) Error() string

func (*CyclicDepError) Unwrap

func (e *CyclicDepError) Unwrap() error

Unwrap allows errors.Is(err, ErrCyclicDep) to return true.

type DependencyGraph

type DependencyGraph struct {
	Nodes []string
	Edges map[string][]string
}

DependencyGraph represents the resolved component dependency graph. Nodes holds the type names of all registered components. Edges maps each component type name to its list of dependencies.

type Lifecycle

type Lifecycle interface {
	OnStart() error
	OnStop() error
}

Lifecycle is implemented by components that need to hook into the application start/stop sequence.

OnStart is called after all dependencies are resolved, before the HTTP server begins accepting connections.

OnStop is called on SIGTERM/SIGINT, in reverse dependency order, allowing components to flush buffers and release resources.

type LifecycleCandidate

type LifecycleCandidate struct {
	Name     string
	Instance Lifecycle
}

LifecycleCandidate holds a resolved non-lazy singleton component that implements the Lifecycle interface.

type LifecycleResolver

type LifecycleResolver interface {
	// LifecycleCandidates returns all non-lazy singleton components implementing
	// Lifecycle, in registration order. Dependency ordering is applied by the caller.
	LifecycleCandidates() ([]LifecycleCandidate, error)
}

LifecycleResolver is an optional capability that a Resolver may implement to enable Container.Start() and Container.Shutdown(). Resolvers that do not implement this interface cause Start() to return a clear error. ReflectResolver satisfies this interface by default.

type Option

type Option func(*Container)

Option is a functional option for configuring a Container.

func WithLogger

func WithLogger(logger *slog.Logger) Option

WithLogger overrides the logger used for lifecycle shutdown errors.

func WithResolver

func WithResolver(r Resolver) Option

WithResolver sets the Resolver implementation used by the Container. Without this option, Register and Resolve return ErrUnresolvable. Panics if r is nil.

func WithShutdownTimeout

func WithShutdownTimeout(timeout time.Duration) Option

WithShutdownTimeout overrides the lifecycle shutdown budget.

func WithValueLookup

func WithValueLookup(lookup func(key string) (any, bool)) Option

WithValueLookup configures scalar value resolution for fields tagged value:"...". Resolver implementations that do not support value lookup ignore this option.

type ReflectResolver

type ReflectResolver struct {
	// contains filtered or unexported fields
}

ReflectResolver resolves dependencies using Go reflection at runtime. It is the default Helix resolver mode and stores singleton instances by type.

func NewReflectResolver

func NewReflectResolver() *ReflectResolver

NewReflectResolver creates a reflection-based resolver with initialized maps.

func (*ReflectResolver) Graph

func (r *ReflectResolver) Graph() DependencyGraph

Graph returns a defensive copy of the current dependency graph.

func (*ReflectResolver) LifecycleCandidates

func (r *ReflectResolver) LifecycleCandidates() ([]LifecycleCandidate, error)

LifecycleCandidates returns all non-lazy singleton components implementing Lifecycle, in registration order. Uses reflect.Type as the seen-set key to prevent collisions between types that share a short string representation.

func (*ReflectResolver) Register

func (r *ReflectResolver) Register(component any) error

Register stores a component registration keyed by its concrete pointer type.

func (*ReflectResolver) Resolve

func (r *ReflectResolver) Resolve(target any) error

Resolve finds the registered component matching target's element type.

type Resolver

type Resolver interface {
	Register(component any) error
	Resolve(target any) error
	Graph() DependencyGraph
}

Resolver is the abstraction layer for dependency resolution strategies. Two implementations are provided:

  • ReflectResolver: runtime reflection, zero configuration required (default)
  • WireResolver: compile-time code generation, zero reflection in production (opt-in)

type Scope

type Scope string

Scope defines the instantiation strategy for a registered component.

const (
	// ScopeSingleton returns the same instance on every Resolve call (default).
	ScopeSingleton Scope = "singleton"
	// ScopePrototype creates a new instance on every Resolve call.
	ScopePrototype Scope = "prototype"
)

type WireResolver

type WireResolver struct {
	// contains filtered or unexported fields
}

WireResolver stores pre-wired singleton instances generated at compile time.

func NewWireResolver

func NewWireResolver() *WireResolver

NewWireResolver creates a resolver for compile-time generated DI wiring.

func (*WireResolver) Graph

func (r *WireResolver) Graph() DependencyGraph

Graph returns a flat dependency graph for generated wiring.

func (*WireResolver) LifecycleCandidates

func (r *WireResolver) LifecycleCandidates() ([]LifecycleCandidate, error)

LifecycleCandidates returns registered components implementing Lifecycle.

func (*WireResolver) Register

func (r *WireResolver) Register(component any) error

Register stores a pre-wired component instance by its concrete pointer type.

func (*WireResolver) Resolve

func (r *WireResolver) Resolve(target any) error

Resolve assigns a registered pre-wired instance to target.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL