overmind
Version:
Frictionless state management
105 lines (103 loc) • 3.57 kB
JavaScript
import { isStateMachine } from "./utils.js";
import { PATH, PROXY_TREE, VALUE } from "proxy-state-tree";
import isPlainObject from "is-plain-obj";
//#region src/statemachine.ts
const INITIAL_STATE = Symbol("INITIAL_STATE");
const TRANSITIONS = Symbol("TRANSITIONS");
const STATE = Symbol("STATE");
const IS_DISPOSED = Symbol("IS_DISPOSED");
const CURRENT_KEYS = Symbol("CURRENT_KEYS");
const BASE_STATE = Symbol("BASE_STATE");
const TRANSITION_LISTENERS = Symbol("TRANSITION_LISTENERS");
function deepCopy(obj) {
if (isStateMachine(obj)) return obj.clone();
else if (isPlainObject(obj)) return Object.keys(obj).reduce((aggr, key) => {
if (key === "__esModule") return aggr;
const originalDescriptor = Object.getOwnPropertyDescriptor(obj, key);
const isAGetter = originalDescriptor && "get" in originalDescriptor;
const value = obj[key];
if (isAGetter) Object.defineProperty(aggr, key, originalDescriptor);
else aggr[key] = deepCopy(value);
return aggr;
}, {});
else if (Array.isArray(obj)) return obj.map((item) => deepCopy(item));
return obj;
}
var StateMachine = class StateMachine {
clone() {
return new StateMachine(this[TRANSITIONS], deepCopy(this[STATE]), deepCopy(this[BASE_STATE]));
}
dispose() {
this[VALUE][TRANSITION_LISTENERS] = [];
Object.keys(this[VALUE]).forEach((key) => {
if (this[VALUE][key] instanceof StateMachine) this[key].dispose();
});
this[VALUE][IS_DISPOSED] = true;
}
constructor(transitions, state, baseState) {
this[TRANSITION_LISTENERS] = [];
this[IS_DISPOSED] = false;
this[STATE] = state;
this[BASE_STATE] = baseState;
this[INITIAL_STATE] = state.current;
this[TRANSITIONS] = transitions;
this[CURRENT_KEYS] = Object.keys(state);
Object.assign(this, state, baseState);
}
send(type, data) {
if (this[VALUE][IS_DISPOSED]) {
if (process.env.NODE_ENV === "development") console.warn(`Overmind - The statemachine at "${this[PATH]}" has been disposed, but you tried to transition on it`);
return this;
}
const tree = this[PROXY_TREE].root.mutationTree || this[PROXY_TREE];
tree.enableMutations();
let result;
if (typeof this[VALUE][TRANSITIONS] === "function") {
const transition = this[VALUE][TRANSITIONS];
result = transition({
type,
data
}, this);
} else if (this[VALUE][TRANSITIONS][this[VALUE].current][type]) {
const transition = this[VALUE][TRANSITIONS][this[VALUE].current][type];
result = transition(data, this);
}
if (result) {
this[VALUE].previousState = this[VALUE].current;
this[VALUE][CURRENT_KEYS].forEach((key) => {
if (key !== "current") delete this[key];
});
this[VALUE][CURRENT_KEYS] = Object.keys(result);
Object.assign(this, result);
const devtools = tree.root.options.getDevtools?.();
if (devtools && typeof devtools.send === "function") devtools.send({
type: "machine:transition",
data: {
path: this[PATH],
fromState: this[VALUE].previousState,
toState: this[VALUE].current,
eventType: type,
payload: data,
timestamp: Date.now()
}
});
this[VALUE][TRANSITION_LISTENERS].forEach((listener) => listener(this));
}
tree.blockMutations();
return this;
}
matches(state) {
if (this.current === state) return this;
}
onTransition(listener) {
this[VALUE][TRANSITION_LISTENERS].push(listener);
}
};
function statemachine(transitions) {
return { create(state, baseState) {
return new StateMachine(transitions, state, baseState);
} };
}
//#endregion
export { StateMachine, deepCopy, statemachine };
//# sourceMappingURL=statemachine.js.map