@alwatr/fsm
Advanced tools
Comparing version 0.30.0 to 0.31.0
@@ -6,2 +6,28 @@ # Change Log | ||
# [0.31.0](https://github.com/AliMD/alwatr/compare/v0.30.0...v0.31.0) (2023-05-08) | ||
### Bug Fixes | ||
- **fms:** import path ([f6770a0](https://github.com/AliMD/alwatr/commit/f6770a07fdf6855ccd63a85822d44d5ef9c72dee)) | ||
- **fsm:** action maybe async ([50efffa](https://github.com/AliMD/alwatr/commit/50efffa34a2ea5a3515561d7425da0c109631f36)) | ||
- **fsm:** autoSignalUnsubscribe type ([f7db30b](https://github.com/AliMD/alwatr/commit/f7db30bf5a90ff3d163f036b313a412a5149ff2b)) | ||
- **fsm:** call render states function in there own this ([a950478](https://github.com/AliMD/alwatr/commit/a95047811366e375785b2cd8fb176b1176638cab)) | ||
- **fsm:** fix order of `initFsmInstance` args ([3b60138](https://github.com/AliMD/alwatr/commit/3b60138ecebcbcb4d732e4d1a3e79f5b8661ae47)) | ||
- **fsm:** initial exec actions ([e7dd5c8](https://github.com/AliMD/alwatr/commit/e7dd5c8aaf9760c9856e4392cc899020f7e796d9)) | ||
- **fsm:** last reported bugs in set state ([e7435c8](https://github.com/AliMD/alwatr/commit/e7435c870a054b0ec3e4004f13c6db7610610be0)) | ||
- **fsm:** review reset process ([af6e81c](https://github.com/AliMD/alwatr/commit/af6e81c068b467d8b3aa96f2431e13ac479f018c)) | ||
- **fsm:** run init entry actions ([777ae45](https://github.com/AliMD/alwatr/commit/777ae459f2b77f79696daf3a0ca355d6d78e57d3)) | ||
- new logger api ([9d83a7d](https://github.com/AliMD/alwatr/commit/9d83a7dc5c103bc3bb4282dacfd85fa998915300)) | ||
### Features | ||
- **fsm:** add `signalRecord` to config ([1a35291](https://github.com/AliMD/alwatr/commit/1a352915fba978da141513517655d1e07350c3ec)) | ||
- **fsm:** add unsubscribe ([85ed3c3](https://github.com/AliMD/alwatr/commit/85ed3c3439e1f40c2760f6011df112242f10be06)) | ||
- **fsm:** callback in provider signals ([772818b](https://github.com/AliMD/alwatr/commit/772818baa7953b6fbb4d4128fcee76733f42cc2d)) | ||
- **fsm:** custom signal callback ([47c22e9](https://github.com/AliMD/alwatr/commit/47c22e92a8a8085148b44b316d649b695ff8071a)) | ||
- **fsm:** destroy and expire api ([e1a1c15](https://github.com/AliMD/alwatr/commit/e1a1c150d81f4428718bd18f039235c7fce9caf2)) | ||
- **fsm:** new types ([2866e3b](https://github.com/AliMD/alwatr/commit/2866e3bd5ff56fd2b5bddcaed3673a5868bae4bb)) | ||
- **fsm:** rewrite state machine ([7f24695](https://github.com/AliMD/alwatr/commit/7f246959e5a80b21c1c4b21e895e75f8fbe56798)) | ||
- **fsm:** subscribe ([2af4f44](https://github.com/AliMD/alwatr/commit/2af4f44f0e8a2dee39cde10dcaa3281075632e6a)) | ||
# [0.30.0](https://github.com/AliMD/alwatr/compare/v0.29.0...v0.30.0) (2023-03-06) | ||
@@ -8,0 +34,0 @@ |
190
core.d.ts
@@ -1,54 +0,158 @@ | ||
import type { Stringifyable, StringifyableRecord } from '@alwatr/type'; | ||
export interface MachineConfig<TState extends string, TEventId extends string, TContext extends Stringifyable> extends StringifyableRecord { | ||
import { ListenerSpec } from '@alwatr/signal'; | ||
import { SubscribeOptions } from '@alwatr/signal/type.js'; | ||
import type { ActionRecord, FsmConstructor, FsmConstructorConfig, FsmConsumerInterface, FsmInstance, FsmState, SignalConfig } from './type.js'; | ||
import type { MaybePromise, SingleOrArray, StringifyableRecord } from '@alwatr/type'; | ||
export declare const defineConstructor: <TState extends string = string, TEventId extends string = string, TActionName extends string = string, TContext extends StringifyableRecord = StringifyableRecord>(id: string, config: FsmConstructorConfig<TState, TEventId, TActionName, TContext>) => FsmConstructorConfig<TState, TEventId, TActionName, TContext>; | ||
/** | ||
* Get finite state machine instance by id. | ||
*/ | ||
export declare const getFsmInstance: <TState extends string = string, TEventId extends string = string, TContext extends StringifyableRecord = StringifyableRecord>(instanceId: string) => FsmInstance<TState, TEventId, TContext>; | ||
/** | ||
* Get finite state machine constructor by id. | ||
*/ | ||
export declare const getFsmConstructor: (constructorId: string) => FsmConstructor; | ||
/** | ||
* Get current state of finite state machine instance. | ||
*/ | ||
export declare const getState: <TState extends string = string, TEventId extends string = string>(instanceId: string) => FsmState<TState, TEventId>; | ||
/** | ||
* Get current context of finite state machine instance. | ||
*/ | ||
export declare const getContext: <TContext extends StringifyableRecord = StringifyableRecord>(instanceId: string) => TContext; | ||
/** | ||
* Set context of finite state machine instance. | ||
*/ | ||
export declare const setContext: <TContext extends StringifyableRecord = StringifyableRecord>(instanceId: string, context: Partial<TContext>, notify?: boolean) => void; | ||
/** | ||
* Transition finite state machine instance to new state. | ||
*/ | ||
export declare const transition: <TEventId extends string = string, TContext extends StringifyableRecord = StringifyableRecord>(instanceId: string, event: TEventId, context?: Partial<TContext> | undefined) => void; | ||
/** | ||
* Define actions for finite state machine constructor. | ||
*/ | ||
export declare const defineActions: <T extends Readonly<{ | ||
TState: string; | ||
TEventId: string; | ||
TActionName: string; | ||
TContext: StringifyableRecord; | ||
}>>(constructorId: string, actionRecord: ActionRecord<T>) => void; | ||
/** | ||
* Execute all actions for current state. | ||
*/ | ||
export declare const _execAllActions: (constructor: FsmConstructor, state: FsmState, consumerInterface: FsmConsumerInterface) => void; | ||
/** | ||
* Execute single action. | ||
*/ | ||
export declare const _execAction: (constructor: FsmConstructor, actionNames: SingleOrArray<string> | undefined, finiteStateMachine: FsmConsumerInterface) => boolean | MaybePromise<void>; | ||
/** | ||
* Initialize new finite state machine instance. | ||
*/ | ||
export declare const initFsmInstance: (instanceId: string, constructorId: string) => void; | ||
/** | ||
* Subscribe to all defined signals for finite state machine instance. | ||
*/ | ||
export declare const subscribeSignals: (instanceId: string, signalList: Array<SignalConfig>, subscribeConstructorSignals?: boolean) => Array<ListenerSpec>; | ||
/** | ||
* Define signals for finite state machine constructor. | ||
*/ | ||
export declare const defineConstructorSignals: <T extends Readonly<{ | ||
TState: string; | ||
TEventId: string; | ||
TActionName: string; | ||
TContext: StringifyableRecord; | ||
}>>(constructorId: string, signalList: SignalConfig<T>[]) => void; | ||
/** | ||
* Define signals for finite state machine instance. | ||
*/ | ||
export declare const defineInstanceSignals: <T extends Readonly<{ | ||
TState: string; | ||
TEventId: string; | ||
TActionName: string; | ||
TContext: StringifyableRecord; | ||
}>>(instanceId: string, signalList: SignalConfig<T>[], subscribeConstructorSignals?: boolean) => Array<ListenerSpec>; | ||
/** | ||
* Render helper for use finite state machine instance in UI. | ||
* | ||
* Example: | ||
* | ||
* ```ts | ||
* render('myFsm', { | ||
* state1: () => html`<div>State 1 Render...</div>`, | ||
* state2: () => html`<div>State 2 Render...</div>`, | ||
* state3: 'state1', | ||
* }); | ||
* ``` | ||
*/ | ||
export declare const render: <TState extends string = string>(instanceId: string, states: { [P in TState]: TState | (() => unknown); }, thisArg?: unknown) => unknown; | ||
/** | ||
* Subscribe to finite state machine instance state changes. | ||
*/ | ||
export declare const subscribe: (instanceId: string, callback: () => void, options?: Partial<SubscribeOptions>) => ListenerSpec; | ||
/** | ||
* Destroy finite state machine instance object to clear memory. | ||
*/ | ||
export declare const destroy: (instanceId: string) => void; | ||
/** | ||
* Reset finite state machine instance to initial state and context. | ||
*/ | ||
export declare const reset: (instanceId: string) => void; | ||
/** | ||
* Finite state machine instance consumer. | ||
* Lookup current finite state machine instance or initialize new one and return consumer object . | ||
*/ | ||
export declare const finiteStateMachineConsumer: <T extends Readonly<{ | ||
TState: string; | ||
TEventId: string; | ||
TActionName: string; | ||
TContext: StringifyableRecord; | ||
}>, TContext extends T["TContext"] = T["TContext"]>(instanceId: string, makeFromConstructor?: string) => { | ||
/** | ||
* Machine ID (It is used in the state change signal identifier, so it must be unique). | ||
* Finite state machine instance id. | ||
*/ | ||
id: string; | ||
readonly id: string; | ||
/** | ||
* Initial state. | ||
* Finite state machine constructor id. | ||
*/ | ||
initial: TState; | ||
readonly constructorId: string; | ||
/** | ||
* Initial context. | ||
* Render helper for use finite state machine instance in UI. | ||
*/ | ||
context: TContext; | ||
readonly render: (states: { [P in T["TState"]]: (() => unknown) | T["TState"]; }, thisArg?: unknown) => unknown; | ||
/** | ||
* States list | ||
* Subscribe to finite state machine instance state changes. | ||
*/ | ||
states: { | ||
[S in TState | '$all']: { | ||
/** | ||
* An object mapping eventId (keys) to state. | ||
*/ | ||
on: { | ||
[E in TEventId]?: TState | '$self'; | ||
}; | ||
}; | ||
}; | ||
} | ||
export interface StateContext<TState extends string, TEventId extends string> { | ||
[T: string]: string; | ||
to: TState; | ||
from: TState | 'init'; | ||
by: TEventId | 'INIT'; | ||
} | ||
export declare class FiniteStateMachine<TState extends string = string, TEventId extends string = string, TContext extends StringifyableRecord = StringifyableRecord> { | ||
readonly config: Readonly<MachineConfig<TState, TEventId, TContext>>; | ||
state: StateContext<TState, TEventId>; | ||
context: TContext; | ||
signal: { | ||
readonly id: string; | ||
readonly getValue: () => StateContext<TState, TEventId> | undefined; | ||
readonly untilChange: () => Promise<StateContext<TState, TEventId>>; | ||
readonly subscribe: (listenerCallback: import("@alwatr/signal/type.js").ListenerFunction<StateContext<TState, TEventId>>, options?: Partial<import("@alwatr/signal/type.js").SubscribeOptions> | undefined) => import("@alwatr/signal").ListenerSpec; | ||
readonly unsubscribe: (listener: import("@alwatr/signal").ListenerSpec) => void; | ||
}; | ||
protected _logger: import("@alwatr/logger").AlwatrLogger; | ||
protected setState(to: TState, by: TEventId | 'INIT'): void; | ||
constructor(config: Readonly<MachineConfig<TState, TEventId, TContext>>); | ||
readonly subscribe: (callback: () => void, options?: Partial<SubscribeOptions> | undefined) => ListenerSpec; | ||
/** | ||
* Machine transition. | ||
* Unsubscribe from finite state machine instance state changes. | ||
*/ | ||
transition(event: TEventId, context?: Partial<TContext>): TState | null; | ||
} | ||
readonly unsubscribe: (listener: ListenerSpec) => void; | ||
/** | ||
* Get current state of finite state machine instance. | ||
*/ | ||
readonly getState: () => FsmState<T["TState"], T["TEventId"]>; | ||
/** | ||
* Get current context of finite state machine instance. | ||
*/ | ||
readonly getContext: () => TContext; | ||
/** | ||
* Set context of finite state machine instance. | ||
*/ | ||
readonly setContext: (context: Partial<TContext>, notify?: boolean | undefined) => void; | ||
/** | ||
* Transition finite state machine instance to new state. | ||
*/ | ||
readonly transition: (event: T["TEventId"], context?: Partial<TContext> | undefined) => void; | ||
/** | ||
* Define signals for finite state machine instance. | ||
*/ | ||
readonly defineSignals: (signalList: SignalConfig<T>[], subscribeConstructorSignals?: boolean | undefined) => ListenerSpec[]; | ||
/** | ||
* Reset finite state machine instance to initial state and context. | ||
*/ | ||
readonly reset: () => void; | ||
/** | ||
* Destroy finite state machine instance object to clear memory. | ||
*/ | ||
readonly destroy: () => void; | ||
}; | ||
//# sourceMappingURL=core.d.ts.map |
410
core.js
import { createLogger, globalAlwatr } from '@alwatr/logger'; | ||
import { contextConsumer } from '@alwatr/signal'; | ||
import { dispatch } from '@alwatr/signal/core.js'; | ||
import { contextProvider, contextConsumer } from '@alwatr/signal'; | ||
import { destroySignal, unsubscribe } from '@alwatr/signal/core.js'; | ||
globalAlwatr.registeredList.push({ | ||
@@ -8,57 +8,365 @@ name: '@alwatr/fsm', | ||
}); | ||
export class FiniteStateMachine { | ||
setState(to, by) { | ||
var _a, _b; | ||
this.state = { | ||
to, | ||
from: (_b = (_a = this.signal.getValue()) === null || _a === void 0 ? void 0 : _a.to) !== null && _b !== void 0 ? _b : 'init', | ||
by, | ||
}; | ||
dispatch(this.signal.id, this.state, { debounce: 'NextCycle' }); | ||
const logger = createLogger(`alwatr/fsm`); | ||
/** | ||
* Finite state machine constructor storage. | ||
*/ | ||
const fsmConstructorStorage = {}; | ||
export const defineConstructor = (id, config) => { | ||
var _a; | ||
(_a = logger.logMethodArgs) === null || _a === void 0 ? void 0 : _a.call(logger, 'defineConstructor', { id, config }); | ||
if (fsmConstructorStorage[id] != null) | ||
throw new Error('fsm_exist', { cause: { id } }); | ||
fsmConstructorStorage[id] = { | ||
id, | ||
config, | ||
actionRecord: {}, | ||
signalList: [], | ||
}; | ||
return config; | ||
}; | ||
/** | ||
* Get finite state machine instance by id. | ||
*/ | ||
export const getFsmInstance = (instanceId) => { | ||
var _a; | ||
(_a = logger.logMethodArgs) === null || _a === void 0 ? void 0 : _a.call(logger, '_getFsmInstance', instanceId); | ||
const fsmInstance = contextConsumer.getValue(instanceId); | ||
if (fsmInstance == null) | ||
throw new Error('fsm_undefined', { cause: { instanceId } }); | ||
return fsmInstance; | ||
}; | ||
/** | ||
* Get finite state machine constructor by id. | ||
*/ | ||
export const getFsmConstructor = (constructorId) => { | ||
var _a; | ||
(_a = logger.logMethodArgs) === null || _a === void 0 ? void 0 : _a.call(logger, '_getFsmConstructor', constructorId); | ||
const fsmConstructor = fsmConstructorStorage[constructorId]; | ||
if (fsmConstructor == null) | ||
throw new Error('fsm_undefined', { cause: { constructorId: constructorId } }); | ||
return fsmConstructor; | ||
}; | ||
/** | ||
* Get current state of finite state machine instance. | ||
*/ | ||
export const getState = (instanceId) => { | ||
var _a; | ||
(_a = logger.logMethodArgs) === null || _a === void 0 ? void 0 : _a.call(logger, 'getState', instanceId); | ||
return getFsmInstance(instanceId).state; | ||
}; | ||
/** | ||
* Get current context of finite state machine instance. | ||
*/ | ||
export const getContext = (instanceId) => { | ||
var _a; | ||
(_a = logger.logMethodArgs) === null || _a === void 0 ? void 0 : _a.call(logger, 'getContext', instanceId); | ||
return getFsmInstance(instanceId).context; | ||
}; | ||
/** | ||
* Set context of finite state machine instance. | ||
*/ | ||
export const setContext = (instanceId, context, notify) => { | ||
var _a; | ||
(_a = logger.logMethodArgs) === null || _a === void 0 ? void 0 : _a.call(logger, 'setContext', { instanceId, context }); | ||
const fsmInstance = getFsmInstance(instanceId); | ||
fsmInstance.context = { | ||
...fsmInstance.context, | ||
...context, | ||
}; | ||
if (notify) { | ||
contextProvider.setValue(instanceId, fsmInstance, { debounce: 'Timeout' }); | ||
} | ||
constructor(config) { | ||
this.config = config; | ||
this.state = { | ||
to: this.config.initial, | ||
from: 'init', | ||
by: 'INIT', | ||
}; | ||
/** | ||
* Transition finite state machine instance to new state. | ||
*/ | ||
export const transition = (instanceId, event, context) => { | ||
var _a, _b, _c, _d, _e, _f, _g; | ||
const fsmInstance = getFsmInstance(instanceId); | ||
const fsmConstructor = getFsmConstructor(fsmInstance.constructorId); | ||
const fromState = fsmInstance.state.target; | ||
const stateRecord = fsmConstructor.config.stateRecord; | ||
const transitionConfig = (_b = (_a = stateRecord[fromState]) === null || _a === void 0 ? void 0 : _a.on[event]) !== null && _b !== void 0 ? _b : stateRecord.$all.on[event]; | ||
(_c = logger.logMethodArgs) === null || _c === void 0 ? void 0 : _c.call(logger, 'transition', { instanceId, fromState, event, context, target: transitionConfig === null || transitionConfig === void 0 ? void 0 : transitionConfig.target }); | ||
if (context !== undefined) { | ||
fsmInstance.context = { | ||
...fsmInstance.context, | ||
...context, | ||
}; | ||
this.context = this.config.context; | ||
this.signal = contextConsumer.bind('finite-state-machine-' + this.config.id); | ||
this._logger = createLogger(`alwatr/fsm:${this.config.id}`); | ||
this._logger.logMethodArgs('constructor', config); | ||
dispatch(this.signal.id, this.state, { debounce: 'NextCycle' }); | ||
if (!config.states[config.initial]) { | ||
this._logger.error('constructor', 'invalid_initial_state', config); | ||
} | ||
} | ||
/** | ||
* Machine transition. | ||
*/ | ||
transition(event, context) { | ||
var _a, _b, _c, _d, _e, _f, _g; | ||
const fromState = this.state.to; | ||
let toState = (_c = (_b = (_a = this.config.states[fromState]) === null || _a === void 0 ? void 0 : _a.on) === null || _b === void 0 ? void 0 : _b[event]) !== null && _c !== void 0 ? _c : (_e = (_d = this.config.states.$all) === null || _d === void 0 ? void 0 : _d.on) === null || _e === void 0 ? void 0 : _e[event]; | ||
if (toState === '$self') { | ||
toState = fromState; | ||
} | ||
this._logger.logMethodFull('transition', { fromState, event, context }, toState); | ||
if (context !== undefined) { | ||
this.context = { | ||
...this.context, | ||
...context, | ||
}; | ||
} | ||
if (toState == null) { | ||
this._logger.incident('transition', 'invalid_target_state', 'Defined target state for this event not found in state config', { | ||
fromState, | ||
event, | ||
events: { ...(_f = this.config.states.$all) === null || _f === void 0 ? void 0 : _f.on, ...(_g = this.config.states[fromState]) === null || _g === void 0 ? void 0 : _g.on }, | ||
if (transitionConfig == null) { | ||
(_d = logger.incident) === null || _d === void 0 ? void 0 : _d.call(logger, 'transition', 'invalid_target_state', 'Defined target state for this event not found in state config', { | ||
fromState, | ||
event, | ||
events: { | ||
...(_e = stateRecord.$all) === null || _e === void 0 ? void 0 : _e.on, | ||
...(_f = stateRecord[fromState]) === null || _f === void 0 ? void 0 : _f.on, | ||
}, | ||
}); | ||
return; | ||
} | ||
const consumerInterface = finiteStateMachineConsumer(instanceId); | ||
if (transitionConfig.condition) { | ||
if (_execAction(fsmConstructor, transitionConfig.condition, consumerInterface) === false) | ||
return; | ||
} | ||
fsmInstance.state = { | ||
target: (_g = transitionConfig.target) !== null && _g !== void 0 ? _g : fromState, | ||
from: fromState, | ||
by: event, | ||
}; | ||
contextProvider.setValue(instanceId, fsmInstance, { debounce: 'Timeout' }); | ||
_execAllActions(fsmConstructor, fsmInstance.state, consumerInterface); | ||
}; | ||
/** | ||
* Define actions for finite state machine constructor. | ||
*/ | ||
export const defineActions = (constructorId, actionRecord) => { | ||
var _a; | ||
(_a = logger.logMethodArgs) === null || _a === void 0 ? void 0 : _a.call(logger, 'defineActions', { constructorId, actionRecord }); | ||
const fmsConstructor = getFsmConstructor(constructorId); | ||
fmsConstructor.actionRecord = { | ||
...fmsConstructor.actionRecord, | ||
...actionRecord, | ||
}; | ||
}; | ||
/** | ||
* Execute all actions for current state. | ||
*/ | ||
export const _execAllActions = (constructor, state, consumerInterface) => { | ||
var _a, _b, _c, _d, _e, _f, _g; | ||
(_a = logger.logMethodArgs) === null || _a === void 0 ? void 0 : _a.call(logger, '_execAllActions', consumerInterface.id); | ||
const stateRecord = constructor.config.stateRecord; | ||
if (state.by === 'INIT') { | ||
_execAction(constructor, stateRecord.$all.entry, consumerInterface); | ||
_execAction(constructor, (_b = stateRecord[state.target]) === null || _b === void 0 ? void 0 : _b.entry, consumerInterface); | ||
return; | ||
} | ||
// else | ||
if (state.from !== state.target) { | ||
_execAction(constructor, stateRecord.$all.exit, consumerInterface); | ||
_execAction(constructor, (_c = stateRecord[state.from]) === null || _c === void 0 ? void 0 : _c.exit, consumerInterface); | ||
_execAction(constructor, stateRecord.$all.entry, consumerInterface); | ||
_execAction(constructor, (_d = stateRecord[state.target]) === null || _d === void 0 ? void 0 : _d.entry, consumerInterface); | ||
} | ||
_execAction(constructor, ((_e = stateRecord[state.from]) === null || _e === void 0 ? void 0 : _e.on[state.by]) != null | ||
? (_f = stateRecord[state.from].on[state.by]) === null || _f === void 0 ? void 0 : _f.actions | ||
: (_g = stateRecord.$all.on[state.by]) === null || _g === void 0 ? void 0 : _g.actions, consumerInterface); | ||
}; | ||
/** | ||
* Execute single action. | ||
*/ | ||
export const _execAction = (constructor, actionNames, finiteStateMachine) => { | ||
var _a; | ||
if (actionNames == null) | ||
return; | ||
(_a = logger.logMethodArgs) === null || _a === void 0 ? void 0 : _a.call(logger, 'execAction', { constructorId: constructor.id, actionNames }); | ||
if (Array.isArray(actionNames)) { | ||
return actionNames | ||
.map((actionName) => _execAction(constructor, actionName, finiteStateMachine)) | ||
.every((r) => r === true); | ||
} | ||
try { | ||
const actionFn = constructor.actionRecord[actionNames]; | ||
if (actionFn == null) { | ||
return logger.error('execAction', 'action_not_found', { | ||
actionNames, | ||
constructorId: constructor.id, | ||
instanceId: finiteStateMachine.id, | ||
}); | ||
return null; | ||
} | ||
this.setState(toState, event); | ||
return toState; | ||
return actionFn(finiteStateMachine); | ||
} | ||
} | ||
catch (error) { | ||
return logger.error('execAction', 'action_error', error, { | ||
actionNames, | ||
constructorId: constructor.id, | ||
instanceId: finiteStateMachine.id, | ||
}); | ||
} | ||
}; | ||
/** | ||
* Initialize new finite state machine instance. | ||
*/ | ||
export const initFsmInstance = (instanceId, constructorId) => { | ||
var _a; | ||
(_a = logger.logMethodArgs) === null || _a === void 0 ? void 0 : _a.call(logger, 'initializeMachine', { constructorId, instanceId }); | ||
const constructor = getFsmConstructor(constructorId); | ||
const { initial, context } = constructor.config; | ||
const newInstance = { | ||
constructorId, | ||
state: { | ||
target: initial, | ||
from: initial, | ||
by: 'INIT', | ||
}, | ||
context, | ||
}; | ||
contextProvider.setValue(instanceId, newInstance, { debounce: 'NextCycle' }); | ||
_execAllActions(constructor, newInstance.state, finiteStateMachineConsumer(instanceId)); | ||
}; | ||
/** | ||
* Subscribe to all defined signals for finite state machine instance. | ||
*/ | ||
export const subscribeSignals = (instanceId, signalList, subscribeConstructorSignals = true) => { | ||
var _a, _b, _c; | ||
(_a = logger.logMethodArgs) === null || _a === void 0 ? void 0 : _a.call(logger, 'subscribeSignals', { instanceId, signalList }); | ||
const listenerList = []; | ||
if (subscribeConstructorSignals) { | ||
signalList = signalList.concat(getFsmConstructor(getFsmInstance(instanceId).constructorId).signalList); | ||
} | ||
for (const signalConfig of signalList) { | ||
(_b = signalConfig.signalId) !== null && _b !== void 0 ? _b : (signalConfig.signalId = instanceId); | ||
listenerList.push(contextConsumer.subscribe(signalConfig.signalId, (signalDetail) => { | ||
var _a; | ||
(_a = logger.logMethodArgs) === null || _a === void 0 ? void 0 : _a.call(logger, 'execSignalCallback', { instanceId, signalId: signalConfig.signalId, signalDetail }); | ||
if (signalConfig.callback) { | ||
signalConfig.callback(signalDetail, finiteStateMachineConsumer(instanceId)); | ||
} | ||
else { | ||
// prettier-ignore | ||
transition(instanceId, signalConfig.transition, signalConfig.contextName == null ? undefined : { | ||
[signalConfig.contextName]: signalDetail, | ||
}); | ||
} | ||
}, { receivePrevious: (_c = signalConfig.receivePrevious) !== null && _c !== void 0 ? _c : 'No' })); | ||
} | ||
return listenerList; | ||
}; | ||
/** | ||
* Define signals for finite state machine constructor. | ||
*/ | ||
export const defineConstructorSignals = (constructorId, signalList) => { | ||
var _a; | ||
(_a = logger.logMethodArgs) === null || _a === void 0 ? void 0 : _a.call(logger, 'defineSignals', { constructorId, signalList: signalList }); | ||
const fsmConstructor = getFsmConstructor(constructorId); | ||
fsmConstructor.signalList = fsmConstructor.signalList.concat(signalList); | ||
}; | ||
/** | ||
* Define signals for finite state machine instance. | ||
*/ | ||
export const defineInstanceSignals = (instanceId, signalList, subscribeConstructorSignals = true) => { | ||
var _a; | ||
(_a = logger.logMethodArgs) === null || _a === void 0 ? void 0 : _a.call(logger, 'defineSignals', { instanceId, signals: signalList }); | ||
return subscribeSignals(instanceId, signalList, subscribeConstructorSignals); | ||
}; | ||
/** | ||
* Render helper for use finite state machine instance in UI. | ||
* | ||
* Example: | ||
* | ||
* ```ts | ||
* render('myFsm', { | ||
* state1: () => html`<div>State 1 Render...</div>`, | ||
* state2: () => html`<div>State 2 Render...</div>`, | ||
* state3: 'state1', | ||
* }); | ||
* ``` | ||
*/ | ||
export const render = (instanceId, states, thisArg = null) => { | ||
var _a; | ||
const state = getFsmInstance(instanceId).state; | ||
(_a = logger.logMethodArgs) === null || _a === void 0 ? void 0 : _a.call(logger, 'render', { instanceId, state: state.target }); | ||
let renderFn = states[state.target]; | ||
if (typeof renderFn === 'string') { | ||
renderFn = states[renderFn]; | ||
} | ||
if (typeof renderFn === 'function') { | ||
return renderFn.call(thisArg); | ||
} | ||
return; | ||
}; | ||
/** | ||
* Subscribe to finite state machine instance state changes. | ||
*/ | ||
export const subscribe = (instanceId, callback, options) => { | ||
var _a; | ||
(_a = logger.logMethodArgs) === null || _a === void 0 ? void 0 : _a.call(logger, 'subscribe', instanceId); | ||
return contextConsumer.subscribe(instanceId, callback, options); | ||
}; | ||
/** | ||
* Destroy finite state machine instance object to clear memory. | ||
*/ | ||
export const destroy = (instanceId) => { | ||
var _a; | ||
(_a = logger.logMethodArgs) === null || _a === void 0 ? void 0 : _a.call(logger, 'destroy', instanceId); | ||
destroySignal(instanceId); | ||
}; | ||
/** | ||
* Reset finite state machine instance to initial state and context. | ||
*/ | ||
export const reset = (instanceId) => { | ||
var _a; | ||
(_a = logger.logMethodArgs) === null || _a === void 0 ? void 0 : _a.call(logger, 'reset', instanceId); | ||
const constructorId = getFsmInstance(instanceId).constructorId; | ||
// contextProvider.expire(instanceId); | ||
initFsmInstance(instanceId, constructorId); | ||
}; | ||
/** | ||
* Finite state machine instance consumer. | ||
* Lookup current finite state machine instance or initialize new one and return consumer object . | ||
*/ | ||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type | ||
export const finiteStateMachineConsumer = (instanceId, makeFromConstructor) => { | ||
var _a, _b; | ||
(_a = logger.logMethodArgs) === null || _a === void 0 ? void 0 : _a.call(logger, 'stateMachineLookup', instanceId); | ||
const machineInstance = contextConsumer.getValue(instanceId); | ||
if (machineInstance == null) { | ||
// instance not initialized. | ||
if (makeFromConstructor == null) { | ||
throw new Error('fsm_undefined', { cause: { instanceId } }); | ||
} | ||
initFsmInstance(instanceId, makeFromConstructor); | ||
} | ||
return { | ||
/** | ||
* Finite state machine instance id. | ||
*/ | ||
id: instanceId, | ||
/** | ||
* Finite state machine constructor id. | ||
*/ | ||
constructorId: (_b = machineInstance === null || machineInstance === void 0 ? void 0 : machineInstance.constructorId) !== null && _b !== void 0 ? _b : makeFromConstructor, | ||
/** | ||
* Render helper for use finite state machine instance in UI. | ||
*/ | ||
render: render.bind(null, instanceId), | ||
/** | ||
* Subscribe to finite state machine instance state changes. | ||
*/ | ||
subscribe: subscribe.bind(null, instanceId), | ||
/** | ||
* Unsubscribe from finite state machine instance state changes. | ||
*/ | ||
unsubscribe: unsubscribe, | ||
/** | ||
* Get current state of finite state machine instance. | ||
*/ | ||
getState: getState.bind(null, instanceId), | ||
/** | ||
* Get current context of finite state machine instance. | ||
*/ | ||
getContext: getContext.bind(null, instanceId), | ||
/** | ||
* Set context of finite state machine instance. | ||
*/ | ||
setContext: setContext.bind(null, instanceId), | ||
/** | ||
* Transition finite state machine instance to new state. | ||
*/ | ||
transition: transition.bind(null, instanceId), | ||
/** | ||
* Define signals for finite state machine instance. | ||
*/ | ||
defineSignals: defineInstanceSignals.bind(null, instanceId), | ||
/** | ||
* Reset finite state machine instance to initial state and context. | ||
*/ | ||
reset: reset.bind(null, instanceId), | ||
/** | ||
* Destroy finite state machine instance object to clear memory. | ||
*/ | ||
destroy: destroy.bind(null, instanceId), | ||
}; | ||
}; | ||
//# sourceMappingURL=core.js.map |
{ | ||
"name": "@alwatr/fsm", | ||
"version": "0.30.0", | ||
"description": "Managing invocations finite-state machines as actors written in tiny TypeScript module.", | ||
"version": "0.31.0", | ||
"description": "Managing invocations finite-state machines for lit-element written in tiny TypeScript module.", | ||
"keywords": [ | ||
@@ -9,2 +9,5 @@ "state", | ||
"machine", | ||
"lit", | ||
"lit-element", | ||
"lit-html", | ||
"typescript", | ||
@@ -14,5 +17,5 @@ "esm", | ||
], | ||
"main": "core.js", | ||
"main": "index.js", | ||
"type": "module", | ||
"types": "core.d.ts", | ||
"types": "index.d.ts", | ||
"author": "S. Ali Mihandoost <ali.mihandoost@gmail.com>", | ||
@@ -36,8 +39,8 @@ "license": "MIT", | ||
"dependencies": { | ||
"@alwatr/logger": "^0.30.0", | ||
"@alwatr/signal": "^0.30.0", | ||
"@alwatr/type": "^0.30.0", | ||
"@alwatr/logger": "^0.31.0", | ||
"@alwatr/signal": "^0.31.0", | ||
"@alwatr/type": "^0.31.0", | ||
"tslib": "^2.5.0" | ||
}, | ||
"gitHead": "36f55780ccdcb1acc07400b0cdb3fe7b0df56cca" | ||
"gitHead": "896e64b58eed6e9048e870557ecf399d42705612" | ||
} |
# Alwatr Finite State Machines - `@alwatr/fsm` | ||
Managing invocations finite-state machines as actors written in tiny TypeScript module. | ||
Managing invocations finite-state machines for lit-element written in tiny TypeScript module. |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
70677
16
668
1
+ Added@alwatr/logger@0.31.0(transitive)
+ Added@alwatr/math@0.31.0(transitive)
+ Added@alwatr/signal@0.31.0(transitive)
+ Added@alwatr/type@0.31.0(transitive)
+ Added@alwatr/util@0.31.0(transitive)
- Removed@alwatr/logger@0.30.0(transitive)
- Removed@alwatr/math@0.30.0(transitive)
- Removed@alwatr/signal@0.30.0(transitive)
- Removed@alwatr/type@0.30.0(transitive)
- Removed@alwatr/util@0.30.0(transitive)
Updated@alwatr/logger@^0.31.0
Updated@alwatr/signal@^0.31.0
Updated@alwatr/type@^0.31.0