From c3a40940c0a5dee11dbb0fbaaa45d811603e5ff7 Mon Sep 17 00:00:00 2001 From: Ali Mihandoost Date: Wed, 6 Nov 2024 14:32:27 +0330 Subject: [PATCH] refactor(fsm): update action naming conventions and enhance event handling in state transitions BREAKING CHANGE: all name of type ActionName in class `actionRecord_` changed --- packages/fetch-state-machine/src/base.ts | 12 ++++---- packages/fsm/src/base.ts | 32 +++++++++------------ packages/fsm/src/type.ts | 23 ++++++++------- packages/remote-context/src/base.ts | 36 ++++++++++++------------ 4 files changed, 48 insertions(+), 55 deletions(-) diff --git a/packages/fetch-state-machine/src/base.ts b/packages/fetch-state-machine/src/base.ts index 69e6ad0..ad159aa 100644 --- a/packages/fetch-state-machine/src/base.ts +++ b/packages/fetch-state-machine/src/base.ts @@ -4,7 +4,7 @@ import {packageTracer, fetch, type FetchOptions} from '@alwatr/nanolib'; __dev_mode__: packageTracer.add(__package_name__, __package_version__); export type ServerRequestState = 'initial' | 'loading' | 'failed' | 'complete'; -export type ServerRequestEvent = 'request' | 'requestFailed' | 'requestSucceeded'; +export type ServerRequestEvent = 'request' | 'request_failed' | 'request_succeeded'; export type {FetchOptions}; @@ -25,8 +25,8 @@ export abstract class AlwatrFetchStateMachineBase< request: 'loading', }, loading: { - requestFailed: 'failed', - requestSucceeded: 'complete', + request_failed: 'failed', + request_succeeded: 'complete', }, failed: { request: 'loading', @@ -37,7 +37,7 @@ export abstract class AlwatrFetchStateMachineBase< } as StateRecord; protected override actionRecord_ = { - on_loading_enter: this.requestAction_, + on_state_loading_enter: this.requestAction_, } as ActionRecord; constructor(config: AlwatrFetchStateMachineConfig) { @@ -83,12 +83,12 @@ export abstract class AlwatrFetchStateMachineBase< protected requestSucceeded_(): void { this.logger_.logMethod?.('requestSucceeded_'); - this.transition_('requestSucceeded'); + this.transition_('request_succeeded'); } protected requestFailed_(error: Error): void { this.logger_.error('requestFailed_', 'fetch_failed', error); - this.transition_('requestFailed'); + this.transition_('request_failed'); } protected setFetchOptions_(options?: Partial): void { diff --git a/packages/fsm/src/base.ts b/packages/fsm/src/base.ts index b56d017..1242fc4 100644 --- a/packages/fsm/src/base.ts +++ b/packages/fsm/src/base.ts @@ -1,18 +1,18 @@ import {packageTracer} from '@alwatr/nanolib'; import {AlwatrObservable, type AlwatrObservableConfig} from '@alwatr/observable'; -import type {ActionName, ActionRecord, MachineEvent, MachineState, StateEventDetail, StateRecord} from './type.js'; +import type {ActionName, ActionRecord, StateEventDetail, StateRecord} from './type.js'; __dev_mode__: packageTracer.add(__package_name__, __package_version__); -export interface AlwatrFluxStateMachineConfig extends AlwatrObservableConfig { +export interface AlwatrFluxStateMachineConfig extends AlwatrObservableConfig { initialState: S; } /** * Flux (Finite) State Machine Base Class */ -export abstract class AlwatrFluxStateMachineBase extends AlwatrObservable<{state: S}> { +export abstract class AlwatrFluxStateMachineBase extends AlwatrObservable<{state: S}> { /** * States and transitions config. */ @@ -45,7 +45,7 @@ export abstract class AlwatrFluxStateMachineBase { const fromState = this.message_.state; - const toState = this.stateRecord_[fromState]?.[event] ?? this.stateRecord_._all?.[event]; + const toState = this.stateRecord_[fromState]?.[event]; this.logger_.logMethodArgs?.('transition_', {fromState, event, toState}); @@ -79,7 +79,7 @@ export abstract class AlwatrFluxStateMachineBase): Promise { this.logger_.logMethodArgs?.('_transitioned', eventDetail); - await this.execAction__(`on_${eventDetail.event}`, eventDetail); + await this.execAction__(`on_event_${eventDetail.event}`, eventDetail); if (eventDetail.from !== eventDetail.to) { - await this.execAction__(`on_state_exit`, eventDetail); - await this.execAction__(`on_${eventDetail.from}_exit`, eventDetail); - await this.execAction__(`on_state_enter`, eventDetail); - await this.execAction__(`on_${eventDetail.to}_enter`, eventDetail); + await this.execAction__(`on_any_state_exit`, eventDetail); + await this.execAction__(`on_state_${eventDetail.from}_exit`, eventDetail); + await this.execAction__(`on_any_state_enter`, eventDetail); + await this.execAction__(`on_state_${eventDetail.to}_enter`, eventDetail); } - if (Object.hasOwn(this, `on_${eventDetail.from}_${eventDetail.event}`)) { - this.execAction__(`on_${eventDetail.from}_${eventDetail.event}`, eventDetail); - } - else { - // The action `all_eventName` is executed only if the action `fromState_eventName` is not defined. - this.execAction__(`on_all_${eventDetail.event}`, eventDetail); - } + this.execAction__(`on_state_${eventDetail.from}_event_${eventDetail.event}`, eventDetail); } /** * Execute action name if defined in _actionRecord. */ - private execAction__(name: ActionName, eventDetail: StateEventDetail): MaybePromise { + private execAction__(name: ActionName, eventDetail: StateEventDetail): MaybePromise { const actionFn = this.actionRecord_[name]; if (typeof actionFn === 'function') { this.logger_.logMethodArgs?.('_$execAction', name); diff --git a/packages/fsm/src/type.ts b/packages/fsm/src/type.ts index 110f63f..0442e2e 100644 --- a/packages/fsm/src/type.ts +++ b/packages/fsm/src/type.ts @@ -1,20 +1,19 @@ -export interface StateEventDetail { +export interface StateEventDetail { from: S; - event: E; + event: E | 'reset'; to: S; } -export type StateRecord = Partial>>>; +export type StateRecord = Partial>>>; -export type Action = (eventDetail?: StateEventDetail) => MaybePromise; +export type Action = (eventDetail?: StateEventDetail) => MaybePromise; export type ActionName = - | `on_${E}` - | `on_state_exit` - | `on_state_enter` - | `on_${S}_exit` - | `on_${S}_enter` - | `on_${S}_${E}` - | `on_all_${E}`; + | `on_event_${E}` + | `on_any_state_exit` + | `on_any_state_enter` + | `on_state_${S}_exit` + | `on_state_${S}_enter` + | `on_state_${S}_event_${E}`; -export type ActionRecord = Partial, Action>>; +export type ActionRecord = Partial, Action>>; diff --git a/packages/remote-context/src/base.ts b/packages/remote-context/src/base.ts index 99d619d..bfb5ff3 100644 --- a/packages/remote-context/src/base.ts +++ b/packages/remote-context/src/base.ts @@ -8,10 +8,10 @@ import {packageTracer} from '@alwatr/nanolib'; __dev_mode__: packageTracer.add(__package_name__, __package_version__); -type ExtraState = 'offlineCheck' | 'reloading' | 'reloadingFailed'; +type ExtraState = 'offline_check' | 'reloading' | 'reloading_failed'; export type ServerContextState = ServerRequestState | ExtraState; -type ExtraEvent = 'cacheNotFound'; +type ExtraEvent = 'cache_not_found'; export type ServerContextEvent = ServerRequestEvent | ExtraEvent; export type AlwatrRemoteContextStateMachineConfig = AlwatrFetchStateMachineConfig; @@ -28,37 +28,37 @@ export abstract class AlwatrRemoteContextStateMachineBase this.stateRecord_ = { initial: { - request: 'offlineCheck', + request: 'offline_check', }, /** * Just check offline cache data before online request. */ - offlineCheck: { - requestFailed: 'failed', - cacheNotFound: 'loading', - requestSucceeded: 'reloading', + offline_check: { + request_failed: 'failed', + cache_not_found: 'loading', + request_succeeded: 'reloading', }, /** * First loading without any cached context. */ loading: { - requestFailed: 'failed', - requestSucceeded: 'complete', + request_failed: 'failed', + request_succeeded: 'complete', }, /** * First loading failed without any cached context. */ failed: { - request: 'loading', // //TODO: why offlineCheck? should be loading! + request: 'loading', // //TODO: why offline_check? should be loading! }, reloading: { - requestFailed: 'reloadingFailed', - requestSucceeded: 'complete', + request_failed: 'reloading_failed', + request_succeeded: 'complete', }, /** * Reloading failed with previously cached context exist. */ - reloadingFailed: { + reloading_failed: { request: 'reloading', }, complete: { @@ -67,10 +67,10 @@ export abstract class AlwatrRemoteContextStateMachineBase }; this.actionRecord_ = { - on_offlineCheck_enter: this.offlineRequestAction_, - on_loading_enter: this.onlineRequestAction_, - on_reloading_enter: this.onlineRequestAction_, - on_requestSucceeded: this.updateContextAction_, + on_state_offline_check_enter: this.offlineRequestAction_, + on_state_loading_enter: this.onlineRequestAction_, + on_state_reloading_enter: this.onlineRequestAction_, + on_event_request_succeeded: this.updateContextAction_, }; } @@ -101,7 +101,7 @@ export abstract class AlwatrRemoteContextStateMachineBase this.logger_.logMethod?.('requestFailed_'); if (error.message === 'fetch_cache_not_found') { - this.transition_('cacheNotFound'); + this.transition_('cache_not_found'); } else { super.requestFailed_(error);