@statewalker/fsm 0.10.1 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index-umd.js CHANGED
@@ -1,4 +1,4 @@
1
- // @statewalker/fsm v0.10.1 https://github.com/statewalker/statewalker-tree Copyright 2022 Mikhail Kotelnikov
1
+ // @statewalker/fsm v0.13.0 https://github.com/statewalker/statewalker-tree Copyright 2022 Mikhail Kotelnikov
2
2
  (function (global, factory) {
3
3
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@statewalker/tree')) :
4
4
  typeof define === 'function' && define.amd ? define(['exports', '@statewalker/tree'], factory) :
@@ -138,38 +138,6 @@ function getAllStateKeys(descriptor) {
138
138
  }
139
139
  }
140
140
 
141
- /**
142
- * Returns all possible transitions from the current state of the process.
143
- * @param {Object} options - method parameters
144
- * @param {Process} options.process - the current process for which
145
- * all transitions should be returned
146
- * @returns {Array<Object>} - an array of all transitions;
147
- * each entry in this returned array contains the following keys:
148
- * 1) parentStateKey - key of the state where the transition can be activated
149
- * 2) sourceStateKey - the initial transition state
150
- * 3) eventKey - key of the event activating transition
151
- * 4) targetStateKey - the resulting state for this transition
152
- *
153
- */
154
- function getAllTransitions(process) {
155
- const list = [];
156
- let stateKey = process.current ? process.current.key : KEYS.STATE_INITIAL;
157
- for (let i = process.stack.length - 1; i >= 0; i--) {
158
- const parentState = process.stack[i];
159
- const parentStateKey = parentState.key;
160
- const descriptor = parentState.descriptor;
161
- for (const sKey of [stateKey, KEYS.STATE_ANY]) {
162
- const stateTransitions = descriptor.transitions[sKey];
163
- if (!stateTransitions) continue;
164
- for (const[eventKey, targetKey] of Object.entries(stateTransitions)) {
165
- list.push([parentStateKey, sKey, eventKey, targetKey]);
166
- }
167
- }
168
- stateKey = parentStateKey;
169
- }
170
- return list;
171
- }
172
-
173
141
  function getTargetStateKey(descriptor, stateKey, eventKey) {
174
142
  const pairs = [
175
143
  [stateKey, eventKey],
@@ -272,6 +240,98 @@ function newAsyncProcess({
272
240
  return process;
273
241
  }
274
242
 
243
+ function initProcess({ config, handler, handleError = console.error }) {
244
+ let process;
245
+ const before = async (process) => {
246
+ process._started = true;
247
+ const state = process.current;
248
+ handler(state);
249
+ await state.init.run();
250
+ };
251
+ const after = async (process) => {
252
+ const state = process.current;
253
+ await state.done.run();
254
+ };
255
+
256
+ process = newAsyncProcess({
257
+ config,
258
+ before,
259
+ after,
260
+ newProcess: (descriptor) => {
261
+ return {
262
+ descriptor,
263
+ config,
264
+ status: 0,
265
+ stack: [],
266
+ event: undefined,
267
+ current: undefined
268
+ };
269
+ },
270
+ newState: (state) => {
271
+ state.init = newStateMethod();
272
+ state.done = newStateMethod();
273
+ state.dispatch = (...args) => state.process.dispatch(...args);
274
+ state.getEvent = () => state.process.event || {};
275
+ state.getEventKey = () => state.getEvent().key;
276
+ return state;
277
+ },
278
+ handleError
279
+ });
280
+ process.dispatch = (key, params = {}) => {
281
+ process.next({ key, params });
282
+ };
283
+ return process;
284
+
285
+ function newStateMethod() {
286
+ const list = [];
287
+ return Object.assign((m) => list.push(m), {
288
+ run: async (...args) => {
289
+ // We use the index to iterate over the list: the list can be updated by called methods
290
+ for (let i = 0; i < list.length; i++) {
291
+ try {
292
+ list[i] && (await list[i](...args));
293
+ } catch (error) {
294
+ console.error(error);
295
+ await handleError(error);
296
+ }
297
+ }
298
+ }
299
+ });
300
+ }
301
+ }
302
+
303
+ /**
304
+ * Returns all possible transitions from the current state of the process.
305
+ * @param {Object} options - method parameters
306
+ * @param {Process} options.process - the current process for which
307
+ * all transitions should be returned
308
+ * @returns {Array<Object>} - an array of all transitions;
309
+ * each entry in this returned array contains the following keys:
310
+ * 1) parentStateKey - key of the state where the transition can be activated
311
+ * 2) sourceStateKey - the initial transition state
312
+ * 3) eventKey - key of the event activating transition
313
+ * 4) targetStateKey - the resulting state for this transition
314
+ *
315
+ */
316
+ function getAllTransitions(process) {
317
+ const list = [];
318
+ let stateKey = process.current ? process.current.key : KEYS.STATE_INITIAL;
319
+ for (let i = process.stack.length - 1; i >= 0; i--) {
320
+ const parentState = process.stack[i];
321
+ const parentStateKey = parentState.key;
322
+ const descriptor = parentState.descriptor;
323
+ for (const sKey of [stateKey, KEYS.STATE_ANY]) {
324
+ const stateTransitions = descriptor.transitions[sKey];
325
+ if (!stateTransitions) continue;
326
+ for (const[eventKey, targetKey] of Object.entries(stateTransitions)) {
327
+ list.push([parentStateKey, sKey, eventKey, targetKey]);
328
+ }
329
+ }
330
+ stateKey = parentStateKey;
331
+ }
332
+ return list;
333
+ }
334
+
275
335
  function newSyncProcess({
276
336
  config,
277
337
  descriptor,
@@ -304,15 +364,9 @@ exports.getAllStateKeys = getAllStateKeys;
304
364
  exports.getAllTransitions = getAllTransitions;
305
365
  exports.getStateDescriptor = getStateDescriptor;
306
366
  exports.getTargetStateKey = getTargetStateKey;
307
- exports.newAsyncProcess = newAsyncProcess;
367
+ exports.initAsyncProcess = initProcess;
308
368
  exports.newProcessInstance = newProcessInstance;
309
369
  exports.newSyncProcess = newSyncProcess;
310
- Object.keys(tree).forEach(function (k) {
311
- if (k !== 'default' && !exports.hasOwnProperty(k)) Object.defineProperty(exports, k, {
312
- enumerable: true,
313
- get: function () { return tree[k]; }
314
- });
315
- });
316
370
 
317
371
  Object.defineProperty(exports, '__esModule', { value: true });
318
372
 
@@ -1,2 +1,2 @@
1
- // @statewalker/fsm v0.10.1 https://github.com/statewalker/statewalker-tree Copyright 2022 Mikhail Kotelnikov
2
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("@statewalker/tree")):"function"==typeof define&&define.amd?define(["exports","@statewalker/tree"],t):t(((e="undefined"!=typeof globalThis?globalThis:e||self).statewalker=e.statewalker||{},e.statewalker.fsm=e.statewalker.fsm||{}),e.statewalker.tree)}(this,(function(e,t){"use strict";function n(e={}){const{key:t,transitions:s=[],states:r={},options:o,...c}=e,i={key:t,transitions:{},states:{},options:Object.assign(o||{},c)};for(const e of s){let t,n,s;if(Array.isArray(e))t=e[0],n=e[1],s=e[2];else{const{from:r,event:o,to:c}=e;t=r,n=o,s=c}(i.transitions[t]=i.transitions[t]||{})[n]=s}if(Array.isArray(r))for(const e of r)i.states[e.key]=n(e);else if(r)for(const e of Object.keys(r))i.states[e]=n(r[e]);return i}var s={STATE_ANY:"*",STATE_INITIAL:"",STATE_FINAL:"",EVENT_ANY:"*",EVENT_EMPTY:""};function r(e,t){let n;for(let s=e.stack.length-1;!n&&s>=0;s--){n=(e.stack[s].descriptor.states||{})[t]}return n||{transitions:{},states:{}}}function o(e,t,n){const r=[[t,n],[s.STATE_ANY,n],[t,s.EVENT_ANY],[s.STATE_ANY,s.EVENT_ANY]];let o;for(let t=0,n=r.length;void 0===o&&t<n;t++){const[n,c]=r[t],i=e.transitions[n];i&&(o=i[c],void 0===o&&(o=i[s.EVENT_ANY]))}return void 0!==o?o:s.STATE_FINAL}function c({config:e,descriptor:c,newState:i=(e=>e),newProcess:a=(e=>({descriptor:e,status:0,stack:[],event:void 0,current:void 0}))}){const f=a(c=c||n(e));return[f,()=>{let e,n;if(f.status)if(f.stack.length){const c=f.event.key||s.EVENT_EMPTY;e=o(f.stack[f.stack.length-1].descriptor,f.status&t.MODE.ENTER?s.STATE_INITIAL:f.current?f.current.key:s.STATE_FINAL,c),e&&(n=r(f,e))}else e=s.STATE_FINAL;else n=f.descriptor,e=n.key;return e?i({process:f,key:e,descriptor:n}):null}]}e.KEYS=s,e.buildStateDescriptor=n,e.checkProcessDescriptor=function(e){return function e(t,n=[],r=[]){r=[...r,t.key];const o=Object.assign({key:t.key},function(e){const t={},n={};for(const[s,r]of Object.entries(e.transitions)){t[s]=(t[s]||0)+1;for(const e of Object.values(r))n[e]=(n[e]||0)+1}const r=[],o=[];for(const e of Object.keys(t))e===s.STATE_ANY||e===s.STATE_FINAL||e===s.STATE_INITIAL||e in n||r.push(e);for(const e of Object.keys(n))e===s.STATE_FINAL||e in t||o.push(e);return{unreachableStates:r,deadendStates:o}}(t));if(o.deadendStates.length||o.unreachableStates.length){const e={path:r};n.push(e),o.deadendStates.length&&(e.deadendStates=o.deadendStates),o.unreachableStates.length&&(e.unreachableStates=o.unreachableStates)}for(const s of Object.values(t.states))e(s,n,r);return n}(e)},e.getAllStateKeys=function(e){const t={},n=e=>t[e]=(t[e]||0)+1;return function e(t){n(t.key);for(const[e,s]of Object.entries(t.transitions)){n(e);for(const e of Object.values(s))n(e)}for(const[s,r]of Object.entries(t.states))n(s),e(r)}(e),Object.keys(t).sort()},e.getAllTransitions=function(e){const t=[];let n=e.current?e.current.key:s.STATE_INITIAL;for(let r=e.stack.length-1;r>=0;r--){const o=e.stack[r],c=o.key,i=o.descriptor;for(const e of[n,s.STATE_ANY]){const n=i.transitions[e];if(n)for(const[s,r]of Object.entries(n))t.push([c,e,s,r])}n=c}return t},e.getStateDescriptor=r,e.getTargetStateKey=o,e.newAsyncProcess=function({config:e,descriptor:n,before:s,after:r,newProcess:o,newState:i,handleError:a=console.error}){const[f,u]=c({config:e,descriptor:n,newProcess:o,newState:i}),l=t.newAsyncTreeWalker(s,r,f);return f.finished=!1,f.next=e=>(f.nextEvent=e,f.running=f.running||Promise.resolve().then((async()=>{try{for(f.event=f.nextEvent,f.nextEvent=void 0;!f.finished&&f.event;)if(f.finished=!await l(u),f.status&t.MODE.EXIT&&f.nextEvent)f.event=f.nextEvent,f.nextEvent=void 0;else if(f.status&t.MODE.LEAF)break}catch(e){a(e)}})).finally((()=>f.running=void 0)).then((()=>!f.finished))),f},e.newProcessInstance=c,e.newSyncProcess=function({config:e,descriptor:n,before:s,after:r,newProcess:o,newState:i}){const[a,f]=c({config:e,descriptor:n,newProcess:o,newState:i}),u=t.newTreeWalker(s,r,a);return a.dispatch=a.next=e=>{for("string"==typeof e&&(e={key:e}),a.event=e;a.event;){if(!u(f))return!1;if(a.status&t.MODE.LEAF)return!0}},a},Object.keys(t).forEach((function(n){"default"===n||e.hasOwnProperty(n)||Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[n]}})})),Object.defineProperty(e,"__esModule",{value:!0})}));
1
+ // @statewalker/fsm v0.13.0 https://github.com/statewalker/statewalker-tree Copyright 2022 Mikhail Kotelnikov
2
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("@statewalker/tree")):"function"==typeof define&&define.amd?define(["exports","@statewalker/tree"],t):t(((e="undefined"!=typeof globalThis?globalThis:e||self).statewalker=e.statewalker||{},e.statewalker.fsm=e.statewalker.fsm||{}),e.statewalker.tree)}(this,(function(e,t){"use strict";function n(e={}){const{key:t,transitions:s=[],states:r={},options:o,...c}=e,a={key:t,transitions:{},states:{},options:Object.assign(o||{},c)};for(const e of s){let t,n,s;if(Array.isArray(e))t=e[0],n=e[1],s=e[2];else{const{from:r,event:o,to:c}=e;t=r,n=o,s=c}(a.transitions[t]=a.transitions[t]||{})[n]=s}if(Array.isArray(r))for(const e of r)a.states[e.key]=n(e);else if(r)for(const e of Object.keys(r))a.states[e]=n(r[e]);return a}var s={STATE_ANY:"*",STATE_INITIAL:"",STATE_FINAL:"",EVENT_ANY:"*",EVENT_EMPTY:""};function r(e,t){let n;for(let s=e.stack.length-1;!n&&s>=0;s--){n=(e.stack[s].descriptor.states||{})[t]}return n||{transitions:{},states:{}}}function o(e,t,n){const r=[[t,n],[s.STATE_ANY,n],[t,s.EVENT_ANY],[s.STATE_ANY,s.EVENT_ANY]];let o;for(let t=0,n=r.length;void 0===o&&t<n;t++){const[n,c]=r[t],a=e.transitions[n];a&&(o=a[c],void 0===o&&(o=a[s.EVENT_ANY]))}return void 0!==o?o:s.STATE_FINAL}function c({config:e,descriptor:c,newState:a=(e=>e),newProcess:i=(e=>({descriptor:e,status:0,stack:[],event:void 0,current:void 0}))}){const f=i(c=c||n(e));return[f,()=>{let e,n;if(f.status)if(f.stack.length){const c=f.event.key||s.EVENT_EMPTY;e=o(f.stack[f.stack.length-1].descriptor,f.status&t.MODE.ENTER?s.STATE_INITIAL:f.current?f.current.key:s.STATE_FINAL,c),e&&(n=r(f,e))}else e=s.STATE_FINAL;else n=f.descriptor,e=n.key;return e?a({process:f,key:e,descriptor:n}):null}]}e.KEYS=s,e.buildStateDescriptor=n,e.checkProcessDescriptor=function(e){return function e(t,n=[],r=[]){r=[...r,t.key];const o=Object.assign({key:t.key},function(e){const t={},n={};for(const[s,r]of Object.entries(e.transitions)){t[s]=(t[s]||0)+1;for(const e of Object.values(r))n[e]=(n[e]||0)+1}const r=[],o=[];for(const e of Object.keys(t))e===s.STATE_ANY||e===s.STATE_FINAL||e===s.STATE_INITIAL||e in n||r.push(e);for(const e of Object.keys(n))e===s.STATE_FINAL||e in t||o.push(e);return{unreachableStates:r,deadendStates:o}}(t));if(o.deadendStates.length||o.unreachableStates.length){const e={path:r};n.push(e),o.deadendStates.length&&(e.deadendStates=o.deadendStates),o.unreachableStates.length&&(e.unreachableStates=o.unreachableStates)}for(const s of Object.values(t.states))e(s,n,r);return n}(e)},e.getAllStateKeys=function(e){const t={},n=e=>t[e]=(t[e]||0)+1;return function e(t){n(t.key);for(const[e,s]of Object.entries(t.transitions)){n(e);for(const e of Object.values(s))n(e)}for(const[s,r]of Object.entries(t.states))n(s),e(r)}(e),Object.keys(t).sort()},e.getAllTransitions=function(e){const t=[];let n=e.current?e.current.key:s.STATE_INITIAL;for(let r=e.stack.length-1;r>=0;r--){const o=e.stack[r],c=o.key,a=o.descriptor;for(const e of[n,s.STATE_ANY]){const n=a.transitions[e];if(n)for(const[s,r]of Object.entries(n))t.push([c,e,s,r])}n=c}return t},e.getStateDescriptor=r,e.getTargetStateKey=o,e.initAsyncProcess=function({config:e,handler:n,handleError:s=console.error}){let r;return r=function({config:e,descriptor:n,before:s,after:r,newProcess:o,newState:a,handleError:i=console.error}){const[f,u]=c({config:e,descriptor:n,newProcess:o,newState:a}),l=t.newAsyncTreeWalker(s,r,f);return f.finished=!1,f.next=e=>(f.nextEvent=e,f.running=f.running||Promise.resolve().then((async()=>{try{for(f.event=f.nextEvent,f.nextEvent=void 0;!f.finished&&f.event;)if(f.finished=!await l(u),f.status&t.MODE.EXIT&&f.nextEvent)f.event=f.nextEvent,f.nextEvent=void 0;else if(f.status&t.MODE.LEAF)break}catch(e){i(e)}})).finally((()=>f.running=void 0)).then((()=>!f.finished))),f}({config:e,before:async e=>{e._started=!0;const t=e.current;n(t),await t.init.run()},after:async e=>{const t=e.current;await t.done.run()},newProcess:t=>({descriptor:t,config:e,status:0,stack:[],event:void 0,current:void 0}),newState:e=>(e.init=o(),e.done=o(),e.dispatch=(...t)=>e.process.dispatch(...t),e.getEvent=()=>e.process.event||{},e.getEventKey=()=>e.getEvent().key,e),handleError:s}),r.dispatch=(e,t={})=>{r.next({key:e,params:t})},r;function o(){const e=[];return Object.assign((t=>e.push(t)),{run:async(...t)=>{for(let n=0;n<e.length;n++)try{e[n]&&await e[n](...t)}catch(e){console.error(e),await s(e)}}})}},e.newProcessInstance=c,e.newSyncProcess=function({config:e,descriptor:n,before:s,after:r,newProcess:o,newState:a}){const[i,f]=c({config:e,descriptor:n,newProcess:o,newState:a}),u=t.newTreeWalker(s,r,i);return i.dispatch=i.next=e=>{for("string"==typeof e&&(e={key:e}),i.event=e;i.event;){if(!u(f))return!1;if(i.status&t.MODE.LEAF)return!0}},i},Object.defineProperty(e,"__esModule",{value:!0})}));
package/dist/index.js CHANGED
@@ -1,6 +1,5 @@
1
- // @statewalker/fsm v0.10.1 https://github.com/statewalker/statewalker-tree Copyright 2022 Mikhail Kotelnikov
1
+ // @statewalker/fsm v0.13.0 https://github.com/statewalker/statewalker-tree Copyright 2022 Mikhail Kotelnikov
2
2
  import { MODE, newAsyncTreeWalker, newTreeWalker } from '@statewalker/tree';
3
- export * from '@statewalker/tree';
4
3
 
5
4
  function buildStateDescriptor(config = {}) {
6
5
  const { key, transitions = [], states = {}, options, ...params } = config;
@@ -135,38 +134,6 @@ function getAllStateKeys(descriptor) {
135
134
  }
136
135
  }
137
136
 
138
- /**
139
- * Returns all possible transitions from the current state of the process.
140
- * @param {Object} options - method parameters
141
- * @param {Process} options.process - the current process for which
142
- * all transitions should be returned
143
- * @returns {Array<Object>} - an array of all transitions;
144
- * each entry in this returned array contains the following keys:
145
- * 1) parentStateKey - key of the state where the transition can be activated
146
- * 2) sourceStateKey - the initial transition state
147
- * 3) eventKey - key of the event activating transition
148
- * 4) targetStateKey - the resulting state for this transition
149
- *
150
- */
151
- function getAllTransitions(process) {
152
- const list = [];
153
- let stateKey = process.current ? process.current.key : KEYS.STATE_INITIAL;
154
- for (let i = process.stack.length - 1; i >= 0; i--) {
155
- const parentState = process.stack[i];
156
- const parentStateKey = parentState.key;
157
- const descriptor = parentState.descriptor;
158
- for (const sKey of [stateKey, KEYS.STATE_ANY]) {
159
- const stateTransitions = descriptor.transitions[sKey];
160
- if (!stateTransitions) continue;
161
- for (const[eventKey, targetKey] of Object.entries(stateTransitions)) {
162
- list.push([parentStateKey, sKey, eventKey, targetKey]);
163
- }
164
- }
165
- stateKey = parentStateKey;
166
- }
167
- return list;
168
- }
169
-
170
137
  function getTargetStateKey(descriptor, stateKey, eventKey) {
171
138
  const pairs = [
172
139
  [stateKey, eventKey],
@@ -269,6 +236,98 @@ function newAsyncProcess({
269
236
  return process;
270
237
  }
271
238
 
239
+ function initProcess({ config, handler, handleError = console.error }) {
240
+ let process;
241
+ const before = async (process) => {
242
+ process._started = true;
243
+ const state = process.current;
244
+ handler(state);
245
+ await state.init.run();
246
+ };
247
+ const after = async (process) => {
248
+ const state = process.current;
249
+ await state.done.run();
250
+ };
251
+
252
+ process = newAsyncProcess({
253
+ config,
254
+ before,
255
+ after,
256
+ newProcess: (descriptor) => {
257
+ return {
258
+ descriptor,
259
+ config,
260
+ status: 0,
261
+ stack: [],
262
+ event: undefined,
263
+ current: undefined
264
+ };
265
+ },
266
+ newState: (state) => {
267
+ state.init = newStateMethod();
268
+ state.done = newStateMethod();
269
+ state.dispatch = (...args) => state.process.dispatch(...args);
270
+ state.getEvent = () => state.process.event || {};
271
+ state.getEventKey = () => state.getEvent().key;
272
+ return state;
273
+ },
274
+ handleError
275
+ });
276
+ process.dispatch = (key, params = {}) => {
277
+ process.next({ key, params });
278
+ };
279
+ return process;
280
+
281
+ function newStateMethod() {
282
+ const list = [];
283
+ return Object.assign((m) => list.push(m), {
284
+ run: async (...args) => {
285
+ // We use the index to iterate over the list: the list can be updated by called methods
286
+ for (let i = 0; i < list.length; i++) {
287
+ try {
288
+ list[i] && (await list[i](...args));
289
+ } catch (error) {
290
+ console.error(error);
291
+ await handleError(error);
292
+ }
293
+ }
294
+ }
295
+ });
296
+ }
297
+ }
298
+
299
+ /**
300
+ * Returns all possible transitions from the current state of the process.
301
+ * @param {Object} options - method parameters
302
+ * @param {Process} options.process - the current process for which
303
+ * all transitions should be returned
304
+ * @returns {Array<Object>} - an array of all transitions;
305
+ * each entry in this returned array contains the following keys:
306
+ * 1) parentStateKey - key of the state where the transition can be activated
307
+ * 2) sourceStateKey - the initial transition state
308
+ * 3) eventKey - key of the event activating transition
309
+ * 4) targetStateKey - the resulting state for this transition
310
+ *
311
+ */
312
+ function getAllTransitions(process) {
313
+ const list = [];
314
+ let stateKey = process.current ? process.current.key : KEYS.STATE_INITIAL;
315
+ for (let i = process.stack.length - 1; i >= 0; i--) {
316
+ const parentState = process.stack[i];
317
+ const parentStateKey = parentState.key;
318
+ const descriptor = parentState.descriptor;
319
+ for (const sKey of [stateKey, KEYS.STATE_ANY]) {
320
+ const stateTransitions = descriptor.transitions[sKey];
321
+ if (!stateTransitions) continue;
322
+ for (const[eventKey, targetKey] of Object.entries(stateTransitions)) {
323
+ list.push([parentStateKey, sKey, eventKey, targetKey]);
324
+ }
325
+ }
326
+ stateKey = parentStateKey;
327
+ }
328
+ return list;
329
+ }
330
+
272
331
  function newSyncProcess({
273
332
  config,
274
333
  descriptor,
@@ -294,4 +353,4 @@ function newSyncProcess({
294
353
  return process;
295
354
  }
296
355
 
297
- export { KEYS, buildStateDescriptor, checkProcessDescriptor, getAllStateKeys, getAllTransitions, getStateDescriptor, getTargetStateKey, newAsyncProcess, newProcessInstance, newSyncProcess };
356
+ export { KEYS, buildStateDescriptor, checkProcessDescriptor, getAllStateKeys, getAllTransitions, getStateDescriptor, getTargetStateKey, initProcess as initAsyncProcess, newProcessInstance, newSyncProcess };
package/dist/index.min.js CHANGED
@@ -1,2 +1,2 @@
1
- // @statewalker/fsm v0.10.1 https://github.com/statewalker/statewalker-tree Copyright 2022 Mikhail Kotelnikov
2
- import{MODE as t,newAsyncTreeWalker as e,newTreeWalker as n}from"@statewalker/tree";export*from"@statewalker/tree";function s(t={}){const{key:e,transitions:n=[],states:r={},options:o,...c}=t,i={key:e,transitions:{},states:{},options:Object.assign(o||{},c)};for(const t of n){let e,n,s;if(Array.isArray(t))e=t[0],n=t[1],s=t[2];else{const{from:r,event:o,to:c}=t;e=r,n=o,s=c}(i.transitions[e]=i.transitions[e]||{})[n]=s}if(Array.isArray(r))for(const t of r)i.states[t.key]=s(t);else if(r)for(const t of Object.keys(r))i.states[t]=s(r[t]);return i}var r={STATE_ANY:"*",STATE_INITIAL:"",STATE_FINAL:"",EVENT_ANY:"*",EVENT_EMPTY:""};function o(t){return function t(e,n=[],s=[]){s=[...s,e.key];const o=Object.assign({key:e.key},function(t){const e={},n={};for(const[s,r]of Object.entries(t.transitions)){e[s]=(e[s]||0)+1;for(const t of Object.values(r))n[t]=(n[t]||0)+1}const s=[],o=[];for(const t of Object.keys(e))t===r.STATE_ANY||t===r.STATE_FINAL||t===r.STATE_INITIAL||t in n||s.push(t);for(const t of Object.keys(n))t===r.STATE_FINAL||t in e||o.push(t);return{unreachableStates:s,deadendStates:o}}(e));if(o.deadendStates.length||o.unreachableStates.length){const t={path:s};n.push(t),o.deadendStates.length&&(t.deadendStates=o.deadendStates),o.unreachableStates.length&&(t.unreachableStates=o.unreachableStates)}for(const r of Object.values(e.states))t(r,n,s);return n}(t)}function c(t,e){let n;for(let s=t.stack.length-1;!n&&s>=0;s--){n=(t.stack[s].descriptor.states||{})[e]}return n||{transitions:{},states:{}}}function i(t){const e={},n=t=>e[t]=(e[t]||0)+1;return function t(e){n(e.key);for(const[t,s]of Object.entries(e.transitions)){n(t);for(const t of Object.values(s))n(t)}for(const[s,r]of Object.entries(e.states))n(s),t(r)}(t),Object.keys(e).sort()}function a(t){const e=[];let n=t.current?t.current.key:r.STATE_INITIAL;for(let s=t.stack.length-1;s>=0;s--){const o=t.stack[s],c=o.key,i=o.descriptor;for(const t of[n,r.STATE_ANY]){const n=i.transitions[t];if(n)for(const[s,r]of Object.entries(n))e.push([c,t,s,r])}n=c}return e}function f(t,e,n){const s=[[e,n],[r.STATE_ANY,n],[e,r.EVENT_ANY],[r.STATE_ANY,r.EVENT_ANY]];let o;for(let e=0,n=s.length;void 0===o&&e<n;e++){const[n,c]=s[e],i=t.transitions[n];i&&(o=i[c],void 0===o&&(o=i[r.EVENT_ANY]))}return void 0!==o?o:r.STATE_FINAL}function u({config:e,descriptor:n,newState:o=(t=>t),newProcess:i=(t=>({descriptor:t,status:0,stack:[],event:void 0,current:void 0}))}){const a=i(n=n||s(e));return[a,()=>{let e,n;if(a.status)if(a.stack.length){const s=a.event.key||r.EVENT_EMPTY;e=f(a.stack[a.stack.length-1].descriptor,a.status&t.ENTER?r.STATE_INITIAL:a.current?a.current.key:r.STATE_FINAL,s),e&&(n=c(a,e))}else e=r.STATE_FINAL;else n=a.descriptor,e=n.key;return e?o({process:a,key:e,descriptor:n}):null}]}function T({config:n,descriptor:s,before:r,after:o,newProcess:c,newState:i,handleError:a=console.error}){const[f,T]=u({config:n,descriptor:s,newProcess:c,newState:i}),E=e(r,o,f);return f.finished=!1,f.next=e=>(f.nextEvent=e,f.running=f.running||Promise.resolve().then((async()=>{try{for(f.event=f.nextEvent,f.nextEvent=void 0;!f.finished&&f.event;)if(f.finished=!await E(T),f.status&t.EXIT&&f.nextEvent)f.event=f.nextEvent,f.nextEvent=void 0;else if(f.status&t.LEAF)break}catch(t){a(t)}})).finally((()=>f.running=void 0)).then((()=>!f.finished))),f}function E({config:e,descriptor:s,before:r,after:o,newProcess:c,newState:i}){const[a,f]=u({config:e,descriptor:s,newProcess:c,newState:i}),T=n(r,o,a);return a.dispatch=a.next=e=>{for("string"==typeof e&&(e={key:e}),a.event=e;a.event;){if(!T(f))return!1;if(a.status&t.LEAF)return!0}},a}export{r as KEYS,s as buildStateDescriptor,o as checkProcessDescriptor,i as getAllStateKeys,a as getAllTransitions,c as getStateDescriptor,f as getTargetStateKey,T as newAsyncProcess,u as newProcessInstance,E as newSyncProcess};
1
+ // @statewalker/fsm v0.13.0 https://github.com/statewalker/statewalker-tree Copyright 2022 Mikhail Kotelnikov
2
+ import{MODE as t,newAsyncTreeWalker as e,newTreeWalker as n}from"@statewalker/tree";function s(t={}){const{key:e,transitions:n=[],states:r={},options:o,...c}=t,i={key:e,transitions:{},states:{},options:Object.assign(o||{},c)};for(const t of n){let e,n,s;if(Array.isArray(t))e=t[0],n=t[1],s=t[2];else{const{from:r,event:o,to:c}=t;e=r,n=o,s=c}(i.transitions[e]=i.transitions[e]||{})[n]=s}if(Array.isArray(r))for(const t of r)i.states[t.key]=s(t);else if(r)for(const t of Object.keys(r))i.states[t]=s(r[t]);return i}var r={STATE_ANY:"*",STATE_INITIAL:"",STATE_FINAL:"",EVENT_ANY:"*",EVENT_EMPTY:""};function o(t){return function t(e,n=[],s=[]){s=[...s,e.key];const o=Object.assign({key:e.key},function(t){const e={},n={};for(const[s,r]of Object.entries(t.transitions)){e[s]=(e[s]||0)+1;for(const t of Object.values(r))n[t]=(n[t]||0)+1}const s=[],o=[];for(const t of Object.keys(e))t===r.STATE_ANY||t===r.STATE_FINAL||t===r.STATE_INITIAL||t in n||s.push(t);for(const t of Object.keys(n))t===r.STATE_FINAL||t in e||o.push(t);return{unreachableStates:s,deadendStates:o}}(e));if(o.deadendStates.length||o.unreachableStates.length){const t={path:s};n.push(t),o.deadendStates.length&&(t.deadendStates=o.deadendStates),o.unreachableStates.length&&(t.unreachableStates=o.unreachableStates)}for(const r of Object.values(e.states))t(r,n,s);return n}(t)}function c(t,e){let n;for(let s=t.stack.length-1;!n&&s>=0;s--){n=(t.stack[s].descriptor.states||{})[e]}return n||{transitions:{},states:{}}}function i(t){const e={},n=t=>e[t]=(e[t]||0)+1;return function t(e){n(e.key);for(const[t,s]of Object.entries(e.transitions)){n(t);for(const t of Object.values(s))n(t)}for(const[s,r]of Object.entries(e.states))n(s),t(r)}(t),Object.keys(e).sort()}function a(t,e,n){const s=[[e,n],[r.STATE_ANY,n],[e,r.EVENT_ANY],[r.STATE_ANY,r.EVENT_ANY]];let o;for(let e=0,n=s.length;void 0===o&&e<n;e++){const[n,c]=s[e],i=t.transitions[n];i&&(o=i[c],void 0===o&&(o=i[r.EVENT_ANY]))}return void 0!==o?o:r.STATE_FINAL}function f({config:e,descriptor:n,newState:o=(t=>t),newProcess:i=(t=>({descriptor:t,status:0,stack:[],event:void 0,current:void 0}))}){const f=i(n=n||s(e));return[f,()=>{let e,n;if(f.status)if(f.stack.length){const s=f.event.key||r.EVENT_EMPTY;e=a(f.stack[f.stack.length-1].descriptor,f.status&t.ENTER?r.STATE_INITIAL:f.current?f.current.key:r.STATE_FINAL,s),e&&(n=c(f,e))}else e=r.STATE_FINAL;else n=f.descriptor,e=n.key;return e?o({process:f,key:e,descriptor:n}):null}]}function u({config:n,handler:s,handleError:r=console.error}){let o;return o=function({config:n,descriptor:s,before:r,after:o,newProcess:c,newState:i,handleError:a=console.error}){const[u,d]=f({config:n,descriptor:s,newProcess:c,newState:i}),l=e(r,o,u);return u.finished=!1,u.next=e=>(u.nextEvent=e,u.running=u.running||Promise.resolve().then((async()=>{try{for(u.event=u.nextEvent,u.nextEvent=void 0;!u.finished&&u.event;)if(u.finished=!await l(d),u.status&t.EXIT&&u.nextEvent)u.event=u.nextEvent,u.nextEvent=void 0;else if(u.status&t.LEAF)break}catch(t){a(t)}})).finally((()=>u.running=void 0)).then((()=>!u.finished))),u}({config:n,before:async t=>{t._started=!0;const e=t.current;s(e),await e.init.run()},after:async t=>{const e=t.current;await e.done.run()},newProcess:t=>({descriptor:t,config:n,status:0,stack:[],event:void 0,current:void 0}),newState:t=>(t.init=c(),t.done=c(),t.dispatch=(...e)=>t.process.dispatch(...e),t.getEvent=()=>t.process.event||{},t.getEventKey=()=>t.getEvent().key,t),handleError:r}),o.dispatch=(t,e={})=>{o.next({key:t,params:e})},o;function c(){const t=[];return Object.assign((e=>t.push(e)),{run:async(...e)=>{for(let n=0;n<t.length;n++)try{t[n]&&await t[n](...e)}catch(t){console.error(t),await r(t)}}})}}function d(t){const e=[];let n=t.current?t.current.key:r.STATE_INITIAL;for(let s=t.stack.length-1;s>=0;s--){const o=t.stack[s],c=o.key,i=o.descriptor;for(const t of[n,r.STATE_ANY]){const n=i.transitions[t];if(n)for(const[s,r]of Object.entries(n))e.push([c,t,s,r])}n=c}return e}function l({config:e,descriptor:s,before:r,after:o,newProcess:c,newState:i}){const[a,u]=f({config:e,descriptor:s,newProcess:c,newState:i}),d=n(r,o,a);return a.dispatch=a.next=e=>{for("string"==typeof e&&(e={key:e}),a.event=e;a.event;){if(!d(u))return!1;if(a.status&t.LEAF)return!0}},a}export{r as KEYS,s as buildStateDescriptor,o as checkProcessDescriptor,i as getAllStateKeys,d as getAllTransitions,c as getStateDescriptor,a as getTargetStateKey,u as initAsyncProcess,f as newProcessInstance,l as newSyncProcess};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statewalker/fsm",
3
- "version": "0.10.1",
3
+ "version": "0.13.0",
4
4
  "description": "HFSM Implementation",
5
5
  "keywords": [],
6
6
  "homepage": "https://github.com/statewalker/statewalker-tree",
package/src/index.js CHANGED
@@ -1,11 +1,10 @@
1
- export * from "@statewalker/tree";
2
1
  export { default as buildStateDescriptor } from "./buildStateDescriptor.js";
3
2
  export { default as checkProcessDescriptor } from "./checkProcessDescriptor.js";
4
3
  export { default as getStateDescriptor } from "./getStateDescriptor.js";
5
4
  export { default as getAllStateKeys } from "./getAllStateKeys.js";
6
- export { default as getAllTransitions } from "./getAllTransitions.js";
7
5
  export { default as getTargetStateKey } from "./getTargetStateKey.js";
8
6
  export { default as KEYS } from "./KEYS.js";
9
- export { default as newAsyncProcess } from "./newAsyncProcess.js";
7
+ export { default as initAsyncProcess } from "./initAsyncProcess.js";
8
+ export { default as getAllTransitions } from "./getAllTransitions.js";
10
9
  export { default as newProcessInstance } from "./newProcessInstance.js";
11
10
  export { default as newSyncProcess } from "./newSyncProcess.js";
@@ -0,0 +1,61 @@
1
+ import newAsyncProcess from "./newAsyncProcess.js";
2
+
3
+ export default function initProcess({ config, handler, handleError = console.error }) {
4
+ let process;
5
+ const before = async (process) => {
6
+ process._started = true;
7
+ const state = process.current;
8
+ handler(state);
9
+ await state.init.run();
10
+ };
11
+ const after = async (process) => {
12
+ const state = process.current;
13
+ await state.done.run();
14
+ };
15
+
16
+ process = newAsyncProcess({
17
+ config,
18
+ before,
19
+ after,
20
+ newProcess: (descriptor) => {
21
+ return {
22
+ descriptor,
23
+ config,
24
+ status: 0,
25
+ stack: [],
26
+ event: undefined,
27
+ current: undefined
28
+ };
29
+ },
30
+ newState: (state) => {
31
+ state.init = newStateMethod();
32
+ state.done = newStateMethod();
33
+ state.dispatch = (...args) => state.process.dispatch(...args);
34
+ state.getEvent = () => state.process.event || {};
35
+ state.getEventKey = () => state.getEvent().key;
36
+ return state;
37
+ },
38
+ handleError
39
+ });
40
+ process.dispatch = (key, params = {}) => {
41
+ process.next({ key, params });
42
+ };
43
+ return process;
44
+
45
+ function newStateMethod() {
46
+ const list = [];
47
+ return Object.assign((m) => list.push(m), {
48
+ run: async (...args) => {
49
+ // We use the index to iterate over the list: the list can be updated by called methods
50
+ for (let i = 0; i < list.length; i++) {
51
+ try {
52
+ list[i] && (await list[i](...args));
53
+ } catch (error) {
54
+ console.error(error);
55
+ await handleError(error);
56
+ }
57
+ }
58
+ }
59
+ });
60
+ }
61
+ }