3.6 KiB
context-helpers
Purpose
Provides ergonomic factory helpers for building context-update and side-effect actions that can be slotted directly into TransitionDefinition.actions arrays. Without these helpers, every action must be written as an explicit arrow function returning a new context, which becomes verbose for simple partial-update or logging scenarios.
The two helpers mirror the assign and pure patterns popularised by XState, adapted to the @suaveplan/fsm action signature.
Features
assign(patch)— shallow-merges a static partial object into the contextassign(fn)— delegates to a context-mapping function for complex derivationspure(fn)— wraps a side-effect function ensuring its return value never corrupts the context- Both helpers are fully typed against the machine's
TContextgeneric parameter
Usage: Basic Example
import { assign, pure, createMachine } from '@suaveplan/fsm';
interface Ctx { count: number; lastEvent: string }
const machine = createMachine<Ctx>({
initial: 'idle',
context: { count: 0, lastEvent: '' },
states: {
idle: {
on: {
INCREMENT: {
target: 'idle',
actions: [
assign<Ctx>(ctx => ({ ...ctx, count: ctx.count + 1 })),
pure<Ctx>((ctx, evt) => { console.log('incremented', ctx, evt); }),
],
},
},
},
},
});
Usage: Advanced Example
import { assign } from '@suaveplan/fsm';
interface WizardCtx {
step: number;
formData: Record<string, string>;
completed: boolean;
}
// Static patch (shallow merge)
const resetWizard = assign<WizardCtx>({ step: 1, completed: false });
// Functional update with derived logic
const advanceStep = assign<WizardCtx>(ctx => ({
...ctx,
step: ctx.step + 1,
completed: ctx.step + 1 >= 5,
}));
// Compose them in a transition
const machine = createMachine<WizardCtx>({
initial: 'wizard',
context: { step: 1, formData: {}, completed: false },
states: {
wizard: {
on: {
NEXT: { target: 'wizard', actions: [advanceStep] },
RESET: { target: 'wizard', actions: [resetWizard] },
},
},
},
});
API Reference
assign<TContext>(updater): (context: TContext, event: FSMEvent) => TContext
Parameters:
updater— either aPartial<TContext>patch object or a function(ctx: TContext) => TContext
Returns: An action function compatible with TransitionDefinition.actions.
Behaviour:
- If
updateris a plain object, it performs{ ...context, ...updater }. - If
updateris a function, it callsupdater(context)and returns the result.
pure<TContext>(fn): (context: TContext, event: FSMEvent) => void
Parameters:
fn— a(context: TContext, event: FSMEvent) => voidside-effect function
Returns: A wrapped action function whose return value is always void.
Behaviour: Calls fn(context, event) and discards any return value. This prevents accidental context mutations from side-effect functions that inadvertently return a value.
Implementation Notes
assign accepts both object and function forms by checking typeof updater === 'function'. The cast updater as (ctx: TContext) => TContext is required because TypeScript cannot narrow a union of Partial<TContext> and (ctx) => TContext cleanly at the call site without the explicit cast.
pure exists as a safety wrapper rather than a direct alias because some developers write side-effect actions as (ctx, evt) => doSomething() where doSomething returns a non-void value. Without pure, that non-void value would be treated as a context replacement by the interpreter.