Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
@xstate/fsm
Advanced tools
@xstate/fsm is a lightweight finite state machine library for JavaScript and TypeScript. It allows you to model the behavior of your application in a predictable way by defining states and transitions. This can help manage complex state logic in a more maintainable and understandable manner.
Define a Finite State Machine
This feature allows you to define a finite state machine with states and transitions. In this example, a simple toggle machine is created with two states: 'inactive' and 'active'. The machine transitions between these states on the 'TOGGLE' event.
const { createMachine } = require('@xstate/fsm');
const toggleMachine = createMachine({
id: 'toggle',
initial: 'inactive',
states: {
inactive: { on: { TOGGLE: 'active' } },
active: { on: { TOGGLE: 'inactive' } }
}
});
console.log(toggleMachine.initialState); // { value: 'inactive' }
Transition Between States
This feature allows you to transition between states using the 'send' method. The example demonstrates how to start the machine and transition from 'inactive' to 'active' state by sending the 'TOGGLE' event.
const { createMachine, interpret } = require('@xstate/fsm');
const toggleMachine = createMachine({
id: 'toggle',
initial: 'inactive',
states: {
inactive: { on: { TOGGLE: 'active' } },
active: { on: { TOGGLE: 'inactive' } }
}
});
const toggleService = interpret(toggleMachine).start();
console.log(toggleService.state.value); // 'inactive'
toggleService.send('TOGGLE');
console.log(toggleService.state.value); // 'active'
State Actions
This feature allows you to define actions that should be executed when entering or exiting a state. The example shows how to log messages when entering and exiting the 'active' state.
const { createMachine, interpret } = require('@xstate/fsm');
const toggleMachine = createMachine({
id: 'toggle',
initial: 'inactive',
states: {
inactive: { on: { TOGGLE: 'active' } },
active: {
on: { TOGGLE: 'inactive' },
entry: () => console.log('Entering active state'),
exit: () => console.log('Exiting active state')
}
}
});
const toggleService = interpret(toggleMachine).start();
toggleService.send('TOGGLE'); // Logs: 'Entering active state'
toggleService.send('TOGGLE'); // Logs: 'Exiting active state'
robot3 is a finite state machine library for JavaScript that provides a simple API for defining states and transitions. It is similar to @xstate/fsm in that it allows you to model state logic in a predictable way, but it offers a more minimalistic approach.
javascript-state-machine is a library for creating finite state machines in JavaScript. It provides a straightforward API for defining states and transitions, similar to @xstate/fsm. However, it is more focused on simplicity and ease of use, making it a good choice for smaller projects.
redux-saga is a library that aims to make application side effects (e.g., asynchronous actions) easier to manage, more efficient to execute, and better at handling failures. While it is not a finite state machine library per se, it can be used to manage complex state logic in a Redux application, similar to how @xstate/fsm can be used.
XState for Finite State Machines
This package contains a minimal, 1kb implementation of XState for finite state machines.
Features:
state.changed
If you want to use statechart features such as nested states, parallel states, history states, activities, invoked services, delayed transitions, transient transitions, etc. please use XState
.
Installation
npm i @xstate/fsm
Usage (machine):
import { createMachine } from '@xstate/fsm';
// Stateless finite state machine definition
// machine.transition(...) is a pure function.
const toggleMachine = createMachine({
id: 'toggle',
initial: 'inactive',
states: {
inactive: { on: { TOGGLE: 'active' } },
active: { on: { TOGGLE: 'inactive' } }
}
});
const { initialState } = toggleMachine;
const toggledState = toggleMachine.transition(initialState, 'TOGGLE');
toggledState.value;
// => 'active'
const untoggledState = toggleMachine.transition(toggledState, 'TOGGLE');
untoggledState.value;
// => 'inactive'
Usage (service):
import { createMachine, interpret } from '@xstate/fsm';
const toggleMachine = createMachine({
/* ... */
});
const toggleService = interpret(toggleMachine).start();
toggleService.subscribe(state => {
console.log(state.value);
});
toggleService.send('TOGGLE');
// => logs 'active'
toggleService.send('TOGGLE');
// => logs 'inactive'
toggleService.stop();
createMachine(config, options)
Creates a new finite state machine from the config.
Argument | Type | Description |
---|---|---|
config | object (see below) | The config object for creating the machine. |
options | object (see below) | The optional options object. |
Returns:
A Machine
, which provides:
machine.initialState
: the machine's resolved initial statemachine.transition(state, event)
: a pure transition function that returns the next state given the current state
and event
The machine config has this schema:
id
(string) - an identifier for the type of machine this is. Useful for debugging.initial
(string) - the key of the initial state.states
(object) - an object mapping state names (keys) to stateson
(object) - an object mapping event types (keys) to transitionsString syntax:
{ target: stateName }
Object syntax:
target?
(string) - the state name to transition to.actions?
(Action | Action[]) - the action(s) to execute when this transition is taken.cond?
(Guard) - the condition (predicate function) to test. If it returns true
, the transition will be taken.actions?
(object) - a lookup object for your string actions.Function syntax:
{ type: actionFn.name, exec: actionFn }
and the function takes the following arguments:
context
(any) - the machine's current context
.event
(object) - the event that caused the action to be executed.Object syntax:
type
(string) - the action type.exec?
(function) - the action function to execute.String syntax:
{ type: actionType, exec: undefined }
. It can resolve to resolved function or resolved object action if the action can be looked up in the options.actions
object.Using the string or object syntax is useful for handling actions in a custom way, rather than baking in the implementation details to your machine:
const nextState = machine.transition(/* ... */);
nextState.actions.forEach(action => {
if (action.type === 'focus') {
// do focus
}
});
machine.initialState
The resolved initial state of the machine
.
machine.transition(state, event)
A pure transition function that returns the next state given the current state
and event
.
The state can be a string
state name, or a State
object (the return type of machine.transition(...)
).
Argument | Type | Description |
---|---|---|
state | string or State object | The current state to transition from |
event | string or { type: string, ... } | The event that transitions the current state to the next state |
Returns:
A State
object, which represents the next state.
Example:
// string state name and
const yellowState = machine.transition('green', 'TIMER');
// => { value: 'yellow', ... }
const redState = machine.transition(yellowState, 'TIMER');
// => { value: 'red', ... }
const greenState = machine.transition(yellowState, { type: 'TIMER' });
// => { value: 'green', ... }
An object that represents the state of a machine with the following schema:
value
(string) - the finite state valuecontext
(object) - the extended state (context)actions
(array) - an array of action objects representing the side-effects (actions) to be executedchanged
(boolean) - whether this state is changed from the previous state (true
if the state.value
and state.context
are the same, and there are no side-effects)matches(value)
(boolean) - whether this state's value matches (i.e., is equal to) the value
. This is useful for typestate checking.interpret(machine)
Creates an instance of an interpreted machine, also known as a service. This is a stateful representation of the running machine, which you can subscribe to, send events to, start, and stop.
Actions will also be executed by the interpreter.
Argument | Type | Description |
---|---|---|
machine | StateMachine | The machine to be interpreted. |
Example:
import { createMachine, interpret } from '@xstate/fsm';
const machine = createMachine({
/* ... */
});
const service = interpret(machine);
const subscription = service.subscribe(state => {
console.log(state);
});
service.start();
service.send('SOME_EVENT');
service.send({ type: 'ANOTHER_EVENT' });
// unsubscribes single subscription
subscription.unsubscribe();
// unsubscribes all subscriptions and stops interpretation
service.stop();
service.subscribe(stateListener)
A service (created from interpret(machine)
) can be subscribed to via the .subscribe(...)
method. The subscription will be notified of all state changes (including the initial state) and can be unsubscribed.
Argument | Type | Description |
---|---|---|
stateListener | (state) => void | The listener that is called with the interpreted machine's current state whenever it changes. |
Returns:
A subscription object with an unsubscribe
method.
service.send(event)
Sends an event
to the interpreted machine. The event can be a string (e.g., "EVENT"
) or an object with a type
property (e.g., { type: "EVENT" }
).
Argument | Type | Description |
---|---|---|
event | string or { type: string, ... } | The event to be sent to the interpreted machine. |
service.start()
Starts the interpreted machine.
Events sent to the interpreted machine will not trigger any transitions until the service is started. All listeners (via service.subscribe(listener)
) will receive the machine.initialState
.
service.stop()
Stops the interpreted machine.
Events sent to a stopped service will no longer trigger any transitions. All listeners (via service.subscribe(listener)
) will be unsubscribed.
A machine can be strictly typed by passing in 3 generic types:
TContext
- the machine's context
TEvent
- all events that the machine acceptsTState
- all states that the machine can be inThe TContext
type should be an object
that represents all possible combined types of state.context
.
The TEvent
type should be the union of all event objects that the machine can accept, where each event object has a { type: string }
property, as well as any other properties that may be present.
The TState
type should be the union of all typestates (value and contexts) that the machine can be in, where each typestate has:
value
(string) - the value (name) of the statecontext
(object) - an object that extends TContext
and narrows the shape of the context to what it should be in this state.Example:
interface User {
name: string;
}
interface UserContext {
user?: User;
error?: string;
}
type UserEvent =
| { type: 'FETCH'; id: string }
| { type: 'RESOLVE'; user: User }
| { type: 'REJECT'; error: string };
type UserState =
| {
value: 'idle';
context: UserContext & {
user: undefined;
error: undefined;
};
}
| {
value: 'loading';
context: UserContext;
}
| {
value: 'success';
context: UserContext & { user: User; error: undefined };
}
| {
value: 'failure';
context: UserContext & { user: undefined; error: string };
};
const userMachine = createMachine<UserContext, UserEvent, UserState>({
/* ... */
});
const userService = interpret(userMachine);
userService.subscribe(state => {
if (state.matches('success')) {
// from UserState, `user` will be defined
state.context.user.name;
}
});
import { createMachine, assign, interpret } from '@xstate/fsm';
const lightMachine = createMachine({
id: 'light',
initial: 'green',
context: { redLights: 0 },
states: {
green: {
on: {
TIMER: 'yellow'
}
},
yellow: {
on: {
TIMER: {
target: 'red',
actions: () => console.log('Going to red!')
}
}
},
red: {
entry: assign({ redLights: ctx => ctx.redLights + 1 }),
on: {
TIMER: 'green'
}
}
}
});
const lightService = interpret(lightMachine);
lightService.subscribe(state => {
console.log(state);
});
lightService.start();
// => logs {
// value: 'green',
// context: { redLights: 0 },
// actions: [],
// changed: undefined
// }
lightService.send('TIMER');
// => logs {
// value: 'yellow',
// context: { redLights: 0 },
// actions: [
// { type: undefined, exec: () => console.log('Going to red!') }
// ],
// changed: true
// }
lightService.send('TIMER');
// => logs {
// value: 'red',
// context: { redLights: 1 },
// actions: [],
// changed: true
// }
FAQs
XState for finite state machines
The npm package @xstate/fsm receives a total of 633,726 weekly downloads. As such, @xstate/fsm popularity was classified as popular.
We found that @xstate/fsm demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?
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.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.