statelyai / xstate

Actor-based state management & orchestration for complex app logic.
https://stately.ai/docs
MIT License
26.85k stars 1.23k forks source link

All resolver kinds and implementation types should receive all possible event types outside of transitions #4720

Open Andarist opened 7 months ago

Andarist commented 7 months ago

It's only correct to assume that everything can receive just any event type within setup and entry/exit actions, invoke inputs, etc.

Events that are missing are done actor events, done state events, error actor events, xstate.init, xstate.after and probably more

rhathas commented 7 months ago

With Typegen in v4, we had no problems with events in actions. But in v5, we need to narrow event types to events manually (The corrent event types according to the places it has been used were provided by typegen). Well, it's managable.

But missing done/error events is a big problem at the moment. I have no idea how to provide the right event type to an action. Since the variables from done events of invoked services are in 'event.output' instead of 'event' (like it's in transition events), I cant't solve it by using a transition event with the same payload to fake it.

rmgryan commented 7 months ago

I'm curious why there's not a flattened Event object versus different types of events?

For example, why not something like:

type DefaultEvent {
  id: string; // I think what is currently being sent as event.type is actually an identifier
  type: 'default';
  data?: Record<string, any>; // data passed to event from success could be standardized under a key like data
  error?: Record<string, any>; // data passed to event from error could be standardized under a key like error
}

type ErrorEvent {
  ...
  type: 'error';
  ...
}

type Event = DefaultEvent | ErrorEvent; // etc., discriminated union

It seems to me that the type being sent for events that result from onDone, onError, etc. are actually identifiers (i.e. xstate.done.after.index.machine.state) rather than pure types (i.e. "my-event"). A flattened event object would allow me to do something like:

type MyDoneEvent = DoneEvent & { id: 'my-done-event' };

And then from things like actions I could make sure my code is running against the proper event like:

if (event.type !== DoneEvent.type || event.id !== MyDoneEvent.id) {
  return context;
}
rhathas commented 6 months ago

@rmgryan I agree that event type on onDone/onError events is more like an identifier. Even a simple type: 'done-event' which is same across all onDone events allows me to do something like:

const results = event.type === 'done-event' ? event.output.data : event.data

To be honest a simple workaround would be great until it's solved throughly (i guess that we will wait typegen to work with v5?).