@demlik/tea 0.13.0 → 0.15.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 (373) hide show
  1. package/README.md +11 -9
  2. package/dist/agent/index.d.ts +638 -45
  3. package/dist/agent/index.js +9 -19
  4. package/dist/{chunk-RH5EFRVJ.js → chunk-22POWGFA.js} +31 -5
  5. package/dist/chunk-22POWGFA.js.map +1 -0
  6. package/dist/{chunk-DMYGQB4O.js → chunk-3B5X7SBB.js} +23 -6
  7. package/dist/chunk-3B5X7SBB.js.map +1 -0
  8. package/dist/chunk-66PY5WYF.js +1816 -0
  9. package/dist/chunk-66PY5WYF.js.map +1 -0
  10. package/dist/{chunk-25LBDCBC.js → chunk-6IQPODUF.js} +2 -2
  11. package/dist/{chunk-25LBDCBC.js.map → chunk-6IQPODUF.js.map} +1 -1
  12. package/dist/chunk-AYS5N6WY.js +505 -0
  13. package/dist/chunk-AYS5N6WY.js.map +1 -0
  14. package/dist/{chunk-EWJP3YYP.js → chunk-HWVW4AXC.js} +3 -3
  15. package/dist/chunk-HWVW4AXC.js.map +1 -0
  16. package/dist/{chunk-O4MVQTEJ.js → chunk-L3TND7LA.js} +20 -16
  17. package/dist/chunk-L3TND7LA.js.map +1 -0
  18. package/dist/{chunk-J6527VRQ.js → chunk-LFZUK6J2.js} +3 -3
  19. package/dist/chunk-LFZUK6J2.js.map +1 -0
  20. package/dist/{chunk-D736QUGF.js → chunk-MYXFNWAJ.js} +4 -5
  21. package/dist/chunk-MYXFNWAJ.js.map +1 -0
  22. package/dist/{chunk-K575PT4S.js → chunk-PR6TB3BL.js} +15 -4
  23. package/dist/chunk-PR6TB3BL.js.map +1 -0
  24. package/dist/{chunk-YUXP55T2.js → chunk-Q5CQTBFA.js} +57 -7
  25. package/dist/chunk-Q5CQTBFA.js.map +1 -0
  26. package/dist/{batch-window/index.js → chunk-QPEVCJ5Q.js} +4 -8
  27. package/dist/chunk-QPEVCJ5Q.js.map +1 -0
  28. package/dist/chunk-TCLBCERH.js +1480 -0
  29. package/dist/chunk-TCLBCERH.js.map +1 -0
  30. package/dist/{chunk-FV3Z5MBE.js → chunk-TXN2KFHF.js} +14 -4
  31. package/dist/chunk-TXN2KFHF.js.map +1 -0
  32. package/dist/{chunk-MUUROGIH.js → chunk-V46HSVCK.js} +62 -12
  33. package/dist/chunk-V46HSVCK.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-ZVJZ72HY.js → chunk-VTQI2NHP.js} +25 -11
  37. package/dist/chunk-VTQI2NHP.js.map +1 -0
  38. package/dist/{core-Co0LWkIA.d.ts → core-DnzqWy6u.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 +43 -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-CWzA-o2B.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 +1284 -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/index-Bc40JWVl.d.ts +131 -0
  55. package/dist/{batch-window/index.d.ts → index-C2DwkjlP.d.ts} +7 -12
  56. package/dist/index-CA_7I5g9.d.ts +806 -0
  57. package/dist/index-CSyMYDAB.d.ts +69 -0
  58. package/dist/{fan-out/index.d.ts → index-CuNO9m8i.d.ts} +7 -9
  59. package/dist/{debounce/index.d.ts → index-DHSUmQTR.d.ts} +7 -7
  60. package/dist/{recorder/index.d.ts → index-DrIRb_Ch.d.ts} +4 -7
  61. package/dist/index-HSYgG2Yt.d.ts +380 -0
  62. package/dist/{monitored-run/index.d.ts → index-KXqTRJyb.d.ts} +37 -13
  63. package/dist/{snapshot/index.d.ts → index-ZCMhnVKN.d.ts} +15 -78
  64. package/dist/index.d.ts +771 -6
  65. package/dist/index.js +1 -2
  66. package/dist/jev/index.d.ts +646 -0
  67. package/dist/jev/index.js +485 -0
  68. package/dist/jev/index.js.map +1 -0
  69. package/dist/machine-DGlPWCWI.d.ts +1781 -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 +140 -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/{protocol-B5YmJr0d.d.ts → protocol-D8VbH-VH.d.ts} +10 -1
  90. package/dist/react/index.d.ts +2 -2
  91. package/dist/react/index.js +1 -2
  92. package/dist/react/index.js.map +1 -1
  93. package/dist/resilience/index.d.ts +744 -0
  94. package/dist/resilience/index.js +657 -0
  95. package/dist/resilience/index.js.map +1 -0
  96. package/dist/retry-backoff/index.d.ts +3 -2
  97. package/dist/retry-backoff/index.js +1 -1
  98. package/dist/runtime-types-CMF13BBh.d.ts +998 -0
  99. package/dist/testing/index.d.ts +147 -3
  100. package/dist/testing/index.js +95 -3
  101. package/dist/testing/index.js.map +1 -1
  102. package/dist/{throttled-input → timing}/index.d.ts +143 -12
  103. package/dist/{throttled-input → timing}/index.js +57 -12
  104. package/dist/timing/index.js.map +1 -0
  105. package/dist/{types-XS_miRnB.d.ts → types-BseNPIhr.d.ts} +1 -1
  106. package/dist/work-queue/index.d.ts +130 -54
  107. package/dist/work-queue/index.js +3 -2
  108. package/dist/work-queue/index.js.map +1 -1
  109. package/package.json +45 -218
  110. package/dist/authed-call/index.d.ts +0 -120
  111. package/dist/authed-call/index.js +0 -134
  112. package/dist/authed-call/index.js.map +0 -1
  113. package/dist/await-terminal/index.d.ts +0 -101
  114. package/dist/await-terminal/index.js +0 -76
  115. package/dist/await-terminal/index.js.map +0 -1
  116. package/dist/batch-window/index.js.map +0 -1
  117. package/dist/cache/index.d.ts +0 -152
  118. package/dist/cache/index.js +0 -8
  119. package/dist/chart/index.d.ts +0 -218
  120. package/dist/chart/index.js +0 -16
  121. package/dist/chart/index.js.map +0 -1
  122. package/dist/chart/inspect/index.d.ts +0 -86
  123. package/dist/chart/inspect/index.js +0 -5
  124. package/dist/chart/inspect/react.d.ts +0 -113
  125. package/dist/chart/inspect/react.js +0 -11
  126. package/dist/chart/inspect/react.js.map +0 -1
  127. package/dist/chart/inspect/styles.css +0 -286
  128. package/dist/chart/lane/index.d.ts +0 -362
  129. package/dist/chart/lane/index.js +0 -9
  130. package/dist/chart/lane/index.js.map +0 -1
  131. package/dist/chart/lane/react.d.ts +0 -84
  132. package/dist/chart/lane/react.js +0 -559
  133. package/dist/chart/lane/react.js.map +0 -1
  134. package/dist/chart/lane/server.d.ts +0 -148
  135. package/dist/chart/lane/server.js +0 -209
  136. package/dist/chart/lane/server.js.map +0 -1
  137. package/dist/chart/lane/styles.css +0 -498
  138. package/dist/chart/lane/viewer/assets/abnfDiagram-N423BO3Z-C729LI31.js +0 -1
  139. package/dist/chart/lane/viewer/assets/architectureDiagram-T3A2C74G-C729LI31.js +0 -1
  140. package/dist/chart/lane/viewer/assets/blockDiagram-VBNYF7ZC-C729LI31.js +0 -1
  141. package/dist/chart/lane/viewer/assets/c4Diagram-5PPSVZJV-C729LI31.js +0 -1
  142. package/dist/chart/lane/viewer/assets/chunk-2GRJ4B5K-HdDf8SI3.js +0 -1
  143. package/dist/chart/lane/viewer/assets/chunk-5RXB4S5H-CcNtpUd7.js +0 -231
  144. package/dist/chart/lane/viewer/assets/chunk-5VM5RSS4-BP4fRS_5.js +0 -15
  145. package/dist/chart/lane/viewer/assets/chunk-KBJHAD2P-DZOw7xU6.js +0 -1
  146. package/dist/chart/lane/viewer/assets/chunk-NSK5VX7P-CBCyTZMs.js +0 -128
  147. package/dist/chart/lane/viewer/assets/classDiagram-JCYQIIEL-C729LI31.js +0 -1
  148. package/dist/chart/lane/viewer/assets/classDiagram-v2-OCEON4UE-D4uAiFr8.js +0 -206
  149. package/dist/chart/lane/viewer/assets/cose-bilkent-JH36ORCC-DOwCrGXH.js +0 -1
  150. package/dist/chart/lane/viewer/assets/cynefinDiagram-MW4NZA55-C729LI31.js +0 -1
  151. package/dist/chart/lane/viewer/assets/dagre-K_PewWfS.js +0 -1
  152. package/dist/chart/lane/viewer/assets/dagre-VZM6K2ZE-831qZcA8.js +0 -4
  153. package/dist/chart/lane/viewer/assets/diagram-7IWD3JNH-C729LI31.js +0 -1
  154. package/dist/chart/lane/viewer/assets/diagram-B4RE2ZJO-C729LI31.js +0 -1
  155. package/dist/chart/lane/viewer/assets/diagram-LBJQPF4R-C729LI31.js +0 -1
  156. package/dist/chart/lane/viewer/assets/diagram-Q27KOJAE-C729LI31.js +0 -1
  157. package/dist/chart/lane/viewer/assets/diagram-UB23O5K3-C729LI31.js +0 -1
  158. package/dist/chart/lane/viewer/assets/ebnfDiagram-BXEA7PRR-C729LI31.js +0 -1
  159. package/dist/chart/lane/viewer/assets/erDiagram-JOGREHBK-C729LI31.js +0 -1
  160. package/dist/chart/lane/viewer/assets/flowDiagram-UKHOOZJN-C729LI31.js +0 -1
  161. package/dist/chart/lane/viewer/assets/ganttDiagram-PKOTCBZU-C729LI31.js +0 -1
  162. package/dist/chart/lane/viewer/assets/gitGraphDiagram-DS77QQ5N-C729LI31.js +0 -1
  163. package/dist/chart/lane/viewer/assets/index-9SkH5NA3.css +0 -1
  164. package/dist/chart/lane/viewer/assets/index-Bgt4sXpW.js +0 -195
  165. package/dist/chart/lane/viewer/assets/infoDiagram-6WML65LV-C729LI31.js +0 -1
  166. package/dist/chart/lane/viewer/assets/ishikawaDiagram-WSZJBQD7-C729LI31.js +0 -1
  167. package/dist/chart/lane/viewer/assets/journeyDiagram-NVQOT4AX-C729LI31.js +0 -1
  168. package/dist/chart/lane/viewer/assets/kanban-definition-27J2QSJJ-G7PaPzy-.js +0 -89
  169. package/dist/chart/lane/viewer/assets/katex-DOwCrGXH.js +0 -1
  170. package/dist/chart/lane/viewer/assets/mindmap-definition-FAOFIHXS-CGj9nrVk.js +0 -96
  171. package/dist/chart/lane/viewer/assets/path-fybaL0A-.js +0 -1
  172. package/dist/chart/lane/viewer/assets/pegDiagram-VL7TDLO6-C729LI31.js +0 -1
  173. package/dist/chart/lane/viewer/assets/pieDiagram-7S7Q4E2Y-C729LI31.js +0 -1
  174. package/dist/chart/lane/viewer/assets/quadrantDiagram-CIZ2JOQS-C729LI31.js +0 -1
  175. package/dist/chart/lane/viewer/assets/railroadDiagram-AXF67PYL-C729LI31.js +0 -1
  176. package/dist/chart/lane/viewer/assets/requirementDiagram-LRYGKXZP-C729LI31.js +0 -1
  177. package/dist/chart/lane/viewer/assets/sankeyDiagram-W5VNT64P-C729LI31.js +0 -1
  178. package/dist/chart/lane/viewer/assets/sequenceDiagram-SI44F4Z6-C729LI31.js +0 -1
  179. package/dist/chart/lane/viewer/assets/stateDiagram-OKZ733FA-B_88AzBS.js +0 -1
  180. package/dist/chart/lane/viewer/assets/stateDiagram-v2-UEYNNEHI-CR5gcgq6.js +0 -1
  181. package/dist/chart/lane/viewer/assets/swimlanes-SLNWSIFB-B4xK-rwg.js +0 -1
  182. package/dist/chart/lane/viewer/assets/swimlanesDiagram-ULZ7WXOC-C729LI31.js +0 -1
  183. package/dist/chart/lane/viewer/assets/timeline-definition-Z64GVDOM-CCv52MfY.js +0 -120
  184. package/dist/chart/lane/viewer/assets/vennDiagram-T6HMQDX7-C729LI31.js +0 -1
  185. package/dist/chart/lane/viewer/assets/wardleyDiagram-T6FBY63Y-C729LI31.js +0 -1
  186. package/dist/chart/lane/viewer/assets/xychartDiagram-ELKLHX3M-C729LI31.js +0 -1
  187. package/dist/chart/lane/viewer/index.html +0 -15
  188. package/dist/chart/report/index.d.ts +0 -117
  189. package/dist/chart/report/index.js +0 -32
  190. package/dist/chart/report/index.js.map +0 -1
  191. package/dist/chunk-2YFLOCWX.js +0 -15
  192. package/dist/chunk-2YFLOCWX.js.map +0 -1
  193. package/dist/chunk-3I52QZZ5.js +0 -519
  194. package/dist/chunk-3I52QZZ5.js.map +0 -1
  195. package/dist/chunk-3ZAFFRRB.js +0 -457
  196. package/dist/chunk-3ZAFFRRB.js.map +0 -1
  197. package/dist/chunk-4EQZ76JC.js +0 -33
  198. package/dist/chunk-4EQZ76JC.js.map +0 -1
  199. package/dist/chunk-4KLG43GY.js +0 -223
  200. package/dist/chunk-4KLG43GY.js.map +0 -1
  201. package/dist/chunk-7FRZ2CT2.js +0 -424
  202. package/dist/chunk-7FRZ2CT2.js.map +0 -1
  203. package/dist/chunk-AZZLE7XR.js +0 -62
  204. package/dist/chunk-AZZLE7XR.js.map +0 -1
  205. package/dist/chunk-BYQUGDD3.js +0 -16
  206. package/dist/chunk-BYQUGDD3.js.map +0 -1
  207. package/dist/chunk-CIQG5L42.js +0 -685
  208. package/dist/chunk-CIQG5L42.js.map +0 -1
  209. package/dist/chunk-CLM6ERNO.js +0 -61
  210. package/dist/chunk-CLM6ERNO.js.map +0 -1
  211. package/dist/chunk-COS5MSTO.js +0 -88
  212. package/dist/chunk-COS5MSTO.js.map +0 -1
  213. package/dist/chunk-CRPZJVD2.js +0 -71
  214. package/dist/chunk-CRPZJVD2.js.map +0 -1
  215. package/dist/chunk-D62FDQKE.js +0 -53
  216. package/dist/chunk-D62FDQKE.js.map +0 -1
  217. package/dist/chunk-D736QUGF.js.map +0 -1
  218. package/dist/chunk-DGE4PV4A.js +0 -26
  219. package/dist/chunk-DGE4PV4A.js.map +0 -1
  220. package/dist/chunk-DMYGQB4O.js.map +0 -1
  221. package/dist/chunk-EWJP3YYP.js.map +0 -1
  222. package/dist/chunk-FV3Z5MBE.js.map +0 -1
  223. package/dist/chunk-HCEVHFJ7.js +0 -54
  224. package/dist/chunk-HCEVHFJ7.js.map +0 -1
  225. package/dist/chunk-J6527VRQ.js.map +0 -1
  226. package/dist/chunk-JELSZN25.js +0 -47
  227. package/dist/chunk-JELSZN25.js.map +0 -1
  228. package/dist/chunk-K575PT4S.js.map +0 -1
  229. package/dist/chunk-L57ZOT5K.js +0 -184
  230. package/dist/chunk-L57ZOT5K.js.map +0 -1
  231. package/dist/chunk-MU6MCIAC.js +0 -15
  232. package/dist/chunk-MU6MCIAC.js.map +0 -1
  233. package/dist/chunk-MUUROGIH.js.map +0 -1
  234. package/dist/chunk-NR7BU22N.js +0 -758
  235. package/dist/chunk-NR7BU22N.js.map +0 -1
  236. package/dist/chunk-O4MVQTEJ.js.map +0 -1
  237. package/dist/chunk-OOYD44JI.js +0 -317
  238. package/dist/chunk-OOYD44JI.js.map +0 -1
  239. package/dist/chunk-Q2UM7BXU.js.map +0 -1
  240. package/dist/chunk-RH5EFRVJ.js.map +0 -1
  241. package/dist/chunk-S2EGRPCM.js +0 -47
  242. package/dist/chunk-S2EGRPCM.js.map +0 -1
  243. package/dist/chunk-SINXE47Y.js +0 -55
  244. package/dist/chunk-SINXE47Y.js.map +0 -1
  245. package/dist/chunk-UF4MZSLT.js +0 -53
  246. package/dist/chunk-UF4MZSLT.js.map +0 -1
  247. package/dist/chunk-UOC2PL46.js +0 -13
  248. package/dist/chunk-UOC2PL46.js.map +0 -1
  249. package/dist/chunk-VYFAZN3F.js +0 -372
  250. package/dist/chunk-VYFAZN3F.js.map +0 -1
  251. package/dist/chunk-WRAA7GVZ.js +0 -209
  252. package/dist/chunk-WRAA7GVZ.js.map +0 -1
  253. package/dist/chunk-XGELA764.js +0 -163
  254. package/dist/chunk-XGELA764.js.map +0 -1
  255. package/dist/chunk-YUXP55T2.js.map +0 -1
  256. package/dist/chunk-YWIKDKHY.js +0 -307
  257. package/dist/chunk-YWIKDKHY.js.map +0 -1
  258. package/dist/chunk-ZJ5AOYBH.js +0 -8
  259. package/dist/chunk-ZJ5AOYBH.js.map +0 -1
  260. package/dist/chunk-ZSUUOEPL.js +0 -731
  261. package/dist/chunk-ZSUUOEPL.js.map +0 -1
  262. package/dist/chunk-ZVJZ72HY.js.map +0 -1
  263. package/dist/circuit-breaker/index.d.ts +0 -197
  264. package/dist/circuit-breaker/index.js +0 -3
  265. package/dist/circuit-breaker/index.js.map +0 -1
  266. package/dist/deadline/index.d.ts +0 -199
  267. package/dist/deadline/index.js +0 -6
  268. package/dist/deadline/index.js.map +0 -1
  269. package/dist/debounce/index.js +0 -3
  270. package/dist/debounce/index.js.map +0 -1
  271. package/dist/extension/react.d.ts +0 -121
  272. package/dist/extension/react.js +0 -75
  273. package/dist/extension/react.js.map +0 -1
  274. package/dist/extension/subs/index.d.ts +0 -45
  275. package/dist/extension/subs/index.js +0 -80
  276. package/dist/extension/subs/index.js.map +0 -1
  277. package/dist/extension/test-utils.d.ts +0 -128
  278. package/dist/extension/test-utils.js +0 -277
  279. package/dist/extension/test-utils.js.map +0 -1
  280. package/dist/fan-out/index.js +0 -5
  281. package/dist/fan-out/index.js.map +0 -1
  282. package/dist/fold-BJPdjc9w.d.ts +0 -435
  283. package/dist/graph-B3L5e0N1.d.ts +0 -923
  284. package/dist/idempotency/adapter.d.ts +0 -62
  285. package/dist/idempotency/adapter.js +0 -4
  286. package/dist/idempotency/adapter.js.map +0 -1
  287. package/dist/idempotent-intake/index.d.ts +0 -188
  288. package/dist/idempotent-intake/index.js +0 -7
  289. package/dist/idempotent-intake/index.js.map +0 -1
  290. package/dist/journal/index.d.ts +0 -373
  291. package/dist/journal/index.js +0 -3
  292. package/dist/journal/index.js.map +0 -1
  293. package/dist/llm-call/index.d.ts +0 -311
  294. package/dist/llm-call/index.js +0 -16
  295. package/dist/llm-call/index.js.map +0 -1
  296. package/dist/machine-BtFWQmmq.d.ts +0 -804
  297. package/dist/monitored-run/index.js +0 -10
  298. package/dist/monitored-run/index.js.map +0 -1
  299. package/dist/paginated-walk/index.d.ts +0 -235
  300. package/dist/paginated-walk/index.js +0 -16
  301. package/dist/paginated-walk/index.js.map +0 -1
  302. package/dist/paginator/index.js +0 -3
  303. package/dist/paginator/index.js.map +0 -1
  304. package/dist/pbt/arbitraries/index.d.ts +0 -136
  305. package/dist/pbt/arbitraries/index.js +0 -3
  306. package/dist/pbt/arbitraries/index.js.map +0 -1
  307. package/dist/pbt/runners/index.d.ts +0 -118
  308. package/dist/pbt/runners/index.js +0 -5
  309. package/dist/pbt/runners/index.js.map +0 -1
  310. package/dist/poller/index.d.ts +0 -352
  311. package/dist/poller/index.js +0 -115
  312. package/dist/poller/index.js.map +0 -1
  313. package/dist/prediction/index.d.ts +0 -131
  314. package/dist/prediction/index.js +0 -4
  315. package/dist/prediction/index.js.map +0 -1
  316. package/dist/pure/index.d.ts +0 -3
  317. package/dist/pure/index.js +0 -4
  318. package/dist/pure/index.js.map +0 -1
  319. package/dist/rate-limit/index.d.ts +0 -115
  320. package/dist/rate-limit/index.js +0 -3
  321. package/dist/rate-limit/index.js.map +0 -1
  322. package/dist/reconciler/index.d.ts +0 -193
  323. package/dist/reconciler/index.js +0 -164
  324. package/dist/reconciler/index.js.map +0 -1
  325. package/dist/recorder/index.js +0 -3
  326. package/dist/recorder/index.js.map +0 -1
  327. package/dist/reducer-4GNR6b8B.d.ts +0 -447
  328. package/dist/resilient-call/index.d.ts +0 -320
  329. package/dist/resilient-call/index.js +0 -14
  330. package/dist/resilient-call/index.js.map +0 -1
  331. package/dist/retry-to-success/index.d.ts +0 -109
  332. package/dist/retry-to-success/index.js +0 -37
  333. package/dist/retry-to-success/index.js.map +0 -1
  334. package/dist/run-pAcmBpah.d.ts +0 -713
  335. package/dist/runtime-types-CNLmhWif.d.ts +0 -517
  336. package/dist/saga/index.d.ts +0 -241
  337. package/dist/saga/index.js +0 -150
  338. package/dist/saga/index.js.map +0 -1
  339. package/dist/samples-B3My5oh2.d.ts +0 -59
  340. package/dist/snapshot/index.js +0 -5
  341. package/dist/snapshot/index.js.map +0 -1
  342. package/dist/subs/index.d.ts +0 -472
  343. package/dist/subs/index.js +0 -7
  344. package/dist/subs/index.js.map +0 -1
  345. package/dist/throttle/index.d.ts +0 -134
  346. package/dist/throttle/index.js +0 -3
  347. package/dist/throttle/index.js.map +0 -1
  348. package/dist/throttled-input/index.js.map +0 -1
  349. package/dist/token-refresh/index.d.ts +0 -216
  350. package/dist/token-refresh/index.js +0 -6
  351. package/dist/token-refresh/index.js.map +0 -1
  352. package/dist/trace-replay/index.js +0 -6
  353. package/dist/trace-replay/index.js.map +0 -1
  354. package/dist/with-deadline/index.d.ts +0 -197
  355. package/dist/with-deadline/index.js +0 -111
  356. package/dist/with-deadline/index.js.map +0 -1
  357. package/dist/with-resilience/index.d.ts +0 -225
  358. package/dist/with-resilience/index.js +0 -240
  359. package/dist/with-resilience/index.js.map +0 -1
  360. package/dist/with-telemetry/index.d.ts +0 -150
  361. package/dist/with-telemetry/index.js +0 -70
  362. package/dist/with-telemetry/index.js.map +0 -1
  363. package/dist/work-queue/adapter.d.ts +0 -84
  364. package/dist/work-queue/adapter.js +0 -4
  365. package/dist/work-queue/adapter.js.map +0 -1
  366. package/dist/work-queue/ops.d.ts +0 -71
  367. package/dist/work-queue/ops.js +0 -3
  368. package/dist/work-queue/ops.js.map +0 -1
  369. package/dist/workflow/index.d.ts +0 -485
  370. package/dist/workflow/index.js +0 -244
  371. package/dist/workflow/index.js.map +0 -1
  372. /package/dist/{cache → paginate}/index.js.map +0 -0
  373. /package/dist/{chart/inspect → persistence}/index.js.map +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/authed-call/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @demlik/tea/authed-call — resilient-call with one extra dimension: a bearer\n * credential that must be minted before a call goes out and re-minted when the\n * server says it is no longer good (a 401). It is composition #10 in the specs:\n * *extends resilient-call; needs the L1 `token-refresh` brick*.\n *\n * The recipe — two existing bricks, stacked, nothing hand-wired:\n *\n * - `../resilient-call` owns ALL the call resilience: cache → circuit-breaker\n * → rate-limit → exponential-backoff retry, plus the per-call deadline. This\n * knob does not re-implement a single line of it. The `attempt` / `succeed`\n * / `fail` / `onTimer` verbs here are thin delegations that thread the\n * composed slice's `resilience` field through the resilient-call verb of the\n * same name. Whatever resilience config a consumer passes flows straight\n * down — every `ResilientConfig` field is still optional, still \"omit a\n * brick → omit its gate\".\n *\n * - `../token-refresh` owns the credential lifecycle: the `{ token, stale }`\n * slice and the decisions about *when* a token must be refreshed (expiry\n * skew, or a reactive 401). This knob adds the one verb resilient-call has\n * no concept of — `on401` — and routes the refresh I/O through token-\n * refresh's `refresh_token` handler.\n *\n * So the slice is exactly *resilient-call's slice + token-refresh's slice*:\n *\n * { resilience: ResilientState<I,R>, auth: TokenState }\n *\n * and the verb set is *resilient-call's four verbs + `on401`*, precisely the\n * spec's \"resilient-call's + `on401(s)` (refresh → retry once)\".\n *\n * ## The 401 dance — \"refresh → retry once\"\n *\n * A 401 is special: it is not a transient failure (backing off and retrying the\n * SAME credential just earns another 401), and it is not a terminal failure (a\n * fresh token usually fixes it). So `on401(s, key, at)` does three things:\n *\n * 1. marks the held token `stale` (via token-refresh's `on401`) and emits a\n * `refresh_token` Cmd to mint a new one;\n * 2. parks the in-flight call in a per-key `authRetry` budget so that, *once*,\n * the refresh landing re-issues that call with the new credential;\n * 3. caps that to a SINGLE auth-retry per call. A second 401 on the same call\n * after we already refreshed and retried does NOT loop — it settles the\n * call `failed` (errors are data: the failure is recorded, never swallowed\n * or spun on forever). The budget resets on the next fresh `attempt`.\n *\n * When the refresh resolves, the consumer folds `refreshed` into the auth slice\n * and calls `onRefreshed(s, at)`, which re-attempts every parked call through\n * the resilient gate with the new token in hand.\n *\n * ## The two non-negotiables (canon — inherited from both bricks)\n *\n * - **Durable** — the slice is `ResilientState` (plain data) + `TokenState`\n * (plain data) + a plain `Record<string, number>` retry budget. All JSON-\n * serializable, so it survives a Durable Object eviction / reload.\n * - **Replayable** — every verb returns `readonly [State, Cmd[]]` and never\n * reads the wall clock or RNG. Time arrives as an `at` parameter; the RNG\n * for retry jitter is injected once at `createAuthedCall(config, rng)` and\n * handed straight to the resilient-call knob. The only clock / network reads\n * live in `handlers` (the effect boundary) — resilient-call's `run` port and\n * token-refresh's `refresh` port.\n *\n * ## Typical wiring\n *\n * const ac = createAuthedCall<string, Resp>({\n * refresh: () => sdk.mintToken(),\n * retry: defaultRetryPolicy,\n * circuit: { threshold: 5, cooldownMs: 30_000 },\n * deadline: { ms: 5_000 },\n * skewMs: 30_000,\n * });\n *\n * // in the machine:\n * init: () => [ac.init(), []],\n * update: {\n * fetch: (s, m) => ac.attempt(s, m.key, m.input, m.at),\n * call_ok: (s, m) => ac.succeed(s, m.key, m),\n * call_err:(s, m) => ac.fail(s, m.key, m),\n * got_401: (s, m) => ac.on401(s, m.key, m.at),\n * token_refreshed: (s, m) => ac.onRefreshed(ac.installToken(s, m.token), m.at),\n * retry_due: (s, m) => ac.onTimer(s, m),\n * },\n * subscriptions: (s) => ac.subs(s),\n * subscribe: { deadline: subscribeDeadline },\n * interpret: ac.handlers({ run: ctx.call, refresh: () => sdk.mintToken() }),\n */\n\nimport type { Cmd } from \"../index\";\nimport type { MsgType } from \"../protocol\";\nimport { without } from \"../pure/core\";\nimport {\n createResilientCall,\n type DeadlineExceeded,\n type DeadlineSub,\n deadlineSub,\n type FailMsg,\n type ResilientConfig,\n type ResilientPorts,\n type ResilientState,\n type ResilientTimerMsg,\n type RunCmd,\n type SucceedMsg,\n subscribeDeadline,\n} from \"../resilient-call\";\nimport {\n createTokenRefresh,\n type RefreshTokenCmd,\n refreshTokenCmd,\n type Token,\n type TokenRefreshConfig,\n type TokenRefreshMsg,\n type TokenRefreshPorts,\n type TokenState,\n} from \"../token-refresh\";\n\n// ===========================================================================\n// Config — the knob. The resilient-call config PLUS the two token-refresh\n// fields. Every field optional except `refresh` (the credential is the whole\n// point: without a mint port there is nothing to authenticate with).\n// ===========================================================================\n\n/**\n * The authed-call knob. It is `ResilientConfig` widened by the token dimension:\n *\n * - `refresh` — the ONLY required field: the port that mints a fresh `Token`.\n * Identical to token-refresh's `TokenRefreshPorts.refresh`; it is hoisted\n * onto the config (not the ports) so the consumer spells the credential\n * source once, the same way `rng` is curried onto the factory.\n * - `skewMs` — token-refresh's refresh-ahead skew (optional, defaults to 0).\n * - every `ResilientConfig` field (`retry` / `circuit` / `rateLimit` /\n * `cache` / `deadline`) — passed straight through to the resilient-call\n * knob, each still optional, each still \"omit a brick → omit its gate\".\n */\nexport interface AuthedConfig extends ResilientConfig, TokenRefreshConfig {\n /** Mint a fresh credential. The single DI seam the auth dimension adds. */\n readonly refresh: () => Promise<Token>;\n}\n\n// ===========================================================================\n// Slice — resilient-call's slice + token-refresh's slice + the auth-retry\n// budget. A flat record, every field plain data.\n// ===========================================================================\n\n/**\n * The composed slice. Three plain-data fields, no closures:\n *\n * - `resilience` — resilient-call's entire slice (circuit, bucket, retry,\n * cache, calls). Owned and mutated only via resilient-call's verbs.\n * - `auth` — token-refresh's `{ token, stale }` slice. Owned and mutated only\n * via token-refresh's verbs.\n * - `authRetry` — per-key count of auth-retries (401 → refresh → retry) spent\n * on a call. Caps the dance at one retry per call so a credential the server\n * keeps rejecting cannot spin forever. A key absent from the record has\n * spent zero. Cleared when a call settles or a fresh `attempt` starts.\n *\n * `pendingAuthRetry` carries the inputs of calls parked waiting for the refresh\n * to land, keyed so `onRefreshed` can re-issue exactly those calls. Plain data —\n * `input` is the same payload the call was issued with.\n */\nexport interface AuthedState<I, R> {\n readonly resilience: ResilientState<I, R>;\n readonly auth: TokenState;\n readonly authRetry: Readonly<Record<string, number>>;\n readonly pendingAuthRetry: Readonly<Record<string, I>>;\n}\n\n// ===========================================================================\n// Cmds + Msgs. The union of both bricks' Cmds/Msgs plus a 401 trigger Msg.\n// ===========================================================================\n\n/** The two effects this knob emits: resilient-call's run, token-refresh's mint. */\nexport type AuthedCmd<I> = RunCmd<I> | RefreshTokenCmd;\n\n/**\n * Every Msg the knob's verbs fold. Resilient-call's settle + timer Msgs, token-\n * refresh's refresh-result Msgs, and the 401 trigger the consumer dispatches\n * when a guarded call comes back unauthorized.\n */\nexport type Unauthorized = {\n readonly type: typeof MsgType.Unauthorized;\n readonly key: string;\n readonly at: number;\n};\n\n/**\n * The plain-data error a 401 we cannot fix settles with. A `{_tag, ...}`\n * sentinel — NEVER a `new Error(...)` — so the slice stays JSON-serializable\n * (an Error object round-trips to `{}` under JSON and carries a stack trace,\n * both of which break the slice's durability invariant once it is persisted\n * into a `Store<S>` and reloaded). Mirrors resilient-call's `DeadlineExceededError`\n * sentinel discipline. `key` echoes the call that was rejected so a consumer can\n * attribute the failure.\n */\nexport type UnauthorizedError = {\n readonly _tag: \"unauthorized\";\n readonly key: string;\n};\n\n/** Ports `handlers` needs — resilient-call's run port AND token-refresh's refresh port. */\nexport interface AuthedPorts<I, R> extends ResilientPorts<I, R> {\n /** Mint a fresh credential. Mirrors `AuthedConfig.refresh` for symmetry at the splice site. */\n readonly refresh: () => Promise<Token>;\n}\n\n// ===========================================================================\n// The knob factory.\n// ===========================================================================\n\n/**\n * Build an authed-call knob from `config`. `rng` is injected for retry jitter\n * and threaded straight into the underlying resilient-call knob — pass a fixed\n * `() => 0.5` in tests to pin backoff; defaults to `Math.random` (read only at\n * the verb boundary inside resilient-call, never in a brick op).\n *\n * Returns the uniform L2 knob contract, widened by the auth verbs (`on401`,\n * `onRefreshed`, `installToken`). `I` is the port input type, `R` the result.\n */\nexport function createAuthedCall<I, R>(\n config: AuthedConfig,\n rng: () => number = Math.random,\n) {\n // Delegate ALL call resilience to the root composition. The resilient config\n // is exactly `config` minus the two auth-only fields — but since\n // `ResilientConfig` only reads the brick fields it knows, passing the wider\n // `config` is harmless. We pass it directly so future resilient bricks flow\n // through without a re-spread.\n const rc = createResilientCall<I, R>(config, rng);\n // Delegate the credential lifecycle to the L1 token brick.\n const tr = createTokenRefresh({ skewMs: config.skewMs });\n\n /** The starting slice — both bricks' inits plus empty auth-retry bookkeeping. */\n function init(): AuthedState<I, R> {\n return {\n resilience: rc.init(),\n auth: tr.init(),\n authRetry: {},\n pendingAuthRetry: {},\n };\n }\n\n // Rebuild the host record after a resilient-call verb, preserving the auth\n // dimension. The single seam where a `[ResilientState, Cmd[]]` becomes a\n // `[AuthedState, Cmd[]]`.\n function liftResilient(\n s: AuthedState<I, R>,\n [resilience, cmds]: readonly [ResilientState<I, R>, readonly RunCmd<I>[]],\n ): readonly [AuthedState<I, R>, readonly AuthedCmd<I>[]] {\n return [{ ...s, resilience }, cmds];\n }\n\n // Forget a key's auth bookkeeping: the 401 budget (`authRetry`) and any\n // parked input (`pendingAuthRetry`) that a settled or restarted call must\n // clear TOGETHER. These two records form a coupled invariant — a call that\n // settles, restarts, or gives up on its 401 dance drops both — so the joint\n // clear lives in one place rather than being open-coded at each site.\n function clearAuthKey(s: AuthedState<I, R>, key: string): AuthedState<I, R> {\n return {\n ...s,\n authRetry: without(s.authRetry, key),\n pendingAuthRetry: without(s.pendingAuthRetry, key),\n };\n }\n\n // === Verb: attempt =======================================================\n\n /**\n * Start (or restart) the call for `key`. Delegates the gate entirely to\n * resilient-call. A fresh attempt resets this key's auth-retry budget (a new\n * logical call gets its own single 401 retry) and drops any stale parked\n * input. PURE — `at` is the only clock.\n *\n * Note this knob does NOT proactively refresh on expiry inside `attempt`: the\n * expiry-driven refresh is the consumer's call-boundary concern (token-refresh\n * exposes `ensureFresh` for exactly that, re-exported below). `attempt` owns\n * the *reactive* path — it issues the call, and a 401 coming back triggers the\n * refresh. Keeping the two seams separate means a consumer who wants only\n * reactive auth (no proactive refresh) wires nothing extra.\n */\n function attempt(\n s: AuthedState<I, R>,\n key: string,\n input: I,\n at: number,\n ): readonly [AuthedState<I, R>, readonly AuthedCmd<I>[]] {\n const cleared = clearAuthKey(s, key);\n return liftResilient(\n cleared,\n rc.attempt(cleared.resilience, key, input, at),\n );\n }\n\n // === Verb: succeed =======================================================\n\n /**\n * Record a success for `key`: delegate to resilient-call (closes the breaker,\n * fills the cache, resets retry) and clear this key's auth-retry bookkeeping —\n * the call settled, so its 401 budget and any parked input are forgotten.\n * PURE.\n */\n function succeed(\n s: AuthedState<I, R>,\n key: string,\n msg: SucceedMsg<R>,\n ): readonly [AuthedState<I, R>, readonly AuthedCmd<I>[]] {\n const settled = clearAuthKey(s, key);\n return liftResilient(settled, rc.succeed(settled.resilience, key, msg));\n }\n\n // === Verb: fail ==========================================================\n\n /**\n * Record a NON-auth failure for `key` (a 5xx, a network drop — anything that\n * is not a 401). Delegate to resilient-call's `fail`, which trips the breaker\n * and backs off / settles per the retry policy. The auth dimension is\n * untouched: a generic failure says nothing about the credential. PURE.\n *\n * A 401 is NOT routed here — the consumer dispatches `unauthorized` and the\n * machine calls `on401` instead. Routing a 401 through `fail` would burn the\n * resilient retry budget on a credential that a plain re-issue cannot fix.\n */\n function fail(\n s: AuthedState<I, R>,\n key: string,\n msg: FailMsg,\n ): readonly [AuthedState<I, R>, readonly AuthedCmd<I>[]] {\n // If the resilient verb settles this call, forget its auth bookkeeping too.\n const [resilience, cmds] = rc.fail(s.resilience, key, msg);\n const next = settleAuthIfDone({ ...s, resilience }, key, resilience);\n return [next, cmds];\n }\n\n // === Verb: on401 =========================================================\n\n /**\n * The auth dimension's one verb: the server rejected `key`'s call as\n * unauthorized. \"Refresh → retry once\":\n *\n * - First 401 on this call → mark the token stale, emit a `refresh_token`\n * Cmd, and PARK the call's input so the refresh landing re-issues it. The\n * resilient call phase is left as-is (still `running`, or `waiting_retry`\n * if a transient failure had already backed it off); the in-flight\n * resilient effect is abandoned in favour of the post-refresh re-issue.\n * - Second 401 (the budget is already spent) → settle the call `failed`\n * with the unauthorized error. No second refresh, no loop. Errors are\n * data: the failure is recorded on the resilient slice, visible to the\n * consumer.\n *\n * A 401 is NOT a downstream-health signal — a credential the server rejected\n * says nothing about the backend's health. So the terminal path routes through\n * resilient-call's `settleFailed` (which settles `failed` WITHOUT tripping the\n * shared breaker or advancing this key's retry counter), never `fail` (which\n * would punish a healthy target and burn the retry budget on a request that a\n * plain re-issue cannot fix).\n *\n * PURE — `at` is carried for symmetry with the other verbs (the refresh Cmd\n * itself carries no time; the refresh port stamps its own result Msg).\n */\n function on401(\n s: AuthedState<I, R>,\n key: string,\n at: number,\n ): readonly [AuthedState<I, R>, readonly AuthedCmd<I>[]] {\n void at; // carried for symmetry; the async refresh's result Msg stamps the clock.\n const spent = s.authRetry[key] ?? 0;\n const call = s.resilience.calls[key];\n // A 401 can arrive while the call is `running` OR after a transient failure\n // backed it off to `waiting_retry`; both hold the input needed to re-issue.\n // Reading only `running` would drop a 401 that races a backoff — the call\n // would never refresh.\n const input =\n call?.phase === \"running\" || call?.phase === \"waiting_retry\"\n ? call.input\n : undefined;\n\n // Budget exhausted (already refreshed + retried once), or nothing to\n // re-issue: settle the call failed with the unauthorized error. Through\n // `settleFailed`, NOT `fail` — a 401 is not a breaker / retry signal.\n if (spent >= 1 || input === undefined) {\n const error: UnauthorizedError = { _tag: \"unauthorized\", key };\n const [settled] = rc.settleFailed(s.resilience, key, error);\n return [{ ...clearAuthKey(s, key), resilience: settled }, []];\n }\n\n // First 401: mark stale, park the call, ask for a fresh token.\n return [\n {\n ...s,\n auth: tr.on401(s.auth),\n authRetry: { ...s.authRetry, [key]: spent + 1 },\n pendingAuthRetry: { ...s.pendingAuthRetry, [key]: input },\n },\n [refreshTokenCmd()],\n ];\n }\n\n // === Verb: installToken ==================================================\n\n /**\n * Fold a freshly minted `token` into the auth slice (delegates to token-\n * refresh's `refreshed`: installs the token, clears `stale`). PURE. Call this\n * from the `token_refreshed` reducer cell, then chain `onRefreshed` to re-fire\n * the parked calls. Split from `onRefreshed` so a consumer can install a token\n * without auto-retrying (e.g. a proactive expiry refresh with nothing parked).\n */\n function installToken(s: AuthedState<I, R>, token: Token): AuthedState<I, R> {\n return { ...s, auth: tr.refreshed(s.auth, token) };\n }\n\n // === Verb: onRefreshed ===================================================\n\n /**\n * Re-issue every call parked by a 401 now that a fresh token has landed. Runs\n * each parked input back through resilient-call's `attempt` gate (so the\n * circuit / rate-limit / cache are all re-checked against the new `at`),\n * clears the parked set, and accumulates the run Cmds. The auth-retry COUNT is\n * preserved (each re-issued call has already spent one auth-retry, so a second\n * 401 on it settles failed). PURE — `at` is the gate's clock.\n *\n * No parked calls → a no-op that returns the slice unchanged by reference, so\n * a proactive (expiry) refresh with nothing in flight costs nothing.\n */\n function onRefreshed(\n s: AuthedState<I, R>,\n at: number,\n ): readonly [AuthedState<I, R>, readonly AuthedCmd<I>[]] {\n const parked = Object.entries(s.pendingAuthRetry);\n if (parked.length === 0) return [s, []];\n\n let resilience = s.resilience;\n const cmds: AuthedCmd<I>[] = [];\n for (const [key, input] of parked) {\n const [next, runCmds] = rc.attempt(resilience, key, input, at);\n resilience = next;\n cmds.push(...runCmds);\n }\n return [{ ...s, resilience, pendingAuthRetry: {} }, cmds];\n }\n\n // === Verb: onTimer =======================================================\n\n /**\n * A retry / deadline timer fired — delegate straight to resilient-call. If the\n * delegated verb settled the call, forget its auth bookkeeping. PURE.\n */\n function onTimer(\n s: AuthedState<I, R>,\n msg: ResilientTimerMsg,\n ): readonly [AuthedState<I, R>, readonly AuthedCmd<I>[]] {\n const [resilience, cmds] = rc.onTimer(s.resilience, msg);\n const next = settleAuthIfDone({ ...s, resilience }, undefined, resilience);\n return [next, cmds];\n }\n\n // --- helpers --------------------------------------------------------------\n\n // After a resilient verb, drop auth bookkeeping for any key the resilient\n // slice now reports as settled (succeeded / failed). `onlyKey` narrows the\n // scan to one key when the verb targeted one (fail); `undefined` scans all\n // (onTimer can settle a deadline on any key).\n function settleAuthIfDone(\n s: AuthedState<I, R>,\n onlyKey: string | undefined,\n resilience: ResilientState<I, R>,\n ): AuthedState<I, R> {\n const keys = onlyKey !== undefined ? [onlyKey] : Object.keys(s.authRetry);\n let next = s;\n for (const key of keys) {\n const phase = resilience.calls[key]?.phase;\n if (phase === \"succeeded\" || phase === \"failed\") {\n next = clearAuthKey(next, key);\n }\n }\n return next;\n }\n\n // === Subs ================================================================\n\n /**\n * Pre-wired subscriptions — exactly resilient-call's (retry + deadline timers\n * keyed per call). The auth dimension arms NO timers: a refresh is a one-shot\n * Cmd, not a recurring sub, and the parked-call re-issue is driven by the\n * `token_refreshed` Msg, not a timer. Wire `subscribe: { deadline:\n * subscribeDeadline }`.\n */\n function subs(s: AuthedState<I, R>): readonly DeadlineSub[] {\n return rc.subs(s.resilience);\n }\n\n // === Handlers ============================================================\n\n /**\n * Pre-wired interpret handlers: resilient-call's `resilient_run` (wraps the\n * `run` port) AND token-refresh's `refresh_token` (wraps the `refresh` port).\n * Both route Ok/Err through `tryInterpret`, stamping `Date.now()` at the\n * effect boundary — the only clock reads in the whole module. Assign to the\n * machine's `interpret`:\n *\n * interpret: ac.handlers({ run: ctx.call, refresh: () => sdk.mintToken() })\n */\n function handlers(ports: AuthedPorts<I, R>) {\n const trPorts: TokenRefreshPorts = { refresh: ports.refresh };\n return {\n ...rc.handlers({ run: ports.run }),\n ...tr.handlers(trPorts),\n };\n }\n\n return {\n init,\n attempt,\n succeed,\n fail,\n on401,\n installToken,\n onRefreshed,\n onTimer,\n /** token-refresh's call-boundary verb, re-exposed for proactive expiry refresh. */\n needsRefresh: (s: AuthedState<I, R>, at: number) =>\n tr.needsRefresh(s.auth, at),\n subs,\n handlers,\n };\n}\n\n/** The shape `createAuthedCall` returns — useful for typing a held knob. */\nexport type AuthedCall<I, R> = ReturnType<typeof createAuthedCall<I, R>>;\n\n/**\n * Lift a knob result `[slice, cmds]` into a host `[State, cmds]` where the slice\n * lives at `state.authed`. A convenience for the common single-slice host;\n * consumers with a differently-named field spread by hand. Pure — a thin record\n * rebuild, no clock / RNG.\n */\nexport function liftAuthed<\n S extends { authed: AuthedState<I, R> },\n I,\n R,\n C extends Cmd,\n>(\n state: S,\n [slice, cmds]: readonly [AuthedState<I, R>, readonly C[]],\n): readonly [S, readonly C[]] {\n return [{ ...state, authed: slice }, cmds];\n}\n\n/**\n * Re-export the deadline Sub primitives (this knob's `subs` emits `DeadlineSub`s,\n * inherited from resilient-call) and the token-refresh Msg constructors a\n * consumer wires into their reducer, so the whole composition imports from one\n * subpath.\n */\nexport { subscribeDeadline, deadlineSub };\nexport type {\n DeadlineSub,\n DeadlineExceeded,\n ResilientState,\n TokenState,\n Token,\n TokenRefreshMsg,\n SucceedMsg,\n FailMsg,\n ResilientTimerMsg,\n RunCmd,\n};\n"]}
@@ -1,101 +0,0 @@
1
- import { R as Runtime, C as CtxArg } from '../runtime-types-CNLmhWif.js';
2
- import { C as Cmd, S as Sub, M as Machine } from '../core-Co0LWkIA.js';
3
- import 'better-result';
4
- import 'zod';
5
-
6
- /**
7
- * @demlik/tea/await-terminal — run a ONE-SHOT / decision-procedure machine to a
8
- * terminal state and hand the caller a `Promise<State>` that resolves the moment
9
- * the machine FIRST enters a terminal state.
10
- *
11
- * This is the ~10 lines both audit suitability probes hand-wired at their
12
- * integration seam: a one-shot decision machine (a credibility check, an auth
13
- * mint, a validation pass — run to a terminal state, then throw-or-proceed at the
14
- * caller boundary) has no built-in "await the terminal", so each consumer re-wires
15
- * a promise off `observe` that resolves when the state is terminal. This helper
16
- * lifts that boilerplate into `@demlik/tea` once.
17
- *
18
- * It rides the EXISTING observation surface (`packages/tea/src/index.ts`) and adds
19
- * NO new runtime capability:
20
- * - `runtime.getState()` (total on a booted `Runtime`) covers the
21
- * already-terminal-at-attach case — resolve synchronously rather than wait for
22
- * a transition that may never come.
23
- * - `runtime.observe((msg, state) => void): () => void` fires on EVERY transition
24
- * and returns an unsubscribe — the "first terminal transition" signal, detached
25
- * the instant it resolves (no leak, no busy-wait / polling loop).
26
- * - `runtime.stop()` is the teardown the fire-and-await `runToTerminal` owns.
27
- *
28
- * `isTerminal` is caller-supplied — tea has no built-in terminal-set concept and
29
- * this helper does NOT invent one; the caller names their own terminal states.
30
- *
31
- * The optional `timeoutMs` is a plain caller-boundary timer (not an in-loop
32
- * progress deadline — see `../deadline` / `../with-deadline` for that shape): it
33
- * clears on resolve and REJECTS with the `instanceof`-checkable
34
- * {@link TerminalTimeoutError} if the deadline elapses first, never silently
35
- * resolving.
36
- */
37
-
38
- /**
39
- * Raised when `awaitTerminal` / `runToTerminal` is wired with a `timeoutMs` and
40
- * the deadline elapses before any terminal state is reached. Follows the tea
41
- * error dialect (`override readonly name`) and carries a `_tag` discriminant, so
42
- * a caller can branch on it either by `instanceof` or on the tag — the timeout is
43
- * a distinct, checkable rejection, never a silent resolve.
44
- */
45
- declare class TerminalTimeoutError extends Error {
46
- readonly timeoutMs: number;
47
- readonly name = "TerminalTimeoutError";
48
- readonly _tag: "TerminalTimeoutError";
49
- constructor(timeoutMs: number);
50
- }
51
- /** Options shared by `awaitTerminal` and `runToTerminal`. */
52
- interface AwaitTerminalOptions {
53
- /**
54
- * Optional bounded deadline in milliseconds. When set, the returned promise
55
- * REJECTS with {@link TerminalTimeoutError} if no terminal state is reached
56
- * within the window; the timer is cleared on the resolve path so a resolved
57
- * await never later rejects or leaves a dangling timer. Omit for an unbounded
58
- * await (never rejects for time).
59
- */
60
- readonly timeoutMs?: number;
61
- }
62
- /**
63
- * Attach to an ALREADY-RUNNING `Runtime<S, M, E>` and resolve with the terminal
64
- * `S` on the first transition for which `isTerminal(state)` is true.
65
- *
66
- * If the runtime is ALREADY terminal at attach time (`isTerminal(getState())`),
67
- * resolves immediately — it never hangs waiting for a transition that will not
68
- * come. Otherwise it subscribes via `observe` and detaches that subscription the
69
- * instant it resolves (or times out), so there is no observer leak and no
70
- * further callbacks fire.
71
- *
72
- * @param runtime a booted `Runtime` (from `await run(machine, opts).ready`).
73
- * @param isTerminal caller-supplied terminal predicate over the machine's state.
74
- * @param opts optional bounded `timeoutMs` deadline (see {@link AwaitTerminalOptions}).
75
- */
76
- declare function awaitTerminal<S, M extends {
77
- type: string;
78
- }, E extends {
79
- type: string;
80
- } = never>(runtime: Runtime<S, M, E>, isTerminal: (state: S) => boolean, opts?: AwaitTerminalOptions): Promise<S>;
81
- /**
82
- * Fire-and-await convenience: `run()` the machine, dispatch the seed `msgs`, and
83
- * resolve with the terminal state — then tear the runtime down (`runtime.stop()`)
84
- * on BOTH the resolve and reject paths. Wraps {@link awaitTerminal}.
85
- *
86
- * The seed carries the machine's `ctx` (conditionally optional, exactly as
87
- * `run`'s `ctx` — a pure machine omits it) and the `msgs` to dispatch once
88
- * booted. A `timeoutMs` in `opts` bounds the await identically to `awaitTerminal`.
89
- *
90
- * @param machine the machine to boot.
91
- * @param seed `{ ctx?, msgs }` — the boot context and the seed messages.
92
- * @param isTerminal caller-supplied terminal predicate over the machine's state.
93
- * @param opts optional bounded `timeoutMs` deadline (see {@link AwaitTerminalOptions}).
94
- */
95
- declare function runToTerminal<S, M extends {
96
- type: string;
97
- }, C extends Cmd, U extends Sub, Ctx>(machine: Machine<S, M, C, U, Ctx>, seed: CtxArg<Ctx> & {
98
- readonly msgs: readonly M[];
99
- }, isTerminal: (state: S) => boolean, opts?: AwaitTerminalOptions): Promise<S>;
100
-
101
- export { type AwaitTerminalOptions, TerminalTimeoutError, awaitTerminal, runToTerminal };
@@ -1,76 +0,0 @@
1
- import { run } from '../chunk-ZSUUOEPL.js';
2
- import '../chunk-OOYD44JI.js';
3
-
4
- // src/await-terminal/index.ts
5
- var TerminalTimeoutError = class extends Error {
6
- constructor(timeoutMs) {
7
- super(
8
- `@demlik/tea: awaitTerminal did not reach a terminal state within ${timeoutMs}ms \u2014 no transition satisfied isTerminal before the deadline.`
9
- );
10
- this.timeoutMs = timeoutMs;
11
- }
12
- timeoutMs;
13
- name = "TerminalTimeoutError";
14
- _tag = "TerminalTimeoutError";
15
- };
16
- function awaitTerminal(runtime, isTerminal, opts = {}) {
17
- return new Promise((resolve, reject) => {
18
- const current = runtime.getState();
19
- if (isTerminal(current)) {
20
- resolve(current);
21
- return;
22
- }
23
- let settled = false;
24
- let timer;
25
- let detach;
26
- const cleanup = () => {
27
- if (timer !== void 0) {
28
- clearTimeout(timer);
29
- timer = void 0;
30
- }
31
- if (detach !== void 0) {
32
- detach();
33
- detach = void 0;
34
- }
35
- };
36
- detach = runtime.observe((_msg, state) => {
37
- if (settled) return;
38
- if (isTerminal(state)) {
39
- settled = true;
40
- cleanup();
41
- resolve(state);
42
- }
43
- });
44
- const timeoutMs = opts.timeoutMs;
45
- if (timeoutMs !== void 0) {
46
- timer = setTimeout(() => {
47
- if (settled) return;
48
- settled = true;
49
- cleanup();
50
- reject(new TerminalTimeoutError(timeoutMs));
51
- }, timeoutMs);
52
- }
53
- });
54
- }
55
- async function runToTerminal(machine, seed, isTerminal, opts = {}) {
56
- const { msgs, ...ctxArg } = seed;
57
- const runtime = await run(
58
- machine,
59
- ctxArg
60
- ).ready;
61
- try {
62
- const terminal = awaitTerminal(runtime, isTerminal, opts);
63
- const seeding = Promise.all(msgs.map((msg) => runtime.dispatch(msg)));
64
- const state = await Promise.race([terminal, seeding.then(() => terminal)]);
65
- await runtime.stop();
66
- return state;
67
- } catch (err) {
68
- await runtime.stop().catch(() => {
69
- });
70
- throw err;
71
- }
72
- }
73
-
74
- export { TerminalTimeoutError, awaitTerminal, runToTerminal };
75
- //# sourceMappingURL=index.js.map
76
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/await-terminal/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @demlik/tea/await-terminal — run a ONE-SHOT / decision-procedure machine to a\n * terminal state and hand the caller a `Promise<State>` that resolves the moment\n * the machine FIRST enters a terminal state.\n *\n * This is the ~10 lines both audit suitability probes hand-wired at their\n * integration seam: a one-shot decision machine (a credibility check, an auth\n * mint, a validation pass — run to a terminal state, then throw-or-proceed at the\n * caller boundary) has no built-in \"await the terminal\", so each consumer re-wires\n * a promise off `observe` that resolves when the state is terminal. This helper\n * lifts that boilerplate into `@demlik/tea` once.\n *\n * It rides the EXISTING observation surface (`packages/tea/src/index.ts`) and adds\n * NO new runtime capability:\n * - `runtime.getState()` (total on a booted `Runtime`) covers the\n * already-terminal-at-attach case — resolve synchronously rather than wait for\n * a transition that may never come.\n * - `runtime.observe((msg, state) => void): () => void` fires on EVERY transition\n * and returns an unsubscribe — the \"first terminal transition\" signal, detached\n * the instant it resolves (no leak, no busy-wait / polling loop).\n * - `runtime.stop()` is the teardown the fire-and-await `runToTerminal` owns.\n *\n * `isTerminal` is caller-supplied — tea has no built-in terminal-set concept and\n * this helper does NOT invent one; the caller names their own terminal states.\n *\n * The optional `timeoutMs` is a plain caller-boundary timer (not an in-loop\n * progress deadline — see `../deadline` / `../with-deadline` for that shape): it\n * clears on resolve and REJECTS with the `instanceof`-checkable\n * {@link TerminalTimeoutError} if the deadline elapses first, never silently\n * resolving.\n */\n\nimport {\n type Cmd,\n type CtxArg,\n type Machine,\n type Runtime,\n run,\n type Sub,\n} from \"../index\";\n\n/**\n * Raised when `awaitTerminal` / `runToTerminal` is wired with a `timeoutMs` and\n * the deadline elapses before any terminal state is reached. Follows the tea\n * error dialect (`override readonly name`) and carries a `_tag` discriminant, so\n * a caller can branch on it either by `instanceof` or on the tag — the timeout is\n * a distinct, checkable rejection, never a silent resolve.\n */\nexport class TerminalTimeoutError extends Error {\n override readonly name = \"TerminalTimeoutError\";\n readonly _tag = \"TerminalTimeoutError\" as const;\n constructor(public readonly timeoutMs: number) {\n super(\n `@demlik/tea: awaitTerminal did not reach a terminal state within ` +\n `${timeoutMs}ms — no transition satisfied isTerminal before the deadline.`,\n );\n }\n}\n\n/** Options shared by `awaitTerminal` and `runToTerminal`. */\nexport interface AwaitTerminalOptions {\n /**\n * Optional bounded deadline in milliseconds. When set, the returned promise\n * REJECTS with {@link TerminalTimeoutError} if no terminal state is reached\n * within the window; the timer is cleared on the resolve path so a resolved\n * await never later rejects or leaves a dangling timer. Omit for an unbounded\n * await (never rejects for time).\n */\n readonly timeoutMs?: number;\n}\n\n/**\n * Attach to an ALREADY-RUNNING `Runtime<S, M, E>` and resolve with the terminal\n * `S` on the first transition for which `isTerminal(state)` is true.\n *\n * If the runtime is ALREADY terminal at attach time (`isTerminal(getState())`),\n * resolves immediately — it never hangs waiting for a transition that will not\n * come. Otherwise it subscribes via `observe` and detaches that subscription the\n * instant it resolves (or times out), so there is no observer leak and no\n * further callbacks fire.\n *\n * @param runtime a booted `Runtime` (from `await run(machine, opts).ready`).\n * @param isTerminal caller-supplied terminal predicate over the machine's state.\n * @param opts optional bounded `timeoutMs` deadline (see {@link AwaitTerminalOptions}).\n */\nexport function awaitTerminal<\n S,\n M extends { type: string },\n E extends { type: string } = never,\n>(\n runtime: Runtime<S, M, E>,\n isTerminal: (state: S) => boolean,\n opts: AwaitTerminalOptions = {},\n): Promise<S> {\n return new Promise<S>((resolve, reject) => {\n // Already-terminal-at-attach: resolve now. Returning here attaches neither an\n // observer nor a timer, so the immediate case cannot leak either.\n const current = runtime.getState();\n if (isTerminal(current)) {\n resolve(current);\n return;\n }\n\n let settled = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let detach: (() => void) | undefined;\n\n const cleanup = (): void => {\n if (timer !== undefined) {\n clearTimeout(timer);\n timer = undefined;\n }\n if (detach !== undefined) {\n detach();\n detach = undefined;\n }\n };\n\n detach = runtime.observe((_msg, state) => {\n if (settled) return;\n if (isTerminal(state)) {\n settled = true;\n cleanup();\n resolve(state);\n }\n });\n\n // Narrow into a const so the closure keeps the `number` narrowing (the field\n // itself is `number | undefined`).\n const timeoutMs = opts.timeoutMs;\n if (timeoutMs !== undefined) {\n timer = setTimeout(() => {\n if (settled) return;\n settled = true;\n cleanup();\n reject(new TerminalTimeoutError(timeoutMs));\n }, timeoutMs);\n }\n });\n}\n\n/**\n * Fire-and-await convenience: `run()` the machine, dispatch the seed `msgs`, and\n * resolve with the terminal state — then tear the runtime down (`runtime.stop()`)\n * on BOTH the resolve and reject paths. Wraps {@link awaitTerminal}.\n *\n * The seed carries the machine's `ctx` (conditionally optional, exactly as\n * `run`'s `ctx` — a pure machine omits it) and the `msgs` to dispatch once\n * booted. A `timeoutMs` in `opts` bounds the await identically to `awaitTerminal`.\n *\n * @param machine the machine to boot.\n * @param seed `{ ctx?, msgs }` — the boot context and the seed messages.\n * @param isTerminal caller-supplied terminal predicate over the machine's state.\n * @param opts optional bounded `timeoutMs` deadline (see {@link AwaitTerminalOptions}).\n */\nexport async function runToTerminal<\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 seed: CtxArg<Ctx> & { readonly msgs: readonly M[] },\n isTerminal: (state: S) => boolean,\n opts: AwaitTerminalOptions = {},\n): Promise<S> {\n // Split the seed messages out from the boot context: `run`'s opts don't carry\n // `msgs`, so what remains is exactly the conditionally-optional `ctx` arg. The\n // cast is because `CtxArg<Ctx>` is a deferred generic conditional TS won't\n // verify against `run`'s all-optional opts (its weak-type check) — the value\n // is structurally valid; `run` defaults an absent `ctx` to `{}`.\n const { msgs, ...ctxArg } = seed;\n const runtime = await run<S, M, C, U, Ctx>(\n machine,\n ctxArg as Parameters<typeof run<S, M, C, U, Ctx>>[1],\n ).ready;\n try {\n // Attach the terminal await BEFORE dispatching so a terminal transition that\n // lands during a seed dispatch is caught (and the immediate-terminal check\n // inside awaitTerminal covers a machine that boots already terminal).\n const terminal = awaitTerminal(runtime, isTerminal, opts);\n // Dispatch runs serially on the runtime's tail; fire all seeds and let a\n // dispatch failure (a reducer/interpret throw) surface by racing it against\n // the terminal await rather than floating as an unhandled rejection.\n const seeding = Promise.all(msgs.map((msg) => runtime.dispatch(msg)));\n const state = await Promise.race([terminal, seeding.then(() => terminal)]);\n await runtime.stop();\n return state;\n } catch (err) {\n // Tear down on the reject path too (timeout, dispatch failure) — a rejected\n // runToTerminal never leaves the runtime running.\n await runtime.stop().catch(() => {});\n throw err;\n }\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/batch-window/index.ts"],"names":[],"mappings":";;;;;;;AA2IO,SAAS,eAAA,GAAqC;AACnD,EAAA,OAAO,EAAE,MAAA,EAAQ,EAAC,EAAG,UAAU,IAAA,EAAK;AACtC;AAqBO,SAAS,kBAAA,CACd,IACA,IAAA,EACoB;AACpB,EAAA,OAAO,gBAAA,CAAiB,IAAI,IAAI,CAAA;AAClC;AAkEO,SAAS,OAAA,CACd,MAAA,EACA,KAAA,EACA,IAAA,EACA,EAAA,EACyC;AACzC,EAAA,MAAM,MAAA,GAAS,CAAC,GAAG,KAAA,CAAM,QAAQ,IAAI,CAAA;AAGrC,EAAA,MAAM,QAAA,GAAW,MAAM,QAAA,IAAY,EAAA;AAInC,EAAA,IAAI,OAAO,MAAA,IAAU,IAAA,CAAK,IAAI,CAAA,EAAG,MAAA,CAAO,QAAQ,CAAA,EAAG;AACjD,IAAA,OAAO,CAAC,iBAAmB,EAAG,CAAC,OAAO,KAAA,CAAM,MAAM,CAAC,CAAC,CAAA;AAAA,EACtD;AAEA,EAAA,OAAO,CAAC,EAAE,MAAA,EAAQ,QAAA,EAAS,EAAG,EAAE,CAAA;AAClC;AAuBO,SAAS,QAAA,CACd,MAAA,EACA,KAAA,EACA,GAAA,EACyC;AAGzC,EAAA,IAAI,KAAA,CAAM,OAAO,MAAA,KAAW,CAAA,SAAU,CAAC,KAAA,EAAO,EAAE,CAAA;AAChD,EAAA,OAAO,CAAC,iBAAmB,EAAG,CAAC,OAAO,KAAA,CAAM,KAAA,CAAM,MAAM,CAAC,CAAC,CAAA;AAC5D;AAqBO,SAAS,OAAA,CACd,MAAA,EACA,KAAA,EACA,EAAA,GAAK,cAAA,EACsB;AAK3B,EAAA,IAAI,KAAA,CAAM,QAAA,KAAa,IAAA,EAAM,OAAO,EAAC;AACrC,EAAA,OAAO,CAAC,WAAA,CAAY,EAAA,EAAI,MAAM,QAAA,GAAW,MAAA,CAAO,KAAK,CAAC,CAAA;AACxD;AAQO,IAAM,oBAAA,GAIT;AAYG,SAAS,kBACd,MAAA,EACuB;AACvB,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,MAAM,eAAA,EAAmB;AAAA,IAC/B,GAAA,EAAK,CAAC,KAAA,EAAO,IAAA,EAAM,OAAO,OAAA,CAAQ,MAAA,EAAQ,KAAA,EAAO,IAAA,EAAM,EAAE,CAAA;AAAA,IACzD,UAAU,CAAC,KAAA,EAAO,OAAO,QAAA,CAAS,MAAA,EAAQ,KAAS,CAAA;AAAA,IACnD,MAAM,CAAC,KAAA,EAAO,OAAO,OAAA,CAAQ,MAAA,EAAQ,OAAO,EAAE,CAAA;AAAA,IAC9C,QAAA,EAAU,OAAO,EAAE,QAAA,EAAU,oBAAA,EAAqB;AAAA,GACpD;AACF","file":"index.js","sourcesContent":["/**\n * @demlik/tea/batch-window — coalesce a stream of items into size- or\n * time-bounded BATCHES, then flush each batch as a single Cmd.\n *\n * What this module actually DELEGATES to, vs. what it merely RE-EXPORTS:\n *\n * - `../deadline` — REAL delegation (the only one). The window timer IS a\n * `deadline` Sub: `subsFor` returns `deadlineSub` keyed on the window's\n * identity, the subscribe cell is `subscribeDeadline` verbatim, and the\n * expiry Msg is `deadlineExceeded`. Nothing about the timer is redrawn here.\n * \"Fire at an absolute instant, even across a rehydrate\" is exactly\n * `deadline`'s absolute-target lifecycle: a late subscribe (post-`Store`\n * rehydrate) recomputes the remaining delay from the current clock and still\n * fires at the correct moment.\n * - `../work-queue` — TYPE re-export only, NOT a sink this module calls. A\n * flush emits a Cmd; the CONSUMER's interpret handler is what enqueues onto a\n * `createQueue(store)`. This module never touches queue status — it has no\n * queue state and no status flip to delegate. It re-exports `EnqueueInput<I>`\n * purely so the consumer's flush Cmd and queue agree on the item shape; any\n * actual status flip belongs in the consumer and is delegated to\n * `../work-queue/ops` there, not here.\n * - `../debounce` — VALUE/TYPE re-export only, NOT wired into `update`. A\n * bursty source (keystrokes, scroll, websocket frames) can be `debounce`d at\n * the host BEFORE it ever becomes an `add` Msg, so the batch window only sees\n * settled items. `debounce` is a sink transformer OUTSIDE `update`;\n * batch-window is the durable, replayable half inside it. Re-exported for\n * one-import ergonomics — this module does not call it.\n *\n * Why this is a knob and not hand-wired logic: the managed state (the buffer +\n * the window-open instant) lives in the Model, so it survives DO eviction /\n * reload (durable) and every transition is still a Msg through `update`\n * (replayable). A closure holding a `setTimeout` and a mutable array would break\n * both — banned. `add` / `onWindow` are PURE: time arrives as an `at`\n * parameter, never `Date.now()`; they return NEW slice values, never mutate.\n *\n * Typical wiring (combinator form):\n *\n * const bw = createBatchWindow<LogLine, Msg>({\n * maxItems: 100,\n * maxMs: 5_000,\n * flush: (items) => ({ type: \"FlushBatch\", items }),\n * });\n *\n * // Model: { batch: BatchWindow<LogLine>, ... }\n * // init: (loaded) => [loaded ?? { batch: bw.init(), ... }, []]\n * // update cells:\n * // LogArrived: (s, m) => mapBatch(s, bw.add(s.batch, m.line, m.at))\n * // WindowFired: (s, m) => mapBatch(s, bw.onWindow(s.batch, m.atMs))\n * // subscriptions: (s) => bw.subs(s.batch)\n * // subscribe: { ...bw.handlers() }\n *\n * where `mapBatch(s, [batch, cmds]) => [{ ...s, batch }, cmds]` lifts the slice\n * transition into the consumer's Model. The `FlushBatch` Cmd's interpret handler\n * is the consumer's — it is where the batch meets I/O (an `enqueue`, a network\n * POST, an R2 write).\n *\n * NOT exported from the package root — reached via the `@demlik/tea/batch-window`\n * subpath, same one-shape-per-package rule as `deadline`, `cache`, and\n * `work-queue`.\n */\n\nimport type { Cmd } from \"../index\";\n\n// `../debounce` re-export (value + type) for one-import ergonomics — see file\n// header. This module does NOT call debounce; a consumer that wants to debounce\n// a bursty source BEFORE it becomes an `add` Msg reaches it from the same import\n// as the batch window.\nexport { type Debounced, debounce } from \"../debounce\";\n\nimport {\n type DeadlineExceeded,\n type DeadlineSub,\n deadlineExceeded,\n deadlineSub,\n subscribeDeadline,\n} from \"../deadline\";\nimport type { SubscribeHandler } from \"../subs/types\";\n\n// `../work-queue` TYPE re-export only (see file header). This module emits a\n// flush Cmd; the CONSUMER's interpret handler enqueues onto a\n// `createQueue(store)`. Re-export the caller-facing enqueue payload type so the\n// consumer's flush Cmd and queue agree on the item shape.\nexport type { EnqueueInput } from \"../work-queue\";\n\n/**\n * Configuration for a batch window — the knob. Pure config, no mutable state\n * (the `BatchWindow<I>` slice carries that). One config object is shared across\n * every window of one logical stream (often a module-scope const).\n *\n * `flush` is a pure Cmd CONSTRUCTOR, not a closure-with-state: it maps the\n * buffered items to the single Cmd the runtime performs when a window closes.\n * Keeping it a `(items) => Cmd` (not a `(items) => void`) is what keeps the\n * trigger replayable — the flush is a Cmd in the transition's output, never a\n * side effect inside the verb.\n */\nexport interface BatchWindowConfig<I, C extends Cmd> {\n /**\n * Size trigger. A batch flushes the instant its buffer reaches `maxItems`,\n * WITHOUT waiting for the time window — the classic \"send as soon as the\n * page is full\" cutoff. Must be `>= 1`; a config of `maxItems: 0` would flush\n * an empty batch on every `add`, which is never useful, so callers treat `1`\n * as the floor (the verb still behaves: with `maxItems <= 1` every item\n * flushes on arrival, a degenerate but legal \"no batching\" window).\n */\n readonly maxItems: number;\n /**\n * Time trigger, in milliseconds. A batch flushes no later than `openedAt +\n * maxMs` — `openedAt` being the `at` of the item that opened the window. This\n * is the latency ceiling: an item never waits longer than `maxMs` to ship,\n * even if the buffer never reaches `maxItems`.\n */\n readonly maxMs: number;\n /**\n * Map a flushed batch to the single Cmd the runtime performs. Called with the\n * buffered items in arrival order; the returned Cmd is what `add` / `onWindow`\n * emit when a window closes. PURE constructor — no I/O, no clock; the I/O\n * happens later in the consumer's interpret handler for the returned Cmd.\n */\n readonly flush: (items: readonly I[]) => C;\n}\n\n/**\n * The Model slice a batch window owns. `buffer` is the items accumulated for the\n * current open window; `openedAt` is the `at` of the item that OPENED it (the\n * anchor the time trigger measures against), or `null` when the window is\n * closed (buffer empty). The pair is the single source of truth for \"is a\n * window open, and when must it flush by?\" — both plain data so the whole slice\n * is trivially JSON-serializable into a consumer's `Store<S>`.\n *\n * Invariant the verbs preserve: `buffer.length === 0` IFF `openedAt === null`.\n * A non-empty buffer always has an `openedAt`; an empty buffer never does. The\n * window timer Sub keys its arm/disarm off exactly this invariant.\n */\nexport interface BatchWindow<I> {\n readonly buffer: readonly I[];\n readonly openedAt: number | null;\n}\n\n/** The starting slice: closed window, empty buffer. */\nexport function initBatchWindow<I>(): BatchWindow<I> {\n return { buffer: [], openedAt: null };\n}\n\n/**\n * The Sub a batch window's open timer produces: it IS `../deadline`'s\n * `DeadlineSub`, not a re-tagged copy. `atMs` is the absolute flush-by instant\n * (`openedAt + maxMs`). Aliasing the deadline type (rather than redeclaring an\n * identical shape) is what lets `subsFor` return `deadlineSub(...)` with no\n * cast — the value's static type already satisfies this name. Consumers running\n * several windows route each one by the Sub's `id`, not by a distinct type.\n */\nexport type BatchWindowSub = DeadlineSub;\n\n/** The Msg a closed-by-time window dispatches. Alias of `deadline`'s Msg. */\nexport type BatchWindowExpired = DeadlineExceeded;\n\n/**\n * Construct the window-expired Msg for the window identified by `id`, flushing\n * by `atMs`. Re-exported alias of `deadlineExceeded` so the subscribe cell and\n * the consumer's reducer share one constructor rather than two literals that\n * can drift. The consumer handles it in a reducer cell by calling `onWindow`.\n */\nexport function batchWindowExpired(\n id: string,\n atMs: number,\n): BatchWindowExpired {\n return deadlineExceeded(id, atMs);\n}\n\n/**\n * The bound knob returned by `createBatchWindow`. `init` + the two pure verbs +\n * `subs` + `handlers`, all closing over one `BatchWindowConfig`. The verbs\n * return `readonly [BatchWindow<I>, readonly C[]]`: the next slice and the Cmds\n * to emit (one `flush` Cmd when a window closes, none otherwise).\n */\nexport interface BatchWindowKnob<I, C extends Cmd> {\n /** The starting slice — `initBatchWindow()`. */\n init(): BatchWindow<I>;\n /**\n * Buffer `item` (arrival-stamped `at`), flushing immediately if that brings\n * the buffer to `maxItems`. See `addItem`.\n */\n add(\n state: BatchWindow<I>,\n item: I,\n at: number,\n ): readonly [BatchWindow<I>, readonly C[]];\n /**\n * Flush whatever is buffered because the time window closed. See `onWindow`.\n */\n onWindow(\n state: BatchWindow<I>,\n at: number,\n ): readonly [BatchWindow<I>, readonly C[]];\n /**\n * The window timer Sub, armed only while a window is open. See `subsFor`.\n * `id` keys the deadline so several windows on one machine route distinctly.\n */\n subs(state: BatchWindow<I>, id?: string): readonly BatchWindowSub[];\n /**\n * The `subscribe` cells this knob splices into `machine.subscribe`. One cell,\n * keyed `\"deadline\"`, delegating to `../deadline`'s subscribe handler. Spread\n * into the consumer's subscribe map: `subscribe: { ...bw.handlers() }`.\n */\n handlers(): {\n deadline: SubscribeHandler<BatchWindowSub, BatchWindowExpired, unknown>;\n };\n}\n\n/**\n * Buffer `item` and decide whether the size trigger fires. PURE.\n *\n * - Empty (closed) window → `item` OPENS it: `openedAt` becomes `at`, buffer\n * becomes `[item]`. This is the anchor the time window measures against.\n * - Open window → `item` appends; `openedAt` is left untouched (the window's\n * latency clock keeps running from the FIRST item, not this one).\n *\n * After buffering, if the buffer has reached `maxItems` the window flushes\n * immediately: emit `config.flush(buffer)` and RESET to the closed slice. The\n * size trigger pre-empts the time trigger — a full page ships now, the window\n * Sub for the (now closed) window disarms on the next reconcile because `subs`\n * returns `[]` for an empty buffer.\n *\n * `maxItems <= 1` is the degenerate \"no batching\" case: every item flushes on\n * arrival (buffer reaches 1 immediately). Legal and useful as a feature flag to\n * disable batching without restructuring the wiring.\n *\n * @param config the knob (maxItems / flush constructor)\n * @param state the current slice\n * @param item the item to buffer\n * @param at the arrival instant (epoch ms) — the caller stamps it; the verb\n * never reads the clock\n */\nexport function addItem<I, C extends Cmd>(\n config: BatchWindowConfig<I, C>,\n state: BatchWindow<I>,\n item: I,\n at: number,\n): readonly [BatchWindow<I>, readonly C[]] {\n const buffer = [...state.buffer, item];\n // A closed window opens at THIS item's `at`; an already-open window keeps its\n // original `openedAt` (the latency clock runs from the first item).\n const openedAt = state.openedAt ?? at;\n\n // Size trigger: a full page ships now, regardless of the time window. The\n // floor of 1 means `maxItems <= 1` flushes every item on arrival.\n if (buffer.length >= Math.max(1, config.maxItems)) {\n return [initBatchWindow<I>(), [config.flush(buffer)]];\n }\n\n return [{ buffer, openedAt }, []];\n}\n\n/**\n * Flush the open window because its time bound was reached. PURE.\n *\n * - Open window (buffer non-empty) → emit `config.flush(buffer)` and RESET to\n * the closed slice.\n * - Closed window (buffer empty) → no-op: return the same slice and no Cmds.\n * This guards the race where the size trigger already flushed and reset the\n * window before the time Sub's Msg lands — a stale `onWindow` after a\n * size-flush must NOT emit an empty batch.\n *\n * `at` is accepted for symmetry with `add` and so the consumer's reducer cell\n * has a uniform `(state, at)` shape; the verb doesn't need it for the flush\n * decision (the buffer's emptiness is the whole signal), but taking it keeps a\n * future \"flush only if the window is genuinely past `openedAt + maxMs`\"\n * tightening a non-breaking change.\n *\n * @param config the knob (flush constructor)\n * @param state the current slice\n * @param at the firing instant (epoch ms) — the caller stamps it; the verb\n * never reads the clock\n */\nexport function onWindow<I, C extends Cmd>(\n config: BatchWindowConfig<I, C>,\n state: BatchWindow<I>,\n _at: number,\n): readonly [BatchWindow<I>, readonly C[]] {\n // Empty buffer = the size trigger already flushed (or nothing arrived). A\n // stale timer Msg must not ship an empty batch — return the slice untouched.\n if (state.buffer.length === 0) return [state, []];\n return [initBatchWindow<I>(), [config.flush(state.buffer)]];\n}\n\n/**\n * The window timer Sub, derived from the slice. PURE.\n *\n * Returns the single `deadline` Sub targeting `openedAt + maxMs` while a window\n * is open (buffer non-empty), or `[]` while it is closed. The substrate's\n * reconcile pass arms the timer when the array gains the Sub and disarms it\n * (clearing the pending `setTimeout`) when the array loses it — so a size-flush\n * that empties the buffer auto-cancels the now-irrelevant time trigger on the\n * next transition.\n *\n * `id` defaults to `\"batch-window\"`; pass a distinct id per window when a\n * machine runs several so the dispatched `BatchWindowExpired` Msg (and its\n * reconcile identity) routes to the right one.\n *\n * @param config the knob (maxMs)\n * @param state the current slice\n * @param id stable identity for this window's timer (default\n * `\"batch-window\"`)\n */\nexport function subsFor<I, C extends Cmd>(\n config: BatchWindowConfig<I, C>,\n state: BatchWindow<I>,\n id = \"batch-window\",\n): readonly BatchWindowSub[] {\n // Closed window → no timer. The invariant (empty buffer IFF openedAt null)\n // means either check suffices; we read `openedAt` so the `atMs` arithmetic\n // below is non-null without a `!`. `deadlineSub` already returns a\n // `DeadlineSub`, which IS `BatchWindowSub` — no cast.\n if (state.openedAt === null) return [];\n return [deadlineSub(id, state.openedAt + config.maxMs)];\n}\n\n/**\n * The `subscribe[\"deadline\"]` cell for a batch window's timer — the exact\n * `../deadline` handler. Arms a one-shot timer for `max(0, atMs - now)` and\n * dispatches `batchWindowExpired(...)`; returns a cleanup that clears it. The\n * consumer assigns it via the knob's `handlers()`.\n */\nexport const subscribeBatchWindow: SubscribeHandler<\n BatchWindowSub,\n BatchWindowExpired,\n unknown\n> = subscribeDeadline;\n\n/**\n * Build a batch window knob from a config. Combinator form (the default L2\n * ergonomic): hand it the config, splice the ~4 returned hooks into your\n * machine. The pure verbs (`add` / `onWindow`) close over the config so the\n * consumer's reducer cells stay a uniform `(state, ...args)` shape; `subs` /\n * `handlers` pre-wire the window timer.\n *\n * @typeParam I the item type buffered into each batch\n * @typeParam C the Cmd type `config.flush` returns\n */\nexport function createBatchWindow<I, C extends Cmd>(\n config: BatchWindowConfig<I, C>,\n): BatchWindowKnob<I, C> {\n return {\n init: () => initBatchWindow<I>(),\n add: (state, item, at) => addItem(config, state, item, at),\n onWindow: (state, at) => onWindow(config, state, at),\n subs: (state, id) => subsFor(config, state, id),\n handlers: () => ({ deadline: subscribeBatchWindow }),\n };\n}\n"]}
@@ -1,152 +0,0 @@
1
- import { S as Sub } from '../core-Co0LWkIA.js';
2
- import { S as SubscribeHandler } from '../types-XS_miRnB.js';
3
- import 'zod';
4
-
5
- /**
6
- * @demlik/tea/cache — a TTL cache as pure state, PLUS a periodic eviction Sub.
7
- *
8
- * Same pure-state-ops shape as `@demlik/tea/idempotency` and
9
- * `@demlik/tea/work-queue`: a state type plus pure transition functions. Host-
10
- * agnostic by construction — no `Date.now()`, no timers, no I/O inside the ops.
11
- * The caller injects the clock (`nowMs`) and folds the result through its own
12
- * reducer, keeping the store inside TEA's invariant 2 (transitions are pure:
13
- * same inputs → same outputs) so a consumer can exercise it with `replay`
14
- * without a fake clock.
15
- *
16
- * Every op returns a NEW cache value and never mutates its input (invariant 1 —
17
- * state is a value, not a place). `get` / `has` are read-shaped; `set` /
18
- * `remove` / `evictExpired` return a new cache.
19
- *
20
- * **Distinct from `/idempotency`.** Both are TTL-bounded key maps, but they
21
- * answer different questions and so carry different shapes:
22
- *
23
- * - `/idempotency` is DEDUPE-focused — "have I already processed this key?"
24
- * TTL is a single store-level policy (`ttlMs`), each entry stores the
25
- * `atMs` it was remembered at, and expiry is derived (`now - atMs >=
26
- * ttlMs`). It carries an `order` ledger + `capacity` because the use case
27
- * (webhook receivers, retried messages) needs bounded LRU eviction.
28
- * - `/cache` is RETRIEVAL-focused — "what is the value for this key right
29
- * now?" TTL is set PER ENTRY at write time: `set(key, value, nowMs,
30
- * ttlMs)` bakes the absolute `expiresAtMs = nowMs + ttlMs` into the entry.
31
- * Different keys can carry different lifetimes (a hot key cached for 1s, a
32
- * cold one for 1h) — the dimension idempotency's single store-level
33
- * `ttlMs` cannot express. No `order`, no `capacity`: a cache bounds itself
34
- * by TTL + periodic eviction, not by LRU count.
35
- *
36
- * The eviction Sub is the "eviction is a Msg" half: a `setInterval`-shaped Sub
37
- * (composed directly on `@demlik/tea/subs`' `fromInterval`) that periodically
38
- * dispatches a `cache_evict` Msg the consumer's reducer handles by calling
39
- * `evictExpired`. Time still never enters the reducer — the Sub fires the Msg,
40
- * the reducer reads `nowMs` off the Msg (the consumer stamps it at the Sub
41
- * boundary), and `evictExpired` does the pure drop.
42
- *
43
- * NOT exported from the package root — reached via the `@demlik/tea/cache`
44
- * subpath, same one-shape-per-package rule as `work-queue`, `idempotency`, and
45
- * `rate-limit`.
46
- */
47
-
48
- /**
49
- * A cached entry: the `value` and the absolute clock reading `expiresAtMs` it
50
- * expires at. Unlike `/idempotency`'s entry (which stores `atMs` and derives
51
- * expiry from a store-level `ttlMs`), the expiry instant is baked in at write
52
- * time — so per-entry TTLs are a first-class capability. Both fields are plain
53
- * data so the whole cache is trivially JSON-serializable into a consumer's
54
- * `Store<S>`.
55
- */
56
- interface CacheEntry<V> {
57
- readonly value: V;
58
- readonly expiresAtMs: number;
59
- }
60
- /**
61
- * The cache: a map of `key → entry`. No `order` ledger and no `capacity` (cf.
62
- * `/idempotency`) — a TTL cache bounds itself by expiry + periodic eviction,
63
- * not by LRU count. Each entry carries its own `expiresAtMs`, so the cache
64
- * needs no store-level TTL policy field.
65
- */
66
- interface TtlCache<V> {
67
- readonly entries: Readonly<Record<string, CacheEntry<V>>>;
68
- }
69
- /** Create an empty cache. */
70
- declare function initCache<V>(): TtlCache<V>;
71
- /**
72
- * The cached `value` for `key` iff present AND unexpired at `nowMs`, else
73
- * `undefined`. An expired entry reads as absent even though it is physically
74
- * still in `entries` until the next `evictExpired` (or an overwriting `set`)
75
- * drops it. PURE — reads only, never mutates.
76
- */
77
- declare function get<V>(cache: TtlCache<V>, key: string, nowMs: number): V | undefined;
78
- /**
79
- * True iff `key` is present AND unexpired at `nowMs`. The boolean sibling of
80
- * `get` for callers that only need presence, not the value. PURE.
81
- */
82
- declare function has<V>(cache: TtlCache<V>, key: string, nowMs: number): boolean;
83
- /**
84
- * Write `key → value` with an absolute expiry of `nowMs + ttlMs`. Re-`set`ting
85
- * an existing key overwrites both the value and the expiry — the TTL clock
86
- * restarts from the new `nowMs`. PURE — returns a new cache; the input is
87
- * untouched (a fresh shallow copy of `entries`).
88
- */
89
- declare function set<V>(cache: TtlCache<V>, key: string, value: V, nowMs: number, ttlMs: number): TtlCache<V>;
90
- /**
91
- * Drop `key` regardless of expiry. PURE — returns a new cache; the input is
92
- * untouched. Removing an absent key returns an equivalent cache (the shallow
93
- * copy still happens, but no entry is dropped).
94
- */
95
- declare function remove<V>(cache: TtlCache<V>, key: string): TtlCache<V>;
96
- /**
97
- * Physically drop every entry expired at `nowMs` (`nowMs >= expiresAtMs`).
98
- * Returns a NEW cache with the survivors. This is the pure work the eviction
99
- * Sub's `cache_evict` Msg triggers — `get` / `has` already treat expired
100
- * entries as absent, so eviction is purely a memory-reclaim pass, not a
101
- * correctness one. PURE — the input cache is never mutated.
102
- */
103
- declare function evictExpired<V>(cache: TtlCache<V>, nowMs: number): TtlCache<V>;
104
- /**
105
- * The Msg the eviction Sub dispatches each tick. `id` is the Sub's own id so a
106
- * consumer with multiple caches can route the tick to the matching cache. The
107
- * reducer reads `Date.now()`-free time by stamping `nowMs` itself at the Sub
108
- * boundary if needed; the canonical reducer cell is
109
- * `cache_evict: (s) => [{ ...s, cache: evictExpired(s.cache, nowMs) }, []]`.
110
- */
111
- interface CacheEvictMsg {
112
- readonly type: "cache_evict";
113
- readonly id: string;
114
- }
115
- /** Construct a `cache_evict` Msg for the cache identified by `id`. */
116
- declare function cacheEvictMsg(id: string): CacheEvictMsg;
117
- /**
118
- * The Sub shape the eviction factory installs: a `setInterval`-shaped Sub
119
- * carrying its tick period (`intervalMs`) per the substrate's
120
- * reconcile-by-id-with-data-on-the-Sub contract. `type: "cache"` is the
121
- * lowercase source-noun Sub convention (canon §2.5 / naming-style).
122
- */
123
- type CacheEvictionSub = Sub<"cache"> & {
124
- readonly intervalMs: number;
125
- };
126
- /**
127
- * Declare an eviction Sub for the cache identified by `id`, ticking every
128
- * `everyMs`. Drop the result into `subscriptions(state)`:
129
- *
130
- * subscriptions: (s) => [cacheEvictionSub("session-cache", 30_000)]
131
- *
132
- * Same id ⇒ same running interval across transitions (no reconcile churn);
133
- * changing `everyMs` alone will NOT re-arm the timer (the substrate leaves a
134
- * same-id Sub running) — change the `id` too if the period must change. This
135
- * mirrors `fromInterval`'s data-on-the-Sub tradeoff exactly.
136
- */
137
- declare function cacheEvictionSub(id: string, everyMs: number): CacheEvictionSub;
138
- /**
139
- * The `subscribe` cell for the eviction Sub. Assign it to
140
- * `machine.subscribe.cache`:
141
- *
142
- * subscribe: { cache: cacheEvictionSubscribe }
143
- *
144
- * Composed directly on `fromInterval` — it owns the `setInterval` /
145
- * `clearInterval` lifecycle; this cell only pins the Msg the tick dispatches
146
- * (`cacheEvictMsg(sub.id)`). The cleanup `fromInterval` returns stops the
147
- * interval when the Sub is reconciled out (the cache's state exits the phase
148
- * that declared it).
149
- */
150
- declare const cacheEvictionSubscribe: SubscribeHandler<CacheEvictionSub, CacheEvictMsg, unknown>;
151
-
152
- export { type CacheEntry, type CacheEvictMsg, type CacheEvictionSub, type TtlCache, cacheEvictMsg, cacheEvictionSub, cacheEvictionSubscribe, evictExpired, get, has, initCache, remove, set };
@@ -1,8 +0,0 @@
1
- export { cacheEvictMsg, cacheEvictionSub, cacheEvictionSubscribe, evictExpired, get, has, initCache, remove, set } from '../chunk-RH5EFRVJ.js';
2
- import '../chunk-VYFAZN3F.js';
3
- import '../chunk-2YFLOCWX.js';
4
- import '../chunk-ZJ5AOYBH.js';
5
- import '../chunk-ZSUUOEPL.js';
6
- import '../chunk-OOYD44JI.js';
7
- //# sourceMappingURL=index.js.map
8
- //# sourceMappingURL=index.js.map