diff --git a/packages/fsm/src/base.ts b/packages/fsm/src/base.ts index b1257b5e..d4a5dca2 100644 --- a/packages/fsm/src/base.ts +++ b/packages/fsm/src/base.ts @@ -11,7 +11,7 @@ definePackage('@alwatr/signal', __package_version__); /** * Finite State Machine Base Class */ -export abstract class FiniteStateMachineBase extends AlwatrObservable { +export abstract class FiniteStateMachineBase extends AlwatrObservable<{state: S}> { /** * States and transitions config. */ @@ -24,20 +24,20 @@ export abstract class FiniteStateMachineBase protected initialState_: S; - protected override data_: S; + protected override message_: {state: S}; constructor(config: {name: string; loggerPrefix?: string; initialState: S}) { config.loggerPrefix ??= 'fsm'; super(config); - this.data_ = this.initialState_ = config.initialState; + this.message_ = {state: this.initialState_ = config.initialState}; } /** - * Reset machine to initial state. + * Reset machine to initial state without notify. */ protected resetToInitialState_(): void { this.logger_.logMethod?.('resetToInitialState_'); - this.data_ = this.initialState_; + this.message_ = {state: this.initialState_}; } /** @@ -52,7 +52,7 @@ export abstract class FiniteStateMachineBase * Transition finite state machine instance to new state. */ protected async transition_(event: E): Promise { - const fromState = this.data_; + const fromState = this.message_.state; const toState = this.stateRecord_[fromState]?.[event] ?? this.stateRecord_._all?.[event]; this.logger_.logMethodArgs?.('transition_', {fromState, event, toState}); @@ -69,7 +69,7 @@ export abstract class FiniteStateMachineBase if ((await this.shouldTransition_(eventDetail)) !== true) return; - this.notify_(toState); + this.notify_({state: toState}); this.postTransition__(eventDetail); } diff --git a/packages/fsm/src/fsm.ts b/packages/fsm/src/fsm.ts index 4493374a..9fca4416 100644 --- a/packages/fsm/src/fsm.ts +++ b/packages/fsm/src/fsm.ts @@ -7,8 +7,8 @@ export abstract class FiniteStateMachine ext /** * Current state. */ - getState(): S { - return this.data_; + get state(): S { + return this.message_.state; } /** diff --git a/packages/server-context/src/api-request.ts b/packages/server-context/src/api-request.ts index 17298545..c1d2bc34 100644 --- a/packages/server-context/src/api-request.ts +++ b/packages/server-context/src/api-request.ts @@ -14,7 +14,7 @@ export abstract class AlwatrApiRequestBase< > extends AlwatrServerRequestBase { protected _responseJson?: T; - protected override async _$fetch(options: FetchOptions): Promise { + protected override async fetch__(options: FetchOptions): Promise { if (!NODE_MODE) { options.headers ??= {}; if (!options.headers['client-id']) { @@ -22,11 +22,11 @@ export abstract class AlwatrApiRequestBase< } } - await super._$fetch(options); + await super.fetch__(options); let responseText: string; try { - responseText = await this._response!.text(); + responseText = await this.response_!.text(); } catch (err) { this._logger.error('_$fetch', 'invalid_response_text', err); @@ -54,8 +54,8 @@ export abstract class AlwatrApiRequestBase< } } - protected override _reset(): void { - super._reset(); + protected override resetToInitialState_(): void { + super.resetToInitialState_(); delete this._responseJson; } } @@ -73,14 +73,14 @@ export class AlwatrApiRequest): void { - return this._request(options); + return this.request_(options); } cleanup(): void { - this._reset(); + this.resetToInitialState_(); } } diff --git a/packages/server-context/src/server-context.ts b/packages/server-context/src/server-context.ts index bdead426..e4d553dc 100644 --- a/packages/server-context/src/server-context.ts +++ b/packages/server-context/src/server-context.ts @@ -14,10 +14,10 @@ export abstract class AlwatrServerContextBase< T extends AlwatrServiceResponse = AlwatrServiceResponse, > extends AlwatrApiRequestBase { protected _context?: T; - constructor(protected override _config: ServerRequestConfig) { - super(_config); + constructor(protected override config_: ServerRequestConfig) { + super(config_); - this._stateRecord = { + this.stateRecord_ = { initial: { request: 'offlineCheck', }, @@ -57,7 +57,7 @@ export abstract class AlwatrServerContextBase< }, }; - this._actionRecord = { + this.actionRecord_ = { _on_offlineCheck_enter: this._$offlineRequestAction, _on_loading_enter: this._$onlineRequestAction, _on_reloading_enter: this._$onlineRequestAction, @@ -66,24 +66,24 @@ export abstract class AlwatrServerContextBase< } protected _$offlineRequestAction(): void { - this._logger.logMethod?.('_$offlineRequestAction'); - this._$fetchOptions!.cacheStrategy === 'cache_only'; - this._$requestAction(); + this.logger_.logMethod?.('_$offlineRequestAction'); + this.fetchOptions__!.cacheStrategy === 'cache_only'; + this.requestAction__(); } protected _$onlineRequestAction(): void { - this._logger.logMethod?.('_$onlineRequestAction'); - this._$fetchOptions!.cacheStrategy === 'update_cache'; - this._$requestAction(); + this.logger_.logMethod?.('_$onlineRequestAction'); + this.fetchOptions__!.cacheStrategy === 'update_cache'; + this.requestAction__(); } protected _$updateContextAction(): void { if (this._responseJson === undefined) { - this._logger.accident('_$updateContextAction', 'no_response_json'); + this.logger_.accident('_$updateContextAction', 'no_response_json'); return; } - this._logger.logMethod?.('_$updateContextAction'); + this.logger_.logMethod?.('_$updateContextAction'); if ( this._context === undefined || @@ -96,15 +96,15 @@ export abstract class AlwatrServerContextBase< this._cleanup(); } - protected override async _$requestAction(): Promise { - this._logger.logMethod?.('_$requestAction'); + protected override async requestAction__(): Promise { + this.logger_.logMethod?.('_$requestAction'); try { - if (this._$fetchOptions === undefined) { + if (this.fetchOptions__ === undefined) { throw new Error('invalid_fetch_options'); } - await this._$fetch(this._$fetchOptions); + await this.fetch__(this.fetchOptions__); this._transition('requestSuccess'); } @@ -113,14 +113,14 @@ export abstract class AlwatrServerContextBase< this._transition('cacheNotFound'); } else { - this._logger.error('_$requestAction', 'fetch_failed', err); + this.logger_.error('_$requestAction', 'fetch_failed', err); this._transition('requestFailed'); } } } protected _cleanup(): void { - delete this._response; + delete this.response_; delete this._responseJson; } } @@ -140,6 +140,6 @@ export class AlwatrServerContext< } request(options?: Partial): void { - return this._request(options); + return this.request_(options); } } diff --git a/packages/server-context/src/server-request.ts b/packages/server-context/src/server-request.ts index 04c6a6ad..05e2233b 100644 --- a/packages/server-context/src/server-request.ts +++ b/packages/server-context/src/server-request.ts @@ -1,8 +1,7 @@ -import {fetch} from '@alwatr/fetch'; -import {ActionRecord, FiniteStateMachineBase, StateRecord} from '@alwatr/fsm'; +import {fetch, type FetchOptions} from '@alwatr/fetch'; +import {FiniteStateMachineBase, type StateRecord, type ActionRecord} from '@alwatr/fsm'; import {definePackage} from '@alwatr/logger'; -import type {FetchOptions} from '@alwatr/fetch'; import type {} from '@alwatr/nano-build'; definePackage('@alwatr/signal', __package_version__); @@ -18,10 +17,10 @@ export abstract class AlwatrServerRequestBase< ExtraState extends string = never, ExtraEvent extends string = never, > extends FiniteStateMachineBase { - protected _$fetchOptions?: FetchOptions; - protected _response?: Response; + protected fetchOptions__?: FetchOptions; + protected response_?: Response; - protected override _stateRecord = { + protected override stateRecord_ = { initial: { request: 'loading', }, @@ -37,56 +36,56 @@ export abstract class AlwatrServerRequestBase< }, } as StateRecord; - protected override _actionRecord = { - _on_loading_enter: this._$requestAction, + protected override actionRecord_ = { + _on_loading_enter: this.requestAction__, } as ActionRecord; - constructor(protected _config: ServerRequestConfig) { - super({name: _config.name, initialState: 'initial'}); + constructor(protected config_: ServerRequestConfig) { + super({name: config_.name, initialState: 'initial'}); } - protected _request(options?: Partial): void { - this._logger.logMethodArgs?.('_request', options); - this._setOptions(options); - this._transition('request'); + protected request_(options?: Partial): void { + this.logger_.logMethodArgs?.('request_', options); + this.setOptions_(options); + this.transition_('request'); } - protected async _$fetch(options: FetchOptions): Promise { - this._logger.logMethodArgs?.('_$fetch', options); - this._response = await fetch(options); + protected async fetch__(options: FetchOptions): Promise { + this.logger_.logMethodArgs?.('fetch__', options); + this.response_ = await fetch(options); - if (!this._response.ok) { + if (!this.response_.ok) { throw new Error('fetch_nok'); } } - protected async _$requestAction(): Promise { - this._logger.logMethod?.('_$requestAction'); + protected async requestAction__(): Promise { + this.logger_.logMethod?.('requestAction__'); try { - if (this._$fetchOptions === undefined) { + if (this.fetchOptions__ === undefined) { throw new Error('invalid_fetch_options'); } - await this._$fetch(this._$fetchOptions); + await this.fetch__(this.fetchOptions__); - this._transition('requestSuccess'); + this.transition_('requestSuccess'); } catch (err) { - this._logger.error('_$requestAction', 'fetch_failed', err); - this._transition('requestFailed'); + this.logger_.error('requestAction__', 'fetch_failed', err); + this.transition_('requestFailed'); } } - protected _setOptions(options?: Partial): void { - this._logger.logMethodArgs?.('_setOptions', {options}); + protected setOptions_(options?: Partial): void { + this.logger_.logMethodArgs?.('setOptions_', {options}); const fetchOptions = { - ...this._config, + ...this.config_, ...options, - queryParameters: { - ...this._config.queryParameters, - ...options?.queryParameters, + queryParams: { + ...this.config_.queryParams, + ...options?.queryParams, }, }; @@ -94,12 +93,12 @@ export abstract class AlwatrServerRequestBase< throw new Error('invalid_fetch_options'); } - this._$fetchOptions = fetchOptions as FetchOptions; + this.fetchOptions__ = fetchOptions as FetchOptions; } - protected override _reset(): void { - super._reset(); - delete this._response; + protected override resetToInitialState_(): void { + super.resetToInitialState_(); + delete this.response_; } } @@ -108,18 +107,21 @@ export class AlwatrServerRequest extends AlwatrServerRequestBase { * Current state. */ get state(): ServerRequestState { - return this._state; + return this.message_.state; } get response(): Response | undefined { - return this._response; + return this.response_; } request(options?: Partial): void { - return this._request(options); + return this.request_(options); } - cleanup(): void { - this._reset(); + /** + * Reset the machine to its initial state without notifying, and clean up existing response and state. + */ + reset(): void { + this.resetToInitialState_(); } } diff --git a/packages/signal/src/context.ts b/packages/signal/src/context.ts index 5885faf0..1ff2ae0f 100644 --- a/packages/signal/src/context.ts +++ b/packages/signal/src/context.ts @@ -17,7 +17,7 @@ export class AlwatrContext extends AlwatrObservable { * Return undefined if context not set before or expired. */ getValue(): T | undefined { - return this.data_; + return this.message_; } /** @@ -34,7 +34,7 @@ export class AlwatrContext extends AlwatrObservable { * `receivePrevious` in new subscribers not work until new context changes. */ expire(): void { - super.clearData_(); + super.clearMessage_(); } /** diff --git a/packages/signal/src/observable.ts b/packages/signal/src/observable.ts index 8524558c..6521de1a 100644 --- a/packages/signal/src/observable.ts +++ b/packages/signal/src/observable.ts @@ -2,16 +2,17 @@ import {createLogger, definePackage} from '@alwatr/logger'; import type {SubscribeOptions, ListenerCallback, Observer, SubscribeResult, AlwatrObservableInterface} from './type.js'; import type {} from '@alwatr/nano-build'; +import type {Dictionary} from '@alwatr/type-helper'; definePackage('@alwatr/signal', __package_version__); /** * Alwatr base signal. */ -export abstract class AlwatrObservable implements AlwatrObservableInterface { +export abstract class AlwatrObservable implements AlwatrObservableInterface { protected name_; protected logger_; - protected data_?: T; + protected message_?: T; protected observers__: Observer[] = []; constructor(config: {name: string; loggerPrefix?: string}) { @@ -24,16 +25,16 @@ export abstract class AlwatrObservable implements AlwatrObservableInterface this.dispatch__(data), 0); + protected notify_(message: T): void { + this.logger_.logMethodArgs?.('notify_', message); + this.message_ = message; + setTimeout(() => this.dispatch__(message), 0); } /** * Execute all observers callback. */ - protected dispatch__(data: T): void { + protected dispatch__(message: T): void { const removeList: Observer[] = []; for (const listener of this.observers__) { @@ -41,7 +42,7 @@ export abstract class AlwatrObservable implements AlwatrObservableInterface this.logger_.error('dispatch__', 'call_listener_failed', err)); } @@ -68,13 +69,13 @@ export abstract class AlwatrObservable implements AlwatrObservableInterface { try { - const ret = listenerCallback.call(this, data); + const ret = listenerCallback.call(this, message); if (ret instanceof Promise) { ret.catch((err) => this.logger_.error('subscribe.receivePrevious', 'call_signal_callback_failed', err)); } @@ -116,13 +117,13 @@ export abstract class AlwatrObservable implements AlwatrObservableInterface { this.logger_.logMethod?.('untilNewNotify_'); diff --git a/packages/signal/src/signal.ts b/packages/signal/src/signal.ts index d5fd2beb..b65e4862 100644 --- a/packages/signal/src/signal.ts +++ b/packages/signal/src/signal.ts @@ -1,9 +1,11 @@ import {AlwatrObservable} from './observable.js'; +import type { Dictionary } from '@alwatr/type-helper'; + /** - * Alwatr event signal. + * Alwatr event signal with special message (event detail). */ -export class AlwatrSignal extends AlwatrObservable { +export class AlwatrSignal extends AlwatrObservable { constructor(config: {name: string; loggerPrefix?: string}) { config.loggerPrefix ??= 'signal'; super(config); @@ -12,8 +14,8 @@ export class AlwatrSignal extends AlwatrObservable { /** * Dispatch an event to all listeners. */ - notify(detail: T): void { - this.notify_(detail); + notify(message: T): void { + this.notify_(message); } /** diff --git a/packages/signal/src/simple-signal.ts b/packages/signal/src/simple-signal.ts index 572f2430..4f875901 100644 --- a/packages/signal/src/simple-signal.ts +++ b/packages/signal/src/simple-signal.ts @@ -1,9 +1,9 @@ import {AlwatrObservable} from './observable.js'; /** - * Alwatr event signal without any data. + * Alwatr event signal without any message (no event detail). */ -export class AlwatrSimpleSignal extends AlwatrObservable { +export class AlwatrSimpleSignal extends AlwatrObservable { constructor(config: {name: string; loggerPrefix?: string}) { config.loggerPrefix ??= 'signal'; super(config); @@ -13,13 +13,13 @@ export class AlwatrSimpleSignal extends AlwatrObservable { * Dispatch an event to all listeners. */ notify(): void { - this.notify_(undefined); + this.notify_({}); } /** * Wait until next event signal. */ - untilNewNotify(): Promise { - return super.untilNewNotify_(); + async untilNewNotify(): Promise { + await super.untilNewNotify_(); } } diff --git a/packages/signal/src/type.ts b/packages/signal/src/type.ts index e8ed8057..703af4da 100644 --- a/packages/signal/src/type.ts +++ b/packages/signal/src/type.ts @@ -1,4 +1,4 @@ -import type {MaybePromise} from '@alwatr/type-helper'; +import type {Dictionary, MaybePromise} from '@alwatr/type-helper'; /** * Subscribe options type. @@ -30,10 +30,10 @@ export interface SubscribeOptions { // debounce?: 'AnimationFrame' | number; } -export type ListenerCallback = (this: T, detail: D) => MaybePromise; +export type ListenerCallback = (this: T, message: M) => MaybePromise; -export interface Observer { - callback: ListenerCallback; +export interface Observer { + callback: ListenerCallback; options: SubscribeOptions; } @@ -41,7 +41,7 @@ export interface SubscribeResult { unsubscribe: () => void; } -export interface AlwatrObservableInterface { +export interface AlwatrObservableInterface { subscribe(listenerCallback: ListenerCallback, options?: SubscribeOptions): SubscribeResult; unsubscribe(listenerCallback: ListenerCallback): void; }