UNPKG

state-switch

Version:

State Switch is a Change Monitor/Guarder for Async Actions.

99 lines 4.54 kB
/** * Licenst: Apache-2.0 * https://github.com/huan/state-switch */ import { createMachine, interpret, } from 'xstate'; import { VERSION } from '../version.js'; import { ServiceableAbstract, } from '../interfaces.js'; import { config, } from './machine-config.js'; import { buildMachineOptions } from './machine-options.js'; import { waitForMachineState } from './wait-for-selector.js'; import { guardMachineEvent } from './guard-machine-event.js'; import { getLoggable } from 'brolog'; import { StateSwitch } from '../state-switch.js'; const serviceCtlFsmMixin = (serviceCtlName = 'ServiceCtlFsm', options) => (superClass) => { class ServiceCtlFsmMixin extends superClass { static VERSION = VERSION; /** * Huan(202110): this state is simple record the start/stop status */ state; __serviceCtlLogger; __serviceCtlFsmInterpreter; constructor(...args) { super(...args); this.__serviceCtlLogger = getLoggable(options?.log); this.__serviceCtlLogger.verbose(`ServiceCtlFsm<${serviceCtlName}>`, 'constructor()'); this.state = new StateSwitch(serviceCtlName, options); const machineOptions = buildMachineOptions({ // reset: () => has been internally implemented by calling stop() and start() start: async () => { if (typeof super.start === 'function') { await super.start(); } await this.onStart(); }, stop: async () => { await this.onStop(); if (typeof super.stop === 'function') { await super.stop(); } }, }); const machine = createMachine(config, machineOptions); this.__serviceCtlFsmInterpreter = interpret(machine); this.__serviceCtlFsmInterpreter.start(); } start() { this.__serviceCtlLogger.verbose(`ServiceCtlFsm<${serviceCtlName}>`, 'start()'); guardMachineEvent(this.__serviceCtlFsmInterpreter, 'START'); const started = waitForMachineState(this.__serviceCtlFsmInterpreter, 'active'); const canceled = waitForMachineState(this.__serviceCtlFsmInterpreter, 'canceled'); this.__serviceCtlFsmInterpreter.send('START'); this.state.active(true); return Promise.race([ started, canceled, ]).then(() => { this.__serviceCtlLogger.verbose(`ServiceCtlFsm<${serviceCtlName}>`, 'start() ... done'); return undefined; }); } stop() { this.__serviceCtlLogger.verbose(`ServiceCtlFsm<${serviceCtlName}>`, 'stop()'); guardMachineEvent(this.__serviceCtlFsmInterpreter, 'STOP'); const stopped = waitForMachineState(this.__serviceCtlFsmInterpreter, 'inactive'); const canceled = waitForMachineState(this.__serviceCtlFsmInterpreter, 'canceled'); this.__serviceCtlFsmInterpreter.send('STOP'); this.state.inactive(true); return Promise.race([ stopped, canceled, ]).then(() => { this.__serviceCtlLogger.verbose(`ServiceCtlFsm<${serviceCtlName}>`, 'stop() ... done'); return undefined; }); } reset() { this.__serviceCtlLogger.verbose(`ServiceCtlFsm<${serviceCtlName}>`, 'reset()'); guardMachineEvent(this.__serviceCtlFsmInterpreter, 'RESET'); const started = waitForMachineState(this.__serviceCtlFsmInterpreter, 'active'); const canceled = waitForMachineState(this.__serviceCtlFsmInterpreter, 'canceled'); this.__serviceCtlFsmInterpreter.send('RESET'); // TODO: emit('error' e) if there's any rejections inside `reset()` // or the error should be handled by the onStart/onStop ? return Promise.race([ started, canceled, ]).then(() => { this.__serviceCtlLogger.verbose(`ServiceCtlFsm<${serviceCtlName}>`, 'reset() ... done'); return undefined; }); } } return ServiceCtlFsmMixin; }; class ServiceCtlFsm extends serviceCtlFsmMixin()(ServiceableAbstract) { } export { ServiceCtlFsm, serviceCtlFsmMixin, }; //# sourceMappingURL=service-ctl-fsm.js.map