xstate 5.0.0-beta.14 → 5.0.0-beta.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/actions/dist/xstate-actions.cjs.js +1 -3
- package/actions/dist/xstate-actions.cjs.mjs +0 -2
- package/actions/dist/xstate-actions.development.cjs.js +1 -3
- package/actions/dist/xstate-actions.development.esm.js +1 -1
- package/actions/dist/xstate-actions.esm.js +1 -1
- package/actions/dist/xstate-actions.umd.min.js +1 -1
- package/actions/dist/xstate-actions.umd.min.js.map +1 -1
- package/actors/dist/xstate-actors.cjs.js +1 -1
- package/actors/dist/xstate-actors.development.cjs.js +1 -1
- package/actors/dist/xstate-actors.development.esm.js +1 -1
- package/actors/dist/xstate-actors.esm.js +1 -1
- package/actors/dist/xstate-actors.umd.min.js +1 -1
- package/actors/dist/xstate-actors.umd.min.js.map +1 -1
- package/dist/{actions-444a17c3.esm.js → actions-0386b622.esm.js} +934 -997
- package/dist/{actions-73b8d456.development.cjs.js → actions-0f903c0d.development.cjs.js} +863 -928
- package/dist/{actions-17c3bcfa.cjs.js → actions-6b9073db.cjs.js} +934 -999
- package/dist/{actions-60622c0c.development.esm.js → actions-6f7fbc84.development.esm.js} +863 -926
- package/dist/declarations/src/State.d.ts +1 -11
- package/dist/declarations/src/StateMachine.d.ts +4 -11
- package/dist/declarations/src/actionTypes.d.ts +1 -1
- package/dist/declarations/src/actions/assign.d.ts +2 -2
- package/dist/declarations/src/actions/cancel.d.ts +2 -2
- package/dist/declarations/src/actions/send.d.ts +12 -22
- package/dist/declarations/src/actions/stop.d.ts +2 -2
- package/dist/declarations/src/actions.d.ts +1 -4
- package/dist/declarations/src/actors/callback.d.ts +3 -3
- package/dist/declarations/src/actors/index.d.ts +1 -1
- package/dist/declarations/src/actors/promise.d.ts +13 -3
- package/dist/declarations/src/guards.d.ts +1 -1
- package/dist/declarations/src/interpreter.d.ts +2 -2
- package/dist/declarations/src/stateUtils.d.ts +3 -2
- package/dist/declarations/src/types.d.ts +44 -71
- package/dist/declarations/src/utils.d.ts +3 -3
- package/dist/xstate.cjs.js +44 -59
- package/dist/xstate.development.cjs.js +44 -59
- package/dist/xstate.development.esm.js +45 -60
- package/dist/xstate.esm.js +45 -60
- package/dist/xstate.umd.min.js +1 -1
- package/dist/xstate.umd.min.js.map +1 -1
- package/guards/dist/xstate-guards.cjs.js +1 -1
- package/guards/dist/xstate-guards.development.cjs.js +1 -1
- package/guards/dist/xstate-guards.development.esm.js +1 -1
- package/guards/dist/xstate-guards.esm.js +1 -1
- package/guards/dist/xstate-guards.umd.min.js +1 -1
- package/guards/dist/xstate-guards.umd.min.js.map +1 -1
- package/package.json +2 -1
|
@@ -37,7 +37,7 @@ var dev_dist_xstateDev = require('../dev/dist/xstate-dev.cjs.js');
|
|
|
37
37
|
let ActionTypes = /*#__PURE__*/function (ActionTypes) {
|
|
38
38
|
ActionTypes["Stop"] = "xstate.stop";
|
|
39
39
|
ActionTypes["Raise"] = "xstate.raise";
|
|
40
|
-
ActionTypes["
|
|
40
|
+
ActionTypes["SendTo"] = "xstate.sendTo";
|
|
41
41
|
ActionTypes["Cancel"] = "xstate.cancel";
|
|
42
42
|
ActionTypes["Assign"] = "xstate.assign";
|
|
43
43
|
ActionTypes["After"] = "xstate.after";
|
|
@@ -63,7 +63,7 @@ let SpecialTargets = /*#__PURE__*/function (SpecialTargets) {
|
|
|
63
63
|
// xstate-specific action types
|
|
64
64
|
const stop$1 = ActionTypes.Stop;
|
|
65
65
|
const raise$1 = ActionTypes.Raise;
|
|
66
|
-
const
|
|
66
|
+
const sendTo$1 = ActionTypes.SendTo;
|
|
67
67
|
const cancel$1 = ActionTypes.Cancel;
|
|
68
68
|
const assign$1 = ActionTypes.Assign;
|
|
69
69
|
const after$1 = ActionTypes.After;
|
|
@@ -81,7 +81,7 @@ var actionTypes = /*#__PURE__*/Object.freeze({
|
|
|
81
81
|
__proto__: null,
|
|
82
82
|
stop: stop$1,
|
|
83
83
|
raise: raise$1,
|
|
84
|
-
|
|
84
|
+
sendTo: sendTo$1,
|
|
85
85
|
cancel: cancel$1,
|
|
86
86
|
assign: assign$1,
|
|
87
87
|
after: after$1,
|
|
@@ -102,9 +102,9 @@ const NULL_EVENT = '';
|
|
|
102
102
|
const STATE_IDENTIFIER = '#';
|
|
103
103
|
const WILDCARD = '*';
|
|
104
104
|
|
|
105
|
-
function matchesState(parentStateId, childStateId
|
|
106
|
-
const parentStateValue = toStateValue(parentStateId
|
|
107
|
-
const childStateValue = toStateValue(childStateId
|
|
105
|
+
function matchesState(parentStateId, childStateId) {
|
|
106
|
+
const parentStateValue = toStateValue(parentStateId);
|
|
107
|
+
const childStateValue = toStateValue(childStateId);
|
|
108
108
|
if (isString(childStateValue)) {
|
|
109
109
|
if (isString(parentStateValue)) {
|
|
110
110
|
return childStateValue === parentStateValue;
|
|
@@ -123,12 +123,12 @@ function matchesState(parentStateId, childStateId, delimiter = STATE_DELIMITER)
|
|
|
123
123
|
return matchesState(parentStateValue[key], childStateValue[key]);
|
|
124
124
|
});
|
|
125
125
|
}
|
|
126
|
-
function toStatePath(stateId
|
|
126
|
+
function toStatePath(stateId) {
|
|
127
127
|
try {
|
|
128
128
|
if (isArray(stateId)) {
|
|
129
129
|
return stateId;
|
|
130
130
|
}
|
|
131
|
-
return stateId.toString().split(
|
|
131
|
+
return stateId.toString().split(STATE_DELIMITER);
|
|
132
132
|
} catch (e) {
|
|
133
133
|
throw new Error(`'${stateId}' is not a valid state path.`);
|
|
134
134
|
}
|
|
@@ -136,7 +136,7 @@ function toStatePath(stateId, delimiter) {
|
|
|
136
136
|
function isStateLike(state) {
|
|
137
137
|
return typeof state === 'object' && 'value' in state && 'context' in state && 'event' in state;
|
|
138
138
|
}
|
|
139
|
-
function toStateValue(stateValue
|
|
139
|
+
function toStateValue(stateValue) {
|
|
140
140
|
if (isStateLike(stateValue)) {
|
|
141
141
|
return stateValue.value;
|
|
142
142
|
}
|
|
@@ -146,7 +146,7 @@ function toStateValue(stateValue, delimiter) {
|
|
|
146
146
|
if (typeof stateValue !== 'string') {
|
|
147
147
|
return stateValue;
|
|
148
148
|
}
|
|
149
|
-
const statePath = toStatePath(stateValue
|
|
149
|
+
const statePath = toStatePath(stateValue);
|
|
150
150
|
return pathToStateValue(statePath);
|
|
151
151
|
}
|
|
152
152
|
function pathToStateValue(statePath) {
|
|
@@ -159,8 +159,9 @@ function pathToStateValue(statePath) {
|
|
|
159
159
|
if (i === statePath.length - 2) {
|
|
160
160
|
marker[statePath[i]] = statePath[i + 1];
|
|
161
161
|
} else {
|
|
162
|
-
|
|
163
|
-
marker =
|
|
162
|
+
const previous = marker;
|
|
163
|
+
marker = {};
|
|
164
|
+
previous[statePath[i]] = marker;
|
|
164
165
|
}
|
|
165
166
|
}
|
|
166
167
|
return value;
|
|
@@ -305,22 +306,19 @@ function isDynamicAction(action) {
|
|
|
305
306
|
}
|
|
306
307
|
|
|
307
308
|
/**
|
|
308
|
-
* Sends an event
|
|
309
|
-
* send the event in the next step, after the current step is finished executing.
|
|
310
|
-
*
|
|
311
|
-
* @deprecated Use the `sendTo(...)` action creator instead.
|
|
309
|
+
* Sends an event to an actor.
|
|
312
310
|
*
|
|
313
|
-
* @param
|
|
314
|
-
* @param
|
|
311
|
+
* @param actor The `ActorRef` to send the event to.
|
|
312
|
+
* @param event The event to send, or an expression that evaluates to the event to send
|
|
313
|
+
* @param options Send action options
|
|
315
314
|
* - `id` - The unique send event identifier (used with `cancel()`).
|
|
316
315
|
* - `delay` - The number of milliseconds to delay the sending of the event.
|
|
317
|
-
* - `to` - The target of this event (by default, the machine the event was sent from).
|
|
318
316
|
*/
|
|
319
|
-
function
|
|
317
|
+
function sendTo(actor, eventOrExpr, options) {
|
|
320
318
|
return createDynamicAction({
|
|
321
|
-
type:
|
|
319
|
+
type: sendTo$1,
|
|
322
320
|
params: {
|
|
323
|
-
to:
|
|
321
|
+
to: actor,
|
|
324
322
|
delay: options ? options.delay : undefined,
|
|
325
323
|
event: eventOrExpr,
|
|
326
324
|
id: options && options.id !== undefined ? options.id : isFunction(eventOrExpr) ? eventOrExpr.name : eventOrExpr.type
|
|
@@ -330,7 +328,7 @@ function send(eventOrExpr, options) {
|
|
|
330
328
|
state
|
|
331
329
|
}) => {
|
|
332
330
|
const params = {
|
|
333
|
-
to:
|
|
331
|
+
to: actor,
|
|
334
332
|
delay: options ? options.delay : undefined,
|
|
335
333
|
event: eventOrExpr,
|
|
336
334
|
// TODO: don't auto-generate IDs here like that
|
|
@@ -343,7 +341,7 @@ function send(eventOrExpr, options) {
|
|
|
343
341
|
self: actorContext?.self ?? null,
|
|
344
342
|
system: actorContext?.system
|
|
345
343
|
};
|
|
346
|
-
const delaysMap = state.machine.
|
|
344
|
+
const delaysMap = state.machine.implementations.delays;
|
|
347
345
|
|
|
348
346
|
// TODO: helper function for resolving Expr
|
|
349
347
|
if (typeof eventOrExpr === 'string') {
|
|
@@ -378,13 +376,12 @@ function send(eventOrExpr, options) {
|
|
|
378
376
|
targetActorRef = resolvedTarget || actorContext?.self;
|
|
379
377
|
}
|
|
380
378
|
const resolvedAction = {
|
|
381
|
-
type:
|
|
379
|
+
type: sendTo$1,
|
|
382
380
|
params: {
|
|
383
381
|
...params,
|
|
384
382
|
to: targetActorRef,
|
|
385
383
|
event: resolvedEvent,
|
|
386
|
-
delay: resolvedDelay
|
|
387
|
-
internal: resolvedTarget === SpecialTargets.Internal
|
|
384
|
+
delay: resolvedDelay
|
|
388
385
|
},
|
|
389
386
|
execute: actorCtx => {
|
|
390
387
|
const sendAction = resolvedAction;
|
|
@@ -414,12 +411,8 @@ function send(eventOrExpr, options) {
|
|
|
414
411
|
* @param options Options to pass into the send event.
|
|
415
412
|
*/
|
|
416
413
|
function sendParent(event, options) {
|
|
417
|
-
return
|
|
418
|
-
...options,
|
|
419
|
-
to: SpecialTargets.Parent
|
|
420
|
-
});
|
|
414
|
+
return sendTo(SpecialTargets.Parent, event, options);
|
|
421
415
|
}
|
|
422
|
-
|
|
423
416
|
/**
|
|
424
417
|
* Forwards (sends) an event to a specified service.
|
|
425
418
|
*
|
|
@@ -427,12 +420,9 @@ function sendParent(event, options) {
|
|
|
427
420
|
* @param options Options to pass into the send action creator.
|
|
428
421
|
*/
|
|
429
422
|
function forwardTo(target, options) {
|
|
430
|
-
return
|
|
423
|
+
return sendTo(target, ({
|
|
431
424
|
event
|
|
432
|
-
}) => event,
|
|
433
|
-
...options,
|
|
434
|
-
to: target
|
|
435
|
-
});
|
|
425
|
+
}) => event, options);
|
|
436
426
|
}
|
|
437
427
|
|
|
438
428
|
/**
|
|
@@ -448,25 +438,7 @@ function escalate(errorData, options) {
|
|
|
448
438
|
type: error$1,
|
|
449
439
|
data: isFunction(errorData) ? errorData(arg) : errorData
|
|
450
440
|
};
|
|
451
|
-
},
|
|
452
|
-
...options,
|
|
453
|
-
to: SpecialTargets.Parent
|
|
454
|
-
});
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
/**
|
|
458
|
-
* Sends an event to an actor.
|
|
459
|
-
*
|
|
460
|
-
* @param actor The `ActorRef` to send the event to.
|
|
461
|
-
* @param event The event to send, or an expression that evaluates to the event to send
|
|
462
|
-
* @param options Send action options
|
|
463
|
-
* @returns An XState send action object
|
|
464
|
-
*/
|
|
465
|
-
function sendTo(actor, event, options) {
|
|
466
|
-
return send(event, {
|
|
467
|
-
...options,
|
|
468
|
-
to: actor
|
|
469
|
-
});
|
|
441
|
+
}, options);
|
|
470
442
|
}
|
|
471
443
|
|
|
472
444
|
const cache = new WeakMap();
|
|
@@ -520,8 +492,6 @@ function cancel(sendId) {
|
|
|
520
492
|
});
|
|
521
493
|
}
|
|
522
494
|
|
|
523
|
-
const symbolObservable = (() => typeof Symbol === 'function' && Symbol.observable || '@@observable')();
|
|
524
|
-
|
|
525
495
|
class Mailbox {
|
|
526
496
|
constructor(_process) {
|
|
527
497
|
this._process = _process;
|
|
@@ -589,833 +559,832 @@ class Mailbox {
|
|
|
589
559
|
}
|
|
590
560
|
}
|
|
591
561
|
|
|
592
|
-
function
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
reverseKeyedActors.delete(actorRef);
|
|
609
|
-
}
|
|
562
|
+
const symbolObservable = (() => typeof Symbol === 'function' && Symbol.observable || '@@observable')();
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Returns actor logic from a transition function and its initial state.
|
|
566
|
+
*
|
|
567
|
+
* A transition function is a function that takes the current state and an event and returns the next state.
|
|
568
|
+
*
|
|
569
|
+
* @param transition The transition function that returns the next state given the current state and event.
|
|
570
|
+
* @param initialState The initial state of the transition function.
|
|
571
|
+
* @returns Actor logic
|
|
572
|
+
*/
|
|
573
|
+
function fromTransition(transition, initialState) {
|
|
574
|
+
const logic = {
|
|
575
|
+
config: transition,
|
|
576
|
+
transition: (state, event, actorContext) => {
|
|
577
|
+
return transition(state, event, actorContext);
|
|
610
578
|
},
|
|
611
|
-
|
|
612
|
-
return
|
|
579
|
+
getInitialState: (_, input) => {
|
|
580
|
+
return typeof initialState === 'function' ? initialState({
|
|
581
|
+
input
|
|
582
|
+
}) : initialState;
|
|
613
583
|
},
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
throw new Error(`Actor with system ID '${systemId}' already exists.`);
|
|
618
|
-
}
|
|
619
|
-
keyedActors.set(systemId, actorRef);
|
|
620
|
-
reverseKeyedActors.set(actorRef, systemId);
|
|
621
|
-
}
|
|
584
|
+
getSnapshot: state => state,
|
|
585
|
+
getPersistedState: state => state,
|
|
586
|
+
restoreState: state => state
|
|
622
587
|
};
|
|
623
|
-
return
|
|
588
|
+
return logic;
|
|
624
589
|
}
|
|
625
590
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
const
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
591
|
+
const resolveEventType = '$$xstate.resolve';
|
|
592
|
+
const rejectEventType = '$$xstate.reject';
|
|
593
|
+
function fromPromise(
|
|
594
|
+
// TODO: add types
|
|
595
|
+
promiseCreator) {
|
|
596
|
+
// TODO: add event types, consider making the `PromiseEvent` a private type or smth alike
|
|
597
|
+
const logic = {
|
|
598
|
+
config: promiseCreator,
|
|
599
|
+
transition: (state, event) => {
|
|
600
|
+
if (state.status !== 'active') {
|
|
601
|
+
return state;
|
|
602
|
+
}
|
|
603
|
+
switch (event.type) {
|
|
604
|
+
case resolveEventType:
|
|
605
|
+
return {
|
|
606
|
+
...state,
|
|
607
|
+
status: 'done',
|
|
608
|
+
data: event.data,
|
|
609
|
+
input: undefined
|
|
610
|
+
};
|
|
611
|
+
case rejectEventType:
|
|
612
|
+
return {
|
|
613
|
+
...state,
|
|
614
|
+
status: 'error',
|
|
615
|
+
data: event.data,
|
|
616
|
+
input: undefined
|
|
617
|
+
};
|
|
618
|
+
case stopSignalType:
|
|
619
|
+
return {
|
|
620
|
+
...state,
|
|
621
|
+
status: 'canceled',
|
|
622
|
+
input: undefined
|
|
623
|
+
};
|
|
624
|
+
default:
|
|
625
|
+
return state;
|
|
626
|
+
}
|
|
637
627
|
},
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
628
|
+
start: (state, {
|
|
629
|
+
self,
|
|
630
|
+
system
|
|
631
|
+
}) => {
|
|
632
|
+
// TODO: determine how to allow customizing this so that promises
|
|
633
|
+
// can be restarted if necessary
|
|
634
|
+
if (state.status !== 'active') {
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
const resolvedPromise = Promise.resolve(promiseCreator({
|
|
638
|
+
input: state.input,
|
|
639
|
+
system
|
|
640
|
+
}));
|
|
641
|
+
resolvedPromise.then(response => {
|
|
642
|
+
// TODO: remove this condition once dead letter queue lands
|
|
643
|
+
if (self._state.status !== 'active') {
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
self.send({
|
|
647
|
+
type: resolveEventType,
|
|
648
|
+
data: response
|
|
649
|
+
});
|
|
650
|
+
}, errorData => {
|
|
651
|
+
// TODO: remove this condition once dead letter queue lands
|
|
652
|
+
if (self._state.status !== 'active') {
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
self.send({
|
|
656
|
+
type: rejectEventType,
|
|
657
|
+
data: errorData
|
|
658
|
+
});
|
|
659
|
+
});
|
|
660
|
+
},
|
|
661
|
+
getInitialState: (_, input) => {
|
|
662
|
+
return {
|
|
663
|
+
status: 'active',
|
|
664
|
+
data: undefined,
|
|
665
|
+
input
|
|
666
|
+
};
|
|
667
|
+
},
|
|
668
|
+
getSnapshot: state => state.data,
|
|
669
|
+
getStatus: state => state,
|
|
670
|
+
getPersistedState: state => state,
|
|
671
|
+
restoreState: state => state
|
|
672
|
+
};
|
|
673
|
+
return logic;
|
|
674
|
+
}
|
|
665
675
|
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
676
|
+
// TODO: this likely shouldn't accept TEvent, observable actor doesn't accept external events
|
|
677
|
+
function fromObservable(observableCreator) {
|
|
678
|
+
const nextEventType = '$$xstate.next';
|
|
679
|
+
const errorEventType = '$$xstate.error';
|
|
680
|
+
const completeEventType = '$$xstate.complete';
|
|
669
681
|
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
*/
|
|
676
|
-
constructor(logic, options) {
|
|
677
|
-
this.logic = logic;
|
|
678
|
-
this._state = void 0;
|
|
679
|
-
this.clock = void 0;
|
|
680
|
-
this.options = void 0;
|
|
681
|
-
this.id = void 0;
|
|
682
|
-
this.mailbox = new Mailbox(this._process.bind(this));
|
|
683
|
-
this.delayedEventsMap = {};
|
|
684
|
-
this.observers = new Set();
|
|
685
|
-
this.logger = void 0;
|
|
686
|
-
this.status = ActorStatus.NotStarted;
|
|
687
|
-
this._parent = void 0;
|
|
688
|
-
this.ref = void 0;
|
|
689
|
-
this._actorContext = void 0;
|
|
690
|
-
this._systemId = void 0;
|
|
691
|
-
this.sessionId = void 0;
|
|
692
|
-
this.system = void 0;
|
|
693
|
-
this._doneEvent = void 0;
|
|
694
|
-
this.src = void 0;
|
|
695
|
-
this._deferred = [];
|
|
696
|
-
const resolvedOptions = {
|
|
697
|
-
...defaultOptions,
|
|
698
|
-
...options
|
|
699
|
-
};
|
|
700
|
-
const {
|
|
701
|
-
clock,
|
|
702
|
-
logger,
|
|
703
|
-
parent,
|
|
682
|
+
// TODO: add event types
|
|
683
|
+
const logic = {
|
|
684
|
+
config: observableCreator,
|
|
685
|
+
transition: (state, event, {
|
|
686
|
+
self,
|
|
704
687
|
id,
|
|
705
|
-
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
688
|
+
defer
|
|
689
|
+
}) => {
|
|
690
|
+
if (state.status !== 'active') {
|
|
691
|
+
return state;
|
|
692
|
+
}
|
|
693
|
+
switch (event.type) {
|
|
694
|
+
case nextEventType:
|
|
695
|
+
// match the exact timing of events sent by machines
|
|
696
|
+
// send actions are not executed immediately
|
|
697
|
+
defer(() => {
|
|
698
|
+
self._parent?.send({
|
|
699
|
+
type: `xstate.snapshot.${id}`,
|
|
700
|
+
data: event.data
|
|
701
|
+
});
|
|
702
|
+
});
|
|
703
|
+
return {
|
|
704
|
+
...state,
|
|
705
|
+
data: event.data
|
|
706
|
+
};
|
|
707
|
+
case errorEventType:
|
|
708
|
+
return {
|
|
709
|
+
...state,
|
|
710
|
+
status: 'error',
|
|
711
|
+
input: undefined,
|
|
712
|
+
data: event.data,
|
|
713
|
+
subscription: undefined
|
|
714
|
+
};
|
|
715
|
+
case completeEventType:
|
|
716
|
+
return {
|
|
717
|
+
...state,
|
|
718
|
+
status: 'done',
|
|
719
|
+
input: undefined,
|
|
720
|
+
subscription: undefined
|
|
721
|
+
};
|
|
722
|
+
case stopSignalType:
|
|
723
|
+
state.subscription.unsubscribe();
|
|
724
|
+
return {
|
|
725
|
+
...state,
|
|
726
|
+
status: 'canceled',
|
|
727
|
+
input: undefined,
|
|
728
|
+
subscription: undefined
|
|
729
|
+
};
|
|
730
|
+
default:
|
|
731
|
+
return state;
|
|
732
|
+
}
|
|
733
|
+
},
|
|
734
|
+
getInitialState: (_, input) => {
|
|
735
|
+
return {
|
|
736
|
+
subscription: undefined,
|
|
737
|
+
status: 'active',
|
|
738
|
+
data: undefined,
|
|
739
|
+
input
|
|
740
|
+
};
|
|
741
|
+
},
|
|
742
|
+
start: (state, {
|
|
722
743
|
self,
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
},
|
|
729
|
-
system: this.system,
|
|
730
|
-
stopChild: child => {
|
|
731
|
-
if (child._parent !== this) {
|
|
732
|
-
throw new Error(`Cannot stop child actor ${child.id} of ${this.id} because it is not a child`);
|
|
733
|
-
}
|
|
734
|
-
child._stop();
|
|
744
|
+
system
|
|
745
|
+
}) => {
|
|
746
|
+
if (state.status === 'done') {
|
|
747
|
+
// Do not restart a completed observable
|
|
748
|
+
return;
|
|
735
749
|
}
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
750
|
+
state.subscription = observableCreator({
|
|
751
|
+
input: state.input,
|
|
752
|
+
system
|
|
753
|
+
}).subscribe({
|
|
754
|
+
next: value => {
|
|
755
|
+
self.send({
|
|
756
|
+
type: nextEventType,
|
|
757
|
+
data: value
|
|
758
|
+
});
|
|
759
|
+
},
|
|
760
|
+
error: err => {
|
|
761
|
+
self.send({
|
|
762
|
+
type: errorEventType,
|
|
763
|
+
data: err
|
|
764
|
+
});
|
|
765
|
+
},
|
|
766
|
+
complete: () => {
|
|
767
|
+
self.send({
|
|
768
|
+
type: completeEventType
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
});
|
|
772
|
+
},
|
|
773
|
+
getSnapshot: state => state.data,
|
|
774
|
+
getPersistedState: ({
|
|
775
|
+
status,
|
|
776
|
+
data,
|
|
777
|
+
input
|
|
778
|
+
}) => ({
|
|
779
|
+
status,
|
|
780
|
+
data,
|
|
781
|
+
input
|
|
782
|
+
}),
|
|
783
|
+
getStatus: state => state,
|
|
784
|
+
restoreState: state => ({
|
|
785
|
+
...state,
|
|
786
|
+
subscription: undefined
|
|
787
|
+
})
|
|
788
|
+
};
|
|
789
|
+
return logic;
|
|
790
|
+
}
|
|
753
791
|
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
const status = this.logic.getStatus?.(state);
|
|
763
|
-
switch (status?.status) {
|
|
764
|
-
case 'done':
|
|
765
|
-
this._stopProcedure();
|
|
766
|
-
this._doneEvent = doneInvoke(this.id, status.data);
|
|
767
|
-
this._parent?.send(this._doneEvent);
|
|
768
|
-
this._complete();
|
|
769
|
-
break;
|
|
770
|
-
case 'error':
|
|
771
|
-
this._stopProcedure();
|
|
772
|
-
this._parent?.send(error(this.id, status.data));
|
|
773
|
-
this._error(status.data);
|
|
774
|
-
break;
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
subscribe(nextListenerOrObserver, errorListener, completeListener) {
|
|
778
|
-
const observer = toObserver(nextListenerOrObserver, errorListener, completeListener);
|
|
779
|
-
this.observers.add(observer);
|
|
780
|
-
if (this.status === ActorStatus.Stopped) {
|
|
781
|
-
observer.complete?.();
|
|
782
|
-
this.observers.delete(observer);
|
|
783
|
-
}
|
|
784
|
-
return {
|
|
785
|
-
unsubscribe: () => {
|
|
786
|
-
this.observers.delete(observer);
|
|
787
|
-
}
|
|
788
|
-
};
|
|
789
|
-
}
|
|
792
|
+
/**
|
|
793
|
+
* Creates event observable logic that listens to an observable
|
|
794
|
+
* that delivers event objects.
|
|
795
|
+
*
|
|
796
|
+
*
|
|
797
|
+
* @param lazyObservable A function that creates an observable
|
|
798
|
+
* @returns Event observable logic
|
|
799
|
+
*/
|
|
790
800
|
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
start() {
|
|
795
|
-
if (this.status === ActorStatus.Running) {
|
|
796
|
-
// Do not restart the service if it is already started
|
|
797
|
-
return this;
|
|
798
|
-
}
|
|
799
|
-
this.system._register(this.sessionId, this);
|
|
800
|
-
if (this._systemId) {
|
|
801
|
-
this.system._set(this._systemId, this);
|
|
802
|
-
}
|
|
803
|
-
this.status = ActorStatus.Running;
|
|
804
|
-
if (this.logic.start) {
|
|
805
|
-
this.logic.start(this._state, this._actorContext);
|
|
806
|
-
}
|
|
801
|
+
function fromEventObservable(lazyObservable) {
|
|
802
|
+
const errorEventType = '$$xstate.error';
|
|
803
|
+
const completeEventType = '$$xstate.complete';
|
|
807
804
|
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
}
|
|
815
|
-
this.mailbox.start();
|
|
816
|
-
return this;
|
|
817
|
-
}
|
|
818
|
-
_process(event) {
|
|
819
|
-
try {
|
|
820
|
-
const nextState = this.logic.transition(this._state, event, this._actorContext);
|
|
821
|
-
this.update(nextState);
|
|
822
|
-
if (event.type === stopSignalType) {
|
|
823
|
-
this._stopProcedure();
|
|
824
|
-
this._complete();
|
|
805
|
+
// TODO: event types
|
|
806
|
+
const logic = {
|
|
807
|
+
config: lazyObservable,
|
|
808
|
+
transition: (state, event) => {
|
|
809
|
+
if (state.status !== 'active') {
|
|
810
|
+
return state;
|
|
825
811
|
}
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
812
|
+
switch (event.type) {
|
|
813
|
+
case errorEventType:
|
|
814
|
+
return {
|
|
815
|
+
...state,
|
|
816
|
+
status: 'error',
|
|
817
|
+
input: undefined,
|
|
818
|
+
data: event.data,
|
|
819
|
+
subscription: undefined
|
|
820
|
+
};
|
|
821
|
+
case completeEventType:
|
|
822
|
+
return {
|
|
823
|
+
...state,
|
|
824
|
+
status: 'done',
|
|
825
|
+
input: undefined,
|
|
826
|
+
subscription: undefined
|
|
827
|
+
};
|
|
828
|
+
case stopSignalType:
|
|
829
|
+
state.subscription.unsubscribe();
|
|
830
|
+
return {
|
|
831
|
+
...state,
|
|
832
|
+
status: 'canceled',
|
|
833
|
+
input: undefined,
|
|
834
|
+
subscription: undefined
|
|
835
|
+
};
|
|
836
|
+
default:
|
|
837
|
+
return state;
|
|
835
838
|
}
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
_stop() {
|
|
839
|
-
if (this.status === ActorStatus.Stopped) {
|
|
840
|
-
return this;
|
|
841
|
-
}
|
|
842
|
-
this.mailbox.clear();
|
|
843
|
-
if (this.status === ActorStatus.NotStarted) {
|
|
844
|
-
this.status = ActorStatus.Stopped;
|
|
845
|
-
return this;
|
|
846
|
-
}
|
|
847
|
-
this.mailbox.enqueue({
|
|
848
|
-
type: stopSignalType
|
|
849
|
-
});
|
|
850
|
-
return this;
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
/**
|
|
854
|
-
* Stops the interpreter and unsubscribe all listeners.
|
|
855
|
-
*/
|
|
856
|
-
stop() {
|
|
857
|
-
if (this._parent) {
|
|
858
|
-
throw new Error('A non-root actor cannot be stopped directly.');
|
|
859
|
-
}
|
|
860
|
-
return this._stop();
|
|
861
|
-
}
|
|
862
|
-
_complete() {
|
|
863
|
-
for (const observer of this.observers) {
|
|
864
|
-
observer.complete?.();
|
|
865
|
-
}
|
|
866
|
-
this.observers.clear();
|
|
867
|
-
}
|
|
868
|
-
_error(data) {
|
|
869
|
-
for (const observer of this.observers) {
|
|
870
|
-
observer.error?.(data);
|
|
871
|
-
}
|
|
872
|
-
this.observers.clear();
|
|
873
|
-
}
|
|
874
|
-
_stopProcedure() {
|
|
875
|
-
if (this.status !== ActorStatus.Running) {
|
|
876
|
-
// Interpreter already stopped; do nothing
|
|
877
|
-
return this;
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
// Cancel all delayed events
|
|
881
|
-
for (const key of Object.keys(this.delayedEventsMap)) {
|
|
882
|
-
this.clock.clearTimeout(this.delayedEventsMap[key]);
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
// TODO: mailbox.reset
|
|
886
|
-
this.mailbox.clear();
|
|
887
|
-
// TODO: after `stop` we must prepare ourselves for receiving events again
|
|
888
|
-
// events sent *after* stop signal must be queued
|
|
889
|
-
// it seems like this should be the common behavior for all of our consumers
|
|
890
|
-
// so perhaps this should be unified somehow for all of them
|
|
891
|
-
this.mailbox = new Mailbox(this._process.bind(this));
|
|
892
|
-
this.status = ActorStatus.Stopped;
|
|
893
|
-
this.system._unregister(this);
|
|
894
|
-
return this;
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
/**
|
|
898
|
-
* Sends an event to the running interpreter to trigger a transition.
|
|
899
|
-
*
|
|
900
|
-
* @param event The event to send
|
|
901
|
-
*/
|
|
902
|
-
send(event) {
|
|
903
|
-
if (typeof event === 'string') {
|
|
904
|
-
throw new Error(`Only event objects may be sent to actors; use .send({ type: "${event}" }) instead`);
|
|
905
|
-
}
|
|
906
|
-
if (this.status === ActorStatus.Stopped) {
|
|
907
|
-
return;
|
|
908
|
-
}
|
|
909
|
-
if (this.status !== ActorStatus.Running && !this.options.deferEvents) {
|
|
910
|
-
throw new Error(`Event "${event.type}" was sent to uninitialized actor "${this.id
|
|
911
|
-
// tslint:disable-next-line:max-line-length
|
|
912
|
-
}". Make sure .start() is called for this actor, or set { deferEvents: true } in the actor options.\nEvent: ${JSON.stringify(event)}`);
|
|
913
|
-
}
|
|
914
|
-
this.mailbox.enqueue(event);
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
// TODO: make private (and figure out a way to do this within the machine)
|
|
918
|
-
delaySend(sendAction) {
|
|
919
|
-
this.delayedEventsMap[sendAction.params.id] = this.clock.setTimeout(() => {
|
|
920
|
-
if ('to' in sendAction.params && sendAction.params.to) {
|
|
921
|
-
sendAction.params.to.send(sendAction.params.event);
|
|
922
|
-
} else {
|
|
923
|
-
this.send(sendAction.params.event);
|
|
924
|
-
}
|
|
925
|
-
}, sendAction.params.delay);
|
|
926
|
-
}
|
|
927
|
-
|
|
928
|
-
// TODO: make private (and figure out a way to do this within the machine)
|
|
929
|
-
cancel(sendId) {
|
|
930
|
-
this.clock.clearTimeout(this.delayedEventsMap[sendId]);
|
|
931
|
-
delete this.delayedEventsMap[sendId];
|
|
932
|
-
}
|
|
933
|
-
attachDevTools() {
|
|
934
|
-
const {
|
|
935
|
-
devTools
|
|
936
|
-
} = this.options;
|
|
937
|
-
if (devTools) {
|
|
938
|
-
const resolvedDevToolsAdapter = typeof devTools === 'function' ? devTools : dev_dist_xstateDev.devToolsAdapter;
|
|
939
|
-
resolvedDevToolsAdapter(this);
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
toJSON() {
|
|
943
|
-
return {
|
|
944
|
-
id: this.id
|
|
945
|
-
};
|
|
946
|
-
}
|
|
947
|
-
getPersistedState() {
|
|
948
|
-
return this.logic.getPersistedState?.(this._state);
|
|
949
|
-
}
|
|
950
|
-
[symbolObservable]() {
|
|
951
|
-
return this;
|
|
952
|
-
}
|
|
953
|
-
getSnapshot() {
|
|
954
|
-
return this.logic.getSnapshot ? this.logic.getSnapshot(this._state) : this._state;
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
|
|
958
|
-
/**
|
|
959
|
-
* Creates a new Interpreter instance for the given machine with the provided options, if any.
|
|
960
|
-
*
|
|
961
|
-
* @param machine The machine to interpret
|
|
962
|
-
* @param options Interpreter options
|
|
963
|
-
*/
|
|
964
|
-
|
|
965
|
-
function interpret(logic, options) {
|
|
966
|
-
const interpreter = new Interpreter(logic, options);
|
|
967
|
-
return interpreter;
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
/**
|
|
971
|
-
* Returns actor logic from a transition function and its initial state.
|
|
972
|
-
*
|
|
973
|
-
* A transition function is a function that takes the current state and an event and returns the next state.
|
|
974
|
-
*
|
|
975
|
-
* @param transition The transition function that returns the next state given the current state and event.
|
|
976
|
-
* @param initialState The initial state of the transition function.
|
|
977
|
-
* @returns Actor logic
|
|
978
|
-
*/
|
|
979
|
-
function fromTransition(transition, initialState) {
|
|
980
|
-
const logic = {
|
|
981
|
-
config: transition,
|
|
982
|
-
transition: (state, event, actorContext) => {
|
|
983
|
-
return transition(state, event, actorContext);
|
|
984
839
|
},
|
|
985
840
|
getInitialState: (_, input) => {
|
|
986
|
-
return
|
|
841
|
+
return {
|
|
842
|
+
subscription: undefined,
|
|
843
|
+
status: 'active',
|
|
844
|
+
data: undefined,
|
|
987
845
|
input
|
|
988
|
-
}
|
|
989
|
-
},
|
|
990
|
-
getSnapshot: state => state,
|
|
991
|
-
getPersistedState: state => state,
|
|
992
|
-
restoreState: state => state
|
|
993
|
-
};
|
|
994
|
-
return logic;
|
|
995
|
-
}
|
|
996
|
-
|
|
997
|
-
function fromPromise(
|
|
998
|
-
// TODO: add types
|
|
999
|
-
promiseCreator) {
|
|
1000
|
-
const resolveEventType = '$$xstate.resolve';
|
|
1001
|
-
const rejectEventType = '$$xstate.reject';
|
|
1002
|
-
|
|
1003
|
-
// TODO: add event types
|
|
1004
|
-
const logic = {
|
|
1005
|
-
config: promiseCreator,
|
|
1006
|
-
transition: (state, event) => {
|
|
1007
|
-
if (state.status !== 'active') {
|
|
1008
|
-
return state;
|
|
1009
|
-
}
|
|
1010
|
-
switch (event.type) {
|
|
1011
|
-
case resolveEventType:
|
|
1012
|
-
return {
|
|
1013
|
-
...state,
|
|
1014
|
-
status: 'done',
|
|
1015
|
-
data: event.data,
|
|
1016
|
-
input: undefined
|
|
1017
|
-
};
|
|
1018
|
-
case rejectEventType:
|
|
1019
|
-
return {
|
|
1020
|
-
...state,
|
|
1021
|
-
status: 'error',
|
|
1022
|
-
data: event.data,
|
|
1023
|
-
input: undefined
|
|
1024
|
-
};
|
|
1025
|
-
case stopSignalType:
|
|
1026
|
-
return {
|
|
1027
|
-
...state,
|
|
1028
|
-
status: 'canceled',
|
|
1029
|
-
input: undefined
|
|
1030
|
-
};
|
|
1031
|
-
default:
|
|
1032
|
-
return state;
|
|
1033
|
-
}
|
|
846
|
+
};
|
|
1034
847
|
},
|
|
1035
848
|
start: (state, {
|
|
1036
849
|
self,
|
|
1037
850
|
system
|
|
1038
851
|
}) => {
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
if (state.status !== 'active') {
|
|
852
|
+
if (state.status === 'done') {
|
|
853
|
+
// Do not restart a completed observable
|
|
1042
854
|
return;
|
|
1043
855
|
}
|
|
1044
|
-
|
|
856
|
+
state.subscription = lazyObservable({
|
|
1045
857
|
input: state.input,
|
|
1046
858
|
system
|
|
1047
|
-
})
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
859
|
+
}).subscribe({
|
|
860
|
+
next: value => {
|
|
861
|
+
self._parent?.send(value);
|
|
862
|
+
},
|
|
863
|
+
error: err => {
|
|
864
|
+
self.send({
|
|
865
|
+
type: errorEventType,
|
|
866
|
+
data: err
|
|
867
|
+
});
|
|
868
|
+
},
|
|
869
|
+
complete: () => {
|
|
870
|
+
self.send({
|
|
871
|
+
type: completeEventType
|
|
872
|
+
});
|
|
1061
873
|
}
|
|
1062
|
-
self.send({
|
|
1063
|
-
type: rejectEventType,
|
|
1064
|
-
data: errorData
|
|
1065
|
-
});
|
|
1066
874
|
});
|
|
1067
875
|
},
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
876
|
+
getSnapshot: _ => undefined,
|
|
877
|
+
getPersistedState: ({
|
|
878
|
+
status,
|
|
879
|
+
data,
|
|
880
|
+
input
|
|
881
|
+
}) => ({
|
|
882
|
+
status,
|
|
883
|
+
data,
|
|
884
|
+
input
|
|
885
|
+
}),
|
|
1076
886
|
getStatus: state => state,
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
function fromObservable(observableCreator) {
|
|
1085
|
-
const nextEventType = '$$xstate.next';
|
|
1086
|
-
const errorEventType = '$$xstate.error';
|
|
1087
|
-
const completeEventType = '$$xstate.complete';
|
|
887
|
+
restoreState: state => ({
|
|
888
|
+
...state,
|
|
889
|
+
subscription: undefined
|
|
890
|
+
})
|
|
891
|
+
};
|
|
892
|
+
return logic;
|
|
893
|
+
}
|
|
1088
894
|
|
|
1089
|
-
|
|
895
|
+
function fromCallback(invokeCallback) {
|
|
1090
896
|
const logic = {
|
|
1091
|
-
config:
|
|
897
|
+
config: invokeCallback,
|
|
898
|
+
start: (_state, {
|
|
899
|
+
self
|
|
900
|
+
}) => {
|
|
901
|
+
self.send({
|
|
902
|
+
type: startSignalType
|
|
903
|
+
});
|
|
904
|
+
},
|
|
1092
905
|
transition: (state, event, {
|
|
1093
906
|
self,
|
|
1094
907
|
id,
|
|
1095
|
-
|
|
908
|
+
system
|
|
1096
909
|
}) => {
|
|
1097
|
-
if (
|
|
910
|
+
if (event.type === startSignalType) {
|
|
911
|
+
const sender = eventForParent => {
|
|
912
|
+
if (state.canceled) {
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
915
|
+
self._parent?.send(eventForParent);
|
|
916
|
+
};
|
|
917
|
+
const receiver = newListener => {
|
|
918
|
+
state.receivers.add(newListener);
|
|
919
|
+
};
|
|
920
|
+
state.dispose = invokeCallback(sender, receiver, {
|
|
921
|
+
input: state.input,
|
|
922
|
+
system
|
|
923
|
+
});
|
|
924
|
+
if (isPromiseLike(state.dispose)) {
|
|
925
|
+
state.dispose.then(resolved => {
|
|
926
|
+
self._parent?.send(doneInvoke(id, resolved));
|
|
927
|
+
state.canceled = true;
|
|
928
|
+
}, errorData => {
|
|
929
|
+
state.canceled = true;
|
|
930
|
+
self._parent?.send(error(id, errorData));
|
|
931
|
+
});
|
|
932
|
+
}
|
|
1098
933
|
return state;
|
|
1099
934
|
}
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
return {
|
|
1111
|
-
...state,
|
|
1112
|
-
data: event.data
|
|
1113
|
-
};
|
|
1114
|
-
case errorEventType:
|
|
1115
|
-
return {
|
|
1116
|
-
...state,
|
|
1117
|
-
status: 'error',
|
|
1118
|
-
input: undefined,
|
|
1119
|
-
data: event.data,
|
|
1120
|
-
subscription: undefined
|
|
1121
|
-
};
|
|
1122
|
-
case completeEventType:
|
|
1123
|
-
return {
|
|
1124
|
-
...state,
|
|
1125
|
-
status: 'done',
|
|
1126
|
-
input: undefined,
|
|
1127
|
-
subscription: undefined
|
|
1128
|
-
};
|
|
1129
|
-
case stopSignalType:
|
|
1130
|
-
state.subscription.unsubscribe();
|
|
1131
|
-
return {
|
|
1132
|
-
...state,
|
|
1133
|
-
status: 'canceled',
|
|
1134
|
-
input: undefined,
|
|
1135
|
-
subscription: undefined
|
|
1136
|
-
};
|
|
1137
|
-
default:
|
|
1138
|
-
return state;
|
|
935
|
+
if (event.type === stopSignalType) {
|
|
936
|
+
state.canceled = true;
|
|
937
|
+
if (isFunction(state.dispose)) {
|
|
938
|
+
state.dispose();
|
|
939
|
+
}
|
|
940
|
+
return state;
|
|
941
|
+
}
|
|
942
|
+
if (isSignal(event)) {
|
|
943
|
+
// TODO: unrecognized signal
|
|
944
|
+
return state;
|
|
1139
945
|
}
|
|
946
|
+
state.receivers.forEach(receiver => receiver(event));
|
|
947
|
+
return state;
|
|
1140
948
|
},
|
|
1141
949
|
getInitialState: (_, input) => {
|
|
1142
950
|
return {
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
951
|
+
canceled: false,
|
|
952
|
+
receivers: new Set(),
|
|
953
|
+
dispose: undefined,
|
|
1146
954
|
input
|
|
1147
955
|
};
|
|
1148
956
|
},
|
|
1149
|
-
|
|
1150
|
-
self,
|
|
1151
|
-
system
|
|
1152
|
-
}) => {
|
|
1153
|
-
if (state.status === 'done') {
|
|
1154
|
-
// Do not restart a completed observable
|
|
1155
|
-
return;
|
|
1156
|
-
}
|
|
1157
|
-
state.subscription = observableCreator({
|
|
1158
|
-
input: state.input,
|
|
1159
|
-
system
|
|
1160
|
-
}).subscribe({
|
|
1161
|
-
next: value => {
|
|
1162
|
-
self.send({
|
|
1163
|
-
type: nextEventType,
|
|
1164
|
-
data: value
|
|
1165
|
-
});
|
|
1166
|
-
},
|
|
1167
|
-
error: err => {
|
|
1168
|
-
self.send({
|
|
1169
|
-
type: errorEventType,
|
|
1170
|
-
data: err
|
|
1171
|
-
});
|
|
1172
|
-
},
|
|
1173
|
-
complete: () => {
|
|
1174
|
-
self.send({
|
|
1175
|
-
type: completeEventType
|
|
1176
|
-
});
|
|
1177
|
-
}
|
|
1178
|
-
});
|
|
1179
|
-
},
|
|
1180
|
-
getSnapshot: state => state.data,
|
|
957
|
+
getSnapshot: () => undefined,
|
|
1181
958
|
getPersistedState: ({
|
|
1182
|
-
status,
|
|
1183
|
-
data,
|
|
1184
|
-
input
|
|
1185
|
-
}) => ({
|
|
1186
|
-
status,
|
|
1187
|
-
data,
|
|
1188
959
|
input
|
|
1189
|
-
})
|
|
1190
|
-
getStatus: state => state,
|
|
1191
|
-
restoreState: state => ({
|
|
1192
|
-
...state,
|
|
1193
|
-
subscription: undefined
|
|
1194
|
-
})
|
|
960
|
+
}) => input
|
|
1195
961
|
};
|
|
1196
962
|
return logic;
|
|
1197
963
|
}
|
|
1198
964
|
|
|
965
|
+
const startSignalType = 'xstate.init';
|
|
966
|
+
const stopSignalType = 'xstate.stop';
|
|
967
|
+
const startSignal = {
|
|
968
|
+
type: 'xstate.init'
|
|
969
|
+
};
|
|
970
|
+
const stopSignal = {
|
|
971
|
+
type: 'xstate.stop'
|
|
972
|
+
};
|
|
1199
973
|
/**
|
|
1200
|
-
*
|
|
1201
|
-
*
|
|
1202
|
-
*
|
|
974
|
+
* An object that expresses the actor logic in reaction to received events,
|
|
975
|
+
* as well as an optionally emitted stream of values.
|
|
1203
976
|
*
|
|
1204
|
-
* @
|
|
1205
|
-
* @
|
|
977
|
+
* @template TReceived The received event
|
|
978
|
+
* @template TSnapshot The emitted value
|
|
1206
979
|
*/
|
|
1207
980
|
|
|
1208
|
-
function
|
|
1209
|
-
|
|
1210
|
-
|
|
981
|
+
function isSignal(event) {
|
|
982
|
+
return event.type === startSignalType || event.type === stopSignalType;
|
|
983
|
+
}
|
|
984
|
+
function isActorRef(item) {
|
|
985
|
+
return !!item && typeof item === 'object' && typeof item.send === 'function';
|
|
986
|
+
}
|
|
1211
987
|
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
data: event.data,
|
|
1226
|
-
subscription: undefined
|
|
1227
|
-
};
|
|
1228
|
-
case completeEventType:
|
|
1229
|
-
return {
|
|
1230
|
-
...state,
|
|
1231
|
-
status: 'done',
|
|
1232
|
-
input: undefined,
|
|
1233
|
-
subscription: undefined
|
|
1234
|
-
};
|
|
1235
|
-
case stopSignalType:
|
|
1236
|
-
state.subscription.unsubscribe();
|
|
1237
|
-
return {
|
|
1238
|
-
...state,
|
|
1239
|
-
status: 'canceled',
|
|
1240
|
-
input: undefined,
|
|
1241
|
-
subscription: undefined
|
|
1242
|
-
};
|
|
1243
|
-
default:
|
|
1244
|
-
return state;
|
|
1245
|
-
}
|
|
988
|
+
// TODO: refactor the return type, this could be written in a better way
|
|
989
|
+
// but it's best to avoid unneccessary breaking changes now
|
|
990
|
+
// @deprecated use `interpret(actorLogic)` instead
|
|
991
|
+
function toActorRef(actorRefLike) {
|
|
992
|
+
return {
|
|
993
|
+
subscribe: () => ({
|
|
994
|
+
unsubscribe: () => void 0
|
|
995
|
+
}),
|
|
996
|
+
id: 'anonymous',
|
|
997
|
+
sessionId: '',
|
|
998
|
+
getSnapshot: () => undefined,
|
|
999
|
+
[symbolObservable]: function () {
|
|
1000
|
+
return this;
|
|
1246
1001
|
},
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1002
|
+
status: ActorStatus.Running,
|
|
1003
|
+
stop: () => void 0,
|
|
1004
|
+
...actorRefLike
|
|
1005
|
+
};
|
|
1006
|
+
}
|
|
1007
|
+
const emptyLogic = fromTransition(_ => undefined, undefined);
|
|
1008
|
+
function createEmptyActor() {
|
|
1009
|
+
return interpret(emptyLogic);
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
function createSystem() {
|
|
1013
|
+
let sessionIdCounter = 0;
|
|
1014
|
+
const children = new Map();
|
|
1015
|
+
const keyedActors = new Map();
|
|
1016
|
+
const reverseKeyedActors = new WeakMap();
|
|
1017
|
+
const system = {
|
|
1018
|
+
_bookId: () => `x:${sessionIdCounter++}`,
|
|
1019
|
+
_register: (sessionId, actorRef) => {
|
|
1020
|
+
children.set(sessionId, actorRef);
|
|
1021
|
+
return sessionId;
|
|
1254
1022
|
},
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
return;
|
|
1023
|
+
_unregister: actorRef => {
|
|
1024
|
+
children.delete(actorRef.sessionId);
|
|
1025
|
+
const systemId = reverseKeyedActors.get(actorRef);
|
|
1026
|
+
if (systemId !== undefined) {
|
|
1027
|
+
keyedActors.delete(systemId);
|
|
1028
|
+
reverseKeyedActors.delete(actorRef);
|
|
1262
1029
|
}
|
|
1263
|
-
state.subscription = lazyObservable({
|
|
1264
|
-
input: state.input,
|
|
1265
|
-
system
|
|
1266
|
-
}).subscribe({
|
|
1267
|
-
next: value => {
|
|
1268
|
-
self._parent?.send(value);
|
|
1269
|
-
},
|
|
1270
|
-
error: err => {
|
|
1271
|
-
self.send({
|
|
1272
|
-
type: errorEventType,
|
|
1273
|
-
data: err
|
|
1274
|
-
});
|
|
1275
|
-
},
|
|
1276
|
-
complete: () => {
|
|
1277
|
-
self.send({
|
|
1278
|
-
type: completeEventType
|
|
1279
|
-
});
|
|
1280
|
-
}
|
|
1281
|
-
});
|
|
1282
1030
|
},
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
restoreState: state => ({
|
|
1295
|
-
...state,
|
|
1296
|
-
subscription: undefined
|
|
1297
|
-
})
|
|
1031
|
+
get: systemId => {
|
|
1032
|
+
return keyedActors.get(systemId);
|
|
1033
|
+
},
|
|
1034
|
+
_set: (systemId, actorRef) => {
|
|
1035
|
+
const existing = keyedActors.get(systemId);
|
|
1036
|
+
if (existing && existing !== actorRef) {
|
|
1037
|
+
throw new Error(`Actor with system ID '${systemId}' already exists.`);
|
|
1038
|
+
}
|
|
1039
|
+
keyedActors.set(systemId, actorRef);
|
|
1040
|
+
reverseKeyedActors.set(actorRef, systemId);
|
|
1041
|
+
}
|
|
1298
1042
|
};
|
|
1299
|
-
return
|
|
1043
|
+
return system;
|
|
1300
1044
|
}
|
|
1301
1045
|
|
|
1302
|
-
function
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
}
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1046
|
+
let ActorStatus = /*#__PURE__*/function (ActorStatus) {
|
|
1047
|
+
ActorStatus[ActorStatus["NotStarted"] = 0] = "NotStarted";
|
|
1048
|
+
ActorStatus[ActorStatus["Running"] = 1] = "Running";
|
|
1049
|
+
ActorStatus[ActorStatus["Stopped"] = 2] = "Stopped";
|
|
1050
|
+
return ActorStatus;
|
|
1051
|
+
}({});
|
|
1052
|
+
const defaultOptions = {
|
|
1053
|
+
deferEvents: true,
|
|
1054
|
+
clock: {
|
|
1055
|
+
setTimeout: (fn, ms) => {
|
|
1056
|
+
return setTimeout(fn, ms);
|
|
1057
|
+
},
|
|
1058
|
+
clearTimeout: id => {
|
|
1059
|
+
return clearTimeout(id);
|
|
1060
|
+
}
|
|
1061
|
+
},
|
|
1062
|
+
logger: console.log.bind(console),
|
|
1063
|
+
devTools: false
|
|
1064
|
+
};
|
|
1065
|
+
class Interpreter {
|
|
1066
|
+
/**
|
|
1067
|
+
* The current state of the interpreted logic.
|
|
1068
|
+
*/
|
|
1069
|
+
|
|
1070
|
+
/**
|
|
1071
|
+
* The clock that is responsible for setting and clearing timeouts, such as delayed events and transitions.
|
|
1072
|
+
*/
|
|
1073
|
+
|
|
1074
|
+
/**
|
|
1075
|
+
* The unique identifier for this actor relative to its parent.
|
|
1076
|
+
*/
|
|
1077
|
+
|
|
1078
|
+
/**
|
|
1079
|
+
* Whether the service is started.
|
|
1080
|
+
*/
|
|
1081
|
+
|
|
1082
|
+
// Actor Ref
|
|
1083
|
+
|
|
1084
|
+
// TODO: add typings for system
|
|
1085
|
+
|
|
1086
|
+
/**
|
|
1087
|
+
* The globally unique process ID for this invocation.
|
|
1088
|
+
*/
|
|
1089
|
+
|
|
1090
|
+
/**
|
|
1091
|
+
* Creates a new Interpreter instance (i.e., service) for the given logic with the provided options, if any.
|
|
1092
|
+
*
|
|
1093
|
+
* @param logic The logic to be interpreted
|
|
1094
|
+
* @param options Interpreter options
|
|
1095
|
+
*/
|
|
1096
|
+
constructor(logic, options) {
|
|
1097
|
+
this.logic = logic;
|
|
1098
|
+
this._state = void 0;
|
|
1099
|
+
this.clock = void 0;
|
|
1100
|
+
this.options = void 0;
|
|
1101
|
+
this.id = void 0;
|
|
1102
|
+
this.mailbox = new Mailbox(this._process.bind(this));
|
|
1103
|
+
this.delayedEventsMap = {};
|
|
1104
|
+
this.observers = new Set();
|
|
1105
|
+
this.logger = void 0;
|
|
1106
|
+
this.status = ActorStatus.NotStarted;
|
|
1107
|
+
this._parent = void 0;
|
|
1108
|
+
this.ref = void 0;
|
|
1109
|
+
this._actorContext = void 0;
|
|
1110
|
+
this._systemId = void 0;
|
|
1111
|
+
this.sessionId = void 0;
|
|
1112
|
+
this.system = void 0;
|
|
1113
|
+
this._doneEvent = void 0;
|
|
1114
|
+
this.src = void 0;
|
|
1115
|
+
this._deferred = [];
|
|
1116
|
+
const resolvedOptions = {
|
|
1117
|
+
...defaultOptions,
|
|
1118
|
+
...options
|
|
1119
|
+
};
|
|
1120
|
+
const {
|
|
1121
|
+
clock,
|
|
1122
|
+
logger,
|
|
1123
|
+
parent,
|
|
1124
|
+
id,
|
|
1125
|
+
systemId
|
|
1126
|
+
} = resolvedOptions;
|
|
1127
|
+
const self = this;
|
|
1128
|
+
this.system = parent?.system ?? createSystem();
|
|
1129
|
+
if (systemId) {
|
|
1130
|
+
this._systemId = systemId;
|
|
1131
|
+
this.system._set(systemId, this);
|
|
1132
|
+
}
|
|
1133
|
+
this.sessionId = this.system._bookId();
|
|
1134
|
+
this.id = id ?? this.sessionId;
|
|
1135
|
+
this.logger = logger;
|
|
1136
|
+
this.clock = clock;
|
|
1137
|
+
this._parent = parent;
|
|
1138
|
+
this.options = resolvedOptions;
|
|
1139
|
+
this.src = resolvedOptions.src;
|
|
1140
|
+
this.ref = this;
|
|
1141
|
+
this._actorContext = {
|
|
1142
|
+
self,
|
|
1143
|
+
id: this.id,
|
|
1144
|
+
sessionId: this.sessionId,
|
|
1145
|
+
logger: this.logger,
|
|
1146
|
+
defer: fn => {
|
|
1147
|
+
this._deferred.push(fn);
|
|
1148
|
+
},
|
|
1149
|
+
system: this.system,
|
|
1150
|
+
stopChild: child => {
|
|
1151
|
+
if (child._parent !== this) {
|
|
1152
|
+
throw new Error(`Cannot stop child actor ${child.id} of ${this.id} because it is not a child`);
|
|
1153
|
+
}
|
|
1154
|
+
child._stop();
|
|
1155
|
+
}
|
|
1156
|
+
};
|
|
1157
|
+
|
|
1158
|
+
// Ensure that the send method is bound to this interpreter instance
|
|
1159
|
+
// if destructured
|
|
1160
|
+
this.send = this.send.bind(this);
|
|
1161
|
+
this._initState();
|
|
1162
|
+
}
|
|
1163
|
+
_initState() {
|
|
1164
|
+
this._state = this.options.state ? this.logic.restoreState ? this.logic.restoreState(this.options.state, this._actorContext) : this.options.state : this.logic.getInitialState(this._actorContext, this.options?.input);
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
// array of functions to defer
|
|
1168
|
+
|
|
1169
|
+
update(state) {
|
|
1170
|
+
// Update state
|
|
1171
|
+
this._state = state;
|
|
1172
|
+
const snapshot = this.getSnapshot();
|
|
1173
|
+
|
|
1174
|
+
// Execute deferred effects
|
|
1175
|
+
let deferredFn;
|
|
1176
|
+
while (deferredFn = this._deferred.shift()) {
|
|
1177
|
+
deferredFn();
|
|
1178
|
+
}
|
|
1179
|
+
for (const observer of this.observers) {
|
|
1180
|
+
observer.next?.(snapshot);
|
|
1181
|
+
}
|
|
1182
|
+
const status = this.logic.getStatus?.(state);
|
|
1183
|
+
switch (status?.status) {
|
|
1184
|
+
case 'done':
|
|
1185
|
+
this._stopProcedure();
|
|
1186
|
+
this._doneEvent = doneInvoke(this.id, status.data);
|
|
1187
|
+
this._parent?.send(this._doneEvent);
|
|
1188
|
+
this._complete();
|
|
1189
|
+
break;
|
|
1190
|
+
case 'error':
|
|
1191
|
+
this._stopProcedure();
|
|
1192
|
+
this._parent?.send(error(this.id, status.data));
|
|
1193
|
+
this._error(status.data);
|
|
1194
|
+
break;
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
subscribe(nextListenerOrObserver, errorListener, completeListener) {
|
|
1198
|
+
const observer = toObserver(nextListenerOrObserver, errorListener, completeListener);
|
|
1199
|
+
this.observers.add(observer);
|
|
1200
|
+
if (this.status === ActorStatus.Stopped) {
|
|
1201
|
+
observer.complete?.();
|
|
1202
|
+
this.observers.delete(observer);
|
|
1203
|
+
}
|
|
1204
|
+
return {
|
|
1205
|
+
unsubscribe: () => {
|
|
1206
|
+
this.observers.delete(observer);
|
|
1207
|
+
}
|
|
1208
|
+
};
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
/**
|
|
1212
|
+
* Starts the interpreter from the initial state
|
|
1213
|
+
*/
|
|
1214
|
+
start() {
|
|
1215
|
+
if (this.status === ActorStatus.Running) {
|
|
1216
|
+
// Do not restart the service if it is already started
|
|
1217
|
+
return this;
|
|
1218
|
+
}
|
|
1219
|
+
this.system._register(this.sessionId, this);
|
|
1220
|
+
if (this._systemId) {
|
|
1221
|
+
this.system._set(this._systemId, this);
|
|
1222
|
+
}
|
|
1223
|
+
this.status = ActorStatus.Running;
|
|
1224
|
+
if (this.logic.start) {
|
|
1225
|
+
this.logic.start(this._state, this._actorContext);
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
// TODO: this notifies all subscribers but usually this is redundant
|
|
1229
|
+
// there is no real change happening here
|
|
1230
|
+
// we need to rethink if this needs to be refactored
|
|
1231
|
+
this.update(this._state);
|
|
1232
|
+
if (this.options.devTools) {
|
|
1233
|
+
this.attachDevTools();
|
|
1234
|
+
}
|
|
1235
|
+
this.mailbox.start();
|
|
1236
|
+
return this;
|
|
1237
|
+
}
|
|
1238
|
+
_process(event) {
|
|
1239
|
+
try {
|
|
1240
|
+
const nextState = this.logic.transition(this._state, event, this._actorContext);
|
|
1241
|
+
this.update(nextState);
|
|
1242
|
+
if (event.type === stopSignalType) {
|
|
1243
|
+
this._stopProcedure();
|
|
1244
|
+
this._complete();
|
|
1245
|
+
}
|
|
1246
|
+
} catch (err) {
|
|
1247
|
+
// TODO: properly handle errors
|
|
1248
|
+
if (this.observers.size > 0) {
|
|
1249
|
+
this.observers.forEach(observer => {
|
|
1250
|
+
observer.error?.(err);
|
|
1251
|
+
});
|
|
1252
|
+
this.stop();
|
|
1253
|
+
} else {
|
|
1254
|
+
throw err;
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
_stop() {
|
|
1259
|
+
if (this.status === ActorStatus.Stopped) {
|
|
1260
|
+
return this;
|
|
1261
|
+
}
|
|
1262
|
+
this.mailbox.clear();
|
|
1263
|
+
if (this.status === ActorStatus.NotStarted) {
|
|
1264
|
+
this.status = ActorStatus.Stopped;
|
|
1265
|
+
return this;
|
|
1266
|
+
}
|
|
1267
|
+
this.mailbox.enqueue({
|
|
1268
|
+
type: stopSignalType
|
|
1269
|
+
});
|
|
1270
|
+
return this;
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
/**
|
|
1274
|
+
* Stops the interpreter and unsubscribe all listeners.
|
|
1275
|
+
*/
|
|
1276
|
+
stop() {
|
|
1277
|
+
if (this._parent) {
|
|
1278
|
+
throw new Error('A non-root actor cannot be stopped directly.');
|
|
1279
|
+
}
|
|
1280
|
+
return this._stop();
|
|
1281
|
+
}
|
|
1282
|
+
_complete() {
|
|
1283
|
+
for (const observer of this.observers) {
|
|
1284
|
+
observer.complete?.();
|
|
1285
|
+
}
|
|
1286
|
+
this.observers.clear();
|
|
1287
|
+
}
|
|
1288
|
+
_error(data) {
|
|
1289
|
+
for (const observer of this.observers) {
|
|
1290
|
+
observer.error?.(data);
|
|
1291
|
+
}
|
|
1292
|
+
this.observers.clear();
|
|
1293
|
+
}
|
|
1294
|
+
_stopProcedure() {
|
|
1295
|
+
if (this.status !== ActorStatus.Running) {
|
|
1296
|
+
// Interpreter already stopped; do nothing
|
|
1297
|
+
return this;
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
// Cancel all delayed events
|
|
1301
|
+
for (const key of Object.keys(this.delayedEventsMap)) {
|
|
1302
|
+
this.clock.clearTimeout(this.delayedEventsMap[key]);
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
// TODO: mailbox.reset
|
|
1306
|
+
this.mailbox.clear();
|
|
1307
|
+
// TODO: after `stop` we must prepare ourselves for receiving events again
|
|
1308
|
+
// events sent *after* stop signal must be queued
|
|
1309
|
+
// it seems like this should be the common behavior for all of our consumers
|
|
1310
|
+
// so perhaps this should be unified somehow for all of them
|
|
1311
|
+
this.mailbox = new Mailbox(this._process.bind(this));
|
|
1312
|
+
this.status = ActorStatus.Stopped;
|
|
1313
|
+
this.system._unregister(this);
|
|
1314
|
+
return this;
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
/**
|
|
1318
|
+
* Sends an event to the running interpreter to trigger a transition.
|
|
1319
|
+
*
|
|
1320
|
+
* @param event The event to send
|
|
1321
|
+
*/
|
|
1322
|
+
send(event) {
|
|
1323
|
+
if (typeof event === 'string') {
|
|
1324
|
+
throw new Error(`Only event objects may be sent to actors; use .send({ type: "${event}" }) instead`);
|
|
1325
|
+
}
|
|
1326
|
+
if (this.status === ActorStatus.Stopped) {
|
|
1327
|
+
return;
|
|
1328
|
+
}
|
|
1329
|
+
if (this.status !== ActorStatus.Running && !this.options.deferEvents) {
|
|
1330
|
+
throw new Error(`Event "${event.type}" was sent to uninitialized actor "${this.id
|
|
1331
|
+
// tslint:disable-next-line:max-line-length
|
|
1332
|
+
}". Make sure .start() is called for this actor, or set { deferEvents: true } in the actor options.\nEvent: ${JSON.stringify(event)}`);
|
|
1333
|
+
}
|
|
1334
|
+
this.mailbox.enqueue(event);
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
// TODO: make private (and figure out a way to do this within the machine)
|
|
1338
|
+
delaySend(sendAction) {
|
|
1339
|
+
this.delayedEventsMap[sendAction.params.id] = this.clock.setTimeout(() => {
|
|
1340
|
+
if ('to' in sendAction.params && sendAction.params.to) {
|
|
1341
|
+
sendAction.params.to.send(sendAction.params.event);
|
|
1342
|
+
} else {
|
|
1343
|
+
this.send(sendAction.params.event);
|
|
1355
1344
|
}
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1345
|
+
}, sendAction.params.delay);
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
// TODO: make private (and figure out a way to do this within the machine)
|
|
1349
|
+
cancel(sendId) {
|
|
1350
|
+
this.clock.clearTimeout(this.delayedEventsMap[sendId]);
|
|
1351
|
+
delete this.delayedEventsMap[sendId];
|
|
1352
|
+
}
|
|
1353
|
+
attachDevTools() {
|
|
1354
|
+
const {
|
|
1355
|
+
devTools
|
|
1356
|
+
} = this.options;
|
|
1357
|
+
if (devTools) {
|
|
1358
|
+
const resolvedDevToolsAdapter = typeof devTools === 'function' ? devTools : dev_dist_xstateDev.devToolsAdapter;
|
|
1359
|
+
resolvedDevToolsAdapter(this);
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
toJSON() {
|
|
1363
|
+
return {
|
|
1364
|
+
id: this.id
|
|
1365
|
+
};
|
|
1366
|
+
}
|
|
1367
|
+
getPersistedState() {
|
|
1368
|
+
return this.logic.getPersistedState?.(this._state);
|
|
1369
|
+
}
|
|
1370
|
+
[symbolObservable]() {
|
|
1371
|
+
return this;
|
|
1372
|
+
}
|
|
1373
|
+
getSnapshot() {
|
|
1374
|
+
return this.logic.getSnapshot ? this.logic.getSnapshot(this._state) : this._state;
|
|
1375
|
+
}
|
|
1372
1376
|
}
|
|
1373
1377
|
|
|
1374
|
-
const startSignalType = 'xstate.init';
|
|
1375
|
-
const stopSignalType = 'xstate.stop';
|
|
1376
|
-
const startSignal = {
|
|
1377
|
-
type: 'xstate.init'
|
|
1378
|
-
};
|
|
1379
|
-
const stopSignal = {
|
|
1380
|
-
type: 'xstate.stop'
|
|
1381
|
-
};
|
|
1382
1378
|
/**
|
|
1383
|
-
*
|
|
1384
|
-
* as well as an optionally emitted stream of values.
|
|
1379
|
+
* Creates a new Interpreter instance for the given machine with the provided options, if any.
|
|
1385
1380
|
*
|
|
1386
|
-
* @
|
|
1387
|
-
* @
|
|
1381
|
+
* @param machine The machine to interpret
|
|
1382
|
+
* @param options Interpreter options
|
|
1388
1383
|
*/
|
|
1389
1384
|
|
|
1390
|
-
function
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
function isActorRef(item) {
|
|
1394
|
-
return !!item && typeof item === 'object' && typeof item.send === 'function';
|
|
1395
|
-
}
|
|
1396
|
-
|
|
1397
|
-
// TODO: refactor the return type, this could be written in a better way
|
|
1398
|
-
// but it's best to avoid unneccessary breaking changes now
|
|
1399
|
-
// @deprecated use `interpret(actorLogic)` instead
|
|
1400
|
-
function toActorRef(actorRefLike) {
|
|
1401
|
-
return {
|
|
1402
|
-
subscribe: () => ({
|
|
1403
|
-
unsubscribe: () => void 0
|
|
1404
|
-
}),
|
|
1405
|
-
id: 'anonymous',
|
|
1406
|
-
sessionId: '',
|
|
1407
|
-
getSnapshot: () => undefined,
|
|
1408
|
-
[symbolObservable]: function () {
|
|
1409
|
-
return this;
|
|
1410
|
-
},
|
|
1411
|
-
status: ActorStatus.Running,
|
|
1412
|
-
stop: () => void 0,
|
|
1413
|
-
...actorRefLike
|
|
1414
|
-
};
|
|
1415
|
-
}
|
|
1416
|
-
const emptyLogic = fromTransition(_ => undefined, undefined);
|
|
1417
|
-
function createEmptyActor() {
|
|
1418
|
-
return interpret(emptyLogic);
|
|
1385
|
+
function interpret(logic, options) {
|
|
1386
|
+
const interpreter = new Interpreter(logic, options);
|
|
1387
|
+
return interpreter;
|
|
1419
1388
|
}
|
|
1420
1389
|
|
|
1421
1390
|
function invoke(invokeDef) {
|
|
@@ -1432,42 +1401,32 @@ function invoke(invokeDef) {
|
|
|
1432
1401
|
src
|
|
1433
1402
|
} = invokeDef;
|
|
1434
1403
|
let resolvedInvokeAction;
|
|
1435
|
-
|
|
1404
|
+
const referenced = resolveReferencedActor(state.machine.implementations.actors[src]);
|
|
1405
|
+
if (!referenced) {
|
|
1406
|
+
resolvedInvokeAction = {
|
|
1407
|
+
type,
|
|
1408
|
+
params: invokeDef
|
|
1409
|
+
};
|
|
1410
|
+
} else {
|
|
1411
|
+
const input = 'input' in invokeDef ? invokeDef.input : referenced.input;
|
|
1412
|
+
const ref = interpret(referenced.src, {
|
|
1413
|
+
id,
|
|
1414
|
+
src,
|
|
1415
|
+
parent: actorContext?.self,
|
|
1416
|
+
systemId: invokeDef.systemId,
|
|
1417
|
+
input: typeof input === 'function' ? input({
|
|
1418
|
+
context: state.context,
|
|
1419
|
+
event,
|
|
1420
|
+
self: actorContext?.self
|
|
1421
|
+
}) : input
|
|
1422
|
+
});
|
|
1436
1423
|
resolvedInvokeAction = {
|
|
1437
1424
|
type,
|
|
1438
1425
|
params: {
|
|
1439
1426
|
...invokeDef,
|
|
1440
|
-
ref
|
|
1427
|
+
ref
|
|
1441
1428
|
}
|
|
1442
1429
|
};
|
|
1443
|
-
} else {
|
|
1444
|
-
const referenced = resolveReferencedActor(state.machine.options.actors[src]);
|
|
1445
|
-
if (!referenced) {
|
|
1446
|
-
resolvedInvokeAction = {
|
|
1447
|
-
type,
|
|
1448
|
-
params: invokeDef
|
|
1449
|
-
};
|
|
1450
|
-
} else {
|
|
1451
|
-
const input = 'input' in invokeDef ? invokeDef.input : referenced.input;
|
|
1452
|
-
const ref = interpret(referenced.src, {
|
|
1453
|
-
id,
|
|
1454
|
-
src,
|
|
1455
|
-
parent: actorContext?.self,
|
|
1456
|
-
systemId: invokeDef.systemId,
|
|
1457
|
-
input: typeof input === 'function' ? input({
|
|
1458
|
-
context: state.context,
|
|
1459
|
-
event,
|
|
1460
|
-
self: actorContext?.self
|
|
1461
|
-
}) : input
|
|
1462
|
-
});
|
|
1463
|
-
resolvedInvokeAction = {
|
|
1464
|
-
type,
|
|
1465
|
-
params: {
|
|
1466
|
-
...invokeDef,
|
|
1467
|
-
ref
|
|
1468
|
-
}
|
|
1469
|
-
};
|
|
1470
|
-
}
|
|
1471
1430
|
}
|
|
1472
1431
|
const actorRef = resolvedInvokeAction.params.ref;
|
|
1473
1432
|
const invokedState = cloneState(state, {
|
|
@@ -1579,7 +1538,7 @@ function evaluateGuard(guard, context, event, state) {
|
|
|
1579
1538
|
const {
|
|
1580
1539
|
machine
|
|
1581
1540
|
} = state;
|
|
1582
|
-
const predicate = machine?.
|
|
1541
|
+
const predicate = machine?.implementations?.guards?.[guard.type] ?? guard.predicate;
|
|
1583
1542
|
if (!predicate) {
|
|
1584
1543
|
throw new Error(`Guard '${guard.type}' is not implemented.'.`);
|
|
1585
1544
|
}
|
|
@@ -1592,14 +1551,28 @@ function evaluateGuard(guard, context, event, state) {
|
|
|
1592
1551
|
});
|
|
1593
1552
|
}
|
|
1594
1553
|
function toGuardDefinition(guardConfig, getPredicate) {
|
|
1554
|
+
// TODO: check for cycles and consider a refactor to more lazily evaluated guards
|
|
1555
|
+
// TODO: resolve this more recursively: https://github.com/statelyai/xstate/pull/4064#discussion_r1229915724
|
|
1595
1556
|
if (isString(guardConfig)) {
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1557
|
+
const predicateOrDef = getPredicate?.(guardConfig);
|
|
1558
|
+
if (isFunction(predicateOrDef)) {
|
|
1559
|
+
return {
|
|
1560
|
+
type: guardConfig,
|
|
1561
|
+
predicate: predicateOrDef,
|
|
1562
|
+
params: {
|
|
1563
|
+
type: guardConfig
|
|
1564
|
+
}
|
|
1565
|
+
};
|
|
1566
|
+
} else if (predicateOrDef) {
|
|
1567
|
+
return predicateOrDef;
|
|
1568
|
+
} else {
|
|
1569
|
+
return {
|
|
1570
|
+
type: guardConfig,
|
|
1571
|
+
params: {
|
|
1572
|
+
type: guardConfig
|
|
1573
|
+
}
|
|
1574
|
+
};
|
|
1575
|
+
}
|
|
1603
1576
|
}
|
|
1604
1577
|
if (isFunction(guardConfig)) {
|
|
1605
1578
|
return {
|
|
@@ -1611,12 +1584,24 @@ function toGuardDefinition(guardConfig, getPredicate) {
|
|
|
1611
1584
|
}
|
|
1612
1585
|
};
|
|
1613
1586
|
}
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1587
|
+
const predicateOrDef = getPredicate?.(guardConfig.type);
|
|
1588
|
+
if (isFunction(predicateOrDef)) {
|
|
1589
|
+
return {
|
|
1590
|
+
type: guardConfig.type,
|
|
1591
|
+
params: guardConfig.params || guardConfig,
|
|
1592
|
+
children: guardConfig.children?.map(childGuard => toGuardDefinition(childGuard, getPredicate)),
|
|
1593
|
+
predicate: getPredicate?.(guardConfig.type) || guardConfig.predicate
|
|
1594
|
+
};
|
|
1595
|
+
} else if (predicateOrDef) {
|
|
1596
|
+
return predicateOrDef;
|
|
1597
|
+
} else {
|
|
1598
|
+
return {
|
|
1599
|
+
type: guardConfig.type,
|
|
1600
|
+
params: guardConfig.params || guardConfig,
|
|
1601
|
+
children: guardConfig.children?.map(childGuard => toGuardDefinition(childGuard, getPredicate)),
|
|
1602
|
+
predicate: guardConfig.predicate
|
|
1603
|
+
};
|
|
1604
|
+
}
|
|
1620
1605
|
}
|
|
1621
1606
|
|
|
1622
1607
|
function getOutput(configuration, context, event) {
|
|
@@ -1815,7 +1800,7 @@ function formatTransition(stateNode, transitionConfig) {
|
|
|
1815
1800
|
const reenter = transitionConfig.reenter ?? false;
|
|
1816
1801
|
const {
|
|
1817
1802
|
guards
|
|
1818
|
-
} = stateNode.machine.
|
|
1803
|
+
} = stateNode.machine.implementations;
|
|
1819
1804
|
const target = resolveTarget(stateNode, normalizedTarget);
|
|
1820
1805
|
const transition = {
|
|
1821
1806
|
...transitionConfig,
|
|
@@ -1906,7 +1891,7 @@ function formatInitialTransition(stateNode, _target) {
|
|
|
1906
1891
|
return formatTransition(stateNode, {
|
|
1907
1892
|
target: toArray(_target.target).map(t => {
|
|
1908
1893
|
if (isString(t)) {
|
|
1909
|
-
return isStateId(t) ? t : `${
|
|
1894
|
+
return isStateId(t) ? t : `${STATE_DELIMITER}${t}`;
|
|
1910
1895
|
}
|
|
1911
1896
|
return t;
|
|
1912
1897
|
}),
|
|
@@ -1926,7 +1911,7 @@ function resolveTarget(stateNode, targets) {
|
|
|
1926
1911
|
if (isStateId(target)) {
|
|
1927
1912
|
return stateNode.machine.getStateNodeById(target);
|
|
1928
1913
|
}
|
|
1929
|
-
const isInternalTarget = target[0] ===
|
|
1914
|
+
const isInternalTarget = target[0] === STATE_DELIMITER;
|
|
1930
1915
|
// If internal target is defined on machine,
|
|
1931
1916
|
// do not include machine key on target
|
|
1932
1917
|
if (isInternalTarget && !stateNode.parent) {
|
|
@@ -2009,7 +1994,7 @@ function getStateNodeByPath(stateNode, statePath) {
|
|
|
2009
1994
|
// throw e;
|
|
2010
1995
|
}
|
|
2011
1996
|
}
|
|
2012
|
-
const arrayStatePath = toStatePath(statePath
|
|
1997
|
+
const arrayStatePath = toStatePath(statePath).slice();
|
|
2013
1998
|
let currentStateNode = stateNode;
|
|
2014
1999
|
while (arrayStatePath.length) {
|
|
2015
2000
|
const key = arrayStatePath.shift();
|
|
@@ -2027,7 +2012,7 @@ function getStateNodeByPath(stateNode, statePath) {
|
|
|
2027
2012
|
* @param state The state value or State instance
|
|
2028
2013
|
*/
|
|
2029
2014
|
function getStateNodes(stateNode, state) {
|
|
2030
|
-
const stateValue = state instanceof State ? state.value : toStateValue(state
|
|
2015
|
+
const stateValue = state instanceof State ? state.value : toStateValue(state);
|
|
2031
2016
|
if (isString(stateValue)) {
|
|
2032
2017
|
return [stateNode, stateNode.states[stateValue]];
|
|
2033
2018
|
}
|
|
@@ -2220,40 +2205,18 @@ function computeExitSet(transitions, configuration, historyValue) {
|
|
|
2220
2205
|
* @param mutConfiguration
|
|
2221
2206
|
*/
|
|
2222
2207
|
|
|
2223
|
-
function microstep(transitions, currentState, actorCtx, event) {
|
|
2224
|
-
const {
|
|
2225
|
-
machine
|
|
2226
|
-
} = currentState;
|
|
2227
|
-
// Transition will "apply" if:
|
|
2228
|
-
// - the state node is the initial state (there is no current state)
|
|
2229
|
-
// - OR there are transitions
|
|
2230
|
-
const willTransition = currentState._initial || transitions.length > 0;
|
|
2208
|
+
function microstep(transitions, currentState, actorCtx, event, isInitial) {
|
|
2231
2209
|
const mutConfiguration = new Set(currentState.configuration);
|
|
2232
|
-
if (!
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
target: [...currentState.configuration].filter(isAtomicStateNode),
|
|
2239
|
-
source: machine.root,
|
|
2240
|
-
reenter: true,
|
|
2241
|
-
actions: [],
|
|
2242
|
-
eventType: null,
|
|
2243
|
-
toJSON: null // TODO: fix
|
|
2244
|
-
}] : transitions, currentState, mutConfiguration, event, actorCtx);
|
|
2245
|
-
const {
|
|
2246
|
-
context
|
|
2247
|
-
} = microstate;
|
|
2248
|
-
const nextState = cloneState(microstate, {
|
|
2249
|
-
value: {},
|
|
2250
|
-
// TODO: make optional
|
|
2251
|
-
transitions
|
|
2210
|
+
if (!transitions.length) {
|
|
2211
|
+
return currentState;
|
|
2212
|
+
}
|
|
2213
|
+
const microstate = microstepProcedure(transitions, currentState, mutConfiguration, event, actorCtx, isInitial);
|
|
2214
|
+
return cloneState(microstate, {
|
|
2215
|
+
value: {} // TODO: make optional
|
|
2252
2216
|
});
|
|
2253
|
-
nextState.changed = currentState._initial ? undefined : !stateValuesEqual(nextState.value, currentState.value) || actions.length > 0 || context !== currentState.context;
|
|
2254
|
-
return nextState;
|
|
2255
2217
|
}
|
|
2256
|
-
|
|
2218
|
+
|
|
2219
|
+
function microstepProcedure(transitions, currentState, mutConfiguration, event, actorCtx, isInitial) {
|
|
2257
2220
|
const actions = [];
|
|
2258
2221
|
const historyValue = {
|
|
2259
2222
|
...currentState.historyValue
|
|
@@ -2262,7 +2225,7 @@ function microstepProcedure(transitions, currentState, mutConfiguration, event,
|
|
|
2262
2225
|
const internalQueue = [...currentState._internalQueue];
|
|
2263
2226
|
|
|
2264
2227
|
// Exit states
|
|
2265
|
-
if (!
|
|
2228
|
+
if (!isInitial) {
|
|
2266
2229
|
exitStates(filteredTransitions, mutConfiguration, historyValue, actions);
|
|
2267
2230
|
}
|
|
2268
2231
|
|
|
@@ -2270,7 +2233,7 @@ function microstepProcedure(transitions, currentState, mutConfiguration, event,
|
|
|
2270
2233
|
actions.push(...filteredTransitions.flatMap(t => t.actions));
|
|
2271
2234
|
|
|
2272
2235
|
// Enter states
|
|
2273
|
-
enterStates(event, filteredTransitions, mutConfiguration, actions, internalQueue, currentState, historyValue);
|
|
2236
|
+
enterStates(event, filteredTransitions, mutConfiguration, actions, internalQueue, currentState, historyValue, isInitial);
|
|
2274
2237
|
const nextConfiguration = [...mutConfiguration];
|
|
2275
2238
|
const done = isInFinalState(nextConfiguration);
|
|
2276
2239
|
if (done) {
|
|
@@ -2278,10 +2241,10 @@ function microstepProcedure(transitions, currentState, mutConfiguration, event,
|
|
|
2278
2241
|
actions.push(...finalActions);
|
|
2279
2242
|
}
|
|
2280
2243
|
try {
|
|
2281
|
-
const
|
|
2244
|
+
const nextState = resolveActionsAndContext(actions, event, currentState, actorCtx);
|
|
2282
2245
|
const output = done ? getOutput(nextConfiguration, nextState.context, event) : undefined;
|
|
2283
2246
|
internalQueue.push(...nextState._internalQueue);
|
|
2284
|
-
return
|
|
2247
|
+
return cloneState(currentState, {
|
|
2285
2248
|
configuration: nextConfiguration,
|
|
2286
2249
|
historyValue,
|
|
2287
2250
|
_internalQueue: internalQueue,
|
|
@@ -2289,20 +2252,20 @@ function microstepProcedure(transitions, currentState, mutConfiguration, event,
|
|
|
2289
2252
|
done,
|
|
2290
2253
|
output,
|
|
2291
2254
|
children: nextState.children
|
|
2292
|
-
})
|
|
2255
|
+
});
|
|
2293
2256
|
} catch (e) {
|
|
2294
2257
|
// TODO: Refactor this once proper error handling is implemented.
|
|
2295
2258
|
// See https://github.com/statelyai/rfcs/pull/4
|
|
2296
2259
|
throw e;
|
|
2297
2260
|
}
|
|
2298
2261
|
}
|
|
2299
|
-
function enterStates(event, filteredTransitions, mutConfiguration, actions, internalQueue, currentState, historyValue) {
|
|
2262
|
+
function enterStates(event, filteredTransitions, mutConfiguration, actions, internalQueue, currentState, historyValue, isInitial) {
|
|
2300
2263
|
const statesToEnter = new Set();
|
|
2301
2264
|
const statesForDefaultEntry = new Set();
|
|
2302
2265
|
computeEntrySet(filteredTransitions, historyValue, statesForDefaultEntry, statesToEnter);
|
|
2303
2266
|
|
|
2304
2267
|
// In the initial state, the root state node is "entered".
|
|
2305
|
-
if (
|
|
2268
|
+
if (isInitial) {
|
|
2306
2269
|
statesForDefaultEntry.add(currentState.machine.root);
|
|
2307
2270
|
}
|
|
2308
2271
|
for (const stateNodeToEnter of [...statesToEnter].sort((a, b) => a.order - b.order)) {
|
|
@@ -2427,7 +2390,7 @@ function exitStates(transitions, mutConfiguration, historyValue, actions) {
|
|
|
2427
2390
|
}
|
|
2428
2391
|
}
|
|
2429
2392
|
for (const s of statesToExit) {
|
|
2430
|
-
actions.push(...s.exit
|
|
2393
|
+
actions.push(...s.exit, ...s.invoke.map(def => stop(def.id)));
|
|
2431
2394
|
mutConfiguration.delete(s);
|
|
2432
2395
|
}
|
|
2433
2396
|
}
|
|
@@ -2435,11 +2398,9 @@ function resolveActionsAndContext(actions, event, currentState, actorCtx) {
|
|
|
2435
2398
|
const {
|
|
2436
2399
|
machine
|
|
2437
2400
|
} = currentState;
|
|
2438
|
-
const resolvedActions = [];
|
|
2439
2401
|
const raiseActions = [];
|
|
2440
2402
|
let intermediateState = currentState;
|
|
2441
2403
|
function handleAction(action) {
|
|
2442
|
-
resolvedActions.push(action);
|
|
2443
2404
|
if (actorCtx?.self.status === ActorStatus.Running) {
|
|
2444
2405
|
action.execute?.(actorCtx);
|
|
2445
2406
|
} else {
|
|
@@ -2447,7 +2408,7 @@ function resolveActionsAndContext(actions, event, currentState, actorCtx) {
|
|
|
2447
2408
|
}
|
|
2448
2409
|
}
|
|
2449
2410
|
function resolveAction(actionObject) {
|
|
2450
|
-
const executableActionObject = resolveActionObject(actionObject, machine.
|
|
2411
|
+
const executableActionObject = resolveActionObject(actionObject, machine.implementations.actions);
|
|
2451
2412
|
if (isDynamicAction(executableActionObject)) {
|
|
2452
2413
|
const [nextState, resolvedAction] = executableActionObject.resolve(event, {
|
|
2453
2414
|
state: intermediateState,
|
|
@@ -2456,7 +2417,7 @@ function resolveActionsAndContext(actions, event, currentState, actorCtx) {
|
|
|
2456
2417
|
});
|
|
2457
2418
|
const matchedActions = resolvedAction.params?.actions;
|
|
2458
2419
|
intermediateState = nextState;
|
|
2459
|
-
if (
|
|
2420
|
+
if (resolvedAction.type === raise$1 && typeof resolvedAction.params.delay !== 'number') {
|
|
2460
2421
|
raiseActions.push(resolvedAction);
|
|
2461
2422
|
}
|
|
2462
2423
|
|
|
@@ -2472,9 +2433,9 @@ function resolveActionsAndContext(actions, event, currentState, actorCtx) {
|
|
|
2472
2433
|
for (const actionObject of actions) {
|
|
2473
2434
|
resolveAction(actionObject);
|
|
2474
2435
|
}
|
|
2475
|
-
return
|
|
2436
|
+
return cloneState(intermediateState, {
|
|
2476
2437
|
_internalQueue: raiseActions.map(a => a.params.event)
|
|
2477
|
-
})
|
|
2438
|
+
});
|
|
2478
2439
|
}
|
|
2479
2440
|
function macrostep(state, event, actorCtx) {
|
|
2480
2441
|
let nextState = state;
|
|
@@ -2482,7 +2443,7 @@ function macrostep(state, event, actorCtx) {
|
|
|
2482
2443
|
|
|
2483
2444
|
// Handle stop event
|
|
2484
2445
|
if (event.type === stopSignalType) {
|
|
2485
|
-
nextState = stopStep(event, nextState, actorCtx)
|
|
2446
|
+
nextState = stopStep(event, nextState, actorCtx);
|
|
2486
2447
|
states.push(nextState);
|
|
2487
2448
|
return {
|
|
2488
2449
|
state: nextState,
|
|
@@ -2495,7 +2456,7 @@ function macrostep(state, event, actorCtx) {
|
|
|
2495
2456
|
// Determine the next state based on the next microstep
|
|
2496
2457
|
if (nextEvent.type !== init) {
|
|
2497
2458
|
const transitions = selectTransitions(nextEvent, nextState);
|
|
2498
|
-
nextState = microstep(transitions, state, actorCtx, nextEvent);
|
|
2459
|
+
nextState = microstep(transitions, state, actorCtx, nextEvent, false);
|
|
2499
2460
|
states.push(nextState);
|
|
2500
2461
|
}
|
|
2501
2462
|
while (!nextState.done) {
|
|
@@ -2506,12 +2467,12 @@ function macrostep(state, event, actorCtx) {
|
|
|
2506
2467
|
} else {
|
|
2507
2468
|
nextEvent = nextState._internalQueue[0];
|
|
2508
2469
|
const transitions = selectTransitions(nextEvent, nextState);
|
|
2509
|
-
nextState = microstep(transitions, nextState, actorCtx, nextEvent);
|
|
2470
|
+
nextState = microstep(transitions, nextState, actorCtx, nextEvent, false);
|
|
2510
2471
|
nextState._internalQueue.shift();
|
|
2511
2472
|
states.push(nextState);
|
|
2512
2473
|
}
|
|
2513
2474
|
} else {
|
|
2514
|
-
nextState = microstep(enabledTransitions, nextState, actorCtx, nextEvent);
|
|
2475
|
+
nextState = microstep(enabledTransitions, nextState, actorCtx, nextEvent, false);
|
|
2515
2476
|
states.push(nextState);
|
|
2516
2477
|
}
|
|
2517
2478
|
}
|
|
@@ -2565,20 +2526,6 @@ function resolveStateValue(rootNode, stateValue) {
|
|
|
2565
2526
|
const configuration = getConfiguration(getStateNodes(rootNode, stateValue));
|
|
2566
2527
|
return getStateValue(rootNode, [...configuration]);
|
|
2567
2528
|
}
|
|
2568
|
-
function stateValuesEqual(a, b) {
|
|
2569
|
-
if (a === b) {
|
|
2570
|
-
return true;
|
|
2571
|
-
}
|
|
2572
|
-
if (a === undefined || b === undefined) {
|
|
2573
|
-
return false;
|
|
2574
|
-
}
|
|
2575
|
-
if (isString(a) || isString(b)) {
|
|
2576
|
-
return a === b;
|
|
2577
|
-
}
|
|
2578
|
-
const aKeys = Object.keys(a);
|
|
2579
|
-
const bKeys = Object.keys(b);
|
|
2580
|
-
return aKeys.length === bKeys.length && aKeys.every(key => stateValuesEqual(a[key], b[key]));
|
|
2581
|
-
}
|
|
2582
2529
|
function getInitialConfiguration(rootNode) {
|
|
2583
2530
|
const configuration = [];
|
|
2584
2531
|
const initialTransition = rootNode.initial;
|
|
@@ -2601,15 +2548,6 @@ class State {
|
|
|
2601
2548
|
*/
|
|
2602
2549
|
// TODO: add an explicit type for `output`
|
|
2603
2550
|
|
|
2604
|
-
/**
|
|
2605
|
-
* Indicates whether the state has changed from the previous state. A state is considered "changed" if:
|
|
2606
|
-
*
|
|
2607
|
-
* - Its value is not equal to its previous value, or:
|
|
2608
|
-
* - It has any new actions (side-effects) to execute.
|
|
2609
|
-
*
|
|
2610
|
-
* An initial state (with no history) will return `undefined`.
|
|
2611
|
-
*/
|
|
2612
|
-
|
|
2613
2551
|
/**
|
|
2614
2552
|
* The enabled state nodes representative of the state value.
|
|
2615
2553
|
*/
|
|
@@ -2632,7 +2570,6 @@ class State {
|
|
|
2632
2570
|
meta: {},
|
|
2633
2571
|
configuration: [],
|
|
2634
2572
|
// TODO: fix,
|
|
2635
|
-
transitions: [],
|
|
2636
2573
|
children: {}
|
|
2637
2574
|
}, machine);
|
|
2638
2575
|
}
|
|
@@ -2662,8 +2599,6 @@ class State {
|
|
|
2662
2599
|
this.context = void 0;
|
|
2663
2600
|
this.historyValue = {};
|
|
2664
2601
|
this._internalQueue = void 0;
|
|
2665
|
-
this._initial = false;
|
|
2666
|
-
this.changed = void 0;
|
|
2667
2602
|
this.configuration = void 0;
|
|
2668
2603
|
this.children = void 0;
|
|
2669
2604
|
this.context = config.context;
|
|
@@ -2684,12 +2619,12 @@ class State {
|
|
|
2684
2619
|
* @param stateValue
|
|
2685
2620
|
* @param delimiter The character(s) that separate each subpath in the string state node path.
|
|
2686
2621
|
*/
|
|
2687
|
-
toStrings(stateValue = this.value
|
|
2622
|
+
toStrings(stateValue = this.value) {
|
|
2688
2623
|
if (isString(stateValue)) {
|
|
2689
2624
|
return [stateValue];
|
|
2690
2625
|
}
|
|
2691
2626
|
const valueKeys = Object.keys(stateValue);
|
|
2692
|
-
return valueKeys.concat(...valueKeys.map(key => this.toStrings(stateValue[key]
|
|
2627
|
+
return valueKeys.concat(...valueKeys.map(key => this.toStrings(stateValue[key]).map(s => key + STATE_DELIMITER + s)));
|
|
2693
2628
|
}
|
|
2694
2629
|
toJSON() {
|
|
2695
2630
|
const {
|
|
@@ -2794,11 +2729,14 @@ function stop(actorRef) {
|
|
|
2794
2729
|
actor
|
|
2795
2730
|
}
|
|
2796
2731
|
}, (event, {
|
|
2797
|
-
state
|
|
2732
|
+
state,
|
|
2733
|
+
actorContext
|
|
2798
2734
|
}) => {
|
|
2799
2735
|
const actorRefOrString = isFunction(actor) ? actor({
|
|
2800
2736
|
context: state.context,
|
|
2801
|
-
event
|
|
2737
|
+
event,
|
|
2738
|
+
self: actorContext?.self ?? {},
|
|
2739
|
+
system: actorContext?.system
|
|
2802
2740
|
}) : actor;
|
|
2803
2741
|
const actorRef = typeof actorRefOrString === 'string' ? state.children[actorRefOrString] : actorRefOrString;
|
|
2804
2742
|
let children = state.children;
|
|
@@ -2882,61 +2820,60 @@ function log(expr = defaultLogExpr, label) {
|
|
|
2882
2820
|
});
|
|
2883
2821
|
}
|
|
2884
2822
|
|
|
2885
|
-
function createSpawner(
|
|
2886
|
-
|
|
2823
|
+
function createSpawner(actorContext, {
|
|
2824
|
+
machine,
|
|
2825
|
+
context
|
|
2826
|
+
}, event, spawnedChildren) {
|
|
2827
|
+
const spawn = (src, options = {}) => {
|
|
2887
2828
|
const {
|
|
2888
2829
|
systemId
|
|
2889
2830
|
} = options;
|
|
2890
|
-
if (
|
|
2891
|
-
const referenced = resolveReferencedActor(machine.
|
|
2892
|
-
if (referenced) {
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
})
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
// TODO
|
|
2911
|
-
ref: actorRef,
|
|
2912
|
-
meta: undefined,
|
|
2913
|
-
input,
|
|
2914
|
-
systemId
|
|
2915
|
-
}));
|
|
2916
|
-
return actorRef; // TODO: fix types
|
|
2917
|
-
}
|
|
2918
|
-
|
|
2919
|
-
throw new Error(`Actor logic '${src}' not implemented in machine '${machine.id}'`);
|
|
2831
|
+
if (typeof src === 'string') {
|
|
2832
|
+
const referenced = resolveReferencedActor(machine.implementations.actors[src]);
|
|
2833
|
+
if (!referenced) {
|
|
2834
|
+
throw new Error(`Actor logic '${src}' not implemented in machine '${machine.id}'`);
|
|
2835
|
+
}
|
|
2836
|
+
const input = 'input' in options ? options.input : referenced.input;
|
|
2837
|
+
|
|
2838
|
+
// TODO: this should also receive `src`
|
|
2839
|
+
const actor = interpret(referenced.src, {
|
|
2840
|
+
id: options.id,
|
|
2841
|
+
parent: actorContext.self,
|
|
2842
|
+
input: typeof input === 'function' ? input({
|
|
2843
|
+
context,
|
|
2844
|
+
event,
|
|
2845
|
+
self: actorContext.self
|
|
2846
|
+
}) : input,
|
|
2847
|
+
systemId
|
|
2848
|
+
});
|
|
2849
|
+
spawnedChildren[actor.id] = actor;
|
|
2850
|
+
return actor;
|
|
2920
2851
|
} else {
|
|
2921
2852
|
// TODO: this should also receive `src`
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
parent: self,
|
|
2853
|
+
return interpret(src, {
|
|
2854
|
+
id: options.id,
|
|
2855
|
+
parent: actorContext.self,
|
|
2926
2856
|
input: options.input,
|
|
2927
2857
|
systemId
|
|
2928
2858
|
});
|
|
2929
|
-
mutCapturedActions.push(invoke({
|
|
2930
|
-
// @ts-ignore TODO: fix types
|
|
2931
|
-
src: actorRef,
|
|
2932
|
-
ref: actorRef,
|
|
2933
|
-
id: actorRef.id,
|
|
2934
|
-
meta: undefined,
|
|
2935
|
-
input: options.input
|
|
2936
|
-
}));
|
|
2937
|
-
return actorRef; // TODO: fix types
|
|
2938
2859
|
}
|
|
2939
2860
|
};
|
|
2861
|
+
return (src, options) => {
|
|
2862
|
+
const actorRef = spawn(src, options);
|
|
2863
|
+
spawnedChildren[actorRef.id] = actorRef;
|
|
2864
|
+
actorContext.defer(() => {
|
|
2865
|
+
if (actorRef.status === ActorStatus.Stopped) {
|
|
2866
|
+
return;
|
|
2867
|
+
}
|
|
2868
|
+
try {
|
|
2869
|
+
actorRef.start?.();
|
|
2870
|
+
} catch (err) {
|
|
2871
|
+
actorContext.self.send(error(actorRef.id, err));
|
|
2872
|
+
return;
|
|
2873
|
+
}
|
|
2874
|
+
});
|
|
2875
|
+
return actorRef;
|
|
2876
|
+
};
|
|
2940
2877
|
}
|
|
2941
2878
|
|
|
2942
2879
|
/**
|
|
@@ -2955,15 +2892,15 @@ function assign(assignment) {
|
|
|
2955
2892
|
action,
|
|
2956
2893
|
actorContext
|
|
2957
2894
|
}) => {
|
|
2958
|
-
const capturedActions = [];
|
|
2959
2895
|
if (!state.context) {
|
|
2960
2896
|
throw new Error('Cannot assign to undefined `context`. Ensure that `context` is defined in the machine config.');
|
|
2961
2897
|
}
|
|
2898
|
+
const spawnedChildren = {};
|
|
2962
2899
|
const args = {
|
|
2963
2900
|
context: state.context,
|
|
2964
2901
|
event,
|
|
2965
2902
|
action,
|
|
2966
|
-
spawn: createSpawner(actorContext
|
|
2903
|
+
spawn: createSpawner(actorContext, state, event, spawnedChildren),
|
|
2967
2904
|
self: actorContext?.self ?? {},
|
|
2968
2905
|
system: actorContext?.system
|
|
2969
2906
|
};
|
|
@@ -2978,12 +2915,15 @@ function assign(assignment) {
|
|
|
2978
2915
|
}
|
|
2979
2916
|
const updatedContext = Object.assign({}, state.context, partialUpdate);
|
|
2980
2917
|
return [cloneState(state, {
|
|
2981
|
-
context: updatedContext
|
|
2918
|
+
context: updatedContext,
|
|
2919
|
+
children: Object.keys(spawnedChildren).length ? {
|
|
2920
|
+
...state.children,
|
|
2921
|
+
...spawnedChildren
|
|
2922
|
+
} : state.children
|
|
2982
2923
|
}), {
|
|
2983
2924
|
type: assign$1,
|
|
2984
2925
|
params: {
|
|
2985
|
-
context: updatedContext
|
|
2986
|
-
actions: capturedActions
|
|
2926
|
+
context: updatedContext
|
|
2987
2927
|
}
|
|
2988
2928
|
}];
|
|
2989
2929
|
});
|
|
@@ -3019,7 +2959,7 @@ function raise(eventOrExpr, options) {
|
|
|
3019
2959
|
self: actorContext?.self ?? {},
|
|
3020
2960
|
system: actorContext?.system
|
|
3021
2961
|
};
|
|
3022
|
-
const delaysMap = state.machine.
|
|
2962
|
+
const delaysMap = state.machine.implementations.delays;
|
|
3023
2963
|
|
|
3024
2964
|
// TODO: helper function for resolving Expr
|
|
3025
2965
|
if (typeof eventOrExpr === 'string') {
|
|
@@ -3061,7 +3001,7 @@ function choose(guards) {
|
|
|
3061
3001
|
state
|
|
3062
3002
|
}) => {
|
|
3063
3003
|
const matchedActions = guards.find(condition => {
|
|
3064
|
-
const guard = condition.guard && toGuardDefinition(condition.guard, guardType => state.machine.
|
|
3004
|
+
const guard = condition.guard && toGuardDefinition(condition.guard, guardType => state.machine.implementations.guards[guardType]);
|
|
3065
3005
|
return !guard || evaluateGuard(guard, state.context, event, state);
|
|
3066
3006
|
})?.actions;
|
|
3067
3007
|
return [state, {
|
|
@@ -3094,9 +3034,6 @@ function pure(getActions) {
|
|
|
3094
3034
|
});
|
|
3095
3035
|
}
|
|
3096
3036
|
|
|
3097
|
-
const initEvent = {
|
|
3098
|
-
type: init
|
|
3099
|
-
};
|
|
3100
3037
|
function resolveActionObject(actionObject, actionFunctionMap) {
|
|
3101
3038
|
if (isDynamicAction(actionObject)) {
|
|
3102
3039
|
return actionObject;
|
|
@@ -3257,7 +3194,6 @@ exports.choose = choose;
|
|
|
3257
3194
|
exports.createEmptyActor = createEmptyActor;
|
|
3258
3195
|
exports.createInitEvent = createInitEvent;
|
|
3259
3196
|
exports.createInvokeId = createInvokeId;
|
|
3260
|
-
exports.createSpawner = createSpawner;
|
|
3261
3197
|
exports.done = done;
|
|
3262
3198
|
exports.doneInvoke = doneInvoke;
|
|
3263
3199
|
exports.error = error;
|
|
@@ -3280,10 +3216,10 @@ exports.getInitialConfiguration = getInitialConfiguration;
|
|
|
3280
3216
|
exports.getPersistedState = getPersistedState;
|
|
3281
3217
|
exports.getStateNodeByPath = getStateNodeByPath;
|
|
3282
3218
|
exports.getStateNodes = getStateNodes;
|
|
3283
|
-
exports.initEvent = initEvent;
|
|
3284
3219
|
exports.interpret = interpret;
|
|
3285
3220
|
exports.invoke = invoke$1;
|
|
3286
3221
|
exports.isActorRef = isActorRef;
|
|
3222
|
+
exports.isAtomicStateNode = isAtomicStateNode;
|
|
3287
3223
|
exports.isErrorEvent = isErrorEvent;
|
|
3288
3224
|
exports.isInFinalState = isInFinalState;
|
|
3289
3225
|
exports.isSignal = isSignal;
|
|
@@ -3304,7 +3240,6 @@ exports.resolveActionObject = resolveActionObject;
|
|
|
3304
3240
|
exports.resolveActionsAndContext = resolveActionsAndContext;
|
|
3305
3241
|
exports.resolveReferencedActor = resolveReferencedActor;
|
|
3306
3242
|
exports.resolveStateValue = resolveStateValue;
|
|
3307
|
-
exports.send = send;
|
|
3308
3243
|
exports.sendParent = sendParent;
|
|
3309
3244
|
exports.sendTo = sendTo;
|
|
3310
3245
|
exports.startSignal = startSignal;
|