-
1e2966aThanks @cevr! -actor.subscribe,actor.client.subscribeandself.client.subscribelisteners now get every state written after they subscribe, each once, in write order, like the waits. A listener reads the states from the state ref's own publication, and a notification only wakes it. Before this change a supervised restart could deliver its restart state after a newer state of the new generation, a send from a listener callback could deliver the newer state to later listeners before the older one and call a listener again inside its own callback, a state whose entry crashed before its notification was never delivered, and a stop of an actor could drop the last write. Behavior changes: a listener that subscribes between a write and its notification, or inside another listener's callback, no longer gets that state (it was current when it subscribed; readgetSnapshotfor it). This includes a listener that a.spawnhandler subscribes: it no longer gets the state that the handler runs for (0.30.0 delivered it); read the handler'sstateorgetSnapshot()for it; a listener is not called again while its own callback runs, and gets the states its callback caused after it returns; the listeners of an actor get a write that a stop overtook beforeawaitExitcompletes; eachsubscribecall is its own registration, so the same function subscribed twice gets each write twice (the listeners were a set of functions before, which called it once), and each unsubscribe ends one registration. The ordering and terminal guarantees cover local actor listeners. An entity'sself.clientlistener can still miss a write that the entity's deactivation overtakes, as in 0.30.0: the entity runtime does not ring its listeners at teardown. -
1469452Thanks @cevr! -actor.waitFor,actor.awaitFinalandactor.sendAndWaitnow end when the actor terminates. They fail withActorStoppedError(the errorawaitOutputalready uses) when the actor stops, drains, defects, or reaches a final state, and no state from the start of the wait matched. Before this change a wait never resolved after the actor terminated without a match, so callers had to race it againstawaitExitor a time limit. The waits now read the states from the state ref's own publication: the predicate sees the snapshot and then every later state write, each once, in write order and one call at a time, and the wait returns the first match. A brief state around the start of the wait is no longer lost, a state counts as reached once it is written (also when its entry crashes later, when a supervised restart follows, or when a stop comes before its notification), the predicate is not called again after it matches, and a predicate that sends an event cannot re-enter the wait.sendAndWaitstarts to observe before it sends, and does not send when the predicate throws on the snapshot. A predicate that throws on a later state now fails the wait with that defect instead of being ignored. A state restored by recovery now notifiessubscribelisteners and waiters. A wait releases its listener and its subscription when it ends, also when it is interrupted at any point. The error channel of all three widens fromnevertoActorStoppedError: code that annotates them asEffect<State>must handle or declare the new error.
d176baaThanks @cevr! - AddMachine.definition(machine), a read-only, typed snapshot of every registration a built machine holds. Each state, in schema order, lists its transitions in the order the runtime tries them, its immediate transitions, its state-owned Effects (spawn,taskandtimeout, in start order), its postponed events, and whether it is final. The machine also lists its event tags, itsonAny/whenAnytransitions (tried after a state's own transitions), and its background Effects. Each entry reports the builder method and names: handler and guard function names, the task'sname, and$timeout:<StateTag>for a timeout. State and event tags are the schema's literal unions. The definition reads the same indexes the runtime selects from, so it cannot drift from runtime behavior. It reports names, not handlers or targets: handlers compute targets at run time, so useMachine.replayto find one. Each call returns a new deeply frozen value, and later builder calls appear only in the next call. Runtime behavior, inspection events and task names do not change.
- #90
ac08b6dThanks @cevr! - Report contained actor failures to the EffectErrorReporters that were registered where the actor was spawned. Each runtime generation reports its complete defect cause once when it closes, including transition, spawn, task, background, and cleanup failures. Supervised restarts report each generation. A restart step that fails before a new generation exists reports with phaserestart. This includes a restart recovery and a restart schedule that dies; an exhausted schedule does not report. A cold-start recovery that fails before the first generation runs reports with phaserecovery, also when a stop interrupts the start. A recovery that throws instead of returning an Effect reports the same way. A schedule defect that settles together with schedule exhaustion still reports. A final output defect reports when the actor completes. Child actors report through the reporters of the handler that spawned them. Normal stops, final states, and pure interruption do not report. Each report annotates the reporting fiber with the actor ID, generation, and defect phase. The actor keeps its spawn-time reporters, so a laterstartorstopcaller cannot replace them. Handler code inside the actor also runs with the spawn-time reporters, soEffect.withErrorReportingin a handler reaches them and no longer reaches reporters that were provided only aroundstart. A failure that also reaches a caller boundary, such asEffect.withErrorReporting, an RPC server or an HTTP handler, can report again there. A reporter built withErrorReporter.makeskips the second report only when the defect is an object; a primitive defect or a raw reporter reports twice. A parent handler that re-raises a child failure, such as a failed child stop or a failedself.spawnstart, reports that cause again as the parent's defect, with the same deduplication rules. A throwing reporter does not change the actor exit. Effect calls the reporters in order, so a throwing reporter can stop the reporters after it. Cluster entity runtimes report generation defects through the reporters in their allocation context. The generation of an entity report names one allocation, so it is0after each reactivation. Without registered reporters, behavior does not change.
abafafcThanks @cevr! - Requires Effect 4.0.0. The peer range is now>=4.0.0 <5. The cluster and atom entries import the stableeffect/cluster,effect/rpcandeffect/reactivitypaths that replacedeffect/unstable/*in Effect 4.0.0.
- #87
9fb5d36Thanks @cevr! - Allow ActorHost factories to receive typed data from the hosting parent as a second argument. Call host(request, hostInput) to bind parent input to a generation while consumers continue to call acquire(request). Existing one-argument factories keep their current behavior.
- #85
a210b82Thanks @cevr! - Add ActorHost for lazy actors owned by a parent state scope. Consumers share startup without owning cancellation or shutdown. The factory captures service dependencies, uses the first matching request input, and releases the actor on state exit or host service shutdown.
- #83
c0aea35Thanks @cevr! - Release waitFor listeners when the caller cancels or the subscription recheck fails. This also applies to timed waits, awaitFinal, and sendAndWait.
-
#81
d4f3deaThanks @cevr! - Allow an actor to stop itself from protected supervised recovery. Wait for the supervisor to finish cleanup and report its cleanup defects to every stop caller.Apply the restart policy when a restarted generation fails during startup. Continue within the retry budget and complete the actor exit when that budget ends.
- #79
4d431d1Thanks @cevr! - Stop pending actor startup before closing its runtime. Wait for recovery cleanup even when a stop caller cancels its wait. Finish shutdown owner creation and cache publication before accepting caller cancellation. Preserve terminal lifecycle during initial startup and supervised activation. Keep synchronous host sends ready when startup completes. Preserve recovery cleanup errors and prevent recovery fallbacks from swallowing self-stop.
- #77
a92ff6aThanks @cevr! - Run actor startup once across concurrent and repeated calls. Preserve terminal lifecycle when start is called after stop. Keep replacement actors registered when an earlier owner scope closes.
- #75
fec20d4Thanks @cevr! - Publish the terminal actor lifecycle before completing shutdown. Callers of stop and awaitExit now observe the same terminal result through lifecycle, without waiting for a detached observer task.
- #73
0b9f0e3Thanks @cevr! - Coordinate actor shutdown through one shared completion. Concurrent and repeated stops now wait for runtime, child, and scope finalizers. Cleanup defects remain visible asActorExit.Defectwith phasecleanup.
- #71
24a421bThanks @cevr! - Validate deferredMachine.deferReplyvalues with the event reply schema before settlingActorRef.ask. If state exit interrupts reply decoding, settle the waiting caller with that interruption instead of leaving it pending.
- #69
c53b0a9Thanks @cevr! - Validate the package against Effect 4.0.0-rc.112 and the current Effect toolchain.
- #67
e183c26Thanks @cevr! - Wait for the old generation resources to close before a supervised actor restarts.
- #65
57c91bdThanks @cevr! - Expose a narrow synchronous client to machine-owned Effects. Bind each background resource to its actor generation.
- #56
623b906Thanks @cevr! - Commit synchronous client transitions beforeActorClient.sendreturns and notify Actor Atom subscribers without a timer delay.
- #53
69d423cThanks @cevr! - Start state and background Effects eagerly so their synchronous setup completes before actor state becomes visible. A task that completes in its first Effect slice can now enqueue its result before a caller sends its next event. A suspending Inspector can delay this setup because inspection remains ordered and awaited.
- #50
1602affThanks @cevr! - Keep actor-local inspection separate from ambient inspection when actors spawn nested actors or restart. Route task inspection through the same actor dispatcher for local, system, and cluster inspectors.
- #48
4b8523dThanks @cevr! - Ensure actors spawned through an ActorSystem use that same system for child actors and inspection.
- #46
41d00a1Thanks @cevr! - Replace the Inspector Hub with typedinspectspawn options and late system-wide registration throughactor.system.inspect.
- #44
1615eeaThanks @cevr! - Expose the actor-owned current state and latest accepted transition refs on typed machine self references for machine-lifetime background observers.
- #38
5cf9b40Thanks @cevr! - Add typed ActorSystem keys, per-ID actor generation streams, and Suspense-ready Actor Atoms for named actor acquisition.
-
#34
c0b6845Thanks @cevr! - Add.when()transitions with Boolean or Effect predicates, reactive guard-awarecan, a non-Effectactor.clientfacade, Effectful transition handlers with anevererror contract, stable eventless transitions, typed machine input and output, scopedMachine.runcomposition, cluster input adapters, actor lifecycle observation, retained latest transitions, and automatic actor system cleanup after terminal exits.Add a Bun examples workspace with tested core, React, and Solid implementations. Add guides for Effect composition, actor topology, async work, Atom selectors, animation retention, persistence, supervision, inspection, testing, cluster entities, and XState migration.
Make task failure handlers receive the typed Effect error. Keep defects in the actor failure and supervision path. Add named transition-operation inspection. Add actor generation to every inspection event.
- #31
a3c1e46Thanks @cevr! - Add a framework-neutral Effect Atom adapter for actors with writable selectors and custom equality support. Include working React and Solid package examples with Suspense-owned actor lifecycles, animated exits, selector performance tests, and exit-safe terminal state modeling.
-
76fcba1Thanks @cevr! - Move the package to Effect 4 RC.Remove Effect 3 compatibility and the Slot API.
Let task, spawn, and background handlers require native Effect services.
Capture provided services when an actor is allocated so a later start keeps the same context.
-
751862bThanks @cevr! - Upgrade to effect 4.0.0-beta.102 and raise the peer floor to>=4.0.0-beta.102.
-
f7ae688Thanks @cevr! - Add synchronous fast paths for pure simulation and same-state actor transitions.Provide captured services once per actor event loop.
Remove internal per-event trace wrappers while keeping inspector events and observation streams.
-
5704c1aThanks @cevr! - Construct schema-tagged errors through.makeinstead ofnew, model finite numeric schema fields withSchema.Finite, and route an unhandledNoReplyErrorin the cluster entity loop throughEffect.orDie. -
52ec1f6Thanks @cevr! - Validate slot codecs through the Effect error channel and route inspector callbacks throughEffect.try, so schema mismatches and throwing inspectors no longer rely ontry/catch.
99fcb3fThanks @cevr! - Update the Effect v4 toolchain toeffect@4.0.0-beta.64and run tsdown through Bun so CI and local builds share Bun's package resolution.
5e344f7Thanks @cevr! - Renamederivetowithon state/event schemas. Union-levelwithaccepts shared fields without casts on generic type parameters. Add.schema(branded) accessor toMachineStateSchema. Remove.plain— use constructors in tests instead of raw object literals. AddSlot.of(slotsSchema, provided)to convertProvideSlotsintoSlotCallswithout consumer-side casts.
-
79a9311Thanks @cevr! - Modernize the repository toolchain around@effect/tsgo, the latest Effect beta, and type-aware oxlint.effectis now peer-only at runtime, v4 service tags use theContext.Serviceclass form withserviceNotAsClassenabled, and the v3 mirror is validated through the same tsgo gate.
-
d17665eThanks @cevr! - Replace ambient Scope detection with explicit ActorScope serviceMachine.spawnandsystem.spawnno longer attach cleanup finalizers to ambientScope.Scope. This fixes a bug where unrelated scopes would unexpectedly tear down actors.- New
ActorScopeservice tag — when present in context, actors attach stop finalizers to it. - New
Machine.scoped(effect)helper bridgesScope.Scope→ActorScopefor opt-in auto-cleanup. - Backport
callimprovements to v3: warning log on stopped actor, completeProcessEventResultfields withsatisfiesfor type safety. - Fix
bun testtsconfig resolution and addtest:all/ v3 tests to gate.
-
ec8ccd3Thanks @cevr! - Upgrade to effect 4.0.0-beta.47, require tsgo for type checkingBreaking: Minimum peer dependency is now
effect@>=4.0.0-beta.47. TheServiceMapmodule was removed upstream — allServiceMap.Serviceusages are nowContext.Service.- Rename
ServiceMap.Service→Context.Servicethroughout - Rename
Effect.services()→Effect.context() - Switch type checker from
tsctotsgo(native Go compiler via@typescript/native-preview) - Switch Effect LSP from tsconfig plugin patch to
effect-language-service diagnosticsCLI - Simplify tsconfig.json for TypeScript 6 defaults
- Rename
-
0c81fd3Thanks @cevr! - fix(v3): backport reply metadata stripping from event constructor payloadsEvent.reply(...)constructor payload typing no longer leaks reply schema metadata into user payload arguments.
-
612e686Thanks @cevr! - FixEvent.reply(...)constructor payload typing so reply schema metadata does not leak into user payload arguments.Add regressions for:
- payload-bearing reply event constructors accepting plain payload objects
ask()with payload-bearing reply events
-
d518ed6Thanks @cevr! - Cold spawn + Recovery/Durability lifecycle API (v3 backport).Breaking changes:
Machine.spawnnow returns an unstarted actor. Callyield* actor.startto fork the event loop, background effects, and spawn effects. Events sent beforestart()are queued.system.spawnauto-starts — no change needed for registry-based spawns.PersistConfig<S>is removed. UseLifecycle<S, E>instead.
New APIs:
ActorRef.start— idempotent Effect that starts the actorRecovery<S>— resolves initial state per generation duringactor.startRecoveryContext<S>—{ actorId, generation, machineInitial }Durability<S, E>— saves state after committed transitionsDurabilityCommit<S, E>—{ actorId, generation, previousState, nextState, event }Lifecycle<S, E>—{ recovery?, durability? }
Migration from
PersistConfig:// Before (PersistConfig) Machine.spawn(machine, { persist: { load: () => storage.get(key), save: (state) => storage.set(key, state), shouldSave: (state, prev) => state._tag !== prev._tag, onRestore: (state, { initial }) => validate(state), }, }); // After (Lifecycle) const actor = yield * Machine.spawn(machine, { lifecycle: { recovery: { // Replaces load() + onRestore() — single callback resolve: ({ actorId, generation, machineInitial }) => storage.get(key).pipe( Effect.map(Option.fromNullable), // Do any validation/migration here ), }, durability: { // Receives full commit context, not just the new state save: ({ actorId, generation, previousState, nextState, event }) => storage.set(key, nextState), shouldSave: (state, prev) => state._tag !== prev._tag, }, }, }); yield * actor.start; // NEW: explicit start required
Key differences from
PersistConfig:Recovery.resolvemergesload()+onRestore()into one callbackRecovery.resolvereceivesRecoveryContextwithactorId,generation(0 = cold start, 1+ = supervision restart), andmachineInitialDurability.savereceivesDurabilityCommitwith full transition context (previous state, next state, event, generation)- Recovery runs during
actor.start, not during allocation hydrateoption overrides recovery entirely (resolve is never called)
-
ff7fd8fThanks @cevr! - Unified slot system redesign + local persistence + slot schemas.Breaking changes (pre-1.0):
Slot.Guards/Slot.Effectsreplaced bySlot.define+Slot.fnguards:/effects:onMachine.makereplaced by singleslots:field- Slot handlers take only params — no ctx parameter. Use
yield* machine.Contextfor machine state. HandlerContext.guards/HandlerContext.effectsreplaced byHandlerContext.slotsStateHandlerContext.effectsreplaced byStateHandlerContext.slots- Removed:
SlotContext,GuardsDef,EffectsDef,GuardSlot,EffectSlot,GuardHandlers,EffectHandlers,HasGuardKeys,HasEffectKeys SlotProvisionError.slotTypeis now"slot"only (was"guard" | "effect" | "slot")Machine.spawnslotsoption is nowProvideSlots<SD>(type-checked, wasRecord<string, any>)
New APIs:
Slot.fn(fields, returnSchema?)— define a slot with typed params and arbitrary return typeSlot.define({ ... })— create a slots schema from slot definitionsSlotFnDef.inputSchema/outputSchema— materialized schemas for runtime validation and serializationSlotsSchema.requestSchema/resultSchema/invocationSchema— wire-format schemas for RPC and persistenceslotValidationoption onMachine.make— runtime input/output validation (default: true)SlotCodecError— tagged error for validation failures (raised as defect)PersistConfig<S>— local persistence forMachine.spawn:load()→ hydrate from storagesave(state)→ save after transitionsshouldSave?(state, prev)→ filter savesonRestore?(state, { initial })→ recovery decision hook
ActorSystem.spawnnow acceptsslotsandpersistoptions- Multi-state
.spawn()and.task()overloads (array of states) .task()shorthand — omitonSuccesswhen task returns Event directly
Internal:
resolveActorSystem()andrunSupervisionLoop()extracted fromcreateActorQueue.clearreplaces manual poll loop for shutdown drain- Plain-object return bug fixed in slot resolve (uses
Effect.isEffect) materializeMachinethreads_slotValidationthrough copies
8e8a9ceThanks @cevr! - - feat: union-levelderiveon State and Event schemas — dispatches by_tag, preserves specific variant subtype- fix:
derivepartial keys not in target variant are now silently dropped - fix:
.task()onSuccessis now optional — omit when task returns Event directly
- fix:
-
74e3feaThanks @cevr! - Add actor supervision with automatic restart on defect.New APIs:
Supervision.restart({ maxRestarts, within, backoff })— Schedule-based restart policySupervision.none— no supervision (default, crashes are terminal)actor.awaitExit— resolves withActorExit<S>when actor terminally stopsactor.watch(other)— now returnsActorExit<unknown>(breaking, pre-1.0)
New types:
ActorExit<S>—Final { state }|Stopped|Defect { cause, phase }DefectPhase—"transition"|"spawn"|"background"|"initial-spawn"Supervision.Policy— Schedule-based restart policy interface
Usage:
import { Machine, Supervision } from "effect-machine"; const actor = yield * Machine.spawn(machine, { supervision: Supervision.restart({ maxRestarts: 3, within: "1 minute" }), }); const exit = yield * actor.awaitExit; // ActorExit<S>
Breaking changes (pre-1.0):
watch()returnsEffect<ActorExit<unknown>>instead ofEffect<void>SystemEvent.ActorStoppedgainsexit: ActorExit<unknown>field- New
SystemEvent.ActorRestartedvariant
Internal:
- Runtime kernel split: cell-owned resources, actorScope, exitDeferred
- Background/spawn/transition defect detection with DefectPhase tagging
- Generation owner fiber for actorScope lifecycle
Effect.runForkWithfor proper service propagation (v4)globalXInEffectdiagnostics enabled in tsconfig
-
f26b21fThanks @cevr! - feat(cluster): entity persistence with snapshot and journal strategiesAdd opt-in state persistence for entity-machines across deactivation/reactivation:
- Snapshot strategy: periodic background saves + deactivation finalizer. Simple, fast.
- Journal strategy: inline event append on each Send/Ask RPC, replay on reactivation. Full audit trail.
- PersistenceAdapter service tag with
saveSnapshot,loadSnapshot,appendEvents(CAS),loadEvents - InMemoryPersistenceAdapter for testing/development
- PersistenceKey =
{ entityType, entityId }prevents cross-type collisions - Journal append failures defect the entity (cluster retry restarts from last snapshot)
- Snapshot scheduler only in snapshot-only mode (prevents state/version tear in journal mode)
- v3 backport included
Also includes the cluster overhaul (runtime kernel, EntityActorRef, WatchState, self.reply, self.spawn).
-
33d8a87Thanks @cevr! - feat: add typed reply schemas for ask()Event.reply(fields, schema)— declare reply-bearing events with schema validationMachine.reply(state, value)— branded helper replacing duck-typed{ state, reply }actor.ask(event)— infers return type from event's reply schema; non-reply events are type errors- Runtime validation: reply values decoded through schema; decode failure = defect
- Entity-machine:
AskRPC propagates replies through cluster boundary - Backported to v3
-
6bdee0cThanks @cevr! - Delete monolithic persistence subsystem, add composable primitives.Added:
Machine.replay(built, events, { from? })— fold events through transition handlers to compute state. Respects postpone rules and final-state cutoff. Runs effectful handlers with stubbed self/system.actor.transitions— PubSub-backed stream of{ fromState, toState, event }on every successful transition. Observational, not a durability guarantee.
Removed:
PersistenceAdapter,PersistenceAdapterTag,PersistenceError,VersionConflictErrorPersistentMachine,PersistentActorRef,PersistenceConfigcreatePersistentActor,restorePersistentActor,isPersistentMachineInMemoryPersistenceAdapter,makeInMemoryPersistenceAdapterMachine.persist(),BuiltMachine.persist()ActorSystem.restore,ActorSystem.restoreMany,ActorSystem.restoreAll,ActorSystem.listPersisted
Migration: Compose persistence from primitives:
- Snapshot:
actor.changes→ save to your store - Event journal:
actor.transitions→ append events - Restore from snapshot:
Machine.spawn(machine, { hydrate: loadedState }) - Restore from events:
Machine.replay(machine, events)→Machine.spawn(machine, { hydrate: state })
3ff2dfbThanks @cevr! - Fix multi-stage postpone drain in live actor event loop. Previously, postponed events were drained in a single pass — if a drained event caused a state change that made other postponed events runnable, they waited until the next mailbox event. Now loops until stable, matching simulate() and replay() behavior.
-
921e063Thanks @cevr! - OTP-inspired API redesign:- rename dispatch→call, add cast alias for send
- extract sync helpers to actor.sync.* namespace
- add ask() for typed domain replies from handlers
- add .timeout() for gen_statem-style state timeouts
- add .postpone() for gen_statem-style event postpone
- fix reply settlement (ActorStoppedError on stop/interrupt)
Breaking: removed top-level sync methods (sendSync, stopSync, etc.), removed dispatchPromise.
-
eee2ff4Thanks @cevr! - Backport all v4 features to Effect v3 variant + restructure intov3/directoryRestructure:
src-v3/→v3/src/,tsconfig.v3.json→v3/tsconfig.json,tsdown.v3.config.ts→v3/tsdown.config.ts- Added
v3/test/with full test suite (248 tests) - Package exports unchanged:
effect-machine/v3,effect-machine/v3/cluster
Features backported from v4:
call()— serialized request-reply (OTP gen_server:call)cast()— fire-and-forget alias for send (OTP gen_server:cast)ask()— typed domain reply from handler's{ state, reply }returnActorRef.syncnamespace — replaces flatsendSync/stopSync/snapshotSync/matchesSync/canSync.timeout()builder — gen_statem-style state timeouts.postpone()builder — gen_statem-style event postpone with drain-until-stablehasReplystructural flag onProcessEventResultActorStoppedError/NoReplyErrorerror typesmakeInspectorEffect/combineInspectors/tracingInspectorinspection helpersactorIdthreaded through all handler contexts
Bug fix:
State.derive()now guards against_tagoverride in partial argument
-
6e3497bThanks @cevr! - AdddispatchanddispatchPromiseto ActorRef — synchronous event processing with transition receipts.dispatch(event)— Effect-based. Sends event through the queue (preserving serialization) and returnsProcessEventResult<State>with{ transitioned, previousState, newState, lifecycleRan, isFinal }. OTPgen_server:callequivalent.dispatchPromise(event)— Promise-based. Same semantics asdispatchfor use at non-Effect boundaries (React event handlers, framework hooks, tests).Also exports
ProcessEventResultfrom the public API.
675f461Thanks @cevr! - Add effectful inspectors, scopedfrom(...)transitions, and named task inspection events.
fa54c61Thanks @cevr! - Update Effect dependency to 4.0.0-beta.21- Replace
Effect.makeSemaphorewithSemaphore.make(removed in beta.6)
- Replace
34c85b8Thanks @cevr! - Migrate to Effect v4 (4.0.0-beta.5)- Default export now targets Effect v4 — v3 users should import from
effect-machine/v3 - Migrate src/ and test suite to v4 APIs (Effect, Schema, SubscriptionRef, ServiceMap)
- MachineStateSchema/MachineEventSchema now use Schema.Codec for encode/decode compat
- 213 tests passing across 19 files
- Default export now targets Effect v4 — v3 users should import from
-
9d5bd6fThanks @cevr! - Add actor.children to expose child actors spawned via self.spawnactor.childrenreturnsReadonlyMap<string, ActorRef>of children spawned viaself.spawn- State-scoped children auto-removed from map on state exit
- Works for both regular and persistent actors
-
9d5bd6fThanks @cevr! - Add observable ActorSystem with event stream and sync observationsystem.subscribe(fn)— sync callback forActorSpawned/ActorStoppedevents, returns unsubscribesystem.actors— sync snapshot of all registered actors (ReadonlyMap)system.events— asyncStream<SystemEvent>via PubSub (each subscriber gets own queue)- Works with both explicit (
ActorSystemDefault) and implicit (Machine.spawn) systems - No events emitted during system teardown
- Double-stop prevention:
system.stop+ scope finalizer won't emit duplicateActorStopped
-
28835c6Thanks @cevr! - Add observable ActorSystem and wire up actor.children for persistent actorsActorSystem observation:
system.subscribe(fn)— sync callback forActorSpawned/ActorStoppedevents, returns unsubscribesystem.actors— sync snapshot of all registered actors (ReadonlyMap)system.events— asyncStream<SystemEvent>via PubSub (each subscriber gets own queue)- Works with both explicit (
ActorSystemDefault) and implicit (Machine.spawn) systems - No events emitted during system teardown
- Double-stop prevention:
system.stop+ scope finalizer won't emit duplicateActorStopped
actor.children:
- Wire up
childrenMapin persistent actors soactor.childrenreflectsself.spawnchildren - Children auto-removed from map on scope close (state-scoped cleanup)
bd3953eThanks @cevr! - Add child actor support:self.spawn()from handlers,actor.systemon every ActorRef, implicit system creation forMachine.spawn, and automatic lifecycle coupling to state scope
a16c44eThanks @cevr! - Add tsdown build step. Library now ships pre-built ESM with .d.ts declarations instead of raw TypeScript source.
- Add
stopSynctoActorRef— fire-and-forget stop for sync contexts (framework cleanup hooks, event handlers).
-
0154ac9Thanks @cevr! - feat: DX overhaul — multi-state.on(),State.derive(),.onAny(),waitFordeadlock fix,sendSync,waitFor(State.X),.build()/BuiltMachine- Multi-state
.on()/.reenter(): Accept arrays of states —.on([State.A, State.B], Event.X, handler) State.derive(): Construct new state from source —State.B.derive(stateA, { extra: val })picks overlapping fields + applies overrides.onAny()wildcard transitions: Handle event from any state —.onAny(Event.Cancel, () => State.Cancelled). Specific.on()takes priority.waitFordeadlock fix: Rewrote to use sync listeners +Deferredinstead ofSubscriptionRef.changesstream, preventing semaphore deadlock on synchronous transitionssendSync: Fire-and-forget sync send for framework integration (React/Solid hooks)waitFor(State.X): Accept state constructor/value instead of predicate —actor.waitFor(State.Active)andactor.sendAndWait(event, State.Done).build()/BuiltMachine: Terminal builder method —.provide()renamed to.build(), returnsBuiltMachine..validate()removed.Machine.spawnandActorSystem.spawnacceptBuiltMachine. No-slot machines:.build()with no args.
- Multi-state
-
365da12Thanks @cevr! - feat: removeScope.ScopefromMachine.spawnandsystem.spawnsignatures- Scope-optional spawn:
Machine.spawnandActorSystem.spawnno longer requireScope.ScopeinR. Both detect scope viaEffect.serviceOption— if present, attach cleanup finalizer; if absent, skip. - Daemon forks: Event loop, background effects, and persistence fibers use
Effect.forkDaemon— detached from parent scope, cleaned up byactor.stop. - System-level cleanup:
ActorSystemlayer teardown stops all registered actors automatically.ActorSystemDefaultis nowLayer.scoped. - Breaking: Callers that relied on
Scope.Scopeappearing in theRtype of spawn may need type adjustments.Effect.scopedwrappers around spawn are no longer required but still work (scope detection finds them).
- Scope-optional spawn:
-
d29f2ffThanks @cevr! - feat: makeInspector accepts Schema constructors as type paramsmakeInspector<typeof MyState, typeof MyEvent>(cb)now auto-extracts.Typefrom schema constructors viaResolveType. No need fortypeof MyState.Typeanymore.
-
85a2854Thanks @cevr! - fix: use forkScoped for event loop fiber to prevent premature interruptionChanged
Effect.forktoEffect.forkScopedfor the actor event loop fiber. Previously, the event loop was attached to the calling fiber's scope, meaning it would be interrupted when a transient caller completed. Now the event loop's lifetime is tied to the providedScope.Scopeservice, allowing callers to control the actor's lifecycle explicitly.
-
2bcb0bbThanks @cevr! - AddAnyInspectionEventtype alias and default generic params onmakeInspector/consoleInspector/collectingInspectorso untyped inspectors work without explicit casts. -
c50c3ceThanks @cevr! - FixwaitForrace condition where a fast state transition betweengetandchangessubscription could causewaitForto hang forever. Now usesstateRef.changesdirectly which emits the current value atomically as its first element.
639ee87Thanks @cevr! - Fix PersistenceAdapterTag key for deterministic key diagnostics and align language-service config.
-
f4a8c8fThanks @cevr! - - send after stop is a no-op for actors and persistent actors- async persistence worker for journaling/metadata to keep event loop fast
- snapshot schedule completion no longer stops event loop
- serialize spawn/restore to avoid duplicate side-effects under concurrency
-
a77f9faThanks @cevr! - Add Machine.task, ActorRef wait/await helpers, and error inspection events. -
3172971Thanks @cevr! - Add Effect.fn tracing across actor, persistence, cluster, and testing flows. Fix persistent actor lifecycle (spawn/background effects, snapshot scheduler), restore-from-events behavior, duplicate spawn handling, and slot preflight errors. Update persistence docs; PersistentActorRef now carries environment R and replayTo runs in R.