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