Skip to content

Latest commit

 

History

History
557 lines (418 loc) · 15.8 KB

File metadata and controls

557 lines (418 loc) · 15.8 KB

API reference

This reference summarizes the exported API. Start with Getting started for a runnable flow and use the topic guides for behavioral details.

Graph model

Arc

type Arc struct {
	Place  string
	Weight int
}

func NewArc(place string, weight int) (Arc, error)

Describes a weighted input or output connection. Place names must be non-empty and weights must be positive.

Transition

func NewTransition(name string, from, to []Arc) (*Transition, error)

func (t *Transition) Name() string
func (t *Transition) From() []Arc
func (t *Transition) To() []Arc

Transitions own defensive copies of their arcs. From and To also return copies.

Definition

func NewDefinition(
	places []string,
	transitions []*Transition,
	options ...DefinitionOption,
) (*Definition, error)

func (d *Definition) Places() []string
func (d *Definition) InitialPlaces() []string
func (d *Definition) Transitions() []*Transition
func (d *Definition) MetadataStore() MetadataStore

An immutable, validated workflow graph.

Definition options:

func WithInitialPlaces(places ...string) DefinitionOption
func WithMetadataStore(store MetadataStore) DefinitionOption

DefinitionBuilder

func NewDefinitionBuilder() *DefinitionBuilder
func (b *DefinitionBuilder) AddPlaces(places ...string) error
func (b *DefinitionBuilder) AddTransition(transition *Transition) error
func (b *DefinitionBuilder) SetInitialPlaces(places ...string) error
func (b *DefinitionBuilder) SetMetadataStore(store MetadataStore)
func (b *DefinitionBuilder) Build() (*Definition, error)

Collects graph configuration incrementally and delegates final validation and copying to NewDefinition.

Marking

func NewMarking(places map[string]int) (*Marking, error)

func (m *Marking) Places() map[string]int
func (m *Marking) Context() map[string]any
func (m *Marking) SetContext(context map[string]any) error
func (m *Marking) Has(place string) bool
func (m *Marking) Mark(place string, tokens int) error
func (m *Marking) Unmark(place string, tokens int) error

Stores positive token counts and copied operation context.

Workflow execution

WorkflowInterface

type WorkflowInterface interface {
	Name() string
	Definition() *Definition
	GetMarking(any) (*Marking, error)
	Can(any, string) (bool, error)
	TransitionBlockers(any, string) (*TransitionBlockerList, error)
	Apply(any, string, map[string]any) (*Marking, error)
	EnabledTransitions(any) ([]*Transition, error)
	EnabledTransition(any, string) (*Transition, error)
}

The shared boundary implemented by workflows, state machines, and trace wrappers.

Workflow

func NewWorkflow(definition *Definition, options ...WorkflowOption) (*Workflow, error)

Supports multiple active places, multiple arcs, weighted tokens, splits, and joins. It implements every method in WorkflowInterface.

StateMachine

type StateMachine struct{ *Workflow }

func NewStateMachine(definition *Definition, options ...WorkflowOption) (*StateMachine, error)

Embeds Workflow and enforces at most one initial place, at most one input and output arc per transition, and unit weights.

Workflow options

func WithName(name string) WorkflowOption
func WithDispatcher(dispatcher Dispatcher) WorkflowOption
func WithMarkingStore(store MarkingStore) WorkflowOption
func WithEventsToDispatch(events ...string) WorkflowOption

Options configure identity, events, state persistence, and lifecycle-event filtering.

Transition blockers

type TransitionBlocker struct {
	Message string
	Code    string
}

func NewTransitionBlockerList(blockers ...TransitionBlocker) *TransitionBlockerList
func (l *TransitionBlockerList) Add(blocker TransitionBlocker)
func (l *TransitionBlockerList) Empty() bool
func (l *TransitionBlockerList) Len() int
func (l *TransitionBlockerList) All() []TransitionBlocker

All returns blockers in insertion order as a defensive slice.

Built-in codes:

const BlockedByMarkingCode = "workflow_marking"
const GuardBlockedCode = "workflow_guard"

Marking stores

Contract

type MarkingStore interface {
	GetMarking(any) (*Marking, error)
	SetMarking(any, *Marking, map[string]any) error
}

SubjectMarkingStore

func NewSubjectMarkingStore() *SubjectMarkingStore

Default multi-place store. It expects WorkflowMarking() map[string]int and SetWorkflowMarking(map[string]int, map[string]any) on the subject.

SingleStateMarkingStore

func NewSingleStateMarkingStore() *SingleStateMarkingStore

Default state-machine store. It expects WorkflowState() string and SetWorkflowState(string, map[string]any).

MethodMarkingStore

func NewMethodMarkingStore(singleState bool, property string) *MethodMarkingStore

Reflection-backed property store. For property status, it resolves GetStatus/SetStatus methods or an exported Status field. State machines use string values; workflows use map[string]int. An empty property selects marking.

AccessorMarkingStore

func NewAccessorMarkingStore(
	get func(any) (map[string]int, error),
	set func(any, map[string]int, map[string]any) error,
) *AccessorMarkingStore

Uses caller-supplied multi-place getter and setter functions.

SingleStateAccessorStore

func NewSingleStateAccessorStore(
	get func(any) (string, error),
	set func(any, string, map[string]any) error,
) *SingleStateAccessorStore

Uses caller-supplied single-state getter and setter functions.

Registry

func NewRegistry() *Registry
func NewRegistryWithLimits(ResourceLimits) *Registry
func (r *Registry) Add(workflow WorkflowInterface, strategy SupportStrategy) error
func (r *Registry) Len() int
func (r *Registry) Get(subject any, name string) (WorkflowInterface, error)
func (r *Registry) Has(subject any, name string) bool
func (r *Registry) All(subject any) []WorkflowInterface

Get requires exactly one supported match. Named lookup uses a name index; an empty name scans all registered entries.

Support strategy APIs:

type SupportStrategy interface {
	Supports(WorkflowInterface, any) bool
}

type SupportStrategyFunc func(WorkflowInterface, any) bool

func TypeSupportStrategy[T any]() SupportStrategy

YAML configuration

func NewConfigurationBindings() *ConfigurationBindings
func (b *ConfigurationBindings) RegisterSupport(string, SupportStrategy) error
func (b *ConfigurationBindings) RegisterMarkingStore(string, MarkingStore) error
func (b *ConfigurationBindings) RegisterPlaceSet(string, ...string) error
func (b *ConfigurationBindings) RegisterDefinitionValidator(string, DefinitionValidator) error
func (b *ConfigurationBindings) SetAuditLogger(*log.Logger) error

type DefinitionValidator interface {
	Validate(*Definition, string) error
}

func NewYAMLLoader(*Registry, *ConfigurationBindings) *YAMLLoader
func NewYAMLLoaderWithLimits(*Registry, *ConfigurationBindings, ResourceLimits) *YAMLLoader
func (l *YAMLLoader) Load(path string) error
func (l *YAMLLoader) Decode(data []byte) error
func (l *YAMLLoader) Registry() *Registry
func (l *YAMLLoader) Dispatcher() *EventDispatcher

Support, service, and validator names are explicitly bound to Go behavior. Method marking stores additionally resolve their configured property through accessor methods or an exported field. YAML maps retain source order, weighted arcs are preserved for workflows, unknown fields are rejected, and a rejected document does not add workflows to the registry. See YAML configuration for the complete schema.

Events

Dispatcher integration

type Dispatcher = contracts.Dispatcher
type EventDispatcher = eventdispatcher.EventDispatcher
type Listener = eventdispatcher.Listener
type ListenerHandle = eventdispatcher.ListenerID
type SubscriberHandle = eventdispatcher.SubscriberID
type EventSubscription = eventdispatcher.Subscription
type EventSubscriber = eventdispatcher.Subscriber
type RegisteredListener = eventdispatcher.RegisteredListener
type GenericEvent = eventdispatcher.GenericEvent
type NamedEvent = contracts.NamedEvent

func NewEventDispatcher() *eventdispatcher.EventDispatcher
func NewImmutableDispatcher(eventdispatcher.Dispatcher) eventdispatcher.Dispatcher
func NewGenericEvent(subject any, arguments map[string]any) *eventdispatcher.GenericEvent

These are integrations and aliases of the standalone dispatcher libraries; workflow does not maintain another listener implementation.

Event names

const (
	EventAnnounce   = "workflow.announce"
	EventCompleted  = "workflow.completed"
	EventEntered    = "workflow.entered"
	EventEnter      = "workflow.enter"
	EventGuard      = "workflow.guard"
	EventLeave      = "workflow.leave"
	EventTransition = "workflow.transition"
)

func WorkflowEventName(kind, workflowName, node string) (string, error)
func NewListenerBinding(kind, workflowName, node string) (ListenerBinding, error)

ListenerBinding contains the resolved Event name.

Lifecycle event contract

type WorkflowEvent interface {
	Subject() any
	Marking() *Marking
	Transition() *Transition
	WorkflowName() string
	Context() map[string]any
	SetContext(map[string]any)
}

Concrete types are AnnounceEvent, CompletedEvent, EnteredEvent, EnterEvent, LeaveEvent, and TransitionEvent.

GuardEvent

func NewGuardEvent(
	subject any,
	marking *Marking,
	transition *Transition,
	workflow WorkflowInterface,
	context map[string]any,
) *GuardEvent

func (e *GuardEvent) Blocked() bool
func (e *GuardEvent) PropagationStopped() bool
func (e *GuardEvent) AddBlocker(blocker TransitionBlocker)
func (e *GuardEvent) Blockers() *TransitionBlockerList
func (e *GuardEvent) SetBlocked(blocked bool, message string)

GuardEvent also implements WorkflowEvent.

Guard rules

type GuardContext struct {
	Subject    any
	Marking    *Marking
	Transition *Transition
	Workflow   WorkflowInterface
	Context    map[string]any
}

type GuardRule struct {
	Transition *Transition
	Evaluate   func(GuardContext) (bool, error)
}

func NewGuardRuleListener(rules map[string][]GuardRule) *GuardRuleListener
func (l *GuardRuleListener) OnTransition(event *GuardEvent, eventName string) error

Event filters

Per-apply context constants:

const (
	DisableLeaveEvent      = "disable_leave_event"
	DisableTransitionEvent = "disable_transition_event"
	DisableEnterEvent      = "disable_enter_event"
	DisableEnteredEvent    = "disable_entered_event"
	DisableCompletedEvent  = "disable_completed_event"
	DisableAnnounceEvent   = "disable_announce_event"
)

AuditTrailSubscriber

func NewAuditTrailSubscriber(logger *log.Logger) *AuditTrailSubscriber
func (s *AuditTrailSubscriber) Subscriptions() []EventSubscription

Logs leave, transition, and enter activity.

Metadata

type Metadata map[string]any

type MetadataStore interface {
	WorkflowMetadata() Metadata
	PlaceMetadata(string) Metadata
	TransitionMetadata(*Transition) Metadata
	WorkflowValue(string) (any, bool)
	PlaceValue(string, string) (any, bool)
	TransitionValue(*Transition, string) (any, bool)
}

In-memory implementation:

func NewInMemoryMetadataStore(
	workflow Metadata,
	places map[string]Metadata,
	transitions map[*Transition]Metadata,
) *InMemoryMetadataStore

It implements every MetadataStore method and uses defensive copies for metadata maps.

Diagram export

type TransitionRendering int

const (
	WorkflowTransitions TransitionRendering = iota
	StateMachineTransitions
)

type DumpOptions struct {
	WithMetadata     bool
	Label            string
	Title            string
	IncludeListeners bool
}

func SupportedDumpFormats() []string
func DumpWorkflow(definition *Definition, format string, options DumpOptions) (string, error)

Concrete dumpers:

func NewMermaidDumper(rendering TransitionRendering) *MermaidDumper
func (d *MermaidDumper) Dump(*Definition, *Marking, any) (string, error)

func NewGraphvizDumper() *GraphvizDumper
func NewStateMachineGraphvizDumper() *GraphvizDumper
func (d *GraphvizDumper) Dump(*Definition, *Marking, any) (string, error)

func NewPlantUMLDumper(rendering TransitionRendering) *PlantUMLDumper
func (d *PlantUMLDumper) Dump(*Definition, *Marking, any) (string, error)

The final argument accepts nil, DumpOptions, or *DumpOptions.

Diagnostics

TraceableWorkflow

type WorkflowCall struct {
	Method   string
	Duration time.Duration
	Return   any
	Err      error
}

func NewTraceableWorkflow(inner WorkflowInterface) *TraceableWorkflow
func NewTraceableWorkflowWithCapacity(inner WorkflowInterface, capacity int) (*TraceableWorkflow, error)
func (t *TraceableWorkflow) Calls() []WorkflowCall
func (t *TraceableWorkflow) Capacity() int
func (t *TraceableWorkflow) DroppedCalls() uint64
func (t *TraceableWorkflow) Reset()

TraceableWorkflow implements WorkflowInterface and records method duration, return value, and error in a bounded chronological ring containing the newest calls. The default capacity is 128 calls.

DiagnosticsCollector

type DiagnosticsItem struct {
	Dump      string
	Listeners map[string][]RegisteredListener
}

func NewDiagnosticsCollector(
	workflows []WorkflowInterface,
	dispatcher *EventDispatcher,
) *DiagnosticsCollector

func (c *DiagnosticsCollector) Collect() (map[string]DiagnosticsItem, error)

Collects Mermaid definitions and workflow-specific listener snapshots.

Validators

func NewWorkflowValidator(singleState bool) *WorkflowValidator
func (v *WorkflowValidator) Validate(definition *Definition, name string) error

func NewStateMachineValidator() *StateMachineValidator
func (v *StateMachineValidator) Validate(definition *Definition, name string) error

Workflow validation checks ambiguous duplicate-name variants. State-machine validation additionally checks initial-place count, input/output cardinality, and arc weights.

Structured errors

UndefinedTransitionError

type UndefinedTransitionError struct {
	Transition string
	Workflow   string
	Context    map[string]any
}

Unwraps to ErrUndefinedTransition.

NotEnabledTransitionError

type NotEnabledTransitionError struct {
	Transition string
	Workflow   WorkflowInterface
	Subject    any
	Blockers   *TransitionBlockerList
	Context    map[string]any
}

Unwraps to ErrNotEnabledTransition.

Sentinel errors

All sentinel errors support errors.Is when wrapped:

Error Condition
ErrEmptyPlace empty place name
ErrInvalidWeight non-positive arc weight
ErrEmptyTransitionName empty transition name
ErrUnknownInitialPlace initial place is absent from the definition
ErrPlaceNotMarked token removal from a missing place
ErrNegativeTokens removal would make a token count negative
ErrInvalidTokenCount non-positive marking token count
ErrNoInitialPlace empty marking cannot be initialized
ErrUnknownPlace a store returns a place outside the definition
ErrUndefinedTransition transition name is not defined
ErrNotEnabledTransition transition is defined but disabled
ErrGuardEventCannotBeDisabled guard event appears in a block-list
ErrMixedEventFilterModes allow-list and block-list are mixed
ErrUnsupportedSubject marking store cannot handle the subject
ErrMultipleStates single-state marking has invalid cardinality or weight
ErrMultipleWorkflows registry selection is ambiguous
ErrWorkflowNotFound registry has no supported match
ErrDuplicateTransitionName same-name variants share an input place
ErrInvalidStateMachineArc state-machine arc weight is not one
ErrMultipleOutputs state-machine transition has several outputs
ErrMultipleInputs state-machine transition has several inputs
ErrWorkflowNameRequired node-specific event lacks workflow name
ErrUnsupportedDumpFormat dump format is unknown
ErrDispatcherRequired requested diagnostics require a dispatcher