@nicia-ai/typegraph 0.50.0 → 0.51.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 (326) hide show
  1. package/README.md +12 -0
  2. package/dist/backend/drizzle/indexes.cjs +10 -8
  3. package/dist/backend/drizzle/indexes.d.cts +1 -1
  4. package/dist/backend/drizzle/indexes.d.ts +1 -1
  5. package/dist/backend/drizzle/indexes.js +6 -4
  6. package/dist/backend/index.cjs +183 -50
  7. package/dist/backend/index.d.cts +90 -4
  8. package/dist/backend/index.d.ts +90 -4
  9. package/dist/backend/index.js +6 -9
  10. package/dist/backend/postgres/index.cjs +30 -28
  11. package/dist/backend/postgres/index.d.cts +7 -7
  12. package/dist/backend/postgres/index.d.ts +7 -7
  13. package/dist/backend/postgres/index.js +16 -14
  14. package/dist/backend/postgres/pglite-store.cjs +6 -62
  15. package/dist/backend/postgres/pglite-store.cjs.map +1 -1
  16. package/dist/backend/postgres/pglite-store.d.cts +8 -8
  17. package/dist/backend/postgres/pglite-store.d.ts +8 -8
  18. package/dist/backend/postgres/pglite-store.js +6 -62
  19. package/dist/backend/postgres/pglite-store.js.map +1 -1
  20. package/dist/backend/postgres/pglite.cjs +18 -16
  21. package/dist/backend/postgres/pglite.d.cts +3 -3
  22. package/dist/backend/postgres/pglite.d.ts +3 -3
  23. package/dist/backend/postgres/pglite.js +17 -15
  24. package/dist/backend/sqlite/index.cjs +27 -25
  25. package/dist/backend/sqlite/index.d.cts +7 -7
  26. package/dist/backend/sqlite/index.d.ts +7 -7
  27. package/dist/backend/sqlite/index.js +14 -12
  28. package/dist/backend/sqlite/libsql.cjs +21 -19
  29. package/dist/backend/sqlite/libsql.cjs.map +1 -1
  30. package/dist/backend/sqlite/libsql.d.cts +3 -3
  31. package/dist/backend/sqlite/libsql.d.ts +3 -3
  32. package/dist/backend/sqlite/libsql.js +16 -14
  33. package/dist/backend/sqlite/libsql.js.map +1 -1
  34. package/dist/backend/sqlite/local-store.cjs +8 -64
  35. package/dist/backend/sqlite/local-store.cjs.map +1 -1
  36. package/dist/backend/sqlite/local-store.d.cts +8 -8
  37. package/dist/backend/sqlite/local-store.d.ts +8 -8
  38. package/dist/backend/sqlite/local-store.js +7 -64
  39. package/dist/backend/sqlite/local-store.js.map +1 -1
  40. package/dist/backend/sqlite/local.cjs +22 -19
  41. package/dist/backend/sqlite/local.d.cts +3 -3
  42. package/dist/backend/sqlite/local.d.ts +3 -3
  43. package/dist/backend/sqlite/local.js +18 -15
  44. package/dist/{chunk-TUVD4DFR.cjs → chunk-2CVDZCXH.cjs} +122 -51
  45. package/dist/chunk-2CVDZCXH.cjs.map +1 -0
  46. package/dist/{chunk-I5C6OTKR.js → chunk-2DWTBNSH.js} +3 -3
  47. package/dist/{chunk-I5C6OTKR.js.map → chunk-2DWTBNSH.js.map} +1 -1
  48. package/dist/{chunk-C76WFIVZ.cjs → chunk-2KCTP3U2.cjs} +180 -127
  49. package/dist/chunk-2KCTP3U2.cjs.map +1 -0
  50. package/dist/{chunk-BYBDM423.cjs → chunk-322TBTOQ.cjs} +48 -48
  51. package/dist/{chunk-BYBDM423.cjs.map → chunk-322TBTOQ.cjs.map} +1 -1
  52. package/dist/chunk-36SX6QXP.cjs +186 -0
  53. package/dist/chunk-36SX6QXP.cjs.map +1 -0
  54. package/dist/{chunk-H3XP6BAV.js → chunk-36VYWVQU.js} +3 -3
  55. package/dist/{chunk-H3XP6BAV.js.map → chunk-36VYWVQU.js.map} +1 -1
  56. package/dist/{chunk-VTJVMP4I.js → chunk-4DKY6RIU.js} +102 -31
  57. package/dist/chunk-4DKY6RIU.js.map +1 -0
  58. package/dist/chunk-4QKOZYMV.js +9 -0
  59. package/dist/chunk-4QKOZYMV.js.map +1 -0
  60. package/dist/{chunk-U3Z7XKAG.js → chunk-57JM6NW3.js} +8 -18
  61. package/dist/chunk-57JM6NW3.js.map +1 -0
  62. package/dist/{chunk-WCLMKNQE.cjs → chunk-5FVZSE3B.cjs} +1356 -1108
  63. package/dist/chunk-5FVZSE3B.cjs.map +1 -0
  64. package/dist/{chunk-XMY2SXOH.cjs → chunk-5WWEYV4H.cjs} +2208 -579
  65. package/dist/chunk-5WWEYV4H.cjs.map +1 -0
  66. package/dist/{chunk-WQBAQZHR.js → chunk-6NLYIMJO.js} +4 -4
  67. package/dist/{chunk-WQBAQZHR.js.map → chunk-6NLYIMJO.js.map} +1 -1
  68. package/dist/{chunk-G2TLHH6T.cjs → chunk-7B7I7YAW.cjs} +6 -6
  69. package/dist/{chunk-G2TLHH6T.cjs.map → chunk-7B7I7YAW.cjs.map} +1 -1
  70. package/dist/{chunk-EDLKIQRI.js → chunk-7G5IYROL.js} +6 -548
  71. package/dist/chunk-7G5IYROL.js.map +1 -0
  72. package/dist/{chunk-46UGOVRW.js → chunk-7QFRK6PU.js} +3 -7
  73. package/dist/chunk-7QFRK6PU.js.map +1 -0
  74. package/dist/chunk-A4C4NJ5U.cjs +12 -0
  75. package/dist/chunk-A4C4NJ5U.cjs.map +1 -0
  76. package/dist/{chunk-7CCMCMJO.cjs → chunk-AOGOC6JF.cjs} +77 -68
  77. package/dist/chunk-AOGOC6JF.cjs.map +1 -0
  78. package/dist/{chunk-TYGGUWMS.cjs → chunk-B3MN7EJR.cjs} +177 -137
  79. package/dist/chunk-B3MN7EJR.cjs.map +1 -0
  80. package/dist/{chunk-K6VKTBST.cjs → chunk-BZ5MVITP.cjs} +4 -4
  81. package/dist/{chunk-K6VKTBST.cjs.map → chunk-BZ5MVITP.cjs.map} +1 -1
  82. package/dist/{chunk-ZAQUKCUC.js → chunk-CBOD7TXH.js} +5 -5
  83. package/dist/{chunk-ZAQUKCUC.js.map → chunk-CBOD7TXH.js.map} +1 -1
  84. package/dist/{chunk-LLVOQOWE.cjs → chunk-CRZ56SZU.cjs} +6 -5
  85. package/dist/chunk-CRZ56SZU.cjs.map +1 -0
  86. package/dist/{chunk-ON47CBE4.cjs → chunk-D3GWARN7.cjs} +47 -47
  87. package/dist/{chunk-ON47CBE4.cjs.map → chunk-D3GWARN7.cjs.map} +1 -1
  88. package/dist/{chunk-W5SOCUBL.js → chunk-D3OUDFU7.js} +5 -5
  89. package/dist/{chunk-W5SOCUBL.js.map → chunk-D3OUDFU7.js.map} +1 -1
  90. package/dist/chunk-EKG2UXNX.cjs +23 -0
  91. package/dist/chunk-EKG2UXNX.cjs.map +1 -0
  92. package/dist/{chunk-2Q45FAIE.js → chunk-EVYW3532.js} +28 -19
  93. package/dist/chunk-EVYW3532.js.map +1 -0
  94. package/dist/{chunk-7PRMDZIA.cjs → chunk-FMMW3KX7.cjs} +21 -55
  95. package/dist/chunk-FMMW3KX7.cjs.map +1 -0
  96. package/dist/chunk-H2VWDQWC.cjs +571 -0
  97. package/dist/chunk-H2VWDQWC.cjs.map +1 -0
  98. package/dist/chunk-H4FY6OZK.js +13 -0
  99. package/dist/chunk-H4FY6OZK.js.map +1 -0
  100. package/dist/{chunk-OJHD3LXJ.cjs → chunk-H5IJSROF.cjs} +2 -8
  101. package/dist/chunk-H5IJSROF.cjs.map +1 -0
  102. package/dist/{chunk-U5EL6JUV.js → chunk-I5IOSA27.js} +101 -103
  103. package/dist/chunk-I5IOSA27.js.map +1 -0
  104. package/dist/{chunk-SHC7BCG5.cjs → chunk-I7YAY2VQ.cjs} +7 -7
  105. package/dist/{chunk-SHC7BCG5.cjs.map → chunk-I7YAY2VQ.cjs.map} +1 -1
  106. package/dist/chunk-IRCWIA3R.js +20 -0
  107. package/dist/chunk-IRCWIA3R.js.map +1 -0
  108. package/dist/chunk-IVQBLXBK.cjs +55 -0
  109. package/dist/chunk-IVQBLXBK.cjs.map +1 -0
  110. package/dist/{chunk-5PP4KRPV.js → chunk-IZD4HTCP.js} +5 -5
  111. package/dist/{chunk-5PP4KRPV.js.map → chunk-IZD4HTCP.js.map} +1 -1
  112. package/dist/{chunk-RDP6ZRSM.cjs → chunk-JQKMEKKW.cjs} +261 -204
  113. package/dist/chunk-JQKMEKKW.cjs.map +1 -0
  114. package/dist/chunk-JVA3KDEA.cjs +691 -0
  115. package/dist/chunk-JVA3KDEA.cjs.map +1 -0
  116. package/dist/{chunk-JG5XIJDY.js → chunk-K67P5DLL.js} +62 -22
  117. package/dist/chunk-K67P5DLL.js.map +1 -0
  118. package/dist/{chunk-MX7EKQPM.js → chunk-K6OZIHKI.js} +345 -97
  119. package/dist/chunk-K6OZIHKI.js.map +1 -0
  120. package/dist/{chunk-CS6JTQQ2.cjs → chunk-KACEYDYV.cjs} +94 -62
  121. package/dist/chunk-KACEYDYV.cjs.map +1 -0
  122. package/dist/{chunk-5OWHU7Z7.cjs → chunk-L6EVYIID.cjs} +248 -210
  123. package/dist/chunk-L6EVYIID.cjs.map +1 -0
  124. package/dist/{chunk-7OWE7WD6.js → chunk-LI4K2KEJ.js} +4 -4
  125. package/dist/{chunk-7OWE7WD6.js.map → chunk-LI4K2KEJ.js.map} +1 -1
  126. package/dist/chunk-LKZDQ2B4.js +1581 -0
  127. package/dist/chunk-LKZDQ2B4.js.map +1 -0
  128. package/dist/{chunk-45YZXI6X.cjs → chunk-LLFPQOBA.cjs} +27 -27
  129. package/dist/{chunk-45YZXI6X.cjs.map → chunk-LLFPQOBA.cjs.map} +1 -1
  130. package/dist/{chunk-ZRJTM7MT.js → chunk-LZMQ3SFK.js} +3 -3
  131. package/dist/{chunk-ZRJTM7MT.js.map → chunk-LZMQ3SFK.js.map} +1 -1
  132. package/dist/{chunk-DI6VC62F.js → chunk-M3VTS3F6.js} +3 -3
  133. package/dist/{chunk-DI6VC62F.js.map → chunk-M3VTS3F6.js.map} +1 -1
  134. package/dist/{chunk-MZ34E2GN.js → chunk-NBOS7BY3.js} +60 -21
  135. package/dist/chunk-NBOS7BY3.js.map +1 -0
  136. package/dist/{chunk-YA36ABIL.js → chunk-NGQDGL5X.js} +14 -48
  137. package/dist/chunk-NGQDGL5X.js.map +1 -0
  138. package/dist/{chunk-ZBFZGLZE.cjs → chunk-NLX5OWK7.cjs} +6 -2
  139. package/dist/chunk-NLX5OWK7.cjs.map +1 -0
  140. package/dist/chunk-NUQ2YFFH.cjs +1637 -0
  141. package/dist/chunk-NUQ2YFFH.cjs.map +1 -0
  142. package/dist/{chunk-TTCOPKUL.cjs → chunk-OGL4X4PH.cjs} +182 -184
  143. package/dist/chunk-OGL4X4PH.cjs.map +1 -0
  144. package/dist/{chunk-HY5BNOUN.cjs → chunk-OTNTGEXR.cjs} +4 -4
  145. package/dist/{chunk-HY5BNOUN.cjs.map → chunk-OTNTGEXR.cjs.map} +1 -1
  146. package/dist/chunk-OTWE63BW.js +53 -0
  147. package/dist/chunk-OTWE63BW.js.map +1 -0
  148. package/dist/chunk-PC5NGH5A.js +549 -0
  149. package/dist/chunk-PC5NGH5A.js.map +1 -0
  150. package/dist/{chunk-IPRP6TDL.cjs → chunk-PS3TUWJM.cjs} +7 -7
  151. package/dist/{chunk-IPRP6TDL.cjs.map → chunk-PS3TUWJM.cjs.map} +1 -1
  152. package/dist/{chunk-V76FGHY3.js → chunk-PVFFH7TL.js} +5 -5
  153. package/dist/{chunk-V76FGHY3.js.map → chunk-PVFFH7TL.js.map} +1 -1
  154. package/dist/chunk-QCD5JKU4.cjs +15 -0
  155. package/dist/chunk-QCD5JKU4.cjs.map +1 -0
  156. package/dist/{chunk-IRPYOHRC.js → chunk-QG5VEOKX.js} +6 -2
  157. package/dist/chunk-QG5VEOKX.js.map +1 -0
  158. package/dist/{chunk-2CYI5HRH.js → chunk-RI7RJH4G.js} +49 -17
  159. package/dist/chunk-RI7RJH4G.js.map +1 -0
  160. package/dist/{chunk-5LKOVCPP.js → chunk-RVUL2HXY.js} +6 -5
  161. package/dist/chunk-RVUL2HXY.js.map +1 -0
  162. package/dist/{chunk-WK23PGTP.cjs → chunk-S5GU4LUN.cjs} +11 -11
  163. package/dist/{chunk-WK23PGTP.cjs.map → chunk-S5GU4LUN.cjs.map} +1 -1
  164. package/dist/{chunk-S33YQEL7.js → chunk-SDPWO2Z5.js} +6 -6
  165. package/dist/{chunk-S33YQEL7.js.map → chunk-SDPWO2Z5.js.map} +1 -1
  166. package/dist/{chunk-6S3PTHL4.js → chunk-SLGGT2XD.js} +3 -3
  167. package/dist/{chunk-6S3PTHL4.js.map → chunk-SLGGT2XD.js.map} +1 -1
  168. package/dist/{chunk-HSEZ7X6Y.cjs → chunk-TIJUA4I6.cjs} +45 -45
  169. package/dist/{chunk-HSEZ7X6Y.cjs.map → chunk-TIJUA4I6.cjs.map} +1 -1
  170. package/dist/{chunk-CSNOYIB4.cjs → chunk-TM54OBHX.cjs} +13 -13
  171. package/dist/{chunk-CSNOYIB4.cjs.map → chunk-TM54OBHX.cjs.map} +1 -1
  172. package/dist/{chunk-MM3JHVKZ.cjs → chunk-TUWSVL6I.cjs} +4 -4
  173. package/dist/{chunk-MM3JHVKZ.cjs.map → chunk-TUWSVL6I.cjs.map} +1 -1
  174. package/dist/{chunk-2GIV5X4P.js → chunk-TYEYRCXL.js} +86 -33
  175. package/dist/chunk-TYEYRCXL.js.map +1 -0
  176. package/dist/{chunk-MJPZNEJZ.js → chunk-UKOVGCRN.js} +2095 -496
  177. package/dist/chunk-UKOVGCRN.js.map +1 -0
  178. package/dist/chunk-UYKDYE5D.cjs +12 -0
  179. package/dist/{chunk-KWNWG4RC.cjs.map → chunk-UYKDYE5D.cjs.map} +1 -1
  180. package/dist/{chunk-FX5ZW6PN.js → chunk-VAAC7L57.js} +95 -40
  181. package/dist/chunk-VAAC7L57.js.map +1 -0
  182. package/dist/{chunk-ISKCIQF2.cjs → chunk-VKHXYVN7.cjs} +14 -25
  183. package/dist/chunk-VKHXYVN7.cjs.map +1 -0
  184. package/dist/chunk-VUQZNUHU.js +171 -0
  185. package/dist/chunk-VUQZNUHU.js.map +1 -0
  186. package/dist/{chunk-K2XYLVVB.cjs → chunk-WDM5WQK4.cjs} +175 -136
  187. package/dist/chunk-WDM5WQK4.cjs.map +1 -0
  188. package/dist/{chunk-GVYVGGPJ.js → chunk-WFSYYU4W.js} +66 -28
  189. package/dist/chunk-WFSYYU4W.js.map +1 -0
  190. package/dist/{chunk-R7MRFE5P.js → chunk-WS5MT2UJ.js} +3 -3
  191. package/dist/{chunk-R7MRFE5P.js.map → chunk-WS5MT2UJ.js.map} +1 -1
  192. package/dist/chunk-XORONCV3.js +202 -0
  193. package/dist/chunk-XORONCV3.js.map +1 -0
  194. package/dist/chunk-ZGVTNKTX.cjs +217 -0
  195. package/dist/chunk-ZGVTNKTX.cjs.map +1 -0
  196. package/dist/core/index.cjs +20 -20
  197. package/dist/core/index.d.cts +4 -4
  198. package/dist/core/index.d.ts +4 -4
  199. package/dist/core/index.js +4 -4
  200. package/dist/{ddl-B49O7dB_.d.ts → ddl-BvcbgzHo.d.ts} +3 -3
  201. package/dist/{ddl-DcZG660s.d.cts → ddl-Ufebz5q5.d.cts} +3 -3
  202. package/dist/graph-extension/index.d.cts +2 -4
  203. package/dist/graph-extension/index.d.ts +2 -4
  204. package/dist/graph-merge/index.cjs +140 -125
  205. package/dist/graph-merge/index.cjs.map +1 -1
  206. package/dist/graph-merge/index.d.cts +8 -9
  207. package/dist/graph-merge/index.d.ts +8 -9
  208. package/dist/graph-merge/index.js +42 -27
  209. package/dist/graph-merge/index.js.map +1 -1
  210. package/dist/index.cjs +144 -143
  211. package/dist/index.cjs.map +1 -1
  212. package/dist/index.d.cts +11 -12
  213. package/dist/index.d.ts +11 -12
  214. package/dist/index.js +30 -29
  215. package/dist/index.js.map +1 -1
  216. package/dist/indexes/index.cjs +14 -12
  217. package/dist/indexes/index.d.cts +4 -4
  218. package/dist/indexes/index.d.ts +4 -4
  219. package/dist/indexes/index.js +7 -5
  220. package/dist/{ingestion-import-target-CI8OWIkM.d.ts → ingestion-import-target-CAs1OX-l.d.cts} +1 -1
  221. package/dist/{ingestion-import-target-DCpfM1PX.d.cts → ingestion-import-target-DCQSPfNx.d.ts} +1 -1
  222. package/dist/interchange/index.cjs +41 -39
  223. package/dist/interchange/index.cjs.map +1 -1
  224. package/dist/interchange/index.d.cts +8 -8
  225. package/dist/interchange/index.d.ts +8 -8
  226. package/dist/interchange/index.js +16 -14
  227. package/dist/interchange/index.js.map +1 -1
  228. package/dist/local-store-impl-JC7OBRAW.js +75 -0
  229. package/dist/local-store-impl-JC7OBRAW.js.map +1 -0
  230. package/dist/local-store-impl-UOS2LUIG.cjs +77 -0
  231. package/dist/local-store-impl-UOS2LUIG.cjs.map +1 -0
  232. package/dist/{manager-Cfg1ENj1.d.ts → manager-B68OrPR2.d.ts} +1 -2
  233. package/dist/{manager-D348fa6W.d.cts → manager-D_t7oK69.d.cts} +1 -2
  234. package/dist/pglite-store-impl-J5J5AA52.cjs +75 -0
  235. package/dist/pglite-store-impl-J5J5AA52.cjs.map +1 -0
  236. package/dist/pglite-store-impl-LDRH2DUH.js +73 -0
  237. package/dist/pglite-store-impl-LDRH2DUH.js.map +1 -0
  238. package/dist/{postgres-UStOycLI.d.ts → postgres-BPLSVyHM.d.ts} +1 -1
  239. package/dist/{postgres-CH1kkM5J.d.cts → postgres-CqvtFC2y.d.cts} +1 -1
  240. package/dist/profiler/index.d.cts +9 -9
  241. package/dist/profiler/index.d.ts +9 -9
  242. package/dist/provenance/index.cjs +33 -20
  243. package/dist/provenance/index.cjs.map +1 -1
  244. package/dist/provenance/index.d.cts +7 -7
  245. package/dist/provenance/index.d.ts +7 -7
  246. package/dist/provenance/index.js +24 -11
  247. package/dist/provenance/index.js.map +1 -1
  248. package/dist/{repair-validity-windows-aWX7VNvv.d.cts → repair-validity-windows-DEzCKAdy.d.cts} +7 -3
  249. package/dist/{repair-validity-windows-BtOEe2pS.d.ts → repair-validity-windows-GMdaS2AH.d.ts} +7 -3
  250. package/dist/resolve-D_H8MHtq.d.ts +1805 -0
  251. package/dist/resolve-WEW8gWws.d.cts +1805 -0
  252. package/dist/schema/index.cjs +42 -40
  253. package/dist/schema/index.d.cts +4 -6
  254. package/dist/schema/index.d.ts +4 -6
  255. package/dist/schema/index.js +15 -13
  256. package/dist/{searchable-z4BiFfHI.d.ts → searchable-C_UREI9K.d.ts} +1 -1
  257. package/dist/{searchable-C8-RrL3l.d.cts → searchable-ClgET_i7.d.cts} +1 -1
  258. package/dist/{sqlite-axLw5Lo5.d.ts → sqlite-D1mXO9Kp.d.cts} +2 -1
  259. package/dist/{sqlite-Cyx9j9o6.d.cts → sqlite-MZA3oZNB.d.ts} +2 -1
  260. package/dist/{store-CTfQ9taw.d.ts → store-CuFX7fCx.d.cts} +38 -41
  261. package/dist/{store-DR9J7JVB.d.cts → store-D-ZjXQpW.d.ts} +38 -41
  262. package/dist/{temporal-CBOrERz5.d.ts → temporal-CpyEiRY8.d.cts} +1 -1
  263. package/dist/{temporal-B4Ky4wzh.d.cts → temporal-y0Aep9Su.d.ts} +1 -1
  264. package/dist/{types-CWg4dTjj.d.ts → types-6yf0Vv-_.d.cts} +1672 -1
  265. package/dist/{types-CWg4dTjj.d.cts → types-6yf0Vv-_.d.ts} +1672 -1
  266. package/dist/{types-BScHXVM7.d.ts → types-DzJ4dlzk.d.cts} +1 -1
  267. package/dist/{types-BE921wRl.d.cts → types-_vVSB9k-.d.ts} +1 -1
  268. package/dist/{where-BWXUPV3C.d.ts → where-DuG2LCLe.d.ts} +1 -1
  269. package/dist/{where-CNtx1aLc.d.cts → where-Mhf_yyKX.d.cts} +1 -1
  270. package/package.json +5 -1
  271. package/dist/chunk-2CYI5HRH.js.map +0 -1
  272. package/dist/chunk-2GIV5X4P.js.map +0 -1
  273. package/dist/chunk-2Q45FAIE.js.map +0 -1
  274. package/dist/chunk-46UGOVRW.js.map +0 -1
  275. package/dist/chunk-5LKOVCPP.js.map +0 -1
  276. package/dist/chunk-5OWHU7Z7.cjs.map +0 -1
  277. package/dist/chunk-6LEOAIIB.js +0 -1160
  278. package/dist/chunk-6LEOAIIB.js.map +0 -1
  279. package/dist/chunk-7CCMCMJO.cjs.map +0 -1
  280. package/dist/chunk-7PRMDZIA.cjs.map +0 -1
  281. package/dist/chunk-C76WFIVZ.cjs.map +0 -1
  282. package/dist/chunk-CS6JTQQ2.cjs.map +0 -1
  283. package/dist/chunk-EDLKIQRI.js.map +0 -1
  284. package/dist/chunk-EH3JZJUM.cjs +0 -567
  285. package/dist/chunk-EH3JZJUM.cjs.map +0 -1
  286. package/dist/chunk-FX5ZW6PN.js.map +0 -1
  287. package/dist/chunk-GHZPZNFC.cjs +0 -132
  288. package/dist/chunk-GHZPZNFC.cjs.map +0 -1
  289. package/dist/chunk-GVYVGGPJ.js.map +0 -1
  290. package/dist/chunk-IHK5KA2Q.cjs +0 -262
  291. package/dist/chunk-IHK5KA2Q.cjs.map +0 -1
  292. package/dist/chunk-IRPYOHRC.js.map +0 -1
  293. package/dist/chunk-ISKCIQF2.cjs.map +0 -1
  294. package/dist/chunk-JG5XIJDY.js.map +0 -1
  295. package/dist/chunk-K2XYLVVB.cjs.map +0 -1
  296. package/dist/chunk-KWNWG4RC.cjs +0 -12
  297. package/dist/chunk-LLVOQOWE.cjs.map +0 -1
  298. package/dist/chunk-MJPZNEJZ.js.map +0 -1
  299. package/dist/chunk-MX7EKQPM.js.map +0 -1
  300. package/dist/chunk-MZ34E2GN.js.map +0 -1
  301. package/dist/chunk-OJHD3LXJ.cjs.map +0 -1
  302. package/dist/chunk-PALYTHGM.js +0 -114
  303. package/dist/chunk-PALYTHGM.js.map +0 -1
  304. package/dist/chunk-RDP6ZRSM.cjs.map +0 -1
  305. package/dist/chunk-S3SDEQMC.js +0 -532
  306. package/dist/chunk-S3SDEQMC.js.map +0 -1
  307. package/dist/chunk-TEC24ZDA.cjs +0 -1254
  308. package/dist/chunk-TEC24ZDA.cjs.map +0 -1
  309. package/dist/chunk-TP4NJTI3.cjs +0 -1192
  310. package/dist/chunk-TP4NJTI3.cjs.map +0 -1
  311. package/dist/chunk-TTCOPKUL.cjs.map +0 -1
  312. package/dist/chunk-TUVD4DFR.cjs.map +0 -1
  313. package/dist/chunk-TYGGUWMS.cjs.map +0 -1
  314. package/dist/chunk-U3Z7XKAG.js.map +0 -1
  315. package/dist/chunk-U5EL6JUV.js.map +0 -1
  316. package/dist/chunk-VTJVMP4I.js.map +0 -1
  317. package/dist/chunk-WCLMKNQE.cjs.map +0 -1
  318. package/dist/chunk-XMY2SXOH.cjs.map +0 -1
  319. package/dist/chunk-Y55DLLEG.js +0 -247
  320. package/dist/chunk-Y55DLLEG.js.map +0 -1
  321. package/dist/chunk-YA36ABIL.js.map +0 -1
  322. package/dist/chunk-ZBFZGLZE.cjs.map +0 -1
  323. package/dist/index-B0PrmE3K.d.cts +0 -1146
  324. package/dist/index-CwcXsL5R.d.ts +0 -1146
  325. package/dist/materialize-indexes-C9GG3O3C.d.ts +0 -266
  326. package/dist/materialize-indexes-CrSiW_lT.d.cts +0 -266
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/store/types.ts","../src/store/runtime-port.ts","../src/store/claims/sites.ts","../src/store/claims/node-claims.ts","../src/store/claims/resolved-node-claims.ts","../src/store/embedding-sync.ts","../src/store/fulltext-sync.ts","../src/store/operations/node-write-pipeline.ts"],"names":["typeGraphGlobalSymbol","disjointnessClaimAxis","DISJOINT_CONSTRAINT_NAME","uniquenessClaimTarget","checkWherePredicate","computeUniqueKey","DisjointError","UniquenessError","constraintFenceRefusal","uniquenessProbeKinds","isSameClaimOwner","compareClaimTargets","requireDefined","encodeTupleKey","ConfigurationError","claimOwnerKey","resolveEmbeddingFields","readOwnProperty","DEFAULT_SEARCHABLE_LANGUAGE","getSearchableFields","RestrictedDeleteError","purgeEdgeClaims","rowPropsToObject","assertsStoredLowerBound","validateNodeProps","canonicalEqual","ValidationError"],"mappings":"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","file":"chunk-TUVD4DFR.cjs","sourcesContent":["/**\n * Store types for TypeGraph operations.\n */\nimport { type z } from \"zod\";\n\nimport {\n type BackendValidityEndMutation,\n type EdgeRow,\n type NodeRow,\n type TransactionBackend,\n type TransactionReadBackend,\n} from \"../backend/types\";\nimport {\n type EdgeKinds,\n type GraphDef,\n type GraphIdentityConfig,\n type NodeKinds,\n} from \"../core/define-graph\";\nimport { type RecordedInstant } from \"../core/temporal\";\nimport {\n type AnyEdgeType,\n type EdgeId,\n type EdgeRegistration,\n type EdgeType,\n type KindEntity,\n type NodeId,\n type NodeRegistration,\n type NodeType,\n type TemporalMode,\n} from \"../core/types\";\nimport type { IdentityFacade, IdentityWriteSummary } from \"../identity/types\";\nimport type { TraversalExpansion } from \"../query/ast\";\n\n/**\n * An explicit validity-end mutation. Omission preserves the stored end,\n * `validTo` sets it, and `clearValidTo` reopens the window. The union keeps the\n * two write actions mutually exclusive without exposing public `null`.\n */\nexport type ValidityEndMutation = BackendValidityEndMutation;\nimport type {\n DynamicEdgeAccessor,\n DynamicNodeAccessor,\n DynamicNodeKind,\n DynamicNodeType,\n} from \"../query/builder\";\nimport type { BatchableQuery, NodeAccessor } from \"../query/builder/types\";\nimport {\n type ExternalRecordedReadSource,\n type SqlSchema,\n} from \"../query/compiler/schema\";\nimport type { Predicate } from \"../query/predicates\";\nimport { typeGraphGlobalSymbol } from \"../utils/global-symbol\";\nimport type {\n CURRENT_ONLY_READ_NAMES,\n EDGE_BATCH_READ_NAMES,\n EDGE_TEMPORAL_READ_NAMES,\n EDGE_WRITE_NAMES,\n NODE_TEMPORAL_READ_NAMES,\n NODE_WRITE_NAMES,\n RECORDED_POINT_READ_NAMES,\n} from \"./collection-surface\";\n\n// ============================================================\n// Row-to-Meta Field Mapping\n// ============================================================\n\n/**\n * Canonical mapping from snake_case row fields to camelCase meta fields.\n *\n * This is the single source of truth for which row columns become metadata.\n * Both the Meta types and the row-to-meta functions in row-mappers.ts must\n * stay in sync with this mapping. If you add a temporal/audit column to a\n * row type, add the mapping here — the compiler will then force you to\n * update the row mapper functions as well (since their return type is\n * NodeMeta/EdgeMeta, which is derived from this mapping).\n */\ntype TemporalMetaFieldMap = Readonly<{\n valid_from: \"validFrom\";\n valid_to: \"validTo\";\n created_at: \"createdAt\";\n updated_at: \"updatedAt\";\n deleted_at: \"deletedAt\";\n}>;\n\n/**\n * Maps row fields to their camelCase meta counterparts, preserving types.\n */\ntype MapRowToMeta<\n R extends Readonly<Record<string, unknown>>,\n M extends Readonly<Record<string, string>>,\n> = Readonly<{\n [SnakeKey in keyof M as M[SnakeKey] & string]: SnakeKey extends keyof R ?\n R[SnakeKey]\n : never;\n}>;\n\n// ============================================================\n// Node Instance Types\n// ============================================================\n\n/**\n * Metadata for a node instance.\n * Derived from NodeRow via TemporalMetaFieldMap + version.\n *\n * Adding a new metadata column requires:\n * 1. Add the column to NodeRow in backend/types.ts\n * 2. Add the mapping to TemporalMetaFieldMap above\n * 3. The compiler will error in rowToNodeMeta() until you add the field there\n */\nexport type NodeMeta = MapRowToMeta<NodeRow, TemporalMetaFieldMap> &\n Readonly<{ version: NodeRow[\"version\"] }>;\n\n/**\n * A node instance in the graph.\n *\n * Properties from the schema are spread at the top level for ergonomic access:\n * - `node.name` instead of `node.props.name`\n * - System metadata is under `node.meta.*`\n */\nexport type Node<N extends NodeType = NodeType> = Readonly<{\n kind: N[\"kind\"];\n id: NodeId<N>;\n meta: NodeMeta;\n}> &\n Readonly<z.infer<N[\"schema\"]>>;\n\n/**\n * Input for creating a node.\n */\nexport type CreateNodeInput<N extends NodeType = NodeType> = Readonly<{\n kind: N[\"kind\"];\n id?: string; // Optional - will generate ULID if not provided\n props: z.infer<N[\"schema\"]>;\n validFrom?: string;\n validTo?: string;\n}>;\n\n/**\n * Input for updating a node.\n */\nexport type UpdateNodeInput<N extends NodeType = NodeType> = Readonly<{\n kind: N[\"kind\"];\n id: NodeId<N>;\n props: Partial<z.infer<N[\"schema\"]>>;\n}> &\n ValidityEndMutation;\n\n// ============================================================\n// Edge Instance Types\n// ============================================================\n\n/**\n * Metadata for an edge instance.\n * Derived from EdgeRow via TemporalMetaFieldMap (edges have no version).\n */\nexport type EdgeMeta = MapRowToMeta<EdgeRow, TemporalMetaFieldMap>;\n\n/**\n * An edge instance in the graph.\n *\n * Properties from the schema are spread at the top level for ergonomic access:\n * - `edge.role` instead of `edge.props.role`\n * - System metadata is under `edge.meta.*`\n */\nexport type Edge<\n E extends AnyEdgeType = EdgeType,\n From extends NodeType = NodeType,\n To extends NodeType = NodeType,\n> = Readonly<{\n id: EdgeId<E>;\n kind: E[\"kind\"];\n fromKind: From[\"kind\"];\n fromId: NodeId<From>;\n toKind: To[\"kind\"];\n toId: NodeId<To>;\n meta: EdgeMeta;\n}> &\n Readonly<z.infer<E[\"schema\"]>>;\n\n/**\n * Input for creating an edge.\n */\nexport type CreateEdgeInput<E extends AnyEdgeType = EdgeType> = Readonly<{\n kind: E[\"kind\"];\n id?: string; // Optional - will generate ULID if not provided\n fromKind: string;\n fromId: string;\n toKind: string;\n toId: string;\n props: z.infer<E[\"schema\"]>;\n validFrom?: string;\n validTo?: string;\n}>;\n\n/**\n * Input for updating an edge.\n */\nexport type UpdateEdgeInput<E extends AnyEdgeType = EdgeType> = Readonly<{\n id: EdgeId<E>;\n props: Partial<z.infer<E[\"schema\"]>>;\n}> &\n ValidityEndMutation;\n\n// ============================================================\n// Query Options\n// ============================================================\n\n/**\n * Options for node and edge queries.\n */\nexport type NoRecordedCoordinate = Readonly<{\n /**\n * Recorded/system-time coordinates are internal-only and supplied by\n * RecordedStoreView. A public options object carrying this key is a type\n * error even when the object is pre-bound before the call site.\n */\n recordedAsOf?: never;\n}>;\n\nexport type QueryOptions = NoRecordedCoordinate &\n Readonly<{\n /** Temporal mode for the query */\n temporalMode?: TemporalMode;\n /** Specific timestamp for asOf queries */\n asOf?: string;\n }>;\n\n// ============================================================\n// Observability Hooks\n// ============================================================\n\n/**\n * Context passed to observability hooks.\n */\nexport type HookContext = Readonly<{\n /** Unique ID for this operation */\n operationId: string;\n /** Graph ID */\n graphId: string;\n /** Timestamp when operation started */\n startedAt: Date;\n}>;\n\n/**\n * Context for one SQL statement a query builder submits to the backend.\n *\n * A single logical query can emit more than one context, for example when a\n * selective projection falls back to a full-row fetch. Backend-internal setup\n * statements are not exposed as separate query-hook events.\n */\nexport type QueryHookContext = HookContext &\n Readonly<{\n /** The SQL statement being executed */\n sql: string;\n /** Query parameters */\n params: readonly unknown[];\n }>;\n\n/**\n * Operation hook context for CRUD operations.\n */\nexport type OperationHookContext = HookContext &\n Readonly<{\n /** Operation type */\n operation: \"create\" | \"update\" | \"delete\";\n /** Entity type */\n entity: KindEntity;\n /** Kind of node or edge */\n kind: string;\n /** Entity ID */\n id: string;\n }>;\n\n/**\n * Context for one set-based collection mutation. Node `updateWhere` is the only\n * mutation that reports one: the `operation` union is the exhaustive list of\n * what `onBulkOperationStart` / `onBulkOperationEnd` observe, so a batch method\n * absent from it (every `bulk*`, including `bulkDelete`) emits no bulk event.\n */\nexport type BulkOperationHookContext = HookContext &\n Readonly<{\n operation: \"updateWhere\";\n entity: \"node\";\n kind: string;\n }>;\n\n/**\n * Observability hooks for monitoring store operations.\n *\n * Note: Batch operations (`bulkCreate`, `bulkInsert`, `bulkUpsertById`,\n * `bulkDelete`) skip per-item operation hooks for throughput, and the bulk\n * hooks below do not stand in for them — those fire only for node\n * `updateWhere`, so a batch method emits no hook events at all, neither\n * per-item nor bulk. Query hooks still fire normally.\n *\n * @example\n * ```typescript\n * const hooks: StoreHooks = {\n * onQueryStart: (ctx) => {\n * console.log(`[${ctx.operationId}] Query: ${ctx.sql}`);\n * },\n * onQueryEnd: (ctx, result) => {\n * const duration = Date.now() - ctx.startedAt.getTime();\n * console.log(`[${ctx.operationId}] Completed in ${duration}ms`);\n * },\n * onError: (ctx, error) => {\n * console.error(`[${ctx.operationId}] Error:`, error);\n * },\n * };\n *\n * const store = createStore(graph, backend, { hooks });\n * ```\n */\nexport type StoreHooks = Readonly<{\n /** Called before each query-builder statement is submitted to the backend. */\n onQueryStart?: (ctx: QueryHookContext) => void;\n /** Called after each submitted query-builder statement succeeds. */\n onQueryEnd?: (\n ctx: QueryHookContext,\n result: Readonly<{ rowCount: number; durationMs: number }>,\n ) => void;\n /** Called before a CRUD operation starts */\n onOperationStart?: (ctx: OperationHookContext) => void;\n /** Called before a set-based collection mutation starts. */\n onBulkOperationStart?: (ctx: BulkOperationHookContext) => void;\n /**\n * Called after a CRUD operation completes AND is durably committed. For a\n * top-level operation that is when its own transaction commits; for an\n * operation inside `store.transaction`, emission is deferred until the\n * enclosing transaction commits — if that commit fails, the operation is\n * reported through `onError` instead. (Inside an adopted transaction —\n * `withTransaction` / `withRecordedTransaction` — the commit belongs to\n * the caller and cannot be observed, so this fires when the operation\n * completes within the still-open transaction.)\n */\n onOperationEnd?: (\n ctx: OperationHookContext,\n result: Readonly<{ durationMs: number }>,\n ) => void;\n /**\n * Called after a set-based mutation durably commits. Inside\n * `store.transaction`, emission is deferred until the enclosing commit.\n */\n onBulkOperationEnd?: (\n ctx: BulkOperationHookContext,\n result: Readonly<{ affectedCount: number; durationMs: number }>,\n ) => void;\n /**\n * Called when an operation fails — including an operation that completed\n * inside a `store.transaction` whose commit then failed and rolled it\n * back.\n */\n onError?: (ctx: HookContext, error: Error) => void;\n}>;\n\n// ============================================================\n// Store Configuration\n// ============================================================\n\n/**\n * Default row count at which an autocommit bulk write triggers an\n * automatic planner-statistics refresh. Below this, the refresh cost is\n * not worth paying per call and autovacuum/PRAGMA optimize cover it.\n */\nexport const AUTO_REFRESH_STATISTICS_ROW_THRESHOLD = 1000;\n\nexport type BaseStoreOptions = Readonly<{\n /** Observability hooks for monitoring */\n hooks?: StoreHooks;\n /**\n * Maintain a durable, graph-wide revision anchor for TypeGraph writes:\n * a random per-graph origin plus a monotonic revision clock. `branch()` uses\n * this anchor to validate that its base has not moved without\n * re-fingerprinting every live row, and cannot confuse a coincident clock in\n * a separately created store for its original base. `history: true` enables\n * the same tracking automatically through its recorded-time commit clock.\n *\n * This is intentionally opt-in for live stores: each successful graph write\n * advances the anchor inside its write transaction. On PostgreSQL, those\n * transactions take a graph-scoped advisory lock until commit; writes to the\n * same graph therefore serialize. Enable it for branchable graphs, but size\n * high-throughput multi-writer workloads for that trade-off.\n *\n * Writes performed directly through a backend — including raw graph-table\n * writes through `tx.sql` — bypass the anchor and are outside the\n * revision-tracking contract.\n */\n revisionTracking?: boolean;\n /**\n * Automatic planner-statistics refresh after large autocommit bulk\n * writes (bulkCreate and bulkInsert on nodes and edges). Stale statistics after a\n * bulk load are a whole class of planner cliffs: the planner keeps\n * pre-load row estimates until ANALYZE runs. When a single\n * autocommit bulk write reaches the threshold\n * ({@link AUTO_REFRESH_STATISTICS_ROW_THRESHOLD} rows by default),\n * the store runs `refreshStatistics()` after the write commits.\n * Pass a number to change the threshold, or `false` to disable.\n * Bulk writes inside a caller-provided transaction never\n * auto-refresh (statistics cannot see uncommitted rows); refresh\n * manually after commit. `importGraph` handles its own refresh.\n */\n autoRefreshStatistics?: false | number;\n /**\n * Skip the write for an `upsertById`, `bulkUpsertById` item, or endpoint\n * get-or-create update whose validated props are value-identical to the\n * existing live row. Default off.\n *\n * Enable this for at-least-once / replay materializers. An event log that\n * re-delivers a byte-identical change would otherwise rewrite the row anyway:\n * today an `upsertById` on an existing id calls `updateNode`\n * unconditionally, allocating a fresh recorded instant and a new history row\n * per re-delivery.\n *\n * The win is scoped to re-delivery of the **current** value. A full\n * replay-from-zero over the current state still writes wherever the stream\n * superseded a value in place: re-applying an older value over the live row\n * is a genuine change (and restoring the current value afterwards is\n * another), so only streams whose rows never supersede each other replay\n * without churn. A rebuild that should avoid that re-walk belongs in a fresh\n * store — replay into it and publish it, rather than re-applying the log\n * over current state.\n *\n * When enabled, an upsert that would not change the stored value performs\n * **no write at all**: no `updateNode`, no recorded-time capture, no history\n * row, no revision-anchor advance, and no `update` operation hooks (nothing\n * happened, so nothing is reported). It resolves with the **existing** node,\n * preserving its original `validFrom` / `updatedAt` / `version`. An endpoint\n * get-or-create reports action `\"found\"` when its requested update is\n * coalesced; `\"updated\"` always means an UPDATE actually ran.\n * Node `getOrCreateByConstraint` updates are outside this option's scope and\n * still write on every `ifExists: \"update\"` match; replay projectors that\n * need coalescing should use `upsertById` for nodes.\n *\n * Receipt shape is unchanged and needs no new signal: a coalesced upsert\n * still counts as one write intent (`writes.total` includes it), but\n * captures nothing (`receipt.recorded` stays `undefined` when it is the only\n * write) — the same shape a no-op delete already produces, so a consumer\n * that carries the prior anchor forward on `recorded === undefined` handles\n * it unchanged.\n *\n * A write is coalesced only when **all** of the following hold; otherwise the\n * normal write happens:\n * 1. A value to compare against is known for the id: an existing row, or —\n * for a repeated id in one bulk batch — the value an earlier item in that\n * batch already queued (a create or an update).\n * 2. That row is not soft-deleted (a deleted row resurrects — a real\n * change — and is never coalesced).\n * 3. Any requested `validFrom` / `validTo` names the window already stored;\n * a changed or inapplicable temporal request reaches the write path.\n * 4. The new props, merged over the stored props and run through the\n * kind's Zod schema (defaults applied, values normalized), are deeply\n * value-identical to the stored props (key order aside).\n *\n * Default-off because some consumers *want* an audit row per re-delivery as\n * proof the event was reprocessed; coalescing removes that signal.\n */\n coalesceUnchangedUpserts?: boolean;\n /** SQL schema configuration from createSqlSchema(...) for custom table names */\n schema?: SqlSchema;\n /** Query default behaviors. */\n queryDefaults?: Readonly<{\n /** Default traversal ontology expansion mode (default: \"inverse\"). */\n traversalExpansion?: TraversalExpansion;\n }>;\n}>;\n\n/**\n * Store options without built-in recorded-time capture. A recorded read relation\n * can still be bound explicitly for hosts that populate history externally.\n */\nexport type LiveStoreOptions = BaseStoreOptions &\n Readonly<{\n history?: false | undefined;\n recordedRead?: ExternalRecordedReadSource | undefined;\n }>;\n\n/** Live-store options that do not bind a recorded-read relation. */\nexport type UnboundLiveStoreOptions = LiveStoreOptions &\n Readonly<{ recordedRead?: undefined }>;\n\n/** Live-store options with an explicitly bound recorded-read relation. */\nexport type RecordedReadStoreOptions = LiveStoreOptions &\n Readonly<{\n recordedRead: NonNullable<LiveStoreOptions[\"recordedRead\"]>;\n }>;\n\n/**\n * Store options with TypeGraph-managed recorded-time capture. `history: true`\n * captures TypeGraph writes and binds TypeGraph's built-in recorded relation\n * internally. Externally populated recorded read sources are read-only bindings\n * and are intentionally accepted only by {@link LiveStoreOptions}.\n */\nexport type HistoryStoreOptions = BaseStoreOptions &\n Readonly<{\n history: true;\n recordedRead?: never;\n }>;\n\n/**\n * Options for creating a store.\n */\nexport type StoreOptions = LiveStoreOptions | HistoryStoreOptions;\n\n/**\n * A mutable handle to the current `Store`, used by `store.evolve(...)`\n * so long-lived consumers can dereference through the ref and pick up\n * the new Store after each evolve call. When the ref is passed via\n * `evolve(extension, { ref })`, `current` is overwritten with the replacement\n * before a successful call resolves.\n *\n * **Request semantics.** Dereference once at request entry only when that\n * request will not change the schema. A schema-changing call such as\n * `evolve()` returns the Store for the resulting schema and updates\n * `ref.current`; a Store captured before that call remains pinned to the old\n * schema. Use the returned Store (or dereference `ref.current` again) for every\n * subsequent operation in the same request:\n *\n * ```ts\n * async function handleRequest(): Promise<void> {\n * const store = ref.current;\n * const evolved = await store.evolve(extension, { ref });\n * await evolved.materializeIndexes();\n * // ref.current === evolved\n * }\n * ```\n *\n * A `StoreRef` tracks only schema changes made by calls that receive that ref.\n * When another process or isolate can commit a schema version, compare\n * `getCommittedSchemaVersion()` with the cached version before reuse and call\n * `createVerifiedStore()` or `createVerifiedAdapterStore()` when it changes.\n *\n * Pure dereferenceable handle — no event/subscription machinery. If\n * consumers need eventing, they wrap the ref themselves.\n *\n * Generic over the held value (typically `Store<G>`) so the store\n * module can refer to it without importing the `Store` class into\n * `types.ts`. The type parameter is deliberately invariant: evolution writes\n * a replacement into `current`, so a ref for an adapter-only or history-only\n * Store must never be accepted by a Store that cannot preserve that surface.\n */\nexport interface StoreRef<in out T> {\n current: T;\n}\n\n// ============================================================\n// Transaction Receipt Types\n// ============================================================\n\n/**\n * Result plus write summary. Returned by `store.transactionWithReceipt(fn)`,\n * `store.withRecordedTransaction(externalTx, fn)` (the adopted-commit path), and\n * `tx.measure(fn)` (a scoped sub-receipt). See {@link TransactionReceipt}.\n */\nexport type TransactionOutcome<T> = Readonly<{\n result: T;\n receipt: TransactionReceipt;\n}>;\n\n/**\n * Transaction write summary.\n *\n * Receipt counts are completed write intents at the collection surface, not\n * rows affected:\n *\n * 1. Every successful completion of a write method on `tx.nodes.*` /\n * `tx.edges.*` counts. The authoritative method list is\n * {@link NodeWrites} / {@link EdgeWrites}.\n * 2. Bulk methods count by input length; an empty bulk call (`bulkCreate([])`)\n * counts 0.\n * 3. Single-row methods count 1 on resolve — including `delete` of an absent\n * id and `getOrCreate*` that found an existing row. Consumers that need\n * \"did anything actually change\" semantics apply their own per-operation\n * policy.\n * 4. A method that rejects counts 0 — even when the backend applied part of a\n * bulk input before failing. On SQLite a failed statement does not abort\n * the surrounding transaction, so a caller that catches the rejection and\n * commits can persist rows the receipt never counted. Do not read the\n * receipt as rows-affected in that scenario.\n * 5. A node `delete` under `cascade` / `disconnect` removes connected edges\n * through the backend, not the edge-collection surface; those removals do\n * not appear in `edges`.\n * 6. Rows-affected fidelity is intentionally out of scope for this first\n * version; a future extension could ask backends to return row counts.\n */\nexport type TransactionReceipt = Readonly<{\n writes: Readonly<{\n /** Completed node write intents by node kind. */\n nodes: Readonly<Record<string, number>>;\n /** Completed edge write intents by edge kind. */\n edges: Readonly<Record<string, number>>;\n /** Completed identity assertion and retraction write intents. */\n identity: IdentityWriteSummary;\n /** Sum of all node, edge, and identity write intents. */\n total: number;\n }>;\n /**\n * The recorded commit instant allocated for this store's graph by this\n * transaction. Undefined when history capture is off, the transaction is\n * read-only, or no captured writes were flushed. **Always undefined on a\n * scoped receipt from {@link ScopedMeasure}** (`tx.measure`) — the recorded\n * instant is a per-transaction flush concern allocated once when the whole\n * transaction's capture flushes, unknowable mid-transaction.\n */\n recorded?: RecordedInstant;\n}>;\n\n// ============================================================\n// Get-Or-Create Types\n// ============================================================\n\n/**\n * Behavior when a get-or-create operation matches an existing record.\n */\nexport type IfExistsMode = \"return\" | \"update\";\n\n/**\n * Action taken by a get-or-create operation.\n */\nexport type GetOrCreateAction = \"created\" | \"found\" | \"updated\" | \"resurrected\";\n\n/**\n * Result of a node getOrCreateByConstraint operation.\n */\nexport type NodeGetOrCreateByConstraintResult<N extends NodeType> = Readonly<{\n node: Node<N>;\n action: GetOrCreateAction;\n}>;\n\n/**\n * Options for node getOrCreateByConstraint operations.\n */\nexport type NodeGetOrCreateByConstraintOptions = Readonly<{\n /** Existing record behavior. Default: \"return\" */\n ifExists?: IfExistsMode;\n}>;\n\n/**\n * Options for node bulkFindByIndex operations.\n */\nexport type NodeBulkFindByIndexOptions = Readonly<{\n /**\n * Maximum number of candidate nodes returned per input item. When omitted,\n * each input's candidate set is unbounded. Must be a positive integer.\n *\n * Candidates are ordered deterministically by node id, so the cap is stable\n * across calls. Use this to bound fan-out on low-selectivity index keys.\n */\n limitPerInput?: number;\n}>;\n\n/**\n * Per-endpoint fan-out cap shared by the live and view forms of the edge\n * bulk endpoint reads.\n */\nexport type EdgeBulkFindOptions = Readonly<{\n /**\n * Maximum number of edges returned for each input endpoint. When omitted,\n * every input's edge set is unbounded. Must be a positive integer.\n *\n * The cap keeps each input's leading edges under the same ordering\n * `findFrom` / `findTo` return, so it bounds fan-out without reordering\n * anything.\n */\n limitPerInput?: number;\n}>;\n\n/**\n * Options for the edge bulk endpoint reads: the temporal coordinate every\n * edge read accepts, plus the per-endpoint fan-out cap.\n */\nexport type EdgeBulkFindEndpointOptions = QueryOptions & EdgeBulkFindOptions;\n\n/** A kind-grouped source list for a heterogeneous bulk edge read. */\nexport type BulkEdgeSourceGroup<G extends GraphDef> = {\n [K in NodeKinds<G>]: Readonly<{\n kind: K;\n ids: readonly NodeId<G[\"nodes\"][K][\"type\"]>[];\n }>;\n}[NodeKinds<G>];\n\n/** A node reference whose kind and branded id remain correlated. */\nexport type GraphNodeReference<G extends GraphDef> = {\n [K in NodeKinds<G>]: Readonly<{\n kind: K;\n id: NodeId<G[\"nodes\"][K][\"type\"]>;\n }>;\n}[NodeKinds<G>];\n\n/** Runtime edge union narrowed to the selected graph edge kinds. */\nexport type GraphEdgeForKinds<G extends GraphDef, K extends EdgeKinds<G>> = {\n [P in K]: Edge<\n G[\"edges\"][P][\"type\"],\n G[\"edges\"][P][\"from\"][number],\n G[\"edges\"][P][\"to\"][number]\n >;\n}[K];\n\n/** Input for {@link Store.bulkFindEdgesFrom}. */\nexport type BulkFindEdgesFromParams<\n G extends GraphDef,\n K extends EdgeKinds<G>,\n> = Readonly<{\n sources: readonly BulkEdgeSourceGroup<G>[];\n edgeKinds: readonly K[];\n}>;\n\n/** One source bucket returned by {@link Store.bulkFindEdgesFrom}. */\nexport type BulkFindEdgesFromResult<\n G extends GraphDef,\n K extends EdgeKinds<G>,\n> = Readonly<{\n source: GraphNodeReference<G>;\n edges: readonly GraphEdgeForKinds<G, K>[];\n}>;\n\n/**\n * Result of an edge getOrCreateByEndpoints operation.\n */\nexport type EdgeGetOrCreateByEndpointsResult<\n E extends AnyEdgeType,\n From extends NodeType = NodeType,\n To extends NodeType = NodeType,\n> = Readonly<{\n edge: Edge<E, From, To>;\n action: GetOrCreateAction;\n}>;\n\n/**\n * Options for edge findByEndpoints operations.\n */\nexport type EdgeFindByEndpointsOptions<E extends AnyEdgeType> = Readonly<{\n /**\n * Edge property fields to include in the match alongside the (from, to) endpoints.\n * When omitted, matches on endpoints only (returns the first edge at the read coordinate).\n */\n matchOn?: readonly (keyof z.input<E[\"schema\"]>)[];\n /** Property values to match against when matchOn is specified. */\n props?: Partial<z.input<E[\"schema\"]>>;\n}>;\n\n/**\n * Options for edge getOrCreateByEndpoints operations.\n */\nexport type EdgeGetOrCreateByEndpointsOptions<E extends AnyEdgeType> =\n Readonly<{\n /**\n * Edge property fields to include in the match key alongside the (from, to) endpoints.\n * Default: `[]` — match on endpoints only.\n */\n matchOn?: readonly (keyof z.input<E[\"schema\"]>)[];\n /** Existing record behavior. Default: \"return\" */\n ifExists?: IfExistsMode;\n /**\n * Valid-time start for a created or RESURRECTED edge — on a resurrection it\n * asserts the complete window, so an omitted `validTo` reopens the revived\n * row.\n *\n * A live edge's stored lower bound is history, so an in-place update\n * (`ifExists: \"update\"`) cannot store this: one naming a different instant is\n * REFUSED with `IMMUTABLE_VALIDITY_LOWER_BOUND_CODE`, and one restating the\n * bound the edge holds is accepted. Ignored when an existing edge is returned\n * (`ifExists: \"return\"`), which writes nothing at all — there the window\n * describes the edge to create if none is found.\n */\n validFrom?: string;\n /**\n * How an `ifExists: \"update\"` write treats a stated `validFrom` that differs\n * from the live edge's stored lower bound. Default: `\"refuse\"`.\n *\n * `\"preserve\"` makes `validFrom` create/resurrection-only input: it is still\n * validated, but a live update keeps the stored lower bound while applying\n * properties and `validTo`.\n */\n onImmutableLowerBound?: \"preserve\" | \"refuse\";\n /**\n * Valid-time end for a created, updated, or resurrected edge. `validTo` is\n * ignored when the operation returns an existing edge without writing;\n * `clearValidTo` instead requires `ifExists: \"update\"` on a live match and\n * is refused with `CLEAR_VALID_TO_REQUIRES_UPDATE` under return mode. May\n * not precede the row's effective start; see\n * `INVERTED_VALIDITY_WINDOW_CODE`.\n */\n }> &\n ValidityEndMutation;\n\n// ============================================================\n// Collection Interfaces\n// ============================================================\n\n/**\n * A collection of nodes of a specific type.\n *\n * Provides ergonomic CRUD operations for a single node type.\n */\nexport type NodeCollection<\n N extends NodeType,\n CN extends string = string,\n> = Readonly<{\n /**\n * Create a new node.\n *\n * `validFrom` defaults to the operation's creation timestamp when omitted —\n * unless a stated `validTo` at or before that instant would leave the row\n * readable at no coordinate, in which case the row is stored with NO lower\n * bound (\"ended at T, start unknown\") and `meta.validFrom` reads back as\n * `undefined`. A future `validTo` is unaffected.\n */\n create: (\n props: z.input<N[\"schema\"]>,\n options?: Readonly<{ id?: string; validFrom?: string; validTo?: string }>,\n ) => Promise<Node<N>>;\n\n /** Get a node by ID */\n getById: (\n id: NodeId<N>,\n options?: QueryOptions,\n ) => Promise<Node<N> | undefined>;\n\n /** Get multiple nodes by ID, preserving input order (undefined for missing) */\n getByIds: (\n ids: readonly NodeId<N>[],\n options?: QueryOptions,\n ) => Promise<readonly (Node<N> | undefined)[]>;\n\n /**\n * Update a node's properties and optionally set or clear its validity end.\n * Omitting both end options preserves the stored window.\n */\n update: (\n id: NodeId<N>,\n props: Partial<z.input<N[\"schema\"]>>,\n options?: ValidityEndMutation,\n ) => Promise<Node<N>>;\n\n /**\n * Updates every current node selected by the supplied predicates in one\n * set-based write. Relationship clauses are independent EXISTS predicates\n * and are ANDed with each other and with `where`.\n */\n updateWhere: (\n params: Readonly<{\n patch: Partial<z.input<N[\"schema\"]>>;\n where?: (\n accessor: string extends N[\"kind\"] ? DynamicNodeAccessor\n : NodeAccessor<N>,\n ) => Predicate;\n exists?: readonly Readonly<{\n edgeKind: string;\n direction: \"out\" | \"in\";\n relatedKind: string;\n whereEdge?: (accessor: DynamicEdgeAccessor) => Predicate;\n whereRelated?: (accessor: DynamicNodeAccessor) => Predicate;\n }>[];\n all?: true;\n }>,\n ) => Promise<Readonly<{ affectedCount: number }>>;\n\n /** Delete a node (soft delete - sets deletedAt timestamp) */\n delete: (id: NodeId<N>) => Promise<void>;\n\n /**\n * Permanently delete a node from the database.\n *\n * Unlike `delete()` which performs a soft delete, this permanently\n * removes the node and its associated data (uniqueness entries, embeddings).\n *\n * **Warning:** This operation is irreversible and should be used carefully.\n * Consider using soft delete (`delete()`) for most use cases.\n *\n * @throws Error if edges are still connected to this node (delete edges first)\n */\n hardDelete: (id: NodeId<N>) => Promise<void>;\n\n /**\n * Find nodes matching criteria.\n *\n * Supports predicate filtering via the `where` option for SQL-level filtering.\n * For simple queries. Use store.query() for complex traversals.\n */\n find: (\n filter?: Readonly<{\n where?: (accessor: NodeAccessor<N>) => Predicate;\n limit?: number;\n offset?: number;\n }>,\n temporal?: QueryOptions,\n ) => Promise<Node<N>[]>;\n\n /** Count nodes matching criteria */\n count: (temporal?: QueryOptions) => Promise<number>;\n\n /**\n * Create a node from untyped data, relying on runtime Zod validation.\n *\n * Use this for dynamic dispatch (changesets, migrations, imports) where\n * the data shape is determined at runtime, not compile time.\n * The return type is fully typed — only the input gate is relaxed.\n *\n * `validFrom` defaults to the operation's creation timestamp when omitted —\n * unless a stated `validTo` at or before that instant would leave the row\n * readable at no coordinate, in which case the row is stored with NO lower\n * bound (\"ended at T, start unknown\") and `meta.validFrom` reads back as\n * `undefined`. A future `validTo` is unaffected.\n */\n createFromRecord: (\n data: Record<string, unknown>,\n options?: Readonly<{ id?: string; validFrom?: string; validTo?: string }>,\n ) => Promise<Node<N>>;\n\n /**\n * Create or update a node.\n *\n * If a node with the given ID exists, updates it with the provided props.\n * Otherwise, creates a new node with that ID.\n *\n * `validFrom` applies when the upsert CREATES the row and when it RESURRECTS\n * a tombstoned one — both write a fresh validity window — defaulting to the\n * operation's timestamp when omitted. That default is dropped when a stated\n * `validTo` at or before the write instant would leave the row readable at no\n * coordinate: NO lower bound is stored (\"ended at T, start unknown\") and\n * `meta.validFrom` reads back as `undefined`. A RESURRECTION takes the same\n * exception, decided against the instant it samples, so one stated window\n * reaches ONE stored shape whether the id is fresh or names a tombstone. An\n * update to a LIVE row stores no lower bound, because that row's is already\n * history, so one naming a different instant is REFUSED (`ValidationError`\n * carrying\n * `IMMUTABLE_VALIDITY_LOWER_BOUND_CODE`) rather than ignored. Restating the\n * bound the row already holds is accepted and changes nothing. Set\n * `onImmutableLowerBound: \"preserve\"` for event materializers whose\n * `validFrom` is create/resurrection input: a live update then preserves the\n * stored lower bound while still applying props and `validTo`.\n */\n upsertById: (\n id: string,\n props: z.input<N[\"schema\"]>,\n options?: Readonly<{\n validFrom?: string;\n onImmutableLowerBound?: \"preserve\" | \"refuse\";\n }> &\n ValidityEndMutation,\n ) => Promise<Node<N>>;\n\n /**\n * Upsert a node from untyped data, relying on runtime Zod validation.\n *\n * Use this for dynamic dispatch (changesets, migrations, imports) where\n * the data shape is determined at runtime, not compile time.\n * The return type is fully typed — only the input gate is relaxed.\n *\n * `validFrom` applies when the upsert CREATES the row and when it RESURRECTS\n * a tombstoned one — both write a fresh validity window — defaulting to the\n * operation's timestamp when omitted. That default is dropped when a stated\n * `validTo` at or before the write instant would leave the row readable at no\n * coordinate: NO lower bound is stored (\"ended at T, start unknown\") and\n * `meta.validFrom` reads back as `undefined`. A RESURRECTION takes the same\n * exception, decided against the instant it samples, so one stated window\n * reaches ONE stored shape whether the id is fresh or names a tombstone. An\n * update to a LIVE row stores no lower bound, because that row's is already\n * history, so one naming a different instant is REFUSED (`ValidationError`\n * carrying\n * `IMMUTABLE_VALIDITY_LOWER_BOUND_CODE`) rather than ignored. Restating the\n * bound the row already holds is accepted and changes nothing. Set\n * `onImmutableLowerBound: \"preserve\"` for create/resurrection-only input.\n */\n upsertByIdFromRecord: (\n id: string,\n data: Record<string, unknown>,\n options?: Readonly<{\n validFrom?: string;\n onImmutableLowerBound?: \"preserve\" | \"refuse\";\n }> &\n ValidityEndMutation,\n ) => Promise<Node<N>>;\n\n /**\n * Create multiple nodes in a batch.\n *\n * More efficient than calling create() multiple times.\n * Use `bulkInsert` for the dedicated fast path that skips returning results.\n *\n * `validFrom` defaults to the operation's creation timestamp when omitted —\n * unless a stated `validTo` at or before that instant would leave the row\n * readable at no coordinate, in which case the row is stored with NO lower\n * bound (\"ended at T, start unknown\") and `meta.validFrom` reads back as\n * `undefined`. A future `validTo` is unaffected.\n */\n bulkCreate: (\n items: readonly Readonly<{\n props: z.input<N[\"schema\"]>;\n id?: string;\n validFrom?: string;\n validTo?: string;\n }>[],\n ) => Promise<Node<N>[]>;\n\n /**\n * Create or update multiple nodes in a batch.\n *\n * For each item, if a node with the given ID exists, updates it.\n * Otherwise, creates a new node with that ID.\n *\n * Items are applied in order, so a **repeated id** within one batch is\n * last-write-wins: the first item creates or updates the row, and every later\n * copy is an update over the value the earlier item wrote — the same final row\n * the equivalent sequence of `upsertById` calls produces. This holds whether\n * or not the row existed before the batch.\n *\n * `validFrom` applies when the upsert CREATES the row and when it RESURRECTS\n * a tombstoned one — both write a fresh validity window — defaulting to the\n * operation's timestamp when omitted. That default is dropped when a stated\n * `validTo` at or before the write instant would leave the row readable at no\n * coordinate: NO lower bound is stored (\"ended at T, start unknown\") and\n * `meta.validFrom` reads back as `undefined`. A RESURRECTION takes the same\n * exception, decided against the instant it samples, so one stated window\n * reaches ONE stored shape whether the id is fresh or names a tombstone. An update to a LIVE row stores no lower\n * bound, because that row's is already history, so one naming a different\n * instant is REFUSED (`ValidationError` carrying\n * `IMMUTABLE_VALIDITY_LOWER_BOUND_CODE`) rather than ignored. Restating the\n * bound the row already holds is accepted and changes nothing. Per-item\n * `onImmutableLowerBound: \"preserve\"` makes the bound create/resurrection\n * input while a live update preserves the stored lower bound.\n *\n * **Limitation — a batch cannot hand a unique value from one row to\n * another.** Item order settles which value each id ends up with, but the\n * writes themselves are grouped: every create runs before every update. So a\n * batch where one item RELEASES a `unique` constraint value and a later item\n * CLAIMS it — `[{ id: \"a\", props: { email: \"moved@x\" } }, { id: \"b\", props:\n * { email: \"shared@x\" } }]` where `a` currently holds `\"shared@x\"` and `b` is\n * new — checks `b`'s create while `a` still reserves the value, and the whole\n * batch fails with a `UniquenessError`. The equivalent sequence of\n * single `upsertById` calls succeeds. A batch states the set of rows it wants,\n * not a script to reach them by; the failure is loud rather than silent, and\n * the workaround is to split the handoff across two batches (release, then\n * claim) or to apply those items as sequential `upsertById` calls.\n */\n bulkUpsertById: (\n items: readonly (Readonly<{\n id: string;\n props: z.input<N[\"schema\"]>;\n validFrom?: string;\n onImmutableLowerBound?: \"preserve\" | \"refuse\";\n }> &\n ValidityEndMutation)[],\n ) => Promise<Node<N>[]>;\n\n /**\n * Insert multiple nodes without returning results.\n *\n * This is the dedicated fast path for bulk inserts. Unlike `bulkCreate`\n * with `returnResults: false`, the intent is unambiguous: no results\n * are returned and the operation is wrapped in a transaction.\n *\n * `validFrom` defaults to the operation's creation timestamp when omitted —\n * unless a stated `validTo` at or before that instant would leave the row\n * readable at no coordinate, in which case the row is stored with NO lower\n * bound (\"ended at T, start unknown\") and `meta.validFrom` reads back as\n * `undefined`. A future `validTo` is unaffected.\n */\n bulkInsert: (\n items: readonly Readonly<{\n props: z.input<N[\"schema\"]>;\n id?: string;\n validFrom?: string;\n validTo?: string;\n }>[],\n ) => Promise<void>;\n\n /**\n * Delete multiple nodes by ID.\n *\n * Atomic when the backend supports transactions. Silently ignores IDs\n * that don't exist.\n */\n bulkDelete: (ids: readonly NodeId<N>[]) => Promise<void>;\n\n /**\n * Find a node by uniqueness constraint.\n *\n * Looks up a live node by the named constraint key computed from `props`.\n * Returns the node if found, or undefined. Soft-deleted nodes are excluded.\n *\n * @param constraintName - Name of the uniqueness constraint to match on\n * @param props - Properties to compute the constraint key from\n */\n findByConstraint: (\n constraintName: CN,\n props: z.input<N[\"schema\"]>,\n ) => Promise<Node<N> | undefined>;\n\n /**\n * Batch version of findByConstraint.\n *\n * Results are returned in the same order as the input items.\n * Returns undefined for entries that don't match.\n */\n bulkFindByConstraint: (\n constraintName: CN,\n items: readonly Readonly<{\n props: z.input<N[\"schema\"]>;\n }>[],\n ) => Promise<(Node<N> | undefined)[]>;\n\n /**\n * Batched candidate retrieval against a declared node index.\n *\n * For each input item, TypeGraph computes the index lookup key from\n * `index.fields` (JSON-pointer extraction, partial-`where` applied to\n * stored rows, null-safe matching) and returns the live, non-soft-deleted\n * nodes that share that key. Unlike {@link bulkFindByConstraint}, the index\n * may be non-unique, so each input yields a (possibly empty) array.\n *\n * Results preserve input order; each inner array is ordered by node id.\n * Empty input returns `[]`. An unknown index name throws\n * `NodeIndexNotFoundError`; a type-incompatible indexed field throws\n * `ValidationError`. This is candidate retrieval, not a uniqueness or\n * identity guarantee.\n *\n * @param indexName - Name of the declared node index to match on\n * @param items - Records whose `props` supply the indexed-field values\n * @param options - Optional `limitPerInput` to bound per-input fan-out\n */\n bulkFindByIndex: (\n indexName: string,\n items: readonly Readonly<{\n props: Partial<z.input<N[\"schema\"]>>;\n }>[],\n options?: NodeBulkFindByIndexOptions,\n ) => Promise<readonly Node<N>[][]>;\n\n /**\n * Get an existing node by uniqueness constraint, or create a new one.\n *\n * Looks up a node by the named constraint key computed from `props`.\n * If found, returns it (optionally updating with `ifExists: \"update\"`).\n * If not found, creates a new node. Soft-deleted matches are always resurrected.\n *\n * @param constraintName - Name of the uniqueness constraint to match on\n * @param props - Full properties for create, or merge source for update\n * @param options - Existing record behavior (default: \"return\")\n */\n getOrCreateByConstraint: (\n constraintName: CN,\n props: z.input<N[\"schema\"]>,\n options?: NodeGetOrCreateByConstraintOptions,\n ) => Promise<NodeGetOrCreateByConstraintResult<N>>;\n\n /**\n * Batch version of getOrCreateByConstraint.\n *\n * Results are returned in the same order as the input items.\n * Atomic when the backend supports transactions.\n */\n bulkGetOrCreateByConstraint: (\n constraintName: CN,\n items: readonly Readonly<{\n props: z.input<N[\"schema\"]>;\n }>[],\n options?: NodeGetOrCreateByConstraintOptions,\n ) => Promise<NodeGetOrCreateByConstraintResult<N>[]>;\n}>;\n\n/**\n * Reference to a node of a specific kind.\n *\n * Accepts either:\n * - A Node instance of the correct kind\n * - An explicit { kind, id } object with the correct kind name\n *\n * This provides compile-time checking that edge endpoints match the\n * allowed node kinds defined in the edge registration.\n */\nexport type NodeRef<N extends NodeType = NodeType> =\n Node<N> | Readonly<{ kind: N[\"kind\"]; id: string }>;\n\n/**\n * Options for creating an edge.\n */\ntype EdgeCreateOptions = Readonly<{\n id?: string;\n validFrom?: string;\n validTo?: string;\n}>;\n\n/**\n * Arguments for edge creation, with props optional when schema allows empty object.\n *\n * Uses `{}` to check if an empty object literal satisfies the schema input type.\n */\n/* eslint-disable @typescript-eslint/no-empty-object-type -- {} is intentional: checking if empty object satisfies schema */\ntype EdgeCreateArguments<E extends AnyEdgeType> =\n {} extends z.input<E[\"schema\"]> ?\n [props?: z.input<E[\"schema\"]>, options?: EdgeCreateOptions]\n : [props: z.input<E[\"schema\"]>, options?: EdgeCreateOptions];\n/* eslint-enable @typescript-eslint/no-empty-object-type */\n\n/**\n * A collection of edges of a specific type.\n *\n * Provides ergonomic CRUD operations for a single edge type.\n * The From and To type parameters enforce that edge endpoints\n * match the allowed node types at compile time.\n *\n * @example\n * ```typescript\n * // Create an edge - pass Node objects directly\n * const edge = await store.edges.worksAt.create(alice, acme, { role: \"Engineer\" });\n *\n * // TypeScript error: Company is not a valid 'from' type for worksAt\n * // store.edges.worksAt.create(acme, alice, { role: \"Engineer\" });\n *\n * // For edges with empty schemas, props is optional\n * await store.edges.wrote.create(author, book);\n *\n * // Find edges from a node\n * const edges = await store.edges.worksAt.findFrom(alice);\n * ```\n */\nexport type EdgeCollection<\n E extends AnyEdgeType,\n From extends NodeType = NodeType,\n To extends NodeType = NodeType,\n> = Readonly<{\n /**\n * Create a new edge.\n *\n * `validFrom` defaults to the operation's creation timestamp when omitted —\n * unless a stated `validTo` at or before that instant would leave the row\n * readable at no coordinate, in which case the row is stored with NO lower\n * bound (\"ended at T, start unknown\") and `meta.validFrom` reads back as\n * `undefined`. A future `validTo` is unaffected.\n *\n * @param from - Source node (must be one of the allowed 'from' types)\n * @param to - Target node (must be one of the allowed 'to' types)\n * @param args - Edge properties (optional if schema is empty) and creation options\n */\n create: (\n from: NodeRef<From>,\n to: NodeRef<To>,\n ...args: EdgeCreateArguments<E>\n ) => Promise<Edge<E, From, To>>;\n\n /** Get an edge by ID */\n getById: (\n id: EdgeId<E>,\n options?: QueryOptions,\n ) => Promise<Edge<E, From, To> | undefined>;\n\n /** Get multiple edges by ID, preserving input order (undefined for missing) */\n getByIds: (\n ids: readonly EdgeId<E>[],\n options?: QueryOptions,\n ) => Promise<readonly (Edge<E, From, To> | undefined)[]>;\n\n /**\n * Update an edge's properties and optionally set or clear its validity end.\n * Reopening a `oneActive` edge rechecks cardinality before the write.\n */\n update: (\n id: EdgeId<E>,\n props: Partial<z.input<E[\"schema\"]>>,\n options?: ValidityEndMutation,\n ) => Promise<Edge<E, From, To>>;\n\n /**\n * Find edges from a specific node.\n *\n * Honors the same temporal model as `getById` / `find`: with no\n * `options`, the graph's default `temporalMode` applies (excluding\n * soft-deleted edges and, in `current` / `asOf` modes, edges outside\n * their validity window). Pass `temporalMode` / `asOf` to read the\n * endpoint's edges at another temporal coordinate.\n */\n findFrom: (\n from: NodeRef<From>,\n options?: QueryOptions,\n ) => Promise<Edge<E, From, To>[]>;\n\n /**\n * Find edges to a specific node.\n *\n * Temporal semantics mirror {@link EdgeCollection.findFrom}.\n */\n findTo: (\n to: NodeRef<To>,\n options?: QueryOptions,\n ) => Promise<Edge<E, From, To>[]>;\n\n /**\n * Find the edges from a SET of source nodes in one read.\n *\n * `findFrom` with a widened endpoint predicate and nothing else: the same\n * temporal model, the same soft-delete filtering, and the same per-source\n * ordering — but `from_id IN (...)` instead of `from_id = ?`, so a page of\n * N sources costs one statement per distinct source kind and bind-budget\n * chunk rather than N singleton statements.\n *\n * Results are grouped per input: the outer array is parallel to `froms`\n * (index `i` holds the edges of `froms[i]`), and repeated inputs each get\n * their own copy of the same edge set. Empty input returns `[]`; a source\n * with no edges gets an empty array. Large inputs are split across\n * statements to respect the backend's bound-parameter budget, which is\n * invisible in the result.\n *\n * Requires a backend implementing `findEdgesByEndpointSet` — both bundled\n * Drizzle backends do. On one that does not, this **refuses** with a\n * `ConfigurationError` rather than looping `findFrom` per input: asking for\n * a bulk read is asking for set-oriented statements, and silently issuing N\n * singleton statements is the cost surprise the method exists to remove.\n *\n * @param froms - Source nodes to read the edges of\n * @param options - Temporal coordinate plus optional `limitPerInput`\n * @throws {ConfigurationError} when the backend cannot read an endpoint set\n */\n bulkFindFrom: (\n froms: readonly NodeRef<From>[],\n options?: EdgeBulkFindEndpointOptions,\n ) => Promise<readonly Edge<E, From, To>[][]>;\n\n /**\n * Find the edges into a SET of target nodes in one read.\n *\n * Mirrors {@link EdgeCollection.bulkFindFrom} on the `to` endpoint.\n */\n bulkFindTo: (\n tos: readonly NodeRef<To>[],\n options?: EdgeBulkFindEndpointOptions,\n ) => Promise<readonly Edge<E, From, To>[][]>;\n\n /**\n * Deferred variant of `findFrom` for use with `store.batch()`.\n *\n * Returns a `BatchableQuery` instead of executing immediately. Accepts\n * the same temporal `options` as {@link EdgeCollection.findFrom}.\n *\n * Batching these still issues one statement per call — it does not merge\n * the reads, and whether they share a connection is up to the adapter. It\n * is not a snapshot: PostgreSQL's default read-committed isolation lets a\n * later read observe a commit the earlier ones did not. To read edges for\n * many sources in one statement, traverse from them in a single query.\n */\n batchFindFrom: (\n from: NodeRef<From>,\n options?: QueryOptions,\n ) => BatchableQuery<Edge<E, From, To>>;\n\n /**\n * Deferred variant of `findTo` for use with `store.batch()`.\n *\n * Returns a `BatchableQuery` instead of executing immediately. Accepts\n * the same temporal `options` as {@link EdgeCollection.findTo}. Costs one\n * statement per call, like {@link EdgeCollection.batchFindFrom}.\n */\n batchFindTo: (\n to: NodeRef<To>,\n options?: QueryOptions,\n ) => BatchableQuery<Edge<E, From, To>>;\n\n /**\n * Deferred variant of `findByEndpoints` for use with `store.batch()`.\n *\n * Returns a `BatchableQuery` that yields a 0-or-1 element array\n * (matching `findByEndpoints`' at-most-one semantics). Costs one statement\n * per call, like {@link EdgeCollection.batchFindFrom}.\n */\n batchFindByEndpoints: (\n from: NodeRef<From>,\n to: NodeRef<To>,\n options?: EdgeFindByEndpointsOptions<E>,\n temporal?: QueryOptions,\n ) => BatchableQuery<Edge<E, From, To>>;\n\n /** Delete an edge (soft delete - sets deletedAt timestamp) */\n delete: (id: EdgeId<E>) => Promise<void>;\n\n /**\n * Permanently delete an edge from the database.\n *\n * Unlike `delete()` which performs a soft delete, this permanently\n * removes the edge record.\n *\n * **Warning:** This operation is irreversible and should be used carefully.\n * Consider using soft delete (`delete()`) for most use cases.\n */\n hardDelete: (id: EdgeId<E>) => Promise<void>;\n\n /** Find edges matching endpoint and pagination criteria */\n find: (\n filter?: Readonly<{\n from?: NodeRef<From>;\n to?: NodeRef<To>;\n limit?: number;\n offset?: number;\n }>,\n temporal?: QueryOptions,\n ) => Promise<Edge<E, From, To>[]>;\n\n /** Count edges matching criteria */\n count: (\n filter?: Readonly<{\n from?: NodeRef<From>;\n to?: NodeRef<To>;\n }>,\n temporal?: QueryOptions,\n ) => Promise<number>;\n\n /**\n * Create multiple edges in a batch.\n *\n * More efficient than calling create() multiple times.\n * Use `bulkInsert` for the dedicated fast path that skips returning results.\n *\n * `validFrom` defaults to the operation's creation timestamp when omitted —\n * unless a stated `validTo` at or before that instant would leave the row\n * readable at no coordinate, in which case the row is stored with NO lower\n * bound (\"ended at T, start unknown\") and `meta.validFrom` reads back as\n * `undefined`. A future `validTo` is unaffected.\n */\n bulkCreate: (\n items: readonly Readonly<{\n from: NodeRef<From>;\n to: NodeRef<To>;\n props?: z.input<E[\"schema\"]>;\n id?: string;\n validFrom?: string;\n validTo?: string;\n }>[],\n ) => Promise<Edge<E, From, To>[]>;\n\n /**\n * Create or update multiple edges in a batch.\n *\n * For each item, if an edge with the given ID exists, updates it.\n * Otherwise, creates a new edge with that ID.\n *\n * Items are applied in order, so a **repeated id** within one batch is\n * last-write-wins: the first item creates or updates the edge, and every later\n * copy is an update over the value the earlier item wrote. This holds whether\n * or not the edge existed before the batch. An update never repoints an edge,\n * so every item's `from` / `to` must exactly restate the endpoints already\n * owned by that id (including an earlier item in the same batch). A mismatch\n * is refused with `ValidationError` carrying\n * `EDGE_IDENTITY_MISMATCH_CODE`; it is never silently ignored.\n *\n * `validFrom` applies when the upsert CREATES the edge and when it RESURRECTS\n * a tombstoned one. A create writes a fresh validity window, defaulting to the\n * operation's timestamp when omitted — unless a stated `validTo` at or before\n * that instant would leave the row readable at no coordinate, in which case NO\n * lower bound is stored (\"ended at T, start unknown\") and `meta.validFrom`\n * reads back as `undefined`. A resurrection stamps nothing: an edge RETAINS\n * its stored lower bound unless the item names a new one, so a `validTo`\n * before the retained bound is REFUSED rather than stamped over. An update to\n * a LIVE row stores no lower bound, because that row's is already history, so\n * one naming a different instant is REFUSED (`ValidationError` carrying\n * `IMMUTABLE_VALIDITY_LOWER_BOUND_CODE`) rather than ignored. Restating the\n * bound the row already holds is accepted and changes nothing.\n *\n * **Limitation — a batch cannot hand a constrained slot from one edge to\n * another.** Item order settles the final props, but the writes are grouped:\n * every create runs before every update. So a batch where one item frees the\n * slot a `cardinality` constraint allows and a later item claims it — ending\n * the lone `oneActive` edge from a source while creating its replacement —\n * checks the create while the old edge is still active, and the whole batch\n * fails with a `CardinalityError`. Applying the update first, as\n * separate `update` / `create` calls, succeeds. A batch states the set of\n * edges it wants, not a script to reach them by; the failure is loud rather\n * than silent, and the workaround is to split the handoff across two batches\n * (free the slot, then claim it) or to apply those items individually.\n */\n bulkUpsertById: (\n items: readonly (Readonly<{\n id: EdgeId<E>;\n from: NodeRef<From>;\n to: NodeRef<To>;\n props?: z.input<E[\"schema\"]>;\n validFrom?: string;\n }> &\n ValidityEndMutation)[],\n ) => Promise<Edge<E, From, To>[]>;\n\n /**\n * Insert multiple edges without returning results.\n *\n * This is the dedicated fast path for bulk inserts. Unlike `bulkCreate`\n * with `returnResults: false`, the intent is unambiguous: no results\n * are returned and the operation is wrapped in a transaction.\n *\n * `validFrom` defaults to the operation's creation timestamp when omitted —\n * unless a stated `validTo` at or before that instant would leave the row\n * readable at no coordinate, in which case the row is stored with NO lower\n * bound (\"ended at T, start unknown\") and `meta.validFrom` reads back as\n * `undefined`. A future `validTo` is unaffected.\n */\n bulkInsert: (\n items: readonly Readonly<{\n from: NodeRef<From>;\n to: NodeRef<To>;\n props?: z.input<E[\"schema\"]>;\n id?: string;\n validFrom?: string;\n validTo?: string;\n }>[],\n ) => Promise<void>;\n\n /**\n * Delete multiple edges by ID.\n *\n * Atomic when the backend supports transactions. Silently ignores IDs\n * that don't exist.\n *\n * The batch runs as one transaction, checking each id's kind against the\n * authoritative row as it goes. An id owned by another edge kind is\n * refused with `ValidationError` carrying `EDGE_IDENTITY_MISMATCH_CODE`;\n * because the whole batch is one transaction, that refusal rolls back\n * every delete already applied for an earlier id in the same batch.\n */\n bulkDelete: (ids: readonly EdgeId<E>[]) => Promise<void>;\n\n /**\n * Find an edge by endpoints and optional property fields.\n *\n * Returns the first matching edge at the read coordinate, or undefined.\n * Honors the temporal model like `findFrom` / `findTo`: by default\n * (`current` mode) soft-deleted and out-of-window edges are excluded; under\n * `includeTombstones` a soft-deleted edge can be returned.\n *\n * @param from - Source node\n * @param to - Target node\n * @param options - Match criteria (matchOn fields and property values)\n * @param temporal - Temporal coordinate. With no `temporal`, the graph's\n * default `temporalMode` applies (so under the default `\"current\"` mode,\n * edges outside their validity window are excluded). Pass\n * `temporalMode` / `asOf` to read the edge as of another coordinate.\n */\n findByEndpoints: (\n from: NodeRef<From>,\n to: NodeRef<To>,\n options?: EdgeFindByEndpointsOptions<E>,\n temporal?: QueryOptions,\n ) => Promise<Edge<E, From, To> | undefined>;\n\n /**\n * Get an existing edge by endpoints and optional property fields, or create a new one.\n *\n * Matches edges of this kind between `(from, to)`. When `matchOn` specifies\n * property fields, only edges whose properties match on those fields are considered.\n * Soft-deleted matches are resurrected when cardinality allows.\n *\n * `validFrom` applies on the create and RESURRECT branches. A create defaults\n * it to the operation's creation timestamp when omitted — unless a stated\n * `validTo` at or before that instant would leave the row readable at no\n * coordinate, in which case no lower bound is stored (\"ended at T, start\n * unknown\"). A resurrection stamps nothing: an edge RETAINS its stored lower\n * bound unless the call names a new one, so a `validTo` before the retained\n * bound is REFUSED rather than stamped over. On the `ifExists: \"update\"`\n * branch a live edge's lower bound is history and cannot be stored, so a\n * `validFrom` naming a different instant is REFUSED (`ValidationError`\n * carrying `IMMUTABLE_VALIDITY_LOWER_BOUND_CODE`); restating the bound the\n * edge holds is accepted. A returned existing edge (`ifExists: \"return\"`)\n * writes nothing and judges nothing — the window options describe the row to\n * create if none is found. `validTo` applies on create, update, and\n * resurrection, but not when an existing edge is returned without a write.\n *\n * @param from - Source node\n * @param to - Target node\n * @param props - Full properties for create, or merge source for update\n * @param options - Match criteria and conflict resolution\n */\n getOrCreateByEndpoints: (\n from: NodeRef<From>,\n to: NodeRef<To>,\n props: z.input<E[\"schema\"]>,\n options?: EdgeGetOrCreateByEndpointsOptions<E>,\n ) => Promise<EdgeGetOrCreateByEndpointsResult<E, From, To>>;\n\n /**\n * Batch version of getOrCreateByEndpoints.\n *\n * Results are returned in the same order as the input items.\n * Atomic when the backend supports transactions.\n */\n bulkGetOrCreateByEndpoints: (\n items: readonly (Readonly<{\n from: NodeRef<From>;\n to: NodeRef<To>;\n props: z.input<E[\"schema\"]>;\n validFrom?: string;\n onImmutableLowerBound?: \"preserve\" | \"refuse\";\n }> &\n ValidityEndMutation)[],\n options?: Pick<\n EdgeGetOrCreateByEndpointsOptions<E>,\n \"matchOn\" | \"ifExists\"\n >,\n ) => Promise<EdgeGetOrCreateByEndpointsResult<E, From, To>[]>;\n}>;\n\n// ============================================================\n// Type Helpers\n// ============================================================\n\n/**\n * Extract uniqueness constraint names from a NodeRegistration.\n *\n * - Returns `never` when no uniqueness constraints are configured.\n * - Returns a literal union when names are inferred via `defineGraph` const params.\n * - Falls back to `string` when names are widened/dynamic.\n */\nexport type ConstraintNames<R extends NodeRegistration> =\n \"unique\" extends keyof R ?\n R[\"unique\"] extends readonly { readonly name: infer N }[] ?\n N & string\n : string\n : never;\n\n/**\n * Extract the union of 'from' node types from an EdgeRegistration.\n */\ntype EdgeFromTypes<R extends EdgeRegistration> =\n R[\"from\"] extends readonly (infer N)[] ? N : never;\n\n/**\n * Extract the union of 'to' node types from an EdgeRegistration.\n */\ntype EdgeToTypes<R extends EdgeRegistration> =\n R[\"to\"] extends readonly (infer N)[] ? N : never;\n\n/**\n * Create a type-safe EdgeCollection from an EdgeRegistration.\n * Extracts the edge type and from/to node types automatically.\n */\nexport type TypedEdgeCollection<R extends EdgeRegistration> = EdgeCollection<\n R[\"type\"],\n EdgeFromTypes<R> extends NodeType ? EdgeFromTypes<R> : NodeType,\n EdgeToTypes<R> extends NodeType ? EdgeToTypes<R> : NodeType\n>;\n\n// ============================================================\n// Transaction Types\n// ============================================================\n\n// ============================================================\n// Graph Collection Maps\n// ============================================================\n\n/** Mapped type of all node collections for a graph. */\nexport type GraphNodeCollections<G extends GraphDef> = {\n [K in keyof G[\"nodes\"] & string]-?: NodeCollection<\n G[\"nodes\"][K][\"type\"],\n ConstraintNames<G[\"nodes\"][K]>\n >;\n};\n\n/** Mapped type of all edge collections for a graph. */\nexport type GraphEdgeCollections<G extends GraphDef> = {\n [K in keyof G[\"edges\"] & string]-?: TypedEdgeCollection<G[\"edges\"][K]>;\n};\n\n// ============================================================\n// StoreView read-only collection surfaces\n// ============================================================\n\n/** Temporal-aware node reads — a {@link StoreView} pins these. */\nexport type NodeTemporalReads<\n N extends NodeType,\n CN extends string = string,\n> = Pick<NodeCollection<N, CN>, (typeof NODE_TEMPORAL_READ_NAMES)[number]>;\n\n/**\n * Current-state-only node reads (constraint / index lookups). No temporal\n * axis, so a {@link StoreView} delegates them on a `current` pin and refuses\n * them on a temporal pin.\n */\nexport type NodeCurrentReads<\n N extends NodeType,\n CN extends string = string,\n> = Pick<NodeCollection<N, CN>, (typeof CURRENT_ONLY_READ_NAMES)[number]>;\n\n/** Node writes — never available on a read-only {@link StoreView}. */\nexport type NodeWrites<N extends NodeType, CN extends string = string> = Pick<\n NodeCollection<N, CN>,\n (typeof NODE_WRITE_NAMES)[number]\n>;\n\n/** Temporal-aware edge reads — a {@link StoreView} pins these. */\nexport type EdgeTemporalReads<\n E extends AnyEdgeType,\n From extends NodeType = NodeType,\n To extends NodeType = NodeType,\n> = Pick<\n EdgeCollection<E, From, To>,\n (typeof EDGE_TEMPORAL_READ_NAMES)[number]\n>;\n\n/**\n * Deferred edge reads for `store.batch()` — absent from a {@link StoreView},\n * which has no batch context.\n */\nexport type EdgeBatchReads<\n E extends AnyEdgeType,\n From extends NodeType = NodeType,\n To extends NodeType = NodeType,\n> = Pick<EdgeCollection<E, From, To>, (typeof EDGE_BATCH_READ_NAMES)[number]>;\n\n/** Edge writes — never available on a read-only {@link StoreView}. */\nexport type EdgeWrites<\n E extends AnyEdgeType,\n From extends NodeType = NodeType,\n To extends NodeType = NodeType,\n> = Pick<EdgeCollection<E, From, To>, (typeof EDGE_WRITE_NAMES)[number]>;\n\n/**\n * The read-only node surface a {@link StoreView} exposes for one node kind.\n * The temporal reads drop the per-call temporal argument — the pin owns the\n * axis; the current reads ({@link NodeCurrentReads}) are exposed as-is\n * (delegated on a `current` view, refused on a temporal pin). Writes live on\n * the live `Store`. The conformance test asserts the temporal part equals the\n * pinned form of {@link NodeTemporalReads}.\n */\nexport type StoreViewNodeCollection<\n N extends NodeType,\n CN extends string = string,\n> = Readonly<{\n /** Get a node by ID at the view's pinned coordinate. */\n getById: (id: NodeId<N>) => Promise<Node<N> | undefined>;\n\n /** Get multiple nodes by ID, preserving input order (undefined for missing). */\n getByIds: (\n ids: readonly NodeId<N>[],\n ) => Promise<readonly (Node<N> | undefined)[]>;\n\n /** Find nodes matching criteria at the view's pinned coordinate. */\n find: (\n filter?: Readonly<{\n where?: (accessor: NodeAccessor<N>) => Predicate;\n limit?: number;\n offset?: number;\n }>,\n ) => Promise<Node<N>[]>;\n\n /** Count nodes at the view's pinned coordinate. */\n count: () => Promise<number>;\n}> &\n NodeCurrentReads<N, CN>;\n\n/**\n * The read-only edge surface a {@link StoreView} exposes for one edge\n * kind, mirroring {@link StoreViewNodeCollection}. Every edge read —\n * including `findByEndpoints` — honors the pin via the same temporal model,\n * so (unlike nodes) there are no current-state-only edge reads.\n */\nexport type StoreViewEdgeCollection<\n E extends AnyEdgeType,\n From extends NodeType = NodeType,\n To extends NodeType = NodeType,\n> = Readonly<{\n /** Get an edge by ID at the view's pinned coordinate. */\n getById: (id: EdgeId<E>) => Promise<Edge<E, From, To> | undefined>;\n\n /** Get multiple edges by ID, preserving input order (undefined for missing). */\n getByIds: (\n ids: readonly EdgeId<E>[],\n ) => Promise<readonly (Edge<E, From, To> | undefined)[]>;\n\n /** Find edges matching endpoint and pagination criteria. */\n find: (\n filter?: Readonly<{\n from?: NodeRef<From>;\n to?: NodeRef<To>;\n limit?: number;\n offset?: number;\n }>,\n ) => Promise<Edge<E, From, To>[]>;\n\n /** Count edges matching criteria at the view's pinned coordinate. */\n count: (\n filter?: Readonly<{ from?: NodeRef<From>; to?: NodeRef<To> }>,\n ) => Promise<number>;\n\n /** Find edges from a specific node at the view's pinned coordinate. */\n findFrom: (from: NodeRef<From>) => Promise<Edge<E, From, To>[]>;\n\n /** Find edges to a specific node at the view's pinned coordinate. */\n findTo: (to: NodeRef<To>) => Promise<Edge<E, From, To>[]>;\n\n /** Find the edges from a set of source nodes at the view's pinned coordinate. */\n bulkFindFrom: (\n froms: readonly NodeRef<From>[],\n options?: EdgeBulkFindOptions,\n ) => Promise<readonly Edge<E, From, To>[][]>;\n\n /** Find the edges into a set of target nodes at the view's pinned coordinate. */\n bulkFindTo: (\n tos: readonly NodeRef<To>[],\n options?: EdgeBulkFindOptions,\n ) => Promise<readonly Edge<E, From, To>[][]>;\n\n /** Find the edge between two endpoints at the view's pinned coordinate. */\n findByEndpoints: (\n from: NodeRef<From>,\n to: NodeRef<To>,\n options?: EdgeFindByEndpointsOptions<E>,\n ) => Promise<Edge<E, From, To> | undefined>;\n}>;\n\n/**\n * Read-only view edge collection derived from an `EdgeRegistration`,\n * extracting the edge type and from/to node types — the read-only\n * counterpart of {@link TypedEdgeCollection}.\n */\nexport type TypedStoreViewEdgeCollection<R extends EdgeRegistration> =\n StoreViewEdgeCollection<\n R[\"type\"],\n EdgeFromTypes<R> extends NodeType ? EdgeFromTypes<R> : NodeType,\n EdgeToTypes<R> extends NodeType ? EdgeToTypes<R> : NodeType\n >;\n\n/** Mapped type of all read-only view node collections for a graph. */\nexport type StoreViewNodeCollections<G extends GraphDef> = {\n [K in keyof G[\"nodes\"] & string]-?: StoreViewNodeCollection<\n G[\"nodes\"][K][\"type\"]\n >;\n};\n\n/** Mapped type of all read-only view edge collections for a graph. */\nexport type StoreViewEdgeCollections<G extends GraphDef> = {\n [K in keyof G[\"edges\"] & string]-?: TypedStoreViewEdgeCollection<\n G[\"edges\"][K]\n >;\n};\n\n/** Options for one bounded, forward-only recorded-time collection scan. */\nexport type RecordedScanOptions = Readonly<{\n /** Maximum entities to return. Defaults to 1,000 and cannot exceed 1,000. */\n limit?: number;\n /** Opaque cursor returned by the preceding page. */\n after?: string;\n}>;\n\n/** One page from a deterministic recorded-time collection scan. */\nexport type RecordedScanPage<T> = Readonly<{\n /** Entities ordered by canonical id ascending. */\n data: readonly T[];\n /** Cursor for the next page, or `undefined` when the scan is complete. */\n nextCursor: string | undefined;\n /** Whether another page exists after this one. */\n hasNextPage: boolean;\n}>;\n\n/** Recorded-time reconstructing reads for one node kind. */\nexport type RecordedStoreViewNodeCollection<N extends NodeType> = Pick<\n StoreViewNodeCollection<N>,\n (typeof RECORDED_POINT_READ_NAMES)[number]\n> &\n Readonly<{\n /** Scan one bounded page at the view's pinned coordinate. */\n scan: (options?: RecordedScanOptions) => Promise<RecordedScanPage<Node<N>>>;\n }>;\n\n/** Recorded-time reconstructing reads for one edge kind. */\nexport type RecordedStoreViewEdgeCollection<\n E extends AnyEdgeType,\n From extends NodeType = NodeType,\n To extends NodeType = NodeType,\n> = Pick<\n StoreViewEdgeCollection<E, From, To>,\n (typeof RECORDED_POINT_READ_NAMES)[number]\n> &\n Readonly<{\n /** Scan one bounded page at the view's pinned coordinate. */\n scan: (\n options?: RecordedScanOptions,\n ) => Promise<RecordedScanPage<Edge<E, From, To>>>;\n }>;\n\n/** Recorded-time edge collection derived from an `EdgeRegistration`. */\nexport type TypedRecordedStoreViewEdgeCollection<R extends EdgeRegistration> =\n RecordedStoreViewEdgeCollection<\n R[\"type\"],\n EdgeFromTypes<R> extends NodeType ? EdgeFromTypes<R> : NodeType,\n EdgeToTypes<R> extends NodeType ? EdgeToTypes<R> : NodeType\n >;\n\n/** Mapped type of all recorded-time node reconstructing-read collections. */\nexport type RecordedStoreViewNodeCollections<G extends GraphDef> = {\n [K in keyof G[\"nodes\"] & string]-?: RecordedStoreViewNodeCollection<\n G[\"nodes\"][K][\"type\"]\n >;\n};\n\n/** Mapped type of all recorded-time edge reconstructing-read collections. */\nexport type RecordedStoreViewEdgeCollections<G extends GraphDef> = {\n [K in keyof G[\"edges\"] & string]-?: TypedRecordedStoreViewEdgeCollection<\n G[\"edges\"][K]\n >;\n};\n\n// ============================================================\n// Transaction Context\n// ============================================================\n\n/**\n * Whether — and why — `tx.sql` can be used on a transaction context. A single\n * required discriminant covering exactly the four states raw-SQL access can be\n * in, so an adapter caller branches on capability instead of truthiness-testing\n * `tx.sql`. The non-available variants omit `sql` entirely, so even reading the\n * handle requires first narrowing `sqlAvailability` to `\"available\"`. The\n * runtime object keeps a fail-loud getter under history / revision tracking for\n * JavaScript and type-suppressed callers. See\n * {@link AdapterTransactionContext} for the per-value semantics.\n */\nexport type SqlAvailability =\n \"available\" | \"history\" | \"revisionTracking\" | \"unavailable\";\n\ntype AdapterTransactionSqlAccess<TNativeTransaction> =\n | Readonly<{\n sql: TNativeTransaction;\n sqlAvailability: \"available\";\n }>\n | Readonly<{\n sqlAvailability: \"history\" | \"revisionTracking\";\n }>\n | Readonly<{\n sqlAvailability: \"unavailable\";\n }>;\n\nexport const TRANSACTION_RUNTIME: unique symbol = typeGraphGlobalSymbol(\n \"transaction-runtime-v1\",\n);\n\ntype TransactionRuntime = Readonly<{\n backend: TransactionBackend;\n runNodeOperationHooks: <T>(\n operation: \"create\" | \"update\" | \"delete\",\n kind: string,\n id: string,\n fn: () => Promise<T>,\n ) => Promise<T>;\n}>;\n\n/**\n * The portable transaction context mirrors the Store's typed graph operations\n * while exposing only a read-only backend projection bound to the transaction.\n * Adapter-native handles are available only through\n * {@link AdapterTransactionContext}; arbitrary backend writes are absent from\n * every public transaction context. TypeGraph's non-enumerable symbol port is\n * an unsupported implementation detail, not a JavaScript security boundary:\n * reflective code can still discover symbol properties.\n *\n * @example\n * ```typescript\n * await store.transaction(async (tx) => {\n * const person = await tx.nodes.Person.create({ name: \"Alice\" });\n * const company = await tx.nodes.Company.create({ name: \"Acme\" });\n * await tx.edges.worksAt.create(person, company, { role: \"Engineer\" });\n * });\n * ```\n */\ntype TransactionCollections<G extends GraphDef> = Readonly<{\n [TRANSACTION_RUNTIME]: TransactionRuntime;\n nodes: GraphNodeCollections<G>;\n edges: GraphEdgeCollections<G>;\n /** Read-only backend projection bound to the same graph transaction. */\n backend: TransactionReadBackend;\n /**\n * Runtime string-keyed node collection access, mirroring\n * `Store.getNodeCollection`. Returns `undefined` when `kind` is not\n * registered in this graph.\n */\n getNodeCollection: <const K extends string>(\n kind: K,\n ) => DynamicNodeCollection<K> | undefined;\n}> &\n (G[\"identity\"] extends GraphIdentityConfig ?\n Readonly<{ identity: IdentityFacade<G> }>\n : Readonly<Record<never, never>>);\n\n/**\n * A portable transaction context containing only TypeGraph-owned graph\n * operations. Managed Stores use this surface so adapter-native handles never\n * enter their public contract.\n */\nexport type TransactionContext<G extends GraphDef> = TransactionCollections<G>;\n\n/**\n * A transaction context exposed by an {@link AdapterStore}. In addition to the\n * portable graph collections, it carries the adapter-native handle when that\n * capability is available. The TypeGraph backend remains the same runtime\n * read projection as the portable context; adapter-native writes intentionally\n * go through `sql`, making that escape hatch explicit.\n */\nexport type AdapterTransactionContext<\n G extends GraphDef,\n TNativeTransaction,\n> = TransactionCollections<G> & AdapterTransactionSqlAccess<TNativeTransaction>;\n\n/**\n * Scoped write measurement, available only on the receipt-enabled transaction\n * contexts (`transactionWithReceipt`, `withRecordedTransaction`). Runs `fn`,\n * passing it a **scoped context** — a second view over the same transaction — and\n * returns a {@link TransactionOutcome} whose receipt counts exactly the writes\n * made *through that scoped context* (`scoped.nodes` / `scoped.edges`).\n *\n * Attribution is by **which context you write through**, not by timing. A write\n * through the scoped context counts in both the scope and the outer receipt (it\n * happened in the transaction); a write through the outer `tx` during the scope\n * counts only in the outer receipt. This makes overlapping and concurrent\n * measures safe by construction — two scopes running under `Promise.all`, each\n * writing through its own scoped context, never cross-count. Nesting composes:\n * `scoped.measure(...)` opens a child scope that counts in itself, every\n * ancestor scope, and the outer receipt.\n *\n * Counts otherwise inherit {@link TransactionReceipt} (bulk by input length, a\n * rejected write counts 0). The returned receipt's `recorded` is **always\n * `undefined`**: the recorded commit instant is a per-transaction flush concern,\n * unknowable mid-transaction.\n */\nexport type ScopedMeasure<Context> = <T>(\n fn: (scoped: Context) => Promise<T>,\n) => Promise<TransactionOutcome<T>>;\n\n/**\n * A {@link TransactionContext} that also exposes {@link ScopedMeasure}. Only the\n * receipt-enabled entry points (`transactionWithReceipt`,\n * `withRecordedTransaction`) hand a callback this type; plain `transaction()`\n * contexts have no recorder and therefore no `measure`, keeping that path\n * zero-overhead. Assignable to {@link TransactionContext}, so a projector helper\n * typed `(tx: TransactionContext<G>) => ...` accepts a measurable context. The\n * scoped context handed to `measure` is itself measurable, so scopes nest.\n */\nexport type MeasurableTransactionContext<G extends GraphDef> =\n TransactionContext<G> &\n Readonly<{\n measure: ScopedMeasure<MeasurableTransactionContext<G>>;\n }>;\n\n/** Receipt-enabled transaction context for an {@link AdapterStore}. */\nexport type MeasurableAdapterTransactionContext<\n G extends GraphDef,\n TNativeTransaction,\n> = AdapterTransactionContext<G, TNativeTransaction> &\n Readonly<{\n measure: ScopedMeasure<\n MeasurableAdapterTransactionContext<G, TNativeTransaction>\n >;\n }>;\n\n// ============================================================\n// Dynamic Collection Types (widened for runtime dispatch)\n// ============================================================\n\n/**\n * A node returned by a runtime string-keyed collection.\n *\n * Its nominal node-type brand proves the value passed through the dynamic\n * collection API while its properties remain runtime-schema-shaped.\n */\nexport type DynamicNode<K extends string = string> = Node<DynamicNodeType<K>>;\n\ndeclare const DYNAMIC_NODE_REFERENCE_BRAND: unique symbol;\n\n/** A nominal lightweight reference returned by runtime-aware identity reads. */\nexport type DynamicNodeReference<K extends string = string> = Readonly<{\n kind: DynamicNodeKind<K>;\n id: NodeId<DynamicNodeType<K>>;\n [DYNAMIC_NODE_REFERENCE_BRAND]: true;\n}>;\n\n/**\n * Replace branded `NodeId` / `EdgeId` with plain `string` in each\n * method's parameter list. Return types are preserved unchanged.\n *\n * Handles three shapes:\n * 1. Direct branded ID parameter → `string`\n * 2. `readonly NodeId[]` / `readonly EdgeId[]` → `readonly string[]`\n * 3. Branded IDs nested one level inside bulk-item object arrays\n */\ntype WidenBrandedIds<T> = {\n readonly [K in keyof T]: T[K] extends (...args: infer A) => infer R ?\n (...args: { [P in keyof A]: UnbrandParam<A[P]> }) => R\n : T[K];\n};\n\n/** Replace a branded ID with `string`, recursing one level into arrays. */\ntype UnbrandParam<T> =\n T extends NodeId<NodeType> ? string\n : T extends EdgeId<AnyEdgeType> ? string\n : T extends readonly NodeId<NodeType>[] ? readonly string[]\n : T extends readonly EdgeId<AnyEdgeType>[] ? readonly string[]\n : T extends readonly (infer Item extends Record<string, unknown>)[] ?\n readonly UnbrandRecord<Item>[]\n : T;\n\n/** Replace branded ID values in object properties (does not recurse into nested structures). */\ntype UnbrandRecord<T extends Record<string, unknown>> = {\n readonly [K in keyof T]: T[K] extends NodeId<NodeType> ? string\n : T[K] extends EdgeId<AnyEdgeType> ? string\n : T[K];\n};\n\n/**\n * A node collection with widened generics for runtime string-keyed access.\n *\n * This is the return type of `store.getNodeCollection(kind)`. It exposes\n * the full `NodeCollection` API but with a nominal `DynamicNodeType` and\n * `string` constraint names instead of the specific generic parameters, since\n * the concrete type is not known at compile time.\n *\n * ID parameters accept plain `string` instead of branded `NodeId<N>`, since\n * the dynamic path typically receives IDs from edge metadata, snapshots,\n * or external input where the brand is not available.\n */\nexport type DynamicNodeCollection<K extends string = string> = WidenBrandedIds<\n NodeCollection<DynamicNodeType<K>, string>\n>;\n\n/**\n * An edge collection with widened generics for runtime string-keyed access.\n *\n * This is the return type of `store.getEdgeCollection(kind)`. It exposes\n * the full `EdgeCollection` API but with `NodeType` endpoint types, since\n * the concrete from/to types are not known at compile time.\n *\n * ID parameters accept plain `string` instead of branded `EdgeId<E>`, since\n * the dynamic path typically receives IDs from edge metadata, snapshots,\n * or external input where the brand is not available.\n */\nexport type DynamicEdgeCollection = WidenBrandedIds<\n EdgeCollection<AnyEdgeType, NodeType, NodeType>\n>;\n\n// ============================================================\n// Store Projection\n// ============================================================\n\n/**\n * A type-level projection of a store's surface onto a subset of its\n * node and edge collections.\n *\n * Node collections are projected with constraint names erased (`never`),\n * so constraint-based methods like `findByConstraint` become uncallable.\n * This is intentional: unique constraints are graph-registration-level\n * details that differ between graphs sharing the same node types.\n *\n * @example\n * ```typescript\n * type CoreStore = StoreProjection<\n * typeof myGraph,\n * \"Document\" | \"Chunk\",\n * \"hasChunk\"\n * >;\n *\n * async function ingestChunk(\n * store: CoreStore,\n * document: Node<typeof Document>,\n * text: string,\n * ) {\n * const chunk = await store.nodes.Chunk.create({ text });\n * await store.edges.hasChunk.create(document, chunk);\n * return chunk;\n * }\n * ```\n *\n * Both `Store<G>` and `TransactionContext<G>` are structurally assignable\n * to a `StoreProjection` whose keys are a subset of `G`.\n */\nexport type StoreProjection<\n G extends GraphDef,\n N extends keyof G[\"nodes\"] & string = never,\n E extends keyof G[\"edges\"] & string = never,\n> = Readonly<{\n nodes: { [K in N]-?: NodeCollection<G[\"nodes\"][K][\"type\"], never> };\n edges: Pick<GraphEdgeCollections<G>, E>;\n}>;\n","import {\n type BackendIdentity,\n type GraphBackend,\n type GraphEntityReadBackend,\n type QueryExecutionBackend,\n type RawQueryExecutionBackend,\n type SchemaReadBackend,\n type SqlCompilationBackend,\n type TransactionBackend,\n} from \"../backend/types\";\nimport {\n type AllNodeTypes,\n type EdgeKinds,\n type GraphDef,\n type NodeKinds,\n} from \"../core/define-graph\";\nimport { type ReadCoordinate } from \"../core/temporal\";\nimport {\n type AnyEdgeType,\n type EdgeId,\n type NodeId,\n type NodeType,\n} from \"../core/types\";\nimport { type IdentityReadFacade } from \"../identity/types\";\nimport { type InitialQueryBuilder } from \"../query/builder\";\nimport { typeGraphGlobalSymbol } from \"../utils/global-symbol\";\nimport { type InternalGraphAlgorithms } from \"./algorithms\";\nimport {\n type InternalSubgraphOptions,\n type SubgraphProject,\n type SubgraphResult,\n} from \"./subgraph\";\nimport {\n type Edge,\n type Node,\n type RecordedScanOptions,\n type RecordedScanPage,\n TRANSACTION_RUNTIME,\n} from \"./types\";\n\nexport const STORE_RUNTIME: unique symbol =\n typeGraphGlobalSymbol(\"store-runtime-v1\");\n\n/**\n * @internal Operations used by Store-owned views. The port is absent from the\n * public Store contract and non-enumerable at runtime. JavaScript reflection\n * can still discover symbol properties, so this is an unsupported internal\n * surface rather than a security boundary.\n */\nexport type StoreRuntime<G extends GraphDef> = Readonly<{\n backend: GraphBackend;\n /**\n * @internal The backend this Store's queries actually execute through for\n * `target` — the Store's own backend when `target` is omitted.\n *\n * It is the SAME private construction the query path uses rather than a\n * reconstruction of it, because the object it returns is the one a lost\n * derivation corrupts: the hooked query backend is a transient local inside\n * query construction that is stored nowhere, so no other handle on it exists\n * and a regression there is invisible to every assertion about\n * {@link StoreRuntime.backend}.\n *\n * NOTE for anything asserting on it: with no query hook configured this\n * returns its argument unchanged, so a hookless Store answers with the very\n * object it was handed and a comparison against that object is a tautology.\n */\n queryBackend: (target?: GraphBackend | TransactionBackend) => GraphBackend;\n sealedQuery: (coordinate: ReadCoordinate) => InitialQueryBuilder<G, \"sealed\">;\n recordedNodeGetById: <N extends NodeType>(\n kind: string,\n id: NodeId<N>,\n coordinate: ReadCoordinate,\n ) => Promise<Node<N> | undefined>;\n recordedNodeGetByIds: <N extends NodeType>(\n kind: string,\n ids: readonly NodeId<N>[],\n coordinate: ReadCoordinate,\n ) => Promise<readonly (Node<N> | undefined)[]>;\n recordedNodeScan: <N extends NodeType>(\n kind: string,\n coordinate: ReadCoordinate,\n options?: RecordedScanOptions,\n ) => Promise<RecordedScanPage<Node<N>>>;\n recordedEdgeGetById: <E extends AnyEdgeType>(\n kind: string,\n id: EdgeId<E>,\n coordinate: ReadCoordinate,\n ) => Promise<Edge<E> | undefined>;\n recordedEdgeGetByIds: <E extends AnyEdgeType>(\n kind: string,\n ids: readonly EdgeId<E>[],\n coordinate: ReadCoordinate,\n ) => Promise<readonly (Edge<E> | undefined)[]>;\n recordedEdgeScan: <E extends AnyEdgeType>(\n kind: string,\n coordinate: ReadCoordinate,\n options?: RecordedScanOptions,\n ) => Promise<RecordedScanPage<Edge<E>>>;\n subgraphAtCoordinate: <\n const EK extends EdgeKinds<G>,\n const NK extends NodeKinds<G> = NodeKinds<G>,\n const P extends SubgraphProject<G, NK, EK> | undefined = undefined,\n >(\n rootId: NodeId<AllNodeTypes<G>>,\n options: InternalSubgraphOptions<G, EK, NK, P>,\n ) => Promise<SubgraphResult<G, NK, EK, P>>;\n algorithmsAtCoordinate: (\n coordinate: ReadCoordinate,\n ) => InternalGraphAlgorithms<G>;\n identityAtCoordinate: (coordinate: ReadCoordinate) => IdentityReadFacade<G>;\n rebuildIdentityClosure: () => Promise<void>;\n validateIdentity: () => Promise<void>;\n /**\n * Validates one final resolved node write set, then clears the affected\n * nodes' claim rows so its upserts may take their approved keys in any order,\n * and re-takes the complete claim set once the writes have landed. The caller\n * must supply a transaction-bound backend.\n *\n * The clear is by OWNER, so it takes every claim the affected nodes hold —\n * uniqueness and `disjointWith` alike — and the rebuild therefore goes through\n * the same claim writer an ordinary create uses rather than a uniqueness-only\n * insert. See `store/claims/resolved-node-claims.ts`.\n */\n applyResolvedNodeUniqueness: <Output>(\n target: TransactionBackend,\n writes: Readonly<{\n upserts: readonly Readonly<{\n kind: string;\n id: string;\n props: Readonly<Record<string, unknown>>;\n }>[];\n releases: readonly Readonly<{ kind: string; id: string }>[];\n }>,\n apply: () => Promise<Output>,\n ) => Promise<Output>;\n /**\n * @internal Reads the graph's identity assertions in transfer shape, honoring\n * this store's SQL binding. Used by interchange export, base-version\n * fingerprinting, and merge staging/diff.\n */\n readCurrentIdentityAssertions: (\n mode: \"state\" | \"archival\",\n options?: Readonly<{\n nodeKinds?: readonly string[];\n includeDeleted?: boolean;\n }>,\n ) => Promise<\n readonly Readonly<{\n id: string;\n relation: \"same\" | \"different\";\n a: Readonly<{ kind: string; id: string }>;\n b: Readonly<{ kind: string; id: string }>;\n validFrom: string;\n validTo?: string | undefined;\n endedBy?: Readonly<{ kind: string; id: string }> | undefined;\n }>[]\n >;\n /**\n * Live nodes (registry kinds only) sharing any of the given bare ids —\n * the cross-kind peer set same-id folding would join. Used by graph-merge's\n * plan-time contradiction simulation to seed its node universe.\n */\n liveNodesSharingIds: (\n ids: readonly string[],\n target?: GraphBackend | TransactionBackend,\n ) => Promise<readonly Readonly<{ kind: string; id: string }>[]>;\n /**\n * Every stored assertion row (ended rows included) for the given assertion\n * ids — the rows the import coordinator's id-conflict check compares\n * against. Used by graph-merge to validate the one-id-one-truth invariant\n * at plan time and inside the commit transaction.\n */\n identityAssertionRowsByIds: (\n ids: readonly string[],\n target?: GraphBackend | TransactionBackend,\n ) => Promise<\n ReadonlyMap<\n string,\n Readonly<{\n id: string;\n relation: \"same\" | \"different\";\n a: Readonly<{ kind: string; id: string }>;\n b: Readonly<{ kind: string; id: string }>;\n validFrom: string;\n validTo?: string | undefined;\n endedBy?: Readonly<{ kind: string; id: string }> | undefined;\n }>\n >\n >;\n /**\n * The CURRENT structural identity class (materialized closure: folds plus\n * asserted links) of each reference, keyed by `refKey` — the\n * `JSON.stringify([kind, id])` serialization exported from\n * `identity/service`, which callers must use to probe the returned map. A\n * missing node coalesces to its singleton. Used by graph-merge's fold-peer\n * window guard to detect class-transitive drift in the plan→commit window.\n */\n structuralIdentityClasses: (\n references: readonly Readonly<{ kind: string; id: string }>[],\n target?: GraphBackend | TransactionBackend,\n ) => Promise<\n ReadonlyMap<string, readonly Readonly<{ kind: string; id: string }>[]>\n >;\n identityAssertionsAtTarget: (\n target: GraphBackend | TransactionBackend,\n mode?: \"state\" | \"archival\",\n ) => Promise<\n readonly Readonly<{\n id: string;\n relation: \"same\" | \"different\";\n a: Readonly<{ kind: string; id: string }>;\n b: Readonly<{ kind: string; id: string }>;\n validFrom: string;\n validTo?: string | undefined;\n endedBy?: Readonly<{ kind: string; id: string }> | undefined;\n }>[]\n >;\n readIdentityAssertionPageAtTarget: (\n target: GraphBackend | TransactionBackend,\n mode: \"state\" | \"archival\",\n options: Readonly<{\n nodeKinds?: readonly string[];\n includeDeleted?: boolean;\n after?: string;\n limit: number;\n }>,\n ) => Promise<\n Readonly<{\n assertions: readonly Readonly<{\n id: string;\n relation: \"same\" | \"different\";\n a: Readonly<{ kind: string; id: string }>;\n b: Readonly<{ kind: string; id: string }>;\n validFrom: string;\n validTo?: string | undefined;\n endedBy?: Readonly<{ kind: string; id: string }> | undefined;\n }>[];\n nextAfter?: string;\n done: boolean;\n }>\n >;\n lockIdentityImportTarget: (\n target: Readonly<\n BackendIdentity &\n GraphEntityReadBackend &\n SchemaReadBackend &\n QueryExecutionBackend &\n SqlCompilationBackend &\n RawQueryExecutionBackend &\n Pick<GraphBackend, \"executeStatement\">\n >,\n ) => Promise<void>;\n foldImportedIdentityNodes: (\n target: Readonly<\n BackendIdentity &\n GraphEntityReadBackend &\n SchemaReadBackend &\n QueryExecutionBackend &\n SqlCompilationBackend &\n RawQueryExecutionBackend &\n Pick<GraphBackend, \"executeStatement\">\n >,\n references: readonly Readonly<{ kind: string; id: string }>[],\n ) => Promise<void>;\n importIdentityAssertionsAtTarget: (\n target: Readonly<\n BackendIdentity &\n GraphEntityReadBackend &\n SchemaReadBackend &\n QueryExecutionBackend &\n SqlCompilationBackend &\n RawQueryExecutionBackend &\n Pick<GraphBackend, \"executeStatement\">\n >,\n assertions: readonly Readonly<{\n id: string;\n relation: \"same\" | \"different\";\n a: Readonly<{ kind: string; id: string }>;\n b: Readonly<{ kind: string; id: string }>;\n validFrom: string;\n validTo?: string | undefined;\n endedBy?: Readonly<{ kind: string; id: string }> | undefined;\n }>[],\n mode: \"state\" | \"archival\",\n ) => Promise<Readonly<{ created: number; skipped: number }>>;\n applyIdentityMergeAtTarget: (\n target: GraphBackend | TransactionBackend,\n retractions: readonly Readonly<{\n id: string;\n relation: \"same\" | \"different\";\n a: Readonly<{ kind: string; id: string }>;\n b: Readonly<{ kind: string; id: string }>;\n validFrom: string;\n validTo?: string | undefined;\n endedBy?: Readonly<{ kind: string; id: string }> | undefined;\n }>[],\n assertions: readonly Readonly<{\n id: string;\n relation: \"same\" | \"different\";\n a: Readonly<{ kind: string; id: string }>;\n b: Readonly<{ kind: string; id: string }>;\n validFrom: string;\n validTo?: string | undefined;\n endedBy?: Readonly<{ kind: string; id: string }> | undefined;\n }>[],\n ) => Promise<Readonly<{ created: number; retracted: number }>>;\n /**\n * Proves the identity classes of `seeds` carry no contradiction in the state\n * the caller's transaction has just written — the post-write half of\n * graph-merge's identity correctness, scoped to the classes the merge\n * touched. A refusal aborts the caller's transaction; identity-disabled\n * graphs resolve immediately.\n */\n assertIdentityClassesConsistentAtTarget: (\n target: GraphBackend | TransactionBackend,\n seeds: readonly Readonly<{ kind: string; id: string }>[],\n ) => Promise<void>;\n}>;\n\nexport function storeRuntime<G extends GraphDef>(\n store: Readonly<{ [STORE_RUNTIME]?: StoreRuntime<G> }>,\n): StoreRuntime<G> {\n const runtime = store[STORE_RUNTIME];\n if (runtime === undefined) {\n throw new TypeError(\n \"Cannot access this Store's runtime port. The Store may come from an incompatible TypeGraph version.\",\n );\n }\n return runtime;\n}\n\nexport function storeBackend<G extends GraphDef>(\n store: Readonly<{ [STORE_RUNTIME]?: StoreRuntime<G> }>,\n): GraphBackend {\n return storeRuntime(store).backend;\n}\n\n/**\n * The backend a Store's queries execute through — see\n * {@link StoreRuntime.queryBackend}, including its note about hookless Stores.\n */\nexport function storeQueryBackend<G extends GraphDef>(\n store: Readonly<{ [STORE_RUNTIME]?: StoreRuntime<G> }>,\n target?: GraphBackend | TransactionBackend,\n): GraphBackend {\n return storeRuntime(store).queryBackend(target);\n}\n\ntype TransactionRuntimePort = Readonly<{\n [TRANSACTION_RUNTIME]?: Readonly<{\n backend: TransactionBackend;\n runNodeOperationHooks: TransactionNodeOperationHookRunner;\n }>;\n}>;\n\ntype TransactionNodeOperationHookRunner = <T>(\n operation: \"create\" | \"update\" | \"delete\",\n kind: string,\n id: string,\n fn: () => Promise<T>,\n) => Promise<T>;\n\n/** Returns the full backend for privileged transaction-bound internals. */\nexport function transactionBackend(\n transaction: TransactionRuntimePort,\n): TransactionBackend {\n const runtime = transaction[TRANSACTION_RUNTIME];\n if (runtime === undefined) {\n throw new TypeError(\n \"Cannot access this transaction's runtime port. The transaction may come from an incompatible TypeGraph version.\",\n );\n }\n return runtime.backend;\n}\n\n/** Returns the hook runner paired with a transaction's internal backend. */\nexport function transactionNodeOperationHookRunner(\n transaction: TransactionRuntimePort,\n): TransactionNodeOperationHookRunner {\n const runtime = transaction[TRANSACTION_RUNTIME];\n if (runtime === undefined) {\n throw new TypeError(\n \"Cannot access this transaction's runtime port. The transaction may come from an incompatible TypeGraph version.\",\n );\n }\n return runtime.runNodeOperationHooks;\n}\n","/**\n * What claims a node of a given kind owes, before its props are known.\n *\n * This is the DECLARATION-level half of the claim set, and it is deliberately\n * separate from the props-level half: the same list answers \"which claim rows\n * does this kind write?\" (the entries, once a row's props select the applying\n * constraints and supply their keys) and \"does a write of this kind need the\n * per-graph lock?\" (the projection in `../constraints.ts`). Two consumers, one\n * classification — a second spelling at either of them is what this module\n * exists to prevent.\n *\n * It imports registry, core types and the axis vocabulary only, so the\n * constraint probes can project it without importing the claim seam.\n */\nimport { type UniqueConstraint } from \"../../core/types\";\nimport { type KindRegistry } from \"../../registry/kind-registry\";\nimport {\n DISJOINT_CONSTRAINT_NAME,\n disjointnessClaimAxis,\n uniquenessClaimTarget,\n} from \"./axis\";\nimport { type ConstraintFenceReason } from \"./backing\";\n\n/**\n * WHEN a claim is issued relative to the row write it gates.\n *\n * `\"pre-insert\"` means the claim is the ONLY fence for a violation the row\n * write itself would not surface, so it must precede the row — and a failure\n * between the two leaves a live claim with no row, which is why it is also the\n * subject of the non-transactional refusal. `\"post-insert\"` means the claim\n * follows the row, which is the order those entries ship in today and the order\n * their own primary key already makes sufficient.\n */\nexport type ClaimPlacement = \"pre-insert\" | \"post-insert\";\n\n/**\n * WHICH declared refusal a foreign owner of this claim produces, and the\n * payload that refusal needs.\n *\n * Two constraint families share one claim relation, so the row alone cannot say\n * what refusing it means. Carrying the answer on the site — decided where the\n * site is decided — is what lets the claim seam re-raise the family's own error\n * without re-deriving which family it was from the axis string.\n */\nexport type ClaimRefusal =\n | Readonly<{ kind: \"uniqueness\"; constraint: UniqueConstraint }>\n | Readonly<{ kind: \"disjointness\"; ownKind: string; otherKind: string }>;\n\n/** One claim a node of this kind owes, before its props are known. */\nexport type NodeClaimSite = Readonly<{\n /** The value the claim row's `node_kind` carries — what the PK fences on. */\n axis: string;\n /** The claim row's `constraint_name`. */\n constraintName: string;\n /**\n * Whether this site is ALSO a reason to take the per-graph write lock — i.e.\n * whether its probe reads state the uniques primary key does not already\n * fence. True for a scope spanning more than the node's own kind; false for a\n * single-kind scope, whose own key IS the fence.\n */\n needsLockFence: boolean;\n /**\n * WHEN this site's claim must be issued relative to the row write it gates,\n * for the operation this list was built for. It is DATA, not a caller's\n * choice: the claim seam partitions on it, and the non-transactional refusal\n * asks whether a write owes a `pre-insert` entry.\n */\n placement: ClaimPlacement;\n /**\n * The class a REFUSAL names when this site's claim cannot be undone. A scope\n * spanning more than the node's own kind is `nodeUniquenessScope` — the class\n * the lock path already reports for it; a single-kind scope is\n * `nodeUniquenessClaim`, whose advice is about the reservation row rather\n * than about the scope it does not have.\n */\n refusalReason: ConstraintFenceReason;\n /** Which typed error a foreign owner of this claim produces. */\n refusal: ClaimRefusal;\n}>;\n\n/**\n * The disjointness sites a write BRINGING A NODE INTO EXISTENCE under this kind\n * owes: one per declared partner.\n *\n * `\"create\"` and `\"resurrect\"` only, matching the two places a node comes into\n * existence under a kind — never `\"update\"`, because an in-place update cannot\n * change a node's kind and so re-derives no cross-kind verdict and owes no\n * claim for one. A resurrect owes exactly what a create owes here: reviving a\n * tombstone re-introduces the same live id under the same kind that a fresh\n * insert would, and {@link file://./node-claims.ts deleteUniquenessEntries}\n * released this node's own disjointness reservations at soft-delete time (it\n * reads the `\"create\"` extent precisely because it is the wider of the two), so\n * a resurrect starts from the same \"holds nothing yet\" state a create does and\n * must re-claim from scratch rather than diff.\n *\n * Pairwise rather than per component, because the registry's disjoint pairs are\n * literal unordered pairs: a node disjoint from two partners owes two claims,\n * on two axes, and the two partners are not thereby disjoint from each other.\n *\n * Every one of them is `pre-insert` and `needsLockFence`: the nodes primary key\n * is `(graph_id, kind, id)`, so a disjoint namesake's row does not collide with\n * this node's own insert — the claim is the ONLY fence for the axis, and a\n * fence issued after the write it fences is not a fence. `needsLockFence` is\n * read only by the lock projection (`nodeWriteNeedsConstraintFence`, consulted\n * with `\"create\"` or `\"update\"` alone — never `\"resurrect\"`), so it stays inert\n * for a resurrect: the per-graph lock's trigger set is unchanged by this site\n * applying to a third operation, and the claim's own primary key — the same\n * `INSERT … ON CONFLICT … RETURNING` `insertUnique` already uses for an\n * own-kind uniqueness claim, which needs no lock either — is what fences a\n * resurrect racing a disjoint partner's create.\n */\nfunction disjointnessSites(\n registry: KindRegistry,\n kind: string,\n): readonly NodeClaimSite[] {\n return registry.getDisjointKinds(kind).map((otherKind) => ({\n axis: disjointnessClaimAxis(kind, otherKind, registry),\n constraintName: DISJOINT_CONSTRAINT_NAME,\n needsLockFence: true,\n placement: \"pre-insert\" as const,\n refusalReason: \"nodeDisjointness\" as const,\n refusal: { kind: \"disjointness\" as const, ownKind: kind, otherKind },\n }));\n}\n\n/**\n * The three shapes a node write can take, for claim purposes.\n *\n * `\"create\"` is a fresh id under a kind that has never held it live.\n * `\"update\"` is an in-place change to a row that stays live throughout — it\n * cannot change the node's kind, so it never owes a disjointness claim.\n * `\"resurrect\"` is a tombstoned row coming back to life under its own kind —\n * the SAME cross-kind event a create is, reached through the transition seam\n * instead of the insert seam, which is why its uniqueness claims are placed\n * like an update's (claim-first, {@link file://./node-claims.ts\n * withNodeClaimTransition} is the only correct sequence for a transition) while\n * its disjointness claims are owed like a create's.\n *\n * Two consumers read this vocabulary and each other's classification would be\n * the wrong one to reuse: {@link nodeClaimEntries} (what a ROW owes, given its\n * operation) reaches `\"resurrect\"`; `nodeWriteNeedsConstraintFence` (the lock\n * projection, `../constraints.ts`) and its callers reach only `\"create\"` and\n * `\"update\"`, by design — see {@link disjointnessSites}'s note on why that\n * keeps the per-graph lock's trigger set unchanged.\n */\nexport type NodeClaimOperation = \"create\" | \"update\" | \"resurrect\";\n\n/**\n * THE owner of \"what claims does a node of this kind owe, and when is each\n * due?\".\n *\n * Two families, one list. Disjointness sites come first, so a kind qualifying\n * on both counts reports the class the lock path already reports for it, and so\n * the two families cannot be maintained by two different sets of write paths: a\n * path that remembers uniqueness cannot forget disjointness when both arrive\n * through the same list.\n *\n * Every uniqueness field but `constraintName` is derived from ONE computation —\n * the {@link uniquenessClaimTarget} of this kind and scope — so the four readers\n * of this list (the entries, the claim seam's partition, the non-transactional\n * refusal, and the lock projection in `../constraints.ts`) read one\n * classification rather than four predicates that agree until one is edited.\n * The fact all of them turn on is the same one: does this claim's axis span\n * kinds beyond the writer's own? Disjointness always does; a uniqueness scope\n * does exactly when it covers more than the node's own kind.\n *\n * - `needsLockFence` reads it as \"the probe reads state the uniques primary key\n * does not fence\", which is what the per-graph lock is taken for;\n * - `placement` reads it as \"the claim is the only fence for this axis, so it\n * must precede the row it gates\". On the CREATE path an own-kind claim keeps\n * its shipped position after the row: the uniques primary key at that axis is\n * already the complete fence for it, so moving it would buy no fence and cost\n * a refusal on backends with no transactions. On the UPDATE and RESURRECT\n * paths every claim is pre-insert, because the transition seam claims before\n * its gated write for every scope — see `withNodeClaimTransition`, which\n * explains why that is the only correct sequence for a transition.\n *\n * A future claim family that breaks the coincidence between those two readings\n * must say WHICH it changes; that is why they are two named fields and not one.\n */\nexport function nodeClaimSites(\n registry: KindRegistry,\n kind: string,\n uniqueConstraints: readonly UniqueConstraint[],\n operation: NodeClaimOperation,\n): readonly NodeClaimSite[] {\n const uniqueness = uniqueConstraints.map((constraint) => {\n const target = uniquenessClaimTarget(kind, constraint.scope, registry);\n return {\n axis: target.axis,\n constraintName: constraint.name,\n needsLockFence: target.crossKind,\n placement:\n operation !== \"create\" || target.crossKind ?\n \"pre-insert\"\n : (\"post-insert\" as const),\n refusalReason:\n target.crossKind ?\n (\"nodeUniquenessScope\" as const)\n : (\"nodeUniquenessClaim\" as const),\n refusal: { kind: \"uniqueness\" as const, constraint },\n } satisfies NodeClaimSite;\n });\n\n return operation === \"update\" ? uniqueness : (\n [...disjointnessSites(registry, kind), ...uniqueness]\n );\n}\n","/**\n * Node claims — what a node row reserves, and how each reservation moves.\n *\n * A declared constraint is a CLAIM on an axis (see {@link file://./axis.ts}):\n * the write reserves the row `(graph_id, axis, constraint_name, key)` and is\n * refused when that reservation comes back owned by somebody else. This module\n * decides which claims a row owes, probes them, writes them, releases them, and\n * sequences each of those against the primary row write it gates.\n *\n * Two families reserve in that one relation — a uniqueness constraint at its\n * scope's axis, and a `disjointWith` pair at the pair's axis with the node's id\n * as the key — so every path that already maintained uniqueness reservations\n * maintains disjointness reservations too, by reading one list rather than by\n * being edited twice.\n */\nimport {\n type GraphBackend,\n type InsertUniqueParams,\n type TransactionBackend,\n type UniqueConstraintBackend,\n} from \"../../backend/types\";\nimport { checkWherePredicate, computeUniqueKey } from \"../../constraints\";\nimport { type UniqueConstraint } from \"../../core/types\";\nimport {\n type ConfigurationError,\n DisjointError,\n UniquenessError,\n} from \"../../errors\";\nimport { type KindRegistry } from \"../../registry/kind-registry\";\nimport { requireDefined } from \"../../utils/presence\";\nimport { constraintFenceRefusal } from \"../operations/write-transaction\";\nimport { type GraphWriteLock } from \"../recorded-capture/clock\";\nimport {\n type ClaimOwner,\n type ClaimTarget,\n compareClaimTargets,\n isSameClaimOwner,\n uniquenessProbeKinds,\n} from \"./axis\";\nimport { type ConstraintFenceReason } from \"./backing\";\nimport {\n type ClaimPlacement,\n type ClaimRefusal,\n type NodeClaimOperation,\n nodeClaimSites,\n} from \"./sites\";\n\n/**\n * Context for node claim operations.\n */\nexport type UniquenessContext = Readonly<{\n graphId: string;\n registry: KindRegistry;\n backend: GraphBackend | TransactionBackend;\n}>;\n\n/**\n * Context for the claim PROBE, whose single backend member is the read.\n *\n * Stated separately because the probe runs on handles that cannot write: a\n * write frame's row work holds the read-only `WriteTarget`, and pre-checking a\n * key there is the whole point of the probe. Every {@link UniquenessContext}\n * satisfies this one, so a writer passes its own context unchanged.\n */\nexport type UniquenessProbeContext = Readonly<{\n graphId: string;\n registry: KindRegistry;\n backend: Pick<UniqueConstraintBackend, \"checkUnique\">;\n}>;\n\n/**\n * Builds a claim context — the one constructor every call site shares.\n *\n * Generic in the handle so it yields exactly what it was given: a full backend\n * produces a {@link UniquenessContext} that can also write the claim rows, and\n * a read-only projection produces a {@link UniquenessProbeContext}, which is\n * all the probe needs and all row work can offer.\n */\nexport function createUniquenessContext<\n T extends Pick<UniqueConstraintBackend, \"checkUnique\">,\n>(\n graphId: string,\n registry: KindRegistry,\n backend: T,\n): Readonly<{ graphId: string; registry: KindRegistry; backend: T }> {\n return { graphId, registry, backend };\n}\n\n/** One claim a node row owes, decided but not written. */\nexport type NodeClaimEntry = Readonly<{\n /** `uniques.node_kind` — the axis the primary key fences on. */\n axis: string;\n /** `uniques.constraint_name`. */\n constraintName: string;\n /** `uniques.key`. */\n key: string;\n /**\n * WHEN this claim is issued relative to the row write it gates — carried\n * through from the entry's {@link NodeClaimSite}, which is the one owner of\n * the decision. The claim seam issues the two groups on either side of its\n * gated write, and {@link claimFenceRefusal} is keyed on the pre-insert group\n * alone.\n */\n placement: ClaimPlacement;\n /**\n * The class a refusal names when this claim cannot be rolled back — carried\n * through from the site, so no consumer re-derives it.\n */\n refusalReason: ConstraintFenceReason;\n /** Which typed error a foreign owner produces — carried through from the site. */\n refusal: ClaimRefusal;\n}>;\n\n/**\n * The key one site reserves for THIS row, or `undefined` when the site does not\n * apply to it.\n *\n * A uniqueness site applies when its `where` predicate holds, and its key is\n * the constraint's own composite key. A disjointness site always applies, and\n * its key is the node's ID — which is the axis `disjointWith` declares: \"no two\n * live nodes of these two kinds share an id\" is a reservation on the id, taken\n * at the pair.\n */\nfunction claimKeyFor(\n refusal: ClaimRefusal,\n id: string,\n props: Record<string, unknown>,\n): string | undefined {\n if (refusal.kind === \"disjointness\") return id;\n return checkWherePredicate(refusal.constraint, props) ?\n computeUniqueKey(\n props,\n refusal.constraint.fields,\n refusal.constraint.collation,\n )\n : undefined;\n}\n\n/**\n * THE single owner of \"what claims does THIS ROW owe, and when is each due?\":\n * the kind's claim sites ({@link nodeClaimSites}, the declaration-level extent\n * for this operation) filtered to the ones that apply to this row and completed\n * with each one's key. `placement`, `refusalReason` and `refusal` are carried\n * through unchanged; this function decides none of them.\n *\n * Every path that maintains a node's reservations — create, update diff,\n * resurrect, delete, batch, import — reads its work from this one list, so no\n * path can compute a key one way and an axis another, and no path that\n * remembers one family can forget the other.\n *\n * `operation` is threaded rather than assumed because the sites function needs\n * it twice: to place each claim (a transition claims before the row it gates for\n * every scope, while a create only does so for an axis spanning kinds beyond its\n * own) and to decide the disjointness arm, which only a write bringing a node\n * into existence under a kind owes — `\"create\"` and `\"resurrect\"` alike, never\n * `\"update\"`.\n */\nexport function nodeClaimEntries(\n registry: KindRegistry,\n kind: string,\n id: string,\n props: Record<string, unknown>,\n constraints: readonly UniqueConstraint[],\n operation: NodeClaimOperation,\n): readonly NodeClaimEntry[] {\n return nodeClaimSites(registry, kind, constraints, operation).flatMap(\n (site) => {\n const key = claimKeyFor(site.refusal, id, props);\n return key === undefined ?\n []\n : [\n {\n axis: site.axis,\n constraintName: site.constraintName,\n key,\n placement: site.placement,\n refusalReason: site.refusalReason,\n refusal: site.refusal,\n },\n ];\n },\n );\n}\n\n/** An entry whose family is disjointness — the one family that needs mapping. */\ntype DisjointnessClaimEntry = NodeClaimEntry &\n Readonly<{ refusal: Extract<ClaimRefusal, { kind: \"disjointness\" }> }>;\n\n/**\n * THE one place a backend claim refusal becomes the refusal its FAMILY\n * declares.\n *\n * Both families reserve in the `uniques` relation, and `insertUnique` /\n * `insertUniqueBatch` report every foreign owner the same way — as a\n * `UniquenessError` naming the constraint, the holder's concrete kind and the\n * two ids. That contract is deliberately unchanged (a third-party backend\n * implementing it keeps working), so the translation happens here, once, where\n * the entries that produced the claim are still in hand.\n *\n * The reserved `constraintName` alone locates the FAMILY (any entry it matches\n * is a disjointness entry, never a declared constraint's — `assertClaimAxisSafe`\n * makes the name unspellable by one), but not the PAIR: every `disjointWith`\n * pair shares that one literal, so a batch carrying disjointness entries for\n * two different pairs that happen to claim the same id (`Person \"X\"` disjoint\n * with `Company`, `Vehicle \"X\"` disjoint with `Boat`, same batch) would match\n * either indiscriminately on `(constraintName, key)` alone. The entry is\n * additionally required to have written the SAME claim axis the backend\n * reports — the row's actual primary key, and therefore unambiguous — so the\n * one located is provably the one that lost. `error.details.axis` is optional\n * (a third-party backend need not carry it to keep working); when a backend\n * omits it the match falls back to `(constraintName, key)` alone, which is\n * HEAD's behavior and no worse than today's. The payload is built from what\n * the BACKEND reported — the holder's own concrete kind — so the fence's error\n * is identical to the probe's, which is what makes a caller unable to tell\n * which layer refused.\n */\nfunction mapClaimRefusal(\n error: UniquenessError,\n entries: readonly NodeClaimEntry[],\n): never {\n const owed = entries.find(\n (entry): entry is DisjointnessClaimEntry =>\n entry.refusal.kind === \"disjointness\" &&\n entry.constraintName === error.details.constraintName &&\n entry.key === error.details.newId &&\n (error.details.axis === undefined || entry.axis === error.details.axis),\n );\n if (owed === undefined) throw error;\n throw new DisjointError(\n {\n nodeId: error.details.newId,\n attemptedKind: owed.refusal.ownKind,\n conflictingKind: error.details.kind,\n },\n { cause: error },\n );\n}\n\n/**\n * Runs a claim statement, re-raising a foreign owner as the declared refusal of\n * whichever family owed the claim.\n *\n * One wrapper around every claim write, rather than a translation at each of\n * them: a claim statement that skipped it would report a `disjointWith`\n * violation as a uniqueness violation on a constraint name no caller ever\n * declared. Both the CREATE seam's `withNodeCreateClaimsIssuedBy` and the\n * transition seam's {@link claimUniqueKeysThen} reuse it — a resurrect's plan\n * can carry a disjointness entry exactly as a create's claim list does (see\n * {@link planNodeClaimReinsert}), so the transition seam needs the same\n * translation the create seam always did. For a plan whose claims are all\n * uniqueness (an in-place update's diff), `mapClaimRefusal` finds no\n * disjointness entry to remap and rethrows the original `UniquenessError`\n * unchanged, so this is a no-op there.\n */\nasync function issuingClaims(\n entries: readonly NodeClaimEntry[],\n issue: () => Promise<void>,\n): Promise<void> {\n try {\n await issue();\n } catch (error) {\n if (error instanceof UniquenessError) mapClaimRefusal(error, entries);\n throw error;\n }\n}\n\n/**\n * THE refusal for \"this write owes a claim it must issue BEFORE the row that\n * claim gates, and this backend cannot roll that pair back together\".\n *\n * A claim row that outlives the write it was taken for is invisible to every\n * read path and blocks its key forever, with no repair path: `deleteUnique` is\n * reached only by a node whose row exists. So a write that would open that\n * window on a backend with no transactions is refused, exactly as a write whose\n * fence is the per-graph lock has been since the lock became the fence. A\n * claim that follows its row opens no such window — a leaked ENTITY row is\n * visible, deletable, and is what that backend already does today — which is\n * why the subject is the pre-insert group and not the claim set.\n *\n * Takes the ENTRIES this write is about to issue rather than the kind's sites:\n * a row whose `where` predicates all fail writes no claim and must not be\n * refused. For a batch, any member's non-empty pre-insert group makes the batch\n * constrained — the same \"any member\" shape the batch lock probe uses.\n *\n * Delegates the error itself to {@link constraintFenceRefusal}, so there is one\n * refusal body, one code and one advice map.\n */\nfunction claimFenceRefusal(\n ctx: Readonly<{ graphId: string }>,\n backend: GraphBackend | TransactionBackend,\n entries: readonly NodeClaimEntry[],\n): ConfigurationError | undefined {\n const gating = entries.find((entry) => entry.placement === \"pre-insert\");\n if (gating === undefined) return undefined;\n return constraintFenceRefusal(ctx, backend, gating.refusalReason);\n}\n\n/** An entry whose family is uniqueness — the only family a claim probe reads. */\nexport type UniquenessClaimEntry = NodeClaimEntry &\n Readonly<{ refusal: Extract<ClaimRefusal, { kind: \"uniqueness\" }> }>;\n\n/**\n * THE narrowing to the uniqueness family, for every path that probes claim rows.\n *\n * Disjointness entries have their own probe (`checkDisjointnessConstraint`,\n * which reads the NODE rows the constraint is declared over) and deliberately\n * do not get a second one here: a claim-row read would be a second spelling of\n * that verdict, and the two would drift.\n *\n * Exported because it is also what \"an ingestion branch defers node UNIQUENESS\n * and nothing else\" means, operationally: the clone's registrations produce\n * claim entries and this predicate is false for every one of them, while the\n * disjointness entries the same list carries are unaffected (see\n * {@link file://../../graph-merge/working-copy.ts graphWithoutNodeUniqueness}).\n */\nexport function isUniquenessClaimEntry(\n entry: NodeClaimEntry,\n): entry is UniquenessClaimEntry {\n return entry.refusal.kind === \"uniqueness\";\n}\n\n/**\n * Probes ONE entry's key across every kind its scope covers, the axis first.\n *\n * THE SINGLE OWNER of \"is this key available to this node?\" — the conflict\n * verdict and the ownership reading are one read, so no caller can consult one\n * without the other. {@link checkUniquenessConstraints} is this probe run for\n * its refusal alone; the plan builders below additionally keep what it read.\n *\n * Ownership is the pair `(concrete_kind, node_id)`, never the id alone: ids are\n * unique only per kind, so a namesake under another kind holds a DIFFERENT\n * node's reservation and must be refused — which is exactly what a scope\n * spanning kinds exists to catch. The refusal reports the holder's concrete\n * kind rather than the axis it was found at, because the axis need not be that\n * node's kind and, after this move, usually is not.\n *\n * @returns whether THIS node already holds the key live AT THE AXIS it is about\n * to claim, in which case a claim would be a no-op and a compensating release\n * would strip a reservation the node is entitled to. A row this node owns at\n * a LEGACY axis deliberately does not suppress the claim: the axis row is a\n * genuinely new reservation whose compensation must run.\n * @throws UniquenessError when a DIFFERENT node holds the key under any kind in\n * scope.\n */\nasync function probeUniqueKey(\n ctx: UniquenessProbeContext,\n kind: string,\n id: string,\n entry: UniquenessClaimEntry,\n): Promise<boolean> {\n const mine: ClaimOwner = { concreteKind: kind, nodeId: id };\n\n // `let` earns its place: the loop must visit EVERY kind in scope to reach its\n // refusal, so the ownership reading cannot be an early return.\n let heldByThisNode = false;\n for (const kindToCheck of uniquenessProbeKinds(\n kind,\n entry.refusal.constraint.scope,\n ctx.registry,\n )) {\n const existing = await ctx.backend.checkUnique({\n graphId: ctx.graphId,\n nodeKind: kindToCheck,\n constraintName: entry.constraintName,\n key: entry.key,\n });\n\n if (existing === undefined) continue;\n if (\n !isSameClaimOwner(\n { concreteKind: existing.concrete_kind, nodeId: existing.node_id },\n mine,\n )\n ) {\n throw new UniquenessError({\n constraintName: entry.constraintName,\n kind: existing.concrete_kind,\n existingId: existing.node_id,\n newId: id,\n fields: entry.refusal.constraint.fields,\n });\n }\n if (kindToCheck === entry.axis) heldByThisNode = true;\n }\n return heldByThisNode;\n}\n\n/**\n * Checks uniqueness constraints for a new or existing node.\n *\n * @throws ValidationError if any constraint is violated\n */\nexport async function checkUniquenessConstraints(\n ctx: UniquenessProbeContext,\n kind: string,\n id: string,\n props: Record<string, unknown>,\n constraints: readonly UniqueConstraint[],\n): Promise<void> {\n // The create extent, which is the wider of the two: a probe wants every claim\n // this row could owe, and placement — the only thing the operation decides\n // for a uniqueness site — says nothing about what is read. The disjointness\n // entries the create extent also carries belong to the other probe, which\n // reads node rows rather than claim rows.\n for (const entry of nodeClaimEntries(\n ctx.registry,\n kind,\n id,\n props,\n constraints,\n \"create\",\n )) {\n if (isUniquenessClaimEntry(entry))\n await probeUniqueKey(ctx, kind, id, entry);\n }\n}\n\n/**\n * What the create claim seam needs from its caller.\n *\n * `lock` is compile-time evidence that the per-graph write-lock discipline was\n * satisfied BEFORE any row work (see {@link GraphWriteLock}); the seam performs\n * no locking of its own, so requiring the token here makes \"claim before lock\"\n * a type error at the call site instead of a lock-order inversion in review.\n */\nexport type NodeClaimContext = Readonly<{\n graphId: string;\n registry: KindRegistry;\n lock: GraphWriteLock;\n}>;\n\n/** One row whose claims a create-shaped write is about to issue. */\nexport type NodeClaimItem = Readonly<{\n kind: string;\n id: string;\n props: Record<string, unknown>;\n constraints: readonly UniqueConstraint[];\n}>;\n\n/** One row's claim, with the owner it will be written under. */\ntype PlacedClaim = Readonly<{\n item: NodeClaimItem;\n entry: NodeClaimEntry;\n target: ClaimTarget;\n}>;\n\n/**\n * How one placement group.s statements are issued.\n *\n * `onIssued` reports which claims actually landed, and it is optional because\n * only the pre-insert group has a use for the answer: that group is compensated\n * when its gated write fails, while a post-insert claim belongs to a row that is\n * already written and has nothing to undo.\n */\ntype ClaimIssuer = (\n ctx: UniquenessContext,\n claims: readonly PlacedClaim[],\n onIssued?: (issued: readonly PlacedClaim[]) => void,\n) => Promise<void>;\n\nfunction claimTarget(graphId: string, entry: NodeClaimEntry): ClaimTarget {\n return {\n relation: \"uniques\",\n graphId,\n axis: entry.axis,\n constraintName: entry.constraintName,\n key: entry.key,\n };\n}\n\nfunction claimInsertParams(\n graphId: string,\n claim: PlacedClaim,\n): InsertUniqueParams {\n return {\n graphId,\n nodeKind: claim.entry.axis,\n constraintName: claim.entry.constraintName,\n key: claim.entry.key,\n nodeId: claim.item.id,\n concreteKind: claim.item.kind,\n };\n}\n\n/** One statement per claim — the shape the single-row create path ships. */\nasync function issueClaimsIndividually(\n ctx: UniquenessContext,\n claims: readonly PlacedClaim[],\n onIssued?: (issued: readonly PlacedClaim[]) => void,\n): Promise<void> {\n for (const claim of claims) {\n await issuingClaims([claim.entry], () =>\n ctx.backend.insertUnique(claimInsertParams(ctx.graphId, claim)),\n );\n onIssued?.([claim]);\n }\n}\n\n/**\n * ONE statement for the whole group, which is also what makes it deadlock-free\n * against itself: a single multi-row statement takes its row locks in a fixed\n * order. Falls back to the per-claim shape on a backend with no batch\n * primitive.\n */\nasync function issueClaimsBatched(\n ctx: UniquenessContext,\n claims: readonly PlacedClaim[],\n onIssued?: (issued: readonly PlacedClaim[]) => void,\n): Promise<void> {\n if (claims.length === 0) return;\n const batch = ctx.backend.insertUniqueBatch;\n if (batch === undefined) {\n await issueClaimsIndividually(ctx, claims, onIssued);\n return;\n }\n await issuingClaims(\n claims.map((claim) => claim.entry),\n async () => {\n await batch(claims.map((claim) => claimInsertParams(ctx.graphId, claim)));\n },\n );\n onIssued?.(claims);\n}\n\n/**\n * Reserves the gating group, runs the row write it gates, and gives those\n * reservations back if the write does not land.\n *\n * Compensate, not swallow — the same give-back {@link withNodeClaimTransition}\n * makes, for the same reason: the reservations this write took are returned and\n * the original failure is rethrown, so the caller sees the error it would have\n * seen with no reservation attempted at all. Only rows that actually landed are\n * given back, and each is named in full (owner pair and claim axis), so nothing\n * a namesake under another kind or an older axis holds is touched.\n */\nasync function claimGroupThenWrite<T>(\n ctx: UniquenessContext,\n issue: ClaimIssuer,\n gating: readonly PlacedClaim[],\n gatedWrite: () => Promise<T>,\n): Promise<T> {\n const issued: PlacedClaim[] = [];\n try {\n await issue(ctx, gating, (landed) => {\n issued.push(...landed);\n });\n return await gatedWrite();\n } catch (error) {\n for (const claim of issued.toReversed()) {\n await releaseClaimedUniqueKeys(ctx, claim.item.kind, claim.item.id, [\n {\n axis: claim.entry.axis,\n constraintName: claim.entry.constraintName,\n key: claim.entry.key,\n },\n ]);\n }\n throw error;\n }\n}\n\n/**\n * Issues a create's claims on the two sides of the row write they gate, and\n * compensates the PRE-INSERT ones away if that write does not land.\n *\n * The entries are partitioned by their {@link ClaimPlacement} and the two\n * groups are issued around `gatedInsert`:\n *\n * 1. the `pre-insert` group — the create path's twin of\n * {@link withNodeClaimTransition}, with the same claim → gated write →\n * compensate sequence and the same reasoning: the claim is the only fence\n * for that axis, and a fence issued after the write it fences is not a\n * fence. A refusal here therefore happens with zero rows written, and a\n * refusal from the write compensates the reservations away;\n * 2. `gatedInsert()`;\n * 3. the `post-insert` group — the position those entries ship in, unchanged,\n * and with that position's failure behavior: a throw propagates and nothing\n * compensates it, because the row it belongs to is already written and\n * visible.\n *\n * Each group is sorted by {@link compareClaimTargets} and the pre-insert group\n * is always issued first, so every writer of a given row computes the same\n * acquisition order. A row owing claims in both groups therefore emits two\n * claim statements where one placement alone emits one.\n */\nasync function withNodeCreateClaimsIssuedBy<T>(\n ctx: NodeClaimContext,\n items: readonly NodeClaimItem[],\n backend: GraphBackend | TransactionBackend,\n issue: ClaimIssuer,\n gatedInsert: () => Promise<T>,\n): Promise<T> {\n const claimContext = createUniquenessContext(\n ctx.graphId,\n ctx.registry,\n backend,\n );\n const claims = items.flatMap((item) =>\n nodeClaimEntries(\n ctx.registry,\n item.kind,\n item.id,\n item.props,\n item.constraints,\n \"create\",\n ).map((entry) => ({\n item,\n entry,\n target: claimTarget(ctx.graphId, entry),\n })),\n );\n\n const refusal = claimFenceRefusal(\n ctx,\n backend,\n claims.map((claim) => claim.entry),\n );\n if (refusal !== undefined) throw refusal;\n\n const inPlacement = (placement: ClaimPlacement): readonly PlacedClaim[] =>\n claims\n .filter((claim) => claim.entry.placement === placement)\n .toSorted((left, right) =>\n compareClaimTargets(left.target, right.target),\n );\n\n const result = await claimGroupThenWrite(\n claimContext,\n issue,\n inPlacement(\"pre-insert\"),\n gatedInsert,\n );\n await issue(claimContext, inPlacement(\"post-insert\"));\n return result;\n}\n\n/**\n * The create claim seam for ONE row: one statement per claim, matching the\n * statement shape the single-row create path ships.\n */\nexport function withNodeCreateClaims<T>(\n ctx: NodeClaimContext,\n item: NodeClaimItem,\n backend: GraphBackend | TransactionBackend,\n gatedInsert: () => Promise<T>,\n): Promise<T> {\n return withNodeCreateClaimsIssuedBy(\n ctx,\n [item],\n backend,\n issueClaimsIndividually,\n gatedInsert,\n );\n}\n\n/**\n * The create claim seam for a BATCH: one statement per placement group across\n * every row, instead of the per-row statement fan.\n */\nexport function withNodeCreateClaimsBatch<T>(\n ctx: NodeClaimContext,\n items: readonly NodeClaimItem[],\n backend: GraphBackend | TransactionBackend,\n gatedInsert: () => Promise<T>,\n): Promise<T> {\n return withNodeCreateClaimsIssuedBy(\n ctx,\n items,\n backend,\n issueClaimsBatched,\n gatedInsert,\n );\n}\n\n/**\n * The gate for a claim set whose row write has ALREADY been applied — a\n * re-claim after a set update, or a test seeding a reservation.\n *\n * Such a write reaches the seam with nothing left to gate, which is exactly why\n * it owes no pre-insert claim: there is no row write left for a claim to\n * precede. Naming it makes that reading explicit at the call site instead of\n * leaving an inline no-op for a reader to interpret.\n */\nexport function alreadyAppliedRowWrite(): Promise<undefined> {\n return Promise.resolve(undefined);\n}\n\n/**\n * Releases the claims a node being deleted holds — the LIFECYCLE shape: every\n * claim this node owns for each applying constraint's key, at whatever axis the\n * claim sits on.\n */\nexport async function deleteUniquenessEntries(\n ctx: UniquenessContext,\n kind: string,\n id: string,\n props: Record<string, unknown>,\n constraints: readonly UniqueConstraint[],\n): Promise<void> {\n // The create extent: a delete gives back everything a create wrote, so the\n // release must be read from the wider of the two lists.\n await releaseOwnedUniqueKeys(\n ctx,\n kind,\n id,\n nodeClaimEntries(ctx.registry, kind, id, props, constraints, \"create\"),\n );\n}\n\n/**\n * Drops EVERY claim a set of nodes holds under one concrete kind, key-blind, so\n * the rebuild that follows can re-claim from the after-images.\n *\n * The key-blind drop is what a set update needs and the per-key\n * {@link deleteUniquenessEntries} cannot give it: the statement rewrote whole\n * rows without reading their before-images, so nobody knows which keys those\n * rows used to hold. It drops BOTH families — every claim the owner pair holds —\n * which is why the rebuild must go back through {@link withNodeCreateClaimsBatch}\n * rather than a uniqueness-only reinsert.\n *\n * Requiring the member rather than probing it: the only caller\n * ({@link file://../operations/node-write-pipeline.ts applyNodeSetUpdate})\n * refuses the write up front when a constrained kind's backend lacks it, with a\n * code that names the operation. A second fallback here would be a quieter\n * answer to a question already asked.\n */\nexport async function hardDeleteClaimsByNodeIds(\n ctx: UniquenessContext,\n concreteKind: string,\n nodeIds: readonly string[],\n): Promise<void> {\n await requireDefined(ctx.backend.hardDeleteUniquesByNodeIds)({\n graphId: ctx.graphId,\n concreteKind,\n nodeIds,\n });\n}\n\n/**\n * A single constraint's sidecar transition, decided by a plan builder and\n * carried out by {@link withNodeClaimTransition}.\n *\n * The two keys move at DIFFERENT times relative to the primary row write, so\n * they are named for when they move rather than for old/new:\n * `claim` is reserved BEFORE the write (it is the write's conflict gate),\n * `release` is given up AFTER it (it is history the write supersedes).\n */\ntype PendingUniqueMutation = Readonly<{\n constraintName: string;\n /** The key to give up once the primary write lands; undefined = none. */\n release: string | undefined;\n /**\n * The entry to reserve before the primary write; undefined = none, either\n * because the constraint stopped applying or because {@link probeUniqueKey}\n * found this node already holding it live. It is the whole entry rather than\n * a bare key so the compensation names the row this transition wrote instead\n * of re-deriving which axis that was, and so the refusal reads the placement\n * and class the entry's site decided.\n */\n claim: NodeClaimEntry | undefined;\n}>;\n\n/**\n * The sidecar transition a node write owes, decided but not yet performed.\n *\n * Opaque to its holder on purpose: the only thing a caller does with a plan is\n * hand it to {@link withNodeClaimTransition} together with the primary row\n * write it belongs to.\n */\nexport type UniquenessUpdatePlan = readonly PendingUniqueMutation[];\n\n/**\n * Decides the claim changes a node's new props require, WITHOUT writing any of\n * them.\n *\n * Preflights EVERY changed constraint before anything is written. A node can\n * carry several unique constraints; probing them one at a time as they are\n * applied would let a later constraint's conflict throw after earlier sidecars\n * already moved. Probing all of them first means a refusal here happens with\n * zero writes, and the probes are independent per `constraintName`, so one\n * constraint's verdict is unaffected by the others' still-unapplied changes.\n *\n * Handles the cases where a constraint starts applying, stops applying, or\n * keeps applying under a different key.\n *\n * @throws UniquenessError if an updated value is already held by another node\n */\nexport async function planNodeClaimUpdate(\n ctx: UniquenessContext,\n kind: string,\n id: string,\n oldProps: Record<string, unknown>,\n newProps: Record<string, unknown>,\n constraints: readonly UniqueConstraint[],\n): Promise<UniquenessUpdatePlan> {\n // Both sides of the diff are read from the one entries function, so the key\n // a release names is computed exactly the way the key a claim names is.\n const oldKeys = new Map(\n nodeClaimEntries(\n ctx.registry,\n kind,\n id,\n oldProps,\n constraints,\n \"update\",\n ).map((entry) => [entry.constraintName, entry.key]),\n );\n const newEntries = new Map(\n nodeClaimEntries(ctx.registry, kind, id, newProps, constraints, \"update\")\n .filter((entry): entry is UniquenessClaimEntry =>\n isUniquenessClaimEntry(entry),\n )\n .map((entry) => [entry.constraintName, entry]),\n );\n\n const pending: PendingUniqueMutation[] = [];\n for (const constraint of constraints) {\n const oldKey = oldKeys.get(constraint.name);\n const newEntry = newEntries.get(constraint.name);\n\n // No change - constraint didn't apply and still doesn't\n if (oldKey === undefined && newEntry === undefined) continue;\n\n // Key is the same and the constraint still applies - nothing to do\n if (oldKey !== undefined && oldKey === newEntry?.key) continue;\n\n // Probe the new key: refuse a value another node holds, and note a value\n // this node somehow holds already so the transition neither re-claims nor\n // releases it.\n const alreadyHeld =\n newEntry === undefined ? false : (\n await probeUniqueKey(ctx, kind, id, newEntry)\n );\n\n pending.push({\n constraintName: constraint.name,\n release: oldKey,\n claim: newEntry === undefined || alreadyHeld ? undefined : newEntry,\n });\n }\n\n return pending;\n}\n\n/**\n * The plan a RESURRECTING write needs: every applying constraint re-reserved\n * from scratch, uniqueness AND disjointness alike.\n *\n * A tombstoned node holds no live reservations — {@link deleteUniquenessEntries}\n * released them at soft-delete time, reading the wider `\"create\"` extent that\n * already covers both families — so the diff-based {@link planNodeClaimUpdate}\n * cannot be used here: it would skip an unchanged key and leave the revived\n * node holding NO reservation, letting a later create (of this node's own key,\n * or of a disjoint partner under this node's id) silently duplicate the value.\n *\n * Reads its entries at `\"resurrect\"`, the {@link NodeClaimOperation} that owes\n * the same disjointness sites a create owes (see `nodeClaimSites`) — a\n * resurrect brings a node back into existence under its kind exactly as a\n * create does, so it owes the same cross-kind claim. A disjointness entry can\n * never already be held by THIS node (its own reservation was released at\n * soft-delete time, and nothing else could have taken it under this node's\n * OWN id/kind pair), so unlike a uniqueness entry it needs no \"already held by\n * myself\" probe — it is claimed fresh, exactly as a create claims it fresh.\n *\n * @throws UniquenessError if a key this node held was taken while it was\n * tombstoned\n * @throws DisjointError if a disjoint partner now holds this id (translated by\n * {@link claimUniqueKeysThen} from the `UniquenessError` `insertUnique`\n * itself reports)\n */\nexport async function planNodeClaimReinsert(\n ctx: UniquenessContext,\n kind: string,\n id: string,\n props: Record<string, unknown>,\n constraints: readonly UniqueConstraint[],\n): Promise<UniquenessUpdatePlan> {\n const pending: PendingUniqueMutation[] = [];\n for (const entry of nodeClaimEntries(\n ctx.registry,\n kind,\n id,\n props,\n constraints,\n \"resurrect\",\n )) {\n const alreadyHeld =\n isUniquenessClaimEntry(entry) ?\n await probeUniqueKey(ctx, kind, id, entry)\n : false;\n\n pending.push({\n constraintName: entry.constraintName,\n release: undefined,\n claim: alreadyHeld ? undefined : entry,\n });\n }\n return pending;\n}\n\n/** One reservation a release names. */\ntype ReleasableKey = Readonly<{ constraintName: string; key: string }>;\n\n/**\n * A reservation a write actually took, remembered together with the claim axis\n * it was written at so the compensation can name the same row rather than\n * re-deriving it.\n */\ntype ClaimedKey = ReleasableKey & Readonly<{ axis: string }>;\n\n/**\n * LIFECYCLE release: gives up every reservation THIS node holds for each\n * `(constraintName, key)`, in plan order, whatever axis the reservation sits\n * on.\n *\n * Scoping to the owner pair `(concreteKind, id)` rather than to the axis is\n * what lets a claim written under an older axis be released by newer code, and\n * what keeps a namesake — same id, different kind — holding its own.\n */\nasync function releaseOwnedUniqueKeys(\n ctx: UniquenessContext,\n kind: string,\n id: string,\n keys: readonly ReleasableKey[],\n): Promise<void> {\n for (const entry of keys) {\n await ctx.backend.deleteUnique({\n graphId: ctx.graphId,\n constraintName: entry.constraintName,\n key: entry.key,\n concreteKind: kind,\n nodeId: id,\n });\n }\n}\n\n/**\n * COMPENSATING release: undoes exactly the rows a failed write just claimed —\n * the owner's reservation AT the axis it claimed on, and nothing else.\n *\n * Deliberately narrower than {@link releaseOwnedUniqueKeys}: a rollback must\n * touch neither a reservation at another axis that predates this write nor one\n * another node holds. Conflating the two would make a refused write strip\n * claims it never took.\n */\nasync function releaseClaimedUniqueKeys(\n ctx: UniquenessContext,\n kind: string,\n id: string,\n keys: readonly ClaimedKey[],\n): Promise<void> {\n for (const entry of keys) {\n await ctx.backend.deleteUnique({\n graphId: ctx.graphId,\n nodeKind: entry.axis,\n constraintName: entry.constraintName,\n key: entry.key,\n concreteKind: kind,\n nodeId: id,\n });\n }\n}\n\n/**\n * Reserves the plan's new keys, runs the primary row write they gate, and undoes\n * the reservations if that write does not land.\n *\n * Each claim statement runs through {@link issuingClaims} — a resurrect's plan\n * can carry a disjointness entry ({@link planNodeClaimReinsert}), and a foreign\n * owner of THAT reservation must surface as `DisjointError`, not the raw\n * `UniquenessError` `insertUnique` reports for every family alike.\n */\nasync function claimUniqueKeysThen<T>(\n ctx: UniquenessContext,\n kind: string,\n id: string,\n plan: UniquenessUpdatePlan,\n gatedWrite: () => Promise<T>,\n): Promise<T> {\n const claimed: ClaimedKey[] = [];\n try {\n for (const mutation of plan) {\n const claim = mutation.claim;\n if (claim === undefined) continue;\n await issuingClaims([claim], () =>\n ctx.backend.insertUnique({\n graphId: ctx.graphId,\n nodeKind: claim.axis,\n constraintName: mutation.constraintName,\n key: claim.key,\n nodeId: id,\n concreteKind: kind,\n }),\n );\n claimed.push({\n axis: claim.axis,\n constraintName: mutation.constraintName,\n key: claim.key,\n });\n }\n return await gatedWrite();\n } catch (error) {\n // Compensate, not swallow: the reservations this transition took are given\n // back and the original failure is rethrown, so the caller sees the SAME\n // error it would have seen with no reservation attempted at all.\n //\n // The give-back names the exact rows this transition wrote — owner pair and\n // claim axis both — so it can strip neither a reservation at another axis\n // that predates this write nor one a namesake under a different kind holds.\n //\n // The give-back is exact because of what a claim can be. `probeUniqueKey`\n // refuses a key another node holds and reports one THIS node already holds\n // (which the plan then does not claim), so every claimed key was free or\n // tombstoned beforehand and releasing it restores precisely that. And no\n // peer can have taken it in between: this transaction holds the row.\n //\n // One detail is restored in kind rather than exactly: a claim that took over\n // a TOMBSTONED reservation rewrites its recorded owner, and the give-back\n // re-tombstones it under THIS node's id instead of the previous holder's.\n // The key reads as free either way — `checkUnique` skips tombstoned rows —\n // and the one reader that looks at them, `getOrCreateByConstraint`'s\n // resurrect-by-key lookup, writes the props it was called with onto whatever\n // row it revives, so the reservation and the row holding the value stay\n // consistent. Which tombstone that lookup revives can differ. If the\n // compensation itself fails the row is genuinely half-written, and the\n // error that then surfaces is a raw backend failure no per-row consumer\n // catches — the enclosing transaction aborts, which is the only honest\n // outcome left.\n await releaseClaimedUniqueKeys(ctx, kind, id, claimed);\n throw error;\n }\n}\n\n/**\n * Runs one node's primary row write with its claim transition wrapped around\n * it, so the pair commits or fails AS ONE UNIT.\n *\n * ## Why the sequence is claim, gate, release\n *\n * A node update is two independently fallible writes — the row itself and its\n * claim rows — with no rollback between them: nothing here is\n * savepoint-protected (see `operations/write-transaction.ts`), and the backends\n * this supports do not all have savepoints to reach for. Sequencing is the only\n * atomicity available, and only ONE sequence makes both failures leave zero net\n * effect:\n *\n * - **Claim first.** `insertUnique` is an upsert that reports the key's final\n * owner, so claiming IS the conflict gate — and once this transaction holds\n * the row, no concurrent writer can take the key from under it. Probing and\n * then claiming later leaves a window (wide open under PostgreSQL READ\n * COMMITTED, where a peer commits between the two) in which the claim fails\n * AFTER the row was already updated.\n * - **Then the row write.** It can legitimately match nothing —\n * `expectedValidFrom`, `deleted_at` — and callers that catch that per row and\n * commit the rest (interchange import) must not be left with reservations for\n * a row that never changed. A refusal here compensates the claims away.\n * - **Release last.** Giving up the old key before the row write would free a\n * value for a write that may never land; giving it up after is safe because\n * nothing can fail on it.\n *\n * The predecessor of this helper ordered the whole sidecar transition after the\n * row write, which closed the second failure and left the first: a caught\n * `UniquenessError` then reported `updated: 0` for a row whose props HAD\n * changed, whose old reservation was gone, and whose new one belonged to someone\n * else.\n *\n * Because that sequence is claim-first for EVERY scope, every claim this seam\n * issues is `pre-insert`, and a backend that cannot roll the pair back together\n * is refused before the first of them — see {@link claimFenceRefusal}. The\n * subject is the plan's claims rather than the kind's constraints: an update\n * that does not move a key issues no reservation and must not be refused.\n */\nexport async function withNodeClaimTransition<T>(\n ctx: UniquenessContext,\n kind: string,\n id: string,\n plan: UniquenessUpdatePlan,\n gatedWrite: () => Promise<T>,\n): Promise<T> {\n const refusal = claimFenceRefusal(\n ctx,\n ctx.backend,\n plan.flatMap((mutation) =>\n mutation.claim === undefined ? [] : [mutation.claim],\n ),\n );\n if (refusal !== undefined) throw refusal;\n\n const result = await claimUniqueKeysThen(ctx, kind, id, plan, gatedWrite);\n await releaseOwnedUniqueKeys(\n ctx,\n kind,\n id,\n plan.flatMap((mutation) =>\n mutation.release === undefined ?\n []\n : [{ constraintName: mutation.constraintName, key: mutation.release }],\n ),\n );\n return result;\n}\n","/**\n * Node claims for a RESOLVED WRITE SET — a whole after-image validated at once,\n * then applied.\n *\n * Every other claim path in this directory decides one row at a time, which is\n * what an ordinary write is. Two callers instead know their complete final state\n * before writing any of it — a graph merge's resolved plan\n * ({@link file://../../graph-merge/merge.ts}) and a set update\n * ({@link file://../operations/node-operations.ts}) — and for them \"one row at a\n * time\" is the wrong verdict: an atomic SWAP of two nodes' keys, or a handoff of\n * one key from a node the set deletes to a node the set writes, is legal in the\n * final state and refused by every intermediate one.\n *\n * So this module reads the set's claims TOGETHER, decides them against each\n * other first and the persisted relation second, and treats every reservation\n * the set itself releases as available. It decides nothing on its own: the\n * entries come from {@link nodeClaimEntries}, the axis fold from\n * {@link uniquenessProbeKinds}, the ownership verdict from\n * {@link isSameClaimOwner} / {@link claimOwnerKey}, and the rebuild from the\n * same claim writer every create-shaped write uses.\n */\nimport { type UniqueConstraint } from \"../../core/types\";\nimport { ConfigurationError, UniquenessError } from \"../../errors\";\nimport { encodeTupleKey } from \"../../utils/tuple-key\";\nimport {\n type ClaimOwner,\n claimOwnerKey,\n isSameClaimOwner,\n uniquenessProbeKinds,\n} from \"./axis\";\nimport {\n alreadyAppliedRowWrite,\n isUniquenessClaimEntry,\n type NodeClaimContext,\n nodeClaimEntries,\n type UniquenessClaimEntry,\n type UniquenessContext,\n withNodeCreateClaimsBatch,\n} from \"./node-claims\";\n\n/** A complete node after-image whose claims belong to one resolved write set. */\nexport type ResolvedNodeUpsert = Readonly<{\n kind: string;\n id: string;\n props: Readonly<Record<string, unknown>>;\n constraints: readonly UniqueConstraint[];\n}>;\n\n/** A node whose current claims the resolved write set gives back. */\nexport type ResolvedNodeRelease = Readonly<{\n kind: string;\n id: string;\n}>;\n\n/**\n * One uniqueness claim the set proposes: who takes it, the row it writes, and\n * the rows its probe reads.\n *\n * `probeKinds` is carried rather than recomputed because it is BOTH halves of\n * the conflict test — the persisted probe issues one read per member, and the\n * in-set test asks whether one claim's write lands in another claim's read set.\n */\ntype ProposedClaim = Readonly<{\n owner: ClaimOwner;\n entry: UniquenessClaimEntry;\n probeKinds: readonly string[];\n}>;\n\n/** The claim rows two proposals share a competition for. */\nfunction claimRowKey(entry: UniquenessClaimEntry): string {\n return encodeTupleKey([entry.constraintName, entry.key]);\n}\n\n/**\n * Whether two proposals in one set are competing — one writes a row the other\n * reads.\n *\n * Reachability in EITHER direction, not axis equality: after the axis fold a\n * `kind`-scoped constraint writes under the writer's own kind while a\n * `kindWithSubClasses` constraint of the same name writes under the component's\n * fold, so two claims can compete without sharing an axis. The wider scope's\n * probe is what would have caught the narrower one's row had it been persisted,\n * and inside one set neither row is persisted yet — which is the whole reason\n * this test exists rather than leaving it to the relation.\n */\nfunction claimsCompete(left: ProposedClaim, right: ProposedClaim): boolean {\n return (\n claimRowKey(left.entry) === claimRowKey(right.entry) &&\n (right.probeKinds.includes(left.entry.axis) ||\n left.probeKinds.includes(right.entry.axis))\n );\n}\n\nfunction proposedClaimRefusal(\n incumbent: ProposedClaim,\n challenger: ProposedClaim,\n): UniquenessError {\n return new UniquenessError({\n constraintName: challenger.entry.constraintName,\n kind: incumbent.owner.concreteKind,\n existingId: incumbent.owner.nodeId,\n newId: challenger.owner.nodeId,\n fields: challenger.entry.refusal.constraint.fields,\n });\n}\n\n/**\n * The set's uniqueness claims, with every in-set competition already refused.\n *\n * Reads its work from {@link nodeClaimEntries} at the CREATE extent — the wider\n * of the two, exactly as {@link file://./node-claims.ts checkUniquenessConstraints}\n * does — and narrows to the uniqueness family. The disjointness entries the same\n * list carries are NOT probed here: they have their own probe over node rows,\n * and each write inside the set's `apply()` reaches it. Filtering rather than\n * asking for a narrower list is what keeps that omission a stated decision\n * instead of an absence nobody notices.\n *\n * @throws UniquenessError when two members of the set compete for one claim.\n */\nfunction proposedClaims(\n ctx: UniquenessContext,\n upserts: readonly ResolvedNodeUpsert[],\n): readonly ProposedClaim[] {\n const accepted: ProposedClaim[] = [];\n for (const upsert of upserts) {\n const owner: ClaimOwner = {\n concreteKind: upsert.kind,\n nodeId: upsert.id,\n };\n const entries = nodeClaimEntries(\n ctx.registry,\n upsert.kind,\n upsert.id,\n { ...upsert.props },\n upsert.constraints,\n \"create\",\n );\n for (const entry of entries) {\n if (!isUniquenessClaimEntry(entry)) continue;\n const proposal: ProposedClaim = {\n owner,\n entry,\n probeKinds: uniquenessProbeKinds(\n upsert.kind,\n entry.refusal.constraint.scope,\n ctx.registry,\n ),\n };\n const incumbent = accepted.find(\n (candidate) =>\n !isSameClaimOwner(candidate.owner, proposal.owner) &&\n claimsCompete(candidate, proposal),\n );\n if (incumbent !== undefined) {\n throw proposedClaimRefusal(incumbent, proposal);\n }\n accepted.push(proposal);\n }\n }\n return accepted;\n}\n\n/** One `checkUniqueBatch` round trip: every key this set claims at one axis. */\ntype ClaimProbeGroup = Readonly<{\n probeKind: string;\n constraintName: string;\n claimsByKey: ReadonlyMap<string, ProposedClaim>;\n}>;\n\n/**\n * Folds the proposals into one probe per `(kind in scope, constraint)`.\n *\n * Keyed on {@link uniquenessProbeKinds} rather than on the axis alone for the\n * reason that function documents: rows written before the axis move sit under\n * their own concrete kind, so a probe that read only the axis would not see them\n * and the move would need a data migration.\n */\nfunction groupClaimsForProbe(\n claims: readonly ProposedClaim[],\n): readonly ClaimProbeGroup[] {\n const groups = new Map<\n string,\n {\n probeKind: string;\n constraintName: string;\n claimsByKey: Map<string, ProposedClaim>;\n }\n >();\n for (const claim of claims) {\n for (const probeKind of claim.probeKinds) {\n const groupKey = encodeTupleKey([probeKind, claim.entry.constraintName]);\n const existing = groups.get(groupKey);\n if (existing === undefined) {\n groups.set(groupKey, {\n probeKind,\n constraintName: claim.entry.constraintName,\n claimsByKey: new Map([[claim.entry.key, claim]]),\n });\n continue;\n }\n existing.claimsByKey.set(claim.entry.key, claim);\n }\n }\n return [...groups.values()];\n}\n\n/** The batch probe this validation is defined in terms of, or a typed refusal. */\nfunction requireBatchProbe(\n ctx: UniquenessContext,\n): NonNullable<UniquenessContext[\"backend\"][\"checkUniqueBatch\"]> {\n const checkUniqueBatch = ctx.backend.checkUniqueBatch;\n if (checkUniqueBatch === undefined) {\n throw new ConfigurationError(\n \"Resolved node writes require batched uniqueness probes\",\n { code: \"RESOLVED_NODE_UNIQUENESS_UNSUPPORTED\" },\n );\n }\n return checkUniqueBatch;\n}\n\n/**\n * Validates node uniqueness against the FINAL state of a resolved write set.\n *\n * All proposed after-images are compared together before persisted claim rows\n * are consulted. Owners the set itself releases or replaces are ignored, which\n * permits atomic swaps and handoffs while still refusing every owner outside the\n * set. The probe is batch-only by contract: a caller that needs this set\n * semantic must not quietly degrade to sequential checks.\n */\nexport async function validateResolvedNodeClaims(\n ctx: UniquenessContext,\n upserts: readonly ResolvedNodeUpsert[],\n releases: readonly ResolvedNodeRelease[] = [],\n): Promise<void> {\n const checkUniqueBatch = requireBatchProbe(ctx);\n const claims = proposedClaims(ctx, upserts);\n if (claims.length === 0) return;\n\n const affectedOwners = new Set(\n [...upserts, ...releases].map((reference) =>\n claimOwnerKey({ concreteKind: reference.kind, nodeId: reference.id }),\n ),\n );\n for (const group of groupClaimsForProbe(claims)) {\n const existingRows = await checkUniqueBatch({\n graphId: ctx.graphId,\n nodeKind: group.probeKind,\n constraintName: group.constraintName,\n keys: [...group.claimsByKey.keys()],\n });\n for (const existing of existingRows) {\n if (\n affectedOwners.has(\n claimOwnerKey({\n concreteKind: existing.concrete_kind,\n nodeId: existing.node_id,\n }),\n )\n ) {\n continue;\n }\n const claim = group.claimsByKey.get(existing.key);\n if (claim === undefined) continue;\n throw new UniquenessError({\n constraintName: group.constraintName,\n // The holder's own kind, never `group.probeKind`: that is the claim\n // AXIS, which a shared scope folds across kinds and which the caller\n // never wrote. The probe and the fence report the same value.\n kind: existing.concrete_kind,\n existingId: existing.node_id,\n newId: claim.owner.nodeId,\n fields: claim.entry.refusal.constraint.fields,\n });\n }\n }\n}\n\n/**\n * Prepares a transaction to apply a resolved write set row by row.\n *\n * Validation happens before any mutation. Once it succeeds, every affected\n * node's claims are batch-cleared so the later per-node upserts can take the\n * validated final keys in any order (including swaps and handoffs).\n */\nasync function prepareResolvedNodeClaims(\n ctx: UniquenessContext,\n upserts: readonly ResolvedNodeUpsert[],\n releases: readonly ResolvedNodeRelease[],\n): Promise<void> {\n const hardDeleteUniquesByNodeIds = ctx.backend.hardDeleteUniquesByNodeIds;\n if (\n ctx.backend.checkUniqueBatch === undefined ||\n ctx.backend.insertUniqueBatch === undefined ||\n hardDeleteUniquesByNodeIds === undefined\n ) {\n throw new ConfigurationError(\n \"Resolved node writes require batched uniqueness operations\",\n { code: \"RESOLVED_NODE_UNIQUENESS_UNSUPPORTED\" },\n );\n }\n await validateResolvedNodeClaims(ctx, upserts, releases);\n\n const idsByKind = new Map<string, Set<string>>();\n for (const reference of [...upserts, ...releases]) {\n const ids = idsByKind.get(reference.kind) ?? new Set<string>();\n ids.add(reference.id);\n idsByKind.set(reference.kind, ids);\n }\n for (const [concreteKind, nodeIds] of idsByKind) {\n await hardDeleteUniquesByNodeIds({\n graphId: ctx.graphId,\n concreteKind,\n nodeIds: [...nodeIds],\n });\n }\n}\n\n/**\n * Applies a resolved write set between the set preflight and one final claim\n * rebuild.\n *\n * The rebuild is required even though ordinary upserts take their own claims: an\n * unchanged upsert may be coalesced and skip all of its normal side effects\n * after preparation cleared its reservations. Re-taking every approved claim is\n * idempotent and makes the transition independent of whether individual writes\n * were coalesced.\n *\n * It goes through {@link withNodeCreateClaimsBatch} — the claim writer every\n * create-shaped write uses — and not through a uniqueness-only insert, for a\n * reason the preparation step makes load-bearing: `hardDeleteUniquesByNodeIds`\n * clears every claim the affected nodes OWN, and after WS2 that includes their\n * `disjointWith` reservations. A rebuild that restored only the uniqueness\n * family would leave each merged node unfenced against a disjoint namesake for\n * the rest of the graph's life. The claim writer restores what\n * {@link nodeClaimEntries} says the row owes, both families, so the set's claims\n * cannot fall out of step with an ordinary create's.\n *\n * `lock` is the caller's evidence that the per-graph write lock was taken before\n * any row work; this function performs no locking of its own.\n */\nexport async function applyResolvedNodeClaims<Output>(\n ctx: NodeClaimContext,\n backend: UniquenessContext[\"backend\"],\n upserts: readonly ResolvedNodeUpsert[],\n releases: readonly ResolvedNodeRelease[],\n apply: () => Promise<Output>,\n): Promise<Output> {\n const claimContext: UniquenessContext = {\n graphId: ctx.graphId,\n registry: ctx.registry,\n backend,\n };\n await prepareResolvedNodeClaims(claimContext, upserts, releases);\n const result = await apply();\n await withNodeCreateClaimsBatch(\n ctx,\n upserts.map((upsert) => ({\n kind: upsert.kind,\n id: upsert.id,\n props: { ...upsert.props },\n constraints: upsert.constraints,\n })),\n backend,\n alreadyAppliedRowWrite,\n );\n return result;\n}\n","/**\n * Embedding Sync Utilities\n *\n * Handles automatic synchronization of embedding fields with the embeddings table.\n * When nodes with embedding properties are created, updated, or deleted,\n * these utilities ensure the embeddings table stays in sync.\n */\nimport { type z } from \"zod\";\n\nimport { type GraphBackend, type TransactionBackend } from \"../backend/types\";\nimport {\n type ResolvedEmbeddingField,\n resolveEmbeddingFields,\n} from \"../core/embedding\";\nimport { readOwnProperty } from \"../utils/object\";\n\n// ============================================================\n// Types\n// ============================================================\n\n/**\n * Information about an embedding field in a node schema.\n *\n * Re-exports the canonical {@link ResolvedEmbeddingField} so the backend\n * gets `dimensions` plus the resolved index `(metric, indexType)` — all\n * three needed to address the field's typed per-`(kind, field)` storage\n * slot when upserting.\n */\ntype EmbeddingFieldInfo = ResolvedEmbeddingField;\n\n/**\n * Context for embedding sync operations.\n */\nexport type EmbeddingSyncContext = Readonly<{\n graphId: string;\n nodeKind: string;\n nodeId: string;\n backend: GraphBackend | TransactionBackend;\n}>;\n\n// ============================================================\n// Schema Introspection\n// ============================================================\n\n/**\n * Cache keyed by the Zod schema instance. Schemas are immutable at\n * runtime; the same reference recurs across every CRUD call.\n */\nconst embeddingFieldsCache = new WeakMap<\n z.ZodType,\n readonly EmbeddingFieldInfo[]\n>();\n\n/**\n * Extracts embedding field information from a Zod schema.\n * Returns all embedding fields found at the top level of an object schema.\n *\n * Thin per-schema-instance memoization over the canonical\n * {@link resolveEmbeddingFields}; the same schema reference recurs across\n * every CRUD call, so caching avoids re-walking the shape each time.\n */\nexport function getEmbeddingFields(\n schema: z.ZodType,\n): readonly EmbeddingFieldInfo[] {\n const cached = embeddingFieldsCache.get(schema);\n if (cached) return cached;\n const fields = resolveEmbeddingFields(schema);\n embeddingFieldsCache.set(schema, fields);\n return fields;\n}\n\n// ============================================================\n// Embedding Sync Operations\n// ============================================================\n\n/**\n * Syncs embeddings after a node create or update operation.\n *\n * For each embedding field in the schema:\n * - If the props contain an embedding value, upsert it to the embeddings table\n * - If the props don't contain an embedding value (undefined), delete any existing embedding\n */\nexport async function syncEmbeddings(\n ctx: EmbeddingSyncContext,\n schema: z.ZodType,\n props: Record<string, unknown>,\n): Promise<void> {\n const { backend } = ctx;\n\n // Check if backend supports embedding operations\n if (!backend.upsertEmbedding || !backend.deleteEmbedding) {\n return;\n }\n\n const embeddingFields = getEmbeddingFields(schema);\n if (embeddingFields.length === 0) {\n return;\n }\n\n for (const field of embeddingFields) {\n const value = readOwnProperty(props, field.fieldPath);\n\n if (isValidEmbeddingValue(value)) {\n // Upsert the embedding\n await backend.upsertEmbedding({\n graphId: ctx.graphId,\n nodeKind: ctx.nodeKind,\n nodeId: ctx.nodeId,\n fieldPath: field.fieldPath,\n embedding: value,\n dimensions: field.dimensions,\n metric: field.metric,\n indexType: field.indexType,\n });\n } else if (value === undefined) {\n // Delete any existing embedding for this field\n await backend.deleteEmbedding({\n graphId: ctx.graphId,\n nodeKind: ctx.nodeKind,\n nodeId: ctx.nodeId,\n fieldPath: field.fieldPath,\n dimensions: field.dimensions,\n metric: field.metric,\n indexType: field.indexType,\n });\n }\n // If value is null or invalid, skip (validation should have caught this)\n }\n}\n\n/**\n * Syncs embeddings for a batch of same-kind node creates through one\n * `upsertEmbeddingBatch` per field (falling back to per-row\n * `upsertEmbedding` when the backend lacks the batch primitive). Mirrors\n * `syncEmbeddings` per row: present values upsert, `undefined` values\n * delete any existing embedding for the field.\n */\nexport async function syncEmbeddingsBatchForKind(\n args: Readonly<{\n graphId: string;\n nodeKind: string;\n backend: GraphBackend | TransactionBackend;\n }>,\n schema: z.ZodType,\n items: readonly Readonly<{\n nodeId: string;\n props: Record<string, unknown>;\n }>[],\n): Promise<void> {\n const { graphId, nodeKind, backend } = args;\n if (!backend.upsertEmbedding || !backend.deleteEmbedding) {\n return;\n }\n\n const embeddingFields = getEmbeddingFields(schema);\n if (embeddingFields.length === 0) {\n return;\n }\n\n for (const field of embeddingFields) {\n const rows: { nodeId: string; embedding: readonly number[] }[] = [];\n const deletionIds: string[] = [];\n for (const item of items) {\n const value = readOwnProperty(item.props, field.fieldPath);\n if (isValidEmbeddingValue(value)) {\n rows.push({ nodeId: item.nodeId, embedding: value });\n } else if (value === undefined) {\n deletionIds.push(item.nodeId);\n }\n }\n\n if (rows.length > 0) {\n if (backend.upsertEmbeddingBatch === undefined) {\n for (const row of rows) {\n await backend.upsertEmbedding({\n graphId,\n nodeKind,\n nodeId: row.nodeId,\n fieldPath: field.fieldPath,\n embedding: row.embedding,\n dimensions: field.dimensions,\n metric: field.metric,\n indexType: field.indexType,\n });\n }\n } else {\n await backend.upsertEmbeddingBatch({\n graphId,\n nodeKind,\n fieldPath: field.fieldPath,\n dimensions: field.dimensions,\n metric: field.metric,\n indexType: field.indexType,\n rows,\n });\n }\n }\n\n if (deletionIds.length > 0 && backend.deleteEmbeddingBatch !== undefined) {\n await backend.deleteEmbeddingBatch({\n graphId,\n nodeKind,\n nodeIds: deletionIds,\n fieldPath: field.fieldPath,\n dimensions: field.dimensions,\n metric: field.metric,\n indexType: field.indexType,\n });\n } else {\n for (const nodeId of deletionIds) {\n await backend.deleteEmbedding({\n graphId,\n nodeKind,\n nodeId,\n fieldPath: field.fieldPath,\n dimensions: field.dimensions,\n metric: field.metric,\n indexType: field.indexType,\n });\n }\n }\n }\n}\n\n/**\n * Deletes a node's embeddings for the embedding fields its kind CURRENTLY\n * declares. A field dropped from the schema after the node was written is\n * intentionally NOT swept here — its entire per-field table is reclaimed\n * wholesale by `store.materializeRemovals()` (the deferred-cleanup verb), which\n * also covers live (never-deleted) nodes' rows. So a hard/soft delete can leave\n * a transient orphan row in a removed field's table until the next reclaim pass;\n * that is the deferred-cleanup design, not a leak.\n */\nexport async function deleteNodeEmbeddings(\n ctx: EmbeddingSyncContext,\n schema: z.ZodType,\n): Promise<void> {\n const { backend } = ctx;\n\n // Check if backend supports embedding operations\n if (!backend.deleteEmbedding) {\n return;\n }\n\n const embeddingFields = getEmbeddingFields(schema);\n if (embeddingFields.length === 0) {\n return;\n }\n\n for (const field of embeddingFields) {\n await backend.deleteEmbedding({\n graphId: ctx.graphId,\n nodeKind: ctx.nodeKind,\n nodeId: ctx.nodeId,\n fieldPath: field.fieldPath,\n dimensions: field.dimensions,\n metric: field.metric,\n indexType: field.indexType,\n });\n }\n}\n\n// ============================================================\n// Validation Helpers\n// ============================================================\n\n/**\n * Checks if a value is a valid embedding (array of numbers).\n */\nfunction isValidEmbeddingValue(value: unknown): value is readonly number[] {\n if (!Array.isArray(value)) {\n return false;\n }\n return value.every((n) => typeof n === \"number\" && Number.isFinite(n));\n}\n","/**\n * Keeps the fulltext index in sync with node data. One row per node:\n * the values of every `searchable()` field are concatenated (joined by\n * `\\n`) and stored as the indexed `content`, so a single FTS query can\n * match terms spanning multiple source fields — which a per-field\n * layout cannot, since FTS5 / Postgres MATCH require all terms in one\n * indexed document.\n *\n * Sync runs inline in the node-operation call path, so it inherits the\n * caller's transaction context.\n */\nimport { type z } from \"zod\";\n\nimport { type GraphBackend, type TransactionBackend } from \"../backend/types\";\nimport {\n DEFAULT_SEARCHABLE_LANGUAGE,\n getSearchableFields,\n type SearchableFieldInfo,\n} from \"../core/searchable\";\nimport { readOwnProperty } from \"../utils/object\";\n\nexport { getSearchableFields } from \"../core/searchable\";\n\nexport type FulltextSyncContext = Readonly<{\n graphId: string;\n nodeKind: string;\n nodeId: string;\n backend: GraphBackend | TransactionBackend;\n}>;\n\nconst FIELD_SEPARATOR = \"\\n\";\n\n/**\n * Picks a representative language when a node has searchable fields with\n * different language settings. Users who need true per-field\n * multilingual indexing should split the data across node kinds.\n */\nfunction resolveCombinedLanguage(\n fields: readonly SearchableFieldInfo[],\n): string {\n return fields[0]?.metadata.language ?? DEFAULT_SEARCHABLE_LANGUAGE;\n}\n\n/**\n * Computes the combined fulltext content for a node, or `undefined`\n * if the node has no non-empty searchable fields.\n *\n * Shared between `syncFulltext` (per-write) and `rebuildFulltextIndex`\n * (bulk) so the two never drift.\n */\nexport function computeFulltextContent(\n schema: z.ZodType,\n props: Record<string, unknown>,\n): { content: string; language: string } | undefined {\n const searchableFields = getSearchableFields(schema);\n if (searchableFields.length === 0) return undefined;\n\n const parts: string[] = [];\n for (const field of searchableFields) {\n const value = readOwnProperty(props, field.fieldPath);\n if (typeof value === \"string\" && value.length > 0) {\n parts.push(value);\n }\n }\n if (parts.length === 0) return undefined;\n\n return {\n content: parts.join(FIELD_SEPARATOR),\n language: resolveCombinedLanguage(searchableFields),\n };\n}\n\n/**\n * Syncs the fulltext index row after a node create or update.\n *\n * Concatenates the values of all searchable fields into a single content\n * string and upserts it. If every field is empty / undefined, deletes\n * any existing row for the node.\n */\nexport async function syncFulltext(\n ctx: FulltextSyncContext,\n schema: z.ZodType,\n props: Record<string, unknown>,\n): Promise<void> {\n const { backend } = ctx;\n\n if (!backend.upsertFulltext || !backend.deleteFulltext) {\n return;\n }\n\n // `computeFulltextContent` returns `undefined` both when the schema has\n // no `searchable()` fields and when every value is empty — either case\n // means the row should not exist. Delete any stale row only when the\n // schema declares searchable fields (nothing to clean up otherwise).\n const computed = computeFulltextContent(schema, props);\n if (computed !== undefined) {\n await backend.upsertFulltext({\n graphId: ctx.graphId,\n nodeKind: ctx.nodeKind,\n nodeId: ctx.nodeId,\n content: computed.content,\n language: computed.language,\n });\n return;\n }\n if (getSearchableFields(schema).length === 0) return;\n await backend.deleteFulltext({\n graphId: ctx.graphId,\n nodeKind: ctx.nodeKind,\n nodeId: ctx.nodeId,\n });\n}\n\n/**\n * Syncs the fulltext rows for a batch of same-kind node creates through\n * one `upsertFulltextBatch` call (falling back to per-row `upsertFulltext`\n * when the backend lacks the batch primitive). Mirrors `syncFulltext`\n * per row: computed content upserts, empty content deletes any stale row\n * when the schema declares searchable fields.\n */\nexport async function syncFulltextBatchForKind(\n args: Readonly<{\n graphId: string;\n nodeKind: string;\n backend: GraphBackend | TransactionBackend;\n }>,\n schema: z.ZodType,\n items: readonly Readonly<{\n nodeId: string;\n props: Record<string, unknown>;\n }>[],\n): Promise<void> {\n const { graphId, nodeKind, backend } = args;\n if (!backend.upsertFulltext || !backend.deleteFulltext) {\n return;\n }\n\n const rows: { nodeId: string; content: string; language: string }[] = [];\n const emptyContentIds: string[] = [];\n const hasSearchableFields = getSearchableFields(schema).length > 0;\n for (const item of items) {\n const computed = computeFulltextContent(schema, item.props);\n if (computed !== undefined) {\n rows.push({\n nodeId: item.nodeId,\n content: computed.content,\n language: computed.language,\n });\n } else if (hasSearchableFields) {\n emptyContentIds.push(item.nodeId);\n }\n }\n\n if (rows.length > 0) {\n if (backend.upsertFulltextBatch === undefined) {\n for (const row of rows) {\n await backend.upsertFulltext({\n graphId,\n nodeKind,\n nodeId: row.nodeId,\n content: row.content,\n language: row.language,\n });\n }\n } else {\n await backend.upsertFulltextBatch({ graphId, nodeKind, rows });\n }\n }\n\n if (emptyContentIds.length > 0) {\n if (backend.deleteFulltextBatch === undefined) {\n for (const nodeId of emptyContentIds) {\n await backend.deleteFulltext({ graphId, nodeKind, nodeId });\n }\n } else {\n await backend.deleteFulltextBatch({\n graphId,\n nodeKind,\n nodeIds: emptyContentIds,\n });\n }\n }\n}\n\n/**\n * Deletes the fulltext row for a node.\n * Called on soft-delete; hard-delete is handled by the backend cascade.\n */\nexport async function deleteNodeFulltext(\n ctx: FulltextSyncContext,\n schema: z.ZodType,\n): Promise<void> {\n const { backend } = ctx;\n\n if (!backend.deleteFulltext) {\n return;\n }\n\n if (getSearchableFields(schema).length === 0) {\n return;\n }\n\n await backend.deleteFulltext({\n graphId: ctx.graphId,\n nodeKind: ctx.nodeKind,\n nodeId: ctx.nodeId,\n });\n}\n","/**\n * Composable node write steps — the integrity side effects that every node\n * mutation must apply, extracted so there is a single implementation instead of\n * one hand-stitched copy per write path.\n *\n * A node mutation is the core row write plus a fixed set of side effects:\n * uniqueness entries, embedding sync, fulltext sync, and — for deletes —\n * delete-behavior enforcement over connected edges. These steps are shared by\n * the canonical collection operations (create / update / delete) and by\n * provenance retraction, which drives the same close/reopen of a fact node's\n * currency but skips delete-behavior enforcement so every connected edge\n * survives for a later reopen.\n *\n * The steps assume they run inside a write transaction (see\n * {@link runInWriteTransaction}); they perform no transaction management of\n * their own.\n */\nimport { type z } from \"zod\";\n\nimport {\n type GraphBackend,\n type LiveNodeRow,\n type NodeRow,\n rowPropsToObject,\n type TombstonedNodeRow,\n type TransactionBackend,\n} from \"../../backend/types\";\nimport {\n type DeleteBehavior,\n type JsonValue,\n type UniqueConstraint,\n} from \"../../core/types\";\nimport {\n ConfigurationError,\n RestrictedDeleteError,\n ValidationError,\n} from \"../../errors\";\nimport { validateNodeProps } from \"../../errors/validation\";\nimport type { CompiledSelectSql } from \"../../query/sql-intent\";\nimport { type KindRegistry } from \"../../registry/kind-registry\";\nimport { canonicalEqual } from \"../../schema/canonical\";\nimport { assertsStoredLowerBound } from \"../../utils/date\";\nimport { purgeEdgeClaims } from \"../claims/edge-claims\";\nimport {\n alreadyAppliedRowWrite,\n createUniquenessContext,\n deleteUniquenessEntries,\n hardDeleteClaimsByNodeIds,\n type NodeClaimContext,\n planNodeClaimReinsert,\n planNodeClaimUpdate,\n type UniquenessUpdatePlan,\n withNodeClaimTransition,\n withNodeCreateClaimsBatch,\n} from \"../claims/node-claims\";\nimport { validateResolvedNodeClaims } from \"../claims/resolved-node-claims\";\nimport {\n deleteNodeEmbeddings,\n getEmbeddingFields,\n syncEmbeddings,\n syncEmbeddingsBatchForKind,\n} from \"../embedding-sync\";\nimport {\n deleteNodeFulltext,\n getSearchableFields,\n syncFulltext,\n syncFulltextBatchForKind,\n} from \"../fulltext-sync\";\nimport { type GraphWriteLock } from \"../recorded-capture/clock\";\n\ntype Backend = GraphBackend | TransactionBackend;\n\n/**\n * The graph-scoped state the node write steps need. `lock` is compile-time\n * evidence that the per-graph write-lock discipline was satisfied BEFORE any\n * row work (see {@link GraphWriteLock}): the pipeline performs no locking of\n * its own, so requiring the token here makes \"sidecar write before lock\" a\n * type error at the call site instead of a lock-order inversion in review.\n *\n * It is the claim seam's context by definition rather than by coincidence: a\n * write path hands the same value to {@link withNodeCreateClaims} and to the\n * sync fans, so the two halves of one insert cannot be given different graphs,\n * registries, or lock evidence.\n */\nexport type NodeWriteContext = NodeClaimContext;\n\n/** Builds a {@link NodeWriteContext} — the one constructor every call site shares. */\nexport function createNodeWriteContext(\n graphId: string,\n registry: KindRegistry,\n lock: GraphWriteLock,\n): NodeWriteContext {\n return { graphId, registry, lock };\n}\n\n/** Whether a delete removes the node (`hard`) or tombstones it (`soft`). */\ntype NodeDeleteMode = \"soft\" | \"hard\";\n\n/**\n * Tunes how a delete treats the node's connected edges.\n *\n * By default delete behavior is enforced: connected edges block a `restrict`\n * node and are removed under `cascade` / `disconnect`. Provenance retraction\n * passes `enforceDeleteBehavior: false` — closing a fact's currency is a\n * belief-status change, not a domain delete, so its edges neither block the\n * close nor get removed; they survive untouched so a later reopen is an exact\n * inverse.\n */\nexport type NodeDeletePolicy = Readonly<{\n enforceDeleteBehavior: boolean;\n}>;\n\nfunction uniquenessContext(ctx: NodeWriteContext, backend: Backend) {\n return createUniquenessContext(ctx.graphId, ctx.registry, backend);\n}\n\n/**\n * The `(graphId, nodeKind, nodeId, backend)` context shared by the embedding and\n * fulltext sync helpers — `EmbeddingSyncContext` and `FulltextSyncContext` are\n * structurally identical, so one builder serves both.\n */\nfunction nodeSyncContext(\n ctx: NodeWriteContext,\n kind: string,\n id: string,\n backend: Backend,\n) {\n return { graphId: ctx.graphId, nodeKind: kind, nodeId: id, backend };\n}\n\n/**\n * Enforces a node's delete behavior against its connected edges: they block\n * the delete (`restrict`) or are removed alongside the node (`cascade` /\n * `disconnect`). Skipped entirely when the caller's {@link NodeDeletePolicy}\n * disables enforcement.\n */\nasync function enforceNodeDeleteBehavior(\n ctx: NodeWriteContext,\n args: Readonly<{\n kind: string;\n id: string;\n mode: NodeDeleteMode;\n onDelete: DeleteBehavior | undefined;\n }>,\n backend: Backend,\n policy?: NodeDeletePolicy,\n): Promise<void> {\n if (policy?.enforceDeleteBehavior === false) return;\n const behavior = args.onDelete ?? \"restrict\";\n const connectedEdges = await backend.findEdgesConnectedTo({\n graphId: ctx.graphId,\n nodeKind: args.kind,\n nodeId: args.id,\n });\n\n if (connectedEdges.length === 0) return;\n\n switch (behavior) {\n case \"restrict\": {\n throw new RestrictedDeleteError({\n nodeKind: args.kind,\n nodeId: args.id,\n edgeCount: connectedEdges.length,\n edgeKinds: [...new Set(connectedEdges.map((edge) => edge.kind))],\n });\n }\n\n case \"cascade\":\n case \"disconnect\": {\n // Both behaviors remove connected edges. \"cascade\" signals intent to\n // remove dependent data; \"disconnect\" signals intent to sever the\n // relationship. The effect is identical because edges cannot exist\n // without both endpoints. One batched statement per bind-budget\n // chunk instead of one statement per edge; the per-edge loop remains\n // for backends without the batch members.\n const connectedEdgeIds = connectedEdges.map((edge) => edge.id);\n const batchDelete =\n args.mode === \"hard\" ?\n backend.hardDeleteEdgesBatch\n : backend.deleteEdgesBatch;\n if (batchDelete === undefined) {\n for (const edge of connectedEdges) {\n await (args.mode === \"hard\" ?\n backend.hardDeleteEdge({ graphId: ctx.graphId, id: edge.id })\n : backend.deleteEdge({ graphId: ctx.graphId, id: edge.id }));\n }\n } else {\n await batchDelete({\n graphId: ctx.graphId,\n ids: connectedEdgeIds,\n });\n }\n // Hard-deleted holders are already takeable through the liveness probe;\n // this is housekeeping so node cascades do not grow edgeClaims forever.\n // Soft-deleted edges retain their rows for resurrection and are not\n // reaped here.\n if (args.mode === \"hard\") {\n await purgeEdgeClaims(backend, ctx.graphId, connectedEdgeIds);\n }\n break;\n }\n }\n}\n\n/** One inserted row, as the post-insert sync fans read it. */\nexport type NodeInsertSyncItem = Readonly<{\n kind: string;\n id: string;\n schema: z.ZodType;\n props: Record<string, unknown>;\n uniqueConstraints: readonly UniqueConstraint[];\n}>;\n\n/**\n * The sync fans that follow a node insert: embedding and fulltext.\n *\n * The claims this row owes are NOT here. They are issued by\n * {@link withNodeCreateClaims}, on the two sides of the insert their placement\n * names — a claim that is the only fence for its axis has to precede the row it\n * gates, and a function that runs entirely after the insert cannot issue one.\n * The fans have no such constraint: they are derived data, they fence nothing,\n * and they stay where they have always been.\n */\nexport async function applyNodeInsertSyncFans(\n ctx: NodeWriteContext,\n args: NodeInsertSyncItem,\n backend: Backend,\n): Promise<void> {\n await Promise.all([\n syncEmbeddings(\n nodeSyncContext(ctx, args.kind, args.id, backend),\n args.schema,\n args.props,\n ),\n syncFulltext(\n nodeSyncContext(ctx, args.kind, args.id, backend),\n args.schema,\n args.props,\n ),\n ]);\n}\n\n/**\n * Batched {@link applyNodeInsertSyncFans}: one embedding batch per (kind,\n * field) and one fulltext batch per kind, instead of the per-row statement fan\n * the single-op path issues.\n */\nexport async function applyNodeInsertSyncFansBatch(\n ctx: NodeWriteContext,\n items: readonly NodeInsertSyncItem[],\n backend: Backend,\n): Promise<void> {\n if (items.length === 0) return;\n\n interface KindGroup {\n schema: z.ZodType;\n rows: { nodeId: string; props: Record<string, unknown> }[];\n }\n const byKind = new Map<string, KindGroup>();\n for (const item of items) {\n const group = byKind.get(item.kind) ?? { schema: item.schema, rows: [] };\n group.rows.push({ nodeId: item.id, props: item.props });\n byKind.set(item.kind, group);\n }\n\n await Promise.all(\n [...byKind.entries()].flatMap(([kind, group]) => {\n const syncArguments = { graphId: ctx.graphId, nodeKind: kind, backend };\n return [\n syncEmbeddingsBatchForKind(syncArguments, group.schema, group.rows),\n syncFulltextBatchForKind(syncArguments, group.schema, group.rows),\n ];\n }),\n );\n}\n\nfunction parseRowProps(row: NodeRow): Record<string, unknown> {\n return rowPropsToObject(row.props);\n}\n\n/**\n * The row a node update targets. A plain update runs live-row side effects\n * (uniqueness diff, embedding/fulltext sync), so it must be handed a\n * {@link LiveNodeRow}; only an explicit `clearDeleted: true` resurrecting\n * upsert may target a possibly-tombstoned row. Encoding the pairing as a\n * union makes \"live-row update pipeline on a tombstoned row\" a type error.\n */\nexport type NodeUpdateTarget =\n | Readonly<{ existing: LiveNodeRow; clearDeleted?: false }>\n | Readonly<{ existing: NodeRow; clearDeleted: true }>;\n\n/**\n * Decides a node update's uniqueness transition, writing nothing.\n *\n * Two shapes behind one seam. A live row diffs its old and new keys; a\n * resurrecting update (`clearDeleted` on a tombstoned row) cannot, because the\n * soft delete already removed its entries — the diff would skip an unchanged\n * key and leave the resurrected node holding NO reservation, so a later create\n * could silently duplicate the value. It re-reserves every applying key\n * instead. Both refuse a conflict before returning, and both hand the caller a\n * plan that only {@link withNodeClaimTransition} may carry out.\n */\nasync function planNodeUpdateUniqueness(\n ctx: NodeWriteContext,\n args: Readonly<{\n existing: NodeRow;\n validatedProps: Record<string, unknown>;\n uniqueConstraints: readonly UniqueConstraint[];\n }>,\n backend: Backend,\n): Promise<UniquenessUpdatePlan> {\n const { kind, id } = args.existing;\n\n if (args.existing.deleted_at !== undefined) {\n return planNodeClaimReinsert(\n uniquenessContext(ctx, backend),\n kind,\n id,\n args.validatedProps,\n args.uniqueConstraints,\n );\n }\n\n return planNodeClaimUpdate(\n uniquenessContext(ctx, backend),\n kind,\n id,\n parseRowProps(args.existing),\n args.validatedProps,\n args.uniqueConstraints,\n );\n}\n\n/**\n * Applies a node update: uniqueness maintenance (diff-based for a live row;\n * check-and-reinsert for a resurrecting update, whose entries the soft delete\n * removed), the core row update, then embedding and fulltext sync. Returns\n * the updated row.\n *\n * ## The row write and its uniqueness transition are ONE unit\n *\n * Both can fail — the row write matches nothing when `expectedValidFrom` or the\n * `deleted_at` fence stopped holding, the uniqueness claim loses a race for a\n * key — and a caller that catches either PER ROW and commits the rest of the\n * transaction (interchange import) must never be left with half of the pair\n * applied. {@link withNodeClaimTransition} owns that sequencing and documents\n * why claim/gate/release is the only order that works; this function just hands\n * it the plan and the write.\n *\n * The embedding and fulltext syncs stay AFTER the gate: they are derived data\n * with no claim to make, so a row write that lands on nothing must not rewrite\n * a fulltext row or re-embed props no row ever received.\n */\nexport async function applyNodeUpdate(\n ctx: NodeWriteContext,\n args: Readonly<{\n schema: z.ZodType;\n validatedProps: Record<string, unknown>;\n uniqueConstraints: readonly UniqueConstraint[];\n validFrom?: string | null;\n validTo?: string;\n clearValidTo?: true;\n /** See {@link UpdateNodeParams.expectedValidFrom}. */\n expectedValidFrom?: string | null;\n }> &\n NodeUpdateTarget,\n backend: Backend,\n): Promise<NodeRow> {\n const { kind, id } = args.existing;\n\n // Read-only phase: decide the sidecar changes, refuse a conflict, write\n // nothing.\n const plan = await planNodeUpdateUniqueness(\n ctx,\n {\n existing: args.existing,\n validatedProps: args.validatedProps,\n uniqueConstraints: args.uniqueConstraints,\n },\n backend,\n );\n\n const updateParams: {\n graphId: string;\n kind: string;\n id: string;\n props: Record<string, unknown>;\n validFrom?: string | null;\n expectedValidFrom?: string | null;\n incrementVersion?: boolean;\n clearDeleted?: boolean;\n } = {\n graphId: ctx.graphId,\n kind,\n id,\n props: args.validatedProps,\n incrementVersion: true,\n };\n if (args.validFrom !== undefined) updateParams.validFrom = args.validFrom;\n // `assertsStoredLowerBound` owns \"does this fence state anything?\" — the same\n // predicate the fence appliers consult, so the step that CARRIES the fence\n // and the seam that VALIDATES it cannot disagree about what an empty fence is.\n if (assertsStoredLowerBound(args)) {\n updateParams.expectedValidFrom = args.expectedValidFrom;\n }\n if (args.clearDeleted) updateParams.clearDeleted = true;\n\n const row = await withNodeClaimTransition(\n uniquenessContext(ctx, backend),\n kind,\n id,\n plan,\n () =>\n backend.updateNode({\n ...updateParams,\n ...(args.clearValidTo === true ? { clearValidTo: true as const }\n : args.validTo === undefined ? {}\n : { validTo: args.validTo }),\n }),\n );\n\n await Promise.all([\n syncEmbeddings(\n nodeSyncContext(ctx, kind, id, backend),\n args.schema,\n args.validatedProps,\n ),\n syncFulltext(\n nodeSyncContext(ctx, kind, id, backend),\n args.schema,\n args.validatedProps,\n ),\n ]);\n\n return row;\n}\n\n/**\n * Applies a node soft delete: delete-behavior enforcement, the tombstone\n * write, then removal of uniqueness entries, embeddings, and fulltext.\n * Requires a {@link LiveNodeRow}: deleting an already-tombstoned row would\n * re-run sidecar cleanup against entries the first delete already removed.\n */\nexport async function applyNodeSoftDelete(\n ctx: NodeWriteContext,\n args: Readonly<{\n existing: LiveNodeRow;\n schema: z.ZodType;\n uniqueConstraints: readonly UniqueConstraint[];\n onDelete: DeleteBehavior | undefined;\n }>,\n backend: Backend,\n policy?: NodeDeletePolicy,\n): Promise<void> {\n const { kind, id } = args.existing;\n await enforceNodeDeleteBehavior(\n ctx,\n { kind, id, mode: \"soft\", onDelete: args.onDelete },\n backend,\n policy,\n );\n await backend.deleteNode({\n graphId: ctx.graphId,\n kind,\n id,\n });\n await deleteUniquenessEntries(\n uniquenessContext(ctx, backend),\n kind,\n id,\n parseRowProps(args.existing),\n args.uniqueConstraints,\n );\n await deleteNodeEmbeddings(\n nodeSyncContext(ctx, kind, id, backend),\n args.schema,\n );\n await deleteNodeFulltext(\n nodeSyncContext(ctx, kind, id, backend),\n args.schema,\n );\n}\n\n/**\n * Applies a node hard delete: delete-behavior enforcement, permanent removal,\n * then embedding cleanup. Uniqueness and fulltext rows are removed by the\n * backend's `hardDeleteNode` cascade; embeddings live in strategy-owned\n * per-`(kind, field)` tables the graph-agnostic cascade cannot reach, so they\n * are cleaned here.\n */\nexport async function applyNodeHardDelete(\n ctx: NodeWriteContext,\n args: Readonly<{\n kind: string;\n id: string;\n schema: z.ZodType;\n onDelete: DeleteBehavior | undefined;\n }>,\n backend: Backend,\n): Promise<void> {\n await enforceNodeDeleteBehavior(\n ctx,\n { kind: args.kind, id: args.id, mode: \"hard\", onDelete: args.onDelete },\n backend,\n );\n await backend.hardDeleteNode({\n graphId: ctx.graphId,\n kind: args.kind,\n id: args.id,\n });\n await deleteNodeEmbeddings(\n nodeSyncContext(ctx, args.kind, args.id, backend),\n args.schema,\n );\n}\n\n/**\n * Reopens a soft-deleted node with its stored props (no merge, no\n * re-validation): re-checks and re-inserts uniqueness entries (the delete\n * removed them), clears the tombstone, then re-syncs embeddings and fulltext.\n * Used by provenance to reinstate a fact whose currency is restored. Returns\n * the reopened row.\n *\n * @throws {UniquenessError} when a unique key the node held was taken by\n * another node while it was tombstoned.\n */\nexport async function applyNodeResurrect(\n ctx: NodeWriteContext,\n args: Readonly<{\n existing: TombstonedNodeRow;\n schema: z.ZodType;\n uniqueConstraints: readonly UniqueConstraint[];\n }>,\n backend: Backend,\n): Promise<NodeRow> {\n const { kind, id } = args.existing;\n const props = parseRowProps(args.existing);\n // One unit, exactly as `applyNodeUpdate`, and for the same reason: the\n // resurrecting UPDATE carries `deleted_at IS NOT NULL`, so a peer that revived\n // this tombstone first makes it match zero rows — and the reservations that\n // revival is entitled to are the peer's, not this caller's.\n // `withNodeClaimTransition` gives them back when the gate refuses.\n const plan = await planNodeClaimReinsert(\n uniquenessContext(ctx, backend),\n kind,\n id,\n props,\n args.uniqueConstraints,\n );\n const row = await withNodeClaimTransition(\n uniquenessContext(ctx, backend),\n kind,\n id,\n plan,\n () =>\n backend.updateNode({\n graphId: ctx.graphId,\n kind,\n id,\n props,\n incrementVersion: true,\n clearDeleted: true,\n }),\n );\n await Promise.all([\n syncEmbeddings(nodeSyncContext(ctx, kind, id, backend), args.schema, props),\n syncFulltext(nodeSyncContext(ctx, kind, id, backend), args.schema, props),\n ]);\n return row;\n}\n\n/**\n * The inputs of one set-based node update: the patch the statement applies and\n * the candidate query that selects the rows it applies to.\n *\n * There is no fence field. `UpdateNodeSetParams` has no `expectedValidFrom`,\n * so a validity lower bound cannot be carried here — it is refused by\n * `NODE_SET_UPDATE_FENCE_APPLIERS` before this step is reached, rather than\n * accepted into a record that would quietly drop it.\n */\nexport type NodeSetUpdateWork = Readonly<{\n kind: string;\n schema: z.ZodType<Record<string, unknown>>;\n uniqueConstraints: readonly UniqueConstraint[];\n patch: Readonly<Record<string, JsonValue>>;\n unsetProperties: readonly string[];\n candidateIds: CompiledSelectSql;\n candidateIdColumn: string;\n}>;\n\n/** What a set update reports: how many live rows it rewrote. */\nexport type NodeSetUpdateResult = Readonly<{ affectedCount: number }>;\n\n/**\n * Applies a set-based node update: one UPDATE over every candidate row, then a\n * full rebuild of the sidecars those after-images oblige.\n *\n * ## Why the capability refusals are HERE and not at the entry point\n *\n * The caller probes the backend it was handed before opening the transaction;\n * these four probe the transaction target, which is a different object — a\n * backend may hand out a transaction handle that implements less than the\n * top-level one. They keep the entry point's error codes, so a caller that\n * refuses before the transaction and one that refuses inside it report the\n * same class.\n *\n * ## Why the order is row -> probe -> drop -> re-claim\n *\n * Unlike every other node write, the uniqueness claim happens AFTER the row\n * write: the statement rewrites whole rows without reading their before-images,\n * so the keys to release are not knowable until the after-images come back.\n * The cross-kind re-check below is what makes that safe — it re-probes every\n * changed key across the constraint's scope and refuses the whole transaction\n * before a single entry is dropped. The per-graph write fence (which a\n * shared-scope constraint forces this write to take) is what keeps a concurrent\n * claim from landing between the probe and the reinsert.\n */\nexport async function applyNodeSetUpdate(\n ctx: NodeWriteContext,\n args: NodeSetUpdateWork,\n backend: Backend,\n): Promise<NodeSetUpdateResult> {\n const { kind, schema, uniqueConstraints } = args;\n const updateNodeSet = backend.updateNodeSet;\n if (updateNodeSet === undefined) {\n throw new ConfigurationError(\n \"The transaction backend does not support set-based node updates\",\n { code: \"SET_UPDATE_UNSUPPORTED\", kind },\n );\n }\n if (\n uniqueConstraints.length > 0 &&\n (backend.hardDeleteUniquesByNodeIds === undefined ||\n backend.insertUniqueBatch === undefined ||\n backend.checkUniqueBatch === undefined)\n ) {\n throw new ConfigurationError(\n \"The transaction backend lacks batched uniqueness operations\",\n { code: \"SET_UPDATE_UNIQUENESS_UNSUPPORTED\", kind },\n );\n }\n if (\n getSearchableFields(schema).length > 0 &&\n (backend.upsertFulltext === undefined ||\n backend.deleteFulltext === undefined ||\n backend.upsertFulltextBatch === undefined ||\n backend.deleteFulltextBatch === undefined)\n ) {\n throw new ConfigurationError(\n \"The transaction backend lacks batched fulltext operations\",\n { code: \"SET_UPDATE_FULLTEXT_UNSUPPORTED\", kind },\n );\n }\n if (\n getEmbeddingFields(schema).length > 0 &&\n (backend.upsertEmbedding === undefined ||\n backend.deleteEmbedding === undefined ||\n backend.upsertEmbeddingBatch === undefined ||\n backend.deleteEmbeddingBatch === undefined)\n ) {\n throw new ConfigurationError(\n \"The transaction backend lacks batched vector operations\",\n { code: \"SET_UPDATE_VECTOR_UNSUPPORTED\", kind },\n );\n }\n const result = await updateNodeSet({\n graphId: ctx.graphId,\n kind,\n patch: args.patch,\n unsetProperties: args.unsetProperties,\n candidateIds: args.candidateIds,\n candidateIdColumn: args.candidateIdColumn,\n });\n if (result.affectedCount === 0) return { affectedCount: 0 };\n\n const sidecarItems = result.rows.map((row) => {\n const props = rowPropsToObject(row.props);\n const validatedProps = validateNodeProps(schema, props, {\n kind,\n operation: \"update\",\n id: row.id,\n });\n if (!canonicalEqual(validatedProps, props)) {\n throw new ValidationError(\n `Set update would persist a non-canonical ${kind} row`,\n {\n entityType: \"node\",\n kind,\n operation: \"update\",\n id: row.id,\n issues: [\n {\n path: \"props\",\n message: \"The complete row requires schema normalization\",\n },\n ],\n },\n );\n }\n return {\n kind,\n id: row.id,\n schema,\n props: validatedProps,\n uniqueConstraints,\n };\n });\n\n const claimItems = sidecarItems.map((item) => ({\n kind: item.kind,\n id: item.id,\n props: item.props,\n constraints: item.uniqueConstraints,\n }));\n if (uniqueConstraints.length > 0) {\n // The RESOLVED-SET verdict, not a row-at-a-time one: the statement rewrote\n // every candidate at once, so a swap or a handoff of one key between two\n // rows it touched is legal in the final state and refused by every\n // intermediate one. One owner of that verdict, shared with the graph merge.\n await validateResolvedNodeClaims(\n createUniquenessContext(ctx.graphId, ctx.registry, backend),\n claimItems,\n [],\n );\n await hardDeleteClaimsByNodeIds(\n uniquenessContext(ctx, backend),\n kind,\n result.rows.map((row) => row.id),\n );\n }\n // The same claim seam every create-shaped writer uses, with an ALREADY-APPLIED\n // row write as its gate: the rows landed above and the old claims were\n // hard-deleted, so what is left is a re-claim and there is no row write left\n // for a claim to precede. Inverting it would mean reserving keys the update has\n // not written. What covers this site on a backend that cannot fence is stated\n // per kind shape: a kind whose scope spans siblings declares\n // `fencesConstraintProbe` at the entry point and is refused before this body\n // runs; a kind-scoped one is not refused, before or after — its\n // delete-then-rebuild window is unchanged in shape and extent here.\n await withNodeCreateClaimsBatch(\n ctx,\n claimItems,\n backend,\n alreadyAppliedRowWrite,\n );\n await applyNodeInsertSyncFansBatch(ctx, sidecarItems, backend);\n return { affectedCount: result.affectedCount };\n}\n"]}