12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517 |
- import { __values, __spreadArray, __read, __assign } from './_virtual/_tslib.js';
- import { SpecialTargets, ActionTypes } from './types.js';
- import { isStateConfig, State, bindActionToState } from './State.js';
- import { errorPlatform, update, error as error$1, log, stop, start, cancel, send, raise } from './actionTypes.js';
- import { initEvent, doneInvoke, toActionObjects, resolveActions, error, getActionFunction } from './actions.js';
- import { IS_PRODUCTION } from './environment.js';
- import { warn, mapContext, toObserver, isFunction, toSCXMLEvent, flatten, isRaisableAction, isPromiseLike, isObservable, isMachine, isBehavior, reportUnhandledExceptionOnInvocation, symbolObservable, isArray, toEventObject, isString, isActor, toInvokeSource, uniqueId } from './utils.js';
- import { Scheduler } from './scheduler.js';
- import { createDeferredActor, isSpawnedActor } from './Actor.js';
- import { registry } from './registry.js';
- import { getGlobal, registerService } from './devTools.js';
- import { provide, consume } from './serviceScope.js';
- import { spawnBehavior } from './behaviors.js';
- var DEFAULT_SPAWN_OPTIONS = {
- sync: false,
- autoForward: false
- };
- var InterpreterStatus;
- (function (InterpreterStatus) {
- InterpreterStatus[InterpreterStatus["NotStarted"] = 0] = "NotStarted";
- InterpreterStatus[InterpreterStatus["Running"] = 1] = "Running";
- InterpreterStatus[InterpreterStatus["Stopped"] = 2] = "Stopped";
- })(InterpreterStatus || (InterpreterStatus = {}));
- var Interpreter =
- /*#__PURE__*/
- /** @class */
- function () {
- /**
- * Creates a new Interpreter instance (i.e., service) for the given machine with the provided options, if any.
- *
- * @param machine The machine to be interpreted
- * @param options Interpreter options
- */
- function Interpreter(machine, options) {
- if (options === void 0) {
- options = Interpreter.defaultOptions;
- }
- var _this = this;
- this.machine = machine;
- this.delayedEventsMap = {};
- this.listeners = new Set();
- this.contextListeners = new Set();
- this.stopListeners = new Set();
- this.doneListeners = new Set();
- this.eventListeners = new Set();
- this.sendListeners = new Set();
- /**
- * Whether the service is started.
- */
- this.initialized = false;
- this.status = InterpreterStatus.NotStarted;
- this.children = new Map();
- this.forwardTo = new Set();
- this._outgoingQueue = [];
- /**
- * Alias for Interpreter.prototype.start
- */
- this.init = this.start;
- /**
- * Sends an event to the running interpreter to trigger a transition.
- *
- * An array of events (batched) can be sent as well, which will send all
- * batched events to the running interpreter. The listeners will be
- * notified only **once** when all events are processed.
- *
- * @param event The event(s) to send
- */
- this.send = function (event, payload) {
- if (isArray(event)) {
- _this.batch(event);
- return _this.state;
- }
- var _event = toSCXMLEvent(toEventObject(event, payload));
- if (_this.status === InterpreterStatus.Stopped) {
- // do nothing
- if (!IS_PRODUCTION) {
- 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)));
- }
- return _this.state;
- }
- if (_this.status !== InterpreterStatus.Running && !_this.options.deferEvents) {
- throw new Error("Event \"".concat(_event.name, "\" was sent to uninitialized service \"").concat(_this.machine.id // tslint:disable-next-line:max-line-length
- , "\". Make sure .start() is called for this service, or set { deferEvents: true } in the service options.\nEvent: ").concat(JSON.stringify(_event.data)));
- }
- _this.scheduler.schedule(function () {
- // Forward copy of event to child actors
- _this.forward(_event);
- var nextState = _this._nextState(_event);
- _this.update(nextState, _event);
- });
- return _this._state; // TODO: deprecate (should return void)
- // tslint:disable-next-line:semicolon
- };
- this.sendTo = function (event, to, immediate) {
- var isParent = _this.parent && (to === SpecialTargets.Parent || _this.parent.id === to);
- var target = isParent ? _this.parent : isString(to) ? to === SpecialTargets.Internal ? _this : _this.children.get(to) || registry.get(to) : isActor(to) ? to : undefined;
- if (!target) {
- if (!isParent) {
- throw new Error("Unable to send event to child '".concat(to, "' from service '").concat(_this.id, "'."));
- } // tslint:disable-next-line:no-console
- if (!IS_PRODUCTION) {
- warn(false, "Service '".concat(_this.id, "' has no parent: unable to send event ").concat(event.type));
- }
- return;
- }
- if ('machine' in target) {
- // perhaps those events should be rejected in the parent
- // but atm it doesn't have easy access to all of the information that is required to do it reliably
- 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
- _this.state.done) {
- // Send SCXML events to machines
- var scxmlEvent = __assign(__assign({}, event), {
- name: event.name === error$1 ? "".concat(error(_this.id)) : event.name,
- origin: _this.sessionId
- });
- if (!immediate && _this.machine.config.predictableActionArguments) {
- _this._outgoingQueue.push([target, scxmlEvent]);
- } else {
- target.send(scxmlEvent);
- }
- }
- } else {
- // Send normal events to other targets
- if (!immediate && _this.machine.config.predictableActionArguments) {
- _this._outgoingQueue.push([target, event.data]);
- } else {
- target.send(event.data);
- }
- }
- };
- this._exec = function (action, context, _event, actionFunctionMap) {
- if (actionFunctionMap === void 0) {
- actionFunctionMap = _this.machine.options.actions;
- }
- var actionOrExec = action.exec || getActionFunction(action.type, actionFunctionMap);
- var exec = isFunction(actionOrExec) ? actionOrExec : actionOrExec ? actionOrExec.exec : action.exec;
- if (exec) {
- try {
- return exec(context, _event.data, !_this.machine.config.predictableActionArguments ? {
- action: action,
- state: _this.state,
- _event: _event
- } : {
- action: action,
- _event: _event
- });
- } catch (err) {
- if (_this.parent) {
- _this.parent.send({
- type: 'xstate.error',
- data: err
- });
- }
- throw err;
- }
- }
- switch (action.type) {
- case raise:
- {
- // if raise action reached the interpreter then it's a delayed one
- var sendAction_1 = action;
- _this.defer(sendAction_1);
- break;
- }
- case send:
- var sendAction = action;
- if (typeof sendAction.delay === 'number') {
- _this.defer(sendAction);
- return;
- } else {
- if (sendAction.to) {
- _this.sendTo(sendAction._event, sendAction.to, _event === initEvent);
- } else {
- _this.send(sendAction._event);
- }
- }
- break;
- case cancel:
- _this.cancel(action.sendId);
- break;
- case start:
- {
- if (_this.status !== InterpreterStatus.Running) {
- return;
- }
- var activity = action.activity; // If the activity will be stopped right after it's started
- // (such as in transient states)
- // don't bother starting the activity.
- if ( // in v4 with `predictableActionArguments` invokes are called eagerly when the `this.state` still points to the previous state
- !_this.machine.config.predictableActionArguments && !_this.state.activities[activity.id || activity.type]) {
- break;
- } // Invoked services
- if (activity.type === ActionTypes.Invoke) {
- var invokeSource = toInvokeSource(activity.src);
- var serviceCreator = _this.machine.options.services ? _this.machine.options.services[invokeSource.type] : undefined;
- var id = activity.id,
- data = activity.data;
- if (!IS_PRODUCTION) {
- warn(!('forward' in activity), // tslint:disable-next-line:max-line-length
- "`forward` property is deprecated (found in invocation of '".concat(activity.src, "' in in machine '").concat(_this.machine.id, "'). ") + "Please use `autoForward` instead.");
- }
- var autoForward = 'autoForward' in activity ? activity.autoForward : !!activity.forward;
- if (!serviceCreator) {
- // tslint:disable-next-line:no-console
- if (!IS_PRODUCTION) {
- warn(false, "No service found for invocation '".concat(activity.src, "' in machine '").concat(_this.machine.id, "'."));
- }
- return;
- }
- var resolvedData = data ? mapContext(data, context, _event) : undefined;
- if (typeof serviceCreator === 'string') {
- // TODO: warn
- return;
- }
- var source = isFunction(serviceCreator) ? serviceCreator(context, _event.data, {
- data: resolvedData,
- src: invokeSource,
- meta: activity.meta
- }) : serviceCreator;
- if (!source) {
- // TODO: warn?
- return;
- }
- var options = void 0;
- if (isMachine(source)) {
- source = resolvedData ? source.withContext(resolvedData) : source;
- options = {
- autoForward: autoForward
- };
- }
- _this.spawn(source, id, options);
- } else {
- _this.spawnActivity(activity);
- }
- break;
- }
- case stop:
- {
- _this.stopChild(action.activity.id);
- break;
- }
- case log:
- var _a = action,
- label = _a.label,
- value = _a.value;
- if (label) {
- _this.logger(label, value);
- } else {
- _this.logger(value);
- }
- break;
- default:
- if (!IS_PRODUCTION) {
- warn(false, "No implementation found for action type '".concat(action.type, "'"));
- }
- break;
- }
- };
- var resolvedOptions = __assign(__assign({}, Interpreter.defaultOptions), options);
- var clock = resolvedOptions.clock,
- logger = resolvedOptions.logger,
- parent = resolvedOptions.parent,
- id = resolvedOptions.id;
- var resolvedId = id !== undefined ? id : machine.id;
- this.id = resolvedId;
- this.logger = logger;
- this.clock = clock;
- this.parent = parent;
- this.options = resolvedOptions;
- this.scheduler = new Scheduler({
- deferEvents: this.options.deferEvents
- });
- this.sessionId = registry.bookId();
- }
- Object.defineProperty(Interpreter.prototype, "initialState", {
- get: function () {
- var _this = this;
- if (this._initialState) {
- return this._initialState;
- }
- return provide(this, function () {
- _this._initialState = _this.machine.initialState;
- return _this._initialState;
- });
- },
- enumerable: false,
- configurable: true
- });
- Object.defineProperty(Interpreter.prototype, "state", {
- /**
- * @deprecated Use `.getSnapshot()` instead.
- */
- get: function () {
- if (!IS_PRODUCTION) {
- warn(this.status !== InterpreterStatus.NotStarted, "Attempted to read state from uninitialized service '".concat(this.id, "'. Make sure the service is started first."));
- }
- return this._state;
- },
- enumerable: false,
- configurable: true
- });
- /**
- * Executes the actions of the given state, with that state's `context` and `event`.
- *
- * @param state The state whose actions will be executed
- * @param actionsConfig The action implementations to use
- */
- Interpreter.prototype.execute = function (state, actionsConfig) {
- var e_1, _a;
- try {
- for (var _b = __values(state.actions), _c = _b.next(); !_c.done; _c = _b.next()) {
- var action = _c.value;
- this.exec(action, state, actionsConfig);
- }
- } catch (e_1_1) {
- e_1 = {
- error: e_1_1
- };
- } finally {
- try {
- if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
- } finally {
- if (e_1) throw e_1.error;
- }
- }
- };
- Interpreter.prototype.update = function (state, _event) {
- var e_2, _a, e_3, _b, e_4, _c, e_5, _d;
- var _this = this; // Attach session ID to state
- state._sessionid = this.sessionId; // Update state
- this._state = state; // Execute actions
- if ((!this.machine.config.predictableActionArguments || // this is currently required to execute initial actions as the `initialState` gets cached
- // we can't just recompute it (and execute actions while doing so) because we try to preserve identity of actors created within initial assigns
- _event === initEvent) && this.options.execute) {
- this.execute(this.state);
- } else {
- var item = void 0;
- while (item = this._outgoingQueue.shift()) {
- item[0].send(item[1]);
- }
- } // Update children
- this.children.forEach(function (child) {
- _this.state.children[child.id] = child;
- }); // Dev tools
- if (this.devTools) {
- this.devTools.send(_event.data, state);
- } // Execute listeners
- if (state.event) {
- try {
- for (var _e = __values(this.eventListeners), _f = _e.next(); !_f.done; _f = _e.next()) {
- var listener = _f.value;
- listener(state.event);
- }
- } catch (e_2_1) {
- e_2 = {
- error: e_2_1
- };
- } finally {
- try {
- if (_f && !_f.done && (_a = _e.return)) _a.call(_e);
- } finally {
- if (e_2) throw e_2.error;
- }
- }
- }
- try {
- for (var _g = __values(this.listeners), _h = _g.next(); !_h.done; _h = _g.next()) {
- var listener = _h.value;
- listener(state, state.event);
- }
- } catch (e_3_1) {
- e_3 = {
- error: e_3_1
- };
- } finally {
- try {
- if (_h && !_h.done && (_b = _g.return)) _b.call(_g);
- } finally {
- if (e_3) throw e_3.error;
- }
- }
- try {
- for (var _j = __values(this.contextListeners), _k = _j.next(); !_k.done; _k = _j.next()) {
- var contextListener = _k.value;
- contextListener(this.state.context, this.state.history ? this.state.history.context : undefined);
- }
- } catch (e_4_1) {
- e_4 = {
- error: e_4_1
- };
- } finally {
- try {
- if (_k && !_k.done && (_c = _j.return)) _c.call(_j);
- } finally {
- if (e_4) throw e_4.error;
- }
- }
- if (this.state.done) {
- // get final child state node
- var finalChildStateNode = state.configuration.find(function (sn) {
- return sn.type === 'final' && sn.parent === _this.machine;
- });
- var doneData = finalChildStateNode && finalChildStateNode.doneData ? mapContext(finalChildStateNode.doneData, state.context, _event) : undefined;
- this._doneEvent = doneInvoke(this.id, doneData);
- try {
- for (var _l = __values(this.doneListeners), _m = _l.next(); !_m.done; _m = _l.next()) {
- var listener = _m.value;
- listener(this._doneEvent);
- }
- } catch (e_5_1) {
- e_5 = {
- error: e_5_1
- };
- } finally {
- try {
- if (_m && !_m.done && (_d = _l.return)) _d.call(_l);
- } finally {
- if (e_5) throw e_5.error;
- }
- }
- this._stop();
- this._stopChildren();
- registry.free(this.sessionId);
- }
- };
- /*
- * Adds a listener that is notified whenever a state transition happens. The listener is called with
- * the next state and the event object that caused the state transition.
- *
- * @param listener The state listener
- */
- Interpreter.prototype.onTransition = function (listener) {
- this.listeners.add(listener); // Send current state to listener
- if (this.status === InterpreterStatus.Running) {
- listener(this.state, this.state.event);
- }
- return this;
- };
- Interpreter.prototype.subscribe = function (nextListenerOrObserver, _, // TODO: error listener
- completeListener) {
- var _this = this;
- var observer = toObserver(nextListenerOrObserver, _, completeListener);
- this.listeners.add(observer.next); // Send current state to listener
- if (this.status !== InterpreterStatus.NotStarted) {
- observer.next(this.state);
- }
- var completeOnce = function () {
- _this.doneListeners.delete(completeOnce);
- _this.stopListeners.delete(completeOnce);
- observer.complete();
- };
- if (this.status === InterpreterStatus.Stopped) {
- observer.complete();
- } else {
- this.onDone(completeOnce);
- this.onStop(completeOnce);
- }
- return {
- unsubscribe: function () {
- _this.listeners.delete(observer.next);
- _this.doneListeners.delete(completeOnce);
- _this.stopListeners.delete(completeOnce);
- }
- };
- };
- /**
- * Adds an event listener that is notified whenever an event is sent to the running interpreter.
- * @param listener The event listener
- */
- Interpreter.prototype.onEvent = function (listener) {
- this.eventListeners.add(listener);
- return this;
- };
- /**
- * Adds an event listener that is notified whenever a `send` event occurs.
- * @param listener The event listener
- */
- Interpreter.prototype.onSend = function (listener) {
- this.sendListeners.add(listener);
- return this;
- };
- /**
- * Adds a context listener that is notified whenever the state context changes.
- * @param listener The context listener
- */
- Interpreter.prototype.onChange = function (listener) {
- this.contextListeners.add(listener);
- return this;
- };
- /**
- * Adds a listener that is notified when the machine is stopped.
- * @param listener The listener
- */
- Interpreter.prototype.onStop = function (listener) {
- this.stopListeners.add(listener);
- return this;
- };
- /**
- * Adds a state listener that is notified when the statechart has reached its final state.
- * @param listener The state listener
- */
- Interpreter.prototype.onDone = function (listener) {
- if (this.status === InterpreterStatus.Stopped && this._doneEvent) {
- listener(this._doneEvent);
- } else {
- this.doneListeners.add(listener);
- }
- return this;
- };
- /**
- * Removes a listener.
- * @param listener The listener to remove
- */
- Interpreter.prototype.off = function (listener) {
- this.listeners.delete(listener);
- this.eventListeners.delete(listener);
- this.sendListeners.delete(listener);
- this.stopListeners.delete(listener);
- this.doneListeners.delete(listener);
- this.contextListeners.delete(listener);
- return this;
- };
- /**
- * Starts the interpreter from the given state, or the initial state.
- * @param initialState The state to start the statechart from
- */
- Interpreter.prototype.start = function (initialState) {
- var _this = this;
- if (this.status === InterpreterStatus.Running) {
- // Do not restart the service if it is already started
- return this;
- } // yes, it's a hack but we need the related cache to be populated for some things to work (like delayed transitions)
- // this is usually called by `machine.getInitialState` but if we rehydrate from a state we might bypass this call
- // we also don't want to call this method here as it resolves the full initial state which might involve calling assign actions
- // and that could potentially lead to some unwanted side-effects (even such as creating some rogue actors)
- this.machine._init();
- registry.register(this.sessionId, this);
- this.initialized = true;
- this.status = InterpreterStatus.Running;
- var resolvedState = initialState === undefined ? this.initialState : provide(this, function () {
- return isStateConfig(initialState) ? _this.machine.resolveState(initialState) : _this.machine.resolveState(State.from(initialState, _this.machine.context));
- });
- if (this.options.devTools) {
- this.attachDev();
- }
- this.scheduler.initialize(function () {
- _this.update(resolvedState, initEvent);
- });
- return this;
- };
- Interpreter.prototype._stopChildren = function () {
- // TODO: think about converting those to actions
- this.children.forEach(function (child) {
- if (isFunction(child.stop)) {
- child.stop();
- }
- });
- this.children.clear();
- };
- Interpreter.prototype._stop = function () {
- var e_6, _a, e_7, _b, e_8, _c, e_9, _d, e_10, _e;
- try {
- for (var _f = __values(this.listeners), _g = _f.next(); !_g.done; _g = _f.next()) {
- var listener = _g.value;
- this.listeners.delete(listener);
- }
- } catch (e_6_1) {
- e_6 = {
- error: e_6_1
- };
- } finally {
- try {
- if (_g && !_g.done && (_a = _f.return)) _a.call(_f);
- } finally {
- if (e_6) throw e_6.error;
- }
- }
- try {
- for (var _h = __values(this.stopListeners), _j = _h.next(); !_j.done; _j = _h.next()) {
- var listener = _j.value; // call listener, then remove
- listener();
- this.stopListeners.delete(listener);
- }
- } catch (e_7_1) {
- e_7 = {
- error: e_7_1
- };
- } finally {
- try {
- if (_j && !_j.done && (_b = _h.return)) _b.call(_h);
- } finally {
- if (e_7) throw e_7.error;
- }
- }
- try {
- for (var _k = __values(this.contextListeners), _l = _k.next(); !_l.done; _l = _k.next()) {
- var listener = _l.value;
- this.contextListeners.delete(listener);
- }
- } catch (e_8_1) {
- e_8 = {
- error: e_8_1
- };
- } finally {
- try {
- if (_l && !_l.done && (_c = _k.return)) _c.call(_k);
- } finally {
- if (e_8) throw e_8.error;
- }
- }
- try {
- for (var _m = __values(this.doneListeners), _o = _m.next(); !_o.done; _o = _m.next()) {
- var listener = _o.value;
- this.doneListeners.delete(listener);
- }
- } catch (e_9_1) {
- e_9 = {
- error: e_9_1
- };
- } finally {
- try {
- if (_o && !_o.done && (_d = _m.return)) _d.call(_m);
- } finally {
- if (e_9) throw e_9.error;
- }
- }
- if (!this.initialized) {
- // Interpreter already stopped; do nothing
- return this;
- }
- this.initialized = false;
- this.status = InterpreterStatus.Stopped;
- this._initialState = undefined;
- try {
- // we are going to stop within the current sync frame
- // so we can safely just cancel this here as nothing async should be fired anyway
- for (var _p = __values(Object.keys(this.delayedEventsMap)), _q = _p.next(); !_q.done; _q = _p.next()) {
- var key = _q.value;
- this.clock.clearTimeout(this.delayedEventsMap[key]);
- }
- } catch (e_10_1) {
- e_10 = {
- error: e_10_1
- };
- } finally {
- try {
- if (_q && !_q.done && (_e = _p.return)) _e.call(_p);
- } finally {
- if (e_10) throw e_10.error;
- }
- } // clear everything that might be enqueued
- this.scheduler.clear();
- this.scheduler = new Scheduler({
- deferEvents: this.options.deferEvents
- });
- };
- /**
- * Stops the interpreter and unsubscribe all listeners.
- *
- * This will also notify the `onStop` listeners.
- */
- Interpreter.prototype.stop = function () {
- // TODO: add warning for stopping non-root interpreters
- var _this = this; // grab the current scheduler as it will be replaced in _stop
- var scheduler = this.scheduler;
- this._stop(); // let what is currently processed to be finished
- scheduler.schedule(function () {
- var _a;
- if ((_a = _this._state) === null || _a === void 0 ? void 0 : _a.done) {
- return;
- } // it feels weird to handle this here but we need to handle this even slightly "out of band"
- var _event = toSCXMLEvent({
- type: 'xstate.stop'
- });
- var nextState = provide(_this, function () {
- var exitActions = flatten(__spreadArray([], __read(_this.state.configuration), false).sort(function (a, b) {
- return b.order - a.order;
- }).map(function (stateNode) {
- return toActionObjects(stateNode.onExit, _this.machine.options.actions);
- }));
- var _a = __read(resolveActions(_this.machine, _this.state, _this.state.context, _event, [{
- type: 'exit',
- actions: exitActions
- }], _this.machine.config.predictableActionArguments ? _this._exec : undefined, _this.machine.config.predictableActionArguments || _this.machine.config.preserveActionOrder), 2),
- resolvedActions = _a[0],
- updatedContext = _a[1];
- var newState = new State({
- value: _this.state.value,
- context: updatedContext,
- _event: _event,
- _sessionid: _this.sessionId,
- historyValue: undefined,
- history: _this.state,
- actions: resolvedActions.filter(function (action) {
- return !isRaisableAction(action);
- }),
- activities: {},
- events: [],
- configuration: [],
- transitions: [],
- children: {},
- done: _this.state.done,
- tags: _this.state.tags,
- machine: _this.machine
- });
- newState.changed = true;
- return newState;
- });
- _this.update(nextState, _event);
- _this._stopChildren();
- registry.free(_this.sessionId);
- });
- return this;
- };
- Interpreter.prototype.batch = function (events) {
- var _this = this;
- if (this.status === InterpreterStatus.NotStarted && this.options.deferEvents) {
- // tslint:disable-next-line:no-console
- if (!IS_PRODUCTION) {
- 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)));
- }
- } else if (this.status !== InterpreterStatus.Running) {
- throw new Error( // tslint:disable-next-line:max-line-length
- "".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."));
- }
- if (!events.length) {
- return;
- }
- var exec = !!this.machine.config.predictableActionArguments && this._exec;
- this.scheduler.schedule(function () {
- var e_11, _a;
- var nextState = _this.state;
- var batchChanged = false;
- var batchedActions = [];
- var _loop_1 = function (event_1) {
- var _event = toSCXMLEvent(event_1);
- _this.forward(_event);
- nextState = provide(_this, function () {
- return _this.machine.transition(nextState, _event, undefined, exec || undefined);
- });
- batchedActions.push.apply(batchedActions, __spreadArray([], __read(_this.machine.config.predictableActionArguments ? nextState.actions : nextState.actions.map(function (a) {
- return bindActionToState(a, nextState);
- })), false));
- batchChanged = batchChanged || !!nextState.changed;
- };
- try {
- for (var events_1 = __values(events), events_1_1 = events_1.next(); !events_1_1.done; events_1_1 = events_1.next()) {
- var event_1 = events_1_1.value;
- _loop_1(event_1);
- }
- } catch (e_11_1) {
- e_11 = {
- error: e_11_1
- };
- } finally {
- try {
- if (events_1_1 && !events_1_1.done && (_a = events_1.return)) _a.call(events_1);
- } finally {
- if (e_11) throw e_11.error;
- }
- }
- nextState.changed = batchChanged;
- nextState.actions = batchedActions;
- _this.update(nextState, toSCXMLEvent(events[events.length - 1]));
- });
- };
- /**
- * Returns a send function bound to this interpreter instance.
- *
- * @param event The event to be sent by the sender.
- */
- Interpreter.prototype.sender = function (event) {
- return this.send.bind(this, event);
- };
- Interpreter.prototype._nextState = function (event, exec) {
- var _this = this;
- if (exec === void 0) {
- exec = !!this.machine.config.predictableActionArguments && this._exec;
- }
- var _event = toSCXMLEvent(event);
- if (_event.name.indexOf(errorPlatform) === 0 && !this.state.nextEvents.some(function (nextEvent) {
- return nextEvent.indexOf(errorPlatform) === 0;
- })) {
- throw _event.data.data;
- }
- var nextState = provide(this, function () {
- return _this.machine.transition(_this.state, _event, undefined, exec || undefined);
- });
- return nextState;
- };
- /**
- * Returns the next state given the interpreter's current state and the event.
- *
- * This is a pure method that does _not_ update the interpreter's state.
- *
- * @param event The event to determine the next state
- */
- Interpreter.prototype.nextState = function (event) {
- return this._nextState(event, false);
- };
- Interpreter.prototype.forward = function (event) {
- var e_12, _a;
- try {
- for (var _b = __values(this.forwardTo), _c = _b.next(); !_c.done; _c = _b.next()) {
- var id = _c.value;
- var child = this.children.get(id);
- if (!child) {
- throw new Error("Unable to forward event '".concat(event, "' from interpreter '").concat(this.id, "' to nonexistant child '").concat(id, "'."));
- }
- child.send(event);
- }
- } catch (e_12_1) {
- e_12 = {
- error: e_12_1
- };
- } finally {
- try {
- if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
- } finally {
- if (e_12) throw e_12.error;
- }
- }
- };
- Interpreter.prototype.defer = function (sendAction) {
- var _this = this;
- var timerId = this.clock.setTimeout(function () {
- if ('to' in sendAction && sendAction.to) {
- _this.sendTo(sendAction._event, sendAction.to, true);
- } else {
- _this.send(sendAction._event);
- }
- }, sendAction.delay);
- if (sendAction.id) {
- this.delayedEventsMap[sendAction.id] = timerId;
- }
- };
- Interpreter.prototype.cancel = function (sendId) {
- this.clock.clearTimeout(this.delayedEventsMap[sendId]);
- delete this.delayedEventsMap[sendId];
- };
- Interpreter.prototype.exec = function (action, state, actionFunctionMap) {
- if (actionFunctionMap === void 0) {
- actionFunctionMap = this.machine.options.actions;
- }
- this._exec(action, state.context, state._event, actionFunctionMap);
- };
- Interpreter.prototype.removeChild = function (childId) {
- var _a;
- this.children.delete(childId);
- this.forwardTo.delete(childId); // this.state might not exist at the time this is called,
- // such as when a child is added then removed while initializing the state
- (_a = this.state) === null || _a === void 0 ? true : delete _a.children[childId];
- };
- Interpreter.prototype.stopChild = function (childId) {
- var child = this.children.get(childId);
- if (!child) {
- return;
- }
- this.removeChild(childId);
- if (isFunction(child.stop)) {
- child.stop();
- }
- };
- Interpreter.prototype.spawn = function (entity, name, options) {
- if (this.status !== InterpreterStatus.Running) {
- return createDeferredActor(entity, name);
- }
- if (isPromiseLike(entity)) {
- return this.spawnPromise(Promise.resolve(entity), name);
- } else if (isFunction(entity)) {
- return this.spawnCallback(entity, name);
- } else if (isSpawnedActor(entity)) {
- return this.spawnActor(entity, name);
- } else if (isObservable(entity)) {
- return this.spawnObservable(entity, name);
- } else if (isMachine(entity)) {
- return this.spawnMachine(entity, __assign(__assign({}, options), {
- id: name
- }));
- } else if (isBehavior(entity)) {
- return this.spawnBehavior(entity, name);
- } else {
- throw new Error("Unable to spawn entity \"".concat(name, "\" of type \"").concat(typeof entity, "\"."));
- }
- };
- Interpreter.prototype.spawnMachine = function (machine, options) {
- var _this = this;
- if (options === void 0) {
- options = {};
- }
- var childService = new Interpreter(machine, __assign(__assign({}, this.options), {
- parent: this,
- id: options.id || machine.id
- }));
- var resolvedOptions = __assign(__assign({}, DEFAULT_SPAWN_OPTIONS), options);
- if (resolvedOptions.sync) {
- childService.onTransition(function (state) {
- _this.send(update, {
- state: state,
- id: childService.id
- });
- });
- }
- var actor = childService;
- this.children.set(childService.id, actor);
- if (resolvedOptions.autoForward) {
- this.forwardTo.add(childService.id);
- }
- childService.onDone(function (doneEvent) {
- _this.removeChild(childService.id);
- _this.send(toSCXMLEvent(doneEvent, {
- origin: childService.id
- }));
- }).start();
- return actor;
- };
- Interpreter.prototype.spawnBehavior = function (behavior, id) {
- var actorRef = spawnBehavior(behavior, {
- id: id,
- parent: this
- });
- this.children.set(id, actorRef);
- return actorRef;
- };
- Interpreter.prototype.spawnPromise = function (promise, id) {
- var _a;
- var _this = this;
- var canceled = false;
- var resolvedData;
- promise.then(function (response) {
- if (!canceled) {
- resolvedData = response;
- _this.removeChild(id);
- _this.send(toSCXMLEvent(doneInvoke(id, response), {
- origin: id
- }));
- }
- }, function (errorData) {
- if (!canceled) {
- _this.removeChild(id);
- var errorEvent = error(id, errorData);
- try {
- // Send "error.platform.id" to this (parent).
- _this.send(toSCXMLEvent(errorEvent, {
- origin: id
- }));
- } catch (error) {
- reportUnhandledExceptionOnInvocation(errorData, error, id);
- if (_this.devTools) {
- _this.devTools.send(errorEvent, _this.state);
- }
- if (_this.machine.strict) {
- // it would be better to always stop the state machine if unhandled
- // exception/promise rejection happens but because we don't want to
- // break existing code so enforce it on strict mode only especially so
- // because documentation says that onError is optional
- _this.stop();
- }
- }
- }
- });
- var actor = (_a = {
- id: id,
- send: function () {
- return void 0;
- },
- subscribe: function (next, handleError, complete) {
- var observer = toObserver(next, handleError, complete);
- var unsubscribed = false;
- promise.then(function (response) {
- if (unsubscribed) {
- return;
- }
- observer.next(response);
- if (unsubscribed) {
- return;
- }
- observer.complete();
- }, function (err) {
- if (unsubscribed) {
- return;
- }
- observer.error(err);
- });
- return {
- unsubscribe: function () {
- return unsubscribed = true;
- }
- };
- },
- stop: function () {
- canceled = true;
- },
- toJSON: function () {
- return {
- id: id
- };
- },
- getSnapshot: function () {
- return resolvedData;
- }
- }, _a[symbolObservable] = function () {
- return this;
- }, _a);
- this.children.set(id, actor);
- return actor;
- };
- Interpreter.prototype.spawnCallback = function (callback, id) {
- var _a;
- var _this = this;
- var canceled = false;
- var receivers = new Set();
- var listeners = new Set();
- var emitted;
- var receive = function (e) {
- emitted = e;
- listeners.forEach(function (listener) {
- return listener(e);
- });
- if (canceled) {
- return;
- }
- _this.send(toSCXMLEvent(e, {
- origin: id
- }));
- };
- var callbackStop;
- try {
- callbackStop = callback(receive, function (newListener) {
- receivers.add(newListener);
- });
- } catch (err) {
- this.send(error(id, err));
- }
- if (isPromiseLike(callbackStop)) {
- // it turned out to be an async function, can't reliably check this before calling `callback`
- // because transpiled async functions are not recognizable
- return this.spawnPromise(callbackStop, id);
- }
- var actor = (_a = {
- id: id,
- send: function (event) {
- return receivers.forEach(function (receiver) {
- return receiver(event);
- });
- },
- subscribe: function (next) {
- var observer = toObserver(next);
- listeners.add(observer.next);
- return {
- unsubscribe: function () {
- listeners.delete(observer.next);
- }
- };
- },
- stop: function () {
- canceled = true;
- if (isFunction(callbackStop)) {
- callbackStop();
- }
- },
- toJSON: function () {
- return {
- id: id
- };
- },
- getSnapshot: function () {
- return emitted;
- }
- }, _a[symbolObservable] = function () {
- return this;
- }, _a);
- this.children.set(id, actor);
- return actor;
- };
- Interpreter.prototype.spawnObservable = function (source, id) {
- var _a;
- var _this = this;
- var emitted;
- var subscription = source.subscribe(function (value) {
- emitted = value;
- _this.send(toSCXMLEvent(value, {
- origin: id
- }));
- }, function (err) {
- _this.removeChild(id);
- _this.send(toSCXMLEvent(error(id, err), {
- origin: id
- }));
- }, function () {
- _this.removeChild(id);
- _this.send(toSCXMLEvent(doneInvoke(id), {
- origin: id
- }));
- });
- var actor = (_a = {
- id: id,
- send: function () {
- return void 0;
- },
- subscribe: function (next, handleError, complete) {
- return source.subscribe(next, handleError, complete);
- },
- stop: function () {
- return subscription.unsubscribe();
- },
- getSnapshot: function () {
- return emitted;
- },
- toJSON: function () {
- return {
- id: id
- };
- }
- }, _a[symbolObservable] = function () {
- return this;
- }, _a);
- this.children.set(id, actor);
- return actor;
- };
- Interpreter.prototype.spawnActor = function (actor, name) {
- this.children.set(name, actor);
- return actor;
- };
- Interpreter.prototype.spawnActivity = function (activity) {
- var implementation = this.machine.options && this.machine.options.activities ? this.machine.options.activities[activity.type] : undefined;
- if (!implementation) {
- if (!IS_PRODUCTION) {
- warn(false, "No implementation found for activity '".concat(activity.type, "'"));
- } // tslint:disable-next-line:no-console
- return;
- } // Start implementation
- var dispose = implementation(this.state.context, activity);
- this.spawnEffect(activity.id, dispose);
- };
- Interpreter.prototype.spawnEffect = function (id, dispose) {
- var _a;
- this.children.set(id, (_a = {
- id: id,
- send: function () {
- return void 0;
- },
- subscribe: function () {
- return {
- unsubscribe: function () {
- return void 0;
- }
- };
- },
- stop: dispose || undefined,
- getSnapshot: function () {
- return undefined;
- },
- toJSON: function () {
- return {
- id: id
- };
- }
- }, _a[symbolObservable] = function () {
- return this;
- }, _a));
- };
- Interpreter.prototype.attachDev = function () {
- var global = getGlobal();
- if (this.options.devTools && global) {
- if (global.__REDUX_DEVTOOLS_EXTENSION__) {
- var devToolsOptions = typeof this.options.devTools === 'object' ? this.options.devTools : undefined;
- this.devTools = global.__REDUX_DEVTOOLS_EXTENSION__.connect(__assign(__assign({
- name: this.id,
- autoPause: true,
- stateSanitizer: function (state) {
- return {
- value: state.value,
- context: state.context,
- actions: state.actions
- };
- }
- }, devToolsOptions), {
- features: __assign({
- jump: false,
- skip: false
- }, devToolsOptions ? devToolsOptions.features : undefined)
- }), this.machine);
- this.devTools.init(this.state);
- } // add XState-specific dev tooling hook
- registerService(this);
- }
- };
- Interpreter.prototype.toJSON = function () {
- return {
- id: this.id
- };
- };
- Interpreter.prototype[symbolObservable] = function () {
- return this;
- };
- Interpreter.prototype.getSnapshot = function () {
- if (this.status === InterpreterStatus.NotStarted) {
- return this.initialState;
- }
- return this._state;
- };
- /**
- * The default interpreter options:
- *
- * - `clock` uses the global `setTimeout` and `clearTimeout` functions
- * - `logger` uses the global `console.log()` method
- */
- Interpreter.defaultOptions = {
- execute: true,
- deferEvents: true,
- clock: {
- setTimeout: function (fn, ms) {
- return setTimeout(fn, ms);
- },
- clearTimeout: function (id) {
- return clearTimeout(id);
- }
- },
- logger: /*#__PURE__*/console.log.bind(console),
- devTools: false
- };
- Interpreter.interpret = interpret;
- return Interpreter;
- }();
- var resolveSpawnOptions = function (nameOrOptions) {
- if (isString(nameOrOptions)) {
- return __assign(__assign({}, DEFAULT_SPAWN_OPTIONS), {
- name: nameOrOptions
- });
- }
- return __assign(__assign(__assign({}, DEFAULT_SPAWN_OPTIONS), {
- name: uniqueId()
- }), nameOrOptions);
- };
- function spawn(entity, nameOrOptions) {
- var resolvedOptions = resolveSpawnOptions(nameOrOptions);
- return consume(function (service) {
- if (!IS_PRODUCTION) {
- var isLazyEntity = isMachine(entity) || isFunction(entity);
- warn(!!service || isLazyEntity, "Attempted to spawn an Actor (ID: \"".concat(isMachine(entity) ? entity.id : 'undefined', "\") outside of a service. This will have no effect."));
- }
- if (service) {
- return service.spawn(entity, resolvedOptions.name, resolvedOptions);
- } else {
- return createDeferredActor(entity, resolvedOptions.name);
- }
- });
- }
- /**
- * Creates a new Interpreter instance for the given machine with the provided options, if any.
- *
- * @param machine The machine to interpret
- * @param options Interpreter options
- */
- function interpret(machine, options) {
- var interpreter = new Interpreter(machine, options);
- return interpreter;
- }
- export { Interpreter, InterpreterStatus, interpret, spawn };
|