@demlik/tea 0.12.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 (368) 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-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-7SVIRMC6.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-7V4EVLZA.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-WLGO64WC.js → chunk-HYDWG35Q.js} +23 -6
  19. package/dist/chunk-HYDWG35Q.js.map +1 -0
  20. package/dist/{chunk-I5OV65MS.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-CTzMJ82M.d.ts +562 -0
  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 +48 -31
  43. package/dist/do/index.js +36 -25
  44. package/dist/do/index.js.map +1 -1
  45. package/dist/{durable-effects-D8SqU_if.d.ts → durable-effects-B0-qGQDy.d.ts} +2 -2
  46. package/dist/extension/index.d.ts +301 -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 -11
  55. package/dist/{fan-out/index.d.ts → index-6A7dB8f6.d.ts} +7 -8
  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 -77
  61. package/dist/{monitored-run/index.d.ts → index-NjrT3fL2.d.ts} +37 -12
  62. package/dist/{recorder/index.d.ts → index-TvRb1KVf.d.ts} +4 -6
  63. package/dist/{deadline/index.d.ts → index-q0hXFeZ0.d.ts} +155 -8
  64. package/dist/index.d.ts +719 -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 +2 -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 +19 -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 +130 -4
  77. package/dist/node/index.js +222 -10
  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 +5 -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 +248 -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 -4
  88. package/dist/persistence/index.js +6 -0
  89. package/dist/react/index.d.ts +4 -3
  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 +3 -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 +144 -12
  102. package/dist/{throttled-input → timing}/index.js +57 -12
  103. package/dist/timing/index.js.map +1 -0
  104. package/dist/{types-D7u9DkH7.d.ts → types-DP3f5NIl.d.ts} +1 -1
  105. package/dist/work-queue/index.d.ts +131 -54
  106. package/dist/work-queue/index.js +3 -2
  107. package/dist/work-queue/index.js.map +1 -1
  108. package/package.json +50 -216
  109. package/dist/authed-call/index.d.ts +0 -119
  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 -100
  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 -151
  117. package/dist/cache/index.js +0 -8
  118. package/dist/chart/index.d.ts +0 -217
  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 -85
  122. package/dist/chart/inspect/index.js +0 -5
  123. package/dist/chart/inspect/react.d.ts +0 -112
  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 -361
  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 -83
  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 -116
  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-3JU6657S.js +0 -372
  193. package/dist/chunk-3JU6657S.js.map +0 -1
  194. package/dist/chunk-4KTWLPL5.js +0 -33
  195. package/dist/chunk-4KTWLPL5.js.map +0 -1
  196. package/dist/chunk-5EHLEUTL.js +0 -686
  197. package/dist/chunk-5EHLEUTL.js.map +0 -1
  198. package/dist/chunk-5KJKJOFC.js +0 -758
  199. package/dist/chunk-5KJKJOFC.js.map +0 -1
  200. package/dist/chunk-7SVIRMC6.js.map +0 -1
  201. package/dist/chunk-7V4EVLZA.js.map +0 -1
  202. package/dist/chunk-A56OR5RF.js +0 -184
  203. package/dist/chunk-A56OR5RF.js.map +0 -1
  204. package/dist/chunk-AZZLE7XR.js +0 -62
  205. package/dist/chunk-AZZLE7XR.js.map +0 -1
  206. package/dist/chunk-B5VXOFMZ.js +0 -266
  207. package/dist/chunk-B5VXOFMZ.js.map +0 -1
  208. package/dist/chunk-BYQUGDD3.js +0 -16
  209. package/dist/chunk-BYQUGDD3.js.map +0 -1
  210. package/dist/chunk-D736QUGF.js.map +0 -1
  211. package/dist/chunk-E6GEDSF4.js +0 -307
  212. package/dist/chunk-E6GEDSF4.js.map +0 -1
  213. package/dist/chunk-EUF7XOEE.js +0 -223
  214. package/dist/chunk-EUF7XOEE.js.map +0 -1
  215. package/dist/chunk-EWJP3YYP.js.map +0 -1
  216. package/dist/chunk-FV3Z5MBE.js.map +0 -1
  217. package/dist/chunk-HCEVHFJ7.js +0 -54
  218. package/dist/chunk-HCEVHFJ7.js.map +0 -1
  219. package/dist/chunk-I5OV65MS.js.map +0 -1
  220. package/dist/chunk-J6527VRQ.js.map +0 -1
  221. package/dist/chunk-JELSZN25.js +0 -47
  222. package/dist/chunk-JELSZN25.js.map +0 -1
  223. package/dist/chunk-JER5O7PQ.js +0 -416
  224. package/dist/chunk-JER5O7PQ.js.map +0 -1
  225. package/dist/chunk-JGVL3WPL.js +0 -26
  226. package/dist/chunk-JGVL3WPL.js.map +0 -1
  227. package/dist/chunk-JP5D6GSL.js +0 -71
  228. package/dist/chunk-JP5D6GSL.js.map +0 -1
  229. package/dist/chunk-K575PT4S.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-ND3DH6KM.js +0 -88
  234. package/dist/chunk-ND3DH6KM.js.map +0 -1
  235. package/dist/chunk-Q2UM7BXU.js.map +0 -1
  236. package/dist/chunk-S2EGRPCM.js +0 -47
  237. package/dist/chunk-S2EGRPCM.js.map +0 -1
  238. package/dist/chunk-SINXE47Y.js +0 -55
  239. package/dist/chunk-SINXE47Y.js.map +0 -1
  240. package/dist/chunk-SUST46KO.js +0 -53
  241. package/dist/chunk-SUST46KO.js.map +0 -1
  242. package/dist/chunk-TP5UPGLY.js +0 -61
  243. package/dist/chunk-TP5UPGLY.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-WLGO64WC.js.map +0 -1
  249. package/dist/chunk-WRAA7GVZ.js +0 -209
  250. package/dist/chunk-WRAA7GVZ.js.map +0 -1
  251. package/dist/chunk-ZCL7SCBK.js +0 -685
  252. package/dist/chunk-ZCL7SCBK.js.map +0 -1
  253. package/dist/chunk-ZD6PNZBK.js +0 -457
  254. package/dist/chunk-ZD6PNZBK.js.map +0 -1
  255. package/dist/chunk-ZF6WDK2D.js +0 -519
  256. package/dist/chunk-ZF6WDK2D.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-ZVJZ72HY.js.map +0 -1
  260. package/dist/circuit-breaker/index.d.ts +0 -197
  261. package/dist/circuit-breaker/index.js +0 -3
  262. package/dist/circuit-breaker/index.js.map +0 -1
  263. package/dist/core-Bjc0bS8j.d.ts +0 -326
  264. package/dist/deadline/index.js +0 -6
  265. package/dist/deadline/index.js.map +0 -1
  266. package/dist/debounce/index.js +0 -3
  267. package/dist/debounce/index.js.map +0 -1
  268. package/dist/extension/react.d.ts +0 -121
  269. package/dist/extension/react.js +0 -75
  270. package/dist/extension/react.js.map +0 -1
  271. package/dist/extension/subs/index.d.ts +0 -44
  272. package/dist/extension/subs/index.js +0 -80
  273. package/dist/extension/subs/index.js.map +0 -1
  274. package/dist/extension/test-utils.d.ts +0 -128
  275. package/dist/extension/test-utils.js +0 -277
  276. package/dist/extension/test-utils.js.map +0 -1
  277. package/dist/fan-out/index.js +0 -5
  278. package/dist/fan-out/index.js.map +0 -1
  279. package/dist/fold-Blbhru48.d.ts +0 -435
  280. package/dist/graph-CcjWZVrd.d.ts +0 -923
  281. package/dist/idempotency/adapter.d.ts +0 -62
  282. package/dist/idempotency/adapter.js +0 -4
  283. package/dist/idempotency/adapter.js.map +0 -1
  284. package/dist/idempotent-intake/index.d.ts +0 -187
  285. package/dist/idempotent-intake/index.js +0 -7
  286. package/dist/idempotent-intake/index.js.map +0 -1
  287. package/dist/llm-call/index.d.ts +0 -310
  288. package/dist/llm-call/index.js +0 -16
  289. package/dist/llm-call/index.js.map +0 -1
  290. package/dist/machine-0OmPvsy_.d.ts +0 -804
  291. package/dist/monitored-run/index.js +0 -10
  292. package/dist/monitored-run/index.js.map +0 -1
  293. package/dist/paginated-walk/index.d.ts +0 -234
  294. package/dist/paginated-walk/index.js +0 -16
  295. package/dist/paginated-walk/index.js.map +0 -1
  296. package/dist/paginator/index.js +0 -3
  297. package/dist/paginator/index.js.map +0 -1
  298. package/dist/pbt/arbitraries/index.d.ts +0 -135
  299. package/dist/pbt/arbitraries/index.js +0 -3
  300. package/dist/pbt/arbitraries/index.js.map +0 -1
  301. package/dist/pbt/runners/index.d.ts +0 -117
  302. package/dist/pbt/runners/index.js +0 -5
  303. package/dist/pbt/runners/index.js.map +0 -1
  304. package/dist/poller/index.d.ts +0 -351
  305. package/dist/poller/index.js +0 -115
  306. package/dist/poller/index.js.map +0 -1
  307. package/dist/prediction/index.d.ts +0 -130
  308. package/dist/prediction/index.js +0 -4
  309. package/dist/prediction/index.js.map +0 -1
  310. package/dist/protocol-B5YmJr0d.d.ts +0 -45
  311. package/dist/pure/index.d.ts +0 -2
  312. package/dist/pure/index.js +0 -4
  313. package/dist/pure/index.js.map +0 -1
  314. package/dist/rate-limit/index.d.ts +0 -115
  315. package/dist/rate-limit/index.js +0 -3
  316. package/dist/rate-limit/index.js.map +0 -1
  317. package/dist/reconciler/index.d.ts +0 -192
  318. package/dist/reconciler/index.js +0 -164
  319. package/dist/reconciler/index.js.map +0 -1
  320. package/dist/recorder/index.js +0 -3
  321. package/dist/recorder/index.js.map +0 -1
  322. package/dist/reducer-BqyoVyBG.d.ts +0 -447
  323. package/dist/resilient-call/index.d.ts +0 -319
  324. package/dist/resilient-call/index.js +0 -14
  325. package/dist/resilient-call/index.js.map +0 -1
  326. package/dist/retry-to-success/index.d.ts +0 -109
  327. package/dist/retry-to-success/index.js +0 -37
  328. package/dist/retry-to-success/index.js.map +0 -1
  329. package/dist/run-BVeqJDBF.d.ts +0 -713
  330. package/dist/runtime-types-Bxc637bp.d.ts +0 -502
  331. package/dist/saga/index.d.ts +0 -240
  332. package/dist/saga/index.js +0 -150
  333. package/dist/saga/index.js.map +0 -1
  334. package/dist/samples-DCt6nF8H.d.ts +0 -59
  335. package/dist/snapshot/index.js +0 -5
  336. package/dist/snapshot/index.js.map +0 -1
  337. package/dist/subs/index.d.ts +0 -471
  338. package/dist/subs/index.js +0 -7
  339. package/dist/subs/index.js.map +0 -1
  340. package/dist/throttle/index.d.ts +0 -134
  341. package/dist/throttle/index.js +0 -3
  342. package/dist/throttle/index.js.map +0 -1
  343. package/dist/throttled-input/index.js.map +0 -1
  344. package/dist/token-refresh/index.d.ts +0 -215
  345. package/dist/token-refresh/index.js +0 -6
  346. package/dist/token-refresh/index.js.map +0 -1
  347. package/dist/trace-replay/index.js +0 -6
  348. package/dist/trace-replay/index.js.map +0 -1
  349. package/dist/with-deadline/index.d.ts +0 -196
  350. package/dist/with-deadline/index.js +0 -111
  351. package/dist/with-deadline/index.js.map +0 -1
  352. package/dist/with-resilience/index.d.ts +0 -224
  353. package/dist/with-resilience/index.js +0 -240
  354. package/dist/with-resilience/index.js.map +0 -1
  355. package/dist/with-telemetry/index.d.ts +0 -149
  356. package/dist/with-telemetry/index.js +0 -70
  357. package/dist/with-telemetry/index.js.map +0 -1
  358. package/dist/work-queue/adapter.d.ts +0 -83
  359. package/dist/work-queue/adapter.js +0 -4
  360. package/dist/work-queue/adapter.js.map +0 -1
  361. package/dist/work-queue/ops.d.ts +0 -70
  362. package/dist/work-queue/ops.js +0 -3
  363. package/dist/work-queue/ops.js.map +0 -1
  364. package/dist/workflow/index.d.ts +0 -484
  365. package/dist/workflow/index.js +0 -244
  366. package/dist/workflow/index.js.map +0 -1
  367. /package/dist/{cache → paginate}/index.js.map +0 -0
  368. /package/dist/{chart/inspect → persistence}/index.js.map +0 -0
@@ -1,351 +0,0 @@
1
- import { DeadlineSub } from '../deadline/index.js';
2
- import { IdempotencyStore } from '../idempotency/index.js';
3
- import { C as Cmd, S as Sub } from '../core-Bjc0bS8j.js';
4
- import { RetryState, AnyRetryPolicy } from '../retry-backoff/index.js';
5
- import '../types-D7u9DkH7.js';
6
-
7
- /**
8
- * @demlik/tea/poller — "poll a source every N ms until a predicate holds, with
9
- * backoff on failure" as a TEA knob: one config object + a few pre-wired hooks
10
- * you spread into your machine.
11
- *
12
- * The recipe (which bricks it composes):
13
- *
14
- * - `../deadline` — the recurring tick is an ABSOLUTE-deadline Sub.
15
- * Each successful tick computes the NEXT absolute
16
- * fire time (`at + everyMs`) and the knob re-arms a
17
- * deadline against it. Absolute (not relative) so a
18
- * poller resumed after a DO eviction / page reload
19
- * fires at the correct wall-clock moment, not
20
- * `everyMs` after the reload (`../deadline`'s late-
21
- * subscribe property).
22
- * - `../retry-backoff` — a failed tick does NOT reschedule on the steady
23
- * cadence; it backs off. The next attempt is armed
24
- * `nextDelayMs(retry, policy)` from now, the retry
25
- * counter advances, and `shouldRetry` decides whether
26
- * the poller keeps trying or gives up. The bound may
27
- * be a COUNT or a wall-clock OUTAGE DURATION — see
28
- * `PollerConfig.retry`; `tickErr`'s `at` is the
29
- * streak clock, so both flow through unchanged.
30
- * - `../idempotency` — successive ticks against a flaky source can deliver
31
- * the SAME observation twice (a retried fetch, a
32
- * duplicate webhook the poll re-reads). `dedupeKey`
33
- * lets the consumer key each tick result; a duplicate
34
- * key is recorded but emits no fresh `onTick` follow-
35
- * up Cmd, so the side effect runs once per distinct
36
- * observation.
37
- *
38
- * Typical wiring (spread the knob's hooks into your machine):
39
- *
40
- * const poll = createPoller<MyState, MyResult>({
41
- * everyMs: 5_000,
42
- * until: (s) => s.poll.lastResult?.status === "ready",
43
- * onTick: () => ({ type: "fetch_status" }),
44
- * retry: { ...defaultRetryPolicy, maxAttempts: 4 },
45
- * });
46
- *
47
- * // Model slice:
48
- * init: (loaded) => [loaded ?? { poll: poll.init(), ... }, []],
49
- *
50
- * // The deadline Sub fires `deadline_exceeded` on the cadence/backoff target;
51
- * // route it to `tick`, the ONLY place the `onTick` fetch is emitted:
52
- * deadline_exceeded: (s) => {
53
- * const [slice, cmds] = poll.tick(s.poll);
54
- * return [{ ...s, poll: slice }, cmds];
55
- * },
56
- *
57
- * // update cells (thread `at` in from the firing Msg / the runtime clock):
58
- * poll_result: (s, m) => {
59
- * const untilHeld = config.until({ ...s, ...folded });
60
- * const [slice, cmds] = poll.tickResult(s.poll, m.result, m.at, untilHeld);
61
- * return [{ ...s, poll: slice }, cmds];
62
- * },
63
- * poll_error: (s, m) => {
64
- * const [slice, cmds] = poll.tickErr(s.poll, m.error, m.at);
65
- * return [{ ...s, poll: slice }, cmds];
66
- * },
67
- *
68
- * // Subs — the knob owns the interval/backoff deadline:
69
- * subscriptions: (s) => poll.subs(s.poll),
70
- *
71
- * The cadence has exactly ONE source: the `everyMs` deadline Sub. `start`,
72
- * `tickResult`, and `tickErr` only RE-ARM the deadline (they emit no cadence
73
- * Cmd); the fetch fires solely from `tick`, which the consumer routes the
74
- * deadline's `deadline_exceeded` Msg to. No verb performs an immediate
75
- * "fire now" observation, so the poller never polls faster than `everyMs`.
76
- *
77
- * The two non-negotiables every knob preserves hold here:
78
- * - **Durable** — `tick`, `lastResult`, `retry`, and the next-fire target all
79
- * live in the Model slice, so a poller survives eviction and resumes its
80
- * cadence from the persisted target.
81
- * - **Replayable** — every transition is a pure verb returning new state; the
82
- * clock is never read inside a verb (the firing Msg carries `at`), and the
83
- * retry RNG is injected ONCE at `createPoller` and threaded internally (no
84
- * verb body names the global RNG). `replay` reconstructs a poll run exactly.
85
- *
86
- * NOT a substrate primitive: it depends only on sibling subpaths
87
- * (`../deadline`, `../retry-backoff`, `../idempotency`) and the core `Cmd` /
88
- * `Sub` types. Consumers reach it via the `@demlik/tea/poller` subpath; the L1
89
- * escape hatch is those three sibling subpaths threaded by hand.
90
- */
91
-
92
- /**
93
- * The poller knob's config — the single object you hand `createPoller`.
94
- *
95
- * `State` is the consumer's WHOLE Model (so `until` can read any field), `R` is
96
- * the shape of one tick observation (`lastResult` carries it forward).
97
- */
98
- interface PollerConfig<State, R> {
99
- /**
100
- * The steady cadence, in milliseconds, between successful ticks. The next
101
- * tick is armed at the absolute target `at + everyMs` (computed from the
102
- * `at` the prior tick's Msg carried), NOT `everyMs` from when the Sub
103
- * happens to be reconciled — so the cadence does not drift across resumes.
104
- */
105
- readonly everyMs: number;
106
- /**
107
- * The stop predicate, read against the consumer's whole Model. When it
108
- * returns `true`, the poller is DONE: `subs` returns `[]` (the timer
109
- * disarms) and no further tick is scheduled. Pure — it must not read the
110
- * clock or mutate; the substrate calls it inside `subscriptions(state)`.
111
- */
112
- readonly until: (state: State) => boolean;
113
- /**
114
- * The effect to run on each tick — the actual "go observe the source" Cmd
115
- * (a fetch, a status read, a list call). The knob returns it from
116
- * `tickResult` when the run continues; the consumer's `interpret` performs
117
- * it and routes the observation back as the next tick-result Msg.
118
- *
119
- * Nullary by contract: a tick carries no per-call argument (the poller
120
- * re-reads the same source every cadence). Capture any target in the Cmd
121
- * literal the consumer's `onTick` returns.
122
- */
123
- readonly onTick: () => Cmd;
124
- /**
125
- * Backoff policy for FAILED ticks. Omit to use `defaultRetryPolicy`
126
- * (full-jitter, 5 attempts). A success resets the retry counter, so the
127
- * policy only governs consecutive failures.
128
- *
129
- * Any bound the `../retry-backoff` union admits: a count (`RetryPolicy`), a
130
- * wall-clock outage budget (`DurationRetryPolicy`), or explicit
131
- * `unbounded: true`. The duration bound needs no extra wiring here — every
132
- * failure already arrives through `tickErr(state, error, at)` carrying the
133
- * instant it was observed, so the streak clock is fed from the Msg's own
134
- * data and the poller never reads a clock of its own (invariant 2).
135
- */
136
- readonly retry?: AnyRetryPolicy;
137
- /**
138
- * Optional dedupe key derived from a tick result. When provided, a result
139
- * whose key was already `seen` records the observation but emits NO fresh
140
- * `onTick` follow-up — the side effect runs once per distinct observation.
141
- * Omit to treat every tick result as fresh (no dedupe). The dedupe store is
142
- * bounded by `dedupeTtlMs` if set; otherwise it grows with distinct keys.
143
- */
144
- readonly dedupeKey?: (result: R) => string;
145
- /**
146
- * TTL for the dedupe store, in milliseconds. Only meaningful when
147
- * `dedupeKey` is set. An observation older than `dedupeTtlMs` is forgotten,
148
- * so a key seen long ago is treated as fresh again. Omit for an unbounded
149
- * dedupe window (every distinct key remembered for the poller's lifetime).
150
- */
151
- readonly dedupeTtlMs?: number;
152
- }
153
- /**
154
- * The bookkeeping every poller phase carries in common, split out so each arm of
155
- * the `PollerState` discriminated union names only what it adds (`nextAtMs` on
156
- * `polling`) — the same discipline the sibling `PaginatorState` uses.
157
- *
158
- * - `tick` — count of ticks recorded so far (success or failure). Doubles
159
- * as an observability cursor and a stable per-tick discriminant.
160
- * - `lastResult` — the most recent successful observation, or `undefined`
161
- * before the first success. `until` typically reads it (via the
162
- * consumer's Model), but the knob never interprets it.
163
- * - `retry` — the `../retry-backoff` counter for consecutive failures.
164
- * Reset to `initRetry()` on every success. Once a failure has
165
- * been recorded it is a `TimedRetryState` (it carries the
166
- * streak's `firstFailureAtMs`, minted from `tickErr`'s `at`);
167
- * the field is typed at the `RetryState` supertype because a
168
- * poller that has never failed has no streak, and a slice
169
- * persisted before the origin existed rehydrates without one.
170
- * - `dedupe` — the `../idempotency` store backing `dedupeKey`. An empty,
171
- * unbounded store when `dedupeKey` is unset (never written).
172
- */
173
- interface PollerCore<R> {
174
- readonly tick: number;
175
- readonly lastResult?: R;
176
- readonly retry: RetryState;
177
- readonly dedupe: IdempotencyStore<true>;
178
- }
179
- /**
180
- * The Model field the poller knob owns — its visible slice (the knob principle:
181
- * managed state lives in the Model, never a closure, so it is durable and
182
- * replayable). A discriminated union on `phase` so the armed-timer target lives
183
- * ONLY where it is meaningful and impossible combinations are unrepresentable
184
- * (pattern 11), the sibling `PaginatorState` idiom:
185
- *
186
- * - `polling` — ticks are scheduled. Carries `nextAtMs`, the ABSOLUTE target
187
- * the interval/backoff timer is armed against (persisted so a
188
- * resumed poller re-arms the same moment), or `null` before the
189
- * first `start` primes it.
190
- * - `done` — `until` held; the poll is finished. Carries NO `nextAtMs` — a
191
- * finished poll can never sit on an armed target.
192
- * - `gave_up` — backoff exhausted `maxAttempts`. Carries NO `nextAtMs` either.
193
- *
194
- * Because only `polling` carries `nextAtMs`, `subs` guards on `phase` alone to
195
- * decide whether a timer is desired — a `done`/`gave_up` state with a live target
196
- * (the old flat record's hazard) simply cannot be built.
197
- */
198
- type PollerState<R> = (PollerCore<R> & {
199
- readonly phase: "polling";
200
- readonly nextAtMs: number | null;
201
- }) | (PollerCore<R> & {
202
- readonly phase: "done";
203
- }) | (PollerCore<R> & {
204
- readonly phase: "gave_up";
205
- });
206
- /**
207
- * The three arms, named — so a verb can DECLARE the phases it can actually
208
- * reach instead of the whole union.
209
- *
210
- * A verb whose return type is the full `PollerState<R>` tells a caller nothing
211
- * about where the poller went, and a caller that needs to know (a chart
212
- * declaring an edge's fan-out; an exhaustive `switch`) has to re-derive it by
213
- * reading the verb's body. Every one of these narrowings is already true of the
214
- * implementation — `start` only ever builds a `polling` arm, `tickResult` never
215
- * builds `gave_up`, `tickErr` never builds `done` — so naming them costs
216
- * nothing at runtime and moves a fact out of the source and into the signature.
217
- */
218
- type PollerPolling<R> = Extract<PollerState<R>, {
219
- phase: "polling";
220
- }>;
221
- type PollerDone<R> = Extract<PollerState<R>, {
222
- phase: "done";
223
- }>;
224
- type PollerGaveUp<R> = Extract<PollerState<R>, {
225
- phase: "gave_up";
226
- }>;
227
- /**
228
- * The poller Sub type — a `../deadline` Sub under a fixed id family. One id per
229
- * knob instance keeps the reconcile pass from churning the timer: the same id
230
- * across transitions means "same armed deadline", even as `nextAtMs` advances.
231
- */
232
- type PollerSub = DeadlineSub;
233
- /**
234
- * The knob handle returned by `createPoller`. Spread its hooks into your
235
- * machine: `init()` seeds the slice, `start(...)` arms the first tick,
236
- * `tickResult` / `tickErr` are the two update verbs, and `subs(...)` returns the
237
- * timer Sub while the poller is live.
238
- *
239
- * Pure-ops + subs module: there is no `handlers(ports)` cell. The poller's I/O
240
- * is the consumer's own `onTick` Cmd run by the consumer's `interpret` — the
241
- * knob never owns a port. (Compare `../deadline`, which ships a `subscribe`
242
- * cell; the poller reuses `../deadline`'s, so it has nothing of its own to
243
- * splice into `interpret`.)
244
- */
245
- interface Poller<State, R> {
246
- /** Seed the Model slice. Idle until `start(...)` arms the first tick. */
247
- init(): PollerPolling<R>;
248
- /**
249
- * Arm the first tick. `at` is the current clock (carried in from the Msg /
250
- * runtime that kicks the poller off — never read inside the verb). Returns
251
- * the slice with the first deadline target set to `at + everyMs` and emits NO
252
- * Cmd. The cadence has ONE source — the deadline Sub — so `start` does not
253
- * perform the first observation itself (an immediate `onTick` here would race
254
- * the timer and poll as fast as the source responds). The first observation
255
- * fires when the deadline crosses `at + everyMs` and the consumer routes
256
- * `deadline_exceeded` → `tick`.
257
- */
258
- start(state: PollerState<R>, at: number): readonly [PollerPolling<R>, readonly Cmd[]];
259
- /**
260
- * Perform one observation — the cadence verb. The consumer routes the
261
- * deadline Sub's `deadline_exceeded` Msg here; `tick` emits the single
262
- * `config.onTick()` Cmd that goes and reads the source. This is the ONLY
263
- * next-tick mechanism: the timer fires, the consumer calls `tick`, the fetch
264
- * runs, and the observation comes back through `tickResult` / `tickErr`
265
- * (which re-arm the next deadline). A no-op on a finished poller — a stale
266
- * deadline fire racing a terminal transition the reconcile pass has not yet
267
- * retired emits nothing.
268
- *
269
- * Takes no `at`: `tick` does not touch the schedule (the just-fired deadline
270
- * is exhausted; the next target is set when the result lands), so it needs no
271
- * clock — keeping it trivially pure.
272
- */
273
- tick(state: PollerState<R>): readonly [PollerState<R>, readonly Cmd[]];
274
- /**
275
- * Record a successful tick observation at clock `at`.
276
- *
277
- * - Resets the retry counter (a success clears consecutive-failure state).
278
- * - Stores `result` as `lastResult` and bumps `tick`.
279
- * - Re-arms the next tick at the absolute target `at + everyMs` (the steady
280
- * cadence). It emits NO cadence `onTick` — the next observation fires from
281
- * the deadline Sub via `tick`, never immediately from a result.
282
- * - Dedupe (opt-in, orthogonal to cadence): when `dedupeKey` is set, a FRESH
283
- * observation emits one `onTick` FOLLOW-UP (the consumer's "run the
284
- * downstream side effect once per distinct observation" hook) and a
285
- * DUPLICATE key records the sighting but suppresses the follow-up. With no
286
- * `dedupeKey`, `tickResult` emits nothing — the cadence `tick` is the only
287
- * `onTick`, so an unconfigured poller never double-fetches.
288
- * - UNLESS `untilHeld` is `true` (the consumer evaluated `config.until`
289
- * against the post-result Model) — then the poller enters `"done"`,
290
- * disarms (`nextAtMs: null`), and emits no Cmd, dedupe or otherwise.
291
- *
292
- * `untilHeld` is passed in rather than computed here because `until` reads
293
- * the consumer's WHOLE Model, which this verb does not hold — the consumer
294
- * evaluates it at the call site after folding `result` into its Model and
295
- * passes the boolean. (`subs` re-checks `phase` to decide arming, so a
296
- * mis-passed `untilHeld` cannot leave a "done" poller arming timers.)
297
- */
298
- tickResult(state: PollerState<R>, result: R, at: number, untilHeld: boolean): readonly [PollerPolling<R> | PollerDone<R>, readonly Cmd[]];
299
- /**
300
- * Record a FAILED tick at clock `at`. Backs off instead of holding the
301
- * steady cadence:
302
- *
303
- * - Advances the retry counter (`recordFailure`).
304
- * - If `shouldRetry` still permits another attempt, arms the next tick at
305
- * `at + nextDelayMs(retry, policy)` (the backoff curve) and emits NO Cmd —
306
- * the retry observation fires when that backoff deadline crosses and the
307
- * consumer routes `deadline_exceeded` → `tick`. The timer is the single
308
- * next-tick mechanism, on the backoff curve here instead of `everyMs`.
309
- * - If backoff is exhausted (`!shouldRetry`), enters `"gave_up"`, disarms
310
- * (`nextAtMs: null`), and emits no Cmd.
311
- *
312
- * `at` is both the backoff anchor AND the streak clock: it is passed to
313
- * `recordFailure` (starting / preserving `firstFailureAtMs`) and to
314
- * `shouldRetry`, so a `DurationRetryPolicy` on `config.retry` is honoured
315
- * with no extra wiring. Under a duration bound the poller keeps retrying
316
- * however many attempts the outage takes, and stops at the declared
317
- * wall-clock budget measured from the streak's FIRST failure.
318
- *
319
- * The backoff jitter uses the `rng` injected ONCE at `createPoller` (default
320
- * `Math.random` at the effect boundary, a fixed value in tests); the verb
321
- * body never names the global RNG, so a poller built with a fixed `rng`
322
- * replays a failure-and-backoff run bit-for-bit.
323
- */
324
- tickErr(state: PollerState<R>, error: unknown, at: number): readonly [PollerPolling<R> | PollerGaveUp<R>, readonly Cmd[]];
325
- /**
326
- * The pre-wired subscriptions cell. Returns the single tick-deadline Sub
327
- * while the poller is `"polling"` with an armed `nextAtMs`; returns `[]`
328
- * once `"done"` / `"gave_up"` (the timer disarms) or before `start`. Assign
329
- * directly: `subscriptions: (s) => poll.subs(s.poll)`.
330
- *
331
- * The `DeadlineSub` it returns is consumed by `../deadline`'s
332
- * `subscribeDeadline` cell, which the consumer wires into `subscribe` —
333
- * the poller does not redraw the timer lifecycle.
334
- */
335
- subs(state: PollerState<R>): readonly Sub[];
336
- }
337
- /**
338
- * Build a poller knob from `config`. Returns the hook bag to spread into a
339
- * machine (see the module doc-comment for the wiring). Pure factory — no clock
340
- * read, no timer; everything happens when the consumer calls the returned
341
- * verbs / `subs` with an injected `at`.
342
- *
343
- * `rng` is injected ONCE here (default `Math.random` at the effect boundary, a
344
- * fixed value in tests) and threaded internally into the backoff jitter. No
345
- * verb body ever names the global RNG, so a poller built with a fixed `rng`
346
- * replays a failure-and-backoff run bit-for-bit (invariant 2: the verbs are
347
- * pure given the injected `rng`).
348
- */
349
- declare function createPoller<State, R>(config: PollerConfig<State, R>, rng?: () => number): Poller<State, R>;
350
-
351
- export { type Poller, type PollerConfig, type PollerDone, type PollerGaveUp, type PollerPolling, type PollerState, type PollerSub, createPoller };
@@ -1,115 +0,0 @@
1
- import { deadlineSub } from '../chunk-JGVL3WPL.js';
2
- import { asRng, defaultRetryPolicy, initRetry, recordFailure, shouldRetry, nextDelayMs } from '../chunk-Q2UM7BXU.js';
3
- import { idempotencyMemory } from '../chunk-BYQUGDD3.js';
4
- import '../chunk-K575PT4S.js';
5
- import '../chunk-2YFLOCWX.js';
6
- import '../chunk-5EHLEUTL.js';
7
- import '../chunk-B5VXOFMZ.js';
8
-
9
- // src/poller/index.ts
10
- function pollerSubId(atMs) {
11
- return `poller:tick:${atMs}`;
12
- }
13
- function createPoller(config, rng = Math.random) {
14
- const rngBranded = asRng(rng);
15
- const policy = config.retry ?? defaultRetryPolicy;
16
- const memory = idempotencyMemory();
17
- function init() {
18
- return {
19
- phase: "polling",
20
- tick: 0,
21
- retry: initRetry(),
22
- nextAtMs: null,
23
- // The dedupe store is created regardless; it is only WRITTEN when
24
- // `dedupeKey` is set, so an unconfigured poller carries an inert empty
25
- // store (no per-op cost, JSON-serializable, durable like the rest of
26
- // the slice).
27
- dedupe: memory.init({ ttlMs: config.dedupeTtlMs })
28
- };
29
- }
30
- function start(state, at) {
31
- return [
32
- {
33
- phase: "polling",
34
- tick: state.tick,
35
- lastResult: state.lastResult,
36
- retry: state.retry,
37
- nextAtMs: at + config.everyMs,
38
- dedupe: state.dedupe
39
- },
40
- []
41
- ];
42
- }
43
- function tick(state) {
44
- if (state.phase !== "polling") return [state, []];
45
- return [state, [config.onTick()]];
46
- }
47
- function tickResult(state, result, at, untilHeld) {
48
- const tick2 = state.tick + 1;
49
- if (untilHeld) {
50
- return [
51
- {
52
- phase: "done",
53
- tick: tick2,
54
- lastResult: result,
55
- retry: initRetry(),
56
- dedupe: state.dedupe
57
- },
58
- []
59
- ];
60
- }
61
- const next = {
62
- phase: "polling",
63
- tick: tick2,
64
- lastResult: result,
65
- retry: initRetry(),
66
- nextAtMs: at + config.everyMs,
67
- dedupe: state.dedupe
68
- };
69
- if (!config.dedupeKey) return [next, []];
70
- const key = config.dedupeKey(result);
71
- const suppress = memory.isSeen(next.dedupe, key, at);
72
- const deduped = {
73
- ...next,
74
- dedupe: memory.remember(next.dedupe, key, true, at)
75
- };
76
- return [deduped, suppress ? [] : [config.onTick()]];
77
- }
78
- function tickErr(state, error, at) {
79
- const retry = recordFailure(state.retry, error, at);
80
- const tick2 = state.tick + 1;
81
- if (!shouldRetry(retry, policy, at)) {
82
- return [
83
- {
84
- phase: "gave_up",
85
- tick: tick2,
86
- lastResult: state.lastResult,
87
- retry,
88
- dedupe: state.dedupe
89
- },
90
- []
91
- ];
92
- }
93
- const delayMs = nextDelayMs(retry, policy, rngBranded);
94
- return [
95
- {
96
- phase: "polling",
97
- tick: tick2,
98
- lastResult: state.lastResult,
99
- retry,
100
- nextAtMs: at + delayMs,
101
- dedupe: state.dedupe
102
- },
103
- []
104
- ];
105
- }
106
- function subs(state) {
107
- if (state.phase !== "polling" || state.nextAtMs === null) return [];
108
- return [deadlineSub(pollerSubId(state.nextAtMs), state.nextAtMs)];
109
- }
110
- return { init, start, tick, tickResult, tickErr, subs };
111
- }
112
-
113
- export { createPoller };
114
- //# sourceMappingURL=index.js.map
115
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/poller/index.ts"],"names":["tick"],"mappings":";;;;;;;;;AA6PA,SAAS,YAAY,IAAA,EAAsB;AACzC,EAAA,OAAO,eAAe,IAAI,CAAA,CAAA;AAC5B;AA4IO,SAAS,YAAA,CACd,MAAA,EACA,GAAA,GAAoB,IAAA,CAAK,MAAA,EACP;AAKlB,EAAA,MAAM,UAAA,GAAa,MAAM,GAAG,CAAA;AAC5B,EAAA,MAAM,MAAA,GAAyB,OAAO,KAAA,IAAS,kBAAA;AAK/C,EAAA,MAAM,SAAS,iBAAA,EAAwB;AAEvC,EAAA,SAAS,IAAA,GAAyB;AAChC,IAAA,OAAO;AAAA,MACL,KAAA,EAAO,SAAA;AAAA,MACP,IAAA,EAAM,CAAA;AAAA,MACN,OAAO,SAAA,EAAU;AAAA,MACjB,QAAA,EAAU,IAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAKV,QAAQ,MAAA,CAAO,IAAA,CAAK,EAAE,KAAA,EAAO,MAAA,CAAO,aAAa;AAAA,KACnD;AAAA,EACF;AAEA,EAAA,SAAS,KAAA,CACP,OACA,EAAA,EAC6C;AAU7C,IAAA,OAAO;AAAA,MACL;AAAA,QACE,KAAA,EAAO,SAAA;AAAA,QACP,MAAM,KAAA,CAAM,IAAA;AAAA,QACZ,YAAY,KAAA,CAAM,UAAA;AAAA,QAClB,OAAO,KAAA,CAAM,KAAA;AAAA,QACb,QAAA,EAAU,KAAK,MAAA,CAAO,OAAA;AAAA,QACtB,QAAQ,KAAA,CAAM;AAAA,OAChB;AAAA,MACA;AAAC,KACH;AAAA,EACF;AAEA,EAAA,SAAS,KACP,KAAA,EAC2C;AAO3C,IAAA,IAAI,MAAM,KAAA,KAAU,SAAA,SAAkB,CAAC,KAAA,EAAO,EAAE,CAAA;AAChD,IAAA,OAAO,CAAC,KAAA,EAAO,CAAC,MAAA,CAAO,MAAA,EAAQ,CAAC,CAAA;AAAA,EAClC;AAEA,EAAA,SAAS,UAAA,CACP,KAAA,EACA,MAAA,EACA,EAAA,EACA,SAAA,EAC6D;AAI7D,IAAA,MAAMA,KAAAA,GAAO,MAAM,IAAA,GAAO,CAAA;AAK1B,IAAA,IAAI,SAAA,EAAW;AACb,MAAA,OAAO;AAAA,QACL;AAAA,UACE,KAAA,EAAO,MAAA;AAAA,UACP,IAAA,EAAAA,KAAAA;AAAA,UACA,UAAA,EAAY,MAAA;AAAA,UACZ,OAAO,SAAA,EAAU;AAAA,UACjB,QAAQ,KAAA,CAAM;AAAA,SAChB;AAAA,QACA;AAAC,OACH;AAAA,IACF;AAOA,IAAA,MAAM,IAAA,GAAyB;AAAA,MAC7B,KAAA,EAAO,SAAA;AAAA,MACP,IAAA,EAAAA,KAAAA;AAAA,MACA,UAAA,EAAY,MAAA;AAAA,MACZ,OAAO,SAAA,EAAU;AAAA,MACjB,QAAA,EAAU,KAAK,MAAA,CAAO,OAAA;AAAA,MACtB,QAAQ,KAAA,CAAM;AAAA,KAChB;AAQA,IAAA,IAAI,CAAC,MAAA,CAAO,SAAA,SAAkB,CAAC,IAAA,EAAM,EAAE,CAAA;AAEvC,IAAA,MAAM,GAAA,GAAM,MAAA,CAAO,SAAA,CAAU,MAAM,CAAA;AACnC,IAAA,MAAM,WAAW,MAAA,CAAO,MAAA,CAAO,IAAA,CAAK,MAAA,EAAQ,KAAK,EAAE,CAAA;AAKnD,IAAA,MAAM,OAAA,GAA4B;AAAA,MAChC,GAAG,IAAA;AAAA,MACH,QAAQ,MAAA,CAAO,QAAA,CAAS,KAAK,MAAA,EAAQ,GAAA,EAAK,MAAM,EAAE;AAAA,KACpD;AACA,IAAA,OAAO,CAAC,SAAS,QAAA,GAAW,KAAK,CAAC,MAAA,CAAO,MAAA,EAAQ,CAAC,CAAA;AAAA,EACpD;AAEA,EAAA,SAAS,OAAA,CACP,KAAA,EACA,KAAA,EACA,EAAA,EAC+D;AAO/D,IAAA,MAAM,KAAA,GAAQ,aAAA,CAAc,KAAA,CAAM,KAAA,EAAO,OAAO,EAAE,CAAA;AAClD,IAAA,MAAMA,KAAAA,GAAO,MAAM,IAAA,GAAO,CAAA;AAO1B,IAAA,IAAI,CAAC,WAAA,CAAY,KAAA,EAAO,MAAA,EAAQ,EAAE,CAAA,EAAG;AACnC,MAAA,OAAO;AAAA,QACL;AAAA,UACE,KAAA,EAAO,SAAA;AAAA,UACP,IAAA,EAAAA,KAAAA;AAAA,UACA,YAAY,KAAA,CAAM,UAAA;AAAA,UAClB,KAAA;AAAA,UACA,QAAQ,KAAA,CAAM;AAAA,SAChB;AAAA,QACA;AAAC,OACH;AAAA,IACF;AASA,IAAA,MAAM,OAAA,GAAU,WAAA,CAAY,KAAA,EAAO,MAAA,EAAQ,UAAU,CAAA;AACrD,IAAA,OAAO;AAAA,MACL;AAAA,QACE,KAAA,EAAO,SAAA;AAAA,QACP,IAAA,EAAAA,KAAAA;AAAA,QACA,YAAY,KAAA,CAAM,UAAA;AAAA,QAClB,KAAA;AAAA,QACA,UAAU,EAAA,GAAK,OAAA;AAAA,QACf,QAAQ,KAAA,CAAM;AAAA,OAChB;AAAA,MACA;AAAC,KACH;AAAA,EACF;AAEA,EAAA,SAAS,KAAK,KAAA,EAAuC;AAMnD,IAAA,IAAI,MAAM,KAAA,KAAU,SAAA,IAAa,MAAM,QAAA,KAAa,IAAA,SAAa,EAAC;AASlE,IAAA,OAAO,CAAC,YAAY,WAAA,CAAY,KAAA,CAAM,QAAQ,CAAA,EAAG,KAAA,CAAM,QAAQ,CAAC,CAAA;AAAA,EAClE;AAEA,EAAA,OAAO,EAAE,IAAA,EAAM,KAAA,EAAO,IAAA,EAAM,UAAA,EAAY,SAAS,IAAA,EAAK;AACxD","file":"index.js","sourcesContent":["/**\n * @demlik/tea/poller — \"poll a source every N ms until a predicate holds, with\n * backoff on failure\" as a TEA knob: one config object + a few pre-wired hooks\n * you spread into your machine.\n *\n * The recipe (which bricks it composes):\n *\n * - `../deadline` — the recurring tick is an ABSOLUTE-deadline Sub.\n * Each successful tick computes the NEXT absolute\n * fire time (`at + everyMs`) and the knob re-arms a\n * deadline against it. Absolute (not relative) so a\n * poller resumed after a DO eviction / page reload\n * fires at the correct wall-clock moment, not\n * `everyMs` after the reload (`../deadline`'s late-\n * subscribe property).\n * - `../retry-backoff` — a failed tick does NOT reschedule on the steady\n * cadence; it backs off. The next attempt is armed\n * `nextDelayMs(retry, policy)` from now, the retry\n * counter advances, and `shouldRetry` decides whether\n * the poller keeps trying or gives up. The bound may\n * be a COUNT or a wall-clock OUTAGE DURATION — see\n * `PollerConfig.retry`; `tickErr`'s `at` is the\n * streak clock, so both flow through unchanged.\n * - `../idempotency` — successive ticks against a flaky source can deliver\n * the SAME observation twice (a retried fetch, a\n * duplicate webhook the poll re-reads). `dedupeKey`\n * lets the consumer key each tick result; a duplicate\n * key is recorded but emits no fresh `onTick` follow-\n * up Cmd, so the side effect runs once per distinct\n * observation.\n *\n * Typical wiring (spread the knob's hooks into your machine):\n *\n * const poll = createPoller<MyState, MyResult>({\n * everyMs: 5_000,\n * until: (s) => s.poll.lastResult?.status === \"ready\",\n * onTick: () => ({ type: \"fetch_status\" }),\n * retry: { ...defaultRetryPolicy, maxAttempts: 4 },\n * });\n *\n * // Model slice:\n * init: (loaded) => [loaded ?? { poll: poll.init(), ... }, []],\n *\n * // The deadline Sub fires `deadline_exceeded` on the cadence/backoff target;\n * // route it to `tick`, the ONLY place the `onTick` fetch is emitted:\n * deadline_exceeded: (s) => {\n * const [slice, cmds] = poll.tick(s.poll);\n * return [{ ...s, poll: slice }, cmds];\n * },\n *\n * // update cells (thread `at` in from the firing Msg / the runtime clock):\n * poll_result: (s, m) => {\n * const untilHeld = config.until({ ...s, ...folded });\n * const [slice, cmds] = poll.tickResult(s.poll, m.result, m.at, untilHeld);\n * return [{ ...s, poll: slice }, cmds];\n * },\n * poll_error: (s, m) => {\n * const [slice, cmds] = poll.tickErr(s.poll, m.error, m.at);\n * return [{ ...s, poll: slice }, cmds];\n * },\n *\n * // Subs — the knob owns the interval/backoff deadline:\n * subscriptions: (s) => poll.subs(s.poll),\n *\n * The cadence has exactly ONE source: the `everyMs` deadline Sub. `start`,\n * `tickResult`, and `tickErr` only RE-ARM the deadline (they emit no cadence\n * Cmd); the fetch fires solely from `tick`, which the consumer routes the\n * deadline's `deadline_exceeded` Msg to. No verb performs an immediate\n * \"fire now\" observation, so the poller never polls faster than `everyMs`.\n *\n * The two non-negotiables every knob preserves hold here:\n * - **Durable** — `tick`, `lastResult`, `retry`, and the next-fire target all\n * live in the Model slice, so a poller survives eviction and resumes its\n * cadence from the persisted target.\n * - **Replayable** — every transition is a pure verb returning new state; the\n * clock is never read inside a verb (the firing Msg carries `at`), and the\n * retry RNG is injected ONCE at `createPoller` and threaded internally (no\n * verb body names the global RNG). `replay` reconstructs a poll run exactly.\n *\n * NOT a substrate primitive: it depends only on sibling subpaths\n * (`../deadline`, `../retry-backoff`, `../idempotency`) and the core `Cmd` /\n * `Sub` types. Consumers reach it via the `@demlik/tea/poller` subpath; the L1\n * escape hatch is those three sibling subpaths threaded by hand.\n */\n\nimport { type DeadlineSub, deadlineSub } from \"../deadline\";\nimport type { IdempotencyStore } from \"../idempotency\";\nimport { idempotencyMemory } from \"../idempotency/adapter\";\nimport type { Cmd, Sub } from \"../index\";\nimport {\n type AnyRetryPolicy,\n asRng,\n defaultRetryPolicy,\n initRetry,\n nextDelayMs,\n type RetryState,\n recordFailure,\n shouldRetry,\n} from \"../retry-backoff\";\n\n/**\n * The poller knob's config — the single object you hand `createPoller`.\n *\n * `State` is the consumer's WHOLE Model (so `until` can read any field), `R` is\n * the shape of one tick observation (`lastResult` carries it forward).\n */\nexport interface PollerConfig<State, R> {\n /**\n * The steady cadence, in milliseconds, between successful ticks. The next\n * tick is armed at the absolute target `at + everyMs` (computed from the\n * `at` the prior tick's Msg carried), NOT `everyMs` from when the Sub\n * happens to be reconciled — so the cadence does not drift across resumes.\n */\n readonly everyMs: number;\n /**\n * The stop predicate, read against the consumer's whole Model. When it\n * returns `true`, the poller is DONE: `subs` returns `[]` (the timer\n * disarms) and no further tick is scheduled. Pure — it must not read the\n * clock or mutate; the substrate calls it inside `subscriptions(state)`.\n */\n readonly until: (state: State) => boolean;\n /**\n * The effect to run on each tick — the actual \"go observe the source\" Cmd\n * (a fetch, a status read, a list call). The knob returns it from\n * `tickResult` when the run continues; the consumer's `interpret` performs\n * it and routes the observation back as the next tick-result Msg.\n *\n * Nullary by contract: a tick carries no per-call argument (the poller\n * re-reads the same source every cadence). Capture any target in the Cmd\n * literal the consumer's `onTick` returns.\n */\n readonly onTick: () => Cmd;\n /**\n * Backoff policy for FAILED ticks. Omit to use `defaultRetryPolicy`\n * (full-jitter, 5 attempts). A success resets the retry counter, so the\n * policy only governs consecutive failures.\n *\n * Any bound the `../retry-backoff` union admits: a count (`RetryPolicy`), a\n * wall-clock outage budget (`DurationRetryPolicy`), or explicit\n * `unbounded: true`. The duration bound needs no extra wiring here — every\n * failure already arrives through `tickErr(state, error, at)` carrying the\n * instant it was observed, so the streak clock is fed from the Msg's own\n * data and the poller never reads a clock of its own (invariant 2).\n */\n readonly retry?: AnyRetryPolicy;\n /**\n * Optional dedupe key derived from a tick result. When provided, a result\n * whose key was already `seen` records the observation but emits NO fresh\n * `onTick` follow-up — the side effect runs once per distinct observation.\n * Omit to treat every tick result as fresh (no dedupe). The dedupe store is\n * bounded by `dedupeTtlMs` if set; otherwise it grows with distinct keys.\n */\n readonly dedupeKey?: (result: R) => string;\n /**\n * TTL for the dedupe store, in milliseconds. Only meaningful when\n * `dedupeKey` is set. An observation older than `dedupeTtlMs` is forgotten,\n * so a key seen long ago is treated as fresh again. Omit for an unbounded\n * dedupe window (every distinct key remembered for the poller's lifetime).\n */\n readonly dedupeTtlMs?: number;\n}\n\n/**\n * The bookkeeping every poller phase carries in common, split out so each arm of\n * the `PollerState` discriminated union names only what it adds (`nextAtMs` on\n * `polling`) — the same discipline the sibling `PaginatorState` uses.\n *\n * - `tick` — count of ticks recorded so far (success or failure). Doubles\n * as an observability cursor and a stable per-tick discriminant.\n * - `lastResult` — the most recent successful observation, or `undefined`\n * before the first success. `until` typically reads it (via the\n * consumer's Model), but the knob never interprets it.\n * - `retry` — the `../retry-backoff` counter for consecutive failures.\n * Reset to `initRetry()` on every success. Once a failure has\n * been recorded it is a `TimedRetryState` (it carries the\n * streak's `firstFailureAtMs`, minted from `tickErr`'s `at`);\n * the field is typed at the `RetryState` supertype because a\n * poller that has never failed has no streak, and a slice\n * persisted before the origin existed rehydrates without one.\n * - `dedupe` — the `../idempotency` store backing `dedupeKey`. An empty,\n * unbounded store when `dedupeKey` is unset (never written).\n */\ninterface PollerCore<R> {\n readonly tick: number;\n readonly lastResult?: R;\n readonly retry: RetryState;\n readonly dedupe: IdempotencyStore<true>;\n}\n\n/**\n * The Model field the poller knob owns — its visible slice (the knob principle:\n * managed state lives in the Model, never a closure, so it is durable and\n * replayable). A discriminated union on `phase` so the armed-timer target lives\n * ONLY where it is meaningful and impossible combinations are unrepresentable\n * (pattern 11), the sibling `PaginatorState` idiom:\n *\n * - `polling` — ticks are scheduled. Carries `nextAtMs`, the ABSOLUTE target\n * the interval/backoff timer is armed against (persisted so a\n * resumed poller re-arms the same moment), or `null` before the\n * first `start` primes it.\n * - `done` — `until` held; the poll is finished. Carries NO `nextAtMs` — a\n * finished poll can never sit on an armed target.\n * - `gave_up` — backoff exhausted `maxAttempts`. Carries NO `nextAtMs` either.\n *\n * Because only `polling` carries `nextAtMs`, `subs` guards on `phase` alone to\n * decide whether a timer is desired — a `done`/`gave_up` state with a live target\n * (the old flat record's hazard) simply cannot be built.\n */\nexport type PollerState<R> =\n | (PollerCore<R> & {\n readonly phase: \"polling\";\n readonly nextAtMs: number | null;\n })\n | (PollerCore<R> & { readonly phase: \"done\" })\n | (PollerCore<R> & { readonly phase: \"gave_up\" });\n\n/**\n * The three arms, named — so a verb can DECLARE the phases it can actually\n * reach instead of the whole union.\n *\n * A verb whose return type is the full `PollerState<R>` tells a caller nothing\n * about where the poller went, and a caller that needs to know (a chart\n * declaring an edge's fan-out; an exhaustive `switch`) has to re-derive it by\n * reading the verb's body. Every one of these narrowings is already true of the\n * implementation — `start` only ever builds a `polling` arm, `tickResult` never\n * builds `gave_up`, `tickErr` never builds `done` — so naming them costs\n * nothing at runtime and moves a fact out of the source and into the signature.\n */\nexport type PollerPolling<R> = Extract<PollerState<R>, { phase: \"polling\" }>;\nexport type PollerDone<R> = Extract<PollerState<R>, { phase: \"done\" }>;\nexport type PollerGaveUp<R> = Extract<PollerState<R>, { phase: \"gave_up\" }>;\n\n/**\n * The poller Sub type — a `../deadline` Sub under a fixed id family. One id per\n * knob instance keeps the reconcile pass from churning the timer: the same id\n * across transitions means \"same armed deadline\", even as `nextAtMs` advances.\n */\nexport type PollerSub = DeadlineSub;\n\n/**\n * The Sub id the poller arms its tick deadline under, keyed on the absolute\n * target. The id ROTATES per target because a one-shot deadline timer is\n * exhausted once it fires and the substrate reconciles subs by `id` alone: a\n * fixed id would leave the fired timer registered and never re-arm, so the\n * cadence would die after exactly one tick. Encoding `atMs` in the id makes each\n * cadence/backoff target a distinct identity — the reconcile pass retires the\n * spent timer and arms the next — while a resumed poller re-arms the SAME id\n * from its persisted `nextAtMs` (eviction is idempotent).\n *\n * A single poller machine arms exactly one tick timer at a time; a consumer\n * running several pollers on one machine namespaces them by composing the knob\n * under distinct slices (a future multi-poller config would thread a prefix).\n */\nfunction pollerSubId(atMs: number): string {\n return `poller:tick:${atMs}`;\n}\n\n/**\n * The knob handle returned by `createPoller`. Spread its hooks into your\n * machine: `init()` seeds the slice, `start(...)` arms the first tick,\n * `tickResult` / `tickErr` are the two update verbs, and `subs(...)` returns the\n * timer Sub while the poller is live.\n *\n * Pure-ops + subs module: there is no `handlers(ports)` cell. The poller's I/O\n * is the consumer's own `onTick` Cmd run by the consumer's `interpret` — the\n * knob never owns a port. (Compare `../deadline`, which ships a `subscribe`\n * cell; the poller reuses `../deadline`'s, so it has nothing of its own to\n * splice into `interpret`.)\n */\nexport interface Poller<\n // State is a phantom-style parameter — present so consumers can spell\n // `Poller<MyState, MyResult>` matching `createPoller<State, R>`. It is\n // consumed by the config's `until` predicate, not by any handle method, so\n // it does not appear in the interface body. Same discipline as\n // `BoundMachine`'s `Ctx`.\n // biome-ignore lint/correctness/noUnusedVariables: phantom type parameter (see above)\n State,\n R,\n> {\n /** Seed the Model slice. Idle until `start(...)` arms the first tick. */\n init(): PollerPolling<R>;\n /**\n * Arm the first tick. `at` is the current clock (carried in from the Msg /\n * runtime that kicks the poller off — never read inside the verb). Returns\n * the slice with the first deadline target set to `at + everyMs` and emits NO\n * Cmd. The cadence has ONE source — the deadline Sub — so `start` does not\n * perform the first observation itself (an immediate `onTick` here would race\n * the timer and poll as fast as the source responds). The first observation\n * fires when the deadline crosses `at + everyMs` and the consumer routes\n * `deadline_exceeded` → `tick`.\n */\n start(\n state: PollerState<R>,\n at: number,\n ): readonly [PollerPolling<R>, readonly Cmd[]];\n /**\n * Perform one observation — the cadence verb. The consumer routes the\n * deadline Sub's `deadline_exceeded` Msg here; `tick` emits the single\n * `config.onTick()` Cmd that goes and reads the source. This is the ONLY\n * next-tick mechanism: the timer fires, the consumer calls `tick`, the fetch\n * runs, and the observation comes back through `tickResult` / `tickErr`\n * (which re-arm the next deadline). A no-op on a finished poller — a stale\n * deadline fire racing a terminal transition the reconcile pass has not yet\n * retired emits nothing.\n *\n * Takes no `at`: `tick` does not touch the schedule (the just-fired deadline\n * is exhausted; the next target is set when the result lands), so it needs no\n * clock — keeping it trivially pure.\n */\n tick(state: PollerState<R>): readonly [PollerState<R>, readonly Cmd[]];\n /**\n * Record a successful tick observation at clock `at`.\n *\n * - Resets the retry counter (a success clears consecutive-failure state).\n * - Stores `result` as `lastResult` and bumps `tick`.\n * - Re-arms the next tick at the absolute target `at + everyMs` (the steady\n * cadence). It emits NO cadence `onTick` — the next observation fires from\n * the deadline Sub via `tick`, never immediately from a result.\n * - Dedupe (opt-in, orthogonal to cadence): when `dedupeKey` is set, a FRESH\n * observation emits one `onTick` FOLLOW-UP (the consumer's \"run the\n * downstream side effect once per distinct observation\" hook) and a\n * DUPLICATE key records the sighting but suppresses the follow-up. With no\n * `dedupeKey`, `tickResult` emits nothing — the cadence `tick` is the only\n * `onTick`, so an unconfigured poller never double-fetches.\n * - UNLESS `untilHeld` is `true` (the consumer evaluated `config.until`\n * against the post-result Model) — then the poller enters `\"done\"`,\n * disarms (`nextAtMs: null`), and emits no Cmd, dedupe or otherwise.\n *\n * `untilHeld` is passed in rather than computed here because `until` reads\n * the consumer's WHOLE Model, which this verb does not hold — the consumer\n * evaluates it at the call site after folding `result` into its Model and\n * passes the boolean. (`subs` re-checks `phase` to decide arming, so a\n * mis-passed `untilHeld` cannot leave a \"done\" poller arming timers.)\n */\n tickResult(\n state: PollerState<R>,\n result: R,\n at: number,\n untilHeld: boolean,\n ): readonly [PollerPolling<R> | PollerDone<R>, readonly Cmd[]];\n /**\n * Record a FAILED tick at clock `at`. Backs off instead of holding the\n * steady cadence:\n *\n * - Advances the retry counter (`recordFailure`).\n * - If `shouldRetry` still permits another attempt, arms the next tick at\n * `at + nextDelayMs(retry, policy)` (the backoff curve) and emits NO Cmd —\n * the retry observation fires when that backoff deadline crosses and the\n * consumer routes `deadline_exceeded` → `tick`. The timer is the single\n * next-tick mechanism, on the backoff curve here instead of `everyMs`.\n * - If backoff is exhausted (`!shouldRetry`), enters `\"gave_up\"`, disarms\n * (`nextAtMs: null`), and emits no Cmd.\n *\n * `at` is both the backoff anchor AND the streak clock: it is passed to\n * `recordFailure` (starting / preserving `firstFailureAtMs`) and to\n * `shouldRetry`, so a `DurationRetryPolicy` on `config.retry` is honoured\n * with no extra wiring. Under a duration bound the poller keeps retrying\n * however many attempts the outage takes, and stops at the declared\n * wall-clock budget measured from the streak's FIRST failure.\n *\n * The backoff jitter uses the `rng` injected ONCE at `createPoller` (default\n * `Math.random` at the effect boundary, a fixed value in tests); the verb\n * body never names the global RNG, so a poller built with a fixed `rng`\n * replays a failure-and-backoff run bit-for-bit.\n */\n tickErr(\n state: PollerState<R>,\n error: unknown,\n at: number,\n ): readonly [PollerPolling<R> | PollerGaveUp<R>, readonly Cmd[]];\n /**\n * The pre-wired subscriptions cell. Returns the single tick-deadline Sub\n * while the poller is `\"polling\"` with an armed `nextAtMs`; returns `[]`\n * once `\"done\"` / `\"gave_up\"` (the timer disarms) or before `start`. Assign\n * directly: `subscriptions: (s) => poll.subs(s.poll)`.\n *\n * The `DeadlineSub` it returns is consumed by `../deadline`'s\n * `subscribeDeadline` cell, which the consumer wires into `subscribe` —\n * the poller does not redraw the timer lifecycle.\n */\n subs(state: PollerState<R>): readonly Sub[];\n}\n\n/**\n * Build a poller knob from `config`. Returns the hook bag to spread into a\n * machine (see the module doc-comment for the wiring). Pure factory — no clock\n * read, no timer; everything happens when the consumer calls the returned\n * verbs / `subs` with an injected `at`.\n *\n * `rng` is injected ONCE here (default `Math.random` at the effect boundary, a\n * fixed value in tests) and threaded internally into the backoff jitter. No\n * verb body ever names the global RNG, so a poller built with a fixed `rng`\n * replays a failure-and-backoff run bit-for-bit (invariant 2: the verbs are\n * pure given the injected `rng`).\n */\nexport function createPoller<State, R>(\n config: PollerConfig<State, R>,\n rng: () => number = Math.random,\n): Poller<State, R> {\n // Re-affirm the `[0, 1)` contract at the seam where the injected generator\n // enters the backoff math (brand from `../retry-backoff`). The factory's\n // public param stays a plain `() => number` so transitive callers are\n // unchanged; `asRng` is the single point where it's branded for `nextDelayMs`.\n const rngBranded = asRng(rng);\n const policy: AnyRetryPolicy = config.retry ?? defaultRetryPolicy;\n\n // The dedupe store seam, bound once. The poller delegates its dedupe-window\n // reads (`memory.isSeen`) and the per-sighting touch (`memory.remember`) to\n // the verbs, so the `IdempotencyStore` shape stays insulated.\n const memory = idempotencyMemory<true>();\n\n function init(): PollerPolling<R> {\n return {\n phase: \"polling\",\n tick: 0,\n retry: initRetry(),\n nextAtMs: null,\n // The dedupe store is created regardless; it is only WRITTEN when\n // `dedupeKey` is set, so an unconfigured poller carries an inert empty\n // store (no per-op cost, JSON-serializable, durable like the rest of\n // the slice).\n dedupe: memory.init({ ttlMs: config.dedupeTtlMs }),\n };\n }\n\n function start(\n state: PollerState<R>,\n at: number,\n ): readonly [PollerPolling<R>, readonly Cmd[]] {\n // Arm the first deadline at `at + everyMs` and emit NOTHING. The cadence has\n // ONE source — the `everyMs` deadline Sub. `start` does not perform the\n // first observation itself: that would be a second, immediate next-tick\n // mechanism racing the timer (it would fire as fast as the source responds,\n // never honoring `everyMs`). The first observation fires when the deadline\n // crosses `at + everyMs` and the consumer routes `deadline_exceeded` → the\n // `tick` verb (which emits `onTick`). No drift, no double-fire. Build the\n // `polling` arm explicitly so a (re)start from a terminal state adopts a live\n // target rather than spreading a terminal arm that never had one.\n return [\n {\n phase: \"polling\",\n tick: state.tick,\n lastResult: state.lastResult,\n retry: state.retry,\n nextAtMs: at + config.everyMs,\n dedupe: state.dedupe,\n },\n [],\n ];\n }\n\n function tick(\n state: PollerState<R>,\n ): readonly [PollerState<R>, readonly Cmd[]] {\n // The deadline fired. Perform the observation — this is the ONLY place the\n // `onTick` Cmd is emitted, so the timer is the single next-tick mechanism.\n // No schedule change here: the just-fired deadline is exhausted, and the\n // NEXT target is re-armed when the observation lands (`tickResult` /\n // `tickErr`). A no-op on a finished poller (a stale fire racing a terminal\n // transition the reconcile pass has not yet retired).\n if (state.phase !== \"polling\") return [state, []];\n return [state, [config.onTick()]];\n }\n\n function tickResult(\n state: PollerState<R>,\n result: R,\n at: number,\n untilHeld: boolean,\n ): readonly [PollerPolling<R> | PollerDone<R>, readonly Cmd[]] {\n // A success always clears consecutive-failure state and records the datum.\n // The next arm is built explicitly (not spread from `state`) so `done`\n // carries no `nextAtMs` — a finished poll can never sit on an armed target.\n const tick = state.tick + 1;\n\n // Stop condition first: if the consumer's `until` held against the\n // post-result Model, we are DONE — disarm and emit nothing, regardless of\n // dedupe (the final observation is still recorded as `lastResult`).\n if (untilHeld) {\n return [\n {\n phase: \"done\",\n tick,\n lastResult: result,\n retry: initRetry(),\n dedupe: state.dedupe,\n },\n [],\n ];\n }\n\n // Re-arm the steady cadence: the next observation fires when THIS deadline\n // (`at + everyMs`) crosses and the consumer routes `deadline_exceeded` →\n // `tick`. `tickResult` itself never emits the cadence `onTick` — the timer\n // is the single next-tick mechanism (recording a result is not a reason to\n // immediately re-fetch; that would poll as fast as the source responds).\n const next: PollerPolling<R> = {\n phase: \"polling\",\n tick,\n lastResult: result,\n retry: initRetry(),\n nextAtMs: at + config.everyMs,\n dedupe: state.dedupe,\n };\n\n // Dedupe is an OPT-IN follow-up, orthogonal to the cadence above. When\n // `dedupeKey` is set, a FRESH observation emits one `onTick` follow-up (the\n // consumer's \"run the downstream side effect once per distinct observation\"\n // hook); a DUPLICATE key records the sighting but suppresses that follow-up.\n // With no `dedupeKey` there is no follow-up at all — the cadence `tick` is\n // the only `onTick`, so an unconfigured poller never double-fetches.\n if (!config.dedupeKey) return [next, []];\n\n const key = config.dedupeKey(result);\n const suppress = memory.isSeen(next.dedupe, key, at);\n // `remember` refreshes the key's TTL clock on every sighting (the\n // idempotency \"touch\" semantics), so a steadily-repeating observation never\n // ages out mid-poll. The stored value is the unit `true` — the poller only\n // needs presence, not a payload.\n const deduped: PollerPolling<R> = {\n ...next,\n dedupe: memory.remember(next.dedupe, key, true, at),\n };\n return [deduped, suppress ? [] : [config.onTick()]];\n }\n\n function tickErr(\n state: PollerState<R>,\n error: unknown,\n at: number,\n ): readonly [PollerPolling<R> | PollerGaveUp<R>, readonly Cmd[]] {\n // `at` is the failure's OBSERVATION instant, already carried in from the\n // Msg — so it doubles as the streak clock a duration bound is measured\n // against. The first failure of a streak sets `firstFailureAtMs`; every\n // later one preserves it, and a success resets to `initRetry()` (dropping\n // the origin with it). Nothing here reads a clock: `at` is Msg data,\n // exactly as `rng` is factory-injected data (invariant 2).\n const retry = recordFailure(state.retry, error, at);\n const tick = state.tick + 1;\n\n // Backoff exhausted → give up: emit nothing. `shouldRetry` reads the\n // just-advanced `retry`, so under a count bound the (maxAttempts)-th\n // failure is the one that stops the poller, and under a duration bound the\n // first failure observed at or past `firstFailureAtMs + maxElapsedMs` is.\n // The `gave_up` arm carries no `nextAtMs` — there is no next attempt to arm.\n if (!shouldRetry(retry, policy, at)) {\n return [\n {\n phase: \"gave_up\",\n tick,\n lastResult: state.lastResult,\n retry,\n dedupe: state.dedupe,\n },\n [],\n ];\n }\n\n // Back off: arm the next attempt at `at + backoff(retry)` rather than the\n // steady cadence, and emit NOTHING. The retry observation fires when this\n // backoff deadline crosses and the consumer routes `deadline_exceeded` →\n // `tick` — the timer is the single next-tick mechanism, on the backoff\n // curve here instead of `everyMs`. `rng` is the value injected once at\n // `createPoller`; the verb body never names the global RNG, so a fixed\n // `rng` makes the backoff target replay bit-for-bit.\n const delayMs = nextDelayMs(retry, policy, rngBranded);\n return [\n {\n phase: \"polling\",\n tick,\n lastResult: state.lastResult,\n retry,\n nextAtMs: at + delayMs,\n dedupe: state.dedupe,\n },\n [],\n ];\n }\n\n function subs(state: PollerState<R>): readonly Sub[] {\n // Arm the tick deadline only while live. `phase` is the authority (so a\n // mis-passed `untilHeld` into `tickResult` cannot resurrect a finished\n // poller); `nextAtMs` is the absolute target `../deadline` translates to a\n // remaining delay at subscribe time — a resumed poller re-arms the exact\n // persisted moment.\n if (state.phase !== \"polling\" || state.nextAtMs === null) return [];\n // The Sub id ROTATES with the target. A one-shot deadline timer is\n // exhausted once it fires, and the substrate's reconcile pass keys identity\n // by `id` alone — a fixed id would leave the fired timer in the registry and\n // NEVER re-arm for the next cadence (cadence dies after one tick). Folding\n // `nextAtMs` into the id makes each successive target a distinct identity:\n // the reconcile pass retires the spent timer (old id absent from the desired\n // set) and arms the next one (new id). A resumed poller re-arms the SAME id\n // from its persisted `nextAtMs`, so eviction is idempotent.\n return [deadlineSub(pollerSubId(state.nextAtMs), state.nextAtMs)];\n }\n\n return { init, start, tick, tickResult, tickErr, subs };\n}\n"]}