@graphrefly/graphrefly 0.47.1 → 0.48.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 (303) hide show
  1. package/dist/base/composition/index.cjs +28 -19
  2. package/dist/base/composition/index.cjs.map +1 -1
  3. package/dist/base/composition/index.d.cts +14 -5
  4. package/dist/base/composition/index.d.ts +14 -5
  5. package/dist/base/composition/index.js +9 -9
  6. package/dist/base/index.cjs +294 -164
  7. package/dist/base/index.cjs.map +1 -1
  8. package/dist/base/index.d.cts +2 -2
  9. package/dist/base/index.d.ts +2 -2
  10. package/dist/base/index.js +77 -72
  11. package/dist/base/io/index.cjs +145 -85
  12. package/dist/base/io/index.cjs.map +1 -1
  13. package/dist/base/io/index.d.cts +32 -5
  14. package/dist/base/io/index.d.ts +32 -5
  15. package/dist/base/io/index.js +5 -5
  16. package/dist/base/mutation/index.cjs +21 -0
  17. package/dist/base/mutation/index.cjs.map +1 -1
  18. package/dist/base/mutation/index.d.cts +23 -1
  19. package/dist/base/mutation/index.d.ts +23 -1
  20. package/dist/base/mutation/index.js +3 -1
  21. package/dist/base/sources/browser/index.cjs +18 -12
  22. package/dist/base/sources/browser/index.cjs.map +1 -1
  23. package/dist/base/sources/browser/index.d.cts +20 -2
  24. package/dist/base/sources/browser/index.d.ts +20 -2
  25. package/dist/base/sources/browser/index.js +18 -12
  26. package/dist/base/sources/browser/index.js.map +1 -1
  27. package/dist/base/sources/event/index.cjs +29 -1
  28. package/dist/base/sources/event/index.cjs.map +1 -1
  29. package/dist/base/sources/event/index.d.cts +67 -3
  30. package/dist/base/sources/event/index.d.ts +67 -3
  31. package/dist/base/sources/event/index.js +5 -2
  32. package/dist/base/sources/index.cjs +96 -50
  33. package/dist/base/sources/index.cjs.map +1 -1
  34. package/dist/base/sources/index.d.cts +1 -1
  35. package/dist/base/sources/index.d.ts +1 -1
  36. package/dist/base/sources/index.js +7 -4
  37. package/dist/base/sources/node/index.cjs +43 -37
  38. package/dist/base/sources/node/index.cjs.map +1 -1
  39. package/dist/base/sources/node/index.js +43 -37
  40. package/dist/base/sources/node/index.js.map +1 -1
  41. package/dist/{chunk-J5WFUEO4.js → chunk-23MAWVOJ.js} +3 -3
  42. package/dist/{chunk-YXCPV26R.js → chunk-3REMCHSS.js} +39 -27
  43. package/dist/chunk-3REMCHSS.js.map +1 -0
  44. package/dist/{chunk-CEVNQ74M.js → chunk-3YGXPUHW.js} +2 -2
  45. package/dist/{chunk-CEVNQ74M.js.map → chunk-3YGXPUHW.js.map} +1 -1
  46. package/dist/{chunk-EVYY4X5A.js → chunk-46X2EFQH.js} +16 -5
  47. package/dist/chunk-46X2EFQH.js.map +1 -0
  48. package/dist/{chunk-NY2PYHNC.js → chunk-5UY3PNFY.js} +12 -5
  49. package/dist/chunk-5UY3PNFY.js.map +1 -0
  50. package/dist/{chunk-RGMTUZCL.js → chunk-65OM4XLQ.js} +50 -4
  51. package/dist/chunk-65OM4XLQ.js.map +1 -0
  52. package/dist/{chunk-3PSLNJDU.js → chunk-6DQYBIHW.js} +314 -49
  53. package/dist/chunk-6DQYBIHW.js.map +1 -0
  54. package/dist/{chunk-LDCSZ72P.js → chunk-6YBER5UP.js} +3 -3
  55. package/dist/{chunk-LDCSZ72P.js.map → chunk-6YBER5UP.js.map} +1 -1
  56. package/dist/{chunk-7EGRP2VX.js → chunk-7BULJTL6.js} +2 -2
  57. package/dist/{chunk-7EGRP2VX.js.map → chunk-7BULJTL6.js.map} +1 -1
  58. package/dist/{chunk-VLAGJZSL.js → chunk-7T7WLEPM.js} +25 -4
  59. package/dist/chunk-7T7WLEPM.js.map +1 -0
  60. package/dist/{chunk-PKPO3JTZ.js → chunk-AQAKDE7F.js} +29 -11
  61. package/dist/chunk-AQAKDE7F.js.map +1 -0
  62. package/dist/{chunk-2OB3CEJS.js → chunk-B5Y5GPD5.js} +2 -2
  63. package/dist/{chunk-BXGZFGZ4.js → chunk-C5QD5DQX.js} +22 -1
  64. package/dist/chunk-C5QD5DQX.js.map +1 -0
  65. package/dist/{chunk-4XCHZRUJ.js → chunk-D5YGR4TP.js} +58 -7
  66. package/dist/chunk-D5YGR4TP.js.map +1 -0
  67. package/dist/{chunk-NPRP3MCV.js → chunk-DHDCOOJU.js} +2 -2
  68. package/dist/chunk-DHDCOOJU.js.map +1 -0
  69. package/dist/{chunk-MTTRCEJT.js → chunk-DVTDF5OI.js} +2 -2
  70. package/dist/{chunk-SOOKUYVM.js → chunk-F7EKHR32.js} +13 -9
  71. package/dist/chunk-F7EKHR32.js.map +1 -0
  72. package/dist/{chunk-A7KV5UK4.js → chunk-G7H6PN7P.js} +2 -2
  73. package/dist/{chunk-OCUDSN63.js → chunk-GGKHHG5Y.js} +110 -64
  74. package/dist/chunk-GGKHHG5Y.js.map +1 -0
  75. package/dist/{chunk-RAGGHLCV.js → chunk-GUNIRPEJ.js} +8 -6
  76. package/dist/{chunk-RAGGHLCV.js.map → chunk-GUNIRPEJ.js.map} +1 -1
  77. package/dist/{chunk-YJ4U2D2C.js → chunk-J5TBZFBD.js} +9 -7
  78. package/dist/chunk-J5TBZFBD.js.map +1 -0
  79. package/dist/{chunk-Y52CS6YA.js → chunk-JA67ZQG2.js} +2 -2
  80. package/dist/{chunk-Y52CS6YA.js.map → chunk-JA67ZQG2.js.map} +1 -1
  81. package/dist/{chunk-U225SKB4.js → chunk-K4ZYJ4EM.js} +569 -424
  82. package/dist/chunk-K4ZYJ4EM.js.map +1 -0
  83. package/dist/{chunk-Z4YXAUDN.js → chunk-KUFXLAEY.js} +11 -7
  84. package/dist/{chunk-Z4YXAUDN.js.map → chunk-KUFXLAEY.js.map} +1 -1
  85. package/dist/{chunk-IHTWQEDR.js → chunk-LTSI7ULC.js} +3 -3
  86. package/dist/{chunk-IHTWQEDR.js.map → chunk-LTSI7ULC.js.map} +1 -1
  87. package/dist/{chunk-DKNHAICT.js → chunk-MMHGYX44.js} +25 -9
  88. package/dist/chunk-MMHGYX44.js.map +1 -0
  89. package/dist/{chunk-K7PDZYQE.js → chunk-MQMTRKY3.js} +129 -50
  90. package/dist/chunk-MQMTRKY3.js.map +1 -0
  91. package/dist/{chunk-42FQ27MQ.js → chunk-MTODGQBR.js} +44 -179
  92. package/dist/chunk-MTODGQBR.js.map +1 -0
  93. package/dist/{chunk-O3MT7DYI.js → chunk-N6MNJNHB.js} +2 -2
  94. package/dist/{chunk-FVINAAKA.js → chunk-NBK6QQMG.js} +14 -13
  95. package/dist/{chunk-FVINAAKA.js.map → chunk-NBK6QQMG.js.map} +1 -1
  96. package/dist/{chunk-DM4OMPWK.js → chunk-NSA5K5G2.js} +2 -2
  97. package/dist/{chunk-MLTPJMH6.js → chunk-QQYULEZL.js} +2 -2
  98. package/dist/chunk-QSW4DFKE.js +31 -0
  99. package/dist/chunk-QSW4DFKE.js.map +1 -0
  100. package/dist/{chunk-PZWISPIQ.js → chunk-S7HN5FHL.js} +17 -11
  101. package/dist/chunk-S7HN5FHL.js.map +1 -0
  102. package/dist/{chunk-4S53H2KR.js → chunk-SUNCHMML.js} +2 -2
  103. package/dist/{chunk-4GYMCUDZ.js → chunk-T2U6N3FV.js} +7 -7
  104. package/dist/{chunk-RJOG4IJU.js → chunk-T5URUIIY.js} +50 -35
  105. package/dist/chunk-T5URUIIY.js.map +1 -0
  106. package/dist/{chunk-B4AKFXGE.js → chunk-TPTZZV25.js} +6 -6
  107. package/dist/chunk-TPTZZV25.js.map +1 -0
  108. package/dist/{chunk-BU3SEFA5.js → chunk-V46JWFGV.js} +7 -6
  109. package/dist/chunk-V46JWFGV.js.map +1 -0
  110. package/dist/{chunk-IJRR6YAI.js → chunk-VLDRAMP7.js} +18 -12
  111. package/dist/chunk-VLDRAMP7.js.map +1 -0
  112. package/dist/{chunk-6XZYT4SW.js → chunk-X6ESZDR6.js} +8 -9
  113. package/dist/chunk-X6ESZDR6.js.map +1 -0
  114. package/dist/{chunk-E5OZPDIW.js → chunk-X7BA5PWG.js} +7 -5
  115. package/dist/chunk-X7BA5PWG.js.map +1 -0
  116. package/dist/{chunk-CXANAIZU.js → chunk-XEWV254I.js} +3 -3
  117. package/dist/{chunk-CXANAIZU.js.map → chunk-XEWV254I.js.map} +1 -1
  118. package/dist/{chunk-V4Y3TM7U.js → chunk-YBJVKMTM.js} +38 -16
  119. package/dist/chunk-YBJVKMTM.js.map +1 -0
  120. package/dist/{chunk-7ADWWI2T.js → chunk-ZW32BPXV.js} +17 -6
  121. package/dist/chunk-ZW32BPXV.js.map +1 -0
  122. package/dist/compat/index.cjs +52 -5
  123. package/dist/compat/index.cjs.map +1 -1
  124. package/dist/compat/index.d.cts +1 -1
  125. package/dist/compat/index.d.ts +1 -1
  126. package/dist/compat/index.js +7 -7
  127. package/dist/compat/nestjs/index.cjs +52 -5
  128. package/dist/compat/nestjs/index.cjs.map +1 -1
  129. package/dist/compat/nestjs/index.d.cts +1 -1
  130. package/dist/compat/nestjs/index.d.ts +1 -1
  131. package/dist/compat/nestjs/index.js +4 -4
  132. package/dist/{fallback-Bx46zqky.d.cts → fallback-BROR6ZhO.d.cts} +1 -1
  133. package/dist/{fallback-pIWW8A2d.d.ts → fallback-DO80aM_3.d.ts} +1 -1
  134. package/dist/{index-B_p8tnvf.d.cts → index-D1z3XcF9.d.cts} +1 -0
  135. package/dist/{index-_HDSmPyp.d.ts → index-DZ6yua0Q.d.ts} +1 -0
  136. package/dist/index.cjs +2387 -1707
  137. package/dist/index.cjs.map +1 -1
  138. package/dist/index.d.cts +10 -10
  139. package/dist/index.d.ts +10 -10
  140. package/dist/index.js +173 -150
  141. package/dist/index.js.map +1 -1
  142. package/dist/presets/ai/index.cjs +88 -26
  143. package/dist/presets/ai/index.cjs.map +1 -1
  144. package/dist/presets/ai/index.js +14 -14
  145. package/dist/presets/harness/index.cjs +183 -51
  146. package/dist/presets/harness/index.cjs.map +1 -1
  147. package/dist/presets/harness/index.d.cts +15 -5
  148. package/dist/presets/harness/index.d.ts +15 -5
  149. package/dist/presets/harness/index.js +26 -26
  150. package/dist/presets/index.cjs +298 -101
  151. package/dist/presets/index.cjs.map +1 -1
  152. package/dist/presets/index.d.cts +2 -2
  153. package/dist/presets/index.d.ts +2 -2
  154. package/dist/presets/index.js +49 -49
  155. package/dist/presets/inspect/index.cjs +63 -14
  156. package/dist/presets/inspect/index.cjs.map +1 -1
  157. package/dist/presets/inspect/index.d.cts +1 -1
  158. package/dist/presets/inspect/index.d.ts +1 -1
  159. package/dist/presets/inspect/index.js +6 -6
  160. package/dist/presets/resilience/index.cjs +64 -44
  161. package/dist/presets/resilience/index.cjs.map +1 -1
  162. package/dist/presets/resilience/index.d.cts +12 -8
  163. package/dist/presets/resilience/index.d.ts +12 -8
  164. package/dist/presets/resilience/index.js +6 -6
  165. package/dist/{rate-limiter-DpVbSYdH.d.cts → rate-limiter-DC26FM8J.d.cts} +10 -1
  166. package/dist/{rate-limiter-CEALq4N1.d.ts → rate-limiter-DyWpwpQP.d.ts} +10 -1
  167. package/dist/{reactive-layout-fswlBUvX.d.ts → reactive-layout-BBBWH0V_.d.cts} +85 -4
  168. package/dist/{reactive-layout-fswlBUvX.d.cts → reactive-layout-BBBWH0V_.d.ts} +85 -4
  169. package/dist/solutions/index.cjs +239 -92
  170. package/dist/solutions/index.cjs.map +1 -1
  171. package/dist/solutions/index.d.cts +2 -2
  172. package/dist/solutions/index.d.ts +2 -2
  173. package/dist/solutions/index.js +32 -32
  174. package/dist/{spawnable-5mDY501F.d.cts → spawnable-B2IlW60f.d.cts} +23 -2
  175. package/dist/{spawnable-D3lR0oQu.d.ts → spawnable-tttFz2Nh.d.ts} +23 -2
  176. package/dist/testing/index.cjs +94 -0
  177. package/dist/testing/index.cjs.map +1 -0
  178. package/dist/testing/index.d.cts +59 -0
  179. package/dist/testing/index.d.ts +59 -0
  180. package/dist/testing/index.js +73 -0
  181. package/dist/testing/index.js.map +1 -0
  182. package/dist/{timeout-U5O4ESK3.js → timeout-BEABACRP.js} +2 -2
  183. package/dist/utils/ai/browser.cjs.map +1 -1
  184. package/dist/utils/ai/browser.d.cts +2 -2
  185. package/dist/utils/ai/browser.d.ts +2 -2
  186. package/dist/utils/ai/browser.js +10 -10
  187. package/dist/utils/ai/browser.js.map +1 -1
  188. package/dist/utils/ai/index.cjs +291 -191
  189. package/dist/utils/ai/index.cjs.map +1 -1
  190. package/dist/utils/ai/index.d.cts +108 -12
  191. package/dist/utils/ai/index.d.ts +108 -12
  192. package/dist/utils/ai/index.js +23 -21
  193. package/dist/utils/ai/node.cjs.map +1 -1
  194. package/dist/utils/ai/node.d.cts +5 -5
  195. package/dist/utils/ai/node.d.ts +5 -5
  196. package/dist/utils/ai/node.js +3 -3
  197. package/dist/utils/ai/node.js.map +1 -1
  198. package/dist/utils/cqrs/index.cjs +29 -3
  199. package/dist/utils/cqrs/index.cjs.map +1 -1
  200. package/dist/utils/cqrs/index.d.cts +12 -7
  201. package/dist/utils/cqrs/index.d.ts +12 -7
  202. package/dist/utils/cqrs/index.js +2 -2
  203. package/dist/utils/demo-shell/index.cjs +45 -19
  204. package/dist/utils/demo-shell/index.cjs.map +1 -1
  205. package/dist/utils/demo-shell/index.d.cts +1 -1
  206. package/dist/utils/demo-shell/index.d.ts +1 -1
  207. package/dist/utils/demo-shell/index.js +2 -2
  208. package/dist/utils/domain-templates/index.cjs +1 -1
  209. package/dist/utils/domain-templates/index.cjs.map +1 -1
  210. package/dist/utils/domain-templates/index.js +3 -3
  211. package/dist/utils/graphspec/index.cjs +1 -1
  212. package/dist/utils/graphspec/index.cjs.map +1 -1
  213. package/dist/utils/graphspec/index.js +3 -3
  214. package/dist/utils/harness/index.cjs +16 -10
  215. package/dist/utils/harness/index.cjs.map +1 -1
  216. package/dist/utils/harness/index.js +1 -1
  217. package/dist/utils/index.cjs +1692 -1192
  218. package/dist/utils/index.cjs.map +1 -1
  219. package/dist/utils/index.d.cts +7 -7
  220. package/dist/utils/index.d.ts +7 -7
  221. package/dist/utils/index.js +77 -59
  222. package/dist/utils/inspect/index.cjs +52 -4
  223. package/dist/utils/inspect/index.cjs.map +1 -1
  224. package/dist/utils/inspect/index.d.cts +32 -3
  225. package/dist/utils/inspect/index.d.ts +32 -3
  226. package/dist/utils/inspect/index.js +4 -4
  227. package/dist/utils/job-queue/index.cjs +46 -9
  228. package/dist/utils/job-queue/index.cjs.map +1 -1
  229. package/dist/utils/job-queue/index.d.cts +33 -3
  230. package/dist/utils/job-queue/index.d.ts +33 -3
  231. package/dist/utils/job-queue/index.js +2 -2
  232. package/dist/utils/memory/index.cjs +570 -425
  233. package/dist/utils/memory/index.cjs.map +1 -1
  234. package/dist/utils/memory/index.d.cts +261 -33
  235. package/dist/utils/memory/index.d.ts +261 -33
  236. package/dist/utils/memory/index.js +10 -2
  237. package/dist/utils/messaging/index.cjs.map +1 -1
  238. package/dist/utils/messaging/index.d.cts +4 -3
  239. package/dist/utils/messaging/index.d.ts +4 -3
  240. package/dist/utils/messaging/index.js +2 -2
  241. package/dist/utils/orchestration/index.cjs +14 -3
  242. package/dist/utils/orchestration/index.cjs.map +1 -1
  243. package/dist/utils/orchestration/index.js +3 -3
  244. package/dist/utils/process/index.cjs +32 -2
  245. package/dist/utils/process/index.cjs.map +1 -1
  246. package/dist/utils/process/index.d.cts +4 -3
  247. package/dist/utils/process/index.d.ts +4 -3
  248. package/dist/utils/process/index.js +3 -3
  249. package/dist/utils/reactive-layout/index.cjs +184 -55
  250. package/dist/utils/reactive-layout/index.cjs.map +1 -1
  251. package/dist/utils/reactive-layout/index.d.cts +128 -3
  252. package/dist/utils/reactive-layout/index.d.ts +128 -3
  253. package/dist/utils/reactive-layout/index.js +16 -8
  254. package/dist/utils/reduction/index.cjs +1 -1
  255. package/dist/utils/reduction/index.cjs.map +1 -1
  256. package/dist/utils/reduction/index.js +2 -2
  257. package/dist/utils/resilience/index.cjs +64 -43
  258. package/dist/utils/resilience/index.cjs.map +1 -1
  259. package/dist/utils/resilience/index.d.cts +1 -1
  260. package/dist/utils/resilience/index.d.ts +1 -1
  261. package/dist/utils/resilience/index.js +5 -5
  262. package/dist/utils/surface/index.cjs +1 -1
  263. package/dist/utils/surface/index.cjs.map +1 -1
  264. package/dist/utils/surface/index.js +4 -4
  265. package/package.json +15 -3
  266. package/dist/chunk-3PSLNJDU.js.map +0 -1
  267. package/dist/chunk-42FQ27MQ.js.map +0 -1
  268. package/dist/chunk-4XCHZRUJ.js.map +0 -1
  269. package/dist/chunk-6XZYT4SW.js.map +0 -1
  270. package/dist/chunk-7ADWWI2T.js.map +0 -1
  271. package/dist/chunk-B4AKFXGE.js.map +0 -1
  272. package/dist/chunk-BU3SEFA5.js.map +0 -1
  273. package/dist/chunk-BXGZFGZ4.js.map +0 -1
  274. package/dist/chunk-DKNHAICT.js.map +0 -1
  275. package/dist/chunk-E5OZPDIW.js.map +0 -1
  276. package/dist/chunk-EVYY4X5A.js.map +0 -1
  277. package/dist/chunk-IJRR6YAI.js.map +0 -1
  278. package/dist/chunk-K7PDZYQE.js.map +0 -1
  279. package/dist/chunk-NPRP3MCV.js.map +0 -1
  280. package/dist/chunk-NY2PYHNC.js.map +0 -1
  281. package/dist/chunk-OCUDSN63.js.map +0 -1
  282. package/dist/chunk-PKPO3JTZ.js.map +0 -1
  283. package/dist/chunk-PZWISPIQ.js.map +0 -1
  284. package/dist/chunk-RGMTUZCL.js.map +0 -1
  285. package/dist/chunk-RJOG4IJU.js.map +0 -1
  286. package/dist/chunk-SOOKUYVM.js.map +0 -1
  287. package/dist/chunk-U225SKB4.js.map +0 -1
  288. package/dist/chunk-V4Y3TM7U.js.map +0 -1
  289. package/dist/chunk-VLAGJZSL.js.map +0 -1
  290. package/dist/chunk-W2BOPXTI.js +0 -1
  291. package/dist/chunk-YJ4U2D2C.js.map +0 -1
  292. package/dist/chunk-YXCPV26R.js.map +0 -1
  293. package/dist/timeout-U5O4ESK3.js.map +0 -1
  294. /package/dist/{chunk-J5WFUEO4.js.map → chunk-23MAWVOJ.js.map} +0 -0
  295. /package/dist/{chunk-2OB3CEJS.js.map → chunk-B5Y5GPD5.js.map} +0 -0
  296. /package/dist/{chunk-MTTRCEJT.js.map → chunk-DVTDF5OI.js.map} +0 -0
  297. /package/dist/{chunk-A7KV5UK4.js.map → chunk-G7H6PN7P.js.map} +0 -0
  298. /package/dist/{chunk-O3MT7DYI.js.map → chunk-N6MNJNHB.js.map} +0 -0
  299. /package/dist/{chunk-DM4OMPWK.js.map → chunk-NSA5K5G2.js.map} +0 -0
  300. /package/dist/{chunk-MLTPJMH6.js.map → chunk-QQYULEZL.js.map} +0 -0
  301. /package/dist/{chunk-4S53H2KR.js.map → chunk-SUNCHMML.js.map} +0 -0
  302. /package/dist/{chunk-4GYMCUDZ.js.map → chunk-T2U6N3FV.js.map} +0 -0
  303. /package/dist/{chunk-W2BOPXTI.js.map → timeout-BEABACRP.js.map} +0 -0
@@ -0,0 +1,31 @@
1
+ // src/base/sources/event/push.ts
2
+ import { node } from "@graphrefly/pure-ts/core";
3
+ function sourceOpts(opts) {
4
+ return { describeKind: "producer", ...opts };
5
+ }
6
+ function fromPushNotification(register, opts) {
7
+ if (typeof register !== "function") {
8
+ throw new TypeError(
9
+ "fromPushNotification: a (deliver) => unsubscribe registration function is required"
10
+ );
11
+ }
12
+ return node((_data, a) => {
13
+ let done = false;
14
+ const deliver = (payload) => {
15
+ if (done) return;
16
+ a.emit(payload);
17
+ };
18
+ const unsubscribe = register(deliver);
19
+ return {
20
+ onDeactivation: () => {
21
+ done = true;
22
+ if (typeof unsubscribe === "function") unsubscribe();
23
+ }
24
+ };
25
+ }, sourceOpts(opts));
26
+ }
27
+
28
+ export {
29
+ fromPushNotification
30
+ };
31
+ //# sourceMappingURL=chunk-QSW4DFKE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/base/sources/event/push.ts"],"sourcesContent":["/**\n * Push-notification reactive source (universal — transport-agnostic).\n *\n * `fromPushNotification` turns host-delivered push messages (FCM / APNS /\n * Expo push / Web Push / a NestJS gateway fan-out — any transport) into a\n * reactive `Node<T>`. It is the sanctioned async/external boundary for the\n * two-reactive-island architecture: a mobile/web island and a backend island,\n * each reactive internally, with push as the transport between them (spec\n * §5.10 — async boundaries live in sources, NOT in node fns/operators; this\n * is the same shape as `fromEvent`, not an imperative trigger into the graph).\n *\n * The native/network bridge stays entirely inside the host-supplied\n * `register` callback — this primitive owns only the reactive emission and\n * teardown, so it carries zero `node:*` / DOM / SDK dependency and is\n * Hermes-safe by construction.\n */\n\nimport { type Node, type NodeOptions, node } from \"@graphrefly/pure-ts/core\";\n\ntype ExtraOpts = Omit<NodeOptions, \"describeKind\">;\n\nfunction sourceOpts<T = unknown>(opts?: ExtraOpts): NodeOptions<T> {\n\treturn { describeKind: \"producer\", ...opts } as NodeOptions<T>;\n}\n\n/** Tears down a push registration (remove listener, close channel, abort). */\nexport type PushUnsubscribe = () => void;\n\n/**\n * Host registration callback for {@link fromPushNotification}.\n *\n * Called once on node activation with a `deliver` sink. Wire your push\n * transport here and call `deliver(payload)` for **each** incoming message.\n * Return a {@link PushUnsubscribe} (or nothing if there is no teardown).\n *\n * Any async/native setup belongs **inside** this callback (spec §5.10). The\n * returned unsubscribe must be synchronous; if your SDK registration is\n * async, kick it off here and return a function that aborts/detaches it\n * (the async boundary stays in the host, not in the reactive layer).\n */\nexport type PushRegister<T> = (deliver: (payload: T) => void) => PushUnsubscribe | undefined;\n\n/**\n * Wraps a host push transport; each delivered message becomes a `DATA`\n * emission. Teardown invokes the registration's unsubscribe.\n *\n * @param register - Called on activation with a `deliver(payload)` sink;\n * returns an optional unsubscribe.\n * @param opts - Producer node options (`name`, `meta`, …).\n * @returns `Node<T>` — push payloads as a reactive stream.\n *\n * @example\n * ```ts\n * import { fromPushNotification } from \"@graphrefly/graphrefly\";\n *\n * // memo:Re premium backend — opt-in cloud audit pushed (not polled).\n * const auditPushes = fromPushNotification<AuditEvent>((deliver) => {\n * const sub = messaging.onMessage((msg) => deliver(msg.data as AuditEvent));\n * return () => sub.remove();\n * });\n * ```\n *\n * @remarks\n * A synchronous throw inside `register` propagates as an activation failure\n * (it is not caught here) — same shape as `fromEvent`. Push transports are\n * open-ended: this source never emits `COMPLETE`/`ERROR` on its own; the\n * stream ends only via `onDeactivation` (unsubscribe). Surface a terminal\n * yourself (e.g. compose a downstream operator) if the host needs one.\n *\n * @category extra\n */\nexport function fromPushNotification<T = unknown>(\n\tregister: PushRegister<T>,\n\topts?: ExtraOpts,\n): Node<T> {\n\tif (typeof register !== \"function\") {\n\t\tthrow new TypeError(\n\t\t\t\"fromPushNotification: a (deliver) => unsubscribe registration function is required\",\n\t\t);\n\t}\n\treturn node<T>((_data, a) => {\n\t\tlet done = false;\n\t\tconst deliver = (payload: T) => {\n\t\t\tif (done) return;\n\t\t\ta.emit(payload);\n\t\t};\n\t\tconst unsubscribe = register(deliver);\n\t\treturn {\n\t\t\tonDeactivation: () => {\n\t\t\t\tdone = true;\n\t\t\t\tif (typeof unsubscribe === \"function\") unsubscribe();\n\t\t\t},\n\t\t};\n\t}, sourceOpts(opts));\n}\n"],"mappings":";AAiBA,SAAsC,YAAY;AAIlD,SAAS,WAAwB,MAAkC;AAClE,SAAO,EAAE,cAAc,YAAY,GAAG,KAAK;AAC5C;AAgDO,SAAS,qBACf,UACA,MACU;AACV,MAAI,OAAO,aAAa,YAAY;AACnC,UAAM,IAAI;AAAA,MACT;AAAA,IACD;AAAA,EACD;AACA,SAAO,KAAQ,CAAC,OAAO,MAAM;AAC5B,QAAI,OAAO;AACX,UAAM,UAAU,CAAC,YAAe;AAC/B,UAAI,KAAM;AACV,QAAE,KAAK,OAAO;AAAA,IACf;AACA,UAAM,cAAc,SAAS,OAAO;AACpC,WAAO;AAAA,MACN,gBAAgB,MAAM;AACrB,eAAO;AACP,YAAI,OAAO,gBAAgB,WAAY,aAAY;AAAA,MACpD;AAAA,IACD;AAAA,EACD,GAAG,WAAW,IAAI,CAAC;AACpB;","names":[]}
@@ -67,9 +67,11 @@ function actuatorExecutor(config) {
67
67
  inner = fromAny(rawResult, { signal: ac.signal });
68
68
  } catch (err) {
69
69
  emitOnce(onError(err, item));
70
- return () => {
71
- unlinkParent();
72
- ac.abort();
70
+ return {
71
+ onDeactivation: () => {
72
+ unlinkParent();
73
+ ac.abort();
74
+ }
73
75
  };
74
76
  }
75
77
  unsub = inner.subscribe((batch) => {
@@ -93,11 +95,13 @@ function actuatorExecutor(config) {
93
95
  unsub();
94
96
  unsub = null;
95
97
  }
96
- return () => {
97
- unlinkParent();
98
- ac.abort();
99
- unsub?.();
100
- unsub = null;
98
+ return {
99
+ onDeactivation: () => {
100
+ unlinkParent();
101
+ ac.abort();
102
+ unsub?.();
103
+ unsub = null;
104
+ }
101
105
  };
102
106
  },
103
107
  { name: `${name}/inner`, describeKind: "producer" }
@@ -188,8 +192,10 @@ function autoSolidify(config) {
188
192
  unsub();
189
193
  unsub = null;
190
194
  }
191
- return () => {
192
- tearDown();
195
+ return {
196
+ onDeactivation: () => {
197
+ tearDown();
198
+ }
193
199
  };
194
200
  },
195
201
  { name, describeKind: "producer" }
@@ -429,4 +435,4 @@ export {
429
435
  codeChangeBridge,
430
436
  notifyEffect
431
437
  };
432
- //# sourceMappingURL=chunk-PZWISPIQ.js.map
438
+ //# sourceMappingURL=chunk-S7HN5FHL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/harness/actuator-executor.ts","../src/utils/harness/auto-solidify.ts","../src/utils/harness/bridge.ts"],"sourcesContent":["/**\n * actuatorExecutor — bridge a side-effecting actuator into the harness\n * EXECUTE work fn.\n *\n * `refineExecutor` covers the artifact-typed case (refine a candidate\n * `T` against an evaluator); `actuatorExecutor` covers the side-effecting\n * case (write a catalog entry, mutate a template registry, edit a doc on\n * disk). The user's `apply` callback owns the side effect; the executor\n * wraps it in the per-claim lifecycle:\n *\n * 1. **One DATA per claim.** The producer captures the first DATA from\n * the bridged `apply` result, emits a {@link HarnessJobPayload} with\n * `execution` filled in, and completes. Subsequent inner DATAs are\n * ignored.\n * 2. **Cancel-on-teardown.** When the JobFlow pump unsubscribes (after\n * capturing first DATA, or on graph teardown), the producer's cleanup\n * fires `ac.abort()` which propagates into `apply`'s `signal`.\n * 3. **Errors surfaced as failure payload.** A thrown / ERROR result is\n * mapped via `onError` into a `failure`-outcome `ExecuteOutput` so the\n * dispatch effect can route the item rather than silently dropping it.\n *\n * **What `apply` may return.** Anything `fromAny` accepts: `Promise<R>`,\n * `Node<R>`, `AsyncIterable<R>`, `Iterable<R>`, or a synchronous `R`.\n * `Promise<R>` is the typical shape (`writeFile`, `fetch`, `db.execute`).\n *\n * **Pairing with `evalVerifier`.** `ExecuteOutput.artifact` is set to\n * the actuation record; an `evalVerifier<R>` whose `extractArtifact`\n * returns the record (or the post-apply world state) closes EXECUTE →\n * VERIFY with consistent typing end-to-end.\n *\n * @module\n */\n\nimport { COMPLETE, DATA, ERROR, type Messages, type Node, node } from \"@graphrefly/pure-ts/core\";\nimport { fromAny, type NodeInput } from \"@graphrefly/pure-ts/extra\";\nimport type { JobEnvelope } from \"../job-queue/index.js\";\n\nimport type { ExecuteOutput, HarnessExecutor, HarnessJobPayload, TriagedItem } from \"./types.js\";\n\n/**\n * What an actuator's `apply` may return. Mirrors `NodeInput<R>` plus a\n * raw `R` for synchronous side effects.\n */\nexport type ActuatorResult<R> = NodeInput<R>;\n\n/** Configuration for {@link actuatorExecutor}. */\nexport interface ActuatorExecutorConfig<R> {\n\t/**\n\t * Apply the side effect for this triaged item. Receives the abort\n\t * signal — actuators that own real I/O should thread `signal` into\n\t * `fetch`, `fs.writeFile`, child-process kills, etc. so that the\n\t * pump's teardown actually cancels in-flight work.\n\t *\n\t * The first DATA emitted by the bridged result wins; later DATAs are\n\t * discarded. ERROR (or a synchronous throw) is mapped via `onError`.\n\t */\n\tapply: (item: TriagedItem, opts: { signal: AbortSignal }) => ActuatorResult<R>;\n\n\t/**\n\t * Optional gate — when provided and returning `false`, the actuator\n\t * is skipped and the executor emits an `ExecuteOutput` with\n\t * `outcome: \"failure\"`. Use to route interventions the actuator can't\n\t * handle into the failure path.\n\t */\n\tshouldApply?: (item: TriagedItem) => boolean;\n\n\t/** Detail string for the skip path. Default: includes intervention name. */\n\tskipDetail?: (item: TriagedItem) => string;\n\n\t/**\n\t * Map a successfully-applied actuation record into an `ExecuteOutput<R>`.\n\t */\n\ttoOutput?: (record: R, item: TriagedItem) => ExecuteOutput<R>;\n\n\t/**\n\t * Map a thrown / ERROR result into an `ExecuteOutput<R>`.\n\t */\n\tonError?: (err: unknown, item: TriagedItem) => ExecuteOutput<R>;\n\n\t/** Node name prefix for `describe()` introspection. */\n\tname?: string;\n}\n\nfunction defaultToOutput<R>(record: R, item: TriagedItem): ExecuteOutput<R> {\n\treturn {\n\t\toutcome: \"success\",\n\t\tdetail: `actuator applied ${item.intervention} for ${truncate(item.summary)}`,\n\t\tartifact: record,\n\t};\n}\n\nfunction defaultOnError<R>(err: unknown, item: TriagedItem): ExecuteOutput<R> {\n\tconst message = err instanceof Error ? err.message : String(err);\n\treturn {\n\t\toutcome: \"failure\",\n\t\tdetail: `actuator threw on ${item.intervention}: ${message}`,\n\t};\n}\n\nfunction defaultSkipDetail(item: TriagedItem): string {\n\treturn `actuator skipped ${item.intervention} (shouldApply returned false)`;\n}\n\nfunction truncate(s: string, max = 80): string {\n\treturn s.length <= max ? s : `${s.slice(0, max - 1)}…`;\n}\n\n/**\n * Build a {@link HarnessExecutor} backed by a side-effecting actuator.\n *\n * @example File-system actuator that writes a catalog entry and emits the diff.\n * ```ts\n * const harness = harnessLoop(\"repair\", {\n * adapter,\n * executor: actuatorExecutor<CatalogPatch>({\n * async apply(item, { signal }) {\n * const patch = patchFromItem(item);\n * await fs.writeFile(patch.path, patch.contents, { signal });\n * return patch;\n * },\n * shouldApply: (item) => item.intervention === \"catalog-fn\",\n * }),\n * verifier: evalVerifier<CatalogPatch>({ ... }),\n * });\n * ```\n */\nexport function actuatorExecutor<R>(config: ActuatorExecutorConfig<R>): HarnessExecutor<R> {\n\tconst name = config.name ?? \"actuator-executor\";\n\tconst toOutput = config.toOutput ?? defaultToOutput<R>;\n\tconst onError = config.onError ?? defaultOnError<R>;\n\tconst skipDetail = config.skipDetail ?? defaultSkipDetail;\n\n\treturn (job: JobEnvelope<HarnessJobPayload<R>>, opts) => {\n\t\tconst item = job.payload.item;\n\n\t\tif (config.shouldApply && !config.shouldApply(item)) {\n\t\t\t// Synchronous failure payload — return as a plain object;\n\t\t\t// `fromAny` accepts the bare value and emits one DATA.\n\t\t\treturn {\n\t\t\t\t...job.payload,\n\t\t\t\texecution: { item, outcome: \"failure\", detail: skipDetail(item) },\n\t\t\t} satisfies HarnessJobPayload<R>;\n\t\t}\n\n\t\treturn node<HarnessJobPayload<R>>(\n\t\t\t[],\n\t\t\t(_data, actions) => {\n\t\t\t\tconst ac = new AbortController();\n\t\t\t\t// Link pump-supplied signal (Tier 6.5 2.5b): parent abort\n\t\t\t\t// (e.g. `harness.destroy()`) cascades into the inner AC and\n\t\t\t\t// thus into `apply(item, { signal })` + `fromAny({ signal })`.\n\t\t\t\tconst parentSignal = opts?.signal;\n\t\t\t\tlet unlinkParent: () => void = () => undefined;\n\t\t\t\tif (parentSignal) {\n\t\t\t\t\tif (parentSignal.aborted) {\n\t\t\t\t\t\tac.abort();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconst onParentAbort = (): void => ac.abort();\n\t\t\t\t\t\tparentSignal.addEventListener(\"abort\", onParentAbort, { once: true });\n\t\t\t\t\t\tunlinkParent = () => parentSignal.removeEventListener(\"abort\", onParentAbort);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tlet captured = false;\n\t\t\t\tlet unsub: (() => void) | null = null;\n\t\t\t\tconst emitOnce = (out: ExecuteOutput<R>): void => {\n\t\t\t\t\tif (captured) return;\n\t\t\t\t\tcaptured = true;\n\t\t\t\t\tactions.down([\n\t\t\t\t\t\t[DATA, { ...job.payload, execution: { item, ...out } }],\n\t\t\t\t\t\t[COMPLETE],\n\t\t\t\t\t] satisfies Messages);\n\t\t\t\t\tunsub?.();\n\t\t\t\t\tunsub = null;\n\t\t\t\t};\n\t\t\t\tlet inner: Node<R>;\n\t\t\t\ttry {\n\t\t\t\t\tconst rawResult = config.apply(item, { signal: ac.signal });\n\t\t\t\t\tinner = fromAny<R>(rawResult, { signal: ac.signal });\n\t\t\t\t} catch (err) {\n\t\t\t\t\temitOnce(onError(err, item));\n\t\t\t\t\treturn {\n\t\t\t\t\t\tonDeactivation: () => {\n\t\t\t\t\t\t\tunlinkParent();\n\t\t\t\t\t\t\tac.abort();\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tunsub = inner.subscribe((batch) => {\n\t\t\t\t\tfor (const m of batch) {\n\t\t\t\t\t\tif (captured) return;\n\t\t\t\t\t\tif (m[0] === DATA) {\n\t\t\t\t\t\t\temitOnce(toOutput(m[1] as R, item));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (m[0] === ERROR) {\n\t\t\t\t\t\t\temitOnce(onError(m[1], item));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (m[0] === COMPLETE) {\n\t\t\t\t\t\t\temitOnce(onError(new Error(\"actuator inner completed without emitting DATA\"), item));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\t// Sync DATA delivery (cached state / `fromAny` over a sync value):\n\t\t\t\t// the callback ran reentrantly before `unsub` was assigned, so\n\t\t\t\t// `emitOnce`'s `unsub?.()` was a no-op. Drop the upstream subscription\n\t\t\t\t// now that we have the handle. Without this, the inner stays\n\t\t\t\t// subscribed until producer teardown — leaks at high volume.\n\t\t\t\tif (captured && unsub) {\n\t\t\t\t\tunsub();\n\t\t\t\t\tunsub = null;\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tonDeactivation: () => {\n\t\t\t\t\t\tunlinkParent();\n\t\t\t\t\t\tac.abort();\n\t\t\t\t\t\tunsub?.();\n\t\t\t\t\t\tunsub = null;\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t},\n\t\t\t{ name: `${name}/inner`, describeKind: \"producer\" },\n\t\t);\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// dispatchActuator\n// ---------------------------------------------------------------------------\n\n/**\n * Apply callback shape consumed by {@link dispatchActuator}. Same shape as\n * {@link ActuatorExecutorConfig.apply}.\n */\nexport type ActuatorApplyFn<R> = (\n\titem: TriagedItem,\n\topts: { signal: AbortSignal },\n) => ActuatorResult<R>;\n\n/** Configuration for {@link dispatchActuator}. */\nexport interface DispatchActuatorConfig<R> {\n\t/**\n\t * Per-intervention apply callbacks. Keyed by `TriagedItem.intervention`.\n\t * Items whose intervention is not in `routes` fall through to `default`\n\t * (when set) or emit a skip-failure `ExecuteOutput`.\n\t */\n\troutes: Readonly<Partial<Record<TriagedItem[\"intervention\"], ActuatorApplyFn<R>>>>;\n\t/** Fallback apply callback for items whose intervention is not in `routes`. */\n\tdefault?: ActuatorApplyFn<R>;\n\t/** Node name prefix for `describe()` introspection. */\n\tname?: string;\n}\n\n/**\n * Multi-intervention actuator — dispatches each `TriagedItem` to one of\n * several `apply` callbacks based on `item.intervention`.\n *\n * Internally builds a single `actuatorExecutor` whose `apply` resolves the\n * intervention → callback at call-time. Items with no matching route and no\n * `default` emit a skip-failure with detail\n * `\"no route for intervention 'X'\"`.\n */\nexport function dispatchActuator<R>(config: DispatchActuatorConfig<R>): HarnessExecutor<R> {\n\tconst name = config.name ?? \"dispatch-actuator\";\n\tconst defaultFn = config.default ?? null;\n\tconst hasDefault = defaultFn != null;\n\treturn actuatorExecutor<R>({\n\t\tapply: (item, opts) => {\n\t\t\tconst fn = Object.hasOwn(config.routes, item.intervention)\n\t\t\t\t? config.routes[item.intervention]!\n\t\t\t\t: defaultFn;\n\t\t\tif (!fn) {\n\t\t\t\tthrow new Error(`dispatchActuator: no route for intervention '${item.intervention}'`);\n\t\t\t}\n\t\t\treturn fn(item, opts);\n\t\t},\n\t\tshouldApply: (item) => Object.hasOwn(config.routes, item.intervention) || hasDefault,\n\t\tskipDetail: (item) => `no route for intervention '${item.intervention}'`,\n\t\tname,\n\t});\n}\n","/**\n * autoSolidify — promote successful VERIFY runs into a durable artifact\n * (catalog entry, skill, template, doc edit, …).\n *\n * Closes the dogfood retrospective loop: when the harness's VERIFY\n * stage reports `verified: true`, the validated intervention should\n * become an authoring artifact the next loop run can rely on. This\n * primitive is the generic substrate — pass a `write` callback that\n * does the actual promotion (e.g. `overlay.upsertTemplate` for the\n * dogfood catalog overlay; `fs.writeFile` for a doc edit; `ctx.skill`\n * for a Hermes-style skill registry).\n *\n * @example Wire the catalog overlay as the solidify target.\n * ```ts\n * const solidified = autoSolidify({\n * verifyResults: harness.verifyResults.latest,\n * extract: (vr) => vr.execution.artifact ?? null,\n * write: (entry, vr) => overlay.upsertFn(`learned-${vr.item.summary}`, entry),\n * });\n * solidified.subscribe(() => {}); // keep alive for log\n * ```\n *\n * **Why a node and not just an effect.** The returned `Node<R>` emits\n * each promoted artifact, so callers can pipe solidifications through\n * the standard reactive surface (`describe()`, `observe()`, replay\n * buffers) instead of side-channel logging. An audit / dashboard that\n * wants \"what was learned this run?\" subscribes to the returned node;\n * the `write` callback owns the durable side effect.\n *\n * **Idempotency is the caller's responsibility.** The primitive\n * promotes every `verified: true` wave that passes the predicate. If\n * the harness re-verifies the same item (e.g. via reingestion), the\n * `write` callback is invoked again. Wrap your write fn with a\n * dedup-by-key guard if your target store would otherwise bloat. The\n * inner `seen` set inside this factory is intentionally absent — the\n * harness already retains via topic logs and the user may want\n * re-promotion semantics that are domain-specific.\n *\n * @module\n */\n\nimport { COMPLETE, DATA, ERROR, type Messages, type Node, node } from \"@graphrefly/pure-ts/core\";\n\nimport type { VerifyResult } from \"./types.js\";\n\n/**\n * Configuration for {@link autoSolidify}.\n *\n * `R` is the artifact type the upstream EXECUTE stage produced (and\n * `evalVerifier` carries through `execution.artifact`). `T` is the\n * promotion shape — what `write` consumes and what the returned node\n * emits. Often `T = R`, but they diverge when the actuator's raw\n * artifact needs a transform before storing (e.g. wrap a `CatalogPatch`\n * into a `CatalogEntry` with effectiveness metadata).\n */\nexport interface AutoSolidifyConfig<R, T = R> {\n\t/** Reactive verify-result stream. Typically `harness.verifyResults.latest`. */\n\tverifyResults: Node<VerifyResult<R> | null>;\n\t/**\n\t * Pull the value-to-promote out of a verified VerifyResult.\n\t * Default: `(vr) => vr.execution.artifact as T | null`. Return `null`\n\t * to skip a particular VerifyResult even when `verified: true` (e.g.\n\t * an LLM-default executor produces no artifact and there's nothing to\n\t * solidify).\n\t */\n\textract?: (vr: VerifyResult<R>) => T | null;\n\t/**\n\t * Optional gate beyond `verified === true`. When provided, the\n\t * primitive only promotes when this returns `true`. Default: pass\n\t * everything verified.\n\t *\n\t * Useful predicates:\n\t * - `(vr) => vr.item.intervention === \"catalog-fn\"` — only catalog work.\n\t * - `(vr) => (vr.findings ?? []).every(f => !/regression/i.test(f))` —\n\t * skip even-passes that mention regressions.\n\t */\n\tpredicate?: (vr: VerifyResult<R>) => boolean;\n\t/**\n\t * Promote — usually a side effect (write to overlay, fs, KG, etc.).\n\t * Receives the extracted artifact AND the originating VerifyResult so\n\t * the writer can use any context it needs (item summary, eval task\n\t * IDs, finding text, …) when shaping the durable record.\n\t */\n\twrite: (artifact: T, vr: VerifyResult<R>) => void;\n\t/** Node name for `describe()` introspection. Default `\"auto-solidify\"`. */\n\tname?: string;\n}\n\n/**\n * Build a `Node<T>` that subscribes to `verifyResults`, filters to\n * verified passes that produced an extractable artifact, runs `write`,\n * and emits the artifact. Use the returned node as a subscription\n * point for audit / dashboard / log pipelines.\n *\n * **Terminal-on-error semantics.** A throw from `predicate`, `extract`,\n * or `write` surfaces as `[[ERROR]]` on the returned node and\n * **terminates** it — the upstream subscription tears down and no\n * further DATA is emitted. This matches the spec's terminal-frame\n * contract for ERROR. If you want the solidify node to stay live\n * across user-callback throws, wrap your callbacks with try/catch\n * internally and emit a sentinel value or no-op on failure. A future\n * non-terminal `errors: Node<unknown>` companion may surface failures\n * without terminating the success stream — flagged as a follow-up.\n *\n * @returns A `Node<T>` that emits one DATA per promoted artifact.\n * Stays live as long as `verifyResults` is live AND no user callback\n * has thrown.\n */\nexport function autoSolidify<R, T = R>(config: AutoSolidifyConfig<R, T>): Node<T> {\n\tconst name = config.name ?? \"auto-solidify\";\n\tconst extract =\n\t\tconfig.extract ?? ((vr: VerifyResult<R>) => (vr.execution.artifact ?? null) as T | null);\n\tconst predicate = config.predicate ?? (() => true);\n\n\treturn node<T>(\n\t\t[],\n\t\t(_data, actions) => {\n\t\t\tlet unsub: (() => void) | null = null;\n\t\t\tlet terminated = false;\n\t\t\tconst tearDown = (): void => {\n\t\t\t\tif (terminated) return;\n\t\t\t\tterminated = true;\n\t\t\t\tunsub?.();\n\t\t\t\tunsub = null;\n\t\t\t};\n\t\t\tconst emitTerminalError = (err: unknown): void => {\n\t\t\t\tif (terminated) return;\n\t\t\t\tactions.down([[ERROR, err]] satisfies Messages);\n\t\t\t\ttearDown();\n\t\t\t};\n\t\t\tunsub = config.verifyResults.subscribe((batch) => {\n\t\t\t\tif (terminated) return;\n\t\t\t\tfor (const m of batch) {\n\t\t\t\t\tif (terminated) return;\n\t\t\t\t\tif (m[0] !== DATA) {\n\t\t\t\t\t\tif (m[0] === COMPLETE) {\n\t\t\t\t\t\t\t// Upstream verifyResults completed (rare; harness destroy).\n\t\t\t\t\t\t\t// Forward COMPLETE and tear down — solidify is terminal too.\n\t\t\t\t\t\t\tactions.down([[COMPLETE]] satisfies Messages);\n\t\t\t\t\t\t\ttearDown();\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tconst vr = m[1] as VerifyResult<R> | null;\n\t\t\t\t\tif (vr == null) continue;\n\t\t\t\t\tif (!vr.verified) continue;\n\t\t\t\t\t// User callbacks (predicate / extract / write) are isolated\n\t\t\t\t\t// in try/catch so a throw lands as a single terminal ERROR\n\t\t\t\t\t// rather than propagating into the upstream emitter where\n\t\t\t\t\t// it would skip later messages in the same batch and leave\n\t\t\t\t\t// the solidify node un-terminated.\n\t\t\t\t\tlet pass: boolean;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tpass = predicate(vr);\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\temitTerminalError(err);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (!pass) continue;\n\t\t\t\t\tlet artifact: T | null;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tartifact = extract(vr);\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\temitTerminalError(err);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (artifact == null) continue;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconfig.write(artifact, vr);\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\temitTerminalError(err);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tactions.down([[DATA, artifact]] satisfies Messages);\n\t\t\t\t}\n\t\t\t});\n\t\t\t// If `subscribe` fired terminally during the call (push-on-subscribe\n\t\t\t// of an already-COMPLETE upstream), `tearDown()` ran inside the\n\t\t\t// callback before `unsub` was assigned, so the unsub is still\n\t\t\t// dangling. Drop it now if we're already terminated.\n\t\t\tif (terminated && unsub) {\n\t\t\t\tunsub();\n\t\t\t\tunsub = null;\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tonDeactivation: () => {\n\t\t\t\t\ttearDown();\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t\t{ name, describeKind: \"producer\" },\n\t);\n}\n","/**\n * Harness bridge factories (roadmap §9.0).\n *\n * Intake bridges, eval source wrapper, before/after comparison,\n * affected-task filter, code-change bridge, and notification effect.\n * All are compositions of existing primitives — no new abstractions.\n *\n * @module\n */\n\nimport { type Node, node } from \"@graphrefly/pure-ts/core\";\nimport { fromAny, switchMap } from \"@graphrefly/pure-ts/extra\";\nimport type { Graph } from \"@graphrefly/pure-ts/graph\";\nimport type { TopicGraph } from \"../messaging/index.js\";\n\nimport type { IntakeItem, Severity, TriagedItem } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Generic intake bridge\n// ---------------------------------------------------------------------------\n\n/** Options for {@link createIntakeBridge}. */\nexport interface CreateIntakeBridgeOptions<T> {\n\t/** Graph to register the effect node on (B.1 narrow-waist visibility). */\n\tgraph: Graph;\n\t/** Reactive node emitting domain-specific data. */\n\tsource: Node<T>;\n\t/** TopicGraph to publish IntakeItem entries to. */\n\tintakeTopic: TopicGraph<IntakeItem>;\n\t/** Converts source data into IntakeItem[]. Return empty array to skip. */\n\tparser: (value: T) => IntakeItem[];\n\t/** Effect-node name (default `\"intake-bridge\"`). */\n\tname?: string;\n}\n\n/**\n * Generic source→intake bridge factory.\n *\n * Watches a source node for new values, passes each through a user-supplied\n * `parser` that produces zero or more `IntakeItem`s, and publishes them to\n * the given intake topic.\n *\n * This is the generalized pattern behind {@link evalIntakeBridge}. Use it for\n * CI results, test failures, Slack messages, monitoring alerts, or any domain\n * where structured results should flow into a harness loop.\n *\n * The effect node is registered on the supplied `graph` so it appears in\n * `describe()` and is owned by the graph's lifecycle.\n *\n * @returns The effect node (for lifecycle management).\n */\nexport function createIntakeBridge<T>(opts: CreateIntakeBridgeOptions<T>): Node<unknown> {\n\tconst { graph, source, intakeTopic, parser, name = \"intake-bridge\" } = opts;\n\tconst eff = node(\n\t\t[source as Node<unknown>],\n\t\t(batchData, _actions, ctx) => {\n\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t);\n\t\t\tconst value = data[0];\n\t\t\tif (value === undefined) return;\n\t\t\tconst items = parser(value as T);\n\t\t\tfor (const item of items) {\n\t\t\t\tintakeTopic.publish(item);\n\t\t\t}\n\t\t},\n\t\t{ describeKind: \"effect\" },\n\t);\n\tgraph.add(eff, { name });\n\treturn eff;\n}\n\n// ---------------------------------------------------------------------------\n// Generic eval result shape\n// ---------------------------------------------------------------------------\n\n/**\n * Minimal eval result shape accepted by the bridge.\n *\n * TS eval runner uses `EvalRun` from `evals/lib/types.ts` which is a superset\n * of this shape. The bridge only reads what it needs.\n */\nexport interface EvalRunResult {\n\trun_id: string;\n\tmodel: string;\n\ttasks: EvalTaskResult[];\n}\n\nexport interface EvalTaskResult {\n\ttask_id: string;\n\tvalid: boolean;\n\tjudge_scores?: EvalJudgeScore[];\n}\n\nexport interface EvalJudgeScore {\n\tclaim: string;\n\tpass: boolean;\n\treasoning: string;\n}\n\n// ---------------------------------------------------------------------------\n// Bridge factory\n// ---------------------------------------------------------------------------\n\nexport interface EvalIntakeBridgeOptions {\n\t/** Graph to register the effect node on (B.1 narrow-waist visibility). */\n\tgraph: Graph;\n\t/** Node emitting EvalRunResult (or EvalRunResult[]). */\n\tsource: Node<EvalRunResult | EvalRunResult[]>;\n\t/** TopicGraph to publish IntakeItem entries to. */\n\tintakeTopic: TopicGraph<IntakeItem>;\n\t/** Effect-node name (default `\"eval-intake-bridge\"`). */\n\tname?: string;\n\t/** Minimum severity for eval-sourced items (default `\"medium\"`). */\n\tdefaultSeverity?: Severity;\n}\n\n/**\n * Create an effect node that watches an eval results source and publishes\n * per-criterion findings to an intake topic.\n *\n * Each failing judge criterion produces a separate IntakeItem — not one\n * item per task. This gives the triage stage granular findings to classify.\n *\n * The effect node is registered on the supplied `graph` so it appears in\n * `describe()` and is owned by the graph's lifecycle.\n *\n * @returns The effect node (for lifecycle management).\n */\nexport function evalIntakeBridge(opts: EvalIntakeBridgeOptions): Node<unknown> {\n\tconst {\n\t\tgraph,\n\t\tsource,\n\t\tintakeTopic,\n\t\tname = \"eval-intake-bridge\",\n\t\tdefaultSeverity = \"medium\",\n\t} = opts;\n\n\tconst eff = node(\n\t\t[source as Node<unknown>],\n\t\t(batchData, _actions, ctx) => {\n\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t);\n\t\t\tconst results = data[0];\n\t\t\tif (results === undefined) return;\n\t\t\tconst runs = Array.isArray(results)\n\t\t\t\t? (results as EvalRunResult[])\n\t\t\t\t: [results as EvalRunResult];\n\n\t\t\tfor (const run of runs) {\n\t\t\t\tfor (const task of run.tasks) {\n\t\t\t\t\t// Only process tasks with failures\n\t\t\t\t\tif (task.valid && task.judge_scores?.every((s) => s.pass)) continue;\n\n\t\t\t\t\t// Task-level validity failure (no judge scores or overall invalid)\n\t\t\t\t\tif (!task.valid && (!task.judge_scores || task.judge_scores.length === 0)) {\n\t\t\t\t\t\tintakeTopic.publish({\n\t\t\t\t\t\t\tsource: \"eval\",\n\t\t\t\t\t\t\tsummary: `Task ${task.task_id} invalid (model: ${run.model})`,\n\t\t\t\t\t\t\tevidence: `Run ${run.run_id}: task produced invalid output`,\n\t\t\t\t\t\t\taffectsAreas: [\"graphspec\"],\n\t\t\t\t\t\t\taffectsEvalTasks: [task.task_id],\n\t\t\t\t\t\t\tseverity: defaultSeverity,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Per-criterion findings\n\t\t\t\t\tif (task.judge_scores) {\n\t\t\t\t\t\tfor (const score of task.judge_scores) {\n\t\t\t\t\t\t\tif (score.pass) continue;\n\t\t\t\t\t\t\tintakeTopic.publish({\n\t\t\t\t\t\t\t\tsource: \"eval\",\n\t\t\t\t\t\t\t\tsummary: `${task.task_id}: ${score.claim} (model: ${run.model})`,\n\t\t\t\t\t\t\t\tevidence: score.reasoning,\n\t\t\t\t\t\t\t\taffectsAreas: [\"graphspec\"],\n\t\t\t\t\t\t\t\taffectsEvalTasks: [task.task_id],\n\t\t\t\t\t\t\t\tseverity: defaultSeverity,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t{ describeKind: \"effect\" },\n\t);\n\tgraph.add(eff, { name });\n\treturn eff;\n}\n\n// ---------------------------------------------------------------------------\n// Composition A: Eval-driven improvement loop\n// ---------------------------------------------------------------------------\n\n/**\n * Wrap any eval runner as a reactive producer node.\n *\n * When `trigger` emits, calls `runner()` and emits the result downstream.\n * Uses `switchMap` + `fromAny` — the async boundary stays in the source\n * layer (spec §5.10). A new trigger cancels any in-flight run.\n *\n * Pure transform via operator composition — does not construct an\n * effect/derived node, so no `graph` parameter is needed.\n *\n * ```ts\n * const trigger = state(0); // bump to trigger a new run\n * const results = evalSource(trigger, () => runEvals(config));\n * results.subscribe(msgs => { ... });\n * trigger.emit(1); // fires the runner\n * ```\n *\n * @param trigger - Any node; each new DATA emission fires the runner.\n * @param runner - Returns an EvalRunResult (or promise of one).\n */\nexport function evalSource<T extends EvalRunResult>(\n\ttrigger: Node<unknown>,\n\trunner: () => T | Promise<T>,\n): Node<T> {\n\treturn switchMap(trigger, () => fromAny(runner()) as Node<T>);\n}\n\n// ---------------------------------------------------------------------------\n\n/** Per-task delta produced by {@link beforeAfterCompare}. */\nexport interface EvalTaskDelta {\n\ttaskId: string;\n\tbefore: boolean;\n\tafter: boolean;\n\t/** Score-level diff (after − before), undefined if no scores present. */\n\tscoreDiff?: number;\n}\n\n/** Output of {@link beforeAfterCompare}. */\nexport interface EvalDelta {\n\t/** Task IDs that newly fail in `after` (were passing in `before`). */\n\tnewFailures: string[];\n\t/** Task IDs that now pass in `after` (were failing in `before`). */\n\tresolved: string[];\n\t/** Full per-task breakdown. */\n\ttaskDeltas: EvalTaskDelta[];\n\t/** True when net resolutions > net failures. */\n\toverallImproved: boolean;\n}\n\n/** Options for {@link beforeAfterCompare}. */\nexport interface BeforeAfterCompareOptions {\n\t/** Graph to register the derived node on (B.1 narrow-waist visibility). */\n\tgraph: Graph;\n\t/** Node holding the baseline eval result. */\n\tbefore: Node<EvalRunResult>;\n\t/** Node holding the new eval result. */\n\tafter: Node<EvalRunResult>;\n\t/** Derived-node name (default `\"eval-delta\"`). */\n\tname?: string;\n}\n\n/**\n * Derived node that computes before/after eval deltas.\n *\n * Pure computation: no LLM, no async. Compares per-task validity and\n * pass counts between two `EvalRunResult` snapshots.\n *\n * The derived node is registered on the supplied `graph` so it appears in\n * `describe()` and is owned by the graph's lifecycle.\n */\nexport function beforeAfterCompare(opts: BeforeAfterCompareOptions): Node<EvalDelta> {\n\tconst { graph, before, after, name = \"eval-delta\" } = opts;\n\tconst der = node<EvalDelta>(\n\t\t[before as Node<unknown>, after as Node<unknown>],\n\t\t(batchData, actions, ctx) => {\n\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t);\n\t\t\tconst bRes = data[0] as EvalRunResult;\n\t\t\tconst aRes = data[1] as EvalRunResult;\n\n\t\t\tconst beforeMap = new Map<string, EvalTaskResult>(bRes.tasks.map((t) => [t.task_id, t]));\n\t\t\tconst afterMap = new Map<string, EvalTaskResult>(aRes.tasks.map((t) => [t.task_id, t]));\n\n\t\t\tconst allIds = new Set([...beforeMap.keys(), ...afterMap.keys()]);\n\t\t\tconst taskDeltas: EvalTaskDelta[] = [];\n\t\t\tconst newFailures: string[] = [];\n\t\t\tconst resolved: string[] = [];\n\n\t\t\tfor (const id of allIds) {\n\t\t\t\tconst bt = beforeMap.get(id);\n\t\t\t\tconst at = afterMap.get(id);\n\t\t\t\tconst beforeValid = bt?.valid ?? false;\n\t\t\t\tconst afterValid = at?.valid ?? false;\n\n\t\t\t\tconst beforeScore = bt?.judge_scores\n\t\t\t\t\t? bt.judge_scores.filter((s) => s.pass).length\n\t\t\t\t\t: undefined;\n\t\t\t\tconst afterScore = at?.judge_scores\n\t\t\t\t\t? at.judge_scores.filter((s) => s.pass).length\n\t\t\t\t\t: undefined;\n\t\t\t\tconst scoreDiff =\n\t\t\t\t\tbeforeScore !== undefined && afterScore !== undefined\n\t\t\t\t\t\t? afterScore - beforeScore\n\t\t\t\t\t\t: undefined;\n\n\t\t\t\ttaskDeltas.push({ taskId: id, before: beforeValid, after: afterValid, scoreDiff });\n\t\t\t\tif (beforeValid && !afterValid) newFailures.push(id);\n\t\t\t\tif (!beforeValid && afterValid) resolved.push(id);\n\t\t\t}\n\n\t\t\tactions.emit({\n\t\t\t\tnewFailures,\n\t\t\t\tresolved,\n\t\t\t\ttaskDeltas,\n\t\t\t\toverallImproved: resolved.length > newFailures.length,\n\t\t\t});\n\t\t},\n\t\t{ describeKind: \"derived\" },\n\t);\n\tgraph.add(der as Node<unknown>, { name });\n\treturn der;\n}\n\n// ---------------------------------------------------------------------------\n\n/** Options for {@link affectedTaskFilter}. */\nexport interface AffectedTaskFilterOptions {\n\t/** Graph to register the derived node on (B.1 narrow-waist visibility). */\n\tgraph: Graph;\n\t/** Node holding the current list of triaged items. */\n\tissues: Node<readonly TriagedItem[]>;\n\t/**\n\t * Optional node (or plain array) of all known task IDs.\n\t * When provided, output is the intersection.\n\t */\n\tfullTaskSet?: Node<readonly string[]> | readonly string[];\n\t/** Derived-node name (default `\"affected-task-filter\"`). */\n\tname?: string;\n}\n\n/**\n * Derived node that selects which eval task IDs to re-run.\n *\n * Collects `affectsEvalTasks` from all triaged items, deduplicates, then\n * optionally intersects with `fullTaskSet`. Returns a sorted array of IDs.\n *\n * Use this to avoid re-running the full eval suite after each fix: only the\n * tasks that the triaged items claim to affect are returned.\n *\n * The derived node is registered on the supplied `graph` so it appears in\n * `describe()` and is owned by the graph's lifecycle.\n */\nexport function affectedTaskFilter(opts: AffectedTaskFilterOptions): Node<string[]> {\n\tconst { graph, issues, fullTaskSet, name = \"affected-task-filter\" } = opts;\n\n\tlet taskSetNode: Node<unknown> | null = null;\n\tif (fullTaskSet != null) {\n\t\tif (Array.isArray(fullTaskSet)) {\n\t\t\t// Static-array form: register the inline state node so it appears\n\t\t\t// in `describe()`/`explain()` walks (EC8 — qa 2026-04-30).\n\t\t\tconst inlineSet = node([], { initial: fullTaskSet as readonly string[] });\n\t\t\tgraph.add(inlineSet, { name: `${name}/fullTaskSet` });\n\t\t\ttaskSetNode = inlineSet as Node<unknown>;\n\t\t} else {\n\t\t\t// User-supplied Node — owned by the caller's graph; don't re-add.\n\t\t\ttaskSetNode = fullTaskSet as Node<unknown>;\n\t\t}\n\t}\n\n\tconst deps: Node<unknown>[] = [issues as Node<unknown>];\n\tif (taskSetNode) deps.push(taskSetNode);\n\n\tconst der = node<string[]>(\n\t\tdeps,\n\t\t(batchData, actions, ctx) => {\n\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t);\n\t\t\tconst items = data[0] as readonly TriagedItem[];\n\t\t\tconst all = taskSetNode ? new Set(data[1] as readonly string[]) : null;\n\n\t\t\tconst affected = new Set<string>();\n\t\t\tfor (const item of items) {\n\t\t\t\tfor (const id of item.affectsEvalTasks ?? []) {\n\t\t\t\t\tif (all == null || all.has(id)) affected.add(id);\n\t\t\t\t}\n\t\t\t}\n\t\t\tactions.emit([...affected].sort());\n\t\t},\n\t\t{ describeKind: \"derived\" },\n\t);\n\tgraph.add(der as Node<unknown>, { name });\n\treturn der;\n}\n\n// ---------------------------------------------------------------------------\n// Composition D: Quality gate (CI/CD)\n// ---------------------------------------------------------------------------\n\n/** A single lint error emitted by a CI tool. */\nexport interface LintError {\n\tfile: string;\n\tline: number;\n\tcol: number;\n\trule: string;\n\tmessage: string;\n}\n\n/** A single test failure emitted by a test runner. */\nexport interface TestFailure {\n\ttestId: string;\n\tfile: string;\n\tmessage: string;\n}\n\n/** Structured code-change / CI event. */\nexport interface CodeChange {\n\t/** Files touched by the change. */\n\tfiles: string[];\n\tlintErrors?: LintError[];\n\ttestFailures?: TestFailure[];\n}\n\n/** Options for {@link codeChangeBridge}. */\nexport interface CodeChangeBridgeOptions {\n\t/** Graph to register the effect node on (B.1 narrow-waist visibility). */\n\tgraph: Graph;\n\t/** Node emitting CodeChange events. */\n\tsource: Node<CodeChange>;\n\t/** TopicGraph to publish IntakeItem entries to. */\n\tintakeTopic: TopicGraph<IntakeItem>;\n\t/** Optional custom parser (overrides default). */\n\tparser?: (change: CodeChange) => IntakeItem[];\n\t/** Effect-node name (default `\"code-change-bridge\"`). */\n\tname?: string;\n\t/** Default severity for generated IntakeItems (default `\"high\"`). */\n\tdefaultSeverity?: Severity;\n}\n\n/**\n * Intake bridge for code-change / CI events.\n *\n * Watches a source node for `CodeChange` events and publishes one\n * `IntakeItem` per lint error and per test failure to the intake topic.\n * Pass a custom `parser` to override the default mapping.\n *\n * The effect node is registered on the supplied `graph` so it appears in\n * `describe()` and is owned by the graph's lifecycle.\n */\nexport function codeChangeBridge(opts: CodeChangeBridgeOptions): Node<unknown> {\n\tconst {\n\t\tgraph,\n\t\tsource,\n\t\tintakeTopic,\n\t\tparser,\n\t\tname = \"code-change-bridge\",\n\t\tdefaultSeverity = \"high\",\n\t} = opts;\n\n\tfunction defaultParser(change: CodeChange): IntakeItem[] {\n\t\tconst items: IntakeItem[] = [];\n\t\tfor (const err of change.lintErrors ?? []) {\n\t\t\titems.push({\n\t\t\t\tsource: \"code-change\",\n\t\t\t\tsummary: `Lint: ${err.rule} in ${err.file}:${err.line}`,\n\t\t\t\tevidence: err.message,\n\t\t\t\taffectsAreas: [err.file],\n\t\t\t\tseverity: defaultSeverity,\n\t\t\t});\n\t\t}\n\t\tfor (const fail of change.testFailures ?? []) {\n\t\t\titems.push({\n\t\t\t\tsource: \"test\",\n\t\t\t\tsummary: `Test failure: ${fail.testId}`,\n\t\t\t\tevidence: fail.message,\n\t\t\t\taffectsAreas: [fail.file],\n\t\t\t\taffectsEvalTasks: [fail.testId],\n\t\t\t\tseverity: defaultSeverity,\n\t\t\t});\n\t\t}\n\t\treturn items;\n\t}\n\n\tconst resolve = parser ?? defaultParser;\n\n\tconst eff = node(\n\t\t[source as Node<unknown>],\n\t\t(batchData, _actions, ctx) => {\n\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t);\n\t\t\tconst change = data[0];\n\t\t\tif (change === undefined) return;\n\t\t\tfor (const item of resolve(change as CodeChange)) {\n\t\t\t\tintakeTopic.publish(item);\n\t\t\t}\n\t\t},\n\t\t{ describeKind: \"effect\" },\n\t);\n\tgraph.add(eff, { name });\n\treturn eff;\n}\n\n// ---------------------------------------------------------------------------\n\n/** Transport function for {@link notifyEffect}. Sync or async. */\nexport type NotifyTransport<T> = (item: T) => void | Promise<void>;\n\n/** Options for {@link notifyEffect}. */\nexport interface NotifyEffectOptions<T> {\n\t/** Graph to register the effect node on (B.1 narrow-waist visibility). */\n\tgraph: Graph;\n\t/** TopicGraph whose latest entry triggers the notification. */\n\ttopic: TopicGraph<T>;\n\t/** Called with each new item. May return a Promise. */\n\ttransport: NotifyTransport<T>;\n\t/** Effect-node name (default `\"notify-effect\"`). */\n\tname?: string;\n}\n\n/**\n * Effect node that sends each new topic entry to an external channel.\n *\n * The `transport` function is called for every item published to `topic`.\n * Async transports are bridged via `fromAny` (spec §5.10 compliant).\n *\n * Typical use: Slack webhook, GitHub PR comment, email notification, etc.\n * The factory provides reactive wiring; the transport supplies domain logic.\n *\n * The effect node is registered on the supplied `graph` so it appears in\n * `describe()` and is owned by the graph's lifecycle.\n *\n * ```ts\n * notifyEffect({ graph, topic: alertQueue, transport: async (item) => {\n * await fetch(SLACK_WEBHOOK, { method: 'POST', body: JSON.stringify({ text: item.summary }) });\n * }});\n * ```\n */\nexport function notifyEffect<T>(opts: NotifyEffectOptions<T>): Node<unknown> {\n\tconst { graph, topic, transport, name = \"notify-effect\" } = opts;\n\t// SENTINEL contract on `topic.latest` (COMPOSITION-GUIDE §1a + spec §5.12):\n\t// - `topic.publish(undefined)` is rejected at the publish boundary, so\n\t// `undefined` is exclusively the protocol SENTINEL on the read side.\n\t// - `topic.latest` stays SENTINEL on empty (no eager DATA emission), so\n\t// the partial-false first-run gate holds this fn until the first publish.\n\t// - Legit `null` DATA (when `T` includes `null`) reaches `transport`;\n\t// user transports must handle `null` themselves per v5.\n\t// The `=== undefined` guard below is defense-in-depth for any future\n\t// empty-batch wave where `prevData[0]` is still SENTINEL — in normal flow\n\t// the first-run gate has already filtered the empty case.\n\tconst eff = node(\n\t\t[topic.latest as Node<unknown>],\n\t\t(batchData, _actions, ctx) => {\n\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t);\n\t\t\tconst item = data[0];\n\t\t\tif (item === undefined) return;\n\t\t\t// transport is a side effect (webhook, Slack, email). Async transports\n\t\t\t// are fire-and-forget — the Promise result does not feed back into the\n\t\t\t// graph. Suppress unhandled-rejection noise by voiding the return.\n\t\t\tvoid transport(item as T);\n\t\t},\n\t\t{ describeKind: \"effect\" },\n\t);\n\tgraph.add(eff, { name });\n\treturn eff;\n}\n"],"mappings":";AAiCA,SAAS,UAAU,MAAM,OAAiC,YAAY;AACtE,SAAS,eAA+B;AAiDxC,SAAS,gBAAmB,QAAW,MAAqC;AAC3E,SAAO;AAAA,IACN,SAAS;AAAA,IACT,QAAQ,oBAAoB,KAAK,YAAY,QAAQ,SAAS,KAAK,OAAO,CAAC;AAAA,IAC3E,UAAU;AAAA,EACX;AACD;AAEA,SAAS,eAAkB,KAAc,MAAqC;AAC7E,QAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,SAAO;AAAA,IACN,SAAS;AAAA,IACT,QAAQ,qBAAqB,KAAK,YAAY,KAAK,OAAO;AAAA,EAC3D;AACD;AAEA,SAAS,kBAAkB,MAA2B;AACrD,SAAO,oBAAoB,KAAK,YAAY;AAC7C;AAEA,SAAS,SAAS,GAAW,MAAM,IAAY;AAC9C,SAAO,EAAE,UAAU,MAAM,IAAI,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;AACpD;AAqBO,SAAS,iBAAoB,QAAuD;AAC1F,QAAM,OAAO,OAAO,QAAQ;AAC5B,QAAM,WAAW,OAAO,YAAY;AACpC,QAAM,UAAU,OAAO,WAAW;AAClC,QAAM,aAAa,OAAO,cAAc;AAExC,SAAO,CAAC,KAAwC,SAAS;AACxD,UAAM,OAAO,IAAI,QAAQ;AAEzB,QAAI,OAAO,eAAe,CAAC,OAAO,YAAY,IAAI,GAAG;AAGpD,aAAO;AAAA,QACN,GAAG,IAAI;AAAA,QACP,WAAW,EAAE,MAAM,SAAS,WAAW,QAAQ,WAAW,IAAI,EAAE;AAAA,MACjE;AAAA,IACD;AAEA,WAAO;AAAA,MACN,CAAC;AAAA,MACD,CAAC,OAAO,YAAY;AACnB,cAAM,KAAK,IAAI,gBAAgB;AAI/B,cAAM,eAAe,MAAM;AAC3B,YAAI,eAA2B,MAAM;AACrC,YAAI,cAAc;AACjB,cAAI,aAAa,SAAS;AACzB,eAAG,MAAM;AAAA,UACV,OAAO;AACN,kBAAM,gBAAgB,MAAY,GAAG,MAAM;AAC3C,yBAAa,iBAAiB,SAAS,eAAe,EAAE,MAAM,KAAK,CAAC;AACpE,2BAAe,MAAM,aAAa,oBAAoB,SAAS,aAAa;AAAA,UAC7E;AAAA,QACD;AACA,YAAI,WAAW;AACf,YAAI,QAA6B;AACjC,cAAM,WAAW,CAAC,QAAgC;AACjD,cAAI,SAAU;AACd,qBAAW;AACX,kBAAQ,KAAK;AAAA,YACZ,CAAC,MAAM,EAAE,GAAG,IAAI,SAAS,WAAW,EAAE,MAAM,GAAG,IAAI,EAAE,CAAC;AAAA,YACtD,CAAC,QAAQ;AAAA,UACV,CAAoB;AACpB,kBAAQ;AACR,kBAAQ;AAAA,QACT;AACA,YAAI;AACJ,YAAI;AACH,gBAAM,YAAY,OAAO,MAAM,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC;AAC1D,kBAAQ,QAAW,WAAW,EAAE,QAAQ,GAAG,OAAO,CAAC;AAAA,QACpD,SAAS,KAAK;AACb,mBAAS,QAAQ,KAAK,IAAI,CAAC;AAC3B,iBAAO;AAAA,YACN,gBAAgB,MAAM;AACrB,2BAAa;AACb,iBAAG,MAAM;AAAA,YACV;AAAA,UACD;AAAA,QACD;AACA,gBAAQ,MAAM,UAAU,CAAC,UAAU;AAClC,qBAAW,KAAK,OAAO;AACtB,gBAAI,SAAU;AACd,gBAAI,EAAE,CAAC,MAAM,MAAM;AAClB,uBAAS,SAAS,EAAE,CAAC,GAAQ,IAAI,CAAC;AAClC;AAAA,YACD;AACA,gBAAI,EAAE,CAAC,MAAM,OAAO;AACnB,uBAAS,QAAQ,EAAE,CAAC,GAAG,IAAI,CAAC;AAC5B;AAAA,YACD;AACA,gBAAI,EAAE,CAAC,MAAM,UAAU;AACtB,uBAAS,QAAQ,IAAI,MAAM,gDAAgD,GAAG,IAAI,CAAC;AACnF;AAAA,YACD;AAAA,UACD;AAAA,QACD,CAAC;AAMD,YAAI,YAAY,OAAO;AACtB,gBAAM;AACN,kBAAQ;AAAA,QACT;AACA,eAAO;AAAA,UACN,gBAAgB,MAAM;AACrB,yBAAa;AACb,eAAG,MAAM;AACT,oBAAQ;AACR,oBAAQ;AAAA,UACT;AAAA,QACD;AAAA,MACD;AAAA,MACA,EAAE,MAAM,GAAG,IAAI,UAAU,cAAc,WAAW;AAAA,IACnD;AAAA,EACD;AACD;AAsCO,SAAS,iBAAoB,QAAuD;AAC1F,QAAM,OAAO,OAAO,QAAQ;AAC5B,QAAM,YAAY,OAAO,WAAW;AACpC,QAAM,aAAa,aAAa;AAChC,SAAO,iBAAoB;AAAA,IAC1B,OAAO,CAAC,MAAM,SAAS;AACtB,YAAM,KAAK,OAAO,OAAO,OAAO,QAAQ,KAAK,YAAY,IACtD,OAAO,OAAO,KAAK,YAAY,IAC/B;AACH,UAAI,CAAC,IAAI;AACR,cAAM,IAAI,MAAM,gDAAgD,KAAK,YAAY,GAAG;AAAA,MACrF;AACA,aAAO,GAAG,MAAM,IAAI;AAAA,IACrB;AAAA,IACA,aAAa,CAAC,SAAS,OAAO,OAAO,OAAO,QAAQ,KAAK,YAAY,KAAK;AAAA,IAC1E,YAAY,CAAC,SAAS,8BAA8B,KAAK,YAAY;AAAA,IACrE;AAAA,EACD,CAAC;AACF;;;AChPA,SAAS,YAAAA,WAAU,QAAAC,OAAM,SAAAC,QAAiC,QAAAC,aAAY;AAmE/D,SAAS,aAAuB,QAA2C;AACjF,QAAM,OAAO,OAAO,QAAQ;AAC5B,QAAM,UACL,OAAO,YAAY,CAAC,OAAyB,GAAG,UAAU,YAAY;AACvE,QAAM,YAAY,OAAO,cAAc,MAAM;AAE7C,SAAOA;AAAA,IACN,CAAC;AAAA,IACD,CAAC,OAAO,YAAY;AACnB,UAAI,QAA6B;AACjC,UAAI,aAAa;AACjB,YAAM,WAAW,MAAY;AAC5B,YAAI,WAAY;AAChB,qBAAa;AACb,gBAAQ;AACR,gBAAQ;AAAA,MACT;AACA,YAAM,oBAAoB,CAAC,QAAuB;AACjD,YAAI,WAAY;AAChB,gBAAQ,KAAK,CAAC,CAACD,QAAO,GAAG,CAAC,CAAoB;AAC9C,iBAAS;AAAA,MACV;AACA,cAAQ,OAAO,cAAc,UAAU,CAAC,UAAU;AACjD,YAAI,WAAY;AAChB,mBAAW,KAAK,OAAO;AACtB,cAAI,WAAY;AAChB,cAAI,EAAE,CAAC,MAAMD,OAAM;AAClB,gBAAI,EAAE,CAAC,MAAMD,WAAU;AAGtB,sBAAQ,KAAK,CAAC,CAACA,SAAQ,CAAC,CAAoB;AAC5C,uBAAS;AACT;AAAA,YACD;AACA;AAAA,UACD;AACA,gBAAM,KAAK,EAAE,CAAC;AACd,cAAI,MAAM,KAAM;AAChB,cAAI,CAAC,GAAG,SAAU;AAMlB,cAAI;AACJ,cAAI;AACH,mBAAO,UAAU,EAAE;AAAA,UACpB,SAAS,KAAK;AACb,8BAAkB,GAAG;AACrB;AAAA,UACD;AACA,cAAI,CAAC,KAAM;AACX,cAAI;AACJ,cAAI;AACH,uBAAW,QAAQ,EAAE;AAAA,UACtB,SAAS,KAAK;AACb,8BAAkB,GAAG;AACrB;AAAA,UACD;AACA,cAAI,YAAY,KAAM;AACtB,cAAI;AACH,mBAAO,MAAM,UAAU,EAAE;AAAA,UAC1B,SAAS,KAAK;AACb,8BAAkB,GAAG;AACrB;AAAA,UACD;AACA,kBAAQ,KAAK,CAAC,CAACC,OAAM,QAAQ,CAAC,CAAoB;AAAA,QACnD;AAAA,MACD,CAAC;AAKD,UAAI,cAAc,OAAO;AACxB,cAAM;AACN,gBAAQ;AAAA,MACT;AACA,aAAO;AAAA,QACN,gBAAgB,MAAM;AACrB,mBAAS;AAAA,QACV;AAAA,MACD;AAAA,IACD;AAAA,IACA,EAAE,MAAM,cAAc,WAAW;AAAA,EAClC;AACD;;;ACvLA,SAAoB,QAAAG,aAAY;AAChC,SAAS,WAAAC,UAAS,iBAAiB;AAwC5B,SAAS,mBAAsB,MAAmD;AACxF,QAAM,EAAE,OAAO,QAAQ,aAAa,QAAQ,OAAO,gBAAgB,IAAI;AACvE,QAAM,MAAMD;AAAA,IACX,CAAC,MAAuB;AAAA,IACxB,CAAC,WAAW,UAAU,QAAQ;AAC7B,YAAM,OAAO,UAAU;AAAA,QAAI,CAAC,OAAO,MAClC,SAAS,QAAQ,MAAM,SAAS,IAAI,MAAM,GAAG,EAAE,IAAI,IAAI,SAAS,CAAC;AAAA,MAClE;AACA,YAAM,QAAQ,KAAK,CAAC;AACpB,UAAI,UAAU,OAAW;AACzB,YAAM,QAAQ,OAAO,KAAU;AAC/B,iBAAW,QAAQ,OAAO;AACzB,oBAAY,QAAQ,IAAI;AAAA,MACzB;AAAA,IACD;AAAA,IACA,EAAE,cAAc,SAAS;AAAA,EAC1B;AACA,QAAM,IAAI,KAAK,EAAE,KAAK,CAAC;AACvB,SAAO;AACR;AA2DO,SAAS,iBAAiB,MAA8C;AAC9E,QAAM;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO;AAAA,IACP,kBAAkB;AAAA,EACnB,IAAI;AAEJ,QAAM,MAAMA;AAAA,IACX,CAAC,MAAuB;AAAA,IACxB,CAAC,WAAW,UAAU,QAAQ;AAC7B,YAAM,OAAO,UAAU;AAAA,QAAI,CAAC,OAAO,MAClC,SAAS,QAAQ,MAAM,SAAS,IAAI,MAAM,GAAG,EAAE,IAAI,IAAI,SAAS,CAAC;AAAA,MAClE;AACA,YAAM,UAAU,KAAK,CAAC;AACtB,UAAI,YAAY,OAAW;AAC3B,YAAM,OAAO,MAAM,QAAQ,OAAO,IAC9B,UACD,CAAC,OAAwB;AAE5B,iBAAW,OAAO,MAAM;AACvB,mBAAW,QAAQ,IAAI,OAAO;AAE7B,cAAI,KAAK,SAAS,KAAK,cAAc,MAAM,CAAC,MAAM,EAAE,IAAI,EAAG;AAG3D,cAAI,CAAC,KAAK,UAAU,CAAC,KAAK,gBAAgB,KAAK,aAAa,WAAW,IAAI;AAC1E,wBAAY,QAAQ;AAAA,cACnB,QAAQ;AAAA,cACR,SAAS,QAAQ,KAAK,OAAO,oBAAoB,IAAI,KAAK;AAAA,cAC1D,UAAU,OAAO,IAAI,MAAM;AAAA,cAC3B,cAAc,CAAC,WAAW;AAAA,cAC1B,kBAAkB,CAAC,KAAK,OAAO;AAAA,cAC/B,UAAU;AAAA,YACX,CAAC;AACD;AAAA,UACD;AAGA,cAAI,KAAK,cAAc;AACtB,uBAAW,SAAS,KAAK,cAAc;AACtC,kBAAI,MAAM,KAAM;AAChB,0BAAY,QAAQ;AAAA,gBACnB,QAAQ;AAAA,gBACR,SAAS,GAAG,KAAK,OAAO,KAAK,MAAM,KAAK,YAAY,IAAI,KAAK;AAAA,gBAC7D,UAAU,MAAM;AAAA,gBAChB,cAAc,CAAC,WAAW;AAAA,gBAC1B,kBAAkB,CAAC,KAAK,OAAO;AAAA,gBAC/B,UAAU;AAAA,cACX,CAAC;AAAA,YACF;AAAA,UACD;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAAA,IACA,EAAE,cAAc,SAAS;AAAA,EAC1B;AACA,QAAM,IAAI,KAAK,EAAE,KAAK,CAAC;AACvB,SAAO;AACR;AA0BO,SAAS,WACf,SACA,QACU;AACV,SAAO,UAAU,SAAS,MAAMC,SAAQ,OAAO,CAAC,CAAY;AAC7D;AA8CO,SAAS,mBAAmB,MAAkD;AACpF,QAAM,EAAE,OAAO,QAAQ,OAAO,OAAO,aAAa,IAAI;AACtD,QAAM,MAAMD;AAAA,IACX,CAAC,QAAyB,KAAsB;AAAA,IAChD,CAAC,WAAW,SAAS,QAAQ;AAC5B,YAAM,OAAO,UAAU;AAAA,QAAI,CAAC,OAAO,MAClC,SAAS,QAAQ,MAAM,SAAS,IAAI,MAAM,GAAG,EAAE,IAAI,IAAI,SAAS,CAAC;AAAA,MAClE;AACA,YAAM,OAAO,KAAK,CAAC;AACnB,YAAM,OAAO,KAAK,CAAC;AAEnB,YAAM,YAAY,IAAI,IAA4B,KAAK,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC;AACvF,YAAM,WAAW,IAAI,IAA4B,KAAK,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC;AAEtF,YAAM,SAAS,oBAAI,IAAI,CAAC,GAAG,UAAU,KAAK,GAAG,GAAG,SAAS,KAAK,CAAC,CAAC;AAChE,YAAM,aAA8B,CAAC;AACrC,YAAM,cAAwB,CAAC;AAC/B,YAAM,WAAqB,CAAC;AAE5B,iBAAW,MAAM,QAAQ;AACxB,cAAM,KAAK,UAAU,IAAI,EAAE;AAC3B,cAAM,KAAK,SAAS,IAAI,EAAE;AAC1B,cAAM,cAAc,IAAI,SAAS;AACjC,cAAM,aAAa,IAAI,SAAS;AAEhC,cAAM,cAAc,IAAI,eACrB,GAAG,aAAa,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,SACtC;AACH,cAAM,aAAa,IAAI,eACpB,GAAG,aAAa,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,SACtC;AACH,cAAM,YACL,gBAAgB,UAAa,eAAe,SACzC,aAAa,cACb;AAEJ,mBAAW,KAAK,EAAE,QAAQ,IAAI,QAAQ,aAAa,OAAO,YAAY,UAAU,CAAC;AACjF,YAAI,eAAe,CAAC,WAAY,aAAY,KAAK,EAAE;AACnD,YAAI,CAAC,eAAe,WAAY,UAAS,KAAK,EAAE;AAAA,MACjD;AAEA,cAAQ,KAAK;AAAA,QACZ;AAAA,QACA;AAAA,QACA;AAAA,QACA,iBAAiB,SAAS,SAAS,YAAY;AAAA,MAChD,CAAC;AAAA,IACF;AAAA,IACA,EAAE,cAAc,UAAU;AAAA,EAC3B;AACA,QAAM,IAAI,KAAsB,EAAE,KAAK,CAAC;AACxC,SAAO;AACR;AA+BO,SAAS,mBAAmB,MAAiD;AACnF,QAAM,EAAE,OAAO,QAAQ,aAAa,OAAO,uBAAuB,IAAI;AAEtE,MAAI,cAAoC;AACxC,MAAI,eAAe,MAAM;AACxB,QAAI,MAAM,QAAQ,WAAW,GAAG;AAG/B,YAAM,YAAYA,MAAK,CAAC,GAAG,EAAE,SAAS,YAAiC,CAAC;AACxE,YAAM,IAAI,WAAW,EAAE,MAAM,GAAG,IAAI,eAAe,CAAC;AACpD,oBAAc;AAAA,IACf,OAAO;AAEN,oBAAc;AAAA,IACf;AAAA,EACD;AAEA,QAAM,OAAwB,CAAC,MAAuB;AACtD,MAAI,YAAa,MAAK,KAAK,WAAW;AAEtC,QAAM,MAAMA;AAAA,IACX;AAAA,IACA,CAAC,WAAW,SAAS,QAAQ;AAC5B,YAAM,OAAO,UAAU;AAAA,QAAI,CAAC,OAAO,MAClC,SAAS,QAAQ,MAAM,SAAS,IAAI,MAAM,GAAG,EAAE,IAAI,IAAI,SAAS,CAAC;AAAA,MAClE;AACA,YAAM,QAAQ,KAAK,CAAC;AACpB,YAAM,MAAM,cAAc,IAAI,IAAI,KAAK,CAAC,CAAsB,IAAI;AAElE,YAAM,WAAW,oBAAI,IAAY;AACjC,iBAAW,QAAQ,OAAO;AACzB,mBAAW,MAAM,KAAK,oBAAoB,CAAC,GAAG;AAC7C,cAAI,OAAO,QAAQ,IAAI,IAAI,EAAE,EAAG,UAAS,IAAI,EAAE;AAAA,QAChD;AAAA,MACD;AACA,cAAQ,KAAK,CAAC,GAAG,QAAQ,EAAE,KAAK,CAAC;AAAA,IAClC;AAAA,IACA,EAAE,cAAc,UAAU;AAAA,EAC3B;AACA,QAAM,IAAI,KAAsB,EAAE,KAAK,CAAC;AACxC,SAAO;AACR;AAwDO,SAAS,iBAAiB,MAA8C;AAC9E,QAAM;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO;AAAA,IACP,kBAAkB;AAAA,EACnB,IAAI;AAEJ,WAAS,cAAc,QAAkC;AACxD,UAAM,QAAsB,CAAC;AAC7B,eAAW,OAAO,OAAO,cAAc,CAAC,GAAG;AAC1C,YAAM,KAAK;AAAA,QACV,QAAQ;AAAA,QACR,SAAS,SAAS,IAAI,IAAI,OAAO,IAAI,IAAI,IAAI,IAAI,IAAI;AAAA,QACrD,UAAU,IAAI;AAAA,QACd,cAAc,CAAC,IAAI,IAAI;AAAA,QACvB,UAAU;AAAA,MACX,CAAC;AAAA,IACF;AACA,eAAW,QAAQ,OAAO,gBAAgB,CAAC,GAAG;AAC7C,YAAM,KAAK;AAAA,QACV,QAAQ;AAAA,QACR,SAAS,iBAAiB,KAAK,MAAM;AAAA,QACrC,UAAU,KAAK;AAAA,QACf,cAAc,CAAC,KAAK,IAAI;AAAA,QACxB,kBAAkB,CAAC,KAAK,MAAM;AAAA,QAC9B,UAAU;AAAA,MACX,CAAC;AAAA,IACF;AACA,WAAO;AAAA,EACR;AAEA,QAAM,UAAU,UAAU;AAE1B,QAAM,MAAMA;AAAA,IACX,CAAC,MAAuB;AAAA,IACxB,CAAC,WAAW,UAAU,QAAQ;AAC7B,YAAM,OAAO,UAAU;AAAA,QAAI,CAAC,OAAO,MAClC,SAAS,QAAQ,MAAM,SAAS,IAAI,MAAM,GAAG,EAAE,IAAI,IAAI,SAAS,CAAC;AAAA,MAClE;AACA,YAAM,SAAS,KAAK,CAAC;AACrB,UAAI,WAAW,OAAW;AAC1B,iBAAW,QAAQ,QAAQ,MAAoB,GAAG;AACjD,oBAAY,QAAQ,IAAI;AAAA,MACzB;AAAA,IACD;AAAA,IACA,EAAE,cAAc,SAAS;AAAA,EAC1B;AACA,QAAM,IAAI,KAAK,EAAE,KAAK,CAAC;AACvB,SAAO;AACR;AAqCO,SAAS,aAAgB,MAA6C;AAC5E,QAAM,EAAE,OAAO,OAAO,WAAW,OAAO,gBAAgB,IAAI;AAW5D,QAAM,MAAMA;AAAA,IACX,CAAC,MAAM,MAAuB;AAAA,IAC9B,CAAC,WAAW,UAAU,QAAQ;AAC7B,YAAM,OAAO,UAAU;AAAA,QAAI,CAAC,OAAO,MAClC,SAAS,QAAQ,MAAM,SAAS,IAAI,MAAM,GAAG,EAAE,IAAI,IAAI,SAAS,CAAC;AAAA,MAClE;AACA,YAAM,OAAO,KAAK,CAAC;AACnB,UAAI,SAAS,OAAW;AAIxB,WAAK,UAAU,IAAS;AAAA,IACzB;AAAA,IACA,EAAE,cAAc,SAAS;AAAA,EAC1B;AACA,QAAM,IAAI,KAAK,EAAE,KAAK,CAAC;AACvB,SAAO;AACR;","names":["COMPLETE","DATA","ERROR","node","node","fromAny"]}
@@ -2,7 +2,7 @@ import {
2
2
  compileSpec,
3
3
  validateSpec,
4
4
  validateSpecAgainstCatalog
5
- } from "./chunk-2OB3CEJS.js";
5
+ } from "./chunk-B5Y5GPD5.js";
6
6
 
7
7
  // src/utils/surface/errors.ts
8
8
  var SurfaceError = class extends Error {
@@ -379,4 +379,4 @@ export {
379
379
  listSnapshots,
380
380
  deleteSnapshot
381
381
  };
382
- //# sourceMappingURL=chunk-4S53H2KR.js.map
382
+ //# sourceMappingURL=chunk-SUNCHMML.js.map
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  circuitBreaker
3
- } from "./chunk-RJOG4IJU.js";
3
+ } from "./chunk-T5URUIIY.js";
4
4
  import {
5
5
  dryRunAdapter,
6
6
  fallbackAdapter,
7
7
  withLayer
8
- } from "./chunk-CXANAIZU.js";
9
- import {
10
- firstValueFrom
11
- } from "./chunk-O3MT7DYI.js";
8
+ } from "./chunk-XEWV254I.js";
12
9
  import {
13
10
  parseSSEStream
14
- } from "./chunk-SOOKUYVM.js";
11
+ } from "./chunk-F7EKHR32.js";
12
+ import {
13
+ firstValueFrom
14
+ } from "./chunk-N6MNJNHB.js";
15
15
 
16
16
  // src/utils/ai/adapters/providers/anthropic.ts
17
17
  import { monotonicNs } from "@graphrefly/pure-ts/core";
@@ -1082,4 +1082,4 @@ export {
1082
1082
  cascadingLlmAdapter,
1083
1083
  tier
1084
1084
  };
1085
- //# sourceMappingURL=chunk-4GYMCUDZ.js.map
1085
+ //# sourceMappingURL=chunk-T2U6N3FV.js.map
@@ -1,6 +1,3 @@
1
- import {
2
- domainMeta
3
- } from "./chunk-FMPF42Q4.js";
4
1
  import {
5
2
  clampNonNegative,
6
3
  isAsyncIterable,
@@ -14,6 +11,9 @@ import {
14
11
  NS_PER_MS,
15
12
  NS_PER_SEC
16
13
  } from "./chunk-P5LBT622.js";
14
+ import {
15
+ domainMeta
16
+ } from "./chunk-FMPF42Q4.js";
17
17
 
18
18
  // src/utils/resilience/breaker.ts
19
19
  import {
@@ -222,7 +222,7 @@ function withBreaker(breaker, options) {
222
222
  }
223
223
  });
224
224
  syncState();
225
- return unsub;
225
+ return { onDeactivation: unsub };
226
226
  },
227
227
  {
228
228
  ...operatorOpts(),
@@ -373,8 +373,10 @@ function budgetGate(source, constraints, opts) {
373
373
  })
374
374
  );
375
375
  }
376
- return () => {
377
- for (const u of unsubs) u();
376
+ return {
377
+ onDeactivation: () => {
378
+ for (const u of unsubs) u();
379
+ }
378
380
  };
379
381
  },
380
382
  {
@@ -512,9 +514,11 @@ function fallback(source, fb, options) {
512
514
  } else a.down([m]);
513
515
  }
514
516
  });
515
- return () => {
516
- sourceUnsub?.();
517
- fallbackUnsub?.();
517
+ return {
518
+ onDeactivation: () => {
519
+ sourceUnsub?.();
520
+ fallbackUnsub?.();
521
+ }
518
522
  };
519
523
  },
520
524
  {
@@ -829,31 +833,40 @@ var RATE_LIMITER_INITIAL_STATE = Object.freeze({
829
833
  pendingCount: 0,
830
834
  paused: false
831
835
  });
836
+ function validateRateLimiterOpts(o) {
837
+ if (o.maxEvents <= 0) throw new RangeError("maxEvents must be > 0");
838
+ if (o.windowNs <= 0) throw new RangeError("windowNs must be > 0");
839
+ if (o.maxBuffer === void 0) {
840
+ throw new RangeError(
841
+ "rateLimiter requires explicit maxBuffer (use Infinity to opt in to unbounded)"
842
+ );
843
+ }
844
+ const isUnbounded = o.maxBuffer === Infinity;
845
+ if (!isUnbounded && (!Number.isInteger(o.maxBuffer) || o.maxBuffer < 1)) {
846
+ throw new RangeError("maxBuffer must be a positive integer (or Infinity for unbounded)");
847
+ }
848
+ }
849
+ function resolveInitialReactiveOpts(optsNode) {
850
+ const cached = optsNode.cache;
851
+ if (cached === void 0) {
852
+ throw new RangeError(
853
+ "rateLimiter: reactive (Node-form) opts must carry a cached value at construction \u2014 seed the opts Node with `initial`. Mode (bounded vs unbounded) and the initial cap are LOCKED from `.cache` at construction and the swap handler rejects mode toggles; an un-seeded Node would silently lock bounded `maxBuffer: 1` (D4)."
854
+ );
855
+ }
856
+ return cached;
857
+ }
832
858
  function rateLimiter(source, opts) {
833
859
  const isReactive = isNode(opts);
834
- if (!isReactive) {
835
- const o = opts;
836
- if (o.maxEvents <= 0) throw new RangeError("maxEvents must be > 0");
837
- if (o.windowNs <= 0) throw new RangeError("windowNs must be > 0");
838
- if (o.maxBuffer === void 0) {
839
- throw new RangeError(
840
- "rateLimiter requires explicit maxBuffer (use Infinity to opt in to unbounded)"
841
- );
842
- }
843
- const isUnbounded0 = o.maxBuffer === Infinity;
844
- if (!isUnbounded0 && (!Number.isInteger(o.maxBuffer) || o.maxBuffer < 1)) {
845
- throw new RangeError("maxBuffer must be a positive integer (or Infinity for unbounded)");
846
- }
847
- }
848
- const initialOpts = isReactive ? opts.cache : opts;
849
- const initialMaxBuffer = initialOpts?.maxBuffer;
860
+ const initialOpts = isReactive ? resolveInitialReactiveOpts(opts) : opts;
861
+ validateRateLimiterOpts(initialOpts);
862
+ const initialMaxBuffer = initialOpts.maxBuffer;
850
863
  const isUnbounded = initialMaxBuffer === Infinity;
851
864
  const out = node5(
852
865
  (_data, a) => {
853
- let maxEvents = initialOpts?.maxEvents ?? 1;
854
- let windowNs = initialOpts?.windowNs ?? NS_PER_SEC;
855
- let maxBuffer = initialMaxBuffer ?? 1;
856
- let onOverflow = initialOpts?.onOverflow ?? "drop-newest";
866
+ let maxEvents = initialOpts.maxEvents;
867
+ let windowNs = initialOpts.windowNs;
868
+ let maxBuffer = initialMaxBuffer;
869
+ let onOverflow = initialOpts.onOverflow ?? "drop-newest";
857
870
  let refillPerSec = maxEvents * NS_PER_SEC / windowNs;
858
871
  let tokenTimeNs = NS_PER_SEC / refillPerSec;
859
872
  let bucket = tokenBucket(maxEvents, refillPerSec);
@@ -976,11 +989,13 @@ function rateLimiter(source, opts) {
976
989
  } else a.down([m]);
977
990
  }
978
991
  });
979
- return () => {
980
- terminated = true;
981
- timer.cancel();
982
- unsub();
983
- optMirror.unsub();
992
+ return {
993
+ onDeactivation: () => {
994
+ terminated = true;
995
+ timer.cancel();
996
+ unsub();
997
+ optMirror.unsub();
998
+ }
984
999
  };
985
1000
  },
986
1001
  {
@@ -1036,4 +1051,4 @@ export {
1036
1051
  RateLimiterOverflowError,
1037
1052
  rateLimiter
1038
1053
  };
1039
- //# sourceMappingURL=chunk-RJOG4IJU.js.map
1054
+ //# sourceMappingURL=chunk-T5URUIIY.js.map