Skip to content

Commit

Permalink
Make updated context available to invokes (#4351)
Browse files Browse the repository at this point in the history
* Call actions while executing algorithm and not at the end of it

* Make updated context available to invokes

* add a test case for delayed events

* add a test case for done.state output

* add changeset
  • Loading branch information
Andarist committed Oct 13, 2023
1 parent f9b17f1 commit 6f18183
Show file tree
Hide file tree
Showing 10 changed files with 281 additions and 107 deletions.
5 changes: 5 additions & 0 deletions .changeset/beige-days-nail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'xstate': patch
---

Fixed an issue that prevented `invoke.input` from seeing the context updated by the same-level `entry` actions.
11 changes: 4 additions & 7 deletions packages/core/src/StateMachine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -417,12 +417,9 @@ export class StateMachine<
if (typeof context === 'function') {
const assignment = ({ spawn, event }: any) =>
context({ spawn, input: event.input });
return resolveActionsAndContext(
[assign(assignment)],
initEvent as TEvent,
preInitial,
actorCtx
) as SnapshotFrom<this>;
return resolveActionsAndContext(preInitial, initEvent, actorCtx, [
assign(assignment)
]) as SnapshotFrom<this>;
}

return preInitial;
Expand Down Expand Up @@ -493,7 +490,7 @@ export class StateMachine<
): void {
Object.values(state.children).forEach((child: any) => {
if (child.status === 0) {
child.start?.();
child.start();
}
});
}
Expand Down
29 changes: 24 additions & 5 deletions packages/core/src/actions/send.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ function resolveSendTo(
EventObject
>
| undefined;
}
},
extra: { deferredActorIds: string[] } | undefined
) {
const delaysMap = state.machine.implementations.delays;

Expand All @@ -82,7 +83,7 @@ function resolveSendTo(
}

const resolvedTarget = typeof to === 'function' ? to(args) : to;
let targetActorRef: AnyActorRef | undefined;
let targetActorRef: AnyActorRef | string | undefined;

if (typeof resolvedTarget === 'string') {
if (resolvedTarget === SpecialTargets.Parent) {
Expand All @@ -94,7 +95,9 @@ function resolveSendTo(
// #_invokeid. If the target is the special term '#_invokeid', where invokeid is the invokeid of an SCXML session that the sending session has created by <invoke>, the Processor must add the event to the external queue of that session.
targetActorRef = state.children[resolvedTarget.slice(2)];
} else {
targetActorRef = state.children[resolvedTarget];
targetActorRef = extra?.deferredActorIds.includes(resolvedTarget)
? resolvedTarget
: state.children[resolvedTarget];
}
if (!targetActorRef) {
throw new Error(
Expand All @@ -110,6 +113,22 @@ function resolveSendTo(
{ to: targetActorRef, event: resolvedEvent, id, delay: resolvedDelay }
];
}

function retryResolveSendTo(
_: AnyActorContext,
state: AnyState,
params: {
to: AnyActorRef;
event: EventObject;
id: string | undefined;
delay: number | undefined;
}
) {
if (typeof params.to === 'string') {
params.to = state.children[params.to];
}
}

function executeSendTo(
actorContext: AnyActorContext,
params: {
Expand All @@ -126,9 +145,8 @@ function executeSendTo(
return;
}

const { to, event } = params;

actorContext.defer(() => {
const { to, event } = params;
actorContext?.system._relay(
actorContext.self,
to,
Expand Down Expand Up @@ -205,6 +223,7 @@ export function sendTo<
sendTo.delay = options?.delay;

sendTo.resolve = resolveSendTo;
sendTo.retryResolve = retryResolveSendTo;
sendTo.execute = executeSendTo;

return sendTo;
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/actors/callback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
Snapshot,
HomomorphicOmit
} from '../types';
import { XSTATE_INIT, XSTATE_STOP } from '../constants.ts';
import { XSTATE_STOP } from '../constants.ts';

type CallbackSnapshot<TInput, TEvent> = Snapshot<undefined> & {
input: TInput;
Expand Down Expand Up @@ -63,10 +63,10 @@ export function fromCallback<TEvent extends EventObject, TInput = unknown>(
const logic: CallbackActorLogic<TEvent, TInput> = {
config: invokeCallback,
start: (_state, { self, system }) => {
system._relay(self, self, { type: XSTATE_INIT });
system._relay(self, self, { type: 'xstate.create' });
},
transition: (state, event, { self, system }) => {
if (event.type === XSTATE_INIT) {
if (event.type === 'xstate.create') {
const sendBack = (eventForParent: AnyEventObject) => {
if (state.status === 'stopped') {
return;
Expand Down
16 changes: 7 additions & 9 deletions packages/core/src/interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
MissingImplementationsError
} from './typegenTypes.ts';
import type {
ActorLogic,
ActorContext,
ActorSystem,
AnyActorLogic,
Expand All @@ -24,7 +23,6 @@ import type {
PersistedStateFrom,
SnapshotFrom,
AnyActorRef,
OutputFrom,
DoneActorEvent
} from './types.ts';
import {
Expand Down Expand Up @@ -500,19 +498,19 @@ export class Actor<TLogic extends AnyActorLogic>
}

// TODO: make private (and figure out a way to do this within the machine)
public delaySend({
event,
id,
delay,
to
}: {
public delaySend(params: {
event: EventObject;
id: string | undefined;
delay: number;
to?: AnyActorRef;
}): void {
const { event, id, delay } = params;
const timerId = this.clock.setTimeout(() => {
this.system._relay(this, to ?? this, event as EventFromLogic<TLogic>);
this.system._relay(
this,
params.to ?? this,
event as EventFromLogic<TLogic>
);
}, delay);

// TODO: consider the rehydration story here
Expand Down
Loading

0 comments on commit 6f18183

Please sign in to comment.