Documentation
¶
Overview ¶
Package events implements a unified event-contract core: a typed Envelope, a runtime Kind registry, table-driven dispatch, a generic config-driven producer engine, and a dependency-free JSONPath subset.
The design is deliberately schema-additive: event kinds are registered at runtime (mirroring verifier_profiles) rather than enumerated in Go, so new event types can be added without touching this package.
Index ¶
- func JSONPath(root any, expr string) (any, error)
- type DefaultFetcher
- type Dispatcher
- type Disposition
- type Envelope
- type FetchSpec
- type Fetcher
- type Handler
- type HandlerFunc
- type Kind
- type Producer
- type ProducerConfig
- type ProducerFactory
- type Registry
- func (r *Registry) DispositionFor(typ string) Disposition
- func (r *Registry) Lookup(name string) (Kind, bool)
- func (r *Registry) Names() []string
- func (r *Registry) Register(name string, disp Disposition) error
- func (r *Registry) RegisterProducer(name string, factory ProducerFactory) error
- func (r *Registry) SetNamespaceDisposition(namespace string, disp Disposition) error
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func JSONPath ¶
JSONPath evaluates a small JSONPath subset against a value decoded from JSON (map[string]any / []any / scalars). Supported syntax:
- dotted traversal: ".a.b"
- array index: ".items[0]"
- one equality filter: ".checks[?(@.conclusion=='FAILURE')]"
A leading "$" is optional. The filter selects the first array element whose field equals the quoted literal. No other predicates are supported.
Types ¶
type DefaultFetcher ¶
type DefaultFetcher struct {
// Client is used for http fetches; nil falls back to http.DefaultClient.
Client *http.Client
}
DefaultFetcher implements Fetcher with real exec and http I/O. It is the production seam; tests substitute a fake.
type Dispatcher ¶
type Dispatcher struct {
// contains filtered or unexported fields
}
Dispatcher routes Envelopes to Handlers using the Registry to resolve disposition. Routing is table-driven: there is no switch on the event type.
func NewDispatcher ¶
func NewDispatcher(registry *Registry, logw io.Writer) *Dispatcher
NewDispatcher builds a Dispatcher over the given registry. logw receives the warning lines emitted for soft, unregistered types; pass io.Discard to mute.
func (*Dispatcher) Dispatch ¶
func (d *Dispatcher) Dispatch(e Envelope) error
Dispatch validates and routes an Envelope. The resolution order is:
- invalid envelope -> error
- explicit per-type handler -> invoke it
- reject disposition -> loud fail-fast error
- soft disposition -> warn + generic handler (no-op if unset)
func (*Dispatcher) On ¶
func (d *Dispatcher) On(typ string, h Handler) error
On registers a per-type handler. The type need not be a registered kind, but emit-time validation still applies the namespace disposition.
func (*Dispatcher) SetSoftHandler ¶
func (d *Dispatcher) SetSoftHandler(h Handler)
SetSoftHandler installs the generic handler used for soft-disposition types that have no explicit per-type handler.
type Disposition ¶
type Disposition int
Disposition controls what happens when an Envelope whose Type is not registered is encountered. It is resolved by the most specific matching namespace; an exact kind registration always wins.
const ( // DispositionReject is the fail-fast default: unregistered types in a // reject namespace are loud errors at emit time. DispositionReject Disposition = iota // DispositionSoft routes unregistered types to a generic handler and logs // a warning instead of failing. DispositionSoft )
func (Disposition) String ¶
func (d Disposition) String() string
String renders a Disposition for diagnostics and error messages.
type Envelope ¶
type Envelope struct {
Type string `json:"type"`
Source string `json:"source"`
OccurredAt time.Time `json:"occurred_at"`
IdempotencyKey string `json:"idempotency_key"`
Payload json.RawMessage `json:"payload"`
}
Envelope is the canonical wire shape for every event flowing through the system. Payload is left as raw JSON so the core never needs to know the concrete shape of any particular kind.
func NewEnvelope ¶
func NewEnvelope(typ, source, idempotencyKey string, occurredAt time.Time, payload json.RawMessage) (Envelope, error)
NewEnvelope constructs an Envelope and validates it. OccurredAt defaults to the current time when the caller passes the zero value.
type FetchSpec ¶
type FetchSpec struct {
Argv []string // exec: command and arguments
URL string // http: request URL
Method string // http: defaults to GET
Headers map[string]string // http: request headers
}
FetchSpec describes how the producer obtains the raw JSON document for a cycle. Exactly one of Argv (exec) or URL (http) must be set.
type Handler ¶
Handler processes a single Envelope. Implementations must be safe to call from the Dispatcher and should treat the Envelope as read-only.
type HandlerFunc ¶
HandlerFunc adapts an ordinary function to the Handler interface.
func (HandlerFunc) Handle ¶
func (f HandlerFunc) Handle(e Envelope) error
Handle calls the underlying function.
type Kind ¶
type Kind struct {
Name string
Disposition Disposition
Producer ProducerFactory
}
Kind is a registered event kind. Producer is optional and only set when a producer factory was registered for the kind's name.
type Producer ¶
type Producer struct {
// contains filtered or unexported fields
}
Producer is a stateful event source: each Cycle fetches, maps, diffs against the previous snapshot, and returns Envelopes for changed items.
func NewProducer ¶
func NewProducer(cfg ProducerConfig, fetcher Fetcher) (*Producer, error)
NewProducer builds a Producer. A nil fetcher defaults to the real exec/http fetcher; tests inject a fake.
type ProducerConfig ¶
type ProducerConfig struct {
Type string // event type emitted for each change
Source string // envelope source
Fetch FetchSpec // how to obtain the document
Each string // jsonpath to the list of items
Map map[string]string // canonical field -> jsonpath (relative to item)
KeyBy string // canonical field used as the snapshot/idempotency key
}
ProducerConfig drives the generic producer engine. There are deliberately no per-platform fields: a GitHub PR producer and a metrics producer differ only by config.
type ProducerFactory ¶
ProducerFactory builds a Producer for a registered kind. It is defined here (rather than producer.go) because the registry owns the binding from name to factory; producer.go supplies the concrete engine.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry is a runtime, thread-safe table of event kinds keyed by name plus a set of namespace-level dispositions. There is intentionally no Go enum of kind names — everything is registered at runtime.
func NewRegistry ¶
func NewRegistry() *Registry
NewRegistry returns an empty Registry whose namespaces default to reject.
func (*Registry) DispositionFor ¶
func (r *Registry) DispositionFor(typ string) Disposition
DispositionFor resolves the disposition for a type: an exact kind wins, then the longest matching namespace prefix, then the registry default.
func (*Registry) Register ¶
func (r *Registry) Register(name string, disp Disposition) error
Register adds (or replaces) a kind by exact name with the given disposition.
func (*Registry) RegisterProducer ¶
func (r *Registry) RegisterProducer(name string, factory ProducerFactory) error
RegisterProducer binds a producer factory to a kind name, creating the kind with the default disposition if it was not registered yet.
func (*Registry) SetNamespaceDisposition ¶
func (r *Registry) SetNamespaceDisposition(namespace string, disp Disposition) error
SetNamespaceDisposition declares the disposition for a whole namespace (e.g. "event.metric"). Unregistered types in that namespace inherit it.