@ue-too/being 0.14.1 → 0.16.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/README.md CHANGED
@@ -14,11 +14,13 @@ Let's say we want to build a state machine for a vending machine.
14
14
  To make it simple, the vending machine only accepts dollar bills and sells 3 types of items at $1, $2, and $3.
15
15
 
16
16
  The items are:
17
+
17
18
  - Coke (1 dollar)
18
19
  - Red Bull (2 dollars)
19
20
  - Water (3 dollars)
20
21
 
21
22
  There are only 3 kinds of actions that the user can take:
23
+
22
24
  - insert coins
23
25
  - select an item (we can break it into multiple events; each event representing a different item)
24
26
  - cancel the transaction
@@ -26,15 +28,18 @@ There are only 3 kinds of actions that the user can take:
26
28
  With the above information, we can create a state machine for the vending machine.
27
29
 
28
30
  For the `@ue-too/being` library, there are 3 main things that we need to define and be clear on in order to create a state machine:
31
+
29
32
  - All possible states of the state machine.
30
33
  - All possible events that can happen in the state machine.
31
34
  - The context of the state machine.
32
35
  - The rules for the state transitions.
33
36
 
34
37
  Let's start with the all possible states of the state machine.
38
+
35
39
  > There are many ways to represent the vending machine in a state machine. My way is only one possible way but you can probably come up with a better way or at least what makes sense to you.
36
40
 
37
41
  I'm defining the states as follows:
42
+
38
43
  - IDLE
39
44
  - 1 Dollar Inserted
40
45
  - 2 Dollars Inserted
@@ -43,12 +48,19 @@ I'm defining the states as follows:
43
48
  To create a type that is a string literal union of the states, we can use the utilty type `CreateStateType`.
44
49
 
45
50
  ```ts
46
- import { CreateStateType } from "@ue-too/being";
47
-
48
- const VENDING_MACHINE_STATES = ["IDLE", "ONE_DOLLAR_INSERTED", "TWO_DOLLARS_INSERTED", "THREE_DOLLARS_INSERTED"] as const;
49
- export type VendingMachineStates = CreateStateType<typeof VENDING_MACHINE_STATES>;
50
-
51
+ import { CreateStateType } from '@ue-too/being';
52
+
53
+ const VENDING_MACHINE_STATES = [
54
+ 'IDLE',
55
+ 'ONE_DOLLAR_INSERTED',
56
+ 'TWO_DOLLARS_INSERTED',
57
+ 'THREE_DOLLARS_INSERTED',
58
+ ] as const;
59
+ export type VendingMachineStates = CreateStateType<
60
+ typeof VENDING_MACHINE_STATES
61
+ >;
51
62
  ```
63
+
52
64
  Next, we should define all the possible events and their payload.
53
65
 
54
66
  ```ts
@@ -58,7 +70,7 @@ type VendingMachineEvents = {
58
70
  selectRedBull: {};
59
71
  selectWater: {};
60
72
  cancelTransaction: {};
61
- }
73
+ };
62
74
  ```
63
75
 
64
76
  Sometimes we need variables to keep track of certain attributes that can persists across different states; that's where the context comes along.
@@ -89,10 +101,19 @@ There's only one thing required to override in the abstract class which is the `
89
101
  It's an object with the key being the event name and the value being the reaction and the default target state to transition to after the reaction.
90
102
 
91
103
  The `EventReactions` looks like this:
104
+
92
105
  ```ts
93
- export type EventReactions<EventPayloadMapping, Context extends BaseContext, States extends string> = {
106
+ export type EventReactions<
107
+ EventPayloadMapping,
108
+ Context extends BaseContext,
109
+ States extends string,
110
+ > = {
94
111
  [K in keyof Partial<EventPayloadMapping>]: {
95
- action: (context: Context, event: EventPayloadMapping[K], stateMachine: StateMachine<EventPayloadMapping, Context, States>) => void;
112
+ action: (
113
+ context: Context,
114
+ event: EventPayloadMapping[K],
115
+ stateMachine: StateMachine<EventPayloadMapping, Context, States>
116
+ ) => void;
96
117
  defaultTargetState?: States;
97
118
  };
98
119
  };
@@ -105,138 +126,171 @@ Now let's implement the `IdleState`.
105
126
  In the idle state, we only care about the `insertBills` event.
106
127
 
107
128
  ```ts
108
- import { TemplateState, EventReactions } from "@ue-too/being";
109
-
110
- class IdleState extends TemplateState<VendingMachineEvents, BaseContext, VendingMachineStates> {
111
-
112
- public eventReactions: EventReactions<VendingMachineEvents, BaseContext, VendingMachineStates> = {
113
- "insertBills": {
129
+ import { EventReactions, TemplateState } from '@ue-too/being';
130
+
131
+ class IdleState extends TemplateState<
132
+ VendingMachineEvents,
133
+ BaseContext,
134
+ VendingMachineStates
135
+ > {
136
+ public eventReactions: EventReactions<
137
+ VendingMachineEvents,
138
+ BaseContext,
139
+ VendingMachineStates
140
+ > = {
141
+ insertBills: {
114
142
  action: (context, event, stateMachine) => {
115
- console.log("inserted bills");
143
+ console.log('inserted bills');
116
144
  },
117
- defaultTargetState: "ONE_DOLLAR_INSERTED"
145
+ defaultTargetState: 'ONE_DOLLAR_INSERTED',
118
146
  },
119
- }
147
+ };
120
148
  }
121
149
  ```
122
150
 
123
151
  After that we can implement the `OneDollarInsertedState`.
124
152
 
125
153
  ```ts
126
- import { TemplateState, EventReactions } from "@ue-too/being";
127
-
128
- class OneDollarInsertedState extends TemplateState<VendingMachineEvents, BaseContext, VendingMachineStates> {
129
- public eventReactions: EventReactions<VendingMachineEvents, BaseContext, VendingMachineStates> = {
130
- "insertBills": {
154
+ import { EventReactions, TemplateState } from '@ue-too/being';
155
+
156
+ class OneDollarInsertedState extends TemplateState<
157
+ VendingMachineEvents,
158
+ BaseContext,
159
+ VendingMachineStates
160
+ > {
161
+ public eventReactions: EventReactions<
162
+ VendingMachineEvents,
163
+ BaseContext,
164
+ VendingMachineStates
165
+ > = {
166
+ insertBills: {
131
167
  action: (context, event, stateMachine) => {
132
- console.log("inserted bills");
168
+ console.log('inserted bills');
133
169
  },
134
- defaultTargetState: "TWO_DOLLARS_INSERTED"
170
+ defaultTargetState: 'TWO_DOLLARS_INSERTED',
135
171
  },
136
- "selectCoke": {
172
+ selectCoke: {
137
173
  action: (context, event, stateMachine) => {
138
- console.log("selected coke");
174
+ console.log('selected coke');
139
175
  },
140
- defaultTargetState: "IDLE"
176
+ defaultTargetState: 'IDLE',
141
177
  },
142
- "selectRedBull": {
178
+ selectRedBull: {
143
179
  action: (context, event, stateMachine) => {
144
180
  console.log('not enough money, 1 dollar short');
145
181
  },
146
182
  },
147
- "selectWater": {
183
+ selectWater: {
148
184
  action: (context, event, stateMachine) => {
149
185
  console.log('not enough money, 2 dollars short');
150
186
  },
151
187
  },
152
- "cancelTransaction": {
188
+ cancelTransaction: {
153
189
  action: (context, event, stateMachine) => {
154
190
  console.log('cancelled transaction');
155
191
  console.log('refunding 1 dollar');
156
192
  },
157
- defaultTargetState: "IDLE"
158
- }
159
- }
193
+ defaultTargetState: 'IDLE',
194
+ },
195
+ };
160
196
  }
161
197
  ```
198
+
162
199
  For the implementation of the `TwoDollarsInsertedState` and `ThreeDollarsInsertedState`, it's very similar to the `OneDollarInsertedState`.
163
200
 
164
201
  ```ts
165
- import { TemplateState, EventReactions } from "@ue-too/being";
166
-
167
- class TwoDollarsInsertedState extends TemplateState<VendingMachineEvents, BaseContext, VendingMachineStates> {
168
- public eventReactions: EventReactions<VendingMachineEvents, BaseContext, VendingMachineStates> = {
169
- "insertBills": {
202
+ import { EventReactions, TemplateState } from '@ue-too/being';
203
+
204
+ class TwoDollarsInsertedState extends TemplateState<
205
+ VendingMachineEvents,
206
+ BaseContext,
207
+ VendingMachineStates
208
+ > {
209
+ public eventReactions: EventReactions<
210
+ VendingMachineEvents,
211
+ BaseContext,
212
+ VendingMachineStates
213
+ > = {
214
+ insertBills: {
170
215
  action: (context, event, stateMachine) => {
171
- console.log("inserted bills");
216
+ console.log('inserted bills');
172
217
  },
173
- defaultTargetState: "THREE_DOLLARS_INSERTED"
218
+ defaultTargetState: 'THREE_DOLLARS_INSERTED',
174
219
  },
175
- "selectCoke": {
220
+ selectCoke: {
176
221
  action: (context, event, stateMachine) => {
177
- console.log("selected coke");
222
+ console.log('selected coke');
178
223
  },
179
- defaultTargetState: "IDLE"
224
+ defaultTargetState: 'IDLE',
180
225
  },
181
- "selectRedBull": {
226
+ selectRedBull: {
182
227
  action: (context, event, stateMachine) => {
183
228
  console.log('selected red bull');
184
229
  },
185
- defaultTargetState: "IDLE"
230
+ defaultTargetState: 'IDLE',
186
231
  },
187
- "selectWater": {
232
+ selectWater: {
188
233
  action: (context, event, stateMachine) => {
189
234
  console.log('not enough money, 1 dollars short');
190
235
  },
191
236
  },
192
- "cancelTransaction": {
237
+ cancelTransaction: {
193
238
  action: (context, event, stateMachine) => {
194
239
  console.log('cancelled transaction');
195
240
  console.log('refunding 2 dollars');
196
241
  },
197
- defaultTargetState: "IDLE"
198
- }
199
- }
242
+ defaultTargetState: 'IDLE',
243
+ },
244
+ };
200
245
  }
201
246
 
202
- class ThreeDollarsInsertedState extends TemplateState<VendingMachineEvents, BaseContext, VendingMachineStates> {
203
- public eventReactions: EventReactions<VendingMachineEvents, BaseContext, VendingMachineStates> = {
204
- "insertBills": {
247
+ class ThreeDollarsInsertedState extends TemplateState<
248
+ VendingMachineEvents,
249
+ BaseContext,
250
+ VendingMachineStates
251
+ > {
252
+ public eventReactions: EventReactions<
253
+ VendingMachineEvents,
254
+ BaseContext,
255
+ VendingMachineStates
256
+ > = {
257
+ insertBills: {
205
258
  action: (context, event, stateMachine) => {
206
259
  console.log('not taking more bills');
207
260
  console.log('returning the inserted bills');
208
261
  },
209
262
  },
210
- "selectCoke": {
263
+ selectCoke: {
211
264
  action: (context, event, stateMachine) => {
212
- console.log("selected coke");
265
+ console.log('selected coke');
213
266
  console.log('no change');
214
267
  },
215
- defaultTargetState: "IDLE"
268
+ defaultTargetState: 'IDLE',
216
269
  },
217
- "selectRedBull": {
270
+ selectRedBull: {
218
271
  action: (context, event, stateMachine) => {
219
272
  console.log('selected red bull');
220
273
  console.log('change: 1 dollar');
221
274
  },
222
- defaultTargetState: "IDLE"
275
+ defaultTargetState: 'IDLE',
223
276
  },
224
- "selectWater": {
277
+ selectWater: {
225
278
  action: (context, event, stateMachine) => {
226
279
  console.log('selected water');
227
280
  console.log('no change');
228
281
  },
229
282
  },
230
- "cancelTransaction": {
283
+ cancelTransaction: {
231
284
  action: (context, event, stateMachine) => {
232
285
  console.log('cancelled transaction');
233
286
  console.log('refunding 3 dollars');
234
287
  },
235
- defaultTargetState: "IDLE"
236
- }
237
- }
288
+ defaultTargetState: 'IDLE',
289
+ },
290
+ };
238
291
  }
239
292
  ```
293
+
240
294
  With all the states implemented, we can now create the state machine.
241
295
 
242
296
  ```ts
@@ -261,4 +315,3 @@ vendingMachine.happens("selectCoke");
261
315
  ```
262
316
 
263
317
  For the full complete example code, please refer to the `src/vending-machine-example.ts` file.
264
-
@@ -1,13 +1 @@
1
- /**
2
- * Example: Hierarchical State Machine POC
3
- *
4
- * @remarks
5
- * This example demonstrates how to use hierarchical state machines with
6
- * composite states that contain child state machines.
7
- *
8
- * Scenario: A media player with hierarchical states
9
- * - Top level: IDLE, PLAYING, PAUSED
10
- * - PLAYING contains: BUFFERING, STREAMING, ERROR
11
- * - Events can be handled at child or parent level
12
- */
13
1
  export declare function runHierarchicalStateMachineExample(): void;
package/hierarchical.d.ts CHANGED
@@ -16,7 +16,7 @@
16
16
  *
17
17
  * @category Hierarchical State Machines
18
18
  */
19
- import { BaseContext, StateMachine, TemplateStateMachine, TemplateState, EventResult, DefaultOutputMapping, EventArgs } from "./interface";
19
+ import { BaseContext, DefaultOutputMapping, EventArgs, EventResult, StateMachine, TemplateState, TemplateStateMachine } from './interface';
20
20
  /**
21
21
  * Represents a hierarchical state path using dot notation.
22
22
  * Example: "PARENT.CHILD" means we're in CHILD state within PARENT state.
@@ -93,8 +93,8 @@ export declare abstract class CompositeState<EventPayloadMapping = any, Context
93
93
  * Gets the full hierarchical path of the current state.
94
94
  */
95
95
  getStatePath(parentState: ParentStates): HierarchicalStatePath<ParentStates, ChildStates>;
96
- uponEnter(context: Context, stateMachine: StateMachine<EventPayloadMapping, Context, ParentStates, EventOutputMapping>, from: ParentStates | "INITIAL"): void;
97
- beforeExit(context: Context, stateMachine: StateMachine<EventPayloadMapping, Context, ParentStates, EventOutputMapping>, to: ParentStates | "TERMINAL"): void;
96
+ uponEnter(context: Context, stateMachine: StateMachine<EventPayloadMapping, Context, ParentStates, EventOutputMapping>, from: ParentStates | 'INITIAL'): void;
97
+ beforeExit(context: Context, stateMachine: StateMachine<EventPayloadMapping, Context, ParentStates, EventOutputMapping>, to: ParentStates | 'TERMINAL'): void;
98
98
  handles<K extends keyof EventPayloadMapping | string>(args: EventArgs<EventPayloadMapping, K>, context: Context, stateMachine: StateMachine<EventPayloadMapping, Context, ParentStates, EventOutputMapping>): EventResult<ParentStates, K extends keyof EventOutputMapping ? EventOutputMapping[K] : void>;
99
99
  }
100
100
  /**
package/index.d.ts CHANGED
@@ -108,6 +108,6 @@
108
108
  * @see {@link TemplateStateMachine} for the main state machine class
109
109
  * @see {@link TemplateState} for creating state implementations
110
110
  */
111
- export * from "./interface";
112
- export * from "./schema-factory";
113
- export * from "./hierarchical";
111
+ export * from './interface';
112
+ export * from './schema-factory';
113
+ export * from './hierarchical';