1512 lines
43 KiB
JavaScript
Executable File
1512 lines
43 KiB
JavaScript
Executable File
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 () {
|
|
// 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 };
|