@demlik/tea 0.13.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (372) hide show
  1. package/README.md +11 -9
  2. package/dist/agent/index.d.ts +637 -45
  3. package/dist/agent/index.js +9 -19
  4. package/dist/{chunk-25LBDCBC.js → chunk-6IQPODUF.js} +2 -2
  5. package/dist/{chunk-25LBDCBC.js.map → chunk-6IQPODUF.js.map} +1 -1
  6. package/dist/chunk-AQ6ZIMEA.js +421 -0
  7. package/dist/chunk-AQ6ZIMEA.js.map +1 -0
  8. package/dist/{chunk-MUUROGIH.js → chunk-BLJ2JQSH.js} +62 -12
  9. package/dist/chunk-BLJ2JQSH.js.map +1 -0
  10. package/dist/{chunk-RH5EFRVJ.js → chunk-F2US5BW6.js} +25 -5
  11. package/dist/chunk-F2US5BW6.js.map +1 -0
  12. package/dist/{batch-window/index.js → chunk-F7FMAWLA.js} +4 -8
  13. package/dist/chunk-F7FMAWLA.js.map +1 -0
  14. package/dist/{chunk-O4MVQTEJ.js → chunk-HUKZQX6U.js} +20 -16
  15. package/dist/chunk-HUKZQX6U.js.map +1 -0
  16. package/dist/{chunk-EWJP3YYP.js → chunk-HWVW4AXC.js} +3 -3
  17. package/dist/chunk-HWVW4AXC.js.map +1 -0
  18. package/dist/{chunk-DMYGQB4O.js → chunk-HYDWG35Q.js} +23 -6
  19. package/dist/chunk-HYDWG35Q.js.map +1 -0
  20. package/dist/{chunk-YUXP55T2.js → chunk-I5MQMJDW.js} +57 -7
  21. package/dist/chunk-I5MQMJDW.js.map +1 -0
  22. package/dist/{chunk-J6527VRQ.js → chunk-LFZUK6J2.js} +3 -3
  23. package/dist/chunk-LFZUK6J2.js.map +1 -0
  24. package/dist/{chunk-ZVJZ72HY.js → chunk-LYLTKJWM.js} +25 -11
  25. package/dist/chunk-LYLTKJWM.js.map +1 -0
  26. package/dist/{chunk-D736QUGF.js → chunk-MYXFNWAJ.js} +4 -5
  27. package/dist/chunk-MYXFNWAJ.js.map +1 -0
  28. package/dist/{chunk-K575PT4S.js → chunk-PR6TB3BL.js} +15 -4
  29. package/dist/chunk-PR6TB3BL.js.map +1 -0
  30. package/dist/chunk-RQULHGYH.js +1804 -0
  31. package/dist/chunk-RQULHGYH.js.map +1 -0
  32. package/dist/{chunk-FV3Z5MBE.js → chunk-TXN2KFHF.js} +14 -4
  33. package/dist/chunk-TXN2KFHF.js.map +1 -0
  34. package/dist/{chunk-Q2UM7BXU.js → chunk-VBDU4JL6.js} +2 -2
  35. package/dist/chunk-VBDU4JL6.js.map +1 -0
  36. package/dist/chunk-Z2L3LSNV.js +1470 -0
  37. package/dist/chunk-Z2L3LSNV.js.map +1 -0
  38. package/dist/{core-Co0LWkIA.d.ts → core-CTzMJ82M.d.ts} +72 -23
  39. package/dist/devtools/index.d.ts +2 -2
  40. package/dist/devtools/index.js +207 -2
  41. package/dist/devtools/index.js.map +1 -1
  42. package/dist/do/index.d.ts +42 -29
  43. package/dist/do/index.js +36 -25
  44. package/dist/do/index.js.map +1 -1
  45. package/dist/{durable-effects-C6uwzjb5.d.ts → durable-effects-B0-qGQDy.d.ts} +2 -2
  46. package/dist/extension/index.d.ts +300 -3
  47. package/dist/extension/index.js +596 -5
  48. package/dist/extension/index.js.map +1 -1
  49. package/dist/flow/index.d.ts +1295 -0
  50. package/dist/flow/index.js +732 -0
  51. package/dist/flow/index.js.map +1 -0
  52. package/dist/idempotency/index.d.ts +90 -116
  53. package/dist/idempotency/index.js +4 -1
  54. package/dist/{batch-window/index.d.ts → index-4vZzljCD.d.ts} +7 -12
  55. package/dist/{fan-out/index.d.ts → index-6A7dB8f6.d.ts} +7 -9
  56. package/dist/index-B5BZ-_cT.d.ts +616 -0
  57. package/dist/index-Bc40JWVl.d.ts +131 -0
  58. package/dist/{debounce/index.d.ts → index-DHSUmQTR.d.ts} +7 -7
  59. package/dist/index-DJWIER_3.d.ts +69 -0
  60. package/dist/{snapshot/index.d.ts → index-DMCeMPMJ.d.ts} +15 -78
  61. package/dist/{monitored-run/index.d.ts → index-NjrT3fL2.d.ts} +37 -13
  62. package/dist/{recorder/index.d.ts → index-TvRb1KVf.d.ts} +4 -7
  63. package/dist/{deadline/index.d.ts → index-q0hXFeZ0.d.ts} +155 -9
  64. package/dist/index.d.ts +711 -6
  65. package/dist/index.js +1 -2
  66. package/dist/jev/index.d.ts +634 -0
  67. package/dist/jev/index.js +433 -0
  68. package/dist/jev/index.js.map +1 -0
  69. package/dist/machine-CdIcN2Xm.d.ts +1773 -0
  70. package/dist/machine-viz/index.d.ts +1 -1
  71. package/dist/machine-viz/index.js +12 -5
  72. package/dist/machine-viz/index.js.map +1 -1
  73. package/dist/mem/index.d.ts +18 -3
  74. package/dist/mem/index.js +22 -2
  75. package/dist/mem/index.js.map +1 -1
  76. package/dist/node/index.d.ts +104 -6
  77. package/dist/node/index.js +132 -9
  78. package/dist/node/index.js.map +1 -1
  79. package/dist/{paginator → paginate}/index.d.ts +151 -6
  80. package/dist/paginate/index.js +7 -0
  81. package/dist/parity/index.d.ts +4 -10
  82. package/dist/parity/index.js +4 -7
  83. package/dist/parity/index.js.map +1 -1
  84. package/dist/pbt/index.d.ts +247 -5
  85. package/dist/pbt/index.js +124 -5
  86. package/dist/pbt/index.js.map +1 -1
  87. package/dist/{trace-replay → persistence}/index.d.ts +7 -5
  88. package/dist/persistence/index.js +6 -0
  89. package/dist/react/index.d.ts +2 -2
  90. package/dist/react/index.js +1 -2
  91. package/dist/react/index.js.map +1 -1
  92. package/dist/resilience/index.d.ts +743 -0
  93. package/dist/resilience/index.js +657 -0
  94. package/dist/resilience/index.js.map +1 -0
  95. package/dist/retry-backoff/index.d.ts +3 -2
  96. package/dist/retry-backoff/index.js +1 -1
  97. package/dist/runtime-types-LWDoi943.d.ts +998 -0
  98. package/dist/testing/index.d.ts +2 -2
  99. package/dist/testing/index.js +1 -2
  100. package/dist/testing/index.js.map +1 -1
  101. package/dist/{throttled-input → timing}/index.d.ts +143 -12
  102. package/dist/{throttled-input → timing}/index.js +57 -12
  103. package/dist/timing/index.js.map +1 -0
  104. package/dist/{types-XS_miRnB.d.ts → types-DP3f5NIl.d.ts} +1 -1
  105. package/dist/work-queue/index.d.ts +130 -54
  106. package/dist/work-queue/index.js +3 -2
  107. package/dist/work-queue/index.js.map +1 -1
  108. package/package.json +45 -218
  109. package/dist/authed-call/index.d.ts +0 -120
  110. package/dist/authed-call/index.js +0 -134
  111. package/dist/authed-call/index.js.map +0 -1
  112. package/dist/await-terminal/index.d.ts +0 -101
  113. package/dist/await-terminal/index.js +0 -76
  114. package/dist/await-terminal/index.js.map +0 -1
  115. package/dist/batch-window/index.js.map +0 -1
  116. package/dist/cache/index.d.ts +0 -152
  117. package/dist/cache/index.js +0 -8
  118. package/dist/chart/index.d.ts +0 -218
  119. package/dist/chart/index.js +0 -16
  120. package/dist/chart/index.js.map +0 -1
  121. package/dist/chart/inspect/index.d.ts +0 -86
  122. package/dist/chart/inspect/index.js +0 -5
  123. package/dist/chart/inspect/react.d.ts +0 -113
  124. package/dist/chart/inspect/react.js +0 -11
  125. package/dist/chart/inspect/react.js.map +0 -1
  126. package/dist/chart/inspect/styles.css +0 -286
  127. package/dist/chart/lane/index.d.ts +0 -362
  128. package/dist/chart/lane/index.js +0 -9
  129. package/dist/chart/lane/index.js.map +0 -1
  130. package/dist/chart/lane/react.d.ts +0 -84
  131. package/dist/chart/lane/react.js +0 -559
  132. package/dist/chart/lane/react.js.map +0 -1
  133. package/dist/chart/lane/server.d.ts +0 -148
  134. package/dist/chart/lane/server.js +0 -209
  135. package/dist/chart/lane/server.js.map +0 -1
  136. package/dist/chart/lane/styles.css +0 -498
  137. package/dist/chart/lane/viewer/assets/abnfDiagram-N423BO3Z-C729LI31.js +0 -1
  138. package/dist/chart/lane/viewer/assets/architectureDiagram-T3A2C74G-C729LI31.js +0 -1
  139. package/dist/chart/lane/viewer/assets/blockDiagram-VBNYF7ZC-C729LI31.js +0 -1
  140. package/dist/chart/lane/viewer/assets/c4Diagram-5PPSVZJV-C729LI31.js +0 -1
  141. package/dist/chart/lane/viewer/assets/chunk-2GRJ4B5K-HdDf8SI3.js +0 -1
  142. package/dist/chart/lane/viewer/assets/chunk-5RXB4S5H-CcNtpUd7.js +0 -231
  143. package/dist/chart/lane/viewer/assets/chunk-5VM5RSS4-BP4fRS_5.js +0 -15
  144. package/dist/chart/lane/viewer/assets/chunk-KBJHAD2P-DZOw7xU6.js +0 -1
  145. package/dist/chart/lane/viewer/assets/chunk-NSK5VX7P-CBCyTZMs.js +0 -128
  146. package/dist/chart/lane/viewer/assets/classDiagram-JCYQIIEL-C729LI31.js +0 -1
  147. package/dist/chart/lane/viewer/assets/classDiagram-v2-OCEON4UE-D4uAiFr8.js +0 -206
  148. package/dist/chart/lane/viewer/assets/cose-bilkent-JH36ORCC-DOwCrGXH.js +0 -1
  149. package/dist/chart/lane/viewer/assets/cynefinDiagram-MW4NZA55-C729LI31.js +0 -1
  150. package/dist/chart/lane/viewer/assets/dagre-K_PewWfS.js +0 -1
  151. package/dist/chart/lane/viewer/assets/dagre-VZM6K2ZE-831qZcA8.js +0 -4
  152. package/dist/chart/lane/viewer/assets/diagram-7IWD3JNH-C729LI31.js +0 -1
  153. package/dist/chart/lane/viewer/assets/diagram-B4RE2ZJO-C729LI31.js +0 -1
  154. package/dist/chart/lane/viewer/assets/diagram-LBJQPF4R-C729LI31.js +0 -1
  155. package/dist/chart/lane/viewer/assets/diagram-Q27KOJAE-C729LI31.js +0 -1
  156. package/dist/chart/lane/viewer/assets/diagram-UB23O5K3-C729LI31.js +0 -1
  157. package/dist/chart/lane/viewer/assets/ebnfDiagram-BXEA7PRR-C729LI31.js +0 -1
  158. package/dist/chart/lane/viewer/assets/erDiagram-JOGREHBK-C729LI31.js +0 -1
  159. package/dist/chart/lane/viewer/assets/flowDiagram-UKHOOZJN-C729LI31.js +0 -1
  160. package/dist/chart/lane/viewer/assets/ganttDiagram-PKOTCBZU-C729LI31.js +0 -1
  161. package/dist/chart/lane/viewer/assets/gitGraphDiagram-DS77QQ5N-C729LI31.js +0 -1
  162. package/dist/chart/lane/viewer/assets/index-9SkH5NA3.css +0 -1
  163. package/dist/chart/lane/viewer/assets/index-Bgt4sXpW.js +0 -195
  164. package/dist/chart/lane/viewer/assets/infoDiagram-6WML65LV-C729LI31.js +0 -1
  165. package/dist/chart/lane/viewer/assets/ishikawaDiagram-WSZJBQD7-C729LI31.js +0 -1
  166. package/dist/chart/lane/viewer/assets/journeyDiagram-NVQOT4AX-C729LI31.js +0 -1
  167. package/dist/chart/lane/viewer/assets/kanban-definition-27J2QSJJ-G7PaPzy-.js +0 -89
  168. package/dist/chart/lane/viewer/assets/katex-DOwCrGXH.js +0 -1
  169. package/dist/chart/lane/viewer/assets/mindmap-definition-FAOFIHXS-CGj9nrVk.js +0 -96
  170. package/dist/chart/lane/viewer/assets/path-fybaL0A-.js +0 -1
  171. package/dist/chart/lane/viewer/assets/pegDiagram-VL7TDLO6-C729LI31.js +0 -1
  172. package/dist/chart/lane/viewer/assets/pieDiagram-7S7Q4E2Y-C729LI31.js +0 -1
  173. package/dist/chart/lane/viewer/assets/quadrantDiagram-CIZ2JOQS-C729LI31.js +0 -1
  174. package/dist/chart/lane/viewer/assets/railroadDiagram-AXF67PYL-C729LI31.js +0 -1
  175. package/dist/chart/lane/viewer/assets/requirementDiagram-LRYGKXZP-C729LI31.js +0 -1
  176. package/dist/chart/lane/viewer/assets/sankeyDiagram-W5VNT64P-C729LI31.js +0 -1
  177. package/dist/chart/lane/viewer/assets/sequenceDiagram-SI44F4Z6-C729LI31.js +0 -1
  178. package/dist/chart/lane/viewer/assets/stateDiagram-OKZ733FA-B_88AzBS.js +0 -1
  179. package/dist/chart/lane/viewer/assets/stateDiagram-v2-UEYNNEHI-CR5gcgq6.js +0 -1
  180. package/dist/chart/lane/viewer/assets/swimlanes-SLNWSIFB-B4xK-rwg.js +0 -1
  181. package/dist/chart/lane/viewer/assets/swimlanesDiagram-ULZ7WXOC-C729LI31.js +0 -1
  182. package/dist/chart/lane/viewer/assets/timeline-definition-Z64GVDOM-CCv52MfY.js +0 -120
  183. package/dist/chart/lane/viewer/assets/vennDiagram-T6HMQDX7-C729LI31.js +0 -1
  184. package/dist/chart/lane/viewer/assets/wardleyDiagram-T6FBY63Y-C729LI31.js +0 -1
  185. package/dist/chart/lane/viewer/assets/xychartDiagram-ELKLHX3M-C729LI31.js +0 -1
  186. package/dist/chart/lane/viewer/index.html +0 -15
  187. package/dist/chart/report/index.d.ts +0 -117
  188. package/dist/chart/report/index.js +0 -32
  189. package/dist/chart/report/index.js.map +0 -1
  190. package/dist/chunk-2YFLOCWX.js +0 -15
  191. package/dist/chunk-2YFLOCWX.js.map +0 -1
  192. package/dist/chunk-3I52QZZ5.js +0 -519
  193. package/dist/chunk-3I52QZZ5.js.map +0 -1
  194. package/dist/chunk-3ZAFFRRB.js +0 -457
  195. package/dist/chunk-3ZAFFRRB.js.map +0 -1
  196. package/dist/chunk-4EQZ76JC.js +0 -33
  197. package/dist/chunk-4EQZ76JC.js.map +0 -1
  198. package/dist/chunk-4KLG43GY.js +0 -223
  199. package/dist/chunk-4KLG43GY.js.map +0 -1
  200. package/dist/chunk-7FRZ2CT2.js +0 -424
  201. package/dist/chunk-7FRZ2CT2.js.map +0 -1
  202. package/dist/chunk-AZZLE7XR.js +0 -62
  203. package/dist/chunk-AZZLE7XR.js.map +0 -1
  204. package/dist/chunk-BYQUGDD3.js +0 -16
  205. package/dist/chunk-BYQUGDD3.js.map +0 -1
  206. package/dist/chunk-CIQG5L42.js +0 -685
  207. package/dist/chunk-CIQG5L42.js.map +0 -1
  208. package/dist/chunk-CLM6ERNO.js +0 -61
  209. package/dist/chunk-CLM6ERNO.js.map +0 -1
  210. package/dist/chunk-COS5MSTO.js +0 -88
  211. package/dist/chunk-COS5MSTO.js.map +0 -1
  212. package/dist/chunk-CRPZJVD2.js +0 -71
  213. package/dist/chunk-CRPZJVD2.js.map +0 -1
  214. package/dist/chunk-D62FDQKE.js +0 -53
  215. package/dist/chunk-D62FDQKE.js.map +0 -1
  216. package/dist/chunk-D736QUGF.js.map +0 -1
  217. package/dist/chunk-DGE4PV4A.js +0 -26
  218. package/dist/chunk-DGE4PV4A.js.map +0 -1
  219. package/dist/chunk-DMYGQB4O.js.map +0 -1
  220. package/dist/chunk-EWJP3YYP.js.map +0 -1
  221. package/dist/chunk-FV3Z5MBE.js.map +0 -1
  222. package/dist/chunk-HCEVHFJ7.js +0 -54
  223. package/dist/chunk-HCEVHFJ7.js.map +0 -1
  224. package/dist/chunk-J6527VRQ.js.map +0 -1
  225. package/dist/chunk-JELSZN25.js +0 -47
  226. package/dist/chunk-JELSZN25.js.map +0 -1
  227. package/dist/chunk-K575PT4S.js.map +0 -1
  228. package/dist/chunk-L57ZOT5K.js +0 -184
  229. package/dist/chunk-L57ZOT5K.js.map +0 -1
  230. package/dist/chunk-MU6MCIAC.js +0 -15
  231. package/dist/chunk-MU6MCIAC.js.map +0 -1
  232. package/dist/chunk-MUUROGIH.js.map +0 -1
  233. package/dist/chunk-NR7BU22N.js +0 -758
  234. package/dist/chunk-NR7BU22N.js.map +0 -1
  235. package/dist/chunk-O4MVQTEJ.js.map +0 -1
  236. package/dist/chunk-OOYD44JI.js +0 -317
  237. package/dist/chunk-OOYD44JI.js.map +0 -1
  238. package/dist/chunk-Q2UM7BXU.js.map +0 -1
  239. package/dist/chunk-RH5EFRVJ.js.map +0 -1
  240. package/dist/chunk-S2EGRPCM.js +0 -47
  241. package/dist/chunk-S2EGRPCM.js.map +0 -1
  242. package/dist/chunk-SINXE47Y.js +0 -55
  243. package/dist/chunk-SINXE47Y.js.map +0 -1
  244. package/dist/chunk-UF4MZSLT.js +0 -53
  245. package/dist/chunk-UF4MZSLT.js.map +0 -1
  246. package/dist/chunk-UOC2PL46.js +0 -13
  247. package/dist/chunk-UOC2PL46.js.map +0 -1
  248. package/dist/chunk-VYFAZN3F.js +0 -372
  249. package/dist/chunk-VYFAZN3F.js.map +0 -1
  250. package/dist/chunk-WRAA7GVZ.js +0 -209
  251. package/dist/chunk-WRAA7GVZ.js.map +0 -1
  252. package/dist/chunk-XGELA764.js +0 -163
  253. package/dist/chunk-XGELA764.js.map +0 -1
  254. package/dist/chunk-YUXP55T2.js.map +0 -1
  255. package/dist/chunk-YWIKDKHY.js +0 -307
  256. package/dist/chunk-YWIKDKHY.js.map +0 -1
  257. package/dist/chunk-ZJ5AOYBH.js +0 -8
  258. package/dist/chunk-ZJ5AOYBH.js.map +0 -1
  259. package/dist/chunk-ZSUUOEPL.js +0 -731
  260. package/dist/chunk-ZSUUOEPL.js.map +0 -1
  261. package/dist/chunk-ZVJZ72HY.js.map +0 -1
  262. package/dist/circuit-breaker/index.d.ts +0 -197
  263. package/dist/circuit-breaker/index.js +0 -3
  264. package/dist/circuit-breaker/index.js.map +0 -1
  265. package/dist/deadline/index.js +0 -6
  266. package/dist/deadline/index.js.map +0 -1
  267. package/dist/debounce/index.js +0 -3
  268. package/dist/debounce/index.js.map +0 -1
  269. package/dist/extension/react.d.ts +0 -121
  270. package/dist/extension/react.js +0 -75
  271. package/dist/extension/react.js.map +0 -1
  272. package/dist/extension/subs/index.d.ts +0 -45
  273. package/dist/extension/subs/index.js +0 -80
  274. package/dist/extension/subs/index.js.map +0 -1
  275. package/dist/extension/test-utils.d.ts +0 -128
  276. package/dist/extension/test-utils.js +0 -277
  277. package/dist/extension/test-utils.js.map +0 -1
  278. package/dist/fan-out/index.js +0 -5
  279. package/dist/fan-out/index.js.map +0 -1
  280. package/dist/fold-BJPdjc9w.d.ts +0 -435
  281. package/dist/graph-B3L5e0N1.d.ts +0 -923
  282. package/dist/idempotency/adapter.d.ts +0 -62
  283. package/dist/idempotency/adapter.js +0 -4
  284. package/dist/idempotency/adapter.js.map +0 -1
  285. package/dist/idempotent-intake/index.d.ts +0 -188
  286. package/dist/idempotent-intake/index.js +0 -7
  287. package/dist/idempotent-intake/index.js.map +0 -1
  288. package/dist/journal/index.d.ts +0 -373
  289. package/dist/journal/index.js +0 -3
  290. package/dist/journal/index.js.map +0 -1
  291. package/dist/llm-call/index.d.ts +0 -311
  292. package/dist/llm-call/index.js +0 -16
  293. package/dist/llm-call/index.js.map +0 -1
  294. package/dist/machine-BtFWQmmq.d.ts +0 -804
  295. package/dist/monitored-run/index.js +0 -10
  296. package/dist/monitored-run/index.js.map +0 -1
  297. package/dist/paginated-walk/index.d.ts +0 -235
  298. package/dist/paginated-walk/index.js +0 -16
  299. package/dist/paginated-walk/index.js.map +0 -1
  300. package/dist/paginator/index.js +0 -3
  301. package/dist/paginator/index.js.map +0 -1
  302. package/dist/pbt/arbitraries/index.d.ts +0 -136
  303. package/dist/pbt/arbitraries/index.js +0 -3
  304. package/dist/pbt/arbitraries/index.js.map +0 -1
  305. package/dist/pbt/runners/index.d.ts +0 -118
  306. package/dist/pbt/runners/index.js +0 -5
  307. package/dist/pbt/runners/index.js.map +0 -1
  308. package/dist/poller/index.d.ts +0 -352
  309. package/dist/poller/index.js +0 -115
  310. package/dist/poller/index.js.map +0 -1
  311. package/dist/prediction/index.d.ts +0 -131
  312. package/dist/prediction/index.js +0 -4
  313. package/dist/prediction/index.js.map +0 -1
  314. package/dist/protocol-B5YmJr0d.d.ts +0 -45
  315. package/dist/pure/index.d.ts +0 -3
  316. package/dist/pure/index.js +0 -4
  317. package/dist/pure/index.js.map +0 -1
  318. package/dist/rate-limit/index.d.ts +0 -115
  319. package/dist/rate-limit/index.js +0 -3
  320. package/dist/rate-limit/index.js.map +0 -1
  321. package/dist/reconciler/index.d.ts +0 -193
  322. package/dist/reconciler/index.js +0 -164
  323. package/dist/reconciler/index.js.map +0 -1
  324. package/dist/recorder/index.js +0 -3
  325. package/dist/recorder/index.js.map +0 -1
  326. package/dist/reducer-4GNR6b8B.d.ts +0 -447
  327. package/dist/resilient-call/index.d.ts +0 -320
  328. package/dist/resilient-call/index.js +0 -14
  329. package/dist/resilient-call/index.js.map +0 -1
  330. package/dist/retry-to-success/index.d.ts +0 -109
  331. package/dist/retry-to-success/index.js +0 -37
  332. package/dist/retry-to-success/index.js.map +0 -1
  333. package/dist/run-pAcmBpah.d.ts +0 -713
  334. package/dist/runtime-types-CNLmhWif.d.ts +0 -517
  335. package/dist/saga/index.d.ts +0 -241
  336. package/dist/saga/index.js +0 -150
  337. package/dist/saga/index.js.map +0 -1
  338. package/dist/samples-B3My5oh2.d.ts +0 -59
  339. package/dist/snapshot/index.js +0 -5
  340. package/dist/snapshot/index.js.map +0 -1
  341. package/dist/subs/index.d.ts +0 -472
  342. package/dist/subs/index.js +0 -7
  343. package/dist/subs/index.js.map +0 -1
  344. package/dist/throttle/index.d.ts +0 -134
  345. package/dist/throttle/index.js +0 -3
  346. package/dist/throttle/index.js.map +0 -1
  347. package/dist/throttled-input/index.js.map +0 -1
  348. package/dist/token-refresh/index.d.ts +0 -216
  349. package/dist/token-refresh/index.js +0 -6
  350. package/dist/token-refresh/index.js.map +0 -1
  351. package/dist/trace-replay/index.js +0 -6
  352. package/dist/trace-replay/index.js.map +0 -1
  353. package/dist/with-deadline/index.d.ts +0 -197
  354. package/dist/with-deadline/index.js +0 -111
  355. package/dist/with-deadline/index.js.map +0 -1
  356. package/dist/with-resilience/index.d.ts +0 -225
  357. package/dist/with-resilience/index.js +0 -240
  358. package/dist/with-resilience/index.js.map +0 -1
  359. package/dist/with-telemetry/index.d.ts +0 -150
  360. package/dist/with-telemetry/index.js +0 -70
  361. package/dist/with-telemetry/index.js.map +0 -1
  362. package/dist/work-queue/adapter.d.ts +0 -84
  363. package/dist/work-queue/adapter.js +0 -4
  364. package/dist/work-queue/adapter.js.map +0 -1
  365. package/dist/work-queue/ops.d.ts +0 -71
  366. package/dist/work-queue/ops.js +0 -3
  367. package/dist/work-queue/ops.js.map +0 -1
  368. package/dist/workflow/index.d.ts +0 -485
  369. package/dist/workflow/index.js +0 -244
  370. package/dist/workflow/index.js.map +0 -1
  371. /package/dist/{cache → paginate}/index.js.map +0 -0
  372. /package/dist/{chart/inspect → persistence}/index.js.map +0 -0
@@ -1,4 +1,4 @@
1
- import { C as Cmd, S as Sub, M as Machine } from '../core-Co0LWkIA.js';
1
+ import { C as Cmd, S as Sub, M as Machine } from '../core-CTzMJ82M.js';
2
2
  import 'zod';
3
3
 
4
4
  /**
@@ -1,7 +1,14 @@
1
- import { safeId, safeLabel } from '../chunk-UOC2PL46.js';
2
- export { safeId, safeLabel } from '../chunk-UOC2PL46.js';
3
- import '../chunk-ZSUUOEPL.js';
4
- import { formOf } from '../chunk-OOYD44JI.js';
1
+ import { formOf } from '../chunk-RQULHGYH.js';
2
+
3
+ // src/machine-viz/mermaid-id.ts
4
+ function safeId(raw) {
5
+ const cleaned = raw.replace(/[^A-Za-z0-9_]/g, "_");
6
+ if (cleaned === "") return "_empty";
7
+ return /^[0-9]/.test(cleaned) ? `s_${cleaned}` : cleaned;
8
+ }
9
+ function safeLabel(raw) {
10
+ return raw.replace(/["\n\r;:]/g, " ").trim();
11
+ }
5
12
 
6
13
  // src/machine-viz/index.ts
7
14
  function toMermaid(machine, opts = {}) {
@@ -124,6 +131,6 @@ function emitSubscriptions(machine, lines, stateSamples) {
124
131
  }
125
132
  }
126
133
 
127
- export { toMermaid };
134
+ export { safeId, safeLabel, toMermaid };
128
135
  //# sourceMappingURL=index.js.map
129
136
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/machine-viz/index.ts"],"names":[],"mappings":";;;;;;AA6FO,SAAS,SAAA,CAOd,OAAA,EAKA,IAAA,GAAkD,EAAC,EAC3C;AACR,EAAA,MAAM,SAAA,GAAY,KAAK,SAAA,IAAa,IAAA;AACpC,EAAA,MAAM,YAAA,GAAe,IAAA,CAAK,OAAA,EAAS,MAAA,IAAU,EAAC;AAC9C,EAAA,MAAM,UAAA,GAAa,IAAA,CAAK,OAAA,EAAS,IAAA,IAAQ,EAAC;AAC1C,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,OAAA,KAAY,MAAA,IAAa,SAAS,IAAA,CAAK,OAAA;AAE3D,EAAA,MAAM,IAAA,GAAO,OAAO,OAAO,CAAA;AAC3B,EAAA,MAAM,QAAkB,EAAC;AAGzB,EAAA,IAAI,IAAA,CAAK,UAAU,MAAA,EAAW;AAC5B,IAAA,KAAA,CAAM,KAAK,KAAA,EAAO,CAAA,OAAA,EAAU,IAAA,CAAK,KAAK,IAAI,KAAK,CAAA;AAAA,EACjD;AACA,EAAA,KAAA,CAAM,KAAK,iBAAiB,CAAA;AAC5B,EAAA,KAAA,CAAM,IAAA,CAAK,CAAA,YAAA,EAAe,SAAS,CAAA,CAAE,CAAA;AAErC,EAAA,IAAI,SAAS,SAAA,EAAW;AACtB,IAAA,WAAA,CAAY,SAAS,KAAK,CAAA;AAAA,EAC5B,CAAA,MAAO;AACL,IAAA,eAAA,CAAgB,SAAS,KAAA,EAAO;AAAA,MAC9B,YAAA;AAAA,MACA,UAAA;AAAA,MACA,MAAA;AAAA,MACA,GAAA,EAAK,KAAK,OAAA,EAAS;AAAA,KACpB,CAAA;AAAA,EACH;AAKA,EAAA,iBAAA,CAAkB,OAAA,EAAS,OAAO,YAAY,CAAA;AAE9C,EAAA,OAAO,KAAA,CAAM,KAAK,IAAI,CAAA;AACxB;AASA,SAAS,WAAA,CAAY,SAA8B,KAAA,EAAuB;AACxE,EAAA,MAAM,QAAA,GAAW,MAAA,CAAO,IAAA,CAAK,OAAA,CAAQ,MAAgB,CAAA;AACrD,EAAA,KAAA,CAAM,KAAK,0BAA0B,CAAA;AACrC,EAAA,IAAI,QAAA,CAAS,WAAW,CAAA,EAAG;AACzB,IAAA,KAAA,CAAM,KAAK,oBAAoB,CAAA;AAC/B,IAAA,KAAA,CAAM,KAAK,oDAA+C,CAAA;AAAA,EAC5D,CAAA,MAAO;AACL,IAAA,KAAA,MAAW,KAAK,QAAA,EAAU;AACxB,MAAA,KAAA,CAAM,IAAA,CAAK,OAAO,MAAA,CAAO,CAAC,CAAC,CAAA,GAAA,EAAM,SAAA,CAAU,CAAC,CAAC,CAAA,CAAE,CAAA;AAAA,IACjD;AAAA,EACF;AACA,EAAA,KAAA,CAAM,KAAK,KAAK,CAAA;AAGhB,EAAA,KAAA,CAAM,KAAK,gCAAgC,CAAA;AAC3C,EAAA,KAAA,CAAM,KAAK,4DAA4D,CAAA;AACvE,EAAA,KAAA,CAAM,IAAA;AAAA,IACJ;AAAA,GACF;AACA,EAAA,KAAA,CAAM,IAAA,CAAK,CAAA,uBAAA,EAA0B,QAAA,CAAS,MAAM,CAAA,CAAE,CAAA;AACtD,EAAA,KAAA,CAAM,KAAK,YAAY,CAAA;AACzB;AAgBA,SAAS,eAAA,CAOP,OAAA,EACA,KAAA,EACA,GAAA,EACM;AAGN,EAAA,MAAM,QAAQ,OAAA,CAAQ,MAAA;AACtB,EAAA,MAAM,UAAA,GAAa,MAAA,CAAO,IAAA,CAAK,KAAK,CAAA;AAGpC,EAAA,KAAA,MAAW,MAAM,UAAA,EAAY;AAC3B,IAAA,KAAA,CAAM,IAAA,CAAK,CAAA,EAAA,EAAK,MAAA,CAAO,EAAE,CAAC,CAAA,CAAE,CAAA;AAAA,EAC9B;AAIA,EAAA,IAAI,IAAI,MAAA,EAAQ;AACd,IAAA,MAAM,CAAC,SAAS,CAAA,GAAI,QAAQ,IAAA,CAAK,IAAA,EAAM,IAAI,GAAU,CAAA;AACrD,IAAA,MAAM,WAAY,SAAA,CAA0C,IAAA;AAC5D,IAAA,KAAA,CAAM,IAAA,CAAK,CAAA,UAAA,EAAa,MAAA,CAAO,QAAQ,CAAC,CAAA,CAAE,CAAA;AAAA,EAC5C,CAAA,MAAO;AACL,IAAA,KAAA,CAAM,IAAA;AAAA,MACJ;AAAA,KACF;AAAA,EACF;AAEA,EAAA,IAAI,aAAA,GAAgB,KAAA;AACpB,EAAA,KAAA,MAAW,MAAM,UAAA,EAAY;AAC3B,IAAA,MAAM,GAAA,GAAM,MAAM,EAAE,CAAA;AACpB,IAAA,IAAI,QAAQ,MAAA,EAAW;AACvB,IAAA,MAAM,QAAA,GAAW,MAAA,CAAO,IAAA,CAAK,GAAG,CAAA;AAChC,IAAA,KAAA,MAAW,MAAM,QAAA,EAAU;AACzB,MAAA,MAAM,IAAA,GAAO,WAAA,CAAY,GAAA,EAAK,EAAA,EAAI,IAAI,GAAG,CAAA;AACzC,MAAA,IAAI,IAAA,CAAK,SAAS,UAAA,EAAY;AAC5B,QAAA,KAAA,CAAM,IAAA;AAAA,UACJ,CAAA,EAAA,EAAK,MAAA,CAAO,IAAA,CAAK,IAAI,CAAC,CAAA,KAAA,EAAQ,MAAA,CAAO,IAAA,CAAK,EAAE,CAAC,CAAA,GAAA,EAAM,SAAA,CAAU,IAAA,CAAK,GAAG,CAAC,CAAA;AAAA,SACxE;AAAA,MACF,CAAA,MAAO;AACL,QAAA,aAAA,GAAgB,IAAA;AAGhB,QAAA,KAAA,CAAM,IAAA;AAAA,UACJ,CAAA,EAAA,EAAK,MAAA,CAAO,IAAA,CAAK,IAAI,CAAC,CAAA,KAAA,EAAQ,MAAA,CAAO,IAAA,CAAK,IAAI,CAAC,CAAA,GAAA,EAAM,SAAA,CAAU,IAAA,CAAK,GAAG,CAAC,CAAA,CAAA;AAAA,SAC1E;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,EAAA,KAAA,CAAM,IAAA,CAAK,mBAAmB,MAAA,CAAO,UAAA,CAAW,CAAC,CAAA,IAAK,QAAQ,CAAC,CAAA,CAAE,CAAA;AACjE,EAAA,KAAA,CAAM,KAAK,+BAA+B,CAAA;AAC1C,EAAA,KAAA,CAAM,KAAK,iEAAiE,CAAA;AAC5E,EAAA,IAAI,aAAA,EAAe;AACjB,IAAA,KAAA,CAAM,IAAA;AAAA,MACJ;AAAA,KACF;AAAA,EACF;AACA,EAAA,KAAA,CAAM,KAAK,YAAY,CAAA;AACzB;AASA,SAAS,WAAA,CACP,GAAA,EACA,SAAA,EACA,OAAA,EACA,GAAA,EAIM;AACN,EAAA,MAAM,WAAA,GAAc,GAAA,CAAI,YAAA,CAAa,SAAS,CAAA;AAC9C,EAAA,MAAM,SAAA,GAAY,GAAA,CAAI,UAAA,CAAW,OAAO,CAAA;AACxC,EAAA,MAAM,IAAA,GAAO,IAAI,OAAO,CAAA;AACxB,EAAA,IACE,WAAA,KAAgB,MAAA,IAChB,SAAA,KAAc,MAAA,IACd,SAAS,MAAA,EACT;AACA,IAAA,OAAO,EAAE,IAAA,EAAM,YAAA,EAAc,IAAA,EAAM,SAAA,EAAW,KAAK,OAAA,EAAQ;AAAA,EAC7D;AACA,EAAA,IAAI;AACF,IAAA,MAAM,CAAC,IAAI,CAAA,GAAI,IAAA,CAAK,aAAa,SAAS,CAAA;AAC1C,IAAA,MAAM,KAAM,IAAA,CAAqC,IAAA;AACjD,IAAA,OAAO,EAAE,IAAA,EAAM,UAAA,EAAY,MAAM,SAAA,EAAW,EAAA,EAAI,KAAK,OAAA,EAAQ;AAAA,EAC/D,CAAA,CAAA,MAAQ;AAEN,IAAA,OAAO,EAAE,IAAA,EAAM,YAAA,EAAc,IAAA,EAAM,SAAA,EAAW,KAAK,OAAA,EAAQ;AAAA,EAC7D;AACF;AAWA,SAAS,iBAAA,CAOP,OAAA,EACA,KAAA,EACA,YAAA,EACM;AACN,EAAA,IAAI,OAAA,CAAQ,kBAAkB,MAAA,EAAW;AACzC,EAAA,MAAM,UAAA,GAAa,MAAA,CAAO,IAAA,CAAK,YAAY,CAAA;AAC3C,EAAA,IAAI,UAAA,CAAW,WAAW,CAAA,EAAG;AAE7B,EAAA,KAAA,MAAW,MAAM,UAAA,EAAY;AAC3B,IAAA,MAAM,MAAA,GAAS,aAAa,EAAE,CAAA;AAC9B,IAAA,IAAI,WAAW,MAAA,EAAW;AAC1B,IAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,aAAA,CAAc,MAAM,CAAA;AACzC,IAAA,MAAM,WAAW,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,IAAI,CAAA;AACvC,IAAA,MAAM,IAAA,GACJ,QAAA,CAAS,MAAA,KAAW,CAAA,GAChB,kBAAA,GACA,CAAA,MAAA,EAAS,QAAA,CAAS,GAAA,CAAI,SAAS,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA;AACjD,IAAA,KAAA,CAAM,IAAA,CAAK,CAAA,eAAA,EAAkB,MAAA,CAAO,EAAE,CAAC,CAAA,CAAE,CAAA;AACzC,IAAA,KAAA,CAAM,IAAA,CAAK,CAAA,IAAA,EAAO,IAAI,CAAA,CAAE,CAAA;AACxB,IAAA,KAAA,CAAM,KAAK,YAAY,CAAA;AAAA,EACzB;AACF","file":"index.js","sourcesContent":["/**\n * @demlik/tea/machine-viz — turn a `Machine` into a Mermaid diagram string.\n *\n * PURE. No deps, no AST parsing, no file I/O. The function reads only the\n * `Machine` value it is handed plus optional sample states/msgs/ctx.\n *\n * The trick: TEA update cells are PURE (invariant 2 — they return\n * `[nextState, cmds]` as data, no effects). So we can SAFELY EXECUTE a cell to\n * discover its target state — exactly what `replay` does. That gives us REAL\n * edges (`red --> green : go`) without parsing source. When no sample is\n * provided for a `(state, msg)` pair we cannot resolve the target, so we fall\n * back to a STRUCTURAL self-edge annotated with `?` — visually distinct from a\n * resolved edge and documented in the diagram's legend.\n *\n * Form detection is `formOf(machine)` — the same reader `run`/`replay` use,\n * honoring the authoritative `__form` tag `defineMachine` stamps (falling back\n * to the structural heuristic for machines built without it; #275).\n *\n * What we MUST NOT do (these are the effectful paths, off-limits here):\n * - call `interpret[*]` handlers (perform I/O)\n * - call `subscribe[*]` handlers (install listeners)\n * - call `run` / the runtime\n * We MAY call `init`, `update[*]` cells, and `subscriptions(state)` — all pure\n * by the invariants (same set `replay` is allowed to touch).\n */\n\nimport type { Cmd, Machine, Sub } from \"../index\";\nimport { formOf } from \"../index\";\nimport { safeId, safeLabel } from \"./mermaid-id\";\n\n// The sanitizers are SHARED with `chart`'s `chartMermaid` — one copy, in a\n// dependency-free module, re-exported here because both drawings are public\n// and a consumer post-processing either one needs the same escaping.\nexport { safeId, safeLabel } from \"./mermaid-id\";\n\n// === Public options ===\n\n/**\n * Options controlling diagram emission.\n *\n * `samples` is the lever that turns STRUCTURAL edges into RESOLVED edges. For a\n * Transitions machine, providing a sample state for `stateType` AND a sample\n * msg for `msgType` lets us execute `update[stateType][msgType](state, msg)` and\n * read the real target `next.type`. Sample maps are keyed by the discriminant\n * string (`state.type` / `msg.type`).\n *\n * `samples.ctx` is the argument to `init(null, ctx)` — provide it to draw the\n * `[*] -->` initial-state edge. Omitted ⇒ no init edge (annotated as such),\n * because `init`'s signature requires a ctx and we will not invent one.\n */\nexport interface MachineVizOptions<S, M> {\n samples?: {\n /** Sample state per `state.type` — unlocks RESOLVED edges + sub annotations. */\n states?: Partial<Record<string, S>>;\n /** Sample msg per `msg.type` — unlocks RESOLVED edges. */\n msgs?: Partial<Record<string, M>>;\n /** Ctx for `init(null, ctx)` — unlocks the `[*] -->` initial-state edge. */\n ctx?: unknown;\n };\n /** Mermaid layout direction. Default `\"TB\"` (top-to-bottom). */\n direction?: \"TB\" | \"LR\";\n /** Optional diagram title, emitted as a Mermaid front-matter `title:` block. */\n title?: string;\n}\n\n// === Edge model ===\n\ntype Edge =\n // Resolved: we executed the cell with samples and read the real target.\n | { kind: \"resolved\"; from: string; to: string; msg: string }\n // Structural: no samples for the pair → target unknown, drawn as a self-loop.\n | { kind: \"structural\"; from: string; msg: string };\n\n// === toMermaid ===\n\n/**\n * Render a `Machine` as a Mermaid `stateDiagram-v2` string.\n *\n * - **Transitions form** (`update` is a record-of-records): nodes are the\n * `state.type` keys; each `(stateType × msgType)` cell becomes an edge. With\n * samples for both, the edge is RESOLVED by executing the (pure) cell. Without\n * samples it is STRUCTURAL — a `from --> from : msg?` self-loop (the `?`\n * suffix marks \"target unresolved\"). A legend note documents the distinction.\n * The `[*] -->` init edge is drawn only when `samples.ctx` is provided.\n *\n * - **Reducer form** (`update` values are functions): there is no static phase\n * graph (state is not a discriminated union over phases). We emit a single\n * node listing the handled `msg.type`s and an honest note saying so. No edges\n * are invented.\n *\n * Output is deterministic for the same input (keys iterated in insertion order,\n * no clocks, no randomness).\n */\nexport function toMermaid<\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 // `NoInfer` pins S/M to the `machine` argument so sample object literals in\n // `opts` are CHECKED against the machine's State/Msg unions rather than\n // driving inference (which would widen `{ type: \"open\" }` to `{ type: string }`\n // and collide with the machine's discriminated union).\n opts: MachineVizOptions<NoInfer<S>, NoInfer<M>> = {},\n): string {\n const direction = opts.direction ?? \"TB\";\n const stateSamples = opts.samples?.states ?? {};\n const msgSamples = opts.samples?.msgs ?? {};\n const hasCtx = opts.samples !== undefined && \"ctx\" in opts.samples;\n\n const mode = formOf(machine);\n const lines: string[] = [];\n\n // Front matter — Mermaid reads a YAML `title:` block before the diagram.\n if (opts.title !== undefined) {\n lines.push(\"---\", `title: ${opts.title}`, \"---\");\n }\n lines.push(\"stateDiagram-v2\");\n lines.push(` direction ${direction}`);\n\n if (mode === \"reducer\") {\n emitReducer(machine, lines);\n } else {\n emitTransitions(machine, lines, {\n stateSamples,\n msgSamples,\n hasCtx,\n ctx: opts.samples?.ctx,\n });\n }\n\n // Subscriptions annotation — pure call to `subscriptions(sampleState)` per\n // sampled state. Skipped entirely when no state samples are provided (we\n // refuse to invent a state to feed it).\n emitSubscriptions(machine, lines, stateSamples);\n\n return lines.join(\"\\n\");\n}\n\n// === Reducer-form emission ===\n\n/**\n * Reducer form has no phase graph. Emit one node listing the handled msg types\n * plus an honest note. The msg set is the `update` record's keys (the Reducer\n * mapped type guarantees one cell per Msg variant).\n */\nfunction emitReducer(machine: { update: unknown }, lines: string[]): void {\n const msgTypes = Object.keys(machine.update as object);\n lines.push(\" state ReducerMachine {\");\n if (msgTypes.length === 0) {\n lines.push(\" [*] --> NoMsgs\");\n lines.push(' NoMsgs : \"(no msg variants — M is never)\"');\n } else {\n for (const m of msgTypes) {\n lines.push(` ${safeId(m)} : ${safeLabel(m)}`);\n }\n }\n lines.push(\" }\");\n // The note IS the design honesty: no edges, because reducer-form state is not\n // a discriminated union over phases — there is no static target to draw.\n lines.push(\" note right of ReducerMachine\");\n lines.push(\" Reducer-form machine: flat dispatch keyed by msg.type.\");\n lines.push(\n \" No phase graph — state is not a discriminated union over phases.\",\n );\n lines.push(` Handled msg types: ${msgTypes.length}`);\n lines.push(\" end note\");\n}\n\n// === Transitions-form emission ===\n\ninterface TransitionsCtx<S, M> {\n stateSamples: Partial<Record<string, S>>;\n msgSamples: Partial<Record<string, M>>;\n hasCtx: boolean;\n ctx: unknown;\n}\n\n/**\n * Transitions form: walk the `update[stateType][msgType]` table. Execute the\n * (pure) cell when both samples exist → RESOLVED edge; otherwise → STRUCTURAL\n * self-loop. Draw the `[*] -->` init edge only when a ctx sample is given.\n */\nfunction emitTransitions<\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 lines: string[],\n ctx: TransitionsCtx<S, M>,\n): void {\n type CellFn = (state: S, msg: M) => readonly [S, readonly C[]];\n type Table = Record<string, Record<string, CellFn>>;\n const table = machine.update as unknown as Table;\n const stateTypes = Object.keys(table);\n\n // Declare every node up front so even unreachable phases render.\n for (const st of stateTypes) {\n lines.push(` ${safeId(st)}`);\n }\n\n // Init edge — only resolvable with a ctx sample. `init(null, ctx)` is pure\n // (invariant 2: fresh-boot branch returns a starting state). Read `next.type`.\n if (ctx.hasCtx) {\n const [initState] = machine.init(null, ctx.ctx as Ctx);\n const initType = (initState as unknown as { type: string }).type;\n lines.push(` [*] --> ${safeId(initType)}`);\n } else {\n lines.push(\n \" %% init edge omitted: no samples.ctx provided to run init(null, ctx)\",\n );\n }\n\n let anyStructural = false;\n for (const st of stateTypes) {\n const row = table[st];\n if (row === undefined) continue;\n const msgTypes = Object.keys(row);\n for (const mt of msgTypes) {\n const edge = resolveEdge(row, st, mt, ctx);\n if (edge.kind === \"resolved\") {\n lines.push(\n ` ${safeId(edge.from)} --> ${safeId(edge.to)} : ${safeLabel(edge.msg)}`,\n );\n } else {\n anyStructural = true;\n // Structural self-loop with a `?` suffix — target unresolved. Visually\n // distinct from a resolved edge (self-loop + trailing `?`).\n lines.push(\n ` ${safeId(edge.from)} --> ${safeId(edge.from)} : ${safeLabel(edge.msg)}?`,\n );\n }\n }\n }\n\n // Legend — document the resolved-vs-structural distinction in the output.\n lines.push(` note right of ${safeId(stateTypes[0] ?? \"_empty\")}`);\n lines.push(\" Transitions-form machine.\");\n lines.push(\" Edge a --> b : msg = RESOLVED (cell executed with samples).\");\n if (anyStructural) {\n lines.push(\n \" Self-loop a --> a : msg? = STRUCTURAL (no sample; target unresolved).\",\n );\n }\n lines.push(\" end note\");\n}\n\n/**\n * Resolve a single `(stateType, msgType)` cell into an Edge. With both a sample\n * state and a sample msg, EXECUTE the pure cell to read the real target. Any\n * missing sample → structural. A thrown cell (e.g. a sample shaped wrong for\n * the cell) degrades to structural rather than crashing the whole diagram —\n * the diagram is a best-effort visualization, not a correctness gate.\n */\nfunction resolveEdge<S, M extends { type: string }, C extends Cmd>(\n row: Record<string, (state: S, msg: M) => readonly [S, readonly C[]]>,\n stateType: string,\n msgType: string,\n ctx: {\n stateSamples: Partial<Record<string, S>>;\n msgSamples: Partial<Record<string, M>>;\n },\n): Edge {\n const sampleState = ctx.stateSamples[stateType];\n const sampleMsg = ctx.msgSamples[msgType];\n const cell = row[msgType];\n if (\n sampleState === undefined ||\n sampleMsg === undefined ||\n cell === undefined\n ) {\n return { kind: \"structural\", from: stateType, msg: msgType };\n }\n try {\n const [next] = cell(sampleState, sampleMsg);\n const to = (next as unknown as { type: string }).type;\n return { kind: \"resolved\", from: stateType, to, msg: msgType };\n } catch {\n // Mismatched sample → cannot resolve. Fall back to structural.\n return { kind: \"structural\", from: stateType, msg: msgType };\n }\n}\n\n// === Subscriptions annotation ===\n\n/**\n * For each sampled state, call the (pure) `subscriptions(state)` description\n * function and annotate which Sub types are active in that state as a Mermaid\n * note. We call `subscriptions` (the description) but NEVER `subscribe[*]` (the\n * effectful installer) — same boundary `replay` honors. Skipped when no state\n * samples are provided or the machine declares no `subscriptions`.\n */\nfunction emitSubscriptions<\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 lines: string[],\n stateSamples: Partial<Record<string, S>>,\n): void {\n if (machine.subscriptions === undefined) return;\n const stateTypes = Object.keys(stateSamples);\n if (stateTypes.length === 0) return;\n\n for (const st of stateTypes) {\n const sample = stateSamples[st];\n if (sample === undefined) continue;\n const subs = machine.subscriptions(sample);\n const subTypes = subs.map((s) => s.type);\n const body =\n subTypes.length === 0\n ? \"(no active subs)\"\n : `subs: ${subTypes.map(safeLabel).join(\", \")}`;\n lines.push(` note left of ${safeId(st)}`);\n lines.push(` ${body}`);\n lines.push(\" end note\");\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/machine-viz/mermaid-id.ts","../../src/machine-viz/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// ═══════════════════════════════════════════════════════════════════════════\n// MERMAID SANITIZING — the two functions every drawing in this package shares.\n//\n// Mermaid state ids must be identifier-safe, and a transition label must not\n// contain the characters that terminate it. A state name like\n// `human:cp-approval` breaks the diagram on both counts.\n//\n// So it lives here, in a module with NO imports, rather than inside\n// `machine-viz/index.ts` — that one pulls in the whole substrate (`formOf`\n// from `../index`), and a future drawing must not acquire that dependency\n// merely to sanitize a string.\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * Sanitize a discriminant string into a Mermaid-safe node identifier.\n *\n * Any run of non-identifier characters becomes `_`; a leading digit is\n * prefixed so the id is always a valid Mermaid token; the empty string gets a\n * stable placeholder. Deterministic and idempotent — `safeId(safeId(x))` is\n * `safeId(x)`, so a diagram may sanitize the same name at several sites.\n */\nexport function safeId(raw: string): string {\n const cleaned = raw.replace(/[^A-Za-z0-9_]/g, \"_\");\n if (cleaned === \"\") return \"_empty\";\n return /^[0-9]/.test(cleaned) ? `s_${cleaned}` : cleaned;\n}\n\n/**\n * Sanitize text used as a Mermaid edge/transition label (the part after `:`).\n *\n * Strips the characters that terminate or confuse a label: newlines, the `:`\n * that separates label from target, and Mermaid's `\"`/`;` delimiters.\n */\nexport function safeLabel(raw: string): string {\n return raw.replace(/[\"\\n\\r;:]/g, \" \").trim();\n}\n","/**\n * @demlik/tea/machine-viz — turn a `Machine` into a Mermaid diagram string.\n *\n * PURE. No deps, no AST parsing, no file I/O. The function reads only the\n * `Machine` value it is handed plus optional sample states/msgs/ctx.\n *\n * The trick: TEA update cells are PURE (invariant 2 — they return\n * `[nextState, cmds]` as data, no effects). So we can SAFELY EXECUTE a cell to\n * discover its target state — exactly what `replay` does. That gives us REAL\n * edges (`red --> green : go`) without parsing source. When no sample is\n * provided for a `(state, msg)` pair we cannot resolve the target, so we fall\n * back to a STRUCTURAL self-edge annotated with `?` — visually distinct from a\n * resolved edge and documented in the diagram's legend.\n *\n * Form detection is `formOf(machine)` — the same reader `run`/`replay` use,\n * honoring the authoritative `__form` tag `defineMachine` stamps (falling back\n * to the structural heuristic for machines built without it; #275).\n *\n * What we MUST NOT do (these are the effectful paths, off-limits here):\n * - call `interpret[*]` handlers (perform I/O)\n * - call `subscribe[*]` handlers (install listeners)\n * - call `run` / the runtime\n * We MAY call `init`, `update[*]` cells, and `subscriptions(state)` — all pure\n * by the invariants (same set `replay` is allowed to touch).\n */\n\nimport type { Cmd, Machine, Sub } from \"../index\";\nimport { formOf } from \"../index\";\nimport { safeId, safeLabel } from \"./mermaid-id\";\n\n// The sanitizers live in a dependency-free module and are re-exported here,\n// because a consumer post-processing this drawing needs the same escaping.\nexport { safeId, safeLabel } from \"./mermaid-id\";\n\n// === Public options ===\n\n/**\n * Options controlling diagram emission.\n *\n * `samples` is the lever that turns STRUCTURAL edges into RESOLVED edges. For a\n * Transitions machine, providing a sample state for `stateType` AND a sample\n * msg for `msgType` lets us execute `update[stateType][msgType](state, msg)` and\n * read the real target `next.type`. Sample maps are keyed by the discriminant\n * string (`state.type` / `msg.type`).\n *\n * `samples.ctx` is the argument to `init(null, ctx)` — provide it to draw the\n * `[*] -->` initial-state edge. Omitted ⇒ no init edge (annotated as such),\n * because `init`'s signature requires a ctx and we will not invent one.\n */\nexport interface MachineVizOptions<S, M> {\n samples?: {\n /** Sample state per `state.type` — unlocks RESOLVED edges + sub annotations. */\n states?: Partial<Record<string, S>>;\n /** Sample msg per `msg.type` — unlocks RESOLVED edges. */\n msgs?: Partial<Record<string, M>>;\n /** Ctx for `init(null, ctx)` — unlocks the `[*] -->` initial-state edge. */\n ctx?: unknown;\n };\n /** Mermaid layout direction. Default `\"TB\"` (top-to-bottom). */\n direction?: \"TB\" | \"LR\";\n /** Optional diagram title, emitted as a Mermaid front-matter `title:` block. */\n title?: string;\n}\n\n// === Edge model ===\n\ntype Edge =\n // Resolved: we executed the cell with samples and read the real target.\n | { kind: \"resolved\"; from: string; to: string; msg: string }\n // Structural: no samples for the pair → target unknown, drawn as a self-loop.\n | { kind: \"structural\"; from: string; msg: string };\n\n// === toMermaid ===\n\n/**\n * Render a `Machine` as a Mermaid `stateDiagram-v2` string.\n *\n * - **Transitions form** (`update` is a record-of-records): nodes are the\n * `state.type` keys; each `(stateType × msgType)` cell becomes an edge. With\n * samples for both, the edge is RESOLVED by executing the (pure) cell. Without\n * samples it is STRUCTURAL — a `from --> from : msg?` self-loop (the `?`\n * suffix marks \"target unresolved\"). A legend note documents the distinction.\n * The `[*] -->` init edge is drawn only when `samples.ctx` is provided.\n *\n * - **Reducer form** (`update` values are functions): there is no static phase\n * graph (state is not a discriminated union over phases). We emit a single\n * node listing the handled `msg.type`s and an honest note saying so. No edges\n * are invented.\n *\n * Output is deterministic for the same input (keys iterated in insertion order,\n * no clocks, no randomness).\n */\nexport function toMermaid<\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 // `NoInfer` pins S/M to the `machine` argument so sample object literals in\n // `opts` are CHECKED against the machine's State/Msg unions rather than\n // driving inference (which would widen `{ type: \"open\" }` to `{ type: string }`\n // and collide with the machine's discriminated union).\n opts: MachineVizOptions<NoInfer<S>, NoInfer<M>> = {},\n): string {\n const direction = opts.direction ?? \"TB\";\n const stateSamples = opts.samples?.states ?? {};\n const msgSamples = opts.samples?.msgs ?? {};\n const hasCtx = opts.samples !== undefined && \"ctx\" in opts.samples;\n\n const mode = formOf(machine);\n const lines: string[] = [];\n\n // Front matter — Mermaid reads a YAML `title:` block before the diagram.\n if (opts.title !== undefined) {\n lines.push(\"---\", `title: ${opts.title}`, \"---\");\n }\n lines.push(\"stateDiagram-v2\");\n lines.push(` direction ${direction}`);\n\n if (mode === \"reducer\") {\n emitReducer(machine, lines);\n } else {\n emitTransitions(machine, lines, {\n stateSamples,\n msgSamples,\n hasCtx,\n ctx: opts.samples?.ctx,\n });\n }\n\n // Subscriptions annotation — pure call to `subscriptions(sampleState)` per\n // sampled state. Skipped entirely when no state samples are provided (we\n // refuse to invent a state to feed it).\n emitSubscriptions(machine, lines, stateSamples);\n\n return lines.join(\"\\n\");\n}\n\n// === Reducer-form emission ===\n\n/**\n * Reducer form has no phase graph. Emit one node listing the handled msg types\n * plus an honest note. The msg set is the `update` record's keys (the Reducer\n * mapped type guarantees one cell per Msg variant).\n */\nfunction emitReducer(machine: { update: unknown }, lines: string[]): void {\n const msgTypes = Object.keys(machine.update as object);\n lines.push(\" state ReducerMachine {\");\n if (msgTypes.length === 0) {\n lines.push(\" [*] --> NoMsgs\");\n lines.push(' NoMsgs : \"(no msg variants — M is never)\"');\n } else {\n for (const m of msgTypes) {\n lines.push(` ${safeId(m)} : ${safeLabel(m)}`);\n }\n }\n lines.push(\" }\");\n // The note IS the design honesty: no edges, because reducer-form state is not\n // a discriminated union over phases — there is no static target to draw.\n lines.push(\" note right of ReducerMachine\");\n lines.push(\" Reducer-form machine: flat dispatch keyed by msg.type.\");\n lines.push(\n \" No phase graph — state is not a discriminated union over phases.\",\n );\n lines.push(` Handled msg types: ${msgTypes.length}`);\n lines.push(\" end note\");\n}\n\n// === Transitions-form emission ===\n\ninterface TransitionsCtx<S, M> {\n stateSamples: Partial<Record<string, S>>;\n msgSamples: Partial<Record<string, M>>;\n hasCtx: boolean;\n ctx: unknown;\n}\n\n/**\n * Transitions form: walk the `update[stateType][msgType]` table. Execute the\n * (pure) cell when both samples exist → RESOLVED edge; otherwise → STRUCTURAL\n * self-loop. Draw the `[*] -->` init edge only when a ctx sample is given.\n */\nfunction emitTransitions<\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 lines: string[],\n ctx: TransitionsCtx<S, M>,\n): void {\n type CellFn = (state: S, msg: M) => readonly [S, readonly C[]];\n type Table = Record<string, Record<string, CellFn>>;\n const table = machine.update as unknown as Table;\n const stateTypes = Object.keys(table);\n\n // Declare every node up front so even unreachable phases render.\n for (const st of stateTypes) {\n lines.push(` ${safeId(st)}`);\n }\n\n // Init edge — only resolvable with a ctx sample. `init(null, ctx)` is pure\n // (invariant 2: fresh-boot branch returns a starting state). Read `next.type`.\n if (ctx.hasCtx) {\n const [initState] = machine.init(null, ctx.ctx as Ctx);\n const initType = (initState as unknown as { type: string }).type;\n lines.push(` [*] --> ${safeId(initType)}`);\n } else {\n lines.push(\n \" %% init edge omitted: no samples.ctx provided to run init(null, ctx)\",\n );\n }\n\n let anyStructural = false;\n for (const st of stateTypes) {\n const row = table[st];\n if (row === undefined) continue;\n const msgTypes = Object.keys(row);\n for (const mt of msgTypes) {\n const edge = resolveEdge(row, st, mt, ctx);\n if (edge.kind === \"resolved\") {\n lines.push(\n ` ${safeId(edge.from)} --> ${safeId(edge.to)} : ${safeLabel(edge.msg)}`,\n );\n } else {\n anyStructural = true;\n // Structural self-loop with a `?` suffix — target unresolved. Visually\n // distinct from a resolved edge (self-loop + trailing `?`).\n lines.push(\n ` ${safeId(edge.from)} --> ${safeId(edge.from)} : ${safeLabel(edge.msg)}?`,\n );\n }\n }\n }\n\n // Legend — document the resolved-vs-structural distinction in the output.\n lines.push(` note right of ${safeId(stateTypes[0] ?? \"_empty\")}`);\n lines.push(\" Transitions-form machine.\");\n lines.push(\" Edge a --> b : msg = RESOLVED (cell executed with samples).\");\n if (anyStructural) {\n lines.push(\n \" Self-loop a --> a : msg? = STRUCTURAL (no sample; target unresolved).\",\n );\n }\n lines.push(\" end note\");\n}\n\n/**\n * Resolve a single `(stateType, msgType)` cell into an Edge. With both a sample\n * state and a sample msg, EXECUTE the pure cell to read the real target. Any\n * missing sample → structural. A thrown cell (e.g. a sample shaped wrong for\n * the cell) degrades to structural rather than crashing the whole diagram —\n * the diagram is a best-effort visualization, not a correctness gate.\n */\nfunction resolveEdge<S, M extends { type: string }, C extends Cmd>(\n row: Record<string, (state: S, msg: M) => readonly [S, readonly C[]]>,\n stateType: string,\n msgType: string,\n ctx: {\n stateSamples: Partial<Record<string, S>>;\n msgSamples: Partial<Record<string, M>>;\n },\n): Edge {\n const sampleState = ctx.stateSamples[stateType];\n const sampleMsg = ctx.msgSamples[msgType];\n const cell = row[msgType];\n if (\n sampleState === undefined ||\n sampleMsg === undefined ||\n cell === undefined\n ) {\n return { kind: \"structural\", from: stateType, msg: msgType };\n }\n try {\n const [next] = cell(sampleState, sampleMsg);\n const to = (next as unknown as { type: string }).type;\n return { kind: \"resolved\", from: stateType, to, msg: msgType };\n } catch {\n // Mismatched sample → cannot resolve. Fall back to structural.\n return { kind: \"structural\", from: stateType, msg: msgType };\n }\n}\n\n// === Subscriptions annotation ===\n\n/**\n * For each sampled state, call the (pure) `subscriptions(state)` description\n * function and annotate which Sub types are active in that state as a Mermaid\n * note. We call `subscriptions` (the description) but NEVER `subscribe[*]` (the\n * effectful installer) — same boundary `replay` honors. Skipped when no state\n * samples are provided or the machine declares no `subscriptions`.\n */\nfunction emitSubscriptions<\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 lines: string[],\n stateSamples: Partial<Record<string, S>>,\n): void {\n if (machine.subscriptions === undefined) return;\n const stateTypes = Object.keys(stateSamples);\n if (stateTypes.length === 0) return;\n\n for (const st of stateTypes) {\n const sample = stateSamples[st];\n if (sample === undefined) continue;\n const subs = machine.subscriptions(sample);\n const subTypes = subs.map((s) => s.type);\n const body =\n subTypes.length === 0\n ? \"(no active subs)\"\n : `subs: ${subTypes.map(safeLabel).join(\", \")}`;\n lines.push(` note left of ${safeId(st)}`);\n lines.push(` ${body}`);\n lines.push(\" end note\");\n }\n}\n"]}
@@ -1,6 +1,6 @@
1
- import { S as Store } from '../runtime-types-CNLmhWif.js';
1
+ import { S as Store, F as FencedStore } from '../runtime-types-LWDoi943.js';
2
2
  import 'better-result';
3
- import '../core-Co0LWkIA.js';
3
+ import '../core-CTzMJ82M.js';
4
4
  import 'zod';
5
5
 
6
6
  /**
@@ -33,6 +33,17 @@ import 'zod';
33
33
  * may pass an explicit `parse` to override.
34
34
  */
35
35
 
36
+ /** Options for {@link memoryStore}. */
37
+ interface MemoryStoreOptions {
38
+ /**
39
+ * Refuse a second live writer (#143). With `{ fenced: true }` the returned
40
+ * store is a `FencedStore<S>` whose version is an in-process counter — honest
41
+ * for this adapter, since the cell it guards is in this process too. Two
42
+ * `run`s handed the SAME `memoryStore` are the case it catches, and it is the
43
+ * cheapest way to exercise the fenced path in a test.
44
+ */
45
+ readonly fenced?: true;
46
+ }
36
47
  /**
37
48
  * Build an in-memory `Store<S>`.
38
49
  *
@@ -45,5 +56,9 @@ import 'zod';
45
56
  * the test wants to reject seeded shapes that don't match `S`.
46
57
  */
47
58
  declare function memoryStore<S>(initial?: S | null, parse?: (raw: unknown) => S | null): Store<S>;
59
+ declare function memoryStore<S>(initial: S | null | undefined, parse: ((raw: unknown) => S | null) | undefined, options: MemoryStoreOptions & {
60
+ readonly fenced: true;
61
+ }): FencedStore<S>;
62
+ declare function memoryStore<S>(initial?: S | null, parse?: (raw: unknown) => S | null, options?: MemoryStoreOptions): Store<S> | FencedStore<S>;
48
63
 
49
- export { memoryStore };
64
+ export { type MemoryStoreOptions, memoryStore };
package/dist/mem/index.js CHANGED
@@ -1,17 +1,37 @@
1
+ import { StoreConflictError } from '../chunk-RQULHGYH.js';
2
+
1
3
  // src/mem/index.ts
2
- function memoryStore(initial, parse = (raw) => raw) {
4
+ function memoryStore(initial, parse = (raw) => raw, options = {}) {
3
5
  let cell = initial ?? null;
4
- return {
6
+ let version = 0;
7
+ const base = {
5
8
  async load() {
6
9
  return cell;
7
10
  },
8
11
  async save(state) {
9
12
  cell = state;
13
+ version += 1;
10
14
  },
11
15
  migrate(raw) {
12
16
  return parse(raw);
13
17
  }
14
18
  };
19
+ if (options.fenced !== true) return base;
20
+ return {
21
+ ...base,
22
+ fenced: true,
23
+ async loadFenced() {
24
+ return { raw: cell, version };
25
+ },
26
+ async saveFenced(state, expectedVersion) {
27
+ if (version !== expectedVersion) {
28
+ throw new StoreConflictError(expectedVersion, version);
29
+ }
30
+ cell = state;
31
+ version += 1;
32
+ return version;
33
+ }
34
+ };
15
35
  }
16
36
 
17
37
  export { memoryStore };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/mem/index.ts"],"names":[],"mappings":";AA2CO,SAAS,WAAA,CACd,OAAA,EACA,KAAA,GAAoC,CAAC,QAAQ,GAAA,EACnC;AAGV,EAAA,IAAI,OAAiB,OAAA,IAAW,IAAA;AAEhC,EAAA,OAAO;AAAA,IACL,MAAM,IAAA,GAAyB;AAC7B,MAAA,OAAO,IAAA;AAAA,IACT,CAAA;AAAA,IACA,MAAM,KAAK,KAAA,EAAyB;AAClC,MAAA,IAAA,GAAO,KAAA;AAAA,IACT,CAAA;AAAA,IACA,QAAQ,GAAA,EAAwB;AAC9B,MAAA,OAAO,MAAM,GAAG,CAAA;AAAA,IAClB;AAAA,GACF;AACF","file":"index.js","sourcesContent":["/**\n * @packageDocumentation\n * @demlik/tea/mem — in-memory `Store<S>` adapter for `@demlik/tea`.\n *\n * The standard test adapter — used everywhere a Worker or DO would be\n * overkill. Mirrors the source-only convention from `packages/widget-engine`\n * and `packages/tea` (no build step).\n *\n * Reference semantics, NOT deep-clone semantics:\n * - `save(state)` stores the reference; subsequent mutations to that\n * reference by the caller are observable through later `load()` calls.\n * - `load()` returns the stored reference; mutations made through it are\n * observable on the next `load()`.\n *\n * This matches what a real KV/DO `get()` would NOT do (those serialize +\n * deserialize), but it also matches what an in-memory cache obviously does.\n * Adding a deep clone here would lie about the boundary and impose a JSON\n * round-trip on every test. Tests that need fresh copies should clone at the\n * call site.\n *\n * Boundary parse (invariant 8): `Store<S>` requires `migrate(raw)`. For\n * `memoryStore` we ship a permissive identity-default — `(raw) => raw as\n * S | null` — because this adapter is a degenerate same-process boundary:\n * there is no serialization, the reference we read is structurally the\n * reference we wrote. The `as` cast here is the documented exception to the\n * invariant-8 no-`as` rule, scoped to the parameter default. Callers that\n * want strict parsing (e.g. a test that hand-seeds the cell from outside)\n * may pass an explicit `parse` to override.\n */\n\nimport type { Store } from \"../index\";\n\n/**\n * Build an in-memory `Store<S>`.\n *\n * @param initial - optional seed value. `undefined` and explicit `null` both\n * mean \"no seed\"; `load()` resolves with `null` until the first `save()`.\n * A concrete `S` seed is returned by `load()` until overwritten.\n * @param parse - optional boundary parse for the `Store<S>.migrate` contract.\n * Defaults to identity (`(raw) => raw as S | null`) — honest for this\n * adapter's degenerate same-process boundary. Pass an explicit parser when\n * the test wants to reject seeded shapes that don't match `S`.\n */\nexport function memoryStore<S>(\n initial?: S | null,\n parse: (raw: unknown) => S | null = (raw) => raw as S | null,\n): Store<S> {\n // Single internal cell — `undefined` and `null` collapse to one\n // representation so the load site is branchless.\n let cell: S | null = initial ?? null;\n\n return {\n async load(): Promise<unknown> {\n return cell;\n },\n async save(state: S): Promise<void> {\n cell = state;\n },\n migrate(raw: unknown): S | null {\n return parse(raw);\n },\n };\n}\n"]}
1
+ {"version":3,"sources":["../../src/mem/index.ts"],"names":[],"mappings":";;;AAsEO,SAAS,WAAA,CACd,SACA,KAAA,GAAoC,CAAC,QAAQ,GAAA,EAC7C,OAAA,GAA8B,EAAC,EACJ;AAG3B,EAAA,IAAI,OAAiB,OAAA,IAAW,IAAA;AAGhC,EAAA,IAAI,OAAA,GAAU,CAAA;AAEd,EAAA,MAAM,IAAA,GAAiB;AAAA,IACrB,MAAM,IAAA,GAAyB;AAC7B,MAAA,OAAO,IAAA;AAAA,IACT,CAAA;AAAA,IACA,MAAM,KAAK,KAAA,EAAyB;AAClC,MAAA,IAAA,GAAO,KAAA;AACP,MAAA,OAAA,IAAW,CAAA;AAAA,IACb,CAAA;AAAA,IACA,QAAQ,GAAA,EAAwB;AAC9B,MAAA,OAAO,MAAM,GAAG,CAAA;AAAA,IAClB;AAAA,GACF;AACA,EAAA,IAAI,OAAA,CAAQ,MAAA,KAAW,IAAA,EAAM,OAAO,IAAA;AAEpC,EAAA,OAAO;AAAA,IACL,GAAG,IAAA;AAAA,IACH,MAAA,EAAQ,IAAA;AAAA,IACR,MAAM,UAAA,GAAyD;AAC7D,MAAA,OAAO,EAAE,GAAA,EAAK,IAAA,EAAM,OAAA,EAAQ;AAAA,IAC9B,CAAA;AAAA,IACA,MAAM,UAAA,CAAW,KAAA,EAAU,eAAA,EAA0C;AAInE,MAAA,IAAI,YAAY,eAAA,EAAiB;AAC/B,QAAA,MAAM,IAAI,kBAAA,CAAmB,eAAA,EAAiB,OAAO,CAAA;AAAA,MACvD;AACA,MAAA,IAAA,GAAO,KAAA;AACP,MAAA,OAAA,IAAW,CAAA;AACX,MAAA,OAAO,OAAA;AAAA,IACT;AAAA,GACF;AACF","file":"index.js","sourcesContent":["/**\n * @packageDocumentation\n * @demlik/tea/mem — in-memory `Store<S>` adapter for `@demlik/tea`.\n *\n * The standard test adapter — used everywhere a Worker or DO would be\n * overkill. Mirrors the source-only convention from `packages/widget-engine`\n * and `packages/tea` (no build step).\n *\n * Reference semantics, NOT deep-clone semantics:\n * - `save(state)` stores the reference; subsequent mutations to that\n * reference by the caller are observable through later `load()` calls.\n * - `load()` returns the stored reference; mutations made through it are\n * observable on the next `load()`.\n *\n * This matches what a real KV/DO `get()` would NOT do (those serialize +\n * deserialize), but it also matches what an in-memory cache obviously does.\n * Adding a deep clone here would lie about the boundary and impose a JSON\n * round-trip on every test. Tests that need fresh copies should clone at the\n * call site.\n *\n * Boundary parse (invariant 8): `Store<S>` requires `migrate(raw)`. For\n * `memoryStore` we ship a permissive identity-default — `(raw) => raw as\n * S | null` — because this adapter is a degenerate same-process boundary:\n * there is no serialization, the reference we read is structurally the\n * reference we wrote. The `as` cast here is the documented exception to the\n * invariant-8 no-`as` rule, scoped to the parameter default. Callers that\n * want strict parsing (e.g. a test that hand-seeds the cell from outside)\n * may pass an explicit `parse` to override.\n */\n\nimport type { FencedStore, Store } from \"../index\";\nimport { StoreConflictError } from \"../index\";\n\n/** Options for {@link memoryStore}. */\nexport interface MemoryStoreOptions {\n /**\n * Refuse a second live writer (#143). With `{ fenced: true }` the returned\n * store is a `FencedStore<S>` whose version is an in-process counter — honest\n * for this adapter, since the cell it guards is in this process too. Two\n * `run`s handed the SAME `memoryStore` are the case it catches, and it is the\n * cheapest way to exercise the fenced path in a test.\n */\n readonly fenced?: true;\n}\n\n/**\n * Build an in-memory `Store<S>`.\n *\n * @param initial - optional seed value. `undefined` and explicit `null` both\n * mean \"no seed\"; `load()` resolves with `null` until the first `save()`.\n * A concrete `S` seed is returned by `load()` until overwritten.\n * @param parse - optional boundary parse for the `Store<S>.migrate` contract.\n * Defaults to identity (`(raw) => raw as S | null`) — honest for this\n * adapter's degenerate same-process boundary. Pass an explicit parser when\n * the test wants to reject seeded shapes that don't match `S`.\n */\nexport function memoryStore<S>(\n initial?: S | null,\n parse?: (raw: unknown) => S | null,\n): Store<S>;\nexport function memoryStore<S>(\n initial: S | null | undefined,\n parse: ((raw: unknown) => S | null) | undefined,\n options: MemoryStoreOptions & { readonly fenced: true },\n): FencedStore<S>;\nexport function memoryStore<S>(\n initial?: S | null,\n parse?: (raw: unknown) => S | null,\n options?: MemoryStoreOptions,\n): Store<S> | FencedStore<S>;\nexport function memoryStore<S>(\n initial?: S | null,\n parse: (raw: unknown) => S | null = (raw) => raw as S | null,\n options: MemoryStoreOptions = {},\n): Store<S> | FencedStore<S> {\n // Single internal cell — `undefined` and `null` collapse to one\n // representation so the load site is branchless.\n let cell: S | null = initial ?? null;\n // The version the cell was last written at. A never-written cell reads `0`,\n // matching `fileStore`'s absent stamp.\n let version = 0;\n\n const base: Store<S> = {\n async load(): Promise<unknown> {\n return cell;\n },\n async save(state: S): Promise<void> {\n cell = state;\n version += 1;\n },\n migrate(raw: unknown): S | null {\n return parse(raw);\n },\n };\n if (options.fenced !== true) return base;\n\n return {\n ...base,\n fenced: true,\n async loadFenced(): Promise<{ raw: unknown; version: number }> {\n return { raw: cell, version };\n },\n async saveFenced(state: S, expectedVersion: number): Promise<number> {\n // No lock: the whole store is one process's memory, and this body has no\n // await before the compare, so nothing can interleave between the check\n // and the write.\n if (version !== expectedVersion) {\n throw new StoreConflictError(expectedVersion, version);\n }\n cell = state;\n version += 1;\n return version;\n },\n };\n}\n"]}
@@ -1,10 +1,85 @@
1
1
  import WebSocket from 'ws';
2
- import { S as Store } from '../runtime-types-CNLmhWif.js';
3
- import { S as Sub, b as SubId } from '../core-Co0LWkIA.js';
4
- import { Journal } from '../journal/index.js';
2
+ import { S as Store, F as FencedStore } from '../runtime-types-LWDoi943.js';
3
+ import { S as Sub, c as SubId } from '../core-CTzMJ82M.js';
5
4
  import 'better-result';
6
5
  import 'zod';
7
6
 
7
+ /**
8
+ * @packageDocumentation
9
+ * internal/journal — an append-only, ordered record log for `@demlik/tea`.
10
+ * Internal since #49 — not published on any subpath; `fileJournal` on
11
+ * `@demlik/tea/node` is the one public route.
12
+ *
13
+ * The log a "durable, replayable state machine" replays from. tea already ships
14
+ * the log's neighbours — `fileStore`/`memoryStore` (`Store<S>`: snapshot with
15
+ * whole-load / whole-save), `recorder` (JSONL debug traces), `durable-effects`
16
+ * (`foldLedger` over an owed-effect ledger) — but not the log itself, so every
17
+ * consumer hand-rolled it. This is that log, domain-blind: a `Journal<R>` over
18
+ * an opaque record `R`, with no pipeline/ledger vocabulary of its own.
19
+ *
20
+ * Three operations carry the contract:
21
+ * - `append(stream, record)` — durable before it returns, assigning a
22
+ * per-stream total-order `seq` (1, 2, 3, …). Concurrent appenders to one
23
+ * stream converge on one order; the lock's only job is an honest order key.
24
+ * - `list(stream)` — every entry of a stream in `seq` order.
25
+ * - `changes(listener)` — a subscription over appends made through this
26
+ * instance after subscribing.
27
+ *
28
+ * `withLock(stream, fx)` runs `fx` with exclusive access to a stream, so a
29
+ * read-decide-append sequence is atomic against other writers. It is
30
+ * re-entrant for the same stream: an `append` inside `fx` does not deadlock on
31
+ * the lock `fx` already holds.
32
+ *
33
+ * The substrate is a `JournalBackend<R>` — how a stream is read, how one record
34
+ * is appended durably, and how a stream is locked across processes.
35
+ * `makeJournal` wires the domain-blind orchestration (per-stream serialisation,
36
+ * `seq` assignment, `changes` fan-out) over any backend. `memoryJournal` is the
37
+ * in-process backend that mirrors `memoryStore` for tests; the Node file
38
+ * backend (`fileJournal`) homes in `@demlik/tea/node` beside `fileStore`.
39
+ *
40
+ * Interface shape follows Effect `unstable/eventlog` `EventJournal`
41
+ * (`entries`/`write`/`changes`/`withLock`) as prior art, not a dependency.
42
+ */
43
+ /** A per-stream total-order key. Starts at 1 and increments by one per append. */
44
+ type Seq = number;
45
+ /** One appended record, tagged with its stream and its per-stream `seq`. */
46
+ interface JournalEntry<R> {
47
+ readonly stream: string;
48
+ readonly seq: Seq;
49
+ readonly record: R;
50
+ }
51
+ /**
52
+ * An append-only, ordered record log over an opaque record `R`.
53
+ *
54
+ * Domain-blind: it knows nothing of what a record means. Ordering, durability
55
+ * and the honest order key are its whole contract.
56
+ */
57
+ interface Journal<R> {
58
+ /**
59
+ * Append `record` to `stream`, durable before the promise resolves, and
60
+ * resolve with the `seq` it was assigned — the next integer in the stream's
61
+ * total order.
62
+ */
63
+ append(stream: string, record: R): Promise<{
64
+ seq: Seq;
65
+ }>;
66
+ /** Every entry of `stream`, in `seq` order. Empty for an unwritten stream. */
67
+ list(stream: string): Promise<ReadonlyArray<JournalEntry<R>>>;
68
+ /**
69
+ * Subscribe to appends made through this instance after this call. Returns an
70
+ * unsubscribe. Cross-process appends are not delivered — a cross-process
71
+ * subscription is a polling concern this log leaves to its consumer.
72
+ */
73
+ changes(listener: (entry: JournalEntry<R>) => void): () => void;
74
+ /**
75
+ * Run `fx` with exclusive access to `stream` — the in-process mutex against
76
+ * other callers here, the backend lock against other processes. Re-entrant
77
+ * for the same stream, so an `append` (or nested `withLock`) inside `fx` runs
78
+ * under the lock this call already holds rather than deadlocking on it.
79
+ */
80
+ withLock<A>(stream: string, fx: () => Promise<A>): Promise<A>;
81
+ }
82
+
8
83
  /**
9
84
  * @packageDocumentation
10
85
  * @demlik/tea/node — Node host adapter for `@demlik/tea`.
@@ -45,7 +120,9 @@ import 'zod';
45
120
  */
46
121
 
47
122
  /**
48
- * `Store<S>` backed by a JSON file at `path`.
123
+ * Persist a machine's state to a JSON file — pass the `path` and a `parse` that
124
+ * validates what comes back, get a `Store<S>` you hand to `run` so the next run
125
+ * resumes where this one stopped.
49
126
  *
50
127
  * `parse` is REQUIRED because the file is a real serialization boundary — the
51
128
  * bytes that come back through `JSON.parse` are structurally `unknown`, and the
@@ -53,10 +130,31 @@ import 'zod';
53
130
  * Returning `null` from `parse` means "no usable persisted state" — the
54
131
  * substrate boots `init` with `loaded = null`. `parse` must NOT throw per the
55
132
  * `Store<S>.migrate` contract.
133
+ *
134
+ * Pass `{ fenced: true }` to get a `FencedStore<S>` instead — see
135
+ * {@link FileStoreOptions}.
56
136
  */
57
137
  declare function fileStore<S>(path: string, parse: (raw: unknown) => S | null): Store<S>;
138
+ declare function fileStore<S>(path: string, parse: (raw: unknown) => S | null, options: FileStoreOptions & {
139
+ readonly fenced: true;
140
+ }): FencedStore<S>;
141
+ declare function fileStore<S>(path: string, parse: (raw: unknown) => S | null, options?: FileStoreOptions): Store<S> | FencedStore<S>;
142
+ /** Options for {@link fileStore}. */
143
+ interface FileStoreOptions {
144
+ /**
145
+ * Refuse a second live writer (#143). With `{ fenced: true }` the returned
146
+ * store is a `FencedStore<S>`: it carries a version stamp beside the state
147
+ * file, and `run` compare-and-swaps against it on every save. A process that
148
+ * boots reads the current version and takes the fence; the older live writer
149
+ * that started from the same version is refused with a `StoreConflictError`
150
+ * at its next save. Omit it and the store is exactly as it was — the last
151
+ * writer wins, and single-writer is the caller's precondition to keep.
152
+ */
153
+ readonly fenced?: true;
154
+ }
58
155
  /**
59
- * The Node file substrate for `@demlik/tea/journal`, beside `fileStore`. A
156
+ * The Node file substrate for the journal (`src/internal/journal/`), beside
157
+ * `fileStore`. A
60
158
  * stream is one JSONL file under `dir` (`<encoded-stream>.jsonl`), one
61
159
  * `{"seq":n,"record":…}` object per line — readable and greppable on disk, no
62
160
  * SQLite. `parse` is REQUIRED for the same reason `fileStore` requires it: the
@@ -161,4 +259,4 @@ declare function nodeSubscribe<M, Ctx extends NodeSubscribeCtx>(): {
161
259
  node_signal: (sub: NodeSignalSub<M>, ctx: Ctx, dispatch: (msg: M) => void) => () => void;
162
260
  };
163
261
 
164
- export { type AssertNodeSubIsSub, type NodeSignalSub, type NodeSub, type NodeSubscribeCtx, type NodeTimerSub, type NodeWsRegistry, type NodeWsSub, fileJournal, fileStore, nodeSubscribe, sendToWebSocket };
262
+ export { type AssertNodeSubIsSub, type FileStoreOptions, type NodeSignalSub, type NodeSub, type NodeSubscribeCtx, type NodeTimerSub, type NodeWsRegistry, type NodeWsSub, fileJournal, fileStore, nodeSubscribe, sendToWebSocket };
@@ -1,11 +1,68 @@
1
- import { dispatchIfPresent } from '../chunk-ZJ5AOYBH.js';
2
- import { makeJournal } from '../chunk-XGELA764.js';
3
- import { mkdir, writeFile, rename, readFile, unlink, open } from 'fs/promises';
1
+ import { StoreConflictError, dispatchIfPresent } from '../chunk-RQULHGYH.js';
2
+ import { mkdir, writeFile, rename, unlink, open, readFile } from 'fs/promises';
3
+ import { createRequire } from 'module';
4
4
  import { dirname, join } from 'path';
5
- import WebSocket from 'ws';
5
+ import { AsyncLocalStorage } from 'async_hooks';
6
6
 
7
- function fileStore(path, parse) {
7
+ var AsyncLock = class {
8
+ tail = Promise.resolve();
9
+ run(fx) {
10
+ const result = this.tail.then(fx, fx);
11
+ this.tail = result.then(
12
+ () => void 0,
13
+ () => void 0
14
+ );
15
+ return result;
16
+ }
17
+ };
18
+ function makeJournal(backend) {
19
+ const mutexes = /* @__PURE__ */ new Map();
20
+ const heldStreams = new AsyncLocalStorage();
21
+ const listeners = /* @__PURE__ */ new Set();
22
+ const mutexFor = (stream) => {
23
+ let mutex = mutexes.get(stream);
24
+ if (!mutex) {
25
+ mutex = new AsyncLock();
26
+ mutexes.set(stream, mutex);
27
+ }
28
+ return mutex;
29
+ };
30
+ const exclusive = (stream, fx) => {
31
+ const held = heldStreams.getStore();
32
+ if (held?.has(stream)) return fx();
33
+ return mutexFor(stream).run(
34
+ () => backend.lock(stream, () => {
35
+ const nested = new Set(held).add(stream);
36
+ return heldStreams.run(nested, fx);
37
+ })
38
+ );
39
+ };
8
40
  return {
41
+ append(stream, record) {
42
+ return exclusive(stream, async () => {
43
+ const entry = await backend.append(stream, record);
44
+ for (const listener of listeners) listener(entry);
45
+ return { seq: entry.seq };
46
+ });
47
+ },
48
+ list(stream) {
49
+ return Promise.resolve(backend.read(stream));
50
+ },
51
+ changes(listener) {
52
+ listeners.add(listener);
53
+ return () => {
54
+ listeners.delete(listener);
55
+ };
56
+ },
57
+ withLock(stream, fx) {
58
+ return exclusive(stream, fx);
59
+ }
60
+ };
61
+ }
62
+
63
+ // src/node/index.ts
64
+ function fileStore(path, parse, options = {}) {
65
+ const base = {
9
66
  async load() {
10
67
  let raw;
11
68
  try {
@@ -26,6 +83,54 @@ function fileStore(path, parse) {
26
83
  return parse(raw);
27
84
  }
28
85
  };
86
+ return options.fenced === true ? fenceFileStore(base, path) : base;
87
+ }
88
+ function fenceFileStore(base, path) {
89
+ const fencePath = `${path}.fence`;
90
+ const lockPath = `${path}.fence.lock`;
91
+ async function readVersion() {
92
+ let raw;
93
+ try {
94
+ raw = await readFile(fencePath, "utf8");
95
+ } catch (err) {
96
+ if (err.code === "ENOENT") return 0;
97
+ throw err;
98
+ }
99
+ const version = Number.parseInt(raw.trim(), 10);
100
+ if (!Number.isSafeInteger(version) || version < 0) {
101
+ throw new Error(
102
+ `fileStore: version stamp at "${fencePath}" is not an integer: ${raw}`
103
+ );
104
+ }
105
+ return version;
106
+ }
107
+ return {
108
+ ...base,
109
+ fenced: true,
110
+ async loadFenced() {
111
+ const version = await readVersion();
112
+ return { raw: await base.load(), version };
113
+ },
114
+ async saveFenced(state, expectedVersion) {
115
+ await mkdir(dirname(path), { recursive: true });
116
+ await acquireFileLock(dirname(path), lockPath);
117
+ try {
118
+ const actual = await readVersion();
119
+ if (actual !== expectedVersion) {
120
+ throw new StoreConflictError(expectedVersion, actual);
121
+ }
122
+ const next = expectedVersion + 1;
123
+ await base.save(state);
124
+ const tmp = `${fencePath}.${process.pid}.tmp`;
125
+ await writeFile(tmp, String(next), "utf8");
126
+ await rename(tmp, fencePath);
127
+ return next;
128
+ } finally {
129
+ await unlink(lockPath).catch(() => {
130
+ });
131
+ }
132
+ }
133
+ };
29
134
  }
30
135
  var LOCK_SPIN_MS = 10;
31
136
  function fileJournal(dir, parse) {
@@ -115,9 +220,27 @@ function isProcessAlive(pid) {
115
220
  return err.code === "EPERM";
116
221
  }
117
222
  }
223
+ var cachedWs;
224
+ function loadWebSocket() {
225
+ if (cachedWs) return cachedWs;
226
+ let mod;
227
+ try {
228
+ mod = createRequire(import.meta.url)("ws");
229
+ } catch (cause) {
230
+ throw new Error(
231
+ 'The `node_ws` Sub needs the optional peer dependency "ws". Install it: `npm install ws`.',
232
+ { cause }
233
+ );
234
+ }
235
+ const ctor = mod.default ?? mod;
236
+ cachedWs = ctor;
237
+ return cachedWs;
238
+ }
239
+ var WS_OPEN = 1;
240
+ var WS_CLOSED = 3;
118
241
  function sendToWebSocket(ctx, id, data) {
119
242
  const socket = ctx.wsRegistry.get(id);
120
- if (!socket || socket.readyState !== WebSocket.OPEN) return false;
243
+ if (!socket || socket.readyState !== WS_OPEN) return false;
121
244
  try {
122
245
  socket.send(data);
123
246
  return true;
@@ -128,7 +251,7 @@ function sendToWebSocket(ctx, id, data) {
128
251
  function nodeSubscribe() {
129
252
  return {
130
253
  node_ws: (sub, ctx, dispatch) => {
131
- const socket = new WebSocket(sub.url);
254
+ const socket = new (loadWebSocket())(sub.url);
132
255
  ctx.wsRegistry.set(sub.id, socket);
133
256
  socket.on("open", () => {
134
257
  if (sub.onOpen) dispatch(sub.onOpen());
@@ -147,9 +270,9 @@ function nodeSubscribe() {
147
270
  socket.removeAllListeners();
148
271
  socket.on("error", () => {
149
272
  });
150
- if (socket.readyState === WebSocket.OPEN) {
273
+ if (socket.readyState === WS_OPEN) {
151
274
  socket.close(1e3, "tea-node sub cleanup");
152
- } else if (socket.readyState !== WebSocket.CLOSED) {
275
+ } else if (socket.readyState !== WS_CLOSED) {
153
276
  socket.terminate();
154
277
  }
155
278
  };