Context
context is the machine’s data: a plain object declared in the config and read directly off the service. Writes go through setContext, the single batched entry point.
import { machine } from '@dunky.dev/state-machine'
const m = machine({ initial: 'idle', context: { name: 'Ada', age: 36 }, states: { idle: { on: { birthday: { actions: $ => $.setContext({ age: $.context.age + 1 }) }, rename: { actions: $ => $.setContext({ name: $.event.name }) }, }, }, },})
m.start()m.context.name // 'Ada'm.send({ type: 'birthday' })m.context.age // 37Memory model
Section titled “Memory model”Context is one plain object per machine, copied from the config at construction. Its identity never changes; only its fields are mutated in place. This means:
- Reads are zero-cost: no proxy, no getter.
- Writes are batched:
setContextapplies a shallow merge, deduplicates unchanged fields, then notifies subscribers once. - A field that didn’t change wakes no observer.
setContext
Section titled “setContext”Accepts a partial patch object, shallow-merged into the current context:
$.setContext({ count: 5 })To derive the next value from the current one, read $.context (or use the act() shorthand):
$.setContext({ count: $.context.count + 1 })// or, terser:act($ => ({ count: $.context.count + 1 }))setContext is available in actions, effects, and watchers via the $ argument. The machine never exposes setContext externally; m.context is read-only. To change state from outside, dispatch an event with m.send(...).
act() shorthand
Section titled “act() shorthand”For the common case of writing context in a transition action, act() is write-sugar that drops the $ => $.setContext(...) wrapper:
import { act } from '@dunky.dev/state-machine'
on: { increment: { actions: act($ => ({ count: $.context.count + 1 })) }, reset: { actions: act({ count: 0 }) },}