@graphrefly/graphrefly 0.47.2 → 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 (266) hide show
  1. package/dist/base/composition/index.cjs +4 -3
  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 +8 -8
  6. package/dist/base/index.cjs +152 -78
  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 +75 -70
  11. package/dist/base/io/index.cjs +31 -17
  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 +1 -1
  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 +5 -3
  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 +5 -3
  26. package/dist/base/sources/browser/index.js.map +1 -1
  27. package/dist/base/sources/event/index.cjs +28 -0
  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 +4 -1
  32. package/dist/base/sources/index.cjs +75 -37
  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 +5 -2
  37. package/dist/{chunk-R6ZCSXKX.js → chunk-23MAWVOJ.js} +3 -3
  38. package/dist/{chunk-MS3WPRJR.js → chunk-3REMCHSS.js} +6 -6
  39. package/dist/chunk-3REMCHSS.js.map +1 -0
  40. package/dist/{chunk-CEVNQ74M.js → chunk-3YGXPUHW.js} +2 -2
  41. package/dist/{chunk-CEVNQ74M.js.map → chunk-3YGXPUHW.js.map} +1 -1
  42. package/dist/{chunk-6ZLCPUXS.js → chunk-46X2EFQH.js} +15 -4
  43. package/dist/chunk-46X2EFQH.js.map +1 -0
  44. package/dist/{chunk-NY2PYHNC.js → chunk-5UY3PNFY.js} +12 -5
  45. package/dist/chunk-5UY3PNFY.js.map +1 -0
  46. package/dist/{chunk-FQSQONOU.js → chunk-65OM4XLQ.js} +49 -3
  47. package/dist/chunk-65OM4XLQ.js.map +1 -0
  48. package/dist/{chunk-3PSLNJDU.js → chunk-6DQYBIHW.js} +314 -49
  49. package/dist/chunk-6DQYBIHW.js.map +1 -0
  50. package/dist/{chunk-LDCSZ72P.js → chunk-6YBER5UP.js} +3 -3
  51. package/dist/{chunk-LDCSZ72P.js.map → chunk-6YBER5UP.js.map} +1 -1
  52. package/dist/{chunk-3O3NKZJW.js → chunk-7T7WLEPM.js} +24 -3
  53. package/dist/chunk-7T7WLEPM.js.map +1 -0
  54. package/dist/{chunk-PKPO3JTZ.js → chunk-AQAKDE7F.js} +29 -11
  55. package/dist/chunk-AQAKDE7F.js.map +1 -0
  56. package/dist/{chunk-6MRSX3YK.js → chunk-B5Y5GPD5.js} +2 -2
  57. package/dist/{chunk-BXGZFGZ4.js → chunk-C5QD5DQX.js} +22 -1
  58. package/dist/chunk-C5QD5DQX.js.map +1 -0
  59. package/dist/{chunk-4XCHZRUJ.js → chunk-D5YGR4TP.js} +58 -7
  60. package/dist/chunk-D5YGR4TP.js.map +1 -0
  61. package/dist/{chunk-NPRP3MCV.js → chunk-DHDCOOJU.js} +2 -2
  62. package/dist/chunk-DHDCOOJU.js.map +1 -0
  63. package/dist/{chunk-VP3TIUDF.js → chunk-DVTDF5OI.js} +2 -2
  64. package/dist/{chunk-OXD5LFQP.js → chunk-G7H6PN7P.js} +2 -2
  65. package/dist/{chunk-EL5VHUGK.js → chunk-GGKHHG5Y.js} +32 -18
  66. package/dist/chunk-GGKHHG5Y.js.map +1 -0
  67. package/dist/{chunk-446I4EGD.js → chunk-J5TBZFBD.js} +2 -2
  68. package/dist/{chunk-7AVQIGF6.js → chunk-K4ZYJ4EM.js} +554 -460
  69. package/dist/chunk-K4ZYJ4EM.js.map +1 -0
  70. package/dist/{chunk-QFE5BQH7.js → chunk-LTSI7ULC.js} +2 -2
  71. package/dist/{chunk-5GVURVIG.js → chunk-MMHGYX44.js} +12 -2
  72. package/dist/{chunk-5GVURVIG.js.map → chunk-MMHGYX44.js.map} +1 -1
  73. package/dist/{chunk-KRFGO5QH.js → chunk-MQMTRKY3.js} +118 -43
  74. package/dist/chunk-MQMTRKY3.js.map +1 -0
  75. package/dist/{chunk-42FQ27MQ.js → chunk-MTODGQBR.js} +44 -179
  76. package/dist/chunk-MTODGQBR.js.map +1 -0
  77. package/dist/{chunk-FVINAAKA.js → chunk-NBK6QQMG.js} +14 -13
  78. package/dist/{chunk-FVINAAKA.js.map → chunk-NBK6QQMG.js.map} +1 -1
  79. package/dist/{chunk-KNU73RZW.js → chunk-NSA5K5G2.js} +2 -2
  80. package/dist/{chunk-MLTPJMH6.js → chunk-QQYULEZL.js} +2 -2
  81. package/dist/chunk-QSW4DFKE.js +31 -0
  82. package/dist/chunk-QSW4DFKE.js.map +1 -0
  83. package/dist/{chunk-VAZXUK6G.js → chunk-SUNCHMML.js} +2 -2
  84. package/dist/{chunk-EP4WVQLX.js → chunk-T2U6N3FV.js} +6 -6
  85. package/dist/{chunk-T7SP3EYR.js → chunk-T5URUIIY.js} +33 -24
  86. package/dist/chunk-T5URUIIY.js.map +1 -0
  87. package/dist/{chunk-VNXAF2KE.js → chunk-TPTZZV25.js} +6 -6
  88. package/dist/chunk-TPTZZV25.js.map +1 -0
  89. package/dist/{chunk-IOJDYUA7.js → chunk-V46JWFGV.js} +6 -5
  90. package/dist/chunk-V46JWFGV.js.map +1 -0
  91. package/dist/{chunk-WGDEBIP4.js → chunk-X6ESZDR6.js} +5 -6
  92. package/dist/chunk-X6ESZDR6.js.map +1 -0
  93. package/dist/{chunk-N65E26UL.js → chunk-XEWV254I.js} +2 -2
  94. package/dist/{chunk-N65E26UL.js.map → chunk-XEWV254I.js.map} +1 -1
  95. package/dist/{chunk-PTWADEH3.js → chunk-YBJVKMTM.js} +34 -14
  96. package/dist/chunk-YBJVKMTM.js.map +1 -0
  97. package/dist/{chunk-DDTS7F5O.js → chunk-ZW32BPXV.js} +12 -3
  98. package/dist/chunk-ZW32BPXV.js.map +1 -0
  99. package/dist/compat/index.cjs +51 -4
  100. package/dist/compat/index.cjs.map +1 -1
  101. package/dist/compat/index.d.cts +1 -1
  102. package/dist/compat/index.d.ts +1 -1
  103. package/dist/compat/index.js +6 -6
  104. package/dist/compat/nestjs/index.cjs +51 -4
  105. package/dist/compat/nestjs/index.cjs.map +1 -1
  106. package/dist/compat/nestjs/index.d.cts +1 -1
  107. package/dist/compat/nestjs/index.d.ts +1 -1
  108. package/dist/compat/nestjs/index.js +3 -3
  109. package/dist/{fallback-Bx46zqky.d.cts → fallback-BROR6ZhO.d.cts} +1 -1
  110. package/dist/{fallback-pIWW8A2d.d.ts → fallback-DO80aM_3.d.ts} +1 -1
  111. package/dist/{index-B_p8tnvf.d.cts → index-D1z3XcF9.d.cts} +1 -0
  112. package/dist/{index-_HDSmPyp.d.ts → index-DZ6yua0Q.d.ts} +1 -0
  113. package/dist/index.cjs +2215 -1676
  114. package/dist/index.cjs.map +1 -1
  115. package/dist/index.d.cts +10 -10
  116. package/dist/index.d.ts +10 -10
  117. package/dist/index.js +169 -146
  118. package/dist/index.js.map +1 -1
  119. package/dist/presets/ai/index.cjs +46 -0
  120. package/dist/presets/ai/index.cjs.map +1 -1
  121. package/dist/presets/ai/index.js +12 -12
  122. package/dist/presets/harness/index.cjs +130 -18
  123. package/dist/presets/harness/index.cjs.map +1 -1
  124. package/dist/presets/harness/index.d.cts +15 -5
  125. package/dist/presets/harness/index.d.ts +15 -5
  126. package/dist/presets/harness/index.js +22 -22
  127. package/dist/presets/index.cjs +222 -53
  128. package/dist/presets/index.cjs.map +1 -1
  129. package/dist/presets/index.d.cts +2 -2
  130. package/dist/presets/index.d.ts +2 -2
  131. package/dist/presets/index.js +45 -45
  132. package/dist/presets/inspect/index.cjs +63 -14
  133. package/dist/presets/inspect/index.cjs.map +1 -1
  134. package/dist/presets/inspect/index.d.cts +1 -1
  135. package/dist/presets/inspect/index.d.ts +1 -1
  136. package/dist/presets/inspect/index.js +6 -6
  137. package/dist/presets/resilience/index.cjs +29 -21
  138. package/dist/presets/resilience/index.cjs.map +1 -1
  139. package/dist/presets/resilience/index.d.cts +12 -8
  140. package/dist/presets/resilience/index.d.ts +12 -8
  141. package/dist/presets/resilience/index.js +3 -3
  142. package/dist/{rate-limiter-DpVbSYdH.d.cts → rate-limiter-DC26FM8J.d.cts} +10 -1
  143. package/dist/{rate-limiter-CEALq4N1.d.ts → rate-limiter-DyWpwpQP.d.ts} +10 -1
  144. package/dist/{reactive-layout-fswlBUvX.d.ts → reactive-layout-BBBWH0V_.d.cts} +85 -4
  145. package/dist/{reactive-layout-fswlBUvX.d.cts → reactive-layout-BBBWH0V_.d.ts} +85 -4
  146. package/dist/solutions/index.cjs +168 -47
  147. package/dist/solutions/index.cjs.map +1 -1
  148. package/dist/solutions/index.d.cts +2 -2
  149. package/dist/solutions/index.d.ts +2 -2
  150. package/dist/solutions/index.js +28 -28
  151. package/dist/{spawnable-5mDY501F.d.cts → spawnable-B2IlW60f.d.cts} +23 -2
  152. package/dist/{spawnable-D3lR0oQu.d.ts → spawnable-tttFz2Nh.d.ts} +23 -2
  153. package/dist/testing/index.cjs +94 -0
  154. package/dist/testing/index.cjs.map +1 -0
  155. package/dist/testing/index.d.cts +59 -0
  156. package/dist/testing/index.d.ts +59 -0
  157. package/dist/testing/index.js +73 -0
  158. package/dist/testing/index.js.map +1 -0
  159. package/dist/utils/ai/browser.cjs.map +1 -1
  160. package/dist/utils/ai/browser.d.cts +2 -2
  161. package/dist/utils/ai/browser.d.ts +2 -2
  162. package/dist/utils/ai/browser.js +6 -6
  163. package/dist/utils/ai/browser.js.map +1 -1
  164. package/dist/utils/ai/index.cjs +250 -166
  165. package/dist/utils/ai/index.cjs.map +1 -1
  166. package/dist/utils/ai/index.d.cts +108 -12
  167. package/dist/utils/ai/index.d.ts +108 -12
  168. package/dist/utils/ai/index.js +21 -19
  169. package/dist/utils/ai/node.cjs.map +1 -1
  170. package/dist/utils/ai/node.d.cts +5 -5
  171. package/dist/utils/ai/node.d.ts +5 -5
  172. package/dist/utils/ai/node.js +2 -2
  173. package/dist/utils/ai/node.js.map +1 -1
  174. package/dist/utils/cqrs/index.cjs +29 -3
  175. package/dist/utils/cqrs/index.cjs.map +1 -1
  176. package/dist/utils/cqrs/index.d.cts +12 -7
  177. package/dist/utils/cqrs/index.d.ts +12 -7
  178. package/dist/utils/cqrs/index.js +2 -2
  179. package/dist/utils/demo-shell/index.cjs +45 -19
  180. package/dist/utils/demo-shell/index.cjs.map +1 -1
  181. package/dist/utils/demo-shell/index.d.cts +1 -1
  182. package/dist/utils/demo-shell/index.d.ts +1 -1
  183. package/dist/utils/demo-shell/index.js +2 -2
  184. package/dist/utils/domain-templates/index.cjs.map +1 -1
  185. package/dist/utils/domain-templates/index.js +3 -3
  186. package/dist/utils/graphspec/index.cjs.map +1 -1
  187. package/dist/utils/graphspec/index.js +3 -3
  188. package/dist/utils/index.cjs +1642 -1225
  189. package/dist/utils/index.cjs.map +1 -1
  190. package/dist/utils/index.d.cts +7 -7
  191. package/dist/utils/index.d.ts +7 -7
  192. package/dist/utils/index.js +72 -54
  193. package/dist/utils/inspect/index.cjs +52 -4
  194. package/dist/utils/inspect/index.cjs.map +1 -1
  195. package/dist/utils/inspect/index.d.cts +32 -3
  196. package/dist/utils/inspect/index.d.ts +32 -3
  197. package/dist/utils/inspect/index.js +4 -4
  198. package/dist/utils/job-queue/index.cjs +46 -9
  199. package/dist/utils/job-queue/index.cjs.map +1 -1
  200. package/dist/utils/job-queue/index.d.cts +33 -3
  201. package/dist/utils/job-queue/index.d.ts +33 -3
  202. package/dist/utils/job-queue/index.js +2 -2
  203. package/dist/utils/memory/index.cjs +556 -462
  204. package/dist/utils/memory/index.cjs.map +1 -1
  205. package/dist/utils/memory/index.d.cts +203 -24
  206. package/dist/utils/memory/index.d.ts +203 -24
  207. package/dist/utils/memory/index.js +10 -2
  208. package/dist/utils/messaging/index.cjs.map +1 -1
  209. package/dist/utils/messaging/index.d.cts +4 -3
  210. package/dist/utils/messaging/index.d.ts +4 -3
  211. package/dist/utils/messaging/index.js +2 -2
  212. package/dist/utils/orchestration/index.cjs +9 -0
  213. package/dist/utils/orchestration/index.cjs.map +1 -1
  214. package/dist/utils/orchestration/index.js +3 -3
  215. package/dist/utils/process/index.cjs +32 -2
  216. package/dist/utils/process/index.cjs.map +1 -1
  217. package/dist/utils/process/index.d.cts +4 -3
  218. package/dist/utils/process/index.d.ts +4 -3
  219. package/dist/utils/process/index.js +2 -2
  220. package/dist/utils/reactive-layout/index.cjs +184 -55
  221. package/dist/utils/reactive-layout/index.cjs.map +1 -1
  222. package/dist/utils/reactive-layout/index.d.cts +128 -3
  223. package/dist/utils/reactive-layout/index.d.ts +128 -3
  224. package/dist/utils/reactive-layout/index.js +16 -8
  225. package/dist/utils/reduction/index.cjs.map +1 -1
  226. package/dist/utils/reduction/index.js +2 -2
  227. package/dist/utils/resilience/index.cjs +29 -20
  228. package/dist/utils/resilience/index.cjs.map +1 -1
  229. package/dist/utils/resilience/index.d.cts +1 -1
  230. package/dist/utils/resilience/index.d.ts +1 -1
  231. package/dist/utils/resilience/index.js +2 -2
  232. package/dist/utils/surface/index.cjs.map +1 -1
  233. package/dist/utils/surface/index.js +4 -4
  234. package/package.json +15 -3
  235. package/dist/chunk-3O3NKZJW.js.map +0 -1
  236. package/dist/chunk-3PSLNJDU.js.map +0 -1
  237. package/dist/chunk-42FQ27MQ.js.map +0 -1
  238. package/dist/chunk-4XCHZRUJ.js.map +0 -1
  239. package/dist/chunk-6ZLCPUXS.js.map +0 -1
  240. package/dist/chunk-7AVQIGF6.js.map +0 -1
  241. package/dist/chunk-BXGZFGZ4.js.map +0 -1
  242. package/dist/chunk-DDTS7F5O.js.map +0 -1
  243. package/dist/chunk-EL5VHUGK.js.map +0 -1
  244. package/dist/chunk-FQSQONOU.js.map +0 -1
  245. package/dist/chunk-IOJDYUA7.js.map +0 -1
  246. package/dist/chunk-KRFGO5QH.js.map +0 -1
  247. package/dist/chunk-MS3WPRJR.js.map +0 -1
  248. package/dist/chunk-NPRP3MCV.js.map +0 -1
  249. package/dist/chunk-NY2PYHNC.js.map +0 -1
  250. package/dist/chunk-PKPO3JTZ.js.map +0 -1
  251. package/dist/chunk-PTWADEH3.js.map +0 -1
  252. package/dist/chunk-T7SP3EYR.js.map +0 -1
  253. package/dist/chunk-VNXAF2KE.js.map +0 -1
  254. package/dist/chunk-W2BOPXTI.js +0 -1
  255. package/dist/chunk-W2BOPXTI.js.map +0 -1
  256. package/dist/chunk-WGDEBIP4.js.map +0 -1
  257. /package/dist/{chunk-R6ZCSXKX.js.map → chunk-23MAWVOJ.js.map} +0 -0
  258. /package/dist/{chunk-6MRSX3YK.js.map → chunk-B5Y5GPD5.js.map} +0 -0
  259. /package/dist/{chunk-VP3TIUDF.js.map → chunk-DVTDF5OI.js.map} +0 -0
  260. /package/dist/{chunk-OXD5LFQP.js.map → chunk-G7H6PN7P.js.map} +0 -0
  261. /package/dist/{chunk-446I4EGD.js.map → chunk-J5TBZFBD.js.map} +0 -0
  262. /package/dist/{chunk-QFE5BQH7.js.map → chunk-LTSI7ULC.js.map} +0 -0
  263. /package/dist/{chunk-KNU73RZW.js.map → chunk-NSA5K5G2.js.map} +0 -0
  264. /package/dist/{chunk-MLTPJMH6.js.map → chunk-QQYULEZL.js.map} +0 -0
  265. /package/dist/{chunk-VAZXUK6G.js.map → chunk-SUNCHMML.js.map} +0 -0
  266. /package/dist/{chunk-EP4WVQLX.js.map → chunk-T2U6N3FV.js.map} +0 -0
@@ -9,11 +9,11 @@ import {
9
9
  restoreSnapshot,
10
10
  runReduction,
11
11
  saveSnapshot
12
- } from "../../chunk-VAZXUK6G.js";
13
- import "../../chunk-6MRSX3YK.js";
14
- import "../../chunk-QFE5BQH7.js";
12
+ } from "../../chunk-SUNCHMML.js";
13
+ import "../../chunk-B5Y5GPD5.js";
14
+ import "../../chunk-LTSI7ULC.js";
15
15
  import "../../chunk-FMPF42Q4.js";
16
- import "../../chunk-BXGZFGZ4.js";
16
+ import "../../chunk-C5QD5DQX.js";
17
17
  import "../../chunk-AZDQPQ3V.js";
18
18
  export {
19
19
  SNAPSHOT_WIRE_VERSION,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphrefly/graphrefly",
3
- "version": "0.47.2",
3
+ "version": "0.48.0",
4
4
  "description": "Reactive harness layer for agent workflows. Describe automations in plain language, trace every decision, enforce policies, persist checkpoints. Zero dependencies.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -426,6 +426,16 @@
426
426
  "default": "./dist/solutions/index.cjs"
427
427
  }
428
428
  },
429
+ "./testing": {
430
+ "import": {
431
+ "types": "./dist/testing/index.d.ts",
432
+ "default": "./dist/testing/index.js"
433
+ },
434
+ "require": {
435
+ "types": "./dist/testing/index.d.cts",
436
+ "default": "./dist/testing/index.cjs"
437
+ }
438
+ },
429
439
  "./compat": {
430
440
  "import": {
431
441
  "types": "./dist/compat/index.d.ts",
@@ -645,8 +655,9 @@
645
655
  "changeset": "changeset",
646
656
  "version-packages": "changeset version",
647
657
  "release": "pnpm run build && changeset publish",
648
- "lint": "biome check . && tsx scripts/check-layer-boundary.ts",
658
+ "lint": "biome check . && tsx scripts/check-layer-boundary.ts && tsx scripts/check-typecheck.ts",
649
659
  "lint:layers": "tsx scripts/check-layer-boundary.ts",
660
+ "lint:typecheck": "tsx scripts/check-typecheck.ts",
650
661
  "lint:fix": "biome check --write .",
651
662
  "format": "biome format --write .",
652
663
  "eval": "tsx evals/scripts/run-all.ts",
@@ -656,6 +667,7 @@
656
667
  "eval:matrix": "tsx evals/scripts/run-matrix.ts",
657
668
  "eval:scorecard": "tsx evals/scripts/publish-scorecard.ts",
658
669
  "eval:dev-dx": "vitest run --config evals/vitest.config.ts",
659
- "eval:harness": "tsx evals/scripts/run-harness.ts"
670
+ "eval:harness": "tsx evals/scripts/run-harness.ts",
671
+ "eval:smoke": "test -f evals/results/l0-conversational-baseline.json || { echo 'eval:smoke: fixture evals/results/l0-conversational-baseline.json is missing — the dogfood wiring smoke would vacuously pass (run-harness exits 0 on empty results). Restore the fixture or repoint eval:smoke.' >&2; exit 1; }; tsx evals/scripts/run-harness.ts --dry-run --file l0-conversational-baseline"
660
672
  }
661
673
  }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/compat/nestjs/index.ts","../src/compat/nestjs/decorators.ts","../src/compat/nestjs/tokens.ts","../src/compat/nestjs/explorer.ts","../src/compat/nestjs/gateway.ts","../src/compat/nestjs/guard.ts","../src/compat/nestjs/module.ts","../src/compat/nestjs/observable.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// NestJS integration — Module, DI, Lifecycle, RxJS bridge (Phase 5.5)\n// ---------------------------------------------------------------------------\n// Bridges GraphReFly into NestJS's DI container and RxJS-based ecosystem.\n// NestJS and RxJS are peer dependencies — install them in your NestJS app.\n//\n// Usage:\n// import { GraphReflyModule, InjectGraph, InjectNode, toObservable }\n// from '@graphrefly/graphrefly-ts/compat/nestjs';\n// ---------------------------------------------------------------------------\n\n// Decorators\nexport {\n\tCOMMAND_HANDLERS,\n\tCommandHandler,\n\ttype CommandHandlerMeta,\n\tCQRS_EVENT_HANDLERS,\n\tCRON_HANDLERS,\n\tEVENT_HANDLERS,\n\tEventHandler,\n\ttype EventHandlerMeta,\n\tGraphCron,\n\ttype GraphCronMeta,\n\tGraphInterval,\n\ttype GraphIntervalMeta,\n\tINTERVAL_HANDLERS,\n\tInjectCqrsGraph,\n\tInjectGraph,\n\tInjectNode,\n\tOnGraphEvent,\n\ttype OnGraphEventMeta,\n\tQUERY_HANDLERS,\n\tQueryHandler,\n\ttype QueryHandlerMeta,\n\tSAGA_HANDLERS,\n\tSagaHandler,\n\ttype SagaHandlerMeta,\n} from \"./decorators.js\";\n// Explorer (event/schedule discovery)\nexport { GraphReflyEventExplorer } from \"./explorer.js\";\n// Gateway helpers (Phase 5.1)\nexport {\n\tObserveGateway,\n\ttype ObserveGatewayOptions,\n\ttype ObserveSSEOptions,\n\ttype ObserveSubscriptionOptions,\n\ttype ObserveWsCommand,\n\ttype ObserveWsMessage,\n\tobserveSSE,\n\tobserveSubscription,\n} from \"./gateway.js\";\n// Actor bridge (Phase 5.1)\nexport {\n\tACTOR_KEY,\n\ttype ActorExtractor,\n\tfromHeader,\n\tfromJwtPayload,\n\tGraphReflyGuard,\n\tGraphReflyGuardImpl,\n\tgetActor,\n} from \"./guard.js\";\n// Module & DI\nexport {\n\ttype GraphReflyCqrsOptions,\n\ttype GraphReflyFeatureOptions,\n\tGraphReflyModule,\n\ttype GraphReflyRootOptions,\n} from \"./module.js\";\n// RxJS bridge — NestJS-flavored: returns a real rxjs `Observable` (the base\n// `toObservable` is dependency-free and returns a Symbol.observable interop;\n// this layer wraps it with rxjs `from()` since `@nestjs/common` pulls rxjs).\nexport { type ToObservableOptions, toObservable } from \"./observable.js\";\n// Injection tokens\nexport {\n\tGRAPHREFLY_REQUEST_GRAPH,\n\tGRAPHREFLY_ROOT_GRAPH,\n\tgetGraphToken,\n\tgetNodeToken,\n} from \"./tokens.js\";\n","// ---------------------------------------------------------------------------\n// NestJS decorators for GraphReFly DI, events, and scheduling.\n// ---------------------------------------------------------------------------\n// NOTE: esbuild (used by vitest/vite) uses TC39 Stage 3 decorators, not\n// legacy TypeScript experimental decorators. Method decorators receive\n// (value: DecoratorBoundMethod, context: ClassMethodDecoratorContext). We use\n// context.addInitializer() to register metadata when the class instance is\n// created, which runs before NestJS lifecycle hooks.\n// ---------------------------------------------------------------------------\n\nimport { Inject } from \"@nestjs/common\";\nimport {\n\tGRAPHREFLY_REQUEST_GRAPH,\n\tGRAPHREFLY_ROOT_GRAPH,\n\tgetGraphToken,\n\tgetNodeToken,\n} from \"./tokens.js\";\n\n/** Class constructor key for decorator registries and Nest `ModuleRef.get()`. */\nexport type DecoratorHostConstructor = abstract new (...args: unknown[]) => unknown;\n\n/**\n * TC39 Stage 3 class method decorator first argument (the method itself).\n * Narrower than `Function` for Biome `noBannedTypes`.\n */\nexport type DecoratorBoundMethod = (...args: unknown[]) => unknown;\n\n// ---------------------------------------------------------------------------\n// Global registries (populated by decorator initializers, read by explorer)\n// ---------------------------------------------------------------------------\n\nexport interface OnGraphEventMeta {\n\tnodeName: string;\n\tmethodKey: string | symbol;\n}\n\nexport interface GraphIntervalMeta {\n\tms: number;\n\tmethodKey: string | symbol;\n}\n\nexport interface GraphCronMeta {\n\texpr: string;\n\tmethodKey: string | symbol;\n}\n\n/** Registry: constructor → event handler metadata. */\nexport const EVENT_HANDLERS = new Map<DecoratorHostConstructor, OnGraphEventMeta[]>();\n/** Registry: constructor → interval metadata. */\nexport const INTERVAL_HANDLERS = new Map<DecoratorHostConstructor, GraphIntervalMeta[]>();\n/** Registry: constructor → cron metadata. */\nexport const CRON_HANDLERS = new Map<DecoratorHostConstructor, GraphCronMeta[]>();\n\n// ---------------------------------------------------------------------------\n// CQRS decorator metadata & registries (Phase 5.5 — CQRS replacement)\n// ---------------------------------------------------------------------------\n\nexport interface CommandHandlerMeta {\n\tcqrsName: string;\n\tcommandName: string;\n\tmethodKey: string | symbol;\n}\n\nexport interface EventHandlerMeta {\n\tcqrsName: string;\n\teventName: string;\n\tmethodKey: string | symbol;\n}\n\nexport interface QueryHandlerMeta {\n\tcqrsName: string;\n\tprojectionName: string;\n\tmethodKey: string | symbol;\n}\n\nexport interface SagaHandlerMeta {\n\tcqrsName: string;\n\teventNames: readonly string[];\n\tsagaName: string;\n\tmethodKey: string | symbol;\n}\n\n/** Registry: constructor → command handler metadata. */\nexport const COMMAND_HANDLERS = new Map<DecoratorHostConstructor, CommandHandlerMeta[]>();\n/** Registry: constructor → event handler metadata. */\nexport const CQRS_EVENT_HANDLERS = new Map<DecoratorHostConstructor, EventHandlerMeta[]>();\n/** Registry: constructor → query handler metadata. */\nexport const QUERY_HANDLERS = new Map<DecoratorHostConstructor, QueryHandlerMeta[]>();\n/** Registry: constructor → saga handler metadata. */\nexport const SAGA_HANDLERS = new Map<DecoratorHostConstructor, SagaHandlerMeta[]>();\n\n// ---------------------------------------------------------------------------\n// DI decorators\n// ---------------------------------------------------------------------------\n\n/**\n * Inject a `Graph` instance into a NestJS service or controller.\n *\n * - No argument → injects the root graph (from `forRoot()`).\n * - With `name` → injects the named feature graph (from `forFeature({ name })`).\n * - With `\"request\"` → injects the request-scoped graph (requires `requestScope: true`).\n *\n * @example\n * ```ts\n * @Injectable()\n * export class PaymentService {\n * constructor(\n * @InjectGraph() private root: Graph,\n * @InjectGraph(\"payments\") private payments: Graph,\n * ) {}\n * }\n * ```\n */\nexport function InjectGraph(name?: string): ParameterDecorator & PropertyDecorator {\n\tif (name === \"request\") return Inject(GRAPHREFLY_REQUEST_GRAPH);\n\treturn Inject(name ? getGraphToken(name) : GRAPHREFLY_ROOT_GRAPH);\n}\n\n/**\n * Inject a `CqrsGraph` instance into a NestJS service or controller.\n *\n * Typed alternative to `@InjectGraph(name)` — returns `CqrsGraph` instead of `Graph`,\n * giving access to `.command()`, `.dispatch()`, `.event()`, `.projection()`, `.saga()`.\n *\n * @param name - The CQRS graph name (from `forCqrs({ name })`).\n *\n * @example\n * ```ts\n * @Injectable()\n * export class OrderService {\n * constructor(@InjectCqrsGraph(\"orders\") private orders: CqrsGraph) {\n * orders.dispatch(\"placeOrder\", { id: \"1\" }); // fully typed\n * }\n * }\n * ```\n */\nexport function InjectCqrsGraph(name: string): ParameterDecorator & PropertyDecorator {\n\treturn Inject(getGraphToken(name));\n}\n\n/**\n * Inject a `Node` from the graph by its qualified path.\n *\n * The path must be declared in the `nodes` array of `forRoot()` or `forFeature()`.\n * The module registers a factory provider that resolves the node from the root graph\n * at injection time.\n *\n * @example\n * ```ts\n * GraphReflyModule.forRoot({ nodes: [\"payment::validate\"] })\n *\n * @Injectable()\n * export class PaymentService {\n * constructor(@InjectNode(\"payment::validate\") private validate: Node<boolean>) {}\n * }\n * ```\n */\nexport function InjectNode(path: string): ParameterDecorator & PropertyDecorator {\n\treturn Inject(getNodeToken(path));\n}\n\n// ---------------------------------------------------------------------------\n// Event & schedule method decorators (TC39 Stage 3 decorator API)\n// ---------------------------------------------------------------------------\n\n/**\n * Subscribe a method to a graph node's DATA emissions — replaces `@OnEvent()`.\n *\n * The method is called with the value payload on each `DATA` message from the\n * named node. Routes through `graph.observe()` so actor guards are respected.\n * Subscription is created on module init and disposed on destroy.\n *\n * For full protocol access (DIRTY, COMPLETE, ERROR, custom types), use\n * `graph.observe()` directly instead of this decorator.\n *\n * @param nodeName - Qualified node path (e.g. `\"orders::placed\"`).\n *\n * @example\n * ```ts\n * @Injectable()\n * export class OrderService {\n * @OnGraphEvent(\"orders::placed\")\n * handleOrder(value: Order) { ... }\n * }\n * ```\n */\nexport function OnGraphEvent(\n\tnodeName: string,\n): (value: DecoratorBoundMethod, context: ClassMethodDecoratorContext) => void {\n\treturn (_value: DecoratorBoundMethod, context: ClassMethodDecoratorContext) => {\n\t\tconst methodKey = context.name;\n\t\tcontext.addInitializer(function (this: unknown) {\n\t\t\tconst ctor = (this as { constructor: DecoratorHostConstructor }).constructor;\n\t\t\tconst existing = EVENT_HANDLERS.get(ctor) ?? [];\n\t\t\texisting.push({ nodeName, methodKey });\n\t\t\tEVENT_HANDLERS.set(ctor, existing);\n\t\t});\n\t};\n}\n\n/**\n * Run a method on a fixed interval — replaces `@Interval()` from `@nestjs/schedule`.\n *\n * Backed by a `fromTimer` node added to the root graph as `__schedule__.<className>.<methodName>`.\n * Visible in `graph.describe()`, pausable via `graph.signal(name, [[PAUSE, lockId]])`.\n *\n * @param ms - Interval in milliseconds.\n *\n * @example\n * ```ts\n * @Injectable()\n * export class CleanupService {\n * @GraphInterval(5000)\n * pruneStale() { ... }\n * }\n * ```\n */\nexport function GraphInterval(\n\tms: number,\n): (value: DecoratorBoundMethod, context: ClassMethodDecoratorContext) => void {\n\treturn (_value: DecoratorBoundMethod, context: ClassMethodDecoratorContext) => {\n\t\tconst methodKey = context.name;\n\t\tcontext.addInitializer(function (this: unknown) {\n\t\t\tconst ctor = (this as { constructor: DecoratorHostConstructor }).constructor;\n\t\t\tconst existing = INTERVAL_HANDLERS.get(ctor) ?? [];\n\t\t\texisting.push({ ms, methodKey });\n\t\t\tINTERVAL_HANDLERS.set(ctor, existing);\n\t\t});\n\t};\n}\n\n/**\n * Run a method on a cron schedule — replaces `@Cron()` from `@nestjs/schedule`.\n *\n * Backed by a `fromCron` node added to the root graph as `__schedule__.<className>.<methodName>`.\n * Visible in `graph.describe()`, pausable via PAUSE/RESUME signals.\n *\n * @param expr - 5-field cron expression (`min hour dom month dow`).\n *\n * @example\n * ```ts\n * @Injectable()\n * export class ReportService {\n * @GraphCron(\"0 9 * * 1\")\n * weeklyReport() { ... }\n * }\n * ```\n */\nexport function GraphCron(\n\texpr: string,\n): (value: DecoratorBoundMethod, context: ClassMethodDecoratorContext) => void {\n\treturn (_value: DecoratorBoundMethod, context: ClassMethodDecoratorContext) => {\n\t\tconst methodKey = context.name;\n\t\tcontext.addInitializer(function (this: unknown) {\n\t\t\tconst ctor = (this as { constructor: DecoratorHostConstructor }).constructor;\n\t\t\tconst existing = CRON_HANDLERS.get(ctor) ?? [];\n\t\t\texisting.push({ expr, methodKey });\n\t\t\tCRON_HANDLERS.set(ctor, existing);\n\t\t});\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// CQRS method decorators (Phase 5.5 — CQRS replacement)\n// ---------------------------------------------------------------------------\n\n/**\n * Register a method as a CQRS command handler — replaces `@CommandHandler()` from `@nestjs/cqrs`.\n *\n * The method receives `(payload, { emit })` — same signature as `CqrsGraph.command()` handlers.\n * Wired reactively via the explorer on module init.\n *\n * @param cqrsName - Name of the CQRS graph (from `forCqrs({ name })`).\n * @param commandName - Command to handle.\n *\n * @example\n * ```ts\n * @Injectable()\n * export class OrderService {\n * @CommandHandler(\"orders\", \"placeOrder\")\n * handlePlace(payload: PlaceOrderDto, { emit }: CommandActions) {\n * emit(\"orderPlaced\", { orderId: payload.id, amount: payload.amount });\n * }\n * }\n * ```\n */\nexport function CommandHandler(\n\tcqrsName: string,\n\tcommandName: string,\n): (value: DecoratorBoundMethod, context: ClassMethodDecoratorContext) => void {\n\treturn (_value: DecoratorBoundMethod, context: ClassMethodDecoratorContext) => {\n\t\tconst methodKey = context.name;\n\t\tcontext.addInitializer(function (this: unknown) {\n\t\t\tconst ctor = (this as { constructor: DecoratorHostConstructor }).constructor;\n\t\t\tconst existing = COMMAND_HANDLERS.get(ctor) ?? [];\n\t\t\texisting.push({ cqrsName, commandName, methodKey });\n\t\t\tCOMMAND_HANDLERS.set(ctor, existing);\n\t\t});\n\t};\n}\n\n/**\n * Subscribe a method to CQRS event stream DATA — replaces `@EventsHandler()` from `@nestjs/cqrs`.\n *\n * The method receives each `CqrsEvent` envelope as events arrive. Subscription is reactive\n * via `graph.observe()` — actor guards are respected.\n *\n * @param cqrsName - Name of the CQRS graph.\n * @param eventName - Event stream to subscribe to.\n *\n * @example\n * ```ts\n * @Injectable()\n * export class NotificationService {\n * @EventHandler(\"orders\", \"orderPlaced\")\n * onOrderPlaced(event: CqrsEvent<{ orderId: string }>) {\n * console.log(\"Order placed:\", event.payload.orderId);\n * }\n * }\n * ```\n */\nexport function EventHandler(\n\tcqrsName: string,\n\teventName: string,\n): (value: DecoratorBoundMethod, context: ClassMethodDecoratorContext) => void {\n\treturn (_value: DecoratorBoundMethod, context: ClassMethodDecoratorContext) => {\n\t\tconst methodKey = context.name;\n\t\tcontext.addInitializer(function (this: unknown) {\n\t\t\tconst ctor = (this as { constructor: DecoratorHostConstructor }).constructor;\n\t\t\tconst existing = CQRS_EVENT_HANDLERS.get(ctor) ?? [];\n\t\t\texisting.push({ cqrsName, eventName, methodKey });\n\t\t\tCQRS_EVENT_HANDLERS.set(ctor, existing);\n\t\t});\n\t};\n}\n\n/**\n * Subscribe a method to CQRS projection changes — replaces `@QueryHandler()` from `@nestjs/cqrs`.\n *\n * The method is called reactively whenever the projection's value changes (DATA emission).\n * This is push-based, not request-response — the projection recomputes on upstream events.\n *\n * @param cqrsName - Name of the CQRS graph.\n * @param projectionName - Projection to observe.\n *\n * @example\n * ```ts\n * @Injectable()\n * export class DashboardService {\n * @QueryHandler(\"orders\", \"orderCount\")\n * onCountChanged(count: number) {\n * this.broadcast({ type: \"orderCount\", value: count });\n * }\n * }\n * ```\n */\nexport function QueryHandler(\n\tcqrsName: string,\n\tprojectionName: string,\n): (value: DecoratorBoundMethod, context: ClassMethodDecoratorContext) => void {\n\treturn (_value: DecoratorBoundMethod, context: ClassMethodDecoratorContext) => {\n\t\tconst methodKey = context.name;\n\t\tcontext.addInitializer(function (this: unknown) {\n\t\t\tconst ctor = (this as { constructor: DecoratorHostConstructor }).constructor;\n\t\t\tconst existing = QUERY_HANDLERS.get(ctor) ?? [];\n\t\t\texisting.push({ cqrsName, projectionName, methodKey });\n\t\t\tQUERY_HANDLERS.set(ctor, existing);\n\t\t});\n\t};\n}\n\n/**\n * Register a method as a CQRS saga — replaces RxJS saga streams from `@nestjs/cqrs`.\n *\n * The method receives each new `CqrsEvent` from the specified event streams. Backed by\n * `CqrsGraph.saga()` — tracks last-processed entry, only delivers new events.\n *\n * @param cqrsName - Name of the CQRS graph.\n * @param sagaName - Name for this saga node in the graph.\n * @param eventNames - Event streams to react to.\n *\n * @example\n * ```ts\n * @Injectable()\n * export class FulfillmentService {\n * @SagaHandler(\"orders\", \"fulfillment\", [\"orderPlaced\", \"paymentConfirmed\"])\n * onOrderFlow(event: CqrsEvent) {\n * if (event.type === \"paymentConfirmed\") this.shipOrder(event.payload);\n * }\n * }\n * ```\n */\nexport function SagaHandler(\n\tcqrsName: string,\n\tsagaName: string,\n\teventNames: readonly string[],\n): (value: DecoratorBoundMethod, context: ClassMethodDecoratorContext) => void {\n\treturn (_value: DecoratorBoundMethod, context: ClassMethodDecoratorContext) => {\n\t\tconst methodKey = context.name;\n\t\tcontext.addInitializer(function (this: unknown) {\n\t\t\tconst ctor = (this as { constructor: DecoratorHostConstructor }).constructor;\n\t\t\tconst existing = SAGA_HANDLERS.get(ctor) ?? [];\n\t\t\texisting.push({ cqrsName, eventNames, sagaName, methodKey });\n\t\t\tSAGA_HANDLERS.set(ctor, existing);\n\t\t});\n\t};\n}\n","// ---------------------------------------------------------------------------\n// NestJS DI tokens for GraphReFly integration.\n// ---------------------------------------------------------------------------\n\n/** Injection token for the root `Graph` singleton created by `forRoot()`. */\nexport const GRAPHREFLY_ROOT_GRAPH = Symbol.for(\"graphrefly:root-graph\");\n\n/** Injection token for `forRoot()` / `forFeature()` options. */\nexport const GRAPHREFLY_MODULE_OPTIONS = Symbol.for(\"graphrefly:module-options\");\n\n/** Injection token for the request-scoped `Graph` created by request scope config. */\nexport const GRAPHREFLY_REQUEST_GRAPH = Symbol.for(\"graphrefly:request-graph\");\n\n/**\n * Get the injection token for a named feature graph.\n *\n * Feature graphs registered via `GraphReflyModule.forFeature({ name })` are\n * injectable using this token (or via the `@InjectGraph(name)` decorator).\n */\nexport function getGraphToken(name: string): symbol {\n\treturn Symbol.for(`graphrefly:graph:${name}`);\n}\n\n/**\n * Get the injection token for a node at a qualified path.\n *\n * Nodes declared in `forRoot({ nodes })` or `forFeature({ nodes })` are\n * injectable using this token (or via the `@InjectNode(path)` decorator).\n */\nexport function getNodeToken(path: string): symbol {\n\treturn Symbol.for(`graphrefly:node:${path}`);\n}\n","// ---------------------------------------------------------------------------\n// GraphReflyEventExplorer — discovers @OnGraphEvent, @GraphInterval, @GraphCron\n// decorated methods and wires them to the root graph.\n// ---------------------------------------------------------------------------\n// Registered by `forRoot()`. On module init, reads global decorator registries,\n// resolves provider instances via ModuleRef, and creates reactive subscriptions\n// / timer nodes. On module destroy, disposes all subscriptions and removes\n// schedule nodes from the graph.\n//\n// Runtime is fully reactive — push-based via graph.observe().\n// No polling, no microtasks, no promises. Timer nodes use central fromTimer /\n// fromCron primitives.\n// ---------------------------------------------------------------------------\n\nimport { DATA, type Messages } from \"@graphrefly/pure-ts/core\";\nimport { fromTimer } from \"@graphrefly/pure-ts/extra\";\nimport type { Graph, GraphObserveOne } from \"@graphrefly/pure-ts/graph\";\nimport type { OnModuleDestroy, OnModuleInit } from \"@nestjs/common\";\nimport type { ModuleRef } from \"@nestjs/core\";\nimport { fromCron } from \"../../base/sources/event/cron.js\";\nimport type { CqrsGraph } from \"../../utils/cqrs/index.js\";\nimport {\n\tCOMMAND_HANDLERS,\n\ttype CommandHandlerMeta,\n\tCQRS_EVENT_HANDLERS,\n\tCRON_HANDLERS,\n\ttype DecoratorBoundMethod,\n\ttype DecoratorHostConstructor,\n\tEVENT_HANDLERS,\n\ttype EventHandlerMeta,\n\ttype GraphCronMeta,\n\ttype GraphIntervalMeta,\n\tINTERVAL_HANDLERS,\n\ttype OnGraphEventMeta,\n\tQUERY_HANDLERS,\n\ttype QueryHandlerMeta,\n\tSAGA_HANDLERS,\n\ttype SagaHandlerMeta,\n} from \"./decorators.js\";\nimport { getGraphToken } from \"./tokens.js\";\n\n/** Monotonic counter for schedule node name disambiguation. */\nlet scheduleSeq = 0;\n\nexport class GraphReflyEventExplorer implements OnModuleInit, OnModuleDestroy {\n\tprivate readonly disposers: Array<() => void> = [];\n\tprivate readonly scheduleNodeNames: string[] = [];\n\n\tconstructor(\n\t\tprivate readonly graph: Graph,\n\t\tprivate readonly moduleRef: ModuleRef,\n\t) {}\n\n\tonModuleInit(): void {\n\t\tthis.wireEvents();\n\t\tthis.wireIntervals();\n\t\tthis.wireCrons();\n\t\tthis.wireCqrsCommands();\n\t\tthis.wireCqrsEvents();\n\t\tthis.wireCqrsQueries();\n\t\tthis.wireCqrsSagas();\n\t}\n\n\tonModuleDestroy(): void {\n\t\tfor (const dispose of this.disposers) dispose();\n\t\tthis.disposers.length = 0;\n\n\t\tfor (const name of this.scheduleNodeNames) {\n\t\t\ttry {\n\t\t\t\tthis.graph.remove(name);\n\t\t\t} catch {\n\t\t\t\t// Node may already be gone if graph.destroy() ran first.\n\t\t\t}\n\t\t}\n\t\tthis.scheduleNodeNames.length = 0;\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// @OnGraphEvent — reactive subscription via graph.observe()\n\t// -----------------------------------------------------------------------\n\n\tprivate wireEvents(): void {\n\t\tfor (const [ctor, metas] of EVENT_HANDLERS) {\n\t\t\tconst instance = this.resolveInstance(ctor);\n\t\t\tif (!instance) continue;\n\n\t\t\tfor (const meta of metas) {\n\t\t\t\tthis.wireEventHandler(instance, meta);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate wireEventHandler(instance: object, meta: OnGraphEventMeta): void {\n\t\tconst method = (instance as Record<string | symbol, DecoratorBoundMethod>)[meta.methodKey];\n\t\tif (typeof method !== \"function\") return;\n\n\t\tconst bound = method.bind(instance);\n\n\t\t// Route through graph.observe() so actor guards are respected.\n\t\tconst handle = this.observeNode(meta.nodeName);\n\t\tconst unsub = handle.subscribe((msgs: Messages) => {\n\t\t\tfor (const m of msgs) {\n\t\t\t\tif (m[0] === DATA) {\n\t\t\t\t\tbound(m[1]);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tthis.disposers.push(unsub);\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// @GraphInterval — reactive via fromTimer central timer primitive\n\t// -----------------------------------------------------------------------\n\n\tprivate wireIntervals(): void {\n\t\tfor (const [ctor, metas] of INTERVAL_HANDLERS) {\n\t\t\tconst instance = this.resolveInstance(ctor);\n\t\t\tif (!instance) continue;\n\n\t\t\tfor (const meta of metas) {\n\t\t\t\tthis.wireIntervalHandler(instance, ctor, meta);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate wireIntervalHandler(\n\t\tinstance: object,\n\t\tctor: DecoratorHostConstructor,\n\t\tmeta: GraphIntervalMeta,\n\t): void {\n\t\tconst method = (instance as Record<string | symbol, DecoratorBoundMethod>)[meta.methodKey];\n\t\tif (typeof method !== \"function\") return;\n\n\t\tconst bound = method.bind(instance);\n\t\tconst className = ctor.name ?? \"anonymous\";\n\t\tconst nodeName = `__schedule__.${className}.${String(meta.methodKey)}.${scheduleSeq++}`;\n\n\t\tconst timerNode = fromTimer(meta.ms, { period: meta.ms, name: nodeName });\n\t\tthis.graph.add(timerNode);\n\t\tthis.scheduleNodeNames.push(nodeName);\n\n\t\t// Subscribe through graph.observe() for consistency.\n\t\tconst handle = this.observeNode(nodeName);\n\t\tconst unsub = handle.subscribe((msgs: Messages) => {\n\t\t\tfor (const m of msgs) {\n\t\t\t\tif (m[0] === DATA) bound(m[1]);\n\t\t\t}\n\t\t});\n\t\tthis.disposers.push(unsub);\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// @GraphCron — reactive via fromCron central timer primitive\n\t// -----------------------------------------------------------------------\n\n\tprivate wireCrons(): void {\n\t\tfor (const [ctor, metas] of CRON_HANDLERS) {\n\t\t\tconst instance = this.resolveInstance(ctor);\n\t\t\tif (!instance) continue;\n\n\t\t\tfor (const meta of metas) {\n\t\t\t\tthis.wireCronHandler(instance, ctor, meta);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate wireCronHandler(\n\t\tinstance: object,\n\t\tctor: DecoratorHostConstructor,\n\t\tmeta: GraphCronMeta,\n\t): void {\n\t\tconst method = (instance as Record<string | symbol, DecoratorBoundMethod>)[meta.methodKey];\n\t\tif (typeof method !== \"function\") return;\n\n\t\tconst bound = method.bind(instance);\n\t\tconst className = ctor.name ?? \"anonymous\";\n\t\tconst nodeName = `__schedule__.${className}.${String(meta.methodKey)}.${scheduleSeq++}`;\n\n\t\tconst cronNode = fromCron(meta.expr, { name: nodeName });\n\t\tthis.graph.add(cronNode);\n\t\tthis.scheduleNodeNames.push(nodeName);\n\n\t\t// Subscribe through graph.observe() for consistency.\n\t\tconst handle = this.observeNode(nodeName);\n\t\tconst unsub = handle.subscribe((msgs: Messages) => {\n\t\t\tfor (const m of msgs) {\n\t\t\t\tif (m[0] === DATA) bound(m[1]);\n\t\t\t}\n\t\t});\n\t\tthis.disposers.push(unsub);\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// @CommandHandler — register method as CqrsGraph command handler\n\t// -----------------------------------------------------------------------\n\n\tprivate wireCqrsCommands(): void {\n\t\tfor (const [ctor, metas] of COMMAND_HANDLERS) {\n\t\t\tconst instance = this.resolveInstance(ctor);\n\t\t\tif (!instance) continue;\n\n\t\t\tfor (const meta of metas) {\n\t\t\t\tthis.wireCqrsCommand(instance, meta);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate wireCqrsCommand(instance: object, meta: CommandHandlerMeta): void {\n\t\tconst method = (instance as Record<string | symbol, DecoratorBoundMethod>)[meta.methodKey];\n\t\tif (typeof method !== \"function\") return;\n\n\t\tconst bound = method.bind(instance);\n\t\tconst cqrsGraph = this.resolveCqrsGraph(meta.cqrsName);\n\t\tif (!cqrsGraph) return;\n\n\t\tcqrsGraph.command(meta.commandName, bound);\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// @EventHandler — subscribe method to CQRS event stream\n\t// -----------------------------------------------------------------------\n\n\tprivate wireCqrsEvents(): void {\n\t\tfor (const [ctor, metas] of CQRS_EVENT_HANDLERS) {\n\t\t\tconst instance = this.resolveInstance(ctor);\n\t\t\tif (!instance) continue;\n\n\t\t\tfor (const meta of metas) {\n\t\t\t\tthis.wireCqrsEventHandler(instance, meta);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate wireCqrsEventHandler(instance: object, meta: EventHandlerMeta): void {\n\t\tconst method = (instance as Record<string | symbol, DecoratorBoundMethod>)[meta.methodKey];\n\t\tif (typeof method !== \"function\") return;\n\n\t\tconst bound = method.bind(instance);\n\t\tconst cqrsGraph = this.resolveCqrsGraph(meta.cqrsName);\n\t\tif (!cqrsGraph) return;\n\n\t\t// Ensure the event stream exists.\n\t\tcqrsGraph.event(meta.eventName);\n\n\t\t// Snapshot the highest seq already in the log so we only deliver new events.\n\t\t// Tracking by seq (monotonic per-graph) is robust against reactive log trim.\n\t\tconst eventNode = cqrsGraph.resolve(meta.eventName);\n\t\tconst existingEntries = eventNode.cache as readonly { seq: number }[] | undefined;\n\t\tlet lastSeq =\n\t\t\texistingEntries && existingEntries.length > 0\n\t\t\t\t? existingEntries[existingEntries.length - 1].seq\n\t\t\t\t: 0;\n\n\t\t// Subscribe reactively via graph.observe() — respects actor guards.\n\t\tconst handle = this.observeNodeOn(cqrsGraph, meta.eventName);\n\t\tconst unsub = handle.subscribe((msgs: Messages) => {\n\t\t\tfor (const m of msgs) {\n\t\t\t\tif (m[0] === DATA) {\n\t\t\t\t\tconst entries = m[1] as readonly { seq: number }[];\n\t\t\t\t\tfor (const entry of entries) {\n\t\t\t\t\t\tif (entry.seq > lastSeq) {\n\t\t\t\t\t\t\tbound(entry);\n\t\t\t\t\t\t\tlastSeq = entry.seq;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tthis.disposers.push(unsub);\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// @QueryHandler — subscribe method to CQRS projection changes\n\t// -----------------------------------------------------------------------\n\n\tprivate wireCqrsQueries(): void {\n\t\tfor (const [ctor, metas] of QUERY_HANDLERS) {\n\t\t\tconst instance = this.resolveInstance(ctor);\n\t\t\tif (!instance) continue;\n\n\t\t\tfor (const meta of metas) {\n\t\t\t\tthis.wireCqrsQuery(instance, meta);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate wireCqrsQuery(instance: object, meta: QueryHandlerMeta): void {\n\t\tconst method = (instance as Record<string | symbol, DecoratorBoundMethod>)[meta.methodKey];\n\t\tif (typeof method !== \"function\") return;\n\n\t\tconst bound = method.bind(instance);\n\t\tconst cqrsGraph = this.resolveCqrsGraph(meta.cqrsName);\n\t\tif (!cqrsGraph) return;\n\n\t\t// Subscribe reactively to the projection node — push on every DATA.\n\t\tconst handle = this.observeNodeOn(cqrsGraph, meta.projectionName);\n\t\tconst unsub = handle.subscribe((msgs: Messages) => {\n\t\t\tfor (const m of msgs) {\n\t\t\t\tif (m[0] === DATA) {\n\t\t\t\t\tbound(m[1]);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tthis.disposers.push(unsub);\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// @SagaHandler — register method as CqrsGraph saga (subgraph side effect)\n\t// -----------------------------------------------------------------------\n\n\tprivate wireCqrsSagas(): void {\n\t\tfor (const [ctor, metas] of SAGA_HANDLERS) {\n\t\t\tconst instance = this.resolveInstance(ctor);\n\t\t\tif (!instance) continue;\n\n\t\t\tfor (const meta of metas) {\n\t\t\t\tthis.wireCqrsSaga(instance, meta);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate wireCqrsSaga(instance: object, meta: SagaHandlerMeta): void {\n\t\tconst method = (instance as Record<string | symbol, DecoratorBoundMethod>)[meta.methodKey];\n\t\tif (typeof method !== \"function\") return;\n\n\t\tconst bound = method.bind(instance);\n\t\tconst cqrsGraph = this.resolveCqrsGraph(meta.cqrsName);\n\t\tif (!cqrsGraph) return;\n\n\t\tcqrsGraph.saga(meta.sagaName, meta.eventNames, bound);\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// Helpers\n\t// -----------------------------------------------------------------------\n\n\tprivate observeNode(name: string): GraphObserveOne {\n\t\t// Overload resolution picks ObserveResult; cast to the correct single-node type.\n\t\treturn this.graph.observe(name) as unknown as GraphObserveOne;\n\t}\n\n\tprivate observeNodeOn(graph: Graph, name: string): GraphObserveOne {\n\t\treturn graph.observe(name) as unknown as GraphObserveOne;\n\t}\n\n\tprivate resolveCqrsGraph(name: string): CqrsGraph | null {\n\t\ttry {\n\t\t\treturn this.moduleRef.get(getGraphToken(name), { strict: false }) as CqrsGraph;\n\t\t} catch {\n\t\t\tconsole.warn(\n\t\t\t\t`[GraphReFly] CqrsGraph \"${name}\" not found in DI — ` +\n\t\t\t\t\t`did you import GraphReflyModule.forCqrs({ name: \"${name}\" })?`,\n\t\t\t);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate resolveInstance(ctor: DecoratorHostConstructor): object | null {\n\t\ttry {\n\t\t\treturn this.moduleRef.get(ctor, { strict: false });\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n}\n","// ---------------------------------------------------------------------------\n// NestJS Gateway helpers — reactive bridges from graph.observe() to transports.\n// ---------------------------------------------------------------------------\n// All helpers are push-based: they subscribe to `graph.observe()` with actor\n// context and forward DATA messages to the transport. No polling.\n//\n// Actor-scoped observation respects node guards (Phase 1.5). Clients only\n// see DATA values from nodes their Actor is allowed to observe.\n// ---------------------------------------------------------------------------\n\nimport {\n\ttype Actor,\n\tCOMPLETE,\n\tDATA,\n\tERROR,\n\ttype Messages,\n\tTEARDOWN,\n} from \"@graphrefly/pure-ts/core\";\nimport type { Graph, GraphObserveOne } from \"@graphrefly/pure-ts/graph\";\nimport {\n\tcreateWatermarkController,\n\ttype WatermarkController,\n} from \"../../base/composition/backpressure.js\";\n\n// ---------------------------------------------------------------------------\n// Shared types\n// ---------------------------------------------------------------------------\n\n/**\n * Client-to-server commands for the WebSocket observe protocol.\n */\nexport type ObserveWsCommand =\n\t| { type: \"subscribe\"; path: string }\n\t| { type: \"unsubscribe\"; path: string }\n\t| { type: \"ack\"; path: string; count?: number };\n\n/**\n * Server-to-client messages for the WebSocket observe protocol.\n */\nexport type ObserveWsMessage<T = unknown> =\n\t| { type: \"data\"; path: string; value: T }\n\t| { type: \"error\"; path: string; error: string }\n\t| { type: \"complete\"; path: string }\n\t| { type: \"subscribed\"; path: string }\n\t| { type: \"unsubscribed\"; path: string }\n\t| { type: \"err\"; message: string };\n\n// ---------------------------------------------------------------------------\n// observeSSE — graph.observe() → SSE ReadableStream\n// ---------------------------------------------------------------------------\n\nexport type ObserveSSEOptions = {\n\tactor?: Actor;\n\tserialize?: (value: unknown) => string;\n\tkeepAliveMs?: number;\n\tsignal?: AbortSignal;\n\t/** Pending DATA count at which PAUSE is sent upstream. Enables backpressure when set. */\n\thighWaterMark?: number;\n\t/** Pending DATA count at which RESUME is sent upstream. Defaults to `Math.floor(highWaterMark / 2)`. */\n\tlowWaterMark?: number;\n};\n\n/**\n * Creates an SSE `ReadableStream` that streams DATA values from a graph node.\n *\n * Routes through `graph.observe(path, { actor })` so node guards are respected.\n * The stream emits `event: data` for DATA, `event: error` for ERROR, and\n * `event: complete` for COMPLETE (then closes). TEARDOWN also closes the stream.\n *\n * @param graph - The graph to observe.\n * @param path - Qualified node path to observe.\n * @param opts - Actor context, serialization, keep-alive.\n * @returns A `ReadableStream<Uint8Array>` suitable for NestJS SSE endpoints.\n *\n * @example\n * ```ts\n * @Sse(\"events/:path\")\n * streamEvents(@Param(\"path\") path: string, @Req() req: Request) {\n * return observeSSE(this.graph, path, { actor: getActor(req) });\n * }\n * ```\n */\nexport function observeSSE(\n\tgraph: Graph,\n\tpath: string,\n\topts?: ObserveSSEOptions,\n): ReadableStream<Uint8Array> {\n\tconst { actor, serialize = defaultSerialize, keepAliveMs, signal } = opts ?? {};\n\tconst encoder = new TextEncoder();\n\tlet stop: (() => void) | undefined;\n\tconst useBackpressure = opts?.highWaterMark != null;\n\n\tlet wm: WatermarkController | undefined;\n\tlet pullResolve: (() => void) | undefined;\n\n\t// When backpressure is enabled we tag buffered entries so pull() only calls\n\t// onDequeue for DATA frames (not keepalive, ERROR, or COMPLETE frames).\n\ttype BufEntry = { frame: Uint8Array; counted: boolean };\n\tconst taggedBuf: BufEntry[] = [];\n\tlet closed = false;\n\n\treturn new ReadableStream<Uint8Array>({\n\t\tstart(controller) {\n\t\t\tlet keepAlive: ReturnType<typeof setInterval> | undefined;\n\t\t\tlet unsub: () => void = () => {};\n\t\t\tconst close = () => {\n\t\t\t\tif (closed) return;\n\t\t\t\tclosed = true;\n\t\t\t\tif (keepAlive !== undefined) clearInterval(keepAlive);\n\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t// Unsub first to prevent further sink callbacks during dispose.\n\t\t\t\tunsub();\n\t\t\t\twm?.dispose();\n\t\t\t\t// Resolve any parked pull() promise so the stream can finish.\n\t\t\t\tpullResolve?.();\n\t\t\t\tpullResolve = undefined;\n\t\t\t\t// Flush remaining buffered frames before closing.\n\t\t\t\tfor (const entry of taggedBuf) controller.enqueue(entry.frame);\n\t\t\t\ttaggedBuf.length = 0;\n\t\t\t\tcontroller.close();\n\t\t\t};\n\t\t\tstop = close;\n\t\t\tconst onAbort = () => close();\n\n\t\t\tconst handle = graph.observe(path, { actor }) as unknown as GraphObserveOne;\n\n\t\t\tif (useBackpressure) {\n\t\t\t\twm = createWatermarkController((msgs) => handle.up(msgs), {\n\t\t\t\t\thighWaterMark: opts!.highWaterMark!,\n\t\t\t\t\tlowWaterMark: opts!.lowWaterMark ?? Math.floor(opts!.highWaterMark! / 2),\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tunsub = handle.subscribe((msgs: Messages) => {\n\t\t\t\tfor (const msg of msgs) {\n\t\t\t\t\tif (closed) return;\n\t\t\t\t\tconst t = msg[0];\n\t\t\t\t\tif (t === DATA) {\n\t\t\t\t\t\tconst frame = encoder.encode(sseFrame(\"data\", serialize(msg[1])));\n\t\t\t\t\t\tif (useBackpressure) {\n\t\t\t\t\t\t\ttaggedBuf.push({ frame, counted: true });\n\t\t\t\t\t\t\twm!.onEnqueue();\n\t\t\t\t\t\t\tpullResolve?.();\n\t\t\t\t\t\t\tpullResolve = undefined;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tcontroller.enqueue(frame);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (t === ERROR) {\n\t\t\t\t\t\tconst frame = encoder.encode(sseFrame(\"error\", serialize(msg[1])));\n\t\t\t\t\t\tif (useBackpressure) {\n\t\t\t\t\t\t\ttaggedBuf.push({ frame, counted: false });\n\t\t\t\t\t\t\tpullResolve?.();\n\t\t\t\t\t\t\tpullResolve = undefined;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tcontroller.enqueue(frame);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tclose();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t} else if (t === COMPLETE || t === TEARDOWN) {\n\t\t\t\t\t\tif (t === COMPLETE) {\n\t\t\t\t\t\t\tconst frame = encoder.encode(sseFrame(\"complete\"));\n\t\t\t\t\t\t\tif (useBackpressure) {\n\t\t\t\t\t\t\t\ttaggedBuf.push({ frame, counted: false });\n\t\t\t\t\t\t\t\tpullResolve?.();\n\t\t\t\t\t\t\t\tpullResolve = undefined;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tcontroller.enqueue(frame);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tclose();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\t// DIRTY, RESOLVED, and other protocol internals are not exposed to SSE clients\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tif (keepAliveMs !== undefined && keepAliveMs > 0) {\n\t\t\t\tkeepAlive = setInterval(() => {\n\t\t\t\t\tif (closed) return;\n\t\t\t\t\tif (useBackpressure) {\n\t\t\t\t\t\t// Keepalive frames bypass watermark accounting entirely.\n\t\t\t\t\t\ttaggedBuf.push({ frame: encoder.encode(\": keepalive\\n\\n\"), counted: false });\n\t\t\t\t\t\tpullResolve?.();\n\t\t\t\t\t\tpullResolve = undefined;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcontroller.enqueue(encoder.encode(\": keepalive\\n\\n\"));\n\t\t\t\t\t}\n\t\t\t\t}, keepAliveMs);\n\t\t\t}\n\t\t\tif (signal?.aborted) onAbort();\n\t\t\telse signal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\t},\n\t\tpull(controller) {\n\t\t\tif (!useBackpressure) return;\n\t\t\tif (closed) return;\n\t\t\tif (taggedBuf.length > 0) {\n\t\t\t\tconst entry = taggedBuf.shift()!;\n\t\t\t\tcontroller.enqueue(entry.frame);\n\t\t\t\tif (entry.counted) wm!.onDequeue();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// No data available — park until the sink callback pushes more.\n\t\t\treturn new Promise<void>((resolve) => {\n\t\t\t\tpullResolve = resolve;\n\t\t\t});\n\t\t},\n\t\tcancel() {\n\t\t\t// Guard against double-close (cancel may fire after COMPLETE/ERROR already closed).\n\t\t\ttry {\n\t\t\t\tstop?.();\n\t\t\t} catch {\n\t\t\t\t/* already closed */\n\t\t\t}\n\t\t},\n\t});\n}\n\n// ---------------------------------------------------------------------------\n// observeSubscription — graph.observe() → AsyncIterableIterator (GraphQL)\n// ---------------------------------------------------------------------------\n\nexport type ObserveSubscriptionOptions<T = unknown> = {\n\tactor?: Actor;\n\t/**\n\t * Optional value filter. Only matching DATA values are enqueued.\n\t *\n\t * **Note:** `filter` and `highWaterMark` are semantically decoupled — the\n\t * watermark counts items that pass the filter, not total upstream work.\n\t * If the filter rejects most items, backpressure may never engage despite\n\t * high upstream throughput. For upstream-level resource protection, place a\n\t * filtering derived node in the graph before the observe point instead.\n\t */\n\tfilter?: (value: T) => boolean;\n\t/** Pending DATA count at which PAUSE is sent upstream. Enables backpressure when set. */\n\thighWaterMark?: number;\n\t/** Pending DATA count at which RESUME is sent upstream. Defaults to `Math.floor(highWaterMark / 2)`. */\n\tlowWaterMark?: number;\n};\n\n/**\n * Creates an `AsyncIterableIterator` that yields DATA values from a graph node.\n *\n * Designed for GraphQL subscription resolvers (Apollo, Mercurius, etc.).\n * Routes through `graph.observe(path, { actor })` for guard-scoped access.\n *\n * The iterator completes on COMPLETE/TEARDOWN and throws on ERROR.\n *\n * @param graph - The graph to observe.\n * @param path - Qualified node path to observe.\n * @param opts - Actor context, optional value filter.\n * @returns An async iterable that yields DATA payloads.\n *\n * @example\n * ```ts\n * // Apollo-style resolver\n * Subscription: {\n * orderStatus: {\n * subscribe: (_parent, args, ctx) =>\n * observeSubscription(ctx.graph, `orders::${args.id}::status`, {\n * actor: ctx.actor,\n * }),\n * },\n * }\n * ```\n */\nexport function observeSubscription<T = unknown>(\n\tgraph: Graph,\n\tpath: string,\n\topts?: ObserveSubscriptionOptions<T>,\n): AsyncIterableIterator<T> {\n\tconst { actor, filter } = opts ?? {};\n\n\ttype QueueItem = { done: false; value: T } | { done: true; value?: undefined; error?: Error };\n\n\tconst queue: QueueItem[] = [];\n\tconst waiters: Array<{\n\t\tresolve: (result: IteratorResult<T>) => void;\n\t\treject: (err: unknown) => void;\n\t}> = [];\n\tlet disposed = false;\n\n\tconst handle = graph.observe(path, { actor }) as unknown as GraphObserveOne;\n\n\tconst wm =\n\t\topts?.highWaterMark != null\n\t\t\t? createWatermarkController((msgs) => handle.up(msgs), {\n\t\t\t\t\thighWaterMark: opts.highWaterMark,\n\t\t\t\t\tlowWaterMark: opts.lowWaterMark ?? Math.floor(opts.highWaterMark / 2),\n\t\t\t\t})\n\t\t\t: undefined;\n\n\tconst dispose = () => {\n\t\tif (disposed) return;\n\t\tdisposed = true;\n\t\twm?.dispose();\n\t\tunsub();\n\t};\n\n\tconst push = (item: QueueItem) => {\n\t\tif (disposed) return;\n\t\tif (waiters.length > 0) {\n\t\t\tconst w = waiters.shift()!;\n\t\t\tif (item.done && item.error) w.reject(item.error);\n\t\t\telse if (item.done) w.resolve({ done: true, value: undefined });\n\t\t\telse w.resolve({ done: false, value: item.value as T });\n\t\t\t// Direct handoff to waiter — no queue growth, no watermark increment.\n\t\t} else {\n\t\t\tqueue.push(item);\n\t\t\tif (!item.done) wm?.onEnqueue();\n\t\t}\n\t};\n\n\tconst unsub = handle.subscribe((msgs: Messages) => {\n\t\tfor (const msg of msgs) {\n\t\t\tconst t = msg[0];\n\t\t\tif (t === DATA) {\n\t\t\t\tconst value = msg[1] as T;\n\t\t\t\tif (filter && !filter(value)) continue;\n\t\t\t\tpush({ done: false, value });\n\t\t\t} else if (t === ERROR) {\n\t\t\t\tconst err = msg[1] instanceof Error ? msg[1] : new Error(String(msg[1]));\n\t\t\t\tpush({ done: true, error: err });\n\t\t\t\tdispose();\n\t\t\t\treturn;\n\t\t\t} else if (t === COMPLETE || t === TEARDOWN) {\n\t\t\t\tpush({ done: true });\n\t\t\t\tdispose();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t});\n\n\tconst iterator: AsyncIterableIterator<T> = {\n\t\tnext(): Promise<IteratorResult<T>> {\n\t\t\tif (queue.length > 0) {\n\t\t\t\tconst item = queue.shift()!;\n\t\t\t\tif (!item.done) wm?.onDequeue();\n\t\t\t\tif (item.done && item.error) return Promise.reject(item.error);\n\t\t\t\treturn Promise.resolve(\n\t\t\t\t\titem.done ? { done: true, value: undefined } : { done: false, value: item.value as T },\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (disposed) return Promise.resolve({ done: true, value: undefined });\n\t\t\treturn new Promise<IteratorResult<T>>((resolve, reject) => {\n\t\t\t\twaiters.push({ resolve, reject });\n\t\t\t});\n\t\t},\n\t\treturn(): Promise<IteratorReturnResult<undefined>> {\n\t\t\tdispose();\n\t\t\t// Resolve any pending waiters\n\t\t\tfor (const w of waiters) w.resolve({ done: true, value: undefined });\n\t\t\twaiters.length = 0;\n\t\t\treturn Promise.resolve({ done: true, value: undefined });\n\t\t},\n\t\tthrow(err: unknown): Promise<IteratorResult<T>> {\n\t\t\tdispose();\n\t\t\treturn Promise.reject(err);\n\t\t},\n\t\t[Symbol.asyncIterator]() {\n\t\t\treturn this;\n\t\t},\n\t};\n\n\treturn iterator;\n}\n\n// ---------------------------------------------------------------------------\n// ObserveGateway — graph.observe() → WebSocket (multi-path subscription)\n// ---------------------------------------------------------------------------\n\nexport type ObserveGatewayOptions = {\n\textractActor?: (client: unknown) => Actor | undefined;\n\tparse?: (data: string) => ObserveWsCommand;\n\t/** Pending DATA count per subscription at which PAUSE is sent upstream. Enables backpressure when set. */\n\thighWaterMark?: number;\n\t/** Pending DATA count per subscription at which RESUME is sent upstream. Defaults to `Math.floor(highWaterMark / 2)`. */\n\tlowWaterMark?: number;\n};\n\n/**\n * Manages per-client WebSocket subscriptions to graph nodes via `observe()`.\n *\n * Not a NestJS decorator or base class — a standalone helper that can be\n * wired into any WebSocket gateway. Each client can subscribe/unsubscribe\n * to individual node paths. Actor-scoped observation respects node guards.\n *\n * @example\n * ```ts\n * @WebSocketGateway()\n * export class GraphGateway {\n * private gw = new ObserveGateway(this.graph);\n *\n * constructor(@InjectGraph() private graph: Graph) {}\n *\n * handleConnection(client: WebSocket) {\n * this.gw.handleConnection(client);\n * }\n *\n * handleDisconnect(client: WebSocket) {\n * this.gw.handleDisconnect(client);\n * }\n *\n * @SubscribeMessage(\"observe\")\n * onObserve(client: WebSocket, data: unknown) {\n * this.gw.handleMessage(client, data);\n * }\n * }\n * ```\n */\nexport class ObserveGateway {\n\tprivate readonly clients = new Map<\n\t\tunknown,\n\t\tMap<string, { unsub: () => void; wm?: WatermarkController }>\n\t>();\n\tprivate readonly extractActor: (client: unknown) => Actor | undefined;\n\tprivate readonly parse: (data: string) => ObserveWsCommand;\n\tprivate readonly highWaterMark: number | undefined;\n\tprivate readonly lowWaterMark: number | undefined;\n\n\tconstructor(\n\t\tprivate readonly graph: Graph,\n\t\topts?: ObserveGatewayOptions,\n\t) {\n\t\tthis.extractActor = opts?.extractActor ?? (() => undefined);\n\t\tthis.parse = opts?.parse ?? defaultParseCommand;\n\t\tthis.highWaterMark = opts?.highWaterMark;\n\t\tthis.lowWaterMark = opts?.lowWaterMark;\n\t}\n\n\t/**\n\t * Register a new client. Call from `handleConnection`.\n\t */\n\thandleConnection(client: unknown): void {\n\t\tif (!this.clients.has(client)) {\n\t\t\tthis.clients.set(client, new Map());\n\t\t}\n\t}\n\n\t/**\n\t * Unregister a client and dispose all its subscriptions. Call from `handleDisconnect`.\n\t */\n\thandleDisconnect(client: unknown): void {\n\t\tconst subs = this.clients.get(client);\n\t\tif (!subs) return;\n\t\tfor (const entry of subs.values()) {\n\t\t\tentry.wm?.dispose();\n\t\t\tentry.unsub();\n\t\t}\n\t\tthis.clients.delete(client);\n\t}\n\n\t/**\n\t * Handle an incoming client message (subscribe/unsubscribe/ack command).\n\t *\n\t * @param client - The WebSocket client reference.\n\t * @param raw - Raw message data (string or parsed object).\n\t * @param send - Function to send a message back to the client.\n\t * Defaults to `client.send(JSON.stringify(msg))`.\n\t */\n\thandleMessage(client: unknown, raw: unknown, send?: (msg: ObserveWsMessage) => void): void {\n\t\tconst sender = send ?? defaultSend.bind(null, client);\n\t\tlet cmd: ObserveWsCommand;\n\t\ttry {\n\t\t\tcmd = typeof raw === \"string\" ? this.parse(raw) : (raw as ObserveWsCommand);\n\t\t} catch {\n\t\t\tsender({ type: \"err\", message: \"invalid command\" });\n\t\t\treturn;\n\t\t}\n\n\t\tif (cmd.type === \"subscribe\") {\n\t\t\tthis.subscribe(client, cmd.path, sender);\n\t\t} else if (cmd.type === \"unsubscribe\") {\n\t\t\tthis.unsubscribe(client, cmd.path, sender);\n\t\t} else if (cmd.type === \"ack\") {\n\t\t\tthis.ack(client, cmd.path, cmd.count ?? 1);\n\t\t} else {\n\t\t\tsender({ type: \"err\", message: `unknown command type: ${(cmd as { type: string }).type}` });\n\t\t}\n\t}\n\n\t/**\n\t * Number of active subscriptions for a client. Useful for tests.\n\t */\n\tsubscriptionCount(client: unknown): number {\n\t\treturn this.clients.get(client)?.size ?? 0;\n\t}\n\n\t/**\n\t * Dispose all clients and subscriptions.\n\t */\n\tdestroy(): void {\n\t\tfor (const [client] of this.clients) {\n\t\t\tthis.handleDisconnect(client);\n\t\t}\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// Internal\n\t// -----------------------------------------------------------------------\n\n\tprivate subscribe(client: unknown, path: string, send: (msg: ObserveWsMessage) => void): void {\n\t\tlet subs = this.clients.get(client);\n\t\tif (!subs) {\n\t\t\tsubs = new Map();\n\t\t\tthis.clients.set(client, subs);\n\t\t}\n\t\tif (subs.has(path)) {\n\t\t\tsend({ type: \"subscribed\", path });\n\t\t\treturn;\n\t\t}\n\n\t\tconst actor = this.extractActor(client);\n\t\tlet handle: GraphObserveOne;\n\t\ttry {\n\t\t\thandle = this.graph.observe(path, { actor }) as unknown as GraphObserveOne;\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\tsend({ type: \"err\", message });\n\t\t\treturn;\n\t\t}\n\n\t\tconst wm =\n\t\t\tthis.highWaterMark != null\n\t\t\t\t? createWatermarkController((msgs) => handle.up(msgs), {\n\t\t\t\t\t\thighWaterMark: this.highWaterMark,\n\t\t\t\t\t\tlowWaterMark: this.lowWaterMark ?? Math.floor(this.highWaterMark / 2),\n\t\t\t\t\t})\n\t\t\t\t: undefined;\n\n\t\tconst cleanup = () => {\n\t\t\twm?.dispose();\n\t\t\tunsub();\n\t\t\tsubs!.delete(path);\n\t\t};\n\n\t\tconst unsub = handle.subscribe((msgs: Messages) => {\n\t\t\tfor (const msg of msgs) {\n\t\t\t\tconst t = msg[0];\n\t\t\t\tif (t === DATA) {\n\t\t\t\t\twm?.onEnqueue();\n\t\t\t\t\ttrySend(send, { type: \"data\", path, value: msg[1] });\n\t\t\t\t} else if (t === ERROR) {\n\t\t\t\t\tconst errMsg = msg[1] instanceof Error ? msg[1].message : String(msg[1]);\n\t\t\t\t\ttrySend(send, { type: \"error\", path, error: errMsg });\n\t\t\t\t\tcleanup();\n\t\t\t\t\treturn;\n\t\t\t\t} else if (t === COMPLETE || t === TEARDOWN) {\n\t\t\t\t\ttrySend(send, { type: \"complete\", path });\n\t\t\t\t\tcleanup();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// DIRTY, RESOLVED not exposed to WS clients\n\t\t\t}\n\t\t});\n\n\t\tsubs.set(path, { unsub, wm });\n\t\tsend({ type: \"subscribed\", path });\n\t}\n\n\tprivate unsubscribe(client: unknown, path: string, send: (msg: ObserveWsMessage) => void): void {\n\t\tconst subs = this.clients.get(client);\n\t\tconst entry = subs?.get(path);\n\t\tif (entry) {\n\t\t\tentry.wm?.dispose();\n\t\t\tentry.unsub();\n\t\t\tsubs!.delete(path);\n\t\t}\n\t\tsend({ type: \"unsubscribed\", path });\n\t}\n\n\tprivate ack(client: unknown, path: string, count: number): void {\n\t\tconst entry = this.clients.get(client)?.get(path);\n\t\tif (!entry?.wm) return;\n\t\tconst n = Math.min(Math.max(0, Math.floor(count)), 1024);\n\t\tfor (let i = 0; i < n; i++) entry.wm.onDequeue();\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction defaultSerialize(value: unknown): string {\n\tif (value instanceof Error) return value.message;\n\ttry {\n\t\treturn JSON.stringify(value);\n\t} catch {\n\t\treturn String(value);\n\t}\n}\n\nfunction sseFrame(event: string, data?: string): string {\n\tlet frame = `event: ${event}\\n`;\n\tif (data !== undefined) {\n\t\tfor (const line of data.split(\"\\n\")) {\n\t\t\tframe += `data: ${line}\\n`;\n\t\t}\n\t}\n\tframe += \"\\n\";\n\treturn frame;\n}\n\nfunction defaultParseCommand(data: string): ObserveWsCommand {\n\treturn JSON.parse(data) as ObserveWsCommand;\n}\n\nfunction defaultSend(client: unknown, msg: ObserveWsMessage): void {\n\ttry {\n\t\t(client as { send: (data: string) => void }).send(JSON.stringify(msg));\n\t} catch {\n\t\t/* client may have disconnected — swallow transport errors */\n\t}\n}\n\nfunction trySend(send: (msg: ObserveWsMessage) => void, msg: ObserveWsMessage): void {\n\ttry {\n\t\tsend(msg);\n\t} catch {\n\t\t/* transport error — client may have disconnected */\n\t}\n}\n","// ---------------------------------------------------------------------------\n// NestJS Actor bridge — maps NestJS ExecutionContext to GraphReFly Actor.\n// ---------------------------------------------------------------------------\n// Implements the NestJS `CanActivate` interface to extract an `Actor` from the\n// request (JWT payload, session, custom header, etc.) and attach it to the\n// request object for downstream graph operations.\n//\n// The decorator does NOT enforce access control — it merely bridges the NestJS\n// authentication context to GraphReFly's ABAC model. Actual access control\n// flows through node `policy()` guards reactively.\n// ---------------------------------------------------------------------------\n\nimport { type Actor, DEFAULT_ACTOR, normalizeActor } from \"@graphrefly/pure-ts/core\";\nimport type { CanActivate, ExecutionContext } from \"@nestjs/common\";\n\n/**\n * Property name under which the extracted {@link Actor} is stored on the\n * request object. Downstream code (controllers, gateways) reads\n * `req[ACTOR_KEY]` to pass actor context to graph operations.\n */\nexport const ACTOR_KEY = \"graphReflyActor\" as const;\n\n/**\n * Extracts a GraphReFly {@link Actor} from a NestJS {@link ExecutionContext}.\n *\n * Return `undefined` to fall back to {@link DEFAULT_ACTOR}.\n */\nexport type ActorExtractor = (context: ExecutionContext) => Actor | undefined;\n\n/**\n * Creates an {@link ActorExtractor} that reads a JWT payload from `req.user`\n * (the standard Passport.js location) and maps it to a GraphReFly {@link Actor}.\n *\n * @param mapping - Optional transform from the JWT payload to an Actor.\n * When omitted, the payload is used directly (must have `type` and `id`).\n *\n * @example\n * ```ts\n * // Default: req.user is already { type, id, ... }\n * GraphReflyGuard(fromJwtPayload())\n *\n * // Custom mapping from your JWT claims\n * GraphReflyGuard(fromJwtPayload((payload) => ({\n * type: payload.role === \"admin\" ? \"human\" : \"llm\",\n * id: payload.sub,\n * org: payload.org_id,\n * })))\n * ```\n */\nexport function fromJwtPayload(mapping?: (payload: unknown) => Actor): ActorExtractor {\n\treturn (context: ExecutionContext): Actor | undefined => {\n\t\tconst req = context.switchToHttp().getRequest();\n\t\tconst user = req?.user;\n\t\tif (user == null) return undefined;\n\t\tif (mapping) return mapping(user);\n\t\treturn user as Actor;\n\t};\n}\n\n/**\n * Creates an {@link ActorExtractor} that reads an Actor from a request header.\n *\n * The header value is parsed as JSON. Useful for service-to-service calls\n * where the caller embeds actor context in a custom header.\n *\n * @param headerName - HTTP header name (case-insensitive). Default: `\"x-graphrefly-actor\"`.\n *\n * @example\n * ```ts\n * GraphReflyGuard(fromHeader(\"x-actor\"))\n * ```\n */\nexport function fromHeader(headerName = \"x-graphrefly-actor\"): ActorExtractor {\n\treturn (context: ExecutionContext): Actor | undefined => {\n\t\tconst req = context.switchToHttp().getRequest();\n\t\tconst raw = req?.headers?.[headerName.toLowerCase()];\n\t\tif (typeof raw !== \"string\" || raw.length === 0) return undefined;\n\t\ttry {\n\t\t\treturn JSON.parse(raw) as Actor;\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t};\n}\n\n/**\n * Reads the extracted {@link Actor} from a request object (set by {@link GraphReflyGuardImpl}).\n *\n * Returns {@link DEFAULT_ACTOR} if no actor was attached.\n *\n * @example\n * ```ts\n * @Get(\"status\")\n * getStatus(@Req() req: Request) {\n * const actor = getActor(req);\n * return this.graph.describe({ actor });\n * }\n * ```\n */\nexport function getActor(req: unknown): Actor {\n\tconst actor = (req as Record<string, unknown>)?.[ACTOR_KEY];\n\treturn actor != null ? normalizeActor(actor as Actor) : DEFAULT_ACTOR;\n}\n\n/**\n * NestJS guard that extracts a GraphReFly {@link Actor} from the execution\n * context and attaches it to the request as `req.graphReflyActor`.\n *\n * This guard always returns `true` (allows the request through). Access\n * control is handled by GraphReFly node guards (`policy()`), not by this\n * NestJS guard. The purpose is purely to **bridge** authentication context.\n *\n * @example\n * ```ts\n * // Global guard — every request gets an Actor\n * app.useGlobalGuards(new GraphReflyGuardImpl(fromJwtPayload()));\n *\n * // Controller-scoped\n * @UseGuards(GraphReflyGuard(fromJwtPayload()))\n * @Controller(\"api\")\n * export class ApiController { ... }\n * ```\n */\nexport class GraphReflyGuardImpl implements CanActivate {\n\tconstructor(private readonly extractor: ActorExtractor) {}\n\n\tcanActivate(context: ExecutionContext): boolean {\n\t\tconst actor = normalizeActor(this.extractor(context));\n\t\tconst req = context.switchToHttp().getRequest();\n\t\tif (req != null) {\n\t\t\t(req as Record<string, unknown>)[ACTOR_KEY] = actor;\n\t\t}\n\t\treturn true;\n\t}\n}\n\n/**\n * Factory that creates a {@link GraphReflyGuardImpl} instance. Use with\n * NestJS `@UseGuards()` or `app.useGlobalGuards()`.\n *\n * @param extractor - How to extract an Actor from the request context.\n * Defaults to {@link fromJwtPayload} (reads `req.user`).\n *\n * @example\n * ```ts\n * import { GraphReflyGuard, fromJwtPayload } from \"@graphrefly/graphrefly-ts/compat/nestjs\";\n *\n * @UseGuards(GraphReflyGuard())\n * @Controller(\"graph\")\n * export class GraphController { ... }\n * ```\n */\nexport function GraphReflyGuard(extractor?: ActorExtractor): GraphReflyGuardImpl {\n\treturn new GraphReflyGuardImpl(extractor ?? fromJwtPayload());\n}\n","// ---------------------------------------------------------------------------\n// GraphReflyModule — NestJS dynamic module for GraphReFly integration.\n// ---------------------------------------------------------------------------\n// Provides `forRoot()` and `forFeature()` following the standard NestJS\n// dynamic-module pattern. Lifecycle hooks wire graph creation on init and\n// `graph.destroy()` on teardown — TEARDOWN propagates through the graph\n// per GRAPHREFLY-SPEC §3.7.\n//\n// No decorator usage in this file — all DI is done via factory providers\n// so the library doesn't require `experimentalDecorators` in consumer's\n// tsconfig (only the consumer's NestJS app needs it).\n// ---------------------------------------------------------------------------\n\nimport type { AppendLogStorageTier } from \"@graphrefly/pure-ts/extra\";\nimport { Graph, type GraphPersistSnapshot } from \"@graphrefly/pure-ts/graph\";\nimport {\n\ttype DynamicModule,\n\tModule,\n\ttype OnModuleDestroy,\n\ttype Provider,\n\tScope,\n} from \"@nestjs/common\";\nimport { ModuleRef } from \"@nestjs/core\";\nimport { type CqrsGraph, type CqrsOptions, cqrs } from \"../../utils/cqrs/index.js\";\nimport { GraphReflyEventExplorer } from \"./explorer.js\";\nimport {\n\tGRAPHREFLY_REQUEST_GRAPH,\n\tGRAPHREFLY_ROOT_GRAPH,\n\tgetGraphToken,\n\tgetNodeToken,\n} from \"./tokens.js\";\n\n// ---------------------------------------------------------------------------\n// Option types\n// ---------------------------------------------------------------------------\n\nexport interface GraphReflyRootOptions {\n\t/** Root graph name (default: `\"root\"`). */\n\tname?: string;\n\t/** Snapshot to hydrate via `graph.restore()` after build. */\n\tsnapshot?: GraphPersistSnapshot;\n\t/** Build callback — registers nodes/mounts on the graph. */\n\tbuild?: (graph: Graph) => void;\n\t/** Qualified node paths to expose as injectable providers. */\n\tnodes?: readonly string[];\n\t/** Enable a request-scoped graph (injectable via `@InjectGraph(\"request\")`). */\n\trequestScope?: boolean;\n}\n\nexport interface GraphReflyCqrsOptions {\n\t/** Feature name — becomes the mount name in the root graph. */\n\tname: string;\n\t/** CQRS graph options (forwarded to `cqrs()` factory). */\n\tcqrs?: CqrsOptions;\n\t/** Build callback — registers commands, events, projections, sagas on the CqrsGraph. */\n\tbuild?: (graph: CqrsGraph) => void;\n\t/** Append-log storage tiers for event persistence (wired via `attachEventStorage()`). */\n\teventStorage?: readonly AppendLogStorageTier<import(\"../../utils/cqrs/index.js\").CqrsEvent>[];\n\t/**\n\t * Node paths (local to this feature) to expose as injectable providers.\n\t * Tokens are auto-qualified as `featureName::path`.\n\t */\n\tnodes?: readonly string[];\n}\n\nexport interface GraphReflyFeatureOptions {\n\t/** Feature name — becomes the mount name in the root graph. */\n\tname: string;\n\t/** Build callback — registers nodes/mounts on the feature graph. */\n\tbuild?: (graph: Graph) => void;\n\t/** Snapshot to hydrate after build. */\n\tsnapshot?: GraphPersistSnapshot;\n\t/**\n\t * Node paths (local to this feature) to expose as injectable providers.\n\t * Tokens are auto-qualified as `featureName::path` to avoid collisions.\n\t */\n\tnodes?: readonly string[];\n}\n\n// ---------------------------------------------------------------------------\n// Lifecycle classes (no decorators — DI is handled via factory providers)\n// ---------------------------------------------------------------------------\n\nclass GraphReflyRootLifecycle implements OnModuleDestroy {\n\tconstructor(readonly graph: Graph) {}\n\n\tonModuleDestroy(): void {\n\t\tthis.graph.destroy();\n\t}\n}\n\nclass GraphReflyRequestLifecycle implements OnModuleDestroy {\n\treadonly graph = new Graph(\"request\");\n\n\tonModuleDestroy(): void {\n\t\tthis.graph.destroy();\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Module\n// ---------------------------------------------------------------------------\n\n// NestJS dynamic modules convention: static `forRoot` / `forFeature` factories on a `@Module` class.\n@Module({})\n// biome-ignore lint/complexity/noStaticOnlyClass: NestJS `DynamicModule` pattern (`@Module` + static factories)\nexport class GraphReflyModule {\n\t/**\n\t * Register the root `Graph` singleton in the NestJS DI container.\n\t *\n\t * The root graph is `@Global()` — injectable everywhere without importing\n\t * the module again. Use `@InjectGraph()` to inject it.\n\t *\n\t * Lifecycle:\n\t * - **init:** Graph created in factory. If `build` is provided, it runs\n\t * first (registers nodes/mounts). If `snapshot` is provided, values\n\t * are restored via `graph.restore()`.\n\t * - **destroy:** Calls `graph.destroy()` — sends `[[TEARDOWN]]` to all\n\t * nodes, including mounted feature subgraphs (cascading teardown).\n\t */\n\tstatic forRoot(opts?: GraphReflyRootOptions): DynamicModule {\n\t\tconst options = opts ?? {};\n\t\tconst graphName = options.name ?? \"root\";\n\n\t\tconst providers: Provider[] = [\n\t\t\t{\n\t\t\t\tprovide: GRAPHREFLY_ROOT_GRAPH,\n\t\t\t\tuseFactory: () => {\n\t\t\t\t\tconst g = new Graph(graphName);\n\t\t\t\t\tif (options.build) options.build(g);\n\t\t\t\t\tif (options.snapshot) g.restore(options.snapshot);\n\t\t\t\t\treturn g;\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tprovide: Symbol.for(\"graphrefly:root-lifecycle\"),\n\t\t\t\tuseFactory: (graph: Graph) => new GraphReflyRootLifecycle(graph),\n\t\t\t\tinject: [GRAPHREFLY_ROOT_GRAPH],\n\t\t\t},\n\t\t\t{\n\t\t\t\tprovide: GraphReflyEventExplorer,\n\t\t\t\tuseFactory: (graph: Graph, moduleRef: InstanceType<typeof ModuleRef>) =>\n\t\t\t\t\tnew GraphReflyEventExplorer(graph, moduleRef),\n\t\t\t\tinject: [GRAPHREFLY_ROOT_GRAPH, ModuleRef],\n\t\t\t},\n\t\t];\n\n\t\t// Node factory providers — each declared path gets a factory that\n\t\t// resolves the node from the root graph at injection time.\n\t\tif (options.nodes) {\n\t\t\tfor (const path of options.nodes) {\n\t\t\t\tproviders.push({\n\t\t\t\t\tprovide: getNodeToken(path),\n\t\t\t\t\tuseFactory: (graph: Graph) => graph.resolve(path),\n\t\t\t\t\tinject: [GRAPHREFLY_ROOT_GRAPH],\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\t// Request-scoped graph provider (opt-in).\n\t\tif (options.requestScope) {\n\t\t\tproviders.push(\n\t\t\t\t{\n\t\t\t\t\tprovide: Symbol.for(\"graphrefly:request-lifecycle\"),\n\t\t\t\t\tuseFactory: () => new GraphReflyRequestLifecycle(),\n\t\t\t\t\tscope: Scope.REQUEST,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tprovide: GRAPHREFLY_REQUEST_GRAPH,\n\t\t\t\t\tuseFactory: (lifecycle: GraphReflyRequestLifecycle) => lifecycle.graph,\n\t\t\t\t\tinject: [Symbol.for(\"graphrefly:request-lifecycle\")],\n\t\t\t\t\tscope: Scope.REQUEST,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\tmodule: GraphReflyModule,\n\t\t\tglobal: true,\n\t\t\tproviders,\n\t\t\texports: [\n\t\t\t\tGRAPHREFLY_ROOT_GRAPH,\n\t\t\t\t...(options.nodes ?? []).map(getNodeToken),\n\t\t\t\t...(options.requestScope ? [GRAPHREFLY_REQUEST_GRAPH] : []),\n\t\t\t],\n\t\t};\n\t}\n\n\t/**\n\t * Register a feature subgraph that auto-mounts into the root graph.\n\t *\n\t * The feature graph is created in the factory, built/restored, then\n\t * mounted into root via `root.mount(name, featureGraph)`. On app\n\t * shutdown, root's `graph.destroy()` cascades TEARDOWN through all\n\t * mounted subgraphs (no explicit remove needed).\n\t *\n\t * Node tokens are auto-qualified as `featureName::path` to prevent\n\t * collisions between features declaring nodes with the same local name.\n\t *\n\t * Injectable via `@InjectGraph(name)`.\n\t */\n\tstatic forFeature(opts: GraphReflyFeatureOptions): DynamicModule {\n\t\tconst providers: Provider[] = [\n\t\t\t{\n\t\t\t\tprovide: getGraphToken(opts.name),\n\t\t\t\tuseFactory: (rootGraph: Graph) => {\n\t\t\t\t\tconst g = new Graph(opts.name);\n\t\t\t\t\tif (opts.build) opts.build(g);\n\t\t\t\t\tif (opts.snapshot) g.restore(opts.snapshot);\n\t\t\t\t\trootGraph.mount(opts.name, g);\n\t\t\t\t\treturn g;\n\t\t\t\t},\n\t\t\t\tinject: [GRAPHREFLY_ROOT_GRAPH],\n\t\t\t},\n\t\t];\n\n\t\t// Node factory providers for feature-scoped nodes.\n\t\t// Tokens are qualified as `featureName::path` to avoid cross-feature collisions.\n\t\tif (opts.nodes) {\n\t\t\tfor (const path of opts.nodes) {\n\t\t\t\tproviders.push({\n\t\t\t\t\tprovide: getNodeToken(`${opts.name}::${path}`),\n\t\t\t\t\tuseFactory: (graph: Graph) => graph.resolve(path),\n\t\t\t\t\tinject: [getGraphToken(opts.name)],\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tmodule: GraphReflyModule,\n\t\t\tproviders,\n\t\t\texports: [\n\t\t\t\tgetGraphToken(opts.name),\n\t\t\t\t...(opts.nodes ?? []).map((p) => getNodeToken(`${opts.name}::${p}`)),\n\t\t\t],\n\t\t};\n\t}\n\n\t/**\n\t * Register a CQRS subgraph that auto-mounts into the root graph.\n\t *\n\t * Creates a `CqrsGraph` via the `cqrs()` factory (roadmap §4.5), mounts it\n\t * into the root graph, and exposes it for DI via `@InjectGraph(name)`.\n\t *\n\t * CQRS decorators (`@CommandHandler`, `@EventHandler`, `@QueryHandler`,\n\t * `@SagaHandler`) are discovered by the explorer and wired to this graph\n\t * on module init.\n\t *\n\t * @example\n\t * ```ts\n\t * GraphReflyModule.forCqrs({\n\t * name: \"orders\",\n\t * build: (g) => {\n\t * g.event(\"orderPlaced\");\n\t * g.projection({ name: \"orderCount\", events: [\"orderPlaced\"], reducer: (_s, evts) => evts.length, initial: 0 });\n\t * },\n\t * })\n\t * ```\n\t */\n\tstatic forCqrs(opts: GraphReflyCqrsOptions): DynamicModule {\n\t\tconst providers: Provider[] = [\n\t\t\t{\n\t\t\t\tprovide: getGraphToken(opts.name),\n\t\t\t\tuseFactory: (rootGraph: Graph) => {\n\t\t\t\t\tconst g = cqrs(opts.name, opts.cqrs);\n\t\t\t\t\tif (opts.eventStorage) g.attachEventStorage(opts.eventStorage);\n\t\t\t\t\tif (opts.build) opts.build(g);\n\t\t\t\t\trootGraph.mount(opts.name, g);\n\t\t\t\t\treturn g;\n\t\t\t\t},\n\t\t\t\tinject: [GRAPHREFLY_ROOT_GRAPH],\n\t\t\t},\n\t\t];\n\n\t\tif (opts.nodes) {\n\t\t\tfor (const path of opts.nodes) {\n\t\t\t\tproviders.push({\n\t\t\t\t\tprovide: getNodeToken(`${opts.name}::${path}`),\n\t\t\t\t\tuseFactory: (graph: Graph) => graph.resolve(path),\n\t\t\t\t\tinject: [getGraphToken(opts.name)],\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tmodule: GraphReflyModule,\n\t\t\tproviders,\n\t\t\texports: [\n\t\t\t\tgetGraphToken(opts.name),\n\t\t\t\t...(opts.nodes ?? []).map((p) => getNodeToken(`${opts.name}::${p}`)),\n\t\t\t],\n\t\t};\n\t}\n}\n","// ---------------------------------------------------------------------------\n// NestJS RxJS bridge — returns a real rxjs `Observable` (so NestJS route\n// handlers' `isObservable()` recognizes it and `.pipe()` works directly).\n//\n// rxjs lives ONLY here, under the opt-in `compat/nestjs` subpath, where it is\n// legitimately available (`@nestjs/common` depends on rxjs). The base\n// `toObservable` (`@graphrefly/graphrefly/base`) is dependency-free and is\n// what RN/Hermes/web consumers load — this wrapper just adopts it via\n// rxjs `from()`.\n// ---------------------------------------------------------------------------\n\nimport type { Messages, Node } from \"@graphrefly/pure-ts/core\";\nimport { from, type Observable, type ObservableInput } from \"rxjs\";\nimport {\n\ttoObservable as baseToObservable,\n\ttype ToObservableOptions,\n} from \"../../base/composition/observable.js\";\n\nexport type { ToObservableOptions };\n\n/**\n * Bridge a `Node<T>` to a real rxjs `Observable` for NestJS controllers,\n * gateways, and interceptors. Wraps the dependency-free base interop\n * observable with rxjs `from()` so `isObservable()` / `.pipe()` work.\n *\n * See {@link baseToObservable} for the DATA/ERROR/COMPLETE mapping and the\n * `{ raw: true }` message-batch mode.\n */\nexport function toObservable<T>(\n\tnode: Node<T>,\n\toptions?: ToObservableOptions & { raw?: false },\n): Observable<T>;\nexport function toObservable<T>(\n\tnode: Node<T>,\n\toptions: ToObservableOptions & { raw: true },\n): Observable<Messages>;\nexport function toObservable<T>(\n\tnode: Node<T>,\n\toptions?: ToObservableOptions,\n): Observable<T | Messages> {\n\t// The base interop observable exposes `subscribe` + the well-known\n\t// `Symbol.observable` method at runtime; rxjs `from()` adopts it. Branch\n\t// so each call hits a concrete base overload (the union impl signature is\n\t// not visible to callers), and cast the minimal base type to rxjs input.\n\tconst base = options?.raw ? baseToObservable(node, { raw: true }) : baseToObservable<T>(node);\n\treturn from(base as unknown as ObservableInput<T | Messages>);\n}\n"],"mappings":"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","names":["toObservable","DATA","DATA","toObservable"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/reactive-layout/reactive-layout.ts"],"sourcesContent":["/**\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\";\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/** 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};\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 Intl.Segmenter (word granularity) and classify break kinds.\n * Returns raw segmentation pieces before merging.\n */\nfunction segmentText(normalized: string): {\n\ttexts: string[];\n\tisWordLike: boolean[];\n\tkinds: SegmentBreakKind[];\n}[] {\n\tconst wordSegmenter = new Intl.Segmenter(undefined, { granularity: \"word\" });\n\tconst pieces: {\n\t\ttexts: string[];\n\t\tisWordLike: boolean[];\n\t\tkinds: SegmentBreakKind[];\n\t}[] = [];\n\n\tfor (const s of wordSegmenter.segment(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): PreparedSegment[] {\n\tconst normalized = normalizeWhitespace(text);\n\tif (normalized.length === 0) return [];\n\n\tconst pieces = segmentText(normalized);\n\tconst graphemeSegmenter = new Intl.Segmenter(undefined, {\n\t\tgranularity: \"grapheme\",\n\t});\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 graphemeSegmenter.segment(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 graphemeSegmenter.segment(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): LineBreaksResult {\n\tif (segments.length === 0) {\n\t\treturn { lines: [], lineCount: 0 };\n\t}\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 graphemeSegmenter = new Intl.Segmenter(undefined, {\n\t\t\t\t\tgranularity: \"grapheme\",\n\t\t\t\t});\n\t\t\t\tconst graphemes = [...graphemeSegmenter.segment(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 graphemeSegmenter = new Intl.Segmenter(undefined, {\n\t\t\t\tgranularity: \"grapheme\",\n\t\t\t});\n\t\t\tconst graphemes = [...graphemeSegmenter.segment(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\n// Module-scoped segmenter — `Intl.Segmenter` construction is non-trivial and\n// its `segment()` method is stateless per-call, so one shared instance is safe\n// and avoids ~N-per-frame allocations when many lines cross segment boundaries.\nlet _graphemeSegmenter: Intl.Segmenter | null = null;\nfunction graphemeSegmenter(): Intl.Segmenter {\n\tif (_graphemeSegmenter === null) {\n\t\t_graphemeSegmenter = new Intl.Segmenter(undefined, { granularity: \"grapheme\" });\n\t}\n\treturn _graphemeSegmenter;\n}\n\nfunction sliceSegmentText(seg: PreparedSegment, startG: number, endG: number): string {\n\tif (startG === 0 && endG < 0) return seg.text;\n\tconst graphemes = [...graphemeSegmenter().segment(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): 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);\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);\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\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(segments, startSeg, startG, lineEndSeg, lineEndG, false);\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);\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(segments, startSeg, startG, lineEndSeg, lineEndG, false);\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);\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);\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(segments, startSeg, startG, lineEndSeg, lineEndG, false);\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(segments, startSeg, startG, lineEndSeg, lineEndG, false);\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(segments, startSeg, startG, lineEndSeg, lineEndG, endsAtSoftHyphen);\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): CharPosition[] {\n\tconst positions: CharPosition[] = [];\n\tconst graphemeSegmenter = new Intl.Segmenter(undefined, {\n\t\tgranularity: \"grapheme\",\n\t});\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 = [...graphemeSegmenter.segment(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/** 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, name = \"reactive-layout\" } = opts;\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);\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),\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),\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":["graphemeSegmenter","w","canBreakAfter","text","endsAtSoftHyphen"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/reactive-layout/measurement-adapters.ts","../src/utils/reactive-layout/reactive-block-layout.ts","../src/utils/reactive-layout/reactive-flow-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 } from \"./reactive-layout.js\";\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// 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 multi-content block layout engine (roadmap §7.1 — mixed content).\n *\n * Extends the text-only `reactiveLayout` with support for image and SVG blocks.\n * Pure-arithmetic layout over measured child sizes — no DOM, no async.\n *\n * Graph shape:\n * ```\n * Graph(\"reactive-block-layout\")\n * ├── node([], { initial: \"blocks\" }) — ContentBlock[] input\n * ├── node([], { initial: \"max-width\" }) — container constraint\n * ├── node([], { initial: \"gap\" }) — vertical gap between blocks (px)\n * ├── derived(\"measured-blocks\") — blocks → MeasuredBlock[] (per-type measurement)\n * ├── derived(\"block-flow\") — measured-blocks + max-width + gap → PositionedBlock[]\n * ├── derived(\"total-height\") — block-flow → total height\n * └── meta: { block-count, layout-time-ns }\n * ```\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 {\n\tanalyzeAndMeasure,\n\ttype CharPosition,\n\tcomputeCharPositions,\n\tcomputeLineBreaks,\n\ttype LineBreaksResult,\n\ttype MeasurementAdapter,\n\ttype PreparedSegment,\n} from \"./reactive-layout.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Pluggable measurement backend for SVG content. */\nexport interface SvgMeasurer {\n\tmeasureSvg(content: string): { width: number; height: number };\n}\n\n/** Pluggable measurement backend for image content. */\nexport interface ImageMeasurer {\n\tmeasureImage(src: string): { width: number; height: number };\n}\n\n/** Adapters map for `reactiveBlockLayout`. */\nexport type BlockAdapters = {\n\t/** Text measurement adapter (required — delegates to `reactiveLayout` internals). */\n\ttext: MeasurementAdapter;\n\t/** SVG measurement (optional — required only if SVG blocks are present). */\n\tsvg?: SvgMeasurer;\n\t/** Image measurement (optional — required only if image blocks without explicit dimensions are present). */\n\timage?: ImageMeasurer;\n};\n\n/** A content block — text, image, or SVG. */\nexport type ContentBlock =\n\t| {\n\t\t\ttype: \"text\";\n\t\t\ttext: string;\n\t\t\tfont?: string;\n\t\t\tlineHeight?: number;\n\t }\n\t| {\n\t\t\ttype: \"image\";\n\t\t\tsrc: string;\n\t\t\t/** Natural width in px. Required if no ImageMeasurer adapter is provided. */\n\t\t\tnaturalWidth?: number;\n\t\t\t/** Natural height in px. Required if no ImageMeasurer adapter is provided. */\n\t\t\tnaturalHeight?: number;\n\t }\n\t| {\n\t\t\ttype: \"svg\";\n\t\t\tcontent: string;\n\t\t\t/** Explicit viewBox dimensions. Required if no SvgMeasurer adapter is provided. */\n\t\t\tviewBox?: { width: number; height: number };\n\t };\n\n/**\n * A block after measurement — knows its natural dimensions.\n *\n * **Equality note:** The reactive `measured-blocks` node uses dimension-only equality\n * (`type`, `width`, `height`, `index`). Inner text layout data (`textSegments`,\n * `textLineBreaks`, `textCharPositions`) is NOT compared for change detection.\n * If you need text-level reactivity, use `reactiveLayout()` directly per text block.\n */\nexport type MeasuredBlock = {\n\tindex: number;\n\ttype: \"text\" | \"image\" | \"svg\";\n\twidth: number;\n\theight: number;\n\t/** For text blocks: the inner layout results. */\n\ttextSegments?: PreparedSegment[];\n\ttextLineBreaks?: LineBreaksResult;\n\ttextCharPositions?: CharPosition[];\n};\n\n/** A block after flow — positioned in the container. */\nexport type PositionedBlock = MeasuredBlock & {\n\tx: number;\n\ty: number;\n};\n\n/** Options for `reactiveBlockLayout`. */\nexport type ReactiveBlockLayoutOptions = {\n\tadapters: BlockAdapters;\n\tname?: string;\n\tblocks?: ContentBlock[];\n\t/** Container max width in px (clamped to ≥ 0 on init and `setMaxWidth`). */\n\tmaxWidth?: number;\n\t/** Vertical gap between blocks in px (default 0). */\n\tgap?: number;\n\t/** Default font for text blocks that don't specify one. */\n\tdefaultFont?: string;\n\t/** Default line height for text blocks that don't specify one. */\n\tdefaultLineHeight?: number;\n};\n\n/** Result bundle from `reactiveBlockLayout`. */\nexport type ReactiveBlockLayoutBundle = {\n\tgraph: Graph;\n\tsetBlocks: (blocks: ContentBlock[]) => void;\n\tsetMaxWidth: (maxWidth: number) => void;\n\tsetGap: (gap: number) => void;\n\tmeasuredBlocks: Node<MeasuredBlock[]>;\n\tblockFlow: Node<PositionedBlock[]>;\n\ttotalHeight: Node<number>;\n};\n\n// ---------------------------------------------------------------------------\n// Block measurement (pure functions)\n// ---------------------------------------------------------------------------\n\n/**\n * Measure a single content block, returning natural (unconstrained) dimensions.\n * Text blocks use the text layout pipeline; image/SVG use adapters or explicit dims.\n */\nexport function measureBlock(\n\tblock: ContentBlock,\n\tmaxWidth: number,\n\tadapters: BlockAdapters,\n\tmeasureCache: Map<string, Map<string, number>>,\n\tdefaultFont: string,\n\tdefaultLineHeight: number,\n\tindex: number,\n): MeasuredBlock {\n\tswitch (block.type) {\n\t\tcase \"text\": {\n\t\t\tconst font = block.font ?? defaultFont;\n\t\t\tconst lineHeight = block.lineHeight ?? defaultLineHeight;\n\t\t\tconst segments = analyzeAndMeasure(block.text, font, adapters.text, measureCache);\n\t\t\tconst lineBreaks = computeLineBreaks(segments, maxWidth, adapters.text, font, measureCache);\n\t\t\tconst charPositions = computeCharPositions(lineBreaks, segments, lineHeight);\n\t\t\tconst height = lineBreaks.lineCount * lineHeight;\n\t\t\t// Width is the max line width (clamped to maxWidth)\n\t\t\tlet width = 0;\n\t\t\tfor (const line of lineBreaks.lines) {\n\t\t\t\tif (line.width > width) width = line.width;\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tindex,\n\t\t\t\ttype: \"text\",\n\t\t\t\twidth: Math.min(width, maxWidth),\n\t\t\t\theight,\n\t\t\t\ttextSegments: segments,\n\t\t\t\ttextLineBreaks: lineBreaks,\n\t\t\t\ttextCharPositions: charPositions,\n\t\t\t};\n\t\t}\n\t\tcase \"image\": {\n\t\t\tlet w: number;\n\t\t\tlet h: number;\n\t\t\tif (block.naturalWidth != null && block.naturalHeight != null) {\n\t\t\t\tw = block.naturalWidth;\n\t\t\t\th = block.naturalHeight;\n\t\t\t} else if (adapters.image) {\n\t\t\t\tconst dims = adapters.image.measureImage(block.src);\n\t\t\t\tw = dims.width;\n\t\t\t\th = dims.height;\n\t\t\t} else {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Image block at index ${index} has no naturalWidth/naturalHeight and no ImageMeasurer adapter`,\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Scale proportionally to fit maxWidth\n\t\t\tif (w > maxWidth) {\n\t\t\t\th = (h * maxWidth) / w;\n\t\t\t\tw = maxWidth;\n\t\t\t}\n\t\t\treturn { index, type: \"image\", width: w, height: h };\n\t\t}\n\t\tcase \"svg\": {\n\t\t\tlet w: number;\n\t\t\tlet h: number;\n\t\t\tif (block.viewBox) {\n\t\t\t\tw = block.viewBox.width;\n\t\t\t\th = block.viewBox.height;\n\t\t\t} else if (adapters.svg) {\n\t\t\t\tconst dims = adapters.svg.measureSvg(block.content);\n\t\t\t\tw = dims.width;\n\t\t\t\th = dims.height;\n\t\t\t} else {\n\t\t\t\tthrow new Error(`SVG block at index ${index} has no viewBox and no SvgMeasurer adapter`);\n\t\t\t}\n\t\t\t// Scale proportionally to fit maxWidth\n\t\t\tif (w > maxWidth) {\n\t\t\t\th = (h * maxWidth) / w;\n\t\t\t\tw = maxWidth;\n\t\t\t}\n\t\t\treturn { index, type: \"svg\", width: w, height: h };\n\t\t}\n\t}\n}\n\n/**\n * Measure all blocks in a content array.\n */\nexport function measureBlocks(\n\tblocks: ContentBlock[],\n\tmaxWidth: number,\n\tadapters: BlockAdapters,\n\tmeasureCache: Map<string, Map<string, number>>,\n\tdefaultFont: string,\n\tdefaultLineHeight: number,\n): MeasuredBlock[] {\n\treturn blocks.map((block, i) =>\n\t\tmeasureBlock(block, maxWidth, adapters, measureCache, defaultFont, defaultLineHeight, i),\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Block flow (pure function)\n// ---------------------------------------------------------------------------\n\n/**\n * Vertical stacking flow: blocks are placed top-to-bottom, left-aligned,\n * separated by `gap` pixels. Pure arithmetic over measured sizes.\n */\nexport function computeBlockFlow(measured: MeasuredBlock[], gap: number): PositionedBlock[] {\n\tconst result: PositionedBlock[] = [];\n\tlet y = 0;\n\tfor (let i = 0; i < measured.length; i++) {\n\t\tconst m = measured[i]!;\n\t\tresult.push({ ...m, x: 0, y });\n\t\ty += m.height + (i < measured.length - 1 ? gap : 0);\n\t}\n\treturn result;\n}\n\n/**\n * Compute total height from positioned blocks.\n */\nexport function computeTotalHeight(flow: PositionedBlock[]): number {\n\tif (flow.length === 0) return 0;\n\tconst last = flow[flow.length - 1]!;\n\treturn last.y + last.height;\n}\n\n// ---------------------------------------------------------------------------\n// Reactive graph factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a reactive block layout graph for mixed content (text + image + SVG).\n *\n * ```\n * Graph(\"reactive-block-layout\")\n * ├── node([], { initial: \"blocks\" }) — ContentBlock[] input\n * ├── node([], { initial: \"max-width\" }) — container constraint\n * ├── node([], { initial: \"gap\" }) — vertical gap (px)\n * ├── derived(\"measured-blocks\") — blocks + max-width → MeasuredBlock[]\n * ├── derived(\"block-flow\") — measured-blocks + gap → PositionedBlock[]\n * ├── derived(\"total-height\") — block-flow → number\n * └── meta: { block-count, layout-time-ns }\n * ```\n */\nexport function reactiveBlockLayout(opts: ReactiveBlockLayoutOptions): ReactiveBlockLayoutBundle {\n\tconst {\n\t\tadapters,\n\t\tname = \"reactive-block-layout\",\n\t\tdefaultFont = \"16px sans-serif\",\n\t\tdefaultLineHeight = 20,\n\t} = opts;\n\tconst g = new Graph(name);\n\n\t// Shared text measurement cache (same structure as reactiveLayout)\n\tconst measureCache = new Map<string, Map<string, number>>();\n\n\t// --- State nodes ---\n\tconst blocksNode = node<ContentBlock[]>([], { name: \"blocks\", initial: opts.blocks ?? [] });\n\tconst maxWidthNode = node<number>([], {\n\t\tname: \"max-width\",\n\t\tinitial: Math.max(0, opts.maxWidth ?? 800),\n\t});\n\tconst gapNode = node<number>([], { name: \"gap\", initial: opts.gap ?? 0 });\n\n\t// --- Derived: measured-blocks ---\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 lets us flush the\n\t// measure cache and the downstream adapter cache reactively — replaces\n\t// the old v4 per-node `onMessage` hook that watched for INVALIDATE.\n\tconst measuredBlocksNode: Node<MeasuredBlock[]> = node<MeasuredBlock[]>(\n\t\t[blocksNode, maxWidthNode],\n\t\t(data, actions, ctx) => {\n\t\t\tconst blocksVal = data[0] != null && data[0].length > 0 ? data[0].at(-1) : ctx.prevData[0];\n\t\t\tconst mwVal = data[1] != null && data[1].length > 0 ? data[1].at(-1) : ctx.prevData[1];\n\t\t\tconst t0 = monotonicNs();\n\t\t\tconst result = measureBlocks(\n\t\t\t\tblocksVal as ContentBlock[],\n\t\t\t\tmwVal as number,\n\t\t\t\tadapters,\n\t\t\t\tmeasureCache,\n\t\t\t\tdefaultFont,\n\t\t\t\tdefaultLineHeight,\n\t\t\t);\n\t\t\tconst elapsed = monotonicNs() - t0;\n\n\t\t\t// Phase-3 meta deferral (parity with reactiveLayout)\n\t\t\tconst meta = measuredBlocksNode.meta;\n\t\t\tif (meta) {\n\t\t\t\temitToMeta(meta[\"block-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: flush on deactivation + INVALIDATE only,\n\t\t\t// NOT before fn re-runs. Preserves cached measurements across\n\t\t\t// block edits so a single-block change doesn't wipe entries from\n\t\t\t// the other blocks. Image/SVG measurers don't expose a cache hook.\n\t\t\tconst flush = (): void => {\n\t\t\t\tmeasureCache.clear();\n\t\t\t\tadapters.text.clearCache?.();\n\t\t\t};\n\t\t\treturn { onDeactivation: flush, onInvalidate: flush };\n\t\t},\n\t\t{\n\t\t\tname: \"measured-blocks\",\n\t\t\tdescribeKind: \"derived\",\n\t\t\tmeta: { \"block-count\": 0, \"layout-time-ns\": 0 },\n\t\t\tequals: (a, b) => {\n\t\t\t\tconst ma = a as MeasuredBlock[] | null;\n\t\t\t\tconst mb = b as MeasuredBlock[] | null;\n\t\t\t\tif (ma == null || mb == null) return ma === mb;\n\t\t\t\tif (ma.length !== mb.length) return false;\n\t\t\t\tfor (let i = 0; i < ma.length; i++) {\n\t\t\t\t\tconst ba = ma[i]!;\n\t\t\t\t\tconst bb = mb[i]!;\n\t\t\t\t\tif (\n\t\t\t\t\t\tba.type !== bb.type ||\n\t\t\t\t\t\tba.width !== bb.width ||\n\t\t\t\t\t\tba.height !== bb.height ||\n\t\t\t\t\t\tba.index !== bb.index\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: block-flow ---\n\tconst blockFlowNode = node<PositionedBlock[]>(\n\t\t[measuredBlocksNode, gapNode],\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(computeBlockFlow(data[0] as MeasuredBlock[], data[1] as number));\n\t\t},\n\t\t{\n\t\t\tname: \"block-flow\",\n\t\t\tdescribeKind: \"derived\",\n\t\t\tequals: (a, b) => {\n\t\t\t\tconst fa = a as PositionedBlock[] | null;\n\t\t\t\tconst fb = b as PositionedBlock[] | null;\n\t\t\t\tif (fa == null || fb == null) return fa === fb;\n\t\t\t\tif (fa.length !== fb.length) return false;\n\t\t\t\tfor (let i = 0; i < fa.length; i++) {\n\t\t\t\t\tconst pa = fa[i]!;\n\t\t\t\t\tconst pb = fb[i]!;\n\t\t\t\t\tif (pa.x !== pb.x || pa.y !== pb.y || pa.width !== pb.width || pa.height !== pb.height)\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: total-height ---\n\tconst totalHeightNode = node<number>(\n\t\t[blockFlowNode],\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(computeTotalHeight(data[0] as PositionedBlock[]));\n\t\t},\n\t\t{ describeKind: \"derived\", name: \"total-height\" },\n\t);\n\n\t// --- Register in graph ---\n\tg.add(blocksNode, { name: \"blocks\" });\n\tg.add(maxWidthNode, { name: \"max-width\" });\n\tg.add(gapNode, { name: \"gap\" });\n\tg.add(measuredBlocksNode, { name: \"measured-blocks\" });\n\tg.add(blockFlowNode, { name: \"block-flow\" });\n\tg.add(totalHeightNode, { name: \"total-height\" });\n\n\t// --- Edges (for describe() visibility) ---\n\n\treturn {\n\t\tgraph: g,\n\t\tsetBlocks: (blocks: ContentBlock[]) => g.set(\"blocks\", blocks),\n\t\tsetMaxWidth: (mw: number) => g.set(\"max-width\", Math.max(0, mw)),\n\t\tsetGap: (gap: number) => g.set(\"gap\", gap),\n\t\tmeasuredBlocks: measuredBlocksNode,\n\t\tblockFlow: blockFlowNode,\n\t\ttotalHeight: totalHeightNode,\n\t};\n}\n","/**\n * Reactive flow layout — multi-column text flowing around shape obstacles\n * (roadmap §7.1, extends `reactiveLayout`).\n *\n * Unlike `reactiveLayout` (single-column, one `maxWidth`) and\n * `reactiveBlockLayout` (vertical stack of heterogeneous blocks), this engine\n * lays out a single stream of text across **N columns** while wrapping around\n * arbitrary **shape obstacles** (circles, rectangles). Each line's available\n * width can differ from every other line's, enabling magazine-style editorial\n * layouts — and the cursor carries seamlessly from column to column so text\n * never duplicates or gaps at boundaries.\n *\n * ```\n * Graph(\"reactive-flow-layout\")\n * ├── node([], { initial: \"text\" })\n * ├── node([], { initial: \"font\" })\n * ├── node([], { initial: \"line-height\" })\n * ├── node([], { initial: \"container\" }) — { width, height, paddingX, paddingY }\n * ├── node([], { initial: \"columns\" }) — { count, gap }\n * ├── node([], { initial: \"obstacles\" }) — Obstacle[] (moves reactively — rAF-friendly)\n * ├── derived(\"segments\") — text + font → PreparedSegment[] (from reactiveLayout)\n * ├── derived(\"flow-lines\") — segments + container + columns + obstacles + line-height\n * │ → PositionedLine[]\n * └── meta: { line-count, layout-time-ns, overflow-segments }\n * ```\n *\n * Obstacle positions change every frame; `flow-lines` re-runs per change, but\n * `segments` stays cached (text hasn't changed). Callers drive obstacles via a\n * reactive source like `fromRaf()` piped into a state node.\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 {\n\tanalyzeAndMeasure,\n\tcarveTextLineSlots,\n\ttype Interval,\n\ttype LayoutCursor,\n\tlayoutNextLine,\n\ttype MeasurementAdapter,\n\ttype PreparedSegment,\n} from \"./reactive-layout.js\";\n\n// ---------------------------------------------------------------------------\n// Obstacle types\n// ---------------------------------------------------------------------------\n\n/** A circle obstacle. Center `(cx, cy)`, radius `r`; text keeps `padding` distance. */\nexport type CircleObstacle = {\n\tkind: \"circle\";\n\tcx: number;\n\tcy: number;\n\tr: number;\n\t/** Horizontal padding between obstacle and wrapped text (default 0). */\n\thPad?: number;\n\t/** Vertical padding — band overlap tolerance (default 0). */\n\tvPad?: number;\n};\n\n/** A rectangle obstacle. Top-left `(x, y)`, size `(w, h)`. */\nexport type RectObstacle = {\n\tkind: \"rect\";\n\tx: number;\n\ty: number;\n\tw: number;\n\th: number;\n\thPad?: number;\n\tvPad?: number;\n};\n\n/** Union of built-in obstacle shapes. */\nexport type Obstacle = CircleObstacle | RectObstacle;\n\n/**\n * Compute the horizontal interval occluded by a circle at vertical band\n * `[bandTop, bandBottom]`, or `null` if no occlusion.\n *\n * Exported so consumers that render obstacle outlines in sync with the flow\n * can reuse the same geometry the flow engine uses — no divergence.\n */\nexport function circleIntervalForBand(\n\to: CircleObstacle,\n\tbandTop: number,\n\tbandBottom: number,\n): Interval | null {\n\tconst hPad = o.hPad ?? 0;\n\tconst vPad = o.vPad ?? 0;\n\tconst top = bandTop - vPad;\n\tconst bottom = bandBottom + vPad;\n\tif (top >= o.cy + o.r || bottom <= o.cy - o.r) return null;\n\tconst minDy = o.cy >= top && o.cy <= bottom ? 0 : o.cy < top ? top - o.cy : o.cy - bottom;\n\tif (minDy >= o.r) return null;\n\tconst maxDx = Math.sqrt(o.r * o.r - minDy * minDy);\n\treturn { left: o.cx - maxDx - hPad, right: o.cx + maxDx + hPad };\n}\n\n/** Same as `circleIntervalForBand` for rectangles. */\nexport function rectIntervalForBand(\n\to: RectObstacle,\n\tbandTop: number,\n\tbandBottom: number,\n): Interval | null {\n\tconst hPad = o.hPad ?? 0;\n\tconst vPad = o.vPad ?? 0;\n\tif (bandBottom <= o.y - vPad) return null;\n\tif (bandTop >= o.y + o.h + vPad) return null;\n\treturn { left: o.x - hPad, right: o.x + o.w + hPad };\n}\n\nfunction obstacleIntervalForBand(\n\to: Obstacle,\n\tbandTop: number,\n\tbandBottom: number,\n): Interval | null {\n\treturn o.kind === \"circle\"\n\t\t? circleIntervalForBand(o, bandTop, bandBottom)\n\t\t: rectIntervalForBand(o, bandTop, bandBottom);\n}\n\n// ---------------------------------------------------------------------------\n// Flow layout types\n// ---------------------------------------------------------------------------\n\nexport type FlowContainer = {\n\twidth: number;\n\theight: number;\n\tpaddingX?: number;\n\tpaddingY?: number;\n};\n\nexport type FlowColumns = {\n\tcount: number;\n\tgap: number;\n};\n\n/** A single positioned line after flow layout. */\nexport type PositionedLine = {\n\tx: number;\n\ty: number;\n\t/** Natural measured width of the text content. */\n\twidth: number;\n\t/** Width of the slot this line was placed in — use this as the DOM element's\n\t * `width` when applying `text-align: justify` so the line stretches to the\n\t * obstacle edge on both sides. */\n\tslotWidth: number;\n\ttext: string;\n\t/** Which column index this line belongs to (0-based). */\n\tcolumnIndex: number;\n\t/** `true` iff the slot's right edge was carved short by an obstacle (the\n\t * slot sits to the LEFT of an obstacle). Renderers can right-align text\n\t * in these slots so single-word lines still hug the obstacle — CSS\n\t * `text-align: justify` can't stretch single-word lines, which otherwise\n\t * produces a visible asymmetry vs. the slot on the other side of the\n\t * obstacle (which is flush by default). */\n\tflushToRight: boolean;\n};\n\n/** Options for `reactiveFlowLayout`. */\nexport type ReactiveFlowLayoutOptions = {\n\tadapter: MeasurementAdapter;\n\tname?: string;\n\ttext?: string;\n\tfont?: string;\n\tlineHeight?: number;\n\tcontainer?: FlowContainer;\n\tcolumns?: FlowColumns;\n\tobstacles?: Obstacle[];\n\t/** Minimum slot width (px) below which a slot is discarded rather than squeezed. Default `20`. */\n\tminSlotWidth?: number;\n\t/**\n\t * Vertical gap (px) inserted after a hard-break segment (the spacing\n\t * between paragraphs). When unset (or explicitly set to `null`), tracks\n\t * the current `lineHeight` reactively — one line-height of visible\n\t * paragraph gap, matching dense editorial layouts. Set to `0` for\n\t * paragraph-preserving layout that reclaims the break line; set larger\n\t * (e.g. `2 * lineHeight`) for looser manuscript settings. Reactive —\n\t * update via `setParagraphSpacing(n)` or restore to track-lineHeight\n\t * mode via `setParagraphSpacing(null)`.\n\t */\n\tparagraphSpacing?: number | null;\n};\n\n/** Result bundle from `reactiveFlowLayout`. */\nexport type ReactiveFlowLayoutBundle = {\n\tgraph: Graph;\n\tsetText: (text: string) => void;\n\tsetFont: (font: string) => void;\n\tsetLineHeight: (lh: number) => void;\n\tsetContainer: (c: FlowContainer) => void;\n\tsetColumns: (c: FlowColumns) => void;\n\tsetObstacles: (o: Obstacle[]) => void;\n\tsetParagraphSpacing: (ps: number | null) => void;\n\tsegments: Node<PreparedSegment[]>;\n\tflowLines: Node<PositionedLine[]>;\n};\n\n// ---------------------------------------------------------------------------\n// Pure flow-layout compute\n// ---------------------------------------------------------------------------\n\n/** Result of `computeFlowLines`. */\nexport type FlowLinesResult = {\n\t/** Positioned lines in render order (columns inner-ordered top-to-bottom). */\n\tlines: PositionedLine[];\n\t/** Cursor position after the last line was placed. If\n\t * `cursor.segmentIndex < segments.length`, the layout **truncated** — the\n\t * container couldn't fit all text. */\n\tcursor: LayoutCursor;\n};\n\n/** Optional tuning knobs for {@link computeFlowLines}. */\nexport type ComputeFlowLinesOptions = {\n\t/**\n\t * Vertical gap (px) inserted after a hard-break segment (the spacing\n\t * between paragraphs). Defaults to `lineHeight` — one line-height of\n\t * visible paragraph gap, which matches dense editorial layouts. Set to\n\t * `0` for paragraph-preserving layout that reclaims the break line;\n\t * set larger (e.g. `2 * lineHeight`) for looser manuscript settings.\n\t */\n\tparagraphSpacing?: number;\n};\n\n/**\n * Lay out `segments` across N columns, wrapping each line around `obstacles`.\n * Pure function — no reactive wiring. Exported for testing and for consumers\n * who want to run flow layout outside a Graph.\n *\n * `carveTextLineSlots` guarantees left-to-right-ordered, non-overlapping slots,\n * so this function does not sort them.\n */\nexport function computeFlowLines(\n\tsegments: PreparedSegment[],\n\tcontainer: FlowContainer,\n\tcolumns: FlowColumns,\n\tobstacles: Obstacle[],\n\tlineHeight: number,\n\tminSlotWidth: number,\n\topts?: ComputeFlowLinesOptions,\n): FlowLinesResult {\n\tconst paragraphSpacing = opts?.paragraphSpacing ?? lineHeight;\n\tconst lines: PositionedLine[] = [];\n\tlet cursor: LayoutCursor = { segmentIndex: 0, graphemeIndex: 0 };\n\tif (segments.length === 0 || columns.count <= 0 || lineHeight <= 0) {\n\t\treturn { lines, cursor };\n\t}\n\n\tconst padX = container.paddingX ?? 0;\n\tconst padY = container.paddingY ?? 0;\n\tconst availWidth = Math.max(0, container.width - padX * 2);\n\tconst availHeight = Math.max(0, container.height - padY * 2);\n\tconst gapTotal = columns.gap * Math.max(0, columns.count - 1);\n\tconst colWidth = Math.max(0, (availWidth - gapTotal) / columns.count);\n\tif (colWidth <= 0) return { lines, cursor };\n\n\touterCol: for (let col = 0; col < columns.count; col++) {\n\t\tconst colLeft = padX + col * (colWidth + columns.gap);\n\t\tconst colRight = colLeft + colWidth;\n\t\tlet bandTop = padY;\n\n\t\twhile (bandTop + lineHeight <= padY + availHeight) {\n\t\t\tconst bandBottom = bandTop + lineHeight;\n\t\t\tconst blocked: Interval[] = [];\n\t\t\tfor (let oi = 0; oi < obstacles.length; oi++) {\n\t\t\t\tconst iv = obstacleIntervalForBand(obstacles[oi]!, bandTop, bandBottom);\n\t\t\t\tif (iv !== null) blocked.push(iv);\n\t\t\t}\n\t\t\tconst slots = carveTextLineSlots({ left: colLeft, right: colRight }, blocked, minSlotWidth);\n\n\t\t\tif (slots.length === 0) {\n\t\t\t\tbandTop += lineHeight;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet hardBreakThisBand = false;\n\t\t\tfor (let si = 0; si < slots.length; si++) {\n\t\t\t\tconst slot = slots[si]!;\n\t\t\t\tconst slotW = slot.right - slot.left;\n\t\t\t\tconst line = layoutNextLine(segments, cursor, slotW);\n\t\t\t\tif (line === null) {\n\t\t\t\t\treturn { lines, cursor };\n\t\t\t\t}\n\t\t\t\tif (line.text.length === 0 && line.width === 0) {\n\t\t\t\t\t// Hard-break — advance cursor past the break segment and end\n\t\t\t\t\t// THIS band so the break produces a visible paragraph gap\n\t\t\t\t\t// rather than being silently absorbed across remaining slots.\n\t\t\t\t\tcursor = line.end;\n\t\t\t\t\thardBreakThisBand = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tlines.push({\n\t\t\t\t\tx: slot.left,\n\t\t\t\t\ty: bandTop,\n\t\t\t\t\twidth: line.width,\n\t\t\t\t\tslotWidth: slotW,\n\t\t\t\t\ttext: line.text,\n\t\t\t\t\tcolumnIndex: col,\n\t\t\t\t\tflushToRight: slot.right < colRight - 0.5,\n\t\t\t\t});\n\t\t\t\tcursor = line.end;\n\t\t\t}\n\n\t\t\tif (hardBreakThisBand) {\n\t\t\t\t// Paragraph gap: `paragraphSpacing` (default lineHeight) of\n\t\t\t\t// vertical space after the break. `0` reclaims the break line;\n\t\t\t\t// larger values space paragraphs further apart.\n\t\t\t\tbandTop += paragraphSpacing;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbandTop += lineHeight;\n\t\t\tif (cursor.segmentIndex >= segments.length) break outerCol;\n\t\t}\n\n\t\tif (cursor.segmentIndex >= segments.length) break;\n\t}\n\n\treturn { lines, cursor };\n}\n\n// ---------------------------------------------------------------------------\n// Reactive graph factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a reactive flow-layout graph: N columns of text wrapping around\n * shape obstacles. Re-runs only the dependent derived nodes on any input\n * change. Obstacle movement (e.g. rAF-driven) invalidates `flow-lines` only;\n * `segments` stays cached as long as `text`/`font` don't change.\n *\n * @example\n * ```ts\n * import { fromRaf, reactiveFlowLayout } from \"@graphrefly/graphrefly-ts\";\n *\n * const flow = reactiveFlowLayout({\n * adapter: new CanvasMeasureAdapter(),\n * text: longEssay,\n * font: \"18px serif\",\n * lineHeight: 26,\n * container: { width: 900, height: 600, paddingX: 40, paddingY: 40 },\n * columns: { count: 2, gap: 32 },\n * obstacles: [{ kind: \"circle\", cx: 450, cy: 300, r: 80 }],\n * });\n *\n * // Animate the obstacle via rAF:\n * fromRaf().subscribe(([[, t]]) => {\n * const x = 450 + 120 * Math.sin((t as number) * 0.001);\n * flow.setObstacles([{ kind: \"circle\", cx: x, cy: 300, r: 80 }]);\n * });\n * ```\n */\nexport function reactiveFlowLayout(opts: ReactiveFlowLayoutOptions): ReactiveFlowLayoutBundle {\n\tconst { adapter, name = \"reactive-flow-layout\", minSlotWidth = 20 } = opts;\n\tconst g = new Graph(name);\n\n\tconst measureCache = new Map<string, Map<string, number>>();\n\n\tconst textNode = node<string>([], { name: \"text\", initial: opts.text ?? \"\" });\n\tconst fontNode = node<string>([], { name: \"font\", initial: opts.font ?? \"16px sans-serif\" });\n\tconst lineHeightNode = node<number>([], { name: \"line-height\", initial: opts.lineHeight ?? 20 });\n\tconst containerNode = node<FlowContainer>([], {\n\t\tname: \"container\",\n\t\tinitial: opts.container ?? { width: 800, height: 600, paddingX: 0, paddingY: 0 },\n\t});\n\tconst columnsNode = node<FlowColumns>([], {\n\t\tname: \"columns\",\n\t\tinitial: opts.columns ?? { count: 1, gap: 0 },\n\t});\n\tconst obstaclesNode = node<Obstacle[]>([], { name: \"obstacles\", initial: opts.obstacles ?? [] });\n\t// `paragraphSpacing` is reactive with a \"track lineHeight\" default. The\n\t// state node holds `number | null` — when `null`, `flow-lines` substitutes\n\t// the CURRENT `lineHeight` value, so the default stays truly reactive\n\t// as lineHeight updates. A caller who wants a fixed independent gap sets\n\t// an explicit number via the constructor or `setParagraphSpacing`; passing\n\t// `null` back restores the track-lineHeight behavior.\n\tconst paragraphSpacingNode = node<number | null>([], {\n\t\tname: \"paragraph-spacing\",\n\t\tinitial: opts.paragraphSpacing ?? null,\n\t});\n\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 result = analyzeAndMeasure(textVal, fontVal, adapter, measureCache);\n\t\t\tactions.emit(result);\n\t\t\t// Flush on deactivation + INVALIDATE only — preserve cache across\n\t\t\t// fn re-runs so text/font edits don't wipe per-segment entries that\n\t\t\t// still match the new text.\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{ name: \"segments\", describeKind: \"derived\" },\n\t);\n\n\tconst flowLinesNode = node<PositionedLine[]>(\n\t\t[segmentsNode, containerNode, columnsNode, obstaclesNode, lineHeightNode, paragraphSpacingNode],\n\t\t(batchData, actions, ctx) => {\n\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t);\n\t\t\tconst segments = data[0] as PreparedSegment[];\n\t\t\tconst t0 = monotonicNs();\n\t\t\t// `ps === null` → track current lineHeight. Any explicit number\n\t\t\t// (0, 60, …) overrides; passing `null` via `setParagraphSpacing`\n\t\t\t// restores tracking.\n\t\t\tconst effectiveSpacing = (data[5] as number | null) ?? (data[4] as number);\n\t\t\tconst { lines: result, cursor } = computeFlowLines(\n\t\t\t\tsegments,\n\t\t\t\tdata[1] as FlowContainer,\n\t\t\t\tdata[2] as FlowColumns,\n\t\t\t\tdata[3] as Obstacle[],\n\t\t\t\tdata[4] as number,\n\t\t\t\tminSlotWidth,\n\t\t\t\t{ paragraphSpacing: effectiveSpacing },\n\t\t\t);\n\t\t\tconst elapsed = monotonicNs() - t0;\n\t\t\t// Overflow signal: segments left unlaid-out after the container is\n\t\t\t// exhausted. `0` means all text fit; `N > 0` means the container\n\t\t\t// occluded/overflowed N segments — consumers can surface a \"…more\"\n\t\t\t// indicator, grow the container, or discard obstacles.\n\t\t\tconst overflow = Math.max(0, segments.length - cursor.segmentIndex);\n\t\t\tconst meta = flowLinesNode.meta;\n\t\t\tif (meta) {\n\t\t\t\temitToMeta(meta[\"line-count\"], result.length);\n\t\t\t\temitToMeta(meta[\"layout-time-ns\"], elapsed);\n\t\t\t\temitToMeta(meta[\"overflow-segments\"], overflow);\n\t\t\t}\n\t\t\tactions.emit(result);\n\t\t},\n\t\t{\n\t\t\tdescribeKind: \"derived\",\n\t\t\tname: \"flow-lines\",\n\t\t\tmeta: {\n\t\t\t\t\"line-count\": 0,\n\t\t\t\t\"layout-time-ns\": 0,\n\t\t\t\t\"overflow-segments\": 0,\n\t\t\t},\n\t\t\tequals: (a, b) => {\n\t\t\t\tconst la = a as PositionedLine[];\n\t\t\t\tconst lb = b as PositionedLine[];\n\t\t\t\tif (la.length !== lb.length) return false;\n\t\t\t\tfor (let i = 0; i < la.length; i++) {\n\t\t\t\t\tconst pa = la[i]!;\n\t\t\t\t\tconst pb = lb[i]!;\n\t\t\t\t\tif (\n\t\t\t\t\t\tpa.x !== pb.x ||\n\t\t\t\t\t\tpa.y !== pb.y ||\n\t\t\t\t\t\tpa.width !== pb.width ||\n\t\t\t\t\t\tpa.slotWidth !== pb.slotWidth ||\n\t\t\t\t\t\tpa.text !== pb.text ||\n\t\t\t\t\t\tpa.columnIndex !== pb.columnIndex ||\n\t\t\t\t\t\tpa.flushToRight !== pb.flushToRight\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\tg.add(textNode, { name: \"text\" });\n\tg.add(fontNode, { name: \"font\" });\n\tg.add(lineHeightNode, { name: \"line-height\" });\n\tg.add(containerNode, { name: \"container\" });\n\tg.add(columnsNode, { name: \"columns\" });\n\tg.add(obstaclesNode, { name: \"obstacles\" });\n\tg.add(paragraphSpacingNode, { name: \"paragraph-spacing\" });\n\tg.add(segmentsNode, { name: \"segments\" });\n\tg.add(flowLinesNode, { name: \"flow-lines\" });\n\n\treturn {\n\t\tgraph: g,\n\t\tsetText: (t: string) => g.set(\"text\", t),\n\t\tsetFont: (f: string) => g.set(\"font\", f),\n\t\tsetLineHeight: (lh: number) => g.set(\"line-height\", lh),\n\t\tsetContainer: (c: FlowContainer) => g.set(\"container\", c),\n\t\tsetColumns: (c: FlowColumns) => g.set(\"columns\", c),\n\t\tsetObstacles: (o: Obstacle[]) => g.set(\"obstacles\", o),\n\t\tsetParagraphSpacing: (ps: number | null) => g.set(\"paragraph-spacing\", ps),\n\t\tsegments: segmentsNode,\n\t\tflowLines: flowLinesNode,\n\t};\n}\n"],"mappings":"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","names":["monotonicNs","node","Graph","Graph","node","monotonicNs"]}