From 9fd8ff60d91c3bc6d1859e99a4456e4f9aeed9c8 Mon Sep 17 00:00:00 2001 From: Dmitriy Pleshevskiy Date: Fri, 20 Aug 2021 11:43:31 +0300 Subject: [PATCH] refac: add more utils and types --- fsm.ts | 61 +++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/fsm.ts b/fsm.ts index 3935be0..96c6422 100644 --- a/fsm.ts +++ b/fsm.ts @@ -3,41 +3,44 @@ type StateTransitions = WeakMap< WeakSet> >; +type StateName = string; +type StateOrName = State | StateName; + export const _states = Symbol("states"); export const _stateTransitions = Symbol("state transitions"); export const _prevState = Symbol("previous state"); export const _currState = Symbol("current state"); export class StateMachineBuilder { - [_states]: Map>; + [_states]: Map>; - [_stateTransitions]: Array<[string, Array]> | undefined; + [_stateTransitions]: Array<[StateName, Array]> | undefined; constructor() { this[_states] = new Map(); } - withTransitions(transitions: Array<[string, Array]>) { + withTransitions(transitions: Array<[StateName, Array]>) { this[_stateTransitions] = transitions; return this; } - withStates(names: string[], actions?: Actions) { + withStates(names: StateName[], actions?: Actions) { names.forEach((name) => this.addStateUnchecked(name, actions)); return this; } - withState(name: string, actions?: Actions) { + withState(name: StateName, actions?: Actions) { this.addStateUnchecked(name, actions); return this; } - private addStateUnchecked(name: string, actions?: Actions) { + private addStateUnchecked(name: StateName, actions?: Actions) { const oldActions = this[_states].get(name); return this[_states].set(name, { ...oldActions, ...actions }); } - build(currentStateName: string) { + build(currentStateName: StateName) { const states = this.buildStates(); const transitions = this.buildTransitions(states); const currState = validStateFromName(states, currentStateName); @@ -81,9 +84,9 @@ export class StateMachine { this[_currState] = currentState; } - async changeState(sourceState: string | State, context?: Context) { + async changeState(sourceState: StateOrName, context?: Context) { const fromState = validState(this[_currState]); - const toState = validState(normalizeState(this[_states], sourceState)); + const toState = validNormalizedState(this[_states], sourceState); if ( !this.hasTransition(toState) || @@ -100,11 +103,11 @@ export class StateMachine { this[_prevState] = fromState; } - hasTransition(to: string | State) { + hasTransition(to: StateOrName) { return hasTransition( this[_stateTransitions], this[_currState], - validState(normalizeState(this[_states], to)), + validNormalizedState(this[_states], to), ); } @@ -123,25 +126,25 @@ interface Actions { beforeExit?( fromState: State, toState: State, - context: Context, + context?: Context, ): boolean; onEntry?( fromState: State, toState: State, - context: Context, + context?: Context, ): Promise | void; } export class State { [_stateActions]: Actions; - [_stateName]: string; + [_stateName]: StateName; - get name(): string { + get name(): StateName { return this[_stateName]; } - constructor(name: string, actions: Actions = {}) { + constructor(name: StateName, actions: Actions = {}) { this[_stateName] = name; this[_stateActions] = actions; } @@ -149,7 +152,7 @@ export class State { async entry( fromState: State, toState: State, - context: Context, + context?: Context, ) { const action = this[_stateActions].onEntry; if (isFn(action)) { @@ -171,21 +174,31 @@ export class State { } } -function stateFromName(states: State[], name: string) { - return states.find((state) => state.name === name); -} - -function validStateFromName(states: State[], name: string) { - return validState(stateFromName(states, name)); +function validNormalizedState( + states: State[], + state: StateOrName, +) { + return validState(normalizeState(states, state)); } function normalizeState( states: State[], - state: string | State, + state: StateOrName, ): State | undefined { return isStr(state) ? stateFromName(states, state) : state; } +function validStateFromName( + states: State[], + name: StateName, +) { + return validState(stateFromName(states, name)); +} + +function stateFromName(states: State[], name: StateName) { + return states.find((state) => state.name === name); +} + function validState(val: unknown): State { if (!isState(val)) { throw new TypeError("an instance of State class is expected");