@demlik/tea 0.12.0 → 0.13.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 (152) hide show
  1. package/dist/agent/index.d.ts +6 -5
  2. package/dist/agent/index.js +11 -11
  3. package/dist/authed-call/index.d.ts +3 -2
  4. package/dist/authed-call/index.js +7 -7
  5. package/dist/await-terminal/index.d.ts +3 -2
  6. package/dist/await-terminal/index.js +2 -2
  7. package/dist/batch-window/index.d.ts +4 -3
  8. package/dist/batch-window/index.js +3 -3
  9. package/dist/cache/index.d.ts +3 -2
  10. package/dist/cache/index.js +4 -4
  11. package/dist/chart/index.d.ts +4 -3
  12. package/dist/chart/index.js +2 -2
  13. package/dist/chart/inspect/index.d.ts +5 -4
  14. package/dist/chart/inspect/index.js +3 -3
  15. package/dist/chart/inspect/react.d.ts +6 -5
  16. package/dist/chart/inspect/react.js +5 -5
  17. package/dist/chart/lane/index.d.ts +7 -6
  18. package/dist/chart/lane/index.js +6 -6
  19. package/dist/chart/lane/react.d.ts +6 -5
  20. package/dist/chart/lane/react.js +7 -7
  21. package/dist/chart/report/index.d.ts +5 -4
  22. package/dist/chart/report/index.js +4 -4
  23. package/dist/{chunk-ZF6WDK2D.js → chunk-3I52QZZ5.js} +7 -7
  24. package/dist/{chunk-ZF6WDK2D.js.map → chunk-3I52QZZ5.js.map} +1 -1
  25. package/dist/{chunk-ZD6PNZBK.js → chunk-3ZAFFRRB.js} +3 -3
  26. package/dist/{chunk-ZD6PNZBK.js.map → chunk-3ZAFFRRB.js.map} +1 -1
  27. package/dist/{chunk-4KTWLPL5.js → chunk-4EQZ76JC.js} +3 -3
  28. package/dist/{chunk-4KTWLPL5.js.map → chunk-4EQZ76JC.js.map} +1 -1
  29. package/dist/{chunk-EUF7XOEE.js → chunk-4KLG43GY.js} +6 -6
  30. package/dist/{chunk-EUF7XOEE.js.map → chunk-4KLG43GY.js.map} +1 -1
  31. package/dist/{chunk-JER5O7PQ.js → chunk-7FRZ2CT2.js} +15 -7
  32. package/dist/chunk-7FRZ2CT2.js.map +1 -0
  33. package/dist/{chunk-ZCL7SCBK.js → chunk-CIQG5L42.js} +5 -5
  34. package/dist/{chunk-ZCL7SCBK.js.map → chunk-CIQG5L42.js.map} +1 -1
  35. package/dist/{chunk-TP5UPGLY.js → chunk-CLM6ERNO.js} +3 -3
  36. package/dist/{chunk-TP5UPGLY.js.map → chunk-CLM6ERNO.js.map} +1 -1
  37. package/dist/{chunk-ND3DH6KM.js → chunk-COS5MSTO.js} +3 -3
  38. package/dist/{chunk-ND3DH6KM.js.map → chunk-COS5MSTO.js.map} +1 -1
  39. package/dist/{chunk-JP5D6GSL.js → chunk-CRPZJVD2.js} +4 -4
  40. package/dist/{chunk-JP5D6GSL.js.map → chunk-CRPZJVD2.js.map} +1 -1
  41. package/dist/{chunk-SUST46KO.js → chunk-D62FDQKE.js} +3 -3
  42. package/dist/{chunk-SUST46KO.js.map → chunk-D62FDQKE.js.map} +1 -1
  43. package/dist/{chunk-JGVL3WPL.js → chunk-DGE4PV4A.js} +3 -3
  44. package/dist/{chunk-JGVL3WPL.js.map → chunk-DGE4PV4A.js.map} +1 -1
  45. package/dist/{chunk-WLGO64WC.js → chunk-DMYGQB4O.js} +4 -4
  46. package/dist/{chunk-WLGO64WC.js.map → chunk-DMYGQB4O.js.map} +1 -1
  47. package/dist/{chunk-A56OR5RF.js → chunk-L57ZOT5K.js} +3 -3
  48. package/dist/{chunk-A56OR5RF.js.map → chunk-L57ZOT5K.js.map} +1 -1
  49. package/dist/{chunk-5KJKJOFC.js → chunk-NR7BU22N.js} +3 -3
  50. package/dist/{chunk-5KJKJOFC.js.map → chunk-NR7BU22N.js.map} +1 -1
  51. package/dist/{chunk-7V4EVLZA.js → chunk-O4MVQTEJ.js} +4 -4
  52. package/dist/{chunk-7V4EVLZA.js.map → chunk-O4MVQTEJ.js.map} +1 -1
  53. package/dist/{chunk-B5VXOFMZ.js → chunk-OOYD44JI.js} +54 -3
  54. package/dist/chunk-OOYD44JI.js.map +1 -0
  55. package/dist/{chunk-7SVIRMC6.js → chunk-RH5EFRVJ.js} +3 -3
  56. package/dist/{chunk-7SVIRMC6.js.map → chunk-RH5EFRVJ.js.map} +1 -1
  57. package/dist/{chunk-3JU6657S.js → chunk-VYFAZN3F.js} +3 -3
  58. package/dist/{chunk-3JU6657S.js.map → chunk-VYFAZN3F.js.map} +1 -1
  59. package/dist/chunk-XGELA764.js +163 -0
  60. package/dist/chunk-XGELA764.js.map +1 -0
  61. package/dist/{chunk-I5OV65MS.js → chunk-YUXP55T2.js} +3 -3
  62. package/dist/{chunk-I5OV65MS.js.map → chunk-YUXP55T2.js.map} +1 -1
  63. package/dist/{chunk-E6GEDSF4.js → chunk-YWIKDKHY.js} +3 -3
  64. package/dist/{chunk-E6GEDSF4.js.map → chunk-YWIKDKHY.js.map} +1 -1
  65. package/dist/{chunk-5EHLEUTL.js → chunk-ZSUUOEPL.js} +51 -6
  66. package/dist/chunk-ZSUUOEPL.js.map +1 -0
  67. package/dist/{core-Bjc0bS8j.d.ts → core-Co0LWkIA.d.ts} +190 -3
  68. package/dist/deadline/index.d.ts +3 -2
  69. package/dist/deadline/index.js +3 -3
  70. package/dist/do/index.d.ts +12 -8
  71. package/dist/do/index.js +11 -11
  72. package/dist/do/index.js.map +1 -1
  73. package/dist/{durable-effects-D8SqU_if.d.ts → durable-effects-C6uwzjb5.d.ts} +1 -1
  74. package/dist/extension/index.d.ts +3 -2
  75. package/dist/extension/index.js +3 -3
  76. package/dist/extension/react.js +3 -3
  77. package/dist/extension/subs/index.d.ts +3 -2
  78. package/dist/fan-out/index.d.ts +3 -2
  79. package/dist/{fold-Blbhru48.d.ts → fold-BJPdjc9w.d.ts} +1 -1
  80. package/dist/{graph-CcjWZVrd.d.ts → graph-B3L5e0N1.d.ts} +1 -1
  81. package/dist/idempotent-intake/index.d.ts +3 -2
  82. package/dist/index.d.ts +14 -6
  83. package/dist/index.js +2 -2
  84. package/dist/journal/index.d.ts +373 -0
  85. package/dist/journal/index.js +3 -0
  86. package/dist/journal/index.js.map +1 -0
  87. package/dist/llm-call/index.d.ts +3 -2
  88. package/dist/llm-call/index.js +8 -8
  89. package/dist/{machine-0OmPvsy_.d.ts → machine-BtFWQmmq.d.ts} +1 -1
  90. package/dist/machine-viz/index.d.ts +2 -1
  91. package/dist/machine-viz/index.js +2 -2
  92. package/dist/mem/index.d.ts +3 -2
  93. package/dist/monitored-run/index.d.ts +4 -3
  94. package/dist/monitored-run/index.js +5 -5
  95. package/dist/node/index.d.ts +31 -3
  96. package/dist/node/index.js +92 -3
  97. package/dist/node/index.js.map +1 -1
  98. package/dist/paginated-walk/index.d.ts +3 -2
  99. package/dist/paginated-walk/index.js +7 -7
  100. package/dist/parity/index.d.ts +3 -2
  101. package/dist/parity/index.js +3 -3
  102. package/dist/pbt/arbitraries/index.d.ts +2 -1
  103. package/dist/pbt/index.d.ts +2 -1
  104. package/dist/pbt/index.js +3 -3
  105. package/dist/pbt/runners/index.d.ts +2 -1
  106. package/dist/pbt/runners/index.js +3 -3
  107. package/dist/poller/index.d.ts +3 -2
  108. package/dist/poller/index.js +3 -3
  109. package/dist/prediction/index.d.ts +2 -1
  110. package/dist/prediction/index.js +2 -2
  111. package/dist/pure/index.d.ts +2 -1
  112. package/dist/pure/index.js +2 -2
  113. package/dist/react/index.d.ts +4 -3
  114. package/dist/react/index.js +2 -2
  115. package/dist/react/index.js.map +1 -1
  116. package/dist/reconciler/index.d.ts +3 -2
  117. package/dist/reconciler/index.js +7 -7
  118. package/dist/recorder/index.d.ts +3 -2
  119. package/dist/{reducer-BqyoVyBG.d.ts → reducer-4GNR6b8B.d.ts} +2 -2
  120. package/dist/resilient-call/index.d.ts +3 -2
  121. package/dist/resilient-call/index.js +6 -6
  122. package/dist/{run-BVeqJDBF.d.ts → run-pAcmBpah.d.ts} +3 -3
  123. package/dist/{runtime-types-Bxc637bp.d.ts → runtime-types-CNLmhWif.d.ts} +17 -2
  124. package/dist/saga/index.d.ts +3 -2
  125. package/dist/{samples-DCt6nF8H.d.ts → samples-B3My5oh2.d.ts} +1 -1
  126. package/dist/snapshot/index.d.ts +2 -1
  127. package/dist/snapshot/index.js +3 -3
  128. package/dist/subs/index.d.ts +4 -3
  129. package/dist/subs/index.js +3 -3
  130. package/dist/testing/index.d.ts +3 -2
  131. package/dist/testing/index.js +2 -2
  132. package/dist/throttled-input/index.d.ts +3 -2
  133. package/dist/throttled-input/index.js +6 -6
  134. package/dist/token-refresh/index.d.ts +2 -1
  135. package/dist/token-refresh/index.js +3 -3
  136. package/dist/trace-replay/index.d.ts +3 -2
  137. package/dist/trace-replay/index.js +3 -3
  138. package/dist/{types-D7u9DkH7.d.ts → types-XS_miRnB.d.ts} +1 -1
  139. package/dist/with-deadline/index.d.ts +2 -1
  140. package/dist/with-deadline/index.js +2 -2
  141. package/dist/with-resilience/index.d.ts +3 -2
  142. package/dist/with-resilience/index.js +6 -6
  143. package/dist/with-telemetry/index.d.ts +2 -1
  144. package/dist/with-telemetry/index.js +2 -2
  145. package/dist/work-queue/adapter.d.ts +3 -2
  146. package/dist/work-queue/index.d.ts +3 -2
  147. package/dist/work-queue/ops.d.ts +3 -2
  148. package/dist/workflow/index.d.ts +4 -3
  149. package/package.json +11 -4
  150. package/dist/chunk-5EHLEUTL.js.map +0 -1
  151. package/dist/chunk-B5VXOFMZ.js.map +0 -1
  152. package/dist/chunk-JER5O7PQ.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/journal/remote.ts","../src/journal/index.ts"],"names":[],"mappings":";;;;;AAuKO,SAAS,mBAAA,GAA2C;AACzD,EAAA,MAAM,UAAU,aAAA,EAAiC;AAEjD,EAAA,MAAM,aAAA,GAAgB,CACpB,KAAA,MACoB;AAAA,IACpB,QAAQ,KAAA,CAAM,MAAA;AAAA,IACd,KAAK,KAAA,CAAM,GAAA;AAAA,IACX,MAAA,EAAQ,MAAM,MAAA,CAAO,MAAA;AAAA,IACrB,MAAA,EAAQ,MAAM,MAAA,CAAO;AAAA,GACvB,CAAA;AAEA,EAAA,OAAO;AAAA,IACL,MAAM,IAAA,CAAK,EAAE,MAAA,EAAQ,SAAQ,EAAG;AAC9B,MAAA,MAAM,OAAoB,EAAC;AAI3B,MAAA,MAAM,OAAA,CAAQ,QAAA,CAAS,MAAA,EAAQ,YAAY;AACzC,QAAA,KAAA,MAAW,UAAU,OAAA,EAAS;AAC5B,UAAA,MAAM,EAAE,GAAA,EAAI,GAAI,MAAM,OAAA,CAAQ,MAAA,CAAO,QAAQ,MAAM,CAAA;AACnD,UAAA,IAAA,CAAK,KAAK,GAAG,CAAA;AAAA,QACf;AAAA,MACF,CAAC,CAAA;AACD,MAAA,OAAO,EAAE,IAAA,EAAK;AAAA,IAChB,CAAA;AAAA,IACA,MAAM,IAAA,CAAK,EAAE,MAAA,EAAQ,OAAM,EAAG;AAC5B,MAAA,MAAM,OAAA,GAAA,CAAW,MAAM,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAA,EACvC,MAAA,CAAO,CAAC,KAAA,KAAU,KAAA,CAAM,GAAA,GAAM,KAAK,CAAA,CACnC,IAAI,aAAa,CAAA;AACpB,MAAA,OAAO,EAAE,OAAA,EAAQ;AAAA,IACnB;AAAA,GACF;AACF;AAoBO,SAAS,iBAAA,GAAiC;AAC/C,EAAA,MAAM,OAAA,uBAAc,GAAA,EAAuB;AAC3C,EAAA,OAAO;AAAA,IACL,MAAM,IAAI,MAAA,EAAQ;AAChB,MAAA,OAAO,OAAA,CAAQ,GAAA,CAAI,MAAM,CAAA,IAAK,CAAA;AAAA,IAChC,CAAA;AAAA,IACA,MAAM,GAAA,CAAI,MAAA,EAAQ,GAAA,EAAK;AACrB,MAAA,OAAA,CAAQ,GAAA,CAAI,QAAQ,GAAG,CAAA;AAAA,IACzB;AAAA,GACF;AACF;AAuFO,SAAS,eACd,OAAA,EACe;AACf,EAAA,MAAM,EAAE,MAAA,EAAQ,MAAA,EAAQ,KAAA,EAAM,GAAI,OAAA;AAClC,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,KAAA,IAAS,aAAA,EAAiB;AAChD,EAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,OAAA,IAAW,iBAAA,EAAkB;AAKrD,EAAA,MAAM,MAAA,uBAAa,GAAA,EAAoB;AAEvC,EAAA,MAAM,SAAA,uBAAgB,GAAA,EAA8B;AAEpD,EAAA,MAAM,QAAA,GAAW,OACf,MAAA,KAC4C;AAC5C,IAAA,MAAM,OAAA,GAAU,MAAM,KAAA,CAAM,IAAA,CAAK,MAAM,CAAA;AACvC,IAAA,OAAO,QAAQ,KAAA,CAAM,MAAA,CAAO,GAAA,CAAI,MAAM,KAAK,CAAC,CAAA;AAAA,EAC9C,CAAA;AAEA,EAAA,OAAO;AAAA,IACL,MAAM,MAAA,CAAO,MAAA,EAAQ,MAAA,EAAQ;AAC3B,MAAA,MAAM,KAAA,CAAM,MAAA,CAAO,MAAA,EAAQ,MAAM,CAAA;AAAA,IACnC,CAAA;AAAA,IACA,MAAM,QAAQ,MAAA,EAAQ;AACpB,MAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,MAAM,CAAA;AAClC,MAAA,OAAO,IAAA,CAAK,IAAI,CAAC,KAAA,MAAW,EAAE,MAAA,EAAQ,MAAA,EAAQ,KAAA,CAAM,MAAA,EAAO,CAAE,CAAA;AAAA,IAC/D,CAAA;AAAA,IACA,MAAM,KAAK,MAAA,EAAQ;AACjB,MAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,MAAM,CAAA;AAClC,MAAA,IAAI,IAAA,CAAK,SAAS,CAAA,EAAG;AACnB,QAAA,MAAM,OAAO,IAAA,CAAK;AAAA,UAChB,KAAA;AAAA,UACA,MAAA;AAAA,UACA,OAAA,EAAS,IAAA,CAAK,GAAA,CAAI,CAAC,KAAA,MAAW,EAAE,MAAA,EAAQ,MAAA,EAAQ,KAAA,CAAM,MAAA,EAAO,CAAE;AAAA,SAChE,CAAA;AACD,QAAA,MAAA,CAAO,GAAA,CAAI,SAAS,MAAA,CAAO,GAAA,CAAI,MAAM,CAAA,IAAK,CAAA,IAAK,KAAK,MAAM,CAAA;AAAA,MAC5D;AAEA,MAAA,MAAM,KAAA,GAAQ,MAAM,OAAA,CAAQ,GAAA,CAAI,MAAM,CAAA;AACtC,MAAA,MAAM,EAAE,OAAA,EAAQ,GAAI,MAAM,MAAA,CAAO,KAAK,EAAE,KAAA,EAAO,MAAA,EAAQ,KAAA,EAAO,CAAA;AAC9D,MAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,OAAA,CAAQ,MAAA,GAAS,CAAC,CAAA;AACvC,MAAA,IAAI,SAAS,MAAA,EAAW;AACtB,QAAA,MAAM,GAAA,GAAM,SAAA,CAAU,GAAA,CAAI,MAAM,KAAK,EAAC;AACtC,QAAA,GAAA,CAAI,IAAA,CAAK,GAAG,OAAO,CAAA;AACnB,QAAA,SAAA,CAAU,GAAA,CAAI,QAAQ,GAAG,CAAA;AACzB,QAAA,MAAM,OAAA,CAAQ,GAAA,CAAI,MAAA,EAAQ,IAAA,CAAK,GAAG,CAAA;AAAA,MACpC;AAAA,IACF,CAAA;AAAA,IACA,IAAI,MAAA,EAAQ;AACV,MAAA,OAAA,CAAQ,UAAU,GAAA,CAAI,MAAM,CAAA,IAAK,IAAI,KAAA,EAAM;AAAA,IAC7C;AAAA,GACF;AACF;;;AC7QA,IAAM,YAAN,MAAgB;AAAA,EACN,IAAA,GAAyB,QAAQ,OAAA,EAAQ;AAAA,EAEjD,IAAO,EAAA,EAAkC;AACvC,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,IAAA,CAAK,IAAA,CAAK,IAAI,EAAE,CAAA;AACpC,IAAA,IAAA,CAAK,OAAO,MAAA,CAAO,IAAA;AAAA,MACjB,MAAM,MAAA;AAAA,MACN,MAAM;AAAA,KACR;AACA,IAAA,OAAO,MAAA;AAAA,EACT;AACF,CAAA;AASO,SAAS,YAAe,OAAA,EAAwC;AACrE,EAAA,MAAM,OAAA,uBAAc,GAAA,EAAuB;AAY3C,EAAA,MAAM,WAAA,GAAc,IAAI,iBAAA,EAAuC;AAC/D,EAAA,MAAM,SAAA,uBAAgB,GAAA,EAAsC;AAE5D,EAAA,MAAM,QAAA,GAAW,CAAC,MAAA,KAA8B;AAC9C,IAAA,IAAI,KAAA,GAAQ,OAAA,CAAQ,GAAA,CAAI,MAAM,CAAA;AAC9B,IAAA,IAAI,CAAC,KAAA,EAAO;AACV,MAAA,KAAA,GAAQ,IAAI,SAAA,EAAU;AACtB,MAAA,OAAA,CAAQ,GAAA,CAAI,QAAQ,KAAK,CAAA;AAAA,IAC3B;AACA,IAAA,OAAO,KAAA;AAAA,EACT,CAAA;AAEA,EAAA,MAAM,SAAA,GAAY,CAAI,MAAA,EAAgB,EAAA,KAAqC;AACzE,IAAA,MAAM,IAAA,GAAO,YAAY,QAAA,EAAS;AAGlC,IAAA,IAAI,IAAA,EAAM,GAAA,CAAI,MAAM,CAAA,SAAU,EAAA,EAAG;AACjC,IAAA,OAAO,QAAA,CAAS,MAAM,CAAA,CAAE,GAAA;AAAA,MAAI,MAC1B,OAAA,CAAQ,IAAA,CAAK,MAAA,EAAQ,MAAM;AAGzB,QAAA,MAAM,SAAS,IAAI,GAAA,CAAI,IAAI,CAAA,CAAE,IAAI,MAAM,CAAA;AACvC,QAAA,OAAO,WAAA,CAAY,GAAA,CAAI,MAAA,EAAQ,EAAE,CAAA;AAAA,MACnC,CAAC;AAAA,KACH;AAAA,EACF,CAAA;AAEA,EAAA,OAAO;AAAA,IACL,MAAA,CAAO,QAAQ,MAAA,EAAQ;AACrB,MAAA,OAAO,SAAA,CAAU,QAAQ,YAAY;AACnC,QAAA,MAAM,KAAA,GAAQ,MAAM,OAAA,CAAQ,MAAA,CAAO,QAAQ,MAAM,CAAA;AACjD,QAAA,KAAA,MAAW,QAAA,IAAY,SAAA,EAAW,QAAA,CAAS,KAAK,CAAA;AAChD,QAAA,OAAO,EAAE,GAAA,EAAK,KAAA,CAAM,GAAA,EAAI;AAAA,MAC1B,CAAC,CAAA;AAAA,IACH,CAAA;AAAA,IACA,KAAK,MAAA,EAAQ;AACX,MAAA,OAAO,OAAA,CAAQ,OAAA,CAAQ,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAAA,IAC7C,CAAA;AAAA,IACA,QAAQ,QAAA,EAAU;AAChB,MAAA,SAAA,CAAU,IAAI,QAAQ,CAAA;AACtB,MAAA,OAAO,MAAM;AACX,QAAA,SAAA,CAAU,OAAO,QAAQ,CAAA;AAAA,MAC3B,CAAA;AAAA,IACF,CAAA;AAAA,IACA,QAAA,CAAS,QAAQ,EAAA,EAAI;AACnB,MAAA,OAAO,SAAA,CAAU,QAAQ,EAAE,CAAA;AAAA,IAC7B;AAAA,GACF;AACF;AAUO,SAAS,aAAA,GAA+B;AAC7C,EAAA,MAAM,OAAA,uBAAc,GAAA,EAA+B;AAEnD,EAAA,OAAO,WAAA,CAAe;AAAA,IACpB,MAAM,KAAK,MAAA,EAAQ;AACjB,MAAA,OAAA,CAAQ,QAAQ,GAAA,CAAI,MAAM,CAAA,IAAK,IAAI,KAAA,EAAM;AAAA,IAC3C,CAAA;AAAA,IACA,MAAM,MAAA,CAAO,MAAA,EAAQ,MAAA,EAAQ;AAC3B,MAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,GAAA,CAAI,MAAM,KAAK,EAAC;AACpC,MAAA,MAAM,QAAyB,EAAE,MAAA,EAAQ,KAAK,GAAA,CAAI,MAAA,GAAS,GAAG,MAAA,EAAO;AACrE,MAAA,GAAA,CAAI,KAAK,KAAK,CAAA;AACd,MAAA,OAAA,CAAQ,GAAA,CAAI,QAAQ,GAAG,CAAA;AACvB,MAAA,OAAO,KAAA;AAAA,IACT,CAAA;AAAA,IACA,MAAM,IAAA,CAAK,OAAA,EAAS,EAAA,EAAI;AACtB,MAAA,OAAO,EAAA,EAAG;AAAA,IACZ;AAAA,GACD,CAAA;AACH","file":"chunk-XGELA764.js","sourcesContent":["/**\n * journal sync — a remote journal and per-remote cursors, so two local journals\n * meet in one order.\n *\n * `Journal<R>` (`./index`) is the LOCAL append-only log a single machine replays\n * from. This is the second half: a **remote** journal that many local journals\n * push their appends into and pull everyone else's out of, with the remote\n * assigning the one `seq` that counts. Records are append-only, so there is\n * nothing to merge — the fold over the remote's order *is* the merge. This is\n * the network-in-the-middle twin of the append-then-fold shape already proven\n * in-repo by `src/do/event-sourced-store.ts` (append each record to a\n * monotonically-keyed log; rebuild state by folding the log via `replay`).\n *\n * The three moving parts:\n * - `RemoteJournal<R>` — the append-only remote. `push` sends the records a\n * client has not yet had acked and gets back the `seq` the remote assigned\n * each; `pull` reads everyone's records after a given `seq`. One monotonic\n * `seq` per stream, assigned by the remote, is the whole ordering contract.\n * - a per-`(client, stream)` **cursor** (`CursorStore`) kept locally — the\n * last remote `seq` a client has pulled — so `push`/`pull` resume\n * incrementally instead of re-sending or re-reading the whole stream.\n * - `SyncClient<R>` — a local `Journal<R>` joined to a `RemoteJournal<R>`\n * through that cursor and a stable author identity. A local `append` is\n * PROVISIONAL (it has a local order but no remote `seq`) until `sync` pushes\n * it and pulls it back under the `seq` the remote assigned.\n *\n * Domain-blind and auth-blind: the remote takes an opaque `token` and an opaque\n * `stream` key and does nothing with the token — who-may-push-to-which-stream is\n * the host's concern, not this primitive's (the same way the local `Journal` is\n * blind to what a record means). Interface shape follows Effect\n * `unstable/eventlog` `EventJournal` (`writeFromRemote`, per-remote cursors, a\n * `changes` stream) as prior art, not a dependency.\n */\n\nimport { type JournalEntry, memoryJournal } from \"./index\";\n\n/**\n * A remote-assigned order key. Like a local `Seq` it starts at 1 and increments\n * by one per record within a stream, but it is assigned by the REMOTE — it is\n * the authoritative order every client folds over, not any one client's local\n * append order.\n */\nexport type RemoteSeq = number;\n\n/**\n * A record paired with the stable identity of whoever authored it. Author\n * identity is the one fact a team-mode record needs that a solo record does not:\n * once records from many clients interleave in the remote's order, the fold\n * still has to know who wrote each. It is opaque — a string the host assigns\n * meaning to (a device id, a user handle) — never interpreted here.\n */\nexport interface AuthoredRecord<R> {\n /** The stable identity of the client that authored `record`. */\n readonly author: string;\n /** The domain record itself; this primitive knows nothing of its shape. */\n readonly record: R;\n}\n\n/**\n * One record as it lives on the remote: the `seq` the remote assigned, the\n * `author` that wrote it, the `stream` it belongs to, and the record. This is\n * the entry every client pulls and folds; two clients that pull the same stream\n * see identical `RemoteEntry`s in identical `seq` order, which is what lets\n * their folds agree.\n */\nexport interface RemoteEntry<R> {\n /** The stream this entry belongs to. */\n readonly stream: string;\n /** The remote-assigned order key — 1-based, gap-free, monotonic per stream. */\n readonly seq: RemoteSeq;\n /** The stable identity of the client that authored the record. */\n readonly author: string;\n /** The domain record. */\n readonly record: R;\n}\n\n/** A `push` — the records a client wants acked, presented under its token. */\nexport interface PushRequest<R> {\n /**\n * An opaque credential the host's remote may check. This primitive does\n * nothing with it: authorization (who may push to which stream) is the host's\n * concern, deliberately out of scope here.\n */\n readonly token: unknown;\n /** The opaque stream key to append to. */\n readonly stream: string;\n /**\n * The records to append, in the client's local order. Normally these are the\n * client's own appends made since its last successful push — the incremental\n * tail, not the whole stream.\n */\n readonly records: ReadonlyArray<AuthoredRecord<R>>;\n}\n\n/** The remote's answer to a `push`: the `seq` it assigned each record, in order. */\nexport interface PushResult {\n /**\n * The remote `seq` assigned to each record of the request, in the same order.\n * `seqs[i]` is the seq of `records[i]`. A client does not need to fold these —\n * it learns the full ordered stream (its own records included) through `pull`,\n * which is the single reader of converged state.\n */\n readonly seqs: ReadonlyArray<RemoteSeq>;\n}\n\n/** A `pull` — read everyone's records after a `seq` the client already has. */\nexport interface PullRequest {\n /** The opaque credential; ignored here, see {@link PushRequest.token}. */\n readonly token: unknown;\n /** The opaque stream key to read. */\n readonly stream: string;\n /**\n * Return only entries with `seq` strictly greater than this. A client passes\n * its cursor (the last `seq` it pulled), so it reads only what is new — never\n * re-reading the whole stream. `0` (the cursor's initial value) reads from the\n * start.\n */\n readonly since: RemoteSeq;\n}\n\n/** The remote's answer to a `pull`: the new entries, in `seq` order. */\nexport interface PullResult<R> {\n /** Every entry with `seq > since`, in ascending `seq` order. */\n readonly entries: ReadonlyArray<RemoteEntry<R>>;\n}\n\n/**\n * An append-only remote journal: many clients push into it, all pull from it,\n * and it assigns the one monotonic `seq` per stream that every client's fold\n * agrees over. It is the network-in-the-middle: a real one is a server; this\n * interface is what a client talks to, host- and transport-blind.\n *\n * The remote never merges and never resolves conflicts — records are\n * append-only, so its only job is to assign an honest total order. That order\n * is enough: two clients that both push and both pull fold the same sequence and\n * reach the same state (the append-then-fold result proven in phoenix spike\n * #6673, reused here rather than reinvented).\n */\nexport interface RemoteJournal<R> {\n /**\n * Append `records` to `stream` durably and return the `seq` assigned to each,\n * in order. Concurrent pushes to one stream are serialised into a single total\n * order — the seq assignment is the remote's whole ordering guarantee.\n */\n push(request: PushRequest<R>): Promise<PushResult>;\n /**\n * Read every entry of `stream` with `seq` strictly greater than\n * `request.since`, in ascending `seq` order. Empty when the client is already\n * caught up.\n */\n pull(request: PullRequest): Promise<PullResult<R>>;\n}\n\n/**\n * An in-memory `RemoteJournal<R>` — the standard test/reference substrate,\n * mirroring `memoryStore` (`@demlik/tea/mem`) and `memoryJournal` (`./index`).\n *\n * It is built ON `memoryJournal`: the remote's authoritative `seq` assignment IS\n * a local `Journal`'s per-stream total order. `push` appends each record under\n * the stream's lock (so concurrent pushes serialise into one order and never\n * race the seq), and `pull` is `list` filtered past the client's cursor. The\n * seq machinery is `makeJournal`'s, reused — this substrate reinvents none of\n * it, exactly as the issue's seam was designed to be consumed.\n *\n * The `token` is accepted and ignored: this substrate enforces no authorization,\n * because who-may-push-to-which-stream is the host's concern, out of scope here.\n */\nexport function memoryRemoteJournal<R>(): RemoteJournal<R> {\n const journal = memoryJournal<AuthoredRecord<R>>();\n\n const toRemoteEntry = (\n entry: JournalEntry<AuthoredRecord<R>>,\n ): RemoteEntry<R> => ({\n stream: entry.stream,\n seq: entry.seq,\n author: entry.record.author,\n record: entry.record.record,\n });\n\n return {\n async push({ stream, records }) {\n const seqs: RemoteSeq[] = [];\n // Hold the stream's lock across the whole batch so a concurrent push\n // cannot interleave its seqs into the middle of this one — a batch lands\n // as a contiguous run, and racing batches serialise as wholes.\n await journal.withLock(stream, async () => {\n for (const record of records) {\n const { seq } = await journal.append(stream, record);\n seqs.push(seq);\n }\n });\n return { seqs };\n },\n async pull({ stream, since }) {\n const entries = (await journal.list(stream))\n .filter((entry) => entry.seq > since)\n .map(toRemoteEntry);\n return { entries };\n },\n };\n}\n\n/**\n * Where a client keeps its per-`(client, stream)` cursor — the last remote `seq`\n * it has pulled. Kept LOCALLY (the client's own disk/memory), never on the\n * remote, so `push`/`pull` resume incrementally after a restart instead of\n * re-reading the whole stream. `get` returns `0` for a stream never pulled.\n */\nexport interface CursorStore {\n /** The last remote `seq` pulled for `stream`, or `0` if none yet. */\n get(stream: string): Promise<RemoteSeq>;\n /** Record `seq` as the last remote `seq` pulled for `stream`. */\n set(stream: string, seq: RemoteSeq): Promise<void>;\n}\n\n/**\n * An in-memory `CursorStore`, mirroring `memoryStore`. A durable client swaps in\n * a file- or DB-backed one so its cursor survives a restart; the semantics are\n * identical.\n */\nexport function memoryCursorStore(): CursorStore {\n const cursors = new Map<string, RemoteSeq>();\n return {\n async get(stream) {\n return cursors.get(stream) ?? 0;\n },\n async set(stream, seq) {\n cursors.set(stream, seq);\n },\n };\n}\n\n/** How to build a {@link SyncClient}. */\nexport interface SyncClientOptions<R> {\n /** The remote every push/pull talks to. */\n readonly remote: RemoteJournal<R>;\n /**\n * This client's stable author identity, stamped on every record it appends —\n * the one fact team mode needs that solo mode does not.\n */\n readonly author: string;\n /**\n * The opaque credential presented on every push/pull. Optional because this\n * primitive never checks it; a host that runs an authorizing remote supplies\n * it here.\n */\n readonly token?: unknown;\n /**\n * The client's LOCAL append-only journal — its own provisional appends live\n * here. Defaults to a `memoryJournal`; a durable client passes a `fileJournal`\n * (or any `Journal<R>`), which is the #30 substrate seam this consumes.\n */\n readonly local?: LocalJournal<R>;\n /**\n * Where the per-stream pull cursor lives. Defaults to a `memoryCursorStore`; a\n * durable client passes one backed by its own storage.\n */\n readonly cursors?: CursorStore;\n}\n\n/**\n * The slice of a local `Journal<R>` a `SyncClient` drives — its own provisional\n * appends and reading them back in local order. This is structurally the #30\n * `Journal<R>` interface narrowed to what sync needs, so a `memoryJournal` or a\n * `fileJournal` satisfies it as-is.\n */\nexport interface LocalJournal<R> {\n append(stream: string, record: R): Promise<{ seq: number }>;\n list(stream: string): Promise<ReadonlyArray<JournalEntry<R>>>;\n}\n\n/**\n * A local `Journal` joined to a `RemoteJournal` through a cursor and an author\n * identity. It is what makes two local journals \"meet in one order\": each\n * appends offline, both `sync`, and both converge on the remote's order.\n */\nexport interface SyncClient<R> {\n /**\n * Append `record` to the client's LOCAL journal. The append is PROVISIONAL: it\n * has a local order immediately, but no remote `seq` — and so no place in the\n * converged order — until a later `sync` pushes it and pulls it back. Resolves\n * once the local append is durable.\n */\n append(stream: string, record: R): Promise<void>;\n /**\n * The client's local appends for `stream` that have not yet been pushed — the\n * provisional tail. Empty right after a successful `sync`.\n */\n pending(stream: string): Promise<ReadonlyArray<AuthoredRecord<R>>>;\n /**\n * Reconcile `stream` with the remote: PUSH every local append not yet pushed,\n * then PULL every remote entry after the cursor and fold it into the converged\n * log, advancing the cursor. One `sync` may not see records a peer pushes\n * concurrently (they may land after this pull); a second `sync` — with nothing\n * new to push — pulls them, which is how two racing clients converge.\n */\n sync(stream: string): Promise<void>;\n /**\n * The converged log for `stream`: every entry the client has pulled, in remote\n * `seq` order. This is the fold's input — reducing over it yields the client's\n * state. After both of two racing clients sync to quiescence, their `log`s are\n * identical, which is the convergence guarantee.\n */\n log(stream: string): ReadonlyArray<RemoteEntry<R>>;\n}\n\n/**\n * Build a {@link SyncClient} over a remote, an author identity, and (optionally)\n * a local journal and cursor store.\n *\n * The client holds two watermarks per stream: how many of its own local appends\n * it has PUSHED (so a re-`sync` sends only the new tail), and — in the\n * `CursorStore` — the last remote `seq` it has PULLED (so it reads only what is\n * new). Its converged `log` is built solely from `pull`, so `pull` is the single\n * source of converged truth; a client learns even its OWN records' remote order\n * by pulling them back.\n */\nexport function makeSyncClient<R>(\n options: SyncClientOptions<R>,\n): SyncClient<R> {\n const { remote, author, token } = options;\n const local = options.local ?? memoryJournal<R>();\n const cursors = options.cursors ?? memoryCursorStore();\n\n // Per stream: how many of this client's local appends have been pushed. Local\n // seqs are 1-based and gap-free, so this count is also the local seq watermark\n // — appends with local seq > pushed are the unpushed tail.\n const pushed = new Map<string, number>();\n // Per stream: the converged log, remote-ordered, built only from `pull`.\n const converged = new Map<string, RemoteEntry<R>[]>();\n\n const unpushed = async (\n stream: string,\n ): Promise<ReadonlyArray<JournalEntry<R>>> => {\n const entries = await local.list(stream);\n return entries.slice(pushed.get(stream) ?? 0);\n };\n\n return {\n async append(stream, record) {\n await local.append(stream, record);\n },\n async pending(stream) {\n const tail = await unpushed(stream);\n return tail.map((entry) => ({ author, record: entry.record }));\n },\n async sync(stream) {\n const tail = await unpushed(stream);\n if (tail.length > 0) {\n await remote.push({\n token,\n stream,\n records: tail.map((entry) => ({ author, record: entry.record })),\n });\n pushed.set(stream, (pushed.get(stream) ?? 0) + tail.length);\n }\n\n const since = await cursors.get(stream);\n const { entries } = await remote.pull({ token, stream, since });\n const last = entries[entries.length - 1];\n if (last !== undefined) {\n const log = converged.get(stream) ?? [];\n log.push(...entries);\n converged.set(stream, log);\n await cursors.set(stream, last.seq);\n }\n },\n log(stream) {\n return (converged.get(stream) ?? []).slice();\n },\n };\n}\n","/**\n * @packageDocumentation\n * @demlik/tea/journal — an append-only, ordered record log for `@demlik/tea`.\n *\n * The log a \"durable, replayable state machine\" replays from. tea already ships\n * the log's neighbours — `fileStore`/`memoryStore` (`Store<S>`: snapshot with\n * whole-load / whole-save), `recorder` (JSONL debug traces), `durable-effects`\n * (`foldLedger` over an owed-effect ledger) — but not the log itself, so every\n * consumer hand-rolled it. This is that log, domain-blind: a `Journal<R>` over\n * an opaque record `R`, with no pipeline/ledger vocabulary of its own.\n *\n * Three operations carry the contract:\n * - `append(stream, record)` — durable before it returns, assigning a\n * per-stream total-order `seq` (1, 2, 3, …). Concurrent appenders to one\n * stream converge on one order; the lock's only job is an honest order key.\n * - `list(stream)` — every entry of a stream in `seq` order.\n * - `changes(listener)` — a subscription over appends made through this\n * instance after subscribing.\n *\n * `withLock(stream, fx)` runs `fx` with exclusive access to a stream, so a\n * read-decide-append sequence is atomic against other writers. It is\n * re-entrant for the same stream: an `append` inside `fx` does not deadlock on\n * the lock `fx` already holds.\n *\n * The substrate is a `JournalBackend<R>` — how a stream is read, how one record\n * is appended durably, and how a stream is locked across processes.\n * `makeJournal` wires the domain-blind orchestration (per-stream serialisation,\n * `seq` assignment, `changes` fan-out) over any backend. `memoryJournal` is the\n * in-process backend that mirrors `memoryStore` for tests; the Node file\n * backend (`fileJournal`) homes in `@demlik/tea/node` beside `fileStore`.\n *\n * Interface shape follows Effect `unstable/eventlog` `EventJournal`\n * (`entries`/`write`/`changes`/`withLock`) as prior art, not a dependency.\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\n\n/** A per-stream total-order key. Starts at 1 and increments by one per append. */\nexport type Seq = number;\n\n/** One appended record, tagged with its stream and its per-stream `seq`. */\nexport interface JournalEntry<R> {\n readonly stream: string;\n readonly seq: Seq;\n readonly record: R;\n}\n\n/**\n * An append-only, ordered record log over an opaque record `R`.\n *\n * Domain-blind: it knows nothing of what a record means. Ordering, durability\n * and the honest order key are its whole contract.\n */\nexport interface Journal<R> {\n /**\n * Append `record` to `stream`, durable before the promise resolves, and\n * resolve with the `seq` it was assigned — the next integer in the stream's\n * total order.\n */\n append(stream: string, record: R): Promise<{ seq: Seq }>;\n /** Every entry of `stream`, in `seq` order. Empty for an unwritten stream. */\n list(stream: string): Promise<ReadonlyArray<JournalEntry<R>>>;\n /**\n * Subscribe to appends made through this instance after this call. Returns an\n * unsubscribe. Cross-process appends are not delivered — a cross-process\n * subscription is a polling concern this log leaves to its consumer.\n */\n changes(listener: (entry: JournalEntry<R>) => void): () => void;\n /**\n * Run `fx` with exclusive access to `stream` — the in-process mutex against\n * other callers here, the backend lock against other processes. Re-entrant\n * for the same stream, so an `append` (or nested `withLock`) inside `fx` runs\n * under the lock this call already holds rather than deadlocking on it.\n */\n withLock<A>(stream: string, fx: () => Promise<A>): Promise<A>;\n}\n\n/**\n * The substrate a `Journal<R>` is built over. Everything host-specific lives\n * here; `makeJournal` owns the host-blind rest.\n *\n * `append` is always invoked under `lock` for the same stream, so a backend\n * assigns `seq` from `read().length + 1` without racing itself.\n */\nexport interface JournalBackend<R> {\n /** Every entry of `stream` in `seq` order; empty for an unwritten stream. */\n read(stream: string): Promise<ReadonlyArray<JournalEntry<R>>>;\n /**\n * Persist `record` as the next entry of `stream`, durable before resolving,\n * and resolve with the entry it wrote. Invoked only while `lock` holds the\n * stream, so `seq` is safely the current length plus one.\n */\n append(stream: string, record: R): Promise<JournalEntry<R>>;\n /** Run `fx` while holding `stream`'s cross-process lock. */\n lock<A>(stream: string, fx: () => Promise<A>): Promise<A>;\n}\n\n/**\n * A promise-chained mutex: each `run` waits for the prior one to settle, so the\n * bodies never interleave. The order key is honest because assignment happens\n * inside a body, never across the `await` that another body could slip through.\n */\nclass AsyncLock {\n private tail: Promise<unknown> = Promise.resolve();\n\n run<A>(fx: () => Promise<A>): Promise<A> {\n const result = this.tail.then(fx, fx);\n this.tail = result.then(\n () => undefined,\n () => undefined,\n );\n return result;\n }\n}\n\n/**\n * Build a `Journal<R>` over `backend`. Owns the host-blind half: per-stream\n * serialisation, re-entrant exclusion, and the `changes` fan-out. `append` and\n * `withLock` both route through `exclusive`, which holds the in-process mutex\n * and the backend lock for one stream and is re-entrant for that stream within\n * a single call chain.\n */\nexport function makeJournal<R>(backend: JournalBackend<R>): Journal<R> {\n const mutexes = new Map<string, AsyncLock>();\n // The streams this async call chain holds. Kept in AsyncLocalStorage, not an\n // instance-wide Set, because re-entrancy is a property of the CALL CHAIN, not\n // of the instance: a genuine re-entrant append (nested inside an `fx` that\n // already holds the stream) and an INDEPENDENT top-level append that merely\n // arrives while another chain holds the lock are indistinguishable to a\n // shared Set — so the shared Set waves the independent call through inline,\n // past the mutex, to run concurrently with the holder. That is a lost record:\n // in `fileJournal` the two both read the same length and compute the same\n // `seq`, and the second `rename` clobbers the first. A per-chain store only\n // ever reports the streams THIS chain entered, so an independent call sees an\n // empty (or unrelated) store and correctly queues on the mutex.\n const heldStreams = new AsyncLocalStorage<ReadonlySet<string>>();\n const listeners = new Set<(entry: JournalEntry<R>) => void>();\n\n const mutexFor = (stream: string): AsyncLock => {\n let mutex = mutexes.get(stream);\n if (!mutex) {\n mutex = new AsyncLock();\n mutexes.set(stream, mutex);\n }\n return mutex;\n };\n\n const exclusive = <A>(stream: string, fx: () => Promise<A>): Promise<A> => {\n const held = heldStreams.getStore();\n // This call chain already holds this stream's lock: run inline, or a\n // re-entrant append/withLock would block forever on a lock it owns.\n if (held?.has(stream)) return fx();\n return mutexFor(stream).run(() =>\n backend.lock(stream, () => {\n // Scope the augmented held-set to `fx` and its async continuations. The\n // store is torn down when `fx` settles — no finally clause to leak it.\n const nested = new Set(held).add(stream);\n return heldStreams.run(nested, fx);\n }),\n );\n };\n\n return {\n append(stream, record) {\n return exclusive(stream, async () => {\n const entry = await backend.append(stream, record);\n for (const listener of listeners) listener(entry);\n return { seq: entry.seq };\n });\n },\n list(stream) {\n return Promise.resolve(backend.read(stream));\n },\n changes(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n withLock(stream, fx) {\n return exclusive(stream, fx);\n },\n };\n}\n\n/**\n * An in-process `Journal<R>` — the standard test substrate, mirroring\n * `memoryStore` from `@demlik/tea/mem`. Reference semantics: a record handed to\n * `append` is stored by reference, exactly as `memoryStore` stores state. No\n * cross-process lock is needed — a single process owns the whole log — so the\n * backend lock is identity and `makeJournal`'s in-process mutex is the only\n * ordering guarantee.\n */\nexport function memoryJournal<R>(): Journal<R> {\n const streams = new Map<string, JournalEntry<R>[]>();\n\n return makeJournal<R>({\n async read(stream) {\n return (streams.get(stream) ?? []).slice();\n },\n async append(stream, record) {\n const log = streams.get(stream) ?? [];\n const entry: JournalEntry<R> = { stream, seq: log.length + 1, record };\n log.push(entry);\n streams.set(stream, log);\n return entry;\n },\n async lock(_stream, fx) {\n return fx();\n },\n });\n}\n\n// The remote-journal + per-remote-cursor half of the journal feature (#31): the\n// remote a client pushes to and pulls from, the local cursor that resumes sync,\n// and the sync client that joins a local `Journal` to a remote so two local\n// journals meet in one order. It builds ON `memoryJournal` above.\nexport * from \"./remote\";\n"]}
@@ -1,5 +1,5 @@
1
1
  import { initPaginator, start, recordPage, drain, resume, isDone } from './chunk-SINXE47Y.js';
2
- import { createResilientCall } from './chunk-EUF7XOEE.js';
2
+ import { createResilientCall } from './chunk-4KLG43GY.js';
3
3
  import { MsgType } from './chunk-JELSZN25.js';
4
4
 
5
5
  // src/paginated-walk/index.ts
@@ -126,5 +126,5 @@ function liftWalk(state, [slice, cmds]) {
126
126
  }
127
127
 
128
128
  export { PAGE_KEY, createPaginatedWalk, liftWalk };
129
- //# sourceMappingURL=chunk-I5OV65MS.js.map
130
- //# sourceMappingURL=chunk-I5OV65MS.js.map
129
+ //# sourceMappingURL=chunk-YUXP55T2.js.map
130
+ //# sourceMappingURL=chunk-YUXP55T2.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/paginated-walk/index.ts"],"names":["start","drain","resume"],"mappings":"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","file":"chunk-I5OV65MS.js","sourcesContent":["/**\n * @demlik/tea/paginated-walk — traverse a paginated API / sitemap end to end\n * WITHOUT a fake clock, without 429s, and resumable across a Durable-Object\n * eviction. The L2 composition that bolts `../resilient-call`'s resilience onto\n * `../paginator`'s cursor walk: each page is fetched as ONE keyed resilient\n * call (retry + backoff + rate-limit gate), and the cursor advances only once a\n * page settles `ok`.\n *\n * ## The recipe (the two siblings it composes)\n *\n * - **`../paginator`** owns the walk loop: the opaque `Cursor`, the `seen` /\n * `pages` counters, and the `idle → fetching → … → done` phase machine.\n * `paginated-walk` never re-implements cursor advancement — it folds each\n * settled page through `recordPage`, letting the paginator decide\n * `done` / `fetching(next)` / `paused`. Backpressure is a TWO-way valve:\n * `recordPage` parks the walk in `paused` once `seen >= highWaterMark`, and\n * the `drain` / `resume` verbs (delegating to the paginator's own\n * `drain` / `resume`) lower the gauge and re-open the parked fetch — without\n * them `highWaterMark` would be a one-way valve that strands a paused walk.\n * - **`../resilient-call`** owns the page fetch: the single `resilient_run`\n * effect per page is gated through that knob's cache → circuit → rate-limit\n * → retry pipeline. A transient page failure backs off and re-issues the\n * SAME cursor (idempotent retry); the walk does not advance until the page\n * genuinely settles.\n *\n * Those are the only two siblings this module calls into. The resilience knobs\n * (`retry` / `circuit` / `rateLimit` / `deadline`) are NOT separate bricks\n * `paginated-walk` composes — they belong to `resilient-call`, which owns the\n * rate-limit gate internally. This module merely passes them through to\n * `createResilientCall` and re-uses two config TYPES at the boundary:\n * `RateLimitConfig` (re-exported by `resilient-call`) and `RetryPolicy` (from\n * `../retry-backoff`). Pass a `rateLimit` and `resilient-call`'s token bucket\n * throttles page fetches (\"don't 429 the upstream\"); omit it and the walk runs\n * as fast as the upstream answers.\n *\n * Because the page-fetch resilience IS a `resilient-call` slice, this knob\n * **inherits that knob's contract verbatim**: the slice is a flat record, every\n * verb returns `readonly [State, Cmd[]]`, time is always an `at` parameter, and\n * the jitter RNG is injected once at `createPaginatedWalk(config, rng)` — never\n * read inside a verb (per the spec, `paginated-walk` *extends* `resilient-call`).\n *\n * ## The two non-negotiables (canon)\n *\n * - **Durable** — the slice is plain data: the paginator phase + counters, and\n * the resilient-call brick states (all JSON-serializable). The cursor lives\n * in the Model, not a closure, so a walk survives eviction mid-traversal and\n * resumes from the exact page it was fetching.\n * - **Replayable** — every transition is a verb returning new state + Cmds.\n * `nextCursor` / `onPage` are pure config functions applied at the verb\n * boundary; the Cmds they yield are plain data. `replay` reconstructs the\n * whole walk exactly.\n *\n * ## Where the clock / RNG live\n *\n * Inside the verbs: nowhere. `at` arrives on `start` / `pageOk` / `pageErr` /\n * `onTimer` and is threaded straight into the underlying resilient-call gate (so\n * a rate-limited or retried page measures backoff from that `at`). The jitter\n * RNG is injected at construction. The only clock read is `Date.now()` inside\n * the resilient-call `handlers` port (the effect boundary), inherited unchanged.\n *\n * ## The fixed fetch key\n *\n * A walk fetches one page at a time, so the resilient-call layer tracks exactly\n * one logical call under the constant key `PAGE_KEY`. The cursor being fetched\n * is the resilient call's `input`, so a post-eviction resume re-issues the same\n * page request. (`resilient-call` is keyed to support N concurrent calls; a walk\n * deliberately uses a single key — the paginator is the sequencer.)\n *\n * ## Typical wiring\n *\n * const walk = createPaginatedWalk<PageToken, ApiPage>({\n * firstPage: null, // the API's \"page one\" sentinel\n * nextCursor: (page) => page.nextToken ?? null,\n * onPage: (page) => [{ type: \"index_rows\", rows: page.items }],\n * rateLimit: { capacity: 10, refillPerSec: 5 },\n * retry: defaultRetryPolicy,\n * });\n *\n * // in the machine:\n * init: () => [{ walk: walk.init() }, []],\n * update: {\n * start: (s, m) => lift(s, walk.start(s.walk, m.at)),\n * page_ok: (s, m) => lift(s, walk.pageOk(s.walk, m.result, m.at)),\n * page_err: (s, m) => lift(s, walk.pageErr(s.walk, m.error, m.at)),\n * retry_due:(s, m) => lift(s, walk.onTimer(s.walk, m)),\n * drained: (s, m) => lift(s, walk.drain(s.walk, m.n)),\n * resume: (s, m) => lift(s, walk.resume(s.walk, m.at)),\n * },\n * subscriptions: (s) => walk.subs(s.walk),\n * subscribe: { deadline: subscribeDeadline },\n * interpret: walk.handlers({ run: (cursor) => api.fetchPage(cursor) }),\n */\n\nimport type { Cmd } from \"../index\";\nimport {\n drain as drainWalk,\n initPaginator,\n isDone,\n type PaginatorPolicy,\n type PaginatorState,\n recordPage,\n resume as resumeWalk,\n start as startWalk,\n} from \"../paginator\";\nimport { MsgType } from \"../protocol\";\nimport {\n type CircuitConfig,\n createResilientCall,\n type DeadlineConfig,\n type DeadlineSub,\n deadlineSub,\n type FailMsg,\n type RateLimitConfig,\n type ResilientPorts,\n type ResilientState,\n type ResilientTimerMsg,\n type RunCmd,\n type SucceedMsg,\n subscribeDeadline,\n} from \"../resilient-call\";\nimport type { RetryPolicy } from \"../retry-backoff\";\n\n// ===========================================================================\n// Config — the knob. `firstPage` / `nextCursor` / `onPage` describe the walk;\n// the resilience bricks are inherited from resilient-call (every one optional —\n// omit a brick, omit its gate).\n// ===========================================================================\n\n/**\n * The paginated-walk knob. Generic in `Cursor` (the opaque page handle the\n * upstream API hands back — offset, page token, \"next\" URL, …) and `Page` (the\n * shape your fetch port resolves with).\n *\n * `nextCursor` and `onPage` are PURE config functions applied at the verb\n * boundary, never stored in the slice or in a Cmd (invariant 3 — Cmds are\n * plain data, not closures). `nextCursor(page)` extracts the API's next-page\n * handle (or `null` at the end); `onPage(page)` yields the Cmds the consumer\n * wants emitted for each successfully fetched page (index rows, write to R2,\n * dispatch a downstream Msg — whatever; an empty array is fine).\n *\n * The resilience bricks (`retry` / `circuit` / `rateLimit` / `deadline`) are\n * the SAME optional knobs `resilient-call` exposes — paginated-walk extends it.\n * `rateLimit` is the brick that keeps the walk from 429-ing the upstream.\n */\nexport interface PaginatedWalkConfig<Cursor, Page, EmittedCmd extends Cmd> {\n /**\n * The cursor the walk fetches first. For an offset API this is typically `0`;\n * for a token API, whatever sentinel the API treats as \"page one\" (often\n * `null` or `\"\"`). Threaded into `paginator`'s `firstCursor`.\n */\n readonly firstPage: Cursor;\n /**\n * Extract the next cursor from a just-fetched page. Return a `Cursor` to keep\n * walking, or `null` when the API signalled the end (the walk finishes). PURE\n * + consumer-specific (only the consumer knows the API's pagination shape).\n */\n readonly nextCursor: (page: Page) => Cursor | null;\n /**\n * The Cmds to emit for each successfully fetched page — the consumer's work\n * per page (index, persist, fan out, …). Returns plain-data Cmds; `[]` is a\n * valid \"I process the page synchronously elsewhere\" answer. PURE.\n */\n readonly onPage: (page: Page) => readonly EmittedCmd[];\n /**\n * How many items a page contributes to the paginator's backpressure gauge\n * (`seen`). Defaults to `1` per page (count pages, not items) when omitted; a\n * consumer that wants per-item backpressure returns `page.items.length`. PURE.\n */\n readonly pageSize?: (page: Page) => number;\n /**\n * Backpressure threshold handed to the paginator. `seen >= highWaterMark`\n * parks the walk in `paused` instead of fetching the next page. Non-positive\n * (the default `0`) disables backpressure — the walk runs flat out until the\n * API exhausts. See `../paginator`'s `highWaterMark` docs.\n */\n readonly highWaterMark?: number;\n\n // --- resilience bricks, inherited from resilient-call (all optional) ---\n /** Exponential-backoff retry policy for a transient page-fetch failure. */\n readonly retry?: RetryPolicy;\n /** Circuit breaker around the page fetch (one upstream → one breaker). */\n readonly circuit?: CircuitConfig;\n /** Token-bucket rate limit — the \"don't 429 the upstream\" knob. */\n readonly rateLimit?: RateLimitConfig;\n /** Overall wall-clock deadline per individual page fetch. */\n readonly deadline?: DeadlineConfig;\n}\n\n// ===========================================================================\n// Slice — the Model field this knob owns. The paginator walk state + the\n// resilient-call slice for the page fetch. Both are flat plain data.\n// ===========================================================================\n\n/**\n * The slice. `walk` is the cursor/phase/counters from `../paginator`;\n * `resilience` is the `../resilient-call` slice that gates the page fetch.\n * Every field is JSON-serializable, so the whole walk survives a reload.\n *\n * The two move in lockstep: `walk` decides WHICH cursor to fetch next, while\n * `resilience` decides HOW that fetch is attempted (gated, retried, backed\n * off). `pageOk` advances `walk`; `pageErr` defers to `resilience` and leaves\n * `walk` parked on the same cursor until the page truly settles.\n */\nexport interface PaginatedWalkState<Cursor, Page> {\n readonly walk: PaginatorState<Cursor>;\n readonly resilience: ResilientState<Cursor, Page>;\n}\n\n// ===========================================================================\n// Cmds + Msgs the knob speaks.\n// ===========================================================================\n\n/**\n * The page-fetch effect this knob emits: the inherited `resilient_run` Cmd from\n * resilient-call, whose `input` is the `Cursor` to fetch and whose `key` is the\n * fixed `PAGE_KEY`. The consumer's `handlers(ports)` interprets it.\n */\nexport type FetchPageCmd<Cursor> = RunCmd<Cursor>;\n\n/** Page-settled Msgs the `handlers` port dispatches back (inherited verbatim). */\nexport type PageOkMsg<Page> = SucceedMsg<Page>;\nexport type PageErrMsg = FailMsg;\n\n/** The retry / deadline timer Msg — inherited from resilient-call. */\nexport type PaginatedWalkTimerMsg = ResilientTimerMsg;\n\n/**\n * Ports the consumer supplies to `handlers`. `run(cursor)` fetches ONE page for\n * the given cursor; throwing routes to `pageErr` (and thus the resilience\n * backoff). Same shape as resilient-call's port, specialized to the page fetch.\n */\nexport interface PaginatedWalkPorts<Cursor, Page>\n extends ResilientPorts<Cursor, Page> {}\n\n// ===========================================================================\n// The fixed key. A walk fetches one page at a time → exactly one logical\n// resilient call. The paginator is the sequencer; resilient-call's keying is\n// pinned to this single key.\n// ===========================================================================\n\n/** The single resilient-call key every page fetch runs under. */\nexport const PAGE_KEY = \"page\";\n\n// ===========================================================================\n// The knob factory.\n// ===========================================================================\n\n/**\n * Build a paginated-walk knob from `config`. `rng` is injected for the\n * underlying retry jitter — pass a fixed `() => 0` in tests to pin backoff;\n * defaults to `Math.random` (read at the verb boundary inside resilient-call,\n * never in a `paginated-walk` verb). Inherited straight from resilient-call.\n *\n * Returns the uniform L2 knob contract: `init()`, the verbs `start` / `pageOk`\n * / `pageErr` / `onTimer`, `subs(state)`, and `handlers(ports)`. `Cursor` is\n * the page handle, `Page` the fetch result, `EmittedCmd` the consumer's\n * per-page Cmds plus the inherited `FetchPageCmd`.\n */\nexport function createPaginatedWalk<Cursor, Page, EmittedCmd extends Cmd = Cmd>(\n config: PaginatedWalkConfig<Cursor, Page, EmittedCmd>,\n rng: () => number = Math.random,\n) {\n // The page fetch is a resilient call keyed by Cursor input → Page result.\n // We feed it only the resilience bricks; the walk-loop knobs are ours.\n const rc = createResilientCall<Cursor, Page>(\n {\n retry: config.retry,\n circuit: config.circuit,\n rateLimit: config.rateLimit,\n deadline: config.deadline,\n },\n rng,\n );\n\n // The paginator policy: our firstPage is the paginator's firstCursor; the\n // high-water mark gates backpressure (0 / omitted → unbounded).\n const paginatorPolicy: PaginatorPolicy<Cursor> = {\n firstCursor: config.firstPage,\n highWaterMark: config.highWaterMark ?? 0,\n };\n\n // The combined Cmd type the verbs can emit: the inherited page-fetch effect\n // plus whatever `onPage` yields.\n type OutCmd = FetchPageCmd<Cursor> | EmittedCmd;\n\n /** Replace the resilience slice, leaving `walk` untouched. */\n function withResilience(\n s: PaginatedWalkState<Cursor, Page>,\n resilience: ResilientState<Cursor, Page>,\n ): PaginatedWalkState<Cursor, Page> {\n return { ...s, resilience };\n }\n\n /** The starting slice: an idle paginator + a fresh resilient-call slice. */\n function init(): PaginatedWalkState<Cursor, Page> {\n return {\n walk: initPaginator<Cursor>(),\n resilience: rc.init(),\n };\n }\n\n /**\n * Issue a resilient page fetch for `cursor` at `at`. The cursor is the\n * resilient call's `input` (so a retry / resume re-fetches the same page) and\n * `PAGE_KEY` is the fixed key. Returns the advanced resilience slice + the\n * `resilient_run` Cmd (or none, if the gate cached / fast-failed / backed off).\n * PURE — `at` is the only clock. Shared by `start` and `pageOk`'s advance.\n */\n function fetch(\n s: PaginatedWalkState<Cursor, Page>,\n cursor: Cursor,\n at: number,\n ): readonly [\n PaginatedWalkState<Cursor, Page>,\n readonly FetchPageCmd<Cursor>[],\n ] {\n const [resilience, cmds] = rc.attempt(s.resilience, PAGE_KEY, cursor, at);\n return [withResilience(s, resilience), cmds];\n }\n\n /**\n * Issue the page fetch for a freshly-armed paginator transition, else a pure\n * no-op. `walk` is the result of a paginator verb (`startWalk` / `resumeWalk`)\n * applied to `s.walk`; this states the arm-and-fetch contract ONCE for the two\n * verbs that share it (`start`, `resume`).\n *\n * A paginator verb is a pure no-op on any phase it does not act on — it returns\n * the SAME state object. Reference identity (not phase) is the honest \"did the\n * verb arm a fresh fetch?\" check: a walk already `fetching` would also satisfy\n * `phase === \"fetching\"` yet must NOT re-issue a fetch (page-one skip /\n * double-fetch). So we proceed ONLY when the verb produced a fresh state. The\n * `phase === \"fetching\"` narrow that follows is therefore always true here\n * (both verbs only ever produce a `fetching` arm), and also narrows `.cursor`\n * for the type checker. PURE — `at` threads into the fetch gate.\n */\n function armAndFetch(\n s: PaginatedWalkState<Cursor, Page>,\n walk: PaginatorState<Cursor>,\n at: number,\n ): readonly [PaginatedWalkState<Cursor, Page>, readonly OutCmd[]] {\n if (walk === s.walk || walk.phase !== \"fetching\") {\n return [s, []];\n }\n return fetch({ ...s, walk }, walk.cursor, at);\n }\n\n // === Verb: start =========================================================\n\n /**\n * Begin the walk: arm the paginator's first cursor and issue the first page\n * fetch (gated through resilience). On any non-`idle` paginator phase this is\n * a no-op — re-starting a walk in flight would skip back to page one and\n * double-count, so a stray `start` after launch is absorbed (the paginator's\n * own `start` guard). PURE — `at` threads into the fetch gate.\n */\n function start(\n s: PaginatedWalkState<Cursor, Page>,\n at: number,\n ): readonly [PaginatedWalkState<Cursor, Page>, readonly OutCmd[]] {\n // `startWalk` is a pure no-op on any non-`idle` phase (same reference); a\n // fresh `fetching(firstCursor)` arm → issue the first page fetch.\n return armAndFetch(s, startWalk(s.walk, paginatorPolicy), at);\n }\n\n // === Verb: pageOk ========================================================\n\n /**\n * Record a successfully fetched `page` and decide what comes next. PURE —\n * `at` is the cache / next-fetch clock.\n *\n * 1. Settle the resilient call OK (closes the breaker, fills the cache,\n * resets the page-fetch retry counter) — inherited from resilient-call.\n * 2. Fold the page through the paginator: add `pageSize(page)` to `seen`,\n * bump `pages`, and branch on `nextCursor(page)`:\n * - `null` → the API is exhausted → paginator goes `done`.\n * - a cursor → advance, then either fetch it (backpressure room) or\n * park in `paused` (high-water mark reached).\n * 3. Emit the consumer's `onPage(page)` Cmds either way, PLUS the next\n * page-fetch effect when the paginator stays `fetching`.\n *\n * A `pageOk` that arrives when the paginator is not `fetching` (a late\n * duplicate, a double-dispatch) is absorbed by `recordPage` — the cursor does\n * not advance and `seen` is not double-counted; only the resilient settle +\n * `onPage` Cmds happen. The walk is a coordination aid, not a correctness\n * gate.\n */\n function pageOk(\n s: PaginatedWalkState<Cursor, Page>,\n page: Page,\n at: number,\n ): readonly [PaginatedWalkState<Cursor, Page>, readonly OutCmd[]] {\n // 1) Settle the underlying resilient call as a success.\n const [resilience] = rc.succeed(s.resilience, PAGE_KEY, {\n type: MsgType.ResilientOk,\n key: PAGE_KEY,\n result: page,\n at,\n });\n\n // 2) Advance the paginator with the page's contribution + the next cursor.\n const count = config.pageSize ? config.pageSize(page) : 1;\n const next = config.nextCursor(page);\n const walk = recordPage(s.walk, count, next, paginatorPolicy);\n\n const advanced: PaginatedWalkState<Cursor, Page> = { walk, resilience };\n\n // 3) The consumer's per-page work, always emitted on a successful page.\n const pageCmds: readonly EmittedCmd[] = config.onPage(page);\n\n // If the paginator stayed `fetching`, issue the next page fetch (gated).\n if (walk.phase === \"fetching\") {\n const [withFetch, fetchCmds] = fetch(advanced, walk.cursor, at);\n return [withFetch, [...pageCmds, ...fetchCmds]];\n }\n\n // `done` (exhausted) or `paused` (backpressure) → no further fetch now.\n return [advanced, pageCmds];\n }\n\n // === Verb: pageErr =======================================================\n\n /**\n * Record a failed page fetch and back off via resilience. PURE — `at` stamps\n * the breaker trip + the retry-delay base.\n *\n * Defers to the resilient-call `fail` verb: it trips the breaker and either\n * schedules a retry (paginator stays parked on the SAME cursor — no advance)\n * or settles the resilient call `failed` once retries are exhausted. The\n * paginator is deliberately untouched: a failed fetch must NOT advance the\n * cursor or skip a page. When the retry timer later fires, `onTimer` re-issues\n * the same page fetch.\n *\n * GUARDED by the paginator phase, exactly as `pageOk` is guarded by\n * `recordPage`'s `fetching`-only check. A `pageErr` only makes sense while a\n * page fetch is outstanding (`walk.phase === \"fetching\"`). A stray `pageErr`\n * that arrives after the walk has settled — `done` (exhausted), `paused`\n * (backpressure), or `idle` (not started) — has NO outstanding fetch to fail.\n * Without this guard it would still call `rc.fail`, which trips the SHARED\n * circuit breaker (dinging a healthy upstream) and overwrites the settled\n * `PAGE_KEY` resilient slot with `failed` (clobbering the succeeded result a\n * resume would otherwise observe). So a stray `pageErr` is absorbed as a pure\n * no-op — identity preserved — matching `pageOk`'s \"the walk is a coordination\n * aid, not a correctness gate\" stance.\n */\n function pageErr(\n s: PaginatedWalkState<Cursor, Page>,\n error: unknown,\n at: number,\n ): readonly [PaginatedWalkState<Cursor, Page>, readonly OutCmd[]] {\n // Only a `fetching` paginator has an outstanding page fetch to fail. On any\n // other phase the fail would corrupt shared resilience state for a fetch\n // that is not in flight — absorb it as a pure no-op.\n if (s.walk.phase !== \"fetching\") {\n return [s, []];\n }\n const [resilience, cmds] = rc.fail(s.resilience, PAGE_KEY, {\n type: MsgType.ResilientErr,\n key: PAGE_KEY,\n error,\n at,\n });\n return [withResilience(s, resilience), cmds];\n }\n\n // === Verb: onTimer =======================================================\n\n /**\n * A retry / deadline timer fired. Defers to resilient-call's `onTimer`: a\n * retry timer re-runs the gate for the page-fetch's remembered cursor (re-\n * issuing the SAME page); a deadline timer settles the resilient call failed.\n * The paginator stays parked on its cursor throughout — only `pageOk` advances\n * it. A stale fire is a no-op (inherited). PURE.\n *\n * GUARDED by the paginator phase, for the same reason as `pageErr`. A timer is\n * only meaningful while a page fetch is outstanding (`walk.phase ===\n * \"fetching\"`). A stray timer that fires after the walk has settled (`done` /\n * `paused` / `idle`) would otherwise re-enter `rc.onTimer` — re-issuing a fetch\n * the paginator no longer wants (a deadline timer could even overwrite the\n * settled `PAGE_KEY` slot with a deadline `failed`). On any non-`fetching`\n * phase the timer is absorbed as a pure no-op.\n */\n function onTimer(\n s: PaginatedWalkState<Cursor, Page>,\n msg: PaginatedWalkTimerMsg,\n ): readonly [PaginatedWalkState<Cursor, Page>, readonly OutCmd[]] {\n if (s.walk.phase !== \"fetching\") {\n return [s, []];\n }\n const [resilience, cmds] = rc.onTimer(s.resilience, msg);\n if (resilience === s.resilience) {\n // Inherited no-op identity → keep our slice identity too (pure no-op).\n return [s, []];\n }\n return [withResilience(s, resilience), cmds];\n }\n\n // === Verb: drain =========================================================\n\n /**\n * Acknowledge that the consumer finished processing `n` items, lowering the\n * paginator's backpressure gauge (`seen`). PURE — no clock. Delegates to\n * `paginator.drain`: subtracts `n` (floored at 0), leaves the phase untouched.\n *\n * The OTHER half of the backpressure valve `highWaterMark` opens: `recordPage`\n * parks the walk in `paused` once `seen >= highWaterMark`, and `drain` is how a\n * consumer lowers `seen` back below the mark so `resume` can re-open the fetch.\n * Without it `highWaterMark` would be a one-way valve — a walk could pause but\n * never continue. Emits no Cmds: draining only frees headroom; `resume` is the\n * separate, explicit \"continue now\" decision.\n */\n function drain(\n s: PaginatedWalkState<Cursor, Page>,\n n: number,\n ): readonly [PaginatedWalkState<Cursor, Page>, readonly OutCmd[]] {\n const walk = drainWalk(s.walk, n);\n if (walk === s.walk) {\n // Pure no-op identity (n <= 0, or seen already at floor).\n return [s, []];\n }\n return [{ ...s, walk }, []];\n }\n\n // === Verb: resume ========================================================\n\n /**\n * Re-open the backpressure valve after a `drain`: if the walk is `paused` and\n * `seen` has dropped below `highWaterMark`, re-arm the parked cursor and issue\n * its page fetch (gated through resilience). PURE — `at` threads into the fetch\n * gate. Delegates the phase decision to `paginator.resume`:\n *\n * - `paused` AND `seen < highWaterMark` → `fetching(cursor)` → emit the fetch.\n * - `paused` but still over the mark → no-op (the valve stays shut until a\n * further `drain` frees real headroom).\n * - any other phase → no-op (nothing to resume).\n *\n * Together with `drain` this completes the valve: `recordPage` shuts it\n * (`paused`), `drain` lowers the gauge, `resume` re-opens it and fires the next\n * page fetch — the same `fetch` helper `start` / `pageOk` use, so a resumed\n * fetch is gated identically (cache / circuit / rate-limit / retry).\n */\n function resume(\n s: PaginatedWalkState<Cursor, Page>,\n at: number,\n ): readonly [PaginatedWalkState<Cursor, Page>, readonly OutCmd[]] {\n // `resumeWalk` is a pure no-op (same reference) on any phase but `paused`, or\n // when `seen` is still at/over the mark. Only a fresh `fetching` arm means\n // the valve actually re-opened — issue the page fetch only then.\n return armAndFetch(s, resumeWalk(s.walk, paginatorPolicy), at);\n }\n\n // === Derived predicates ==================================================\n\n /**\n * Whether the walk has finished — the API returned a null next cursor and the\n * paginator is `done`. PURE, read-only. Lets a consumer's reducer fire a\n * \"walk complete\" Cmd (`if (walk.isDone(s.walk)) …`) without re-checking the\n * paginator phase by hand.\n */\n function isComplete(s: PaginatedWalkState<Cursor, Page>): boolean {\n return isDone(s.walk);\n }\n\n /**\n * The terminal page-fetch failure that stranded the walk, or `undefined` if no\n * page has terminally failed. PURE, read-only. Errors are data: a page that\n * exhausts its retries (or trips the deadline) settles the `PAGE_KEY` resilient\n * slot to `failed` while the paginator stays parked on its cursor — the walk is\n * stuck and will never advance on its own. Without a way to read that phase the\n * failure is unobservable: the walk simply stops emitting fetches with no\n * signal. `failure(s)` surfaces the settled error (the same plain-data sentinel\n * resilient-call records) so a consumer can fire a \"walk dead\" Cmd, alert, or\n * re-`start` a fresh walk.\n */\n function failure(s: PaginatedWalkState<Cursor, Page>): unknown {\n const call = s.resilience.calls[PAGE_KEY];\n return call?.phase === \"failed\" ? call.error : undefined;\n }\n\n /**\n * Whether the walk is STUCK: a page fetch has terminally `failed` yet the\n * paginator is still parked on a cursor (not `done`), so no further fetch will\n * ever be issued without intervention. PURE, read-only.\n *\n * This is the dead-walk predicate the consumer polls (`if (walk.isStuck(s)) …`)\n * to distinguish a healthy finish (`isComplete`) from a silent death. A walk is\n * stuck precisely when the resilient slot settled `failed` AND the paginator\n * has NOT reached `done` — a terminal failure on the final page that also\n * finished the walk is `isComplete`, not stuck. Pairs with `failure(s)`, which\n * hands back the error that stuck it.\n */\n function isStuck(s: PaginatedWalkState<Cursor, Page>): boolean {\n return s.resilience.calls[PAGE_KEY]?.phase === \"failed\" && !isDone(s.walk);\n }\n\n // === Subs ================================================================\n\n /**\n * Pre-wired subscriptions: exactly the resilient-call subs for the page\n * fetch — a retry timer while the fetch is `waiting_retry`, and (with the\n * `deadline` brick) a per-fetch deadline timer while it is active. Reconciled\n * by id, so a phase change cancels the matching timer automatically. Wire\n * `subscribe: { deadline: subscribeDeadline }` (re-exported below).\n */\n function subs(s: PaginatedWalkState<Cursor, Page>): readonly DeadlineSub[] {\n return rc.subs(s.resilience);\n }\n\n // === Handlers ============================================================\n\n /**\n * Pre-wired interpret handler for the page-fetch effect. Inherited from\n * resilient-call (`resilient_run`): wraps the consumer's `run(cursor)` port\n * via `tryInterpret` (Railway) — success → `resilient_ok` (handled by\n * `pageOk`), failure → `resilient_err` (handled by `pageErr`), each stamped\n * with `Date.now()` at the effect boundary (the ONE permitted clock read).\n *\n * interpret: walk.handlers({ run: (cursor) => api.fetchPage(cursor) })\n *\n * The consumer maps the inherited Msg types to their `pageOk` / `pageErr`\n * reducer cells (the wire shape is `resilient_ok` / `resilient_err`).\n */\n function handlers(ports: PaginatedWalkPorts<Cursor, Page>) {\n return rc.handlers(ports);\n }\n\n return {\n init,\n start,\n pageOk,\n pageErr,\n onTimer,\n drain,\n resume,\n isComplete,\n failure,\n isStuck,\n subs,\n handlers,\n };\n}\n\n/**\n * Lift a knob result `[slice, cmds]` into a host `[State, cmds]` where the slice\n * lives at `state.walk`. 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 liftWalk<\n S extends { walk: PaginatedWalkState<Cursor, Page> },\n Cursor,\n Page,\n C extends Cmd,\n>(\n state: S,\n [slice, cmds]: readonly [PaginatedWalkState<Cursor, Page>, readonly C[]],\n): readonly [S, readonly C[]] {\n return [{ ...state, walk: slice }, cmds];\n}\n\n/**\n * Re-export the deadline Sub primitives (inherited from resilient-call) so\n * consumers wire one import: `subscribeDeadline` is the `subscribe` cell,\n * `deadlineSub` builds the Sub literal this knob's `subs` emits.\n */\nexport { subscribeDeadline, deadlineSub };\nexport type { DeadlineSub };\n"]}
1
+ {"version":3,"sources":["../src/paginated-walk/index.ts"],"names":["start","drain","resume"],"mappings":"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","file":"chunk-YUXP55T2.js","sourcesContent":["/**\n * @demlik/tea/paginated-walk — traverse a paginated API / sitemap end to end\n * WITHOUT a fake clock, without 429s, and resumable across a Durable-Object\n * eviction. The L2 composition that bolts `../resilient-call`'s resilience onto\n * `../paginator`'s cursor walk: each page is fetched as ONE keyed resilient\n * call (retry + backoff + rate-limit gate), and the cursor advances only once a\n * page settles `ok`.\n *\n * ## The recipe (the two siblings it composes)\n *\n * - **`../paginator`** owns the walk loop: the opaque `Cursor`, the `seen` /\n * `pages` counters, and the `idle → fetching → … → done` phase machine.\n * `paginated-walk` never re-implements cursor advancement — it folds each\n * settled page through `recordPage`, letting the paginator decide\n * `done` / `fetching(next)` / `paused`. Backpressure is a TWO-way valve:\n * `recordPage` parks the walk in `paused` once `seen >= highWaterMark`, and\n * the `drain` / `resume` verbs (delegating to the paginator's own\n * `drain` / `resume`) lower the gauge and re-open the parked fetch — without\n * them `highWaterMark` would be a one-way valve that strands a paused walk.\n * - **`../resilient-call`** owns the page fetch: the single `resilient_run`\n * effect per page is gated through that knob's cache → circuit → rate-limit\n * → retry pipeline. A transient page failure backs off and re-issues the\n * SAME cursor (idempotent retry); the walk does not advance until the page\n * genuinely settles.\n *\n * Those are the only two siblings this module calls into. The resilience knobs\n * (`retry` / `circuit` / `rateLimit` / `deadline`) are NOT separate bricks\n * `paginated-walk` composes — they belong to `resilient-call`, which owns the\n * rate-limit gate internally. This module merely passes them through to\n * `createResilientCall` and re-uses two config TYPES at the boundary:\n * `RateLimitConfig` (re-exported by `resilient-call`) and `RetryPolicy` (from\n * `../retry-backoff`). Pass a `rateLimit` and `resilient-call`'s token bucket\n * throttles page fetches (\"don't 429 the upstream\"); omit it and the walk runs\n * as fast as the upstream answers.\n *\n * Because the page-fetch resilience IS a `resilient-call` slice, this knob\n * **inherits that knob's contract verbatim**: the slice is a flat record, every\n * verb returns `readonly [State, Cmd[]]`, time is always an `at` parameter, and\n * the jitter RNG is injected once at `createPaginatedWalk(config, rng)` — never\n * read inside a verb (per the spec, `paginated-walk` *extends* `resilient-call`).\n *\n * ## The two non-negotiables (canon)\n *\n * - **Durable** — the slice is plain data: the paginator phase + counters, and\n * the resilient-call brick states (all JSON-serializable). The cursor lives\n * in the Model, not a closure, so a walk survives eviction mid-traversal and\n * resumes from the exact page it was fetching.\n * - **Replayable** — every transition is a verb returning new state + Cmds.\n * `nextCursor` / `onPage` are pure config functions applied at the verb\n * boundary; the Cmds they yield are plain data. `replay` reconstructs the\n * whole walk exactly.\n *\n * ## Where the clock / RNG live\n *\n * Inside the verbs: nowhere. `at` arrives on `start` / `pageOk` / `pageErr` /\n * `onTimer` and is threaded straight into the underlying resilient-call gate (so\n * a rate-limited or retried page measures backoff from that `at`). The jitter\n * RNG is injected at construction. The only clock read is `Date.now()` inside\n * the resilient-call `handlers` port (the effect boundary), inherited unchanged.\n *\n * ## The fixed fetch key\n *\n * A walk fetches one page at a time, so the resilient-call layer tracks exactly\n * one logical call under the constant key `PAGE_KEY`. The cursor being fetched\n * is the resilient call's `input`, so a post-eviction resume re-issues the same\n * page request. (`resilient-call` is keyed to support N concurrent calls; a walk\n * deliberately uses a single key — the paginator is the sequencer.)\n *\n * ## Typical wiring\n *\n * const walk = createPaginatedWalk<PageToken, ApiPage>({\n * firstPage: null, // the API's \"page one\" sentinel\n * nextCursor: (page) => page.nextToken ?? null,\n * onPage: (page) => [{ type: \"index_rows\", rows: page.items }],\n * rateLimit: { capacity: 10, refillPerSec: 5 },\n * retry: defaultRetryPolicy,\n * });\n *\n * // in the machine:\n * init: () => [{ walk: walk.init() }, []],\n * update: {\n * start: (s, m) => lift(s, walk.start(s.walk, m.at)),\n * page_ok: (s, m) => lift(s, walk.pageOk(s.walk, m.result, m.at)),\n * page_err: (s, m) => lift(s, walk.pageErr(s.walk, m.error, m.at)),\n * retry_due:(s, m) => lift(s, walk.onTimer(s.walk, m)),\n * drained: (s, m) => lift(s, walk.drain(s.walk, m.n)),\n * resume: (s, m) => lift(s, walk.resume(s.walk, m.at)),\n * },\n * subscriptions: (s) => walk.subs(s.walk),\n * subscribe: { deadline: subscribeDeadline },\n * interpret: walk.handlers({ run: (cursor) => api.fetchPage(cursor) }),\n */\n\nimport type { Cmd } from \"../index\";\nimport {\n drain as drainWalk,\n initPaginator,\n isDone,\n type PaginatorPolicy,\n type PaginatorState,\n recordPage,\n resume as resumeWalk,\n start as startWalk,\n} from \"../paginator\";\nimport { MsgType } from \"../protocol\";\nimport {\n type CircuitConfig,\n createResilientCall,\n type DeadlineConfig,\n type DeadlineSub,\n deadlineSub,\n type FailMsg,\n type RateLimitConfig,\n type ResilientPorts,\n type ResilientState,\n type ResilientTimerMsg,\n type RunCmd,\n type SucceedMsg,\n subscribeDeadline,\n} from \"../resilient-call\";\nimport type { RetryPolicy } from \"../retry-backoff\";\n\n// ===========================================================================\n// Config — the knob. `firstPage` / `nextCursor` / `onPage` describe the walk;\n// the resilience bricks are inherited from resilient-call (every one optional —\n// omit a brick, omit its gate).\n// ===========================================================================\n\n/**\n * The paginated-walk knob. Generic in `Cursor` (the opaque page handle the\n * upstream API hands back — offset, page token, \"next\" URL, …) and `Page` (the\n * shape your fetch port resolves with).\n *\n * `nextCursor` and `onPage` are PURE config functions applied at the verb\n * boundary, never stored in the slice or in a Cmd (invariant 3 — Cmds are\n * plain data, not closures). `nextCursor(page)` extracts the API's next-page\n * handle (or `null` at the end); `onPage(page)` yields the Cmds the consumer\n * wants emitted for each successfully fetched page (index rows, write to R2,\n * dispatch a downstream Msg — whatever; an empty array is fine).\n *\n * The resilience bricks (`retry` / `circuit` / `rateLimit` / `deadline`) are\n * the SAME optional knobs `resilient-call` exposes — paginated-walk extends it.\n * `rateLimit` is the brick that keeps the walk from 429-ing the upstream.\n */\nexport interface PaginatedWalkConfig<Cursor, Page, EmittedCmd extends Cmd> {\n /**\n * The cursor the walk fetches first. For an offset API this is typically `0`;\n * for a token API, whatever sentinel the API treats as \"page one\" (often\n * `null` or `\"\"`). Threaded into `paginator`'s `firstCursor`.\n */\n readonly firstPage: Cursor;\n /**\n * Extract the next cursor from a just-fetched page. Return a `Cursor` to keep\n * walking, or `null` when the API signalled the end (the walk finishes). PURE\n * + consumer-specific (only the consumer knows the API's pagination shape).\n */\n readonly nextCursor: (page: Page) => Cursor | null;\n /**\n * The Cmds to emit for each successfully fetched page — the consumer's work\n * per page (index, persist, fan out, …). Returns plain-data Cmds; `[]` is a\n * valid \"I process the page synchronously elsewhere\" answer. PURE.\n */\n readonly onPage: (page: Page) => readonly EmittedCmd[];\n /**\n * How many items a page contributes to the paginator's backpressure gauge\n * (`seen`). Defaults to `1` per page (count pages, not items) when omitted; a\n * consumer that wants per-item backpressure returns `page.items.length`. PURE.\n */\n readonly pageSize?: (page: Page) => number;\n /**\n * Backpressure threshold handed to the paginator. `seen >= highWaterMark`\n * parks the walk in `paused` instead of fetching the next page. Non-positive\n * (the default `0`) disables backpressure — the walk runs flat out until the\n * API exhausts. See `../paginator`'s `highWaterMark` docs.\n */\n readonly highWaterMark?: number;\n\n // --- resilience bricks, inherited from resilient-call (all optional) ---\n /** Exponential-backoff retry policy for a transient page-fetch failure. */\n readonly retry?: RetryPolicy;\n /** Circuit breaker around the page fetch (one upstream → one breaker). */\n readonly circuit?: CircuitConfig;\n /** Token-bucket rate limit — the \"don't 429 the upstream\" knob. */\n readonly rateLimit?: RateLimitConfig;\n /** Overall wall-clock deadline per individual page fetch. */\n readonly deadline?: DeadlineConfig;\n}\n\n// ===========================================================================\n// Slice — the Model field this knob owns. The paginator walk state + the\n// resilient-call slice for the page fetch. Both are flat plain data.\n// ===========================================================================\n\n/**\n * The slice. `walk` is the cursor/phase/counters from `../paginator`;\n * `resilience` is the `../resilient-call` slice that gates the page fetch.\n * Every field is JSON-serializable, so the whole walk survives a reload.\n *\n * The two move in lockstep: `walk` decides WHICH cursor to fetch next, while\n * `resilience` decides HOW that fetch is attempted (gated, retried, backed\n * off). `pageOk` advances `walk`; `pageErr` defers to `resilience` and leaves\n * `walk` parked on the same cursor until the page truly settles.\n */\nexport interface PaginatedWalkState<Cursor, Page> {\n readonly walk: PaginatorState<Cursor>;\n readonly resilience: ResilientState<Cursor, Page>;\n}\n\n// ===========================================================================\n// Cmds + Msgs the knob speaks.\n// ===========================================================================\n\n/**\n * The page-fetch effect this knob emits: the inherited `resilient_run` Cmd from\n * resilient-call, whose `input` is the `Cursor` to fetch and whose `key` is the\n * fixed `PAGE_KEY`. The consumer's `handlers(ports)` interprets it.\n */\nexport type FetchPageCmd<Cursor> = RunCmd<Cursor>;\n\n/** Page-settled Msgs the `handlers` port dispatches back (inherited verbatim). */\nexport type PageOkMsg<Page> = SucceedMsg<Page>;\nexport type PageErrMsg = FailMsg;\n\n/** The retry / deadline timer Msg — inherited from resilient-call. */\nexport type PaginatedWalkTimerMsg = ResilientTimerMsg;\n\n/**\n * Ports the consumer supplies to `handlers`. `run(cursor)` fetches ONE page for\n * the given cursor; throwing routes to `pageErr` (and thus the resilience\n * backoff). Same shape as resilient-call's port, specialized to the page fetch.\n */\nexport interface PaginatedWalkPorts<Cursor, Page>\n extends ResilientPorts<Cursor, Page> {}\n\n// ===========================================================================\n// The fixed key. A walk fetches one page at a time → exactly one logical\n// resilient call. The paginator is the sequencer; resilient-call's keying is\n// pinned to this single key.\n// ===========================================================================\n\n/** The single resilient-call key every page fetch runs under. */\nexport const PAGE_KEY = \"page\";\n\n// ===========================================================================\n// The knob factory.\n// ===========================================================================\n\n/**\n * Build a paginated-walk knob from `config`. `rng` is injected for the\n * underlying retry jitter — pass a fixed `() => 0` in tests to pin backoff;\n * defaults to `Math.random` (read at the verb boundary inside resilient-call,\n * never in a `paginated-walk` verb). Inherited straight from resilient-call.\n *\n * Returns the uniform L2 knob contract: `init()`, the verbs `start` / `pageOk`\n * / `pageErr` / `onTimer`, `subs(state)`, and `handlers(ports)`. `Cursor` is\n * the page handle, `Page` the fetch result, `EmittedCmd` the consumer's\n * per-page Cmds plus the inherited `FetchPageCmd`.\n */\nexport function createPaginatedWalk<Cursor, Page, EmittedCmd extends Cmd = Cmd>(\n config: PaginatedWalkConfig<Cursor, Page, EmittedCmd>,\n rng: () => number = Math.random,\n) {\n // The page fetch is a resilient call keyed by Cursor input → Page result.\n // We feed it only the resilience bricks; the walk-loop knobs are ours.\n const rc = createResilientCall<Cursor, Page>(\n {\n retry: config.retry,\n circuit: config.circuit,\n rateLimit: config.rateLimit,\n deadline: config.deadline,\n },\n rng,\n );\n\n // The paginator policy: our firstPage is the paginator's firstCursor; the\n // high-water mark gates backpressure (0 / omitted → unbounded).\n const paginatorPolicy: PaginatorPolicy<Cursor> = {\n firstCursor: config.firstPage,\n highWaterMark: config.highWaterMark ?? 0,\n };\n\n // The combined Cmd type the verbs can emit: the inherited page-fetch effect\n // plus whatever `onPage` yields.\n type OutCmd = FetchPageCmd<Cursor> | EmittedCmd;\n\n /** Replace the resilience slice, leaving `walk` untouched. */\n function withResilience(\n s: PaginatedWalkState<Cursor, Page>,\n resilience: ResilientState<Cursor, Page>,\n ): PaginatedWalkState<Cursor, Page> {\n return { ...s, resilience };\n }\n\n /** The starting slice: an idle paginator + a fresh resilient-call slice. */\n function init(): PaginatedWalkState<Cursor, Page> {\n return {\n walk: initPaginator<Cursor>(),\n resilience: rc.init(),\n };\n }\n\n /**\n * Issue a resilient page fetch for `cursor` at `at`. The cursor is the\n * resilient call's `input` (so a retry / resume re-fetches the same page) and\n * `PAGE_KEY` is the fixed key. Returns the advanced resilience slice + the\n * `resilient_run` Cmd (or none, if the gate cached / fast-failed / backed off).\n * PURE — `at` is the only clock. Shared by `start` and `pageOk`'s advance.\n */\n function fetch(\n s: PaginatedWalkState<Cursor, Page>,\n cursor: Cursor,\n at: number,\n ): readonly [\n PaginatedWalkState<Cursor, Page>,\n readonly FetchPageCmd<Cursor>[],\n ] {\n const [resilience, cmds] = rc.attempt(s.resilience, PAGE_KEY, cursor, at);\n return [withResilience(s, resilience), cmds];\n }\n\n /**\n * Issue the page fetch for a freshly-armed paginator transition, else a pure\n * no-op. `walk` is the result of a paginator verb (`startWalk` / `resumeWalk`)\n * applied to `s.walk`; this states the arm-and-fetch contract ONCE for the two\n * verbs that share it (`start`, `resume`).\n *\n * A paginator verb is a pure no-op on any phase it does not act on — it returns\n * the SAME state object. Reference identity (not phase) is the honest \"did the\n * verb arm a fresh fetch?\" check: a walk already `fetching` would also satisfy\n * `phase === \"fetching\"` yet must NOT re-issue a fetch (page-one skip /\n * double-fetch). So we proceed ONLY when the verb produced a fresh state. The\n * `phase === \"fetching\"` narrow that follows is therefore always true here\n * (both verbs only ever produce a `fetching` arm), and also narrows `.cursor`\n * for the type checker. PURE — `at` threads into the fetch gate.\n */\n function armAndFetch(\n s: PaginatedWalkState<Cursor, Page>,\n walk: PaginatorState<Cursor>,\n at: number,\n ): readonly [PaginatedWalkState<Cursor, Page>, readonly OutCmd[]] {\n if (walk === s.walk || walk.phase !== \"fetching\") {\n return [s, []];\n }\n return fetch({ ...s, walk }, walk.cursor, at);\n }\n\n // === Verb: start =========================================================\n\n /**\n * Begin the walk: arm the paginator's first cursor and issue the first page\n * fetch (gated through resilience). On any non-`idle` paginator phase this is\n * a no-op — re-starting a walk in flight would skip back to page one and\n * double-count, so a stray `start` after launch is absorbed (the paginator's\n * own `start` guard). PURE — `at` threads into the fetch gate.\n */\n function start(\n s: PaginatedWalkState<Cursor, Page>,\n at: number,\n ): readonly [PaginatedWalkState<Cursor, Page>, readonly OutCmd[]] {\n // `startWalk` is a pure no-op on any non-`idle` phase (same reference); a\n // fresh `fetching(firstCursor)` arm → issue the first page fetch.\n return armAndFetch(s, startWalk(s.walk, paginatorPolicy), at);\n }\n\n // === Verb: pageOk ========================================================\n\n /**\n * Record a successfully fetched `page` and decide what comes next. PURE —\n * `at` is the cache / next-fetch clock.\n *\n * 1. Settle the resilient call OK (closes the breaker, fills the cache,\n * resets the page-fetch retry counter) — inherited from resilient-call.\n * 2. Fold the page through the paginator: add `pageSize(page)` to `seen`,\n * bump `pages`, and branch on `nextCursor(page)`:\n * - `null` → the API is exhausted → paginator goes `done`.\n * - a cursor → advance, then either fetch it (backpressure room) or\n * park in `paused` (high-water mark reached).\n * 3. Emit the consumer's `onPage(page)` Cmds either way, PLUS the next\n * page-fetch effect when the paginator stays `fetching`.\n *\n * A `pageOk` that arrives when the paginator is not `fetching` (a late\n * duplicate, a double-dispatch) is absorbed by `recordPage` — the cursor does\n * not advance and `seen` is not double-counted; only the resilient settle +\n * `onPage` Cmds happen. The walk is a coordination aid, not a correctness\n * gate.\n */\n function pageOk(\n s: PaginatedWalkState<Cursor, Page>,\n page: Page,\n at: number,\n ): readonly [PaginatedWalkState<Cursor, Page>, readonly OutCmd[]] {\n // 1) Settle the underlying resilient call as a success.\n const [resilience] = rc.succeed(s.resilience, PAGE_KEY, {\n type: MsgType.ResilientOk,\n key: PAGE_KEY,\n result: page,\n at,\n });\n\n // 2) Advance the paginator with the page's contribution + the next cursor.\n const count = config.pageSize ? config.pageSize(page) : 1;\n const next = config.nextCursor(page);\n const walk = recordPage(s.walk, count, next, paginatorPolicy);\n\n const advanced: PaginatedWalkState<Cursor, Page> = { walk, resilience };\n\n // 3) The consumer's per-page work, always emitted on a successful page.\n const pageCmds: readonly EmittedCmd[] = config.onPage(page);\n\n // If the paginator stayed `fetching`, issue the next page fetch (gated).\n if (walk.phase === \"fetching\") {\n const [withFetch, fetchCmds] = fetch(advanced, walk.cursor, at);\n return [withFetch, [...pageCmds, ...fetchCmds]];\n }\n\n // `done` (exhausted) or `paused` (backpressure) → no further fetch now.\n return [advanced, pageCmds];\n }\n\n // === Verb: pageErr =======================================================\n\n /**\n * Record a failed page fetch and back off via resilience. PURE — `at` stamps\n * the breaker trip + the retry-delay base.\n *\n * Defers to the resilient-call `fail` verb: it trips the breaker and either\n * schedules a retry (paginator stays parked on the SAME cursor — no advance)\n * or settles the resilient call `failed` once retries are exhausted. The\n * paginator is deliberately untouched: a failed fetch must NOT advance the\n * cursor or skip a page. When the retry timer later fires, `onTimer` re-issues\n * the same page fetch.\n *\n * GUARDED by the paginator phase, exactly as `pageOk` is guarded by\n * `recordPage`'s `fetching`-only check. A `pageErr` only makes sense while a\n * page fetch is outstanding (`walk.phase === \"fetching\"`). A stray `pageErr`\n * that arrives after the walk has settled — `done` (exhausted), `paused`\n * (backpressure), or `idle` (not started) — has NO outstanding fetch to fail.\n * Without this guard it would still call `rc.fail`, which trips the SHARED\n * circuit breaker (dinging a healthy upstream) and overwrites the settled\n * `PAGE_KEY` resilient slot with `failed` (clobbering the succeeded result a\n * resume would otherwise observe). So a stray `pageErr` is absorbed as a pure\n * no-op — identity preserved — matching `pageOk`'s \"the walk is a coordination\n * aid, not a correctness gate\" stance.\n */\n function pageErr(\n s: PaginatedWalkState<Cursor, Page>,\n error: unknown,\n at: number,\n ): readonly [PaginatedWalkState<Cursor, Page>, readonly OutCmd[]] {\n // Only a `fetching` paginator has an outstanding page fetch to fail. On any\n // other phase the fail would corrupt shared resilience state for a fetch\n // that is not in flight — absorb it as a pure no-op.\n if (s.walk.phase !== \"fetching\") {\n return [s, []];\n }\n const [resilience, cmds] = rc.fail(s.resilience, PAGE_KEY, {\n type: MsgType.ResilientErr,\n key: PAGE_KEY,\n error,\n at,\n });\n return [withResilience(s, resilience), cmds];\n }\n\n // === Verb: onTimer =======================================================\n\n /**\n * A retry / deadline timer fired. Defers to resilient-call's `onTimer`: a\n * retry timer re-runs the gate for the page-fetch's remembered cursor (re-\n * issuing the SAME page); a deadline timer settles the resilient call failed.\n * The paginator stays parked on its cursor throughout — only `pageOk` advances\n * it. A stale fire is a no-op (inherited). PURE.\n *\n * GUARDED by the paginator phase, for the same reason as `pageErr`. A timer is\n * only meaningful while a page fetch is outstanding (`walk.phase ===\n * \"fetching\"`). A stray timer that fires after the walk has settled (`done` /\n * `paused` / `idle`) would otherwise re-enter `rc.onTimer` — re-issuing a fetch\n * the paginator no longer wants (a deadline timer could even overwrite the\n * settled `PAGE_KEY` slot with a deadline `failed`). On any non-`fetching`\n * phase the timer is absorbed as a pure no-op.\n */\n function onTimer(\n s: PaginatedWalkState<Cursor, Page>,\n msg: PaginatedWalkTimerMsg,\n ): readonly [PaginatedWalkState<Cursor, Page>, readonly OutCmd[]] {\n if (s.walk.phase !== \"fetching\") {\n return [s, []];\n }\n const [resilience, cmds] = rc.onTimer(s.resilience, msg);\n if (resilience === s.resilience) {\n // Inherited no-op identity → keep our slice identity too (pure no-op).\n return [s, []];\n }\n return [withResilience(s, resilience), cmds];\n }\n\n // === Verb: drain =========================================================\n\n /**\n * Acknowledge that the consumer finished processing `n` items, lowering the\n * paginator's backpressure gauge (`seen`). PURE — no clock. Delegates to\n * `paginator.drain`: subtracts `n` (floored at 0), leaves the phase untouched.\n *\n * The OTHER half of the backpressure valve `highWaterMark` opens: `recordPage`\n * parks the walk in `paused` once `seen >= highWaterMark`, and `drain` is how a\n * consumer lowers `seen` back below the mark so `resume` can re-open the fetch.\n * Without it `highWaterMark` would be a one-way valve — a walk could pause but\n * never continue. Emits no Cmds: draining only frees headroom; `resume` is the\n * separate, explicit \"continue now\" decision.\n */\n function drain(\n s: PaginatedWalkState<Cursor, Page>,\n n: number,\n ): readonly [PaginatedWalkState<Cursor, Page>, readonly OutCmd[]] {\n const walk = drainWalk(s.walk, n);\n if (walk === s.walk) {\n // Pure no-op identity (n <= 0, or seen already at floor).\n return [s, []];\n }\n return [{ ...s, walk }, []];\n }\n\n // === Verb: resume ========================================================\n\n /**\n * Re-open the backpressure valve after a `drain`: if the walk is `paused` and\n * `seen` has dropped below `highWaterMark`, re-arm the parked cursor and issue\n * its page fetch (gated through resilience). PURE — `at` threads into the fetch\n * gate. Delegates the phase decision to `paginator.resume`:\n *\n * - `paused` AND `seen < highWaterMark` → `fetching(cursor)` → emit the fetch.\n * - `paused` but still over the mark → no-op (the valve stays shut until a\n * further `drain` frees real headroom).\n * - any other phase → no-op (nothing to resume).\n *\n * Together with `drain` this completes the valve: `recordPage` shuts it\n * (`paused`), `drain` lowers the gauge, `resume` re-opens it and fires the next\n * page fetch — the same `fetch` helper `start` / `pageOk` use, so a resumed\n * fetch is gated identically (cache / circuit / rate-limit / retry).\n */\n function resume(\n s: PaginatedWalkState<Cursor, Page>,\n at: number,\n ): readonly [PaginatedWalkState<Cursor, Page>, readonly OutCmd[]] {\n // `resumeWalk` is a pure no-op (same reference) on any phase but `paused`, or\n // when `seen` is still at/over the mark. Only a fresh `fetching` arm means\n // the valve actually re-opened — issue the page fetch only then.\n return armAndFetch(s, resumeWalk(s.walk, paginatorPolicy), at);\n }\n\n // === Derived predicates ==================================================\n\n /**\n * Whether the walk has finished — the API returned a null next cursor and the\n * paginator is `done`. PURE, read-only. Lets a consumer's reducer fire a\n * \"walk complete\" Cmd (`if (walk.isDone(s.walk)) …`) without re-checking the\n * paginator phase by hand.\n */\n function isComplete(s: PaginatedWalkState<Cursor, Page>): boolean {\n return isDone(s.walk);\n }\n\n /**\n * The terminal page-fetch failure that stranded the walk, or `undefined` if no\n * page has terminally failed. PURE, read-only. Errors are data: a page that\n * exhausts its retries (or trips the deadline) settles the `PAGE_KEY` resilient\n * slot to `failed` while the paginator stays parked on its cursor — the walk is\n * stuck and will never advance on its own. Without a way to read that phase the\n * failure is unobservable: the walk simply stops emitting fetches with no\n * signal. `failure(s)` surfaces the settled error (the same plain-data sentinel\n * resilient-call records) so a consumer can fire a \"walk dead\" Cmd, alert, or\n * re-`start` a fresh walk.\n */\n function failure(s: PaginatedWalkState<Cursor, Page>): unknown {\n const call = s.resilience.calls[PAGE_KEY];\n return call?.phase === \"failed\" ? call.error : undefined;\n }\n\n /**\n * Whether the walk is STUCK: a page fetch has terminally `failed` yet the\n * paginator is still parked on a cursor (not `done`), so no further fetch will\n * ever be issued without intervention. PURE, read-only.\n *\n * This is the dead-walk predicate the consumer polls (`if (walk.isStuck(s)) …`)\n * to distinguish a healthy finish (`isComplete`) from a silent death. A walk is\n * stuck precisely when the resilient slot settled `failed` AND the paginator\n * has NOT reached `done` — a terminal failure on the final page that also\n * finished the walk is `isComplete`, not stuck. Pairs with `failure(s)`, which\n * hands back the error that stuck it.\n */\n function isStuck(s: PaginatedWalkState<Cursor, Page>): boolean {\n return s.resilience.calls[PAGE_KEY]?.phase === \"failed\" && !isDone(s.walk);\n }\n\n // === Subs ================================================================\n\n /**\n * Pre-wired subscriptions: exactly the resilient-call subs for the page\n * fetch — a retry timer while the fetch is `waiting_retry`, and (with the\n * `deadline` brick) a per-fetch deadline timer while it is active. Reconciled\n * by id, so a phase change cancels the matching timer automatically. Wire\n * `subscribe: { deadline: subscribeDeadline }` (re-exported below).\n */\n function subs(s: PaginatedWalkState<Cursor, Page>): readonly DeadlineSub[] {\n return rc.subs(s.resilience);\n }\n\n // === Handlers ============================================================\n\n /**\n * Pre-wired interpret handler for the page-fetch effect. Inherited from\n * resilient-call (`resilient_run`): wraps the consumer's `run(cursor)` port\n * via `tryInterpret` (Railway) — success → `resilient_ok` (handled by\n * `pageOk`), failure → `resilient_err` (handled by `pageErr`), each stamped\n * with `Date.now()` at the effect boundary (the ONE permitted clock read).\n *\n * interpret: walk.handlers({ run: (cursor) => api.fetchPage(cursor) })\n *\n * The consumer maps the inherited Msg types to their `pageOk` / `pageErr`\n * reducer cells (the wire shape is `resilient_ok` / `resilient_err`).\n */\n function handlers(ports: PaginatedWalkPorts<Cursor, Page>) {\n return rc.handlers(ports);\n }\n\n return {\n init,\n start,\n pageOk,\n pageErr,\n onTimer,\n drain,\n resume,\n isComplete,\n failure,\n isStuck,\n subs,\n handlers,\n };\n}\n\n/**\n * Lift a knob result `[slice, cmds]` into a host `[State, cmds]` where the slice\n * lives at `state.walk`. 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 liftWalk<\n S extends { walk: PaginatedWalkState<Cursor, Page> },\n Cursor,\n Page,\n C extends Cmd,\n>(\n state: S,\n [slice, cmds]: readonly [PaginatedWalkState<Cursor, Page>, readonly C[]],\n): readonly [S, readonly C[]] {\n return [{ ...state, walk: slice }, cmds];\n}\n\n/**\n * Re-export the deadline Sub primitives (inherited from resilient-call) so\n * consumers wire one import: `subscribeDeadline` is the `subscribe` cell,\n * `deadlineSub` builds the Sub literal this knob's `subs` emits.\n */\nexport { subscribeDeadline, deadlineSub };\nexport type { DeadlineSub };\n"]}
@@ -1,5 +1,5 @@
1
1
  import { safeId, safeLabel } from './chunk-UOC2PL46.js';
2
- import { Cmd, NoCellError } from './chunk-B5VXOFMZ.js';
2
+ import { Cmd, NoCellError } from './chunk-OOYD44JI.js';
3
3
 
4
4
  // src/chart/compile.ts
5
5
  var CellTargetError = class extends Error {
@@ -303,5 +303,5 @@ function chartMermaid(chart, opts = {}) {
303
303
  }
304
304
 
305
305
  export { CellTargetError, chartMermaid, compile, compileReducer, compileTable, edgeKey, initFrom, initialStateOf, reducerInitFrom, reducerMermaid };
306
- //# sourceMappingURL=chunk-E6GEDSF4.js.map
307
- //# sourceMappingURL=chunk-E6GEDSF4.js.map
306
+ //# sourceMappingURL=chunk-YWIKDKHY.js.map
307
+ //# sourceMappingURL=chunk-YWIKDKHY.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/chart/compile.ts"],"names":[],"mappings":"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","file":"chunk-E6GEDSF4.js","sourcesContent":["// ═══════════════════════════════════════════════════════════════════════════\n// RUNTIME — the chart walk that emits a real `Transitions<S, M, C>`.\n// ═══════════════════════════════════════════════════════════════════════════\nimport { safeId, safeLabel } from \"../machine-viz/mermaid-id\";\nimport { Cmd, NoCellError, type Reducer, type Transitions } from \"../pure/core\";\nimport type {\n Assigns,\n CellName,\n Cells,\n Chart,\n CmdOf,\n Cmds,\n GuardName,\n Guards,\n InitialData,\n InitialState,\n MsgIn,\n MsgOf,\n RAssigns,\n RCellName,\n RCells,\n RCmds,\n ReducerChart,\n RGuardName,\n RGuards,\n RStateOf,\n RUsedCmdName,\n StateOf,\n UsedCmdName,\n} from \"./graph\";\n\n/**\n * A cell returned a state OUTSIDE its edge's declared `to`.\n *\n * The escape hatch's bargain is that code may pick the target but only from the\n * set the chart admits — otherwise the drawing stops being a truthful picture\n * of the machine. For a SINGLE-site cell, and for the per-site form of a\n * multi-site one, the clamp is a compile error and this never fires. For the\n * function form of a MULTI-SITE cell it is the only enforcement there is: the\n * return of one rest signature over a union of tuples cannot be made to depend\n * on `at`, so the union of every site's `to` is the tightest static clamp\n * available and this closes the difference at the moment it is violated.\n *\n * It sits inside `buildCell`, so BOTH chart forms — the grid and the reducer —\n * get the same net from the same three lines.\n */\nexport class CellTargetError extends Error {\n override readonly name = \"CellTargetError\";\n readonly _tag = \"CellTargetError\" as const;\n constructor(\n public readonly at: string,\n public readonly cell: string,\n public readonly returned: string,\n public readonly declared: readonly string[],\n ) {\n super(\n `@demlik/tea: cell \"${cell}\" at edge \"${at}\" returned state ` +\n `\"${returned}\", which is not among that edge's declared targets ` +\n `[${declared.map((t) => `\"${t}\"`).join(\", \")}] — either the cell is ` +\n `wrong, or the edge's \\`to\\` is missing a target the code can reach ` +\n `(in which case add it, so the chart still draws the whole fan-out).`,\n );\n }\n}\n\n/** `undefined` → none; `\"x\"` → one; `[\"x\",\"y\"]` → both, in order. */\nfunction cmdNames(\n ref: string | readonly string[] | undefined,\n): readonly string[] {\n if (ref === undefined) return [];\n return typeof ref === \"string\" ? [ref] : ref;\n}\n\n// There is no `unhandled` policy any more. A pair is DECLARED (an edge), or\n// REFUSED — by the event's `scope`, or by this state's `ignore`, or by\n// `end: true` — and `Total<C>` refuses to compile on any third case. The\n// refusal is a self-loop with no cmds; the throw below is the safety net under\n// the compile-time refusal (`.decisions/0011`), reached only when the mapped\n// types were bypassed with a cast.\nexport type Parts<C, S extends { type: string }, M extends { type: string }> = {\n readonly assign: Assigns<C, S, M>;\n} & ([GuardName<C>] extends [never]\n ? { readonly guards?: undefined }\n : { readonly guards: Guards<C, S, M> }) &\n ([UsedCmdName<C>] extends [never]\n ? { readonly cmds?: undefined }\n : { readonly cmds: Cmds<C, S, M> }) &\n // the escape hatch's bag — demanded exactly when an edge names a cell.\n ([CellName<C>] extends [never]\n ? { readonly cells?: undefined }\n : { readonly cells: Cells<C, S, M> });\n\n// ── runtime views of the chart (the shapes the walk actually reads) ────────\ntype RtEdge =\n | string\n | {\n readonly target?: string;\n readonly when?: string;\n readonly otherwise?: string;\n readonly cmd?: string | readonly string[];\n readonly otherwiseCmd?: string | readonly string[];\n readonly resume?: { readonly fallback: string };\n readonly to?: readonly string[];\n readonly cell?: string;\n };\ntype RtNode = {\n readonly initial?: true;\n readonly on?: Record<string, RtEdge>;\n readonly ignore?: readonly string[];\n // BOTH polarities, because both are final and only one of them is a success.\n // The walk reads `!== undefined` (either is terminal); the drawing reads the\n // value, which is the whole reason it is not narrowed to `true` here.\n readonly end?: true | \"error\";\n};\ntype RtChart = {\n readonly events: Record<\n string,\n { readonly scope: string | readonly string[]; readonly foreign?: true }\n >;\n readonly states: Record<string, Record<string, RtNode>>;\n};\ntype RtState = { readonly type: string; readonly was?: string };\ntype RtMsg = { readonly type: string };\ntype RtCell = (s: RtState, m: RtMsg) => readonly [RtState, readonly Cmd[]];\ntype RtCellFn = (\n s: RtState,\n m: RtMsg,\n at: string,\n) => readonly [RtState, readonly Cmd[]];\n/** Either form: one body for every site, or one body PER site keyed by `at`. */\ntype RtCellImpl = RtCellFn | Record<string, RtCellFn | undefined>;\ntype RtFn = (s: RtState, m: RtMsg) => object;\ntype RtGuard = (s: RtState, m: RtMsg, at: string) => boolean;\ntype RtAssign = RtFn | { readonly then: RtFn; readonly else: RtFn };\n\n/** State name → its node and its phase, flattened out of the grouped shape. */\ntype Flat = { readonly node: RtNode; readonly group: string };\n\nfunction flatten(c: RtChart): Map<string, Flat> {\n const out = new Map<string, Flat>();\n for (const group of Object.keys(c.states)) {\n const members = c.states[group] ?? {};\n for (const s of Object.keys(members)) {\n const node = members[s];\n if (node !== undefined) out.set(s, { node, group });\n }\n }\n return out;\n}\n\nfunction scopeList(scope: string | readonly string[]): readonly string[] {\n return typeof scope === \"string\" ? [scope] : scope;\n}\n\n/** The code parts, after the type layer has been erased. Shared by both forms. */\ntype RtParts = {\n readonly assign: Record<string, RtAssign | undefined>;\n readonly guards: Record<string, RtGuard | undefined>;\n readonly cmds: Record<\n string,\n ((s: RtState, m: RtMsg, at: string) => object) | undefined\n >;\n readonly cells: Record<string, RtCellImpl | undefined>;\n};\n\nfunction rtParts(parts: object): RtParts {\n const p = parts as Record<string, object | undefined>;\n return {\n assign: (p.assign ?? {}) as RtParts[\"assign\"],\n guards: (p.guards ?? {}) as RtParts[\"guards\"],\n cmds: (p.cmds ?? {}) as RtParts[\"cmds\"],\n cells: (p.cells ?? {}) as RtParts[\"cells\"],\n };\n}\n\n/**\n * ONE edge → one transition cell. This is the whole walk, and it is shared\n * verbatim by both chart forms: a `Transitions` cell and a `Reducer` cell\n * differ only in what `at` is (`\"state.event\"` vs `\"event\"`) and in whether a\n * parking set exists to inject `was` from — both parameters here, not branches.\n *\n * `bare` is the un-namespaced event name the author's parts are written\n * against; the compiled cell restores it before calling them.\n */\nfunction buildCell(\n spec: RtEdge,\n bare: string,\n at: string,\n p: RtParts,\n parking: ReadonlySet<string>,\n): RtCell {\n const edge = typeof spec === \"string\" ? { target: spec } : spec;\n const cell = p.assign[at];\n\n // ── THE ESCAPE HATCH ────────────────────────────────────────────────────\n // A `{ to, cell }` edge is the whole transition: the cell picks the target\n // from `to` and returns its own cmds, so there is no assign to call, no\n // guard to evaluate, no cmd list to rebuild and no `was` to inject (a cell\n // landing on a parking state supplies `was` itself — its return type demands\n // it). The other fields are compile errors beside `cell`, so this branch is\n // not silently skipping anything.\n if (edge.cell !== undefined) {\n const name = edge.cell;\n const impl = p.cells[name];\n if (impl === undefined) {\n throw new Error(\n `@demlik/tea: edge \"${at}\" names cell \"${name}\" with no implementation`,\n );\n }\n // the two forms: one body for every site, or one body per site. A\n // multi-site cell may be written either way (`Cells`/`RCells` offer both);\n // a single-site one is always the plain function.\n const hand = typeof impl === \"function\" ? impl : impl[at];\n if (hand === undefined) {\n throw new Error(\n `@demlik/tea: cell \"${name}\" is written in the per-site form but has no entry for edge \"${at}\"`,\n );\n }\n const to = edge.to ?? [];\n return (st, nsMsg) => {\n const out = hand(st, { ...nsMsg, type: bare }, at);\n // the runtime half of the `to` clamp — see `CellTargetError`.\n if (!to.includes(out[0].type)) {\n throw new CellTargetError(at, name, out[0].type, to);\n }\n return out;\n };\n }\n\n // A guarded edge is resolved HERE, not at dispatch — same as `cell` above and\n // for the same reason: an unimplemented name is a wiring mistake, and a\n // wiring mistake should surface when the chart is compiled rather than on the\n // first message that happens to reach this edge.\n //\n // It is the only one of the three that ever read a missing part as a VALUE.\n // A missing cmd builder throws, a missing cell throws, and a missing guard\n // used to quietly evaluate to `false` — which is not an error, it is the\n // `otherwise` arm. On the imported door (`chart/lane`) the guard alphabet is\n // `string`, so no type ever checked the name either: a typo in an operator's\n // parts routed the whole lane down its failure arm in silence.\n const guard = edge.when === undefined ? undefined : p.guards[edge.when];\n if (edge.when !== undefined && guard === undefined) {\n throw new Error(\n `@demlik/tea: edge \"${at}\" names guard \"${edge.when}\" with no implementation`,\n );\n }\n\n return (st, nsMsg) => {\n // strip the namespace so the author's parts see the bare event.\n const msg: RtMsg = { ...nsMsg, type: bare };\n\n let target: string;\n let payloadFn: RtFn;\n let fired = true;\n\n if (edge.resume !== undefined) {\n target = st.was ?? edge.resume.fallback;\n payloadFn = cell as RtFn;\n } else if (guard !== undefined) {\n fired = guard(st, msg, at) === true;\n const branch = cell as { then: RtFn; else: RtFn };\n target = fired ? (edge.target as string) : (edge.otherwise as string);\n payloadFn = fired ? branch.then : branch.else;\n } else {\n target = edge.target as string;\n payloadFn = cell as RtFn;\n }\n\n const data = payloadFn(st, msg);\n // `was` is INJECTED, never authored: entering a parking state records where\n // you came from. The type of `was` is `ResumeTargets<C, target>`, and\n // `st.type` is in that set by construction.\n const next: RtState = parking.has(target)\n ? { ...data, type: target, was: st.type }\n : { ...data, type: target };\n\n // 0..n Cmds, in declaration order. A guarded edge picks its arm's list —\n // `Cmd.when` lifted onto the edge, so which effects fire is visible in the\n // chart rather than buried in a cell body.\n const emitted = cmdNames(fired ? edge.cmd : edge.otherwiseCmd).map(\n (n): Cmd => {\n const build = p.cmds[n];\n if (build === undefined) {\n throw new Error(\n `@demlik/tea: edge \"${at}\" names cmd \"${n}\" with no builder`,\n );\n }\n return { ...build(st, msg, at), type: n };\n },\n );\n\n return [next, emitted];\n };\n}\n\n/** `undefined` → bare; a foreign event → bare; otherwise `${ns}.${event}`. */\nfunction keyOf(\n c: {\n readonly events: Record<string, { readonly foreign?: true } | undefined>;\n },\n e: string,\n ns: string | undefined,\n): string {\n return ns === undefined || c.events[e]?.foreign === true ? e : `${ns}.${e}`;\n}\n\nconst NO_PARKING: ReadonlySet<string> = new Set<string>();\n\n/**\n * One compiled table, BEFORE the typed boundary — `state.type` → the event key\n * → the cell.\n *\n * The shape `compile` builds and then declares as a `Transitions<S, M, C>`. It\n * is named because a second caller needs the walk without the declaration:\n * `chart/lane/run` compiles ONE region per task and the tables it holds are\n * keyed by task id, so the per-region `Transitions` type is never the type of\n * anything it stores.\n */\nexport type CompiledTable = Readonly<\n Record<string, Readonly<Record<string, RtCell | undefined>> | undefined>\n>;\n\n/**\n * THE WALK, with no type layer over it — `compile`'s body, callable by a second\n * compiler that has already done its own type checking.\n *\n * `chart`/`parts` are `object` rather than the F-bounded `Chart<C>`/`Parts<…>`\n * because this entry is not where the checking happens; it is where the\n * checking has ALREADY happened. The erasing cast lives here, exactly once, at\n * the top of the walk — which is where it lived before this function had a\n * name, so nothing gained a cast by being reachable from two places.\n */\nexport function compileTable(\n chart: object,\n parts: object,\n ns: string | undefined,\n): CompiledTable {\n const c = chart as unknown as RtChart;\n const p = rtParts(parts);\n\n const flat = flatten(c);\n const events = Object.keys(c.events);\n const parking = new Set<string>();\n for (const [s, { node }] of flat) {\n for (const spec of Object.values(node.on ?? {})) {\n if (typeof spec === \"object\" && spec.resume !== undefined) parking.add(s);\n }\n }\n\n const table: Record<string, Record<string, RtCell>> = {};\n\n for (const [s, { node, group }] of flat) {\n const row: Record<string, RtCell> = {};\n const on = node.on ?? {};\n\n for (const e of events) {\n // the one runtime consequence of per-event namespacing.\n const key = keyOf(c, e, ns);\n const spec = on[e];\n\n if (spec === undefined) {\n // the mirror of `MissingAt<C, S>`: live-but-undecided is the only case\n // the type layer forbids, so it is the only case that throws.\n const scope = scopeList(c.events[e]?.scope ?? \"edges\");\n const live = scope.includes(\"all\") || scope.includes(group);\n // `end` is `true` (success) or `\"error\"`; BOTH are final, and the type\n // layer's `IsEndOf` reads both. Testing `=== true` here would put an\n // error final back under the totality obligation at runtime only.\n const refused =\n node.end !== undefined || !live || (node.ignore ?? []).includes(e);\n row[key] = refused\n ? (st) => [st, []]\n : () => {\n throw new NoCellError(key, s);\n };\n continue;\n }\n\n // the site tag, passed to guards/cmds/cells as their last argument — it is\n // what lets a multi-site helper discriminate (see `SiteArgs` in graph.ts).\n row[key] = buildCell(spec, e, `${s}.${e}`, p, parking);\n }\n table[s] = row;\n }\n\n return table;\n}\n\n/**\n * Compile a chart + the code parts into a genuine `Transitions<S, M, C>`.\n *\n * `ns` is OPTIONAL and PER-EVENT. Omitted, nothing is decorated and the table\n * is keyed by the bare event names — a single-instance machine carries no\n * namespace and passes no dummy string. Given, the author's own events are\n * keyed `${ns}.${event}` (so N instances share one dispatch surface with\n * genuinely disjoint literal unions), while every event the chart marks\n * `foreign: true` keeps its BARE name: a library-minted Msg like\n * `deadline_exceeded` is the same event for every instance, and its name was\n * never the author's to rename.\n *\n * The returned type is `Transitions<S, MsgIn<C, NS>, K>` — literal keys, never\n * `string`. The parts are authored against the BARE msg union; the compiled\n * cell restores the bare event name before calling them.\n *\n * `S`/`M`/`K` DEFAULT to the chart's own derivations, so the common call is\n * `compile(chart, parts)` with no type arguments at all.\n */\nexport function compile<\n const C extends Chart<C>,\n S extends { type: string } = StateOf<C>,\n M extends { type: string } = MsgOf<C>,\n K extends Cmd = CmdOf<C>,\n const NS extends string | undefined = undefined,\n>(chart: C, parts: Parts<C, S, M>, ns?: NS): Transitions<S, MsgIn<C, NS>, K> {\n // ── THE ONE CAST ────────────────────────────────────────────────────────\n // Inside the library, at the construction boundary. `Transitions` is a\n // mapped type over `S[\"type\"] × M[\"type\"]`; the walk builds the same keys\n // from the flattened chart × the per-event keys, but tsc cannot see that a\n // string-keyed record built in a loop is total over those unions.\n return compileTable(chart, parts, ns) as unknown as Transitions<\n S,\n MsgIn<C, NS>,\n K\n >;\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// REDUCER FORM — the same walk, one loop shallower.\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * The reducer form's parts bag. Structurally identical to `Parts`, over the\n * reducer chart's derivations — `assign` is total over the DECLARATIVE events\n * (a cell event owes none), and each of `guards`/`cmds`/`cells` is demanded\n * exactly when the chart names one.\n */\nexport type RParts<\n C,\n S extends { type: string },\n M extends { type: string },\n> = {\n readonly assign: RAssigns<C, S, M>;\n} & ([RGuardName<C>] extends [never]\n ? { readonly guards?: undefined }\n : { readonly guards: RGuards<C, S, M> }) &\n ([RUsedCmdName<C>] extends [never]\n ? { readonly cmds?: undefined }\n : { readonly cmds: RCmds<C, S, M> }) &\n ([RCellName<C>] extends [never]\n ? { readonly cells?: undefined }\n : { readonly cells: RCells<C, S, M> });\n\ntype RtReducerChart = {\n readonly events: Record<string, { readonly foreign?: true }>;\n readonly initial: string;\n readonly on: Record<string, RtEdge>;\n};\n\n/**\n * Compile a reducer-form chart into a genuine `Reducer<S, M, K>` — the flat,\n * msg-keyed `update` shape `defineMachine` already accepts (`src/pure/core.ts`).\n *\n * There is no `NoCellError` branch and no refusal branch here, and that is not\n * an omission: `on` is a TOTAL mapped type over the event alphabet, so an\n * undeclared event cannot exist to be missing. The safety the grid form buys\n * with `scope` + `ignore` + `Total<C>`, this form gets from the mapped type.\n *\n * `ns` behaves exactly as in `compile`: per-event, optional, and never applied\n * to an event the chart marks `foreign`.\n */\nexport function compileReducer<\n const C extends ReducerChart<C>,\n S extends { type: string } = RStateOf<C>,\n M extends { type: string } = MsgOf<C>,\n K extends Cmd = CmdOf<C>,\n const NS extends string | undefined = undefined,\n>(chart: C, parts: RParts<C, S, M>, ns?: NS): Reducer<S, MsgIn<C, NS>, K> {\n const c = chart as unknown as RtReducerChart;\n const p = rtParts(parts);\n const out: Record<string, RtCell> = {};\n\n for (const e of Object.keys(c.events)) {\n const spec = c.on[e];\n if (spec === undefined) {\n // unreachable through the typed door — `on` is total over `events`. The\n // safety net under the compile-time obligation, as `NoCellError` is\n // under `Total<C>`.\n throw new Error(`@demlik/tea: reducer chart declares no edge for \"${e}\"`);\n }\n // the site tag IS the event: one dimension, so `SiteArgs`'s `at` is the\n // bare name and a multi-site cell discriminates on it exactly as before.\n out[keyOf(c, e, ns)] = buildCell(spec, e, e, p, NO_PARKING);\n }\n\n // The same one cast, for the same reason: a string-keyed record built in a\n // loop is total over `M[\"type\"]`, and tsc cannot see it.\n return out as unknown as Reducer<S, MsgIn<C, NS>, K>;\n}\n\n/**\n * `init` for a reducer chart. The entry state is the chart's `initial` field —\n * one word, still never repeated by the author — and `boot()` supplies `ctx`,\n * which for this form IS the whole state minus its tag.\n */\nexport function reducerInitFrom<\n const C extends ReducerChart<C>,\n S extends { type: string } = RStateOf<C>,\n K extends Cmd = CmdOf<C>,\n>(\n chart: C,\n boot: () => Omit<RStateOf<C>, \"type\">,\n): (loaded: S | null) => readonly [S, readonly K[]] {\n const type: string = (chart as unknown as RtReducerChart).initial;\n return (loaded) => [\n loaded ?? ({ ...boot(), type } as unknown as S),\n Cmd.none,\n ];\n}\n\n/**\n * The reducer chart, drawn. There is no per-state routing to show, so the\n * honest picture is one `any` node — every edge is reachable from every state,\n * which is what having no phase dimension MEANS — plus each event's full\n * fan-out, cell edges included.\n */\nexport function reducerMermaid<const C extends ReducerChart<C>>(\n chart: C,\n): string {\n const c = chart as unknown as RtReducerChart;\n const lines = [\n \"stateDiagram-v2\",\n \" direction TB\",\n ` [*] --> ${safeId(c.initial)}`,\n ];\n // The SAME edge grammar `chartMermaid` draws — see {@link edgeLines}. The one\n // difference this form has is `from`, which is `any` at every edge, because\n // having no phase dimension MEANS every edge is reachable from every state.\n for (const [e, spec] of Object.entries(c.on)) {\n lines.push(...edgeLines(\"any\", \"any\", e, spec, NO_MARKS));\n }\n return lines.join(\"\\n\");\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// `init`, DERIVED FROM THE CHART\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * The name of the state marked `initial: true`. Throws unless EXACTLY one is\n * marked — the runtime half of what `InitialData`'s marker types say at the\n * type level.\n */\nexport function initialStateOf<const C extends Chart<C>>(\n chart: C,\n): InitialState<C> {\n const flat = flatten(chart as unknown as RtChart);\n const marked = [...flat].filter(([, f]) => f.node.initial === true);\n const only = marked[0];\n if (marked.length !== 1 || only === undefined) {\n throw new Error(\n `@demlik/tea: chart must mark exactly one state \\`initial: true\\` (found ${marked.length})`,\n );\n }\n // `Object.keys` yields `string`; the type-level answer is the same key.\n return only[0] as InitialState<C>;\n}\n\n/**\n * Build a `Machine[\"init\"]` from the chart's declared entry state plus a\n * `boot()` that supplies ONLY that state's data. The state NAME is never\n * repeated by the author — it comes from the same `initial: true` that\n * `machine-viz` reads to draw the `[*] -->` edge.\n *\n * Rehydrate is honoured verbatim: a non-null `loaded` is returned untouched\n * with NO cmds, which is exactly Invariant 2's contract.\n */\nexport function initFrom<\n const C extends Chart<C>,\n S extends { type: string },\n K extends Cmd,\n>(\n chart: C,\n boot: () => InitialData<C, S>,\n): (loaded: S | null) => readonly [S, readonly K[]] {\n const type: string = initialStateOf(chart);\n return (loaded) => [\n // The one cast this helper needs: `{ ...InitialData, type: <the initial\n // name> }` IS `Extract<S, { type: InitialState<C> }>`, but tsc cannot\n // rebuild a union member from a spread plus a `string`-typed discriminant.\n loaded ?? ({ ...boot(), type } as unknown as S),\n Cmd.none,\n ];\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// DRAWING THE CHART — every declared edge, including a cell edge's fan-out.\n//\n// `machine-viz`'s `toMermaid` works on a COMPILED machine and resolves a cell\n// by executing it against a sample, so it draws the ONE edge that sample takes\n// — true of a guarded edge before the escape hatch existed, and equally true\n// of a `{ to, cell }` edge now. The chart, though, still knows every possible\n// target, which is the whole point of declaring `to`. This reads the chart\n// directly and draws the full fan-out, no samples and no execution needed.\n//\n// THIS IS THE ONLY CHART RENDERER. `chart/report/draw.ts` used to carry a\n// second one, written in parallel against the same mermaid grammar while this\n// one was growing its options bag; the two diverged before the swap its banner\n// promised ever happened (a guarded then-arm drawn bare here and labelled\n// there, two spellings of the highlight class, sanitizing on one side only).\n// How a chart is drawn is ONE fact, so there is one body and the report enters\n// it through `drawTask`, which is now nothing but a translation of its option\n// names into these.\n//\n// WHAT MERMAID CAN AND CANNOT DO, stated so `walked` is judged fairly:\n// `stateDiagram-v2` supports `classDef`/`class`, so LIGHTING A NODE is real\n// styling. It supports no per-edge styling at all — there is no `linkStyle` for\n// a state diagram — so a walked edge is marked ON ITS LABEL (`»`, plus the\n// visit count) rather than thickened. Still true as of mermaid 11.\n// ═══════════════════════════════════════════════════════════════════════════\n\n/** How many times each {@link edgeKey} was walked. */\nexport type WalkCounts = ReadonlyMap<string, number>;\n\n/**\n * The key a {@link ChartMermaidOptions.walked} map is read by.\n *\n * One spelling, shared by whoever counts the walk and the drawing that marks\n * it — the names are the chart's OWN, unsanitized, because the counter reads\n * them off a log and has no business knowing what mermaid can spell.\n *\n * NOT `EdgeKey<C>` (`graph.ts`), despite the casing. That one is the SITE tag\n * — `\"state.event\"` — which is what a guard or a cmd builder is keyed by,\n * because a site is where code hangs. This one names a drawn ARROW, and a\n * guarded site draws two of them, so it needs the target as well.\n */\nexport const edgeKey = (from: string, event: string, to: string): string =>\n `${from}|${event}|${to}`;\n\n/**\n * Options for {@link chartMermaid}. Every field is optional and every default\n * reproduces the drawing this function emitted before options existed — a flat\n * node set, `direction TB`, no title, no highlight, no polarity and no walk —\n * so an existing caller is untouched.\n *\n * Shaped after `MachineVizOptions` (`../machine-viz`) where the two overlap\n * (`direction`, `title`), because the two drawings are read side by side and a\n * second spelling of the same knob is a fact said twice. What is NOT shared is\n * `samples`: `toMermaid` needs them because it must EXECUTE a compiled cell to\n * learn a target; the chart declares its targets, so there is nothing to\n * resolve and nothing to sample.\n */\nexport interface ChartMermaidOptions {\n /**\n * Draw this state as the ACTIVE node — a `classDef`/`class` pair Mermaid\n * renders with the highlight fill. Pass the live `state.type`. A name that is\n * not in the chart is ignored rather than drawn as a phantom node.\n */\n readonly highlight?: string;\n /**\n * Draw each phase as a Mermaid COMPOSITE state (`state working { … }`).\n *\n * The chart groups its states into named phases and the flat drawing throws\n * that away — which is the one structural fact `chartMermaid` knew and did\n * not show. Off by default: turning it on changes the emitted text, and the\n * drawing is something a reviewer approves rather than something that moves\n * under them.\n */\n readonly phases?: boolean;\n /**\n * Draw the two ENDINGS differently — a dashed outline on every `end:\n * \"error\"` final, a heavier one on every `end: true`.\n *\n * A success final and an error final both terminate, so both draw the `[*]`\n * edge, and a picture that stopped there paints `shipped` and `frozen` alike\n * in the one place a reader looks first. Off by default for the same reason\n * `phases` is: it changes the emitted text.\n */\n readonly polarity?: boolean;\n /**\n * Edges the caller actually WALKED, keyed by {@link edgeKey} — each drawn\n * edge that has a count picks up a `»` (or `»×N`) on its label.\n *\n * A missing or zero count marks nothing, so a chart nobody has run through\n * draws exactly as it does without this option.\n */\n readonly walked?: WalkCounts;\n /** Mermaid layout direction. Default `\"TB\"`. */\n readonly direction?: \"TB\" | \"LR\";\n /** Optional title, emitted as a Mermaid front-matter `title:` block. */\n readonly title?: string;\n}\n\n/** The CSS class Mermaid applies to the highlighted node. */\nconst ACTIVE_CLASS = \"teaActive\";\n/** The highlighted node when it is where a task DIED — see `chartMermaid`. */\nconst ACTIVE_ERROR_CLASS = \"teaActiveError\";\n/** …and to the two endings, when `polarity` asks for them. */\nconst TRIPPED_CLASS = \"teaTripped\";\nconst SHIPPED_CLASS = \"teaShipped\";\n\n/**\n * A node's declaration line, or `undefined` when the sanitized id already IS\n * the name and the bare id carries the whole fact. Emitting the label only for\n * a renamed node is what keeps the default drawing byte-identical for charts\n * whose names were already Mermaid-safe.\n */\nfunction nodeDecl(raw: string, indent: string): string | undefined {\n const id = safeId(raw);\n return id === raw ? undefined : `${indent}${id} : ${safeLabel(raw)}`;\n}\n\n/**\n * ONE edge, as the arrows that draw it — the whole edge grammar, in one place.\n *\n * Both drawings in this file come through here: the grid form draws it from\n * each state, the reducer form from the single `any` node, and the two forms\n * differ in what `from` is and in nothing else. That is not tidiness — the\n * guarded arm's labelling was wrong in both of them, in the same way, and one\n * of them was fixed once already without the other moving.\n *\n * `at` is the UNSANITIZED source-state name the walk counter keyed by; `from`\n * is what mermaid will read.\n */\nfunction edgeLines(\n from: string,\n at: string,\n e: string,\n spec: RtEdge,\n mark: (from: string, event: string, to: string) => string,\n): readonly string[] {\n const edge = typeof spec === \"string\" ? { target: spec } : spec;\n const out: string[] = [];\n // ONE arrow-writer for every edge form, so the walk mark cannot be\n // remembered on three of the four and forgotten on the fourth.\n const arrow = (to: string, label: string): void => {\n out.push(\n ` ${from} --> ${safeId(to)} : ${safeLabel(`${label}${mark(at, e, to)}`)}`,\n );\n };\n if (edge.cell !== undefined) {\n // one real edge per DECLARED target — the fan-out, labelled with the cell\n // that picks among them.\n for (const t of edge.to ?? []) arrow(t, `${e} / ${edge.cell}()`);\n return out;\n }\n if (edge.resume !== undefined) {\n arrow(edge.resume.fallback, `${e} (resume)`);\n return out;\n }\n // A GUARDED edge labels BOTH arms. Drawing the then-arm bare — `review -->\n // build : FAIL` beside `review --> frozen : FAIL [!retriesRemaining]` —\n // reads as an unconditional edge racing a conditional one, which is not what\n // the chart says: the then-arm is exactly as conditional as the else-arm,\n // and the reader is left to infer its guard from a sibling arrow.\n const guard = edge.when === undefined ? \"\" : ` [${edge.when}]`;\n if (edge.target !== undefined) arrow(edge.target, `${e}${guard}`);\n if (edge.otherwise !== undefined)\n arrow(edge.otherwise, `${e} [!${edge.when ?? \"\"}]`);\n return out;\n}\n\n/** No walk to mark — the drawings that take no `walked` map. */\nconst NO_MARKS = (): string => \"\";\n\nexport function chartMermaid<const C extends Chart<C>>(\n chart: C,\n opts: ChartMermaidOptions = {},\n): string {\n const c = chart as unknown as RtChart;\n const flat = flatten(c);\n const lines: string[] = [];\n if (opts.title !== undefined)\n lines.push(\"---\", `title: ${opts.title}`, \"---\");\n lines.push(\"stateDiagram-v2\", ` direction ${opts.direction ?? \"TB\"}`);\n\n // The walk mark, keyed by the chart's OWN names — `walked` is counted off a\n // log, so it never saw a sanitized id.\n const walked = opts.walked;\n const mark = (from: string, event: string, to: string): string => {\n const n = walked?.get(edgeKey(from, event, to)) ?? 0;\n return n === 0 ? \"\" : n === 1 ? \" »\" : ` »×${n}`;\n };\n\n // Phases, as composite states. Declared BEFORE the edges so every node is\n // already inside its phase by the time an arrow references it — Mermaid\n // otherwise hoists the first mention to the top level.\n if (opts.phases === true) {\n for (const [group, members] of Object.entries(c.states)) {\n lines.push(` state ${safeId(group)} {`);\n for (const s of Object.keys(members)) {\n lines.push(nodeDecl(s, \" \") ?? ` ${safeId(s)}`);\n }\n lines.push(\" }\");\n }\n }\n\n for (const [s, { node }] of flat) {\n const from = safeId(s);\n // A renamed node needs its human name shown once. In `phases` mode the\n // declaration already went inside the composite block.\n if (opts.phases !== true) {\n const decl = nodeDecl(s, \" \");\n if (decl !== undefined) lines.push(decl);\n }\n if (node.initial === true) lines.push(` [*] --> ${from}`);\n for (const [e, spec] of Object.entries(node.on ?? {})) {\n lines.push(...edgeLines(from, s, e, spec, mark));\n }\n // Both polarities terminate, so both draw the `[*]` edge — an error final\n // that stopped drawing one would read as a state the machine can leave.\n if (node.end !== undefined) lines.push(` ${from} --> [*]`);\n }\n\n // The classes, last: a `classDef` must exist before the `class` line that\n // uses it, and both must sit outside any composite block. Each pair is\n // emitted only when something wears it, so an unused `classDef` never lands.\n if (opts.polarity === true) {\n const tripped: string[] = [];\n const shipped: string[] = [];\n for (const [s, { node }] of flat) {\n if (node.end === \"error\") tripped.push(safeId(s));\n if (node.end === true) shipped.push(safeId(s));\n }\n if (tripped.length > 0) {\n lines.push(\n ` classDef ${TRIPPED_CLASS} stroke-dasharray:4 4`,\n ` class ${tripped.join(\",\")} ${TRIPPED_CLASS}`,\n );\n }\n if (shipped.length > 0) {\n lines.push(\n ` classDef ${SHIPPED_CLASS} stroke-width:2px`,\n ` class ${shipped.join(\",\")} ${SHIPPED_CLASS}`,\n );\n }\n }\n if (opts.highlight !== undefined && flat.has(opts.highlight)) {\n // The lit node carries POLARITY too, when polarity is on. `polarity` draws\n // the two endings apart by STROKE and the highlight then painted a fill\n // over the top, so an error final and a success final were the same picture\n // at the exact moment one of them is where a task DIED. Every other surface\n // — the chip, the badge, the stuck panel — says which; the drawing is the\n // thing a reader looks at first and it was the one saying nothing.\n const lit = flat.get(opts.highlight)?.node;\n const died = opts.polarity === true && lit?.end === \"error\";\n const cls = died ? ACTIVE_ERROR_CLASS : ACTIVE_CLASS;\n const paint = died ? \"#f85149\" : \"#2f81f7\";\n lines.push(\n ` classDef ${cls} fill:${paint},stroke:${paint},color:#fff,font-weight:bold`,\n ` class ${safeId(opts.highlight)} ${cls}`,\n );\n }\n return lines.join(\"\\n\");\n}\n"]}
1
+ {"version":3,"sources":["../src/chart/compile.ts"],"names":[],"mappings":"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","file":"chunk-YWIKDKHY.js","sourcesContent":["// ═══════════════════════════════════════════════════════════════════════════\n// RUNTIME — the chart walk that emits a real `Transitions<S, M, C>`.\n// ═══════════════════════════════════════════════════════════════════════════\nimport { safeId, safeLabel } from \"../machine-viz/mermaid-id\";\nimport { Cmd, NoCellError, type Reducer, type Transitions } from \"../pure/core\";\nimport type {\n Assigns,\n CellName,\n Cells,\n Chart,\n CmdOf,\n Cmds,\n GuardName,\n Guards,\n InitialData,\n InitialState,\n MsgIn,\n MsgOf,\n RAssigns,\n RCellName,\n RCells,\n RCmds,\n ReducerChart,\n RGuardName,\n RGuards,\n RStateOf,\n RUsedCmdName,\n StateOf,\n UsedCmdName,\n} from \"./graph\";\n\n/**\n * A cell returned a state OUTSIDE its edge's declared `to`.\n *\n * The escape hatch's bargain is that code may pick the target but only from the\n * set the chart admits — otherwise the drawing stops being a truthful picture\n * of the machine. For a SINGLE-site cell, and for the per-site form of a\n * multi-site one, the clamp is a compile error and this never fires. For the\n * function form of a MULTI-SITE cell it is the only enforcement there is: the\n * return of one rest signature over a union of tuples cannot be made to depend\n * on `at`, so the union of every site's `to` is the tightest static clamp\n * available and this closes the difference at the moment it is violated.\n *\n * It sits inside `buildCell`, so BOTH chart forms — the grid and the reducer —\n * get the same net from the same three lines.\n */\nexport class CellTargetError extends Error {\n override readonly name = \"CellTargetError\";\n readonly _tag = \"CellTargetError\" as const;\n constructor(\n public readonly at: string,\n public readonly cell: string,\n public readonly returned: string,\n public readonly declared: readonly string[],\n ) {\n super(\n `@demlik/tea: cell \"${cell}\" at edge \"${at}\" returned state ` +\n `\"${returned}\", which is not among that edge's declared targets ` +\n `[${declared.map((t) => `\"${t}\"`).join(\", \")}] — either the cell is ` +\n `wrong, or the edge's \\`to\\` is missing a target the code can reach ` +\n `(in which case add it, so the chart still draws the whole fan-out).`,\n );\n }\n}\n\n/** `undefined` → none; `\"x\"` → one; `[\"x\",\"y\"]` → both, in order. */\nfunction cmdNames(\n ref: string | readonly string[] | undefined,\n): readonly string[] {\n if (ref === undefined) return [];\n return typeof ref === \"string\" ? [ref] : ref;\n}\n\n// There is no `unhandled` policy any more. A pair is DECLARED (an edge), or\n// REFUSED — by the event's `scope`, or by this state's `ignore`, or by\n// `end: true` — and `Total<C>` refuses to compile on any third case. The\n// refusal is a self-loop with no cmds; the throw below is the safety net under\n// the compile-time refusal (`.decisions/0011`), reached only when the mapped\n// types were bypassed with a cast.\nexport type Parts<C, S extends { type: string }, M extends { type: string }> = {\n readonly assign: Assigns<C, S, M>;\n} & ([GuardName<C>] extends [never]\n ? { readonly guards?: undefined }\n : { readonly guards: Guards<C, S, M> }) &\n ([UsedCmdName<C>] extends [never]\n ? { readonly cmds?: undefined }\n : { readonly cmds: Cmds<C, S, M> }) &\n // the escape hatch's bag — demanded exactly when an edge names a cell.\n ([CellName<C>] extends [never]\n ? { readonly cells?: undefined }\n : { readonly cells: Cells<C, S, M> });\n\n// ── runtime views of the chart (the shapes the walk actually reads) ────────\ntype RtEdge =\n | string\n | {\n readonly target?: string;\n readonly when?: string;\n readonly otherwise?: string;\n readonly cmd?: string | readonly string[];\n readonly otherwiseCmd?: string | readonly string[];\n readonly resume?: { readonly fallback: string };\n readonly to?: readonly string[];\n readonly cell?: string;\n };\ntype RtNode = {\n readonly initial?: true;\n readonly on?: Record<string, RtEdge>;\n readonly ignore?: readonly string[];\n // BOTH polarities, because both are final and only one of them is a success.\n // The walk reads `!== undefined` (either is terminal); the drawing reads the\n // value, which is the whole reason it is not narrowed to `true` here.\n readonly end?: true | \"error\";\n};\ntype RtChart = {\n readonly events: Record<\n string,\n { readonly scope: string | readonly string[]; readonly foreign?: true }\n >;\n readonly states: Record<string, Record<string, RtNode>>;\n};\ntype RtState = { readonly type: string; readonly was?: string };\ntype RtMsg = { readonly type: string };\ntype RtCell = (s: RtState, m: RtMsg) => readonly [RtState, readonly Cmd[]];\ntype RtCellFn = (\n s: RtState,\n m: RtMsg,\n at: string,\n) => readonly [RtState, readonly Cmd[]];\n/** Either form: one body for every site, or one body PER site keyed by `at`. */\ntype RtCellImpl = RtCellFn | Record<string, RtCellFn | undefined>;\ntype RtFn = (s: RtState, m: RtMsg) => object;\ntype RtGuard = (s: RtState, m: RtMsg, at: string) => boolean;\ntype RtAssign = RtFn | { readonly then: RtFn; readonly else: RtFn };\n\n/** State name → its node and its phase, flattened out of the grouped shape. */\ntype Flat = { readonly node: RtNode; readonly group: string };\n\nfunction flatten(c: RtChart): Map<string, Flat> {\n const out = new Map<string, Flat>();\n for (const group of Object.keys(c.states)) {\n const members = c.states[group] ?? {};\n for (const s of Object.keys(members)) {\n const node = members[s];\n if (node !== undefined) out.set(s, { node, group });\n }\n }\n return out;\n}\n\nfunction scopeList(scope: string | readonly string[]): readonly string[] {\n return typeof scope === \"string\" ? [scope] : scope;\n}\n\n/** The code parts, after the type layer has been erased. Shared by both forms. */\ntype RtParts = {\n readonly assign: Record<string, RtAssign | undefined>;\n readonly guards: Record<string, RtGuard | undefined>;\n readonly cmds: Record<\n string,\n ((s: RtState, m: RtMsg, at: string) => object) | undefined\n >;\n readonly cells: Record<string, RtCellImpl | undefined>;\n};\n\nfunction rtParts(parts: object): RtParts {\n const p = parts as Record<string, object | undefined>;\n return {\n assign: (p.assign ?? {}) as RtParts[\"assign\"],\n guards: (p.guards ?? {}) as RtParts[\"guards\"],\n cmds: (p.cmds ?? {}) as RtParts[\"cmds\"],\n cells: (p.cells ?? {}) as RtParts[\"cells\"],\n };\n}\n\n/**\n * ONE edge → one transition cell. This is the whole walk, and it is shared\n * verbatim by both chart forms: a `Transitions` cell and a `Reducer` cell\n * differ only in what `at` is (`\"state.event\"` vs `\"event\"`) and in whether a\n * parking set exists to inject `was` from — both parameters here, not branches.\n *\n * `bare` is the un-namespaced event name the author's parts are written\n * against; the compiled cell restores it before calling them.\n */\nfunction buildCell(\n spec: RtEdge,\n bare: string,\n at: string,\n p: RtParts,\n parking: ReadonlySet<string>,\n): RtCell {\n const edge = typeof spec === \"string\" ? { target: spec } : spec;\n const cell = p.assign[at];\n\n // ── THE ESCAPE HATCH ────────────────────────────────────────────────────\n // A `{ to, cell }` edge is the whole transition: the cell picks the target\n // from `to` and returns its own cmds, so there is no assign to call, no\n // guard to evaluate, no cmd list to rebuild and no `was` to inject (a cell\n // landing on a parking state supplies `was` itself — its return type demands\n // it). The other fields are compile errors beside `cell`, so this branch is\n // not silently skipping anything.\n if (edge.cell !== undefined) {\n const name = edge.cell;\n const impl = p.cells[name];\n if (impl === undefined) {\n throw new Error(\n `@demlik/tea: edge \"${at}\" names cell \"${name}\" with no implementation`,\n );\n }\n // the two forms: one body for every site, or one body per site. A\n // multi-site cell may be written either way (`Cells`/`RCells` offer both);\n // a single-site one is always the plain function.\n const hand = typeof impl === \"function\" ? impl : impl[at];\n if (hand === undefined) {\n throw new Error(\n `@demlik/tea: cell \"${name}\" is written in the per-site form but has no entry for edge \"${at}\"`,\n );\n }\n const to = edge.to ?? [];\n return (st, nsMsg) => {\n const out = hand(st, { ...nsMsg, type: bare }, at);\n // the runtime half of the `to` clamp — see `CellTargetError`.\n if (!to.includes(out[0].type)) {\n throw new CellTargetError(at, name, out[0].type, to);\n }\n return out;\n };\n }\n\n // A guarded edge is resolved HERE, not at dispatch — same as `cell` above and\n // for the same reason: an unimplemented name is a wiring mistake, and a\n // wiring mistake should surface when the chart is compiled rather than on the\n // first message that happens to reach this edge.\n //\n // It is the only one of the three that ever read a missing part as a VALUE.\n // A missing cmd builder throws, a missing cell throws, and a missing guard\n // used to quietly evaluate to `false` — which is not an error, it is the\n // `otherwise` arm. On the imported door (`chart/lane`) the guard alphabet is\n // `string`, so no type ever checked the name either: a typo in an operator's\n // parts routed the whole lane down its failure arm in silence.\n const guard = edge.when === undefined ? undefined : p.guards[edge.when];\n if (edge.when !== undefined && guard === undefined) {\n throw new Error(\n `@demlik/tea: edge \"${at}\" names guard \"${edge.when}\" with no implementation`,\n );\n }\n\n return (st, nsMsg) => {\n // strip the namespace so the author's parts see the bare event.\n const msg: RtMsg = { ...nsMsg, type: bare };\n\n let target: string;\n let payloadFn: RtFn;\n let fired = true;\n\n if (edge.resume !== undefined) {\n target = st.was ?? edge.resume.fallback;\n payloadFn = cell as RtFn;\n } else if (guard !== undefined) {\n fired = guard(st, msg, at) === true;\n const branch = cell as { then: RtFn; else: RtFn };\n target = fired ? (edge.target as string) : (edge.otherwise as string);\n payloadFn = fired ? branch.then : branch.else;\n } else {\n target = edge.target as string;\n payloadFn = cell as RtFn;\n }\n\n const data = payloadFn(st, msg);\n // `was` is INJECTED, never authored: entering a parking state records where\n // you came from. The type of `was` is `ResumeTargets<C, target>`, and\n // `st.type` is in that set by construction.\n const next: RtState = parking.has(target)\n ? { ...data, type: target, was: st.type }\n : { ...data, type: target };\n\n // 0..n Cmds, in declaration order. A guarded edge picks its arm's list —\n // `Cmd.when` lifted onto the edge, so which effects fire is visible in the\n // chart rather than buried in a cell body.\n const emitted = cmdNames(fired ? edge.cmd : edge.otherwiseCmd).map(\n (n): Cmd => {\n const build = p.cmds[n];\n if (build === undefined) {\n throw new Error(\n `@demlik/tea: edge \"${at}\" names cmd \"${n}\" with no builder`,\n );\n }\n return { ...build(st, msg, at), type: n };\n },\n );\n\n return [next, emitted];\n };\n}\n\n/** `undefined` → bare; a foreign event → bare; otherwise `${ns}.${event}`. */\nfunction keyOf(\n c: {\n readonly events: Record<string, { readonly foreign?: true } | undefined>;\n },\n e: string,\n ns: string | undefined,\n): string {\n return ns === undefined || c.events[e]?.foreign === true ? e : `${ns}.${e}`;\n}\n\nconst NO_PARKING: ReadonlySet<string> = new Set<string>();\n\n/**\n * One compiled table, BEFORE the typed boundary — `state.type` → the event key\n * → the cell.\n *\n * The shape `compile` builds and then declares as a `Transitions<S, M, C>`. It\n * is named because a second caller needs the walk without the declaration:\n * `chart/lane/run` compiles ONE region per task and the tables it holds are\n * keyed by task id, so the per-region `Transitions` type is never the type of\n * anything it stores.\n */\nexport type CompiledTable = Readonly<\n Record<string, Readonly<Record<string, RtCell | undefined>> | undefined>\n>;\n\n/**\n * THE WALK, with no type layer over it — `compile`'s body, callable by a second\n * compiler that has already done its own type checking.\n *\n * `chart`/`parts` are `object` rather than the F-bounded `Chart<C>`/`Parts<…>`\n * because this entry is not where the checking happens; it is where the\n * checking has ALREADY happened. The erasing cast lives here, exactly once, at\n * the top of the walk — which is where it lived before this function had a\n * name, so nothing gained a cast by being reachable from two places.\n */\nexport function compileTable(\n chart: object,\n parts: object,\n ns: string | undefined,\n): CompiledTable {\n const c = chart as unknown as RtChart;\n const p = rtParts(parts);\n\n const flat = flatten(c);\n const events = Object.keys(c.events);\n const parking = new Set<string>();\n for (const [s, { node }] of flat) {\n for (const spec of Object.values(node.on ?? {})) {\n if (typeof spec === \"object\" && spec.resume !== undefined) parking.add(s);\n }\n }\n\n const table: Record<string, Record<string, RtCell>> = {};\n\n for (const [s, { node, group }] of flat) {\n const row: Record<string, RtCell> = {};\n const on = node.on ?? {};\n\n for (const e of events) {\n // the one runtime consequence of per-event namespacing.\n const key = keyOf(c, e, ns);\n const spec = on[e];\n\n if (spec === undefined) {\n // the mirror of `MissingAt<C, S>`: live-but-undecided is the only case\n // the type layer forbids, so it is the only case that throws.\n const scope = scopeList(c.events[e]?.scope ?? \"edges\");\n const live = scope.includes(\"all\") || scope.includes(group);\n // `end` is `true` (success) or `\"error\"`; BOTH are final, and the type\n // layer's `IsEndOf` reads both. Testing `=== true` here would put an\n // error final back under the totality obligation at runtime only.\n const refused =\n node.end !== undefined || !live || (node.ignore ?? []).includes(e);\n row[key] = refused\n ? (st) => [st, []]\n : () => {\n throw new NoCellError(key, s);\n };\n continue;\n }\n\n // the site tag, passed to guards/cmds/cells as their last argument — it is\n // what lets a multi-site helper discriminate (see `SiteArgs` in graph.ts).\n row[key] = buildCell(spec, e, `${s}.${e}`, p, parking);\n }\n table[s] = row;\n }\n\n return table;\n}\n\n/**\n * Compile a chart + the code parts into a genuine `Transitions<S, M, C>`.\n *\n * `ns` is OPTIONAL and PER-EVENT. Omitted, nothing is decorated and the table\n * is keyed by the bare event names — a single-instance machine carries no\n * namespace and passes no dummy string. Given, the author's own events are\n * keyed `${ns}.${event}` (so N instances share one dispatch surface with\n * genuinely disjoint literal unions), while every event the chart marks\n * `foreign: true` keeps its BARE name: a library-minted Msg like\n * `deadline_exceeded` is the same event for every instance, and its name was\n * never the author's to rename.\n *\n * The returned type is `Transitions<S, MsgIn<C, NS>, K>` — literal keys, never\n * `string`. The parts are authored against the BARE msg union; the compiled\n * cell restores the bare event name before calling them.\n *\n * `S`/`M`/`K` DEFAULT to the chart's own derivations, so the common call is\n * `compile(chart, parts)` with no type arguments at all.\n */\nexport function compile<\n const C extends Chart<C>,\n S extends { type: string } = StateOf<C>,\n M extends { type: string } = MsgOf<C>,\n K extends Cmd = CmdOf<C>,\n const NS extends string | undefined = undefined,\n>(chart: C, parts: Parts<C, S, M>, ns?: NS): Transitions<S, MsgIn<C, NS>, K> {\n // ── THE ONE CAST ────────────────────────────────────────────────────────\n // Inside the library, at the construction boundary. `Transitions` is a\n // mapped type over `S[\"type\"] × M[\"type\"]`; the walk builds the same keys\n // from the flattened chart × the per-event keys, but tsc cannot see that a\n // string-keyed record built in a loop is total over those unions.\n return compileTable(chart, parts, ns) as unknown as Transitions<\n S,\n MsgIn<C, NS>,\n K\n >;\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// REDUCER FORM — the same walk, one loop shallower.\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * The reducer form's parts bag. Structurally identical to `Parts`, over the\n * reducer chart's derivations — `assign` is total over the DECLARATIVE events\n * (a cell event owes none), and each of `guards`/`cmds`/`cells` is demanded\n * exactly when the chart names one.\n */\nexport type RParts<\n C,\n S extends { type: string },\n M extends { type: string },\n> = {\n readonly assign: RAssigns<C, S, M>;\n} & ([RGuardName<C>] extends [never]\n ? { readonly guards?: undefined }\n : { readonly guards: RGuards<C, S, M> }) &\n ([RUsedCmdName<C>] extends [never]\n ? { readonly cmds?: undefined }\n : { readonly cmds: RCmds<C, S, M> }) &\n ([RCellName<C>] extends [never]\n ? { readonly cells?: undefined }\n : { readonly cells: RCells<C, S, M> });\n\ntype RtReducerChart = {\n readonly events: Record<string, { readonly foreign?: true }>;\n readonly initial: string;\n readonly on: Record<string, RtEdge>;\n};\n\n/**\n * Compile a reducer-form chart into a genuine `Reducer<S, M, K>` — the flat,\n * msg-keyed `update` shape `defineMachine` already accepts (`src/pure/core.ts`).\n *\n * There is no `NoCellError` branch and no refusal branch here, and that is not\n * an omission: `on` is a TOTAL mapped type over the event alphabet, so an\n * undeclared event cannot exist to be missing. The safety the grid form buys\n * with `scope` + `ignore` + `Total<C>`, this form gets from the mapped type.\n *\n * `ns` behaves exactly as in `compile`: per-event, optional, and never applied\n * to an event the chart marks `foreign`.\n */\nexport function compileReducer<\n const C extends ReducerChart<C>,\n S extends { type: string } = RStateOf<C>,\n M extends { type: string } = MsgOf<C>,\n K extends Cmd = CmdOf<C>,\n const NS extends string | undefined = undefined,\n>(chart: C, parts: RParts<C, S, M>, ns?: NS): Reducer<S, MsgIn<C, NS>, K> {\n const c = chart as unknown as RtReducerChart;\n const p = rtParts(parts);\n const out: Record<string, RtCell> = {};\n\n for (const e of Object.keys(c.events)) {\n const spec = c.on[e];\n if (spec === undefined) {\n // unreachable through the typed door — `on` is total over `events`. The\n // safety net under the compile-time obligation, as `NoCellError` is\n // under `Total<C>`.\n throw new Error(`@demlik/tea: reducer chart declares no edge for \"${e}\"`);\n }\n // the site tag IS the event: one dimension, so `SiteArgs`'s `at` is the\n // bare name and a multi-site cell discriminates on it exactly as before.\n out[keyOf(c, e, ns)] = buildCell(spec, e, e, p, NO_PARKING);\n }\n\n // The same one cast, for the same reason: a string-keyed record built in a\n // loop is total over `M[\"type\"]`, and tsc cannot see it.\n return out as unknown as Reducer<S, MsgIn<C, NS>, K>;\n}\n\n/**\n * `init` for a reducer chart. The entry state is the chart's `initial` field —\n * one word, still never repeated by the author — and `boot()` supplies `ctx`,\n * which for this form IS the whole state minus its tag.\n */\nexport function reducerInitFrom<\n const C extends ReducerChart<C>,\n S extends { type: string } = RStateOf<C>,\n K extends Cmd = CmdOf<C>,\n>(\n chart: C,\n boot: () => Omit<RStateOf<C>, \"type\">,\n): (loaded: S | null) => readonly [S, readonly K[]] {\n const type: string = (chart as unknown as RtReducerChart).initial;\n return (loaded) => [\n loaded ?? ({ ...boot(), type } as unknown as S),\n Cmd.none,\n ];\n}\n\n/**\n * The reducer chart, drawn. There is no per-state routing to show, so the\n * honest picture is one `any` node — every edge is reachable from every state,\n * which is what having no phase dimension MEANS — plus each event's full\n * fan-out, cell edges included.\n */\nexport function reducerMermaid<const C extends ReducerChart<C>>(\n chart: C,\n): string {\n const c = chart as unknown as RtReducerChart;\n const lines = [\n \"stateDiagram-v2\",\n \" direction TB\",\n ` [*] --> ${safeId(c.initial)}`,\n ];\n // The SAME edge grammar `chartMermaid` draws — see {@link edgeLines}. The one\n // difference this form has is `from`, which is `any` at every edge, because\n // having no phase dimension MEANS every edge is reachable from every state.\n for (const [e, spec] of Object.entries(c.on)) {\n lines.push(...edgeLines(\"any\", \"any\", e, spec, NO_MARKS));\n }\n return lines.join(\"\\n\");\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// `init`, DERIVED FROM THE CHART\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * The name of the state marked `initial: true`. Throws unless EXACTLY one is\n * marked — the runtime half of what `InitialData`'s marker types say at the\n * type level.\n */\nexport function initialStateOf<const C extends Chart<C>>(\n chart: C,\n): InitialState<C> {\n const flat = flatten(chart as unknown as RtChart);\n const marked = [...flat].filter(([, f]) => f.node.initial === true);\n const only = marked[0];\n if (marked.length !== 1 || only === undefined) {\n throw new Error(\n `@demlik/tea: chart must mark exactly one state \\`initial: true\\` (found ${marked.length})`,\n );\n }\n // `Object.keys` yields `string`; the type-level answer is the same key.\n return only[0] as InitialState<C>;\n}\n\n/**\n * Build a `Machine[\"init\"]` from the chart's declared entry state plus a\n * `boot()` that supplies ONLY that state's data. The state NAME is never\n * repeated by the author — it comes from the same `initial: true` that\n * `machine-viz` reads to draw the `[*] -->` edge.\n *\n * Rehydrate is honoured verbatim: a non-null `loaded` is returned untouched\n * with NO cmds, which is exactly Invariant 2's contract.\n */\nexport function initFrom<\n const C extends Chart<C>,\n S extends { type: string },\n K extends Cmd,\n>(\n chart: C,\n boot: () => InitialData<C, S>,\n): (loaded: S | null) => readonly [S, readonly K[]] {\n const type: string = initialStateOf(chart);\n return (loaded) => [\n // The one cast this helper needs: `{ ...InitialData, type: <the initial\n // name> }` IS `Extract<S, { type: InitialState<C> }>`, but tsc cannot\n // rebuild a union member from a spread plus a `string`-typed discriminant.\n loaded ?? ({ ...boot(), type } as unknown as S),\n Cmd.none,\n ];\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// DRAWING THE CHART — every declared edge, including a cell edge's fan-out.\n//\n// `machine-viz`'s `toMermaid` works on a COMPILED machine and resolves a cell\n// by executing it against a sample, so it draws the ONE edge that sample takes\n// — true of a guarded edge before the escape hatch existed, and equally true\n// of a `{ to, cell }` edge now. The chart, though, still knows every possible\n// target, which is the whole point of declaring `to`. This reads the chart\n// directly and draws the full fan-out, no samples and no execution needed.\n//\n// THIS IS THE ONLY CHART RENDERER. `chart/report/draw.ts` used to carry a\n// second one, written in parallel against the same mermaid grammar while this\n// one was growing its options bag; the two diverged before the swap its banner\n// promised ever happened (a guarded then-arm drawn bare here and labelled\n// there, two spellings of the highlight class, sanitizing on one side only).\n// How a chart is drawn is ONE fact, so there is one body and the report enters\n// it through `drawTask`, which is now nothing but a translation of its option\n// names into these.\n//\n// WHAT MERMAID CAN AND CANNOT DO, stated so `walked` is judged fairly:\n// `stateDiagram-v2` supports `classDef`/`class`, so LIGHTING A NODE is real\n// styling. It supports no per-edge styling at all — there is no `linkStyle` for\n// a state diagram — so a walked edge is marked ON ITS LABEL (`»`, plus the\n// visit count) rather than thickened. Still true as of mermaid 11.\n// ═══════════════════════════════════════════════════════════════════════════\n\n/** How many times each {@link edgeKey} was walked. */\nexport type WalkCounts = ReadonlyMap<string, number>;\n\n/**\n * The key a {@link ChartMermaidOptions.walked} map is read by.\n *\n * One spelling, shared by whoever counts the walk and the drawing that marks\n * it — the names are the chart's OWN, unsanitized, because the counter reads\n * them off a log and has no business knowing what mermaid can spell.\n *\n * NOT `EdgeKey<C>` (`graph.ts`), despite the casing. That one is the SITE tag\n * — `\"state.event\"` — which is what a guard or a cmd builder is keyed by,\n * because a site is where code hangs. This one names a drawn ARROW, and a\n * guarded site draws two of them, so it needs the target as well.\n */\nexport const edgeKey = (from: string, event: string, to: string): string =>\n `${from}|${event}|${to}`;\n\n/**\n * Options for {@link chartMermaid}. Every field is optional and every default\n * reproduces the drawing this function emitted before options existed — a flat\n * node set, `direction TB`, no title, no highlight, no polarity and no walk —\n * so an existing caller is untouched.\n *\n * Shaped after `MachineVizOptions` (`../machine-viz`) where the two overlap\n * (`direction`, `title`), because the two drawings are read side by side and a\n * second spelling of the same knob is a fact said twice. What is NOT shared is\n * `samples`: `toMermaid` needs them because it must EXECUTE a compiled cell to\n * learn a target; the chart declares its targets, so there is nothing to\n * resolve and nothing to sample.\n */\nexport interface ChartMermaidOptions {\n /**\n * Draw this state as the ACTIVE node — a `classDef`/`class` pair Mermaid\n * renders with the highlight fill. Pass the live `state.type`. A name that is\n * not in the chart is ignored rather than drawn as a phantom node.\n */\n readonly highlight?: string;\n /**\n * Draw each phase as a Mermaid COMPOSITE state (`state working { … }`).\n *\n * The chart groups its states into named phases and the flat drawing throws\n * that away — which is the one structural fact `chartMermaid` knew and did\n * not show. Off by default: turning it on changes the emitted text, and the\n * drawing is something a reviewer approves rather than something that moves\n * under them.\n */\n readonly phases?: boolean;\n /**\n * Draw the two ENDINGS differently — a dashed outline on every `end:\n * \"error\"` final, a heavier one on every `end: true`.\n *\n * A success final and an error final both terminate, so both draw the `[*]`\n * edge, and a picture that stopped there paints `shipped` and `frozen` alike\n * in the one place a reader looks first. Off by default for the same reason\n * `phases` is: it changes the emitted text.\n */\n readonly polarity?: boolean;\n /**\n * Edges the caller actually WALKED, keyed by {@link edgeKey} — each drawn\n * edge that has a count picks up a `»` (or `»×N`) on its label.\n *\n * A missing or zero count marks nothing, so a chart nobody has run through\n * draws exactly as it does without this option.\n */\n readonly walked?: WalkCounts;\n /** Mermaid layout direction. Default `\"TB\"`. */\n readonly direction?: \"TB\" | \"LR\";\n /** Optional title, emitted as a Mermaid front-matter `title:` block. */\n readonly title?: string;\n}\n\n/** The CSS class Mermaid applies to the highlighted node. */\nconst ACTIVE_CLASS = \"teaActive\";\n/** The highlighted node when it is where a task DIED — see `chartMermaid`. */\nconst ACTIVE_ERROR_CLASS = \"teaActiveError\";\n/** …and to the two endings, when `polarity` asks for them. */\nconst TRIPPED_CLASS = \"teaTripped\";\nconst SHIPPED_CLASS = \"teaShipped\";\n\n/**\n * A node's declaration line, or `undefined` when the sanitized id already IS\n * the name and the bare id carries the whole fact. Emitting the label only for\n * a renamed node is what keeps the default drawing byte-identical for charts\n * whose names were already Mermaid-safe.\n */\nfunction nodeDecl(raw: string, indent: string): string | undefined {\n const id = safeId(raw);\n return id === raw ? undefined : `${indent}${id} : ${safeLabel(raw)}`;\n}\n\n/**\n * ONE edge, as the arrows that draw it — the whole edge grammar, in one place.\n *\n * Both drawings in this file come through here: the grid form draws it from\n * each state, the reducer form from the single `any` node, and the two forms\n * differ in what `from` is and in nothing else. That is not tidiness — the\n * guarded arm's labelling was wrong in both of them, in the same way, and one\n * of them was fixed once already without the other moving.\n *\n * `at` is the UNSANITIZED source-state name the walk counter keyed by; `from`\n * is what mermaid will read.\n */\nfunction edgeLines(\n from: string,\n at: string,\n e: string,\n spec: RtEdge,\n mark: (from: string, event: string, to: string) => string,\n): readonly string[] {\n const edge = typeof spec === \"string\" ? { target: spec } : spec;\n const out: string[] = [];\n // ONE arrow-writer for every edge form, so the walk mark cannot be\n // remembered on three of the four and forgotten on the fourth.\n const arrow = (to: string, label: string): void => {\n out.push(\n ` ${from} --> ${safeId(to)} : ${safeLabel(`${label}${mark(at, e, to)}`)}`,\n );\n };\n if (edge.cell !== undefined) {\n // one real edge per DECLARED target — the fan-out, labelled with the cell\n // that picks among them.\n for (const t of edge.to ?? []) arrow(t, `${e} / ${edge.cell}()`);\n return out;\n }\n if (edge.resume !== undefined) {\n arrow(edge.resume.fallback, `${e} (resume)`);\n return out;\n }\n // A GUARDED edge labels BOTH arms. Drawing the then-arm bare — `review -->\n // build : FAIL` beside `review --> frozen : FAIL [!retriesRemaining]` —\n // reads as an unconditional edge racing a conditional one, which is not what\n // the chart says: the then-arm is exactly as conditional as the else-arm,\n // and the reader is left to infer its guard from a sibling arrow.\n const guard = edge.when === undefined ? \"\" : ` [${edge.when}]`;\n if (edge.target !== undefined) arrow(edge.target, `${e}${guard}`);\n if (edge.otherwise !== undefined)\n arrow(edge.otherwise, `${e} [!${edge.when ?? \"\"}]`);\n return out;\n}\n\n/** No walk to mark — the drawings that take no `walked` map. */\nconst NO_MARKS = (): string => \"\";\n\nexport function chartMermaid<const C extends Chart<C>>(\n chart: C,\n opts: ChartMermaidOptions = {},\n): string {\n const c = chart as unknown as RtChart;\n const flat = flatten(c);\n const lines: string[] = [];\n if (opts.title !== undefined)\n lines.push(\"---\", `title: ${opts.title}`, \"---\");\n lines.push(\"stateDiagram-v2\", ` direction ${opts.direction ?? \"TB\"}`);\n\n // The walk mark, keyed by the chart's OWN names — `walked` is counted off a\n // log, so it never saw a sanitized id.\n const walked = opts.walked;\n const mark = (from: string, event: string, to: string): string => {\n const n = walked?.get(edgeKey(from, event, to)) ?? 0;\n return n === 0 ? \"\" : n === 1 ? \" »\" : ` »×${n}`;\n };\n\n // Phases, as composite states. Declared BEFORE the edges so every node is\n // already inside its phase by the time an arrow references it — Mermaid\n // otherwise hoists the first mention to the top level.\n if (opts.phases === true) {\n for (const [group, members] of Object.entries(c.states)) {\n lines.push(` state ${safeId(group)} {`);\n for (const s of Object.keys(members)) {\n lines.push(nodeDecl(s, \" \") ?? ` ${safeId(s)}`);\n }\n lines.push(\" }\");\n }\n }\n\n for (const [s, { node }] of flat) {\n const from = safeId(s);\n // A renamed node needs its human name shown once. In `phases` mode the\n // declaration already went inside the composite block.\n if (opts.phases !== true) {\n const decl = nodeDecl(s, \" \");\n if (decl !== undefined) lines.push(decl);\n }\n if (node.initial === true) lines.push(` [*] --> ${from}`);\n for (const [e, spec] of Object.entries(node.on ?? {})) {\n lines.push(...edgeLines(from, s, e, spec, mark));\n }\n // Both polarities terminate, so both draw the `[*]` edge — an error final\n // that stopped drawing one would read as a state the machine can leave.\n if (node.end !== undefined) lines.push(` ${from} --> [*]`);\n }\n\n // The classes, last: a `classDef` must exist before the `class` line that\n // uses it, and both must sit outside any composite block. Each pair is\n // emitted only when something wears it, so an unused `classDef` never lands.\n if (opts.polarity === true) {\n const tripped: string[] = [];\n const shipped: string[] = [];\n for (const [s, { node }] of flat) {\n if (node.end === \"error\") tripped.push(safeId(s));\n if (node.end === true) shipped.push(safeId(s));\n }\n if (tripped.length > 0) {\n lines.push(\n ` classDef ${TRIPPED_CLASS} stroke-dasharray:4 4`,\n ` class ${tripped.join(\",\")} ${TRIPPED_CLASS}`,\n );\n }\n if (shipped.length > 0) {\n lines.push(\n ` classDef ${SHIPPED_CLASS} stroke-width:2px`,\n ` class ${shipped.join(\",\")} ${SHIPPED_CLASS}`,\n );\n }\n }\n if (opts.highlight !== undefined && flat.has(opts.highlight)) {\n // The lit node carries POLARITY too, when polarity is on. `polarity` draws\n // the two endings apart by STROKE and the highlight then painted a fill\n // over the top, so an error final and a success final were the same picture\n // at the exact moment one of them is where a task DIED. Every other surface\n // — the chip, the badge, the stuck panel — says which; the drawing is the\n // thing a reader looks at first and it was the one saying nothing.\n const lit = flat.get(opts.highlight)?.node;\n const died = opts.polarity === true && lit?.end === \"error\";\n const cls = died ? ACTIVE_ERROR_CLASS : ACTIVE_CLASS;\n const paint = died ? \"#f85149\" : \"#2f81f7\";\n lines.push(\n ` classDef ${cls} fill:${paint},stroke:${paint},color:#fff,font-weight:bold`,\n ` class ${safeId(opts.highlight)} ${cls}`,\n );\n }\n return lines.join(\"\\n\");\n}\n"]}