@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.
Files changed (2) hide show
  1. package/README.md +42 -4
  2. 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 | `context: {} as MyContext` | casting in every handler |
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
- Cast the context type once at the constructor level. Handlers receive the typed context automatically. No casting needed per-handler.
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: {} as AuthContext,
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: { retryCount: 0, maxRetries: 3 } as MyContext,
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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mantaq/core",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "State machine runtime with actor model, event system, state hierarchy, effects, and virtual clock.",
5
5
  "license": "MIT",
6
6
  "repository": {