@statewalker/fsm 0.9.3 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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.3 https://github.com/statewalker/statewalker Copyright 2022 Mikhail Kotelnikov
1
+ // @statewalker/fsm v0.13.0 https://github.com/statewalker/statewalker-tree Copyright 2022 Mikhail Kotelnikov
2
2
  (function (global, factory) {
3
- 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;
@@ -194,38 +138,6 @@ function getAllStateKeys(descriptor) {
194
138
  }
195
139
  }
196
140
 
197
- /**
198
- * Returns all possible transitions from the current state of the process.
199
- * @param {Object} options - method parameters
200
- * @param {Process} options.process - the current process for which
201
- * all transitions should be returned
202
- * @returns {Array<Object>} - an array of all transitions;
203
- * each entry in this returned array contains the following keys:
204
- * 1) parentStateKey - key of the state where the transition can be activated
205
- * 2) sourceStateKey - the initial transition state
206
- * 3) eventKey - key of the event activating transition
207
- * 4) targetStateKey - the resulting state for this transition
208
- *
209
- */
210
- function getAllTransitions(process) {
211
- const list = [];
212
- let stateKey = process.current ? process.current.key : KEYS.STATE_INITIAL;
213
- for (let i = process.stack.length - 1; i >= 0; i--) {
214
- const parentState = process.stack[i];
215
- const parentStateKey = parentState.key;
216
- const descriptor = parentState.descriptor;
217
- for (const sKey of [stateKey, KEYS.STATE_ANY]) {
218
- const stateTransitions = descriptor.transitions[sKey];
219
- if (!stateTransitions) continue;
220
- for (const[eventKey, targetKey] of Object.entries(stateTransitions)) {
221
- list.push([parentStateKey, sKey, eventKey, targetKey]);
222
- }
223
- }
224
- stateKey = parentStateKey;
225
- }
226
- return list;
227
- }
228
-
229
141
  function getTargetStateKey(descriptor, stateKey, eventKey) {
230
142
  const pairs = [
231
143
  [stateKey, eventKey],
@@ -269,7 +181,7 @@ function newProcessInstance({
269
181
  } else {
270
182
  const eventKey = process.event.key || KEYS.EVENT_EMPTY;
271
183
  const parentDescriptor = process.stack[process.stack.length - 1].descriptor;
272
- const stateKey = process.status & MODE$1.ENTER
184
+ const stateKey = process.status & tree.MODE.ENTER
273
185
  ? KEYS.STATE_INITIAL
274
186
  : process.current ? process.current.key : KEYS.STATE_FINAL;
275
187
  key = getTargetStateKey(parentDescriptor, stateKey, eventKey);
@@ -297,7 +209,7 @@ function newAsyncProcess({
297
209
  newProcess,
298
210
  newState
299
211
  });
300
- const shift = newAsyncTreeWalker(before, after, process);
212
+ const shift = tree.newAsyncTreeWalker(before, after, process);
301
213
  process.finished = false;
302
214
  process.next = (event) => {
303
215
  process.nextEvent = event;
@@ -305,16 +217,18 @@ function newAsyncProcess({
305
217
  .resolve()
306
218
  .then(async () => {
307
219
  try {
308
- while (!process.finished) {
309
- if (process.nextEvent) {
220
+ process.event = process.nextEvent;
221
+ process.nextEvent = undefined;
222
+ while (!process.finished && process.event) {
223
+ process.finished = !await shift(loadNextState);
224
+ if ((process.status & tree.MODE.EXIT) && process.nextEvent) {
310
225
  // Consume the next event if it exists.
311
226
  process.event = process.nextEvent;
312
227
  process.nextEvent = undefined;
313
- } else if (process.status & MODE$1.LEAF) {
228
+ } else if (process.status & tree.MODE.LEAF) {
314
229
  // Returns control when the process is in a "leaf" state (a state without children)
315
230
  break;
316
231
  }
317
- process.finished = !await shift(loadNextState);
318
232
  }
319
233
  } catch (error) {
320
234
  handleError(error);
@@ -326,6 +240,98 @@ function newAsyncProcess({
326
240
  return process;
327
241
  }
328
242
 
243
+ function initProcess({ config, handler, handleError = console.error }) {
244
+ let process;
245
+ const before = async (process) => {
246
+ process._started = true;
247
+ const state = process.current;
248
+ handler(state);
249
+ await state.init.run();
250
+ };
251
+ const after = async (process) => {
252
+ const state = process.current;
253
+ await state.done.run();
254
+ };
255
+
256
+ process = newAsyncProcess({
257
+ config,
258
+ before,
259
+ after,
260
+ newProcess: (descriptor) => {
261
+ return {
262
+ descriptor,
263
+ config,
264
+ status: 0,
265
+ stack: [],
266
+ event: undefined,
267
+ current: undefined
268
+ };
269
+ },
270
+ newState: (state) => {
271
+ state.init = newStateMethod();
272
+ state.done = newStateMethod();
273
+ state.dispatch = (...args) => state.process.dispatch(...args);
274
+ state.getEvent = () => state.process.event || {};
275
+ state.getEventKey = () => state.getEvent().key;
276
+ return state;
277
+ },
278
+ handleError
279
+ });
280
+ process.dispatch = (key, params = {}) => {
281
+ process.next({ key, params });
282
+ };
283
+ return process;
284
+
285
+ function newStateMethod() {
286
+ const list = [];
287
+ return Object.assign((m) => list.push(m), {
288
+ run: async (...args) => {
289
+ // We use the index to iterate over the list: the list can be updated by called methods
290
+ for (let i = 0; i < list.length; i++) {
291
+ try {
292
+ list[i] && (await list[i](...args));
293
+ } catch (error) {
294
+ console.error(error);
295
+ await handleError(error);
296
+ }
297
+ }
298
+ }
299
+ });
300
+ }
301
+ }
302
+
303
+ /**
304
+ * Returns all possible transitions from the current state of the process.
305
+ * @param {Object} options - method parameters
306
+ * @param {Process} options.process - the current process for which
307
+ * all transitions should be returned
308
+ * @returns {Array<Object>} - an array of all transitions;
309
+ * each entry in this returned array contains the following keys:
310
+ * 1) parentStateKey - key of the state where the transition can be activated
311
+ * 2) sourceStateKey - the initial transition state
312
+ * 3) eventKey - key of the event activating transition
313
+ * 4) targetStateKey - the resulting state for this transition
314
+ *
315
+ */
316
+ function getAllTransitions(process) {
317
+ const list = [];
318
+ let stateKey = process.current ? process.current.key : KEYS.STATE_INITIAL;
319
+ for (let i = process.stack.length - 1; i >= 0; i--) {
320
+ const parentState = process.stack[i];
321
+ const parentStateKey = parentState.key;
322
+ const descriptor = parentState.descriptor;
323
+ for (const sKey of [stateKey, KEYS.STATE_ANY]) {
324
+ const stateTransitions = descriptor.transitions[sKey];
325
+ if (!stateTransitions) continue;
326
+ for (const[eventKey, targetKey] of Object.entries(stateTransitions)) {
327
+ list.push([parentStateKey, sKey, eventKey, targetKey]);
328
+ }
329
+ }
330
+ stateKey = parentStateKey;
331
+ }
332
+ return list;
333
+ }
334
+
329
335
  function newSyncProcess({
330
336
  config,
331
337
  descriptor,
@@ -339,31 +345,28 @@ function newSyncProcess({
339
345
  newProcess,
340
346
  newState,
341
347
  });
342
- const shift = newTreeWalker(before, after, process);
348
+ const shift = tree.newTreeWalker(before, after, process);
343
349
  process.dispatch = process.next = (event) => {
344
350
  if (typeof event === 'string') event = { key : event };
345
351
  process.event = event;
346
352
  while (process.event) {
347
353
  if (!shift(loadNextState)) return false;
348
- if (process.status & MODE$1.LEAF) return true;
354
+ if (process.status & tree.MODE.LEAF) return true;
349
355
  }
350
356
  };
351
357
  return process;
352
358
  }
353
359
 
354
360
  exports.KEYS = KEYS;
355
- exports.MODE = MODE$1;
356
361
  exports.buildStateDescriptor = buildStateDescriptor;
357
362
  exports.checkProcessDescriptor = checkProcessDescriptor;
358
363
  exports.getAllStateKeys = getAllStateKeys;
359
364
  exports.getAllTransitions = getAllTransitions;
360
365
  exports.getStateDescriptor = getStateDescriptor;
361
366
  exports.getTargetStateKey = getTargetStateKey;
362
- exports.newAsyncProcess = newAsyncProcess;
363
- exports.newAsyncTreeWalker = newAsyncTreeWalker;
367
+ exports.initAsyncProcess = initProcess;
364
368
  exports.newProcessInstance = newProcessInstance;
365
369
  exports.newSyncProcess = newSyncProcess;
366
- exports.newTreeWalker = newTreeWalker;
367
370
 
368
371
  Object.defineProperty(exports, '__esModule', { value: true });
369
372
 
@@ -0,0 +1,2 @@
1
+ // @statewalker/fsm v0.13.0 https://github.com/statewalker/statewalker-tree Copyright 2022 Mikhail Kotelnikov
2
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("@statewalker/tree")):"function"==typeof define&&define.amd?define(["exports","@statewalker/tree"],t):t(((e="undefined"!=typeof globalThis?globalThis:e||self).statewalker=e.statewalker||{},e.statewalker.fsm=e.statewalker.fsm||{}),e.statewalker.tree)}(this,(function(e,t){"use strict";function n(e={}){const{key:t,transitions:s=[],states:r={},options:o,...c}=e,a={key:t,transitions:{},states:{},options:Object.assign(o||{},c)};for(const e of s){let t,n,s;if(Array.isArray(e))t=e[0],n=e[1],s=e[2];else{const{from:r,event:o,to:c}=e;t=r,n=o,s=c}(a.transitions[t]=a.transitions[t]||{})[n]=s}if(Array.isArray(r))for(const e of r)a.states[e.key]=n(e);else if(r)for(const e of Object.keys(r))a.states[e]=n(r[e]);return a}var s={STATE_ANY:"*",STATE_INITIAL:"",STATE_FINAL:"",EVENT_ANY:"*",EVENT_EMPTY:""};function r(e,t){let n;for(let s=e.stack.length-1;!n&&s>=0;s--){n=(e.stack[s].descriptor.states||{})[t]}return n||{transitions:{},states:{}}}function o(e,t,n){const r=[[t,n],[s.STATE_ANY,n],[t,s.EVENT_ANY],[s.STATE_ANY,s.EVENT_ANY]];let o;for(let t=0,n=r.length;void 0===o&&t<n;t++){const[n,c]=r[t],a=e.transitions[n];a&&(o=a[c],void 0===o&&(o=a[s.EVENT_ANY]))}return void 0!==o?o:s.STATE_FINAL}function c({config:e,descriptor:c,newState:a=(e=>e),newProcess:i=(e=>({descriptor:e,status:0,stack:[],event:void 0,current:void 0}))}){const f=i(c=c||n(e));return[f,()=>{let e,n;if(f.status)if(f.stack.length){const c=f.event.key||s.EVENT_EMPTY;e=o(f.stack[f.stack.length-1].descriptor,f.status&t.MODE.ENTER?s.STATE_INITIAL:f.current?f.current.key:s.STATE_FINAL,c),e&&(n=r(f,e))}else e=s.STATE_FINAL;else n=f.descriptor,e=n.key;return e?a({process:f,key:e,descriptor:n}):null}]}e.KEYS=s,e.buildStateDescriptor=n,e.checkProcessDescriptor=function(e){return function e(t,n=[],r=[]){r=[...r,t.key];const o=Object.assign({key:t.key},function(e){const t={},n={};for(const[s,r]of Object.entries(e.transitions)){t[s]=(t[s]||0)+1;for(const e of Object.values(r))n[e]=(n[e]||0)+1}const r=[],o=[];for(const e of Object.keys(t))e===s.STATE_ANY||e===s.STATE_FINAL||e===s.STATE_INITIAL||e in n||r.push(e);for(const e of Object.keys(n))e===s.STATE_FINAL||e in t||o.push(e);return{unreachableStates:r,deadendStates:o}}(t));if(o.deadendStates.length||o.unreachableStates.length){const e={path:r};n.push(e),o.deadendStates.length&&(e.deadendStates=o.deadendStates),o.unreachableStates.length&&(e.unreachableStates=o.unreachableStates)}for(const s of Object.values(t.states))e(s,n,r);return n}(e)},e.getAllStateKeys=function(e){const t={},n=e=>t[e]=(t[e]||0)+1;return function e(t){n(t.key);for(const[e,s]of Object.entries(t.transitions)){n(e);for(const e of Object.values(s))n(e)}for(const[s,r]of Object.entries(t.states))n(s),e(r)}(e),Object.keys(t).sort()},e.getAllTransitions=function(e){const t=[];let n=e.current?e.current.key:s.STATE_INITIAL;for(let r=e.stack.length-1;r>=0;r--){const o=e.stack[r],c=o.key,a=o.descriptor;for(const e of[n,s.STATE_ANY]){const n=a.transitions[e];if(n)for(const[s,r]of Object.entries(n))t.push([c,e,s,r])}n=c}return t},e.getStateDescriptor=r,e.getTargetStateKey=o,e.initAsyncProcess=function({config:e,handler:n,handleError:s=console.error}){let r;return r=function({config:e,descriptor:n,before:s,after:r,newProcess:o,newState:a,handleError:i=console.error}){const[f,u]=c({config:e,descriptor:n,newProcess:o,newState:a}),l=t.newAsyncTreeWalker(s,r,f);return f.finished=!1,f.next=e=>(f.nextEvent=e,f.running=f.running||Promise.resolve().then((async()=>{try{for(f.event=f.nextEvent,f.nextEvent=void 0;!f.finished&&f.event;)if(f.finished=!await l(u),f.status&t.MODE.EXIT&&f.nextEvent)f.event=f.nextEvent,f.nextEvent=void 0;else if(f.status&t.MODE.LEAF)break}catch(e){i(e)}})).finally((()=>f.running=void 0)).then((()=>!f.finished))),f}({config:e,before:async e=>{e._started=!0;const t=e.current;n(t),await t.init.run()},after:async e=>{const t=e.current;await t.done.run()},newProcess:t=>({descriptor:t,config:e,status:0,stack:[],event:void 0,current:void 0}),newState:e=>(e.init=o(),e.done=o(),e.dispatch=(...t)=>e.process.dispatch(...t),e.getEvent=()=>e.process.event||{},e.getEventKey=()=>e.getEvent().key,e),handleError:s}),r.dispatch=(e,t={})=>{r.next({key:e,params:t})},r;function o(){const e=[];return Object.assign((t=>e.push(t)),{run:async(...t)=>{for(let n=0;n<e.length;n++)try{e[n]&&await e[n](...t)}catch(e){console.error(e),await s(e)}}})}},e.newProcessInstance=c,e.newSyncProcess=function({config:e,descriptor:n,before:s,after:r,newProcess:o,newState:a}){const[i,f]=c({config:e,descriptor:n,newProcess:o,newState:a}),u=t.newTreeWalker(s,r,i);return i.dispatch=i.next=e=>{for("string"==typeof e&&(e={key:e}),i.event=e;i.event;){if(!u(f))return!1;if(i.status&t.MODE.LEAF)return!0}},i},Object.defineProperty(e,"__esModule",{value:!0})}));
@@ -1,59 +1,5 @@
1
- // @statewalker/fsm v0.9.3 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.13.0 https://github.com/statewalker/statewalker-tree Copyright 2022 Mikhail Kotelnikov
2
+ import { MODE, newAsyncTreeWalker, newTreeWalker } from '@statewalker/tree';
57
3
 
58
4
  function buildStateDescriptor(config = {}) {
59
5
  const { key, transitions = [], states = {}, options, ...params } = config;
@@ -188,38 +134,6 @@ function getAllStateKeys(descriptor) {
188
134
  }
189
135
  }
190
136
 
191
- /**
192
- * Returns all possible transitions from the current state of the process.
193
- * @param {Object} options - method parameters
194
- * @param {Process} options.process - the current process for which
195
- * all transitions should be returned
196
- * @returns {Array<Object>} - an array of all transitions;
197
- * each entry in this returned array contains the following keys:
198
- * 1) parentStateKey - key of the state where the transition can be activated
199
- * 2) sourceStateKey - the initial transition state
200
- * 3) eventKey - key of the event activating transition
201
- * 4) targetStateKey - the resulting state for this transition
202
- *
203
- */
204
- function getAllTransitions(process) {
205
- const list = [];
206
- let stateKey = process.current ? process.current.key : KEYS.STATE_INITIAL;
207
- for (let i = process.stack.length - 1; i >= 0; i--) {
208
- const parentState = process.stack[i];
209
- const parentStateKey = parentState.key;
210
- const descriptor = parentState.descriptor;
211
- for (const sKey of [stateKey, KEYS.STATE_ANY]) {
212
- const stateTransitions = descriptor.transitions[sKey];
213
- if (!stateTransitions) continue;
214
- for (const[eventKey, targetKey] of Object.entries(stateTransitions)) {
215
- list.push([parentStateKey, sKey, eventKey, targetKey]);
216
- }
217
- }
218
- stateKey = parentStateKey;
219
- }
220
- return list;
221
- }
222
-
223
137
  function getTargetStateKey(descriptor, stateKey, eventKey) {
224
138
  const pairs = [
225
139
  [stateKey, eventKey],
@@ -263,7 +177,7 @@ function newProcessInstance({
263
177
  } else {
264
178
  const eventKey = process.event.key || KEYS.EVENT_EMPTY;
265
179
  const parentDescriptor = process.stack[process.stack.length - 1].descriptor;
266
- const stateKey = process.status & MODE$1.ENTER
180
+ const stateKey = process.status & MODE.ENTER
267
181
  ? KEYS.STATE_INITIAL
268
182
  : process.current ? process.current.key : KEYS.STATE_FINAL;
269
183
  key = getTargetStateKey(parentDescriptor, stateKey, eventKey);
@@ -299,16 +213,18 @@ function newAsyncProcess({
299
213
  .resolve()
300
214
  .then(async () => {
301
215
  try {
302
- while (!process.finished) {
303
- if (process.nextEvent) {
216
+ process.event = process.nextEvent;
217
+ process.nextEvent = undefined;
218
+ while (!process.finished && process.event) {
219
+ process.finished = !await shift(loadNextState);
220
+ if ((process.status & MODE.EXIT) && process.nextEvent) {
304
221
  // Consume the next event if it exists.
305
222
  process.event = process.nextEvent;
306
223
  process.nextEvent = undefined;
307
- } else if (process.status & MODE$1.LEAF) {
224
+ } else if (process.status & MODE.LEAF) {
308
225
  // Returns control when the process is in a "leaf" state (a state without children)
309
226
  break;
310
227
  }
311
- process.finished = !await shift(loadNextState);
312
228
  }
313
229
  } catch (error) {
314
230
  handleError(error);
@@ -320,6 +236,98 @@ function newAsyncProcess({
320
236
  return process;
321
237
  }
322
238
 
239
+ function initProcess({ config, handler, handleError = console.error }) {
240
+ let process;
241
+ const before = async (process) => {
242
+ process._started = true;
243
+ const state = process.current;
244
+ handler(state);
245
+ await state.init.run();
246
+ };
247
+ const after = async (process) => {
248
+ const state = process.current;
249
+ await state.done.run();
250
+ };
251
+
252
+ process = newAsyncProcess({
253
+ config,
254
+ before,
255
+ after,
256
+ newProcess: (descriptor) => {
257
+ return {
258
+ descriptor,
259
+ config,
260
+ status: 0,
261
+ stack: [],
262
+ event: undefined,
263
+ current: undefined
264
+ };
265
+ },
266
+ newState: (state) => {
267
+ state.init = newStateMethod();
268
+ state.done = newStateMethod();
269
+ state.dispatch = (...args) => state.process.dispatch(...args);
270
+ state.getEvent = () => state.process.event || {};
271
+ state.getEventKey = () => state.getEvent().key;
272
+ return state;
273
+ },
274
+ handleError
275
+ });
276
+ process.dispatch = (key, params = {}) => {
277
+ process.next({ key, params });
278
+ };
279
+ return process;
280
+
281
+ function newStateMethod() {
282
+ const list = [];
283
+ return Object.assign((m) => list.push(m), {
284
+ run: async (...args) => {
285
+ // We use the index to iterate over the list: the list can be updated by called methods
286
+ for (let i = 0; i < list.length; i++) {
287
+ try {
288
+ list[i] && (await list[i](...args));
289
+ } catch (error) {
290
+ console.error(error);
291
+ await handleError(error);
292
+ }
293
+ }
294
+ }
295
+ });
296
+ }
297
+ }
298
+
299
+ /**
300
+ * Returns all possible transitions from the current state of the process.
301
+ * @param {Object} options - method parameters
302
+ * @param {Process} options.process - the current process for which
303
+ * all transitions should be returned
304
+ * @returns {Array<Object>} - an array of all transitions;
305
+ * each entry in this returned array contains the following keys:
306
+ * 1) parentStateKey - key of the state where the transition can be activated
307
+ * 2) sourceStateKey - the initial transition state
308
+ * 3) eventKey - key of the event activating transition
309
+ * 4) targetStateKey - the resulting state for this transition
310
+ *
311
+ */
312
+ function getAllTransitions(process) {
313
+ const list = [];
314
+ let stateKey = process.current ? process.current.key : KEYS.STATE_INITIAL;
315
+ for (let i = process.stack.length - 1; i >= 0; i--) {
316
+ const parentState = process.stack[i];
317
+ const parentStateKey = parentState.key;
318
+ const descriptor = parentState.descriptor;
319
+ for (const sKey of [stateKey, KEYS.STATE_ANY]) {
320
+ const stateTransitions = descriptor.transitions[sKey];
321
+ if (!stateTransitions) continue;
322
+ for (const[eventKey, targetKey] of Object.entries(stateTransitions)) {
323
+ list.push([parentStateKey, sKey, eventKey, targetKey]);
324
+ }
325
+ }
326
+ stateKey = parentStateKey;
327
+ }
328
+ return list;
329
+ }
330
+
323
331
  function newSyncProcess({
324
332
  config,
325
333
  descriptor,
@@ -339,10 +347,10 @@ function newSyncProcess({
339
347
  process.event = event;
340
348
  while (process.event) {
341
349
  if (!shift(loadNextState)) return false;
342
- if (process.status & MODE$1.LEAF) return true;
350
+ if (process.status & MODE.LEAF) return true;
343
351
  }
344
352
  };
345
353
  return process;
346
354
  }
347
355
 
348
- export { KEYS, MODE$1 as MODE, buildStateDescriptor, checkProcessDescriptor, getAllStateKeys, getAllTransitions, getStateDescriptor, getTargetStateKey, newAsyncProcess, newAsyncTreeWalker, newProcessInstance, newSyncProcess, newTreeWalker };
356
+ export { KEYS, buildStateDescriptor, checkProcessDescriptor, getAllStateKeys, getAllTransitions, getStateDescriptor, getTargetStateKey, initProcess as initAsyncProcess, newProcessInstance, newSyncProcess };
@@ -0,0 +1,2 @@
1
+ // @statewalker/fsm v0.13.0 https://github.com/statewalker/statewalker-tree Copyright 2022 Mikhail Kotelnikov
2
+ import{MODE as t,newAsyncTreeWalker as e,newTreeWalker as n}from"@statewalker/tree";function s(t={}){const{key:e,transitions:n=[],states:r={},options:o,...c}=t,i={key:e,transitions:{},states:{},options:Object.assign(o||{},c)};for(const t of n){let e,n,s;if(Array.isArray(t))e=t[0],n=t[1],s=t[2];else{const{from:r,event:o,to:c}=t;e=r,n=o,s=c}(i.transitions[e]=i.transitions[e]||{})[n]=s}if(Array.isArray(r))for(const t of r)i.states[t.key]=s(t);else if(r)for(const t of Object.keys(r))i.states[t]=s(r[t]);return i}var r={STATE_ANY:"*",STATE_INITIAL:"",STATE_FINAL:"",EVENT_ANY:"*",EVENT_EMPTY:""};function o(t){return function t(e,n=[],s=[]){s=[...s,e.key];const o=Object.assign({key:e.key},function(t){const e={},n={};for(const[s,r]of Object.entries(t.transitions)){e[s]=(e[s]||0)+1;for(const t of Object.values(r))n[t]=(n[t]||0)+1}const s=[],o=[];for(const t of Object.keys(e))t===r.STATE_ANY||t===r.STATE_FINAL||t===r.STATE_INITIAL||t in n||s.push(t);for(const t of Object.keys(n))t===r.STATE_FINAL||t in e||o.push(t);return{unreachableStates:s,deadendStates:o}}(e));if(o.deadendStates.length||o.unreachableStates.length){const t={path:s};n.push(t),o.deadendStates.length&&(t.deadendStates=o.deadendStates),o.unreachableStates.length&&(t.unreachableStates=o.unreachableStates)}for(const r of Object.values(e.states))t(r,n,s);return n}(t)}function c(t,e){let n;for(let s=t.stack.length-1;!n&&s>=0;s--){n=(t.stack[s].descriptor.states||{})[e]}return n||{transitions:{},states:{}}}function i(t){const e={},n=t=>e[t]=(e[t]||0)+1;return function t(e){n(e.key);for(const[t,s]of Object.entries(e.transitions)){n(t);for(const t of Object.values(s))n(t)}for(const[s,r]of Object.entries(e.states))n(s),t(r)}(t),Object.keys(e).sort()}function a(t,e,n){const s=[[e,n],[r.STATE_ANY,n],[e,r.EVENT_ANY],[r.STATE_ANY,r.EVENT_ANY]];let o;for(let e=0,n=s.length;void 0===o&&e<n;e++){const[n,c]=s[e],i=t.transitions[n];i&&(o=i[c],void 0===o&&(o=i[r.EVENT_ANY]))}return void 0!==o?o:r.STATE_FINAL}function f({config:e,descriptor:n,newState:o=(t=>t),newProcess:i=(t=>({descriptor:t,status:0,stack:[],event:void 0,current:void 0}))}){const f=i(n=n||s(e));return[f,()=>{let e,n;if(f.status)if(f.stack.length){const s=f.event.key||r.EVENT_EMPTY;e=a(f.stack[f.stack.length-1].descriptor,f.status&t.ENTER?r.STATE_INITIAL:f.current?f.current.key:r.STATE_FINAL,s),e&&(n=c(f,e))}else e=r.STATE_FINAL;else n=f.descriptor,e=n.key;return e?o({process:f,key:e,descriptor:n}):null}]}function u({config:n,handler:s,handleError:r=console.error}){let o;return o=function({config:n,descriptor:s,before:r,after:o,newProcess:c,newState:i,handleError:a=console.error}){const[u,d]=f({config:n,descriptor:s,newProcess:c,newState:i}),l=e(r,o,u);return u.finished=!1,u.next=e=>(u.nextEvent=e,u.running=u.running||Promise.resolve().then((async()=>{try{for(u.event=u.nextEvent,u.nextEvent=void 0;!u.finished&&u.event;)if(u.finished=!await l(d),u.status&t.EXIT&&u.nextEvent)u.event=u.nextEvent,u.nextEvent=void 0;else if(u.status&t.LEAF)break}catch(t){a(t)}})).finally((()=>u.running=void 0)).then((()=>!u.finished))),u}({config:n,before:async t=>{t._started=!0;const e=t.current;s(e),await e.init.run()},after:async t=>{const e=t.current;await e.done.run()},newProcess:t=>({descriptor:t,config:n,status:0,stack:[],event:void 0,current:void 0}),newState:t=>(t.init=c(),t.done=c(),t.dispatch=(...e)=>t.process.dispatch(...e),t.getEvent=()=>t.process.event||{},t.getEventKey=()=>t.getEvent().key,t),handleError:r}),o.dispatch=(t,e={})=>{o.next({key:t,params:e})},o;function c(){const t=[];return Object.assign((e=>t.push(e)),{run:async(...e)=>{for(let n=0;n<t.length;n++)try{t[n]&&await t[n](...e)}catch(t){console.error(t),await r(t)}}})}}function d(t){const e=[];let n=t.current?t.current.key:r.STATE_INITIAL;for(let s=t.stack.length-1;s>=0;s--){const o=t.stack[s],c=o.key,i=o.descriptor;for(const t of[n,r.STATE_ANY]){const n=i.transitions[t];if(n)for(const[s,r]of Object.entries(n))e.push([c,t,s,r])}n=c}return e}function l({config:e,descriptor:s,before:r,after:o,newProcess:c,newState:i}){const[a,u]=f({config:e,descriptor:s,newProcess:c,newState:i}),d=n(r,o,a);return a.dispatch=a.next=e=>{for("string"==typeof e&&(e={key:e}),a.event=e;a.event;){if(!d(u))return!1;if(a.status&t.LEAF)return!0}},a}export{r as KEYS,s as buildStateDescriptor,o as checkProcessDescriptor,i as getAllStateKeys,d as getAllTransitions,c as getStateDescriptor,a as getTargetStateKey,u as initAsyncProcess,f as newProcessInstance,l as newSyncProcess};
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@statewalker/fsm",
3
- "version": "0.9.3",
4
- "description": "Utility graph methods (iterators, tree builders etc)",
3
+ "version": "0.13.0",
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,
package/src/index.js CHANGED
@@ -1,11 +1,10 @@
1
- export * from "@statewalker/tree";
2
1
  export { default as buildStateDescriptor } from "./buildStateDescriptor.js";
3
2
  export { default as checkProcessDescriptor } from "./checkProcessDescriptor.js";
4
3
  export { default as getStateDescriptor } from "./getStateDescriptor.js";
5
4
  export { default as getAllStateKeys } from "./getAllStateKeys.js";
6
- export { default as getAllTransitions } from "./getAllTransitions.js";
7
5
  export { default as getTargetStateKey } from "./getTargetStateKey.js";
8
6
  export { default as KEYS } from "./KEYS.js";
9
- export { default as newAsyncProcess } from "./newAsyncProcess.js";
7
+ export { default as initAsyncProcess } from "./initAsyncProcess.js";
8
+ export { default as getAllTransitions } from "./getAllTransitions.js";
10
9
  export { default as newProcessInstance } from "./newProcessInstance.js";
11
10
  export { default as newSyncProcess } from "./newSyncProcess.js";
@@ -0,0 +1,61 @@
1
+ import newAsyncProcess from "./newAsyncProcess.js";
2
+
3
+ export default function initProcess({ config, handler, handleError = console.error }) {
4
+ let process;
5
+ const before = async (process) => {
6
+ process._started = true;
7
+ const state = process.current;
8
+ handler(state);
9
+ await state.init.run();
10
+ };
11
+ const after = async (process) => {
12
+ const state = process.current;
13
+ await state.done.run();
14
+ };
15
+
16
+ process = newAsyncProcess({
17
+ config,
18
+ before,
19
+ after,
20
+ newProcess: (descriptor) => {
21
+ return {
22
+ descriptor,
23
+ config,
24
+ status: 0,
25
+ stack: [],
26
+ event: undefined,
27
+ current: undefined
28
+ };
29
+ },
30
+ newState: (state) => {
31
+ state.init = newStateMethod();
32
+ state.done = newStateMethod();
33
+ state.dispatch = (...args) => state.process.dispatch(...args);
34
+ state.getEvent = () => state.process.event || {};
35
+ state.getEventKey = () => state.getEvent().key;
36
+ return state;
37
+ },
38
+ handleError
39
+ });
40
+ process.dispatch = (key, params = {}) => {
41
+ process.next({ key, params });
42
+ };
43
+ return process;
44
+
45
+ function newStateMethod() {
46
+ const list = [];
47
+ return Object.assign((m) => list.push(m), {
48
+ run: async (...args) => {
49
+ // We use the index to iterate over the list: the list can be updated by called methods
50
+ for (let i = 0; i < list.length; i++) {
51
+ try {
52
+ list[i] && (await list[i](...args));
53
+ } catch (error) {
54
+ console.error(error);
55
+ await handleError(error);
56
+ }
57
+ }
58
+ }
59
+ });
60
+ }
61
+ }
@@ -23,8 +23,11 @@ export default function newAsyncProcess({
23
23
  .resolve()
24
24
  .then(async () => {
25
25
  try {
26
- while (!process.finished) {
27
- if (process.nextEvent) {
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) {
28
31
  // Consume the next event if it exists.
29
32
  process.event = process.nextEvent;
30
33
  process.nextEvent = undefined;
@@ -32,7 +35,6 @@ export default function newAsyncProcess({
32
35
  // Returns control when the process is in a "leaf" state (a state without children)
33
36
  break;
34
37
  }
35
- process.finished = !await shift(loadNextState);
36
38
  }
37
39
  } catch (error) {
38
40
  handleError(error);
@@ -1,3 +0,0 @@
1
- {
2
- "type": "commonjs"
3
- }
@@ -1,2 +0,0 @@
1
- // @statewalker/fsm v0.9.3 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.next=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)).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}),T=n(r,o,f);return f.dispatch=f.next=t=>{for("string"==typeof t&&(t={key:t}),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.3 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.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)).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};