github statelyai/xstate @xstate/graph@2.0.0

latest releases: xstate@5.14.0, @xstate/store@1.0.0, xstate@5.13.2...
one month ago

Major Changes

  • #4896 7c6e2ea Thanks @davidkpiano! - Test model path generation now has the option to allow duplicate paths by setting allowDuplicatePaths: true:

    const paths = model.getSimplePaths({
      allowDuplicatePaths: true
    });
    // a
    // a -> b
    // a -> b -> c
    // a -> d
    // a -> d -> e

    By default, allowDuplicatePaths is set to false:

    const paths = model.getSimplePaths();
    // a -> b -> c
    // a -> d -> e
  • #4896 7c6e2ea Thanks @davidkpiano! - The adjacencyMapToArray(…) helper function has been introduced, which converts an adjacency map to an array of { state, event, nextState } objects.

    import { getAdjacencyMap, adjacencyMapToArray } from '@xstate/graph';
    
    const machine = createMachine({
      initial: 'green',
      states: {
        green: {
          on: {
            TIMER: 'yellow'
          }
        },
        yellow: {
          on: {
            TIMER: 'red'
          }
        },
        red: {
          on: {
            TIMER: 'green'
          }
        }
      }
    });
    
    const arr = adjacencyMapToArray(getAdjacencyMap(machine));
    // [
    //   {
    //     "state": {value: "green", ... },
    //     "event": { type: "TIMER" },
    //     "nextState": { value: "yellow", ... }
    //   },
    //   {
    //     "state": {value: "yellow", ... },
    //     "event": { type: "TIMER" },
    //     "nextState": { value: "red", ... }
    //   },
    //   {
    //     "state": {value: "red", ... },
    //     "event": { type: "TIMER" },
    //     "nextState": { value: "green", ... }
    //   },
    //   {
    //     "state": {value: "green", ... },
    //     "event": { type: "TIMER" },
    //     "nextState": { value: "yellow", ... }
    //   },
    // ]
  • #4896 7c6e2ea Thanks @davidkpiano! - The traversalLimit option has been renamed to limit:

    model.getShortestPaths({
    - traversalLimit: 100
    + limit: 100
    });
  • #4233 3d96d0f95 Thanks @davidkpiano! - Remove getMachineShortestPaths and getMachineSimplePaths

    import {
    - getMachineShortestPaths,
    + getShortestPaths,
    - getMachineSimplePaths,
    + getSimplePaths
    } from '@xstate/graph';
    
    -const paths = getMachineShortestPaths(machine);
    +const paths = getShortestPaths(machine);
    
    -const paths = getMachineSimplePaths(machine);
    +const paths = getSimplePaths(machine);
  • #4238 b4f12a517 Thanks @davidkpiano! - The steps in the paths returned from functions like getShortestPaths(...) and getSimplePaths(...) have the following changes:

    • The step.event property now represents the event object that resulted in the transition to the step.state, not the event that comes before the next step.
    • The path.steps array now includes the target path.state as the last step.
      • Note: this means that path.steps always has at least one step.
    • The first step now has the { type: 'xstate.init' } event
  • #4896 7c6e2ea Thanks @davidkpiano! - The createTestMachine(…) function has been removed. Use a normal createMachine(…) or setup(…).createMachine(…) function instead to create machines for path generation.

  • #4896 7c6e2ea Thanks @davidkpiano! - The filter and stopCondition option for path generation has been renamed to stopWhen, which is used to stop path generation when a condition is met. This is a breaking change, but it is a more accurate name for the option.

    const shortestPaths = getShortestPaths(machine, {
      events: [{ type: 'INC' }],
    - filter: (state) => state.context.count < 5
    - stopCondition: (state) => state.context.count < 5
    + stopWhen: (state) => state.context.count === 5
    });
  • #4896 7c6e2ea Thanks @davidkpiano! - Path generation now supports input for actor logic:

    const model = createTestModel(
      setup({
        types: {
          input: {} as {
            name: string;
          },
          context: {} as {
            name: string;
          }
        }
      }).createMachine({
        context: (x) => ({
          name: x.input.name
        }),
        initial: 'checking',
        states: {
          checking: {
            always: [
              { guard: (x) => x.context.name.length > 3, target: 'longName' },
              { target: 'shortName' }
            ]
          },
          longName: {},
          shortName: {}
        }
      })
    );
    
    const path1 = model.getShortestPaths({
      input: { name: 'ed' }
    });
    
    expect(path1[0].steps.map((s) => s.state.value)).toEqual(['shortName']);
    
    const path2 = model.getShortestPaths({
      input: { name: 'edward' }
    });
    
    expect(path2[0].steps.map((s) => s.state.value)).toEqual(['longName']);
  • #4896 7c6e2ea Thanks @davidkpiano! - The test model "sync" methods have been removed, including:

    • testModel.testPathSync(…)
    • testModel.testStateSync(…)
    • testPath.testSync(…)

    The async methods should always be used instead.

    model.getShortestPaths().forEach(async (path) => {
    - model.testPathSync(path, {
    + await model.testPath(path, {
        states: { /* ... */ },
        events: { /* ... */ },
      });
    })

Minor Changes

  • #3727 5fb3c683d Thanks @Andarist! - exports field has been added to the package.json manifest. It limits what files can be imported from a package - it's no longer possible to import from files that are not considered to be a part of the public API.

Patch Changes

  • #4896 7c6e2ea Thanks @davidkpiano! - The @xstate/graph package now includes everything from @xstate/test.

  • #4308 af032db12 Thanks @davidkpiano! - Traversing state machines that have delayed transitions will now work as expected:

    const machine = createMachine({
      initial: 'a',
      states: {
        a: {
          after: {
            1000: 'b'
          }
        },
        b: {}
      }
    });
    
    const paths = getShortestPaths(machine); // works

Don't miss a new xstate release

NewReleases is sending notifications on new releases.