@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 @@
1
+ {"version":3,"sources":["../src/utils/reactive-layout/measurement-adapters.ts","../src/utils/reactive-layout/reactive-layout.ts"],"sourcesContent":["/**\n * MeasurementAdapter implementations (roadmap §7.1 — pluggable backends).\n *\n * All adapters implement {@link MeasurementAdapter} from `reactive-layout.ts`.\n * Sync constructors, sync `measureSegment()` — no async, no polling.\n */\n\nimport { countCells } from \"../../base/render/_ascii-width.js\";\nimport type { MeasurementAdapter, SegmentAdapter, SegmentInfo } from \"./reactive-layout.js\";\n\n// ---------------------------------------------------------------------------\n// IntlSegmentAdapter (universal default — wraps the platform `Intl.Segmenter`)\n// ---------------------------------------------------------------------------\n\n/**\n * Reference {@link SegmentAdapter} backed by the platform `Intl.Segmenter`\n * — the substrate's default on engines that ship `Intl.Segmenter` (V8,\n * SpiderMonkey, JSC, modern Node, browsers).\n *\n * **Hermes / RN consumers must not use this** — Hermes ships without\n * `Intl.Segmenter`, and the constructor below throws a clear `TypeError`\n * naming the polyfill path (`optimizations.md` 🟠 (d), 2026-05-20). Wire a\n * custom {@link SegmentAdapter} via `reactiveLayout({ segmentAdapter })`\n * instead — see the {@link SegmentAdapter} JSDoc for the recipe.\n *\n * Per-granularity `Intl.Segmenter` instances are lazy-cached internally\n * (matches the pre-DS module-scoped lazy that this class replaces — see\n * the pre-2026-05-20 `_graphemeSegmenter` helper in `reactive-layout.ts`).\n * Construction is eager (`new IntlSegmentAdapter()` throws on Hermes) so\n * the failure surfaces at factory boot, not at first text-measure call.\n *\n * @remarks\n * Construction policy is **fail-fast**: an engine without `Intl.Segmenter`\n * is fundamentally unable to use this adapter, and silently lazy-deferring\n * the throw would just shift it from `reactiveLayout({})` into the first\n * `analyzeAndMeasure` invocation deep in a reactive wave (the original\n * memo:Re Story 3.6 failure shape — non-resubscribable terminal on\n * `measured-blocks`, cryptic `Cannot read property 'prototype' of\n * undefined`). Eager throw with the polyfill recipe is the better DX.\n */\nexport class IntlSegmentAdapter implements SegmentAdapter {\n\tprivate wordSeg: Intl.Segmenter | null = null;\n\tprivate graphemeSeg: Intl.Segmenter | null = null;\n\n\tconstructor() {\n\t\tif (typeof Intl === \"undefined\" || typeof Intl.Segmenter !== \"function\") {\n\t\t\tthrow new TypeError(\n\t\t\t\t\"IntlSegmentAdapter: Intl.Segmenter is not available in this runtime \" +\n\t\t\t\t\t\"(Hermes / older embedded JS engines). Pass a custom SegmentAdapter via \" +\n\t\t\t\t\t\"`reactiveLayout({ segmentAdapter })` — see the SegmentAdapter JSDoc for the \" +\n\t\t\t\t\t\"polyfill recipe (e.g. `intl-segmenter-polyfill` or `@formatjs/intl-segmenter`).\",\n\t\t\t);\n\t\t}\n\t}\n\n\tsegmentWords(text: string): Iterable<SegmentInfo> {\n\t\tif (this.wordSeg === null) {\n\t\t\tthis.wordSeg = new Intl.Segmenter(undefined, { granularity: \"word\" });\n\t\t}\n\t\treturn this.wordSeg.segment(text);\n\t}\n\n\tsegmentGraphemes(text: string): Iterable<SegmentInfo> {\n\t\tif (this.graphemeSeg === null) {\n\t\t\tthis.graphemeSeg = new Intl.Segmenter(undefined, { granularity: \"grapheme\" });\n\t\t}\n\t\treturn this.graphemeSeg.segment(text);\n\t}\n}\n\n/**\n * Module-shared lazy default {@link SegmentAdapter}. Constructed at most once,\n * on first call. Throws via {@link IntlSegmentAdapter}'s constructor if the\n * runtime lacks `Intl.Segmenter`.\n *\n * Used by the substrate's `analyzeAndMeasure` / `computeLineBreaks` /\n * `computeCharPositions` / `layoutNextLine` helpers when the caller did not\n * supply an explicit `segmentAdapter`. Public factories\n * (`reactiveLayout`/`reactiveBlockLayout`/`reactiveFlowLayout`) expose\n * `segmentAdapter?` in their options — Hermes consumers wire their own and\n * never reach this default.\n */\nlet _defaultSegmentAdapter: SegmentAdapter | null = null;\nexport function getDefaultSegmentAdapter(): SegmentAdapter {\n\tif (_defaultSegmentAdapter === null) {\n\t\t_defaultSegmentAdapter = new IntlSegmentAdapter();\n\t}\n\treturn _defaultSegmentAdapter;\n}\n\n/**\n * Test-only: reset the module-shared default. Use in `afterEach` after stubbing\n * `Intl.Segmenter` so a subsequent unstubbed test rebuilds a fresh default.\n *\n * @internal\n */\nexport function _resetDefaultSegmentAdapterForTests(): void {\n\t_defaultSegmentAdapter = null;\n}\n\n// ---------------------------------------------------------------------------\n// CliMeasureAdapter\n// ---------------------------------------------------------------------------\n\nexport type CliMeasureAdapterOptions = {\n\t/** Pixel width per terminal cell (default: 8). */\n\tcellPx?: number;\n};\n\n/**\n * Monospace terminal measurement adapter.\n *\n * Width = cell count × `cellPx`. CJK / fullwidth characters count as 2 cells.\n * No external dependencies. Works in any JS environment.\n */\nexport class CliMeasureAdapter implements MeasurementAdapter {\n\tprivate readonly cellPx: number;\n\n\tconstructor(opts?: CliMeasureAdapterOptions) {\n\t\tthis.cellPx = opts?.cellPx ?? 8;\n\t}\n\n\tmeasureSegment(text: string, _font: string): { width: number } {\n\t\treturn { width: countCells(text) * this.cellPx };\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// InjectedMeasureAdapter (universal — React Native / Hermes reference adapter)\n// ---------------------------------------------------------------------------\n\n/**\n * A synchronous text-measurement function.\n *\n * `(text, font) => widthPx`. Must be **synchronous** (the layout engine is a\n * pure-arithmetic reactive graph — no async, no polling per spec §5.8/§5.10)\n * and **pure** for a given `(text, font)` pair within a layout pass.\n *\n * `font` is the same CSS-`font`-shorthand string the rest of Reactive Layout\n * uses (e.g. `\"16px Kalam\"`). A host backend that keys on a parsed\n * size/family instead can ignore parts it does not need.\n */\nexport type MeasureFn = (text: string, font: string) => number;\n\n/**\n * Backend-agnostic measurement adapter — wraps an injected synchronous\n * `(text, font) => widthPx` function.\n *\n * This is the **React Native / Hermes reference adapter** and the documented\n * RN measure-adapter contract. RN has no DOM/`OffscreenCanvas`, so\n * {@link CanvasMeasureAdapter} cannot run there; instead the host supplies a\n * sync measure function bound to its native text engine and passes it here.\n * The substrate ships only this generic seam + contract — the concrete\n * native binding stays userland (same split as the `bytes`-`StorageBackend`\n * Drizzle/Expo-SQLite adapter vs. the upstream `bigintJsonCodecFor`).\n *\n * ### React Native (Skia) reference wiring — userland\n *\n * ```ts\n * import { Skia } from \"@shopify/react-native-skia\";\n * import { InjectedMeasureAdapter } from \"@graphrefly/graphrefly/utils/reactive-layout\";\n *\n * // Build the font(s) you lay out with once, outside the measure fn.\n * const typeface = Skia.Typeface.MakeFreeTypeFaceFromData(kalamTtf);\n * const skFont = Skia.Font(typeface, 16);\n *\n * const adapter = new InjectedMeasureAdapter((text, _font) => {\n * // Skia Font.measureText / getGlyphWidths is synchronous — perfect fit.\n * return skFont.measureText(text).width;\n * });\n * ```\n *\n * RN core (no Skia) can instead inject a precomputed per-glyph metric table\n * lookup, or `@shopify/react-native-skia`'s `Paragraph` builder measured\n * synchronously. The only contract is **sync + pure**.\n *\n * @remarks\n * - **Hermes-safe:** this adapter has zero `node:*` and zero DOM globals —\n * the injected fn is the sole boundary to the host text engine. The\n * root-package browser-safety bundle assertion enforces no `node:*` and\n * that DOM globals in the `reactive-layout` graph stay `typeof`-guarded\n * (the `CanvasMeasureAdapter` convention) — see the `reactive-layout`\n * solution doc for the precise guarantee.\n * - An optional `cache: true` memoizes by the `(font, text)` pair (an internal `\\u0000` delimiter, collision-safe). Leave it\n * off (default) when the host engine is already fast or when the working\n * set is unbounded; turn it on for repeated re-layout of stable content.\n */\nexport class InjectedMeasureAdapter implements MeasurementAdapter {\n\tprivate readonly fn: MeasureFn;\n\tprivate readonly cache: Map<string, number> | null;\n\n\tconstructor(fn: MeasureFn, opts?: { cache?: boolean }) {\n\t\tif (typeof fn !== \"function\") {\n\t\t\tthrow new TypeError(\n\t\t\t\t\"InjectedMeasureAdapter: a synchronous (text, font) => widthPx function is required\",\n\t\t\t);\n\t\t}\n\t\tthis.fn = fn;\n\t\tthis.cache = opts?.cache ? new Map<string, number>() : null;\n\t}\n\n\tmeasureSegment(text: string, font: string): { width: number } {\n\t\tif (this.cache) {\n\t\t\tconst key = `${font}\\u0000${text}`;\n\t\t\tconst hit = this.cache.get(key);\n\t\t\tif (hit !== undefined) return { width: hit };\n\t\t\tconst w = this.fn(text, font);\n\t\t\tthis.cache.set(key, w);\n\t\t\treturn { width: w };\n\t\t}\n\t\treturn { width: this.fn(text, font) };\n\t}\n\n\tclearCache(): void {\n\t\tthis.cache?.clear();\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// PrecomputedAdapter\n// ---------------------------------------------------------------------------\n\nexport type PrecomputedAdapterOptions = {\n\t/**\n\t * Pre-computed metrics: `{ font: { segment: widthPx } }`.\n\t * Outer key is the CSS font string; inner key is the text segment.\n\t */\n\tmetrics: Record<string, Record<string, number>>;\n\t/**\n\t * Fallback when a segment is not found in the metrics map.\n\t * - `\"per-char\"`: sum individual character widths from the same font map (default)\n\t * - `\"error\"`: throw an error for unknown segments\n\t */\n\tfallback?: \"per-char\" | \"error\";\n};\n\nclass PrecomputedAdapterKeyError extends Error {\n\tname = \"KeyError\";\n}\n\n/**\n * Pre-computed measurement adapter for SSR / snapshot replay.\n *\n * Reads from a static metrics object — zero measurement at runtime.\n * Ideal for server-side rendering or replaying snapshotted layouts.\n */\nexport class PrecomputedAdapter implements MeasurementAdapter {\n\tprivate readonly metrics: Record<string, Record<string, number>>;\n\tprivate readonly fallback: \"per-char\" | \"error\";\n\n\tconstructor(opts: PrecomputedAdapterOptions) {\n\t\tthis.metrics = opts.metrics;\n\t\tconst fb = opts.fallback ?? \"per-char\";\n\t\tif (fb !== \"per-char\" && fb !== \"error\") {\n\t\t\t// Keep parity with Python: validate at runtime.\n\t\t\tthrow new Error(\n\t\t\t\t`fallback must be 'per-char' or 'error', got ${JSON.stringify(opts.fallback)}`,\n\t\t\t);\n\t\t}\n\t\tthis.fallback = fb;\n\t}\n\n\tmeasureSegment(text: string, font: string): { width: number } {\n\t\tconst fontMap = this.metrics[font];\n\t\tif (fontMap) {\n\t\t\tconst w = fontMap[text];\n\t\t\tif (w !== undefined) return { width: w };\n\t\t}\n\n\t\tif (this.fallback === \"error\") {\n\t\t\tthrow new PrecomputedAdapterKeyError(\n\t\t\t\t`PrecomputedAdapter: no metrics for segment ${JSON.stringify(text)} in font ${JSON.stringify(font)}`,\n\t\t\t);\n\t\t}\n\n\t\t// per-char fallback: sum individual character widths\n\t\tlet total = 0;\n\t\tif (fontMap) {\n\t\t\tfor (const ch of text) {\n\t\t\t\tconst cw = fontMap[ch];\n\t\t\t\tif (cw !== undefined) {\n\t\t\t\t\ttotal += cw;\n\t\t\t\t}\n\t\t\t\t// unknown char contributes 0 (best-effort)\n\t\t\t}\n\t\t}\n\t\treturn { width: total };\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// CanvasMeasureAdapter (browser)\n// ---------------------------------------------------------------------------\n\nexport type CanvasMeasureAdapterOptions = {\n\t/** Emoji width correction factor (default: 1, no correction). */\n\temojiCorrection?: number;\n};\n\n/**\n * Browser measurement adapter using `OffscreenCanvas.measureText()`.\n *\n * Lazily creates an OffscreenCanvas and 2D context on first call.\n * Requires a browser environment with OffscreenCanvas support.\n */\nexport class CanvasMeasureAdapter implements MeasurementAdapter {\n\tprivate ctx: OffscreenCanvasRenderingContext2D | null = null;\n\tprivate currentFont = \"\";\n\tprivate readonly emojiCorrection: number;\n\n\tconstructor(opts?: CanvasMeasureAdapterOptions) {\n\t\tthis.emojiCorrection = opts?.emojiCorrection ?? 1;\n\t}\n\n\tprivate getContext(): OffscreenCanvasRenderingContext2D {\n\t\tif (!this.ctx) {\n\t\t\tif (typeof OffscreenCanvas === \"undefined\") {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"CanvasMeasureAdapter requires a browser environment with OffscreenCanvas support. \" +\n\t\t\t\t\t\t\"Use CliMeasureAdapter or NodeCanvasMeasureAdapter for Node.js.\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst canvas = new OffscreenCanvas(0, 0);\n\t\t\tconst ctx = canvas.getContext(\"2d\");\n\t\t\tif (!ctx) throw new Error(\"CanvasMeasureAdapter: failed to get 2d context\");\n\t\t\tthis.ctx = ctx;\n\t\t}\n\t\treturn this.ctx;\n\t}\n\n\tmeasureSegment(text: string, font: string): { width: number } {\n\t\tconst ctx = this.getContext();\n\t\tif (font !== this.currentFont) {\n\t\t\tctx.font = font;\n\t\t\tthis.currentFont = font;\n\t\t}\n\t\tlet width = ctx.measureText(text).width;\n\t\t// Apply emoji correction if configured\n\t\tif (this.emojiCorrection !== 1 && /\\p{Emoji_Presentation}/u.test(text)) {\n\t\t\twidth *= this.emojiCorrection;\n\t\t}\n\t\treturn { width };\n\t}\n\n\tclearCache(): void {\n\t\t// No segment cache; context font is the only state.\n\t\tthis.currentFont = \"\";\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// NodeCanvasMeasureAdapter (Node.js / CLI with canvas package)\n// ---------------------------------------------------------------------------\n\n/**\n * Canvas API subset expected from `@napi-rs/canvas` or `skia-canvas`.\n * Passed via dependency injection — no dynamic import, no polling.\n */\nexport type CanvasModule = {\n\tcreateCanvas(\n\t\twidth: number,\n\t\theight: number,\n\t): {\n\t\tgetContext(type: \"2d\"): {\n\t\t\tfont: string;\n\t\t\tmeasureText(text: string): { width: number };\n\t\t};\n\t};\n};\n\n/**\n * Node.js measurement adapter using an injected canvas module.\n *\n * ```ts\n * import * as canvas from \"@napi-rs/canvas\";\n * const adapter = new NodeCanvasMeasureAdapter(canvas);\n * ```\n *\n * Works with `@napi-rs/canvas`, `skia-canvas`, or any module exposing\n * `createCanvas(w, h).getContext(\"2d\").measureText(text)`.\n */\nexport class NodeCanvasMeasureAdapter implements MeasurementAdapter {\n\tprivate ctx: { font: string; measureText(text: string): { width: number } } | null = null;\n\tprivate currentFont = \"\";\n\tprivate readonly canvasModule: CanvasModule;\n\n\tconstructor(canvasModule: CanvasModule) {\n\t\tthis.canvasModule = canvasModule;\n\t}\n\n\tprivate getContext(): { font: string; measureText(text: string): { width: number } } {\n\t\tif (!this.ctx) {\n\t\t\tconst canvas = this.canvasModule.createCanvas(0, 0);\n\t\t\tconst ctx = canvas.getContext(\"2d\");\n\t\t\tif (!ctx) throw new Error(\"NodeCanvasMeasureAdapter: failed to get 2d context\");\n\t\t\tthis.ctx = ctx;\n\t\t}\n\t\treturn this.ctx;\n\t}\n\n\tmeasureSegment(text: string, font: string): { width: number } {\n\t\tconst ctx = this.getContext();\n\t\tif (font !== this.currentFont) {\n\t\t\tctx.font = font;\n\t\t\tthis.currentFont = font;\n\t\t}\n\t\treturn { width: ctx.measureText(text).width };\n\t}\n\n\tclearCache(): void {\n\t\tthis.currentFont = \"\";\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// SvgBoundsAdapter\n// ---------------------------------------------------------------------------\n\n/**\n * SVG measurement adapter — extracts dimensions from `viewBox` attribute\n * or explicit `width`/`height` attributes in the SVG string.\n *\n * Pure arithmetic: parses the SVG string for dimension attributes.\n * No DOM required. Works in any JS environment.\n *\n * Browser users who need `getBBox()` should pre-measure and pass explicit\n * `viewBox` on the content block instead.\n */\nexport class SvgBoundsAdapter {\n\tmeasureSvg(content: string): { width: number; height: number } {\n\t\t// Try viewBox first: viewBox=\"minX minY width height\"\n\t\tconst viewBoxMatch = content.match(/viewBox\\s*=\\s*[\"']([^\"']+)[\"']/);\n\t\tif (viewBoxMatch) {\n\t\t\tconst parts = viewBoxMatch[1]!.trim().split(/[\\s,]+/);\n\t\t\tif (parts.length >= 4) {\n\t\t\t\tconst w = Number.parseFloat(parts[2]!);\n\t\t\t\tconst h = Number.parseFloat(parts[3]!);\n\t\t\t\tif (Number.isFinite(w) && Number.isFinite(h) && w > 0 && h > 0) {\n\t\t\t\t\treturn { width: w, height: h };\n\t\t\t\t}\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"SvgBoundsAdapter: viewBox width/height are missing, non-finite, or not positive\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Fall back to explicit width/height attributes\n\t\tconst widthMatch = content.match(/<svg[^>]*\\bwidth\\s*=\\s*[\"']?([\\d.]+)/);\n\t\tconst heightMatch = content.match(/<svg[^>]*\\bheight\\s*=\\s*[\"']?([\\d.]+)/);\n\t\tif (widthMatch && heightMatch) {\n\t\t\tconst w = Number.parseFloat(widthMatch[1]!);\n\t\t\tconst h = Number.parseFloat(heightMatch[1]!);\n\t\t\tif (Number.isFinite(w) && Number.isFinite(h) && w > 0 && h > 0) {\n\t\t\t\treturn { width: w, height: h };\n\t\t\t}\n\t\t\tthrow new Error(\n\t\t\t\t\"SvgBoundsAdapter: svg width/height attributes are non-finite or not positive\",\n\t\t\t);\n\t\t}\n\n\t\tthrow new Error(\n\t\t\t\"SvgBoundsAdapter: cannot determine dimensions — SVG has no viewBox or width/height attributes\",\n\t\t);\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// ImageSizeAdapter\n// ---------------------------------------------------------------------------\n\n/**\n * Image measurement adapter — returns pre-registered dimensions by src key.\n *\n * Sync-only: dimensions must be provided upfront via the `sizes` map.\n * No I/O, no polling, no async. For browser use, pre-measure via\n * `Image.onload` and pass natural dimensions on the content block directly,\n * or register them here.\n *\n * ```ts\n * const adapter = new ImageSizeAdapter({\n * \"hero.png\": { width: 1200, height: 630 },\n * \"logo.svg\": { width: 120, height: 40 },\n * });\n * ```\n */\nexport class ImageSizeAdapter {\n\tprivate readonly sizes: Map<string, { width: number; height: number }>;\n\n\tconstructor(sizes: Record<string, { width: number; height: number }>) {\n\t\tthis.sizes = new Map(Object.entries(sizes));\n\t}\n\n\tmeasureImage(src: string): { width: number; height: number } {\n\t\tconst dims = this.sizes.get(src);\n\t\tif (!dims) {\n\t\t\tthrow new Error(`ImageSizeAdapter: no dimensions registered for ${JSON.stringify(src)}`);\n\t\t}\n\t\treturn { width: dims.width, height: dims.height };\n\t}\n}\n","/**\n * Reactive text layout engine (roadmap §7.1 — Pretext parity).\n *\n * Pure-arithmetic text measurement and line breaking without DOM thrashing.\n * Inspired by [Pretext](https://github.com/chenglou/pretext), rebuilt as a\n * GraphReFly graph — inspectable via `describe()`, snapshotable, debuggable.\n *\n * Two-tier DX:\n * - `reactiveLayout({ adapter, text?, font?, lineHeight?, maxWidth?, name? })` — convenience factory\n * - `MeasurementAdapter` — pluggable backends (`measureSegment`; optional `clearCache`)\n */\nimport { monotonicNs, type Node, node } from \"@graphrefly/pure-ts/core\";\n\nimport { Graph } from \"@graphrefly/pure-ts/graph\";\nimport { emitToMeta } from \"../../base/meta/emit-to-meta.js\";\nimport { getDefaultSegmentAdapter } from \"./measurement-adapters.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Pluggable measurement backend. */\nexport interface MeasurementAdapter {\n\tmeasureSegment(text: string, font: string): { width: number };\n\t/** Optional; adapters may omit for read-only / stateless measurement. */\n\tclearCache?(): void;\n}\n\n/**\n * A single segmented piece — the structurally-narrowed common shape across\n * `Intl.Segmenter`'s `Intl.SegmentData` and host-provided polyfills.\n *\n * Drops `input` (redundant — the caller already has the text) and narrows\n * `isWordLike` to \"may be missing\" so grapheme-granularity callers ignore it\n * cleanly.\n */\nexport type SegmentInfo = {\n\t/** The segmented substring. */\n\tsegment: string;\n\t/** Code-unit offset of `segment` within the input. */\n\tindex: number;\n\t/** True if the word-granularity segment looks like a word (letters / kana / etc.). Always undefined for grapheme granularity. */\n\tisWordLike?: boolean;\n};\n\n/**\n * Pluggable text-segmentation backend (separate from {@link MeasurementAdapter}\n * because measurement and segmentation are different host concerns —\n * Skia/Canvas measure widths, ICU segments graphemes/words).\n *\n * **Why this exists (DS-2026-05-20 — `optimizations.md` 🟠 (d)).**\n * `reactive-layout`'s default backend uses `new Intl.Segmenter(...)` for word /\n * grapheme iteration. Hermes (iOS 26.5 / RN 0.83) ships **without**\n * `Intl.Segmenter` — `typeof Intl.Segmenter === \"undefined\"` — and the\n * constructor throws `Cannot read property 'prototype' of undefined`. This\n * interface lets RN/Hermes consumers inject their own segmenter (typically a\n * polyfill wrapper) so the substrate never touches the missing global.\n *\n * **Contract:** sync, pure, idempotent. `segmentWords` must mirror\n * `Intl.Segmenter(undefined, { granularity: \"word\" }).segment(text)`'s shape\n * (an iterable of `{ segment, index, isWordLike }`); `segmentGraphemes`\n * mirrors `{ granularity: \"grapheme\" }`. The reference implementation is\n * {@link IntlSegmentAdapter}.\n *\n * **Polyfill recipe (RN/Hermes consumer userland — NOT shipped here per the\n * `bigintJsonCodecFor` userland-binding precedent):**\n *\n * ```ts\n * // Userland — at app entry, before any reactive-layout import:\n * import \"intl-segmenter-polyfill/dist/polyfill\"; // or @formatjs/intl-segmenter\n * // Then the substrate's default IntlSegmentAdapter works:\n * import { reactiveLayout } from \"@graphrefly/graphrefly/utils/reactive-layout\";\n * ```\n *\n * Or, without polyfilling the global (preferred — keeps ICU bytes scoped):\n *\n * ```ts\n * // Userland — wrap any segmenter implementation:\n * import { createIntlSegmenterPolyfill } from \"intl-segmenter-polyfill\";\n * import type { SegmentAdapter, SegmentInfo } from \"@graphrefly/graphrefly/utils/reactive-layout\";\n *\n * const wordSeg = await createIntlSegmenterPolyfill({ granularity: \"word\" });\n * const graphemeSeg = await createIntlSegmenterPolyfill({ granularity: \"grapheme\" });\n * const segmentAdapter: SegmentAdapter = {\n * segmentWords: (text) => wordSeg.segment(text) as Iterable<SegmentInfo>,\n * segmentGraphemes: (text) => graphemeSeg.segment(text) as Iterable<SegmentInfo>,\n * };\n * reactiveLayout({ adapter, segmentAdapter, ... });\n * ```\n */\nexport interface SegmentAdapter {\n\t/** Word-granularity segmentation — yields `{ segment, index, isWordLike }`. */\n\tsegmentWords(text: string): Iterable<SegmentInfo>;\n\t/** Grapheme-granularity segmentation — yields `{ segment, index }`. `isWordLike` is unused / undefined. */\n\tsegmentGraphemes(text: string): Iterable<SegmentInfo>;\n}\n\n/** Mutable counters for `analyzeAndMeasure` cache hit ratio (hits / (hits + misses)). */\nexport type SegmentMeasureStats = { hits: number; misses: number };\n\n/** Break kind for each segment (ported from Pretext analysis.ts). */\nexport type SegmentBreakKind = \"text\" | \"space\" | \"zero-width-break\" | \"soft-hyphen\" | \"hard-break\";\n\n/** A measured text segment ready for line breaking. */\nexport type PreparedSegment = {\n\ttext: string;\n\twidth: number;\n\tkind: SegmentBreakKind;\n\t/** Grapheme widths for overflow-wrap: break-word (null if single grapheme). */\n\tgraphemeWidths: number[] | null;\n};\n\n/** A laid-out line with start/end cursors. */\nexport type LayoutLine = {\n\ttext: string;\n\twidth: number;\n\tstartSegment: number;\n\tstartGrapheme: number;\n\tendSegment: number;\n\tendGrapheme: number;\n};\n\n/** Per-character position for hit testing. */\nexport type CharPosition = {\n\tx: number;\n\ty: number;\n\twidth: number;\n\theight: number;\n\tline: number;\n};\n\n/** Full layout result from the line-breaks derived node. */\nexport type LineBreaksResult = {\n\tlines: LayoutLine[];\n\tlineCount: number;\n};\n\n/**\n * A position within `PreparedSegment[]` — segment + grapheme offset.\n * `graphemeIndex: 0` at segment boundaries.\n *\n * Used by {@link layoutNextLine} for cursor-based line walking; needed when\n * lines have varying widths (multi-column flow, text wrapping around obstacles).\n */\nexport type LayoutCursor = {\n\tsegmentIndex: number;\n\tgraphemeIndex: number;\n};\n\n/** A horizontal span `[left, right]` in pixels — used by flow-layout slot carving. */\nexport type Interval = { left: number; right: number };\n\n/** Result of a single `layoutNextLine` call. */\nexport type LayoutNextLineResult = {\n\ttext: string;\n\twidth: number;\n\tstart: LayoutCursor;\n\tend: LayoutCursor;\n};\n\n/** Optional context for `layoutNextLine` — enables soft-hyphen visible-hyphen rendering. */\nexport type LayoutNextLineContext = {\n\tadapter?: MeasurementAdapter;\n\tfont?: string;\n\tcache?: Map<string, Map<string, number>>;\n\t/**\n\t * Optional {@link SegmentAdapter} for grapheme slicing during partial-segment\n\t * line builds. Defaults to {@link IntlSegmentAdapter} (lazy module shared);\n\t * Hermes / RN consumers wire their own to avoid the missing-`Intl.Segmenter`\n\t * runtime throw — see {@link SegmentAdapter} JSDoc.\n\t */\n\tsegmentAdapter?: SegmentAdapter;\n};\n\n/** Result of the reactive layout graph's describe-accessible state. */\nexport type ReactiveLayoutBundle = {\n\tgraph: Graph;\n\t/** Set input text. */\n\tsetText: (text: string) => void;\n\t/** Set CSS font string. */\n\tsetFont: (font: string) => void;\n\t/** Set line height (px). */\n\tsetLineHeight: (lineHeight: number) => void;\n\t/** Set max width constraint (px). */\n\tsetMaxWidth: (maxWidth: number) => void;\n\t/** Segments node. */\n\tsegments: Node<PreparedSegment[]>;\n\t/** Line breaks node. */\n\tlineBreaks: Node<LineBreaksResult>;\n\t/** Total height node. */\n\theight: Node<number>;\n\t/** Per-character positions node. */\n\tcharPositions: Node<CharPosition[]>;\n};\n\n// ---------------------------------------------------------------------------\n// Text analysis (ported from Pretext analysis.ts — core subset)\n// ---------------------------------------------------------------------------\n\n// CJK detection (Unicode CJK Unified Ideographs + common ranges)\nfunction isCJK(s: string): boolean {\n\tfor (const ch of s) {\n\t\tconst c = ch.codePointAt(0)!;\n\t\tif (\n\t\t\t(c >= 0x4e00 && c <= 0x9fff) || // CJK Unified Ideographs\n\t\t\t(c >= 0x3400 && c <= 0x4dbf) || // CJK Extension A\n\t\t\t(c >= 0x3000 && c <= 0x303f) || // CJK Symbols and Punctuation\n\t\t\t(c >= 0x3040 && c <= 0x309f) || // Hiragana\n\t\t\t(c >= 0x30a0 && c <= 0x30ff) || // Katakana\n\t\t\t(c >= 0xac00 && c <= 0xd7af) || // Hangul\n\t\t\t(c >= 0xff00 && c <= 0xffef) // Fullwidth Forms\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\n// Kinsoku: characters that cannot start a line (CJK punctuation)\nconst kinsokuStart = new Set([\n\t\"\\uff0c\",\n\t\"\\uff0e\",\n\t\"\\uff01\",\n\t\"\\uff1a\",\n\t\"\\uff1b\",\n\t\"\\uff1f\",\n\t\"\\u3001\",\n\t\"\\u3002\",\n\t\"\\u30fb\",\n\t\"\\uff09\",\n\t\"\\u3015\",\n\t\"\\u3009\",\n\t\"\\u300b\",\n\t\"\\u300d\",\n\t\"\\u300f\",\n\t\"\\u3011\",\n]);\n\n// Left-sticky punctuation (merges into preceding segment)\nconst leftStickyPunctuation = new Set([\n\t\".\",\n\t\",\",\n\t\"!\",\n\t\"?\",\n\t\":\",\n\t\";\",\n\t\")\",\n\t\"]\",\n\t\"}\",\n\t\"%\",\n\t'\"',\n\t\"\\u201d\",\n\t\"\\u2019\",\n\t\"\\u00bb\",\n\t\"\\u203a\",\n\t\"\\u2026\",\n]);\n\n/** Normalize collapsible whitespace (CSS white-space: normal). */\nfunction normalizeWhitespace(text: string): string {\n\treturn text.replace(/[\\t\\n\\r\\f ]+/g, \" \").replace(/^ | $/g, \"\");\n}\n\n/**\n * Segment text using the supplied {@link SegmentAdapter} (word granularity)\n * and classify break kinds. Returns raw segmentation pieces before merging.\n */\nfunction segmentText(\n\tnormalized: string,\n\tsegmentAdapter: SegmentAdapter,\n): {\n\ttexts: string[];\n\tisWordLike: boolean[];\n\tkinds: SegmentBreakKind[];\n}[] {\n\tconst pieces: {\n\t\ttexts: string[];\n\t\tisWordLike: boolean[];\n\t\tkinds: SegmentBreakKind[];\n\t}[] = [];\n\n\tfor (const s of segmentAdapter.segmentWords(normalized)) {\n\t\tconst text = s.segment;\n\t\tconst isWordLike = s.isWordLike ?? false;\n\n\t\t// Split segment by break-relevant characters\n\t\tconst texts: string[] = [];\n\t\tconst wordLikes: boolean[] = [];\n\t\tconst kinds: SegmentBreakKind[] = [];\n\n\t\tlet currentText = \"\";\n\t\tlet currentKind: SegmentBreakKind | null = null;\n\n\t\tfor (const ch of text) {\n\t\t\tlet kind: SegmentBreakKind;\n\t\t\tif (ch === \" \") kind = \"space\";\n\t\t\telse if (ch === \"\\u200b\") kind = \"zero-width-break\";\n\t\t\telse if (ch === \"\\u00ad\") kind = \"soft-hyphen\";\n\t\t\telse if (ch === \"\\n\") kind = \"hard-break\";\n\t\t\telse kind = \"text\";\n\n\t\t\tif (currentKind !== null && kind === currentKind) {\n\t\t\t\tcurrentText += ch;\n\t\t\t} else {\n\t\t\t\tif (currentKind !== null) {\n\t\t\t\t\ttexts.push(currentText);\n\t\t\t\t\twordLikes.push(currentKind === \"text\" && isWordLike);\n\t\t\t\t\tkinds.push(currentKind);\n\t\t\t\t}\n\t\t\t\tcurrentText = ch;\n\t\t\t\tcurrentKind = kind;\n\t\t\t}\n\t\t}\n\n\t\tif (currentKind !== null) {\n\t\t\ttexts.push(currentText);\n\t\t\twordLikes.push(currentKind === \"text\" && isWordLike);\n\t\t\tkinds.push(currentKind);\n\t\t}\n\n\t\tpieces.push({ texts, isWordLike: wordLikes, kinds });\n\t}\n\treturn pieces;\n}\n\n/**\n * Merge segmentation pieces: sticky punctuation, CJK per-grapheme splitting,\n * and produce the final measured segment list.\n */\nexport function analyzeAndMeasure(\n\ttext: string,\n\tfont: string,\n\tadapter: MeasurementAdapter,\n\tcache: Map<string, Map<string, number>>,\n\tstats?: SegmentMeasureStats,\n\tsegmentAdapter?: SegmentAdapter,\n): PreparedSegment[] {\n\tconst normalized = normalizeWhitespace(text);\n\tif (normalized.length === 0) return [];\n\n\tconst segAdapter = segmentAdapter ?? getDefaultSegmentAdapter();\n\tconst pieces = segmentText(normalized, segAdapter);\n\n\t// Flatten pieces into a single segment list with merging\n\tconst rawTexts: string[] = [];\n\tconst rawKinds: SegmentBreakKind[] = [];\n\tconst rawWordLike: boolean[] = [];\n\n\tfor (const piece of pieces) {\n\t\tfor (let i = 0; i < piece.texts.length; i++) {\n\t\t\trawTexts.push(piece.texts[i]!);\n\t\t\trawKinds.push(piece.kinds[i]!);\n\t\t\trawWordLike.push(piece.isWordLike[i]!);\n\t\t}\n\t}\n\n\t// Merge: left-sticky punctuation and kinsoku-start into preceding text segment\n\tconst mergedTexts: string[] = [];\n\tconst mergedKinds: SegmentBreakKind[] = [];\n\tconst mergedWordLike: boolean[] = [];\n\n\tfor (let i = 0; i < rawTexts.length; i++) {\n\t\tconst t = rawTexts[i]!;\n\t\tconst k = rawKinds[i]!;\n\t\tconst wl = rawWordLike[i]!;\n\n\t\t// Merge left-sticky punctuation into preceding text\n\t\tif (\n\t\t\tk === \"text\" &&\n\t\t\t!wl &&\n\t\t\tmergedTexts.length > 0 &&\n\t\t\tmergedKinds[mergedKinds.length - 1] === \"text\"\n\t\t) {\n\t\t\tconst isSticky = t.length === 1 && (leftStickyPunctuation.has(t) || kinsokuStart.has(t));\n\t\t\tif (isSticky) {\n\t\t\t\tmergedTexts[mergedTexts.length - 1] += t;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// Merge hyphen after word into preceding text (\"well-known\" stays together)\n\t\tif (\n\t\t\tt === \"-\" &&\n\t\t\tmergedTexts.length > 0 &&\n\t\t\tmergedKinds[mergedKinds.length - 1] === \"text\" &&\n\t\t\tmergedWordLike[mergedWordLike.length - 1]\n\t\t) {\n\t\t\tmergedTexts[mergedTexts.length - 1] += t;\n\t\t\tcontinue;\n\t\t}\n\n\t\tmergedTexts.push(t);\n\t\tmergedKinds.push(k);\n\t\tmergedWordLike.push(wl);\n\t}\n\n\t// Get or create font-specific cache\n\tlet fontCache = cache.get(font);\n\tif (!fontCache) {\n\t\tfontCache = new Map<string, number>();\n\t\tcache.set(font, fontCache);\n\t}\n\n\tfunction measureCached(seg: string): number {\n\t\tlet w = fontCache!.get(seg);\n\t\tif (w === undefined) {\n\t\t\tif (stats) stats.misses += 1;\n\t\t\tconst raw = adapter.measureSegment(seg, font).width;\n\t\t\t// Coerce adapter misbehavior (NaN / Infinity / negative) to 0 — downstream\n\t\t\t// arithmetic would propagate NaN widths, breaking line-break decisions and\n\t\t\t// rendering. Cached so the coercion happens once per (font, segment).\n\t\t\tw = Number.isFinite(raw) && raw >= 0 ? raw : 0;\n\t\t\tfontCache!.set(seg, w);\n\t\t} else if (stats) {\n\t\t\tstats.hits += 1;\n\t\t}\n\t\treturn w;\n\t}\n\n\t// Build final prepared segments, splitting CJK into per-grapheme\n\tconst segments: PreparedSegment[] = [];\n\n\tfor (let i = 0; i < mergedTexts.length; i++) {\n\t\tconst t = mergedTexts[i]!;\n\t\tconst k = mergedKinds[i]!;\n\n\t\tif (k !== \"text\") {\n\t\t\t// Non-text segments: space, hard-break, soft-hyphen, zero-width-break\n\t\t\tsegments.push({\n\t\t\t\ttext: t,\n\t\t\t\twidth: k === \"space\" ? measureCached(\" \") * t.length : 0,\n\t\t\t\tkind: k,\n\t\t\t\tgraphemeWidths: null,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\t// CJK text: split into per-grapheme segments for line breaking\n\t\tif (isCJK(t)) {\n\t\t\tlet unitText = \"\";\n\t\t\tfor (const gs of segAdapter.segmentGraphemes(t)) {\n\t\t\t\tconst grapheme = gs.segment;\n\n\t\t\t\t// Kinsoku: line-start-prohibited chars stick to preceding unit\n\t\t\t\tif (unitText.length > 0 && kinsokuStart.has(grapheme)) {\n\t\t\t\t\tunitText += grapheme;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (unitText.length > 0) {\n\t\t\t\t\tconst w = measureCached(unitText);\n\t\t\t\t\tsegments.push({\n\t\t\t\t\t\ttext: unitText,\n\t\t\t\t\t\twidth: w,\n\t\t\t\t\t\tkind: \"text\",\n\t\t\t\t\t\tgraphemeWidths: null,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tunitText = grapheme;\n\t\t\t}\n\t\t\tif (unitText.length > 0) {\n\t\t\t\tconst w = measureCached(unitText);\n\t\t\t\tsegments.push({\n\t\t\t\t\ttext: unitText,\n\t\t\t\t\twidth: w,\n\t\t\t\t\tkind: \"text\",\n\t\t\t\t\tgraphemeWidths: null,\n\t\t\t\t});\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Non-CJK text: measure whole segment, pre-compute grapheme widths for break-word\n\t\tconst w = measureCached(t);\n\t\tlet graphemeWidths: number[] | null = null;\n\n\t\tif (mergedWordLike[i] && t.length > 1) {\n\t\t\tconst gWidths: number[] = [];\n\t\t\tfor (const gs of segAdapter.segmentGraphemes(t)) {\n\t\t\t\tgWidths.push(measureCached(gs.segment));\n\t\t\t}\n\t\t\tif (gWidths.length > 1) {\n\t\t\t\tgraphemeWidths = gWidths;\n\t\t\t}\n\t\t}\n\n\t\tsegments.push({ text: t, width: w, kind: \"text\", graphemeWidths });\n\t}\n\n\treturn segments;\n}\n\n// ---------------------------------------------------------------------------\n// Line breaking (greedy, ported from Pretext line-break.ts — core subset)\n// ---------------------------------------------------------------------------\n\n/**\n * Greedy line-breaking algorithm.\n *\n * Walks segments left to right, accumulating width. Breaks when a segment would\n * overflow maxWidth. Supports:\n * - Trailing space hang (spaces don't trigger breaks)\n * - overflow-wrap: break-word via grapheme widths\n * - Soft hyphens (break opportunity, adds visible hyphen width)\n * - Hard breaks (forced newline)\n */\nexport function computeLineBreaks(\n\tsegments: PreparedSegment[],\n\tmaxWidth: number,\n\tadapter: MeasurementAdapter,\n\tfont: string,\n\tcache: Map<string, Map<string, number>>,\n\tsegmentAdapter?: SegmentAdapter,\n): LineBreaksResult {\n\tif (segments.length === 0) {\n\t\treturn { lines: [], lineCount: 0 };\n\t}\n\tconst segAdapter = segmentAdapter ?? getDefaultSegmentAdapter();\n\n\tconst lines: LayoutLine[] = [];\n\tlet lineW = 0;\n\tlet hasContent = false;\n\tlet lineStartSeg = 0;\n\tlet lineStartGrapheme = 0;\n\tlet lineEndSeg = 0;\n\tlet lineEndGrapheme = 0;\n\tlet pendingBreakSeg = -1;\n\tlet pendingBreakWidth = 0;\n\n\t// Measure hyphen for soft-hyphen support\n\tlet fontCache = cache.get(font);\n\tif (!fontCache) {\n\t\tfontCache = new Map<string, number>();\n\t\tcache.set(font, fontCache);\n\t}\n\tlet hyphenWidth = fontCache.get(\"-\");\n\tif (hyphenWidth === undefined) {\n\t\thyphenWidth = adapter.measureSegment(\"-\", font).width;\n\t\tfontCache.set(\"-\", hyphenWidth);\n\t}\n\n\tfunction flushLine(endSeg = lineEndSeg, endGrapheme = lineEndGrapheme, width = lineW) {\n\t\t// Build line text\n\t\tlet text = \"\";\n\t\tfor (let i = lineStartSeg; i < endSeg; i++) {\n\t\t\tconst seg = segments[i]!;\n\t\t\tif (seg.kind === \"soft-hyphen\" || seg.kind === \"hard-break\") continue;\n\t\t\tif (i === lineStartSeg && lineStartGrapheme > 0 && seg.graphemeWidths) {\n\t\t\t\t// Partial segment from grapheme break\n\t\t\t\tconst graphemes = [...segAdapter.segmentGraphemes(seg.text)].map((g) => g.segment);\n\t\t\t\ttext += graphemes.slice(lineStartGrapheme).join(\"\");\n\t\t\t} else {\n\t\t\t\ttext += seg.text;\n\t\t\t}\n\t\t}\n\t\t// Handle partial end segment\n\t\tif (endGrapheme > 0 && endSeg < segments.length) {\n\t\t\tconst seg = segments[endSeg]!;\n\t\t\tconst graphemes = [...segAdapter.segmentGraphemes(seg.text)].map((g) => g.segment);\n\t\t\tconst startG = lineStartSeg === endSeg ? lineStartGrapheme : 0;\n\t\t\ttext += graphemes.slice(startG, endGrapheme).join(\"\");\n\t\t}\n\t\t// Add visible hyphen if line ends at soft-hyphen\n\t\tif (\n\t\t\tendSeg > 0 &&\n\t\t\tsegments[endSeg - 1]?.kind === \"soft-hyphen\" &&\n\t\t\t!(lineStartSeg === endSeg && lineStartGrapheme > 0)\n\t\t) {\n\t\t\ttext += \"-\";\n\t\t}\n\n\t\tlines.push({\n\t\t\ttext,\n\t\t\twidth,\n\t\t\tstartSegment: lineStartSeg,\n\t\t\tstartGrapheme: lineStartGrapheme,\n\t\t\tendSegment: endSeg,\n\t\t\tendGrapheme,\n\t\t});\n\t\tlineW = 0;\n\t\thasContent = false;\n\t\tpendingBreakSeg = -1;\n\t\tpendingBreakWidth = 0;\n\t}\n\n\tfunction canBreakAfter(kind: SegmentBreakKind): boolean {\n\t\treturn kind === \"space\" || kind === \"zero-width-break\" || kind === \"soft-hyphen\";\n\t}\n\n\tfunction startLine(segIdx: number, graphemeIdx: number, width: number) {\n\t\thasContent = true;\n\t\tlineStartSeg = segIdx;\n\t\tlineStartGrapheme = graphemeIdx;\n\t\tlineEndSeg = segIdx + 1;\n\t\tlineEndGrapheme = 0;\n\t\tlineW = width;\n\t}\n\n\tfunction startLineAtGrapheme(segIdx: number, graphemeIdx: number, width: number) {\n\t\thasContent = true;\n\t\tlineStartSeg = segIdx;\n\t\tlineStartGrapheme = graphemeIdx;\n\t\tlineEndSeg = segIdx;\n\t\tlineEndGrapheme = graphemeIdx + 1;\n\t\tlineW = width;\n\t}\n\n\tfor (let i = 0; i < segments.length; i++) {\n\t\tconst seg = segments[i]!;\n\n\t\t// Hard break: emit current line, start fresh\n\t\tif (seg.kind === \"hard-break\") {\n\t\t\tif (hasContent) {\n\t\t\t\tflushLine();\n\t\t\t} else {\n\t\t\t\t// Empty line\n\t\t\t\tlines.push({\n\t\t\t\t\ttext: \"\",\n\t\t\t\t\twidth: 0,\n\t\t\t\t\tstartSegment: i,\n\t\t\t\t\tstartGrapheme: 0,\n\t\t\t\t\tendSegment: i,\n\t\t\t\t\tendGrapheme: 0,\n\t\t\t\t});\n\t\t\t}\n\t\t\tlineStartSeg = i + 1;\n\t\t\tlineStartGrapheme = 0;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst w = seg.width;\n\n\t\tif (!hasContent) {\n\t\t\t// First content on a new line\n\t\t\tif (w > maxWidth && seg.graphemeWidths) {\n\t\t\t\t// Word wider than maxWidth: break at grapheme level\n\t\t\t\tappendBreakableSegment(i, 0, seg.graphemeWidths);\n\t\t\t} else {\n\t\t\t\tstartLine(i, 0, w);\n\t\t\t}\n\t\t\tif (canBreakAfter(seg.kind)) {\n\t\t\t\tpendingBreakSeg = i + 1;\n\t\t\t\tpendingBreakWidth = seg.kind === \"space\" ? lineW - w : lineW;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst newW = lineW + w;\n\n\t\tif (newW > maxWidth + 0.005) {\n\t\t\t// Overflow\n\t\t\tif (canBreakAfter(seg.kind)) {\n\t\t\t\t// Trailing space: hang past edge, then break\n\t\t\t\tlineW += w;\n\t\t\t\tlineEndSeg = i + 1;\n\t\t\t\tlineEndGrapheme = 0;\n\t\t\t\tflushLine(i + 1, 0, seg.kind === \"space\" ? lineW - w : lineW);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (pendingBreakSeg >= 0) {\n\t\t\t\t// Break at last break opportunity\n\t\t\t\tflushLine(pendingBreakSeg, 0, pendingBreakWidth);\n\t\t\t\t// Don't advance i — re-process current segment on new line\n\t\t\t\ti--;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (w > maxWidth && seg.graphemeWidths) {\n\t\t\t\t// Break-word: split at grapheme level\n\t\t\t\tflushLine();\n\t\t\t\tappendBreakableSegment(i, 0, seg.graphemeWidths);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// No break opportunity: force break before this segment\n\t\t\tflushLine();\n\t\t\ti--;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Fits on current line\n\t\tlineW = newW;\n\t\tlineEndSeg = i + 1;\n\t\tlineEndGrapheme = 0;\n\n\t\tif (canBreakAfter(seg.kind)) {\n\t\t\tpendingBreakSeg = i + 1;\n\t\t\tpendingBreakWidth = seg.kind === \"space\" ? lineW - w : lineW;\n\t\t}\n\t}\n\n\tif (hasContent) {\n\t\tflushLine();\n\t}\n\n\treturn { lines, lineCount: lines.length };\n\n\tfunction appendBreakableSegment(segIdx: number, startG: number, gWidths: number[]) {\n\t\tfor (let g = startG; g < gWidths.length; g++) {\n\t\t\tconst gw = gWidths[g]!;\n\t\t\tif (!hasContent) {\n\t\t\t\tstartLineAtGrapheme(segIdx, g, gw);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (lineW + gw > maxWidth + 0.005) {\n\t\t\t\tflushLine();\n\t\t\t\tstartLineAtGrapheme(segIdx, g, gw);\n\t\t\t} else {\n\t\t\t\tlineW += gw;\n\t\t\t\tlineEndSeg = segIdx;\n\t\t\t\tlineEndGrapheme = g + 1;\n\t\t\t}\n\t\t}\n\t\t// If we consumed the whole segment, advance end past it\n\t\tif (hasContent && lineEndSeg === segIdx && lineEndGrapheme === gWidths.length) {\n\t\t\tlineEndSeg = segIdx + 1;\n\t\t\tlineEndGrapheme = 0;\n\t\t}\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Cursor-based single-line layout (for multi-column flow / shape wrapping)\n// ---------------------------------------------------------------------------\n\nfunction canBreakAfter(kind: SegmentBreakKind): boolean {\n\treturn kind === \"space\" || kind === \"zero-width-break\" || kind === \"soft-hyphen\";\n}\n\nfunction sliceSegmentText(\n\tseg: PreparedSegment,\n\tstartG: number,\n\tendG: number,\n\tsegmentAdapter: SegmentAdapter,\n): string {\n\tif (startG === 0 && endG < 0) return seg.text;\n\tconst graphemes = [...segmentAdapter.segmentGraphemes(seg.text)].map((g) => g.segment);\n\tconst stop = endG < 0 ? graphemes.length : endG;\n\treturn graphemes.slice(startG, stop).join(\"\");\n}\n\nfunction buildLineText(\n\tsegments: PreparedSegment[],\n\tstartSeg: number,\n\tstartG: number,\n\tendSeg: number,\n\tendG: number,\n\tappendHyphen: boolean,\n\tsegmentAdapter: SegmentAdapter,\n): string {\n\tlet text = \"\";\n\tfor (let i = startSeg; i < endSeg; i++) {\n\t\tconst seg = segments[i]!;\n\t\tif (seg.kind === \"soft-hyphen\" || seg.kind === \"hard-break\") continue;\n\t\tif (i === startSeg && startG > 0) {\n\t\t\ttext += sliceSegmentText(seg, startG, -1, segmentAdapter);\n\t\t} else {\n\t\t\ttext += seg.text;\n\t\t}\n\t}\n\tif (endG > 0 && endSeg < segments.length) {\n\t\tconst seg = segments[endSeg]!;\n\t\tconst from = startSeg === endSeg ? startG : 0;\n\t\ttext += sliceSegmentText(seg, from, endG, segmentAdapter);\n\t}\n\tif (appendHyphen) text += \"-\";\n\treturn text;\n}\n\nfunction resolveHyphenWidth(ctx: LayoutNextLineContext | undefined): number {\n\tif (!ctx?.adapter || !ctx.font) return 0;\n\tconst cache = ctx.cache;\n\tif (cache) {\n\t\tlet fc = cache.get(ctx.font);\n\t\tif (!fc) {\n\t\t\tfc = new Map<string, number>();\n\t\t\tcache.set(ctx.font, fc);\n\t\t}\n\t\tlet hw = fc.get(\"-\");\n\t\tif (hw === undefined) {\n\t\t\thw = ctx.adapter.measureSegment(\"-\", ctx.font).width;\n\t\t\tfc.set(\"-\", hw);\n\t\t}\n\t\treturn hw;\n\t}\n\treturn ctx.adapter.measureSegment(\"-\", ctx.font).width;\n}\n\n/**\n * Lay out the next single line starting from `cursor`, fitting into `slotWidth`.\n *\n * Unlike `computeLineBreaks`, which consumes whole text with one `maxWidth`,\n * this is the cursor-based primitive needed when successive lines have different\n * widths (multi-column flow, text wrapping around shape obstacles, mixed\n * column+pullquote layouts).\n *\n * Returns `null` when the cursor is past all segments (text exhausted).\n * At a hard-break with no preceding content, returns an empty line and advances\n * the cursor past the break so the caller can continue.\n *\n * ```ts\n * let cursor: LayoutCursor = { segmentIndex: 0, graphemeIndex: 0 };\n * while (true) {\n * const line = layoutNextLine(segments, cursor, availableWidth);\n * if (line === null) break;\n * render(line);\n * cursor = line.end;\n * }\n * ```\n */\nexport function layoutNextLine(\n\tsegments: PreparedSegment[],\n\tcursor: LayoutCursor,\n\tslotWidth: number,\n\tctx?: LayoutNextLineContext,\n): LayoutNextLineResult | null {\n\tlet i = cursor.segmentIndex;\n\tconst initialG = cursor.graphemeIndex;\n\tconst segAdapter = ctx?.segmentAdapter ?? getDefaultSegmentAdapter();\n\n\tif (i >= segments.length) return null;\n\n\tif (initialG === 0) {\n\t\twhile (i < segments.length) {\n\t\t\tconst seg = segments[i]!;\n\t\t\tif (seg.kind === \"hard-break\") {\n\t\t\t\treturn {\n\t\t\t\t\ttext: \"\",\n\t\t\t\t\twidth: 0,\n\t\t\t\t\tstart: { segmentIndex: cursor.segmentIndex, graphemeIndex: 0 },\n\t\t\t\t\tend: { segmentIndex: i + 1, graphemeIndex: 0 },\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (seg.kind === \"space\" || seg.kind === \"zero-width-break\" || seg.kind === \"soft-hyphen\") {\n\t\t\t\ti += 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tif (i >= segments.length) return null;\n\t}\n\n\tconst hyphenWidth = resolveHyphenWidth(ctx);\n\n\tconst startSeg = i;\n\tconst startG = i === cursor.segmentIndex ? initialG : 0;\n\n\tlet lineW = 0;\n\tlet lineEndSeg = startSeg;\n\tlet lineEndG = 0;\n\tlet hasContent = false;\n\tlet pendingBreakSeg = -1;\n\tlet pendingBreakG = 0;\n\tlet pendingBreakWidth = 0;\n\tlet pendingBreakSoftHyphen = false;\n\n\tconst recordPending = (\n\t\tsIdx: number,\n\t\tgIdx: number,\n\t\twidthAtBreak: number,\n\t\tkind: SegmentBreakKind,\n\t): void => {\n\t\tpendingBreakSeg = sIdx;\n\t\tpendingBreakG = gIdx;\n\t\tpendingBreakWidth = widthAtBreak;\n\t\tpendingBreakSoftHyphen = kind === \"soft-hyphen\";\n\t};\n\n\tconst consumeBreakable = (segIdx: number, gStart: number, gWidths: number[]): boolean => {\n\t\tfor (let g = gStart; g < gWidths.length; g++) {\n\t\t\tconst gw = gWidths[g]!;\n\t\t\tif (!hasContent) {\n\t\t\t\tlineW = gw;\n\t\t\t\tlineEndSeg = segIdx;\n\t\t\t\tlineEndG = g + 1;\n\t\t\t\thasContent = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (lineW + gw > slotWidth + 0.005) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tlineW += gw;\n\t\t\tlineEndSeg = segIdx;\n\t\t\tlineEndG = g + 1;\n\t\t}\n\t\tif (lineEndSeg === segIdx && lineEndG === gWidths.length) {\n\t\t\tlineEndSeg = segIdx + 1;\n\t\t\tlineEndG = 0;\n\t\t}\n\t\treturn false;\n\t};\n\n\tif (startG > 0 && startSeg < segments.length) {\n\t\tconst seg = segments[startSeg]!;\n\t\tif (seg.graphemeWidths) {\n\t\t\tconst overflowed = consumeBreakable(startSeg, startG, seg.graphemeWidths);\n\t\t\tif (overflowed) {\n\t\t\t\tconst text = buildLineText(\n\t\t\t\t\tsegments,\n\t\t\t\t\tstartSeg,\n\t\t\t\t\tstartG,\n\t\t\t\t\tlineEndSeg,\n\t\t\t\t\tlineEndG,\n\t\t\t\t\tfalse,\n\t\t\t\t\tsegAdapter,\n\t\t\t\t);\n\t\t\t\treturn {\n\t\t\t\t\ttext,\n\t\t\t\t\twidth: lineW,\n\t\t\t\t\tstart: { segmentIndex: startSeg, graphemeIndex: startG },\n\t\t\t\t\tend: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG },\n\t\t\t\t};\n\t\t\t}\n\t\t\ti = lineEndSeg;\n\t\t} else {\n\t\t\t// Mid-segment cursor on a non-breakable segment is an invariant\n\t\t\t// violation (cursor should only advance to a grapheme boundary via\n\t\t\t// `consumeBreakable` on a segment that HAS graphemeWidths). Treat as\n\t\t\t// segment-start so the caller gets well-formed output instead of\n\t\t\t// silently re-including the prefix.\n\t\t\t//\n\t\t\t// Not reachable through `computeFlowLines` but possible with\n\t\t\t// externally-constructed cursors.\n\t\t}\n\t}\n\n\tfor (; i < segments.length; ) {\n\t\tconst seg = segments[i]!;\n\n\t\tif (seg.kind === \"hard-break\") {\n\t\t\tif (hasContent) {\n\t\t\t\tconst endsAtSoftHyphen = lineEndSeg > 0 && segments[lineEndSeg - 1]?.kind === \"soft-hyphen\";\n\t\t\t\tconst text = buildLineText(\n\t\t\t\t\tsegments,\n\t\t\t\t\tstartSeg,\n\t\t\t\t\tstartG,\n\t\t\t\t\tlineEndSeg,\n\t\t\t\t\tlineEndG,\n\t\t\t\t\tendsAtSoftHyphen,\n\t\t\t\t\tsegAdapter,\n\t\t\t\t);\n\t\t\t\treturn {\n\t\t\t\t\ttext,\n\t\t\t\t\twidth: lineW + (endsAtSoftHyphen ? hyphenWidth : 0),\n\t\t\t\t\tstart: { segmentIndex: startSeg, graphemeIndex: startG },\n\t\t\t\t\tend: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG },\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn {\n\t\t\t\ttext: \"\",\n\t\t\t\twidth: 0,\n\t\t\t\tstart: { segmentIndex: startSeg, graphemeIndex: startG },\n\t\t\t\tend: { segmentIndex: i + 1, graphemeIndex: 0 },\n\t\t\t};\n\t\t}\n\n\t\tconst w = seg.width;\n\n\t\tif (!hasContent) {\n\t\t\tif (w > slotWidth && seg.graphemeWidths) {\n\t\t\t\tconst overflowed = consumeBreakable(i, 0, seg.graphemeWidths);\n\t\t\t\tif (overflowed) {\n\t\t\t\t\tconst text = buildLineText(\n\t\t\t\t\t\tsegments,\n\t\t\t\t\t\tstartSeg,\n\t\t\t\t\t\tstartG,\n\t\t\t\t\t\tlineEndSeg,\n\t\t\t\t\t\tlineEndG,\n\t\t\t\t\t\tfalse,\n\t\t\t\t\t\tsegAdapter,\n\t\t\t\t\t);\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttext,\n\t\t\t\t\t\twidth: lineW,\n\t\t\t\t\t\tstart: { segmentIndex: startSeg, graphemeIndex: startG },\n\t\t\t\t\t\tend: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG },\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\t// No `recordPending` here: segments with `graphemeWidths` are always\n\t\t\t\t// `kind === \"text\"` (see `analyzeAndMeasure`), which is not a break-\n\t\t\t\t// after kind, so the check would always fail.\n\t\t\t\ti = lineEndSeg;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlineW = w;\n\t\t\tlineEndSeg = i + 1;\n\t\t\tlineEndG = 0;\n\t\t\thasContent = true;\n\t\t\tif (canBreakAfter(seg.kind)) {\n\t\t\t\trecordPending(i + 1, 0, seg.kind === \"space\" ? lineW - w : lineW, seg.kind);\n\t\t\t}\n\t\t\ti += 1;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst newW = lineW + w;\n\n\t\tif (newW > slotWidth + 0.005) {\n\t\t\tif (canBreakAfter(seg.kind)) {\n\t\t\t\t// `lineW` is carried from before this segment — no mutation needed;\n\t\t\t\t// trailing space/soft-hyphen width is excluded from the line width.\n\t\t\t\tlineEndSeg = i + 1;\n\t\t\t\tlineEndG = 0;\n\t\t\t\tconst endsAtSoftHyphen = seg.kind === \"soft-hyphen\";\n\t\t\t\tconst finalWidth =\n\t\t\t\t\tseg.kind === \"space\" ? lineW : lineW + (endsAtSoftHyphen ? hyphenWidth : 0);\n\t\t\t\tconst text = buildLineText(\n\t\t\t\t\tsegments,\n\t\t\t\t\tstartSeg,\n\t\t\t\t\tstartG,\n\t\t\t\t\tlineEndSeg,\n\t\t\t\t\tlineEndG,\n\t\t\t\t\tendsAtSoftHyphen,\n\t\t\t\t\tsegAdapter,\n\t\t\t\t);\n\t\t\t\treturn {\n\t\t\t\t\ttext,\n\t\t\t\t\twidth: finalWidth,\n\t\t\t\t\tstart: { segmentIndex: startSeg, graphemeIndex: startG },\n\t\t\t\t\tend: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (pendingBreakSeg >= 0) {\n\t\t\t\tconst text = buildLineText(\n\t\t\t\t\tsegments,\n\t\t\t\t\tstartSeg,\n\t\t\t\t\tstartG,\n\t\t\t\t\tpendingBreakSeg,\n\t\t\t\t\tpendingBreakG,\n\t\t\t\t\tpendingBreakSoftHyphen,\n\t\t\t\t\tsegAdapter,\n\t\t\t\t);\n\t\t\t\treturn {\n\t\t\t\t\ttext,\n\t\t\t\t\twidth: pendingBreakWidth + (pendingBreakSoftHyphen ? hyphenWidth : 0),\n\t\t\t\t\tstart: { segmentIndex: startSeg, graphemeIndex: startG },\n\t\t\t\t\tend: { segmentIndex: pendingBreakSeg, graphemeIndex: pendingBreakG },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (w > slotWidth && seg.graphemeWidths) {\n\t\t\t\tconst text = buildLineText(\n\t\t\t\t\tsegments,\n\t\t\t\t\tstartSeg,\n\t\t\t\t\tstartG,\n\t\t\t\t\tlineEndSeg,\n\t\t\t\t\tlineEndG,\n\t\t\t\t\tfalse,\n\t\t\t\t\tsegAdapter,\n\t\t\t\t);\n\t\t\t\treturn {\n\t\t\t\t\ttext,\n\t\t\t\t\twidth: lineW,\n\t\t\t\t\tstart: { segmentIndex: startSeg, graphemeIndex: startG },\n\t\t\t\t\tend: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst text = buildLineText(\n\t\t\t\tsegments,\n\t\t\t\tstartSeg,\n\t\t\t\tstartG,\n\t\t\t\tlineEndSeg,\n\t\t\t\tlineEndG,\n\t\t\t\tfalse,\n\t\t\t\tsegAdapter,\n\t\t\t);\n\t\t\treturn {\n\t\t\t\ttext,\n\t\t\t\twidth: lineW,\n\t\t\t\tstart: { segmentIndex: startSeg, graphemeIndex: startG },\n\t\t\t\tend: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG },\n\t\t\t};\n\t\t}\n\n\t\tlineW = newW;\n\t\tlineEndSeg = i + 1;\n\t\tlineEndG = 0;\n\t\tif (canBreakAfter(seg.kind)) {\n\t\t\trecordPending(i + 1, 0, seg.kind === \"space\" ? lineW - w : lineW, seg.kind);\n\t\t}\n\t\ti += 1;\n\t}\n\n\tif (!hasContent) return null;\n\n\tconst endsAtSoftHyphen = lineEndSeg > 0 && segments[lineEndSeg - 1]?.kind === \"soft-hyphen\";\n\tconst text = buildLineText(\n\t\tsegments,\n\t\tstartSeg,\n\t\tstartG,\n\t\tlineEndSeg,\n\t\tlineEndG,\n\t\tendsAtSoftHyphen,\n\t\tsegAdapter,\n\t);\n\treturn {\n\t\ttext,\n\t\twidth: lineW + (endsAtSoftHyphen ? hyphenWidth : 0),\n\t\tstart: { segmentIndex: startSeg, graphemeIndex: startG },\n\t\tend: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG },\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Slot carving (flow-layout helper)\n// ---------------------------------------------------------------------------\n\n/**\n * Subtract blocked horizontal intervals from a base interval, producing\n * remaining ordered, non-overlapping slots wide enough to fit text.\n *\n * Pure geometry — no text dependency. Used by flow-layout to turn obstacle\n * intersections into per-line layout slots.\n *\n * ```ts\n * carveTextLineSlots({left: 0, right: 600}, [{left: 200, right: 280}])\n * // → [{left: 0, right: 200}, {left: 280, right: 600}]\n * ```\n */\nexport function carveTextLineSlots(\n\tbase: Interval,\n\tblocked: Interval[],\n\tminSlotWidth = 0,\n): Interval[] {\n\tlet slots: Interval[] = [base];\n\tfor (let bi = 0; bi < blocked.length; bi++) {\n\t\tconst block = blocked[bi]!;\n\t\tconst next: Interval[] = [];\n\t\tfor (let si = 0; si < slots.length; si++) {\n\t\t\tconst slot = slots[si]!;\n\t\t\tif (block.right <= slot.left || block.left >= slot.right) {\n\t\t\t\tnext.push(slot);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (block.left > slot.left) next.push({ left: slot.left, right: block.left });\n\t\t\tif (block.right < slot.right) next.push({ left: block.right, right: slot.right });\n\t\t}\n\t\tslots = next;\n\t}\n\tif (minSlotWidth > 0) {\n\t\treturn slots.filter((s) => s.right - s.left >= minSlotWidth);\n\t}\n\treturn slots;\n}\n\n// ---------------------------------------------------------------------------\n// Character positions\n// ---------------------------------------------------------------------------\n\n/** Compute per-character x,y positions from line breaks and segments. */\nexport function computeCharPositions(\n\tlineBreaks: LineBreaksResult,\n\tsegments: PreparedSegment[],\n\tlineHeight: number,\n\tsegmentAdapter?: SegmentAdapter,\n): CharPosition[] {\n\tconst positions: CharPosition[] = [];\n\tconst segAdapter = segmentAdapter ?? getDefaultSegmentAdapter();\n\n\tfor (let lineIdx = 0; lineIdx < lineBreaks.lines.length; lineIdx++) {\n\t\tconst line = lineBreaks.lines[lineIdx]!;\n\t\tconst y = lineIdx * lineHeight;\n\t\tlet x = 0;\n\n\t\tfor (let si = line.startSegment; si < segments.length; si++) {\n\t\t\tconst seg = segments[si]!;\n\t\t\tif (seg.kind === \"soft-hyphen\" || seg.kind === \"hard-break\") {\n\t\t\t\t// Skip non-visual segments but stop if past the line\n\t\t\t\tif (si >= line.endSegment && line.endGrapheme === 0) break;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst graphemes = [...segAdapter.segmentGraphemes(seg.text)].map((g) => g.segment);\n\t\t\tif (graphemes.length === 0) continue;\n\t\t\tconst startG = si === line.startSegment ? line.startGrapheme : 0;\n\n\t\t\t// Determine how many graphemes of this segment belong to this line\n\t\t\tlet endG: number;\n\t\t\tif (si < line.endSegment) {\n\t\t\t\t// Full segment is on this line\n\t\t\t\tendG = graphemes.length;\n\t\t\t} else if (si === line.endSegment && line.endGrapheme > 0) {\n\t\t\t\t// Partial segment (grapheme-level break)\n\t\t\t\tendG = line.endGrapheme;\n\t\t\t} else {\n\t\t\t\t// Past the line's content\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tfor (let g = startG; g < endG; g++) {\n\t\t\t\tconst gWidth = seg.graphemeWidths ? seg.graphemeWidths[g]! : seg.width / graphemes.length;\n\t\t\t\tpositions.push({ x, y, width: gWidth, height: lineHeight, line: lineIdx });\n\t\t\t\tx += gWidth;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn positions;\n}\n\n// ---------------------------------------------------------------------------\n// Reactive graph factory\n// ---------------------------------------------------------------------------\n\nexport type ReactiveLayoutOptions = {\n\t/** Measurement backend (required). */\n\tadapter: MeasurementAdapter;\n\t/**\n\t * Segmentation backend (optional). Defaults to a lazy {@link IntlSegmentAdapter}\n\t * (uses platform `Intl.Segmenter`). **Required on Hermes / RN** where\n\t * `Intl.Segmenter` is undefined — wire a polyfilled {@link SegmentAdapter}\n\t * here. See {@link SegmentAdapter} JSDoc for the polyfill recipe.\n\t */\n\tsegmentAdapter?: SegmentAdapter;\n\t/** Graph name (default: \"reactive-layout\"). */\n\tname?: string;\n\t/** Initial text. */\n\ttext?: string;\n\t/** Initial CSS font string. */\n\tfont?: string;\n\t/** Initial line height in px. */\n\tlineHeight?: number;\n\t/** Initial max width in px (clamped to ≥ 0). */\n\tmaxWidth?: number;\n};\n\n/**\n * Create a reactive text layout graph.\n *\n * ```\n * Graph(\"reactive-layout\")\n * ├── node([], { initial: \"text\" })\n * ├── node([], { initial: \"font\" })\n * ├── node([], { initial: \"line-height\" })\n * ├── node([], { initial: \"max-width\" })\n * ├── derived(\"segments\") — text + font → PreparedSegment[]\n * ├── derived(\"line-breaks\") — segments + max-width → LineBreaksResult\n * ├── derived(\"height\") — line-breaks → number\n * └── derived(\"char-positions\") — line-breaks + segments → CharPosition[]\n * ```\n */\nexport function reactiveLayout(opts: ReactiveLayoutOptions): ReactiveLayoutBundle {\n\tconst { adapter, segmentAdapter: segmentAdapterOpt, name = \"reactive-layout\" } = opts;\n\t// Resolve eagerly so a Hermes consumer without an explicit `segmentAdapter`\n\t// sees the clear `IntlSegmentAdapter` \"supply a SegmentAdapter\" error at\n\t// factory construction time (not later, on first text wave). When the\n\t// caller wires their own, no `Intl.Segmenter` access happens here.\n\tconst segmentAdapter: SegmentAdapter = segmentAdapterOpt ?? getDefaultSegmentAdapter();\n\tconst g = new Graph(name);\n\n\t// Shared measurement cache: Map<font, Map<segment, width>>\n\tconst measureCache = new Map<string, Map<string, number>>();\n\n\t// --- State nodes ---\n\tconst textNode = node<string>([], { name: \"text\", initial: opts.text ?? \"\" });\n\tconst fontNode = node<string>([], {\n\t\tname: \"font\",\n\t\tinitial: opts.font ?? \"16px sans-serif\",\n\t});\n\tconst lineHeightNode = node<number>([], {\n\t\tname: \"line-height\",\n\t\tinitial: opts.lineHeight ?? 20,\n\t});\n\tconst maxWidthNode = node<number>([], {\n\t\tname: \"max-width\",\n\t\tinitial: Math.max(0, opts.maxWidth ?? 800),\n\t});\n\n\t// --- Derived: segments (text + font → PreparedSegment[]) ---\n\tfunction graphemeWidthsEqual(a: number[] | null, b: number[] | null): boolean {\n\t\tif (a === null || b === null) return a === b;\n\t\tif (a.length !== b.length) return false;\n\t\tfor (let i = 0; i < a.length; i++) {\n\t\t\tif (a[i] !== b[i]!) return false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t// Raw `node(...)` instead of `derived(...)` so the fn can return a\n\t// cleanup function. Core fires function-form cleanup on INVALIDATE (see\n\t// `node.ts:_updateState` INVALIDATE branch), which replaces the old v4\n\t// per-node `onMessage` hook that watched for INVALIDATE/TEARDOWN to flush\n\t// measurement caches.\n\tconst segmentsNode: Node<PreparedSegment[]> = node<PreparedSegment[]>(\n\t\t[textNode, fontNode],\n\t\t(data, actions, ctx) => {\n\t\t\tconst b0 = data[0];\n\t\t\tconst textVal = (b0 != null && b0.length > 0 ? b0.at(-1) : ctx.prevData[0]) as string;\n\t\t\tconst b1 = data[1];\n\t\t\tconst fontVal = (b1 != null && b1.length > 0 ? b1.at(-1) : ctx.prevData[1]) as string;\n\t\t\tconst t0 = monotonicNs();\n\t\t\tconst measureStats: SegmentMeasureStats = { hits: 0, misses: 0 };\n\t\t\tconst result = analyzeAndMeasure(\n\t\t\t\ttextVal as string,\n\t\t\t\tfontVal as string,\n\t\t\t\tadapter,\n\t\t\t\tmeasureCache,\n\t\t\t\tmeasureStats,\n\t\t\t\tsegmentAdapter,\n\t\t\t);\n\t\t\tconst elapsed = monotonicNs() - t0;\n\n\t\t\tconst lookups = measureStats.hits + measureStats.misses;\n\t\t\tconst hitRate = lookups === 0 ? 1 : measureStats.hits / lookups;\n\n\t\t\t// After parent `segments` emits, deliver metrics via phase-3 deferral\n\t\t\t// so observers see the parent value first.\n\t\t\t// Forwards via `emitToMeta` (see `patterns/_internal.ts`).\n\t\t\tconst meta = segmentsNode.meta;\n\t\t\tif (meta) {\n\t\t\t\temitToMeta(meta[\"cache-hit-rate\"], hitRate);\n\t\t\t\temitToMeta(meta[\"segment-count\"], result.length);\n\t\t\t\temitToMeta(meta[\"layout-time-ns\"], elapsed);\n\t\t\t}\n\n\t\t\tactions.emit(result);\n\n\t\t\t// Object-form cleanup: fires on deactivation AND on INVALIDATE, but\n\t\t\t// NOT before re-runs. Re-runs that edit text / font retain the\n\t\t\t// measurement cache — the per-segment entries still valid for the\n\t\t\t// new text overlap the previous set and are reused. Before-run\n\t\t\t// flushing (previous behaviour) wiped 100-segment caches on a\n\t\t\t// one-character edit; this shape keeps cache hit-rate proportional\n\t\t\t// to content overlap rather than invalidation frequency.\n\t\t\tconst flush = (): void => {\n\t\t\t\tmeasureCache.clear();\n\t\t\t\tadapter.clearCache?.();\n\t\t\t};\n\t\t\treturn { onDeactivation: flush, onInvalidate: flush };\n\t\t},\n\t\t{\n\t\t\tname: \"segments\",\n\t\t\tdescribeKind: \"derived\",\n\t\t\tmeta: {\n\t\t\t\t\"cache-hit-rate\": 0,\n\t\t\t\t\"segment-count\": 0,\n\t\t\t\t\"layout-time-ns\": 0,\n\t\t\t},\n\t\t\tequals: (a, b) => {\n\t\t\t\tconst sa = a as PreparedSegment[] | null;\n\t\t\t\tconst sb = b as PreparedSegment[] | null;\n\t\t\t\tif (sa == null || sb == null) return sa === sb;\n\t\t\t\tif (sa.length !== sb.length) return false;\n\t\t\t\tfor (let i = 0; i < sa.length; i++) {\n\t\t\t\t\tconst pa = sa[i]!;\n\t\t\t\t\tconst pb = sb[i]!;\n\t\t\t\t\tif (\n\t\t\t\t\t\tpa.text !== pb.text ||\n\t\t\t\t\t\tpa.width !== pb.width ||\n\t\t\t\t\t\tpa.kind !== pb.kind ||\n\t\t\t\t\t\t!graphemeWidthsEqual(pa.graphemeWidths ?? null, pb.graphemeWidths ?? null)\n\t\t\t\t\t)\n\t\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t},\n\t\t},\n\t);\n\n\t// --- Derived: line-breaks (segments + max-width + font → LineBreaksResult) ---\n\tconst lineBreaksNode = node<LineBreaksResult>(\n\t\t[segmentsNode, maxWidthNode, fontNode],\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\tactions.emit(\n\t\t\t\tcomputeLineBreaks(\n\t\t\t\t\tdata[0] as PreparedSegment[],\n\t\t\t\t\tdata[1] as number,\n\t\t\t\t\tadapter,\n\t\t\t\t\tdata[2] as string,\n\t\t\t\t\tmeasureCache,\n\t\t\t\t\tsegmentAdapter,\n\t\t\t\t),\n\t\t\t);\n\t\t},\n\t\t{\n\t\t\tname: \"line-breaks\",\n\t\t\tdescribeKind: \"derived\",\n\t\t\tequals: (a, b) => {\n\t\t\t\tconst la = a as LineBreaksResult | null;\n\t\t\t\tconst lb = b as LineBreaksResult | null;\n\t\t\t\tif (la == null || lb == null) return la === lb;\n\t\t\t\tif (la.lineCount !== lb.lineCount) return false;\n\t\t\t\tfor (let i = 0; i < la.lines.length; i++) {\n\t\t\t\t\tconst lineA = la.lines[i]!;\n\t\t\t\t\tconst lineB = lb.lines[i]!;\n\t\t\t\t\tif (\n\t\t\t\t\t\tlineA.text !== lineB.text ||\n\t\t\t\t\t\tlineA.width !== lineB.width ||\n\t\t\t\t\t\tlineA.startSegment !== lineB.startSegment ||\n\t\t\t\t\t\tlineA.startGrapheme !== lineB.startGrapheme ||\n\t\t\t\t\t\tlineA.endSegment !== lineB.endSegment ||\n\t\t\t\t\t\tlineA.endGrapheme !== lineB.endGrapheme\n\t\t\t\t\t)\n\t\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t},\n\t\t},\n\t);\n\n\t// --- Derived: height ---\n\tconst heightNode = node<number>(\n\t\t[lineBreaksNode, lineHeightNode],\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\tactions.emit((data[0] as LineBreaksResult).lineCount * (data[1] as number));\n\t\t},\n\t\t{ describeKind: \"derived\", name: \"height\" },\n\t);\n\n\t// --- Derived: char-positions ---\n\tconst charPositionsNode = node<CharPosition[]>(\n\t\t[lineBreaksNode, segmentsNode, lineHeightNode],\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\tactions.emit(\n\t\t\t\tcomputeCharPositions(\n\t\t\t\t\tdata[0] as LineBreaksResult,\n\t\t\t\t\tdata[1] as PreparedSegment[],\n\t\t\t\t\tdata[2] as number,\n\t\t\t\t\tsegmentAdapter,\n\t\t\t\t),\n\t\t\t);\n\t\t},\n\t\t{\n\t\t\tname: \"char-positions\",\n\t\t\tdescribeKind: \"derived\",\n\t\t\tequals: (a, b) => {\n\t\t\t\tconst ca = a as CharPosition[] | null;\n\t\t\t\tconst cb = b as CharPosition[] | null;\n\t\t\t\tif (ca == null || cb == null) return ca === cb;\n\t\t\t\tif (ca.length !== cb.length) return false;\n\t\t\t\tfor (let i = 0; i < ca.length; i++) {\n\t\t\t\t\tif (ca[i]!.x !== cb[i]!.x || ca[i]!.y !== cb[i]!.y || ca[i]!.width !== cb[i]!.width)\n\t\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t},\n\t\t},\n\t);\n\n\t// --- Register in graph ---\n\tg.add(textNode, { name: \"text\" });\n\tg.add(fontNode, { name: \"font\" });\n\tg.add(lineHeightNode, { name: \"line-height\" });\n\tg.add(maxWidthNode, { name: \"max-width\" });\n\tg.add(segmentsNode, { name: \"segments\" });\n\tg.add(lineBreaksNode, { name: \"line-breaks\" });\n\tg.add(heightNode, { name: \"height\" });\n\tg.add(charPositionsNode, { name: \"char-positions\" });\n\n\t// --- Edges (for describe() visibility) ---\n\n\treturn {\n\t\tgraph: g,\n\t\tsetText: (text: string) => g.set(\"text\", text),\n\t\tsetFont: (font: string) => g.set(\"font\", font),\n\t\tsetLineHeight: (lh: number) => g.set(\"line-height\", lh),\n\t\tsetMaxWidth: (mw: number) => g.set(\"max-width\", Math.max(0, mw)),\n\t\tsegments: segmentsNode,\n\t\tlineBreaks: lineBreaksNode,\n\t\theight: heightNode,\n\t\tcharPositions: charPositionsNode,\n\t};\n}\n"],"mappings":"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","names":["w","canBreakAfter","text","endsAtSoftHyphen"]}
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  graphLens
3
- } from "./chunk-4XCHZRUJ.js";
3
+ } from "./chunk-D5YGR4TP.js";
4
4
  import {
5
5
  auditTrail,
6
6
  complianceSnapshot
7
- } from "./chunk-CEVNQ74M.js";
7
+ } from "./chunk-3YGXPUHW.js";
8
8
 
9
9
  // src/presets/inspect/composite.ts
10
10
  import { placeholderArgs } from "@graphrefly/pure-ts/core";
@@ -89,4 +89,4 @@ export {
89
89
  InspectGraph,
90
90
  inspect
91
91
  };
92
- //# sourceMappingURL=chunk-LDCSZ72P.js.map
92
+ //# sourceMappingURL=chunk-6YBER5UP.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/presets/inspect/composite.ts"],"sourcesContent":["/**\n * `inspect()` preset — Tier 9.1 γ-form γ-II + Q5-6 medium scope.\n *\n * Composes graph observability into a single mounted facade:\n * - `lens` (mounted as a `LensSubgraph` child) — exposes `topology` /\n * `health` / `flow` Nodes via `inspect.lens.*`.\n * - `audit` (mounted `AuditTrailGraph`) — every mutation on the wrapped\n * target captured as an audit entry.\n * - `explainTarget(from, to, opts?)` — facade over\n * `target.describe({ explain: {...} })`; supports both static and\n * reactive forms.\n * - `complianceSnapshot()` — one-shot tamper-evident snapshot pairing the\n * target's persisted state with the audit log.\n *\n * **Path-namespace boundary.** `inspect.describe()` shows InspectGraph's\n * OWN topology (the lens + audit subgraphs and any caller-added siblings) —\n * NOT the wrapped target's topology. Use `inspect.target.describe()` to\n * walk the target. `inspect.node(\"counter\")` resolves under the inspect\n * graph, NOT the target.\n *\n * **Why a Graph subclass.** Per Tier 9.1 γ-II lock: closure-bundled returns\n * (`{lens, audit, explain, ...}` of independently-constructed primitives)\n * hide topology — `describe()` from the wrapper can't walk into the\n * audit/lens pieces. Mounting them under a real `InspectGraph` keeps every\n * contained primitive visible in describe / explain across the boundary,\n * which is the point of the inspect preset.\n *\n * **Why the lens lives in a child `LensSubgraph` mount.** `Graph.destroy()`\n * signals TEARDOWN through `this._nodes` after disposers drain. If the\n * lens nodes were `add()`ed directly to InspectGraph's path table, they\n * would receive TEARDOWN at parent destroy — invalidating any externally\n * held `view.lens.topology.subscribe(...)` reference. Mounting via a child\n * subgraph contains the cascade: inspect's TEARDOWN visits the lens\n * subgraph's nodes through `mount → child._destroyClearOnly`, which clears\n * structure but does NOT broadcast TEARDOWN. The `lens.dispose()` disposer\n * still tears down the underlying observe handle as designed (D1 fix per\n * /qa lock).\n *\n * **Why `inspect()` mounts `graphLens` rather than rebuilding `health` / `flow`.**\n * Per Q3 yellow lock: rebuilding would duplicate the `topology → health`\n * and `dataFlow → flow` deriveds, doubling subscription cost and risking\n * semantic drift. Mounting graphLens once lets `inspect.lens` and the\n * standalone `graphLens(target)` factory share a single source of truth.\n *\n * @module\n */\n\nimport type { Actor, Node } from \"@graphrefly/pure-ts/core\";\nimport { placeholderArgs } from \"@graphrefly/pure-ts/core\";\nimport { type CausalChain, Graph } from \"@graphrefly/pure-ts/graph\";\nimport {\n\ttype AuditTrailGraph,\n\ttype AuditTrailOptions,\n\tauditTrail,\n\ttype ComplianceSnapshotResult,\n\tcomplianceSnapshot,\n\ttype PolicyGateGraph,\n} from \"../../utils/inspect/audit.js\";\nimport { type GraphLensView, graphLens } from \"../../utils/inspect/lens.js\";\n\n/** Options for {@link inspect}. */\nexport interface InspectOptions {\n\t/** Default actor recorded on `complianceSnapshot()` calls. */\n\tactor?: Actor;\n\t/**\n\t * Forwarded to the mounted {@link auditTrail} so callers can configure\n\t * retention / inclusion policy. Pre-allocated to keep `inspect()`\n\t * callable with no opts.\n\t */\n\taudit?: AuditTrailOptions;\n\t/** Optional name override for the `InspectGraph` itself. */\n\tname?: string;\n}\n\n/**\n * Thin Graph-subclass shell that owns the `graphLens(target)` Nodes so\n * inspect's TEARDOWN cascade reaches them via `_destroyClearOnly` (which\n * clears structure WITHOUT broadcasting TEARDOWN) instead of via\n * `signal([[TEARDOWN]])` (which DOES broadcast and would invalidate any\n * externally held lens-node subscription).\n *\n * @internal\n */\nclass LensSubgraph extends Graph {\n\treadonly view: GraphLensView;\n\n\tconstructor(target: Graph) {\n\t\tsuper(\"lens\");\n\t\tthis.view = graphLens(target);\n\t\tthis.add(this.view.topology, { name: \"topology\" });\n\t\tthis.add(this.view.health, { name: \"health\" });\n\t\tthis.add(this.view.flow, { name: \"flow\" });\n\t}\n}\n\n/**\n * Graph subclass returned by {@link inspect}. Mounts a `graphLens` view (as\n * a child `LensSubgraph`), an `auditTrail` (as a child subgraph), and\n * exposes `explainTarget()` + `complianceSnapshot()` facades over the\n * wrapped target.\n *\n * Mounted children (visible in `describe()`):\n * - `lens::topology` / `lens::health` / `lens::flow` — `graphLens(target)` Nodes.\n * - `audit::*` — the mounted {@link AuditTrailGraph}.\n *\n * @category observability\n */\nexport class InspectGraph extends Graph {\n\treadonly target: Graph;\n\t/**\n\t * Underlying lens view — reach individual Nodes via\n\t * `inspect.lens.topology` / `inspect.lens.health` / `inspect.lens.flow`.\n\t *\n\t * Direct `inspect.topology` / `inspect.health` / `inspect.flow`\n\t * accessors are NOT shipped because `Graph.topology` is already an\n\t * accessor on the base class with a different shape (`Node<TopologyEvent>`\n\t * — the mount/unmount stream of THIS graph, not the wrapped target's\n\t * describe snapshot). Going through `.lens.*` keeps the two concepts\n\t * cleanly separated.\n\t */\n\treadonly lens: GraphLensView;\n\t/** Mounted audit trail subgraph. */\n\treadonly audit: AuditTrailGraph;\n\n\tprivate readonly _defaultActor?: Actor;\n\tprivate readonly _lensSubgraph: LensSubgraph;\n\n\tconstructor(target: Graph, opts: InspectOptions = {}) {\n\t\tsuper(opts.name ?? `inspect(${target.name})`);\n\t\tthis.target = target;\n\t\tthis._defaultActor = opts.actor;\n\n\t\t// D1 (qa lock): lens lives inside a child mount so `inspect.destroy()`'s\n\t\t// TEARDOWN signal cascade reaches the lens nodes via\n\t\t// `_destroyClearOnly` (no broadcast) rather than via `_signalDeliver`\n\t\t// over `inspect._nodes` (which WOULD broadcast). External holders of\n\t\t// `view.lens.topology.subscribe(...)` see only the `lens.dispose()`\n\t\t// teardown of the underlying observe handle, not a stray TEARDOWN\n\t\t// from inspect's path table.\n\t\tthis._lensSubgraph = new LensSubgraph(target);\n\t\tthis.lens = this._lensSubgraph.view;\n\t\tthis.mount(\"lens\", this._lensSubgraph);\n\n\t\tthis.audit = auditTrail(target, opts.audit ?? {});\n\t\tthis.mount(\"audit\", this.audit);\n\n\t\t// Tear down the lens's underlying observe subscription on destroy.\n\t\t// The mounted subgraphs themselves tear down via mount lifecycle.\n\t\tthis.addDisposer(() => this.lens.dispose());\n\t}\n\n\t/**\n\t * Causal-chain facade over `target.describe({ explain: {...} })`. Supports\n\t * both static (one-shot {@link CausalChain}) and reactive\n\t * (`{ reactive: true }`) forms.\n\t *\n\t * Named `explainTarget` (not folded into a `describe` mode on this class)\n\t * because `inspect.describe(...)` walks `InspectGraph`'s OWN topology\n\t * (lens + audit subgraphs) rather than the wrapped target's. Use\n\t * `inspect.explainTarget(...)` for chains across the wrapped graph;\n\t * `inspect.describe({ explain: {...} })` for chains across the lens /\n\t * audit composition.\n\t */\n\texplainTarget(\n\t\tfrom: string | Node<string>,\n\t\tto: string | Node<string>,\n\t\topts?: {\n\t\t\tmaxDepth?: number | Node<number>;\n\t\t\tfindCycle?: boolean | Node<boolean>;\n\t\t},\n\t): CausalChain;\n\texplainTarget(\n\t\tfrom: string | Node<string>,\n\t\tto: string | Node<string>,\n\t\topts: {\n\t\t\treactive: true;\n\t\t\tmaxDepth?: number | Node<number>;\n\t\t\tfindCycle?: boolean | Node<boolean>;\n\t\t\tname?: string;\n\t\t},\n\t): { node: Node<CausalChain>; dispose: () => void };\n\texplainTarget(\n\t\tfrom: string | Node<string>,\n\t\tto: string | Node<string>,\n\t\topts?: {\n\t\t\treactive?: boolean;\n\t\t\tmaxDepth?: number | Node<number>;\n\t\t\tfindCycle?: boolean | Node<boolean>;\n\t\t\tname?: string;\n\t\t},\n\t): CausalChain | { node: Node<CausalChain>; dispose: () => void } {\n\t\t// Cast through the discriminated overload — TypeScript can't pick a\n\t\t// signature on `target.describe({explain})` because `opts` has the\n\t\t// union shape (reactive: boolean | undefined).\n\t\tconst explainArg: {\n\t\t\tfrom: string | Node<string>;\n\t\t\tto: string | Node<string>;\n\t\t\tmaxDepth?: number | Node<number>;\n\t\t\tfindCycle?: boolean | Node<boolean>;\n\t\t} = { from, to };\n\t\tif (opts?.maxDepth !== undefined) explainArg.maxDepth = opts.maxDepth;\n\t\tif (opts?.findCycle !== undefined) explainArg.findCycle = opts.findCycle;\n\t\tconst describeOpts: Record<string, unknown> = { explain: explainArg };\n\t\tif (opts?.reactive === true) describeOpts.reactive = true;\n\t\tif (opts?.name !== undefined) describeOpts.name = opts.name;\n\t\treturn (\n\t\t\tthis.target.describe as unknown as (\n\t\t\t\to: typeof describeOpts,\n\t\t\t) => CausalChain | { node: Node<CausalChain>; dispose: () => void }\n\t\t)(describeOpts);\n\t}\n\n\t/**\n\t * One-shot tamper-evident snapshot pairing the target's persisted state\n\t * with the audit log + (optional) policy-gate violations.\n\t *\n\t * Uses the inspect's mounted `audit` by default; pair with a separate\n\t * `policyGate` (mounted elsewhere) by passing `policies` explicitly.\n\t *\n\t * **Cryptographic strength caveat (echoed from {@link complianceSnapshot}):**\n\t * the returned `fingerprint` is a truncated SHA-256 (16 hex chars / ~64\n\t * bits) optimized for compact archival. Sufficient for casual integrity\n\t * checks and content-addressed dedup; for adversarial tamper-evidence,\n\t * pair with a full SHA-256 over the canonical JSON externally.\n\t */\n\tcomplianceSnapshot(opts?: {\n\t\tactor?: Actor;\n\t\tpolicies?: PolicyGateGraph;\n\t}): ComplianceSnapshotResult {\n\t\tconst actor = opts?.actor ?? this._defaultActor;\n\t\treturn complianceSnapshot(this.target, {\n\t\t\taudit: this.audit,\n\t\t\t...(actor != null ? { actor } : {}),\n\t\t\t...(opts?.policies != null ? { policies: opts.policies } : {}),\n\t\t});\n\t}\n}\n\n/**\n * Build an {@link InspectGraph} that mounts `graphLens` + `auditTrail` over\n * the wrapped target and exposes `explainTarget()` + `complianceSnapshot()`\n * facades.\n *\n * @example\n * ```ts\n * import { inspect } from \"@graphrefly/graphrefly/patterns/inspect\";\n *\n * const target = buildMyApp();\n * const view = inspect(target, { actor: { id: \"ops-bot\", role: \"monitor\" } });\n *\n * // Live observability\n * view.lens.health.subscribe((msgs) => console.log(\"health:\", msgs));\n * view.lens.flow.subscribe((msgs) => console.log(\"flow:\", msgs));\n *\n * // Causal explainability across the wrapped target\n * const chain = view.explainTarget(\"input\", \"output\");\n *\n * // Tamper-evident snapshot for archival\n * const snapshot = view.complianceSnapshot();\n * ```\n *\n * @category observability\n */\nexport function inspect(target: Graph, opts: InspectOptions = {}): InspectGraph {\n\tconst g = new InspectGraph(target, opts);\n\t// A1 (qa lock): self-tag so `inspect.describe().factory === \"inspect\"`,\n\t// matching the policyGate / pipelineGraph / harnessLoop precedent.\n\tg.tagFactory(\"inspect\", placeholderArgs(opts as unknown as Record<string, unknown>));\n\treturn g;\n}\n"],"mappings":";;;;;;;;;AAgDA,SAAS,uBAAuB;AAChC,SAA2B,aAAa;AAkCxC,IAAM,eAAN,cAA2B,MAAM;AAAA,EACvB;AAAA,EAET,YAAY,QAAe;AAC1B,UAAM,MAAM;AACZ,SAAK,OAAO,UAAU,MAAM;AAC5B,SAAK,IAAI,KAAK,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AACjD,SAAK,IAAI,KAAK,KAAK,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC7C,SAAK,IAAI,KAAK,KAAK,MAAM,EAAE,MAAM,OAAO,CAAC;AAAA,EAC1C;AACD;AAcO,IAAM,eAAN,cAA2B,MAAM;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA;AAAA;AAAA,EAEA;AAAA,EAEQ;AAAA,EACA;AAAA,EAEjB,YAAY,QAAe,OAAuB,CAAC,GAAG;AACrD,UAAM,KAAK,QAAQ,WAAW,OAAO,IAAI,GAAG;AAC5C,SAAK,SAAS;AACd,SAAK,gBAAgB,KAAK;AAS1B,SAAK,gBAAgB,IAAI,aAAa,MAAM;AAC5C,SAAK,OAAO,KAAK,cAAc;AAC/B,SAAK,MAAM,QAAQ,KAAK,aAAa;AAErC,SAAK,QAAQ,WAAW,QAAQ,KAAK,SAAS,CAAC,CAAC;AAChD,SAAK,MAAM,SAAS,KAAK,KAAK;AAI9B,SAAK,YAAY,MAAM,KAAK,KAAK,QAAQ,CAAC;AAAA,EAC3C;AAAA,EAgCA,cACC,MACA,IACA,MAMiE;AAIjE,UAAM,aAKF,EAAE,MAAM,GAAG;AACf,QAAI,MAAM,aAAa,OAAW,YAAW,WAAW,KAAK;AAC7D,QAAI,MAAM,cAAc,OAAW,YAAW,YAAY,KAAK;AAC/D,UAAM,eAAwC,EAAE,SAAS,WAAW;AACpE,QAAI,MAAM,aAAa,KAAM,cAAa,WAAW;AACrD,QAAI,MAAM,SAAS,OAAW,cAAa,OAAO,KAAK;AACvD,WACC,KAAK,OAAO,SAGX,YAAY;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,mBAAmB,MAGU;AAC5B,UAAM,QAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,mBAAmB,KAAK,QAAQ;AAAA,MACtC,OAAO,KAAK;AAAA,MACZ,GAAI,SAAS,OAAO,EAAE,MAAM,IAAI,CAAC;AAAA,MACjC,GAAI,MAAM,YAAY,OAAO,EAAE,UAAU,KAAK,SAAS,IAAI,CAAC;AAAA,IAC7D,CAAC;AAAA,EACF;AACD;AA2BO,SAAS,QAAQ,QAAe,OAAuB,CAAC,GAAiB;AAC/E,QAAM,IAAI,IAAI,aAAa,QAAQ,IAAI;AAGvC,IAAE,WAAW,WAAW,gBAAgB,IAA0C,CAAC;AACnF,SAAO;AACR;","names":[]}
1
+ {"version":3,"sources":["../src/presets/inspect/composite.ts"],"sourcesContent":["/**\n * `inspect()` preset — Tier 9.1 γ-form γ-II + Q5-6 medium scope.\n *\n * Composes graph observability into a single mounted facade:\n * - `lens` (mounted as a `LensSubgraph` child) — exposes `topology` /\n * `health` / `flow` Nodes via `inspect.lens.*`.\n * - `audit` (mounted `AuditTrailGraph`) — every mutation on the wrapped\n * target captured as an audit entry.\n * - `explainTarget(from, to, opts?)` — facade over\n * `target.describe({ explain: {...} })`; supports both static and\n * reactive forms.\n * - `complianceSnapshot()` — one-shot tamper-evident snapshot pairing the\n * target's persisted state with the audit log.\n *\n * **Path-namespace boundary.** `inspect.describe()` shows InspectGraph's\n * OWN topology (the lens + audit subgraphs and any caller-added siblings) —\n * NOT the wrapped target's topology. Use `inspect.target.describe()` to\n * walk the target. `inspect.node(\"counter\")` resolves under the inspect\n * graph, NOT the target.\n *\n * **Why a Graph subclass.** Per Tier 9.1 γ-II lock: closure-bundled returns\n * (`{lens, audit, explain, ...}` of independently-constructed primitives)\n * hide topology — `describe()` from the wrapper can't walk into the\n * audit/lens pieces. Mounting them under a real `InspectGraph` keeps every\n * contained primitive visible in describe / explain across the boundary,\n * which is the point of the inspect preset.\n *\n * **Why the lens lives in a child `LensSubgraph` mount.** `Graph.destroy()`\n * signals TEARDOWN through `this._nodes` after disposers drain. If the\n * lens nodes were `add()`ed directly to InspectGraph's path table, they\n * would receive TEARDOWN at parent destroy — invalidating any externally\n * held `view.lens.topology.subscribe(...)` reference. Mounting via a child\n * subgraph contains the cascade: inspect's TEARDOWN visits the lens\n * subgraph's nodes through `mount → child._destroyClearOnly`, which clears\n * structure but does NOT broadcast TEARDOWN. The `lens.dispose()` disposer\n * still tears down the underlying observe handle as designed (D1 fix per\n * /qa lock).\n *\n * **Why `inspect()` mounts `graphLens` rather than rebuilding `health` / `flow`.**\n * Per Q3 yellow lock: rebuilding would duplicate the `topology → health`\n * and `dataFlow → flow` deriveds, doubling subscription cost and risking\n * semantic drift. Mounting graphLens once lets `inspect.lens` and the\n * standalone `graphLens(target)` factory share a single source of truth.\n *\n * @module\n */\n\nimport type { Actor, Node } from \"@graphrefly/pure-ts/core\";\nimport { placeholderArgs } from \"@graphrefly/pure-ts/core\";\nimport { type CausalChain, Graph } from \"@graphrefly/pure-ts/graph\";\nimport {\n\ttype AuditTrailGraph,\n\ttype AuditTrailOptions,\n\tauditTrail,\n\ttype ComplianceSnapshotResult,\n\tcomplianceSnapshot,\n\ttype PolicyGateGraph,\n} from \"../../utils/inspect/audit.js\";\nimport { type GraphLensView, graphLens } from \"../../utils/inspect/lens.js\";\n\n/** Options for {@link inspect}. */\nexport interface InspectOptions {\n\t/** Default actor recorded on `complianceSnapshot()` calls. */\n\tactor?: Actor;\n\t/**\n\t * Forwarded to the mounted {@link auditTrail} so callers can configure\n\t * retention / inclusion policy. Pre-allocated to keep `inspect()`\n\t * callable with no opts.\n\t */\n\taudit?: AuditTrailOptions;\n\t/** Optional name override for the `InspectGraph` itself. */\n\tname?: string;\n}\n\n/**\n * Thin Graph-subclass shell that owns the `graphLens(target)` Nodes so\n * inspect's TEARDOWN cascade reaches them via `_destroyClearOnly` (which\n * clears structure WITHOUT broadcasting TEARDOWN) instead of via\n * `signal([[TEARDOWN]])` (which DOES broadcast and would invalidate any\n * externally held lens-node subscription).\n *\n * @internal\n */\nclass LensSubgraph extends Graph {\n\treadonly view: GraphLensView;\n\n\tconstructor(target: Graph) {\n\t\tsuper(\"lens\");\n\t\tthis.view = graphLens(target);\n\t\tthis.add(this.view.topology, { name: \"topology\" });\n\t\tthis.add(this.view.health, { name: \"health\" });\n\t\tthis.add(this.view.flow, { name: \"flow\" });\n\t}\n}\n\n/**\n * Graph subclass returned by {@link inspect}. Mounts a `graphLens` view (as\n * a child `LensSubgraph`), an `auditTrail` (as a child subgraph), and\n * exposes `explainTarget()` + `complianceSnapshot()` facades over the\n * wrapped target.\n *\n * Mounted children (visible in `describe()`):\n * - `lens::topology` / `lens::health` / `lens::flow` — `graphLens(target)` Nodes.\n * - `audit::*` — the mounted {@link AuditTrailGraph}.\n *\n * @category observability\n */\nexport class InspectGraph extends Graph {\n\treadonly target: Graph;\n\t/**\n\t * Underlying lens view — reach individual Nodes via\n\t * `inspect.lens.topology` / `inspect.lens.health` / `inspect.lens.flow`.\n\t *\n\t * Direct `inspect.topology` / `inspect.health` / `inspect.flow`\n\t * accessors are NOT shipped because `Graph.topology` is already an\n\t * accessor on the base class with a different shape (`Node<TopologyEvent>`\n\t * — the mount/unmount stream of THIS graph, not the wrapped target's\n\t * describe snapshot). Going through `.lens.*` keeps the two concepts\n\t * cleanly separated.\n\t */\n\treadonly lens: GraphLensView;\n\t/** Mounted audit trail subgraph. */\n\treadonly audit: AuditTrailGraph;\n\n\tprivate readonly _defaultActor?: Actor;\n\tprivate readonly _lensSubgraph: LensSubgraph;\n\n\tconstructor(target: Graph, opts: InspectOptions = {}) {\n\t\tsuper(opts.name ?? `inspect(${target.name})`);\n\t\tthis.target = target;\n\t\tthis._defaultActor = opts.actor;\n\n\t\t// D1 (qa lock): lens lives inside a child mount so `inspect.destroy()`'s\n\t\t// TEARDOWN signal cascade reaches the lens nodes via\n\t\t// `_destroyClearOnly` (no broadcast) rather than via `_signalDeliver`\n\t\t// over `inspect._nodes` (which WOULD broadcast). External holders of\n\t\t// `view.lens.topology.subscribe(...)` see only the `lens.dispose()`\n\t\t// teardown of the underlying observe handle, not a stray TEARDOWN\n\t\t// from inspect's path table.\n\t\tthis._lensSubgraph = new LensSubgraph(target);\n\t\tthis.lens = this._lensSubgraph.view;\n\t\tthis.mount(\"lens\", this._lensSubgraph);\n\n\t\tthis.audit = auditTrail(target, opts.audit ?? {});\n\t\tthis.mount(\"audit\", this.audit);\n\n\t\t// Tear down the lens's underlying observe subscription on destroy.\n\t\t// The mounted subgraphs themselves tear down via mount lifecycle.\n\t\tthis.addDisposer(() => this.lens.dispose());\n\t}\n\n\t/**\n\t * Causal-chain facade over `target.describe({ explain: {...} })`. Supports\n\t * both static (one-shot {@link CausalChain}) and reactive\n\t * (`{ reactive: true }`) forms.\n\t *\n\t * Named `explainTarget` (not folded into a `describe` mode on this class)\n\t * because `inspect.describe(...)` walks `InspectGraph`'s OWN topology\n\t * (lens + audit subgraphs) rather than the wrapped target's. Use\n\t * `inspect.explainTarget(...)` for chains across the wrapped graph;\n\t * `inspect.describe({ explain: {...} })` for chains across the lens /\n\t * audit composition.\n\t */\n\texplainTarget(\n\t\tfrom: string | Node<string>,\n\t\tto: string | Node<string>,\n\t\topts?: {\n\t\t\tmaxDepth?: number | Node<number>;\n\t\t\tfindCycle?: boolean | Node<boolean>;\n\t\t},\n\t): CausalChain;\n\texplainTarget(\n\t\tfrom: string | Node<string>,\n\t\tto: string | Node<string>,\n\t\topts: {\n\t\t\treactive: true;\n\t\t\tmaxDepth?: number | Node<number>;\n\t\t\tfindCycle?: boolean | Node<boolean>;\n\t\t\tname?: string;\n\t\t},\n\t): { node: Node<CausalChain>; dispose: () => void };\n\texplainTarget(\n\t\tfrom: string | Node<string>,\n\t\tto: string | Node<string>,\n\t\topts?: {\n\t\t\treactive?: boolean;\n\t\t\tmaxDepth?: number | Node<number>;\n\t\t\tfindCycle?: boolean | Node<boolean>;\n\t\t\tname?: string;\n\t\t},\n\t): CausalChain | { node: Node<CausalChain>; dispose: () => void } {\n\t\t// Cast through the discriminated overload — TypeScript can't pick a\n\t\t// signature on `target.describe({explain})` because `opts` has the\n\t\t// union shape (reactive: boolean | undefined).\n\t\tconst explainArg: {\n\t\t\tfrom: string | Node<string>;\n\t\t\tto: string | Node<string>;\n\t\t\tmaxDepth?: number | Node<number>;\n\t\t\tfindCycle?: boolean | Node<boolean>;\n\t\t} = { from, to };\n\t\tif (opts?.maxDepth !== undefined) explainArg.maxDepth = opts.maxDepth;\n\t\tif (opts?.findCycle !== undefined) explainArg.findCycle = opts.findCycle;\n\t\tconst describeOpts: Record<string, unknown> = { explain: explainArg };\n\t\tif (opts?.reactive === true) describeOpts.reactive = true;\n\t\tif (opts?.name !== undefined) describeOpts.name = opts.name;\n\t\treturn (\n\t\t\tthis.target.describe as unknown as (\n\t\t\t\to: typeof describeOpts,\n\t\t\t) => CausalChain | { node: Node<CausalChain>; dispose: () => void }\n\t\t)(describeOpts);\n\t}\n\n\t/**\n\t * One-shot tamper-evident snapshot pairing the target's persisted state\n\t * with the audit log + (optional) policy-gate violations.\n\t *\n\t * Uses the inspect's mounted `audit` by default; pair with a separate\n\t * `policyGate` (mounted elsewhere) by passing `policies` explicitly.\n\t *\n\t * **Cryptographic strength caveat (echoed from {@link complianceSnapshot}):**\n\t * the returned `fingerprint` is a truncated SHA-256 (16 hex chars / ~64\n\t * bits) optimized for compact archival. Sufficient for casual integrity\n\t * checks and content-addressed dedup; for adversarial tamper-evidence,\n\t * pair with a full SHA-256 over the canonical JSON externally.\n\t */\n\tcomplianceSnapshot(opts?: {\n\t\tactor?: Actor;\n\t\tpolicies?: PolicyGateGraph;\n\t}): ComplianceSnapshotResult {\n\t\tconst actor = opts?.actor ?? this._defaultActor;\n\t\treturn complianceSnapshot(this.target, {\n\t\t\taudit: this.audit,\n\t\t\t...(actor != null ? { actor } : {}),\n\t\t\t...(opts?.policies != null ? { policies: opts.policies } : {}),\n\t\t});\n\t}\n}\n\n/**\n * Build an {@link InspectGraph} that mounts `graphLens` + `auditTrail` over\n * the wrapped target and exposes `explainTarget()` + `complianceSnapshot()`\n * facades.\n *\n * @example\n * ```ts\n * import { inspect } from \"@graphrefly/graphrefly/presets/inspect\";\n *\n * const target = buildMyApp();\n * const view = inspect(target, { actor: { id: \"ops-bot\", role: \"monitor\" } });\n *\n * // Live observability\n * view.lens.health.subscribe((msgs) => console.log(\"health:\", msgs));\n * view.lens.flow.subscribe((msgs) => console.log(\"flow:\", msgs));\n *\n * // Causal explainability across the wrapped target\n * const chain = view.explainTarget(\"input\", \"output\");\n *\n * // Tamper-evident snapshot for archival\n * const snapshot = view.complianceSnapshot();\n * ```\n *\n * @category observability\n */\nexport function inspect(target: Graph, opts: InspectOptions = {}): InspectGraph {\n\tconst g = new InspectGraph(target, opts);\n\t// A1 (qa lock): self-tag so `inspect.describe().factory === \"inspect\"`,\n\t// matching the policyGate / pipelineGraph / harnessLoop precedent.\n\tg.tagFactory(\"inspect\", placeholderArgs(opts as unknown as Record<string, unknown>));\n\treturn g;\n}\n"],"mappings":";;;;;;;;;AAgDA,SAAS,uBAAuB;AAChC,SAA2B,aAAa;AAkCxC,IAAM,eAAN,cAA2B,MAAM;AAAA,EACvB;AAAA,EAET,YAAY,QAAe;AAC1B,UAAM,MAAM;AACZ,SAAK,OAAO,UAAU,MAAM;AAC5B,SAAK,IAAI,KAAK,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AACjD,SAAK,IAAI,KAAK,KAAK,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC7C,SAAK,IAAI,KAAK,KAAK,MAAM,EAAE,MAAM,OAAO,CAAC;AAAA,EAC1C;AACD;AAcO,IAAM,eAAN,cAA2B,MAAM;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA;AAAA;AAAA,EAEA;AAAA,EAEQ;AAAA,EACA;AAAA,EAEjB,YAAY,QAAe,OAAuB,CAAC,GAAG;AACrD,UAAM,KAAK,QAAQ,WAAW,OAAO,IAAI,GAAG;AAC5C,SAAK,SAAS;AACd,SAAK,gBAAgB,KAAK;AAS1B,SAAK,gBAAgB,IAAI,aAAa,MAAM;AAC5C,SAAK,OAAO,KAAK,cAAc;AAC/B,SAAK,MAAM,QAAQ,KAAK,aAAa;AAErC,SAAK,QAAQ,WAAW,QAAQ,KAAK,SAAS,CAAC,CAAC;AAChD,SAAK,MAAM,SAAS,KAAK,KAAK;AAI9B,SAAK,YAAY,MAAM,KAAK,KAAK,QAAQ,CAAC;AAAA,EAC3C;AAAA,EAgCA,cACC,MACA,IACA,MAMiE;AAIjE,UAAM,aAKF,EAAE,MAAM,GAAG;AACf,QAAI,MAAM,aAAa,OAAW,YAAW,WAAW,KAAK;AAC7D,QAAI,MAAM,cAAc,OAAW,YAAW,YAAY,KAAK;AAC/D,UAAM,eAAwC,EAAE,SAAS,WAAW;AACpE,QAAI,MAAM,aAAa,KAAM,cAAa,WAAW;AACrD,QAAI,MAAM,SAAS,OAAW,cAAa,OAAO,KAAK;AACvD,WACC,KAAK,OAAO,SAGX,YAAY;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,mBAAmB,MAGU;AAC5B,UAAM,QAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,mBAAmB,KAAK,QAAQ;AAAA,MACtC,OAAO,KAAK;AAAA,MACZ,GAAI,SAAS,OAAO,EAAE,MAAM,IAAI,CAAC;AAAA,MACjC,GAAI,MAAM,YAAY,OAAO,EAAE,UAAU,KAAK,SAAS,IAAI,CAAC;AAAA,IAC7D,CAAC;AAAA,EACF;AACD;AA2BO,SAAS,QAAQ,QAAe,OAAuB,CAAC,GAAiB;AAC/E,QAAM,IAAI,IAAI,aAAa,QAAQ,IAAI;AAGvC,IAAE,WAAW,WAAW,gBAAgB,IAA0C,CAAC;AACnF,SAAO;AACR;","names":[]}
@@ -62,7 +62,7 @@ function fromCron(expr, opts) {
62
62
  };
63
63
  check();
64
64
  const id = setInterval(check, tickMs);
65
- return () => clearInterval(id);
65
+ return { onDeactivation: () => clearInterval(id) };
66
66
  },
67
67
  { ...sourceOpts(rest), name: rest.name ?? `cron:${expr}` }
68
68
  );
@@ -73,4 +73,4 @@ export {
73
73
  matchesCron,
74
74
  fromCron
75
75
  };
76
- //# sourceMappingURL=chunk-7EGRP2VX.js.map
76
+ //# sourceMappingURL=chunk-7BULJTL6.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/base/sources/event/cron.ts"],"sourcesContent":["/**\n * Cron-based reactive sources and schedule types.\n *\n * Merged from extra/cron.ts + extra/sources/event.ts (fromCron) during cleave A2.\n * `fromCron` relocated here from `dom.ts` (post-cleave /qa A1): it uses zero DOM\n * APIs (only `setInterval` + `new Date()`), so it belongs on the universal\n * `event/index.ts` barrel, not the browser-only `dom.ts` subpath.\n */\n\nimport { type Node, type NodeOptions, node, wallClockNs } 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/** Options for {@link fromCron}. */\nexport type FromCronOptions = ExtraOpts & {\n\t/** Polling interval in ms. Default `60_000`. */\n\ttickMs?: number;\n\t/** Output format: `\"timestamp_ns\"` (default) emits wall-clock nanoseconds; `\"date\"` emits a `Date` object. */\n\toutput?: \"timestamp_ns\" | \"date\";\n};\n\n/**\n * Minimal 5-field cron parser and matcher (minute hour day-of-month month day-of-week).\n * Ported from callbag-recharge `extra/cron.ts` for `fromCron` (roadmap §2.3).\n */\nexport interface CronSchedule {\n\tminutes: Set<number>;\n\thours: Set<number>;\n\tdaysOfMonth: Set<number>;\n\tmonths: Set<number>;\n\tdaysOfWeek: Set<number>;\n}\n\nfunction parseField(field: string, min: number, max: number): Set<number> {\n\tconst result = new Set<number>();\n\tfor (const part of field.split(\",\")) {\n\t\tconst [range, stepStr] = part.split(\"/\");\n\t\tconst step = stepStr ? Number.parseInt(stepStr, 10) : 1;\n\t\tif (Number.isNaN(step) || step < 1) throw new Error(`Invalid cron step: ${part}`);\n\t\tlet start: number;\n\t\tlet end: number;\n\t\tif (range === \"*\") {\n\t\t\tstart = min;\n\t\t\tend = max;\n\t\t} else if (range.includes(\"-\")) {\n\t\t\tconst [a, b] = range.split(\"-\");\n\t\t\tstart = Number.parseInt(a, 10);\n\t\t\tend = Number.parseInt(b, 10);\n\t\t} else {\n\t\t\tstart = Number.parseInt(range, 10);\n\t\t\tend = start;\n\t\t}\n\t\tif (Number.isNaN(start) || Number.isNaN(end)) throw new Error(`Invalid cron field: ${field}`);\n\t\tif (start < min || end > max)\n\t\t\tthrow new Error(`Cron field out of range: ${field} (${min}-${max})`);\n\t\tif (start > end) throw new Error(`Invalid cron range: ${start}-${end} in ${field}`);\n\t\tfor (let i = start; i <= end; i += step) result.add(i);\n\t}\n\treturn result;\n}\n\n/**\n * Parses a standard 5-field cron expression into a {@link CronSchedule}.\n *\n * Supports `*`, ranges (`1-5`), steps (`*\\/5`, `0-30/10`), and comma-separated\n * lists. Fields are: minute (0–59), hour (0–23), day-of-month (1–31),\n * month (1–12), day-of-week (0–6, Sunday = 0).\n *\n * @param expr - Five-field whitespace-separated cron string (e.g. `\"0 9 * * 1-5\"`).\n * @returns Parsed {@link CronSchedule} with one `Set<number>` per field.\n * @throws Error when the expression does not have exactly 5 fields, contains\n * out-of-range values, or uses an invalid step.\n *\n * @example\n * ```ts\n * import { parseCron } from \"@graphrefly/graphrefly-ts\";\n *\n * const sched = parseCron(\"0 9 * * 1-5\"); // weekdays at 09:00\n * sched.hours; // Set { 9 }\n * sched.daysOfWeek; // Set { 1, 2, 3, 4, 5 }\n * ```\n */\nexport function parseCron(expr: string): CronSchedule {\n\tconst parts = expr.trim().split(/\\s+/);\n\tif (parts.length !== 5) throw new Error(`Invalid cron: expected 5 fields, got ${parts.length}`);\n\treturn {\n\t\tminutes: parseField(parts[0], 0, 59),\n\t\thours: parseField(parts[1], 0, 23),\n\t\tdaysOfMonth: parseField(parts[2], 1, 31),\n\t\tmonths: parseField(parts[3], 1, 12),\n\t\tdaysOfWeek: parseField(parts[4], 0, 6),\n\t};\n}\n\n/**\n * Returns `true` if `date` satisfies every field of `schedule`.\n *\n * @param schedule - Parsed schedule from {@link parseCron}.\n * @param date - Moment to test (local time via `getMinutes`, `getHours`, etc.).\n * @returns `true` when all five cron fields match the given date.\n *\n * @example\n * ```ts\n * import { parseCron, matchesCron } from \"@graphrefly/graphrefly-ts\";\n *\n * const sched = parseCron(\"30 8 * * 1\"); // Mondays at 08:30\n * const monday = new Date(\"2026-03-30T08:30:00\"); // a Monday\n * matchesCron(sched, monday); // true\n * ```\n */\nexport function matchesCron(schedule: CronSchedule, date: Date): boolean {\n\treturn (\n\t\tschedule.minutes.has(date.getMinutes()) &&\n\t\tschedule.hours.has(date.getHours()) &&\n\t\tschedule.daysOfMonth.has(date.getDate()) &&\n\t\tschedule.months.has(date.getMonth() + 1) &&\n\t\tschedule.daysOfWeek.has(date.getDay())\n\t);\n}\n\n/**\n * Polls on an interval; when the current minute matches a 5-field cron expression, emits once (see {@link parseCron}).\n *\n * @param expr - Cron string (`min hour dom month dow`).\n * @param opts - Producer options plus `tickMs` (default `60_000`) and `output` (`timestamp_ns` default, or `date` for `Date` values).\n * @returns `Node<number>` (nanosecond timestamp) or `Node<Date>` when `output: \"date\"`.\n *\n * @example\n * ```ts\n * import { fromCron } from \"@graphrefly/graphrefly\";\n *\n * fromCron(\"0 9 * * 1\");\n * ```\n *\n * @category extra\n */\nexport function fromCron(expr: string, opts?: FromCronOptions & { output: \"date\" }): Node<Date>;\nexport function fromCron(expr: string, opts?: FromCronOptions): Node<number>;\nexport function fromCron(expr: string, opts?: FromCronOptions): Node<number | Date> {\n\tconst schedule: CronSchedule = parseCron(expr);\n\tconst { tickMs: tickOpt, output, ...rest } = opts ?? {};\n\tconst tickMs = tickOpt ?? 60_000;\n\tconst emitDate = output === \"date\";\n\treturn node<number | Date>(\n\t\t(_data, a) => {\n\t\t\tlet lastFiredKey = -1;\n\t\t\tconst check = () => {\n\t\t\t\tconst now = new Date();\n\t\t\t\tconst key =\n\t\t\t\t\tnow.getFullYear() * 100_000_000 +\n\t\t\t\t\t(now.getMonth() + 1) * 1_000_000 +\n\t\t\t\t\tnow.getDate() * 10_000 +\n\t\t\t\t\tnow.getHours() * 100 +\n\t\t\t\t\tnow.getMinutes();\n\t\t\t\tif (key !== lastFiredKey && matchesCron(schedule, now)) {\n\t\t\t\t\tlastFiredKey = key;\n\t\t\t\t\ta.emit(emitDate ? now : wallClockNs());\n\t\t\t\t}\n\t\t\t};\n\t\t\tcheck();\n\t\t\tconst id = setInterval(check, tickMs);\n\t\t\treturn () => clearInterval(id);\n\t\t},\n\t\t{ ...sourceOpts(rest), name: rest.name ?? `cron:${expr}` },\n\t);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/base/sources/event/cron.ts"],"sourcesContent":["/**\n * Cron-based reactive sources and schedule types.\n *\n * Merged from extra/cron.ts + extra/sources/event.ts (fromCron) during cleave A2.\n * `fromCron` relocated here from `dom.ts` (post-cleave /qa A1): it uses zero DOM\n * APIs (only `setInterval` + `new Date()`), so it belongs on the universal\n * `event/index.ts` barrel, not the browser-only `dom.ts` subpath.\n */\n\nimport { type Node, type NodeOptions, node, wallClockNs } 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/** Options for {@link fromCron}. */\nexport type FromCronOptions = ExtraOpts & {\n\t/** Polling interval in ms. Default `60_000`. */\n\ttickMs?: number;\n\t/** Output format: `\"timestamp_ns\"` (default) emits wall-clock nanoseconds; `\"date\"` emits a `Date` object. */\n\toutput?: \"timestamp_ns\" | \"date\";\n};\n\n/**\n * Minimal 5-field cron parser and matcher (minute hour day-of-month month day-of-week).\n * Ported from callbag-recharge `extra/cron.ts` for `fromCron` (roadmap §2.3).\n */\nexport interface CronSchedule {\n\tminutes: Set<number>;\n\thours: Set<number>;\n\tdaysOfMonth: Set<number>;\n\tmonths: Set<number>;\n\tdaysOfWeek: Set<number>;\n}\n\nfunction parseField(field: string, min: number, max: number): Set<number> {\n\tconst result = new Set<number>();\n\tfor (const part of field.split(\",\")) {\n\t\tconst [range, stepStr] = part.split(\"/\");\n\t\tconst step = stepStr ? Number.parseInt(stepStr, 10) : 1;\n\t\tif (Number.isNaN(step) || step < 1) throw new Error(`Invalid cron step: ${part}`);\n\t\tlet start: number;\n\t\tlet end: number;\n\t\tif (range === \"*\") {\n\t\t\tstart = min;\n\t\t\tend = max;\n\t\t} else if (range.includes(\"-\")) {\n\t\t\tconst [a, b] = range.split(\"-\");\n\t\t\tstart = Number.parseInt(a, 10);\n\t\t\tend = Number.parseInt(b, 10);\n\t\t} else {\n\t\t\tstart = Number.parseInt(range, 10);\n\t\t\tend = start;\n\t\t}\n\t\tif (Number.isNaN(start) || Number.isNaN(end)) throw new Error(`Invalid cron field: ${field}`);\n\t\tif (start < min || end > max)\n\t\t\tthrow new Error(`Cron field out of range: ${field} (${min}-${max})`);\n\t\tif (start > end) throw new Error(`Invalid cron range: ${start}-${end} in ${field}`);\n\t\tfor (let i = start; i <= end; i += step) result.add(i);\n\t}\n\treturn result;\n}\n\n/**\n * Parses a standard 5-field cron expression into a {@link CronSchedule}.\n *\n * Supports `*`, ranges (`1-5`), steps (`*\\/5`, `0-30/10`), and comma-separated\n * lists. Fields are: minute (0–59), hour (0–23), day-of-month (1–31),\n * month (1–12), day-of-week (0–6, Sunday = 0).\n *\n * @param expr - Five-field whitespace-separated cron string (e.g. `\"0 9 * * 1-5\"`).\n * @returns Parsed {@link CronSchedule} with one `Set<number>` per field.\n * @throws Error when the expression does not have exactly 5 fields, contains\n * out-of-range values, or uses an invalid step.\n *\n * @example\n * ```ts\n * import { parseCron } from \"@graphrefly/graphrefly-ts\";\n *\n * const sched = parseCron(\"0 9 * * 1-5\"); // weekdays at 09:00\n * sched.hours; // Set { 9 }\n * sched.daysOfWeek; // Set { 1, 2, 3, 4, 5 }\n * ```\n */\nexport function parseCron(expr: string): CronSchedule {\n\tconst parts = expr.trim().split(/\\s+/);\n\tif (parts.length !== 5) throw new Error(`Invalid cron: expected 5 fields, got ${parts.length}`);\n\treturn {\n\t\tminutes: parseField(parts[0], 0, 59),\n\t\thours: parseField(parts[1], 0, 23),\n\t\tdaysOfMonth: parseField(parts[2], 1, 31),\n\t\tmonths: parseField(parts[3], 1, 12),\n\t\tdaysOfWeek: parseField(parts[4], 0, 6),\n\t};\n}\n\n/**\n * Returns `true` if `date` satisfies every field of `schedule`.\n *\n * @param schedule - Parsed schedule from {@link parseCron}.\n * @param date - Moment to test (local time via `getMinutes`, `getHours`, etc.).\n * @returns `true` when all five cron fields match the given date.\n *\n * @example\n * ```ts\n * import { parseCron, matchesCron } from \"@graphrefly/graphrefly-ts\";\n *\n * const sched = parseCron(\"30 8 * * 1\"); // Mondays at 08:30\n * const monday = new Date(\"2026-03-30T08:30:00\"); // a Monday\n * matchesCron(sched, monday); // true\n * ```\n */\nexport function matchesCron(schedule: CronSchedule, date: Date): boolean {\n\treturn (\n\t\tschedule.minutes.has(date.getMinutes()) &&\n\t\tschedule.hours.has(date.getHours()) &&\n\t\tschedule.daysOfMonth.has(date.getDate()) &&\n\t\tschedule.months.has(date.getMonth() + 1) &&\n\t\tschedule.daysOfWeek.has(date.getDay())\n\t);\n}\n\n/**\n * Polls on an interval; when the current minute matches a 5-field cron expression, emits once (see {@link parseCron}).\n *\n * @param expr - Cron string (`min hour dom month dow`).\n * @param opts - Producer options plus `tickMs` (default `60_000`) and `output` (`timestamp_ns` default, or `date` for `Date` values).\n * @returns `Node<number>` (nanosecond timestamp) or `Node<Date>` when `output: \"date\"`.\n *\n * @example\n * ```ts\n * import { fromCron } from \"@graphrefly/graphrefly\";\n *\n * fromCron(\"0 9 * * 1\");\n * ```\n *\n * @category extra\n */\nexport function fromCron(expr: string, opts?: FromCronOptions & { output: \"date\" }): Node<Date>;\nexport function fromCron(expr: string, opts?: FromCronOptions): Node<number>;\nexport function fromCron(expr: string, opts?: FromCronOptions): Node<number | Date> {\n\tconst schedule: CronSchedule = parseCron(expr);\n\tconst { tickMs: tickOpt, output, ...rest } = opts ?? {};\n\tconst tickMs = tickOpt ?? 60_000;\n\tconst emitDate = output === \"date\";\n\treturn node<number | Date>(\n\t\t(_data, a) => {\n\t\t\tlet lastFiredKey = -1;\n\t\t\tconst check = () => {\n\t\t\t\tconst now = new Date();\n\t\t\t\tconst key =\n\t\t\t\t\tnow.getFullYear() * 100_000_000 +\n\t\t\t\t\t(now.getMonth() + 1) * 1_000_000 +\n\t\t\t\t\tnow.getDate() * 10_000 +\n\t\t\t\t\tnow.getHours() * 100 +\n\t\t\t\t\tnow.getMinutes();\n\t\t\t\tif (key !== lastFiredKey && matchesCron(schedule, now)) {\n\t\t\t\t\tlastFiredKey = key;\n\t\t\t\t\ta.emit(emitDate ? now : wallClockNs());\n\t\t\t\t}\n\t\t\t};\n\t\t\tcheck();\n\t\t\tconst id = setInterval(check, tickMs);\n\t\t\treturn { onDeactivation: () => clearInterval(id) };\n\t\t},\n\t\t{ ...sourceOpts(rest), name: rest.name ?? `cron:${expr}` },\n\t);\n}\n"],"mappings":"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","names":[]}
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  fromCron
3
- } from "./chunk-7EGRP2VX.js";
3
+ } from "./chunk-7BULJTL6.js";
4
4
  import {
5
5
  cqrs
6
- } from "./chunk-NY2PYHNC.js";
6
+ } from "./chunk-5UY3PNFY.js";
7
7
  import {
8
8
  createWatermarkController,
9
9
  toObservable
@@ -173,6 +173,12 @@ var GraphReflyEventExplorer = class {
173
173
  }
174
174
  disposers = [];
175
175
  scheduleNodeNames = [];
176
+ // F-CATCH D-1 (qa F-b): de-dup the DI-miss warning. `resolveInstance` is
177
+ // called once per handler-registry that a host appears in (EVENT / CRON /
178
+ // CQRS / …), so an unresolvable host decorated in multiple families would
179
+ // otherwise emit one warning per registry for a single root cause. Scoped
180
+ // per explorer instance (one instance per module via the useFactory).
181
+ warnedUnresolved = /* @__PURE__ */ new Set();
176
182
  onModuleInit() {
177
183
  this.wireEvents();
178
184
  this.wireIntervals();
@@ -403,11 +409,23 @@ var GraphReflyEventExplorer = class {
403
409
  }
404
410
  }
405
411
  resolveInstance(ctor) {
412
+ let instance = null;
406
413
  try {
407
- return this.moduleRef.get(ctor, { strict: false });
414
+ instance = this.moduleRef.get(ctor, { strict: false }) ?? null;
408
415
  } catch {
416
+ instance = null;
417
+ }
418
+ if (instance == null) {
419
+ if (!this.warnedUnresolved.has(ctor)) {
420
+ this.warnedUnresolved.add(ctor);
421
+ const name = ctor.name ?? String(ctor);
422
+ console.warn(
423
+ `[GraphReFly] decorator host "${name}" not resolvable from DI \u2014 is it registered as a provider in the same module as GraphReflyModule? Its @OnGraphEvent/@Cron/@CqrsHandler handlers will NOT be wired.`
424
+ );
425
+ }
409
426
  return null;
410
427
  }
428
+ return instance;
411
429
  }
412
430
  };
413
431
 
@@ -808,6 +826,9 @@ function fromHeader(headerName = "x-graphrefly-actor") {
808
826
  try {
809
827
  return JSON.parse(raw);
810
828
  } catch {
829
+ console.warn(
830
+ `[GraphReFly] fromHeader: header "${headerName}" is present but not valid JSON \u2014 ignoring it and falling back to DEFAULT_ACTOR.`
831
+ );
811
832
  return void 0;
812
833
  }
813
834
  };
@@ -1076,4 +1097,4 @@ export {
1076
1097
  toObservable2 as toObservable,
1077
1098
  nestjs_exports
1078
1099
  };
1079
- //# sourceMappingURL=chunk-VLAGJZSL.js.map
1100
+ //# sourceMappingURL=chunk-7T7WLEPM.js.map