@nicia-ai/typegraph 0.44.0 → 0.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (265) hide show
  1. package/dist/backend/drizzle/indexes.cjs +7 -7
  2. package/dist/backend/drizzle/indexes.d.cts +1 -1
  3. package/dist/backend/drizzle/indexes.d.ts +1 -1
  4. package/dist/backend/drizzle/indexes.js +3 -3
  5. package/dist/backend/index.cjs +37 -37
  6. package/dist/backend/index.d.cts +3 -3
  7. package/dist/backend/index.d.ts +3 -3
  8. package/dist/backend/index.js +6 -6
  9. package/dist/backend/postgres/index.cjs +23 -23
  10. package/dist/backend/postgres/index.d.cts +6 -6
  11. package/dist/backend/postgres/index.d.ts +6 -6
  12. package/dist/backend/postgres/index.js +10 -10
  13. package/dist/backend/postgres/pglite-store.cjs +24 -25
  14. package/dist/backend/postgres/pglite-store.cjs.map +1 -1
  15. package/dist/backend/postgres/pglite-store.d.cts +7 -7
  16. package/dist/backend/postgres/pglite-store.d.ts +7 -7
  17. package/dist/backend/postgres/pglite-store.js +20 -21
  18. package/dist/backend/postgres/pglite-store.js.map +1 -1
  19. package/dist/backend/postgres/pglite.cjs +12 -12
  20. package/dist/backend/postgres/pglite.d.cts +3 -3
  21. package/dist/backend/postgres/pglite.d.ts +3 -3
  22. package/dist/backend/postgres/pglite.js +11 -11
  23. package/dist/backend/sqlite/index.cjs +21 -21
  24. package/dist/backend/sqlite/index.d.cts +6 -6
  25. package/dist/backend/sqlite/index.d.ts +6 -6
  26. package/dist/backend/sqlite/index.js +9 -9
  27. package/dist/backend/sqlite/libsql.cjs +14 -14
  28. package/dist/backend/sqlite/libsql.cjs.map +1 -1
  29. package/dist/backend/sqlite/libsql.d.cts +3 -3
  30. package/dist/backend/sqlite/libsql.d.ts +3 -3
  31. package/dist/backend/sqlite/libsql.js +10 -10
  32. package/dist/backend/sqlite/libsql.js.map +1 -1
  33. package/dist/backend/sqlite/local-store.cjs +25 -26
  34. package/dist/backend/sqlite/local-store.cjs.map +1 -1
  35. package/dist/backend/sqlite/local-store.d.cts +8 -8
  36. package/dist/backend/sqlite/local-store.d.ts +8 -8
  37. package/dist/backend/sqlite/local-store.js +21 -22
  38. package/dist/backend/sqlite/local-store.js.map +1 -1
  39. package/dist/backend/sqlite/local.cjs +13 -13
  40. package/dist/backend/sqlite/local.d.cts +3 -3
  41. package/dist/backend/sqlite/local.d.ts +3 -3
  42. package/dist/backend/sqlite/local.js +11 -11
  43. package/dist/{chunk-TVLKA4AY.js → chunk-42PYUGLS.js} +57 -3
  44. package/dist/chunk-42PYUGLS.js.map +1 -0
  45. package/dist/{chunk-IJTLXNGJ.js → chunk-4GUBTVPV.js} +3 -4
  46. package/dist/chunk-4GUBTVPV.js.map +1 -0
  47. package/dist/{chunk-63KCKSSH.js → chunk-4GUTQPVO.js} +33 -9
  48. package/dist/chunk-4GUTQPVO.js.map +1 -0
  49. package/dist/{chunk-FGO5JFVJ.cjs → chunk-4MEWSUVM.cjs} +10 -10
  50. package/dist/chunk-4MEWSUVM.cjs.map +1 -0
  51. package/dist/{chunk-FMGEZBAQ.js → chunk-4QIKLIGG.js} +15 -3
  52. package/dist/chunk-4QIKLIGG.js.map +1 -0
  53. package/dist/{chunk-FMMIX6N4.js → chunk-55ITBQJI.js} +2 -2
  54. package/dist/chunk-55ITBQJI.js.map +1 -0
  55. package/dist/{chunk-TCRIQMBU.js → chunk-5EKPX6QU.js} +4 -4
  56. package/dist/{chunk-TCRIQMBU.js.map → chunk-5EKPX6QU.js.map} +1 -1
  57. package/dist/{chunk-GFRGG2US.cjs → chunk-5GAONX5G.cjs} +7 -7
  58. package/dist/{chunk-GFRGG2US.cjs.map → chunk-5GAONX5G.cjs.map} +1 -1
  59. package/dist/{chunk-ALFUGRYB.cjs → chunk-6RYPD3NJ.cjs} +29 -29
  60. package/dist/{chunk-ALFUGRYB.cjs.map → chunk-6RYPD3NJ.cjs.map} +1 -1
  61. package/dist/{chunk-QGKILWGZ.cjs → chunk-7PF2L3H6.cjs} +4 -4
  62. package/dist/{chunk-QGKILWGZ.cjs.map → chunk-7PF2L3H6.cjs.map} +1 -1
  63. package/dist/chunk-B3F2D3SP.cjs +12 -0
  64. package/dist/{chunk-EPTSMFH7.cjs.map → chunk-B3F2D3SP.cjs.map} +1 -1
  65. package/dist/{chunk-7G32LIQM.js → chunk-BGYJW37L.js} +5 -5
  66. package/dist/{chunk-7G32LIQM.js.map → chunk-BGYJW37L.js.map} +1 -1
  67. package/dist/{chunk-EE7N5XXQ.cjs → chunk-BNS75YNF.cjs} +9 -10
  68. package/dist/chunk-BNS75YNF.cjs.map +1 -0
  69. package/dist/{chunk-VITTKBWF.js → chunk-CBKG6HEP.js} +5 -5
  70. package/dist/{chunk-VITTKBWF.js.map → chunk-CBKG6HEP.js.map} +1 -1
  71. package/dist/{chunk-A4PBRX4C.cjs → chunk-D54WH24V.cjs} +57 -2
  72. package/dist/chunk-D54WH24V.cjs.map +1 -0
  73. package/dist/{chunk-MCHP7RHZ.cjs → chunk-D56XG4EI.cjs} +26 -14
  74. package/dist/chunk-D56XG4EI.cjs.map +1 -0
  75. package/dist/{chunk-SQRD2CYF.js → chunk-DXEEHSGN.js} +293 -45
  76. package/dist/chunk-DXEEHSGN.js.map +1 -0
  77. package/dist/{chunk-GWEDD3DH.js → chunk-EFEOOCJ3.js} +4 -4
  78. package/dist/{chunk-GWEDD3DH.js.map → chunk-EFEOOCJ3.js.map} +1 -1
  79. package/dist/{chunk-3PRKZW6D.cjs → chunk-EKFUG4G7.cjs} +2 -2
  80. package/dist/chunk-EKFUG4G7.cjs.map +1 -0
  81. package/dist/{chunk-HJH3RMXG.cjs → chunk-F4NHQHEZ.cjs} +4 -4
  82. package/dist/{chunk-HJH3RMXG.cjs.map → chunk-F4NHQHEZ.cjs.map} +1 -1
  83. package/dist/{chunk-46GDII45.js → chunk-FL5UQRES.js} +3 -3
  84. package/dist/{chunk-46GDII45.js.map → chunk-FL5UQRES.js.map} +1 -1
  85. package/dist/{chunk-TFMVL4QH.js → chunk-FLEQ2HBK.js} +3 -3
  86. package/dist/{chunk-TFMVL4QH.js.map → chunk-FLEQ2HBK.js.map} +1 -1
  87. package/dist/{chunk-BEFM74L2.js → chunk-FQ7ZPOTZ.js} +6 -6
  88. package/dist/{chunk-BEFM74L2.js.map → chunk-FQ7ZPOTZ.js.map} +1 -1
  89. package/dist/{chunk-ST3LPB2V.cjs → chunk-H7VE3FL4.cjs} +318 -70
  90. package/dist/chunk-H7VE3FL4.cjs.map +1 -0
  91. package/dist/{chunk-BCP7LLOY.js → chunk-ID4WXTKL.js} +367 -41
  92. package/dist/{chunk-BCP7LLOY.js.map → chunk-ID4WXTKL.js.map} +1 -1
  93. package/dist/{chunk-EFELJVLA.js → chunk-ISLPNELU.js} +15 -3
  94. package/dist/chunk-ISLPNELU.js.map +1 -0
  95. package/dist/{chunk-A2I2XO52.cjs → chunk-IX64T5OL.cjs} +4 -4
  96. package/dist/{chunk-A2I2XO52.cjs.map → chunk-IX64T5OL.cjs.map} +1 -1
  97. package/dist/{chunk-QYWINYEA.cjs → chunk-J2ICGNHO.cjs} +5 -6
  98. package/dist/chunk-J2ICGNHO.cjs.map +1 -0
  99. package/dist/{chunk-7V2FGGI2.cjs → chunk-J7LXTUTP.cjs} +54 -12
  100. package/dist/chunk-J7LXTUTP.cjs.map +1 -0
  101. package/dist/{chunk-WBUFJ7BS.cjs → chunk-JQXSZHF2.cjs} +12 -2
  102. package/dist/chunk-JQXSZHF2.cjs.map +1 -0
  103. package/dist/{chunk-OVUUTCPJ.cjs → chunk-JXUTCSXX.cjs} +28 -2
  104. package/dist/chunk-JXUTCSXX.cjs.map +1 -0
  105. package/dist/{chunk-66UVLHQA.cjs → chunk-KMXWZLQX.cjs} +14 -14
  106. package/dist/{chunk-66UVLHQA.cjs.map → chunk-KMXWZLQX.cjs.map} +1 -1
  107. package/dist/{chunk-STQ3BSED.cjs → chunk-NAAMUYMS.cjs} +23 -11
  108. package/dist/chunk-NAAMUYMS.cjs.map +1 -0
  109. package/dist/{chunk-NN3QSOVS.cjs → chunk-OOA273IJ.cjs} +14 -14
  110. package/dist/{chunk-NN3QSOVS.cjs.map → chunk-OOA273IJ.cjs.map} +1 -1
  111. package/dist/{chunk-OSZPLZIV.cjs → chunk-P7GTYOLB.cjs} +130 -106
  112. package/dist/chunk-P7GTYOLB.cjs.map +1 -0
  113. package/dist/{chunk-K3BUWSFR.js → chunk-PVUI6KGR.js} +28 -2
  114. package/dist/chunk-PVUI6KGR.js.map +1 -0
  115. package/dist/{chunk-SBPC5IP3.js → chunk-Q6EDCUXV.js} +242 -10
  116. package/dist/chunk-Q6EDCUXV.js.map +1 -0
  117. package/dist/{chunk-2BIG7U6E.cjs → chunk-QC4MCOFV.cjs} +5 -5
  118. package/dist/{chunk-2BIG7U6E.cjs.map → chunk-QC4MCOFV.cjs.map} +1 -1
  119. package/dist/{chunk-JRP3VGHS.js → chunk-QLUXRWKL.js} +3 -3
  120. package/dist/{chunk-JRP3VGHS.js.map → chunk-QLUXRWKL.js.map} +1 -1
  121. package/dist/{chunk-IIT5K7SK.cjs → chunk-QR24DJDF.cjs} +6 -6
  122. package/dist/{chunk-IIT5K7SK.cjs.map → chunk-QR24DJDF.cjs.map} +1 -1
  123. package/dist/{chunk-HOIFFY4M.js → chunk-QRNYDV5Z.js} +48 -21
  124. package/dist/chunk-QRNYDV5Z.js.map +1 -0
  125. package/dist/{chunk-FRHHDHKH.js → chunk-RFRM3Q5K.js} +15 -3
  126. package/dist/chunk-RFRM3Q5K.js.map +1 -0
  127. package/dist/{chunk-ATLN63JA.js → chunk-RUQS6M7G.js} +5 -5
  128. package/dist/chunk-RUQS6M7G.js.map +1 -0
  129. package/dist/{chunk-XMYJY7NI.js → chunk-S3ZRKJHY.js} +3 -3
  130. package/dist/{chunk-XMYJY7NI.js.map → chunk-S3ZRKJHY.js.map} +1 -1
  131. package/dist/{chunk-FSXTPBRN.js → chunk-TKORQCG4.js} +49 -7
  132. package/dist/chunk-TKORQCG4.js.map +1 -0
  133. package/dist/{chunk-2Y4IFKW7.js → chunk-TKP5T34P.js} +3 -4
  134. package/dist/chunk-TKP5T34P.js.map +1 -0
  135. package/dist/{chunk-NCX65HBY.cjs → chunk-UCQ3BFSY.cjs} +301 -68
  136. package/dist/chunk-UCQ3BFSY.cjs.map +1 -0
  137. package/dist/{chunk-B75XRV3O.js → chunk-V34A6R5V.js} +12 -3
  138. package/dist/chunk-V34A6R5V.js.map +1 -0
  139. package/dist/{chunk-BNUFUFAU.js → chunk-VTN3C3CH.js} +6 -6
  140. package/dist/chunk-VTN3C3CH.js.map +1 -0
  141. package/dist/{chunk-5MUSU4ZP.cjs → chunk-VVQM4CZD.cjs} +12 -12
  142. package/dist/chunk-VVQM4CZD.cjs.map +1 -0
  143. package/dist/{chunk-IIMQJYAM.js → chunk-WDAVPX46.js} +3 -3
  144. package/dist/{chunk-IIMQJYAM.js.map → chunk-WDAVPX46.js.map} +1 -1
  145. package/dist/{chunk-YNULOIR5.cjs → chunk-WDBXJOU5.cjs} +28 -16
  146. package/dist/chunk-WDBXJOU5.cjs.map +1 -0
  147. package/dist/{chunk-PAW2XUZN.js → chunk-WUFASQUO.js} +3 -3
  148. package/dist/{chunk-PAW2XUZN.js.map → chunk-WUFASQUO.js.map} +1 -1
  149. package/dist/{chunk-PDGEXGS6.cjs → chunk-YDWLUT5V.cjs} +136 -109
  150. package/dist/chunk-YDWLUT5V.cjs.map +1 -0
  151. package/dist/{chunk-PHXITZE4.js → chunk-YMTXMPV6.js} +3 -3
  152. package/dist/{chunk-PHXITZE4.js.map → chunk-YMTXMPV6.js.map} +1 -1
  153. package/dist/{chunk-XR4YZFKW.cjs → chunk-YOALGKRE.cjs} +690 -364
  154. package/dist/chunk-YOALGKRE.cjs.map +1 -0
  155. package/dist/{chunk-6JZUSDY2.cjs → chunk-Z3WO6MND.cjs} +27 -27
  156. package/dist/{chunk-6JZUSDY2.cjs.map → chunk-Z3WO6MND.cjs.map} +1 -1
  157. package/dist/{chunk-2VLBGQBY.cjs → chunk-ZI6VQSZA.cjs} +37 -37
  158. package/dist/{chunk-2VLBGQBY.cjs.map → chunk-ZI6VQSZA.cjs.map} +1 -1
  159. package/dist/core/index.cjs +10 -11
  160. package/dist/core/index.d.cts +5 -5
  161. package/dist/core/index.d.ts +5 -5
  162. package/dist/core/index.js +3 -4
  163. package/dist/{ddl-eWg7hx6R.d.cts → ddl-BG4J805L.d.cts} +3 -3
  164. package/dist/{ddl-D5369kOY.d.ts → ddl-CgiS9QLj.d.ts} +3 -3
  165. package/dist/{define-graph-SvYXOl78.d.ts → define-graph-BGrJahvp.d.ts} +1 -1
  166. package/dist/{define-graph-DVF-oN1i.d.cts → define-graph-alREY2wj.d.cts} +1 -1
  167. package/dist/graph-extension/index.cjs +18 -19
  168. package/dist/graph-extension/index.d.cts +4 -4
  169. package/dist/graph-extension/index.d.ts +4 -4
  170. package/dist/graph-extension/index.js +3 -4
  171. package/dist/graph-merge/index.cjs +89 -90
  172. package/dist/graph-merge/index.cjs.map +1 -1
  173. package/dist/graph-merge/index.d.cts +9 -9
  174. package/dist/graph-merge/index.d.ts +9 -9
  175. package/dist/graph-merge/index.js +16 -17
  176. package/dist/graph-merge/index.js.map +1 -1
  177. package/dist/{index-By89Sezh.d.ts → index-BBxZCUdu.d.ts} +2 -2
  178. package/dist/{index-DcQ3Sxs-.d.cts → index-C3y_98iN.d.cts} +2 -2
  179. package/dist/index.cjs +124 -125
  180. package/dist/index.cjs.map +1 -1
  181. package/dist/index.d.cts +13 -13
  182. package/dist/index.d.ts +13 -13
  183. package/dist/index.js +23 -24
  184. package/dist/index.js.map +1 -1
  185. package/dist/indexes/index.cjs +11 -11
  186. package/dist/indexes/index.d.cts +4 -4
  187. package/dist/indexes/index.d.ts +4 -4
  188. package/dist/indexes/index.js +4 -4
  189. package/dist/interchange/index.cjs +27 -26
  190. package/dist/interchange/index.cjs.map +1 -1
  191. package/dist/interchange/index.d.cts +7 -7
  192. package/dist/interchange/index.d.ts +7 -7
  193. package/dist/interchange/index.js +8 -7
  194. package/dist/interchange/index.js.map +1 -1
  195. package/dist/{manager-Cn0NFIhN.d.cts → manager-KS7CGQQT.d.cts} +3 -3
  196. package/dist/{manager-B0KjKE93.d.ts → manager-YoLEfcc1.d.ts} +3 -3
  197. package/dist/{materialize-indexes-BD4V3svD.d.ts → materialize-indexes-Bhhyb1B3.d.ts} +1 -1
  198. package/dist/{materialize-indexes-DVRhd5h4.d.cts → materialize-indexes-OHRWypW6.d.cts} +1 -1
  199. package/dist/{postgres-DaEWvN4x.d.cts → postgres-DpkW3R5Y.d.cts} +1 -1
  200. package/dist/{postgres-u0a0HN2D.d.ts → postgres-DwqyIniy.d.ts} +1 -1
  201. package/dist/profiler/index.d.cts +10 -10
  202. package/dist/profiler/index.d.ts +10 -10
  203. package/dist/provenance/index.cjs +15 -15
  204. package/dist/provenance/index.cjs.map +1 -1
  205. package/dist/provenance/index.d.cts +7 -7
  206. package/dist/provenance/index.d.ts +7 -7
  207. package/dist/provenance/index.js +3 -3
  208. package/dist/provenance/index.js.map +1 -1
  209. package/dist/schema/index.cjs +30 -31
  210. package/dist/schema/index.d.cts +7 -7
  211. package/dist/schema/index.d.ts +7 -7
  212. package/dist/schema/index.js +8 -9
  213. package/dist/{searchable-BT7j9_IK.d.cts → searchable-CIZL8Hyw.d.cts} +2 -2
  214. package/dist/{searchable-CnYEancP.d.ts → searchable-DLWEfC4a.d.ts} +2 -2
  215. package/dist/{sqlite-BrMoSsI7.d.cts → sqlite-DY0WODUK.d.cts} +1 -1
  216. package/dist/{sqlite-CHhZUyO5.d.ts → sqlite-L-VIRzqA.d.ts} +1 -1
  217. package/dist/{store-view-Bvdku0Ht.d.ts → store-view-66p92IPi.d.ts} +1887 -1811
  218. package/dist/{store-view-w-md0nQ4.d.cts → store-view-A85FFRnk.d.cts} +1887 -1811
  219. package/dist/{temporal-BCORwUau.d.cts → temporal-BNbjXqer.d.cts} +1 -1
  220. package/dist/{temporal--x9T8K4S.d.ts → temporal-Bob6Nfbp.d.ts} +1 -1
  221. package/dist/{types-Cjvkj0jA.d.ts → types-Ch9a0Dx6.d.cts} +141 -17
  222. package/dist/{types-Cjvkj0jA.d.cts → types-Ch9a0Dx6.d.ts} +141 -17
  223. package/dist/{types-COODtTJU.d.ts → types-ClCzT-dT.d.ts} +1 -1
  224. package/dist/{types-6-WJmSD4.d.cts → types-DeORZhxn.d.cts} +1 -1
  225. package/dist/{where-CUQmCR6d.d.cts → where-DPw52ksb.d.cts} +1 -1
  226. package/dist/{where-5xVawAwL.d.ts → where-kWUZPvEA.d.ts} +1 -1
  227. package/package.json +1 -1
  228. package/dist/chunk-2WDD2NO3.js +0 -59
  229. package/dist/chunk-2WDD2NO3.js.map +0 -1
  230. package/dist/chunk-2Y4IFKW7.js.map +0 -1
  231. package/dist/chunk-3PRKZW6D.cjs.map +0 -1
  232. package/dist/chunk-5MUSU4ZP.cjs.map +0 -1
  233. package/dist/chunk-63KCKSSH.js.map +0 -1
  234. package/dist/chunk-7V2FGGI2.cjs.map +0 -1
  235. package/dist/chunk-A4PBRX4C.cjs.map +0 -1
  236. package/dist/chunk-ATLN63JA.js.map +0 -1
  237. package/dist/chunk-B75XRV3O.js.map +0 -1
  238. package/dist/chunk-BNUFUFAU.js.map +0 -1
  239. package/dist/chunk-EE7N5XXQ.cjs.map +0 -1
  240. package/dist/chunk-EFELJVLA.js.map +0 -1
  241. package/dist/chunk-EPTSMFH7.cjs +0 -12
  242. package/dist/chunk-FGO5JFVJ.cjs.map +0 -1
  243. package/dist/chunk-FMGEZBAQ.js.map +0 -1
  244. package/dist/chunk-FMMIX6N4.js.map +0 -1
  245. package/dist/chunk-FRHHDHKH.js.map +0 -1
  246. package/dist/chunk-FSXTPBRN.js.map +0 -1
  247. package/dist/chunk-HOHAJ2NR.cjs +0 -61
  248. package/dist/chunk-HOHAJ2NR.cjs.map +0 -1
  249. package/dist/chunk-HOIFFY4M.js.map +0 -1
  250. package/dist/chunk-IJTLXNGJ.js.map +0 -1
  251. package/dist/chunk-K3BUWSFR.js.map +0 -1
  252. package/dist/chunk-MCHP7RHZ.cjs.map +0 -1
  253. package/dist/chunk-NCX65HBY.cjs.map +0 -1
  254. package/dist/chunk-OSZPLZIV.cjs.map +0 -1
  255. package/dist/chunk-OVUUTCPJ.cjs.map +0 -1
  256. package/dist/chunk-PDGEXGS6.cjs.map +0 -1
  257. package/dist/chunk-QYWINYEA.cjs.map +0 -1
  258. package/dist/chunk-SBPC5IP3.js.map +0 -1
  259. package/dist/chunk-SQRD2CYF.js.map +0 -1
  260. package/dist/chunk-ST3LPB2V.cjs.map +0 -1
  261. package/dist/chunk-STQ3BSED.cjs.map +0 -1
  262. package/dist/chunk-TVLKA4AY.js.map +0 -1
  263. package/dist/chunk-WBUFJ7BS.cjs.map +0 -1
  264. package/dist/chunk-XR4YZFKW.cjs.map +0 -1
  265. package/dist/chunk-YNULOIR5.cjs.map +0 -1
@@ -1,10 +1,10 @@
1
- import { G as GraphDef, E as EdgeKinds, N as NodeKinds, a as AllNodeTypes } from './define-graph-DVF-oN1i.cjs';
2
- import { R as RecordedInstant, d as ReadCoordinate } from './temporal-BCORwUau.cjs';
3
- import { c5 as ValueType, c6 as PredicateExpression, az as FulltextQueryMode, c7 as VectorMetricType, Q as QueryAst, J as JsonPointer, a2 as FieldRef, a$ as ParameterRef, b as NodeType, c as AnyEdgeType, a0 as EdgeType, bx as TraversalExpansion, x as GraphBackend, S as SqlDialect, bo as SqlSchema, c8 as Traversal, c9 as NodePredicate, ca as ProjectedField, a_ as OrderSpec, cb as AggregateOrderSpec, T as TemporalMode, cc as GroupBySpec, aH as HybridFusionOptions, aL as JsonPointerInput, cd as RecursiveCyclePolicy, a5 as TraversalDirection$1, E as EdgeRegistration, aY as NodeId, bu as TransactionBackend, K as KindEntity, at as ExternalRecordedReadSource, ce as NodeRow, ap as EdgeId, cf as EdgeRow, N as NodeRegistration, bw as TransactionReadBackend, a1 as AggregateExpr, bh as SortDirection, ai as CompiledSelectSql, F as FulltextStrategy, V as VectorStrategy, cg as VectorSlotMap, ch as RecordedReadBinding, ci as SelectiveField, cj as ComposableQuery, ck as SetOperationType, cl as SetOperation, cm as VectorMetric, aQ as KindAnnotations, Y as JsonSchema, bC as UniquenessScope, ad as Collation, ac as Cardinality, as as EndpointExistence, G as GraphExtension, B as BackendCapabilities, C as ContributionDiagnostic, bv as TransactionOptions, A as AdapterBackend } from './types-Cjvkj0jA.cjs';
4
- import { K as KindRegistry, S as SchemaManagerOptions, b as SchemaValidationResult } from './manager-Cn0NFIhN.cjs';
1
+ import { G as GraphDef, E as EdgeKinds, N as NodeKinds, a as AllNodeTypes } from './define-graph-alREY2wj.cjs';
2
+ import { R as RecordedInstant, d as ReadCoordinate } from './temporal-BNbjXqer.cjs';
3
+ import { cb as ValueType, cc as PredicateExpression, aC as FulltextQueryMode, cd as VectorMetricType, Q as QueryAst, J as JsonPointer, a4 as FieldRef, b3 as ParameterRef, d as NodeType, e as AnyEdgeType, a2 as EdgeType, bB as TraversalExpansion, z as GraphBackend, S as SqlDialect, bs as SqlSchema, ce as Traversal, cf as NodePredicate, cg as ProjectedField, b2 as OrderSpec, ch as AggregateOrderSpec, T as TemporalMode, ci as GroupBySpec, aL as HybridFusionOptions, aP as JsonPointerInput, cj as RecursiveCyclePolicy, a7 as TraversalDirection$1, E as EdgeRegistration, b0 as NodeId, by as TransactionBackend, a3 as AggregateExpr, bl as SortDirection, ak as CompiledSelectSql, F as FulltextStrategy, V as VectorStrategy, ck as VectorSlotMap, cl as RecordedReadBinding, cm as SelectiveField, cn as ComposableQuery, co as SetOperationType, cp as SetOperation, K as KindEntity, av as ExternalRecordedReadSource, cq as NodeRow, ar as EdgeId, cr as EdgeRow, N as NodeRegistration, bA as TransactionReadBackend, cs as VectorMetric, aU as KindAnnotations, _ as JsonSchema, bG as UniquenessScope, af as Collation, ae as Cardinality, au as EndpointExistence, G as GraphExtension, B as BackendCapabilities, C as ContributionDiagnostic, c as ContributionRepairResult, bz as TransactionOptions, A as AdapterBackend } from './types-Ch9a0Dx6.cjs';
4
+ import { K as KindRegistry, S as SchemaManagerOptions, b as SchemaValidationResult } from './manager-KS7CGQQT.cjs';
5
5
  import { z } from 'zod';
6
- import { S as SearchableMetadata, c as EmbeddingValue } from './searchable-BT7j9_IK.cjs';
7
- import { S as SchemaDiff, h as MaterializeIndexesOptions, M as MaterializeIndexesResult, j as MaterializeSystemIndexesOptions } from './materialize-indexes-DVRhd5h4.cjs';
6
+ import { S as SearchableMetadata, c as EmbeddingValue } from './searchable-CIZL8Hyw.cjs';
7
+ import { S as SchemaDiff, h as MaterializeIndexesOptions, M as MaterializeIndexesResult, j as MaterializeSystemIndexesOptions } from './materialize-indexes-OHRWypW6.cjs';
8
8
 
9
9
  type FieldTypeInfo = Readonly<{
10
10
  valueType: ValueType;
@@ -765,2162 +765,2234 @@ type CreateQueryBuilderOptions = Readonly<{
765
765
  }>;
766
766
 
767
767
  /**
768
- * StoreView collection surface buckets.
769
- *
770
- * The live NodeCollection / EdgeCollection partition into buckets a StoreView
771
- * treats differently:
772
- *
773
- * - temporal reads honor the pinned coordinate;
774
- * - current reads have no temporal axis and are refused on temporal pins;
775
- * - writes are never available on a read-only view; and
776
- * - batch reads require store.batch() and are absent from a view.
777
- *
778
- * These arrays are the single runtime source of truth for the proxy routing and
779
- * the type-level source for the derived StoreView collection types. The
780
- * StoreView surface-classification test asserts they exactly partition the live
781
- * collection methods so new collection methods cannot be silently omitted.
782
- */
783
- /** Temporal-aware node read method names. */
784
- declare const NODE_TEMPORAL_READ_NAMES: readonly ["getById", "getByIds", "find", "count"];
785
- /** Current-state-only node reads (constraint / index lookups). */
786
- declare const CURRENT_ONLY_READ_NAMES: readonly ["findByConstraint", "bulkFindByConstraint", "bulkFindByIndex"];
787
- /** Node write method names: never available on a read-only StoreView. */
788
- declare const NODE_WRITE_NAMES: readonly ["create", "createFromRecord", "update", "delete", "hardDelete", "upsertById", "upsertByIdFromRecord", "bulkCreate", "bulkUpsertById", "bulkInsert", "bulkDelete", "getOrCreateByConstraint", "bulkGetOrCreateByConstraint"];
789
- /** Temporal-aware edge read method names. */
790
- declare const EDGE_TEMPORAL_READ_NAMES: readonly ["getById", "getByIds", "find", "count", "findFrom", "findTo", "bulkFindFrom", "bulkFindTo", "findByEndpoints"];
791
- /** Deferred edge batch-read method names. */
792
- declare const EDGE_BATCH_READ_NAMES: readonly ["batchFindFrom", "batchFindTo", "batchFindByEndpoints"];
793
- /** Edge write method names: never available on a read-only StoreView. */
794
- declare const EDGE_WRITE_NAMES: readonly ["create", "update", "delete", "hardDelete", "bulkCreate", "bulkUpsertById", "bulkInsert", "bulkDelete", "getOrCreateByEndpoints", "bulkGetOrCreateByEndpoints"];
795
- /** Recorded-time collection point reads that reconstruct safely by id. */
796
- declare const RECORDED_POINT_READ_NAMES: readonly ["getById", "getByIds"];
797
-
798
- /**
799
- * Store types for TypeGraph operations.
768
+ * ExecutableAggregateQuery - A query with aggregate functions that can be executed.
800
769
  */
801
770
 
802
771
  /**
803
- * Canonical mapping from snake_case row fields to camelCase meta fields.
804
- *
805
- * This is the single source of truth for which row columns become metadata.
806
- * Both the Meta types and the row-to-meta functions in row-mappers.ts must
807
- * stay in sync with this mapping. If you add a temporal/audit column to a
808
- * row type, add the mapping here — the compiler will then force you to
809
- * update the row mapper functions as well (since their return type is
810
- * NodeMeta/EdgeMeta, which is derived from this mapping).
772
+ * Result type for aggregate queries.
773
+ * Maps field refs to their value types and aggregates to numbers.
811
774
  */
812
- type TemporalMetaFieldMap = Readonly<{
813
- valid_from: "validFrom";
814
- valid_to: "validTo";
815
- created_at: "createdAt";
816
- updated_at: "updatedAt";
817
- deleted_at: "deletedAt";
818
- }>;
775
+ type AggregateResult<R extends Record<string, FieldRef | AggregateExpr>> = {
776
+ [K in keyof R]: R[K] extends AggregateExpr ? number : R[K] extends FieldRef ? unknown : never;
777
+ };
819
778
  /**
820
- * Maps row fields to their camelCase meta counterparts, preserving types.
779
+ * An aggregate query that can be executed.
821
780
  */
822
- type MapRowToMeta<R extends Readonly<Record<string, unknown>>, M extends Readonly<Record<string, string>>> = Readonly<{
823
- [SnakeKey in keyof M as M[SnakeKey] & string]: SnakeKey extends keyof R ? R[SnakeKey] : never;
824
- }>;
781
+ declare class ExecutableAggregateQuery<G extends GraphDef, Aliases extends AliasMap, R extends Record<string, FieldRef | AggregateExpr>> {
782
+ #private;
783
+ constructor(config: QueryBuilderConfig, state: QueryBuilderState, fields: R);
784
+ /**
785
+ * Builds the query AST.
786
+ */
787
+ toAst(): QueryAst;
788
+ /**
789
+ * Orders results by a grouped field or aggregate alias.
790
+ *
791
+ * `key` is one of the output names passed to `.aggregate({...})` — either
792
+ * a grouped field (e.g. `genre`) or an aggregate alias (e.g. `bookCount`).
793
+ * Both are ordered the same way: by referencing the SELECT-list output
794
+ * column, since every `.aggregate()` field is projected with an alias.
795
+ *
796
+ * Chain multiple calls to sort by more than one key, in call order:
797
+ * `.orderBy("genre").orderBy("bookCount", "desc")` sorts by genre first,
798
+ * then by book count within each genre.
799
+ *
800
+ * @example
801
+ * ```typescript
802
+ * // Top 2 authors by book count
803
+ * store.query()
804
+ * .from("Author", "a")
805
+ * .traverse("wrote", "e")
806
+ * .to("Book", "b")
807
+ * .groupByNode("a")
808
+ * .aggregate({ author: field("a", "name"), bookCount: count("b") })
809
+ * .orderBy("bookCount", "desc")
810
+ * .limit(2)
811
+ * .execute();
812
+ * ```
813
+ */
814
+ orderBy<K extends keyof R & string>(key: K, direction?: SortDirection): ExecutableAggregateQuery<G, Aliases, R>;
815
+ /**
816
+ * Limits the number of results.
817
+ */
818
+ limit(n: number): ExecutableAggregateQuery<G, Aliases, R>;
819
+ /**
820
+ * Offsets the results.
821
+ */
822
+ offset(n: number): ExecutableAggregateQuery<G, Aliases, R>;
823
+ /**
824
+ * Compiles the query and returns the SQL text and parameters.
825
+ *
826
+ * Requires a backend to be configured (the backend determines the SQL dialect).
827
+ * Use this for debugging, logging, or running the query with a custom executor.
828
+ */
829
+ toSQL(): Readonly<{
830
+ sql: string;
831
+ params: readonly unknown[];
832
+ }>;
833
+ /**
834
+ * Compiles the query to TypeGraph's database-independent SQL fragment.
835
+ */
836
+ compile(): CompiledSelectSql;
837
+ /**
838
+ * Executes the query and returns typed results.
839
+ *
840
+ * @throws Error if no backend is configured
841
+ */
842
+ execute(): Promise<readonly AggregateResult<R>[]>;
843
+ }
844
+
825
845
  /**
826
- * Metadata for a node instance.
827
- * Derived from NodeRow via TemporalMetaFieldMap + version.
846
+ * How a "current" valid-time read instant is emitted into compiled SQL:
828
847
  *
829
- * Adding a new metadata column requires:
830
- * 1. Add the column to NodeRow in backend/types.ts
831
- * 2. Add the mapping to TemporalMetaFieldMap above
832
- * 3. The compiler will error in rowToNodeMeta() until you add the field there
848
+ * - `"literal"` — bound as a concrete value at compile time
849
+ * ({@link currentReadInstant}). The instant is frozen into the compiled
850
+ * statement, so the statement must be recompiled to observe a newer "now".
851
+ * - `"placeholder"` — bound as a reserved execution-time placeholder
852
+ * ({@link currentReadInstantPlaceholder}). The statement can be compiled
853
+ * once and reused across executions, filling a fresh instant each call.
854
+ *
855
+ * Defaults to `"literal"` everywhere except the query builder's compiled
856
+ * template cache, which opts into `"placeholder"` so a reused/prepared query
857
+ * never freezes "now" the way a cached literal would (the #246 regression).
833
858
  */
834
- type NodeMeta = MapRowToMeta<NodeRow, TemporalMetaFieldMap> & Readonly<{
835
- version: NodeRow["version"];
836
- }>;
859
+ type ReadInstantMode = "literal" | "placeholder";
860
+
837
861
  /**
838
- * A node instance in the graph.
862
+ * Query Compiler Module
839
863
  *
840
- * Properties from the schema are spread at the top level for ergonomic access:
841
- * - `node.name` instead of `node.props.name`
842
- * - System metadata is under `node.meta.*`
864
+ * Main entry point for compiling query ASTs to SQL.
865
+ * Re-exports individual compiler modules and provides the main compile functions.
843
866
  */
844
- type Node<N extends NodeType = NodeType> = Readonly<{
845
- kind: N["kind"];
846
- id: NodeId<N>;
847
- meta: NodeMeta;
848
- }> & Readonly<z.infer<N["schema"]>>;
867
+
849
868
  /**
850
- * Input for creating a node.
869
+ * Options for query compilation.
851
870
  */
852
- type CreateNodeInput<N extends NodeType = NodeType> = Readonly<{
853
- kind: N["kind"];
854
- id?: string;
855
- props: z.infer<N["schema"]>;
856
- validFrom?: string;
857
- validTo?: string;
871
+ type CompileQueryOptions = Readonly<{
872
+ /** SQL dialect ("sqlite" or "postgres"). Defaults to "sqlite". */
873
+ dialect?: SqlDialect | undefined;
874
+ /** SQL schema configuration from createSqlSchema(...). Defaults to standard names. */
875
+ schema?: SqlSchema | undefined;
876
+ /**
877
+ * Fulltext strategy override. When set, overrides the dialect's
878
+ * default fulltext strategy for `$fulltext.matches()` compilation.
879
+ * Callers typically read this from `backend.fulltextStrategy` so a
880
+ * backend-declared strategy (e.g. ParadeDB) wins over the dialect
881
+ * default (tsvector).
882
+ */
883
+ fulltextStrategy?: FulltextStrategy | undefined;
884
+ /**
885
+ * Vector strategy override. When set, `field.similarTo(...)`
886
+ * predicates compile against the strategy's per-`(kind, field)`
887
+ * tables and distance expression. Callers read it from
888
+ * `backend.vectorStrategy` so a backend-declared strategy (pgvector,
889
+ * libsql-native, sqlite-vec) drives the per-field relevance scan.
890
+ */
891
+ vectorStrategy?: VectorStrategy | undefined;
892
+ /**
893
+ * Whether the active backend supports SQL window functions such as
894
+ * `ROW_NUMBER()`. Defaults to true for direct compiler callers.
895
+ */
896
+ windowFunctions?: boolean | undefined;
897
+ /**
898
+ * Declared embedding slots `(kind, fieldPath) -> descriptor` used by
899
+ * the `field.similarTo(...)` CTE to know which kinds in an alias
900
+ * declare the field. Callers build it from the graph's node schemas.
901
+ */
902
+ vectorSlots?: VectorSlotMap | undefined;
903
+ /** Per-kind declared fulltext language (see buildFulltextLanguages). */
904
+ fulltextLanguages?: ReadonlyMap<string, string> | undefined;
905
+ /**
906
+ * Recorded read relation to use when the AST carries `recordedAsOf`.
907
+ * Supplying a recorded timestamp without this binding is rejected so the
908
+ * compiler never silently swaps to TypeGraph's built-in history tables when
909
+ * the store was only configured for live/valid-time reads.
910
+ */
911
+ recordedReadBinding?: RecordedReadBinding | undefined;
912
+ /**
913
+ * How the "current" valid-time read instant is emitted. Defaults to
914
+ * `"literal"` (a value frozen at compile time). The query builder's
915
+ * compiled template cache passes `"placeholder"` so the instant is a
916
+ * reserved execution-time slot, letting one compiled statement be reused
917
+ * across executions with a fresh "now" each call. See {@link ReadInstantMode}.
918
+ */
919
+ readInstant?: ReadInstantMode | undefined;
858
920
  }>;
921
+
859
922
  /**
860
- * Input for updating a node.
923
+ * PreparedQuery — a pre-validated, parameterized query.
924
+ *
925
+ * Created via `ExecutableQuery.prepare()`. Builds and structurally validates
926
+ * the query AST once at prepare time (so a malformed query fails fast, before
927
+ * the first `execute()`).
928
+ *
929
+ * Fast path: when the backend can compile and run raw SQL (`compileSql` +
930
+ * `executeRaw`) AND the statement is raw-executable, it is compiled ONCE
931
+ * into a cached template whose
932
+ * "current" read instant and user `param()` refs are reserved placeholders
933
+ * (see {@link buildReadInstantTemplate}). Every `execute()` fills those
934
+ * placeholders — a fresh instant plus the call's bindings — and runs the
935
+ * cached SQL text directly, so a reused prepared query never recompiles and
936
+ * never freezes "now" the way a cached literal instant would (the #246
937
+ * regression).
938
+ *
939
+ * Fallback: substitutes parameter refs into the AST, compiles fresh per call,
940
+ * and executes via the standard `backend.execute` path. Taken in two cases —
941
+ * a backend without raw execution (custom/async), and a statement that is not
942
+ * raw-executable because its execution semantics ride on the compiled SQL
943
+ * OBJECT rather than its text (approximate vector search's iterative-scan
944
+ * wrapper, `subgraph()`'s force-custom-plan fetches). The second applies even
945
+ * on PostgreSQL with `executeRaw` available; `isRawExecutable` in
946
+ * `sql-intent.ts` is the predicate that decides it.
861
947
  */
862
- type UpdateNodeInput<N extends NodeType = NodeType> = Readonly<{
863
- kind: N["kind"];
864
- id: NodeId<N>;
865
- props: Partial<z.infer<N["schema"]>>;
866
- validTo?: string;
948
+
949
+ type PreparedQueryConfig<R> = Readonly<{
950
+ ast: QueryAst;
951
+ unoptimizedAst: QueryAst;
952
+ backend: GraphBackend;
953
+ dialect: SqlDialect;
954
+ graphId: string;
955
+ compileOptions: CompileQueryOptions;
956
+ state: QueryBuilderState;
957
+ selectiveFields: readonly SelectiveField[] | undefined;
958
+ selectFn: (context: SelectContext<AliasMap, EdgeAliasMap>) => R;
959
+ schemaIntrospector: SchemaIntrospector;
867
960
  }>;
868
961
  /**
869
- * Metadata for an edge instance.
870
- * Derived from EdgeRow via TemporalMetaFieldMap (edges have no version).
871
- */
872
- type EdgeMeta = MapRowToMeta<EdgeRow, TemporalMetaFieldMap>;
873
- /**
874
- * An edge instance in the graph.
962
+ * A pre-validated, parameterized query — see the module doc comment above for
963
+ * how a reused prepared query runs a cached SQL template with a fresh read
964
+ * instant filled per call.
875
965
  *
876
- * Properties from the schema are spread at the top level for ergonomic access:
877
- * - `edge.role` instead of `edge.props.role`
878
- * - System metadata is under `edge.meta.*`
879
- */
880
- type Edge<E extends AnyEdgeType = EdgeType, From extends NodeType = NodeType, To extends NodeType = NodeType> = Readonly<{
881
- id: EdgeId<E>;
882
- kind: E["kind"];
883
- fromKind: From["kind"];
884
- fromId: NodeId<From>;
885
- toKind: To["kind"];
886
- toId: NodeId<To>;
887
- meta: EdgeMeta;
888
- }> & Readonly<z.infer<E["schema"]>>;
889
- /**
890
- * Input for creating an edge.
891
- */
892
- type CreateEdgeInput<E extends AnyEdgeType = EdgeType> = Readonly<{
893
- kind: E["kind"];
894
- id?: string;
895
- fromKind: string;
896
- fromId: string;
897
- toKind: string;
898
- toId: string;
899
- props: z.infer<E["schema"]>;
900
- validFrom?: string;
901
- validTo?: string;
902
- }>;
903
- /**
904
- * Input for updating an edge.
905
- */
906
- type UpdateEdgeInput<E extends AnyEdgeType = EdgeType> = Readonly<{
907
- id: EdgeId<E>;
908
- props: Partial<z.infer<E["schema"]>>;
909
- validTo?: string;
910
- }>;
911
- /**
912
- * Options for node and edge queries.
966
+ * @example
967
+ * ```typescript
968
+ * const prepared = store.query()
969
+ * .from("Person", "p")
970
+ * .whereNode("p", (p) => p.name.eq(param("name")))
971
+ * .select((ctx) => ctx.p)
972
+ * .prepare();
973
+ *
974
+ * // Execute with different bindings
975
+ * const alice = await prepared.execute({ name: "Alice" });
976
+ * const bob = await prepared.execute({ name: "Bob" });
977
+ * ```
913
978
  */
914
- type NoRecordedCoordinate = Readonly<{
979
+ declare class PreparedQuery<R> {
980
+ #private;
981
+ constructor(config: PreparedQueryConfig<R>);
982
+ /** The set of parameter names required by this prepared query. */
983
+ get parameterNames(): ReadonlySet<string>;
915
984
  /**
916
- * Recorded/system-time coordinates are internal-only and supplied by
917
- * RecordedStoreView. A public options object carrying this key is a type
918
- * error even when the object is pre-bound before the call site.
985
+ * Executes the prepared query with the given parameter bindings.
986
+ *
987
+ * @param bindings - A record mapping parameter names to their values
988
+ * @returns The query results
919
989
  */
920
- recordedAsOf?: never;
921
- }>;
922
- type QueryOptions = NoRecordedCoordinate & Readonly<{
923
- /** Temporal mode for the query */
924
- temporalMode?: TemporalMode;
925
- /** Specific timestamp for asOf queries */
926
- asOf?: string;
927
- }>;
928
- /**
929
- * Context passed to observability hooks.
930
- */
931
- type HookContext = Readonly<{
932
- /** Unique ID for this operation */
933
- operationId: string;
934
- /** Graph ID */
935
- graphId: string;
936
- /** Timestamp when operation started */
937
- startedAt: Date;
938
- }>;
990
+ execute(bindings?: Readonly<Record<string, unknown>>): Promise<readonly R[]>;
991
+ }
992
+
939
993
  /**
940
- * Context for one SQL statement a query builder submits to the backend.
941
- *
942
- * A single logical query can emit more than one context, for example when a
943
- * selective projection falls back to a full-row fetch. Backend-internal setup
944
- * statements are not exposed as separate query-hook events.
994
+ * UnionableQuery - A query formed by combining multiple queries with set operations.
945
995
  */
946
- type QueryHookContext = HookContext & Readonly<{
947
- /** The SQL statement being executed */
948
- sql: string;
949
- /** Query parameters */
950
- params: readonly unknown[];
951
- }>;
996
+
997
+ interface ExecutableQueryLike<G extends GraphDef, R> {
998
+ toAst(): QueryAst;
999
+ }
952
1000
  /**
953
- * Operation hook context for CRUD operations.
1001
+ * Internal state for unionable query.
954
1002
  */
955
- type OperationHookContext = HookContext & Readonly<{
956
- /** Operation type */
957
- operation: "create" | "update" | "delete";
958
- /** Entity type */
959
- entity: KindEntity;
960
- /** Kind of node or edge */
961
- kind: string;
962
- /** Entity ID */
963
- id: string;
1003
+ type UnionableQueryState = Readonly<{
1004
+ left: ComposableQuery;
1005
+ operator: SetOperationType;
1006
+ right: ComposableQuery;
1007
+ limit?: number;
1008
+ offset?: number;
1009
+ startAlias?: string;
1010
+ traversals?: readonly Traversal[];
1011
+ selectFn?: (context: SelectContext<AliasMap, EdgeAliasMap>) => unknown;
964
1012
  }>;
965
- /**
966
- * Observability hooks for monitoring store operations.
967
- *
968
- * Note: Batch operations (`bulkCreate`, `bulkInsert`, `bulkUpsertById`) skip
969
- * per-item operation hooks for throughput. Query hooks still fire normally.
970
- *
971
- * @example
972
- * ```typescript
973
- * const hooks: StoreHooks = {
974
- * onQueryStart: (ctx) => {
975
- * console.log(`[${ctx.operationId}] Query: ${ctx.sql}`);
976
- * },
977
- * onQueryEnd: (ctx, result) => {
978
- * const duration = Date.now() - ctx.startedAt.getTime();
979
- * console.log(`[${ctx.operationId}] Completed in ${duration}ms`);
980
- * },
981
- * onError: (ctx, error) => {
982
- * console.error(`[${ctx.operationId}] Error:`, error);
983
- * },
984
- * };
985
- *
986
- * const store = createStore(graph, backend, { hooks });
987
- * ```
988
- */
989
- type StoreHooks = Readonly<{
990
- /** Called before each query-builder statement is submitted to the backend. */
991
- onQueryStart?: (ctx: QueryHookContext) => void;
992
- /** Called after each submitted query-builder statement succeeds. */
993
- onQueryEnd?: (ctx: QueryHookContext, result: Readonly<{
994
- rowCount: number;
995
- durationMs: number;
996
- }>) => void;
997
- /** Called before a CRUD operation starts */
998
- onOperationStart?: (ctx: OperationHookContext) => void;
1013
+ declare class UnionableQuery<G extends GraphDef, R> {
1014
+ #private;
1015
+ constructor(config: QueryBuilderConfig, state: UnionableQueryState);
999
1016
  /**
1000
- * Called after a CRUD operation completes AND is durably committed. For a
1001
- * top-level operation that is when its own transaction commits; for an
1002
- * operation inside `store.transaction`, emission is deferred until the
1003
- * enclosing transaction commits — if that commit fails, the operation is
1004
- * reported through `onError` instead. (Inside an adopted transaction —
1005
- * `withTransaction` / `withRecordedTransaction` — the commit belongs to
1006
- * the caller and cannot be observed, so this fires when the operation
1007
- * completes within the still-open transaction.)
1017
+ * Combines with another query using UNION.
1008
1018
  */
1009
- onOperationEnd?: (ctx: OperationHookContext, result: Readonly<{
1010
- durationMs: number;
1011
- }>) => void;
1019
+ union(other: ExecutableQueryLike<G, R>): UnionableQuery<G, R>;
1012
1020
  /**
1013
- * Called when an operation fails — including an operation that completed
1014
- * inside a `store.transaction` whose commit then failed and rolled it
1015
- * back.
1021
+ * Combines with another query using UNION ALL.
1016
1022
  */
1017
- onError?: (ctx: HookContext, error: Error) => void;
1018
- }>;
1019
- type BaseStoreOptions = Readonly<{
1020
- /** Observability hooks for monitoring */
1021
- hooks?: StoreHooks;
1023
+ unionAll(other: ExecutableQueryLike<G, R>): UnionableQuery<G, R>;
1022
1024
  /**
1023
- * Maintain a durable, graph-wide revision anchor for TypeGraph writes:
1024
- * a random per-graph origin plus a monotonic revision clock. `branch()` uses
1025
- * this anchor to validate that its base has not moved without
1026
- * re-fingerprinting every live row, and cannot confuse a coincident clock in
1027
- * a separately created store for its original base. `history: true` enables
1028
- * the same tracking automatically through its recorded-time commit clock.
1029
- *
1030
- * This is intentionally opt-in for live stores: each successful graph write
1031
- * advances the anchor inside its write transaction. On PostgreSQL, those
1032
- * transactions take a graph-scoped advisory lock until commit; writes to the
1033
- * same graph therefore serialize. Enable it for branchable graphs, but size
1034
- * high-throughput multi-writer workloads for that trade-off.
1025
+ * Combines with another query using INTERSECT.
1026
+ */
1027
+ intersect(other: ExecutableQueryLike<G, R>): UnionableQuery<G, R>;
1028
+ /**
1029
+ * Combines with another query using EXCEPT.
1030
+ */
1031
+ except(other: ExecutableQueryLike<G, R>): UnionableQuery<G, R>;
1032
+ /**
1033
+ * Limits the number of results from the combined query.
1034
+ */
1035
+ limit(n: number): UnionableQuery<G, R>;
1036
+ /**
1037
+ * Offsets the results from the combined query.
1038
+ */
1039
+ offset(n: number): UnionableQuery<G, R>;
1040
+ /**
1041
+ * Builds the set operation AST.
1042
+ */
1043
+ toAst(): SetOperation;
1044
+ /**
1045
+ * Compiles the query and returns the SQL text and parameters.
1035
1046
  *
1036
- * Writes performed directly through a backend — including raw graph-table
1037
- * writes through `tx.sql` — bypass the anchor and are outside the
1038
- * revision-tracking contract.
1047
+ * Requires a backend to be configured (the backend determines the SQL dialect).
1048
+ * Use this for debugging, logging, or running the query with a custom executor.
1039
1049
  */
1040
- revisionTracking?: boolean;
1050
+ toSQL(): Readonly<{
1051
+ sql: string;
1052
+ params: readonly unknown[];
1053
+ }>;
1041
1054
  /**
1042
- * Automatic planner-statistics refresh after large autocommit bulk
1043
- * writes (bulkCreate and bulkInsert on nodes and edges). Stale statistics after a
1044
- * bulk load are a whole class of planner cliffs: the planner keeps
1045
- * pre-load row estimates until ANALYZE runs. When a single
1046
- * autocommit bulk write reaches the threshold
1047
- * ({@link AUTO_REFRESH_STATISTICS_ROW_THRESHOLD} rows by default),
1048
- * the store runs `refreshStatistics()` after the write commits.
1049
- * Pass a number to change the threshold, or `false` to disable.
1050
- * Bulk writes inside a caller-provided transaction never
1051
- * auto-refresh (statistics cannot see uncommitted rows); refresh
1052
- * manually after commit. `importGraph` handles its own refresh.
1055
+ * Compiles the set operation to SQL.
1053
1056
  */
1054
- autoRefreshStatistics?: false | number;
1057
+ compile(): CompiledSelectSql;
1055
1058
  /**
1056
- * Skip the write for an `upsertById` (or `bulkUpsertById` item) whose
1057
- * validated props are value-identical to the existing live row. Default
1058
- * off.
1059
+ * Executes the combined query.
1060
+ */
1061
+ execute(): Promise<readonly R[]>;
1062
+ /**
1063
+ * Executes the combined query against a provided backend.
1059
1064
  *
1060
- * Enable this for at-least-once / replay materializers. An event log that
1061
- * re-delivers a byte-identical change would otherwise rewrite the row anyway:
1062
- * today an `upsertById` on an existing id calls `updateNode`
1063
- * unconditionally, allocating a fresh recorded instant and a new history row
1064
- * per re-delivery.
1065
- *
1066
- * The win is scoped to re-delivery of the **current** value. A full
1067
- * replay-from-zero over the current state still writes wherever the stream
1068
- * superseded a value in place: re-applying an older value over the live row
1069
- * is a genuine change (and restoring the current value afterwards is
1070
- * another), so only streams whose rows never supersede each other replay
1071
- * without churn. A rebuild that should avoid that re-walk belongs in a fresh
1072
- * store — replay into it and publish it, rather than re-applying the log
1073
- * over current state.
1074
- *
1075
- * When enabled, an upsert that would not change the stored value performs
1076
- * **no write at all**: no `updateNode`, no recorded-time capture, no history
1077
- * row, no revision-anchor advance, and no `update` operation hooks (nothing
1078
- * happened, so nothing is reported). It resolves with the **existing** node,
1079
- * preserving its original `validFrom` / `updatedAt` / `version`.
1080
- *
1081
- * Receipt shape is unchanged and needs no new signal: a coalesced upsert
1082
- * still counts as one write intent (`writes.total` includes it), but
1083
- * captures nothing (`receipt.recorded` stays `undefined` when it is the only
1084
- * write) — the same shape a no-op delete already produces, so a consumer
1085
- * that carries the prior anchor forward on `recorded === undefined` handles
1086
- * it unchanged.
1087
- *
1088
- * A write is coalesced only when **all** of the following hold; otherwise the
1089
- * normal write happens:
1090
- * 1. An existing row is found for the id (else it is a create).
1091
- * 2. That row is not soft-deleted (a deleted row resurrects — a real
1092
- * change — and is never coalesced).
1093
- * 3. The caller passed no explicit `validFrom` / `validTo` (an explicit
1094
- * temporal override is a deliberate request and is never coalesced;
1095
- * applied per item in the bulk path).
1096
- * 4. The new props, merged over the stored props and run through the
1097
- * kind's Zod schema (defaults applied, values normalized), are deeply
1098
- * value-identical to the stored props (key order aside).
1099
- *
1100
- * Default-off because some consumers *want* an audit row per re-delivery as
1101
- * proof the event was reprocessed; coalescing removes that signal.
1065
+ * Used by `store.batch()` to run several queries in sequence against one
1066
+ * target — a transaction on backends that have them, the backend itself
1067
+ * otherwise. A set operation compiles to one statement; whether that target
1068
+ * is a shared connection is the adapter's business, not a function of
1069
+ * transaction support.
1102
1070
  */
1103
- coalesceUnchangedUpserts?: boolean;
1104
- /** SQL schema configuration from createSqlSchema(...) for custom table names */
1105
- schema?: SqlSchema;
1106
- /** Query default behaviors. */
1107
- queryDefaults?: Readonly<{
1108
- /** Default traversal ontology expansion mode (default: "inverse"). */
1109
- traversalExpansion?: TraversalExpansion;
1110
- }>;
1111
- }>;
1112
- /**
1113
- * Store options without built-in recorded-time capture. A recorded read relation
1114
- * can still be bound explicitly for hosts that populate history externally.
1115
- */
1116
- type LiveStoreOptions = BaseStoreOptions & Readonly<{
1117
- history?: false | undefined;
1118
- recordedRead?: ExternalRecordedReadSource | undefined;
1119
- }>;
1120
- /** Live-store options that do not bind a recorded-read relation. */
1121
- type UnboundLiveStoreOptions = LiveStoreOptions & Readonly<{
1122
- recordedRead?: undefined;
1123
- }>;
1124
- /** Live-store options with an explicitly bound recorded-read relation. */
1125
- type RecordedReadStoreOptions = LiveStoreOptions & Readonly<{
1126
- recordedRead: NonNullable<LiveStoreOptions["recordedRead"]>;
1127
- }>;
1128
- /**
1129
- * Store options with TypeGraph-managed recorded-time capture. `history: true`
1130
- * captures TypeGraph writes and binds TypeGraph's built-in recorded relation
1131
- * internally. Externally populated recorded read sources are read-only bindings
1132
- * and are intentionally accepted only by {@link LiveStoreOptions}.
1133
- */
1134
- type HistoryStoreOptions = BaseStoreOptions & Readonly<{
1135
- history: true;
1136
- recordedRead?: never;
1137
- }>;
1138
- /**
1139
- * Options for creating a store.
1140
- */
1141
- type StoreOptions = LiveStoreOptions | HistoryStoreOptions;
1142
- /**
1143
- * A mutable handle to the current `Store`, used by `store.evolve(...)`
1144
- * so long-lived consumers can dereference through the ref and pick up
1145
- * the new store after each evolve call. `current` is overwritten by
1146
- * `evolve()` atomically with the schema commit when the ref is passed
1147
- * via `evolve(extension, { ref })`.
1148
- *
1149
- * **Mid-request semantics.** `ref.current` flips on the *next* call
1150
- * after evolve, not mid-handler. Dereference once at request entry and
1151
- * reuse the captured `Store` for the duration:
1152
- *
1153
- * ```ts
1154
- * async function handleRequest(): Promise<void> {
1155
- * const store = ref.current; // capture once
1156
- * const tag = await store.nodes.Tag?.create({ label: "..." });
1157
- * // ...further work on the same `store`...
1158
- * }
1159
- * ```
1160
- *
1161
- * Pure dereferenceable handle — no event/subscription machinery. If
1162
- * consumers need eventing, they wrap the ref themselves.
1163
- *
1164
- * Generic over the held value (typically `Store<G>`) so the store
1165
- * module can refer to it without importing the `Store` class into
1166
- * `types.ts`. The type parameter is deliberately invariant: evolution writes
1167
- * a replacement into `current`, so a ref for an adapter-only or history-only
1168
- * Store must never be accepted by a Store that cannot preserve that surface.
1169
- */
1170
- interface StoreRef<in out T> {
1171
- current: T;
1071
+ executeOn(backend: GraphBackend | TransactionBackend): Promise<readonly R[]>;
1172
1072
  }
1073
+
1173
1074
  /**
1174
- * Result plus write summary. Returned by `store.transactionWithReceipt(fn)`,
1175
- * `store.withRecordedTransaction(externalTx, fn)` (the adopted-commit path), and
1176
- * `tx.measure(fn)` (a scoped sub-receipt). See {@link TransactionReceipt}.
1177
- */
1178
- type TransactionOutcome<T> = Readonly<{
1179
- result: T;
1180
- receipt: TransactionReceipt;
1181
- }>;
1182
- /**
1183
- * Transaction write summary.
1184
- *
1185
- * Receipt counts are completed write intents at the collection surface, not
1186
- * rows affected:
1187
- *
1188
- * 1. Every successful completion of a write method on `tx.nodes.*` /
1189
- * `tx.edges.*` counts. The authoritative method list is
1190
- * {@link NodeWrites} / {@link EdgeWrites}.
1191
- * 2. Bulk methods count by input length; an empty bulk call (`bulkCreate([])`)
1192
- * counts 0.
1193
- * 3. Single-row methods count 1 on resolve — including `delete` of an absent
1194
- * id and `getOrCreate*` that found an existing row. Consumers that need
1195
- * "did anything actually change" semantics apply their own per-operation
1196
- * policy.
1197
- * 4. A method that rejects counts 0 — even when the backend applied part of a
1198
- * bulk input before failing. On SQLite a failed statement does not abort
1199
- * the surrounding transaction, so a caller that catches the rejection and
1200
- * commits can persist rows the receipt never counted. Do not read the
1201
- * receipt as rows-affected in that scenario.
1202
- * 5. A node `delete` under `cascade` / `disconnect` removes connected edges
1203
- * through the backend, not the edge-collection surface; those removals do
1204
- * not appear in `edges`.
1205
- * 6. Rows-affected fidelity is intentionally out of scope for this first
1206
- * version; a future extension could ask backends to return row counts.
1207
- */
1208
- type TransactionReceipt = Readonly<{
1209
- writes: Readonly<{
1210
- /** Completed node write intents by node kind. */
1211
- nodes: Readonly<Record<string, number>>;
1212
- /** Completed edge write intents by edge kind. */
1213
- edges: Readonly<Record<string, number>>;
1214
- /** Sum of all node and edge write intents. */
1215
- total: number;
1216
- }>;
1217
- /**
1218
- * The recorded commit instant allocated for this store's graph by this
1219
- * transaction. Undefined when history capture is off, the transaction is
1220
- * read-only, or no captured writes were flushed. **Always undefined on a
1221
- * scoped receipt from {@link ScopedMeasure}** (`tx.measure`) — the recorded
1222
- * instant is a per-transaction flush concern allocated once when the whole
1223
- * transaction's capture flushes, unknowable mid-transaction.
1224
- */
1225
- recorded?: RecordedInstant;
1226
- }>;
1227
- /**
1228
- * Behavior when a get-or-create operation matches an existing record.
1229
- */
1230
- type IfExistsMode = "return" | "update";
1231
- /**
1232
- * Action taken by a get-or-create operation.
1233
- */
1234
- type GetOrCreateAction = "created" | "found" | "updated" | "resurrected";
1235
- /**
1236
- * Result of a node getOrCreateByConstraint operation.
1237
- */
1238
- type NodeGetOrCreateByConstraintResult<N extends NodeType> = Readonly<{
1239
- node: Node<N>;
1240
- action: GetOrCreateAction;
1241
- }>;
1242
- /**
1243
- * Options for node getOrCreateByConstraint operations.
1244
- */
1245
- type NodeGetOrCreateByConstraintOptions = Readonly<{
1246
- /** Existing record behavior. Default: "return" */
1247
- ifExists?: IfExistsMode;
1248
- }>;
1249
- /**
1250
- * Options for node bulkFindByIndex operations.
1075
+ * ExecutableQuery - A query that can be executed, paginated, or streamed.
1251
1076
  */
1252
- type NodeBulkFindByIndexOptions = Readonly<{
1253
- /**
1254
- * Maximum number of candidate nodes returned per input item. When omitted,
1255
- * each input's candidate set is unbounded. Must be a positive integer.
1256
- *
1257
- * Candidates are ordered deterministically by node id, so the cap is stable
1258
- * across calls. Use this to bound fan-out on low-selectivity index keys.
1259
- */
1260
- limitPerInput?: number;
1261
- }>;
1077
+
1262
1078
  /**
1263
- * Per-endpoint fan-out cap shared by the live and view forms of the edge
1264
- * bulk endpoint reads.
1079
+ * A query that can be executed.
1265
1080
  */
1266
- type EdgeBulkFindOptions = Readonly<{
1081
+ declare class ExecutableQuery<G extends GraphDef, Aliases extends AliasMap, EdgeAliases extends EdgeAliasMap = {}, RecursiveAliases extends RecursiveAliasMap = {}, R = unknown> {
1082
+ #private;
1083
+ constructor(config: QueryBuilderConfig, state: QueryBuilderState, selectFunction: (context: SelectContext<Aliases, EdgeAliases, RecursiveAliases>) => R);
1267
1084
  /**
1268
- * Maximum number of edges returned for each input endpoint. When omitted,
1269
- * every input's edge set is unbounded. Must be a positive integer.
1270
- *
1271
- * The cap keeps each input's leading edges under the same ordering
1272
- * `findFrom` / `findTo` return, so it bounds fan-out without reordering
1273
- * anything.
1085
+ * Builds the query AST (memoized — the instance is immutable).
1274
1086
  */
1275
- limitPerInput?: number;
1276
- }>;
1277
- /**
1278
- * Options for the edge bulk endpoint reads: the temporal coordinate every
1279
- * edge read accepts, plus the per-endpoint fan-out cap.
1280
- */
1281
- type EdgeBulkFindEndpointOptions = QueryOptions & EdgeBulkFindOptions;
1282
- /**
1283
- * Result of an edge getOrCreateByEndpoints operation.
1284
- */
1285
- type EdgeGetOrCreateByEndpointsResult<E extends AnyEdgeType, From extends NodeType = NodeType, To extends NodeType = NodeType> = Readonly<{
1286
- edge: Edge<E, From, To>;
1287
- action: GetOrCreateAction;
1288
- }>;
1289
- /**
1290
- * Options for edge findByEndpoints operations.
1291
- */
1292
- type EdgeFindByEndpointsOptions<E extends AnyEdgeType> = Readonly<{
1087
+ toAst(): QueryAst;
1293
1088
  /**
1294
- * Edge property fields to include in the match alongside the (from, to) endpoints.
1295
- * When omitted, matches on endpoints only (returns the first edge at the read coordinate).
1089
+ * Orders results.
1296
1090
  */
1297
- matchOn?: readonly (keyof z.input<E["schema"]>)[];
1298
- /** Property values to match against when matchOn is specified. */
1299
- props?: Partial<z.input<E["schema"]>>;
1300
- }>;
1301
- /**
1302
- * Options for edge getOrCreateByEndpoints operations.
1303
- */
1304
- type EdgeGetOrCreateByEndpointsOptions<E extends AnyEdgeType> = Readonly<{
1091
+ orderBy<A extends (keyof Aliases | keyof EdgeAliases) & string>(alias: A, field: string, direction?: SortDirection): ExecutableQuery<G, Aliases, EdgeAliases, RecursiveAliases, R>;
1305
1092
  /**
1306
- * Edge property fields to include in the match key alongside the (from, to) endpoints.
1307
- * Default: `[]` — match on endpoints only.
1093
+ * Limits the number of results.
1308
1094
  */
1309
- matchOn?: readonly (keyof z.input<E["schema"]>)[];
1310
- /** Existing record behavior. Default: "return" */
1311
- ifExists?: IfExistsMode;
1095
+ limit(n: number): ExecutableQuery<G, Aliases, EdgeAliases, RecursiveAliases, R>;
1312
1096
  /**
1313
- * Valid-time start for a newly created edge. Ignored when an edge is found,
1314
- * updated, or resurrected.
1097
+ * Offsets the results.
1315
1098
  */
1316
- validFrom?: string;
1099
+ offset(n: number): ExecutableQuery<G, Aliases, EdgeAliases, RecursiveAliases, R>;
1317
1100
  /**
1318
- * Valid-time end for a created, updated, or resurrected edge. Ignored when
1319
- * the operation returns an existing edge without writing.
1320
- */
1321
- validTo?: string;
1322
- }>;
1323
- /**
1324
- * A collection of nodes of a specific type.
1325
- *
1326
- * Provides ergonomic CRUD operations for a single node type.
1327
- */
1328
- type NodeCollection<N extends NodeType, CN extends string = string> = Readonly<{
1329
- /**
1330
- * Create a new node.
1101
+ * Applies a query fragment to transform this executable query.
1331
1102
  *
1332
- * `validFrom` defaults to the operation's creation timestamp when omitted.
1333
- */
1334
- create: (props: z.input<N["schema"]>, options?: Readonly<{
1335
- id?: string;
1336
- validFrom?: string;
1337
- validTo?: string;
1338
- }>) => Promise<Node<N>>;
1339
- /** Get a node by ID */
1340
- getById: (id: NodeId<N>, options?: QueryOptions) => Promise<Node<N> | undefined>;
1341
- /** Get multiple nodes by ID, preserving input order (undefined for missing) */
1342
- getByIds: (ids: readonly NodeId<N>[], options?: QueryOptions) => Promise<readonly (Node<N> | undefined)[]>;
1343
- /** Update a node */
1344
- update: (id: NodeId<N>, props: Partial<z.input<N["schema"]>>, options?: Readonly<{
1345
- validTo?: string;
1346
- }>) => Promise<Node<N>>;
1347
- /** Delete a node (soft delete - sets deletedAt timestamp) */
1348
- delete: (id: NodeId<N>) => Promise<void>;
1349
- /**
1350
- * Permanently delete a node from the database.
1103
+ * Useful for applying post-select transformations like ordering,
1104
+ * limits, and offsets from reusable fragments.
1351
1105
  *
1352
- * Unlike `delete()` which performs a soft delete, this permanently
1353
- * removes the node and its associated data (uniqueness entries, embeddings).
1106
+ * @example
1107
+ * ```typescript
1108
+ * const paginated = (q) => q.orderBy("u", "createdAt", "desc").limit(10);
1354
1109
  *
1355
- * **Warning:** This operation is irreversible and should be used carefully.
1356
- * Consider using soft delete (`delete()`) for most use cases.
1110
+ * const results = await query()
1111
+ * .from("User", "u")
1112
+ * .select((ctx) => ctx.u)
1113
+ * .pipe(paginated)
1114
+ * .execute();
1115
+ * ```
1357
1116
  *
1358
- * @throws Error if edges are still connected to this node (delete edges first)
1117
+ * @param fragment - A function that transforms the executable query
1118
+ * @returns The transformed executable query
1359
1119
  */
1360
- hardDelete: (id: NodeId<N>) => Promise<void>;
1120
+ pipe<NewR = R>(fragment: (query: ExecutableQuery<G, Aliases, EdgeAliases, RecursiveAliases, R>) => ExecutableQuery<G, Aliases, EdgeAliases, RecursiveAliases, NewR>): ExecutableQuery<G, Aliases, EdgeAliases, RecursiveAliases, NewR>;
1361
1121
  /**
1362
- * Find nodes matching criteria.
1363
- *
1364
- * Supports predicate filtering via the `where` option for SQL-level filtering.
1365
- * For simple queries. Use store.query() for complex traversals.
1122
+ * Combines this query with another using UNION (removes duplicates).
1366
1123
  */
1367
- find: (filter?: Readonly<{
1368
- where?: (accessor: NodeAccessor<N>) => Predicate;
1369
- limit?: number;
1370
- offset?: number;
1371
- }>, temporal?: QueryOptions) => Promise<Node<N>[]>;
1372
- /** Count nodes matching criteria */
1373
- count: (temporal?: QueryOptions) => Promise<number>;
1124
+ union(other: ExecutableQuery<G, any, any, any, R>): UnionableQuery<G, R>;
1374
1125
  /**
1375
- * Create a node from untyped data, relying on runtime Zod validation.
1376
- *
1377
- * Use this for dynamic dispatch (changesets, migrations, imports) where
1378
- * the data shape is determined at runtime, not compile time.
1379
- * The return type is fully typed — only the input gate is relaxed.
1380
- *
1381
- * `validFrom` defaults to the operation's creation timestamp when omitted.
1126
+ * Combines this query with another using UNION ALL (keeps duplicates).
1382
1127
  */
1383
- createFromRecord: (data: Record<string, unknown>, options?: Readonly<{
1384
- id?: string;
1385
- validFrom?: string;
1386
- validTo?: string;
1387
- }>) => Promise<Node<N>>;
1128
+ unionAll(other: ExecutableQuery<G, any, any, any, R>): UnionableQuery<G, R>;
1388
1129
  /**
1389
- * Create or update a node.
1390
- *
1391
- * If a node with the given ID exists, updates it with the provided props.
1392
- * Otherwise, creates a new node with that ID.
1393
- *
1394
- * `validFrom` only applies on the create branch, defaulting to the
1395
- * operation's creation timestamp when omitted; it has no effect on update.
1130
+ * Combines this query with another using INTERSECT.
1396
1131
  */
1397
- upsertById: (id: string, props: z.input<N["schema"]>, options?: Readonly<{
1398
- validFrom?: string;
1399
- validTo?: string;
1400
- }>) => Promise<Node<N>>;
1132
+ intersect(other: ExecutableQuery<G, any, any, any, R>): UnionableQuery<G, R>;
1401
1133
  /**
1402
- * Upsert a node from untyped data, relying on runtime Zod validation.
1403
- *
1404
- * Use this for dynamic dispatch (changesets, migrations, imports) where
1405
- * the data shape is determined at runtime, not compile time.
1406
- * The return type is fully typed — only the input gate is relaxed.
1407
- *
1408
- * `validFrom` only applies on the create branch, defaulting to the
1409
- * operation's creation timestamp when omitted; it has no effect on update.
1134
+ * Combines this query with another using EXCEPT.
1410
1135
  */
1411
- upsertByIdFromRecord: (id: string, data: Record<string, unknown>, options?: Readonly<{
1412
- validFrom?: string;
1413
- validTo?: string;
1414
- }>) => Promise<Node<N>>;
1136
+ except(other: ExecutableQuery<G, any, any, any, R>): UnionableQuery<G, R>;
1415
1137
  /**
1416
- * Create multiple nodes in a batch.
1417
- *
1418
- * More efficient than calling create() multiple times.
1419
- * Use `bulkInsert` for the dedicated fast path that skips returning results.
1138
+ * Compiles the query and returns the SQL text and parameters.
1420
1139
  *
1421
- * `validFrom` defaults to the operation's creation timestamp when omitted.
1140
+ * Requires a backend to be configured (the backend determines the SQL dialect).
1141
+ * Use this for debugging, logging, or running the query with a custom executor.
1422
1142
  */
1423
- bulkCreate: (items: readonly Readonly<{
1424
- props: z.input<N["schema"]>;
1425
- id?: string;
1426
- validFrom?: string;
1427
- validTo?: string;
1428
- }>[]) => Promise<Node<N>[]>;
1143
+ toSQL(): Readonly<{
1144
+ sql: string;
1145
+ params: readonly unknown[];
1146
+ }>;
1429
1147
  /**
1430
- * Create or update multiple nodes in a batch.
1431
- *
1432
- * For each item, if a node with the given ID exists, updates it.
1433
- * Otherwise, creates a new node with that ID.
1148
+ * Compiles the query to TypeGraph's database-independent SQL fragment.
1434
1149
  *
1435
- * `validFrom` only applies on the create branch, defaulting to the
1436
- * operation's creation timestamp when omitted; it has no effect on update.
1150
+ * Pass the result to a GraphBackend, or use toSQL() to render SQL text and
1151
+ * parameters for the configured dialect.
1437
1152
  */
1438
- bulkUpsertById: (items: readonly Readonly<{
1439
- id: string;
1440
- props: z.input<N["schema"]>;
1441
- validFrom?: string;
1442
- validTo?: string;
1443
- }>[]) => Promise<Node<N>[]>;
1153
+ compile(): CompiledSelectSql;
1444
1154
  /**
1445
- * Insert multiple nodes without returning results.
1155
+ * Creates a prepared (pre-validated) query that can be executed multiple
1156
+ * times with different parameter bindings. Builds and structurally
1157
+ * validates the AST once (a malformed query fails fast, here, instead of on
1158
+ * first use); the prepared query then compiles once into a reusable template
1159
+ * and fills a fresh read instant per execute() — see PreparedQuery's class
1160
+ * doc comment, which also covers the two cases that recompile per call
1161
+ * instead (no `executeRaw`, or a statement whose semantics ride on the SQL
1162
+ * object rather than its text).
1446
1163
  *
1447
- * This is the dedicated fast path for bulk inserts. Unlike `bulkCreate`
1448
- * with `returnResults: false`, the intent is unambiguous: no results
1449
- * are returned and the operation is wrapped in a transaction.
1164
+ * Use `param("name")` in predicates to create parameterized slots,
1165
+ * then pass values via `prepared.execute({ name: "value" })`.
1450
1166
  *
1451
- * `validFrom` defaults to the operation's creation timestamp when omitted.
1452
- */
1453
- bulkInsert: (items: readonly Readonly<{
1454
- props: z.input<N["schema"]>;
1455
- id?: string;
1456
- validFrom?: string;
1457
- validTo?: string;
1458
- }>[]) => Promise<void>;
1459
- /**
1460
- * Delete multiple nodes by ID.
1167
+ * @example
1168
+ * ```typescript
1169
+ * import { param } from "@nicia-ai/typegraph";
1461
1170
  *
1462
- * Atomic when the backend supports transactions. Silently ignores IDs
1463
- * that don't exist.
1464
- */
1465
- bulkDelete: (ids: readonly NodeId<N>[]) => Promise<void>;
1466
- /**
1467
- * Find a node by uniqueness constraint.
1171
+ * const prepared = store.query()
1172
+ * .from("Person", "p")
1173
+ * .whereNode("p", (p) => p.name.eq(param("name")))
1174
+ * .select((ctx) => ctx.p)
1175
+ * .prepare();
1468
1176
  *
1469
- * Looks up a live node by the named constraint key computed from `props`.
1470
- * Returns the node if found, or undefined. Soft-deleted nodes are excluded.
1177
+ * const alice = await prepared.execute({ name: "Alice" });
1178
+ * const bob = await prepared.execute({ name: "Bob" });
1179
+ * ```
1471
1180
  *
1472
- * @param constraintName - Name of the uniqueness constraint to match on
1473
- * @param props - Properties to compute the constraint key from
1181
+ * @throws Error if no backend is configured
1474
1182
  */
1475
- findByConstraint: (constraintName: CN, props: z.input<N["schema"]>) => Promise<Node<N> | undefined>;
1183
+ prepare(): PreparedQuery<R>;
1476
1184
  /**
1477
- * Batch version of findByConstraint.
1185
+ * Executes the query and returns typed results.
1478
1186
  *
1479
- * Results are returned in the same order as the input items.
1480
- * Returns undefined for entries that don't match.
1187
+ * Uses smart optimization to detect when only specific fields are accessed
1188
+ * in the select callback. If the callback only accesses simple field
1189
+ * references (no method calls or computations), generates optimized SQL
1190
+ * that only extracts those fields instead of the full props blob.
1191
+ *
1192
+ * @throws Error if no backend is configured
1481
1193
  */
1482
- bulkFindByConstraint: (constraintName: CN, items: readonly Readonly<{
1483
- props: z.input<N["schema"]>;
1484
- }>[]) => Promise<(Node<N> | undefined)[]>;
1194
+ execute(): Promise<readonly R[]>;
1485
1195
  /**
1486
- * Batched candidate retrieval against a declared node index.
1487
- *
1488
- * For each input item, TypeGraph computes the index lookup key from
1489
- * `index.fields` (JSON-pointer extraction, partial-`where` applied to
1490
- * stored rows, null-safe matching) and returns the live, non-soft-deleted
1491
- * nodes that share that key. Unlike {@link bulkFindByConstraint}, the index
1492
- * may be non-unique, so each input yields a (possibly empty) array.
1196
+ * Executes the query against a provided backend.
1493
1197
  *
1494
- * Results preserve input order; each inner array is ordered by node id.
1495
- * Empty input returns `[]`. An unknown index name throws
1496
- * `NodeIndexNotFoundError`; a type-incompatible indexed field throws
1497
- * `ValidationError`. This is candidate retrieval, not a uniqueness or
1498
- * identity guarantee.
1198
+ * Used by `store.batch()` to run several queries in sequence against one
1199
+ * target — a transaction on backends that have them, the backend itself
1200
+ * otherwise. The full compile → execute → transform pipeline runs
1201
+ * identically to `execute()`, but against the given backend.
1499
1202
  *
1500
- * @param indexName - Name of the declared node index to match on
1501
- * @param items - Records whose `props` supply the indexed-field values
1502
- * @param options - Optional `limitPerInput` to bound per-input fan-out
1203
+ * Costs one statement, or two when the selective-field path runs and its
1204
+ * mapping then falls back: `#tryOptimizedExecutionOn` detects that only
1205
+ * after its statement has executed, and the caller re-runs the full fetch.
1206
+ * The fallback clears the fast path for this instance.
1503
1207
  */
1504
- bulkFindByIndex: (indexName: string, items: readonly Readonly<{
1505
- props: Partial<z.input<N["schema"]>>;
1506
- }>[], options?: NodeBulkFindByIndexOptions) => Promise<readonly Node<N>[][]>;
1208
+ executeOn(backend: GraphBackend | TransactionBackend): Promise<readonly R[]>;
1507
1209
  /**
1508
- * Get an existing node by uniqueness constraint, or create a new one.
1210
+ * Executes a paginated query using cursor-based keyset pagination.
1509
1211
  *
1510
- * Looks up a node by the named constraint key computed from `props`.
1511
- * If found, returns it (optionally updating with `ifExists: "update"`).
1512
- * If not found, creates a new node. Soft-deleted matches are always resurrected.
1212
+ * Cursor pagination is efficient for large datasets as it avoids OFFSET.
1213
+ * Requires ORDER BY to be specified for deterministic results.
1513
1214
  *
1514
- * @param constraintName - Name of the uniqueness constraint to match on
1515
- * @param props - Full properties for create, or merge source for update
1516
- * @param options - Existing record behavior (default: "return")
1215
+ * @param options - Pagination options (first/after for forward, last/before for backward)
1216
+ * @throws ValidationError if ORDER BY is not specified
1217
+ * @throws ValidationError if cursor columns don't match query ORDER BY columns
1517
1218
  */
1518
- getOrCreateByConstraint: (constraintName: CN, props: z.input<N["schema"]>, options?: NodeGetOrCreateByConstraintOptions) => Promise<NodeGetOrCreateByConstraintResult<N>>;
1219
+ paginate(options: PaginateOptions): Promise<PaginatedResult<R>>;
1519
1220
  /**
1520
- * Batch version of getOrCreateByConstraint.
1221
+ * Returns an async iterator that streams results in batches.
1521
1222
  *
1522
- * Results are returned in the same order as the input items.
1523
- * Atomic when the backend supports transactions.
1223
+ * Uses cursor pagination internally for efficient memory usage.
1224
+ * Requires ORDER BY to be specified for deterministic results.
1225
+ *
1226
+ * @param options - Stream options (batchSize defaults to 1000)
1227
+ * @throws ValidationError if ORDER BY is not specified
1524
1228
  */
1525
- bulkGetOrCreateByConstraint: (constraintName: CN, items: readonly Readonly<{
1526
- props: z.input<N["schema"]>;
1527
- }>[], options?: NodeGetOrCreateByConstraintOptions) => Promise<NodeGetOrCreateByConstraintResult<N>[]>;
1528
- }>;
1229
+ stream(options?: StreamOptions): AsyncIterable<R>;
1230
+ }
1231
+
1529
1232
  /**
1530
- * Reference to a node of a specific kind.
1531
- *
1532
- * Accepts either:
1533
- * - A Node instance of the correct kind
1534
- * - An explicit { kind, id } object with the correct kind name
1535
- *
1536
- * This provides compile-time checking that edge endpoints match the
1537
- * allowed node kinds defined in the edge registration.
1233
+ * TraversalBuilder - Intermediate builder for edge traversals.
1538
1234
  */
1539
- type NodeRef<N extends NodeType = NodeType> = Node<N> | Readonly<{
1540
- kind: N["kind"];
1541
- id: string;
1542
- }>;
1235
+
1543
1236
  /**
1544
- * Options for creating an edge.
1237
+ * Resolves the edge type for an edge alias based on its kind type parameter.
1238
+ *
1239
+ * For `traverse(...)`, `EK` is a literal like `"authoredBy"` and the
1240
+ * type comes from the typed graph. For `traverseDynamic(...)`, `EK` is
1241
+ * widened to `string` — `string extends EK` distinguishes that case and
1242
+ * resolves to `DynamicEdgeType`, so the conditional accessor types fire.
1545
1243
  */
1546
- type EdgeCreateOptions = Readonly<{
1547
- id?: string;
1548
- validFrom?: string;
1549
- validTo?: string;
1550
- }>;
1244
+ type EdgeTypeForKey<G extends GraphDef, EK> = string extends EK ? DynamicEdgeType : EK extends keyof G["edges"] & string ? G["edges"][EK]["type"] : DynamicEdgeType;
1551
1245
  /**
1552
- * Arguments for edge creation, with props optional when schema allows empty object.
1553
- *
1554
- * Uses `{}` to check if an empty object literal satisfies the schema input type.
1246
+ * State for variable-length traversal configuration.
1555
1247
  */
1556
- type EdgeCreateArguments<E extends AnyEdgeType> = {} extends z.input<E["schema"]> ? [
1557
- props?: z.input<E["schema"]>,
1558
- options?: EdgeCreateOptions
1559
- ] : [props: z.input<E["schema"]>, options?: EdgeCreateOptions];
1248
+ interface VariableLengthState {
1249
+ enabled: boolean;
1250
+ minDepth: number;
1251
+ maxDepth: number;
1252
+ cyclePolicy: RecursiveCyclePolicy;
1253
+ pathEnabled: boolean;
1254
+ pathAlias?: string;
1255
+ depthEnabled: boolean;
1256
+ depthAlias?: string;
1257
+ }
1560
1258
  /**
1561
- * A collection of edges of a specific type.
1562
- *
1563
- * Provides ergonomic CRUD operations for a single edge type.
1564
- * The From and To type parameters enforce that edge endpoints
1565
- * match the allowed node types at compile time.
1566
- *
1567
- * @example
1568
- * ```typescript
1569
- * // Create an edge - pass Node objects directly
1570
- * const edge = await store.edges.worksAt.create(alice, acme, { role: "Engineer" });
1571
- *
1572
- * // TypeScript error: Company is not a valid 'from' type for worksAt
1573
- * // store.edges.worksAt.create(acme, alice, { role: "Engineer" });
1574
- *
1575
- * // For edges with empty schemas, props is optional
1576
- * await store.edges.wrote.create(author, book);
1259
+ * Intermediate builder for traversal operations.
1577
1260
  *
1578
- * // Find edges from a node
1579
- * const edges = await store.edges.worksAt.findFrom(alice);
1580
- * ```
1261
+ * Type parameters track the edge kind and direction to constrain
1262
+ * which node kinds are valid targets in the `to()` method.
1581
1263
  */
1582
- type EdgeCollection<E extends AnyEdgeType, From extends NodeType = NodeType, To extends NodeType = NodeType> = Readonly<{
1264
+ declare class TraversalBuilder<G extends GraphDef, Aliases extends AliasMap, EdgeAliases extends EdgeAliasMap = EmptyEdgeAliasMap, EK extends keyof G["edges"] & string = keyof G["edges"] & string, EA extends string = string, Dir extends TraversalDirection$1 = "out", Optional extends boolean = false, DC extends boolean | string = false, PC extends boolean | string = false, RecAliases extends RecursiveAliasMap = EmptyRecursiveAliasMap, CoordinateState extends QueryCoordinateState = "open"> {
1265
+ #private;
1266
+ constructor(config: QueryBuilderConfig, state: QueryBuilderState, edgeKinds: readonly string[], edgeAlias: EA, direction: Dir, fromAlias: string, inverseEdgeKinds?: readonly string[], optional?: Optional, variableLength?: VariableLengthState, pendingEdgePredicates?: readonly NodePredicate[]);
1583
1267
  /**
1584
- * Create a new edge.
1585
- *
1586
- * `validFrom` defaults to the operation's creation timestamp when omitted.
1587
- *
1588
- * @param from - Source node (must be one of the allowed 'from' types)
1589
- * @param to - Target node (must be one of the allowed 'to' types)
1590
- * @param args - Edge properties (optional if schema is empty) and creation options
1268
+ * Enables variable-length (recursive) traversal.
1269
+ * Defaults to MAX_RECURSIVE_DEPTH (10) hops with cycle prevention.
1270
+ * Use `maxHops` to override (up to MAX_EXPLICIT_RECURSIVE_DEPTH).
1591
1271
  */
1592
- create: (from: NodeRef<From>, to: NodeRef<To>, ...args: EdgeCreateArguments<E>) => Promise<Edge<E, From, To>>;
1593
- /** Get an edge by ID */
1594
- getById: (id: EdgeId<E>, options?: QueryOptions) => Promise<Edge<E, From, To> | undefined>;
1595
- /** Get multiple edges by ID, preserving input order (undefined for missing) */
1596
- getByIds: (ids: readonly EdgeId<E>[], options?: QueryOptions) => Promise<readonly (Edge<E, From, To> | undefined)[]>;
1597
- /** Update an edge's properties */
1598
- update: (id: EdgeId<E>, props: Partial<z.input<E["schema"]>>, options?: Readonly<{
1599
- validTo?: string;
1600
- }>) => Promise<Edge<E, From, To>>;
1272
+ recursive<const O extends RecursiveTraversalOptions = Record<string, never>>(options?: O): TraversalBuilder<G, Aliases, EdgeAliases, EK, EA, Dir, Optional, O extends {
1273
+ depth: infer D extends boolean | string;
1274
+ } ? D : DC, O extends {
1275
+ path: infer P extends boolean | string;
1276
+ } ? P : PC, RecAliases, CoordinateState>;
1601
1277
  /**
1602
- * Find edges from a specific node.
1278
+ * Adds a WHERE clause for the edge being traversed.
1603
1279
  *
1604
- * Honors the same temporal model as `getById` / `find`: with no
1605
- * `options`, the graph's default `temporalMode` applies (excluding
1606
- * soft-deleted edges and, in `current` / `asOf` modes, edges outside
1607
- * their validity window). Pass `temporalMode` / `asOf` to read the
1608
- * endpoint's edges at another temporal coordinate.
1280
+ * @param alias - The edge alias to filter on (must be the current edge alias)
1281
+ * @param predicateFunction - A function that builds predicates using the edge accessor
1609
1282
  */
1610
- findFrom: (from: NodeRef<From>, options?: QueryOptions) => Promise<Edge<E, From, To>[]>;
1283
+ whereEdge(alias: EA, predicateFunction: (edge: EdgeAccessor<EdgeTypeForKey<G, EK>>) => Predicate): TraversalBuilder<G, Aliases, EdgeAliases, EK, EA, Dir, Optional, DC, PC, RecAliases, CoordinateState>;
1611
1284
  /**
1612
- * Find edges to a specific node.
1285
+ * Specifies the target node kind.
1613
1286
  *
1614
- * Temporal semantics mirror {@link EdgeCollection.findFrom}.
1287
+ * The kind must be a valid target for this edge based on the traversal direction:
1288
+ * - "out" direction: kind must be in the edge's "to" array
1289
+ * - "in" direction: kind must be in the edge's "from" array
1290
+ *
1291
+ * @param kind - The target node kind
1292
+ * @param alias - A unique alias for this node (compile-time error if duplicate)
1615
1293
  */
1616
- findTo: (to: NodeRef<To>, options?: QueryOptions) => Promise<Edge<E, From, To>[]>;
1294
+ to<K extends ValidEdgeTargets<G, EK, Dir>, A extends string>(kind: K, alias: UniqueAlias<A, Aliases>, options?: {
1295
+ includeSubClasses?: false;
1296
+ }): QueryBuilder<G, Aliases & Record<A, NodeAlias<G["nodes"][K]["type"], Optional>>, EdgeAliases & Record<EA, EdgeAlias<G["edges"][EK]["type"], Optional>>, RecAliases & BuildRecursiveAliases<DC, PC, A>, CoordinateState>;
1297
+ to<K extends ValidEdgeTargets<G, EK, Dir>, A extends string>(kind: K, alias: UniqueAlias<A, Aliases>, options: {
1298
+ includeSubClasses: true;
1299
+ }): QueryBuilder<G, Aliases & Record<A, NodeAlias<NodeType, Optional>>, EdgeAliases & Record<EA, EdgeAlias<G["edges"][EK]["type"], Optional>>, RecAliases & BuildRecursiveAliases<DC, PC, A>, CoordinateState>;
1617
1300
  /**
1618
- * Find the edges from a SET of source nodes in one read.
1619
- *
1620
- * `findFrom` with a widened endpoint predicate and nothing else: the same
1621
- * temporal model, the same soft-delete filtering, and the same per-source
1622
- * ordering — but `from_id IN (...)` instead of `from_id = ?`, so a page of
1623
- * N sources costs one statement per distinct source kind and bind-budget
1624
- * chunk rather than N singleton statements.
1625
- *
1626
- * Results are grouped per input: the outer array is parallel to `froms`
1627
- * (index `i` holds the edges of `froms[i]`), and repeated inputs each get
1628
- * their own copy of the same edge set. Empty input returns `[]`; a source
1629
- * with no edges gets an empty array. Large inputs are split across
1630
- * statements to respect the backend's bound-parameter budget, which is
1631
- * invisible in the result.
1632
- *
1633
- * Requires a backend implementing `findEdgesByEndpointSet` — both bundled
1634
- * Drizzle backends do. On one that does not, this **refuses** with a
1635
- * `ConfigurationError` rather than looping `findFrom` per input: asking for
1636
- * a bulk read is asking for set-oriented statements, and silently issuing N
1637
- * singleton statements is the cost surprise the method exists to remove.
1638
- *
1639
- * @param froms - Source nodes to read the edges of
1640
- * @param options - Temporal coordinate plus optional `limitPerInput`
1641
- * @throws {ConfigurationError} when the backend cannot read an endpoint set
1301
+ * String-keyed sibling of `to` for runtime-declared target kinds.
1302
+ * Throws `KindNotFoundError` if the kind is not registered.
1642
1303
  */
1643
- bulkFindFrom: (froms: readonly NodeRef<From>[], options?: EdgeBulkFindEndpointOptions) => Promise<readonly Edge<E, From, To>[][]>;
1304
+ toDynamic<A extends string>(kind: string, alias: UniqueAlias<A, Aliases>, options?: {
1305
+ includeSubClasses?: boolean;
1306
+ }): QueryBuilder<G, Aliases & Record<A, NodeAlias<DynamicNodeType, Optional>>, EdgeAliases & Record<EA, EdgeAlias<EdgeTypeForKey<G, EK>, Optional>>, RecAliases & BuildRecursiveAliases<DC, PC, A>, CoordinateState>;
1307
+ }
1308
+
1309
+ /**
1310
+ * QueryBuilder - The fluent query builder.
1311
+ */
1312
+
1313
+ type TemporalMethod<G extends GraphDef, Aliases extends AliasMap, EdgeAliases extends EdgeAliasMap, RecursiveAliases extends RecursiveAliasMap, CoordinateState extends QueryCoordinateState> = CoordinateState extends "open" ? (mode: TemporalMode, asOf?: string) => QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases, "open"> : never;
1314
+ /**
1315
+ * The fluent query builder.
1316
+ *
1317
+ * Type parameters accumulate as methods are chained:
1318
+ * - G: The graph definition
1319
+ * - Aliases: Map of alias names to their node kinds
1320
+ * - EdgeAliases: Map of alias names to their edge kinds (accumulated during traversals)
1321
+ */
1322
+ declare class QueryBuilder<G extends GraphDef, Aliases extends AliasMap = EmptyAliasMap, EdgeAliases extends EdgeAliasMap = EmptyEdgeAliasMap, RecursiveAliases extends RecursiveAliasMap = EmptyRecursiveAliasMap, CoordinateState extends QueryCoordinateState = "open"> {
1323
+ #private;
1324
+ readonly temporal: TemporalMethod<G, Aliases, EdgeAliases, RecursiveAliases, CoordinateState>;
1325
+ constructor(config: QueryBuilderConfig, state: QueryBuilderState);
1644
1326
  /**
1645
- * Find the edges into a SET of target nodes in one read.
1327
+ * Starts a query from a node kind.
1646
1328
  *
1647
- * Mirrors {@link EdgeCollection.bulkFindFrom} on the `to` endpoint.
1329
+ * @param kind - The node kind to start from
1330
+ * @param alias - A unique alias for this node (compile-time error if duplicate)
1648
1331
  */
1649
- bulkFindTo: (tos: readonly NodeRef<To>[], options?: EdgeBulkFindEndpointOptions) => Promise<readonly Edge<E, From, To>[][]>;
1332
+ from<K extends keyof G["nodes"] & string, A extends string>(kind: K, alias: UniqueAlias<A, Aliases>, options?: {
1333
+ includeSubClasses?: false;
1334
+ }): QueryBuilder<G, Aliases & Record<A, NodeAlias<G["nodes"][K]["type"]>>, EdgeAliases, RecursiveAliases, CoordinateState>;
1335
+ from<K extends keyof G["nodes"] & string, A extends string>(kind: K, alias: UniqueAlias<A, Aliases>, options: {
1336
+ includeSubClasses: true;
1337
+ }): QueryBuilder<G, Aliases & Record<A, NodeAlias>, EdgeAliases, RecursiveAliases, CoordinateState>;
1650
1338
  /**
1651
- * Deferred variant of `findFrom` for use with `store.batch()`.
1652
- *
1653
- * Returns a `BatchableQuery` instead of executing immediately. Accepts
1654
- * the same temporal `options` as {@link EdgeCollection.findFrom}.
1655
- *
1656
- * Batching these still issues one statement per call — it does not merge
1657
- * the reads, and whether they share a connection is up to the adapter. It
1658
- * is not a snapshot: PostgreSQL's default read-committed isolation lets a
1659
- * later read observe a commit the earlier ones did not. To read edges for
1660
- * many sources in one statement, traverse from them in a single query.
1339
+ * String-keyed sibling of `from` for runtime-declared kinds. Throws
1340
+ * `KindNotFoundError` if the kind is not registered. Predicates use
1341
+ * the `n.field("name").number().gte(...)` discriminator.
1661
1342
  */
1662
- batchFindFrom: (from: NodeRef<From>, options?: QueryOptions) => BatchableQuery<Edge<E, From, To>>;
1343
+ fromDynamic<A extends string>(kind: string, alias: UniqueAlias<A, Aliases>, options?: {
1344
+ includeSubClasses?: boolean;
1345
+ }): QueryBuilder<G, Aliases & Record<A, NodeAlias<DynamicNodeType>>, EdgeAliases, RecursiveAliases, CoordinateState>;
1663
1346
  /**
1664
- * Deferred variant of `findTo` for use with `store.batch()`.
1665
- *
1666
- * Returns a `BatchableQuery` instead of executing immediately. Accepts
1667
- * the same temporal `options` as {@link EdgeCollection.findTo}. Costs one
1668
- * statement per call, like {@link EdgeCollection.batchFindFrom}.
1347
+ * Adds a WHERE clause for a node.
1669
1348
  */
1670
- batchFindTo: (to: NodeRef<To>, options?: QueryOptions) => BatchableQuery<Edge<E, From, To>>;
1349
+ whereNode<A extends keyof Aliases & string>(alias: A, predicateFunction: (n: NodeAccessor<Aliases[A]["type"]>) => Predicate): QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases, CoordinateState>;
1671
1350
  /**
1672
- * Deferred variant of `findByEndpoints` for use with `store.batch()`.
1351
+ * Adds a WHERE clause for an edge.
1673
1352
  *
1674
- * Returns a `BatchableQuery` that yields a 0-or-1 element array
1675
- * (matching `findByEndpoints`' at-most-one semantics). Costs one statement
1676
- * per call, like {@link EdgeCollection.batchFindFrom}.
1353
+ * @param alias - The edge alias to filter on
1354
+ * @param predicateFunction - A function that builds predicates using the edge accessor
1677
1355
  */
1678
- batchFindByEndpoints: (from: NodeRef<From>, to: NodeRef<To>, options?: EdgeFindByEndpointsOptions<E>, temporal?: QueryOptions) => BatchableQuery<Edge<E, From, To>>;
1679
- /** Delete an edge (soft delete - sets deletedAt timestamp) */
1680
- delete: (id: EdgeId<E>) => Promise<void>;
1356
+ whereEdge<EA extends keyof EdgeAliases & string>(alias: EA, predicateFunction: (edge: EdgeAccessor<EdgeAliases[EA]["type"]>) => Predicate): QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases, CoordinateState>;
1681
1357
  /**
1682
- * Permanently delete an edge from the database.
1358
+ * Traverses an edge to another node (outgoing direction).
1683
1359
  *
1684
- * Unlike `delete()` which performs a soft delete, this permanently
1685
- * removes the edge record.
1360
+ * By default, traverses from the current node (last traversal target, or start node).
1361
+ * Use the `from` option to traverse from a different alias (fan-out pattern).
1686
1362
  *
1687
- * **Warning:** This operation is irreversible and should be used carefully.
1688
- * Consider using soft delete (`delete()`) for most use cases.
1363
+ * @param options.expand - Ontology expansion mode for implying/inverse edges
1364
+ * @param options.from - Alias to traverse from (defaults to current/last traversal target)
1689
1365
  */
1690
- hardDelete: (id: EdgeId<E>) => Promise<void>;
1691
- /** Find edges matching endpoint and pagination criteria */
1692
- find: (filter?: Readonly<{
1693
- from?: NodeRef<From>;
1694
- to?: NodeRef<To>;
1695
- limit?: number;
1696
- offset?: number;
1697
- }>, temporal?: QueryOptions) => Promise<Edge<E, From, To>[]>;
1698
- /** Count edges matching criteria */
1699
- count: (filter?: Readonly<{
1700
- from?: NodeRef<From>;
1701
- to?: NodeRef<To>;
1702
- }>, temporal?: QueryOptions) => Promise<number>;
1366
+ traverse<EK extends keyof G["edges"] & string, EA extends string>(edgeKind: EK, edgeAlias: EA, options?: {
1367
+ direction?: "out";
1368
+ expand?: TraversalExpansion;
1369
+ from?: keyof Aliases & string;
1370
+ }): TraversalBuilder<G, Aliases, EdgeAliases, EK, EA, "out", false, false, false, RecursiveAliases, CoordinateState>;
1703
1371
  /**
1704
- * Create multiple edges in a batch.
1372
+ * Traverses an edge to another node (incoming direction).
1705
1373
  *
1706
- * More efficient than calling create() multiple times.
1707
- * Use `bulkInsert` for the dedicated fast path that skips returning results.
1374
+ * By default, traverses from the current node (last traversal target, or start node).
1375
+ * Use the `from` option to traverse from a different alias (fan-out pattern).
1708
1376
  *
1709
- * `validFrom` defaults to the operation's creation timestamp when omitted.
1377
+ * @param options.direction - Set to "in" for incoming edge traversal
1378
+ * @param options.expand - Ontology expansion mode for implying/inverse edges
1379
+ * @param options.from - Alias to traverse from (defaults to current/last traversal target)
1710
1380
  */
1711
- bulkCreate: (items: readonly Readonly<{
1712
- from: NodeRef<From>;
1713
- to: NodeRef<To>;
1714
- props?: z.input<E["schema"]>;
1715
- id?: string;
1716
- validFrom?: string;
1717
- validTo?: string;
1718
- }>[]) => Promise<Edge<E, From, To>[]>;
1381
+ traverse<EK extends keyof G["edges"] & string, EA extends string>(edgeKind: EK, edgeAlias: EA, options: {
1382
+ direction: "in";
1383
+ expand?: TraversalExpansion;
1384
+ from?: keyof Aliases & string;
1385
+ }): TraversalBuilder<G, Aliases, EdgeAliases, EK, EA, "in", false, false, false, RecursiveAliases, CoordinateState>;
1719
1386
  /**
1720
- * Create or update multiple edges in a batch.
1721
- *
1722
- * For each item, if an edge with the given ID exists, updates it.
1723
- * Otherwise, creates a new edge with that ID.
1724
- *
1725
- * `validFrom` only applies on the create branch, defaulting to the
1726
- * operation's creation timestamp when omitted; it has no effect on update.
1387
+ * String-keyed sibling of `traverse` for runtime-declared edge kinds.
1388
+ * Throws `KindNotFoundError` if the edge kind is not registered.
1727
1389
  */
1728
- bulkUpsertById: (items: readonly Readonly<{
1729
- id: EdgeId<E>;
1730
- from: NodeRef<From>;
1731
- to: NodeRef<To>;
1732
- props?: z.input<E["schema"]>;
1733
- validFrom?: string;
1734
- validTo?: string;
1735
- }>[]) => Promise<Edge<E, From, To>[]>;
1390
+ traverseDynamic<EA extends string>(edgeKind: string, edgeAlias: EA, options?: {
1391
+ direction?: TraversalDirection$1;
1392
+ expand?: TraversalExpansion;
1393
+ from?: keyof Aliases & string;
1394
+ }): TraversalBuilder<G, Aliases, EdgeAliases & Record<EA, EdgeAlias<DynamicEdgeType>>, string, EA, TraversalDirection$1, false, false, false, RecursiveAliases, CoordinateState>;
1736
1395
  /**
1737
- * Insert multiple edges without returning results.
1396
+ * Optionally traverses an edge to another node (LEFT JOIN semantics).
1397
+ * If no matching edge/node exists, the result will include null values.
1738
1398
  *
1739
- * This is the dedicated fast path for bulk inserts. Unlike `bulkCreate`
1740
- * with `returnResults: false`, the intent is unambiguous: no results
1741
- * are returned and the operation is wrapped in a transaction.
1399
+ * By default, traverses from the current node (last traversal target, or start node).
1400
+ * Use the `from` option to traverse from a different alias (fan-out pattern).
1742
1401
  *
1743
- * `validFrom` defaults to the operation's creation timestamp when omitted.
1402
+ * @param options.direction - Direction of traversal: "out" (default) or "in"
1403
+ * @param options.expand - Ontology expansion mode for implying/inverse edges
1404
+ * @param options.from - Alias to traverse from (defaults to current/last traversal target)
1744
1405
  */
1745
- bulkInsert: (items: readonly Readonly<{
1746
- from: NodeRef<From>;
1747
- to: NodeRef<To>;
1748
- props?: z.input<E["schema"]>;
1749
- id?: string;
1750
- validFrom?: string;
1751
- validTo?: string;
1752
- }>[]) => Promise<void>;
1406
+ optionalTraverse<EK extends keyof G["edges"] & string, EA extends string>(edgeKind: EK, edgeAlias: EA, options?: {
1407
+ direction?: "out";
1408
+ expand?: TraversalExpansion;
1409
+ from?: keyof Aliases & string;
1410
+ }): TraversalBuilder<G, Aliases, EdgeAliases, EK, EA, "out", true, false, false, RecursiveAliases, CoordinateState>;
1411
+ optionalTraverse<EK extends keyof G["edges"] & string, EA extends string>(edgeKind: EK, edgeAlias: EA, options: {
1412
+ direction: "in";
1413
+ expand?: TraversalExpansion;
1414
+ from?: keyof Aliases & string;
1415
+ }): TraversalBuilder<G, Aliases, EdgeAliases, EK, EA, "in", true, false, false, RecursiveAliases, CoordinateState>;
1753
1416
  /**
1754
- * Delete multiple edges by ID.
1417
+ * String-keyed sibling of `optionalTraverse` for runtime-declared edge
1418
+ * kinds. LEFT JOIN semantics — non-matching rows produce a null edge
1419
+ * alias instead of dropping. Throws `KindNotFoundError` if the edge
1420
+ * kind is not registered.
1421
+ */
1422
+ optionalTraverseDynamic<EA extends string>(edgeKind: string, edgeAlias: EA, options?: {
1423
+ direction?: TraversalDirection$1;
1424
+ expand?: TraversalExpansion;
1425
+ from?: keyof Aliases & string;
1426
+ }): TraversalBuilder<G, Aliases, EdgeAliases & Record<EA, EdgeAlias<DynamicEdgeType, true>>, string, EA, TraversalDirection$1, true, false, false, RecursiveAliases, CoordinateState>;
1427
+ /**
1428
+ * Selects fields to return.
1429
+ */
1430
+ select<R>(selectFunction: (context: SelectContext<Aliases, EdgeAliases, RecursiveAliases>) => R): ExecutableQuery<G, Aliases, EdgeAliases, RecursiveAliases, R>;
1431
+ /**
1432
+ * Selects fields including aggregates.
1433
+ * Use with groupBy() for aggregate queries.
1755
1434
  *
1756
- * Atomic when the backend supports transactions. Silently ignores IDs
1757
- * that don't exist.
1435
+ * @param fields - Object mapping output names to field refs or aggregate expressions
1758
1436
  */
1759
- bulkDelete: (ids: readonly EdgeId<E>[]) => Promise<void>;
1437
+ aggregate<R extends Record<string, FieldRef | AggregateExpr>>(fields: R): ExecutableAggregateQuery<G, Aliases, R>;
1760
1438
  /**
1761
- * Find an edge by endpoints and optional property fields.
1439
+ * Orders results.
1440
+ */
1441
+ orderBy<A extends (keyof Aliases | keyof EdgeAliases) & string>(alias: A, field: string, direction?: SortDirection): QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases, CoordinateState>;
1442
+ /**
1443
+ * Limits the number of results.
1444
+ */
1445
+ limit(n: number): QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases, CoordinateState>;
1446
+ /**
1447
+ * Offsets the results.
1448
+ */
1449
+ offset(n: number): QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases, CoordinateState>;
1450
+ /**
1451
+ * Groups results by the specified field.
1452
+ * Use with aggregate functions like COUNT, SUM, AVG in select().
1762
1453
  *
1763
- * Returns the first matching edge at the read coordinate, or undefined.
1764
- * Honors the temporal model like `findFrom` / `findTo`: by default
1765
- * (`current` mode) soft-deleted and out-of-window edges are excluded; under
1766
- * `includeTombstones` a soft-deleted edge can be returned.
1454
+ * @param alias - The node alias to group by
1455
+ * @param field - The field name to group by
1456
+ */
1457
+ groupBy<A extends keyof Aliases & string>(alias: A, field: string): QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases, CoordinateState>;
1458
+ /**
1459
+ * Groups results by the node ID.
1460
+ * Use when you want to group by a complete node rather than a specific field.
1767
1461
  *
1768
- * @param from - Source node
1769
- * @param to - Target node
1770
- * @param options - Match criteria (matchOn fields and property values)
1771
- * @param temporal - Temporal coordinate. With no `temporal`, the graph's
1772
- * default `temporalMode` applies (so under the default `"current"` mode,
1773
- * edges outside their validity window are excluded). Pass
1774
- * `temporalMode` / `asOf` to read the edge as of another coordinate.
1462
+ * @param alias - The node alias to group by (uses the node's ID)
1775
1463
  */
1776
- findByEndpoints: (from: NodeRef<From>, to: NodeRef<To>, options?: EdgeFindByEndpointsOptions<E>, temporal?: QueryOptions) => Promise<Edge<E, From, To> | undefined>;
1464
+ groupByNode<A extends keyof Aliases & string>(alias: A): QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases, CoordinateState>;
1777
1465
  /**
1778
- * Get an existing edge by endpoints and optional property fields, or create a new one.
1466
+ * Filters grouped results using aggregate conditions (HAVING clause).
1467
+ * Use after groupBy() to filter based on aggregate values.
1779
1468
  *
1780
- * Matches edges of this kind between `(from, to)`. When `matchOn` specifies
1781
- * property fields, only edges whose properties match on those fields are considered.
1782
- * Soft-deleted matches are resurrected when cardinality allows.
1469
+ * @param predicate - A predicate expression to filter groups
1470
+ */
1471
+ having(predicate: PredicateExpression): QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases, CoordinateState>;
1472
+ /**
1473
+ * Sets fusion parameters for hybrid (vector + fulltext) queries.
1783
1474
  *
1784
- * `validFrom` only applies on the create branch, defaulting to the
1785
- * operation's creation timestamp when omitted. `validTo` applies on create,
1786
- * update, and resurrection, but not when an existing edge is returned
1787
- * without a write.
1475
+ * Applies only when the query contains both a `.similarTo()` and a
1476
+ * `.$fulltext.matches()` predicate. Without this call, the default is
1477
+ * RRF with k=60 and equal weights. A mismatch between `.fuseWith()`
1478
+ * configuration and the predicates on the query is caught during
1479
+ * compilation, not here.
1788
1480
  *
1789
- * @param from - Source node
1790
- * @param to - Target node
1791
- * @param props - Full properties for create, or merge source for update
1792
- * @param options - Match criteria and conflict resolution
1481
+ * @example
1482
+ * ```typescript
1483
+ * store.query()
1484
+ * .from("Document", "d")
1485
+ * .whereNode("d", d =>
1486
+ * d.$fulltext.matches("renewable energy", 50)
1487
+ * .and(d.embedding.similarTo(vec, 50))
1488
+ * .and(d.tenantId.eq(tenant))
1489
+ * )
1490
+ * .fuseWith({ k: 60, weights: { fulltext: 1.5 } })
1491
+ * .limit(10)
1492
+ * .execute();
1493
+ * ```
1793
1494
  */
1794
- getOrCreateByEndpoints: (from: NodeRef<From>, to: NodeRef<To>, props: z.input<E["schema"]>, options?: EdgeGetOrCreateByEndpointsOptions<E>) => Promise<EdgeGetOrCreateByEndpointsResult<E, From, To>>;
1495
+ fuseWith(options: HybridFusionOptions): QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases, CoordinateState>;
1795
1496
  /**
1796
- * Batch version of getOrCreateByEndpoints.
1497
+ * Applies a query fragment to transform this builder.
1797
1498
  *
1798
- * Results are returned in the same order as the input items.
1799
- * Atomic when the backend supports transactions.
1499
+ * Fragments are reusable query transformations that can add predicates,
1500
+ * traversals, ordering, and other query operations. Use this for
1501
+ * composing complex queries from simpler, reusable parts.
1502
+ *
1503
+ * @example
1504
+ * ```typescript
1505
+ * // Define a reusable fragment
1506
+ * const activeUsers = createFragment<MyGraph>()((q) =>
1507
+ * q.whereNode("u", ({ status }) => status.eq("active"))
1508
+ * );
1509
+ *
1510
+ * // Apply the fragment
1511
+ * const results = await query()
1512
+ * .from("User", "u")
1513
+ * .pipe(activeUsers)
1514
+ * .select((ctx) => ctx.u)
1515
+ * .execute();
1516
+ * ```
1517
+ *
1518
+ * @param fragment - A function that transforms the builder
1519
+ * @returns The transformed builder
1800
1520
  */
1801
- bulkGetOrCreateByEndpoints: (items: readonly Readonly<{
1802
- from: NodeRef<From>;
1803
- to: NodeRef<To>;
1804
- props: z.input<E["schema"]>;
1805
- validFrom?: string;
1806
- validTo?: string;
1807
- }>[], options?: Pick<EdgeGetOrCreateByEndpointsOptions<E>, "matchOn" | "ifExists">) => Promise<EdgeGetOrCreateByEndpointsResult<E, From, To>[]>;
1808
- }>;
1521
+ pipe<OutAliases extends AliasMap, OutEdgeAliases extends EdgeAliasMap = EdgeAliases, OutRecAliases extends RecursiveAliasMap = RecursiveAliases>(fragment: (builder: QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases, CoordinateState>) => QueryBuilder<G, OutAliases, OutEdgeAliases, OutRecAliases, CoordinateState>): QueryBuilder<G, OutAliases, OutEdgeAliases, OutRecAliases, CoordinateState>;
1522
+ }
1523
+
1809
1524
  /**
1810
- * Extract uniqueness constraint names from a NodeRegistration.
1525
+ * Fluent query builder for TypeGraph.
1811
1526
  *
1812
- * - Returns `never` when no uniqueness constraints are configured.
1813
- * - Returns a literal union when names are inferred via `defineGraph` const params.
1814
- * - Falls back to `string` when names are widened/dynamic.
1527
+ * Provides a type-safe, chainable API for building queries.
1528
+ * Each method returns a new builder instance with expanded type information.
1529
+ *
1530
+ * This module re-exports from the builder submodules and provides the
1531
+ * createQueryBuilder factory function.
1815
1532
  */
1816
- type ConstraintNames<R extends NodeRegistration> = "unique" extends keyof R ? R["unique"] extends readonly {
1817
- readonly name: infer N;
1818
- }[] ? N & string : string : never;
1533
+
1534
+ type InitialQueryBuilder<G extends GraphDef, CoordinateState extends QueryCoordinateState = "open"> = QueryBuilder<G, EmptyAliasMap, EmptyEdgeAliasMap, EmptyRecursiveAliasMap, CoordinateState>;
1535
+ declare function createQueryBuilder<G extends GraphDef>(graphId: string, registry: KindRegistry, options?: CreateQueryBuilderOptions): InitialQueryBuilder<G>;
1536
+
1819
1537
  /**
1820
- * Extract the union of 'from' node types from an EdgeRegistration.
1538
+ * StoreView collection surface buckets.
1539
+ *
1540
+ * The live NodeCollection / EdgeCollection partition into buckets a StoreView
1541
+ * treats differently:
1542
+ *
1543
+ * - temporal reads honor the pinned coordinate;
1544
+ * - current reads have no temporal axis and are refused on temporal pins;
1545
+ * - writes are never available on a read-only view; and
1546
+ * - batch reads require store.batch() and are absent from a view.
1547
+ *
1548
+ * These arrays are the single runtime source of truth for the proxy routing and
1549
+ * the type-level source for the derived StoreView collection types. The
1550
+ * StoreView surface-classification test asserts they exactly partition the live
1551
+ * collection methods so new collection methods cannot be silently omitted.
1821
1552
  */
1822
- type EdgeFromTypes<R extends EdgeRegistration> = R["from"] extends readonly (infer N)[] ? N : never;
1553
+ /** Temporal-aware node read method names. */
1554
+ declare const NODE_TEMPORAL_READ_NAMES: readonly ["getById", "getByIds", "find", "count"];
1555
+ /** Current-state-only node reads (constraint / index lookups). */
1556
+ declare const CURRENT_ONLY_READ_NAMES: readonly ["findByConstraint", "bulkFindByConstraint", "bulkFindByIndex"];
1557
+ /** Node write method names: never available on a read-only StoreView. */
1558
+ declare const NODE_WRITE_NAMES: readonly ["create", "createFromRecord", "update", "updateWhere", "delete", "hardDelete", "upsertById", "upsertByIdFromRecord", "bulkCreate", "bulkUpsertById", "bulkInsert", "bulkDelete", "getOrCreateByConstraint", "bulkGetOrCreateByConstraint"];
1559
+ /** Temporal-aware edge read method names. */
1560
+ declare const EDGE_TEMPORAL_READ_NAMES: readonly ["getById", "getByIds", "find", "count", "findFrom", "findTo", "bulkFindFrom", "bulkFindTo", "findByEndpoints"];
1561
+ /** Deferred edge batch-read method names. */
1562
+ declare const EDGE_BATCH_READ_NAMES: readonly ["batchFindFrom", "batchFindTo", "batchFindByEndpoints"];
1563
+ /** Edge write method names: never available on a read-only StoreView. */
1564
+ declare const EDGE_WRITE_NAMES: readonly ["create", "update", "delete", "hardDelete", "bulkCreate", "bulkUpsertById", "bulkInsert", "bulkDelete", "getOrCreateByEndpoints", "bulkGetOrCreateByEndpoints"];
1565
+ /** Recorded-time collection point reads that reconstruct safely by id. */
1566
+ declare const RECORDED_POINT_READ_NAMES: readonly ["getById", "getByIds"];
1567
+
1823
1568
  /**
1824
- * Extract the union of 'to' node types from an EdgeRegistration.
1569
+ * Store types for TypeGraph operations.
1825
1570
  */
1826
- type EdgeToTypes<R extends EdgeRegistration> = R["to"] extends readonly (infer N)[] ? N : never;
1571
+
1827
1572
  /**
1828
- * Create a type-safe EdgeCollection from an EdgeRegistration.
1829
- * Extracts the edge type and from/to node types automatically.
1573
+ * Canonical mapping from snake_case row fields to camelCase meta fields.
1574
+ *
1575
+ * This is the single source of truth for which row columns become metadata.
1576
+ * Both the Meta types and the row-to-meta functions in row-mappers.ts must
1577
+ * stay in sync with this mapping. If you add a temporal/audit column to a
1578
+ * row type, add the mapping here — the compiler will then force you to
1579
+ * update the row mapper functions as well (since their return type is
1580
+ * NodeMeta/EdgeMeta, which is derived from this mapping).
1830
1581
  */
1831
- type TypedEdgeCollection<R extends EdgeRegistration> = EdgeCollection<R["type"], EdgeFromTypes<R> extends NodeType ? EdgeFromTypes<R> : NodeType, EdgeToTypes<R> extends NodeType ? EdgeToTypes<R> : NodeType>;
1832
- /** Mapped type of all node collections for a graph. */
1833
- type GraphNodeCollections<G extends GraphDef> = {
1834
- [K in keyof G["nodes"] & string]-?: NodeCollection<G["nodes"][K]["type"], ConstraintNames<G["nodes"][K]>>;
1835
- };
1836
- /** Mapped type of all edge collections for a graph. */
1837
- type GraphEdgeCollections<G extends GraphDef> = {
1838
- [K in keyof G["edges"] & string]-?: TypedEdgeCollection<G["edges"][K]>;
1839
- };
1840
- /** Temporal-aware node reads — a {@link StoreView} pins these. */
1841
- type NodeTemporalReads<N extends NodeType, CN extends string = string> = Pick<NodeCollection<N, CN>, (typeof NODE_TEMPORAL_READ_NAMES)[number]>;
1582
+ type TemporalMetaFieldMap = Readonly<{
1583
+ valid_from: "validFrom";
1584
+ valid_to: "validTo";
1585
+ created_at: "createdAt";
1586
+ updated_at: "updatedAt";
1587
+ deleted_at: "deletedAt";
1588
+ }>;
1842
1589
  /**
1843
- * Current-state-only node reads (constraint / index lookups). No temporal
1844
- * axis, so a {@link StoreView} delegates them on a `current` pin and refuses
1845
- * them on a temporal pin.
1590
+ * Maps row fields to their camelCase meta counterparts, preserving types.
1846
1591
  */
1847
- type NodeCurrentReads<N extends NodeType, CN extends string = string> = Pick<NodeCollection<N, CN>, (typeof CURRENT_ONLY_READ_NAMES)[number]>;
1848
- /** Node writes — never available on a read-only {@link StoreView}. */
1849
- type NodeWrites<N extends NodeType, CN extends string = string> = Pick<NodeCollection<N, CN>, (typeof NODE_WRITE_NAMES)[number]>;
1850
- /** Temporal-aware edge reads — a {@link StoreView} pins these. */
1851
- type EdgeTemporalReads<E extends AnyEdgeType, From extends NodeType = NodeType, To extends NodeType = NodeType> = Pick<EdgeCollection<E, From, To>, (typeof EDGE_TEMPORAL_READ_NAMES)[number]>;
1592
+ type MapRowToMeta<R extends Readonly<Record<string, unknown>>, M extends Readonly<Record<string, string>>> = Readonly<{
1593
+ [SnakeKey in keyof M as M[SnakeKey] & string]: SnakeKey extends keyof R ? R[SnakeKey] : never;
1594
+ }>;
1852
1595
  /**
1853
- * Deferred edge reads for `store.batch()` — absent from a {@link StoreView},
1854
- * which has no batch context.
1596
+ * Metadata for a node instance.
1597
+ * Derived from NodeRow via TemporalMetaFieldMap + version.
1598
+ *
1599
+ * Adding a new metadata column requires:
1600
+ * 1. Add the column to NodeRow in backend/types.ts
1601
+ * 2. Add the mapping to TemporalMetaFieldMap above
1602
+ * 3. The compiler will error in rowToNodeMeta() until you add the field there
1855
1603
  */
1856
- type EdgeBatchReads<E extends AnyEdgeType, From extends NodeType = NodeType, To extends NodeType = NodeType> = Pick<EdgeCollection<E, From, To>, (typeof EDGE_BATCH_READ_NAMES)[number]>;
1857
- /** Edge writes — never available on a read-only {@link StoreView}. */
1858
- type EdgeWrites<E extends AnyEdgeType, From extends NodeType = NodeType, To extends NodeType = NodeType> = Pick<EdgeCollection<E, From, To>, (typeof EDGE_WRITE_NAMES)[number]>;
1604
+ type NodeMeta = MapRowToMeta<NodeRow, TemporalMetaFieldMap> & Readonly<{
1605
+ version: NodeRow["version"];
1606
+ }>;
1859
1607
  /**
1860
- * The read-only node surface a {@link StoreView} exposes for one node kind.
1861
- * The temporal reads drop the per-call temporal argument — the pin owns the
1862
- * axis; the current reads ({@link NodeCurrentReads}) are exposed as-is
1863
- * (delegated on a `current` view, refused on a temporal pin). Writes live on
1864
- * the live `Store`. The conformance test asserts the temporal part equals the
1865
- * pinned form of {@link NodeTemporalReads}.
1608
+ * A node instance in the graph.
1609
+ *
1610
+ * Properties from the schema are spread at the top level for ergonomic access:
1611
+ * - `node.name` instead of `node.props.name`
1612
+ * - System metadata is under `node.meta.*`
1866
1613
  */
1867
- type StoreViewNodeCollection<N extends NodeType, CN extends string = string> = Readonly<{
1868
- /** Get a node by ID at the view's pinned coordinate. */
1869
- getById: (id: NodeId<N>) => Promise<Node<N> | undefined>;
1870
- /** Get multiple nodes by ID, preserving input order (undefined for missing). */
1871
- getByIds: (ids: readonly NodeId<N>[]) => Promise<readonly (Node<N> | undefined)[]>;
1872
- /** Find nodes matching criteria at the view's pinned coordinate. */
1873
- find: (filter?: Readonly<{
1874
- where?: (accessor: NodeAccessor<N>) => Predicate;
1875
- limit?: number;
1876
- offset?: number;
1877
- }>) => Promise<Node<N>[]>;
1878
- /** Count nodes at the view's pinned coordinate. */
1879
- count: () => Promise<number>;
1880
- }> & NodeCurrentReads<N, CN>;
1614
+ type Node<N extends NodeType = NodeType> = Readonly<{
1615
+ kind: N["kind"];
1616
+ id: NodeId<N>;
1617
+ meta: NodeMeta;
1618
+ }> & Readonly<z.infer<N["schema"]>>;
1881
1619
  /**
1882
- * The read-only edge surface a {@link StoreView} exposes for one edge
1883
- * kind, mirroring {@link StoreViewNodeCollection}. Every edge read —
1884
- * including `findByEndpoints` — honors the pin via the same temporal model,
1885
- * so (unlike nodes) there are no current-state-only edge reads.
1620
+ * Input for creating a node.
1886
1621
  */
1887
- type StoreViewEdgeCollection<E extends AnyEdgeType, From extends NodeType = NodeType, To extends NodeType = NodeType> = Readonly<{
1888
- /** Get an edge by ID at the view's pinned coordinate. */
1889
- getById: (id: EdgeId<E>) => Promise<Edge<E, From, To> | undefined>;
1890
- /** Get multiple edges by ID, preserving input order (undefined for missing). */
1891
- getByIds: (ids: readonly EdgeId<E>[]) => Promise<readonly (Edge<E, From, To> | undefined)[]>;
1892
- /** Find edges matching endpoint and pagination criteria. */
1893
- find: (filter?: Readonly<{
1894
- from?: NodeRef<From>;
1895
- to?: NodeRef<To>;
1896
- limit?: number;
1897
- offset?: number;
1898
- }>) => Promise<Edge<E, From, To>[]>;
1899
- /** Count edges matching criteria at the view's pinned coordinate. */
1900
- count: (filter?: Readonly<{
1901
- from?: NodeRef<From>;
1902
- to?: NodeRef<To>;
1903
- }>) => Promise<number>;
1904
- /** Find edges from a specific node at the view's pinned coordinate. */
1905
- findFrom: (from: NodeRef<From>) => Promise<Edge<E, From, To>[]>;
1906
- /** Find edges to a specific node at the view's pinned coordinate. */
1907
- findTo: (to: NodeRef<To>) => Promise<Edge<E, From, To>[]>;
1908
- /** Find the edges from a set of source nodes at the view's pinned coordinate. */
1909
- bulkFindFrom: (froms: readonly NodeRef<From>[], options?: EdgeBulkFindOptions) => Promise<readonly Edge<E, From, To>[][]>;
1910
- /** Find the edges into a set of target nodes at the view's pinned coordinate. */
1911
- bulkFindTo: (tos: readonly NodeRef<To>[], options?: EdgeBulkFindOptions) => Promise<readonly Edge<E, From, To>[][]>;
1912
- /** Find the edge between two endpoints at the view's pinned coordinate. */
1913
- findByEndpoints: (from: NodeRef<From>, to: NodeRef<To>, options?: EdgeFindByEndpointsOptions<E>) => Promise<Edge<E, From, To> | undefined>;
1622
+ type CreateNodeInput<N extends NodeType = NodeType> = Readonly<{
1623
+ kind: N["kind"];
1624
+ id?: string;
1625
+ props: z.infer<N["schema"]>;
1626
+ validFrom?: string;
1627
+ validTo?: string;
1914
1628
  }>;
1915
1629
  /**
1916
- * Read-only view edge collection derived from an `EdgeRegistration`,
1917
- * extracting the edge type and from/to node types — the read-only
1918
- * counterpart of {@link TypedEdgeCollection}.
1630
+ * Input for updating a node.
1919
1631
  */
1920
- type TypedStoreViewEdgeCollection<R extends EdgeRegistration> = StoreViewEdgeCollection<R["type"], EdgeFromTypes<R> extends NodeType ? EdgeFromTypes<R> : NodeType, EdgeToTypes<R> extends NodeType ? EdgeToTypes<R> : NodeType>;
1921
- /** Mapped type of all read-only view node collections for a graph. */
1922
- type StoreViewNodeCollections<G extends GraphDef> = {
1923
- [K in keyof G["nodes"] & string]-?: StoreViewNodeCollection<G["nodes"][K]["type"]>;
1924
- };
1925
- /** Mapped type of all read-only view edge collections for a graph. */
1926
- type StoreViewEdgeCollections<G extends GraphDef> = {
1927
- [K in keyof G["edges"] & string]-?: TypedStoreViewEdgeCollection<G["edges"][K]>;
1928
- };
1929
- /** Options for one bounded, forward-only recorded-time collection scan. */
1930
- type RecordedScanOptions = Readonly<{
1931
- /** Maximum entities to return. Defaults to 1,000 and cannot exceed 1,000. */
1932
- limit?: number;
1933
- /** Opaque cursor returned by the preceding page. */
1934
- after?: string;
1935
- }>;
1936
- /** One page from a deterministic recorded-time collection scan. */
1937
- type RecordedScanPage<T> = Readonly<{
1938
- /** Entities ordered by canonical id ascending. */
1939
- data: readonly T[];
1940
- /** Cursor for the next page, or `undefined` when the scan is complete. */
1941
- nextCursor: string | undefined;
1942
- /** Whether another page exists after this one. */
1943
- hasNextPage: boolean;
1944
- }>;
1945
- /** Recorded-time reconstructing reads for one node kind. */
1946
- type RecordedStoreViewNodeCollection<N extends NodeType> = Pick<StoreViewNodeCollection<N>, (typeof RECORDED_POINT_READ_NAMES)[number]> & Readonly<{
1947
- /** Scan one bounded page at the view's pinned coordinate. */
1948
- scan: (options?: RecordedScanOptions) => Promise<RecordedScanPage<Node<N>>>;
1949
- }>;
1950
- /** Recorded-time reconstructing reads for one edge kind. */
1951
- type RecordedStoreViewEdgeCollection<E extends AnyEdgeType, From extends NodeType = NodeType, To extends NodeType = NodeType> = Pick<StoreViewEdgeCollection<E, From, To>, (typeof RECORDED_POINT_READ_NAMES)[number]> & Readonly<{
1952
- /** Scan one bounded page at the view's pinned coordinate. */
1953
- scan: (options?: RecordedScanOptions) => Promise<RecordedScanPage<Edge<E, From, To>>>;
1632
+ type UpdateNodeInput<N extends NodeType = NodeType> = Readonly<{
1633
+ kind: N["kind"];
1634
+ id: NodeId<N>;
1635
+ props: Partial<z.infer<N["schema"]>>;
1636
+ validTo?: string;
1954
1637
  }>;
1955
- /** Recorded-time edge collection derived from an `EdgeRegistration`. */
1956
- type TypedRecordedStoreViewEdgeCollection<R extends EdgeRegistration> = RecordedStoreViewEdgeCollection<R["type"], EdgeFromTypes<R> extends NodeType ? EdgeFromTypes<R> : NodeType, EdgeToTypes<R> extends NodeType ? EdgeToTypes<R> : NodeType>;
1957
- /** Mapped type of all recorded-time node reconstructing-read collections. */
1958
- type RecordedStoreViewNodeCollections<G extends GraphDef> = {
1959
- [K in keyof G["nodes"] & string]-?: RecordedStoreViewNodeCollection<G["nodes"][K]["type"]>;
1960
- };
1961
- /** Mapped type of all recorded-time edge reconstructing-read collections. */
1962
- type RecordedStoreViewEdgeCollections<G extends GraphDef> = {
1963
- [K in keyof G["edges"] & string]-?: TypedRecordedStoreViewEdgeCollection<G["edges"][K]>;
1964
- };
1965
1638
  /**
1966
- * Whether — and why — `tx.sql` can be used on a transaction context. A single
1967
- * required discriminant covering exactly the four states raw-SQL access can be
1968
- * in, so an adapter caller branches on capability instead of truthiness-testing
1969
- * `tx.sql`. The non-available variants omit `sql` entirely, so even reading the
1970
- * handle requires first narrowing `sqlAvailability` to `"available"`. The
1971
- * runtime object keeps a fail-loud getter under history / revision tracking for
1972
- * JavaScript and type-suppressed callers. See
1973
- * {@link AdapterTransactionContext} for the per-value semantics.
1639
+ * Metadata for an edge instance.
1640
+ * Derived from EdgeRow via TemporalMetaFieldMap (edges have no version).
1974
1641
  */
1975
- type SqlAvailability = "available" | "history" | "revisionTracking" | "unavailable";
1976
- type AdapterTransactionSqlAccess<TNativeTransaction> = Readonly<{
1977
- sql: TNativeTransaction;
1978
- sqlAvailability: "available";
1979
- }> | Readonly<{
1980
- sqlAvailability: "history" | "revisionTracking";
1981
- }> | Readonly<{
1982
- sqlAvailability: "unavailable";
1983
- }>;
1984
- declare const TRANSACTION_RUNTIME: unique symbol;
1985
- type TransactionRuntime = Readonly<{
1986
- backend: TransactionBackend;
1987
- runNodeOperationHooks: <T>(operation: "create" | "update" | "delete", kind: string, id: string, fn: () => Promise<T>) => Promise<T>;
1988
- }>;
1642
+ type EdgeMeta = MapRowToMeta<EdgeRow, TemporalMetaFieldMap>;
1989
1643
  /**
1990
- * The portable transaction context mirrors the Store's typed graph operations
1991
- * while exposing only a read-only backend projection bound to the transaction.
1992
- * Adapter-native handles are available only through
1993
- * {@link AdapterTransactionContext}; arbitrary backend writes are absent from
1994
- * every public transaction context. TypeGraph's non-enumerable symbol port is
1995
- * an unsupported implementation detail, not a JavaScript security boundary:
1996
- * reflective code can still discover symbol properties.
1644
+ * An edge instance in the graph.
1997
1645
  *
1998
- * @example
1999
- * ```typescript
2000
- * await store.transaction(async (tx) => {
2001
- * const person = await tx.nodes.Person.create({ name: "Alice" });
2002
- * const company = await tx.nodes.Company.create({ name: "Acme" });
2003
- * await tx.edges.worksAt.create(person, company, { role: "Engineer" });
2004
- * });
2005
- * ```
1646
+ * Properties from the schema are spread at the top level for ergonomic access:
1647
+ * - `edge.role` instead of `edge.props.role`
1648
+ * - System metadata is under `edge.meta.*`
2006
1649
  */
2007
- type TransactionCollections<G extends GraphDef> = Readonly<{
2008
- [TRANSACTION_RUNTIME]: TransactionRuntime;
2009
- nodes: GraphNodeCollections<G>;
2010
- edges: GraphEdgeCollections<G>;
2011
- /** Read-only backend projection bound to the same graph transaction. */
2012
- backend: TransactionReadBackend;
2013
- /**
2014
- * Runtime string-keyed node collection access, mirroring
2015
- * `Store.getNodeCollection`. Returns `undefined` when `kind` is not
2016
- * registered in this graph.
2017
- */
2018
- getNodeCollection: (kind: string) => DynamicNodeCollection | undefined;
2019
- }>;
1650
+ type Edge<E extends AnyEdgeType = EdgeType, From extends NodeType = NodeType, To extends NodeType = NodeType> = Readonly<{
1651
+ id: EdgeId<E>;
1652
+ kind: E["kind"];
1653
+ fromKind: From["kind"];
1654
+ fromId: NodeId<From>;
1655
+ toKind: To["kind"];
1656
+ toId: NodeId<To>;
1657
+ meta: EdgeMeta;
1658
+ }> & Readonly<z.infer<E["schema"]>>;
2020
1659
  /**
2021
- * A portable transaction context containing only TypeGraph-owned graph
2022
- * operations. Managed Stores use this surface so adapter-native handles never
2023
- * enter their public contract.
1660
+ * Input for creating an edge.
2024
1661
  */
2025
- type TransactionContext<G extends GraphDef> = TransactionCollections<G>;
1662
+ type CreateEdgeInput<E extends AnyEdgeType = EdgeType> = Readonly<{
1663
+ kind: E["kind"];
1664
+ id?: string;
1665
+ fromKind: string;
1666
+ fromId: string;
1667
+ toKind: string;
1668
+ toId: string;
1669
+ props: z.infer<E["schema"]>;
1670
+ validFrom?: string;
1671
+ validTo?: string;
1672
+ }>;
2026
1673
  /**
2027
- * A transaction context exposed by an {@link AdapterStore}. In addition to the
2028
- * portable graph collections, it carries the adapter-native handle when that
2029
- * capability is available. The TypeGraph backend remains the same runtime
2030
- * read projection as the portable context; adapter-native writes intentionally
2031
- * go through `sql`, making that escape hatch explicit.
2032
- */
2033
- type AdapterTransactionContext<G extends GraphDef, TNativeTransaction> = TransactionCollections<G> & AdapterTransactionSqlAccess<TNativeTransaction>;
2034
- /**
2035
- * Scoped write measurement, available only on the receipt-enabled transaction
2036
- * contexts (`transactionWithReceipt`, `withRecordedTransaction`). Runs `fn`,
2037
- * passing it a **scoped context** — a second view over the same transaction — and
2038
- * returns a {@link TransactionOutcome} whose receipt counts exactly the writes
2039
- * made *through that scoped context* (`scoped.nodes` / `scoped.edges`).
2040
- *
2041
- * Attribution is by **which context you write through**, not by timing. A write
2042
- * through the scoped context counts in both the scope and the outer receipt (it
2043
- * happened in the transaction); a write through the outer `tx` during the scope
2044
- * counts only in the outer receipt. This makes overlapping and concurrent
2045
- * measures safe by construction — two scopes running under `Promise.all`, each
2046
- * writing through its own scoped context, never cross-count. Nesting composes:
2047
- * `scoped.measure(...)` opens a child scope that counts in itself, every
2048
- * ancestor scope, and the outer receipt.
2049
- *
2050
- * Counts otherwise inherit {@link TransactionReceipt} (bulk by input length, a
2051
- * rejected write counts 0). The returned receipt's `recorded` is **always
2052
- * `undefined`**: the recorded commit instant is a per-transaction flush concern,
2053
- * unknowable mid-transaction.
1674
+ * Input for updating an edge.
2054
1675
  */
2055
- type ScopedMeasure<Context> = <T>(fn: (scoped: Context) => Promise<T>) => Promise<TransactionOutcome<T>>;
1676
+ type UpdateEdgeInput<E extends AnyEdgeType = EdgeType> = Readonly<{
1677
+ id: EdgeId<E>;
1678
+ props: Partial<z.infer<E["schema"]>>;
1679
+ validTo?: string;
1680
+ }>;
2056
1681
  /**
2057
- * A {@link TransactionContext} that also exposes {@link ScopedMeasure}. Only the
2058
- * receipt-enabled entry points (`transactionWithReceipt`,
2059
- * `withRecordedTransaction`) hand a callback this type; plain `transaction()`
2060
- * contexts have no recorder and therefore no `measure`, keeping that path
2061
- * zero-overhead. Assignable to {@link TransactionContext}, so a projector helper
2062
- * typed `(tx: TransactionContext<G>) => ...` accepts a measurable context. The
2063
- * scoped context handed to `measure` is itself measurable, so scopes nest.
1682
+ * Options for node and edge queries.
2064
1683
  */
2065
- type MeasurableTransactionContext<G extends GraphDef> = TransactionContext<G> & Readonly<{
2066
- measure: ScopedMeasure<MeasurableTransactionContext<G>>;
1684
+ type NoRecordedCoordinate = Readonly<{
1685
+ /**
1686
+ * Recorded/system-time coordinates are internal-only and supplied by
1687
+ * RecordedStoreView. A public options object carrying this key is a type
1688
+ * error even when the object is pre-bound before the call site.
1689
+ */
1690
+ recordedAsOf?: never;
2067
1691
  }>;
2068
- /** Receipt-enabled transaction context for an {@link AdapterStore}. */
2069
- type MeasurableAdapterTransactionContext<G extends GraphDef, TNativeTransaction> = AdapterTransactionContext<G, TNativeTransaction> & Readonly<{
2070
- measure: ScopedMeasure<MeasurableAdapterTransactionContext<G, TNativeTransaction>>;
1692
+ type QueryOptions = NoRecordedCoordinate & Readonly<{
1693
+ /** Temporal mode for the query */
1694
+ temporalMode?: TemporalMode;
1695
+ /** Specific timestamp for asOf queries */
1696
+ asOf?: string;
2071
1697
  }>;
2072
1698
  /**
2073
- * Replace branded `NodeId` / `EdgeId` with plain `string` in each
2074
- * method's parameter list. Return types are preserved unchanged.
2075
- *
2076
- * Handles three shapes:
2077
- * 1. Direct branded ID parameter → `string`
2078
- * 2. `readonly NodeId[]` / `readonly EdgeId[]` → `readonly string[]`
2079
- * 3. Branded IDs nested one level inside bulk-item object arrays
1699
+ * Context passed to observability hooks.
2080
1700
  */
2081
- type WidenBrandedIds<T> = {
2082
- readonly [K in keyof T]: T[K] extends (...args: infer A) => infer R ? (...args: {
2083
- [P in keyof A]: UnbrandParam<A[P]>;
2084
- }) => R : T[K];
2085
- };
2086
- /** Replace a branded ID with `string`, recursing one level into arrays. */
2087
- type UnbrandParam<T> = T extends NodeId<NodeType> ? string : T extends EdgeId<AnyEdgeType> ? string : T extends readonly NodeId<NodeType>[] ? readonly string[] : T extends readonly EdgeId<AnyEdgeType>[] ? readonly string[] : T extends readonly (infer Item extends Record<string, unknown>)[] ? readonly UnbrandRecord<Item>[] : T;
2088
- /** Replace branded ID values in object properties (does not recurse into nested structures). */
2089
- type UnbrandRecord<T extends Record<string, unknown>> = {
2090
- readonly [K in keyof T]: T[K] extends NodeId<NodeType> ? string : T[K] extends EdgeId<AnyEdgeType> ? string : T[K];
2091
- };
1701
+ type HookContext = Readonly<{
1702
+ /** Unique ID for this operation */
1703
+ operationId: string;
1704
+ /** Graph ID */
1705
+ graphId: string;
1706
+ /** Timestamp when operation started */
1707
+ startedAt: Date;
1708
+ }>;
2092
1709
  /**
2093
- * A node collection with widened generics for runtime string-keyed access.
2094
- *
2095
- * This is the return type of `store.getNodeCollection(kind)`. It exposes
2096
- * the full `NodeCollection` API but with `NodeType` and `string` constraint
2097
- * names instead of the specific generic parameters, since the concrete type
2098
- * is not known at compile time.
1710
+ * Context for one SQL statement a query builder submits to the backend.
2099
1711
  *
2100
- * ID parameters accept plain `string` instead of branded `NodeId<N>`, since
2101
- * the dynamic path typically receives IDs from edge metadata, snapshots,
2102
- * or external input where the brand is not available.
1712
+ * A single logical query can emit more than one context, for example when a
1713
+ * selective projection falls back to a full-row fetch. Backend-internal setup
1714
+ * statements are not exposed as separate query-hook events.
2103
1715
  */
2104
- type DynamicNodeCollection = WidenBrandedIds<NodeCollection<NodeType, string>>;
1716
+ type QueryHookContext = HookContext & Readonly<{
1717
+ /** The SQL statement being executed */
1718
+ sql: string;
1719
+ /** Query parameters */
1720
+ params: readonly unknown[];
1721
+ }>;
2105
1722
  /**
2106
- * An edge collection with widened generics for runtime string-keyed access.
2107
- *
2108
- * This is the return type of `store.getEdgeCollection(kind)`. It exposes
2109
- * the full `EdgeCollection` API but with `NodeType` endpoint types, since
2110
- * the concrete from/to types are not known at compile time.
2111
- *
2112
- * ID parameters accept plain `string` instead of branded `EdgeId<E>`, since
2113
- * the dynamic path typically receives IDs from edge metadata, snapshots,
2114
- * or external input where the brand is not available.
1723
+ * Operation hook context for CRUD operations.
2115
1724
  */
2116
- type DynamicEdgeCollection = WidenBrandedIds<EdgeCollection<AnyEdgeType, NodeType, NodeType>>;
1725
+ type OperationHookContext = HookContext & Readonly<{
1726
+ /** Operation type */
1727
+ operation: "create" | "update" | "delete";
1728
+ /** Entity type */
1729
+ entity: KindEntity;
1730
+ /** Kind of node or edge */
1731
+ kind: string;
1732
+ /** Entity ID */
1733
+ id: string;
1734
+ }>;
1735
+ /** Context for one set-based collection mutation. */
1736
+ type BulkOperationHookContext = HookContext & Readonly<{
1737
+ operation: "updateWhere";
1738
+ entity: "node";
1739
+ kind: string;
1740
+ }>;
2117
1741
  /**
2118
- * A type-level projection of a store's surface onto a subset of its
2119
- * node and edge collections.
1742
+ * Observability hooks for monitoring store operations.
2120
1743
  *
2121
- * Node collections are projected with constraint names erased (`never`),
2122
- * so constraint-based methods like `findByConstraint` become uncallable.
2123
- * This is intentional: unique constraints are graph-registration-level
2124
- * details that differ between graphs sharing the same node types.
1744
+ * Note: Batch operations (`bulkCreate`, `bulkInsert`, `bulkUpsertById`) skip
1745
+ * per-item operation hooks for throughput. Query hooks still fire normally.
2125
1746
  *
2126
1747
  * @example
2127
1748
  * ```typescript
2128
- * type CoreStore = StoreProjection<
2129
- * typeof myGraph,
2130
- * "Document" | "Chunk",
2131
- * "hasChunk"
2132
- * >;
1749
+ * const hooks: StoreHooks = {
1750
+ * onQueryStart: (ctx) => {
1751
+ * console.log(`[${ctx.operationId}] Query: ${ctx.sql}`);
1752
+ * },
1753
+ * onQueryEnd: (ctx, result) => {
1754
+ * const duration = Date.now() - ctx.startedAt.getTime();
1755
+ * console.log(`[${ctx.operationId}] Completed in ${duration}ms`);
1756
+ * },
1757
+ * onError: (ctx, error) => {
1758
+ * console.error(`[${ctx.operationId}] Error:`, error);
1759
+ * },
1760
+ * };
2133
1761
  *
2134
- * async function ingestChunk(
2135
- * store: CoreStore,
2136
- * document: Node<typeof Document>,
2137
- * text: string,
2138
- * ) {
2139
- * const chunk = await store.nodes.Chunk.create({ text });
2140
- * await store.edges.hasChunk.create(document, chunk);
2141
- * return chunk;
2142
- * }
1762
+ * const store = createStore(graph, backend, { hooks });
2143
1763
  * ```
2144
- *
2145
- * Both `Store<G>` and `TransactionContext<G>` are structurally assignable
2146
- * to a `StoreProjection` whose keys are a subset of `G`.
2147
- */
2148
- type StoreProjection<G extends GraphDef, N extends keyof G["nodes"] & string = never, E extends keyof G["edges"] & string = never> = Readonly<{
2149
- nodes: {
2150
- [K in N]-?: NodeCollection<G["nodes"][K]["type"], never>;
2151
- };
2152
- edges: Pick<GraphEdgeCollections<G>, E>;
2153
- }>;
2154
-
2155
- /**
2156
- * ExecutableAggregateQuery - A query with aggregate functions that can be executed.
2157
- */
2158
-
2159
- /**
2160
- * Result type for aggregate queries.
2161
- * Maps field refs to their value types and aggregates to numbers.
2162
- */
2163
- type AggregateResult<R extends Record<string, FieldRef | AggregateExpr>> = {
2164
- [K in keyof R]: R[K] extends AggregateExpr ? number : R[K] extends FieldRef ? unknown : never;
2165
- };
2166
- /**
2167
- * An aggregate query that can be executed.
2168
1764
  */
2169
- declare class ExecutableAggregateQuery<G extends GraphDef, Aliases extends AliasMap, R extends Record<string, FieldRef | AggregateExpr>> {
2170
- #private;
2171
- constructor(config: QueryBuilderConfig, state: QueryBuilderState, fields: R);
2172
- /**
2173
- * Builds the query AST.
2174
- */
2175
- toAst(): QueryAst;
1765
+ type StoreHooks = Readonly<{
1766
+ /** Called before each query-builder statement is submitted to the backend. */
1767
+ onQueryStart?: (ctx: QueryHookContext) => void;
1768
+ /** Called after each submitted query-builder statement succeeds. */
1769
+ onQueryEnd?: (ctx: QueryHookContext, result: Readonly<{
1770
+ rowCount: number;
1771
+ durationMs: number;
1772
+ }>) => void;
1773
+ /** Called before a CRUD operation starts */
1774
+ onOperationStart?: (ctx: OperationHookContext) => void;
1775
+ /** Called before a set-based collection mutation starts. */
1776
+ onBulkOperationStart?: (ctx: BulkOperationHookContext) => void;
2176
1777
  /**
2177
- * Orders results by a grouped field or aggregate alias.
2178
- *
2179
- * `key` is one of the output names passed to `.aggregate({...})` — either
2180
- * a grouped field (e.g. `genre`) or an aggregate alias (e.g. `bookCount`).
2181
- * Both are ordered the same way: by referencing the SELECT-list output
2182
- * column, since every `.aggregate()` field is projected with an alias.
2183
- *
2184
- * Chain multiple calls to sort by more than one key, in call order:
2185
- * `.orderBy("genre").orderBy("bookCount", "desc")` sorts by genre first,
2186
- * then by book count within each genre.
2187
- *
2188
- * @example
2189
- * ```typescript
2190
- * // Top 2 authors by book count
2191
- * store.query()
2192
- * .from("Author", "a")
2193
- * .traverse("wrote", "e")
2194
- * .to("Book", "b")
2195
- * .groupByNode("a")
2196
- * .aggregate({ author: field("a", "name"), bookCount: count("b") })
2197
- * .orderBy("bookCount", "desc")
2198
- * .limit(2)
2199
- * .execute();
2200
- * ```
1778
+ * Called after a CRUD operation completes AND is durably committed. For a
1779
+ * top-level operation that is when its own transaction commits; for an
1780
+ * operation inside `store.transaction`, emission is deferred until the
1781
+ * enclosing transaction commits — if that commit fails, the operation is
1782
+ * reported through `onError` instead. (Inside an adopted transaction —
1783
+ * `withTransaction` / `withRecordedTransaction` — the commit belongs to
1784
+ * the caller and cannot be observed, so this fires when the operation
1785
+ * completes within the still-open transaction.)
2201
1786
  */
2202
- orderBy<K extends keyof R & string>(key: K, direction?: SortDirection): ExecutableAggregateQuery<G, Aliases, R>;
1787
+ onOperationEnd?: (ctx: OperationHookContext, result: Readonly<{
1788
+ durationMs: number;
1789
+ }>) => void;
2203
1790
  /**
2204
- * Limits the number of results.
1791
+ * Called after a set-based mutation durably commits. Inside
1792
+ * `store.transaction`, emission is deferred until the enclosing commit.
2205
1793
  */
2206
- limit(n: number): ExecutableAggregateQuery<G, Aliases, R>;
1794
+ onBulkOperationEnd?: (ctx: BulkOperationHookContext, result: Readonly<{
1795
+ affectedCount: number;
1796
+ durationMs: number;
1797
+ }>) => void;
2207
1798
  /**
2208
- * Offsets the results.
1799
+ * Called when an operation fails — including an operation that completed
1800
+ * inside a `store.transaction` whose commit then failed and rolled it
1801
+ * back.
2209
1802
  */
2210
- offset(n: number): ExecutableAggregateQuery<G, Aliases, R>;
1803
+ onError?: (ctx: HookContext, error: Error) => void;
1804
+ }>;
1805
+ type BaseStoreOptions = Readonly<{
1806
+ /** Observability hooks for monitoring */
1807
+ hooks?: StoreHooks;
2211
1808
  /**
2212
- * Compiles the query and returns the SQL text and parameters.
1809
+ * Maintain a durable, graph-wide revision anchor for TypeGraph writes:
1810
+ * a random per-graph origin plus a monotonic revision clock. `branch()` uses
1811
+ * this anchor to validate that its base has not moved without
1812
+ * re-fingerprinting every live row, and cannot confuse a coincident clock in
1813
+ * a separately created store for its original base. `history: true` enables
1814
+ * the same tracking automatically through its recorded-time commit clock.
2213
1815
  *
2214
- * Requires a backend to be configured (the backend determines the SQL dialect).
2215
- * Use this for debugging, logging, or running the query with a custom executor.
1816
+ * This is intentionally opt-in for live stores: each successful graph write
1817
+ * advances the anchor inside its write transaction. On PostgreSQL, those
1818
+ * transactions take a graph-scoped advisory lock until commit; writes to the
1819
+ * same graph therefore serialize. Enable it for branchable graphs, but size
1820
+ * high-throughput multi-writer workloads for that trade-off.
1821
+ *
1822
+ * Writes performed directly through a backend — including raw graph-table
1823
+ * writes through `tx.sql` — bypass the anchor and are outside the
1824
+ * revision-tracking contract.
2216
1825
  */
2217
- toSQL(): Readonly<{
2218
- sql: string;
2219
- params: readonly unknown[];
2220
- }>;
1826
+ revisionTracking?: boolean;
2221
1827
  /**
2222
- * Compiles the query to TypeGraph's database-independent SQL fragment.
1828
+ * Automatic planner-statistics refresh after large autocommit bulk
1829
+ * writes (bulkCreate and bulkInsert on nodes and edges). Stale statistics after a
1830
+ * bulk load are a whole class of planner cliffs: the planner keeps
1831
+ * pre-load row estimates until ANALYZE runs. When a single
1832
+ * autocommit bulk write reaches the threshold
1833
+ * ({@link AUTO_REFRESH_STATISTICS_ROW_THRESHOLD} rows by default),
1834
+ * the store runs `refreshStatistics()` after the write commits.
1835
+ * Pass a number to change the threshold, or `false` to disable.
1836
+ * Bulk writes inside a caller-provided transaction never
1837
+ * auto-refresh (statistics cannot see uncommitted rows); refresh
1838
+ * manually after commit. `importGraph` handles its own refresh.
2223
1839
  */
2224
- compile(): CompiledSelectSql;
1840
+ autoRefreshStatistics?: false | number;
2225
1841
  /**
2226
- * Executes the query and returns typed results.
1842
+ * Skip the write for an `upsertById` (or `bulkUpsertById` item) whose
1843
+ * validated props are value-identical to the existing live row. Default
1844
+ * off.
2227
1845
  *
2228
- * @throws Error if no backend is configured
1846
+ * Enable this for at-least-once / replay materializers. An event log that
1847
+ * re-delivers a byte-identical change would otherwise rewrite the row anyway:
1848
+ * today an `upsertById` on an existing id calls `updateNode`
1849
+ * unconditionally, allocating a fresh recorded instant and a new history row
1850
+ * per re-delivery.
1851
+ *
1852
+ * The win is scoped to re-delivery of the **current** value. A full
1853
+ * replay-from-zero over the current state still writes wherever the stream
1854
+ * superseded a value in place: re-applying an older value over the live row
1855
+ * is a genuine change (and restoring the current value afterwards is
1856
+ * another), so only streams whose rows never supersede each other replay
1857
+ * without churn. A rebuild that should avoid that re-walk belongs in a fresh
1858
+ * store — replay into it and publish it, rather than re-applying the log
1859
+ * over current state.
1860
+ *
1861
+ * When enabled, an upsert that would not change the stored value performs
1862
+ * **no write at all**: no `updateNode`, no recorded-time capture, no history
1863
+ * row, no revision-anchor advance, and no `update` operation hooks (nothing
1864
+ * happened, so nothing is reported). It resolves with the **existing** node,
1865
+ * preserving its original `validFrom` / `updatedAt` / `version`.
1866
+ *
1867
+ * Receipt shape is unchanged and needs no new signal: a coalesced upsert
1868
+ * still counts as one write intent (`writes.total` includes it), but
1869
+ * captures nothing (`receipt.recorded` stays `undefined` when it is the only
1870
+ * write) — the same shape a no-op delete already produces, so a consumer
1871
+ * that carries the prior anchor forward on `recorded === undefined` handles
1872
+ * it unchanged.
1873
+ *
1874
+ * A write is coalesced only when **all** of the following hold; otherwise the
1875
+ * normal write happens:
1876
+ * 1. An existing row is found for the id (else it is a create).
1877
+ * 2. That row is not soft-deleted (a deleted row resurrects — a real
1878
+ * change — and is never coalesced).
1879
+ * 3. The caller passed no explicit `validFrom` / `validTo` (an explicit
1880
+ * temporal override is a deliberate request and is never coalesced;
1881
+ * applied per item in the bulk path).
1882
+ * 4. The new props, merged over the stored props and run through the
1883
+ * kind's Zod schema (defaults applied, values normalized), are deeply
1884
+ * value-identical to the stored props (key order aside).
1885
+ *
1886
+ * Default-off because some consumers *want* an audit row per re-delivery as
1887
+ * proof the event was reprocessed; coalescing removes that signal.
2229
1888
  */
2230
- execute(): Promise<readonly AggregateResult<R>[]>;
2231
- }
2232
-
1889
+ coalesceUnchangedUpserts?: boolean;
1890
+ /** SQL schema configuration from createSqlSchema(...) for custom table names */
1891
+ schema?: SqlSchema;
1892
+ /** Query default behaviors. */
1893
+ queryDefaults?: Readonly<{
1894
+ /** Default traversal ontology expansion mode (default: "inverse"). */
1895
+ traversalExpansion?: TraversalExpansion;
1896
+ }>;
1897
+ }>;
2233
1898
  /**
2234
- * How a "current" valid-time read instant is emitted into compiled SQL:
2235
- *
2236
- * - `"literal"` — bound as a concrete value at compile time
2237
- * ({@link currentReadInstant}). The instant is frozen into the compiled
2238
- * statement, so the statement must be recompiled to observe a newer "now".
2239
- * - `"placeholder"` — bound as a reserved execution-time placeholder
2240
- * ({@link currentReadInstantPlaceholder}). The statement can be compiled
2241
- * once and reused across executions, filling a fresh instant each call.
2242
- *
2243
- * Defaults to `"literal"` everywhere except the query builder's compiled
2244
- * template cache, which opts into `"placeholder"` so a reused/prepared query
2245
- * never freezes "now" the way a cached literal would (the #246 regression).
1899
+ * Store options without built-in recorded-time capture. A recorded read relation
1900
+ * can still be bound explicitly for hosts that populate history externally.
2246
1901
  */
2247
- type ReadInstantMode = "literal" | "placeholder";
2248
-
1902
+ type LiveStoreOptions = BaseStoreOptions & Readonly<{
1903
+ history?: false | undefined;
1904
+ recordedRead?: ExternalRecordedReadSource | undefined;
1905
+ }>;
1906
+ /** Live-store options that do not bind a recorded-read relation. */
1907
+ type UnboundLiveStoreOptions = LiveStoreOptions & Readonly<{
1908
+ recordedRead?: undefined;
1909
+ }>;
1910
+ /** Live-store options with an explicitly bound recorded-read relation. */
1911
+ type RecordedReadStoreOptions = LiveStoreOptions & Readonly<{
1912
+ recordedRead: NonNullable<LiveStoreOptions["recordedRead"]>;
1913
+ }>;
2249
1914
  /**
2250
- * Query Compiler Module
2251
- *
2252
- * Main entry point for compiling query ASTs to SQL.
2253
- * Re-exports individual compiler modules and provides the main compile functions.
1915
+ * Store options with TypeGraph-managed recorded-time capture. `history: true`
1916
+ * captures TypeGraph writes and binds TypeGraph's built-in recorded relation
1917
+ * internally. Externally populated recorded read sources are read-only bindings
1918
+ * and are intentionally accepted only by {@link LiveStoreOptions}.
2254
1919
  */
2255
-
1920
+ type HistoryStoreOptions = BaseStoreOptions & Readonly<{
1921
+ history: true;
1922
+ recordedRead?: never;
1923
+ }>;
2256
1924
  /**
2257
- * Options for query compilation.
1925
+ * Options for creating a store.
2258
1926
  */
2259
- type CompileQueryOptions = Readonly<{
2260
- /** SQL dialect ("sqlite" or "postgres"). Defaults to "sqlite". */
2261
- dialect?: SqlDialect | undefined;
2262
- /** SQL schema configuration from createSqlSchema(...). Defaults to standard names. */
2263
- schema?: SqlSchema | undefined;
2264
- /**
2265
- * Fulltext strategy override. When set, overrides the dialect's
2266
- * default fulltext strategy for `$fulltext.matches()` compilation.
2267
- * Callers typically read this from `backend.fulltextStrategy` so a
2268
- * backend-declared strategy (e.g. ParadeDB) wins over the dialect
2269
- * default (tsvector).
2270
- */
2271
- fulltextStrategy?: FulltextStrategy | undefined;
2272
- /**
2273
- * Vector strategy override. When set, `field.similarTo(...)`
2274
- * predicates compile against the strategy's per-`(kind, field)`
2275
- * tables and distance expression. Callers read it from
2276
- * `backend.vectorStrategy` so a backend-declared strategy (pgvector,
2277
- * libsql-native, sqlite-vec) drives the per-field relevance scan.
2278
- */
2279
- vectorStrategy?: VectorStrategy | undefined;
2280
- /**
2281
- * Whether the active backend supports SQL window functions such as
2282
- * `ROW_NUMBER()`. Defaults to true for direct compiler callers.
2283
- */
2284
- windowFunctions?: boolean | undefined;
2285
- /**
2286
- * Declared embedding slots `(kind, fieldPath) -> descriptor` used by
2287
- * the `field.similarTo(...)` CTE to know which kinds in an alias
2288
- * declare the field. Callers build it from the graph's node schemas.
2289
- */
2290
- vectorSlots?: VectorSlotMap | undefined;
2291
- /** Per-kind declared fulltext language (see buildFulltextLanguages). */
2292
- fulltextLanguages?: ReadonlyMap<string, string> | undefined;
2293
- /**
2294
- * Recorded read relation to use when the AST carries `recordedAsOf`.
2295
- * Supplying a recorded timestamp without this binding is rejected so the
2296
- * compiler never silently swaps to TypeGraph's built-in history tables when
2297
- * the store was only configured for live/valid-time reads.
2298
- */
2299
- recordedReadBinding?: RecordedReadBinding | undefined;
2300
- /**
2301
- * How the "current" valid-time read instant is emitted. Defaults to
2302
- * `"literal"` (a value frozen at compile time). The query builder's
2303
- * compiled template cache passes `"placeholder"` so the instant is a
2304
- * reserved execution-time slot, letting one compiled statement be reused
2305
- * across executions with a fresh "now" each call. See {@link ReadInstantMode}.
2306
- */
2307
- readInstant?: ReadInstantMode | undefined;
2308
- }>;
2309
-
1927
+ type StoreOptions = LiveStoreOptions | HistoryStoreOptions;
2310
1928
  /**
2311
- * PreparedQuery — a pre-validated, parameterized query.
1929
+ * A mutable handle to the current `Store`, used by `store.evolve(...)`
1930
+ * so long-lived consumers can dereference through the ref and pick up
1931
+ * the new Store after each evolve call. When the ref is passed via
1932
+ * `evolve(extension, { ref })`, `current` is overwritten with the replacement
1933
+ * before a successful call resolves.
2312
1934
  *
2313
- * Created via `ExecutableQuery.prepare()`. Builds and structurally validates
2314
- * the query AST once at prepare time (so a malformed query fails fast, before
2315
- * the first `execute()`).
1935
+ * **Request semantics.** Dereference once at request entry only when that
1936
+ * request will not change the schema. A schema-changing call such as
1937
+ * `evolve()` returns the Store for the resulting schema and updates
1938
+ * `ref.current`; a Store captured before that call remains pinned to the old
1939
+ * schema. Use the returned Store (or dereference `ref.current` again) for every
1940
+ * subsequent operation in the same request:
2316
1941
  *
2317
- * Fast path: when the backend can compile and run raw SQL (`compileSql` +
2318
- * `executeRaw`) AND the statement is raw-executable, it is compiled ONCE
2319
- * into a cached template whose
2320
- * "current" read instant and user `param()` refs are reserved placeholders
2321
- * (see {@link buildReadInstantTemplate}). Every `execute()` fills those
2322
- * placeholders — a fresh instant plus the call's bindings — and runs the
2323
- * cached SQL text directly, so a reused prepared query never recompiles and
2324
- * never freezes "now" the way a cached literal instant would (the #246
2325
- * regression).
1942
+ * ```ts
1943
+ * async function handleRequest(): Promise<void> {
1944
+ * const store = ref.current;
1945
+ * const evolved = await store.evolve(extension, { ref });
1946
+ * await evolved.materializeIndexes();
1947
+ * // ref.current === evolved
1948
+ * }
1949
+ * ```
2326
1950
  *
2327
- * Fallback: substitutes parameter refs into the AST, compiles fresh per call,
2328
- * and executes via the standard `backend.execute` path. Taken in two cases —
2329
- * a backend without raw execution (custom/async), and a statement that is not
2330
- * raw-executable because its execution semantics ride on the compiled SQL
2331
- * OBJECT rather than its text (approximate vector search's iterative-scan
2332
- * wrapper, `subgraph()`'s force-custom-plan fetches). The second applies even
2333
- * on PostgreSQL with `executeRaw` available; `isRawExecutable` in
2334
- * `sql-intent.ts` is the predicate that decides it.
1951
+ * A `StoreRef` tracks only schema changes made by calls that receive that ref.
1952
+ * When another process or isolate can commit a schema version, compare
1953
+ * `getCommittedSchemaVersion()` with the cached version before reuse and call
1954
+ * `createVerifiedStore()` or `createVerifiedAdapterStore()` when it changes.
1955
+ *
1956
+ * Pure dereferenceable handle — no event/subscription machinery. If
1957
+ * consumers need eventing, they wrap the ref themselves.
1958
+ *
1959
+ * Generic over the held value (typically `Store<G>`) so the store
1960
+ * module can refer to it without importing the `Store` class into
1961
+ * `types.ts`. The type parameter is deliberately invariant: evolution writes
1962
+ * a replacement into `current`, so a ref for an adapter-only or history-only
1963
+ * Store must never be accepted by a Store that cannot preserve that surface.
2335
1964
  */
2336
-
2337
- type PreparedQueryConfig<R> = Readonly<{
2338
- ast: QueryAst;
2339
- unoptimizedAst: QueryAst;
2340
- backend: GraphBackend;
2341
- dialect: SqlDialect;
2342
- graphId: string;
2343
- compileOptions: CompileQueryOptions;
2344
- state: QueryBuilderState;
2345
- selectiveFields: readonly SelectiveField[] | undefined;
2346
- selectFn: (context: SelectContext<AliasMap, EdgeAliasMap>) => R;
2347
- schemaIntrospector: SchemaIntrospector;
1965
+ interface StoreRef<in out T> {
1966
+ current: T;
1967
+ }
1968
+ /**
1969
+ * Result plus write summary. Returned by `store.transactionWithReceipt(fn)`,
1970
+ * `store.withRecordedTransaction(externalTx, fn)` (the adopted-commit path), and
1971
+ * `tx.measure(fn)` (a scoped sub-receipt). See {@link TransactionReceipt}.
1972
+ */
1973
+ type TransactionOutcome<T> = Readonly<{
1974
+ result: T;
1975
+ receipt: TransactionReceipt;
2348
1976
  }>;
2349
1977
  /**
2350
- * A pre-validated, parameterized query — see the module doc comment above for
2351
- * how a reused prepared query runs a cached SQL template with a fresh read
2352
- * instant filled per call.
1978
+ * Transaction write summary.
2353
1979
  *
2354
- * @example
2355
- * ```typescript
2356
- * const prepared = store.query()
2357
- * .from("Person", "p")
2358
- * .whereNode("p", (p) => p.name.eq(param("name")))
2359
- * .select((ctx) => ctx.p)
2360
- * .prepare();
1980
+ * Receipt counts are completed write intents at the collection surface, not
1981
+ * rows affected:
2361
1982
  *
2362
- * // Execute with different bindings
2363
- * const alice = await prepared.execute({ name: "Alice" });
2364
- * const bob = await prepared.execute({ name: "Bob" });
2365
- * ```
1983
+ * 1. Every successful completion of a write method on `tx.nodes.*` /
1984
+ * `tx.edges.*` counts. The authoritative method list is
1985
+ * {@link NodeWrites} / {@link EdgeWrites}.
1986
+ * 2. Bulk methods count by input length; an empty bulk call (`bulkCreate([])`)
1987
+ * counts 0.
1988
+ * 3. Single-row methods count 1 on resolve — including `delete` of an absent
1989
+ * id and `getOrCreate*` that found an existing row. Consumers that need
1990
+ * "did anything actually change" semantics apply their own per-operation
1991
+ * policy.
1992
+ * 4. A method that rejects counts 0 — even when the backend applied part of a
1993
+ * bulk input before failing. On SQLite a failed statement does not abort
1994
+ * the surrounding transaction, so a caller that catches the rejection and
1995
+ * commits can persist rows the receipt never counted. Do not read the
1996
+ * receipt as rows-affected in that scenario.
1997
+ * 5. A node `delete` under `cascade` / `disconnect` removes connected edges
1998
+ * through the backend, not the edge-collection surface; those removals do
1999
+ * not appear in `edges`.
2000
+ * 6. Rows-affected fidelity is intentionally out of scope for this first
2001
+ * version; a future extension could ask backends to return row counts.
2366
2002
  */
2367
- declare class PreparedQuery<R> {
2368
- #private;
2369
- constructor(config: PreparedQueryConfig<R>);
2370
- /** The set of parameter names required by this prepared query. */
2371
- get parameterNames(): ReadonlySet<string>;
2003
+ type TransactionReceipt = Readonly<{
2004
+ writes: Readonly<{
2005
+ /** Completed node write intents by node kind. */
2006
+ nodes: Readonly<Record<string, number>>;
2007
+ /** Completed edge write intents by edge kind. */
2008
+ edges: Readonly<Record<string, number>>;
2009
+ /** Sum of all node and edge write intents. */
2010
+ total: number;
2011
+ }>;
2372
2012
  /**
2373
- * Executes the prepared query with the given parameter bindings.
2374
- *
2375
- * @param bindings - A record mapping parameter names to their values
2376
- * @returns The query results
2013
+ * The recorded commit instant allocated for this store's graph by this
2014
+ * transaction. Undefined when history capture is off, the transaction is
2015
+ * read-only, or no captured writes were flushed. **Always undefined on a
2016
+ * scoped receipt from {@link ScopedMeasure}** (`tx.measure`) — the recorded
2017
+ * instant is a per-transaction flush concern allocated once when the whole
2018
+ * transaction's capture flushes, unknowable mid-transaction.
2377
2019
  */
2378
- execute(bindings?: Readonly<Record<string, unknown>>): Promise<readonly R[]>;
2379
- }
2380
-
2020
+ recorded?: RecordedInstant;
2021
+ }>;
2381
2022
  /**
2382
- * UnionableQuery - A query formed by combining multiple queries with set operations.
2023
+ * Behavior when a get-or-create operation matches an existing record.
2383
2024
  */
2384
-
2385
- interface ExecutableQueryLike<G extends GraphDef, R> {
2386
- toAst(): QueryAst;
2387
- }
2025
+ type IfExistsMode = "return" | "update";
2388
2026
  /**
2389
- * Internal state for unionable query.
2027
+ * Action taken by a get-or-create operation.
2390
2028
  */
2391
- type UnionableQueryState = Readonly<{
2392
- left: ComposableQuery;
2393
- operator: SetOperationType;
2394
- right: ComposableQuery;
2395
- limit?: number;
2396
- offset?: number;
2397
- startAlias?: string;
2398
- traversals?: readonly Traversal[];
2399
- selectFn?: (context: SelectContext<AliasMap, EdgeAliasMap>) => unknown;
2029
+ type GetOrCreateAction = "created" | "found" | "updated" | "resurrected";
2030
+ /**
2031
+ * Result of a node getOrCreateByConstraint operation.
2032
+ */
2033
+ type NodeGetOrCreateByConstraintResult<N extends NodeType> = Readonly<{
2034
+ node: Node<N>;
2035
+ action: GetOrCreateAction;
2400
2036
  }>;
2401
- declare class UnionableQuery<G extends GraphDef, R> {
2402
- #private;
2403
- constructor(config: QueryBuilderConfig, state: UnionableQueryState);
2037
+ /**
2038
+ * Options for node getOrCreateByConstraint operations.
2039
+ */
2040
+ type NodeGetOrCreateByConstraintOptions = Readonly<{
2041
+ /** Existing record behavior. Default: "return" */
2042
+ ifExists?: IfExistsMode;
2043
+ }>;
2044
+ /**
2045
+ * Options for node bulkFindByIndex operations.
2046
+ */
2047
+ type NodeBulkFindByIndexOptions = Readonly<{
2404
2048
  /**
2405
- * Combines with another query using UNION.
2049
+ * Maximum number of candidate nodes returned per input item. When omitted,
2050
+ * each input's candidate set is unbounded. Must be a positive integer.
2051
+ *
2052
+ * Candidates are ordered deterministically by node id, so the cap is stable
2053
+ * across calls. Use this to bound fan-out on low-selectivity index keys.
2406
2054
  */
2407
- union(other: ExecutableQueryLike<G, R>): UnionableQuery<G, R>;
2055
+ limitPerInput?: number;
2056
+ }>;
2057
+ /**
2058
+ * Per-endpoint fan-out cap shared by the live and view forms of the edge
2059
+ * bulk endpoint reads.
2060
+ */
2061
+ type EdgeBulkFindOptions = Readonly<{
2408
2062
  /**
2409
- * Combines with another query using UNION ALL.
2063
+ * Maximum number of edges returned for each input endpoint. When omitted,
2064
+ * every input's edge set is unbounded. Must be a positive integer.
2065
+ *
2066
+ * The cap keeps each input's leading edges under the same ordering
2067
+ * `findFrom` / `findTo` return, so it bounds fan-out without reordering
2068
+ * anything.
2410
2069
  */
2411
- unionAll(other: ExecutableQueryLike<G, R>): UnionableQuery<G, R>;
2070
+ limitPerInput?: number;
2071
+ }>;
2072
+ /**
2073
+ * Options for the edge bulk endpoint reads: the temporal coordinate every
2074
+ * edge read accepts, plus the per-endpoint fan-out cap.
2075
+ */
2076
+ type EdgeBulkFindEndpointOptions = QueryOptions & EdgeBulkFindOptions;
2077
+ /** A kind-grouped source list for a heterogeneous bulk edge read. */
2078
+ type BulkEdgeSourceGroup<G extends GraphDef> = {
2079
+ [K in NodeKinds<G>]: Readonly<{
2080
+ kind: K;
2081
+ ids: readonly NodeId<G["nodes"][K]["type"]>[];
2082
+ }>;
2083
+ }[NodeKinds<G>];
2084
+ /** A node reference whose kind and branded id remain correlated. */
2085
+ type GraphNodeReference<G extends GraphDef> = {
2086
+ [K in NodeKinds<G>]: Readonly<{
2087
+ kind: K;
2088
+ id: NodeId<G["nodes"][K]["type"]>;
2089
+ }>;
2090
+ }[NodeKinds<G>];
2091
+ /** Runtime edge union narrowed to the selected graph edge kinds. */
2092
+ type GraphEdgeForKinds<G extends GraphDef, K extends EdgeKinds<G>> = {
2093
+ [P in K]: Edge<G["edges"][P]["type"], G["edges"][P]["from"][number], G["edges"][P]["to"][number]>;
2094
+ }[K];
2095
+ /** Input for {@link Store.bulkFindEdgesFrom}. */
2096
+ type BulkFindEdgesFromParams<G extends GraphDef, K extends EdgeKinds<G>> = Readonly<{
2097
+ sources: readonly BulkEdgeSourceGroup<G>[];
2098
+ edgeKinds: readonly K[];
2099
+ }>;
2100
+ /** One source bucket returned by {@link Store.bulkFindEdgesFrom}. */
2101
+ type BulkFindEdgesFromResult<G extends GraphDef, K extends EdgeKinds<G>> = Readonly<{
2102
+ source: GraphNodeReference<G>;
2103
+ edges: readonly GraphEdgeForKinds<G, K>[];
2104
+ }>;
2105
+ /**
2106
+ * Result of an edge getOrCreateByEndpoints operation.
2107
+ */
2108
+ type EdgeGetOrCreateByEndpointsResult<E extends AnyEdgeType, From extends NodeType = NodeType, To extends NodeType = NodeType> = Readonly<{
2109
+ edge: Edge<E, From, To>;
2110
+ action: GetOrCreateAction;
2111
+ }>;
2112
+ /**
2113
+ * Options for edge findByEndpoints operations.
2114
+ */
2115
+ type EdgeFindByEndpointsOptions<E extends AnyEdgeType> = Readonly<{
2412
2116
  /**
2413
- * Combines with another query using INTERSECT.
2117
+ * Edge property fields to include in the match alongside the (from, to) endpoints.
2118
+ * When omitted, matches on endpoints only (returns the first edge at the read coordinate).
2414
2119
  */
2415
- intersect(other: ExecutableQueryLike<G, R>): UnionableQuery<G, R>;
2120
+ matchOn?: readonly (keyof z.input<E["schema"]>)[];
2121
+ /** Property values to match against when matchOn is specified. */
2122
+ props?: Partial<z.input<E["schema"]>>;
2123
+ }>;
2124
+ /**
2125
+ * Options for edge getOrCreateByEndpoints operations.
2126
+ */
2127
+ type EdgeGetOrCreateByEndpointsOptions<E extends AnyEdgeType> = Readonly<{
2416
2128
  /**
2417
- * Combines with another query using EXCEPT.
2129
+ * Edge property fields to include in the match key alongside the (from, to) endpoints.
2130
+ * Default: `[]` — match on endpoints only.
2418
2131
  */
2419
- except(other: ExecutableQueryLike<G, R>): UnionableQuery<G, R>;
2132
+ matchOn?: readonly (keyof z.input<E["schema"]>)[];
2133
+ /** Existing record behavior. Default: "return" */
2134
+ ifExists?: IfExistsMode;
2420
2135
  /**
2421
- * Limits the number of results from the combined query.
2136
+ * Valid-time start for a newly created edge. Ignored when an edge is found,
2137
+ * updated, or resurrected.
2422
2138
  */
2423
- limit(n: number): UnionableQuery<G, R>;
2139
+ validFrom?: string;
2424
2140
  /**
2425
- * Offsets the results from the combined query.
2141
+ * Valid-time end for a created, updated, or resurrected edge. Ignored when
2142
+ * the operation returns an existing edge without writing.
2426
2143
  */
2427
- offset(n: number): UnionableQuery<G, R>;
2144
+ validTo?: string;
2145
+ }>;
2146
+ /**
2147
+ * A collection of nodes of a specific type.
2148
+ *
2149
+ * Provides ergonomic CRUD operations for a single node type.
2150
+ */
2151
+ type NodeCollection<N extends NodeType, CN extends string = string> = Readonly<{
2428
2152
  /**
2429
- * Builds the set operation AST.
2153
+ * Create a new node.
2154
+ *
2155
+ * `validFrom` defaults to the operation's creation timestamp when omitted.
2430
2156
  */
2431
- toAst(): SetOperation;
2157
+ create: (props: z.input<N["schema"]>, options?: Readonly<{
2158
+ id?: string;
2159
+ validFrom?: string;
2160
+ validTo?: string;
2161
+ }>) => Promise<Node<N>>;
2162
+ /** Get a node by ID */
2163
+ getById: (id: NodeId<N>, options?: QueryOptions) => Promise<Node<N> | undefined>;
2164
+ /** Get multiple nodes by ID, preserving input order (undefined for missing) */
2165
+ getByIds: (ids: readonly NodeId<N>[], options?: QueryOptions) => Promise<readonly (Node<N> | undefined)[]>;
2166
+ /** Update a node */
2167
+ update: (id: NodeId<N>, props: Partial<z.input<N["schema"]>>, options?: Readonly<{
2168
+ validTo?: string;
2169
+ }>) => Promise<Node<N>>;
2432
2170
  /**
2433
- * Compiles the query and returns the SQL text and parameters.
2171
+ * Updates every current node selected by the supplied predicates in one
2172
+ * set-based write. Relationship clauses are independent EXISTS predicates
2173
+ * and are ANDed with each other and with `where`.
2174
+ */
2175
+ updateWhere: (params: Readonly<{
2176
+ patch: Partial<z.input<N["schema"]>>;
2177
+ where?: (accessor: string extends N["kind"] ? DynamicNodeAccessor : NodeAccessor<N>) => Predicate;
2178
+ exists?: readonly Readonly<{
2179
+ edgeKind: string;
2180
+ direction: "out" | "in";
2181
+ relatedKind: string;
2182
+ whereEdge?: (accessor: DynamicEdgeAccessor) => Predicate;
2183
+ whereRelated?: (accessor: DynamicNodeAccessor) => Predicate;
2184
+ }>[];
2185
+ all?: true;
2186
+ }>) => Promise<Readonly<{
2187
+ affectedCount: number;
2188
+ }>>;
2189
+ /** Delete a node (soft delete - sets deletedAt timestamp) */
2190
+ delete: (id: NodeId<N>) => Promise<void>;
2191
+ /**
2192
+ * Permanently delete a node from the database.
2434
2193
  *
2435
- * Requires a backend to be configured (the backend determines the SQL dialect).
2436
- * Use this for debugging, logging, or running the query with a custom executor.
2194
+ * Unlike `delete()` which performs a soft delete, this permanently
2195
+ * removes the node and its associated data (uniqueness entries, embeddings).
2196
+ *
2197
+ * **Warning:** This operation is irreversible and should be used carefully.
2198
+ * Consider using soft delete (`delete()`) for most use cases.
2199
+ *
2200
+ * @throws Error if edges are still connected to this node (delete edges first)
2437
2201
  */
2438
- toSQL(): Readonly<{
2439
- sql: string;
2440
- params: readonly unknown[];
2441
- }>;
2202
+ hardDelete: (id: NodeId<N>) => Promise<void>;
2442
2203
  /**
2443
- * Compiles the set operation to SQL.
2204
+ * Find nodes matching criteria.
2205
+ *
2206
+ * Supports predicate filtering via the `where` option for SQL-level filtering.
2207
+ * For simple queries. Use store.query() for complex traversals.
2444
2208
  */
2445
- compile(): CompiledSelectSql;
2209
+ find: (filter?: Readonly<{
2210
+ where?: (accessor: NodeAccessor<N>) => Predicate;
2211
+ limit?: number;
2212
+ offset?: number;
2213
+ }>, temporal?: QueryOptions) => Promise<Node<N>[]>;
2214
+ /** Count nodes matching criteria */
2215
+ count: (temporal?: QueryOptions) => Promise<number>;
2446
2216
  /**
2447
- * Executes the combined query.
2217
+ * Create a node from untyped data, relying on runtime Zod validation.
2218
+ *
2219
+ * Use this for dynamic dispatch (changesets, migrations, imports) where
2220
+ * the data shape is determined at runtime, not compile time.
2221
+ * The return type is fully typed — only the input gate is relaxed.
2222
+ *
2223
+ * `validFrom` defaults to the operation's creation timestamp when omitted.
2448
2224
  */
2449
- execute(): Promise<readonly R[]>;
2225
+ createFromRecord: (data: Record<string, unknown>, options?: Readonly<{
2226
+ id?: string;
2227
+ validFrom?: string;
2228
+ validTo?: string;
2229
+ }>) => Promise<Node<N>>;
2450
2230
  /**
2451
- * Executes the combined query against a provided backend.
2231
+ * Create or update a node.
2452
2232
  *
2453
- * Used by `store.batch()` to run several queries in sequence against one
2454
- * target — a transaction on backends that have them, the backend itself
2455
- * otherwise. A set operation compiles to one statement; whether that target
2456
- * is a shared connection is the adapter's business, not a function of
2457
- * transaction support.
2458
- */
2459
- executeOn(backend: GraphBackend | TransactionBackend): Promise<readonly R[]>;
2460
- }
2461
-
2462
- /**
2463
- * ExecutableQuery - A query that can be executed, paginated, or streamed.
2464
- */
2465
-
2466
- /**
2467
- * A query that can be executed.
2468
- */
2469
- declare class ExecutableQuery<G extends GraphDef, Aliases extends AliasMap, EdgeAliases extends EdgeAliasMap = {}, RecursiveAliases extends RecursiveAliasMap = {}, R = unknown> {
2470
- #private;
2471
- constructor(config: QueryBuilderConfig, state: QueryBuilderState, selectFunction: (context: SelectContext<Aliases, EdgeAliases, RecursiveAliases>) => R);
2472
- /**
2473
- * Builds the query AST (memoized — the instance is immutable).
2474
- */
2475
- toAst(): QueryAst;
2476
- /**
2477
- * Orders results.
2478
- */
2479
- orderBy<A extends (keyof Aliases | keyof EdgeAliases) & string>(alias: A, field: string, direction?: SortDirection): ExecutableQuery<G, Aliases, EdgeAliases, RecursiveAliases, R>;
2480
- /**
2481
- * Limits the number of results.
2482
- */
2483
- limit(n: number): ExecutableQuery<G, Aliases, EdgeAliases, RecursiveAliases, R>;
2484
- /**
2485
- * Offsets the results.
2233
+ * If a node with the given ID exists, updates it with the provided props.
2234
+ * Otherwise, creates a new node with that ID.
2235
+ *
2236
+ * `validFrom` only applies on the create branch, defaulting to the
2237
+ * operation's creation timestamp when omitted; it has no effect on update.
2486
2238
  */
2487
- offset(n: number): ExecutableQuery<G, Aliases, EdgeAliases, RecursiveAliases, R>;
2239
+ upsertById: (id: string, props: z.input<N["schema"]>, options?: Readonly<{
2240
+ validFrom?: string;
2241
+ validTo?: string;
2242
+ }>) => Promise<Node<N>>;
2488
2243
  /**
2489
- * Applies a query fragment to transform this executable query.
2490
- *
2491
- * Useful for applying post-select transformations like ordering,
2492
- * limits, and offsets from reusable fragments.
2493
- *
2494
- * @example
2495
- * ```typescript
2496
- * const paginated = (q) => q.orderBy("u", "createdAt", "desc").limit(10);
2244
+ * Upsert a node from untyped data, relying on runtime Zod validation.
2497
2245
  *
2498
- * const results = await query()
2499
- * .from("User", "u")
2500
- * .select((ctx) => ctx.u)
2501
- * .pipe(paginated)
2502
- * .execute();
2503
- * ```
2246
+ * Use this for dynamic dispatch (changesets, migrations, imports) where
2247
+ * the data shape is determined at runtime, not compile time.
2248
+ * The return type is fully typed — only the input gate is relaxed.
2504
2249
  *
2505
- * @param fragment - A function that transforms the executable query
2506
- * @returns The transformed executable query
2507
- */
2508
- pipe<NewR = R>(fragment: (query: ExecutableQuery<G, Aliases, EdgeAliases, RecursiveAliases, R>) => ExecutableQuery<G, Aliases, EdgeAliases, RecursiveAliases, NewR>): ExecutableQuery<G, Aliases, EdgeAliases, RecursiveAliases, NewR>;
2509
- /**
2510
- * Combines this query with another using UNION (removes duplicates).
2511
- */
2512
- union(other: ExecutableQuery<G, any, any, any, R>): UnionableQuery<G, R>;
2513
- /**
2514
- * Combines this query with another using UNION ALL (keeps duplicates).
2250
+ * `validFrom` only applies on the create branch, defaulting to the
2251
+ * operation's creation timestamp when omitted; it has no effect on update.
2515
2252
  */
2516
- unionAll(other: ExecutableQuery<G, any, any, any, R>): UnionableQuery<G, R>;
2253
+ upsertByIdFromRecord: (id: string, data: Record<string, unknown>, options?: Readonly<{
2254
+ validFrom?: string;
2255
+ validTo?: string;
2256
+ }>) => Promise<Node<N>>;
2517
2257
  /**
2518
- * Combines this query with another using INTERSECT.
2258
+ * Create multiple nodes in a batch.
2259
+ *
2260
+ * More efficient than calling create() multiple times.
2261
+ * Use `bulkInsert` for the dedicated fast path that skips returning results.
2262
+ *
2263
+ * `validFrom` defaults to the operation's creation timestamp when omitted.
2519
2264
  */
2520
- intersect(other: ExecutableQuery<G, any, any, any, R>): UnionableQuery<G, R>;
2265
+ bulkCreate: (items: readonly Readonly<{
2266
+ props: z.input<N["schema"]>;
2267
+ id?: string;
2268
+ validFrom?: string;
2269
+ validTo?: string;
2270
+ }>[]) => Promise<Node<N>[]>;
2521
2271
  /**
2522
- * Combines this query with another using EXCEPT.
2272
+ * Create or update multiple nodes in a batch.
2273
+ *
2274
+ * For each item, if a node with the given ID exists, updates it.
2275
+ * Otherwise, creates a new node with that ID.
2276
+ *
2277
+ * `validFrom` only applies on the create branch, defaulting to the
2278
+ * operation's creation timestamp when omitted; it has no effect on update.
2523
2279
  */
2524
- except(other: ExecutableQuery<G, any, any, any, R>): UnionableQuery<G, R>;
2280
+ bulkUpsertById: (items: readonly Readonly<{
2281
+ id: string;
2282
+ props: z.input<N["schema"]>;
2283
+ validFrom?: string;
2284
+ validTo?: string;
2285
+ }>[]) => Promise<Node<N>[]>;
2525
2286
  /**
2526
- * Compiles the query and returns the SQL text and parameters.
2287
+ * Insert multiple nodes without returning results.
2527
2288
  *
2528
- * Requires a backend to be configured (the backend determines the SQL dialect).
2529
- * Use this for debugging, logging, or running the query with a custom executor.
2289
+ * This is the dedicated fast path for bulk inserts. Unlike `bulkCreate`
2290
+ * with `returnResults: false`, the intent is unambiguous: no results
2291
+ * are returned and the operation is wrapped in a transaction.
2292
+ *
2293
+ * `validFrom` defaults to the operation's creation timestamp when omitted.
2530
2294
  */
2531
- toSQL(): Readonly<{
2532
- sql: string;
2533
- params: readonly unknown[];
2534
- }>;
2295
+ bulkInsert: (items: readonly Readonly<{
2296
+ props: z.input<N["schema"]>;
2297
+ id?: string;
2298
+ validFrom?: string;
2299
+ validTo?: string;
2300
+ }>[]) => Promise<void>;
2535
2301
  /**
2536
- * Compiles the query to TypeGraph's database-independent SQL fragment.
2302
+ * Delete multiple nodes by ID.
2537
2303
  *
2538
- * Pass the result to a GraphBackend, or use toSQL() to render SQL text and
2539
- * parameters for the configured dialect.
2304
+ * Atomic when the backend supports transactions. Silently ignores IDs
2305
+ * that don't exist.
2540
2306
  */
2541
- compile(): CompiledSelectSql;
2307
+ bulkDelete: (ids: readonly NodeId<N>[]) => Promise<void>;
2542
2308
  /**
2543
- * Creates a prepared (pre-validated) query that can be executed multiple
2544
- * times with different parameter bindings. Builds and structurally
2545
- * validates the AST once (a malformed query fails fast, here, instead of on
2546
- * first use); the prepared query then compiles once into a reusable template
2547
- * and fills a fresh read instant per execute() — see PreparedQuery's class
2548
- * doc comment, which also covers the two cases that recompile per call
2549
- * instead (no `executeRaw`, or a statement whose semantics ride on the SQL
2550
- * object rather than its text).
2551
- *
2552
- * Use `param("name")` in predicates to create parameterized slots,
2553
- * then pass values via `prepared.execute({ name: "value" })`.
2554
- *
2555
- * @example
2556
- * ```typescript
2557
- * import { param } from "@nicia-ai/typegraph";
2558
- *
2559
- * const prepared = store.query()
2560
- * .from("Person", "p")
2561
- * .whereNode("p", (p) => p.name.eq(param("name")))
2562
- * .select((ctx) => ctx.p)
2563
- * .prepare();
2309
+ * Find a node by uniqueness constraint.
2564
2310
  *
2565
- * const alice = await prepared.execute({ name: "Alice" });
2566
- * const bob = await prepared.execute({ name: "Bob" });
2567
- * ```
2311
+ * Looks up a live node by the named constraint key computed from `props`.
2312
+ * Returns the node if found, or undefined. Soft-deleted nodes are excluded.
2568
2313
  *
2569
- * @throws Error if no backend is configured
2314
+ * @param constraintName - Name of the uniqueness constraint to match on
2315
+ * @param props - Properties to compute the constraint key from
2570
2316
  */
2571
- prepare(): PreparedQuery<R>;
2317
+ findByConstraint: (constraintName: CN, props: z.input<N["schema"]>) => Promise<Node<N> | undefined>;
2572
2318
  /**
2573
- * Executes the query and returns typed results.
2574
- *
2575
- * Uses smart optimization to detect when only specific fields are accessed
2576
- * in the select callback. If the callback only accesses simple field
2577
- * references (no method calls or computations), generates optimized SQL
2578
- * that only extracts those fields instead of the full props blob.
2319
+ * Batch version of findByConstraint.
2579
2320
  *
2580
- * @throws Error if no backend is configured
2321
+ * Results are returned in the same order as the input items.
2322
+ * Returns undefined for entries that don't match.
2581
2323
  */
2582
- execute(): Promise<readonly R[]>;
2324
+ bulkFindByConstraint: (constraintName: CN, items: readonly Readonly<{
2325
+ props: z.input<N["schema"]>;
2326
+ }>[]) => Promise<(Node<N> | undefined)[]>;
2583
2327
  /**
2584
- * Executes the query against a provided backend.
2328
+ * Batched candidate retrieval against a declared node index.
2585
2329
  *
2586
- * Used by `store.batch()` to run several queries in sequence against one
2587
- * target — a transaction on backends that have them, the backend itself
2588
- * otherwise. The full compile → execute → transform pipeline runs
2589
- * identically to `execute()`, but against the given backend.
2330
+ * For each input item, TypeGraph computes the index lookup key from
2331
+ * `index.fields` (JSON-pointer extraction, partial-`where` applied to
2332
+ * stored rows, null-safe matching) and returns the live, non-soft-deleted
2333
+ * nodes that share that key. Unlike {@link bulkFindByConstraint}, the index
2334
+ * may be non-unique, so each input yields a (possibly empty) array.
2590
2335
  *
2591
- * Costs one statement, or two when the selective-field path runs and its
2592
- * mapping then falls back: `#tryOptimizedExecutionOn` detects that only
2593
- * after its statement has executed, and the caller re-runs the full fetch.
2594
- * The fallback clears the fast path for this instance.
2336
+ * Results preserve input order; each inner array is ordered by node id.
2337
+ * Empty input returns `[]`. An unknown index name throws
2338
+ * `NodeIndexNotFoundError`; a type-incompatible indexed field throws
2339
+ * `ValidationError`. This is candidate retrieval, not a uniqueness or
2340
+ * identity guarantee.
2341
+ *
2342
+ * @param indexName - Name of the declared node index to match on
2343
+ * @param items - Records whose `props` supply the indexed-field values
2344
+ * @param options - Optional `limitPerInput` to bound per-input fan-out
2595
2345
  */
2596
- executeOn(backend: GraphBackend | TransactionBackend): Promise<readonly R[]>;
2346
+ bulkFindByIndex: (indexName: string, items: readonly Readonly<{
2347
+ props: Partial<z.input<N["schema"]>>;
2348
+ }>[], options?: NodeBulkFindByIndexOptions) => Promise<readonly Node<N>[][]>;
2597
2349
  /**
2598
- * Executes a paginated query using cursor-based keyset pagination.
2350
+ * Get an existing node by uniqueness constraint, or create a new one.
2599
2351
  *
2600
- * Cursor pagination is efficient for large datasets as it avoids OFFSET.
2601
- * Requires ORDER BY to be specified for deterministic results.
2352
+ * Looks up a node by the named constraint key computed from `props`.
2353
+ * If found, returns it (optionally updating with `ifExists: "update"`).
2354
+ * If not found, creates a new node. Soft-deleted matches are always resurrected.
2602
2355
  *
2603
- * @param options - Pagination options (first/after for forward, last/before for backward)
2604
- * @throws ValidationError if ORDER BY is not specified
2605
- * @throws ValidationError if cursor columns don't match query ORDER BY columns
2356
+ * @param constraintName - Name of the uniqueness constraint to match on
2357
+ * @param props - Full properties for create, or merge source for update
2358
+ * @param options - Existing record behavior (default: "return")
2606
2359
  */
2607
- paginate(options: PaginateOptions): Promise<PaginatedResult<R>>;
2360
+ getOrCreateByConstraint: (constraintName: CN, props: z.input<N["schema"]>, options?: NodeGetOrCreateByConstraintOptions) => Promise<NodeGetOrCreateByConstraintResult<N>>;
2608
2361
  /**
2609
- * Returns an async iterator that streams results in batches.
2610
- *
2611
- * Uses cursor pagination internally for efficient memory usage.
2612
- * Requires ORDER BY to be specified for deterministic results.
2362
+ * Batch version of getOrCreateByConstraint.
2613
2363
  *
2614
- * @param options - Stream options (batchSize defaults to 1000)
2615
- * @throws ValidationError if ORDER BY is not specified
2364
+ * Results are returned in the same order as the input items.
2365
+ * Atomic when the backend supports transactions.
2616
2366
  */
2617
- stream(options?: StreamOptions): AsyncIterable<R>;
2618
- }
2619
-
2620
- /**
2621
- * TraversalBuilder - Intermediate builder for edge traversals.
2622
- */
2623
-
2367
+ bulkGetOrCreateByConstraint: (constraintName: CN, items: readonly Readonly<{
2368
+ props: z.input<N["schema"]>;
2369
+ }>[], options?: NodeGetOrCreateByConstraintOptions) => Promise<NodeGetOrCreateByConstraintResult<N>[]>;
2370
+ }>;
2624
2371
  /**
2625
- * Resolves the edge type for an edge alias based on its kind type parameter.
2372
+ * Reference to a node of a specific kind.
2626
2373
  *
2627
- * For `traverse(...)`, `EK` is a literal like `"authoredBy"` and the
2628
- * type comes from the typed graph. For `traverseDynamic(...)`, `EK` is
2629
- * widened to `string` — `string extends EK` distinguishes that case and
2630
- * resolves to `DynamicEdgeType`, so the conditional accessor types fire.
2374
+ * Accepts either:
2375
+ * - A Node instance of the correct kind
2376
+ * - An explicit { kind, id } object with the correct kind name
2377
+ *
2378
+ * This provides compile-time checking that edge endpoints match the
2379
+ * allowed node kinds defined in the edge registration.
2631
2380
  */
2632
- type EdgeTypeForKey<G extends GraphDef, EK> = string extends EK ? DynamicEdgeType : EK extends keyof G["edges"] & string ? G["edges"][EK]["type"] : DynamicEdgeType;
2381
+ type NodeRef<N extends NodeType = NodeType> = Node<N> | Readonly<{
2382
+ kind: N["kind"];
2383
+ id: string;
2384
+ }>;
2633
2385
  /**
2634
- * State for variable-length traversal configuration.
2386
+ * Options for creating an edge.
2635
2387
  */
2636
- interface VariableLengthState {
2637
- enabled: boolean;
2638
- minDepth: number;
2639
- maxDepth: number;
2640
- cyclePolicy: RecursiveCyclePolicy;
2641
- pathEnabled: boolean;
2642
- pathAlias?: string;
2643
- depthEnabled: boolean;
2644
- depthAlias?: string;
2645
- }
2388
+ type EdgeCreateOptions = Readonly<{
2389
+ id?: string;
2390
+ validFrom?: string;
2391
+ validTo?: string;
2392
+ }>;
2646
2393
  /**
2647
- * Intermediate builder for traversal operations.
2394
+ * Arguments for edge creation, with props optional when schema allows empty object.
2648
2395
  *
2649
- * Type parameters track the edge kind and direction to constrain
2650
- * which node kinds are valid targets in the `to()` method.
2396
+ * Uses `{}` to check if an empty object literal satisfies the schema input type.
2651
2397
  */
2652
- declare class TraversalBuilder<G extends GraphDef, Aliases extends AliasMap, EdgeAliases extends EdgeAliasMap = EmptyEdgeAliasMap, EK extends keyof G["edges"] & string = keyof G["edges"] & string, EA extends string = string, Dir extends TraversalDirection$1 = "out", Optional extends boolean = false, DC extends boolean | string = false, PC extends boolean | string = false, RecAliases extends RecursiveAliasMap = EmptyRecursiveAliasMap, CoordinateState extends QueryCoordinateState = "open"> {
2653
- #private;
2654
- constructor(config: QueryBuilderConfig, state: QueryBuilderState, edgeKinds: readonly string[], edgeAlias: EA, direction: Dir, fromAlias: string, inverseEdgeKinds?: readonly string[], optional?: Optional, variableLength?: VariableLengthState, pendingEdgePredicates?: readonly NodePredicate[]);
2398
+ type EdgeCreateArguments<E extends AnyEdgeType> = {} extends z.input<E["schema"]> ? [
2399
+ props?: z.input<E["schema"]>,
2400
+ options?: EdgeCreateOptions
2401
+ ] : [props: z.input<E["schema"]>, options?: EdgeCreateOptions];
2402
+ /**
2403
+ * A collection of edges of a specific type.
2404
+ *
2405
+ * Provides ergonomic CRUD operations for a single edge type.
2406
+ * The From and To type parameters enforce that edge endpoints
2407
+ * match the allowed node types at compile time.
2408
+ *
2409
+ * @example
2410
+ * ```typescript
2411
+ * // Create an edge - pass Node objects directly
2412
+ * const edge = await store.edges.worksAt.create(alice, acme, { role: "Engineer" });
2413
+ *
2414
+ * // TypeScript error: Company is not a valid 'from' type for worksAt
2415
+ * // store.edges.worksAt.create(acme, alice, { role: "Engineer" });
2416
+ *
2417
+ * // For edges with empty schemas, props is optional
2418
+ * await store.edges.wrote.create(author, book);
2419
+ *
2420
+ * // Find edges from a node
2421
+ * const edges = await store.edges.worksAt.findFrom(alice);
2422
+ * ```
2423
+ */
2424
+ type EdgeCollection<E extends AnyEdgeType, From extends NodeType = NodeType, To extends NodeType = NodeType> = Readonly<{
2655
2425
  /**
2656
- * Enables variable-length (recursive) traversal.
2657
- * Defaults to MAX_RECURSIVE_DEPTH (10) hops with cycle prevention.
2658
- * Use `maxHops` to override (up to MAX_EXPLICIT_RECURSIVE_DEPTH).
2426
+ * Create a new edge.
2427
+ *
2428
+ * `validFrom` defaults to the operation's creation timestamp when omitted.
2429
+ *
2430
+ * @param from - Source node (must be one of the allowed 'from' types)
2431
+ * @param to - Target node (must be one of the allowed 'to' types)
2432
+ * @param args - Edge properties (optional if schema is empty) and creation options
2659
2433
  */
2660
- recursive<const O extends RecursiveTraversalOptions = Record<string, never>>(options?: O): TraversalBuilder<G, Aliases, EdgeAliases, EK, EA, Dir, Optional, O extends {
2661
- depth: infer D extends boolean | string;
2662
- } ? D : DC, O extends {
2663
- path: infer P extends boolean | string;
2664
- } ? P : PC, RecAliases, CoordinateState>;
2434
+ create: (from: NodeRef<From>, to: NodeRef<To>, ...args: EdgeCreateArguments<E>) => Promise<Edge<E, From, To>>;
2435
+ /** Get an edge by ID */
2436
+ getById: (id: EdgeId<E>, options?: QueryOptions) => Promise<Edge<E, From, To> | undefined>;
2437
+ /** Get multiple edges by ID, preserving input order (undefined for missing) */
2438
+ getByIds: (ids: readonly EdgeId<E>[], options?: QueryOptions) => Promise<readonly (Edge<E, From, To> | undefined)[]>;
2439
+ /** Update an edge's properties */
2440
+ update: (id: EdgeId<E>, props: Partial<z.input<E["schema"]>>, options?: Readonly<{
2441
+ validTo?: string;
2442
+ }>) => Promise<Edge<E, From, To>>;
2665
2443
  /**
2666
- * Adds a WHERE clause for the edge being traversed.
2444
+ * Find edges from a specific node.
2667
2445
  *
2668
- * @param alias - The edge alias to filter on (must be the current edge alias)
2669
- * @param predicateFunction - A function that builds predicates using the edge accessor
2446
+ * Honors the same temporal model as `getById` / `find`: with no
2447
+ * `options`, the graph's default `temporalMode` applies (excluding
2448
+ * soft-deleted edges and, in `current` / `asOf` modes, edges outside
2449
+ * their validity window). Pass `temporalMode` / `asOf` to read the
2450
+ * endpoint's edges at another temporal coordinate.
2670
2451
  */
2671
- whereEdge(alias: EA, predicateFunction: (edge: EdgeAccessor<EdgeTypeForKey<G, EK>>) => Predicate): TraversalBuilder<G, Aliases, EdgeAliases, EK, EA, Dir, Optional, DC, PC, RecAliases, CoordinateState>;
2452
+ findFrom: (from: NodeRef<From>, options?: QueryOptions) => Promise<Edge<E, From, To>[]>;
2672
2453
  /**
2673
- * Specifies the target node kind.
2674
- *
2675
- * The kind must be a valid target for this edge based on the traversal direction:
2676
- * - "out" direction: kind must be in the edge's "to" array
2677
- * - "in" direction: kind must be in the edge's "from" array
2454
+ * Find edges to a specific node.
2678
2455
  *
2679
- * @param kind - The target node kind
2680
- * @param alias - A unique alias for this node (compile-time error if duplicate)
2456
+ * Temporal semantics mirror {@link EdgeCollection.findFrom}.
2681
2457
  */
2682
- to<K extends ValidEdgeTargets<G, EK, Dir>, A extends string>(kind: K, alias: UniqueAlias<A, Aliases>, options?: {
2683
- includeSubClasses?: false;
2684
- }): QueryBuilder<G, Aliases & Record<A, NodeAlias<G["nodes"][K]["type"], Optional>>, EdgeAliases & Record<EA, EdgeAlias<G["edges"][EK]["type"], Optional>>, RecAliases & BuildRecursiveAliases<DC, PC, A>, CoordinateState>;
2685
- to<K extends ValidEdgeTargets<G, EK, Dir>, A extends string>(kind: K, alias: UniqueAlias<A, Aliases>, options: {
2686
- includeSubClasses: true;
2687
- }): QueryBuilder<G, Aliases & Record<A, NodeAlias<NodeType, Optional>>, EdgeAliases & Record<EA, EdgeAlias<G["edges"][EK]["type"], Optional>>, RecAliases & BuildRecursiveAliases<DC, PC, A>, CoordinateState>;
2458
+ findTo: (to: NodeRef<To>, options?: QueryOptions) => Promise<Edge<E, From, To>[]>;
2688
2459
  /**
2689
- * String-keyed sibling of `to` for runtime-declared target kinds.
2690
- * Throws `KindNotFoundError` if the kind is not registered.
2460
+ * Find the edges from a SET of source nodes in one read.
2461
+ *
2462
+ * `findFrom` with a widened endpoint predicate and nothing else: the same
2463
+ * temporal model, the same soft-delete filtering, and the same per-source
2464
+ * ordering — but `from_id IN (...)` instead of `from_id = ?`, so a page of
2465
+ * N sources costs one statement per distinct source kind and bind-budget
2466
+ * chunk rather than N singleton statements.
2467
+ *
2468
+ * Results are grouped per input: the outer array is parallel to `froms`
2469
+ * (index `i` holds the edges of `froms[i]`), and repeated inputs each get
2470
+ * their own copy of the same edge set. Empty input returns `[]`; a source
2471
+ * with no edges gets an empty array. Large inputs are split across
2472
+ * statements to respect the backend's bound-parameter budget, which is
2473
+ * invisible in the result.
2474
+ *
2475
+ * Requires a backend implementing `findEdgesByEndpointSet` — both bundled
2476
+ * Drizzle backends do. On one that does not, this **refuses** with a
2477
+ * `ConfigurationError` rather than looping `findFrom` per input: asking for
2478
+ * a bulk read is asking for set-oriented statements, and silently issuing N
2479
+ * singleton statements is the cost surprise the method exists to remove.
2480
+ *
2481
+ * @param froms - Source nodes to read the edges of
2482
+ * @param options - Temporal coordinate plus optional `limitPerInput`
2483
+ * @throws {ConfigurationError} when the backend cannot read an endpoint set
2691
2484
  */
2692
- toDynamic<A extends string>(kind: string, alias: UniqueAlias<A, Aliases>, options?: {
2693
- includeSubClasses?: boolean;
2694
- }): QueryBuilder<G, Aliases & Record<A, NodeAlias<DynamicNodeType, Optional>>, EdgeAliases & Record<EA, EdgeAlias<EdgeTypeForKey<G, EK>, Optional>>, RecAliases & BuildRecursiveAliases<DC, PC, A>, CoordinateState>;
2695
- }
2696
-
2697
- /**
2698
- * QueryBuilder - The fluent query builder.
2699
- */
2700
-
2701
- type TemporalMethod<G extends GraphDef, Aliases extends AliasMap, EdgeAliases extends EdgeAliasMap, RecursiveAliases extends RecursiveAliasMap, CoordinateState extends QueryCoordinateState> = CoordinateState extends "open" ? (mode: TemporalMode, asOf?: string) => QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases, "open"> : never;
2702
- /**
2703
- * The fluent query builder.
2704
- *
2705
- * Type parameters accumulate as methods are chained:
2706
- * - G: The graph definition
2707
- * - Aliases: Map of alias names to their node kinds
2708
- * - EdgeAliases: Map of alias names to their edge kinds (accumulated during traversals)
2709
- */
2710
- declare class QueryBuilder<G extends GraphDef, Aliases extends AliasMap = EmptyAliasMap, EdgeAliases extends EdgeAliasMap = EmptyEdgeAliasMap, RecursiveAliases extends RecursiveAliasMap = EmptyRecursiveAliasMap, CoordinateState extends QueryCoordinateState = "open"> {
2711
- #private;
2712
- readonly temporal: TemporalMethod<G, Aliases, EdgeAliases, RecursiveAliases, CoordinateState>;
2713
- constructor(config: QueryBuilderConfig, state: QueryBuilderState);
2485
+ bulkFindFrom: (froms: readonly NodeRef<From>[], options?: EdgeBulkFindEndpointOptions) => Promise<readonly Edge<E, From, To>[][]>;
2714
2486
  /**
2715
- * Starts a query from a node kind.
2487
+ * Find the edges into a SET of target nodes in one read.
2716
2488
  *
2717
- * @param kind - The node kind to start from
2718
- * @param alias - A unique alias for this node (compile-time error if duplicate)
2489
+ * Mirrors {@link EdgeCollection.bulkFindFrom} on the `to` endpoint.
2719
2490
  */
2720
- from<K extends keyof G["nodes"] & string, A extends string>(kind: K, alias: UniqueAlias<A, Aliases>, options?: {
2721
- includeSubClasses?: false;
2722
- }): QueryBuilder<G, Aliases & Record<A, NodeAlias<G["nodes"][K]["type"]>>, EdgeAliases, RecursiveAliases, CoordinateState>;
2723
- from<K extends keyof G["nodes"] & string, A extends string>(kind: K, alias: UniqueAlias<A, Aliases>, options: {
2724
- includeSubClasses: true;
2725
- }): QueryBuilder<G, Aliases & Record<A, NodeAlias>, EdgeAliases, RecursiveAliases, CoordinateState>;
2491
+ bulkFindTo: (tos: readonly NodeRef<To>[], options?: EdgeBulkFindEndpointOptions) => Promise<readonly Edge<E, From, To>[][]>;
2726
2492
  /**
2727
- * String-keyed sibling of `from` for runtime-declared kinds. Throws
2728
- * `KindNotFoundError` if the kind is not registered. Predicates use
2729
- * the `n.field("name").number().gte(...)` discriminator.
2493
+ * Deferred variant of `findFrom` for use with `store.batch()`.
2494
+ *
2495
+ * Returns a `BatchableQuery` instead of executing immediately. Accepts
2496
+ * the same temporal `options` as {@link EdgeCollection.findFrom}.
2497
+ *
2498
+ * Batching these still issues one statement per call — it does not merge
2499
+ * the reads, and whether they share a connection is up to the adapter. It
2500
+ * is not a snapshot: PostgreSQL's default read-committed isolation lets a
2501
+ * later read observe a commit the earlier ones did not. To read edges for
2502
+ * many sources in one statement, traverse from them in a single query.
2730
2503
  */
2731
- fromDynamic<A extends string>(kind: string, alias: UniqueAlias<A, Aliases>, options?: {
2732
- includeSubClasses?: boolean;
2733
- }): QueryBuilder<G, Aliases & Record<A, NodeAlias<DynamicNodeType>>, EdgeAliases, RecursiveAliases, CoordinateState>;
2504
+ batchFindFrom: (from: NodeRef<From>, options?: QueryOptions) => BatchableQuery<Edge<E, From, To>>;
2734
2505
  /**
2735
- * Adds a WHERE clause for a node.
2506
+ * Deferred variant of `findTo` for use with `store.batch()`.
2507
+ *
2508
+ * Returns a `BatchableQuery` instead of executing immediately. Accepts
2509
+ * the same temporal `options` as {@link EdgeCollection.findTo}. Costs one
2510
+ * statement per call, like {@link EdgeCollection.batchFindFrom}.
2736
2511
  */
2737
- whereNode<A extends keyof Aliases & string>(alias: A, predicateFunction: (n: NodeAccessor<Aliases[A]["type"]>) => Predicate): QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases, CoordinateState>;
2512
+ batchFindTo: (to: NodeRef<To>, options?: QueryOptions) => BatchableQuery<Edge<E, From, To>>;
2738
2513
  /**
2739
- * Adds a WHERE clause for an edge.
2514
+ * Deferred variant of `findByEndpoints` for use with `store.batch()`.
2740
2515
  *
2741
- * @param alias - The edge alias to filter on
2742
- * @param predicateFunction - A function that builds predicates using the edge accessor
2516
+ * Returns a `BatchableQuery` that yields a 0-or-1 element array
2517
+ * (matching `findByEndpoints`' at-most-one semantics). Costs one statement
2518
+ * per call, like {@link EdgeCollection.batchFindFrom}.
2743
2519
  */
2744
- whereEdge<EA extends keyof EdgeAliases & string>(alias: EA, predicateFunction: (edge: EdgeAccessor<EdgeAliases[EA]["type"]>) => Predicate): QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases, CoordinateState>;
2520
+ batchFindByEndpoints: (from: NodeRef<From>, to: NodeRef<To>, options?: EdgeFindByEndpointsOptions<E>, temporal?: QueryOptions) => BatchableQuery<Edge<E, From, To>>;
2521
+ /** Delete an edge (soft delete - sets deletedAt timestamp) */
2522
+ delete: (id: EdgeId<E>) => Promise<void>;
2745
2523
  /**
2746
- * Traverses an edge to another node (outgoing direction).
2524
+ * Permanently delete an edge from the database.
2747
2525
  *
2748
- * By default, traverses from the current node (last traversal target, or start node).
2749
- * Use the `from` option to traverse from a different alias (fan-out pattern).
2526
+ * Unlike `delete()` which performs a soft delete, this permanently
2527
+ * removes the edge record.
2750
2528
  *
2751
- * @param options.expand - Ontology expansion mode for implying/inverse edges
2752
- * @param options.from - Alias to traverse from (defaults to current/last traversal target)
2529
+ * **Warning:** This operation is irreversible and should be used carefully.
2530
+ * Consider using soft delete (`delete()`) for most use cases.
2753
2531
  */
2754
- traverse<EK extends keyof G["edges"] & string, EA extends string>(edgeKind: EK, edgeAlias: EA, options?: {
2755
- direction?: "out";
2756
- expand?: TraversalExpansion;
2757
- from?: keyof Aliases & string;
2758
- }): TraversalBuilder<G, Aliases, EdgeAliases, EK, EA, "out", false, false, false, RecursiveAliases, CoordinateState>;
2532
+ hardDelete: (id: EdgeId<E>) => Promise<void>;
2533
+ /** Find edges matching endpoint and pagination criteria */
2534
+ find: (filter?: Readonly<{
2535
+ from?: NodeRef<From>;
2536
+ to?: NodeRef<To>;
2537
+ limit?: number;
2538
+ offset?: number;
2539
+ }>, temporal?: QueryOptions) => Promise<Edge<E, From, To>[]>;
2540
+ /** Count edges matching criteria */
2541
+ count: (filter?: Readonly<{
2542
+ from?: NodeRef<From>;
2543
+ to?: NodeRef<To>;
2544
+ }>, temporal?: QueryOptions) => Promise<number>;
2759
2545
  /**
2760
- * Traverses an edge to another node (incoming direction).
2546
+ * Create multiple edges in a batch.
2761
2547
  *
2762
- * By default, traverses from the current node (last traversal target, or start node).
2763
- * Use the `from` option to traverse from a different alias (fan-out pattern).
2548
+ * More efficient than calling create() multiple times.
2549
+ * Use `bulkInsert` for the dedicated fast path that skips returning results.
2764
2550
  *
2765
- * @param options.direction - Set to "in" for incoming edge traversal
2766
- * @param options.expand - Ontology expansion mode for implying/inverse edges
2767
- * @param options.from - Alias to traverse from (defaults to current/last traversal target)
2551
+ * `validFrom` defaults to the operation's creation timestamp when omitted.
2768
2552
  */
2769
- traverse<EK extends keyof G["edges"] & string, EA extends string>(edgeKind: EK, edgeAlias: EA, options: {
2770
- direction: "in";
2771
- expand?: TraversalExpansion;
2772
- from?: keyof Aliases & string;
2773
- }): TraversalBuilder<G, Aliases, EdgeAliases, EK, EA, "in", false, false, false, RecursiveAliases, CoordinateState>;
2553
+ bulkCreate: (items: readonly Readonly<{
2554
+ from: NodeRef<From>;
2555
+ to: NodeRef<To>;
2556
+ props?: z.input<E["schema"]>;
2557
+ id?: string;
2558
+ validFrom?: string;
2559
+ validTo?: string;
2560
+ }>[]) => Promise<Edge<E, From, To>[]>;
2774
2561
  /**
2775
- * String-keyed sibling of `traverse` for runtime-declared edge kinds.
2776
- * Throws `KindNotFoundError` if the edge kind is not registered.
2562
+ * Create or update multiple edges in a batch.
2563
+ *
2564
+ * For each item, if an edge with the given ID exists, updates it.
2565
+ * Otherwise, creates a new edge with that ID.
2566
+ *
2567
+ * `validFrom` only applies on the create branch, defaulting to the
2568
+ * operation's creation timestamp when omitted; it has no effect on update.
2777
2569
  */
2778
- traverseDynamic<EA extends string>(edgeKind: string, edgeAlias: EA, options?: {
2779
- direction?: TraversalDirection$1;
2780
- expand?: TraversalExpansion;
2781
- from?: keyof Aliases & string;
2782
- }): TraversalBuilder<G, Aliases, EdgeAliases & Record<EA, EdgeAlias<DynamicEdgeType>>, string, EA, TraversalDirection$1, false, false, false, RecursiveAliases, CoordinateState>;
2570
+ bulkUpsertById: (items: readonly Readonly<{
2571
+ id: EdgeId<E>;
2572
+ from: NodeRef<From>;
2573
+ to: NodeRef<To>;
2574
+ props?: z.input<E["schema"]>;
2575
+ validFrom?: string;
2576
+ validTo?: string;
2577
+ }>[]) => Promise<Edge<E, From, To>[]>;
2783
2578
  /**
2784
- * Optionally traverses an edge to another node (LEFT JOIN semantics).
2785
- * If no matching edge/node exists, the result will include null values.
2579
+ * Insert multiple edges without returning results.
2786
2580
  *
2787
- * By default, traverses from the current node (last traversal target, or start node).
2788
- * Use the `from` option to traverse from a different alias (fan-out pattern).
2581
+ * This is the dedicated fast path for bulk inserts. Unlike `bulkCreate`
2582
+ * with `returnResults: false`, the intent is unambiguous: no results
2583
+ * are returned and the operation is wrapped in a transaction.
2789
2584
  *
2790
- * @param options.direction - Direction of traversal: "out" (default) or "in"
2791
- * @param options.expand - Ontology expansion mode for implying/inverse edges
2792
- * @param options.from - Alias to traverse from (defaults to current/last traversal target)
2793
- */
2794
- optionalTraverse<EK extends keyof G["edges"] & string, EA extends string>(edgeKind: EK, edgeAlias: EA, options?: {
2795
- direction?: "out";
2796
- expand?: TraversalExpansion;
2797
- from?: keyof Aliases & string;
2798
- }): TraversalBuilder<G, Aliases, EdgeAliases, EK, EA, "out", true, false, false, RecursiveAliases, CoordinateState>;
2799
- optionalTraverse<EK extends keyof G["edges"] & string, EA extends string>(edgeKind: EK, edgeAlias: EA, options: {
2800
- direction: "in";
2801
- expand?: TraversalExpansion;
2802
- from?: keyof Aliases & string;
2803
- }): TraversalBuilder<G, Aliases, EdgeAliases, EK, EA, "in", true, false, false, RecursiveAliases, CoordinateState>;
2804
- /**
2805
- * String-keyed sibling of `optionalTraverse` for runtime-declared edge
2806
- * kinds. LEFT JOIN semantics — non-matching rows produce a null edge
2807
- * alias instead of dropping. Throws `KindNotFoundError` if the edge
2808
- * kind is not registered.
2809
- */
2810
- optionalTraverseDynamic<EA extends string>(edgeKind: string, edgeAlias: EA, options?: {
2811
- direction?: TraversalDirection$1;
2812
- expand?: TraversalExpansion;
2813
- from?: keyof Aliases & string;
2814
- }): TraversalBuilder<G, Aliases, EdgeAliases & Record<EA, EdgeAlias<DynamicEdgeType, true>>, string, EA, TraversalDirection$1, true, false, false, RecursiveAliases, CoordinateState>;
2815
- /**
2816
- * Selects fields to return.
2585
+ * `validFrom` defaults to the operation's creation timestamp when omitted.
2817
2586
  */
2818
- select<R>(selectFunction: (context: SelectContext<Aliases, EdgeAliases, RecursiveAliases>) => R): ExecutableQuery<G, Aliases, EdgeAliases, RecursiveAliases, R>;
2587
+ bulkInsert: (items: readonly Readonly<{
2588
+ from: NodeRef<From>;
2589
+ to: NodeRef<To>;
2590
+ props?: z.input<E["schema"]>;
2591
+ id?: string;
2592
+ validFrom?: string;
2593
+ validTo?: string;
2594
+ }>[]) => Promise<void>;
2819
2595
  /**
2820
- * Selects fields including aggregates.
2821
- * Use with groupBy() for aggregate queries.
2596
+ * Delete multiple edges by ID.
2822
2597
  *
2823
- * @param fields - Object mapping output names to field refs or aggregate expressions
2824
- */
2825
- aggregate<R extends Record<string, FieldRef | AggregateExpr>>(fields: R): ExecutableAggregateQuery<G, Aliases, R>;
2826
- /**
2827
- * Orders results.
2828
- */
2829
- orderBy<A extends (keyof Aliases | keyof EdgeAliases) & string>(alias: A, field: string, direction?: SortDirection): QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases, CoordinateState>;
2830
- /**
2831
- * Limits the number of results.
2832
- */
2833
- limit(n: number): QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases, CoordinateState>;
2834
- /**
2835
- * Offsets the results.
2598
+ * Atomic when the backend supports transactions. Silently ignores IDs
2599
+ * that don't exist.
2836
2600
  */
2837
- offset(n: number): QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases, CoordinateState>;
2601
+ bulkDelete: (ids: readonly EdgeId<E>[]) => Promise<void>;
2838
2602
  /**
2839
- * Groups results by the specified field.
2840
- * Use with aggregate functions like COUNT, SUM, AVG in select().
2603
+ * Find an edge by endpoints and optional property fields.
2841
2604
  *
2842
- * @param alias - The node alias to group by
2843
- * @param field - The field name to group by
2844
- */
2845
- groupBy<A extends keyof Aliases & string>(alias: A, field: string): QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases, CoordinateState>;
2846
- /**
2847
- * Groups results by the node ID.
2848
- * Use when you want to group by a complete node rather than a specific field.
2605
+ * Returns the first matching edge at the read coordinate, or undefined.
2606
+ * Honors the temporal model like `findFrom` / `findTo`: by default
2607
+ * (`current` mode) soft-deleted and out-of-window edges are excluded; under
2608
+ * `includeTombstones` a soft-deleted edge can be returned.
2849
2609
  *
2850
- * @param alias - The node alias to group by (uses the node's ID)
2610
+ * @param from - Source node
2611
+ * @param to - Target node
2612
+ * @param options - Match criteria (matchOn fields and property values)
2613
+ * @param temporal - Temporal coordinate. With no `temporal`, the graph's
2614
+ * default `temporalMode` applies (so under the default `"current"` mode,
2615
+ * edges outside their validity window are excluded). Pass
2616
+ * `temporalMode` / `asOf` to read the edge as of another coordinate.
2851
2617
  */
2852
- groupByNode<A extends keyof Aliases & string>(alias: A): QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases, CoordinateState>;
2618
+ findByEndpoints: (from: NodeRef<From>, to: NodeRef<To>, options?: EdgeFindByEndpointsOptions<E>, temporal?: QueryOptions) => Promise<Edge<E, From, To> | undefined>;
2853
2619
  /**
2854
- * Filters grouped results using aggregate conditions (HAVING clause).
2855
- * Use after groupBy() to filter based on aggregate values.
2620
+ * Get an existing edge by endpoints and optional property fields, or create a new one.
2856
2621
  *
2857
- * @param predicate - A predicate expression to filter groups
2858
- */
2859
- having(predicate: PredicateExpression): QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases, CoordinateState>;
2860
- /**
2861
- * Sets fusion parameters for hybrid (vector + fulltext) queries.
2622
+ * Matches edges of this kind between `(from, to)`. When `matchOn` specifies
2623
+ * property fields, only edges whose properties match on those fields are considered.
2624
+ * Soft-deleted matches are resurrected when cardinality allows.
2862
2625
  *
2863
- * Applies only when the query contains both a `.similarTo()` and a
2864
- * `.$fulltext.matches()` predicate. Without this call, the default is
2865
- * RRF with k=60 and equal weights. A mismatch between `.fuseWith()`
2866
- * configuration and the predicates on the query is caught during
2867
- * compilation, not here.
2626
+ * `validFrom` only applies on the create branch, defaulting to the
2627
+ * operation's creation timestamp when omitted. `validTo` applies on create,
2628
+ * update, and resurrection, but not when an existing edge is returned
2629
+ * without a write.
2868
2630
  *
2869
- * @example
2870
- * ```typescript
2871
- * store.query()
2872
- * .from("Document", "d")
2873
- * .whereNode("d", d =>
2874
- * d.$fulltext.matches("renewable energy", 50)
2875
- * .and(d.embedding.similarTo(vec, 50))
2876
- * .and(d.tenantId.eq(tenant))
2877
- * )
2878
- * .fuseWith({ k: 60, weights: { fulltext: 1.5 } })
2879
- * .limit(10)
2880
- * .execute();
2881
- * ```
2631
+ * @param from - Source node
2632
+ * @param to - Target node
2633
+ * @param props - Full properties for create, or merge source for update
2634
+ * @param options - Match criteria and conflict resolution
2882
2635
  */
2883
- fuseWith(options: HybridFusionOptions): QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases, CoordinateState>;
2636
+ getOrCreateByEndpoints: (from: NodeRef<From>, to: NodeRef<To>, props: z.input<E["schema"]>, options?: EdgeGetOrCreateByEndpointsOptions<E>) => Promise<EdgeGetOrCreateByEndpointsResult<E, From, To>>;
2884
2637
  /**
2885
- * Applies a query fragment to transform this builder.
2886
- *
2887
- * Fragments are reusable query transformations that can add predicates,
2888
- * traversals, ordering, and other query operations. Use this for
2889
- * composing complex queries from simpler, reusable parts.
2890
- *
2891
- * @example
2892
- * ```typescript
2893
- * // Define a reusable fragment
2894
- * const activeUsers = createFragment<MyGraph>()((q) =>
2895
- * q.whereNode("u", ({ status }) => status.eq("active"))
2896
- * );
2897
- *
2898
- * // Apply the fragment
2899
- * const results = await query()
2900
- * .from("User", "u")
2901
- * .pipe(activeUsers)
2902
- * .select((ctx) => ctx.u)
2903
- * .execute();
2904
- * ```
2638
+ * Batch version of getOrCreateByEndpoints.
2905
2639
  *
2906
- * @param fragment - A function that transforms the builder
2907
- * @returns The transformed builder
2640
+ * Results are returned in the same order as the input items.
2641
+ * Atomic when the backend supports transactions.
2908
2642
  */
2909
- pipe<OutAliases extends AliasMap, OutEdgeAliases extends EdgeAliasMap = EdgeAliases, OutRecAliases extends RecursiveAliasMap = RecursiveAliases>(fragment: (builder: QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases, CoordinateState>) => QueryBuilder<G, OutAliases, OutEdgeAliases, OutRecAliases, CoordinateState>): QueryBuilder<G, OutAliases, OutEdgeAliases, OutRecAliases, CoordinateState>;
2910
- }
2911
-
2643
+ bulkGetOrCreateByEndpoints: (items: readonly Readonly<{
2644
+ from: NodeRef<From>;
2645
+ to: NodeRef<To>;
2646
+ props: z.input<E["schema"]>;
2647
+ validFrom?: string;
2648
+ validTo?: string;
2649
+ }>[], options?: Pick<EdgeGetOrCreateByEndpointsOptions<E>, "matchOn" | "ifExists">) => Promise<EdgeGetOrCreateByEndpointsResult<E, From, To>[]>;
2650
+ }>;
2912
2651
  /**
2913
- * Fluent query builder for TypeGraph.
2914
- *
2915
- * Provides a type-safe, chainable API for building queries.
2916
- * Each method returns a new builder instance with expanded type information.
2652
+ * Extract uniqueness constraint names from a NodeRegistration.
2917
2653
  *
2918
- * This module re-exports from the builder submodules and provides the
2919
- * createQueryBuilder factory function.
2654
+ * - Returns `never` when no uniqueness constraints are configured.
2655
+ * - Returns a literal union when names are inferred via `defineGraph` const params.
2656
+ * - Falls back to `string` when names are widened/dynamic.
2920
2657
  */
2921
-
2922
- type InitialQueryBuilder<G extends GraphDef, CoordinateState extends QueryCoordinateState = "open"> = QueryBuilder<G, EmptyAliasMap, EmptyEdgeAliasMap, EmptyRecursiveAliasMap, CoordinateState>;
2923
- declare function createQueryBuilder<G extends GraphDef>(graphId: string, registry: KindRegistry, options?: CreateQueryBuilderOptions): InitialQueryBuilder<G>;
2658
+ type ConstraintNames<R extends NodeRegistration> = "unique" extends keyof R ? R["unique"] extends readonly {
2659
+ readonly name: infer N;
2660
+ }[] ? N & string : string : never;
2661
+ /**
2662
+ * Extract the union of 'from' node types from an EdgeRegistration.
2663
+ */
2664
+ type EdgeFromTypes<R extends EdgeRegistration> = R["from"] extends readonly (infer N)[] ? N : never;
2665
+ /**
2666
+ * Extract the union of 'to' node types from an EdgeRegistration.
2667
+ */
2668
+ type EdgeToTypes<R extends EdgeRegistration> = R["to"] extends readonly (infer N)[] ? N : never;
2669
+ /**
2670
+ * Create a type-safe EdgeCollection from an EdgeRegistration.
2671
+ * Extracts the edge type and from/to node types automatically.
2672
+ */
2673
+ type TypedEdgeCollection<R extends EdgeRegistration> = EdgeCollection<R["type"], EdgeFromTypes<R> extends NodeType ? EdgeFromTypes<R> : NodeType, EdgeToTypes<R> extends NodeType ? EdgeToTypes<R> : NodeType>;
2674
+ /** Mapped type of all node collections for a graph. */
2675
+ type GraphNodeCollections<G extends GraphDef> = {
2676
+ [K in keyof G["nodes"] & string]-?: NodeCollection<G["nodes"][K]["type"], ConstraintNames<G["nodes"][K]>>;
2677
+ };
2678
+ /** Mapped type of all edge collections for a graph. */
2679
+ type GraphEdgeCollections<G extends GraphDef> = {
2680
+ [K in keyof G["edges"] & string]-?: TypedEdgeCollection<G["edges"][K]>;
2681
+ };
2682
+ /** Temporal-aware node reads — a {@link StoreView} pins these. */
2683
+ type NodeTemporalReads<N extends NodeType, CN extends string = string> = Pick<NodeCollection<N, CN>, (typeof NODE_TEMPORAL_READ_NAMES)[number]>;
2684
+ /**
2685
+ * Current-state-only node reads (constraint / index lookups). No temporal
2686
+ * axis, so a {@link StoreView} delegates them on a `current` pin and refuses
2687
+ * them on a temporal pin.
2688
+ */
2689
+ type NodeCurrentReads<N extends NodeType, CN extends string = string> = Pick<NodeCollection<N, CN>, (typeof CURRENT_ONLY_READ_NAMES)[number]>;
2690
+ /** Node writes — never available on a read-only {@link StoreView}. */
2691
+ type NodeWrites<N extends NodeType, CN extends string = string> = Pick<NodeCollection<N, CN>, (typeof NODE_WRITE_NAMES)[number]>;
2692
+ /** Temporal-aware edge reads — a {@link StoreView} pins these. */
2693
+ type EdgeTemporalReads<E extends AnyEdgeType, From extends NodeType = NodeType, To extends NodeType = NodeType> = Pick<EdgeCollection<E, From, To>, (typeof EDGE_TEMPORAL_READ_NAMES)[number]>;
2694
+ /**
2695
+ * Deferred edge reads for `store.batch()` — absent from a {@link StoreView},
2696
+ * which has no batch context.
2697
+ */
2698
+ type EdgeBatchReads<E extends AnyEdgeType, From extends NodeType = NodeType, To extends NodeType = NodeType> = Pick<EdgeCollection<E, From, To>, (typeof EDGE_BATCH_READ_NAMES)[number]>;
2699
+ /** Edge writes — never available on a read-only {@link StoreView}. */
2700
+ type EdgeWrites<E extends AnyEdgeType, From extends NodeType = NodeType, To extends NodeType = NodeType> = Pick<EdgeCollection<E, From, To>, (typeof EDGE_WRITE_NAMES)[number]>;
2701
+ /**
2702
+ * The read-only node surface a {@link StoreView} exposes for one node kind.
2703
+ * The temporal reads drop the per-call temporal argument — the pin owns the
2704
+ * axis; the current reads ({@link NodeCurrentReads}) are exposed as-is
2705
+ * (delegated on a `current` view, refused on a temporal pin). Writes live on
2706
+ * the live `Store`. The conformance test asserts the temporal part equals the
2707
+ * pinned form of {@link NodeTemporalReads}.
2708
+ */
2709
+ type StoreViewNodeCollection<N extends NodeType, CN extends string = string> = Readonly<{
2710
+ /** Get a node by ID at the view's pinned coordinate. */
2711
+ getById: (id: NodeId<N>) => Promise<Node<N> | undefined>;
2712
+ /** Get multiple nodes by ID, preserving input order (undefined for missing). */
2713
+ getByIds: (ids: readonly NodeId<N>[]) => Promise<readonly (Node<N> | undefined)[]>;
2714
+ /** Find nodes matching criteria at the view's pinned coordinate. */
2715
+ find: (filter?: Readonly<{
2716
+ where?: (accessor: NodeAccessor<N>) => Predicate;
2717
+ limit?: number;
2718
+ offset?: number;
2719
+ }>) => Promise<Node<N>[]>;
2720
+ /** Count nodes at the view's pinned coordinate. */
2721
+ count: () => Promise<number>;
2722
+ }> & NodeCurrentReads<N, CN>;
2723
+ /**
2724
+ * The read-only edge surface a {@link StoreView} exposes for one edge
2725
+ * kind, mirroring {@link StoreViewNodeCollection}. Every edge read —
2726
+ * including `findByEndpoints` — honors the pin via the same temporal model,
2727
+ * so (unlike nodes) there are no current-state-only edge reads.
2728
+ */
2729
+ type StoreViewEdgeCollection<E extends AnyEdgeType, From extends NodeType = NodeType, To extends NodeType = NodeType> = Readonly<{
2730
+ /** Get an edge by ID at the view's pinned coordinate. */
2731
+ getById: (id: EdgeId<E>) => Promise<Edge<E, From, To> | undefined>;
2732
+ /** Get multiple edges by ID, preserving input order (undefined for missing). */
2733
+ getByIds: (ids: readonly EdgeId<E>[]) => Promise<readonly (Edge<E, From, To> | undefined)[]>;
2734
+ /** Find edges matching endpoint and pagination criteria. */
2735
+ find: (filter?: Readonly<{
2736
+ from?: NodeRef<From>;
2737
+ to?: NodeRef<To>;
2738
+ limit?: number;
2739
+ offset?: number;
2740
+ }>) => Promise<Edge<E, From, To>[]>;
2741
+ /** Count edges matching criteria at the view's pinned coordinate. */
2742
+ count: (filter?: Readonly<{
2743
+ from?: NodeRef<From>;
2744
+ to?: NodeRef<To>;
2745
+ }>) => Promise<number>;
2746
+ /** Find edges from a specific node at the view's pinned coordinate. */
2747
+ findFrom: (from: NodeRef<From>) => Promise<Edge<E, From, To>[]>;
2748
+ /** Find edges to a specific node at the view's pinned coordinate. */
2749
+ findTo: (to: NodeRef<To>) => Promise<Edge<E, From, To>[]>;
2750
+ /** Find the edges from a set of source nodes at the view's pinned coordinate. */
2751
+ bulkFindFrom: (froms: readonly NodeRef<From>[], options?: EdgeBulkFindOptions) => Promise<readonly Edge<E, From, To>[][]>;
2752
+ /** Find the edges into a set of target nodes at the view's pinned coordinate. */
2753
+ bulkFindTo: (tos: readonly NodeRef<To>[], options?: EdgeBulkFindOptions) => Promise<readonly Edge<E, From, To>[][]>;
2754
+ /** Find the edge between two endpoints at the view's pinned coordinate. */
2755
+ findByEndpoints: (from: NodeRef<From>, to: NodeRef<To>, options?: EdgeFindByEndpointsOptions<E>) => Promise<Edge<E, From, To> | undefined>;
2756
+ }>;
2757
+ /**
2758
+ * Read-only view edge collection derived from an `EdgeRegistration`,
2759
+ * extracting the edge type and from/to node types — the read-only
2760
+ * counterpart of {@link TypedEdgeCollection}.
2761
+ */
2762
+ type TypedStoreViewEdgeCollection<R extends EdgeRegistration> = StoreViewEdgeCollection<R["type"], EdgeFromTypes<R> extends NodeType ? EdgeFromTypes<R> : NodeType, EdgeToTypes<R> extends NodeType ? EdgeToTypes<R> : NodeType>;
2763
+ /** Mapped type of all read-only view node collections for a graph. */
2764
+ type StoreViewNodeCollections<G extends GraphDef> = {
2765
+ [K in keyof G["nodes"] & string]-?: StoreViewNodeCollection<G["nodes"][K]["type"]>;
2766
+ };
2767
+ /** Mapped type of all read-only view edge collections for a graph. */
2768
+ type StoreViewEdgeCollections<G extends GraphDef> = {
2769
+ [K in keyof G["edges"] & string]-?: TypedStoreViewEdgeCollection<G["edges"][K]>;
2770
+ };
2771
+ /** Options for one bounded, forward-only recorded-time collection scan. */
2772
+ type RecordedScanOptions = Readonly<{
2773
+ /** Maximum entities to return. Defaults to 1,000 and cannot exceed 1,000. */
2774
+ limit?: number;
2775
+ /** Opaque cursor returned by the preceding page. */
2776
+ after?: string;
2777
+ }>;
2778
+ /** One page from a deterministic recorded-time collection scan. */
2779
+ type RecordedScanPage<T> = Readonly<{
2780
+ /** Entities ordered by canonical id ascending. */
2781
+ data: readonly T[];
2782
+ /** Cursor for the next page, or `undefined` when the scan is complete. */
2783
+ nextCursor: string | undefined;
2784
+ /** Whether another page exists after this one. */
2785
+ hasNextPage: boolean;
2786
+ }>;
2787
+ /** Recorded-time reconstructing reads for one node kind. */
2788
+ type RecordedStoreViewNodeCollection<N extends NodeType> = Pick<StoreViewNodeCollection<N>, (typeof RECORDED_POINT_READ_NAMES)[number]> & Readonly<{
2789
+ /** Scan one bounded page at the view's pinned coordinate. */
2790
+ scan: (options?: RecordedScanOptions) => Promise<RecordedScanPage<Node<N>>>;
2791
+ }>;
2792
+ /** Recorded-time reconstructing reads for one edge kind. */
2793
+ type RecordedStoreViewEdgeCollection<E extends AnyEdgeType, From extends NodeType = NodeType, To extends NodeType = NodeType> = Pick<StoreViewEdgeCollection<E, From, To>, (typeof RECORDED_POINT_READ_NAMES)[number]> & Readonly<{
2794
+ /** Scan one bounded page at the view's pinned coordinate. */
2795
+ scan: (options?: RecordedScanOptions) => Promise<RecordedScanPage<Edge<E, From, To>>>;
2796
+ }>;
2797
+ /** Recorded-time edge collection derived from an `EdgeRegistration`. */
2798
+ type TypedRecordedStoreViewEdgeCollection<R extends EdgeRegistration> = RecordedStoreViewEdgeCollection<R["type"], EdgeFromTypes<R> extends NodeType ? EdgeFromTypes<R> : NodeType, EdgeToTypes<R> extends NodeType ? EdgeToTypes<R> : NodeType>;
2799
+ /** Mapped type of all recorded-time node reconstructing-read collections. */
2800
+ type RecordedStoreViewNodeCollections<G extends GraphDef> = {
2801
+ [K in keyof G["nodes"] & string]-?: RecordedStoreViewNodeCollection<G["nodes"][K]["type"]>;
2802
+ };
2803
+ /** Mapped type of all recorded-time edge reconstructing-read collections. */
2804
+ type RecordedStoreViewEdgeCollections<G extends GraphDef> = {
2805
+ [K in keyof G["edges"] & string]-?: TypedRecordedStoreViewEdgeCollection<G["edges"][K]>;
2806
+ };
2807
+ /**
2808
+ * Whether — and why — `tx.sql` can be used on a transaction context. A single
2809
+ * required discriminant covering exactly the four states raw-SQL access can be
2810
+ * in, so an adapter caller branches on capability instead of truthiness-testing
2811
+ * `tx.sql`. The non-available variants omit `sql` entirely, so even reading the
2812
+ * handle requires first narrowing `sqlAvailability` to `"available"`. The
2813
+ * runtime object keeps a fail-loud getter under history / revision tracking for
2814
+ * JavaScript and type-suppressed callers. See
2815
+ * {@link AdapterTransactionContext} for the per-value semantics.
2816
+ */
2817
+ type SqlAvailability = "available" | "history" | "revisionTracking" | "unavailable";
2818
+ type AdapterTransactionSqlAccess<TNativeTransaction> = Readonly<{
2819
+ sql: TNativeTransaction;
2820
+ sqlAvailability: "available";
2821
+ }> | Readonly<{
2822
+ sqlAvailability: "history" | "revisionTracking";
2823
+ }> | Readonly<{
2824
+ sqlAvailability: "unavailable";
2825
+ }>;
2826
+ declare const TRANSACTION_RUNTIME: unique symbol;
2827
+ type TransactionRuntime = Readonly<{
2828
+ backend: TransactionBackend;
2829
+ runNodeOperationHooks: <T>(operation: "create" | "update" | "delete", kind: string, id: string, fn: () => Promise<T>) => Promise<T>;
2830
+ }>;
2831
+ /**
2832
+ * The portable transaction context mirrors the Store's typed graph operations
2833
+ * while exposing only a read-only backend projection bound to the transaction.
2834
+ * Adapter-native handles are available only through
2835
+ * {@link AdapterTransactionContext}; arbitrary backend writes are absent from
2836
+ * every public transaction context. TypeGraph's non-enumerable symbol port is
2837
+ * an unsupported implementation detail, not a JavaScript security boundary:
2838
+ * reflective code can still discover symbol properties.
2839
+ *
2840
+ * @example
2841
+ * ```typescript
2842
+ * await store.transaction(async (tx) => {
2843
+ * const person = await tx.nodes.Person.create({ name: "Alice" });
2844
+ * const company = await tx.nodes.Company.create({ name: "Acme" });
2845
+ * await tx.edges.worksAt.create(person, company, { role: "Engineer" });
2846
+ * });
2847
+ * ```
2848
+ */
2849
+ type TransactionCollections<G extends GraphDef> = Readonly<{
2850
+ [TRANSACTION_RUNTIME]: TransactionRuntime;
2851
+ nodes: GraphNodeCollections<G>;
2852
+ edges: GraphEdgeCollections<G>;
2853
+ /** Read-only backend projection bound to the same graph transaction. */
2854
+ backend: TransactionReadBackend;
2855
+ /**
2856
+ * Runtime string-keyed node collection access, mirroring
2857
+ * `Store.getNodeCollection`. Returns `undefined` when `kind` is not
2858
+ * registered in this graph.
2859
+ */
2860
+ getNodeCollection: (kind: string) => DynamicNodeCollection | undefined;
2861
+ }>;
2862
+ /**
2863
+ * A portable transaction context containing only TypeGraph-owned graph
2864
+ * operations. Managed Stores use this surface so adapter-native handles never
2865
+ * enter their public contract.
2866
+ */
2867
+ type TransactionContext<G extends GraphDef> = TransactionCollections<G>;
2868
+ /**
2869
+ * A transaction context exposed by an {@link AdapterStore}. In addition to the
2870
+ * portable graph collections, it carries the adapter-native handle when that
2871
+ * capability is available. The TypeGraph backend remains the same runtime
2872
+ * read projection as the portable context; adapter-native writes intentionally
2873
+ * go through `sql`, making that escape hatch explicit.
2874
+ */
2875
+ type AdapterTransactionContext<G extends GraphDef, TNativeTransaction> = TransactionCollections<G> & AdapterTransactionSqlAccess<TNativeTransaction>;
2876
+ /**
2877
+ * Scoped write measurement, available only on the receipt-enabled transaction
2878
+ * contexts (`transactionWithReceipt`, `withRecordedTransaction`). Runs `fn`,
2879
+ * passing it a **scoped context** — a second view over the same transaction — and
2880
+ * returns a {@link TransactionOutcome} whose receipt counts exactly the writes
2881
+ * made *through that scoped context* (`scoped.nodes` / `scoped.edges`).
2882
+ *
2883
+ * Attribution is by **which context you write through**, not by timing. A write
2884
+ * through the scoped context counts in both the scope and the outer receipt (it
2885
+ * happened in the transaction); a write through the outer `tx` during the scope
2886
+ * counts only in the outer receipt. This makes overlapping and concurrent
2887
+ * measures safe by construction — two scopes running under `Promise.all`, each
2888
+ * writing through its own scoped context, never cross-count. Nesting composes:
2889
+ * `scoped.measure(...)` opens a child scope that counts in itself, every
2890
+ * ancestor scope, and the outer receipt.
2891
+ *
2892
+ * Counts otherwise inherit {@link TransactionReceipt} (bulk by input length, a
2893
+ * rejected write counts 0). The returned receipt's `recorded` is **always
2894
+ * `undefined`**: the recorded commit instant is a per-transaction flush concern,
2895
+ * unknowable mid-transaction.
2896
+ */
2897
+ type ScopedMeasure<Context> = <T>(fn: (scoped: Context) => Promise<T>) => Promise<TransactionOutcome<T>>;
2898
+ /**
2899
+ * A {@link TransactionContext} that also exposes {@link ScopedMeasure}. Only the
2900
+ * receipt-enabled entry points (`transactionWithReceipt`,
2901
+ * `withRecordedTransaction`) hand a callback this type; plain `transaction()`
2902
+ * contexts have no recorder and therefore no `measure`, keeping that path
2903
+ * zero-overhead. Assignable to {@link TransactionContext}, so a projector helper
2904
+ * typed `(tx: TransactionContext<G>) => ...` accepts a measurable context. The
2905
+ * scoped context handed to `measure` is itself measurable, so scopes nest.
2906
+ */
2907
+ type MeasurableTransactionContext<G extends GraphDef> = TransactionContext<G> & Readonly<{
2908
+ measure: ScopedMeasure<MeasurableTransactionContext<G>>;
2909
+ }>;
2910
+ /** Receipt-enabled transaction context for an {@link AdapterStore}. */
2911
+ type MeasurableAdapterTransactionContext<G extends GraphDef, TNativeTransaction> = AdapterTransactionContext<G, TNativeTransaction> & Readonly<{
2912
+ measure: ScopedMeasure<MeasurableAdapterTransactionContext<G, TNativeTransaction>>;
2913
+ }>;
2914
+ /**
2915
+ * Replace branded `NodeId` / `EdgeId` with plain `string` in each
2916
+ * method's parameter list. Return types are preserved unchanged.
2917
+ *
2918
+ * Handles three shapes:
2919
+ * 1. Direct branded ID parameter → `string`
2920
+ * 2. `readonly NodeId[]` / `readonly EdgeId[]` → `readonly string[]`
2921
+ * 3. Branded IDs nested one level inside bulk-item object arrays
2922
+ */
2923
+ type WidenBrandedIds<T> = {
2924
+ readonly [K in keyof T]: T[K] extends (...args: infer A) => infer R ? (...args: {
2925
+ [P in keyof A]: UnbrandParam<A[P]>;
2926
+ }) => R : T[K];
2927
+ };
2928
+ /** Replace a branded ID with `string`, recursing one level into arrays. */
2929
+ type UnbrandParam<T> = T extends NodeId<NodeType> ? string : T extends EdgeId<AnyEdgeType> ? string : T extends readonly NodeId<NodeType>[] ? readonly string[] : T extends readonly EdgeId<AnyEdgeType>[] ? readonly string[] : T extends readonly (infer Item extends Record<string, unknown>)[] ? readonly UnbrandRecord<Item>[] : T;
2930
+ /** Replace branded ID values in object properties (does not recurse into nested structures). */
2931
+ type UnbrandRecord<T extends Record<string, unknown>> = {
2932
+ readonly [K in keyof T]: T[K] extends NodeId<NodeType> ? string : T[K] extends EdgeId<AnyEdgeType> ? string : T[K];
2933
+ };
2934
+ /**
2935
+ * A node collection with widened generics for runtime string-keyed access.
2936
+ *
2937
+ * This is the return type of `store.getNodeCollection(kind)`. It exposes
2938
+ * the full `NodeCollection` API but with `NodeType` and `string` constraint
2939
+ * names instead of the specific generic parameters, since the concrete type
2940
+ * is not known at compile time.
2941
+ *
2942
+ * ID parameters accept plain `string` instead of branded `NodeId<N>`, since
2943
+ * the dynamic path typically receives IDs from edge metadata, snapshots,
2944
+ * or external input where the brand is not available.
2945
+ */
2946
+ type DynamicNodeCollection = WidenBrandedIds<NodeCollection<NodeType, string>>;
2947
+ /**
2948
+ * An edge collection with widened generics for runtime string-keyed access.
2949
+ *
2950
+ * This is the return type of `store.getEdgeCollection(kind)`. It exposes
2951
+ * the full `EdgeCollection` API but with `NodeType` endpoint types, since
2952
+ * the concrete from/to types are not known at compile time.
2953
+ *
2954
+ * ID parameters accept plain `string` instead of branded `EdgeId<E>`, since
2955
+ * the dynamic path typically receives IDs from edge metadata, snapshots,
2956
+ * or external input where the brand is not available.
2957
+ */
2958
+ type DynamicEdgeCollection = WidenBrandedIds<EdgeCollection<AnyEdgeType, NodeType, NodeType>>;
2959
+ /**
2960
+ * A type-level projection of a store's surface onto a subset of its
2961
+ * node and edge collections.
2962
+ *
2963
+ * Node collections are projected with constraint names erased (`never`),
2964
+ * so constraint-based methods like `findByConstraint` become uncallable.
2965
+ * This is intentional: unique constraints are graph-registration-level
2966
+ * details that differ between graphs sharing the same node types.
2967
+ *
2968
+ * @example
2969
+ * ```typescript
2970
+ * type CoreStore = StoreProjection<
2971
+ * typeof myGraph,
2972
+ * "Document" | "Chunk",
2973
+ * "hasChunk"
2974
+ * >;
2975
+ *
2976
+ * async function ingestChunk(
2977
+ * store: CoreStore,
2978
+ * document: Node<typeof Document>,
2979
+ * text: string,
2980
+ * ) {
2981
+ * const chunk = await store.nodes.Chunk.create({ text });
2982
+ * await store.edges.hasChunk.create(document, chunk);
2983
+ * return chunk;
2984
+ * }
2985
+ * ```
2986
+ *
2987
+ * Both `Store<G>` and `TransactionContext<G>` are structurally assignable
2988
+ * to a `StoreProjection` whose keys are a subset of `G`.
2989
+ */
2990
+ type StoreProjection<G extends GraphDef, N extends keyof G["nodes"] & string = never, E extends keyof G["edges"] & string = never> = Readonly<{
2991
+ nodes: {
2992
+ [K in N]-?: NodeCollection<G["nodes"][K]["type"], never>;
2993
+ };
2994
+ edges: Pick<GraphEdgeCollections<G>, E>;
2995
+ }>;
2924
2996
 
2925
2997
  /**
2926
2998
  * Shared types for Tier 1 graph algorithms.
@@ -3682,9 +3754,9 @@ declare class StoreSearch<G extends GraphDef> {
3682
3754
  * transactions stay internal because each can mutate live rows without a
3683
3755
  * corresponding capture flush.
3684
3756
  */
3685
- declare const HISTORY_STORE_BACKEND_KEYS: readonly ["assertRuntimeContributionsInitialized", "assertVectorSlotInitialized", "assertVectorSlotsInitialized", "bootstrapTables", "capabilities", "checkUnique", "checkUniqueBatch", "claimIndexMaterialization", "close", "commitSchemaVersion", "commitSchemaVersionIfKindsEmpty", "lockSchemaVersionForWrite", "compileSql", "countEdgesByKind", "countEdgesFrom", "countNodesByKind", "createVectorIndex", "deleteEdge", "deleteEdgesBatch", "deleteEmbedding", "deleteFulltext", "deleteFulltextBatch", "deleteNode", "deleteUnique", "deleteVectorSlotContribution", "dialect", "dropVectorIndex", "edgeExistsBetween", "ensureContributionMaterializationsTable", "ensureFulltextTable", "ensureIndexMaterializationsTable", "ensureKindRemovalsTable", "ensureReconciliationMarkersTable", "ensureRevisionOriginsTable", "ensureRuntimeContributions", "ensureVectorSlotContribution", "ensureVectorSlotContributions", "execute", "executeTemporaryStatement", "findEdgesByKind", "findEdgesByEndpointSet", "findEdgesConnectedTo", "findNodesByKind", "fulltextSearch", "fulltextStrategy", "getActiveSchema", "getAllKindRemovals", "getContributionMaterialization", "getEdge", "getEdges", "getIndexMaterialization", "getIndexMaterializations", "getNode", "getNodes", "getPendingKindRemovals", "getReconciliationMarker", "getSchemaVersion", "hardDeleteEdge", "hardDeleteEdgesBatch", "hardDeleteNode", "hybridSearch", "insertEdge", "insertEdgeNoReturn", "insertEdgesBatch", "insertEdgesBatchReturning", "insertNode", "insertNodeNoReturn", "insertNodesBatch", "insertNodesBatchReturning", "insertUnique", "insertUniqueBatch", "recordContributionMaterialization", "recordIndexMaterialization", "recordKindRemoval", "refreshStatistics", "releaseIndexMaterializationClaim", "setActiveVersion", "setReconciliationMarker", "tableNames", "updateEdge", "updateNode", "upsertEmbedding", "upsertEmbeddingBatch", "upsertFulltext", "upsertFulltextBatch", "vectorSearch", "vectorStrategy", "verifyContributions"];
3757
+ declare const HISTORY_STORE_BACKEND_KEYS: readonly ["assertRuntimeContributionsInitialized", "assertVectorSlotInitialized", "assertVectorSlotsInitialized", "bootstrapTables", "capabilities", "checkUnique", "checkUniqueBatch", "claimIndexMaterialization", "close", "commitSchemaVersion", "commitSchemaVersionIfKindsEmpty", "lockSchemaVersionForWrite", "compileSql", "countEdgesByKind", "countEdgesFrom", "countNodesByKind", "createVectorIndex", "deleteEdge", "deleteEdgesBatch", "deleteEmbedding", "deleteEmbeddingBatch", "deleteFulltext", "deleteFulltextBatch", "deleteNode", "deleteUnique", "hardDeleteUniquesByNodeIds", "deleteVectorSlotContribution", "dialect", "dropVectorIndex", "edgeExistsBetween", "ensureContributionMaterializationsTable", "ensureFulltextTable", "ensureIndexMaterializationsTable", "ensureKindRemovalsTable", "ensureReconciliationMarkersTable", "ensureRevisionOriginsTable", "ensureRuntimeContributions", "ensureVectorSlotContribution", "ensureVectorSlotContributions", "execute", "executeTemporaryStatement", "findEdgesByKind", "findEdgesByEndpointSet", "findEdgesByHeterogeneousEndpointSet", "findEdgesConnectedTo", "findNodesByKind", "fulltextSearch", "fulltextStrategy", "getActiveSchema", "getAllKindRemovals", "getContributionMaterialization", "getEdge", "getEdges", "getIndexMaterialization", "getIndexMaterializations", "getNode", "getNodes", "getPendingKindRemovals", "getReconciliationMarker", "getSchemaVersion", "hardDeleteEdge", "hardDeleteEdgesBatch", "hardDeleteNode", "hardDeleteUniquesByNodeIds", "hybridSearch", "insertEdge", "insertEdgeNoReturn", "insertEdgesBatch", "insertEdgesBatchReturning", "insertNode", "insertNodeNoReturn", "insertNodesBatch", "insertNodesBatchReturning", "insertUnique", "insertUniqueBatch", "recordContributionMaterialization", "recordIndexMaterialization", "recordKindRemoval", "refreshStatistics", "releaseIndexMaterializationClaim", "setActiveVersion", "setReconciliationMarker", "tableNames", "updateEdge", "updateNode", "updateNodeSet", "upsertEmbedding", "upsertEmbeddingBatch", "upsertFulltext", "upsertFulltextBatch", "vectorSearch", "vectorStrategy", "verifyContributions"];
3686
3758
  type HistoryStoreBackendMember = (typeof HISTORY_STORE_BACKEND_KEYS)[number];
3687
- type UnsafeHistoryStoreBackendMember = "clearGraph" | "executeDdl" | "executeRaw" | "executeStatement" | "schemaWriteTransaction" | "transaction" | "trustedImport";
3759
+ type UnsafeHistoryStoreBackendMember = "clearGraph" | "executeDdl" | "executeRaw" | "executeStatement" | "repairContributions" | "schemaWriteTransaction" | "transaction" | "trustedImport";
3688
3760
  type UnclassifiedHistoryStoreBackendMember = Exclude<keyof GraphBackend, HistoryStoreBackendMember | UnsafeHistoryStoreBackendMember>;
3689
3761
  type HistoryStoreBackend = UnclassifiedHistoryStoreBackendMember extends never ? Readonly<Pick<GraphBackend, HistoryStoreBackendMember>> : never;
3690
3762
 
@@ -4138,6 +4210,7 @@ type StoreCore<G extends GraphDef> = Readonly<{
4138
4210
  BatchableQuery<unknown>,
4139
4211
  ...BatchableQuery<unknown>[]
4140
4212
  ]>(...queries: Queries) => Promise<BatchResults<Queries>>;
4213
+ bulkFindEdgesFrom: <const K extends EdgeKinds<G>>(params: BulkFindEdgesFromParams<G, K>, options?: EdgeBulkFindEndpointOptions) => Promise<readonly BulkFindEdgesFromResult<G, K>[]>;
4141
4214
  subgraph: <const EK extends EdgeKinds<G>, const NK extends NodeKinds<G> = NodeKinds<G>, const P extends SubgraphProject<G, NK, EK> | undefined = undefined>(rootId: NodeId<AllNodeTypes<G>>, options: SubgraphOptions<G, EK, NK, P>) => Promise<SubgraphResult<G, NK, EK, P>>;
4142
4215
  clear: () => Promise<void>;
4143
4216
  refreshStatistics: () => Promise<void>;
@@ -4145,6 +4218,7 @@ type StoreCore<G extends GraphDef> = Readonly<{
4145
4218
  materializeSystemIndexes: (options?: MaterializeSystemIndexesOptions) => Promise<MaterializeIndexesResult>;
4146
4219
  reembedVectorField: (kind: string, fieldPath: string, options?: ReembedVectorFieldOptions) => Promise<ReembedVectorFieldResult>;
4147
4220
  verifyContributions: () => Promise<readonly ContributionDiagnostic[]>;
4221
+ repairContributions: () => Promise<ContributionRepairResult>;
4148
4222
  materializeRemovals: (options?: MaterializeRemovalsOptions) => Promise<MaterializeRemovalsResult>;
4149
4223
  close: () => Promise<void>;
4150
4224
  }>;
@@ -4591,6 +4665,8 @@ declare class StoreView<G extends GraphDef> extends CoordinatePinnedView<G> {
4591
4665
  get nodes(): StoreViewNodeCollections<G>;
4592
4666
  /** Read-only edge collections pinned to this view's coordinate. */
4593
4667
  get edges(): StoreViewEdgeCollections<G>;
4668
+ /** Heterogeneous multi-kind edge read pinned to this view's coordinate. */
4669
+ bulkFindEdgesFrom<const K extends EdgeKinds<G>>(params: BulkFindEdgesFromParams<G, K>, options?: Omit<EdgeBulkFindEndpointOptions, "temporalMode" | "asOf">): Promise<readonly BulkFindEdgesFromResult<G, K>[]>;
4594
4670
  /**
4595
4671
  * Read-only search facade. On a `current` view the read methods
4596
4672
  * (`fulltext` / `vector` / `hybrid`) delegate to the live `store.search`,
@@ -4619,4 +4695,4 @@ declare class RecordedStoreView<G extends GraphDef> extends CoordinatePinnedView
4619
4695
  get edges(): RecordedStoreViewEdgeCollections<G>;
4620
4696
  }
4621
4697
 
4622
- export { type EmptyAliasMap as $, type AliasMap as A, type BaseStoreOptions as B, type ConstraintNames as C, type DegreeOptions as D, type EdgeAliasMap as E, type DynamicNodeType as F, type DynamicSelectableEdge as G, type HistoryStoreOptions as H, type DynamicSelectableNode as I, type Edge as J, type EdgeAccessor as K, type LiveStoreOptions as L, type EdgeBatchReads as M, type EdgeBulkFindEndpointOptions as N, type EdgeBulkFindOptions as O, type EdgeCollection as P, type QueryCoordinateState as Q, type RecordedReadStoreOptions as R, type StoreOptions as S, TraversalBuilder as T, type UnboundLiveStoreOptions as U, type EdgeFindByEndpointsOptions as V, type EdgeGetOrCreateByEndpointsOptions as W, type EdgeGetOrCreateByEndpointsResult as X, type EdgeIntrospection as Y, type EdgeTemporalReads as Z, type EdgeWrites as _, type Store as a, RecordedStoreView as a$, type EmptyEdgeAliasMap as a0, ExecutableAggregateQuery as a1, ExecutableQuery as a2, type FieldAccessor as a3, type FulltextSearchHit as a4, type FulltextSearchOptions as a5, type GetOrCreateAction as a6, type GraphAlgorithms as a7, type GraphEdgeCollections as a8, type GraphNodeCollections as a9, type NodeGetOrCreateByConstraintResult as aA, type NodeIdentifier as aB, type NodeRef as aC, type NodeTemporalReads as aD, type NodeWrites as aE, type OntologyIntrospection as aF, type OperationHookContext as aG, type PageRankOptions as aH, type PageRankScore as aI, type PaginateOptions as aJ, type PaginatedResult as aK, type PathNode as aL, type PersonalizedPageRankOptions as aM, type PersonalizedPageRankSeed as aN, type Predicate as aO, PreparedQuery as aP, type PropsAccessor as aQ, type QueryHookContext as aR, type QueryOptions as aS, type ReachableNode as aT, type ReachableOptions as aU, type RebuildFulltextOptions as aV, type RebuildFulltextResult as aW, type ReclaimedVectorFieldEntry as aX, type ReconciledSchema as aY, type RecordedScanOptions as aZ, type RecordedScanPage as a_, type HistoryStoreBackend as aa, type HookContext as ab, type HybridFulltextOptions as ac, type HybridSearchHit as ad, type HybridSearchOptions as ae, type HybridVectorOptions as af, type IfExistsMode as ag, type InitialQueryBuilder as ah, type KindIntrospection as ai, type LabelPropagationMembership as aj, type LabelPropagationOptions as ak, type MaterializeRemovalsEntry as al, type MaterializeRemovalsOptions as am, type MaterializeRemovalsResult as an, type MeasurableAdapterHistoryTransactionContext as ao, type MeasurableAdapterTransactionContext as ap, type MeasurableTransactionContext as aq, type NeighborsOptions as ar, type NoRecordedCoordinate as as, type Node as at, type NodeAccessor as au, type NodeAlias as av, type NodeBulkFindByIndexOptions as aw, type NodeCollection as ax, type NodeCurrentReads as ay, type NodeGetOrCreateByConstraintOptions as az, type HistoryStore as b, type WeightedShortestPathResult as b$, type RecordedStoreViewEdgeCollection as b0, type RecordedStoreViewEdgeCollections as b1, type RecordedStoreViewNodeCollection as b2, type RecordedStoreViewNodeCollections as b3, type RecursiveTraversalOptions as b4, type ReembedFunction as b5, type ReembedVectorFieldOptions as b6, type ReembedVectorFieldResult as b7, type SchemaIntrospection as b8, type ScopedMeasure as b9, type StoreViewSubgraphOptions as bA, type StoreViewWeaklyConnectedComponentsOptions as bB, type StoreViewWeightedShortestPathOptions as bC, type StreamOptions as bD, type SubgraphEdgeResult as bE, type SubgraphNodeResult as bF, type SubgraphOptions as bG, type SubgraphResult as bH, type SubsetEdge as bI, type SubsetNode as bJ, type TemporalAlgorithmOptions as bK, type TransactionContext as bL, type TransactionOutcome as bM, type TransactionReceipt as bN, type TraversalDirection as bO, type TypedEdgeCollection as bP, type TypedRecordedStoreViewEdgeCollection as bQ, type TypedStoreViewEdgeCollection as bR, UnionableQuery as bS, type UniqueIntrospection as bT, type UpdateEdgeInput as bU, type UpdateNodeInput as bV, type VectorSearchHit as bW, type VectorSearchOptions as bX, type WeaklyConnectedComponentMembership as bY, type WeaklyConnectedComponentsOptions as bZ, type WeightedShortestPathOptions as b_, type SearchScopeOptions as ba, type SelectContext as bb, type SelectableEdge as bc, type SelectableNode as bd, type ShortestPathOptions as be, type ShortestPathResult as bf, type SqlAvailability as bg, type StoreHooks as bh, type StoreProjection as bi, type StoreRef as bj, StoreSearch as bk, StoreView as bl, type StoreViewCanReachOptions as bm, type StoreViewCoordinate as bn, type StoreViewDegreeOptions as bo, type StoreViewEdgeCollection as bp, type StoreViewEdgeCollections as bq, type StoreViewGraphAlgorithms as br, type StoreViewLabelPropagationOptions as bs, type StoreViewNeighborsOptions as bt, type StoreViewNodeCollection as bu, type StoreViewNodeCollections as bv, type StoreViewPageRankOptions as bw, type StoreViewPersonalizedPageRankOptions as bx, type StoreViewReachableOptions as by, type StoreViewShortestPathOptions as bz, type RecordedReadStore as c, createAdapterStore as c0, createAdapterStoreWithSchema as c1, createQueryBuilder as c2, createStore as c3, createStoreWithSchema as c4, createVerifiedAdapterStore as c5, createVerifiedStore as c6, defineSubgraphProject as c7, exists as c8, fieldRef as c9, inSubquery as ca, isParameterRef as cb, notExists as cc, notInSubquery as cd, param as ce, type RecursiveAliasMap as d, QueryBuilder as e, type EmptyRecursiveAliasMap as f, type AdapterHistoryStore as g, type AdapterHistoryTransactionContext as h, type AdapterRecordedReadStore as i, type AdapterStore as j, type AdapterTransactionContext as k, type AggregateResult as l, type AlgorithmCyclePolicy as m, type AnyEdge as n, type AnyNode as o, type BaseTraversalOptions as p, type BatchResults as q, type BatchableQuery as r, type CreateEdgeInput as s, type CreateNodeInput as t, type DynamicEdgeAccessor as u, type DynamicEdgeCollection as v, type DynamicEdgeType as w, type DynamicFieldBuilder as x, type DynamicNodeAccessor as y, type DynamicNodeCollection as z };
4698
+ export { type EdgeGetOrCreateByEndpointsResult as $, type AliasMap as A, type BaseStoreOptions as B, type ConstraintNames as C, type DegreeOptions as D, type EdgeAliasMap as E, type DynamicEdgeType as F, type DynamicFieldBuilder as G, type HistoryStoreOptions as H, type DynamicNodeAccessor as I, type DynamicNodeCollection as J, type DynamicNodeType as K, type LiveStoreOptions as L, type DynamicSelectableEdge as M, type DynamicSelectableNode as N, type Edge as O, type EdgeAccessor as P, type QueryCoordinateState as Q, type RecordedReadStoreOptions as R, type StoreOptions as S, TraversalBuilder as T, type UnboundLiveStoreOptions as U, type EdgeBatchReads as V, type EdgeBulkFindEndpointOptions as W, type EdgeBulkFindOptions as X, type EdgeCollection as Y, type EdgeFindByEndpointsOptions as Z, type EdgeGetOrCreateByEndpointsOptions as _, type Store as a, type RebuildFulltextOptions as a$, type EdgeIntrospection as a0, type EdgeTemporalReads as a1, type EdgeWrites as a2, type EmptyAliasMap as a3, type EmptyEdgeAliasMap as a4, ExecutableAggregateQuery as a5, ExecutableQuery as a6, type FieldAccessor as a7, type FulltextSearchHit as a8, type FulltextSearchOptions as a9, type NodeAccessor as aA, type NodeAlias as aB, type NodeBulkFindByIndexOptions as aC, type NodeCollection as aD, type NodeCurrentReads as aE, type NodeGetOrCreateByConstraintOptions as aF, type NodeGetOrCreateByConstraintResult as aG, type NodeIdentifier as aH, type NodeRef as aI, type NodeTemporalReads as aJ, type NodeWrites as aK, type OntologyIntrospection as aL, type OperationHookContext as aM, type PageRankOptions as aN, type PageRankScore as aO, type PaginateOptions as aP, type PaginatedResult as aQ, type PathNode as aR, type PersonalizedPageRankOptions as aS, type PersonalizedPageRankSeed as aT, type Predicate as aU, PreparedQuery as aV, type PropsAccessor as aW, type QueryHookContext as aX, type QueryOptions as aY, type ReachableNode as aZ, type ReachableOptions as a_, type GetOrCreateAction as aa, type GraphAlgorithms as ab, type GraphEdgeCollections as ac, type GraphEdgeForKinds as ad, type GraphNodeCollections as ae, type GraphNodeReference as af, type HistoryStoreBackend as ag, type HookContext as ah, type HybridFulltextOptions as ai, type HybridSearchHit as aj, type HybridSearchOptions as ak, type HybridVectorOptions as al, type IfExistsMode as am, type InitialQueryBuilder as an, type KindIntrospection as ao, type LabelPropagationMembership as ap, type LabelPropagationOptions as aq, type MaterializeRemovalsEntry as ar, type MaterializeRemovalsOptions as as, type MaterializeRemovalsResult as at, type MeasurableAdapterHistoryTransactionContext as au, type MeasurableAdapterTransactionContext as av, type MeasurableTransactionContext as aw, type NeighborsOptions as ax, type NoRecordedCoordinate as ay, type Node as az, type HistoryStore as b, type UpdateNodeInput as b$, type RebuildFulltextResult as b0, type ReclaimedVectorFieldEntry as b1, type ReconciledSchema as b2, type RecordedScanOptions as b3, type RecordedScanPage as b4, RecordedStoreView as b5, type RecordedStoreViewEdgeCollection as b6, type RecordedStoreViewEdgeCollections as b7, type RecordedStoreViewNodeCollection as b8, type RecordedStoreViewNodeCollections as b9, type StoreViewNodeCollection as bA, type StoreViewNodeCollections as bB, type StoreViewPageRankOptions as bC, type StoreViewPersonalizedPageRankOptions as bD, type StoreViewReachableOptions as bE, type StoreViewShortestPathOptions as bF, type StoreViewSubgraphOptions as bG, type StoreViewWeaklyConnectedComponentsOptions as bH, type StoreViewWeightedShortestPathOptions as bI, type StreamOptions as bJ, type SubgraphEdgeResult as bK, type SubgraphNodeResult as bL, type SubgraphOptions as bM, type SubgraphResult as bN, type SubsetEdge as bO, type SubsetNode as bP, type TemporalAlgorithmOptions as bQ, type TransactionContext as bR, type TransactionOutcome as bS, type TransactionReceipt as bT, type TraversalDirection as bU, type TypedEdgeCollection as bV, type TypedRecordedStoreViewEdgeCollection as bW, type TypedStoreViewEdgeCollection as bX, UnionableQuery as bY, type UniqueIntrospection as bZ, type UpdateEdgeInput as b_, type RecursiveTraversalOptions as ba, type ReembedFunction as bb, type ReembedVectorFieldOptions as bc, type ReembedVectorFieldResult as bd, type SchemaIntrospection as be, type ScopedMeasure as bf, type SearchScopeOptions as bg, type SelectContext as bh, type SelectableEdge as bi, type SelectableNode as bj, type ShortestPathOptions as bk, type ShortestPathResult as bl, type SqlAvailability as bm, type StoreHooks as bn, type StoreProjection as bo, type StoreRef as bp, StoreSearch as bq, StoreView as br, type StoreViewCanReachOptions as bs, type StoreViewCoordinate as bt, type StoreViewDegreeOptions as bu, type StoreViewEdgeCollection as bv, type StoreViewEdgeCollections as bw, type StoreViewGraphAlgorithms as bx, type StoreViewLabelPropagationOptions as by, type StoreViewNeighborsOptions as bz, type RecordedReadStore as c, type VectorSearchHit as c0, type VectorSearchOptions as c1, type WeaklyConnectedComponentMembership as c2, type WeaklyConnectedComponentsOptions as c3, type WeightedShortestPathOptions as c4, type WeightedShortestPathResult as c5, createAdapterStore as c6, createAdapterStoreWithSchema as c7, createQueryBuilder as c8, createStore as c9, createStoreWithSchema as ca, createVerifiedAdapterStore as cb, createVerifiedStore as cc, defineSubgraphProject as cd, exists as ce, fieldRef as cf, inSubquery as cg, isParameterRef as ch, notExists as ci, notInSubquery as cj, param as ck, type RecursiveAliasMap as d, QueryBuilder as e, type EmptyRecursiveAliasMap as f, type AdapterHistoryStore as g, type AdapterHistoryTransactionContext as h, type AdapterRecordedReadStore as i, type AdapterStore as j, type AdapterTransactionContext as k, type AggregateResult as l, type AlgorithmCyclePolicy as m, type AnyEdge as n, type AnyNode as o, type BaseTraversalOptions as p, type BatchResults as q, type BatchableQuery as r, type BulkEdgeSourceGroup as s, type BulkFindEdgesFromParams as t, type BulkFindEdgesFromResult as u, type BulkOperationHookContext as v, type CreateEdgeInput as w, type CreateNodeInput as x, type DynamicEdgeAccessor as y, type DynamicEdgeCollection as z };