@demlik/tea 0.17.0 → 0.19.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 (119) hide show
  1. package/README.md +4 -3
  2. package/dist/agent/index.d.ts +182 -35
  3. package/dist/agent/index.js +11 -11
  4. package/dist/{chunk-KWNCM6XM.js → chunk-AHW2CLPT.js} +65 -18
  5. package/dist/chunk-AHW2CLPT.js.map +1 -0
  6. package/dist/{chunk-K4ETBNKS.js → chunk-AIIXV4XB.js} +38 -9
  7. package/dist/chunk-AIIXV4XB.js.map +1 -0
  8. package/dist/{chunk-LYVOGOBC.js → chunk-BRX3243I.js} +3 -3
  9. package/dist/{chunk-LYVOGOBC.js.map → chunk-BRX3243I.js.map} +1 -1
  10. package/dist/{chunk-GWWCCSDH.js → chunk-C3G2QFEZ.js} +4 -4
  11. package/dist/{chunk-GWWCCSDH.js.map → chunk-C3G2QFEZ.js.map} +1 -1
  12. package/dist/{chunk-C4U5D5HP.js → chunk-FNGZMVIT.js} +3 -3
  13. package/dist/chunk-FNGZMVIT.js.map +1 -0
  14. package/dist/{chunk-QWIPH4YH.js → chunk-H2ELKHI6.js} +16 -6
  15. package/dist/chunk-H2ELKHI6.js.map +1 -0
  16. package/dist/chunk-H3MWNH7A.js +119 -0
  17. package/dist/chunk-H3MWNH7A.js.map +1 -0
  18. package/dist/chunk-I7E7AD2S.js +213 -0
  19. package/dist/chunk-I7E7AD2S.js.map +1 -0
  20. package/dist/{chunk-OYR7ENKU.js → chunk-IHUNKHDX.js} +3 -3
  21. package/dist/{chunk-OYR7ENKU.js.map → chunk-IHUNKHDX.js.map} +1 -1
  22. package/dist/chunk-JVE2LBFJ.js +21 -0
  23. package/dist/chunk-JVE2LBFJ.js.map +1 -0
  24. package/dist/chunk-JVKU2UQC.js +34 -0
  25. package/dist/chunk-JVKU2UQC.js.map +1 -0
  26. package/dist/{chunk-OPH6Y4UT.js → chunk-JYNYG5SF.js} +4 -4
  27. package/dist/{chunk-OPH6Y4UT.js.map → chunk-JYNYG5SF.js.map} +1 -1
  28. package/dist/{chunk-XBZQT2TD.js → chunk-PCJA2HTZ.js} +437 -162
  29. package/dist/chunk-PCJA2HTZ.js.map +1 -0
  30. package/dist/{chunk-DFUEAA6L.js → chunk-PQSDUX6B.js} +3 -3
  31. package/dist/{chunk-DFUEAA6L.js.map → chunk-PQSDUX6B.js.map} +1 -1
  32. package/dist/{chunk-6XBEYZME.js → chunk-WTSOW2MD.js} +5 -5
  33. package/dist/{chunk-6XBEYZME.js.map → chunk-WTSOW2MD.js.map} +1 -1
  34. package/dist/{chunk-4PXNZ5UY.js → chunk-XVIKSRCK.js} +4 -4
  35. package/dist/{chunk-4PXNZ5UY.js.map → chunk-XVIKSRCK.js.map} +1 -1
  36. package/dist/{chunk-4NTCPTNQ.js → chunk-YIMOFXST.js} +3 -3
  37. package/dist/{chunk-4NTCPTNQ.js.map → chunk-YIMOFXST.js.map} +1 -1
  38. package/dist/{core-rDbOaJTN.d.ts → core-Ce9ncpuv.d.ts} +5 -4
  39. package/dist/do/index.d.ts +140 -64
  40. package/dist/do/index.js +17 -18
  41. package/dist/do/index.js.map +1 -1
  42. package/dist/drive-loop-CsUsW3Wo.d.ts +127 -0
  43. package/dist/{durable-effects-CNaw97Be.d.ts → durable-effects-BmUrRA-h.d.ts} +1 -1
  44. package/dist/effect/index.d.ts +6 -150
  45. package/dist/effect/index.js +4 -96
  46. package/dist/effect/index.js.map +1 -1
  47. package/dist/extension/index.d.ts +7 -5
  48. package/dist/extension/index.js +2 -3
  49. package/dist/extension/index.js.map +1 -1
  50. package/dist/flow/index.d.ts +13 -13
  51. package/dist/flow/index.js +13 -12
  52. package/dist/flow/index.js.map +1 -1
  53. package/dist/idempotency/index.d.ts +3 -3
  54. package/dist/idempotency/index.js +4 -4
  55. package/dist/{index-q1PoukOZ.d.ts → index-2kNxy6az.d.ts} +2 -2
  56. package/dist/{index-B_BxplWi.d.ts → index-B1Wzarkl.d.ts} +1 -1
  57. package/dist/{index-Cberb5Rr.d.ts → index-Bf9_bNql.d.ts} +2 -2
  58. package/dist/index-BnsAqn0-.d.ts +286 -0
  59. package/dist/{index-Cx0F2M34.d.ts → index-Ce5GYAuI.d.ts} +2 -2
  60. package/dist/{index-CWmsXNdX.d.ts → index-D8sYVzCI.d.ts} +1 -1
  61. package/dist/{index-CcrKLtse.d.ts → index-DWdg7R1l.d.ts} +1 -1
  62. package/dist/{index-BOrsbdAf.d.ts → index-Dy3379ve.d.ts} +5 -5
  63. package/dist/{index-DaX_P35Q.d.ts → index-e36uEyl4.d.ts} +3 -3
  64. package/dist/index.d.ts +109 -36
  65. package/dist/index.js +2 -2
  66. package/dist/jev/index.d.ts +8 -8
  67. package/dist/jev/index.js +6 -7
  68. package/dist/jev/index.js.map +1 -1
  69. package/dist/{machine-BcfCFMYF.d.ts → machine-CuojfZup.d.ts} +353 -57
  70. package/dist/machine-viz/index.d.ts +1 -1
  71. package/dist/machine-viz/index.js +2 -2
  72. package/dist/mem/index.d.ts +6 -6
  73. package/dist/mem/index.js +6 -2
  74. package/dist/mem/index.js.map +1 -1
  75. package/dist/node/index.d.ts +16 -10
  76. package/dist/node/index.js +22 -3
  77. package/dist/node/index.js.map +1 -1
  78. package/dist/otel/index.d.ts +161 -0
  79. package/dist/otel/index.js +403 -0
  80. package/dist/otel/index.js.map +1 -0
  81. package/dist/paginate/index.d.ts +4 -4
  82. package/dist/paginate/index.js +5 -5
  83. package/dist/parity/index.d.ts +3 -3
  84. package/dist/parity/index.js +3 -3
  85. package/dist/pbt/index.d.ts +166 -53
  86. package/dist/pbt/index.js +2 -2
  87. package/dist/pbt/index.js.map +1 -1
  88. package/dist/persistence/index.d.ts +5 -5
  89. package/dist/persistence/index.js +5 -5
  90. package/dist/promise/index.d.ts +2 -2
  91. package/dist/promise/index.js +4 -3
  92. package/dist/react/index.d.ts +2 -2
  93. package/dist/resilience/index.d.ts +13 -9
  94. package/dist/resilience/index.js +5 -5
  95. package/dist/resilience/index.js.map +1 -1
  96. package/dist/{runtime-types-B62Ucnxr.d.ts → runtime-types-BKg2CGMx.d.ts} +88 -14
  97. package/dist/testing/effect/index.d.ts +65 -0
  98. package/dist/testing/effect/index.js +148 -0
  99. package/dist/testing/effect/index.js.map +1 -0
  100. package/dist/testing/index.d.ts +12 -158
  101. package/dist/testing/index.js +4 -130
  102. package/dist/testing/index.js.map +1 -1
  103. package/dist/testing/promise/index.d.ts +41 -0
  104. package/dist/testing/promise/index.js +37 -0
  105. package/dist/testing/promise/index.js.map +1 -0
  106. package/dist/timing/index.d.ts +4 -4
  107. package/dist/timing/index.js +4 -4
  108. package/dist/{types-DPm5YCDQ.d.ts → types-SzNSHKlV.d.ts} +1 -1
  109. package/dist/work-queue/index.d.ts +4 -4
  110. package/dist/work-queue/index.js +4 -3
  111. package/dist/work-queue/index.js.map +1 -1
  112. package/package.json +26 -14
  113. package/dist/chunk-C4U5D5HP.js.map +0 -1
  114. package/dist/chunk-G5E74IJB.js +0 -8
  115. package/dist/chunk-G5E74IJB.js.map +0 -1
  116. package/dist/chunk-K4ETBNKS.js.map +0 -1
  117. package/dist/chunk-KWNCM6XM.js.map +0 -1
  118. package/dist/chunk-QWIPH4YH.js.map +0 -1
  119. package/dist/chunk-XBZQT2TD.js.map +0 -1
@@ -0,0 +1,148 @@
1
+ import { CellFailure, builtinRunners, cellResult, settleCellExit } from '../../chunk-I7E7AD2S.js';
2
+ import { driveLoop, DEFAULT_MAX_ROUNDS, driveContractOf, DriveRoundsExceededError, DriveNoHandlerError, annotateTrace, driveTraceOf, desiredSubs } from '../../chunk-H3MWNH7A.js';
3
+ export { DEFAULT_MAX_ROUNDS, DriveNoHandlerError, DriveRoundsExceededError, driveTraceOf } from '../../chunk-H3MWNH7A.js';
4
+ import '../../chunk-JVKU2UQC.js';
5
+ import '../../chunk-AHW2CLPT.js';
6
+ import '../../chunk-JVE2LBFJ.js';
7
+ import '../../chunk-FNGZMVIT.js';
8
+ import '../../chunk-AIIXV4XB.js';
9
+ import { Effect, Stream, Exit, Cause, Fiber } from 'effect';
10
+
11
+ var DriveInterrupted = class {
12
+ };
13
+ function drive(machine, initial, msg, interpret, ...[opts]) {
14
+ const options = opts ?? {};
15
+ return Effect.gen(function* () {
16
+ const services = yield* Effect.context();
17
+ const runEffect = Effect.runPromiseExitWith(services);
18
+ const fork = Effect.runForkWith(services);
19
+ return yield* Effect.callback((resume, signal) => {
20
+ const cells = interpret;
21
+ const defined = new Set((machine.cmds ?? []).map((def) => def.cmdType));
22
+ const cellFor = (type) => {
23
+ const cell = cells[type];
24
+ if (cell === void 0) return void 0;
25
+ return async (cmd) => {
26
+ const exit = await runEffect(cellResult(cell(cmd)), { signal });
27
+ if (signal.aborted) throw new DriveInterrupted();
28
+ return settleCellExit(exit, defined.has(type));
29
+ };
30
+ };
31
+ driveLoop(
32
+ machine,
33
+ initial,
34
+ msg,
35
+ options.ctx,
36
+ options.maxRounds ?? DEFAULT_MAX_ROUNDS,
37
+ {
38
+ contract: driveContractOf(machine, options.clock ?? Date.now),
39
+ cellFor,
40
+ subs: effectSubs(
41
+ machine,
42
+ options.subscribe ?? {},
43
+ fork,
44
+ signal
45
+ )
46
+ }
47
+ ).then(
48
+ (result) => resume(Effect.succeed(result)),
49
+ (error) => resume(failureOf(error))
50
+ );
51
+ });
52
+ });
53
+ }
54
+ function failureOf(error) {
55
+ if (error instanceof DriveRoundsExceededError || error instanceof DriveNoHandlerError) {
56
+ return Effect.fail(error);
57
+ }
58
+ if (error instanceof CellFailure) {
59
+ annotateTrace(error.failure, driveTraceOf(error) ?? []);
60
+ return Effect.fail(error.failure);
61
+ }
62
+ if (error instanceof DriveInterrupted) return Effect.interrupt;
63
+ return Effect.die(error);
64
+ }
65
+ function effectSubs(machine, subscribe, fork, signal) {
66
+ const builtins = builtinRunners;
67
+ const started = /* @__PURE__ */ new Map();
68
+ let buffered = [];
69
+ let failed;
70
+ let wake;
71
+ const notify = () => {
72
+ const woken = wake;
73
+ wake = void 0;
74
+ woken?.();
75
+ };
76
+ signal.addEventListener("abort", notify, { once: true });
77
+ const stop = (sub) => {
78
+ sub.stopped = true;
79
+ return sub.ended || sub.fiber === void 0 ? Promise.resolve() : Effect.runPromise(Fiber.interrupt(sub.fiber));
80
+ };
81
+ const anyRunning = () => [...started.values()].some((sub) => !sub.ended);
82
+ return {
83
+ reconcile(state) {
84
+ const desired = desiredSubs(machine, state);
85
+ for (const [id, sub] of started) {
86
+ if (desired.has(id)) continue;
87
+ started.delete(id);
88
+ void stop(sub);
89
+ }
90
+ for (const [id, sub] of desired) {
91
+ if (started.has(id)) continue;
92
+ const make = subscribe[sub.type] ?? builtins[sub.type];
93
+ if (make === void 0) {
94
+ throw new Error(
95
+ `@demlik/tea: no subscribe runner for Sub type "${sub.type}". Pass one to drive in \`subscribe\`.`
96
+ );
97
+ }
98
+ const entry = { stopped: false, ended: false };
99
+ started.set(id, entry);
100
+ entry.fiber = fork(
101
+ Stream.runForEach(
102
+ make(sub),
103
+ (next) => Effect.sync(() => {
104
+ if (entry.stopped) return;
105
+ buffered.push(next);
106
+ notify();
107
+ })
108
+ ).pipe(
109
+ Effect.onExit(
110
+ (exit) => Effect.sync(() => {
111
+ entry.ended = true;
112
+ if (!entry.stopped && Exit.isFailure(exit) && !Cause.hasInterruptsOnly(exit.cause)) {
113
+ failed ??= exit.cause;
114
+ }
115
+ notify();
116
+ })
117
+ )
118
+ )
119
+ );
120
+ }
121
+ },
122
+ async next() {
123
+ for (; ; ) {
124
+ if (signal.aborted) throw new DriveInterrupted();
125
+ if (buffered.length > 0) {
126
+ const out = buffered;
127
+ buffered = [];
128
+ return out;
129
+ }
130
+ if (failed !== void 0) throw Cause.squash(failed);
131
+ if (!anyRunning()) return void 0;
132
+ await new Promise((resolve) => {
133
+ wake = resolve;
134
+ });
135
+ }
136
+ },
137
+ async close() {
138
+ signal.removeEventListener("abort", notify);
139
+ const stopping = [...started.values()].map(stop);
140
+ started.clear();
141
+ await Promise.all(stopping);
142
+ }
143
+ };
144
+ }
145
+
146
+ export { drive };
147
+ //# sourceMappingURL=index.js.map
148
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/testing/effect/drive.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// ---------------------------------------------------------------------------\n// `drive(machine, initial, msg, interpret, opts)` for the Effect engine: the\n// Promise `drive`'s rounds, trace and bound, over the Effect engine's handlers\n// and Subs (#321).\n//\n// There is no second loop. Each Effect cell is adapted into the shared loop's\n// cell with the settle rule the engine itself uses (`settleCellExit` in\n// `src/effect/run.ts`), run with the services the caller provided, so a cell\n// settles here exactly as it does under `run`. The Subs the machine wants are\n// started and stopped after every fold, as the engine reconciles them, and\n// their Msgs are folded once the Cmds in flight have settled.\n// ---------------------------------------------------------------------------\n\nimport { Cause, Effect, Exit, Fiber, Stream } from \"effect\";\nimport { CellFailure } from \"../../effect/failures\";\nimport {\n builtinRunners,\n type CellErrors,\n cellResult,\n type EffectInterpret,\n type EffectSubscribe,\n type InterpretServices,\n type SubscribeOption,\n type SubscribeServices,\n settleCellExit,\n} from \"../../effect/run\";\nimport type { Cmd, Machine, Sub } from \"../../pure/core\";\nimport {\n annotateTrace,\n DEFAULT_MAX_ROUNDS,\n type DriveCell,\n DriveNoHandlerError,\n type DriveOptions,\n type DriveResult,\n DriveRoundsExceededError,\n type DriveSubs,\n desiredSubs,\n driveContractOf,\n driveLoop,\n driveTraceOf,\n} from \"../drive-loop\";\n\n/**\n * The Effect `drive`'s options: the Promise `drive`'s, plus `subscribe` — the\n * runners the engine's `run` takes, required exactly when `run` requires them.\n */\nexport type EffectDriveOptions<U extends Sub, Ctx, B> = DriveOptions<Ctx> &\n SubscribeOption<U, B>;\n\n/** The failures a drive ends with: the loop's own, and a hand-written cell's. */\nexport type EffectDriveError<M, C extends Cmd, I> =\n | DriveRoundsExceededError<M, C>\n | DriveNoHandlerError<M, C>\n | CellErrors<C, I>;\n\n/** The drive was interrupted; nothing is left to run. */\nclass DriveInterrupted {}\n\ntype AnyCell = (cmd: unknown) => Effect.Effect<unknown, unknown>;\ntype AnyRunner = (sub: Sub) => Stream.Stream<unknown, unknown>;\ntype RunEffect = (\n effect: Effect.Effect<unknown, unknown>,\n options: { readonly signal: AbortSignal },\n) => Promise<Exit.Exit<unknown, unknown>>;\ntype ForkEffect = (\n effect: Effect.Effect<unknown, unknown>,\n) => Fiber.Fiber<unknown, unknown>;\n\n/**\n * Drive `machine` from `initial` through `msg` against the REAL Effect\n * `interpret` map — the one a host hands the Effect engine's `run` — and the\n * Subs the machine wants, feeding every Msg back until the machine goes quiet.\n * Yields the settled state together with the whole history, `{ state, trace }`.\n *\n * The rounds, the trace, `maxRounds` and `clock` are the Promise `drive`'s\n * (`@demlik/tea/testing/promise`). What is the Effect engine's:\n *\n * - a cell returns an Effect. A `Cmd.define`d Cmd's success mints `_ok` and\n * its declared failure `_err`; a hand-written Cmd's success is its\n * follow-up — a Msg, a list of them folded in order, or nothing (#324) —\n * and its failure fails the drive, typed. A defect dies. A cell takes no\n * `dispatch`, so a defined Cmd's handler cannot send its own `_ok` /\n * `_err`: the engine's rule holds by construction;\n * - the services the cells and runners read are this Effect's requirements,\n * so a test provides them the way a host does: `Effect.provide(layer)`;\n * - the Subs the machine wants run, through `opts.subscribe` or the engine's\n * built-in runner, started and stopped after every fold. Their Msgs are\n * folded once no Cmd's follow-up is pending. The drive settles once no Msg\n * is pending and no Sub is still running, so hand it a runner whose Stream\n * ends — a Sub that never ends keeps it waiting. A Stream that fails with\n * an error it did not map to a Msg dies, as it stops the engine's run.\n *\n * Interrupting the drive interrupts every cell and Sub still running.\n *\n * ```ts\n * const { state, trace } = await Effect.runPromise(\n * drive(machine, initial, { type: \"go\" }, {\n * fetch: (cmd) =>\n * Effect.gen(function* () {\n * const api = yield* Api;\n * return yield* api.get(cmd.id);\n * }),\n * }).pipe(Effect.provide(ApiTest)),\n * );\n * ```\n *\n * Fails with {@link DriveRoundsExceededError} when the machine is still\n * emitting work after `opts.maxRounds` rounds, {@link DriveNoHandlerError}\n * for a Cmd no cell answers, or a hand-written cell's own failure — each with\n * the partial trace, readable via `driveTraceOf`.\n */\nexport function drive<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n // The defaults read no services, so an omitted map adds none to `R`.\n I extends EffectInterpret<M, C> = EffectInterpret<M, C, never>,\n B extends EffectSubscribe<M, U> = EffectSubscribe<M, U, never>,\n>(\n machine: Machine<S, M, C, U, Ctx>,\n initial: S,\n msg: M,\n interpret: I,\n ...[opts]: Record<never, never> extends EffectDriveOptions<U, Ctx, B>\n ? [opts?: EffectDriveOptions<U, Ctx, B>]\n : [opts: EffectDriveOptions<U, Ctx, B>]\n): Effect.Effect<\n DriveResult<S, M, C>,\n EffectDriveError<M, C, I>,\n InterpretServices<I> | SubscribeServices<B>\n> {\n const options = (opts ?? {}) as {\n ctx?: Ctx;\n maxRounds?: number;\n clock?: () => number;\n subscribe?: Readonly<Record<string, AnyRunner | undefined>>;\n };\n return Effect.gen(function* () {\n const services = yield* Effect.context<\n InterpretServices<I> | SubscribeServices<B>\n >();\n const runEffect = Effect.runPromiseExitWith(services) as RunEffect;\n const fork = Effect.runForkWith(services) as ForkEffect;\n\n return yield* Effect.callback<\n DriveResult<S, M, C>,\n EffectDriveError<M, C, I>\n >((resume, signal) => {\n const cells = interpret as unknown as Readonly<\n Record<string, AnyCell | undefined>\n >;\n const defined = new Set((machine.cmds ?? []).map((def) => def.cmdType));\n const cellFor = (type: string): DriveCell<M, C> | undefined => {\n const cell = cells[type];\n if (cell === undefined) return undefined;\n return async (cmd) => {\n const exit = await runEffect(cellResult(cell(cmd)), { signal });\n if (signal.aborted) throw new DriveInterrupted();\n return settleCellExit(exit, defined.has(type));\n };\n };\n\n driveLoop(\n machine,\n initial,\n msg,\n options.ctx as Ctx,\n options.maxRounds ?? DEFAULT_MAX_ROUNDS,\n {\n contract: driveContractOf(machine, options.clock ?? Date.now),\n cellFor,\n subs: effectSubs<S, M>(\n machine,\n options.subscribe ?? {},\n fork,\n signal,\n ),\n },\n ).then(\n (result) => resume(Effect.succeed(result)),\n (error: unknown) => resume(failureOf<M, C, I>(error)),\n );\n });\n });\n}\n\n/**\n * Sort what left the loop: its own failures and a hand-written cell's failure\n * are typed, everything else — a defect, a contract breach, a failed Sub — dies.\n */\nfunction failureOf<M, C extends Cmd, I>(\n error: unknown,\n): Effect.Effect<never, EffectDriveError<M, C, I>> {\n if (\n error instanceof DriveRoundsExceededError ||\n error instanceof DriveNoHandlerError\n ) {\n return Effect.fail(error as EffectDriveError<M, C, I>);\n }\n if (error instanceof CellFailure) {\n annotateTrace(error.failure, driveTraceOf(error) ?? []);\n return Effect.fail(error.failure as EffectDriveError<M, C, I>);\n }\n if (error instanceof DriveInterrupted) return Effect.interrupt;\n return Effect.die(error);\n}\n\n/**\n * One started Sub. It stays registered while the machine wants it, even after\n * its Stream ends, so a Sub that finished is not started again — the engine\n * keeps it the same way. `stopped` drops any Msg that arrives after a fold\n * turned it off; `ended` says its Stream no longer runs.\n */\ninterface StartedSub {\n stopped: boolean;\n ended: boolean;\n fiber?: Fiber.Fiber<unknown, unknown>;\n}\n\n/**\n * The Effect engine's Subs as the shared loop runs them: each wanted Sub's\n * Stream is drained on its own fiber into a buffer the loop reads between\n * rounds.\n */\nfunction effectSubs<S, M>(\n machine: { readonly subs?: ReadonlyArray<unknown> },\n subscribe: Readonly<Record<string, AnyRunner | undefined>>,\n fork: ForkEffect,\n signal: AbortSignal,\n): DriveSubs<S, M> {\n const builtins = builtinRunners as unknown as Readonly<\n Record<string, AnyRunner | undefined>\n >;\n const started = new Map<string, StartedSub>();\n let buffered: M[] = [];\n let failed: Cause.Cause<unknown> | undefined;\n let wake: (() => void) | undefined;\n const notify = (): void => {\n const woken = wake;\n wake = undefined;\n woken?.();\n };\n signal.addEventListener(\"abort\", notify, { once: true });\n\n const stop = (sub: StartedSub): Promise<void> => {\n sub.stopped = true;\n return sub.ended || sub.fiber === undefined\n ? Promise.resolve()\n : Effect.runPromise(Fiber.interrupt(sub.fiber));\n };\n const anyRunning = (): boolean =>\n [...started.values()].some((sub) => !sub.ended);\n\n return {\n reconcile(state) {\n const desired = desiredSubs(machine, state);\n for (const [id, sub] of started) {\n if (desired.has(id)) continue;\n started.delete(id);\n void stop(sub);\n }\n for (const [id, sub] of desired) {\n if (started.has(id)) continue;\n const make = subscribe[sub.type] ?? builtins[sub.type];\n if (make === undefined) {\n throw new Error(\n `@demlik/tea: no subscribe runner for Sub type \"${sub.type}\". ` +\n \"Pass one to drive in `subscribe`.\",\n );\n }\n const entry: StartedSub = { stopped: false, ended: false };\n started.set(id, entry);\n entry.fiber = fork(\n Stream.runForEach(make(sub), (next) =>\n Effect.sync(() => {\n if (entry.stopped) return;\n buffered.push(next as M);\n notify();\n }),\n ).pipe(\n Effect.onExit((exit) =>\n Effect.sync(() => {\n entry.ended = true;\n if (\n !entry.stopped &&\n Exit.isFailure(exit) &&\n !Cause.hasInterruptsOnly(exit.cause)\n ) {\n failed ??= exit.cause;\n }\n notify();\n }),\n ),\n ),\n );\n }\n },\n async next() {\n for (;;) {\n if (signal.aborted) throw new DriveInterrupted();\n if (buffered.length > 0) {\n const out = buffered;\n buffered = [];\n return out;\n }\n if (failed !== undefined) throw Cause.squash(failed);\n if (!anyRunning()) return undefined;\n await new Promise<void>((resolve) => {\n wake = resolve;\n });\n }\n },\n async close() {\n signal.removeEventListener(\"abort\", notify);\n const stopping = [...started.values()].map(stop);\n started.clear();\n await Promise.all(stopping);\n },\n };\n}\n"]}
@@ -1,5 +1,5 @@
1
- import { C as Cmd, S as Sub, M as Machine, a as BuiltinSub, o as Interpret } from '../core-rDbOaJTN.js';
2
- import { C as CtxArg, R as Runtime } from '../runtime-types-B62Ucnxr.js';
1
+ import { C as Cmd, c as Sub, M as Machine, B as BuiltinSub } from '../core-Ce9ncpuv.js';
2
+ import { R as Runtime } from '../runtime-types-BKg2CGMx.js';
3
3
  import '@standard-schema/spec';
4
4
 
5
5
  /**
@@ -160,156 +160,6 @@ declare function bindMachine<S, M extends {
160
160
  type: string;
161
161
  }, C extends Cmd, U extends Sub, Ctx>(machine: Machine<S, M, C, U, Ctx>, ctx: Ctx): BoundMachine<S, M, C, U, Ctx>;
162
162
 
163
- /**
164
- * The default round bound when `opts.maxRounds` is omitted: **100**.
165
- *
166
- * A round is one drain of the pending Msgs plus one drain of the Cmds those
167
- * folds emitted, so a settling machine costs a handful; 100 is loose enough
168
- * that a legitimate retry ladder finishes inside it and tight enough that a
169
- * genuine cycle stops rather than hangs.
170
- */
171
- declare const DEFAULT_MAX_ROUNDS = 100;
172
- /**
173
- * One entry of a driven run's history, in dispatch order.
174
- *
175
- * - `msg` — a Msg folded through the machine's reducer. The seed Msg is the
176
- * first entry of every trace; every later one is a settle Msg a handler
177
- * returned (or fired through its injected `dispatch`).
178
- * - `cmd` — a Cmd handed to an interpret handler. It is recorded BEFORE the
179
- * handler is awaited, so a trace recovered from a rejected handler names the
180
- * Cmd that rejected as its last entry.
181
- */
182
- type DriveTraceEntry<M, C> = {
183
- readonly kind: "msg";
184
- readonly msg: M;
185
- } | {
186
- readonly kind: "cmd";
187
- readonly cmd: C;
188
- };
189
- /** What a settled {@link drive} hands back. */
190
- interface DriveResult<S, M, C> {
191
- /** The state after the machine went quiet — no Msg pending, no Cmd unperformed. */
192
- readonly state: S;
193
- /**
194
- * Every Msg folded and every Cmd dispatched, in order. Filtering it to its
195
- * `msg` entries and replaying those through `replay` from the same initial
196
- * state reproduces {@link DriveResult.state}.
197
- */
198
- readonly trace: readonly DriveTraceEntry<M, C>[];
199
- }
200
- /**
201
- * {@link drive}'s `ctx` field. `CtxArg` widened by exactly one case: a machine
202
- * declaring `ctx: undefined` in its `types` block — the pure shape every
203
- * how-to on this package writes — may omit it too. `CtxArg` alone keeps `ctx`
204
- * REQUIRED there, because `{}` is not assignable to `undefined`, which would
205
- * make the common pure call site spell `{ ctx: undefined }` to say nothing.
206
- * A machine whose `Ctx` carries a field a handler reads still requires it.
207
- */
208
- type DriveCtxArg<Ctx> = [undefined] extends [Ctx] ? {
209
- readonly ctx?: Ctx;
210
- } : CtxArg<Ctx>;
211
- /**
212
- * {@link drive}'s options. `ctx` is conditionally optional (see
213
- * {@link DriveCtxArg}).
214
- */
215
- type DriveOptions<Ctx> = DriveCtxArg<Ctx> & {
216
- /**
217
- * The round bound. Exceeding it throws {@link DriveRoundsExceededError};
218
- * `drive` never returns a half-driven state. Defaults to
219
- * {@link DEFAULT_MAX_ROUNDS} (100).
220
- */
221
- readonly maxRounds?: number;
222
- /**
223
- * The clock that stamps `at` on a `Cmd.define`d Cmd's minted `_ok` / `_err`
224
- * Msg — the one `run` takes. Defaults to `Date.now`; pin it for a test that
225
- * asserts on `at`.
226
- */
227
- readonly clock?: () => number;
228
- };
229
- /**
230
- * Raised when a driven machine is still emitting work after `maxRounds` rounds.
231
- * Follows the tea error dialect (`override readonly name`) and carries a `_tag`
232
- * discriminant, so a test branches on it by `instanceof` or on the tag.
233
- *
234
- * It carries the round count it stopped at and the PARTIAL trace up to that
235
- * point, which is what a test needs to see the cycle — the bound alone says
236
- * only that one exists.
237
- */
238
- declare class DriveRoundsExceededError<M, C> extends Error {
239
- /** The bound that was exceeded. */
240
- readonly maxRounds: number;
241
- /** The round the driver stopped at — always `maxRounds + 1`. */
242
- readonly rounds: number;
243
- /** Every Msg folded and Cmd dispatched before the driver gave up. */
244
- readonly trace: readonly DriveTraceEntry<M, C>[];
245
- readonly name = "DriveRoundsExceededError";
246
- readonly _tag: "DriveRoundsExceededError";
247
- constructor(
248
- /** The bound that was exceeded. */
249
- maxRounds: number,
250
- /** The round the driver stopped at — always `maxRounds + 1`. */
251
- rounds: number,
252
- /** Every Msg folded and Cmd dispatched before the driver gave up. */
253
- trace: readonly DriveTraceEntry<M, C>[]);
254
- }
255
- /**
256
- * Raised when a Cmd reaches {@link drive} with no handler for its `type` in the
257
- * `handlers` record. `Interpret`'s mapped type makes that a compile error at a
258
- * well-typed call site; this is the runtime arm for a handler record assembled
259
- * dynamically or cast.
260
- */
261
- declare class DriveNoHandlerError<M, C> extends Error {
262
- /** The Cmd `type` nothing in the handler record answered. */
263
- readonly cmdType: string;
264
- /** Every Msg folded and Cmd dispatched before the miss. */
265
- readonly trace: readonly DriveTraceEntry<M, C>[];
266
- readonly name = "DriveNoHandlerError";
267
- readonly _tag: "DriveNoHandlerError";
268
- constructor(
269
- /** The Cmd `type` nothing in the handler record answered. */
270
- cmdType: string,
271
- /** Every Msg folded and Cmd dispatched before the miss. */
272
- trace: readonly DriveTraceEntry<M, C>[]);
273
- }
274
- /**
275
- * Read back the partial trace `drive` attached to an error a handler rejected
276
- * with. Returns `undefined` for anything `drive` did not annotate.
277
- *
278
- * A rejecting handler's error propagates out of `drive` UNSWALLOWED — same
279
- * value, same `instanceof`, same `_tag` — so a test that already branches on
280
- * the handler's own error type keeps doing so. The trace rides along on a
281
- * non-enumerable property so it is recoverable without changing how the error
282
- * prints or serializes.
283
- */
284
- declare function driveTraceOf<M, C>(err: unknown): readonly DriveTraceEntry<M, C>[] | undefined;
285
- /**
286
- * Drive `machine` from `initial` through `msg` against the REAL interpret
287
- * `handlers`, feeding every settle Msg back until the machine goes quiet, and
288
- * hand back the settled state together with the whole history.
289
- *
290
- * `handlers` is the same table a host hands `run` as its `interpret` (e.g.
291
- * a jev knob's `resilient_run` handler), so the test drives the real interpreter and
292
- * mocks only the port beneath it.
293
- *
294
- * One round is: fold every pending Msg, then await a handler for every Cmd
295
- * those folds emitted. A Msg a handler returns — or fires through its injected
296
- * `dispatch` — is pending for the next round. A machine that emits no Cmd for
297
- * its seed Msg therefore settles in ONE round and never awaits a handler at
298
- * all.
299
- *
300
- * The `ctx` handed to each handler is `opts.ctx` augmented with a NO-OP `emit`,
301
- * so a handler that emits to a `Port` runs without a live runtime; nothing here
302
- * observes ports. Supply your own `emit` on `ctx` to capture them.
303
- *
304
- * @throws {DriveRoundsExceededError} when the machine is still emitting work
305
- * after `opts.maxRounds` rounds. It never returns a half-driven state.
306
- * @throws whatever a handler rejects with, unchanged — annotated with the
307
- * partial trace, readable via {@link driveTraceOf}.
308
- */
309
- declare function drive<S, M extends {
310
- type: string;
311
- }, C extends Cmd, U extends Sub, Ctx>(machine: Machine<S, M, C, U, Ctx>, initial: S, msg: M, handlers: Interpret<M, C, Ctx>, ...[opts]: Record<never, never> extends DriveOptions<Ctx> ? [opts?: DriveOptions<Ctx>] : [opts: DriveOptions<Ctx>]): Promise<DriveResult<S, M, C>>;
312
-
313
163
  /**
314
164
  * Assert that replaying `opts.msgs` is a pure function of the Msg log: the
315
165
  * final state and every emitted Cmd come out the same under two different
@@ -378,14 +228,18 @@ type StateFactoryAPI<S extends {
378
228
  * overrides (undefineds stripped), then stamps the `type` discriminator.
379
229
  *
380
230
  * @example
381
- * type S = { type: "a"; n: number } | { type: "b"; s: string };
382
- * const f = stateFactory<S>({ a: { n: 0 }, b: { s: "" } });
383
- * f.a(); // { type: "a", n: 0 }
384
- * f.a({ n: 42 }); // { type: "a", n: 42 }
385
- * f.b({ s: "hi" }); // { type: "b", s: "hi" }
231
+ * ```ts
232
+ * import { stateFactory } from "@demlik/tea/testing";
233
+ *
234
+ * type S = { type: "a"; n: number } | { type: "b"; s: string };
235
+ * const f = stateFactory<S>({ a: { n: 0 }, b: { s: "" } });
236
+ * f.a(); // { type: "a", n: 0 }
237
+ * f.a({ n: 42 }); // { type: "a", n: 42 }
238
+ * f.b({ s: "hi" }); // { type: "b", s: "hi" }
239
+ * ```
386
240
  */
387
241
  declare function stateFactory<S extends {
388
242
  type: string;
389
243
  }>(defaults: StateFactoryDefaults<S>): StateFactoryAPI<S>;
390
244
 
391
- export { type BoundMachine, DEFAULT_MAX_ROUNDS, type DriveCtxArg, DriveNoHandlerError, type DriveOptions, type DriveResult, DriveRoundsExceededError, type DriveTraceEntry, type ReplayOpts, type StateFactoryAPI, type StateFactoryDefaults, bindMachine, drive, driveTraceOf, expectActiveSubs, expectCmdEmitted, expectCmdSequence, expectFinalState, expectReplayDeterministic, noopRuntime, stateFactory, step };
245
+ export { type BoundMachine, type ReplayOpts, type StateFactoryAPI, type StateFactoryDefaults, bindMachine, expectActiveSubs, expectCmdEmitted, expectCmdSequence, expectFinalState, expectReplayDeterministic, noopRuntime, stateFactory, step };
@@ -1,5 +1,6 @@
1
- import '../chunk-C4U5D5HP.js';
2
- import { replay, cmdEdgeOver, Outcome, cmdEdge } from '../chunk-K4ETBNKS.js';
1
+ export { bindMachine } from '../chunk-JVKU2UQC.js';
2
+ import '../chunk-FNGZMVIT.js';
3
+ import { replay } from '../chunk-AIIXV4XB.js';
3
4
  import { expect, vi } from 'vitest';
4
5
 
5
6
  function expectFinalState(machine, opts, expected) {
@@ -22,133 +23,6 @@ function step(machine, loaded, msg, ctx) {
22
23
  const { state, cmds } = replay(machine, { msgs: [msg], ctx, loaded });
23
24
  return [state, cmds];
24
25
  }
25
- function bindMachine(machine, ctx) {
26
- return {
27
- step(loaded, msg) {
28
- const { state, cmds } = replay(machine, { msgs: [msg], ctx, loaded });
29
- return [state, cmds];
30
- },
31
- expectFinalState(opts, expected) {
32
- const { state } = replay(machine, { ...opts, ctx });
33
- expect(state).toEqual(expected);
34
- },
35
- expectCmdEmitted(opts, cmd) {
36
- const { cmds } = replay(machine, { ...opts, ctx });
37
- expect(cmds).toContainEqual(cmd);
38
- },
39
- expectCmdSequence(opts, expected) {
40
- const { cmds } = replay(machine, { ...opts, ctx });
41
- expect(cmds).toEqual(expected);
42
- },
43
- expectActiveSubs(opts, expected) {
44
- const { subs } = replay(machine, { ...opts, ctx });
45
- expect(subs).toEqual(expected);
46
- },
47
- replay(opts) {
48
- return replay(machine, { ...opts, ctx });
49
- }
50
- };
51
- }
52
-
53
- // src/testing/drive.ts
54
- var DEFAULT_MAX_ROUNDS = 100;
55
- var DriveRoundsExceededError = class extends Error {
56
- constructor(maxRounds, rounds, trace) {
57
- super(
58
- `@demlik/tea: drive did not settle within ${maxRounds} round(s) \u2014 the machine was still emitting work at round ${rounds}. The partial trace (${trace.length} entr${trace.length === 1 ? "y" : "ies"}) is on the error's \`trace\` field.`
59
- );
60
- this.maxRounds = maxRounds;
61
- this.rounds = rounds;
62
- this.trace = trace;
63
- }
64
- maxRounds;
65
- rounds;
66
- trace;
67
- name = "DriveRoundsExceededError";
68
- _tag = "DriveRoundsExceededError";
69
- };
70
- var DriveNoHandlerError = class extends Error {
71
- constructor(cmdType, trace) {
72
- super(
73
- `@demlik/tea: drive found no interpret handler for Cmd type "${cmdType}" \u2014 the handlers record must answer every Cmd the machine emits.`
74
- );
75
- this.cmdType = cmdType;
76
- this.trace = trace;
77
- }
78
- cmdType;
79
- trace;
80
- name = "DriveNoHandlerError";
81
- _tag = "DriveNoHandlerError";
82
- };
83
- var TRACE_KEY = "driveTrace";
84
- function driveTraceOf(err) {
85
- if (typeof err !== "object" || err === null) return void 0;
86
- const carried = err[TRACE_KEY];
87
- return Array.isArray(carried) ? carried : void 0;
88
- }
89
- async function drive(machine, initial, msg, handlers, ...[opts]) {
90
- const options = opts ?? {};
91
- const maxRounds = options.maxRounds ?? DEFAULT_MAX_ROUNDS;
92
- const bound = bindMachine(machine, options.ctx);
93
- const settle = cmdEdgeOver(machine.cmds ?? [], options.clock ?? Date.now);
94
- const noopEmit = { emit: () => {
95
- } };
96
- const handlerCtx = {
97
- ...noopEmit,
98
- ok: Outcome.ok,
99
- err: Outcome.err,
100
- [cmdEdge]: settle,
101
- ...options.ctx
102
- };
103
- const cells = handlers;
104
- const trace = [];
105
- let state = initial;
106
- let pending = [msg];
107
- let round = 0;
108
- while (pending.length > 0) {
109
- round += 1;
110
- if (round > maxRounds) {
111
- throw new DriveRoundsExceededError(maxRounds, round, trace);
112
- }
113
- const folding = pending;
114
- pending = [];
115
- const emitted = [];
116
- for (const next of folding) {
117
- trace.push({ kind: "msg", msg: next });
118
- const [advanced, cmds] = bound.step(state, next);
119
- state = advanced;
120
- emitted.push(...cmds);
121
- }
122
- for (const cmd of emitted) {
123
- trace.push({ kind: "cmd", cmd });
124
- const cell = cells[cmd.type];
125
- if (cell === void 0) {
126
- throw new DriveNoHandlerError(cmd.type, trace);
127
- }
128
- let settled;
129
- try {
130
- settled = settle(
131
- cmd,
132
- await cell(cmd, handlerCtx, (fired) => {
133
- pending.push(fired);
134
- })
135
- );
136
- } catch (err) {
137
- if (typeof err === "object" && err !== null) {
138
- Object.defineProperty(err, TRACE_KEY, {
139
- value: [...trace],
140
- enumerable: false,
141
- configurable: true,
142
- writable: true
143
- });
144
- }
145
- throw err;
146
- }
147
- if (settled !== void 0) pending.push(settled);
148
- }
149
- }
150
- return { state, trace };
151
- }
152
26
  var AMBIENT_A = { now: 1e6, rng: 0.123 };
153
27
  var AMBIENT_B = { now: 9876543, rng: 0.987 };
154
28
  function expectReplayDeterministic(machine, opts) {
@@ -248,6 +122,6 @@ function stateFactory(defaults) {
248
122
  return api;
249
123
  }
250
124
 
251
- export { DEFAULT_MAX_ROUNDS, DriveNoHandlerError, DriveRoundsExceededError, bindMachine, drive, driveTraceOf, expectActiveSubs, expectCmdEmitted, expectCmdSequence, expectFinalState, expectReplayDeterministic, noopRuntime, stateFactory, step };
125
+ export { expectActiveSubs, expectCmdEmitted, expectCmdSequence, expectFinalState, expectReplayDeterministic, noopRuntime, stateFactory, step };
252
126
  //# sourceMappingURL=index.js.map
253
127
  //# sourceMappingURL=index.js.map