Inheritance
AFSM supports inheritance. Subclasses auto-merge the parent’s state diagram.
Basic inheritance
class Base extends FSM { @ChangeState(FSM.INIT, 'connected') async connect() {}
@ChangeState([], 'disconnected') async disconnect() {}}
class Advanced extends Base { @ChangeState('connected', 'authenticated') async authenticate() {}}Advanced’s stateDiagram includes all of Base’s edges plus its own authenticate edge.
stateDiagram merging logic
The stateDiagram getter, on first access:
- Reads the decorator metadata for the current prototype (from the
stateDiagramMap) - Recursively reads the parent prototype’s
stateDiagram(viaparent.stateDiagram) - Merges all edges and states
- Caches via
Object.definePropertieson the current prototype
// simplified pseudo-codeconst proto = Object.getPrototypeOf(this)const parentProto = Object.getPrototypeOf(proto)if (stateDiagram.has(parentProto)) { parent.stateDiagram.forEach(line => result.add(line)) parent.allStates.forEach(s => allState.add(s))}stateConfig.forEach(({ from, to, action }) => { // add current class's edges})allStates
allStates (also cached via Object.defineProperties) includes all known states:
- All
fromandtostates - All
action + 'ing'intermediate states
Used by from: [] to generate “from every state into the intermediate” edges.
Caveats
Decorator metadata is per-prototype
const a = new Base()const b = new Advanced()a.stateDiagram // only Base's edgesb.stateDiagram // Base + Advanced edgesCache is immutable
Object.defineProperties defines stateDiagram and allStates as value properties (non-writable, non-configurable). After first access, adding new @ChangeState won’t update the cache.
Subclasses can’t override parent transitions
If a subclass defines a same-named method, TS decorators register new metadata on the subclass prototype, but the parent’s metadata stays on the parent prototype. The stateDiagram getter reads both, potentially producing duplicate edges (deduped by Set).