Comparing version 3.1.0 to 3.1.1
@@ -1,1 +0,1 @@ | ||
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.xstate=e():t.xstate=e()}(this,function(){return function(t){function e(i){if(n[i])return n[i].exports;var a=n[i]={i:i,l:!1,exports:{}};return t[i].call(a.exports,a,a.exports,e),a.l=!0,a.exports}var n={};return e.m=t,e.c=n,e.d=function(t,n,i){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:i})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=4)}([function(t,e,n){"use strict";function i(t){try{return"string"==typeof t||"number"==typeof t?""+t:t.type}catch(t){throw new Error("Events must be strings or objects with a string event.type property.")}}function a(t){try{return Array.isArray(t)?t:t.toString().split(".")}catch(e){throw new Error("'"+t+"' is not a valid state path.")}}function r(t){if(t instanceof s.State)return t.value;if("object"==typeof t&&!(t instanceof s.State))return t;var e=a(t);if(1===e.length)return e[0];for(var n={},i=n,r=0;r<e.length-1;r++)r===e.length-2?i[e[r]]=e[r+1]:(i[e[r]]={},i=i[e[r]]);return n}function o(t,e){var n={};return Object.keys(t).forEach(function(i){n[i]=e(t[i],i,t)}),n}Object.defineProperty(e,"__esModule",{value:!0});var s=n(2);e.getEventType=i,e.toStatePath=a,e.toTrie=r,e.mapValues=o},function(t,e,n){"use strict";function i(t){return new l(t)}var a=this&&this.__assign||Object.assign||function(t){for(var e,n=1,i=arguments.length;n<i;n++){e=arguments[n];for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&(t[a]=e[a])}return t};Object.defineProperty(e,"__esModule",{value:!0});var r=n(0),o=n(3);e.matchesState=o.default;var s=n(6);e.mapState=s.default;var c=n(2);e.State=c.State;var u=n(7),f=".",l=function(){function t(e){var n=this;this.config=e,this.__cache={events:void 0,relativeValue:new Map,initialState:void 0},this.key=e.key||"(machine)",this.parent=e.parent,this.machine=this.parent?this.parent.machine:this,this.id=this.parent?this.parent.id+f+this.key:this.key,this.relativeId=this.parent&&this.parent.parent?this.parent.relativeId+f+this.key:this.key,this.initial=e.initial,this.parallel=!!e.parallel,this.states=e.states?r.mapValues(e.states,function(e,i){return new t(a({},e,{key:i,parent:n}))}):{},this.on=e.on,this.strict=!!e.strict,this.onEntry=e.onEntry?[].concat(e.onEntry):void 0,this.onExit=e.onExit?[].concat(e.onExit):void 0,this.data=e.data,this.activities=e.activities}return t.prototype.getStateNodes=function(t){var e=this,n=t instanceof c.State?t.value:r.toTrie(t);if("string"==typeof n){var i=this.states[n].initial;return i?this.getStateNodes((o={},o[n]=i,o)):[this.states[n]]}var a=Object.keys(n);return a.map(function(t){return e.states[t]}).concat(a.map(function(t){return e.states[t].getStateNodes(n[t])}).reduce(function(t,e){return t.concat(e)}));var o},t.prototype.handles=function(t){var e=r.getEventType(t);return!!this.on&&e in this.on},t.prototype.transition=function(t,e,n){if(this.strict){var i=r.getEventType(e);if(-1===this.events.indexOf(i))throw new Error("Machine '"+this.id+"' does not accept event '"+i+"'")}var a=this.transitionStateValue(t,e,n),o=this.stateTransitionToState(a,t);if(!o)return c.State.inert(t);var s,u=o;do{s=u,u=this.stateTransitionToState(this.transitionStateValue(s,"",n),s)}while(u);return s},t.prototype.stateTransitionToState=function(t,e){var n=t.stateValue,i=t.actions,r=t.activities;if(n){var o=e instanceof c.State?e.activities:void 0,s=a({},o,r);return new c.State(n,c.State.from(e),i?i.onExit.concat(i.actions).concat(i.onEntry):[],s,this.getStateNodes(n).reduce(function(t,e){return t[e.id]=e.data,t},{}))}},t.prototype.transitionStateValue=function(t,e,n){var i=this,o=t instanceof c.State?t.history:void 0,s=r.toTrie(t);if("string"==typeof s){if(!this.states[s])throw new Error("State '"+s+"' does not exist on machine '"+this.id+"'");var u=this.states[s];if(!u.states||!u.initial)return u.next(e,o?o.value:void 0,n);var f=u.initialState.value;m={},m[s]=f,s=m}var l=[],h=r.mapValues(s,function(t,a){var r=o?o.value[a]:void 0,s=new c.State(t,r?c.State.from(r):void 0),u=i.states[a],f=u.transitionStateValue(s,e,n);return f.stateValue||l.push(u.next(e,o?o.value:void 0,n)),f});if(Array.prototype.every.call(Object.keys(h),function(t){return void 0===h[t].stateValue})){if(this.parallel)return l.length?l[0]:{stateValue:void 0,actions:{onEntry:[],onExit:[],actions:[]},activities:void 0};var v=Object.keys(h)[0],p=this.states[v].next(e,o?o.value:void 0,n),y=p.stateValue,d=p.actions,E=p.activities,g=h[v].actions,S=h[v].activities,b=a({},S,E);return{stateValue:y,actions:d?g?{onEntry:g.onEntry.concat(d.onEntry),actions:g.actions.concat(d.actions),onExit:g.onExit.concat(d.onExit)}:d:g,activities:b}}this.parallel&&(h=a({},r.mapValues(this.initialState.value,function(t){return{stateValue:t,actions:{onEntry:[],onExit:[],actions:[]},activities:void 0}}),h));var x={onEntry:[],actions:[],onExit:[]},_={};return{stateValue:r.mapValues(h,function(t,e){var n=t.stateValue,i=t.actions,a=t.activities;return i&&(i.onEntry&&(r=x.onEntry).push.apply(r,i.onEntry),i.actions&&(o=x.actions).push.apply(o,i.actions),i.onExit&&(c=x.onExit).push.apply(c,i.onExit)),a&&Object.assign(_,a),n||s[e];var r,o,c}),actions:x,activities:_};var m},t.prototype.next=function(t,e,n){var i=this,o=r.getEventType(t),s={onEntry:[],onExit:[],actions:[]},c={};if(this.onExit&&(s.onExit=this.onExit),this.activities&&this.activities.forEach(function(t){c[r.getEventType(t)]=!1,s.onExit=s.onExit.concat(u.stop(t))}),!this.on||!this.on[o])return{stateValue:void 0,actions:s,activities:c};var l,h=this.on[o];if("string"==typeof h)l=h;else for(var v=Array.isArray(h)?h:Object.keys(h).map(function(t){return a({},h[t],{target:t})}),p=0,y=v;p<y.length;p++){var d=y[p],E=d,g=E.cond,S=E.actions,b=n||{},x="string"==typeof t||"number"==typeof t?{type:t}:t;if(!g||g(b,x)){l=d.target,S&&(s.actions=s.actions.concat(S));break}}if(!l)return{stateValue:void 0,actions:s,activities:c};var _=r.toStatePath(l),m=this.parent,T=e,V=this.key;if(_.forEach(function(e){if(!m||!m.states)throw new Error("Unable to read '"+e+"'");if("$history"===e)if(T)e="object"==typeof T?Object.keys(T)[0]:T;else{if(!m.initial)throw new Error("Cannot read '$history' from state '"+m.id+"': missing 'initial'");e=m.initial}else if(""===e)return s.onExit=[],void(m=m.states[i.key]);if(void 0===(m=m.states[e]))throw new Error("Event '"+t+"' on state '"+V+"' leads to undefined state '"+_.join(f)+"'.");m.onEntry&&(s.onEntry=s.onEntry.concat(m.onEntry)),m.activities&&m.activities.forEach(function(t){c[r.getEventType(t)]=!0,s.onEntry=s.onEntry.concat(u.start(t))}),V=e,T&&(T=T[e])}),!m)throw new Error("no state");for(;m.initial;){if(!m||!m.states)throw new Error("Invalid initial state");m=m.states[m.initial],m.onEntry&&(s.onEntry=s.onEntry.concat(m.onEntry)),m.activities&&m.activities.forEach(function(t){c[r.getEventType(t)]=!0,s.onEntry=s.onEntry.concat(u.start(t))})}return{stateValue:m.getRelativeValue(this.parent),actions:s,activities:c}},Object.defineProperty(t.prototype,"resolvedStateValue",{get:function(){var t=this.key;return this.parallel?(e={},e[t]=r.mapValues(this.states,function(t){return t.resolvedStateValue[t.key]}),e):this.initial?(n={},n[t]=this.states[this.initial].resolvedStateValue,n):t;var e,n},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"initialStateValue",{get:function(){var t=this.__cache.initialState||(this.parallel?r.mapValues(this.states,function(t){return t.initialStateValue}):this.resolvedStateValue[this.key]);return this.__cache.initialState=t,this.__cache.initialState},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"initialState",{get:function(){var t=this.initialStateValue;if(!t)throw new Error("Cannot retrieve initial state from simple state '"+this.id+".'");var e=this.getStateNodes(t).reduce(function(t,e){return e.onEntry?t.concat(e.onEntry):t},[]);return new c.State(t,void 0,e)},enumerable:!0,configurable:!0}),t.prototype.getStates=function(t){var e=this;if("string"==typeof t)return[this.states[t]];var n=[];return Object.keys(t).forEach(function(i){n.push.apply(n,e.states[i].getStates(t[i]))}),n},t.prototype.getState=function(t){var e=r.toStatePath(t);try{return e.reduce(function(t,e){if(!t.states)throw new Error("Cannot retrieve subPath '"+e+"' from node with no states");return t.states[e]},this)}catch(e){throw new Error("State '"+t+" does not exist on machine '"+this.id+"'")}},Object.defineProperty(t.prototype,"events",{get:function(){if(this.__cache.events)return this.__cache.events;var t=this.states,e=new Set(this.on?Object.keys(this.on):void 0);return t&&Object.keys(t).forEach(function(n){var i=t[n];if(i.states)for(var a=0,r=i.events;a<r.length;a++){var o=r[a];e.add(""+o)}}),this.__cache.events=Array.from(e)},enumerable:!0,configurable:!0}),t.prototype.getRelativeValue=function(t){var e=t?this.__cache.relativeValue.get(t):void 0;if(e)return e;for(var n=this.initialStateValue,i=n?(s={},s[this.key]=n,s):this.key,r=this.parent;r&&r!==t;){var o=r.initialStateValue;c={},c[r.key]=r.parallel&&"object"==typeof o&&"object"==typeof i?a({},o,i):i,i=c,r=r.parent}return this.__cache.relativeValue.set(t,i),i;var s,c},t}();e.StateNode=l,e.Machine=i},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var i=n(5),a=n(0),r=function(){function t(t,e,n,a,r){void 0===n&&(n=[]),void 0===a&&(a=i.EMPTY_ACTIVITY_MAP),void 0===r&&(r={}),this.value=t,this.history=e,this.actions=n,this.activities=a,this.data=r}return t.from=function(e){return e instanceof t?e:new t(a.toTrie(e))},t.inert=function(e){return e instanceof t?e.actions.length?new t(e.value,e.history,[]):e:t.from(e)},t.prototype.toString=function(){if("string"==typeof this.value)return this.value;for(var t=[],e=this.value;;){if("string"==typeof e){t.push(e);break}var n=Object.keys(e),a=n[0];if(n.slice(1).length)return;t.push(a),e=e[a]}return t.join(i.STATE_DELIMITER)},t}();e.State=r},function(t,e,n){"use strict";function i(t,e){var n=a.toTrie(t),r=a.toTrie(e);return"string"==typeof r?"string"==typeof n?r===n:r in n:"string"==typeof n?n in r:Object.keys(n).every(function(t){return t in r&&i(n[t],r[t])})}Object.defineProperty(e,"__esModule",{value:!0});var a=n(0);e.default=i},function(t,e,n){n(1),t.exports=n(1)},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.STATE_DELIMITER=".",e.EMPTY_ACTIVITY_MAP={}},function(t,e,n){"use strict";function i(t,e){var n;return Object.keys(t).forEach(function(t){a.default(t,e)&&(!n||e.length>n.length)&&(n=t)}),t[n]}Object.defineProperty(e,"__esModule",{value:!0});var a=n(3);e.default=i},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var i=n(0);e.actionTypes={start:"xstate.start",stop:"xstate.stop",raise:"xstate.raise",send:"xstate.send",cancel:"xstate.cancel"};var a=function(t){return function(e){var n="string"==typeof e||"number"==typeof e?{type:e}:e;return{type:t,activity:i.getEventType(e),data:n}}},r=function(t){return"string"==typeof t||"number"==typeof t?{type:t}:t};e.raise=function(t){return{type:e.actionTypes.raise,event:t}},e.send=function(t,n){return{type:e.actionTypes.send,event:r(t),delay:n?n.delay:void 0,id:n&&void 0!==n.id?n.id:i.getEventType(t)}},e.cancel=function(t){return{type:e.actionTypes.cancel,sendId:t}},e.start=a(e.actionTypes.start),e.stop=a(e.actionTypes.stop)}])}); | ||
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.xstate=e():t.xstate=e()}(this,function(){return function(t){function e(i){if(n[i])return n[i].exports;var a=n[i]={i:i,l:!1,exports:{}};return t[i].call(a.exports,a,a.exports,e),a.l=!0,a.exports}var n={};return e.m=t,e.c=n,e.d=function(t,n,i){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:i})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=6)}([function(t,e,n){"use strict";function i(t){try{return"string"==typeof t||"number"==typeof t?""+t:t.type}catch(t){throw new Error("Events must be strings or objects with a string event.type property.")}}function a(t){try{return Array.isArray(t)?t:t.toString().split(".")}catch(e){throw new Error("'"+t+"' is not a valid state path.")}}function r(t){if(t instanceof s.State)return t.value;if("object"==typeof t&&!(t instanceof s.State))return t;var e=a(t);if(1===e.length)return e[0];for(var n={},i=n,r=0;r<e.length-1;r++)r===e.length-2?i[e[r]]=e[r+1]:(i[e[r]]={},i=i[e[r]]);return n}function o(t,e){var n={};return Object.keys(t).forEach(function(i){n[i]=e(t[i],i,t)}),n}Object.defineProperty(e,"__esModule",{value:!0});var s=n(2);e.getEventType=i,e.toStatePath=a,e.toStateValue=r,e.mapValues=o,e.path=function(t){return function(e){for(var n=e,i=0,a=t;i<a.length;i++){n=n[a[i]]}return n}}},function(t,e,n){"use strict";function i(t,e){var n=a.toStateValue(t),r=a.toStateValue(e);return"string"==typeof r?"string"==typeof n?r===n:r in n:"string"==typeof n?n in r:Object.keys(n).every(function(t){return t in r&&i(n[t],r[t])})}Object.defineProperty(e,"__esModule",{value:!0});var a=n(0);e.matchesState=i},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var i=n(7),a=n(0),r=function(){function t(t,e,n,a,r){void 0===n&&(n=[]),void 0===a&&(a=i.EMPTY_ACTIVITY_MAP),void 0===r&&(r={}),this.value=t,this.history=e,this.actions=n,this.activities=a,this.data=r}return t.from=function(e){return e instanceof t?e:new t(a.toStateValue(e))},t.inert=function(e){return e instanceof t?e.actions.length?new t(e.value,e.history,[]):e:t.from(e)},t.prototype.toString=function(){if("string"==typeof this.value)return this.value;for(var t=[],e=this.value;;){if("string"==typeof e){t.push(e);break}var n=Object.keys(e),a=n[0];if(n.slice(1).length)return;t.push(a),e=e[a]}return t.join(i.STATE_DELIMITER)},t}();e.State=r},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var i=n(1);e.matchesState=i.matchesState;var a=n(8);e.mapState=a.mapState;var r=n(4);e.StateNode=r.StateNode;var o=n(2);e.State=o.State;var s=n(9);e.Machine=s.Machine;var c=n(5);e.actions=c},function(t,e,n){"use strict";function i(t){return new f(t)}var a=this&&this.__assign||Object.assign||function(t){for(var e,n=1,i=arguments.length;n<i;n++){e=arguments[n];for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&(t[a]=e[a])}return t};Object.defineProperty(e,"__esModule",{value:!0});var r=n(0),o=n(1),s=n(2),c=n(5),u=".",f=function(){function t(e){var n=this;this.config=e,this.__cache={events:void 0,relativeValue:new Map,initialState:void 0},this.key=e.key||"(machine)",this.parent=e.parent,this.machine=this.parent?this.parent.machine:this,this.id=this.parent?this.parent.path.concat(this.key).join(u):this.key,this.path=this.parent?this.parent.path.concat(this.key):[this.key],this.initial=e.initial,this.parallel=!!e.parallel,this.states=e.states?r.mapValues(e.states,function(e,i){return new t(a({},e,{key:i,parent:n}))}):{},this.on=e.on,this.strict=!!e.strict,this.onEntry=e.onEntry?[].concat(e.onEntry):void 0,this.onExit=e.onExit?[].concat(e.onExit):void 0,this.data=e.data,this.activities=e.activities}return t.prototype.getStateNodes=function(t){var e=this,n=t instanceof s.State?t.value:r.toStateValue(t);if("string"==typeof n){var i=this.states[n].initial;return i?this.getStateNodes((o={},o[n]=i,o)):[this.states[n]]}var a=Object.keys(n);return a.map(function(t){return e.states[t]}).concat(a.map(function(t){return e.states[t].getStateNodes(n[t])}).reduce(function(t,e){return t.concat(e)}));var o},t.prototype.handles=function(t){var e=r.getEventType(t);return-1!==this.events.indexOf(e)},t.prototype.transition=function(t,e,n){if(this.strict){var i=r.getEventType(e);if(-1===this.events.indexOf(i))throw new Error("Machine '"+this.id+"' does not accept event '"+i+"'")}var a=s.State.from(t),o=this.transitionStateValue(a,e,a,n),c=this.stateTransitionToState(o,a);if(!c)return s.State.inert(a);var u,f=c,h=[];do{h.push.apply(h,f.actions),u=f,f=this.stateTransitionToState(this.transitionStateValue(u,"",f,n),u)}while(f);return u.actions=h,u},t.prototype.stateTransitionToState=function(t,e){var n=t.stateValue,i=t.actions,r=t.activities;if(n){var o=e instanceof s.State?e.activities:void 0,c=a({},o,r);return new s.State(n,s.State.from(e),i?i.onExit.concat(i.actions).concat(i.onEntry):[],c,this.getStateNodes(n).reduce(function(t,e){return void 0!==e.data&&(t[e.id]=e.data),t},{}))}},t.prototype.transitionStateValue=function(t,e,n,i){var o=this,c=t.history,u=t.value;if("string"==typeof u){if(!this.states[u])throw new Error("State '"+u+"' does not exist on machine '"+this.id+"'");var f=this.states[u];if("string"==typeof(u=f.resolvedStateValue))return f.next(e,n,c?c.value:void 0,i)}var h=[],l=r.mapValues(u,function(t,a){var r=c?c.value[a]:void 0,u=new s.State(t,r?s.State.from(r):void 0),f=o.states[a],l=f.transitionStateValue(u,e,n,i);return l.stateValue||h.push(f.next(e,n,c?c.value:void 0,i)),l});if(Array.prototype.every.call(Object.keys(l),function(t){return void 0===l[t].stateValue})){if(this.parallel)return h.length?h[0]:{stateValue:void 0,actions:{onEntry:[],onExit:[],actions:[]},activities:void 0};var v=Object.keys(l)[0],p=this.states[v].next(e,n,c?c.value:void 0,i),y=p.stateValue,d=p.actions,E=p.activities,S=l[v].actions,g=l[v].activities,b=a({},g,E);return{stateValue:y,actions:d?S?{onEntry:S.onEntry.concat(d.onEntry),actions:S.actions.concat(d.actions),onExit:S.onExit.concat(d.onExit)}:d:S,activities:b}}this.parallel&&(l=a({},r.mapValues(this.initialState.value,function(t){return{stateValue:t,actions:{onEntry:[],onExit:[],actions:[]},activities:void 0}}),l));var m={onEntry:[],actions:[],onExit:[]},V={};return{stateValue:r.mapValues(l,function(t,e){var n=t.stateValue,i=t.actions,a=t.activities;return i&&(i.onEntry&&(r=m.onEntry).push.apply(r,i.onEntry),i.actions&&(o=m.actions).push.apply(o,i.actions),i.onExit&&(s=m.onExit).push.apply(s,i.onExit)),a&&Object.assign(V,a),n||u[e];var r,o,s}),actions:m,activities:V}},t.prototype.next=function(t,e,n,i){var s=this,f=r.getEventType(t),h={onEntry:[],onExit:[],actions:[]},l={};if(this.onExit&&(h.onExit=this.onExit),this.activities&&this.activities.forEach(function(t){l[r.getEventType(t)]=!1,h.onExit=h.onExit.concat(c.stop(t))}),!this.on||!this.on[f])return{stateValue:void 0,actions:h,activities:l};var v,p=this.on[f];if("string"==typeof p)v=p;else for(var y=Array.isArray(p)?p:Object.keys(p).map(function(t){return a({},p[t],{target:t})}),d=0,E=y;d<E.length;d++){var S=E[d],g=S,b=g.cond,m=g.in,V=g.actions,_=i||{},j=c.toEventObject(t),x=!m||o.matchesState(r.toStateValue(m),r.path(this.path.slice(1,-2))(e.value));if((!b||b(_,j))&&(!m||x)){v=S.target,V&&(h.actions=h.actions.concat(V));break}}if(!v)return{stateValue:void 0,actions:h,activities:l};var w=r.toStatePath(v),O=this.parent,T=n,k=this.key;if(w.forEach(function(e){if(!O||!O.states)throw new Error("Unable to read '"+e+"'");if("$history"===e)if(0==Object.keys(O.states).length)e="";else if(T)e="object"==typeof T?Object.keys(T)[0]:T;else{if(!O.initial)throw new Error("Cannot read '$history' from state '"+O.id+"': missing 'initial'");e=O.initial}else if(""===e)return h.onExit=[],void(O=O.states[s.key]);if(""!==e&&(O=O.states[e]),void 0===O)throw new Error("Event '"+t+"' on state '"+k+"' leads to undefined state '"+w.join(u)+"'.");O.onEntry&&(h.onEntry=h.onEntry.concat(O.onEntry)),O.activities&&O.activities.forEach(function(t){l[r.getEventType(t)]=!0,h.onEntry=h.onEntry.concat(c.start(t))}),k=e,T&&(T=T[e])}),!O)throw new Error("no state");for(;O.initial;){if(!O||!O.states)throw new Error("Invalid initial state");O=O.states[O.initial],O.onEntry&&(h.onEntry=h.onEntry.concat(O.onEntry)),O.activities&&O.activities.forEach(function(t){l[r.getEventType(t)]=!0,h.onEntry=h.onEntry.concat(c.start(t))})}return{stateValue:O.getRelativeValue(this.parent),actions:h,activities:l}},Object.defineProperty(t.prototype,"resolvedStateValue",{get:function(){var t=this.key;return this.parallel?(e={},e[t]=r.mapValues(this.states,function(t){return t.resolvedStateValue[t.key]}),e):this.initial?(n={},n[t]=this.states[this.initial].resolvedStateValue,n):t;var e,n},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"initialStateValue",{get:function(){var t=this.__cache.initialState||(this.parallel?r.mapValues(this.states,function(t){return t.initialStateValue}):"string"==typeof this.resolvedStateValue?void 0:this.resolvedStateValue[this.key]);return this.__cache.initialState=t,this.__cache.initialState},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"initialState",{get:function(){var t=this.initialStateValue;if(!t)throw new Error("Cannot retrieve initial state from simple state '"+this.id+".'");var e=this.getStateNodes(t).reduce(function(t,e){return e.onEntry?t.concat(e.onEntry):t},[]);return new s.State(t,void 0,e)},enumerable:!0,configurable:!0}),t.prototype.getStates=function(t){var e=this;if("string"==typeof t)return[this.states[t]];var n=[];return Object.keys(t).forEach(function(i){n.push.apply(n,e.states[i].getStates(t[i]))}),n},t.prototype.getState=function(t){var e=r.toStatePath(t);try{return e.reduce(function(t,e){if(!t.states)throw new Error("Cannot retrieve subPath '"+e+"' from node with no states");return t.states[e]},this)}catch(e){throw new Error("State '"+t+" does not exist on machine '"+this.id+"'")}},Object.defineProperty(t.prototype,"events",{get:function(){if(this.__cache.events)return this.__cache.events;var t=this.states,e=new Set(this.on?Object.keys(this.on):void 0);return t&&Object.keys(t).forEach(function(n){var i=t[n];if(i.states)for(var a=0,r=i.events;a<r.length;a++){var o=r[a];e.add(""+o)}}),this.__cache.events=Array.from(e)},enumerable:!0,configurable:!0}),t.prototype.getRelativeValue=function(t){var e=t?this.__cache.relativeValue.get(t):void 0;if(e)return e;for(var n=this.initialStateValue,i=n?(s={},s[this.key]=n,s):this.key,r=this.parent;r&&r!==t;){var o=r.initialStateValue;c={},c[r.key]=r.parallel&&"object"==typeof o&&"object"==typeof i?a({},o,i):i,i=c,r=r.parent}return this.__cache.relativeValue.set(t,i),i;var s,c},t}();e.StateNode=f,e.Machine=i},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var i=n(0);e.actionTypes={start:"xstate.start",stop:"xstate.stop",raise:"xstate.raise",send:"xstate.send",cancel:"xstate.cancel"};var a=function(t){return function(e){var n="string"==typeof e||"number"==typeof e?{type:e}:e;return{type:t,activity:i.getEventType(e),data:n}}};e.toEventObject=function(t){return"string"==typeof t||"number"==typeof t?{type:t}:t},e.raise=function(t){return{type:e.actionTypes.raise,event:t}},e.send=function(t,n){return{type:e.actionTypes.send,event:e.toEventObject(t),delay:n?n.delay:void 0,id:n&&void 0!==n.id?n.id:i.getEventType(t)}},e.cancel=function(t){return{type:e.actionTypes.cancel,sendId:t}},e.start=a(e.actionTypes.start),e.stop=a(e.actionTypes.stop)},function(t,e,n){n(3),t.exports=n(3)},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.STATE_DELIMITER=".",e.EMPTY_ACTIVITY_MAP={}},function(t,e,n){"use strict";function i(t,e){var n;return Object.keys(t).forEach(function(t){a.matchesState(t,e)&&(!n||e.length>n.length)&&(n=t)}),t[n]}Object.defineProperty(e,"__esModule",{value:!0});var a=n(1);e.mapState=i},function(t,e,n){"use strict";function i(t){return new a.StateNode(t)}Object.defineProperty(e,"__esModule",{value:!0});var a=n(4);e.Machine=i}])}); |
@@ -1,1 +0,1 @@ | ||
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.xstateUtils=e():t.xstateUtils=e()}(this,function(){return function(t){function e(n){if(r[n])return r[n].exports;var a=r[n]={i:n,l:!1,exports:{}};return t[n].call(a.exports,a,a.exports,e),a.l=!0,a.exports}var r={};return e.m=t,e.c=r,e.d=function(t,r,n){e.o(t,r)||Object.defineProperty(t,r,{configurable:!1,enumerable:!0,get:n})},e.n=function(t){var r=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(r,"a",r),r},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=2)}([function(t,e,r){"use strict";function n(t){var e=t.states;return Object.keys(e).reduce(function(t,r){var a=e[r],i=n(e[r]);return t.push.apply(t,[a].concat(i)),t},[])}function a(t){return"string"==typeof t?[{target:t}]:Array.isArray(t)?t:Object.keys(t).map(function(e){return p({target:e},t[e])})}function i(t,e){if(void 0===e&&(e={}),t.parallel)return Object.keys(t.states).map(function(e){return i(t.states[e])}).reduce(function(t,e){return t.concat(e)},[]);var r=t.states;e[t.key]=!0;var n=Object.keys(r).reduce(function(t,n){if(e[n])return t;var a=r[n];return t.push.apply(t,i(a,e)),e[n]=!0,t},[]);if(!t.on)return n;var o=Object.keys(t.on).reduce(function(r,n){if(!t.on||!t.parent)return r;var o=t.parent,s=t.on[n];return s?(a(s).forEach(function(a){var s=a.target,u=a.cond,c=o.getState(s),f={event:n,source:t,target:c,cond:u};r.push(f),e[s]||(r.push.apply(r,i(c,e)),e[s]=!0)}),r):r},[]);return n.concat(o)}function o(t){function e(a){var i=JSON.stringify(a);if(!r[i]){r[i]={};for(var o=0,s=n;o<s.length;o++){var u=s[o],c=t.transition(a,u);r[i][u]={state:c.value},e(c.value)}}}var r={},n=t.events;return e(t.initialState.value),r}function s(t){function e(t){var n=JSON.stringify(t);i.add(n);for(var o=r[n],s=0,u=Object.keys(o);s<u.length;s++){var c=u[s],f=o[c].state;if(f){var p=JSON.stringify(v.toTrie(f));(!a[p]||a[p].length>a[n].length+1)&&(a[p]=(a[n]||[]).concat([{state:t,event:c}]))}}for(var l=0,y=Object.keys(o);l<y.length;l++){var h=y[l],f=o[h].state;if(f){var p=JSON.stringify(f);i.has(p)||e(f)}}return a}if(!t.states)return l;var r=o(t),n=JSON.stringify(t.initialState.value),a=(s={},s[n]=[],s),i=new Set;return e(t.initialState.value),a;var s}function u(t){var e=s(t);return Object.keys(e).map(function(t){return{state:JSON.parse(t),path:e[t]}})}function c(t){function e(t,o){if(n.add(t),t===o)i[o]=i[o]||[],i[o].push(a.slice());else for(var s=0,u=Object.keys(r[t]);s<u.length;s++){var c=u[s],f=r[t][c].state;if(f){var p=JSON.stringify(f);n.has(p)||(a.push({state:JSON.parse(t),event:c}),e(p,o))}}a.pop(),n.delete(t)}if(!t.states)return l;var r=o(t),n=new Set,a=[],i={},s=JSON.stringify(t.initialState.value);return Object.keys(r).forEach(function(t){e(s,t)}),i}function f(t){var e=c(t);return Object.keys(e).map(function(t){return{state:JSON.parse(t),paths:e[t]}})}var p=this&&this.__assign||Object.assign||function(t){for(var e,r=1,n=arguments.length;r<n;r++){e=arguments[r];for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&(t[a]=e[a])}return t};Object.defineProperty(e,"__esModule",{value:!0});var v=r(1),l={};e.getNodes=n,e.getEdges=i,e.getAdjacencyMap=o,e.getShortestPaths=s,e.getShortestPathsAsArray=u,e.getSimplePaths=c,e.getSimplePathsAsArray=f},function(t,e,r){"use strict";function n(t){try{return"string"==typeof t||"number"==typeof t?""+t:t.type}catch(t){throw new Error("Events must be strings or objects with a string event.type property.")}}function a(t){try{return Array.isArray(t)?t:t.toString().split(".")}catch(e){throw new Error("'"+t+"' is not a valid state path.")}}function i(t){if(t instanceof s.State)return t.value;if("object"==typeof t&&!(t instanceof s.State))return t;var e=a(t);if(1===e.length)return e[0];for(var r={},n=r,i=0;i<e.length-1;i++)i===e.length-2?n[e[i]]=e[i+1]:(n[e[i]]={},n=n[e[i]]);return r}function o(t,e){var r={};return Object.keys(t).forEach(function(n){r[n]=e(t[n],n,t)}),r}Object.defineProperty(e,"__esModule",{value:!0});var s=r(3);e.getEventType=n,e.toStatePath=a,e.toTrie=i,e.mapValues=o},function(t,e,r){r(0),t.exports=r(0)},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(4),a=r(1),i=function(){function t(t,e,r,a,i){void 0===r&&(r=[]),void 0===a&&(a=n.EMPTY_ACTIVITY_MAP),void 0===i&&(i={}),this.value=t,this.history=e,this.actions=r,this.activities=a,this.data=i}return t.from=function(e){return e instanceof t?e:new t(a.toTrie(e))},t.inert=function(e){return e instanceof t?e.actions.length?new t(e.value,e.history,[]):e:t.from(e)},t.prototype.toString=function(){if("string"==typeof this.value)return this.value;for(var t=[],e=this.value;;){if("string"==typeof e){t.push(e);break}var r=Object.keys(e),a=r[0];if(r.slice(1).length)return;t.push(a),e=e[a]}return t.join(n.STATE_DELIMITER)},t}();e.State=i},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.STATE_DELIMITER=".",e.EMPTY_ACTIVITY_MAP={}}])}); | ||
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.xstateUtils=e():t.xstateUtils=e()}(this,function(){return function(t){function e(r){if(n[r])return n[r].exports;var a=n[r]={i:r,l:!1,exports:{}};return t[r].call(a.exports,a,a.exports,e),a.l=!0,a.exports}var n={};return e.m=t,e.c=n,e.d=function(t,n,r){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:r})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=2)}([function(t,e,n){"use strict";function r(t){var e=t.states;return Object.keys(e).reduce(function(t,n){var a=e[n],i=r(e[n]);return t.push.apply(t,[a].concat(i)),t},[])}function a(t,e){var n=t.on[e];return"string"==typeof n?[{source:t,target:t.parent.getState(n),event:e,actions:[]}]:Array.isArray(n)?n.map(function(n){return{source:t,target:t.parent.getState(n.target),event:e,actions:n.actions||[],cond:n.cond}}):Object.keys(n).map(function(r){return{source:t,target:t.parent.getState(r),event:e,actions:n[r].actions||[],cond:n[r].cond}})}function i(t){var e=[];return t.states&&Object.keys(t.states).forEach(function(n){e.push.apply(e,i(t.states[n]))}),t.on&&Object.keys(t.on).forEach(function(n){e.push.apply(e,a(t,n))}),e}function o(t){function e(a){var i=JSON.stringify(a);if(!n[i]){n[i]={};for(var o=0,s=r;o<s.length;o++){var u=s[o],c=t.transition(a,u);n[i][u]={state:c.value},e(c.value)}}}var n={},r=t.events;return e(t.initialState.value),n}function s(t){function e(t){var r=JSON.stringify(t);i.add(r);for(var o=n[r],s=0,u=Object.keys(o);s<u.length;s++){var c=u[s],f=o[c].state;if(f){var l=JSON.stringify(p.toStateValue(f));(!a[l]||a[l].length>a[r].length+1)&&(a[l]=(a[r]||[]).concat([{state:t,event:c}]))}}for(var v=0,y=Object.keys(o);v<y.length;v++){var h=y[v],f=o[h].state;if(f){var l=JSON.stringify(f);i.has(l)||e(f)}}return a}if(!t.states)return l;var n=o(t),r=JSON.stringify(t.initialState.value),a=(s={},s[r]=[],s),i=new Set;return e(t.initialState.value),a;var s}function u(t){var e=s(t);return Object.keys(e).map(function(t){return{state:JSON.parse(t),path:e[t]}})}function c(t){function e(t,o){if(r.add(t),t===o)i[o]=i[o]||[],i[o].push(a.slice());else for(var s=0,u=Object.keys(n[t]);s<u.length;s++){var c=u[s],f=n[t][c].state;if(f){var p=JSON.stringify(f);r.has(p)||(a.push({state:JSON.parse(t),event:c}),e(p,o))}}a.pop(),r.delete(t)}if(!t.states)return l;var n=o(t),r=new Set,a=[],i={},s=JSON.stringify(t.initialState.value);return Object.keys(n).forEach(function(t){e(s,t)}),i}function f(t){var e=c(t);return Object.keys(e).map(function(t){return{state:JSON.parse(t),paths:e[t]}})}Object.defineProperty(e,"__esModule",{value:!0});var p=n(1),l={};e.getNodes=r,e.getEdges=i,e.getAdjacencyMap=o,e.getShortestPaths=s,e.getShortestPathsAsArray=u,e.getSimplePaths=c,e.getSimplePathsAsArray=f},function(t,e,n){"use strict";function r(t){try{return"string"==typeof t||"number"==typeof t?""+t:t.type}catch(t){throw new Error("Events must be strings or objects with a string event.type property.")}}function a(t){try{return Array.isArray(t)?t:t.toString().split(".")}catch(e){throw new Error("'"+t+"' is not a valid state path.")}}function i(t){if(t instanceof s.State)return t.value;if("object"==typeof t&&!(t instanceof s.State))return t;var e=a(t);if(1===e.length)return e[0];for(var n={},r=n,i=0;i<e.length-1;i++)i===e.length-2?r[e[i]]=e[i+1]:(r[e[i]]={},r=r[e[i]]);return n}function o(t,e){var n={};return Object.keys(t).forEach(function(r){n[r]=e(t[r],r,t)}),n}Object.defineProperty(e,"__esModule",{value:!0});var s=n(3);e.getEventType=r,e.toStatePath=a,e.toStateValue=i,e.mapValues=o,e.path=function(t){return function(e){for(var n=e,r=0,a=t;r<a.length;r++){n=n[a[r]]}return n}}},function(t,e,n){n(0),t.exports=n(0)},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(4),a=n(1),i=function(){function t(t,e,n,a,i){void 0===n&&(n=[]),void 0===a&&(a=r.EMPTY_ACTIVITY_MAP),void 0===i&&(i={}),this.value=t,this.history=e,this.actions=n,this.activities=a,this.data=i}return t.from=function(e){return e instanceof t?e:new t(a.toStateValue(e))},t.inert=function(e){return e instanceof t?e.actions.length?new t(e.value,e.history,[]):e:t.from(e)},t.prototype.toString=function(){if("string"==typeof this.value)return this.value;for(var t=[],e=this.value;;){if("string"==typeof e){t.push(e);break}var n=Object.keys(e),a=n[0];if(n.slice(1).length)return;t.push(a),e=e[a]}return t.join(r.STATE_DELIMITER)},t}();e.State=i},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.STATE_DELIMITER=".",e.EMPTY_ACTIVITY_MAP={}}])}); |
@@ -1,2 +0,2 @@ | ||
import { ActionObject, Action, ActionType, Event, EventObject } from './types'; | ||
import { Action, Event, EventObject, ActivityAction, SendAction, SendActionOptions, CancelAction } from './types'; | ||
export declare const actionTypes: { | ||
@@ -9,24 +9,7 @@ start: string; | ||
}; | ||
export interface ActivityAction extends ActionObject { | ||
activity: ActionType; | ||
data: { | ||
type: ActionType; | ||
[key: string]: any; | ||
}; | ||
} | ||
export declare const toEventObject: (event: Event) => EventObject; | ||
export declare const raise: (eventType: string | number) => EventObject; | ||
export interface SendAction extends ActionObject { | ||
event: EventObject; | ||
delay?: number; | ||
} | ||
export interface SendActionOptions { | ||
delay?: number; | ||
id?: string | number; | ||
} | ||
export declare const send: (event: Event, options?: SendActionOptions | undefined) => SendAction; | ||
export interface CancelAction extends ActionObject { | ||
sendId: string | number; | ||
} | ||
export declare const cancel: (sendId: string | number) => CancelAction; | ||
export declare const start: (activity: Action) => ActivityAction; | ||
export declare const stop: (activity: Action) => ActivityAction; |
@@ -23,3 +23,3 @@ "use strict"; | ||
}; }; | ||
var toEventObject = function (event) { | ||
exports.toEventObject = function (event) { | ||
if (typeof event === 'string' || typeof event === 'number') { | ||
@@ -37,3 +37,3 @@ return { type: event }; | ||
type: exports.actionTypes.send, | ||
event: toEventObject(event), | ||
event: exports.toEventObject(event), | ||
delay: options ? options.delay : undefined, | ||
@@ -40,0 +40,0 @@ id: options && options.id !== undefined ? options.id : utils_1.getEventType(event) |
import { StateNode } from './index'; | ||
import { StateValue, Machine, Event, Condition, Action } from './types'; | ||
export interface Edge { | ||
event: string; | ||
source: StateNode; | ||
target: StateNode; | ||
cond?: Condition; | ||
actions: Action[]; | ||
} | ||
export interface INodesAndEdges { | ||
nodes: StateNode[]; | ||
edges: Edge[]; | ||
} | ||
import { Machine, Edge, PathMap, PathItem, PathsItem, PathsMap, AdjacencyMap } from './types'; | ||
export declare function getNodes(node: StateNode): StateNode[]; | ||
export declare function getEdges(node: StateNode): Edge[]; | ||
export interface Segment { | ||
state: StateValue; | ||
event: Event; | ||
} | ||
export interface IPathMap { | ||
[key: string]: Segment[]; | ||
} | ||
export interface IPathItem { | ||
state: StateValue; | ||
path: Segment[]; | ||
} | ||
export interface IPathsItem { | ||
state: StateValue; | ||
paths: Segment[][]; | ||
} | ||
export interface IPathsMap { | ||
[key: string]: Segment[][]; | ||
} | ||
export interface ITransitionMap { | ||
state: StateValue | undefined; | ||
} | ||
export interface IAdjacencyMap { | ||
[stateId: string]: Record<string, ITransitionMap>; | ||
} | ||
export declare function getAdjacencyMap(node: Machine): IAdjacencyMap; | ||
export declare function getShortestPaths(machine: Machine): IPathMap; | ||
export declare function getShortestPathsAsArray(machine: Machine): IPathItem[]; | ||
export declare function getSimplePaths(machine: Machine): IPathsMap; | ||
export declare function getSimplePathsAsArray(machine: Machine): IPathsItem[]; | ||
export declare function getAdjacencyMap(node: Machine): AdjacencyMap; | ||
export declare function getShortestPaths(machine: Machine): PathMap; | ||
export declare function getShortestPathsAsArray(machine: Machine): PathItem[]; | ||
export declare function getSimplePaths(machine: Machine): PathsMap; | ||
export declare function getSimplePathsAsArray(machine: Machine): PathsItem[]; |
@@ -104,3 +104,3 @@ "use strict"; | ||
} | ||
var nextStateId = JSON.stringify(utils_1.toTrie(nextStateValue)); | ||
var nextStateId = JSON.stringify(utils_1.toStateValue(nextStateValue)); | ||
if (!pathMap[nextStateId] || | ||
@@ -107,0 +107,0 @@ pathMap[nextStateId].length > pathMap[stateId].length + 1) { |
@@ -1,43 +0,7 @@ | ||
import { Event, StateValue, Transition, Action, StandardMachine, ParallelMachine, SimpleOrCompoundStateNodeConfig, MachineConfig, ParallelMachineConfig, EventType, StandardMachineConfig, StateNodeConfig, Activity } from './types'; | ||
import matchesState from './matchesState'; | ||
import mapState from './mapState'; | ||
import { matchesState } from './matchesState'; | ||
import { mapState } from './mapState'; | ||
import { StateNode } from './StateNode'; | ||
import { State } from './State'; | ||
declare class StateNode implements StateNodeConfig { | ||
config: SimpleOrCompoundStateNodeConfig | StandardMachineConfig | ParallelMachineConfig; | ||
key: string; | ||
id: string; | ||
relativeId: string; | ||
initial?: string; | ||
parallel?: boolean; | ||
states: Record<string, StateNode>; | ||
on?: Record<string, Transition | undefined>; | ||
onEntry?: Action[]; | ||
onExit?: Action[]; | ||
activities?: Activity[]; | ||
strict: boolean; | ||
parent?: StateNode; | ||
machine: StateNode; | ||
data: object | undefined; | ||
private __cache; | ||
constructor(config: SimpleOrCompoundStateNodeConfig | StandardMachineConfig | ParallelMachineConfig); | ||
getStateNodes(state: StateValue | State): StateNode[]; | ||
handles(event: Event): boolean; | ||
transition(state: StateValue | State, event: Event, extendedState?: any): State; | ||
private stateTransitionToState(stateTransition, prevState); | ||
private transitionStateValue(state, event, extendedState?); | ||
private next(event, history?, extendedState?); | ||
private readonly resolvedStateValue; | ||
private readonly initialStateValue; | ||
readonly initialState: State; | ||
getStates(stateValue: StateValue): StateNode[]; | ||
getState(relativeStateId: string | string[]): StateNode | undefined; | ||
readonly events: EventType[]; | ||
private getRelativeValue(toNode?); | ||
} | ||
export interface MachineFactory { | ||
(config: MachineConfig | ParallelMachineConfig): StandardMachine | ParallelMachine; | ||
standard: (config: MachineConfig) => StandardMachine; | ||
parallel: (config: ParallelMachineConfig) => ParallelMachine; | ||
} | ||
export declare function Machine(config: MachineConfig | ParallelMachineConfig): StandardMachine | ParallelMachine; | ||
export { StateNode, State, matchesState, mapState }; | ||
import { Machine } from './Machine'; | ||
import * as actions from './actions'; | ||
export { Machine, StateNode, State, matchesState, mapState, actions }; |
491
lib/index.js
"use strict"; | ||
var __assign = (this && this.__assign) || Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var utils_1 = require("./utils"); | ||
var matchesState_1 = require("./matchesState"); | ||
exports.matchesState = matchesState_1.default; | ||
exports.matchesState = matchesState_1.matchesState; | ||
var mapState_1 = require("./mapState"); | ||
exports.mapState = mapState_1.default; | ||
exports.mapState = mapState_1.mapState; | ||
var StateNode_1 = require("./StateNode"); | ||
exports.StateNode = StateNode_1.StateNode; | ||
var State_1 = require("./State"); | ||
exports.State = State_1.State; | ||
var actions_1 = require("./actions"); | ||
var STATE_DELIMITER = '.'; | ||
var HISTORY_KEY = '$history'; | ||
var NULL_EVENT = ''; | ||
var StateNode = /** @class */ (function () { | ||
function StateNode(config) { | ||
var _this = this; | ||
this.config = config; | ||
this.__cache = { | ||
events: undefined, | ||
relativeValue: new Map(), | ||
initialState: undefined | ||
}; | ||
this.key = config.key || '(machine)'; | ||
this.parent = config.parent; | ||
this.machine = this.parent ? this.parent.machine : this; | ||
this.id = this.parent | ||
? this.parent.id + STATE_DELIMITER + this.key | ||
: this.key; | ||
this.relativeId = | ||
this.parent && this.parent.parent | ||
? this.parent.relativeId + STATE_DELIMITER + this.key | ||
: this.key; | ||
this.initial = config.initial; | ||
this.parallel = !!config.parallel; | ||
this.states = (config.states | ||
? utils_1.mapValues(config.states, function (stateConfig, key) { | ||
return new StateNode(__assign({}, stateConfig, { key: key, parent: _this })); | ||
}) | ||
: {}); | ||
this.on = config.on; | ||
this.strict = !!config.strict; | ||
this.onEntry = config.onEntry | ||
? [].concat(config.onEntry) | ||
: undefined; | ||
this.onExit = config.onExit | ||
? [].concat(config.onExit) | ||
: undefined; | ||
this.data = config.data; | ||
this.activities = config.activities; | ||
} | ||
StateNode.prototype.getStateNodes = function (state) { | ||
var _this = this; | ||
var stateValue = state instanceof State_1.State ? state.value : utils_1.toTrie(state); | ||
if (typeof stateValue === 'string') { | ||
var initialStateValue = this.states[stateValue].initial; | ||
return initialStateValue | ||
? this.getStateNodes((_a = {}, _a[stateValue] = initialStateValue, _a)) | ||
: [this.states[stateValue]]; | ||
} | ||
var subStateKeys = Object.keys(stateValue); | ||
var subStateNodes = subStateKeys.map(function (subStateKey) { return _this.states[subStateKey]; }); | ||
return subStateNodes.concat(subStateKeys | ||
.map(function (subStateKey) { | ||
return _this.states[subStateKey].getStateNodes(stateValue[subStateKey]); | ||
}) | ||
.reduce(function (a, b) { return a.concat(b); })); | ||
var _a; | ||
}; | ||
StateNode.prototype.handles = function (event) { | ||
var eventType = utils_1.getEventType(event); | ||
if (this.on) { | ||
return eventType in this.on; | ||
} | ||
return false; | ||
}; | ||
StateNode.prototype.transition = function (state, event, extendedState) { | ||
if (this.strict) { | ||
var eventType = utils_1.getEventType(event); | ||
if (this.events.indexOf(eventType) === -1) { | ||
throw new Error("Machine '" + this.id + "' does not accept event '" + eventType + "'"); | ||
} | ||
} | ||
var stateTransition = this.transitionStateValue(state, event, extendedState); | ||
var nextState = this.stateTransitionToState(stateTransition, state); | ||
if (!nextState) { | ||
return State_1.State.inert(state); | ||
} | ||
// Try transitioning from "transient" states from the NULL_EVENT | ||
// until a state with non-transient transitions is found | ||
var maybeNextState = nextState; | ||
var nextStateFromInternalTransition; | ||
do { | ||
nextStateFromInternalTransition = maybeNextState; | ||
maybeNextState = this.stateTransitionToState(this.transitionStateValue(nextStateFromInternalTransition, NULL_EVENT, extendedState), nextStateFromInternalTransition); | ||
} while (maybeNextState); | ||
// TODO: handle internally raised events | ||
return nextStateFromInternalTransition; | ||
}; | ||
StateNode.prototype.stateTransitionToState = function (stateTransition, prevState) { | ||
var nextStateValue = stateTransition.stateValue, nextActions = stateTransition.actions, nextActivities = stateTransition.activities; | ||
if (!nextStateValue) { | ||
return undefined; | ||
} | ||
var prevActivities = prevState instanceof State_1.State ? prevState.activities : undefined; | ||
var activities = __assign({}, prevActivities, nextActivities); | ||
return new State_1.State( | ||
// next state value | ||
nextStateValue, | ||
// history | ||
State_1.State.from(prevState), | ||
// effects | ||
nextActions | ||
? nextActions.onExit | ||
.concat(nextActions.actions) | ||
.concat(nextActions.onEntry) | ||
: [], | ||
// activities | ||
activities, | ||
// data | ||
this.getStateNodes(nextStateValue).reduce(function (data, stateNode) { | ||
data[stateNode.id] = stateNode.data; | ||
return data; | ||
}, {})); | ||
}; | ||
StateNode.prototype.transitionStateValue = function (state, event, extendedState) { | ||
var _this = this; | ||
var history = state instanceof State_1.State ? state.history : undefined; | ||
var stateValue = utils_1.toTrie(state); | ||
if (typeof stateValue === 'string') { | ||
if (!this.states[stateValue]) { | ||
throw new Error("State '" + stateValue + "' does not exist on machine '" + this.id + "'"); | ||
} | ||
var subStateNode = this.states[stateValue]; | ||
if (subStateNode.states && subStateNode.initial) { | ||
// Get the initial state value | ||
var initialStateValue = subStateNode.initialState.value; | ||
stateValue = (_a = {}, _a[stateValue] = initialStateValue, _a); | ||
} | ||
else { | ||
// Transition from the substate | ||
return subStateNode.next(event, history ? history.value : undefined, extendedState); | ||
} | ||
} | ||
// Potential transition tuples from parent state nodes | ||
var potentialStateTransitions = []; | ||
var nextStateValue = utils_1.mapValues(stateValue, function (subStateValue, subStateKey) { | ||
var subHistory = history ? history.value[subStateKey] : undefined; | ||
var subState = new State_1.State(subStateValue, subHistory ? State_1.State.from(subHistory) : undefined); | ||
var subStateNode = _this.states[subStateKey]; | ||
var subStateTransition = subStateNode.transitionStateValue(subState, event, extendedState); | ||
if (!subStateTransition.stateValue) { | ||
potentialStateTransitions.push(subStateNode.next(event, history ? history.value : undefined, extendedState)); | ||
} | ||
return subStateTransition; | ||
}); | ||
if (Array.prototype.every.call(Object.keys(nextStateValue), function (key) { | ||
return nextStateValue[key].stateValue === undefined; | ||
})) { | ||
if (this.parallel) { | ||
if (potentialStateTransitions.length) { | ||
return potentialStateTransitions[0]; | ||
} | ||
return { | ||
stateValue: undefined, | ||
actions: { onEntry: [], onExit: [], actions: [] }, | ||
activities: undefined | ||
}; | ||
} | ||
var subStateKey = Object.keys(nextStateValue)[0]; | ||
// try with parent | ||
var _b = this.states[subStateKey].next(event, history ? history.value : undefined, extendedState), parentNextValue = _b.stateValue, parentNextActions = _b.actions, parentActivities = _b.activities; | ||
var nextActions = nextStateValue[subStateKey].actions; | ||
var activities = nextStateValue[subStateKey].activities; | ||
var allActivities = __assign({}, activities, parentActivities); | ||
var allActions = parentNextActions | ||
? nextActions | ||
? { | ||
onEntry: nextActions.onEntry.concat(parentNextActions.onEntry), | ||
actions: nextActions.actions.concat(parentNextActions.actions), | ||
onExit: nextActions.onExit.concat(parentNextActions.onExit) | ||
} | ||
: parentNextActions | ||
: nextActions; | ||
return { | ||
stateValue: parentNextValue, | ||
actions: allActions, | ||
activities: allActivities | ||
}; | ||
} | ||
if (this.parallel) { | ||
nextStateValue = __assign({}, utils_1.mapValues(this.initialState.value, function (subStateValue) { return ({ | ||
stateValue: subStateValue, | ||
actions: { onEntry: [], onExit: [], actions: [] }, | ||
activities: undefined | ||
}); }), nextStateValue); | ||
} | ||
var finalActions = { | ||
onEntry: [], | ||
actions: [], | ||
onExit: [] | ||
}; | ||
var finalActivities = {}; | ||
var finalStateValue = utils_1.mapValues(nextStateValue, function (subStateTransition, key) { | ||
var nextSubStateValue = subStateTransition.stateValue, nextSubActions = subStateTransition.actions, nextSubActivities = subStateTransition.activities; | ||
if (nextSubActions) { | ||
if (nextSubActions.onEntry) { | ||
(_a = finalActions.onEntry).push.apply(_a, nextSubActions.onEntry); | ||
} | ||
if (nextSubActions.actions) { | ||
(_b = finalActions.actions).push.apply(_b, nextSubActions.actions); | ||
} | ||
if (nextSubActions.onExit) { | ||
(_c = finalActions.onExit).push.apply(_c, nextSubActions.onExit); | ||
} | ||
} | ||
if (nextSubActivities) { | ||
Object.assign(finalActivities, nextSubActivities); | ||
} | ||
if (!nextSubStateValue) { | ||
return stateValue[key]; | ||
} | ||
return nextSubStateValue; | ||
var _a, _b, _c; | ||
}); | ||
return { | ||
stateValue: finalStateValue, | ||
actions: finalActions, | ||
activities: finalActivities | ||
}; | ||
var _a; | ||
}; | ||
StateNode.prototype.next = function (event, history, extendedState) { | ||
var _this = this; | ||
var eventType = utils_1.getEventType(event); | ||
var actionMap = { onEntry: [], onExit: [], actions: [] }; | ||
var activityMap = {}; | ||
if (this.onExit) { | ||
actionMap.onExit = this.onExit; | ||
} | ||
if (this.activities) { | ||
this.activities.forEach(function (activity) { | ||
activityMap[utils_1.getEventType(activity)] = false; | ||
actionMap.onExit = actionMap.onExit.concat(actions_1.stop(activity)); | ||
}); | ||
} | ||
if (!this.on || !this.on[eventType]) { | ||
return { | ||
stateValue: undefined, | ||
actions: actionMap, | ||
activities: activityMap | ||
}; | ||
} | ||
var transition = this.on[eventType]; | ||
var nextStateString; | ||
if (typeof transition === 'string') { | ||
nextStateString = transition; | ||
} | ||
else { | ||
var candidates = Array.isArray(transition) | ||
? transition | ||
: Object.keys(transition).map(function (key) { return (__assign({}, transition[key], { target: key })); }); | ||
for (var _i = 0, candidates_1 = candidates; _i < candidates_1.length; _i++) { | ||
var candidate = candidates_1[_i]; | ||
var _a = candidate, cond = _a.cond, transitionActions = _a.actions; | ||
var extendedStateObject = extendedState || {}; | ||
var eventObject = typeof event === 'string' || typeof event === 'number' | ||
? { type: event } | ||
: event; | ||
if (!cond || cond(extendedStateObject, eventObject)) { | ||
nextStateString = candidate.target; | ||
if (transitionActions) { | ||
actionMap.actions = actionMap.actions.concat(transitionActions); | ||
} | ||
break; | ||
} | ||
} | ||
} | ||
if (!nextStateString) { | ||
return { | ||
stateValue: undefined, | ||
actions: actionMap, | ||
activities: activityMap | ||
}; | ||
} | ||
var nextStatePath = utils_1.toStatePath(nextStateString); | ||
var currentState = this.parent; | ||
var currentHistory = history; | ||
var currentPath = this.key; | ||
nextStatePath.forEach(function (subPath) { | ||
if (!currentState || !currentState.states) { | ||
throw new Error("Unable to read '" + subPath + "'"); | ||
} | ||
if (subPath === HISTORY_KEY) { | ||
if (currentHistory) { | ||
subPath = | ||
typeof currentHistory === 'object' | ||
? Object.keys(currentHistory)[0] | ||
: currentHistory; | ||
} | ||
else if (currentState.initial) { | ||
subPath = currentState.initial; | ||
} | ||
else { | ||
throw new Error("Cannot read '" + HISTORY_KEY + "' from state '" + currentState.id + "': missing 'initial'"); | ||
} | ||
} | ||
else if (subPath === NULL_EVENT) { | ||
actionMap.onExit = []; | ||
currentState = currentState.states[_this.key]; | ||
return; | ||
} | ||
currentState = currentState.states[subPath]; | ||
if (currentState === undefined) { | ||
throw new Error("Event '" + event + "' on state '" + currentPath + "' leads to undefined state '" + nextStatePath.join(STATE_DELIMITER) + "'."); | ||
} | ||
if (currentState.onEntry) { | ||
actionMap.onEntry = actionMap.onEntry.concat(currentState.onEntry); | ||
} | ||
if (currentState.activities) { | ||
currentState.activities.forEach(function (activity) { | ||
activityMap[utils_1.getEventType(activity)] = true; | ||
actionMap.onEntry = actionMap.onEntry.concat(actions_1.start(activity)); | ||
}); | ||
} | ||
currentPath = subPath; | ||
if (currentHistory) { | ||
currentHistory = currentHistory[subPath]; | ||
} | ||
}); | ||
if (!currentState) { | ||
throw new Error('no state'); | ||
} | ||
while (currentState.initial) { | ||
if (!currentState || !currentState.states) { | ||
throw new Error("Invalid initial state"); | ||
} | ||
currentState = currentState.states[currentState.initial]; | ||
if (currentState.onEntry) { | ||
actionMap.onEntry = actionMap.onEntry.concat(currentState.onEntry); | ||
} | ||
if (currentState.activities) { | ||
currentState.activities.forEach(function (activity) { | ||
activityMap[utils_1.getEventType(activity)] = true; | ||
actionMap.onEntry = actionMap.onEntry.concat(actions_1.start(activity)); | ||
}); | ||
} | ||
} | ||
return { | ||
stateValue: currentState.getRelativeValue(this.parent), | ||
actions: actionMap, | ||
activities: activityMap | ||
}; | ||
}; | ||
Object.defineProperty(StateNode.prototype, "resolvedStateValue", { | ||
get: function () { | ||
var key = this.key; | ||
if (this.parallel) { | ||
return _a = {}, | ||
_a[key] = utils_1.mapValues(this.states, function (stateNode) { return stateNode.resolvedStateValue[stateNode.key]; }), | ||
_a; | ||
} | ||
if (!this.initial) { | ||
// If leaf node, value is just the state node's key | ||
return key; | ||
} | ||
return _b = {}, | ||
_b[key] = this.states[this.initial].resolvedStateValue, | ||
_b; | ||
var _a, _b; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(StateNode.prototype, "initialStateValue", { | ||
get: function () { | ||
var initialStateValue = this.__cache.initialState || | ||
(this.parallel | ||
? utils_1.mapValues(this.states, function (state) { return state.initialStateValue; }) | ||
: this.resolvedStateValue[this.key]); | ||
this.__cache.initialState = initialStateValue; | ||
return this.__cache.initialState; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(StateNode.prototype, "initialState", { | ||
get: function () { | ||
var initialStateValue = this.initialStateValue; | ||
if (!initialStateValue) { | ||
throw new Error("Cannot retrieve initial state from simple state '" + this.id + ".'"); | ||
} | ||
var entryActions = this.getStateNodes(initialStateValue).reduce(function (actions, stateNode) { | ||
return stateNode.onEntry ? actions.concat(stateNode.onEntry) : actions; | ||
}, []); | ||
return new State_1.State(initialStateValue, undefined, entryActions); | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
StateNode.prototype.getStates = function (stateValue) { | ||
var _this = this; | ||
if (typeof stateValue === 'string') { | ||
return [this.states[stateValue]]; | ||
} | ||
var stateNodes = []; | ||
Object.keys(stateValue).forEach(function (key) { | ||
stateNodes.push.apply(stateNodes, _this.states[key].getStates(stateValue[key])); | ||
}); | ||
return stateNodes; | ||
}; | ||
StateNode.prototype.getState = function (relativeStateId) { | ||
var statePath = utils_1.toStatePath(relativeStateId); | ||
try { | ||
return statePath.reduce(function (subState, subPath) { | ||
if (!subState.states) { | ||
throw new Error("Cannot retrieve subPath '" + subPath + "' from node with no states"); | ||
} | ||
return subState.states[subPath]; | ||
}, this); | ||
} | ||
catch (e) { | ||
throw new Error("State '" + relativeStateId + " does not exist on machine '" + this.id + "'"); | ||
} | ||
}; | ||
Object.defineProperty(StateNode.prototype, "events", { | ||
get: function () { | ||
if (this.__cache.events) { | ||
return this.__cache.events; | ||
} | ||
var states = this.states; | ||
var events = new Set(this.on ? Object.keys(this.on) : undefined); | ||
if (states) { | ||
Object.keys(states).forEach(function (stateId) { | ||
var state = states[stateId]; | ||
if (state.states) { | ||
for (var _i = 0, _a = state.events; _i < _a.length; _i++) { | ||
var event_1 = _a[_i]; | ||
events.add("" + event_1); | ||
} | ||
} | ||
}); | ||
} | ||
return (this.__cache.events = Array.from(events)); | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
StateNode.prototype.getRelativeValue = function (toNode) { | ||
var memoizedRelativeValue = toNode | ||
? this.__cache.relativeValue.get(toNode) | ||
: undefined; | ||
if (memoizedRelativeValue) { | ||
return memoizedRelativeValue; | ||
} | ||
var initialStateValue = this.initialStateValue; | ||
var relativeValue = initialStateValue | ||
? (_a = {}, | ||
_a[this.key] = initialStateValue, | ||
_a) : this.key; | ||
var currentNode = this.parent; | ||
while (currentNode && currentNode !== toNode) { | ||
var currentInitialState = currentNode.initialStateValue; | ||
relativeValue = (_b = {}, | ||
_b[currentNode.key] = currentNode.parallel && | ||
typeof currentInitialState === 'object' && | ||
typeof relativeValue === 'object' | ||
? __assign({}, currentInitialState, relativeValue) : relativeValue, | ||
_b); | ||
currentNode = currentNode.parent; | ||
} | ||
this.__cache.relativeValue.set(toNode, relativeValue); | ||
return relativeValue; | ||
var _a, _b; | ||
}; | ||
return StateNode; | ||
}()); | ||
exports.StateNode = StateNode; | ||
function Machine(config) { | ||
return new StateNode(config); | ||
} | ||
exports.Machine = Machine; | ||
var Machine_1 = require("./Machine"); | ||
exports.Machine = Machine_1.Machine; | ||
var actions = require("./actions"); | ||
exports.actions = actions; |
@@ -1,3 +0,3 @@ | ||
export default function mapState(stateMap: { | ||
export declare function mapState(stateMap: { | ||
[stateId: string]: any; | ||
}, stateId: string): any; |
@@ -7,3 +7,3 @@ "use strict"; | ||
Object.keys(stateMap).forEach(function (mappedStateId) { | ||
if (matchesState_1.default(mappedStateId, stateId) && | ||
if (matchesState_1.matchesState(mappedStateId, stateId) && | ||
(!foundStateId || stateId.length > foundStateId.length)) { | ||
@@ -15,2 +15,2 @@ foundStateId = mappedStateId; | ||
} | ||
exports.default = mapState; | ||
exports.mapState = mapState; |
import { StateValue } from './types'; | ||
export default function matchesState(parentStateId: StateValue, childStateId: StateValue): boolean; | ||
export declare function matchesState(parentStateId: StateValue, childStateId: StateValue): boolean; |
@@ -5,4 +5,4 @@ "use strict"; | ||
function matchesState(parentStateId, childStateId) { | ||
var parentStateValue = utils_1.toTrie(parentStateId); | ||
var childStateValue = utils_1.toTrie(childStateId); | ||
var parentStateValue = utils_1.toStateValue(parentStateId); | ||
var childStateValue = utils_1.toStateValue(childStateId); | ||
if (typeof childStateValue === 'string') { | ||
@@ -24,2 +24,2 @@ if (typeof parentStateValue === 'string') { | ||
} | ||
exports.default = matchesState; | ||
exports.matchesState = matchesState; |
import { Machine } from './types'; | ||
export declare function fromMachine(machine: Machine): string; | ||
export declare function toMachine(xml: string): any; |
110
lib/scxml.js
@@ -12,42 +12,4 @@ "use strict"; | ||
var xml_js_1 = require("xml-js"); | ||
var xstate = require("./index"); | ||
var utils_1 = require("./utils"); | ||
var pedestrianStates = { | ||
initial: 'walk', | ||
states: { | ||
walk: { | ||
on: { | ||
PED_COUNTDOWN: 'wait' | ||
} | ||
}, | ||
wait: { | ||
on: { | ||
PED_COUNTDOWN: 'stop' | ||
} | ||
}, | ||
stop: {} | ||
} | ||
}; | ||
var lightMachine = xstate.Machine({ | ||
key: 'light', | ||
initial: 'green', | ||
states: { | ||
green: { | ||
on: { | ||
TIMER: 'yellow', | ||
POWER_OUTAGE: 'red' | ||
} | ||
}, | ||
yellow: { | ||
on: { | ||
TIMER: 'red', | ||
POWER_OUTAGE: 'red' | ||
} | ||
}, | ||
red: __assign({ on: { | ||
TIMER: 'green', | ||
POWER_OUTAGE: 'red' | ||
} }, pedestrianStates) | ||
} | ||
}); | ||
var actions = require("./actions"); | ||
function stateNodeToSCXML(stateNode) { | ||
@@ -186,2 +148,70 @@ var parallel = stateNode.parallel; | ||
exports.fromMachine = fromMachine; | ||
console.log(fromMachine(lightMachine)); | ||
function indexedRecord(items, identifier) { | ||
var record = {}; | ||
var identifierFn = typeof identifier === 'string' ? function (item) { return item[identifier]; } : identifier; | ||
items.forEach(function (item) { | ||
record[identifierFn(item)] = item; | ||
}); | ||
return record; | ||
} | ||
function executableContent(elements) { | ||
var transition = { | ||
actions: [] | ||
}; | ||
elements.forEach(function (element) { | ||
switch (element.name) { | ||
case 'raise': | ||
transition.actions.push(actions.raise(element.attributes.event)); | ||
default: | ||
return; | ||
} | ||
}); | ||
return transition; | ||
} | ||
function toConfig(nodeJson) { | ||
var initial = nodeJson.attributes.initial; | ||
var states; | ||
var on; | ||
if (nodeJson.elements) { | ||
var stateElements = nodeJson.elements.filter(function (element) { return element.name === 'state'; }); | ||
var transitionElements = nodeJson.elements.filter(function (element) { return element.name === 'transition'; }); | ||
var onEntryElement = nodeJson.elements.find(function (element) { return element.name === 'onentry'; }); | ||
var onExitElement = nodeJson.elements.find(function (element) { return element.name === 'onexit'; }); | ||
states = indexedRecord(stateElements, function (item) { return "" + item.attributes.id; }); | ||
on = utils_1.mapValues(indexedRecord(transitionElements, function (item) { return item.attributes.event || ''; }), function (value) { | ||
return [ | ||
__assign({ target: value.attributes.target }, value.elements ? executableContent(value.elements) : undefined) | ||
]; | ||
}); | ||
var onEntry = onEntryElement | ||
? onEntryElement.elements.map(function (element) { | ||
switch (element.name) { | ||
case 'raise': | ||
return actions.raise(element.attributes.event); | ||
default: | ||
return 'not-implemented'; | ||
} | ||
}) | ||
: undefined; | ||
var onExit = onExitElement | ||
? onExitElement.elements.map(function (element) { | ||
switch (element.name) { | ||
case 'raise': | ||
return actions.raise(element.attributes.event); | ||
default: | ||
return 'not-implemented'; | ||
} | ||
}) | ||
: undefined; | ||
return __assign({}, initial ? { initial: initial } : undefined, stateElements.length | ||
? { states: utils_1.mapValues(states, toConfig) } | ||
: undefined, transitionElements.length ? { on: on } : undefined, onEntry ? { onEntry: onEntry } : undefined, onExit ? { onExit: onExit } : undefined); | ||
} | ||
return {}; | ||
} | ||
function toMachine(xml) { | ||
var json = xml_js_1.xml2js(xml); | ||
var machineElement = json.elements.filter(function (element) { return element.name === 'scxml'; })[0]; | ||
return toConfig(machineElement); | ||
} | ||
exports.toMachine = toMachine; |
@@ -20,3 +20,3 @@ "use strict"; | ||
} | ||
return new State(utils_1.toTrie(stateValue)); | ||
return new State(utils_1.toStateValue(stateValue)); | ||
}; | ||
@@ -23,0 +23,0 @@ State.inert = function (stateValue) { |
@@ -1,2 +0,2 @@ | ||
import { StateNode } from './index'; | ||
import { StateNode } from './StateNode'; | ||
import { State } from './State'; | ||
@@ -25,2 +25,3 @@ export declare type EventType = string | number; | ||
actions?: Action[]; | ||
in?: StateValue; | ||
} | ||
@@ -77,3 +78,2 @@ export interface TargetTransitionConfig extends TransitionConfig { | ||
id: string; | ||
relativeId: string; | ||
initial: string | undefined; | ||
@@ -102,3 +102,2 @@ parallel: boolean; | ||
states: Record<string, StateNode>; | ||
on: never; | ||
onEntry: never; | ||
@@ -134,1 +133,55 @@ onExit: never; | ||
} | ||
export interface ActivityAction extends ActionObject { | ||
activity: ActionType; | ||
data: { | ||
type: ActionType; | ||
[key: string]: any; | ||
}; | ||
} | ||
export interface SendAction extends ActionObject { | ||
event: EventObject; | ||
delay?: number; | ||
id: string | number; | ||
} | ||
export interface SendActionOptions { | ||
delay?: number; | ||
id?: string | number; | ||
} | ||
export interface CancelAction extends ActionObject { | ||
sendId: string | number; | ||
} | ||
export interface Edge { | ||
event: string; | ||
source: StateNode; | ||
target: StateNode; | ||
cond?: Condition; | ||
actions: Action[]; | ||
} | ||
export interface NodesAndEdges { | ||
nodes: StateNode[]; | ||
edges: Edge[]; | ||
} | ||
export interface Segment { | ||
state: StateValue; | ||
event: Event; | ||
} | ||
export interface PathMap { | ||
[key: string]: Segment[]; | ||
} | ||
export interface PathItem { | ||
state: StateValue; | ||
path: Segment[]; | ||
} | ||
export interface PathsItem { | ||
state: StateValue; | ||
paths: Segment[][]; | ||
} | ||
export interface PathsMap { | ||
[key: string]: Segment[][]; | ||
} | ||
export interface TransitionMap { | ||
state: StateValue | undefined; | ||
} | ||
export interface AdjacencyMap { | ||
[stateId: string]: Record<string, TransitionMap>; | ||
} |
@@ -5,3 +5,3 @@ import { State } from './State'; | ||
export declare function toStatePath(stateId: string | string[]): string[]; | ||
export declare function toTrie(stateValue: State | StateValue): StateValue; | ||
export declare function toStateValue(stateValue: State | StateValue): StateValue; | ||
export declare function mapValues<T, P>(collection: { | ||
@@ -14,1 +14,6 @@ [key: string]: T; | ||
}; | ||
/** | ||
* Retrieves a value at the given path. | ||
* @param props The deep path to the prop of the desired value | ||
*/ | ||
export declare const path: (props: string[]) => any; |
@@ -27,3 +27,3 @@ "use strict"; | ||
exports.toStatePath = toStatePath; | ||
function toTrie(stateValue) { | ||
function toStateValue(stateValue) { | ||
if (stateValue instanceof State_1.State) { | ||
@@ -52,3 +52,3 @@ return stateValue.value; | ||
} | ||
exports.toTrie = toTrie; | ||
exports.toStateValue = toStateValue; | ||
function mapValues(collection, iteratee) { | ||
@@ -62,1 +62,13 @@ var result = {}; | ||
exports.mapValues = mapValues; | ||
/** | ||
* Retrieves a value at the given path. | ||
* @param props The deep path to the prop of the desired value | ||
*/ | ||
exports.path = function (props) { return function (object) { | ||
var result = object; | ||
for (var _i = 0, props_1 = props; _i < props_1.length; _i++) { | ||
var prop = props_1[_i]; | ||
result = result[prop]; | ||
} | ||
return result; | ||
}; }; |
{ | ||
"name": "xstate", | ||
"version": "3.1.0", | ||
"version": "3.1.1", | ||
"description": "Simple JavaScript Finite State Machines and Statecharts", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
import { | ||
ActionObject, | ||
Action, | ||
ActionType, | ||
Event, | ||
EventType, | ||
EventObject | ||
EventObject, | ||
ActivityAction, | ||
SendAction, | ||
SendActionOptions, | ||
CancelAction | ||
} from './types'; | ||
@@ -22,10 +24,2 @@ import { getEventType } from './utils'; | ||
export interface ActivityAction extends ActionObject { | ||
activity: ActionType; | ||
data: { | ||
type: ActionType; | ||
[key: string]: any; | ||
}; | ||
} | ||
const createActivityAction = (actionType: string) => ( | ||
@@ -45,3 +39,3 @@ activity: Action | ||
const toEventObject = (event: Event): EventObject => { | ||
export const toEventObject = (event: Event): EventObject => { | ||
if (typeof event === 'string' || typeof event === 'number') { | ||
@@ -59,11 +53,2 @@ return { type: event }; | ||
export interface SendAction extends ActionObject { | ||
event: EventObject; | ||
delay?: number; | ||
} | ||
export interface SendActionOptions { | ||
delay?: number; | ||
id?: string | number; | ||
} | ||
export const send = (event: Event, options?: SendActionOptions): SendAction => { | ||
@@ -78,6 +63,2 @@ return { | ||
export interface CancelAction extends ActionObject { | ||
sendId: string | number; | ||
} | ||
export const cancel = (sendId: string | number): CancelAction => { | ||
@@ -84,0 +65,0 @@ return { |
import { StateNode } from './index'; | ||
import { toTrie } from './utils'; | ||
import { toStateValue } from './utils'; | ||
import { | ||
// Transition, | ||
StateValue, | ||
Machine, | ||
Event, | ||
// TargetTransitionConfig, | ||
Condition, | ||
Action | ||
Edge, | ||
Segment, | ||
PathMap, | ||
PathItem, | ||
PathsItem, | ||
PathsMap, | ||
AdjacencyMap | ||
} from './types'; | ||
const EMPTY_MAP = {}; | ||
export interface Edge { | ||
event: string; | ||
source: StateNode; | ||
target: StateNode; | ||
cond?: Condition; | ||
actions: Action[]; | ||
} | ||
export interface INodesAndEdges { | ||
nodes: StateNode[]; | ||
edges: Edge[]; | ||
} | ||
@@ -95,36 +86,5 @@ export function getNodes(node: StateNode): StateNode[] { | ||
export interface Segment { | ||
state: StateValue; | ||
event: Event; | ||
} | ||
export function getAdjacencyMap(node: Machine): AdjacencyMap { | ||
const adjacency: AdjacencyMap = {}; | ||
export interface IPathMap { | ||
[key: string]: Segment[]; | ||
} | ||
export interface IPathItem { | ||
state: StateValue; | ||
path: Segment[]; | ||
} | ||
export interface IPathsItem { | ||
state: StateValue; | ||
paths: Segment[][]; | ||
} | ||
export interface IPathsMap { | ||
[key: string]: Segment[][]; | ||
} | ||
export interface ITransitionMap { | ||
state: StateValue | undefined; | ||
} | ||
export interface IAdjacencyMap { | ||
[stateId: string]: Record<string, ITransitionMap>; | ||
} | ||
export function getAdjacencyMap(node: Machine): IAdjacencyMap { | ||
const adjacency: IAdjacencyMap = {}; | ||
const events = node.events; | ||
@@ -154,3 +114,3 @@ | ||
export function getShortestPaths(machine: Machine): IPathMap { | ||
export function getShortestPaths(machine: Machine): PathMap { | ||
if (!machine.states) { | ||
@@ -161,3 +121,3 @@ return EMPTY_MAP; | ||
const initialStateId = JSON.stringify(machine.initialState.value); | ||
const pathMap: IPathMap = { | ||
const pathMap: PathMap = { | ||
[initialStateId]: [] | ||
@@ -167,3 +127,3 @@ }; | ||
function util(stateValue: StateValue): IPathMap { | ||
function util(stateValue: StateValue): PathMap { | ||
const stateId = JSON.stringify(stateValue); | ||
@@ -180,3 +140,3 @@ visited.add(stateId); | ||
const nextStateId = JSON.stringify(toTrie(nextStateValue)); | ||
const nextStateId = JSON.stringify(toStateValue(nextStateValue)); | ||
@@ -218,3 +178,3 @@ if ( | ||
export function getShortestPathsAsArray(machine: Machine): IPathItem[] { | ||
export function getShortestPathsAsArray(machine: Machine): PathItem[] { | ||
const result = getShortestPaths(machine); | ||
@@ -227,3 +187,3 @@ return Object.keys(result).map(key => ({ | ||
export function getSimplePaths(machine: Machine): IPathsMap { | ||
export function getSimplePaths(machine: Machine): PathsMap { | ||
if (!machine.states) { | ||
@@ -236,3 +196,3 @@ return EMPTY_MAP; | ||
const path: Segment[] = []; | ||
const paths: IPathsMap = {}; | ||
const paths: PathsMap = {}; | ||
@@ -275,3 +235,3 @@ function util(fromPathId: string, toPathId: string) { | ||
export function getSimplePathsAsArray(machine: Machine): IPathsItem[] { | ||
export function getSimplePathsAsArray(machine: Machine): PathsItem[] { | ||
const result = getSimplePaths(machine); | ||
@@ -278,0 +238,0 @@ return Object.keys(result).map(key => ({ |
698
src/index.ts
@@ -1,694 +0,8 @@ | ||
import { getEventType, toStatePath, toTrie, mapValues } from './utils'; | ||
import { | ||
Event, | ||
StateValue, | ||
Transition, | ||
Action, | ||
Machine, | ||
StandardMachine, | ||
ParallelMachine, | ||
SimpleOrCompoundStateNodeConfig, | ||
MachineConfig, | ||
ParallelMachineConfig, | ||
EventType, | ||
EventObject, | ||
ActionMap, | ||
StandardMachineConfig, | ||
TransitionConfig, | ||
ActivityMap, | ||
StateNodeConfig, | ||
Activity, | ||
StateTransition | ||
} from './types'; | ||
import matchesState from './matchesState'; | ||
import mapState from './mapState'; | ||
import { matchesState } from './matchesState'; | ||
import { mapState } from './mapState'; | ||
import { StateNode } from './StateNode'; | ||
import { State } from './State'; | ||
import { start, stop } from './actions'; | ||
import { Machine } from './Machine'; | ||
import * as actions from './actions'; | ||
const STATE_DELIMITER = '.'; | ||
const HISTORY_KEY = '$history'; | ||
const NULL_EVENT = ''; | ||
class StateNode implements StateNodeConfig { | ||
public key: string; | ||
public id: string; | ||
public relativeId: string; | ||
public initial?: string; | ||
public parallel?: boolean; | ||
public states: Record<string, StateNode>; | ||
public on?: Record<string, Transition | undefined>; | ||
public onEntry?: Action[]; | ||
public onExit?: Action[]; | ||
public activities?: Activity[]; | ||
public strict: boolean; | ||
public parent?: StateNode; | ||
public machine: StateNode; | ||
public data: object | undefined; | ||
private __cache = { | ||
events: undefined as EventType[] | undefined, | ||
relativeValue: new Map() as Map<StateNode, StateValue>, | ||
initialState: undefined as StateValue | undefined | ||
}; | ||
constructor( | ||
public config: | ||
| SimpleOrCompoundStateNodeConfig | ||
| StandardMachineConfig | ||
| ParallelMachineConfig | ||
) { | ||
this.key = config.key || '(machine)'; | ||
this.parent = config.parent; | ||
this.machine = this.parent ? this.parent.machine : this; | ||
this.id = this.parent | ||
? this.parent.id + STATE_DELIMITER + this.key | ||
: this.key; | ||
this.relativeId = | ||
this.parent && this.parent.parent | ||
? this.parent.relativeId + STATE_DELIMITER + this.key | ||
: this.key; | ||
this.initial = config.initial; | ||
this.parallel = !!config.parallel; | ||
this.states = (config.states | ||
? mapValues<SimpleOrCompoundStateNodeConfig, StateNode>( | ||
config.states, | ||
(stateConfig, key) => | ||
new StateNode({ | ||
...stateConfig, | ||
key, | ||
parent: this | ||
}) | ||
) | ||
: {}) as Record<string, StateNode>; | ||
this.on = config.on; | ||
this.strict = !!config.strict; | ||
this.onEntry = config.onEntry | ||
? ([] as Action[]).concat(config.onEntry) | ||
: undefined; | ||
this.onExit = config.onExit | ||
? ([] as Action[]).concat(config.onExit) | ||
: undefined; | ||
this.data = config.data; | ||
this.activities = config.activities; | ||
} | ||
public getStateNodes(state: StateValue | State): StateNode[] { | ||
const stateValue = state instanceof State ? state.value : toTrie(state); | ||
if (typeof stateValue === 'string') { | ||
const initialStateValue = (this.states[stateValue] as StateNode).initial; | ||
return initialStateValue | ||
? this.getStateNodes({ [stateValue]: initialStateValue }) | ||
: [this.states[stateValue]]; | ||
} | ||
const subStateKeys = Object.keys(stateValue); | ||
const subStateNodes: StateNode[] = subStateKeys.map( | ||
subStateKey => this.states[subStateKey] | ||
); | ||
return subStateNodes.concat( | ||
subStateKeys | ||
.map(subStateKey => { | ||
return this.states[subStateKey].getStateNodes( | ||
stateValue[subStateKey] | ||
); | ||
}) | ||
.reduce((a, b) => a.concat(b)) | ||
); | ||
} | ||
public handles(event: Event): boolean { | ||
const eventType = getEventType(event); | ||
if (this.on) { | ||
return eventType in this.on; | ||
} | ||
return false; | ||
} | ||
public transition( | ||
state: StateValue | State, | ||
event: Event, | ||
extendedState?: any | ||
): State { | ||
if (this.strict) { | ||
const eventType = getEventType(event); | ||
if (this.events.indexOf(eventType) === -1) { | ||
throw new Error( | ||
`Machine '${this.id}' does not accept event '${eventType}'` | ||
); | ||
} | ||
} | ||
const stateTransition = this.transitionStateValue( | ||
state, | ||
event, | ||
extendedState | ||
); | ||
const nextState = this.stateTransitionToState(stateTransition, state); | ||
if (!nextState) { | ||
return State.inert(state); | ||
} | ||
// Try transitioning from "transient" states from the NULL_EVENT | ||
// until a state with non-transient transitions is found | ||
let maybeNextState: State | undefined = nextState; | ||
let nextStateFromInternalTransition: State; | ||
do { | ||
nextStateFromInternalTransition = maybeNextState; | ||
maybeNextState = this.stateTransitionToState( | ||
this.transitionStateValue( | ||
nextStateFromInternalTransition, | ||
NULL_EVENT, | ||
extendedState | ||
), | ||
nextStateFromInternalTransition | ||
); | ||
} while (maybeNextState); | ||
// TODO: handle internally raised events | ||
return nextStateFromInternalTransition; | ||
} | ||
private stateTransitionToState( | ||
stateTransition: StateTransition, | ||
prevState: StateValue | State | ||
): State | undefined { | ||
const { | ||
stateValue: nextStateValue, | ||
actions: nextActions, | ||
activities: nextActivities | ||
} = stateTransition; | ||
if (!nextStateValue) { | ||
return undefined; | ||
} | ||
const prevActivities = | ||
prevState instanceof State ? prevState.activities : undefined; | ||
const activities = { ...prevActivities, ...nextActivities }; | ||
return new State( | ||
// next state value | ||
nextStateValue, | ||
// history | ||
State.from(prevState), | ||
// effects | ||
nextActions | ||
? nextActions.onExit | ||
.concat(nextActions.actions) | ||
.concat(nextActions.onEntry) | ||
: [], | ||
// activities | ||
activities, | ||
// data | ||
this.getStateNodes(nextStateValue).reduce( | ||
(data, stateNode) => { | ||
data[stateNode.id] = stateNode.data; | ||
return data; | ||
}, | ||
{} as Record<string, any> | ||
) | ||
); | ||
} | ||
private transitionStateValue( | ||
state: StateValue | State, | ||
event: Event, | ||
extendedState?: any | ||
): StateTransition { | ||
const history = state instanceof State ? state.history : undefined; | ||
let stateValue = toTrie(state); | ||
if (typeof stateValue === 'string') { | ||
if (!this.states[stateValue]) { | ||
throw new Error( | ||
`State '${stateValue}' does not exist on machine '${this.id}'` | ||
); | ||
} | ||
const subStateNode = this.states[stateValue] as StateNode; | ||
if (subStateNode.states && subStateNode.initial) { | ||
// Get the initial state value | ||
const initialStateValue = subStateNode.initialState.value; | ||
stateValue = { [stateValue]: initialStateValue }; | ||
} else { | ||
// Transition from the substate | ||
return subStateNode.next( | ||
event, | ||
history ? history.value : undefined, | ||
extendedState | ||
); | ||
} | ||
} | ||
// Potential transition tuples from parent state nodes | ||
const potentialStateTransitions: StateTransition[] = []; | ||
let nextStateValue = mapValues(stateValue, (subStateValue, subStateKey) => { | ||
const subHistory = history ? history.value[subStateKey] : undefined; | ||
const subState = new State( | ||
subStateValue, | ||
subHistory ? State.from(subHistory) : undefined | ||
); | ||
const subStateNode = this.states[subStateKey]; | ||
const subStateTransition = subStateNode.transitionStateValue( | ||
subState, | ||
event, | ||
extendedState | ||
); | ||
if (!subStateTransition.stateValue) { | ||
potentialStateTransitions.push( | ||
subStateNode.next( | ||
event, | ||
history ? history.value : undefined, | ||
extendedState | ||
) | ||
); | ||
} | ||
return subStateTransition; | ||
}); | ||
if ( | ||
Array.prototype.every.call(Object.keys(nextStateValue), key => { | ||
return nextStateValue[key].stateValue === undefined; | ||
}) | ||
) { | ||
if (this.parallel) { | ||
if (potentialStateTransitions.length) { | ||
return potentialStateTransitions[0]; | ||
} | ||
return { | ||
stateValue: undefined, | ||
actions: { onEntry: [], onExit: [], actions: [] }, | ||
activities: undefined | ||
}; | ||
} | ||
const subStateKey = Object.keys(nextStateValue)[0]; | ||
// try with parent | ||
const { | ||
stateValue: parentNextValue, | ||
actions: parentNextActions, | ||
activities: parentActivities | ||
} = this.states[subStateKey].next( | ||
event, | ||
history ? history.value : undefined, | ||
extendedState | ||
); | ||
const nextActions = nextStateValue[subStateKey].actions; | ||
const activities = nextStateValue[subStateKey].activities; | ||
const allActivities = { | ||
...activities, | ||
...parentActivities | ||
}; | ||
const allActions = parentNextActions | ||
? nextActions | ||
? { | ||
onEntry: [...nextActions.onEntry, ...parentNextActions.onEntry], | ||
actions: [...nextActions.actions, ...parentNextActions.actions], | ||
onExit: [...nextActions.onExit, ...parentNextActions.onExit] | ||
} | ||
: parentNextActions | ||
: nextActions; | ||
return { | ||
stateValue: parentNextValue, | ||
actions: allActions, | ||
activities: allActivities | ||
}; | ||
} | ||
if (this.parallel) { | ||
nextStateValue = { | ||
...mapValues( | ||
this.initialState.value as Record<string, StateValue>, | ||
subStateValue => ({ | ||
stateValue: subStateValue, | ||
actions: { onEntry: [], onExit: [], actions: [] }, | ||
activities: undefined | ||
}) | ||
), | ||
...nextStateValue | ||
}; | ||
} | ||
const finalActions: ActionMap = { | ||
onEntry: [], | ||
actions: [], | ||
onExit: [] | ||
}; | ||
const finalActivities: ActivityMap = {}; | ||
const finalStateValue = mapValues( | ||
nextStateValue, | ||
(subStateTransition, key) => { | ||
const { | ||
stateValue: nextSubStateValue, | ||
actions: nextSubActions, | ||
activities: nextSubActivities | ||
} = subStateTransition; | ||
if (nextSubActions) { | ||
if (nextSubActions.onEntry) { | ||
finalActions.onEntry.push(...nextSubActions.onEntry); | ||
} | ||
if (nextSubActions.actions) { | ||
finalActions.actions.push(...nextSubActions.actions); | ||
} | ||
if (nextSubActions.onExit) { | ||
finalActions.onExit.push(...nextSubActions.onExit); | ||
} | ||
} | ||
if (nextSubActivities) { | ||
Object.assign(finalActivities, nextSubActivities); | ||
} | ||
if (!nextSubStateValue) { | ||
return stateValue[key]; | ||
} | ||
return nextSubStateValue; | ||
} | ||
); | ||
return { | ||
stateValue: finalStateValue, | ||
actions: finalActions, | ||
activities: finalActivities | ||
}; | ||
} | ||
private next( | ||
event: Event, | ||
history?: StateValue, | ||
extendedState?: any | ||
): StateTransition { | ||
const eventType = getEventType(event); | ||
const actionMap: ActionMap = { onEntry: [], onExit: [], actions: [] }; | ||
const activityMap: ActivityMap = {}; | ||
if (this.onExit) { | ||
actionMap.onExit = this.onExit; | ||
} | ||
if (this.activities) { | ||
this.activities.forEach(activity => { | ||
activityMap[getEventType(activity)] = false; | ||
actionMap.onExit = actionMap.onExit.concat(stop(activity)); | ||
}); | ||
} | ||
if (!this.on || !this.on[eventType]) { | ||
return { | ||
stateValue: undefined, | ||
actions: actionMap, | ||
activities: activityMap | ||
}; | ||
} | ||
const transition = this.on[eventType] as Transition; | ||
let nextStateString: string | undefined; | ||
if (typeof transition === 'string') { | ||
nextStateString = transition; | ||
} else { | ||
const candidates = Array.isArray(transition) | ||
? transition | ||
: Object.keys(transition).map(key => ({ | ||
...transition[key], | ||
target: key | ||
})); | ||
for (const candidate of candidates) { | ||
const { | ||
cond, | ||
actions: transitionActions | ||
} = candidate as TransitionConfig; | ||
const extendedStateObject = extendedState || {}; | ||
const eventObject: EventObject = | ||
typeof event === 'string' || typeof event === 'number' | ||
? { type: event } | ||
: event; | ||
if (!cond || cond(extendedStateObject, eventObject)) { | ||
nextStateString = candidate.target; | ||
if (transitionActions) { | ||
actionMap.actions = actionMap.actions.concat(transitionActions); | ||
} | ||
break; | ||
} | ||
} | ||
} | ||
if (!nextStateString) { | ||
return { | ||
stateValue: undefined, | ||
actions: actionMap, | ||
activities: activityMap | ||
}; | ||
} | ||
const nextStatePath = toStatePath(nextStateString); | ||
let currentState = this.parent; | ||
let currentHistory = history; | ||
let currentPath = this.key; | ||
nextStatePath.forEach(subPath => { | ||
if (!currentState || !currentState.states) { | ||
throw new Error(`Unable to read '${subPath}'`); | ||
} | ||
if (subPath === HISTORY_KEY) { | ||
if (currentHistory) { | ||
subPath = | ||
typeof currentHistory === 'object' | ||
? Object.keys(currentHistory)[0] | ||
: currentHistory; | ||
} else if (currentState.initial) { | ||
subPath = currentState.initial; | ||
} else { | ||
throw new Error( | ||
`Cannot read '${HISTORY_KEY}' from state '${currentState.id}': missing 'initial'` | ||
); | ||
} | ||
} else if (subPath === NULL_EVENT) { | ||
actionMap.onExit = []; | ||
currentState = currentState.states[this.key]; | ||
return; | ||
} | ||
currentState = currentState.states[subPath]; | ||
if (currentState === undefined) { | ||
throw new Error( | ||
`Event '${event}' on state '${currentPath}' leads to undefined state '${nextStatePath.join( | ||
STATE_DELIMITER | ||
)}'.` | ||
); | ||
} | ||
if (currentState.onEntry) { | ||
actionMap.onEntry = actionMap.onEntry.concat(currentState.onEntry); | ||
} | ||
if (currentState.activities) { | ||
currentState.activities.forEach(activity => { | ||
activityMap[getEventType(activity)] = true; | ||
actionMap.onEntry = actionMap.onEntry.concat(start(activity)); | ||
}); | ||
} | ||
currentPath = subPath; | ||
if (currentHistory) { | ||
currentHistory = currentHistory[subPath]; | ||
} | ||
}); | ||
if (!currentState) { | ||
throw new Error('no state'); | ||
} | ||
while (currentState.initial) { | ||
if (!currentState || !currentState.states) { | ||
throw new Error(`Invalid initial state`); | ||
} | ||
currentState = currentState.states[currentState.initial]; | ||
if (currentState.onEntry) { | ||
actionMap.onEntry = actionMap.onEntry.concat(currentState.onEntry); | ||
} | ||
if (currentState.activities) { | ||
currentState.activities.forEach(activity => { | ||
activityMap[getEventType(activity)] = true; | ||
actionMap.onEntry = actionMap.onEntry.concat(start(activity)); | ||
}); | ||
} | ||
} | ||
return { | ||
stateValue: currentState.getRelativeValue(this.parent), | ||
actions: actionMap, | ||
activities: activityMap | ||
}; | ||
} | ||
private get resolvedStateValue(): StateValue { | ||
const { key } = this; | ||
if (this.parallel) { | ||
return { | ||
[key]: mapValues( | ||
this.states, | ||
stateNode => stateNode.resolvedStateValue[stateNode.key] | ||
) | ||
}; | ||
} | ||
if (!this.initial) { | ||
// If leaf node, value is just the state node's key | ||
return key; | ||
} | ||
return { | ||
[key]: this.states[this.initial].resolvedStateValue | ||
}; | ||
} | ||
private get initialStateValue(): StateValue | undefined { | ||
const initialStateValue = | ||
this.__cache.initialState || | ||
((this.parallel | ||
? mapValues( | ||
this.states as Record<string, StateNode>, | ||
state => state.initialStateValue | ||
) | ||
: this.resolvedStateValue[this.key]) as StateValue); | ||
this.__cache.initialState = initialStateValue; | ||
return this.__cache.initialState; | ||
} | ||
public get initialState(): State { | ||
const { initialStateValue } = this; | ||
if (!initialStateValue) { | ||
throw new Error( | ||
`Cannot retrieve initial state from simple state '${this.id}.'` | ||
); | ||
} | ||
const entryActions = this.getStateNodes(initialStateValue).reduce( | ||
(actions, stateNode) => | ||
stateNode.onEntry ? actions.concat(stateNode.onEntry) : actions, | ||
[] as Action[] | ||
); | ||
return new State(initialStateValue, undefined, entryActions); | ||
} | ||
public getStates(stateValue: StateValue): StateNode[] { | ||
if (typeof stateValue === 'string') { | ||
return [this.states[stateValue]]; | ||
} | ||
const stateNodes: StateNode[] = []; | ||
Object.keys(stateValue).forEach(key => { | ||
stateNodes.push(...this.states[key].getStates(stateValue[key])); | ||
}); | ||
return stateNodes; | ||
} | ||
public getState(relativeStateId: string | string[]): StateNode | undefined { | ||
const statePath = toStatePath(relativeStateId); | ||
try { | ||
return statePath.reduce( | ||
(subState, subPath) => { | ||
if (!subState.states) { | ||
throw new Error( | ||
`Cannot retrieve subPath '${subPath}' from node with no states` | ||
); | ||
} | ||
return subState.states[subPath]; | ||
}, | ||
this as StateNode | ||
); | ||
} catch (e) { | ||
throw new Error( | ||
`State '${relativeStateId} does not exist on machine '${this.id}'` | ||
); | ||
} | ||
} | ||
get events(): EventType[] { | ||
if (this.__cache.events) { | ||
return this.__cache.events; | ||
} | ||
const { states } = this; | ||
const events = new Set(this.on ? Object.keys(this.on) : undefined); | ||
if (states) { | ||
Object.keys(states).forEach(stateId => { | ||
const state = states[stateId]; | ||
if (state.states) { | ||
for (const event of state.events) { | ||
events.add(`${event}`); | ||
} | ||
} | ||
}); | ||
} | ||
return (this.__cache.events = Array.from(events)); | ||
} | ||
private getRelativeValue(toNode?: StateNode): StateValue { | ||
const memoizedRelativeValue = toNode | ||
? this.__cache.relativeValue.get(toNode) | ||
: undefined; | ||
if (memoizedRelativeValue) { | ||
return memoizedRelativeValue; | ||
} | ||
const initialStateValue = this.initialStateValue; | ||
let relativeValue = initialStateValue | ||
? { | ||
[this.key]: initialStateValue | ||
} | ||
: this.key; | ||
let currentNode: StateNode = this.parent as StateNode; | ||
while (currentNode && currentNode !== toNode) { | ||
const currentInitialState = currentNode.initialStateValue; | ||
relativeValue = { | ||
[currentNode.key]: | ||
currentNode.parallel && | ||
typeof currentInitialState === 'object' && | ||
typeof relativeValue === 'object' | ||
? { ...currentInitialState, ...relativeValue } | ||
: relativeValue | ||
}; | ||
currentNode = currentNode.parent as StateNode; | ||
} | ||
this.__cache.relativeValue.set(toNode as StateNode, relativeValue); | ||
return relativeValue; | ||
} | ||
} | ||
export interface MachineFactory { | ||
(config: MachineConfig | ParallelMachineConfig): | ||
| StandardMachine | ||
| ParallelMachine; | ||
standard: (config: MachineConfig) => StandardMachine; | ||
parallel: (config: ParallelMachineConfig) => ParallelMachine; | ||
} | ||
export function Machine( | ||
config: MachineConfig | ParallelMachineConfig | ||
): StandardMachine | ParallelMachine { | ||
return new StateNode(config) as StandardMachine | ParallelMachine; | ||
} | ||
export { StateNode, State, matchesState, mapState }; | ||
export { Machine, StateNode, State, matchesState, mapState, actions }; |
@@ -1,4 +0,4 @@ | ||
import matchesState from './matchesState'; | ||
import { matchesState } from './matchesState'; | ||
export default function mapState( | ||
export function mapState( | ||
stateMap: { [stateId: string]: any }, | ||
@@ -5,0 +5,0 @@ stateId: string |
@@ -1,10 +0,10 @@ | ||
import { toTrie } from './utils'; // TODO: change to utils | ||
import { toStateValue } from './utils'; // TODO: change to utils | ||
import { StateValue } from './types'; | ||
export default function matchesState( | ||
export function matchesState( | ||
parentStateId: StateValue, | ||
childStateId: StateValue | ||
): boolean { | ||
const parentStateValue = toTrie(parentStateId); | ||
const childStateValue = toTrie(childStateId); | ||
const parentStateValue = toStateValue(parentStateId); | ||
const childStateValue = toStateValue(childStateId); | ||
@@ -11,0 +11,0 @@ if (typeof childStateValue === 'string') { |
import { StateValue, Action, ActivityMap } from './types'; | ||
import { STATE_DELIMITER, EMPTY_ACTIVITY_MAP } from './constants'; | ||
import { toTrie } from './utils'; | ||
import { toStateValue } from './utils'; | ||
@@ -11,3 +11,3 @@ export class State { | ||
return new State(toTrie(stateValue)); | ||
return new State(toStateValue(stateValue)); | ||
} | ||
@@ -14,0 +14,0 @@ public static inert(stateValue: State | StateValue): State { |
@@ -1,2 +0,2 @@ | ||
import { StateNode } from './index'; | ||
import { StateNode } from './StateNode'; | ||
import { State } from './State'; | ||
@@ -32,2 +32,3 @@ | ||
actions?: Action[]; | ||
in?: StateValue; | ||
} | ||
@@ -100,3 +101,2 @@ | ||
id: string; | ||
relativeId: string; | ||
initial: string | undefined; | ||
@@ -128,3 +128,2 @@ parallel: boolean; | ||
states: Record<string, StateNode>; | ||
on: never; | ||
onEntry: never; | ||
@@ -169,1 +168,66 @@ onExit: never; | ||
} | ||
export interface ActivityAction extends ActionObject { | ||
activity: ActionType; | ||
data: { | ||
type: ActionType; | ||
[key: string]: any; | ||
}; | ||
} | ||
export interface SendAction extends ActionObject { | ||
event: EventObject; | ||
delay?: number; | ||
id: string | number; | ||
} | ||
export interface SendActionOptions { | ||
delay?: number; | ||
id?: string | number; | ||
} | ||
export interface CancelAction extends ActionObject { | ||
sendId: string | number; | ||
} | ||
export interface Edge { | ||
event: string; | ||
source: StateNode; | ||
target: StateNode; | ||
cond?: Condition; | ||
actions: Action[]; | ||
} | ||
export interface NodesAndEdges { | ||
nodes: StateNode[]; | ||
edges: Edge[]; | ||
} | ||
export interface Segment { | ||
state: StateValue; | ||
event: Event; | ||
} | ||
export interface PathMap { | ||
[key: string]: Segment[]; | ||
} | ||
export interface PathItem { | ||
state: StateValue; | ||
path: Segment[]; | ||
} | ||
export interface PathsItem { | ||
state: StateValue; | ||
paths: Segment[][]; | ||
} | ||
export interface PathsMap { | ||
[key: string]: Segment[][]; | ||
} | ||
export interface TransitionMap { | ||
state: StateValue | undefined; | ||
} | ||
export interface AdjacencyMap { | ||
[stateId: string]: Record<string, TransitionMap>; | ||
} |
@@ -28,3 +28,3 @@ import { State } from './State'; | ||
export function toTrie(stateValue: State | StateValue): StateValue { | ||
export function toStateValue(stateValue: State | StateValue): StateValue { | ||
if (stateValue instanceof State) { | ||
@@ -70,1 +70,17 @@ return stateValue.value; | ||
} | ||
/** | ||
* Retrieves a value at the given path. | ||
* @param props The deep path to the prop of the desired value | ||
*/ | ||
export const path = (props: string[]): any => <T extends Record<string, any>>( | ||
object: T | ||
): any => { | ||
let result: Record<string, any> = object; | ||
for (const prop of props) { | ||
result = result[prop]; | ||
} | ||
return result; | ||
}; |
@@ -11,3 +11,9 @@ import { Machine } from '../../src/index'; | ||
states: { | ||
C: { on: { 2: 'D' } }, | ||
C: { | ||
on: { | ||
2: { | ||
D: { in: 'B.E' } | ||
} | ||
} | ||
}, | ||
D: { on: { 1: 'C' } } | ||
@@ -40,3 +46,3 @@ } | ||
1: undefined, | ||
2: { A: 'D', B: 'G' }, | ||
2: undefined, | ||
3: { A: 'C', B: 'F' } | ||
@@ -43,0 +49,0 @@ } |
@@ -7,3 +7,2 @@ import { assert } from 'chai'; | ||
getShortestPaths, | ||
IPathMap, | ||
getSimplePaths, | ||
@@ -14,2 +13,3 @@ getAdjacencyMap, | ||
} from '../src/graph'; | ||
import { PathMap } from '../src/types'; | ||
// tslint:disable-next-line:no-var-requires | ||
@@ -339,3 +339,3 @@ // import * as util from 'util'; | ||
assert.lengthOf( | ||
(getShortestPaths(lightMachine) as IPathMap)[ | ||
(getShortestPaths(lightMachine) as PathMap)[ | ||
JSON.stringify(lightMachine.initialState.value) | ||
@@ -342,0 +342,0 @@ ], |
@@ -47,1 +47,227 @@ import { assert } from 'chai'; | ||
}); | ||
describe('deep history states', () => { | ||
const historyMachine = Machine({ | ||
key: 'history', | ||
initial: 'off', | ||
states: { | ||
off: { | ||
on: { | ||
POWER: 'on.$history', | ||
DEEP_POWER: 'on.$history.$history', | ||
DEEPEST_POWER: 'on.$history.$history.$history' | ||
} | ||
}, | ||
on: { | ||
initial: 'first', | ||
states: { | ||
first: { | ||
on: { SWITCH: 'second' } | ||
}, | ||
second: { | ||
initial: 'A', | ||
states: { | ||
A: { | ||
on: { INNER: 'B' } | ||
}, | ||
B: { | ||
initial: 'P', | ||
states: { | ||
P: { | ||
on: { INNER: 'Q' } | ||
}, | ||
Q: {} | ||
} | ||
} | ||
} | ||
} | ||
}, | ||
on: { | ||
POWER: 'off' | ||
} | ||
} | ||
} | ||
}); | ||
describe('$history', () => { | ||
// on.first -> on.second.A | ||
const state2A = historyMachine.transition({ on: 'first' }, 'SWITCH'); | ||
// on.second.A -> on.second.B.P | ||
const state2BP = historyMachine.transition(state2A, 'INNER'); | ||
// on.second.B.P -> on.second.B.Q | ||
const state2BQ = historyMachine.transition(state2BP, 'INNER'); | ||
it('should go to the shallow history', () => { | ||
// on.second.B.P -> off | ||
const stateOff = historyMachine.transition(state2BP, 'POWER'); | ||
assert.equal( | ||
historyMachine.transition(stateOff, 'POWER').toString(), | ||
'on.second.A' | ||
); | ||
}); | ||
it('should go to the deep history', () => { | ||
// on.second.B.P -> off | ||
const stateOff = historyMachine.transition(state2BP, 'POWER'); | ||
assert.equal( | ||
historyMachine.transition(stateOff, 'DEEP_POWER').toString(), | ||
'on.second.B.P' | ||
); | ||
}); | ||
it('should go to the deepest history', () => { | ||
// on.second.B.Q -> off | ||
const stateOff = historyMachine.transition(state2BQ, 'POWER'); | ||
assert.equal( | ||
historyMachine.transition(stateOff, 'DEEPEST_POWER').toString(), | ||
'on.second.B.Q' | ||
); | ||
}); | ||
it('can go to the shallow histor even when $history.$history is used', () => { | ||
const stateOff = historyMachine.transition(state2A, 'POWER'); | ||
assert.equal( | ||
historyMachine.transition(stateOff, 'DEEPEST_POWER').toString(), | ||
'on.second.A' | ||
); | ||
}); | ||
}); | ||
}); | ||
describe('parallel history states', () => { | ||
const historyMachine = Machine({ | ||
key: 'parallelhistory', | ||
initial: 'off', | ||
states: { | ||
off: { | ||
on: { | ||
SWITCH: 'on', // go to the initial states | ||
POWER: 'on.$history', | ||
DEEP_POWER: 'on.$history.$history', | ||
DEEPEST_POWER: 'on.$history.$history.$history' | ||
} | ||
}, | ||
on: { | ||
parallel: true, | ||
states: { | ||
A: { | ||
initial: 'B', | ||
states: { | ||
B: { | ||
on: { INNER_A: 'C' } | ||
}, | ||
C: { | ||
initial: 'D', | ||
states: { | ||
D: { | ||
on: { INNER_A: 'E' } | ||
}, | ||
E: {} | ||
} | ||
} | ||
} | ||
}, | ||
K: { | ||
initial: 'L', | ||
states: { | ||
L: { | ||
on: { INNER_K: 'M' } | ||
}, | ||
M: { | ||
initial: 'N', | ||
states: { | ||
N: { | ||
on: { INNER_K: 'O' } | ||
}, | ||
O: {} | ||
} | ||
} | ||
} | ||
} | ||
}, | ||
on: { | ||
POWER: 'off' | ||
} | ||
} | ||
} | ||
}); | ||
describe('$history', () => { | ||
// on.first -> on.second.A | ||
const stateABKL = historyMachine.transition( | ||
historyMachine.initialState, | ||
'SWITCH' | ||
); | ||
// INNER_A twice | ||
const stateACDKL = historyMachine.transition(stateABKL, 'INNER_A'); | ||
const stateACEKL = historyMachine.transition(stateACDKL, 'INNER_A'); | ||
// INNER_K twice | ||
const stateACEKMN = historyMachine.transition(stateACEKL, 'INNER_K'); | ||
const stateACEKMO = historyMachine.transition(stateACEKMN, 'INNER_K'); | ||
it('should ignore parallel state history', () => { | ||
const stateOff = historyMachine.transition(stateACDKL, 'POWER'); | ||
assert.deepEqual(historyMachine.transition(stateOff, 'POWER').value, { | ||
on: { A: 'B', K: 'L' } | ||
}); | ||
}); | ||
it('should remember first level state history', () => { | ||
const stateOff = historyMachine.transition(stateACDKL, 'POWER'); | ||
assert.deepEqual( | ||
historyMachine.transition(stateOff, 'DEEP_POWER').value, | ||
{ | ||
on: { A: { C: 'D' }, K: 'L' } | ||
} | ||
); | ||
}); | ||
it('should remember second level state history', () => { | ||
const stateOff = historyMachine.transition(stateACDKL, 'POWER'); | ||
assert.deepEqual( | ||
historyMachine.transition(stateOff, 'DEEPEST_POWER').value, | ||
{ | ||
on: { A: { C: 'D' }, K: 'L' } | ||
} | ||
); | ||
}); | ||
it('should remember second level state history, ignoring too many levels of $history', () => { | ||
const stateOff = historyMachine.transition(stateACDKL, 'POWER'); | ||
assert.deepEqual( | ||
historyMachine.transition(stateOff, 'DEEPEST_POWER').value, | ||
{ | ||
on: { A: { C: 'D' }, K: 'L' } | ||
} | ||
); | ||
}); | ||
it('should remember three levels of state history', () => { | ||
const stateOff = historyMachine.transition(stateACEKL, 'POWER'); | ||
assert.deepEqual( | ||
historyMachine.transition(stateOff, 'DEEPEST_POWER').value, | ||
{ | ||
on: { A: { C: 'E' }, K: 'L' } | ||
} | ||
); | ||
}); | ||
xit('should re-enter each regions of parallel state correctly', () => { | ||
const stateOff = historyMachine.transition(stateACEKMO, 'POWER'); | ||
assert.deepEqual( | ||
historyMachine.transition(stateOff, 'DEEP_POWER').value, | ||
{ | ||
on: { A: { C: 'D' }, K: { M: 'N' } } | ||
} | ||
); | ||
}); | ||
xit('should retain all regions of parallel state', () => { | ||
const stateOff = historyMachine.transition(stateACEKMO, 'POWER'); | ||
assert.deepEqual( | ||
historyMachine.transition(stateOff, 'DEEPEST_POWER').value, | ||
{ | ||
on: { A: { C: 'E' }, K: { M: 'O' } } | ||
} | ||
); | ||
}); | ||
}); | ||
}); |
@@ -48,2 +48,17 @@ import { assert } from 'chai'; | ||
const topLevelMachine = Machine({ | ||
initial: 'Hidden', | ||
on: { | ||
CLICKED_CLOSE: 'Hidden' | ||
}, | ||
states: { | ||
Hidden: { | ||
on: { | ||
PUBLISH_FAILURE: 'Failure' | ||
} | ||
}, | ||
Failure: {} | ||
} | ||
}); | ||
describe('machine.states', () => { | ||
@@ -78,2 +93,8 @@ it('should properly register machine states', () => { | ||
}); | ||
xit('should listen to events declared at top state', () => { | ||
const actualState = topLevelMachine.transition('Failure', 'CLICKED_CLOSE'); | ||
assert.deepEqual(actualState.value, 'Hidden'); | ||
}); | ||
}); |
@@ -60,2 +60,30 @@ import { Machine } from '../src/index'; | ||
}); | ||
it('should carry actions from previous transitions within same step', () => { | ||
const machine = Machine({ | ||
initial: 'A', | ||
states: { | ||
A: { | ||
onExit: 'exit_A', | ||
on: { | ||
TIMER: { | ||
T: { actions: ['timer'] } | ||
} | ||
} | ||
}, | ||
T: { | ||
on: { | ||
'': [{ target: 'B' }] | ||
} | ||
}, | ||
B: { | ||
onEntry: 'enter_B' | ||
} | ||
} | ||
}); | ||
const state = machine.transition('A', 'TIMER'); | ||
assert.deepEqual(state.actions, ['exit_A', 'timer', 'enter_B']); | ||
}); | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
98
615947
6556