@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/agent-guide.md
CHANGED
|
@@ -22,12 +22,12 @@ const CounterState = Schema.TaggedUnion({
|
|
|
22
22
|
Running: { count: Schema.Number }
|
|
23
23
|
})
|
|
24
24
|
|
|
25
|
-
export const CounterStates = Machine.
|
|
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.
|
|
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
|
-
|
|
40
|
+
root: CounterStates,
|
|
41
41
|
events: CounterEvents,
|
|
42
|
-
|
|
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.
|
|
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.
|
|
52
|
-
Stop: (to) => to.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
298
|
-
events: Machine.
|
|
299
|
-
|
|
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.
|
|
307
|
-
.onFailure((to) => to.
|
|
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.
|
|
360
|
-
.onFailure((to) => to.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
394
|
-
rejected: { target: to.
|
|
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
|
-
|
|
43
|
-
|
|
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 {
|
|
47
|
-
import {
|
|
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
|
|
52
|
-
|
|
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
|
-
<
|
|
67
|
-
{
|
|
68
|
-
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
87
|
-
|
|
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
|
-
|
|
75
|
+
Render a typed path in the smallest component that needs it:
|
|
102
76
|
|
|
103
77
|
```tsx
|
|
104
|
-
import {
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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
|
|
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.
|
|
295
|
+
state: (plant) => plant.result,
|
|
324
296
|
send: (plant) => plant.send
|
|
325
297
|
}
|
|
326
298
|
})
|
package/docs/machine-review.md
CHANGED
|
@@ -39,12 +39,12 @@ job is returning `target.from()`.
|
|
|
39
39
|
// Redundant
|
|
40
40
|
const handlers = {
|
|
41
41
|
Start: (to) =>
|
|
42
|
-
to.
|
|
42
|
+
to.branch.Running().resolve(({ target }) => target.from())
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
// Preferred
|
|
46
46
|
const handlers = {
|
|
47
|
-
Start: (to) => to.
|
|
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.
|
|
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.
|
|
193
|
+
to.branch.Complete().resolve(({ output, target }) =>
|
|
194
194
|
target.from({ order: output })
|
|
195
195
|
)
|
|
196
196
|
)
|
|
197
197
|
.onFailure((to) =>
|
|
198
|
-
to.
|
|
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
|