@ue-too/being 0.13.0 → 0.14.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/index.js CHANGED
@@ -1,3 +1,5 @@
1
1
  var w=()=>{};class W{_currentState;_states;_context;_statesArray;_stateChangeCallbacks;_happensCallbacks;_timeouts=void 0;_initialState;constructor(j,z,B,q=!0){if(this._states=j,this._currentState="INITIAL",this._initialState=z,this._context=B,this._statesArray=Object.keys(j),this._stateChangeCallbacks=[],this._happensCallbacks=[],q)this.start()}reset(){this.wrapup(),this.switchTo("INITIAL"),this.start()}start(){if(this.currentState!=="INITIAL")return;this._context.setup(),this.switchTo(this._initialState),this._states[this._initialState].uponEnter(this._context,this,this._initialState)}wrapup(){if(this._currentState==="TERMINAL")return;let j=this._currentState;if(j!=="INITIAL")this._states[j].beforeExit(this._context,this,"TERMINAL");this._context.cleanup(),this.switchTo("TERMINAL")}switchTo(j){this._currentState=j}happens(...j){if(this._timeouts)clearTimeout(this._timeouts);if(this._currentState==="INITIAL"||this._currentState==="TERMINAL")return{handled:!1};this._happensCallbacks.forEach((B)=>B(j,this._context));let z=this._states[this._currentState].handles(j,this._context,this);if(z.handled&&z.nextState!==void 0&&z.nextState!==this._currentState){let B=this._currentState;this._states[this._currentState].beforeExit(this._context,this,z.nextState),this.switchTo(z.nextState),this._states[this._currentState].uponEnter(this._context,this,B);for(let q of this._stateChangeCallbacks)q(B,this._currentState)}return z}onStateChange(j){this._stateChangeCallbacks.push(j)}onHappens(j){this._happensCallbacks.push(j)}get currentState(){return this._currentState}setContext(j){this._context=j}get possibleStates(){return this._statesArray}get states(){return this._states}}class H{_eventReactions={};_guards={};_eventGuards={};_delay=void 0;get handlingEvents(){return Object.keys(this._eventReactions)}get guards(){return this._guards}get eventGuards(){return this._eventGuards}get delay(){return this._delay}uponEnter(j,z,B){}beforeExit(j,z,B){}handles(j,z,B){let q=j[0],J=j[1];if(this._eventReactions[q]){let V=this._eventReactions[q].action(z,J,B),Z=this._eventReactions[q].defaultTargetState,U=this._eventGuards[q],$={handled:!0,nextState:Z},Q=V!==void 0?{...$,output:V}:$;if(U){let X=U.find((Y)=>{if(this._guards[Y.guard])return this._guards[Y.guard](z);return!1});return X?{...Q,nextState:X.target}:Q}return Q}return{handled:!1}}}function P(j){return(z)=>j.includes(z)}function C(j){return j}function I(j,z){if(!j.states.includes(j.initialState))throw Error(`Initial state "${j.initialState}" must be in the states array`);for(let q of j.stateDefinitions){if(!j.states.includes(q.name))throw Error(`State definition "${q.name}" is not in the states array`);for(let J of q.transitions){let V=j.events;if(!(String(J.event)in V))throw Error(`Event "${String(J.event)}" in state "${q.name}" is not defined in events`);if(!j.states.includes(J.targetState))throw Error(`Target state "${J.targetState}" in transition from "${q.name}" is not in the states array`);if(J.guards)for(let Z of J.guards){if(!j.states.includes(Z.targetState))throw Error(`Guard target state "${Z.targetState}" is not in the states array`);if(typeof Z.guard==="string"){if(!q.guards||!(Z.guard in q.guards))throw Error(`Guard "${Z.guard}" referenced in state "${q.name}" for event "${String(J.event)}" is not defined in the state's guards section`)}}}}let B={};for(let q of j.stateDefinitions){let J={},V={},Z={};if(q.guards)for(let[Q,X]of Object.entries(q.guards))V[Q]=X;for(let Q of q.transitions){let X=Q.event;if(J[X]={action:Q.action||w,defaultTargetState:Q.targetState},Q.guards&&Q.guards.length>0){let F=[];Q.guards.forEach((Y,G)=>{let L;if(typeof Y.guard==="string"){if(L=Y.guard,!V[L])throw Error(`Guard "${L}" referenced in state "${q.name}" for event "${String(X)}" is not defined in the state's guards section`)}else L=`guard_${q.name}_${String(X)}_${G}`,V[L]=Y.guard;F.push({guard:L,target:Y.targetState})}),Z[X]=F}}class U extends H{eventReactions=J;_guards=V;_eventGuards=Z}let $=new U;if(q.onEnter){let Q=$.uponEnter.bind($);$.uponEnter=(X,F,Y)=>{Q(X,F,Y),q.onEnter(X,Y)}}if(q.onExit){let Q=$.beforeExit.bind($);$.beforeExit=(X,F,Y)=>{Q(X,F,Y),q.onExit(X,Y)}}B[q.name]=$}for(let q of j.states)if(!B[q]){class J extends H{eventReactions={}}B[q]=new J}return new W(B,j.initialState,z,!0)}class _ extends H{_childStateMachineConfig=null;_historyState=null;_context=null;getCurrentChildState(){if(!this._childStateMachineConfig)return null;let z=this._childStateMachineConfig.stateMachine.currentState;return z==="INITIAL"||z==="TERMINAL"?null:z}getStatePath(j){let z=this.getCurrentChildState();if(z===null)return j;return`${j}.${z}`}uponEnter(j,z,B){super.uponEnter(j,z,B),this._context=j;let q=this.getChildStateMachine();this._childStateMachineConfig=q;let J=q.rememberHistory&&this._historyState?this._historyState:q.defaultChildState,V=q.stateMachine;if(V.currentState==="INITIAL")V.start();V.switchTo(J),V.states[J].uponEnter(j,V,"INITIAL")}beforeExit(j,z,B){if(this._childStateMachineConfig?.rememberHistory){let q=this.getCurrentChildState();if(q!==null)this._historyState=q}if(this._childStateMachineConfig){let q=this.getCurrentChildState();if(q!==null)this._childStateMachineConfig.stateMachine.states[q].beforeExit(j,this._childStateMachineConfig.stateMachine,"TERMINAL");this._childStateMachineConfig.stateMachine.wrapup()}super.beforeExit(j,z,B)}handles(j,z,B){if(this._childStateMachineConfig){if(this._childStateMachineConfig.stateMachine.happens(...j).handled)return{handled:!0}}return super.handles(j,z,B)}}class O extends W{getCurrentStatePath(){let j=this.currentState;if(j==="INITIAL"||j==="TERMINAL")return j;let z=this.states[j];if(z instanceof _)return z.getStatePath(j);return j}getActiveStatePath(){let j=this.currentState;if(j==="INITIAL"||j==="TERMINAL")return[j];let z=[j],B=this.states[j];if(B instanceof _){let q=B.getCurrentChildState();if(q!==null)z.push(q)}return z}isInStatePath(j){let z=this.getCurrentStatePath();return z===j||z.startsWith(j+".")}}export{C as createStateMachineSchemaWithInferredStates,I as createStateMachineFromSchema,P as createStateGuard,W as TemplateStateMachine,H as TemplateState,w as NO_OP,O as HierarchicalStateMachine,_ as CompositeState};
2
2
 
3
3
  //# debugId=A4A1425C023A074164756E2164756E21
4
+
5
+ //# sourceMappingURL=index.js.map
package/index.js.map CHANGED
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/interface.ts", "../src/schema-factory.ts", "../src/hierarchical.ts"],
3
+ "sources": [
4
+ "interface.ts",
5
+ "schema-factory.ts",
6
+ "hierarchical.ts"
7
+ ],
4
8
  "sourcesContent": [
5
9
  "/**\n * Base context interface for state machines.\n *\n * @remarks\n * The context is shared across all states in a state machine and can be used to store data\n * that persists between state transitions. All custom contexts must extend this interface.\n *\n * The setup and cleanup methods provide lifecycle hooks for resource management:\n * - `setup()`: Called when the context is initialized\n * - `cleanup()`: Called when the context is destroyed\n *\n * @example\n * ```typescript\n * interface MyContext extends BaseContext {\n * counter: number;\n * data: string[];\n * setup() {\n * this.counter = 0;\n * this.data = [];\n * }\n * cleanup() {\n * this.data = [];\n * }\n * }\n * ```\n *\n * @category Core\n */\nexport interface BaseContext {\n setup(): void;\n cleanup(): void;\n}\n\ntype NOOP = () => void;\n\n/**\n * Utility type to check if an object type is empty.\n * @internal\n */\ntype IsEmptyObject<T> = T extends {} ? {} extends T ? true : false : false;\n\n/**\n * Utility type to derive a string literal union from a readonly array of string literals.\n *\n * @remarks\n * This helper type extracts the element types from a readonly array to create a union type.\n * Useful for defining state machine states from an array.\n *\n * @example\n * ```typescript\n * const TEST_STATES = [\"one\", \"two\", \"three\"] as const;\n * type TestStates = CreateStateType<typeof TEST_STATES>; // \"one\" | \"two\" | \"three\"\n * ```\n *\n * @category Utilities\n */\nexport type CreateStateType<ArrayLiteral extends readonly string[]> = ArrayLiteral[number];\n\n/**\n * Type for event arguments with conditional payload requirement.\n *\n * @remarks\n * This utility type determines whether an event requires a payload argument based on the\n * event payload mapping. If the payload is an empty object, no payload is required.\n *\n * @typeParam EventPayloadMapping - Mapping of event names to their payload types\n * @typeParam K - The event key\n *\n * @category Utilities\n */\nexport type EventArgs<EventPayloadMapping, K> =\n K extends keyof EventPayloadMapping\n ? IsEmptyObject<EventPayloadMapping[K]> extends true\n ? [event: K] // No payload needed\n : [event: K, payload: EventPayloadMapping[K]] // Payload required\n : [event: K, payload?: unknown]; // Unknown events\n\n/**\n * No-operation function constant used as a placeholder for optional actions.\n *\n * @remarks\n * Use this when you need to provide a function but don't want it to do anything,\n * such as for default state transition actions that have no side effects.\n *\n * @category Core\n */\nexport const NO_OP: NOOP = ()=>{};\n\n/**\n * Result type indicating an event was not handled by the current state.\n *\n * @remarks\n * When a state doesn't have a handler defined for a particular event, it returns this type.\n * The state machine will not transition and the event is effectively ignored.\n *\n * @category Core\n */\nexport type EventNotHandled = {\n handled: false;\n}\n\n/**\n * Helper type that conditionally includes the output property.\n * @internal\n */\ntype WithOutput<Output> = Output extends void ? {} : { output?: Output };\n\n/**\n * Result type when an event is successfully handled by a state.\n *\n * @remarks\n * This type represents a successful event handling result. It can optionally include:\n * - `nextState`: The state to transition to (if different from current)\n * - `output`: A return value from the event handler (only present when Output is not void)\n *\n * @typeParam States - Union of all possible state names in the state machine\n * @typeParam Output - The output type for this event (defaults to void)\n *\n * @example\n * ```typescript\n * // Simple transition without output\n * const result: EventHandled<\"IDLE\" | \"ACTIVE\"> = {\n * handled: true,\n * nextState: \"ACTIVE\"\n * // output property does not exist when Output is void\n * };\n *\n * // With output value\n * const resultWithOutput: EventHandled<\"IDLE\" | \"ACTIVE\", number> = {\n * handled: true,\n * nextState: \"IDLE\",\n * output: 42\n * };\n * ```\n *\n * @category Core\n */\nexport type EventHandled<States extends string, Output = void> = {\n handled: true;\n nextState?: States;\n} & WithOutput<Output>;\n\n/**\n * Discriminated union representing the result of event handling.\n *\n * @remarks\n * Every event handler returns an EventResult, which is either:\n * - {@link EventHandled}: The event was processed successfully\n * - {@link EventNotHandled}: The event was not recognized/handled\n *\n * Use the `handled` discriminant to narrow the type in TypeScript.\n *\n * @typeParam States - Union of all possible state names\n * @typeParam Output - The output type for handled events\n *\n * @category Core\n */\nexport type EventResult<States extends string, Output = void> = EventNotHandled | EventHandled<States, Output>;\n\n/**\n * @description A default output mapping that maps all events to void.\n * Used as default when no output mapping is provided.\n * \n * @category Types\n */\nexport type DefaultOutputMapping<EventPayloadMapping> = {\n [K in keyof EventPayloadMapping]: void;\n};\n\n/**\n * @description This is the interface for the state machine. The interface takes in a few generic parameters.\n * \n * Generic parameters:\n * - EventPayloadMapping: A mapping of events to their payloads.\n * - Context: The context of the state machine. (which can be used by each state to do calculations that would persist across states)\n * - States: All of the possible states that the state machine can be in. e.g. a string literal union like \"IDLE\" | \"SELECTING\" | \"PAN\" | \"ZOOM\"\n * - EventOutputMapping: A mapping of events to their output types. Defaults to void for all events.\n * \n * You can probably get by using the TemplateStateMachine class.\n * The naming is that an event would \"happen\" and the state of the state machine would \"handle\" it.\n *\n * @see {@link TemplateStateMachine}\n * @see {@link KmtInputStateMachine}\n * \n * @category Types\n */\nexport interface StateMachine<\n EventPayloadMapping, \n Context extends BaseContext, \n States extends string = 'IDLE',\n EventOutputMapping extends Partial<Record<keyof EventPayloadMapping, unknown>> = DefaultOutputMapping<EventPayloadMapping>\n> {\n switchTo(state: States): void;\n // Overload for known events - provides IntelliSense with typed output\n happens<K extends keyof EventPayloadMapping>(\n ...args: EventArgs<EventPayloadMapping, K>\n ): EventResult<States, K extends keyof EventOutputMapping ? EventOutputMapping[K] : void>;\n // Overload for unknown events - maintains backward compatibility\n happens<K extends string>(\n ...args: EventArgs<EventPayloadMapping, K>\n ): EventResult<States, unknown>;\n setContext(context: Context): void;\n states: Record<States, State<EventPayloadMapping, Context, string extends States ? string : States, EventOutputMapping>>;\n onStateChange(callback: StateChangeCallback<States>): void;\n possibleStates: States[];\n onHappens(callback: (args: EventArgs<EventPayloadMapping, keyof EventPayloadMapping | string>, context: Context) => void): void;\n reset(): void;\n start(): void;\n wrapup(): void;\n}\n\n/**\n * @description This is the type for the callback that is called when the state changes.\n *\n * @category Types\n */\nexport type StateChangeCallback<States extends string = 'IDLE'> = (currentState: States, nextState: States) => void;\n\n/**\n * @description This is the interface for the state. The interface takes in a few generic parameters:\n * You can probably get by extending the TemplateState class. \n *\n * Generic parameters:\n * - EventPayloadMapping: A mapping of events to their payloads.\n * - Context: The context of the state machine. (which can be used by each state to do calculations that would persist across states)\n * - States: All of the possible states that the state machine can be in. e.g. a string literal union like \"IDLE\" | \"SELECTING\" | \"PAN\" | \"ZOOM\"\n * - EventOutputMapping: A mapping of events to their output types. Defaults to void for all events.\n * \n * A state's all possible states can be only a subset of the possible states of the state machine. (a state only needs to know what states it can transition to)\n * This allows for a state to be reusable across different state machines.\n *\n * @see {@link TemplateState}\n * \n * @category Types\n */\nexport interface State<\n EventPayloadMapping, \n Context extends BaseContext, \n States extends string = 'IDLE',\n EventOutputMapping extends Partial<Record<keyof EventPayloadMapping, unknown>> = DefaultOutputMapping<EventPayloadMapping>\n> { \n uponEnter(context: Context, stateMachine: StateMachine<EventPayloadMapping, Context, States, EventOutputMapping>, from: States | \"INITIAL\"): void;\n beforeExit(context: Context, stateMachine: StateMachine<EventPayloadMapping, Context, States, EventOutputMapping>, to: States | \"TERMINAL\"): void;\n handles<K extends (keyof EventPayloadMapping | string)>(args: EventArgs<EventPayloadMapping, K>, context: Context, stateMachine: StateMachine<EventPayloadMapping, Context, States, EventOutputMapping>): EventResult<States, K extends keyof EventOutputMapping ? EventOutputMapping[K] : void>;\n // eventReactions: EventReactions<EventPayloadMapping, Context, States, EventOutputMapping>;\n guards: Guard<Context>;\n eventGuards: Partial<EventGuards<EventPayloadMapping, States, Context, Guard<Context>>>;\n delay: Delay<Context, EventPayloadMapping, States, EventOutputMapping> | undefined;\n}\n\n/**\n * @description This is the type for the event reactions of a state.\n * \n * Generic parameters:\n * - EventPayloadMapping: A mapping of events to their payloads.\n * - Context: The context of the state machine. (which can be used by each state to do calculations that would persist across states)\n * - States: All of the possible states that the state machine can be in. e.g. a string literal union like \"IDLE\" | \"SELECTING\" | \"PAN\" | \"ZOOM\"\n * - EventOutputMapping: A mapping of events to their output types. Defaults to void for all events.\n * \n * The action function can now return an output value that will be included in the EventHandledResult.\n * \n * @category Types\n */\nexport type EventReactions<\n EventPayloadMapping, \n Context extends BaseContext, \n States extends string,\n EventOutputMapping extends Partial<Record<keyof EventPayloadMapping, unknown>> = DefaultOutputMapping<EventPayloadMapping>\n> = {\n [K in keyof Partial<EventPayloadMapping>]: { \n action: (\n context: Context, \n event: EventPayloadMapping[K], \n stateMachine: StateMachine<EventPayloadMapping, Context, States, EventOutputMapping>\n ) => K extends keyof EventOutputMapping ? (EventOutputMapping[K] | void) : void;\n defaultTargetState?: States;\n };\n};\n\n/**\n * @description This is the type for the guard evaluation when a state transition is happening.\n * \n * Guard evaluations are evaluated after the state has handled the event with the action.\n * Guard evaluations can be defined in an array and the first guard that evaluates to true will be used to determine the next state.\n * \n * Generic parameters:\n * - Context: The context of the state machine. (which can be used by each state to do calculations that would persist across states)\n * \n * @category Types\n */\nexport type GuardEvaluation<Context extends BaseContext> = (context: Context) => boolean;\n\n/**\n * @description This is the type for the guard of a state.\n * \n * guard is an object that maps a key to a guard evaluation.\n * K is all the possible keys that can be used to evaluate the guard.\n * K is optional but if it is not provided, typescript won't be able to type guard in the EventGuards type.\n * \n * @category Types\n */\nexport type Guard<Context extends BaseContext, K extends string = string> = {\n [P in K]: GuardEvaluation<Context>;\n}\n\nexport type Action<\n Context extends BaseContext, \n EventPayloadMapping, \n States extends string,\n EventOutputMapping extends Partial<Record<keyof EventPayloadMapping, unknown>> = DefaultOutputMapping<EventPayloadMapping>,\n Output = void\n> = {\n action: (context: Context, event: EventPayloadMapping[keyof EventPayloadMapping], stateMachine: StateMachine<EventPayloadMapping, Context, States, EventOutputMapping>) => Output | void;\n defaultTargetState?: States;\n}\n\nexport type Delay<\n Context extends BaseContext, \n EventPayloadMapping, \n States extends string,\n EventOutputMapping extends Partial<Record<keyof EventPayloadMapping, unknown>> = DefaultOutputMapping<EventPayloadMapping>\n> = {\n time: number;\n action: Action<Context, EventPayloadMapping, States, EventOutputMapping>;\n}\n\n/**\n * @description This is a mapping of a guard to a target state.\n * \n * Generic parameters:\n * - Context: The context of the state machine. (which can be used by each state to do calculations that would persist across states)\n * - G: The guard type.\n * - States: All of the possible states that the state machine can be in. e.g. a string literal union like \"IDLE\" | \"SELECTING\" | \"PAN\" | \"ZOOM\"\n * \n * You probably don't need to use this type directly.\n * \n * @see {@link TemplateState['eventGuards']}\n * \n * @category Types\n */\nexport type GuardMapping<Context extends BaseContext, G, States extends string> = {\n guard: G extends Guard<Context, infer K> ? K : never;\n target: States;\n}\n\n/**\n * @description This is a mapping of an event to a guard evaluation.\n * \n * Generic parameters:\n * - EventPayloadMapping: A mapping of events to their payloads.\n * - States: All of the possible states that the state machine can be in. e.g. a string literal union like \"IDLE\" | \"SELECTING\" | \"PAN\" | \"ZOOM\"\n * - Context: The context of the state machine. (which can be used by each state to do calculations that would persist across states)\n * - T: The guard type.\n * \n * You probably don't need to use this type directly.\n * This is a mapping of an event to a guard evaluation.\n * \n * @see {@link TemplateState['eventGuards']}\n * \n * @category Types\n */\nexport type EventGuards<EventPayloadMapping, States extends string, Context extends BaseContext, T extends Guard<Context>> = {\n [K in keyof EventPayloadMapping]: GuardMapping<Context, T, States>[];\n}\n\n/**\n * Concrete implementation of a finite state machine.\n *\n * @remarks\n * This class provides a complete, ready-to-use state machine implementation. It's generic enough\n * to handle most use cases without requiring custom extensions.\n *\n * ## Features\n *\n * - **Type-safe events**: Events and their payloads are fully typed via the EventPayloadMapping\n * - **State transitions**: Automatic state transitions based on event handlers\n * - **Event outputs**: Handlers can return values that are included in the result\n * - **Lifecycle hooks**: States can define `uponEnter` and `beforeExit` callbacks\n * - **State change listeners**: Subscribe to state transitions\n * - **Shared context**: All states access the same context object for persistent data\n *\n * ## Usage Pattern\n *\n * 1. Define your event payload mapping type\n * 2. Define your states as a string union type\n * 3. Create state classes extending {@link TemplateState}\n * 4. Instantiate TemplateStateMachine with your states and initial state\n *\n * @typeParam EventPayloadMapping - Object mapping event names to their payload types\n * @typeParam Context - Context type shared across all states\n * @typeParam States - Union of all possible state names (string literals)\n * @typeParam EventOutputMapping - Optional mapping of events to their output types\n *\n * @example\n * Basic vending machine state machine\n * ```typescript\n * type Events = {\n * insertCoin: { amount: number };\n * selectItem: { itemId: string };\n * cancel: {};\n * };\n *\n * type States = \"IDLE\" | \"PAYMENT\" | \"DISPENSING\";\n *\n * interface VendingContext extends BaseContext {\n * balance: number;\n * setup() { this.balance = 0; }\n * cleanup() {}\n * }\n *\n * const context: VendingContext = {\n * balance: 0,\n * setup() { this.balance = 0; },\n * cleanup() {}\n * };\n *\n * const machine = new TemplateStateMachine<Events, VendingContext, States>(\n * {\n * IDLE: new IdleState(),\n * PAYMENT: new PaymentState(),\n * DISPENSING: new DispensingState()\n * },\n * \"IDLE\",\n * context\n * );\n *\n * // Trigger events\n * machine.happens(\"insertCoin\", { amount: 100 });\n * machine.happens(\"selectItem\", { itemId: \"A1\" });\n * ```\n *\n * @category State Machine Core\n * @see {@link TemplateState} for creating state implementations\n * @see {@link StateMachine} for the interface definition\n */\nexport class TemplateStateMachine<\n EventPayloadMapping, \n Context extends BaseContext, \n States extends string = 'IDLE',\n EventOutputMapping extends Partial<Record<keyof EventPayloadMapping, unknown>> = DefaultOutputMapping<EventPayloadMapping>\n> implements StateMachine<EventPayloadMapping, Context, States, EventOutputMapping> {\n\n protected _currentState: States | \"INITIAL\" | \"TERMINAL\";\n protected _states: Record<States, State<EventPayloadMapping, Context, States, EventOutputMapping>>;\n protected _context: Context;\n protected _statesArray: States[];\n protected _stateChangeCallbacks: StateChangeCallback<States>[];\n protected _happensCallbacks: ((args: EventArgs<EventPayloadMapping, keyof EventPayloadMapping | string>, context: Context) => void)[];\n protected _timeouts: ReturnType<typeof setTimeout> | undefined = undefined;\n protected _initialState: States;\n\n constructor(states: Record<States, State<EventPayloadMapping, Context, States, EventOutputMapping>>, initialState: States, context: Context, autoStart: boolean = true){\n this._states = states;\n this._currentState = \"INITIAL\";\n this._initialState = initialState;\n this._context = context;\n this._statesArray = Object.keys(states) as States[];\n this._stateChangeCallbacks = [];\n this._happensCallbacks = [];\n if(autoStart){\n this.start();\n }\n }\n\n reset(): void {\n this.wrapup();\n this.switchTo(\"INITIAL\");\n this.start();\n }\n\n start(): void {\n if(this.currentState !== \"INITIAL\"){\n return;\n }\n this._context.setup();\n this.switchTo(this._initialState);\n this._states[this._initialState].uponEnter(this._context, this, this._initialState);\n }\n\n wrapup(): void {\n if(this._currentState === \"TERMINAL\") {\n return;\n }\n const originalState = this._currentState;\n if(originalState !== \"INITIAL\") {\n this._states[originalState].beforeExit(this._context, this, \"TERMINAL\");\n }\n this._context.cleanup();\n this.switchTo(\"TERMINAL\");\n }\n\n switchTo(state: States | \"INITIAL\" | \"TERMINAL\"): void {\n this._currentState = state;\n }\n \n // Implementation signature - matches both overloads\n happens<K extends keyof EventPayloadMapping>(...args: EventArgs<EventPayloadMapping, K>): EventResult<States, K extends keyof EventOutputMapping ? EventOutputMapping[K] : void>;\n happens<K extends string>(...args: EventArgs<EventPayloadMapping, K>): EventResult<States, unknown>;\n happens<K extends keyof EventPayloadMapping | string>(...args: EventArgs<EventPayloadMapping, K>): EventResult<States, unknown> {\n if(this._timeouts){\n clearTimeout(this._timeouts);\n }\n if(this._currentState === \"INITIAL\" || this._currentState === \"TERMINAL\"){\n return { handled: false };\n }\n this._happensCallbacks.forEach(callback => callback(args, this._context));\n const result = this._states[this._currentState].handles(args, this._context, this);\n if(result.handled && result.nextState !== undefined && result.nextState !== this._currentState){ // TODO: whether or not to transition to the same state (currently no, uponEnter and beforeExit will not be called if the state is the same)\n const originalState = this._currentState;\n this._states[this._currentState].beforeExit(this._context, this, result.nextState);\n this.switchTo(result.nextState);\n this._states[this._currentState].uponEnter(this._context, this, originalState);\n for(const callback of this._stateChangeCallbacks){\n callback(originalState, this._currentState);\n }\n }\n return result;\n }\n\n onStateChange(callback: StateChangeCallback<States>): void {\n this._stateChangeCallbacks.push(callback);\n }\n\n onHappens(callback: (args: EventArgs<EventPayloadMapping, keyof EventPayloadMapping | string>, context: Context) => void): void {\n this._happensCallbacks.push(callback);\n }\n\n get currentState(): States | \"INITIAL\" | \"TERMINAL\" {\n return this._currentState;\n }\n\n setContext(context: Context): void {\n this._context = context;\n }\n\n get possibleStates(): States[] {\n return this._statesArray;\n }\n\n get states(): Record<States, State<EventPayloadMapping, Context, States, EventOutputMapping>> {\n return this._states;\n }\n}\n/**\n * Abstract base class for state machine states.\n *\n * @remarks\n * This abstract class provides the foundation for implementing individual states in a state machine.\n * Each state defines how it responds to events through the `eventReactions` object.\n *\n * ## Key Concepts\n *\n * - **Event Reactions**: Define handlers for events this state cares about. Unhandled events are ignored.\n * - **Guards**: Conditional logic that determines which state to transition to based on context\n * - **Lifecycle Hooks**: `uponEnter` and `beforeExit` callbacks for state transition side effects\n * - **Selective Handling**: Only define reactions for events relevant to this state\n *\n * ## Implementation Pattern\n *\n * 1. Extend this class for each state in your state machine\n * 2. Implement the `eventReactions` property with handlers for relevant events\n * 3. Optionally override `uponEnter` and `beforeExit` for lifecycle logic\n * 4. Optionally define `guards` and `eventGuards` for conditional transitions\n *\n * @typeParam EventPayloadMapping - Object mapping event names to their payload types\n * @typeParam Context - Context type shared across all states\n * @typeParam States - Union of all possible state names (string literals)\n * @typeParam EventOutputMapping - Optional mapping of events to their output types\n *\n * @example\n * Simple state implementation\n * ```typescript\n * class IdleState extends TemplateState<MyEvents, MyContext, MyStates> {\n * eventReactions = {\n * start: {\n * action: (context, event) => {\n * console.log('Starting...');\n * context.startTime = Date.now();\n * },\n * defaultTargetState: \"ACTIVE\"\n * },\n * reset: {\n * action: (context, event) => {\n * context.counter = 0;\n * }\n * // No state transition - stays in IDLE\n * }\n * };\n *\n * uponEnter(context, stateMachine, fromState) {\n * console.log(`Entered IDLE from ${fromState}`);\n * }\n * }\n * ```\n *\n * @example\n * State with guards for conditional transitions\n * ```typescript\n * class PaymentState extends TemplateState<Events, VendingContext, States> {\n * guards = {\n * hasEnoughMoney: (context) => context.balance >= context.itemPrice,\n * needsChange: (context) => context.balance > context.itemPrice\n * };\n *\n * eventReactions = {\n * selectItem: {\n * action: (context, event) => {\n * context.selectedItem = event.itemId;\n * context.itemPrice = getPrice(event.itemId);\n * },\n * defaultTargetState: \"IDLE\" // Fallback if no guard matches\n * }\n * };\n *\n * eventGuards = {\n * selectItem: [\n * { guard: 'hasEnoughMoney', target: 'DISPENSING' },\n * // If hasEnoughMoney is false, uses defaultTargetState (IDLE)\n * ]\n * };\n * }\n * ```\n *\n * @category State Machine Core\n * @see {@link TemplateStateMachine} for the state machine implementation\n * @see {@link EventReactions} for defining event handlers\n */\nexport abstract class TemplateState<\n EventPayloadMapping, \n Context extends BaseContext, \n States extends string = 'IDLE',\n EventOutputMapping extends Partial<Record<keyof EventPayloadMapping, unknown>> = DefaultOutputMapping<EventPayloadMapping>\n> implements State<EventPayloadMapping, Context, States, EventOutputMapping> {\n\n protected _eventReactions: EventReactions<EventPayloadMapping, Context, States, EventOutputMapping> = {} as EventReactions<EventPayloadMapping, Context, States, EventOutputMapping>;\n protected _guards: Guard<Context> = {} as Guard<Context>;\n protected _eventGuards: Partial<EventGuards<EventPayloadMapping, States, Context, Guard<Context>>> = {} as Partial<EventGuards<EventPayloadMapping, States, Context, Guard<Context>>>;\n protected _delay: Delay<Context, EventPayloadMapping, States, EventOutputMapping> | undefined = undefined;\n\n get handlingEvents(): (keyof EventPayloadMapping)[] {\n return Object.keys(this._eventReactions) as (keyof EventPayloadMapping)[];\n }\n\n get guards(): Guard<Context> {\n return this._guards;\n }\n\n get eventGuards(): Partial<EventGuards<EventPayloadMapping, States, Context, Guard<Context>>> {\n return this._eventGuards;\n }\n\n get delay(): Delay<Context, EventPayloadMapping, States, EventOutputMapping> | undefined {\n return this._delay;\n }\n\n uponEnter(context: Context, stateMachine: StateMachine<EventPayloadMapping, Context, States, EventOutputMapping>, from: States | \"INITIAL\"): void {\n // console.log(\"enter\");\n }\n\n beforeExit(context: Context, stateMachine: StateMachine<EventPayloadMapping, Context, States, EventOutputMapping>, to: States | \"TERMINAL\"): void {\n // console.log('leave');\n }\n\n handles<K extends (keyof EventPayloadMapping | string)>(args: EventArgs<EventPayloadMapping, K>, context: Context, stateMachine: StateMachine<EventPayloadMapping, Context, States, EventOutputMapping>): EventResult<States, K extends keyof EventOutputMapping ? EventOutputMapping[K] : void>{\n const eventKey = args[0] as keyof EventPayloadMapping;\n const eventPayload = args[1] as EventPayloadMapping[keyof EventPayloadMapping];\n if (this._eventReactions[eventKey]) {\n // Capture the output from the action\n const output = this._eventReactions[eventKey].action(context, eventPayload, stateMachine);\n const targetState = this._eventReactions[eventKey].defaultTargetState;\n const guardsToEvaluate = this._eventGuards[eventKey];\n const baseResult = { handled: true as const, nextState: targetState };\n const resultWithOutput = output !== undefined \n ? { ...baseResult, output } \n : baseResult;\n \n if(guardsToEvaluate){\n const target = guardsToEvaluate.find((guard)=>{\n if(this._guards[guard.guard]){\n return this._guards[guard.guard](context);\n }\n return false;\n });\n const finalResult = target \n ? { ...resultWithOutput, nextState: target.target }\n : resultWithOutput;\n return finalResult as EventResult<States, K extends keyof EventOutputMapping ? EventOutputMapping[K] : void>;\n }\n return resultWithOutput as EventResult<States, K extends keyof EventOutputMapping ? EventOutputMapping[K] : void>;\n }\n return {handled: false};\n }\n}\n\n\n/**\n * Creates a type guard function for checking if a value belongs to a specific set of states.\n *\n * @remarks\n * This utility function generates a TypeScript type guard that narrows a string type\n * to a specific union of string literals. Useful when you have multiple state types\n * and need to distinguish between them at runtime.\n *\n * @typeParam T - String literal type to guard for\n * @param set - Readonly array of string literals defining the valid states\n * @returns A type guard function that checks if a string is in the set\n *\n * @example\n * Creating state guards for hierarchical state machines\n * ```typescript\n * type MainStates = \"idle\" | \"active\" | \"paused\";\n * type SubStates = \"loading\" | \"processing\" | \"complete\";\n * type AllStates = MainStates | SubStates;\n *\n * const MAIN_STATES = [\"idle\", \"active\", \"paused\"] as const;\n * const isMainState = createStateGuard(MAIN_STATES);\n *\n * function handleState(state: AllStates) {\n * if (isMainState(state)) {\n * // TypeScript knows state is MainStates here\n * console.log('Main state:', state);\n * } else {\n * // TypeScript knows state is SubStates here\n * console.log('Sub state:', state);\n * }\n * }\n * ```\n *\n * @category Utilities\n */\nexport function createStateGuard<T extends string>(set: readonly T[]) {\n return (s: string): s is T => set.includes(s as T);\n}\n",
6
10
  "/**\n * Schema-based state machine factory for runtime creation.\n *\n * @remarks\n * This module provides utilities for creating state machines from JSON-like schemas,\n * enabling dynamic state machine creation at runtime. This is useful for GUI builders\n * or configuration-driven state machines.\n *\n * @category Runtime Factory\n */\n\nimport {\n BaseContext,\n TemplateState,\n TemplateStateMachine,\n StateMachine,\n EventResult,\n NO_OP,\n DefaultOutputMapping,\n} from \"./interface\";\n\n/**\n * Payload type definition for an event in the schema.\n * Can be an empty object (no payload) or an object with typed fields.\n */\nexport type EventPayloadSchema = Record<string, string> | {};\n\n/**\n * Action function that can be executed when an event is handled.\n * Receives context, event payload, and the state machine instance.\n * Can return a value that will be included in the event result.\n * \n * @typeParam Context - The context type\n * @typeParam EventPayloadMapping - Mapping of event names to their payload types\n * @typeParam EventName - The specific event name this action handles\n * @typeParam EventOutputMapping - Optional mapping of events to their output types\n */\nexport type ActionFunction<\n Context extends BaseContext = BaseContext,\n EventPayloadMapping = any,\n EventName extends keyof EventPayloadMapping = keyof EventPayloadMapping,\n EventOutputMapping extends Partial<Record<keyof EventPayloadMapping, unknown>> = DefaultOutputMapping<EventPayloadMapping>\n> = (\n context: Context,\n payload: EventPayloadMapping[EventName],\n stateMachine: StateMachine<EventPayloadMapping, Context, any, EventOutputMapping>\n) => EventName extends keyof EventOutputMapping ? (EventOutputMapping[EventName] | void) : void | unknown;\n\n/**\n * Guard function that evaluates whether a transition should occur.\n * Returns true if the guard condition is met.\n */\nexport type GuardFunction<Context extends BaseContext = BaseContext> = (\n context: Context\n) => boolean;\n\n/**\n * Definition of a single state transition.\n * \n * @typeParam Context - The context type\n * @typeParam EventPayloadMapping - Mapping of event names to their payload types\n * @typeParam EventName - The specific event name for this transition\n * @typeParam StateNames - Union type of all valid state names\n * @typeParam EventOutputMapping - Optional mapping of events to their output types\n */\nexport interface TransitionDefinition<\n Context extends BaseContext = BaseContext,\n EventPayloadMapping = any,\n EventName extends keyof EventPayloadMapping = keyof EventPayloadMapping,\n StateNames extends string = string,\n EventOutputMapping extends Partial<Record<keyof EventPayloadMapping, unknown>> = DefaultOutputMapping<EventPayloadMapping>\n> {\n /** The event that triggers this transition */\n event: EventName;\n /** The target state after this transition */\n targetState: StateNames;\n /** Optional action to execute when this transition occurs. Can return a value that will be included in the event result. */\n action?: ActionFunction<Context, EventPayloadMapping, EventName, EventOutputMapping>;\n /** \n * Optional guard conditions (evaluated in order, first true guard wins).\n * Guards can be either:\n * - A guard function defined inline\n * - A string reference to a guard defined in the state's `guards` section\n */\n guards?: Array<{\n /** Guard function to evaluate, or name of a guard defined in the state's guards section */\n guard: GuardFunction<Context> | string;\n /** Target state if this guard evaluates to true */\n targetState: StateNames;\n }>;\n}\n\n/**\n * Union type of all possible transition definitions for a given event payload mapping.\n * This ensures each transition's action payload is typed based on its specific event.\n * \n * @typeParam Context - The context type\n * @typeParam EventPayloadMapping - Mapping of event names to their payload types\n * @typeParam StateNames - Union type of all valid state names\n * @typeParam EventOutputMapping - Optional mapping of events to their output types\n */\nexport type TransitionDefinitionUnion<\n Context extends BaseContext = BaseContext,\n EventPayloadMapping = any,\n StateNames extends string = string,\n EventOutputMapping extends Partial<Record<keyof EventPayloadMapping, unknown>> = DefaultOutputMapping<EventPayloadMapping>\n> = {\n [K in keyof EventPayloadMapping]: TransitionDefinition<Context, EventPayloadMapping, K, StateNames, EventOutputMapping>\n}[keyof EventPayloadMapping];\n\n/**\n * Definition of a single state in the state machine.\n * \n * @typeParam Context - The context type\n * @typeParam EventPayloadMapping - Mapping of event names to their payload types\n * @typeParam StateNames - Union type of all valid state names\n * @typeParam EventOutputMapping - Optional mapping of events to their output types\n * \n * @example\n * ```typescript\n * {\n * name: \"PAYING\",\n * guards: {\n * hasEnoughBalance: (context) => context.balance >= context.itemPrice,\n * hasInsufficientBalance: (context) => context.balance < context.itemPrice,\n * },\n * transitions: [\n * {\n * event: \"pay\",\n * targetState: \"SELECTING\",\n * guards: [\n * { guard: \"hasEnoughBalance\", targetState: \"CONFIRMED\" },\n * { guard: \"hasInsufficientBalance\", targetState: \"PAYING\" },\n * ],\n * },\n * ],\n * }\n * ```\n */\nexport interface StateDefinition<\n Context extends BaseContext = BaseContext,\n EventPayloadMapping = any,\n StateNames extends string = string,\n EventOutputMapping extends Partial<Record<keyof EventPayloadMapping, unknown>> = DefaultOutputMapping<EventPayloadMapping>\n> {\n /** Name of this state */\n name: StateNames;\n /** Transitions available from this state */\n transitions: TransitionDefinitionUnion<Context, EventPayloadMapping, StateNames, EventOutputMapping>[];\n /** \n * Optional mapping of guard names to guard functions.\n * Guards defined here can be reused across multiple transitions within this state\n * by referencing them by name in the transition's guards array.\n * \n * The guard functions receive the context parameter typed with the Context type parameter,\n * not BaseContext, so you get full type safety for your context properties.\n */\n guards?: Record<string, GuardFunction<Context>>;\n /** Optional callback when entering this state */\n onEnter?: (context: Context, fromState: StateNames) => void;\n /** Optional callback when exiting this state */\n onExit?: (context: Context, toState: StateNames) => void;\n}\n\n/**\n * Helper type to extract state names from a states array.\n * If the array is a readonly tuple of string literals, it extracts the union type.\n * Otherwise, it falls back to string.\n * \n * @typeParam StatesArray - The states array type\n */\nexport type ExtractStateNames<StatesArray> = \n StatesArray extends readonly (infer S)[]\n ? S extends string\n ? S\n : string\n : StatesArray extends (infer S)[]\n ? S extends string\n ? S\n : string\n : string;\n\n/**\n * Helper type to infer StateNames from a StateMachineSchema.\n * Extracts the state names from the schema's states array.\n */\ntype InferStateNamesFromSchema<Schema> = \n Schema extends StateMachineSchema<any, any, infer SN, any>\n ? SN\n : Schema extends { states: infer S }\n ? ExtractStateNames<S>\n : string;\n\n/**\n * Helper function to create a typed state machine schema with inferred state names.\n * Use this when you have a const states array and want TypeScript to infer the state names.\n * \n * @example\n * ```typescript\n * const states = [\"IDLE\", \"RUNNING\", \"PAUSED\"] as const;\n * const schema = createStateMachineSchemaWithInferredStates<TimerContext, TimerEvents>({\n * states,\n * events: { start: {}, stop: {} },\n * initialState: \"IDLE\",\n * stateDefinitions: [\n * {\n * name: \"IDLE\", // TypeScript knows this must be one of the states\n * transitions: [\n * {\n * event: \"start\",\n * targetState: \"RUNNING\", // TypeScript knows this must be one of the states\n * }\n * ]\n * }\n * ]\n * });\n * ```\n */\nexport function createStateMachineSchemaWithInferredStates<\n Context extends BaseContext,\n EventPayloadMapping,\n States extends readonly string[],\n EventOutputMapping extends Partial<Record<keyof EventPayloadMapping, unknown>> = DefaultOutputMapping<EventPayloadMapping>\n>(\n schema: Omit<StateMachineSchema<Context, EventPayloadMapping, ExtractStateNames<States>, EventOutputMapping>, 'states'> & {\n states: States;\n }\n): StateMachineSchema<Context, EventPayloadMapping, ExtractStateNames<States>, EventOutputMapping> {\n return schema as unknown as StateMachineSchema<Context, EventPayloadMapping, ExtractStateNames<States>, EventOutputMapping>;\n}\n\n/**\n * Complete schema definition for a state machine.\n * \n * @typeParam Context - The context type\n * @typeParam EventPayloadMapping - Mapping of event names to their payload types\n * @typeParam StateNames - Union type of all valid state names (inferred from states array)\n * @typeParam EventOutputMapping - Optional mapping of events to their output types\n */\nexport interface StateMachineSchema<\n Context extends BaseContext = BaseContext,\n EventPayloadMapping = any,\n StateNames extends string = string,\n EventOutputMapping extends Partial<Record<keyof EventPayloadMapping, unknown>> = DefaultOutputMapping<EventPayloadMapping>\n> {\n /** Array of all possible state names */\n states: readonly StateNames[] | StateNames[];\n /** Mapping of event names to their payload types */\n events: EventPayloadMapping;\n /** Array of state definitions */\n stateDefinitions: StateDefinition<Context, EventPayloadMapping, StateNames, EventOutputMapping>[];\n /** Initial state name */\n initialState: StateNames;\n}\n\n/**\n * Creates a state machine from a schema definition.\n *\n * @remarks\n * This factory function takes a schema and creates a fully functional state machine\n * at runtime. The resulting state machine uses type erasure (`any` types) but maintains\n * full runtime functionality.\n *\n * Actions can return values that will be included in the event result. To enable\n * typed outputs, provide an EventOutputMapping type parameter that maps event names\n * to their output types.\n *\n * @typeParam Context - The context type\n * @typeParam EventPayloadMapping - Mapping of event names to their payload types\n * @typeParam EventOutputMapping - Optional mapping of events to their output types\n *\n * @param schema - The schema definition for the state machine\n * @param context - The context instance to use for the state machine\n * @returns A fully configured state machine instance\n *\n * @example\n * Basic state machine without outputs\n * ```typescript\n * const schema: StateMachineSchema = {\n * states: [\"IDLE\", \"RUNNING\", \"PAUSED\"],\n * events: {\n * start: {},\n * stop: {},\n * pause: {},\n * resume: {}\n * },\n * initialState: \"IDLE\",\n * stateDefinitions: [\n * {\n * name: \"IDLE\",\n * transitions: [\n * {\n * event: \"start\",\n * targetState: \"RUNNING\",\n * action: (context) => console.log(\"Starting...\")\n * }\n * ]\n * }\n * ]\n * };\n *\n * const machine = createStateMachineFromSchema(schema, context);\n * machine.happens(\"start\");\n * ```\n *\n * @example\n * State machine with typed outputs\n * ```typescript\n * type Events = { calculate: { value: number }; getResult: {} };\n * type Outputs = { calculate: number; getResult: number };\n *\n * const schema: StateMachineSchema<MyContext, Events, Outputs> = {\n * states: [\"READY\"],\n * events: { calculate: { value: 0 }, getResult: {} },\n * initialState: \"READY\",\n * stateDefinitions: [\n * {\n * name: \"READY\",\n * transitions: [\n * {\n * event: \"calculate\",\n * targetState: \"READY\",\n * action: (context, payload) => {\n * context.total += payload.value;\n * return context.total; // Return value included in result\n * }\n * },\n * {\n * event: \"getResult\",\n * targetState: \"READY\",\n * action: (context) => context.total // Return current total\n * }\n * ]\n * }\n * ]\n * };\n *\n * const machine = createStateMachineFromSchema<MyContext, Events, Outputs>(schema, context);\n * const result = machine.happens(\"calculate\", { value: 10 });\n * if (result.handled && \"output\" in result) {\n * console.log(result.output); // Typed as number\n * }\n * ```\n *\n * @category Runtime Factory\n */\nexport function createStateMachineFromSchema<\n Schema extends StateMachineSchema<any, any, any, any>\n>(\n schema: Schema,\n context: Schema extends StateMachineSchema<infer C, any, any, any> ? C : BaseContext\n): Schema extends StateMachineSchema<infer C, infer EPM, any, infer EOM>\n ? StateMachine<EPM, C, any, EOM>\n : StateMachine<any, BaseContext, any, any> {\n // Extract types from schema for internal use\n type SchemaContext = Schema extends StateMachineSchema<infer C, any, any, any> ? C : BaseContext;\n type SchemaEventPayloadMapping = Schema extends StateMachineSchema<any, infer EPM, any, any> ? EPM : any;\n type SchemaEventOutputMapping = Schema extends StateMachineSchema<any, any, any, infer EOM> ? EOM : any;\n\n // Validate schema\n if (!schema.states.includes(schema.initialState)) {\n throw new Error(\n `Initial state \"${schema.initialState}\" must be in the states array`\n );\n }\n\n // Validate all state definitions reference valid states\n for (const stateDef of schema.stateDefinitions) {\n if (!schema.states.includes(stateDef.name)) {\n throw new Error(\n `State definition \"${stateDef.name}\" is not in the states array`\n );\n }\n\n for (const transition of stateDef.transitions) {\n const eventsRecord = schema.events as Record<string, any>;\n if (!(String(transition.event) in eventsRecord)) {\n throw new Error(\n `Event \"${String(transition.event)}\" in state \"${stateDef.name}\" is not defined in events`\n );\n }\n if (!schema.states.includes(transition.targetState)) {\n throw new Error(\n `Target state \"${transition.targetState}\" in transition from \"${stateDef.name}\" is not in the states array`\n );\n }\n\n if (transition.guards) {\n for (const guard of transition.guards) {\n if (!schema.states.includes(guard.targetState)) {\n throw new Error(\n `Guard target state \"${guard.targetState}\" is not in the states array`\n );\n }\n // Validate guard references (if guard is a string, it must exist in state's guards)\n if (typeof guard.guard === 'string') {\n if (!stateDef.guards || !(guard.guard in stateDef.guards)) {\n throw new Error(\n `Guard \"${guard.guard}\" referenced in state \"${stateDef.name}\" for event \"${String(transition.event)}\" is not defined in the state's guards section`\n );\n }\n }\n }\n }\n }\n }\n\n // Create dynamic state classes\n const stateInstances: Record<string, TemplateState<any, SchemaContext, any>> = {};\n\n for (const stateDef of schema.stateDefinitions) {\n // Build event reactions from transitions\n const eventReactions: any = {};\n const allGuards: Record<string, GuardFunction<SchemaContext>> = {};\n const eventGuards: any = {};\n\n // First, collect state-level guard definitions\n if (stateDef.guards) {\n for (const [guardName, guardFunction] of Object.entries(stateDef.guards)) {\n allGuards[guardName] = guardFunction;\n }\n }\n\n // Then, collect guards from transitions and event reactions\n for (const transition of stateDef.transitions) {\n const eventName = transition.event;\n\n // Build event reaction\n eventReactions[eventName] = {\n action: transition.action || NO_OP,\n defaultTargetState: transition.targetState,\n };\n\n // Build guards for this event if they exist\n if (transition.guards && transition.guards.length > 0) {\n const guardMappings: any[] = [];\n transition.guards.forEach((guardDef, index) => {\n let guardKey: string;\n \n // Check if guard is a string (reference to state-level guard) or a function\n if (typeof guardDef.guard === 'string') {\n // Reference to a state-level guard\n guardKey = guardDef.guard;\n // Validate that the guard exists\n if (!allGuards[guardKey]) {\n throw new Error(\n `Guard \"${guardKey}\" referenced in state \"${stateDef.name}\" for event \"${String(eventName)}\" is not defined in the state's guards section`\n );\n }\n } else {\n // Inline guard function - create a unique key\n guardKey = `guard_${stateDef.name}_${String(eventName)}_${index}`;\n allGuards[guardKey] = guardDef.guard;\n }\n \n guardMappings.push({\n guard: guardKey,\n target: guardDef.targetState,\n });\n });\n eventGuards[eventName] = guardMappings;\n }\n }\n\n // Create state instance with all guards and reactions\n class DynamicState extends TemplateState<any, SchemaContext, any> {\n eventReactions = eventReactions;\n protected _guards = allGuards as any;\n protected _eventGuards = eventGuards as any;\n }\n\n const stateInstance = new DynamicState();\n \n // Add lifecycle hooks\n if (stateDef.onEnter) {\n const originalOnEnter = stateInstance.uponEnter.bind(stateInstance);\n stateInstance.uponEnter = (ctx, sm, from) => {\n originalOnEnter(ctx, sm, from);\n stateDef.onEnter!(ctx, from);\n };\n }\n if (stateDef.onExit) {\n const originalOnExit = stateInstance.beforeExit.bind(stateInstance);\n stateInstance.beforeExit = (ctx, sm, to) => {\n originalOnExit(ctx, sm, to);\n stateDef.onExit!(ctx, to);\n };\n }\n \n stateInstances[stateDef.name] = stateInstance;\n }\n\n // Ensure all states have instances (create empty states for states without definitions)\n for (const stateName of schema.states) {\n if (!stateInstances[stateName]) {\n class EmptyState extends TemplateState<any, SchemaContext, any> {\n eventReactions = {};\n }\n stateInstances[stateName] = new EmptyState();\n }\n }\n\n // Create and return the state machine\n return new TemplateStateMachine<SchemaEventPayloadMapping, SchemaContext, any, SchemaEventOutputMapping>(\n stateInstances as any,\n schema.initialState as any,\n context,\n true\n ) as any;\n}\n",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ue-too/being",
3
3
  "type": "module",
4
- "version": "0.13.0",
4
+ "version": "0.14.0",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/ue-too/ue-too.git"