@statewalker/fsm 0.20.0 → 0.21.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/context/index.d.ts +23 -0
- package/dist/context/index.js +78 -0
- package/dist/index.d.ts +7 -23
- package/dist/index.js +25 -95
- package/package.json +6 -8
- package/src/FsmBaseClass.ts +4 -2
- package/src/FsmProcess.ts +12 -6
- package/src/FsmStateConfig.ts +5 -5
- package/src/FsmStateDescriptor.ts +1 -1
- package/src/context/handlers.ts +1 -1
- package/src/context/printer.ts +7 -7
- package/src/context/tracer.ts +4 -3
- package/src/index.ts +0 -1
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { FsmState, FsmStateHandler, FsmProcess } from '../index.js';
|
|
2
|
+
|
|
3
|
+
declare const KEY_HANDLERS = "handlers";
|
|
4
|
+
declare function callStateHandlers(state: FsmState): void;
|
|
5
|
+
declare function addSubstateHandlers(state: FsmState, handlers: Record<string, FsmStateHandler>): void;
|
|
6
|
+
|
|
7
|
+
type Printer = (...args: any[]) => void;
|
|
8
|
+
type PrinterConfig = {
|
|
9
|
+
prefix?: string;
|
|
10
|
+
print?: (...args: any[]) => void;
|
|
11
|
+
lineNumbers?: boolean;
|
|
12
|
+
};
|
|
13
|
+
declare const KEY_PRINTER = "printer";
|
|
14
|
+
declare function preparePrinter(process: FsmProcess, { prefix, print, lineNumbers }: PrinterConfig): Printer;
|
|
15
|
+
declare function setPrinter(state: FsmState, config?: PrinterConfig): void;
|
|
16
|
+
declare function setProcessPrinter(process: FsmProcess, config?: PrinterConfig): void;
|
|
17
|
+
declare function getProcessPrinter(process: FsmProcess): Printer;
|
|
18
|
+
declare function getPrinter(state: FsmState): Printer;
|
|
19
|
+
|
|
20
|
+
declare function setProcessTracer(process: FsmProcess, print?: Printer): () => void;
|
|
21
|
+
declare function setStateTracer(state: FsmState, print?: Printer): void;
|
|
22
|
+
|
|
23
|
+
export { KEY_HANDLERS, KEY_PRINTER, type Printer, type PrinterConfig, addSubstateHandlers, callStateHandlers, getPrinter, getProcessPrinter, preparePrinter, setPrinter, setProcessPrinter, setProcessTracer, setStateTracer };
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// src/context/handlers.ts
|
|
2
|
+
var KEY_HANDLERS = "handlers";
|
|
3
|
+
function callStateHandlers(state) {
|
|
4
|
+
const key = state.key;
|
|
5
|
+
for (let parent = state.parent; !!parent; parent = parent?.parent) {
|
|
6
|
+
const handlers = parent.getData(KEY_HANDLERS)?.[key];
|
|
7
|
+
handlers?.forEach((handler) => handler(state));
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function addSubstateHandlers(state, handlers) {
|
|
11
|
+
const oldIndex = state.getData(KEY_HANDLERS);
|
|
12
|
+
const index = oldIndex ? { ...oldIndex } : {};
|
|
13
|
+
for (const [key, handler] of Object.entries(handlers)) {
|
|
14
|
+
const list = index[key] = index[key] || [];
|
|
15
|
+
list.push(handler);
|
|
16
|
+
}
|
|
17
|
+
state.setData(KEY_HANDLERS, index);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// src/context/printer.ts
|
|
21
|
+
var KEY_PRINTER = "printer";
|
|
22
|
+
function preparePrinter(process, { prefix = "", print = console.log, lineNumbers = false }) {
|
|
23
|
+
let lineCounter = 0;
|
|
24
|
+
const shift = () => {
|
|
25
|
+
let prefix2 = "";
|
|
26
|
+
for (let s = process.state?.parent; !!s; s = s.parent) {
|
|
27
|
+
prefix2 += " ";
|
|
28
|
+
}
|
|
29
|
+
return prefix2;
|
|
30
|
+
};
|
|
31
|
+
const getPrefix = lineNumbers ? () => `[${++lineCounter}]${shift()}` : shift;
|
|
32
|
+
const printer = (...args) => print(prefix, getPrefix(), ...args);
|
|
33
|
+
return printer;
|
|
34
|
+
}
|
|
35
|
+
function setPrinter(state, config = {}) {
|
|
36
|
+
const printer = preparePrinter(state.process, config);
|
|
37
|
+
state.setData(KEY_PRINTER, printer);
|
|
38
|
+
}
|
|
39
|
+
function setProcessPrinter(process, config = {}) {
|
|
40
|
+
const printer = preparePrinter(process, config);
|
|
41
|
+
process.setData(KEY_PRINTER, printer);
|
|
42
|
+
}
|
|
43
|
+
function getProcessPrinter(process) {
|
|
44
|
+
return process.getData(KEY_PRINTER) || console.log;
|
|
45
|
+
}
|
|
46
|
+
function getPrinter(state) {
|
|
47
|
+
return state.getData(KEY_PRINTER, true) || getProcessPrinter(state.process);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// src/context/tracer.ts
|
|
51
|
+
function setProcessTracer(process, print) {
|
|
52
|
+
return process.onStateCreate((state) => {
|
|
53
|
+
setStateTracer(state, print);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
function setStateTracer(state, print) {
|
|
57
|
+
state.onEnter(() => {
|
|
58
|
+
const printLine = print || getPrinter(state);
|
|
59
|
+
printLine(`<${state?.key} event="${state.process.event}">`);
|
|
60
|
+
});
|
|
61
|
+
state.onExit(() => {
|
|
62
|
+
const printLine = print || getPrinter(state);
|
|
63
|
+
printLine(`</${state.key}> <!-- event="${state.process.event}" -->`);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
export {
|
|
67
|
+
KEY_HANDLERS,
|
|
68
|
+
KEY_PRINTER,
|
|
69
|
+
addSubstateHandlers,
|
|
70
|
+
callStateHandlers,
|
|
71
|
+
getPrinter,
|
|
72
|
+
getProcessPrinter,
|
|
73
|
+
preparePrinter,
|
|
74
|
+
setPrinter,
|
|
75
|
+
setProcessPrinter,
|
|
76
|
+
setProcessTracer,
|
|
77
|
+
setStateTracer
|
|
78
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -16,11 +16,13 @@ declare const STATE_INITIAL = "";
|
|
|
16
16
|
declare const STATE_FINAL = "";
|
|
17
17
|
declare const EVENT_ANY = "*";
|
|
18
18
|
declare const EVENT_EMPTY = "";
|
|
19
|
+
type FsmStateKey = string;
|
|
20
|
+
type FsmEventKey = string;
|
|
19
21
|
type FsmStateConfig = {
|
|
20
|
-
key:
|
|
21
|
-
transitions:
|
|
22
|
+
key: FsmStateKey;
|
|
23
|
+
transitions?: [from: FsmStateKey, event: FsmEventKey, to: FsmStateKey][];
|
|
22
24
|
states?: FsmStateConfig[];
|
|
23
|
-
}
|
|
25
|
+
} & Record<string, any>;
|
|
24
26
|
|
|
25
27
|
declare class FsmStateDescriptor {
|
|
26
28
|
transitions: Record<string, Record<string, string>>;
|
|
@@ -75,6 +77,7 @@ declare class FsmProcess extends FsmBaseClass {
|
|
|
75
77
|
config: FsmStateConfig;
|
|
76
78
|
rootDescriptor: FsmStateDescriptor;
|
|
77
79
|
constructor(config: FsmStateConfig);
|
|
80
|
+
shutdown(event?: string): Promise<void>;
|
|
78
81
|
dispatch(event: string, mask?: number): Promise<boolean>;
|
|
79
82
|
dump(...args: unknown[]): Promise<FsmProcessDump>;
|
|
80
83
|
restore(dump: FsmProcessDump, ...args: unknown[]): Promise<this>;
|
|
@@ -87,23 +90,4 @@ declare class FsmProcess extends FsmBaseClass {
|
|
|
87
90
|
_update(): boolean;
|
|
88
91
|
}
|
|
89
92
|
|
|
90
|
-
|
|
91
|
-
declare function callStateHandlers(state: FsmState): void;
|
|
92
|
-
declare function addSubstateHandlers(state: FsmState, handlers: Record<string, FsmStateHandler>): void;
|
|
93
|
-
|
|
94
|
-
type Printer = (...args: any[]) => void;
|
|
95
|
-
type PrinterConfig = {
|
|
96
|
-
prefix?: string;
|
|
97
|
-
print?: (...args: any[]) => void;
|
|
98
|
-
lineNumbers?: boolean;
|
|
99
|
-
};
|
|
100
|
-
declare const KEY_PRINTER = "printer";
|
|
101
|
-
declare function preparePrinter(process: FsmProcess, { prefix, print, lineNumbers }: PrinterConfig): Printer;
|
|
102
|
-
declare function setPrinter(state: FsmState, config?: PrinterConfig): void;
|
|
103
|
-
declare function setProcessPrinter(process: FsmProcess, config?: PrinterConfig): void;
|
|
104
|
-
declare function getPrinter(state: FsmState): Printer;
|
|
105
|
-
|
|
106
|
-
declare function setProcessTracer(process: FsmProcess, print?: Printer): () => void;
|
|
107
|
-
declare function setStateTracer(state: FsmState, print?: Printer): void;
|
|
108
|
-
|
|
109
|
-
export { EVENT_ANY, EVENT_EMPTY, FsmProcess, type FsmProcessDump, type FsmProcessDumpHandler, type FsmProcessHandler, FsmState, type FsmStateConfig, FsmStateDescriptor, type FsmStateDump, type FsmStateDumpHandler, type FsmStateErrorHandler, type FsmStateHandler, type FsmStateSyncHandler, KEY_HANDLERS, 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, addSubstateHandlers, callStateHandlers, getPrinter, preparePrinter, setPrinter, setProcessPrinter, setProcessTracer, setStateTracer };
|
|
93
|
+
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 FsmStateSyncHandler, STATE_ANY, STATE_FINAL, STATE_INITIAL, STATUS_ENTER, STATUS_EXIT, STATUS_FINISHED, STATUS_FIRST, STATUS_LAST, STATUS_LEAF, STATUS_NEXT, STATUS_NONE };
|
package/dist/index.js
CHANGED
|
@@ -9,9 +9,11 @@ function bindMethods(obj, ...methods) {
|
|
|
9
9
|
|
|
10
10
|
// src/FsmBaseClass.ts
|
|
11
11
|
var FsmBaseClass = class {
|
|
12
|
-
handlers
|
|
13
|
-
data
|
|
12
|
+
handlers;
|
|
13
|
+
data;
|
|
14
14
|
constructor() {
|
|
15
|
+
this.handlers = {};
|
|
16
|
+
this.data = {};
|
|
15
17
|
bindMethods(this, "setData", "getData");
|
|
16
18
|
}
|
|
17
19
|
setData(key, value) {
|
|
@@ -30,8 +32,7 @@ var FsmBaseClass = class {
|
|
|
30
32
|
}
|
|
31
33
|
_removeHandler(type, handler) {
|
|
32
34
|
let list = this.handlers[type];
|
|
33
|
-
if (!list)
|
|
34
|
-
return;
|
|
35
|
+
if (!list) return;
|
|
35
36
|
list = list.filter((h) => h !== handler);
|
|
36
37
|
if (list.length > 0) {
|
|
37
38
|
this.handlers[type] = list;
|
|
@@ -123,7 +124,7 @@ var FsmStateDescriptor = class _FsmStateDescriptor {
|
|
|
123
124
|
states = {};
|
|
124
125
|
static build(config) {
|
|
125
126
|
const descriptor = new _FsmStateDescriptor();
|
|
126
|
-
for (const [from, event, to] of config.transitions) {
|
|
127
|
+
for (const [from, event, to] of config.transitions || []) {
|
|
127
128
|
const index = descriptor.transitions[from] = descriptor.transitions[from] || {};
|
|
128
129
|
index[event] = to;
|
|
129
130
|
}
|
|
@@ -145,8 +146,7 @@ var FsmStateDescriptor = class _FsmStateDescriptor {
|
|
|
145
146
|
for (let i = 0, len = pairs.length; targetKey === void 0 && i < len; i++) {
|
|
146
147
|
const [stateKey2, eventKey2] = pairs[i];
|
|
147
148
|
const stateTransitions = this.transitions[stateKey2];
|
|
148
|
-
if (!stateTransitions)
|
|
149
|
-
continue;
|
|
149
|
+
if (!stateTransitions) continue;
|
|
150
150
|
targetKey = stateTransitions[eventKey2];
|
|
151
151
|
}
|
|
152
152
|
return targetKey !== void 0 ? targetKey : STATE_FINAL;
|
|
@@ -165,28 +165,35 @@ var STATUS_EXIT = STATUS_LEAF | STATUS_LAST;
|
|
|
165
165
|
var FsmProcess = class extends FsmBaseClass {
|
|
166
166
|
state;
|
|
167
167
|
event;
|
|
168
|
-
status
|
|
168
|
+
status;
|
|
169
169
|
config;
|
|
170
170
|
rootDescriptor;
|
|
171
171
|
constructor(config) {
|
|
172
172
|
super();
|
|
173
|
+
this.status = 0;
|
|
173
174
|
this.rootDescriptor = FsmStateDescriptor.build(config);
|
|
174
175
|
this.config = config;
|
|
175
176
|
bindMethods(this, "dispatch", "dump", "restore");
|
|
176
177
|
}
|
|
178
|
+
async shutdown(event) {
|
|
179
|
+
while (this.state) {
|
|
180
|
+
this.event = event;
|
|
181
|
+
this.status = STATUS_FINISHED;
|
|
182
|
+
await this.state?._runHandler("onExit", this.state);
|
|
183
|
+
this.state = this.state.parent;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
177
186
|
async dispatch(event, mask = STATUS_LEAF) {
|
|
178
187
|
this.event = event;
|
|
179
188
|
while (true) {
|
|
180
189
|
if (this.status & STATUS_EXIT) {
|
|
181
190
|
await this.state?._runHandler("onExit", this.state);
|
|
182
191
|
}
|
|
183
|
-
if (!this._update())
|
|
184
|
-
return false;
|
|
192
|
+
if (!this._update()) return false;
|
|
185
193
|
if (this.status & STATUS_ENTER) {
|
|
186
194
|
await this.state?._runHandler("onEnter", this.state);
|
|
187
195
|
}
|
|
188
|
-
if (this.status & mask)
|
|
189
|
-
return true;
|
|
196
|
+
if (this.status & mask) return true;
|
|
190
197
|
}
|
|
191
198
|
}
|
|
192
199
|
async dump(...args) {
|
|
@@ -199,8 +206,7 @@ var FsmProcess = class extends FsmBaseClass {
|
|
|
199
206
|
return stateDump;
|
|
200
207
|
};
|
|
201
208
|
const dumpStates = async (state, stack = []) => {
|
|
202
|
-
if (!state)
|
|
203
|
-
return stack;
|
|
209
|
+
if (!state) return stack;
|
|
204
210
|
state.parent && await dumpStates(state.parent, stack);
|
|
205
211
|
stack.push(await dumpState(state));
|
|
206
212
|
return stack;
|
|
@@ -244,14 +250,12 @@ var FsmProcess = class extends FsmBaseClass {
|
|
|
244
250
|
return state;
|
|
245
251
|
}
|
|
246
252
|
_getSubstate(parent, prevStateKey) {
|
|
247
|
-
if (!parent)
|
|
248
|
-
return;
|
|
253
|
+
if (!parent) return;
|
|
249
254
|
const toState = parent.descriptor?.getTargetStateKey(
|
|
250
255
|
prevStateKey || STATE_INITIAL,
|
|
251
256
|
this.event || EVENT_EMPTY
|
|
252
257
|
) || STATE_FINAL;
|
|
253
|
-
if (!toState)
|
|
254
|
-
return;
|
|
258
|
+
if (!toState) return;
|
|
255
259
|
return this._newSubstate(parent, toState);
|
|
256
260
|
}
|
|
257
261
|
_newSubstate(parent, toState) {
|
|
@@ -262,8 +266,7 @@ var FsmProcess = class extends FsmBaseClass {
|
|
|
262
266
|
return this._newState(parent, toState, descriptor);
|
|
263
267
|
}
|
|
264
268
|
_update() {
|
|
265
|
-
if (this.status & STATUS_FINISHED)
|
|
266
|
-
return false;
|
|
269
|
+
if (this.status & STATUS_FINISHED) return false;
|
|
267
270
|
const nextState = this.status !== STATUS_NONE ? this.status & STATUS_ENTER ? this._getSubstate(this.state, STATE_INITIAL) : this._getSubstate(this.state?.parent, this.state?.key) : this._newState(void 0, this.config.key, this.rootDescriptor);
|
|
268
271
|
if (nextState !== void 0) {
|
|
269
272
|
this.state = nextState;
|
|
@@ -275,82 +278,17 @@ var FsmProcess = class extends FsmBaseClass {
|
|
|
275
278
|
} else {
|
|
276
279
|
this.status = STATUS_LEAF;
|
|
277
280
|
}
|
|
278
|
-
if (!this.state)
|
|
279
|
-
this.status = STATUS_FINISHED;
|
|
281
|
+
if (!this.state) this.status = STATUS_FINISHED;
|
|
280
282
|
}
|
|
281
283
|
return !(this.status & STATUS_FINISHED);
|
|
282
284
|
}
|
|
283
285
|
};
|
|
284
|
-
|
|
285
|
-
// src/context/handlers.ts
|
|
286
|
-
var KEY_HANDLERS = "handlers";
|
|
287
|
-
function callStateHandlers(state) {
|
|
288
|
-
const key = state.key;
|
|
289
|
-
for (let parent = state.parent; !!parent; parent = parent?.parent) {
|
|
290
|
-
const handlers = parent.getData(KEY_HANDLERS)?.[key];
|
|
291
|
-
handlers?.forEach((handler) => handler(state));
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
function addSubstateHandlers(state, handlers) {
|
|
295
|
-
const oldIndex = state.getData(KEY_HANDLERS);
|
|
296
|
-
const index = oldIndex ? { ...oldIndex } : {};
|
|
297
|
-
for (const [key, handler] of Object.entries(handlers)) {
|
|
298
|
-
const list = index[key] = index[key] || [];
|
|
299
|
-
list.push(handler);
|
|
300
|
-
}
|
|
301
|
-
state.setData(KEY_HANDLERS, index);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// src/context/printer.ts
|
|
305
|
-
var KEY_PRINTER = "printer";
|
|
306
|
-
function preparePrinter(process, { prefix = "", print = console.log, lineNumbers = false }) {
|
|
307
|
-
let lineCounter = 0;
|
|
308
|
-
const shift = () => {
|
|
309
|
-
let prefix2 = "";
|
|
310
|
-
for (let s = process.state?.parent; !!s; s = s.parent) {
|
|
311
|
-
prefix2 += " ";
|
|
312
|
-
}
|
|
313
|
-
return prefix2;
|
|
314
|
-
};
|
|
315
|
-
const getPrefix = lineNumbers ? () => `[${++lineCounter}]${shift()}` : shift;
|
|
316
|
-
const printer = (...args) => print(prefix, getPrefix(), ...args);
|
|
317
|
-
return printer;
|
|
318
|
-
}
|
|
319
|
-
function setPrinter(state, config = {}) {
|
|
320
|
-
const printer = preparePrinter(state.process, config);
|
|
321
|
-
state.setData(KEY_PRINTER, printer);
|
|
322
|
-
}
|
|
323
|
-
function setProcessPrinter(process, config = {}) {
|
|
324
|
-
const printer = preparePrinter(process, config);
|
|
325
|
-
process.setData(KEY_PRINTER, printer);
|
|
326
|
-
}
|
|
327
|
-
function getPrinter(state) {
|
|
328
|
-
return state.getData(KEY_PRINTER, true) || state.process.getData(KEY_PRINTER) || console.log;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// src/context/tracer.ts
|
|
332
|
-
function setProcessTracer(process, print) {
|
|
333
|
-
return process.onStateCreate((state) => {
|
|
334
|
-
setStateTracer(state, print);
|
|
335
|
-
});
|
|
336
|
-
}
|
|
337
|
-
function setStateTracer(state, print) {
|
|
338
|
-
const printLine = print || getPrinter(state);
|
|
339
|
-
state.onEnter(() => {
|
|
340
|
-
printLine(`<${state?.key} event="${state.process.event}">`);
|
|
341
|
-
});
|
|
342
|
-
state.onExit(() => {
|
|
343
|
-
printLine(`</${state.key}> <!-- event="${state.process.event}" -->`);
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
286
|
export {
|
|
347
287
|
EVENT_ANY,
|
|
348
288
|
EVENT_EMPTY,
|
|
349
289
|
FsmProcess,
|
|
350
290
|
FsmState,
|
|
351
291
|
FsmStateDescriptor,
|
|
352
|
-
KEY_HANDLERS,
|
|
353
|
-
KEY_PRINTER,
|
|
354
292
|
STATE_ANY,
|
|
355
293
|
STATE_FINAL,
|
|
356
294
|
STATE_INITIAL,
|
|
@@ -361,13 +299,5 @@ export {
|
|
|
361
299
|
STATUS_LAST,
|
|
362
300
|
STATUS_LEAF,
|
|
363
301
|
STATUS_NEXT,
|
|
364
|
-
STATUS_NONE
|
|
365
|
-
addSubstateHandlers,
|
|
366
|
-
callStateHandlers,
|
|
367
|
-
getPrinter,
|
|
368
|
-
preparePrinter,
|
|
369
|
-
setPrinter,
|
|
370
|
-
setProcessPrinter,
|
|
371
|
-
setProcessTracer,
|
|
372
|
-
setStateTracer
|
|
302
|
+
STATUS_NONE
|
|
373
303
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@statewalker/fsm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "HFSM Implementation",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"homepage": "https://github.com/statewalker/statewalker-fsm",
|
|
@@ -20,14 +20,11 @@
|
|
|
20
20
|
"unpkg": "./dist/index.js",
|
|
21
21
|
"types": "./dist/index.d.ts",
|
|
22
22
|
"exports": {
|
|
23
|
-
".":
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
"dependencies": {
|
|
28
|
-
"@statewalker/tree": "0.10.2"
|
|
23
|
+
".": "./dist/index.js",
|
|
24
|
+
"./context": "./dist/context/index.js"
|
|
29
25
|
},
|
|
30
26
|
"devDependencies": {
|
|
27
|
+
"@changesets/cli": "^2.27.7",
|
|
31
28
|
"@statewalker/eslint-config": "*",
|
|
32
29
|
"@statewalker/typescript-config": "*",
|
|
33
30
|
"eslint": "^9.0.0",
|
|
@@ -40,7 +37,8 @@
|
|
|
40
37
|
"url": "git@github.com:statewalker/statewalker-fsm.git"
|
|
41
38
|
},
|
|
42
39
|
"scripts": {
|
|
43
|
-
"
|
|
40
|
+
"dist": "yarn test && tsup",
|
|
41
|
+
"build": "yarn dist",
|
|
44
42
|
"watch": "tsup --watch",
|
|
45
43
|
"clean": "rm -rf dist",
|
|
46
44
|
"lint": "eslint \"**/*.(js|ts)\"",
|
package/src/FsmBaseClass.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { bindMethods } from "./utils/bindMethods.ts";
|
|
2
2
|
|
|
3
3
|
export class FsmBaseClass {
|
|
4
|
-
handlers: Record<string, Function[]
|
|
5
|
-
data: Record<string, unknown
|
|
4
|
+
handlers: Record<string, Function[]>;
|
|
5
|
+
data: Record<string, unknown>;
|
|
6
6
|
constructor() {
|
|
7
|
+
this.handlers = {};
|
|
8
|
+
this.data = {};
|
|
7
9
|
bindMethods(this, "setData", "getData");
|
|
8
10
|
}
|
|
9
11
|
setData<T>(key: string, value: T) {
|
package/src/FsmProcess.ts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import { bindMethods } from "./utils/bindMethods.ts";
|
|
2
2
|
import { FsmBaseClass } from "./FsmBaseClass.ts";
|
|
3
|
-
import {
|
|
4
|
-
FsmState,
|
|
5
|
-
FsmStateDump,
|
|
6
|
-
FsmStateSyncHandler,
|
|
7
|
-
} from "./FsmState.ts";
|
|
3
|
+
import { FsmState, FsmStateDump, FsmStateSyncHandler } from "./FsmState.ts";
|
|
8
4
|
import {
|
|
9
5
|
EVENT_EMPTY,
|
|
10
6
|
FsmStateConfig,
|
|
@@ -43,17 +39,27 @@ export type FsmProcessDumpHandler = (
|
|
|
43
39
|
export class FsmProcess extends FsmBaseClass {
|
|
44
40
|
state?: FsmState;
|
|
45
41
|
event?: string;
|
|
46
|
-
status: number
|
|
42
|
+
status: number;
|
|
47
43
|
config: FsmStateConfig;
|
|
48
44
|
rootDescriptor: FsmStateDescriptor;
|
|
49
45
|
|
|
50
46
|
constructor(config: FsmStateConfig) {
|
|
51
47
|
super();
|
|
48
|
+
this.status = 0;
|
|
52
49
|
this.rootDescriptor = FsmStateDescriptor.build(config);
|
|
53
50
|
this.config = config;
|
|
54
51
|
bindMethods(this, "dispatch", "dump", "restore");
|
|
55
52
|
}
|
|
56
53
|
|
|
54
|
+
async shutdown(event?: string) {
|
|
55
|
+
while (this.state) {
|
|
56
|
+
this.event = event;
|
|
57
|
+
this.status = STATUS_FINISHED;
|
|
58
|
+
await this.state?._runHandler("onExit", this.state);
|
|
59
|
+
this.state = this.state.parent;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
57
63
|
async dispatch(event: string, mask: number = STATUS_LEAF) {
|
|
58
64
|
this.event = event;
|
|
59
65
|
while (true) {
|
package/src/FsmStateConfig.ts
CHANGED
|
@@ -4,10 +4,10 @@ export const STATE_FINAL = "";
|
|
|
4
4
|
export const EVENT_ANY = "*";
|
|
5
5
|
export const EVENT_EMPTY = "";
|
|
6
6
|
|
|
7
|
+
export type FsmStateKey = string;
|
|
8
|
+
export type FsmEventKey = string;
|
|
7
9
|
export type FsmStateConfig = {
|
|
8
|
-
key:
|
|
9
|
-
|
|
10
|
-
transitions: [string, string, string][];
|
|
11
|
-
|
|
10
|
+
key: FsmStateKey;
|
|
11
|
+
transitions?: [from: FsmStateKey, event: FsmEventKey, to: FsmStateKey][];
|
|
12
12
|
states?: FsmStateConfig[];
|
|
13
|
-
}
|
|
13
|
+
} & Record<string, any>;
|
|
@@ -11,7 +11,7 @@ export class FsmStateDescriptor {
|
|
|
11
11
|
|
|
12
12
|
static build(config: FsmStateConfig) {
|
|
13
13
|
const descriptor = new FsmStateDescriptor();
|
|
14
|
-
for (const [from, event, to] of config.transitions) {
|
|
14
|
+
for (const [from, event, to] of config.transitions || []) {
|
|
15
15
|
const index = (descriptor.transitions[from] =
|
|
16
16
|
descriptor.transitions[from] || {});
|
|
17
17
|
index[event] = to;
|
package/src/context/handlers.ts
CHANGED
package/src/context/printer.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import type { FsmProcess } from "../FsmProcess.ts";
|
|
2
|
+
import type { FsmState } from "../FsmState.ts";
|
|
3
3
|
export type Printer = (...args: any[]) => void;
|
|
4
4
|
export type PrinterConfig = {
|
|
5
5
|
prefix?: string;
|
|
@@ -39,10 +39,10 @@ export function setProcessPrinter(
|
|
|
39
39
|
process.setData(KEY_PRINTER, printer);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
export function getProcessPrinter(process: FsmProcess): Printer {
|
|
43
|
+
return process.getData(KEY_PRINTER) || console.log;
|
|
44
|
+
}
|
|
45
|
+
|
|
42
46
|
export function getPrinter(state: FsmState): Printer {
|
|
43
|
-
return (
|
|
44
|
-
state.getData(KEY_PRINTER, true) ||
|
|
45
|
-
state.process.getData(KEY_PRINTER) ||
|
|
46
|
-
console.log
|
|
47
|
-
);
|
|
47
|
+
return state.getData(KEY_PRINTER, true) || getProcessPrinter(state.process);
|
|
48
48
|
}
|
package/src/context/tracer.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { FsmProcess } from "../FsmProcess.ts";
|
|
2
|
-
import { FsmState } from "../FsmState.ts";
|
|
1
|
+
import type { FsmProcess } from "../FsmProcess.ts";
|
|
2
|
+
import type { FsmState } from "../FsmState.ts";
|
|
3
3
|
import { getPrinter, type Printer } from "./printer.ts";
|
|
4
4
|
|
|
5
5
|
export function setProcessTracer(process: FsmProcess, print?: Printer) {
|
|
@@ -9,11 +9,12 @@ export function setProcessTracer(process: FsmProcess, print?: Printer) {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export function setStateTracer(state: FsmState, print?: Printer) {
|
|
12
|
-
const printLine = print || getPrinter(state);
|
|
13
12
|
state.onEnter(() => {
|
|
13
|
+
const printLine = print || getPrinter(state);
|
|
14
14
|
printLine(`<${state?.key} event="${state.process.event}">`);
|
|
15
15
|
});
|
|
16
16
|
state.onExit(() => {
|
|
17
|
+
const printLine = print || getPrinter(state);
|
|
17
18
|
printLine(`</${state.key}> <!-- event="${state.process.event}" -->`);
|
|
18
19
|
});
|
|
19
20
|
}
|
package/src/index.ts
CHANGED