@demlik/tea 0.1.0 → 0.1.1

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 (238) hide show
  1. package/LICENSE +21 -0
  2. package/dist/agent/index.d.ts +907 -0
  3. package/dist/agent/index.js +20 -0
  4. package/dist/agent/index.js.map +1 -0
  5. package/dist/authed-call/index.d.ts +119 -0
  6. package/dist/authed-call/index.js +148 -0
  7. package/dist/authed-call/index.js.map +1 -0
  8. package/dist/await-terminal/index.d.ts +99 -0
  9. package/dist/await-terminal/index.js +75 -0
  10. package/dist/await-terminal/index.js.map +1 -0
  11. package/dist/batch-window/index.d.ts +259 -0
  12. package/dist/batch-window/index.js +43 -0
  13. package/dist/batch-window/index.js.map +1 -0
  14. package/dist/cache/index.d.ts +2 -2
  15. package/dist/cache/index.js +2 -50
  16. package/dist/cache/index.js.map +1 -1
  17. package/dist/{chunk-CMG2KMWX.js → chunk-2WZHS4P2.js} +220 -177
  18. package/dist/chunk-2WZHS4P2.js.map +1 -0
  19. package/dist/chunk-2YOE5PIS.js +45 -0
  20. package/dist/chunk-2YOE5PIS.js.map +1 -0
  21. package/dist/chunk-3545BOYL.js +65 -0
  22. package/dist/chunk-3545BOYL.js.map +1 -0
  23. package/dist/chunk-5VNWZADP.js +49 -0
  24. package/dist/chunk-5VNWZADP.js.map +1 -0
  25. package/dist/{chunk-ZRUST6BK.js → chunk-7FYQGDMF.js} +2 -2
  26. package/dist/chunk-7FYQGDMF.js.map +1 -0
  27. package/dist/chunk-7VEG4KHB.js +202 -0
  28. package/dist/chunk-7VEG4KHB.js.map +1 -0
  29. package/dist/{chunk-PYBQREFU.js → chunk-A56GNPI7.js} +20 -6
  30. package/dist/chunk-A56GNPI7.js.map +1 -0
  31. package/dist/chunk-BNJYE4AU.js +157 -0
  32. package/dist/chunk-BNJYE4AU.js.map +1 -0
  33. package/dist/chunk-BYQUGDD3.js +16 -0
  34. package/dist/chunk-BYQUGDD3.js.map +1 -0
  35. package/dist/chunk-D5EQDLDV.js +186 -0
  36. package/dist/chunk-D5EQDLDV.js.map +1 -0
  37. package/dist/chunk-EDGDR5XX.js +60 -0
  38. package/dist/chunk-EDGDR5XX.js.map +1 -0
  39. package/dist/chunk-EIZJTYKR.js +93 -0
  40. package/dist/chunk-EIZJTYKR.js.map +1 -0
  41. package/dist/chunk-EQRUAP2A.js +56 -0
  42. package/dist/chunk-EQRUAP2A.js.map +1 -0
  43. package/dist/chunk-FH5RUIIL.js +61 -0
  44. package/dist/chunk-FH5RUIIL.js.map +1 -0
  45. package/dist/chunk-HJO6VNQI.js +15 -0
  46. package/dist/chunk-HJO6VNQI.js.map +1 -0
  47. package/dist/chunk-IFBQJYGI.js +132 -0
  48. package/dist/chunk-IFBQJYGI.js.map +1 -0
  49. package/dist/chunk-IN33Q4VJ.js +52 -0
  50. package/dist/chunk-IN33Q4VJ.js.map +1 -0
  51. package/dist/chunk-JELSZN25.js +47 -0
  52. package/dist/chunk-JELSZN25.js.map +1 -0
  53. package/dist/chunk-K575PT4S.js +58 -0
  54. package/dist/chunk-K575PT4S.js.map +1 -0
  55. package/dist/chunk-LEC4U6CX.js +91 -0
  56. package/dist/chunk-LEC4U6CX.js.map +1 -0
  57. package/dist/{chunk-Y5JM3UUH.js → chunk-NSDFA6M6.js} +75 -3
  58. package/dist/chunk-NSDFA6M6.js.map +1 -0
  59. package/dist/chunk-QGZW4W3E.js +20 -0
  60. package/dist/chunk-QGZW4W3E.js.map +1 -0
  61. package/dist/{chunk-PYOCUBQJ.js → chunk-RGTPPF4U.js} +22 -15
  62. package/dist/chunk-RGTPPF4U.js.map +1 -0
  63. package/dist/chunk-RVJFGRGK.js +222 -0
  64. package/dist/chunk-RVJFGRGK.js.map +1 -0
  65. package/dist/chunk-RVU3RBE6.js +31 -0
  66. package/dist/chunk-RVU3RBE6.js.map +1 -0
  67. package/dist/chunk-SC3FAFZN.js +53 -0
  68. package/dist/chunk-SC3FAFZN.js.map +1 -0
  69. package/dist/chunk-SINXE47Y.js +55 -0
  70. package/dist/chunk-SINXE47Y.js.map +1 -0
  71. package/dist/chunk-TAO4YOLO.js +87 -0
  72. package/dist/chunk-TAO4YOLO.js.map +1 -0
  73. package/dist/chunk-TC4HWABA.js +506 -0
  74. package/dist/chunk-TC4HWABA.js.map +1 -0
  75. package/dist/chunk-TGG26XMK.js +52 -0
  76. package/dist/chunk-TGG26XMK.js.map +1 -0
  77. package/dist/chunk-UV6UWGEO.js +147 -0
  78. package/dist/chunk-UV6UWGEO.js.map +1 -0
  79. package/dist/chunk-UYWUVEP6.js +158 -0
  80. package/dist/chunk-UYWUVEP6.js.map +1 -0
  81. package/dist/chunk-WUV3PSCZ.js +54 -0
  82. package/dist/chunk-WUV3PSCZ.js.map +1 -0
  83. package/dist/chunk-XRXKH7KA.js +268 -0
  84. package/dist/chunk-XRXKH7KA.js.map +1 -0
  85. package/dist/circuit-breaker/index.js +1 -51
  86. package/dist/circuit-breaker/index.js.map +1 -1
  87. package/dist/core-CymXZCeY.d.ts +178 -0
  88. package/dist/deadline/index.d.ts +24 -5
  89. package/dist/deadline/index.js +4 -18
  90. package/dist/deadline/index.js.map +1 -1
  91. package/dist/debounce/index.js +1 -54
  92. package/dist/debounce/index.js.map +1 -1
  93. package/dist/devtools/index.d.ts +1 -1
  94. package/dist/devtools/index.js +18 -6
  95. package/dist/devtools/index.js.map +1 -1
  96. package/dist/do/index.d.ts +1732 -92
  97. package/dist/do/index.js +677 -31
  98. package/dist/do/index.js.map +1 -1
  99. package/dist/durable-effects-2MDxDs45.d.ts +180 -0
  100. package/dist/extension/index.d.ts +31 -13
  101. package/dist/extension/index.js +5 -3
  102. package/dist/extension/index.js.map +1 -1
  103. package/dist/extension/react.js +8 -3
  104. package/dist/extension/react.js.map +1 -1
  105. package/dist/extension/subs/index.d.ts +2 -2
  106. package/dist/extension/subs/index.js.map +1 -1
  107. package/dist/extension/test-utils.js +11 -3
  108. package/dist/extension/test-utils.js.map +1 -1
  109. package/dist/fan-out/index.d.ts +239 -0
  110. package/dist/fan-out/index.js +5 -0
  111. package/dist/fan-out/index.js.map +1 -0
  112. package/dist/idempotency/adapter.d.ts +62 -0
  113. package/dist/idempotency/adapter.js +4 -0
  114. package/dist/idempotency/adapter.js.map +1 -0
  115. package/dist/idempotency/index.js +1 -56
  116. package/dist/idempotency/index.js.map +1 -1
  117. package/dist/idempotent-intake/index.d.ts +186 -0
  118. package/dist/idempotent-intake/index.js +7 -0
  119. package/dist/idempotent-intake/index.js.map +1 -0
  120. package/dist/index.d.ts +341 -191
  121. package/dist/index.js +2 -1
  122. package/dist/llm-call/index.d.ts +309 -0
  123. package/dist/llm-call/index.js +14 -0
  124. package/dist/llm-call/index.js.map +1 -0
  125. package/dist/machine-viz/index.d.ts +1 -1
  126. package/dist/machine-viz/index.js +1 -1
  127. package/dist/machine-viz/index.js.map +1 -1
  128. package/dist/mem/index.d.ts +1 -0
  129. package/dist/monitored-run/index.d.ts +253 -0
  130. package/dist/monitored-run/index.js +10 -0
  131. package/dist/monitored-run/index.js.map +1 -0
  132. package/dist/node/index.d.ts +5 -5
  133. package/dist/node/index.js.map +1 -1
  134. package/dist/paginated-walk/index.d.ts +234 -0
  135. package/dist/paginated-walk/index.js +15 -0
  136. package/dist/paginated-walk/index.js.map +1 -0
  137. package/dist/paginator/index.d.ts +271 -0
  138. package/dist/paginator/index.js +3 -0
  139. package/dist/paginator/index.js.map +1 -0
  140. package/dist/parity/index.d.ts +86 -0
  141. package/dist/parity/index.js +77 -0
  142. package/dist/parity/index.js.map +1 -0
  143. package/dist/pbt/arbitraries/index.d.ts +1 -1
  144. package/dist/pbt/arbitraries/index.js +1 -1
  145. package/dist/pbt/index.d.ts +1 -1
  146. package/dist/pbt/index.js +5 -4
  147. package/dist/pbt/index.js.map +1 -1
  148. package/dist/pbt/runners/index.d.ts +1 -1
  149. package/dist/pbt/runners/index.js +3 -2
  150. package/dist/poller/index.d.ts +288 -0
  151. package/dist/poller/index.js +79 -0
  152. package/dist/poller/index.js.map +1 -0
  153. package/dist/prediction/index.d.ts +109 -0
  154. package/dist/prediction/index.js +4 -0
  155. package/dist/prediction/index.js.map +1 -0
  156. package/dist/protocol-B5YmJr0d.d.ts +45 -0
  157. package/dist/pure/index.d.ts +2 -0
  158. package/dist/pure/index.js +4 -0
  159. package/dist/pure/index.js.map +1 -0
  160. package/dist/raft/demo.d.ts +105 -0
  161. package/dist/raft/demo.js +172 -0
  162. package/dist/raft/demo.js.map +1 -0
  163. package/dist/raft/index.d.ts +548 -0
  164. package/dist/raft/index.js +7 -0
  165. package/dist/raft/index.js.map +1 -0
  166. package/dist/raft/sim.d.ts +222 -0
  167. package/dist/raft/sim.js +8 -0
  168. package/dist/raft/sim.js.map +1 -0
  169. package/dist/raft/timeline.d.ts +46 -0
  170. package/dist/raft/timeline.js +62 -0
  171. package/dist/raft/timeline.js.map +1 -0
  172. package/dist/rate-limit/index.js +1 -43
  173. package/dist/rate-limit/index.js.map +1 -1
  174. package/dist/react/index.d.ts +2 -1
  175. package/dist/react/index.js +39 -20
  176. package/dist/react/index.js.map +1 -1
  177. package/dist/reconciler/index.d.ts +192 -0
  178. package/dist/reconciler/index.js +166 -0
  179. package/dist/reconciler/index.js.map +1 -0
  180. package/dist/recorder/index.d.ts +7 -3
  181. package/dist/recorder/index.js +1 -155
  182. package/dist/recorder/index.js.map +1 -1
  183. package/dist/resilient-call/index.d.ts +251 -0
  184. package/dist/resilient-call/index.js +13 -0
  185. package/dist/resilient-call/index.js.map +1 -0
  186. package/dist/retry-backoff/index.d.ts +42 -5
  187. package/dist/retry-backoff/index.js +1 -35
  188. package/dist/retry-backoff/index.js.map +1 -1
  189. package/dist/saga/index.d.ts +225 -0
  190. package/dist/saga/index.js +150 -0
  191. package/dist/saga/index.js.map +1 -0
  192. package/dist/snapshot/index.d.ts +334 -0
  193. package/dist/snapshot/index.js +5 -0
  194. package/dist/snapshot/index.js.map +1 -0
  195. package/dist/subs/index.d.ts +48 -10
  196. package/dist/subs/index.js +1 -1
  197. package/dist/testing/index.d.ts +107 -2
  198. package/dist/testing/index.js +204 -6
  199. package/dist/testing/index.js.map +1 -1
  200. package/dist/throttle/index.js +1 -52
  201. package/dist/throttle/index.js.map +1 -1
  202. package/dist/throttled-input/index.d.ts +372 -0
  203. package/dist/throttled-input/index.js +88 -0
  204. package/dist/throttled-input/index.js.map +1 -0
  205. package/dist/token-refresh/index.d.ts +215 -0
  206. package/dist/token-refresh/index.js +6 -0
  207. package/dist/token-refresh/index.js.map +1 -0
  208. package/dist/trace-replay/index.d.ts +11 -2
  209. package/dist/trace-replay/index.js +3 -55
  210. package/dist/trace-replay/index.js.map +1 -1
  211. package/dist/{types-CwBC5wMn.d.ts → types-N9LZbdnr.d.ts} +1 -1
  212. package/dist/with-deadline/index.d.ts +196 -0
  213. package/dist/with-deadline/index.js +130 -0
  214. package/dist/with-deadline/index.js.map +1 -0
  215. package/dist/with-resilience/index.d.ts +205 -0
  216. package/dist/with-resilience/index.js +274 -0
  217. package/dist/with-resilience/index.js.map +1 -0
  218. package/dist/with-telemetry/index.d.ts +149 -0
  219. package/dist/with-telemetry/index.js +95 -0
  220. package/dist/with-telemetry/index.js.map +1 -0
  221. package/dist/work-queue/adapter.d.ts +82 -0
  222. package/dist/work-queue/adapter.js +4 -0
  223. package/dist/work-queue/adapter.js.map +1 -0
  224. package/dist/work-queue/index.d.ts +25 -11
  225. package/dist/work-queue/index.js +1 -46
  226. package/dist/work-queue/index.js.map +1 -1
  227. package/dist/work-queue/ops.d.ts +69 -0
  228. package/dist/work-queue/ops.js +3 -0
  229. package/dist/work-queue/ops.js.map +1 -0
  230. package/dist/workflow/index.d.ts +444 -0
  231. package/dist/workflow/index.js +225 -0
  232. package/dist/workflow/index.js.map +1 -0
  233. package/package.json +162 -25
  234. package/dist/chunk-CMG2KMWX.js.map +0 -1
  235. package/dist/chunk-PYBQREFU.js.map +0 -1
  236. package/dist/chunk-PYOCUBQJ.js.map +0 -1
  237. package/dist/chunk-Y5JM3UUH.js.map +0 -1
  238. package/dist/chunk-ZRUST6BK.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":["state"],"mappings":"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","file":"chunk-CMG2KMWX.js","sourcesContent":["/**\n * @demlik/tea — TEA-faithful state machine substrate.\n *\n * This module exposes the full Locked API Surface from the PRD as types and\n * implements `defineMachine` (identity, task 1), `run` (task 2), and\n * `replay` + `tryInterpret` (task 3).\n *\n * `replay` is the pure unit-test tool: it composes `init` + `update` only,\n * never touches `Store`, never calls `interpret`, never starts subscriptions.\n *\n * `tryInterpret` is Railway sugar — wraps a fallible async function and routes\n * Ok/Err to two Msg constructors via better-result.\n */\n\nimport { Result } from \"better-result\";\n\n// === Dev-mode invariant enforcement ===\n//\n// Three runtime guards that catch TEA invariant violations during development.\n// Dead-code-eliminated in production by any bundler that replaces\n// `import.meta.env.DEV` (Vite) or `process.env.NODE_ENV` (webpack/esbuild).\n//\n// 1. deepFreeze(state) before passing to update — catches mutation\n// 2. Thenable check on return value — catches async update\n// 3. Walk cmd fields for function types — catches closures-as-data\n//\n// These enforce invariants 1 and 2 at runtime where the type system cannot.\nconst __DEV__: boolean = (() => {\n try {\n const meta = import.meta as unknown as { env?: { DEV?: boolean } };\n if (meta.env?.DEV !== undefined) {\n return meta.env.DEV;\n }\n } catch {\n // import.meta not available (Node CJS, service workers)\n }\n try {\n return typeof process !== \"undefined\" && process.env?.NODE_ENV !== \"production\";\n } catch {\n return false;\n }\n})();\n\nfunction deepFreeze<T>(obj: T, seen?: WeakSet<object>): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const proto = Object.getPrototypeOf(obj);\n if (proto !== Object.prototype && proto !== Array.prototype && proto !== null) return obj;\n const visited = seen ?? new WeakSet();\n if (visited.has(obj as object)) return obj;\n visited.add(obj as object);\n Object.freeze(obj);\n for (const v of Object.values(obj as Record<string, unknown>)) deepFreeze(v, visited);\n return obj;\n}\n\nfunction hasFunctionValues(obj: unknown): string | null {\n if (obj === null || typeof obj !== \"object\") return null;\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n if (typeof value === \"function\") return key;\n }\n return null;\n}\n\nfunction assertPureResult(result: unknown, msgType: string): void {\n if (result && typeof (result as { then?: unknown }).then === \"function\") {\n throw new Error(\n `@demlik/tea: update cell \"${msgType}\" returned a Promise. ` +\n `update must be synchronous. Move async work to interpret.`,\n );\n }\n if (!Array.isArray(result) || result.length < 2 || !Array.isArray(result[1])) {\n throw new Error(\n `@demlik/tea: update cell \"${msgType}\" returned a non-tuple. ` +\n `Expected [State, Cmd[]], got ${typeof result}.`,\n );\n }\n const cmds = result[1] as readonly { type: string }[];\n for (const cmd of cmds) {\n const fnField = hasFunctionValues(cmd);\n if (fnField !== null) {\n throw new Error(\n `@demlik/tea: Cmd \"${cmd.type}\" has function field \"${fnField}\". ` +\n `Cmds must be plain data, not closures.`,\n );\n }\n }\n}\n\n// === Cmd: tagged-union, one-shot effect ===\nexport type Cmd<T extends string = string> = { type: T };\n\n// === Reducer<S, M, C>: record-of-handlers form of `update` ===\n//\n// Flat dispatch table keyed by `Msg.type`. Each cell is a pure transition for\n// a single Msg variant, narrowed via `Extract<M, { type: K }>`. The mapped type\n// is *load-bearing*: adding a Msg variant without a matching key in the\n// Reducer record is a compile error — no `absurd()` helper at call sites, no\n// silent fall-through, no default branch hiding impurity.\n//\n// Strengthens invariant 2 (pure transitions — the record form has no\n// fall-through default to hide impurity behind) and invariant 7 (identity is\n// explicit — the Msg variant set is load-bearing at the type level).\n//\n// `defineMachine` accepts the Reducer record form via overload. The runtime\n// dispatches via `update[msg.type](state, msg)`.\nexport type Reducer<S, M extends { type: string }, C extends Cmd> = {\n [K in M[\"type\"]]: (state: S, msg: Extract<M, { type: K }>) => readonly [S, readonly C[]];\n};\n\n// === Transitions<S, M, C>: table form of `update` for state-machine-shaped machines ===\n//\n// When `State` is itself a discriminated union (`State.type` is the active\n// phase), the table form makes every (state.type × msg.type) cell explicit at\n// the type level. Missing a cell — e.g. forgetting how `phase: \"settling\"`\n// handles `msg.type: \"tab_stepped\"` — fails to compile. No nested switch, no\n// fall-through, no `_ -> (state, [])` default branch hiding regressions.\n//\n// Each cell receives the narrowed `State` for its phase and the narrowed\n// `Msg` for its variant via two `Extract` lookups. Phantom-narrow at the\n// type level; pure data at runtime.\n//\n// `defineMachine` accepts a third `update` form for `Transitions<S, M, C>` —\n// the runtime dispatches via `update[state.type][msg.type](state, msg)`.\n//\n// Strengthens invariant 2 (table form has no fall-through default),\n// invariant 6 (runtime walks the table predictably, no hidden dispatch\n// fallback), and invariant 7 (both state.type and msg.type are load-bearing\n// at the type level).\nexport type Transitions<S extends { type: string }, M extends { type: string }, C extends Cmd> = {\n [P in S[\"type\"]]: {\n [K in M[\"type\"]]: (\n state: Extract<S, { type: P }>,\n msg: Extract<M, { type: K }>,\n ) => readonly [S, readonly C[]];\n };\n};\n\n// === absurd: compile-time exhaustiveness assertion ===\n//\n// Use in default branches of switches over discriminated unions where every\n// case should be handled explicitly. TS narrows the operand to `never` only\n// if every variant has been covered upstream; adding a new variant produces\n// a compile error at the `absurd(x)` site.\n//\n// The substrate's `Reducer<S, M, C>` and `Transitions<S, M, C>` forms cover\n// the reducer-side exhaustiveness with mapped types — no `absurd` needed\n// there. This helper exists for the OTHER boundaries where a discriminated\n// union is narrowed by hand: Sub handlers mapping a wire-protocol\n// discriminant to flat Msg variants, message-bridge dispatchers, port\n// fanouts, etc. Without it, consumers redeclare the same 3-line helper\n// across files; with it, the substrate owns the one canonical version.\n//\n// Strengthens invariant 7 (identity is explicit).\n/**\n * Compile-time exhaustiveness assertion. Use in default branches of\n * switches over discriminated unions where every case should be handled\n * explicitly — TS narrows the type to `never` only if every variant has\n * been covered. Strengthens invariant 7 (identity is explicit).\n *\n * @example\n * switch (msg.type) {\n * case \"a\": return ...;\n * case \"b\": return ...;\n * default: return absurd(msg); // compile error if a \"c\" is added\n * }\n */\nexport function absurd(x: never): never {\n throw new Error(`unreachable: ${JSON.stringify(x)}`);\n}\n\n// === noop: the explicit \"this cell ignores this msg\" helper ===\n//\n// Elm's fallback for unhandled (state × msg) pairs is implicit:\n// `_ -> (model, Cmd.none)` in a case expression. We make it explicit because\n// implicit no-ops are silent regressions waiting to happen — a future Msg\n// variant that *should* be handled gets swallowed by the wildcard.\n//\n// Naming the no-op forces the author to *decide*: \"this cell does nothing\"\n// is a real decision, distinct from \"I forgot to write this cell.\" The\n// mapped type rejects the second case; `noop` names the first.\n//\n// Assignable to any Transitions cell regardless of S/M/C concrete types\n// (the type parameters are free) — drop it into any cell without per-type\n// casts.\nexport const noop = <S, M, C extends Cmd>(state: S, _msg: M): readonly [S, readonly C[]] => [\n state,\n [],\n];\n\n// === Cmd: namespace for conditional Cmd emission ===\n//\n// Two recurring frictions in reducer cells:\n//\n// 1. \"Emit cmd X only when condition Y holds.\"\n// Hand-rolled: `cond ? [{ type: \"x\", ... }] : []`\n// With Cmd: `Cmd.when(cond, { type: \"x\", ... })`\n//\n// 2. \"Emit cmd X carrying a value that may be undefined; if undefined,\n// emit nothing.\"\n// Hand-rolled: `value !== undefined ? [{ type: \"x\", value }] : []`\n// With Cmd: `Cmd.whenDefined(value, (v) => ({ type: \"x\", value: v }))`\n//\n// Both forms return `readonly C[]` so they spread cleanly into the cmds\n// array returned by a Transitions cell:\n//\n// return [next, [\n// ...Cmd.when(state.tabId !== undefined, { type: \"detach_debugger\", tabId: state.tabId }),\n// ...Cmd.whenDefined(state.queueItemId, (id) => ({\n// type: \"queue:complete\", queueItemId: id, status: \"done\",\n// })),\n// ]];\n//\n// `Cmd` as a value namespace coexists with `Cmd<T>` as a type — TypeScript's\n// declaration merging puts them in different name spaces (type vs. value).\n// The reader sees `Cmd<...>` in type position and `Cmd.when(...)` in\n// expression position; no ambiguity at use sites.\n//\n// Why a namespace and not top-level `when` / `whenDefined`: `when` at module\n// scope reads as a generic conditional, but these helpers are specifically\n// for *Cmd emission* (return `readonly C[]`, the cmds-array contract of\n// every Transitions cell). The namespace pins that intent at the call site.\nexport const Cmd = {\n /**\n * The empty Cmd array. Typed `readonly never[]` so it's assignable to any\n * `readonly C[]` for any `C extends Cmd`. Use in `init` returns and\n * Transitions cells that emit zero effects:\n *\n * init: (loaded) => [loaded ?? initial, Cmd.none],\n * tick: (state) => [state, Cmd.none],\n *\n * Elm's `Cmd.none` analogue. Cultural signal alongside the runtime help:\n * `[state, Cmd.none]` reads as intent (\"this transition emits nothing\"),\n * `[state, []]` reads as \"empty array of what\".\n *\n * Frozen at runtime so a downstream consumer can't `.push()` into the\n * shared reference.\n */\n none: Object.freeze([]) as readonly never[],\n\n /**\n * Flat concat of cmd arrays. Use when a cell composes effects from\n * multiple conditional sources:\n *\n * return [next, Cmd.batch(\n * Cmd.whenDefined(state.queueItemId, (id) => ({ type: \"complete\", id })),\n * Cmd.when(state.windowId !== undefined, { type: \"close_window\", windowId }),\n * [{ type: \"detach_debugger\", tabId: state.tabId }],\n * )];\n *\n * `<const C>` keeps inline cmd literals' discriminants narrow (same\n * rationale as `when` / `whenDefined` above).\n */\n batch: <const C extends Cmd>(...arrs: readonly (readonly C[])[]): readonly C[] => {\n const out: C[] = [];\n for (const arr of arrs) out.push(...arr);\n return out;\n },\n\n /**\n * Emit `cmd` wrapped in a single-element array iff `cond` is true.\n * Otherwise return the empty array. Spreads cleanly into the cmds array\n * returned by a Transitions cell.\n *\n * `const C` (TypeScript 5.0+) keeps the inferred `type:` discriminator\n * literal narrow when called with an object literal — so `Cmd.when(b,\n * { type: \"x\", ... })` infers as `{ type: \"x\", ... }`, not `{ type:\n * string, ... }`, and stays assignable to a discriminated-union arm\n * without `as const` at the call site.\n */\n when: <const C extends Cmd>(cond: boolean, cmd: C): readonly C[] => (cond ? [cmd] : []),\n\n /**\n * If `value` is defined, call `build(value)` and emit the resulting cmd\n * wrapped in a single-element array. If `value` is `undefined`, return\n * the empty array. `value` is narrowed to `T` inside `build`.\n *\n * `const C` on the return type keeps the callback's object-literal\n * `type:` field narrow (see `when` above for the full rationale).\n */\n whenDefined: <T, const C extends Cmd>(\n value: T | undefined,\n build: (value: T) => C,\n ): readonly C[] => (value !== undefined ? [build(value)] : []),\n} as const;\n\n// === SubId: branded string for Sub identity ===\n//\n// Subs are reconciled by id on every transition (canon §2.5, invariant 7).\n// Before branding, `Sub.id` was a raw `string` — a typo at one call site and a\n// different typo at another would silently produce two distinct subs that\n// looked identical to a human reader. The brand makes id construction explicit\n// (`subId(\"...\")`) and catches accidental string-where-SubId at the type\n// level.\n//\n// The brand is structural: `string & { __brand: \"SubId\" }`. The `subId(s)`\n// constructor is the ONE permitted cast in the substrate — every other call\n// site must go through it.\nexport type SubId = string & { readonly __brand: \"SubId\" };\n\n/**\n * Construct a `SubId` from a string. Use this everywhere a Sub literal is\n * built — including ids derived from runtime data (`subId(\\`ws:${auditId}\\`)`).\n * The brand is structural, not nominal, so dynamic ids are permitted; the\n * constructor's job is to make the identity decision explicit at every call\n * site so the type system can catch accidental raw-string drift.\n *\n * Strengthens invariant 7 (identity is explicit).\n */\nexport function subId(s: string): SubId {\n return s as SubId;\n}\n\n// === Sub: tagged-union, continuous source of msgs; stable id used for diff/reconcile ===\nexport type Sub<T extends string = string> = { id: SubId; type: T };\n\n// === Port<T>: typed escape hatch for \"data leaving the runtime\" ===\n//\n// A Port is a named, typed channel that Cmd handlers can `emit` to via the\n// augmented `ctx.emit(port, value)`. Subscribers attach via\n// `runtime.subscribePort(port, listener)` and receive every emitted value\n// synchronously (same fanout discipline as `runtime.observe`).\n//\n// Why Ports are a substrate primitive, distinct from State and Observe:\n// - **State** is \"the world as the program sees it.\" Folding outgoing\n// announcements into State turns ephemeral signals into persisted facts —\n// the classic anti-pattern that motivated this primitive.\n// - **Observe** sees every transition `(msg, state)`. That's the right channel\n// for devtools and logging, but it forces every consumer to filter the\n// entire firehose for a single signal.\n// - **Ports** are typed and selective: one channel per concept, subscribers\n// only see what the handler chose to emit. Mirrors Elm's outgoing-port\n// semantics — a typed declaration with `port` keyword whose values flow\n// out of the program to the host.\n//\n// Port identity is **by reference** — the returned object IS the identity.\n// Name is metadata for debugging. To prevent the \"two modules definePort the\n// same name expecting to share a channel, silently get distinct ports\" class\n// of bug, `definePort` asserts the name has not been seen in this process —\n// the symmetric runtime check to `SubId` (canon §2.12, invariant 7). Each\n// definePort call must use a unique name; if two modules need the same port,\n// one module exports it and the other imports it.\nexport interface Port<T> {\n readonly __brand: \"port\";\n readonly name: string;\n // Phantom field — never assigned, never read at runtime. Carries `T` for\n // inference at `ctx.emit` and `subscribePort` call sites. Using `?` so\n // `definePort` can produce a Port without constructing a value of `T`.\n readonly __t?: T;\n}\n\n// Module-level registry — process-scope, identity-by-name. The set is the\n// runtime witness that every port name in this process is unique. Cleared\n// only by the test-only `__resetPortRegistry()` helper.\nconst definedPortNames = new Set<string>();\n\n/**\n * Thrown by `definePort` when a name has already been registered in the\n * current process. Symmetric with the `Sub.id` collision assert in\n * `reconcileSubs` — both mechanize invariant 7 (identity is explicit) at the\n * runtime layer the type system cannot reach (string names compared at\n * runtime, not at compile time).\n */\nexport class PortNameCollisionError extends Error {\n override readonly name = \"PortNameCollisionError\";\n constructor(portName: string) {\n super(\n `definePort: a port named \"${portName}\" was already defined. ` +\n `Each definePort call must use a unique name. ` +\n `If two modules need the same port, export it from one module and import it.`,\n );\n }\n}\n\n/**\n * Test-only escape hatch — clears the module-level port-name registry so the\n * collision assert in `definePort` doesn't false-positive across vitest's\n * non-isolated runs. Production code MUST NOT call this; the registry is the\n * invariant-7 witness for the process lifetime.\n *\n * @internal test-only\n */\nexport function __resetPortRegistry(): void {\n definedPortNames.clear();\n}\n\n/**\n * Define a typed port. `name` is metadata (shown in devtools / debug logs);\n * port identity is by reference. Each definePort call must use a unique\n * `name` — a second call with the same name throws\n * `PortNameCollisionError`. If two modules need the same port, export it\n * from one module and import it from the other.\n *\n * Use ports for \"data leaving the runtime selectively\" — e.g. cursor\n * announcements, DOM-mutation broadcasts, telemetry events that should NOT be\n * folded into State and should NOT be observed by every transition listener.\n *\n * Strengthens invariant 7 (identity is explicit) — the runtime assert is the\n * Port-side counterpart of `SubId`'s id collision check in `reconcileSubs`.\n */\nexport function definePort<T>(name: string): Port<T> {\n // HMR skip — Vite / dev servers re-import the module on hot reload, which\n // would otherwise false-positive on every definePort across reloads. The\n // try/catch is load-bearing for service-worker bundles: Vite polyfills\n // `import.meta.url` via `document.baseURI`, which throws ReferenceError in\n // an MV3 service worker. A throw here would abort SW install (Chrome\n // surfaces it as \"registration failed status 15\"). Catching it = \"no HMR\n // detected\" = the collision check applies, which is the correct SW\n // behavior (SWs reload via chrome.runtime.reload, not module HMR).\n let isHmr = false;\n try {\n const meta = import.meta as { hot?: unknown };\n isHmr = meta?.hot !== undefined && meta?.hot !== null;\n } catch {\n isHmr = false;\n }\n if (!isHmr && definedPortNames.has(name)) {\n throw new PortNameCollisionError(name);\n }\n definedPortNames.add(name);\n return { __brand: \"port\", name } as Port<T>;\n}\n\n/**\n * Augmentation injected onto `ctx` inside Cmd handlers. Handlers receive\n * `ctx & PortEmitter` so they can call `ctx.emit(port, value)` synchronously.\n * Subscribers registered via `runtime.subscribePort(port, listener)` receive\n * the value immediately.\n *\n * Emitting to a port with no subscribers is a no-op (does not throw).\n */\nexport interface PortEmitter {\n emit<T>(port: Port<T>, value: T): void;\n}\n\n// === Interpret<M, C, Ctx>: record-of-handlers form of `interpret` ===\n//\n// Flat dispatch table keyed by `Cmd.type`. Each cell receives the narrowed Cmd\n// and the runtime-augmented Ctx (`Ctx & PortEmitter`) and resolves to a\n// follow-up Msg or `void` (fire-and-forget). The mapped type makes a missing\n// handler a compile error — `defineMachine` cannot accept the dictionary until\n// every Cmd variant has one.\n//\n// Hoisted out of `Machine.interpret` so consumers can type a free-standing\n// handler dictionary with `Interpret<MyMsg, MyCmd, MyCtx>` instead of\n// re-declaring the mapped type at every effects module.\n//\n// Strengthens invariant 2 (the record form has no fall-through default to\n// hide impurity behind) and invariant 7 (identity is explicit — the Cmd\n// variant set is load-bearing at the type level).\nexport type Interpret<M extends { type: string }, C extends Cmd, Ctx> = {\n [K in C[\"type\"]]: (cmd: Extract<C, { type: K }>, ctx: Ctx & PortEmitter) => Promise<M | void>;\n};\n\n// === Subscribe<M, U, Ctx>: record-of-handlers form of `subscribe` ===\n//\n// Flat dispatch table keyed by `Sub.type`. Each cell receives the narrowed\n// Sub, the Ctx, and a `dispatch` to fire follow-up Msgs. Returns a cleanup\n// function the substrate calls when the Sub is reconciled out (state\n// transitioned away). The mapped type guarantees every Sub variant has a\n// handler at the type level; runtime dispatch is a single property lookup.\n//\n// Hoisted out of `Machine.subscribe` so consumers can type a free-standing\n// handler dictionary with `Subscribe<MyMsg, MySub, MyCtx>` instead of\n// re-declaring the mapped type at every subs module.\n//\n// Strengthens invariant 7 (identity is explicit — the Sub variant set is\n// load-bearing at the type level).\nexport type Subscribe<M extends { type: string }, U extends Sub, Ctx> = {\n [K in U[\"type\"]]: (\n sub: Extract<U, { type: K }>,\n ctx: Ctx,\n dispatch: (msg: M) => void,\n ) => () => void;\n};\n\n// === Machine: pure data, host-agnostic ===\n//\n// `update` is stored as a union of the two record forms — the runtime branches\n// on shape at dispatch time. The public `defineMachine` overloads enforce one\n// shape per call so consumers never see the union themselves.\n//\n// - Reducer form: `Reducer<S, M, C>` — flat record keyed by Msg.type;\n// exhaustiveness enforced by the mapped type.\n// - Transitions form: `Transitions<S, M, C>` — 2D table keyed by\n// `state.type` then `msg.type`; only available when `S extends { type:\n// string }`. Conditional in the union so machines without a discriminated\n// State don't widen to include this branch.\n//\n// `M` is constrained to `{ type: string }` because both record forms require\n// a string discriminant.\n//\n// `interpret` is conditionally optional: when `C` is `Cmd<never>` the\n// interpret map is keyed by `never`, so the only valid value is `{}`. Forcing\n// every cmdless machine to write `interpret: {} as never` is ceremony for a\n// shape the type already pins. The tuple-wrap (`[C] extends [Cmd<never>]`)\n// disables distributive conditional so a real cmd union (e.g.\n// `Cmd<\"a\"> | Cmd<\"b\">`) doesn't degrade to optional just because one arm\n// happens to be `Cmd<never>`. Same trick the `update` field uses.\nexport type Machine<S, M extends { type: string }, C extends Cmd, U extends Sub, Ctx> = {\n /**\n * Boot the runtime. Called once by `run(...)` with whatever `Store.load()`\n * returned: `null` on fresh boot, the persisted state on rehydrate.\n *\n * **Contract:** when `loaded !== null`, init MUST return `[loaded, []]` —\n * no Cmds. Init's rehydrate branch is the migration / parse boundary, not\n * the boot-effect hook. See Invariant 2 in `.patterns/tea/tea-invariants.md`.\n *\n * Boot effects routes:\n * - Stateless infrastructure → host module top, outside TEA.\n * - State-conditional resume → a `boot` Msg the host dispatches once\n * after `run(...)` returns.\n *\n * Violations are caught at runtime by `replay` (which throws with a\n * pointer to the alternatives).\n */\n init: (loaded: S | null, ctx: Ctx) => readonly [S, readonly C[]];\n update:\n | Reducer<S, M, C>\n // Wrap in tuple to disable distributive conditional behavior. Without\n // `[S]`, TS distributes the conditional across `S`'s union members,\n // producing `Transitions<A,...> | Transitions<B,...>` instead of the\n // desired `Transitions<A|B,...>` — the table form needs a single\n // mapping over the full union of state types, not separate tables per\n // member.\n | ([S] extends [{ type: string }] ? Transitions<S, M, C> : never);\n subscriptions?: (state: S) => readonly U[];\n subscribe?: Subscribe<M, U, Ctx>;\n} & ([C] extends [Cmd<never>]\n ? { interpret?: Interpret<M, C, Ctx> }\n : { interpret: Interpret<M, C, Ctx> });\n\n// === Store: pluggable persistence adapter ===\n//\n// `load()` returns `unknown` because storage genuinely doesn't know `S` —\n// chrome.storage returns whatever JSON lives at the key, DurableObjectStorage\n// returns whatever bytes were serialized. Casting raw storage to `S` at the\n// adapter boundary is the exact `as S` move forbidden by invariant 8\n// everywhere else in the substrate. `load()` returning `unknown` makes the\n// substrate stop pretending.\n//\n// `migrate(raw)` is the boundary parse. Returns `S` on a recognized shape —\n// the substrate passes it to `init` as `loaded`. Returns `null` on any\n// unrecognized shape — the substrate passes `null` to `init`, fresh-boot\n// path. Must NOT throw: if a future shape is unrecognizable, returning\n// `null` boots clean from a known-good path. Throwing here would\n// indistinguishably collapse \"storage corruption\" and \"schema migration not\n// yet written\" — both are runtime decisions, not panics.\n//\n// Strengthens invariant 8 (the boundary parses; the core trusts) — the\n// substrate now enforces the parse instead of relying on every adapter\n// remembering to.\nexport interface Store<S> {\n load(): Promise<unknown>;\n save(state: S): Promise<void>;\n migrate(raw: unknown): S | null;\n}\n\n// === RuntimeRef<M>: typed sibling-runtime handle ===\n//\n// A `RuntimeRef<M>` exposes only the inbox of a Runtime — `dispatch(msg)`.\n// Use it as the field type when one runtime holds a reference to a *sibling*\n// runtime (composition by reduction across orthogonal lifecycles — invariant\n// 5). The holder learns the Msg shape it can send, nothing about the\n// referenced runtime's State, Cmd, Sub, Ctx, observers, listeners, or Port\n// fanout. That asymmetry is the point: cross-runtime coupling collapses to\n// \"I can put a Msg in your inbox\", which is the smallest coordination\n// surface that preserves typed cross-runtime calls.\n//\n// `Runtime<S, M>` extends `RuntimeRef<M>` structurally — every Runtime is\n// trivially a RuntimeRef. The narrowing happens at the *consumer's* Ctx\n// type, not via a runtime cast.\n//\n// Strengthens invariant 5 (composition by reduction — sibling runtimes\n// coordinate through the narrowest typed surface) and invariant 6 (runtime\n// is small and inspectable — what a holder *can* do to a sibling runtime is\n// exactly one method, not the full surface).\nexport interface RuntimeRef<M extends { type: string }> {\n dispatch(msg: M): Promise<void>;\n}\n\n// === Runtime: handle returned from run() ===\n//\n// `subscribe(listener)` is the React-shaped change notifier (zero-arg, paired\n// with `getState()` via `useSyncExternalStore`). `observe(observer)` is the\n// devtools-shaped trace hook: it receives `(msg, state)` for every completed\n// transition, including the boot transition where `msg` is `null`. Both fire\n// from the same point in the dispatch loop (after save → reconcile →\n// interpret), so what an observer sees is consistent with what a subscriber\n// sees via `getState()`.\n//\n// Observe is separate from subscribe because the contracts differ: React\n// consumers want \"something changed, re-read state\"; devtools consumers want\n// \"here is the exact (msg, state) pair, append it to a log\". Folding them\n// would force every React subscriber to type-check a `msg | null` arg they\n// will never use, and would couple `useSyncExternalStore` to a particular Msg\n// type at the substrate.\nexport interface Runtime<S, M extends { type: string }> extends RuntimeRef<M> {\n dispatch(msg: M): Promise<void>;\n getState(): S;\n subscribe(listener: () => void): () => void;\n observe(observer: (msg: M | null, state: S) => void): () => void;\n /**\n * Subscribe to a typed Port. Returns a cleanup function that removes the\n * listener. Multiple listeners per port are supported; fanout is synchronous\n * (same shape as `observe`) and isolated — one listener throw does not strand\n * the others.\n *\n * Ports are for \"data leaving the runtime selectively\" — see `definePort`.\n * Distinct from `observe` (every transition) and from State (the world).\n */\n subscribePort<T>(port: Port<T>, listener: (value: T) => void): () => void;\n /**\n * Emit a value on a Port from OUTSIDE an interpret handler. Same synchronous\n * fanout discipline as `ctx.emit` inside interpret — every subscriber\n * receives the value immediately; emitting to a port with no subscribers is\n * a no-op; listener throws are isolated.\n *\n * Use this for `observe`-driven Port emission: the canonical case is one\n * runtime publishing a state-derived signal (e.g. \"am I idle?\") that\n * another runtime subscribes to via `subscribePort`. Without\n * `runtime.emitPort`, the only way to emit a Port is from inside an\n * `interpret` handler — which forces the reducer to spawn a \"tell the\n * outside world\" Cmd on every transition that should signal. With it,\n * `runtime.observe((msg, state) => runtime.emitPort(port, derive(state)))`\n * keeps the reducer free of Port-plumbing Cmds and matches the \"every\n * transition is observable\" contract this invariant set already commits to.\n *\n * Strengthens invariant 6 (runtime is small and inspectable — Port emission\n * is no longer privileged to interpret handlers; `observe`-driven emission\n * is structurally supported).\n */\n emitPort<T>(port: Port<T>, value: T): void;\n /**\n * Resolves after boot completes — i.e. after `store.load()` (if any), `init`,\n * `store.save()` of the initial state, `reconcileSubs`, `interpret` of any\n * init-emitted cmds, and the initial listener / observer fanout. Rejects with\n * the boot error if boot fails (the same error every subsequent `dispatch`\n * call rejects with).\n *\n * Idempotent — subsequent reads return the same settled promise. The runtime\n * holds exactly one `ready` promise per `run()` call.\n *\n * Closes the v1 boot-await gap previously documented in `stepBootEffects`:\n * components that need synchronous initial state under a store (e.g.,\n * `useSyncExternalStore` consumers) can `await runtime.ready` instead of\n * `runtime.dispatch(noopMsg)`.\n *\n * Expresses canon §2.3 (the `init` contract — boot is a named, awaitable\n * moment). Strengthens invariant 6 (runtime is small and inspectable).\n */\n ready: Promise<void>;\n stop(): Promise<void>;\n}\n\n// === defineMachine: identity-typed pass-through ===\n//\n// Overloads enforce exactly one `update` shape per call so consumers see a\n// concrete type for `update`, not the substrate-internal union. Implementation\n// is identity at runtime — the runtime (`run`) branches on the structural\n// shape of `update` (Reducer vs Transitions) when dispatching.\n//\n// - Reducer-form overload: `update: Reducer<S, M, C>` — flat record keyed by\n// Msg.type. Exhaustiveness is enforced by the mapped type; adding a Msg\n// variant without a matching key is a compile error.\n// - Transitions-form overload: `update: Transitions<S, M, C>` — 2D table\n// keyed by State.type then Msg.type. Only available when `S extends {\n// type: string }`.\n//\n// Distinguishing the two: TS overload resolution checks the literal shape of\n// the argument. Reducer values are functions; Transitions values are records\n// (of functions). Structurally distinct, so overload resolution picks the\n// right branch.\n//\n// Strengthens invariant 2 (record forms have no fall-through default) and\n// invariant 7 (the Msg variant set — and, for Transitions, the State variant\n// set — is load-bearing at the type level).\n\n// Transitions-form overload — `update` is a 2D table keyed by State.type\n// then Msg.type. Declared FIRST (most specific): the value shape (record of\n// records of functions) is structurally distinct from Reducer (record of\n// functions), so TS overload resolution picks this branch only when the\n// literal matches the table shape.\n//\n// The S constraint is expressed via a conditional `S extends { type: string\n// } ? Transitions<S, M, C> : never` instead of a generic-parameter\n// constraint. The conditional approach keeps `S` unconstrained at the\n// overload signature, so `Parameters<typeof defineMachine<NonDiscriminatedS,\n// ...>>` (the public `satisfies` pattern) doesn't fail at the first\n// overload — it just skips to the next one because `update` becomes\n// `never`.\nexport function defineMachine<S, M extends { type: string }, C extends Cmd, U extends Sub, Ctx>(\n m: Omit<Machine<S, M, C, U, Ctx>, \"update\"> & {\n // `[S]` tuple-wrap disables distributive conditional behavior — see\n // the comment on `Machine.update` for why distribution is wrong here.\n update: [S] extends [{ type: string }] ? Transitions<S, M, C> : never;\n },\n): Machine<S, M, C, U, Ctx>;\n// Reducer-form overload — `update` is a flat record keyed by Msg.type.\n// Object literals where every property is a function match this shape.\nexport function defineMachine<S, M extends { type: string }, C extends Cmd, U extends Sub, Ctx>(\n m: Omit<Machine<S, M, C, U, Ctx>, \"update\"> & {\n update: Reducer<S, M, C>;\n },\n): Machine<S, M, C, U, Ctx>;\n// Implementation signature — accepts both via the union on `Machine.update`.\nexport function defineMachine<S, M extends { type: string }, C extends Cmd, U extends Sub, Ctx>(\n m: Machine<S, M, C, U, Ctx>,\n): Machine<S, M, C, U, Ctx> {\n return m;\n}\n\n// === run: implemented in task 2 ===\n//\n// Returns a `Runtime<S, M>` synchronously. Boot (`store?.load()` + `init`) runs\n// asynchronously as the FIRST entry on the serial dispatch tail; every public\n// runtime method (`dispatch`, `stop`, `getState`) awaits the boot phase\n// implicitly through the queue. Failures inside `store.load` propagate via the\n// boot promise — subsequent calls reject/throw with that error. There is no\n// \"synchronous load\" branch because `Store#load` is async by contract.\n//\n// Save-then-effects ordering is structural: every transition mutates state,\n// awaits `store.save(newState)`, then reconciles subscriptions, then runs\n// `interpret` for emitted cmds, then fires external listeners. A throw in any\n// effect phase leaves the persisted state ahead of the host's belief about\n// what executed — the Railway discipline (`tryInterpret` in handlers) makes\n// that safe in practice.\nexport function run<S, M extends { type: string }, C extends Cmd, U extends Sub, Ctx>(\n machine: Machine<S, M, C, U, Ctx>,\n opts: { ctx: Ctx; store?: Store<S> },\n): Runtime<S, M> {\n const { ctx, store } = opts;\n\n // Holders are intentionally late-initialized: `state` is set inside the boot\n // step, which runs as the head of the tail. `getState()` before boot throws.\n let state: S | undefined;\n let bootError: unknown = null;\n let stopped = false;\n\n const subRegistry = new Map<string, () => void>();\n const listeners = new Set<() => void>();\n // Observers receive (msg, state) for every completed transition. Boot fires\n // once with msg = null. Same throw-isolation contract as listeners.\n const observers = new Set<(msg: M | null, state: S) => void>();\n\n // Port subscriber registry. Keyed by Port reference (NOT name) — port\n // identity is nominal/by-reference per Elm semantics. Each port can have\n // many listeners; emissions fan out synchronously to all of them.\n // `Set<(value: unknown) => void>` is the storage shape; per-port type\n // safety is enforced at the `subscribePort` / `emit` call sites via the\n // phantom `__t` field on `Port<T>`.\n const portRegistry = new Map<Port<unknown>, Set<(value: unknown) => void>>();\n\n // The `emit` function injected onto ctx. Synchronous fanout — same shape as\n // `observe`. No-op when the port has no subscribers. Listener throws are\n // isolated + logged so one bad subscriber does not strand the others.\n function portEmit<T>(port: Port<T>, value: T): void {\n const subscribers = portRegistry.get(port as Port<unknown>);\n if (!subscribers || subscribers.size === 0) return;\n for (const listener of subscribers) {\n try {\n listener(value);\n } catch (err) {\n console.error(err);\n }\n }\n }\n\n // Augmented ctx passed to every Cmd handler. `ctx` is intentionally spread\n // into a fresh object so handlers receive a Ctx & PortEmitter without\n // mutating the caller's ctx (which may be shared across runtimes / tests).\n const augmentedCtx: Ctx & PortEmitter = { ...ctx, emit: portEmit };\n\n // === update dispatch ===\n //\n // The substrate accepts two `update` shapes (see `defineMachine` overloads):\n //\n // 1. Reducer: `{ [msg.type]: (state, msg) => [S, C[]] }`\n // 2. Transitions: `{ [state.type]: { [msg.type]: (state, msg) => [S, C[]] } }`\n //\n // Branch is structural: an object's first inspected handler tells us\n // flat-vs-nested — if `update[msg.type]` is itself a function it's a\n // Reducer; if it's an object whose value is a function it's a Transitions\n // table. No runtime tag added to the Machine — the form is detectable from\n // the value shape, and the type system guarantees `update` matches one of\n // the two shapes at the `defineMachine` boundary.\n //\n // The form is detected ONCE per `run()` call (here, at runtime setup) and\n // cached as `updateMode`. The per-dispatch path is then a single branch +\n // 1-or-2 property lookups — no per-call shape inspection.\n type CellFn = (state: S, msg: M) => readonly [S, readonly C[]];\n type ReducerRecord = Reducer<S, M, C>;\n type TransitionsTable = {\n [stateType: string]: { [msgType: string]: CellFn };\n };\n const updateForm = machine.update;\n type UpdateMode = \"reducer\" | \"transitions\";\n const updateMode: UpdateMode = (() => {\n // Inspect a single value to disambiguate. Both Reducer and Transitions\n // are non-null objects; their values differ: Reducer's values are\n // functions, Transitions's values are objects (of functions). Pick the\n // first own key — order doesn't matter because the record forms are\n // uniform (every cell has the same value shape).\n const firstKey = Object.keys(updateForm as object)[0];\n if (firstKey === undefined) {\n // Empty object literal: M is `never`. No msg can ever be dispatched,\n // so the mode is irrelevant — pick \"reducer\" arbitrarily. Pre-boot\n // this never executes because the runtime never dispatches `never`.\n return \"reducer\";\n }\n const firstValue = (updateForm as Record<string, unknown>)[firstKey];\n return typeof firstValue === \"function\" ? \"reducer\" : \"transitions\";\n })();\n function applyUpdate(state: S, msg: M): readonly [S, readonly C[]] {\n if (__DEV__) deepFreeze(state);\n\n let result: readonly [S, readonly C[]];\n if (updateMode === \"reducer\") {\n // Flat record dispatch. Mapped type (`Reducer<S, M, C>`) guarantees\n // every Msg variant has a handler at the type level; runtime is a\n // single property lookup with no fallback default branch.\n const record = updateForm as ReducerRecord;\n const handler = record[msg.type as M[\"type\"]];\n result = handler(state, msg as Extract<M, { type: M[\"type\"] }>);\n } else {\n // Transitions table dispatch. `state` is constrained to\n // `{ type: string }` by the `defineMachine` Transitions overload; the\n // table guarantees every (state.type × msg.type) cell exists at the\n // type level. Runtime is two property lookups + the call.\n const table = updateForm as unknown as TransitionsTable;\n const stateKey = (state as unknown as { type: string }).type;\n // The Transitions overload guarantees every (state.type × msg.type) cell\n // exists at the type level; the runtime lookup cannot miss.\n const handler = table[stateKey]![msg.type]!;\n result = handler(state, msg);\n }\n\n if (__DEV__) assertPureResult(result, msg.type);\n return result;\n }\n\n // The serial dispatch queue: every step is chained onto `tail`. New dispatch\n // calls (and re-entrant ones from interpret follow-ups) wait for the\n // previous step to fully resolve before running their reducer. This is the\n // single concurrency gate.\n let tail: Promise<void> = Promise.resolve();\n\n /**\n * Reconcile subscriptions against `state`. Called after every save.\n *\n * - Same id present in old + new → leave running.\n * - Removed id → call cleanup. Swallow cleanup throws + `console.error`.\n * - New id → call subscribe[type]; on success store cleanup. On throw, the\n * sub is NOT registered; throw is remembered and re-thrown after the loop\n * so all other new subs still register.\n */\n function reconcileSubs(): void {\n if (!machine.subscriptions) return;\n const desired = machine.subscriptions(state as S);\n const desiredIds = new Set<string>();\n\n // Collision assert: within a single desired set, two subs sharing an id\n // but declaring different types is the silent class of bug that Elm's\n // sub diffing relies on the user to avoid — we catch it explicitly here.\n // (Same id + same type within the desired set is a redundant declaration\n // — harmless, and the `subRegistry.has` check below dedupes it on the\n // start path. Same id across transitions is the no-churn case the\n // reconcile pass is designed for and MUST NOT throw.)\n const desiredTypeById = new Map<string, string>();\n for (const sub of desired) {\n const existing = desiredTypeById.get(sub.id);\n if (existing !== undefined && existing !== sub.type) {\n throw new Error(\n `@demlik/tea: Sub.id collision: id=\"${sub.id}\" declared as type=\"${existing}\" and type=\"${sub.type}\"`,\n );\n }\n desiredTypeById.set(sub.id, sub.type);\n }\n\n // Removals first — anything in the registry not in `desired` should stop.\n for (const sub of desired) desiredIds.add(sub.id);\n for (const [id, cleanup] of subRegistry) {\n if (!desiredIds.has(id)) {\n try {\n cleanup();\n } catch (err) {\n console.error(err);\n }\n subRegistry.delete(id);\n }\n }\n\n // Additions — anything in `desired` not in the registry should start.\n let firstStartError: unknown = null;\n for (const sub of desired) {\n if (subRegistry.has(sub.id)) continue;\n const handler = machine.subscribe?.[sub.type as U[\"type\"]];\n if (!handler) {\n // No handler for this sub type — programmer error; skip. (TS would\n // normally forbid this at compile time when `subscribe` is provided\n // and U has variants, but a runtime guard is cheap.)\n continue;\n }\n try {\n const cleanup = handler(sub as Extract<U, { type: U[\"type\"] }>, ctx, enqueueDispatch);\n subRegistry.set(sub.id, cleanup);\n } catch (err) {\n if (firstStartError === null) firstStartError = err;\n // Do NOT register; continue to next sub so other starts still run.\n }\n }\n if (firstStartError !== null) throw firstStartError;\n }\n\n // `interpret` is optional at the type level when `C extends Cmd<never>`\n // (see `Machine<...>`). The runtime safely defaults a missing map to `{}`\n // because such a machine never emits cmds — the cmd loop below never\n // dereferences a handler. The defensive default also guards against a\n // miswired non-Cmd<never> consumer reaching this code path; the per-cmd\n // `if (!handler) continue` then preserves invariant-6 forward progress\n // instead of throwing on `undefined.foo`.\n type InterpretMap = {\n [K in C[\"type\"]]: (cmd: Extract<C, { type: K }>, ctx: Ctx & PortEmitter) => Promise<M | void>;\n };\n const interpretMap: InterpretMap =\n (machine as { interpret?: InterpretMap }).interpret ?? ({} as InterpretMap);\n\n /**\n * Run `interpret` for each emitted cmd. Each handler may return a follow-up\n * Msg — that Msg is enqueued onto the tail (NOT dispatched re-entrantly\n * inside the current transition).\n *\n * Throws propagate to the caller of `step`. The first error stops further\n * handlers in this transition.\n */\n async function runInterpret(cmds: readonly C[]): Promise<void> {\n for (const cmd of cmds) {\n const handler = interpretMap[cmd.type as C[\"type\"]];\n if (!handler) continue;\n const follow = await handler(cmd as Extract<C, { type: C[\"type\"] }>, augmentedCtx);\n if (follow !== undefined && follow !== null) {\n // Schedule follow-up Msg on the tail. The current step resolves\n // first; the follow-up runs after, as a fresh transition. Swallow the\n // returned rejection — fire-and-forget at this site; the runtime\n // never re-throws follow-up errors at the dispatcher of the original\n // cmd. If you need observable error handling, name a failure Msg via\n // `tryInterpret` (Railway).\n enqueueDispatch(follow as M).catch(() => {});\n }\n }\n }\n\n /**\n * Fire every external listener. Listener throws are isolated — one bad\n * listener does not strand the others. This is the dual of cleanup\n * semantics: the runtime guarantees forward progress for unrelated\n * observers.\n */\n function fireListeners(): void {\n for (const listener of listeners) {\n try {\n listener();\n } catch (err) {\n console.error(err);\n }\n }\n }\n\n /**\n * Fire every observer with the just-applied msg + post-transition state.\n * Same throw-isolation contract as `fireListeners`. Called immediately\n * after `fireListeners` so observers see exactly what subscribers see.\n */\n function fireObservers(msg: M | null): void {\n if (observers.size === 0 || state === undefined) return;\n const snapshot = state;\n for (const observer of observers) {\n try {\n observer(msg, snapshot);\n } catch (err) {\n console.error(err);\n }\n }\n }\n\n /**\n * One full transition: update → save → reconcile subs → interpret → fire\n * listeners. Save-before-effects is the hard ordering; tests pin it.\n *\n * Throws from `update` propagate immediately (state unchanged). Throws from\n * `save` propagate after the in-memory `state` is advanced (PRD row: \"state\n * advanced; persisted state did not\"). Throws from reconcile or interpret\n * propagate after `save` has succeeded (PRD row: \"state already saved\").\n */\n async function stepDispatch(msg: M): Promise<void> {\n if (state === undefined) {\n // This should never happen — boot always runs before any dispatch.\n throw new Error(\"@demlik/tea: runtime not booted\");\n }\n const [next, cmds] = applyUpdate(state, msg);\n state = next;\n if (store) await store.save(state);\n // Subscriptions reconcile against the new state; throws propagate AFTER\n // the entire diff pass completes (so other subs still register / clean\n // up correctly). Cleanup throws are swallowed inside reconcileSubs.\n reconcileSubs();\n await runInterpret(cmds);\n fireListeners();\n fireObservers(msg);\n }\n\n /**\n * Boot is split into two phases.\n *\n * - **Synchronous (`run()` call):** when `store` is absent, we know `loaded\n * = null` without an await — run `init(null, ctx)` IMMEDIATELY so that\n * `getState()` is observable synchronously. This is what\n * `useSyncExternalStore` consumers (e.g. `@demlik/tea/react`) need to render\n * on the first commit without an undefined-state flicker.\n * - **Asynchronous (`stepBootEffects` on the tail):** save + sub reconcile\n * + interpret-of-init-cmds + listener fire. These run as the first tail\n * entry; subsequent dispatches queue behind them.\n *\n * When `store` IS present we cannot do `init` synchronously (we'd be\n * inventing a loaded value), so we keep the full async path. Components\n * that want synchronous initial state in that case should omit the store\n * for the React mount and persist via a different mechanism (or accept the\n * default-snapshot story in a future v2).\n *\n * `store.load` throws propagate via the boot promise; subsequent calls\n * reject with the load error. (The PRD row \"run() throws synchronously\" is\n * imprecise relative to Store#load being async — we honor the intent: no\n * usable runtime is ever observable on load failure.)\n */\n // Synchronous init when there is NO store: `getState()` is observable\n // synchronously by `useSyncExternalStore` consumers. When a store IS\n // present, `init` must run AFTER `store.load()` (the contract: `init`\n // receives the loaded snapshot), and callers must await boot before\n // calling `getState()` — they do so implicitly via the first `dispatch()`\n // (which queues behind the boot step).\n //\n // The asymmetry is the substrate being honest about a real constraint: a\n // store is, by definition, an async boundary. Components that need\n // synchronous initial state under a store should await the runtime's boot\n // before mounting — `await runtime.ready` (not provided in v1; use `await\n // runtime.dispatch(noopMsg)` if needed, or a parent-level loading state).\n let pendingInitCmds: readonly C[] = [];\n if (!store) {\n const [initialState, initCmds] = machine.init(null, ctx);\n state = initialState;\n pendingInitCmds = initCmds;\n }\n\n async function stepBootEffects(): Promise<void> {\n if (store) {\n // Boundary parse (invariant 8). `store.load()` returns `unknown`\n // because storage doesn't know `S`; `store.migrate(raw)` is the\n // substrate's required parse — `S` on recognized shape, `null` on\n // unrecognized (which boots fresh, a known-good path). `migrate` MUST\n // NOT throw per its contract; if it does we surface the error via the\n // boot promise (same as a `load` throw — `runtime.ready` rejects and\n // every subsequent `dispatch` surfaces `bootError`).\n const raw = await store.load();\n const parsed = store.migrate(raw);\n const [initialState, initCmds] = machine.init(parsed, ctx);\n state = initialState;\n pendingInitCmds = initCmds;\n await store.save(state);\n }\n reconcileSubs();\n await runInterpret(pendingInitCmds);\n fireListeners();\n fireObservers(null);\n }\n\n /**\n * Enqueue a dispatch on the tail. Re-entrant calls from interpret or\n * subscribe handlers go through this same gate — guaranteeing serial\n * execution across the entire system.\n *\n * `dispatch` rejects when:\n * - the runtime is stopped (next dispatch always rejects after stop);\n * - boot failed (every dispatch surfaces the boot error);\n * - the reducer, save, sub start, or interpret throws.\n */\n function enqueueDispatch(msg: M): Promise<void> {\n if (stopped) return Promise.reject(new Error(\"@demlik/tea: runtime stopped\"));\n const next = tail.then(() => {\n if (bootError !== null) throw bootError;\n return stepDispatch(msg);\n });\n // Swallow the rejection on the tail so a single failing dispatch does NOT\n // poison every subsequent one. The original `next` promise still rejects\n // for the dispatch caller.\n tail = next.catch(() => {});\n return next;\n }\n\n // Kick off boot-effects immediately. Boot rejections are remembered on\n // `bootError`; they surface on every subsequent dispatch call. Note: when\n // `store` is absent the synchronous-init step above already set `state` —\n // `stepBootEffects` only runs save/sub/interpret/listener-fire. When\n // `store` is present, the full async path runs here.\n //\n // `bootPromise` is the original (un-swallowed) promise — it is what\n // `runtime.ready` exposes so callers see the boot error directly. `tail`\n // gets the swallowed branch so a boot failure does NOT poison every\n // subsequent dispatch's `.then` chain (each dispatch surfaces `bootError`\n // explicitly inside `enqueueDispatch`).\n const bootPromise = stepBootEffects();\n tail = bootPromise.catch((err) => {\n bootError = err;\n });\n\n const runtime: Runtime<S, M> = {\n dispatch: enqueueDispatch,\n getState(): S {\n if (bootError !== null) throw bootError;\n // Synchronous-init path (no store): `state` is set inside `run()`\n // before this getter can ever be called → returns immediately. Resumed\n // path (store present): `state` becomes defined after\n // `stepBootEffects` awaits `store.load()`; calls before that throw.\n // The `booted` flag is no longer a precondition — `state !==\n // undefined` is the single source of truth that init information is\n // available.\n if (state === undefined) {\n throw new Error(\"@demlik/tea: getState() called before runtime booted\");\n }\n return state;\n },\n subscribe(listener: () => void): () => void {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n observe(observer: (msg: M | null, state: S) => void): () => void {\n observers.add(observer);\n return () => {\n observers.delete(observer);\n };\n },\n subscribePort<T>(port: Port<T>, listener: (value: T) => void): () => void {\n const key = port as Port<unknown>;\n let bucket = portRegistry.get(key);\n if (!bucket) {\n bucket = new Set<(value: unknown) => void>();\n portRegistry.set(key, bucket);\n }\n // The bucket stores `(value: unknown) => void` to keep the registry\n // monomorphic; per-port type safety lives at this call site (the\n // listener's `T` is bound to the port's phantom `__t`).\n const erased = listener as (value: unknown) => void;\n bucket.add(erased);\n return () => {\n const set = portRegistry.get(key);\n if (!set) return;\n set.delete(erased);\n if (set.size === 0) portRegistry.delete(key);\n };\n },\n emitPort<T>(port: Port<T>, value: T): void {\n // Same closure as `ctx.emit` inside interpret — synchronous fanout,\n // no-op on no subscribers, listener-throw isolation. Exposing it on\n // the Runtime interface lets `observe`-driven Port emission work\n // without spawning a \"tell the outside world\" Cmd per transition.\n portEmit(port, value);\n },\n // `ready` is the original (un-swallowed) boot promise. Idempotency\n // comes for free — promises settle exactly once; every read returns the\n // same settled value. See the boot-promise note above for why this is\n // distinct from `tail`.\n ready: bootPromise,\n async stop(): Promise<void> {\n stopped = true;\n // Drain whatever is in flight before tearing down. Any rejections in\n // the tail were already swallowed at enqueue time, so this await\n // always resolves.\n await tail;\n // Run every active sub cleanup. Throws are swallowed + logged so one\n // bad cleanup does not strand the others.\n for (const [id, cleanup] of subRegistry) {\n try {\n cleanup();\n } catch (err) {\n console.error(err);\n }\n subRegistry.delete(id);\n }\n // Flush final state. A save throw here is logged but does not reject\n // `stop()` — the contract is \"returns a resolved Promise\".\n if (store && state !== undefined && bootError === null) {\n try {\n await store.save(state);\n } catch (err) {\n console.error(err);\n }\n }\n },\n };\n\n return runtime;\n}\n\n// === historyTracker: composable observability helper ===\n//\n// A bounded ring buffer of recent `(msg, state)` transitions, built on the\n// Runtime's public `observe` API. The substrate itself stays minimal — it\n// has no notion of history, no opt to enable one, no method on the Runtime\n// interface to read one. Anything that wants \"recent transitions\" composes\n// via this helper. The substrate provides the primitive (`observe`); this\n// is the bookkeeping that builds on it.\n//\n// Use cases:\n// - Bridge backlog replay for late subscribers (see `@demlik/tea/extension`).\n// - Debug inspectors showing recent traffic on attach.\n// - Sentry breadcrumb collection.\n// - Property/contract tests that want to assert \"these N transitions\n// happened in this order.\"\n//\n// Discipline:\n// - One-line creation: `const tr = historyTracker(runtime, 100);`\n// - Snapshot at any time: `tr.snapshot()` — shallow copy, oldest first.\n// - Detach when done: `tr.stop()` — unsubscribes the observer; the buffer\n// freezes at its current contents (snapshot() still works, useful for\n// post-hoc inspection in tests).\n//\n// Why not put this on the Runtime interface as `runtime.history()`?\n// Because history is derivable. Putting it on the Runtime privileges one\n// use case (debug inspectors) at the substrate level and grows the\n// interface for everyone. The composition shape stays out of the dispatch\n// loop entirely — the substrate's hot path doesn't pay any cost for a\n// feature it doesn't need to provide.\n// =============================================================================\n\nexport interface HistoryTracker<S, M extends { type: string }> {\n /**\n * Snapshot of recorded transitions, oldest first. Each entry is\n * `{ msg, state }` exactly as the underlying `observe` callback received\n * it — `msg` is `null` for the boot transition, a Msg for every other.\n *\n * Returns a shallow copy: the array is fresh on every call (callers may\n * iterate, slice, or replay without affecting the tracker's buffer).\n * Entry values are references to the originals (TEA states/msgs are\n * conventionally immutable; do not mutate them if your S/M is not).\n */\n snapshot(): readonly { readonly msg: M | null; readonly state: S }[];\n /**\n * Detach the underlying observer. After `stop()`:\n * - No new transitions are recorded.\n * - `snapshot()` continues to work and returns the buffer's contents\n * at the moment of stop (useful for post-hoc inspection in tests).\n * - The tracker holds no references that would prevent GC of the\n * `runtime` argument other than the entries already buffered.\n *\n * Idempotent — subsequent calls are no-ops.\n */\n stop(): void;\n}\n\n/**\n * Create a bounded history tracker over a Runtime. The tracker subscribes\n * to the runtime via `observe(...)` and retains the last `size` `(msg,\n * state)` transitions in a FIFO ring buffer.\n *\n * @param runtime Any `Runtime<S, M>`. The tracker uses only `observe`.\n * @param size Buffer cap. `size <= 0` produces a tracker whose\n * snapshot is always `[]` (the observer is still attached\n * but pushes a no-op; cheaper to skip the tracker entirely\n * in that case).\n *\n * Memory cost: O(size × avg(msg + state size)). For audit-extension's\n * inspector use case (size 100, audit state ~5KB), ~500KB max — held\n * in-memory only, no persistence.\n */\nexport function historyTracker<S, M extends { type: string }>(\n runtime: Runtime<S, M>,\n size: number,\n): HistoryTracker<S, M> {\n const buffer: { msg: M | null; state: S }[] = [];\n let stopped = false;\n\n const unobserve =\n size <= 0\n ? (): void => {}\n : runtime.observe((msg, state) => {\n buffer.push({ msg, state });\n if (buffer.length > size) buffer.shift();\n });\n\n return {\n snapshot() {\n // Shallow copy — callers may freely iterate or mutate the returned\n // array. Entry values are references; see HistoryTracker docs.\n return buffer.slice();\n },\n stop() {\n if (stopped) return;\n stopped = true;\n unobserve();\n },\n };\n}\n\n// === replay: pure unit-test helper ===\n//\n// Composes `init(loaded ?? null, ctx)` then `update(state, msg)` for each msg.\n// Returns the final state plus the cmds that *would* have been emitted and the\n// subs that *would* have been desired at the final state.\n//\n// Crucially:\n// - does NOT call any `interpret[type]` handler\n// - does NOT touch `Store`\n// - does NOT start any subscription via `machine.subscribe?.[type]`\n//\n// `subscriptions` (the description function) IS called to derive the returned\n// `subs` array. That's intentional — it lets tests assert what would be wired\n// up without actually wiring it.\nexport function replay<S, M extends { type: string }, C extends Cmd, U extends Sub, Ctx>(\n machine: Machine<S, M, C, U, Ctx>,\n opts: { msgs: readonly M[]; ctx: Ctx; loaded?: S | null },\n): { state: S; cmds: C[]; subs: U[] } {\n // `init` is `(loaded: S | null, ctx) => ...`; coerce undefined → null so the\n // AC \"`replay` with `loaded: undefined` calls `init(null, ctx)`\" holds.\n const loaded = opts.loaded ?? null;\n const [initialState, initCmds] = machine.init(loaded, opts.ctx);\n\n if (loaded !== null && initCmds.length > 0) {\n throw new Error(\n \"TEA contract violation: machine.init must return [state, []] (no Cmds) \" +\n \"when `loaded` is non-null. Init's rehydrate branch is a pure state \" +\n \"passthrough — the migration/parse boundary, not the boot-effect hook.\\n\\n\" +\n \"Routes for boot effects:\\n\" +\n \" - Stateless infrastructure (e.g. chrome.alarms ensure) → host module \" +\n \"top level, outside TEA.\\n\" +\n \" - State-conditional resume (e.g. re-attach a session) → a `boot` Msg \" +\n \"dispatched once from the host after `run(...)` returns, handled in update.\\n\\n\" +\n \"See .patterns/tea/tea-invariants.md (Invariant 2).\",\n );\n }\n\n let state: S = initialState;\n const cmds: C[] = [...initCmds];\n\n // Mirror the same form-branching the runtime does (see `applyUpdate` in\n // `run`). Detected once; reused per-msg. Keeping it inline (not factored\n // into a shared helper) so `replay` stays standalone — it is the pure\n // unit-test tool and must not depend on `run`'s internal closures.\n type CellFn = (state: S, msg: M) => readonly [S, readonly C[]];\n type ReducerRecord = Reducer<S, M, C>;\n type TransitionsTable = { [stateType: string]: { [msgType: string]: CellFn } };\n const updateForm = machine.update;\n type UpdateMode = \"reducer\" | \"transitions\";\n const updateMode: UpdateMode = (() => {\n const firstKey = Object.keys(updateForm as object)[0];\n if (firstKey === undefined) return \"reducer\";\n const firstValue = (updateForm as Record<string, unknown>)[firstKey];\n return typeof firstValue === \"function\" ? \"reducer\" : \"transitions\";\n })();\n\n for (const msg of opts.msgs) {\n if (__DEV__) deepFreeze(state);\n\n let result: readonly [S, readonly C[]];\n if (updateMode === \"reducer\") {\n result = (updateForm as ReducerRecord)[msg.type as M[\"type\"]](\n state,\n msg as Extract<M, { type: M[\"type\"] }>,\n );\n } else {\n const table = updateForm as unknown as TransitionsTable;\n const stateKey = (state as unknown as { type: string }).type;\n result = table[stateKey]![msg.type]!(state, msg);\n }\n\n if (__DEV__) assertPureResult(result, msg.type);\n\n const [next, emitted] = result;\n state = next;\n cmds.push(...emitted);\n }\n\n const subs: U[] = machine.subscriptions ? [...machine.subscriptions(state)] : [];\n\n return { state, cmds, subs };\n}\n\n// === tryInterpret: Railway sugar over `Result.tryPromise` ===\n//\n// Wraps a fallible `(cmd, ctx) => Promise<Ok>` and returns a handler suitable\n// for `interpret[type]`. On success the returned function resolves with\n// `onOk(value, cmd)`; on rejection it resolves with `onErr(error, cmd)`. It\n// NEVER rejects (assuming `onOk`/`onErr` are total — that's the caller's\n// contract).\n//\n// Implementation note: we use the `{try, catch: (e) => e}` form of\n// `Result.tryPromise` rather than the one-arg `(thunk)` form so the original\n// error passes through untouched. The one-arg form wraps errors in\n// `UnhandledException`, which would prevent callers from `instanceof`-checking\n// their own error types inside `onErr`.\n//\n// M is a Msg type (not a Cmd) — the helper routes Ok/Err to two Msg constructors.\nexport function tryInterpret<C extends Cmd, Ok, M, Ctx>(\n work: (cmd: C, ctx: Ctx) => Promise<Ok>,\n onOk: (value: Ok, cmd: C) => M,\n onErr: (error: unknown, cmd: C) => M,\n): (cmd: C, ctx: Ctx) => Promise<M> {\n return async (cmd, ctx) => {\n const result = await Result.tryPromise({\n try: () => work(cmd, ctx),\n catch: (error: unknown): unknown => error,\n });\n return result.match({\n ok: (value) => onOk(value, cmd),\n err: (error) => onErr(error, cmd),\n });\n };\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/pbt/runners/replay-fold.ts","../src/pbt/runners/property.ts"],"names":[],"mappings":"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","file":"chunk-PYBQREFU.js","sourcesContent":["// ---------------------------------------------------------------------------\n// `foldEvents` — pure deterministic fold over a Msg sequence.\n//\n// PBT needs the per-step (prev/msg/next/cmds) structure to assert\n// step-shaped invariants. The substrate's `replay(machine, opts)` returns\n// only final state + cumulative cmds — insufficient. We use `replay` for\n// the boot step (calls `machine.init(loaded, ctx)` faithfully) then walk\n// msgs ourselves using the SAME cell-dispatch shape the substrate uses in\n// both `run()` and `replay()`.\n//\n// Why duplicate the cell-dispatch (5 LOC) instead of looping `replay` once\n// per msg with `loaded: prevState`? Because `init(loaded)` runs every\n// call, and on machines with non-trivial init transforms (e.g. the audit\n// machine's `auditing → attach_debugger` resume cell) that would either\n// (a) re-fire the transform on every step, or (b) require fragile cmd-\n// subtraction to isolate this step's cmds. Calling `update` directly\n// keeps the fold pure and the cmds per-step exactly.\n//\n// Mirrors the dispatch in `packages/tea/src/index.ts` `applyUpdate` and\n// `replay`. The form-detect is identical, the same single-property lookup,\n// the same `as unknown as` cast at the table boundary.\n//\n// `foldEvents` does NOT call `interpret`, does NOT touch a Store, does NOT\n// start Subs. The reducer surface is the contract.\n// ---------------------------------------------------------------------------\n\nimport { type Cmd, type Machine, type Reducer, replay, type Sub } from \"../../index\";\n\n/**\n * One unit in a fold trace — the four pieces of data a property\n * invariant typically asserts on. `prev` is the state before the msg\n * applied; `next` is the state after; `cmds` is the cmds emitted by this\n * single step (NOT the running total).\n */\nexport interface Step<S, M, C> {\n readonly prev: S;\n readonly msg: M;\n readonly next: S;\n readonly cmds: readonly C[];\n}\n\n/**\n * Fold a Msg sequence through a machine's pure reducer, returning the\n * per-step trace, every state in order, and the final state. Initial\n * state comes from `replay(machine, { msgs: [], ctx, loaded })` — i.e.\n * `machine.init(loaded ?? null, ctx)` faithfully. Init's emitted cmds\n * are NOT included in any `Step` (they're the boot moment, not a Msg).\n *\n * @param machine The machine under test.\n * @param ctx The Ctx — typically `stubCtxThrowingProxy<Ctx>()` when\n * init is pure. Pass a real ctx if init reads from it.\n * @param loaded Optional pre-init snapshot (the `loaded` arg to\n * `machine.init`). Defaults to `null` (fresh boot).\n * @param msgs The Msg sequence to fold.\n *\n * @example\n * const { states, steps, finalState } = foldEvents(machine, ctx, null, [\n * { type: \"start\" }, { type: \"tick\" }, { type: \"stop\" },\n * ]);\n * // steps[1] = { prev: { type: \"counting\", n: 0 }, msg: { type: \"tick\" }, ... }\n */\nexport function foldEvents<S, M extends { type: string }, C extends Cmd, U extends Sub, Ctx>(\n machine: Machine<S, M, C, U, Ctx>,\n ctx: NoInfer<Ctx>,\n loaded: NoInfer<S> | null,\n msgs: readonly NoInfer<M>[],\n): {\n readonly states: readonly S[];\n readonly steps: readonly Step<S, M, C>[];\n readonly finalState: S;\n} {\n // Boot through `replay` so `init` runs faithfully and we honor the\n // substrate's loaded-snapshot contract (init may transform the loaded\n // value before the first Msg lands — e.g. the audit machine flips\n // `debuggerAttached: false` on resume).\n const { state: initialState } = replay(machine, { msgs: [], ctx, loaded: loaded ?? null });\n\n // Per-msg dispatch mirrors the substrate's `applyUpdate` in `run()` and\n // the same loop in `replay()`. Two record forms; structural detect once.\n type CellFn = (state: S, msg: M) => readonly [S, readonly C[]];\n type ReducerRecord = Reducer<S, M, C>;\n type TransitionsTable = { [stateType: string]: { [msgType: string]: CellFn } };\n\n const updateForm = machine.update;\n type UpdateMode = \"reducer\" | \"transitions\";\n const updateMode: UpdateMode = (() => {\n const firstKey = Object.keys(updateForm as object)[0];\n if (firstKey === undefined) return \"reducer\";\n const firstValue = (updateForm as Record<string, unknown>)[firstKey];\n return typeof firstValue === \"function\" ? \"reducer\" : \"transitions\";\n })();\n\n const states: S[] = [initialState];\n const steps: Step<S, M, C>[] = [];\n let current = initialState;\n\n for (const msg of msgs) {\n let next: S;\n let emitted: readonly C[];\n if (updateMode === \"reducer\") {\n const record = updateForm as ReducerRecord;\n [next, emitted] = record[msg.type as M[\"type\"]](\n current,\n msg as Extract<M, { type: M[\"type\"] }>,\n );\n } else {\n const table = updateForm as unknown as TransitionsTable;\n const stateKey = (current as unknown as { type: string }).type;\n [next, emitted] = table[stateKey]![msg.type]!(current, msg);\n }\n steps.push({ prev: current, msg, next, cmds: [...emitted] });\n states.push(next);\n current = next;\n }\n\n return { states, steps, finalState: current };\n}\n","// ---------------------------------------------------------------------------\n// Property runners — three thin wrappers over `fc.assert(fc.property(...))`\n// that fold a generated Msg sequence through the machine via `foldEvents`\n// and pipe the result into a TEA-shaped predicate.\n//\n// - `propertyTerminates` — every sequence ends in a terminal state.\n// - `propertyInvariant` — predicate holds on every (prev, msg, next, cmds) step.\n// - `propertyTrace` — predicate holds on the full (trace, final) shape.\n//\n// Each runner accepts `numRuns` (passed through to fast-check), an optional\n// `loaded` snapshot (forwarded to `foldEvents`), and the Msg-sequence\n// arbitrary. They return `void`; failures throw via fast-check's standard\n// shrinking output.\n//\n// Default shrinker is fast-check's built-in (open question 4): drop-msg /\n// prefix shrinkers can be layered later but are opt-in, not default. The\n// built-in shrinker handles array-shape shrinking + per-element variant\n// shrinking automatically.\n// ---------------------------------------------------------------------------\n\nimport type { Cmd, Machine, Sub } from \"../../index\";\nimport * as fc from \"fast-check\";\nimport { foldEvents, type Step } from \"./replay-fold\";\n\n/** Common options shared by every property runner. */\ninterface PropertyOpts<S> {\n /** Pre-init snapshot — passed to `machine.init(loaded, ctx)`. Default `null`. */\n readonly loaded?: S | null;\n /** Number of generated sequences to test. Forwarded to `fc.assert`. */\n readonly numRuns?: number;\n /** Optional fast-check seed for reproducibility. */\n readonly seed?: number;\n}\n\n/**\n * Assert every generated Msg sequence ends in a terminal state. `terminal`\n * is the predicate that names the acceptable final states (often\n * `(s) => s.type === \"done\" || s.type === \"idle\"`).\n *\n * The \"every sequence\" property catches stuck states (machine gets to a\n * non-terminal state and no further msg can escape) AND silent loops\n * (every msg variant is a noop from a given state).\n *\n * @example\n * propertyTerminates(\n * auditBackgroundMachine,\n * stubCtxThrowingProxy<BackgroundCtx>(),\n * arbMsgSequence(msgArb, { minLength: 1, maxLength: 30 }),\n * (s) => s.type === \"done\" || s.type === \"idle\",\n * { loaded: auditingState, numRuns: 500 },\n * );\n */\nexport function propertyTerminates<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n>(\n machine: Machine<S, M, C, U, Ctx>,\n ctx: NoInfer<Ctx>,\n seqArb: fc.Arbitrary<readonly NoInfer<M>[]>,\n terminal: (state: NoInfer<S>) => boolean,\n opts?: NoInfer<PropertyOpts<S>>,\n): void {\n const fcParams: fc.Parameters<[readonly M[]]> = {\n numRuns: opts?.numRuns ?? 200,\n ...(opts?.seed !== undefined ? { seed: opts.seed } : {}),\n };\n fc.assert(\n fc.property(seqArb, (msgs) => {\n const { finalState } = foldEvents(machine, ctx, opts?.loaded ?? null, msgs);\n if (!terminal(finalState)) {\n // Throwing here (vs returning false) lets the error message carry\n // the offending final state — fast-check pretty-prints it inside\n // the shrunk counter-example.\n throw new Error(\n `propertyTerminates: final state did not match terminal predicate. ` +\n `Final: ${JSON.stringify(finalState)}`,\n );\n }\n }),\n fcParams,\n );\n}\n\n/**\n * Assert a predicate holds on EVERY transition step in a generated\n * sequence. The predicate sees `{ prev, msg, next, cmds }` per step;\n * return true to accept, false (or throw) to fail.\n *\n * Use to pin a \"when X happens in state Y, cmd Z must be emitted\" rule.\n * The shrinker minimizes the offending sequence to the smallest Msg list\n * that still violates the predicate.\n *\n * @example\n * // Pin the resume-handshake fix: `ws:client:hello` in auditing must\n * // emit `send_ws { client:ready }`.\n * propertyInvariant(\n * auditBackgroundMachine, ctx,\n * arbMsgSequence(msgArb, { prefix: [{ type: \"ws:client:hello\" }] }),\n * ({ prev, msg, cmds }) =>\n * !(prev.type === \"auditing\" && msg.type === \"ws:client:hello\") ||\n * cmds.some((c) => c.type === \"send_ws\" && c.msg.type === \"client:ready\"),\n * { loaded: auditingState, numRuns: 200 },\n * );\n */\nexport function propertyInvariant<S, M extends { type: string }, C extends Cmd, U extends Sub, Ctx>(\n machine: Machine<S, M, C, U, Ctx>,\n ctx: NoInfer<Ctx>,\n seqArb: fc.Arbitrary<readonly NoInfer<M>[]>,\n invariant: (step: Step<NoInfer<S>, NoInfer<M>, NoInfer<C>>) => boolean,\n opts?: NoInfer<PropertyOpts<S>>,\n): void {\n const fcParams: fc.Parameters<[readonly M[]]> = {\n numRuns: opts?.numRuns ?? 200,\n ...(opts?.seed !== undefined ? { seed: opts.seed } : {}),\n };\n fc.assert(\n fc.property(seqArb, (msgs) => {\n const { steps } = foldEvents(machine, ctx, opts?.loaded ?? null, msgs);\n for (const step of steps) {\n if (!invariant(step)) {\n throw new Error(\n `propertyInvariant: failed at step msg=${JSON.stringify(step.msg)} ` +\n `prev=${JSON.stringify(step.prev)} next=${JSON.stringify(step.next)} ` +\n `cmds=${JSON.stringify(step.cmds)}`,\n );\n }\n }\n }),\n fcParams,\n );\n}\n\n/**\n * Assert a predicate over the full trace plus the final state. Use when\n * the property is \"some Msg appeared in the trace before reaching the\n * final state\" or \"no two consecutive steps had the same state.type\" —\n * predicates that need the full sequence, not a single step.\n *\n * @example\n * propertyTrace(\n * machine, ctx, seqArb,\n * (steps, final) =>\n * final.type !== \"done\" ||\n * steps.some((step) => step.msg.type === \"ws:audit:done\"),\n * { loaded: auditingState, numRuns: 500 },\n * );\n */\nexport function propertyTrace<S, M extends { type: string }, C extends Cmd, U extends Sub, Ctx>(\n machine: Machine<S, M, C, U, Ctx>,\n ctx: NoInfer<Ctx>,\n seqArb: fc.Arbitrary<readonly NoInfer<M>[]>,\n predicate: (\n steps: readonly Step<NoInfer<S>, NoInfer<M>, NoInfer<C>>[],\n finalState: NoInfer<S>,\n ) => boolean,\n opts?: NoInfer<PropertyOpts<S>>,\n): void {\n const fcParams: fc.Parameters<[readonly M[]]> = {\n numRuns: opts?.numRuns ?? 200,\n ...(opts?.seed !== undefined ? { seed: opts.seed } : {}),\n };\n fc.assert(\n fc.property(seqArb, (msgs) => {\n const { steps, finalState } = foldEvents(machine, ctx, opts?.loaded ?? null, msgs);\n if (!predicate(steps, finalState)) {\n throw new Error(\n `propertyTrace: predicate failed on trace length=${steps.length}, ` +\n `final=${JSON.stringify(finalState)}`,\n );\n }\n }),\n fcParams,\n );\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/extension/bridge.ts"],"names":[],"mappings":"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","file":"chunk-PYOCUBQJ.js","sourcesContent":["/**\n * Runtime bridge — expose a `@demlik/tea` runtime running in one chrome context\n * (typically the background service worker) to consumers in other contexts\n * (sidepanel, popup, devtools panel) over `chrome.runtime.sendMessage` /\n * `chrome.runtime.onMessage`.\n *\n * Two halves, one channel name string.\n *\n * ┌──── background (host) ────┐ ┌──── react surface ────┐\n * │ bridgeRuntime(rt, {ch}) │ chrome.* │ useBackgroundRuntime │\n * │ ├ observe → broadcast │ ──────────▶ │ ├ hydrate (initial) │\n * │ └ onMessage(\":dispatch\") │ ◀────────── │ └ dispatch (msgs) │\n * │ └ onMessage(\":hydrate\") │ │ │\n * └────────────────────────────┘ └───────────────────────┘\n *\n * Wire-shape (single `channel` string, three messages):\n *\n * { type: channel, msg, state } // background → surfaces (broadcast)\n * { type: `${channel}:hydrate` } // surface → background (request)\n * { type: `${channel}:dispatch`, msg } // surface → background (input)\n *\n * The `:hydrate` reply is the current `runtime.getState()` snapshot —\n * lets a newly-opened surface render the right view BEFORE the next\n * transition broadcast arrives.\n *\n * The bridge is intentionally agnostic to `chrome.runtime.sendMessage`\n * failure modes (no receiver, sender gone, etc.) — every cross-context send\n * is fire-and-forget with `.catch(() => {})`. Surfaces that need\n * delivery guarantees should layer their own retry, not bake it here.\n */\n\nimport { type HistoryTracker, historyTracker, type Runtime } from \"../index\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Wire types — internal. Consumers don't import these directly; they pick\n// the channel string and the bridge handles serialization.\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface BroadcastEnvelope<S, M> {\n type: string; // === `channel`\n msg: M | null;\n state: S;\n}\n\ninterface DispatchEnvelope<M> {\n type: string; // === `${channel}:dispatch`\n msg: M;\n}\n\ninterface HydrateRequest {\n type: string; // === `${channel}:hydrate`\n}\n\n/**\n * Wire shape of one entry in the hydrate `backlog`. Mirrors a single\n * broadcast envelope, but without the `type` discriminator (the parent\n * `HydrateResponse` already identified the channel).\n */\ninterface BacklogEntry<V, M> {\n msg: M | null;\n state: V;\n}\n\ninterface HydrateResponse<S, M> {\n state: S;\n /**\n * The runtime's history (most recent transitions, oldest first) projected\n * through `serialize` if provided. Present when the host runtime was\n * started with `historySize > 0`; an empty array otherwise. The surface\n * replays each entry through registered listeners before firing the\n * current-state marker, so a late subscriber sees the recent msg log AND\n * the current state on connect — not just the current state.\n */\n backlog: BacklogEntry<S, M>[];\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Background side — bridgeRuntime(runtime, { channel, serialize? })\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface BridgeRuntimeOpts<S, V = S> {\n channel: string;\n /**\n * Optional projection from the runtime's live state `S` to a JSON-safe\n * view `V` that goes over the wire (broadcast + hydrate). Required when\n * `S` carries non-serializable values (DOM refs, Map, Date, …).\n *\n * Identity passthrough by default — existing callers with POJO state pass\n * `serialize` unset and `V = S`.\n *\n * Wire shape with `serialize`:\n * { type: channel, msg, state: serialize(currentState) } // broadcast\n * { type: hydrate } → { state: serialize(currentState) } // hydrate\n *\n * Dispatch is NOT affected — it carries `M` end-to-end. Surfaces parse the\n * `V` they receive (they should pick a type parameter matching the\n * projection).\n */\n serialize?: (state: S) => V;\n /**\n * If > 0, the bridge composes a `historyTracker(runtime, size)` over the\n * runtime and returns the captured `(msg, state)` backlog in every\n * `:hydrate` reply. Late subscribers receive recent traffic on connect —\n * the inspector then sees prior tool calls / phase transitions instead\n * of just the current state snapshot.\n *\n * Defaults to 0 (no tracker, empty backlog). Composition stays opt-in:\n * the substrate is untouched, the dispatch loop is untouched, and the\n * bridge attaches the tracker only when this opt is set. Cleanup is\n * the bridge's responsibility — the disposer returned by `bridgeRuntime`\n * stops the tracker along with the observer.\n *\n * Memory cost: ~size × (msg + state) bytes, in-memory only, dies with\n * the bridge. Privacy: backlog entries flow to every `:hydrate` reply —\n * any surface that can hydrate sees them. Do not enable on bridges\n * carrying user secrets.\n */\n historySize?: number;\n}\n\n/**\n * Install the bridge for a runtime. Returns a cleanup function that removes\n * the observer and the chrome listener.\n *\n * - **Broadcast:** every (msg, state) transition emits\n * `{ type: channel, msg, state }` via `chrome.runtime.sendMessage`. The\n * send is fire-and-forget — when no surface is listening, chrome.runtime\n * rejects with \"Could not establish connection. Receiving end does not\n * exist.\" which we swallow. If `serialize` is provided, `state` carries\n * `serialize(state)` instead of the raw state.\n * - **Hydrate:** `{ type: \":hydrate\" }` is answered via `sendResponse` with\n * the current (optionally projected) `runtime.getState()`. If the runtime\n * is mid-boot (store load not yet resolved), `getState()` throws — we\n * swallow and reply with `state: null`; surfaces should treat null as\n * \"not ready yet\" and wait for the first broadcast.\n * - **Dispatch:** `{ type: \":dispatch\", msg }` routes to `runtime.dispatch`.\n * Returns `true` from the listener so chrome keeps the message channel\n * open until dispatch's promise settles (we ack with `{ ok: true }`).\n */\nexport function bridgeRuntime<S, M extends { type: string }, V = S>(\n runtime: Runtime<S, M>,\n { channel, serialize, historySize }: BridgeRuntimeOpts<S, V>,\n): () => void {\n const dispatchType = `${channel}:dispatch`;\n const hydrateType = `${channel}:hydrate`;\n // Default to identity passthrough when no projection is provided. The\n // type parameter defaults `V = S` so callers with no projection see no\n // change in the wire-shape — the broadcast still carries `S`.\n const project: (state: S) => V = serialize ?? ((state: S): V => state as unknown as V);\n // Compose a history tracker only when the caller opted in. Substrate\n // (`@demlik/tea`'s `run()`) knows nothing about history — the tracker is a\n // pure composition over `runtime.observe(...)`, owned by the bridge.\n // `null` when unset means \"no backlog in :hydrate replies\"; the wire\n // shape stays uniform (backlog is always an array, possibly empty).\n const tracker: HistoryTracker<S, M> | null =\n historySize !== undefined && historySize > 0 ? historyTracker(runtime, historySize) : null;\n\n const unobserve = runtime.observe((msg, state) => {\n const envelope: BroadcastEnvelope<V, M | null> = {\n type: channel,\n msg,\n state: project(state),\n };\n chrome.runtime.sendMessage(envelope).catch(() => {\n // No surface listening — drop silently. Costs are negligible at\n // typical audit-event rates; surfaces poll on hydrate on open.\n });\n });\n\n const onMessage = (\n message: unknown,\n _sender: chrome.runtime.MessageSender,\n sendResponse: (response: unknown) => void,\n ): boolean | undefined => {\n if (!message || typeof message !== \"object\") return undefined;\n const obj = message as Record<string, unknown>;\n\n if (obj.type === hydrateType) {\n let state: V | null;\n try {\n state = project(runtime.getState());\n } catch {\n // Runtime still booting (store.load pending). Surfaces fall back to\n // waiting for the first broadcast.\n state = null;\n }\n // Read the tracker's snapshot and project each entry's state through\n // `serialize`. When `historySize` was unset (tracker === null) we\n // send `[]` — uniform wire shape means surfaces never branch on\n // \"did the bridge enable history\" — they always iterate.\n const backlog: BacklogEntry<V, M>[] = tracker\n ? tracker.snapshot().map((entry) => ({ msg: entry.msg, state: project(entry.state) }))\n : [];\n const response: HydrateResponse<V | null, M> = { state, backlog };\n sendResponse(response);\n return false;\n }\n\n if (obj.type === dispatchType) {\n const msg = obj.msg as M;\n runtime\n .dispatch(msg)\n .then(() => {\n sendResponse({ ok: true });\n })\n .catch((err: unknown) => {\n sendResponse({ ok: false, error: err instanceof Error ? err.message : String(err) });\n });\n return true; // keep channel open for async sendResponse\n }\n\n return undefined;\n };\n\n chrome.runtime.onMessage.addListener(onMessage);\n\n return () => {\n unobserve();\n chrome.runtime.onMessage.removeListener(onMessage);\n // Detach the history observer too — leaving it attached after the\n // bridge tears down would silently retain memory and keep observing\n // a runtime whose backlog can no longer reach any consumer.\n tracker?.stop();\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Surface side — the bridge half that lives in a React surface. Exported as\n// a pure helper so the React adapter (or a test) wires the listeners and the\n// hydrate round-trip with the same wire format the host emits.\n//\n// Note: this file deliberately has NO React import. The React hook lives in\n// `react.ts` and consumes these helpers. Keeps the bridge testable in plain\n// vitest without happy-dom + React renderers.\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface BridgeClient<S, M> {\n /** Send the hydrate request; resolves with the initial snapshot (or null\n * if the host is still booting OR the response failed to parse). Also\n * primes `getSnapshot()` so React's `useSyncExternalStore` sees the\n * hydrated value on the next render. */\n hydrate(): Promise<S | null>;\n /** Send a dispatch envelope; resolves once the host's dispatch settles. */\n dispatch(msg: M): Promise<void>;\n /** Subscribe to broadcasts. Returns a cleanup function. Broadcasts whose\n * payload fails `parseState` (or, if provided, `parseMsg`) are dropped\n * silently — the listener is not called. Each successful broadcast\n * updates `getSnapshot()`. */\n subscribe(listener: (msg: M | null, state: S) => void): () => void;\n /** Synchronous accessor for the latest known state, or `null` until the\n * first successful hydrate or broadcast lands. Required reference stability\n * for `useSyncExternalStore`: returns the SAME object reference when the\n * underlying state hasn't changed since the prior call. */\n getSnapshot(): S | null;\n}\n\nexport interface BridgeClientOpts<S, M> {\n channel: string;\n /**\n * REQUIRED parse for inbound `state` payloads (from `:hydrate` reply AND\n * every broadcast). Chrome runtime messages are an `unknown` boundary\n * (Inv 8) — the surface is the only party that knows the domain type `S`,\n * so it owns the boundary parse. Returning `null` drops the payload\n * silently (subscriber not called; hydrate resolves to `null`). The\n * parser MUST NOT throw — model rejection as `null`.\n */\n parseState: (raw: unknown) => S | null;\n /**\n * Optional parse for inbound `msg` payloads on broadcasts. Defaults to a\n * pass-through cast — most surfaces only need to gate on state and treat\n * the msg union as advisory. Provide this when malformed msgs must be\n * dropped (e.g. when the host's M is a real wire enum, not a local type).\n * Returning `null` drops the broadcast silently.\n */\n parseMsg?: (raw: unknown) => M | null;\n}\n\nexport function bridgeClient<S, M extends { type: string }>({\n channel,\n parseState,\n parseMsg,\n}: BridgeClientOpts<S, M>): BridgeClient<S, M> {\n const dispatchType = `${channel}:dispatch`;\n const hydrateType = `${channel}:hydrate`;\n // Closure-held latest snapshot. `useSyncExternalStore` reads this via\n // `getSnapshot`; the reference is stable across calls until a hydrate\n // reply or broadcast overwrites it. Identity matters: returning a new\n // object for the same logical state would loop React. `null` is the\n // pre-hydrate sentinel.\n let latestState: S | null = null;\n // Each active subscribe() registers a listener. Hydrate replies notify\n // every subscriber with `msg: null` (no host transition occurred — the\n // change source is the hydrate round-trip) so `useSyncExternalStore`\n // sees the snapshot update on first mount.\n const listeners = new Set<(msg: M | null, state: S) => void>();\n\n const client: BridgeClient<S, M> = {\n async hydrate(): Promise<S | null> {\n const req: HydrateRequest = { type: hydrateType };\n const res = (await chrome.runtime.sendMessage(req)) as\n | { state: unknown; backlog?: unknown }\n | undefined;\n // Replay the backlog FIRST — listeners observing for a msg log see\n // historical (msg, state) pairs in oldest-first order before the\n // current-state marker. Backlog entries that fail boundary parse are\n // dropped silently (same posture as live broadcasts). When the host\n // didn't enable history, `backlog` is `[]` and this loop is a no-op.\n replayBacklog(res?.backlog, parseState, parseMsg, listeners, (next) => {\n latestState = next;\n });\n const raw = res?.state;\n if (raw === undefined || raw === null) return null;\n const parsed = parseState(raw);\n if (parsed === null) return null;\n latestState = parsed;\n // Fire the current-state marker (msg = null) — distinct from any\n // backlog entry. Surfaces that show \"X just connected\" key off this.\n for (const l of listeners) l(null, parsed);\n return parsed;\n },\n\n async dispatch(msg: M): Promise<void> {\n const env: DispatchEnvelope<M> = { type: dispatchType, msg };\n await chrome.runtime.sendMessage(env);\n },\n\n subscribe(listener: (msg: M | null, state: S) => void): () => void {\n listeners.add(listener);\n const onMessage = (message: unknown): void => {\n if (!message || typeof message !== \"object\") return;\n const obj = message as Record<string, unknown>;\n if (obj.type !== channel) return;\n // Boundary parse (Inv 8): chrome runtime messages are `unknown`.\n // A failed state parse drops the broadcast — null state would\n // be indistinguishable from \"host still booting\" and is not a\n // useful signal to surfaces. Same for msg when parseMsg is set.\n const state = parseState(obj.state);\n if (state === null) return;\n let msg: M | null;\n if (obj.msg === null || obj.msg === undefined) {\n msg = null;\n } else if (parseMsg !== undefined) {\n msg = parseMsg(obj.msg);\n if (msg === null) return;\n } else {\n msg = obj.msg as M;\n }\n latestState = state;\n listener(msg, state);\n };\n chrome.runtime.onMessage.addListener(onMessage);\n return () => {\n chrome.runtime.onMessage.removeListener(onMessage);\n listeners.delete(listener);\n };\n },\n\n getSnapshot(): S | null {\n return latestState;\n },\n };\n\n return client;\n}\n\n/**\n * Shared backlog-replay helper used by both `bridgeClient` and\n * `bridgeTabClient`. Iterates raw entries from the wire, runs each through\n * the same boundary-parse rules a live broadcast would (parseState +\n * optional parseMsg, both returning `null` to drop), and fires the listeners\n * with the parsed `(msg, state)` pair. Updates the closure-held `latestState`\n * via `commitState` so post-replay `getSnapshot()` returns the most recent\n * backlog state if the current-state marker is absent or unparseable.\n *\n * Extracted so the two clients can't drift in how they parse backlog\n * payloads — both must stay symmetric with their live `subscribe` parse.\n */\nfunction replayBacklog<S, M extends { type: string }>(\n raw: unknown,\n parseState: (raw: unknown) => S | null,\n parseMsg: ((raw: unknown) => M | null) | undefined,\n listeners: Set<(msg: M | null, state: S) => void>,\n commitState: (next: S) => void,\n): void {\n if (!Array.isArray(raw)) return;\n for (const entryRaw of raw) {\n if (!entryRaw || typeof entryRaw !== \"object\") continue;\n const entry = entryRaw as Record<string, unknown>;\n const state = parseState(entry.state);\n if (state === null) continue;\n let msg: M | null;\n if (entry.msg === null || entry.msg === undefined) {\n msg = null;\n } else if (parseMsg !== undefined) {\n msg = parseMsg(entry.msg);\n if (msg === null) continue;\n } else {\n msg = entry.msg as M;\n }\n commitState(state);\n for (const l of listeners) l(msg, state);\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// SW-side client of a content-script-hosted runtime.\n//\n// `bridgeClient` above sends via `chrome.runtime.sendMessage`, which lands on\n// the SW's `chrome.runtime.onMessage`. That's the surface-to-SW direction.\n//\n// For the SW-to-content direction we need:\n// - SENDS via `chrome.tabs.sendMessage(tabId, …)` — `chrome.runtime.sendMessage`\n// does NOT reach content scripts running in a tab.\n// - BROADCAST subscribers must filter on `sender.tab?.id === tabId`, since\n// every tab's content script broadcasts through the same SW\n// `chrome.runtime.onMessage`. Without the filter, a SW client subscribed\n// to \"tab 17\" would also see broadcasts from tabs 22, 31, ….\n//\n// Shape mirrors `BridgeClient<S, M>` so consumers can pick the constructor by\n// context and use the rest of the surface identically.\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface BridgeTabClientOpts<S, M> {\n tabId: number;\n channel: string;\n /**\n * REQUIRED parse for inbound `state` payloads. See `BridgeClientOpts.parseState`\n * — same contract, same rationale. Chrome runtime messages from a tab are\n * an `unknown` boundary; the SW client owns the parse.\n */\n parseState: (raw: unknown) => S | null;\n /**\n * Optional parse for inbound `msg` payloads. See\n * `BridgeClientOpts.parseMsg`.\n */\n parseMsg?: (raw: unknown) => M | null;\n}\n\n/**\n * SW-side client of a `bridgeRuntime` hosted in a content script.\n *\n * Identical contract to `bridgeClient` (same `BridgeClient<S, M>` shape).\n * Differences are confined to transport:\n *\n * - `hydrate()` / `dispatch(msg)` send via `chrome.tabs.sendMessage(tabId, …)`.\n * If the tab is gone, `chrome.tabs.sendMessage` rejects (same shape as\n * `chrome.runtime.sendMessage` with no receiver) — the promise propagates\n * the rejection. Callers decide whether to retry, drop, or surface; the\n * bridge has no opinion. Fire-and-forget posture matches `bridgeRuntime`.\n * - `subscribe(listener)` registers a `chrome.runtime.onMessage` listener\n * filtered by both `envelope.type === channel` AND `sender.tab?.id === tabId`,\n * so broadcasts from other tabs are ignored (no cross-contamination across\n * per-tab content-script runtimes).\n */\nexport function bridgeTabClient<S, M extends { type: string }>({\n tabId,\n channel,\n parseState,\n parseMsg,\n}: BridgeTabClientOpts<S, M>): BridgeClient<S, M> {\n const dispatchType = `${channel}:dispatch`;\n const hydrateType = `${channel}:hydrate`;\n let latestState: S | null = null;\n const listeners = new Set<(msg: M | null, state: S) => void>();\n\n const client: BridgeClient<S, M> = {\n async hydrate(): Promise<S | null> {\n const req: HydrateRequest = { type: hydrateType };\n const res = (await chrome.tabs.sendMessage(tabId, req)) as\n | { state: unknown; backlog?: unknown }\n | undefined;\n // Mirror bridgeClient.hydrate's backlog replay: same parse rules,\n // same oldest-first ordering, same current-state-marker semantics.\n // The two helpers diverge only in transport (tabs vs runtime), not\n // in protocol shape.\n replayBacklog(res?.backlog, parseState, parseMsg, listeners, (next) => {\n latestState = next;\n });\n const raw = res?.state;\n if (raw === undefined || raw === null) return null;\n const parsed = parseState(raw);\n if (parsed === null) return null;\n latestState = parsed;\n for (const l of listeners) l(null, parsed);\n return parsed;\n },\n\n async dispatch(msg: M): Promise<void> {\n const env: DispatchEnvelope<M> = { type: dispatchType, msg };\n await chrome.tabs.sendMessage(tabId, env);\n },\n\n subscribe(listener: (msg: M | null, state: S) => void): () => void {\n listeners.add(listener);\n const onMessage = (message: unknown, sender: chrome.runtime.MessageSender): void => {\n if (sender.tab?.id !== tabId) return;\n if (!message || typeof message !== \"object\") return;\n const obj = message as Record<string, unknown>;\n if (obj.type !== channel) return;\n // Boundary parse (Inv 8) — mirror bridgeClient.\n const state = parseState(obj.state);\n if (state === null) return;\n let msg: M | null;\n if (obj.msg === null || obj.msg === undefined) {\n msg = null;\n } else if (parseMsg !== undefined) {\n msg = parseMsg(obj.msg);\n if (msg === null) return;\n } else {\n msg = obj.msg as M;\n }\n latestState = state;\n listener(msg, state);\n };\n chrome.runtime.onMessage.addListener(onMessage);\n return () => {\n chrome.runtime.onMessage.removeListener(onMessage);\n listeners.delete(listener);\n };\n },\n\n getSnapshot(): S | null {\n return latestState;\n },\n };\n\n return client;\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/subs/from-broadcast-channel.ts","../src/subs/from-event-source.ts","../src/subs/from-event-target.ts","../src/subs/from-interval.ts","../src/subs/from-port.ts","../src/subs/from-web-socket.ts"],"names":[],"mappings":"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","file":"chunk-Y5JM3UUH.js","sourcesContent":["// ---------------------------------------------------------------------------\n// fromBroadcastChannel — universal `BroadcastChannel`-shaped Sub factory.\n//\n// `BroadcastChannel` is a same-origin pub/sub primitive available in\n// every modern browser context (window, worker, service worker) AND in\n// Cloudflare Workers (with origin scoped to the worker isolate). The\n// constructor opens the channel; the `message` event fires whenever\n// another participant on the same channel name posts a message; the\n// `close()` method releases the OS resource.\n//\n// The factory carries `channelName` on the Sub itself (not in the\n// closure) so the same factory instance handles every BroadcastChannel\n// Sub in a machine — one Sub per channel, named by id, sharing the\n// factory's lifecycle code. Renaming a channel requires a new Sub.id\n// (the reconcile pass otherwise leaves the existing channel open).\n//\n// `msgFn` returns `M | null` — same drop-on-null pattern as\n// `fromEventTarget`. The MessageEvent's `data` is `unknown` from the\n// platform's perspective; the caller decides whether the payload is a\n// recognizable shape, and returns `null` to drop unknown messages\n// without polluting the reducer with parse-discard cells.\n//\n// Cleanup calls `channel.close()`, which removes the listener AND\n// releases the channel reference — `removeEventListener` alone would\n// leak the BroadcastChannel handle. The pair is intentional.\n// ---------------------------------------------------------------------------\n\nimport type { Sub } from \"../index\";\nimport type { MinimalBroadcastChannelCtor, MinimalMessageEvent } from \"./platform\";\nimport type { SubscribeHandler } from \"./types\";\n\ntype BroadcastSubData = { channelName: string };\n\ndeclare const BroadcastChannel: MinimalBroadcastChannelCtor;\n\nexport function fromBroadcastChannel<S extends Sub & BroadcastSubData, M>(\n msgFn: (event: MinimalMessageEvent, sub: S) => M | null,\n): SubscribeHandler<S, M, unknown> {\n return (sub, _ctx, dispatch) => {\n const channel = new BroadcastChannel(sub.channelName);\n const listener = (event: MinimalMessageEvent): void => {\n const msg = msgFn(event, sub);\n if (msg !== null) dispatch(msg);\n };\n channel.addEventListener(\"message\", listener);\n return () => {\n channel.removeEventListener(\"message\", listener);\n channel.close();\n };\n };\n}\n","// ---------------------------------------------------------------------------\n// fromEventSource — universal `EventSource` (server-sent events) Sub\n// factory.\n//\n// SSE is the asymmetric streaming protocol used by the agent log /\n// audit log pipelines: HTTP-shaped open, server pushes events forever,\n// client receives until close. `EventSource` is the standard browser\n// API; the constructor opens the connection, `message` fires per\n// server-pushed event, `error` fires on disconnect or non-2xx, `open`\n// fires once on initial handshake, `close()` tears the connection\n// down.\n//\n// Three callbacks because three observable events ARE three concerns\n// the caller routinely cares about:\n//\n// - `onMessage(data, sub)` — required. The SSE payload's `data` field\n// is `string` from the platform; the caller decides parse + Msg\n// shape. Returns `M | null`; null drops the dispatch.\n//\n// - `onError(event, sub)` — optional. SSE's `error` event fires both\n// on transient network drops (the browser auto-reconnects) AND on\n// permanent failures. Most consumers want to surface ONE of these\n// as a Msg (e.g. \"stream-disconnected\") without folding both into\n// `onMessage`'s discriminant. Omitting the callback drops error\n// events silently.\n//\n// - `onOpen(sub)` — optional. The connection's initial handshake\n// succeeded. Useful for \"show 'connected' indicator\" Msgs. Omitting\n// drops open events silently. NOT fired on auto-reconnect by every\n// browser implementation — treat the event as best-effort.\n//\n// `url` lives on the Sub itself (`EventSourceSubData`). Same id +\n// different url = the substrate's reconcile pass leaves the existing\n// connection alone (the URL change is silent). Changing the URL\n// requires a new Sub.id.\n//\n// Cleanup is `close()` + `removeEventListener` for all three —\n// belt-and-suspenders against EventSource implementations that\n// dispatch events into already-closed sources.\n// ---------------------------------------------------------------------------\n\nimport type { Sub } from \"../index\";\nimport type { MinimalEvent, MinimalEventSourceCtor, MinimalMessageEvent } from \"./platform\";\nimport type { SubscribeHandler } from \"./types\";\n\ntype EventSourceSubData = { url: string };\n\ndeclare const EventSource: MinimalEventSourceCtor;\n\nexport interface EventSourceFactoryOpts<S, M> {\n onMessage: (data: string, sub: S) => M | null;\n onError?: (event: MinimalEvent, sub: S) => M | null;\n onOpen?: (sub: S) => M | null;\n}\n\nexport function fromEventSource<S extends Sub & EventSourceSubData, M>(\n opts: EventSourceFactoryOpts<S, M>,\n): SubscribeHandler<S, M, unknown> {\n return (sub, _ctx, dispatch) => {\n const source = new EventSource(sub.url);\n\n const messageListener = (event: MinimalMessageEvent): void => {\n // The SSE `data` payload is a string per the platform contract.\n // Cast at the boundary: callers express `(data: string) => M | null`\n // and never see `unknown` flowing in from the platform global.\n const msg = opts.onMessage(event.data as string, sub);\n if (msg !== null) dispatch(msg);\n };\n source.addEventListener(\"message\", messageListener);\n\n const onError = opts.onError;\n const errorListener = onError\n ? (event: MinimalEvent): void => {\n const msg = onError(event, sub);\n if (msg !== null) dispatch(msg);\n }\n : undefined;\n if (errorListener) source.addEventListener(\"error\", errorListener);\n\n const onOpen = opts.onOpen;\n const openListener = onOpen\n ? (): void => {\n const msg = onOpen(sub);\n if (msg !== null) dispatch(msg);\n }\n : undefined;\n if (openListener) source.addEventListener(\"open\", openListener);\n\n return () => {\n source.removeEventListener(\"message\", messageListener);\n if (errorListener) source.removeEventListener(\"error\", errorListener);\n if (openListener) source.removeEventListener(\"open\", openListener);\n source.close();\n };\n };\n}\n","// ---------------------------------------------------------------------------\n// fromEventTarget — universal addEventListener/removeEventListener Sub\n// factory over any `EventTarget` (DOM nodes, `window`, `document`,\n// `BroadcastChannel`, `MessagePort`, `Worker`, `ServiceWorker`, etc.).\n//\n// Two non-obvious shape choices:\n//\n// - `getTarget: () => EventTarget` is a thunk, not a direct value. The\n// factory MUST work in environments where the target is not available\n// at module-load time — service workers run before any `document`\n// exists; some targets are constructed lazily from `ctx`. Deferring\n// the lookup to subscribe time also means a state-driven swap of the\n// target requires only a new Sub.id; the factory pulls the current\n// target on subscribe.\n//\n// - `msgFn` may return `null` — that emission is *dropped*. The event\n// fired, the factory called `msgFn`, but the caller decided \"this\n// event is not interesting given current state.\" No dispatch is made;\n// the listener stays armed for the next event. This is the explicit\n// filter point at the Sub boundary — the alternative (filtering inside\n// the reducer via a noop cell) costs a transition + reconcile pass per\n// filtered event.\n//\n// Cleanup uses the SAME `getTarget()` instance captured at subscribe time\n// so a target swap mid-Sub doesn't leak the listener on the old target.\n// We close over the resolved target rather than calling `getTarget()`\n// again at cleanup — the alternative (re-resolving) is the cause of\n// silent listener leaks in long-lived runtimes.\n// ---------------------------------------------------------------------------\n\nimport type { Sub } from \"../index\";\nimport type { MinimalEvent, MinimalEventTarget } from \"./platform\";\nimport type { SubscribeHandler } from \"./types\";\n\nexport function fromEventTarget<S extends Sub, M>(\n getTarget: () => MinimalEventTarget,\n eventName: string,\n msgFn: (event: MinimalEvent, sub: S) => M | null,\n): SubscribeHandler<S, M, unknown> {\n return (sub, _ctx, dispatch) => {\n const target = getTarget();\n const listener = (event: MinimalEvent): void => {\n const msg = msgFn(event, sub);\n if (msg !== null) dispatch(msg);\n };\n target.addEventListener(eventName, listener);\n return () => {\n target.removeEventListener(eventName, listener);\n };\n };\n}\n","// ---------------------------------------------------------------------------\n// fromInterval — universal `setInterval`-shaped Sub factory.\n//\n// The recurring shape at every `subscribe[type]` cell that wraps a periodic\n// timer is the same five lines: `setInterval` the dispatch, return a\n// `clearInterval` cleanup. The factory absorbs both halves of the lifecycle\n// (subscribe + cleanup); the caller keeps the intent — which Sub variant,\n// which Msg to dispatch.\n//\n// Timing data (`intervalMs`) lives on the Sub itself, not in the closure.\n// The substrate already reconciles Subs by id on every transition; carrying\n// the period on the Sub means a `subscriptions(state)` function that\n// returns `{ id, type, intervalMs: 30_000 }` in one state and the same id\n// with `intervalMs: 60_000` later... will NOT re-wire the interval (the id\n// is unchanged, the reconcile pass leaves it running). That's a tradeoff\n// the substrate's reconcile contract pins, not this factory — if you want\n// the interval to change, change the Sub's id too.\n//\n// Strengthens invariant 9 (named, small surface) and invariant 2 (the Sub\n// carries the data; the lifecycle is in the factory; the reducer never\n// sees the timer).\n// ---------------------------------------------------------------------------\n\nimport type { Sub } from \"../index\";\nimport type { SubscribeHandler } from \"./types\";\n\ntype IntervalSubData = { intervalMs: number };\n\nexport function fromInterval<S extends Sub & IntervalSubData, M>(\n msgFn: (sub: S) => M,\n): SubscribeHandler<S, M, unknown> {\n return (sub, _ctx, dispatch) => {\n const handle = setInterval(() => {\n dispatch(msgFn(sub));\n }, sub.intervalMs);\n return () => {\n clearInterval(handle);\n };\n };\n}\n","// ---------------------------------------------------------------------------\n// fromPort — universal `Runtime.subscribePort`-shaped Sub factory.\n//\n// The canonical cross-runtime coordination pattern in @demlik/tea: one\n// runtime emits to a typed Port; another runtime's machine declares an\n// `audit-idle`-shaped Sub whose subscribe handler attaches to the\n// publisher's Port. The recurring 4-line shape at every such cell:\n//\n// \"audit-idle\": (_sub, ctx, dispatch) =>\n// ctx.auditRuntime.subscribePort(auditIdlePort, (value) => {\n// dispatch(buildMsg(value));\n// });\n//\n// The factory absorbs the cleanup-returning subscribePort call AND the\n// dispatch-on-emit wiring. The caller keeps the three pieces of intent:\n//\n// 1. `selectRuntime(ctx)` — which sibling Runtime publishes the Port.\n// This is a selector (not a direct ctx.auditRuntime reference) so\n// the factory does NOT pin the ctx shape — any machine whose ctx\n// exposes some Runtime<...> reference is compatible by selecting it.\n// A direct `Runtime` parameter would have made the factory shape\n// depend on having that Runtime in scope at machine-definition\n// time, which is a worse match for the \"ctx is the boundary\"\n// contract this substrate already commits to.\n//\n// 2. `port: Port<T>` — the typed channel itself. Identity is by\n// reference (the substrate enforces unique names at definePort\n// time); pass the same `Port<T>` value the publisher emits to.\n//\n// 3. `msgFn(value, sub) => M | null` — the value-to-Msg mapping, with\n// drop-on-null semantics matching every other factory in this\n// subpath. The published value's type `T` flows through to `msgFn`'s\n// first param so the caller never re-asserts the payload shape.\n//\n// Strengthens invariant 9 (the cross-runtime Port topology is named at\n// one factory site instead of redrawn at every subscribe cell).\n// ---------------------------------------------------------------------------\n\nimport type { Port, Runtime, Sub } from \"../index\";\nimport type { SubscribeHandler } from \"./types\";\n\nexport function fromPort<S extends Sub, M, Ctx, T>(\n selectRuntime: (ctx: Ctx) => Runtime<unknown, { type: string }>,\n port: Port<T>,\n msgFn: (value: T, sub: S) => M | null,\n): SubscribeHandler<S, M, Ctx> {\n return (sub, ctx, dispatch) => {\n const runtime = selectRuntime(ctx);\n return runtime.subscribePort(port, (value) => {\n const msg = msgFn(value, sub);\n if (msg !== null) dispatch(msg);\n });\n };\n}\n","// ---------------------------------------------------------------------------\n// fromWebSocket — universal `WebSocket` Sub factory. The symmetric sibling\n// of `fromEventSource`.\n//\n// WebSocket is the bidirectional streaming protocol used wherever the\n// server pushes events AND the client pushes commands on the same\n// connection. The constructor opens the connection, `onopen` fires once\n// on handshake, `onmessage` fires per server-pushed frame, `onerror`\n// fires on transient or permanent failure, `onclose` fires once on\n// teardown carrying `code` + `reason`. `close(code)` initiates a clean\n// shutdown from the client side.\n//\n// Four callbacks because four observable events ARE four concerns the\n// caller routinely cares about. The Sub's `wsUrl` lives on the Sub itself\n// (`WebSocketSubData`) — same id + different `wsUrl` is silent under the\n// reconcile pass, identical to `fromEventSource`'s `url` contract.\n//\n// - `onMessage(data, sub)` — required. The platform delivers `data` as\n// `string | Blob | ArrayBuffer` depending on `binaryType` and the\n// server's frame type. The factory does NOT bake in a parse contract:\n// the callback receives the raw `unknown` and decides parse + Msg\n// shape. Returns `M | null`; null drops the dispatch.\n//\n// - `onOpen(sub)` — optional. Connection handshake succeeded. Useful\n// for \"show 'connected' indicator\" Msgs. Omitting drops open events\n// silently.\n//\n// - `onError(event, sub)` — optional. WebSocket's `error` event fires\n// on transient drops AND permanent failures; like SSE, most consumers\n// want one Msg (\"connection-errored\") without folding both into\n// `onMessage`'s discriminant. Omitting drops error events silently.\n//\n// - `onClose(code, reason, sub)` — optional. The connection is gone.\n// `code` is the WebSocket close code (1000 = clean, 1006 = abnormal,\n// etc.). `reason` is the server-supplied string (often empty).\n// Omitting drops close events silently.\n//\n// Cleanup is `close(1000)` — the IANA-registered clean-shutdown code.\n// The factory intentionally does NOT bake in registry semantics (e.g.\n// the extension's `ctx.wsRegistry` so `interpret.send_ws` can reach the\n// live socket): that's consumer glue. A consumer that needs the live\n// socket for outbound sends wires it from inside the subscribe handler\n// or keeps a hand-written Sub alongside this factory.\n// ---------------------------------------------------------------------------\n\nimport type { Sub } from \"../index\";\nimport type {\n MinimalCloseEvent,\n MinimalEvent,\n MinimalMessageEvent,\n MinimalWebSocketCtor,\n} from \"./platform\";\nimport type { SubscribeHandler } from \"./types\";\n\nexport type WebSocketSubData = { wsUrl: string };\n\ndeclare const WebSocket: MinimalWebSocketCtor;\n\nexport interface WebSocketFactoryOpts<S, M> {\n onMessage: (data: unknown, sub: S) => M | null;\n onOpen?: (sub: S) => M | null;\n onError?: (event: MinimalEvent, sub: S) => M | null;\n onClose?: (code: number, reason: string, sub: S) => M | null;\n}\n\nexport function fromWebSocket<S extends Sub & WebSocketSubData, M>(\n opts: WebSocketFactoryOpts<S, M>,\n): SubscribeHandler<S, M, unknown> {\n return (sub, _ctx, dispatch) => {\n const ws = new WebSocket(sub.wsUrl);\n\n ws.onmessage = (event: MinimalMessageEvent): void => {\n // The platform delivers `data` as `unknown` — the factory hands it\n // through untouched and the caller chooses the parse strategy.\n const msg = opts.onMessage(event.data, sub);\n if (msg !== null) dispatch(msg);\n };\n\n const onOpen = opts.onOpen;\n if (onOpen) {\n ws.onopen = (): void => {\n const msg = onOpen(sub);\n if (msg !== null) dispatch(msg);\n };\n }\n\n const onError = opts.onError;\n if (onError) {\n ws.onerror = (event: MinimalEvent): void => {\n const msg = onError(event, sub);\n if (msg !== null) dispatch(msg);\n };\n }\n\n const onClose = opts.onClose;\n if (onClose) {\n ws.onclose = (event: MinimalCloseEvent): void => {\n const msg = onClose(event.code, event.reason, sub);\n if (msg !== null) dispatch(msg);\n };\n }\n\n return () => {\n // Detach the lifecycle handlers BEFORE close() so the consumer's\n // `onClose` Msg isn't fired from a teardown the substrate initiated.\n // Mirrors fromEventSource's removeEventListener pass.\n ws.onmessage = null;\n ws.onopen = null;\n ws.onerror = null;\n ws.onclose = null;\n // 1000 = \"normal closure\" per RFC 6455 §7.4.1.\n ws.close(1000);\n };\n };\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/pbt/arbitraries/ctx.ts","../src/pbt/arbitraries/msg.ts","../src/pbt/arbitraries/sequence.ts"],"names":["fc2"],"mappings":";;;AAyCO,SAAS,oBAAA,GAAiC;AAC/C,EAAA,MAAM,OAAA,GAAgC;AAAA,IACpC,GAAA,CAAI,SAAS,IAAA,EAAgC;AAK3C,MAAA,IAAI,OAAO,IAAA,KAAS,QAAA,EAAU,OAAO,MAAA;AAKrC,MAAA,IAAI,SAAS,QAAA,IAAY,IAAA,KAAS,cAAc,IAAA,KAAS,SAAA,IAAa,SAAS,MAAA,EAAQ;AACrF,QAAA,OAAO,MAAA;AAAA,MACT;AACA,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,oFACgB,IAAI,CAAA,wEAAA;AAAA,OAEtB;AAAA,IACF,CAAA;AAAA,IACA,GAAA,GAAe;AACb,MAAA,OAAO,KAAA;AAAA,IACT,CAAA;AAAA,IACA,OAAA,GAAsC;AACpC,MAAA,OAAO,EAAC;AAAA,IACV;AAAA,GACF;AACA,EAAA,OAAO,IAAI,KAAA,CAAM,EAAC,EAAG,OAAO,CAAA;AAC9B;ACVO,SAAS,MAAA,CACd,OACA,IAAA,EACiB;AACjB,EAAA,MAAM,UAAU,IAAA,EAAM,OAAA;AACtB,EAAA,MAAM,OAAA,GAAU,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA;AACpC,EAAA,IAAI,OAAA,CAAQ,WAAW,CAAA,EAAG;AACxB,IAAA,MAAM,IAAI,MAAM,wCAAwC,CAAA;AAAA,EAC1D;AACA,EAAA,MAAM,WAAW,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAC,GAAA,EAAK,GAAG,CAAA,MAAO;AAAA,IAC5C,SAAA,EAAW,GAAA;AAAA,IACX,MAAA,EAAQ,OAAA,GAAU,GAAG,CAAA,IAAK;AAAA,GAC5B,CAAE,CAAA;AACF,EAAA,OAAU,EAAA,CAAA,KAAA,CAAM,GAAG,QAAQ,CAAA;AAC7B;AASO,SAAS,eAAiC,IAAA,EAAoC;AACnF,EAAA,OAAU,EAAA,CAAA,QAAA,CAAS,EAAE,IAAA,EAAM,CAAA;AAC7B;AAWO,SAAS,YAAA,CACd,MACA,MAAA,EAC+B;AAa/B,EAAA,MAAM,iBAAA,GAA2D;AAAA,IAC/D,IAAA,EAAS,YAAS,IAAI,CAAA;AAAA,IACtB,GAAI;AAAA,GACN;AACA,EAAA,OAAU,UAAO,iBAAiB,CAAA;AACpC;ACnFO,SAAS,cAAA,CACd,QACA,IAAA,EAK4B;AAC5B,EAAA,MAAM,SAAA,GAAY,MAAM,SAAA,IAAa,CAAA;AACrC,EAAA,MAAM,SAAA,GAAY,MAAM,SAAA,IAAa,EAAA;AACrC,EAAA,MAAM,MAAA,GAAS,IAAA,EAAM,MAAA,IAAU,EAAC;AAChC,EAAA,MAAM,OAAUA,EAAA,CAAA,KAAA,CAAM,MAAA,EAAQ,EAAE,SAAA,EAAW,WAAW,CAAA;AACtD,EAAA,IAAI,MAAA,CAAO,MAAA,KAAW,CAAA,EAAG,OAAO,IAAA;AAChC,EAAA,OAAO,IAAA,CAAK,IAAI,CAAC,GAAA,KAAQ,CAAC,GAAG,MAAA,EAAQ,GAAG,GAAG,CAAC,CAAA;AAC9C;AAWO,SAAS,iBAAA,CACd,QAAA,EACA,IAAA,EACA,MAAA,EAGA,KAAA,EAC4B;AAC5B,EAAA,MAAM,IAAI,KAAA;AAAA,IACR;AAAA,GAIF;AACF","file":"chunk-ZRUST6BK.js","sourcesContent":["// ---------------------------------------------------------------------------\n// `stubCtxThrowingProxy<Ctx>()` — a Proxy that satisfies the Ctx type-shape\n// at compile time but throws on every property access at runtime.\n//\n// Why this exists: PBT runs the pure reducer surface ONLY — `replay` never\n// invokes `interpret` and never starts a Sub, so Ctx should never be\n// dereferenced during property generation. But the substrate's `replay`\n// signature requires a `ctx: Ctx` value at compile time (it forwards ctx\n// into `init`, which on some machines IS pure but on others reads ctx). A\n// hand-rolled `{} as Ctx` would silently pass through any accidental ctx\n// access — exactly the silent footgun this proxy catches.\n//\n// Open question 2 (design doc): we ship this as the default. Tests that\n// genuinely need a working ctx (because their `init` reads from it) can\n// pass a real ctx value instead — the proxy is opt-in, not load-bearing.\n//\n// Strengthens invariant 6 (no silent failures — accidental ctx reads\n// produce a useful error, not a `undefined.foo` confusion later).\n// ---------------------------------------------------------------------------\n\n/**\n * Build a Ctx value whose every property access throws. Use as the `ctx`\n * argument to PBT runners when the reducer under test is pure (does not\n * read from ctx). Accidental ctx access during property generation\n * surfaces as a labelled error instead of a silent `undefined` or\n * downstream type-error.\n *\n * Returns a typed `Ctx` (via `as unknown as Ctx`) — the proxy satisfies the\n * type-shape requirement at the call site; the runtime contract is\n * \"throw on first read.\" The `as unknown` cast is the one boundary cast\n * this helper requires; the proxy is structurally a `Ctx` for every\n * property accessor.\n *\n * @example\n * import { stubCtxThrowingProxy } from \"../index\";\n * const ctx = stubCtxThrowingProxy<MyCtx>();\n * propertyTerminates(machine, ctx, ...);\n * // If anything reads ctx.someField during the run, an Error throws:\n * // \"@demlik/tea/pbt: ctx access denied (PBT operates on the pure\n * // reducer; ctx field 'someField' was read)\"\n */\nexport function stubCtxThrowingProxy<Ctx>(): Ctx {\n const handler: ProxyHandler<object> = {\n get(_target, prop: string | symbol): unknown {\n // Allow vitest / fast-check internals to introspect via Symbol-keyed\n // checks (e.g. Symbol.toStringTag, Symbol.iterator). Returning\n // undefined on symbol keys keeps property-tree walks cheap; the\n // throw fires on real domain field reads.\n if (typeof prop === \"symbol\") return undefined;\n // Some shrinkers / pretty-printers in fast-check probe a small set\n // of conventional field names (`toJSON`, `toString`, `valueOf`,\n // `then` — the last for thenable-check). Return undefined for those\n // so the proxy doesn't fail a probe before the property body runs.\n if (prop === \"toJSON\" || prop === \"toString\" || prop === \"valueOf\" || prop === \"then\") {\n return undefined;\n }\n throw new Error(\n `@demlik/tea/pbt: ctx access denied (PBT operates on the pure reducer; ` +\n `ctx field '${prop}' was read). Pass a real ctx value if the reducer's ` +\n `init reads from ctx.`,\n );\n },\n has(): boolean {\n return false;\n },\n ownKeys(): ArrayLike<string | symbol> {\n return [];\n },\n };\n return new Proxy({}, handler) as unknown as Ctx;\n}\n","// ---------------------------------------------------------------------------\n// Msg arbitrary builders.\n//\n// `MsgArbitraryTable<M>` is a strict mapped type — every Msg variant must\n// have an arbitrary entry, keyed by `M[\"type\"]`, narrowing the arbitrary's\n// `T` to `Extract<M, { type: K }>`. This mirrors the load-bearing discipline\n// of `Reducer<S, M, C>` and `Transitions<S, M, C>` in the substrate: adding\n// a Msg variant without an arbitrary fails to compile, not at runtime.\n//\n// `arbMsg(table, opts?)` collapses the table into a single `fc.Arbitrary<M>`\n// via `fc.oneof`. Optional per-variant weighting for sequence-skew tests.\n//\n// `arbConstantMsg(type)` / `arbRecordMsg(type, fields)` are sugar over\n// `fc.constant({ type })` / `fc.record({ type: fc.constant(type), ...fields })`.\n// They exist because every Msg variant arbitrary either (a) carries no\n// payload — `arbConstantMsg(\"stop\")` — or (b) carries a record of fields —\n// `arbRecordMsg(\"tick\", { now: fc.integer() })`. Naming the two shapes\n// prevents callers from hand-rolling `fc.record({ type: fc.constant(\"...\") })`\n// inconsistently across test files.\n//\n// Open question 1 from the design doc (strict vs Partial table): strict by\n// default. The mapped type catches drift at compile time; `arbMsgPartial`\n// sugar is a future addition iff friction becomes real.\n// ---------------------------------------------------------------------------\n\nimport * as fc from \"fast-check\";\n\n/**\n * Strict per-variant Msg arbitrary table. Mapped type: every key in\n * `M[\"type\"]` MUST have an arbitrary entry whose `T` is the narrowed\n * variant. Adding a new Msg variant without an entry is a compile error\n * (same load-bearing discipline as `Reducer<S, M, C>` in the substrate).\n *\n * @example\n * type Msg = { type: \"start\" } | { type: \"tick\"; now: number };\n * const table: MsgArbitraryTable<Msg> = {\n * start: arbConstantMsg(\"start\"),\n * tick: arbRecordMsg(\"tick\", { now: fc.integer({ min: 0 }) }),\n * };\n */\nexport type MsgArbitraryTable<M extends { type: string }> = {\n [K in M[\"type\"]]: fc.Arbitrary<Extract<M, { type: K }>>;\n};\n\n/**\n * Build a single `fc.Arbitrary<M>` from a `MsgArbitraryTable<M>`. Picks one\n * variant per generation via `fc.oneof`; optional per-variant `weights`\n * bias the distribution (default uniform).\n *\n * @param table Per-variant arbitrary table (strict — every variant required).\n * @param opts Optional bias: `weights` is a `Partial<Record<M[\"type\"], number>>`.\n * Variants without an explicit weight default to 1.\n *\n * @example\n * const msgArb = arbMsg<Msg>({\n * start: arbConstantMsg(\"start\"),\n * tick: arbRecordMsg(\"tick\", { now: fc.integer() }),\n * stop: arbConstantMsg(\"stop\"),\n * }, { weights: { tick: 5, start: 1, stop: 1 } });\n */\nexport function arbMsg<M extends { type: string }>(\n table: MsgArbitraryTable<M>,\n opts?: { weights?: Partial<Record<M[\"type\"], number>> },\n): fc.Arbitrary<M> {\n const weights = opts?.weights;\n const entries = Object.entries(table) as [M[\"type\"], fc.Arbitrary<M>][];\n if (entries.length === 0) {\n throw new Error(\"@demlik/tea/pbt: arbMsg table is empty\");\n }\n const weighted = entries.map(([key, arb]) => ({\n arbitrary: arb,\n weight: weights?.[key] ?? 1,\n }));\n return fc.oneof(...weighted);\n}\n\n/**\n * Build a payload-free Msg variant arbitrary. Sugar for\n * `fc.constant({ type })` that narrows the result to `{ type: T }` literal.\n *\n * @example\n * arbConstantMsg(\"stop\") // fc.Arbitrary<{ type: \"stop\" }>\n */\nexport function arbConstantMsg<T extends string>(type: T): fc.Arbitrary<{ type: T }> {\n return fc.constant({ type });\n}\n\n/**\n * Build a record-shaped Msg variant arbitrary. Sugar for\n * `fc.record({ type: fc.constant(type), ...fields })` that stamps the\n * discriminator literal and combines with the per-field arbitraries.\n *\n * @example\n * arbRecordMsg(\"tick\", { now: fc.integer({ min: 0 }) })\n * // fc.Arbitrary<{ type: \"tick\"; now: number }>\n */\nexport function arbRecordMsg<T extends string, R>(\n type: T,\n fields: { [K in keyof R]: fc.Arbitrary<R[K]> },\n): fc.Arbitrary<{ type: T } & R> {\n // `fc.record` builds an arbitrary of the merged-shape record. The\n // `type: fc.constant(type)` field stamps the literal discriminator;\n // the spread of `fields` provides every payload field's arbitrary.\n //\n // The intermediate `as Record<string, fc.Arbitrary<unknown>>` is the\n // single load-bearing cast — fc.record's parameter type is invariant in\n // its inferred field shape, and the merged literal-and-generic spread\n // doesn't unify with the strict `{ [K in keyof R]: Arbitrary<R[K]> }`\n // shape TS computes for us. The outer cast to `fc.Arbitrary<{ type: T }\n // & R>` restates the result shape we know to be correct at this\n // boundary. Mirrors the same discipline `fc.record` callers use\n // throughout the prior-art branch.\n const recordArbitraries: Record<string, fc.Arbitrary<unknown>> = {\n type: fc.constant(type),\n ...(fields as Record<string, fc.Arbitrary<unknown>>),\n };\n return fc.record(recordArbitraries) as unknown as fc.Arbitrary<{ type: T } & R>;\n}\n","// ---------------------------------------------------------------------------\n// Msg-sequence arbitraries.\n//\n// `arbMsgSequence(msgArb, opts)` produces `fc.Arbitrary<readonly M[]>` —\n// fast-check's default-shrinker handles the array; the per-element shrinker\n// inside `msgArb` shrinks each variant. Optional `prefix` is prepended\n// verbatim (used by Test A: \"the bug-triggering Msg leads the sequence\").\n//\n// `arbGuidedSequence` is the state-aware sequence builder. In v1 it throws\n// — Phase 6 of the design doc lands the `fc.commands`-backed implementation.\n// The export exists today so the surface is forward-compatible: consumers\n// that need state-aware generation get a useful error pointing at the\n// design doc instead of \"is not a function.\"\n// ---------------------------------------------------------------------------\n\nimport type { Cmd, Machine, Sub } from \"../../index\";\nimport * as fc from \"fast-check\";\n\n/**\n * Build an arbitrary of Msg sequences over a per-Msg arbitrary. Optionally\n * bounded in length and / or prefixed with a fixed sequence.\n *\n * - `minLength` (default 0) and `maxLength` (default 30) bound the random\n * array length. `prefix` is prepended AFTER the random tail is built; the\n * final length is `prefix.length + tail.length`.\n * - `prefix` is the load-bearing escape hatch for \"the first msg is the\n * one that exercises the cell I care about\" (e.g. Test A's\n * `[{ type: \"ws:client:hello\" }]` prefix to land in the\n * `auditing.ws:client:hello` cell on the first transition).\n *\n * @example\n * arbMsgSequence(msgArb, { minLength: 1, maxLength: 30 });\n * arbMsgSequence(msgArb, { prefix: [{ type: \"ws:client:hello\" }], maxLength: 20 });\n */\nexport function arbMsgSequence<M extends { type: string }>(\n msgArb: fc.Arbitrary<M>,\n opts?: {\n readonly minLength?: number;\n readonly maxLength?: number;\n readonly prefix?: readonly NoInfer<M>[];\n },\n): fc.Arbitrary<readonly M[]> {\n const minLength = opts?.minLength ?? 0;\n const maxLength = opts?.maxLength ?? 30;\n const prefix = opts?.prefix ?? [];\n const tail = fc.array(msgArb, { minLength, maxLength });\n if (prefix.length === 0) return tail;\n return tail.map((arr) => [...prefix, ...arr]);\n}\n\n/**\n * State-aware Msg sequence arbitrary — generates only msgs whose\n * precondition holds in the current state. Backed by `fc.commands`\n * internally (Phase 6 of `.patterns/tea/tea-pbt-design.md`).\n *\n * **NOT IMPLEMENTED IN v1.** The export exists today so the surface is\n * forward-compatible; the design doc §7 ships this in Phase 6 alongside\n * shrinkers. Calling it throws.\n */\nexport function arbGuidedSequence<S, M extends { type: string }, C extends Cmd, U extends Sub, Ctx>(\n _machine: Machine<S, M, C, U, Ctx>,\n _ctx: Ctx,\n _cells: {\n [K in M[\"type\"]]?: (state: S) => fc.Arbitrary<Extract<M, { type: K }>> | null;\n },\n _opts?: { maxLength?: number },\n): fc.Arbitrary<readonly M[]> {\n throw new Error(\n \"@demlik/tea/pbt: arbGuidedSequence is not implemented in v1 — see \" +\n \".patterns/tea/tea-pbt-design.md §7 (Phase 6). Use arbMsgSequence with \" +\n \"an unguided arbMsg table for now; states that don't accept a given \" +\n \"msg should noop in the reducer (mapped-type discipline).\",\n );\n}\n"]}