@vsirotin/ts-stop 1.13.1 → 2.4.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.
Files changed (122) hide show
  1. package/ai/skills/sfsm-compare-json-uml-diagram/SKILL.md +62 -0
  2. package/ai/skills/sfsm-json-to-uml-diagram/SKILL.md +50 -0
  3. package/ai/skills/sfsm-uml-diagram-to-json/SKILL.md +49 -0
  4. package/lib/esm/sfsm/CommandReceiver.js +17 -0
  5. package/lib/esm/sfsm/ControllerHub.js +82 -0
  6. package/lib/esm/sfsm/FaResolver.js +24 -3
  7. package/lib/esm/sfsm/Sfsm.js +48 -7
  8. package/lib/esm/sfsm/SignalSender.js +27 -0
  9. package/lib/esm/sfsm/index.js +3 -1
  10. package/lib/sfsm/CommandReceiver.d.ts +23 -0
  11. package/lib/sfsm/CommandReceiver.d.ts.map +1 -0
  12. package/lib/sfsm/CommandReceiver.js +22 -0
  13. package/lib/sfsm/CommandReceiver.js.map +1 -0
  14. package/lib/sfsm/ControllerHub.d.ts +56 -0
  15. package/lib/sfsm/ControllerHub.d.ts.map +1 -0
  16. package/lib/sfsm/ControllerHub.js +87 -0
  17. package/lib/sfsm/ControllerHub.js.map +1 -0
  18. package/lib/sfsm/FaResolver.d.ts +14 -0
  19. package/lib/sfsm/FaResolver.d.ts.map +1 -1
  20. package/lib/sfsm/FaResolver.js +24 -3
  21. package/lib/sfsm/FaResolver.js.map +1 -1
  22. package/lib/sfsm/Sfsm.d.ts +26 -0
  23. package/lib/sfsm/Sfsm.d.ts.map +1 -1
  24. package/lib/sfsm/Sfsm.js +48 -7
  25. package/lib/sfsm/Sfsm.js.map +1 -1
  26. package/lib/sfsm/SignalSender.d.ts +24 -0
  27. package/lib/sfsm/SignalSender.d.ts.map +1 -0
  28. package/lib/sfsm/SignalSender.js +32 -0
  29. package/lib/sfsm/SignalSender.js.map +1 -0
  30. package/lib/sfsm/index.d.ts +3 -1
  31. package/lib/sfsm/index.d.ts.map +1 -1
  32. package/lib/sfsm/index.js +7 -3
  33. package/lib/sfsm/index.js.map +1 -1
  34. package/lib/sfsm/interfaces.d.ts +1 -1
  35. package/lib/sfsm/types.d.ts +21 -0
  36. package/lib/sfsm/types.d.ts.map +1 -1
  37. package/package.json +9 -8
  38. package/scripts/compare-compact-jsons.js +146 -0
  39. package/scripts/drawio-to-json.js +149 -0
  40. package/scripts/json-to-drawio-old.js +311 -0
  41. package/scripts/json-to-drawio.js +309 -0
  42. package/tutorial/01-finite-state-machine.md +223 -0
  43. package/tutorial/02-stacked-finite-state-machine.md +32 -0
  44. package/tutorial/03-advanced-themes.md +110 -0
  45. package/tutorial/04-tools.md +164 -0
  46. package/tutorial/images/Turnstile-img.png +0 -0
  47. package/tutorial/images/TurnstileBaseFA.png +0 -0
  48. package/LICENSE +0 -201
  49. package/LICENSE-COMMERCIAL.md +0 -41
  50. package/README.md +0 -19
  51. package/lib/DefaultState.d.ts +0 -9
  52. package/lib/DefaultState.d.ts.map +0 -1
  53. package/lib/DefaultState.js +0 -15
  54. package/lib/DefaultState.js.map +0 -1
  55. package/lib/FiniteStateMachine.d.ts +0 -227
  56. package/lib/FiniteStateMachine.d.ts.map +0 -1
  57. package/lib/FiniteStateMachine.js +0 -410
  58. package/lib/FiniteStateMachine.js.map +0 -1
  59. package/lib/IStateWithActions.d.ts +0 -18
  60. package/lib/IStateWithActions.d.ts.map +0 -1
  61. package/lib/IStateWithActions.js +0 -3
  62. package/lib/IStateWithActions.js.map +0 -1
  63. package/lib/IStateWithOutputSignal.d.ts +0 -4
  64. package/lib/IStateWithOutputSignal.d.ts.map +0 -1
  65. package/lib/IStateWithOutputSignal.js +0 -3
  66. package/lib/IStateWithOutputSignal.js.map +0 -1
  67. package/lib/MatrixBasedStateMachine.d.ts +0 -108
  68. package/lib/MatrixBasedStateMachine.d.ts.map +0 -1
  69. package/lib/MatrixBasedStateMachine.js +0 -132
  70. package/lib/MatrixBasedStateMachine.js.map +0 -1
  71. package/lib/TransitionMatrix.d.ts +0 -61
  72. package/lib/TransitionMatrix.d.ts.map +0 -1
  73. package/lib/TransitionMatrix.js +0 -104
  74. package/lib/TransitionMatrix.js.map +0 -1
  75. package/lib/esm/DefaultState.js +0 -10
  76. package/lib/esm/FiniteStateMachine.js +0 -405
  77. package/lib/esm/IStateWithActions.js +0 -1
  78. package/lib/esm/IStateWithOutputSignal.js +0 -1
  79. package/lib/esm/MatrixBasedStateMachine.js +0 -127
  80. package/lib/esm/TransitionMatrix.js +0 -98
  81. package/lib/esm/fa/DefaultState.js +0 -10
  82. package/lib/esm/fa/FiniteStateMachine.js +0 -405
  83. package/lib/esm/fa/IStateWithActions.js +0 -1
  84. package/lib/esm/fa/IStateWithOutputSignal.js +0 -1
  85. package/lib/esm/fa/MatrixBasedStateMachine.js +0 -127
  86. package/lib/esm/fa/TransitionMatrix.js +0 -98
  87. package/lib/esm/index.js +0 -9
  88. package/lib/esm/sfsm/ExternalWorldHub.js +0 -80
  89. package/lib/esm/sfsm/index.browser.js +0 -5
  90. package/lib/fa/DefaultState.d.ts +0 -9
  91. package/lib/fa/DefaultState.d.ts.map +0 -1
  92. package/lib/fa/DefaultState.js +0 -15
  93. package/lib/fa/DefaultState.js.map +0 -1
  94. package/lib/fa/FiniteStateMachine.d.ts +0 -227
  95. package/lib/fa/FiniteStateMachine.d.ts.map +0 -1
  96. package/lib/fa/FiniteStateMachine.js +0 -410
  97. package/lib/fa/FiniteStateMachine.js.map +0 -1
  98. package/lib/fa/IStateWithActions.d.ts +0 -18
  99. package/lib/fa/IStateWithActions.d.ts.map +0 -1
  100. package/lib/fa/IStateWithActions.js +0 -3
  101. package/lib/fa/IStateWithActions.js.map +0 -1
  102. package/lib/fa/IStateWithOutputSignal.d.ts +0 -4
  103. package/lib/fa/IStateWithOutputSignal.d.ts.map +0 -1
  104. package/lib/fa/IStateWithOutputSignal.js +0 -3
  105. package/lib/fa/IStateWithOutputSignal.js.map +0 -1
  106. package/lib/fa/MatrixBasedStateMachine.d.ts +0 -108
  107. package/lib/fa/MatrixBasedStateMachine.d.ts.map +0 -1
  108. package/lib/fa/MatrixBasedStateMachine.js +0 -132
  109. package/lib/fa/MatrixBasedStateMachine.js.map +0 -1
  110. package/lib/fa/TransitionMatrix.d.ts +0 -61
  111. package/lib/fa/TransitionMatrix.d.ts.map +0 -1
  112. package/lib/fa/TransitionMatrix.js +0 -104
  113. package/lib/fa/TransitionMatrix.js.map +0 -1
  114. package/lib/index.d.ts +0 -10
  115. package/lib/index.d.ts.map +0 -1
  116. package/lib/index.js +0 -26
  117. package/lib/index.js.map +0 -1
  118. package/lib/sfsm/ExternalWorldHub.d.ts +0 -52
  119. package/lib/sfsm/ExternalWorldHub.d.ts.map +0 -1
  120. package/lib/sfsm/ExternalWorldHub.js +0 -85
  121. package/lib/sfsm/ExternalWorldHub.js.map +0 -1
  122. package/lib/sfsm/index.browser.d.ts +0 -7
@@ -1,405 +0,0 @@
1
- import { DefaultState } from './DefaultState';
2
- /**
3
- * Concrete implementation of a finite state machine with optional state actions.
4
- *
5
- * This class supports states that can optionally implement action interfaces:
6
- * - IStateWithAfterEntryAction: Execute action when entering the state
7
- * - IStateWithBeforeExitAction: Execute action when exiting the state
8
- * - Both interfaces: Execute both entry and exit actions
9
- * - Neither interface: Traditional state machine behavior (no actions)
10
- *
11
- * @template STATE - The type representing possible states
12
- * @template SIGNAL - The type representing signals/events
13
- *
14
- * @example
15
- * ```typescript
16
- * // Basic string-based turnstile (traditional approach)
17
- * class Turnstile extends FiniteStateMachine<string, string> {
18
- * constructor() {
19
- * super(
20
- * ['locked', 'unlocked'],
21
- * ['coin', 'push'],
22
- * [
23
- * { from: 'locked', signal: 'coin', to: 'unlocked' },
24
- * { from: 'unlocked', signal: 'push', to: 'locked' }
25
- * ],
26
- * 'locked'
27
- * );
28
- * }
29
- *
30
- * ...
31
- * }
32
- *
33
-
34
- *
35
- * class TurnstileWithActions extends FiniteStateMachine<ActionState, string> {
36
- * private locked = new ActionState('locked', 'Payment required');
37
- * private unlocked = new ActionState('unlocked', 'Please proceed');
38
- *
39
- * constructor() {
40
- * super(
41
- * [this.locked, this.unlocked],
42
- * ['coin', 'push'],
43
- * [
44
- * { from: this.locked, signal: 'coin', to: this.unlocked },
45
- * { from: this.unlocked, signal: 'push', to: this.locked }
46
- * ],
47
- * this.locked
48
- * );
49
- * }
50
- *
51
- * insertCoin(): ActionState { return this.sendSignal('coin'); }
52
- * pushThrough(): ActionState { return this.sendSignal('push'); }
53
- * }
54
- *
55
- * ```
56
- */
57
- export class FiniteStateMachine {
58
- /**
59
- * Creates a new finite state machine.
60
- *
61
- * @param states - Array of all possible states
62
- * @param signals - Array of all possible signals/events
63
- * @param transitions - Array of transition rules defining how signals move between states
64
- * @param startState - The initial state of the machine
65
- * @param skipValidation - When true, all structural validations are skipped (default: false)
66
- */
67
- constructor(states, signals, transitions, startState, skipValidation = false) {
68
- this.states = states;
69
- this.signals = signals;
70
- this.transitions = transitions;
71
- this.startState = startState;
72
- /**
73
- * The default state that handles invalid signals.
74
- * Set to null if no state implements IDefaultState.
75
- */
76
- this.defaultState = null;
77
- // Find states that implement IDefaultState
78
- const defaultStates = states.filter(state => this.isDefaultState(state));
79
- if (!skipValidation) {
80
- // Validate default state count
81
- if (defaultStates.length > 1) {
82
- throw new Error(`ERROR-STOP-01: Multiple states implement IDefaultState interface. ` +
83
- `Only one state can handle invalid signals. ` +
84
- `Found ${defaultStates.length} states with IDefaultState.`);
85
- }
86
- // Validate that all transition and start-state references are defined
87
- this.validateDanglingReferences();
88
- // Validate that all output signal states have at least one outgoing transition
89
- this.validateOutputSignalStates();
90
- // Validate that output-signal states do not form cycles
91
- this.validateOutputSignalCycles();
92
- // Validate that all states are reachable from the start state
93
- this.validateUnreachableStates();
94
- }
95
- // Set default state if exactly one found
96
- if (defaultStates.length === 1) {
97
- this.defaultState = defaultStates[0];
98
- }
99
- // Initialize the machine to its starting state
100
- this.currentState = startState;
101
- // Check if a current state has output signal and process it
102
- this.tryProcessStateWithOutputSignal();
103
- // Execute initial state entry action if supported
104
- this.executeEntryAction(this.currentState);
105
- }
106
- /**
107
- * Gets the current state of the state machine.
108
- *
109
- * @returns The current state
110
- */
111
- getCurrentState() {
112
- return this.currentState;
113
- }
114
- /**
115
- * Processes a signal and potentially transitions to a new state with action execution.
116
- *
117
- * This method:
118
- * 1. Finds a matching transition for the current state and signal
119
- * 2. If found and the target state is different:
120
- * a. Executes beforeExitAction on current state (if implemented)
121
- * b. Changes to the new state
122
- * c. Executes afterEntryAction on new state (if implemented)
123
- * 3. Returns the resulting state
124
- *
125
- * @param signal - The signal/event to process
126
- * @returns The resulting state after processing the signal
127
- */
128
- sendSignal(signal) {
129
- // Find a transition that matches current state and signal
130
- const transition = this.transitions.find(t => t.from === this.currentState && t.signal === signal);
131
- // If valid transition found, handle state change with actions
132
- if (transition) {
133
- this.executeStateTransition(transition);
134
- }
135
- else if (this.defaultState) {
136
- // No valid transition found, but default state exists
137
- // Execute default state's actions (entry/exit if implemented)
138
- this.executeStateActions(this.defaultState);
139
- // Current state remains unchanged
140
- }
141
- this.tryProcessStateWithOutputSignal();
142
- return this.currentState;
143
- }
144
- tryProcessStateWithOutputSignal() {
145
- if (this.isStateWithOutputSignal(this.currentState)) {
146
- // Processing of behaviour for state with inside calculated output signal
147
- this.executeStateActions(this.currentState);
148
- const stateWithOutputSignal = this.currentState;
149
- const outputSignal = stateWithOutputSignal.getOutputSignal();
150
- // Add null/undefined check to prevent errors
151
- if (outputSignal !== null && outputSignal !== undefined) {
152
- this.sendSignal(outputSignal);
153
- }
154
- }
155
- }
156
- validateUnreachableStates() {
157
- const reachable = new Set();
158
- const queue = [this.startState];
159
- reachable.add(this.startState);
160
- while (queue.length > 0) {
161
- const current = queue.shift();
162
- for (const t of this.transitions) {
163
- if (t.from === current && !reachable.has(t.to)) {
164
- reachable.add(t.to);
165
- queue.push(t.to);
166
- }
167
- }
168
- }
169
- for (const state of this.states) {
170
- if (this.isDefaultState(state))
171
- continue; // DefaultState handles invalid signals — unreachable by design
172
- if (!reachable.has(state)) {
173
- throw new Error(`ERROR-STOP-08: State '${state}' is unreachable from start state '${this.startState}'. ` +
174
- `All non-default states must be reachable from the start state via the transition graph.`);
175
- }
176
- }
177
- }
178
- validateDanglingReferences() {
179
- const stateSet = new Set(this.states);
180
- const signalSet = new Set(this.signals);
181
- if (!stateSet.has(this.startState)) {
182
- throw new Error(`ERROR-STOP-03: Start state '${this.startState}' is not in the states array. ` +
183
- `The start state must be one of the provided states.`);
184
- }
185
- for (const t of this.transitions) {
186
- if (!stateSet.has(t.from)) {
187
- throw new Error(`ERROR-STOP-04: Transition references unknown state '${t.from}' as 'from'. ` +
188
- `All transition states must be in the states array.`);
189
- }
190
- if (!stateSet.has(t.to)) {
191
- throw new Error(`ERROR-STOP-05: Transition references unknown state '${t.to}' as 'to'. ` +
192
- `All transition states must be in the states array.`);
193
- }
194
- if (!signalSet.has(t.signal)) {
195
- throw new Error(`ERROR-STOP-06: Transition references unknown signal '${String(t.signal)}'. ` +
196
- `All transition signals must be in the signals array.`);
197
- }
198
- }
199
- }
200
- validateOutputSignalStates() {
201
- for (const state of this.states) {
202
- if (this.isStateWithOutputSignal(state)) {
203
- const hasOutgoing = this.transitions.some(t => t.from === state);
204
- if (!hasOutgoing) {
205
- throw new Error(`ERROR-STOP-02: Configuration error: State '${state}' implements IStateWithOutputSignal ` +
206
- `but has no outgoing transitions. States with output signals must have ` +
207
- `at least one outgoing transition.`);
208
- }
209
- }
210
- }
211
- }
212
- validateOutputSignalCycles() {
213
- const outputStateSet = new Set(this.states.filter(s => this.isStateWithOutputSignal(s)));
214
- if (outputStateSet.size < 2)
215
- return;
216
- // Build adjacency: output state → output states reachable via one transition
217
- const adj = new Map();
218
- for (const state of outputStateSet) {
219
- adj.set(state, this.transitions
220
- .filter(t => t.from === state && outputStateSet.has(t.to))
221
- .map(t => t.to));
222
- }
223
- // DFS-based cycle detection (colour: 0=unvisited, 1=in-stack, 2=done)
224
- const visited = new Set();
225
- const onStack = new Set();
226
- const stackPath = [];
227
- const dfs = (state) => {
228
- var _a;
229
- visited.add(state);
230
- onStack.add(state);
231
- stackPath.push(state);
232
- for (const neighbor of ((_a = adj.get(state)) !== null && _a !== void 0 ? _a : [])) {
233
- if (onStack.has(neighbor)) {
234
- const cycleStart = stackPath.indexOf(neighbor);
235
- const cyclePath = [...stackPath.slice(cycleStart), neighbor];
236
- throw new Error(`ERROR-STOP-07: Circular dependency detected in output signals: ` +
237
- `${cyclePath.map(s => String(s)).join(' -> ')}. ` +
238
- `States with output signals cannot form cycles as this creates infinite loops.`);
239
- }
240
- if (!visited.has(neighbor)) {
241
- dfs(neighbor);
242
- }
243
- }
244
- onStack.delete(state);
245
- stackPath.pop();
246
- };
247
- for (const state of outputStateSet) {
248
- if (!visited.has(state)) {
249
- dfs(state);
250
- }
251
- }
252
- }
253
- isStateWithOutputSignal(state) {
254
- const obj = state;
255
- return (typeof obj === 'object' &&
256
- obj !== null &&
257
- typeof obj.getOutputSignal === 'function');
258
- }
259
- executeStateTransition(transition) {
260
- const oldState = this.currentState;
261
- const newState = transition.to;
262
- // Only execute actions if there's an actual state change
263
- if (oldState !== newState) {
264
- // Execute before exit action on current state
265
- this.executeExitAction(oldState);
266
- // Change state
267
- this.currentState = newState;
268
- // Execute after entry action on new state
269
- this.executeEntryAction(newState);
270
- }
271
- }
272
- executeStateActions(state) {
273
- this.executeEntryAction(state);
274
- this.executeExitAction(state);
275
- }
276
- /**
277
- * Type guard to check if a state is an instance of DefaultState class.
278
- *
279
- * @param state - The state to check
280
- * @returns true if the state is an instance of DefaultState
281
- */
282
- isDefaultState(state) {
283
- return state instanceof DefaultState;
284
- }
285
- /**
286
- * Type guard to check if a state implements IStateWithAfterEntryAction.
287
- *
288
- * @param state - The state to check
289
- * @returns true if the state implements afterEntryAction method
290
- */
291
- hasAfterEntryAction(state) {
292
- return typeof state === 'object' &&
293
- state !== null &&
294
- 'afterEntryAction' in state &&
295
- typeof state.afterEntryAction === 'function';
296
- }
297
- /**
298
- * Type guard to check if a state implements IStateWithBeforeExitAction.
299
- *
300
- * @param state - The state to check
301
- * @returns true if the state implements beforeExitAction method
302
- */
303
- hasBeforeExitAction(state) {
304
- return typeof state === 'object' &&
305
- state !== null &&
306
- 'beforeExitAction' in state &&
307
- typeof state.beforeExitAction === 'function';
308
- }
309
- /**
310
- * Executes the afterEntryAction on a state if it implements IStateWithAfterEntryAction.
311
- *
312
- * @param state - The state to execute the entry action on
313
- */
314
- executeEntryAction(state) {
315
- if (this.hasAfterEntryAction(state)) {
316
- state.afterEntryAction();
317
- }
318
- }
319
- /**
320
- * Executes the beforeExitAction on a state if it implements IStateWithBeforeExitAction.
321
- *
322
- * @param state - The state to execute the exit action on
323
- */
324
- executeExitAction(state) {
325
- if (this.hasBeforeExitAction(state)) {
326
- state.beforeExitAction();
327
- }
328
- }
329
- /**
330
- * Gets all possible states.
331
- *
332
- * @returns Array of all states
333
- */
334
- getAllStates() {
335
- return [...this.states];
336
- }
337
- /**
338
- * Gets all possible signals.
339
- *
340
- * @returns Array of all signals
341
- */
342
- getAllSignals() {
343
- return [...this.signals];
344
- }
345
- /**
346
- * Checks if a given signal is valid from the current state.
347
- *
348
- * @param signal - The signal to check
349
- * @returns true if the signal can trigger a transition from the current state
350
- */
351
- isValidSignalFromCurrentState(signal) {
352
- return this.transitions.some(t => t.from === this.currentState && t.signal === signal);
353
- }
354
- /**
355
- * Gets all valid signals from the current state.
356
- *
357
- * @returns Array of signals that can trigger transitions from the current state
358
- */
359
- getValidSignalsFromCurrentState() {
360
- return this.transitions
361
- .filter(t => t.from === this.currentState)
362
- .map(t => t.signal);
363
- }
364
- /**
365
- * Gets all transitions.
366
- *
367
- * @returns Array of all transitions
368
- */
369
- getTransitions() {
370
- return [...this.transitions];
371
- }
372
- /**
373
- * Checks if a signal is valid in the state machine.
374
- * @param signal - The signal to check
375
- * @returns True if the signal is valid, false otherwise.
376
- */
377
- hasSignal(signal) {
378
- return this.getAllSignals().includes(signal);
379
- }
380
- /**
381
- * Checks if a given state exists in the state machine.
382
- *
383
- * @param state - The state to check
384
- * @returns true if the state exists, false otherwise
385
- */
386
- hasState(state) {
387
- return this.getAllStates().includes(state);
388
- }
389
- /**
390
- * Gets the default state that handles invalid signals.
391
- *
392
- * @returns The default state or null if none exists
393
- */
394
- getDefaultState() {
395
- return this.defaultState;
396
- }
397
- /**
398
- * Checks if the state machine has a default state.
399
- *
400
- * @returns true if a default state exists
401
- */
402
- hasDefaultState() {
403
- return this.defaultState !== null;
404
- }
405
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1,127 +0,0 @@
1
- import { FiniteStateMachine } from './FiniteStateMachine';
2
- /**
3
- * Abstract base class for finite state machines that use transition matrices.
4
- *
5
- * This class extends FiniteStateMachine to provide a more intuitive way to define
6
- * state transitions using a 2D matrix representation instead of explicit transition arrays.
7
- *
8
- * The matrix format allows for visual representation of state transitions:
9
- * - Columns represent states
10
- * - Rows represent signals
11
- * - Cell values represent target states
12
- * - Empty cells represent invalid/ignored transitions
13
- *
14
- * @template STATE - The type representing possible states
15
- * @template SIGNAL - The type representing signals/events
16
- *
17
- * @example
18
- * ```typescript
19
- * // Example 1: Using explicit start state
20
- * class TurnstileMatrix extends MatrixBasedStateMachine<string, string> {
21
- * constructor() {
22
- * const matrix = transitionMatrix([
23
- * [ , "locked" , "unlocked" ],
24
- * [ "coin" , "unlocked" , ],
25
- * [ "push" , , "locked" ]
26
- * ]);
27
- * super(matrix, "locked"); // Explicit start state
28
- * }
29
- * }
30
- *
31
- * // Example 2: Using first state as start state (auto)
32
- * class TurnstileMatrixAuto extends MatrixBasedStateMachine<string, string> {
33
- * constructor() {
34
- * const matrix = transitionMatrix([
35
- * [ , "locked" , "unlocked" ], // "locked" becomes start state
36
- * [ "coin" , "unlocked" , ],
37
- * [ "push" , , "locked" ]
38
- * ]);
39
- * super(matrix); // Uses first state ("locked") as start state
40
- * }
41
- * }
42
- * ```
43
- */
44
- export class MatrixBasedStateMachine extends FiniteStateMachine {
45
- /**
46
- * Creates a new matrix-based finite state machine.
47
- *
48
- * @param matrix - The transition matrix defining all states, signals, and transitions
49
- * @param startState - Optional. The initial state of the machine. If not provided,
50
- * the first state from the matrix (first column in header row) will be used.
51
- * If provided, must be one of the states in the matrix.
52
- *
53
- * @throws {Error} If matrix has no states
54
- * @throws {Error} If startState is provided but not found in the matrix states
55
- *
56
- * @example
57
- * ```typescript
58
- * // Constructor with explicit start state
59
- * const matrix = transitionMatrix([
60
- * [ , "locked" , "unlocked" ],
61
- * [ "coin" , "unlocked" , ],
62
- * [ "push" , , "locked" ]
63
- * ]);
64
- * super(matrix, "unlocked"); // Start in unlocked state
65
- *
66
- * // Constructor with auto start state (uses first state)
67
- * super(matrix); // Automatically starts in "locked" state
68
- * ```
69
- *
70
- * @example
71
- * ```typescript
72
- * // TurnstileMatrix implementation from test/Turnstile/TurnstileMatrix.ts
73
- * class TurnstileMatrix extends MatrixBasedStateMachine<string, string> {
74
- * constructor() {
75
- * const matrix = transitionMatrix([
76
- * [ , "locked" , "unlocked" ],
77
- * [ "coin" , "unlocked" , ],
78
- * [ "push" , , "locked" ]
79
- * ]);
80
- *
81
- * // Two ways to call the constructor:
82
- * super(matrix, "locked"); // Option 1: Explicit start state
83
- * // OR
84
- * super(matrix); // Option 2: Auto start state (uses "locked")
85
- * }
86
- *
87
- * insertCoin(): string { return this.sendSignal('coin'); }
88
- * pushThrough(): string { return this.sendSignal('push'); }
89
- * isLocked(): boolean { return this.getCurrentState() === 'locked'; }
90
- * isUnlocked(): boolean { return this.getCurrentState() === 'unlocked'; }
91
- * }
92
- * ```
93
- */
94
- constructor(matrix, startState) {
95
- // Get states from matrix
96
- const states = matrix.getStates();
97
- // Validate matrix has states
98
- if (states.length === 0) {
99
- throw new Error('Matrix must contain at least one state');
100
- }
101
- // Determine the actual start state
102
- let actualStartState;
103
- if (startState !== undefined) {
104
- // Explicit start state provided - validate it exists
105
- if (!states.includes(startState)) {
106
- throw new Error(`Start state '${String(startState)}' not found in matrix states: [${states.map(s => String(s)).join(', ')}]`);
107
- }
108
- actualStartState = startState;
109
- }
110
- else {
111
- // No start state provided - use first state from matrix
112
- actualStartState = states[0];
113
- }
114
- // Call parent constructor with matrix-derived data
115
- super(matrix.getStates(), matrix.getSignals(), matrix.getTransitions(), actualStartState);
116
- // Store the matrix for potential future use
117
- this.matrix = matrix;
118
- }
119
- /**
120
- * Gets the transition matrix used by this state machine.
121
- *
122
- * @returns The transition matrix
123
- */
124
- getMatrix() {
125
- return this.matrix;
126
- }
127
- }
@@ -1,98 +0,0 @@
1
- /**
2
- * Creates transitions from a 2D matrix representation.
3
- *
4
- * Matrix format:
5
- * - First row: [undefined, ...states] - column headers
6
- * - Subsequent rows: [signal, ...transitions] - signal + target states
7
- * - Empty cells: undefined, null, "", or omitted
8
- *
9
- * @template STATE - Type for states
10
- * @template SIGNAL - Type for signals
11
- */
12
- export class TransitionMatrix {
13
- constructor() {
14
- this.states = [];
15
- this.signals = [];
16
- this.transitions = [];
17
- }
18
- /**
19
- * Creates a transition matrix from a 2D array.
20
- *
21
- * @param matrix - 2D array where:
22
- * - matrix[0] = [undefined, ...states] (header row)
23
- * - matrix[i] = [signal, target1, target2, ...] (transition rows)
24
- *
25
- * @example
26
- * ```typescript
27
- * const matrix = [
28
- * [ , "locked" , "unlocked" ], // States header
29
- * [ "coin" , "unlocked" , ], // coin transitions
30
- * [ "push" , , "locked" ] // push transitions
31
- * ];
32
- * ```
33
- */
34
- static fromArray(matrix) {
35
- const instance = new TransitionMatrix();
36
- if (matrix.length === 0) {
37
- throw new Error('Matrix cannot be empty');
38
- }
39
- // Extract states from header row (skip first empty cell)
40
- const headerRow = matrix[0];
41
- const stateValues = headerRow.slice(1).filter(cell => cell !== undefined && cell !== null && cell !== "");
42
- // Type assertion: assume header row contains only states
43
- instance.states = stateValues;
44
- // Process each signal row
45
- for (let rowIndex = 1; rowIndex < matrix.length; rowIndex++) {
46
- const row = matrix[rowIndex];
47
- const signal = row[0];
48
- // Skip rows without valid signal
49
- if (!signal || signal === "" || signal === null || signal === undefined) {
50
- continue;
51
- }
52
- // Type assertion: assume first column contains only signals
53
- instance.signals.push(signal);
54
- // Process each state column
55
- for (let colIndex = 1; colIndex < row.length; colIndex++) {
56
- const targetState = row[colIndex];
57
- const fromState = instance.states[colIndex - 1];
58
- // If cell has a value, create transition
59
- if (targetState && targetState !== "" && targetState !== null && targetState !== undefined && fromState) {
60
- // Type assertion: assume cell values are states
61
- instance.transitions.push({
62
- from: fromState,
63
- to: targetState,
64
- signal: signal
65
- });
66
- }
67
- }
68
- }
69
- return instance;
70
- }
71
- /**
72
- * Get all states from the matrix.
73
- */
74
- getStates() {
75
- return [...this.states];
76
- }
77
- /**
78
- * Get all signals from the matrix.
79
- */
80
- getSignals() {
81
- return [...this.signals];
82
- }
83
- /**
84
- * Get all transitions from the matrix.
85
- */
86
- getTransitions() {
87
- return [...this.transitions];
88
- }
89
- }
90
- /**
91
- * Helper function to create transition matrix from 2D array.
92
- *
93
- * @template S - State type
94
- * @template G - Signal type
95
- */
96
- export function transitionMatrix(matrix) {
97
- return TransitionMatrix.fromArray(matrix);
98
- }
package/lib/esm/index.js DELETED
@@ -1,9 +0,0 @@
1
- /**
2
- * Public API Surface of ts-stop
3
- */
4
- export * from './fa/DefaultState';
5
- export * from './fa/FiniteStateMachine';
6
- export * from './fa/IStateWithActions';
7
- export * from './fa/IStateWithOutputSignal';
8
- export * from './fa/MatrixBasedStateMachine';
9
- export * from './fa/TransitionMatrix';