@hexworks/cobalt-state
Version: 
State machine utility for Cobalt.
54 lines • 2.6 kB
JavaScript
/* eslint-disable @typescript-eslint/ban-types */
import { safeParseAsync } from "@hexworks/cobalt-core";
import * as RTE from "fp-ts/ReaderTaskEither";
import { pipe } from "fp-ts/lib/function";
import { Map } from "immutable";
import { ThisIsABugError, UnknownEventError, UnknownStateError, } from ".";
import { StateTransitionError } from "./errors/StateTransitionError";
export const newStateMachine = (possibleStates) => {
    const supportedStates = possibleStates.reduce((map, state) => map.set(state.name, state), Map());
    const supportedEventTypes = possibleStates
        .flatMap((state) => Object.keys(state.transitions))
        .toSet();
    const enter = (stateInstance) => {
        const { state, data } = stateInstance;
        const stateName = state.name;
        const expectedState = supportedStates.get(stateName);
        if (!expectedState || expectedState !== state) {
            return RTE.left(new UnknownStateError(stateName));
        }
        return pipe(RTE.fromTaskEither(safeParseAsync(state.schema)(data)), RTE.chain(() => state.onEntry(data)), RTE.map((data) => ({
            ...stateInstance,
            data,
        })));
    };
    const dispatch = (stateInstance, event) => {
        const { state, data } = stateInstance;
        const stateName = state.name;
        const expectedState = supportedStates.get(stateName);
        const eventType = event.type;
        if (expectedState && expectedState === state) {
            const transitions = state.transitions[eventType];
            if (transitions) {
                const transition = transitions.find((t) => t.condition(data, event));
                if (transition) {
                    return pipe(RTE.fromTaskEither(safeParseAsync(state.schema)(data)), RTE.chain(() => state.onExit(data)), RTE.chain((d) => transition.transitionTo(d, event)), RTE.chain((stateInstance) => pipe(stateInstance.state.onEntry(stateInstance.data), RTE.map((data) => ({
                        ...stateInstance,
                        data,
                    })))), RTE.mapLeft((e) => new StateTransitionError(state.name, e)));
                }
                else {
                    return RTE.left(new ThisIsABugError(event, data));
                }
            }
            else {
                return RTE.left(new UnknownEventError(state.name, event.type));
            }
        }
        else {
            return RTE.left(new UnknownStateError(stateName));
        }
    };
    return { supportedStates, supportedEventTypes, enter, dispatch };
};
//# sourceMappingURL=StateMachine.js.map