AuthoringxState v5SCXML W3C

xState as the canonical authoring surface

Statecharts replace flat DAGs. Parallel regions, hierarchical states, history, formal transition semantics, SCXML for cross-engine portability, and a free visualizer (Stately) — all without building any of it ourselves.

Why

WDK's grammar is a flat DAG of awaits.

Each step is an await; control flow is JS itself. That works for linear pipelines and breaks down everywhere else — parallel regions, hierarchical states, history, formal verification. xFlow adopts xState as the canonical inner shape so we get those for free.

What xState gets you

  • · Parallel regions (multiple states active simultaneously)
  • · Hierarchical states (compound/atomic/final/history)
  • · Formal transition semantics (SCXML-aligned)
  • · Stately Studio for visual authoring + simulation
  • · SCXML serialization for cross-engine handoff
  • · An ecosystem of test utilities and inspectors

What xFlow adds

  • · Statecharts become content-addressed registry entries
  • · Round-trip codec to/from WorkflowDefinition
  • · Per-step claim/placement/retry config via meta.xflow
  • · Substrate-pluggable execution; no xstate runtime dep

Codec

fromXState — author once, run anywhere.

Take any xstate v5 machine config and produce a WorkflowDefinition the runtime can drive.

fromXState
import { fromXState } from "@decoperations/xflow-xstate"

const machine = {
  id: "shop.checkout",
  initial: "load",
  states: {
    load:    { invoke: { src: "cart.load",       onDone: "price"  } },
    price:   { invoke: { src: "cart.price",      onDone: "charge" } },
    charge:  { invoke: { src: "payments.charge", onDone: "done", onError: "review" } },
    review:  { invoke: { src: "human.review",    onDone: "done"   } },
    done:    { type: "final" },
  },
}

const workflow = fromXState(machine, { id: "shop.checkout", version: "1.0.0" })
// → WorkflowDefinition with steps + links derived from invoke / onDone / onError

Codec (inverse)

toXState — round-trip preserves the topology.

The WorkflowDefinition coming out of fromXState round-trips back to a machine config that you can hand to Stately's visualizer or to another engine. SCXML serialization is on the deliverable list (#2).

toXState
import { toXState } from "@decoperations/xflow-xstate"

const machine = toXState(workflow)
// Round-trip preserves: step ids, types, link topology,
// and per-step config (claim, placement, retry, sideEffects, timeoutMs)
// via meta.xflow on each state.

Per-step config

meta.xflow rides alongside each state.

Claim mode, placement policy, retry policy, side-effect kind, timeout — anything xFlow needs that xstate doesn't model. Standard xstate consumers ignore it; xFlow's codec extracts it.

State-level config via meta.xflow
// Per-step xFlow config rides as meta.xflow on each xstate state.
const machine = {
  id: "deploy",
  initial: "build",
  states: {
    build: {
      invoke: { src: "ci.build", onDone: "deploy" },
      meta: {
        xflow: {
          claim: { mode: "lease", ttlMs: 30_000 },
          retry: { maxAttempts: 3, backoff: { kind: "exponential", initialMs: 200, maxMs: 5000 } },
          timeoutMs: 120_000,
        },
      },
    },
    deploy: { invoke: { src: "ci.deploy", onDone: "done" } },
    done:   { type: "final" },
  },
}

Next

Read on.