@typeonce/effect-machine 0.18.0 → 0.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -16
- package/dist/Machine.d.ts +300 -30
- package/dist/Machine.d.ts.map +1 -1
- package/dist/Machine.js +10 -6
- package/dist/Machine.js.map +1 -1
- package/dist/internal/machine/cluster.d.ts +2 -2
- package/dist/internal/machine/cluster.d.ts.map +1 -1
- package/dist/internal/machine/cluster.js +2 -1
- package/dist/internal/machine/cluster.js.map +1 -1
- package/dist/internal/machine/serialization.d.ts.map +1 -1
- package/dist/internal/machine/serialization.js +75 -18
- package/dist/internal/machine/serialization.js.map +1 -1
- package/dist/internal/testing/machine/verification.d.ts +1 -1
- package/dist/internal/testing/machine/verification.d.ts.map +1 -1
- package/dist/internal/testing/machine/verification.js +9 -6
- package/dist/internal/testing/machine/verification.js.map +1 -1
- package/dist/testing/MachineTest.d.ts +9 -6
- package/dist/testing/MachineTest.d.ts.map +1 -1
- package/dist/testing/MachineTest.js +5 -3
- package/dist/testing/MachineTest.js.map +1 -1
- package/dist/unstable/cluster/ClusterMachine.d.ts +22 -2
- package/dist/unstable/cluster/ClusterMachine.d.ts.map +1 -1
- package/dist/unstable/cluster/ClusterMachine.js +4 -0
- package/dist/unstable/cluster/ClusterMachine.js.map +1 -1
- package/docs/agent-guide.md +278 -1389
- package/docs/effect-atom-react.md +202 -0
- package/package.json +4 -4
- package/src/Machine.ts +315 -33
- package/src/internal/machine/cluster.ts +4 -1
- package/src/internal/machine/serialization.ts +100 -25
- package/src/internal/testing/machine/verification.ts +15 -10
- package/src/testing/MachineTest.ts +9 -6
- package/src/unstable/cluster/ClusterMachine.ts +51 -1
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ Cluster and are exposed only through explicit integration boundaries.
|
|
|
34
34
|
## Install
|
|
35
35
|
|
|
36
36
|
```sh
|
|
37
|
-
pnpm add @typeonce/effect-machine effect@4.0.0-rc.
|
|
37
|
+
pnpm add @typeonce/effect-machine effect@4.0.0-rc.111
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
`effect` is an exact peer dependency. Install the version above and upgrade it
|
|
@@ -49,11 +49,14 @@ import { Machine } from "@typeonce/effect-machine"
|
|
|
49
49
|
import { Effect, Schema, Stream } from "effect"
|
|
50
50
|
|
|
51
51
|
const State = Schema.TaggedUnion({
|
|
52
|
-
Idle: {},
|
|
53
52
|
Running: { count: Schema.Number }
|
|
54
53
|
})
|
|
55
54
|
|
|
56
|
-
const States = Machine.states(
|
|
55
|
+
const States = Machine.states({
|
|
56
|
+
Idle: {},
|
|
57
|
+
Running: State.cases.Running
|
|
58
|
+
})
|
|
59
|
+
|
|
57
60
|
const CounterEvent = Machine.events(
|
|
58
61
|
Schema.TaggedUnion({
|
|
59
62
|
Start: {},
|
|
@@ -66,7 +69,7 @@ const CounterDefinition = Machine.make({
|
|
|
66
69
|
id: "Counter",
|
|
67
70
|
states: States.states,
|
|
68
71
|
events: CounterEvent,
|
|
69
|
-
initial: (to) => to.Idle()
|
|
72
|
+
initial: (to) => to.Idle()
|
|
70
73
|
})
|
|
71
74
|
|
|
72
75
|
const Counter = CounterDefinition.handle({
|
|
@@ -78,7 +81,7 @@ const Counter = CounterDefinition.handle({
|
|
|
78
81
|
Running: {
|
|
79
82
|
on: {
|
|
80
83
|
Increment: (to) => to.full.Running().resolve(({ state, target }) => target.from({ count: state.count + 1 })),
|
|
81
|
-
Stop: (to) => to.full.Idle()
|
|
84
|
+
Stop: (to) => to.full.Idle()
|
|
82
85
|
}
|
|
83
86
|
}
|
|
84
87
|
})
|
|
@@ -127,6 +130,23 @@ or `Machine.Snapshot<typeof machine>`, schema-backed state payloads as
|
|
|
127
130
|
`Machine.Value<typeof States, Path>`, and path-rooted snapshots as
|
|
128
131
|
`Machine.SnapshotAt<typeof States, Path>`.
|
|
129
132
|
|
|
133
|
+
### Make invalid states unrepresentable
|
|
134
|
+
|
|
135
|
+
Treat topology as a domain contract, not as file organization. A parallel state
|
|
136
|
+
declares the full Cartesian product of its regions, so use it only when every
|
|
137
|
+
combination has a coherent meaning. If one region must inspect another before
|
|
138
|
+
entering a state safely, prefer a compound hierarchy that makes the forbidden
|
|
139
|
+
combination impossible. `matches` remains useful for views, tests, and genuine
|
|
140
|
+
coordination between independent regions; it should not repair an invalid
|
|
141
|
+
state product.
|
|
142
|
+
|
|
143
|
+
Keep state-scoped Effects beneath the state that guarantees their resources,
|
|
144
|
+
and enforce command availability in the machine rather than only by disabling
|
|
145
|
+
UI controls. When entering an inactive compound or parallel state's declared
|
|
146
|
+
default, select `.initial`; explicitly construct descendants only for a
|
|
147
|
+
non-default configuration or a complete replacement of an already-active
|
|
148
|
+
parallel root.
|
|
149
|
+
|
|
130
150
|
### Construct state through builders
|
|
131
151
|
|
|
132
152
|
Use `.from(...)` when constructing a new state from fields:
|
|
@@ -153,7 +173,8 @@ const handlers = {
|
|
|
153
173
|
}
|
|
154
174
|
```
|
|
155
175
|
|
|
156
|
-
Omit `schema` when a state represents control flow but owns no data
|
|
176
|
+
Omit `schema` when a state represents control flow but owns no data. Use `{}`
|
|
177
|
+
instead of defining an empty tagged schema:
|
|
157
178
|
|
|
158
179
|
```ts
|
|
159
180
|
const States = Machine.states({
|
|
@@ -178,6 +199,11 @@ Schema-less states remain active, targetable, matchable, and visible through
|
|
|
178
199
|
their handler `state` is `undefined`, and `get` / `getWithParents` accept only
|
|
179
200
|
schema-backed paths. Add a schema later if the state starts owning data.
|
|
180
201
|
|
|
202
|
+
Keep data-bearing state schemas together in a named `Schema.TaggedUnion` and
|
|
203
|
+
reference its cases from the topology. For a standalone state schema whose
|
|
204
|
+
class identity is useful, declare a named `Schema.TaggedClass`. Do not bury
|
|
205
|
+
one-off tagged schema declarations inside `Machine.states`.
|
|
206
|
+
|
|
181
207
|
Put data on the narrowest state where it is valid. If sibling phases share
|
|
182
208
|
data, put it on their compound parent.
|
|
183
209
|
|
|
@@ -209,7 +235,7 @@ const definition = Machine.make({
|
|
|
209
235
|
events: CommandEvent,
|
|
210
236
|
internalEvents: InternalEvent,
|
|
211
237
|
emittedEvents: Emissions,
|
|
212
|
-
initial: (to) => to.Idle()
|
|
238
|
+
initial: (to) => to.Idle()
|
|
213
239
|
})
|
|
214
240
|
```
|
|
215
241
|
|
|
@@ -321,7 +347,7 @@ const child = Machine.make({
|
|
|
321
347
|
states: ChildStates.states,
|
|
322
348
|
events: ChildEvents,
|
|
323
349
|
parent: Machine.parent(ParentEvents),
|
|
324
|
-
initial: (to) => to.Working()
|
|
350
|
+
initial: (to) => to.Working()
|
|
325
351
|
}).handle({
|
|
326
352
|
Working: {
|
|
327
353
|
on: {
|
|
@@ -372,9 +398,13 @@ paths. `parent` always means the owning machine target.
|
|
|
372
398
|
| `target.history` | Restoring a declared history node | The remembered configuration or its typed default |
|
|
373
399
|
|
|
374
400
|
Every required transition handler selects a target from its inline `to`
|
|
375
|
-
builder.
|
|
376
|
-
|
|
377
|
-
|
|
401
|
+
builder. Return a bare selection such as `to.full.Idle()` when the selected
|
|
402
|
+
builder supports zero-argument construction; the machine applies the same
|
|
403
|
+
default construction as `target.from()`. This includes empty schemas and
|
|
404
|
+
schemas whose constructor fields are all optional or defaulted. TypeScript
|
|
405
|
+
rejects the bare form when state data or nested configuration is required.
|
|
406
|
+
Call `.resolve(...)` when construction depends on handler context or the
|
|
407
|
+
transition needs to enqueue commands. An absent handler ignores the trigger; `to.none` handles
|
|
378
408
|
it and retains queued commands, raised events, and emitted events without
|
|
379
409
|
selecting a destination. Concrete destinations stay narrowed inside their
|
|
380
410
|
resolver, and `to.branches({...})` gives the resolver only the declared named
|
|
@@ -594,6 +624,14 @@ const decoded = yield * Machine.decodeSnapshot(machine, encoded)
|
|
|
594
624
|
const ref = yield * Machine.resume(machine, decoded)
|
|
595
625
|
```
|
|
596
626
|
|
|
627
|
+
Decoded snapshots are local runtime values and may contain class instances or
|
|
628
|
+
other process-local data. `encodeSnapshot` is the persistence boundary: it uses
|
|
629
|
+
each declared schema's canonical JSON codec and succeeds only when every active
|
|
630
|
+
state value, completion output, and history value is JSON. Rich values such as
|
|
631
|
+
dates and bigints use their schema-defined JSON representation; cyclic or
|
|
632
|
+
non-JSON values fail with `MachineSchemaEncodeError` instead of escaping to a
|
|
633
|
+
later `JSON.stringify` crash.
|
|
634
|
+
|
|
597
635
|
Resumption restores logical state, values, completion, and history metadata.
|
|
598
636
|
It creates a fresh runtime: active invokes restart, timers restart at their
|
|
599
637
|
full duration, and prior fibers, subscriptions, queues, and child runtimes are
|
|
@@ -646,11 +684,11 @@ Each ESM entrypoint is independent and tree-shakeable.
|
|
|
646
684
|
Every package directly under [`examples/`](./examples) has its own lockfile and
|
|
647
685
|
`check` script.
|
|
648
686
|
|
|
649
|
-
| Example | What it demonstrates
|
|
650
|
-
| ----------------------------------- |
|
|
651
|
-
| [Playground](./examples/playground) | Five focused React examples: atomic turnstile commands, state-scoped traffic-light timers, microwave safety
|
|
652
|
-
| [Pokémon](./examples/pokemon) | Compound
|
|
653
|
-
| [Platformer](./examples/platformer) | Nested parallel statecharts, typed deep history, raised events, state-scoped timers, deterministic model tests, and a playable SVG adapter
|
|
687
|
+
| Example | What it demonstrates |
|
|
688
|
+
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
689
|
+
| [Playground](./examples/playground) | Five focused React examples: atomic turnstile commands, state-scoped traffic-light timers, hierarchical microwave safety, a resource-owned media player, and a worker-hosted machine synchronized across tabs |
|
|
690
|
+
| [Pokémon](./examples/pokemon) | Compound workflow states, invoked child machines, typed emissions, Atom reactivity, and a live Effect service |
|
|
691
|
+
| [Platformer](./examples/platformer) | Nested parallel statecharts, typed deep history, raised events, state-scoped timers, deterministic model tests, and a playable SVG adapter |
|
|
654
692
|
|
|
655
693
|
The playground is the shortest path from one concept to working code. The
|
|
656
694
|
standalone examples show larger composition and ownership boundaries.
|