@graphrefly/ts 0.0.0 → 0.0.1

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 (307) hide show
  1. package/README.md +135 -0
  2. package/dist/adapters/index.cjs +105 -66
  3. package/dist/adapters/index.cjs.map +1 -1
  4. package/dist/adapters/index.d.cts +11 -136
  5. package/dist/adapters/index.d.ts +11 -136
  6. package/dist/adapters/index.js +27 -225
  7. package/dist/adapters/index.js.map +1 -1
  8. package/dist/adapters/observe-storage.d.cts +4 -4
  9. package/dist/adapters/observe-storage.d.ts +4 -4
  10. package/dist/adapters/observe-storage.js +1 -0
  11. package/dist/adapters/observe-storage.js.map +1 -1
  12. package/dist/adapters/react.cjs +166 -0
  13. package/dist/adapters/react.cjs.map +1 -0
  14. package/dist/adapters/react.d.cts +31 -0
  15. package/dist/adapters/react.d.ts +31 -0
  16. package/dist/adapters/react.js +51 -0
  17. package/dist/adapters/react.js.map +1 -0
  18. package/dist/adapters/solid.cjs +157 -0
  19. package/dist/adapters/solid.cjs.map +1 -0
  20. package/dist/adapters/solid.d.cts +26 -0
  21. package/dist/adapters/solid.d.ts +26 -0
  22. package/dist/adapters/solid.js +48 -0
  23. package/dist/adapters/solid.js.map +1 -0
  24. package/dist/adapters/svelte.cjs +148 -0
  25. package/dist/adapters/svelte.cjs.map +1 -0
  26. package/dist/adapters/svelte.d.cts +31 -0
  27. package/dist/adapters/svelte.d.ts +31 -0
  28. package/dist/adapters/svelte.js +46 -0
  29. package/dist/adapters/svelte.js.map +1 -0
  30. package/dist/adapters/vue.cjs +155 -0
  31. package/dist/adapters/vue.cjs.map +1 -0
  32. package/dist/adapters/vue.d.cts +26 -0
  33. package/dist/adapters/vue.d.ts +26 -0
  34. package/dist/adapters/vue.js +46 -0
  35. package/dist/adapters/vue.js.map +1 -0
  36. package/dist/agent-runtime-BesGDZcB.d.ts +555 -0
  37. package/dist/agent-runtime-DZTJ3C-p.d.cts +555 -0
  38. package/dist/agentic-memory-BmRCvh0d.d.ts +517 -0
  39. package/dist/agentic-memory-DfuVZ-lY.d.cts +517 -0
  40. package/dist/capability-admission-B0BzqAm5.d.cts +102 -0
  41. package/dist/capability-admission-g0mn9hpq.d.ts +102 -0
  42. package/dist/{chunk-JR4IKCR2.js → chunk-2747WN4Y.js} +5 -5
  43. package/dist/{chunk-2ILL7HQI.js → chunk-3JR3EDB2.js} +17 -17
  44. package/dist/{chunk-2ILL7HQI.js.map → chunk-3JR3EDB2.js.map} +1 -1
  45. package/dist/{chunk-GDL26N2T.js → chunk-3KN7NJDR.js} +2 -2
  46. package/dist/{chunk-DJC4CHLQ.js → chunk-43BKLFZU.js} +9 -9
  47. package/dist/chunk-6XAIOL3X.js +2407 -0
  48. package/dist/chunk-6XAIOL3X.js.map +1 -0
  49. package/dist/{chunk-SCRFRCGR.js → chunk-BSQGE5PS.js} +1 -1
  50. package/dist/{chunk-Y3ZLRGHC.js → chunk-CZNV3VXM.js} +16 -16
  51. package/dist/chunk-DGUM43GV.js +11 -0
  52. package/dist/chunk-DGUM43GV.js.map +1 -0
  53. package/dist/{chunk-JIMUDQN3.js → chunk-FBUU43PM.js} +2 -2
  54. package/dist/chunk-H75VXTGT.js +219 -0
  55. package/dist/chunk-H75VXTGT.js.map +1 -0
  56. package/dist/{chunk-RIIOUTGM.js → chunk-HKK2JD7Z.js} +12 -9
  57. package/dist/chunk-HKK2JD7Z.js.map +1 -0
  58. package/dist/{chunk-D6BG7U6A.js → chunk-IXZBUWSP.js} +3 -3
  59. package/dist/chunk-PC3BTTBE.js +3217 -0
  60. package/dist/chunk-PC3BTTBE.js.map +1 -0
  61. package/dist/chunk-QFY4AFY4.js +879 -0
  62. package/dist/chunk-QFY4AFY4.js.map +1 -0
  63. package/dist/{chunk-DK554R7P.js → chunk-S6HXABGL.js} +2 -1
  64. package/dist/chunk-S6HXABGL.js.map +1 -0
  65. package/dist/{chunk-PKVBHGBM.js → chunk-SS2A464I.js} +7 -7
  66. package/dist/chunk-UT3MHJCW.js +49 -0
  67. package/dist/chunk-UT3MHJCW.js.map +1 -0
  68. package/dist/{chunk-G4M3NHRJ.js → chunk-V7CIJE4B.js} +3 -3
  69. package/dist/{chunk-TEW47IVR.js → chunk-VPEFSDME.js} +2 -2
  70. package/dist/chunk-VTHLCFN4.js +368 -0
  71. package/dist/chunk-VTHLCFN4.js.map +1 -0
  72. package/dist/{chunk-SWTLEQL6.js → chunk-WQIA3GFB.js} +2 -2
  73. package/dist/chunk-WQIA3GFB.js.map +1 -0
  74. package/dist/{chunk-I3IHOHCJ.js → chunk-XKXWK2KP.js} +108 -64
  75. package/dist/chunk-XKXWK2KP.js.map +1 -0
  76. package/dist/{chunk-COHO25CZ.js → chunk-YDSIVHRR.js} +553 -7
  77. package/dist/chunk-YDSIVHRR.js.map +1 -0
  78. package/dist/chunk-YXMZ2N6J.js +1884 -0
  79. package/dist/chunk-YXMZ2N6J.js.map +1 -0
  80. package/dist/composition/index.cjs +102 -57
  81. package/dist/composition/index.cjs.map +1 -1
  82. package/dist/composition/index.d.cts +4 -4
  83. package/dist/composition/index.d.ts +4 -4
  84. package/dist/composition/index.js +6 -5
  85. package/dist/core/index.cjs +102 -57
  86. package/dist/core/index.cjs.map +1 -1
  87. package/dist/core/index.d.cts +2 -2
  88. package/dist/core/index.d.ts +2 -2
  89. package/dist/core/index.js +5 -4
  90. package/dist/cqrs/index.cjs.map +1 -1
  91. package/dist/cqrs/index.d.cts +4 -4
  92. package/dist/cqrs/index.d.ts +4 -4
  93. package/dist/cqrs/index.js +3 -2
  94. package/dist/cqrs/index.js.map +1 -1
  95. package/dist/cqrs/messaging.cjs +312 -0
  96. package/dist/cqrs/messaging.cjs.map +1 -0
  97. package/dist/cqrs/messaging.d.cts +57 -0
  98. package/dist/cqrs/messaging.d.ts +57 -0
  99. package/dist/cqrs/messaging.js +274 -0
  100. package/dist/cqrs/messaging.js.map +1 -0
  101. package/dist/cqrs/work-queue.cjs +329 -0
  102. package/dist/cqrs/work-queue.cjs.map +1 -0
  103. package/dist/cqrs/work-queue.d.cts +90 -0
  104. package/dist/cqrs/work-queue.d.ts +90 -0
  105. package/dist/cqrs/work-queue.js +292 -0
  106. package/dist/cqrs/work-queue.js.map +1 -0
  107. package/dist/data/index.cjs +19 -0
  108. package/dist/data/index.cjs.map +1 -0
  109. package/dist/data/index.d.cts +36 -0
  110. package/dist/data/index.d.ts +36 -0
  111. package/dist/data/index.js +1 -0
  112. package/dist/data/index.js.map +1 -0
  113. package/dist/data-structures/index.cjs +105 -57
  114. package/dist/data-structures/index.cjs.map +1 -1
  115. package/dist/data-structures/index.d.cts +8 -8
  116. package/dist/data-structures/index.d.ts +8 -8
  117. package/dist/data-structures/index.js +7 -6
  118. package/dist/{describe-BZb7AS2y.d.ts → describe-BpyJuWkw.d.ts} +1 -1
  119. package/dist/{describe-Ciu8SUnN.d.cts → describe-DYMw_l1C.d.cts} +1 -1
  120. package/dist/executors/work-queue.cjs +342 -0
  121. package/dist/executors/work-queue.cjs.map +1 -0
  122. package/dist/executors/work-queue.d.cts +90 -0
  123. package/dist/executors/work-queue.d.ts +90 -0
  124. package/dist/executors/work-queue.js +306 -0
  125. package/dist/executors/work-queue.js.map +1 -0
  126. package/dist/graph/index.cjs +112 -58
  127. package/dist/graph/index.cjs.map +1 -1
  128. package/dist/graph/index.d.cts +9 -9
  129. package/dist/graph/index.d.ts +9 -9
  130. package/dist/graph/index.js +18 -17
  131. package/dist/{graph-DfdF9zy4.d.ts → graph-D-1-SQeC.d.ts} +3 -3
  132. package/dist/{graph-s23RbbFO.d.cts → graph-DCEWh4T2.d.cts} +3 -3
  133. package/dist/{index-CAEmLCYF.d.cts → index-72VZC0-E.d.cts} +2 -2
  134. package/dist/{index-SnzZu3x7.d.ts → index-Bmxeu0nK.d.ts} +2 -2
  135. package/dist/index.cjs +112 -58
  136. package/dist/index.cjs.map +1 -1
  137. package/dist/index.d.cts +11 -10
  138. package/dist/index.d.ts +11 -10
  139. package/dist/index.js +118 -113
  140. package/dist/inspection/boundary.cjs +98 -0
  141. package/dist/inspection/boundary.cjs.map +1 -0
  142. package/dist/inspection/boundary.d.cts +61 -0
  143. package/dist/inspection/boundary.d.ts +61 -0
  144. package/dist/inspection/boundary.js +75 -0
  145. package/dist/inspection/boundary.js.map +1 -0
  146. package/dist/{messages-BZqrGGm1.d.ts → messages-CAzUPPJM.d.cts} +10 -5
  147. package/dist/{messages-BZqrGGm1.d.cts → messages-CAzUPPJM.d.ts} +10 -5
  148. package/dist/messaging/index.cjs +744 -613
  149. package/dist/messaging/index.cjs.map +1 -1
  150. package/dist/messaging/index.d.cts +216 -160
  151. package/dist/messaging/index.d.ts +216 -160
  152. package/dist/messaging/index.js +19 -749
  153. package/dist/messaging/index.js.map +1 -1
  154. package/dist/operators/index.cjs +102 -57
  155. package/dist/operators/index.cjs.map +1 -1
  156. package/dist/operators/index.d.cts +6 -6
  157. package/dist/operators/index.d.ts +6 -6
  158. package/dist/operators/index.js +8 -7
  159. package/dist/orchestration/index.cjs +3333 -59
  160. package/dist/orchestration/index.cjs.map +1 -1
  161. package/dist/orchestration/index.d.cts +7 -4
  162. package/dist/orchestration/index.d.ts +7 -4
  163. package/dist/orchestration/index.js +49 -8
  164. package/dist/orchestration/index.js.map +1 -1
  165. package/dist/orchestration/messaging.cjs +314 -0
  166. package/dist/orchestration/messaging.cjs.map +1 -0
  167. package/dist/orchestration/messaging.d.cts +59 -0
  168. package/dist/orchestration/messaging.d.ts +59 -0
  169. package/dist/orchestration/messaging.js +276 -0
  170. package/dist/orchestration/messaging.js.map +1 -0
  171. package/dist/orchestration/work-queue.cjs +262 -0
  172. package/dist/orchestration/work-queue.cjs.map +1 -0
  173. package/dist/orchestration/work-queue.d.cts +85 -0
  174. package/dist/orchestration/work-queue.d.ts +85 -0
  175. package/dist/orchestration/work-queue.js +226 -0
  176. package/dist/orchestration/work-queue.js.map +1 -0
  177. package/dist/patterns/event-flow.cjs +447 -0
  178. package/dist/patterns/event-flow.cjs.map +1 -0
  179. package/dist/patterns/event-flow.d.cts +115 -0
  180. package/dist/patterns/event-flow.d.ts +115 -0
  181. package/dist/patterns/event-flow.js +14 -0
  182. package/dist/patterns/event-flow.js.map +1 -0
  183. package/dist/patterns/index.cjs +423 -18
  184. package/dist/patterns/index.cjs.map +1 -1
  185. package/dist/patterns/index.d.cts +8 -5
  186. package/dist/patterns/index.d.ts +8 -5
  187. package/dist/patterns/index.js +13 -4
  188. package/dist/{reactive-collection-Cb1II1Fi.d.ts → reactive-collection-BAmfrGjd.d.ts} +9 -9
  189. package/dist/{reactive-collection-BxBqtIOE.d.cts → reactive-collection-DNV0-gFi.d.cts} +9 -9
  190. package/dist/{reactive-collection-storage-D-KmjE0J.d.cts → reactive-collection-storage-BNEldDyi.d.cts} +5 -5
  191. package/dist/{reactive-collection-storage-BNiEc-Gd.d.ts → reactive-collection-storage-YZsAQsWK.d.ts} +5 -5
  192. package/dist/{reactive-map-D-g3dv8I.d.cts → reactive-map-DRD1LJ2H.d.cts} +6 -6
  193. package/dist/{reactive-map-BcB4KsQR.d.ts → reactive-map-EsDiCmJk.d.ts} +6 -6
  194. package/dist/render/index.d.cts +3 -3
  195. package/dist/render/index.d.ts +3 -3
  196. package/dist/render/index.js +1 -0
  197. package/dist/{semantic-memory-graph-BYoIlP35.d.cts → semantic-memory-graph-DKuNzuWw.d.cts} +2 -2
  198. package/dist/{semantic-memory-graph-CxyXLms3.d.ts → semantic-memory-graph-uwbwJdhe.d.ts} +2 -2
  199. package/dist/solutions/index.cjs +4478 -2
  200. package/dist/solutions/index.cjs.map +1 -1
  201. package/dist/solutions/index.d.cts +13 -519
  202. package/dist/solutions/index.d.ts +13 -519
  203. package/dist/solutions/index.js +90 -6
  204. package/dist/solutions/reactive-layout/browser/index.cjs +449 -0
  205. package/dist/solutions/reactive-layout/browser/index.cjs.map +1 -0
  206. package/dist/solutions/reactive-layout/browser/index.d.cts +44 -0
  207. package/dist/solutions/reactive-layout/browser/index.d.ts +44 -0
  208. package/dist/solutions/reactive-layout/browser/index.js +69 -0
  209. package/dist/solutions/reactive-layout/browser/index.js.map +1 -0
  210. package/dist/solutions/reactive-layout/index.cjs +4266 -0
  211. package/dist/solutions/reactive-layout/index.cjs.map +1 -0
  212. package/dist/solutions/reactive-layout/index.d.cts +581 -0
  213. package/dist/solutions/reactive-layout/index.d.ts +581 -0
  214. package/dist/solutions/reactive-layout/index.js +86 -0
  215. package/dist/solutions/reactive-layout/index.js.map +1 -0
  216. package/dist/solutions/reactive-layout/node-canvas/index.cjs +545 -0
  217. package/dist/solutions/reactive-layout/node-canvas/index.cjs.map +1 -0
  218. package/dist/solutions/reactive-layout/node-canvas/index.d.cts +54 -0
  219. package/dist/solutions/reactive-layout/node-canvas/index.d.ts +54 -0
  220. package/dist/solutions/reactive-layout/node-canvas/index.js +131 -0
  221. package/dist/solutions/reactive-layout/node-canvas/index.js.map +1 -0
  222. package/dist/solutions/reactive-layout/react-native/index.cjs +494 -0
  223. package/dist/solutions/reactive-layout/react-native/index.cjs.map +1 -0
  224. package/dist/solutions/reactive-layout/react-native/index.d.cts +51 -0
  225. package/dist/solutions/reactive-layout/react-native/index.d.ts +51 -0
  226. package/dist/solutions/reactive-layout/react-native/index.js +77 -0
  227. package/dist/solutions/reactive-layout/react-native/index.js.map +1 -0
  228. package/dist/solutions/reactive-layout/skia/index.cjs +547 -0
  229. package/dist/solutions/reactive-layout/skia/index.cjs.map +1 -0
  230. package/dist/solutions/reactive-layout/skia/index.d.cts +64 -0
  231. package/dist/solutions/reactive-layout/skia/index.d.ts +64 -0
  232. package/dist/solutions/reactive-layout/skia/index.js +70 -0
  233. package/dist/solutions/reactive-layout/skia/index.js.map +1 -0
  234. package/dist/solutions/work-item/actions.cjs +2164 -0
  235. package/dist/solutions/work-item/actions.cjs.map +1 -0
  236. package/dist/solutions/work-item/actions.d.cts +201 -0
  237. package/dist/solutions/work-item/actions.d.ts +201 -0
  238. package/dist/solutions/work-item/actions.js +1454 -0
  239. package/dist/solutions/work-item/actions.js.map +1 -0
  240. package/dist/solutions/work-item/scheduling.cjs +2454 -0
  241. package/dist/solutions/work-item/scheduling.cjs.map +1 -0
  242. package/dist/solutions/work-item/scheduling.d.cts +433 -0
  243. package/dist/solutions/work-item/scheduling.d.ts +433 -0
  244. package/dist/solutions/work-item/scheduling.js +32 -0
  245. package/dist/solutions/work-item/scheduling.js.map +1 -0
  246. package/dist/solutions/work-item/work-queue.cjs +306 -0
  247. package/dist/solutions/work-item/work-queue.cjs.map +1 -0
  248. package/dist/solutions/work-item/work-queue.d.cts +71 -0
  249. package/dist/solutions/work-item/work-queue.d.ts +71 -0
  250. package/dist/solutions/work-item/work-queue.js +270 -0
  251. package/dist/solutions/work-item/work-queue.js.map +1 -0
  252. package/dist/sources/browser.cjs +169 -57
  253. package/dist/sources/browser.cjs.map +1 -1
  254. package/dist/sources/browser.d.cts +33 -6
  255. package/dist/sources/browser.d.ts +33 -6
  256. package/dist/sources/browser.js +73 -6
  257. package/dist/sources/browser.js.map +1 -1
  258. package/dist/sources/index.cjs +102 -57
  259. package/dist/sources/index.cjs.map +1 -1
  260. package/dist/sources/index.d.cts +5 -5
  261. package/dist/sources/index.d.ts +5 -5
  262. package/dist/sources/index.js +6 -5
  263. package/dist/sources/node.cjs.map +1 -1
  264. package/dist/sources/node.d.cts +4 -4
  265. package/dist/sources/node.d.ts +4 -4
  266. package/dist/sources/node.js +2 -1
  267. package/dist/sources/node.js.map +1 -1
  268. package/dist/storage/browser.js +1 -0
  269. package/dist/storage/browser.js.map +1 -1
  270. package/dist/storage/index.d.cts +5 -5
  271. package/dist/storage/index.d.ts +5 -5
  272. package/dist/storage/index.js +5 -4
  273. package/dist/storage/node.js +1 -0
  274. package/dist/storage/node.js.map +1 -1
  275. package/dist/store-C-joM4gh.d.ts +123 -0
  276. package/dist/store-CNJwmT-h.d.cts +123 -0
  277. package/dist/testing/index.cjs +1 -0
  278. package/dist/testing/index.cjs.map +1 -1
  279. package/dist/testing/index.d.cts +1 -1
  280. package/dist/testing/index.d.ts +1 -1
  281. package/dist/testing/index.js +3 -2
  282. package/dist/{types-h12mNYbq.d.ts → types-BsJvElYZ.d.ts} +31 -31
  283. package/dist/{types-bNMSWKqF.d.cts → types-CcPIn7sy.d.cts} +31 -31
  284. package/dist/work-item-runtime-C3M79vE9.d.ts +228 -0
  285. package/dist/work-item-runtime-qaVgsL0T.d.cts +228 -0
  286. package/dist/work-queue/index.cjs +1132 -0
  287. package/dist/work-queue/index.cjs.map +1 -0
  288. package/dist/work-queue/index.d.cts +369 -0
  289. package/dist/work-queue/index.d.ts +369 -0
  290. package/dist/work-queue/index.js +1078 -0
  291. package/dist/work-queue/index.js.map +1 -0
  292. package/package.json +266 -2
  293. package/dist/chunk-COHO25CZ.js.map +0 -1
  294. package/dist/chunk-DK554R7P.js.map +0 -1
  295. package/dist/chunk-I3IHOHCJ.js.map +0 -1
  296. package/dist/chunk-RIIOUTGM.js.map +0 -1
  297. package/dist/chunk-SWTLEQL6.js.map +0 -1
  298. /package/dist/{chunk-JR4IKCR2.js.map → chunk-2747WN4Y.js.map} +0 -0
  299. /package/dist/{chunk-GDL26N2T.js.map → chunk-3KN7NJDR.js.map} +0 -0
  300. /package/dist/{chunk-DJC4CHLQ.js.map → chunk-43BKLFZU.js.map} +0 -0
  301. /package/dist/{chunk-SCRFRCGR.js.map → chunk-BSQGE5PS.js.map} +0 -0
  302. /package/dist/{chunk-Y3ZLRGHC.js.map → chunk-CZNV3VXM.js.map} +0 -0
  303. /package/dist/{chunk-JIMUDQN3.js.map → chunk-FBUU43PM.js.map} +0 -0
  304. /package/dist/{chunk-D6BG7U6A.js.map → chunk-IXZBUWSP.js.map} +0 -0
  305. /package/dist/{chunk-PKVBHGBM.js.map → chunk-SS2A464I.js.map} +0 -0
  306. /package/dist/{chunk-G4M3NHRJ.js.map → chunk-V7CIJE4B.js.map} +0 -0
  307. /package/dist/{chunk-TEW47IVR.js.map → chunk-VPEFSDME.js.map} +0 -0
@@ -22,6 +22,17 @@ var solutions_exports = {};
22
22
  __export(solutions_exports, {
23
23
  AGENTIC_MEMORY_RECORD_FRAME_FORMAT: () => AGENTIC_MEMORY_RECORD_FRAME_FORMAT,
24
24
  AGENTIC_MEMORY_RECORD_FRAME_VERSION: () => AGENTIC_MEMORY_RECORD_FRAME_VERSION,
25
+ BLOCKS_MEASUREMENT_KIND: () => BLOCKS_MEASUREMENT_KIND,
26
+ CapabilityMeasureAdapter: () => CapabilityMeasureAdapter,
27
+ CellMeasureAdapter: () => CellMeasureAdapter,
28
+ IMAGE_SIZE_MEASUREMENT_KIND: () => IMAGE_SIZE_MEASUREMENT_KIND,
29
+ ImageSizeAdapter: () => ImageSizeAdapter,
30
+ InjectedMeasureAdapter: () => InjectedMeasureAdapter,
31
+ PrecomputedMeasureAdapter: () => PrecomputedMeasureAdapter,
32
+ READINESS_MEASUREMENT_KIND: () => READINESS_MEASUREMENT_KIND,
33
+ SVG_BOUNDS_MEASUREMENT_KIND: () => SVG_BOUNDS_MEASUREMENT_KIND,
34
+ SvgBoundsAdapter: () => SvgBoundsAdapter,
35
+ TEXT_SEGMENTS_MEASUREMENT_KIND: () => TEXT_SEGMENTS_MEASUREMENT_KIND,
25
36
  agenticMemoryBundle: () => agenticMemoryBundle,
26
37
  agenticMemoryConsolidationBundle: () => agenticMemoryConsolidationBundle,
27
38
  agenticMemoryContextPackingBundle: () => agenticMemoryContextPackingBundle,
@@ -30,15 +41,91 @@ __export(solutions_exports, {
30
41
  agenticMemoryRecordFrame: () => agenticMemoryRecordFrame,
31
42
  agenticMemoryRecordFrameCodec: () => agenticMemoryRecordFrameCodec,
32
43
  agenticMemoryRetentionBundle: () => agenticMemoryRetentionBundle,
33
- assertAgenticMemoryRecordFrame: () => assertAgenticMemoryRecordFrame
44
+ analyzeAndMeasure: () => analyzeAndMeasure,
45
+ assertAgenticMemoryRecordFrame: () => assertAgenticMemoryRecordFrame,
46
+ blockAdaptersProvider: () => blockAdaptersProvider,
47
+ blockMeasurementProvider: () => blockMeasurementProvider,
48
+ capabilityAdmissionProjector: () => capabilityAdmissionProjector,
49
+ capabilityAdmissionProposal: () => capabilityAdmissionProposal,
50
+ capabilityTextMeasurements: () => capabilityTextMeasurements,
51
+ carveTextLineSlots: () => carveTextLineSlots,
52
+ cellTextMeasurements: () => cellTextMeasurements,
53
+ circleIntervalForBand: () => circleIntervalForBand,
54
+ computeBlockFlow: () => computeBlockFlow,
55
+ computeCharPositions: () => computeCharPositions,
56
+ computeFlowLines: () => computeFlowLines,
57
+ computeLineBreaks: () => computeLineBreaks,
58
+ computeTotalHeight: () => computeTotalHeight,
59
+ imageSizeMeasurements: () => imageSizeMeasurements,
60
+ injectedTextMeasurements: () => injectedTextMeasurements,
61
+ layoutNextLine: () => layoutNextLine,
62
+ measureBlock: () => measureBlock,
63
+ measureBlocks: () => measureBlocks,
64
+ mergeMeasurements: () => mergeMeasurements,
65
+ precomputedTextMeasurements: () => precomputedTextMeasurements,
66
+ reactiveBlockLayout: () => reactiveBlockLayout,
67
+ reactiveFlowLayout: () => reactiveFlowLayout,
68
+ reactiveLayout: () => reactiveLayout,
69
+ readinessMeasurements: () => readinessMeasurements,
70
+ readinessTextMeasurements: () => readinessTextMeasurements,
71
+ rectIntervalForBand: () => rectIntervalForBand,
72
+ svgBoundsMeasurements: () => svgBoundsMeasurements,
73
+ textMeasurementProvider: () => textMeasurementProvider
34
74
  });
35
75
  module.exports = __toCommonJS(solutions_exports);
36
76
 
37
77
  // src/protocol/messages.ts
38
78
  var SENTINEL = void 0;
79
+ function isInvalidErrorPayload(v) {
80
+ return v === SENTINEL || typeof v === "boolean";
81
+ }
82
+ function errorPayload(reason, fallback = "error without a valid payload") {
83
+ return isInvalidErrorPayload(reason) ? new Error(fallback) : reason;
84
+ }
85
+ var TIER_START = 0;
86
+ var TIER_CONTROL = 1;
87
+ var TIER_NOTIFICATION = 2;
88
+ var TIER_VALUE = 3;
89
+ var TIER_SETTLE = 4;
90
+ var TIER_TERMINAL = 5;
91
+ var TIER_TEARDOWN = 6;
92
+ var TIER = {
93
+ START: TIER_START,
94
+ PAUSE: TIER_CONTROL,
95
+ RESUME: TIER_CONTROL,
96
+ PULL: TIER_CONTROL,
97
+ DIRTY: TIER_NOTIFICATION,
98
+ DATA: TIER_VALUE,
99
+ RESOLVED: TIER_VALUE,
100
+ INVALIDATE: TIER_SETTLE,
101
+ COMPLETE: TIER_TERMINAL,
102
+ ERROR: TIER_TERMINAL,
103
+ TEARDOWN: TIER_TEARDOWN
104
+ };
105
+ function messageTier(t) {
106
+ return TIER[t];
107
+ }
108
+ function isDeferredTier(t) {
109
+ return TIER[t] >= TIER_VALUE;
110
+ }
111
+ function isValueTier(t) {
112
+ return TIER[t] === TIER_VALUE;
113
+ }
114
+ function isPauseBufferedTier(t) {
115
+ const tier = TIER[t];
116
+ return tier === TIER_VALUE || tier === TIER_SETTLE;
117
+ }
118
+ function isTerminal(t) {
119
+ return TIER[t] === TIER_TERMINAL;
120
+ }
121
+ function isUpAllowed(t) {
122
+ const tier = TIER[t];
123
+ return tier !== TIER_VALUE && tier !== TIER_TERMINAL;
124
+ }
39
125
 
40
126
  // src/ctx/types.ts
41
127
  var CTX_DEP_CACHE = /* @__PURE__ */ Symbol("graphrefly.ctx.depCache");
128
+ var CTX_NODE_BINDING = /* @__PURE__ */ Symbol("graphrefly.ctx.nodeBinding");
42
129
  function depWaves(ctx, depIndex) {
43
130
  return ctx.waveData[depIndex] ?? [];
44
131
  }
@@ -352,6 +439,9 @@ function strictJsonCodecFor() {
352
439
  };
353
440
  }
354
441
  var strictJsonCodec = strictJsonCodecFor();
442
+ function strictCanonicalJsonBytes(value) {
443
+ return strictJsonCodec.encode(value);
444
+ }
355
445
 
356
446
  // src/patterns/semantic-memory.ts
357
447
  function cosineSimilarity(a, b) {
@@ -3232,10 +3322,4367 @@ function safeArrayLength2(value) {
3232
3322
  function errorMessage2(error) {
3233
3323
  return error instanceof Error ? error.message : String(error);
3234
3324
  }
3325
+
3326
+ // src/solutions/capability-admission.ts
3327
+ function capabilityAdmissionProposal(opts) {
3328
+ return { kind: "capability-admission-proposal", ...opts };
3329
+ }
3330
+ function capabilityAdmissionProjector(graph, opts) {
3331
+ const name = opts.name ?? "capabilityAdmissions";
3332
+ const policyDeps = opts.admissionPolicies ?? [];
3333
+ const now = opts.now ?? Date.now;
3334
+ const runtime = graph.node(
3335
+ [opts.proposals, opts.decisions, ...policyDeps],
3336
+ (ctx) => {
3337
+ const state = ctx.state.get() ?? emptyCapabilityAdmissionState();
3338
+ for (const raw of depBatch(ctx, 0) ?? []) {
3339
+ const proposal = raw;
3340
+ const issue = capabilityAdmissionProposalIssue(proposal);
3341
+ if (issue !== void 0) {
3342
+ emitIssue(
3343
+ ctx,
3344
+ state,
3345
+ nextMalformedIssueKey(state, "proposal", proposalKeyFallback(proposal)),
3346
+ issue
3347
+ );
3348
+ continue;
3349
+ }
3350
+ if (state.proposals.has(proposal.proposalId)) {
3351
+ emitIssue(
3352
+ ctx,
3353
+ state,
3354
+ `duplicate-proposal:${proposal.proposalId}`,
3355
+ dataIssue(
3356
+ "duplicate-capability-admission-proposal",
3357
+ `CapabilityAdmissionProposal '${proposal.proposalId}' was already seen`,
3358
+ { subjectId: proposal.subjectId, refs: capabilityProposalRefs(proposal) }
3359
+ )
3360
+ );
3361
+ continue;
3362
+ }
3363
+ state.proposals.set(proposal.proposalId, proposal);
3364
+ }
3365
+ forEachPolicyDepBatch(ctx, 2, policyDeps.length, (raw) => {
3366
+ const policy = raw;
3367
+ const issue = capabilityAdmissionPolicyIssue(policy);
3368
+ if (issue !== void 0) {
3369
+ emitIssue(
3370
+ ctx,
3371
+ state,
3372
+ nextMalformedIssueKey(state, "policy", policyKeyFallback(policy)),
3373
+ issue
3374
+ );
3375
+ return;
3376
+ }
3377
+ if (state.policies.has(policy.policyId)) {
3378
+ emitIssue(
3379
+ ctx,
3380
+ state,
3381
+ `duplicate-policy:${policy.policyId}`,
3382
+ dataIssue(
3383
+ "duplicate-capability-admission-policy",
3384
+ `CapabilityAdmissionPolicy '${policy.policyId}' was already seen`,
3385
+ { refs: [ref("capability-admission-policy", policy.policyId)] }
3386
+ )
3387
+ );
3388
+ return;
3389
+ }
3390
+ state.policies.set(policy.policyId, policy);
3391
+ });
3392
+ for (const raw of depBatch(ctx, 1) ?? []) {
3393
+ const decision = raw;
3394
+ const issue = capabilityAdmissionDecisionIssue(decision);
3395
+ if (issue !== void 0) {
3396
+ emitIssue(
3397
+ ctx,
3398
+ state,
3399
+ nextMalformedIssueKey(state, "decision", decisionKeyFallback(decision)),
3400
+ issue
3401
+ );
3402
+ continue;
3403
+ }
3404
+ if (state.decisions.has(decision.decisionId)) {
3405
+ emitIssue(
3406
+ ctx,
3407
+ state,
3408
+ `duplicate-decision:${decision.decisionId}`,
3409
+ dataIssue(
3410
+ "duplicate-capability-admission-decision",
3411
+ `CapabilityAdmissionDecision '${decision.decisionId}' was already seen`,
3412
+ { refs: capabilityDecisionRefs(decision) }
3413
+ )
3414
+ );
3415
+ continue;
3416
+ }
3417
+ state.decisions.set(decision.decisionId, decision);
3418
+ }
3419
+ evaluateCapabilityAdmissions(ctx, state, now());
3420
+ ctx.state.set(state);
3421
+ },
3422
+ { name: `${name}/runtime`, factory: "capabilityAdmissionProjector", partial: true }
3423
+ );
3424
+ return {
3425
+ admissions: projectRuntimeFact(
3426
+ graph,
3427
+ runtime,
3428
+ `${name}/admissions`,
3429
+ "capabilityAdmissions",
3430
+ (fact) => fact.kind === "admission" ? fact.admission : void 0
3431
+ ),
3432
+ status: projectRuntimeFact(
3433
+ graph,
3434
+ runtime,
3435
+ `${name}/status`,
3436
+ "capabilityAdmissionStatus",
3437
+ (fact) => fact.kind === "status" ? fact.status : void 0
3438
+ ),
3439
+ issues: projectRuntimeFact(
3440
+ graph,
3441
+ runtime,
3442
+ `${name}/issues`,
3443
+ "capabilityAdmissionIssues",
3444
+ (fact) => fact.kind === "issue" ? fact.issue : void 0
3445
+ ),
3446
+ audit: projectRuntimeFact(
3447
+ graph,
3448
+ runtime,
3449
+ `${name}/audit`,
3450
+ "capabilityAdmissionAudit",
3451
+ (fact) => fact.kind === "audit" ? fact.audit : void 0
3452
+ ),
3453
+ views: capabilityAdmissionViews(graph, runtime, `${name}/views`)
3454
+ };
3455
+ }
3456
+ function evaluateCapabilityAdmissions(ctx, state, admittedAtMs) {
3457
+ for (const decision of state.decisions.values()) {
3458
+ if (state.terminalDecisionIds.has(decision.decisionId)) continue;
3459
+ evaluateCapabilityAdmission(ctx, state, decision, admittedAtMs);
3460
+ }
3461
+ }
3462
+ function evaluateCapabilityAdmission(ctx, state, decision, admittedAtMs) {
3463
+ const proposal = state.proposals.get(decision.proposalId);
3464
+ if (proposal === void 0) {
3465
+ emitIssue(
3466
+ ctx,
3467
+ state,
3468
+ `missing-proposal:${decision.decisionId}:${decision.proposalId}`,
3469
+ dataIssue(
3470
+ "missing-capability-admission-proposal",
3471
+ `CapabilityAdmissionDecision '${decision.decisionId}' references missing proposal '${decision.proposalId}'`,
3472
+ { subjectId: decision.proposalId, refs: capabilityDecisionRefs(decision) }
3473
+ )
3474
+ );
3475
+ return;
3476
+ }
3477
+ const policy = capabilityPolicyForDecision(state, decision);
3478
+ if (typeof policy === "string") {
3479
+ emitIssue(
3480
+ ctx,
3481
+ state,
3482
+ `${policy}:${decision.decisionId}:${decision.policyId ?? "missing"}`,
3483
+ dataIssue(policy, capabilityPolicyIssueMessage(policy, decision), {
3484
+ subjectId: proposal.subjectId,
3485
+ refs: capabilityDecisionRefs(decision, proposal)
3486
+ })
3487
+ );
3488
+ if (policy === "stale-capability-admission-policy-ref")
3489
+ state.terminalDecisionIds.add(decision.decisionId);
3490
+ return;
3491
+ }
3492
+ if (policy !== void 0) {
3493
+ const policyIssue = validateCapabilityAdmissionPolicy(policy, decision, proposal);
3494
+ if (policyIssue !== void 0) {
3495
+ emitIssue(
3496
+ ctx,
3497
+ state,
3498
+ `${policyIssue.code}:${decision.decisionId}:${policy.policyId}`,
3499
+ dataIssue(policyIssue.code, policyIssue.message, {
3500
+ subjectId: proposal.subjectId,
3501
+ refs: capabilityDecisionRefs(decision, proposal, policy)
3502
+ })
3503
+ );
3504
+ return;
3505
+ }
3506
+ }
3507
+ if (state.admissionsById.has(decision.admissionId)) {
3508
+ emitIssue(
3509
+ ctx,
3510
+ state,
3511
+ `duplicate-admission-id:${decision.admissionId}`,
3512
+ dataIssue(
3513
+ "duplicate-capability-admission",
3514
+ `CapabilityAdmission '${decision.admissionId}' was already emitted`,
3515
+ { subjectId: proposal.subjectId, refs: capabilityDecisionRefs(decision, proposal, policy) }
3516
+ )
3517
+ );
3518
+ state.terminalDecisionIds.add(decision.decisionId);
3519
+ return;
3520
+ }
3521
+ if (state.admissionsByProposal.has(decision.proposalId)) {
3522
+ emitIssue(
3523
+ ctx,
3524
+ state,
3525
+ `duplicate-admission-proposal:${decision.proposalId}:${decision.decisionId}`,
3526
+ dataIssue(
3527
+ "duplicate-capability-admission-proposal",
3528
+ `CapabilityAdmissionProposal '${decision.proposalId}' already has an admission decision`,
3529
+ { subjectId: proposal.subjectId, refs: capabilityDecisionRefs(decision, proposal, policy) }
3530
+ )
3531
+ );
3532
+ state.terminalDecisionIds.add(decision.decisionId);
3533
+ return;
3534
+ }
3535
+ const admissionState = admissionStateForOutcome(decision.outcome);
3536
+ if (admissionState === void 0) {
3537
+ emitIssue(
3538
+ ctx,
3539
+ state,
3540
+ `unsupported-outcome:${decision.decisionId}:${String(decision.outcome)}`,
3541
+ dataIssue(
3542
+ "unsupported-capability-admission-outcome",
3543
+ `CapabilityAdmissionDecision '${decision.decisionId}' uses unsupported outcome '${String(decision.outcome)}'`,
3544
+ { subjectId: proposal.subjectId, refs: capabilityDecisionRefs(decision, proposal, policy) }
3545
+ )
3546
+ );
3547
+ state.terminalDecisionIds.add(decision.decisionId);
3548
+ return;
3549
+ }
3550
+ const admission = {
3551
+ kind: "capability-admission",
3552
+ admissionId: decision.admissionId,
3553
+ proposalId: proposal.proposalId,
3554
+ subjectId: proposal.subjectId,
3555
+ capability: cloneCapabilityRef(proposal.capability),
3556
+ state: admissionState,
3557
+ decisionId: decision.decisionId,
3558
+ boundaryName: proposal.boundaryName,
3559
+ role: proposal.role,
3560
+ policyId: decision.policyId,
3561
+ reason: decision.reason ?? proposal.reason,
3562
+ sourceRefs: capabilityDecisionRefs(decision, proposal, policy),
3563
+ admittedAtMs: decision.decidedAtMs ?? admittedAtMs,
3564
+ metadata: {
3565
+ ...proposal.metadata ?? {},
3566
+ ...policy?.metadata ?? {},
3567
+ ...decision.metadata ?? {}
3568
+ }
3569
+ };
3570
+ state.admissionsById.set(admission.admissionId, admission);
3571
+ state.admissionsByProposal.set(admission.proposalId, admission);
3572
+ state.terminalDecisionIds.add(decision.decisionId);
3573
+ emitAdmission(ctx, state, admission);
3574
+ }
3575
+ function capabilityAdmissionViews(graph, runtime, name) {
3576
+ return graph.node(
3577
+ [runtime],
3578
+ (ctx) => {
3579
+ const state = ctx.state.get() ?? {
3580
+ admissionsByProposal: /* @__PURE__ */ new Map(),
3581
+ admissionsBySubject: /* @__PURE__ */ new Map(),
3582
+ issues: [],
3583
+ audit: []
3584
+ };
3585
+ for (const raw of depBatch(ctx, 0) ?? []) {
3586
+ const fact = raw;
3587
+ if (fact.kind === "admission") {
3588
+ const admission = fact.admission;
3589
+ state.admissionsByProposal.set(admission.proposalId, admission);
3590
+ const bySubject = state.admissionsBySubject.get(admission.subjectId) ?? [];
3591
+ bySubject.push(admission);
3592
+ state.admissionsBySubject.set(admission.subjectId, bySubject);
3593
+ }
3594
+ if (fact.kind === "issue") state.issues.push(fact.issue);
3595
+ if (fact.kind === "audit") state.audit.push(fact.audit);
3596
+ }
3597
+ ctx.state.set(state);
3598
+ ctx.down([["DATA", freezeCapabilityAdmissionViews(state)]]);
3599
+ },
3600
+ { name, factory: "capabilityAdmissionViews" }
3601
+ );
3602
+ }
3603
+ function emitAdmission(ctx, state, admission) {
3604
+ state.statusSeq += 1;
3605
+ state.auditSeq += 1;
3606
+ const statusState2 = capabilityAdmissionStatusState(admission.state);
3607
+ const status = {
3608
+ kind: "capability-admission-status",
3609
+ statusId: `${admission.subjectId}:${statusState2}:${state.statusSeq}`,
3610
+ state: statusState2,
3611
+ proposalId: admission.proposalId,
3612
+ admissionId: admission.admissionId,
3613
+ subjectId: admission.subjectId,
3614
+ capabilityId: admission.capability.id,
3615
+ capabilityKind: admission.capability.kind,
3616
+ policyId: admission.policyId,
3617
+ sourceRefs: admission.sourceRefs
3618
+ };
3619
+ const audit = {
3620
+ id: `${admission.subjectId}:${statusState2}:${state.auditSeq}`,
3621
+ kind: `capability-admission-${admission.state}`,
3622
+ subjectId: admission.subjectId,
3623
+ sourceRefs: admission.sourceRefs,
3624
+ metadata: {
3625
+ admissionId: admission.admissionId,
3626
+ capabilityId: admission.capability.id,
3627
+ capabilityKind: admission.capability.kind,
3628
+ policyId: admission.policyId,
3629
+ proposalId: admission.proposalId
3630
+ }
3631
+ };
3632
+ ctx.down([
3633
+ ["DATA", { kind: "admission", admission }],
3634
+ ["DATA", { kind: "status", status }],
3635
+ ["DATA", { kind: "audit", audit }]
3636
+ ]);
3637
+ }
3638
+ function emitIssue(ctx, state, key, issue) {
3639
+ if (state.issueKeys.has(key)) return;
3640
+ state.issueKeys.add(key);
3641
+ state.statusSeq += 1;
3642
+ state.auditSeq += 1;
3643
+ const status = {
3644
+ kind: "capability-admission-status",
3645
+ statusId: `capability-admission-issue:${state.statusSeq}`,
3646
+ state: "capability-admission-issue",
3647
+ subjectId: issue.subjectId,
3648
+ issues: [issue]
3649
+ };
3650
+ const audit = {
3651
+ id: `capability-admission-issue:${state.auditSeq}`,
3652
+ kind: "capability-admission-issue",
3653
+ subjectId: issue.subjectId,
3654
+ message: issue.message,
3655
+ issueCode: issue.code
3656
+ };
3657
+ ctx.down([
3658
+ ["DATA", { kind: "issue", issue }],
3659
+ ["DATA", { kind: "status", status }],
3660
+ ["DATA", { kind: "audit", audit }]
3661
+ ]);
3662
+ }
3663
+ function capabilityAdmissionProposalIssue(proposal) {
3664
+ if (proposal.kind !== "capability-admission-proposal" || typeof proposal.proposalId !== "string" || proposal.proposalId.length === 0 || typeof proposal.subjectId !== "string" || proposal.subjectId.length === 0 || !isBoundaryCapabilityRef(proposal.capability)) {
3665
+ return dataIssue(
3666
+ "malformed-capability-admission-proposal",
3667
+ "CapabilityAdmissionProposal requires proposalId, subjectId, and a D348 BoundaryCapabilityRef",
3668
+ { subjectId: typeof proposal.subjectId === "string" ? proposal.subjectId : void 0 }
3669
+ );
3670
+ }
3671
+ return void 0;
3672
+ }
3673
+ function capabilityAdmissionPolicyIssue(policy) {
3674
+ if (policy.kind !== "capability-admission-policy" || typeof policy.policyId !== "string" || policy.policyId.length === 0 || policy.capabilityIds !== void 0 && (!Array.isArray(policy.capabilityIds) || !policy.capabilityIds.every((id) => typeof id === "string" && id.length > 0)) || policy.capabilityKinds !== void 0 && (!Array.isArray(policy.capabilityKinds) || !policy.capabilityKinds.every(isBoundaryCapabilityKind)) || policy.requiredOnly !== void 0 && typeof policy.requiredOnly !== "boolean" || policy.allowedOutcomes !== void 0 && (!Array.isArray(policy.allowedOutcomes) || !policy.allowedOutcomes.every(isCapabilityAdmissionOutcome))) {
3675
+ return dataIssue(
3676
+ "malformed-capability-admission-policy",
3677
+ "CapabilityAdmissionPolicy requires a policyId and D348 capability/outcome filters",
3678
+ { subjectId: typeof policy.policyId === "string" ? policy.policyId : void 0 }
3679
+ );
3680
+ }
3681
+ return void 0;
3682
+ }
3683
+ function capabilityAdmissionDecisionIssue(decision) {
3684
+ if (decision.kind !== "capability-admission-decision" || typeof decision.decisionId !== "string" || decision.decisionId.length === 0 || typeof decision.admissionId !== "string" || decision.admissionId.length === 0 || typeof decision.proposalId !== "string" || decision.proposalId.length === 0 || !isCapabilityAdmissionOutcome(decision.outcome) || decision.policyId !== void 0 && (typeof decision.policyId !== "string" || decision.policyId.length === 0)) {
3685
+ return dataIssue(
3686
+ "malformed-capability-admission-decision",
3687
+ "CapabilityAdmissionDecision requires decisionId, admissionId, proposalId, and allow/block/defer outcome",
3688
+ { subjectId: typeof decision.proposalId === "string" ? decision.proposalId : void 0 }
3689
+ );
3690
+ }
3691
+ return void 0;
3692
+ }
3693
+ function validateCapabilityAdmissionPolicy(policy, decision, proposal) {
3694
+ if (policy.capabilityIds !== void 0 && !policy.capabilityIds.includes(proposal.capability.id)) {
3695
+ return {
3696
+ code: "capability-admission-policy-mismatch",
3697
+ message: `CapabilityAdmissionPolicy '${policy.policyId}' does not apply to capability '${proposal.capability.id}'`
3698
+ };
3699
+ }
3700
+ if (policy.capabilityKinds !== void 0 && !policy.capabilityKinds.includes(proposal.capability.kind)) {
3701
+ return {
3702
+ code: "capability-admission-policy-mismatch",
3703
+ message: `CapabilityAdmissionPolicy '${policy.policyId}' does not apply to capability kind '${proposal.capability.kind}'`
3704
+ };
3705
+ }
3706
+ if (policy.requiredOnly === true && !proposal.capability.required) {
3707
+ return {
3708
+ code: "capability-admission-policy-mismatch",
3709
+ message: `CapabilityAdmissionPolicy '${policy.policyId}' applies only to required capabilities`
3710
+ };
3711
+ }
3712
+ if (policy.allowedOutcomes !== void 0 && !policy.allowedOutcomes.includes(decision.outcome)) {
3713
+ return {
3714
+ code: "unsupported-capability-admission-outcome",
3715
+ message: `CapabilityAdmissionPolicy '${policy.policyId}' does not allow outcome '${decision.outcome}'`
3716
+ };
3717
+ }
3718
+ return void 0;
3719
+ }
3720
+ function capabilityPolicyForDecision(state, decision) {
3721
+ if (decision.policyId === void 0) return void 0;
3722
+ const stalePolicyRef = (decision.sourceRefs ?? []).find(
3723
+ (sourceRef) => sourceRef.kind === "capability-admission-policy" && sourceRef.id !== decision.policyId
3724
+ );
3725
+ if (stalePolicyRef !== void 0) return "stale-capability-admission-policy-ref";
3726
+ return state.policies.get(decision.policyId) ?? "missing-capability-admission-policy";
3727
+ }
3728
+ function capabilityPolicyIssueMessage(code, decision) {
3729
+ if (code === "missing-capability-admission-policy") {
3730
+ return `CapabilityAdmissionPolicy '${decision.policyId}' was referenced but not present`;
3731
+ }
3732
+ return `CapabilityAdmissionDecision '${decision.decisionId}' carries a stale admission policy source ref`;
3733
+ }
3734
+ function admissionStateForOutcome(outcome) {
3735
+ if (outcome === "allow") return "allowed";
3736
+ if (outcome === "block") return "blocked";
3737
+ if (outcome === "defer") return "deferred";
3738
+ return void 0;
3739
+ }
3740
+ function capabilityAdmissionStatusState(state) {
3741
+ if (state === "allowed") return "capability-admission-allowed";
3742
+ if (state === "blocked") return "capability-admission-blocked";
3743
+ return "capability-admission-deferred";
3744
+ }
3745
+ function cloneCapabilityRef(ref2) {
3746
+ return {
3747
+ id: ref2.id,
3748
+ kind: ref2.kind,
3749
+ required: ref2.required,
3750
+ ...ref2.sourceRefs === void 0 ? {} : { sourceRefs: [...ref2.sourceRefs] }
3751
+ };
3752
+ }
3753
+ function isBoundaryCapabilityRef(value) {
3754
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
3755
+ const record = value;
3756
+ return typeof record.id === "string" && record.id.length > 0 && isBoundaryCapabilityKind(record.kind) && typeof record.required === "boolean" && (record.sourceRefs === void 0 || Array.isArray(record.sourceRefs) && record.sourceRefs.every((sourceRef) => typeof sourceRef === "string"));
3757
+ }
3758
+ function isBoundaryCapabilityKind(value) {
3759
+ return value === "auth" || value === "permission" || value === "config" || value === "resource";
3760
+ }
3761
+ function isCapabilityAdmissionOutcome(value) {
3762
+ return value === "allow" || value === "block" || value === "defer";
3763
+ }
3764
+ function proposalKeyFallback(proposal) {
3765
+ return typeof proposal.proposalId === "string" && proposal.proposalId.length > 0 ? proposal.proposalId : `${stringValue(proposal.subjectId) ?? "unknown"}:${capabilityKeyFallback(proposal.capability)}`;
3766
+ }
3767
+ function policyKeyFallback(policy) {
3768
+ return typeof policy.policyId === "string" && policy.policyId.length > 0 ? policy.policyId : "unknown";
3769
+ }
3770
+ function decisionKeyFallback(decision) {
3771
+ return typeof decision.decisionId === "string" && decision.decisionId.length > 0 ? decision.decisionId : `${stringValue(decision.proposalId) ?? "unknown"}:${stringValue(decision.admissionId) ?? "unknown"}`;
3772
+ }
3773
+ function capabilityKeyFallback(value) {
3774
+ if (!isBoundaryCapabilityRef(value)) return "unknown-capability";
3775
+ return `${value.kind}:${value.id}`;
3776
+ }
3777
+ function nextMalformedIssueKey(state, kind, fallback) {
3778
+ state.issueSeq += 1;
3779
+ return `malformed-${kind}:${state.issueSeq}:${fallback}`;
3780
+ }
3781
+ function stringValue(value) {
3782
+ return typeof value === "string" && value.length > 0 ? value : void 0;
3783
+ }
3784
+ function capabilityProposalRefs(proposal) {
3785
+ return uniqueSourceRefs([
3786
+ ref("capability-admission-proposal", proposal.proposalId),
3787
+ ref("boundary-capability", `${proposal.capability.kind}:${proposal.capability.id}`),
3788
+ ...proposal.sourceRefs ?? []
3789
+ ]);
3790
+ }
3791
+ function capabilityDecisionRefs(decision, proposal, policy) {
3792
+ return uniqueSourceRefs([
3793
+ ref("capability-admission-decision", decision.decisionId),
3794
+ ref("capability-admission", decision.admissionId),
3795
+ ref("capability-admission-proposal", decision.proposalId),
3796
+ ...proposal === void 0 ? [] : capabilityProposalRefs(proposal),
3797
+ ...decision.policyId === void 0 ? [] : [ref("capability-admission-policy", decision.policyId)],
3798
+ ...policy === void 0 ? [] : [ref("capability-admission-policy", policy.policyId)],
3799
+ ...decision.sourceRefs ?? []
3800
+ ]);
3801
+ }
3802
+ function forEachPolicyDepBatch(ctx, start, count, fn) {
3803
+ for (let i = 0; i < count; i += 1) {
3804
+ for (const value of depBatch(ctx, start + i) ?? []) fn(value);
3805
+ }
3806
+ }
3807
+ function projectRuntimeFact(graph, runtime, name, factory, pick) {
3808
+ return graph.node(
3809
+ [runtime],
3810
+ (ctx) => {
3811
+ for (const fact of depBatch(ctx, 0) ?? []) {
3812
+ const typed = fact;
3813
+ const value = pick(typed);
3814
+ if (value !== void 0) ctx.down([["DATA", value]]);
3815
+ }
3816
+ },
3817
+ { name, factory }
3818
+ );
3819
+ }
3820
+ function freezeCapabilityAdmissionViews(state) {
3821
+ return {
3822
+ admissionsByProposal: new Map(state.admissionsByProposal),
3823
+ admissionsBySubject: new Map(
3824
+ Array.from(state.admissionsBySubject, ([key, value]) => [key, Object.freeze([...value])])
3825
+ ),
3826
+ issues: Object.freeze([...state.issues]),
3827
+ audit: Object.freeze([...state.audit])
3828
+ };
3829
+ }
3830
+ function emptyCapabilityAdmissionState() {
3831
+ return {
3832
+ proposals: /* @__PURE__ */ new Map(),
3833
+ policies: /* @__PURE__ */ new Map(),
3834
+ decisions: /* @__PURE__ */ new Map(),
3835
+ admissionsById: /* @__PURE__ */ new Map(),
3836
+ admissionsByProposal: /* @__PURE__ */ new Map(),
3837
+ terminalDecisionIds: /* @__PURE__ */ new Set(),
3838
+ issueKeys: /* @__PURE__ */ new Set(),
3839
+ issueSeq: 0,
3840
+ statusSeq: 0,
3841
+ auditSeq: 0
3842
+ };
3843
+ }
3844
+ function dataIssue(code, message, opts = {}) {
3845
+ return {
3846
+ kind: "issue",
3847
+ code,
3848
+ message,
3849
+ severity: "error",
3850
+ subjectId: opts.subjectId,
3851
+ refs: opts.refs?.map((sourceRef) => `${sourceRef.kind}:${sourceRef.id}`),
3852
+ details: opts.details
3853
+ };
3854
+ }
3855
+ function uniqueSourceRefs(sourceRefs) {
3856
+ const seen = /* @__PURE__ */ new Set();
3857
+ const out = [];
3858
+ for (const sourceRef of sourceRefs) {
3859
+ const key = `${sourceRef.kind}:${sourceRef.id}:${JSON.stringify(sourceRef.metadata ?? {})}`;
3860
+ if (seen.has(key)) continue;
3861
+ seen.add(key);
3862
+ out.push(sourceRef);
3863
+ }
3864
+ return out;
3865
+ }
3866
+ function ref(kind, id) {
3867
+ return { kind, id };
3868
+ }
3869
+
3870
+ // src/batch/boundary.ts
3871
+ var depth = 0;
3872
+ var pendingCores = [];
3873
+ var pendingHead = 0;
3874
+ function enterWave() {
3875
+ depth++;
3876
+ }
3877
+ function exitWave() {
3878
+ depth--;
3879
+ if (depth === 0 && pendingHead < pendingCores.length) drain();
3880
+ }
3881
+ function deferRewire(core, apply, options = {}) {
3882
+ core.enqueueBoundaryTask({ apply, batchToken: options.batchToken, isReady: options.isReady });
3883
+ pendingCores.push(core);
3884
+ }
3885
+ function scheduleBoundaryDrain(core) {
3886
+ for (let i = 0; i < core.boundaryTaskCount(); i++) pendingCores.push(core);
3887
+ if (depth === 0 && pendingHead < pendingCores.length) drain();
3888
+ }
3889
+ function drain() {
3890
+ let escaped = null;
3891
+ while (pendingHead < pendingCores.length) {
3892
+ const core = pendingCores[pendingHead++];
3893
+ const task = core.shiftBoundaryTask();
3894
+ if (task === void 0) continue;
3895
+ if (task.batchToken !== void 0) {
3896
+ const committed = task.batchToken.committed === true;
3897
+ if (!committed) continue;
3898
+ }
3899
+ if (task.isReady !== void 0 && !task.isReady()) {
3900
+ core.unshiftBoundaryTask(task);
3901
+ continue;
3902
+ }
3903
+ depth++;
3904
+ try {
3905
+ task.apply();
3906
+ } catch (e) {
3907
+ if (escaped === null) escaped = { e };
3908
+ } finally {
3909
+ depth--;
3910
+ }
3911
+ }
3912
+ pendingCores.length = 0;
3913
+ pendingHead = 0;
3914
+ if (escaped !== null) throw escaped.e;
3915
+ }
3916
+
3917
+ // src/batch/batch.ts
3918
+ var active = null;
3919
+ var boundaryOwner = null;
3920
+ function currentBatch() {
3921
+ return active !== null;
3922
+ }
3923
+ function currentBoundaryBatchToken() {
3924
+ return active ?? boundaryOwner ?? void 0;
3925
+ }
3926
+ function deferToBatch(target, tier3Wave) {
3927
+ if (active === null) return false;
3928
+ if (!active.deferred.has(target)) active.order.push(target);
3929
+ active.deferred.set(target, tier3Wave);
3930
+ return true;
3931
+ }
3932
+ function deferAfterBatchForTarget(target, fn) {
3933
+ if (active === null || !active.deferred.has(target)) return false;
3934
+ const owner = active;
3935
+ target.__deferBoundary(() => {
3936
+ if (owner.committed) fn();
3937
+ }, owner);
3938
+ return true;
3939
+ }
3940
+
3941
+ // src/dispatcher/index.ts
3942
+ var PoolTable = class {
3943
+ constructor(kind) {
3944
+ this.kind = kind;
3945
+ }
3946
+ fns = [];
3947
+ free = [];
3948
+ register(fn) {
3949
+ const reused = this.free.pop();
3950
+ if (reused !== void 0) {
3951
+ this.fns[reused] = fn;
3952
+ return reused;
3953
+ }
3954
+ const id = this.fns.length;
3955
+ this.fns.push(fn);
3956
+ return id;
3957
+ }
3958
+ unregister(handleId) {
3959
+ if (this.fns[handleId] === void 0) return;
3960
+ this.fns[handleId] = void 0;
3961
+ this.free.push(handleId);
3962
+ }
3963
+ invoke(handleId, ctx) {
3964
+ this.fns[handleId](ctx);
3965
+ }
3966
+ };
3967
+ var statKey = (h) => `${h.poolId}:${h.handleId}`;
3968
+ var Dispatcher = class {
3969
+ pools = [];
3970
+ syncPoolId;
3971
+ asyncPoolId;
3972
+ // opt-in profile recorder (default OFF → zero overhead, F-PERF).
3973
+ _recording = false;
3974
+ _stats = /* @__PURE__ */ new Map();
3975
+ _totalInvokes = 0;
3976
+ constructor() {
3977
+ this.syncPoolId = this.addPool(new PoolTable("sync"));
3978
+ this.asyncPoolId = this.addPool(new PoolTable("async"));
3979
+ }
3980
+ /** Turn the profile recorder on/off (D39). Off = zero overhead on invoke. */
3981
+ setRecording(on) {
3982
+ this._recording = on;
3983
+ }
3984
+ /** Reset accumulated profiling counters. */
3985
+ clearStats() {
3986
+ this._stats.clear();
3987
+ this._totalInvokes = 0;
3988
+ }
3989
+ /** Read a handle's accumulated counters (undefined if it never ran while recording). */
3990
+ statFor(handle) {
3991
+ return this._stats.get(statKey(handle));
3992
+ }
3993
+ /** Total fn invocations recorded across the dispatcher. */
3994
+ get totalInvokes() {
3995
+ return this._totalInvokes;
3996
+ }
3997
+ addPool(pool) {
3998
+ const id = this.pools.length;
3999
+ this.pools.push(pool);
4000
+ return id;
4001
+ }
4002
+ /** Register a fn in a pool, returning its Handle. Default pool = sync (R-sync-core). */
4003
+ register(fn, pool = "sync") {
4004
+ const poolId = pool === "sync" ? this.syncPoolId : pool === "async" ? this.asyncPoolId : pool;
4005
+ const handleId = this.pools[poolId].register(fn);
4006
+ return { poolId, handleId };
4007
+ }
4008
+ /**
4009
+ * Release a handle (B15): frees the pool slot (closure GC'd, id reusable) and drops any
4010
+ * accumulated profile stat so a reused id never inherits the previous tenant's counters.
4011
+ * Called on rewire fn-swap (node._rewire) — the old handle is dropped before the node
4012
+ * adopts the new one. Idempotent. NOT called on deactivate (a node's handle survives
4013
+ * activate↔deactivate and is reused on reactivation; only a rewire swaps it).
4014
+ */
4015
+ unregister(handle) {
4016
+ this.pools[handle.poolId].unregister(handle.handleId);
4017
+ this._stats.delete(statKey(handle));
4018
+ }
4019
+ /** Uniform sync-void invoke (R-sync-core / R-dispatch-all). */
4020
+ invoke(handle, ctx) {
4021
+ if (!this._recording) {
4022
+ this.pools[handle.poolId].invoke(handle.handleId, ctx);
4023
+ return;
4024
+ }
4025
+ this._totalInvokes++;
4026
+ const t0 = performance.now();
4027
+ try {
4028
+ this.pools[handle.poolId].invoke(handle.handleId, ctx);
4029
+ } finally {
4030
+ const dur = (performance.now() - t0) * 1e6;
4031
+ const key = statKey(handle);
4032
+ const s = this._stats.get(key) ?? {
4033
+ invokes: 0,
4034
+ totalDurationNs: 0,
4035
+ lastDurationNs: 0
4036
+ };
4037
+ s.invokes++;
4038
+ s.lastDurationNs = dur;
4039
+ s.totalDurationNs += dur;
4040
+ this._stats.set(key, s);
4041
+ }
4042
+ }
4043
+ poolKind(poolId) {
4044
+ return this.pools[poolId].kind;
4045
+ }
4046
+ };
4047
+ var defaultDispatcher = new Dispatcher();
4048
+
4049
+ // src/graph/environment.ts
4050
+ var EnvironmentDrivers = class _EnvironmentDrivers {
4051
+ process;
4052
+ http;
4053
+ sse;
4054
+ websocket;
4055
+ webhook;
4056
+ constructor(init = {}) {
4057
+ this.process = init.process;
4058
+ this.http = init.http;
4059
+ this.sse = init.sse;
4060
+ this.websocket = init.websocket;
4061
+ this.webhook = init.webhook;
4062
+ Object.freeze(this);
4063
+ }
4064
+ static empty() {
4065
+ return EMPTY_ENVIRONMENT;
4066
+ }
4067
+ withProcess(driver) {
4068
+ return new _EnvironmentDrivers({ ...this, process: driver });
4069
+ }
4070
+ withHttp(driver) {
4071
+ return new _EnvironmentDrivers({ ...this, http: driver });
4072
+ }
4073
+ withSse(driver) {
4074
+ return new _EnvironmentDrivers({ ...this, sse: driver });
4075
+ }
4076
+ withWebSocket(driver) {
4077
+ return new _EnvironmentDrivers({ ...this, websocket: driver });
4078
+ }
4079
+ withWebhook(driver) {
4080
+ return new _EnvironmentDrivers({ ...this, webhook: driver });
4081
+ }
4082
+ processDriver() {
4083
+ return this.process;
4084
+ }
4085
+ httpDriver() {
4086
+ return this.http;
4087
+ }
4088
+ sseDriver() {
4089
+ return this.sse;
4090
+ }
4091
+ webSocketDriver() {
4092
+ return this.websocket;
4093
+ }
4094
+ webhookDriver() {
4095
+ return this.webhook;
4096
+ }
4097
+ };
4098
+ var EMPTY_ENVIRONMENT = new EnvironmentDrivers();
4099
+
4100
+ // src/node/core.ts
4101
+ var NodeCore = class {
4102
+ nextId = 0;
4103
+ slots = [];
4104
+ values = [];
4105
+ waves = [];
4106
+ controls = [];
4107
+ lifecycles = [];
4108
+ depStates = [];
4109
+ privateStates = [];
4110
+ hooks = [];
4111
+ syncCtxs = [];
4112
+ versionStates = [];
4113
+ boundary = { queue: [], head: 0 };
4114
+ createSlot(slot, state) {
4115
+ const id = this.nextId++;
4116
+ const full = { ...slot, id };
4117
+ this.slots[id] = full;
4118
+ this.depStates[id] = state.dep;
4119
+ this.lifecycles[id] = state.lifecycle;
4120
+ this.values[id] = state.value;
4121
+ this.waves[id] = state.wave;
4122
+ this.controls[id] = state.control;
4123
+ this.privateStates[id] = state.privateState;
4124
+ this.hooks[id] = state.hooks;
4125
+ this.syncCtxs[id] = state.syncCtx;
4126
+ this.versionStates[id] = state.version;
4127
+ return { id, slot: full };
4128
+ }
4129
+ get(id) {
4130
+ const slot = this.slots[id];
4131
+ if (slot === void 0) throw new Error("NodeCore: unknown node slot");
4132
+ return slot;
4133
+ }
4134
+ getValue(id) {
4135
+ const value = this.values[id];
4136
+ if (value === void 0) throw new Error("NodeCore: unknown node value state");
4137
+ return value;
4138
+ }
4139
+ getWave(id) {
4140
+ const wave = this.waves[id];
4141
+ if (wave === void 0) throw new Error("NodeCore: unknown node wave state");
4142
+ return wave;
4143
+ }
4144
+ getControl(id) {
4145
+ const control = this.controls[id];
4146
+ if (control === void 0) throw new Error("NodeCore: unknown node control state");
4147
+ return control;
4148
+ }
4149
+ getLifecycle(id) {
4150
+ const lifecycle = this.lifecycles[id];
4151
+ if (lifecycle === void 0) throw new Error("NodeCore: unknown node lifecycle state");
4152
+ return lifecycle;
4153
+ }
4154
+ getDep(id) {
4155
+ const dep = this.depStates[id];
4156
+ if (dep === void 0) throw new Error("NodeCore: unknown node dep state");
4157
+ return dep;
4158
+ }
4159
+ getPrivateState(id) {
4160
+ const state = this.privateStates[id];
4161
+ if (state === void 0) throw new Error("NodeCore: unknown node private state");
4162
+ return state;
4163
+ }
4164
+ getHooks(id) {
4165
+ const hooks = this.hooks[id];
4166
+ if (hooks === void 0) throw new Error("NodeCore: unknown node cleanup hooks");
4167
+ return hooks;
4168
+ }
4169
+ getSyncCtx(id) {
4170
+ const state = this.syncCtxs[id];
4171
+ if (state === void 0) throw new Error("NodeCore: unknown node ctx state");
4172
+ return state;
4173
+ }
4174
+ getVersion(id) {
4175
+ const state = this.versionStates[id];
4176
+ if (state === void 0) throw new Error("NodeCore: unknown node version state");
4177
+ return state;
4178
+ }
4179
+ /** @internal D122: release graph-owned ephemeral node runtime state from core retention. */
4180
+ releaseSlot(id) {
4181
+ this.slots[id] = void 0;
4182
+ this.depStates[id] = void 0;
4183
+ this.lifecycles[id] = void 0;
4184
+ this.values[id] = void 0;
4185
+ this.waves[id] = void 0;
4186
+ this.controls[id] = void 0;
4187
+ this.privateStates[id] = void 0;
4188
+ this.hooks[id] = void 0;
4189
+ this.syncCtxs[id] = void 0;
4190
+ this.versionStates[id] = void 0;
4191
+ }
4192
+ /** @internal B49: graph-local deferred-boundary queue (rewireNext/upNext/batch-after-commit). */
4193
+ enqueueBoundaryTask(task) {
4194
+ this.boundary.queue.push(task);
4195
+ }
4196
+ /** @internal */
4197
+ hasBoundaryTasks() {
4198
+ return this.boundary.head < this.boundary.queue.length;
4199
+ }
4200
+ /** @internal */
4201
+ boundaryTaskCount() {
4202
+ return this.boundary.queue.length - this.boundary.head;
4203
+ }
4204
+ /** @internal */
4205
+ shiftBoundaryTask() {
4206
+ if (!this.hasBoundaryTasks()) {
4207
+ this.boundary.queue = [];
4208
+ this.boundary.head = 0;
4209
+ return void 0;
4210
+ }
4211
+ const task = this.boundary.queue[this.boundary.head++];
4212
+ if (!this.hasBoundaryTasks()) {
4213
+ this.boundary.queue = [];
4214
+ this.boundary.head = 0;
4215
+ }
4216
+ return task;
4217
+ }
4218
+ /** @internal Put a not-yet-ready task back at this core's FIFO head. */
4219
+ unshiftBoundaryTask(task) {
4220
+ const remaining = this.boundary.queue.slice(this.boundary.head);
4221
+ this.boundary.queue = [task, ...remaining];
4222
+ this.boundary.head = 0;
4223
+ }
4224
+ /** @internal D110: discard all pending tasks caused by an uncommitted batch. */
4225
+ dropBoundaryTasksForBatch(batchToken) {
4226
+ const remaining = this.boundary.queue.slice(this.boundary.head).filter((task) => task.batchToken !== batchToken);
4227
+ this.boundary.queue = remaining;
4228
+ this.boundary.head = 0;
4229
+ }
4230
+ };
4231
+ function makeDepBookkeeping(depCount) {
4232
+ return {
4233
+ batch: new Array(depCount).fill(null),
4234
+ waveData: Array.from({ length: depCount }, () => []),
4235
+ waveTokens: new Array(depCount).fill(void 0),
4236
+ prev: new Array(depCount).fill(SENTINEL),
4237
+ hasData: new Array(depCount).fill(false),
4238
+ dirty: new Array(depCount).fill(false),
4239
+ tier: new Array(depCount).fill(0),
4240
+ terminal: new Array(depCount).fill(void 0),
4241
+ terminalInput: new Array(depCount).fill(void 0),
4242
+ unsubs: [],
4243
+ idxBoxes: []
4244
+ };
4245
+ }
4246
+
4247
+ // src/node/versioning.ts
4248
+ var ABSENT_V1_SEED = Object.freeze({
4249
+ "@graphrefly/node-version": "v1-absent"
4250
+ });
4251
+ function fnv1a64(input) {
4252
+ let hash = 0xcbf29ce484222325n;
4253
+ const prime = 0x100000001b3n;
4254
+ for (const byte of input) {
4255
+ hash ^= BigInt(byte);
4256
+ hash = BigInt.asUintN(64, hash * prime);
4257
+ }
4258
+ return hash.toString(16).padStart(16, "0");
4259
+ }
4260
+ function defaultNodeVersionHash(bytes) {
4261
+ return `fnv1a64:${fnv1a64(bytes)}`;
4262
+ }
4263
+ function computeV1Cid(policy, value) {
4264
+ return policy.hash(strictCanonicalJsonBytes(value));
4265
+ }
4266
+ function assertNodeVersionDataCompatible(policy, value) {
4267
+ if (!policy.enabled || policy.level === 0) return;
4268
+ strictCanonicalJsonBytes(value);
4269
+ }
4270
+ function snapshotNodeVersionData(policy, value) {
4271
+ if (!policy.enabled || policy.level === 0) return value;
4272
+ const bytes = strictCanonicalJsonBytes(value);
4273
+ return strictJsonCodec.decode(bytes);
4274
+ }
4275
+ function resolveNodeVersioningPolicy(policy) {
4276
+ if (policy === false) return { enabled: false };
4277
+ if (policy === void 0 || policy === 0) return { enabled: true, level: 0 };
4278
+ if (policy === 1) return { enabled: true, level: 1, hash: defaultNodeVersionHash };
4279
+ if (typeof policy === "object" && policy !== null) {
4280
+ if (policy.level === 0) return { enabled: true, level: 0 };
4281
+ if (policy.level === 1) {
4282
+ return { enabled: true, level: 1, hash: policy.hash ?? defaultNodeVersionHash };
4283
+ }
4284
+ }
4285
+ throw new Error("node: versioning level must be 0 or 1; V2/V3 are not locked yet (D109)");
4286
+ }
4287
+ function createNodeVersion(policy, initialValue = ABSENT_V1_SEED) {
4288
+ if (!policy.enabled) return void 0;
4289
+ if (policy.level === 0) return Object.freeze({ level: 0, counter: 0 });
4290
+ return Object.freeze({
4291
+ level: 1,
4292
+ counter: 0,
4293
+ cid: computeV1Cid(policy, initialValue),
4294
+ prev: null
4295
+ });
4296
+ }
4297
+ function advanceNodeVersion(current, policy, value) {
4298
+ if (!policy.enabled) return void 0;
4299
+ if (current === void 0) return createNodeVersion(policy, value);
4300
+ if (policy.level === 0) {
4301
+ return Object.freeze({ level: 0, counter: current.counter + 1 });
4302
+ }
4303
+ const previous = current.level === 1 ? current.cid : null;
4304
+ return Object.freeze({
4305
+ level: 1,
4306
+ counter: current.counter + 1,
4307
+ cid: computeV1Cid(policy, value),
4308
+ prev: previous
4309
+ });
4310
+ }
4311
+ function cloneNodeVersion(version) {
4312
+ if (version === void 0) return void 0;
4313
+ if (version.level === 0) return Object.freeze({ level: 0, counter: version.counter });
4314
+ return Object.freeze({
4315
+ level: 1,
4316
+ counter: version.counter,
4317
+ cid: version.cid,
4318
+ prev: version.prev
4319
+ });
4320
+ }
4321
+ function restoredV1Cid(policy, hasData, cache) {
4322
+ return computeV1Cid(policy, hasData ? cache : ABSENT_V1_SEED);
4323
+ }
4324
+
4325
+ // src/node/node.ts
4326
+ var constructingCore;
4327
+ var constructingEnvironment;
4328
+ var ownerTokens = /* @__PURE__ */ new WeakMap();
4329
+ var topologyDepsChangedObservers = /* @__PURE__ */ new WeakMap();
4330
+ var checkpointReaders = /* @__PURE__ */ new WeakMap();
4331
+ var restoreWriters = /* @__PURE__ */ new WeakMap();
4332
+ var runtimeReleasers = /* @__PURE__ */ new WeakMap();
4333
+ var runtimeQuiescenceReaders = /* @__PURE__ */ new WeakMap();
4334
+ var subscriberCountReaders = /* @__PURE__ */ new WeakMap();
4335
+ var activationReaders = /* @__PURE__ */ new WeakMap();
4336
+ var releasedNodes = /* @__PURE__ */ new WeakSet();
4337
+ function withNodeCore(core, create) {
4338
+ const prev = constructingCore;
4339
+ constructingCore = core;
4340
+ try {
4341
+ return create();
4342
+ } finally {
4343
+ constructingCore = prev;
4344
+ }
4345
+ }
4346
+ function withEnvironmentDrivers(environment, create) {
4347
+ const prev = constructingEnvironment;
4348
+ constructingEnvironment = environment;
4349
+ try {
4350
+ return create();
4351
+ } finally {
4352
+ constructingEnvironment = prev;
4353
+ }
4354
+ }
4355
+ function getNodeOwner(n) {
4356
+ return ownerTokens.get(n);
4357
+ }
4358
+ function notifyTopologyDepsChanged(node, prevDeps, deps) {
4359
+ topologyDepsChangedObservers.get(node)?.(node, prevDeps, deps);
4360
+ }
4361
+ function terminalView(t) {
4362
+ return t === void 0 ? false : t;
4363
+ }
4364
+ function normalizePullDemand(demand) {
4365
+ if (typeof demand !== "object" || demand === null || Array.isArray(demand)) {
4366
+ throw new Error("ctx.up: PULL requires { pullId, params? } demand payload (D269)");
4367
+ }
4368
+ const pullId = demand.pullId;
4369
+ if (typeof pullId !== "string" && typeof pullId !== "symbol") {
4370
+ throw new Error("ctx.up: PULL demand requires a string or symbol pullId (D269)");
4371
+ }
4372
+ const params = demand.params;
4373
+ return params === void 0 ? { pullId } : { pullId, params };
4374
+ }
4375
+ function validateDownPayloads(msgs) {
4376
+ for (const m of msgs) {
4377
+ if (m[0] === "DATA" && m[1] === void 0) {
4378
+ throw new Error("down: DATA requires a non-SENTINEL payload (R-data-payload)");
4379
+ }
4380
+ if (m[0] === "ERROR" && isInvalidErrorPayload(m[1])) {
4381
+ throw new Error("down: ERROR requires a non-SENTINEL, non-boolean payload (R-data-payload)");
4382
+ }
4383
+ }
4384
+ }
4385
+ var Node = class {
4386
+ _core;
4387
+ _id;
4388
+ _slot;
4389
+ _dep;
4390
+ _value;
4391
+ _wave;
4392
+ _control;
4393
+ _lifecycle;
4394
+ _privateState;
4395
+ _hooks;
4396
+ _syncCtxState;
4397
+ _version;
4398
+ _restoredActivationPending = false;
4399
+ _released = false;
4400
+ get _syncCtx() {
4401
+ return this._syncCtxState.value;
4402
+ }
4403
+ set _syncCtx(ctx) {
4404
+ this._syncCtxState.value = ctx;
4405
+ }
4406
+ constructor(deps, handleOrFn, opts = {}) {
4407
+ const core = constructingCore;
4408
+ constructingCore = void 0;
4409
+ const dispatcher = opts.dispatcher ?? defaultDispatcher;
4410
+ const environment = constructingEnvironment ?? EnvironmentDrivers.empty();
4411
+ constructingEnvironment = void 0;
4412
+ const pool = opts.pool ?? "sync";
4413
+ const pausable = opts.pausable ?? true;
4414
+ const pullLock = opts.pullId;
4415
+ const pull = opts.pullId !== void 0;
4416
+ if (pull && pausable === false)
4417
+ throw new Error(
4418
+ "node: pullId is incompatible with pausable:false \u2014 a pull node uses the pausable delivery-content axis (R-pull / R-pause-modes / D55,D269)"
4419
+ );
4420
+ let handle;
4421
+ if (handleOrFn === null) handle = null;
4422
+ else if (typeof handleOrFn === "function") handle = dispatcher.register(handleOrFn, pool);
4423
+ else handle = handleOrFn;
4424
+ const n = deps.length;
4425
+ const dep = makeDepBookkeeping(n);
4426
+ const versioning = resolveNodeVersioningPolicy(opts.versioning);
4427
+ const value = {
4428
+ cache: SENTINEL,
4429
+ hasData: false,
4430
+ status: "sentinel",
4431
+ terminal: void 0,
4432
+ hasTorndown: false,
4433
+ replayRing: []
4434
+ };
4435
+ if (opts.initial !== void 0) {
4436
+ value.cache = opts.initial;
4437
+ value.hasData = true;
4438
+ value.status = "settled";
4439
+ }
4440
+ const pauseLockset = /* @__PURE__ */ new Set();
4441
+ this._core = core ?? new NodeCore();
4442
+ const created = this._core.createSlot(
4443
+ {
4444
+ deps,
4445
+ handle,
4446
+ pool,
4447
+ dispatcher,
4448
+ environment,
4449
+ partial: opts.partial ?? false,
4450
+ terminalAsRealInput: opts.terminalAsRealInput ?? false,
4451
+ completeWhenDepsComplete: opts.completeWhenDepsComplete ?? true,
4452
+ errorWhenDepsError: opts.errorWhenDepsError ?? true,
4453
+ resubscribable: opts.resubscribable ?? false,
4454
+ resetOnTeardown: opts.resetOnTeardown ?? false,
4455
+ pausable,
4456
+ pull,
4457
+ pullLock,
4458
+ replayN: opts.replayBuffer ?? 0,
4459
+ dynamic: opts.dynamic ?? false,
4460
+ name: opts.name,
4461
+ factory: opts.factory
4462
+ },
4463
+ {
4464
+ dep,
4465
+ lifecycle: { subscribers: /* @__PURE__ */ new Set(), activated: false },
4466
+ value,
4467
+ wave: {
4468
+ pending: 0,
4469
+ hasCalledFnOnce: false,
4470
+ emittedDirtyThisWave: false,
4471
+ emittedSettleThisWave: false,
4472
+ insideRunWave: false,
4473
+ inDepMutation: false,
4474
+ rewireRunPending: false,
4475
+ batchDirtyOwed: false
4476
+ },
4477
+ control: {
4478
+ pauseLockset,
4479
+ pausedDepWaveOccurred: false,
4480
+ pauseBuffer: [],
4481
+ demandOwed: void 0,
4482
+ activePull: void 0,
4483
+ pullDirtyOwed: false,
4484
+ inDeliverDemand: false
4485
+ },
4486
+ privateState: { value: SENTINEL, persist: false },
4487
+ hooks: { onDeactivation: [], onInvalidate: [] },
4488
+ syncCtx: { value: null },
4489
+ version: {
4490
+ policy: versioning,
4491
+ value: createNodeVersion(
4492
+ versioning,
4493
+ opts.initial !== void 0 ? opts.initial : void 0
4494
+ )
4495
+ }
4496
+ }
4497
+ );
4498
+ this._id = created.id;
4499
+ this._slot = this._core.get(this._id);
4500
+ this._dep = this._core.getDep(this._id);
4501
+ this._value = this._core.getValue(this._id);
4502
+ this._wave = this._core.getWave(this._id);
4503
+ this._control = this._core.getControl(this._id);
4504
+ this._lifecycle = this._core.getLifecycle(this._id);
4505
+ this._privateState = this._core.getPrivateState(this._id);
4506
+ this._hooks = this._core.getHooks(this._id);
4507
+ this._syncCtxState = this._core.getSyncCtx(this._id);
4508
+ this._version = this._core.getVersion(this._id);
4509
+ checkpointReaders.set(this, () => ({
4510
+ cache: this._value.cache,
4511
+ hasData: this._value.hasData,
4512
+ terminal: this._value.terminal,
4513
+ activated: this._lifecycle.activated,
4514
+ hasCalledFnOnce: this._wave.hasCalledFnOnce,
4515
+ ctxState: {
4516
+ value: this._privateState.value,
4517
+ persist: this._privateState.persist
4518
+ },
4519
+ version: cloneNodeVersion(this._version.value),
4520
+ handle: this._slot.handle
4521
+ }));
4522
+ restoreWriters.set(this, (state) => {
4523
+ this._assertNotReleased("restoreGraph");
4524
+ this._value.cache = state.cache;
4525
+ this._value.hasData = state.hasData;
4526
+ this._value.status = state.status;
4527
+ this._value.terminal = state.terminal;
4528
+ this._value.hasTorndown = false;
4529
+ this._value.replayRing = [];
4530
+ this._wave.hasCalledFnOnce = state.hasCalledFnOnce;
4531
+ this._wave.emittedDirtyThisWave = false;
4532
+ this._wave.emittedSettleThisWave = false;
4533
+ this._wave.pending = 0;
4534
+ this._wave.insideRunWave = false;
4535
+ this._wave.inDepMutation = false;
4536
+ this._wave.rewireRunPending = false;
4537
+ this._wave.batchDirtyOwed = false;
4538
+ this._control.pauseBuffer = [];
4539
+ this._control.pausedDepWaveOccurred = false;
4540
+ this._control.demandOwed = void 0;
4541
+ this._control.activePull = void 0;
4542
+ this._control.pullDirtyOwed = false;
4543
+ this._control.inDeliverDemand = false;
4544
+ this._control.pauseLockset.clear();
4545
+ this._privateState.value = state.ctxState.value;
4546
+ this._privateState.persist = state.ctxState.persist;
4547
+ if (state.version === false) {
4548
+ this._version.policy = { enabled: false };
4549
+ this._version.value = void 0;
4550
+ } else if (state.version.level === 0) {
4551
+ this._version.policy = { enabled: true, level: 0 };
4552
+ this._version.value = cloneNodeVersion(state.version);
4553
+ } else {
4554
+ if (!this._version.policy.enabled || this._version.policy.level !== 1) {
4555
+ throw new Error(
4556
+ `restoreGraph: checkpoint node version level ${state.version.level} requires matching node versioning policy`
4557
+ );
4558
+ }
4559
+ if (!state.hasData && state.version.counter > 0) {
4560
+ throw new Error(
4561
+ "restoreGraph: checkpoint node version cid cannot be verified without current DATA under V1 versioning (D109)"
4562
+ );
4563
+ }
4564
+ const expectedCid = restoredV1Cid(this._version.policy, state.hasData, state.cache);
4565
+ if (expectedCid !== state.version.cid) {
4566
+ throw new Error(
4567
+ "restoreGraph: checkpoint node version cid does not match the selected node versioning hash policy (D109)"
4568
+ );
4569
+ }
4570
+ this._version.value = cloneNodeVersion(state.version);
4571
+ }
4572
+ this._syncCtx = null;
4573
+ this._resetDepState();
4574
+ this._lifecycle.activated = false;
4575
+ this._lifecycle.subscribers.clear();
4576
+ this._restoredActivationPending = true;
4577
+ });
4578
+ runtimeReleasers.set(this, () => this._releaseRuntime());
4579
+ runtimeQuiescenceReaders.set(this, () => this._isRuntimeQuiescentForRelease());
4580
+ subscriberCountReaders.set(this, () => this._subscriberCount());
4581
+ activationReaders.set(this, () => this._lifecycle.activated);
4582
+ }
4583
+ /** R-pull (D55/D272): true while a pull node is not serving a PULL demand pulse. */
4584
+ _isPullQuiet() {
4585
+ return this._slot.pull && this._control.activePull === void 0;
4586
+ }
4587
+ /**
4588
+ * R-pull (D269/D272): this pull node's pullId (pure data, like {@link cache}/{@link handle} —
4589
+ * never triggers computation). A consumer demands one delivery by cone-routing PULL of it (no
4590
+ * node reference): `ctx.up([["PULL", { pullId }]])` (immediate; loops back → D37 for a self-read
4591
+ * dep) or `ctx.upNext([["PULL", { pullId }]])` (boundary-deferred self-demand). Undefined for a
4592
+ * non-pull node. The author writes the pullId verbatim; routing matches by identity.
4593
+ */
4594
+ get pullId() {
4595
+ return this._slot.pullLock;
4596
+ }
4597
+ get cache() {
4598
+ return this._value.cache;
4599
+ }
4600
+ get status() {
4601
+ return this._value.status;
4602
+ }
4603
+ get version() {
4604
+ return cloneNodeVersion(this._version.value);
4605
+ }
4606
+ get name() {
4607
+ return this._slot.name;
4608
+ }
4609
+ /** R-describe/D51: real factory name for a standalone graph-less node (a runtime *Map inner). */
4610
+ get factory() {
4611
+ return this._slot.factory;
4612
+ }
4613
+ /**
4614
+ * The node's CURRENT/LIVE deps (R-describe / R-edges-derived / D51) — readonly view of the
4615
+ * live `_deps`, which a rewire (C-8 / C-11) mutates. The graph's describe() reads this (NOT a
4616
+ * construction-time snapshot) so every edge corresponds to a real current subscription (D3).
4617
+ * Inspection-only, like cache/status; never triggers computation.
4618
+ */
4619
+ get deps() {
4620
+ return this._slot.deps;
4621
+ }
4622
+ /**
4623
+ * The fn handle (pure data `(poolId, handleId)`, D7) or null for state/passthrough
4624
+ * nodes. Inspection-only (L1.6 handle is referenceable/inspectable) — lets the graph
4625
+ * layer key a dispatcher-backed profile recorder WITHOUT putting counters on the node
4626
+ * (R-node-thin / D39).
4627
+ */
4628
+ get handle() {
4629
+ return this._slot.handle;
4630
+ }
4631
+ /** R-push-subscribe: a new sink receives START, then cached DATA (or DIRTY if dirty). */
4632
+ subscribe(sink) {
4633
+ this._assertNotReleased("subscribe");
4634
+ enterWave();
4635
+ try {
4636
+ if (this._value.terminal !== void 0) {
4637
+ if (this._slot.resubscribable) {
4638
+ this._restoredActivationPending = false;
4639
+ this._resetLifecycle();
4640
+ } else
4641
+ throw new Error(
4642
+ "subscribe: node is non-resubscribable and has terminated; the stream is permanently over (R-terminal / R2.2.7.b)"
4643
+ );
4644
+ }
4645
+ this._lifecycle.subscribers.add(sink);
4646
+ sink(["START"]);
4647
+ if (this._slot.replayN > 0 && this._value.replayRing.length > 0) {
4648
+ for (const v of this._value.replayRing) sink(["DATA", v]);
4649
+ } else if (this._value.hasData && !this._slot.pull) {
4650
+ sink(["DATA", this._value.cache]);
4651
+ } else if (this._value.status === "dirty" && !this._slot.pull) {
4652
+ sink(["DIRTY"]);
4653
+ }
4654
+ if (!this._lifecycle.activated) this._activate();
4655
+ return () => {
4656
+ if (!this._lifecycle.subscribers.delete(sink)) return;
4657
+ if (this._lifecycle.subscribers.size === 0) this._deactivate();
4658
+ };
4659
+ } finally {
4660
+ exitWave();
4661
+ }
4662
+ }
4663
+ /** External emission toward sinks (state-node push, or async late-emit). One call = one wave. */
4664
+ down(msgs) {
4665
+ this._assertNotReleased("down");
4666
+ enterWave();
4667
+ try {
4668
+ this._down(msgs);
4669
+ } finally {
4670
+ exitWave();
4671
+ }
4672
+ }
4673
+ /**
4674
+ * Emit upstream toward deps — control tiers only (R-ctx-up). `towardDep` (a dep index) routes up
4675
+ * ONE declared edge (R-up-routing directed-up); omitted = broadcast up all deps.
4676
+ */
4677
+ up(msgs, towardDep) {
4678
+ this._assertNotReleased("up");
4679
+ enterWave();
4680
+ try {
4681
+ this._up(msgs, towardDep);
4682
+ } finally {
4683
+ exitWave();
4684
+ }
4685
+ }
4686
+ // ── deferred self-rewire (R-rewire-deferred / D47): ctx.rewireNext drain support ──
4687
+ /**
4688
+ * Enqueue a deferred self-rewire op (issued from this node's fn via `ctx.rewireNext`).
4689
+ * Applied at the committed wave boundary (boundary.ts drain), never in place — the in-fn
4690
+ * immediate path (`subscribeDep`/`replaceDeps`/`unsubscribeDep`) still throws mid-run (D37/R-reentrancy).
4691
+ */
4692
+ _requestRewireNext(op) {
4693
+ deferRewire(this._core, () => this._applyRewireNext(op), {
4694
+ batchToken: currentBoundaryBatchToken(),
4695
+ isReady: () => !this._hasBoundaryPauseLock()
4696
+ });
4697
+ }
4698
+ /**
4699
+ * Enqueue a deferred up-going control/demand wave (R-up-routing / R-pull / D269): at the committed wave
4700
+ * boundary, route `msgs` up from THIS node (broadcast, or up the single `towardDep` edge). The
4701
+ * deferred form of `ctx.up` — the SELF-demand path: a consumer issues `ctx.upNext([[PULL,
4702
+ * { pullId }]])` to demand a dep it ALSO reads; the demand applies at the boundary (not mid-fn), so
4703
+ * the pull node's delivery loops back as a FRESH wave rather than re-entering the consumer (D37 /
4704
+ * R-reentrancy). Rides the same R-rewire-deferred (D47) drain as ctx.rewireNext.
4705
+ */
4706
+ _requestUpNext(msgs, towardDep) {
4707
+ deferRewire(
4708
+ this._core,
4709
+ () => {
4710
+ if (!this._released) this._up(msgs, towardDep);
4711
+ },
4712
+ {
4713
+ batchToken: currentBoundaryBatchToken(),
4714
+ isReady: () => !this._hasBoundaryPauseLock()
4715
+ }
4716
+ );
4717
+ }
4718
+ /** Apply one queued self-rewire at the boundary (drain thunk). */
4719
+ _applyRewireNext(op) {
4720
+ if (this._released) return;
4721
+ try {
4722
+ if (op.kind === "add") {
4723
+ const next = this._slot.deps.includes(op.dep) ? [...this._slot.deps] : [...this._slot.deps, op.dep];
4724
+ this._rewire(next, op.fn, { allowTerminalOwner: true });
4725
+ } else if (op.kind === "remove") {
4726
+ this._rewire(
4727
+ this._slot.deps.filter((d) => d !== op.dep),
4728
+ op.fn,
4729
+ { allowTerminalOwner: true }
4730
+ );
4731
+ } else {
4732
+ this._rewire(this._dedupDeps(op.deps), op.fn, { allowTerminalOwner: true });
4733
+ }
4734
+ } catch (e) {
4735
+ this._down([["ERROR", errorPayload(e, "rewireNext op failed")]]);
4736
+ }
4737
+ }
4738
+ // ── rewire (R-rewire / D42): intra-graph runtime topology mutation ──
4739
+ /**
4740
+ * Replace this node's deps atomically (surgical, Option-C). Requires an explicit
4741
+ * `fn` (SD-1 fn-deps pairing — user fns read dep input positionally). Kept deps
4742
+ * keep their subscription + per-dep state; only removed deps unsubscribe and only
4743
+ * added deps fresh-subscribe (push-on-subscribe for an added cached dep). The
4744
+ * first-run gate and cache are PRESERVED (R-rewire Q2/Q7). Intra-graph only (D22).
4745
+ */
4746
+ replaceDeps(newDeps, fn) {
4747
+ this._assertNotReleased("replaceDeps");
4748
+ this._rewire(this._dedupDeps(newDeps), fn);
4749
+ }
4750
+ /** Subscribe to one dep (special case of replaceDeps); returns its index. fn required (SD-1). */
4751
+ subscribeDep(depNode, fn) {
4752
+ this._assertNotReleased("subscribeDep");
4753
+ const next = this._slot.deps.includes(depNode) ? [...this._slot.deps] : [...this._slot.deps, depNode];
4754
+ const deferred = this._rewire(next, fn);
4755
+ return deferred ? next.indexOf(depNode) : this._slot.deps.indexOf(depNode);
4756
+ }
4757
+ /** Unsubscribe from one dep (special case of replaceDeps); idempotent if absent (fn swap still applies). */
4758
+ unsubscribeDep(depNode, fn) {
4759
+ this._assertNotReleased("unsubscribeDep");
4760
+ this._rewire(
4761
+ this._slot.deps.filter((d) => d !== depNode),
4762
+ fn
4763
+ );
4764
+ }
4765
+ _dedupDeps(deps) {
4766
+ const seen = /* @__PURE__ */ new Set();
4767
+ const out = [];
4768
+ for (const d of deps)
4769
+ if (!seen.has(d)) {
4770
+ seen.add(d);
4771
+ out.push(d);
4772
+ }
4773
+ return out;
4774
+ }
4775
+ /** Is `target` reachable upstream from `from` (following deps)? Cycle-prevention DFS. */
4776
+ _reachableUpstream(from, target) {
4777
+ const seen = /* @__PURE__ */ new Set();
4778
+ const stack = [from];
4779
+ while (stack.length > 0) {
4780
+ const n = stack.pop();
4781
+ if (n === void 0) continue;
4782
+ if (n === target) return true;
4783
+ if (seen.has(n)) continue;
4784
+ seen.add(n);
4785
+ for (const d of n._slot.deps) stack.push(d);
4786
+ }
4787
+ return false;
4788
+ }
4789
+ _assertRewireDepOwner(dep) {
4790
+ const selfOwner = getNodeOwner(this);
4791
+ const depOwner = getNodeOwner(dep);
4792
+ if (selfOwner !== void 0 && depOwner !== void 0 && selfOwner !== depOwner) {
4793
+ throw new Error(
4794
+ "rewire: dep belongs to a different graph; cross-graph deps require a wire bridge (D22 / R-graph-domain)"
4795
+ );
4796
+ }
4797
+ }
4798
+ _rewire(newDeps, fn, opts = {}) {
4799
+ if (this._value.terminal !== void 0 && !opts.allowTerminalOwner)
4800
+ throw new Error(
4801
+ "rewire: node is terminal (completed/errored) \u2014 cannot rewire (R-rewire / D42)"
4802
+ );
4803
+ if (this._wave.insideRunWave)
4804
+ throw new Error(
4805
+ "rewire: mid-fn topology mutation \u2014 a fn mutating its own deps mid-wave is the feedback cycle (R-rewire / D37)"
4806
+ );
4807
+ if (this._wave.inDepMutation)
4808
+ throw new Error(
4809
+ "rewire: reentrant dep mutation \u2014 another replaceDeps/subscribeDep/unsubscribeDep is in flight (R-rewire)"
4810
+ );
4811
+ if (newDeps.includes(this))
4812
+ throw new Error("rewire: self-dependency rejected (R-rewire / D42)");
4813
+ const oldDeps = this._slot.deps;
4814
+ const added = newDeps.filter((d) => !oldDeps.includes(d));
4815
+ for (const d of added) {
4816
+ if (this._reachableUpstream(d, this))
4817
+ throw new Error(
4818
+ "rewire: would create a cycle \u2014 dep already transitively depends on this node (R-rewire / D42)"
4819
+ );
4820
+ if (d._value.terminal !== void 0 && !d._slot.resubscribable)
4821
+ throw new Error(
4822
+ "rewire: cannot add a non-resubscribable terminal dep \u2014 would wedge (R-rewire / D42)"
4823
+ );
4824
+ this._assertRewireDepOwner(d);
4825
+ }
4826
+ if (deferAfterBatchForTarget(this, () => {
4827
+ this._rewire(newDeps, fn, { ...opts, allowTerminalOwner: true });
4828
+ })) {
4829
+ return true;
4830
+ }
4831
+ if (!this._lifecycle.activated) this._restoredActivationPending = false;
4832
+ this._wave.inDepMutation = true;
4833
+ this._wave.rewireRunPending = false;
4834
+ let zeroDepUnDirty = false;
4835
+ try {
4836
+ const oldHandle = this._slot.handle;
4837
+ this._slot.handle = this._slot.dispatcher.register(fn, this._slot.pool);
4838
+ if (oldHandle !== null) this._slot.dispatcher.unregister(oldHandle);
4839
+ const removed = oldDeps.filter((d) => !newDeps.includes(d));
4840
+ let removedDirtyContributor = false;
4841
+ for (const d of removed) {
4842
+ const oldIdx = oldDeps.indexOf(d);
4843
+ if (this._dep.dirty[oldIdx]) {
4844
+ removedDirtyContributor = true;
4845
+ this._wave.pending--;
4846
+ }
4847
+ if (this._lifecycle.activated) {
4848
+ const box = this._dep.idxBoxes[oldIdx];
4849
+ if (box) box.v = -1;
4850
+ const unsub = this._dep.unsubs[oldIdx];
4851
+ if (unsub) unsub();
4852
+ }
4853
+ }
4854
+ const n = newDeps.length;
4855
+ const newBatch = new Array(n).fill(null);
4856
+ const newPrev = new Array(n).fill(SENTINEL);
4857
+ const newHasData = new Array(n).fill(false);
4858
+ const newDirty = new Array(n).fill(false);
4859
+ const newTier = new Array(n).fill(0);
4860
+ const newTerminal = new Array(n).fill(void 0);
4861
+ const newTerminalInput = new Array(n).fill(void 0);
4862
+ const newUnsubs = new Array(n);
4863
+ const newBoxes = new Array(n);
4864
+ for (let j = 0; j < n; j++) {
4865
+ const oldIdx = oldDeps.indexOf(newDeps[j]);
4866
+ if (oldIdx !== -1) {
4867
+ newBatch[j] = this._dep.batch[oldIdx];
4868
+ newPrev[j] = this._dep.prev[oldIdx];
4869
+ newHasData[j] = this._dep.hasData[oldIdx];
4870
+ newDirty[j] = this._dep.dirty[oldIdx];
4871
+ newTier[j] = this._dep.tier[oldIdx];
4872
+ newTerminal[j] = this._dep.terminal[oldIdx];
4873
+ newUnsubs[j] = this._dep.unsubs[oldIdx];
4874
+ const box = this._dep.idxBoxes[oldIdx];
4875
+ if (box) box.v = j;
4876
+ newBoxes[j] = box;
4877
+ }
4878
+ }
4879
+ this._slot.deps = newDeps;
4880
+ this._dep.batch = newBatch;
4881
+ this._dep.prev = newPrev;
4882
+ this._dep.hasData = newHasData;
4883
+ this._dep.dirty = newDirty;
4884
+ this._dep.tier = newTier;
4885
+ this._dep.terminal = newTerminal;
4886
+ this._dep.terminalInput = newTerminalInput;
4887
+ this._dep.unsubs = newUnsubs;
4888
+ this._dep.idxBoxes = newBoxes;
4889
+ this._dep.waveData = newDeps.map(() => []);
4890
+ this._dep.waveTokens = new Array(newDeps.length).fill(void 0);
4891
+ this._syncCtx = null;
4892
+ if (this._lifecycle.activated) {
4893
+ for (const d of added) this._subscribeDepAt(d);
4894
+ }
4895
+ notifyTopologyDepsChanged(this, oldDeps, newDeps);
4896
+ if (removedDirtyContributor && this._wave.pending === 0 && this._value.status === "dirty") {
4897
+ if (newDeps.length > 0) this._wave.rewireRunPending = true;
4898
+ else zeroDepUnDirty = true;
4899
+ }
4900
+ } finally {
4901
+ this._wave.inDepMutation = false;
4902
+ }
4903
+ if (this._wave.rewireRunPending) {
4904
+ this._wave.rewireRunPending = false;
4905
+ this._settleRewire();
4906
+ } else if (zeroDepUnDirty) {
4907
+ if (this._wave.emittedDirtyThisWave) this._down([["RESOLVED"]]);
4908
+ else this._value.status = this._value.hasData ? "settled" : "sentinel";
4909
+ }
4910
+ return false;
4911
+ }
4912
+ // ── activation / deactivation (lazy; R-rom-ram) ──
4913
+ _activate() {
4914
+ this._lifecycle.activated = true;
4915
+ const seedRestoredDeps = this._restoredActivationPending;
4916
+ this._restoredActivationPending = false;
4917
+ this._dep.unsubs = new Array(this._slot.deps.length);
4918
+ this._dep.idxBoxes = new Array(this._slot.deps.length);
4919
+ for (const dep of this._slot.deps)
4920
+ this._subscribeDepAt(dep, { seedRestored: seedRestoredDeps });
4921
+ if (this._slot.deps.length === 0 && this._slot.handle !== null && !this._wave.hasCalledFnOnce) {
4922
+ this._runWave();
4923
+ }
4924
+ }
4925
+ /**
4926
+ * Subscribe to a dep. The dispatch callback reads the dep's CURRENT index from a
4927
+ * mutable box (O(1)); a surgical rewire that reorders kept deps just updates the box
4928
+ * (R-rewire Option-C / D42) — no re-subscribe, no per-message indexOf scan. A removed
4929
+ * dep's box is set to -1 so any stale in-flight callback drops (drain).
4930
+ */
4931
+ _subscribeDepAt(depNode, opts = {}) {
4932
+ const idx0 = this._slot.deps.indexOf(depNode);
4933
+ const box = { v: idx0 };
4934
+ let ignoreInitialPush = opts.seedRestored === true;
4935
+ if (ignoreInitialPush && idx0 !== -1) {
4936
+ this._seedRestoredDepAt(idx0, depNode);
4937
+ if (depNode._value.terminal !== void 0 && !depNode._slot.resubscribable) {
4938
+ this._dep.unsubs[idx0] = () => {
4939
+ };
4940
+ this._dep.idxBoxes[idx0] = box;
4941
+ return;
4942
+ }
4943
+ }
4944
+ const unsub = depNode.subscribe((msg, delivery) => {
4945
+ if (ignoreInitialPush && delivery === void 0) return;
4946
+ if (ignoreInitialPush) ignoreInitialPush = false;
4947
+ if (box.v === -1) return;
4948
+ this._receiveFromDep(box.v, msg, delivery);
4949
+ });
4950
+ if (ignoreInitialPush && idx0 !== -1 && box.v !== -1) this._seedRestoredDepAt(idx0, depNode);
4951
+ ignoreInitialPush = false;
4952
+ if (idx0 !== -1) {
4953
+ this._dep.unsubs[idx0] = unsub;
4954
+ this._dep.idxBoxes[idx0] = box;
4955
+ }
4956
+ }
4957
+ _seedRestoredDepAt(idx, depNode) {
4958
+ const seedData = depNode._value.hasData && !depNode._slot.pull;
4959
+ this._dep.batch[idx] = null;
4960
+ this._dep.waveData[idx] = [];
4961
+ this._dep.waveTokens[idx] = void 0;
4962
+ this._dep.prev[idx] = seedData ? depNode._value.cache : SENTINEL;
4963
+ this._dep.hasData[idx] = seedData;
4964
+ this._dep.dirty[idx] = false;
4965
+ this._dep.tier[idx] = seedData ? 3 : 0;
4966
+ this._dep.terminal[idx] = depNode._value.terminal;
4967
+ this._dep.terminalInput[idx] = void 0;
4968
+ }
4969
+ _deactivate() {
4970
+ this._lifecycle.activated = false;
4971
+ for (const u of this._dep.unsubs) if (u) u();
4972
+ this._dep.unsubs = [];
4973
+ this._dep.idxBoxes = [];
4974
+ for (const fn of this._hooks.onDeactivation) fn();
4975
+ this._hooks.onDeactivation = [];
4976
+ this._hooks.onInvalidate = [];
4977
+ const isCompute = this._slot.handle !== null || this._slot.deps.length > 0;
4978
+ if (isCompute) {
4979
+ this._value.cache = SENTINEL;
4980
+ this._value.hasData = false;
4981
+ this._value.status = "sentinel";
4982
+ }
4983
+ this._resetDepState();
4984
+ this._wave.hasCalledFnOnce = false;
4985
+ this._control.pauseLockset.clear();
4986
+ this._control.pauseBuffer = [];
4987
+ this._control.pausedDepWaveOccurred = false;
4988
+ this._control.demandOwed = void 0;
4989
+ this._control.activePull = void 0;
4990
+ this._control.pullDirtyOwed = false;
4991
+ this._value.replayRing = [];
4992
+ if (!this._privateState.persist) this._privateState.value = SENTINEL;
4993
+ }
4994
+ _assertNotReleased(op) {
4995
+ if (this._released)
4996
+ throw new Error(`${op}: node has been released from its graph lifecycle (D122)`);
4997
+ }
4998
+ _subscriberCount() {
4999
+ return this._lifecycle.subscribers.size;
5000
+ }
5001
+ _isRuntimeQuiescentForRelease() {
5002
+ return !this._released && this._value.status !== "dirty" && this._value.status !== "pending" && this._wave.pending === 0 && !this._wave.insideRunWave && !this._wave.inDepMutation && !this._wave.rewireRunPending && !this._wave.batchDirtyOwed && this._dep.dirty.every((dirty) => !dirty) && this._control.pauseBuffer.length === 0 && !this._control.pausedDepWaveOccurred && this._control.demandOwed === void 0 && this._control.activePull === void 0 && !this._control.inDeliverDemand && this._control.pauseLockset.size === 0;
5003
+ }
5004
+ _releaseRuntime() {
5005
+ if (this._released) return;
5006
+ this._released = true;
5007
+ releasedNodes.add(this);
5008
+ let releaseError;
5009
+ const recordReleaseError = (error) => {
5010
+ if (releaseError === void 0) releaseError = error;
5011
+ };
5012
+ this._lifecycle.activated = false;
5013
+ for (const u of this._dep.unsubs) {
5014
+ try {
5015
+ u();
5016
+ } catch (error) {
5017
+ recordReleaseError(error);
5018
+ }
5019
+ }
5020
+ for (const fn of this._hooks.onDeactivation) {
5021
+ try {
5022
+ fn();
5023
+ } catch (error) {
5024
+ recordReleaseError(error);
5025
+ }
5026
+ }
5027
+ this._dep.unsubs = [];
5028
+ this._dep.idxBoxes = [];
5029
+ this._lifecycle.subscribers.clear();
5030
+ if (this._slot.handle !== null) {
5031
+ this._slot.dispatcher.unregister(this._slot.handle);
5032
+ this._slot.handle = null;
5033
+ }
5034
+ this._slot.deps = [];
5035
+ this._dep.batch = [];
5036
+ this._dep.waveData = [];
5037
+ this._dep.waveTokens = [];
5038
+ this._dep.prev = [];
5039
+ this._dep.hasData = [];
5040
+ this._dep.dirty = [];
5041
+ this._dep.tier = [];
5042
+ this._dep.terminal = [];
5043
+ this._dep.terminalInput = [];
5044
+ this._value.cache = SENTINEL;
5045
+ this._value.hasData = false;
5046
+ this._value.status = "sentinel";
5047
+ this._value.terminal = void 0;
5048
+ this._value.replayRing = [];
5049
+ this._privateState.value = SENTINEL;
5050
+ this._privateState.persist = false;
5051
+ this._syncCtx = null;
5052
+ this._resetDepState();
5053
+ this._hooks.onDeactivation = [];
5054
+ this._hooks.onInvalidate = [];
5055
+ this._control.pauseLockset.clear();
5056
+ this._control.pauseBuffer = [];
5057
+ this._control.pausedDepWaveOccurred = false;
5058
+ this._control.demandOwed = void 0;
5059
+ this._control.activePull = void 0;
5060
+ this._control.pullDirtyOwed = false;
5061
+ this._restoredActivationPending = false;
5062
+ checkpointReaders.delete(this);
5063
+ restoreWriters.delete(this);
5064
+ runtimeReleasers.delete(this);
5065
+ runtimeQuiescenceReaders.delete(this);
5066
+ subscriberCountReaders.delete(this);
5067
+ activationReaders.delete(this);
5068
+ ownerTokens.delete(this);
5069
+ topologyDepsChangedObservers.delete(this);
5070
+ this._core.releaseSlot(this._id);
5071
+ if (releaseError !== void 0) throw releaseError;
5072
+ }
5073
+ _resetDepState() {
5074
+ const n = this._slot.deps.length;
5075
+ for (let i = 0; i < n; i++) {
5076
+ this._dep.batch[i] = null;
5077
+ this._dep.waveData[i] = [];
5078
+ this._dep.waveTokens[i] = void 0;
5079
+ this._dep.prev[i] = SENTINEL;
5080
+ this._dep.hasData[i] = false;
5081
+ this._dep.dirty[i] = false;
5082
+ this._dep.tier[i] = 0;
5083
+ this._dep.terminal[i] = void 0;
5084
+ this._dep.terminalInput[i] = void 0;
5085
+ }
5086
+ this._wave.pending = 0;
5087
+ this._wave.emittedDirtyThisWave = false;
5088
+ }
5089
+ // ── upstream wave receive (two-phase + diamond) ──
5090
+ _recordDepProjection(idx, delivery) {
5091
+ const token = delivery?.wave ?? {};
5092
+ if (this._dep.waveTokens[idx] !== token) {
5093
+ this._dep.waveData[idx].push([]);
5094
+ this._dep.waveTokens[idx] = token;
5095
+ }
5096
+ return this._dep.waveData[idx][this._dep.waveData[idx].length - 1];
5097
+ }
5098
+ _depProjectionHasData(idx) {
5099
+ const projection = this._dep.waveData[idx][this._dep.waveData[idx].length - 1];
5100
+ return projection?.some((v) => v !== SENTINEL) ?? false;
5101
+ }
5102
+ _receiveFromDep(idx, msg, delivery) {
5103
+ if (this._released) return;
5104
+ const t = msg[0];
5105
+ if (t === "START") return;
5106
+ const isLastInDeliveredWave = delivery?.last ?? true;
5107
+ if (this._value.terminal !== void 0) {
5108
+ if (t === "TEARDOWN") this._down([["TEARDOWN"]]);
5109
+ return;
5110
+ }
5111
+ if (t === "INVALIDATE") {
5112
+ const projection = this._recordDepProjection(idx, delivery);
5113
+ projection.push(SENTINEL);
5114
+ if (projection.some((v) => v !== SENTINEL) && isLastInDeliveredWave) this._maybeRun();
5115
+ this._dep.prev[idx] = SENTINEL;
5116
+ this._dep.hasData[idx] = false;
5117
+ this._dep.batch[idx] = null;
5118
+ if (this._dep.dirty[idx]) {
5119
+ this._dep.dirty[idx] = false;
5120
+ this._wave.pending--;
5121
+ }
5122
+ if (this._control.pausedDepWaveOccurred && this._dep.batch.every((b) => b === null)) {
5123
+ this._control.pausedDepWaveOccurred = false;
5124
+ }
5125
+ const hadData = this._value.hasData;
5126
+ this._invalidate();
5127
+ if (this._wave.pending === 0 && this._wave.emittedDirtyThisWave) {
5128
+ if (!hadData) this._down([["RESOLVED"]]);
5129
+ else this._wave.emittedDirtyThisWave = false;
5130
+ }
5131
+ this._fireOwedDemandIfReady();
5132
+ return;
5133
+ }
5134
+ if (isTerminal(t)) {
5135
+ const isError = t === "ERROR";
5136
+ const errPayload = isError ? msg[1] : void 0;
5137
+ this._dep.terminal[idx] = isError ? errPayload : true;
5138
+ this._dep.terminalInput[idx] = isError ? errPayload : true;
5139
+ this._releaseDepDirty(idx);
5140
+ const ranValueBeforeTerminal = this._depProjectionHasData(idx) && isLastInDeliveredWave;
5141
+ if (ranValueBeforeTerminal) this._maybeRun();
5142
+ if (isError && this._slot.errorWhenDepsError) {
5143
+ this._down([["ERROR", errPayload]]);
5144
+ } else if (this._slot.terminalAsRealInput) {
5145
+ if (ranValueBeforeTerminal) {
5146
+ this._fireOwedDemandIfReady();
5147
+ return;
5148
+ }
5149
+ this._maybeRun();
5150
+ } else if (this._slot.completeWhenDepsComplete && this._allDepsTerminal()) {
5151
+ this._down([["COMPLETE"]]);
5152
+ } else {
5153
+ this._settleAfterAbsorbedTerminal();
5154
+ }
5155
+ this._fireOwedDemandIfReady();
5156
+ return;
5157
+ }
5158
+ if (t === "TEARDOWN") {
5159
+ this._down([["TEARDOWN"]]);
5160
+ return;
5161
+ }
5162
+ if (t === "DIRTY") {
5163
+ if (!this._dep.dirty[idx]) {
5164
+ this._dep.dirty[idx] = true;
5165
+ this._wave.pending++;
5166
+ this._dep.tier[idx] = 2;
5167
+ this._markDirty();
5168
+ }
5169
+ return;
5170
+ }
5171
+ if (t === "DATA") {
5172
+ const v = msg[1];
5173
+ this._recordDepProjection(idx, delivery).push(v);
5174
+ const b = this._dep.batch[idx];
5175
+ if (b === null) this._dep.batch[idx] = [v];
5176
+ else b.push(v);
5177
+ this._dep.prev[idx] = v;
5178
+ this._dep.hasData[idx] = true;
5179
+ this._dep.tier[idx] = 3;
5180
+ if (this._dep.dirty[idx]) {
5181
+ this._dep.dirty[idx] = false;
5182
+ this._wave.pending--;
5183
+ }
5184
+ if (isLastInDeliveredWave) this._maybeRun();
5185
+ this._fireOwedDemandIfReady();
5186
+ return;
5187
+ }
5188
+ if (t === "RESOLVED") {
5189
+ this._recordDepProjection(idx, delivery);
5190
+ this._dep.tier[idx] = 3;
5191
+ if (this._dep.dirty[idx]) {
5192
+ this._dep.dirty[idx] = false;
5193
+ this._wave.pending--;
5194
+ }
5195
+ if (isLastInDeliveredWave) this._maybeRun();
5196
+ this._fireOwedDemandIfReady();
5197
+ return;
5198
+ }
5199
+ }
5200
+ /**
5201
+ * R-terminal-settles-dirty (B35): release a dep's outstanding in-wave DIRTY contribution.
5202
+ * A settle-class event for that dep (DATA/RESOLVED inline above, INVALIDATE, and now
5203
+ * COMPLETE/ERROR) clears its dirty flag + decrements _pending. No-op if the dep already
5204
+ * settled this wave (DATA/RESOLVED ran first) — so the normal DATA-then-COMPLETE flow is
5205
+ * unaffected. This makes the exactly-one-settle invariant a single, shared step.
5206
+ */
5207
+ _releaseDepDirty(idx) {
5208
+ if (this._dep.dirty[idx]) {
5209
+ this._dep.dirty[idx] = false;
5210
+ this._wave.pending--;
5211
+ }
5212
+ }
5213
+ /**
5214
+ * R-terminal-settles-dirty (B35): settle a node whose dirtied dep was released by an ABSORBED
5215
+ * terminal that is NOT a real input (a plain derived/effect — the common default-node case when
5216
+ * one of several deps completes while others stay live). Runs only when the release drained
5217
+ * _pending while the node still owes a downstream settle (it broadcast DIRTY this wave):
5218
+ * - if some OTHER dep delivered real DATA this wave (a value occurred) → recompute (→ DATA);
5219
+ * - else the terminal's signalled change did not materialise (no value) → one undirty RESOLVED
5220
+ * (R-resolved-undirty), keeping the cache (a terminal, unlike INVALIDATE, leaves the value).
5221
+ * A terminalAsRealInput node instead recomputes unconditionally (its fn reads the terminal).
5222
+ */
5223
+ _settleAfterAbsorbedTerminal() {
5224
+ if (this._wave.pending !== 0 || !this._wave.emittedDirtyThisWave) return;
5225
+ const sawData = this._dep.batch.some((b) => b !== null && b.length > 0);
5226
+ if (sawData) this._maybeRun();
5227
+ if (this._wave.emittedDirtyThisWave) this._down([["RESOLVED"]]);
5228
+ }
5229
+ _markDirty() {
5230
+ this._value.status = "dirty";
5231
+ if (this._isPullQuiet()) return;
5232
+ if (!this._wave.emittedDirtyThisWave) {
5233
+ this._wave.emittedDirtyThisWave = true;
5234
+ this._emitToSubs(["DIRTY"]);
5235
+ }
5236
+ }
5237
+ _maybeRun() {
5238
+ if (this._wave.inDepMutation) {
5239
+ this._wave.rewireRunPending = true;
5240
+ return;
5241
+ }
5242
+ if (this._slot.pausable === true && (this._isPaused() || this._isPullQuiet())) {
5243
+ this._control.pausedDepWaveOccurred = true;
5244
+ return;
5245
+ }
5246
+ this._tryRun();
5247
+ }
5248
+ /**
5249
+ * R-rewire atomic settle (after a rewire that warrants a recompute): emit a proper
5250
+ * two-phase DIRTY→DATA wave (R-dirty-before-data — a rewire-triggered settle is a wave),
5251
+ * once, with every added dep already wired. Mirrors _maybeRun/_tryRun's pause + gate +
5252
+ * pending guards, then injects the phase-1 DIRTY the added dep's [START,DATA] handshake
5253
+ * did not carry.
5254
+ */
5255
+ _settleRewire() {
5256
+ if (this._slot.pausable === true && this._isPaused()) {
5257
+ this._control.pausedDepWaveOccurred = true;
5258
+ return;
5259
+ }
5260
+ if (this._wave.pending > 0) return;
5261
+ if (this._slot.handle === null) {
5262
+ this._passthroughEmit();
5263
+ return;
5264
+ }
5265
+ if (!this._wave.hasCalledFnOnce && !(this._slot.partial || this._allDepsSettled())) return;
5266
+ this._markDirty();
5267
+ this._runWave();
5268
+ }
5269
+ _tryRun() {
5270
+ if (this._wave.pending > 0) return;
5271
+ if (this._slot.handle === null) {
5272
+ this._passthroughEmit();
5273
+ return;
5274
+ }
5275
+ if (!this._wave.hasCalledFnOnce) {
5276
+ if (this._slot.partial || this._allDepsSettled()) this._runWave();
5277
+ return;
5278
+ }
5279
+ this._runWave();
5280
+ }
5281
+ _allDepsSettled() {
5282
+ for (let i = 0; i < this._slot.deps.length; i++) {
5283
+ if (this._dep.hasData[i]) continue;
5284
+ if (this._slot.terminalAsRealInput && this._dep.terminal[i] !== void 0) continue;
5285
+ return false;
5286
+ }
5287
+ return true;
5288
+ }
5289
+ _passthroughEmit() {
5290
+ const b = this._dep.batch[0];
5291
+ if (b !== null && b.length > 0) {
5292
+ this._down([["DATA", b[b.length - 1]]]);
5293
+ } else if (this._wave.emittedDirtyThisWave) {
5294
+ this._down([["RESOLVED"]]);
5295
+ }
5296
+ this._dep.batch[0] = null;
5297
+ this._wave.emittedDirtyThisWave = false;
5298
+ }
5299
+ _runWave() {
5300
+ if (this._wave.insideRunWave)
5301
+ throw new Error(
5302
+ "synchronous feedback cycle: node fn re-entered its own wave (R-reentrancy / D37)"
5303
+ );
5304
+ this._wave.hasCalledFnOnce = true;
5305
+ this._hooks.onInvalidate = [];
5306
+ this._hooks.onDeactivation = [];
5307
+ const ctx = this._buildCtx();
5308
+ const wasDirty = this._wave.emittedDirtyThisWave;
5309
+ this._wave.emittedSettleThisWave = false;
5310
+ this._wave.insideRunWave = true;
5311
+ try {
5312
+ this._slot.dispatcher.invoke(this._slot.handle, ctx);
5313
+ } finally {
5314
+ this._wave.insideRunWave = false;
5315
+ }
5316
+ if (wasDirty && !this._wave.emittedSettleThisWave && this._value.terminal === void 0 && !this._isAsyncPool()) {
5317
+ this._down([["RESOLVED"]]);
5318
+ }
5319
+ for (let i = 0; i < this._dep.batch.length; i++) {
5320
+ this._dep.batch[i] = null;
5321
+ this._dep.waveData[i] = [];
5322
+ this._dep.waveTokens[i] = void 0;
5323
+ this._dep.terminalInput[i] = void 0;
5324
+ }
5325
+ this._wave.emittedDirtyThisWave = false;
5326
+ }
5327
+ // ── ctx construction (L3-Q5: sync = node-stable reused ctx; async = per-invocation) ──
5328
+ _buildCtx() {
5329
+ const kind = this._slot.handle ? this._slot.dispatcher.poolKind(this._slot.handle.poolId) : "sync";
5330
+ if (kind === "sync") {
5331
+ if (this._syncCtx === null) this._syncCtx = this._makeCtx();
5332
+ this._refreshCtx(this._syncCtx);
5333
+ return this._syncCtx;
5334
+ }
5335
+ return this._makeCtx({
5336
+ waveData: this._dep.waveData.map((waves) => waves.map((w) => [...w])),
5337
+ terminal: this._dep.terminalInput.map(terminalView),
5338
+ latest: [...this._dep.prev]
5339
+ });
5340
+ }
5341
+ _makeCtx(snapshot) {
5342
+ const ctx = {
5343
+ // Wave-owner boundary (D47): a SYNC fn's emit nests under the public entry that drove
5344
+ // it (cheap inc/dec, no early drain); an ASYNC-pool fn re-enters here from its stashed
5345
+ // ctx at depth 0, so this is the boundary that drains any rewireNext it issued.
5346
+ up: (msgs, towardDep) => {
5347
+ if (this._released) return;
5348
+ enterWave();
5349
+ try {
5350
+ this._up(msgs, towardDep);
5351
+ } finally {
5352
+ exitWave();
5353
+ }
5354
+ },
5355
+ down: (msgs) => {
5356
+ if (this._released) return;
5357
+ enterWave();
5358
+ try {
5359
+ this._down(msgs);
5360
+ } finally {
5361
+ exitWave();
5362
+ }
5363
+ },
5364
+ waveData: snapshot?.waveData ?? this._dep.waveData,
5365
+ terminal: snapshot?.terminal ?? this._dep.terminalInput.map(terminalView),
5366
+ state: this._makeState(),
5367
+ onDeactivation: (fn) => {
5368
+ if (this._released) return;
5369
+ this._hooks.onDeactivation.push(fn);
5370
+ },
5371
+ onInvalidate: (fn) => {
5372
+ if (this._released) return;
5373
+ this._hooks.onInvalidate.push(fn);
5374
+ },
5375
+ environment: () => this._slot.environment,
5376
+ // R-rewire-deferred (D47): defer a self-dep-set mutation to the committed boundary.
5377
+ rewireNext: {
5378
+ subscribeDep: (dep, fn) => this._requestRewireNext({ kind: "add", dep, fn }),
5379
+ unsubscribeDep: (dep, fn) => this._requestRewireNext({ kind: "remove", dep, fn }),
5380
+ replaceDeps: (deps, fn) => this._requestRewireNext({ kind: "set", deps, fn })
5381
+ },
5382
+ // R-up-routing / R-pull (D269): deferred up — route a control/demand wave (e.g. PULL)
5383
+ // up the declared cone at the committed boundary. The SELF-demand path: an
5384
+ // immediate ctx.up whose delivery loops back re-enters this fn (D37 / R-reentrancy).
5385
+ upNext: (msgs, towardDep) => this._requestUpNext(msgs, towardDep),
5386
+ ...this._control.activePull === void 0 ? {} : { pull: this._control.activePull },
5387
+ [CTX_DEP_CACHE]: { latest: snapshot?.latest ?? this._dep.prev },
5388
+ [CTX_NODE_BINDING]: {
5389
+ dispatcher: this._slot.dispatcher,
5390
+ create: (factory) => withEnvironmentDrivers(this._slot.environment, () => withNodeCore(this._core, factory))
5391
+ }
5392
+ };
5393
+ if (this._slot.dynamic) {
5394
+ ctx.track = (i) => ctx[CTX_DEP_CACHE]?.latest[i];
5395
+ }
5396
+ return ctx;
5397
+ }
5398
+ _refreshCtx(ctx) {
5399
+ ctx.waveData = this._dep.waveData;
5400
+ ctx.terminal = this._dep.terminalInput.map(terminalView);
5401
+ if (this._control.activePull === void 0) {
5402
+ delete ctx.pull;
5403
+ } else {
5404
+ ctx.pull = this._control.activePull;
5405
+ }
5406
+ ctx[CTX_DEP_CACHE] = { latest: this._dep.prev };
5407
+ }
5408
+ _makeState() {
5409
+ return {
5410
+ get: () => this._privateState.value,
5411
+ set: (v) => {
5412
+ this._privateState.value = v;
5413
+ },
5414
+ persist: (on = true) => {
5415
+ this._privateState.persist = on;
5416
+ }
5417
+ };
5418
+ }
5419
+ // ── downstream emission pipeline (the unified waist) ──
5420
+ _down(msgs) {
5421
+ if (this._released) return;
5422
+ validateDownPayloads(msgs);
5423
+ const deliveryWave = {};
5424
+ const assertVersionDataCompatible = (wave) => {
5425
+ for (const m of wave) {
5426
+ if (m[0] === "DATA") assertNodeVersionDataCompatible(this._version.policy, m[1]);
5427
+ }
5428
+ };
5429
+ const snapshotVersionData = (wave) => wave.map(
5430
+ (m) => m[0] === "DATA" ? ["DATA", snapshotNodeVersionData(this._version.policy, m[1])] : m
5431
+ );
5432
+ assertVersionDataCompatible(msgs);
5433
+ if (this._value.terminal !== void 0) {
5434
+ if (!msgs.some((m) => m[0] === "TEARDOWN")) return;
5435
+ this._value.hasTorndown = true;
5436
+ if (this._slot.resetOnTeardown) {
5437
+ this._value.cache = SENTINEL;
5438
+ this._value.hasData = false;
5439
+ }
5440
+ this._emitToSubs(["TEARDOWN"], { wave: deliveryWave, last: true });
5441
+ return;
5442
+ }
5443
+ let sorted = [...msgs].sort((a, b) => messageTier(a[0]) - messageTier(b[0]));
5444
+ const firstInvalidate = sorted.findIndex((m) => m[0] === "INVALIDATE");
5445
+ if (firstInvalidate !== -1) {
5446
+ sorted = sorted.filter((m, i) => m[0] !== "INVALIDATE" || i === firstInvalidate);
5447
+ }
5448
+ const hasTeardown = sorted.some((m) => m[0] === "TEARDOWN");
5449
+ const hasTerminal = sorted.some((m) => m[0] === "COMPLETE" || m[0] === "ERROR");
5450
+ if (hasTeardown && !hasTerminal && this._value.terminal === void 0 && !this._value.hasTorndown) {
5451
+ sorted = [["COMPLETE"], ...sorted];
5452
+ }
5453
+ if (!this._wave.insideRunWave && currentBatch()) {
5454
+ const deferred = snapshotVersionData(sorted.filter((m) => isDeferredTier(m[0])));
5455
+ if (deferred.length > 0) {
5456
+ if (!this._wave.emittedDirtyThisWave) {
5457
+ this._wave.emittedDirtyThisWave = true;
5458
+ this._value.status = "dirty";
5459
+ this._emitToSubs(["DIRTY"], { wave: deliveryWave, last: false });
5460
+ }
5461
+ this._wave.batchDirtyOwed = true;
5462
+ deferToBatch(this, deferred);
5463
+ return;
5464
+ }
5465
+ }
5466
+ if (this._shouldBufferOnPause()) {
5467
+ const buffered = snapshotVersionData(sorted.filter((m) => isPauseBufferedTier(m[0])));
5468
+ if (buffered.length > 0) {
5469
+ this._wave.emittedSettleThisWave = true;
5470
+ this._control.pauseBuffer.push(buffered);
5471
+ }
5472
+ sorted = sorted.filter((m) => !isPauseBufferedTier(m[0]));
5473
+ if (sorted.length === 0) return;
5474
+ }
5475
+ let dataCount = 0;
5476
+ let hasTier3 = false;
5477
+ let hasResolved = false;
5478
+ for (const m of sorted) {
5479
+ if (m[0] === "DATA") dataCount++;
5480
+ if (m[0] === "RESOLVED") hasResolved = true;
5481
+ if (isValueTier(m[0])) hasTier3 = true;
5482
+ }
5483
+ if (dataCount >= 1 && hasResolved) {
5484
+ throw new Error(
5485
+ "down: a wave cannot mix DATA and RESOLVED (tier-3 exclusivity, R-resolved-undirty)"
5486
+ );
5487
+ }
5488
+ const plannedVersions = new Array(sorted.length);
5489
+ if (dataCount > 0) {
5490
+ let plannedVersion = this._version.value;
5491
+ for (let i = 0; i < sorted.length; i++) {
5492
+ const m = sorted[i];
5493
+ if (m[0] !== "DATA") continue;
5494
+ plannedVersion = advanceNodeVersion(plannedVersion, this._version.policy, m[1]);
5495
+ plannedVersions[i] = plannedVersion;
5496
+ }
5497
+ }
5498
+ if (hasTier3 && (!this._wave.insideRunWave || this._control.pullDirtyOwed) && !this._wave.emittedDirtyThisWave) {
5499
+ this._wave.emittedDirtyThisWave = true;
5500
+ this._value.status = "dirty";
5501
+ this._emitToSubs(["DIRTY"], { wave: deliveryWave, last: false });
5502
+ }
5503
+ for (let i = 0; i < sorted.length; i++) {
5504
+ const m = sorted[i];
5505
+ const delivery = { wave: deliveryWave, last: i === sorted.length - 1 };
5506
+ if (isDeferredTier(m[0])) this._wave.emittedSettleThisWave = true;
5507
+ if (m[0] === "DIRTY") {
5508
+ if (!this._wave.emittedDirtyThisWave) {
5509
+ this._wave.emittedDirtyThisWave = true;
5510
+ this._value.status = "dirty";
5511
+ this._emitToSubs(["DIRTY"], delivery);
5512
+ }
5513
+ continue;
5514
+ }
5515
+ if (m[0] === "DATA") {
5516
+ const v = m[1];
5517
+ this._value.cache = v;
5518
+ this._value.hasData = true;
5519
+ this._value.status = "settled";
5520
+ this._version.value = plannedVersions[i];
5521
+ if (this._slot.replayN > 0) {
5522
+ this._value.replayRing.push(v);
5523
+ if (this._value.replayRing.length > this._slot.replayN) this._value.replayRing.shift();
5524
+ }
5525
+ this._emitToSubs(["DATA", v], delivery);
5526
+ continue;
5527
+ }
5528
+ if (m[0] === "RESOLVED") {
5529
+ this._value.status = this._value.hasData ? "resolved" : "sentinel";
5530
+ this._emitToSubs(["RESOLVED"], delivery);
5531
+ continue;
5532
+ }
5533
+ if (m[0] === "INVALIDATE") {
5534
+ this._invalidate(delivery);
5535
+ continue;
5536
+ }
5537
+ if (m[0] === "COMPLETE") {
5538
+ if (this._value.terminal !== void 0) continue;
5539
+ this._value.terminal = true;
5540
+ this._control.pauseBuffer = [];
5541
+ this._value.status = "completed";
5542
+ this._emitToSubs(["COMPLETE"], delivery);
5543
+ continue;
5544
+ }
5545
+ if (m[0] === "ERROR") {
5546
+ if (this._value.terminal !== void 0) continue;
5547
+ this._value.terminal = m[1];
5548
+ this._control.pauseBuffer = [];
5549
+ this._value.status = "errored";
5550
+ this._emitToSubs(["ERROR", m[1]], delivery);
5551
+ continue;
5552
+ }
5553
+ if (m[0] === "TEARDOWN") {
5554
+ this._value.hasTorndown = true;
5555
+ if (this._slot.resetOnTeardown) {
5556
+ this._value.cache = SENTINEL;
5557
+ this._value.hasData = false;
5558
+ }
5559
+ this._emitToSubs(["TEARDOWN"], delivery);
5560
+ }
5561
+ }
5562
+ if (!this._wave.insideRunWave) this._wave.emittedDirtyThisWave = false;
5563
+ }
5564
+ _up(msgs, towardDep, route) {
5565
+ if (this._released) return;
5566
+ const routeState = route ?? { demandFired: /* @__PURE__ */ new Map() };
5567
+ for (const m of msgs) {
5568
+ if (!isUpAllowed(m[0])) {
5569
+ throw new Error(
5570
+ `ctx.up: ${m[0]} is down-only (tier ${messageTier(m[0])}); up carries control tiers only (R-ctx-up)`
5571
+ );
5572
+ }
5573
+ }
5574
+ for (const m of msgs) {
5575
+ if (m[0] === "PAUSE") {
5576
+ this._pauseAcquire(m[1]);
5577
+ } else if (m[0] === "RESUME") {
5578
+ if (this._control.pauseLockset.has(m[1])) {
5579
+ this._pauseRelease(m[1]);
5580
+ } else {
5581
+ this._forwardUp(m, towardDep, routeState);
5582
+ }
5583
+ } else if (m[0] === "PULL") {
5584
+ const demand = normalizePullDemand(m[1]);
5585
+ if (this._slot.pull && demand.pullId === this._slot.pullLock) {
5586
+ if (!this._markDemandRouted(demand.pullId, routeState)) this._onDemand(demand);
5587
+ } else {
5588
+ this._forwardUp(["PULL", demand], towardDep, routeState);
5589
+ }
5590
+ } else if (this._slot.deps.length === 0) {
5591
+ if (m[0] === "INVALIDATE") this._down([["INVALIDATE"]]);
5592
+ } else {
5593
+ this._forwardUp(m, towardDep, routeState);
5594
+ }
5595
+ }
5596
+ }
5597
+ _markDemandRouted(lockId, route) {
5598
+ let holders = route.demandFired.get(lockId);
5599
+ if (holders === void 0) {
5600
+ holders = /* @__PURE__ */ new Set();
5601
+ route.demandFired.set(lockId, holders);
5602
+ }
5603
+ if (holders.has(this)) return true;
5604
+ holders.add(this);
5605
+ return false;
5606
+ }
5607
+ /**
5608
+ * Forward one up-going control message toward deps (R-up-routing): up the single declared edge
5609
+ * `towardDep` (directed — prunes other branches; only meaningful on the issuer's FIRST hop) or
5610
+ * broadcast up ALL deps. A depless source has no deps → the message DROPS here (the terminus —
5611
+ * e.g. a cone-routed PULL whose pullId no node up this cone holds, R-up-at-source). Recursive
5612
+ * forwarding carries the same per-wave route state and no `towardDep` → broadcast beyond the
5613
+ * first hop.
5614
+ */
5615
+ _forwardUp(m, towardDep, route) {
5616
+ if (this._slot.deps.length === 0) return;
5617
+ if (towardDep !== void 0) {
5618
+ const d = this._slot.deps[towardDep];
5619
+ if (d !== void 0) d._up([m], void 0, route);
5620
+ } else {
5621
+ for (const dep of this._slot.deps) dep._up([m], void 0, route);
5622
+ }
5623
+ }
5624
+ // ── PAUSE/RESUME lockset (R-pause-lockset) + modes (R-pause-modes) ──
5625
+ _isPaused() {
5626
+ return this._control.pauseLockset.size > 0;
5627
+ }
5628
+ _hasBoundaryPauseLock() {
5629
+ if (this._slot.pausable === false) return false;
5630
+ return this._control.pauseLockset.size > 0;
5631
+ }
5632
+ _isAsyncPool() {
5633
+ return this._slot.handle !== null && this._slot.dispatcher.poolKind(this._slot.handle.poolId) === "async";
5634
+ }
5635
+ _pauseAcquire(lockId) {
5636
+ this._control.pauseLockset.add(lockId);
5637
+ }
5638
+ _pauseRelease(lockId) {
5639
+ if (!this._control.pauseLockset.has(lockId)) return;
5640
+ this._control.pauseLockset.delete(lockId);
5641
+ if (this._slot.pull && this._control.demandOwed !== void 0) this._fireOwedDemandIfReady();
5642
+ if (this._hasBoundaryPauseLock()) return;
5643
+ scheduleBoundaryDrain(this._core);
5644
+ if (this._slot.pull) return;
5645
+ this._onResume();
5646
+ }
5647
+ _onResume() {
5648
+ if (this._value.terminal !== void 0) {
5649
+ this._control.pauseBuffer = [];
5650
+ this._control.pausedDepWaveOccurred = false;
5651
+ this._control.demandOwed = void 0;
5652
+ this._control.activePull = void 0;
5653
+ this._control.pullDirtyOwed = false;
5654
+ return;
5655
+ }
5656
+ if (this._control.pauseBuffer.length > 0) {
5657
+ const buf = this._control.pauseBuffer;
5658
+ this._control.pauseBuffer = [];
5659
+ for (const wave of buf) this._down(wave);
5660
+ }
5661
+ if (this._control.pausedDepWaveOccurred) {
5662
+ this._control.pausedDepWaveOccurred = false;
5663
+ this._tryRun();
5664
+ }
5665
+ }
5666
+ /**
5667
+ * R-pull (D269): can this pull node fire a demand NOW? — not terminal, settle-ready (_pending===0),
5668
+ * and no OTHER (non-pullId) lock holds it paused. The gate that decides fire-now vs OWE.
5669
+ */
5670
+ _canFireDemand() {
5671
+ if (this._value.terminal !== void 0 || this._wave.pending > 0) return false;
5672
+ return this._control.pauseLockset.size === 0;
5673
+ }
5674
+ /**
5675
+ * R-pull (D269/D272): deliver ONE demand pulse — install activePull so the demand wave may
5676
+ * emit, fire it, then clear activePull so the node is quiet again (1:1).
5677
+ */
5678
+ _deliverPullDemand(demand) {
5679
+ this._control.demandOwed = void 0;
5680
+ this._control.activePull = demand;
5681
+ this._control.inDeliverDemand = true;
5682
+ try {
5683
+ this._firePullDemand();
5684
+ } finally {
5685
+ this._control.activePull = void 0;
5686
+ this._control.inDeliverDemand = false;
5687
+ }
5688
+ }
5689
+ /**
5690
+ * R-pull (D269): a cone-routed PULL of THIS node's pullId arrived (R-up-routing) = a DEMAND.
5691
+ * Fire immediately if able; else OWE it — pin-5 (a dep DIRTY in flight, _pending>0) or F4/F5 (an
5692
+ * external PAUSE lock co-holds the node) — and fire when the node next becomes able. A demand
5693
+ * arriving WHILE a delivery is in flight (synchronous re-entry) is DROPPED (1:1, QA guard).
5694
+ */
5695
+ _onDemand(demand) {
5696
+ if (this._control.inDeliverDemand) return;
5697
+ if (this._canFireDemand()) this._deliverPullDemand(demand);
5698
+ else this._control.demandOwed = demand;
5699
+ }
5700
+ /**
5701
+ * R-pull (D55): service ONE demand. Delivery content = the orthogonal pausable mode:
5702
+ * - 'resumeAll' → drain the buffered BACKLOG (each `_down` re-synthesizes a leading DIRTY, so
5703
+ * the per-entry replay is DIRTY-before-DATA — R-pause-modes per-entry arrival order);
5704
+ * - true → deliver the coalesced LATEST as ONE DIRTY-before-DATA wave (pin 2: the quiet absorb
5705
+ * suppressed the dep's original DIRTY, so the demand wave restores it before the value).
5706
+ * Neither branch fires when nothing changed since the last demand → the demand is SILENT.
5707
+ * Called by `_deliverPullDemand` only when settle-ready; the caller has released the pullId, so
5708
+ * `_markDirty` relays the leading DIRTY (not absorbed).
5709
+ */
5710
+ _firePullDemand() {
5711
+ let drainedBuffer = false;
5712
+ if (this._control.pauseBuffer.length > 0) {
5713
+ const buf = this._control.pauseBuffer;
5714
+ this._control.pauseBuffer = [];
5715
+ for (const wave of buf) this._down(wave);
5716
+ drainedBuffer = true;
5717
+ }
5718
+ if (drainedBuffer) return;
5719
+ if (this._control.pausedDepWaveOccurred) {
5720
+ const gated = this._slot.handle !== null && !this._wave.hasCalledFnOnce && !(this._slot.partial || this._allDepsSettled());
5721
+ if (this._wave.pending > 0 || gated) return;
5722
+ this._control.pausedDepWaveOccurred = false;
5723
+ this._wave.emittedDirtyThisWave = false;
5724
+ this._control.pullDirtyOwed = true;
5725
+ try {
5726
+ this._tryRun();
5727
+ } finally {
5728
+ this._control.pullDirtyOwed = false;
5729
+ }
5730
+ return;
5731
+ }
5732
+ if (this._slot.handle !== null) {
5733
+ if (!this._wave.hasCalledFnOnce && !(this._slot.partial || this._allDepsSettled())) return;
5734
+ this._wave.emittedDirtyThisWave = false;
5735
+ this._control.pullDirtyOwed = true;
5736
+ try {
5737
+ this._runWave();
5738
+ } finally {
5739
+ this._control.pullDirtyOwed = false;
5740
+ }
5741
+ }
5742
+ }
5743
+ /**
5744
+ * R-pull (D269): fire a demand that was OWED (deferred at demand time) once the node becomes
5745
+ * able — called from the dep-settle arms (DATA/RESOLVED/INVALIDATE/terminal drain _pending, pin-5
5746
+ * + B1/F6) and from `_pauseRelease` (an external PAUSE lock releases, F4/F5).
5747
+ */
5748
+ _fireOwedDemandIfReady() {
5749
+ if (this._control.inDeliverDemand) return;
5750
+ if (this._slot.pull && this._control.demandOwed !== void 0 && this._canFireDemand()) {
5751
+ this._deliverPullDemand(this._control.demandOwed);
5752
+ }
5753
+ }
5754
+ /** Should an outgoing settle slice be deferred into the pause buffer? */
5755
+ _shouldBufferOnPause() {
5756
+ if (this._slot.pausable === false) return false;
5757
+ if (!this._isPaused() && !this._isPullQuiet()) return false;
5758
+ if (this._slot.pausable === "resumeAll") return true;
5759
+ if (!this._wave.insideRunWave && this._isAsyncPool() && this._slot.deps.length > 0) return true;
5760
+ return false;
5761
+ }
5762
+ /** R-invalidate-idempotent: clear cache + flush + cascade; no-op if nothing cached. */
5763
+ _invalidate(delivery) {
5764
+ if (!this._value.hasData) return;
5765
+ this._value.cache = SENTINEL;
5766
+ this._value.hasData = false;
5767
+ this._value.status = "sentinel";
5768
+ this._value.replayRing = [];
5769
+ for (const fn of this._hooks.onInvalidate) fn();
5770
+ this._emitToSubs(["INVALIDATE"], delivery);
5771
+ }
5772
+ // B42 (R-deps-terminal): ALL deps TERMINAL = every dep has reached COMPLETE *or* an ABSORBED
5773
+ // ERROR. Block only on a LIVE dep (_depTerminal[i] === undefined); an errored dep (terminal = the
5774
+ // error payload, which R-data-payload guarantees is !== undefined and non-boolean) COUNTS as
5775
+ // terminal-done. Was
5776
+ // `tm !== true`, which wedged a node whose errorWhenDepsError:false dep ERRORed (it never
5777
+ // auto-completed even after every other dep completed). Drives the completeWhenDepsComplete cascade.
5778
+ _allDepsTerminal() {
5779
+ if (this._slot.deps.length === 0) return false;
5780
+ for (const tm of this._dep.terminal) if (tm === void 0) return false;
5781
+ return true;
5782
+ }
5783
+ /** R-terminal: resubscribable reset clears terminal + dep state + re-arms the gate. */
5784
+ _resetLifecycle() {
5785
+ for (const u of this._dep.unsubs) if (u) u();
5786
+ this._dep.unsubs = [];
5787
+ this._dep.idxBoxes = [];
5788
+ this._lifecycle.subscribers.clear();
5789
+ this._lifecycle.activated = false;
5790
+ this._value.terminal = void 0;
5791
+ this._value.hasTorndown = false;
5792
+ this._wave.hasCalledFnOnce = false;
5793
+ this._resetDepState();
5794
+ this._control.pauseLockset.clear();
5795
+ this._control.pauseBuffer = [];
5796
+ this._control.pausedDepWaveOccurred = false;
5797
+ this._control.demandOwed = void 0;
5798
+ this._control.activePull = void 0;
5799
+ this._control.pullDirtyOwed = false;
5800
+ this._value.replayRing = [];
5801
+ const isCompute = this._slot.handle !== null || this._slot.deps.length > 0;
5802
+ if (isCompute) {
5803
+ this._value.cache = SENTINEL;
5804
+ this._value.hasData = false;
5805
+ this._value.status = "sentinel";
5806
+ } else {
5807
+ this._value.status = this._value.hasData ? "settled" : "sentinel";
5808
+ }
5809
+ if (!this._privateState.persist) this._privateState.value = SENTINEL;
5810
+ }
5811
+ _emitToSubs(msg, delivery) {
5812
+ if (this._released) return;
5813
+ const subs = [...this._lifecycle.subscribers];
5814
+ for (const sink of subs) sink(msg, delivery);
5815
+ }
5816
+ /** Batch commit (R-batch-coalesce): deliver the deferred tier-3 wave now. */
5817
+ __commitBatchedWave(wave) {
5818
+ this._wave.batchDirtyOwed = false;
5819
+ this._down(wave);
5820
+ }
5821
+ /** Batch rollback: balance the immediate DIRTY with a RESOLVED so downstream un-dirties. */
5822
+ __rollbackBatched() {
5823
+ if (this._wave.batchDirtyOwed) {
5824
+ this._wave.batchDirtyOwed = false;
5825
+ this._wave.emittedDirtyThisWave = false;
5826
+ this._value.status = this._value.hasData ? "settled" : "sentinel";
5827
+ this._emitToSubs(["RESOLVED"]);
5828
+ }
5829
+ }
5830
+ /** B49: enqueue a committed-boundary task on this node's graph-local core. */
5831
+ __deferBoundary(fn, batchToken) {
5832
+ deferRewire(this._core, fn, {
5833
+ batchToken,
5834
+ isReady: () => !this._hasBoundaryPauseLock()
5835
+ });
5836
+ }
5837
+ };
5838
+
5839
+ // src/solutions/reactive-layout/index.ts
5840
+ var TEXT_SEGMENTS_MEASUREMENT_KIND = "text-segments";
5841
+ var BLOCKS_MEASUREMENT_KIND = "blocks";
5842
+ var READINESS_MEASUREMENT_KIND = "readiness";
5843
+ var IMAGE_SIZE_MEASUREMENT_KIND = "image-size";
5844
+ var SVG_BOUNDS_MEASUREMENT_KIND = "svg-bounds";
5845
+ var kinsokuStart = /* @__PURE__ */ new Set([
5846
+ "\u3001",
5847
+ "\u3002",
5848
+ "\u30FB",
5849
+ "\uFF09",
5850
+ "\u3015",
5851
+ "\u3009",
5852
+ "\u300B",
5853
+ "\u300D",
5854
+ "\u300F",
5855
+ "\u3011"
5856
+ ]);
5857
+ var leftStickyPunctuation = /* @__PURE__ */ new Set([
5858
+ ".",
5859
+ ",",
5860
+ "!",
5861
+ "?",
5862
+ ":",
5863
+ ";",
5864
+ ")",
5865
+ "]",
5866
+ "}",
5867
+ "%",
5868
+ '"',
5869
+ "\u201D",
5870
+ "\u2019",
5871
+ "\xBB",
5872
+ "\u203A",
5873
+ "\u2026"
5874
+ ]);
5875
+ function isCJK(text) {
5876
+ for (const ch of text) {
5877
+ const c = ch.codePointAt(0);
5878
+ if (c !== void 0 && c >= 19968 && c <= 40959 || c !== void 0 && c >= 13312 && c <= 19903 || c !== void 0 && c >= 12288 && c <= 12351 || c !== void 0 && c >= 12352 && c <= 12447 || c !== void 0 && c >= 44032 && c <= 55215 || c !== void 0 && c >= 65280 && c <= 65519) {
5879
+ return true;
5880
+ }
5881
+ }
5882
+ return false;
5883
+ }
5884
+ function normalizeWhitespace(text) {
5885
+ return text.replace(/[\t\f ]+/g, " ").replace(/^ | $/g, "");
5886
+ }
5887
+ function segmentText(text, segmentAdapter) {
5888
+ const pieces = [];
5889
+ for (const s of segmentAdapter.segmentWords(text)) {
5890
+ const wordSegment = s.segment;
5891
+ const rawWordLike = s.isWordLike ?? false;
5892
+ const pieceTexts = [];
5893
+ const pieceWordLikes = [];
5894
+ const pieceKinds = [];
5895
+ let currentText = "";
5896
+ let currentKind = null;
5897
+ for (const ch of wordSegment) {
5898
+ let kind;
5899
+ if (ch === " ") {
5900
+ kind = "space";
5901
+ } else if (ch === "\u200B") {
5902
+ kind = "zero-width-break";
5903
+ } else if (ch === "\xAD") {
5904
+ kind = "soft-hyphen";
5905
+ } else if (ch === "\n") {
5906
+ kind = "hard-break";
5907
+ } else {
5908
+ kind = "text";
5909
+ }
5910
+ if (currentKind !== null && kind === currentKind) {
5911
+ currentText += ch;
5912
+ } else {
5913
+ if (currentKind !== null) {
5914
+ pieceTexts.push(currentText);
5915
+ pieceWordLikes.push(currentKind === "text" && rawWordLike);
5916
+ pieceKinds.push(currentKind);
5917
+ }
5918
+ currentText = ch;
5919
+ currentKind = kind;
5920
+ }
5921
+ }
5922
+ if (currentKind !== null) {
5923
+ pieceTexts.push(currentText);
5924
+ pieceWordLikes.push(currentKind === "text" && rawWordLike);
5925
+ pieceKinds.push(currentKind);
5926
+ }
5927
+ pieces.push({ texts: pieceTexts, isWordLike: pieceWordLikes, kinds: pieceKinds });
5928
+ }
5929
+ return pieces;
5930
+ }
5931
+ function* fallbackSegmentWords(text) {
5932
+ let index = 0;
5933
+ let current = "";
5934
+ let currentIndex = 0;
5935
+ let currentWordLike = null;
5936
+ const flush = () => {
5937
+ if (current.length === 0 || currentWordLike === null) return void 0;
5938
+ const out2 = { segment: current, index: currentIndex, isWordLike: currentWordLike };
5939
+ current = "";
5940
+ currentWordLike = null;
5941
+ return out2;
5942
+ };
5943
+ for (const ch of text) {
5944
+ const isWordLike = !/[\s\u200B\u00AD]/u.test(ch);
5945
+ if (currentWordLike !== null && currentWordLike !== isWordLike) {
5946
+ const out2 = flush();
5947
+ if (out2) yield out2;
5948
+ }
5949
+ if (currentWordLike === null) {
5950
+ currentWordLike = isWordLike;
5951
+ currentIndex = index;
5952
+ }
5953
+ current += ch;
5954
+ index += ch.length;
5955
+ }
5956
+ const out = flush();
5957
+ if (out) yield out;
5958
+ }
5959
+ function* fallbackSegmentGraphemes(text) {
5960
+ let index = 0;
5961
+ for (const ch of text) {
5962
+ yield { segment: ch, index };
5963
+ index += ch.length;
5964
+ }
5965
+ }
5966
+ function createDefaultSegmentAdapter() {
5967
+ return {
5968
+ segmentWords(text) {
5969
+ return fallbackSegmentWords(text);
5970
+ },
5971
+ segmentGraphemes(text) {
5972
+ return fallbackSegmentGraphemes(text);
5973
+ }
5974
+ };
5975
+ }
5976
+ var defaultSegmentAdapter = null;
5977
+ function getDefaultSegmentAdapter() {
5978
+ if (defaultSegmentAdapter === null) {
5979
+ defaultSegmentAdapter = createDefaultSegmentAdapter();
5980
+ }
5981
+ return defaultSegmentAdapter;
5982
+ }
5983
+ function measuredWidth(value) {
5984
+ const width = typeof value === "number" ? value : value.width;
5985
+ return Number.isFinite(width) && width >= 0 ? width : 0;
5986
+ }
5987
+ function metricKey(text, font) {
5988
+ return `${font}\0${text}`;
5989
+ }
5990
+ var InjectedMeasureAdapter = class {
5991
+ measure;
5992
+ shouldCache;
5993
+ cache = /* @__PURE__ */ new Map();
5994
+ constructor(measure, opts = {}) {
5995
+ this.measure = measure;
5996
+ this.shouldCache = opts.cache ?? true;
5997
+ }
5998
+ measureSegment(text, font) {
5999
+ const key = metricKey(text, font);
6000
+ if (this.shouldCache) {
6001
+ const cached = this.cache.get(key);
6002
+ if (cached !== void 0) return { width: cached };
6003
+ }
6004
+ const width = measuredWidth(this.measure(text, font));
6005
+ if (this.shouldCache) this.cache.set(key, width);
6006
+ return { width };
6007
+ }
6008
+ clearCache() {
6009
+ this.cache.clear();
6010
+ }
6011
+ };
6012
+ var PrecomputedMeasureAdapter = class {
6013
+ metrics;
6014
+ fallback;
6015
+ cellWidth;
6016
+ constructor(opts) {
6017
+ this.metrics = opts.metrics instanceof Map ? opts.metrics : new Map(Object.entries(opts.metrics));
6018
+ this.fallback = opts.fallback ?? "error";
6019
+ this.cellWidth = nonNegativeFinite(opts.cellWidth ?? 8, 8);
6020
+ }
6021
+ measureSegment(text, font) {
6022
+ const fontScoped = this.metrics.get(metricKey(text, font));
6023
+ if (fontScoped !== void 0) return { width: nonNegativeFinite(fontScoped, 0) };
6024
+ const unscoped = this.metrics.get(text);
6025
+ if (unscoped !== void 0) return { width: nonNegativeFinite(unscoped, 0) };
6026
+ if (this.fallback === "per-char") {
6027
+ return { width: Array.from(text).length * this.cellWidth };
6028
+ }
6029
+ throw new Error(`No precomputed metric for segment ${JSON.stringify(text)}`);
6030
+ }
6031
+ };
6032
+ var CellMeasureAdapter = class {
6033
+ cellWidth;
6034
+ wideCellWidth;
6035
+ tabCells;
6036
+ constructor(opts = {}) {
6037
+ this.cellWidth = nonNegativeFinite(opts.cellWidth ?? 8, 8);
6038
+ this.wideCellWidth = nonNegativeFinite(
6039
+ opts.wideCellWidth ?? this.cellWidth * 2,
6040
+ this.cellWidth * 2
6041
+ );
6042
+ this.tabCells = Math.max(1, Math.floor(nonNegativeFinite(opts.tabCells ?? 4, 4)));
6043
+ }
6044
+ measureSegment(text) {
6045
+ let width = 0;
6046
+ for (const char of text) {
6047
+ if (char === " ") width += this.cellWidth * this.tabCells;
6048
+ else width += isCJK(char) ? this.wideCellWidth : this.cellWidth;
6049
+ }
6050
+ return { width };
6051
+ }
6052
+ };
6053
+ var CapabilityMeasureAdapter = class {
6054
+ capability;
6055
+ constructor(capability) {
6056
+ this.capability = capability;
6057
+ }
6058
+ measureSegment(text, font) {
6059
+ return { width: measuredWidth(this.capability.measureText(text, font)) };
6060
+ }
6061
+ clearCache() {
6062
+ this.capability.clearCache?.();
6063
+ }
6064
+ };
6065
+ function stripSvgIgnoredContent(svg) {
6066
+ return svg.replace(/<!--[\s\S]*?-->/g, "").replace(/<!\[CDATA\[[\s\S]*?\]\]>/g, "");
6067
+ }
6068
+ function readSvgRootTag(svg) {
6069
+ return /<svg\b[^>]*>/i.exec(svg)?.[0] ?? null;
6070
+ }
6071
+ function readNumericAttr(tag, attr) {
6072
+ const pattern = new RegExp(`(?:^|\\s)${attr}\\s*=\\s*["']?(-?\\d+(?:\\.\\d+)?)`, "i");
6073
+ const match = pattern.exec(tag);
6074
+ if (!match) return null;
6075
+ const value = Number(match[1]);
6076
+ return Number.isFinite(value) && value >= 0 ? value : null;
6077
+ }
6078
+ function readViewBox(tag) {
6079
+ const match = /\bviewBox\s*=\s*["']\s*(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?)\s*["']/i.exec(
6080
+ tag
6081
+ );
6082
+ if (!match) return null;
6083
+ const width = Number(match[3]);
6084
+ const height = Number(match[4]);
6085
+ if (!Number.isFinite(width) || !Number.isFinite(height) || width < 0 || height < 0) {
6086
+ return null;
6087
+ }
6088
+ return { width, height };
6089
+ }
6090
+ var SvgBoundsAdapter = class {
6091
+ measureSvg(svg) {
6092
+ const cleaned = stripSvgIgnoredContent(svg);
6093
+ const rootTag = readSvgRootTag(cleaned);
6094
+ if (rootTag === null) throw new Error("Cannot measure SVG without a root <svg> element");
6095
+ const width = readNumericAttr(rootTag, "width");
6096
+ const height = readNumericAttr(rootTag, "height");
6097
+ if (width !== null && height !== null) return { width, height };
6098
+ const viewBox = readViewBox(rootTag);
6099
+ if (viewBox) return viewBox;
6100
+ throw new Error("Cannot measure SVG without width/height or viewBox");
6101
+ }
6102
+ };
6103
+ var ImageSizeAdapter = class {
6104
+ sizes;
6105
+ constructor(sizes) {
6106
+ this.sizes = isImageSizeLookup(sizes) ? sizes : new Map(Object.entries(sizes));
6107
+ }
6108
+ measureImage(src) {
6109
+ const size = this.sizes.get(src);
6110
+ if (!size) throw new Error(`No image size registered for ${JSON.stringify(src)}`);
6111
+ return size;
6112
+ }
6113
+ };
6114
+ function isImageSizeLookup(value) {
6115
+ if (value === null) return false;
6116
+ const valueType = typeof value;
6117
+ if (valueType !== "object" && valueType !== "function") return false;
6118
+ return typeof value.get === "function";
6119
+ }
6120
+ function measurementIssue(code, message, targetId, measurementKind, opts = {}) {
6121
+ return {
6122
+ kind: "issue",
6123
+ code,
6124
+ message,
6125
+ severity: opts.severity ?? "warning",
6126
+ source: opts.source,
6127
+ subjectId: targetId,
6128
+ measurementKind,
6129
+ details: opts.details,
6130
+ metadata: opts.metadata
6131
+ };
6132
+ }
6133
+ function measurementOk(targetId, measurementKind, value, opts = {}) {
6134
+ return {
6135
+ kind: "ok",
6136
+ targetId,
6137
+ measurementKind,
6138
+ value,
6139
+ source: opts.source,
6140
+ metadata: opts.metadata
6141
+ };
6142
+ }
6143
+ function latestMeasurementValue(measurements, targetId, measurementKind) {
6144
+ let value;
6145
+ for (const fact of measurements) {
6146
+ if (fact.kind === "ok" && fact.targetId === targetId && fact.measurementKind === measurementKind) {
6147
+ value = fact.value;
6148
+ }
6149
+ }
6150
+ return value;
6151
+ }
6152
+ function tryMeasureHyphenWidth(adapter, font) {
6153
+ try {
6154
+ return nonNegativeFinite(adapter.measureSegment("-", font).width, 0);
6155
+ } catch {
6156
+ return void 0;
6157
+ }
6158
+ }
6159
+ function validMeasurementSize(size, label) {
6160
+ if (!Number.isFinite(size.width) || !Number.isFinite(size.height) || size.width < 0 || size.height < 0) {
6161
+ throw new Error(`Invalid ${label} measurement size`);
6162
+ }
6163
+ return size;
6164
+ }
6165
+ function textMeasurementFacts(text, font, adapter, cache, segmentAdapter, targetId, source) {
6166
+ try {
6167
+ const segments = analyzeAndMeasure(text, font, adapter, cache, void 0, segmentAdapter);
6168
+ const hyphenWidth = tryMeasureHyphenWidth(adapter, font);
6169
+ const facts = [
6170
+ measurementOk(
6171
+ targetId,
6172
+ TEXT_SEGMENTS_MEASUREMENT_KIND,
6173
+ hyphenWidth === void 0 ? { segments } : { segments, hyphenWidth },
6174
+ { source }
6175
+ )
6176
+ ];
6177
+ if (hyphenWidth === void 0) {
6178
+ facts.push(
6179
+ measurementIssue(
6180
+ "measurement.hyphen.failed",
6181
+ `Hyphen measurement failed for '${targetId}'`,
6182
+ targetId,
6183
+ TEXT_SEGMENTS_MEASUREMENT_KIND,
6184
+ { source, severity: "warning" }
6185
+ )
6186
+ );
6187
+ }
6188
+ return facts;
6189
+ } catch (error) {
6190
+ return [
6191
+ measurementIssue(
6192
+ "measurement.failed",
6193
+ `Text measurement failed for '${targetId}'`,
6194
+ targetId,
6195
+ TEXT_SEGMENTS_MEASUREMENT_KIND,
6196
+ { source, details: error, severity: "error" }
6197
+ )
6198
+ ];
6199
+ }
6200
+ }
6201
+ function inputNode(g, input, fallback, name) {
6202
+ if (input instanceof Node) {
6203
+ return {
6204
+ node: input,
6205
+ set(value) {
6206
+ const maybeState = input;
6207
+ if (typeof maybeState.set !== "function") {
6208
+ throw new TypeError(`reactive-layout input '${name}' is not a writable state node`);
6209
+ }
6210
+ maybeState.set(value);
6211
+ }
6212
+ };
6213
+ }
6214
+ const state = g.state(input ?? fallback, { name });
6215
+ return { node: state, set: (value) => state.set(value) };
6216
+ }
6217
+ function scopedName(scope, local) {
6218
+ return scope === local ? local : `${scope}:${local}`;
6219
+ }
6220
+ function textMeasurementProvider(opts) {
6221
+ const targetId = opts.targetId ?? "text";
6222
+ const name = opts.name ?? `${targetId}-measurements`;
6223
+ const source = opts.source ?? name;
6224
+ const cache = /* @__PURE__ */ new Map();
6225
+ let activeAdapter = null;
6226
+ return opts.graph.node(
6227
+ opts.segmentAdapter ? [opts.text, opts.font, opts.adapter, opts.segmentAdapter] : [opts.text, opts.font, opts.adapter],
6228
+ (ctx) => {
6229
+ const flush = () => {
6230
+ cache.clear();
6231
+ depLatest(ctx, 2).clearCache?.();
6232
+ };
6233
+ ctx.onDeactivation(flush);
6234
+ ctx.onInvalidate(flush);
6235
+ const text = depLatest(ctx, 0);
6236
+ const font = depLatest(ctx, 1);
6237
+ const adapter = depLatest(ctx, 2);
6238
+ if (adapter !== activeAdapter) {
6239
+ cache.clear();
6240
+ activeAdapter = adapter;
6241
+ }
6242
+ const segmentAdapter = opts.segmentAdapter === void 0 ? getDefaultSegmentAdapter() : depLatest(ctx, 3);
6243
+ ctx.down([
6244
+ [
6245
+ "DATA",
6246
+ textMeasurementFacts(text, font, adapter, cache, segmentAdapter, targetId, source)
6247
+ ]
6248
+ ]);
6249
+ },
6250
+ { name }
6251
+ );
6252
+ }
6253
+ function injectedTextMeasurements(opts) {
6254
+ const targetId = opts.targetId ?? "text";
6255
+ const adapter = opts.graph.state(
6256
+ new InjectedMeasureAdapter(opts.measure, { cache: opts.cache }),
6257
+ {
6258
+ name: opts.name ? scopedName(opts.name, "measure-capability") : `${targetId}-measure-capability`
6259
+ }
6260
+ );
6261
+ return textMeasurementProvider({
6262
+ ...opts,
6263
+ adapter,
6264
+ source: opts.source ?? "injectedTextMeasurements"
6265
+ });
6266
+ }
6267
+ function precomputedTextMeasurements(opts) {
6268
+ const targetId = opts.targetId ?? "text";
6269
+ const adapter = opts.graph.state(new PrecomputedMeasureAdapter(opts), {
6270
+ name: opts.name ? scopedName(opts.name, "measure-capability") : `${targetId}-measure-capability`
6271
+ });
6272
+ return textMeasurementProvider({
6273
+ ...opts,
6274
+ adapter,
6275
+ source: opts.source ?? "precomputedTextMeasurements"
6276
+ });
6277
+ }
6278
+ function cellTextMeasurements(opts) {
6279
+ const targetId = opts.targetId ?? "text";
6280
+ const adapter = opts.graph.state(new CellMeasureAdapter(opts), {
6281
+ name: opts.name ? scopedName(opts.name, "measure-capability") : `${targetId}-measure-capability`
6282
+ });
6283
+ return textMeasurementProvider({
6284
+ ...opts,
6285
+ adapter,
6286
+ source: opts.source ?? "cellTextMeasurements"
6287
+ });
6288
+ }
6289
+ function capabilityTextMeasurements(opts) {
6290
+ const targetId = opts.targetId ?? "text";
6291
+ const adapter = opts.graph.node(
6292
+ [opts.capability],
6293
+ (ctx) => {
6294
+ ctx.down([
6295
+ ["DATA", new CapabilityMeasureAdapter(depLatest(ctx, 0))]
6296
+ ]);
6297
+ },
6298
+ {
6299
+ name: opts.name ? scopedName(opts.name, "measure-capability") : `${targetId}-measure-capability`
6300
+ }
6301
+ );
6302
+ return textMeasurementProvider({
6303
+ ...opts,
6304
+ adapter,
6305
+ source: opts.source ?? "capabilityTextMeasurements"
6306
+ });
6307
+ }
6308
+ function readinessTextMeasurements(opts) {
6309
+ const targetId = opts.targetId ?? "text";
6310
+ const name = opts.name ?? `${targetId}-measurements`;
6311
+ const source = opts.source ?? name;
6312
+ const cache = /* @__PURE__ */ new Map();
6313
+ let activeAdapter = null;
6314
+ let activeReadiness = null;
6315
+ const deps = opts.segmentAdapter ? [opts.text, opts.font, opts.adapter, opts.readiness, opts.segmentAdapter] : [opts.text, opts.font, opts.adapter, opts.readiness];
6316
+ return opts.graph.node(
6317
+ deps,
6318
+ (ctx) => {
6319
+ const flush = () => {
6320
+ cache.clear();
6321
+ depLatest(ctx, 2).clearCache?.();
6322
+ };
6323
+ ctx.onDeactivation(flush);
6324
+ ctx.onInvalidate(flush);
6325
+ const text = depLatest(ctx, 0);
6326
+ const font = depLatest(ctx, 1);
6327
+ const adapter = depLatest(ctx, 2);
6328
+ if (adapter !== activeAdapter) {
6329
+ cache.clear();
6330
+ activeAdapter = adapter;
6331
+ }
6332
+ const readiness = depLatest(ctx, 3);
6333
+ if (readiness !== activeReadiness) {
6334
+ cache.clear();
6335
+ activeReadiness = readiness;
6336
+ }
6337
+ if (!readiness.ready) {
6338
+ ctx.down([
6339
+ [
6340
+ "DATA",
6341
+ [
6342
+ measurementIssue(
6343
+ readiness.code ?? "measurement.not-ready",
6344
+ readiness.message ?? `Measurement readiness blocked '${targetId}'`,
6345
+ targetId,
6346
+ TEXT_SEGMENTS_MEASUREMENT_KIND,
6347
+ {
6348
+ source: readiness.source ?? source,
6349
+ details: readiness.details,
6350
+ metadata: readiness.metadata,
6351
+ severity: "warning"
6352
+ }
6353
+ )
6354
+ ]
6355
+ ]
6356
+ ]);
6357
+ return;
6358
+ }
6359
+ const segmentAdapter = opts.segmentAdapter === void 0 ? getDefaultSegmentAdapter() : depLatest(ctx, 4);
6360
+ ctx.down([
6361
+ [
6362
+ "DATA",
6363
+ textMeasurementFacts(text, font, adapter, cache, segmentAdapter, targetId, source)
6364
+ ]
6365
+ ]);
6366
+ },
6367
+ { name }
6368
+ );
6369
+ }
6370
+ function readinessMeasurements(opts) {
6371
+ const targetId = opts.targetId ?? "measurement-readiness";
6372
+ const measurementKind = opts.measurementKind ?? READINESS_MEASUREMENT_KIND;
6373
+ const name = opts.name ?? `${targetId}-measurements`;
6374
+ const source = opts.source ?? name;
6375
+ return opts.graph.node(
6376
+ [opts.readiness],
6377
+ (ctx) => {
6378
+ const readiness = depLatest(ctx, 0);
6379
+ if (!readiness.ready) {
6380
+ ctx.down([
6381
+ [
6382
+ "DATA",
6383
+ [
6384
+ measurementIssue(
6385
+ readiness.code ?? "measurement.not-ready",
6386
+ readiness.message ?? `Measurement readiness blocked '${targetId}'`,
6387
+ targetId,
6388
+ measurementKind,
6389
+ {
6390
+ source: readiness.source ?? source,
6391
+ details: readiness.details,
6392
+ metadata: readiness.metadata,
6393
+ severity: "warning"
6394
+ }
6395
+ )
6396
+ ]
6397
+ ]
6398
+ ]);
6399
+ return;
6400
+ }
6401
+ ctx.down([
6402
+ [
6403
+ "DATA",
6404
+ [
6405
+ measurementOk(targetId, measurementKind, readiness, {
6406
+ source: readiness.source ?? source,
6407
+ metadata: readiness.metadata
6408
+ })
6409
+ ]
6410
+ ]
6411
+ ]);
6412
+ },
6413
+ { name }
6414
+ );
6415
+ }
6416
+ function imageSizeMeasurements(opts) {
6417
+ const measurementKind = opts.measurementKind ?? IMAGE_SIZE_MEASUREMENT_KIND;
6418
+ const name = opts.name ?? "image-size-measurements";
6419
+ const source = opts.source ?? name;
6420
+ return opts.graph.node(
6421
+ [opts.images, opts.measurer],
6422
+ (ctx) => {
6423
+ const images = depLatest(ctx, 0);
6424
+ const measurer = depLatest(ctx, 1);
6425
+ const facts = [];
6426
+ for (let i = 0; i < images.length; i += 1) {
6427
+ const image = images[i];
6428
+ const subjectId = image?.id ?? image?.src ?? `image:${i}`;
6429
+ try {
6430
+ if (image === void 0) throw new Error(`Missing image target at index ${i}`);
6431
+ facts.push(
6432
+ measurementOk(
6433
+ subjectId,
6434
+ measurementKind,
6435
+ validMeasurementSize(measurer.measureImage(image.src), "image"),
6436
+ {
6437
+ source,
6438
+ metadata: image.metadata
6439
+ }
6440
+ )
6441
+ );
6442
+ } catch (error) {
6443
+ facts.push(
6444
+ measurementIssue(
6445
+ "measurement.image-size.failed",
6446
+ `Image size measurement failed for '${subjectId}'`,
6447
+ subjectId,
6448
+ measurementKind,
6449
+ {
6450
+ source,
6451
+ details: { error, index: i, src: image?.src },
6452
+ metadata: image?.metadata,
6453
+ severity: "error"
6454
+ }
6455
+ )
6456
+ );
6457
+ }
6458
+ }
6459
+ ctx.down([["DATA", facts]]);
6460
+ },
6461
+ { name }
6462
+ );
6463
+ }
6464
+ function svgBoundsMeasurements(opts) {
6465
+ const measurementKind = opts.measurementKind ?? SVG_BOUNDS_MEASUREMENT_KIND;
6466
+ const name = opts.name ?? "svg-bounds-measurements";
6467
+ const source = opts.source ?? name;
6468
+ return opts.graph.node(
6469
+ [opts.svgs, opts.measurer],
6470
+ (ctx) => {
6471
+ const svgs = depLatest(ctx, 0);
6472
+ const measurer = depLatest(ctx, 1);
6473
+ const facts = [];
6474
+ for (let i = 0; i < svgs.length; i += 1) {
6475
+ const svg = svgs[i];
6476
+ const subjectId = svg?.id ?? `svg:${i}`;
6477
+ try {
6478
+ if (svg === void 0) throw new Error(`Missing SVG target at index ${i}`);
6479
+ facts.push(
6480
+ measurementOk(
6481
+ subjectId,
6482
+ measurementKind,
6483
+ validMeasurementSize(measurer.measureSvg(svg.svg), "SVG"),
6484
+ {
6485
+ source,
6486
+ metadata: svg.metadata
6487
+ }
6488
+ )
6489
+ );
6490
+ } catch (error) {
6491
+ facts.push(
6492
+ measurementIssue(
6493
+ "measurement.svg-bounds.failed",
6494
+ `SVG bounds measurement failed for '${subjectId}'`,
6495
+ subjectId,
6496
+ measurementKind,
6497
+ {
6498
+ source,
6499
+ details: { error, index: i },
6500
+ metadata: svg?.metadata,
6501
+ severity: "error"
6502
+ }
6503
+ )
6504
+ );
6505
+ }
6506
+ }
6507
+ ctx.down([["DATA", facts]]);
6508
+ },
6509
+ { name }
6510
+ );
6511
+ }
6512
+ function mergeMeasurements(opts) {
6513
+ const sources = [...opts.sources];
6514
+ return opts.graph.node(
6515
+ sources,
6516
+ (ctx) => {
6517
+ const facts = [];
6518
+ for (let i = 0; i < sources.length; i += 1) {
6519
+ facts.push(...depLatest(ctx, i));
6520
+ }
6521
+ ctx.down([["DATA", facts]]);
6522
+ },
6523
+ { name: opts.name ?? "measurements" }
6524
+ );
6525
+ }
6526
+ function blockAdaptersProvider(opts) {
6527
+ const deps = [];
6528
+ const positions = {};
6529
+ if (opts.text) {
6530
+ positions.text = deps.length;
6531
+ deps.push(opts.text);
6532
+ }
6533
+ if (opts.svg) {
6534
+ positions.svg = deps.length;
6535
+ deps.push(opts.svg);
6536
+ }
6537
+ if (opts.image) {
6538
+ positions.image = deps.length;
6539
+ deps.push(opts.image);
6540
+ }
6541
+ return opts.graph.node(
6542
+ deps,
6543
+ (ctx) => {
6544
+ const adapters = {
6545
+ text: positions.text === void 0 ? void 0 : depLatest(ctx, positions.text),
6546
+ svg: positions.svg === void 0 ? void 0 : depLatest(ctx, positions.svg),
6547
+ image: positions.image === void 0 ? void 0 : depLatest(ctx, positions.image)
6548
+ };
6549
+ ctx.down([["DATA", adapters]]);
6550
+ },
6551
+ { name: opts.name ?? "block-adapters" }
6552
+ );
6553
+ }
6554
+ function blockMeasurementProvider(opts) {
6555
+ const targetId = opts.targetId ?? "blocks";
6556
+ const name = opts.name ?? `${targetId}-measurements`;
6557
+ const source = opts.source ?? name;
6558
+ const cache = /* @__PURE__ */ new Map();
6559
+ let activeTextAdapter;
6560
+ const deps = [opts.blocks, opts.maxWidth, opts.adapters];
6561
+ if (opts.font) deps.push(opts.font);
6562
+ if (opts.lineHeight) deps.push(opts.lineHeight);
6563
+ if (opts.segmentAdapter) deps.push(opts.segmentAdapter);
6564
+ return opts.graph.node(
6565
+ deps,
6566
+ (ctx) => {
6567
+ const flush = () => {
6568
+ cache.clear();
6569
+ const adapters = depLatest(ctx, 2);
6570
+ adapters.text?.clearCache?.();
6571
+ };
6572
+ ctx.onDeactivation(flush);
6573
+ ctx.onInvalidate(flush);
6574
+ try {
6575
+ const blocks = depLatest(ctx, 0);
6576
+ const maxWidth = nonNegativeFinite(depLatest(ctx, 1), 0);
6577
+ const adapters = depLatest(ctx, 2);
6578
+ if (adapters.text !== activeTextAdapter) {
6579
+ cache.clear();
6580
+ activeTextAdapter = adapters.text;
6581
+ }
6582
+ let depIndex = 3;
6583
+ const font = opts.font ? depLatest(ctx, depIndex++) : void 0;
6584
+ const lineHeight = opts.lineHeight ? nonNegativeFinite(depLatest(ctx, depIndex++), 0) : void 0;
6585
+ const segmentAdapter = opts.segmentAdapter ? depLatest(ctx, depIndex) : getDefaultSegmentAdapter();
6586
+ const measured = [];
6587
+ const issues = [];
6588
+ for (let i = 0; i < blocks.length; i += 1) {
6589
+ const block = blocks[i];
6590
+ const subjectId = block?.id ?? `${targetId}:${i}`;
6591
+ try {
6592
+ if (block === void 0) {
6593
+ throw new Error(`Missing content block at index ${i}`);
6594
+ }
6595
+ const blockFont = block.kind === "text" ? block.font ?? font ?? "16px sans-serif" : void 0;
6596
+ const hyphenWidth = block.kind === "text" && adapters.text ? tryMeasureHyphenWidth(adapters.text, blockFont ?? "16px sans-serif") : void 0;
6597
+ measured.push(
6598
+ measureBlock(block, {
6599
+ adapters,
6600
+ font,
6601
+ hyphenWidth,
6602
+ lineHeight,
6603
+ maxWidth,
6604
+ cache,
6605
+ segmentAdapter
6606
+ })
6607
+ );
6608
+ if (block.kind === "text" && adapters.text) {
6609
+ if (hyphenWidth === void 0) {
6610
+ issues.push(
6611
+ measurementIssue(
6612
+ "measurement.hyphen.failed",
6613
+ `Hyphen measurement failed for '${subjectId}'`,
6614
+ subjectId,
6615
+ BLOCKS_MEASUREMENT_KIND,
6616
+ {
6617
+ source,
6618
+ details: { targetId, index: i },
6619
+ severity: "warning"
6620
+ }
6621
+ )
6622
+ );
6623
+ }
6624
+ }
6625
+ } catch (error) {
6626
+ issues.push(
6627
+ measurementIssue(
6628
+ "measurement.block.failed",
6629
+ `Block measurement failed for '${subjectId}'`,
6630
+ subjectId,
6631
+ BLOCKS_MEASUREMENT_KIND,
6632
+ {
6633
+ source,
6634
+ details: { error, targetId, index: i, blockKind: block?.kind },
6635
+ severity: "error"
6636
+ }
6637
+ )
6638
+ );
6639
+ }
6640
+ }
6641
+ ctx.down([
6642
+ [
6643
+ "DATA",
6644
+ [
6645
+ measurementOk(
6646
+ targetId,
6647
+ BLOCKS_MEASUREMENT_KIND,
6648
+ { blocks: measured },
6649
+ { source }
6650
+ ),
6651
+ ...issues
6652
+ ]
6653
+ ]
6654
+ ]);
6655
+ } catch (error) {
6656
+ ctx.down([
6657
+ [
6658
+ "DATA",
6659
+ [
6660
+ measurementIssue(
6661
+ "measurement.failed",
6662
+ `Block measurement failed for '${targetId}'`,
6663
+ targetId,
6664
+ BLOCKS_MEASUREMENT_KIND,
6665
+ { source, details: error, severity: "error" }
6666
+ )
6667
+ ]
6668
+ ]
6669
+ ]);
6670
+ }
6671
+ },
6672
+ { name }
6673
+ );
6674
+ }
6675
+ function analyzeAndMeasure(text, font, adapter, cache, stats, segmentAdapter) {
6676
+ const normalized = normalizeWhitespace(text);
6677
+ if (normalized.length === 0) {
6678
+ return [];
6679
+ }
6680
+ const segAdapter = segmentAdapter ?? getDefaultSegmentAdapter();
6681
+ const pieces = segmentText(normalized, segAdapter);
6682
+ const rawTexts = [];
6683
+ const rawKinds = [];
6684
+ const rawWordLike = [];
6685
+ for (const piece of pieces) {
6686
+ for (let i = 0; i < piece.texts.length; i += 1) {
6687
+ rawTexts.push(piece.texts[i]);
6688
+ rawKinds.push(piece.kinds[i]);
6689
+ rawWordLike.push(piece.isWordLike[i]);
6690
+ }
6691
+ }
6692
+ const mergedTexts = [];
6693
+ const mergedKinds = [];
6694
+ const mergedWordLike = [];
6695
+ for (let i = 0; i < rawTexts.length; i += 1) {
6696
+ const t = rawTexts[i];
6697
+ const k = rawKinds[i];
6698
+ const wl = rawWordLike[i];
6699
+ if (k === "text" && !wl && mergedTexts.length > 0 && mergedKinds[mergedTexts.length - 1] === "text") {
6700
+ const isSticky = t.length === 1 && (leftStickyPunctuation.has(t) || kinsokuStart.has(t));
6701
+ if (isSticky) {
6702
+ mergedTexts[mergedTexts.length - 1] += t;
6703
+ continue;
6704
+ }
6705
+ }
6706
+ if (t === "-" && mergedTexts.length > 0 && mergedKinds[mergedKinds.length - 1] === "text" && mergedWordLike[mergedWordLike.length - 1]) {
6707
+ mergedTexts[mergedTexts.length - 1] += t;
6708
+ continue;
6709
+ }
6710
+ mergedTexts.push(t);
6711
+ mergedKinds.push(k);
6712
+ mergedWordLike.push(wl);
6713
+ }
6714
+ const fontCache = cache.get(font) ?? /* @__PURE__ */ new Map();
6715
+ if (!cache.has(font)) cache.set(font, fontCache);
6716
+ function measureCached(segment) {
6717
+ const cached = fontCache.get(segment);
6718
+ if (cached === void 0) {
6719
+ if (stats) stats.misses += 1;
6720
+ const measured = adapter.measureSegment(segment, font).width;
6721
+ const width = Number.isFinite(measured) && measured >= 0 ? measured : 0;
6722
+ fontCache.set(segment, width);
6723
+ return width;
6724
+ }
6725
+ if (stats) stats.hits += 1;
6726
+ return cached;
6727
+ }
6728
+ const out = [];
6729
+ for (let i = 0; i < mergedTexts.length; i += 1) {
6730
+ const t = mergedTexts[i];
6731
+ const kind = mergedKinds[i];
6732
+ if (kind !== "text") {
6733
+ out.push({
6734
+ text: t,
6735
+ width: kind === "space" ? measureCached(" ") * t.length : 0,
6736
+ kind,
6737
+ graphemeWidths: null
6738
+ });
6739
+ continue;
6740
+ }
6741
+ if (isCJK(t)) {
6742
+ let unitText = "";
6743
+ for (const graphemeSegment of segAdapter.segmentGraphemes(t)) {
6744
+ const grapheme = graphemeSegment.segment;
6745
+ if (unitText.length > 0 && kinsokuStart.has(grapheme)) {
6746
+ unitText += grapheme;
6747
+ continue;
6748
+ }
6749
+ if (unitText.length > 0) {
6750
+ out.push({
6751
+ text: unitText,
6752
+ width: measureCached(unitText),
6753
+ kind: "text",
6754
+ graphemeWidths: null
6755
+ });
6756
+ }
6757
+ unitText = grapheme;
6758
+ }
6759
+ if (unitText.length > 0) {
6760
+ out.push({
6761
+ text: unitText,
6762
+ width: measureCached(unitText),
6763
+ kind: "text",
6764
+ graphemeWidths: null
6765
+ });
6766
+ }
6767
+ continue;
6768
+ }
6769
+ const textWidth = measureCached(t);
6770
+ let graphemeWidths = null;
6771
+ if (mergedWordLike[i] && t.length > 1) {
6772
+ const widths = [];
6773
+ for (const grapheme of segAdapter.segmentGraphemes(t)) {
6774
+ widths.push(measureCached(grapheme.segment));
6775
+ }
6776
+ if (widths.length > 1) graphemeWidths = widths;
6777
+ }
6778
+ out.push({
6779
+ text: t,
6780
+ width: textWidth,
6781
+ kind: "text",
6782
+ graphemeWidths
6783
+ });
6784
+ }
6785
+ return out;
6786
+ }
6787
+ function computeLineBreaks(segments, maxWidth, opts = {}) {
6788
+ if (segments.length === 0) return { lines: [], lineCount: 0 };
6789
+ const segAdapter = opts.segmentAdapter ?? getDefaultSegmentAdapter();
6790
+ const lines = [];
6791
+ let cursor = { segmentIndex: 0, graphemeIndex: 0 };
6792
+ while (cursor.segmentIndex < segments.length) {
6793
+ const next = layoutNextLine(segments, cursor, maxWidth, {
6794
+ hyphenWidth: opts.hyphenWidth,
6795
+ segmentAdapter: segAdapter
6796
+ });
6797
+ if (next === null) break;
6798
+ lines.push({
6799
+ text: next.text,
6800
+ width: next.width,
6801
+ startSegment: next.start.segmentIndex,
6802
+ startGrapheme: next.start.graphemeIndex,
6803
+ endSegment: next.end.segmentIndex,
6804
+ endGrapheme: next.end.graphemeIndex
6805
+ });
6806
+ if (next.end.segmentIndex === cursor.segmentIndex && next.end.graphemeIndex === cursor.graphemeIndex) {
6807
+ break;
6808
+ }
6809
+ cursor = next.end;
6810
+ }
6811
+ return { lines, lineCount: lines.length };
6812
+ }
6813
+ function canBreakAfter(kind) {
6814
+ return kind === "space" || kind === "zero-width-break" || kind === "soft-hyphen";
6815
+ }
6816
+ function sliceSegmentText(seg, startGrapheme, endGrapheme, segmentAdapter) {
6817
+ if (startGrapheme === 0 && endGrapheme < 0) return seg.text;
6818
+ const graphemes = [...segmentAdapter.segmentGraphemes(seg.text)].map((g) => g.segment);
6819
+ const stop = endGrapheme < 0 ? graphemes.length : endGrapheme;
6820
+ return graphemes.slice(startGrapheme, stop).join("");
6821
+ }
6822
+ function buildLineText(segments, startSegment, startGrapheme, endSegment, endGrapheme, appendHyphen, segmentAdapter) {
6823
+ let text = "";
6824
+ for (let i = startSegment; i < endSegment; i += 1) {
6825
+ const seg = segments[i];
6826
+ if (seg.kind === "soft-hyphen" || seg.kind === "hard-break") continue;
6827
+ if (i === startSegment && startGrapheme > 0) {
6828
+ text += sliceSegmentText(seg, startGrapheme, -1, segmentAdapter);
6829
+ } else {
6830
+ text += seg.text;
6831
+ }
6832
+ }
6833
+ if (endGrapheme > 0 && endSegment < segments.length) {
6834
+ const seg = segments[endSegment];
6835
+ const from = startSegment === endSegment ? startGrapheme : 0;
6836
+ text += sliceSegmentText(seg, from, endGrapheme, segmentAdapter);
6837
+ }
6838
+ if (appendHyphen) text += "-";
6839
+ return text;
6840
+ }
6841
+ function resolveHyphenWidth(ctx) {
6842
+ if (ctx?.hyphenWidth !== void 0) return nonNegativeFinite(ctx.hyphenWidth, 0);
6843
+ if (ctx?.adapter === void 0 || ctx.font === void 0) return 0;
6844
+ const cache = ctx.cache;
6845
+ if (cache) {
6846
+ let fontCache = cache.get(ctx.font);
6847
+ if (fontCache === void 0) {
6848
+ fontCache = /* @__PURE__ */ new Map();
6849
+ cache.set(ctx.font, fontCache);
6850
+ }
6851
+ let width = fontCache.get("-");
6852
+ if (width === void 0) {
6853
+ width = ctx.adapter.measureSegment("-", ctx.font).width;
6854
+ fontCache.set("-", width);
6855
+ }
6856
+ return width;
6857
+ }
6858
+ return ctx.adapter.measureSegment("-", ctx.font).width;
6859
+ }
6860
+ function layoutNextLine(segments, cursor, slotWidth, ctx) {
6861
+ let i = cursor.segmentIndex;
6862
+ const initialGrapheme = cursor.graphemeIndex;
6863
+ const segmentAdapter = ctx?.segmentAdapter ?? getDefaultSegmentAdapter();
6864
+ if (i >= segments.length) return null;
6865
+ if (initialGrapheme === 0) {
6866
+ while (i < segments.length) {
6867
+ const seg = segments[i];
6868
+ if (seg.kind === "hard-break") {
6869
+ return {
6870
+ text: "",
6871
+ width: 0,
6872
+ start: { segmentIndex: cursor.segmentIndex, graphemeIndex: 0 },
6873
+ end: { segmentIndex: i + 1, graphemeIndex: 0 }
6874
+ };
6875
+ }
6876
+ if (seg.kind === "space" || seg.kind === "zero-width-break" || seg.kind === "soft-hyphen") {
6877
+ i += 1;
6878
+ continue;
6879
+ }
6880
+ break;
6881
+ }
6882
+ if (i >= segments.length) return null;
6883
+ }
6884
+ const hyphenWidth = resolveHyphenWidth(ctx);
6885
+ const startSegment = i;
6886
+ const startGrapheme = i === cursor.segmentIndex ? initialGrapheme : 0;
6887
+ let lineW = 0;
6888
+ let lineEndSegment = startSegment;
6889
+ let lineEndGrapheme = 0;
6890
+ let hasContent = false;
6891
+ let pendingBreakSegment = -1;
6892
+ let pendingBreakGrapheme = 0;
6893
+ let pendingBreakWidth = 0;
6894
+ let pendingBreakSoftHyphen = false;
6895
+ const recordPending = (sIdx, gIdx, width, kind) => {
6896
+ pendingBreakSegment = sIdx;
6897
+ pendingBreakGrapheme = gIdx;
6898
+ pendingBreakWidth = width;
6899
+ pendingBreakSoftHyphen = kind === "soft-hyphen";
6900
+ };
6901
+ const consumeBreakable = (segIdx, startG, widths) => {
6902
+ for (let g = startG; g < widths.length; g += 1) {
6903
+ const gw = widths[g];
6904
+ if (!hasContent) {
6905
+ lineW = gw;
6906
+ lineEndSegment = segIdx;
6907
+ lineEndGrapheme = g + 1;
6908
+ hasContent = true;
6909
+ continue;
6910
+ }
6911
+ if (lineW + gw > slotWidth + 1e-3) {
6912
+ return true;
6913
+ }
6914
+ lineW += gw;
6915
+ lineEndSegment = segIdx;
6916
+ lineEndGrapheme = g + 1;
6917
+ }
6918
+ if (lineEndSegment === segIdx && lineEndGrapheme === widths.length) {
6919
+ lineEndSegment = segIdx + 1;
6920
+ lineEndGrapheme = 0;
6921
+ }
6922
+ return false;
6923
+ };
6924
+ if (startGrapheme > 0 && startSegment < segments.length) {
6925
+ const seg = segments[startSegment];
6926
+ if (seg.graphemeWidths) {
6927
+ const overflow = consumeBreakable(startSegment, startGrapheme, seg.graphemeWidths);
6928
+ if (overflow) {
6929
+ const text2 = buildLineText(
6930
+ segments,
6931
+ startSegment,
6932
+ startGrapheme,
6933
+ lineEndSegment,
6934
+ lineEndGrapheme,
6935
+ false,
6936
+ segmentAdapter
6937
+ );
6938
+ return {
6939
+ text: text2,
6940
+ width: lineW,
6941
+ start: { segmentIndex: startSegment, graphemeIndex: startGrapheme },
6942
+ end: { segmentIndex: lineEndSegment, graphemeIndex: lineEndGrapheme }
6943
+ };
6944
+ }
6945
+ i = lineEndSegment;
6946
+ }
6947
+ }
6948
+ for (; i < segments.length; i += 1) {
6949
+ const seg = segments[i];
6950
+ if (seg.kind === "hard-break") {
6951
+ if (hasContent) {
6952
+ const endsAtSoftHyphen2 = lineEndSegment > 0 && segments[lineEndSegment - 1]?.kind === "soft-hyphen";
6953
+ const text2 = buildLineText(
6954
+ segments,
6955
+ startSegment,
6956
+ startGrapheme,
6957
+ lineEndSegment,
6958
+ lineEndGrapheme,
6959
+ endsAtSoftHyphen2,
6960
+ segmentAdapter
6961
+ );
6962
+ return {
6963
+ text: text2,
6964
+ width: lineW + (endsAtSoftHyphen2 ? hyphenWidth : 0),
6965
+ start: { segmentIndex: startSegment, graphemeIndex: startGrapheme },
6966
+ end: { segmentIndex: i + 1, graphemeIndex: 0 }
6967
+ };
6968
+ }
6969
+ return {
6970
+ text: "",
6971
+ width: 0,
6972
+ start: { segmentIndex: startSegment, graphemeIndex: startGrapheme },
6973
+ end: { segmentIndex: i + 1, graphemeIndex: 0 }
6974
+ };
6975
+ }
6976
+ const w = seg.width;
6977
+ if (!hasContent) {
6978
+ if (w > slotWidth && seg.graphemeWidths) {
6979
+ const overflow = consumeBreakable(i, 0, seg.graphemeWidths);
6980
+ if (overflow) {
6981
+ const text2 = buildLineText(
6982
+ segments,
6983
+ startSegment,
6984
+ startGrapheme,
6985
+ lineEndSegment,
6986
+ lineEndGrapheme,
6987
+ false,
6988
+ segmentAdapter
6989
+ );
6990
+ return {
6991
+ text: text2,
6992
+ width: lineW,
6993
+ start: { segmentIndex: startSegment, graphemeIndex: startGrapheme },
6994
+ end: { segmentIndex: lineEndSegment, graphemeIndex: lineEndGrapheme }
6995
+ };
6996
+ }
6997
+ i = lineEndSegment - 1;
6998
+ continue;
6999
+ }
7000
+ lineW = w;
7001
+ lineEndSegment = i + 1;
7002
+ lineEndGrapheme = 0;
7003
+ hasContent = true;
7004
+ if (canBreakAfter(seg.kind)) {
7005
+ recordPending(i + 1, 0, seg.kind === "space" ? lineW - w : lineW, seg.kind);
7006
+ }
7007
+ continue;
7008
+ }
7009
+ const projectedWidth = lineW + w;
7010
+ if (projectedWidth > slotWidth + 1e-3) {
7011
+ if (canBreakAfter(seg.kind)) {
7012
+ lineEndSegment = i + 1;
7013
+ lineEndGrapheme = 0;
7014
+ const endsAtSoftHyphen2 = seg.kind === "soft-hyphen";
7015
+ const text3 = buildLineText(
7016
+ segments,
7017
+ startSegment,
7018
+ startGrapheme,
7019
+ lineEndSegment,
7020
+ lineEndGrapheme,
7021
+ endsAtSoftHyphen2,
7022
+ segmentAdapter
7023
+ );
7024
+ return {
7025
+ text: text3,
7026
+ width: seg.kind === "space" ? lineW : lineW + (endsAtSoftHyphen2 ? hyphenWidth : 0),
7027
+ start: { segmentIndex: startSegment, graphemeIndex: startGrapheme },
7028
+ end: { segmentIndex: lineEndSegment, graphemeIndex: lineEndGrapheme }
7029
+ };
7030
+ }
7031
+ if (pendingBreakSegment >= 0) {
7032
+ const text3 = buildLineText(
7033
+ segments,
7034
+ startSegment,
7035
+ startGrapheme,
7036
+ pendingBreakSegment,
7037
+ pendingBreakGrapheme,
7038
+ pendingBreakSoftHyphen,
7039
+ segmentAdapter
7040
+ );
7041
+ return {
7042
+ text: text3,
7043
+ width: pendingBreakWidth + (pendingBreakSoftHyphen ? hyphenWidth : 0),
7044
+ start: { segmentIndex: startSegment, graphemeIndex: startGrapheme },
7045
+ end: { segmentIndex: pendingBreakSegment, graphemeIndex: pendingBreakGrapheme }
7046
+ };
7047
+ }
7048
+ if (w > slotWidth && seg.graphemeWidths) {
7049
+ const text3 = buildLineText(
7050
+ segments,
7051
+ startSegment,
7052
+ startGrapheme,
7053
+ lineEndSegment,
7054
+ lineEndGrapheme,
7055
+ false,
7056
+ segmentAdapter
7057
+ );
7058
+ return {
7059
+ text: text3,
7060
+ width: lineW,
7061
+ start: { segmentIndex: startSegment, graphemeIndex: startGrapheme },
7062
+ end: { segmentIndex: lineEndSegment, graphemeIndex: lineEndGrapheme }
7063
+ };
7064
+ }
7065
+ const text2 = buildLineText(
7066
+ segments,
7067
+ startSegment,
7068
+ startGrapheme,
7069
+ lineEndSegment,
7070
+ lineEndGrapheme,
7071
+ false,
7072
+ segmentAdapter
7073
+ );
7074
+ return {
7075
+ text: text2,
7076
+ width: lineW,
7077
+ start: { segmentIndex: startSegment, graphemeIndex: startGrapheme },
7078
+ end: { segmentIndex: lineEndSegment, graphemeIndex: lineEndGrapheme }
7079
+ };
7080
+ }
7081
+ lineW = projectedWidth;
7082
+ lineEndSegment = i + 1;
7083
+ lineEndGrapheme = 0;
7084
+ if (canBreakAfter(seg.kind)) {
7085
+ recordPending(i + 1, 0, seg.kind === "space" ? lineW - w : lineW, seg.kind);
7086
+ }
7087
+ }
7088
+ if (!hasContent) return null;
7089
+ const endsAtSoftHyphen = lineEndSegment > 0 && segments[lineEndSegment - 1]?.kind === "soft-hyphen";
7090
+ const text = buildLineText(
7091
+ segments,
7092
+ startSegment,
7093
+ startGrapheme,
7094
+ lineEndSegment,
7095
+ lineEndGrapheme,
7096
+ endsAtSoftHyphen,
7097
+ segmentAdapter
7098
+ );
7099
+ return {
7100
+ text,
7101
+ width: lineW + (endsAtSoftHyphen ? hyphenWidth : 0),
7102
+ start: { segmentIndex: startSegment, graphemeIndex: startGrapheme },
7103
+ end: { segmentIndex: lineEndSegment, graphemeIndex: lineEndGrapheme }
7104
+ };
7105
+ }
7106
+ function carveTextLineSlots(base, blocked, minSlotWidth = 0) {
7107
+ let slots = [base];
7108
+ for (const block of blocked) {
7109
+ const next = [];
7110
+ for (const slot of slots) {
7111
+ if (block.right <= slot.left || block.left >= slot.right) {
7112
+ next.push(slot);
7113
+ continue;
7114
+ }
7115
+ if (block.left > slot.left) next.push({ left: slot.left, right: block.left });
7116
+ if (block.right < slot.right) next.push({ left: block.right, right: slot.right });
7117
+ }
7118
+ slots = next;
7119
+ }
7120
+ if (minSlotWidth > 0) return slots.filter((slot) => slot.right - slot.left >= minSlotWidth);
7121
+ return slots;
7122
+ }
7123
+ function computeCharPositions(lineBreaks, segments, lineHeight, segmentAdapter) {
7124
+ const positions = [];
7125
+ const segAdapter = segmentAdapter ?? getDefaultSegmentAdapter();
7126
+ for (let lineIdx = 0; lineIdx < lineBreaks.lines.length; lineIdx += 1) {
7127
+ const line = lineBreaks.lines[lineIdx];
7128
+ const y = lineIdx * lineHeight;
7129
+ let x = 0;
7130
+ for (let si = line.startSegment; si < segments.length; si += 1) {
7131
+ const seg = segments[si];
7132
+ if (seg.kind === "soft-hyphen" || seg.kind === "hard-break") {
7133
+ if (si >= line.endSegment && line.endGrapheme === 0) break;
7134
+ continue;
7135
+ }
7136
+ const graphemes = [...segAdapter.segmentGraphemes(seg.text)].map((g) => g.segment);
7137
+ if (graphemes.length === 0) continue;
7138
+ const startGrapheme = si === line.startSegment ? line.startGrapheme : 0;
7139
+ let endGrapheme;
7140
+ if (si < line.endSegment) {
7141
+ endGrapheme = graphemes.length;
7142
+ } else if (si === line.endSegment && line.endGrapheme > 0) {
7143
+ endGrapheme = line.endGrapheme;
7144
+ } else {
7145
+ break;
7146
+ }
7147
+ for (let g = startGrapheme; g < endGrapheme; g += 1) {
7148
+ const width = seg.graphemeWidths ? seg.graphemeWidths[g] : seg.width / graphemes.length;
7149
+ positions.push({ x, y, width, height: lineHeight, line: lineIdx });
7150
+ x += width;
7151
+ }
7152
+ }
7153
+ }
7154
+ return positions;
7155
+ }
7156
+ function nonNegativeFinite(value, fallback) {
7157
+ return Number.isFinite(value) ? Math.max(0, value) : fallback;
7158
+ }
7159
+ function finiteNumber(value, fallback) {
7160
+ return Number.isFinite(value) ? value : fallback;
7161
+ }
7162
+ function emitLayoutError(ctx, error, fallback) {
7163
+ ctx.down([["ERROR", errorPayload(error, fallback)]]);
7164
+ }
7165
+ function positiveFinite(value, fallback) {
7166
+ return Number.isFinite(value) && value > 0 ? value : fallback;
7167
+ }
7168
+ function clampDimension(value) {
7169
+ return nonNegativeFinite(value ?? 0, 0);
7170
+ }
7171
+ function fitSize(size, maxWidth) {
7172
+ const width = nonNegativeFinite(size.width, 0);
7173
+ const height = nonNegativeFinite(size.height, 0);
7174
+ if (width === 0 || maxWidth <= 0 || width <= maxWidth) return { width, height };
7175
+ const scale = maxWidth / width;
7176
+ return { width: maxWidth, height: height * scale };
7177
+ }
7178
+ function blockMaxWidth(blockMaxWidth2, maxWidth) {
7179
+ const own = blockMaxWidth2 === void 0 ? maxWidth : nonNegativeFinite(blockMaxWidth2, maxWidth);
7180
+ if (maxWidth <= 0) return own;
7181
+ if (own <= 0) return maxWidth;
7182
+ return Math.min(own, maxWidth);
7183
+ }
7184
+ function resolveTextAdapter(opts) {
7185
+ const adapter = opts.adapters?.text ?? opts.adapter;
7186
+ if (!adapter) throw new Error("Text blocks require a MeasurementAdapter");
7187
+ return adapter;
7188
+ }
7189
+ function measureBlock(block, opts) {
7190
+ const maxWidth = nonNegativeFinite(opts.maxWidth ?? 800, 800);
7191
+ const marginTop = nonNegativeFinite(block.marginTop ?? 0, 0);
7192
+ const marginBottom = nonNegativeFinite(block.marginBottom ?? 0, 0);
7193
+ if (block.kind === "text") {
7194
+ const adapter = resolveTextAdapter(opts);
7195
+ const font = block.font ?? opts.font ?? "16px sans-serif";
7196
+ const lineHeight = positiveFinite(block.lineHeight ?? opts.lineHeight ?? 20, 20);
7197
+ const cache = opts.cache ?? /* @__PURE__ */ new Map();
7198
+ const width = blockMaxWidth(block.maxWidth, maxWidth);
7199
+ const segments = analyzeAndMeasure(
7200
+ block.text,
7201
+ font,
7202
+ adapter,
7203
+ cache,
7204
+ void 0,
7205
+ opts.segmentAdapter
7206
+ );
7207
+ const hyphenWidth = Object.hasOwn(opts, "hyphenWidth") ? opts.hyphenWidth : tryMeasureHyphenWidth(adapter, font);
7208
+ const lineBreaks = computeLineBreaks(segments, width, {
7209
+ hyphenWidth,
7210
+ segmentAdapter: opts.segmentAdapter
7211
+ });
7212
+ const charPositions = computeCharPositions(
7213
+ lineBreaks,
7214
+ segments,
7215
+ lineHeight,
7216
+ opts.segmentAdapter
7217
+ );
7218
+ return {
7219
+ block,
7220
+ kind: block.kind,
7221
+ id: block.id,
7222
+ width: lineBreaks.lines.reduce((acc, line) => Math.max(acc, line.width), 0),
7223
+ height: lineBreaks.lineCount * lineHeight,
7224
+ marginTop,
7225
+ marginBottom,
7226
+ segments,
7227
+ lineBreaks,
7228
+ charPositions
7229
+ };
7230
+ }
7231
+ if (block.kind === "image") {
7232
+ const explicit2 = block.width !== void 0 && block.height !== void 0 ? { width: clampDimension(block.width), height: clampDimension(block.height) } : opts.adapters?.image?.measureImage(block.src);
7233
+ if (!explicit2) throw new Error("Image blocks require width/height or an ImageMeasurer");
7234
+ const size2 = fitSize(explicit2, blockMaxWidth(block.maxWidth, maxWidth));
7235
+ return {
7236
+ block,
7237
+ kind: block.kind,
7238
+ id: block.id,
7239
+ width: size2.width,
7240
+ height: size2.height,
7241
+ marginTop,
7242
+ marginBottom
7243
+ };
7244
+ }
7245
+ const explicit = block.width !== void 0 && block.height !== void 0 ? { width: clampDimension(block.width), height: clampDimension(block.height) } : opts.adapters?.svg?.measureSvg(block.svg);
7246
+ if (!explicit) throw new Error("SVG blocks require width/height or a SvgMeasurer");
7247
+ const size = fitSize(explicit, blockMaxWidth(block.maxWidth, maxWidth));
7248
+ return {
7249
+ block,
7250
+ kind: block.kind,
7251
+ id: block.id,
7252
+ width: size.width,
7253
+ height: size.height,
7254
+ marginTop,
7255
+ marginBottom
7256
+ };
7257
+ }
7258
+ function measureBlocks(blocks, opts) {
7259
+ const cache = opts.cache ?? /* @__PURE__ */ new Map();
7260
+ return blocks.map((block) => measureBlock(block, { ...opts, cache }));
7261
+ }
7262
+ function computeBlockFlow(blocks, gap = 0) {
7263
+ const spacing = nonNegativeFinite(gap, 0);
7264
+ let y = 0;
7265
+ return blocks.map((block) => {
7266
+ y += block.marginTop;
7267
+ const positioned = { ...block, x: 0, y };
7268
+ y += block.height + block.marginBottom + spacing;
7269
+ return positioned;
7270
+ });
7271
+ }
7272
+ function computeTotalHeight(blocks) {
7273
+ if (blocks.length === 0) return 0;
7274
+ let bottom = 0;
7275
+ for (const block of blocks) {
7276
+ bottom = Math.max(bottom, block.y + block.height + block.marginBottom);
7277
+ }
7278
+ return bottom;
7279
+ }
7280
+ function columnGeometry(container, columns) {
7281
+ const paddingX = nonNegativeFinite(container.paddingX ?? 0, 0);
7282
+ const paddingY = nonNegativeFinite(container.paddingY ?? 0, 0);
7283
+ const count = Math.max(1, Math.floor(nonNegativeFinite(columns?.count ?? 1, 1)));
7284
+ const gap = nonNegativeFinite(columns?.gap ?? 0, 0);
7285
+ const width = nonNegativeFinite(container.width, 0);
7286
+ const height = nonNegativeFinite(container.height, 0);
7287
+ const innerWidth = Math.max(0, width - paddingX * 2 - gap * (count - 1));
7288
+ const columnWidth = count > 0 ? innerWidth / count : 0;
7289
+ return { paddingX, paddingY, count, gap, width, height, columnWidth };
7290
+ }
7291
+ function sanitizeFlowContainer(container) {
7292
+ return {
7293
+ width: nonNegativeFinite(container.width, 0),
7294
+ height: nonNegativeFinite(container.height, 0),
7295
+ paddingX: container.paddingX === void 0 ? void 0 : nonNegativeFinite(container.paddingX, 0),
7296
+ paddingY: container.paddingY === void 0 ? void 0 : nonNegativeFinite(container.paddingY, 0)
7297
+ };
7298
+ }
7299
+ function sanitizeFlowColumns(columns) {
7300
+ return {
7301
+ count: columns.count === void 0 ? void 0 : Math.max(1, Math.floor(nonNegativeFinite(columns.count, 1))),
7302
+ gap: columns.gap === void 0 ? void 0 : nonNegativeFinite(columns.gap, 0)
7303
+ };
7304
+ }
7305
+ function sanitizeObstacles(obstacles) {
7306
+ return obstacles.map((obstacle) => {
7307
+ if (obstacle.kind === "circle") {
7308
+ return {
7309
+ kind: obstacle.kind,
7310
+ cx: finiteNumber(obstacle.cx, 0),
7311
+ cy: finiteNumber(obstacle.cy, 0),
7312
+ r: nonNegativeFinite(obstacle.r, 0)
7313
+ };
7314
+ }
7315
+ return {
7316
+ kind: obstacle.kind,
7317
+ x: finiteNumber(obstacle.x, 0),
7318
+ y: finiteNumber(obstacle.y, 0),
7319
+ width: nonNegativeFinite(obstacle.width, 0),
7320
+ height: nonNegativeFinite(obstacle.height, 0)
7321
+ };
7322
+ });
7323
+ }
7324
+ function circleIntervalForBand(obstacle, bandTop, bandBottom) {
7325
+ const radius = nonNegativeFinite(obstacle.r, 0);
7326
+ const centerY = obstacle.cy;
7327
+ const nearestY = Math.max(bandTop, Math.min(centerY, bandBottom));
7328
+ const dy = nearestY - centerY;
7329
+ if (Math.abs(dy) > radius) return null;
7330
+ const dx = Math.sqrt(Math.max(0, radius * radius - dy * dy));
7331
+ return { left: obstacle.cx - dx, right: obstacle.cx + dx };
7332
+ }
7333
+ function rectIntervalForBand(obstacle, bandTop, bandBottom) {
7334
+ const left = obstacle.x;
7335
+ const right = obstacle.x + nonNegativeFinite(obstacle.width, 0);
7336
+ const top = obstacle.y;
7337
+ const bottom = obstacle.y + nonNegativeFinite(obstacle.height, 0);
7338
+ if (bottom <= bandTop || top >= bandBottom || right <= left) return null;
7339
+ return { left, right };
7340
+ }
7341
+ function blockedIntervalsForBand(obstacles, bandTop, bandBottom) {
7342
+ const intervals = [];
7343
+ for (const obstacle of obstacles) {
7344
+ const interval = obstacle.kind === "circle" ? circleIntervalForBand(obstacle, bandTop, bandBottom) : rectIntervalForBand(obstacle, bandTop, bandBottom);
7345
+ if (interval) intervals.push(interval);
7346
+ }
7347
+ return intervals.sort((a, b) => a.left - b.left);
7348
+ }
7349
+ function computeFlowLines(segments, opts) {
7350
+ const geometry = columnGeometry(opts.container, opts.columns);
7351
+ const lineHeight = positiveFinite(opts.lineHeight, 20);
7352
+ const minSlotWidth = nonNegativeFinite(opts.minSlotWidth ?? 0, 0);
7353
+ const obstacles = opts.obstacles ?? [];
7354
+ const segmentAdapter = opts.segmentAdapter ?? getDefaultSegmentAdapter();
7355
+ const lines = [];
7356
+ let cursor = { segmentIndex: 0, graphemeIndex: 0 };
7357
+ const contentBottom = Math.max(geometry.paddingY, geometry.height - geometry.paddingY);
7358
+ for (let columnIndex = 0; columnIndex < geometry.count; columnIndex += 1) {
7359
+ const columnLeft = geometry.paddingX + columnIndex * (geometry.columnWidth + geometry.gap);
7360
+ const columnRight = columnLeft + geometry.columnWidth;
7361
+ for (let y = geometry.paddingY; y + lineHeight <= contentBottom + 1e-3 && cursor.segmentIndex < segments.length; y += lineHeight) {
7362
+ const blocked = blockedIntervalsForBand(obstacles, y, y + lineHeight);
7363
+ const slots = carveTextLineSlots(
7364
+ { left: columnLeft, right: columnRight },
7365
+ blocked,
7366
+ Math.max(minSlotWidth, 1e-6)
7367
+ );
7368
+ for (const slot of slots) {
7369
+ if (cursor.segmentIndex >= segments.length) break;
7370
+ const slotWidth = slot.right - slot.left;
7371
+ const next = layoutNextLine(segments, cursor, slotWidth, {
7372
+ hyphenWidth: opts.hyphenWidth,
7373
+ segmentAdapter
7374
+ });
7375
+ if (next === null) {
7376
+ cursor = { segmentIndex: segments.length, graphemeIndex: 0 };
7377
+ break;
7378
+ }
7379
+ if (next.end.segmentIndex === cursor.segmentIndex && next.end.graphemeIndex === cursor.graphemeIndex) {
7380
+ break;
7381
+ }
7382
+ cursor = next.end;
7383
+ if (next.text.length === 0 && next.width === 0) break;
7384
+ lines.push({
7385
+ text: next.text,
7386
+ width: next.width,
7387
+ startSegment: next.start.segmentIndex,
7388
+ startGrapheme: next.start.graphemeIndex,
7389
+ endSegment: next.end.segmentIndex,
7390
+ endGrapheme: next.end.graphemeIndex,
7391
+ x: slot.left,
7392
+ y,
7393
+ slotWidth,
7394
+ columnIndex
7395
+ });
7396
+ }
7397
+ }
7398
+ }
7399
+ return {
7400
+ lines,
7401
+ cursor,
7402
+ done: cursor.segmentIndex >= segments.length
7403
+ };
7404
+ }
7405
+ function reactiveLayout(opts) {
7406
+ const { measurements, segmentAdapter: segmentAdapterOpt, name = "reactive-layout" } = opts;
7407
+ const targetId = opts.targetId ?? "text";
7408
+ const segAdapter = segmentAdapterOpt ?? getDefaultSegmentAdapter();
7409
+ const g = opts.graph;
7410
+ const lineHeightInput = inputNode(
7411
+ g,
7412
+ opts.lineHeight instanceof Node ? opts.lineHeight : nonNegativeFinite(opts.lineHeight ?? 20, 20),
7413
+ nonNegativeFinite(20, 20),
7414
+ scopedName(name, "line-height")
7415
+ );
7416
+ const maxWidthInput = inputNode(
7417
+ g,
7418
+ opts.maxWidth instanceof Node ? opts.maxWidth : nonNegativeFinite(opts.maxWidth ?? 800, 800),
7419
+ nonNegativeFinite(800, 800),
7420
+ scopedName(name, "max-width")
7421
+ );
7422
+ const segments = g.node(
7423
+ [measurements],
7424
+ (ctx) => {
7425
+ try {
7426
+ const measured = latestMeasurementValue(
7427
+ depLatest(ctx, 0),
7428
+ targetId,
7429
+ TEXT_SEGMENTS_MEASUREMENT_KIND
7430
+ );
7431
+ ctx.down([["DATA", measured?.segments ?? []]]);
7432
+ } catch (error) {
7433
+ emitLayoutError(ctx, error, "reactiveLayout segments failed");
7434
+ }
7435
+ },
7436
+ { name: scopedName(name, "segments") }
7437
+ );
7438
+ const lineBreaks = g.node(
7439
+ [segments, maxWidthInput.node, measurements],
7440
+ (ctx) => {
7441
+ try {
7442
+ const measured = latestMeasurementValue(
7443
+ depLatest(ctx, 2),
7444
+ targetId,
7445
+ TEXT_SEGMENTS_MEASUREMENT_KIND
7446
+ );
7447
+ const computed = computeLineBreaks(
7448
+ depLatest(ctx, 0),
7449
+ nonNegativeFinite(depLatest(ctx, 1), 0),
7450
+ { hyphenWidth: measured?.hyphenWidth, segmentAdapter: segAdapter }
7451
+ );
7452
+ ctx.down([["DATA", computed]]);
7453
+ } catch (error) {
7454
+ emitLayoutError(ctx, error, "reactiveLayout line breaks failed");
7455
+ }
7456
+ },
7457
+ { name: scopedName(name, "line-breaks") }
7458
+ );
7459
+ const height = g.node(
7460
+ [lineBreaks, lineHeightInput.node],
7461
+ (ctx) => {
7462
+ try {
7463
+ const lines = depLatest(ctx, 0);
7464
+ const lineHeight = nonNegativeFinite(depLatest(ctx, 1), 0);
7465
+ ctx.down([["DATA", lines.lineCount * lineHeight]]);
7466
+ } catch (error) {
7467
+ emitLayoutError(ctx, error, "reactiveLayout height failed");
7468
+ }
7469
+ },
7470
+ { name: scopedName(name, "height") }
7471
+ );
7472
+ const charPositions = g.node(
7473
+ [lineBreaks, segments, lineHeightInput.node],
7474
+ (ctx) => {
7475
+ try {
7476
+ const positions = computeCharPositions(
7477
+ depLatest(ctx, 0),
7478
+ depLatest(ctx, 1),
7479
+ nonNegativeFinite(depLatest(ctx, 2), 0),
7480
+ segAdapter
7481
+ );
7482
+ ctx.down([["DATA", positions]]);
7483
+ } catch (error) {
7484
+ emitLayoutError(ctx, error, "reactiveLayout char positions failed");
7485
+ }
7486
+ },
7487
+ { name: scopedName(name, "char-positions") }
7488
+ );
7489
+ return {
7490
+ graph: g,
7491
+ input: {
7492
+ lineHeight: lineHeightInput.node,
7493
+ maxWidth: maxWidthInput.node,
7494
+ measurements
7495
+ },
7496
+ setLineHeight(lineHeight) {
7497
+ lineHeightInput.set(nonNegativeFinite(lineHeight, 0));
7498
+ },
7499
+ setMaxWidth(maxWidth) {
7500
+ maxWidthInput.set(nonNegativeFinite(maxWidth, 0));
7501
+ },
7502
+ segments,
7503
+ lineBreaks,
7504
+ height,
7505
+ charPositions
7506
+ };
7507
+ }
7508
+ function reactiveBlockLayout(opts) {
7509
+ const { measurements, name = "reactive-block-layout" } = opts;
7510
+ const targetId = opts.targetId ?? "blocks";
7511
+ const g = opts.graph;
7512
+ const gapInput = inputNode(
7513
+ g,
7514
+ opts.gap instanceof Node ? opts.gap : nonNegativeFinite(opts.gap ?? 0, 0),
7515
+ nonNegativeFinite(0, 0),
7516
+ scopedName(name, "gap")
7517
+ );
7518
+ const measuredBlocks = g.node(
7519
+ [measurements],
7520
+ (ctx) => {
7521
+ try {
7522
+ const measured = latestMeasurementValue(
7523
+ depLatest(ctx, 0),
7524
+ targetId,
7525
+ BLOCKS_MEASUREMENT_KIND
7526
+ );
7527
+ ctx.down([["DATA", measured?.blocks ?? []]]);
7528
+ } catch (error) {
7529
+ emitLayoutError(ctx, error, "reactiveBlockLayout measured-blocks failed");
7530
+ }
7531
+ },
7532
+ { name: scopedName(name, "measured-blocks") }
7533
+ );
7534
+ const blockFlow = g.node(
7535
+ [measuredBlocks, gapInput.node],
7536
+ (ctx) => {
7537
+ try {
7538
+ const positioned = computeBlockFlow(
7539
+ depLatest(ctx, 0),
7540
+ nonNegativeFinite(depLatest(ctx, 1), 0)
7541
+ );
7542
+ ctx.down([["DATA", positioned]]);
7543
+ } catch (error) {
7544
+ emitLayoutError(ctx, error, "reactiveBlockLayout flow failed");
7545
+ }
7546
+ },
7547
+ { name: scopedName(name, "block-flow") }
7548
+ );
7549
+ const totalHeight = g.node(
7550
+ [blockFlow],
7551
+ (ctx) => {
7552
+ try {
7553
+ ctx.down([["DATA", computeTotalHeight(depLatest(ctx, 0))]]);
7554
+ } catch (error) {
7555
+ emitLayoutError(ctx, error, "reactiveBlockLayout total height failed");
7556
+ }
7557
+ },
7558
+ { name: scopedName(name, "total-height") }
7559
+ );
7560
+ return {
7561
+ graph: g,
7562
+ input: {
7563
+ gap: gapInput.node,
7564
+ measurements
7565
+ },
7566
+ setGap(gap) {
7567
+ gapInput.set(nonNegativeFinite(gap, 0));
7568
+ },
7569
+ measuredBlocks,
7570
+ blockFlow,
7571
+ totalHeight
7572
+ };
7573
+ }
7574
+ function reactiveFlowLayout(opts) {
7575
+ const { measurements, segmentAdapter: segmentAdapterOpt, name = "reactive-flow-layout" } = opts;
7576
+ const targetId = opts.targetId ?? "text";
7577
+ const segAdapter = segmentAdapterOpt ?? getDefaultSegmentAdapter();
7578
+ const g = opts.graph;
7579
+ const lineHeightInput = inputNode(
7580
+ g,
7581
+ opts.lineHeight instanceof Node ? opts.lineHeight : nonNegativeFinite(opts.lineHeight ?? 20, 20),
7582
+ nonNegativeFinite(20, 20),
7583
+ scopedName(name, "line-height")
7584
+ );
7585
+ const containerInput = inputNode(
7586
+ g,
7587
+ opts.container,
7588
+ { width: 800, height: 600 },
7589
+ scopedName(name, "container")
7590
+ );
7591
+ const columnsInput = inputNode(
7592
+ g,
7593
+ opts.columns,
7594
+ { count: 1, gap: 0 },
7595
+ scopedName(name, "columns")
7596
+ );
7597
+ const obstaclesInput = inputNode(g, opts.obstacles, [], scopedName(name, "obstacles"));
7598
+ const segments = g.node(
7599
+ [measurements],
7600
+ (ctx) => {
7601
+ try {
7602
+ const measured = latestMeasurementValue(
7603
+ depLatest(ctx, 0),
7604
+ targetId,
7605
+ TEXT_SEGMENTS_MEASUREMENT_KIND
7606
+ );
7607
+ ctx.down([["DATA", measured?.segments ?? []]]);
7608
+ } catch (error) {
7609
+ emitLayoutError(ctx, error, "reactiveFlowLayout segments failed");
7610
+ }
7611
+ },
7612
+ { name: scopedName(name, "segments") }
7613
+ );
7614
+ const flowLines = g.node(
7615
+ [
7616
+ segments,
7617
+ lineHeightInput.node,
7618
+ containerInput.node,
7619
+ columnsInput.node,
7620
+ obstaclesInput.node,
7621
+ measurements
7622
+ ],
7623
+ (ctx) => {
7624
+ try {
7625
+ const measured = latestMeasurementValue(
7626
+ depLatest(ctx, 5),
7627
+ targetId,
7628
+ TEXT_SEGMENTS_MEASUREMENT_KIND
7629
+ );
7630
+ const computed = computeFlowLines(depLatest(ctx, 0), {
7631
+ lineHeight: nonNegativeFinite(depLatest(ctx, 1), 0),
7632
+ container: sanitizeFlowContainer(depLatest(ctx, 2)),
7633
+ columns: sanitizeFlowColumns(depLatest(ctx, 3)),
7634
+ obstacles: sanitizeObstacles(depLatest(ctx, 4)),
7635
+ minSlotWidth: nonNegativeFinite(opts.minSlotWidth ?? 0, 0),
7636
+ hyphenWidth: measured?.hyphenWidth,
7637
+ segmentAdapter: segAdapter
7638
+ });
7639
+ ctx.down([["DATA", computed]]);
7640
+ } catch (error) {
7641
+ emitLayoutError(ctx, error, "reactiveFlowLayout flow lines failed");
7642
+ }
7643
+ },
7644
+ { name: scopedName(name, "flow-lines") }
7645
+ );
7646
+ return {
7647
+ graph: g,
7648
+ input: {
7649
+ lineHeight: lineHeightInput.node,
7650
+ container: containerInput.node,
7651
+ columns: columnsInput.node,
7652
+ obstacles: obstaclesInput.node,
7653
+ measurements
7654
+ },
7655
+ setLineHeight(lineHeight) {
7656
+ lineHeightInput.set(nonNegativeFinite(lineHeight, 0));
7657
+ },
7658
+ setContainer(container) {
7659
+ containerInput.set(container);
7660
+ },
7661
+ setColumns(columns) {
7662
+ columnsInput.set(columns);
7663
+ },
7664
+ setObstacles(obstacles) {
7665
+ obstaclesInput.set(obstacles);
7666
+ },
7667
+ segments,
7668
+ flowLines
7669
+ };
7670
+ }
3235
7671
  // Annotate the CommonJS export names for ESM import in node:
3236
7672
  0 && (module.exports = {
3237
7673
  AGENTIC_MEMORY_RECORD_FRAME_FORMAT,
3238
7674
  AGENTIC_MEMORY_RECORD_FRAME_VERSION,
7675
+ BLOCKS_MEASUREMENT_KIND,
7676
+ CapabilityMeasureAdapter,
7677
+ CellMeasureAdapter,
7678
+ IMAGE_SIZE_MEASUREMENT_KIND,
7679
+ ImageSizeAdapter,
7680
+ InjectedMeasureAdapter,
7681
+ PrecomputedMeasureAdapter,
7682
+ READINESS_MEASUREMENT_KIND,
7683
+ SVG_BOUNDS_MEASUREMENT_KIND,
7684
+ SvgBoundsAdapter,
7685
+ TEXT_SEGMENTS_MEASUREMENT_KIND,
3239
7686
  agenticMemoryBundle,
3240
7687
  agenticMemoryConsolidationBundle,
3241
7688
  agenticMemoryContextPackingBundle,
@@ -3244,6 +7691,35 @@ function errorMessage2(error) {
3244
7691
  agenticMemoryRecordFrame,
3245
7692
  agenticMemoryRecordFrameCodec,
3246
7693
  agenticMemoryRetentionBundle,
3247
- assertAgenticMemoryRecordFrame
7694
+ analyzeAndMeasure,
7695
+ assertAgenticMemoryRecordFrame,
7696
+ blockAdaptersProvider,
7697
+ blockMeasurementProvider,
7698
+ capabilityAdmissionProjector,
7699
+ capabilityAdmissionProposal,
7700
+ capabilityTextMeasurements,
7701
+ carveTextLineSlots,
7702
+ cellTextMeasurements,
7703
+ circleIntervalForBand,
7704
+ computeBlockFlow,
7705
+ computeCharPositions,
7706
+ computeFlowLines,
7707
+ computeLineBreaks,
7708
+ computeTotalHeight,
7709
+ imageSizeMeasurements,
7710
+ injectedTextMeasurements,
7711
+ layoutNextLine,
7712
+ measureBlock,
7713
+ measureBlocks,
7714
+ mergeMeasurements,
7715
+ precomputedTextMeasurements,
7716
+ reactiveBlockLayout,
7717
+ reactiveFlowLayout,
7718
+ reactiveLayout,
7719
+ readinessMeasurements,
7720
+ readinessTextMeasurements,
7721
+ rectIntervalForBand,
7722
+ svgBoundsMeasurements,
7723
+ textMeasurementProvider
3248
7724
  });
3249
7725
  //# sourceMappingURL=index.cjs.map