@statewalker/fsm 0.24.0 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -117,4 +117,9 @@ declare function setStateTracer(state: FsmState, print?: Printer): void;
117
117
  declare function isStateTransitionEnabled(process: FsmProcess, event: string): boolean;
118
118
  declare function getStateTransitions(state?: FsmState): [from: string, event: string, to: string][];
119
119
 
120
- export { EVENT_ANY, EVENT_EMPTY, type FsmEventKey, FsmProcess, type FsmProcessDump, type FsmProcessDumpHandler, type FsmProcessHandler, FsmState, type FsmStateConfig, FsmStateDescriptor, type FsmStateDump, type FsmStateDumpHandler, type FsmStateErrorHandler, type FsmStateHandler, type FsmStateKey, KEY_PRINTER, type Printer, type PrinterConfig, STATE_ANY, STATE_FINAL, STATE_INITIAL, STATUS_ENTER, STATUS_EXIT, STATUS_FINISHED, STATUS_FIRST, STATUS_LAST, STATUS_LEAF, STATUS_NEXT, STATUS_NONE, getPrinter, getProcessPrinter, getStateTransitions, isStateTransitionEnabled, newProcess, preparePrinter, setPrinter, setProcessPrinter, setProcessTracer, setStateTracer };
120
+ declare function newFsmProcess(config: FsmStateConfig, handler: (state: string, event: undefined | string) => void | Promise<void> | ((event: undefined | string) => void | Promise<void>) | Promise<(event: undefined | string) => void | Promise<void>>): [
121
+ dispatch: (event: string) => Promise<boolean>,
122
+ shutdown: () => Promise<void>
123
+ ];
124
+
125
+ export { EVENT_ANY, EVENT_EMPTY, type FsmEventKey, FsmProcess, type FsmProcessDump, type FsmProcessDumpHandler, type FsmProcessHandler, FsmState, type FsmStateConfig, FsmStateDescriptor, type FsmStateDump, type FsmStateDumpHandler, type FsmStateErrorHandler, type FsmStateHandler, type FsmStateKey, KEY_PRINTER, type Printer, type PrinterConfig, STATE_ANY, STATE_FINAL, STATE_INITIAL, STATUS_ENTER, STATUS_EXIT, STATUS_FINISHED, STATUS_FIRST, STATUS_LAST, STATUS_LEAF, STATUS_NEXT, STATUS_NONE, getPrinter, getProcessPrinter, getStateTransitions, isStateTransitionEnabled, newFsmProcess, newProcess, preparePrinter, setPrinter, setProcessPrinter, setProcessTracer, setStateTracer };
package/dist/index.js CHANGED
@@ -204,9 +204,9 @@ var FsmProcess = class extends FsmBaseClass {
204
204
  } finally {
205
205
  this.running = false;
206
206
  }
207
- if (this.nextEvent !== void 0) {
208
- await new Promise((r) => setImmediate(r));
209
- return this.dispatch(this.nextEvent);
207
+ const nextEvent = this.nextEvent;
208
+ if (nextEvent !== void 0) {
209
+ return Promise.resolve().then(() => this.dispatch(nextEvent));
210
210
  }
211
211
  }
212
212
  return !(this.status & STATUS_FINISHED);
@@ -408,6 +408,31 @@ function getTransitionsFromDescriptor(descriptor, prevStateKey, index = {}) {
408
408
  }
409
409
  return result;
410
410
  }
411
+
412
+ // src/newFsmProcess.ts
413
+ function newFsmProcess(config, handler) {
414
+ let started = false;
415
+ let terminated = false;
416
+ const process = new FsmProcess(config);
417
+ process.onStateCreate((state) => {
418
+ started = true;
419
+ let cleanup;
420
+ state.onEnter(async () => {
421
+ cleanup = await handler(state.key, process.event);
422
+ });
423
+ state.onExit(async () => {
424
+ cleanup?.(process.event);
425
+ });
426
+ });
427
+ async function dispatch(event) {
428
+ return terminated || event !== void 0 ? process.dispatch(event) : false;
429
+ }
430
+ async function shutdown() {
431
+ await process.shutdown();
432
+ terminated = true;
433
+ }
434
+ return [dispatch, shutdown];
435
+ }
411
436
  export {
412
437
  EVENT_ANY,
413
438
  EVENT_EMPTY,
@@ -430,6 +455,7 @@ export {
430
455
  getProcessPrinter,
431
456
  getStateTransitions,
432
457
  isStateTransitionEnabled,
458
+ newFsmProcess,
433
459
  newProcess,
434
460
  preparePrinter,
435
461
  setPrinter,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statewalker/fsm",
3
- "version": "0.24.0",
3
+ "version": "0.26.0",
4
4
  "description": "HFSM Implementation",
5
5
  "keywords": [],
6
6
  "homepage": "https://github.com/statewalker/statewalker-fsm",
package/src/FsmProcess.ts CHANGED
@@ -83,9 +83,9 @@ export class FsmProcess extends FsmBaseClass {
83
83
  } finally {
84
84
  this.running = false;
85
85
  }
86
- if (this.nextEvent !== undefined) {
87
- await new Promise((r) => setImmediate(r));
88
- return this.dispatch(this.nextEvent);
86
+ const nextEvent = this.nextEvent;
87
+ if (nextEvent !== undefined) {
88
+ return Promise.resolve().then(() => this.dispatch(nextEvent));
89
89
  }
90
90
  }
91
91
  return !(this.status & STATUS_FINISHED);
package/src/index.ts CHANGED
@@ -3,3 +3,4 @@ export * from "./FsmState.ts";
3
3
  export * from "./FsmStateConfig.ts";
4
4
  export * from "./FsmStateDescriptor.ts";
5
5
  export * from "./utils/index.ts";
6
+ export * from "./newFsmProcess.js";
@@ -0,0 +1,45 @@
1
+ import { FsmProcess } from "./FsmProcess.js";
2
+ import type { FsmStateConfig } from "./FsmStateConfig.js";
3
+ import { isStateTransitionEnabled } from "./utils/transitions.js";
4
+
5
+ export function newFsmProcess(
6
+ config: FsmStateConfig,
7
+ handler: (
8
+ state: string,
9
+ event: undefined | string,
10
+ ) =>
11
+ | void
12
+ | Promise<void>
13
+ | ((event: undefined | string) => void | Promise<void>)
14
+ | Promise<(event: undefined | string) => void | Promise<void>>,
15
+ ): [
16
+ dispatch: (event: string) => Promise<boolean>,
17
+ shutdown: () => Promise<void>,
18
+ ] {
19
+ let started = false;
20
+ let terminated = false;
21
+ const process = new FsmProcess(config);
22
+ process.onStateCreate((state) => {
23
+ started = true;
24
+ let cleanup: Awaited<ReturnType<typeof handler>> | undefined;
25
+ state.onEnter(async () => {
26
+ cleanup = await handler(state.key, process.event);
27
+ });
28
+ state.onExit(async () => {
29
+ cleanup?.(process.event);
30
+ });
31
+ });
32
+ async function dispatch(event: string): Promise<boolean> {
33
+ return terminated ||
34
+ (event !== undefined
35
+ // && (!started || isStateTransitionEnabled(process, event))
36
+ )
37
+ ? process.dispatch(event)
38
+ : false;
39
+ }
40
+ async function shutdown(): Promise<void> {
41
+ await process.shutdown();
42
+ terminated = true;
43
+ }
44
+ return [dispatch, shutdown] as const;
45
+ }
@@ -72,16 +72,12 @@ export type FsmStateModule<
72
72
  | ((...stack: FsmStateStore[]) => FsmStateContext)
73
73
  | (new (...stack: FsmStateStore[]) => FsmStateContext);
74
74
  // trigger?: (context: FsmStateContext) => AsyncGenerator<string>;
75
- trigger?: (
76
- context: FsmStateContext,
77
- onEvent: (event: string) => void,
78
- ) => void | (() => void);
75
+ trigger?: (context: FsmStateContext) => AsyncIterator<string>;
79
76
  handler?: (
80
77
  context: FsmStateContext,
81
78
  ) => void | (() => void) | Promise<void | (() => void)>;
82
79
  };
83
80
 
84
-
85
81
  export function newModuleContext<
86
82
  C = Record<string, unknown>,
87
83
  S = Record<string, unknown>,
@@ -103,8 +99,6 @@ function isClass<T extends Array<unknown>, R = unknown>(
103
99
  value: unknown,
104
100
  ): value is new (...args: T) => R {
105
101
  if (!isFunction(value)) return false;
106
- // console.log('============')
107
- // console.log('???', value, value?.prototype, value?.prototype?.constructor);
108
102
  return value?.prototype?.constructor?.toString().match(/^class/);
109
103
  }
110
104
 
@@ -127,6 +121,7 @@ export function newStateHandlers<
127
121
  FsmStateContext
128
122
  >(),
129
123
  ) {
124
+ const triggers = new Map<unknown, () => void>();
130
125
  return (state: FsmState) => {
131
126
  const stack: FsmStateStore[] = [];
132
127
  for (let s: FsmState | undefined = state.parent; !!s; s = s.parent) {
@@ -161,20 +156,35 @@ export function newStateHandlers<
161
156
  }
162
157
 
163
158
  // Initialize triggers
164
- if (module.trigger) {
159
+ if (module.trigger && !triggers.has(module.trigger)) {
165
160
  const process = state.process;
166
- // Set trigger
167
- let notify = async (event: string) => {
168
- if (event && isStateTransitionEnabled(process, event)) {
169
- process.dispatch(event);
161
+ const iterator = module.trigger(stateContext);
162
+ let finalize: undefined | (() => void);
163
+ const end = new Promise<{ done: boolean; value?: string }>(
164
+ (r) => (finalize = () => r({ done: true })),
165
+ );
166
+ registrations.push(() => triggers.delete(module.trigger));
167
+ registrations.push(finalize);
168
+ registrations.push(() => iterator?.return?.(void 0));
169
+ (async () => {
170
+ try {
171
+ while (true) {
172
+ const { done, value: event } = await Promise.race([
173
+ iterator.next(),
174
+ end,
175
+ ]);
176
+ if (done) break;
177
+ if (event && isStateTransitionEnabled(process, event)) {
178
+ process.dispatch(event);
179
+ }
180
+ }
181
+ } finally {
182
+ finalize?.();
170
183
  }
171
- };
172
- const cleanup = module.trigger(stateContext, notify);
173
- registrations.push(() => {
174
- notify = async () => {};
175
- cleanup?.();
176
- });
184
+ })();
177
185
  }
186
+
187
+ // Run actions associated with this event
178
188
  if (module.handler) {
179
189
  const cleanup = module.handler(stateContext);
180
190
  registrations.push(cleanup);