New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

xstate-async-guards

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

xstate-async-guards

XState helper for using asynchronous guards

  • 1.1.0
  • latest
  • Source
  • npm
  • Socket score

Version published
Maintainers
1
Created
Source

xstate-async-guards

This is a helper for using asynchronous guards in XState state machines with ease.

npm version code style: prettier PRs Welcome

Rationale

Out of the box, XState supports only synchronous, pure condition functions (a.k.a. guards) in guarded transitions. This makes sense since synchronous transitions undertaken right after an event is received by a state machine are easy to conceptualize.

What if a guard needs external data from some asynchronous API (fetch, IndexedDB, etc.)? What if the guard execution is CPU intensive and you wish to offload it to a Web Worker? XState wants you to perform all async jobs in invoked services (or in spawned actors) which boils down to transitioning to some helper states first. That generates some boilerplate and may obscur your intentions. The problem is exacerbated when there are multiple async guard cases which need to be evaluated sequentially in a waterfall fashion.

The xstate-async-guards library provides a withAsyncGuards helper to abstract asynchronously guarded transitions like so:

// within a state machine declaration
states: {

  Idle: withAsyncGuards({
    on: {
      EVENT: {
        cond: async (_, event) => event.value === 'start', // async function!
        target: '#root.Started'
      }
    }
  }),

  Started: {}
}

Installation

$ npm install xstate-async-guards --save

Example

Simply wrap the state node with asynchronous guard functions in a withAsyncGuards call:

import { withAsyncGuards } from 'xstate-async-guards'

const machine = createMachine({
  id: 'root',
  context: {}, // must not be undefined
  initial: 'Idle',
  states: {
    // this state uses async guards
    Idle: withAsyncGuards({
      on: {
        EVENT: [
          {
            cond: isValueStart, // async function reference
            target: '#root.Started', // must use absolute targets
          },
          {
            cond: 'isValueFaulty', // string reference to an async function in configured guards
            target: '#root.Broken',
            actions: send('BROKEN'), // actions are supported
          },
          {
            target: '#root.Off', // default transition is optional
          },
        ],

        // rejected guard promises can be handled explicitly
        'error.async-guard.isValueStart': {
          actions: (_, event) => console.log('Async guard error!', event),
        },
      },

      id: 'idle', // state ID is mandatory

      // all standard state props are supported. E.g.:
      entry: () => console.log('entered Idle'),
      exit: () => console.log('exited Idle'),
      invoke: {
        /*...*/
      },
      // etc.
    }),

    Started: {},
    Broken: {},
    Off: {},
  },
})

Options

Function withAsyncGuards accepts an object as the second argument which may contain the following options:

  • inGuardEvaluation - an object with leading and trailing boolean props.
    • leading (boolean) - When true (default), in guards will be evaluated first, before async guards are evaluated. An async guard will be evaluated only if the in guard is satisfied.
    • trailing (boolean) - When true, in guards will be evaluated after async guards have been successfully resolved. If an in guard is not satisfied at this moment, the transition will not be taken (even though the async guard is satisfied). Defaults to false.

Caution

A thorough consideration should be given to consequences of using asynchronous guards for conditional transitions. Importantly, by the time an async guard resolves, the world may be in a state different from where it was at the time the event triggering the transition was received. Special care should be taken if your state machine uses parallel states or globally defined transitions.

TODO

  • Support combining sync and async guards within the same state node
  • Relax the requirement for absolute targets
  • Document error handling

Keywords

FAQs

Package last updated on 21 Feb 2022

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc