@demlik/tea 0.15.0 → 0.16.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 (115) hide show
  1. package/README.md +30 -7
  2. package/dist/agent/index.d.ts +49 -37
  3. package/dist/agent/index.js +12 -8
  4. package/dist/chunk-4LPY67E4.js +18 -0
  5. package/dist/chunk-4LPY67E4.js.map +1 -0
  6. package/dist/chunk-5XU6ZJO2.js +533 -0
  7. package/dist/chunk-5XU6ZJO2.js.map +1 -0
  8. package/dist/{chunk-AYS5N6WY.js → chunk-6NG4V5SB.js} +62 -172
  9. package/dist/chunk-6NG4V5SB.js.map +1 -0
  10. package/dist/{chunk-3B5X7SBB.js → chunk-7MGXQZPW.js} +11 -9
  11. package/dist/chunk-7MGXQZPW.js.map +1 -0
  12. package/dist/{chunk-V46HSVCK.js → chunk-BCTEOVOU.js} +3 -3
  13. package/dist/{chunk-V46HSVCK.js.map → chunk-BCTEOVOU.js.map} +1 -1
  14. package/dist/chunk-CH5KTOEZ.js +710 -0
  15. package/dist/chunk-CH5KTOEZ.js.map +1 -0
  16. package/dist/chunk-CIHSHLQX.js +64 -0
  17. package/dist/chunk-CIHSHLQX.js.map +1 -0
  18. package/dist/{chunk-TCLBCERH.js → chunk-EHUDXAKC.js} +238 -212
  19. package/dist/chunk-EHUDXAKC.js.map +1 -0
  20. package/dist/chunk-GOX2CYIM.js +380 -0
  21. package/dist/chunk-GOX2CYIM.js.map +1 -0
  22. package/dist/{chunk-22POWGFA.js → chunk-IVJP3GKJ.js} +46 -20
  23. package/dist/chunk-IVJP3GKJ.js.map +1 -0
  24. package/dist/{chunk-QPEVCJ5Q.js → chunk-MVZS5FDM.js} +13 -6
  25. package/dist/chunk-MVZS5FDM.js.map +1 -0
  26. package/dist/{chunk-VTQI2NHP.js → chunk-MYNLY3HP.js} +9 -8
  27. package/dist/chunk-MYNLY3HP.js.map +1 -0
  28. package/dist/{chunk-Q5CQTBFA.js → chunk-NSIAORRY.js} +20 -30
  29. package/dist/chunk-NSIAORRY.js.map +1 -0
  30. package/dist/{chunk-MYXFNWAJ.js → chunk-QNGYQMZB.js} +4 -4
  31. package/dist/chunk-QNGYQMZB.js.map +1 -0
  32. package/dist/chunk-WT5PBPLB.js +98 -0
  33. package/dist/chunk-WT5PBPLB.js.map +1 -0
  34. package/dist/{core-DnzqWy6u.d.ts → core-BbgtiXdg.d.ts} +231 -139
  35. package/dist/do/index.d.ts +37 -33
  36. package/dist/do/index.js +21 -14
  37. package/dist/do/index.js.map +1 -1
  38. package/dist/{durable-effects-CWzA-o2B.d.ts → durable-effects-7MTK_jnj.d.ts} +1 -1
  39. package/dist/effect/index.d.ts +151 -0
  40. package/dist/effect/index.js +98 -0
  41. package/dist/effect/index.js.map +1 -0
  42. package/dist/extension/index.d.ts +6 -7
  43. package/dist/extension/index.js +3 -2
  44. package/dist/extension/index.js.map +1 -1
  45. package/dist/flow/index.d.ts +68 -59
  46. package/dist/flow/index.js +32 -27
  47. package/dist/flow/index.js.map +1 -1
  48. package/dist/idempotency/index.d.ts +6 -7
  49. package/dist/idempotency/index.js +4 -2
  50. package/dist/{index-CSyMYDAB.d.ts → index-7vV8xmsD.d.ts} +1 -1
  51. package/dist/{index-KXqTRJyb.d.ts → index-Bz5Rh9Ga.d.ts} +19 -96
  52. package/dist/index-CjFDUBeq.d.ts +207 -0
  53. package/dist/{index-CuNO9m8i.d.ts → index-CkFYA9Y6.d.ts} +14 -16
  54. package/dist/{index-CA_7I5g9.d.ts → index-Cou86-GX.d.ts} +137 -246
  55. package/dist/{index-DrIRb_Ch.d.ts → index-DU6JpQ8G.d.ts} +1 -1
  56. package/dist/{index-HSYgG2Yt.d.ts → index-Xufl55W8.d.ts} +247 -211
  57. package/dist/{index-C2DwkjlP.d.ts → index-_dOR3xqA.d.ts} +57 -45
  58. package/dist/index.d.ts +121 -385
  59. package/dist/index.js +2 -1
  60. package/dist/jev/index.d.ts +133 -97
  61. package/dist/jev/index.js +205 -161
  62. package/dist/jev/index.js.map +1 -1
  63. package/dist/{machine-DGlPWCWI.d.ts → machine-BQzTV8On.d.ts} +171 -288
  64. package/dist/machine-viz/index.d.ts +5 -5
  65. package/dist/machine-viz/index.js +8 -4
  66. package/dist/machine-viz/index.js.map +1 -1
  67. package/dist/mem/index.d.ts +3 -4
  68. package/dist/mem/index.js +2 -1
  69. package/dist/mem/index.js.map +1 -1
  70. package/dist/node/index.d.ts +119 -78
  71. package/dist/node/index.js +52 -38
  72. package/dist/node/index.js.map +1 -1
  73. package/dist/paginate/index.d.ts +27 -22
  74. package/dist/paginate/index.js +6 -4
  75. package/dist/parity/index.d.ts +4 -5
  76. package/dist/parity/index.js +3 -2
  77. package/dist/parity/index.js.map +1 -1
  78. package/dist/pbt/index.d.ts +2 -2
  79. package/dist/pbt/index.js +2 -1
  80. package/dist/pbt/index.js.map +1 -1
  81. package/dist/persistence/index.d.ts +6 -7
  82. package/dist/persistence/index.js +5 -3
  83. package/dist/promise/index.d.ts +186 -0
  84. package/dist/promise/index.js +5 -0
  85. package/dist/promise/index.js.map +1 -0
  86. package/dist/{protocol-D8VbH-VH.d.ts → protocol-ClRZBhHQ.d.ts} +7 -8
  87. package/dist/react/index.d.ts +36 -17
  88. package/dist/react/index.js +27 -4
  89. package/dist/react/index.js.map +1 -1
  90. package/dist/resilience/index.d.ts +63 -456
  91. package/dist/resilience/index.js +37 -483
  92. package/dist/resilience/index.js.map +1 -1
  93. package/dist/{runtime-types-CMF13BBh.d.ts → runtime-types-CiVpC0HN.d.ts} +131 -388
  94. package/dist/testing/index.d.ts +22 -16
  95. package/dist/testing/index.js +15 -7
  96. package/dist/testing/index.js.map +1 -1
  97. package/dist/timing/index.d.ts +25 -22
  98. package/dist/timing/index.js +4 -3
  99. package/dist/timing/index.js.map +1 -1
  100. package/dist/{types-BseNPIhr.d.ts → types-LxZxGYda.d.ts} +2 -2
  101. package/dist/work-queue/index.d.ts +5 -6
  102. package/package.json +18 -5
  103. package/dist/chunk-22POWGFA.js.map +0 -1
  104. package/dist/chunk-3B5X7SBB.js.map +0 -1
  105. package/dist/chunk-66PY5WYF.js +0 -1816
  106. package/dist/chunk-66PY5WYF.js.map +0 -1
  107. package/dist/chunk-AYS5N6WY.js.map +0 -1
  108. package/dist/chunk-L3TND7LA.js +0 -97
  109. package/dist/chunk-L3TND7LA.js.map +0 -1
  110. package/dist/chunk-MYXFNWAJ.js.map +0 -1
  111. package/dist/chunk-Q5CQTBFA.js.map +0 -1
  112. package/dist/chunk-QPEVCJ5Q.js.map +0 -1
  113. package/dist/chunk-TCLBCERH.js.map +0 -1
  114. package/dist/chunk-VTQI2NHP.js.map +0 -1
  115. package/dist/index-ZCMhnVKN.d.ts +0 -272
@@ -1,10 +1,9 @@
1
1
  import { I as IdempotencyStore } from '../index-Bc40JWVl.js';
2
2
  export { a as IdempotencyEntry, e as evictExpired, i as initStore, r as recall, b as remember, s as seen } from '../index-Bc40JWVl.js';
3
- import { a as CmdOf, b as CmdDef } from '../core-DnzqWy6u.js';
4
- import { Q as QueueItem } from '../index-CSyMYDAB.js';
5
- import 'zod';
6
- import '../runtime-types-CMF13BBh.js';
7
- import 'better-result';
3
+ import { c as CmdOf, b as CmdDef } from '../core-BbgtiXdg.js';
4
+ import { Q as QueueItem } from '../index-7vV8xmsD.js';
5
+ import '@standard-schema/spec';
6
+ import '../runtime-types-CiVpC0HN.js';
8
7
 
9
8
  /**
10
9
  * A cached intake entry: either a key whose work is still in flight
@@ -69,7 +68,7 @@ declare function intakeProcessDef<P>(): CmdDef<"intake:process", {
69
68
  readonly itemId: string;
70
69
  /** The original payload, for the worker to process. */
71
70
  readonly payload: P;
72
- }, void, never, unknown>;
71
+ }, undefined, never>;
73
72
  type IntakeProcessCmd<P> = CmdOf<ReturnType<typeof intakeProcessDef<P>>>;
74
73
  /**
75
74
  * A duplicate of an already-completed key arrived — replay the cached result
@@ -80,7 +79,7 @@ declare function intakeReplayDef<R>(): CmdDef<"intake:replay", {
80
79
  readonly key: string;
81
80
  /** The result the ORIGINAL request produced, recalled from the cache. */
82
81
  readonly result: R;
83
- }, void, never, unknown>;
82
+ }, undefined, never>;
84
83
  type IntakeReplayCmd<R> = CmdOf<ReturnType<typeof intakeReplayDef<R>>>;
85
84
  /** The Cmd union the intake verbs emit. */
86
85
  type IntakeCmd<P, R> = IntakeProcessCmd<P> | IntakeReplayCmd<R>;
@@ -1,6 +1,8 @@
1
- export { createIntake, intakeProcessDef, intakeReplayDef } from '../chunk-VTQI2NHP.js';
1
+ export { createIntake, intakeProcessDef, intakeReplayDef } from '../chunk-MYNLY3HP.js';
2
2
  export { evictExpired, initStore, recall, remember, seen } from '../chunk-PR6TB3BL.js';
3
3
  import '../chunk-TXN2KFHF.js';
4
- import '../chunk-66PY5WYF.js';
4
+ import '../chunk-4LPY67E4.js';
5
+ import '../chunk-GOX2CYIM.js';
6
+ import '../chunk-CH5KTOEZ.js';
5
7
  //# sourceMappingURL=index.js.map
6
8
  //# sourceMappingURL=index.js.map
@@ -1,4 +1,4 @@
1
- import { S as Store } from './runtime-types-CMF13BBh.js';
1
+ import { S as Store } from './runtime-types-CiVpC0HN.js';
2
2
 
3
3
  /**
4
4
  * internal/work-queue — substrate-agnostic work-queue lifecycle on `Store<S>`.
@@ -1,89 +1,7 @@
1
- import { I as Interpret, C as Cmd } from './core-DnzqWy6u.js';
2
- import { S as SnapshotWriteCmd, a as SnapshotState, b as SnapshotStore, c as SnapshotSavedMsg, d as SnapshotFailedMsg, e as SnapshotLoadedMsg, f as SnapshotLoadFailedMsg, g as SnapshotLoadCmd } from './index-ZCMhnVKN.js';
3
- import { D as DeadlineExceeded, a as DeadlineSub } from './index-HSYgG2Yt.js';
4
- import { Q as QueueItem } from './index-CSyMYDAB.js';
5
-
6
- /**
7
- * internal/flow/monitored-run — a long-running operation that is BOTH staged and
8
- * watched: an ordered stage pipeline whose POSITION survives eviction, wrapped
9
- * by a no-progress safety deadline and (optionally) a periodic durable
10
- * checkpoint.
11
- *
12
- * This is the merge of two field-guide concerns the audit-agent machine
13
- * hand-wired together (`seed/audit-agent-machine`): the OUTER pipeline position
14
- * (`RunStage` — planning → testing → summarizing → reporting, replayed on cold
15
- * wake by re-emitting the one outstanding effect) and the no-progress safety
16
- * alarm (`subs.ts`, keyed on `progressSeq` so every progress event retires the
17
- * old alarm and arms a fresh one). `monitored-run` lifts both into one config-
18
- * driven knob with the uniform L2 contract `resilient-call` established:
19
- * `init()`, the verbs (`start` / `advance` / `progress` / `onDeadline` /
20
- * `boot`), `subs(state)`, and `handlers(ports)`.
21
- *
22
- * - It is **staged, optionally**. Omit `stages` and the run is single-shot:
23
- * `start` enters `running`, `advance` finishes it to `done`. Provide
24
- * `stages` and it becomes an ordered pipeline — each `advance` retires the
25
- * current stage and claims the next, finishing to `done` only when the last
26
- * stage is retired. The pipeline POSITION lives in the Model (a
27
- * `work-queue` of stage items), so an eviction mid-pipeline resumes at the
28
- * same stage via `boot` — the audit machine's "pipeline position survives
29
- * eviction" property, generalized.
30
- * - It is **watched**. While `running`, a no-progress deadline is armed at
31
- * `lastProgressAt + deadlineMs` and keyed on `progressSeq`. Every
32
- * `progress` / `advance` bumps the seq, so the substrate retires the old
33
- * alarm and arms a new one — a self-rearming watchdog. If the alarm fires
34
- * (`onDeadline`) the run goes `failed` with a deadline reason. Omit
35
- * `deadlineMs` → no watchdog (the `stale` phase is never reachable).
36
- * - It **checkpoints, optionally**. Provide `snapshotEvery` and every
37
- * progress unit is accounted to a `../snapshot` slice; once the cadence is
38
- * reached a checkpoint Cmd is emitted (interpreted by the spliced snapshot
39
- * `handlers`), so a crashed run restarts from the last durable checkpoint
40
- * rather than from zero. Omit it → no checkpointing.
41
- *
42
- * ## Why the run lifecycle is INLINE (run-tracker not extracted)
43
- *
44
- * The phase union (`idle` / `running` / `stale` / `done` / `failed` / `cancelled`, a
45
- * discriminated union carrying only each phase's own data) and its bookkeeping
46
- * (`runId`, `startedAt`, `lastProgressAt`, `progressSeq`) live directly on this
47
- * slice rather than behind a `run-tracker` sub-knob.
48
- * Extraction earns its keep when two independent axes vary; here the lifecycle
49
- * has exactly one anchoring axis (this run), so a separate module would be a
50
- * premature seam. The two genuinely independent axes — the safety DEADLINE and
51
- * the periodic SNAPSHOT — ARE extracted (`../deadline`, `../snapshot`), and the
52
- * stage list is a `../work-queue` slice (`QueueItem<Stage>[]`) whose every
53
- * status flip is DELEGATED to the blessed `../work-queue/ops` (`enqueueOp` /
54
- * `claimNextOp` / `markDoneOp`) — never re-rolled here. The lifecycle is the
55
- * glue between them.
56
- *
57
- * ## The two non-negotiables (canon)
58
- *
59
- * - **Durable** — the slice is plain data (the stage queue is a
60
- * JSON-serializable `QueueItem<Stage>[]`, the snapshot slice is plain
61
- * numbers), so it survives DO eviction / reload.
62
- * - **Replayable** — every transition is a pure verb returning new state +
63
- * Cmds; nothing here reads the clock or RNG. Time arrives as an `at` / `atMs`
64
- * parameter; the only clock read is `Date.now()` inside the snapshot
65
- * `handlers` port (the effect boundary), exactly as `../snapshot` stamps it.
66
- *
67
- * ## Typical wiring
68
- *
69
- * const run = createMonitoredRun<MyStage, MyState>({
70
- * stages: ["plan", "test", "report"],
71
- * deadlineMs: 10 * 60_000,
72
- * snapshotEvery: 25,
73
- * });
74
- *
75
- * init: (loaded) => loaded ? [loaded, []] : [{ run: run.init() }, []],
76
- * update: {
77
- * begin: (s, m) => lift(s, run.start(s.run, m.runId, m.at)),
78
- * advance: (s, m) => lift(s, run.advance(s.run, checkpoint(s), m.result, m.at)),
79
- * progress: (s, m) => lift(s, run.progress(s.run, checkpoint(s), m.at)),
80
- * run_deadline: (s, m) => lift(s, run.onDeadline(s.run, m)),
81
- * boot: (s, m) => lift(s, run.boot(s.run, m.at)),
82
- * },
83
- * subscriptions: (s) => run.subs(s.run),
84
- * subscribe: { deadline: subscribeDeadline },
85
- * interpret: run.handlers({ store: r2 }),
86
- */
1
+ import { b as CmdDef, T as TimerDeps, C as Cmd } from './core-BbgtiXdg.js';
2
+ import { S as SnapshotWriteCmd, a as SnapshotState, b as SnapshotSavedMsg } from './index-CjFDUBeq.js';
3
+ import { D as DeadlineExceeded, a as DeadlineSub } from './index-Xufl55W8.js';
4
+ import { Q as QueueItem } from './index-7vV8xmsD.js';
87
5
 
88
6
  /**
89
7
  * The monitored-run knob. EVERY field is optional:
@@ -263,11 +181,6 @@ type StageResult = {
263
181
  type MonitoredRunCmd<V> = SnapshotWriteCmd<V>;
264
182
  /** The deadline Msg the safety alarm dispatches when the watchdog fires. */
265
183
  type MonitoredRunTimerMsg = DeadlineExceeded;
266
- /** Ports the consumer supplies to `handlers` — just the checkpoint store. */
267
- interface MonitoredRunPorts<V> {
268
- /** Where periodic checkpoints are written (R2 / KV / DO sub-key / Map). */
269
- readonly store: SnapshotStore<V>;
270
- }
271
184
  /**
272
185
  * Build a monitored-run knob from `config`. `Stage` is the consumer's stage
273
186
  * identifier type; `V` is the consumer's checkpoint value type (what
@@ -275,9 +188,19 @@ interface MonitoredRunPorts<V> {
275
188
  *
276
189
  * No `rng` parameter: unlike `resilient-call`, nothing here jitters — the only
277
190
  * external inputs are time (an `at` / `atMs` parameter) and the consumer's
278
- * stage outcomes. The clock is read only inside the snapshot `handlers` port.
191
+ * stage outcomes.
279
192
  */
280
193
  declare function createMonitoredRun<Stage, V = unknown>(config?: MonitoredRunConfig<Stage>): {
194
+ /** The `snapshot_write` Cmd def — list it in `cmds` when checkpointing. */
195
+ checkpoint: CmdDef<"snapshot_write", {
196
+ readonly key: string;
197
+ readonly seq: number;
198
+ readonly at: number;
199
+ readonly payload: V;
200
+ }, undefined, {
201
+ readonly [detail: string]: unknown;
202
+ readonly _tag: "snapshot_write_failed";
203
+ }>;
281
204
  init: () => MonitoredRunState<Stage>;
282
205
  start: (s: MonitoredRunState<Stage>, runId: string, at: number) => readonly [MonitoredRunState<Stage>, readonly MonitoredRunCmd<V>[]];
283
206
  progress: (s: MonitoredRunState<Stage>, payload: V, at: number) => readonly [MonitoredRunState<Stage>, readonly MonitoredRunCmd<V>[]];
@@ -286,9 +209,9 @@ declare function createMonitoredRun<Stage, V = unknown>(config?: MonitoredRunCon
286
209
  advance: (s: MonitoredRunState<Stage>, payload: V, result: StageResult, at: number) => readonly [MonitoredRunState<Stage>, readonly MonitoredRunCmd<V>[]];
287
210
  onDeadline: (s: MonitoredRunState<Stage>, msg: MonitoredRunTimerMsg) => readonly [MonitoredRunState<Stage>, readonly MonitoredRunCmd<V>[]];
288
211
  boot: (s: MonitoredRunState<Stage>, at: number) => readonly [MonitoredRunState<Stage>, readonly MonitoredRunCmd<V>[]];
289
- confirmSnapshot: (s: MonitoredRunState<Stage>, msg: SnapshotSavedMsg) => MonitoredRunState<Stage>;
290
- subs: (s: MonitoredRunState<Stage>) => readonly DeadlineSub[];
291
- handlers: (ports: MonitoredRunPorts<V>) => Interpret<SnapshotSavedMsg | SnapshotFailedMsg | SnapshotLoadedMsg<V> | SnapshotLoadFailedMsg, SnapshotWriteCmd<V> | SnapshotLoadCmd, unknown>;
212
+ confirmSnapshot: (s: MonitoredRunState<Stage>, msg: SnapshotSavedMsg<V>) => MonitoredRunState<Stage>;
213
+ deadlines: (s: MonitoredRunState<Stage>) => readonly DeadlineSub[];
214
+ timer: (s: MonitoredRunState<Stage>) => TimerDeps<MonitoredRunTimerMsg> | null;
292
215
  };
293
216
  /**
294
217
  * Lift a knob result `[slice, cmds]` into a host `[State, cmds]` where the
@@ -300,4 +223,4 @@ declare function liftRun<S extends {
300
223
  run: MonitoredRunState<Stage>;
301
224
  }, Stage, C extends Cmd>(state: S, [slice, cmds]: readonly [MonitoredRunState<Stage>, readonly C[]]): readonly [S, readonly C[]];
302
225
 
303
- export { type EndedRun as E, type MonitoredRunCmd as M, type RunFailure as R, type StageResult as S, type MonitoredRunState as a, type MonitoredRunConfig as b, type MonitoredRunPorts as c, type MonitoredRunTimerMsg as d, createMonitoredRun as e, liftRun as l };
226
+ export { type EndedRun as E, type MonitoredRunCmd as M, type RunFailure as R, type StageResult as S, type MonitoredRunState as a, type MonitoredRunConfig as b, type MonitoredRunTimerMsg as c, createMonitoredRun as d, liftRun as l };
@@ -0,0 +1,207 @@
1
+ import { c as CmdOf, b as CmdDef, e as SettledOk, f as SettledErr, g as TaggedError, C as Cmd } from './core-BbgtiXdg.js';
2
+
3
+ /**
4
+ * The snapshot knob.
5
+ *
6
+ * `every` is the cadence: a checkpoint is written once `every` units of
7
+ * progress have accumulated since the last one (see `record`). It is the
8
+ * `snapshotEvery` field `monitored-run` threads through to this module.
9
+ *
10
+ * `key` is the store key the checkpoint is written under. A single machine
11
+ * keeps a single rolling checkpoint, so the key is fixed at config time rather
12
+ * than passed per write — the latest checkpoint overwrites the previous one
13
+ * (R2/KV are last-write-wins on a key). Defaults to `"@@snapshot"`, mirroring
14
+ * `doStore`'s `@@state` default-key convention.
15
+ */
16
+ interface SnapshotConfig {
17
+ /**
18
+ * Write a checkpoint once this many progress units have accumulated since the
19
+ * last write. `every: 1` checkpoints on every `record`; a value `<= 0` is
20
+ * treated as `1` (every progress unit triggers a write) — a non-positive
21
+ * cadence has no other sensible meaning, and clamping keeps `record` from
22
+ * never checkpointing on a misconfigured `0`.
23
+ */
24
+ readonly every: number;
25
+ /** Store key the rolling checkpoint is written under. Defaults to `"@@snapshot"`. */
26
+ readonly key?: string;
27
+ }
28
+ /**
29
+ * The snapshot bookkeeping slice. Plain data — JSON-serializable so it rides
30
+ * inside the consumer's `Store<S>` blob and survives DO eviction / reload (the
31
+ * "durable" non-negotiable). It carries NO payload: the thing being
32
+ * checkpointed lives elsewhere in the machine's state; this slice only tracks
33
+ * *when* to checkpoint and *what has been acknowledged durable*.
34
+ *
35
+ * - `sinceLast` — progress units accumulated since the last checkpoint
36
+ * DECISION (reset to 0 the moment `record` decides to write, not when the
37
+ * write lands). The cadence counter.
38
+ * - `seq` — monotonic checkpoint sequence. Bumped each time a write is
39
+ * emitted, so the write Cmd and its later acknowledgement can be matched
40
+ * even if several are in flight. The "version" of the latest emitted
41
+ * checkpoint.
42
+ * - `lastSavedSeq` — the highest `seq` whose write the host has confirmed
43
+ * durable (via `confirm`). `null` until the first confirmed write. The gap
44
+ * `seq - lastSavedSeq` is the "writes emitted but not yet acknowledged"
45
+ * depth — observable for backpressure / health.
46
+ * - `lastSavedAt` — the `at` of the most recent confirmed write (epoch ms),
47
+ * `null` until the first. Carried for observability, never compared inside
48
+ * a verb (time is injected, never read here).
49
+ */
50
+ interface SnapshotState {
51
+ readonly sinceLast: number;
52
+ readonly seq: number;
53
+ readonly lastSavedSeq: number | null;
54
+ readonly lastSavedAt: number | null;
55
+ }
56
+ /**
57
+ * The checkpoint-write Cmd a `record` / `force` decision emits. Plain data
58
+ * (invariant 1 — Cmds are data, not closures): it carries the `payload` to
59
+ * write, the `key` to write it under, the `seq` it represents, and the `at` it
60
+ * was decided.
61
+ *
62
+ * `payload` is `V` — the consumer's serializable checkpoint value (the run
63
+ * state, the cursor, whatever the machine wants restartable). The verb takes it
64
+ * as an argument so the snapshot slice never has to hold a copy of the
65
+ * machine's domain state.
66
+ *
67
+ * `Cmd.define`d (ADR 0014, 0021), minted per `V` by a factory the way
68
+ * `workflowActivityDef<A>()` is, since the payload type is the knob's own
69
+ * parameter. The handler you write puts the payload in your store and returns
70
+ * `ok(undefined)`, or `err({ _tag: "snapshot_write_failed", … })`; the engine
71
+ * mints `snapshot_write_ok` / `snapshot_write_err`.
72
+ */
73
+ declare function snapshotWriteDef<V>(): CmdDef<"snapshot_write", {
74
+ readonly key: string;
75
+ readonly seq: number;
76
+ readonly at: number;
77
+ readonly payload: V;
78
+ }, undefined, {
79
+ readonly [detail: string]: unknown;
80
+ readonly _tag: "snapshot_write_failed";
81
+ }>;
82
+ type SnapshotWriteCmd<V> = CmdOf<ReturnType<typeof snapshotWriteDef<V>>>;
83
+ /**
84
+ * The checkpoint-READ Cmd a `requestLoad` decision emits — the recovery half of
85
+ * the module. Plain data (invariant 1): it carries only the `key` to read. The
86
+ * handler you write reads the store and returns `ok(payload)` — `null` when
87
+ * the store has no checkpoint under `key` — or
88
+ * `err({ _tag: "snapshot_load_failed", … })`.
89
+ *
90
+ * Distinct from `snapshot_write`: a write carries a payload OUT to the store; a
91
+ * load asks the store to hand a payload BACK. The consumer dispatches the
92
+ * triggering Msg once on resume (a `boot` Msg, never `init`'s rehydrate branch —
93
+ * invariant 2), folds the recovered payload into its run slice, then continues.
94
+ */
95
+ declare function snapshotLoadDef<V>(): CmdDef<"snapshot_load", {
96
+ readonly key: string;
97
+ }, V | null, {
98
+ readonly [detail: string]: unknown;
99
+ readonly _tag: "snapshot_load_failed";
100
+ }>;
101
+ type SnapshotLoadCmd<V = unknown> = CmdOf<ReturnType<typeof snapshotLoadDef<V>>>;
102
+ /**
103
+ * The Msg the engine mints when a write lands. Fold it through
104
+ * `confirm(state, msg)` to advance `lastSavedSeq` / `lastSavedAt`. It carries
105
+ * the write Cmd, so `msg.cmd.seq` matches the emitting decision and a late
106
+ * acknowledgement of an older write can never regress a newer confirmed one.
107
+ */
108
+ type SnapshotSavedMsg<V = unknown> = SettledOk<"snapshot_write", SnapshotWriteCmd<V>, undefined>;
109
+ /**
110
+ * The Msg the engine mints when a write fails. Errors are data (never
111
+ * swallowed): the consumer's reducer decides whether a failed checkpoint is
112
+ * fatal, retryable (compose `@demlik/tea/retry-backoff`), or merely logged.
113
+ * `msg.cmd.seq` identifies which checkpoint failed.
114
+ */
115
+ type SnapshotFailedMsg<V = unknown> = SettledErr<"snapshot_write", SnapshotWriteCmd<V>, TaggedError<"snapshot_write_failed">>;
116
+ /**
117
+ * The Msg the engine mints when a read resolves. `value` is the recovered
118
+ * checkpoint, or `null` when the store has no checkpoint under `key` (fresh
119
+ * boot — never checkpointed, or a wiped store). `null` is NOT an error — "no
120
+ * checkpoint yet" is a normal first-run state, distinct from "the read failed"
121
+ * (`snapshot_load_err`).
122
+ */
123
+ type SnapshotLoadedMsg<V> = SettledOk<"snapshot_load", SnapshotLoadCmd<V>, V | null>;
124
+ /**
125
+ * The Msg the engine mints when a read fails. Errors are data: a failed
126
+ * recovery read is a visible signal the consumer's reducer decides on
127
+ * (fatal-abort, retry, or boot-fresh-and-log). Kept apart from a `null` load
128
+ * so "the store has no checkpoint" can never masquerade as "the read failed".
129
+ */
130
+ type SnapshotLoadFailedMsg<V = unknown> = SettledErr<"snapshot_load", SnapshotLoadCmd<V>, TaggedError<"snapshot_load_failed">>;
131
+ /**
132
+ * The bundle `createSnapshot` returns: plain functions plus the two Cmd defs to
133
+ * list in `cmds`. There are no subs: a checkpoint is driven by progress (a
134
+ * `record` call from the consumer's reducer), not by a timer.
135
+ */
136
+ interface SnapshotKnob<V> {
137
+ /** The `snapshot_write` Cmd def — list it in the machine's `cmds`. */
138
+ readonly write: ReturnType<typeof snapshotWriteDef<V>>;
139
+ /** The `snapshot_load` Cmd def — list it in the machine's `cmds`. */
140
+ readonly load: ReturnType<typeof snapshotLoadDef<V>>;
141
+ /** The initial slice — zero progress, no checkpoint emitted or confirmed yet. */
142
+ init(): SnapshotState;
143
+ /**
144
+ * Account one unit of progress. PURE. Increments `sinceLast`; once the
145
+ * accumulated count reaches the configured cadence, DECIDES to checkpoint:
146
+ * resets `sinceLast` to 0, bumps `seq`, and emits a single `snapshot_write`
147
+ * Cmd carrying `payload` + `key` + the new `seq` + `at`. Below the cadence it
148
+ * returns the bumped slice and NO Cmd.
149
+ *
150
+ * `payload` is the checkpoint value to persist (the consumer's domain state);
151
+ * `at` is the caller-stamped time the progress occurred (epoch ms) — injected,
152
+ * never read from the clock here.
153
+ */
154
+ record(state: SnapshotState, payload: V, at: number): readonly [SnapshotState, readonly SnapshotWriteCmd<V>[]];
155
+ /**
156
+ * Force a checkpoint regardless of the cadence counter. PURE. Always bumps
157
+ * `seq`, resets `sinceLast`, and emits the write Cmd. Use on a phase boundary
158
+ * the cadence would otherwise miss — e.g. "the run just finished, checkpoint
159
+ * the terminal state now" — so the last durable checkpoint is the final state,
160
+ * not whatever the modulo happened to land on.
161
+ */
162
+ force(state: SnapshotState, payload: V, at: number): readonly [SnapshotState, readonly SnapshotWriteCmd<V>[]];
163
+ /**
164
+ * Record a confirmed durable write. PURE. Advances `lastSavedSeq` /
165
+ * `lastSavedAt` to the acknowledged write's `seq` / `at`, but only forward:
166
+ * an out-of-order acknowledgement of an OLDER `seq` (a slow write landing
167
+ * after a newer one) is ignored, so the confirmed watermark is monotonic.
168
+ * Fold the `snapshot_write_ok` Msg here.
169
+ *
170
+ * Returns the reducer-cell shape `readonly [SnapshotState, readonly Cmd[]]`
171
+ * (with `Cmd.none` — an ack emits nothing) for uniformity with every sibling
172
+ * knob, so it drops straight into a `snapshot_write_ok` cell.
173
+ */
174
+ confirm(state: SnapshotState, msg: SnapshotSavedMsg<V>): readonly [SnapshotState, readonly Cmd[]];
175
+ /**
176
+ * Resume after a reload. PURE. Resets `sinceLast` to 0 because any
177
+ * un-checkpointed progress accumulated before the crash is moot — recovery
178
+ * resumes from the last DURABLE checkpoint, so the cadence counter starts
179
+ * fresh from there. `seq` / `lastSavedSeq` / `lastSavedAt` are preserved
180
+ * (they describe what is durable, which a reload does not change). Call from
181
+ * a `boot` Msg the host dispatches once after `run(...)`, never from `init`'s
182
+ * rehydrate branch (invariant 2).
183
+ *
184
+ * Returns the reducer-cell shape `readonly [SnapshotState, readonly Cmd[]]`
185
+ * (with `Cmd.none` — a resume emits nothing) so it drops straight into a
186
+ * `snapshot_load_ok` cell.
187
+ */
188
+ boot(state: SnapshotState): readonly [SnapshotState, readonly Cmd[]];
189
+ /**
190
+ * Ask the store for the durable checkpoint. PURE. Emits a single
191
+ * `snapshot_load` Cmd carrying the configured `key`; returns the slice
192
+ * UNCHANGED (a read decides nothing about the cadence bookkeeping — only the
193
+ * later `snapshot_load_ok` fold, via `boot`, touches the slice). This is the
194
+ * entry point of the recovery loop: the consumer dispatches a `boot` Msg once
195
+ * after `run(...)` and folds `requestLoad` there.
196
+ */
197
+ requestLoad(state: SnapshotState): readonly [SnapshotState, readonly SnapshotLoadCmd<V>[]];
198
+ }
199
+ /**
200
+ * Create a snapshot knob from a `SnapshotConfig`. The returned bundle is spliced
201
+ * into a consumer machine: `init()` seeds the slice, `record` / `force` are
202
+ * folded in reducer cells, `confirm` / `boot` handle the acknowledgement +
203
+ * resume Msgs, and `write` / `load` are the Cmd defs whose handlers you write.
204
+ */
205
+ declare function createSnapshot<V>(config: SnapshotConfig): SnapshotKnob<V>;
206
+
207
+ export { type SnapshotWriteCmd as S, type SnapshotState as a, type SnapshotSavedMsg as b, type SnapshotConfig as c, type SnapshotFailedMsg as d, type SnapshotKnob as e, type SnapshotLoadCmd as f, type SnapshotLoadFailedMsg as g, type SnapshotLoadedMsg as h, createSnapshot as i, snapshotWriteDef as j, snapshotLoadDef as s };
@@ -1,5 +1,5 @@
1
- import { C as Cmd, P as Port } from './core-DnzqWy6u.js';
2
- import { Q as QueueItem } from './index-CSyMYDAB.js';
1
+ import { C as Cmd, P as Port } from './core-BbgtiXdg.js';
2
+ import { Q as QueueItem } from './index-7vV8xmsD.js';
3
3
 
4
4
  /**
5
5
  * internal/flow/fan-out — scatter-gather over a bounded-concurrency work list.
@@ -54,15 +54,13 @@ import { Q as QueueItem } from './index-CSyMYDAB.js';
54
54
  * is supplied by the caller's `idOf` so `scatter` is deterministic, and the
55
55
  * launch effects are plain data the runtime performs (invariant 3).
56
56
  *
57
- * `handlers(ports)` is the I/O splice: it wires the runtime side that actually
58
- * runs each launched `of(item)` Cmd. Because `of` already produces a fully
59
- * caller-defined Cmd, fan-out does NOT own the interpret handler for it — the
60
- * consumer's own `interpret` performs the effect and routes Ok/Err back to the
61
- * `itemOk` / `itemErr` verbs (the same shape resilient-fetch uses with
62
- * `tryInterpret`). `handlers` therefore exposes the one piece fan-out CAN
63
- * pre-wire generically: emitting the gathered batch onto a typed Port when the
64
- * batch completes, for consumers that observe completion out-of-band rather
65
- * than through the `join` Cmd.
57
+ * Fan-out does NOT own the interpret handler for a launched `of(item)` Cmd: `of`
58
+ * already produces a fully caller-defined Cmd, so the consumer's own
59
+ * `interpret` performs the effect and routes Ok/Err back to the `itemOk` /
60
+ * `itemErr` verbs. `completion(ports)` is the one piece fan-out CAN pre-wire
61
+ * generically: emitting the gathered batch onto a typed Port when the batch
62
+ * completes, for consumers that observe completion out-of-band rather than
63
+ * through the `join` Cmd.
66
64
  *
67
65
  * NOT a substrate primitive: it depends only on `../work-queue`'s pure types +
68
66
  * blessed ops and the core `Cmd` / `Port` types. Internal since #48 — not
@@ -162,7 +160,7 @@ interface FanOutState<I, R> {
162
160
  *
163
161
  * Field optionality follows the resilient-call rule — omit `join`, omit the
164
162
  * completion Cmd (a consumer that observes `isComplete` itself, or wires the
165
- * `handlers` Port, doesn't need one).
163
+ * `completion` Port, doesn't need one).
166
164
  */
167
165
  interface FanOutConfig<I, R, C extends Cmd, J extends Cmd> {
168
166
  /** Max in-flight `of(item)` effects. Clamped to `>= 1` (a 0/negative cap would deadlock). */
@@ -180,7 +178,7 @@ interface FanOutConfig<I, R, C extends Cmd, J extends Cmd> {
180
178
  /**
181
179
  * Optional: fold the gathered done-results into a single completion Cmd,
182
180
  * fired exactly once at the transition that empties `pending` + `running`.
183
- * Omit to drive completion off `isComplete` (or the `handlers` Port) instead.
181
+ * Omit to drive completion off `isComplete` (or the `completion` Port) instead.
184
182
  */
185
183
  readonly join?: (results: readonly R[]) => J;
186
184
  }
@@ -199,7 +197,7 @@ declare function initFanOut<I, R>(): FanOutState<I, R>;
199
197
  declare function isComplete<I, R>(state: FanOutState<I, R>): boolean;
200
198
  /**
201
199
  * Optional Port for observing batch completion out-of-band. When a consumer
202
- * passes `handlers(ports)` a `complete` Port, the runtime side can emit the
200
+ * passes `completion(ports)` a `complete` Port, the runtime side can emit the
203
201
  * gathered results onto it the moment `isComplete` flips true — distinct from
204
202
  * the in-band `join` Cmd (which threads back through `update`). Use the Port
205
203
  * for "tell the outside world the batch finished" without folding a completion
@@ -212,7 +210,7 @@ interface FanOutPorts<R> {
212
210
  /**
213
211
  * Build the fan-out knob from `config`. Returns the uniform combinator
214
212
  * contract: `init` for the owned slice, the three settle verbs + the derived
215
- * `isComplete`, and `handlers` for the optional out-of-band completion Port.
213
+ * `isComplete`, and `completion` for the optional out-of-band completion Port.
216
214
  *
217
215
  * No `subs` — fan-out runs no timers (the spec pins "Subs: none"). The
218
216
  * per-item effect's lifetime is owned by the consumer's `interpret`, and
@@ -225,7 +223,7 @@ declare function createFanOut<I, R, C extends Cmd = Cmd, J extends Cmd = Cmd>(co
225
223
  itemOk: (state: FanOutState<I, R>, id: string, result: R) => readonly [FanOutState<I, R>, readonly (C | J)[]];
226
224
  itemErr: (state: FanOutState<I, R>, id: string, error: unknown) => readonly [FanOutState<I, R>, readonly (C | J)[]];
227
225
  isComplete: typeof isComplete;
228
- handlers: (ports: FanOutPorts<R>) => {
226
+ completion: (ports: FanOutPorts<R>) => {
229
227
  /**
230
228
  * Emit the gathered done-results onto `ports.complete` iff `state` is a
231
229
  * just-completed batch. Idempotency is the caller's concern (call it once