@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 +1 @@
1
- {"version":3,"sources":["../../src/node/index.ts"],"names":["handle"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @packageDocumentation\n * @demlik/tea/node — Node host adapter for `@demlik/tea`.\n *\n * Every host adapter ships two halves (mirrors `@demlik/tea/do`):\n *\n * 1. `fileStore<S>(path, parse)` — a `Store<S>` over a JSON file on disk.\n * Atomic save (temp + rename). Absent file → `load()` resolves `null`\n * (fresh boot). Structurally malformed JSON throws at `load()` (an infra\n * error), matching `doStore`; a shape mismatch is NOT a throw — the\n * substrate calls `migrate`, which returns `null` and boots fresh.\n * Use for resumable scripts / local repro of prod machines. Ephemeral\n * CLIs can skip persistence with `memoryStore` from `@demlik/tea/mem`.\n *\n * 2. `nodeSubscribe<M, Ctx>()` — handler registry for the three node-native\n * Sub types this package owns:\n * - `node_ws` — opens a `ws` WebSocket; routes open/message/close/\n * error into Msgs. Registers the live socket in\n * `ctx.wsRegistry` so a Cmd handler can write to it\n * (see `sendToWebSocket`). Cleanup closes the socket\n * for ANY non-CLOSED state.\n * - `node_timer` — setTimeout / setInterval; cleanup clears it.\n * - `node_signal` — a process signal (SIGINT/SIGTERM/…) → Msg; cleanup\n * detaches the listener.\n *\n * Unlike tea-do — whose `alarm()` / `webSocketMessage()` are DO lifecycle\n * methods the handler can't observe from the outside, hence its registries —\n * a node process is continuously alive: each handler holds its resource in\n * closure and the returned cleanup closes it. The substrate's subscription\n * reconciler runs that cleanup the instant `subscriptions(state)` stops listing\n * the Sub — end-then-start choreography for free (Rule 9, [invariant 4]).\n * A lifecycle-bound resource (a socket, a timer) becomes impossible to leak\n * from a reducer cell, because no reducer cell owns its teardown.\n *\n * `ctx.wsRegistry` is the one shared surface: `node_ws` populates it; a Cmd\n * interpreter reads it to write to the open socket. Same shape as tea-do's\n * `wsRegistry`.\n */\n\nimport {\n mkdir,\n open,\n readFile,\n rename,\n unlink,\n writeFile,\n} from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport WebSocket, { type RawData } from \"ws\";\nimport type { Store, Sub, SubId } from \"../index\";\nimport {\n type Journal,\n type JournalEntry,\n makeJournal,\n type Seq,\n} from \"../journal\";\nimport { dispatchIfPresent } from \"../subs/types\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// fileStore — `Store<S>` over a JSON file.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * `Store<S>` backed by a JSON file at `path`.\n *\n * `parse` is REQUIRED because the file is a real serialization boundary — the\n * bytes that come back through `JSON.parse` are structurally `unknown`, and the\n * caller (who owns `S`) is the only party that can validate the shape.\n * Returning `null` from `parse` means \"no usable persisted state\" — the\n * substrate boots `init` with `loaded = null`. `parse` must NOT throw per the\n * `Store<S>.migrate` contract.\n */\nexport function fileStore<S>(\n path: string,\n parse: (raw: unknown) => S | null,\n): Store<S> {\n return {\n async load(): Promise<unknown> {\n let raw: string;\n try {\n raw = await readFile(path, \"utf8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw err;\n }\n // JSON.parse throws on structural malformation — propagate (infra error),\n // matching `doStore`. The decoded value is intentionally returned as\n // `unknown`; the substrate's `migrate` callback is the boundary parse.\n return JSON.parse(raw);\n },\n async save(state: S): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n // Write to a pid-scoped temp then atomically rename. The rename gives\n // torn-write safety (a reader sees the old file or the new one, never a\n // partial) and survives process crash / Ctrl-C — the only failure a local\n // resumable run resumes from. fsync (file + dir) would add power-loss\n // durability the use case doesn't need; the substrate serializes saves\n // through run()'s queue, so there's never a second concurrent writer.\n const tmp = `${path}.${process.pid}.tmp`;\n await writeFile(tmp, JSON.stringify(state), \"utf8\");\n await rename(tmp, path);\n },\n migrate(raw: unknown): S | null {\n return parse(raw);\n },\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// fileJournal — a `Journal<R>` over one on-disk JSONL file per stream.\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst LOCK_SPIN_MS = 10;\n\n/**\n * The Node file substrate for `@demlik/tea/journal`, beside `fileStore`. A\n * stream is one JSONL file under `dir` (`<encoded-stream>.jsonl`), one\n * `{\"seq\":n,\"record\":…}` object per line — readable and greppable on disk, no\n * SQLite. `parse` is REQUIRED for the same reason `fileStore` requires it: the\n * bytes read back through `JSON.parse` are structurally `unknown`, and the\n * caller who owns `R` is the only party that can validate a record's shape.\n *\n * `append` rewrites the whole stream file via a pid-scoped temp + `rename`,\n * exactly as `fileStore.save` does — a reader sees the old file or the new one,\n * never a torn line, and the write survives a crash mid-append. The append is\n * O(n) in the stream's length; a durable record log a machine replays is read\n * whole anyway, so the atomic-rewrite guarantee is worth the cost.\n *\n * The ordering key is taken under a `wx` lock file (`<file>.lock`) holding the\n * writer's PID: a second writer racing the same stream blocks on the lock, so\n * `seq` is a single total order across processes. A crashed writer's lock is\n * stolen once its PID proves dead (`process.kill(pid, 0)` → `ESRCH`), so a\n * crash never wedges the stream. This is the lock phoenix spike #6673 proved:\n * its only job is an honest order key, not the win.\n *\n * `changes` fires for appends made through THIS instance; a cross-process\n * append lands on disk but is not broadcast here (that is a polling concern the\n * log leaves to its consumer, and the remote-journal work downstream).\n */\nexport function fileJournal<R>(\n dir: string,\n parse: (raw: unknown) => R,\n): Journal<R> {\n const streamPath = (stream: string): string =>\n join(dir, `${encodeURIComponent(stream)}.jsonl`);\n\n const readEntries = async (stream: string): Promise<JournalEntry<R>[]> => {\n const path = streamPath(stream);\n let raw: string;\n try {\n raw = await readFile(path, \"utf8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return [];\n throw err;\n }\n return raw\n .split(\"\\n\")\n .filter((line) => line.length > 0)\n .map((line) => {\n const row = JSON.parse(line) as { seq: Seq; record: unknown };\n return { stream, seq: row.seq, record: parse(row.record) };\n });\n };\n\n return makeJournal<R>({\n read: readEntries,\n async append(stream, record) {\n const entries = await readEntries(stream);\n const entry: JournalEntry<R> = {\n stream,\n seq: entries.length + 1,\n record,\n };\n const path = streamPath(stream);\n const lines = [...entries, entry]\n .map((e) => JSON.stringify({ seq: e.seq, record: e.record }))\n .join(\"\\n\");\n await mkdir(dir, { recursive: true });\n const tmp = `${path}.${process.pid}.tmp`;\n await writeFile(tmp, `${lines}\\n`, \"utf8\");\n await rename(tmp, path);\n return entry;\n },\n async lock(stream, fx) {\n const lockPath = `${streamPath(stream)}.lock`;\n await acquireFileLock(dir, lockPath);\n try {\n return await fx();\n } finally {\n await unlink(lockPath).catch(() => {});\n }\n },\n });\n}\n\n/**\n * Acquire an exclusive lock by creating `lockPath` with `wx` (O_CREAT|O_EXCL) —\n * the one primitive that is atomic across processes. On contention, steal the\n * lock only once its holder's PID proves dead; otherwise spin. `process.kill(pid,\n * 0)` sends no signal — it tests existence: it throws `ESRCH` for a dead PID,\n * and `EPERM` for a live one this process may not signal (still alive, so hold\n * off). A lock that vanishes between the failed create and the read is already\n * free, so retry.\n */\nasync function acquireFileLock(dir: string, lockPath: string): Promise<void> {\n await mkdir(dir, { recursive: true });\n for (;;) {\n try {\n const handle = await open(lockPath, \"wx\");\n try {\n await handle.writeFile(String(process.pid));\n } finally {\n await handle.close();\n }\n return;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") throw err;\n if (await stealIfStale(lockPath)) continue;\n await new Promise((resolve) => setTimeout(resolve, LOCK_SPIN_MS));\n }\n }\n}\n\nasync function stealIfStale(lockPath: string): Promise<boolean> {\n let holder: string;\n try {\n holder = await readFile(lockPath, \"utf8\");\n } catch (err) {\n // Vanished between the failed create and this read — already free.\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return true;\n throw err;\n }\n const pid = Number(holder.trim());\n if (!Number.isInteger(pid) || pid <= 0) return false;\n if (isProcessAlive(pid)) return false;\n await unlink(lockPath).catch(() => {});\n return true;\n}\n\nfunction isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n return (err as NodeJS.ErrnoException).code === \"EPERM\";\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// NodeSub — the three node-native subscription variants.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * A node WebSocket sub. The substrate diffs subs by `id`; the same `id` across\n * transitions = the same socket (kept open). Emit a DIFFERENT `id` to force a\n * reconnect.\n *\n * Only `onMessage` returns `M | null` (null drops the frame) because inbound\n * frames are the boundary that needs a parse-and-maybe-discard seam. `onOpen`/\n * `onClose`/`onError` always dispatch — a spurious one is dropped in the reducer\n * (the cell ignores it), not here.\n */\nexport type NodeWsSub<M> = {\n id: SubId;\n type: \"node_ws\";\n url: string;\n onMessage: (data: string) => M | null;\n onOpen?: () => M;\n onClose?: (code: number, reason: string) => M;\n onError?: (message: string) => M;\n};\n\n/**\n * A node timer sub. One-shot `setTimeout` by default; `repeat: true` →\n * `setInterval`. Cleanup clears the timer — a timer keyed to a phase is\n * cancelled the moment the machine leaves that phase.\n */\nexport type NodeTimerSub<M> = {\n id: SubId;\n type: \"node_timer\";\n delayMs: number;\n msg: M;\n repeat?: boolean;\n};\n\n/**\n * A process-signal sub: `signal` (SIGINT/SIGTERM/…) → `msg`. Cleanup detaches\n * the listener. Lets graceful shutdown be a real machine transition rather than\n * an out-of-band `process.on` that the reducer can't see.\n */\nexport type NodeSignalSub<M> = {\n id: SubId;\n type: \"node_signal\";\n signal: NodeJS.Signals;\n msg: M;\n};\n\nexport type NodeSub<M> = NodeWsSub<M> | NodeTimerSub<M> | NodeSignalSub<M>;\n\n// Compile-time guarantee: `NodeSub<M>` is structurally a `Sub` (has `id`+`type`).\n// Erased at runtime — the `extends Sub` constraint on the alias's type\n// parameter fails to compile if `NodeSub` ever drifts off the `Sub` shape.\nexport type AssertNodeSubIsSub<T extends Sub = NodeSub<unknown>> = T;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Ctx — what the node Subs read / populate.\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type NodeWsRegistry = Map<SubId, WebSocket>;\n\n/**\n * The Ctx fields the node Subs depend on. A consumer's Ctx must extend this so\n * `node_ws` can register the live socket and Cmd handlers can reach it.\n *\n * Deliberately NOT parameterized by Msg: the registry holds raw `WebSocket`s,\n * and each Sub variant carries its own Msg-typed callbacks. Trip-wire: if a\n * future node Sub holds a Msg-typed callback on ctx, this becomes\n * `NodeSubscribeCtx<M>`.\n */\nexport interface NodeSubscribeCtx {\n wsRegistry: NodeWsRegistry;\n}\n\n/**\n * Write a frame to a registered `node_ws` socket. No-op (returns `false`) when\n * the socket is absent, not OPEN, or `send()` throws. The Cmd-side companion to\n * the `node_ws` Sub: the Sub owns the socket's lifetime; this writes to it.\n *\n * The contract is a boolean no-op, never a throw: the socket can transition out\n * of OPEN between the `readyState` check and `send()` (or `ws` can throw on an\n * errored underlying connection), and a Cmd interpreter must not crash on that.\n */\nexport function sendToWebSocket(\n ctx: NodeSubscribeCtx,\n id: SubId,\n data: string,\n): boolean {\n const socket = ctx.wsRegistry.get(id);\n if (!socket || socket.readyState !== WebSocket.OPEN) return false;\n try {\n socket.send(data);\n return true;\n } catch {\n return false;\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// nodeSubscribe — the handler registry.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Build the `subscribe` handlers for `node_ws`, `node_timer`, and\n * `node_signal`. Matches `Machine#subscribe`'s\n * `[K in U[\"type\"]]: (sub, ctx, dispatch) => () => void` contract for the\n * `NodeSub<M>` variants. Generic `Ctx` must extend `NodeSubscribeCtx` so the\n * `node_ws` handler can register its socket.\n */\nexport function nodeSubscribe<M, Ctx extends NodeSubscribeCtx>(): {\n node_ws: (\n sub: NodeWsSub<M>,\n ctx: Ctx,\n dispatch: (msg: M) => void,\n ) => () => void;\n node_timer: (\n sub: NodeTimerSub<M>,\n ctx: Ctx,\n dispatch: (msg: M) => void,\n ) => () => void;\n node_signal: (\n sub: NodeSignalSub<M>,\n ctx: Ctx,\n dispatch: (msg: M) => void,\n ) => () => void;\n} {\n return {\n node_ws: (sub, ctx, dispatch) => {\n const socket = new WebSocket(sub.url);\n ctx.wsRegistry.set(sub.id, socket);\n\n socket.on(\"open\", () => {\n if (sub.onOpen) dispatch(sub.onOpen());\n });\n socket.on(\"message\", (data: RawData) => {\n dispatchIfPresent(dispatch, sub.onMessage(data.toString()));\n });\n socket.on(\"close\", (code: number, reason: Buffer) => {\n if (sub.onClose) dispatch(sub.onClose(code, reason.toString()));\n });\n socket.on(\"error\", (err: Error) => {\n if (sub.onError) dispatch(sub.onError(err.message));\n });\n\n return () => {\n ctx.wsRegistry.delete(sub.id);\n socket.removeAllListeners();\n // Swallow teardown-induced errors: terminate()-ing a CONNECTING socket\n // emits an 'error' (\"closed before the connection was established\"),\n // and with our listeners gone that would be an unhandled 'error' that\n // crashes the process. A no-op listener absorbs it.\n socket.on(\"error\", () => {});\n // Close for ANY non-closed state: a graceful 1000 when OPEN, a hard\n // terminate() otherwise (CONNECTING/CLOSING) so a socket caught mid-\n // handshake can't dangle. This is the teardown the hand-driven Cmd-pair\n // approach (`cleanup_browser` closing the ws only `if OPEN`) could not\n // guarantee.\n if (socket.readyState === WebSocket.OPEN) {\n socket.close(1000, \"tea-node sub cleanup\");\n } else if (socket.readyState !== WebSocket.CLOSED) {\n socket.terminate();\n }\n };\n },\n\n node_timer: (sub, _ctx, dispatch) => {\n // Two reconcile lifecycles under one variant: a one-shot (`setTimeout`)\n // that `subscriptions(state)` should drop on the render after it fires,\n // vs a repeating (`setInterval`) one the state keeps listing across\n // renders. Same Sub.type; the `repeat` flag picks the install + cleanup.\n if (sub.repeat) {\n const handle = setInterval(() => dispatch(sub.msg), sub.delayMs);\n return () => clearInterval(handle);\n }\n const handle = setTimeout(() => dispatch(sub.msg), sub.delayMs);\n return () => clearTimeout(handle);\n },\n\n node_signal: (sub, _ctx, dispatch) => {\n const handler = () => dispatch(sub.msg);\n process.on(sub.signal, handler);\n return () => {\n process.off(sub.signal, handler);\n };\n },\n };\n}\n"]}
1
+ {"version":3,"sources":["../../src/internal/journal/index.ts","../../src/node/index.ts"],"names":["handle"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @packageDocumentation\n * internal/journal — an append-only, ordered record log for `@demlik/tea`.\n * Internal since #49 — not published on any subpath; `fileJournal` on\n * `@demlik/tea/node` is the one public route.\n *\n * The log a \"durable, replayable state machine\" replays from. tea already ships\n * the log's neighbours — `fileStore`/`memoryStore` (`Store<S>`: snapshot with\n * whole-load / whole-save), `recorder` (JSONL debug traces), `durable-effects`\n * (`foldLedger` over an owed-effect ledger) — but not the log itself, so every\n * consumer hand-rolled it. This is that log, domain-blind: a `Journal<R>` over\n * an opaque record `R`, with no pipeline/ledger vocabulary of its own.\n *\n * Three operations carry the contract:\n * - `append(stream, record)` — durable before it returns, assigning a\n * per-stream total-order `seq` (1, 2, 3, …). Concurrent appenders to one\n * stream converge on one order; the lock's only job is an honest order key.\n * - `list(stream)` — every entry of a stream in `seq` order.\n * - `changes(listener)` — a subscription over appends made through this\n * instance after subscribing.\n *\n * `withLock(stream, fx)` runs `fx` with exclusive access to a stream, so a\n * read-decide-append sequence is atomic against other writers. It is\n * re-entrant for the same stream: an `append` inside `fx` does not deadlock on\n * the lock `fx` already holds.\n *\n * The substrate is a `JournalBackend<R>` — how a stream is read, how one record\n * is appended durably, and how a stream is locked across processes.\n * `makeJournal` wires the domain-blind orchestration (per-stream serialisation,\n * `seq` assignment, `changes` fan-out) over any backend. `memoryJournal` is the\n * in-process backend that mirrors `memoryStore` for tests; the Node file\n * backend (`fileJournal`) homes in `@demlik/tea/node` beside `fileStore`.\n *\n * Interface shape follows Effect `unstable/eventlog` `EventJournal`\n * (`entries`/`write`/`changes`/`withLock`) as prior art, not a dependency.\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\n\n/** A per-stream total-order key. Starts at 1 and increments by one per append. */\nexport type Seq = number;\n\n/** One appended record, tagged with its stream and its per-stream `seq`. */\nexport interface JournalEntry<R> {\n readonly stream: string;\n readonly seq: Seq;\n readonly record: R;\n}\n\n/**\n * An append-only, ordered record log over an opaque record `R`.\n *\n * Domain-blind: it knows nothing of what a record means. Ordering, durability\n * and the honest order key are its whole contract.\n */\nexport interface Journal<R> {\n /**\n * Append `record` to `stream`, durable before the promise resolves, and\n * resolve with the `seq` it was assigned — the next integer in the stream's\n * total order.\n */\n append(stream: string, record: R): Promise<{ seq: Seq }>;\n /** Every entry of `stream`, in `seq` order. Empty for an unwritten stream. */\n list(stream: string): Promise<ReadonlyArray<JournalEntry<R>>>;\n /**\n * Subscribe to appends made through this instance after this call. Returns an\n * unsubscribe. Cross-process appends are not delivered — a cross-process\n * subscription is a polling concern this log leaves to its consumer.\n */\n changes(listener: (entry: JournalEntry<R>) => void): () => void;\n /**\n * Run `fx` with exclusive access to `stream` — the in-process mutex against\n * other callers here, the backend lock against other processes. Re-entrant\n * for the same stream, so an `append` (or nested `withLock`) inside `fx` runs\n * under the lock this call already holds rather than deadlocking on it.\n */\n withLock<A>(stream: string, fx: () => Promise<A>): Promise<A>;\n}\n\n/**\n * The substrate a `Journal<R>` is built over. Everything host-specific lives\n * here; `makeJournal` owns the host-blind rest.\n *\n * `append` is always invoked under `lock` for the same stream, so a backend\n * assigns `seq` from `read().length + 1` without racing itself.\n */\nexport interface JournalBackend<R> {\n /** Every entry of `stream` in `seq` order; empty for an unwritten stream. */\n read(stream: string): Promise<ReadonlyArray<JournalEntry<R>>>;\n /**\n * Persist `record` as the next entry of `stream`, durable before resolving,\n * and resolve with the entry it wrote. Invoked only while `lock` holds the\n * stream, so `seq` is safely the current length plus one.\n */\n append(stream: string, record: R): Promise<JournalEntry<R>>;\n /** Run `fx` while holding `stream`'s cross-process lock. */\n lock<A>(stream: string, fx: () => Promise<A>): Promise<A>;\n}\n\n/**\n * A promise-chained mutex: each `run` waits for the prior one to settle, so the\n * bodies never interleave. The order key is honest because assignment happens\n * inside a body, never across the `await` that another body could slip through.\n */\nclass AsyncLock {\n private tail: Promise<unknown> = Promise.resolve();\n\n run<A>(fx: () => Promise<A>): Promise<A> {\n const result = this.tail.then(fx, fx);\n this.tail = result.then(\n () => undefined,\n () => undefined,\n );\n return result;\n }\n}\n\n/**\n * Build a `Journal<R>` over `backend`. Owns the host-blind half: per-stream\n * serialisation, re-entrant exclusion, and the `changes` fan-out. `append` and\n * `withLock` both route through `exclusive`, which holds the in-process mutex\n * and the backend lock for one stream and is re-entrant for that stream within\n * a single call chain.\n */\nexport function makeJournal<R>(backend: JournalBackend<R>): Journal<R> {\n const mutexes = new Map<string, AsyncLock>();\n // The streams this async call chain holds. Kept in AsyncLocalStorage, not an\n // instance-wide Set, because re-entrancy is a property of the CALL CHAIN, not\n // of the instance: a genuine re-entrant append (nested inside an `fx` that\n // already holds the stream) and an INDEPENDENT top-level append that merely\n // arrives while another chain holds the lock are indistinguishable to a\n // shared Set — so the shared Set waves the independent call through inline,\n // past the mutex, to run concurrently with the holder. That is a lost record:\n // in `fileJournal` the two both read the same length and compute the same\n // `seq`, and the second `rename` clobbers the first. A per-chain store only\n // ever reports the streams THIS chain entered, so an independent call sees an\n // empty (or unrelated) store and correctly queues on the mutex.\n const heldStreams = new AsyncLocalStorage<ReadonlySet<string>>();\n const listeners = new Set<(entry: JournalEntry<R>) => void>();\n\n const mutexFor = (stream: string): AsyncLock => {\n let mutex = mutexes.get(stream);\n if (!mutex) {\n mutex = new AsyncLock();\n mutexes.set(stream, mutex);\n }\n return mutex;\n };\n\n const exclusive = <A>(stream: string, fx: () => Promise<A>): Promise<A> => {\n const held = heldStreams.getStore();\n // This call chain already holds this stream's lock: run inline, or a\n // re-entrant append/withLock would block forever on a lock it owns.\n if (held?.has(stream)) return fx();\n return mutexFor(stream).run(() =>\n backend.lock(stream, () => {\n // Scope the augmented held-set to `fx` and its async continuations. The\n // store is torn down when `fx` settles — no finally clause to leak it.\n const nested = new Set(held).add(stream);\n return heldStreams.run(nested, fx);\n }),\n );\n };\n\n return {\n append(stream, record) {\n return exclusive(stream, async () => {\n const entry = await backend.append(stream, record);\n for (const listener of listeners) listener(entry);\n return { seq: entry.seq };\n });\n },\n list(stream) {\n return Promise.resolve(backend.read(stream));\n },\n changes(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n withLock(stream, fx) {\n return exclusive(stream, fx);\n },\n };\n}\n\n/**\n * An in-process `Journal<R>` — the standard test substrate, mirroring\n * `memoryStore` from `@demlik/tea/mem`. Reference semantics: a record handed to\n * `append` is stored by reference, exactly as `memoryStore` stores state. No\n * cross-process lock is needed — a single process owns the whole log — so the\n * backend lock is identity and `makeJournal`'s in-process mutex is the only\n * ordering guarantee.\n */\nexport function memoryJournal<R>(): Journal<R> {\n const streams = new Map<string, JournalEntry<R>[]>();\n\n return makeJournal<R>({\n async read(stream) {\n return (streams.get(stream) ?? []).slice();\n },\n async append(stream, record) {\n const log = streams.get(stream) ?? [];\n const entry: JournalEntry<R> = { stream, seq: log.length + 1, record };\n log.push(entry);\n streams.set(stream, log);\n return entry;\n },\n async lock(_stream, fx) {\n return fx();\n },\n });\n}\n\n// The remote-journal + per-remote-cursor half of the journal feature (#31): the\n// remote a client pushes to and pulls from, the local cursor that resumes sync,\n// and the sync client that joins a local `Journal` to a remote so two local\n// journals meet in one order. It builds ON `memoryJournal` above.\nexport * from \"./remote\";\n","/**\n * @packageDocumentation\n * @demlik/tea/node — Node host adapter for `@demlik/tea`.\n *\n * Every host adapter ships two halves (mirrors `@demlik/tea/do`):\n *\n * 1. `fileStore<S>(path, parse)` — a `Store<S>` over a JSON file on disk.\n * Atomic save (temp + rename). Absent file → `load()` resolves `null`\n * (fresh boot). Structurally malformed JSON throws at `load()` (an infra\n * error), matching `doStore`; a shape mismatch is NOT a throw — the\n * substrate calls `migrate`, which returns `null` and boots fresh.\n * Use for resumable scripts / local repro of prod machines. Ephemeral\n * CLIs can skip persistence with `memoryStore` from `@demlik/tea/mem`.\n *\n * 2. `nodeSubscribe<M, Ctx>()` — handler registry for the three node-native\n * Sub types this package owns:\n * - `node_ws` — opens a `ws` WebSocket; routes open/message/close/\n * error into Msgs. Registers the live socket in\n * `ctx.wsRegistry` so a Cmd handler can write to it\n * (see `sendToWebSocket`). Cleanup closes the socket\n * for ANY non-CLOSED state.\n * - `node_timer` — setTimeout / setInterval; cleanup clears it.\n * - `node_signal` — a process signal (SIGINT/SIGTERM/…) → Msg; cleanup\n * detaches the listener.\n *\n * Unlike tea-do — whose `alarm()` / `webSocketMessage()` are DO lifecycle\n * methods the handler can't observe from the outside, hence its registries —\n * a node process is continuously alive: each handler holds its resource in\n * closure and the returned cleanup closes it. The substrate's subscription\n * reconciler runs that cleanup the instant `subscriptions(state)` stops listing\n * the Sub — end-then-start choreography for free (Rule 9, [invariant 4]).\n * A lifecycle-bound resource (a socket, a timer) becomes impossible to leak\n * from a reducer cell, because no reducer cell owns its teardown.\n *\n * `ctx.wsRegistry` is the one shared surface: `node_ws` populates it; a Cmd\n * interpreter reads it to write to the open socket. Same shape as tea-do's\n * `wsRegistry`.\n */\n\nimport {\n mkdir,\n open,\n readFile,\n rename,\n unlink,\n writeFile,\n} from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport { dirname, join } from \"node:path\";\nimport type WebSocket from \"ws\";\nimport type { RawData } from \"ws\";\nimport type { FencedStore, Store, Sub, SubId } from \"../index\";\nimport { StoreConflictError } from \"../index\";\nimport {\n type Journal,\n type JournalEntry,\n makeJournal,\n type Seq,\n} from \"../internal/journal\";\nimport { dispatchIfPresent } from \"../subs/types\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// fileStore — `Store<S>` over a JSON file.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Persist a machine's state to a JSON file — pass the `path` and a `parse` that\n * validates what comes back, get a `Store<S>` you hand to `run` so the next run\n * resumes where this one stopped.\n *\n * `parse` is REQUIRED because the file is a real serialization boundary — the\n * bytes that come back through `JSON.parse` are structurally `unknown`, and the\n * caller (who owns `S`) is the only party that can validate the shape.\n * Returning `null` from `parse` means \"no usable persisted state\" — the\n * substrate boots `init` with `loaded = null`. `parse` must NOT throw per the\n * `Store<S>.migrate` contract.\n *\n * Pass `{ fenced: true }` to get a `FencedStore<S>` instead — see\n * {@link FileStoreOptions}.\n */\nexport function fileStore<S>(\n path: string,\n parse: (raw: unknown) => S | null,\n): Store<S>;\nexport function fileStore<S>(\n path: string,\n parse: (raw: unknown) => S | null,\n options: FileStoreOptions & { readonly fenced: true },\n): FencedStore<S>;\nexport function fileStore<S>(\n path: string,\n parse: (raw: unknown) => S | null,\n options?: FileStoreOptions,\n): Store<S> | FencedStore<S>;\nexport function fileStore<S>(\n path: string,\n parse: (raw: unknown) => S | null,\n options: FileStoreOptions = {},\n): Store<S> | FencedStore<S> {\n const base: Store<S> = {\n async load(): Promise<unknown> {\n let raw: string;\n try {\n raw = await readFile(path, \"utf8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw err;\n }\n // JSON.parse throws on structural malformation — propagate (infra error),\n // matching `doStore`. The decoded value is intentionally returned as\n // `unknown`; the substrate's `migrate` callback is the boundary parse.\n return JSON.parse(raw);\n },\n async save(state: S): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n // Write to a pid-scoped temp then atomically rename. The rename gives\n // torn-write safety (a reader sees the old file or the new one, never a\n // partial) and survives process crash / Ctrl-C — the only failure a local\n // resumable run resumes from. fsync (file + dir) would add power-loss\n // durability the use case doesn't need; the substrate serializes saves\n // through run()'s queue, so there's never a second concurrent writer\n // WITHIN one run. Between two runs there is nothing here refusing one —\n // that is what `{ fenced: true }` below adds (#143).\n const tmp = `${path}.${process.pid}.tmp`;\n await writeFile(tmp, JSON.stringify(state), \"utf8\");\n await rename(tmp, path);\n },\n migrate(raw: unknown): S | null {\n return parse(raw);\n },\n };\n return options.fenced === true ? fenceFileStore(base, path) : base;\n}\n\n/** Options for {@link fileStore}. */\nexport interface FileStoreOptions {\n /**\n * Refuse a second live writer (#143). With `{ fenced: true }` the returned\n * store is a `FencedStore<S>`: it carries a version stamp beside the state\n * file, and `run` compare-and-swaps against it on every save. A process that\n * boots reads the current version and takes the fence; the older live writer\n * that started from the same version is refused with a `StoreConflictError`\n * at its next save. Omit it and the store is exactly as it was — the last\n * writer wins, and single-writer is the caller's precondition to keep.\n */\n readonly fenced?: true;\n}\n\n/**\n * The fencing half of {@link fileStore}. The state file itself is unchanged, so\n * a file written unfenced resumes fenced and back again; the version lives in a\n * sidecar `<path>.fence` holding a decimal integer, and an absent stamp is `0`.\n *\n * The compare-and-swap is taken under the same `wx` lock file `fileJournal`\n * uses, because `open(…, \"wx\")` is the one primitive that is atomic ACROSS\n * PROCESSES. Without it the read-compare-write is a TOCTOU race and two writers\n * both pass their own check. Rename-over-tmp still does the torn-write work for\n * each of the two files; the lock does the exclusion no rename could.\n */\nfunction fenceFileStore<S>(base: Store<S>, path: string): FencedStore<S> {\n const fencePath = `${path}.fence`;\n const lockPath = `${path}.fence.lock`;\n\n async function readVersion(): Promise<number> {\n let raw: string;\n try {\n raw = await readFile(fencePath, \"utf8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return 0;\n throw err;\n }\n const version = Number.parseInt(raw.trim(), 10);\n // A stamp we cannot read is not a version we may assume: defaulting it to 0\n // would let a fresh writer win a fence against a live one.\n if (!Number.isSafeInteger(version) || version < 0) {\n throw new Error(\n `fileStore: version stamp at \"${fencePath}\" is not an integer: ${raw}`,\n );\n }\n return version;\n }\n\n return {\n ...base,\n fenced: true,\n async loadFenced(): Promise<{ raw: unknown; version: number }> {\n // Version FIRST, bytes second: a writer landing between the two reads\n // leaves us with a version OLDER than the bytes, so our next save is\n // refused. Reading bytes first could hand back a version newer than them,\n // and that save would silently win.\n const version = await readVersion();\n return { raw: await base.load(), version };\n },\n async saveFenced(state: S, expectedVersion: number): Promise<number> {\n await mkdir(dirname(path), { recursive: true });\n await acquireFileLock(dirname(path), lockPath);\n try {\n const actual = await readVersion();\n if (actual !== expectedVersion) {\n throw new StoreConflictError(expectedVersion, actual);\n }\n const next = expectedVersion + 1;\n // State before stamp. A crash between the two leaves a stamp BEHIND the\n // bytes, which reads as a conflict for the next writer — the safe\n // direction. The reverse advertises a write that never landed.\n await base.save(state);\n const tmp = `${fencePath}.${process.pid}.tmp`;\n await writeFile(tmp, String(next), \"utf8\");\n await rename(tmp, fencePath);\n return next;\n } finally {\n await unlink(lockPath).catch(() => {});\n }\n },\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// fileJournal — a `Journal<R>` over one on-disk JSONL file per stream.\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst LOCK_SPIN_MS = 10;\n\n/**\n * The Node file substrate for the journal (`src/internal/journal/`), beside\n * `fileStore`. A\n * stream is one JSONL file under `dir` (`<encoded-stream>.jsonl`), one\n * `{\"seq\":n,\"record\":…}` object per line — readable and greppable on disk, no\n * SQLite. `parse` is REQUIRED for the same reason `fileStore` requires it: the\n * bytes read back through `JSON.parse` are structurally `unknown`, and the\n * caller who owns `R` is the only party that can validate a record's shape.\n *\n * `append` rewrites the whole stream file via a pid-scoped temp + `rename`,\n * exactly as `fileStore.save` does — a reader sees the old file or the new one,\n * never a torn line, and the write survives a crash mid-append. The append is\n * O(n) in the stream's length; a durable record log a machine replays is read\n * whole anyway, so the atomic-rewrite guarantee is worth the cost.\n *\n * The ordering key is taken under a `wx` lock file (`<file>.lock`) holding the\n * writer's PID: a second writer racing the same stream blocks on the lock, so\n * `seq` is a single total order across processes. A crashed writer's lock is\n * stolen once its PID proves dead (`process.kill(pid, 0)` → `ESRCH`), so a\n * crash never wedges the stream. This is the lock phoenix spike #6673 proved:\n * its only job is an honest order key, not the win.\n *\n * `changes` fires for appends made through THIS instance; a cross-process\n * append lands on disk but is not broadcast here (that is a polling concern the\n * log leaves to its consumer, and the remote-journal work downstream).\n */\nexport function fileJournal<R>(\n dir: string,\n parse: (raw: unknown) => R,\n): Journal<R> {\n const streamPath = (stream: string): string =>\n join(dir, `${encodeURIComponent(stream)}.jsonl`);\n\n const readEntries = async (stream: string): Promise<JournalEntry<R>[]> => {\n const path = streamPath(stream);\n let raw: string;\n try {\n raw = await readFile(path, \"utf8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return [];\n throw err;\n }\n return raw\n .split(\"\\n\")\n .filter((line) => line.length > 0)\n .map((line) => {\n const row = JSON.parse(line) as { seq: Seq; record: unknown };\n return { stream, seq: row.seq, record: parse(row.record) };\n });\n };\n\n return makeJournal<R>({\n read: readEntries,\n async append(stream, record) {\n const entries = await readEntries(stream);\n const entry: JournalEntry<R> = {\n stream,\n seq: entries.length + 1,\n record,\n };\n const path = streamPath(stream);\n const lines = [...entries, entry]\n .map((e) => JSON.stringify({ seq: e.seq, record: e.record }))\n .join(\"\\n\");\n await mkdir(dir, { recursive: true });\n const tmp = `${path}.${process.pid}.tmp`;\n await writeFile(tmp, `${lines}\\n`, \"utf8\");\n await rename(tmp, path);\n return entry;\n },\n async lock(stream, fx) {\n const lockPath = `${streamPath(stream)}.lock`;\n await acquireFileLock(dir, lockPath);\n try {\n return await fx();\n } finally {\n await unlink(lockPath).catch(() => {});\n }\n },\n });\n}\n\n/**\n * Acquire an exclusive lock by creating `lockPath` with `wx` (O_CREAT|O_EXCL) —\n * the one primitive that is atomic across processes. On contention, steal the\n * lock only once its holder's PID proves dead; otherwise spin. `process.kill(pid,\n * 0)` sends no signal — it tests existence: it throws `ESRCH` for a dead PID,\n * and `EPERM` for a live one this process may not signal (still alive, so hold\n * off). A lock that vanishes between the failed create and the read is already\n * free, so retry.\n */\nasync function acquireFileLock(dir: string, lockPath: string): Promise<void> {\n await mkdir(dir, { recursive: true });\n for (;;) {\n try {\n const handle = await open(lockPath, \"wx\");\n try {\n await handle.writeFile(String(process.pid));\n } finally {\n await handle.close();\n }\n return;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") throw err;\n if (await stealIfStale(lockPath)) continue;\n await new Promise((resolve) => setTimeout(resolve, LOCK_SPIN_MS));\n }\n }\n}\n\nasync function stealIfStale(lockPath: string): Promise<boolean> {\n let holder: string;\n try {\n holder = await readFile(lockPath, \"utf8\");\n } catch (err) {\n // Vanished between the failed create and this read — already free.\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return true;\n throw err;\n }\n const pid = Number(holder.trim());\n if (!Number.isInteger(pid) || pid <= 0) return false;\n if (isProcessAlive(pid)) return false;\n await unlink(lockPath).catch(() => {});\n return true;\n}\n\nfunction isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n return (err as NodeJS.ErrnoException).code === \"EPERM\";\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// NodeSub — the three node-native subscription variants.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * A node WebSocket sub. The substrate diffs subs by `id`; the same `id` across\n * transitions = the same socket (kept open). Emit a DIFFERENT `id` to force a\n * reconnect.\n *\n * Only `onMessage` returns `M | null` (null drops the frame) because inbound\n * frames are the boundary that needs a parse-and-maybe-discard seam. `onOpen`/\n * `onClose`/`onError` always dispatch — a spurious one is dropped in the reducer\n * (the cell ignores it), not here.\n */\nexport type NodeWsSub<M> = {\n id: SubId;\n type: \"node_ws\";\n url: string;\n onMessage: (data: string) => M | null;\n onOpen?: () => M;\n onClose?: (code: number, reason: string) => M;\n onError?: (message: string) => M;\n};\n\n/**\n * A node timer sub. One-shot `setTimeout` by default; `repeat: true` →\n * `setInterval`. Cleanup clears the timer — a timer keyed to a phase is\n * cancelled the moment the machine leaves that phase.\n */\nexport type NodeTimerSub<M> = {\n id: SubId;\n type: \"node_timer\";\n delayMs: number;\n msg: M;\n repeat?: boolean;\n};\n\n/**\n * A process-signal sub: `signal` (SIGINT/SIGTERM/…) → `msg`. Cleanup detaches\n * the listener. Lets graceful shutdown be a real machine transition rather than\n * an out-of-band `process.on` that the reducer can't see.\n */\nexport type NodeSignalSub<M> = {\n id: SubId;\n type: \"node_signal\";\n signal: NodeJS.Signals;\n msg: M;\n};\n\nexport type NodeSub<M> = NodeWsSub<M> | NodeTimerSub<M> | NodeSignalSub<M>;\n\n// Compile-time guarantee: `NodeSub<M>` is structurally a `Sub` (has `id`+`type`).\n// Erased at runtime — the `extends Sub` constraint on the alias's type\n// parameter fails to compile if `NodeSub` ever drifts off the `Sub` shape.\nexport type AssertNodeSubIsSub<T extends Sub = NodeSub<unknown>> = T;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Ctx — what the node Subs read / populate.\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type NodeWsRegistry = Map<SubId, WebSocket>;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ws — loaded on first use, never at module load.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * `ws` is an OPTIONAL peer: the `node_ws` Sub needs it, `fileStore` and\n * `fileJournal` — everything the tutorial uses — do not. A static\n * `import … from \"ws\"` puts the dependency on the module's load path, so a\n * consumer who installed only the tutorial's three packages could not import\n * this door at all (`ERR_MODULE_NOT_FOUND`). The type import above is erased,\n * and the value arrives through this loader at the moment a socket is actually\n * opened — so the cost of an optional peer is paid by the feature that needs\n * it, not by the door it happens to share.\n *\n * `createRequire` rather than `await import(\"ws\")` because `subscribe`'s\n * contract is synchronous — the handler must return its cleanup on the same\n * tick the substrate installs it, and a deferred socket would give cleanup\n * nothing to close.\n */\ntype NodeWsConstructor = new (url: string) => WebSocket;\n\nlet cachedWs: NodeWsConstructor | undefined;\n\nfunction loadWebSocket(): NodeWsConstructor {\n if (cachedWs) return cachedWs;\n let mod: unknown;\n try {\n mod = createRequire(import.meta.url)(\"ws\");\n } catch (cause) {\n throw new Error(\n 'The `node_ws` Sub needs the optional peer dependency \"ws\". Install it: `npm install ws`.',\n { cause },\n );\n }\n const ctor = (mod as { default?: NodeWsConstructor }).default ?? mod;\n cachedWs = ctor as NodeWsConstructor;\n return cachedWs;\n}\n\n/**\n * `WebSocket.OPEN`, inlined. Reading it off the class would drag `ws` onto the\n * load path of `sendToWebSocket`, which is a Cmd-side helper a caller can reach\n * without ever opening a socket. The value is fixed by the WHATWG WebSocket\n * spec, not by `ws`.\n */\nconst WS_OPEN = 1;\n\n/** `WebSocket.CLOSED`, inlined for the same reason as {@link WS_OPEN}. */\nconst WS_CLOSED = 3;\n\n/**\n * The Ctx fields the node Subs depend on. A consumer's Ctx must extend this so\n * `node_ws` can register the live socket and Cmd handlers can reach it.\n *\n * Deliberately NOT parameterized by Msg: the registry holds raw `WebSocket`s,\n * and each Sub variant carries its own Msg-typed callbacks. Trip-wire: if a\n * future node Sub holds a Msg-typed callback on ctx, this becomes\n * `NodeSubscribeCtx<M>`.\n */\nexport interface NodeSubscribeCtx {\n wsRegistry: NodeWsRegistry;\n}\n\n/**\n * Write a frame to a registered `node_ws` socket. No-op (returns `false`) when\n * the socket is absent, not OPEN, or `send()` throws. The Cmd-side companion to\n * the `node_ws` Sub: the Sub owns the socket's lifetime; this writes to it.\n *\n * The contract is a boolean no-op, never a throw: the socket can transition out\n * of OPEN between the `readyState` check and `send()` (or `ws` can throw on an\n * errored underlying connection), and a Cmd interpreter must not crash on that.\n */\nexport function sendToWebSocket(\n ctx: NodeSubscribeCtx,\n id: SubId,\n data: string,\n): boolean {\n const socket = ctx.wsRegistry.get(id);\n if (!socket || socket.readyState !== WS_OPEN) return false;\n try {\n socket.send(data);\n return true;\n } catch {\n return false;\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// nodeSubscribe — the handler registry.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Build the `subscribe` handlers for `node_ws`, `node_timer`, and\n * `node_signal`. Matches `Machine#subscribe`'s\n * `[K in U[\"type\"]]: (sub, ctx, dispatch) => () => void` contract for the\n * `NodeSub<M>` variants. Generic `Ctx` must extend `NodeSubscribeCtx` so the\n * `node_ws` handler can register its socket.\n */\nexport function nodeSubscribe<M, Ctx extends NodeSubscribeCtx>(): {\n node_ws: (\n sub: NodeWsSub<M>,\n ctx: Ctx,\n dispatch: (msg: M) => void,\n ) => () => void;\n node_timer: (\n sub: NodeTimerSub<M>,\n ctx: Ctx,\n dispatch: (msg: M) => void,\n ) => () => void;\n node_signal: (\n sub: NodeSignalSub<M>,\n ctx: Ctx,\n dispatch: (msg: M) => void,\n ) => () => void;\n} {\n return {\n node_ws: (sub, ctx, dispatch) => {\n const socket = new (loadWebSocket())(sub.url);\n ctx.wsRegistry.set(sub.id, socket);\n\n socket.on(\"open\", () => {\n if (sub.onOpen) dispatch(sub.onOpen());\n });\n socket.on(\"message\", (data: RawData) => {\n dispatchIfPresent(dispatch, sub.onMessage(data.toString()));\n });\n socket.on(\"close\", (code: number, reason: Buffer) => {\n if (sub.onClose) dispatch(sub.onClose(code, reason.toString()));\n });\n socket.on(\"error\", (err: Error) => {\n if (sub.onError) dispatch(sub.onError(err.message));\n });\n\n return () => {\n ctx.wsRegistry.delete(sub.id);\n socket.removeAllListeners();\n // Swallow teardown-induced errors: terminate()-ing a CONNECTING socket\n // emits an 'error' (\"closed before the connection was established\"),\n // and with our listeners gone that would be an unhandled 'error' that\n // crashes the process. A no-op listener absorbs it.\n socket.on(\"error\", () => {});\n // Close for ANY non-closed state: a graceful 1000 when OPEN, a hard\n // terminate() otherwise (CONNECTING/CLOSING) so a socket caught mid-\n // handshake can't dangle. This is the teardown the hand-driven Cmd-pair\n // approach (`cleanup_browser` closing the ws only `if OPEN`) could not\n // guarantee.\n if (socket.readyState === WS_OPEN) {\n socket.close(1000, \"tea-node sub cleanup\");\n } else if (socket.readyState !== WS_CLOSED) {\n socket.terminate();\n }\n };\n },\n\n node_timer: (sub, _ctx, dispatch) => {\n // Two reconcile lifecycles under one variant: a one-shot (`setTimeout`)\n // that `subscriptions(state)` should drop on the render after it fires,\n // vs a repeating (`setInterval`) one the state keeps listing across\n // renders. Same Sub.type; the `repeat` flag picks the install + cleanup.\n if (sub.repeat) {\n const handle = setInterval(() => dispatch(sub.msg), sub.delayMs);\n return () => clearInterval(handle);\n }\n const handle = setTimeout(() => dispatch(sub.msg), sub.delayMs);\n return () => clearTimeout(handle);\n },\n\n node_signal: (sub, _ctx, dispatch) => {\n const handler = () => dispatch(sub.msg);\n process.on(sub.signal, handler);\n return () => {\n process.off(sub.signal, handler);\n };\n },\n };\n}\n"]}
@@ -1,9 +1,17 @@
1
+ import { C as Cmd, i as CmdValue } from '../core-CTzMJ82M.js';
2
+ import { RetryPolicy } from '../retry-backoff/index.js';
3
+ import { a as ResilientState, f as ResilientTimerMsg, R as RunCmd, F as FailMsg, S as SucceedMsg, C as CircuitConfig, c as RateLimitConfig, D as DeadlineConfig, d as ResilientPorts } from '../index-B5BZ-_cT.js';
4
+ import { a as DeadlineSub } from '../index-q0hXFeZ0.js';
5
+ export { b as deadlineSub, s as subscribeDeadline } from '../index-q0hXFeZ0.js';
6
+ import 'zod';
7
+ import '../types-DP3f5NIl.js';
8
+
1
9
  /**
2
- * @demlik/tea/paginator — the cursor/offset/page-token walk loop as pure
10
+ * internal/paginate/paginator — the cursor/offset/page-token walk loop as pure
3
11
  * state + ops.
4
12
  *
5
- * Same shape as `@demlik/tea/retry-backoff`, `@demlik/tea/circuit-breaker`,
6
- * and `@demlik/tea/rate-limit`: a state type plus pure transition functions,
13
+ * Same shape as `../../../retry-backoff`, `../../resilience/circuit-breaker`,
14
+ * and `../../resilience/rate-limit`: a state type plus pure transition functions,
7
15
  * host-agnostic by construction. Nothing here reads the clock, the RNG, or any
8
16
  * I/O on its own behalf — the consumer fetches a page out-of-band and folds the
9
17
  * *result* back in through `recordPage`. That keeps every function inside TEA's
@@ -100,8 +108,8 @@
100
108
  * `hwm <= 0` would pause immediately, so we treat `hwm <= 0` as "unbounded" and
101
109
  * never pause. The walk then runs flat-out, `fetching → fetching` until `done`.
102
110
  *
103
- * NOT exported from the package root — reached via the `@demlik/tea/paginator`
104
- * subpath, same one-shape-per-package rule as `work-queue`, `retry-backoff`,
111
+ * Internal since #47 — not published on any subpath; reached from inside the
112
+ * package as `internal/paginate/paginator`. Same one-shape-per-package rule as `work-queue`, `retry-backoff`,
105
113
  * `circuit-breaker`, and `rate-limit`. It is the L1 escape hatch the
106
114
  * `paginated-walk` composition builds its walk loop on.
107
115
  */
@@ -268,4 +276,141 @@ declare function resume<Cursor>(state: PaginatorState<Cursor>, policy: Paginator
268
276
  */
269
277
  declare function isDone<Cursor>(state: PaginatorState<Cursor>): boolean;
270
278
 
271
- export { type PaginatorPolicy, type PaginatorState, defaultPaginatorPolicy, drain, initPaginator, isDone, recordPage, resume, start };
279
+ /**
280
+ * The paginated-walk knob. Generic in `Cursor` (the opaque page handle the
281
+ * upstream API hands back — offset, page token, "next" URL, …) and `Page` (the
282
+ * shape your fetch port resolves with).
283
+ *
284
+ * `nextCursor` and `onPage` are PURE config functions applied at the verb
285
+ * boundary, never stored in the slice or in a Cmd (invariant 3 — Cmds are
286
+ * plain data, not closures). `nextCursor(page)` extracts the API's next-page
287
+ * handle (or `null` at the end); `onPage(page)` yields the Cmds the consumer
288
+ * wants emitted for each successfully fetched page (index rows, write to R2,
289
+ * dispatch a downstream Msg — whatever; an empty array is fine).
290
+ *
291
+ * The resilience bricks (`retry` / `circuit` / `rateLimit` / `deadline`) are
292
+ * the SAME optional knobs `resilient-call` exposes — paginated-walk extends it.
293
+ * `rateLimit` is the brick that keeps the walk from 429-ing the upstream.
294
+ */
295
+ interface PaginatedWalkConfig<Cursor, Page, EmittedCmd extends Cmd> {
296
+ /**
297
+ * The cursor the walk fetches first. For an offset API this is typically `0`;
298
+ * for a token API, whatever sentinel the API treats as "page one" (often
299
+ * `null` or `""`). Threaded into `paginator`'s `firstCursor`.
300
+ */
301
+ readonly firstPage: Cursor;
302
+ /**
303
+ * Extract the next cursor from a just-fetched page. Return a `Cursor` to keep
304
+ * walking, or `null` when the API signalled the end (the walk finishes). PURE
305
+ * + consumer-specific (only the consumer knows the API's pagination shape).
306
+ */
307
+ readonly nextCursor: (page: Page) => Cursor | null;
308
+ /**
309
+ * The Cmds to emit for each successfully fetched page — the consumer's work
310
+ * per page (index, persist, fan out, …). Returns plain-data Cmds; `[]` is a
311
+ * valid "I process the page synchronously elsewhere" answer. PURE.
312
+ */
313
+ readonly onPage: (page: Page) => readonly EmittedCmd[];
314
+ /**
315
+ * How many items a page contributes to the paginator's backpressure gauge
316
+ * (`seen`). Defaults to `1` per page (count pages, not items) when omitted; a
317
+ * consumer that wants per-item backpressure returns `page.items.length`. PURE.
318
+ */
319
+ readonly pageSize?: (page: Page) => number;
320
+ /**
321
+ * Backpressure threshold handed to the paginator. `seen >= highWaterMark`
322
+ * parks the walk in `paused` instead of fetching the next page. Non-positive
323
+ * (the default `0`) disables backpressure — the walk runs flat out until the
324
+ * API exhausts. See `../paginator`'s `highWaterMark` docs.
325
+ */
326
+ readonly highWaterMark?: number;
327
+ /** Exponential-backoff retry policy for a transient page-fetch failure. */
328
+ readonly retry?: RetryPolicy;
329
+ /** Circuit breaker around the page fetch (one upstream → one breaker). */
330
+ readonly circuit?: CircuitConfig;
331
+ /** Token-bucket rate limit — the "don't 429 the upstream" knob. */
332
+ readonly rateLimit?: RateLimitConfig;
333
+ /** Overall wall-clock deadline per individual page fetch. */
334
+ readonly deadline?: DeadlineConfig;
335
+ }
336
+ /**
337
+ * The slice. `walk` is the cursor/phase/counters from `../paginator`;
338
+ * `resilience` is the `../resilient-call` slice that gates the page fetch.
339
+ * Every field is JSON-serializable, so the whole walk survives a reload.
340
+ *
341
+ * The two move in lockstep: `walk` decides WHICH cursor to fetch next, while
342
+ * `resilience` decides HOW that fetch is attempted (gated, retried, backed
343
+ * off). `pageOk` advances `walk`; `pageErr` defers to `resilience` and leaves
344
+ * `walk` parked on the same cursor until the page truly settles.
345
+ */
346
+ interface PaginatedWalkState<Cursor, Page> {
347
+ readonly walk: PaginatorState<Cursor>;
348
+ readonly resilience: ResilientState<Cursor, Page>;
349
+ }
350
+ /**
351
+ * The page-fetch effect this knob emits: the inherited `resilient_run` Cmd from
352
+ * resilient-call, whose `input` is the `Cursor` to fetch and whose `key` is the
353
+ * fixed `PAGE_KEY`. The consumer's `handlers(ports)` interprets it.
354
+ */
355
+ type FetchPageCmd<Cursor> = RunCmd<Cursor>;
356
+ /** Page-settled Msgs the `handlers` port dispatches back (inherited verbatim). */
357
+ type PageOkMsg<Page> = SucceedMsg<Page>;
358
+ type PageErrMsg = FailMsg;
359
+ /** The retry / deadline timer Msg — inherited from resilient-call. */
360
+ type PaginatedWalkTimerMsg = ResilientTimerMsg;
361
+ /**
362
+ * Ports the consumer supplies to `handlers`. `run(cursor)` fetches ONE page for
363
+ * the given cursor; throwing routes to `pageErr` (and thus the resilience
364
+ * backoff). Same shape as resilient-call's port, specialized to the page fetch.
365
+ */
366
+ interface PaginatedWalkPorts<Cursor, Page> extends ResilientPorts<Cursor, Page> {
367
+ }
368
+ /** The single resilient-call key every page fetch runs under. */
369
+ declare const PAGE_KEY = "page";
370
+ /**
371
+ * Build a paginated-walk knob from `config`. `rng` is injected for the
372
+ * underlying retry jitter — pass a fixed `() => 0` in tests to pin backoff;
373
+ * defaults to `Math.random` (read at the verb boundary inside resilient-call,
374
+ * never in a `paginated-walk` verb). Inherited straight from resilient-call.
375
+ *
376
+ * Returns the uniform L2 knob contract: `init()`, the verbs `start` / `pageOk`
377
+ * / `pageErr` / `onTimer`, `subs(state)`, and `handlers(ports)`. `Cursor` is
378
+ * the page handle, `Page` the fetch result, `EmittedCmd` the consumer's
379
+ * per-page Cmds plus the inherited `FetchPageCmd`.
380
+ */
381
+ declare function createPaginatedWalk<Cursor, Page, EmittedCmd extends Cmd = Cmd>(config: PaginatedWalkConfig<Cursor, Page, EmittedCmd>, rng?: () => number): {
382
+ init: () => PaginatedWalkState<Cursor, Page>;
383
+ start: (s: PaginatedWalkState<Cursor, Page>, at: number) => readonly [PaginatedWalkState<Cursor, Page>, readonly (EmittedCmd | FetchPageCmd<Cursor>)[]];
384
+ pageOk: (s: PaginatedWalkState<Cursor, Page>, page: Page, at: number) => readonly [PaginatedWalkState<Cursor, Page>, readonly (EmittedCmd | FetchPageCmd<Cursor>)[]];
385
+ pageErr: (s: PaginatedWalkState<Cursor, Page>, error: unknown, at: number) => readonly [PaginatedWalkState<Cursor, Page>, readonly (EmittedCmd | FetchPageCmd<Cursor>)[]];
386
+ onTimer: (s: PaginatedWalkState<Cursor, Page>, msg: PaginatedWalkTimerMsg) => readonly [PaginatedWalkState<Cursor, Page>, readonly (EmittedCmd | FetchPageCmd<Cursor>)[]];
387
+ drain: (s: PaginatedWalkState<Cursor, Page>, n: number) => readonly [PaginatedWalkState<Cursor, Page>, readonly (EmittedCmd | FetchPageCmd<Cursor>)[]];
388
+ resume: (s: PaginatedWalkState<Cursor, Page>, at: number) => readonly [PaginatedWalkState<Cursor, Page>, readonly (EmittedCmd | FetchPageCmd<Cursor>)[]];
389
+ isComplete: (s: PaginatedWalkState<Cursor, Page>) => boolean;
390
+ failure: (s: PaginatedWalkState<Cursor, Page>) => unknown;
391
+ isStuck: (s: PaginatedWalkState<Cursor, Page>) => boolean;
392
+ subs: (s: PaginatedWalkState<Cursor, Page>) => readonly DeadlineSub[];
393
+ handlers: (ports: PaginatedWalkPorts<Cursor, Page>) => {
394
+ resilient_run: (cmd: CmdValue<"resilient_run", {
395
+ readonly key: string;
396
+ readonly input: Cursor;
397
+ }, {
398
+ readonly [detail: string]: unknown;
399
+ readonly _tag: "deadline_exceeded";
400
+ } | {
401
+ readonly [detail: string]: unknown;
402
+ readonly _tag: "port_rejected";
403
+ }, unknown>, ctx: Readonly<Record<never, never>>) => Promise<FailMsg | SucceedMsg<Page>>;
404
+ };
405
+ };
406
+ /**
407
+ * Lift a knob result `[slice, cmds]` into a host `[State, cmds]` where the slice
408
+ * lives at `state.walk`. A convenience for the common single-slice host;
409
+ * consumers with a differently-named field spread by hand. Pure — a thin record
410
+ * rebuild, no clock / RNG.
411
+ */
412
+ declare function liftWalk<S extends {
413
+ walk: PaginatedWalkState<Cursor, Page>;
414
+ }, Cursor, Page, C extends Cmd>(state: S, [slice, cmds]: readonly [PaginatedWalkState<Cursor, Page>, readonly C[]]): readonly [S, readonly C[]];
415
+
416
+ export { DeadlineSub, type FetchPageCmd, PAGE_KEY, type PageErrMsg, type PageOkMsg, type PaginatedWalkConfig, type PaginatedWalkPorts, type PaginatedWalkState, type PaginatedWalkTimerMsg, type PaginatorPolicy, type PaginatorState, createPaginatedWalk, defaultPaginatorPolicy, drain, initPaginator, isDone, liftWalk, recordPage, resume, start };
@@ -0,0 +1,7 @@
1
+ export { PAGE_KEY, createPaginatedWalk, defaultPaginatorPolicy, drain, initPaginator, isDone, liftWalk, recordPage, resume, start } from '../chunk-I5MQMJDW.js';
2
+ import '../chunk-AQ6ZIMEA.js';
3
+ import '../chunk-VBDU4JL6.js';
4
+ export { deadlineSub, subscribeDeadline } from '../chunk-F2US5BW6.js';
5
+ import '../chunk-RQULHGYH.js';
6
+ //# sourceMappingURL=index.js.map
7
+ //# sourceMappingURL=index.js.map
@@ -1,6 +1,6 @@
1
- import { B as BootingRuntime } from '../runtime-types-CNLmhWif.js';
2
- import { C as Cmd, S as Sub, M as Machine } from '../core-Co0LWkIA.js';
3
- import { Trace, RecorderOptions } from '../recorder/index.js';
1
+ import { B as BootingRuntime } from '../runtime-types-LWDoi943.js';
2
+ import { C as Cmd, S as Sub, M as Machine } from '../core-CTzMJ82M.js';
3
+ import { T as Trace, R as RecorderOptions } from '../index-TvRb1KVf.js';
4
4
  import 'better-result';
5
5
  import 'zod';
6
6
 
@@ -113,11 +113,5 @@ declare function normalizeForParity(schema?: ParitySchema): (value: unknown) =>
113
113
  * normalized-JSON equality the gate rests on.
114
114
  */
115
115
  declare function parityEqual(a: unknown, b: unknown): boolean;
116
- /**
117
- * @deprecated Inverted name — it returns `true` when the values are EQUAL, so
118
- * `if (diff(old, new))` reads backwards (#278). Use {@link parityEqual}.
119
- * Alias kept for one minor, then removed.
120
- */
121
- declare const diff: typeof parityEqual;
122
116
 
123
- export { type ParitySchema, type Recording, diff, goldenReplay, normalizeForParity, parityEqual, recordRun };
117
+ export { type ParitySchema, RecorderOptions, type Recording, Trace, goldenReplay, normalizeForParity, parityEqual, recordRun };
@@ -1,8 +1,6 @@
1
- import { deepEqual } from '../chunk-D62FDQKE.js';
2
- import { recorder } from '../chunk-MUUROGIH.js';
3
- import { replay } from '../chunk-ZSUUOEPL.js';
4
- import '../chunk-OOYD44JI.js';
5
- import '../chunk-EWJP3YYP.js';
1
+ import { recorder, deepEqual } from '../chunk-BLJ2JQSH.js';
2
+ import '../chunk-HWVW4AXC.js';
3
+ import { replay } from '../chunk-RQULHGYH.js';
6
4
 
7
5
  // src/parity/index.ts
8
6
  function recordRun(runtime, opts) {
@@ -72,8 +70,7 @@ function isPlainObject(value) {
72
70
  function parityEqual(a, b) {
73
71
  return deepEqual(a, b);
74
72
  }
75
- var diff = parityEqual;
76
73
 
77
- export { diff, goldenReplay, normalizeForParity, parityEqual, recordRun };
74
+ export { goldenReplay, normalizeForParity, parityEqual, recordRun };
78
75
  //# sourceMappingURL=index.js.map
79
76
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/parity/index.ts"],"names":["key"],"mappings":";;;;;;;AAiEO,SAAS,SAAA,CACd,SACA,IAAA,EACiB;AACjB,EAAA,MAAM,GAAA,GAAM,QAAA,CAAS,OAAA,EAAS,IAAI,CAAA;AAClC,EAAA,OAAO;AAAA,IACL,KAAA,EAAO,MAAM,GAAA,CAAI,IAAA,EAAK;AAAA,IACtB,OAAA,EAAS,MAAM,GAAA,CAAI,OAAA,EAAQ;AAAA,IAC3B,IAAA,EAAM,MAAM,GAAA,CAAI,IAAA;AAAK,GACvB;AACF;AAaO,SAAS,YAAA,CAOd,OAAA,EACA,SAAA,EACA,GAAA,EACG;AACH,EAAA,MAAM,KAAA,GAAQ,QAAQ,SAAS,CAAA;AAC/B,EAAA,MAAM,EAAE,KAAA,EAAM,GAAI,MAAA,CAAO,OAAA,EAAS;AAAA,IAChC,MAAM,KAAA,CAAM,IAAA;AAAA,IACZ,GAAA;AAAA,IACA,QAAQ,KAAA,CAAM;AAAA,GACf,CAAA;AACD,EAAA,OAAO,KAAA;AACT;AAGA,SAAS,QAAc,SAAA,EAAuD;AAC5E,EAAA,OAAO,OAAQ,SAAA,CAA8B,KAAA,KAAU,UAAA,GAClD,SAAA,CAA8B,OAAM,GACpC,SAAA;AACP;AAuBA,IAAM,kBAAA,GAAwC,CAAC,IAAA,EAAM,OAAA,EAAS,WAAW,CAAA;AACzE,IAAM,iBAAA,GAAuC,CAAC,QAAA,EAAU,UAAA,EAAY,KAAK,CAAA;AAUlE,SAAS,mBACd,MAAA,EAC6B;AAC7B,EAAA,MAAM,SAAA,GAAY,IAAI,GAAA,CAAI,MAAA,EAAQ,aAAa,kBAAkB,CAAA;AACjE,EAAA,MAAM,QAAA,GAAW,QAAQ,QAAA,IAAY,iBAAA;AAErC,EAAA,MAAM,SAAA,GAAY,CAAC,KAAA,KAA4B;AAC7C,IAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACxB,MAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,GAAA,CAAI,SAAS,CAAA;AAIjC,MAAA,OAAO,KAAA,CACJ,GAAA,CAAI,CAAC,IAAA,EAAM,KAAA,MAAW;AAAA,QACrB,IAAA;AAAA,QACA,KAAA;AAAA,QACA,OAAA,EAAS,cAAA,CAAe,IAAA,EAAM,QAAQ,CAAA;AAAA,QACtC,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,IAAI,CAAA,IAAK;AAAA,QAC9B,CAAA,CACD,IAAA;AAAA,QACC,CAAC,CAAA,EAAG,CAAA,KACF,cAAA,CAAe,CAAA,CAAE,SAAS,CAAA,CAAE,OAAO,CAAA,IACnC,cAAA,CAAe,EAAE,IAAA,EAAM,CAAA,CAAE,IAAI,CAAA,IAC7B,CAAA,CAAE,QAAQ,CAAA,CAAE;AAAA,OAChB,CACC,GAAA,CAAI,CAAC,SAAA,KAAc,UAAU,IAAI,CAAA;AAAA,IACtC;AACA,IAAA,IAAI,aAAA,CAAc,KAAK,CAAA,EAAG;AACxB,MAAA,MAAM,MAA+B,EAAC;AACtC,MAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,IAAA,CAAK,KAAK,EAChC,MAAA,CAAO,CAACA,IAAAA,KAAQ,CAAC,UAAU,GAAA,CAAIA,IAAG,CAAC,CAAA,CACnC,MAAK,EAAG;AACT,QAAA,GAAA,CAAI,GAAG,CAAA,GAAI,SAAA,CAAU,KAAA,CAAM,GAAG,CAAC,CAAA;AAAA,MACjC;AACA,MAAA,OAAO,GAAA;AAAA,IACT;AACA,IAAA,OAAO,KAAA;AAAA,EACT,CAAA;AAEA,EAAA,OAAO,SAAA;AACT;AAGA,SAAS,cAAA,CAAe,MAAe,QAAA,EAAqC;AAC1E,EAAA,IAAI,CAAC,aAAA,CAAc,IAAI,CAAA,EAAG,OAAO,EAAA;AACjC,EAAA,KAAA,MAAW,OAAO,QAAA,EAAU;AAC1B,IAAA,MAAM,SAAA,GAAY,KAAK,GAAG,CAAA;AAC1B,IAAA,IACE,OAAO,cAAc,QAAA,IACrB,OAAO,cAAc,QAAA,IACrB,OAAO,cAAc,SAAA,EACrB;AACA,MAAA,OAAO,OAAO,SAAS,CAAA;AAAA,IACzB;AAAA,EACF;AACA,EAAA,OAAO,EAAA;AACT;AAEA,SAAS,cAAA,CAAe,GAAW,CAAA,EAAmB;AACpD,EAAA,OAAO,CAAA,GAAI,CAAA,GAAI,EAAA,GAAK,CAAA,GAAI,IAAI,CAAA,GAAI,CAAA;AAClC;AAEA,SAAS,cAAc,KAAA,EAAkD;AACvE,EAAA,OAAO,OAAO,UAAU,QAAA,IAAY,KAAA,KAAU,QAAQ,CAAC,KAAA,CAAM,QAAQ,KAAK,CAAA;AAC5E;AASO,SAAS,WAAA,CAAY,GAAY,CAAA,EAAqB;AAC3D,EAAA,OAAO,SAAA,CAAU,GAAG,CAAC,CAAA;AACvB;AAOO,IAAM,IAAA,GAAO","file":"index.js","sourcesContent":["/**\n * the record → replay → normalized-diff go/no-go gate.\n *\n * The parity spike proved the mechanism already composes from in-tree\n * primitives: `recorder` captures a run's Msg sequence (the recording), `replay`\n * re-folds it purely, and `trace-replay`'s order-insensitive deep-equal diffs\n * the result. Non-determinism is a RECORD-TIME property — ids/timestamps freeze\n * into the recording, `replay` never re-mints them — so the same recording\n * replays byte-identical every time.\n *\n * This module is the thin packaging over that substrate plus the one genuinely\n * new helper, `normalizeForParity`: the finding-normalizer that strips\n * record-time non-determinism (generated ids/timestamps) and stable-key-sorts\n * collections, so an OLD-engine golden run and a NEW-engine run diff as equal\n * exactly when they are behaviourally equivalent. Every domain (audit, auth,\n * ingest, …) gets its old-vs-new parity check without reinventing the capture,\n * the normalization, and the diff.\n *\n * ```text\n * recordRun(runtime) → Recording // wraps the recorder\n * goldenReplay(machine, rec) → State // wraps replay\n * normalizeForParity(schema) → (finding) => norm // strip + stable-sort\n * parityEqual(a, b) → boolean // normalized deep-equal\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { BootingRuntime, Cmd, Machine, Sub } from \"../index\";\nimport { replay } from \"../index\";\nimport { type RecorderOptions, recorder, type Trace } from \"../recorder\";\nimport { deepEqual } from \"../trace-replay\";\n\n/**\n * A live parity recording attached to a Runtime — the go/no-go gate's golden\n * artifact. Wraps a {@link recorder}: drive the runtime, then snapshot with\n * {@link Recording.trace} (or hand the live `Recording` straight to\n * {@link goldenReplay}, which snapshots for you).\n *\n * @typeParam S - the machine's State type.\n * @typeParam M - the machine's Msg union.\n */\nexport interface Recording<S, M> {\n /**\n * Snapshot the recording so far as a replayable {@link Trace} — `loaded`\n * (boot state), the ordered non-null `msgs`, and `finalState`. Sufficient to\n * reproduce the run via {@link goldenReplay}. Fresh object each call.\n */\n trace(): Trace<S, M>;\n /** Serialize the recording as JSONL (see {@link recorder}'s `toJSONL`). */\n toJSONL(): string;\n /** Detach the underlying observer. Idempotent. */\n stop(): void;\n}\n\n/**\n * Attach a parity recording to a Runtime, reusing the `recorder`/`historyTracker`\n * capture (the boot observe for `loaded` + every non-null msg in order) rather\n * than a fresh observer path.\n *\n * Attach on the synchronous `run()` handle so the boot transition is captured\n * as `loaded`; drive the runtime; then `trace()` (or pass the handle to\n * {@link goldenReplay}). See {@link RecorderOptions} for `captureSteps` (needed\n * only for the per-step / resume-cache-prefix inspection, not for replay).\n */\nexport function recordRun<S, M extends { type: string }>(\n runtime: BootingRuntime<S, M>,\n opts?: RecorderOptions,\n): Recording<S, M> {\n const rec = recorder(runtime, opts);\n return {\n trace: () => rec.dump(),\n toJSONL: () => rec.toJSONL(),\n stop: () => rec.stop(),\n };\n}\n\n/**\n * Reproduce a recording's final state by folding its msgs through the pure\n * `replay` — `init(loaded, ctx)` then `update` per msg, never `interpret`,\n * never a `Store`, never a live subscription. Accepts a live {@link Recording}\n * (snapshotted here) or a bare {@link Trace}.\n *\n * `ctx` is the ENVIRONMENT supplied fresh at replay time (the trace is the\n * INPUT: `loaded` + `msgs`), mirroring `replayTrace`. It is optional so a\n * void-ctx machine can call `goldenReplay(machine, rec)`; a machine that reads\n * ctx passes it explicitly.\n */\nexport function goldenReplay<\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 recording: Recording<S, M> | Trace<S, M>,\n ctx?: Ctx,\n): S {\n const trace = asTrace(recording);\n const { state } = replay(machine, {\n msgs: trace.msgs,\n ctx: ctx as Ctx,\n loaded: trace.loaded,\n });\n return state;\n}\n\n/** Narrow a `Recording | Trace` to a `Trace`, snapshotting a live recording. */\nfunction asTrace<S, M>(recording: Recording<S, M> | Trace<S, M>): Trace<S, M> {\n return typeof (recording as Recording<S, M>).trace === \"function\"\n ? (recording as Recording<S, M>).trace()\n : (recording as Trace<S, M>);\n}\n\n/** Configuration for {@link normalizeForParity}. */\nexport interface ParitySchema {\n /**\n * Object keys stripped wherever they appear — the record-time\n * non-determinism `replay` never re-mints. Default: `id`, `runId`,\n * `timestamp`.\n */\n readonly stripKeys?: readonly string[];\n /**\n * Candidate keys used to stable-sort arrays of objects. For each array the\n * first candidate present (as a primitive) on the items decides the order;\n * arrays with no candidate fall back to canonical-JSON order. Default:\n * `ruleId`, `selector`, `url`.\n */\n readonly sortKeys?: readonly string[];\n}\n\n// Defaults proven by the spike: strip the generated id/runId/timestamp,\n// stable-key-sort by ruleId | selector | url. Fan-out ordering is already\n// deterministic on this substrate (array order); the sort only defends against\n// intra-effect concurrency (Promise.all / Set).\nconst DEFAULT_STRIP_KEYS: readonly string[] = [\"id\", \"runId\", \"timestamp\"];\nconst DEFAULT_SORT_KEYS: readonly string[] = [\"ruleId\", \"selector\", \"url\"];\n\n/**\n * Build a finding-normalizer: a pure `(value) => normalized` that strips the\n * schema's `stripKeys`, recursively sorts object keys, and stable-key-sorts\n * arrays by the schema's `sortKeys`. Two runs that differ only in generated\n * ids/timestamps or intra-effect ordering normalize to a byte-identical value,\n * so {@link parityEqual} on the normalized pair reflects behavioural (not incidental)\n * difference.\n */\nexport function normalizeForParity(\n schema?: ParitySchema,\n): (value: unknown) => unknown {\n const stripKeys = new Set(schema?.stripKeys ?? DEFAULT_STRIP_KEYS);\n const sortKeys = schema?.sortKeys ?? DEFAULT_SORT_KEYS;\n\n const normalize = (value: unknown): unknown => {\n if (Array.isArray(value)) {\n const items = value.map(normalize);\n // Decorate-sort-undecorate: primary key = first present sortKey value,\n // secondary = canonical JSON (items are already normalized so keys are\n // sorted), original index last for a stable total order.\n return items\n .map((item, index) => ({\n item,\n index,\n primary: primarySortKey(item, sortKeys),\n json: JSON.stringify(item) ?? \"\",\n }))\n .sort(\n (a, b) =>\n compareStrings(a.primary, b.primary) ||\n compareStrings(a.json, b.json) ||\n a.index - b.index,\n )\n .map((decorated) => decorated.item);\n }\n if (isPlainObject(value)) {\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(value)\n .filter((key) => !stripKeys.has(key))\n .sort()) {\n out[key] = normalize(value[key]);\n }\n return out;\n }\n return value;\n };\n\n return normalize;\n}\n\n/** The first present `sortKeys` candidate on `item` as a string, or `\"\"`. */\nfunction primarySortKey(item: unknown, sortKeys: readonly string[]): string {\n if (!isPlainObject(item)) return \"\";\n for (const key of sortKeys) {\n const candidate = item[key];\n if (\n typeof candidate === \"string\" ||\n typeof candidate === \"number\" ||\n typeof candidate === \"boolean\"\n ) {\n return String(candidate);\n }\n }\n return \"\";\n}\n\nfunction compareStrings(a: string, b: string): number {\n return a < b ? -1 : a > b ? 1 : 0;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * The parity verdict: `true` iff `a` and `b` are structurally equal. Reuses\n * `trace-replay`'s order-insensitive deep-equal (the comparator the\n * record/replay lane owns) rather than adding another boolean deep-compare —\n * feed it the pair produced by {@link normalizeForParity} for the\n * normalized-JSON equality the gate rests on.\n */\nexport function parityEqual(a: unknown, b: unknown): boolean {\n return deepEqual(a, b);\n}\n\n/**\n * @deprecated Inverted name — it returns `true` when the values are EQUAL, so\n * `if (diff(old, new))` reads backwards (#278). Use {@link parityEqual}.\n * Alias kept for one minor, then removed.\n */\nexport const diff = parityEqual;\n"]}
1
+ {"version":3,"sources":["../../src/parity/index.ts"],"names":["key"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * the record → replay → normalized-diff go/no-go gate.\n *\n * The parity spike proved the mechanism already composes from in-tree\n * primitives: `recorder` captures a run's Msg sequence (the recording), `replay`\n * re-folds it purely, and `trace-replay`'s order-insensitive deep-equal diffs\n * the result. Non-determinism is a RECORD-TIME property — ids/timestamps freeze\n * into the recording, `replay` never re-mints them — so the same recording\n * replays byte-identical every time.\n *\n * This module is the thin packaging over that substrate plus the one genuinely\n * new helper, `normalizeForParity`: the finding-normalizer that strips\n * record-time non-determinism (generated ids/timestamps) and stable-key-sorts\n * collections, so an OLD-engine golden run and a NEW-engine run diff as equal\n * exactly when they are behaviourally equivalent. Every domain (audit, auth,\n * ingest, …) gets its old-vs-new parity check without reinventing the capture,\n * the normalization, and the diff.\n *\n * ```text\n * recordRun(runtime) → Recording // wraps the recorder\n * goldenReplay(machine, rec) → State // wraps replay\n * normalizeForParity(schema) → (finding) => norm // strip + stable-sort\n * parityEqual(a, b) → boolean // normalized deep-equal\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { BootingRuntime, Cmd, Machine, Sub } from \"../index\";\nimport { replay } from \"../index\";\nimport {\n type RecorderOptions,\n recorder,\n type Trace,\n} from \"../internal/persistence/recorder\";\nimport { deepEqual } from \"../internal/persistence/trace-replay\";\n\n/**\n * The two recorder types this module's own signatures name — `Trace` is what\n * {@link Recording.trace} returns and what {@link goldenReplay} accepts bare,\n * `RecorderOptions` is what {@link recordRun} takes. The recorder itself is\n * internal, so this door is the only place either type can be named from\n * outside the package.\n */\nexport type { RecorderOptions, Trace } from \"../internal/persistence/recorder\";\n\n/**\n * A live parity recording attached to a Runtime — the go/no-go gate's golden\n * artifact. Wraps a {@link recorder}: drive the runtime, then snapshot with\n * {@link Recording.trace} (or hand the live `Recording` straight to\n * {@link goldenReplay}, which snapshots for you).\n *\n * @typeParam S - the machine's State type.\n * @typeParam M - the machine's Msg union.\n */\nexport interface Recording<S, M> {\n /**\n * Snapshot the recording so far as a replayable {@link Trace} — `loaded`\n * (boot state), the ordered non-null `msgs`, and `finalState`. Sufficient to\n * reproduce the run via {@link goldenReplay}. Fresh object each call.\n */\n trace(): Trace<S, M>;\n /** Serialize the recording as JSONL (see {@link recorder}'s `toJSONL`). */\n toJSONL(): string;\n /** Detach the underlying observer. Idempotent. */\n stop(): void;\n}\n\n/**\n * Attach a parity recording to a Runtime, reusing the `recorder`/`historyTracker`\n * capture (the boot observe for `loaded` + every non-null msg in order) rather\n * than a fresh observer path.\n *\n * Attach on the synchronous `run()` handle so the boot transition is captured\n * as `loaded`; drive the runtime; then `trace()` (or pass the handle to\n * {@link goldenReplay}). See {@link RecorderOptions} for `captureSteps` (needed\n * only for the per-step / resume-cache-prefix inspection, not for replay).\n */\nexport function recordRun<S, M extends { type: string }>(\n runtime: BootingRuntime<S, M>,\n opts?: RecorderOptions,\n): Recording<S, M> {\n const rec = recorder(runtime, opts);\n return {\n trace: () => rec.dump(),\n toJSONL: () => rec.toJSONL(),\n stop: () => rec.stop(),\n };\n}\n\n/**\n * Reproduce a recording's final state by folding its msgs through the pure\n * `replay` — `init(loaded, ctx)` then `update` per msg, never `interpret`,\n * never a `Store`, never a live subscription. Accepts a live {@link Recording}\n * (snapshotted here) or a bare {@link Trace}.\n *\n * `ctx` is the ENVIRONMENT supplied fresh at replay time (the trace is the\n * INPUT: `loaded` + `msgs`), mirroring `replayTrace`. It is optional so a\n * void-ctx machine can call `goldenReplay(machine, rec)`; a machine that reads\n * ctx passes it explicitly.\n */\nexport function goldenReplay<\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 recording: Recording<S, M> | Trace<S, M>,\n ctx?: Ctx,\n): S {\n const trace = asTrace(recording);\n const { state } = replay(machine, {\n msgs: trace.msgs,\n ctx: ctx as Ctx,\n loaded: trace.loaded,\n });\n return state;\n}\n\n/** Narrow a `Recording | Trace` to a `Trace`, snapshotting a live recording. */\nfunction asTrace<S, M>(recording: Recording<S, M> | Trace<S, M>): Trace<S, M> {\n return typeof (recording as Recording<S, M>).trace === \"function\"\n ? (recording as Recording<S, M>).trace()\n : (recording as Trace<S, M>);\n}\n\n/** Configuration for {@link normalizeForParity}. */\nexport interface ParitySchema {\n /**\n * Object keys stripped wherever they appear — the record-time\n * non-determinism `replay` never re-mints. Default: `id`, `runId`,\n * `timestamp`.\n */\n readonly stripKeys?: readonly string[];\n /**\n * Candidate keys used to stable-sort arrays of objects. For each array the\n * first candidate present (as a primitive) on the items decides the order;\n * arrays with no candidate fall back to canonical-JSON order. Default:\n * `ruleId`, `selector`, `url`.\n */\n readonly sortKeys?: readonly string[];\n}\n\n// Defaults proven by the spike: strip the generated id/runId/timestamp,\n// stable-key-sort by ruleId | selector | url. Fan-out ordering is already\n// deterministic on this substrate (array order); the sort only defends against\n// intra-effect concurrency (Promise.all / Set).\nconst DEFAULT_STRIP_KEYS: readonly string[] = [\"id\", \"runId\", \"timestamp\"];\nconst DEFAULT_SORT_KEYS: readonly string[] = [\"ruleId\", \"selector\", \"url\"];\n\n/**\n * Build a finding-normalizer: a pure `(value) => normalized` that strips the\n * schema's `stripKeys`, recursively sorts object keys, and stable-key-sorts\n * arrays by the schema's `sortKeys`. Two runs that differ only in generated\n * ids/timestamps or intra-effect ordering normalize to a byte-identical value,\n * so {@link parityEqual} on the normalized pair reflects behavioural (not incidental)\n * difference.\n */\nexport function normalizeForParity(\n schema?: ParitySchema,\n): (value: unknown) => unknown {\n const stripKeys = new Set(schema?.stripKeys ?? DEFAULT_STRIP_KEYS);\n const sortKeys = schema?.sortKeys ?? DEFAULT_SORT_KEYS;\n\n const normalize = (value: unknown): unknown => {\n if (Array.isArray(value)) {\n const items = value.map(normalize);\n // Decorate-sort-undecorate: primary key = first present sortKey value,\n // secondary = canonical JSON (items are already normalized so keys are\n // sorted), original index last for a stable total order.\n return items\n .map((item, index) => ({\n item,\n index,\n primary: primarySortKey(item, sortKeys),\n json: JSON.stringify(item) ?? \"\",\n }))\n .sort(\n (a, b) =>\n compareStrings(a.primary, b.primary) ||\n compareStrings(a.json, b.json) ||\n a.index - b.index,\n )\n .map((decorated) => decorated.item);\n }\n if (isPlainObject(value)) {\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(value)\n .filter((key) => !stripKeys.has(key))\n .sort()) {\n out[key] = normalize(value[key]);\n }\n return out;\n }\n return value;\n };\n\n return normalize;\n}\n\n/** The first present `sortKeys` candidate on `item` as a string, or `\"\"`. */\nfunction primarySortKey(item: unknown, sortKeys: readonly string[]): string {\n if (!isPlainObject(item)) return \"\";\n for (const key of sortKeys) {\n const candidate = item[key];\n if (\n typeof candidate === \"string\" ||\n typeof candidate === \"number\" ||\n typeof candidate === \"boolean\"\n ) {\n return String(candidate);\n }\n }\n return \"\";\n}\n\nfunction compareStrings(a: string, b: string): number {\n return a < b ? -1 : a > b ? 1 : 0;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * The parity verdict: `true` iff `a` and `b` are structurally equal. Reuses\n * `trace-replay`'s order-insensitive deep-equal (the comparator the\n * record/replay lane owns) rather than adding another boolean deep-compare —\n * feed it the pair produced by {@link normalizeForParity} for the\n * normalized-JSON equality the gate rests on.\n */\nexport function parityEqual(a: unknown, b: unknown): boolean {\n return deepEqual(a, b);\n}\n"]}