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
7 changes: 0 additions & 7 deletions .changeset/bright-trees-listen.md

This file was deleted.

5 changes: 0 additions & 5 deletions .changeset/calm-events-simulate.md

This file was deleted.

5 changes: 0 additions & 5 deletions .changeset/calm-tools-organize.md

This file was deleted.

6 changes: 0 additions & 6 deletions .changeset/fair-tools-align.md

This file was deleted.

7 changes: 0 additions & 7 deletions .changeset/young-machines-watch.md

This file was deleted.

21 changes: 21 additions & 0 deletions packages/devtools/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# @typeonce/effect-machine-devtools

## 0.23.0

### Minor Changes

- f90b37d: Add a local interactive text visualizer prototype that renders the public machine inspection data as a collapsible tree.

Use the text tree to navigate topology, expand nested states, select subtrees, and inspect structured machine details without converting the model into a chart.

- f90b37d: Add `MachineSimulator` and browser controls for side-effect-free, best-effort topology simulation. Direct required transitions advance the active tree; runtime-dependent transitions remain visibly indeterminate instead of executing user code or guessing.
- f90b37d: Add a local `effect-machine` command that discovers exported `.handle(...)` machines, keeps their last valid inspection document across incomplete reloads, and serves the live interactive text visualizer.

Native file-system events are used by default. Pass `--watch-polling` on platforms where native events are unavailable.

### Patch Changes

- f90b37d: Release `@typeonce/effect-machine` and `@typeonce/effect-machine-devtools` at the same version. Install matching versions so the devtools inspection protocol and machine model remain compatible.
- Updated dependencies [f90b37d]
- Updated dependencies [f90b37d]
- @typeonce/effect-machine@0.23.0
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.22.0",
"version": "0.23.0",
"description": "Local development tools for Effect Machine",
"author": "Sandro Maglione",
"repository": {
Expand Down
111 changes: 61 additions & 50 deletions packages/effect-machine/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# @typeonce/effect-machine

## 0.23.0

### Patch Changes

- f90b37d: Move the published package into an Effect-style workspace without changing its public exports.
- f90b37d: Release `@typeonce/effect-machine` and `@typeonce/effect-machine-devtools` at the same version. Install matching versions so the devtools inspection protocol and machine model remain compatible.

## 0.22.0

### Minor Changes
Expand All @@ -18,7 +25,7 @@
target
.from({ request })
.update(owner.decoded(new Ready({ ...current, notice: null })))
)
);
```

Transition inspection and retained microsteps now include an `updates` array naming replaced owners.
Expand Down Expand Up @@ -101,9 +108,9 @@
from
.effect("load", () => loadUser())
.onDone((to) => to.full.Ready())
.onFailure((to) => to.full.Failed())
}
})
.onFailure((to) => to.full.Failed()),
},
});
```

Return an array of completed chains for multiple activities. Sources and child descriptors remain reusable, while keeping the invocation declaration local preserves exact owner-state, event, parent, output, failure, element, snapshot, and service inference.
Expand Down Expand Up @@ -136,10 +143,12 @@
.branches({
running: { target: to.full.Running() },
done: { target: to.full.Done() },
unchanged: { target: to.none }
unchanged: { target: to.none },
})
.resolve(({ event, select }) => event.cached ? select.done.from() : select.running.from())
}
.resolve(({ event, select }) =>
event.cached ? select.done.from() : select.running.from()
),
};
```

Use `.reenter()` for resolver-free reentry, or pass literal `declinable: true` to `.resolve(...)` when the resolver must receive `decline()`. Bare targets are accepted only when their schemas support default construction.
Expand Down Expand Up @@ -168,13 +177,13 @@
Machine.transition({
branches: (to) => ({
moving: { target: to.local.Running() },
unchanged: { target: to.none() }
unchanged: { target: to.none() },
}),
resolve: ({ event, select }) =>
event.axis === 0
? select.unchanged()
: select.moving.from({ startedAt: event.at })
})
: select.moving.from({ startedAt: event.at }),
});
```

Branch keys are stable inspection, visualization, trace-verification, and coverage identities. Optional branch titles remain presentation metadata.
Expand Down Expand Up @@ -235,14 +244,14 @@
title: "cached",
when: ({ event }) => event.cached,
target: (to) => to.full.Ready(),
resolve: ({ match, target }) => target.from({ data: match })
})
resolve: ({ match, target }) => target.from({ data: match }),
}),
],
otherwise: {
target: (to) => to.full.Loading(),
resolve: ({ target }) => target.from()
}
})
resolve: ({ target }) => target.from(),
},
});
```

Replace each object previously written directly in the `cases` array with `branch({ ... })` inside the `cases: (branch) => [...]` factory. Direct transitions and `otherwise` keep their existing shape.
Expand All @@ -262,8 +271,8 @@
Machine.invoke({
id: "load",
effect: () => load,
onDone: ({ output, target }) => target.none()
})
onDone: ({ output, target }) => target.none(),
});
```

- 34a9a26: Add typed declared-initial entry to compound and parallel transition targets.
Expand All @@ -277,15 +286,17 @@
The hot Effect `Stream` observes ordered creation, initialization, mailbox delivery and processing, state changes, emissions, Effect and timer activities, and termination for a prepared root and all locally owned descendants:

```ts
const prepared = yield * Machine.prepare(machine)
const prepared = yield * Machine.prepare(machine);

yield *
prepared.inspection.pipe(
Stream.runForEach((event) => Console.log(event.sequence, event.subject.id, event._tag)),
Stream.runForEach((event) =>
Console.log(event.sequence, event.subject.id, event._tag)
),
Effect.forkScoped({ startImmediately: true })
)
);

const ref = yield * prepared.start
const ref = yield * prepared.start;
```

Inspection is non-replayed, never fails, and completes with the root. Its session ids and ordering are local to one prepared ownership tree; distributed identity and delivery remain an Effect Cluster concern.
Expand All @@ -297,11 +308,11 @@
- 9798994: Rename the minimal inter-machine reference types so they use machine terminology and remain distinct from Effect Cluster concepts.

```ts
Machine.ActorRef<Event> // before
Machine.MachineTarget<Event> // after
Machine.ActorRef<Event>; // before
Machine.MachineTarget<Event>; // after

Machine.ActorContext<InputEvents, ParentEvents> // before
Machine.MachineReferences<InputEvents, ParentEvents> // after
Machine.ActorContext<InputEvents, ParentEvents>; // before
Machine.MachineReferences<InputEvents, ParentEvents>; // after
```

The inferred `self` and `parent` fields and all runtime behavior are unchanged.
Expand All @@ -313,13 +324,13 @@
- 2a84cd2: Add `Machine.prepare` for composing snapshot and emission streams before a machine initializes, while keeping `Machine.start` as the one-step convenience.

```ts
const prepared = yield * Machine.prepare(machine)
const prepared = yield * Machine.prepare(machine);
yield *
prepared.emissions.pipe(
Stream.runForEach(handleEmission),
Effect.forkScoped({ startImmediately: true })
)
const ref = yield * prepared.start
);
const ref = yield * prepared.start;
```

AtomMachine emission streams use the same preparation boundary, and machine definitions now expose `definition.invoke(...)` so invocation `self` and `parent` references use the exact public input and `parentEvents` protocols.
Expand All @@ -331,15 +342,15 @@
- b7004c2: Make `Machine.events` and `Machine.internalEvents` definition-time protocol descriptors that are passed directly to `Machine.make`. The descriptors expose type-safe deferred constructors while retaining their schemas privately, so applications can export the event API without exporting schemas or reaching for throwing schema `.make` methods.

```ts
const Events = Machine.events(PublicEvent)
const InternalEvents = Machine.internalEvents(InternalEvent)
const Events = Machine.events(PublicEvent);
const InternalEvents = Machine.internalEvents(InternalEvent);

const machine = Machine.make({
states: States.states,
events: Events,
internalEvents: InternalEvents,
initial: () => States.initial.Idle.from()
})
initial: () => States.initial.Idle.from(),
});
```

Remove the eager schema-based `Machine.event` constructor. Pass complete decoded event objects directly to APIs that intentionally retain values, such as manual model-testing scenarios or transport messages.
Expand All @@ -349,30 +360,30 @@
Remove `Machine.retag`. To reuse compatible fields across sibling states, destructure away the source discriminator and construct the destination through its target builder:

```ts
const { _tag: _, ...fields } = state
return target.local.Saving.from({ ...fields, attempt: 1 })
const { _tag: _, ...fields } = state;
return target.local.Saving.from({ ...fields, attempt: 1 });
```

- 62e2281: Separate actor inputs from outward notifications. Declare emissions with `Machine.emittedEvents`, publish them with `emit`, and observe the hot, non-replaying `MachineRef.emissions` stream. Children declare the public inputs they expect from their owner through `parentEvents`, then communicate explicitly with the typed, optional `parent` actor reference:

```ts
const Emissions = Machine.emittedEvents(Progress)
const ParentEvents = Machine.events(Completed)
const Emissions = Machine.emittedEvents(Progress);
const ParentEvents = Machine.events(Completed);

const worker = Machine.make({
// ...
emittedEvents: Emissions,
parentEvents: ParentEvents
parentEvents: ParentEvents,
}).handle({
Working: {
entry: ({ parent }, enqueue) => {
enqueue.emit(Emissions.Progress({ value: 0.5 }))
enqueue.emit(Emissions.Progress({ value: 0.5 }));
if (parent !== undefined) {
enqueue.sendTo(parent, ParentEvents.Completed({ value: 42 }))
enqueue.sendTo(parent, ParentEvents.Completed({ value: 42 }));
}
}
}
})
},
},
});
```

Handler contexts also expose typed `self`; invoked-child composition checks that every `parentEvents` case is accepted by the parent. This release renames structural handler ancestry to `containingState` and `ancestors`, supports zero-payload event and emission constructors with `()`, and exposes root and child emission streams through AtomMachine.
Expand Down Expand Up @@ -401,11 +412,11 @@
const States = Machine.defineStates({
Form: {
initial: "Editing",
states: { Editing: {}, Saving }
}
})
states: { Editing: {}, Saving },
},
});

States.initial.Form.from((form) => form.Editing.from())
States.initial.Form.from((form) => form.Editing.from());
```

## 0.7.0
Expand All @@ -418,17 +429,17 @@
earlier instead of requiring the complete root snapshot.

```ts
const readySnapshot = States.getSnapshot(snapshot, "Ready")
const readySnapshot = States.getSnapshot(snapshot, "Ready");

if (Option.isSome(readySnapshot)) {
States.get(readySnapshot.value, "Ready.editor")
States.matches(readySnapshot.value, "Ready.editor.Editing")
States.get(readySnapshot.value, "Ready.editor");
States.matches(readySnapshot.value, "Ready.editor.Editing");
}

const editorSnapshotAtom = AtomMachine.selectSnapshot(
machineAtom,
"Ready.editor"
)
);
```

Add equality-aware `AtomMachine.selectSnapshot` and
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.22.0",
"version": "0.23.0",
"description": "Schema-first state machines and statecharts for Effect",
"author": "Sandro Maglione",
"repository": {
Expand Down