@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.
Files changed (151) hide show
  1. package/README.md +168 -162
  2. package/dist/Machine.d.ts +495 -284
  3. package/dist/Machine.d.ts.map +1 -1
  4. package/dist/Machine.js +91 -111
  5. package/dist/Machine.js.map +1 -1
  6. package/dist/internal/machine/atom.d.ts +3 -4
  7. package/dist/internal/machine/atom.d.ts.map +1 -1
  8. package/dist/internal/machine/atom.js +0 -2
  9. package/dist/internal/machine/atom.js.map +1 -1
  10. package/dist/internal/machine/childRegistry.d.ts +52 -0
  11. package/dist/internal/machine/childRegistry.d.ts.map +1 -0
  12. package/dist/internal/machine/childRegistry.js +74 -0
  13. package/dist/internal/machine/childRegistry.js.map +1 -0
  14. package/dist/internal/machine/cluster.d.ts +2 -3
  15. package/dist/internal/machine/cluster.d.ts.map +1 -1
  16. package/dist/internal/machine/cluster.js.map +1 -1
  17. package/dist/internal/machine/configuration.d.ts.map +1 -1
  18. package/dist/internal/machine/configuration.js +14 -4
  19. package/dist/internal/machine/configuration.js.map +1 -1
  20. package/dist/internal/machine/executionPlan.d.ts +1 -0
  21. package/dist/internal/machine/executionPlan.d.ts.map +1 -1
  22. package/dist/internal/machine/executionPlan.js +97 -53
  23. package/dist/internal/machine/executionPlan.js.map +1 -1
  24. package/dist/internal/machine/invocation.d.ts.map +1 -1
  25. package/dist/internal/machine/invocation.js +4 -2
  26. package/dist/internal/machine/invocation.js.map +1 -1
  27. package/dist/internal/machine/invocationDefinition.d.ts +35 -0
  28. package/dist/internal/machine/invocationDefinition.d.ts.map +1 -0
  29. package/dist/internal/machine/invocationDefinition.js +21 -0
  30. package/dist/internal/machine/invocationDefinition.js.map +1 -0
  31. package/dist/internal/machine/invocationEvent.d.ts +0 -2
  32. package/dist/internal/machine/invocationEvent.d.ts.map +1 -1
  33. package/dist/internal/machine/invocationEvent.js +0 -8
  34. package/dist/internal/machine/invocationEvent.js.map +1 -1
  35. package/dist/internal/machine/machine.d.ts +21 -43
  36. package/dist/internal/machine/machine.d.ts.map +1 -1
  37. package/dist/internal/machine/machine.js +134 -403
  38. package/dist/internal/machine/machine.js.map +1 -1
  39. package/dist/internal/machine/planner.d.ts +13 -19
  40. package/dist/internal/machine/planner.d.ts.map +1 -1
  41. package/dist/internal/machine/planner.js +63 -35
  42. package/dist/internal/machine/planner.js.map +1 -1
  43. package/dist/internal/machine/process.d.ts +6 -8
  44. package/dist/internal/machine/process.d.ts.map +1 -1
  45. package/dist/internal/machine/process.js +1 -0
  46. package/dist/internal/machine/process.js.map +1 -1
  47. package/dist/internal/machine/protocol.d.ts +2 -2
  48. package/dist/internal/machine/protocol.d.ts.map +1 -1
  49. package/dist/internal/machine/protocol.js +4 -18
  50. package/dist/internal/machine/protocol.js.map +1 -1
  51. package/dist/internal/machine/readiness.d.ts +1 -1
  52. package/dist/internal/machine/readiness.d.ts.map +1 -1
  53. package/dist/internal/machine/requirements.d.ts +4 -0
  54. package/dist/internal/machine/requirements.d.ts.map +1 -0
  55. package/dist/internal/machine/requirements.js +2 -0
  56. package/dist/internal/machine/requirements.js.map +1 -0
  57. package/dist/internal/machine/runtime.d.ts +4 -58
  58. package/dist/internal/machine/runtime.d.ts.map +1 -1
  59. package/dist/internal/machine/runtime.js +1 -69
  60. package/dist/internal/machine/runtime.js.map +1 -1
  61. package/dist/internal/machine/serialization.d.ts.map +1 -1
  62. package/dist/internal/machine/serialization.js +16 -0
  63. package/dist/internal/machine/serialization.js.map +1 -1
  64. package/dist/internal/machine/stateDefinition.d.ts +3 -1
  65. package/dist/internal/machine/stateDefinition.d.ts.map +1 -1
  66. package/dist/internal/machine/stateDefinition.js +36 -2
  67. package/dist/internal/machine/stateDefinition.js.map +1 -1
  68. package/dist/internal/machine/targetBuilder.d.ts +16 -0
  69. package/dist/internal/machine/targetBuilder.d.ts.map +1 -0
  70. package/dist/internal/machine/targetBuilder.js +333 -0
  71. package/dist/internal/machine/targetBuilder.js.map +1 -0
  72. package/dist/internal/machine/topology.js +3 -3
  73. package/dist/internal/machine/topology.js.map +1 -1
  74. package/dist/internal/machine/transition.d.ts +16 -0
  75. package/dist/internal/machine/transition.d.ts.map +1 -0
  76. package/dist/internal/machine/transition.js +2 -0
  77. package/dist/internal/machine/transition.js.map +1 -0
  78. package/dist/internal/testing/machine/finiteModel.d.ts.map +1 -1
  79. package/dist/internal/testing/machine/finiteModel.js +11 -13
  80. package/dist/internal/testing/machine/finiteModel.js.map +1 -1
  81. package/dist/internal/testing/machine/format.d.ts +7 -0
  82. package/dist/internal/testing/machine/format.d.ts.map +1 -0
  83. package/dist/internal/testing/machine/format.js +104 -0
  84. package/dist/internal/testing/machine/format.js.map +1 -0
  85. package/dist/internal/testing/machine/probe.d.ts +1 -1
  86. package/dist/internal/testing/machine/probe.d.ts.map +1 -1
  87. package/dist/internal/testing/machine/probe.js +1 -1
  88. package/dist/internal/testing/machine/probe.js.map +1 -1
  89. package/dist/internal/testing/machine/referenceModel.d.ts.map +1 -1
  90. package/dist/internal/testing/machine/referenceModel.js +15 -19
  91. package/dist/internal/testing/machine/referenceModel.js.map +1 -1
  92. package/dist/internal/testing/machine/trace.d.ts +1 -1
  93. package/dist/internal/testing/machine/trace.d.ts.map +1 -1
  94. package/dist/internal/testing/machine/trace.js +3 -3
  95. package/dist/internal/testing/machine/trace.js.map +1 -1
  96. package/dist/internal/testing/machine/value.d.ts +8 -0
  97. package/dist/internal/testing/machine/value.d.ts.map +1 -0
  98. package/dist/internal/testing/machine/value.js +82 -0
  99. package/dist/internal/testing/machine/value.js.map +1 -0
  100. package/dist/internal/testing/machine/verification.d.ts +2 -13
  101. package/dist/internal/testing/machine/verification.d.ts.map +1 -1
  102. package/dist/internal/testing/machine/verification.js +21 -113
  103. package/dist/internal/testing/machine/verification.js.map +1 -1
  104. package/dist/testing/MachineTest.d.ts +89 -104
  105. package/dist/testing/MachineTest.d.ts.map +1 -1
  106. package/dist/testing/MachineTest.js +70 -84
  107. package/dist/testing/MachineTest.js.map +1 -1
  108. package/dist/unstable/cluster/ClusterMachine.d.ts +10 -13
  109. package/dist/unstable/cluster/ClusterMachine.d.ts.map +1 -1
  110. package/dist/unstable/cluster/ClusterMachine.js +5 -8
  111. package/dist/unstable/cluster/ClusterMachine.js.map +1 -1
  112. package/dist/unstable/reactivity/AtomMachine.d.ts +26 -61
  113. package/dist/unstable/reactivity/AtomMachine.d.ts.map +1 -1
  114. package/dist/unstable/reactivity/AtomMachine.js +17 -26
  115. package/dist/unstable/reactivity/AtomMachine.js.map +1 -1
  116. package/docs/agent-guide.md +45 -45
  117. package/docs/effect-atom-react.md +35 -63
  118. package/docs/machine-review.md +7 -7
  119. package/docs/root-api.md +259 -0
  120. package/package.json +1 -1
  121. package/src/Machine.ts +1053 -538
  122. package/src/internal/machine/atom.ts +3 -13
  123. package/src/internal/machine/childRegistry.ts +141 -0
  124. package/src/internal/machine/cluster.ts +5 -14
  125. package/src/internal/machine/configuration.ts +20 -4
  126. package/src/internal/machine/executionPlan.ts +109 -52
  127. package/src/internal/machine/invocation.ts +10 -6
  128. package/src/internal/machine/invocationDefinition.ts +52 -0
  129. package/src/internal/machine/invocationEvent.ts +0 -8
  130. package/src/internal/machine/machine.ts +222 -716
  131. package/src/internal/machine/planner.ts +74 -62
  132. package/src/internal/machine/process.ts +14 -21
  133. package/src/internal/machine/protocol.ts +9 -32
  134. package/src/internal/machine/readiness.ts +1 -0
  135. package/src/internal/machine/requirements.ts +11 -0
  136. package/src/internal/machine/runtime.ts +17 -186
  137. package/src/internal/machine/serialization.ts +22 -0
  138. package/src/internal/machine/stateDefinition.ts +36 -3
  139. package/src/internal/machine/targetBuilder.ts +525 -0
  140. package/src/internal/machine/topology.ts +3 -3
  141. package/src/internal/machine/transition.ts +26 -0
  142. package/src/internal/testing/machine/finiteModel.ts +23 -24
  143. package/src/internal/testing/machine/format.ts +120 -0
  144. package/src/internal/testing/machine/probe.ts +4 -4
  145. package/src/internal/testing/machine/referenceModel.ts +25 -17
  146. package/src/internal/testing/machine/trace.ts +4 -4
  147. package/src/internal/testing/machine/value.ts +77 -0
  148. package/src/internal/testing/machine/verification.ts +904 -1109
  149. package/src/testing/MachineTest.ts +103 -114
  150. package/src/unstable/cluster/ClusterMachine.ts +10 -20
  151. package/src/unstable/reactivity/AtomMachine.ts +42 -79
@@ -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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@typeonce/effect-machine",
3
- "version": "0.31.2",
3
+ "version": "0.33.0",
4
4
  "description": "Schema-first state machines and statecharts for Effect",
5
5
  "author": "Sandro Maglione",
6
6
  "repository": {