@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,132 +0,0 @@
1
- 'use strict';
2
-
3
- var chunkOJHD3LXJ_cjs = require('./chunk-OJHD3LXJ.cjs');
4
-
5
- // src/backend/types.ts
6
- function normalizeGraphAnalyticsCapabilities(capabilities) {
7
- if (capabilities.graphAnalytics?.supported !== true || capabilities.transactions && capabilities.windowFunctions && capabilities.returning !== false) {
8
- return capabilities;
9
- }
10
- return {
11
- ...capabilities,
12
- graphAnalytics: { ...capabilities.graphAnalytics, supported: false }
13
- };
14
- }
15
- function rowPropsToObject(props) {
16
- return typeof props === "string" ? JSON.parse(props) : props;
17
- }
18
- function rowPropsToJsonText(props) {
19
- return typeof props === "string" ? props : JSON.stringify(props);
20
- }
21
- function isLiveNodeRow(row) {
22
- return row.deleted_at === void 0;
23
- }
24
- function isTombstonedNodeRow(row) {
25
- return row.deleted_at !== void 0;
26
- }
27
- var INTERNAL_TEMPORARY_WRITES = chunkOJHD3LXJ_cjs.typeGraphGlobalSymbol(
28
- "internal-temporary-writes-v1"
29
- );
30
- var DATABASE_EXTENSION_NAMES = ["pg_trgm", "vector"];
31
- function createTransactionReadBackend(backend) {
32
- const getNodes = backend.getNodes;
33
- const getEdges = backend.getEdges;
34
- const compileSql = backend.compileSql;
35
- return Object.freeze({
36
- dialect: backend.dialect,
37
- capabilities: backend.capabilities,
38
- ...backend.tableNames === void 0 ? {} : { tableNames: backend.tableNames },
39
- ...backend.fulltextStrategy === void 0 ? {} : { fulltextStrategy: backend.fulltextStrategy },
40
- ...backend.vectorStrategy === void 0 ? {} : { vectorStrategy: backend.vectorStrategy },
41
- getNode: (graphId, kind, id) => backend.getNode(graphId, kind, id),
42
- ...getNodes === void 0 ? {} : {
43
- getNodes: (graphId, kind, ids) => getNodes(graphId, kind, ids)
44
- },
45
- getEdge: (graphId, id) => backend.getEdge(graphId, id),
46
- ...getEdges === void 0 ? {} : {
47
- getEdges: (graphId, ids) => getEdges(graphId, ids)
48
- },
49
- countEdgesFrom: (params) => backend.countEdgesFrom(params),
50
- edgeExistsBetween: (params) => backend.edgeExistsBetween(params),
51
- findEdgesConnectedTo: (params) => backend.findEdgesConnectedTo(params),
52
- findNodesByKind: (params) => backend.findNodesByKind(params),
53
- countNodesByKind: (params) => backend.countNodesByKind(params),
54
- findEdgesByKind: (params) => backend.findEdgesByKind(params),
55
- countEdgesByKind: (params) => backend.countEdgesByKind(params),
56
- getActiveSchema: (graphId) => backend.getActiveSchema(graphId),
57
- getSchemaVersion: (graphId, version) => backend.getSchemaVersion(graphId, version),
58
- execute: (query) => backend.execute(query),
59
- ...compileSql === void 0 ? {} : { compileSql: (query) => compileSql(query) }
60
- });
61
- }
62
- async function closeAfterFailure(resource, provisioningError) {
63
- try {
64
- await resource.close();
65
- } catch {
66
- }
67
- throw provisioningError;
68
- }
69
- async function runOptionallyInTransaction(backend, fn, options) {
70
- if ("transaction" in backend && backend.capabilities.transactions) {
71
- return backend.transaction(
72
- (tx) => fn(tx, { atomic: true }),
73
- options?.transaction
74
- );
75
- }
76
- return fn(options?.fallback ?? backend, { atomic: false });
77
- }
78
- var SQLITE_MAX_BIND_PARAMETERS = 999;
79
- var MODERN_SQLITE_MAX_BIND_PARAMETERS = 32766;
80
- var D1_MAX_BIND_PARAMETERS = 100;
81
- var DURABLE_OBJECT_MAX_BIND_PARAMETERS = 100;
82
- var POSTGRES_MAX_BIND_PARAMETERS = 65533;
83
- var SQLITE_CAPABILITIES = Object.freeze({
84
- transactions: true,
85
- // SQLite supports transactions
86
- windowFunctions: true,
87
- // SQLite has supported window functions since 3.25.0
88
- clearValidTo: true,
89
- returning: true,
90
- // SQLite has supported RETURNING since 3.35.0
91
- // The bundled schema ships both claim relations and the shared operation
92
- // backend implements every claim member.
93
- constraintClaims: true,
94
- maxBindParameters: SQLITE_MAX_BIND_PARAMETERS,
95
- // Generic SQLite builds do not guarantee ENABLE_MATH_FUNCTIONS. The local
96
- // better-sqlite3 factory overrides this flag for its bundled build contract.
97
- graphAnalytics: Object.freeze({ supported: true, mathFunctions: false })
98
- });
99
- var POSTGRES_CAPABILITIES = Object.freeze({
100
- transactions: true,
101
- // PostgreSQL supports transactions
102
- windowFunctions: true,
103
- // PostgreSQL supports ROW_NUMBER() and related windows
104
- clearValidTo: true,
105
- returning: true,
106
- // PostgreSQL has supported RETURNING since 8.2
107
- // The bundled schema ships both claim relations and the shared operation
108
- // backend implements every claim member.
109
- constraintClaims: true,
110
- maxBindParameters: POSTGRES_MAX_BIND_PARAMETERS,
111
- graphAnalytics: Object.freeze({ supported: true, mathFunctions: true })
112
- });
113
-
114
- exports.D1_MAX_BIND_PARAMETERS = D1_MAX_BIND_PARAMETERS;
115
- exports.DATABASE_EXTENSION_NAMES = DATABASE_EXTENSION_NAMES;
116
- exports.DURABLE_OBJECT_MAX_BIND_PARAMETERS = DURABLE_OBJECT_MAX_BIND_PARAMETERS;
117
- exports.INTERNAL_TEMPORARY_WRITES = INTERNAL_TEMPORARY_WRITES;
118
- exports.MODERN_SQLITE_MAX_BIND_PARAMETERS = MODERN_SQLITE_MAX_BIND_PARAMETERS;
119
- exports.POSTGRES_CAPABILITIES = POSTGRES_CAPABILITIES;
120
- exports.POSTGRES_MAX_BIND_PARAMETERS = POSTGRES_MAX_BIND_PARAMETERS;
121
- exports.SQLITE_CAPABILITIES = SQLITE_CAPABILITIES;
122
- exports.SQLITE_MAX_BIND_PARAMETERS = SQLITE_MAX_BIND_PARAMETERS;
123
- exports.closeAfterFailure = closeAfterFailure;
124
- exports.createTransactionReadBackend = createTransactionReadBackend;
125
- exports.isLiveNodeRow = isLiveNodeRow;
126
- exports.isTombstonedNodeRow = isTombstonedNodeRow;
127
- exports.normalizeGraphAnalyticsCapabilities = normalizeGraphAnalyticsCapabilities;
128
- exports.rowPropsToJsonText = rowPropsToJsonText;
129
- exports.rowPropsToObject = rowPropsToObject;
130
- exports.runOptionallyInTransaction = runOptionallyInTransaction;
131
- //# sourceMappingURL=chunk-GHZPZNFC.cjs.map
132
- //# sourceMappingURL=chunk-GHZPZNFC.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/backend/types.ts"],"names":["typeGraphGlobalSymbol"],"mappings":";;;;;AAyVO,SAAS,oCACd,YAAA,EACqB;AACrB,EAAA,IACE,YAAA,CAAa,cAAA,EAAgB,SAAA,KAAc,IAAA,IAC1C,YAAA,CAAa,gBACZ,YAAA,CAAa,eAAA,IACb,YAAA,CAAa,SAAA,KAAc,KAAA,EAC7B;AACA,IAAA,OAAO,YAAA;AAAA,EACT;AACA,EAAA,OAAO;AAAA,IACL,GAAG,YAAA;AAAA,IACH,gBAAgB,EAAE,GAAG,YAAA,CAAa,cAAA,EAAgB,WAAW,KAAA;AAAM,GACrE;AACF;AAiBO,SAAS,iBAAiB,KAAA,EAA0C;AACzE,EAAA,OAAO,OAAO,KAAA,KAAU,QAAA,GACnB,IAAA,CAAK,KAAA,CAAM,KAAK,CAAA,GACjB,KAAA;AACN;AAGO,SAAS,mBAAmB,KAAA,EAAyB;AAC1D,EAAA,OAAO,OAAO,KAAA,KAAU,QAAA,GAAW,KAAA,GAAQ,IAAA,CAAK,UAAU,KAAK,CAAA;AACjE;AAkCO,SAAS,cAAc,GAAA,EAAkC;AAC9D,EAAA,OAAO,IAAI,UAAA,KAAe,MAAA;AAC5B;AAEO,SAAS,oBAAoB,GAAA,EAAwC;AAC1E,EAAA,OAAO,IAAI,UAAA,KAAe,MAAA;AAC5B;AAqpCO,IAAM,yBAAA,GAA2CA,uCAAA;AAAA,EACtD;AACF;AAuDO,IAAM,wBAAA,GAA2B,CAAC,SAAA,EAAW,QAAQ;AA05CrD,SAAS,6BACd,OAAA,EACwB;AACxB,EAAA,MAAM,WAAW,OAAA,CAAQ,QAAA;AACzB,EAAA,MAAM,WAAW,OAAA,CAAQ,QAAA;AACzB,EAAA,MAAM,aAAa,OAAA,CAAQ,UAAA;AAE3B,EAAA,OAAO,OAAO,MAAA,CAAO;AAAA,IACnB,SAAS,OAAA,CAAQ,OAAA;AAAA,IACjB,cAAc,OAAA,CAAQ,YAAA;AAAA,IACtB,GAAI,QAAQ,UAAA,KAAe,MAAA,GACzB,EAAC,GACD,EAAE,UAAA,EAAY,OAAA,CAAQ,UAAA,EAAW;AAAA,IACnC,GAAI,QAAQ,gBAAA,KAAqB,MAAA,GAC/B,EAAC,GACD,EAAE,gBAAA,EAAkB,OAAA,CAAQ,gBAAA,EAAiB;AAAA,IAC/C,GAAI,QAAQ,cAAA,KAAmB,MAAA,GAC7B,EAAC,GACD,EAAE,cAAA,EAAgB,OAAA,CAAQ,cAAA,EAAe;AAAA,IAC3C,OAAA,EAAS,CAAC,OAAA,EAAS,IAAA,EAAM,OAAO,OAAA,CAAQ,OAAA,CAAQ,OAAA,EAAS,IAAA,EAAM,EAAE,CAAA;AAAA,IACjE,GAAI,QAAA,KAAa,MAAA,GACf,EAAC,GACD;AAAA,MACE,QAAA,EAAU,CAAC,OAAA,EAAiB,IAAA,EAAc,QACxC,QAAA,CAAS,OAAA,EAAS,MAAM,GAAG;AAAA,KAC/B;AAAA,IACF,SAAS,CAAC,OAAA,EAAS,OAAO,OAAA,CAAQ,OAAA,CAAQ,SAAS,EAAE,CAAA;AAAA,IACrD,GAAI,QAAA,KAAa,MAAA,GACf,EAAC,GACD;AAAA,MACE,UAAU,CAAC,OAAA,EAAiB,GAAA,KAC1B,QAAA,CAAS,SAAS,GAAG;AAAA,KACzB;AAAA,IACF,cAAA,EAAgB,CAAC,MAAA,KAAW,OAAA,CAAQ,eAAe,MAAM,CAAA;AAAA,IACzD,iBAAA,EAAmB,CAAC,MAAA,KAAW,OAAA,CAAQ,kBAAkB,MAAM,CAAA;AAAA,IAC/D,oBAAA,EAAsB,CAAC,MAAA,KAAW,OAAA,CAAQ,qBAAqB,MAAM,CAAA;AAAA,IACrE,eAAA,EAAiB,CAAC,MAAA,KAAW,OAAA,CAAQ,gBAAgB,MAAM,CAAA;AAAA,IAC3D,gBAAA,EAAkB,CAAC,MAAA,KAAW,OAAA,CAAQ,iBAAiB,MAAM,CAAA;AAAA,IAC7D,eAAA,EAAiB,CAAC,MAAA,KAAW,OAAA,CAAQ,gBAAgB,MAAM,CAAA;AAAA,IAC3D,gBAAA,EAAkB,CAAC,MAAA,KAAW,OAAA,CAAQ,iBAAiB,MAAM,CAAA;AAAA,IAC7D,eAAA,EAAiB,CAAC,OAAA,KAAY,OAAA,CAAQ,gBAAgB,OAAO,CAAA;AAAA,IAC7D,kBAAkB,CAAC,OAAA,EAAS,YAC1B,OAAA,CAAQ,gBAAA,CAAiB,SAAS,OAAO,CAAA;AAAA,IAC3C,OAAA,EAAS,CAAI,KAAA,KAA2B,OAAA,CAAQ,QAAW,KAAK,CAAA;AAAA,IAChE,GAAI,UAAA,KAAe,MAAA,GACjB,EAAC,GACD,EAAE,UAAA,EAAY,CAAC,KAAA,KAAuB,UAAA,CAAW,KAAK,CAAA;AAAE,GAC3D,CAAA;AACH;AAMA,eAAsB,iBAAA,CACpB,UACA,iBAAA,EACgB;AAChB,EAAA,IAAI;AACF,IAAA,MAAM,SAAS,KAAA,EAAM;AAAA,EACvB,CAAA,CAAA,MAAQ;AAAA,EAIR;AACA,EAAA,MAAM,iBAAA;AACR;AA0CA,eAAsB,0BAAA,CACpB,OAAA,EACA,EAAA,EAIA,OAAA,EACY;AACZ,EAAA,IAAI,aAAA,IAAiB,OAAA,IAAW,OAAA,CAAQ,YAAA,CAAa,YAAA,EAAc;AACjE,IAAA,OAAO,OAAA,CAAQ,WAAA;AAAA,MACb,CAAC,EAAA,KAAO,EAAA,CAAG,IAAI,EAAE,MAAA,EAAQ,MAAM,CAAA;AAAA,MAC/B,OAAA,EAAS;AAAA,KACX;AAAA,EACF;AACA,EAAA,OAAO,GAAG,OAAA,EAAS,QAAA,IAAY,SAAS,EAAE,MAAA,EAAQ,OAAO,CAAA;AAC3D;AAigBO,IAAM,0BAAA,GAA6B;AAOnC,IAAM,iCAAA,GAAoC;AAO1C,IAAM,sBAAA,GAAyB;AAO/B,IAAM,kCAAA,GAAqC;AAO3C,IAAM,4BAAA,GAA+B;AAKrC,IAAM,mBAAA,GAA2C,OAAO,MAAA,CAAO;AAAA,EACpE,YAAA,EAAc,IAAA;AAAA;AAAA,EACd,eAAA,EAAiB,IAAA;AAAA;AAAA,EACjB,YAAA,EAAc,IAAA;AAAA,EACd,SAAA,EAAW,IAAA;AAAA;AAAA;AAAA;AAAA,EAGX,gBAAA,EAAkB,IAAA;AAAA,EAClB,iBAAA,EAAmB,0BAAA;AAAA;AAAA;AAAA,EAGnB,cAAA,EAAgB,OAAO,MAAA,CAAO,EAAE,WAAW,IAAA,EAAM,aAAA,EAAe,OAAO;AACzE,CAAC;AAKM,IAAM,qBAAA,GAA6C,OAAO,MAAA,CAAO;AAAA,EACtE,YAAA,EAAc,IAAA;AAAA;AAAA,EACd,eAAA,EAAiB,IAAA;AAAA;AAAA,EACjB,YAAA,EAAc,IAAA;AAAA,EACd,SAAA,EAAW,IAAA;AAAA;AAAA;AAAA;AAAA,EAGX,gBAAA,EAAkB,IAAA;AAAA,EAClB,iBAAA,EAAmB,4BAAA;AAAA,EACnB,cAAA,EAAgB,OAAO,MAAA,CAAO,EAAE,WAAW,IAAA,EAAM,aAAA,EAAe,MAAM;AACxE,CAAC","file":"chunk-GHZPZNFC.cjs","sourcesContent":["/**\n * Backend interface types for TypeGraph storage.\n *\n * The backend abstracts database operations, allowing different\n * SQL implementations (SQLite, PostgreSQL) behind a common interface.\n */\nimport {\n type Cardinality,\n type IndexEntity,\n type JsonValue,\n type KindEntity,\n type TemporalMode,\n} from \"../core/types\";\nimport { type SqlTableNames } from \"../query/compiler/schema\";\nimport { type FulltextStrategy } from \"../query/dialect/fulltext-strategy\";\nimport {\n type VectorSlot,\n type VectorStrategy,\n} from \"../query/dialect/vector-strategy\";\nimport { type SqlFragment } from \"../query/sql-fragment\";\nimport {\n type CompiledRowsSql,\n type CompiledSelectSql,\n type CompiledStatementSql,\n type CompiledTemporaryStatementSql,\n} from \"../query/sql-intent\";\nimport { type SerializedSchema } from \"../schema/types\";\nimport { typeGraphGlobalSymbol } from \"../utils/global-symbol\";\n\n// ============================================================\n// Vector Search Types\n// ============================================================\n\n/**\n * Supported vector similarity metrics.\n */\nexport type VectorMetric = \"cosine\" | \"l2\" | \"inner_product\";\n\n/**\n * Supported vector index types.\n */\nexport type VectorIndexType = \"hnsw\" | \"ivfflat\" | \"none\";\n\n/**\n * The mechanism a strategy uses to combine a row filter with an *approximate*\n * (ANN) vector search.\n *\n * Every approximate search TypeGraph issues carries at least one filter: the\n * liveness predicate that excludes soft-deleted and out-of-validity rows. Add\n * a `.where(...)` predicate and it narrows further. Engines differ in where\n * they apply it relative to the index traversal:\n *\n * - `\"filter-pushdown\"` — the filter constrains the ANN candidate set itself.\n * sqlite-vec's `vec0` KNN accepts primary-key `IN (SELECT …)` pushdown.\n * - `\"iterative-scan\"` — the engine re-enters the index, gathering more\n * candidates until `LIMIT` rows survive the filter *or* it hits its own scan\n * bound. pgvector >= 0.8 (`hnsw.iterative_scan` / `ivfflat.iterative_scan`,\n * applied automatically by the backend).\n * - `\"post-filter\"` — the engine retrieves a fixed multiple of the page from\n * the ANN index and applies the filter afterwards. libSQL's DiskANN\n * `vector_top_k` is a table function with no filter pushdown, so this is the\n * only shape available to it.\n *\n * This names what the strategy *asks the engine to do*. Whether the engine can\n * honor it — and whether honoring it is enough to fill a page — is\n * {@link FilteredApproximateSearch.guaranteesFullPage}.\n */\nexport type FilteredApproximateSearchMode =\n \"filter-pushdown\" | \"iterative-scan\" | \"post-filter\";\n\n/**\n * How a filtered approximate (ANN) vector search behaves, and whether it can\n * silently return fewer rows than `limit` while more matches exist.\n *\n * Read `guaranteesFullPage` — not `mode` — to decide whether a short page is\n * possible. Only `\"filter-pushdown\"` guarantees a full page:\n *\n * - **sqlite-vec** pushes the filter into the KNN. Exact.\n * - **pgvector** re-enters the index, but the iterative scan stops at\n * `hnsw.max_scan_tuples` / `ivfflat.max_probes`, and on **pgvector < 0.8**\n * there is no iterative scan at all — the backend detects that at runtime,\n * warns once, and the search stays `ef_search`-bounded, behaving like\n * `\"post-filter\"`. Neither case is visible in a statically-declared `mode`,\n * which is exactly why the guarantee is a separate field.\n * - **libSQL** over-fetches a fixed multiple of the page and filters\n * afterwards, so it under-fills once more than that headroom is filtered out.\n *\n * A store with heavy tombstone drift — routine in a temporal store — is what\n * turns a short page from a theoretical caveat into an observed one. Exact\n * (`approximate: false`) searches are unaffected on every engine: they scan, so\n * the filter reaches every row.\n */\nexport type FilteredApproximateSearch = Readonly<{\n mode: FilteredApproximateSearchMode;\n /**\n * `true` only when a filtered approximate search is guaranteed to return\n * `limit` rows whenever `limit` matching rows exist — no engine-side scan\n * bound, no version dependence, no over-fetch headroom to exhaust.\n */\n guaranteesFullPage: boolean;\n}>;\n\n/**\n * Whether an engine exposes a PER-SEARCH knob for the ANN candidate frontier —\n * the parameter {@link VectorSearchParams.efSearch} maps onto.\n *\n * Declared, never inferred: `efSearch` is an accepted option, so a backend that\n * cannot apply it must refuse it rather than ignore it (AGENTS.md contract\n * discipline). This capability is what the shared refusal predicate reads, and\n * what a caller reads to know whether passing `efSearch` will be honored.\n *\n * - **pgvector**: `hnsw.ef_search`, applied per search with `SET LOCAL` inside\n * the search's own transaction — hence `requiresTransactionScope`, since a\n * session-wide `SET` would leak the override into concurrent searches.\n * - **sqlite-vec**: `tunable: false`. A vec0 KNN takes `k` and nothing else;\n * there is no frontier to widen.\n * - **libSQL DiskANN**: `tunable: false`. `vector_top_k(idx, q, k)` is a table\n * function with no per-query parameters; DiskANN's `search_l` is fixed at\n * index-creation time.\n */\nexport type VectorSearchFrontierTuning =\n | Readonly<{\n tunable: true;\n /** Engine-native parameter `efSearch` maps to (`\"hnsw.ef_search\"`). */\n parameter: string;\n /**\n * The one index type whose search honors the parameter. `efSearch` on a\n * slot of any other index type is refused, not ignored: an IVFFlat or\n * brute-force slot would silently discard it.\n */\n indexType: VectorIndexType;\n /**\n * `true` when applying the parameter needs a transaction to scope it to\n * the one search. A backend that reports `transactions: false` refuses\n * `efSearch` rather than leaking a session-wide setting.\n */\n requiresTransactionScope: boolean;\n }>\n | Readonly<{\n tunable: false;\n /**\n * Why this engine has no per-search frontier knob — surfaced in the\n * refusal's details so the state is named rather than implied.\n */\n reason: string;\n }>;\n\n/**\n * Vector search capabilities.\n */\nexport type VectorCapabilities = Readonly<{\n /** Whether the backend supports vector operations */\n supported: boolean;\n /** Supported similarity metrics */\n metrics: readonly VectorMetric[];\n /** Supported index types */\n indexTypes: readonly VectorIndexType[];\n /** Maximum dimensions supported */\n maxDimensions: number;\n /**\n * How a filtered approximate search bounds recall — and whether it can\n * silently return a short page. See {@link FilteredApproximateSearch}.\n *\n * Required, so a new vector strategy cannot omit the declaration and inherit\n * an engine promise it does not keep.\n */\n filteredApproximateSearch: FilteredApproximateSearch;\n /**\n * Whether a per-search ANN frontier override (`efSearch`) can be applied.\n * See {@link VectorSearchFrontierTuning}.\n *\n * Required, so a new vector strategy must state whether it honors the\n * option instead of inheriting silence — the exact defect this closes.\n */\n searchFrontierTuning: VectorSearchFrontierTuning;\n}>;\n\n// ============================================================\n// Fulltext Search Types\n// ============================================================\n\n/**\n * Query modes for fulltext search.\n *\n * - \"websearch\": Google-style syntax (quoted phrases, +required, -excluded).\n * Postgres: `websearch_to_tsquery`. SQLite: translated to FTS5 MATCH.\n * - \"phrase\": treats the whole query as a phrase.\n * Postgres: `phraseto_tsquery`. SQLite: FTS5 `\"...\"` phrase.\n * - \"plain\": splits on whitespace and ANDs terms.\n * Postgres: `plainto_tsquery`. SQLite: default FTS5 AND.\n * - \"raw\": dialect-native syntax passed through unchanged.\n */\nexport type FulltextQueryMode = \"websearch\" | \"phrase\" | \"plain\" | \"raw\";\n\n/**\n * Fulltext search capabilities declared by a backend.\n */\nexport type FulltextCapabilities = Readonly<{\n /** Whether the backend supports fulltext operations */\n supported: boolean;\n /**\n * Language / tokenizer names understood by this backend.\n * Postgres: installed regconfigs (english, simple, ...).\n * SQLite FTS5: tokenizer names (unicode61, porter, trigram).\n */\n languages: readonly string[];\n /** Whether phrase queries are supported. */\n phraseQueries: boolean;\n /** Whether prefix (`foo*`) queries are supported. */\n prefixQueries: boolean;\n /** Whether highlighting / snippets are supported. */\n highlighting: boolean;\n}>;\n\n/**\n * Whole-graph analytics capabilities declared by a backend.\n *\n * `supported` means the backend can pin one transactional connection while\n * an algorithm creates, updates, and drops session-local temporary state.\n * `mathFunctions` records whether transcendental SQL functions needed by\n * deferred algorithms such as MCL/spectral are available; exact WCC does not\n * require them.\n */\nexport type GraphAnalyticsCapabilities = Readonly<{\n supported: boolean;\n mathFunctions: boolean;\n}>;\n\n/**\n * How much of the contribution health lifecycle a backend can serve.\n *\n * The three rungs escalate — probe (read-only) → repair\n * (non-destructive) → rebuild (destructive) — and each is declared\n * separately because a backend can genuinely stop at any of them. A\n * backend that provisions contributions but cannot probe its own catalog\n * is a real gap, and the honest answer to `store.probeContributions()`\n * there is a refusal, not `ready`: \"assessed and healthy\" and \"never\n * looked\" must never share a return value.\n */\nexport type ContributionCapabilities = Readonly<{\n /**\n * Whether strategy-owned contribution tables are provisioned and\n * attested by durable markers on this backend at all. `false` means\n * there is nothing to assess, and the probe reports no entries rather\n * than refusing.\n */\n supported: boolean;\n /**\n * Whether the backend can cross its durable markers against the live\n * catalog — `store.probeContributions()` and\n * `store.verifyContributions()`. False with `supported: true` is the\n * declared gap: both refuse with `ConfigurationError`.\n */\n probe: boolean;\n /**\n * Whether the backend can run the destructive drop → recreate →\n * repopulate → stamp rebuild (`store.rebuildContribution()`). Requires\n * both a strategy that declares `dropDdl` and a transactional schema\n * fence to run it under; false means `rebuildContribution` refuses.\n *\n * Describes the fulltext projection only. `rebuildContribution(\"vector\")`\n * refuses on every backend regardless of this flag — embeddings exist\n * only in the storage a rebuild would drop, so there is nothing to\n * reconstruct them from — and `reembedVectorField` is the sanctioned\n * destructive path there.\n */\n rebuild: boolean;\n}>;\n\n// ============================================================\n// SQL Dialect & Capabilities\n// ============================================================\n\nimport { type SqlDialect } from \"../query/dialect/types\";\n\nexport type { SqlDialect } from \"../query/dialect/types\";\n\n/**\n * Backend capabilities that vary by dialect.\n */\nexport type BackendCapabilities = Readonly<{\n /** Whether the backend supports atomic transactions (D1 does not) */\n transactions: boolean;\n /** Whether the backend supports SQL window functions such as ROW_NUMBER() */\n windowFunctions: boolean;\n /**\n * Whether `updateNode` / `updateEdge` honor `clearValidTo: true` by storing\n * SQL NULL in `valid_to`. Absent is `false`: custom backends must opt in so\n * the store refuses every explicit clear-bearing call before lookup,\n * coalescing, or write instead of making support depend on row state.\n */\n clearValidTo?: boolean;\n /**\n * Whether the backend's `execute()` supports `UPDATE … RETURNING`. Absent or\n * `true` means supported (every engine TypeGraph ships — SQLite ≥ 3.35,\n * PostgreSQL ≥ 8.2 — supports it). A custom backend whose engine cannot run\n * `RETURNING` must set this to `false` so recorded-time history capture\n * (`history: true`), which relies on `UPDATE … RETURNING` on its hot path,\n * is refused up front instead of failing mid-flush.\n */\n returning?: boolean;\n /**\n * Maximum number of bound parameters the engine accepts in one statement.\n * SQLite defaults to 999 (raisable at compile time via\n * `SQLITE_MAX_VARIABLE_NUMBER`), while hosted SQLite runtimes may impose a\n * lower platform ceiling. PostgreSQL's wire protocol encodes a 65535-count,\n * but postgres.js accepts at most 65533 bound values, so the bundled\n * PostgreSQL capability advertises that lower shared-driver ceiling.\n * Recorded-time capture and recorded point reads size their multi-row\n * statements to this ceiling — the same budget the backend's own batched\n * inserts use — instead of a conservative dialect-blind constant. Custom\n * runtimes can override a detected or probed limit here, but hosted platform\n * hard ceilings may only be lowered. Absent means the recorded-time fallback\n * budget applies.\n */\n maxBindParameters?: number;\n /**\n * Whether this backend supplies the claim relations that fence declared\n * constraints (`uniques`, re-keyed onto claim axes, plus\n * `typegraph_edge_claims`) AND implements the claim members.\n *\n * Absent means `false`: the backend predates the claim relations, keeps every\n * fence it has today, and is listed as such in the parity matrix. Never\n * INFERRED from member presence — a projection that forwarded `capabilities`\n * while dropping a method would otherwise yield a verdict read from a\n * different object than the write goes to. `claimSupport`\n * (`store/claims/backing.ts`) is the one reader, and it refuses a backend\n * whose declaration and surface disagree in either direction.\n */\n readonly constraintClaims?: boolean;\n /** Vector search capabilities (undefined if not configured) */\n vector?: VectorCapabilities | undefined;\n /** Fulltext search capabilities (undefined if not configured) */\n fulltext?: FulltextCapabilities | undefined;\n /** Whole-graph analytics capabilities (undefined when unavailable). */\n graphAnalytics?: GraphAnalyticsCapabilities | undefined;\n /**\n * How far up the contribution health ladder this backend goes\n * (undefined on a backend with no contribution machinery at all,\n * equivalent to every member `false`).\n */\n contributions?: ContributionCapabilities | undefined;\n}>;\n\n/** Keeps session-scoped analytics honest when a required SQL feature is absent. */\nexport function normalizeGraphAnalyticsCapabilities(\n capabilities: BackendCapabilities,\n): BackendCapabilities {\n if (\n capabilities.graphAnalytics?.supported !== true ||\n (capabilities.transactions &&\n capabilities.windowFunctions &&\n capabilities.returning !== false)\n ) {\n return capabilities;\n }\n return {\n ...capabilities,\n graphAnalytics: { ...capabilities.graphAnalytics, supported: false },\n };\n}\n\n// ============================================================\n// Row Types (Database Records)\n// ============================================================\n\n/**\n * A node/edge row's `props` column as the driver returned it: SQLite always\n * yields the JSON text; PostgreSQL drivers yield the jsonb value already\n * parsed. Keeping the driver's shape avoids a per-row\n * parse→stringify→re-parse round trip on the PostgreSQL read path —\n * consumers normalize through {@link rowPropsToObject} /\n * {@link rowPropsToJsonText} at the point of use.\n */\nexport type RowProps = string | Readonly<Record<string, unknown>>;\n\n/** The row's props as an object, parsing only when the driver gave text. */\nexport function rowPropsToObject(props: RowProps): Record<string, unknown> {\n return typeof props === \"string\" ?\n (JSON.parse(props) as Record<string, unknown>)\n : props;\n}\n\n/** The row's props as JSON text, serializing only when the driver gave an object. */\nexport function rowPropsToJsonText(props: RowProps): string {\n return typeof props === \"string\" ? props : JSON.stringify(props);\n}\n\n/**\n * A row from the typegraph_nodes table.\n */\nexport type NodeRow = Readonly<{\n graph_id: string;\n kind: string;\n id: string;\n props: RowProps;\n version: number;\n valid_from: string | undefined;\n valid_to: string | undefined;\n created_at: string;\n updated_at: string;\n deleted_at: string | undefined;\n}>;\n\n/**\n * A {@link NodeRow} proven live (not soft-deleted). Functions that must only\n * ever touch live rows — the live-row update pipeline, soft delete — take\n * this type so a caller holding a possibly-tombstoned row is forced through\n * {@link isLiveNodeRow} first, instead of silently running live-row side\n * effects (uniqueness reservations, embedding/fulltext sync) for a row that\n * stays invisible.\n */\nexport type LiveNodeRow = NodeRow & Readonly<{ deleted_at: undefined }>;\n\n/**\n * A {@link NodeRow} proven soft-deleted. A resurrect targets exactly this\n * shape; see {@link isTombstonedNodeRow}.\n */\nexport type TombstonedNodeRow = NodeRow & Readonly<{ deleted_at: string }>;\n\nexport function isLiveNodeRow(row: NodeRow): row is LiveNodeRow {\n return row.deleted_at === undefined;\n}\n\nexport function isTombstonedNodeRow(row: NodeRow): row is TombstonedNodeRow {\n return row.deleted_at !== undefined;\n}\n\n/**\n * The read-only slice of a backend. Give this type to code that is\n * semantically a read — probes, gates, support-graph loads — so a read path\n * structurally CANNOT open a write transaction, mutate rows, or execute raw\n * SQL: those members do not exist on the type. Extend the pick as read paths\n * need more of the read surface.\n */\nexport type GraphReadBackend = Pick<\n GraphBackend,\n | \"dialect\"\n | \"getNode\"\n | \"getEdge\"\n | \"findNodesByKind\"\n | \"findEdgesByKind\"\n | \"findEdgesConnectedTo\"\n>;\n\n/**\n * A row from the typegraph_edges table.\n */\nexport type EdgeRow = Readonly<{\n graph_id: string;\n id: string;\n kind: string;\n from_kind: string;\n from_id: string;\n to_kind: string;\n to_id: string;\n props: RowProps;\n valid_from: string | undefined;\n valid_to: string | undefined;\n created_at: string;\n updated_at: string;\n deleted_at: string | undefined;\n}>;\n\n/**\n * A row from the typegraph_node_uniques table.\n */\nexport type UniqueRow = Readonly<{\n graph_id: string;\n node_kind: string;\n constraint_name: string;\n key: string;\n node_id: string;\n concrete_kind: string;\n deleted_at: string | undefined;\n}>;\n\n/**\n * A row from the typegraph_schema_versions table.\n */\nexport type SchemaVersionRow = Readonly<{\n graph_id: string;\n version: number;\n schema_hash: string;\n schema_doc: string; // JSON string\n created_at: string;\n is_active: boolean;\n}>;\n\n// ============================================================\n// Insert Parameters\n// ============================================================\n\n/**\n * Parameters for inserting a node.\n */\nexport type InsertNodeParams = Readonly<{\n graphId: string;\n kind: string;\n id: string;\n props: Readonly<Record<string, unknown>>;\n /**\n * Omitted (`undefined`): the insert stamps its own creation timestamp —\n * UNLESS a stated `validTo` at or before that instant would make the stored\n * window readable at no coordinate, in which case the row is stored with no\n * lower bound (\"ended at T, start unknown\"). See\n * `resolveStampedValidityLowerBound`, which every insert builder decides\n * through. Custom backend implementations can import that owner from\n * `@nicia-ai/typegraph/backend` rather than reimplementing the rule.\n * `null`: preserves an explicit open-left validity window (no lower\n * bound) — used by interchange import to round-trip a row that was\n * already NULL, instead of re-stamping it to the import's own timestamp.\n */\n validFrom?: string | null;\n validTo?: string;\n}>;\n\n/**\n * A backend 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 SQL `NULL`.\n */\nexport type BackendValidityEndMutation =\n | Readonly<{ validTo?: string; clearValidTo?: never }>\n | Readonly<{ validTo?: never; clearValidTo: true }>;\n\n/** Parameters for updating a node. */\nexport type UpdateNodeParams = Readonly<{\n graphId: string;\n kind: string;\n id: string;\n props: Readonly<Record<string, unknown>>;\n /**\n * Applied when resurrecting a tombstone, which RESETS the window. Omitted\n * means the resurrection instant — unless a stated `validTo` at or before it\n * would leave the row readable at no coordinate, in which case the\n * resurrection stores no lower bound. Same rule, same owner, as an insert:\n * `resolveStampedValidityLowerBound`.\n *\n * The store's own resurrection paths never omit it. Their window guard has to\n * judge the bound the write will STORE, so they resolve it through that owner\n * against the instant they sampled and pass the result — `null` included,\n * which is how \"store no lower bound\" is spelled here. Omission is for callers\n * with no such verdict to honor; it lets this builder decide against its own,\n * strictly later sample.\n */\n validFrom?: string | null;\n /**\n * The effective `valid_from` this write ASSERTS the target row already\n * carries, stated only by a caller whose decision DEPENDED on it.\n *\n * `(graph_id, kind, id)` does not pin a row across time. A caller that\n * validated the document's window against the bound it probed — interchange\n * import's `onConflict: \"update\"` is the case — decided what to write from a\n * value a concurrent `hardDelete` + recreate can replace between the probe\n * and the write, so a predicate on identity alone lets that decision land on\n * a row it was never computed for: the document's stated `validFrom` is\n * ignored, or a `valid_to` is persisted below the new row's `valid_from`.\n * Stating the bound here puts it in the UPDATE's own `WHERE`, which is the\n * only placement the race cannot slip past.\n *\n * NULL-SAFE: `null` asserts the row has NO lower bound (`IS NULL`), a string\n * asserts equality, and omitting it asserts nothing. `null` and `undefined`\n * are therefore NOT interchangeable here — see `expectedValidFromPredicate`.\n *\n * A backend MUST apply the predicate when it is present, on the same terms as\n * {@link UpdateEdgeParams.kind}: silently ignoring it re-opens the window the\n * caller stated it to close. A write whose stated bound does not match affects\n * zero rows and surfaces as a `no_row_returned` `DatabaseOperationError`.\n */\n expectedValidFrom?: string | null;\n incrementVersion?: boolean;\n /** If true, clears deleted_at (un-deletes the node). Used by upsert. */\n clearDeleted?: boolean;\n}> &\n BackendValidityEndMutation;\n\n/**\n * Parameters for updating a set of live nodes selected by a compiled\n * TypeGraph query.\n *\n * The candidate query must expose the named node-id column from the same graph\n * and kind. The backend independently fences the rows it writes to\n * `graphId` and `kind`; constructing the matching candidate query remains the\n * caller's responsibility.\n * Property values replace top-level keys, including preserving an explicit\n * JSON `null` value.\n *\n * This is a storage primitive: callers that expose it as a graph mutation are\n * responsible for schema validation and for synchronizing uniqueness,\n * fulltext, and vector sidecars. The result contains after-images only; callers\n * that need previous property values must freeze the candidate set and retain\n * its before-images in the same transaction before invoking this method.\n */\nexport type UpdateNodeSetParams = Readonly<{\n graphId: string;\n kind: string;\n patch: Readonly<Record<string, JsonValue>>;\n /** Top-level properties to remove (the storage form of an `undefined` patch value). */\n unsetProperties?: readonly string[];\n candidateIds: CompiledSelectSql;\n /** Projected SQL column holding the candidate node id (for example `n_id`). */\n candidateIdColumn: string;\n}>;\n\n/** The after-images changed by {@link GraphBackend.updateNodeSet}. */\nexport type UpdateNodeSetResult = Readonly<{\n affectedCount: number;\n rows: readonly NodeRow[];\n}>;\n\n/**\n * Parameters for deleting a node (soft delete).\n */\nexport type DeleteNodeParams = Readonly<{\n graphId: string;\n kind: string;\n id: string;\n}>;\n\n/**\n * Parameters for inserting an edge.\n */\nexport type InsertEdgeParams = Readonly<{\n graphId: string;\n id: string;\n kind: string;\n fromKind: string;\n fromId: string;\n toKind: string;\n toId: string;\n props: Readonly<Record<string, unknown>>;\n /**\n * Omitted (`undefined`): the insert stamps its own creation timestamp —\n * UNLESS a stated `validTo` at or before that instant would make the stored\n * window readable at no coordinate, in which case the row is stored with no\n * lower bound (\"ended at T, start unknown\"). See\n * `resolveStampedValidityLowerBound`, which every insert builder decides\n * through. Custom backend implementations can import that owner from\n * `@nicia-ai/typegraph/backend` rather than reimplementing the rule.\n * `null`: preserves an explicit open-left validity window (no lower\n * bound) — used by interchange import to round-trip a row that was\n * already NULL, instead of re-stamping it to the import's own timestamp.\n */\n validFrom?: string | null;\n validTo?: string;\n}>;\n\n/**\n * Write-only session exposed inside {@link GraphBackend.trustedImport}.\n *\n * The caller has accepted the trusted-import contract: properties, endpoint\n * references, uniqueness, and cardinality are not validated. Backends may\n * therefore use engine-native ingestion primitives that bypass the normal\n * store write pipeline.\n */\nexport type TrustedImportSession = Readonly<{\n insertNodes: (params: readonly InsertNodeParams[]) => Promise<void>;\n insertEdges: (params: readonly InsertEdgeParams[]) => Promise<void>;\n}>;\n\n/**\n * Parameters for updating an edge.\n */\nexport type UpdateEdgeParams = Readonly<{\n graphId: string;\n id: string;\n /**\n * The kind this write ASSERTS the target row already carries.\n *\n * Edge ids are graph-global while public collections are kind-scoped, so a\n * kind-scoped write has an expected kind and must not land on a row carrying\n * a different one. Stating it here puts the predicate in the write\n * statement's own `WHERE`, which is the only placement a concurrent\n * `hardDelete` + recreate cannot slip past: a read-then-write pair keyed on\n * `(graph_id, id)` alone re-resolves that id between the read and the write\n * under PostgreSQL READ COMMITTED. A write whose stated kind does not match\n * affects zero rows.\n *\n * A backend MUST apply the predicate when it is present — silently ignoring\n * it re-opens the window the caller stated it to close, and the ignoring\n * backend looks correct until it is raced.\n * `tests/edge-write-self-verification.test.ts` asserts the behavior against\n * the built-in backends.\n *\n * Omitted where the operation legitimately spans kinds: the node delete\n * cascade removes every connected edge whatever its kind, so it states none.\n * {@link DeleteEdgeParams} and {@link HardDeleteEdgeParams} carry the same\n * field with the same contract.\n */\n kind?: string;\n /**\n * The ENDPOINTS this write asserts the target row already carries, stated\n * only by a write that actually checked them.\n *\n * Kind alone does not pin a row's identity. An edge's endpoints are immutable\n * for a given row, but the ID is not: a concurrent `hardDelete` + recreate\n * under the SAME kind with DIFFERENT endpoints satisfies a kind-only\n * predicate, so an upsert that resolved this id BY endpoints could otherwise\n * write to an edge pointing somewhere it never looked. Carrying them here\n * closes that, on the same terms and in the same `WHERE` as `kind`.\n *\n * All four move together or not at all — they are one assertion. A plain\n * `update` on a kind-scoped collection states none of them, because it\n * resolved the edge by id and kind and made no claim about where it points;\n * predicating on endpoints it never checked would refuse legitimate writes.\n * The same MUST-apply contract as `kind` binds a backend that receives them.\n */\n fromKind?: string;\n fromId?: string;\n toKind?: string;\n toId?: string;\n props: Readonly<Record<string, unknown>>;\n /**\n * Applied when resurrecting a tombstone, where it asserts a COMPLETE window:\n * `validTo` is rewritten alongside it (omitted `validTo` reopens the window).\n * Omitting `validFrom` on a resurrection leaves the stored window in place.\n */\n validFrom?: string | null;\n /**\n * The effective `valid_from` this write asserts the target row already\n * carries. Same three states, same NULL-safety, and the same MUST-apply\n * contract as {@link UpdateNodeParams.expectedValidFrom}; stated by the same\n * kind of caller, one whose verdict READ that bound.\n *\n * Separate from the immutable-identity components above because it is not\n * immutable: a resurrection rewrites `valid_from`. It is asserted for the\n * opposite reason — precisely because it can change, a decision computed from\n * it must be fenced against it having changed.\n */\n expectedValidFrom?: string | null;\n /**\n * The `valid_to` state a constraint decision read. NULL-safe and MUST apply\n * when present, like `expectedValidFrom`; used to fence ended-to-open edges.\n */\n expectedValidTo?: string | null;\n clearDeleted?: boolean;\n}> &\n BackendValidityEndMutation;\n\n/**\n * Parameters for deleting an edge (soft delete).\n */\nexport type DeleteEdgeParams = Readonly<{\n graphId: string;\n id: string;\n /** See {@link UpdateEdgeParams.kind}. */\n kind?: string;\n}>;\n\n/**\n * Parameters for hard deleting a node (permanent removal).\n */\nexport type HardDeleteNodeParams = Readonly<{\n graphId: string;\n kind: string;\n id: string;\n}>;\n\n/**\n * Parameters for hard deleting an edge (permanent removal).\n */\nexport type HardDeleteEdgeParams = Readonly<{\n graphId: string;\n id: string;\n /** See {@link UpdateEdgeParams.kind}. */\n kind?: string;\n}>;\n\n/** Parameters for a batched edge delete (soft or hard). */\nexport type DeleteEdgesBatchParams = Readonly<{\n graphId: string;\n ids: readonly string[];\n}>;\n\n// ============================================================\n// Embedding Parameters\n// ============================================================\n\n/**\n * Parameters for inserting or updating an embedding.\n *\n * `dimensions`, `metric`, and `indexType` together resolve the slot's\n * typed per-`(nodeKind, fieldPath)` storage (the strategy needs the fixed\n * dimension for the column type, and the metric/index type to address the\n * right ANN structure). The store populates them from the schema's\n * `embedding()` declaration via `getEmbeddingDimensions()` /\n * `getEmbeddingIndex()`; the backend constructs a `VectorSlot` from them\n * and stays graph-agnostic.\n */\nexport type UpsertEmbeddingParams = Readonly<{\n graphId: string;\n nodeKind: string;\n nodeId: string;\n fieldPath: string;\n embedding: readonly number[];\n dimensions: number;\n metric: VectorMetric;\n indexType: VectorIndexType;\n}>;\n\n/**\n * One row of a batched embedding upsert.\n */\nexport type UpsertEmbeddingBatchRow = Readonly<{\n nodeId: string;\n embedding: readonly number[];\n}>;\n\n/**\n * Parameters for a batched embedding upsert. Homogeneous per vector slot:\n * one graph, one node kind, one field, many nodes. Duplicate `nodeId`\n * values within a batch are deduplicated last-write-wins by the backend\n * (a multi-row upsert cannot affect one row twice).\n */\nexport type UpsertEmbeddingBatchParams = Readonly<{\n graphId: string;\n nodeKind: string;\n fieldPath: string;\n dimensions: number;\n metric: VectorMetric;\n indexType: VectorIndexType;\n rows: readonly UpsertEmbeddingBatchRow[];\n}>;\n\n/**\n * Parameters for deleting an embedding.\n *\n * `dimensions` / `metric` / `indexType` mirror {@link UpsertEmbeddingParams}\n * so the backend can resolve the slot's typed per-`(nodeKind, fieldPath)`\n * storage and idempotently ensure it exists before the DELETE. That\n * matters because a delete can run before any embedding was ever written\n * (e.g. a node hard-deleted having never carried one), and on Postgres a\n * DELETE against a missing relation inside a transaction aborts the whole\n * transaction. The store populates them from the schema's `embedding()`\n * declaration, exactly as for upserts.\n */\nexport type DeleteEmbeddingParams = Readonly<{\n graphId: string;\n nodeKind: string;\n nodeId: string;\n fieldPath: string;\n dimensions: number;\n metric: VectorMetric;\n indexType: VectorIndexType;\n}>;\n\n/**\n * Parameters for vector similarity search.\n */\nexport type VectorSearchParams = Readonly<{\n graphId: string;\n nodeKind: string;\n fieldPath: string;\n queryEmbedding: readonly number[];\n metric: VectorMetric;\n /**\n * Fixed vector dimension `N` for the searched `(nodeKind, fieldPath)`\n * slot. The store populates it from the schema's `embedding()`\n * declaration via `getEmbeddingDimensions()`; the backend uses it to\n * construct the `VectorSlot` whose typed storage the strategy scans.\n */\n dimensions: number;\n /**\n * Index type materialized for this slot. `\"none\"` means brute-force\n * only; otherwise the strategy may route through its ANN structure.\n * The store populates it from `getEmbeddingIndex()`.\n */\n indexType: VectorIndexType;\n limit: number;\n minScore?: number;\n /**\n * Subquery of eligible node ids (single column, exposed as `node_id`).\n * When present, the search statement computes top-k over this candidate\n * set — the store passes its compiled current-read query here so\n * `where` predicates push down into the search SQL. Exact on engines\n * whose search form takes the filter inside retrieval (pgvector,\n * sqlite-vec, tsvector, FTS5); libSQL's DiskANN cannot pre-filter, so\n * it post-filters an over-fetched ANN set and recall against the\n * candidate set is bounded by that headroom. When absent, the backend\n * restricts to CURRENT nodes of the kind — non-tombstoned AND inside\n * their validity window — matching a `current` read.\n */\n candidates?: SqlFragment;\n /**\n * Rows to skip AFTER ranking (pagination). The engine fetches\n * `limit + offset` ranked candidates and discards the first `offset`.\n */\n offset?: number;\n /**\n * HNSW search frontier for this query (pgvector `hnsw.ef_search`).\n * Sizes the dynamic candidate list the index scan maintains: higher\n * trades latency for recall. The floor for the over-fetch to fill its\n * candidate set is `efSearch >= limit`; ~2–4× is the high-recall\n * target. Applied transaction-locally (`SET LOCAL`) on the Postgres\n * HNSW path only.\n *\n * APPLIED OR REFUSED, never ignored. Read\n * `capabilities.vector.searchFrontierTuning` to know which you get:\n *\n * - `tunable: false` (sqlite-vec, libSQL DiskANN — no per-search frontier\n * knob exists): `UnsupportedBackendCapabilityError`.\n * - a slot whose index type is not the tunable one (pgvector IVFFlat or\n * brute-force): `ConfigurationError`.\n * - `requiresTransactionScope` on a backend reporting\n * `transactions: false` (for example `drizzle-orm/neon-http`):\n * `UnsupportedBackendCapabilityError`, because `SET LOCAL` has no frame\n * to be local to.\n */\n efSearch?: number;\n}>;\n\n/**\n * Result from a vector similarity search.\n */\nexport type VectorSearchResult = Readonly<{\n nodeId: string;\n /**\n * Cosine metric returns similarity score (higher is better).\n * L2 and inner_product return raw distance (lower is better).\n */\n score: number;\n}>;\n\n/**\n * Parameters for a single-statement hybrid (vector + fulltext, RRF-fused)\n * search. The store facade resolves every default before calling — the\n * per-source candidate depths (`k`), the fusion constants, and the shared\n * `candidates` subquery — so the backend composes `SqlFragment` values without policy.\n */\nexport type HybridSearchParams = Readonly<{\n graphId: string;\n nodeKind: string;\n vector: Readonly<{\n fieldPath: string;\n queryEmbedding: readonly number[];\n metric: VectorMetric;\n dimensions: number;\n indexType: VectorIndexType;\n /** Candidate depth of the vector source (rows entering fusion). */\n k: number;\n minScore?: number;\n efSearch?: number;\n }>;\n fulltext: Readonly<{\n query: string;\n mode?: FulltextQueryMode;\n language?: string;\n /** Candidate depth of the fulltext source (rows entering fusion). */\n k: number;\n minScore?: number;\n includeSnippets?: boolean;\n }>;\n fusion: Readonly<{\n /** RRF rank constant (`1 / (k + rank)`). */\n k: number;\n vectorWeight: number;\n fulltextWeight: number;\n }>;\n /** Fused rows to return after `offset`. */\n limit: number;\n /** Fused rows to skip (rank-relative pagination). */\n offset?: number;\n /** See {@link VectorSearchParams.candidates}; applies to both sources. */\n candidates?: SqlFragment;\n}>;\n\n/**\n * One fused hybrid hit, hydrated in the same statement: the node row\n * rides along so the caller needs no follow-up id fetch.\n */\nexport type HybridSearchRow = Readonly<{\n node: NodeRow;\n fusedScore: number;\n vectorRank?: number;\n vectorScore?: number;\n fulltextRank?: number;\n fulltextScore?: number;\n snippet?: string;\n}>;\n\n/**\n * Parameters for creating a vector index.\n */\nexport type CreateVectorIndexParams = Readonly<{\n graphId: string;\n nodeKind: string;\n fieldPath: string;\n dimensions: number;\n metric: VectorMetric;\n indexType: VectorIndexType;\n /** Index-specific parameters */\n indexParams?: Readonly<{\n /** HNSW: max connections per layer */\n m?: number;\n /** HNSW: construction search depth */\n efConstruction?: number;\n /** IVFFlat: number of lists */\n lists?: number;\n }>;\n /**\n * Build the index without taking an `AccessExclusiveLock` on live\n * tables (Postgres `CREATE INDEX CONCURRENTLY`). Mirrors the\n * `concurrent` flag the relational DDL path uses inside\n * `materializeIndexes()`. Cannot be set inside a transaction —\n * callers (`materializeIndexes()`) run at top level. Backends that\n * don't have a CONCURRENTLY equivalent (SQLite) ignore this flag.\n */\n concurrent?: boolean;\n}>;\n\n/**\n * Parameters for dropping a vector index.\n */\nexport type DropVectorIndexParams = Readonly<{\n graphId: string;\n nodeKind: string;\n fieldPath: string;\n}>;\n\n// ============================================================\n// Fulltext Parameters\n// ============================================================\n\n/**\n * Parameters for inserting or updating a fulltext entry.\n *\n * One row per node. Callers concatenate the searchable fields into\n * `content` so a single MATCH query can find terms spread across fields.\n *\n * Note: `createFulltextIndex` / `dropFulltextIndex` were removed as\n * dead code in #PR_E. The fulltext table's canonical index (Postgres\n * GIN on `tsv`, SQLite FTS5 virtual table) is created with the table\n * itself by `bootstrapTables` per the active `FulltextStrategy`;\n * per-kind fulltext indexes are an \"advanced strategy\" surface that\n * doesn't fit the relational-style declaration model and is reserved\n * for future work.\n */\nexport type UpsertFulltextParams = Readonly<{\n graphId: string;\n nodeKind: string;\n nodeId: string;\n content: string;\n language: string;\n}>;\n\n/**\n * Parameters for deleting a single fulltext entry.\n */\nexport type DeleteFulltextParams = Readonly<{\n graphId: string;\n nodeKind: string;\n nodeId: string;\n}>;\n\n/**\n * A single row in a fulltext batch upsert.\n */\nexport type FulltextBatchRow = Readonly<{\n nodeId: string;\n content: string;\n language: string;\n}>;\n\n/**\n * Parameters for a batched fulltext upsert.\n *\n * Homogeneous: one graph, one node kind, many nodes. Duplicate `nodeId`\n * values within a single batch are deduplicated last-write-wins by the\n * builders before SQL generation — Postgres `ON CONFLICT` errors on\n * repeated conflict keys in one statement, and SQLite `DELETE + INSERT`\n * would create duplicate virtual-table rows otherwise.\n */\nexport type UpsertFulltextBatchParams = Readonly<{\n graphId: string;\n nodeKind: string;\n rows: readonly FulltextBatchRow[];\n}>;\n\n/**\n * Parameters for a batched fulltext delete.\n */\nexport type DeleteFulltextBatchParams = Readonly<{\n graphId: string;\n nodeKind: string;\n nodeIds: readonly string[];\n}>;\n\n/**\n * Parameters for fulltext search.\n */\nexport type FulltextSearchParams = Readonly<{\n graphId: string;\n nodeKind: string;\n /** The user-supplied query string. */\n query: string;\n /** How to parse `query`. Default: \"websearch\". */\n mode?: FulltextQueryMode;\n /**\n * Language for query parsing. The store facade resolves the kind's\n * declared language and passes it here, so the tsquery is a plan-time\n * constant (GIN-servable); when absent the backend falls back to the\n * per-row language column.\n * Postgres: passed as the regconfig to `to_tsquery` / `websearch_to_tsquery`.\n * SQLite: informational only (FTS5 tokenizer is fixed at table-create time).\n */\n language?: string;\n /** Max rows to return. */\n limit: number;\n /** Minimum rank to include (backend-dependent units). */\n minScore?: number;\n /** Whether to return a highlighted snippet per match. */\n includeSnippets?: boolean;\n /**\n * Subquery of eligible node ids (single column). When present, the\n * search statement computes top-k over this candidate set — the store\n * passes its compiled current-read query here so `where` predicates,\n * subclass expansion, and valid-time currency all push down into the\n * search SQL. Exact on engines whose search form takes the filter\n * inside retrieval (pgvector, sqlite-vec, tsvector, FTS5); libSQL's\n * DiskANN cannot pre-filter, so it post-filters an over-fetched ANN\n * set and recall against the candidate set is bounded by that\n * headroom. When absent, the backend restricts to CURRENT nodes of\n * the kind — non-tombstoned AND inside their validity window —\n * matching a `current` read.\n */\n candidates?: SqlFragment;\n /**\n * Rows to skip AFTER ranking (pagination). The engine fetches\n * `limit + offset` ranked candidates and discards the first `offset`.\n */\n offset?: number;\n}>;\n\n/**\n * Result from a fulltext search.\n *\n * Score semantics differ by backend; prefer `rank` (1-based) when fusing\n * with another source via RRF.\n */\nexport type FulltextSearchResult = Readonly<{\n nodeId: string;\n /**\n * Backend-native relevance score.\n * Postgres: `ts_rank_cd` (higher is better).\n * SQLite FTS5: negated `bm25()` (higher is better; FTS5 returns lower-is-better).\n */\n score: number;\n /** 1-based rank within the result set, suitable for RRF. */\n rank: number;\n /** Highlighted snippet of the content (if `includeSnippets` was set). */\n snippet?: string;\n}>;\n\n/**\n * Parameters for creating a fulltext index.\n *\n * The canonical index (Postgres GIN on `tsv`, SQLite FTS5 virtual table)\n * is created when the fulltext table itself is created. This is reserved\n * for advanced per-kind specializations.\n */\n\n// ============================================================\n// Index Materialization Status\n// ============================================================\n\n/**\n * Per-deployment record for a single declared index.\n *\n * Identified by `indexName` because SQL index names are physical,\n * database-global identifiers — `graphId` is provenance, not identity.\n * `materializedAt` is null until the first successful CREATE INDEX\n * completes; `lastAttemptedAt` is always set, even on failure.\n */\n/**\n * Parameters for atomically claiming an index build (see\n * `claimIndexMaterialization`). The declaration fields ride along so a\n * fresh claim row is honest while the build is in flight.\n */\nexport type ClaimIndexMaterializationParams = Readonly<{\n indexName: string;\n graphId: string;\n entity: IndexEntity;\n kind: string;\n signature: string;\n schemaVersion: number;\n /** Opaque claim identity; release is token-guarded. */\n token: string;\n /** A claim older than this is stale and may be taken over. */\n leaseMs: number;\n}>;\n\n/** Parameters for releasing a build claim (token-guarded). */\nexport type ReleaseIndexMaterializationClaimParams = Readonly<{\n indexName: string;\n token: string;\n}>;\n\nexport type IndexMaterializationRow = Readonly<{\n indexName: string;\n graphId: string;\n entity: IndexEntity;\n kind: string;\n signature: string;\n schemaVersion: number;\n materializedAt: string | undefined;\n lastAttemptedAt: string;\n lastError: string | undefined;\n}>;\n\n/**\n * Parameters for upserting a materialization attempt.\n *\n * On success: pass `materializedAt` (ISO timestamp) and undefined `error`.\n * On failure: pass undefined `materializedAt` (preserve any existing\n * timestamp from a prior success) and the error message.\n */\nexport type RecordIndexMaterializationParams = Readonly<{\n indexName: string;\n graphId: string;\n entity: IndexEntity;\n kind: string;\n signature: string;\n schemaVersion: number;\n attemptedAt: string;\n /** ISO timestamp on success; undefined on failure (preserves existing). */\n materializedAt: string | undefined;\n /** Error message on failure; undefined on success (clears existing). */\n error: string | undefined;\n}>;\n\n// ============================================================\n// Contribution Materializations (#135 — durable strategy-owned\n// storage marker, sibling of index materializations)\n// ============================================================\n\n/**\n * Durable identity of a strategy-owned table contribution (#129),\n * scoped to a graph. This is the primary key of\n * `typegraph_contribution_materializations`.\n *\n * `logicalName` is the stable slot (\"fulltext\"); `owner` is the\n * producing strategy (\"tsvector\" / \"fts5\"); `tableName` is the resolved\n * physical name (custom per-deployment names must be distinguishable).\n * `graphId` is part of identity here — unlike the index status table\n * where the physical index name is database-global, two graphs can each\n * own a logically-identical fulltext contribution.\n */\nexport type ContributionMaterializationIdentity = Readonly<{\n graphId: string;\n logicalName: string;\n owner: string;\n tableName: string;\n}>;\n\n/**\n * Per-deployment record that a strategy-owned contribution has been\n * durably materialized against this database.\n *\n * `signature` is intentionally NOT part of the identity: a row with the\n * same identity but a different signature means \"materialized artifact\n * is stale/drifted\" — a loud error on the hot path, never a silent\n * re-materialize. `materializedAt` is undefined until the first\n * successful materialization; `lastAttemptedAt` is always set.\n */\nexport type ContributionMaterializationRow = Readonly<{\n graphId: string;\n logicalName: string;\n owner: string;\n tableName: string;\n signature: string;\n materializedAt: string | undefined;\n lastAttemptedAt: string;\n lastError: string | undefined;\n}>;\n\n/**\n * Parameters for upserting a contribution-materialization attempt.\n * Same success/failure contract as\n * {@link RecordIndexMaterializationParams}: on failure pass undefined\n * `materializedAt` so a prior successful timestamp is preserved via\n * COALESCE, and the error message.\n */\nexport type RecordContributionMaterializationParams = Readonly<{\n graphId: string;\n logicalName: string;\n owner: string;\n tableName: string;\n signature: string;\n attemptedAt: string;\n /** ISO timestamp on success; undefined on failure (preserves existing). */\n materializedAt: string | undefined;\n /** Error message on failure; undefined on success (clears existing). */\n error: string | undefined;\n}>;\n\n/**\n * Why a contribution is not usable. Most members are a disagreement\n * between the durable marker and the physical catalog; one\n * (`failed-materialization`) is a state the two agree on and that is\n * broken anyway. A contribution that is genuinely healthy — or that was\n * simply never attempted — is not reported at all.\n *\n * - `orphaned-marker` — the marker records a successful materialization\n * but the physical table is gone (a partial restore, an out-of-band\n * `DROP`, a schema-scoped restore that missed the contribution\n * tables). The state {@link GraphBackend.verifyContributions} exists\n * to surface: nothing on the open path probes the catalog, so this\n * database opens clean and fails at the first dependent read or write.\n * - `missing-marker` — the physical table exists but no marker attests\n * it as initialized (no row, no recorded success, or a recorded\n * failure). Reads and writes are refused with\n * `StoreNotInitializedError` even though storage is present. These\n * three causes share one state because they share one repair; check\n * {@link ContributionDiagnostic.lastError} to tell them apart.\n * - `failed-materialization` — the marker records a *failed* attempt\n * and no table was produced. Marker and catalog agree here, so this\n * is not a disagreement — but it is where a contribution lands when\n * provisioning genuinely broke (the `fts5` module is absent, the role\n * lacks `CREATE`, the extension was never loaded), and calling it\n * healthy on the grounds that both sides agree would be the one\n * answer this method must never give. Distinguished from a\n * contribution that was never attempted, which has no marker row and\n * is correctly silent. {@link ContributionDiagnostic.lastError}\n * carries the reason it failed.\n * - `stale` — the table exists and the marker records a prior success\n * at a different signature (a strategy swap, or a declared embedding\n * dimension that has moved ahead of the provisioned table).\n *\n * **Do not re-frame this union as \"how the marker disagrees with the\n * catalog.\"** It was described that way once, and the description was\n * load-bearing in the wrong direction: under it, a marker recording a\n * failed attempt with no table looks like agreement, therefore not a\n * disagreement, therefore correctly silent — and a contribution that\n * had genuinely broken was reported as healthy. The gap read as\n * principled rather than as an oversight precisely because the framing\n * endorsed it. \"Why the contribution is unusable\" is the question that\n * makes `failed-materialization` obviously belong, and any future\n * tidy-up that narrows the framing back will re-open the same hole.\n */\nexport type ContributionDiagnosticState =\n \"orphaned-marker\" | \"missing-marker\" | \"failed-materialization\" | \"stale\";\n\n/**\n * One contribution that is not usable, reported by\n * {@link GraphBackend.verifyContributions}.\n *\n * The identity fields come from the active contribution declaration and\n * match the durable-marker identity contract, so a caller can route an entry\n * to its repair without reconstructing any internal naming contract. A\n * `missing-marker` entry may have no marker row at all.\n *\n * **Route on {@link ContributionDiagnosticState}, not on whether the\n * entry is a vector slot.** The repair differs per state and the wrong\n * choice destroys data: `store.reembedVectorField` drops and recreates\n * storage, so applying it to a `missing-marker` — where the table is\n * intact and only the bookkeeping is wrong — discards every embedding\n * to fix a marker, and without an `embed` callback it leaves the field\n * empty. Prefer `store.repairContributions()` for `missing-marker` and\n * `failed-materialization`; it resolves the current strategy declarations\n * internally and keeps the normal signature-drift guard enabled. It reports\n * `stale` and `orphaned-marker` as `requires-rebuild`. See the per-state\n * repair table in the troubleshooting guide.\n */\nexport type ContributionDiagnostic = Readonly<{\n /** Producing strategy, e.g. `\"fts5\"` / `\"tsvector\"` / `\"pgvector\"`. */\n owner: string;\n /** Stable logical slot, e.g. `\"fulltext\"`. Never the SQL name. */\n logicalName: string;\n /** Resolved physical table name that was probed. */\n physicalName: string;\n /** Node kind — vector slots only. */\n kind?: string;\n /** Embedding field path — vector slots only. */\n fieldPath?: string;\n state: ContributionDiagnosticState;\n /**\n * The error the marker recorded against its last attempt, when it\n * recorded one. Absent otherwise.\n *\n * This is the part of the picture the catalog cannot supply. `state`\n * says what to do — the states are chosen so that each maps to one\n * repair — while this says *why it broke*, which is a different\n * question and often a different investigation. A failed attempt with no\n * table surfaces as `failed-materialization`; when the table exists but\n * the marker does not attest a successful materialization, it surfaces as\n * `missing-marker`. This field distinguishes only failures whose marker\n * recorded a reason; a missing row has no `lastError`. Present on any state\n * whose marker row carries an error, not just those two states.\n */\n lastError?: string;\n}>;\n\n/**\n * Outcome of one contribution considered by\n * {@link GraphBackend.repairContributions}.\n *\n * Repair targets are always resolved from the backend's current strategy\n * declarations. The diagnostic is returned for operator context only; callers\n * never pass diagnostics or physical DDL back into the repair API.\n */\nexport type ContributionRepairEntry =\n | Readonly<{\n diagnostic: ContributionDiagnostic;\n status: \"repaired\";\n }>\n | Readonly<{\n diagnostic: ContributionDiagnostic;\n status: \"requires-rebuild\";\n }>\n | Readonly<{\n diagnostic: ContributionDiagnostic;\n status: \"failed\";\n error: string;\n }>;\n\n/** Result of a contribution repair pass followed by a fresh verification. */\nexport type ContributionRepairResult = Readonly<{\n results: readonly ContributionRepairEntry[];\n remaining: readonly ContributionDiagnostic[];\n}>;\n\n// ============================================================\n// Contribution readiness probe (#377)\n// ============================================================\n\n/**\n * The search projection a probe entry summarizes. Deliberately the\n * logical *class* rather than one physical table: a caller asking \"is\n * search ready?\" is deciding whether to issue a fulltext or a vector\n * query, and a per-`(kind, field)` breakdown of pgvector tables answers\n * a question they did not ask. The physical detail an operator needs to\n * act is one `verifyContributions()` call away, and\n * {@link ContributionProbeEntry.detail} names the affected slots.\n */\nexport type ContributionProbeContribution = \"fulltext\" | \"vector\";\n\n/**\n * Readiness of one search projection.\n *\n * - `ready` — every contribution backing this projection is attested by\n * its durable marker and present in the catalog. Not a promise about\n * future coherence: a write that lands after the probe returns is\n * outside what any assessment can cover.\n * - `degraded` — at least one contribution is unusable. Dependent operations\n * may be refused with a typed contribution error, fail at the engine boundary\n * when compiled SQL directly references missing storage, or observe\n * incomplete storage.\n * {@link ContributionProbeEntry.detail} says which and why.\n * - `building` — **reserved.** No shipped path publishes it. Recording\n * an in-flight marker would need a fifth\n * {@link ContributionDiagnosticState} and would widen the hot-path\n * gate's verdict set; the destructive rebuild instead runs inside one\n * transaction, so a concurrent probe observes the state before or\n * after it and never a partial one. Reserved rather than removed so a\n * future streaming/async materializer can populate it without a\n * breaking change — treat it as \"not `ready`\" today.\n */\nexport type ContributionProbeState = \"ready\" | \"degraded\" | \"building\";\n\n/**\n * One search projection's readiness, from\n * {@link GraphBackend.probeContributions}.\n */\nexport type ContributionProbeEntry = Readonly<{\n contribution: ContributionProbeContribution;\n state: ContributionProbeState;\n /**\n * Operator-facing summary of why the projection is not `ready` —\n * e.g. `\"fulltext table \\\"typegraph_node_fulltext\\\" is missing\n * (orphaned-marker)\"`. Absent on a `ready` entry.\n *\n * Human-readable and not a stable format: route on `state`, and call\n * `store.verifyContributions()` for the structured per-table\n * diagnostics this string is derived from.\n */\n detail?: string;\n}>;\n\n/**\n * Result of {@link GraphBackend.probeContributions}.\n *\n * A projection with no declared contributions is omitted rather than\n * reported `ready`, so an empty `entries` array means \"nothing to\n * assess\" — a graph with no `searchable()` or `embedding()` fields, or a\n * backend with no contribution support — never \"assessed and healthy\".\n */\nexport type ContributionProbeResult = Readonly<{\n /**\n * The durable graph revision the assessment was taken at, so a caller\n * can place this probe in the graph's committed history.\n *\n * Graph-global, like the clock it reads: it advances on every committed\n * capture from any writer, so an advance between two probes means\n * \"something committed in between\", never \"the write this caller just\n * made landed\". Confirm a specific write by observing the write itself.\n *\n * Absent unless the Store is revision-tracked (`revisionTracking:\n * true` or `history: true`) — the same condition under which\n * `store.revisionNow()` returns a value. This is the one honest shape:\n * a store with no durable revision clock has no revision to stamp, and\n * substituting a wall-clock timestamp or the schema version would be a\n * materially weaker guarantee wearing the name of a stronger one. The\n * schema version in particular does not advance on data writes, so it\n * could not order a probe against a caller's write at all.\n */\n graphRevision?: string;\n entries: readonly ContributionProbeEntry[];\n}>;\n\n// ============================================================\n// Destructive contribution rebuild (#337)\n// ============================================================\n\n/**\n * Which search projection {@link GraphBackend.rebuildContribution}\n * targets. Scoped explicitly at the call site rather than inferred from\n * a diagnostic: the operation destroys storage, so which storage is the\n * caller's decision to state, not TypeGraph's to guess.\n */\nexport type ContributionRebuildScope = ContributionProbeContribution;\n\n/**\n * What a rebuild's `repopulate` callback reconstructed. Reported back so\n * the rebuild result can distinguish \"recreated empty storage\" from\n * \"recreated and refilled\", which is the difference between a finished\n * repair and half of one.\n */\nexport type ContributionRepopulationStats = Readonly<{\n /** Nodes scanned. */\n processed: number;\n /** Content rows written. */\n repopulated: number;\n /** Nodes whose stored `props` could not be read as an object. */\n skipped: number;\n}>;\n\n/** Outcome of one destructive contribution rebuild. */\nexport type ContributionRebuildResult = Readonly<{\n /** Physical tables dropped and recreated, in the order rebuilt. */\n rebuilt: readonly string[];\n /** Nodes scanned while reconstructing content from stored rows. */\n processed: number;\n /** Content rows written into the recreated storage. */\n repopulated: number;\n /**\n * Nodes whose stored `props` could not be read as an object, so no\n * content row could be reconstructed for them. Non-zero means the\n * rebuilt index is missing those nodes; the IDs come back from\n * `store.search.rebuildFulltext()`, which reports them individually.\n */\n skipped: number;\n}>;\n\n// ============================================================\n// Kind Removals (data-cleanup status)\n// ============================================================\n\n/**\n * One row of the per-deployment `typegraph_kind_removals` table:\n * a graph-extension kind that has been removed from the schema and whose\n * data may or may not have been cleaned up yet.\n */\nexport type KindRemovalRow = Readonly<{\n graphId: string;\n kindName: string;\n entity: KindEntity;\n schemaVersion: number;\n /** ISO timestamp when the data-cleanup pass succeeded; undefined while pending. */\n removedAt: string | undefined;\n lastAttemptedAt: string;\n lastError: string | undefined;\n}>;\n\n/**\n * Upsert payload for a kind-removal status row. On removal-commit:\n * pass `removedAt: undefined` (the pending state). On successful\n * data-cleanup: pass `removedAt` set and `error: undefined`. On\n * cleanup failure: pass `removedAt: undefined` (preserves any prior\n * timestamp from a partial success on a different replica) and the\n * error message.\n */\nexport type RecordKindRemovalParams = Readonly<{\n graphId: string;\n kindName: string;\n entity: KindEntity;\n schemaVersion: number;\n attemptedAt: string;\n /** ISO timestamp on success; undefined while pending or on failure. */\n removedAt: string | undefined;\n /** Error message on failure; undefined on success or while pending. */\n error: string | undefined;\n}>;\n\n// ============================================================\n// Query Types\n// ============================================================\n\n/** @internal Capability token for connection-local temporary writes. */\nexport const INTERNAL_TEMPORARY_WRITES: unique symbol = typeGraphGlobalSymbol(\n \"internal-temporary-writes-v1\",\n);\n\n/**\n * Transaction options.\n */\nexport type TransactionOptions = Readonly<{\n /** Transaction isolation level (if supported) */\n isolationLevel?:\n \"read_uncommitted\" | \"read_committed\" | \"repeatable_read\" | \"serializable\";\n /**\n * Transaction access mode (if supported). `read_only` is intended for\n * multi-statement reads that need one snapshot and must not perform writes.\n */\n accessMode?: \"read_only\" | \"read_write\";\n}>;\n\n/** @internal Transaction options available only to TypeGraph-owned execution. */\nexport type InternalTransactionOptions = TransactionOptions &\n Readonly<{\n /**\n * Permit writes only to connection-local temporary state. The iterative\n * operation primitive uses this when an engine rejects temporary DDL in a\n * read-only transaction.\n */\n temporaryWrites?: typeof INTERNAL_TEMPORARY_WRITES;\n }>;\n\n// ============================================================\n// Backend Interface\n// ============================================================\n\n/**\n * The physical names of the four Operational Identity relations: the current\n * assertion ledger, its recorded-time twin, and the two DERIVED relations\n * (closure, separation) rebuilt from the ledger.\n *\n * Named once so the two ports that speak about them —\n * {@link GraphBackend.ensureIdentityTables} and\n * {@link GraphBackend.identityTableDdl} — cannot drift apart.\n */\nexport type IdentityTableNames = Readonly<{\n identityAssertions: string;\n recordedIdentityAssertions: string;\n identityClosure: string;\n identitySeparation: string;\n}>;\n\n/**\n * The database-global extensions TypeGraph installs on a caller's behalf.\n *\n * The extent is closed on purpose: {@link GraphBackend.ensureExtension}\n * interpolates the name into DDL, so the allowlist — not the caller — is what\n * decides the identifier can be trusted. `pg_trgm` backs `method: \"trigram\"`\n * index materialization; `vector` backs pgvector storage.\n */\nexport const DATABASE_EXTENSION_NAMES = [\"pg_trgm\", \"vector\"] as const;\n\n/** A name {@link GraphBackend.ensureExtension} accepts. */\nexport type DatabaseExtensionName = (typeof DATABASE_EXTENSION_NAMES)[number];\n\n/**\n * The GraphBackend interface abstracts database operations.\n *\n * Implementations should provide:\n * - SQLite backend via better-sqlite3 or libsql\n * - PostgreSQL backend via pg or postgres\n *\n * Every function is receiver-free (`this: void`). Implementations must close\n * over their state instead of reading `this`, which makes saved optional\n * capabilities and other detached port calls safe by construction.\n */\nexport type GraphBackend = Readonly<{\n /** The SQL dialect */\n dialect: SqlDialect;\n /** Backend capabilities */\n capabilities: BackendCapabilities;\n /** Table names used by this backend (for query schema auto-derivation) */\n tableNames?: SqlTableNames | undefined;\n /**\n * Optional fulltext strategy override. When present, both the compiler\n * (for `$fulltext.matches()` in query builder) and backend-direct\n * search paths use this instead of the dialect's default strategy —\n * allowing a Postgres backend to ship pg_trgm, ParadeDB, pgroonga etc.\n * When absent, the dialect's default strategy is used.\n */\n fulltextStrategy?: FulltextStrategy | undefined;\n /**\n * Optional vector strategy this backend is wired with. The query\n * compiler reads it (via `compileQuery` options) to emit per-`(kind,\n * field)` relevance scans for `field.similarTo(...)` predicates, and\n * the index-materialization / removal paths read its deterministic\n * `tableName(...)` to address the right physical per-field storage.\n * `undefined` when the backend has no vector support (e.g. a generic\n * SQLite backend without sqlite-vec).\n */\n vectorStrategy?: VectorStrategy | undefined;\n\n // === Node Operations ===\n insertNode: (this: void, params: InsertNodeParams) => Promise<NodeRow>;\n insertNodeNoReturn?: (this: void, params: InsertNodeParams) => Promise<void>;\n insertNodesBatch?: (\n this: void,\n params: readonly InsertNodeParams[],\n ) => Promise<void>;\n insertNodesBatchReturning?: (\n this: void,\n params: readonly InsertNodeParams[],\n ) => Promise<readonly NodeRow[]>;\n updateNode: (this: void, params: UpdateNodeParams) => Promise<NodeRow>;\n updateNodeSet?: (\n this: void,\n params: UpdateNodeSetParams,\n ) => Promise<UpdateNodeSetResult>;\n deleteNode: (this: void, params: DeleteNodeParams) => Promise<void>;\n hardDeleteNode: (this: void, params: HardDeleteNodeParams) => Promise<void>;\n getNode: (\n this: void,\n graphId: string,\n kind: string,\n id: string,\n ) => Promise<NodeRow | undefined>;\n getNodes?: (\n this: void,\n graphId: string,\n kind: string,\n ids: readonly string[],\n ) => Promise<readonly NodeRow[]>;\n\n // === Edge Operations ===\n insertEdge: (this: void, params: InsertEdgeParams) => Promise<EdgeRow>;\n insertEdgeNoReturn?: (this: void, params: InsertEdgeParams) => Promise<void>;\n insertEdgesBatch?: (\n this: void,\n params: readonly InsertEdgeParams[],\n ) => Promise<void>;\n insertEdgesBatchReturning?: (\n this: void,\n params: readonly InsertEdgeParams[],\n ) => Promise<readonly EdgeRow[]>;\n updateEdge: (this: void, params: UpdateEdgeParams) => Promise<EdgeRow>;\n deleteEdge: (this: void, params: DeleteEdgeParams) => Promise<void>;\n hardDeleteEdge: (this: void, params: HardDeleteEdgeParams) => Promise<void>;\n /**\n * Batched {@link deleteEdge}: one soft-delete statement per bind-budget\n * chunk instead of one per edge. Optional — cascade deletes fall back to\n * the per-edge form when unset. Same per-row semantics (idempotent on\n * already-tombstoned rows).\n */\n deleteEdgesBatch?: (\n this: void,\n params: DeleteEdgesBatchParams,\n ) => Promise<void>;\n /** Batched {@link hardDeleteEdge}; see {@link deleteEdgesBatch}. */\n hardDeleteEdgesBatch?: (\n this: void,\n params: DeleteEdgesBatchParams,\n ) => Promise<void>;\n getEdge: (\n this: void,\n graphId: string,\n id: string,\n ) => Promise<EdgeRow | undefined>;\n getEdges?: (\n this: void,\n graphId: string,\n ids: readonly string[],\n ) => Promise<readonly EdgeRow[]>;\n\n // === Edge Cardinality Operations ===\n countEdgesFrom: (this: void, params: CountEdgesFromParams) => Promise<number>;\n edgeExistsBetween: (\n this: void,\n params: EdgeExistsBetweenParams,\n ) => Promise<boolean>;\n\n // === Edge Query Operations ===\n findEdgesConnectedTo: (\n this: void,\n params: FindEdgesConnectedToParams,\n ) => Promise<readonly EdgeRow[]>;\n\n // === Collection Query Operations ===\n findNodesByKind: (\n this: void,\n params: FindNodesByKindParams,\n ) => Promise<readonly NodeRow[]>;\n countNodesByKind: (\n this: void,\n params: CountNodesByKindParams,\n ) => Promise<number>;\n findEdgesByKind: (\n this: void,\n params: FindEdgesByKindParams,\n ) => Promise<readonly EdgeRow[]>;\n /**\n * Reads the edges of a SET of endpoints in one statement per bind-budget\n * chunk — see {@link FindEdgesByEndpointSetParams}.\n *\n * **Optional, and its absence is the capability signal.** `store.edges.<kind>\n * .bulkFindFrom` / `.bulkFindTo` check for this method before issuing any\n * read and refuse with a typed `ConfigurationError` when it is missing,\n * rather than degrading to a per-endpoint loop. A caller reaching for a bulk\n * endpoint read is asking for set-oriented statements; quietly giving them N\n * singleton statements is the surprise the method exists to prevent.\n *\n * Both bundled Drizzle backends implement it. A custom backend that does not\n * simply omits it and the bulk reads refuse; the singleton `findEdgesByKind`\n * path is unaffected.\n */\n findEdgesByEndpointSet?: (\n this: void,\n params: FindEdgesByEndpointSetParams,\n ) => Promise<readonly EdgeRow[]>;\n /**\n * Reads several edge kinds from a heterogeneous set of endpoints without\n * issuing one statement per licensed `(edge kind, endpoint kind)` pair.\n *\n * Optional for custom backends. Store-level heterogeneous bulk reads refuse\n * when it is absent rather than hiding a per-kind/per-endpoint fallback.\n */\n findEdgesByHeterogeneousEndpointSet?: (\n this: void,\n params: FindEdgesByHeterogeneousEndpointSetParams,\n ) => Promise<readonly EdgeRow[]>;\n countEdgesByKind: (\n this: void,\n params: CountEdgesByKindParams,\n ) => Promise<number>;\n\n // === Unique Constraint Operations ===\n insertUnique: (this: void, params: InsertUniqueParams) => Promise<void>;\n /**\n * Batched variant of `insertUnique`: one multi-row statement per chunk\n * with the same per-entry conflict semantics (throws `UniquenessError`\n * for the first entry whose key a different live node holds). Optional —\n * callers fall back to per-entry `insertUnique` when unset.\n */\n insertUniqueBatch?: (\n this: void,\n entries: readonly InsertUniqueParams[],\n ) => Promise<void>;\n deleteUnique: (this: void, params: DeleteUniqueParams) => Promise<void>;\n /**\n * Permanently removes every uniqueness sidecar owned by the specified\n * concrete nodes. Optional capability used by set-based updates before they\n * rebuild reservations from the returned node after-images.\n */\n hardDeleteUniquesByNodeIds?: (\n this: void,\n params: HardDeleteUniquesByNodeIdsParams,\n ) => Promise<void>;\n /**\n * Permanently removes every uniqueness claim owned by nodes of one concrete\n * kind, at whatever axis each claim sits on. THE definition of \"the claims\n * this kind owns\" — kind removal reaps through it so it can neither leak a\n * claim whose axis is a sibling kind nor delete a surviving sibling's claim.\n * Optional capability.\n */\n hardDeleteUniquesByConcreteKind?: (\n this: void,\n params: HardDeleteUniquesByConcreteKindParams,\n ) => Promise<void>;\n checkUnique: (\n this: void,\n params: CheckUniqueParams,\n ) => Promise<UniqueRow | undefined>;\n checkUniqueBatch?: (\n this: void,\n params: CheckUniqueBatchParams,\n ) => Promise<readonly UniqueRow[]>;\n\n // === Edge Cardinality Claim Operations ===\n /**\n * Takes the claim on one edge cardinality axis, in two statements: a\n * decision-free create-or-lock that reports the committed holder, and — only\n * when that holder is a different edge — a conditional takeover that succeeds\n * exactly when the incumbent is no longer an edge the axis and key describe.\n *\n * The claim is the FENCE for a declared cardinality: `(kind, from)` and\n * `(kind, from, to)` are predicates the edges primary key `(graph_id, id)`\n * cannot enforce, so without this relation two concurrent writers can both\n * pass the probe and both commit. Read through the `constraintClaims`\n * capability, never through member presence — see `claimSupport`\n * (`store/claims/backing.ts`).\n */\n claimEdgeCardinality?: (\n this: void,\n params: ClaimEdgeCardinalityParams,\n ) => Promise<EdgeClaimOutcome>;\n /**\n * Batched variant of `claimEdgeCardinality`: one multi-row create-or-lock\n * statement, then a takeover statement only for the entries a different edge\n * holds. Outcomes are returned positionally, one per entry.\n *\n * Callers must not pass two entries with the same conflict target\n * (`axis`, `key`): a multi-row upsert cannot affect one row twice.\n */\n claimEdgeCardinalityBatch?: (\n this: void,\n entries: readonly ClaimEdgeCardinalityParams[],\n ) => Promise<readonly EdgeClaimOutcome[]>;\n /**\n * Housekeeping only: drops the claim rows named edges hold, so hard deletes,\n * kind removal and `clearGraph` do not grow the relation without bound. The\n * FENCE never depends on it having run — a claim whose holder is no longer\n * live (or, for `oneActive`, no longer active) is taken over in place.\n */\n purgeEdgeClaims?: (\n this: void,\n params: PurgeEdgeClaimsParams,\n ) => Promise<void>;\n\n /**\n * Read-only diagnostic: the rows that make a declared constraint currently\n * violated, for a caller (`store.verifyConstraintFences()`) that folds them\n * onto the claim axes they contend for.\n *\n * It reads the ENTITY relations for the two families whose pre-upgrade\n * violations no claim row records — a database written before the claim\n * relations existed holds no edge claims at all — and the `uniques` relation\n * for uniqueness, where a pre-upgrade duplicate is exactly two live rows\n * sitting at two different `node_kind`s. A scan of a claim relation's primary\n * key can find neither, which is why this is not one.\n *\n * Returns the contending ROWS, not the verdict: the axis a `uniques` row\n * belongs to is a fold over the graph's subclass component, and the key an\n * edge's claim sits on is `EDGE_CARDINALITY_SPECS`' — both of which live\n * above the backend, so a backend that decided either would be a second\n * spelling of a decision the fence already owns.\n *\n * Writes nothing: no DDL, no claim writes. Safe on a replica and under a\n * least-privilege role. Present only on backends that can run the audit;\n * `store.verifyConstraintFences()` refuses with a typed error when absent\n * rather than reporting an empty (and therefore reassuring) result.\n */\n readConstraintFenceViolations?: (\n this: void,\n params: ReadConstraintFenceViolationsParams,\n ) => Promise<ConstraintFenceViolationRows>;\n\n // === Schema Operations ===\n getActiveSchema: (\n this: void,\n graphId: string,\n ) => Promise<SchemaVersionRow | undefined>;\n getSchemaVersion: (\n this: void,\n graphId: string,\n version: number,\n ) => Promise<SchemaVersionRow | undefined>;\n /**\n * Atomically inserts a new schema version and activates it as a single\n * transactional unit, with optimistic compare-and-swap on the currently\n * active version.\n *\n * - If `expected.kind === \"active\"` and the actual active version\n * differs, throws `StaleVersionError` (caller should refetch and\n * retry).\n * - If a row already exists at `params.version` with the same\n * `schemaHash`, returns it idempotently — reactivating it if it was\n * left inactive by an earlier crashed commit.\n * - If a row already exists at `params.version` with a different\n * `schemaHash`, throws `SchemaContentConflictError`.\n *\n * Requires `capabilities.transactions === true`. On non-transactional\n * backends (e.g. Cloudflare D1, drizzle-orm/neon-http) this method\n * throws `ConfigurationError` rather than running with degraded\n * atomicity that would silently re-introduce the orphan-row crash\n * window the primitive exists to eliminate.\n */\n commitSchemaVersion: (\n this: void,\n params: CommitSchemaVersionParams,\n ) => Promise<SchemaVersionRow>;\n /**\n * Commit a schema version only if every requested kind is empty. The probes\n * and schema CAS run under one backend-owned write fence, preventing a\n * participating schema-managed Store write from landing between the final\n * count and commit. Raw Stores and direct backend writes are not fenced.\n */\n commitSchemaVersionIfKindsEmpty?: (\n this: void,\n params: CommitSchemaVersionParams,\n probes: readonly Readonly<{\n entity: \"node\" | \"edge\";\n kind: string;\n }>[],\n ) => Promise<\n | Readonly<{ status: \"committed\"; row: SchemaVersionRow }>\n | Readonly<{\n status: \"populated\";\n kinds: readonly Readonly<{\n entity: \"node\" | \"edge\";\n kind: string;\n count: number;\n }>[];\n }>\n >;\n /**\n * Acquire the transaction-scoped shared fence for a schema-managed graph\n * write, then verify that the active schema still matches the Store that is\n * issuing it. Official transactional backends provide this method; custom\n * backends may omit it, in which case schema-managed Store writes fail\n * closed rather than racing a schema change. This method must be called on a\n * transaction-scoped backend. PostgreSQL locks the active schema row, so a\n * concurrent change either becomes visible at read committed or raises the\n * database's native serialization failure at stronger isolation.\n *\n * On rejection the thrown `StaleVersionError` reports the active\n * version this transaction can observe after the conflict resolves, so\n * `details.actual` names the version that won rather than the absence the\n * blocked read momentarily saw.\n */\n lockSchemaVersionForWrite?: (\n this: void,\n params: Readonly<{ graphId: string; expectedVersion: number }>,\n ) => Promise<void>;\n /**\n * Internal schema-lifecycle seam for features whose data preflight must\n * commit atomically with the schema CAS. The callback runs in the same\n * write transaction after the schema write fence is acquired and before the\n * version write; it receives no schema-version write methods, so callers\n * cannot bypass the CAS.\n *\n * The preflight target is a {@link SchemaCommitPreflightBackend}: the\n * transaction backend plus the schema-write DDL primitive, because a\n * transition may have to CREATE the storage its own preflight then fills —\n * and creating it outside this transaction would publish it empty whenever\n * the commit is refused.\n *\n * @internal\n */\n commitSchemaVersionWithPreflight?: (\n this: void,\n params: CommitSchemaVersionParams,\n preflight: (target: SchemaCommitPreflightBackend) => Promise<void>,\n ) => Promise<SchemaVersionRow>;\n /**\n * Atomically flips the active schema pointer to an existing version,\n * with optimistic compare-and-swap on the currently active version.\n * Used by `rollbackSchema` and any other \"promote/demote existing\n * version\" workflow. Throws `StaleVersionError` on CAS mismatch and\n * `MigrationError` if the target version row does not exist.\n *\n * Same transactional requirements as `commitSchemaVersion`.\n */\n setActiveVersion: (\n this: void,\n params: SetActiveVersionParams,\n ) => Promise<void>;\n\n /**\n * Run an administrative callback while holding the same per-graph lock as\n * schema commits. The callback receives the transaction-scoped backend, so\n * its reads, DML, and DDL are committed atomically before another schema\n * writer can proceed.\n *\n * This is intentionally absent from {@link TransactionBackend}: nesting a\n * schema-write lock from an already-open transaction can deadlock. It is an\n * optional backend capability so custom backends can decline operations\n * that require a schema fence rather than silently running them unsafely.\n */\n schemaWriteTransaction?: <T>(\n this: void,\n graphId: string,\n fn: (\n tx: TransactionBackend &\n Readonly<{\n executeStatement: NonNullable<TransactionBackend[\"executeStatement\"]>;\n tableExists: (this: void, tableName: string) => Promise<boolean>;\n executeSchemaDdl: (this: void, ddl: string) => Promise<void>;\n deleteSchemaVectorSlotContribution: (\n this: void,\n slot: VectorSlot,\n ) => Promise<void>;\n }>,\n ) => Promise<T>,\n ) => Promise<T>;\n\n // === Embedding Operations (optional - depends on vector capabilities) ===\n upsertEmbedding?: (\n this: void,\n params: UpsertEmbeddingParams,\n ) => Promise<void>;\n /**\n * Batched variant of `upsertEmbedding` for one vector slot. Optional —\n * callers fall back to per-row `upsertEmbedding` when unset.\n */\n upsertEmbeddingBatch?: (\n this: void,\n params: UpsertEmbeddingBatchParams,\n ) => Promise<void>;\n deleteEmbedding?: (\n this: void,\n params: DeleteEmbeddingParams,\n ) => Promise<void>;\n deleteEmbeddingBatch?: (\n this: void,\n params: Omit<DeleteEmbeddingParams, \"nodeId\"> &\n Readonly<{ nodeIds: readonly string[] }>,\n ) => Promise<void>;\n /**\n * KNN search over one `(nodeKind, fieldPath)` slot. Top-k is computed\n * over CURRENT nodes only — non-tombstoned AND inside their validity\n * window, matching a `current` read — so index drift (embedding rows\n * whose node was deleted or expired outside the store pipeline) can\n * neither surface nor crowd current rows out of the top-k. Custom\n * backends implementing this member must honor the same contract. Exact on pgvector >= 0.8 (HNSW via\n * `hnsw.iterative_scan = strict_order`; IVFFlat via\n * `ivfflat.iterative_scan = relaxed_order` plus a re-sort of the\n * bounded set; probe-bounded below 0.8) and sqlite-vec;\n * libSQL's DiskANN over-fetches 4x and post-filters (recall bounded by\n * that headroom).\n */\n vectorSearch?: (\n this: void,\n params: VectorSearchParams,\n ) => Promise<readonly VectorSearchResult[]>;\n createVectorIndex?: (\n this: void,\n params: CreateVectorIndexParams,\n ) => Promise<void>;\n dropVectorIndex?: (\n this: void,\n params: DropVectorIndexParams,\n ) => Promise<void>;\n /**\n * Single-statement hybrid search: both sources, RRF fusion, liveness\n * join, and node hydration composed into ONE statement — replacing the\n * facade's two search round trips plus id-hydration fetch. Optional;\n * the store falls back to the multi-statement path when unset (custom\n * backends, engines without window functions). Same liveness contract\n * as `vectorSearch` / `fulltextSearch`.\n */\n hybridSearch?: (\n this: void,\n params: HybridSearchParams,\n ) => Promise<readonly HybridSearchRow[]>;\n\n // === Fulltext Operations (optional - depends on fulltext capabilities) ===\n upsertFulltext?: (this: void, params: UpsertFulltextParams) => Promise<void>;\n deleteFulltext?: (this: void, params: DeleteFulltextParams) => Promise<void>;\n /**\n * Batched variant of `upsertFulltext`. Optional — callers fall back to\n * per-row `upsertFulltext` when unset.\n */\n upsertFulltextBatch?: (\n this: void,\n params: UpsertFulltextBatchParams,\n ) => Promise<void>;\n /**\n * Batched variant of `deleteFulltext`. Optional — callers fall back to\n * per-row `deleteFulltext` when unset.\n */\n deleteFulltextBatch?: (\n this: void,\n params: DeleteFulltextBatchParams,\n ) => Promise<void>;\n /**\n * Ranked fulltext search over one node kind. Like `vectorSearch`, top-k\n * is computed over CURRENT nodes only — non-tombstoned AND inside their\n * validity window (exact on both engines — plain WHERE, no top-k table\n * function involved). Custom backends implementing this member must\n * honor the same contract.\n */\n fulltextSearch?: (\n this: void,\n params: FulltextSearchParams,\n ) => Promise<readonly FulltextSearchResult[]>;\n\n // === Index Materialization (used by store.materializeIndexes) ===\n /**\n * Idempotently ensure ONLY the `typegraph_index_materializations`\n * table exists — separate from `bootstrapTables` so that\n * `materializeIndexes` doesn't pull in the full base-table DDL set\n * just to access the status table.\n *\n * Why focused: `bootstrapTables` issues 20+ `CREATE TABLE / CREATE\n * INDEX IF NOT EXISTS` statements covering every base table. Two\n * concurrent calls (e.g. two replicas of the same `schema_doc` both\n * starting up and calling `materializeIndexes`) race on\n * Postgres SHARE locks and DEADLOCK. Restricting the ensure-step to\n * the single status table eliminates the cross-table race entirely\n * — concurrent `CREATE TABLE IF NOT EXISTS` for one specific table\n * is well-behaved on Postgres.\n */\n ensureIndexMaterializationsTable?: (this: void) => Promise<void>;\n\n /**\n * Install the PostgreSQL `pg_trgm` extension under a database-global\n * concurrency fence. Relational trigram index materialization calls this\n * before emitting its index DDL; non-PostgreSQL backends omit it.\n *\n * @deprecated The `pg_trgm`-only spelling of {@link GraphBackend.ensureExtension},\n * which says the same thing for every extension the library installs. It is\n * retained — and still consulted, after `ensureExtension` — so a backend\n * written against 0.47 keeps its fence; the bundled PostgreSQL backend\n * implements it by delegating. Implement `ensureExtension` in new backends.\n */\n ensureTrigramExtension?: (this: void) => Promise<void>;\n\n /**\n * Idempotently ensure ONLY the `typegraph_revision_origins` table exists.\n *\n * Revision-tracked stores use this per-graph, durable random origin together\n * with the recorded clock to prevent two independent stores with coincident\n * timestamps from sharing a merge base anchor. It is focused rather than\n * using `bootstrapTables` so existing deployments can adopt revision anchors\n * without replaying all base-table DDL during a merge read.\n */\n ensureRevisionOriginsTable?: (this: void) => Promise<void>;\n\n /**\n * Idempotently ensure ONLY the three Operational Identity relations exist —\n * the current-assertions table, the recorded-time assertions table, and the\n * derived closure and separation tables — with their indexes and CHECK\n * constraints (CREATE TABLE / CREATE INDEX IF NOT EXISTS).\n *\n * First enablement of identity on an existing populated deployment attaches\n * via `createStore` / `createSqliteBackend` / `createPostgresBackend`, none\n * of which run DDL, so the enablement preflight would otherwise\n * SELECT/DELETE/INSERT tables that do not exist yet. The store calls this\n * before the enablement locks and closure rebuild. Focused rather than\n * `bootstrapTables` for the same concurrency rationale as\n * {@link ensureRevisionOriginsTable}. Stores call it before opening the\n * schema-commit transaction so DDL does not re-enter its per-graph lock.\n * Returns the logical names that were absent before this call. Callers set\n * `provisionMissing` only for safe first enablement; on an already-enabled\n * graph, missing ledger tables are deliberately left absent so a failed open\n * cannot make its next retry silently accept empty replacement storage.\n *\n * @internal\n */\n ensureIdentityTables?: (\n this: void,\n tableNames: IdentityTableNames,\n options: Readonly<{ provisionMissing: boolean }>,\n ) => Promise<readonly string[]>;\n\n /**\n * The idempotent CREATE statements for exactly the identity relations —\n * the same DDL {@link ensureIdentityTables} issues, handed back as data\n * instead of executed.\n *\n * Pure and synchronous: it executes nothing and probes no catalog, so it is\n * safe to call from inside an already-open transaction, where invoking a\n * top-level backend method would re-enter the backend's serialized statement\n * queue and deadlock. That is the whole point. Provisioning a missing DERIVED\n * relation (closure, separation) on an already-enabled graph has to CREATE it\n * and FILL it in one transaction, because a created-but-empty derived\n * relation is readable and answers every question with \"nothing\" — for the\n * separation relation, \"nothing\" means \"not separated\", which is exactly the\n * answer that lets a contradictory merge commit.\n *\n * A backend that omits this cannot offer that atomicity. Callers do not fall\n * back: when a fill is owed, the upgrade is REFUSED with the typed\n * `IDENTITY_UPGRADE_REQUIRES_ATOMIC_DDL` error naming this port — creating\n * and filling back-to-back would publish the readable-empty state the\n * paragraph above forbids.\n *\n * @internal\n */\n identityTableDdl?: (\n this: void,\n tableNames: IdentityTableNames,\n ) => readonly string[];\n\n /**\n * Look up a recorded materialization for a declared index by its\n * physical SQL index name. Returns `undefined` if no row exists.\n */\n getIndexMaterialization?: (\n this: void,\n indexName: string,\n ) => Promise<IndexMaterializationRow | undefined>;\n /**\n * Bulk variant of `getIndexMaterialization`: load every recorded\n * materialization whose `indexName` (status key) is in `statusKeys`,\n * in a single round-trip. Returned rows are unordered — callers index\n * by `indexName`. Optional; consumers fall back to per-key\n * `getIndexMaterialization` when unset.\n */\n getIndexMaterializations?: (\n this: void,\n statusKeys: readonly string[],\n ) => Promise<readonly IndexMaterializationRow[]>;\n /**\n * Upsert a materialization attempt — success or failure. Failure rows\n * preserve any prior `materializedAt` so the historical successful\n * timestamp survives across error windows.\n */\n recordIndexMaterialization?: (\n this: void,\n params: RecordIndexMaterializationParams,\n ) => Promise<void>;\n /**\n * Atomically claims the build of one index across every materializer\n * (process- and pool-safe: the status row's own atomicity is the mutex).\n * Returns true when this caller now holds the claim. Backends whose\n * index builds cannot deadlock across callers (SQLite: engine-level\n * write serialization, no CONCURRENTLY) omit it; `materializeIndexes`\n * then builds without a claim, exactly as before.\n */\n claimIndexMaterialization?: (\n this: void,\n params: ClaimIndexMaterializationParams,\n ) => Promise<boolean>;\n /** Releases a claim taken by `claimIndexMaterialization` (token-guarded). */\n releaseIndexMaterializationClaim?: (\n this: void,\n params: ReleaseIndexMaterializationClaimParams,\n ) => Promise<void>;\n\n // === Contribution Materialization (#135 — durable strategy-owned\n // storage marker, sibling of the index status table) ===\n\n /**\n * Idempotently ensure ONLY the\n * `typegraph_contribution_materializations` table exists. Same\n * focused-bootstrap rationale as `ensureIndexMaterializationsTable`:\n * a single `CREATE TABLE IF NOT EXISTS` is concurrency-safe under\n * replica startup, where the full `bootstrapTables` set risks a\n * Postgres SHARE-lock deadlock.\n */\n ensureContributionMaterializationsTable?: (this: void) => Promise<void>;\n\n /**\n * Look up the durable materialization marker for one strategy-owned\n * contribution identity. Returns `undefined` when no row exists\n * (\"never initialized\").\n */\n getContributionMaterialization?: (\n this: void,\n identity: ContributionMaterializationIdentity,\n ) => Promise<ContributionMaterializationRow | undefined>;\n\n /**\n * Upsert a contribution-materialization attempt — success or failure.\n * Failure rows preserve any prior `materializedAt` via COALESCE so a\n * later failed re-attempt doesn't erase the historical success.\n */\n recordContributionMaterialization?: (\n this: void,\n params: RecordContributionMaterializationParams,\n ) => Promise<void>;\n\n /**\n * Resolve (once per backend instance, cached) and assert the durable\n * materialization markers for every `runtimeEnsure` contribution this\n * backend's strategy declares, for `graphId`. Throws\n * `StoreNotInitializedError` when a marker is missing, stale\n * (signature drift), or recorded a failed last attempt.\n *\n * This is the single read-side gate the fulltext hot-path wrappers\n * and `store.transaction()` consult. It performs ZERO DDL and ZERO\n * marker writes — initialization is the exclusive job of the async\n * boot path (`createStoreWithSchema` → `ensureRuntimeContributions`).\n */\n assertRuntimeContributionsInitialized?: (\n this: void,\n graphId: string,\n ) => Promise<void>;\n\n // === Kind Removal Status ===\n\n /**\n * Bootstraps the per-deployment `typegraph_kind_removals` table so\n * `store.evolve()` can check pending removals and the removal verbs can\n * persist and materialize removal status. Mirrors the focused-bootstrap\n * rationale documented on `ensureIndexMaterializationsTable` — the full\n * `bootstrapTables` touches every base table and risks Postgres SHARE-lock\n * deadlock under concurrent replica startup.\n */\n ensureKindRemovalsTable?: (this: void) => Promise<void>;\n\n /**\n * List graph-extension kind removals whose data-cleanup pass has not yet\n * succeeded for this `graphId`. Returns rows with\n * `removedAt: undefined`. Order is unspecified; callers materialize\n * one-at-a-time and don't depend on it.\n */\n getPendingKindRemovals?: (\n this: void,\n graphId: string,\n ) => Promise<readonly KindRemovalRow[]>;\n\n /**\n * List ALL kind-removal rows for a `graphId` — pending and completed.\n * Used by `materializeRemovals()` reconciliation to detect rows that\n * are missing entirely (the `removeKinds()` crash window) versus\n * already completed. Without this distinction the reconciler would\n * have to upsert every expected historical removal on every call,\n * churning `last_attempted_at` on rows that long since succeeded.\n * Order is unspecified.\n */\n getAllKindRemovals?: (\n this: void,\n graphId: string,\n ) => Promise<readonly KindRemovalRow[]>;\n\n /**\n * Upsert a kind-removal status row. `removedAt: undefined` records\n * the pending state at schema-commit time; `removedAt: <iso>`\n * marks the data cleanup successful. The COALESCE rule on `removedAt`\n * mirrors `recordIndexMaterialization` so a later failure doesn't\n * clobber the historical successful timestamp from another replica.\n */\n recordKindRemoval?: (\n this: void,\n params: RecordKindRemovalParams,\n ) => Promise<void>;\n\n // === Reconciliation Watermark ===\n\n /**\n * Bootstraps the per-deployment `typegraph_reconciliation_markers`\n * table so `materializeRemovals()` can persist reconciliation\n * progress. Same focused-bootstrap rationale as the other status\n * tables — full `bootstrapTables` risks Postgres SHARE-lock\n * deadlock under concurrent replica startup.\n */\n ensureReconciliationMarkersTable?: (this: void) => Promise<void>;\n\n // === Table Contributions (#129) ===\n\n /**\n * Materializes every contribution flagged `runtimeEnsure` — the\n * strategy-owned runtime tables (fulltext today) that drizzle-kit-\n * managed setups don't create. Called once after a successful schema\n * load. Deliberately scoped: base/drizzle-visible tables are\n * `runtimeEnsure: false`, so this does not regress startup into\n * broad DDL/probing across every table.\n *\n * The canonical durable-marker writer (#135): for each runtime\n * contribution it short-circuits when the marker already records a\n * matching signature, otherwise runs the idempotent `createDdl` and\n * records the marker (success or failure) keyed by `graphId`.\n */\n ensureRuntimeContributions?: (this: void, graphId: string) => Promise<void>;\n\n /**\n * Privileged materializer for one embedding `(kind, field)` slot's\n * `ownedTables` contribution(s): creates the per-field vector table and\n * records its durable marker, idempotently. The vector counterpart of\n * `ensureRuntimeContributions` — vectors are per-`(kind, field)` and\n * graph-derived, so the store enumerates slots (via\n * `resolveEmbeddingFields`) and uses the batch counterpart at boot under\n * the privileged role. Pass `{ force: true }` to overwrite the marker\n * at the current signature, bypassing the drift-guard — the sanctioned\n * path `store.reembedVectorField()` uses after recreating storage at a\n * new dimension. Pass `{ onDrift: \"skip\" }` to leave an\n * already-provisioned slot whose shape has since changed untouched\n * (warn + no-op) instead of refusing — the boot/evolve path, so a\n * declared dimension change never blocks startup before the operator\n * can run `reembedVectorField`. Present only on backends wired with a\n * vector strategy.\n */\n ensureVectorSlotContribution?: (\n this: void,\n slot: VectorSlot,\n options?: Readonly<{ force?: boolean; onDrift?: \"throw\" | \"skip\" }>,\n ) => Promise<void>;\n\n /**\n * Batch form of `ensureVectorSlotContribution`, used by privileged boot to\n * resolve every slot's durable markers with one graph-scoped query. The\n * singular method remains available for re-embedding and compatibility.\n */\n ensureVectorSlotContributions?: (\n this: void,\n slots: readonly VectorSlot[],\n options?: Readonly<{ force?: boolean; onDrift?: \"throw\" | \"skip\" }>,\n ) => Promise<void>;\n\n /**\n * SELECT-only gate for one embedding `(kind, field)` slot: asserts the\n * durable marker(s) for the slot's `ownedTables` contribution(s),\n * cached per backend instance. Throws `StoreNotInitializedError` when\n * the slot is missing, stale (signature drift), or recorded a failed\n * last attempt. The vector counterpart of\n * `assertRuntimeContributionsInitialized`, consulted by the verified\n * runtime attach. Performs ZERO DDL and ZERO writes. Present only on\n * backends wired with a vector strategy.\n */\n assertVectorSlotInitialized?: (this: void, slot: VectorSlot) => Promise<void>;\n\n /**\n * Batch form of `assertVectorSlotInitialized`, used by verified attach to\n * resolve every slot's durable markers with one graph-scoped query.\n */\n assertVectorSlotsInitialized?: (\n this: void,\n slots: readonly VectorSlot[],\n ) => Promise<void>;\n\n /**\n * Forget one embedding `(kind, field)` slot's durable contribution\n * marker(s). Called after the slot's per-field table is dropped\n * (vector-field reclaim) so a later `ensureVectorSlotContribution`\n * re-creates the table instead of trusting an orphaned \"initialized\"\n * marker. Does NOT drop the table itself (the caller already did).\n * Present only on backends wired with a vector strategy.\n */\n deleteVectorSlotContribution?: (\n this: void,\n slot: VectorSlot,\n ) => Promise<void>;\n\n /**\n * Diagnostic: compare each contribution currently expected for `graphId`\n * against its durable marker and the physical catalog.\n *\n * Covers the strategy-owned `runtimeEnsure` contributions (fulltext)\n * plus the `ownedTables` contribution(s) of each supplied vector slot.\n * Returns one {@link ContributionDiagnostic} per detected unusable\n * contribution. A recorded failed materialization is unusable even when\n * marker and catalog agree that no table was produced. A contribution with\n * neither a marker nor a table is treated as never attempted and omitted;\n * marker rows outside the current declaration set are not audited. An empty\n * array therefore is not proof that storage was initialized.\n *\n * Deliberately NOT part of the open path. `ensureRuntimeContributions`\n * and `assertRuntimeContributionsInitialized` short-circuit on a\n * per-instance signature cache and then on the marker row alone, which\n * is the right default for a hot path but leaves a database whose\n * contribution tables were dropped out of band opening clean and\n * failing at the first dependent read or write. This method is the explicit,\n * operator-invoked catalog probe that fills that gap: it issues one uncached\n * existence query per distinct physical table and performs ZERO DDL\n * and ZERO writes, so it is safe under a least-privilege runtime role.\n *\n * Present only on backends that can probe their own catalog.\n */\n verifyContributions?: (\n this: void,\n graphId: string,\n vectorSlots: readonly VectorSlot[],\n ) => Promise<readonly ContributionDiagnostic[]>;\n\n /**\n * Re-audit current contribution declarations and non-destructively repair\n * `missing-marker` and `failed-materialization` findings. The backend must\n * resolve every target itself; it must not accept caller-provided physical\n * identities or DDL. `stale` and `orphaned-marker` findings are returned as\n * `requires-rebuild` because repairing either can require destructive data\n * reconstruction.\n *\n * Present only on backends that can both probe their catalog and run the\n * strategy-owned contribution DDL.\n */\n repairContributions?: (\n this: void,\n graphId: string,\n vectorSlots: readonly VectorSlot[],\n ) => Promise<ContributionRepairResult>;\n\n /**\n * Read-only readiness probe: the same marker-versus-catalog audit\n * `verifyContributions` performs, projected onto one entry per search\n * projection. Shares that method's detection logic exactly — a second\n * implementation would be free to disagree with the one the hot-path\n * gate actually consults, and a health check that disagrees with the\n * gate is worse than none.\n *\n * Writes nothing: no DDL, no marker writes, no effect on the\n * per-instance caches the hot path relies on. Safe on a read path, on\n * a replica, and under a least-privilege role.\n *\n * Present only on backends that can probe their catalog; a backend\n * that provisions contributions without this method declares the gap\n * as `capabilities.contributions.probe === false`.\n */\n probeContributions?: (\n this: void,\n graphId: string,\n vectorSlots: readonly VectorSlot[],\n ) => Promise<readonly ContributionProbeEntry[]>;\n\n /**\n * Destructively rebuild one search projection for one graph: clear that\n * graph's rows from the projection's storage, ensure the storage matches\n * the current `createDdl`, reconstruct the graph's content, and stamp the\n * durable marker at the current signature.\n *\n * This is the repair `repairContributions` deliberately refuses to\n * perform, and it must never be reachable from it. A `stale`\n * contribution's table exists at the *old* physical shape, so the\n * idempotent `CREATE ... IF NOT EXISTS` in the ordinary ensure path\n * no-ops; re-stamping the marker there would leave it blessing a table\n * whose shape is wrong, which is exactly what the drift guard exists\n * to prevent. Only a drop makes the recreate meaningful, and destroying\n * content is a decision the caller states rather than one a flag named\n * `force` implies.\n *\n * The fulltext projection's storage is one physical table shared by every\n * graph in the database, while this call is fenced per graph.\n * Implementations must therefore scope the teardown to `graphId` and may\n * drop the storage only when doing so destroys no other graph's rows;\n * when the recorded shape is stale and the drop that would repair it is\n * not available, they refuse (`shared-storage-in-use`) rather than\n * re-stamp a shape nothing verified.\n *\n * Runs the whole sequence inside one transaction under the same\n * per-graph fence as a schema commit, so an interrupted rebuild leaves\n * the contribution exactly as it was rather than attested-but-empty.\n *\n * `repopulate` receives that transaction and reconstructs content from\n * rows TypeGraph already stores. The inversion exists because deciding\n * *what* content a node contributes needs the schema registry, which\n * is a Store-layer concern the backend must not reach into.\n *\n * @param scope which projection to rebuild. Implementations must\n * refuse `\"vector\"`: embeddings live only in the storage this would\n * drop, so there is nothing to reconstruct them from.\n */\n rebuildContribution?: (\n this: void,\n graphId: string,\n scope: ContributionRebuildScope,\n repopulate: (\n target: TransactionBackend,\n ) => Promise<ContributionRepopulationStats>,\n ) => Promise<ContributionRebuildResult>;\n\n /**\n * Bootstraps the fulltext storage table the active `FulltextStrategy`\n * owns. Same focused-bootstrap rationale as the other `ensure*Table`\n * methods: idempotent and concurrency-safe under replica startup.\n *\n * Superseded by `ensureRuntimeContributions()` (#129); retained as\n * a thin back-compat wrapper for backends/callers predating #129. Not\n * machine-`@deprecated` because the manager still calls it as the\n * pre-#129 fallback. #135 removed the remaining hot-path callers and\n * routed this through the durable-marker writer.\n */\n ensureFulltextTable?: (this: void, graphId: string) => Promise<void>;\n\n /**\n * Read the high-water mark schema version for which\n * `materializeRemovals` reconciliation has already verified history\n * for `graphId`. Returns `undefined` when no marker has been\n * recorded yet. Used to skip already-checked transitions in the\n * recovery walk.\n */\n getReconciliationMarker?: (\n this: void,\n graphId: string,\n ) => Promise<number | undefined>;\n\n /**\n * Persist the reconciliation high-water mark for `graphId`. Called\n * after `materializeRemovals` completes a clean walk; subsequent\n * calls walk only versions newer than this marker. Idempotent\n * upsert by `graphId`.\n */\n setReconciliationMarker?: (\n this: void,\n graphId: string,\n version: number,\n ) => Promise<void>;\n\n // === Graph Lifecycle ===\n /**\n * Hard-deletes all data for a graph (nodes, edges, uniques, embeddings, schema versions).\n * Intended for import-replacement workflows. No hooks, no per-row logic.\n */\n clearGraph: (this: void, graphId: string) => Promise<void>;\n\n /**\n * Creates the base TypeGraph tables if they don't already exist.\n *\n * Called automatically by `createStoreWithSchema()` when a fresh database\n * is detected. Users who manage DDL themselves via `createStore()` never\n * hit this path.\n */\n bootstrapTables?: (this: void) => Promise<void>;\n\n /**\n * Refreshes the backend's query-planner statistics.\n *\n * Call this once after a large initial import or bulk backfill. Without\n * up-to-date statistics, the planner can pick suboptimal execution plans\n * — on PostgreSQL this is the difference between a 0.5ms and a 5ms\n * forward traversal; on SQLite it's the difference between 0.9ms and\n * 23ms fulltext search. Autovacuum / background statistics collection\n * will catch up eventually, but calling this explicitly after a bulk\n * load gives you correct latencies immediately.\n *\n * Implementations:\n * - SQLite runs `ANALYZE`, which populates `sqlite_stat1`\n * - PostgreSQL runs `ANALYZE` on the TypeGraph-managed tables\n *\n * Safe to call at any time; costs a few tens of milliseconds on the\n * sizes this library is designed for.\n */\n refreshStatistics: (this: void) => Promise<void>;\n\n /**\n * Runs an intentionally trusted, all-or-nothing initial import.\n *\n * Optional because only backends that can pin one transaction and use a\n * native high-throughput write path expose it. Implementations must reject\n * a database whose TypeGraph node or edge tables already contain rows. They\n * may temporarily remove rebuildable secondary indexes, but must restore\n * them before committing. Any callback, insert, or rebuild failure rolls the\n * entire operation back.\n *\n * This is a top-level-only lifecycle operation and is deliberately absent\n * from {@link TransactionBackend}. When `schemaWrite` is supplied, the\n * import acquires and validates that Store version's managed-write fence\n * before checking emptiness or writing. Omitting it retains the raw,\n * unversioned import behavior.\n */\n trustedImport?: <T>(\n this: void,\n fn: (session: TrustedImportSession) => Promise<T>,\n options?: Readonly<{\n schemaWrite?: Readonly<{ graphId: string; expectedVersion: number }>;\n }>,\n ) => Promise<T>;\n\n // === Query Execution ===\n execute: <T>(this: void, query: CompiledRowsSql) => Promise<readonly T[]>;\n\n /**\n * Execute a non-row-returning SQL statement bound to this backend or\n * transaction. Optional because custom backends may only expose the public\n * row-returning query path; features that need statement execution must\n * capability-check and fail loudly.\n */\n executeStatement?: (this: void, query: CompiledStatementSql) => Promise<void>;\n\n /**\n * Execute an internally compiled statement against connection-local\n * temporary state. History wrappers preserve this path because it cannot\n * mutate graph or history tables; callers cannot construct its branded\n * input through the public API.\n */\n executeTemporaryStatement?: (\n this: void,\n query: CompiledTemporaryStatementSql,\n ) => Promise<void>;\n\n /** Execute pre-compiled SQL text with bound parameters. Available on sync SQLite and pg backends. */\n executeRaw?: <T>(\n this: void,\n sqlText: string,\n params: readonly unknown[],\n ) => Promise<readonly T[]>;\n\n /** Compile a TypeGraph `SqlFragment` to `{ sql, params }` without executing. */\n compileSql?: (\n this: void,\n query: SqlFragment,\n ) => Readonly<{ sql: string; params: readonly unknown[] }>;\n\n /**\n * Execute a DDL statement that returns no rows (CREATE INDEX,\n * CREATE TABLE, ALTER TABLE, etc.). Separate from `executeRaw`\n * because some drivers (better-sqlite3) require `.run()` for DDL\n * and `.all()` for queries — the ambiguity can't be resolved by\n * inspecting the SQL string portably.\n *\n * Postgres path can use this for `CREATE INDEX CONCURRENTLY`, which\n * cannot run inside a transaction. Implementations must execute the\n * statement outside `transaction(...)`.\n */\n executeDdl?: (this: void, ddl: string) => Promise<void>;\n\n /**\n * Installs a database-global extension idempotently, tolerating the\n * concurrent-install race.\n *\n * `CREATE EXTENSION IF NOT EXISTS` is not a concurrency primitive on\n * PostgreSQL: the existence check cannot see another session's uncommitted\n * `pg_extension` row, so the loser of a race waits for the winner and is\n * then handed SQLSTATE 23505 instead of the harmless \"already exists\"\n * notice (#446). A backend implementing this member owns that retry, and\n * may additionally serialize same-extension installers behind a fence of its\n * own (the bundled PostgreSQL backend takes a transaction advisory lock\n * keyed on the extension, #475).\n *\n * This is the one member the library asks for an extension install; it\n * supersedes {@link GraphBackend.ensureTrigramExtension}, which said the same\n * thing for one extension.\n *\n * Like `executeDdl`, implementations MUST run the statement at the\n * top-level backend, never inside `transaction(...)`: the 23505 aborts the\n * enclosing transaction, so a retry issued inside one would only collect\n * `25P02` on the way out. That is why this member is absent from\n * {@link TransactionBackend}.\n */\n ensureExtension?: (this: void, name: DatabaseExtensionName) => Promise<void>;\n\n // === Transaction ===\n /** Runs TypeGraph operations inside a backend-owned transaction. */\n transaction: <T>(\n this: void,\n fn: (tx: TransactionBackend) => Promise<T>,\n options?: TransactionOptions,\n ) => Promise<T>;\n\n // === Lifecycle ===\n close: (this: void) => Promise<void>;\n}>;\n\n/**\n * Adapter-native transaction interoperability layered on top of the portable\n * TypeGraph backend. Only adapter entrypoints expose this capability.\n */\nexport type AdapterBackend<TNativeTransaction> = GraphBackend &\n Readonly<{\n /**\n * Runs TypeGraph operations and exposes the exact adapter-native handle\n * bound to the same transaction.\n */\n transactionWithNative: <T>(\n this: void,\n fn: (\n tx: TransactionBackend,\n nativeTransaction: TNativeTransaction,\n ) => Promise<T>,\n options?: TransactionOptions,\n ) => Promise<T>;\n /** Adopts a caller-owned, already-open adapter transaction. */\n adoptTransaction: (\n this: void,\n externalTransaction: TNativeTransaction,\n ) => TransactionBackend;\n }>;\n\nexport type BackendIdentity = Pick<\n GraphBackend,\n | \"dialect\"\n | \"capabilities\"\n | \"tableNames\"\n | \"fulltextStrategy\"\n | \"vectorStrategy\"\n>;\n\nexport type NodeEntityReadBackend = Pick<\n GraphBackend,\n \"getNode\" | \"getNodes\" | \"findNodesByKind\" | \"countNodesByKind\"\n>;\n\nexport type NodeEntityWriteBackend = Pick<\n GraphBackend,\n | \"insertNode\"\n | \"insertNodeNoReturn\"\n | \"insertNodesBatch\"\n | \"insertNodesBatchReturning\"\n | \"updateNode\"\n | \"updateNodeSet\"\n | \"deleteNode\"\n | \"hardDeleteNode\"\n>;\n\nexport type EdgeEntityReadBackend = Pick<\n GraphBackend,\n | \"getEdge\"\n | \"getEdges\"\n | \"countEdgesFrom\"\n | \"edgeExistsBetween\"\n | \"findEdgesConnectedTo\"\n | \"findEdgesByKind\"\n | \"findEdgesByEndpointSet\"\n | \"findEdgesByHeterogeneousEndpointSet\"\n | \"countEdgesByKind\"\n>;\n\nexport type EdgeEntityWriteBackend = Pick<\n GraphBackend,\n | \"insertEdge\"\n | \"insertEdgeNoReturn\"\n | \"insertEdgesBatch\"\n | \"insertEdgesBatchReturning\"\n | \"updateEdge\"\n | \"deleteEdge\"\n | \"deleteEdgesBatch\"\n | \"hardDeleteEdge\"\n | \"hardDeleteEdgesBatch\"\n>;\n\nexport type GraphEntityReadBackend = NodeEntityReadBackend &\n EdgeEntityReadBackend;\n\nexport type GraphEntityWriteBackend = NodeEntityWriteBackend &\n EdgeEntityWriteBackend;\n\nexport type UniqueConstraintBackend = Pick<\n GraphBackend,\n | \"insertUnique\"\n | \"insertUniqueBatch\"\n | \"deleteUnique\"\n | \"hardDeleteUniquesByNodeIds\"\n | \"hardDeleteUniquesByConcreteKind\"\n | \"checkUnique\"\n | \"checkUniqueBatch\"\n>;\n\nexport type SchemaReadBackend = Pick<\n GraphBackend,\n \"getActiveSchema\" | \"getSchemaVersion\"\n>;\n\nexport type SchemaCommitBackend = Pick<\n GraphBackend,\n \"commitSchemaVersion\" | \"commitSchemaVersionIfKindsEmpty\" | \"setActiveVersion\"\n>;\n\nexport type SchemaWriteFenceBackend = Pick<\n GraphBackend,\n \"lockSchemaVersionForWrite\"\n>;\n\nexport type VectorOperationBackend = Pick<\n GraphBackend,\n | \"upsertEmbedding\"\n | \"upsertEmbeddingBatch\"\n | \"deleteEmbedding\"\n | \"deleteEmbeddingBatch\"\n | \"vectorSearch\"\n | \"createVectorIndex\"\n | \"dropVectorIndex\"\n>;\n\nexport type FulltextOperationBackend = Pick<\n GraphBackend,\n | \"upsertFulltext\"\n | \"deleteFulltext\"\n | \"upsertFulltextBatch\"\n | \"deleteFulltextBatch\"\n | \"fulltextSearch\"\n>;\n\nexport type IndexMaterializationBackend = Pick<\n GraphBackend,\n | \"ensureIndexMaterializationsTable\"\n | \"getIndexMaterialization\"\n | \"getIndexMaterializations\"\n | \"recordIndexMaterialization\"\n>;\n\nexport type ContributionMaterializationBackend = Pick<\n GraphBackend,\n | \"ensureContributionMaterializationsTable\"\n | \"getContributionMaterialization\"\n | \"recordContributionMaterialization\"\n | \"assertRuntimeContributionsInitialized\"\n | \"ensureRuntimeContributions\"\n | \"ensureFulltextTable\"\n>;\n\nexport type RemovalMaterializationBackend = Pick<\n GraphBackend,\n | \"ensureKindRemovalsTable\"\n | \"getPendingKindRemovals\"\n | \"getAllKindRemovals\"\n | \"recordKindRemoval\"\n | \"ensureReconciliationMarkersTable\"\n | \"getReconciliationMarker\"\n | \"setReconciliationMarker\"\n>;\n\nexport type GraphLifecycleBackend = Pick<\n GraphBackend,\n \"clearGraph\" | \"bootstrapTables\"\n>;\n\nexport type QueryExecutionBackend = Pick<GraphBackend, \"execute\">;\n\nexport type RawQueryExecutionBackend = Pick<\n GraphBackend,\n \"executeRaw\" | \"compileSql\"\n>;\n\nexport type SqlCompilationBackend = Pick<GraphBackend, \"compileSql\">;\n\nexport type RawStatementExecutionBackend = Pick<\n GraphBackend,\n \"executeStatement\" | \"executeTemporaryStatement\" | \"executeDdl\"\n>;\n\nexport type BackendMaintenance = Pick<GraphBackend, \"refreshStatistics\">;\n\nexport type BackendTransactions = Pick<GraphBackend, \"transaction\">;\n\nexport type AdapterBackendTransactions<TNativeTransaction> = Pick<\n AdapterBackend<TNativeTransaction>,\n \"transactionWithNative\" | \"adoptTransaction\"\n>;\n\nexport type BackendLifecycle = Pick<GraphBackend, \"close\">;\n\n/**\n * Read-oriented transaction projection exposed by portable transaction\n * contexts. It preserves snapshot-aware graph reads and TypeGraph-compiled\n * row queries while excluding graph writes, arbitrary raw SQL, DDL,\n * transaction adoption, and lifecycle control.\n */\nexport type TransactionReadBackend = Readonly<\n BackendIdentity &\n GraphEntityReadBackend &\n SchemaReadBackend &\n QueryExecutionBackend &\n SqlCompilationBackend\n>;\n\n/**\n * Transaction backend — a backend scoped to a transaction.\n *\n * This is an explicit facet composition, not `Omit<GraphBackend, ...>`.\n * New top-level-only backend methods therefore do not silently appear on\n * transaction-scoped backends. `commitSchemaVersion`, `setActiveVersion`,\n * `refreshStatistics`, `transaction`, `adoptTransaction`, and `close` stay\n * deliberately absent: their guarantees depend on the top-level backend or\n * lifecycle owner rather than an already-open transaction.\n */\nexport type TransactionBackend = Readonly<\n BackendIdentity &\n GraphEntityReadBackend &\n GraphEntityWriteBackend &\n UniqueConstraintBackend &\n // The edge cardinality claim relation's surface. Inline rather than a named\n // member type: it is a different relation from `uniques` with a different\n // key, and naming it would add a type the public surface references but no\n // entrypoint exports.\n Pick<\n GraphBackend,\n \"claimEdgeCardinality\" | \"claimEdgeCardinalityBatch\" | \"purgeEdgeClaims\"\n > &\n SchemaReadBackend &\n Pick<GraphBackend, \"lockSchemaVersionForWrite\"> &\n VectorOperationBackend &\n FulltextOperationBackend &\n IndexMaterializationBackend &\n ContributionMaterializationBackend &\n RemovalMaterializationBackend &\n GraphLifecycleBackend &\n QueryExecutionBackend &\n RawQueryExecutionBackend &\n RawStatementExecutionBackend\n>;\n\n/**\n * Transaction backend exposed only while the backend's schema-write lock is\n * held. Unlike the ordinary top-level `executeDdl` port, this DDL primitive is\n * explicitly transaction-scoped and must use the callback's transaction.\n *\n * @internal\n */\nexport type SchemaWriteTransactionBackend = TransactionBackend &\n Readonly<{\n executeStatement: NonNullable<TransactionBackend[\"executeStatement\"]>;\n tableExists: (this: void, tableName: string) => Promise<boolean>;\n executeSchemaDdl: (this: void, ddl: string) => Promise<void>;\n deleteSchemaVectorSlotContribution: (\n this: void,\n slot: VectorSlot,\n ) => Promise<void>;\n }>;\n\n/**\n * The target a schema-commit preflight runs against: a transaction backend\n * that MAY also carry the schema-write DDL primitive.\n *\n * Optional, deliberately. Both bundled backends pass the same\n * {@link SchemaWriteTransactionBackend} their `schemaWriteTransaction` exposes,\n * so DDL is available there; a custom backend may implement\n * {@link GraphBackend.commitSchemaVersionWithPreflight} over a transaction that\n * cannot run DDL. Declaring the port as present-or-absent lets a preflight that\n * needs it refuse with a typed error naming the capability, instead of calling\n * `undefined` — or, far worse, skipping the DDL and continuing.\n *\n * @internal\n */\nexport type SchemaCommitPreflightBackend = TransactionBackend &\n Readonly<{\n executeSchemaDdl?: (this: void, ddl: string) => Promise<void>;\n }>;\n\n/**\n * Builds the actual runtime projection exposed as `TransactionContext.backend`.\n * This is an explicit object rather than a type-only narrowing so portable\n * callers cannot discover write or arbitrary-SQL methods on the underlying\n * transaction backend at runtime.\n *\n * @internal\n */\nexport function createTransactionReadBackend(\n backend: TransactionBackend,\n): TransactionReadBackend {\n const getNodes = backend.getNodes;\n const getEdges = backend.getEdges;\n const compileSql = backend.compileSql;\n\n return Object.freeze({\n dialect: backend.dialect,\n capabilities: backend.capabilities,\n ...(backend.tableNames === undefined ?\n {}\n : { tableNames: backend.tableNames }),\n ...(backend.fulltextStrategy === undefined ?\n {}\n : { fulltextStrategy: backend.fulltextStrategy }),\n ...(backend.vectorStrategy === undefined ?\n {}\n : { vectorStrategy: backend.vectorStrategy }),\n getNode: (graphId, kind, id) => backend.getNode(graphId, kind, id),\n ...(getNodes === undefined ?\n {}\n : {\n getNodes: (graphId: string, kind: string, ids: readonly string[]) =>\n getNodes(graphId, kind, ids),\n }),\n getEdge: (graphId, id) => backend.getEdge(graphId, id),\n ...(getEdges === undefined ?\n {}\n : {\n getEdges: (graphId: string, ids: readonly string[]) =>\n getEdges(graphId, ids),\n }),\n countEdgesFrom: (params) => backend.countEdgesFrom(params),\n edgeExistsBetween: (params) => backend.edgeExistsBetween(params),\n findEdgesConnectedTo: (params) => backend.findEdgesConnectedTo(params),\n findNodesByKind: (params) => backend.findNodesByKind(params),\n countNodesByKind: (params) => backend.countNodesByKind(params),\n findEdgesByKind: (params) => backend.findEdgesByKind(params),\n countEdgesByKind: (params) => backend.countEdgesByKind(params),\n getActiveSchema: (graphId) => backend.getActiveSchema(graphId),\n getSchemaVersion: (graphId, version) =>\n backend.getSchemaVersion(graphId, version),\n execute: <T>(query: CompiledRowsSql) => backend.execute<T>(query),\n ...(compileSql === undefined ?\n {}\n : { compileSql: (query: SqlFragment) => compileSql(query) }),\n });\n}\n\n/**\n * Closes an owned resource after provisioning fails without replacing the\n * original domain error with a secondary cleanup failure.\n */\nexport async function closeAfterFailure(\n resource: Readonly<{ close: () => void | Promise<void> }>,\n provisioningError: unknown,\n): Promise<never> {\n try {\n await resource.close();\n } catch {\n // The provisioning failure remains the actionable error. Managed close is\n // retryable when the resource is returned, but a failed factory has no\n // handle it can safely expose for a second attempt.\n }\n throw provisioningError;\n}\n\n/** Options for {@link runOptionallyInTransaction}. */\nexport type RunOptionallyInTransactionOptions = Readonly<{\n /**\n * Pass only when the toplevel backend method would recurse — pass the\n * operation-level backend so the no-transaction path doesn't loop back\n * through the same toplevel method.\n */\n fallback?: GraphBackend | TransactionBackend;\n /**\n * Options for the transaction this opens — most usefully\n * `accessMode: \"read_only\"`, which lets a multi-statement READ declare itself\n * to the engine instead of only promising it (SQLite issues `BEGIN` rather\n * than reserving the single writer slot with `BEGIN IMMEDIATE`; PostgreSQL\n * issues `BEGIN … READ ONLY`).\n *\n * Scopes the transaction and nothing else, so it is not honored on the\n * fallthrough path: a backend reporting `transactions: false` opens no\n * transaction to configure. The callback receives an explicit execution\n * context stating whether a transaction was opened, so callers never infer\n * atomicity from backend object identity.\n */\n transaction?: TransactionOptions;\n}>;\n\n/** What {@link runOptionallyInTransaction} actually did for this invocation. */\nexport type OptionalTransactionExecution = Readonly<{\n /** True only when the helper opened a transaction around the callback. */\n atomic: boolean;\n}>;\n\n/**\n * Runs `fn` inside a transaction when given a top-level backend that supports\n * one, falling through to a direct invocation otherwise. Lets call sites benefit\n * from atomicity on backends that have transactions while staying functional on\n * backends that don't (Cloudflare D1, `drizzle-orm/neon-http` over HTTP), and\n * avoids opening nested transactions when the caller already holds a\n * transaction-scoped backend. The single-statement race window is already\n * implicit on any backend that reports `transactions: false`; callers that\n * cannot tolerate it must branch on the capability themselves.\n */\nexport async function runOptionallyInTransaction<T>(\n backend: GraphBackend | TransactionBackend,\n fn: (\n target: GraphBackend | TransactionBackend,\n execution: OptionalTransactionExecution,\n ) => Promise<T>,\n options?: RunOptionallyInTransactionOptions,\n): Promise<T> {\n if (\"transaction\" in backend && backend.capabilities.transactions) {\n return backend.transaction(\n (tx) => fn(tx, { atomic: true }),\n options?.transaction,\n );\n }\n return fn(options?.fallback ?? backend, { atomic: false });\n}\n\n// ============================================================\n// Additional Parameter Types\n// ============================================================\n\n/**\n * Parameters for inserting a unique constraint entry.\n */\nexport type InsertUniqueParams = Readonly<{\n graphId: string;\n nodeKind: string;\n constraintName: string;\n key: string;\n nodeId: string;\n concreteKind: string;\n}>;\n\n/**\n * Parameters for releasing a unique constraint entry.\n *\n * Every release is scoped to the claim's OWNER — the pair\n * `(concreteKind, nodeId)`, because a node id is unique only within its kind.\n * The two shapes differ only in whether the claim AXIS participates:\n *\n * - **Lifecycle release** (`nodeKind` absent) — \"give up every claim this node\n * holds for this constraint and key, whatever axis it sits on\". Soft delete,\n * an update's key-change release and the resurrect diff use it, so a claim\n * written under an older axis is still released by newer code.\n * - **Compensating release** (`nodeKind` present) — \"undo exactly the row I\n * just claimed, at the axis I claimed it on\". A failed write's rollback uses\n * it, so it touches neither a row that predates the write nor one another\n * node holds.\n */\nexport type DeleteUniqueParams = Readonly<{\n graphId: string;\n /**\n * The claim axis (`uniques.node_kind`) the release is restricted to. Absent\n * releases the owner's claims at every axis — see the two shapes above.\n */\n nodeKind?: string;\n constraintName: string;\n key: string;\n /** The claim owner's concrete kind (`uniques.concrete_kind`). */\n concreteKind: string;\n /** The claim owner's node id (`uniques.node_id`). */\n nodeId: string;\n}>;\n\n/** Parameters for permanently clearing uniqueness sidecars by node identity. */\nexport type HardDeleteUniquesByNodeIdsParams = Readonly<{\n graphId: string;\n concreteKind: string;\n nodeIds: readonly string[];\n}>;\n\n/** Parameters for permanently clearing every uniqueness claim a kind owns. */\nexport type HardDeleteUniquesByConcreteKindParams = Readonly<{\n graphId: string;\n concreteKind: string;\n}>;\n\n/**\n * One edge cardinality claim, named by the components its axis, its key and its\n * holder-liveness predicate are all built from.\n *\n * The components are passed RAW rather than pre-rendered: `EDGE_CARDINALITY_SPECS`\n * (`store/claims/edge-claims.ts`) is the one table that decides which endpoints\n * the key covers and what a holder must still be, and both the TypeScript probe\n * and the SQL builder read it. A caller that rendered the axis and key itself\n * would be a second spelling of that decision.\n */\nexport type ClaimEdgeCardinalityParams = Readonly<{\n graphId: string;\n /** The declared cardinality; `many` declares nothing and never claims. */\n cardinality: Exclude<Cardinality, \"many\">;\n edgeKind: string;\n /** The edge that will hold the axis if this claim lands. */\n edgeId: string;\n fromKind: string;\n fromId: string;\n toKind: string;\n toId: string;\n}>;\n\n/**\n * What a claim statement decided.\n *\n * `refused` carries the incumbent so the caller can say which edge holds the\n * axis; the typed refusal itself is the store's, built from the same\n * `checkCardinality` / `checkUniqueEdge` owners the probe uses, so a caller\n * cannot tell which layer refused.\n */\nexport type EdgeClaimOutcome =\n | Readonly<{ status: \"claimed\" }>\n | Readonly<{ status: \"refused\"; holderEdgeId: string }>;\n\n/** Parameters for the housekeeping purge of claims held by named edges. */\nexport type PurgeEdgeClaimsParams = Readonly<{\n graphId: string;\n edgeIds: readonly string[];\n}>;\n\n/** One edge kind's declared cardinality, as the fence audit reads it. */\nexport type EdgeCardinalityDeclaration = Readonly<{\n edgeKind: string;\n /** `many` declares nothing, so it is unrepresentable here. */\n cardinality: Exclude<Cardinality, \"many\">;\n}>;\n\n/**\n * What a constraint-fence audit asks the database about: the declarations the\n * graph carries, one list per family.\n *\n * The declarations are passed rather than discovered because the backend holds\n * no schema — and because the `uniqueConstraintNames` restriction is\n * load-bearing rather than an optimization: the `uniques` relation also holds\n * disjointness claims, whose `node_kind` is a pair label rather than a kind and\n * for which no uniqueness axis can be computed. Those rows are covered by\n * `disjointKindPairs`, from the nodes relation.\n */\nexport type ReadConstraintFenceViolationsParams = Readonly<{\n graphId: string;\n /** Every unique constraint name the graph declares, in any scope. */\n uniqueConstraintNames: readonly string[];\n /** Every declared `disjointWith` pair, each read as one intersection. */\n disjointKindPairs: readonly (readonly [string, string])[];\n /** Every edge kind declaring a cardinality other than `many`. */\n edgeCardinalities: readonly EdgeCardinalityDeclaration[];\n}>;\n\n/**\n * One live `uniques` row that shares its `(constraint_name, key)` with at least\n * one other live row — a candidate, not a verdict: two rows contend only when\n * their `node_kind`s fold onto ONE claim axis, which the caller decides.\n */\nexport type ContendedUniqueRow = Readonly<{\n nodeKind: string;\n constraintName: string;\n key: string;\n concreteKind: string;\n nodeId: string;\n}>;\n\n/**\n * One live edge that shares its declared cardinality's population with at least\n * one other live edge. The endpoints are returned whole so the caller can name\n * the claim key through the one builder that renders it.\n */\nexport type ContendedEdgeRow = Readonly<{\n edgeKind: string;\n cardinality: Exclude<Cardinality, \"many\">;\n edgeId: string;\n fromKind: string;\n fromId: string;\n toKind: string;\n toId: string;\n}>;\n\n/** One node id live under BOTH kinds of a declared disjoint pair. */\nexport type DisjointOverlapRow = Readonly<{\n kinds: readonly [string, string];\n nodeId: string;\n}>;\n\n/** Everything one constraint-fence audit read, per family. */\nexport type ConstraintFenceViolationRows = Readonly<{\n contendedUniqueRows: readonly ContendedUniqueRow[];\n contendedEdgeRows: readonly ContendedEdgeRow[];\n disjointOverlaps: readonly DisjointOverlapRow[];\n}>;\n\n/**\n * Parameters for checking a unique constraint.\n */\nexport type CheckUniqueParams = Readonly<{\n graphId: string;\n nodeKind: string;\n constraintName: string;\n key: string;\n /** If true, also returns soft-deleted entries. Used by get-or-create operations. */\n includeDeleted?: boolean;\n}>;\n\n/**\n * Parameters for batch-checking unique constraints.\n */\nexport type CheckUniqueBatchParams = Readonly<{\n graphId: string;\n nodeKind: string;\n constraintName: string;\n keys: readonly string[];\n /** If true, also returns soft-deleted entries. Used by get-or-create operations. */\n includeDeleted?: boolean;\n}>;\n\n/**\n * Parameters for inserting a schema version.\n *\n * Used internally by backend implementations. Public callers go through\n * `commitSchemaVersion`, which handles the insert + activate atomically\n * with CAS guarantees.\n */\nexport type InsertSchemaParams = Readonly<{\n graphId: string;\n version: number;\n schemaHash: string;\n schemaDoc: SerializedSchema;\n isActive: boolean;\n}>;\n\n/**\n * The caller's claim about the currently-active schema version, used as\n * the optimistic compare-and-swap guard for `commitSchemaVersion`.\n *\n * - `{ kind: \"initial\" }` — caller is committing the first-ever version\n * for this graph and asserts no active version exists yet.\n * - `{ kind: \"active\", version: N }` — caller observed version N as\n * active and is committing version N+1 against that baseline. The\n * commit fails with `StaleVersionError` if some other writer has\n * advanced or rolled back the pointer in the meantime.\n *\n * Tagged-union form (rather than a magic `version: 0` sentinel) because\n * the two cases have materially different semantics: the initial path\n * skips the \"deactivate prior\" UPDATE, and the no-active-row state is\n * a *valid* expected state, not an out-of-band signal.\n */\nexport type CommitSchemaVersionExpected =\n Readonly<{ kind: \"initial\" }> | Readonly<{ kind: \"active\"; version: number }>;\n\n/**\n * Parameters for `commitSchemaVersion`.\n */\nexport type CommitSchemaVersionParams = Readonly<{\n graphId: string;\n /** CAS guard — see `CommitSchemaVersionExpected`. */\n expected: CommitSchemaVersionExpected;\n /** The new version to insert and activate. */\n version: number;\n schemaHash: string;\n schemaDoc: SerializedSchema;\n}>;\n\nexport type SchemaKindEmptinessProbe = Readonly<{\n entity: \"node\" | \"edge\";\n kind: string;\n}>;\n\nexport type PopulatedSchemaKind = SchemaKindEmptinessProbe &\n Readonly<{ count: number }>;\n\nexport type CommitSchemaVersionIfKindsEmptyResult =\n | Readonly<{ status: \"committed\"; row: SchemaVersionRow }>\n | Readonly<{\n status: \"populated\";\n kinds: readonly PopulatedSchemaKind[];\n }>;\n\nexport type LockSchemaVersionForWriteParams = Readonly<{\n graphId: string;\n expectedVersion: number;\n}>;\n\n/**\n * Optional schema-managed write identity for a trusted import. Supplying it\n * acquires and validates the Store version's transaction-scoped schema fence;\n * omitting it leaves the import outside the versioned guarantee.\n */\nexport type TrustedImportOptions = Readonly<{\n schemaWrite?: Readonly<{ graphId: string; expectedVersion: number }>;\n}>;\n\n/**\n * Parameters for `setActiveVersion`.\n *\n * Flips the active pointer from `expected` to `version` for an existing\n * row. CAS prevents overwriting a concurrent rollback or commit.\n */\nexport type SetActiveVersionParams = Readonly<{\n graphId: string;\n /** CAS guard. Same semantics as `commitSchemaVersion.expected`. */\n expected: CommitSchemaVersionExpected;\n /** The version to mark active. Must already exist. */\n version: number;\n}>;\n\n/**\n * Parameters for counting edges from a source node.\n */\nexport type CountEdgesFromParams = Readonly<{\n graphId: string;\n edgeKind: string;\n fromKind: string;\n fromId: string;\n /** If true, only count edges where valid_to IS NULL */\n activeOnly?: boolean;\n}>;\n\n/**\n * Parameters for checking if an edge exists between two nodes.\n */\nexport type EdgeExistsBetweenParams = Readonly<{\n graphId: string;\n edgeKind: string;\n fromKind: string;\n fromId: string;\n toKind: string;\n toId: string;\n}>;\n\n/**\n * Parameters for finding edges connected to a node.\n */\nexport type FindEdgesConnectedToParams = Readonly<{\n graphId: string;\n nodeKind: string;\n nodeId: string;\n}>;\n\n/**\n * Parameters for finding nodes by kind.\n */\nexport type FindNodesByKindParams = Readonly<{\n graphId: string;\n kind: string;\n /** Max rows to return. */\n limit?: number;\n /** Offset. Present for backward compat; rebuild uses `after` instead. */\n offset?: number;\n /** If true, exclude deleted nodes. Default true. */\n excludeDeleted?: boolean;\n /** Temporal mode for filtering by validity period. */\n temporalMode?: TemporalMode;\n /** Timestamp for \"current\" and \"asOf\" temporal modes. */\n asOf?: string;\n /**\n * Stable ordering for keyset pagination. Default: \"created_at\" (existing\n * behavior). Rebuild should use \"id\" for iteration that is stable under\n * concurrent writes and shared timestamps.\n */\n orderBy?: \"id\" | \"created_at\";\n /**\n * Keyset cursor. Returns rows strictly greater (by `orderBy`) than this\n * value. When `orderBy: \"id\"`, compared lexicographically. Mutually\n * exclusive with `offset` — callers pick one.\n */\n after?: string;\n}>;\n\n/**\n * Parameters for counting nodes by kind.\n */\nexport type CountNodesByKindParams = Readonly<{\n graphId: string;\n kind: string;\n /** If true, exclude deleted nodes. Default true. */\n excludeDeleted?: boolean;\n /** Temporal mode for filtering by validity period. */\n temporalMode?: TemporalMode;\n /** Timestamp for \"current\" and \"asOf\" temporal modes. */\n asOf?: string;\n}>;\n\n/**\n * Parameters for finding edges by kind.\n */\nexport type FindEdgesByKindParams = Readonly<{\n graphId: string;\n kind: string;\n fromKind?: string;\n fromId?: string;\n toKind?: string;\n toId?: string;\n limit?: number;\n offset?: number;\n /** If true, exclude deleted edges. Default true. */\n excludeDeleted?: boolean;\n /** Temporal mode for filtering by validity period. */\n temporalMode?: TemporalMode;\n /** Timestamp for \"current\" and \"asOf\" temporal modes. */\n asOf?: string;\n /**\n * Stable ordering for keyset pagination. Default: \"created_at\" (existing\n * behavior). Use \"id\" for iteration that is stable under shared `created_at`\n * timestamps — the offset path orders by the NON-unique `created_at`, so a\n * full enumeration must page by the unique `id` to avoid skipping a row at a\n * page boundary. Mirrors {@link FindNodesByKindParams.orderBy}.\n */\n orderBy?: \"id\" | \"created_at\";\n /**\n * Keyset cursor. Returns rows strictly greater (by `orderBy`) than this value.\n * When `orderBy: \"id\"`, compared lexicographically. Mutually exclusive with\n * `offset` — callers pick one. Mirrors {@link FindNodesByKindParams.after}.\n */\n after?: string;\n}>;\n\n/**\n * The endpoint side a {@link FindEdgesByEndpointSetParams} read fans out over.\n */\nexport type EdgeEndpointSide = \"from\" | \"to\";\n\n/**\n * Parameters for reading the edges of a SET of endpoints in one statement per\n * bind-budget chunk — the widened form of {@link FindEdgesByKindParams}'s\n * scalar `fromId` / `toId`.\n *\n * Deliberately a SEPARATE parameter type on a separate operation rather than\n * optional fields on `FindEdgesByKindParams`. A backend that did not implement\n * set membership would still type-check while ignoring the id list, and would\n * then return every edge of the kind — which the caller would rebucket into a\n * correct-looking answer at unbounded cost. Splitting the operation makes that\n * failure unreachable: support is detected by the method's presence, before any\n * read is issued.\n *\n * The shape also makes the previously-validated illegal states\n * unrepresentable. One `side` instead of two independent id lists means both\n * endpoints can never fan out at once; no scalar `fromId` / `toId` field means\n * a scalar and a set can never disagree; no `limit` / `offset` / `after` means\n * a global slice can never be requested across a read the backend splits into\n * bind-budget chunks.\n */\nexport type FindEdgesByEndpointSetParams = Readonly<{\n graphId: string;\n kind: string;\n /** Which endpoint column the id set constrains. */\n side: EdgeEndpointSide;\n /**\n * Kind of the fanned-out endpoint. Required: it is the index prefix that\n * makes the set a seek rather than a scan, so a set is always scoped to one\n * endpoint kind and a heterogeneous page costs one read per distinct kind.\n */\n endpointKind: string;\n /**\n * Endpoint NODE ids to match — deliberately not named `ids`, which in the\n * backend params vocabulary means edge ids (see\n * {@link DeleteEdgesBatchParams}); the write-surface assertion in\n * `recorded-capture/write-surface.ts` classifies params structurally and a\n * `{ graphId, ids }` read would be misread as a write.\n *\n * The backend deduplicates before splitting the list across bind-budget\n * chunks — a repeated id spanning two chunks would return its edges twice.\n * An empty list reads nothing and yields no rows.\n */\n endpointIds: readonly string[];\n /**\n * Maximum rows per distinct endpoint id, applied inside the statement (via\n * `ROW_NUMBER()` over the read's own ordering) rather than by the caller.\n * Only meaningful on a backend whose `capabilities.windowFunctions` is true;\n * callers must still cap client-side, so a backend that ignores this returns\n * a superset rather than a wrong answer. Unlike a global `limit`, a\n * per-endpoint cap composes with chunking: each endpoint's rows fall\n * entirely within one chunk.\n */\n limitPerEndpoint?: number;\n /** If true, exclude deleted edges. Default true. */\n excludeDeleted?: boolean;\n /** Temporal mode for filtering by validity period. */\n temporalMode?: TemporalMode;\n /** Timestamp for \"current\" and \"asOf\" temporal modes. */\n asOf?: string;\n}>;\n\n/**\n * Parameters for reading several edge kinds from several endpoint kinds.\n *\n * The endpoint pairs are joined as a relation, so round trips are independent\n * of the number of licensed edge-kind/endpoint-kind combinations. Large source\n * sets may still be split to respect the backend's bind-parameter budget.\n */\nexport type FindEdgesByHeterogeneousEndpointSetParams = Readonly<{\n graphId: string;\n side: EdgeEndpointSide;\n endpoints: readonly Readonly<{ kind: string; id: string }>[];\n edgeKinds: readonly string[];\n limitPerEndpoint?: number;\n /** If true, exclude deleted edges. Default true. */\n excludeDeleted?: boolean;\n /** Temporal mode for filtering by validity period. */\n temporalMode?: TemporalMode;\n /** Timestamp for \"current\" and \"asOf\" temporal modes. */\n asOf?: string;\n}>;\n\n/**\n * Parameters for counting edges by kind.\n */\nexport type CountEdgesByKindParams = Readonly<{\n graphId: string;\n kind: string;\n fromKind?: string;\n fromId?: string;\n toKind?: string;\n toId?: string;\n /** If true, exclude deleted edges. Default true. */\n excludeDeleted?: boolean;\n /** Temporal mode for filtering by validity period. */\n temporalMode?: TemporalMode;\n /** Timestamp for \"current\" and \"asOf\" temporal modes. */\n asOf?: string;\n}>;\n\n// ============================================================\n// Default Capabilities\n// ============================================================\n\n/**\n * Conservative per-statement bound-parameter floor for SQLite. The\n * compiled-in `SQLITE_MAX_VARIABLE_NUMBER` defaulted to 999 before SQLite\n * 3.32.0; drivers whose real ceiling cannot be probed (async/remote\n * connections) keep this floor. Single source of truth for\n * {@link SQLITE_CAPABILITIES} and the SQLite backend's batch math fallback.\n */\nexport const SQLITE_MAX_BIND_PARAMETERS = 999;\n\n/**\n * `SQLITE_MAX_VARIABLE_NUMBER` default since SQLite 3.32.0 (better-sqlite3\n * also compiles it in explicitly). Used when a synchronous driver's probe\n * confirms a modern build.\n */\nexport const MODERN_SQLITE_MAX_BIND_PARAMETERS = 32_766;\n\n/**\n * Cloudflare D1's documented per-statement bound-parameter ceiling. Far\n * below the classic SQLite floor, so D1 detection must cap the budget or\n * batched writes fail at runtime.\n */\nexport const D1_MAX_BIND_PARAMETERS = 100;\n\n/**\n * Cloudflare SQLite-backed Durable Objects' documented per-statement\n * bound-parameter ceiling. The resolved `do-sqlite` execution profile must\n * advertise this limit so every capability-driven batch path stays below it.\n */\nexport const DURABLE_OBJECT_MAX_BIND_PARAMETERS = 100;\n\n/**\n * Safe bound-parameter ceiling shared by every bundled PostgreSQL driver.\n * The protocol count can represent 65535, but postgres.js rejects a statement\n * with 65534 bound values, so backend batch math uses the lower portable limit.\n */\nexport const POSTGRES_MAX_BIND_PARAMETERS = 65_533;\n\n/**\n * Default capabilities for SQLite.\n */\nexport const SQLITE_CAPABILITIES: BackendCapabilities = Object.freeze({\n transactions: true, // SQLite supports transactions\n windowFunctions: true, // SQLite has supported window functions since 3.25.0\n clearValidTo: true,\n returning: true, // SQLite has supported RETURNING since 3.35.0\n // The bundled schema ships both claim relations and the shared operation\n // backend implements every claim member.\n constraintClaims: true,\n maxBindParameters: SQLITE_MAX_BIND_PARAMETERS,\n // Generic SQLite builds do not guarantee ENABLE_MATH_FUNCTIONS. The local\n // better-sqlite3 factory overrides this flag for its bundled build contract.\n graphAnalytics: Object.freeze({ supported: true, mathFunctions: false }),\n});\n\n/**\n * Default capabilities for PostgreSQL.\n */\nexport const POSTGRES_CAPABILITIES: BackendCapabilities = Object.freeze({\n transactions: true, // PostgreSQL supports transactions\n windowFunctions: true, // PostgreSQL supports ROW_NUMBER() and related windows\n clearValidTo: true,\n returning: true, // PostgreSQL has supported RETURNING since 8.2\n // The bundled schema ships both claim relations and the shared operation\n // backend implements every claim member.\n constraintClaims: true,\n maxBindParameters: POSTGRES_MAX_BIND_PARAMETERS,\n graphAnalytics: Object.freeze({ supported: true, mathFunctions: true }),\n});\n"]}