UNPKG

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