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