import { __assign, __spreadArray, __read, __rest } from './_virtual/_tslib.js'; import { EMPTY_ACTIVITY_MAP } from './constants.js'; import { isString, matchesState, warn } from './utils.js'; import { getMeta, nextEvents } from './stateUtils.js'; import { initEvent } from './actions.js'; import { IS_PRODUCTION } from './environment.js'; function stateValuesEqual(a, b) { if (a === b) { return true; } if (a === undefined || b === undefined) { return false; } if (isString(a) || isString(b)) { return a === b; } var aKeys = Object.keys(a); var bKeys = Object.keys(b); return aKeys.length === bKeys.length && aKeys.every(function (key) { return stateValuesEqual(a[key], b[key]); }); } function isStateConfig(state) { if (typeof state !== 'object' || state === null) { return false; } return 'value' in state && '_event' in state; } /** * @deprecated Use `isStateConfig(object)` or `state instanceof State` instead. */ var isState = isStateConfig; function bindActionToState(action, state) { var exec = action.exec; var boundAction = __assign(__assign({}, action), { exec: exec !== undefined ? function () { return exec(state.context, state.event, { action: action, state: state, _event: state._event }); } : undefined }); return boundAction; } var State = /*#__PURE__*/ /** @class */ function () { /** * Creates a new State instance. * @param value The state value * @param context The extended state * @param historyValue The tree representing historical values of the state nodes * @param history The previous state * @param actions An array of action objects to execute as side-effects * @param activities A mapping of activities and whether they are started (`true`) or stopped (`false`). * @param meta * @param events Internal event queue. Should be empty with run-to-completion semantics. * @param configuration */ function State(config) { var _this = this; var _a; this.actions = []; this.activities = EMPTY_ACTIVITY_MAP; this.meta = {}; this.events = []; this.value = config.value; this.context = config.context; this._event = config._event; this._sessionid = config._sessionid; this.event = this._event.data; this.historyValue = config.historyValue; this.history = config.history; this.actions = config.actions || []; this.activities = config.activities || EMPTY_ACTIVITY_MAP; this.meta = getMeta(config.configuration); this.events = config.events || []; this.matches = this.matches.bind(this); this.toStrings = this.toStrings.bind(this); this.configuration = config.configuration; this.transitions = config.transitions; this.children = config.children; this.done = !!config.done; this.tags = (_a = Array.isArray(config.tags) ? new Set(config.tags) : config.tags) !== null && _a !== void 0 ? _a : new Set(); this.machine = config.machine; Object.defineProperty(this, 'nextEvents', { get: function () { return nextEvents(_this.configuration); } }); } /** * Creates a new State instance for the given `stateValue` and `context`. * @param stateValue * @param context */ State.from = function (stateValue, context) { if (stateValue instanceof State) { if (stateValue.context !== context) { return new State({ value: stateValue.value, context: context, _event: stateValue._event, _sessionid: null, historyValue: stateValue.historyValue, history: stateValue.history, actions: [], activities: stateValue.activities, meta: {}, events: [], configuration: [], transitions: [], children: {} }); } return stateValue; } var _event = initEvent; return new State({ value: stateValue, context: context, _event: _event, _sessionid: null, historyValue: undefined, history: undefined, actions: [], activities: undefined, meta: undefined, events: [], configuration: [], transitions: [], children: {} }); }; /** * Creates a new State instance for the given `config`. * @param config The state config */ State.create = function (config) { return new State(config); }; /** * Creates a new `State` instance for the given `stateValue` and `context` with no actions (side-effects). * @param stateValue * @param context */ State.inert = function (stateValue, context) { if (stateValue instanceof State) { if (!stateValue.actions.length) { return stateValue; } var _event = initEvent; return new State({ value: stateValue.value, context: context, _event: _event, _sessionid: null, historyValue: stateValue.historyValue, history: stateValue.history, activities: stateValue.activities, configuration: stateValue.configuration, transitions: [], children: {} }); } return State.from(stateValue, context); }; /** * Returns an array of all the string leaf state node paths. * @param stateValue * @param delimiter The character(s) that separate each subpath in the string state node path. */ State.prototype.toStrings = function (stateValue, delimiter) { var _this = this; if (stateValue === void 0) { stateValue = this.value; } if (delimiter === void 0) { delimiter = '.'; } if (isString(stateValue)) { return [stateValue]; } var valueKeys = Object.keys(stateValue); return valueKeys.concat.apply(valueKeys, __spreadArray([], __read(valueKeys.map(function (key) { return _this.toStrings(stateValue[key], delimiter).map(function (s) { return key + delimiter + s; }); })), false)); }; State.prototype.toJSON = function () { var _a = this; _a.configuration; _a.transitions; var tags = _a.tags; _a.machine; var jsonValues = __rest(_a, ["configuration", "transitions", "tags", "machine"]); return __assign(__assign({}, jsonValues), { tags: Array.from(tags) }); }; State.prototype.matches = function (parentStateValue) { return matchesState(parentStateValue, this.value); }; /** * Whether the current state configuration has a state node with the specified `tag`. * @param tag */ State.prototype.hasTag = function (tag) { return this.tags.has(tag); }; /** * Determines whether sending the `event` will cause a non-forbidden transition * to be selected, even if the transitions have no actions nor * change the state value. * * @param event The event to test * @returns Whether the event will cause a transition */ State.prototype.can = function (event) { var _a; if (IS_PRODUCTION) { warn(!!this.machine, "state.can(...) used outside of a machine-created State object; this will always return false."); } var transitionData = (_a = this.machine) === null || _a === void 0 ? void 0 : _a.getTransitionData(this, event); return !!(transitionData === null || transitionData === void 0 ? void 0 : transitionData.transitions.length) && // Check that at least one transition is not forbidden transitionData.transitions.some(function (t) { return t.target !== undefined || t.actions.length; }); }; return State; }(); export { State, bindActionToState, isState, isStateConfig, stateValuesEqual };