Skip to content

Latest commit

 

History

History
659 lines (424 loc) · 46.4 KB

CHANGELOG.md

File metadata and controls

659 lines (424 loc) · 46.4 KB

effect-machine

0.31.0

Minor Changes

  • 1e2966a Thanks @cevr! - actor.subscribe, actor.client.subscribe and self.client.subscribe listeners 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; read getSnapshot for it). This includes a listener that a .spawn handler subscribes: it no longer gets the state that the handler runs for (0.30.0 delivered it); read the handler's state or getSnapshot() 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 before awaitExit completes; each subscribe call 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's self.client listener 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.

  • 1469452 Thanks @cevr! - actor.waitFor, actor.awaitFinal and actor.sendAndWait now end when the actor terminates. They fail with ActorStoppedError (the error awaitOutput already 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 against awaitExit or 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. sendAndWait starts 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 notifies subscribe listeners 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 from never to ActorStoppedError: code that annotates them as Effect<State> must handle or declare the new error.

0.30.0

Minor Changes

  • d176baa Thanks @cevr! - Add Machine.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, task and timeout, in start order), its postponed events, and whether it is final. The machine also lists its event tags, its onAny/whenAny transitions (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's name, 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 use Machine.replay to 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.

0.29.0

Minor Changes

  • #90 ac08b6d Thanks @cevr! - Report contained actor failures to the Effect ErrorReporters 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 phase restart. 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 phase recovery, 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 later start or stop caller cannot replace them. Handler code inside the actor also runs with the spawn-time reporters, so Effect.withErrorReporting in a handler reaches them and no longer reaches reporters that were provided only around start. A failure that also reaches a caller boundary, such as Effect.withErrorReporting, an RPC server or an HTTP handler, can report again there. A reporter built with ErrorReporter.make skips 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 failed self.spawn start, 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 is 0 after each reactivation. Without registered reporters, behavior does not change.

0.28.0

Minor Changes

  • abafafc Thanks @cevr! - Requires Effect 4.0.0. The peer range is now >=4.0.0 <5. The cluster and atom entries import the stable effect/cluster, effect/rpc and effect/reactivity paths that replaced effect/unstable/* in Effect 4.0.0.

0.27.0

Minor Changes

  • #87 9fb5d36 Thanks @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.

0.26.0

Minor Changes

  • #85 a210b82 Thanks @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.

0.25.9

Patch Changes

  • #83 c0aea35 Thanks @cevr! - Release waitFor listeners when the caller cancels or the subscription recheck fails. This also applies to timed waits, awaitFinal, and sendAndWait.

0.25.8

Patch Changes

  • #81 d4f3dea Thanks @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.

0.25.7

Patch Changes

  • #79 4d431d1 Thanks @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.

0.25.6

Patch Changes

  • #77 a92ff6a Thanks @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.

0.25.5

Patch Changes

  • #75 fec20d4 Thanks @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.

0.25.4

Patch Changes

  • #73 0b9f0e3 Thanks @cevr! - Coordinate actor shutdown through one shared completion. Concurrent and repeated stops now wait for runtime, child, and scope finalizers. Cleanup defects remain visible as ActorExit.Defect with phase cleanup.

0.25.3

Patch Changes

  • #71 24a421b Thanks @cevr! - Validate deferred Machine.deferReply values with the event reply schema before settling ActorRef.ask. If state exit interrupts reply decoding, settle the waiting caller with that interruption instead of leaving it pending.

0.25.2

Patch Changes

  • #69 c53b0a9 Thanks @cevr! - Validate the package against Effect 4.0.0-rc.112 and the current Effect toolchain.

0.25.1

Patch Changes

  • #67 e183c26 Thanks @cevr! - Wait for the old generation resources to close before a supervised actor restarts.

0.25.0

Minor Changes

  • #65 57c91bd Thanks @cevr! - Expose a narrow synchronous client to machine-owned Effects. Bind each background resource to its actor generation.

0.24.6

Patch Changes

  • #60 9a3510f Thanks @cevr! - Allow nested actors to start before their parent spawn returns.

0.24.5

Patch Changes

  • #58 6c19e68 Thanks @cevr! - Commit an entered state before its state Effect starts.

0.24.4

Patch Changes

  • #56 623b906 Thanks @cevr! - Commit synchronous client transitions before ActorClient.send returns and notify Actor Atom subscribers without a timer delay.

0.24.3

Patch Changes

  • #53 69d423c Thanks @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.

0.24.2

Patch Changes

  • #50 1602aff Thanks @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.

0.24.1

Patch Changes

  • #48 4b8523d Thanks @cevr! - Ensure actors spawned through an ActorSystem use that same system for child actors and inspection.

0.24.0

Minor Changes

  • #46 41d00a1 Thanks @cevr! - Replace the Inspector Hub with typed inspect spawn options and late system-wide registration through actor.system.inspect.

0.23.0

Minor Changes

  • #44 1615eea Thanks @cevr! - Expose the actor-owned current state and latest accepted transition refs on typed machine self references for machine-lifetime background observers.

0.22.0

Minor Changes

  • #41 fd317fd Thanks @cevr! - Add a dynamic Inspector hub for lazy inspection consumers.

0.21.0

Minor Changes

  • #38 5cf9b40 Thanks @cevr! - Add typed ActorSystem keys, per-ID actor generation streams, and Suspense-ready Actor Atoms for named actor acquisition.

0.20.0

Minor Changes

  • #34 c0b6845 Thanks @cevr! - Add .when() transitions with Boolean or Effect predicates, reactive guard-aware can, a non-Effect actor.client facade, Effectful transition handlers with a never error contract, stable eventless transitions, typed machine input and output, scoped Machine.run composition, 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.

0.19.0

Minor Changes

  • #31 a3c1e46 Thanks @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.

0.18.0

Minor Changes

  • 76fcba1 Thanks @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.

  • 751862b Thanks @cevr! - Upgrade to effect 4.0.0-beta.102 and raise the peer floor to >=4.0.0-beta.102.

Patch Changes

  • f7ae688 Thanks @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.

  • 5704c1a Thanks @cevr! - Construct schema-tagged errors through .make instead of new, model finite numeric schema fields with Schema.Finite, and route an unhandled NoReplyError in the cluster entity loop through Effect.orDie.

  • 52ec1f6 Thanks @cevr! - Validate slot codecs through the Effect error channel and route inspector callbacks through Effect.try, so schema mismatches and throwing inspectors no longer rely on try/catch.

0.17.1

Patch Changes

  • 99fcb3f Thanks @cevr! - Update the Effect v4 toolchain to effect@4.0.0-beta.64 and run tsdown through Bun so CI and local builds share Bun's package resolution.

0.17.0

Minor Changes

  • 5e344f7 Thanks @cevr! - Rename derive to with on state/event schemas. Union-level with accepts shared fields without casts on generic type parameters. Add .schema (branded) accessor to MachineStateSchema. Remove .plain — use constructors in tests instead of raw object literals. Add Slot.of(slotsSchema, provided) to convert ProvideSlots into SlotCalls without consumer-side casts.

Patch Changes

  • 79a9311 Thanks @cevr! - Modernize the repository toolchain around @effect/tsgo, the latest Effect beta, and type-aware oxlint.

    effect is now peer-only at runtime, v4 service tags use the Context.Service class form with serviceNotAsClass enabled, and the v3 mirror is validated through the same tsgo gate.

0.16.0

Minor Changes

  • d17665e Thanks @cevr! - Replace ambient Scope detection with explicit ActorScope service

    • Machine.spawn and system.spawn no longer attach cleanup finalizers to ambient Scope.Scope. This fixes a bug where unrelated scopes would unexpectedly tear down actors.
    • New ActorScope service tag — when present in context, actors attach stop finalizers to it.
    • New Machine.scoped(effect) helper bridges Scope.Scope → ActorScope for opt-in auto-cleanup.
    • Backport call improvements to v3: warning log on stopped actor, complete ProcessEventResult fields with satisfies for type safety.
    • Fix bun test tsconfig resolution and add test:all / v3 tests to gate.
  • ec8ccd3 Thanks @cevr! - Upgrade to effect 4.0.0-beta.47, require tsgo for type checking

    Breaking: Minimum peer dependency is now effect@>=4.0.0-beta.47. The ServiceMap module was removed upstream — all ServiceMap.Service usages are now Context.Service.

    • Rename ServiceMap.Service → Context.Service throughout
    • Rename Effect.services() → Effect.context()
    • Switch type checker from tsc to tsgo (native Go compiler via @typescript/native-preview)
    • Switch Effect LSP from tsconfig plugin patch to effect-language-service diagnostics CLI
    • Simplify tsconfig.json for TypeScript 6 defaults

0.15.2

Patch Changes

  • 0c81fd3 Thanks @cevr! - fix(v3): backport reply metadata stripping from event constructor payloads

    Event.reply(...) constructor payload typing no longer leaks reply schema metadata into user payload arguments.

0.15.1

Patch Changes

  • 612e686 Thanks @cevr! - Fix Event.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

0.15.0

Minor Changes

  • d518ed6 Thanks @cevr! - Cold spawn + Recovery/Durability lifecycle API (v3 backport).

    Breaking changes:

    • Machine.spawn now returns an unstarted actor. Call yield* actor.start to fork the event loop, background effects, and spawn effects. Events sent before start() are queued.
    • system.spawn auto-starts — no change needed for registry-based spawns.
    • PersistConfig<S> is removed. Use Lifecycle<S, E> instead.

    New APIs:

    • ActorRef.start — idempotent Effect that starts the actor
    • Recovery<S> — resolves initial state per generation during actor.start
    • RecoveryContext<S> — { actorId, generation, machineInitial }
    • Durability<S, E> — saves state after committed transitions
    • DurabilityCommit<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.resolve merges load() + onRestore() into one callback
    • Recovery.resolve receives RecoveryContext with actorId, generation (0 = cold start, 1+ = supervision restart), and machineInitial
    • Durability.save receives DurabilityCommit with full transition context (previous state, next state, event, generation)
    • Recovery runs during actor.start, not during allocation
    • hydrate option overrides recovery entirely (resolve is never called)

0.14.0

Minor Changes

  • ff7fd8f Thanks @cevr! - Unified slot system redesign + local persistence + slot schemas.

    Breaking changes (pre-1.0):

    • Slot.Guards / Slot.Effects replaced by Slot.define + Slot.fn
    • guards: / effects: on Machine.make replaced by single slots: field
    • Slot handlers take only params — no ctx parameter. Use yield* machine.Context for machine state.
    • HandlerContext.guards / HandlerContext.effects replaced by HandlerContext.slots
    • StateHandlerContext.effects replaced by StateHandlerContext.slots
    • Removed: SlotContext, GuardsDef, EffectsDef, GuardSlot, EffectSlot, GuardHandlers, EffectHandlers, HasGuardKeys, HasEffectKeys
    • SlotProvisionError.slotType is now "slot" only (was "guard" | "effect" | "slot")
    • Machine.spawn slots option is now ProvideSlots<SD> (type-checked, was Record<string, any>)

    New APIs:

    • Slot.fn(fields, returnSchema?) — define a slot with typed params and arbitrary return type
    • Slot.define({ ... }) — create a slots schema from slot definitions
    • SlotFnDef.inputSchema / outputSchema — materialized schemas for runtime validation and serialization
    • SlotsSchema.requestSchema / resultSchema / invocationSchema — wire-format schemas for RPC and persistence
    • slotValidation option on Machine.make — runtime input/output validation (default: true)
    • SlotCodecError — tagged error for validation failures (raised as defect)
    • PersistConfig<S> — local persistence for Machine.spawn:
      • load() → hydrate from storage
      • save(state) → save after transitions
      • shouldSave?(state, prev) → filter saves
      • onRestore?(state, { initial }) → recovery decision hook
    • ActorSystem.spawn now accepts slots and persist options
    • Multi-state .spawn() and .task() overloads (array of states)
    • .task() shorthand — omit onSuccess when task returns Event directly

    Internal:

    • resolveActorSystem() and runSupervisionLoop() extracted from createActor
    • Queue.clear replaces manual poll loop for shutdown drain
    • Plain-object return bug fixed in slot resolve (uses Effect.isEffect)
    • materializeMachine threads _slotValidation through copies

Patch Changes

  • 8e8a9ce Thanks @cevr! - - feat: union-level derive on State and Event schemas — dispatches by _tag, preserves specific variant subtype
    • fix: derive partial keys not in target variant are now silently dropped
    • fix: .task() onSuccess is now optional — omit when task returns Event directly

0.13.0

Minor Changes

  • 74e3fea Thanks @cevr! - Add actor supervision with automatic restart on defect.

    New APIs:

    • Supervision.restart({ maxRestarts, within, backoff }) — Schedule-based restart policy
    • Supervision.none — no supervision (default, crashes are terminal)
    • actor.awaitExit — resolves with ActorExit<S> when actor terminally stops
    • actor.watch(other) — now returns ActorExit<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() returns Effect<ActorExit<unknown>> instead of Effect<void>
    • SystemEvent.ActorStopped gains exit: ActorExit<unknown> field
    • New SystemEvent.ActorRestarted variant

    Internal:

    • Runtime kernel split: cell-owned resources, actorScope, exitDeferred
    • Background/spawn/transition defect detection with DefectPhase tagging
    • Generation owner fiber for actorScope lifecycle
    • Effect.runForkWith for proper service propagation (v4)
    • globalXInEffect diagnostics enabled in tsconfig

0.12.0

Minor Changes

  • f26b21f Thanks @cevr! - feat(cluster): entity persistence with snapshot and journal strategies

    Add 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).

  • 33d8a87 Thanks @cevr! - feat: add typed reply schemas for ask()

    • Event.reply(fields, schema) — declare reply-bearing events with schema validation
    • Machine.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: Ask RPC propagates replies through cluster boundary
    • Backported to v3

0.11.0

Minor Changes

  • 6bdee0c Thanks @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, VersionConflictError
    • PersistentMachine, PersistentActorRef, PersistenceConfig
    • createPersistentActor, restorePersistentActor, isPersistentMachine
    • InMemoryPersistenceAdapter, makeInMemoryPersistenceAdapter
    • Machine.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 })

Patch Changes

  • 3ff2dfb Thanks @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.

0.10.0

Minor Changes

  • 921e063 Thanks @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.

  • eee2ff4 Thanks @cevr! - Backport all v4 features to Effect v3 variant + restructure into v3/ directory

    Restructure:

    • 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 } return
    • ActorRef.sync namespace — replaces flat sendSync/stopSync/snapshotSync/matchesSync/canSync
    • .timeout() builder — gen_statem-style state timeouts
    • .postpone() builder — gen_statem-style event postpone with drain-until-stable
    • hasReply structural flag on ProcessEventResult
    • ActorStoppedError / NoReplyError error types
    • makeInspectorEffect / combineInspectors / tracingInspector inspection helpers
    • actorId threaded through all handler contexts

    Bug fix:

    • State.derive() now guards against _tag override in partial argument

0.9.0

Minor Changes

  • 6e3497b Thanks @cevr! - Add dispatch and dispatchPromise to ActorRef — synchronous event processing with transition receipts.

    dispatch(event) — Effect-based. Sends event through the queue (preserving serialization) and returns ProcessEventResult<State> with { transitioned, previousState, newState, lifecycleRan, isFinal }. OTP gen_server:call equivalent.

    dispatchPromise(event) — Promise-based. Same semantics as dispatch for use at non-Effect boundaries (React event handlers, framework hooks, tests).

    Also exports ProcessEventResult from the public API.

0.8.0

Minor Changes

  • 675f461 Thanks @cevr! - Add effectful inspectors, scoped from(...) transitions, and named task inspection events.

Patch Changes

  • ac8f691 Thanks @cevr! - Update Effect dependency to 4.0.0-beta.35

0.7.2

Patch Changes

  • f6c5bbe Thanks @cevr! - Update Effect dependency to 4.0.0-beta.26

0.7.1

Patch Changes

  • fa54c61 Thanks @cevr! - Update Effect dependency to 4.0.0-beta.21
    • Replace Effect.makeSemaphore with Semaphore.make (removed in beta.6)

0.7.0

Minor Changes

  • 34c85b8 Thanks @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

0.6.0

Minor Changes

  • 9d5bd6f Thanks @cevr! - Add actor.children to expose child actors spawned via self.spawn

    • actor.children returns ReadonlyMap<string, ActorRef> of children spawned via self.spawn
    • State-scoped children auto-removed from map on state exit
    • Works for both regular and persistent actors
  • 9d5bd6f Thanks @cevr! - Add observable ActorSystem with event stream and sync observation

    • system.subscribe(fn) — sync callback for ActorSpawned / ActorStopped events, returns unsubscribe
    • system.actors — sync snapshot of all registered actors (ReadonlyMap)
    • system.events — async Stream<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 duplicate ActorStopped

0.5.0

Minor Changes

  • 28835c6 Thanks @cevr! - Add observable ActorSystem and wire up actor.children for persistent actors

    ActorSystem observation:

    • system.subscribe(fn) — sync callback for ActorSpawned / ActorStopped events, returns unsubscribe
    • system.actors — sync snapshot of all registered actors (ReadonlyMap)
    • system.events — async Stream<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 duplicate ActorStopped

    actor.children:

    • Wire up childrenMap in persistent actors so actor.children reflects self.spawn children
    • Children auto-removed from map on scope close (state-scoped cleanup)

0.4.0

Minor Changes

  • bd3953e Thanks @cevr! - Add child actor support: self.spawn() from handlers, actor.system on every ActorRef, implicit system creation for Machine.spawn, and automatic lifecycle coupling to state scope

0.3.2

Patch Changes

  • a16c44e Thanks @cevr! - Add tsdown build step. Library now ships pre-built ESM with .d.ts declarations instead of raw TypeScript source.

0.3.1

Patch Changes

  • Add stopSync to ActorRef — fire-and-forget stop for sync contexts (framework cleanup hooks, event handlers).

0.3.0

Minor Changes

  • 0154ac9 Thanks @cevr! - feat: DX overhaul — multi-state .on(), State.derive(), .onAny(), waitFor deadlock 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.
    • waitFor deadlock fix: Rewrote to use sync listeners + Deferred instead of SubscriptionRef.changes stream, preventing semaphore deadlock on synchronous transitions
    • sendSync: 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) and actor.sendAndWait(event, State.Done)
    • .build() / BuiltMachine: Terminal builder method — .provide() renamed to .build(), returns BuiltMachine. .validate() removed. Machine.spawn and ActorSystem.spawn accept BuiltMachine. No-slot machines: .build() with no args.
  • 365da12 Thanks @cevr! - feat: remove Scope.Scope from Machine.spawn and system.spawn signatures

    • Scope-optional spawn: Machine.spawn and ActorSystem.spawn no longer require Scope.Scope in R. Both detect scope via Effect.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 by actor.stop.
    • System-level cleanup: ActorSystem layer teardown stops all registered actors automatically. ActorSystemDefault is now Layer.scoped.
    • Breaking: Callers that relied on Scope.Scope appearing in the R type of spawn may need type adjustments. Effect.scoped wrappers around spawn are no longer required but still work (scope detection finds them).

0.2.4

Patch Changes

  • d29f2ff Thanks @cevr! - feat: makeInspector accepts Schema constructors as type params

    makeInspector<typeof MyState, typeof MyEvent>(cb) now auto-extracts .Type from schema constructors via ResolveType. No need for typeof MyState.Type anymore.

0.2.3

Patch Changes

  • 85a2854 Thanks @cevr! - fix: use forkScoped for event loop fiber to prevent premature interruption

    Changed Effect.fork to Effect.forkScoped for 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 provided Scope.Scope service, allowing callers to control the actor's lifecycle explicitly.

0.2.2

Patch Changes

  • 2bcb0bb Thanks @cevr! - Add AnyInspectionEvent type alias and default generic params on makeInspector/consoleInspector/collectingInspector so untyped inspectors work without explicit casts.

  • c50c3ce Thanks @cevr! - Fix waitFor race condition where a fast state transition between get and changes subscription could cause waitFor to hang forever. Now uses stateRef.changes directly which emits the current value atomically as its first element.

0.2.1

Patch Changes

  • 639ee87 Thanks @cevr! - Fix PersistenceAdapterTag key for deterministic key diagnostics and align language-service config.

0.2.0

Minor Changes

  • f4a8c8f Thanks @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
  • a77f9fa Thanks @cevr! - Add Machine.task, ActorRef wait/await helpers, and error inspection events.

  • 3172971 Thanks @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.