Skip to content

sink/bridge

import "github.com/stablekernel/crucible/sink/bridge"

Package bridge composes a crucible/state machine with a crucible/sink Manifold without either core importing the other. It adapts state’s two non-required observation seams to a sink fan-out:

  • Middleware wraps a machine’s Fire so every successful transition fans out through a Manifold. Because Fire carries a context.Context, the middleware starts a “state.transition” span and propagates its context into Manifold.Sink, so the “sink.Sink” span (and each outlet’s downstream span) nests under the transition span through the shared crucible/telemetry tracer. This is the context-propagating, trace-correlating path.
  • Inspector adapts a Manifold to state’s Inspector observer. It is the ergonomic one-liner for “fan every transition out”, but state.Inspector carries no context.Context, so it cannot propagate trace context; use Middleware when span nesting matters.

Experimental (pre-v1); the API may change until the suite locks v1.0.0.

func Inspector(m *csink.Manifold) state.InspectorFunc

Inspector adapts m to a state.Inspector, fanning each transition event out through m. It uses context.Background because state.Inspector carries no context, so emit spans do not nest under a transition span; use Middleware for trace correlation. Register it with the WithInspector cast option.

func Middleware[S comparable, E comparable, C any](m *csink.Manifold, opts ...Option) state.Middleware[S, E, C]

Middleware returns a state.Middleware that fans every successful transition out through m. It starts a transition span on the configured tracer and propagates that span’s context into m.Sink, so the emit span nests under the transition span. Install it with the machine builder’s Use method.

Example

bucket := csink.NewBucket()
m := csink.NewManifold(csink.WithOutlets(bucket))
// Install the bridge as middleware: every transition fans out through m.
machine := state.Forge[string, string, *bulb]("switch").
Use(bridge.Middleware[string, string, *bulb](m)).
State("off").State("on").
Initial("off").
CurrentStateFn(func(b *bulb) string { return b.cur() }).
Transition("off").On("toggle").GoTo("on").
Quench(state.Strict())
machine.Cast(&bulb{}).Fire(context.Background(), "toggle")
tr := csink.RecordsOf[bridge.Transition](bucket)[0]
fmt.Printf("%s: %s -> %s\n", tr.Event, tr.From, tr.To)
// Output: toggle: off -> on
toggle: off -> on

Option configures the bridge adapters.

type Option func(*config)

func WithSpanName(name string) Option

WithSpanName overrides the transition span name (default “state.transition”).

func WithTracer(t telemetry.Tracer) Option

WithTracer sets the tracer the Middleware starts the transition span on. The default is telemetry.NopTracer(). Wire the same tracer the Manifold uses so the emit span nests under the transition span. A nil tracer is ignored.

Transition is the payload the bridge fans out for each observed state transition. Register a transformer for it on each destination’s registry to persist or publish transitions.

type Transition struct {
// Machine is the name of the machine that transitioned.
Machine string
// Event is the human-readable label of the event that drove the transition.
Event string
// From is the primary active leaf before the transition.
From string
// To is the primary active leaf after the transition.
To string
}

Generated by gomarkdoc