From 1ea34a3977d602ce541b6a4fc40e22bce2b4068d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 22 Oct 2026 10:57:51 +0000 Subject: [PATCH] Version Packages --- .changeset/atom-send-buffering.md | 15 ----- .changeset/hydration-safe-machine-state.md | 13 ----- .../invoke-error-channels-and-diagnostics.md | 9 --- .changeset/no-conditional-none-branch.md | 15 ----- .changeset/scope-bound-machine-start.md | 21 ------- .changeset/server-safe-machine-atoms.md | 7 --- .changeset/update-into-choice-states.md | 13 ----- packages/devtools/CHANGELOG.md | 11 ++++ packages/devtools/package.json | 2 +- packages/effect-machine-react/CHANGELOG.md | 23 ++++++++ packages/effect-machine-react/package.json | 2 +- packages/effect-machine/CHANGELOG.md | 56 +++++++++++++++++++ packages/effect-machine/package.json | 2 +- packages/oxlint-plugin/CHANGELOG.md | 16 ++++++ packages/oxlint-plugin/package.json | 2 +- 15 files changed, 110 insertions(+), 97 deletions(-) delete mode 100644 .changeset/atom-send-buffering.md delete mode 100644 .changeset/hydration-safe-machine-state.md delete mode 100644 .changeset/invoke-error-channels-and-diagnostics.md delete mode 100644 .changeset/no-conditional-none-branch.md delete mode 100644 .changeset/scope-bound-machine-start.md delete mode 100644 .changeset/server-safe-machine-atoms.md delete mode 100644 .changeset/update-into-choice-states.md diff --git a/.changeset/atom-send-buffering.md b/.changeset/atom-send-buffering.md deleted file mode 100644 index bec64984..00000000 --- a/.changeset/atom-send-buffering.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -"@typeonce/effect-machine": minor ---- - -Deliver `send` and `stop` writes made while an atom machine is still starting. Writes to `MachineAtom.send`, `MachineAtom.stop`, `ChildMachineAtom.send` and `ChildMachineAtom.stop` before readiness are now held and delivered in write order once the machine is ready, instead of being dropped. The write result stays waiting until delivery and fails with the startup error if startup fails. Writes after the machine stopped still fail with `StoppedError`, and child writes still fail with `ChildNotActiveError` when the owning state is inactive. - -`NotReadyError` is removed from `AtomMachine` and from the `send` and `stop` failure types. Remove any handling of it. - -Add `AtomMachine.MachineAtom.Any` and `AtomMachine.ChildMachineAtom.Any` for code that accepts any bridge, including bridges for machines without events, and the `MachineAtom.State`, `MachineAtom.Event` and `ChildMachineAtom.State` extractors: - -```ts -type Bridge = AtomMachine.MachineAtom.Any -type State = AtomMachine.MachineAtom.State -type Event = AtomMachine.MachineAtom.Event -``` diff --git a/.changeset/hydration-safe-machine-state.md b/.changeset/hydration-safe-machine-state.md deleted file mode 100644 index c9b5cf0c..00000000 --- a/.changeset/hydration-safe-machine-state.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -"@typeonce/effect-machine-react": minor ---- - -`MachineState` no longer suspends. Server rendering and hydration render the machine's planned startup snapshot, and the same tree then receives live snapshots, so hydrated form inputs keep their DOM nodes, typed text, and focus. Remove Suspense boundaries that only wrapped `MachineState` for its first paint. - -Add the optional `pending` prop, rendered until the machine has a snapshot. Once a snapshot has rendered, `MachineState` keeps it while the machine is still starting, for example while a bound runtime layer builds after hydration: - -```tsx -}> - {(editing) => } - -``` diff --git a/.changeset/invoke-error-channels-and-diagnostics.md b/.changeset/invoke-error-channels-and-diagnostics.md deleted file mode 100644 index 323342b8..00000000 --- a/.changeset/invoke-error-channels-and-diagnostics.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@typeonce/effect-machine": minor ---- - -`ChildAlreadyExistsError` now appears in a machine's error type only for invocations whose child id you choose: logic invocations with an `address` and `Machine.child` invocations. Effect, Stream, and timer invocations run under runtime-owned ids and cannot collide, so a machine that only invokes them no longer carries `ChildAlreadyExistsError` in `Machine.Error`. Invocation ids within one state must be distinct; a repeated source without distinct `id`s or a reused `id` is now a type error and is rejected when the machine is constructed. - -A parent that invokes a fixed child through `children` now needs `onFailure` exactly when the child's own error type is reachable. Remove `onFailure: { none: true }` handlers that only swallowed `ChildAlreadyExistsError`; they are now rejected as unreachable, like an unreachable `onDone`. - -Constraint failures now explain themselves instead of reporting `never`. Starting, resuming, preparing, or planning a machine with a required parent reports `Machine declares a required parent and cannot run as a root`, and an unreachable `onDone`, `onFailure`, `onElement`, or `onSnapshot` names the channel and source, for example `Invocation 'forever' never completes, so onDone is unreachable`. diff --git a/.changeset/no-conditional-none-branch.md b/.changeset/no-conditional-none-branch.md deleted file mode 100644 index 70559bcb..00000000 --- a/.changeset/no-conditional-none-branch.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -"@typeonce/oxlint-plugin-effect-machine": minor ---- - -Add the recommended `effect-machine/no-conditional-none-branch` rule. - -A `{ none: true }` branch accepts its event, so `Machine.can` and `AtomMachine.can` return `true` when a resolver selects it. The rule reports a resolver that selects a `none` branch conditionally without `declinable: true`. When the event must be refused, for example to disable a control, decline it instead: - -```ts -Submitted: { - branches: "submit", - declinable: true, - resolve: ({ state, select, decline }) => state.complete ? select.review() : decline() -} -``` diff --git a/.changeset/scope-bound-machine-start.md b/.changeset/scope-bound-machine-start.md deleted file mode 100644 index 369777b5..00000000 --- a/.changeset/scope-bound-machine-start.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -"@typeonce/effect-machine": minor ---- - -Bind started machines to the caller's `Scope`. - -`Machine.start`, `Machine.resume`, and `prepared.start` now require `Scope.Scope`, like `Effect.forkScoped`. Closing that scope is equivalent to `ref.stop`: active invocations are interrupted, owned children stop, the snapshot becomes `stopped`, and later sends fail with `StoppedError`. A machine that stops or completes earlier releases its place in the scope, so long-lived scopes do not accumulate finalizers. For `prepared.start`, the scope that first evaluates it owns the machine. `AtomMachine` is unaffected. - -Wrap programs that start machines in `Effect.scoped`, or own a scope explicitly when a machine must outlive the caller: - -```ts -const program = Effect.scoped(Effect.gen(function*() { - const ref = yield* Machine.start(machine) - yield* ref.send(Events.Go()) -})) - -const detached = Effect.gen(function*() { - const scope = yield* Scope.make() - return yield* Machine.start(machine).pipe(Scope.provide(scope)) -}) -``` diff --git a/.changeset/server-safe-machine-atoms.md b/.changeset/server-safe-machine-atoms.md deleted file mode 100644 index 412aceae..00000000 --- a/.changeset/server-safe-machine-atoms.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@typeonce/effect-machine": minor ---- - -Reading an `AtomMachine` bridge on the server no longer starts the machine. Every bridge atom now has an Effect Atom server value, so `Atom.getServerValue` and the server render of `@effect/atom-react` hooks return the machine's planned startup state without running startup commands, invocations, or services. - -`result` serves `Success(state)` and `snapshot` serves `Success({ status: "active", state })`, both waiting, where `state` is the state `Machine.planInitial` settles into with the bridge's input. `select`, `selectSnapshot`, `matches`, `can`, and `family` projections serve the same projection of that state. `child(Child)` serves the planned child state when the planned parent state invokes `Child`, and `Option.none()` otherwise. `ref`, `send`, and `stop` serve `Initial`, as does every atom of a bridge whose startup state cannot be planned synchronously. Resumed bridges serve their resumed snapshot. diff --git a/.changeset/update-into-choice-states.md b/.changeset/update-into-choice-states.md deleted file mode 100644 index b5f9f7b0..00000000 --- a/.changeset/update-into-choice-states.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -"@typeonce/effect-machine": minor ---- - -A transition that combines `target` and `update` can now enter a choice state. The owner is updated first, so the choice resolver reads the new value through `containingState`, `ancestors`, or `root` before the chosen branch is entered. With a choice target, `data` constructs only the owner value: - -```ts -Idle: { - on: { Bump: { target: "Route", update: "root", data: ({ root }) => ({ count: root.count + 1 }) } } -} -``` - -A declared branch group can declare the same pair and select it with `select.route({ update: { data } })`. Every branch the choice can reach, including through nested choices, must stay below the updated owner: an escaping branch is a type error such as `Choice 'Route' branch 'out' targets 'Outside' outside the updated owner 'Flow'`, and `handle` rejects it at construction. diff --git a/packages/devtools/CHANGELOG.md b/packages/devtools/CHANGELOG.md index 6966e6f6..137bf5bd 100644 --- a/packages/devtools/CHANGELOG.md +++ b/packages/devtools/CHANGELOG.md @@ -1,5 +1,16 @@ # @typeonce/effect-machine-devtools +## 0.42.0 + +### Patch Changes + +- Updated dependencies [4e9d412] +- Updated dependencies [36b1c0a] +- Updated dependencies [71d41b9] +- Updated dependencies [5eda754] +- Updated dependencies [edebb70] + - @typeonce/effect-machine@0.42.0 + ## 0.41.1 ### Patch Changes diff --git a/packages/devtools/package.json b/packages/devtools/package.json index 7392f698..e0ed46ef 100644 --- a/packages/devtools/package.json +++ b/packages/devtools/package.json @@ -1,6 +1,6 @@ { "name": "@typeonce/effect-machine-devtools", - "version": "0.41.1", + "version": "0.42.0", "description": "Local development tools for Effect Machine", "author": "Sandro Maglione", "repository": { diff --git a/packages/effect-machine-react/CHANGELOG.md b/packages/effect-machine-react/CHANGELOG.md index 23ba99e3..f3ace573 100644 --- a/packages/effect-machine-react/CHANGELOG.md +++ b/packages/effect-machine-react/CHANGELOG.md @@ -1,5 +1,28 @@ # @typeonce/effect-machine-react +## 0.42.0 + +### Minor Changes + +- 5eda754: `MachineState` no longer suspends. Server rendering and hydration render the machine's planned startup snapshot, and the same tree then receives live snapshots, so hydrated form inputs keep their DOM nodes, typed text, and focus. Remove Suspense boundaries that only wrapped `MachineState` for its first paint. + + Add the optional `pending` prop, rendered until the machine has a snapshot. Once a snapshot has rendered, `MachineState` keeps it while the machine is still starting, for example while a bound runtime layer builds after hydration: + + ```tsx + }> + {(editing) => } + + ``` + +### Patch Changes + +- Updated dependencies [4e9d412] +- Updated dependencies [36b1c0a] +- Updated dependencies [71d41b9] +- Updated dependencies [5eda754] +- Updated dependencies [edebb70] + - @typeonce/effect-machine@0.42.0 + ## 0.41.1 ### Patch Changes diff --git a/packages/effect-machine-react/package.json b/packages/effect-machine-react/package.json index bdb1d725..3c505a45 100644 --- a/packages/effect-machine-react/package.json +++ b/packages/effect-machine-react/package.json @@ -1,6 +1,6 @@ { "name": "@typeonce/effect-machine-react", - "version": "0.41.1", + "version": "0.42.0", "description": "React ownership hooks for Effect Machine atoms", "author": "Sandro Maglione", "repository": { diff --git a/packages/effect-machine/CHANGELOG.md b/packages/effect-machine/CHANGELOG.md index a450c4e0..8c17a501 100644 --- a/packages/effect-machine/CHANGELOG.md +++ b/packages/effect-machine/CHANGELOG.md @@ -1,5 +1,61 @@ # @typeonce/effect-machine +## 0.42.0 + +### Minor Changes + +- 4e9d412: Deliver `send` and `stop` writes made while an atom machine is still starting. Writes to `MachineAtom.send`, `MachineAtom.stop`, `ChildMachineAtom.send` and `ChildMachineAtom.stop` before readiness are now held and delivered in write order once the machine is ready, instead of being dropped. The write result stays waiting until delivery and fails with the startup error if startup fails. Writes after the machine stopped still fail with `StoppedError`, and child writes still fail with `ChildNotActiveError` when the owning state is inactive. + + `NotReadyError` is removed from `AtomMachine` and from the `send` and `stop` failure types. Remove any handling of it. + + Add `AtomMachine.MachineAtom.Any` and `AtomMachine.ChildMachineAtom.Any` for code that accepts any bridge, including bridges for machines without events, and the `MachineAtom.State`, `MachineAtom.Event` and `ChildMachineAtom.State` extractors: + + ```ts + type Bridge = AtomMachine.MachineAtom.Any; + type State = AtomMachine.MachineAtom.State; + type Event = AtomMachine.MachineAtom.Event; + ``` + +- 36b1c0a: `ChildAlreadyExistsError` now appears in a machine's error type only for invocations whose child id you choose: logic invocations with an `address` and `Machine.child` invocations. Effect, Stream, and timer invocations run under runtime-owned ids and cannot collide, so a machine that only invokes them no longer carries `ChildAlreadyExistsError` in `Machine.Error`. Invocation ids within one state must be distinct; a repeated source without distinct `id`s or a reused `id` is now a type error and is rejected when the machine is constructed. + + A parent that invokes a fixed child through `children` now needs `onFailure` exactly when the child's own error type is reachable. Remove `onFailure: { none: true }` handlers that only swallowed `ChildAlreadyExistsError`; they are now rejected as unreachable, like an unreachable `onDone`. + + Constraint failures now explain themselves instead of reporting `never`. Starting, resuming, preparing, or planning a machine with a required parent reports `Machine declares a required parent and cannot run as a root`, and an unreachable `onDone`, `onFailure`, `onElement`, or `onSnapshot` names the channel and source, for example `Invocation 'forever' never completes, so onDone is unreachable`. + +- 71d41b9: Bind started machines to the caller's `Scope`. + + `Machine.start`, `Machine.resume`, and `prepared.start` now require `Scope.Scope`, like `Effect.forkScoped`. Closing that scope is equivalent to `ref.stop`: active invocations are interrupted, owned children stop, the snapshot becomes `stopped`, and later sends fail with `StoppedError`. A machine that stops or completes earlier releases its place in the scope, so long-lived scopes do not accumulate finalizers. For `prepared.start`, the scope that first evaluates it owns the machine. `AtomMachine` is unaffected. + + Wrap programs that start machines in `Effect.scoped`, or own a scope explicitly when a machine must outlive the caller: + + ```ts + const program = Effect.scoped( + Effect.gen(function* () { + const ref = yield* Machine.start(machine); + yield* ref.send(Events.Go()); + }) + ); + + const detached = Effect.gen(function* () { + const scope = yield* Scope.make(); + return yield* Machine.start(machine).pipe(Scope.provide(scope)); + }); + ``` + +- 5eda754: Reading an `AtomMachine` bridge on the server no longer starts the machine. Every bridge atom now has an Effect Atom server value, so `Atom.getServerValue` and the server render of `@effect/atom-react` hooks return the machine's planned startup state without running startup commands, invocations, or services. + + `result` serves `Success(state)` and `snapshot` serves `Success({ status: "active", state })`, both waiting, where `state` is the state `Machine.planInitial` settles into with the bridge's input. `select`, `selectSnapshot`, `matches`, `can`, and `family` projections serve the same projection of that state. `child(Child)` serves the planned child state when the planned parent state invokes `Child`, and `Option.none()` otherwise. `ref`, `send`, and `stop` serve `Initial`, as does every atom of a bridge whose startup state cannot be planned synchronously. Resumed bridges serve their resumed snapshot. + +- edebb70: A transition that combines `target` and `update` can now enter a choice state. The owner is updated first, so the choice resolver reads the new value through `containingState`, `ancestors`, or `root` before the chosen branch is entered. With a choice target, `data` constructs only the owner value: + + ```ts + Idle: { + on: { Bump: { target: "Route", update: "root", data: ({ root }) => ({ count: root.count + 1 }) } } + } + ``` + + A declared branch group can declare the same pair and select it with `select.route({ update: { data } })`. Every branch the choice can reach, including through nested choices, must stay below the updated owner: an escaping branch is a type error such as `Choice 'Route' branch 'out' targets 'Outside' outside the updated owner 'Flow'`, and `handle` rejects it at construction. + ## 0.41.1 ### Patch Changes diff --git a/packages/effect-machine/package.json b/packages/effect-machine/package.json index 120bebe6..deeccce2 100644 --- a/packages/effect-machine/package.json +++ b/packages/effect-machine/package.json @@ -1,6 +1,6 @@ { "name": "@typeonce/effect-machine", - "version": "0.41.1", + "version": "0.42.0", "description": "Schema-first state machines and statecharts for Effect", "author": "Sandro Maglione", "repository": { diff --git a/packages/oxlint-plugin/CHANGELOG.md b/packages/oxlint-plugin/CHANGELOG.md index 85c4df72..5987ccb7 100644 --- a/packages/oxlint-plugin/CHANGELOG.md +++ b/packages/oxlint-plugin/CHANGELOG.md @@ -1,5 +1,21 @@ # @typeonce/oxlint-plugin-effect-machine +## 0.42.0 + +### Minor Changes + +- 14e1f39: Add the recommended `effect-machine/no-conditional-none-branch` rule. + + A `{ none: true }` branch accepts its event, so `Machine.can` and `AtomMachine.can` return `true` when a resolver selects it. The rule reports a resolver that selects a `none` branch conditionally without `declinable: true`. When the event must be refused, for example to disable a control, decline it instead: + + ```ts + Submitted: { + branches: "submit", + declinable: true, + resolve: ({ state, select, decline }) => state.complete ? select.review() : decline() + } + ``` + ## 0.41.1 ## 0.41.0 diff --git a/packages/oxlint-plugin/package.json b/packages/oxlint-plugin/package.json index 93a49ab8..45b99caf 100644 --- a/packages/oxlint-plugin/package.json +++ b/packages/oxlint-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@typeonce/oxlint-plugin-effect-machine", - "version": "0.41.1", + "version": "0.42.0", "description": "Oxlint rules for Effect Machine models", "author": "Sandro Maglione", "repository": {