@statewalker/fsm 0.9.1 → 0.9.2

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.
@@ -1,9 +1,9 @@
1
- // @statewalker/fsm v0.9.1 https://github.com/statewalker/statewalker Copyright 2021 Mikhail Kotelnikov
1
+ // @statewalker/fsm v0.9.2 https://github.com/statewalker/statewalker Copyright 2022 Mikhail Kotelnikov
2
2
  (function (global, factory) {
3
3
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
4
4
  typeof define === 'function' && define.amd ? define(['exports'], factory) :
5
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';
6
+ })(this, (function (exports) { 'use strict';
7
7
 
8
8
  const MODE = {
9
9
  NONE : 0,
@@ -15,48 +15,49 @@ const MODE = {
15
15
  ENTER: 1 | 2, // MODE.FIRST | MODE.NEXT
16
16
  EXIT : 4 | 8 // MODE.LEAF | MODE.LAST
17
17
  };
18
+ var MODE$1 = MODE;
18
19
 
19
20
  function newTreeWalker(
20
21
  before, after,
21
- context = { stack: [], status: MODE.NONE }
22
+ context = { stack: [], status: MODE$1.NONE }
22
23
  ) {
23
24
  return (getNode) => {
24
25
  let status = context.status;
25
- const prev = status & MODE.EXIT;
26
+ const prev = status & MODE$1.EXIT;
26
27
  if (prev) after(context);
27
- if (status & MODE.ENTER) context.stack.push(context.current);
28
+ if (status & MODE$1.ENTER) context.stack.push(context.current);
28
29
  let node = getNode(context);
29
30
  if (node) {
30
31
  context.current = node;
31
- status = context.status = prev ? MODE.NEXT : MODE.FIRST;
32
+ status = context.status = prev ? MODE$1.NEXT : MODE$1.FIRST;
32
33
  before(context);
33
34
  } else {
34
35
  node = context.current = context.stack.pop();
35
- status = context.status = node ? prev ? MODE.LAST : MODE.LEAF : MODE.NONE;
36
+ status = context.status = node ? prev ? MODE$1.LAST : MODE$1.LEAF : MODE$1.NONE;
36
37
  }
37
- return status !== MODE.NONE;
38
+ return status !== MODE$1.NONE;
38
39
  }
39
40
  }
40
41
 
41
42
  function newAsyncTreeWalker(
42
43
  before, after,
43
- context = { stack: [], status: MODE.NONE }
44
+ context = { stack: [], status: MODE$1.NONE }
44
45
  ) {
45
46
  return async (getNode) => {
46
47
  let status = context.status;
47
- const prev = status & MODE.EXIT;
48
+ const prev = status & MODE$1.EXIT;
48
49
  if (prev) await after(context);
49
- if (status & MODE.ENTER) await context.stack.push(context.current);
50
+ if (status & MODE$1.ENTER) await context.stack.push(context.current);
50
51
  let node = await getNode(context);
51
52
  if (node) {
52
53
  context.current = node;
53
- status = context.status = prev ? MODE.NEXT : MODE.FIRST;
54
+ status = context.status = prev ? MODE$1.NEXT : MODE$1.FIRST;
54
55
  await before(context);
55
56
  } else {
56
57
  node = context.current = await context.stack.pop();
57
- status = context.status = node ? prev ? MODE.LAST : MODE.LEAF : MODE.NONE;
58
+ status = context.status = node ? prev ? MODE$1.LAST : MODE$1.LEAF : MODE$1.NONE;
58
59
  }
59
- return status !== MODE.NONE;
60
+ return status !== MODE$1.NONE;
60
61
  }
61
62
  }
62
63
 
@@ -268,7 +269,7 @@ function newProcessInstance({
268
269
  } else {
269
270
  const eventKey = process.event.key || KEYS.EVENT_EMPTY;
270
271
  const parentDescriptor = process.stack[process.stack.length - 1].descriptor;
271
- const stateKey = process.status & MODE.ENTER
272
+ const stateKey = process.status & MODE$1.ENTER
272
273
  ? KEYS.STATE_INITIAL
273
274
  : process.current ? process.current.key : KEYS.STATE_FINAL;
274
275
  key = getTargetStateKey(parentDescriptor, stateKey, eventKey);
@@ -288,24 +289,43 @@ function newAsyncProcess({
288
289
  before, after,
289
290
  newProcess,
290
291
  newState,
292
+ handleError = console.error
291
293
  }) {
292
294
  const [process, loadNextState] = newProcessInstance({
293
295
  config,
294
296
  descriptor,
295
297
  newProcess,
296
- newState,
298
+ newState
297
299
  });
298
300
  const shift = newAsyncTreeWalker(before, after, process);
299
- process.running = Promise.resolve();
301
+ process.finished = false;
302
+ process.dispatch = (event) => {
303
+ 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;
316
+ }
317
+ process.finished = !await shift(loadNextState);
318
+ }
319
+ } catch (error) {
320
+ handleError(error);
321
+ }
322
+ }).finally(() => process.running = undefined);
323
+ };
324
+
300
325
  process.next = async (event) => {
326
+ process.dispatch(event);
301
327
  await process.running;
302
- return await (process.running = (async() => {
303
- process.event = event;
304
- while (process.event) {
305
- if (!await shift(loadNextState)) return false;
306
- if (process.status & MODE.LEAF) return true;
307
- }
308
- })())
328
+ return !process.finished;
309
329
  };
310
330
  return process;
311
331
  }
@@ -324,18 +344,18 @@ function newSyncProcess({
324
344
  newState,
325
345
  });
326
346
  const shift = newTreeWalker(before, after, process);
327
- process.next = (event) => {
347
+ process.dispatch = process.next =(event) => {
328
348
  process.event = event;
329
349
  while (process.event) {
330
350
  if (!shift(loadNextState)) return false;
331
- if (process.status & MODE.LEAF) return true;
351
+ if (process.status & MODE$1.LEAF) return true;
332
352
  }
333
353
  };
334
354
  return process;
335
355
  }
336
356
 
337
357
  exports.KEYS = KEYS;
338
- exports.MODE = MODE;
358
+ exports.MODE = MODE$1;
339
359
  exports.buildStateDescriptor = buildStateDescriptor;
340
360
  exports.checkProcessDescriptor = checkProcessDescriptor;
341
361
  exports.getAllStateKeys = getAllStateKeys;
@@ -350,4 +370,4 @@ exports.newTreeWalker = newTreeWalker;
350
370
 
351
371
  Object.defineProperty(exports, '__esModule', { value: true });
352
372
 
353
- })));
373
+ }));
@@ -1,2 +1,2 @@
1
- // @statewalker/fsm v0.9.1 https://github.com/statewalker/statewalker Copyright 2021 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";const e={NONE:0,FIRST:1,NEXT:2,LEAF:4,LAST:8,ENTER:3,EXIT:12};function s(t,s,n={stack:[],status:e.NONE}){return r=>{let o=n.status;const c=o&e.EXIT;c&&s(n),o&e.ENTER&&n.stack.push(n.current);let a=r(n);return a?(n.current=a,o=n.status=c?e.NEXT:e.FIRST,t(n)):(a=n.current=n.stack.pop(),o=n.status=a?c?e.LAST:e.LEAF:e.NONE),o!==e.NONE}}function n(t,s,n={stack:[],status:e.NONE}){return async r=>{let o=n.status;const c=o&e.EXIT;c&&await s(n),o&e.ENTER&&await n.stack.push(n.current);let a=await r(n);return a?(n.current=a,o=n.status=c?e.NEXT:e.FIRST,await t(n)):(a=n.current=await n.stack.pop(),o=n.status=a?c?e.LAST:e.LEAF:e.NONE),o!==e.NONE}}function r(t={}){const{key:e,transitions:s=[],states:n={},options:o,...c}=t,a={key:e,transitions:{},states:{},options:Object.assign(o||{},c)};for(const t of s){let e,s,n;if(Array.isArray(t))e=t[0],s=t[1],n=t[2];else{const{from:r,event:o,to:c}=t;e=r,s=o,n=c}(a.transitions[e]=a.transitions[e]||{})[s]=n}if(Array.isArray(n))for(const t of n)a.states[t.key]=r(t);else if(n)for(const t of Object.keys(n))a.states[t]=r(n[t]);return a}var o={STATE_ANY:"*",STATE_INITIAL:"",STATE_FINAL:"",EVENT_ANY:"*",EVENT_EMPTY:""};function c(t,e){let s;for(let n=t.stack.length-1;!s&&n>=0;n--){s=(t.stack[n].descriptor.states||{})[e]}return s||{transitions:{},states:{}}}function a(t,e,s){const n=[[e,s],[o.STATE_ANY,s],[e,o.EVENT_ANY],[o.STATE_ANY,o.EVENT_ANY]];let r;for(let e=0,s=n.length;void 0===r&&e<s;e++){const[s,c]=n[e],a=t.transitions[s];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:s,newState:n=(t=>t),newProcess:i=(t=>({descriptor:t,status:0,stack:[],event:void 0,current:void 0}))}){const u=i(s=s||r(t));return[u,()=>{let t,s;if(u.status)if(u.stack.length){const n=u.event.key||o.EVENT_EMPTY;t=a(u.stack[u.stack.length-1].descriptor,u.status&e.ENTER?o.STATE_INITIAL:u.current?u.current.key:o.STATE_FINAL,n),t&&(s=c(u,t))}else t=o.STATE_FINAL;else s=u.descriptor,t=s.key;return t?n({process:u,key:t,descriptor:s}):null}]}t.KEYS=o,t.MODE=e,t.buildStateDescriptor=r,t.checkProcessDescriptor=function(t){return function t(e,s=[],n=[]){n=[...n,e.key];const r=Object.assign({key:e.key},function(t){const e={},s={};for(const[n,r]of Object.entries(t.transitions)){e[n]=(e[n]||0)+1;for(const t of Object.values(r))s[t]=(s[t]||0)+1}const n=[],r=[];for(const t of Object.keys(e))t===o.STATE_ANY||t===o.STATE_FINAL||t===o.STATE_INITIAL||t in s||n.push(t);for(const t of Object.keys(s))t===o.STATE_FINAL||t in e||r.push(t);return{unreachableStates:n,deadendStates:r}}(e));if(r.deadendStates.length||r.unreachableStates.length){const t={path:n};s.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,s,n);return s}(t)},t.getAllStateKeys=function(t){const e={},s=t=>e[t]=(e[t]||0)+1;return function t(e){s(e.key);for(const[t,n]of Object.entries(e.transitions)){s(t);for(const t of Object.values(n))s(t)}for(const[n,r]of Object.entries(e.states))s(n),t(r)}(t),Object.keys(e).sort()},t.getAllTransitions=function(t){const e=[];let s=t.current?t.current.key:o.STATE_INITIAL;for(let n=t.stack.length-1;n>=0;n--){const r=t.stack[n],c=r.key,a=r.descriptor;for(const t of[s,o.STATE_ANY]){const s=a.transitions[t];if(s)for(const[n,r]of Object.entries(s))e.push([c,t,n,r])}s=c}return e},t.getStateDescriptor=c,t.getTargetStateKey=a,t.newAsyncProcess=function({config:t,descriptor:s,before:r,after:o,newProcess:c,newState:a}){const[u,f]=i({config:t,descriptor:s,newProcess:c,newState:a}),T=n(r,o,u);return u.running=Promise.resolve(),u.next=async t=>(await u.running,await(u.running=(async()=>{for(u.event=t;u.event;){if(!await T(f))return!1;if(u.status&e.LEAF)return!0}})())),u},t.newAsyncTreeWalker=n,t.newProcessInstance=i,t.newSyncProcess=function({config:t,descriptor:n,before:r,after:o,newProcess:c,newState:a}){const[u,f]=i({config:t,descriptor:n,newProcess:c,newState:a}),T=s(r,o,u);return u.next=t=>{for(u.event=t;u.event;){if(!T(f))return!1;if(u.status&e.LEAF)return!0}},u},t.newTreeWalker=s,Object.defineProperty(t,"__esModule",{value:!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,4 +1,4 @@
1
- // @statewalker/fsm v0.9.1 https://github.com/statewalker/statewalker Copyright 2021 Mikhail Kotelnikov
1
+ // @statewalker/fsm v0.9.2 https://github.com/statewalker/statewalker Copyright 2022 Mikhail Kotelnikov
2
2
  const MODE = {
3
3
  NONE : 0,
4
4
  FIRST: 1, // old=true; new=true => ENTER + ENTER
@@ -9,48 +9,49 @@ const MODE = {
9
9
  ENTER: 1 | 2, // MODE.FIRST | MODE.NEXT
10
10
  EXIT : 4 | 8 // MODE.LEAF | MODE.LAST
11
11
  };
12
+ var MODE$1 = MODE;
12
13
 
13
14
  function newTreeWalker(
14
15
  before, after,
15
- context = { stack: [], status: MODE.NONE }
16
+ context = { stack: [], status: MODE$1.NONE }
16
17
  ) {
17
18
  return (getNode) => {
18
19
  let status = context.status;
19
- const prev = status & MODE.EXIT;
20
+ const prev = status & MODE$1.EXIT;
20
21
  if (prev) after(context);
21
- if (status & MODE.ENTER) context.stack.push(context.current);
22
+ if (status & MODE$1.ENTER) context.stack.push(context.current);
22
23
  let node = getNode(context);
23
24
  if (node) {
24
25
  context.current = node;
25
- status = context.status = prev ? MODE.NEXT : MODE.FIRST;
26
+ status = context.status = prev ? MODE$1.NEXT : MODE$1.FIRST;
26
27
  before(context);
27
28
  } else {
28
29
  node = context.current = context.stack.pop();
29
- status = context.status = node ? prev ? MODE.LAST : MODE.LEAF : MODE.NONE;
30
+ status = context.status = node ? prev ? MODE$1.LAST : MODE$1.LEAF : MODE$1.NONE;
30
31
  }
31
- return status !== MODE.NONE;
32
+ return status !== MODE$1.NONE;
32
33
  }
33
34
  }
34
35
 
35
36
  function newAsyncTreeWalker(
36
37
  before, after,
37
- context = { stack: [], status: MODE.NONE }
38
+ context = { stack: [], status: MODE$1.NONE }
38
39
  ) {
39
40
  return async (getNode) => {
40
41
  let status = context.status;
41
- const prev = status & MODE.EXIT;
42
+ const prev = status & MODE$1.EXIT;
42
43
  if (prev) await after(context);
43
- if (status & MODE.ENTER) await context.stack.push(context.current);
44
+ if (status & MODE$1.ENTER) await context.stack.push(context.current);
44
45
  let node = await getNode(context);
45
46
  if (node) {
46
47
  context.current = node;
47
- status = context.status = prev ? MODE.NEXT : MODE.FIRST;
48
+ status = context.status = prev ? MODE$1.NEXT : MODE$1.FIRST;
48
49
  await before(context);
49
50
  } else {
50
51
  node = context.current = await context.stack.pop();
51
- status = context.status = node ? prev ? MODE.LAST : MODE.LEAF : MODE.NONE;
52
+ status = context.status = node ? prev ? MODE$1.LAST : MODE$1.LEAF : MODE$1.NONE;
52
53
  }
53
- return status !== MODE.NONE;
54
+ return status !== MODE$1.NONE;
54
55
  }
55
56
  }
56
57
 
@@ -262,7 +263,7 @@ function newProcessInstance({
262
263
  } else {
263
264
  const eventKey = process.event.key || KEYS.EVENT_EMPTY;
264
265
  const parentDescriptor = process.stack[process.stack.length - 1].descriptor;
265
- const stateKey = process.status & MODE.ENTER
266
+ const stateKey = process.status & MODE$1.ENTER
266
267
  ? KEYS.STATE_INITIAL
267
268
  : process.current ? process.current.key : KEYS.STATE_FINAL;
268
269
  key = getTargetStateKey(parentDescriptor, stateKey, eventKey);
@@ -282,24 +283,43 @@ function newAsyncProcess({
282
283
  before, after,
283
284
  newProcess,
284
285
  newState,
286
+ handleError = console.error
285
287
  }) {
286
288
  const [process, loadNextState] = newProcessInstance({
287
289
  config,
288
290
  descriptor,
289
291
  newProcess,
290
- newState,
292
+ newState
291
293
  });
292
294
  const shift = newAsyncTreeWalker(before, after, process);
293
- process.running = Promise.resolve();
295
+ process.finished = false;
296
+ process.dispatch = (event) => {
297
+ 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;
310
+ }
311
+ process.finished = !await shift(loadNextState);
312
+ }
313
+ } catch (error) {
314
+ handleError(error);
315
+ }
316
+ }).finally(() => process.running = undefined);
317
+ };
318
+
294
319
  process.next = async (event) => {
320
+ process.dispatch(event);
295
321
  await process.running;
296
- return await (process.running = (async() => {
297
- process.event = event;
298
- while (process.event) {
299
- if (!await shift(loadNextState)) return false;
300
- if (process.status & MODE.LEAF) return true;
301
- }
302
- })())
322
+ return !process.finished;
303
323
  };
304
324
  return process;
305
325
  }
@@ -318,14 +338,14 @@ function newSyncProcess({
318
338
  newState,
319
339
  });
320
340
  const shift = newTreeWalker(before, after, process);
321
- process.next = (event) => {
341
+ process.dispatch = process.next =(event) => {
322
342
  process.event = event;
323
343
  while (process.event) {
324
344
  if (!shift(loadNextState)) return false;
325
- if (process.status & MODE.LEAF) return true;
345
+ if (process.status & MODE$1.LEAF) return true;
326
346
  }
327
347
  };
328
348
  return process;
329
349
  }
330
350
 
331
- export { KEYS, MODE, buildStateDescriptor, checkProcessDescriptor, getAllStateKeys, getAllTransitions, getStateDescriptor, getTargetStateKey, newAsyncProcess, newAsyncTreeWalker, newProcessInstance, newSyncProcess, newTreeWalker };
351
+ export { KEYS, MODE$1 as MODE, buildStateDescriptor, checkProcessDescriptor, getAllStateKeys, getAllTransitions, getStateDescriptor, getTargetStateKey, newAsyncProcess, newAsyncTreeWalker, newProcessInstance, newSyncProcess, newTreeWalker };
@@ -1,2 +1,2 @@
1
- // @statewalker/fsm v0.9.1 https://github.com/statewalker/statewalker Copyright 2021 Mikhail Kotelnikov
2
- const 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 T({config:e,descriptor:s,before:r,after:o,newProcess:c,newState:a}){const[i,u]=f({config:e,descriptor:s,newProcess:c,newState:a}),T=n(r,o,i);return i.running=Promise.resolve(),i.next=async e=>(await i.running,await(i.running=(async()=>{for(i.event=e;i.event;){if(!await T(u))return!1;if(i.status&t.LEAF)return!0}})())),i}function E({config:n,descriptor:s,before:r,after:o,newProcess:c,newState:a}){const[i,u]=f({config:n,descriptor:s,newProcess:c,newState:a}),T=e(r,o,i);return i.next=e=>{for(i.event=e;i.event;){if(!T(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,T as newAsyncProcess,n as newAsyncTreeWalker,f as newProcessInstance,E as newSyncProcess,e as newTreeWalker};
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};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statewalker/fsm",
3
- "version": "0.9.1",
3
+ "version": "0.9.2",
4
4
  "description": "Utility graph methods (iterators, tree builders etc)",
5
5
  "keywords": [],
6
6
  "homepage": "https://github.com/statewalker/statewalker",
@@ -45,6 +45,5 @@
45
45
  "sideEffects": false,
46
46
  "publishConfig": {
47
47
  "access": "public"
48
- },
49
- "gitHead": "d1f5418f6f1f621d3e47378e6b5d3dd3aff9bdad"
48
+ }
50
49
  }
@@ -7,24 +7,43 @@ export default function newAsyncProcess({
7
7
  before, after,
8
8
  newProcess,
9
9
  newState,
10
+ handleError = console.error
10
11
  }) {
11
12
  const [process, loadNextState] = newProcessInstance({
12
13
  config,
13
14
  descriptor,
14
15
  newProcess,
15
- newState,
16
+ newState
16
17
  });
17
18
  const shift = newAsyncTreeWalker(before, after, process);
18
- process.running = Promise.resolve();
19
+ process.finished = false;
20
+ process.dispatch = (event) => {
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;
34
+ }
35
+ process.finished = !await shift(loadNextState);
36
+ }
37
+ } catch (error) {
38
+ handleError(error);
39
+ }
40
+ }).finally(() => process.running = undefined);
41
+ }
42
+
19
43
  process.next = async (event) => {
44
+ process.dispatch(event);
20
45
  await process.running;
21
- return await (process.running = (async() => {
22
- process.event = event;
23
- while (process.event) {
24
- if (!await shift(loadNextState)) return false;
25
- if (process.status & MODE.LEAF) return true;
26
- }
27
- })())
46
+ return !process.finished;
28
47
  }
29
48
  return process;
30
49
  }
@@ -15,7 +15,7 @@ export default function newSyncProcess({
15
15
  newState,
16
16
  });
17
17
  const shift = newTreeWalker(before, after, process);
18
- process.next = (event) => {
18
+ process.dispatch = process.next =(event) => {
19
19
  process.event = event;
20
20
  while (process.event) {
21
21
  if (!shift(loadNextState)) return false;
package/LICENSE DELETED
@@ -1,27 +0,0 @@
1
- Copyright 2010-2020 Mikhail Kotelnikov
2
- All rights reserved.
3
-
4
- Redistribution and use in source and binary forms, with or without modification,
5
- are permitted provided that the following conditions are met:
6
-
7
- * Redistributions of source code must retain the above copyright notice, this
8
- list of conditions and the following disclaimer.
9
-
10
- * Redistributions in binary form must reproduce the above copyright notice,
11
- this list of conditions and the following disclaimer in the documentation
12
- and/or other materials provided with the distribution.
13
-
14
- * Neither the name of the author nor the names of contributors may be used to
15
- endorse or promote products derived from this software without specific prior
16
- written permission.
17
-
18
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
22
- ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25
- ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.