@statewalker/fsm 0.30.0 → 0.31.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 +2 -10
- package/dist/index.js +63 -13
- package/package.json +1 -1
- package/src/orchestrator/launcher.ts +86 -29
- package/src/utils/newFsmStateHandler.ts +0 -199
package/dist/index.d.ts
CHANGED
|
@@ -231,15 +231,7 @@ type ProcessConfig = {
|
|
|
231
231
|
[state: string]: StageHandler | StageHandler[];
|
|
232
232
|
})[];
|
|
233
233
|
};
|
|
234
|
-
|
|
235
|
-
start: string[];
|
|
236
|
-
context?: Record<string, unknown>;
|
|
237
|
-
processes: ProcessConfig[];
|
|
238
|
-
};
|
|
239
|
-
type LauncherRootContext = Record<string, unknown> & {
|
|
240
|
-
launch: (processName: string, context: Record<string, unknown>, startEvent?: string) => Promise<() => Promise<void>>;
|
|
241
|
-
};
|
|
242
|
-
declare function launcher(init: (root: LauncherRootContext) => ProcessLauncherConfig | Promise<ProcessLauncherConfig>): Promise<() => Promise<void>>;
|
|
234
|
+
declare function launcher(options: unknown): Promise<() => Promise<void>>;
|
|
243
235
|
|
|
244
236
|
interface IProcessConfigManager {
|
|
245
237
|
getProcessConfig(processName: string): FsmStateConfig;
|
|
@@ -283,4 +275,4 @@ declare function setStateTracer(state: FsmState, print?: Printer): void;
|
|
|
283
275
|
declare function isStateTransitionEnabled(process: FsmProcess, event: string): boolean;
|
|
284
276
|
declare function getStateTransitions(state?: FsmState): [from: string, event: string, to: string][];
|
|
285
277
|
|
|
286
|
-
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, type IProcessConfigManager, KEY_DISPATCH, KEY_EVENT, KEY_LAUNCH_PROCESS, KEY_PRINTER, KEY_REGISTER_CONFIG, KEY_REGISTER_HANDLERS, KEY_STATES, KEY_TERMINATE, type
|
|
278
|
+
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, type IProcessConfigManager, KEY_DISPATCH, KEY_EVENT, KEY_LAUNCH_PROCESS, KEY_PRINTER, KEY_REGISTER_CONFIG, KEY_REGISTER_HANDLERS, KEY_STATES, KEY_TERMINATE, type Module, type Printer, type PrinterConfig, type ProcessConfig, ProcessConfigManager, STATE_ANY, STATE_FINAL, STATE_INITIAL, STATUS_ENTER, STATUS_EXIT, STATUS_FINISHED, STATUS_FIRST, STATUS_LAST, STATUS_LEAF, STATUS_NEXT, STATUS_NONE, type StageHandler, getPrinter, getProcessPrinter, getStateTransitions, isStateTransitionEnabled, launcher, newFsmProcess, newProcess, preparePrinter, setPrinter, setProcessPrinter, setProcessTracer, setStateTracer, startFsmProcess, toStageHandlers };
|
package/dist/index.js
CHANGED
|
@@ -610,29 +610,79 @@ async function startFsmProcess(context, config, load, startEvent = "") {
|
|
|
610
610
|
}
|
|
611
611
|
|
|
612
612
|
// src/orchestrator/launcher.ts
|
|
613
|
-
async function launcher(
|
|
613
|
+
async function launcher(options) {
|
|
614
614
|
const configsManager = new ProcessConfigManager();
|
|
615
615
|
async function launch(processName, context, startEvent = "start") {
|
|
616
616
|
const config2 = configsManager.getProcessConfig(processName);
|
|
617
617
|
const load = configsManager.getHandlersLoader(processName);
|
|
618
618
|
return startFsmProcess(context, config2, load, startEvent);
|
|
619
619
|
}
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
if (
|
|
626
|
-
|
|
620
|
+
function registerProcess(configsManager2, process) {
|
|
621
|
+
if (!process || typeof process !== "object") {
|
|
622
|
+
throw new Error("Invalid process configuration: not an object");
|
|
623
|
+
}
|
|
624
|
+
const processName = process.name;
|
|
625
|
+
if (!processName || typeof processName !== "string") {
|
|
626
|
+
throw new Error("Invalid process configuration: missing or invalid name");
|
|
627
|
+
}
|
|
628
|
+
const processConfig = process.config;
|
|
629
|
+
if (processConfig && typeof processConfig !== "object") {
|
|
630
|
+
throw new Error(
|
|
631
|
+
`Invalid process configuration: config is not an object in process ${process.name}`
|
|
632
|
+
);
|
|
627
633
|
}
|
|
628
|
-
if (
|
|
629
|
-
|
|
634
|
+
if (processConfig) {
|
|
635
|
+
configsManager2.registerConfig(process.name, processConfig);
|
|
630
636
|
}
|
|
637
|
+
let processHandlers = process.handlers;
|
|
638
|
+
if (processHandlers) {
|
|
639
|
+
if (typeof processHandlers !== "object") {
|
|
640
|
+
throw new Error(
|
|
641
|
+
`Invalid process configuration: handlers is not an array or object in process ${process.name}`
|
|
642
|
+
);
|
|
643
|
+
}
|
|
644
|
+
processHandlers = Array.isArray(processHandlers) ? processHandlers : [processHandlers];
|
|
645
|
+
configsManager2.registerHandlers(process.name, ...processHandlers);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
const rootContext = { "fsm:launch": launch };
|
|
649
|
+
const init = typeof options === "function" ? options : async () => options;
|
|
650
|
+
const config = await init(rootContext);
|
|
651
|
+
if (typeof config !== "object" || !config) {
|
|
652
|
+
throw new Error("Invalid launcher configuration");
|
|
653
|
+
}
|
|
654
|
+
let processes = config.processes ?? config.default;
|
|
655
|
+
if (!processes || typeof processes !== "object") {
|
|
656
|
+
throw new Error("Invalid launcher configuration: missing processes");
|
|
657
|
+
}
|
|
658
|
+
processes = Array.isArray(processes) ? processes : [];
|
|
659
|
+
for (const process of processes) {
|
|
660
|
+
registerProcess(configsManager, process);
|
|
631
661
|
}
|
|
632
662
|
const shutdowns = [];
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
663
|
+
const startProcesses = Array.isArray(config.start) ? config.start : [];
|
|
664
|
+
let initContext = (context) => context;
|
|
665
|
+
const configContext = config.context ?? config.init;
|
|
666
|
+
if (configContext) {
|
|
667
|
+
if (typeof configContext === "function") {
|
|
668
|
+
initContext = configContext;
|
|
669
|
+
} else if (typeof configContext === "object") {
|
|
670
|
+
initContext = () => ({ parent: configContext });
|
|
671
|
+
} else {
|
|
672
|
+
throw new Error("Invalid launcher configuration: context");
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
for (const processName of startProcesses) {
|
|
676
|
+
let context = {
|
|
677
|
+
parent: rootContext,
|
|
678
|
+
"fsm:name": processName
|
|
679
|
+
};
|
|
680
|
+
context = initContext(context) ?? context;
|
|
681
|
+
if (typeof context !== "object") {
|
|
682
|
+
throw new Error(
|
|
683
|
+
`Invalid launcher context for process "${processName}". Expected an object, but recieved ${typeof context}.`
|
|
684
|
+
);
|
|
685
|
+
}
|
|
636
686
|
const shutdown = await launch(processName, context);
|
|
637
687
|
shutdowns.push(shutdown);
|
|
638
688
|
}
|
package/package.json
CHANGED
|
@@ -13,24 +13,21 @@ export type ProcessConfig = {
|
|
|
13
13
|
}
|
|
14
14
|
)[];
|
|
15
15
|
};
|
|
16
|
-
export type ProcessLauncherConfig = {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
};
|
|
16
|
+
// export type ProcessLauncherConfig = {
|
|
17
|
+
// start: string[];
|
|
18
|
+
// context?: Record<string, unknown>;
|
|
19
|
+
// processes: ProcessConfig[];
|
|
20
|
+
// };
|
|
21
21
|
|
|
22
|
-
export type LauncherRootContext = Record<string, unknown> & {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
root: LauncherRootContext,
|
|
32
|
-
) => ProcessLauncherConfig | Promise<ProcessLauncherConfig>,
|
|
33
|
-
) {
|
|
22
|
+
// export type LauncherRootContext = Record<string, unknown> & {
|
|
23
|
+
// launch: (
|
|
24
|
+
// processName: string,
|
|
25
|
+
// context: Record<string, unknown>,
|
|
26
|
+
// startEvent?: string,
|
|
27
|
+
// ) => Promise<() => Promise<void>>;
|
|
28
|
+
// };
|
|
29
|
+
|
|
30
|
+
export async function launcher(options: unknown) {
|
|
34
31
|
const configsManager = new ProcessConfigManager();
|
|
35
32
|
|
|
36
33
|
async function launch(
|
|
@@ -43,23 +40,83 @@ export async function launcher(
|
|
|
43
40
|
return startFsmProcess(context, config, load, startEvent);
|
|
44
41
|
}
|
|
45
42
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
43
|
+
function registerProcess(
|
|
44
|
+
configsManager: ProcessConfigManager,
|
|
45
|
+
process: ProcessConfig,
|
|
46
|
+
) {
|
|
47
|
+
if (!process || typeof process !== "object") {
|
|
48
|
+
throw new Error("Invalid process configuration: not an object");
|
|
49
|
+
}
|
|
50
|
+
const processName = process.name;
|
|
51
|
+
if (!processName || typeof processName !== "string") {
|
|
52
|
+
throw new Error("Invalid process configuration: missing or invalid name");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const processConfig = process.config;
|
|
56
|
+
if (processConfig && typeof processConfig !== "object") {
|
|
57
|
+
throw new Error(
|
|
58
|
+
`Invalid process configuration: config is not an object in process ${process.name}`,
|
|
59
|
+
);
|
|
53
60
|
}
|
|
54
|
-
if (
|
|
55
|
-
configsManager.
|
|
61
|
+
if (processConfig) {
|
|
62
|
+
configsManager.registerConfig(process.name, processConfig);
|
|
63
|
+
}
|
|
64
|
+
let processHandlers = process.handlers;
|
|
65
|
+
if (processHandlers) {
|
|
66
|
+
if (typeof processHandlers !== "object") {
|
|
67
|
+
throw new Error(
|
|
68
|
+
`Invalid process configuration: handlers is not an array or object in process ${process.name}`,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
processHandlers = Array.isArray(processHandlers)
|
|
72
|
+
? processHandlers
|
|
73
|
+
: [processHandlers];
|
|
74
|
+
configsManager.registerHandlers(process.name, ...processHandlers);
|
|
56
75
|
}
|
|
57
76
|
}
|
|
58
77
|
|
|
78
|
+
const rootContext = { "fsm:launch": launch };
|
|
79
|
+
const init = typeof options === "function" ? options : async () => options;
|
|
80
|
+
const config = await init(rootContext);
|
|
81
|
+
if (typeof config !== "object" || !config) {
|
|
82
|
+
throw new Error("Invalid launcher configuration");
|
|
83
|
+
}
|
|
84
|
+
let processes = config.processes ?? config.default;
|
|
85
|
+
if (!processes || typeof processes !== "object") {
|
|
86
|
+
throw new Error("Invalid launcher configuration: missing processes");
|
|
87
|
+
}
|
|
88
|
+
processes = Array.isArray(processes) ? processes : [];
|
|
89
|
+
for (const process of processes) {
|
|
90
|
+
registerProcess(configsManager, process);
|
|
91
|
+
}
|
|
92
|
+
|
|
59
93
|
const shutdowns: (() => Promise<void>)[] = [];
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
94
|
+
const startProcesses = Array.isArray(config.start) ? config.start : [];
|
|
95
|
+
let initContext: (
|
|
96
|
+
parent: Record<string, unknown>,
|
|
97
|
+
) => Record<string, unknown> = (context) => context;
|
|
98
|
+
|
|
99
|
+
const configContext = config.context ?? config.init;
|
|
100
|
+
if (configContext) {
|
|
101
|
+
if (typeof configContext === "function") {
|
|
102
|
+
initContext = configContext;
|
|
103
|
+
} else if (typeof configContext === "object") {
|
|
104
|
+
initContext = () => ({ parent: configContext });
|
|
105
|
+
} else {
|
|
106
|
+
throw new Error("Invalid launcher configuration: context");
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
for (const processName of startProcesses) {
|
|
110
|
+
let context: Record<string, unknown> = {
|
|
111
|
+
parent: rootContext,
|
|
112
|
+
"fsm:name": processName,
|
|
113
|
+
};
|
|
114
|
+
context = initContext(context) ?? context;
|
|
115
|
+
if (typeof context !== "object") {
|
|
116
|
+
throw new Error(
|
|
117
|
+
`Invalid launcher context for process "${processName}". Expected an object, but recieved ${typeof context}.`,
|
|
118
|
+
);
|
|
119
|
+
}
|
|
63
120
|
const shutdown = await launch(processName, context);
|
|
64
121
|
shutdowns.push(shutdown);
|
|
65
122
|
}
|
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
import { FsmState } from "../FsmState";
|
|
2
|
-
import { isStateTransitionEnabled } from "./transitions";
|
|
3
|
-
|
|
4
|
-
export function newModuleLoader<T>(baseUrl: string) {
|
|
5
|
-
const cache: Record<string, Promise<T>> = {};
|
|
6
|
-
return async (path: string) => {
|
|
7
|
-
return (cache[path] =
|
|
8
|
-
cache[path] ||
|
|
9
|
-
(async () => {
|
|
10
|
-
const suffixes = ["/", "", ".ts", "/index.ts"];
|
|
11
|
-
for (const suffix of suffixes) {
|
|
12
|
-
const url = new URL(path + suffix, baseUrl).pathname;
|
|
13
|
-
try {
|
|
14
|
-
return (await import(url)) as T;
|
|
15
|
-
} catch (error) {
|
|
16
|
-
continue;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
})());
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export async function newHandlerLoader(
|
|
24
|
-
baseUrl: string,
|
|
25
|
-
loader: (
|
|
26
|
-
path: string,
|
|
27
|
-
) => Promise<undefined | FsmStateModule> = newModuleLoader(baseUrl),
|
|
28
|
-
): Promise<(state: FsmState) => Promise<FsmStateModule[]>> {
|
|
29
|
-
return async (state: FsmState) => {
|
|
30
|
-
const stack: string[] = [];
|
|
31
|
-
for (let s: FsmState | undefined = state; s; s = s.parent) {
|
|
32
|
-
stack.unshift(s.key);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const handlers: FsmStateModule[] = [];
|
|
36
|
-
let topName: undefined | string;
|
|
37
|
-
while (stack.length > 0) {
|
|
38
|
-
const lastSegment = stack.pop();
|
|
39
|
-
if (!topName) {
|
|
40
|
-
topName = lastSegment;
|
|
41
|
-
if (topName === undefined) break;
|
|
42
|
-
}
|
|
43
|
-
const path = [...stack, topName].join("/");
|
|
44
|
-
const handler = await loader(path);
|
|
45
|
-
handler && handlers.push(handler);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return handlers;
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/*
|
|
53
|
-
* StateContext:
|
|
54
|
-
- Each context is a function or a class
|
|
55
|
-
- It recieves the stack of state objects
|
|
56
|
-
- The returned value is used to pass to the handler function
|
|
57
|
-
* Trigger function
|
|
58
|
-
- recieves the context instance
|
|
59
|
-
- notifies about changes using the given callback
|
|
60
|
-
* Handler
|
|
61
|
-
- recieves the context
|
|
62
|
-
- starts activities on call
|
|
63
|
-
- optionally returns a cleanup function
|
|
64
|
-
- could return a promise with optional cleanup function; the state is not
|
|
65
|
-
activated until all handlers return the control
|
|
66
|
-
*/
|
|
67
|
-
export type FsmStateModule<
|
|
68
|
-
FsmStateContext = Record<string, unknown>,
|
|
69
|
-
FsmStateStore = Record<string, unknown>,
|
|
70
|
-
> = {
|
|
71
|
-
context?:
|
|
72
|
-
| ((...stack: FsmStateStore[]) => FsmStateContext)
|
|
73
|
-
| (new (
|
|
74
|
-
...stack: FsmStateStore[]
|
|
75
|
-
) => FsmStateContext);
|
|
76
|
-
// trigger?: (context: FsmStateContext) => AsyncGenerator<string>;
|
|
77
|
-
trigger?: (context: FsmStateContext) => AsyncIterator<string>;
|
|
78
|
-
handler?: (
|
|
79
|
-
context: FsmStateContext,
|
|
80
|
-
) => void | (() => void) | Promise<void | (() => void)>;
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
export function newModuleContext<
|
|
84
|
-
C = Record<string, unknown>,
|
|
85
|
-
S = Record<string, unknown>,
|
|
86
|
-
>(module: FsmStateModule<C, S>, ...stack: S[]): C {
|
|
87
|
-
if (isClass<S[], C>(module.context)) {
|
|
88
|
-
return new module.context(...stack) as unknown as C;
|
|
89
|
-
} else if (isFunction<S[], C>(module.context)) {
|
|
90
|
-
return module.context(...stack);
|
|
91
|
-
} else {
|
|
92
|
-
return stack[0] as unknown as C;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
function isFunction<T extends Array<unknown>, R = unknown>(
|
|
96
|
-
value: unknown,
|
|
97
|
-
): value is (...args: T) => R {
|
|
98
|
-
return typeof value === "function";
|
|
99
|
-
}
|
|
100
|
-
function isClass<T extends Array<unknown>, R = unknown>(
|
|
101
|
-
value: unknown,
|
|
102
|
-
): value is new (
|
|
103
|
-
...args: T
|
|
104
|
-
) => R {
|
|
105
|
-
if (!isFunction(value)) return false;
|
|
106
|
-
return value?.prototype?.constructor?.toString().match(/^class/);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export function newStateHandlers<
|
|
110
|
-
FsmStateContext = Record<string, unknown>,
|
|
111
|
-
FsmStateStore = Record<string, unknown>,
|
|
112
|
-
>(
|
|
113
|
-
loader: (
|
|
114
|
-
state: FsmState,
|
|
115
|
-
) => Promise<FsmStateModule<FsmStateContext, FsmStateStore>[]>,
|
|
116
|
-
newContext: (state: FsmState, ...stack: FsmStateStore[]) => FsmStateStore = (
|
|
117
|
-
state,
|
|
118
|
-
) =>
|
|
119
|
-
({
|
|
120
|
-
state: state.key,
|
|
121
|
-
event: state.process.event,
|
|
122
|
-
}) as unknown as FsmStateStore,
|
|
123
|
-
contextCache: Map<unknown, FsmStateContext> = new Map<
|
|
124
|
-
unknown,
|
|
125
|
-
FsmStateContext
|
|
126
|
-
>(),
|
|
127
|
-
) {
|
|
128
|
-
const triggers = new Map<unknown, () => void>();
|
|
129
|
-
return (state: FsmState) => {
|
|
130
|
-
const stack: FsmStateStore[] = [];
|
|
131
|
-
for (let s: FsmState | undefined = state.parent; !!s; s = s.parent) {
|
|
132
|
-
const store = s.getData<FsmStateStore>("context");
|
|
133
|
-
store && stack.push(store);
|
|
134
|
-
}
|
|
135
|
-
const store = newContext(state, ...stack);
|
|
136
|
-
stack.unshift(store);
|
|
137
|
-
state.setData("context", store);
|
|
138
|
-
|
|
139
|
-
let registrations: (void | (() => void) | Promise<void | (() => void)>)[] =
|
|
140
|
-
[];
|
|
141
|
-
state.onExit(async () => {
|
|
142
|
-
for (const r of registrations) {
|
|
143
|
-
const registration = await r;
|
|
144
|
-
registration?.();
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
state.onEnter(async () => {
|
|
148
|
-
const modules = await loader(state);
|
|
149
|
-
if (!modules?.length) return;
|
|
150
|
-
for (const module of modules) {
|
|
151
|
-
const contextKey: unknown = module.context;
|
|
152
|
-
// Create state context or get it from the local cache
|
|
153
|
-
let stateContext = contextCache.get(contextKey);
|
|
154
|
-
if (!stateContext) {
|
|
155
|
-
stateContext = newModuleContext(module, ...stack);
|
|
156
|
-
contextCache.set(contextKey, stateContext);
|
|
157
|
-
registrations.push(() => {
|
|
158
|
-
contextCache.delete(contextKey);
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Initialize triggers
|
|
163
|
-
if (module.trigger && !triggers.has(module.trigger)) {
|
|
164
|
-
const process = state.process;
|
|
165
|
-
const iterator = module.trigger(stateContext);
|
|
166
|
-
let finalize: undefined | (() => void);
|
|
167
|
-
const end = new Promise<{ done: boolean; value?: string }>(
|
|
168
|
-
(r) => (finalize = () => r({ done: true })),
|
|
169
|
-
);
|
|
170
|
-
registrations.push(() => triggers.delete(module.trigger));
|
|
171
|
-
registrations.push(finalize);
|
|
172
|
-
registrations.push(() => iterator?.return?.(void 0));
|
|
173
|
-
(async () => {
|
|
174
|
-
try {
|
|
175
|
-
while (true) {
|
|
176
|
-
const { done, value: event } = await Promise.race([
|
|
177
|
-
iterator.next(),
|
|
178
|
-
end,
|
|
179
|
-
]);
|
|
180
|
-
if (done) break;
|
|
181
|
-
if (event && isStateTransitionEnabled(process, event)) {
|
|
182
|
-
process.dispatch(event);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
} finally {
|
|
186
|
-
finalize?.();
|
|
187
|
-
}
|
|
188
|
-
})();
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Run actions associated with this event
|
|
192
|
-
if (module.handler) {
|
|
193
|
-
const cleanup = module.handler(stateContext);
|
|
194
|
-
registrations.push(cleanup);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
});
|
|
198
|
-
};
|
|
199
|
-
}
|