@statewalker/fsm 0.9.2 → 0.10.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 statewalker
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1 +1 @@
1
- # Hierarchical Finite State Machine Implementation
1
+ # @statewalker/fsm: Hierarchical Finite State Machine
@@ -1,65 +1,9 @@
1
- // @statewalker/fsm v0.9.2 https://github.com/statewalker/statewalker Copyright 2022 Mikhail Kotelnikov
1
+ // @statewalker/fsm v0.10.1 https://github.com/statewalker/statewalker-tree Copyright 2022 Mikhail Kotelnikov
2
2
  (function (global, factory) {
3
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
4
- typeof define === 'function' && define.amd ? define(['exports'], factory) :
5
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.statewalker = global.statewalker || {}, global.statewalker.fsm = global.statewalker.fsm || {})));
6
- })(this, (function (exports) { 'use strict';
7
-
8
- const MODE = {
9
- NONE : 0,
10
- FIRST: 1, // old=true; new=true => ENTER + ENTER
11
- NEXT: 2, // old=false; new=true => EXIT + ENTER
12
- LEAF : 4, // old=true; new=false => ENTER + EXIT
13
- LAST : 8, // old=false; new=false => EXIT + EXIT
14
-
15
- ENTER: 1 | 2, // MODE.FIRST | MODE.NEXT
16
- EXIT : 4 | 8 // MODE.LEAF | MODE.LAST
17
- };
18
- var MODE$1 = MODE;
19
-
20
- function newTreeWalker(
21
- before, after,
22
- context = { stack: [], status: MODE$1.NONE }
23
- ) {
24
- return (getNode) => {
25
- let status = context.status;
26
- const prev = status & MODE$1.EXIT;
27
- if (prev) after(context);
28
- if (status & MODE$1.ENTER) context.stack.push(context.current);
29
- let node = getNode(context);
30
- if (node) {
31
- context.current = node;
32
- status = context.status = prev ? MODE$1.NEXT : MODE$1.FIRST;
33
- before(context);
34
- } else {
35
- node = context.current = context.stack.pop();
36
- status = context.status = node ? prev ? MODE$1.LAST : MODE$1.LEAF : MODE$1.NONE;
37
- }
38
- return status !== MODE$1.NONE;
39
- }
40
- }
41
-
42
- function newAsyncTreeWalker(
43
- before, after,
44
- context = { stack: [], status: MODE$1.NONE }
45
- ) {
46
- return async (getNode) => {
47
- let status = context.status;
48
- const prev = status & MODE$1.EXIT;
49
- if (prev) await after(context);
50
- if (status & MODE$1.ENTER) await context.stack.push(context.current);
51
- let node = await getNode(context);
52
- if (node) {
53
- context.current = node;
54
- status = context.status = prev ? MODE$1.NEXT : MODE$1.FIRST;
55
- await before(context);
56
- } else {
57
- node = context.current = await context.stack.pop();
58
- status = context.status = node ? prev ? MODE$1.LAST : MODE$1.LEAF : MODE$1.NONE;
59
- }
60
- return status !== MODE$1.NONE;
61
- }
62
- }
3
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@statewalker/tree')) :
4
+ typeof define === 'function' && define.amd ? define(['exports', '@statewalker/tree'], factory) :
5
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.statewalker = global.statewalker || {}, global.statewalker.fsm = global.statewalker.fsm || {}), global.statewalker.tree));
6
+ })(this, (function (exports, tree) { 'use strict';
63
7
 
64
8
  function buildStateDescriptor(config = {}) {
65
9
  const { key, transitions = [], states = {}, options, ...params } = config;
@@ -269,7 +213,7 @@ function newProcessInstance({
269
213
  } else {
270
214
  const eventKey = process.event.key || KEYS.EVENT_EMPTY;
271
215
  const parentDescriptor = process.stack[process.stack.length - 1].descriptor;
272
- const stateKey = process.status & MODE$1.ENTER
216
+ const stateKey = process.status & tree.MODE.ENTER
273
217
  ? KEYS.STATE_INITIAL
274
218
  : process.current ? process.current.key : KEYS.STATE_FINAL;
275
219
  key = getTargetStateKey(parentDescriptor, stateKey, eventKey);
@@ -297,40 +241,38 @@ function newAsyncProcess({
297
241
  newProcess,
298
242
  newState
299
243
  });
300
- const shift = newAsyncTreeWalker(before, after, process);
244
+ const shift = tree.newAsyncTreeWalker(before, after, process);
301
245
  process.finished = false;
302
- process.dispatch = (event) => {
246
+ process.next = (event) => {
303
247
  process.nextEvent = event;
304
- process.running = process.running || Promise.resolve().then(async () => {
305
- try {
306
- while (!process.finished) {
307
- // await Promise.resolve();
308
- // console.log('XXX', process.nextEvent, process.running)
309
- if (process.nextEvent) {
310
- // Consume the next event if exists.
311
- process.event = process.nextEvent;
312
- process.nextEvent = undefined;
313
- } else if (process.status & MODE$1.LEAF) {
314
- // Returns control when the process is in a "leaf" state (a state without children)
315
- break;
248
+ return process.running = process.running || Promise
249
+ .resolve()
250
+ .then(async () => {
251
+ try {
252
+ process.event = process.nextEvent;
253
+ process.nextEvent = undefined;
254
+ while (!process.finished && process.event) {
255
+ process.finished = !await shift(loadNextState);
256
+ if ((process.status & tree.MODE.EXIT) && process.nextEvent) {
257
+ // Consume the next event if it exists.
258
+ process.event = process.nextEvent;
259
+ process.nextEvent = undefined;
260
+ } else if (process.status & tree.MODE.LEAF) {
261
+ // Returns control when the process is in a "leaf" state (a state without children)
262
+ break;
263
+ }
316
264
  }
317
- process.finished = !await shift(loadNextState);
265
+ } catch (error) {
266
+ handleError(error);
318
267
  }
319
- } catch (error) {
320
- handleError(error);
321
- }
322
- }).finally(() => process.running = undefined);
323
- };
324
-
325
- process.next = async (event) => {
326
- process.dispatch(event);
327
- await process.running;
328
- return !process.finished;
268
+ })
269
+ .finally(() => process.running = undefined)
270
+ .then(() => !process.finished);
329
271
  };
330
272
  return process;
331
273
  }
332
274
 
333
- function newSyncProcess({
275
+ function newSyncProcess({
334
276
  config,
335
277
  descriptor,
336
278
  before, after,
@@ -343,19 +285,19 @@ function newSyncProcess({
343
285
  newProcess,
344
286
  newState,
345
287
  });
346
- const shift = newTreeWalker(before, after, process);
347
- process.dispatch = process.next =(event) => {
288
+ const shift = tree.newTreeWalker(before, after, process);
289
+ process.dispatch = process.next = (event) => {
290
+ if (typeof event === 'string') event = { key : event };
348
291
  process.event = event;
349
292
  while (process.event) {
350
293
  if (!shift(loadNextState)) return false;
351
- if (process.status & MODE$1.LEAF) return true;
294
+ if (process.status & tree.MODE.LEAF) return true;
352
295
  }
353
296
  };
354
297
  return process;
355
298
  }
356
299
 
357
300
  exports.KEYS = KEYS;
358
- exports.MODE = MODE$1;
359
301
  exports.buildStateDescriptor = buildStateDescriptor;
360
302
  exports.checkProcessDescriptor = checkProcessDescriptor;
361
303
  exports.getAllStateKeys = getAllStateKeys;
@@ -363,10 +305,14 @@ exports.getAllTransitions = getAllTransitions;
363
305
  exports.getStateDescriptor = getStateDescriptor;
364
306
  exports.getTargetStateKey = getTargetStateKey;
365
307
  exports.newAsyncProcess = newAsyncProcess;
366
- exports.newAsyncTreeWalker = newAsyncTreeWalker;
367
308
  exports.newProcessInstance = newProcessInstance;
368
309
  exports.newSyncProcess = newSyncProcess;
369
- exports.newTreeWalker = newTreeWalker;
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
+ });
370
316
 
371
317
  Object.defineProperty(exports, '__esModule', { value: true });
372
318
 
@@ -0,0 +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,59 +1,6 @@
1
- // @statewalker/fsm v0.9.2 https://github.com/statewalker/statewalker Copyright 2022 Mikhail Kotelnikov
2
- const MODE = {
3
- NONE : 0,
4
- FIRST: 1, // old=true; new=true => ENTER + ENTER
5
- NEXT: 2, // old=false; new=true => EXIT + ENTER
6
- LEAF : 4, // old=true; new=false => ENTER + EXIT
7
- LAST : 8, // old=false; new=false => EXIT + EXIT
8
-
9
- ENTER: 1 | 2, // MODE.FIRST | MODE.NEXT
10
- EXIT : 4 | 8 // MODE.LEAF | MODE.LAST
11
- };
12
- var MODE$1 = MODE;
13
-
14
- function newTreeWalker(
15
- before, after,
16
- context = { stack: [], status: MODE$1.NONE }
17
- ) {
18
- return (getNode) => {
19
- let status = context.status;
20
- const prev = status & MODE$1.EXIT;
21
- if (prev) after(context);
22
- if (status & MODE$1.ENTER) context.stack.push(context.current);
23
- let node = getNode(context);
24
- if (node) {
25
- context.current = node;
26
- status = context.status = prev ? MODE$1.NEXT : MODE$1.FIRST;
27
- before(context);
28
- } else {
29
- node = context.current = context.stack.pop();
30
- status = context.status = node ? prev ? MODE$1.LAST : MODE$1.LEAF : MODE$1.NONE;
31
- }
32
- return status !== MODE$1.NONE;
33
- }
34
- }
35
-
36
- function newAsyncTreeWalker(
37
- before, after,
38
- context = { stack: [], status: MODE$1.NONE }
39
- ) {
40
- return async (getNode) => {
41
- let status = context.status;
42
- const prev = status & MODE$1.EXIT;
43
- if (prev) await after(context);
44
- if (status & MODE$1.ENTER) await context.stack.push(context.current);
45
- let node = await getNode(context);
46
- if (node) {
47
- context.current = node;
48
- status = context.status = prev ? MODE$1.NEXT : MODE$1.FIRST;
49
- await before(context);
50
- } else {
51
- node = context.current = await context.stack.pop();
52
- status = context.status = node ? prev ? MODE$1.LAST : MODE$1.LEAF : MODE$1.NONE;
53
- }
54
- return status !== MODE$1.NONE;
55
- }
56
- }
1
+ // @statewalker/fsm v0.10.1 https://github.com/statewalker/statewalker-tree Copyright 2022 Mikhail Kotelnikov
2
+ import { MODE, newAsyncTreeWalker, newTreeWalker } from '@statewalker/tree';
3
+ export * from '@statewalker/tree';
57
4
 
58
5
  function buildStateDescriptor(config = {}) {
59
6
  const { key, transitions = [], states = {}, options, ...params } = config;
@@ -263,7 +210,7 @@ function newProcessInstance({
263
210
  } else {
264
211
  const eventKey = process.event.key || KEYS.EVENT_EMPTY;
265
212
  const parentDescriptor = process.stack[process.stack.length - 1].descriptor;
266
- const stateKey = process.status & MODE$1.ENTER
213
+ const stateKey = process.status & MODE.ENTER
267
214
  ? KEYS.STATE_INITIAL
268
215
  : process.current ? process.current.key : KEYS.STATE_FINAL;
269
216
  key = getTargetStateKey(parentDescriptor, stateKey, eventKey);
@@ -293,38 +240,36 @@ function newAsyncProcess({
293
240
  });
294
241
  const shift = newAsyncTreeWalker(before, after, process);
295
242
  process.finished = false;
296
- process.dispatch = (event) => {
243
+ process.next = (event) => {
297
244
  process.nextEvent = event;
298
- process.running = process.running || Promise.resolve().then(async () => {
299
- try {
300
- while (!process.finished) {
301
- // await Promise.resolve();
302
- // console.log('XXX', process.nextEvent, process.running)
303
- if (process.nextEvent) {
304
- // Consume the next event if exists.
305
- process.event = process.nextEvent;
306
- process.nextEvent = undefined;
307
- } else if (process.status & MODE$1.LEAF) {
308
- // Returns control when the process is in a "leaf" state (a state without children)
309
- break;
245
+ return process.running = process.running || Promise
246
+ .resolve()
247
+ .then(async () => {
248
+ try {
249
+ process.event = process.nextEvent;
250
+ process.nextEvent = undefined;
251
+ while (!process.finished && process.event) {
252
+ process.finished = !await shift(loadNextState);
253
+ if ((process.status & MODE.EXIT) && process.nextEvent) {
254
+ // Consume the next event if it exists.
255
+ process.event = process.nextEvent;
256
+ process.nextEvent = undefined;
257
+ } else if (process.status & MODE.LEAF) {
258
+ // Returns control when the process is in a "leaf" state (a state without children)
259
+ break;
260
+ }
310
261
  }
311
- process.finished = !await shift(loadNextState);
262
+ } catch (error) {
263
+ handleError(error);
312
264
  }
313
- } catch (error) {
314
- handleError(error);
315
- }
316
- }).finally(() => process.running = undefined);
317
- };
318
-
319
- process.next = async (event) => {
320
- process.dispatch(event);
321
- await process.running;
322
- return !process.finished;
265
+ })
266
+ .finally(() => process.running = undefined)
267
+ .then(() => !process.finished);
323
268
  };
324
269
  return process;
325
270
  }
326
271
 
327
- function newSyncProcess({
272
+ function newSyncProcess({
328
273
  config,
329
274
  descriptor,
330
275
  before, after,
@@ -338,14 +283,15 @@ function newSyncProcess({
338
283
  newState,
339
284
  });
340
285
  const shift = newTreeWalker(before, after, process);
341
- process.dispatch = process.next =(event) => {
286
+ process.dispatch = process.next = (event) => {
287
+ if (typeof event === 'string') event = { key : event };
342
288
  process.event = event;
343
289
  while (process.event) {
344
290
  if (!shift(loadNextState)) return false;
345
- if (process.status & MODE$1.LEAF) return true;
291
+ if (process.status & MODE.LEAF) return true;
346
292
  }
347
293
  };
348
294
  return process;
349
295
  }
350
296
 
351
- export { KEYS, MODE$1 as MODE, buildStateDescriptor, checkProcessDescriptor, getAllStateKeys, getAllTransitions, getStateDescriptor, getTargetStateKey, newAsyncProcess, newAsyncTreeWalker, newProcessInstance, newSyncProcess, newTreeWalker };
297
+ export { KEYS, buildStateDescriptor, checkProcessDescriptor, getAllStateKeys, getAllTransitions, getStateDescriptor, getTargetStateKey, newAsyncProcess, newProcessInstance, newSyncProcess };
@@ -0,0 +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};
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@statewalker/fsm",
3
- "version": "0.9.2",
4
- "description": "Utility graph methods (iterators, tree builders etc)",
3
+ "version": "0.10.1",
4
+ "description": "HFSM Implementation",
5
5
  "keywords": [],
6
- "homepage": "https://github.com/statewalker/statewalker",
6
+ "homepage": "https://github.com/statewalker/statewalker-tree",
7
7
  "author": {
8
8
  "name": "Mikhail Kotelnikov",
9
9
  "email": "mikhail.kotelnikov@gmail.com"
@@ -15,31 +15,31 @@
15
15
  "src/**/*.js",
16
16
  "index.js"
17
17
  ],
18
- "main": "dist/esm/statewalker-fsm-esm.js",
19
- "module": "dist/esm/statewalker-fsm-esm.js",
20
- "jsdelivr": "dist/cjs/statewalker-fsm.min.js",
21
- "unpkg": "dist/cjs/statewalker-fsm.min.js",
18
+ "dependencies": {
19
+ "@statewalker/tree": "0.10.0"
20
+ },
21
+ "devDependencies": {
22
+ "@statewalker/rollup": "^0.1.4",
23
+ "eslint": "^8",
24
+ "expect.js": "^0.3",
25
+ "mocha": "^10",
26
+ "rollup": "^2"
27
+ },
28
+ "module": "src/index.js",
22
29
  "exports": {
23
- "package.json": "./package.json",
24
- "umd": "./dist/cjs/statewalker-fsm.min.js",
25
- "require": "./dist/cjs/statewalker-fsm.js",
26
30
  "import": "./src/index.js",
27
- "default": "./dist/esm/statewalker-fsm-esm.js"
31
+ "umd": "./dist/index-umd.min.js",
32
+ "default": "./dist/index.js"
28
33
  },
29
34
  "repository": {
30
35
  "type": "git",
31
- "url": "https://github.com/statewalker/statewalker.git"
32
- },
33
- "devDependencies": {
34
- "@statewalker/tree": "^0.9.1"
36
+ "url": "git@github.com:statewalker/statewalker-fsm.git"
35
37
  },
36
38
  "scripts": {
37
- "eslint": "../../node_modules/eslint/bin/eslint.js src",
38
- "rollup": "../../node_modules/rollup/dist/bin/rollup -c",
39
- "test": "../../node_modules/mocha/bin/mocha -R spec ./test/index.js && yarn eslint",
40
- "prepare": "yarn rollup",
41
- "prepublishOnly": "rm -rf dist && yarn test && yarn rollup",
42
- "postpublish": "zip -j dist/statewalker-fsm.zip -- ../../LICENSE README.md dist/*.js"
39
+ "eslint": "eslint src",
40
+ "rollup": "rollup -c",
41
+ "test": "mocha -R spec ./test/index.js && yarn eslint",
42
+ "prepublishOnly": "rm -rf dist && yarn test && yarn rollup"
43
43
  },
44
44
  "license": "MIT",
45
45
  "sideEffects": false,
@@ -17,33 +17,31 @@ export default function newAsyncProcess({
17
17
  });
18
18
  const shift = newAsyncTreeWalker(before, after, process);
19
19
  process.finished = false;
20
- process.dispatch = (event) => {
20
+ process.next = (event) => {
21
21
  process.nextEvent = event;
22
- process.running = process.running || Promise.resolve().then(async () => {
23
- try {
24
- while (!process.finished) {
25
- // await Promise.resolve();
26
- // console.log('XXX', process.nextEvent, process.running)
27
- if (process.nextEvent) {
28
- // Consume the next event if exists.
29
- process.event = process.nextEvent;
30
- process.nextEvent = undefined;
31
- } else if (process.status & MODE.LEAF) {
32
- // Returns control when the process is in a "leaf" state (a state without children)
33
- break;
22
+ return process.running = process.running || Promise
23
+ .resolve()
24
+ .then(async () => {
25
+ try {
26
+ process.event = process.nextEvent;
27
+ process.nextEvent = undefined;
28
+ while (!process.finished && process.event) {
29
+ process.finished = !await shift(loadNextState);
30
+ if ((process.status & MODE.EXIT) && process.nextEvent) {
31
+ // Consume the next event if it exists.
32
+ process.event = process.nextEvent;
33
+ process.nextEvent = undefined;
34
+ } else if (process.status & MODE.LEAF) {
35
+ // Returns control when the process is in a "leaf" state (a state without children)
36
+ break;
37
+ }
34
38
  }
35
- process.finished = !await shift(loadNextState);
39
+ } catch (error) {
40
+ handleError(error);
36
41
  }
37
- } catch (error) {
38
- handleError(error);
39
- }
40
- }).finally(() => process.running = undefined);
41
- }
42
-
43
- process.next = async (event) => {
44
- process.dispatch(event);
45
- await process.running;
46
- return !process.finished;
42
+ })
43
+ .finally(() => process.running = undefined)
44
+ .then(() => !process.finished);
47
45
  }
48
46
  return process;
49
47
  }
@@ -1,7 +1,7 @@
1
1
  import { MODE, newTreeWalker } from "@statewalker/tree";
2
2
  import newProcessInstance from "./newProcessInstance.js";
3
3
 
4
- export default function newSyncProcess({
4
+ export default function newSyncProcess({
5
5
  config,
6
6
  descriptor,
7
7
  before, after,
@@ -15,7 +15,8 @@ export default function newSyncProcess({
15
15
  newState,
16
16
  });
17
17
  const shift = newTreeWalker(before, after, process);
18
- process.dispatch = process.next =(event) => {
18
+ process.dispatch = process.next = (event) => {
19
+ if (typeof event === 'string') event = { key : event };
19
20
  process.event = event;
20
21
  while (process.event) {
21
22
  if (!shift(loadNextState)) return false;
@@ -1,3 +0,0 @@
1
- {
2
- "type": "commonjs"
3
- }
@@ -1,2 +0,0 @@
1
- // @statewalker/fsm v0.9.2 https://github.com/statewalker/statewalker Copyright 2022 Mikhail Kotelnikov
2
- !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e(((t="undefined"!=typeof globalThis?globalThis:t||self).statewalker=t.statewalker||{},t.statewalker.fsm=t.statewalker.fsm||{}))}(this,(function(t){"use strict";var e={NONE:0,FIRST:1,NEXT:2,LEAF:4,LAST:8,ENTER:3,EXIT:12};function n(t,n,s={stack:[],status:e.NONE}){return r=>{let o=s.status;const c=o&e.EXIT;c&&n(s),o&e.ENTER&&s.stack.push(s.current);let a=r(s);return a?(s.current=a,o=s.status=c?e.NEXT:e.FIRST,t(s)):(a=s.current=s.stack.pop(),o=s.status=a?c?e.LAST:e.LEAF:e.NONE),o!==e.NONE}}function s(t,n,s={stack:[],status:e.NONE}){return async r=>{let o=s.status;const c=o&e.EXIT;c&&await n(s),o&e.ENTER&&await s.stack.push(s.current);let a=await r(s);return a?(s.current=a,o=s.status=c?e.NEXT:e.FIRST,await t(s)):(a=s.current=await s.stack.pop(),o=s.status=a?c?e.LAST:e.LEAF:e.NONE),o!==e.NONE}}function r(t={}){const{key:e,transitions:n=[],states:s={},options:o,...c}=t,a={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}(a.transitions[e]=a.transitions[e]||{})[n]=s}if(Array.isArray(s))for(const t of s)a.states[t.key]=r(t);else if(s)for(const t of Object.keys(s))a.states[t]=r(s[t]);return a}var o={STATE_ANY:"*",STATE_INITIAL:"",STATE_FINAL:"",EVENT_ANY:"*",EVENT_EMPTY:""};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 a(t,e,n){const s=[[e,n],[o.STATE_ANY,n],[e,o.EVENT_ANY],[o.STATE_ANY,o.EVENT_ANY]];let r;for(let e=0,n=s.length;void 0===r&&e<n;e++){const[n,c]=s[e],a=t.transitions[n];a&&(r=a[c],void 0===r&&(r=a[o.EVENT_ANY]))}return void 0!==r?r:o.STATE_FINAL}function i({config:t,descriptor:n,newState:s=(t=>t),newProcess:i=(t=>({descriptor:t,status:0,stack:[],event:void 0,current:void 0}))}){const f=i(n=n||r(t));return[f,()=>{let t,n;if(f.status)if(f.stack.length){const s=f.event.key||o.EVENT_EMPTY;t=a(f.stack[f.stack.length-1].descriptor,f.status&e.ENTER?o.STATE_INITIAL:f.current?f.current.key:o.STATE_FINAL,s),t&&(n=c(f,t))}else t=o.STATE_FINAL;else n=f.descriptor,t=n.key;return t?s({process:f,key:t,descriptor:n}):null}]}t.KEYS=o,t.MODE=e,t.buildStateDescriptor=r,t.checkProcessDescriptor=function(t){return function t(e,n=[],s=[]){s=[...s,e.key];const r=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=[],r=[];for(const t of Object.keys(e))t===o.STATE_ANY||t===o.STATE_FINAL||t===o.STATE_INITIAL||t in n||s.push(t);for(const t of Object.keys(n))t===o.STATE_FINAL||t in e||r.push(t);return{unreachableStates:s,deadendStates:r}}(e));if(r.deadendStates.length||r.unreachableStates.length){const t={path:s};n.push(t),r.deadendStates.length&&(t.deadendStates=r.deadendStates),r.unreachableStates.length&&(t.unreachableStates=r.unreachableStates)}for(const r of Object.values(e.states))t(r,n,s);return n}(t)},t.getAllStateKeys=function(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()},t.getAllTransitions=function(t){const e=[];let n=t.current?t.current.key:o.STATE_INITIAL;for(let s=t.stack.length-1;s>=0;s--){const r=t.stack[s],c=r.key,a=r.descriptor;for(const t of[n,o.STATE_ANY]){const n=a.transitions[t];if(n)for(const[s,r]of Object.entries(n))e.push([c,t,s,r])}n=c}return e},t.getStateDescriptor=c,t.getTargetStateKey=a,t.newAsyncProcess=function({config:t,descriptor:n,before:r,after:o,newProcess:c,newState:a,handleError:f=console.error}){const[u,T]=i({config:t,descriptor:n,newProcess:c,newState:a}),E=s(r,o,u);return u.finished=!1,u.dispatch=t=>{u.nextEvent=t,u.running=u.running||Promise.resolve().then((async()=>{try{for(;!u.finished;){if(u.nextEvent)u.event=u.nextEvent,u.nextEvent=void 0;else if(u.status&e.LEAF)break;u.finished=!await E(T)}}catch(t){f(t)}})).finally((()=>u.running=void 0))},u.next=async t=>(u.dispatch(t),await u.running,!u.finished),u},t.newAsyncTreeWalker=s,t.newProcessInstance=i,t.newSyncProcess=function({config:t,descriptor:s,before:r,after:o,newProcess:c,newState:a}){const[f,u]=i({config:t,descriptor:s,newProcess:c,newState:a}),T=n(r,o,f);return f.dispatch=f.next=t=>{for(f.event=t;f.event;){if(!T(u))return!1;if(f.status&e.LEAF)return!0}},f},t.newTreeWalker=n,Object.defineProperty(t,"__esModule",{value:!0})}));
@@ -1,2 +0,0 @@
1
- // @statewalker/fsm v0.9.2 https://github.com/statewalker/statewalker Copyright 2022 Mikhail Kotelnikov
2
- var t={NONE:0,FIRST:1,NEXT:2,LEAF:4,LAST:8,ENTER:3,EXIT:12};function e(e,n,s={stack:[],status:t.NONE}){return r=>{let o=s.status;const c=o&t.EXIT;c&&n(s),o&t.ENTER&&s.stack.push(s.current);let a=r(s);return a?(s.current=a,o=s.status=c?t.NEXT:t.FIRST,e(s)):(a=s.current=s.stack.pop(),o=s.status=a?c?t.LAST:t.LEAF:t.NONE),o!==t.NONE}}function n(e,n,s={stack:[],status:t.NONE}){return async r=>{let o=s.status;const c=o&t.EXIT;c&&await n(s),o&t.ENTER&&await s.stack.push(s.current);let a=await r(s);return a?(s.current=a,o=s.status=c?t.NEXT:t.FIRST,await e(s)):(a=s.current=await s.stack.pop(),o=s.status=a?c?t.LAST:t.LEAF:t.NONE),o!==t.NONE}}function s(t={}){const{key:e,transitions:n=[],states:r={},options:o,...c}=t,a={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}(a.transitions[e]=a.transitions[e]||{})[n]=s}if(Array.isArray(r))for(const t of r)a.states[t.key]=s(t);else if(r)for(const t of Object.keys(r))a.states[t]=s(r[t]);return a}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 a(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 i(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,a=o.descriptor;for(const t of[n,r.STATE_ANY]){const n=a.transitions[t];if(n)for(const[s,r]of Object.entries(n))e.push([c,t,s,r])}n=c}return e}function u(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],a=t.transitions[n];a&&(o=a[c],void 0===o&&(o=a[r.EVENT_ANY]))}return void 0!==o?o:r.STATE_FINAL}function f({config:e,descriptor:n,newState:o=(t=>t),newProcess:a=(t=>({descriptor:t,status:0,stack:[],event:void 0,current:void 0}))}){const i=a(n=n||s(e));return[i,()=>{let e,n;if(i.status)if(i.stack.length){const s=i.event.key||r.EVENT_EMPTY;e=u(i.stack[i.stack.length-1].descriptor,i.status&t.ENTER?r.STATE_INITIAL:i.current?i.current.key:r.STATE_FINAL,s),e&&(n=c(i,e))}else e=r.STATE_FINAL;else n=i.descriptor,e=n.key;return e?o({process:i,key:e,descriptor:n}):null}]}function E({config:e,descriptor:s,before:r,after:o,newProcess:c,newState:a,handleError:i=console.error}){const[u,E]=f({config:e,descriptor:s,newProcess:c,newState:a}),T=n(r,o,u);return u.finished=!1,u.dispatch=e=>{u.nextEvent=e,u.running=u.running||Promise.resolve().then((async()=>{try{for(;!u.finished;){if(u.nextEvent)u.event=u.nextEvent,u.nextEvent=void 0;else if(u.status&t.LEAF)break;u.finished=!await T(E)}}catch(t){i(t)}})).finally((()=>u.running=void 0))},u.next=async t=>(u.dispatch(t),await u.running,!u.finished),u}function T({config:n,descriptor:s,before:r,after:o,newProcess:c,newState:a}){const[i,u]=f({config:n,descriptor:s,newProcess:c,newState:a}),E=e(r,o,i);return i.dispatch=i.next=e=>{for(i.event=e;i.event;){if(!E(u))return!1;if(i.status&t.LEAF)return!0}},i}export{r as KEYS,t as MODE,s as buildStateDescriptor,o as checkProcessDescriptor,a as getAllStateKeys,i as getAllTransitions,c as getStateDescriptor,u as getTargetStateKey,E as newAsyncProcess,n as newAsyncTreeWalker,f as newProcessInstance,T as newSyncProcess,e as newTreeWalker};