Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 0 additions & 15 deletions .changeset/atom-send-buffering.md

This file was deleted.

13 changes: 0 additions & 13 deletions .changeset/hydration-safe-machine-state.md

This file was deleted.

9 changes: 0 additions & 9 deletions .changeset/invoke-error-channels-and-diagnostics.md

This file was deleted.

15 changes: 0 additions & 15 deletions .changeset/no-conditional-none-branch.md

This file was deleted.

21 changes: 0 additions & 21 deletions .changeset/scope-bound-machine-start.md

This file was deleted.

7 changes: 0 additions & 7 deletions .changeset/server-safe-machine-atoms.md

This file was deleted.

13 changes: 0 additions & 13 deletions .changeset/update-into-choice-states.md

This file was deleted.

11 changes: 11 additions & 0 deletions packages/devtools/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion packages/devtools/package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand Down
23 changes: 23 additions & 0 deletions packages/effect-machine-react/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
<MachineState machine={machine} path="Editing" pending={<Spinner />}>
{(editing) => <input value={editing.value.email} onChange={onChange} />}
</MachineState>
```

### 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
Expand Down
2 changes: 1 addition & 1 deletion packages/effect-machine-react/package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand Down
56 changes: 56 additions & 0 deletions packages/effect-machine/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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<typeof CounterMachineAtom>;
type Event = AtomMachine.MachineAtom.Event<typeof CounterMachineAtom>;
```

- 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
Expand Down
2 changes: 1 addition & 1 deletion packages/effect-machine/package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand Down
16 changes: 16 additions & 0 deletions packages/oxlint-plugin/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion packages/oxlint-plugin/package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand Down
Loading