-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
Copy pathstop.ts
115 lines (107 loc) · 3.12 KB
/
stop.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import isDevelopment from '#is-development';
import { cloneState } from '../State.ts';
import { ActorStatus } from '../interpreter.ts';
import {
ActionArgs,
ActorRef,
AnyActorContext,
AnyState,
EventObject,
MachineContext,
ParameterizedObject
} from '../types.ts';
type ResolvableActorRef<
TContext extends MachineContext,
TExpressionEvent extends EventObject,
TExpressionAction extends ParameterizedObject | undefined,
TEvent extends EventObject
> =
| string
| ActorRef<any, any>
| ((
args: ActionArgs<TContext, TExpressionEvent, TExpressionAction, TEvent>
) => ActorRef<any, any> | string);
function resolveStop(
_: AnyActorContext,
state: AnyState,
args: ActionArgs<any, any, any, any>,
{ actorRef }: { actorRef: ResolvableActorRef<any, any, any, any> }
) {
const actorRefOrString =
typeof actorRef === 'function' ? actorRef(args) : actorRef;
const resolvedActorRef: ActorRef<any, any> | undefined =
typeof actorRefOrString === 'string'
? state.children[actorRefOrString]
: actorRefOrString;
let children = state.children;
if (resolvedActorRef) {
children = { ...children };
delete children[resolvedActorRef.id];
}
return [
cloneState(state, {
children
}),
resolvedActorRef
];
}
function executeStop(
actorContext: AnyActorContext,
actorRef: ActorRef<any, any> | undefined
) {
if (!actorRef) {
return;
}
// this allows us to prevent an actor from being started if it gets stopped within the same macrostep
// this can happen, for example, when the invoking state is being exited immediately by an always transition
if (actorRef.status !== ActorStatus.Running) {
actorContext.stopChild(actorRef);
return;
}
// stopping a child enqueues a stop event in the child actor's mailbox
// we need for all of the already enqueued events to be processed before we stop the child
// the parent itself might want to send some events to a child (for example from exit actions on the invoking state)
// and we don't want to ignore those events
actorContext.defer(() => {
actorContext.stopChild(actorRef);
});
}
export interface StopAction<
TContext extends MachineContext,
TExpressionEvent extends EventObject,
TExpressionAction extends ParameterizedObject | undefined,
TEvent extends EventObject
> {
(_: ActionArgs<TContext, TExpressionEvent, TExpressionAction, TEvent>): void;
}
/**
* Stops an actor.
*
* @param actorRef The actor to stop.
*/
export function stop<
TContext extends MachineContext,
TExpressionEvent extends EventObject,
TExpressionAction extends ParameterizedObject | undefined,
TEvent extends EventObject
>(
actorRef: ResolvableActorRef<
TContext,
TExpressionEvent,
TExpressionAction,
TEvent
>
): StopAction<TContext, TExpressionEvent, TExpressionAction, TEvent> {
function stop(
_: ActionArgs<TContext, TExpressionEvent, TExpressionAction, TEvent>
) {
if (isDevelopment) {
throw new Error(`This isn't supposed to be called`);
}
}
stop.type = 'xstate.stop';
stop.actorRef = actorRef;
stop.resolve = resolveStop;
stop.execute = executeStop;
return stop;
}