xstate@6.0.0-alpha.41
Pre-release
Pre-release
·
0 commits
to main
since this release
Minor Changes
-
14cfdc3: Actors now have deterministic, location-transparent identity.
- Every actor has a logical
address: the/-joined path of actor ids from the root. Root actors are named after their logic'sid, and children spawned without an explicit id get deterministic per-parent counters keyed by their actor source (worker:0,worker:1). Addresses are stable across persistence and restore;sessionIdidentifies one incarnation of an address, and completions from a previous incarnation of a local child are dropped. enq.spawn(actors.x)records the registered source key so spawned children persist by key.getEffectDescriptor(effect)returns a serializable view of any executable effect, with actor references replaced by addresses and actor sources by source keys (payload fields pass through by reference).- A host runtime can be installed as
system.runtime; the built-in local runtime is the default. The newdeliverEvent,stopActorandterminateActorhelpers expose the local behaviors for custom runtimes to delegate to. createDurable(fromxstate/durable) adapters carry their runtime operations directly (sendEvent,scheduleTimer, …), and the execution installs them on every snapshot's actor system; it exposesrootAddressandgetActorRef(snapshot), tags every effect with a serializabledescriptor, andexecuteEffectsresolves only when every transitively initiated runtime operation has been accepted — returning the events addressed to the root actor for the durable loop. Breaking for existing adapters: duringexecuteEffects, root-addressed events no longer reach any runtimesendEvent(including per-effectruntime()implementations) — drain them from theexecuteEffectsresult instead. While the loop is parked inwaitForEvent, a root-addressed event reachessendEventlike any other target, and the host should enqueue it in its own mailbox. Runtime objects are also wrapped before effects see them, so identity comparisons and extra non-runtime properties on the returned object are not preserved.getPersistedSnapshot(snapshot, { embedChildren: false })persists children by logical address, leaving each child's state with the runtime that owns it; restoring an address-only child produces a location-transparent handle whose sends route through the system runtime.- Explicit child ids are unique per parent: spawning or invoking with an id already held by a live sibling throws (ids stopped earlier in the same transition stay reusable). Previously a duplicate id silently created a second running actor at the same address. A transition to a history state that restores its own source now exits and reenters the source, so its invoked actors restart instead of leaking.
- Undeliverable events are reported through the new
deadLetterruntime operation and a@xstate.deadletterinspection event. Delivery stays at-most-once — this is observability, not retry. - A persisted remote child entry round-trips an optional opaque
incarnationtoken. XState never stamps one, but when a host does, completions from a different incarnation of the address are dropped andsendToeffect descriptors journal the target's token. createDurableexposesmachineIdandmachineVersionso hosts can pin an execution's journal to the machine version that produced it.- Async-actor steps (
enq.step) route through the newrunStepruntime operation. The built-in behavior memoizes results in the actor's own snapshot as before; a durable host implementsrunStepto own the step journal, replaying memoized results without re-running the step. TherunStephelper export exposes the built-in behavior. - Serialized actor references (
Actor.toJSON, persisted context refs) carryxstate$type: 'actorRef'instead of the v5xstate$$type: 1marker. Migrate v5-persisted context refs withmachineVersionsif you restore them. - Timers persisted from a running actor carry their wall-clock start (
startedAt); restoring the snapshot schedules the remaining time toward the original deadline (clamped to the declared delay), so a timer past due fires immediately instead of restarting its full delay. Pure-transition snapshots carry no timestamp and restart the declared delay.
const durable = createDurable(machine, { sendEvent: (source, target, event) => host.send(source?.address, target.address, event), executeAction: (action, { id }, runtime) => host.runAction(id, () => action.exec(runtime)), waitForEvent: ({ id }) => host.waitForEvent(id) });
- Every actor has a logical