@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,19 +1,19 @@
1
- import { M as Machine, I as Interpret, S as Subscribe, H as HandlerCtx, C as Cmd } from '../core-BKJW1Kmq.js';
2
- import { b as DeadlinesSub } from '../index-Diyo8WBO.js';
3
- export { a as DeadlineSub, m as deadlineSub, o as deadlinesSub, s as subscribeDeadline } from '../index-Diyo8WBO.js';
4
- import { M as MessageContent, T as ToolCall, a as ToolOutcome, C as ContentPart, b as Conversation, c as AnyToolDef, d as ToolsCtx, e as AgentState, f as AgentTurn, g as ToolResult, A as AgentEvent, h as TurnChunk, i as AgentMachineMsg, W as WiredToolMsg, j as AgentCmd, k as ToolCmd, P as PlainModel, S as StreamingModel, l as ToolFailureOf, m as AgentTerminalFailure, N as NotReserved, n as ToolDef, o as ToolThrown, p as AgentEndedStatus, q as AgentConfigCore, r as CompactionPolicy, s as AgentKnob } from '../machine-BuPTS1QH.js';
5
- export { t as AGENT_EVENT_TYPES, u as AgentBootMsg, v as AgentCancelMsg, w as AgentCompactErrMsg, x as AgentCompactOkMsg, y as AgentCompactRunCmd, z as AgentCompactionConfig, B as AgentConfig, D as AgentEventHead, E as AgentFailure, F as AgentLifecycleNote, G as AgentLlmErrMsg, H as AgentLlmOkMsg, I as AgentLlmRunCmd, J as AgentSnapshotConfig, K as AgentStatus, L as AgentTimerMsg, O as AgentToMachine, Q as Awaiting, R as COMPACTION_PURPOSE, U as CompactInterpret, V as CompactionOutputs, X as CompactionPurpose, Y as CompactionSummary, Z as FILE_OMITTED, _ as FilePart, $ as IMAGE_OMITTED, a0 as ImagePart, a1 as Llm, a2 as LlmCall, a3 as LlmErr, a4 as LlmFailMsg, a5 as LlmOk, a6 as LlmRunCmd, a7 as LlmSucceedMsg, a8 as MediaSource, a9 as MessageLoader, aa as ModelFactory, ab as ModelPort, ac as ModelStream, ad as PLAIN_MODEL_MISROUTE_REASON, ae as ReservedToolName, af as Schema, ag as SnapshotInterpret, ah as TOOL_RETRY_EXHAUSTED_TAG, ai as TOOL_TIMEOUT_TAG, aj as TaggedFailure, ak as TextPart, al as ToolConstructors, am as ToolError, an as ToolFail, ao as ToolFailure, ap as ToolHandler, aq as ToolInput, ar as ToolMsg, as as ToolOk, at as ToolRecord, au as ToolRejectedCmd, av as ToolRejection, aw as ToolResilience, ax as ToolResilienceError, ay as ToolRetryExhausted, az as ToolRouter, aA as ToolSettlement, aB as ToolTimedOut, aC as TurnUsage, aD as WiredToolCmd, aE as agentBootMsg, aF as agentCancelMsg, aG as agentEvents, aH as agentTurnSchema, aI as compactionSummarySchema, aJ as contentParts, aK as fanOutInterpret, aL as isAgentTurn, aM as isCompactionSummary, aN as isReservedToolName, aO as isStreamingModel, aP as isTurnUsage, aQ as liftAgent, aR as mergeInterpret, aS as omitMedia, aT as plainModel, aU as status, aV as tool, aW as toolErrorReason, aX as toolRouter } from '../machine-BuPTS1QH.js';
1
+ import { M as Machine, I as Interpret, S as Subscribe, H as HandlerCtx, C as Cmd } from '../core-DNDAEhO2.js';
2
+ import { b as DeadlinesSub } from '../index-Cg0QXnzf.js';
3
+ export { a as DeadlineSub, m as deadlineSub, o as deadlinesSub, s as subscribeDeadline } from '../index-Cg0QXnzf.js';
4
+ import { M as MessageContent, T as ToolCall, a as ToolOutcome, C as ContentPart, b as Conversation, c as AnyToolDef, d as ToolsCtx, e as AgentState, f as AgentTurn, g as ToolResult, A as AgentEvent, h as TurnChunk, i as AgentMachineMsg, W as WiredToolMsg, j as AgentCmd, k as ToolCmd, P as PlainModel, S as StreamingModel, l as ToolFailureOf, m as AgentTerminalFailure, N as NotReserved, n as ToolDef, o as ToolThrown, p as AgentEndedStatus, q as AgentConfigCore, r as CompactionPolicy, s as AgentKnob } from '../machine-C0WhOAyB.js';
5
+ export { t as AGENT_EVENT_TYPES, u as AgentBootMsg, v as AgentCancelMsg, w as AgentCompactErrMsg, x as AgentCompactOkMsg, y as AgentCompactRunCmd, z as AgentCompactionConfig, B as AgentConfig, D as AgentEventHead, E as AgentFailure, F as AgentLifecycleNote, G as AgentLlmErrMsg, H as AgentLlmOkMsg, I as AgentLlmRunCmd, J as AgentSnapshotConfig, K as AgentStatus, L as AgentTimerMsg, O as AgentToMachine, Q as Awaiting, R as COMPACTION_PURPOSE, U as CompactInterpret, V as CompactionOutputs, X as CompactionPurpose, Y as CompactionSummary, Z as FILE_OMITTED, _ as FilePart, $ as IMAGE_OMITTED, a0 as ImagePart, a1 as Llm, a2 as LlmCall, a3 as LlmErr, a4 as LlmFailMsg, a5 as LlmOk, a6 as LlmRunCmd, a7 as LlmSucceedMsg, a8 as MediaSource, a9 as MessageLoader, aa as ModelFactory, ab as ModelPort, ac as ModelStream, ad as PLAIN_MODEL_MISROUTE_REASON, ae as ReservedToolName, af as Schema, ag as SnapshotInterpret, ah as TOOL_RETRY_EXHAUSTED_TAG, ai as TOOL_TIMEOUT_TAG, aj as TaggedFailure, ak as TextPart, al as ToolConstructors, am as ToolError, an as ToolFail, ao as ToolFailure, ap as ToolHandler, aq as ToolInput, ar as ToolMsg, as as ToolOk, at as ToolRecord, au as ToolRejectedCmd, av as ToolRejection, aw as ToolResilience, ax as ToolResilienceError, ay as ToolRetryExhausted, az as ToolRouter, aA as ToolSettlement, aB as ToolTimedOut, aC as TurnUsage, aD as WiredToolCmd, aE as agentBootMsg, aF as agentCancelMsg, aG as agentEvents, aH as agentTurnSchema, aI as compactionSummarySchema, aJ as contentParts, aK as fanOutInterpret, aL as isAgentTurn, aM as isCompactionSummary, aN as isReservedToolName, aO as isStreamingModel, aP as isTurnUsage, aQ as liftAgent, aR as mergeInterpret, aS as omitMedia, aT as plainModel, aU as status, aV as tool, aW as toolErrorReason, aX as toolRouter } from '../machine-C0WhOAyB.js';
6
6
  import { StandardSchemaV1 } from '@standard-schema/spec';
7
- import { C as CtxArg, S as Store } from '../runtime-types-BPKp2bGo.js';
8
- import { E as EndedRun } from '../index-CYrUWi1c.js';
9
- export { M as MonitoredRunCmd, R as RunFailure } from '../index-CYrUWi1c.js';
7
+ import { C as CtxArg, S as StoreAndOnError, O as OnError, U as UnfencedStore, b as Store } from '../runtime-types-CP-ykwgf.js';
8
+ import { E as EndedRun } from '../index-CAsUE_d1.js';
9
+ export { M as MonitoredRunCmd, R as RunFailure } from '../index-CAsUE_d1.js';
10
10
  import { RetryPolicy } from '../retry-backoff/index.js';
11
- import '../types-BiXj7k_k.js';
12
- import '../index-B-VC4qjx.js';
11
+ import '../types-S53WDy_h.js';
12
+ import '../index-C92SgNWz.js';
13
13
  import '../protocol-ClRZBhHQ.js';
14
- import '../index-1hit3dJ9.js';
15
- import '../index-Cuy1_xTh.js';
16
- import '../index-DB8Yn0-K.js';
14
+ import '../index-9P1PaXIV.js';
15
+ import '../index-BBLI2WWN.js';
16
+ import '../index-COeX9Bmz.js';
17
17
 
18
18
  /**
19
19
  * @demlik/tea/agent — `defineAgent`, the convenience layer over `createAgent`.
@@ -408,9 +408,11 @@ type DefinedAgentEvent<T extends AnyToolDef> = AgentEvent<ToolResult<T>>;
408
408
  *
409
409
  * `ctx` is the plain object the tools' handlers read (ADR 0020). A host with a
410
410
  * resource to release wraps `run` in its own `try` / `finally`.
411
+ *
412
+ * `onError` is the run's sink, and a fenced `store` needs one (see
413
+ * {@link StoreAndOnError}).
411
414
  */
412
- type DefinedAgentRunOptions<T extends AnyToolDef> = CtxArg<DefinedAgentCtx<T>> & {
413
- readonly store?: Store<DefinedAgentState<T>>;
415
+ type DefinedAgentRunOptions<T extends AnyToolDef> = CtxArg<DefinedAgentCtx<T>> & StoreAndOnError<DefinedAgentState<T>> & {
414
416
  /** The run's identity. Omit → a fresh UUID. */
415
417
  readonly runId?: string;
416
418
  /** The clock that stamps `at`. Omit → `Date.now`. */
@@ -654,12 +656,23 @@ type AgentToolSpec<Args, Ok, CT extends AnyToolDef, Ctx> = {
654
656
  */
655
657
  readonly namespace: (ctx: HandlerCtx<Ctx>) => string;
656
658
  /**
657
- * The child run's Store at `key`. The same key must reach the same durable
658
- * cell on every call, in every process — that is what makes a re-fired call
659
- * resume the child instead of restarting it.
659
+ * What the child run's failures with no caller to reject at reach. See
660
+ * `run`'s `onError`.
660
661
  */
662
+ readonly onError?: OnError;
663
+ } & ChildCtxOption<CT, Ctx> & ChildStoreOption<CT, Ctx>;
664
+ /**
665
+ * The child run's Store at `key`. The same key must reach the same durable cell
666
+ * on every call, in every process — that is what makes a re-fired call resume
667
+ * the child instead of restarting it. A child on a fenced Store needs an
668
+ * `onError`, the same rule `run`'s own `store` follows (ADR 0031).
669
+ */
670
+ type ChildStoreOption<CT extends AnyToolDef, Ctx> = {
671
+ readonly store: (key: string, ctx: HandlerCtx<Ctx>) => UnfencedStore<DefinedAgentState<CT>>;
672
+ } | {
661
673
  readonly store: (key: string, ctx: HandlerCtx<Ctx>) => Store<DefinedAgentState<CT>>;
662
- } & ChildCtxOption<CT, Ctx>;
674
+ readonly onError: OnError;
675
+ };
663
676
  /**
664
677
  * Wrap a child `defineAgent` as a tool a parent `defineAgent` calls and awaits
665
678
  * — pass it the child, how to phrase its input and how to read its answer, and
@@ -1,16 +1,18 @@
1
- export { AGENT_EVENT_TYPES, COMPACTION_PURPOSE, FILE_OMITTED, IMAGE_OMITTED, PLAIN_MODEL_MISROUTE_REASON, TOOL_RETRY_EXHAUSTED_TAG, TOOL_TIMEOUT_TAG, agentBootMsg, agentCancelMsg, agentEvents, agentTool, agentTurnSchema, compactionSummarySchema, contentParts, createAgent, defineAgent, fanOutInterpret, isAgentTurn, isCompactionSummary, isReservedToolName, isStreamingModel, isTurnUsage, liftAgent, mergeInterpret, omitMedia, plainModel, renderPrompt, status, tool, toolErrorReason, toolRouter, transcript } from '../chunk-W6XWI6QE.js';
2
- import '../chunk-6NIQIUGR.js';
3
- import '../chunk-YDF6P74H.js';
1
+ export { AGENT_EVENT_TYPES, COMPACTION_PURPOSE, FILE_OMITTED, IMAGE_OMITTED, PLAIN_MODEL_MISROUTE_REASON, TOOL_RETRY_EXHAUSTED_TAG, TOOL_TIMEOUT_TAG, agentBootMsg, agentCancelMsg, agentEvents, agentTool, agentTurnSchema, compactionSummarySchema, contentParts, createAgent, defineAgent, fanOutInterpret, isAgentTurn, isCompactionSummary, isReservedToolName, isStreamingModel, isTurnUsage, liftAgent, mergeInterpret, omitMedia, plainModel, renderPrompt, status, tool, toolErrorReason, toolRouter, transcript } from '../chunk-WZEDIUWR.js';
2
+ import '../chunk-VH5MYJ2K.js';
3
+ import '../chunk-IAHEYPYJ.js';
4
4
  import '../chunk-QNGYQMZB.js';
5
- import '../chunk-MBQR3LFW.js';
6
- export { deadlineSub, deadlinesSub, subscribeDeadline } from '../chunk-QIE4Y6BK.js';
5
+ import '../chunk-NFOGYDRS.js';
6
+ export { deadlineSub, deadlinesSub, subscribeDeadline } from '../chunk-UGTC33VH.js';
7
7
  import '../chunk-VBDU4JL6.js';
8
8
  import '../chunk-4LPY67E4.js';
9
9
  import '../chunk-TXN2KFHF.js';
10
- import '../chunk-EB5OMOXX.js';
11
- import '../chunk-VM7PMFV5.js';
12
- import '../chunk-Z7IL735U.js';
13
- import '../chunk-VHKDUBXC.js';
14
- import '../chunk-GAVLND5C.js';
10
+ import '../chunk-7K7SEYCH.js';
11
+ import '../chunk-GFGOQIEC.js';
12
+ import '../chunk-2A5XTKQQ.js';
13
+ import '../chunk-R2YNDRGL.js';
14
+ import '../chunk-DEAPNMKO.js';
15
+ import '../chunk-BVFS3ICC.js';
16
+ import '../chunk-A3QPMX5M.js';
15
17
  //# sourceMappingURL=index.js.map
16
18
  //# sourceMappingURL=index.js.map
@@ -1,5 +1,6 @@
1
- import { restore } from './chunk-Z7IL735U.js';
2
- import { RuntimeDiscardNotice, detachWork, DispatchDiscardedError, QuiescenceTimeoutError, IdentityDropNotice, checkedStep, cmdContractOver, Outcome, cmdEdge, isFencedStore, RuntimeDiscardedError, NoCellError, lookupCell, structuralHash, desiredSub, followUps, DrainTimeoutNotice, DisposeTimeoutNotice } from './chunk-GAVLND5C.js';
1
+ import { restore } from './chunk-R2YNDRGL.js';
2
+ import { installedState } from './chunk-DEAPNMKO.js';
3
+ import { isFencedStore, RuntimeDiscardNotice, detachWork, acceptedTypes, DispatchDiscardedError, QuiescenceTimeoutError, IdentityDropNotice, checkedStep, cmdContractOver, Outcome, cmdEdge, SubFailedError, RuntimeDiscardedError, NoCellError, lookupCell, structuralHash, desiredSub, followUps, DrainTimeoutNotice, DisposeTimeoutNotice } from './chunk-A3QPMX5M.js';
3
4
 
4
5
  // src/internal/engine/builtins.ts
5
6
  function supervision(declared, machine) {
@@ -17,7 +18,7 @@ function supervision(declared, machine) {
17
18
  case "escalate":
18
19
  throw reduceError;
19
20
  default:
20
- loop.halt();
21
+ loop.halt(reduceError);
21
22
  throw reduceError;
22
23
  }
23
24
  }
@@ -98,6 +99,14 @@ function fencing() {
98
99
  }
99
100
  });
100
101
  }
102
+ function refuseFencedRunWithoutSink(store, onError) {
103
+ if (onError !== void 0 || store === void 0 || !isFencedStore(store)) {
104
+ return;
105
+ }
106
+ throw new Error(
107
+ "@demlik/tea: a run on a fenced store needs an `onError`. Without one, a lost fence ends the process and every run in it. Pass `onError` to `run`."
108
+ );
109
+ }
101
110
  function ports() {
102
111
  return (loop) => {
103
112
  const registry = /* @__PURE__ */ new Map();
@@ -133,8 +142,10 @@ function observation() {
133
142
  const bootHandlers = /* @__PURE__ */ new Set();
134
143
  let booted = false;
135
144
  return {
136
- commit(msg, state) {
145
+ installed() {
137
146
  loop.fanout(listeners, "listener", (listener) => listener());
147
+ },
148
+ commit(msg, state) {
138
149
  if (msg === void 0) {
139
150
  booted = true;
140
151
  loop.fanout(bootHandlers, "boot", (handler) => handler(state));
@@ -205,23 +216,57 @@ function terminality(isTerminal) {
205
216
  const holds = isTerminal ?? (() => false);
206
217
  return (loop) => {
207
218
  const waiters = /* @__PURE__ */ new Set();
219
+ let failure;
220
+ let stoppedBy;
208
221
  return {
222
+ subFailed(error) {
223
+ failure ??= { answer: new SubFailedError(error) };
224
+ },
225
+ halted(error) {
226
+ failure ??= { answer: error };
227
+ },
228
+ drained() {
229
+ if (failure === void 0) return;
230
+ stoppedBy = failure;
231
+ const state = loop.state();
232
+ const terminal = holds(state);
233
+ if (terminal && loop.saveStatus().type !== "saved") return;
234
+ const parked = [...waiters];
235
+ waiters.clear();
236
+ for (const waiter of parked) {
237
+ if (terminal) waiter.resolve(state);
238
+ else waiter.reject(stoppedBy.answer);
239
+ }
240
+ },
209
241
  commit(_msg, state) {
242
+ if (loop.saveStatus().type !== "saved" || waiters.size === 0 || !holds(state))
243
+ return;
244
+ const parked = [...waiters];
245
+ waiters.clear();
246
+ for (const waiter of parked) waiter.resolve(state);
247
+ },
248
+ saveFailed(state, error) {
210
249
  if (waiters.size === 0 || !holds(state)) return;
211
250
  const parked = [...waiters];
212
251
  waiters.clear();
213
- for (const resolve of parked) resolve(state);
252
+ for (const waiter of parked) waiter.reject(error);
214
253
  },
215
254
  handle: {
216
255
  result() {
217
256
  const current = loop.state();
218
- return holds(current) ? current : void 0;
257
+ return loop.saveStatus().type === "saved" && holds(current) ? current : void 0;
219
258
  },
220
259
  done() {
221
260
  const current = loop.state();
222
- if (holds(current)) return Promise.resolve(current);
223
- return new Promise((resolve) => {
224
- waiters.add(resolve);
261
+ if (holds(current)) {
262
+ const saved = loop.saveStatus();
263
+ if (saved.type === "saved") return Promise.resolve(current);
264
+ if (saved.type === "failed") return Promise.reject(saved.error);
265
+ } else if (stoppedBy !== void 0) {
266
+ return Promise.reject(stoppedBy.answer);
267
+ }
268
+ return new Promise((resolve, reject) => {
269
+ waiters.add({ resolve, reject });
225
270
  });
226
271
  }
227
272
  }
@@ -266,6 +311,7 @@ function builtinExtensions(machine, opts) {
266
311
 
267
312
  // src/internal/engine/loop.ts
268
313
  var liveWork = /* @__PURE__ */ Symbol("demlik-tea.liveWork");
314
+ var post = /* @__PURE__ */ Symbol("demlik-tea.post");
269
315
  function defaultOnError(error, _context) {
270
316
  if (error instanceof RuntimeDiscardNotice) {
271
317
  console.warn(error);
@@ -291,6 +337,21 @@ var RunningSub = class {
291
337
  };
292
338
  failed = false;
293
339
  };
340
+ var MsgLine = class {
341
+ counts = /* @__PURE__ */ new Map();
342
+ joined(type) {
343
+ this.counts.set(type, (this.counts.get(type) ?? 0) + 1);
344
+ }
345
+ reachedTurn(type) {
346
+ const left = (this.counts.get(type) ?? 0) - 1;
347
+ if (left > 0) this.counts.set(type, left);
348
+ else this.counts.delete(type);
349
+ }
350
+ /** The counts now, in a fresh object later turns cannot change. */
351
+ snapshot() {
352
+ return Object.fromEntries(this.counts);
353
+ }
354
+ };
294
355
  function startLoop(config) {
295
356
  const { idleCap, disposeTimeoutMs } = config;
296
357
  const onError = config.onError ?? defaultOnError;
@@ -312,18 +373,27 @@ function startLoop(config) {
312
373
  }
313
374
  }
314
375
  let state;
376
+ let installed;
377
+ let saveStatus = { type: "pending" };
315
378
  let bootError = null;
316
379
  let gate = "open";
317
380
  const inFlight = /* @__PURE__ */ new Set();
318
381
  const services = {
319
382
  report,
320
383
  fanout,
321
- halt() {
384
+ halt(error) {
385
+ const stopping = gate !== "open";
322
386
  if (gate !== "abandoned") gate = "closed";
387
+ for (const ext of extensions) ext.halted?.(error);
388
+ if (!stopping) config.stopOnFailure({ kind: "halt", error });
323
389
  },
324
- state: () => state
390
+ state: () => state,
391
+ saveStatus: () => config.store === void 0 ? { type: "saved" } : saveStatus
325
392
  };
326
393
  const extensions = config.extensions.map((make) => make(services));
394
+ function lastStateReached() {
395
+ fanout(extensions, "sub", (ext) => ext.drained?.());
396
+ }
327
397
  const update = extensions.reduceRight(
328
398
  (next, ext) => ext.update ? ext.update(next) : next,
329
399
  config.reduce
@@ -333,6 +403,9 @@ function startLoop(config) {
333
403
  (inner, ext) => ext.store ? ext.store(inner) : inner,
334
404
  config.store
335
405
  );
406
+ const installedCallbacks = extensions.flatMap(
407
+ (ext) => ext.installed ? [ext.installed] : []
408
+ );
336
409
  const commitCallbacks = extensions.flatMap(
337
410
  (ext) => ext.commit ? [ext.commit] : []
338
411
  );
@@ -343,6 +416,7 @@ function startLoop(config) {
343
416
  { [detachWork]: detachInFlight }
344
417
  );
345
418
  let tail = Promise.resolve();
419
+ const line = new MsgLine();
346
420
  const subRegistry = /* @__PURE__ */ new Map();
347
421
  function stopSubs(ids) {
348
422
  for (const id of ids) {
@@ -388,7 +462,8 @@ function startLoop(config) {
388
462
  new DrainTimeoutNotice(
389
463
  [...inFlight].flatMap(({ cmdType }) => cmdType ?? []),
390
464
  inFlight.size,
391
- disposeTimeoutMs
465
+ disposeTimeoutMs,
466
+ line.snapshot()
392
467
  ),
393
468
  "discard"
394
469
  );
@@ -469,7 +544,8 @@ function startLoop(config) {
469
544
  report(error, "sub");
470
545
  if (gate !== "open") return;
471
546
  gate = "draining";
472
- config.stopOnSubFailure(error);
547
+ for (const ext of extensions) ext.subFailed?.(error);
548
+ config.stopOnFailure({ kind: "sub", error });
473
549
  }
474
550
  function dispatchUnawaited(msg) {
475
551
  enqueueDispatch(msg, "loop").catch(reportUndelivered);
@@ -510,8 +586,20 @@ function startLoop(config) {
510
586
  }
511
587
  async function commit(next, msg, cmds) {
512
588
  state = next;
513
- if (store && gate !== "abandoned") await store.save(next);
589
+ saveStatus = { type: "pending" };
590
+ if (store && gate !== "abandoned") {
591
+ try {
592
+ await store.save(next);
593
+ saveStatus = { type: "saved" };
594
+ } catch (error) {
595
+ saveStatus = { type: "failed", error };
596
+ for (const ext of extensions) ext.saveFailed?.(next, error);
597
+ throw error;
598
+ }
599
+ }
514
600
  reconcileSubs();
601
+ installed = next;
602
+ for (const callback of installedCallbacks) callback();
515
603
  try {
516
604
  await runCmds(cmds);
517
605
  } finally {
@@ -538,15 +626,33 @@ function startLoop(config) {
538
626
  gate === "draining" || gate === "abandoned" && origin === "loop" ? new DispatchDiscardedError(msg.type) : new RuntimeStoppedError(msg.type)
539
627
  );
540
628
  }
629
+ return enqueueStep(msg, origin, stepDispatch);
630
+ }
631
+ function enqueueStep(msg, origin, step) {
632
+ line.joined(msg.type);
541
633
  const next = tail.then(() => {
542
- if (gate === "abandoned") throw new DispatchDiscardedError(msg.type);
634
+ line.reachedTurn(msg.type);
635
+ if (gate === "abandoned") {
636
+ if (origin === "caller") throw new DispatchDiscardedError(msg.type);
637
+ return;
638
+ }
543
639
  if (bootError !== null) throw bootError;
544
- return stepDispatch(msg);
640
+ return step(msg);
545
641
  });
546
642
  tail = next.catch(() => {
547
643
  });
548
644
  return next;
549
645
  }
646
+ function postMsg(msg, takes) {
647
+ if (gate !== "open") return "stopped";
648
+ if (!takes(state)) return "no_cell";
649
+ enqueueStep(
650
+ msg,
651
+ "tell",
652
+ (queued) => takes(state) ? stepDispatch(queued) : void 0
653
+ ).catch(reportUndelivered);
654
+ return "queued";
655
+ }
550
656
  async function drainToQuiescence() {
551
657
  for (let i = 0; i < idleCap; i++) {
552
658
  const observed = tail;
@@ -570,6 +676,7 @@ function startLoop(config) {
570
676
  report(new RuntimeDiscardedError(inFlight.size), "discard");
571
677
  }
572
678
  await drainTail();
679
+ lastStateReached();
573
680
  stopSubs([...subRegistry.keys()]);
574
681
  await drainDisposals();
575
682
  if (store && state !== void 0 && bootError === null) {
@@ -583,6 +690,8 @@ function startLoop(config) {
583
690
  let stopped;
584
691
  const core = {
585
692
  [liveWork]: () => ({ subs: subRegistry.size, cmds: inFlight.size }),
693
+ [post]: postMsg,
694
+ [installedState]: () => installed,
586
695
  dispatch: dispatchToQuiescence,
587
696
  dispatchOnce: (msg) => enqueueDispatch(msg),
588
697
  getState() {
@@ -604,6 +713,20 @@ function startLoop(config) {
604
713
  return handle;
605
714
  }
606
715
 
607
- export { RuntimeStoppedError, builtinExtensions, defaultOnError, liveWork, startLoop };
608
- //# sourceMappingURL=chunk-VM7PMFV5.js.map
609
- //# sourceMappingURL=chunk-VM7PMFV5.js.map
716
+ // src/internal/engine/tell.ts
717
+ function tellRun(machine, runtime, msg) {
718
+ const postable = runtime[post];
719
+ if (postable === void 0) {
720
+ throw new Error(
721
+ "@demlik/tea: `tell` needs a run handle from `run`; this object has no line to post to."
722
+ );
723
+ }
724
+ return postable(
725
+ msg,
726
+ (state) => acceptedTypes(machine, state).includes(msg.type)
727
+ );
728
+ }
729
+
730
+ export { RuntimeStoppedError, builtinExtensions, defaultOnError, liveWork, post, refuseFencedRunWithoutSink, startLoop, tellRun };
731
+ //# sourceMappingURL=chunk-2A5XTKQQ.js.map
732
+ //# sourceMappingURL=chunk-2A5XTKQQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/internal/engine/builtins.ts","../src/internal/engine/loop.ts","../src/internal/engine/tell.ts"],"names":[],"mappings":";;;;;AAmDO,SAAS,WAAA,CACd,UACA,OAAA,EACuB;AACvB,EAAA,MAAM,MAAA,GACJ,QAAA,KAAa,MAAA,GACR,EAAE,QAAA,EAAU,MAAA,EAAO,GACpB,OAAO,QAAA,KAAa,QAAA,GACjB,EAAE,QAAA,EAAU,UAAS,GACtB,QAAA;AACR,EAAA,OAAO,CAAC,IAAA,MAAU;AAAA,IAChB,MAAA,EAAQ,CAAC,IAAA,KAAS,CAAC,OAAO,GAAA,KAAQ;AAChC,MAAA,IAAI;AACF,QAAA,OAAO,IAAA,CAAK,OAAO,GAAG,CAAA;AAAA,MACxB,SAAS,WAAA,EAAa;AACpB,QAAA,IAAI,UAAU,OAAA,EAAS,WAAA,EAAa,KAAA,EAAO,GAAG,GAAG,MAAM,WAAA;AACvD,QAAA,IAAA,CAAK,MAAA,CAAO,aAAa,QAAQ,CAAA;AACjC,QAAA,QAAQ,OAAO,QAAA;AAAU,UACvB,KAAK,SAAA;AAEH,YAAA,OAAO,CAAC,OAAO,SAAA,CAAU,KAAA,EAAO,KAAK,WAAW,CAAA,EAAG,EAAE,CAAA;AAAA,UACvD,KAAK,UAAA;AACH,YAAA,MAAM,WAAA;AAAA,UACR;AACE,YAAA,IAAA,CAAK,KAAK,WAAW,CAAA;AACrB,YAAA,MAAM,WAAA;AAAA;AACV,MACF;AAAA,IACF;AAAA,GACF,CAAA;AACF;AAWA,SAAS,SAAA,CACP,OAAA,EACA,KAAA,EACA,KAAA,EACA,GAAA,EACS;AACT,EAAA,OACE,iBAAiB,WAAA,IACjB,UAAA,CAAW,SAAS,KAAA,EAAO,GAAG,EAAE,IAAA,KAAS,MAAA;AAE7C;AAaO,SAAS,eACd,QAAA,EACuB;AACvB,EAAA,OAAO,CAAC,IAAA,KAAS;AACf,IAAA,IAAI,QAAA,KAAa,MAAA,EAAW,OAAO,EAAC;AACpC,IAAA,MAAM,YAAA,GAAe,CAAC,GAAA,EAAQ,KAAA,KAAsB;AAClD,MAAA,MAAM,SAAA,GAAY,QAAA,CAAS,KAAA,CAAM,GAAG,CAAA;AACpC,MAAA,IAAI,SAAA,KAAc,QAAW,OAAO,KAAA;AACpC,MAAA,MAAM,GAAA,GAAM,QAAA,CAAS,OAAA,CAAQ,KAAK,CAAA;AAClC,MAAA,IAAI,GAAA,KAAQ,QAAW,OAAO,KAAA;AAC9B,MAAA,OAAO,cAAA,CAAe,SAAS,CAAA,KAAM,cAAA,CAAe,GAAG,CAAA;AAAA,IACzD,CAAA;AACA,IAAA,OAAO;AAAA,MACL,MAAA,EAAQ,CAAC,IAAA,KAAS,CAAC,OAAO,GAAA,KAAQ;AAChC,QAAA,IAAI,CAAC,aAAa,GAAA,EAAK,KAAK,GAAG,OAAO,IAAA,CAAK,OAAO,GAAG,CAAA;AACrD,QAAA,IAAA,CAAK,OAAO,IAAI,kBAAA,CAAmB,GAAA,CAAI,IAAI,GAAG,eAAe,CAAA;AAC7D,QAAA,OAAO,IAAA;AAAA,MACT;AAAA,KACF;AAAA,EACF,CAAA;AACF;AAQO,SAAS,SAAA,GAId;AACA,EAAA,OAAO,OAAO;AAAA,IACZ,MAAA,EAAQ,CAAC,IAAA,KAAS,CAAC,OAAO,GAAA,KAAQ,WAAA,CAAY,KAAA,EAAO,GAAA,EAAK,IAAI;AAAA,GAChE,CAAA;AACF;AAoBO,SAAS,cAAA,CACd,MACA,KAAA,EACuB;AACvB,EAAA,OAAO,CAAC,IAAA,KAAS;AACf,IAAA,MAAM,EAAE,MAAA,EAAQ,aAAA,EAAe,OAAM,GAAI,eAAA,CAAgB,MAAM,KAAK,CAAA;AACpE,IAAA,MAAM,OAAA,GAAU,IAAI,GAAA,CAAI,IAAA,CAAK,IAAI,CAAC,CAAA,KAAM,CAAA,CAAE,OAAO,CAAC,CAAA;AAClD,IAAA,OAAO;AAAA,MACL,SAAA,EAAW,CAAC,IAAA,KAAS,OAAO,KAAK,QAAA,KAAa;AAC5C,QAAA,MAAM,OAAQ,GAAA,CAAyB,IAAA;AACvC,QAAA,MAAM,UAAU,OAAA,CAAQ,GAAA,CAAI,IAAI,CAAA,GAC5B,CAAC,GAAA,KAAW;AACV,UAAA,IAAI;AACF,YAAA,KAAA,CAAM,KAAyB,GAAG,CAAA;AAAA,UACpC,SAAS,GAAA,EAAK;AACZ,YAAA,IAAA,CAAK,MAAA,CAAO,KAAK,WAAW,CAAA;AAC5B,YAAA;AAAA,UACF;AACA,UAAA,QAAA,CAAS,GAAG,CAAA;AAAA,QACd,CAAA,GACA,QAAA;AACJ,QAAA,IAAI;AACF,UAAA,OAAO,OAAO,GAAA,EAAyB,MAAM,IAAA,CAAK,GAAA,EAAK,OAAO,CAAC,CAAA;AAAA,QACjE,SAAS,GAAA,EAAK;AACZ,UAAA,IAAI,CAAC,OAAA,CAAQ,GAAA,CAAI,IAAI,GAAG,MAAM,GAAA;AAC9B,UAAA,IAAA,CAAK,MAAA,CAAO,KAAK,WAAW,CAAA;AAC5B,UAAA,OAAO,MAAA;AAAA,QACT;AAAA,MACF,CAAA;AAAA,MACA,GAAA,EAAK,EAAE,EAAA,EAAI,OAAA,CAAQ,EAAA,EAAI,GAAA,EAAK,OAAA,CAAQ,GAAA,EAAK,CAAC,OAAO,GAAG,MAAA;AAAO,KAC7D;AAAA,EACF,CAAA;AACF;AAWO,SAAS,OAAA,GAA0C;AACxD,EAAA,OAAO,OAAO;AAAA,IACZ,KAAA,EAAO,CAAC,KAAA,KAA8B;AACpC,MAAA,IAAI,CAAC,aAAA,CAAc,KAAK,CAAA,EAAG,OAAO,KAAA;AAClC,MAAA,IAAI,OAAA,GAAU,CAAA;AACd,MAAA,OAAO;AAAA,QACL,MAAM,IAAA,GAAO;AACX,UAAA,MAAM,IAAA,GAAO,MAAM,KAAA,CAAM,UAAA,EAAW;AACpC,UAAA,OAAA,GAAU,IAAA,CAAK,OAAA;AACf,UAAA,OAAO,IAAA,CAAK,GAAA;AAAA,QACd,CAAA;AAAA,QACA,MAAM,KAAK,KAAA,EAAO;AAChB,UAAA,OAAA,GAAU,MAAM,KAAA,CAAM,UAAA,CAAW,KAAA,EAAO,OAAO,CAAA;AAAA,QACjD,CAAA;AAAA,QACA,OAAA,EAAS,CAAC,GAAA,KAAQ,KAAA,CAAM,QAAQ,GAAG;AAAA,OACrC;AAAA,IACF;AAAA,GACF,CAAA;AACF;AAYO,SAAS,0BAAA,CACd,OACA,OAAA,EACM;AACN,EAAA,IAAI,YAAY,MAAA,IAAa,KAAA,KAAU,UAAa,CAAC,aAAA,CAAc,KAAK,CAAA,EAAG;AACzE,IAAA;AAAA,EACF;AACA,EAAA,MAAM,IAAI,KAAA;AAAA,IACR;AAAA,GACF;AACF;AASO,SAAS,KAAA,GAAwC;AACtD,EAAA,OAAO,CAAC,IAAA,KAAS;AACf,IAAA,MAAM,QAAA,uBAAe,GAAA,EAAkD;AACvE,IAAA,SAAS,IAAA,CAAQ,MAAe,KAAA,EAAgB;AAC9C,MAAA,MAAM,WAAA,GAAc,QAAA,CAAS,GAAA,CAAI,IAAqB,CAAA;AACtD,MAAA,IAAI,CAAC,WAAA,IAAe,WAAA,CAAY,IAAA,KAAS,CAAA,EAAG;AAC5C,MAAA,IAAA,CAAK,OAAO,WAAA,EAAa,WAAA,EAAa,CAAC,QAAA,KAAa,QAAA,CAAS,KAAK,CAAC,CAAA;AAAA,IACrE;AACA,IAAA,OAAO;AAAA,MACL,GAAA,EAAK,EAAE,IAAA,EAAK;AAAA,MACZ,MAAA,EAAQ;AAAA,QACN,aAAA,CACE,MACA,QAAA,EACY;AACZ,UAAA,MAAM,GAAA,GAAM,IAAA;AACZ,UAAA,MAAM,SAAS,QAAA,CAAS,GAAA,CAAI,GAAG,CAAA,wBAAS,GAAA,EAAI;AAC5C,UAAA,QAAA,CAAS,GAAA,CAAI,KAAK,MAAM,CAAA;AACxB,UAAA,MAAM,MAAA,GAAS,QAAA;AACf,UAAA,MAAA,CAAO,IAAI,MAAM,CAAA;AACjB,UAAA,OAAO,MAAM;AACX,YAAA,MAAA,CAAO,OAAO,MAAM,CAAA;AACpB,YAAA,IAAI,OAAO,IAAA,KAAS,CAAA,IAAK,QAAA,CAAS,GAAA,CAAI,GAAG,CAAA,KAAM,MAAA;AAC7C,cAAA,QAAA,CAAS,OAAO,GAAG,CAAA;AAAA,UACvB,CAAA;AAAA,QACF,CAAA;AAAA,QACA,QAAA,EAAU;AAAA;AACZ,KACF;AAAA,EACF,CAAA;AACF;AAWO,SAAS,WAAA,GAA8C;AAC5D,EAAA,OAAO,CAAC,IAAA,KAAS;AACf,IAAA,MAAM,SAAA,uBAAgB,GAAA,EAAgB;AACtC,IAAA,MAAM,SAAA,uBAAgB,GAAA,EAAgC;AACtD,IAAA,MAAM,YAAA,uBAAmB,GAAA,EAAwB;AACjD,IAAA,IAAI,MAAA,GAAS,KAAA;AACb,IAAA,OAAO;AAAA,MACL,SAAA,GAAY;AACV,QAAA,IAAA,CAAK,OAAO,SAAA,EAAW,UAAA,EAAY,CAAC,QAAA,KAAa,UAAU,CAAA;AAAA,MAC7D,CAAA;AAAA,MACA,MAAA,CAAO,KAAK,KAAA,EAAO;AACjB,QAAA,IAAI,QAAQ,MAAA,EAAW;AACrB,UAAA,MAAA,GAAS,IAAA;AACT,UAAA,IAAA,CAAK,OAAO,YAAA,EAAc,MAAA,EAAQ,CAAC,OAAA,KAAY,OAAA,CAAQ,KAAK,CAAC,CAAA;AAC7D,UAAA;AAAA,QACF;AACA,QAAA,IAAA,CAAK,MAAA,CAAO,WAAW,UAAA,EAAY,CAAC,aAAa,QAAA,CAAS,GAAA,EAAK,KAAK,CAAC,CAAA;AAAA,MACvE,CAAA;AAAA,MACA,MAAA,EAAQ;AAAA,QACN,SAAA,EAAW,YAAY,SAAS,CAAA;AAAA,QAChC,OAAA,EAAS,YAAY,SAAS,CAAA;AAAA,QAC9B,OAAO,OAAA,EAAyC;AAC9C,UAAA,MAAM,KAAA,GAAQ,KAAK,KAAA,EAAM;AACzB,UAAA,IAAI,MAAA,IAAU,UAAU,MAAA,EAAW;AACjC,YAAA,IAAA,CAAK,MAAA,CAAO,CAAC,OAAO,CAAA,EAAG,QAAQ,CAAC,CAAA,KAAM,CAAA,CAAE,KAAK,CAAC,CAAA;AAC9C,YAAA,OAAO,MAAM;AAAA,YAAC,CAAA;AAAA,UAChB;AACA,UAAA,OAAO,WAAA,CAAY,YAAY,CAAA,CAAE,OAAO,CAAA;AAAA,QAC1C;AAAA;AACF,KACF;AAAA,EACF,CAAA;AACF;AAEA,SAAS,YAAe,GAAA,EAAsC;AAC5D,EAAA,OAAO,CAAC,IAAA,KAAS;AACf,IAAA,GAAA,CAAI,IAAI,IAAI,CAAA;AACZ,IAAA,OAAO,MAAM;AACX,MAAA,GAAA,CAAI,OAAO,IAAI,CAAA;AAAA,IACjB,CAAA;AAAA,EACF,CAAA;AACF;AAQO,SAAS,eACd,OAAA,EACuB;AACvB,EAAA,OAAO,CAAC,IAAA,KAAS;AACf,IAAA,MAAM,QAAA,uBAAe,GAAA,EAAqC;AAC1D,IAAA,OAAO;AAAA,MACL,MAAA,CAAO,KAAK,KAAA,EAAO;AACjB,QAAA,IAAI,GAAA,KAAQ,MAAA,IAAa,OAAA,KAAY,MAAA,IAAa,SAAS,IAAA,KAAS,CAAA;AAClE,UAAA;AACF,QAAA,IAAI,MAAA;AACJ,QAAA,IAAI;AACF,UAAA,MAAA,GAAS,OAAA,CAAQ,KAAK,KAAK,CAAA;AAAA,QAC7B,SAAS,GAAA,EAAK;AACZ,UAAA,IAAA,CAAK,MAAA,CAAO,KAAK,OAAO,CAAA;AACxB,UAAA;AAAA,QACF;AACA,QAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,UAAA,MAAM,MAAA,GAAS,QAAA,CAAS,GAAA,CAAI,KAAA,CAAM,IAAI,CAAA;AACtC,UAAA,IAAI,WAAW,MAAA,EAAW;AAC1B,UAAA,IAAA,CAAK,OAAO,MAAA,EAAQ,OAAA,EAAS,CAAC,OAAA,KAAY,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,QAC1D;AAAA,MACF,CAAA;AAAA,MACA,MAAA,EAAQ;AAAA,QACN,EAAA,CAAG,MAAc,OAAA,EAAyC;AACxD,UAAA,MAAM,SAAS,QAAA,CAAS,GAAA,CAAI,IAAI,CAAA,wBAAS,GAAA,EAAI;AAC7C,UAAA,QAAA,CAAS,GAAA,CAAI,MAAM,MAAM,CAAA;AACzB,UAAA,MAAA,CAAO,IAAI,OAAO,CAAA;AAClB,UAAA,OAAO,MAAM;AACX,YAAA,MAAA,CAAO,OAAO,OAAO,CAAA;AACrB,YAAA,IAAI,OAAO,IAAA,KAAS,CAAA,IAAK,QAAA,CAAS,GAAA,CAAI,IAAI,CAAA,KAAM,MAAA;AAC9C,cAAA,QAAA,CAAS,OAAO,IAAI,CAAA;AAAA,UACxB,CAAA;AAAA,QACF;AAAA;AACF,KACF;AAAA,EACF,CAAA;AACF;AAiBO,SAAS,YACd,UAAA,EACuB;AACvB,EAAA,MAAM,KAAA,GAAQ,eAAe,MAAM,KAAA,CAAA;AACnC,EAAA,OAAO,CAAC,IAAA,KAAS;AACf,IAAA,MAAM,OAAA,uBAAc,GAAA,EAGjB;AAEH,IAAA,IAAI,OAAA;AAEJ,IAAA,IAAI,SAAA;AACJ,IAAA,OAAO;AAAA,MACL,UAAU,KAAA,EAAO;AACf,QAAA,OAAA,KAAY,EAAE,MAAA,EAAQ,IAAI,cAAA,CAAe,KAAK,CAAA,EAAE;AAAA,MAClD,CAAA;AAAA,MACA,OAAO,KAAA,EAAO;AACZ,QAAA,OAAA,KAAY,EAAE,QAAQ,KAAA,EAAM;AAAA,MAC9B,CAAA;AAAA,MACA,OAAA,GAAU;AACR,QAAA,IAAI,YAAY,MAAA,EAAW;AAC3B,QAAA,SAAA,GAAY,OAAA;AACZ,QAAA,MAAM,KAAA,GAAQ,KAAK,KAAA,EAAM;AACzB,QAAA,MAAM,QAAA,GAAW,MAAM,KAAK,CAAA;AAG5B,QAAA,IAAI,QAAA,IAAY,IAAA,CAAK,UAAA,EAAW,CAAE,SAAS,OAAA,EAAS;AACpD,QAAA,MAAM,MAAA,GAAS,CAAC,GAAG,OAAO,CAAA;AAC1B,QAAA,OAAA,CAAQ,KAAA,EAAM;AACd,QAAA,KAAA,MAAW,UAAU,MAAA,EAAQ;AAC3B,UAAA,IAAI,QAAA,EAAU,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA;AAAA,eAC7B,MAAA,CAAO,MAAA,CAAO,SAAA,CAAU,MAAM,CAAA;AAAA,QACrC;AAAA,MACF,CAAA;AAAA,MACA,MAAA,CAAO,MAAM,KAAA,EAAO;AAClB,QAAA,IACE,IAAA,CAAK,UAAA,EAAW,CAAE,IAAA,KAAS,OAAA,IAC3B,QAAQ,IAAA,KAAS,CAAA,IACjB,CAAC,KAAA,CAAM,KAAK,CAAA;AAEZ,UAAA;AACF,QAAA,MAAM,MAAA,GAAS,CAAC,GAAG,OAAO,CAAA;AAC1B,QAAA,OAAA,CAAQ,KAAA,EAAM;AACd,QAAA,KAAA,MAAW,MAAA,IAAU,MAAA,EAAQ,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA;AAAA,MACnD,CAAA;AAAA,MACA,UAAA,CAAW,OAAO,KAAA,EAAO;AACvB,QAAA,IAAI,QAAQ,IAAA,KAAS,CAAA,IAAK,CAAC,KAAA,CAAM,KAAK,CAAA,EAAG;AACzC,QAAA,MAAM,MAAA,GAAS,CAAC,GAAG,OAAO,CAAA;AAC1B,QAAA,OAAA,CAAQ,KAAA,EAAM;AACd,QAAA,KAAA,MAAW,MAAA,IAAU,MAAA,EAAQ,MAAA,CAAO,MAAA,CAAO,KAAK,CAAA;AAAA,MAClD,CAAA;AAAA,MACA,MAAA,EAAQ;AAAA,QACN,MAAA,GAAwB;AACtB,UAAA,MAAM,OAAA,GAAU,KAAK,KAAA,EAAM;AAC3B,UAAA,OAAO,IAAA,CAAK,YAAW,CAAE,IAAA,KAAS,WAAW,KAAA,CAAM,OAAO,IACtD,OAAA,GACA,MAAA;AAAA,QACN,CAAA;AAAA,QACA,IAAA,GAAmB;AACjB,UAAA,MAAM,OAAA,GAAU,KAAK,KAAA,EAAM;AAC3B,UAAA,IAAI,KAAA,CAAM,OAAO,CAAA,EAAG;AAClB,YAAA,MAAM,KAAA,GAAQ,KAAK,UAAA,EAAW;AAC9B,YAAA,IAAI,MAAM,IAAA,KAAS,OAAA,EAAS,OAAO,OAAA,CAAQ,QAAQ,OAAO,CAAA;AAC1D,YAAA,IAAI,MAAM,IAAA,KAAS,QAAA,SAAiB,OAAA,CAAQ,MAAA,CAAO,MAAM,KAAK,CAAA;AAAA,UAChE,CAAA,MAAA,IAAW,cAAc,MAAA,EAAW;AAClC,YAAA,OAAO,OAAA,CAAQ,MAAA,CAAO,SAAA,CAAU,MAAM,CAAA;AAAA,UACxC;AACA,UAAA,OAAO,IAAI,OAAA,CAAW,CAAC,OAAA,EAAS,MAAA,KAAW;AACzC,YAAA,OAAA,CAAQ,GAAA,CAAI,EAAE,OAAA,EAAS,MAAA,EAAQ,CAAA;AAAA,UACjC,CAAC,CAAA;AAAA,QACH;AAAA;AACF,KACF;AAAA,EACF,CAAA;AACF;AASO,SAAS,SAAA,CACd,MACA,KAAA,EACuB;AACvB,EAAA,OAAO,CAAC,IAAA,KAA8C;AACpD,IAAA,IAAI,IAAA,KAAS,MAAA,EAAW,OAAO,EAAC;AAChC,IAAA,IAAI,GAAA,GAAM,CAAA;AACV,IAAA,OAAO;AAAA,MACL,OAAO,GAAA,EAAK;AACV,QAAA,IAAI,QAAQ,MAAA,EAAW;AACvB,QAAA,GAAA,IAAO,CAAA;AACP,QAAA,MAAM,KAAA,GAAwB,EAAE,GAAA,EAAK,OAAA,EAAS,IAAI,IAAA,EAAM,EAAA,EAAI,OAAM,EAAE;AACpE,QAAA,IAAI;AACF,UAAA,OAAA,CAAQ,OAAA,CAAQ,IAAA,CAAK,KAAK,CAAC,CAAA,CAAE,KAAA;AAAA,YAAM,CAAC,GAAA,KAClC,IAAA,CAAK,MAAA,CAAO,KAAK,UAAU;AAAA,WAC7B;AAAA,QACF,SAAS,GAAA,EAAK;AACZ,UAAA,IAAA,CAAK,MAAA,CAAO,KAAK,UAAU,CAAA;AAAA,QAC7B;AAAA,MACF;AAAA,KACF;AAAA,EACF,CAAA;AACF;AA4BO,SAAS,iBAAA,CAMd,SAIA,IAAA,EACsC;AACtC,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,IAAS,IAAA,CAAK,GAAA;AACjC,EAAA,OAAO;AAAA,IACL,WAAA,CAAY,IAAA,CAAK,WAAA,EAAa,OAAO,CAAA;AAAA,IACrC,cAAA,CAAe,QAAQ,QAAQ,CAAA;AAAA,IAC/B,SAAA,EAAU;AAAA,IACV,cAAA,CAAe,OAAA,CAAQ,IAAA,IAAQ,IAAI,KAAK,CAAA;AAAA,IACxC,OAAA,EAAQ;AAAA,IACR,KAAA,EAAM;AAAA,IACN,WAAA,EAAY;AAAA,IACZ,cAAA,CAAe,KAAK,MAAM,CAAA;AAAA,IAC1B,WAAA,CAAY,KAAK,QAAQ,CAAA;AAAA,IACzB,SAAA,CAAU,IAAA,CAAK,SAAA,EAAW,KAAK;AAAA,GACjC;AACF;;;AChXO,IAAM,QAAA,0BAAiC,qBAAqB;AAmB5D,IAAM,IAAA,0BAA6B,iBAAiB;AA0DpD,SAAS,cAAA,CACd,OACA,QAAA,EACM;AACN,EAAA,IAAI,iBAAiB,oBAAA,EAAsB;AACzC,IAAA,OAAA,CAAQ,KAAK,KAAK,CAAA;AAClB,IAAA;AAAA,EACF;AACA,EAAA,UAAA,CAAW,MAAM;AACf,IAAA,MAAM,KAAA;AAAA,EACR,GAAG,CAAC,CAAA;AACN;AAQO,IAAM,mBAAA,GAAN,cAAkC,KAAA,CAAM;AAAA,EAC7C,YAAqB,OAAA,EAAiB;AACpC,IAAA,KAAA,CAAM,8BAA8B,CAAA;AADjB,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAAA,EAErB;AAAA,EAFqB,OAAA;AAGvB;AAGA,IAAM,aAAN,MAAiB;AAAA,EAGf,YAAqB,IAAA,EAAc;AAAd,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA,EAAe;AAAA,EAAf,IAAA;AAAA,EAFrB,UAAmB,MAAM;AAAA,EAAC,CAAA;AAAA,EAC1B,MAAA,GAAS,KAAA;AAEX,CAAA;AAOA,IAAM,UAAN,MAAc;AAAA,EACK,MAAA,uBAAa,GAAA,EAAoB;AAAA,EAElD,OAAO,IAAA,EAAoB;AACzB,IAAA,IAAA,CAAK,MAAA,CAAO,IAAI,IAAA,EAAA,CAAO,IAAA,CAAK,OAAO,GAAA,CAAI,IAAI,CAAA,IAAK,CAAA,IAAK,CAAC,CAAA;AAAA,EACxD;AAAA,EAEA,YAAY,IAAA,EAAoB;AAC9B,IAAA,MAAM,QAAQ,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,IAAI,KAAK,CAAA,IAAK,CAAA;AAC5C,IAAA,IAAI,OAAO,CAAA,EAAG,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,MAAM,IAAI,CAAA;AAAA,SACnC,IAAA,CAAK,MAAA,CAAO,MAAA,CAAO,IAAI,CAAA;AAAA,EAC9B;AAAA;AAAA,EAGA,QAAA,GAA6C;AAC3C,IAAA,OAAO,MAAA,CAAO,WAAA,CAAY,IAAA,CAAK,MAAM,CAAA;AAAA,EACvC;AACF,CAAA;AASO,SAAS,UACd,MAAA,EACkB;AAClB,EAAA,MAAM,EAAE,OAAA,EAAS,gBAAA,EAAiB,GAAI,MAAA;AACtC,EAAA,MAAM,OAAA,GAAmB,OAAO,OAAA,IAAW,cAAA;AAI3C,EAAA,SAAS,MAAA,CAAO,OAAgB,KAAA,EAAgC;AAC9D,IAAA,MAAM,OAAA,GAA+B,EAAE,KAAA,EAAM;AAC7C,IAAA,IAAI;AACF,MAAA,OAAA,CAAQ,OAAO,OAAO,CAAA;AAAA,IACxB,SAAS,SAAA,EAAW;AAClB,MAAA,cAAA,CAAe,SAAkB,CAAA;AAAA,IACnC;AAAA,EACF;AAIA,EAAA,SAAS,MAAA,CACP,KAAA,EACA,KAAA,EACA,EAAA,EACM;AACN,IAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,MAAA,IAAI;AACF,QAAA,EAAA,CAAG,IAAI,CAAA;AAAA,MACT,SAAS,GAAA,EAAK;AACZ,QAAA,MAAA,CAAO,KAAK,KAAK,CAAA;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AAIA,EAAA,IAAI,KAAA;AAGJ,EAAA,IAAI,SAAA;AACJ,EAAA,IAAI,UAAA,GAAyB,EAAE,IAAA,EAAM,SAAA,EAAU;AAC/C,EAAA,IAAI,SAAA,GAAqB,IAAA;AAuBzB,EAAA,IAAI,IAAA,GAAqD,MAAA;AAKzD,EAAA,MAAM,QAAA,uBAAe,GAAA,EAA8C;AAEnE,EAAA,MAAM,QAAA,GAA4B;AAAA,IAChC,MAAA;AAAA,IACA,MAAA;AAAA,IACA,KAAK,KAAA,EAAO;AAGV,MAAA,MAAM,WAAW,IAAA,KAAS,MAAA;AAC1B,MAAA,IAAI,IAAA,KAAS,aAAa,IAAA,GAAO,QAAA;AACjC,MAAA,KAAA,MAAW,GAAA,IAAO,UAAA,EAAY,GAAA,CAAI,MAAA,GAAS,KAAK,CAAA;AAIhD,MAAA,IAAI,CAAC,UAAU,MAAA,CAAO,aAAA,CAAc,EAAE,IAAA,EAAM,MAAA,EAAQ,OAAO,CAAA;AAAA,IAC7D,CAAA;AAAA,IACA,OAAO,MAAM,KAAA;AAAA,IACb,UAAA,EAAY,MACV,MAAA,CAAO,KAAA,KAAU,SAAY,EAAE,IAAA,EAAM,SAAQ,GAAI;AAAA,GACrD;AACA,EAAA,MAAM,UAAA,GAAa,OAAO,UAAA,CAAW,GAAA,CAAI,CAAC,IAAA,KAAS,IAAA,CAAK,QAAQ,CAAC,CAAA;AAEjE,EAAA,SAAS,gBAAA,GAAyB;AAChC,IAAA,MAAA,CAAO,YAAY,KAAA,EAAO,CAAC,GAAA,KAAQ,GAAA,CAAI,WAAW,CAAA;AAAA,EACpD;AAIA,EAAA,MAAM,SAAwB,UAAA,CAAW,WAAA;AAAA,IACvC,CAAC,MAAM,GAAA,KAAS,GAAA,CAAI,SAAS,GAAA,CAAI,MAAA,CAAO,IAAI,CAAA,GAAI,IAAA;AAAA,IAChD,MAAA,CAAO;AAAA,GACT;AACA,EAAA,MAAM,SAAA,GAAiC,UAAA,CAAW,WAAA,CAEhD,CAAC,IAAA,EAAM,GAAA,KAAS,GAAA,CAAI,SAAA,GAAY,GAAA,CAAI,SAAA,CAAU,IAAI,CAAA,GAAI,MAAO,WAAW,CAAA;AAC1E,EAAA,MAAM,KAAA,GACJ,MAAA,CAAO,KAAA,KAAU,MAAA,GACb,SACA,UAAA,CAAW,WAAA;AAAA,IACT,CAAC,OAAO,GAAA,KAAS,GAAA,CAAI,QAAQ,GAAA,CAAI,KAAA,CAAM,KAAK,CAAA,GAAI,KAAA;AAAA,IAChD,MAAA,CAAO;AAAA,GACT;AACN,EAAA,MAAM,qBAAqB,UAAA,CAAW,OAAA;AAAA,IAAQ,CAAC,QAC7C,GAAA,CAAI,SAAA,GAAY,CAAC,GAAA,CAAI,SAAS,IAAI;AAAC,GACrC;AACA,EAAA,MAAM,kBAAkB,UAAA,CAAW,OAAA;AAAA,IAAQ,CAAC,QAC1C,GAAA,CAAI,MAAA,GAAS,CAAC,GAAA,CAAI,MAAM,IAAI;AAAC,GAC/B;AAMA,EAAA,MAAM,aAAsB,MAAA,CAAO,MAAA;AAAA,IACjC,EAAC;AAAA,IACD,MAAA,CAAO,GAAA;AAAA,IACP,GAAG,WAAW,GAAA,CAAI,CAAC,QAAQ,GAAA,CAAI,GAAA,IAAO,EAAE,CAAA;AAAA,IACxC,EAAE,CAAC,UAAU,GAAG,cAAA;AAAe,GACjC;AAGA,EAAA,IAAI,IAAA,GAAsB,QAAQ,OAAA,EAAQ;AAG1C,EAAA,MAAM,IAAA,GAAO,IAAI,OAAA,EAAQ;AAIzB,EAAA,MAAM,WAAA,uBAAkB,GAAA,EAAwB;AAIhD,EAAA,SAAS,SAAS,GAAA,EAA6B;AAC7C,IAAA,KAAA,MAAW,MAAM,GAAA,EAAK;AACpB,MAAA,MAAM,OAAA,GAAU,WAAA,CAAY,GAAA,CAAI,EAAE,CAAA;AAClC,MAAA,IAAI,YAAY,MAAA,EAAW;AAC3B,MAAA,IAAI;AACF,QAAA,aAAA,CAAc,OAAA,CAAQ,SAAS,CAAA;AAAA,MACjC,SAAS,GAAA,EAAK;AACZ,QAAA,MAAA,CAAO,KAAK,aAAa,CAAA;AAAA,MAC3B;AACA,MAAA,WAAA,CAAY,OAAO,EAAE,CAAA;AAAA,IACvB;AAAA,EACF;AASA,EAAA,MAAM,gBAAA,uBAAuB,GAAA,EAAmB;AAEhD,EAAA,SAAS,cAAc,MAAA,EAAoC;AACzD,IAAA,IAAI,EAAE,kBAAkB,OAAA,CAAA,EAAU;AAClC,IAAA,MAAM,QAAA,GAA0B,MAAA,CAC7B,KAAA,CAAM,CAAC,GAAA,KAAQ;AACd,MAAA,MAAA,CAAO,KAAK,aAAa,CAAA;AAAA,IAC3B,CAAC,CAAA,CACA,OAAA,CAAQ,MAAM;AACb,MAAA,gBAAA,CAAiB,OAAO,QAAQ,CAAA;AAAA,IAClC,CAAC,CAAA;AACH,IAAA,gBAAA,CAAiB,IAAI,QAAQ,CAAA;AAAA,EAC/B;AAQA,EAAA,eAAe,YACb,IAAA,EACgC;AAChC,IAAA,IAAI,KAAA;AACJ,IAAA,IAAI;AACF,MAAA,OAAO,MAAM,QAAQ,IAAA,CAAK;AAAA,QACxB,IAAA,CAAK,IAAA,CAAK,MAAM,SAAkB,CAAA;AAAA,QAClC,IAAI,OAAA,CAAmB,CAAC,OAAA,KAAY;AAClC,UAAA,KAAA,GAAQ,UAAA,CAAW,MAAM,OAAA,CAAQ,SAAS,GAAG,gBAAgB,CAAA;AAAA,QAC/D,CAAC;AAAA,OACF,CAAA;AAAA,IACH,CAAA,SAAE;AACA,MAAA,IAAI,KAAA,KAAU,MAAA,EAAW,YAAA,CAAa,KAAK,CAAA;AAAA,IAC7C;AAAA,EACF;AASA,EAAA,eAAe,SAAA,GAA2B;AACxC,IAAA,IAAK,MAAM,WAAA,CAAY,IAAI,CAAA,KAAO,SAAA,EAAW;AAC3C,MAAA,IAAA,GAAO,QAAA;AACP,MAAA;AAAA,IACF;AACA,IAAA,MAAA;AAAA,MACE,IAAI,kBAAA;AAAA,QACF,CAAC,GAAG,QAAQ,CAAA,CAAE,OAAA,CAAQ,CAAC,EAAE,OAAA,EAAQ,KAAM,OAAA,IAAW,EAAE,CAAA;AAAA,QACpD,QAAA,CAAS,IAAA;AAAA,QACT,gBAAA;AAAA,QACA,KAAK,QAAA;AAAS,OAChB;AAAA,MACA;AAAA,KACF;AACA,IAAA,IAAA,GAAO,WAAA;AAAA,EACT;AAMA,EAAA,eAAe,cAAA,GAAgC;AAC7C,IAAA,IAAI,gBAAA,CAAiB,SAAS,CAAA,EAAG;AACjC,IAAA,MAAM,WAAA,GAAc,CAAC,GAAG,gBAAgB,CAAA;AACxC,IAAA,IAAK,MAAM,WAAA,CAAY,OAAA,CAAQ,IAAI,WAAW,CAAC,MAAO,SAAA,EAAW;AAC/D,MAAA,MAAA;AAAA,QACE,IAAI,oBAAA,CAAqB,WAAA,CAAY,MAAA,EAAQ,gBAAgB,CAAA;AAAA,QAC7D;AAAA,OACF;AAAA,IACF;AAAA,EACF;AAaA,EAAA,SAAS,aAAA,GAAsB;AAC7B,IAAA,IAAI,UAAA,GAAsB,IAAA;AAC1B,IAAA,MAAM,QAAA,GAAW,CAAC,GAAA,KAAuB;AACvC,MAAA,IAAI,UAAA,KAAe,MAAM,UAAA,GAAa,GAAA;AAAA,IACxC,CAAA;AACA,IAAA,MAAM,OAAA,uBAAc,GAAA,EAAiB;AAErC,IAAA,MAAM,WAAA,uBAAkB,GAAA,EAAY;AACpC,IAAA,KAAA,MAAW,KAAA,IAAS,OAAO,IAAA,EAAM;AAC/B,MAAA,IAAI;AACF,QAAA,MAAM,GAAA,GAAM,UAAA,CAAW,KAAA,EAAO,KAAU,CAAA;AACxC,QAAA,IAAI,QAAQ,IAAA,EAAM,OAAA,CAAQ,GAAA,CAAI,GAAA,CAAI,IAAI,GAAG,CAAA;AAAA,MAC3C,SAAS,GAAA,EAAK;AACZ,QAAA,QAAA,CAAS,GAAG,CAAA;AACZ,QAAA,WAAA,CAAY,GAAA,CAAI,MAAM,IAAI,CAAA;AAAA,MAC5B;AAAA,IACF;AAEA,IAAA,QAAA;AAAA,MACE,CAAC,GAAG,WAAW,CAAA,CAAE,OAAA;AAAA,QAAQ,CAAC,CAAC,EAAA,EAAI,OAAO,CAAA,KACpC,QAAQ,GAAA,CAAI,EAAE,CAAA,IAAK,WAAA,CAAY,IAAI,OAAA,CAAQ,IAAI,IAAI,EAAC,GAAI,CAAC,EAAE;AAAA;AAC7D,KACF;AAIA,IAAA,MAAM,QAAA,GAAW,IAAA,KAAS,MAAA,GAAS,OAAA,uBAAc,GAAA,EAAiB;AAClE,IAAA,KAAA,MAAW,CAAC,EAAA,EAAI,GAAG,CAAA,IAAK,QAAA,EAAU;AAChC,MAAA,IAAI,WAAA,CAAY,GAAA,CAAI,EAAE,CAAA,EAAG;AACzB,MAAA,MAAM,MAAA,GAAS,MAAA,CAAO,SAAA,CAAU,GAAA,CAAI,IAAI,CAAA;AACxC,MAAA,IAAI,WAAW,MAAA,EAAW;AACxB,QAAA,QAAA;AAAA,UACE,IAAI,KAAA;AAAA,YACF,CAAA,+CAAA,EAAkD,IAAI,IAAI,CAAA,oCAAA;AAAA;AAE5D,SACF;AACA,QAAA;AAAA,MACF;AACA,MAAA,MAAM,OAAA,GAAU,IAAI,UAAA,CAAW,GAAA,CAAI,IAAI,CAAA;AACvC,MAAA,IAAI;AAIF,QAAA,OAAA,CAAQ,OAAA,GAAU,MAAA;AAAA,UAAO,GAAA;AAAA,UAAK,MAAA,CAAO,GAAA;AAAA,UAAK,iBAAA;AAAA,UAAmB,CAAC,KAAA,KAC5D,SAAA,CAAU,EAAA,EAAI,SAAS,KAAK;AAAA,SAC9B;AAAA,MACF,SAAS,GAAA,EAAK;AAGZ,QAAA,QAAA,CAAS,GAAG,CAAA;AACZ,QAAA,SAAA,CAAU,EAAA,EAAI,SAAS,GAAG,CAAA;AAC1B,QAAA;AAAA,MACF;AACA,MAAA,IAAI,CAAC,QAAQ,MAAA,EAAQ;AACnB,QAAA,WAAA,CAAY,GAAA,CAAI,IAAI,OAAO,CAAA;AAC3B,QAAA;AAAA,MACF;AAGA,MAAA,IAAI;AACF,QAAA,aAAA,CAAc,OAAA,CAAQ,SAAS,CAAA;AAAA,MACjC,SAAS,GAAA,EAAK;AACZ,QAAA,MAAA,CAAO,KAAK,aAAa,CAAA;AAAA,MAC3B;AAAA,IACF;AACA,IAAA,IAAI,UAAA,KAAe,MAAM,MAAM,UAAA;AAAA,EACjC;AAWA,EAAA,SAAS,SAAA,CAAU,EAAA,EAAY,OAAA,EAAqB,KAAA,EAAsB;AACxE,IAAA,IAAI,QAAQ,MAAA,EAAQ;AACpB,IAAA,OAAA,CAAQ,MAAA,GAAS,IAAA;AACjB,IAAA,IAAI,YAAY,GAAA,CAAI,EAAE,MAAM,OAAA,EAAS,WAAA,CAAY,OAAO,EAAE,CAAA;AAC1D,IAAA,MAAA,CAAO,OAAO,KAAK,CAAA;AACnB,IAAA,IAAI,SAAS,MAAA,EAAQ;AACrB,IAAA,IAAA,GAAO,UAAA;AACP,IAAA,KAAA,MAAW,GAAA,IAAO,UAAA,EAAY,GAAA,CAAI,SAAA,GAAY,KAAK,CAAA;AACnD,IAAA,MAAA,CAAO,aAAA,CAAc,EAAE,IAAA,EAAM,KAAA,EAAO,OAAO,CAAA;AAAA,EAC7C;AAQA,EAAA,SAAS,kBAAkB,GAAA,EAAc;AACvC,IAAA,eAAA,CAAgB,GAAA,EAAK,MAAM,CAAA,CAAE,KAAA,CAAM,iBAAiB,CAAA;AAAA,EACtD;AAIA,EAAA,SAAS,kBAAkB,KAAA,EAAsB;AAC/C,IAAA,MAAA;AAAA,MACE,KAAA;AAAA,MACA,KAAA,YAAiB,yBAAyB,SAAA,GAAY;AAAA,KACxD;AAAA,EACF;AASA,EAAA,SAAS,aAAA,CACP,MACA,OAAA,EACY;AACZ,IAAA,MAAM,KAAA,GAAQ,EAAE,OAAA,EAAQ;AACxB,IAAA,QAAA,CAAS,IAAI,KAAK,CAAA;AAClB,IAAA,OAAO,OAAA,CAAQ,OAAA,CAAQ,IAAI,CAAA,CAAE,QAAQ,MAAM;AACzC,MAAA,QAAA,CAAS,OAAO,KAAK,CAAA;AAAA,IACvB,CAAC,CAAA;AAAA,EACH;AAWA,EAAA,SAAS,eAAe,IAAA,EAA8B;AACpD,IAAA,MAAM,OAAA,GAAU,cAAc,IAAI,CAAA;AAClC,IAAA,OAAA,CAAQ,MAAM,iBAAiB,CAAA;AAC/B,IAAA,IAAA,GAAO,IAAA,CACJ,KAAK,YAAY;AAChB,MAAA,MAAM,OAAA;AAAA,IACR,CAAC,CAAA,CACA,KAAA,CAAM,MAAM;AAAA,IAAC,CAAC,CAAA;AAAA,EACnB;AAKA,EAAA,SAAS,WAAA,CAAY,KAAQ,QAAA,EAA8C;AACzE,IAAA,MAAM,EAAE,MAAK,GAAI,GAAA;AACjB,IAAA,MAAM,OAAA,GAAU,MAAA,CAAO,UAAA,CAAW,IAAI,CAAA;AACtC,IAAA,IAAI,OAAA,KAAY,MAAA,EAAW,OAAO,OAAA,CAAQ,QAAQ,MAAS,CAAA;AAC3D,IAAA,OAAO,cAAc,OAAA,CAAQ,GAAA,EAAc,UAAA,EAAY,QAAQ,GAAG,IAAI,CAAA;AAAA,EACxE;AAiBA,EAAA,eAAe,QAAQ,IAAA,EAAmC;AACxD,IAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,MAAA,IAAI,SAAS,WAAA,EAAa;AAC1B,MAAA,MAAM,QAAA,GAAW,MAAM,SAAA,CAAU,GAAA,EAAK,iBAAiB,CAAA;AACvD,MAAA,KAAA,MAAW,MAAA,IAAU,SAAA,CAAa,QAAQ,CAAA,oBAAqB,MAAM,CAAA;AAAA,IACvE;AAAA,EACF;AAuBA,EAAA,eAAe,MAAA,CACb,IAAA,EACA,GAAA,EACA,IAAA,EACe;AACf,IAAA,KAAA,GAAQ,IAAA;AACR,IAAA,UAAA,GAAa,EAAE,MAAM,SAAA,EAAU;AAC/B,IAAA,IAAI,KAAA,IAAS,SAAS,WAAA,EAAa;AACjC,MAAA,IAAI;AACF,QAAA,MAAM,KAAA,CAAM,KAAK,IAAI,CAAA;AACrB,QAAA,UAAA,GAAa,EAAE,MAAM,OAAA,EAAQ;AAAA,MAC/B,SAAS,KAAA,EAAO;AACd,QAAA,UAAA,GAAa,EAAE,IAAA,EAAM,QAAA,EAAU,KAAA,EAAM;AACrC,QAAA,KAAA,MAAW,GAAA,IAAO,UAAA,EAAY,GAAA,CAAI,UAAA,GAAa,MAAM,KAAK,CAAA;AAC1D,QAAA,MAAM,KAAA;AAAA,MACR;AAAA,IACF;AACA,IAAA,aAAA,EAAc;AACd,IAAA,SAAA,GAAY,IAAA;AACZ,IAAA,KAAA,MAAW,QAAA,IAAY,oBAAoB,QAAA,EAAS;AACpD,IAAA,IAAI;AACF,MAAA,MAAM,QAAQ,IAAI,CAAA;AAAA,IACpB,CAAA,SAAE;AACA,MAAA,KAAA,MAAW,QAAA,IAAY,eAAA,EAAiB,QAAA,CAAS,GAAA,EAAK,IAAI,CAAA;AAAA,IAC5D;AAAA,EACF;AAIA,EAAA,eAAe,aAAa,GAAA,EAAuB;AACjD,IAAA,IAAI,UAAU,MAAA,EAAW;AACvB,MAAA,MAAM,IAAI,MAAM,iCAAiC,CAAA;AAAA,IACnD;AACA,IAAA,MAAM,UAAA,GAAa,MAAA,CAAO,KAAA,EAAO,GAAG,CAAA;AACpC,IAAA,IAAI,eAAe,IAAA,EAAM;AACzB,IAAA,MAAM,OAAO,UAAA,CAAW,CAAC,GAAG,GAAA,EAAK,UAAA,CAAW,CAAC,CAAC,CAAA;AAAA,EAChD;AAQA,EAAA,MAAM,SAAA,GACJ,UAAU,MAAA,GAAY,MAAA,CAAO,KAAK,IAAA,EAAM,MAAA,CAAO,GAAG,CAAA,GAAI,IAAA;AACxD,EAAA,IAAI,SAAA,KAAc,IAAA,EAAM,KAAA,GAAQ,SAAA,CAAU,CAAC,CAAA;AAE3C,EAAA,eAAe,IAAA,GAAsB;AAInC,IAAA,MAAM,CAAC,OAAA,EAAS,IAAI,CAAA,GAClB,SAAA,IAAa,MAAA,CAAO,IAAA,CAAK,MAAM,OAAA,CAAQ,KAAiB,CAAA,EAAG,MAAA,CAAO,GAAG,CAAA;AACvE,IAAA,MAAM,MAAA,CAAO,OAAA,EAAS,MAAA,EAAW,IAAI,CAAA;AAAA,EACvC;AAeA,EAAA,SAAS,eAAA,CACP,GAAA,EACA,MAAA,GAA4B,QAAA,EACb;AACf,IAAA,IAAI,SAAS,MAAA,EAAQ;AACnB,MAAA,OAAO,OAAA,CAAQ,MAAA;AAAA,QACb,IAAA,KAAS,UAAA,IAAe,IAAA,KAAS,WAAA,IAAe,WAAW,MAAA,GACvD,IAAI,sBAAA,CAAuB,GAAA,CAAI,IAAI,CAAA,GACnC,IAAI,mBAAA,CAAoB,IAAI,IAAI;AAAA,OACtC;AAAA,IACF;AACA,IAAA,OAAO,WAAA,CAAY,GAAA,EAAK,MAAA,EAAQ,YAAY,CAAA;AAAA,EAC9C;AAUA,EAAA,SAAS,WAAA,CACP,GAAA,EACA,MAAA,EACA,IAAA,EACe;AACf,IAAA,IAAA,CAAK,MAAA,CAAO,IAAI,IAAI,CAAA;AACpB,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,IAAA,CAAK,MAAM;AAC3B,MAAA,IAAA,CAAK,WAAA,CAAY,IAAI,IAAI,CAAA;AACzB,MAAA,IAAI,SAAS,WAAA,EAAa;AACxB,QAAA,IAAI,WAAW,QAAA,EAAU,MAAM,IAAI,sBAAA,CAAuB,IAAI,IAAI,CAAA;AAClE,QAAA;AAAA,MACF;AACA,MAAA,IAAI,SAAA,KAAc,MAAM,MAAM,SAAA;AAC9B,MAAA,OAAO,KAAK,GAAG,CAAA;AAAA,IACjB,CAAC,CAAA;AAGD,IAAA,IAAA,GAAO,IAAA,CAAK,MAAM,MAAM;AAAA,IAAC,CAAC,CAAA;AAC1B,IAAA,OAAO,IAAA;AAAA,EACT;AAWA,EAAA,SAAS,OAAA,CAAQ,KAAQ,KAAA,EAA0C;AACjE,IAAA,IAAI,IAAA,KAAS,QAAQ,OAAO,SAAA;AAC5B,IAAA,IAAI,CAAC,KAAA,CAAM,KAAU,CAAA,EAAG,OAAO,SAAA;AAC/B,IAAA,WAAA;AAAA,MAAY,GAAA;AAAA,MAAK,MAAA;AAAA,MAAQ,CAAC,MAAA,KACxB,KAAA,CAAM,KAAU,CAAA,GAAI,YAAA,CAAa,MAAM,CAAA,GAAI;AAAA,KAC7C,CAAE,MAAM,iBAAiB,CAAA;AACzB,IAAA,OAAO,QAAA;AAAA,EACT;AASA,EAAA,eAAe,iBAAA,GAAmC;AAChD,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,OAAA,EAAS,CAAA,EAAA,EAAK;AAChC,MAAA,MAAM,QAAA,GAAW,IAAA;AACjB,MAAA,MAAM,QAAA;AACN,MAAA,IAAI,SAAS,QAAA,EAAU;AAAA,IACzB;AACA,IAAA,MAAM,IAAI,uBAAuB,OAAO,CAAA;AAAA,EAC1C;AAOA,EAAA,eAAe,oBAAA,CACb,KACA,IAAA,EACe;AACf,IAAA,MAAM,gBAAgB,GAAG,CAAA;AACzB,IAAA,IAAI,IAAA,EAAM,WAAW,MAAA,EAAQ;AAC7B,IAAA,MAAM,iBAAA,EAAkB;AAAA,EAC1B;AAMA,EAAA,MAAM,cAAc,IAAA,EAAK;AACzB,EAAA,IAAA,GAAO,WAAA,CAAY,KAAA,CAAM,CAAC,GAAA,KAAQ;AAChC,IAAA,SAAA,GAAY,GAAA;AAAA,EACd,CAAC,CAAA;AAED,EAAA,eAAe,QAAA,GAA0B;AAGvC,IAAA,IAAI,IAAA,KAAS,QAAQ,IAAA,GAAO,UAAA;AAI5B,IAAA,IAAI,QAAA,CAAS,OAAO,CAAA,EAAG;AACrB,MAAA,MAAA,CAAO,IAAI,qBAAA,CAAsB,QAAA,CAAS,IAAI,GAAG,SAAS,CAAA;AAAA,IAC5D;AAKA,IAAA,MAAM,SAAA,EAAU;AAChB,IAAA,gBAAA,EAAiB;AACjB,IAAA,QAAA,CAAS,CAAC,GAAG,WAAA,CAAY,IAAA,EAAM,CAAC,CAAA;AAGhC,IAAA,MAAM,cAAA,EAAe;AAIrB,IAAA,IAAI,KAAA,IAAS,KAAA,KAAU,MAAA,IAAa,SAAA,KAAc,IAAA,EAAM;AACtD,MAAA,IAAI;AACF,QAAA,MAAM,KAAA,CAAM,KAAK,KAAK,CAAA;AAAA,MACxB,SAAS,KAAA,EAAO;AACd,QAAA,MAAA,CAAO,OAAO,WAAW,CAAA;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AAOA,EAAA,IAAI,OAAA;AAEJ,EAAA,MAAM,IAAA,GAAyB;AAAA,IAC7B,CAAC,QAAQ,GAAG,OAAO,EAAE,MAAM,WAAA,CAAY,IAAA,EAAM,IAAA,EAAM,QAAA,CAAS,IAAA,EAAK,CAAA;AAAA,IACjE,CAAC,IAAI,GAAG,OAAA;AAAA,IACR,CAAC,cAAc,GAAG,MAAM,SAAA;AAAA,IACxB,QAAA,EAAU,oBAAA;AAAA,IACV,YAAA,EAAc,CAAC,GAAA,KAAQ,eAAA,CAAgB,GAAG,CAAA;AAAA,IAC1C,QAAA,GAAc;AAIZ,MAAA,OAAO,KAAA;AAAA,IACT,CAAA;AAAA;AAAA;AAAA,IAGA,KAAA,EAAO,WAAA,CAAY,IAAA,CAAK,MAAM,MAAM,CAAA;AAAA,IACpC,IAAA,EAAM,iBAAA;AAAA,IACN,IAAA,GAAsB;AACpB,MAAA,OAAA,KAAY,QAAA,EAAS;AACrB,MAAA,OAAO,OAAA;AAAA,IACT;AAAA,GACF;AACA,EAAA,MAAM,SAA2B,MAAA,CAAO,MAAA;AAAA,IACtC,IAAA;AAAA,IACA,GAAG,WAAW,GAAA,CAAI,CAAC,QAAQ,GAAA,CAAI,MAAA,IAAU,EAAE;AAAA,GAC7C;AACA,EAAA,OAAO,MAAA;AACT;;;AC3+BO,SAAS,OAAA,CACd,OAAA,EACA,OAAA,EACA,GAAA,EACY;AACZ,EAAA,MAAM,QAAA,GAAY,QAAoC,IAAI,CAAA;AAC1D,EAAA,IAAI,aAAa,MAAA,EAAW;AAC1B,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AACA,EAAA,OAAO,QAAA;AAAA,IAAS,GAAA;AAAA,IAAK,CAAC,UACpB,aAAA,CAAc,OAAA,EAAS,KAAK,CAAA,CAAE,QAAA,CAAS,IAAI,IAAI;AAAA,GACjD;AACF","file":"chunk-2A5XTKQQ.js","sourcesContent":["/**\n * tea's built-ins, each one an {@link Extension} of the core loop (`./loop`,\n * #280). None is a public plugin API (#268): each engine's `run` builds them\n * from its options through {@link builtinExtensions}, which fixes their order.\n */\n\nimport type { AnyCmdDef, Identity, Port } from \"../../pure/core\";\nimport {\n checkedStep,\n cmdContractOver,\n cmdEdge,\n lookupCell,\n NoCellError,\n Outcome,\n structuralHash,\n} from \"../../pure/core\";\nimport type {\n OnError,\n Store,\n Supervision,\n TelemetryEvent,\n TelemetrySink,\n} from \"../../runtime-types\";\nimport {\n IdentityDropNotice,\n isFencedStore,\n SubFailedError,\n} from \"../../runtime-types\";\nimport type { Extension, ExtensionFactory, LoopServices } from \"./loop\";\n\ntype AnyExtension<S, M, C> = ExtensionFactory<S, M, C>;\n\n// === update middleware ===\n\n/**\n * Supervision: a throw from the fold (the reducer, or any update middleware\n * inside this one) is reported under `\"reduce\"` for every strategy, then\n * `restart` commits the host's rehydrated State with no Cmds, `escalate`\n * rethrows with the runtime still live, and `stop` (the default) halts the run\n * and rethrows, so THIS dispatch rejects too and `done()` answers with the throw\n * (see {@link terminality}). The halt starts the same stop a Sub failure starts:\n * the Msgs already in line fold, then the Subs close, the final save runs and,\n * on the Effect engine, the run's Scope closes (ADR 0033).\n *\n * A Msg the current State has no cell for is not a reducer throw — no machine\n * code ran, the caller dispatched something this State does not accept (#310).\n * Its `NoCellError` rejects only that dispatch and keeps the run alive under\n * every strategy: it is not reported under `\"reduce\"`, `restart` does not\n * rehydrate, and `stop` does not halt. The refusal stays loud; it just is not\n * the program's failure.\n */\nexport function supervision<S, M extends { type: string }, C>(\n declared: Supervision<S, M> | undefined,\n machine: CellTable,\n): AnyExtension<S, M, C> {\n const policy =\n declared === undefined\n ? ({ strategy: \"stop\" } as const)\n : typeof declared === \"string\"\n ? ({ strategy: declared } as const)\n : declared;\n return (loop) => ({\n update: (next) => (state, msg) => {\n try {\n return next(state, msg);\n } catch (reduceError) {\n if (isRefusal(machine, reduceError, state, msg)) throw reduceError;\n loop.report(reduceError, \"reduce\");\n switch (policy.strategy) {\n case \"restart\":\n // A throw inside `rehydrate` is NOT caught — it reaches the caller.\n return [policy.rehydrate(state, msg, reduceError), []];\n case \"escalate\":\n throw reduceError;\n default:\n loop.halt(reduceError);\n throw reduceError;\n }\n }\n },\n });\n}\n\n/** The part of a machine that says which cell a `(state, msg)` pair selects. */\ntype CellTable = Parameters<typeof lookupCell>[0];\n\n/**\n * A `NoCellError` is a refusal only when the machine really has no cell for\n * this `(state, msg)`: then nothing ran, and the error is the lookup's own. A\n * `NoCellError` raised INSIDE a cell that does exist (a reducer stepping a\n * child machine by hand) is that reducer failing, and stays supervised.\n */\nfunction isRefusal<S, M extends { type: string }>(\n machine: CellTable,\n error: unknown,\n state: S,\n msg: M,\n): boolean {\n return (\n error instanceof NoCellError &&\n lookupCell(machine, state, msg).cell === undefined\n );\n}\n\n/**\n * The instance-identity filter: drop a Msg addressed to a DIFFERENT instance\n * than the one this State owns, before the reducer sees it. A Msg with no\n * identity, or a State that has none yet, always proceeds. The drop is no\n * transition, and it RESOLVES the dispatch — a mis-addressed Msg is not the\n * caller's error — so it is reported as an `IdentityDropNotice` (warn by\n * default) or the caller could not tell \"applied\" from \"discarded\".\n *\n * Sits INSIDE {@link supervision}, so a throwing `ofMsg` / `ofState` is reported\n * and supervised exactly like a reducer throw (spike #264 run 3).\n */\nexport function identityFilter<S, M extends { type: string }, C>(\n identity: Identity<S, M> | undefined,\n): AnyExtension<S, M, C> {\n return (loop) => {\n if (identity === undefined) return {};\n const misaddressed = (msg: M, state: S): boolean => {\n const addressed = identity.ofMsg(msg);\n if (addressed === undefined) return false;\n const own = identity.ofState(state);\n if (own === undefined) return false;\n return structuralHash(addressed) !== structuralHash(own);\n };\n return {\n update: (next) => (state, msg) => {\n if (!misaddressed(msg, state)) return next(state, msg);\n loop.report(new IdentityDropNotice(msg.type), \"identity-drop\");\n return null;\n },\n };\n };\n}\n\n/**\n * The dev pre/post pair around the reducer (`checkedStep`): freeze the input\n * State so an in-place mutation trips, and check the returned `[state, cmds]`\n * shape. Both compile out of production. Innermost, so it sees exactly what the\n * reducer saw.\n */\nexport function devChecks<S, M extends { type: string }, C>(): AnyExtension<\n S,\n M,\n C\n> {\n return () => ({\n update: (next) => (state, msg) => checkedStep(state, msg, next),\n });\n}\n\n// === interpret middleware ===\n\n/**\n * The `Cmd.define` edge (ADR 0021): a defined Cmd's handler return is minted\n * into its `_ok` / `_err` Msg, its `ok` value parsed against the def's schema\n * and stamped with `at` from `clock`. A defined Cmd's handler failing outside\n * its declared channel goes to the sink under `\"interpret\"`; a hand-written\n * Cmd's handler throw keeps rejecting the dispatch.\n *\n * A defined Cmd's handler that DISPATCHES its own `_ok` / `_err`, built by hand\n * rather than minted by this edge, breaks the same contract as one that\n * returns it: the Msg is dropped and the `OutcomeContractError` goes to the\n * sink under `\"interpret\"`. Its other Msgs are delivered as before.\n *\n * The same edge rides on ctx under `cmdEdge`, beside the `ok` / `err` builders,\n * so a handler that runs a base handler inside its own settles the base's\n * outcome through this one edge (#66).\n */\nexport function cmdDefinitions<S, M extends { type: string }, C>(\n defs: readonly AnyCmdDef[],\n clock: () => number,\n): AnyExtension<S, M, C> {\n return (loop) => {\n const { settle, checkDispatch: check } = cmdContractOver(defs, clock);\n const defined = new Set(defs.map((d) => d.cmdType));\n return {\n interpret: (next) => async (cmd, dispatch) => {\n const type = (cmd as { type: string }).type;\n const checked = defined.has(type)\n ? (msg: M) => {\n try {\n check(cmd as { type: string }, msg);\n } catch (err) {\n loop.report(err, \"interpret\");\n return;\n }\n dispatch(msg);\n }\n : dispatch;\n try {\n return settle(cmd as { type: string }, await next(cmd, checked));\n } catch (err) {\n if (!defined.has(type)) throw err;\n loop.report(err, \"interpret\");\n return undefined;\n }\n },\n ctx: { ok: Outcome.ok, err: Outcome.err, [cmdEdge]: settle },\n };\n };\n}\n\n// === store wrapper ===\n\n/**\n * Fencing is a property of the store the caller handed `run`, never a flag\n * (#143). A fenced store is read with its version at boot and every save is a\n * compare-and-swap against the last version this run saw, so a second writer\n * that took over is refused with `StoreConflictError` at this run's next save —\n * before that transition's Cmds run. A plain store passes through unchanged.\n */\nexport function fencing<S, M, C>(): AnyExtension<S, M, C> {\n return () => ({\n store: (store: Store<S>): Store<S> => {\n if (!isFencedStore(store)) return store;\n let version = 0;\n return {\n async load() {\n const read = await store.loadFenced();\n version = read.version;\n return read.raw;\n },\n async save(state) {\n version = await store.saveFenced(state, version);\n },\n migrate: (raw) => store.migrate(raw),\n };\n },\n });\n}\n\n/**\n * Refuse a run on a store that is fenced at runtime when the caller passed no\n * `onError` (ADR 0031, amended by #810). The type already refuses it; this is\n * the same rule for a store typed plain `Store<S>` and for a JavaScript caller.\n *\n * Each engine's `run` calls it on the caller's own `onError`, before the loop\n * starts, so nothing is loaded or saved. It is not an extension: the Effect\n * engine always hands the loop a wrapped sink, so the loop never sees the\n * caller's option.\n */\nexport function refuseFencedRunWithoutSink<S>(\n store: Store<S> | undefined,\n onError: OnError | undefined,\n): void {\n if (onError !== undefined || store === undefined || !isFencedStore(store)) {\n return;\n }\n throw new Error(\n \"@demlik/tea: a run on a fenced store needs an `onError`. Without one, a lost fence ends the process and every run in it. Pass `onError` to `run`.\",\n );\n}\n\n// === ctx + handle: ports ===\n\n/**\n * Ports (`definePort`): `ctx.emit(port, value)` from a handler and\n * `emitPort` / `subscribePort` on the handle. Fanout is synchronous and\n * throw-isolated under `\"port-emit\"`; no subscribers is a no-op.\n */\nexport function ports<S, M, C>(): AnyExtension<S, M, C> {\n return (loop) => {\n const registry = new Map<Port<unknown>, Set<(value: unknown) => void>>();\n function emit<T>(port: Port<T>, value: T): void {\n const subscribers = registry.get(port as Port<unknown>);\n if (!subscribers || subscribers.size === 0) return;\n loop.fanout(subscribers, \"port-emit\", (listener) => listener(value));\n }\n return {\n ctx: { emit },\n handle: {\n subscribePort<T>(\n port: Port<T>,\n listener: (value: T) => void,\n ): () => void {\n const key = port as Port<unknown>;\n const bucket = registry.get(key) ?? new Set();\n registry.set(key, bucket);\n const erased = listener as (value: unknown) => void;\n bucket.add(erased);\n return () => {\n bucket.delete(erased);\n if (bucket.size === 0 && registry.get(key) === bucket)\n registry.delete(key);\n };\n },\n emitPort: emit,\n },\n };\n };\n}\n\n// === commit observers ===\n\n/**\n * `subscribe` (zero-arg change notifiers, told after the save and before the\n * Cmds, #695), then, after the Cmds, `observe` (every applied transition's\n * `(msg, state)`) or, at boot, `onBoot` (the initial State, once; a handler\n * registered after boot fires at once). Each fanout is throw-isolated under its\n * own phase.\n */\nexport function observation<S, M, C>(): AnyExtension<S, M, C> {\n return (loop) => {\n const listeners = new Set<() => void>();\n const observers = new Set<(msg: M, state: S) => void>();\n const bootHandlers = new Set<(state: S) => void>();\n let booted = false;\n return {\n installed() {\n loop.fanout(listeners, \"listener\", (listener) => listener());\n },\n commit(msg, state) {\n if (msg === undefined) {\n booted = true;\n loop.fanout(bootHandlers, \"boot\", (handler) => handler(state));\n return;\n }\n loop.fanout(observers, \"observer\", (observer) => observer(msg, state));\n },\n handle: {\n subscribe: subscribeTo(listeners),\n observe: subscribeTo(observers),\n onBoot(handler: (state: S) => void): () => void {\n const state = loop.state();\n if (booted && state !== undefined) {\n loop.fanout([handler], \"boot\", (h) => h(state));\n return () => {};\n }\n return subscribeTo(bootHandlers)(handler);\n },\n },\n };\n };\n}\n\nfunction subscribeTo<T>(set: Set<T>): (item: T) => () => void {\n return (item) => {\n set.add(item);\n return () => {\n set.delete(item);\n };\n };\n}\n\n/**\n * The semantic event channel: `project` maps each APPLIED transition to zero or\n * more public events, and `on(type, handler)` receives only its own type. The\n * machine's private Msg names never reach `on`. A throw in the projector or a\n * handler is isolated under `\"event\"`. Boot projects nothing.\n */\nexport function semanticEvents<S, M, C, E extends { type: string }>(\n project: ((msg: M, state: S) => readonly E[]) | undefined,\n): AnyExtension<S, M, C> {\n return (loop) => {\n const handlers = new Map<string, Set<(event: E) => void>>();\n return {\n commit(msg, state) {\n if (msg === undefined || project === undefined || handlers.size === 0)\n return;\n let events: readonly E[];\n try {\n events = project(msg, state);\n } catch (err) {\n loop.report(err, \"event\");\n return;\n }\n for (const event of events) {\n const bucket = handlers.get(event.type);\n if (bucket === undefined) continue;\n loop.fanout(bucket, \"event\", (handler) => handler(event));\n }\n },\n handle: {\n on(type: string, handler: (event: E) => void): () => void {\n const bucket = handlers.get(type) ?? new Set();\n handlers.set(type, bucket);\n bucket.add(handler);\n return () => {\n bucket.delete(handler);\n if (bucket.size === 0 && handlers.get(type) === bucket)\n handlers.delete(type);\n };\n },\n },\n };\n };\n}\n\n/**\n * Run terminality: only saved State can be reported terminal. `done()` resolves\n * on a terminal commit (boot included — a rehydrated run can land terminal),\n * or rejects when that State's save fails. Storeless State is eligible as soon\n * as it is set. No predicate → never terminal.\n *\n * When tea closes a run because of a failure, `done()` fails with it: a Sub\n * failure as `SubFailedError`, and a reducer throw that halts the run as what was\n * thrown. The first failure is the one that counts. It answers only once no Msg\n * can fold any more, because a Msg already in line can still reach a terminal\n * State. A State terminal by then keeps its answer: its own save decides. So a\n * waiter, a later caller and `result()` read one outcome.\n *\n * A `stop()` with no failure leaves `done()` as it was.\n */\nexport function terminality<S, M, C>(\n isTerminal: ((state: S) => boolean) | undefined,\n): AnyExtension<S, M, C> {\n const holds = isTerminal ?? (() => false);\n return (loop) => {\n const waiters = new Set<{\n resolve(state: S): void;\n reject(error: unknown): void;\n }>();\n // Boxed: a reducer can throw anything, `undefined` included.\n let failure: { readonly answer: unknown } | undefined;\n // Set once the queue is finished with: the failure is now `done()`'s answer.\n let stoppedBy: { readonly answer: unknown } | undefined;\n return {\n subFailed(error) {\n failure ??= { answer: new SubFailedError(error) };\n },\n halted(error) {\n failure ??= { answer: error };\n },\n drained() {\n if (failure === undefined) return;\n stoppedBy = failure;\n const state = loop.state() as S;\n const terminal = holds(state);\n // A terminal State still waiting on its save keeps its waiters. A saved\n // one can have them too: a runner that throws skips its step's commit.\n if (terminal && loop.saveStatus().type !== \"saved\") return;\n const parked = [...waiters];\n waiters.clear();\n for (const waiter of parked) {\n if (terminal) waiter.resolve(state);\n else waiter.reject(stoppedBy.answer);\n }\n },\n commit(_msg, state) {\n if (\n loop.saveStatus().type !== \"saved\" ||\n waiters.size === 0 ||\n !holds(state)\n )\n return;\n const parked = [...waiters];\n waiters.clear();\n for (const waiter of parked) waiter.resolve(state);\n },\n saveFailed(state, error) {\n if (waiters.size === 0 || !holds(state)) return;\n const parked = [...waiters];\n waiters.clear();\n for (const waiter of parked) waiter.reject(error);\n },\n handle: {\n result(): S | undefined {\n const current = loop.state() as S;\n return loop.saveStatus().type === \"saved\" && holds(current)\n ? current\n : undefined;\n },\n done(): Promise<S> {\n const current = loop.state() as S;\n if (holds(current)) {\n const saved = loop.saveStatus();\n if (saved.type === \"saved\") return Promise.resolve(current);\n if (saved.type === \"failed\") return Promise.reject(saved.error);\n } else if (stoppedBy !== undefined) {\n return Promise.reject(stoppedBy.answer);\n }\n return new Promise<S>((resolve, reject) => {\n waiters.add({ resolve, reject });\n });\n },\n },\n };\n };\n}\n\n/**\n * Telemetry (#268: `withTelemetry` moved inside tea). Every applied transition\n * hands the sink `{ seq, msgType, at }` — `seq` counts this run's applied\n * transitions from 1, `at` reads `clock`. Fire-and-forget: the loop never waits\n * on the sink, and a sink that throws or rejects reaches `onError` under\n * `\"observer\"` without touching the run.\n */\nexport function telemetry<S, M extends { type: string }, C>(\n sink: TelemetrySink | undefined,\n clock: () => number,\n): AnyExtension<S, M, C> {\n return (loop: LoopServices<S>): Extension<S, M, C> => {\n if (sink === undefined) return {};\n let seq = 0;\n return {\n commit(msg) {\n if (msg === undefined) return;\n seq += 1;\n const event: TelemetryEvent = { seq, msgType: msg.type, at: clock() };\n try {\n Promise.resolve(sink(event)).catch((err: unknown) =>\n loop.report(err, \"observer\"),\n );\n } catch (err) {\n loop.report(err, \"observer\");\n }\n },\n };\n };\n}\n\n// === assembly: the one order both engines run the built-ins in ===\n\n/** The `run` options the built-ins read. Every engine's `run` accepts them. */\nexport interface BuiltinOptions<\n S,\n M extends { type: string },\n E extends { type: string },\n> {\n readonly clock?: () => number;\n readonly events?: (msg: M, state: S) => readonly E[];\n readonly supervision?: Supervision<S, M>;\n readonly terminal?: (state: S) => boolean;\n readonly telemetry?: TelemetrySink;\n}\n\n/**\n * The built-ins, in the one order that gives them their meaning. Every engine\n * builds its loop from this list, so a machine means the same thing on each.\n *\n * Update middleware nests first-outermost: supervision wraps the identity\n * filter (a throwing `ofMsg` is supervised like a reducer throw — spike #264\n * run 3), which wraps the dev checks around the reducer itself. A Msg with no\n * cell rejects through supervision unsupervised (#310). Change listeners hear\n * before the Cmds. Commit callbacks run after them, in order: `observe` /\n * `onBoot`, then semantic events, then `done()` waiters, then telemetry.\n */\nexport function builtinExtensions<\n S,\n M extends { type: string },\n C,\n E extends { type: string },\n>(\n machine: CellTable & {\n readonly identity?: Identity<S, M>;\n readonly cmds?: readonly AnyCmdDef[];\n },\n opts: BuiltinOptions<S, M, E>,\n): readonly ExtensionFactory<S, M, C>[] {\n const clock = opts.clock ?? Date.now;\n return [\n supervision(opts.supervision, machine),\n identityFilter(machine.identity),\n devChecks(),\n cmdDefinitions(machine.cmds ?? [], clock),\n fencing(),\n ports(),\n observation(),\n semanticEvents(opts.events),\n terminality(opts.terminal),\n telemetry(opts.telemetry, clock),\n ];\n}\n","/**\n * The core loop both engines run (#280, spike #264). It does five things and\n * nothing else: it keeps one serial tail, folds a Msg into a transition, saves\n * before any effect, reconciles the running Subs, and runs the transition's\n * Cmds, dispatching what they return. It has no special case for any built-in.\n *\n * One loop, not one per engine (#283). The loop calls a handler and a runner\n * through erased signatures and awaits a Promise either way, so an engine's\n * own types never pass through it. The Effect engine turns each handler and\n * runner into a fiber at its own edge, run with the services the caller\n * provided, so Effect services (`R`) reach every handler without the loop\n * carrying a type slot for them — the gap spike #260's generic loop left open.\n * Sharing the loop is also what makes the two engines' traces identical: the\n * fold, the save order and the built-ins are the same code.\n *\n * Everything else `run` offers is an {@link Extension} built on ten points:\n *\n * 1. `update` — middleware over the fold. Returning `null` means \"no\n * transition\": no save, no effects, no commit.\n * 2. `interpret` — middleware over one Cmd's handler call; it maps what the\n * handler returned to the Msgs to dispatch, if any.\n * 3. `store` — a wrapper over the store the caller handed `run`.\n * 4. `installed` — called once a transition's State is past its save and its\n * Subs are reconciled, before its first Cmd's handler starts.\n * This is Elm's `view`: it never waits on a Cmd (#695).\n * 5. `commit` — called after a transition's effects, with the Msg and the\n * committed State, even when a Cmd handler threw. Boot\n * passes `undefined` for the Msg.\n * 6. `saveFailed` — called when a transition's save rejects, before that\n * rejection reaches the dispatch caller.\n * 7. `subFailed` — called when a Sub's failure stops the run, before the\n * engine is told to stop it.\n * 8. `halted` — called when an extension halts the run with an error.\n * 9. `drained` — called once no Msg can fold any more, so the State is the\n * run's last: `stop()` has finished with the queue, the\n * host's or the one a failure started.\n * 10. `ctx` — keys added to the ctx every Cmd handler receives, so a\n * handler that runs another handler inside its own can reach\n * the same machinery the loop applies to it.\n *\n * An extension may also add methods to the run handle (`handle`). Array order\n * is nesting order: the first extension's middleware is the outermost, and\n * `installed` and commit callbacks run first to last. `builtinExtensions`\n * (`./builtins`) fixes that order for every engine; it is not a user-facing\n * plugin API (#268).\n *\n * `stop()` is the core's own lifecycle, because the gate it drives is what every\n * dispatch passes through: open → draining → closed, or → abandoned when\n * `stop()` gives up on the queue (ADR 0025), never backwards.\n */\n\nimport type { Dispose, Sub, SubEntry } from \"../../pure/core\";\nimport { desiredSub, detachWork, followUps } from \"../../pure/core\";\nimport type {\n DispatchSettle,\n OnError,\n RuntimeErrorContext,\n RuntimeErrorPhase,\n Store,\n} from \"../../runtime-types\";\nimport {\n DispatchDiscardedError,\n DisposeTimeoutNotice,\n DrainTimeoutNotice,\n QuiescenceTimeoutError,\n RuntimeDiscardedError,\n RuntimeDiscardNotice,\n} from \"../../runtime-types\";\nimport { type InstalledStateRead, installedState } from \"./installed-state\";\nimport { restore } from \"./restore\";\n\n// === The extension points ===\n\n/** A fold's result: the next State and the Cmds it emits. */\nexport type Transition<S, C> = readonly [S, readonly C[]];\n\n/** One fold step. `null` means the Msg produced no transition. */\nexport type Step<S, M, C> = (state: S, msg: M) => Transition<S, C> | null;\n\n/**\n * One Cmd's handler call; resolves to the Msg to dispatch, a list of them in\n * order, or nothing. `dispatch` is what the handler is handed for Msgs it fires\n * itself, so an extension can check them on the way out.\n */\nexport type InterpretStep<C, M> = (\n cmd: C,\n dispatch: (msg: M) => void,\n) => Promise<unknown>;\n\n/** Whether the current State has crossed this transition's save boundary. */\ntype SaveStatus =\n | { readonly type: \"pending\" }\n | { readonly type: \"saved\" }\n | { readonly type: \"failed\"; readonly error: unknown };\n\n/** What the loop lends an extension. */\nexport interface LoopServices<S> {\n /** Route a failure with no caller to the `onError` sink. */\n report(error: unknown, phase: RuntimeErrorPhase): void;\n /** Call `fn` for each item, routing every throw to the sink under `phase`. */\n fanout<T>(\n items: Iterable<T>,\n phase: RuntimeErrorPhase,\n fn: (item: T) => void,\n ): void;\n /**\n * Close the gate for good because of `error`, and have the engine stop the\n * run: every later dispatch is refused, the Msgs already in line still fold,\n * and then the Subs close and the final save runs (ADR 0033).\n */\n halt(error: unknown): void;\n /** The current State, or `undefined` before boot has produced one. */\n state(): S | undefined;\n /** The current State's save status; storeless State is saved immediately. */\n saveStatus(): SaveStatus;\n}\n\n/** One built-in, as the loop sees it. Every member is optional. */\nexport interface Extension<S, M, C> {\n readonly update?: (next: Step<S, M, C>) => Step<S, M, C>;\n readonly interpret?: (next: InterpretStep<C, M>) => InterpretStep<C, M>;\n readonly store?: (store: Store<S>) => Store<S>;\n readonly installed?: () => void;\n readonly commit?: (msg: M | undefined, state: S) => void;\n readonly saveFailed?: (state: S, error: unknown) => void;\n readonly subFailed?: (error: unknown) => void;\n readonly halted?: (error: unknown) => void;\n readonly drained?: () => void;\n readonly ctx?: object;\n readonly handle?: object;\n}\n\n/** An extension is built once per run, from the loop's services. */\nexport type ExtensionFactory<S, M, C> = (\n loop: LoopServices<S>,\n) => Extension<S, M, C>;\n\n/** A Cmd handler as the loop calls it. */\nexport type LoopHandler<M> = (\n cmd: never,\n ctx: unknown,\n dispatch: (msg: M) => void,\n) => unknown;\n\n/**\n * A Sub runner as the loop calls it. `fail` is how an engine edge reports that\n * a Sub's source died with an error the Sub did not turn into a Msg; the loop\n * then drops the Sub and stops the run. A user runner is never handed it.\n */\nexport type LoopRunner<Ctx, M> = (\n sub: Sub,\n ctx: Ctx,\n dispatch: (msg: M) => void,\n fail: (error: unknown) => void,\n) => Dispose;\n\n/** Everything the loop needs from `run`. */\nexport interface LoopConfig<S, M extends { type: string }, C, Ctx> {\n readonly init: (loaded: S | null, ctx: Ctx) => Transition<S, C>;\n /** The innermost fold: the machine's own `update`. */\n readonly reduce: (state: S, msg: M) => Transition<S, C>;\n readonly subs: readonly SubEntry<S>[];\n readonly runnerFor: (type: string) => LoopRunner<Ctx, M> | undefined;\n readonly handlerFor: (type: string) => LoopHandler<M> | undefined;\n readonly store: Store<S> | undefined;\n readonly ctx: Ctx;\n readonly onError: OnError | undefined;\n readonly disposeTimeoutMs: number;\n readonly idleCap: number;\n readonly extensions: readonly ExtensionFactory<S, M, C>[];\n /**\n * How the engine stops the run once tea itself closed it because of a\n * failure: a Sub failed with an error it did not handle (#309), or an\n * extension halted the run, as the `stop` strategy does on a reducer throw\n * (ADR 0033). The loop has already reported the error and closed the gate to\n * new work; this does the rest — `stop()` on the Promise engine, closing the\n * run's Scope with the failure on the Effect engine. Called at most once,\n * possibly before `startLoop` has returned.\n */\n readonly stopOnFailure: (failure: RunFailure) => void;\n}\n\n/** Why tea itself stops a run, as {@link LoopConfig.stopOnFailure} hears it. */\nexport type RunFailure =\n /** A Sub's source died with an error the Sub did not turn into a Msg. */\n | { readonly kind: \"sub\"; readonly error: unknown }\n /** An extension halted the run with `error`: a reducer throw under `stop`. */\n | { readonly kind: \"halt\"; readonly error: unknown };\n\n// === liveWork: \"can anything still transition me without a dispatch?\" ===\n//\n// The one read `driveToDone` needs that the public `Runtime` does not carry:\n// after `start`'s chain quiesces, only a live Sub or an in-flight Cmd can still\n// enqueue a transition; with neither, waiting is a leak (#68). Module-private\n// and symbol-keyed so it reaches no export (the export map is the contract —\n// MAINTAINING.md), yet enumerable so it survives the spread a wrapper does over\n// the handle.\nexport const liveWork: unique symbol = Symbol(\"demlik-tea.liveWork\");\n\n/** The runtime's own sources of a caller-less transition, counted. */\nexport interface LiveWork {\n /** Live Subs — the running `machine.subs` entries. */\n readonly subs: number;\n /** Interpret handlers currently awaiting. */\n readonly cmds: number;\n}\n\nexport interface LiveWorkProbe {\n readonly [liveWork]: () => LiveWork;\n}\n\n// === post: the one entry onto the line that waits for nothing ===\n//\n// What `tell` stands on (ADR 0029, item 5). Module-private and symbol-keyed for\n// the same reason `liveWork` is: it reaches no export, and it survives the\n// spread a wrapper does over the handle.\nexport const post: unique symbol = Symbol(\"demlik-tea.post\");\n\n/**\n * What `tell` answers, at once, about the Msg it was handed. It says where the\n * Msg stands at that moment and nothing about how it ends.\n *\n * - `\"queued\"`: the Msg is on the receiver's line. It has not been handled.\n * - `\"stopped\"`: the receiver is stopping or has stopped, so the Msg is not on\n * its line and never will be.\n * - `\"no_cell\"`: the receiver's current State has no cell for the Msg, so the\n * Msg is not on its line. That reads the State as it is now, not the State\n * the Msgs already in line will leave.\n */\nexport type TellAnswer = \"queued\" | \"stopped\" | \"no_cell\";\n\n/** A run handle `tell` can reach the line of. */\nexport interface Postable<S, M> {\n readonly [post]: (msg: M, takes: (state: S) => boolean) => TellAnswer;\n}\n\n/** The members the core itself puts on the run handle. */\nexport interface LoopHandle<S, M>\n extends LiveWorkProbe,\n Postable<S, M>,\n InstalledStateRead<S> {\n dispatch(msg: M, opts?: { readonly settle?: DispatchSettle }): Promise<void>;\n dispatchOnce(msg: M): Promise<void>;\n getState(): S;\n ready: Promise<unknown>;\n idle(): Promise<void>;\n stop(): Promise<void>;\n}\n\n// Default `onError` sink: re-throw on a fresh macrotask so the failure reaches\n// the host's global error handler instead of vanishing — surface, not swallow\n// (invariant 6).\n//\n// A `RuntimeDiscardNotice` is NOT a failure of the runtime's own contract — a\n// host tearing a runtime down with Cmds in flight (a React ctx change, a\n// navigation) is legal, merely lossy. Rethrowing it would make every\n// unmount-during-fetch an uncaught error for consumers who never configured a\n// sink, so the default WARNS: loud enough that the silent discard #365\n// describes cannot happen again, never fatal. A configured `onError` sees\n// `\"discard\"` like any other phase and can route or ignore it.\n//\n// The branch keys on the ERROR CLASS, never on `context.phase`: `report`\n// hands a THROWING consumer sink's own error back here with the phase it was\n// handling, so a phase-keyed branch would warn away a broken sink and re-create\n// the very silent failure this exists to remove.\n//\n// Everything else is rethrown on a fresh macrotask, which ends a Node process\n// and every run in it: a failing Sub (`\"sub\"`) on a run with no sink keeps that\n// behaviour on purpose. A store that refuses a second writer never gets here by\n// omission: a run on one must pass an `onError`, held by its type\n// (`StoreAndOnError`, ADR 0031) and, where the type was thrown away, by each\n// engine's `run` refusing to start (`./builtins`). So there is no case here for\n// `StoreConflictError`: one that arrives came through a throwing sink, or from a\n// store that does not say what it is, and both stay loud.\nexport function defaultOnError(\n error: unknown,\n _context: RuntimeErrorContext,\n): void {\n if (error instanceof RuntimeDiscardNotice) {\n console.warn(error);\n return;\n }\n setTimeout(() => {\n throw error;\n }, 0);\n}\n\n/**\n * The rejection of a dispatch into a run whose gate is closed: `stop()` has\n * returned, or a built-in halted it. Internal, so the Promise engine's\n * rejection reads as before, an `Error` with this message; the Effect engine\n * reads the class to fail the dispatch with `Stopped`.\n */\nexport class RuntimeStoppedError extends Error {\n constructor(readonly msgType: string) {\n super(\"@demlik/tea: runtime stopped\");\n }\n}\n\n/** One started Sub: its type, the cleanup its runner returned, and whether it failed. */\nclass RunningSub {\n dispose: Dispose = () => {};\n failed = false;\n constructor(readonly type: string) {}\n}\n\n/**\n * The Msgs on the line, per Msg type: one more when a Msg joins the tail, one\n * fewer when its turn comes. Types and counts only, so a sink that logs what\n * this reports never sees a Msg's payload.\n */\nclass MsgLine {\n private readonly counts = new Map<string, number>();\n\n joined(type: string): void {\n this.counts.set(type, (this.counts.get(type) ?? 0) + 1);\n }\n\n reachedTurn(type: string): void {\n const left = (this.counts.get(type) ?? 0) - 1;\n if (left > 0) this.counts.set(type, left);\n else this.counts.delete(type);\n }\n\n /** The counts now, in a fresh object later turns cannot change. */\n snapshot(): Readonly<Record<string, number>> {\n return Object.fromEntries(this.counts);\n }\n}\n\n/**\n * Start the loop. Returns the handle synchronously; boot runs as the FIRST entry\n * on the serial tail. Save-then-effects ordering is structural: every\n * transition installs State, awaits the save, reconciles Subs, tells `subscribe`\n * listeners, runs the Cmds, then calls the commit callbacks — even when a Cmd\n * handler throws.\n */\nexport function startLoop<S, M extends { type: string }, C, Ctx>(\n config: LoopConfig<S, M, C, Ctx>,\n): LoopHandle<S, M> {\n const { idleCap, disposeTimeoutMs } = config;\n const onError: OnError = config.onError ?? defaultOnError;\n\n // A throwing sink routes THAT throw through `defaultOnError`, with the context\n // the sink was handling, so it cannot re-create a silent failure.\n function report(error: unknown, phase: RuntimeErrorPhase): void {\n const context: RuntimeErrorContext = { phase };\n try {\n onError(error, context);\n } catch (sinkError) {\n defaultOnError(sinkError, context);\n }\n }\n\n // Throw-isolated fanout: one bad consumer never strands its siblings\n // (invariant 6). The single home for that isolation discipline.\n function fanout<T>(\n items: Iterable<T>,\n phase: RuntimeErrorPhase,\n fn: (item: T) => void,\n ): void {\n for (const item of items) {\n try {\n fn(item);\n } catch (err) {\n report(err, phase);\n }\n }\n }\n\n // `state` is late-initialized: synchronously when there is no store to read,\n // otherwise inside the boot step (the head of the tail).\n let state: S | undefined;\n // The State the listeners were last told about. Behind `state` while a save is\n // pending, and for good when one fails.\n let installed: S | undefined;\n let saveStatus: SaveStatus = { type: \"pending\" };\n let bootError: unknown = null;\n // The dispatch gate. It only ever ADVANCES — `\"open\"` → `\"draining\"` →\n // `\"closed\"` or `\"abandoned\"` — so the discard window can never re-open under\n // a second `stop()`.\n //\n // \"open\" — normal operation; `enqueueDispatch` accepts.\n // \"draining\" — inside `stop()`, before the tail has settled. New work is\n // still refused (refusing is what makes the drain terminate),\n // but a Msg refused HERE was discarded BY the teardown: an\n // in-flight Cmd's follow-up, a detached handler's last Msg, or\n // a Sub still live because subs are torn down after the drain.\n // So the rejection is a `DispatchDiscardedError` reported\n // under `phase: \"discard\"` — lossy, legal, warn-only.\n // \"closed\" — halted by an extension, or `stop()` has drained the tail. A\n // dispatch refused here is a consumer using a retired runtime\n // — a real error, and it stays loud.\n // \"abandoned\" — `stop()` gave up on the tail at `disposeTimeoutMs` (ADR\n // 0025). Closed to a caller exactly as `\"closed\"` is, and the\n // work still on the tail is dropped as it surfaces: a stuck\n // handler's late result, the next Cmd of its step, the Msgs\n // queued behind it. Each is a teardown discard, never a save.\n // A boot whose load returns only now saves nothing either\n // (`commit`): no store call starts once `stop()` gave up.\n let gate: \"open\" | \"draining\" | \"closed\" | \"abandoned\" = \"open\";\n // The work currently awaiting: one entry per interpret handler call, named by\n // its Cmd type, and one unnamed entry per piece of detached work. Written only\n // by `trackInFlight`, read by `stop()` to make a mid-flight teardown LOUD\n // instead of silent (#365) and to name what it gave up on.\n const inFlight = new Set<{ readonly cmdType: string | undefined }>();\n\n const services: LoopServices<S> = {\n report,\n fanout,\n halt(error) {\n // Only a run that was open still needs stopping: past that, a Sub failure\n // or the host's `stop()` has already started the teardown.\n const stopping = gate !== \"open\";\n if (gate !== \"abandoned\") gate = \"closed\";\n for (const ext of extensions) ext.halted?.(error);\n // The same stop a Sub failure starts (ADR 0033). Its teardown waits for\n // `tail`, which already covers this step and every Msg behind it, so a\n // Msg in line still folds before the Subs close and the final save runs.\n if (!stopping) config.stopOnFailure({ kind: \"halt\", error });\n },\n state: () => state,\n saveStatus: () =>\n config.store === undefined ? { type: \"saved\" } : saveStatus,\n };\n const extensions = config.extensions.map((make) => make(services));\n\n function lastStateReached(): void {\n fanout(extensions, \"sub\", (ext) => ext.drained?.());\n }\n\n // The fold, the handler call and the store, each wrapped by the extensions\n // that ask for it. `reduceRight` makes the first extension the outermost.\n const update: Step<S, M, C> = extensions.reduceRight<Step<S, M, C>>(\n (next, ext) => (ext.update ? ext.update(next) : next),\n config.reduce,\n );\n const interpret: InterpretStep<C, M> = extensions.reduceRight<\n InterpretStep<C, M>\n >((next, ext) => (ext.interpret ? ext.interpret(next) : next), callHandler);\n const store: Store<S> | undefined =\n config.store === undefined\n ? undefined\n : extensions.reduceRight<Store<S>>(\n (inner, ext) => (ext.store ? ext.store(inner) : inner),\n config.store,\n );\n const installedCallbacks = extensions.flatMap((ext) =>\n ext.installed ? [ext.installed] : [],\n );\n const commitCallbacks = extensions.flatMap((ext) =>\n ext.commit ? [ext.commit] : [],\n );\n\n // Copied into a fresh object so handlers get the contributed keys without\n // mutating the caller's ctx (which may be shared across runtimes / tests).\n // `Object.assign`, not a spread: tsc cannot prove a generic `Ctx` is an\n // object type, and a spread refuses it.\n const handlerCtx: unknown = Object.assign(\n {},\n config.ctx,\n ...extensions.map((ext) => ext.ctx ?? {}),\n { [detachWork]: detachInFlight },\n );\n\n // Every step chains onto `tail` — the single concurrency gate.\n let tail: Promise<void> = Promise.resolve();\n // Every Msg on `tail` whose turn has not come. All of them are dropped if\n // `stop()` gives up, so this is what its notice counts.\n const line = new MsgLine();\n\n // The running Subs, keyed by their derived id. An id present here is a runner\n // that started and has neither been disposed nor failed.\n const subRegistry = new Map<string, RunningSub>();\n\n // Tear down each named sub: run its cleanup (throws routed to the sink under\n // `\"sub-cleanup\"`), then drop it from the registry regardless.\n function stopSubs(ids: Iterable<string>): void {\n for (const id of ids) {\n const running = subRegistry.get(id);\n if (running === undefined) continue;\n try {\n trackDisposal(running.dispose());\n } catch (err) {\n report(err, \"sub-cleanup\");\n }\n subRegistry.delete(id);\n }\n }\n\n // Teardown work that returned a Promise and has not settled. Reconcile runs\n // inside the synchronous transition path (invariant 2) and cannot await a\n // cleanup, so every async disposal is remembered here the moment it starts,\n // and `stop()` drains the set before resolving: `await runtime.stop()` then\n // means \"teardown is done\", the only reading a host evicting an isolate can\n // act on. The rejection is attached HERE, once, so it can never become an\n // unhandled rejection.\n const pendingDisposals = new Set<Promise<void>>();\n\n function trackDisposal(result: void | Promise<void>): void {\n if (!(result instanceof Promise)) return;\n const settling: Promise<void> = result\n .catch((err) => {\n report(err, \"sub-cleanup\");\n })\n .finally(() => {\n pendingDisposals.delete(settling);\n });\n pendingDisposals.add(settling);\n }\n\n /**\n * Wait for `work`, for at most `disposeTimeoutMs`. The one bound both of\n * `stop()`'s waits share: `stop()` resolves regardless (contract), so neither\n * a handler nor a release that never settles may become a host that never\n * shuts down (ADR 0025).\n */\n async function withinLimit(\n work: Promise<unknown>,\n ): Promise<\"settled\" | \"timeout\"> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n work.then(() => \"settled\" as const),\n new Promise<\"timeout\">((resolve) => {\n timer = setTimeout(() => resolve(\"timeout\"), disposeTimeoutMs);\n }),\n ]);\n } finally {\n if (timer !== undefined) clearTimeout(timer);\n }\n }\n\n /**\n * Wait for the tail, up to the limit, and settle the gate on what happened.\n * On expiry report a `DrainTimeoutNotice` naming the handlers still awaiting\n * and counting the Msgs on the line, and leave the gate `\"abandoned\"`, which\n * is what drops their late work and those Msgs. The gate has refused new work\n * since `stop()` began, so the count cannot grow after this.\n */\n async function drainTail(): Promise<void> {\n if ((await withinLimit(tail)) === \"settled\") {\n gate = \"closed\";\n return;\n }\n report(\n new DrainTimeoutNotice(\n [...inFlight].flatMap(({ cmdType }) => cmdType ?? []),\n inFlight.size,\n disposeTimeoutMs,\n line.snapshot(),\n ),\n \"discard\",\n );\n gate = \"abandoned\";\n }\n\n /**\n * Wait for the disposals started so far, up to the limit. On expiry report a\n * `DisposeTimeoutNotice` and return anyway.\n */\n async function drainDisposals(): Promise<void> {\n if (pendingDisposals.size === 0) return;\n const outstanding = [...pendingDisposals];\n if ((await withinLimit(Promise.all(outstanding))) === \"timeout\") {\n report(\n new DisposeTimeoutNotice(outstanding.length, disposeTimeoutMs),\n \"discard\",\n );\n }\n }\n\n /**\n * Reconcile the running Subs against `state`, after every save. An id already\n * running is left alone, a running id no longer wanted is stopped, and a\n * wanted id not yet running is started through its runner.\n *\n * Every failure is collected, never thrown mid-pass, so one bad entry cannot\n * strand its siblings: a throwing `deps` leaves the running Subs of that type\n * untouched, and a throwing runner is not registered. The first error is\n * thrown after the pass. A Sub type with no runner is a failure too — nothing\n * would ever start it.\n */\n function reconcileSubs(): void {\n let firstError: unknown = null;\n const remember = (err: unknown): void => {\n if (firstError === null) firstError = err;\n };\n const desired = new Map<string, Sub>();\n // Types whose entry could not be read this pass: keep what is running.\n const unreadTypes = new Set<string>();\n for (const entry of config.subs) {\n try {\n const sub = desiredSub(entry, state as S);\n if (sub !== null) desired.set(sub.id, sub);\n } catch (err) {\n remember(err);\n unreadTypes.add(entry.type);\n }\n }\n\n stopSubs(\n [...subRegistry].flatMap(([id, running]) =>\n desired.has(id) || unreadTypes.has(running.type) ? [] : [id],\n ),\n );\n\n // A run that is stopping starts nothing: stop() would only tear it down,\n // and a Sub that failed must not be re-armed by a step still queued.\n const starting = gate === \"open\" ? desired : new Map<string, Sub>();\n for (const [id, sub] of starting) {\n if (subRegistry.has(id)) continue;\n const runner = config.runnerFor(sub.type);\n if (runner === undefined) {\n remember(\n new Error(\n `@demlik/tea: no subscribe runner for Sub type \"${sub.type}\". ` +\n \"Pass one to run in `subscribe`.\",\n ),\n );\n continue;\n }\n const running = new RunningSub(sub.type);\n try {\n // A runner's dispatch is unawaited by construction, so it gets the\n // wrapped form, which also queues the transition behind this step —\n // never on the runner's own stack (spike #260).\n running.dispose = runner(sub, config.ctx, dispatchUnawaited, (error) =>\n subFailed(id, running, error),\n );\n } catch (err) {\n // A runner that throws while starting is a Sub failure too: the step\n // that started it rejects with the throw, and the run stops.\n remember(err);\n subFailed(id, running, err);\n continue;\n }\n if (!running.failed) {\n subRegistry.set(id, running);\n continue;\n }\n // The source died before its runner returned: never registered, but\n // its cleanup still runs.\n try {\n trackDisposal(running.dispose());\n } catch (err) {\n report(err, \"sub-cleanup\");\n }\n }\n if (firstError !== null) throw firstError;\n }\n\n /**\n * A Sub's source died with an error the Sub did not turn into a Msg (#309,\n * ruling R3.1 of #325). The Elm answer: a Sub maps the failures it expects to\n * Msgs itself, so one that reaches here is unhandled, and the run stops\n * rather than keep a dead Sub counted as live. The Sub leaves the registry,\n * the error reaches the sink under `\"sub\"`, the gate starts draining so no\n * new Msg is accepted, `done()` hears it, and the engine stops the run. A\n * failure while the run is already stopping is reported and nothing more.\n */\n function subFailed(id: string, running: RunningSub, error: unknown): void {\n if (running.failed) return;\n running.failed = true;\n if (subRegistry.get(id) === running) subRegistry.delete(id);\n report(error, \"sub\");\n if (gate !== \"open\") return;\n gate = \"draining\";\n for (const ext of extensions) ext.subFailed?.(error);\n config.stopOnFailure({ kind: \"sub\", error });\n }\n\n // The ONE `(msg) => void` handed to every producer that cannot await its own\n // dispatch: a Cmd handler's late tail, a Sub runner. Its rejection has no\n // caller, so it routes to the sink with the phase derived from the error\n // class (`reportUndelivered`). Handing those sites the raw `enqueueDispatch`\n // would turn a Sub firing during `stop()`'s drain into an unhandled rejection\n // the sink never sees.\n function dispatchUnawaited(msg: M): void {\n enqueueDispatch(msg, \"loop\").catch(reportUndelivered);\n }\n\n // A `DispatchDiscardedError` means the teardown refused the Msg (`\"discard\"`,\n // warn-only); anything else is a genuine failure of the follow-up itself.\n function reportUndelivered(error: unknown): void {\n report(\n error,\n error instanceof DispatchDiscardedError ? \"discard\" : \"follow-up\",\n );\n }\n\n /**\n * Count a handler's work as in flight for exactly the lifetime of its\n * promise. `finally`-balanced, so a rejecting handler can never strand the\n * count above zero. A handler that throws SYNCHRONOUSLY never reaches here,\n * which is correct — it was never in flight. `cmdType` names a handler call;\n * detached work has no Cmd of its own to name.\n */\n function trackInFlight<T>(\n work: T | Promise<T>,\n cmdType?: string,\n ): Promise<T> {\n const entry = { cmdType };\n inFlight.add(entry);\n return Promise.resolve(work).finally(() => {\n inFlight.delete(entry);\n });\n }\n\n /**\n * Enlist work a handler will NOT return — the `detachWork` key on ctx. A\n * handler that fans out inside its own Cmd (ADR 0018) returns before its\n * effect finishes; handing the promise here puts it back on both accountings\n * a returned promise had: `trackInFlight`, so `stop()` still reports it, and\n * the serial `tail`, so `idle()` drains it. The tail extension is\n * SYNCHRONOUS, so a `drainToQuiescence` that already read `tail` sees the\n * reference change and loops again.\n */\n function detachInFlight(work: Promise<unknown>): void {\n const tracked = trackInFlight(work);\n tracked.catch(reportUndelivered);\n tail = tail\n .then(async () => {\n await tracked;\n })\n .catch(() => {});\n }\n\n // The innermost interpret step: call the Cmd's handler, if one is wired. A\n // missing handler returns nothing, which is invariant 6's forward progress\n // for a miswired consumer.\n function callHandler(cmd: C, dispatch: (msg: M) => void): Promise<unknown> {\n const { type } = cmd as { type: string };\n const handler = config.handlerFor(type);\n if (handler === undefined) return Promise.resolve(undefined);\n return trackInFlight(handler(cmd as never, handlerCtx, dispatch), type);\n }\n\n /**\n * Run each emitted Cmd. A returned Msg is enqueued onto the tail, never\n * dispatched re-entrantly; a returned list enqueues each of its Msgs in\n * order, before the next Cmd's handler runs (#324). The first error stops\n * further handlers in this transition.\n *\n * THIS LOOP IS SERIAL BY RULING, NOT BY OVERSIGHT (ADR 0018). It runs a\n * transition's Cmds one at a time and never interleaves two handlers, so the\n * Msgs it enqueues fold in Cmd-EMISSION order and a replayed log reproduces\n * the same fold — invariant 2's serializability. Overlap in time belongs\n * INSIDE one Cmd's handler, never across the Cmds of one transition.\n *\n * Once `stop()` has given up on this step, no further handler starts; what\n * the stuck one returned is refused at the gate like any other late Msg.\n */\n async function runCmds(cmds: readonly C[]): Promise<void> {\n for (const cmd of cmds) {\n if (gate === \"abandoned\") return;\n const returned = await interpret(cmd, dispatchUnawaited);\n for (const follow of followUps<M>(returned)) dispatchUnawaited(follow);\n }\n }\n\n // Install State, then save → reconcile subs → tell `subscribe` listeners →\n // run Cmds → commit callbacks. Save-before-effects is the hard ordering; tests\n // pin it. Boot and every transition end here; boot passes no Msg.\n //\n // A listener hears here, once per transition, because it is the view: it\n // reads the new State while that State's Cmds run and never waits on them, as\n // in Elm (#695). A failed save returns before this point, so a listener never\n // hears unsaved State.\n //\n // The commit callbacks run in a `finally`: a Cmd handler that throws still\n // rejects the dispatch with its own error, but the State it left installed\n // and saved still reaches every observer, `on` handler, `done()` waiter and\n // the telemetry sink (#311). Those stay after the Cmds: `driveToDone` settles\n // on `observe`, so the Cmds of the State it settles on have finished by then.\n //\n // No store call starts once `stop()` has given up (ADR 0025). A step never\n // gets here abandoned, because the queued step in `enqueueDispatch` drops it\n // first; boot does, when `stop()` gave up while `store.load()` was pending and\n // the load returns afterwards. That boot installs the loaded State, so\n // `getState()` stays total, and saves nothing: the store may already belong\n // to a newer run. It starts no Sub and no handler either, as the gate rules.\n async function commit(\n next: S,\n msg: M | undefined,\n cmds: readonly C[],\n ): Promise<void> {\n state = next;\n saveStatus = { type: \"pending\" };\n if (store && gate !== \"abandoned\") {\n try {\n await store.save(next);\n saveStatus = { type: \"saved\" };\n } catch (error) {\n saveStatus = { type: \"failed\", error };\n for (const ext of extensions) ext.saveFailed?.(next, error);\n throw error;\n }\n }\n reconcileSubs();\n installed = next;\n for (const callback of installedCallbacks) callback();\n try {\n await runCmds(cmds);\n } finally {\n for (const callback of commitCallbacks) callback(msg, next);\n }\n }\n\n // One full transition. A `null` from the fold is no transition at all, and\n // it RESOLVES the dispatch. A throw from the fold rejects it.\n async function stepDispatch(msg: M): Promise<void> {\n if (state === undefined) {\n throw new Error(\"@demlik/tea: runtime not booted\");\n }\n const transition = update(state, msg);\n if (transition === null) return;\n await commit(transition[0], msg, transition[1]);\n }\n\n // With NO store there is nothing to read, so `init` runs IMMEDIATELY and\n // `getState()` is observable synchronously (what `useSyncExternalStore`\n // consumers need to render the first commit without a flicker); an `init`\n // throw leaves `run` itself. With a store, the read is the first thing boot\n // awaits; saved state it cannot restore rejects `ready` with a\n // `StoreRefusedError` before `init` runs, so nothing is saved over it.\n const storeless: Transition<S, C> | null =\n store === undefined ? config.init(null, config.ctx) : null;\n if (storeless !== null) state = storeless[0];\n\n async function boot(): Promise<void> {\n // Boundary parse (invariant 8): `store.load()` returns `unknown`;\n // `store.migrate(raw)` is the required parse — `S` on a recognized shape,\n // `null` when nothing was saved (boots fresh), a refusal otherwise.\n const [initial, cmds] =\n storeless ?? config.init(await restore(store as Store<S>), config.ctx);\n await commit(initial, undefined, cmds);\n }\n\n /**\n * Enqueue a dispatch on the tail — the single gate re-entrant handler and\n * runner calls also go through. Rejects when the gate is not open (with a\n * `DispatchDiscardedError` while `stop()` drains, a `RuntimeStoppedError`\n * once it has), boot failed, or the fold / save / sub start / handler throws.\n *\n * `origin` separates the two refusals an abandoned run makes. A Msg from the\n * loop's own producers is the late work `stop()` gave up on, so it is a\n * discard; a caller's dispatch is still a retired runtime being used. A Msg\n * accepted before `stop()` gave up is dropped when its turn comes, unfolded\n * and unsaved: a discard for a caller, and no word at all for a Msg with no\n * caller, since the notice already counted it (`enqueueStep`).\n */\n function enqueueDispatch(\n msg: M,\n origin: \"caller\" | \"loop\" = \"caller\",\n ): Promise<void> {\n if (gate !== \"open\") {\n return Promise.reject(\n gate === \"draining\" || (gate === \"abandoned\" && origin === \"loop\")\n ? new DispatchDiscardedError(msg.type)\n : new RuntimeStoppedError(msg.type),\n );\n }\n return enqueueStep(msg, origin, stepDispatch);\n }\n\n /**\n * Put `step` on the tail, past the gate: the caller has already checked it.\n *\n * Every Msg dropped at its turn was on the line when `stop()` gave up, so the\n * `DrainTimeoutNotice` has already counted it. A caller holds a promise that\n * must settle, so it gets a `DispatchDiscardedError`. A Msg with no caller\n * goes without a word: an error here would reach the sink and count it twice.\n */\n function enqueueStep(\n msg: M,\n origin: \"caller\" | \"loop\" | \"tell\",\n step: (msg: M) => Promise<void> | undefined,\n ): Promise<void> {\n line.joined(msg.type);\n const next = tail.then(() => {\n line.reachedTurn(msg.type);\n if (gate === \"abandoned\") {\n if (origin === \"caller\") throw new DispatchDiscardedError(msg.type);\n return;\n }\n if (bootError !== null) throw bootError;\n return step(msg);\n });\n // Swallow the rejection on the tail so one failing dispatch does NOT poison\n // every later one. The original `next` still rejects for the caller.\n tail = next.catch(() => {});\n return next;\n }\n\n /**\n * Put a Msg on the tail and answer at once, with no caller left to reject at.\n * The tail is extended synchronously, so two posts from one sender fold in\n * the order they were made. Whatever the step ends with goes to this run's\n * own sink, as for any Msg with no caller. The one exception is a cell that\n * is gone by the Msg's turn: the Msg is dropped there, before any fold, so\n * nothing is raised and the default sink has nothing to rethrow. A `stop()`\n * that gave up counts the Msg in its notice instead (`enqueueStep`).\n */\n function postMsg(msg: M, takes: (state: S) => boolean): TellAnswer {\n if (gate !== \"open\") return \"stopped\";\n if (!takes(state as S)) return \"no_cell\";\n enqueueStep(msg, \"tell\", (queued) =>\n takes(state as S) ? stepDispatch(queued) : undefined,\n ).catch(reportUndelivered);\n return \"queued\";\n }\n\n /**\n * Drain the serial tail to quiescence. Every follow-up reassigns `tail`\n * SYNCHRONOUSLY before its parent step resolves, so awaiting the current\n * `tail` and re-reading it catches every transitively enqueued follow-up.\n * Bounded by `idleCap` — on cap REJECT with `QuiescenceTimeoutError`, keeping\n * a livelock distinguishable from quiescence (invariant 6).\n */\n async function drainToQuiescence(): Promise<void> {\n for (let i = 0; i < idleCap; i++) {\n const observed = tail;\n await observed;\n if (tail === observed) return;\n }\n throw new QuiescenceTimeoutError(idleCap);\n }\n\n /**\n * The public `dispatch`: await the Msg's OWN transition first (so its own\n * failure surfaces before any drain), then — unless `{ settle: \"once\" }` —\n * drain the transitive follow-up chain.\n */\n async function dispatchToQuiescence(\n msg: M,\n opts?: { readonly settle?: DispatchSettle },\n ): Promise<void> {\n await enqueueDispatch(msg);\n if (opts?.settle === \"once\") return;\n await drainToQuiescence();\n }\n\n // `bootPromise` is the un-swallowed promise `ready` chains off (so callers\n // see the boot error directly); `tail` gets the swallowed branch so a failed\n // boot does not poison every later dispatch's chain (each surfaces\n // `bootError` in `enqueueDispatch`).\n const bootPromise = boot();\n tail = bootPromise.catch((err) => {\n bootError = err;\n });\n\n async function teardown(): Promise<void> {\n // Open the discard window unless the gate is already past it (a halt):\n // the gate only advances.\n if (gate === \"open\") gate = \"draining\";\n // Report BEFORE the drain: a drained tail leaves nothing in flight, and\n // the fact worth surfacing is what was outstanding at the moment the\n // host let go (#365).\n if (inFlight.size > 0) {\n report(new RuntimeDiscardedError(inFlight.size), \"discard\");\n }\n // Drain in-flight work, up to `disposeTimeoutMs`. The gate refuses the\n // follow-ups those handlers return, so the tail ends once each handler\n // has returned; a handler that never returns is what the limit is for\n // (ADR 0025). Either way this resolves, and the teardown below runs.\n await drainTail();\n lastStateReached();\n stopSubs([...subRegistry.keys()]);\n // …then WAIT for the teardown still settling, up to the same limit (see\n // `drainDisposals`).\n await drainDisposals();\n // Flush final State. A save throw does not reject `stop()` (contract:\n // resolves regardless) but IS loss of the last write, so it reaches the\n // sink (invariant 6).\n if (store && state !== undefined && bootError === null) {\n try {\n await store.save(state);\n } catch (error) {\n report(error, \"stop-save\");\n }\n }\n }\n\n // The teardown runs once. A second `stop()` — a host calling it again, or the\n // Effect engine's Scope closing after the handle's own `stop()` — gets the\n // first one's promise, so it reports nothing twice and, above all, runs no\n // second final save: that would be a store call after `stop()` has returned\n // (ADR 0025), over a store that may by then belong to a newer run.\n let stopped: Promise<void> | undefined;\n\n const core: LoopHandle<S, M> = {\n [liveWork]: () => ({ subs: subRegistry.size, cmds: inFlight.size }),\n [post]: postMsg,\n [installedState]: () => installed,\n dispatch: dispatchToQuiescence,\n dispatchOnce: (msg) => enqueueDispatch(msg),\n getState(): S {\n // TOTAL. A booted handle is only obtainable by awaiting `ready`, which\n // resolves AFTER boot set `state`. That holds for a run `stop()` gave up\n // on mid-load too: its late boot installs the loaded State (`commit`).\n return state as S;\n },\n // A failed boot never hands out a handle. The forward reference resolves\n // at `.then` time, after `handle` below is built.\n ready: bootPromise.then(() => handle),\n idle: drainToQuiescence,\n stop(): Promise<void> {\n stopped ??= teardown();\n return stopped;\n },\n };\n const handle: LoopHandle<S, M> = Object.assign(\n core,\n ...extensions.map((ext) => ext.handle ?? {}),\n );\n return handle;\n}\n","/**\n * The one `tell` both engines' helpers call (ADR 0029, item 5), so what each\n * answer means cannot drift between them. It reads the machine's accepted Msgs\n * for the loop, which holds the gate and the State but not the cell table.\n */\n\nimport { acceptedTypes } from \"../../pure/core\";\nimport { type Postable, post, type TellAnswer } from \"./loop\";\n\n/**\n * Hand `msg` to the run behind `runtime`. Both `tell`s ask for a type that carries\n * the `post` symbol, so a handle that did not come from `run` reaches this only\n * through a cast. It is refused here, loudly: it has no line to put a Msg on.\n */\nexport function tellRun<S, M extends { type: string }>(\n machine: Parameters<typeof acceptedTypes>[0],\n runtime: object,\n msg: M,\n): TellAnswer {\n const postable = (runtime as Partial<Postable<S, M>>)[post];\n if (postable === undefined) {\n throw new Error(\n \"@demlik/tea: `tell` needs a run handle from `run`; this object has no line to post to.\",\n );\n }\n return postable(msg, (state) =>\n acceptedTypes(machine, state).includes(msg.type),\n );\n}\n"]}