@typeonce/effect-machine 0.31.2 → 0.33.0
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 +168 -162
- package/dist/Machine.d.ts +495 -284
- package/dist/Machine.d.ts.map +1 -1
- package/dist/Machine.js +91 -111
- package/dist/Machine.js.map +1 -1
- package/dist/internal/machine/atom.d.ts +3 -4
- package/dist/internal/machine/atom.d.ts.map +1 -1
- package/dist/internal/machine/atom.js +0 -2
- package/dist/internal/machine/atom.js.map +1 -1
- package/dist/internal/machine/childRegistry.d.ts +52 -0
- package/dist/internal/machine/childRegistry.d.ts.map +1 -0
- package/dist/internal/machine/childRegistry.js +74 -0
- package/dist/internal/machine/childRegistry.js.map +1 -0
- package/dist/internal/machine/cluster.d.ts +2 -3
- package/dist/internal/machine/cluster.d.ts.map +1 -1
- package/dist/internal/machine/cluster.js.map +1 -1
- package/dist/internal/machine/configuration.d.ts.map +1 -1
- package/dist/internal/machine/configuration.js +14 -4
- package/dist/internal/machine/configuration.js.map +1 -1
- package/dist/internal/machine/executionPlan.d.ts +1 -0
- package/dist/internal/machine/executionPlan.d.ts.map +1 -1
- package/dist/internal/machine/executionPlan.js +97 -53
- package/dist/internal/machine/executionPlan.js.map +1 -1
- package/dist/internal/machine/invocation.d.ts.map +1 -1
- package/dist/internal/machine/invocation.js +4 -2
- package/dist/internal/machine/invocation.js.map +1 -1
- package/dist/internal/machine/invocationDefinition.d.ts +35 -0
- package/dist/internal/machine/invocationDefinition.d.ts.map +1 -0
- package/dist/internal/machine/invocationDefinition.js +21 -0
- package/dist/internal/machine/invocationDefinition.js.map +1 -0
- package/dist/internal/machine/invocationEvent.d.ts +0 -2
- package/dist/internal/machine/invocationEvent.d.ts.map +1 -1
- package/dist/internal/machine/invocationEvent.js +0 -8
- package/dist/internal/machine/invocationEvent.js.map +1 -1
- package/dist/internal/machine/machine.d.ts +21 -43
- package/dist/internal/machine/machine.d.ts.map +1 -1
- package/dist/internal/machine/machine.js +134 -403
- package/dist/internal/machine/machine.js.map +1 -1
- package/dist/internal/machine/planner.d.ts +13 -19
- package/dist/internal/machine/planner.d.ts.map +1 -1
- package/dist/internal/machine/planner.js +63 -35
- package/dist/internal/machine/planner.js.map +1 -1
- package/dist/internal/machine/process.d.ts +6 -8
- package/dist/internal/machine/process.d.ts.map +1 -1
- package/dist/internal/machine/process.js +1 -0
- package/dist/internal/machine/process.js.map +1 -1
- package/dist/internal/machine/protocol.d.ts +2 -2
- package/dist/internal/machine/protocol.d.ts.map +1 -1
- package/dist/internal/machine/protocol.js +4 -18
- package/dist/internal/machine/protocol.js.map +1 -1
- package/dist/internal/machine/readiness.d.ts +1 -1
- package/dist/internal/machine/readiness.d.ts.map +1 -1
- package/dist/internal/machine/requirements.d.ts +4 -0
- package/dist/internal/machine/requirements.d.ts.map +1 -0
- package/dist/internal/machine/requirements.js +2 -0
- package/dist/internal/machine/requirements.js.map +1 -0
- package/dist/internal/machine/runtime.d.ts +4 -58
- package/dist/internal/machine/runtime.d.ts.map +1 -1
- package/dist/internal/machine/runtime.js +1 -69
- package/dist/internal/machine/runtime.js.map +1 -1
- package/dist/internal/machine/serialization.d.ts.map +1 -1
- package/dist/internal/machine/serialization.js +16 -0
- package/dist/internal/machine/serialization.js.map +1 -1
- package/dist/internal/machine/stateDefinition.d.ts +3 -1
- package/dist/internal/machine/stateDefinition.d.ts.map +1 -1
- package/dist/internal/machine/stateDefinition.js +36 -2
- package/dist/internal/machine/stateDefinition.js.map +1 -1
- package/dist/internal/machine/targetBuilder.d.ts +16 -0
- package/dist/internal/machine/targetBuilder.d.ts.map +1 -0
- package/dist/internal/machine/targetBuilder.js +333 -0
- package/dist/internal/machine/targetBuilder.js.map +1 -0
- package/dist/internal/machine/topology.js +3 -3
- package/dist/internal/machine/topology.js.map +1 -1
- package/dist/internal/machine/transition.d.ts +16 -0
- package/dist/internal/machine/transition.d.ts.map +1 -0
- package/dist/internal/machine/transition.js +2 -0
- package/dist/internal/machine/transition.js.map +1 -0
- package/dist/internal/testing/machine/finiteModel.d.ts.map +1 -1
- package/dist/internal/testing/machine/finiteModel.js +11 -13
- package/dist/internal/testing/machine/finiteModel.js.map +1 -1
- package/dist/internal/testing/machine/format.d.ts +7 -0
- package/dist/internal/testing/machine/format.d.ts.map +1 -0
- package/dist/internal/testing/machine/format.js +104 -0
- package/dist/internal/testing/machine/format.js.map +1 -0
- package/dist/internal/testing/machine/probe.d.ts +1 -1
- package/dist/internal/testing/machine/probe.d.ts.map +1 -1
- package/dist/internal/testing/machine/probe.js +1 -1
- package/dist/internal/testing/machine/probe.js.map +1 -1
- package/dist/internal/testing/machine/referenceModel.d.ts.map +1 -1
- package/dist/internal/testing/machine/referenceModel.js +15 -19
- package/dist/internal/testing/machine/referenceModel.js.map +1 -1
- package/dist/internal/testing/machine/trace.d.ts +1 -1
- package/dist/internal/testing/machine/trace.d.ts.map +1 -1
- package/dist/internal/testing/machine/trace.js +3 -3
- package/dist/internal/testing/machine/trace.js.map +1 -1
- package/dist/internal/testing/machine/value.d.ts +8 -0
- package/dist/internal/testing/machine/value.d.ts.map +1 -0
- package/dist/internal/testing/machine/value.js +82 -0
- package/dist/internal/testing/machine/value.js.map +1 -0
- package/dist/internal/testing/machine/verification.d.ts +2 -13
- package/dist/internal/testing/machine/verification.d.ts.map +1 -1
- package/dist/internal/testing/machine/verification.js +21 -113
- package/dist/internal/testing/machine/verification.js.map +1 -1
- package/dist/testing/MachineTest.d.ts +89 -104
- package/dist/testing/MachineTest.d.ts.map +1 -1
- package/dist/testing/MachineTest.js +70 -84
- package/dist/testing/MachineTest.js.map +1 -1
- package/dist/unstable/cluster/ClusterMachine.d.ts +10 -13
- package/dist/unstable/cluster/ClusterMachine.d.ts.map +1 -1
- package/dist/unstable/cluster/ClusterMachine.js +5 -8
- package/dist/unstable/cluster/ClusterMachine.js.map +1 -1
- package/dist/unstable/reactivity/AtomMachine.d.ts +26 -61
- package/dist/unstable/reactivity/AtomMachine.d.ts.map +1 -1
- package/dist/unstable/reactivity/AtomMachine.js +17 -26
- package/dist/unstable/reactivity/AtomMachine.js.map +1 -1
- package/docs/agent-guide.md +45 -45
- package/docs/effect-atom-react.md +35 -63
- package/docs/machine-review.md +7 -7
- package/docs/root-api.md +259 -0
- package/package.json +1 -1
- package/src/Machine.ts +1053 -538
- package/src/internal/machine/atom.ts +3 -13
- package/src/internal/machine/childRegistry.ts +141 -0
- package/src/internal/machine/cluster.ts +5 -14
- package/src/internal/machine/configuration.ts +20 -4
- package/src/internal/machine/executionPlan.ts +109 -52
- package/src/internal/machine/invocation.ts +10 -6
- package/src/internal/machine/invocationDefinition.ts +52 -0
- package/src/internal/machine/invocationEvent.ts +0 -8
- package/src/internal/machine/machine.ts +222 -716
- package/src/internal/machine/planner.ts +74 -62
- package/src/internal/machine/process.ts +14 -21
- package/src/internal/machine/protocol.ts +9 -32
- package/src/internal/machine/readiness.ts +1 -0
- package/src/internal/machine/requirements.ts +11 -0
- package/src/internal/machine/runtime.ts +17 -186
- package/src/internal/machine/serialization.ts +22 -0
- package/src/internal/machine/stateDefinition.ts +36 -3
- package/src/internal/machine/targetBuilder.ts +525 -0
- package/src/internal/machine/topology.ts +3 -3
- package/src/internal/machine/transition.ts +26 -0
- package/src/internal/testing/machine/finiteModel.ts +23 -24
- package/src/internal/testing/machine/format.ts +120 -0
- package/src/internal/testing/machine/probe.ts +4 -4
- package/src/internal/testing/machine/referenceModel.ts +25 -17
- package/src/internal/testing/machine/trace.ts +4 -4
- package/src/internal/testing/machine/value.ts +77 -0
- package/src/internal/testing/machine/verification.ts +904 -1109
- package/src/testing/MachineTest.ts +103 -114
- package/src/unstable/cluster/ClusterMachine.ts +10 -20
- package/src/unstable/reactivity/AtomMachine.ts +42 -79
package/docs/root-api.md
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
# Root machine API
|
|
2
|
+
|
|
3
|
+
A machine is one explicit root state. The root can own data, child topology,
|
|
4
|
+
both, or neither. There is no separate global context store.
|
|
5
|
+
|
|
6
|
+
## Events and transitions only
|
|
7
|
+
|
|
8
|
+
```ts
|
|
9
|
+
import { Machine } from "@typeonce/effect-machine"
|
|
10
|
+
import { Schema } from "effect"
|
|
11
|
+
|
|
12
|
+
const Events = Machine.events({ Open: {}, Close: {} })
|
|
13
|
+
const Root = Machine.state({
|
|
14
|
+
initial: "Closed",
|
|
15
|
+
states: { Closed: {}, Open: {} }
|
|
16
|
+
})
|
|
17
|
+
const Door = Machine.make({ root: Root, events: Events }).handle({
|
|
18
|
+
states: {
|
|
19
|
+
Closed: { on: { Open: (to) => to.local.Open() } },
|
|
20
|
+
Open: { on: { Close: (to) => to.local.Closed() } }
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The root declares the default child once. No startup target, empty schema, or
|
|
26
|
+
resolver is needed. Structural nodes have `value: undefined` in snapshots.
|
|
27
|
+
|
|
28
|
+
## Root data and child data
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
const Root = Machine.state({
|
|
32
|
+
fields: { title: Schema.String },
|
|
33
|
+
initial: "Editing",
|
|
34
|
+
states: {
|
|
35
|
+
Editing: { fields: { draft: Schema.String } },
|
|
36
|
+
Saving: {}
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
const Events = Machine.events({ Rename: { title: Schema.String }, Save: {} })
|
|
40
|
+
const Editor = Machine.make({
|
|
41
|
+
root: Root,
|
|
42
|
+
events: Events,
|
|
43
|
+
initial: (root) => root.from(() => ({ title: "Untitled" }))
|
|
44
|
+
}).handle({
|
|
45
|
+
initialize: ({ builder, state }) => builder.from({ draft: state.title }),
|
|
46
|
+
on: {
|
|
47
|
+
Rename: (to) => to.self.update.from(({ event }) => ({ title: event.title }))
|
|
48
|
+
},
|
|
49
|
+
states: {
|
|
50
|
+
Editing: { on: { Save: (to) => to.local.Saving() } }
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`initialize` constructs the declared child configuration. Required values must
|
|
56
|
+
be supplied before the machine becomes executable. Values whose schema make
|
|
57
|
+
input is optional can use their schema defaults. A parallel owner's initializer
|
|
58
|
+
constructs every required region through the typed builder.
|
|
59
|
+
|
|
60
|
+
The complete snapshot always includes the root:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
// While Editing is active:
|
|
64
|
+
{
|
|
65
|
+
path: "",
|
|
66
|
+
value: { _tag: "", title: "Untitled" },
|
|
67
|
+
state: {
|
|
68
|
+
path: "Editing",
|
|
69
|
+
value: { _tag: "Editing", draft: "Untitled" }
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Root handlers remain active across child transitions. Child handlers receive
|
|
75
|
+
their own state, typed ancestors, and the complete snapshot. `to.root.update`
|
|
76
|
+
addresses root data from a child. `to.self.update` addresses the handler owner.
|
|
77
|
+
Both replace the entire value; omitted fields are not silently retained.
|
|
78
|
+
Updates preserve the active topology and do not restart scoped work.
|
|
79
|
+
|
|
80
|
+
Simultaneous transitions retain conflict checking. Two parallel handlers cannot
|
|
81
|
+
silently overwrite the same owner. Use `.updating(owner)` to combine a selected
|
|
82
|
+
destination and a retained owner's value in one atomic transition.
|
|
83
|
+
|
|
84
|
+
## Schemas and reusable descriptors
|
|
85
|
+
|
|
86
|
+
Use `fields` for local data or `schema` for an existing tagged schema. They are
|
|
87
|
+
mutually exclusive. A schema preserves class identity, refinements, defaults,
|
|
88
|
+
and transformations; its tag is independent of its mount path.
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
class Ready extends Schema.TaggedClass<Ready>("Ready")("Ready", {
|
|
92
|
+
name: Schema.String
|
|
93
|
+
}) {}
|
|
94
|
+
|
|
95
|
+
const Form = Machine.state({
|
|
96
|
+
initial: "Idle",
|
|
97
|
+
states: { Idle: {}, Ready: { schema: Ready } }
|
|
98
|
+
})
|
|
99
|
+
const Root = Machine.state({
|
|
100
|
+
type: "parallel",
|
|
101
|
+
states: { First: Form, Second: Form }
|
|
102
|
+
})
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Each mount has its own path and lifecycle. Descriptors capture their definition
|
|
106
|
+
without retaining caller-owned mutable topology containers. `Machine.make`
|
|
107
|
+
accepts the complete descriptor as `root`; callers do not extract its internals.
|
|
108
|
+
|
|
109
|
+
`Machine.events({ Save: { title: Schema.String } })` creates a deferred
|
|
110
|
+
`Events.Save({ title })` constructor. The machine validates it when processing
|
|
111
|
+
the event. To reuse tagged classes or a tagged union, use
|
|
112
|
+
`Machine.eventsFromSchemas(...)`. Internal and emitted protocols have distinct
|
|
113
|
+
constructors with the same fields/import distinction. Public sends cannot send
|
|
114
|
+
internal events, and emissions do not become machine input events implicitly.
|
|
115
|
+
|
|
116
|
+
## Default initialization and explicit configuration
|
|
117
|
+
|
|
118
|
+
`initial` supplies root values while following the topology's declared child
|
|
119
|
+
defaults. It does not override those defaults. A structural root usually needs
|
|
120
|
+
no initializer.
|
|
121
|
+
|
|
122
|
+
Use the separately named `initialConfiguration` to select a complete startup
|
|
123
|
+
configuration, for example to start the door open:
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
const DoorOpen = Machine.make({
|
|
127
|
+
root: Root,
|
|
128
|
+
events: Events,
|
|
129
|
+
initialConfiguration: (root) => root.resolve(({ target }) =>
|
|
130
|
+
target.from((states) => states.Open.from())
|
|
131
|
+
)
|
|
132
|
+
}).handle({})
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
This example uses the door root and events from the first section. Every active
|
|
136
|
+
value and region in an explicit configuration must be provided. `initial` and
|
|
137
|
+
`initialConfiguration` cannot be combined. Use `Machine.resume` when restoring
|
|
138
|
+
a validated snapshot, including its completion and history metadata.
|
|
139
|
+
|
|
140
|
+
## Construction, guards, and branches
|
|
141
|
+
|
|
142
|
+
A selected target supports a direct callback:
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
Save: (to) => to.local.Saving().from(({ event }) => ({ requestId: event.requestId }))
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Use `.decoded(({ state }) => state)` for already decoded schema values. Use
|
|
149
|
+
`.resolve((context, enqueue) => ...)` when constructing child configurations or
|
|
150
|
+
queuing commands; its `context.target.from(...)` and `.decoded(...)` take values
|
|
151
|
+
and explicit child builders.
|
|
152
|
+
|
|
153
|
+
A guard runs before construction and commands:
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
Save: (to) => to.local.Saving()
|
|
157
|
+
.guard(({ state }) => state.draft.length > 0)
|
|
158
|
+
.from(({ state }) => ({ requestId: state.draft }))
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
A false guard declines this handler, allowing ancestor fallback. Guards are not
|
|
162
|
+
available for mandatory initial resolution or a total choice. `.none` accepts
|
|
163
|
+
an event without changing topology; declining and accepting have different
|
|
164
|
+
semantics. Named branches keep `{ target, title? }`: `target` identifies the
|
|
165
|
+
checked destination and `title` supplies optional presentation metadata.
|
|
166
|
+
|
|
167
|
+
Guards also apply to standalone owner updates and combined transitions:
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
Increment: (to) => to.self.update
|
|
171
|
+
.guard(({ current }) => current.count < 10)
|
|
172
|
+
.from(({ current }) => ({ ...current, count: current.count + 1 }))
|
|
173
|
+
|
|
174
|
+
Save: (to) => to.local.Saving().updating(to.root)
|
|
175
|
+
.guard(({ current }) => current.draft.length > 0)
|
|
176
|
+
.from(({ current }) => ({
|
|
177
|
+
target: { requestId: current.draft },
|
|
178
|
+
update: { ...current, attempts: current.attempts + 1 }
|
|
179
|
+
}))
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Combined `.from` returns constructor inputs for both the destination and the
|
|
183
|
+
complete owner replacement. `.decoded` returns their decoded values instead.
|
|
184
|
+
For a destination with no construction arguments, use `target: undefined`.
|
|
185
|
+
Both values are validated before applying either change; destination entry
|
|
186
|
+
observes the updated owner. Use `.resolve` for mixed construction methods,
|
|
187
|
+
explicit child configurations, or commands.
|
|
188
|
+
|
|
189
|
+
Reentry is a modifier before construction:
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
Retry: (to) => to.local.Saving().reenter()
|
|
193
|
+
.from(({ event }) => ({ requestId: event.requestId }))
|
|
194
|
+
|
|
195
|
+
Refresh: (to) => to.none.reenter()
|
|
196
|
+
|
|
197
|
+
Choose: (to) => to.branches({
|
|
198
|
+
saving: { target: to.local.Saving() },
|
|
199
|
+
idle: { target: to.local.Idle() }
|
|
200
|
+
}).reenter().resolve(({ state, select }) =>
|
|
201
|
+
state.retry ? select.saving.from({ requestId: state.requestId }) : select.idle.from()
|
|
202
|
+
)
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
`.reenter()` restarts the handler source. It composes with `.updating`, `.guard`,
|
|
206
|
+
`.from`, `.decoded`, and `.resolve` wherever reentry is supported. Apply it to
|
|
207
|
+
the whole named-branches builder, whose individual targets describe topology.
|
|
208
|
+
Migrate `.resolve(callback, { reenter: true })` to `.reenter().resolve(callback)`;
|
|
209
|
+
remove `{ reenter: false }`. `to.self.update` retains the source lifecycle and
|
|
210
|
+
does not expose `.reenter()`.
|
|
211
|
+
|
|
212
|
+
## Completion and history
|
|
213
|
+
|
|
214
|
+
A compound root returns its completed direct workflow's output when that child
|
|
215
|
+
has no `onDone` handler. A child with `onDone` first handles its completion.
|
|
216
|
+
Nested compound states retain statechart completion rules: an arbitrary
|
|
217
|
+
completed descendant does not make every ancestor final. Parallel roots
|
|
218
|
+
complete when all required regions complete.
|
|
219
|
+
|
|
220
|
+
Restoring history beneath the root preserves current root data. Restoring the
|
|
221
|
+
root's own history can restore root-owned data. History fallback builders must
|
|
222
|
+
construct a complete configuration containing the history owner.
|
|
223
|
+
|
|
224
|
+
## Observation and testing
|
|
225
|
+
|
|
226
|
+
- `MachineRef.state` reads the complete logical root snapshot.
|
|
227
|
+
- `MachineRef.snapshot` includes runtime status and completion information.
|
|
228
|
+
- `MachineAtom.result` retains asynchronous startup, failure, and the successful
|
|
229
|
+
logical snapshot. `MachineAtom.snapshot` exposes the runtime snapshot.
|
|
230
|
+
- `AtomMachine.select` selects an optional value; `selectSnapshot` selects an
|
|
231
|
+
optional subtree. Child inactivity remains `Option.none()`.
|
|
232
|
+
- `MachineState` renders a typed path in React. `createMachineContext(factory)`
|
|
233
|
+
owns a separate machine per Provider without subscribing the Provider to it.
|
|
234
|
+
|
|
235
|
+
The Atom bridge and child bridge no longer expose `.state`; use `.result` or a
|
|
236
|
+
path selector. The core `MachineRef.state` contract remains available.
|
|
237
|
+
|
|
238
|
+
`MachineTest.run` and `MachineTest.probe` accept the same public event inputs as
|
|
239
|
+
production sends, including deferred constructors. Traces and acknowledgements
|
|
240
|
+
record the decoded event that was processed, including ignored events.
|
|
241
|
+
|
|
242
|
+
## Migration
|
|
243
|
+
|
|
244
|
+
Replace `Machine.states(tree)` and `Machine.make({ states: States.states })`
|
|
245
|
+
with one `Machine.state({ initial, states: tree })` descriptor and
|
|
246
|
+
`Machine.make({ root })`. Move the old root-level handler map under
|
|
247
|
+
`handle({ states: ... })`; place machine-wide behavior directly in `handle`.
|
|
248
|
+
Use root `fields` to replace a wrapper state introduced only to carry shared data.
|
|
249
|
+
Use `Machine.Snapshot<typeof root>` for the complete logical snapshot. `Machine.StateAccessors` replaces the former `Machine.DefinedStates`
|
|
250
|
+
helper interface; it exposes paths and projections without a public state map.
|
|
251
|
+
|
|
252
|
+
Replace schema arguments to `Machine.events`, `internalEvents`, and
|
|
253
|
+
`emittedEvents` with field records, or use their explicit `FromSchemas`
|
|
254
|
+
constructors. Replace old Atom `.state` reads with `.result`. Use
|
|
255
|
+
`EmittedEvent`, `EmittedEvents`, `EmittedEventOf`, and `SchemaLessStateAnnotations` in place
|
|
256
|
+
of the removed deprecated type aliases.
|
|
257
|
+
|
|
258
|
+
Encoded snapshots now use version 2 and include the root at path `""`.
|
|
259
|
+
Version 1 payloads must be explicitly migrated; the decoder rejects them.
|