@axi-engine/states 0.3.1 → 0.3.3

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
@@ -149,9 +149,9 @@ await gameState.call(GameState.Paused); // Error: Transition from MainMenu to Pa
149
149
  The public `onChange` property is an `Emitter`. You can use its `subscribe` method to be notified of any state change. The method returns a function to unsubscribe.
150
150
 
151
151
  ```typescript
152
- const unsubscribe = gameState.onChange.subscribe((from, to, payload) => {
153
- const fromState = from !== undefined ? GameState[from] : 'Start';
154
- console.log(`State changed from ${fromState} to ${GameState[to]}`, { payload });
152
+ const unsubscribe = gameState.onChange.subscribe(event => {
153
+ const fromState = event.from !== undefined ? GameState[event.from] : 'Start';
154
+ console.log(`State changed from ${fromState} to ${GameState[event.to]}`, { payload: event.payload });
155
155
  });
156
156
 
157
157
  await gameState.call(GameState.MainMenu);
@@ -176,9 +176,9 @@ enum GameState {
176
176
  // --- Setup ---
177
177
  const game = new StateMachine<GameState>();
178
178
 
179
- game.onChange.subscribe((from, to) => {
180
- const fromState = from !== undefined ? GameState[from] : 'Start';
181
- console.log(`[SYSTEM] Transition: ${fromState} -> ${GameState[to]}`);
179
+ game.onChange.subscribe(e => {
180
+ const fromState = e.from !== undefined ? GameState[e.from] : 'Start';
181
+ console.log(`[SYSTEM] Transition: ${fromState} -> ${GameState[e.to]}`);
182
182
  });
183
183
 
184
184
  game.register(GameState.MainMenu, {
package/dist/index.d.mts CHANGED
@@ -1,9 +1,32 @@
1
1
  import { Emitter } from '@axijs/emitter';
2
2
 
3
+ /**
4
+ * Represents a lifecycle handler function for a state.
5
+ * If a payload type `P` is provided and is not `void`, the handler receives the payload.
6
+ * Supports both synchronous and asynchronous execution.
7
+ *
8
+ * @template P The type of the payload passed to the handler.
9
+ */
3
10
  type StateHandler<P = void> = P extends void ? () => void | Promise<void> : (payload: P) => void | Promise<void>;
11
+ /**
12
+ * Configuration object for defining a state's behavior and transition rules.
13
+ *
14
+ * @template T The type used for state identifiers.
15
+ * @template P The type of the payload passed to the `onEnter` handler.
16
+ */
4
17
  interface StateHandlerConfig<T, P = void> {
18
+ /**
19
+ * Hook executed when transitioning into this state.
20
+ */
5
21
  onEnter?: StateHandler<P>;
22
+ /**
23
+ * Hook executed when transitioning out of this state.
24
+ */
6
25
  onExit?: () => void | Promise<void>;
26
+ /**
27
+ * Optional list of states from which a transition to this state is permitted.
28
+ * If undefined, the state can be entered from any state.
29
+ */
7
30
  allowedFrom?: T[];
8
31
  }
9
32
  type StateHandlerRegistration<T, P = void> = StateHandler<P> | StateHandlerConfig<T, P>;
@@ -43,11 +66,15 @@ declare class StateMachine<T, P = void> {
43
66
  * The event provides the old state, the new state, and the payload.
44
67
  * @see Emitter
45
68
  * @example
46
- * fsm.onChange.subscribe((from, to, payload) => {
47
- * console.log(`State transitioned from ${from} to ${to}`);
69
+ * fsm.onChange.subscribe(e => {
70
+ * console.log(`State transitioned from ${e.from} to ${e.to}`);
48
71
  * });
49
72
  */
50
- readonly onChange: Emitter<[from?: T | undefined, to?: T | undefined, payload?: P | undefined]>;
73
+ readonly onChange: Emitter<{
74
+ from?: T;
75
+ to?: T;
76
+ payload?: P;
77
+ }>;
51
78
  /**
52
79
  * Gets the current state of the machine.
53
80
  * @returns The current state identifier, or `undefined` if the machine has not been started.
package/dist/index.d.ts CHANGED
@@ -1,9 +1,32 @@
1
1
  import { Emitter } from '@axijs/emitter';
2
2
 
3
+ /**
4
+ * Represents a lifecycle handler function for a state.
5
+ * If a payload type `P` is provided and is not `void`, the handler receives the payload.
6
+ * Supports both synchronous and asynchronous execution.
7
+ *
8
+ * @template P The type of the payload passed to the handler.
9
+ */
3
10
  type StateHandler<P = void> = P extends void ? () => void | Promise<void> : (payload: P) => void | Promise<void>;
11
+ /**
12
+ * Configuration object for defining a state's behavior and transition rules.
13
+ *
14
+ * @template T The type used for state identifiers.
15
+ * @template P The type of the payload passed to the `onEnter` handler.
16
+ */
4
17
  interface StateHandlerConfig<T, P = void> {
18
+ /**
19
+ * Hook executed when transitioning into this state.
20
+ */
5
21
  onEnter?: StateHandler<P>;
22
+ /**
23
+ * Hook executed when transitioning out of this state.
24
+ */
6
25
  onExit?: () => void | Promise<void>;
26
+ /**
27
+ * Optional list of states from which a transition to this state is permitted.
28
+ * If undefined, the state can be entered from any state.
29
+ */
7
30
  allowedFrom?: T[];
8
31
  }
9
32
  type StateHandlerRegistration<T, P = void> = StateHandler<P> | StateHandlerConfig<T, P>;
@@ -43,11 +66,15 @@ declare class StateMachine<T, P = void> {
43
66
  * The event provides the old state, the new state, and the payload.
44
67
  * @see Emitter
45
68
  * @example
46
- * fsm.onChange.subscribe((from, to, payload) => {
47
- * console.log(`State transitioned from ${from} to ${to}`);
69
+ * fsm.onChange.subscribe(e => {
70
+ * console.log(`State transitioned from ${e.from} to ${e.to}`);
48
71
  * });
49
72
  */
50
- readonly onChange: Emitter<[from?: T | undefined, to?: T | undefined, payload?: P | undefined]>;
73
+ readonly onChange: Emitter<{
74
+ from?: T;
75
+ to?: T;
76
+ payload?: P;
77
+ }>;
51
78
  /**
52
79
  * Gets the current state of the machine.
53
80
  * @returns The current state identifier, or `undefined` if the machine has not been started.
package/dist/index.js CHANGED
@@ -43,8 +43,8 @@ var StateMachine = class {
43
43
  * The event provides the old state, the new state, and the payload.
44
44
  * @see Emitter
45
45
  * @example
46
- * fsm.onChange.subscribe((from, to, payload) => {
47
- * console.log(`State transitioned from ${from} to ${to}`);
46
+ * fsm.onChange.subscribe(e => {
47
+ * console.log(`State transitioned from ${e.from} to ${e.to}`);
48
48
  * });
49
49
  */
50
50
  onChange = new import_emitter.Emitter();
@@ -98,7 +98,7 @@ var StateMachine = class {
98
98
  */
99
99
  async call(newState, payload) {
100
100
  const oldState = this._state;
101
- const oldStateConfig = this._state ? this.states.get(this._state) : void 0;
101
+ const oldStateConfig = (0, import_ensure.isUndefined)(this._state) ? void 0 : this.states.get(this._state);
102
102
  const newStateConfig = this.states.get(newState);
103
103
  (0, import_ensure.throwIfEmpty)(newStateConfig, `State ${String(newState)} is not registered.`);
104
104
  (0, import_ensure.throwIf)(
@@ -108,7 +108,7 @@ var StateMachine = class {
108
108
  await oldStateConfig?.onExit?.();
109
109
  this._state = newState;
110
110
  await newStateConfig.onEnter?.(payload);
111
- this.onChange.emit(oldState, newState, payload);
111
+ this.onChange.emit({ from: oldState, to: newState, payload });
112
112
  }
113
113
  /**
114
114
  * Removes a single state configuration from the machine.
package/dist/index.mjs CHANGED
@@ -17,8 +17,8 @@ var StateMachine = class {
17
17
  * The event provides the old state, the new state, and the payload.
18
18
  * @see Emitter
19
19
  * @example
20
- * fsm.onChange.subscribe((from, to, payload) => {
21
- * console.log(`State transitioned from ${from} to ${to}`);
20
+ * fsm.onChange.subscribe(e => {
21
+ * console.log(`State transitioned from ${e.from} to ${e.to}`);
22
22
  * });
23
23
  */
24
24
  onChange = new Emitter();
@@ -72,7 +72,7 @@ var StateMachine = class {
72
72
  */
73
73
  async call(newState, payload) {
74
74
  const oldState = this._state;
75
- const oldStateConfig = this._state ? this.states.get(this._state) : void 0;
75
+ const oldStateConfig = isUndefined(this._state) ? void 0 : this.states.get(this._state);
76
76
  const newStateConfig = this.states.get(newState);
77
77
  throwIfEmpty(newStateConfig, `State ${String(newState)} is not registered.`);
78
78
  throwIf(
@@ -82,7 +82,7 @@ var StateMachine = class {
82
82
  await oldStateConfig?.onExit?.();
83
83
  this._state = newState;
84
84
  await newStateConfig.onEnter?.(payload);
85
- this.onChange.emit(oldState, newState, payload);
85
+ this.onChange.emit({ from: oldState, to: newState, payload });
86
86
  }
87
87
  /**
88
88
  * Removes a single state configuration from the machine.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axi-engine/states",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "A minimal, type-safe state machine for the Axi Engine, designed for managing game logic and component states with a simple API.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -32,15 +32,16 @@
32
32
  },
33
33
  "scripts": {
34
34
  "build": "tsup",
35
- "docs": "typedoc src/index.ts --out docs/api --options ../../typedoc.json",
36
- "test": "echo 'No tests yet for @axi-engine/states'"
35
+ "prebuild": "npm test",
36
+ "test": "vitest run",
37
+ "docs": "typedoc src/index.ts --out docs/api --options ../../typedoc.json"
37
38
  },
38
39
  "files": [
39
40
  "dist"
40
41
  ],
41
42
  "peerDependencies": {
42
- "@axijs/emitter": "^1.0.0",
43
- "@axijs/ensure": "^1.0.0",
43
+ "@axijs/emitter": "^1.1.1",
44
+ "@axijs/ensure": "^1.0.2",
44
45
  "@axi-engine/utils": "^0.3.0"
45
46
  }
46
47
  }