xstate
Version:
Finite State Machines and Statecharts for the Modern Web.
283 lines (241 loc) • 7.95 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var _tslib = require('./_virtual/_tslib.js');
var constants = require('./constants.js');
var utils = require('./utils.js');
var stateUtils = require('./stateUtils.js');
var actions = require('./actions.js');
var environment = require('./environment.js');
function stateValuesEqual(a, b) {
if (a === b) {
return true;
}
if (a === undefined || b === undefined) {
return false;
}
if (utils.isString(a) || utils.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 = _tslib.__assign(_tslib.__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 = constants.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 || constants.EMPTY_ACTIVITY_MAP;
this.meta = stateUtils.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 stateUtils.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 = actions.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 = actions.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 (utils.isString(stateValue)) {
return [stateValue];
}
var valueKeys = Object.keys(stateValue);
return valueKeys.concat.apply(valueKeys, _tslib.__spreadArray([], _tslib.__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 = _tslib.__rest(_a, ["configuration", "transitions", "tags", "machine"]);
return _tslib.__assign(_tslib.__assign({}, jsonValues), {
tags: Array.from(tags)
});
};
State.prototype.matches = function (parentStateValue) {
return utils.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 (environment.IS_PRODUCTION) {
utils.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;
}();
exports.State = State;
exports.bindActionToState = bindActionToState;
exports.isState = isState;
exports.isStateConfig = isStateConfig;
exports.stateValuesEqual = stateValuesEqual;