@demlik/tea 0.21.0 → 0.22.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 (126) hide show
  1. package/dist/agent/index.d.ts +32 -19
  2. package/dist/agent/index.js +12 -10
  3. package/dist/{chunk-VM7PMFV5.js → chunk-2A5XTKQQ.js} +142 -19
  4. package/dist/chunk-2A5XTKQQ.js.map +1 -0
  5. package/dist/{chunk-EB5OMOXX.js → chunk-7K7SEYCH.js} +16 -15
  6. package/dist/chunk-7K7SEYCH.js.map +1 -0
  7. package/dist/{chunk-GAVLND5C.js → chunk-A3QPMX5M.js} +17 -10
  8. package/dist/chunk-A3QPMX5M.js.map +1 -0
  9. package/dist/{chunk-VHKDUBXC.js → chunk-BVFS3ICC.js} +3 -3
  10. package/dist/{chunk-VHKDUBXC.js.map → chunk-BVFS3ICC.js.map} +1 -1
  11. package/dist/{chunk-4I2LCQKB.js → chunk-CPE5ZQXQ.js} +3 -3
  12. package/dist/{chunk-4I2LCQKB.js.map → chunk-CPE5ZQXQ.js.map} +1 -1
  13. package/dist/chunk-DEAPNMKO.js +11 -0
  14. package/dist/chunk-DEAPNMKO.js.map +1 -0
  15. package/dist/{chunk-BFSZ4L6Y.js → chunk-ETRNSJCR.js} +4 -4
  16. package/dist/{chunk-BFSZ4L6Y.js.map → chunk-ETRNSJCR.js.map} +1 -1
  17. package/dist/chunk-FXWDDNGD.js +98 -0
  18. package/dist/chunk-FXWDDNGD.js.map +1 -0
  19. package/dist/chunk-GFGOQIEC.js +10 -0
  20. package/dist/chunk-GFGOQIEC.js.map +1 -0
  21. package/dist/{chunk-YDF6P74H.js → chunk-IAHEYPYJ.js} +3 -3
  22. package/dist/{chunk-YDF6P74H.js.map → chunk-IAHEYPYJ.js.map} +1 -1
  23. package/dist/{chunk-YCJPLPQQ.js → chunk-J5JNLXO7.js} +3 -3
  24. package/dist/{chunk-YCJPLPQQ.js.map → chunk-J5JNLXO7.js.map} +1 -1
  25. package/dist/{chunk-MBQR3LFW.js → chunk-NFOGYDRS.js} +5 -5
  26. package/dist/{chunk-MBQR3LFW.js.map → chunk-NFOGYDRS.js.map} +1 -1
  27. package/dist/{chunk-Z7IL735U.js → chunk-R2YNDRGL.js} +3 -3
  28. package/dist/{chunk-Z7IL735U.js.map → chunk-R2YNDRGL.js.map} +1 -1
  29. package/dist/{chunk-ZV4WGW74.js → chunk-R5NXOPL2.js} +3 -3
  30. package/dist/{chunk-ZV4WGW74.js.map → chunk-R5NXOPL2.js.map} +1 -1
  31. package/dist/chunk-RMBZ7YH7.js +52 -0
  32. package/dist/chunk-RMBZ7YH7.js.map +1 -0
  33. package/dist/{chunk-HU3Y2AZY.js → chunk-SHXUSTHM.js} +62 -15
  34. package/dist/chunk-SHXUSTHM.js.map +1 -0
  35. package/dist/{chunk-QIE4Y6BK.js → chunk-UGTC33VH.js} +4 -4
  36. package/dist/{chunk-QIE4Y6BK.js.map → chunk-UGTC33VH.js.map} +1 -1
  37. package/dist/{chunk-6NIQIUGR.js → chunk-VH5MYJ2K.js} +4 -4
  38. package/dist/{chunk-6NIQIUGR.js.map → chunk-VH5MYJ2K.js.map} +1 -1
  39. package/dist/{chunk-W6XWI6QE.js → chunk-WZEDIUWR.js} +8 -7
  40. package/dist/chunk-WZEDIUWR.js.map +1 -0
  41. package/dist/{chunk-TMYUDZLE.js → chunk-ZRRET3RP.js} +11 -11
  42. package/dist/chunk-ZRRET3RP.js.map +1 -0
  43. package/dist/{core-BKJW1Kmq.d.ts → core-DNDAEhO2.d.ts} +7 -10
  44. package/dist/devtools/index.d.ts +4 -4
  45. package/dist/do/index.d.ts +98 -35
  46. package/dist/do/index.js +40 -21
  47. package/dist/do/index.js.map +1 -1
  48. package/dist/{drive-loop-Dc6Z8Vbs.d.ts → drive-loop-DlOEv49c.d.ts} +2 -2
  49. package/dist/{durable-effects-CgrLNccA.d.ts → durable-effects-wLWEdy9H.d.ts} +1 -1
  50. package/dist/effect/index.d.ts +52 -26
  51. package/dist/effect/index.js +7 -31
  52. package/dist/effect/index.js.map +1 -1
  53. package/dist/extension/index.d.ts +3 -3
  54. package/dist/extension/index.js +2 -2
  55. package/dist/flow/index.d.ts +13 -13
  56. package/dist/flow/index.js +13 -11
  57. package/dist/flow/index.js.map +1 -1
  58. package/dist/idempotency/index.d.ts +3 -3
  59. package/dist/idempotency/index.js +3 -3
  60. package/dist/{index-1hit3dJ9.d.ts → index-9P1PaXIV.d.ts} +2 -2
  61. package/dist/{index-Cuy1_xTh.d.ts → index-BBLI2WWN.d.ts} +1 -1
  62. package/dist/{index-Z6Uw7C9t.d.ts → index-C1WhBZ6a.d.ts} +4 -5
  63. package/dist/{index-B-VC4qjx.d.ts → index-C92SgNWz.d.ts} +2 -2
  64. package/dist/{index-CYrUWi1c.d.ts → index-CAsUE_d1.d.ts} +4 -4
  65. package/dist/{index-BjwRcjBi.d.ts → index-CO34esWT.d.ts} +2 -2
  66. package/dist/{index-DB8Yn0-K.d.ts → index-COeX9Bmz.d.ts} +11 -9
  67. package/dist/{index-Diyo8WBO.d.ts → index-Cg0QXnzf.d.ts} +2 -2
  68. package/dist/index.d.ts +5 -5
  69. package/dist/index.js +2 -2
  70. package/dist/jev/index.d.ts +8 -8
  71. package/dist/jev/index.js +5 -5
  72. package/dist/laws-DTtCDDlU.d.ts +135 -0
  73. package/dist/loop-DReKGV1X.d.ts +70 -0
  74. package/dist/{machine-BuPTS1QH.d.ts → machine-C0WhOAyB.d.ts} +5 -5
  75. package/dist/machine-viz/index.d.ts +1 -1
  76. package/dist/machine-viz/index.js +2 -2
  77. package/dist/mem/index.d.ts +4 -3
  78. package/dist/mem/index.js +2 -2
  79. package/dist/mem/index.js.map +1 -1
  80. package/dist/node/index.d.ts +57 -9
  81. package/dist/node/index.js +175 -68
  82. package/dist/node/index.js.map +1 -1
  83. package/dist/otel/index.d.ts +28 -12
  84. package/dist/otel/index.js +25 -21
  85. package/dist/otel/index.js.map +1 -1
  86. package/dist/paginate/index.d.ts +4 -4
  87. package/dist/paginate/index.js +5 -5
  88. package/dist/parity/index.d.ts +11 -15
  89. package/dist/parity/index.js +4 -9
  90. package/dist/parity/index.js.map +1 -1
  91. package/dist/pbt/index.d.ts +279 -67
  92. package/dist/pbt/index.js +299 -58
  93. package/dist/pbt/index.js.map +1 -1
  94. package/dist/persistence/index.d.ts +10 -12
  95. package/dist/persistence/index.js +4 -4
  96. package/dist/promise/index.d.ts +95 -13
  97. package/dist/promise/index.js +6 -4
  98. package/dist/react/index.d.ts +34 -22
  99. package/dist/react/index.js +271 -42
  100. package/dist/react/index.js.map +1 -1
  101. package/dist/resilience/index.d.ts +6 -6
  102. package/dist/resilience/index.js +5 -5
  103. package/dist/{run-D0sxbyno.d.ts → run-Cnw4qXj2.d.ts} +86 -17
  104. package/dist/{runtime-types-BPKp2bGo.d.ts → runtime-types-CP-ykwgf.d.ts} +120 -31
  105. package/dist/testing/effect/index.d.ts +162 -9
  106. package/dist/testing/effect/index.js +110 -10
  107. package/dist/testing/effect/index.js.map +1 -1
  108. package/dist/testing/index.d.ts +6 -8
  109. package/dist/testing/index.js +4 -24
  110. package/dist/testing/index.js.map +1 -1
  111. package/dist/testing/promise/index.d.ts +4 -4
  112. package/dist/testing/promise/index.js +5 -5
  113. package/dist/timing/index.d.ts +4 -4
  114. package/dist/timing/index.js +4 -4
  115. package/dist/{types-BiXj7k_k.d.ts → types-S53WDy_h.d.ts} +1 -1
  116. package/dist/work-queue/index.d.ts +4 -4
  117. package/dist/work-queue/index.js +2 -2
  118. package/package.json +18 -18
  119. package/dist/chunk-7Z2IDZ7R.js +0 -34
  120. package/dist/chunk-7Z2IDZ7R.js.map +0 -1
  121. package/dist/chunk-EB5OMOXX.js.map +0 -1
  122. package/dist/chunk-GAVLND5C.js.map +0 -1
  123. package/dist/chunk-HU3Y2AZY.js.map +0 -1
  124. package/dist/chunk-TMYUDZLE.js.map +0 -1
  125. package/dist/chunk-VM7PMFV5.js.map +0 -1
  126. package/dist/chunk-W6XWI6QE.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { C as Cmd, c as Sub, M as Machine, R as RunHandlers, P as Port, N as NoCellError, g as Reducer, A as AnyCmdDef, h as Settled, a as CmdOf, i as Transitions, B as BuiltinSub, U as UpdateForm, O as Outcome, j as InterpretDetached, H as HandlerCtx } from './core-BKJW1Kmq.js';
1
+ import { C as Cmd, c as Sub, M as Machine, R as RunHandlers, P as Port, N as NoCellError, g as Reducer, A as AnyCmdDef, h as Settled, a as CmdOf, i as Transitions, B as BuiltinSub, U as UpdateForm, O as Outcome, j as InterpretDetached, H as HandlerCtx } from './core-DNDAEhO2.js';
2
2
 
3
3
  /**
4
4
  * @demlik/tea runtime interface surface + pure helpers — the public types,
@@ -101,10 +101,11 @@ declare function __resetPortRegistry(): void;
101
101
  * - `"discard"` — work lost to a teardown the host asked for. Four witnesses,
102
102
  * each carrying a `RuntimeDiscardNotice`: `stop()` was called while interpret
103
103
  * handlers were still awaiting (`RuntimeDiscardedError`), a Msg that
104
- * arrived DURING `stop()`'s drain, or was still queued when `stop()` gave up
105
- * on it, and so could not be delivered (`DispatchDiscardedError`), a drain
106
- * that did not finish inside `disposeTimeoutMs` (`DrainTimeoutNotice`), and
107
- * teardown work that did not settle inside `disposeTimeoutMs`
104
+ * arrived DURING `stop()`'s drain, or from a handler `stop()` gave up on, and
105
+ * so could not be delivered (`DispatchDiscardedError`), a drain that did not
106
+ * finish inside `disposeTimeoutMs`, which also counts the Msgs that were still
107
+ * queued (`DrainTimeoutNotice`), and teardown work that did not settle inside
108
+ * `disposeTimeoutMs`
108
109
  * (`DisposeTimeoutNotice`). The one phase that is a
109
110
  * LIFECYCLE report rather than a failure: legal, merely lossy.
110
111
  * - `"identity-drop"` — the `Identity` filter dropped a message addressed to a
@@ -146,6 +147,14 @@ interface RuntimeErrorContext {
146
147
  * `run(machine, { onError })`. Should be total — a throw inside the sink is
147
148
  * itself surfaced via the default sink, so it can never re-introduce a silent
148
149
  * failure.
150
+ *
151
+ * Optional, except beside a fenced store (see {@link StoreAndOnError}): there it
152
+ * does not compile without one, and a run whose store is fenced at runtime is
153
+ * refused at start without one, whatever its type says. With no
154
+ * sink a `RuntimeDiscardNotice` is warned, and every other failure is rethrown on
155
+ * a fresh macrotask, which ends a Node process and every run in it. That includes
156
+ * a failing Sub (`"sub"`). A host running several machines in one process passes
157
+ * a sink that decides what one run's failure means for the rest.
149
158
  */
150
159
  type OnError = (error: unknown, context: RuntimeErrorContext) => void;
151
160
  /** The three declared reducer-throw supervision strategies. */
@@ -161,6 +170,14 @@ type SupervisionStrategy = "stop" | "escalate" | "restart";
161
170
  type Supervision<S, M extends {
162
171
  type: string;
163
172
  }> = "stop" | "escalate" | {
173
+ /**
174
+ * The default. A reducer throw stops the run by itself, the same stop a
175
+ * Sub failure starts: the Msgs already in line fold, then the Subs close,
176
+ * the final save runs and, on the Effect engine, the run's own Scope
177
+ * closes, so a run `spawn` started leaves the host's table and its
178
+ * `notify` runs. `done()` fails with the throw. A later `stop()` from the
179
+ * host does nothing more.
180
+ */
164
181
  readonly strategy: "stop";
165
182
  } | {
166
183
  readonly strategy: "escalate";
@@ -175,6 +192,17 @@ type Supervision<S, M extends {
175
192
  */
176
193
  readonly rehydrate: (state: S, msg: M, error: unknown) => S;
177
194
  };
195
+ /**
196
+ * What `done()` rejects with once a Sub's failure the Sub did not map to a Msg
197
+ * has stopped the run, which can then never reach a terminal State. `cause` is
198
+ * what the Sub failed with, the same value `onError` heard under `"sub"`.
199
+ */
200
+ declare class SubFailedError extends Error {
201
+ readonly cause: unknown;
202
+ readonly name = "SubFailedError";
203
+ readonly _tag: "SubFailedError";
204
+ constructor(cause: unknown);
205
+ }
178
206
  /**
179
207
  * Raised by `idle()` when the quiescence wait hits its iteration cap without the
180
208
  * dispatch tail stabilizing — `idle()` REJECTS rather than silently resolving,
@@ -306,10 +334,18 @@ declare class DisposeTimeoutNotice extends RuntimeDiscardNotice {
306
334
  * for it without limit. It then stops the Subs and runs the final save as it
307
335
  * always does. The work it gave up on is not recalled — a Promise cannot be
308
336
  * cancelled — so a handler may still be running after `stop()` has returned.
309
- * Whatever it returns later is dropped, and so are the Msgs queued behind it:
310
- * each dropped Msg is a `DispatchDiscardedError`. The rest of its step's
311
- * Cmds are skipped: no handler starts for them, and they get no notice of
312
- * their own. None of it is saved.
337
+ * Whatever it returns later is dropped, and so are the Msgs queued behind it.
338
+ * The rest of its step's Cmds are skipped: no handler starts for them, and they
339
+ * get no notice of their own. None of it is saved.
340
+ *
341
+ * `droppedMsgs` is how a host learns what it lost. It counts the Msgs queued
342
+ * behind the stuck work when `stop()` gave up, per Msg type: Msgs handed to
343
+ * `dispatch` or `tell`, and those a handler or a Sub sent. It carries types and
344
+ * counts only, never a payload, so a sink can log it. It is read once and does
345
+ * not change. A Msg it counts is not reported again: if the handler returns
346
+ * late, a `dispatch` caller's promise rejects with a `DispatchDiscardedError`,
347
+ * and a Msg with no caller reaches the sink no second time. If the handler
348
+ * never returns, those promises never settle, and this count is the only word.
313
349
  *
314
350
  * tea starts no store call after `stop()` has returned. A store call already
315
351
  * under way when `stop()` gave up is not cancelled and can finish afterwards;
@@ -324,9 +360,10 @@ declare class DrainTimeoutNotice extends RuntimeDiscardNotice {
324
360
  readonly cmdTypes: readonly string[];
325
361
  readonly pendingCmds: number;
326
362
  readonly timeoutMs: number;
363
+ readonly droppedMsgs: Readonly<Record<string, number>>;
327
364
  readonly name = "DrainTimeoutNotice";
328
365
  readonly _tag: "DrainTimeoutNotice";
329
- constructor(cmdTypes: readonly string[], pendingCmds: number, timeoutMs: number);
366
+ constructor(cmdTypes: readonly string[], pendingCmds: number, timeoutMs: number, droppedMsgs?: Readonly<Record<string, number>>);
330
367
  }
331
368
  /**
332
369
  * The rejection of a dispatch that arrived DURING `stop()`'s drain — an in-flight
@@ -470,6 +507,38 @@ interface DeletableStore<S> extends Store<S> {
470
507
  }
471
508
  /** Narrow a `Store<S>` to a {@link FencedStore} — what `run` uses to decide. */
472
509
  declare function isFencedStore<S>(store: Store<S>): store is FencedStore<S>;
510
+ /**
511
+ * A `Store<S>` whose type says it is not fenced. A plain `Store<S>` fits it and
512
+ * a {@link FencedStore} does not. It is what lets {@link StoreAndOnError} tell the
513
+ * two apart, because a `FencedStore` is otherwise also a `Store`.
514
+ */
515
+ type UnfencedStore<S> = Store<S> & {
516
+ readonly fenced?: never;
517
+ };
518
+ /**
519
+ * The `store` and `onError` options of a run: a fenced store needs an `onError`,
520
+ * and the mistake does not compile (ADR 0031).
521
+ *
522
+ * Losing a fence is routine for a host that moves runs between processes, and
523
+ * with no `onError` the default sink rethrows the conflict and ends the process,
524
+ * along with every other run in it. A conflict can reach the sink at any save
525
+ * with no caller to reject at: `"stop-save"` from `stop()`, `"follow-up"` from a
526
+ * Msg a Sub or a Cmd sent. So the sink is required at the type, not left to be
527
+ * remembered.
528
+ *
529
+ * It reads a store's declared type. A fenced store first assigned to a variable
530
+ * typed plain `Store<S>` has given that type up, so it compiles with no
531
+ * `onError`. Both engines then refuse it at start, before anything is loaded:
532
+ * the Promise engine's `run` throws, and the Effect engine's dies. A JavaScript
533
+ * caller is refused the same way.
534
+ */
535
+ type StoreAndOnError<S> = {
536
+ readonly store?: UnfencedStore<S>;
537
+ readonly onError?: OnError;
538
+ } | {
539
+ readonly store: Store<S>;
540
+ readonly onError: OnError;
541
+ };
473
542
  /**
474
543
  * Thrown when a fenced save finds a version other than the one it expected —
475
544
  * another live writer has this run.
@@ -557,9 +626,12 @@ interface RunHandle<S, M extends {
557
626
  } = never> {
558
627
  /** Put a Msg in the inbox; resolves once the engine has processed it. */
559
628
  dispatch(msg: M): Promise<void>;
560
- /** A zero-arg change notifier, fired after each applied transition. */
629
+ /**
630
+ * A zero-arg change notifier, fired for each applied transition after its
631
+ * save and before its first Cmd's handler starts.
632
+ */
561
633
  subscribe(listener: () => void): () => void;
562
- /** A `(msg, state)` hook, fired after each applied transition. */
634
+ /** A `(msg, state)` hook, fired after each applied transition's Cmds. */
563
635
  observe(observer: (msg: M, state: S) => void): () => void;
564
636
  /** Fires once with the initial State — at once if boot already ran. */
565
637
  onBoot(handler: (state: S) => void): () => void;
@@ -584,15 +656,15 @@ interface BootedRunHandle<S, M extends {
584
656
  }
585
657
  /**
586
658
  * The options every engine's `run` accepts: the `ctx`, the handlers the
587
- * machine runs under, an optional `store`, and the `events` projector that
588
- * feeds `on`. An engine may take more; a host adapter hands only these.
659
+ * machine runs under, an optional `store` with the `onError` a fenced one needs,
660
+ * and the `events` projector that feeds `on`. An engine may take more; a host
661
+ * adapter hands only these.
589
662
  */
590
663
  type RunOptions<S, M extends {
591
664
  type: string;
592
665
  }, C extends Cmd, U extends Sub, Ctx, E extends {
593
666
  type: string;
594
- } = never> = CtxArg<Ctx> & RunHandlers<M, C, U, Ctx> & {
595
- readonly store?: Store<S>;
667
+ } = never> = CtxArg<Ctx> & RunHandlers<M, C, U, Ctx> & StoreAndOnError<S> & {
596
668
  readonly events?: (msg: M, state: S) => readonly E[];
597
669
  };
598
670
  /**
@@ -633,12 +705,16 @@ interface BootingRuntime<S, M extends {
633
705
  }): Promise<void>;
634
706
  dispatchOnce(msg: M): Promise<void>;
635
707
  /**
636
- * Register a zero-arg change notifier, fired for every applied transition
637
- * with the fold's State already committed — `getState()` inside the listener
638
- * reads it, not the State before. A `dispatch` issued from the listener is
639
- * SCHEDULED onto the serial tail behind the fold that fired it, never folded
640
- * re-entrantly and never discarded, so it needs no `setTimeout(fn, 0)`
641
- * deferral; two listeners issuing in order fold in that order.
708
+ * Register a zero-arg change notifier, fired once for every applied
709
+ * transition, after its save and before its first Cmd's handler starts, so a
710
+ * view shows the State while that State's Cmds run. `getState()` inside the
711
+ * listener reads the new State, not the State before. A failed save fires
712
+ * nothing. The listener does not mean the Cmds are done: `observe`, `on` and
713
+ * `done()` hear after them. A `dispatch` issued from the listener is SCHEDULED
714
+ * onto the serial tail behind the fold that fired it, never folded re-entrantly
715
+ * and never discarded, so it needs no `setTimeout(fn, 0)` deferral; it folds
716
+ * ahead of what that fold's Cmds return, and two listeners issuing in order
717
+ * fold in that order.
642
718
  */
643
719
  subscribe(listener: () => void): () => void;
644
720
  /**
@@ -734,19 +810,30 @@ interface Runtime<S, M extends {
734
810
  */
735
811
  idle(): Promise<void>;
736
812
  /**
737
- * The terminal State of the run, or `undefined` while in flight — "terminal"
813
+ * The saved terminal State of the run, or `undefined` while in flight — "terminal"
738
814
  * per the `terminal` predicate passed to `run()` (no predicate → never
739
815
  * terminal → always `undefined`). The first-class result read: the run's
740
816
  * product off the State the machine already owns, NOT scraped off the `observe`
741
- * firehose by matching an internal Msg name. Total — never throws.
817
+ * firehose by matching an internal Msg name. With a store, returns `undefined`
818
+ * until the current State's save succeeds, including after a failed save.
819
+ * Without a store, answers as soon as State is set. Total — never throws, so a
820
+ * run a failure closed reads `undefined` here; `done()` says why.
742
821
  */
743
822
  result(): S | undefined;
744
823
  /**
745
- * Resolves with the terminal State the first time the run reaches one (per the
746
- * `terminal` predicate). If ALREADY terminal, resolves immediately; otherwise
747
- * on the transition that first makes `terminal` hold. The awaitable companion
748
- * to `result()`. With no predicate this never resolves. Idempotent and
749
- * multi-caller safe.
824
+ * Resolves with the terminal State only once it is saved (per the `terminal`
825
+ * predicate). If ALREADY saved and terminal, resolves immediately; otherwise
826
+ * on that terminal commit. Rejects with the save failure if a terminal State
827
+ * cannot be saved, for both waiting and later callers. When a failure closes
828
+ * the run short of a terminal State, it rejects for both waiting and later
829
+ * callers too: with `SubFailedError` for a Sub failure the Sub did not map to
830
+ * a Msg, and with what was thrown for a reducer throw under the `stop`
831
+ * strategy. It answers that only after the Msgs already in line have folded,
832
+ * because one of them can still reach a terminal State, and then the State is
833
+ * the answer. A `stop()` with no failure leaves it pending. Without a store,
834
+ * answers as soon as State is set. The awaitable companion to `result()`. With
835
+ * no predicate this resolves never, and rejects only on those failures.
836
+ * Idempotent and multi-caller safe.
750
837
  */
751
838
  done(): Promise<S>;
752
839
  }
@@ -833,7 +920,9 @@ type CtxArg<Ctx> = [Record<never, never>] extends [Ctx] ? {
833
920
  * Cmds emitted, and the Subs that would be running.
834
921
  *
835
922
  * It composes `init(loaded ?? null, ctx)` and then `update(state, msg)` for each
836
- * Msg. No Cmd handler is called, no `Store` is touched and no Sub is started.
923
+ * Msg. Restored init may change State and emit Cmds; those Cmds are collected
924
+ * alongside update Cmds. No handler, Store or Sub runner is called.
925
+ * Recorded traces instead use `replayTrace` to fold their captured post-init State.
837
926
  *
838
927
  * `subs` is each `machine.subs` entry that is on at the final state, as its
839
928
  * runner would see it: `{ id, type, deps }`, the id derived exactly as the
@@ -923,4 +1012,4 @@ declare function tryFoldMsgs<S, M extends {
923
1012
  */
924
1013
  declare function tryInterpret<C extends Cmd, Ok, M, Ctx>(work: (cmd: C, ctx: Ctx) => Promise<Ok>, onOk: (value: Ok, cmd: C) => M, onErr: (error: unknown, cmd: C) => M): (cmd: C, ctx: Ctx) => Promise<M>;
925
1014
 
926
- export { asReducer as A, type BootingRuntime as B, type CtxArg as C, type DeletableStore as D, type EngineRun as E, type FencedStore as F, defineMachine as G, definePort as H, IdentityDropNotice as I, isFencedStore as J, noop as K, refuse as L, type Migrated as M, replay as N, type OnError as O, PortNameCollisionError as P, QuiescenceTimeoutError as Q, type Runtime as R, type Store as S, type TelemetrySink as T, schemaMigrate as U, tryApplyCell as V, tryFoldMsgs as W, tryInterpret as X, wrapDetached as Y, __resetPortRegistry as _, type Supervision as a, type BootedRunHandle as b, type DispatchSettle as c, type RunHandle as d, type Branded as e, DispatchDiscardedError as f, DisposeTimeoutNotice as g, DrainTimeoutNotice as h, DriveFailedError as i, DriveStalledError as j, type FencedRead as k, type FoldRefusal as l, type MachineTypes as m, Refusal as n, type RunOptions as o, RuntimeDiscardNotice as p, RuntimeDiscardedError as q, type RuntimeErrorContext as r, type RuntimeErrorPhase as s, type RuntimeRef as t, type Schema as u, StoreConflictError as v, StoreRefusedError as w, type SupervisionStrategy as x, type TelemetryEvent as y, absurd as z };
1015
+ export { tryInterpret as $, type TelemetryEvent as A, type BootingRuntime as B, type CtxArg as C, type DeletableStore as D, type EngineRun as E, type FencedStore as F, absurd as G, asReducer as H, IdentityDropNotice as I, defineMachine as J, definePort as K, isFencedStore as L, type Migrated as M, noop as N, type OnError as O, PortNameCollisionError as P, QuiescenceTimeoutError as Q, type Runtime as R, type StoreAndOnError as S, type TelemetrySink as T, type UnfencedStore as U, refuse as V, replay as W, schemaMigrate as X, tryApplyCell as Y, tryFoldMsgs as Z, __resetPortRegistry as _, type BootedRunHandle as a, wrapDetached as a0, type Store as b, type Supervision as c, type DispatchSettle as d, type RunHandle as e, type Branded as f, DispatchDiscardedError as g, DisposeTimeoutNotice as h, DrainTimeoutNotice as i, DriveFailedError as j, DriveStalledError as k, type FencedRead as l, type FoldRefusal as m, type MachineTypes as n, Refusal as o, type RunOptions as p, RuntimeDiscardNotice as q, RuntimeDiscardedError as r, type RuntimeErrorContext as s, type RuntimeErrorPhase as t, type RuntimeRef as u, type Schema as v, StoreConflictError as w, StoreRefusedError as x, SubFailedError as y, type SupervisionStrategy as z };
@@ -1,12 +1,165 @@
1
- import { D as DriveRoundsExceededError, a as DriveNoHandlerError, b as DriveOptions, c as DriveResult } from '../../drive-loop-Dc6Z8Vbs.js';
2
- export { d as DEFAULT_MAX_ROUNDS, e as DriveCtxArg, f as DriveTraceEntry, g as driveTraceOf } from '../../drive-loop-Dc6Z8Vbs.js';
3
- import { Effect } from 'effect';
4
- import { C as CellErrors, S as SubscribeOption, E as EffectInterpret, a as EffectSubscribe, I as InterpretServices, b as SubscribeServices } from '../../run-D0sxbyno.js';
5
- import { C as Cmd, c as Sub, M as Machine } from '../../core-BKJW1Kmq.js';
6
- import '../../runtime-types-BPKp2bGo.js';
7
- import 'effect/Cause';
8
- import 'effect/Types';
1
+ export { F as Fold, L as LawBreak, S as Step, f as foldEvents, l as lawInvariant, a as lawTerminates, b as lawTrace } from '../../laws-DTtCDDlU.js';
2
+ import { e as DriveRoundsExceededError, d as DriveNoHandlerError, D as DriveOptions, a as DriveResult } from '../../drive-loop-DlOEv49c.js';
3
+ export { b as DEFAULT_MAX_ROUNDS, c as DriveCtxArg, f as DriveTraceEntry, g as driveTraceOf } from '../../drive-loop-DlOEv49c.js';
4
+ import * as effect_Cause from 'effect/Cause';
5
+ import * as effect_Types from 'effect/Types';
6
+ import { Arbitrary, Result, Effect } from 'effect';
7
+ import { C as Cmd, c as Sub, M as Machine } from '../../core-DNDAEhO2.js';
8
+ import { C as CellErrors, l as SubscribeOption, c as EffectInterpret, h as EffectSubscribe, I as InterpretServices, k as SubscribeServices } from '../../run-Cnw4qXj2.js';
9
+ import '../../runtime-types-CP-ykwgf.js';
9
10
  import '@standard-schema/spec';
11
+ import '../../loop-DReKGV1X.js';
12
+
13
+ declare const NoCmdsError_base: new <A extends Record<string, any> = {}>(args: effect_Types.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => effect_Cause.YieldableError & {
14
+ readonly _tag: "NoCmdsError";
15
+ } & Readonly<A>;
16
+ /**
17
+ * The machine has no `cmds` list, so there is no schema to draw an answer from.
18
+ *
19
+ * @experimental
20
+ */
21
+ declare class NoCmdsError extends NoCmdsError_base<{
22
+ readonly message: string;
23
+ }> {
24
+ }
25
+ declare const NotEffectSchemaError_base: new <A extends Record<string, any> = {}>(args: effect_Types.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => effect_Cause.YieldableError & {
26
+ readonly _tag: "NotEffectSchemaError";
27
+ } & Readonly<A>;
28
+ /**
29
+ * A Cmd's `ok` schema is not an Effect schema, so no answer can be drawn from
30
+ * it. A Cmd made with `Cmd.define` from `@demlik/tea` over zod, or any other
31
+ * Standard Schema, lands here. `cmdType` names that Cmd.
32
+ *
33
+ * @experimental
34
+ */
35
+ declare class NotEffectSchemaError extends NotEffectSchemaError_base<{
36
+ readonly cmdType: string;
37
+ readonly message: string;
38
+ }> {
39
+ }
40
+ declare const NothingToDrawError_base: new <A extends Record<string, any> = {}>(args: effect_Types.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => effect_Cause.YieldableError & {
41
+ readonly _tag: "NothingToDrawError";
42
+ } & Readonly<A>;
43
+ /**
44
+ * `machine.cmds` is empty and no `outside` Msg was given, so every list drawn
45
+ * would be empty.
46
+ *
47
+ * @experimental
48
+ */
49
+ declare class NothingToDrawError extends NothingToDrawError_base<{
50
+ readonly message: string;
51
+ }> {
52
+ }
53
+ /**
54
+ * Why {@link arbSchemaGuidedSequence} drew no lists.
55
+ *
56
+ * @experimental
57
+ */
58
+ type SchemaAnswersError = NoCmdsError | NotEffectSchemaError | NothingToDrawError;
59
+ /**
60
+ * What {@link arbSchemaGuidedSequence} takes beside the machine and its ctx.
61
+ *
62
+ * @experimental
63
+ */
64
+ interface SchemaGuidedOptions<S, M extends {
65
+ type: string;
66
+ }> {
67
+ /**
68
+ * The Msgs nothing sends a Cmd for, by Msg type: a user's click, a timer. A
69
+ * Msg type here and in no Cmd's answers is drawn only from here.
70
+ */
71
+ readonly outside?: {
72
+ readonly [K in M["type"]]?: Arbitrary.Arbitrary<Extract<M, {
73
+ type: K;
74
+ }>>;
75
+ };
76
+ /**
77
+ * Caps the events drawn. Default 60. `checkEffect` and `sampleEffect` cap a
78
+ * list at their `size` too, which defaults to 10, so pass a `size` of at least
79
+ * this to draw lists this long.
80
+ */
81
+ readonly maxLength?: number;
82
+ /**
83
+ * The saved state the fold starts from. Give `foldEvents` the same one, or it
84
+ * reads the list from a different start. Default `null`.
85
+ */
86
+ readonly loaded?: S | null;
87
+ }
88
+ /**
89
+ * Draw Msg lists for `Arbitrary.checkEffect` in which every answer answers a Cmd
90
+ * that was sent and not yet answered, and comes from that Cmd's own schema: an
91
+ * `ok` answer is drawn from its `schema.ok`, an `err` answer carries one of its
92
+ * `errTags`. Edit a schema and what is drawn changes with it, with no change to
93
+ * the test.
94
+ *
95
+ * Each list is drawn as blind events folded through the machine. An answer event
96
+ * names one waiting Cmd and is minted with that Cmd's def, `def.ok(cmd, value)`
97
+ * or `def.err(cmd, { _tag })`. With nothing waiting the answer is dropped, and an
98
+ * `outside` Msg the event drew goes in its place, or nothing when there is no
99
+ * `outside`. An event whose Msg the state has no cell for is dropped too, and a
100
+ * refused answer leaves its Cmd waiting. A Cmd no def in `machine.cmds` builds is
101
+ * never answered.
102
+ *
103
+ * It runs no check. Fold each list with `foldEvents` and judge it with a law,
104
+ * `lawInvariant`, `lawTerminates` or `lawTrace`, inside `Arbitrary.checkEffect`.
105
+ * A list it finds there shrinks by deleting events and shrinking them, and what
106
+ * is left still folds by the same rule. With `outside` given and every Msg
107
+ * accepted in every state, each event means one Msg, so a shrunk list is never
108
+ * longer than the one first found. A dropped event breaks that: deleting an
109
+ * answer before it can let it back in.
110
+ *
111
+ * It returns a failed `Result`, never an empty arbitrary, on a machine with no
112
+ * `machine.cmds` ({@link NoCmdsError}), on a Cmd whose `ok` schema is not an
113
+ * Effect schema ({@link NotEffectSchemaError}, naming it), and when there is
114
+ * nothing at all to draw ({@link NothingToDrawError}).
115
+ *
116
+ * @example
117
+ * ```ts
118
+ * import type { CmdOf, Machine, Settled } from "@demlik/tea";
119
+ * import { Cmd } from "@demlik/tea/effect";
120
+ * import {
121
+ * arbSchemaGuidedSequence,
122
+ * foldEvents,
123
+ * lawInvariant,
124
+ * } from "@demlik/tea/testing/effect";
125
+ * import { Arbitrary, Effect, Result, Schema } from "effect";
126
+ *
127
+ * const fetchCount = Cmd.define("fetch_count", {
128
+ * input: Schema.Struct({}),
129
+ * ok: Schema.Struct({ n: Schema.Int }),
130
+ * err: ["offline"],
131
+ * });
132
+ * type Msg = { type: "start" } | Settled<typeof fetchCount>;
133
+ * declare const machine: Machine<
134
+ * { readonly total: number },
135
+ * Msg,
136
+ * CmdOf<typeof fetchCount>,
137
+ * never,
138
+ * object
139
+ * >;
140
+ *
141
+ * const lists = Result.getOrThrow(
142
+ * arbSchemaGuidedSequence(machine, {}, {
143
+ * outside: { start: Arbitrary.Constant({ type: "start" as const }) },
144
+ * }),
145
+ * );
146
+ * const result = await Effect.runPromise(
147
+ * Arbitrary.checkEffect(
148
+ * lists,
149
+ * (msgs) =>
150
+ * lawInvariant(foldEvents(machine, {}, null, msgs), ({ next }) =>
151
+ * Number.isFinite(next.total),
152
+ * ) === null,
153
+ * { runs: 500, size: 60 },
154
+ * ),
155
+ * );
156
+ * ```
157
+ *
158
+ * @experimental
159
+ */
160
+ declare function arbSchemaGuidedSequence<S, M extends {
161
+ type: string;
162
+ }, C extends Cmd, U extends Sub, Ctx>(machine: Machine<S, M, C, U, Ctx>, ctx: NoInfer<Ctx>, opts?: SchemaGuidedOptions<NoInfer<S>, M>): Result.Result<Arbitrary.Arbitrary<readonly M[]>, SchemaAnswersError>;
10
163
 
11
164
  /**
12
165
  * The Effect `drive`'s options: the Promise `drive`'s, plus `subscribe` — the
@@ -62,4 +215,4 @@ declare function drive<S, M extends {
62
215
  type: string;
63
216
  }, C extends Cmd, U extends Sub, Ctx, I extends EffectInterpret<M, C> = EffectInterpret<M, C, never>, B extends EffectSubscribe<M, U> = EffectSubscribe<M, U, never>>(machine: Machine<S, M, C, U, Ctx>, initial: S, msg: M, interpret: I, ...[opts]: Record<never, never> extends EffectDriveOptions<U, Ctx, B> ? [opts?: EffectDriveOptions<U, Ctx, B>] : [opts: EffectDriveOptions<U, Ctx, B>]): Effect.Effect<DriveResult<S, M, C>, EffectDriveError<M, C, I>, InterpretServices<I> | SubscribeServices<B>>;
64
217
 
65
- export { DriveNoHandlerError, DriveOptions, DriveResult, DriveRoundsExceededError, type EffectDriveError, type EffectDriveOptions, drive };
218
+ export { DriveNoHandlerError, DriveOptions, DriveResult, DriveRoundsExceededError, type EffectDriveError, type EffectDriveOptions, NoCmdsError, NotEffectSchemaError, NothingToDrawError, type SchemaAnswersError, type SchemaGuidedOptions, arbSchemaGuidedSequence, drive };
@@ -1,13 +1,113 @@
1
- import { CellFailure, builtinRunners, cellResult, settleCellExit } from '../../chunk-HU3Y2AZY.js';
2
- import { driveLoop, DEFAULT_MAX_ROUNDS, driveContractOf, DriveRoundsExceededError, DriveNoHandlerError, annotateTrace, driveTraceOf, desiredSubs } from '../../chunk-BFSZ4L6Y.js';
3
- export { DEFAULT_MAX_ROUNDS, DriveNoHandlerError, DriveRoundsExceededError, driveTraceOf } from '../../chunk-BFSZ4L6Y.js';
4
- import '../../chunk-7Z2IDZ7R.js';
5
- import '../../chunk-VM7PMFV5.js';
6
- import '../../chunk-Z7IL735U.js';
7
- import '../../chunk-VHKDUBXC.js';
8
- import '../../chunk-GAVLND5C.js';
9
- import { Effect, Stream, Exit, Cause, Fiber } from 'effect';
1
+ import { resolveGuided, MAX_PICK } from '../../chunk-FXWDDNGD.js';
2
+ export { foldEvents, lawInvariant, lawTerminates, lawTrace } from '../../chunk-FXWDDNGD.js';
3
+ import { CellFailure, builtinRunners, cellResult, settleCellExit } from '../../chunk-SHXUSTHM.js';
4
+ import { driveLoop, DEFAULT_MAX_ROUNDS, driveContractOf, DriveRoundsExceededError, DriveNoHandlerError, annotateTrace, driveTraceOf, desiredSubs } from '../../chunk-ETRNSJCR.js';
5
+ export { DEFAULT_MAX_ROUNDS, DriveNoHandlerError, DriveRoundsExceededError, driveTraceOf } from '../../chunk-ETRNSJCR.js';
6
+ import '../../chunk-RMBZ7YH7.js';
7
+ import '../../chunk-2A5XTKQQ.js';
8
+ import '../../chunk-R2YNDRGL.js';
9
+ import '../../chunk-DEAPNMKO.js';
10
+ import '../../chunk-BVFS3ICC.js';
11
+ import '../../chunk-A3QPMX5M.js';
12
+ import { Data, Result, Arbitrary, Effect, Stream, Exit, Cause, Schema, Fiber } from 'effect';
10
13
 
14
+ var NoCmdsError = class extends Data.TaggedError("NoCmdsError") {
15
+ };
16
+ var NotEffectSchemaError = class extends Data.TaggedError(
17
+ "NotEffectSchemaError"
18
+ ) {
19
+ };
20
+ var NothingToDrawError = class extends Data.TaggedError("NothingToDrawError") {
21
+ };
22
+ var PREFIX = "@demlik/tea/testing/effect: arbSchemaGuidedSequence";
23
+ var upTo = (max) => Arbitrary.schema(
24
+ Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: max }))
25
+ );
26
+ var oneOf = (arms) => arms.length === 1 ? arms[0] : Arbitrary.flatMap(
27
+ upTo(arms.length - 1),
28
+ (at) => arms[at]
29
+ );
30
+ function outcomesOf(def) {
31
+ const ok = def.schema.ok;
32
+ if (!Schema.isSchema(ok)) return null;
33
+ const okArm = Schema.Struct({ outcome: Schema.Literal("ok"), value: ok });
34
+ const outcome = def.errTags.length === 0 ? okArm : Schema.Union([
35
+ okArm,
36
+ Schema.Struct({
37
+ outcome: Schema.Literal("err"),
38
+ tag: Schema.Literals(def.errTags)
39
+ })
40
+ ]);
41
+ return Arbitrary.schema(outcome);
42
+ }
43
+ function arbSchemaGuidedSequence(machine, ctx, opts) {
44
+ const defs = machine.cmds;
45
+ if (defs === void 0) {
46
+ return Result.fail(
47
+ new NoCmdsError({
48
+ message: `${PREFIX}: the machine has no \`cmds\`, so no Cmd has a schema to draw its answer from. List its \`Cmd.define\`d Cmds in \`defineMachine({ cmds })\`.`
49
+ })
50
+ );
51
+ }
52
+ const answerers = [];
53
+ const outcomes = [];
54
+ for (const def of defs) {
55
+ const drawn = outcomesOf(def);
56
+ if (drawn === null) {
57
+ return Result.fail(
58
+ new NotEffectSchemaError({
59
+ cmdType: def.cmdType,
60
+ message: `${PREFIX}: the "${def.cmdType}" Cmd's ok schema is not an Effect schema, so no answer can be drawn from it. Define it with \`Cmd.define\` from \`@demlik/tea/effect\`.`
61
+ })
62
+ );
63
+ }
64
+ const mint = def;
65
+ answerers.push({
66
+ cmdType: def.cmdType,
67
+ make: (cmd, value) => {
68
+ const drew = value;
69
+ return drew.outcome === "ok" ? mint.ok(cmd, drew.value) : mint.err(cmd, { _tag: drew.tag });
70
+ }
71
+ });
72
+ outcomes.push(drawn);
73
+ }
74
+ const outsides = Object.values(
75
+ opts?.outside ?? {}
76
+ );
77
+ const outside = outsides.length === 0 ? null : oneOf(outsides);
78
+ const answer = outcomes.length === 0 ? null : Arbitrary.map(
79
+ Arbitrary.all([
80
+ upTo(MAX_PICK),
81
+ Arbitrary.all(outcomes),
82
+ outside ?? Arbitrary.Constant(void 0)
83
+ ]),
84
+ ([pick, values, otherwise]) => otherwise === void 0 ? { kind: "answer", pick, values } : { kind: "answer", pick, values, otherwise }
85
+ );
86
+ const arms = [];
87
+ if (answer !== null) arms.push(answer, answer);
88
+ if (outside !== null) {
89
+ arms.push(
90
+ Arbitrary.map(
91
+ outside,
92
+ (msg) => ({ kind: "outside", msg })
93
+ )
94
+ );
95
+ }
96
+ if (arms.length === 0) {
97
+ return Result.fail(
98
+ new NothingToDrawError({
99
+ message: `${PREFIX}: \`machine.cmds\` is empty and no \`outside\` Msg was given, so every list would be empty.`
100
+ })
101
+ );
102
+ }
103
+ const loaded = opts?.loaded ?? null;
104
+ return Result.succeed(
105
+ Arbitrary.map(
106
+ Arbitrary.array(oneOf(arms), { maxLength: opts?.maxLength ?? 60 }),
107
+ (events) => resolveGuided(machine, ctx, loaded, answerers, events)
108
+ )
109
+ );
110
+ }
11
111
  var DriveInterrupted = class {
12
112
  };
13
113
  function drive(machine, initial, msg, interpret, ...[opts]) {
@@ -143,6 +243,6 @@ function effectSubs(machine, subscribe, fork, signal) {
143
243
  };
144
244
  }
145
245
 
146
- export { drive };
246
+ export { NoCmdsError, NotEffectSchemaError, NothingToDrawError, arbSchemaGuidedSequence, drive };
147
247
  //# sourceMappingURL=index.js.map
148
248
  //# sourceMappingURL=index.js.map