@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
@@ -22,12 +22,12 @@ const CounterState = Schema.TaggedUnion({
22
22
  Running: { count: Schema.Number }
23
23
  })
24
24
 
25
- export const CounterStates = Machine.states({
25
+ export const CounterStates = Machine.state({ initial: "Idle", states: {
26
26
  Idle: {},
27
27
  Running: CounterState.cases.Running
28
- })
28
+ } })
29
29
 
30
- export const CounterEvents = Machine.events(
30
+ export const CounterEvents = Machine.eventsFromSchemas(
31
31
  Schema.TaggedUnion({
32
32
  Start: {},
33
33
  Increment: {},
@@ -37,27 +37,27 @@ export const CounterEvents = Machine.events(
37
37
 
38
38
  export const CounterMachine = Machine.make({
39
39
  id: "Counter",
40
- states: CounterStates.states,
40
+ root: CounterStates,
41
41
  events: CounterEvents,
42
- initial: (to) => to.Idle()
43
- }).handle({
42
+ initialConfiguration: root => root.resolve(({ target }) => target.from(to => to.Idle.from()))
43
+ }).handle({ states: {
44
44
  Idle: {
45
45
  on: {
46
- Start: (to) => to.full.Running().resolve(({ target }) => target.from({ count: 0 }))
46
+ Start: (to) => to.branch.Running().resolve(({ target }) => target.from({ count: 0 }))
47
47
  }
48
48
  },
49
49
  Running: {
50
50
  on: {
51
- Increment: (to) => to.full.Running().resolve(({ state, target }) => target.from({ count: state.count + 1 })),
52
- Stop: (to) => to.full.Idle()
51
+ Increment: (to) => to.branch.Running().resolve(({ state, target }) => target.from({ count: state.count + 1 })),
52
+ Stop: (to) => to.branch.Idle()
53
53
  }
54
54
  }
55
- })
55
+ } })
56
56
  ```
57
57
 
58
58
  Each step has one job:
59
59
 
60
- - `Machine.states` declares the state tree and the data owned by each state.
60
+ - `Machine.state` declares the root, its child topology, and state-owned data.
61
61
  - `Machine.events` declares the public messages the machine accepts and returns
62
62
  typed event constructors.
63
63
  - `Machine.make` joins the state tree, event protocol, input, and initial state.
@@ -106,12 +106,12 @@ const RequestState = Schema.TaggedUnion({
106
106
  Failed: { message: Schema.String }
107
107
  })
108
108
 
109
- const RequestStates = Machine.states({
109
+ const RequestStates = Machine.state({ initial: "Idle", states: {
110
110
  Idle: {},
111
111
  Loading: {},
112
112
  Ready: RequestState.cases.Ready,
113
113
  Failed: RequestState.cases.Failed
114
- })
114
+ } })
115
115
  ```
116
116
 
117
117
  The machine can now be `Loading`, `Ready`, or `Failed`. It cannot construct a
@@ -139,7 +139,7 @@ const DocumentState = Schema.TaggedUnion({
139
139
  }
140
140
  })
141
141
 
142
- const DocumentStates = Machine.states({
142
+ const DocumentStates = Machine.state({ initial: "Closed", states: {
143
143
  Closed: {},
144
144
  Open: {
145
145
  // Editing, Saving, and SaveFailed all need the document and draft.
@@ -152,7 +152,7 @@ const DocumentStates = Machine.states({
152
152
  SaveFailed: DocumentState.cases.SaveFailed
153
153
  }
154
154
  }
155
- })
155
+ } })
156
156
  ```
157
157
 
158
158
  Do not copy `documentId` and `draft` into every child. Copies can disagree after
@@ -165,22 +165,22 @@ Hierarchy owns behavior as well as data. Define a transition on the lowest
165
165
  compound state whose children share it.
166
166
 
167
167
  ```ts
168
- const DocumentEvents = Machine.events(
168
+ const DocumentEvents = Machine.eventsFromSchemas(
169
169
  Schema.TaggedUnion({
170
170
  Close: {}
171
171
  })
172
172
  )
173
173
 
174
174
  const DocumentMachine = Machine.make({
175
- states: DocumentStates.states,
175
+ root: DocumentStates,
176
176
  events: DocumentEvents,
177
177
  initial: (to) => to.Closed()
178
- }).handle({
178
+ }).handle({ states: {
179
179
  Closed: {},
180
180
  Open: {
181
181
  on: {
182
182
  // All Open children close the document in the same way.
183
- Close: (to) => to.full.Closed()
183
+ Close: (to) => to.branch.Closed()
184
184
  },
185
185
  states: {
186
186
  Editing: {},
@@ -188,7 +188,7 @@ const DocumentMachine = Machine.make({
188
188
  SaveFailed: {}
189
189
  }
190
190
  }
191
- })
191
+ } })
192
192
  ```
193
193
 
194
194
  The machine checks the deepest active state first, then its ancestors. Put a
@@ -202,7 +202,7 @@ after domain actions and outcomes. Do not expose state setters such as
202
202
  `SetLoading` or `SetError`.
203
203
 
204
204
  ```ts
205
- export const CheckoutEvents = Machine.events(
205
+ export const CheckoutEvents = Machine.eventsFromSchemas(
206
206
  Schema.TaggedUnion({
207
207
  Submit: {},
208
208
  Cancel: {}
@@ -210,23 +210,23 @@ export const CheckoutEvents = Machine.events(
210
210
  )
211
211
 
212
212
  const CheckoutMachine = Machine.make({
213
- states: CheckoutStates.states,
213
+ root: CheckoutStates,
214
214
  events: CheckoutEvents,
215
215
  initial: (to) => to.Editing()
216
- }).handle({
216
+ }).handle({ states: {
217
217
  Editing: {
218
218
  on: {
219
- Submit: (to) => to.full.Submitting()
219
+ Submit: (to) => to.branch.Submitting()
220
220
  }
221
221
  },
222
222
  Submitting: {
223
223
  on: {
224
224
  // Cancel has meaning while work is in progress.
225
- Cancel: (to) => to.full.Editing()
225
+ Cancel: (to) => to.branch.Editing()
226
226
  }
227
227
  },
228
228
  Complete: {}
229
- })
229
+ } })
230
230
  ```
231
231
 
232
232
  The sender requests `Submit`. The machine decides whether `Submit` has a
@@ -243,7 +243,7 @@ child in every region. A parallel model therefore accepts the full product of
243
243
  those regions.
244
244
 
245
245
  ```ts
246
- const ScreenStates = Machine.states({
246
+ const ScreenStates = Machine.state({ initial: "Screen", states: {
247
247
  Screen: {
248
248
  type: "parallel",
249
249
  states: {
@@ -263,7 +263,7 @@ const ScreenStates = Machine.states({
263
263
  }
264
264
  }
265
265
  }
266
- })
266
+ } })
267
267
  ```
268
268
 
269
269
  This model permits all four combinations: online with a closed panel, online
@@ -286,29 +286,29 @@ const LoadState = Schema.TaggedUnion({
286
286
  Failed: { message: Schema.String }
287
287
  })
288
288
 
289
- const LoadStates = Machine.states({
289
+ const LoadStates = Machine.state({ initial: "Idle", states: {
290
290
  Idle: {},
291
291
  Loading: LoadState.cases.Loading,
292
292
  Ready: LoadState.cases.Ready,
293
293
  Failed: LoadState.cases.Failed
294
- })
294
+ } })
295
295
 
296
296
  const LoadMachine = Machine.make({
297
- states: LoadStates.states,
298
- events: Machine.events(),
299
- initial: (to) => to.Idle()
300
- }).handle({
297
+ root: LoadStates,
298
+ events: Machine.eventsFromSchemas(),
299
+ initialConfiguration: root => root.resolve(({ target }) => target.from(to => to.Idle.from()))
300
+ }).handle({ states: {
301
301
  Idle: {},
302
302
  Loading: {
303
303
  invoke: (from) =>
304
304
  from
305
305
  .effect("load-document", ({ state }) => loadDocument(state.documentId))
306
- .onDone((to) => to.full.Ready().resolve(({ output, target }) => target.from({ content: output })))
307
- .onFailure((to) => to.full.Failed().resolve(({ error, target }) => target.from({ message: String(error) })))
306
+ .onDone((to) => to.branch.Ready().resolve(({ output, target }) => target.from({ content: output })))
307
+ .onFailure((to) => to.branch.Failed().resolve(({ error, target }) => target.from({ message: String(error) })))
308
308
  },
309
309
  Ready: {},
310
310
  Failed: {}
311
- })
311
+ } })
312
312
  ```
313
313
 
314
314
  `loadDocument` may require Effect services. Those requirements remain on the
@@ -356,8 +356,8 @@ Commissioning: {
356
356
  { discard: true }
357
357
  )
358
358
  )
359
- .onDone((to) => to.full.Running())
360
- .onFailure((to) => to.full.Failed())
359
+ .onDone((to) => to.branch.Running())
360
+ .onFailure((to) => to.branch.Failed())
361
361
  }
362
362
  ```
363
363
 
@@ -374,24 +374,24 @@ A transition should choose the next state from the current snapshot and event.
374
374
  Use ordinary TypeScript conditions when one event has several valid outcomes.
375
375
 
376
376
  ```ts
377
- const ReviewEvents = Machine.events(
377
+ const ReviewEvents = Machine.eventsFromSchemas(
378
378
  Schema.TaggedUnion({
379
379
  Evaluate: { score: Schema.Number }
380
380
  })
381
381
  )
382
382
 
383
383
  const ReviewMachine = Machine.make({
384
- states: ReviewStates.states,
384
+ root: ReviewStates,
385
385
  events: ReviewEvents,
386
386
  initial: (to) => to.Pending()
387
- }).handle({
387
+ }).handle({ states: {
388
388
  Pending: {
389
389
  on: {
390
390
  Evaluate: (to) =>
391
391
  to
392
392
  .branches({
393
- accepted: { target: to.full.Accepted() },
394
- rejected: { target: to.full.Rejected() }
393
+ accepted: { target: to.branch.Accepted() },
394
+ rejected: { target: to.branch.Rejected() }
395
395
  })
396
396
  .resolve(({ event, select }) =>
397
397
  event.score >= 80
@@ -402,7 +402,7 @@ const ReviewMachine = Machine.make({
402
402
  },
403
403
  Accepted: {},
404
404
  Rejected: {}
405
- })
405
+ } })
406
406
  ```
407
407
 
408
408
  Given the same snapshot and event, the handler should choose the same result.
@@ -39,85 +39,61 @@ Service-free machines can use `AtomMachine.make` directly.
39
39
 
40
40
  ## Own a machine in one React subtree
41
41
 
42
- Use `useMachineAtom` when a provider, route, dialog, or other React subtree
43
- owns one machine instance:
42
+ Create a context from a factory. Each Provider owns a fresh bridge in its current
43
+ Atom registry; it does not subscribe to state changes.
44
44
 
45
45
  ```tsx
46
- import { useMachineAtom } from "@typeonce/effect-machine-react"
47
- import { createContext, type ReactNode, useContext } from "react"
48
- import { AuthMachine, type AuthMachineInput } from "../machines/auth-machine"
46
+ import { createMachineContext } from "@typeonce/effect-machine-react"
47
+ import { AuthMachine } from "../machines/auth-machine"
49
48
  import { MachineAtoms } from "../lib/atom-runtime"
50
49
 
51
- const makeAuthMachine = MachineAtoms.factory(AuthMachine)
52
- type AuthMachineAtom = ReturnType<typeof makeAuthMachine>
53
-
54
- const AuthMachineContext = createContext<AuthMachineAtom | null>(null)
55
-
56
- export function AuthMachineProvider({
57
- children,
58
- input
59
- }: {
60
- readonly children: ReactNode
61
- readonly input: AuthMachineInput
62
- }) {
63
- const machine = useMachineAtom(() => makeAuthMachine(input))
50
+ export const Auth = createMachineContext(MachineAtoms.factory(AuthMachine))
51
+ export const useAuthMachine = Auth.useMachine
64
52
 
53
+ function AuthRoute({ input, attemptId }: Props) {
65
54
  return (
66
- <AuthMachineContext.Provider value={machine}>
67
- {children}
68
- </AuthMachineContext.Provider>
55
+ <Auth.Provider key={attemptId} input={input}>
56
+ <Suspense fallback={<Loading />}>
57
+ <AuthCard />
58
+ </Suspense>
59
+ </Auth.Provider>
69
60
  )
70
61
  }
71
-
72
- export function useAuthMachine(): AuthMachineAtom {
73
- const machine = useContext(AuthMachineContext)
74
- if (machine === null) {
75
- throw new Error("useAuthMachine must be used inside AuthMachineProvider")
76
- }
77
- return machine
78
- }
79
62
  ```
80
63
 
81
- The provider strongly owns the complete `MachineAtom`. The hook mounts
82
- `machine.ref` after React commits the owner, but it does not read `state`,
83
- `snapshot`, or `result`. Machine updates therefore do not rerender the
84
- provider.
64
+ Input is startup-only. Send events to update the existing workflow; change the
65
+ Provider's React key to create a new one. Keep the Provider above Suspense so
66
+ suspension does not discard the owner. Supply an Atom RegistryProvider at the
67
+ application boundary. The same factory used by two Providers creates isolated
68
+ machines, even when their inputs are equal.
85
69
 
86
- The factory captures startup input once. A later `input` prop change does not
87
- replace the running workflow. Send an event when the change belongs to that
88
- workflow. Change the provider's React key when React should own a new machine:
89
-
90
- ```tsx
91
- <AuthMachineProvider key={attemptId} input={input}>
92
- <AuthCard />
93
- </AuthMachineProvider>
94
- ```
95
-
96
- Put the owner above a Suspense boundary. React can then retain the same machine
97
- while a state-reading descendant suspends.
70
+ `useMachineAtom(() => makeMachine(input))` remains available for custom owners.
71
+ It mounts the reference after commit without reading `result` or `snapshot`.
98
72
 
99
73
  ## Render state-owned data
100
74
 
101
- Subscribe in the smallest component that renders a state path:
75
+ Render a typed path in the smallest component that needs it:
102
76
 
103
77
  ```tsx
104
- import { useAtomSuspense } from "@effect/atom-react"
105
- import { AtomMachine } from "@typeonce/effect-machine/reactivity"
106
- import { Option } from "effect"
78
+ import { MachineState } from "@typeonce/effect-machine-react"
107
79
 
108
80
  function EditingFields() {
109
81
  const machine = useAuthMachine()
110
- const editing = useAtomSuspense(
111
- AtomMachine.selectSnapshot(machine, "Editing")
112
- ).value
113
-
114
- return Option.match(editing, {
115
- onNone: () => null,
116
- onSome: ({ value }) => <EmailField email={value.email} />
117
- })
82
+ return (
83
+ <MachineState machine={machine} path="Editing" inactive={null}>
84
+ {({ value }) => <EmailField email={value.email} />}
85
+ </MachineState>
86
+ )
118
87
  }
119
88
  ```
120
89
 
90
+ `MachineState` subscribes only its renderer. The path determines the callback's
91
+ snapshot type. Startup suspends, failures reach the nearest error boundary,
92
+ and an inactive path renders `inactive` (null by default).
93
+
94
+ For custom hooks, `useAtomSuspense(AtomMachine.selectSnapshot(machine, path)).value`
95
+ returns an Option of the selected snapshot.
96
+
121
97
  `AtomMachine.select` returns the selected state value.
122
98
  `AtomMachine.selectSnapshot` also retains the selected state's child topology.
123
99
  Both return `Option.none()` while the path is inactive. Do not replace that
@@ -240,11 +216,7 @@ function AuthScreen() {
240
216
  const machine = useAuthMachine()
241
217
  const state = useAtomSuspense(machine.result).value
242
218
 
243
- return AuthStates.match(state, {
244
- Editing: (editing) => <EditingScreen state={editing} />,
245
- Verification: (verification) => <VerificationScreen state={verification} />,
246
- Failed: (failed) => <FailureScreen state={failed} />
247
- })
219
+ return <MachineSummary snapshot={state} />
248
220
  }
249
221
  ```
250
222
 
@@ -320,7 +292,7 @@ export const plantAtoms = AtomMachine.familyChild(CentralMachineAtom, {
320
292
  child: (plantId: string) => Plant(plantId),
321
293
  atoms: {
322
294
  broken: AtomMachine.matchesChild("Broken"),
323
- state: (plant) => plant.state,
295
+ state: (plant) => plant.result,
324
296
  send: (plant) => plant.send
325
297
  }
326
298
  })
@@ -39,12 +39,12 @@ job is returning `target.from()`.
39
39
  // Redundant
40
40
  const handlers = {
41
41
  Start: (to) =>
42
- to.full.Running().resolve(({ target }) => target.from())
42
+ to.branch.Running().resolve(({ target }) => target.from())
43
43
  }
44
44
 
45
45
  // Preferred
46
46
  const handlers = {
47
- Start: (to) => to.full.Running()
47
+ Start: (to) => to.branch.Running()
48
48
  }
49
49
  ```
50
50
 
@@ -176,11 +176,11 @@ Model `Submit` as the component-facing event. Let a machine state own the work
176
176
  and its lifetime:
177
177
 
178
178
  ```ts
179
- machine.handle({
179
+ machine.handle({ states: {
180
180
  Editing: {
181
181
  on: {
182
182
  Submit: (to) =>
183
- to.full.Submitting().resolve(({ event, target }) =>
183
+ to.branch.Submitting().resolve(({ event, target }) =>
184
184
  target.from({ order: event.order })
185
185
  )
186
186
  }
@@ -190,17 +190,17 @@ machine.handle({
190
190
  from
191
191
  .effect("submit-order", ({ state }) => submitOrder(state.order))
192
192
  .onDone((to) =>
193
- to.full.Complete().resolve(({ output, target }) =>
193
+ to.branch.Complete().resolve(({ output, target }) =>
194
194
  target.from({ order: output })
195
195
  )
196
196
  )
197
197
  .onFailure((to) =>
198
- to.full.Failed().resolve(({ error, target }) =>
198
+ to.branch.Failed().resolve(({ error, target }) =>
199
199
  target.from({ message: String(error) })
200
200
  )
201
201
  )
202
202
  }
203
- })
203
+ } })
204
204
  ```
205
205
 
206
206
  `submitOrder` can use Effect services for the API request and analytics. The