UNPKG

node-red-contrib-finite-statemachine

Version:
1 lines 2.4 kB
const _=require("lodash"),{fromJS:fromJS}=require("immutable"),{Subject:Subject}=require("rxjs"),{share:share}=require("rxjs/operators");class StatemachineError extends Error{constructor(t,s="0"){super(),this.message=t,this.code=s}}function parseTransitionEntry(t){return _.isString(t)?{status:t,data:{}}:_.isObject(t)?{status:t.status,data:t.data||{}}:void 0}class StateMachine{constructor(t){if(this.subject=new Subject,!_.isObject(t.state))throw new StatemachineError("No inital state specified.",1);if(!_.isObject(t.transitions))throw new StatemachineError("No transitions specified.",2);if(!_.isString(t.state.status))throw new StatemachineError("state must contain a status field of type string",3);this._initialState=_.extend({data:{}},t.state),_.values(t.transitions).forEach(t=>{_.values(t).forEach(t=>{if(!_.isString(t)&&!_.has(t,"status"))throw new StatemachineError("Transition table has no status field: "+JSON.stringify(t),4)})}),this._transitions=t.transitions,this._state=fromJS(this._initialState),this.subject.next({state:this._state.toJS()})}triggerAction(t,s){if(!_.isString(t.type))throw new StatemachineError("action must contain a type.",14);let e=this.getCurrentTransitions();if(_.isEmpty(e))throw new StatemachineError("no possible transitions to go to a new state.",11);if(!_.has(e,t.type))throw new StatemachineError("transition not possible from the current state.",12);let a=parseTransitionEntry(e[t.type]),i=Object.assign({},a.data,_.isObject(t.data)?t.data:{});this._state=this._state.set("status",a.status).mergeDeep({data:i}),this.subject.next({state:this._state.toJS(),trigger:s})}getCurrentTransitions(){return _.get(this._transitions,this._state.get("status"))||{}}getAllTransitions(){let t=_.reduce(_.values(this._transitions),(t,s)=>(t.push(..._.keys(s)),t),[]);return _.uniq(t)}reset(){this._state=fromJS(this._initialState),this.subject.next({state:this._state.toJS(),action:"reset"})}queryState(){this.subject.next({state:this._state.toJS()})}getState(){return this._state.toJS()}setState(t){if(!_.has(t,"status"))throw new StatemachineError("the state needs to contain a status field",4);if(!_.has(this._transitions,t.status))throw new StatemachineError("status does not exist in transition table",5);this._state=fromJS(t),this.subject.next({state:this._state.toJS()})}get observable(){return this.subject.pipe(share())}}exports.StateMachine=StateMachine;