State.js 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. import { __assign, __spreadArray, __read, __rest } from './_virtual/_tslib.js';
  2. import { EMPTY_ACTIVITY_MAP } from './constants.js';
  3. import { isString, matchesState, warn } from './utils.js';
  4. import { getMeta, nextEvents } from './stateUtils.js';
  5. import { initEvent } from './actions.js';
  6. import { IS_PRODUCTION } from './environment.js';
  7. function stateValuesEqual(a, b) {
  8. if (a === b) {
  9. return true;
  10. }
  11. if (a === undefined || b === undefined) {
  12. return false;
  13. }
  14. if (isString(a) || isString(b)) {
  15. return a === b;
  16. }
  17. var aKeys = Object.keys(a);
  18. var bKeys = Object.keys(b);
  19. return aKeys.length === bKeys.length && aKeys.every(function (key) {
  20. return stateValuesEqual(a[key], b[key]);
  21. });
  22. }
  23. function isStateConfig(state) {
  24. if (typeof state !== 'object' || state === null) {
  25. return false;
  26. }
  27. return 'value' in state && '_event' in state;
  28. }
  29. /**
  30. * @deprecated Use `isStateConfig(object)` or `state instanceof State` instead.
  31. */
  32. var isState = isStateConfig;
  33. function bindActionToState(action, state) {
  34. var exec = action.exec;
  35. var boundAction = __assign(__assign({}, action), {
  36. exec: exec !== undefined ? function () {
  37. return exec(state.context, state.event, {
  38. action: action,
  39. state: state,
  40. _event: state._event
  41. });
  42. } : undefined
  43. });
  44. return boundAction;
  45. }
  46. var State =
  47. /*#__PURE__*/
  48. /** @class */
  49. function () {
  50. /**
  51. * Creates a new State instance.
  52. * @param value The state value
  53. * @param context The extended state
  54. * @param historyValue The tree representing historical values of the state nodes
  55. * @param history The previous state
  56. * @param actions An array of action objects to execute as side-effects
  57. * @param activities A mapping of activities and whether they are started (`true`) or stopped (`false`).
  58. * @param meta
  59. * @param events Internal event queue. Should be empty with run-to-completion semantics.
  60. * @param configuration
  61. */
  62. function State(config) {
  63. var _this = this;
  64. var _a;
  65. this.actions = [];
  66. this.activities = EMPTY_ACTIVITY_MAP;
  67. this.meta = {};
  68. this.events = [];
  69. this.value = config.value;
  70. this.context = config.context;
  71. this._event = config._event;
  72. this._sessionid = config._sessionid;
  73. this.event = this._event.data;
  74. this.historyValue = config.historyValue;
  75. this.history = config.history;
  76. this.actions = config.actions || [];
  77. this.activities = config.activities || EMPTY_ACTIVITY_MAP;
  78. this.meta = getMeta(config.configuration);
  79. this.events = config.events || [];
  80. this.matches = this.matches.bind(this);
  81. this.toStrings = this.toStrings.bind(this);
  82. this.configuration = config.configuration;
  83. this.transitions = config.transitions;
  84. this.children = config.children;
  85. this.done = !!config.done;
  86. this.tags = (_a = Array.isArray(config.tags) ? new Set(config.tags) : config.tags) !== null && _a !== void 0 ? _a : new Set();
  87. this.machine = config.machine;
  88. Object.defineProperty(this, 'nextEvents', {
  89. get: function () {
  90. return nextEvents(_this.configuration);
  91. }
  92. });
  93. }
  94. /**
  95. * Creates a new State instance for the given `stateValue` and `context`.
  96. * @param stateValue
  97. * @param context
  98. */
  99. State.from = function (stateValue, context) {
  100. if (stateValue instanceof State) {
  101. if (stateValue.context !== context) {
  102. return new State({
  103. value: stateValue.value,
  104. context: context,
  105. _event: stateValue._event,
  106. _sessionid: null,
  107. historyValue: stateValue.historyValue,
  108. history: stateValue.history,
  109. actions: [],
  110. activities: stateValue.activities,
  111. meta: {},
  112. events: [],
  113. configuration: [],
  114. transitions: [],
  115. children: {}
  116. });
  117. }
  118. return stateValue;
  119. }
  120. var _event = initEvent;
  121. return new State({
  122. value: stateValue,
  123. context: context,
  124. _event: _event,
  125. _sessionid: null,
  126. historyValue: undefined,
  127. history: undefined,
  128. actions: [],
  129. activities: undefined,
  130. meta: undefined,
  131. events: [],
  132. configuration: [],
  133. transitions: [],
  134. children: {}
  135. });
  136. };
  137. /**
  138. * Creates a new State instance for the given `config`.
  139. * @param config The state config
  140. */
  141. State.create = function (config) {
  142. return new State(config);
  143. };
  144. /**
  145. * Creates a new `State` instance for the given `stateValue` and `context` with no actions (side-effects).
  146. * @param stateValue
  147. * @param context
  148. */
  149. State.inert = function (stateValue, context) {
  150. if (stateValue instanceof State) {
  151. if (!stateValue.actions.length) {
  152. return stateValue;
  153. }
  154. var _event = initEvent;
  155. return new State({
  156. value: stateValue.value,
  157. context: context,
  158. _event: _event,
  159. _sessionid: null,
  160. historyValue: stateValue.historyValue,
  161. history: stateValue.history,
  162. activities: stateValue.activities,
  163. configuration: stateValue.configuration,
  164. transitions: [],
  165. children: {}
  166. });
  167. }
  168. return State.from(stateValue, context);
  169. };
  170. /**
  171. * Returns an array of all the string leaf state node paths.
  172. * @param stateValue
  173. * @param delimiter The character(s) that separate each subpath in the string state node path.
  174. */
  175. State.prototype.toStrings = function (stateValue, delimiter) {
  176. var _this = this;
  177. if (stateValue === void 0) {
  178. stateValue = this.value;
  179. }
  180. if (delimiter === void 0) {
  181. delimiter = '.';
  182. }
  183. if (isString(stateValue)) {
  184. return [stateValue];
  185. }
  186. var valueKeys = Object.keys(stateValue);
  187. return valueKeys.concat.apply(valueKeys, __spreadArray([], __read(valueKeys.map(function (key) {
  188. return _this.toStrings(stateValue[key], delimiter).map(function (s) {
  189. return key + delimiter + s;
  190. });
  191. })), false));
  192. };
  193. State.prototype.toJSON = function () {
  194. var _a = this;
  195. _a.configuration;
  196. _a.transitions;
  197. var tags = _a.tags;
  198. _a.machine;
  199. var jsonValues = __rest(_a, ["configuration", "transitions", "tags", "machine"]);
  200. return __assign(__assign({}, jsonValues), {
  201. tags: Array.from(tags)
  202. });
  203. };
  204. State.prototype.matches = function (parentStateValue) {
  205. return matchesState(parentStateValue, this.value);
  206. };
  207. /**
  208. * Whether the current state configuration has a state node with the specified `tag`.
  209. * @param tag
  210. */
  211. State.prototype.hasTag = function (tag) {
  212. return this.tags.has(tag);
  213. };
  214. /**
  215. * Determines whether sending the `event` will cause a non-forbidden transition
  216. * to be selected, even if the transitions have no actions nor
  217. * change the state value.
  218. *
  219. * @param event The event to test
  220. * @returns Whether the event will cause a transition
  221. */
  222. State.prototype.can = function (event) {
  223. var _a;
  224. if (IS_PRODUCTION) {
  225. warn(!!this.machine, "state.can(...) used outside of a machine-created State object; this will always return false.");
  226. }
  227. var transitionData = (_a = this.machine) === null || _a === void 0 ? void 0 : _a.getTransitionData(this, event);
  228. return !!(transitionData === null || transitionData === void 0 ? void 0 : transitionData.transitions.length) && // Check that at least one transition is not forbidden
  229. transitionData.transitions.some(function (t) {
  230. return t.target !== undefined || t.actions.length;
  231. });
  232. };
  233. return State;
  234. }();
  235. export { State, bindActionToState, isState, isStateConfig, stateValuesEqual };