Skip to content

Update dependency xstate to v5 #568

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

renovate[bot]
Copy link
Contributor

@renovate renovate bot commented Feb 18, 2025

This PR contains the following updates:

Package Change Age Adoption Passing Confidence
xstate (source) ^4.38.3 -> ^5.19.2 age adoption passing confidence

Release Notes

statelyai/xstate (xstate)

v5.19.2

Compare Source

Patch Changes

v5.19.1

Compare Source

Patch Changes
  • #​5139 bf6119a7310a878afbf4f5b01f5e24288f9a0f16 Thanks @​SandroMaglione! - Make spawn input required when defined inside referenced actor:

    const childMachine = createMachine({
      types: { input: {} as { value: number } }
    });
    
    const machine = createMachine({
      types: {} as { context: { ref: ActorRefFrom<typeof childMachine> } },
      context: ({ spawn }) => ({
        ref: spawn(
          childMachine,
          // Input is now required!
          { input: { value: 42 } }
        )
      })
    });

v5.19.0

Compare Source

Minor Changes
  • #​4954 8c4b70652acaef2702f32435362e4755679a516d Thanks @​davidkpiano! - Added a new transition function that takes an actor logic, a snapshot, and an event, and returns a tuple containing the next snapshot and the actions to execute. This function is a pure function and does not execute the actions itself. It can be used like this:

    import { transition } from 'xstate';
    
    const [nextState, actions] = transition(actorLogic, currentState, event);
    // Execute actions as needed

    Added a new initialTransition function that takes an actor logic and an optional input, and returns a tuple containing the initial snapshot and the actions to execute from the initial transition. This function is also a pure function and does not execute the actions itself. It can be used like this:

    import { initialTransition } from 'xstate';
    
    const [initialState, actions] = initialTransition(actorLogic, input);
    // Execute actions as needed

    These new functions provide a way to separate the calculation of the next snapshot and actions from the execution of those actions, allowing for more control and flexibility in the transition process.

v5.18.2

Compare Source

Patch Changes

v5.18.1

Compare Source

Patch Changes

v5.18.0

Compare Source

Minor Changes
  • #​5042 54c9d9e6a4 Thanks @​boneskull! - waitFor() now accepts a {signal: AbortSignal} in WaitForOptions

  • #​5006 1ab974547f Thanks @​davidkpiano! - The state value typings for setup state machine actors (setup({}).createMachine({ ... })) have been improved to represent the actual expected state values.

    const machine = setup({}).createMachine({
      initial: 'green',
      states: {
        green: {},
        yellow: {},
        red: {
          initial: 'walk',
          states: {
            walk: {},
            wait: {},
            stop: {}
          }
        },
        emergency: {
          type: 'parallel',
          states: {
            main: {
              initial: 'blinking',
              states: {
                blinking: {}
              }
            },
            cross: {
              initial: 'blinking',
              states: {
                blinking: {}
              }
            }
          }
        }
      }
    });
    
    const actor = createActor(machine).start();
    
    const stateValue = actor.getSnapshot().value;
    
    if (stateValue === 'green') {
      // ...
    } else if (stateValue === 'yellow') {
      // ...
    } else if ('red' in stateValue) {
      stateValue;
      // {
      //   red: "walk" | "wait" | "stop";
      // }
    } else {
      stateValue;
      // {
      //   emergency: {
      //     main: "blinking";
      //     cross: "blinking";
      //   };
      // }
    }
Patch Changes
  • #​5054 853f6daa0b Thanks @​davidkpiano! - The CallbackLogicFunction type (previously InvokeCallback) is now exported. This is the callback function that you pass into fromCallback(callbackLogicFn) to create an actor from a callback function.

    import { type CallbackLogicFunction } from 'xstate';
    
    // ...

v5.17.4

Compare Source

Patch Changes
  • #​5039 d6df8fb470 Thanks @​Andarist! - Fixed an inference issue that prevented emit used directly in setup (or bare createMachine) to benefit from types.emitted types.

v5.17.3

Compare Source

Patch Changes

v5.17.2

Compare Source

Patch Changes

v5.17.1

Compare Source

Patch Changes
  • #​5009 51d4c4fc5 Thanks @​davidkpiano! - The internal types for StateMachine<...> have been improved so that all type params are required, to prevent errors when using the types. This fixes weird issues like #​5008.

v5.17.0

Compare Source

Minor Changes
  • #​4979 a0e9ebcef Thanks @​davidkpiano! - State IDs are now strongly typed as keys of snapshot.getMeta() for state machine actor snapshots.

    const machine = setup({
      // ...
    }).createMachine({
      id: 'root',
      initial: 'parentState',
      states: {
        parentState: {
          meta: {},
          initial: 'childState',
          states: {
            childState: {
              meta: {}
            },
            stateWithId: {
              id: 'state with id',
              meta: {}
            }
          }
        }
      }
    });
    
    const actor = createActor(machine);
    
    const metaValues = actor.getSnapshot().getMeta();
    
    // Auto-completed keys:
    metaValues.root;
    metaValues['root.parentState'];
    metaValues['root.parentState.childState'];
    metaValues['state with id'];
    
    // @&#8203;ts-expect-error
    metaValues['root.parentState.stateWithId'];
    
    // @&#8203;ts-expect-error
    metaValues['unknown state'];
Patch Changes

v5.16.0

Compare Source

Minor Changes
  • #​4996 5be796cd2 Thanks @​ronvoluted! - The actor snapshot status type ('active' | 'done' | 'error' | 'stopped') is now exposed as SnapshotStatus

  • #​4981 c4ae156b2 Thanks @​davidkpiano! - Added sendParent to the enqueueActions feature. This allows users to enqueue actions that send events to the parent actor within the enqueueActions block.

    import { createMachine, enqueueActions } from 'xstate';
    
    const childMachine = createMachine({
      entry: enqueueActions(({ enqueue }) => {
        enqueue.sendParent({ type: 'CHILD_READY' });
      })
    });

v5.15.0

Compare Source

Minor Changes
  • #​4976 452bce71e Thanks @​with-heart! - Added exports for actor logic-specific ActorRef types: CallbackActorRef, ObservableActorRef, PromiseActorRef, and TransitionActorRef.

    Each type represents ActorRef narrowed to the corresponding type of logic (the type of self within the actor's logic):

    • CallbackActorRef: actor created by fromCallback

      import { fromCallback, createActor } from 'xstate';
      
      /** The events the actor receives. */
      type Event = { type: 'someEvent' };
      /** The actor's input. */
      type Input = { name: string };
      
      /** Actor logic that logs whenever it receives an event of type `someEvent`. */
      const logic = fromCallback<Event, Input>(({ self, input, receive }) => {
        self;
        // ^? CallbackActorRef<Event, Input>
      
        receive((event) => {
          if (event.type === 'someEvent') {
            console.log(`${input.name}: received "someEvent" event`);
            // logs 'myActor: received "someEvent" event'
          }
        });
      });
      
      const actor = createActor(logic, { input: { name: 'myActor' } });
      //    ^? CallbackActorRef<Event, Input>
    • ObservableActorRef: actor created by fromObservable and fromEventObservable

      import { fromObservable, createActor } from 'xstate';
      import { interval } from 'rxjs';
      
      /** The type of the value observed by the actor's logic. */
      type Context = number;
      /** The actor's input. */
      type Input = { period?: number };
      
      /**
       * Actor logic that observes a number incremented every `input.period`
       * milliseconds (default: 1_000).
       */
      const logic = fromObservable<Context, Input>(({ input, self }) => {
        self;
        // ^? ObservableActorRef<Event, Input>
      
        return interval(input.period ?? 1_000);
      });
      
      const actor = createActor(logic, { input: { period: 2_000 } });
      //    ^? ObservableActorRef<Event, Input>
    • PromiseActorRef: actor created by fromPromise

      import { fromPromise, createActor } from 'xstate';
      
      /** The actor's resolved output. */
      type Output = string;
      /** The actor's input. */
      type Input = { message: string };
      
      /** Actor logic that fetches the url of an image of a cat saying `input.message`. */
      const logic = fromPromise<Output, Input>(async ({ input, self }) => {
        self;
        // ^? PromiseActorRef<Output, Input>
      
        const data = await fetch(`https://cataas.com/cat/says/${input.message}`);
        const url = await data.json();
        return url;
      });
      
      const actor = createActor(logic, { input: { message: 'hello world' } });
      //    ^? PromiseActorRef<Output, Input>
    • TransitionActorRef: actor created by fromTransition

      import { fromTransition, createActor, type AnyActorSystem } from 'xstate';
      
      /** The actor's stored context. */
      type Context = {
        /** The current count. */
        count: number;
        /** The amount to increase `count` by. */
        step: number;
      };
      /** The events the actor receives. */
      type Event = { type: 'increment' };
      /** The actor's input. */
      type Input = { step?: number };
      
      /**
       * Actor logic that increments `count` by `step` when it receives an event of
       * type `increment`.
       */
      const logic = fromTransition<Context, Event, AnyActorSystem, Input>(
        (state, event, actorScope) => {
          actorScope.self;
          //         ^? TransitionActorRef<Context, Event>
      
          if (event.type === 'increment') {
            return {
              ...state,
              count: state.count + state.step
            };
          }
          return state;
        },
        ({ input, self }) => {
          self;
          // ^? TransitionActorRef<Context, Event>
      
          return {
            count: 0,
            step: input.step ?? 1
          };
        }
      );
      
      const actor = createActor(logic, { input: { step: 10 } });
      //    ^? TransitionActorRef<Context, Event>
  • #​4949 8aa4c2b90 Thanks @​davidkpiano! - The TypeGen-related types have been removed from XState, simplifying the internal types without affecting normal XState usage.

v5.14.0

Compare Source

Minor Changes
  • #​4936 c58b36dc3 Thanks @​davidkpiano! - Inspecting an actor system via actor.system.inspect(ev => …) now accepts a function or observer, and returns a subscription:

    const actor = createActor(someMachine);
    
    const sub = actor.system.inspect((inspectionEvent) => {
      console.log(inspectionEvent);
    });
    
    // Inspection events will be logged
    actor.start();
    actor.send({ type: 'anEvent' });
    
    // ...
    
    sub.unsubscribe();
    
    // Will no longer log inspection events
    actor.send({ type: 'someEvent' });
  • #​4942 9caaa1f70 Thanks @​boneskull! - DoneActorEvent and ErrorActorEvent now contain property actorId, which refers to the ID of the actor the event refers to.

  • #​4935 2ac08b700 Thanks @​davidkpiano! - All actor logic creators now support emitting events:

    Promise actors

    const logic = fromPromise(async ({ emit }) => {
      // ...
      emit({
        type: 'emitted',
        msg: 'hello'
      });
      // ...
    });

    Transition actors

    const logic = fromTransition((state, event, { emit }) => {
      // ...
      emit({
        type: 'emitted',
        msg: 'hello'
      });
      // ...
      return state;
    }, {});

    Observable actors

    const logic = fromObservable(({ emit }) => {
      // ...
    
      emit({
        type: 'emitted',
        msg: 'hello'
      });
    
      // ...
    });

    Callback actors

    const logic = fromCallback(({ emit }) => {
      // ...
      emit({
        type: 'emitted',
        msg: 'hello'
      });
      // ...
    });
Patch Changes

v5.13.2

Compare Source

Patch Changes

v5.13.1

Compare Source

Patch Changes
  • #​4905 dbeafeb25 Thanks @​davidkpiano! - You can now use a wildcard to listen for any emitted event from an actor:

    actor.on('*', (emitted) => {
      console.log(emitted); // Any emitted event
    });

v5.13.0

Compare Source

Minor Changes
  • #​4832 148d8fcef Thanks @​cevr! - fromPromise now passes a signal into its creator function.

    const logic = fromPromise(({ signal }) =>
      fetch('https://api.example.com', { signal })
    );

    This will be called whenever the state transitions before the promise is resolved. This is useful for cancelling the promise if the state changes.

Patch Changes
  • #​4876 3f6a73b56 Thanks @​davidkpiano! - XState will now warn when calling built-in actions like assign, sendTo, raise, emit, etc. directly inside of a custom action. See https://stately.ai/docs/actions#built-in-actions for more details.

    const machine = createMachine({
      entry: () => {
        // Will warn:
        // "Custom actions should not call \`assign()\` directly, as it is not imperative. See https://stately.ai/docs/actions#built-in-actions for more details."
        assign({
          // ...
        });
      }
    });

v5.12.0

Compare Source

Minor Changes
  • #​4863 0696adc21 Thanks @​davidkpiano! - Meta objects for state nodes and transitions can now be specified in setup({ types: … }):

    const machine = setup({
      types: {
        meta: {} as {
          layout: string;
        }
      }
    }).createMachine({
      initial: 'home',
      states: {
        home: {
          meta: {
            layout: 'full'
          }
        }
      }
    });
    
    const actor = createActor(machine).start();
    
    actor.getSnapshot().getMeta().home;
    // => { layout: 'full' }
    // if in "home" state

v5.11.0

Compare Source

Minor Changes
  • #​4806 f4e0ec48c Thanks @​davidkpiano! - Inline actor logic is now permitted when named actors are present. Defining inline actors will no longer cause a TypeScript error:

    const machine = setup({
      actors: {
        existingActor: fromPromise(async () => {
          // ...
        })
      }
    }).createMachine({
      invoke: {
        src: fromPromise(async () => {
          // Inline actor
        })
        // ...
      }
    });

v5.10.0

Compare Source

Minor Changes
  • #​4822 f7f1fbbf3 Thanks @​davidkpiano! - The clock and logger specified in the options object of createActor(logic, options) will now propagate to all actors created within the same actor system.

    import { setup, log, createActor } from 'xstate';
    
    const childMachine = setup({
      // ...
    }).createMachine({
      // ...
      // Uses custom logger from root actor
      entry: log('something')
    });
    
    const parentMachine = setup({
      // ...
    }).createMachine({
      // ...
      invoke: {
        src: childMachine
      }
    });
    
    const actor = createActor(parentMachine, {
      logger: (...args) => {
        // custom logger for args
      }
    });
    
    actor.start();

v5.9.1

Compare Source

Patch Changes

v5.9.0

Compare Source

Minor Changes
  • #​4746 b570ba20d Thanks @​davidkpiano! - The new emit(…) action creator emits events that can be received by listeners. Actors are now event emitters.

    import { emit } from 'xstate';
    
    const machine = createMachine({
      // ...
      on: {
        something: {
          actions: emit({
            type: 'emitted',
            some: 'data'
          })
        }
      }
      // ...
    });
    
    const actor = createActor(machine).start();
    
    actor.on('emitted', (event) => {
      console.log(event);
    });
    
    actor.send({ type: 'something' });
    // logs:
    // {
    //   type: 'emitted',
    //   some: 'data'
    // }
  • #​4777 4abeed9df Thanks @​Andarist! - Added support for params to enqueueActions

v5.8.2

Compare Source

Patch Changes
  • #​4772 9a0120901 Thanks @​Andarist! - Fixed a type issue that prevent sendParent to be accepted by setup when delays stayed not configured.

v5.8.1

Compare Source

Patch Changes

v5.8.0

Compare Source

Minor Changes

v5.7.1

Compare Source

Patch Changes
  • #​4739 15b7dd1f0 Thanks @​devanfarrell! - Removed this from machine snapshot methods to fix issues with accessing those methods from union of actors and their snapshots.

v5.7.0

Compare Source

Minor Changes
  • #​4290 7a8796f80 Thanks @​davidkpiano! - An error will now be thrown if an incompatible state value is passed to machine.resolveState({ value }).

  • #​4693 11b6a1ae1 Thanks @​davidkpiano! - You can now inspect microsteps (@xstate.microstep) and actions (@xstate.action):

    const machine = createMachine({
      initial: 'a',
      states: {
        a: {
          on: {
            event: 'b'
          }
        },
        b: {
          entry: 'someAction',
          always: 'c'
        },
        c: {}
      }
    });
    
    const actor = createActor(machine, {
      inspect: (inspEvent) => {
        if (inspEvent.type === '@&#8203;xstate.microstep') {
          console.log(inspEvent.snapshot);
          // logs:
          // { value: 'a', … }
          // { value: 'b', … }
          // { value: 'c', … }
    
          console.log(inspEvent.event);
          // logs:
          // { type: 'event', … }
        } else if (inspEvent.type === '@&#8203;xstate.action') {
          console.log(inspEvent.action);
          // logs:
          // { type: 'someAction', … }
        }
      }
    });
    
    actor.start();
    
    actor.send({ type: 'event' });

v5.6.2

Compare Source

Patch Changes
  • #​4731 960cdcbcb Thanks @​davidkpiano! - You can now import getInitialSnapshot(…) from xstate directly, which is useful for getting a mock of the initial snapshot when interacting with machines (or other actor logic) without createActor(…):

    import { getInitialSnapshot } from 'xstate';
    import { someMachine } from './someMachine';
    
    // Returns the initial snapshot (state) of the machine
    const initialSnapshot = getInitialSnapshot(
      someMachine,
      { name: 'Mateusz' } // optional input
    );

v5.6.1

Compare Source

Patch Changes

v5.6.0

Compare Source

Minor Changes
  • #​4704 78699aef6 Thanks @​Andarist! - createActor will now error if the required input is not given to it.

  • #​4688 14902e17a Thanks @​Andarist! - The schemas property in setup(...) is now passed through to the resulting machine. This property is meant to be used with future developer tooling, and is typed as unknown for now.

Patch Changes

v5.5.2

Compare Source

Patch Changes
  • #​4685 e43eab144 Thanks @​davidkpiano! - State IDs that have periods in them are now supported if those periods are escaped.

    The motivation is that external tools, such as Stately Studio, may allow users to enter any text into the state ID field. This change allows those tools to escape periods in state IDs, so that they don't conflict with the internal path-based state IDs.

    E.g. if a state ID of "Loading..." is entered into the state ID field, instead of crashing either the external tool and/or the XState state machine, it should be converted by the tool to "Loading\\.\\.\\.", and those periods will be ignored by XState.

v5.5.1

Compare Source

Patch Changes

v5.5.0

Compare Source

Minor Changes
  • #​4596 6113a590a Thanks @​davidkpiano! - Introduce getNextSnapshot(...), which determines the next snapshot for the given actorLogic based on the given snapshot and event.

    If the snapshot is undefined, the initial snapshot of the actorLogic is used.

    import { getNextSnapshot } from 'xstate';
    import { trafficLightMachine } from './trafficLightMachine.ts';
    
    const nextSnapshot = getNextSnapshot(
      trafficLightMachine, // actor logic
      undefined, // snapshot (or initial state if undefined)
      { type: 'TIMER' }
    ); // event object
    
    console.log(nextSnapshot.value);
    // => 'yellow'
    
    const nextSnapshot2 = getNextSnapshot(
      trafficLightMachine, // actor logic
      nextSnapshot, // snapshot
      { type: 'TIMER' }
    ); // event object
    
    console.log(nextSnapshot2.value);
    // =>'red'
Patch Changes

v5.4.1

Compare Source

Patch Changes

v5.4.0

Compare Source

Minor Changes
  • #​4616 e8c0b15b2 Thanks @​Andarist! - context factories receive self now so you can immediately pass that as part of the input to spawned actors.

    setup({
      /* ... */
    }).createMachine({
      context: ({ spawn, self }) => {
        return {
          childRef: spawn('child', { input: { parent: self } })
        };
      }
    });

v5.3.1

Compare Source

Patch Changes
  • #​4597 ae0b05f11 Thanks @​davidkpiano! - Update the argument object of enqueueActions(...) to include the self and system properties:

    // ...
    entry: enqueueActions(({ self, system }) => {
      // ...
    });

v5.3.0

Compare Source

Minor Changes
  • #​4547 8e8d2ba38 Thanks @​davidkpiano! - Add assertEvent(...) to help provide strong typings for events that can't be easily inferred, such as events in entry and exit actions, or in invoke.input.

    The assertEvent(event, 'someType') function will throw if the event is not the expected type. This ensures that the event is guaranteed to have that type, and assumes that the event object has the expected payload (naturally enforced by TypeScript).

    // ...
    entry: ({ event }) => {
      assertEvent(event, 'greet');
      // event is { type: 'greet'; message: string }
    
      assertEvent(event, ['greet', 'notify']);
      // event is { type: 'greet'; message: string }
      // or { type: 'notify'; message: string; level: 'info' | 'error' }
    },
    exit: ({ event }) => {
      assertEvent(event, 'doNothing');
      // event is { type: 'doNothing' }
    }
Patch Changes
  • #​4586 97f1cbd5f Thanks @​Andarist! - Fixed an issue with ancestors of the default history target that lie outside of the transition domain being incorrectly entered.

v5.2.1

Compare Source

Patch Changes

v5.2.0

Compare Source

Minor Changes
  • #​4198 ca58904ad Thanks @​davidkpiano! - Introduce toPromise(actor), which creates a promise from an actor that resolves with the actor snapshot's output when done, or rejects with the actor snapshot's error when it fails.

    import { createMachine, createActor, toPromise } from 'xstate';
    
    const machine = createMachine({
      // ...
      states: {
        // ...
        done: { type: 'final', output: 42 }
      }
    });
    
    const actor = createActor(machine);
    
    actor.start();
    
    const output = await toPromise(actor);
    
    console.log(output);
    // => 42
Patch Changes
  • #​4568 a5c55fae2 Thanks @​Andarist! - Fixed an issue with spawn within assign not returning a narrowed down ActorRef type on TypeScrip 5.0

  • #​4570 c11127336 Thanks @​Andarist! - Fixed an issue that caused a complete listener to be called instead of the error one when the actor was subscribed after being stopped.

v5.1.0

Compare Source

Minor Changes
Patch Changes

v5.0.2

Compare Source

Patch Changes

v5.0.1

Compare Source

Patch Changes

v5.0.0

Compare Source

Patch Changes

Configuration

📅 Schedule: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate renovate bot force-pushed the renovate/major-xstate-monorepo branch 12 times, most recently from 0fdf398 to f3162c5 Compare February 19, 2025 05:41
@renovate renovate bot force-pushed the renovate/major-xstate-monorepo branch 3 times, most recently from 5ec2efd to 226680a Compare March 19, 2025 06:06
@miyamiyaz
Copy link
Contributor

We need a tough migration, as referred to here:
https://stately.ai/docs/migration

@renovate renovate bot force-pushed the renovate/major-xstate-monorepo branch 9 times, most recently from da5b0b8 to 4117a99 Compare March 21, 2025 07:58
@renovate renovate bot force-pushed the renovate/major-xstate-monorepo branch 2 times, most recently from 613bcf8 to b6cabed Compare April 2, 2025 07:54
@renovate renovate bot changed the title fix(deps): update dependency xstate to v5 Update dependency xstate to v5 Apr 2, 2025
@renovate renovate bot force-pushed the renovate/major-xstate-monorepo branch 2 times, most recently from 7759ceb to 3308873 Compare April 3, 2025 03:07
@renovate renovate bot force-pushed the renovate/major-xstate-monorepo branch 4 times, most recently from 89bac16 to 2f90a9e Compare April 9, 2025 06:45
@renovate renovate bot force-pushed the renovate/major-xstate-monorepo branch 8 times, most recently from 9d4ed18 to b2818e8 Compare April 17, 2025 05:52
@renovate renovate bot force-pushed the renovate/major-xstate-monorepo branch 2 times, most recently from 2c8b7bc to ef54a55 Compare April 21, 2025 08:18
@renovate renovate bot force-pushed the renovate/major-xstate-monorepo branch from ef54a55 to e3ddba4 Compare April 21, 2025 09:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

1 participant