Headless runtime
Every igniteCore(...) registration is also a headless runtime. The same value you call with a tag name to register a custom element exposes methods that drive and inspect the component contract without a DOM — the surface agents and tests use before any selector is involved.
import { igniteCore } from 'ignite-element/xstate';
const counter = igniteCore({ source: counterMachine, view: ({ matches }) => ({ isOn: matches('on'), }), commands: ({ actor }) => ({ toggle: () => actor.send({ type: 'TOGGLE' }), }), events: (event) => ({ toggled: event<{ isOn: boolean }>(), }), effects: ({ emit, select }) => { const isOn = select((snapshot) => snapshot.matches('on')); if (!isOn.changed) return; emit('toggled', { isOn: isOn.current }); },});
// Register a DOM projection later — the runtime is available immediately.const { state, events } = await counter.execute('toggle');const current = counter.getSnapshot();const view = counter.getView();const schema = counter.getSchema();Methods
Section titled “Methods”execute(commandName, payload?)
Section titled “execute(commandName, payload?)”Runs a command by name and resolves once the resulting transition settles. The payload type is inferred from the command function’s first parameter, so commands that take no argument are called with the name alone.
Returns Promise<{ state, events }> — the state after the transition plus the ordered events emitted by that single transition (an IgniteAgentExecutionResult).
const result = await counter.execute('toggle');The shape:
// IgniteAgentExecutionResult<State, Events>{ state: { value: 'on', context: {} }, events: [{ type: 'toggled', payload: { isOn: true } }],}events is the same ordered stream the runtime emitted for that transition, so assertions stay deterministic and align with the events delivered to on(...) listeners.
For sources with a native emit stream (Actor-Web today), domain events the source emits during the command window are captured too — each as { type, payload } where payload is the emitted event object — independent of the events: declaration. See declared vs source-emitted events.
getSnapshot()
Section titled “getSnapshot()”Returns the current raw snapshot of the underlying source.
Returns State — the underlying source snapshot (the XState snapshot, the Redux state, or the MobX observable value).
const snapshot = counter.getSnapshot();The pre-stable getState() alias was removed at v3.0.0 — use getSnapshot().
getView()
Section titled “getView()”Returns the current projected view — the object your view(...) builder derives from the snapshot.
Returns View — the projected view object, or {} when no view is configured.
const view = counter.getView();on(eventName, handler)
Section titled “on(eventName, handler)”Subscribes to a public emitted event. The handler receives a CustomEvent whose detail is the event payload.
Returns a subscription — { unsubscribe(): void }. Call unsubscribe() to stop listening.
const subscription = counter.on('toggled', (event) => { console.log(event.detail.isOn);});
subscription.unsubscribe();Use on(...) for the public events your component emits through effects. When the source has a native emit stream (Actor-Web today), on(...) also receives the source’s emitted domain events — event.detail is the emitted event object itself, and the names are typed from the source’s Emitted union. (The pre-stable subscribe(eventName, handler) alias was removed at v3.0.0 — use on.)
watchSnapshot(handler)
Section titled “watchSnapshot(handler)”Subscribes to raw snapshot updates. The handler receives (snapshot, prevSnapshot) on every source update.
Returns a subscription — { unsubscribe(): void }.
const subscription = counter.watchSnapshot((snapshot, prevSnapshot) => { console.log(prevSnapshot.value, '->', snapshot.value);});The pre-stable watch(handler) alias was removed at v3.0.0 — use watchSnapshot().
watchView(handler)
Section titled “watchView(handler)”Subscribes to projected view updates. The handler receives (view, prevView) on every view change.
Returns a subscription — { unsubscribe(): void }.
const subscription = counter.watchView((view, prevView) => { console.log(prevView.isOn, '->', view.isOn);});getSchema()
Section titled “getSchema()”Returns a JSON-serializable contract describing the component’s commands, public events, and current state. Command metadata declared with command(...) is included here.
Returns { commands, events, state } — a JSON-serializable IgniteAgentSchema: commands maps each name to its metadata ({} when none), events is the array of declared event names, and state is the current state value.
const schema = counter.getSchema();The shape:
// getSchema() returns:{ commands: { toggle: {}, }, events: ['toggled'], state: { value: 'off', context: {} },}record(name)
Section titled “record(name)”Starts a named story recorder over the same runtime.
Returns a story — an object that records command, state, view, and event activity as one ordered behavior trace (execute, until, trace, lifecycle, summary, stop).
const story = counter.record('toggles on');Determinism
Section titled “Determinism”The runtime seeds prevSnapshot on attach, never replays history, and runs effects(...) once per update in a stable order — which is what keeps execute() and the testing helpers reproducible. The full semantics are documented once in Deterministic effects.
Related
Section titled “Related”- Build for agents — the behavior-first workflow these methods support.
- Command metadata — enrich
getSchema()output. - Testing DSL — scenario and story helpers built on this runtime.