@mantaq/core 0.5.0 → 0.5.2
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 +42 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ npm install @mantaq/core
|
|
|
12
12
|
|
|
13
13
|
| Pattern | Correct | Anti-pattern |
|
|
14
14
|
| --------------- | -------------------------------------------------- | ------------------------------------- |
|
|
15
|
-
| Type context | `
|
|
15
|
+
| Type context | `const c: MyContext = {...}`, `context: c` | `as` cast on the context value |
|
|
16
16
|
| Write context | `const s = context.get(); s.x = y; context.set(s)` | mutate without `set()` |
|
|
17
17
|
| Type events | `event("ID")<Payload>()` | `event("ID")()` without generic |
|
|
18
18
|
| Send events | `actor.send(signInEvent.create(data))` | raw `{ type: "ID", payload }` objects |
|
|
@@ -81,7 +81,7 @@ See [anderscan.github.io/mantaq](https://anderscan.github.io/mantaq) for full do
|
|
|
81
81
|
|
|
82
82
|
### Typed Actor Context
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
Declare the context value with its type once, then pass it to the constructor. Handlers receive the typed context automatically. `as` on the context is an anti-pattern: it hides missing fields.
|
|
85
85
|
|
|
86
86
|
```ts
|
|
87
87
|
interface AuthContext {
|
|
@@ -89,11 +89,13 @@ interface AuthContext {
|
|
|
89
89
|
phoneNumber?: string;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
const initialContext: AuthContext = {};
|
|
93
|
+
|
|
92
94
|
const actor = new Actor({
|
|
93
95
|
inputs: [signInEvent],
|
|
94
96
|
states: [idleState, signingInState],
|
|
95
97
|
initial: idleState,
|
|
96
|
-
context:
|
|
98
|
+
context: initialContext,
|
|
97
99
|
setup: (m) => {
|
|
98
100
|
m.on(idleState, signInEvent, (event, opts) => {
|
|
99
101
|
// ✅ typed — context.get()/set() flow the constructor type
|
|
@@ -299,12 +301,13 @@ const doneEvent = event("WORK_DONE")();
|
|
|
299
301
|
const failedEvent = event("WORK_FAILED")<{ error: string }>();
|
|
300
302
|
|
|
301
303
|
function createActor() {
|
|
304
|
+
const initialContext: MyContext = { retryCount: 0, maxRetries: 3 };
|
|
302
305
|
return new Actor({
|
|
303
306
|
inputs: [startEvent],
|
|
304
307
|
internal: [doneEvent, failedEvent],
|
|
305
308
|
states: [idleState, workingState, doneState, errorState],
|
|
306
309
|
initial: idleState,
|
|
307
|
-
context:
|
|
310
|
+
context: initialContext,
|
|
308
311
|
setup: (m) => {
|
|
309
312
|
m.effect(workingState, {
|
|
310
313
|
name: "attemptWork",
|
|
@@ -533,6 +536,41 @@ m.on(idleState, submitEvent, (event, opts) => {
|
|
|
533
536
|
});
|
|
534
537
|
```
|
|
535
538
|
|
|
539
|
+
### Reading state in a UI
|
|
540
|
+
|
|
541
|
+
`actor.state.name` is the typed state name: a union of your declared states plus `"__error"`. Read it, not `snapshot().path[0]`, which is an untyped `string`.
|
|
542
|
+
|
|
543
|
+
```ts
|
|
544
|
+
const render = () => show(actor.state.name);
|
|
545
|
+
actor.on("change", { fn: render });
|
|
546
|
+
render();
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
Two different failures need two different responses:
|
|
550
|
+
|
|
551
|
+
- **Dead machine.** `actor.state.name === "__error"` (details in `snapshot().error`). A handler threw and the actor is terminal. Create a new actor and offer Reload.
|
|
552
|
+
- **Failed state the machine models.** An `error` state reached through a failure event is an ordinary state. Render it like any other state.
|
|
553
|
+
|
|
554
|
+
`recover()` is not for UIs: it breaks determinism. Recreate the actor instead.
|
|
555
|
+
|
|
556
|
+
To bind any actor without naming `Actor`'s five generics, type the binder on the parts it uses:
|
|
557
|
+
|
|
558
|
+
```ts
|
|
559
|
+
import type { Snapshot } from "@mantaq/core";
|
|
560
|
+
|
|
561
|
+
function bind<
|
|
562
|
+
A extends {
|
|
563
|
+
state: { name: string };
|
|
564
|
+
on(event: "change", options: { fn: () => void }): () => void;
|
|
565
|
+
snapshot(): Snapshot<unknown>;
|
|
566
|
+
},
|
|
567
|
+
>(actor: A, render: (name: A["state"]["name"]) => void): () => void {
|
|
568
|
+
const paint = () => render(actor.state.name);
|
|
569
|
+
paint();
|
|
570
|
+
return actor.on("change", { fn: paint });
|
|
571
|
+
}
|
|
572
|
+
```
|
|
573
|
+
|
|
536
574
|
### Any Handler
|
|
537
575
|
|
|
538
576
|
Use `onAny` to intercept events across all states. Useful for universal error handling, cleanup, or logging.
|