diff --git a/README.md b/README.md index 8cd3b58..6f36c74 100644 --- a/README.md +++ b/README.md @@ -236,7 +236,8 @@ using `send` will reject. - `subscribe`: You can register a callback that will be invoked on every state transition between the `onEntry` and `onExit` hooks. -The callback returns the `subscriptionId` and receives `context` and `currentStateId`. +The callback returns the `subscriptionId` and receives `context`, +`event`, `sharedData`, and `currentStateId`. - `unsubscribe`: Remove the subscription with the given `subscriptionId`. diff --git a/docs/api.md b/docs/api.md index 7686f00..fa9d302 100644 --- a/docs/api.md +++ b/docs/api.md @@ -66,7 +66,7 @@ using `send` will reject. - `subscribe`: You can register a callback that will be invoked on every state transition between the `onEntry` and `onExit` hooks. -The callback returns the `subscriptionId` and receives `context` and `currentStateId`. +The callback returns the `subscriptionId` and receives `context`, `event`, `sharedData`, and `currentStateId`. - `unsubscribe`: Remove the subscription with the given `subscriptionId`. diff --git a/src/fsm.ts b/src/fsm.ts index 631583b..59475cf 100644 --- a/src/fsm.ts +++ b/src/fsm.ts @@ -40,7 +40,10 @@ export class StateMachine< #initial: State; #states: Map>; #sharedData: TSharedData; - #subscriptions: Map; + #subscriptions: Map< + SubscriptionIdentifier, + SubscriptionCallback + >; private constructor( states: Array>, @@ -158,6 +161,7 @@ export class StateMachine< context: this.context, currentStateId: this.#current.id, sharedData: this.#sharedData, + event, }); } @@ -212,7 +216,9 @@ export class StateMachine< await this.enter(destination, event); } - public subscribe(callback: SubscriptionCallback): SubscriptionIdentifier { + public subscribe( + callback: SubscriptionCallback, + ): SubscriptionIdentifier { const id = crypto.randomUUID(); this.#subscriptions.set(id, callback); return id; diff --git a/src/state.ts b/src/state.ts index 929df91..0039d90 100644 --- a/src/state.ts +++ b/src/state.ts @@ -3,15 +3,21 @@ export type SubscriptionIdentifier = string; export type SubscriptionCallbackInput< TContext = unknown, + TEvent = unknown, TSharedData = unknown, > = { context: TContext; currentStateId: StateIdentifier; sharedData: TSharedData; + event?: TEvent; }; -export type SubscriptionCallback = ( - callback: SubscriptionCallbackInput, +export type SubscriptionCallback< + TContext = unknown, + TEvent = unknown, + TSharedData = unknown, +> = ( + callback: SubscriptionCallbackInput, ) => void; export type HookInput< diff --git a/test/subscribe.spec.js b/test/subscribe.spec.js new file mode 100644 index 0000000..2454851 --- /dev/null +++ b/test/subscribe.spec.js @@ -0,0 +1,59 @@ +import assert from "node:assert"; +import test from "node:test"; +import { StateMachine } from "../dist/index.js"; + +test("#171 - event is available on 'subscribe' callback", async () => { + const subscriptionEvent = []; + + const states = [ + { + id: "OFF", + initial: true, + transitionTo: () => "ON", + }, + { + id: "ON", + transitionTo: () => "OFF", + }, + ]; + + const context = {}; + const sharedData = {}; + const machine = StateMachine.from(states, { + context, + sharedData, + }); + + machine.subscribe((event) => { + subscriptionEvent.push(event); + }); + + // Start the state machine + await machine.start(); + + const event1 = { name: "foo", value: 1 }; + await machine.send(event1); + const event2 = { name: "foo", value: 2 }; + await machine.send(event2); + + assert.deepStrictEqual(subscriptionEvent, [ + { + context, + currentStateId: "OFF", + event: undefined, + sharedData, + }, + { + context, + currentStateId: "ON", + event: event1, + sharedData, + }, + { + context, + currentStateId: "OFF", + event: event2, + sharedData, + }, + ]); +});