UNPKG

44.9 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', { value: true });
4
5var _tslib = require('./_virtual/_tslib.js');
6var types = require('./types.js');
7var State = require('./State.js');
8var actionTypes = require('./actionTypes.js');
9var actions = require('./actions.js');
10var environment = require('./environment.js');
11var utils = require('./utils.js');
12var scheduler = require('./scheduler.js');
13var Actor = require('./Actor.js');
14var registry = require('./registry.js');
15var devTools = require('./devTools.js');
16var serviceScope = require('./serviceScope.js');
17var behaviors = require('./behaviors.js');
18
19var DEFAULT_SPAWN_OPTIONS = {
20 sync: false,
21 autoForward: false
22};
23exports.InterpreterStatus = void 0;
24
25(function (InterpreterStatus) {
26 InterpreterStatus[InterpreterStatus["NotStarted"] = 0] = "NotStarted";
27 InterpreterStatus[InterpreterStatus["Running"] = 1] = "Running";
28 InterpreterStatus[InterpreterStatus["Stopped"] = 2] = "Stopped";
29})(exports.InterpreterStatus || (exports.InterpreterStatus = {}));
30
31var Interpreter =
32/*#__PURE__*/
33
34/** @class */
35function () {
36 /**
37 * Creates a new Interpreter instance (i.e., service) for the given machine with the provided options, if any.
38 *
39 * @param machine The machine to be interpreted
40 * @param options Interpreter options
41 */
42 function Interpreter(machine, options) {
43 if (options === void 0) {
44 options = Interpreter.defaultOptions;
45 }
46
47 var _this = this;
48
49 this.machine = machine;
50 this.delayedEventsMap = {};
51 this.listeners = new Set();
52 this.contextListeners = new Set();
53 this.stopListeners = new Set();
54 this.doneListeners = new Set();
55 this.eventListeners = new Set();
56 this.sendListeners = new Set();
57 /**
58 * Whether the service is started.
59 */
60
61 this.initialized = false;
62 this.status = exports.InterpreterStatus.NotStarted;
63 this.children = new Map();
64 this.forwardTo = new Set();
65 this._outgoingQueue = [];
66 /**
67 * Alias for Interpreter.prototype.start
68 */
69
70 this.init = this.start;
71 /**
72 * Sends an event to the running interpreter to trigger a transition.
73 *
74 * An array of events (batched) can be sent as well, which will send all
75 * batched events to the running interpreter. The listeners will be
76 * notified only **once** when all events are processed.
77 *
78 * @param event The event(s) to send
79 */
80
81 this.send = function (event, payload) {
82 if (utils.isArray(event)) {
83 _this.batch(event);
84
85 return _this.state;
86 }
87
88 var _event = utils.toSCXMLEvent(utils.toEventObject(event, payload));
89
90 if (_this.status === exports.InterpreterStatus.Stopped) {
91 // do nothing
92 if (!environment.IS_PRODUCTION) {
93 utils.warn(false, "Event \"".concat(_event.name, "\" was sent to stopped service \"").concat(_this.machine.id, "\". This service has already reached its final state, and will not transition.\nEvent: ").concat(JSON.stringify(_event.data)));
94 }
95
96 return _this.state;
97 }
98
99 if (_this.status !== exports.InterpreterStatus.Running && !_this.options.deferEvents) {
100 throw new Error("Event \"".concat(_event.name, "\" was sent to uninitialized service \"").concat(_this.machine.id // tslint:disable-next-line:max-line-length
101 , "\". Make sure .start() is called for this service, or set { deferEvents: true } in the service options.\nEvent: ").concat(JSON.stringify(_event.data)));
102 }
103
104 _this.scheduler.schedule(function () {
105 // Forward copy of event to child actors
106 _this.forward(_event);
107
108 var nextState = _this._nextState(_event);
109
110 _this.update(nextState, _event);
111 });
112
113 return _this._state; // TODO: deprecate (should return void)
114 // tslint:disable-next-line:semicolon
115 };
116
117 this.sendTo = function (event, to, immediate) {
118 var isParent = _this.parent && (to === types.SpecialTargets.Parent || _this.parent.id === to);
119 var target = isParent ? _this.parent : utils.isString(to) ? to === types.SpecialTargets.Internal ? _this : _this.children.get(to) || registry.registry.get(to) : utils.isActor(to) ? to : undefined;
120
121 if (!target) {
122 if (!isParent) {
123 throw new Error("Unable to send event to child '".concat(to, "' from service '").concat(_this.id, "'."));
124 } // tslint:disable-next-line:no-console
125
126
127 if (!environment.IS_PRODUCTION) {
128 utils.warn(false, "Service '".concat(_this.id, "' has no parent: unable to send event ").concat(event.type));
129 }
130
131 return;
132 }
133
134 if ('machine' in target) {
135 // perhaps those events should be rejected in the parent
136 // but atm it doesn't have easy access to all of the information that is required to do it reliably
137 if (_this.status !== exports.InterpreterStatus.Stopped || _this.parent !== target || // we need to send events to the parent from exit handlers of a machine that reached its final state
138 _this.state.done) {
139 // Send SCXML events to machines
140 var scxmlEvent = _tslib.__assign(_tslib.__assign({}, event), {
141 name: event.name === actionTypes.error ? "".concat(actions.error(_this.id)) : event.name,
142 origin: _this.sessionId
143 });
144
145 if (!immediate && _this.machine.config.predictableActionArguments) {
146 _this._outgoingQueue.push([target, scxmlEvent]);
147 } else {
148 target.send(scxmlEvent);
149 }
150 }
151 } else {
152 // Send normal events to other targets
153 if (!immediate && _this.machine.config.predictableActionArguments) {
154 _this._outgoingQueue.push([target, event.data]);
155 } else {
156 target.send(event.data);
157 }
158 }
159 };
160
161 this._exec = function (action, context, _event, actionFunctionMap) {
162 if (actionFunctionMap === void 0) {
163 actionFunctionMap = _this.machine.options.actions;
164 }
165
166 var actionOrExec = action.exec || actions.getActionFunction(action.type, actionFunctionMap);
167 var exec = utils.isFunction(actionOrExec) ? actionOrExec : actionOrExec ? actionOrExec.exec : action.exec;
168
169 if (exec) {
170 try {
171 return exec(context, _event.data, !_this.machine.config.predictableActionArguments ? {
172 action: action,
173 state: _this.state,
174 _event: _event
175 } : {
176 action: action,
177 _event: _event
178 });
179 } catch (err) {
180 if (_this.parent) {
181 _this.parent.send({
182 type: 'xstate.error',
183 data: err
184 });
185 }
186
187 throw err;
188 }
189 }
190
191 switch (action.type) {
192 case actionTypes.raise:
193 {
194 // if raise action reached the interpreter then it's a delayed one
195 var sendAction_1 = action;
196
197 _this.defer(sendAction_1);
198
199 break;
200 }
201
202 case actionTypes.send:
203 var sendAction = action;
204
205 if (typeof sendAction.delay === 'number') {
206 _this.defer(sendAction);
207
208 return;
209 } else {
210 if (sendAction.to) {
211 _this.sendTo(sendAction._event, sendAction.to, _event === actions.initEvent);
212 } else {
213 _this.send(sendAction._event);
214 }
215 }
216
217 break;
218
219 case actionTypes.cancel:
220 _this.cancel(action.sendId);
221
222 break;
223
224 case actionTypes.start:
225 {
226 if (_this.status !== exports.InterpreterStatus.Running) {
227 return;
228 }
229
230 var activity = action.activity; // If the activity will be stopped right after it's started
231 // (such as in transient states)
232 // don't bother starting the activity.
233
234 if ( // in v4 with `predictableActionArguments` invokes are called eagerly when the `this.state` still points to the previous state
235 !_this.machine.config.predictableActionArguments && !_this.state.activities[activity.id || activity.type]) {
236 break;
237 } // Invoked services
238
239
240 if (activity.type === types.ActionTypes.Invoke) {
241 var invokeSource = utils.toInvokeSource(activity.src);
242 var serviceCreator = _this.machine.options.services ? _this.machine.options.services[invokeSource.type] : undefined;
243 var id = activity.id,
244 data = activity.data;
245
246 if (!environment.IS_PRODUCTION) {
247 utils.warn(!('forward' in activity), // tslint:disable-next-line:max-line-length
248 "`forward` property is deprecated (found in invocation of '".concat(activity.src, "' in in machine '").concat(_this.machine.id, "'). ") + "Please use `autoForward` instead.");
249 }
250
251 var autoForward = 'autoForward' in activity ? activity.autoForward : !!activity.forward;
252
253 if (!serviceCreator) {
254 // tslint:disable-next-line:no-console
255 if (!environment.IS_PRODUCTION) {
256 utils.warn(false, "No service found for invocation '".concat(activity.src, "' in machine '").concat(_this.machine.id, "'."));
257 }
258
259 return;
260 }
261
262 var resolvedData = data ? utils.mapContext(data, context, _event) : undefined;
263
264 if (typeof serviceCreator === 'string') {
265 // TODO: warn
266 return;
267 }
268
269 var source = utils.isFunction(serviceCreator) ? serviceCreator(context, _event.data, {
270 data: resolvedData,
271 src: invokeSource,
272 meta: activity.meta
273 }) : serviceCreator;
274
275 if (!source) {
276 // TODO: warn?
277 return;
278 }
279
280 var options = void 0;
281
282 if (utils.isMachine(source)) {
283 source = resolvedData ? source.withContext(resolvedData) : source;
284 options = {
285 autoForward: autoForward
286 };
287 }
288
289 _this.spawn(source, id, options);
290 } else {
291 _this.spawnActivity(activity);
292 }
293
294 break;
295 }
296
297 case actionTypes.stop:
298 {
299 _this.stopChild(action.activity.id);
300
301 break;
302 }
303
304 case actionTypes.log:
305 var _a = action,
306 label = _a.label,
307 value = _a.value;
308
309 if (label) {
310 _this.logger(label, value);
311 } else {
312 _this.logger(value);
313 }
314
315 break;
316
317 default:
318 if (!environment.IS_PRODUCTION) {
319 utils.warn(false, "No implementation found for action type '".concat(action.type, "'"));
320 }
321
322 break;
323 }
324 };
325
326 var resolvedOptions = _tslib.__assign(_tslib.__assign({}, Interpreter.defaultOptions), options);
327
328 var clock = resolvedOptions.clock,
329 logger = resolvedOptions.logger,
330 parent = resolvedOptions.parent,
331 id = resolvedOptions.id;
332 var resolvedId = id !== undefined ? id : machine.id;
333 this.id = resolvedId;
334 this.logger = logger;
335 this.clock = clock;
336 this.parent = parent;
337 this.options = resolvedOptions;
338 this.scheduler = new scheduler.Scheduler({
339 deferEvents: this.options.deferEvents
340 });
341 this.sessionId = registry.registry.bookId();
342 }
343
344 Object.defineProperty(Interpreter.prototype, "initialState", {
345 get: function () {
346 var _this = this;
347
348 if (this._initialState) {
349 return this._initialState;
350 }
351
352 return serviceScope.provide(this, function () {
353 _this._initialState = _this.machine.initialState;
354 return _this._initialState;
355 });
356 },
357 enumerable: false,
358 configurable: true
359 });
360 Object.defineProperty(Interpreter.prototype, "state", {
361 /**
362 * @deprecated Use `.getSnapshot()` instead.
363 */
364 get: function () {
365 if (!environment.IS_PRODUCTION) {
366 utils.warn(this.status !== exports.InterpreterStatus.NotStarted, "Attempted to read state from uninitialized service '".concat(this.id, "'. Make sure the service is started first."));
367 }
368
369 return this._state;
370 },
371 enumerable: false,
372 configurable: true
373 });
374 /**
375 * Executes the actions of the given state, with that state's `context` and `event`.
376 *
377 * @param state The state whose actions will be executed
378 * @param actionsConfig The action implementations to use
379 */
380
381 Interpreter.prototype.execute = function (state, actionsConfig) {
382 var e_1, _a;
383
384 try {
385 for (var _b = _tslib.__values(state.actions), _c = _b.next(); !_c.done; _c = _b.next()) {
386 var action = _c.value;
387 this.exec(action, state, actionsConfig);
388 }
389 } catch (e_1_1) {
390 e_1 = {
391 error: e_1_1
392 };
393 } finally {
394 try {
395 if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
396 } finally {
397 if (e_1) throw e_1.error;
398 }
399 }
400 };
401
402 Interpreter.prototype.update = function (state, _event) {
403 var e_2, _a, e_3, _b, e_4, _c, e_5, _d;
404
405 var _this = this; // Attach session ID to state
406
407
408 state._sessionid = this.sessionId; // Update state
409
410 this._state = state; // Execute actions
411
412 if ((!this.machine.config.predictableActionArguments || // this is currently required to execute initial actions as the `initialState` gets cached
413 // we can't just recompute it (and execute actions while doing so) because we try to preserve identity of actors created within initial assigns
414 _event === actions.initEvent) && this.options.execute) {
415 this.execute(this.state);
416 } else {
417 var item = void 0;
418
419 while (item = this._outgoingQueue.shift()) {
420 item[0].send(item[1]);
421 }
422 } // Update children
423
424
425 this.children.forEach(function (child) {
426 _this.state.children[child.id] = child;
427 }); // Dev tools
428
429 if (this.devTools) {
430 this.devTools.send(_event.data, state);
431 } // Execute listeners
432
433
434 if (state.event) {
435 try {
436 for (var _e = _tslib.__values(this.eventListeners), _f = _e.next(); !_f.done; _f = _e.next()) {
437 var listener = _f.value;
438 listener(state.event);
439 }
440 } catch (e_2_1) {
441 e_2 = {
442 error: e_2_1
443 };
444 } finally {
445 try {
446 if (_f && !_f.done && (_a = _e.return)) _a.call(_e);
447 } finally {
448 if (e_2) throw e_2.error;
449 }
450 }
451 }
452
453 try {
454 for (var _g = _tslib.__values(this.listeners), _h = _g.next(); !_h.done; _h = _g.next()) {
455 var listener = _h.value;
456 listener(state, state.event);
457 }
458 } catch (e_3_1) {
459 e_3 = {
460 error: e_3_1
461 };
462 } finally {
463 try {
464 if (_h && !_h.done && (_b = _g.return)) _b.call(_g);
465 } finally {
466 if (e_3) throw e_3.error;
467 }
468 }
469
470 try {
471 for (var _j = _tslib.__values(this.contextListeners), _k = _j.next(); !_k.done; _k = _j.next()) {
472 var contextListener = _k.value;
473 contextListener(this.state.context, this.state.history ? this.state.history.context : undefined);
474 }
475 } catch (e_4_1) {
476 e_4 = {
477 error: e_4_1
478 };
479 } finally {
480 try {
481 if (_k && !_k.done && (_c = _j.return)) _c.call(_j);
482 } finally {
483 if (e_4) throw e_4.error;
484 }
485 }
486
487 if (this.state.done) {
488 // get final child state node
489 var finalChildStateNode = state.configuration.find(function (sn) {
490 return sn.type === 'final' && sn.parent === _this.machine;
491 });
492 var doneData = finalChildStateNode && finalChildStateNode.doneData ? utils.mapContext(finalChildStateNode.doneData, state.context, _event) : undefined;
493 this._doneEvent = actions.doneInvoke(this.id, doneData);
494
495 try {
496 for (var _l = _tslib.__values(this.doneListeners), _m = _l.next(); !_m.done; _m = _l.next()) {
497 var listener = _m.value;
498 listener(this._doneEvent);
499 }
500 } catch (e_5_1) {
501 e_5 = {
502 error: e_5_1
503 };
504 } finally {
505 try {
506 if (_m && !_m.done && (_d = _l.return)) _d.call(_l);
507 } finally {
508 if (e_5) throw e_5.error;
509 }
510 }
511
512 this._stop();
513
514 this._stopChildren();
515
516 registry.registry.free(this.sessionId);
517 }
518 };
519 /*
520 * Adds a listener that is notified whenever a state transition happens. The listener is called with
521 * the next state and the event object that caused the state transition.
522 *
523 * @param listener The state listener
524 */
525
526
527 Interpreter.prototype.onTransition = function (listener) {
528 this.listeners.add(listener); // Send current state to listener
529
530 if (this.status === exports.InterpreterStatus.Running) {
531 listener(this.state, this.state.event);
532 }
533
534 return this;
535 };
536
537 Interpreter.prototype.subscribe = function (nextListenerOrObserver, _, // TODO: error listener
538 completeListener) {
539 var _this = this;
540
541 var observer = utils.toObserver(nextListenerOrObserver, _, completeListener);
542 this.listeners.add(observer.next); // Send current state to listener
543
544 if (this.status !== exports.InterpreterStatus.NotStarted) {
545 observer.next(this.state);
546 }
547
548 var completeOnce = function () {
549 _this.doneListeners.delete(completeOnce);
550
551 _this.stopListeners.delete(completeOnce);
552
553 observer.complete();
554 };
555
556 if (this.status === exports.InterpreterStatus.Stopped) {
557 observer.complete();
558 } else {
559 this.onDone(completeOnce);
560 this.onStop(completeOnce);
561 }
562
563 return {
564 unsubscribe: function () {
565 _this.listeners.delete(observer.next);
566
567 _this.doneListeners.delete(completeOnce);
568
569 _this.stopListeners.delete(completeOnce);
570 }
571 };
572 };
573 /**
574 * Adds an event listener that is notified whenever an event is sent to the running interpreter.
575 * @param listener The event listener
576 */
577
578
579 Interpreter.prototype.onEvent = function (listener) {
580 this.eventListeners.add(listener);
581 return this;
582 };
583 /**
584 * Adds an event listener that is notified whenever a `send` event occurs.
585 * @param listener The event listener
586 */
587
588
589 Interpreter.prototype.onSend = function (listener) {
590 this.sendListeners.add(listener);
591 return this;
592 };
593 /**
594 * Adds a context listener that is notified whenever the state context changes.
595 * @param listener The context listener
596 */
597
598
599 Interpreter.prototype.onChange = function (listener) {
600 this.contextListeners.add(listener);
601 return this;
602 };
603 /**
604 * Adds a listener that is notified when the machine is stopped.
605 * @param listener The listener
606 */
607
608
609 Interpreter.prototype.onStop = function (listener) {
610 this.stopListeners.add(listener);
611 return this;
612 };
613 /**
614 * Adds a state listener that is notified when the statechart has reached its final state.
615 * @param listener The state listener
616 */
617
618
619 Interpreter.prototype.onDone = function (listener) {
620 if (this.status === exports.InterpreterStatus.Stopped && this._doneEvent) {
621 listener(this._doneEvent);
622 } else {
623 this.doneListeners.add(listener);
624 }
625
626 return this;
627 };
628 /**
629 * Removes a listener.
630 * @param listener The listener to remove
631 */
632
633
634 Interpreter.prototype.off = function (listener) {
635 this.listeners.delete(listener);
636 this.eventListeners.delete(listener);
637 this.sendListeners.delete(listener);
638 this.stopListeners.delete(listener);
639 this.doneListeners.delete(listener);
640 this.contextListeners.delete(listener);
641 return this;
642 };
643 /**
644 * Starts the interpreter from the given state, or the initial state.
645 * @param initialState The state to start the statechart from
646 */
647
648
649 Interpreter.prototype.start = function (initialState) {
650 var _this = this;
651
652 if (this.status === exports.InterpreterStatus.Running) {
653 // Do not restart the service if it is already started
654 return this;
655 } // yes, it's a hack but we need the related cache to be populated for some things to work (like delayed transitions)
656 // this is usually called by `machine.getInitialState` but if we rehydrate from a state we might bypass this call
657 // we also don't want to call this method here as it resolves the full initial state which might involve calling assign actions
658 // and that could potentially lead to some unwanted side-effects (even such as creating some rogue actors)
659
660
661 this.machine._init();
662
663 registry.registry.register(this.sessionId, this);
664 this.initialized = true;
665 this.status = exports.InterpreterStatus.Running;
666 var resolvedState = initialState === undefined ? this.initialState : serviceScope.provide(this, function () {
667 return State.isStateConfig(initialState) ? _this.machine.resolveState(initialState) : _this.machine.resolveState(State.State.from(initialState, _this.machine.context));
668 });
669
670 if (this.options.devTools) {
671 this.attachDev();
672 }
673
674 this.scheduler.initialize(function () {
675 _this.update(resolvedState, actions.initEvent);
676 });
677 return this;
678 };
679
680 Interpreter.prototype._stopChildren = function () {
681 // TODO: think about converting those to actions
682 this.children.forEach(function (child) {
683 if (utils.isFunction(child.stop)) {
684 child.stop();
685 }
686 });
687 this.children.clear();
688 };
689
690 Interpreter.prototype._stop = function () {
691 var e_6, _a, e_7, _b, e_8, _c, e_9, _d, e_10, _e;
692
693 try {
694 for (var _f = _tslib.__values(this.listeners), _g = _f.next(); !_g.done; _g = _f.next()) {
695 var listener = _g.value;
696 this.listeners.delete(listener);
697 }
698 } catch (e_6_1) {
699 e_6 = {
700 error: e_6_1
701 };
702 } finally {
703 try {
704 if (_g && !_g.done && (_a = _f.return)) _a.call(_f);
705 } finally {
706 if (e_6) throw e_6.error;
707 }
708 }
709
710 try {
711 for (var _h = _tslib.__values(this.stopListeners), _j = _h.next(); !_j.done; _j = _h.next()) {
712 var listener = _j.value; // call listener, then remove
713
714 listener();
715 this.stopListeners.delete(listener);
716 }
717 } catch (e_7_1) {
718 e_7 = {
719 error: e_7_1
720 };
721 } finally {
722 try {
723 if (_j && !_j.done && (_b = _h.return)) _b.call(_h);
724 } finally {
725 if (e_7) throw e_7.error;
726 }
727 }
728
729 try {
730 for (var _k = _tslib.__values(this.contextListeners), _l = _k.next(); !_l.done; _l = _k.next()) {
731 var listener = _l.value;
732 this.contextListeners.delete(listener);
733 }
734 } catch (e_8_1) {
735 e_8 = {
736 error: e_8_1
737 };
738 } finally {
739 try {
740 if (_l && !_l.done && (_c = _k.return)) _c.call(_k);
741 } finally {
742 if (e_8) throw e_8.error;
743 }
744 }
745
746 try {
747 for (var _m = _tslib.__values(this.doneListeners), _o = _m.next(); !_o.done; _o = _m.next()) {
748 var listener = _o.value;
749 this.doneListeners.delete(listener);
750 }
751 } catch (e_9_1) {
752 e_9 = {
753 error: e_9_1
754 };
755 } finally {
756 try {
757 if (_o && !_o.done && (_d = _m.return)) _d.call(_m);
758 } finally {
759 if (e_9) throw e_9.error;
760 }
761 }
762
763 if (!this.initialized) {
764 // Interpreter already stopped; do nothing
765 return this;
766 }
767
768 this.initialized = false;
769 this.status = exports.InterpreterStatus.Stopped;
770 this._initialState = undefined;
771
772 try {
773 // we are going to stop within the current sync frame
774 // so we can safely just cancel this here as nothing async should be fired anyway
775 for (var _p = _tslib.__values(Object.keys(this.delayedEventsMap)), _q = _p.next(); !_q.done; _q = _p.next()) {
776 var key = _q.value;
777 this.clock.clearTimeout(this.delayedEventsMap[key]);
778 }
779 } catch (e_10_1) {
780 e_10 = {
781 error: e_10_1
782 };
783 } finally {
784 try {
785 if (_q && !_q.done && (_e = _p.return)) _e.call(_p);
786 } finally {
787 if (e_10) throw e_10.error;
788 }
789 } // clear everything that might be enqueued
790
791
792 this.scheduler.clear();
793 this.scheduler = new scheduler.Scheduler({
794 deferEvents: this.options.deferEvents
795 });
796 };
797 /**
798 * Stops the interpreter and unsubscribe all listeners.
799 *
800 * This will also notify the `onStop` listeners.
801 */
802
803
804 Interpreter.prototype.stop = function () {
805 // TODO: add warning for stopping non-root interpreters
806 var _this = this; // grab the current scheduler as it will be replaced in _stop
807
808
809 var scheduler = this.scheduler;
810
811 this._stop(); // let what is currently processed to be finished
812
813
814 scheduler.schedule(function () {
815 var _a;
816
817 if ((_a = _this._state) === null || _a === void 0 ? void 0 : _a.done) {
818 return;
819 } // it feels weird to handle this here but we need to handle this even slightly "out of band"
820
821
822 var _event = utils.toSCXMLEvent({
823 type: 'xstate.stop'
824 });
825
826 var nextState = serviceScope.provide(_this, function () {
827 var exitActions = utils.flatten(_tslib.__spreadArray([], _tslib.__read(_this.state.configuration), false).sort(function (a, b) {
828 return b.order - a.order;
829 }).map(function (stateNode) {
830 return actions.toActionObjects(stateNode.onExit, _this.machine.options.actions);
831 }));
832
833 var _a = _tslib.__read(actions.resolveActions(_this.machine, _this.state, _this.state.context, _event, [{
834 type: 'exit',
835 actions: exitActions
836 }], _this.machine.config.predictableActionArguments ? _this._exec : undefined, _this.machine.config.predictableActionArguments || _this.machine.config.preserveActionOrder), 2),
837 resolvedActions = _a[0],
838 updatedContext = _a[1];
839
840 var newState = new State.State({
841 value: _this.state.value,
842 context: updatedContext,
843 _event: _event,
844 _sessionid: _this.sessionId,
845 historyValue: undefined,
846 history: _this.state,
847 actions: resolvedActions.filter(function (action) {
848 return !utils.isRaisableAction(action);
849 }),
850 activities: {},
851 events: [],
852 configuration: [],
853 transitions: [],
854 children: {},
855 done: _this.state.done,
856 tags: _this.state.tags,
857 machine: _this.machine
858 });
859 newState.changed = true;
860 return newState;
861 });
862
863 _this.update(nextState, _event);
864
865 _this._stopChildren();
866
867 registry.registry.free(_this.sessionId);
868 });
869 return this;
870 };
871
872 Interpreter.prototype.batch = function (events) {
873 var _this = this;
874
875 if (this.status === exports.InterpreterStatus.NotStarted && this.options.deferEvents) {
876 // tslint:disable-next-line:no-console
877 if (!environment.IS_PRODUCTION) {
878 utils.warn(false, "".concat(events.length, " event(s) were sent to uninitialized service \"").concat(this.machine.id, "\" and are deferred. Make sure .start() is called for this service.\nEvent: ").concat(JSON.stringify(event)));
879 }
880 } else if (this.status !== exports.InterpreterStatus.Running) {
881 throw new Error( // tslint:disable-next-line:max-line-length
882 "".concat(events.length, " event(s) were sent to uninitialized service \"").concat(this.machine.id, "\". Make sure .start() is called for this service, or set { deferEvents: true } in the service options."));
883 }
884
885 if (!events.length) {
886 return;
887 }
888
889 var exec = !!this.machine.config.predictableActionArguments && this._exec;
890 this.scheduler.schedule(function () {
891 var e_11, _a;
892
893 var nextState = _this.state;
894 var batchChanged = false;
895 var batchedActions = [];
896
897 var _loop_1 = function (event_1) {
898 var _event = utils.toSCXMLEvent(event_1);
899
900 _this.forward(_event);
901
902 nextState = serviceScope.provide(_this, function () {
903 return _this.machine.transition(nextState, _event, undefined, exec || undefined);
904 });
905 batchedActions.push.apply(batchedActions, _tslib.__spreadArray([], _tslib.__read(_this.machine.config.predictableActionArguments ? nextState.actions : nextState.actions.map(function (a) {
906 return State.bindActionToState(a, nextState);
907 })), false));
908 batchChanged = batchChanged || !!nextState.changed;
909 };
910
911 try {
912 for (var events_1 = _tslib.__values(events), events_1_1 = events_1.next(); !events_1_1.done; events_1_1 = events_1.next()) {
913 var event_1 = events_1_1.value;
914
915 _loop_1(event_1);
916 }
917 } catch (e_11_1) {
918 e_11 = {
919 error: e_11_1
920 };
921 } finally {
922 try {
923 if (events_1_1 && !events_1_1.done && (_a = events_1.return)) _a.call(events_1);
924 } finally {
925 if (e_11) throw e_11.error;
926 }
927 }
928
929 nextState.changed = batchChanged;
930 nextState.actions = batchedActions;
931
932 _this.update(nextState, utils.toSCXMLEvent(events[events.length - 1]));
933 });
934 };
935 /**
936 * Returns a send function bound to this interpreter instance.
937 *
938 * @param event The event to be sent by the sender.
939 */
940
941
942 Interpreter.prototype.sender = function (event) {
943 return this.send.bind(this, event);
944 };
945
946 Interpreter.prototype._nextState = function (event, exec) {
947 var _this = this;
948
949 if (exec === void 0) {
950 exec = !!this.machine.config.predictableActionArguments && this._exec;
951 }
952
953 var _event = utils.toSCXMLEvent(event);
954
955 if (_event.name.indexOf(actionTypes.errorPlatform) === 0 && !this.state.nextEvents.some(function (nextEvent) {
956 return nextEvent.indexOf(actionTypes.errorPlatform) === 0;
957 })) {
958 throw _event.data.data;
959 }
960
961 var nextState = serviceScope.provide(this, function () {
962 return _this.machine.transition(_this.state, _event, undefined, exec || undefined);
963 });
964 return nextState;
965 };
966 /**
967 * Returns the next state given the interpreter's current state and the event.
968 *
969 * This is a pure method that does _not_ update the interpreter's state.
970 *
971 * @param event The event to determine the next state
972 */
973
974
975 Interpreter.prototype.nextState = function (event) {
976 return this._nextState(event, false);
977 };
978
979 Interpreter.prototype.forward = function (event) {
980 var e_12, _a;
981
982 try {
983 for (var _b = _tslib.__values(this.forwardTo), _c = _b.next(); !_c.done; _c = _b.next()) {
984 var id = _c.value;
985 var child = this.children.get(id);
986
987 if (!child) {
988 throw new Error("Unable to forward event '".concat(event, "' from interpreter '").concat(this.id, "' to nonexistant child '").concat(id, "'."));
989 }
990
991 child.send(event);
992 }
993 } catch (e_12_1) {
994 e_12 = {
995 error: e_12_1
996 };
997 } finally {
998 try {
999 if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
1000 } finally {
1001 if (e_12) throw e_12.error;
1002 }
1003 }
1004 };
1005
1006 Interpreter.prototype.defer = function (sendAction) {
1007 var _this = this;
1008
1009 var timerId = this.clock.setTimeout(function () {
1010 if ('to' in sendAction && sendAction.to) {
1011 _this.sendTo(sendAction._event, sendAction.to, true);
1012 } else {
1013 _this.send(sendAction._event);
1014 }
1015 }, sendAction.delay);
1016
1017 if (sendAction.id) {
1018 this.delayedEventsMap[sendAction.id] = timerId;
1019 }
1020 };
1021
1022 Interpreter.prototype.cancel = function (sendId) {
1023 this.clock.clearTimeout(this.delayedEventsMap[sendId]);
1024 delete this.delayedEventsMap[sendId];
1025 };
1026
1027 Interpreter.prototype.exec = function (action, state, actionFunctionMap) {
1028 if (actionFunctionMap === void 0) {
1029 actionFunctionMap = this.machine.options.actions;
1030 }
1031
1032 this._exec(action, state.context, state._event, actionFunctionMap);
1033 };
1034
1035 Interpreter.prototype.removeChild = function (childId) {
1036 var _a;
1037
1038 this.children.delete(childId);
1039 this.forwardTo.delete(childId); // this.state might not exist at the time this is called,
1040 // such as when a child is added then removed while initializing the state
1041
1042 (_a = this.state) === null || _a === void 0 ? true : delete _a.children[childId];
1043 };
1044
1045 Interpreter.prototype.stopChild = function (childId) {
1046 var child = this.children.get(childId);
1047
1048 if (!child) {
1049 return;
1050 }
1051
1052 this.removeChild(childId);
1053
1054 if (utils.isFunction(child.stop)) {
1055 child.stop();
1056 }
1057 };
1058
1059 Interpreter.prototype.spawn = function (entity, name, options) {
1060 if (this.status !== exports.InterpreterStatus.Running) {
1061 return Actor.createDeferredActor(entity, name);
1062 }
1063
1064 if (utils.isPromiseLike(entity)) {
1065 return this.spawnPromise(Promise.resolve(entity), name);
1066 } else if (utils.isFunction(entity)) {
1067 return this.spawnCallback(entity, name);
1068 } else if (Actor.isSpawnedActor(entity)) {
1069 return this.spawnActor(entity, name);
1070 } else if (utils.isObservable(entity)) {
1071 return this.spawnObservable(entity, name);
1072 } else if (utils.isMachine(entity)) {
1073 return this.spawnMachine(entity, _tslib.__assign(_tslib.__assign({}, options), {
1074 id: name
1075 }));
1076 } else if (utils.isBehavior(entity)) {
1077 return this.spawnBehavior(entity, name);
1078 } else {
1079 throw new Error("Unable to spawn entity \"".concat(name, "\" of type \"").concat(typeof entity, "\"."));
1080 }
1081 };
1082
1083 Interpreter.prototype.spawnMachine = function (machine, options) {
1084 var _this = this;
1085
1086 if (options === void 0) {
1087 options = {};
1088 }
1089
1090 var childService = new Interpreter(machine, _tslib.__assign(_tslib.__assign({}, this.options), {
1091 parent: this,
1092 id: options.id || machine.id
1093 }));
1094
1095 var resolvedOptions = _tslib.__assign(_tslib.__assign({}, DEFAULT_SPAWN_OPTIONS), options);
1096
1097 if (resolvedOptions.sync) {
1098 childService.onTransition(function (state) {
1099 _this.send(actionTypes.update, {
1100 state: state,
1101 id: childService.id
1102 });
1103 });
1104 }
1105
1106 var actor = childService;
1107 this.children.set(childService.id, actor);
1108
1109 if (resolvedOptions.autoForward) {
1110 this.forwardTo.add(childService.id);
1111 }
1112
1113 childService.onDone(function (doneEvent) {
1114 _this.removeChild(childService.id);
1115
1116 _this.send(utils.toSCXMLEvent(doneEvent, {
1117 origin: childService.id
1118 }));
1119 }).start();
1120 return actor;
1121 };
1122
1123 Interpreter.prototype.spawnBehavior = function (behavior, id) {
1124 var actorRef = behaviors.spawnBehavior(behavior, {
1125 id: id,
1126 parent: this
1127 });
1128 this.children.set(id, actorRef);
1129 return actorRef;
1130 };
1131
1132 Interpreter.prototype.spawnPromise = function (promise, id) {
1133 var _a;
1134
1135 var _this = this;
1136
1137 var canceled = false;
1138 var resolvedData;
1139 promise.then(function (response) {
1140 if (!canceled) {
1141 resolvedData = response;
1142
1143 _this.removeChild(id);
1144
1145 _this.send(utils.toSCXMLEvent(actions.doneInvoke(id, response), {
1146 origin: id
1147 }));
1148 }
1149 }, function (errorData) {
1150 if (!canceled) {
1151 _this.removeChild(id);
1152
1153 var errorEvent = actions.error(id, errorData);
1154
1155 try {
1156 // Send "error.platform.id" to this (parent).
1157 _this.send(utils.toSCXMLEvent(errorEvent, {
1158 origin: id
1159 }));
1160 } catch (error) {
1161 utils.reportUnhandledExceptionOnInvocation(errorData, error, id);
1162
1163 if (_this.devTools) {
1164 _this.devTools.send(errorEvent, _this.state);
1165 }
1166
1167 if (_this.machine.strict) {
1168 // it would be better to always stop the state machine if unhandled
1169 // exception/promise rejection happens but because we don't want to
1170 // break existing code so enforce it on strict mode only especially so
1171 // because documentation says that onError is optional
1172 _this.stop();
1173 }
1174 }
1175 }
1176 });
1177 var actor = (_a = {
1178 id: id,
1179 send: function () {
1180 return void 0;
1181 },
1182 subscribe: function (next, handleError, complete) {
1183 var observer = utils.toObserver(next, handleError, complete);
1184 var unsubscribed = false;
1185 promise.then(function (response) {
1186 if (unsubscribed) {
1187 return;
1188 }
1189
1190 observer.next(response);
1191
1192 if (unsubscribed) {
1193 return;
1194 }
1195
1196 observer.complete();
1197 }, function (err) {
1198 if (unsubscribed) {
1199 return;
1200 }
1201
1202 observer.error(err);
1203 });
1204 return {
1205 unsubscribe: function () {
1206 return unsubscribed = true;
1207 }
1208 };
1209 },
1210 stop: function () {
1211 canceled = true;
1212 },
1213 toJSON: function () {
1214 return {
1215 id: id
1216 };
1217 },
1218 getSnapshot: function () {
1219 return resolvedData;
1220 }
1221 }, _a[utils.symbolObservable] = function () {
1222 return this;
1223 }, _a);
1224 this.children.set(id, actor);
1225 return actor;
1226 };
1227
1228 Interpreter.prototype.spawnCallback = function (callback, id) {
1229 var _a;
1230
1231 var _this = this;
1232
1233 var canceled = false;
1234 var receivers = new Set();
1235 var listeners = new Set();
1236 var emitted;
1237
1238 var receive = function (e) {
1239 emitted = e;
1240 listeners.forEach(function (listener) {
1241 return listener(e);
1242 });
1243
1244 if (canceled) {
1245 return;
1246 }
1247
1248 _this.send(utils.toSCXMLEvent(e, {
1249 origin: id
1250 }));
1251 };
1252
1253 var callbackStop;
1254
1255 try {
1256 callbackStop = callback(receive, function (newListener) {
1257 receivers.add(newListener);
1258 });
1259 } catch (err) {
1260 this.send(actions.error(id, err));
1261 }
1262
1263 if (utils.isPromiseLike(callbackStop)) {
1264 // it turned out to be an async function, can't reliably check this before calling `callback`
1265 // because transpiled async functions are not recognizable
1266 return this.spawnPromise(callbackStop, id);
1267 }
1268
1269 var actor = (_a = {
1270 id: id,
1271 send: function (event) {
1272 return receivers.forEach(function (receiver) {
1273 return receiver(event);
1274 });
1275 },
1276 subscribe: function (next) {
1277 var observer = utils.toObserver(next);
1278 listeners.add(observer.next);
1279 return {
1280 unsubscribe: function () {
1281 listeners.delete(observer.next);
1282 }
1283 };
1284 },
1285 stop: function () {
1286 canceled = true;
1287
1288 if (utils.isFunction(callbackStop)) {
1289 callbackStop();
1290 }
1291 },
1292 toJSON: function () {
1293 return {
1294 id: id
1295 };
1296 },
1297 getSnapshot: function () {
1298 return emitted;
1299 }
1300 }, _a[utils.symbolObservable] = function () {
1301 return this;
1302 }, _a);
1303 this.children.set(id, actor);
1304 return actor;
1305 };
1306
1307 Interpreter.prototype.spawnObservable = function (source, id) {
1308 var _a;
1309
1310 var _this = this;
1311
1312 var emitted;
1313 var subscription = source.subscribe(function (value) {
1314 emitted = value;
1315
1316 _this.send(utils.toSCXMLEvent(value, {
1317 origin: id
1318 }));
1319 }, function (err) {
1320 _this.removeChild(id);
1321
1322 _this.send(utils.toSCXMLEvent(actions.error(id, err), {
1323 origin: id
1324 }));
1325 }, function () {
1326 _this.removeChild(id);
1327
1328 _this.send(utils.toSCXMLEvent(actions.doneInvoke(id), {
1329 origin: id
1330 }));
1331 });
1332 var actor = (_a = {
1333 id: id,
1334 send: function () {
1335 return void 0;
1336 },
1337 subscribe: function (next, handleError, complete) {
1338 return source.subscribe(next, handleError, complete);
1339 },
1340 stop: function () {
1341 return subscription.unsubscribe();
1342 },
1343 getSnapshot: function () {
1344 return emitted;
1345 },
1346 toJSON: function () {
1347 return {
1348 id: id
1349 };
1350 }
1351 }, _a[utils.symbolObservable] = function () {
1352 return this;
1353 }, _a);
1354 this.children.set(id, actor);
1355 return actor;
1356 };
1357
1358 Interpreter.prototype.spawnActor = function (actor, name) {
1359 this.children.set(name, actor);
1360 return actor;
1361 };
1362
1363 Interpreter.prototype.spawnActivity = function (activity) {
1364 var implementation = this.machine.options && this.machine.options.activities ? this.machine.options.activities[activity.type] : undefined;
1365
1366 if (!implementation) {
1367 if (!environment.IS_PRODUCTION) {
1368 utils.warn(false, "No implementation found for activity '".concat(activity.type, "'"));
1369 } // tslint:disable-next-line:no-console
1370
1371
1372 return;
1373 } // Start implementation
1374
1375
1376 var dispose = implementation(this.state.context, activity);
1377 this.spawnEffect(activity.id, dispose);
1378 };
1379
1380 Interpreter.prototype.spawnEffect = function (id, dispose) {
1381 var _a;
1382
1383 this.children.set(id, (_a = {
1384 id: id,
1385 send: function () {
1386 return void 0;
1387 },
1388 subscribe: function () {
1389 return {
1390 unsubscribe: function () {
1391 return void 0;
1392 }
1393 };
1394 },
1395 stop: dispose || undefined,
1396 getSnapshot: function () {
1397 return undefined;
1398 },
1399 toJSON: function () {
1400 return {
1401 id: id
1402 };
1403 }
1404 }, _a[utils.symbolObservable] = function () {
1405 return this;
1406 }, _a));
1407 };
1408
1409 Interpreter.prototype.attachDev = function () {
1410 var global = devTools.getGlobal();
1411
1412 if (this.options.devTools && global) {
1413 if (global.__REDUX_DEVTOOLS_EXTENSION__) {
1414 var devToolsOptions = typeof this.options.devTools === 'object' ? this.options.devTools : undefined;
1415 this.devTools = global.__REDUX_DEVTOOLS_EXTENSION__.connect(_tslib.__assign(_tslib.__assign({
1416 name: this.id,
1417 autoPause: true,
1418 stateSanitizer: function (state) {
1419 return {
1420 value: state.value,
1421 context: state.context,
1422 actions: state.actions
1423 };
1424 }
1425 }, devToolsOptions), {
1426 features: _tslib.__assign({
1427 jump: false,
1428 skip: false
1429 }, devToolsOptions ? devToolsOptions.features : undefined)
1430 }), this.machine);
1431 this.devTools.init(this.state);
1432 } // add XState-specific dev tooling hook
1433
1434
1435 devTools.registerService(this);
1436 }
1437 };
1438
1439 Interpreter.prototype.toJSON = function () {
1440 return {
1441 id: this.id
1442 };
1443 };
1444
1445 Interpreter.prototype[utils.symbolObservable] = function () {
1446 return this;
1447 };
1448
1449 Interpreter.prototype.getSnapshot = function () {
1450 if (this.status === exports.InterpreterStatus.NotStarted) {
1451 return this.initialState;
1452 }
1453
1454 return this._state;
1455 };
1456 /**
1457 * The default interpreter options:
1458 *
1459 * - `clock` uses the global `setTimeout` and `clearTimeout` functions
1460 * - `logger` uses the global `console.log()` method
1461 */
1462
1463
1464 Interpreter.defaultOptions = {
1465 execute: true,
1466 deferEvents: true,
1467 clock: {
1468 setTimeout: function (fn, ms) {
1469 return setTimeout(fn, ms);
1470 },
1471 clearTimeout: function (id) {
1472 return clearTimeout(id);
1473 }
1474 },
1475 logger: /*#__PURE__*/console.log.bind(console),
1476 devTools: false
1477 };
1478 Interpreter.interpret = interpret;
1479 return Interpreter;
1480}();
1481
1482var resolveSpawnOptions = function (nameOrOptions) {
1483 if (utils.isString(nameOrOptions)) {
1484 return _tslib.__assign(_tslib.__assign({}, DEFAULT_SPAWN_OPTIONS), {
1485 name: nameOrOptions
1486 });
1487 }
1488
1489 return _tslib.__assign(_tslib.__assign(_tslib.__assign({}, DEFAULT_SPAWN_OPTIONS), {
1490 name: utils.uniqueId()
1491 }), nameOrOptions);
1492};
1493
1494function spawn(entity, nameOrOptions) {
1495 var resolvedOptions = resolveSpawnOptions(nameOrOptions);
1496 return serviceScope.consume(function (service) {
1497 if (!environment.IS_PRODUCTION) {
1498 var isLazyEntity = utils.isMachine(entity) || utils.isFunction(entity);
1499 utils.warn(!!service || isLazyEntity, "Attempted to spawn an Actor (ID: \"".concat(utils.isMachine(entity) ? entity.id : 'undefined', "\") outside of a service. This will have no effect."));
1500 }
1501
1502 if (service) {
1503 return service.spawn(entity, resolvedOptions.name, resolvedOptions);
1504 } else {
1505 return Actor.createDeferredActor(entity, resolvedOptions.name);
1506 }
1507 });
1508}
1509/**
1510 * Creates a new Interpreter instance for the given machine with the provided options, if any.
1511 *
1512 * @param machine The machine to interpret
1513 * @param options Interpreter options
1514 */
1515
1516function interpret(machine, options) {
1517 var interpreter = new Interpreter(machine, options);
1518 return interpreter;
1519}
1520
1521exports.Interpreter = Interpreter;
1522exports.interpret = interpret;
1523exports.spawn = spawn;