@statewalker/fsm 0.23.0 → 0.23.1

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
@@ -88,4 +88,29 @@ declare class FsmProcess extends FsmBaseClass {
88
88
  _update(): boolean;
89
89
  }
90
90
 
91
- 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, STATE_ANY, STATE_FINAL, STATE_INITIAL, STATUS_ENTER, STATUS_EXIT, STATUS_FINISHED, STATUS_FIRST, STATUS_LAST, STATUS_LEAF, STATUS_NEXT, STATUS_NONE };
91
+ type Printer = (...args: unknown[]) => void;
92
+ type PrinterConfig = {
93
+ prefix?: string;
94
+ print?: (...args: unknown[]) => void;
95
+ lineNumbers?: boolean;
96
+ };
97
+ declare const KEY_PRINTER = "printer";
98
+ declare function preparePrinter(process: FsmProcess, { prefix, print, lineNumbers }: PrinterConfig): Printer;
99
+ declare function setPrinter(state: FsmState, config?: PrinterConfig): void;
100
+ declare function setProcessPrinter(process: FsmProcess, config?: PrinterConfig): void;
101
+ declare function getProcessPrinter(process: FsmProcess): Printer;
102
+ declare function getPrinter(state: FsmState): Printer;
103
+
104
+ declare function newProcess(config: FsmStateConfig, { prefix, print, lineNumbers, }: {
105
+ prefix?: string;
106
+ print?: (...args: any[]) => void;
107
+ lineNumbers?: boolean;
108
+ }): FsmProcess;
109
+
110
+ declare function setProcessTracer(process: FsmProcess, print?: Printer): () => void;
111
+ declare function setStateTracer(state: FsmState, print?: Printer): void;
112
+
113
+ declare function isStateTransitionEnabled(process: FsmProcess, event: string): boolean;
114
+ declare function getStateTransitions(state?: FsmState): [from: string, event: string, to: string][];
115
+
116
+ 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 };
package/dist/index.js CHANGED
@@ -1,27 +1,391 @@
1
- import {
2
- EVENT_ANY,
3
- EVENT_EMPTY,
4
- FsmProcess,
5
- FsmState,
6
- FsmStateDescriptor,
7
- STATE_ANY,
8
- STATE_FINAL,
9
- STATE_INITIAL,
10
- STATUS_ENTER,
11
- STATUS_EXIT,
12
- STATUS_FINISHED,
13
- STATUS_FIRST,
14
- STATUS_LAST,
15
- STATUS_LEAF,
16
- STATUS_NEXT,
17
- STATUS_NONE
18
- } from "./chunk-CRID7TZR.js";
1
+ // src/FsmBaseClass.ts
2
+ var FsmBaseClass = class {
3
+ constructor() {
4
+ this.handlers = {};
5
+ this.data = {};
6
+ bindMethods(this, "setData", "getData");
7
+ }
8
+ setData(key, value) {
9
+ this.data[key] = value;
10
+ return this;
11
+ }
12
+ getData(key) {
13
+ return this.data[key];
14
+ }
15
+ // ----------------------------------------------
16
+ // internal methods
17
+ _addHandler(type, handler, direct = true) {
18
+ const list = this.handlers[type] = this.handlers[type] || [];
19
+ direct ? list.push(handler) : list.unshift(handler);
20
+ return () => this._removeHandler(type, handler);
21
+ }
22
+ _removeHandler(type, handler) {
23
+ let list = this.handlers[type];
24
+ if (!list) return;
25
+ list = list.filter((h) => h !== handler);
26
+ if (list.length > 0) {
27
+ this.handlers[type] = list;
28
+ } else {
29
+ delete this.handlers[type];
30
+ }
31
+ }
32
+ async _runHandler(type, ...args) {
33
+ const list = this.handlers[type] || [];
34
+ const promises = list.map((handler) => handler(...args));
35
+ for (const promise of promises) {
36
+ try {
37
+ await promise;
38
+ } catch (error) {
39
+ await this._handleError(error);
40
+ }
41
+ }
42
+ }
43
+ async _handleError(error) {
44
+ console.error(error);
45
+ }
46
+ };
47
+ function bindMethods(obj, ...methods) {
48
+ const o = obj;
49
+ for (const methodName of methods) {
50
+ const method = o[methodName];
51
+ if (typeof method !== "function") continue;
52
+ o[methodName] = method.bind(o);
53
+ }
54
+ return obj;
55
+ }
56
+
57
+ // src/FsmState.ts
58
+ var FsmState = class extends FsmBaseClass {
59
+ constructor(process, parent, key, descriptor) {
60
+ super();
61
+ this.process = process;
62
+ this.key = key;
63
+ this.parent = parent;
64
+ this.descriptor = descriptor;
65
+ bindMethods(
66
+ this,
67
+ "onEnter",
68
+ "onExit",
69
+ "dump",
70
+ "restore",
71
+ "useData",
72
+ "onStateError"
73
+ );
74
+ }
75
+ onEnter(handler) {
76
+ return this._addHandler("onEnter", handler, true);
77
+ }
78
+ onExit(handler) {
79
+ return this._addHandler("onExit", handler, false);
80
+ }
81
+ onStateError(handler) {
82
+ return this._addHandler("onStateError", handler);
83
+ }
84
+ dump(handler) {
85
+ return this._addHandler("dump", handler, true);
86
+ }
87
+ restore(handler) {
88
+ return this._addHandler("restore", handler, true);
89
+ }
90
+ getData(key, recursive = true) {
91
+ return this.data[key] ?? (recursive ? this.parent?.getData(key, recursive) : void 0);
92
+ }
93
+ useData(key) {
94
+ return [
95
+ (recursive = true) => this.getData(key, recursive),
96
+ (value) => this.setData(key, value)
97
+ ];
98
+ }
99
+ async _handleError(error) {
100
+ await this._runHandler("onStateError", this, error);
101
+ await this.process._handleStateError(this, error);
102
+ }
103
+ };
104
+
105
+ // src/FsmStateConfig.ts
106
+ var STATE_ANY = "*";
107
+ var STATE_INITIAL = "";
108
+ var STATE_FINAL = "";
109
+ var EVENT_ANY = "*";
110
+ var EVENT_EMPTY = "";
111
+
112
+ // src/FsmStateDescriptor.ts
113
+ var FsmStateDescriptor = class _FsmStateDescriptor {
114
+ constructor() {
115
+ this.transitions = {};
116
+ this.states = {};
117
+ }
118
+ static build(config) {
119
+ const descriptor = new _FsmStateDescriptor();
120
+ for (const [from, event, to] of config.transitions || []) {
121
+ const index = descriptor.transitions[from] = descriptor.transitions[from] || {};
122
+ index[event] = to;
123
+ }
124
+ if (config.states) {
125
+ for (const substateConfig of config.states) {
126
+ descriptor.states[substateConfig.key] = this.build(substateConfig);
127
+ }
128
+ }
129
+ return descriptor;
130
+ }
131
+ getTargetStateKey(stateKey, eventKey) {
132
+ const pairs = [
133
+ [stateKey, eventKey],
134
+ [STATE_ANY, eventKey],
135
+ [stateKey, EVENT_ANY],
136
+ [STATE_ANY, EVENT_ANY]
137
+ ];
138
+ let targetKey;
139
+ for (let i = 0, len = pairs.length; targetKey === void 0 && i < len; i++) {
140
+ const [stateKey2, eventKey2] = pairs[i];
141
+ const stateTransitions = this.transitions[stateKey2];
142
+ if (!stateTransitions) continue;
143
+ targetKey = stateTransitions[eventKey2];
144
+ }
145
+ return targetKey !== void 0 ? targetKey : STATE_FINAL;
146
+ }
147
+ };
148
+
149
+ // src/FsmProcess.ts
150
+ var STATUS_NONE = 0;
151
+ var STATUS_FIRST = 1;
152
+ var STATUS_NEXT = 2;
153
+ var STATUS_LEAF = 4;
154
+ var STATUS_LAST = 8;
155
+ var STATUS_FINISHED = 16;
156
+ var STATUS_ENTER = STATUS_FIRST | STATUS_NEXT;
157
+ var STATUS_EXIT = STATUS_LEAF | STATUS_LAST;
158
+ var FsmProcess = class extends FsmBaseClass {
159
+ constructor(config) {
160
+ super();
161
+ this.status = 0;
162
+ this.rootDescriptor = FsmStateDescriptor.build(config);
163
+ this.config = config;
164
+ bindMethods(this, "dispatch", "dump", "restore");
165
+ }
166
+ async shutdown(event) {
167
+ while (this.state) {
168
+ this.event = event;
169
+ this.status = STATUS_FINISHED;
170
+ await this.state?._runHandler("onExit", this.state);
171
+ this.state = this.state.parent;
172
+ }
173
+ }
174
+ async dispatch(event, mask = STATUS_LEAF) {
175
+ this.event = event;
176
+ while (true) {
177
+ if (this.status & STATUS_EXIT) {
178
+ await this.state?._runHandler("onExit", this.state);
179
+ }
180
+ if (!this._update()) return false;
181
+ if (this.status & STATUS_ENTER) {
182
+ await this.state?._runHandler("onEnter", this.state);
183
+ }
184
+ if (this.status & mask) return true;
185
+ }
186
+ }
187
+ async dump(...args) {
188
+ const dumpState = async (state) => {
189
+ const stateDump = {
190
+ key: state.key,
191
+ data: {}
192
+ };
193
+ await state._runHandler("dump", state, stateDump.data, ...args);
194
+ return stateDump;
195
+ };
196
+ const dumpStates = async (state, stack = []) => {
197
+ if (!state) return stack;
198
+ state.parent && await dumpStates(state.parent, stack);
199
+ stack.push(await dumpState(state));
200
+ return stack;
201
+ };
202
+ const dump = {
203
+ status: this.status,
204
+ event: this.event,
205
+ stack: await dumpStates(this.state)
206
+ };
207
+ return dump;
208
+ }
209
+ async restore(dump, ...args) {
210
+ this.status = dump.status || 0;
211
+ this.event = dump.event;
212
+ this.state = void 0;
213
+ for (let i = 0; i < dump.stack.length; i++) {
214
+ const stateDump = dump.stack[i];
215
+ this.state = this.state ? this._newSubstate(this.state, stateDump.key) : this._newState(void 0, stateDump.key, this.rootDescriptor);
216
+ await this.state._runHandler(
217
+ "restore",
218
+ this.state,
219
+ stateDump.data,
220
+ ...args
221
+ );
222
+ }
223
+ return this;
224
+ }
225
+ onStateCreate(handler) {
226
+ return this._addHandler("onStateCreate", handler, true);
227
+ }
228
+ onStateError(handler) {
229
+ return this._addHandler("onStateError", handler);
230
+ }
231
+ async _handleStateError(state, error) {
232
+ await this._runHandler("onStateError", state, error);
233
+ this._handleError(error);
234
+ }
235
+ _newState(parent, key, descriptor) {
236
+ const state = new FsmState(this, parent, key, descriptor);
237
+ this._runHandler("onStateCreate", state);
238
+ return state;
239
+ }
240
+ _getSubstate(parent, prevStateKey) {
241
+ if (!parent) return;
242
+ const toState = parent.descriptor?.getTargetStateKey(
243
+ prevStateKey || STATE_INITIAL,
244
+ this.event || EVENT_EMPTY
245
+ ) || STATE_FINAL;
246
+ if (!toState) return;
247
+ return this._newSubstate(parent, toState);
248
+ }
249
+ _newSubstate(parent, toState) {
250
+ let descriptor;
251
+ for (let state = parent; !descriptor && state; state = state.parent) {
252
+ descriptor = state.descriptor?.states[toState];
253
+ }
254
+ return this._newState(parent, toState, descriptor);
255
+ }
256
+ _update() {
257
+ if (this.status & STATUS_FINISHED) return false;
258
+ 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);
259
+ if (nextState !== void 0) {
260
+ this.state = nextState;
261
+ this.status = this.status & STATUS_EXIT ? STATUS_NEXT : STATUS_FIRST;
262
+ } else {
263
+ if (this.status & STATUS_EXIT) {
264
+ this.state = this.state?.parent;
265
+ this.status = STATUS_LAST;
266
+ } else {
267
+ this.status = STATUS_LEAF;
268
+ }
269
+ if (!this.state) this.status = STATUS_FINISHED;
270
+ }
271
+ return !(this.status & STATUS_FINISHED);
272
+ }
273
+ };
274
+
275
+ // src/utils/printer.ts
276
+ var KEY_PRINTER = "printer";
277
+ function preparePrinter(process, { prefix = "", print = console.log, lineNumbers = false }) {
278
+ let lineCounter = 0;
279
+ const shift = () => {
280
+ let prefix2 = "";
281
+ for (let s = process.state?.parent; !!s; s = s.parent) {
282
+ prefix2 += " ";
283
+ }
284
+ return prefix2;
285
+ };
286
+ const getPrefix = lineNumbers ? () => `[${++lineCounter}]${shift()}` : shift;
287
+ const printer = (...args) => print(prefix, getPrefix(), ...args);
288
+ return printer;
289
+ }
290
+ function setPrinter(state, config = {}) {
291
+ const printer = preparePrinter(state.process, config);
292
+ state.setData(KEY_PRINTER, printer);
293
+ }
294
+ function setProcessPrinter(process, config = {}) {
295
+ const printer = preparePrinter(process, config);
296
+ process.setData(KEY_PRINTER, printer);
297
+ }
298
+ function getProcessPrinter(process) {
299
+ return process.getData(KEY_PRINTER) || console.log;
300
+ }
301
+ function getPrinter(state) {
302
+ return state.getData(KEY_PRINTER, true) || getProcessPrinter(state.process);
303
+ }
304
+
305
+ // src/utils/tracer.ts
306
+ function setProcessTracer(process, print) {
307
+ return process.onStateCreate((state) => {
308
+ setStateTracer(state, print);
309
+ });
310
+ }
311
+ function setStateTracer(state, print) {
312
+ state.onEnter(() => {
313
+ const printLine = print || getPrinter(state);
314
+ printLine(`<${state?.key} event="${state.process.event}">`);
315
+ });
316
+ state.onExit(() => {
317
+ const printLine = print || getPrinter(state);
318
+ printLine(`</${state.key}> <!-- event="${state.process.event}" -->`);
319
+ });
320
+ }
321
+
322
+ // src/utils/process.ts
323
+ function newProcess(config, {
324
+ prefix,
325
+ print,
326
+ lineNumbers = true
327
+ }) {
328
+ let process = new FsmProcess(config);
329
+ setProcessPrinter(process, {
330
+ prefix,
331
+ print,
332
+ lineNumbers
333
+ });
334
+ setProcessTracer(process);
335
+ return process;
336
+ }
337
+
338
+ // src/utils/transitions.ts
339
+ function isStateTransitionEnabled(process, event) {
340
+ const transitions = getStateTransitions(process.state);
341
+ let active = false;
342
+ for (const [from, ev, to] of transitions) {
343
+ if (ev === event) {
344
+ active = true;
345
+ break;
346
+ }
347
+ }
348
+ return active;
349
+ }
350
+ function getStateTransitions(state) {
351
+ const result = [];
352
+ let index = {};
353
+ if (state) {
354
+ let prevStateKey = state.key;
355
+ for (let parent = state?.parent; parent; parent = parent.parent) {
356
+ if (!parent.descriptor) continue;
357
+ result.push(
358
+ ...getTransitionsFromDescriptor(parent.descriptor, prevStateKey, index)
359
+ );
360
+ prevStateKey = parent.key;
361
+ }
362
+ }
363
+ return result.reverse();
364
+ }
365
+ function getTransitionsFromDescriptor(descriptor, prevStateKey, index = {}) {
366
+ const result = [];
367
+ const prevStateKeys = [prevStateKey, "*"];
368
+ for (const prevKey of prevStateKeys) {
369
+ const targets = descriptor.transitions[prevKey];
370
+ if (targets) {
371
+ for (const [event, target] of Object.entries(targets)) {
372
+ if (index[event]) continue;
373
+ if (target) {
374
+ index[event] = true;
375
+ }
376
+ result.push([prevStateKey, event, target]);
377
+ }
378
+ }
379
+ }
380
+ return result;
381
+ }
19
382
  export {
20
383
  EVENT_ANY,
21
384
  EVENT_EMPTY,
22
385
  FsmProcess,
23
386
  FsmState,
24
387
  FsmStateDescriptor,
388
+ KEY_PRINTER,
25
389
  STATE_ANY,
26
390
  STATE_FINAL,
27
391
  STATE_INITIAL,
@@ -32,5 +396,15 @@ export {
32
396
  STATUS_LAST,
33
397
  STATUS_LEAF,
34
398
  STATUS_NEXT,
35
- STATUS_NONE
399
+ STATUS_NONE,
400
+ getPrinter,
401
+ getProcessPrinter,
402
+ getStateTransitions,
403
+ isStateTransitionEnabled,
404
+ newProcess,
405
+ preparePrinter,
406
+ setPrinter,
407
+ setProcessPrinter,
408
+ setProcessTracer,
409
+ setStateTracer
36
410
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statewalker/fsm",
3
- "version": "0.23.0",
3
+ "version": "0.23.1",
4
4
  "description": "HFSM Implementation",
5
5
  "keywords": [],
6
6
  "homepage": "https://github.com/statewalker/statewalker-fsm",
@@ -24,12 +24,6 @@
24
24
  "types": "./dist/index.d.ts",
25
25
  "import": "./dist/index.js",
26
26
  "default": "./dist/index.js"
27
- },
28
- "./utils": {
29
- "source": "./src/utils/index.ts",
30
- "types": "./dist/utils/index.d.ts",
31
- "import": "./dist/utils/index.js",
32
- "default": "./dist/utils/index.js"
33
27
  }
34
28
  },
35
29
  "devDependencies": {
package/src/index.ts CHANGED
@@ -2,3 +2,4 @@ export * from "./FsmProcess.ts";
2
2
  export * from "./FsmState.ts";
3
3
  export * from "./FsmStateConfig.ts";
4
4
  export * from "./FsmStateDescriptor.ts";
5
+ export * from "./utils/index.ts";
@@ -1,6 +1,4 @@
1
- export * from "./handlers.ts";
2
1
  export * from "./printer.ts";
3
2
  export * from "./process.ts";
4
3
  export * from "./tracer.ts";
5
4
  export * from "./transitions.ts";
6
- export * from "./newFsmStateHandler.ts";
@@ -1,292 +0,0 @@
1
- // src/FsmBaseClass.ts
2
- var FsmBaseClass = class {
3
- constructor() {
4
- this.handlers = {};
5
- this.data = {};
6
- bindMethods(this, "setData", "getData");
7
- }
8
- setData(key, value) {
9
- this.data[key] = value;
10
- return this;
11
- }
12
- getData(key) {
13
- return this.data[key];
14
- }
15
- // ----------------------------------------------
16
- // internal methods
17
- _addHandler(type, handler, direct = true) {
18
- const list = this.handlers[type] = this.handlers[type] || [];
19
- direct ? list.push(handler) : list.unshift(handler);
20
- return () => this._removeHandler(type, handler);
21
- }
22
- _removeHandler(type, handler) {
23
- let list = this.handlers[type];
24
- if (!list) return;
25
- list = list.filter((h) => h !== handler);
26
- if (list.length > 0) {
27
- this.handlers[type] = list;
28
- } else {
29
- delete this.handlers[type];
30
- }
31
- }
32
- async _runHandler(type, ...args) {
33
- const list = this.handlers[type] || [];
34
- const promises = list.map((handler) => handler(...args));
35
- for (const promise of promises) {
36
- try {
37
- await promise;
38
- } catch (error) {
39
- await this._handleError(error);
40
- }
41
- }
42
- }
43
- async _handleError(error) {
44
- console.error(error);
45
- }
46
- };
47
- function bindMethods(obj, ...methods) {
48
- const o = obj;
49
- for (const methodName of methods) {
50
- const method = o[methodName];
51
- if (typeof method !== "function") continue;
52
- o[methodName] = method.bind(o);
53
- }
54
- return obj;
55
- }
56
-
57
- // src/FsmState.ts
58
- var FsmState = class extends FsmBaseClass {
59
- constructor(process, parent, key, descriptor) {
60
- super();
61
- this.process = process;
62
- this.key = key;
63
- this.parent = parent;
64
- this.descriptor = descriptor;
65
- bindMethods(
66
- this,
67
- "onEnter",
68
- "onExit",
69
- "dump",
70
- "restore",
71
- "useData",
72
- "onStateError"
73
- );
74
- }
75
- onEnter(handler) {
76
- return this._addHandler("onEnter", handler, true);
77
- }
78
- onExit(handler) {
79
- return this._addHandler("onExit", handler, false);
80
- }
81
- onStateError(handler) {
82
- return this._addHandler("onStateError", handler);
83
- }
84
- dump(handler) {
85
- return this._addHandler("dump", handler, true);
86
- }
87
- restore(handler) {
88
- return this._addHandler("restore", handler, true);
89
- }
90
- getData(key, recursive = true) {
91
- return this.data[key] ?? (recursive ? this.parent?.getData(key, recursive) : void 0);
92
- }
93
- useData(key) {
94
- return [
95
- (recursive = true) => this.getData(key, recursive),
96
- (value) => this.setData(key, value)
97
- ];
98
- }
99
- async _handleError(error) {
100
- await this._runHandler("onStateError", this, error);
101
- await this.process._handleStateError(this, error);
102
- }
103
- };
104
-
105
- // src/FsmStateConfig.ts
106
- var STATE_ANY = "*";
107
- var STATE_INITIAL = "";
108
- var STATE_FINAL = "";
109
- var EVENT_ANY = "*";
110
- var EVENT_EMPTY = "";
111
-
112
- // src/FsmStateDescriptor.ts
113
- var FsmStateDescriptor = class _FsmStateDescriptor {
114
- constructor() {
115
- this.transitions = {};
116
- this.states = {};
117
- }
118
- static build(config) {
119
- const descriptor = new _FsmStateDescriptor();
120
- for (const [from, event, to] of config.transitions || []) {
121
- const index = descriptor.transitions[from] = descriptor.transitions[from] || {};
122
- index[event] = to;
123
- }
124
- if (config.states) {
125
- for (const substateConfig of config.states) {
126
- descriptor.states[substateConfig.key] = this.build(substateConfig);
127
- }
128
- }
129
- return descriptor;
130
- }
131
- getTargetStateKey(stateKey, eventKey) {
132
- const pairs = [
133
- [stateKey, eventKey],
134
- [STATE_ANY, eventKey],
135
- [stateKey, EVENT_ANY],
136
- [STATE_ANY, EVENT_ANY]
137
- ];
138
- let targetKey;
139
- for (let i = 0, len = pairs.length; targetKey === void 0 && i < len; i++) {
140
- const [stateKey2, eventKey2] = pairs[i];
141
- const stateTransitions = this.transitions[stateKey2];
142
- if (!stateTransitions) continue;
143
- targetKey = stateTransitions[eventKey2];
144
- }
145
- return targetKey !== void 0 ? targetKey : STATE_FINAL;
146
- }
147
- };
148
-
149
- // src/FsmProcess.ts
150
- var STATUS_NONE = 0;
151
- var STATUS_FIRST = 1;
152
- var STATUS_NEXT = 2;
153
- var STATUS_LEAF = 4;
154
- var STATUS_LAST = 8;
155
- var STATUS_FINISHED = 16;
156
- var STATUS_ENTER = STATUS_FIRST | STATUS_NEXT;
157
- var STATUS_EXIT = STATUS_LEAF | STATUS_LAST;
158
- var FsmProcess = class extends FsmBaseClass {
159
- constructor(config) {
160
- super();
161
- this.status = 0;
162
- this.rootDescriptor = FsmStateDescriptor.build(config);
163
- this.config = config;
164
- bindMethods(this, "dispatch", "dump", "restore");
165
- }
166
- async shutdown(event) {
167
- while (this.state) {
168
- this.event = event;
169
- this.status = STATUS_FINISHED;
170
- await this.state?._runHandler("onExit", this.state);
171
- this.state = this.state.parent;
172
- }
173
- }
174
- async dispatch(event, mask = STATUS_LEAF) {
175
- this.event = event;
176
- while (true) {
177
- if (this.status & STATUS_EXIT) {
178
- await this.state?._runHandler("onExit", this.state);
179
- }
180
- if (!this._update()) return false;
181
- if (this.status & STATUS_ENTER) {
182
- await this.state?._runHandler("onEnter", this.state);
183
- }
184
- if (this.status & mask) return true;
185
- }
186
- }
187
- async dump(...args) {
188
- const dumpState = async (state) => {
189
- const stateDump = {
190
- key: state.key,
191
- data: {}
192
- };
193
- await state._runHandler("dump", state, stateDump.data, ...args);
194
- return stateDump;
195
- };
196
- const dumpStates = async (state, stack = []) => {
197
- if (!state) return stack;
198
- state.parent && await dumpStates(state.parent, stack);
199
- stack.push(await dumpState(state));
200
- return stack;
201
- };
202
- const dump = {
203
- status: this.status,
204
- event: this.event,
205
- stack: await dumpStates(this.state)
206
- };
207
- return dump;
208
- }
209
- async restore(dump, ...args) {
210
- this.status = dump.status || 0;
211
- this.event = dump.event;
212
- this.state = void 0;
213
- for (let i = 0; i < dump.stack.length; i++) {
214
- const stateDump = dump.stack[i];
215
- this.state = this.state ? this._newSubstate(this.state, stateDump.key) : this._newState(void 0, stateDump.key, this.rootDescriptor);
216
- await this.state._runHandler(
217
- "restore",
218
- this.state,
219
- stateDump.data,
220
- ...args
221
- );
222
- }
223
- return this;
224
- }
225
- onStateCreate(handler) {
226
- return this._addHandler("onStateCreate", handler, true);
227
- }
228
- onStateError(handler) {
229
- return this._addHandler("onStateError", handler);
230
- }
231
- async _handleStateError(state, error) {
232
- await this._runHandler("onStateError", state, error);
233
- this._handleError(error);
234
- }
235
- _newState(parent, key, descriptor) {
236
- const state = new FsmState(this, parent, key, descriptor);
237
- this._runHandler("onStateCreate", state);
238
- return state;
239
- }
240
- _getSubstate(parent, prevStateKey) {
241
- if (!parent) return;
242
- const toState = parent.descriptor?.getTargetStateKey(
243
- prevStateKey || STATE_INITIAL,
244
- this.event || EVENT_EMPTY
245
- ) || STATE_FINAL;
246
- if (!toState) return;
247
- return this._newSubstate(parent, toState);
248
- }
249
- _newSubstate(parent, toState) {
250
- let descriptor;
251
- for (let state = parent; !descriptor && state; state = state.parent) {
252
- descriptor = state.descriptor?.states[toState];
253
- }
254
- return this._newState(parent, toState, descriptor);
255
- }
256
- _update() {
257
- if (this.status & STATUS_FINISHED) return false;
258
- 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);
259
- if (nextState !== void 0) {
260
- this.state = nextState;
261
- this.status = this.status & STATUS_EXIT ? STATUS_NEXT : STATUS_FIRST;
262
- } else {
263
- if (this.status & STATUS_EXIT) {
264
- this.state = this.state?.parent;
265
- this.status = STATUS_LAST;
266
- } else {
267
- this.status = STATUS_LEAF;
268
- }
269
- if (!this.state) this.status = STATUS_FINISHED;
270
- }
271
- return !(this.status & STATUS_FINISHED);
272
- }
273
- };
274
-
275
- export {
276
- FsmState,
277
- STATE_ANY,
278
- STATE_INITIAL,
279
- STATE_FINAL,
280
- EVENT_ANY,
281
- EVENT_EMPTY,
282
- FsmStateDescriptor,
283
- STATUS_NONE,
284
- STATUS_FIRST,
285
- STATUS_NEXT,
286
- STATUS_LEAF,
287
- STATUS_LAST,
288
- STATUS_FINISHED,
289
- STATUS_ENTER,
290
- STATUS_EXIT,
291
- FsmProcess
292
- };
@@ -1,42 +0,0 @@
1
- import { FsmState, FsmStateHandler as FsmStateHandler$1, FsmProcess, FsmStateConfig } 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$1>): void;
6
-
7
- type Printer = (...args: unknown[]) => void;
8
- type PrinterConfig = {
9
- prefix?: string;
10
- print?: (...args: unknown[]) => 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 newProcess(config: FsmStateConfig, { prefix, print, lineNumbers, }: {
21
- prefix?: string;
22
- print?: (...args: any[]) => void;
23
- lineNumbers?: boolean;
24
- }): FsmProcess;
25
-
26
- declare function setProcessTracer(process: FsmProcess, print?: Printer): () => void;
27
- declare function setStateTracer(state: FsmState, print?: Printer): void;
28
-
29
- declare function isStateTransitionEnabled(process: FsmProcess, event: string): boolean;
30
- declare function getStateTransitions(state?: FsmState): [from: string, event: string, to: string][];
31
-
32
- declare function newModuleLoader<T>(baseUrl: string): (path: string) => Promise<T>;
33
- declare function newHandlerLoader(baseUrl: string, loader?: (path: string) => Promise<undefined | FsmStateHandler>): Promise<(state: FsmState) => Promise<FsmStateHandler[]>>;
34
- type FsmStateHandler<FsmStateContext extends Record<string, unknown> = Record<string, unknown>, FsmStateStore = Record<string, unknown>> = {
35
- context?: (...stack: FsmStateStore[]) => FsmStateContext;
36
- trigger?: (context: FsmStateContext, listener: (event: string) => void) => void | (() => void);
37
- default?: (context: FsmStateContext) => void | (() => void);
38
- handler?: (context: FsmStateContext) => void | (() => void);
39
- };
40
- declare function newStateHandlers<FsmStateContext extends Record<string, unknown> = Record<string, unknown>, FsmStateStore = Record<string, unknown>>(loader: (state: FsmState) => Promise<FsmStateHandler[]>, newContext?: (state: FsmState, ...stack: FsmStateStore[]) => FsmStateStore): (state: FsmState) => void;
41
-
42
- export { type FsmStateHandler, KEY_HANDLERS, KEY_PRINTER, type Printer, type PrinterConfig, addSubstateHandlers, callStateHandlers, getPrinter, getProcessPrinter, getStateTransitions, isStateTransitionEnabled, newHandlerLoader, newModuleLoader, newProcess, newStateHandlers, preparePrinter, setPrinter, setProcessPrinter, setProcessTracer, setStateTracer };
@@ -1,238 +0,0 @@
1
- import {
2
- FsmProcess
3
- } from "../chunk-CRID7TZR.js";
4
-
5
- // src/utils/handlers.ts
6
- var KEY_HANDLERS = "handlers";
7
- function callStateHandlers(state) {
8
- const key = state.key;
9
- for (let parent = state.parent; !!parent; parent = parent?.parent) {
10
- const handlers = parent.getData(KEY_HANDLERS)?.[key];
11
- handlers?.forEach((handler) => handler(state));
12
- }
13
- }
14
- function addSubstateHandlers(state, handlers) {
15
- const oldIndex = state.getData(KEY_HANDLERS);
16
- const index = oldIndex ? { ...oldIndex } : {};
17
- for (const [key, handler] of Object.entries(handlers)) {
18
- const list = index[key] = index[key] || [];
19
- list.push(handler);
20
- }
21
- state.setData(KEY_HANDLERS, index);
22
- }
23
-
24
- // src/utils/printer.ts
25
- var KEY_PRINTER = "printer";
26
- function preparePrinter(process, { prefix = "", print = console.log, lineNumbers = false }) {
27
- let lineCounter = 0;
28
- const shift = () => {
29
- let prefix2 = "";
30
- for (let s = process.state?.parent; !!s; s = s.parent) {
31
- prefix2 += " ";
32
- }
33
- return prefix2;
34
- };
35
- const getPrefix = lineNumbers ? () => `[${++lineCounter}]${shift()}` : shift;
36
- const printer = (...args) => print(prefix, getPrefix(), ...args);
37
- return printer;
38
- }
39
- function setPrinter(state, config = {}) {
40
- const printer = preparePrinter(state.process, config);
41
- state.setData(KEY_PRINTER, printer);
42
- }
43
- function setProcessPrinter(process, config = {}) {
44
- const printer = preparePrinter(process, config);
45
- process.setData(KEY_PRINTER, printer);
46
- }
47
- function getProcessPrinter(process) {
48
- return process.getData(KEY_PRINTER) || console.log;
49
- }
50
- function getPrinter(state) {
51
- return state.getData(KEY_PRINTER, true) || getProcessPrinter(state.process);
52
- }
53
-
54
- // src/utils/tracer.ts
55
- function setProcessTracer(process, print) {
56
- return process.onStateCreate((state) => {
57
- setStateTracer(state, print);
58
- });
59
- }
60
- function setStateTracer(state, print) {
61
- state.onEnter(() => {
62
- const printLine = print || getPrinter(state);
63
- printLine(`<${state?.key} event="${state.process.event}">`);
64
- });
65
- state.onExit(() => {
66
- const printLine = print || getPrinter(state);
67
- printLine(`</${state.key}> <!-- event="${state.process.event}" -->`);
68
- });
69
- }
70
-
71
- // src/utils/process.ts
72
- function newProcess(config, {
73
- prefix,
74
- print,
75
- lineNumbers = true
76
- }) {
77
- let process = new FsmProcess(config);
78
- setProcessPrinter(process, {
79
- prefix,
80
- print,
81
- lineNumbers
82
- });
83
- setProcessTracer(process);
84
- return process;
85
- }
86
-
87
- // src/utils/transitions.ts
88
- function isStateTransitionEnabled(process, event) {
89
- const transitions = getStateTransitions(process.state);
90
- let active = false;
91
- for (const [from, ev, to] of transitions) {
92
- if (ev === event) {
93
- active = true;
94
- break;
95
- }
96
- }
97
- return active;
98
- }
99
- function getStateTransitions(state) {
100
- const result = [];
101
- let index = {};
102
- if (state) {
103
- let prevStateKey = state.key;
104
- for (let parent = state?.parent; parent; parent = parent.parent) {
105
- if (!parent.descriptor) continue;
106
- result.push(
107
- ...getTransitionsFromDescriptor(parent.descriptor, prevStateKey, index)
108
- );
109
- prevStateKey = parent.key;
110
- }
111
- }
112
- return result.reverse();
113
- }
114
- function getTransitionsFromDescriptor(descriptor, prevStateKey, index = {}) {
115
- const result = [];
116
- const prevStateKeys = [prevStateKey, "*"];
117
- for (const prevKey of prevStateKeys) {
118
- const targets = descriptor.transitions[prevKey];
119
- if (targets) {
120
- for (const [event, target] of Object.entries(targets)) {
121
- if (index[event]) continue;
122
- if (target) {
123
- index[event] = true;
124
- }
125
- result.push([prevStateKey, event, target]);
126
- }
127
- }
128
- }
129
- return result;
130
- }
131
-
132
- // src/utils/newFsmStateHandler.ts
133
- function newModuleLoader(baseUrl) {
134
- const cache = {};
135
- return async (path) => {
136
- return cache[path] = cache[path] || (async () => {
137
- const suffixes = ["/", "", ".js", "/index.js"];
138
- for (const suffix of suffixes) {
139
- const url = new URL(path + suffix, baseUrl).pathname;
140
- try {
141
- return await import(url);
142
- } catch (error) {
143
- continue;
144
- }
145
- }
146
- })();
147
- };
148
- }
149
- async function newHandlerLoader(baseUrl, loader = newModuleLoader(baseUrl)) {
150
- return async (state) => {
151
- const stack = [];
152
- for (let s = state; s; s = s.parent) {
153
- stack.unshift(s.key);
154
- }
155
- const handlers = [];
156
- let topName;
157
- while (stack.length > 0) {
158
- const lastSegment = stack.pop();
159
- if (!topName) {
160
- topName = lastSegment;
161
- if (topName === void 0) break;
162
- }
163
- const path = [...stack, topName].join("/");
164
- const handler = await loader(path);
165
- handler && handlers.push(handler);
166
- }
167
- return handlers;
168
- };
169
- }
170
- function newStateHandlers(loader, newContext = (state) => ({
171
- state: state.key,
172
- event: state.process.event
173
- })) {
174
- return (state) => {
175
- const stack = [];
176
- for (let s = state.parent; !!s; s = s.parent) {
177
- const store2 = s.getData("context");
178
- store2 && stack.push(store2);
179
- }
180
- const store = newContext(state, ...stack);
181
- stack.unshift(store);
182
- state.setData("context", store);
183
- let registrations = [];
184
- state.onExit(() => {
185
- for (const registration of registrations) {
186
- registration?.();
187
- }
188
- });
189
- state.onEnter(async () => {
190
- const modules = await loader(state);
191
- if (!modules?.length) return;
192
- for (const module of modules) {
193
- const ctx = isClass(module.context) ? new module.context(...stack) : isFunction(module.context) ? module.context(...stack) || store : store;
194
- const stateContext = ctx;
195
- let notifyTrigger = (event) => {
196
- if (event) {
197
- state.process.dispatch(event);
198
- }
199
- };
200
- const removeTrigger = module?.trigger?.(
201
- stateContext,
202
- (event) => notifyTrigger(event)
203
- );
204
- registrations.push(() => notifyTrigger = () => {
205
- });
206
- removeTrigger && registrations.push(removeTrigger);
207
- const handler = module.default || module.handler;
208
- registrations.push(handler?.(stateContext));
209
- }
210
- function isFunction(value) {
211
- return typeof value === "function";
212
- }
213
- function isClass(value) {
214
- if (!isFunction(value)) return false;
215
- return value.prototype.constructor.toString().match(/^class/);
216
- }
217
- });
218
- };
219
- }
220
- export {
221
- KEY_HANDLERS,
222
- KEY_PRINTER,
223
- addSubstateHandlers,
224
- callStateHandlers,
225
- getPrinter,
226
- getProcessPrinter,
227
- getStateTransitions,
228
- isStateTransitionEnabled,
229
- newHandlerLoader,
230
- newModuleLoader,
231
- newProcess,
232
- newStateHandlers,
233
- preparePrinter,
234
- setPrinter,
235
- setProcessPrinter,
236
- setProcessTracer,
237
- setStateTracer
238
- };