@statewalker/fsm 0.9.1 → 0.9.4

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.4 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,29 +289,46 @@ 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();
300
- process.next = async (event) => {
301
- 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
- })())
301
+ process.finished = false;
302
+ process.next = (event) => {
303
+ process.nextEvent = event;
304
+ return process.running = process.running || Promise
305
+ .resolve()
306
+ .then(async () => {
307
+ try {
308
+ process.event = process.nextEvent;
309
+ process.nextEvent = undefined;
310
+ while (!process.finished && process.event) {
311
+ process.finished = !await shift(loadNextState);
312
+ if ((process.status & MODE$1.EXIT) && process.nextEvent) {
313
+ // Consume the next event if it exists.
314
+ process.event = process.nextEvent;
315
+ process.nextEvent = undefined;
316
+ } else if (process.status & MODE$1.LEAF) {
317
+ // Returns control when the process is in a "leaf" state (a state without children)
318
+ break;
319
+ }
320
+ }
321
+ } catch (error) {
322
+ handleError(error);
323
+ }
324
+ })
325
+ .finally(() => process.running = undefined)
326
+ .then(() => !process.finished);
309
327
  };
310
328
  return process;
311
329
  }
312
330
 
313
- function newSyncProcess({
331
+ function newSyncProcess({
314
332
  config,
315
333
  descriptor,
316
334
  before, after,
@@ -324,18 +342,19 @@ function newSyncProcess({
324
342
  newState,
325
343
  });
326
344
  const shift = newTreeWalker(before, after, process);
327
- process.next = (event) => {
345
+ process.dispatch = process.next = (event) => {
346
+ if (typeof event === 'string') event = { key : event };
328
347
  process.event = event;
329
348
  while (process.event) {
330
349
  if (!shift(loadNextState)) return false;
331
- if (process.status & MODE.LEAF) return true;
350
+ if (process.status & MODE$1.LEAF) return true;
332
351
  }
333
352
  };
334
353
  return process;
335
354
  }
336
355
 
337
356
  exports.KEYS = KEYS;
338
- exports.MODE = MODE;
357
+ exports.MODE = MODE$1;
339
358
  exports.buildStateDescriptor = buildStateDescriptor;
340
359
  exports.checkProcessDescriptor = checkProcessDescriptor;
341
360
  exports.getAllStateKeys = getAllStateKeys;
@@ -350,4 +369,4 @@ exports.newTreeWalker = newTreeWalker;
350
369
 
351
370
  Object.defineProperty(exports, '__esModule', { value: true });
352
371
 
353
- })));
372
+ }));
@@ -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.4 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,E]=i({config:t,descriptor:n,newProcess:c,newState:a}),T=s(r,o,u);return u.finished=!1,u.next=t=>(u.nextEvent=t,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 T(E),u.status&e.EXIT&&u.nextEvent)u.event=u.nextEvent,u.nextEvent=void 0;else if(u.status&e.LEAF)break}catch(t){f(t)}})).finally((()=>u.running=void 0)).then((()=>!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}),E=n(r,o,f);return f.dispatch=f.next=t=>{for("string"==typeof t&&(t={key:t}),f.event=t;f.event;){if(!E(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.4 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,29 +283,46 @@ 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();
294
- process.next = async (event) => {
295
- 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
- })())
295
+ process.finished = false;
296
+ process.next = (event) => {
297
+ process.nextEvent = event;
298
+ return process.running = process.running || Promise
299
+ .resolve()
300
+ .then(async () => {
301
+ try {
302
+ process.event = process.nextEvent;
303
+ process.nextEvent = undefined;
304
+ while (!process.finished && process.event) {
305
+ process.finished = !await shift(loadNextState);
306
+ if ((process.status & MODE$1.EXIT) && process.nextEvent) {
307
+ // Consume the next event if it exists.
308
+ process.event = process.nextEvent;
309
+ process.nextEvent = undefined;
310
+ } else if (process.status & MODE$1.LEAF) {
311
+ // Returns control when the process is in a "leaf" state (a state without children)
312
+ break;
313
+ }
314
+ }
315
+ } catch (error) {
316
+ handleError(error);
317
+ }
318
+ })
319
+ .finally(() => process.running = undefined)
320
+ .then(() => !process.finished);
303
321
  };
304
322
  return process;
305
323
  }
306
324
 
307
- function newSyncProcess({
325
+ function newSyncProcess({
308
326
  config,
309
327
  descriptor,
310
328
  before, after,
@@ -318,14 +336,15 @@ function newSyncProcess({
318
336
  newState,
319
337
  });
320
338
  const shift = newTreeWalker(before, after, process);
321
- process.next = (event) => {
339
+ process.dispatch = process.next = (event) => {
340
+ if (typeof event === 'string') event = { key : event };
322
341
  process.event = event;
323
342
  while (process.event) {
324
343
  if (!shift(loadNextState)) return false;
325
- if (process.status & MODE.LEAF) return true;
344
+ if (process.status & MODE$1.LEAF) return true;
326
345
  }
327
346
  };
328
347
  return process;
329
348
  }
330
349
 
331
- export { KEYS, MODE, buildStateDescriptor, checkProcessDescriptor, getAllStateKeys, getAllTransitions, getStateDescriptor, getTargetStateKey, newAsyncProcess, newAsyncTreeWalker, newProcessInstance, newSyncProcess, newTreeWalker };
350
+ 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.4 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.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 T(E),u.status&t.EXIT&&u.nextEvent)u.event=u.nextEvent,u.nextEvent=void 0;else if(u.status&t.LEAF)break}catch(t){i(t)}})).finally((()=>u.running=void 0)).then((()=>!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("string"==typeof e&&(e={key:e}),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.4",
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,41 @@ 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.next = async (event) => {
20
- 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
- })())
19
+ process.finished = false;
20
+ process.next = (event) => {
21
+ process.nextEvent = event;
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
+ }
38
+ }
39
+ } catch (error) {
40
+ handleError(error);
41
+ }
42
+ })
43
+ .finally(() => process.running = undefined)
44
+ .then(() => !process.finished);
28
45
  }
29
46
  return process;
30
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.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;
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.