@statewalker/fsm 0.27.0 → 0.28.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
@@ -123,4 +123,7 @@ declare function newFsmProcess<C>(context: C, config: FsmStateConfig, load: (sta
123
123
  shutdown: () => Promise<void>
124
124
  ];
125
125
 
126
- 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 Module, 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 };
126
+ type ModuleOrTrigger<C = unknown> = (context: C) => void | (() => void | Promise<void>) | Promise<void | (() => void | Promise<void>)> | AsyncGenerator<string, void, unknown> | Generator<string, void, unknown>;
127
+ declare function startFsmProcess<C>(context: C, config: FsmStateConfig, load: (state: string, event: undefined | string) => undefined | ModuleOrTrigger<C> | ModuleOrTrigger<C>[] | Promise<undefined | ModuleOrTrigger<C> | ModuleOrTrigger<C>[]>, startEvent?: string): () => Promise<void>;
128
+
129
+ 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 Module, type ModuleOrTrigger, 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, startFsmProcess };
package/dist/index.js CHANGED
@@ -435,6 +435,48 @@ function newFsmProcess(context, config, load) {
435
435
  }
436
436
  return [dispatch, shutdown];
437
437
  }
438
+
439
+ // src/startFsmProcess.ts
440
+ function startFsmProcess(context, config, load, startEvent = "") {
441
+ let started = false;
442
+ let terminated = false;
443
+ const process = new FsmProcess(config);
444
+ process.onStateCreate((state) => {
445
+ started = true;
446
+ state.onEnter(async () => {
447
+ const ModuleOrTriggers = await load(state.key, process.event) ?? [];
448
+ for (const ModuleOrTrigger of Array.isArray(ModuleOrTriggers) ? ModuleOrTriggers : [ModuleOrTriggers]) {
449
+ const result = await ModuleOrTrigger?.(context);
450
+ if (isGenerator(result)) {
451
+ state.onExit(() => {
452
+ result.return?.(void 0);
453
+ });
454
+ (async () => {
455
+ for await (const event of result) {
456
+ if (terminated) {
457
+ break;
458
+ }
459
+ if (event !== void 0 && isStateTransitionEnabled(process, event)) {
460
+ await process.dispatch(event);
461
+ }
462
+ }
463
+ })();
464
+ } else if (typeof result === "function") {
465
+ state.onExit(result);
466
+ }
467
+ }
468
+ });
469
+ });
470
+ function isGenerator(value) {
471
+ return typeof value === "object" && value !== null && "next" in value && typeof value.next === "function";
472
+ }
473
+ async function shutdown() {
474
+ await process.shutdown();
475
+ terminated = true;
476
+ }
477
+ process.dispatch(startEvent);
478
+ return shutdown;
479
+ }
438
480
  export {
439
481
  EVENT_ANY,
440
482
  EVENT_EMPTY,
@@ -463,5 +505,6 @@ export {
463
505
  setPrinter,
464
506
  setProcessPrinter,
465
507
  setProcessTracer,
466
- setStateTracer
508
+ setStateTracer,
509
+ startFsmProcess
467
510
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statewalker/fsm",
3
- "version": "0.27.0",
3
+ "version": "0.28.0",
4
4
  "description": "HFSM Implementation",
5
5
  "keywords": [],
6
6
  "homepage": "https://github.com/statewalker/statewalker-fsm",
package/src/index.ts CHANGED
@@ -4,3 +4,4 @@ export * from "./FsmStateConfig.ts";
4
4
  export * from "./FsmStateDescriptor.ts";
5
5
  export * from "./utils/index.ts";
6
6
  export * from "./newFsmProcess.js";
7
+ export * from "./startFsmProcess.js";
@@ -0,0 +1,74 @@
1
+ import type { FsmStateConfig } from "./FsmStateConfig.js";
2
+ import { type Module, newFsmProcess } from "./newFsmProcess.js";
3
+
4
+ export type ModuleOrTrigger<C = unknown> =
5
+ | Module<C>
6
+ | AsyncGenerator<string, void, unknown>
7
+ | Generator<string, void, unknown>;
8
+ export function startFsmProcess<C>(
9
+ context: C,
10
+ config: FsmStateConfig,
11
+ load: (
12
+ state: string,
13
+ event: undefined | string,
14
+ ) =>
15
+ | undefined
16
+ | ModuleOrTrigger<C>
17
+ | ModuleOrTrigger<C>[]
18
+ | Promise<undefined | ModuleOrTrigger<C> | ModuleOrTrigger<C>[]>,
19
+ startEvent = "",
20
+ ) {
21
+ function validateModule(
22
+ module: ModuleOrTrigger<C> | undefined,
23
+ ): Module<C> | undefined {
24
+ if (module === undefined) {
25
+ return undefined;
26
+ }
27
+ if (isGeneratorFunction<string, [C]>(module)) {
28
+ const generator = module as (context: C) => AsyncGenerator<string>;
29
+ return async (context: C) => {
30
+ const iterator = generator(context);
31
+ console.warn('INIT', module, iterator);
32
+ (async () => {
33
+ for await (const event of iterator) {
34
+ await dispatch(event);
35
+ }
36
+ })();
37
+ return () => {
38
+ console.warn('DONE', module, iterator);
39
+ iterator.return?.(void 0);
40
+ };
41
+ };
42
+ }
43
+ return module as Module<C>;
44
+ }
45
+
46
+ const [dispatch, terminate] = newFsmProcess(
47
+ context,
48
+ config,
49
+ async (state, event) => {
50
+ const modules = (await load(state, event)) ?? [];
51
+ const modulesList = Array.isArray(modules) ? modules : [modules];
52
+ return modulesList
53
+ .map(validateModule)
54
+ .flat()
55
+ .filter(Boolean) as Module<C>[];
56
+ },
57
+ );
58
+ dispatch(startEvent);
59
+ return terminate;
60
+ }
61
+
62
+ const AsyncGeneratorFunction = async function* () {}.constructor;
63
+ const GeneratorFunction = function* () {}.constructor;
64
+
65
+ function isGeneratorFunction<T, P extends unknown[] = []>(
66
+ value: unknown,
67
+ ): value is
68
+ | ((...args: P) => AsyncGenerator<T>)
69
+ | ((...args: P) => Generator<T>) {
70
+ return (
71
+ value instanceof AsyncGeneratorFunction ||
72
+ value instanceof GeneratorFunction
73
+ );
74
+ }
@@ -0,0 +1,77 @@
1
+ import { FsmProcess } from "./FsmProcess.js";
2
+ import type { FsmStateConfig } from "./FsmStateConfig.js";
3
+ import { isStateTransitionEnabled } from "./utils/transitions.js";
4
+
5
+ export type Module<C = unknown> = (
6
+ context: C,
7
+ ) =>
8
+ | void
9
+ | (() => void | Promise<void>)
10
+ | Promise<void | (() => void | Promise<void>)>
11
+ | AsyncGenerator<string, void, unknown>
12
+ | Generator<string, void, unknown>;
13
+
14
+ export function startFsmProcess<C>(
15
+ context: C,
16
+ config: FsmStateConfig,
17
+ load: (
18
+ state: string,
19
+ event: undefined | string,
20
+ ) =>
21
+ | undefined
22
+ | Module<C>
23
+ | Module<C>[]
24
+ | Promise<undefined | Module<C> | Module<C>[]>,
25
+ startEvent = ""
26
+ ): () => Promise<void> {
27
+ let started = false;
28
+ let terminated = false;
29
+ const process = new FsmProcess(config);
30
+ process.onStateCreate((state) => {
31
+ started = true;
32
+ state.onEnter(async () => {
33
+ const modules = (await load(state.key, process.event)) ?? [];
34
+ for (const module of Array.isArray(modules) ? modules : [modules]) {
35
+ const result = await module(context);
36
+ if (isGenerator(result)) {
37
+ state.onExit(() => {
38
+ result.return?.(void 0);
39
+ });
40
+ (async () => {
41
+ for await (const event of result) {
42
+ await dispatch(event);
43
+ }
44
+ })();
45
+ } else if (typeof result === "function") {
46
+ state.onExit(result);
47
+ }
48
+ }
49
+ });
50
+ });
51
+
52
+ function isGenerator(
53
+ value: unknown,
54
+ ): value is
55
+ | Generator<string, void, unknown>
56
+ | AsyncGenerator<string, void, unknown> {
57
+ return (
58
+ typeof value === "object" &&
59
+ value !== null &&
60
+ "next" in value &&
61
+ typeof (value as Generator<string, void, unknown>).next === "function"
62
+ );
63
+ }
64
+ async function dispatch(event: string): Promise<boolean> {
65
+ return terminated ||
66
+ (event !== undefined &&
67
+ (!started || isStateTransitionEnabled(process, event)))
68
+ ? process.dispatch(event)
69
+ : false;
70
+ }
71
+ async function shutdown(): Promise<void> {
72
+ await process.shutdown();
73
+ terminated = true;
74
+ }
75
+ dispatch(startEvent);
76
+ return shutdown;
77
+ }
@@ -0,0 +1,78 @@
1
+ import { FsmProcess } from "./FsmProcess.js";
2
+ import type { FsmStateConfig } from "./FsmStateConfig.js";
3
+ import { isStateTransitionEnabled } from "./utils/transitions.js";
4
+
5
+ export type ModuleOrTrigger<C = unknown> = (
6
+ context: C,
7
+ ) =>
8
+ | void
9
+ | (() => void | Promise<void>)
10
+ | Promise<void | (() => void | Promise<void>)>
11
+ | AsyncGenerator<string, void, unknown>
12
+ | Generator<string, void, unknown>;
13
+
14
+ export function startFsmProcess<C>(
15
+ context: C,
16
+ config: FsmStateConfig,
17
+ load: (
18
+ state: string,
19
+ event: undefined | string,
20
+ ) =>
21
+ | undefined
22
+ | ModuleOrTrigger<C>
23
+ | ModuleOrTrigger<C>[]
24
+ | Promise<undefined | ModuleOrTrigger<C> | ModuleOrTrigger<C>[]>,
25
+ startEvent = "",
26
+ ): () => Promise<void> {
27
+ let started = false;
28
+ let terminated = false;
29
+ const process = new FsmProcess(config);
30
+ process.onStateCreate((state) => {
31
+ started = true;
32
+ state.onEnter(async () => {
33
+ const ModuleOrTriggers = (await load(state.key, process.event)) ?? [];
34
+ for (const ModuleOrTrigger of Array.isArray(ModuleOrTriggers) ? ModuleOrTriggers : [ModuleOrTriggers]) {
35
+ const result = await ModuleOrTrigger?.(context);
36
+ if (isGenerator(result)) {
37
+ state.onExit(() => {
38
+ result.return?.(void 0);
39
+ });
40
+ (async () => {
41
+ for await (const event of result) {
42
+ if (terminated) {
43
+ break;
44
+ }
45
+ if (
46
+ event !== undefined &&
47
+ isStateTransitionEnabled(process, event)
48
+ ) {
49
+ await process.dispatch(event);
50
+ }
51
+ }
52
+ })();
53
+ } else if (typeof result === "function") {
54
+ state.onExit(result);
55
+ }
56
+ }
57
+ });
58
+ });
59
+
60
+ function isGenerator(
61
+ value: unknown,
62
+ ): value is
63
+ | Generator<string, void, unknown>
64
+ | AsyncGenerator<string, void, unknown> {
65
+ return (
66
+ typeof value === "object" &&
67
+ value !== null &&
68
+ "next" in value &&
69
+ typeof (value as Generator<string, void, unknown>).next === "function"
70
+ );
71
+ }
72
+ async function shutdown(): Promise<void> {
73
+ await process.shutdown();
74
+ terminated = true;
75
+ }
76
+ process.dispatch(startEvent);
77
+ return shutdown;
78
+ }