@nicia-ai/typegraph 0.36.0 → 0.37.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 (232) hide show
  1. package/dist/backend/postgres/index.cjs +24 -24
  2. package/dist/backend/postgres/index.d.cts +6 -6
  3. package/dist/backend/postgres/index.d.ts +6 -6
  4. package/dist/backend/postgres/index.js +11 -11
  5. package/dist/backend/postgres/pglite.cjs +16 -16
  6. package/dist/backend/postgres/pglite.d.cts +3 -3
  7. package/dist/backend/postgres/pglite.d.ts +3 -3
  8. package/dist/backend/postgres/pglite.js +11 -11
  9. package/dist/backend/sqlite/index.cjs +22 -21
  10. package/dist/backend/sqlite/index.d.cts +11 -9
  11. package/dist/backend/sqlite/index.d.ts +11 -9
  12. package/dist/backend/sqlite/index.js +10 -9
  13. package/dist/backend/sqlite/libsql.cjs +15 -14
  14. package/dist/backend/sqlite/libsql.cjs.map +1 -1
  15. package/dist/backend/sqlite/libsql.d.cts +3 -3
  16. package/dist/backend/sqlite/libsql.d.ts +3 -3
  17. package/dist/backend/sqlite/libsql.js +11 -10
  18. package/dist/backend/sqlite/libsql.js.map +1 -1
  19. package/dist/backend/sqlite/local.cjs +30 -23
  20. package/dist/backend/sqlite/local.cjs.map +1 -1
  21. package/dist/backend/sqlite/local.d.cts +3 -3
  22. package/dist/backend/sqlite/local.d.ts +3 -3
  23. package/dist/backend/sqlite/local.js +18 -11
  24. package/dist/backend/sqlite/local.js.map +1 -1
  25. package/dist/{chunk-TCOCYN2P.cjs → chunk-27SSDCEP.cjs} +14 -14
  26. package/dist/{chunk-TCOCYN2P.cjs.map → chunk-27SSDCEP.cjs.map} +1 -1
  27. package/dist/{chunk-5AQIKK56.cjs → chunk-2N6EOBTH.cjs} +16 -16
  28. package/dist/{chunk-5AQIKK56.cjs.map → chunk-2N6EOBTH.cjs.map} +1 -1
  29. package/dist/{chunk-FN35DLCN.cjs → chunk-3C2RYI6L.cjs} +13 -107
  30. package/dist/chunk-3C2RYI6L.cjs.map +1 -0
  31. package/dist/{chunk-HW3LW7DH.cjs → chunk-3DS7Z4T4.cjs} +39 -39
  32. package/dist/{chunk-HW3LW7DH.cjs.map → chunk-3DS7Z4T4.cjs.map} +1 -1
  33. package/dist/{chunk-RSSQKPSZ.cjs → chunk-3GH67G4Z.cjs} +5 -5
  34. package/dist/{chunk-RSSQKPSZ.cjs.map → chunk-3GH67G4Z.cjs.map} +1 -1
  35. package/dist/{chunk-M75SUBFG.js → chunk-3RWZPJKV.js} +24 -9
  36. package/dist/chunk-3RWZPJKV.js.map +1 -0
  37. package/dist/{chunk-SKCOUWAX.js → chunk-3SLPDKHQ.js} +4 -4
  38. package/dist/chunk-3SLPDKHQ.js.map +1 -0
  39. package/dist/{chunk-5A6FNS6N.cjs → chunk-3XXGYU62.cjs} +11 -11
  40. package/dist/{chunk-5A6FNS6N.cjs.map → chunk-3XXGYU62.cjs.map} +1 -1
  41. package/dist/{chunk-IU46OHVW.js → chunk-44TA22QT.js} +58 -11
  42. package/dist/chunk-44TA22QT.js.map +1 -0
  43. package/dist/{chunk-5DIPR3L3.js → chunk-552KNYQJ.js} +2392 -279
  44. package/dist/chunk-552KNYQJ.js.map +1 -0
  45. package/dist/{chunk-2THZ3TQI.cjs → chunk-5KEQ74YU.cjs} +14 -14
  46. package/dist/chunk-5KEQ74YU.cjs.map +1 -0
  47. package/dist/{chunk-Q5TEXCTB.js → chunk-5KPNWT6X.js} +3 -3
  48. package/dist/{chunk-Q5TEXCTB.js.map → chunk-5KPNWT6X.js.map} +1 -1
  49. package/dist/{chunk-WI7ISNQF.js → chunk-5PNMDJ7J.js} +94 -5
  50. package/dist/chunk-5PNMDJ7J.js.map +1 -0
  51. package/dist/{chunk-AW4A3P5A.cjs → chunk-5VQM6VWP.cjs} +2 -2
  52. package/dist/{chunk-AW4A3P5A.cjs.map → chunk-5VQM6VWP.cjs.map} +1 -1
  53. package/dist/{chunk-A3W7QG7K.js → chunk-5WWVZMVX.js} +2 -2
  54. package/dist/{chunk-A3W7QG7K.js.map → chunk-5WWVZMVX.js.map} +1 -1
  55. package/dist/{chunk-FACJJWGF.cjs → chunk-6EEO3OVU.cjs} +7 -7
  56. package/dist/{chunk-FACJJWGF.cjs.map → chunk-6EEO3OVU.cjs.map} +1 -1
  57. package/dist/{chunk-DIK74MXX.cjs → chunk-BVRDGYXQ.cjs} +97 -4
  58. package/dist/chunk-BVRDGYXQ.cjs.map +1 -0
  59. package/dist/{chunk-4BCU6VFZ.cjs → chunk-DEFQM7JU.cjs} +7 -7
  60. package/dist/{chunk-4BCU6VFZ.cjs.map → chunk-DEFQM7JU.cjs.map} +1 -1
  61. package/dist/{chunk-456Z2U7Y.cjs → chunk-DIQ3AV5W.cjs} +574 -268
  62. package/dist/chunk-DIQ3AV5W.cjs.map +1 -0
  63. package/dist/{chunk-FPIZFYHE.js → chunk-EXU3RASM.js} +74 -15
  64. package/dist/chunk-EXU3RASM.js.map +1 -0
  65. package/dist/{chunk-H7YNSMA7.cjs → chunk-HAHNRASW.cjs} +20 -2
  66. package/dist/chunk-HAHNRASW.cjs.map +1 -0
  67. package/dist/{chunk-J3LEGNQB.cjs → chunk-IP3CGGPA.cjs} +8 -8
  68. package/dist/{chunk-J3LEGNQB.cjs.map → chunk-IP3CGGPA.cjs.map} +1 -1
  69. package/dist/{chunk-X4JBT642.cjs → chunk-IPSVVTWT.cjs} +94 -35
  70. package/dist/chunk-IPSVVTWT.cjs.map +1 -0
  71. package/dist/{chunk-VQEXF5D4.js → chunk-IXGYRP2P.js} +184 -92
  72. package/dist/chunk-IXGYRP2P.js.map +1 -0
  73. package/dist/{chunk-BJVEU3ZL.js → chunk-J4IUSYFF.js} +251 -121
  74. package/dist/chunk-J4IUSYFF.js.map +1 -0
  75. package/dist/{chunk-JFMEONLG.cjs → chunk-JBNOGRKW.cjs} +318 -188
  76. package/dist/chunk-JBNOGRKW.cjs.map +1 -0
  77. package/dist/{chunk-4GX2JJXS.js → chunk-K3Q3LJY3.js} +520 -221
  78. package/dist/chunk-K3Q3LJY3.js.map +1 -0
  79. package/dist/{chunk-OR5ZA5WP.js → chunk-K5UBVGJN.js} +4 -4
  80. package/dist/{chunk-OR5ZA5WP.js.map → chunk-K5UBVGJN.js.map} +1 -1
  81. package/dist/{chunk-UZV3AZKF.js → chunk-KQJ54OG5.js} +3 -3
  82. package/dist/{chunk-UZV3AZKF.js.map → chunk-KQJ54OG5.js.map} +1 -1
  83. package/dist/{chunk-SXGE7ITL.js → chunk-L7HQ2OSI.js} +4 -4
  84. package/dist/{chunk-SXGE7ITL.js.map → chunk-L7HQ2OSI.js.map} +1 -1
  85. package/dist/{chunk-RAXU3RMB.js → chunk-M6VES7V6.js} +4 -4
  86. package/dist/{chunk-RAXU3RMB.js.map → chunk-M6VES7V6.js.map} +1 -1
  87. package/dist/{chunk-7BQLPXK6.cjs → chunk-NBP2WTJW.cjs} +3011 -898
  88. package/dist/chunk-NBP2WTJW.cjs.map +1 -0
  89. package/dist/{chunk-PWGX5DF5.cjs → chunk-NUIG3BCV.cjs} +100 -85
  90. package/dist/chunk-NUIG3BCV.cjs.map +1 -0
  91. package/dist/{chunk-ZAMFOJ5P.cjs → chunk-NXQFATMF.cjs} +269 -177
  92. package/dist/chunk-NXQFATMF.cjs.map +1 -0
  93. package/dist/{chunk-KBGQIPRQ.js → chunk-PC2UBAUB.js} +20 -3
  94. package/dist/chunk-PC2UBAUB.js.map +1 -0
  95. package/dist/chunk-QARBLBXT.cjs +219 -0
  96. package/dist/chunk-QARBLBXT.cjs.map +1 -0
  97. package/dist/{chunk-6FR2MMS6.js → chunk-QERAIDRJ.js} +45 -4
  98. package/dist/chunk-QERAIDRJ.js.map +1 -0
  99. package/dist/chunk-QSXTWU4U.cjs +12 -0
  100. package/dist/{chunk-4BMPGT35.cjs.map → chunk-QSXTWU4U.cjs.map} +1 -1
  101. package/dist/{chunk-DN5VP4JC.js → chunk-RCVLFTCT.js} +9 -10
  102. package/dist/chunk-RCVLFTCT.js.map +1 -0
  103. package/dist/{chunk-ZIGXMKQQ.js → chunk-RRSNR3TP.js} +3 -3
  104. package/dist/{chunk-ZIGXMKQQ.js.map → chunk-RRSNR3TP.js.map} +1 -1
  105. package/dist/{chunk-4CAXEB2F.cjs → chunk-TGOMW4MB.cjs} +91 -92
  106. package/dist/chunk-TGOMW4MB.cjs.map +1 -0
  107. package/dist/{chunk-PZKNKG5M.js → chunk-TKT2XHED.js} +8 -8
  108. package/dist/{chunk-PZKNKG5M.js.map → chunk-TKT2XHED.js.map} +1 -1
  109. package/dist/{chunk-S5F7ZLP2.js → chunk-TPF25IG6.js} +3 -3
  110. package/dist/{chunk-S5F7ZLP2.js.map → chunk-TPF25IG6.js.map} +1 -1
  111. package/dist/{chunk-5SEGFTZX.js → chunk-UGZUAYAR.js} +3 -3
  112. package/dist/{chunk-5SEGFTZX.js.map → chunk-UGZUAYAR.js.map} +1 -1
  113. package/dist/{chunk-VHMPG6UL.js → chunk-UXLSNRYL.js} +11 -105
  114. package/dist/chunk-UXLSNRYL.js.map +1 -0
  115. package/dist/{chunk-FRV5Q5IO.js → chunk-UYQMBBWJ.js} +3 -3
  116. package/dist/{chunk-FRV5Q5IO.js.map → chunk-UYQMBBWJ.js.map} +1 -1
  117. package/dist/{chunk-UUT7UN2N.cjs → chunk-VGDDG3XJ.cjs} +7 -7
  118. package/dist/{chunk-UUT7UN2N.cjs.map → chunk-VGDDG3XJ.cjs.map} +1 -1
  119. package/dist/{chunk-5VTR2F2K.js → chunk-VKGKDOKR.js} +12 -10
  120. package/dist/chunk-VKGKDOKR.js.map +1 -0
  121. package/dist/{chunk-5W4L6DLF.cjs → chunk-VL2V6PEN.cjs} +11 -11
  122. package/dist/{chunk-5W4L6DLF.cjs.map → chunk-VL2V6PEN.cjs.map} +1 -1
  123. package/dist/{chunk-ZUNQ5BEP.cjs → chunk-WOJMKB3N.cjs} +82 -80
  124. package/dist/chunk-WOJMKB3N.cjs.map +1 -0
  125. package/dist/{chunk-UMDVATGY.cjs → chunk-WWFHO375.cjs} +6 -6
  126. package/dist/{chunk-UMDVATGY.cjs.map → chunk-WWFHO375.cjs.map} +1 -1
  127. package/dist/{chunk-JFUPDJXD.js → chunk-XGPUQ56G.js} +3 -3
  128. package/dist/{chunk-JFUPDJXD.js.map → chunk-XGPUQ56G.js.map} +1 -1
  129. package/dist/{chunk-D7ZEOS56.cjs → chunk-XGU7DZDA.cjs} +74 -14
  130. package/dist/chunk-XGU7DZDA.cjs.map +1 -0
  131. package/dist/{chunk-S55OV6PW.cjs → chunk-XPCZADMT.cjs} +54 -11
  132. package/dist/chunk-XPCZADMT.cjs.map +1 -0
  133. package/dist/{chunk-T2WUSPDA.js → chunk-XWV4JT34.js} +3 -3
  134. package/dist/{chunk-T2WUSPDA.js.map → chunk-XWV4JT34.js.map} +1 -1
  135. package/dist/chunk-YTSN73ND.js +212 -0
  136. package/dist/chunk-YTSN73ND.js.map +1 -0
  137. package/dist/{ddl-CZz7a0E8.d.cts → ddl-BD-ZfKeX.d.cts} +3 -3
  138. package/dist/{ddl-C2rZ-Aen.d.ts → ddl-CS0KUVwB.d.ts} +3 -3
  139. package/dist/{extension-types-ixRpJVUp.d.ts → extension-types-BM2ACfYO.d.cts} +6 -1
  140. package/dist/{extension-types-ixRpJVUp.d.cts → extension-types-BM2ACfYO.d.ts} +6 -1
  141. package/dist/{fulltext-strategy-CDkSSYhq.d.cts → fulltext-strategy-C27vXaeB.d.cts} +127 -8
  142. package/dist/{fulltext-strategy-B57yvC5Q.d.ts → fulltext-strategy-DebsMrqg.d.ts} +127 -8
  143. package/dist/graph-extension/index.cjs +19 -19
  144. package/dist/graph-extension/index.d.cts +4 -4
  145. package/dist/graph-extension/index.d.ts +4 -4
  146. package/dist/graph-extension/index.js +4 -4
  147. package/dist/graph-merge/index.cjs +88 -88
  148. package/dist/graph-merge/index.d.cts +6 -6
  149. package/dist/graph-merge/index.d.ts +6 -6
  150. package/dist/graph-merge/index.js +19 -19
  151. package/dist/{index-BLtQWKRj.d.cts → index-BPM59vsg.d.cts} +52 -7
  152. package/dist/{index-BFvdmBPV.d.ts → index-BeacbPoe.d.ts} +52 -7
  153. package/dist/index.cjs +224 -187
  154. package/dist/index.cjs.map +1 -1
  155. package/dist/index.d.cts +11 -11
  156. package/dist/index.d.ts +11 -11
  157. package/dist/index.js +53 -40
  158. package/dist/index.js.map +1 -1
  159. package/dist/indexes/index.cjs +30 -21
  160. package/dist/indexes/index.d.cts +5 -5
  161. package/dist/indexes/index.d.ts +5 -5
  162. package/dist/indexes/index.js +8 -7
  163. package/dist/interchange/index.cjs +207 -30
  164. package/dist/interchange/index.cjs.map +1 -1
  165. package/dist/interchange/index.d.cts +25 -8
  166. package/dist/interchange/index.d.ts +25 -8
  167. package/dist/interchange/index.js +190 -11
  168. package/dist/interchange/index.js.map +1 -1
  169. package/dist/{manager-DwQi8eV0.d.cts → manager-D00_NXvT.d.ts} +11 -2
  170. package/dist/{manager-ejUCB52T.d.ts → manager-nU1vTJLy.d.cts} +11 -2
  171. package/dist/{materialize-indexes-B4e9360H.d.cts → materialize-indexes-CuR3gAZb.d.cts} +8 -2
  172. package/dist/{materialize-indexes-te9TPMMJ.d.ts → materialize-indexes-zK2J_flP.d.ts} +8 -2
  173. package/dist/{postgres-CfEqsRMp.d.cts → postgres-IaBA4RgE.d.ts} +1 -1
  174. package/dist/{postgres-DQ5R6rzV.d.ts → postgres-YrYmPlaU.d.cts} +1 -1
  175. package/dist/profiler/index.cjs +3 -3
  176. package/dist/profiler/index.d.cts +7 -7
  177. package/dist/profiler/index.d.ts +7 -7
  178. package/dist/profiler/index.js +2 -2
  179. package/dist/provenance/index.cjs +32 -33
  180. package/dist/provenance/index.cjs.map +1 -1
  181. package/dist/provenance/index.d.cts +5 -5
  182. package/dist/provenance/index.d.ts +5 -5
  183. package/dist/provenance/index.js +5 -6
  184. package/dist/provenance/index.js.map +1 -1
  185. package/dist/schema/index.cjs +35 -35
  186. package/dist/schema/index.d.cts +7 -7
  187. package/dist/schema/index.d.ts +7 -7
  188. package/dist/schema/index.js +11 -11
  189. package/dist/{sqlite-DvQndLky.d.ts → sqlite-BUOHPx7p.d.ts} +1 -1
  190. package/dist/{sqlite-D7zBinNR.d.cts → sqlite-Pp8O19WE.d.cts} +1 -1
  191. package/dist/{store-view-DplHC3Nt.d.ts → store-view-ANL6mTph.d.ts} +213 -27
  192. package/dist/{store-view-9KDOpZzF.d.cts → store-view-X96Rd00T.d.cts} +213 -27
  193. package/dist/{types-Bv9DcS_E.d.cts → types-r-Zyq2Hz.d.cts} +2 -2
  194. package/dist/{types-CoxuV7EJ.d.ts → types-wsvuBgPK.d.ts} +2 -2
  195. package/dist/where-CJOpJj3a.d.ts +36 -0
  196. package/dist/where-K3uBP33l.d.cts +36 -0
  197. package/package.json +1 -1
  198. package/dist/chunk-2THZ3TQI.cjs.map +0 -1
  199. package/dist/chunk-456Z2U7Y.cjs.map +0 -1
  200. package/dist/chunk-4BMPGT35.cjs +0 -12
  201. package/dist/chunk-4CAXEB2F.cjs.map +0 -1
  202. package/dist/chunk-4GX2JJXS.js.map +0 -1
  203. package/dist/chunk-5DIPR3L3.js.map +0 -1
  204. package/dist/chunk-5VTR2F2K.js.map +0 -1
  205. package/dist/chunk-6FR2MMS6.js.map +0 -1
  206. package/dist/chunk-7BQLPXK6.cjs.map +0 -1
  207. package/dist/chunk-BJVEU3ZL.js.map +0 -1
  208. package/dist/chunk-D7ZEOS56.cjs.map +0 -1
  209. package/dist/chunk-DIK74MXX.cjs.map +0 -1
  210. package/dist/chunk-DN5VP4JC.js.map +0 -1
  211. package/dist/chunk-EOLERH7Y.cjs +0 -40
  212. package/dist/chunk-EOLERH7Y.cjs.map +0 -1
  213. package/dist/chunk-FN35DLCN.cjs.map +0 -1
  214. package/dist/chunk-FPIZFYHE.js.map +0 -1
  215. package/dist/chunk-H7YNSMA7.cjs.map +0 -1
  216. package/dist/chunk-IU46OHVW.js.map +0 -1
  217. package/dist/chunk-JFMEONLG.cjs.map +0 -1
  218. package/dist/chunk-KBGQIPRQ.js.map +0 -1
  219. package/dist/chunk-M75SUBFG.js.map +0 -1
  220. package/dist/chunk-PWGX5DF5.cjs.map +0 -1
  221. package/dist/chunk-S55OV6PW.cjs.map +0 -1
  222. package/dist/chunk-SKCOUWAX.js.map +0 -1
  223. package/dist/chunk-UT3S4SEN.js +0 -31
  224. package/dist/chunk-UT3S4SEN.js.map +0 -1
  225. package/dist/chunk-VHMPG6UL.js.map +0 -1
  226. package/dist/chunk-VQEXF5D4.js.map +0 -1
  227. package/dist/chunk-WI7ISNQF.js.map +0 -1
  228. package/dist/chunk-X4JBT642.cjs.map +0 -1
  229. package/dist/chunk-ZAMFOJ5P.cjs.map +0 -1
  230. package/dist/chunk-ZUNQ5BEP.cjs.map +0 -1
  231. package/dist/where-7RKFOsXt.d.ts +0 -11
  232. package/dist/where-DdMIXOtH.d.cts +0 -11
@@ -1,23 +1,23 @@
1
- import { createEdgeRowMapper, createNodeRowMapper, POSTGRES_ROW_MAPPER_CONFIG, SQLITE_ROW_MAPPER_CONFIG, quoteIdentifier, EMPTY_REQUIRED_COLUMNS, addRequiredColumn, mapSelectiveSystemFieldToColumn, markFieldRefAsRequired, findSelectivePropsFieldForFieldRef, markSelectiveFieldAsRequired, NODE_COLUMNS, shouldProjectColumn, isIdFieldRef, isAggregateExpr, EDGE_COLUMNS } from './chunk-RAXU3RMB.js';
2
- import { rowToNode, executeNodeBulkFindByIndex, executeNodeBulkFindByConstraint, executeNodeFindByConstraint, executeNodeBulkGetOrCreateByConstraint, executeNodeGetOrCreateByConstraint, executeNodeHardDelete, executeNodeDelete, nodeUpsertDirtyCheck, executeNodeUpsertUpdate, executeNodeUpdate, executeNodeCreateNoReturnBatch, executeNodeCreateBatch, executeNodeCreate, rowToEdge, runInWriteTransaction, checkCardinalityConstraint, runHookedWriteOperation, rowToNodeMeta, rowToEdgeMeta } from './chunk-DN5VP4JC.js';
3
- import { loadAndMergeGraphExtensionDocument, ensureSchema, loadAndVerifyGraph, buildKindRegistry, mergeGraphExtension, commitNewSchemaVersion, applyDeprecatedKinds, loadActiveSchemaWithBootstrap, parseSerializedSchema, extensionKindNames, serializeSchemaProperties, buildGraphExtensionOntologyKeySet, compileTimeOntologyKey, getTypeName, serializeIndexDeclaration } from './chunk-M75SUBFG.js';
4
- import { isKnownKind, validateEdgeProps } from './chunk-SKCOUWAX.js';
1
+ import { createEdgeRowMapper, createNodeRowMapper, POSTGRES_ROW_MAPPER_CONFIG, SQLITE_ROW_MAPPER_CONFIG, quoteIdentifier, EMPTY_REQUIRED_COLUMNS, addRequiredColumn, mapSelectiveSystemFieldToColumn, markFieldRefAsRequired, findSelectivePropsFieldForFieldRef, markSelectiveFieldAsRequired, NODE_COLUMNS, shouldProjectColumn, isIdFieldRef, isAggregateExpr, EDGE_COLUMNS } from './chunk-M6VES7V6.js';
2
+ import { rowToNode, executeNodeBulkFindByIndex, executeNodeBulkFindByConstraint, executeNodeFindByConstraint, executeNodeBulkGetOrCreateByConstraint, executeNodeGetOrCreateByConstraint, executeNodeHardDelete, executeNodeDelete, nodeUpsertDirtyCheck, executeNodeUpsertUpdate, executeNodeUpdate, executeNodeCreateNoReturnBatch, executeNodeCreateBatch, executeNodeCreate, rowToEdge, runInWriteTransaction, checkCardinalityConstraint, runHookedWriteOperation, rowToNodeMeta, rowToEdgeMeta } from './chunk-RCVLFTCT.js';
3
+ import { loadAndMergeGraphExtensionDocument, ensureSchema, loadAndVerifyGraph, buildKindRegistry, mergeGraphExtension, commitNewSchemaVersion, applyDeprecatedKinds, loadActiveSchemaWithBootstrap, parseSerializedSchema, extensionKindNames, serializeSchemaProperties, buildGraphExtensionOntologyKeySet, compileTimeOntologyKey, getTypeName, serializeIndexDeclaration } from './chunk-3RWZPJKV.js';
4
+ import { isKnownKind, validateEdgeProps } from './chunk-3SLPDKHQ.js';
5
5
  import { canonicalEqual, sortedReplacer } from './chunk-UR7W5YQK.js';
6
- import { generateIndexDDL } from './chunk-UZV3AZKF.js';
7
- import { NODE_META_KEYS, EDGE_META_KEYS } from './chunk-OR5ZA5WP.js';
6
+ import { generateIndexDDL } from './chunk-KQJ54OG5.js';
7
+ import { SYSTEM_INDEX_DECLARATIONS, resolveSystemIndexTableName, systemIndexName, resolveSystemIndexNames, generateSystemIndexDDL } from './chunk-YTSN73ND.js';
8
+ import { NODE_META_KEYS, EDGE_META_KEYS } from './chunk-K5UBVGJN.js';
8
9
  import { sha256Hex, fnv1aBase36 } from './chunk-W63OVYY4.js';
9
- import { getDialect, assertVectorMinScore, vectorScoreExpression, vectorMinScoreCondition } from './chunk-FPIZFYHE.js';
10
- import { createSchemaIntrospector } from './chunk-5SEGFTZX.js';
11
- import { IncompatibleChangeError, RemoveCompileTimeKindError, KindHasReferentsError } from './chunk-SXGE7ITL.js';
12
- import { validateProjectionField } from './chunk-S5F7ZLP2.js';
10
+ import { getDialect, assertVectorMinScore, vectorScoreExpression, vectorMinScoreCondition } from './chunk-EXU3RASM.js';
11
+ import { createSchemaIntrospector } from './chunk-UGZUAYAR.js';
12
+ import { IncompatibleChangeError, RemoveCompileTimeKindError, KindHasReferentsError } from './chunk-L7HQ2OSI.js';
13
+ import { validateProjectionField } from './chunk-TPF25IG6.js';
13
14
  import { compareStrings, compareCodePoints } from './chunk-BYPOGQWK.js';
14
- import { resolveReadCoordinate, withRecordedCoordinate, describeCoordinate, coordinateContext, DEFAULT_SQL_SCHEMA, requireSqlSchema, recordedReadSchemaFor, assertRevisionTrackableBackend, createSqlSchema, requireExternalRecordedReadSource, createRecordedReadBinding, createRecordedBackend, readRecordedClock, asRecordedInstant, ensureRevisionOrigin, withRecordedFlushObserver, recordedCaptureRequiresCallbackTransactionError, assertRecordedCaptureTransactionIsolation, createRecordedTransactionScope, lockRecordedGraphWrite, createHistoryUnsafeSqlRef, createRevisionTrackingUnsafeSqlRef, generateId, vectorSlotKey, computeFulltextContent, advanceRevisionClock, recordedReadSqlSchema, requireRecordedReadBinding, runInsertBatch, edgeInsertDispatch, runInsertBatchReturning, getEmbeddingFields, getNodeRowsByIds, getEdgeRowsByIds, assertValidRecordedInstant, validateEdgeEndpoints, recordedBindParamBudget, closeRecordedHardDeletedKind, ALIAS_CTE_PREFIX, EMBEDDINGS_CTE_ALIAS, FULLTEXT_CTE_ALIAS, HYBRID_CANDIDATES_CTE_ALIAS } from './chunk-5VTR2F2K.js';
15
- import { markAnnIndexScan, asCompiledSelectSql, isRawExecutable, annIndexScanTypes, asCompiledRowsSql, markForceCustomPlan, asCompiledStatementSql } from './chunk-UT3S4SEN.js';
16
- import { isParameterRef, fieldRef, buildFieldBuilderForTypeInfo, stringField, createFulltextAccessor, CURRENT_READ_INSTANT_PLACEHOLDER, withPinnedReadInstant, runOptionallyInTransaction, baseField, objectField, arrayField, dateField, numberField, rowPropsToObject, createBackendOverlay, compileKindFilter, currentReadInstant, compileTemporalFilter, sqlValueList, chunk, buildPredicateIndex, compileFieldValue, extractVectorSimilarityPredicates, extractFulltextMatchPredicates, currentReadInstantFor, extractTemporalOptions, compilePredicateExpression, getPredicatesForAlias, getNodeKindsForAlias, getHybridTargetAlias, compilePredicateClauses } from './chunk-IU46OHVW.js';
17
- import { withRecordedRelationsPrecondition, nowIso, validateOptionalCanonicalIsoDate, isMissingTableError } from './chunk-JFUPDJXD.js';
15
+ import { resolveReadCoordinate, withRecordedCoordinate, describeCoordinate, coordinateContext, DEFAULT_SQL_SCHEMA, requireSqlSchema, recordedReadSchemaFor, assertRevisionTrackableBackend, createSqlSchema, requireExternalRecordedReadSource, createRecordedReadBinding, createRecordedBackend, readRecordedClock, asRecordedInstant, ensureRevisionOrigin, withRecordedFlushObserver, recordedCaptureRequiresCallbackTransactionError, assertRecordedCaptureTransactionIsolation, createRecordedTransactionScope, lockRecordedGraphWrite, createHistoryUnsafeSqlRef, createRevisionTrackingUnsafeSqlRef, generateId, vectorSlotKey, computeFulltextContent, advanceRevisionClock, recordedReadSqlSchema, requireRecordedReadBinding, runInsertBatch, edgeInsertDispatch, runInsertBatchReturning, getEmbeddingFields, getNodeRowsByIds, getEdgeRowsByIds, validateEdgeEndpoints, recordedBindParamBudget, assertValidRecordedInstant, closeRecordedHardDeletedKind, ALIAS_CTE_PREFIX, EMBEDDINGS_CTE_ALIAS, FULLTEXT_CTE_ALIAS, HYBRID_CANDIDATES_CTE_ALIAS } from './chunk-VKGKDOKR.js';
16
+ import { isParameterRef, fieldRef, buildFieldBuilderForTypeInfo, stringField, createFulltextAccessor, CURRENT_READ_INSTANT_PLACEHOLDER, withPinnedReadInstant, markAnnIndexScan, asCompiledSelectSql, isRawExecutable, runOptionallyInTransaction, baseField, objectField, arrayField, dateField, numberField, annIndexScanTypes, rowPropsToObject, asCompiledRowsSql, sqlValueList, createBackendOverlay, compileKindFilter, currentReadInstant, compileTemporalFilter, markForceCustomPlan, chunk, INTERNAL_TEMPORARY_WRITES, buildPredicateIndex, compileFieldValue, asCompiledTemporaryStatementSql, extractVectorSimilarityPredicates, extractFulltextMatchPredicates, currentReadInstantFor, extractTemporalOptions, compilePredicateExpression, getPredicatesForAlias, getNodeKindsForAlias, getHybridTargetAlias, asCompiledStatementSql, compilePredicateClauses, isPresent } from './chunk-44TA22QT.js';
17
+ import { withRecordedRelationsPrecondition, nowIso, validateOptionalCanonicalIsoDate, isMissingTableError } from './chunk-XGPUQ56G.js';
18
18
  import { DEFAULT_PAGINATION_LIMIT, DEFAULT_STREAM_BATCH_SIZE, SQLITE_PATH_DELIMITER, PG_ARRAY_START, PG_ARRAY_END, PG_PATH_ELEMENT_SEPARATOR, MAX_PG_IDENTIFIER_LENGTH } from './chunk-OHMP6N2J.js';
19
- import { getSearchableFields, resolveEmbeddingFields, resolveDeclaredFulltextLanguage } from './chunk-KBGQIPRQ.js';
20
- import { ConfigurationError, UnsupportedPredicateError, ValidationError, KindNotFoundError, EndpointError, EagerMaterializationError, CompilerInvariantError, CardinalityError, EdgeNotFoundError, DatabaseOperationError, EndpointNotFoundError } from './chunk-WI7ISNQF.js';
19
+ import { getSearchableFields, resolveEmbeddingFields, resolveGraphVectorSlots, resolveDeclaredFulltextLanguage } from './chunk-PC2UBAUB.js';
20
+ import { ConfigurationError, UnsupportedPredicateError, ValidationError, KindNotFoundError, EndpointError, EagerMaterializationError, CompilerInvariantError, CardinalityError, EdgeNotFoundError, DatabaseOperationError, UnsupportedBackendCapabilityError, GraphAlgorithmConvergenceError, EndpointNotFoundError, InvalidEdgeWeightError } from './chunk-5PNMDJ7J.js';
21
21
  import { jsonPointer, parseJsonPointer, resolveJsonPointer } from './chunk-G6Q5UM7U.js';
22
22
  import { Placeholder, sql } from 'drizzle-orm';
23
23
 
@@ -394,7 +394,8 @@ function recordedSearch(coordinate) {
394
394
  var CoordinatePinnedView = class {
395
395
  store;
396
396
  coordinate;
397
- #algorithms;
397
+ #algorithmFacade;
398
+ #internalAlgorithms;
398
399
  constructor(store, coordinate) {
399
400
  this.store = store;
400
401
  this.coordinate = coordinate;
@@ -415,9 +416,24 @@ var CoordinatePinnedView = class {
415
416
  query() {
416
417
  return this.store.sealedQuery(this.coordinate);
417
418
  }
418
- algorithms() {
419
- this.#algorithms ??= this.store.algorithmsAtCoordinate(this.coordinate);
420
- return this.#algorithms;
419
+ internalAlgorithms() {
420
+ this.#internalAlgorithms ??= this.store.algorithmsAtCoordinate(
421
+ this.coordinate
422
+ );
423
+ return this.#internalAlgorithms;
424
+ }
425
+ /** Graph algorithms pinned to this view's immutable temporal coordinate. */
426
+ get algorithms() {
427
+ this.#algorithmFacade ??= Object.freeze({
428
+ shortestPath: (from, to, options) => this.shortestPath(from, to, options),
429
+ weightedShortestPath: (from, to, options) => this.weightedShortestPath(from, to, options),
430
+ reachable: (from, options) => this.reachable(from, options),
431
+ canReach: (from, to, options) => this.canReach(from, to, options),
432
+ neighbors: (node, options) => this.neighbors(node, options),
433
+ degree: (node, options) => this.degree(node, options),
434
+ weaklyConnectedComponents: (options) => this.weaklyConnectedComponents(options)
435
+ });
436
+ return this.#algorithmFacade;
421
437
  }
422
438
  /** Extracts a subgraph at this view's pinned coordinate. */
423
439
  subgraph(rootId, options) {
@@ -429,35 +445,49 @@ var CoordinatePinnedView = class {
429
445
  }
430
446
  /** Shortest path between two nodes at this view's pinned coordinate. */
431
447
  shortestPath(from, to, options) {
432
- return this.algorithms().shortestPath(from, to, {
448
+ return this.internalAlgorithms().shortestPath(from, to, {
449
+ ...options,
450
+ ...withCoordinate(this.coordinate)
451
+ });
452
+ }
453
+ /** Minimum-total-weight path at this view's pinned coordinate. */
454
+ weightedShortestPath(from, to, options) {
455
+ return this.internalAlgorithms().weightedShortestPath(from, to, {
433
456
  ...options,
434
457
  ...withCoordinate(this.coordinate)
435
458
  });
436
459
  }
437
460
  /** Nodes reachable from `from` at this view's pinned coordinate. */
438
461
  reachable(from, options) {
439
- return this.algorithms().reachable(from, {
462
+ return this.internalAlgorithms().reachable(from, {
440
463
  ...options,
441
464
  ...withCoordinate(this.coordinate)
442
465
  });
443
466
  }
444
467
  /** Whether `to` is reachable from `from` at this view's pinned coordinate. */
445
468
  canReach(from, to, options) {
446
- return this.algorithms().canReach(from, to, {
469
+ return this.internalAlgorithms().canReach(from, to, {
447
470
  ...options,
448
471
  ...withCoordinate(this.coordinate)
449
472
  });
450
473
  }
451
474
  /** The k-hop neighborhood of `node` at this view's pinned coordinate. */
452
475
  neighbors(node, options) {
453
- return this.algorithms().neighbors(node, {
476
+ return this.internalAlgorithms().neighbors(node, {
454
477
  ...options,
455
478
  ...withCoordinate(this.coordinate)
456
479
  });
457
480
  }
458
481
  /** Counts active edges incident to `node` at this view's pinned coordinate. */
459
482
  degree(node, options) {
460
- return this.algorithms().degree(node, {
483
+ return this.internalAlgorithms().degree(node, {
484
+ ...options,
485
+ ...withCoordinate(this.coordinate)
486
+ });
487
+ }
488
+ /** Exact WCC memberships at this view's pinned coordinate. */
489
+ weaklyConnectedComponents(options) {
490
+ return this.internalAlgorithms().weaklyConnectedComponents({
461
491
  ...options,
462
492
  ...withCoordinate(this.coordinate)
463
493
  });
@@ -1457,6 +1487,97 @@ function buildStandardOrderBy(input) {
1457
1487
  }
1458
1488
  return sql`ORDER BY ${sql.join(parts, sql`, `)}`;
1459
1489
  }
1490
+ var LATE_MAT_TOPK_CTE_ALIAS = "cte_lm_topk";
1491
+ var LATE_MAT_SORT_KEY_PREFIX = "__lm_sk";
1492
+ var LATE_MAT_PHYSICAL_ALIAS_PREFIX = "lm_";
1493
+ function lateMaterializedPhysicalAlias(alias) {
1494
+ return `${LATE_MAT_PHYSICAL_ALIAS_PREFIX}${alias}`;
1495
+ }
1496
+ function lateMaterializedNodeAliases(ast) {
1497
+ return [
1498
+ ast.start.alias,
1499
+ ...ast.traversals.map((traversal) => traversal.nodeAlias)
1500
+ ];
1501
+ }
1502
+ function lateMaterializedProjectedNodeAliases(ast) {
1503
+ const referenced = /* @__PURE__ */ new Set();
1504
+ for (const field2 of ast.selectiveFields ?? []) {
1505
+ referenced.add(field2.alias);
1506
+ }
1507
+ return lateMaterializedNodeAliases(ast).filter(
1508
+ (alias) => referenced.has(alias)
1509
+ );
1510
+ }
1511
+ function buildLateMaterializedTopKCte(input) {
1512
+ const { ast, collapsedTraversalCteAlias, dialect, fromClause } = input;
1513
+ const aliasToCte = buildAliasToCteMap(ast);
1514
+ const cteAliasFor = (alias) => collapsedTraversalCteAlias ?? aliasToCte.get(alias) ?? `${ALIAS_CTE_PREFIX}${alias}`;
1515
+ const columns = [];
1516
+ for (const alias of lateMaterializedNodeAliases(ast)) {
1517
+ const cteAlias = cteAliasFor(alias);
1518
+ columns.push(
1519
+ sql`${qualifyColumn(cteAlias, `${alias}_id`)} AS ${sql.raw(`${alias}_id`)}`,
1520
+ sql`${qualifyColumn(cteAlias, `${alias}_kind`)} AS ${sql.raw(`${alias}_kind`)}`
1521
+ );
1522
+ }
1523
+ for (const [index, orderSpec] of (ast.orderBy ?? []).entries()) {
1524
+ const value = compileOrderFieldValue(
1525
+ ast,
1526
+ orderSpec.field,
1527
+ dialect,
1528
+ cteAliasFor(orderSpec.field.alias)
1529
+ );
1530
+ columns.push(
1531
+ sql`${value} AS ${sql.raw(`${LATE_MAT_SORT_KEY_PREFIX}${index}`)}`
1532
+ );
1533
+ }
1534
+ const innerOrderBy = buildStandardOrderBy({
1535
+ ast,
1536
+ dialect,
1537
+ ...collapsedTraversalCteAlias === void 0 ? {} : { collapsedTraversalCteAlias }
1538
+ });
1539
+ const limitOffset = buildLimitOffsetClause({
1540
+ limit: input.limit,
1541
+ offset: input.offset
1542
+ });
1543
+ const parts = [sql`SELECT ${sql.join(columns, sql`, `)}`, fromClause];
1544
+ if (innerOrderBy !== void 0) parts.push(innerOrderBy);
1545
+ if (limitOffset !== void 0) parts.push(limitOffset);
1546
+ return sql`${sql.raw(LATE_MAT_TOPK_CTE_ALIAS)} AS (${sql.join(parts, sql` `)})`;
1547
+ }
1548
+ function buildLateMaterializedOuterProjection(ast, dialect) {
1549
+ const columns = (ast.selectiveFields ?? []).map((field2) => {
1550
+ const physicalAlias = lateMaterializedPhysicalAlias(field2.alias);
1551
+ if (field2.isSystemField) {
1552
+ const dbColumn = mapSelectiveSystemFieldToColumn(field2.field);
1553
+ return sql`${compileColumnReference(physicalAlias, dbColumn)} AS ${quoteIdentifier(field2.outputName)}`;
1554
+ }
1555
+ const extracted = compileSelectivePropsExtraction(
1556
+ field2,
1557
+ compileColumnReference(physicalAlias, "props"),
1558
+ dialect
1559
+ );
1560
+ return sql`${extracted} AS ${quoteIdentifier(field2.outputName)}`;
1561
+ });
1562
+ return sql.join(columns, sql`, `);
1563
+ }
1564
+ function buildLateMaterializedOuterOrderBy(ast) {
1565
+ const orderBy = ast.orderBy ?? [];
1566
+ if (orderBy.length === 0) return void 0;
1567
+ const topk = sql.raw(LATE_MAT_TOPK_CTE_ALIAS);
1568
+ const parts = [];
1569
+ for (const [index, orderSpec] of orderBy.entries()) {
1570
+ const column = sql`${topk}.${sql.raw(`${LATE_MAT_SORT_KEY_PREFIX}${index}`)}`;
1571
+ const direction = sql.raw(orderSpec.direction.toUpperCase());
1572
+ const nulls = orderSpec.nulls ?? (orderSpec.direction === "asc" ? "last" : "first");
1573
+ const nullsDirection = sql.raw(nulls === "first" ? "DESC" : "ASC");
1574
+ parts.push(
1575
+ sql`(${column} IS NULL) ${nullsDirection}`,
1576
+ sql`${column} ${direction}`
1577
+ );
1578
+ }
1579
+ return sql`ORDER BY ${sql.join(parts, sql`, `)}`;
1580
+ }
1460
1581
  function fieldRefKey(field2) {
1461
1582
  const pointer = field2.jsonPointer ?? "";
1462
1583
  return `${field2.alias}:${field2.path.join(".")}:${pointer}`;
@@ -3611,31 +3732,34 @@ function markPredicateFieldsAsRequired(requiredColumnsByAlias, expression) {
3611
3732
  }
3612
3733
  }
3613
3734
  }
3614
- function collectRequiredColumnsByAlias2(ast) {
3735
+ function collectRequiredColumnsByAlias2(ast, options) {
3736
+ const includeProjection = options?.includeProjection ?? true;
3615
3737
  const requiredColumnsByAlias = /* @__PURE__ */ new Map();
3616
3738
  addRequiredColumn(requiredColumnsByAlias, ast.start.alias, "id");
3617
3739
  for (const traversal of ast.traversals) {
3618
3740
  addRequiredColumn(requiredColumnsByAlias, traversal.nodeAlias, "id");
3619
3741
  }
3620
3742
  const hasSelectiveFields = (ast.selectiveFields?.length ?? 0) > 0;
3621
- if (hasSelectiveFields) {
3622
- for (const field2 of ast.selectiveFields ?? []) {
3623
- if (field2.isSystemField) {
3624
- addRequiredColumn(
3625
- requiredColumnsByAlias,
3626
- field2.alias,
3627
- mapSelectiveSystemFieldToColumn(field2.field)
3628
- );
3743
+ if (includeProjection) {
3744
+ if (hasSelectiveFields) {
3745
+ for (const field2 of ast.selectiveFields ?? []) {
3746
+ if (field2.isSystemField) {
3747
+ addRequiredColumn(
3748
+ requiredColumnsByAlias,
3749
+ field2.alias,
3750
+ mapSelectiveSystemFieldToColumn(field2.field)
3751
+ );
3752
+ }
3629
3753
  }
3630
- }
3631
- } else {
3632
- for (const projectedField of ast.projection.fields) {
3633
- const source = projectedField.source;
3634
- if (source.__type === "field_ref") {
3635
- markFieldRefAsRequired(requiredColumnsByAlias, source);
3636
- } else {
3637
- addRequiredColumn(requiredColumnsByAlias, source.field.alias, "id");
3638
- markFieldRefAsRequired(requiredColumnsByAlias, source.field);
3754
+ } else {
3755
+ for (const projectedField of ast.projection.fields) {
3756
+ const source = projectedField.source;
3757
+ if (source.__type === "field_ref") {
3758
+ markFieldRefAsRequired(requiredColumnsByAlias, source);
3759
+ } else {
3760
+ addRequiredColumn(requiredColumnsByAlias, source.field.alias, "id");
3761
+ markFieldRefAsRequired(requiredColumnsByAlias, source.field);
3762
+ }
3639
3763
  }
3640
3764
  }
3641
3765
  }
@@ -4318,6 +4442,169 @@ function stripLimitOffsetFromPlanNode(node) {
4318
4442
  }
4319
4443
  return node;
4320
4444
  }
4445
+ function buildStandardStartAndTraversalCtes(input) {
4446
+ const {
4447
+ ast,
4448
+ carryForwardPreviousColumns,
4449
+ ctx,
4450
+ graphId,
4451
+ predicateIndex,
4452
+ requiredColumnsByAlias,
4453
+ temporalFilterPass,
4454
+ traversalLimit
4455
+ } = input;
4456
+ const ctes = [
4457
+ buildStandardStartCte({
4458
+ ast,
4459
+ ctx,
4460
+ graphId,
4461
+ predicateIndex,
4462
+ requiredColumnsByAlias,
4463
+ temporalFilterPass
4464
+ })
4465
+ ];
4466
+ for (let index = 0; index < ast.traversals.length; index++) {
4467
+ ctes.push(
4468
+ buildStandardTraversalCte({
4469
+ ast,
4470
+ carryForwardPreviousColumns,
4471
+ ctx,
4472
+ graphId,
4473
+ materializeCte: shouldMaterializeTraversalCte(
4474
+ ctx.dialect,
4475
+ ast.traversals.length,
4476
+ index
4477
+ ),
4478
+ predicateIndex,
4479
+ requiredColumnsByAlias,
4480
+ temporalFilterPass,
4481
+ traversalIndex: index,
4482
+ traversalLimit
4483
+ })
4484
+ );
4485
+ }
4486
+ return ctes;
4487
+ }
4488
+ function selectiveFieldIsIdentity(field2) {
4489
+ if (!field2.isSystemField) {
4490
+ return false;
4491
+ }
4492
+ const column = mapSelectiveSystemFieldToColumn(field2.field);
4493
+ return column === "id" || column === "kind";
4494
+ }
4495
+ function resolveLateMaterializationPlan(ast) {
4496
+ if (ast.recordedAsOf !== void 0) {
4497
+ return void 0;
4498
+ }
4499
+ const selectiveFields = ast.selectiveFields;
4500
+ if (selectiveFields === void 0 || selectiveFields.length === 0) {
4501
+ return void 0;
4502
+ }
4503
+ const orderBy = ast.orderBy;
4504
+ if (orderBy === void 0 || orderBy.length === 0) {
4505
+ return void 0;
4506
+ }
4507
+ if (ast.limit === void 0 || ast.limit <= 0) {
4508
+ return void 0;
4509
+ }
4510
+ if (ast.groupBy !== void 0 || ast.having !== void 0) {
4511
+ return void 0;
4512
+ }
4513
+ if (ast.aggregateOrderBy !== void 0 && ast.aggregateOrderBy.length > 0) {
4514
+ return void 0;
4515
+ }
4516
+ if (ast.traversals.some((traversal) => traversal.optional)) {
4517
+ return void 0;
4518
+ }
4519
+ const nodeAliases = /* @__PURE__ */ new Set([
4520
+ ast.start.alias,
4521
+ ...ast.traversals.map((traversal) => traversal.nodeAlias)
4522
+ ]);
4523
+ for (const field2 of selectiveFields) {
4524
+ if (!nodeAliases.has(field2.alias)) {
4525
+ return void 0;
4526
+ }
4527
+ }
4528
+ for (const orderSpec of orderBy) {
4529
+ if (!nodeAliases.has(orderSpec.field.alias)) {
4530
+ return void 0;
4531
+ }
4532
+ }
4533
+ const orderReferenced = /* @__PURE__ */ new Set();
4534
+ for (const orderSpec of orderBy) {
4535
+ const matched = findSelectivePropsFieldForFieldRef(
4536
+ selectiveFields,
4537
+ orderSpec.field
4538
+ );
4539
+ if (matched !== void 0) {
4540
+ orderReferenced.add(matched);
4541
+ }
4542
+ }
4543
+ const hasDeferredColumn = selectiveFields.some(
4544
+ (field2) => !selectiveFieldIsIdentity(field2) && !orderReferenced.has(field2)
4545
+ );
4546
+ if (!hasDeferredColumn) {
4547
+ return void 0;
4548
+ }
4549
+ const prunedSelectiveFields = selectiveFields.filter(
4550
+ (field2) => field2.isSystemField || orderReferenced.has(field2)
4551
+ );
4552
+ return {
4553
+ leanAst: { ...ast, selectiveFields: prunedSelectiveFields },
4554
+ limit: ast.limit,
4555
+ offset: ast.offset
4556
+ };
4557
+ }
4558
+ function buildLateMaterializedOuterFromClause(ast, graphId, ctx) {
4559
+ const topk = sql.raw(LATE_MAT_TOPK_CTE_ALIAS);
4560
+ const joins = lateMaterializedProjectedNodeAliases(ast).map((alias) => {
4561
+ const physical = sql.raw(lateMaterializedPhysicalAlias(alias));
4562
+ return sql`JOIN ${ctx.schema.nodesTable} ${physical} ON ${physical}.graph_id = ${graphId} AND ${physical}.kind = ${topk}.${sql.raw(`${alias}_kind`)} AND ${physical}.id = ${topk}.${sql.raw(`${alias}_id`)}`;
4563
+ });
4564
+ return joins.length === 0 ? sql`FROM ${topk}` : sql`FROM ${topk} ${sql.join(joins, sql` `)}`;
4565
+ }
4566
+ function compileLateMaterializedQuery(ast, graphId, ctx, logicalPlan, predicateIndex, temporalFilterPass, collapsedTraversalCteAlias, shouldCollapseSelectiveTraversalRowset) {
4567
+ const plan = resolveLateMaterializationPlan(ast);
4568
+ if (plan === void 0) {
4569
+ return void 0;
4570
+ }
4571
+ const { leanAst, limit, offset } = plan;
4572
+ const { dialect } = ctx;
4573
+ const leanRequiredColumns = collectRequiredColumnsByAlias2(leanAst, {
4574
+ includeProjection: false
4575
+ });
4576
+ const ctes = buildStandardStartAndTraversalCtes({
4577
+ ast: leanAst,
4578
+ carryForwardPreviousColumns: shouldCollapseSelectiveTraversalRowset,
4579
+ ctx,
4580
+ graphId,
4581
+ predicateIndex,
4582
+ requiredColumnsByAlias: leanRequiredColumns,
4583
+ temporalFilterPass,
4584
+ traversalLimit: void 0
4585
+ });
4586
+ ctes.push(
4587
+ buildLateMaterializedTopKCte({
4588
+ ast: leanAst,
4589
+ dialect,
4590
+ fromClause: buildStandardFromClause({
4591
+ ast: leanAst,
4592
+ ...collapsedTraversalCteAlias === void 0 ? {} : { collapsedTraversalCteAlias }
4593
+ }),
4594
+ limit,
4595
+ offset,
4596
+ ...collapsedTraversalCteAlias === void 0 ? {} : { collapsedTraversalCteAlias }
4597
+ })
4598
+ );
4599
+ const orderBy = buildLateMaterializedOuterOrderBy(ast);
4600
+ return emitStandardQuerySql({
4601
+ ctes,
4602
+ fromClause: buildLateMaterializedOuterFromClause(ast, graphId, ctx),
4603
+ ...orderBy === void 0 ? {} : { orderBy },
4604
+ logicalPlan: stripLimitOffsetFromPlan(logicalPlan),
4605
+ projection: buildLateMaterializedOuterProjection(ast, dialect)
4606
+ });
4607
+ }
4321
4608
  var STANDARD_QUERY_STRATEGY_HANDLERS = {
4322
4609
  cte_project: compileStandardQueryWithCteStrategy
4323
4610
  };
@@ -4417,38 +4704,30 @@ function compileStandardQueryWithCteStrategy(ast, graphId, ctx) {
4417
4704
  if (fastPathSql) {
4418
4705
  return fastPathSql;
4419
4706
  }
4420
- }
4421
- const ctes = [
4422
- buildStandardStartCte({
4707
+ const lateMaterializedSql = compileLateMaterializedQuery(
4423
4708
  ast,
4424
- ctx,
4425
4709
  graphId,
4710
+ ctx,
4711
+ logicalPlan,
4426
4712
  predicateIndex,
4427
- requiredColumnsByAlias,
4428
- temporalFilterPass
4429
- })
4430
- ];
4431
- for (let index = 0; index < ast.traversals.length; index++) {
4432
- const materializeTraversalCte = shouldMaterializeTraversalCte(
4433
- dialect,
4434
- ast.traversals.length,
4435
- index
4436
- );
4437
- ctes.push(
4438
- buildStandardTraversalCte({
4439
- ast,
4440
- carryForwardPreviousColumns: shouldCollapseSelectiveTraversalRowset,
4441
- ctx,
4442
- graphId,
4443
- materializeCte: materializeTraversalCte,
4444
- predicateIndex,
4445
- requiredColumnsByAlias,
4446
- temporalFilterPass,
4447
- traversalIndex: index,
4448
- traversalLimit: traversalCteLimit
4449
- })
4713
+ temporalFilterPass,
4714
+ collapsedTraversalCteAlias,
4715
+ shouldCollapseSelectiveTraversalRowset
4450
4716
  );
4717
+ if (lateMaterializedSql) {
4718
+ return lateMaterializedSql;
4719
+ }
4451
4720
  }
4721
+ const ctes = buildStandardStartAndTraversalCtes({
4722
+ ast,
4723
+ carryForwardPreviousColumns: shouldCollapseSelectiveTraversalRowset,
4724
+ ctx,
4725
+ graphId,
4726
+ predicateIndex,
4727
+ requiredColumnsByAlias,
4728
+ temporalFilterPass,
4729
+ traversalLimit: traversalCteLimit
4730
+ });
4452
4731
  if (vectorPredicate) {
4453
4732
  const nodeKinds = getNodeKindsForAlias(ast, vectorPredicate.field.alias);
4454
4733
  ctes.push(
@@ -10512,6 +10791,16 @@ function resolveMaxHops(rawMaxHops, fallback, optionName) {
10512
10791
  }
10513
10792
  return value;
10514
10793
  }
10794
+ function resolveMaxIterations(value, fallback, algorithm) {
10795
+ const maxIterations = value ?? fallback;
10796
+ if (!Number.isSafeInteger(maxIterations) || maxIterations < 1) {
10797
+ throw new ConfigurationError(
10798
+ `${algorithm} maxIterations must be a positive safe integer, got ${String(maxIterations)}.`,
10799
+ { maxIterations }
10800
+ );
10801
+ }
10802
+ return maxIterations;
10803
+ }
10515
10804
  function assertEdgeKinds(edges) {
10516
10805
  if (edges.length === 0) {
10517
10806
  throw new ConfigurationError(
@@ -10561,65 +10850,982 @@ function compileDirectionFilter(ctx, direction, nodeId, nodesTable) {
10561
10850
  }
10562
10851
  }
10563
10852
  }
10564
- async function executeReachable(ctx, sourceId, options) {
10565
- assertEdgeKinds(options.edges);
10566
- const maxHops = resolveMaxHops(
10567
- options.maxHops,
10568
- DEFAULT_ALGORITHM_MAX_HOPS,
10569
- "maxHops"
10570
- );
10571
- const cte = buildReachableCte({
10572
- graphId: ctx.graphId,
10573
- sourceId,
10574
- edgeKinds: options.edges,
10575
- maxHops,
10576
- direction: options.direction ?? "out",
10577
- cyclePolicy: options.cyclePolicy ?? "prevent",
10578
- includePath: false,
10579
- ...resolveTemporalOptions(ctx, options),
10580
- dialect: ctx.dialect,
10581
- schema: ctx.schema,
10582
- ...ctx.recordedReadBinding === void 0 ? {} : { recordedReadBinding: ctx.recordedReadBinding }
10583
- });
10584
- const sourceFilter = options.excludeSource === true ? sql` WHERE id != ${sourceId}` : sql``;
10585
- const query = sql`${cte} SELECT id, kind, MIN(depth) AS depth FROM reachable${sourceFilter} GROUP BY id, kind ORDER BY depth ASC, id ASC`;
10586
- const rows = await ctx.backend.execute(
10587
- asCompiledRowsSql(query)
10588
- );
10589
- return rows.map((row) => ({
10590
- id: row.id,
10591
- kind: row.kind,
10592
- depth: Number(row.depth)
10593
- }));
10853
+ var DEFAULT_MAX_BIND_PARAMETERS = 999;
10854
+ var RESERVED_TEMPORAL_BIND_PARAMETERS_PER_BRANCH = 12;
10855
+ var RESERVED_WEIGHT_BIND_PARAMETERS_PER_BRANCH = 4;
10856
+ var WORKING_TABLE_ANALYZE_MINIMUM_ROWS = 16;
10857
+ var WORKING_TABLE_ANALYZE_GROWTH_FACTOR = 4;
10858
+ var ITERATIVE_WORKING_MEMORY_PATTERN = /^(\d+)(kB|MB|GB)$/;
10859
+ var WORKING_MEMORY_KILOBYTES_PER_UNIT = {
10860
+ kB: 1,
10861
+ MB: 1024,
10862
+ GB: 1024 * 1024
10863
+ };
10864
+ var MIN_WORKING_MEMORY_KILOBYTES = 64;
10865
+ var MAX_WORKING_MEMORY_KILOBYTES = 2147483647;
10866
+ function resolveWorkingMemory(workingMemory) {
10867
+ if (workingMemory === void 0) return void 0;
10868
+ const match = ITERATIVE_WORKING_MEMORY_PATTERN.exec(workingMemory);
10869
+ if (match === null) {
10870
+ throw new ConfigurationError(
10871
+ `Iterative graph operation workingMemory must be digits followed by kB, MB, or GB (for example "64MB"), got ${JSON.stringify(workingMemory)}.`,
10872
+ { workingMemory }
10873
+ );
10874
+ }
10875
+ const kilobytes = Number(match[1]) * (WORKING_MEMORY_KILOBYTES_PER_UNIT[match[2] ?? ""] ?? 0);
10876
+ if (kilobytes < MIN_WORKING_MEMORY_KILOBYTES || kilobytes > MAX_WORKING_MEMORY_KILOBYTES) {
10877
+ throw new ConfigurationError(
10878
+ `Iterative graph operation workingMemory must be between ${MIN_WORKING_MEMORY_KILOBYTES}kB and ${MAX_WORKING_MEMORY_KILOBYTES}kB, got ${JSON.stringify(workingMemory)}.`,
10879
+ { workingMemory, kilobytes }
10880
+ );
10881
+ }
10882
+ return workingMemory;
10594
10883
  }
10595
- async function executeCanReach(ctx, sourceId, targetId, options) {
10596
- assertEdgeKinds(options.edges);
10597
- const maxHops = resolveMaxHops(
10598
- options.maxHops,
10599
- DEFAULT_ALGORITHM_MAX_HOPS,
10600
- "maxHops"
10601
- );
10602
- const cte = buildReachableCte({
10603
- graphId: ctx.graphId,
10604
- sourceId,
10605
- edgeKinds: options.edges,
10606
- maxHops,
10607
- direction: options.direction ?? "out",
10608
- cyclePolicy: options.cyclePolicy ?? "prevent",
10609
- includePath: false,
10610
- ...resolveTemporalOptions(ctx, options),
10611
- dialect: ctx.dialect,
10612
- schema: ctx.schema,
10613
- ...ctx.recordedReadBinding === void 0 ? {} : { recordedReadBinding: ctx.recordedReadBinding }
10614
- });
10615
- const query = sql`${cte} SELECT 1 AS hit FROM reachable WHERE id = ${targetId} LIMIT 1`;
10616
- const rows = await ctx.backend.execute(
10617
- asCompiledRowsSql(query)
10618
- );
10619
- return rows.length > 0;
10884
+ async function withInlineIterativeGraphOperation(ctx, options, run) {
10885
+ if (ctx.backend.capabilities.transactions) {
10886
+ return ctx.backend.transaction(
10887
+ async (backend) => run(createOperation(ctx, options, backend)),
10888
+ {
10889
+ isolationLevel: "repeatable_read",
10890
+ accessMode: "read_only"
10891
+ }
10892
+ );
10893
+ }
10894
+ return run(createOperation(ctx, options, ctx.backend));
10620
10895
  }
10621
- async function executeNeighbors(ctx, sourceId, options) {
10622
- const depth = resolveMaxHops(options.depth, DEFAULT_NEIGHBOR_DEPTH, "depth");
10896
+ async function runIterativeGraphOperation(ctx, options, plan) {
10897
+ if (!ctx.backend.capabilities.transactions) {
10898
+ throw new ConfigurationError(
10899
+ "Temporary-table graph iteration requires a transactional backend.",
10900
+ { dialect: ctx.backend.dialect }
10901
+ );
10902
+ }
10903
+ return ctx.backend.transaction(
10904
+ async (backend) => {
10905
+ const temporaryBackend = requireTemporaryStatements(backend);
10906
+ const runId = generateId();
10907
+ const workingTable = sql.identifier(
10908
+ `typegraph_iterative_${runId.replaceAll("-", "_")}`
10909
+ );
10910
+ const context = {
10911
+ operation: createOperation(ctx, options, temporaryBackend),
10912
+ backend: temporaryBackend,
10913
+ workingTable,
10914
+ graphId: ctx.graphId,
10915
+ runId,
10916
+ executeTemporary: async (query) => {
10917
+ await temporaryBackend.executeTemporaryStatement(
10918
+ asCompiledTemporaryStatementSql(query)
10919
+ );
10920
+ }
10921
+ };
10922
+ if (context.operation.workingMemory !== void 0) {
10923
+ const workingMemoryStatement = ctx.dialect.setTransactionWorkingMemory(
10924
+ context.operation.workingMemory
10925
+ );
10926
+ if (workingMemoryStatement !== void 0) {
10927
+ await context.executeTemporary(workingMemoryStatement);
10928
+ }
10929
+ }
10930
+ await context.executeTemporary(plan.createWorkingTable(context));
10931
+ let operationError;
10932
+ try {
10933
+ let state = await plan.initialize(context);
10934
+ let analyzedRowCount = await refreshWorkingTableStatistics(
10935
+ context,
10936
+ state.workingTableSize
10937
+ );
10938
+ for (let iteration = 1; iteration <= plan.maxIterations && !plan.hasConverged(state); iteration++) {
10939
+ state = await plan.runRound(context, state, iteration);
10940
+ if (iteration < plan.maxIterations && !plan.hasConverged(state)) {
10941
+ analyzedRowCount = await refreshWorkingTableStatistics(
10942
+ context,
10943
+ state.workingTableSize,
10944
+ analyzedRowCount
10945
+ );
10946
+ }
10947
+ }
10948
+ if (!plan.hasConverged(state)) {
10949
+ throw new GraphAlgorithmConvergenceError(
10950
+ plan.algorithm,
10951
+ plan.maxIterations
10952
+ );
10953
+ }
10954
+ return await plan.extractResult(context, state);
10955
+ } catch (error) {
10956
+ operationError = error;
10957
+ throw error;
10958
+ } finally {
10959
+ await dropWorkingTable(context, operationError);
10960
+ }
10961
+ },
10962
+ {
10963
+ isolationLevel: "repeatable_read",
10964
+ accessMode: "read_only",
10965
+ temporaryWrites: INTERNAL_TEMPORARY_WRITES
10966
+ }
10967
+ );
10968
+ }
10969
+ function shouldRefreshWorkingTableStatistics(workingTableSize, analyzedRowCount) {
10970
+ if (workingTableSize < WORKING_TABLE_ANALYZE_MINIMUM_ROWS) return false;
10971
+ if (analyzedRowCount === void 0) return true;
10972
+ return workingTableSize >= analyzedRowCount * WORKING_TABLE_ANALYZE_GROWTH_FACTOR;
10973
+ }
10974
+ async function refreshWorkingTableStatistics(context, workingTableSize, analyzedRowCount) {
10975
+ if (!shouldRefreshWorkingTableStatistics(workingTableSize, analyzedRowCount)) {
10976
+ return analyzedRowCount;
10977
+ }
10978
+ const statement = context.operation.ctx.dialect.analyzeTemporaryTable(
10979
+ context.workingTable
10980
+ );
10981
+ if (statement === void 0) return analyzedRowCount;
10982
+ await context.executeTemporary(statement);
10983
+ return workingTableSize;
10984
+ }
10985
+ async function dropWorkingTable(context, operationError) {
10986
+ try {
10987
+ await context.executeTemporary(
10988
+ sql`DROP TABLE IF EXISTS ${context.workingTable}`
10989
+ );
10990
+ } catch (cleanupError) {
10991
+ if (operationError === void 0) throw cleanupError;
10992
+ }
10993
+ }
10994
+ async function fetchVisibleWorkingNodes(operation, nodeIds) {
10995
+ const nodes = /* @__PURE__ */ new Map();
10996
+ for (const chunk2 of chunkValues(nodeIds, operation.maxWorkingSetSize)) {
10997
+ const query = sql`SELECT n.id, n.kind FROM ${operation.schema.nodesTable} n WHERE n.graph_id = ${operation.ctx.graphId} AND n.id IN (${sqlValueList(chunk2)}) AND ${operation.nodeTemporalFilter}`;
10998
+ const rows = await operation.backend.execute(
10999
+ asCompiledRowsSql(query)
11000
+ );
11001
+ for (const row of rows) nodes.set(nodeIdentityKey(row), row);
11002
+ }
11003
+ return [...nodes.values()].toSorted(
11004
+ (left, right) => compareNodeIdentity(left, right)
11005
+ );
11006
+ }
11007
+ async function reduceExpandedWorkingSet(operation, workingSet, direction, reduce) {
11008
+ const reduced = /* @__PURE__ */ new Map();
11009
+ for (const edgeKinds of operation.edgeKindChunks) {
11010
+ for (const chunk2 of chunkValues(workingSet, operation.maxWorkingSetSize)) {
11011
+ const rows = await operation.backend.execute(
11012
+ asCompiledRowsSql(
11013
+ compileExpansionQuery(operation, chunk2, direction, edgeKinds)
11014
+ )
11015
+ );
11016
+ for (const row of rows) {
11017
+ if (operation.weightExpression !== void 0 && !isPresent(row.weight)) {
11018
+ throw new CompilerInvariantError(
11019
+ "Weighted graph expansion produced a NULL weight. Either the plan skipped its weight audit, or edge data changed concurrently during a run on a backend without snapshot isolation.",
11020
+ { source: row.source_id, target: row.target_id }
11021
+ );
11022
+ }
11023
+ const expansion = {
11024
+ source: { id: row.source_id, kind: row.source_kind },
11025
+ target: { id: row.target_id, kind: row.target_kind },
11026
+ ...isPresent(row.weight) ? { weight: Number(row.weight) } : {}
11027
+ };
11028
+ const targetKey = nodeIdentityKey(expansion.target);
11029
+ const selected = reduce(reduced.get(targetKey), expansion);
11030
+ if (selected !== void 0) reduced.set(targetKey, selected);
11031
+ }
11032
+ }
11033
+ }
11034
+ return reduced;
11035
+ }
11036
+ function nodeIdentityKey(node) {
11037
+ return `${node.kind}\0${node.id}`;
11038
+ }
11039
+ function nodeIdentityKeyFromRow(id, kind) {
11040
+ if (typeof id !== "string" || typeof kind !== "string") return void 0;
11041
+ return nodeIdentityKey({ id, kind });
11042
+ }
11043
+ function compareNodeIdentity(left, right) {
11044
+ return compareStrings(left.id, right.id) || compareStrings(left.kind, right.kind);
11045
+ }
11046
+ function compileExpansionQuery(operation, workingSet, direction, edgeKinds) {
11047
+ const workingValues = sql.join(
11048
+ workingSet.map((node) => sql`(${node.id}, ${node.kind})`),
11049
+ sql`, `
11050
+ );
11051
+ const workingCte = sql`WITH working_set(node_id, node_kind) AS (VALUES ${workingValues})`;
11052
+ switch (direction) {
11053
+ case "out": {
11054
+ return sql`${workingCte} ${compileDirectionalExpansion(operation, sql`working_set`, sql`TRUE`, edgeKinds, "from_id", "from_kind", "to_id", "to_kind")}`;
11055
+ }
11056
+ case "in": {
11057
+ return sql`${workingCte} ${compileDirectionalExpansion(operation, sql`working_set`, sql`TRUE`, edgeKinds, "to_id", "to_kind", "from_id", "from_kind")}`;
11058
+ }
11059
+ case "both": {
11060
+ const outgoing = compileDirectionalExpansion(
11061
+ operation,
11062
+ sql`working_set`,
11063
+ sql`TRUE`,
11064
+ edgeKinds,
11065
+ "from_id",
11066
+ "from_kind",
11067
+ "to_id",
11068
+ "to_kind"
11069
+ );
11070
+ const incoming = compileDirectionalExpansion(
11071
+ operation,
11072
+ sql`working_set`,
11073
+ sql`TRUE`,
11074
+ edgeKinds,
11075
+ "to_id",
11076
+ "to_kind",
11077
+ "from_id",
11078
+ "from_kind"
11079
+ );
11080
+ return sql`${workingCte} ${outgoing} UNION ALL ${incoming}`;
11081
+ }
11082
+ }
11083
+ }
11084
+ function compileWorkingTableExpansion(operation, workingTable, sourceFilter, direction, edgeKinds) {
11085
+ switch (direction) {
11086
+ case "out": {
11087
+ return compileDirectionalExpansion(
11088
+ operation,
11089
+ workingTable,
11090
+ sourceFilter,
11091
+ edgeKinds,
11092
+ "from_id",
11093
+ "from_kind",
11094
+ "to_id",
11095
+ "to_kind"
11096
+ );
11097
+ }
11098
+ case "in": {
11099
+ return compileDirectionalExpansion(
11100
+ operation,
11101
+ workingTable,
11102
+ sourceFilter,
11103
+ edgeKinds,
11104
+ "to_id",
11105
+ "to_kind",
11106
+ "from_id",
11107
+ "from_kind"
11108
+ );
11109
+ }
11110
+ case "both": {
11111
+ const outgoing = compileDirectionalExpansion(
11112
+ operation,
11113
+ workingTable,
11114
+ sourceFilter,
11115
+ edgeKinds,
11116
+ "from_id",
11117
+ "from_kind",
11118
+ "to_id",
11119
+ "to_kind"
11120
+ );
11121
+ const incoming = compileDirectionalExpansion(
11122
+ operation,
11123
+ workingTable,
11124
+ sourceFilter,
11125
+ edgeKinds,
11126
+ "to_id",
11127
+ "to_kind",
11128
+ "from_id",
11129
+ "from_kind"
11130
+ );
11131
+ return sql`${outgoing} UNION ALL ${incoming}`;
11132
+ }
11133
+ }
11134
+ }
11135
+ function compileWorkingTableEdgeExpansion(operation, workingTable, sourceFilter, direction, edgeKinds) {
11136
+ switch (direction) {
11137
+ case "out": {
11138
+ return compileDirectionalEdgeExpansion(
11139
+ operation,
11140
+ workingTable,
11141
+ sourceFilter,
11142
+ edgeKinds,
11143
+ "from_id",
11144
+ "from_kind",
11145
+ "to_id",
11146
+ "to_kind"
11147
+ );
11148
+ }
11149
+ case "in": {
11150
+ return compileDirectionalEdgeExpansion(
11151
+ operation,
11152
+ workingTable,
11153
+ sourceFilter,
11154
+ edgeKinds,
11155
+ "to_id",
11156
+ "to_kind",
11157
+ "from_id",
11158
+ "from_kind"
11159
+ );
11160
+ }
11161
+ case "both": {
11162
+ const outgoing = compileDirectionalEdgeExpansion(
11163
+ operation,
11164
+ workingTable,
11165
+ sourceFilter,
11166
+ edgeKinds,
11167
+ "from_id",
11168
+ "from_kind",
11169
+ "to_id",
11170
+ "to_kind"
11171
+ );
11172
+ const incoming = compileDirectionalEdgeExpansion(
11173
+ operation,
11174
+ workingTable,
11175
+ sourceFilter,
11176
+ edgeKinds,
11177
+ "to_id",
11178
+ "to_kind",
11179
+ "from_id",
11180
+ "from_kind"
11181
+ );
11182
+ return sql`${outgoing} UNION ALL ${incoming}`;
11183
+ }
11184
+ }
11185
+ }
11186
+ function compileDirectionalExpansion(operation, workingRelation, sourceFilter, edgeKinds, joinField, joinKindField, targetField, targetKindField) {
11187
+ const edgeExpansion = compileDirectionalEdgeExpansion(
11188
+ operation,
11189
+ workingRelation,
11190
+ sourceFilter,
11191
+ edgeKinds,
11192
+ joinField,
11193
+ joinKindField,
11194
+ targetField,
11195
+ targetKindField
11196
+ );
11197
+ const weightColumn = operation.weightExpression === void 0 ? sql`` : sql`, expanded.weight`;
11198
+ return sql`SELECT expanded.source_id, expanded.source_kind, n.id AS target_id, n.kind AS target_kind${weightColumn} FROM (${edgeExpansion}) expanded JOIN ${operation.schema.nodesTable} n ON n.graph_id = ${operation.ctx.graphId} AND n.id = expanded.target_id AND n.kind = expanded.target_kind WHERE ${operation.nodeTemporalFilter}`;
11199
+ }
11200
+ function compileDirectionalEdgeExpansion(operation, workingRelation, sourceFilter, edgeKinds, joinField, joinKindField, targetField, targetKindField) {
11201
+ const edgeKindFilter = compileKindFilter(sql.raw("e.kind"), edgeKinds);
11202
+ const weightColumn = operation.weightExpression === void 0 ? sql`` : sql`, ${operation.weightExpression} AS weight`;
11203
+ return sql`SELECT w.node_id AS source_id, w.node_kind AS source_kind, e.${sql.raw(targetField)} AS target_id, e.${sql.raw(targetKindField)} AS target_kind${weightColumn} FROM ${workingRelation} w JOIN ${operation.schema.edgesTable} e ON e.${sql.raw(joinField)} = w.node_id AND e.${sql.raw(joinKindField)} = w.node_kind AND e.graph_id = ${operation.ctx.graphId} WHERE ${sourceFilter} AND ${edgeKindFilter} AND ${operation.edgeTemporalFilter}`;
11204
+ }
11205
+ function chunkValues(values, chunkSize) {
11206
+ const chunks = [];
11207
+ for (let offset = 0; offset < values.length; offset += chunkSize) {
11208
+ chunks.push(values.slice(offset, offset + chunkSize));
11209
+ }
11210
+ return chunks;
11211
+ }
11212
+ function createOperation(ctx, options, backend) {
11213
+ const temporal = resolveTemporalOptions(ctx, options);
11214
+ const currentTimestamp = currentReadInstant();
11215
+ const nodeTemporalFilter = compileTemporalFilter({
11216
+ mode: temporal.temporalMode,
11217
+ ...temporal.asOf === void 0 ? {} : { asOf: temporal.asOf },
11218
+ ...temporal.recordedAsOf === void 0 ? {} : { recordedAsOf: temporal.recordedAsOf },
11219
+ tableAlias: "n",
11220
+ currentTimestamp
11221
+ });
11222
+ const edgeTemporalFilter = compileTemporalFilter({
11223
+ mode: temporal.temporalMode,
11224
+ ...temporal.asOf === void 0 ? {} : { asOf: temporal.asOf },
11225
+ ...temporal.recordedAsOf === void 0 ? {} : { recordedAsOf: temporal.recordedAsOf },
11226
+ tableAlias: "e",
11227
+ currentTimestamp
11228
+ });
11229
+ const weightExpression = options.weightProperty === void 0 ? void 0 : compileWeightExpression(
11230
+ ctx.dialect,
11231
+ options.weightProperty,
11232
+ options.defaultWeight
11233
+ );
11234
+ const direction = options.direction ?? "out";
11235
+ const branchCount = direction === "both" ? 2 : 1;
11236
+ const parameterLimit = backend.capabilities.maxBindParameters ?? DEFAULT_MAX_BIND_PARAMETERS;
11237
+ const reservedPerBranch = RESERVED_TEMPORAL_BIND_PARAMETERS_PER_BRANCH + (weightExpression === void 0 ? 0 : RESERVED_WEIGHT_BIND_PARAMETERS_PER_BRANCH);
11238
+ const fixedParameters = branchCount * reservedPerBranch;
11239
+ const sharedBudget = parameterLimit - fixedParameters;
11240
+ const maxEdgeKindsPerQuery = Math.floor(sharedBudget / (branchCount + 2));
11241
+ if (maxEdgeKindsPerQuery < 1) {
11242
+ throw new ConfigurationError(
11243
+ "An iterative graph operation cannot fit its fixed filters within the backend bind-parameter limit.",
11244
+ { parameterLimit, direction }
11245
+ );
11246
+ }
11247
+ const edgeKindChunks = chunkValues(
11248
+ [...new Set(options.edges)].toSorted(
11249
+ (left, right) => compareStrings(left, right)
11250
+ ),
11251
+ maxEdgeKindsPerQuery
11252
+ );
11253
+ const largestEdgeKindChunk = Math.max(
11254
+ ...edgeKindChunks.map((chunk2) => chunk2.length)
11255
+ );
11256
+ const maxWorkingSetSize = Math.floor(
11257
+ (parameterLimit - branchCount * (largestEdgeKindChunk + reservedPerBranch)) / 2
11258
+ );
11259
+ if (maxWorkingSetSize < 1) {
11260
+ throw new ConfigurationError(
11261
+ "An iterative graph operation cannot fit one working-set node within the backend bind-parameter limit.",
11262
+ { parameterLimit, direction }
11263
+ );
11264
+ }
11265
+ return {
11266
+ backend,
11267
+ ctx,
11268
+ edgeKindChunks,
11269
+ direction,
11270
+ maxWorkingSetSize,
11271
+ nodeTemporalFilter,
11272
+ edgeTemporalFilter,
11273
+ schema: resolveReadSchema(ctx, options),
11274
+ workingMemory: resolveWorkingMemory(options.workingMemory),
11275
+ weightExpression
11276
+ };
11277
+ }
11278
+ function compileWeightExpression(dialect, weightProperty, defaultWeight) {
11279
+ const extracted = dialect.jsonExtractDouble(
11280
+ sql.raw("e.props"),
11281
+ jsonPointer([weightProperty])
11282
+ );
11283
+ return defaultWeight === void 0 ? extracted : sql`COALESCE(${extracted}, ${defaultWeight})`;
11284
+ }
11285
+ function supportsTemporaryIteration(ctx) {
11286
+ return ctx.backend.capabilities.transactions && ctx.backend.capabilities.returning !== false && ctx.backend.executeTemporaryStatement !== void 0;
11287
+ }
11288
+ function requireTemporaryStatements(backend) {
11289
+ if (backend.executeTemporaryStatement === void 0) {
11290
+ throw new ConfigurationError(
11291
+ "Iterative graph operations require temporary-statement support on the transaction backend.",
11292
+ { dialect: backend.dialect },
11293
+ {
11294
+ suggestion: "Use a built-in SQLite/PostgreSQL backend or implement executeTemporaryStatement on the custom backend."
11295
+ }
11296
+ );
11297
+ }
11298
+ return backend;
11299
+ }
11300
+
11301
+ // src/store/algorithms/breadth-first.ts
11302
+ async function findReachableNodes(ctx, sourceId, maxHops, options) {
11303
+ if (supportsTemporaryIteration(ctx)) {
11304
+ return findReachableNodesInWorkingTable(ctx, sourceId, maxHops, options);
11305
+ }
11306
+ return findReachableNodesInline(ctx, sourceId, maxHops, options);
11307
+ }
11308
+ async function findShortestPath(ctx, sourceId, targetId, maxHops, options) {
11309
+ if (supportsTemporaryIteration(ctx)) {
11310
+ return findShortestPathInWorkingTable(
11311
+ ctx,
11312
+ sourceId,
11313
+ targetId,
11314
+ maxHops,
11315
+ options
11316
+ );
11317
+ }
11318
+ return findShortestPathInline(ctx, sourceId, targetId, maxHops, options);
11319
+ }
11320
+ async function findReachableNodesInWorkingTable(ctx, sourceId, maxHops, options) {
11321
+ return runIterativeGraphOperation(ctx, options, {
11322
+ algorithm: "reachable",
11323
+ maxIterations: maxHops,
11324
+ createWorkingTable,
11325
+ async initialize(context) {
11326
+ const seeded = await seedWorkingSide(context, sourceId, "forward");
11327
+ return {
11328
+ depth: 0,
11329
+ frontierCount: seeded.length,
11330
+ workingTableSize: seeded.length
11331
+ };
11332
+ },
11333
+ async runRound(context, state) {
11334
+ const nextDepth = state.depth + 1;
11335
+ const frontierCount = await expandReachableWorkingTableRound(
11336
+ context,
11337
+ "forward",
11338
+ state.depth,
11339
+ nextDepth,
11340
+ context.operation.direction
11341
+ );
11342
+ return {
11343
+ depth: nextDepth,
11344
+ frontierCount,
11345
+ workingTableSize: state.workingTableSize + frontierCount
11346
+ };
11347
+ },
11348
+ hasConverged(state) {
11349
+ return state.frontierCount === 0 || state.depth >= maxHops;
11350
+ },
11351
+ async extractResult(context) {
11352
+ const rows = await readWorkingRows(context, "forward");
11353
+ return rows.map((row) => ({
11354
+ id: row.node_id,
11355
+ kind: row.node_kind,
11356
+ depth: Number(row.depth)
11357
+ })).toSorted(
11358
+ (left, right) => left.depth - right.depth || compareNodeIdentity(left, right)
11359
+ );
11360
+ }
11361
+ });
11362
+ }
11363
+ async function findShortestPathInWorkingTable(ctx, sourceId, targetId, maxHops, options) {
11364
+ return runIterativeGraphOperation(ctx, options, {
11365
+ algorithm: "shortestPath",
11366
+ maxIterations: maxHops,
11367
+ createWorkingTable,
11368
+ async initialize(context) {
11369
+ const forwardSeeds = await seedWorkingSide(context, sourceId, "forward");
11370
+ const reverseSeeds = await seedWorkingSide(context, targetId, "reverse");
11371
+ return {
11372
+ forwardDepth: 0,
11373
+ reverseDepth: 0,
11374
+ forwardFrontierCount: forwardSeeds.length,
11375
+ reverseFrontierCount: reverseSeeds.length,
11376
+ meeting: findSeedMeeting(forwardSeeds, reverseSeeds),
11377
+ workingTableSize: forwardSeeds.length + reverseSeeds.length
11378
+ };
11379
+ },
11380
+ async runRound(context, state) {
11381
+ const expandForward = state.forwardFrontierCount <= state.reverseFrontierCount;
11382
+ const side = expandForward ? "forward" : "reverse";
11383
+ const currentDepth = expandForward ? state.forwardDepth : state.reverseDepth;
11384
+ const nextDepth = currentDepth + 1;
11385
+ const direction = expandForward ? context.operation.direction : reverseDirection(context.operation.direction);
11386
+ const round = await expandWorkingTableRound(
11387
+ context,
11388
+ side,
11389
+ currentDepth,
11390
+ nextDepth,
11391
+ direction,
11392
+ maxHops
11393
+ );
11394
+ return {
11395
+ forwardDepth: expandForward ? nextDepth : state.forwardDepth,
11396
+ reverseDepth: expandForward ? state.reverseDepth : nextDepth,
11397
+ forwardFrontierCount: expandForward ? round.insertedCount : state.forwardFrontierCount,
11398
+ reverseFrontierCount: expandForward ? state.reverseFrontierCount : round.insertedCount,
11399
+ meeting: round.meeting,
11400
+ workingTableSize: state.workingTableSize + round.insertedCount
11401
+ };
11402
+ },
11403
+ hasConverged(state) {
11404
+ return state.meeting !== void 0 || state.forwardFrontierCount === 0 || state.reverseFrontierCount === 0 || state.forwardDepth + state.reverseDepth >= maxHops;
11405
+ },
11406
+ async extractResult(context, state) {
11407
+ if (state.meeting === void 0) return;
11408
+ const rows = await readWorkingRows(context);
11409
+ return buildShortestPath(
11410
+ nodeIdentityKey(state.meeting),
11411
+ createVisitedMapFromRows(rows, "forward"),
11412
+ createVisitedMapFromRows(rows, "reverse")
11413
+ );
11414
+ }
11415
+ });
11416
+ }
11417
+ function createWorkingTable(context) {
11418
+ return sql`
11419
+ CREATE TEMP TABLE ${context.workingTable} (
11420
+ graph_id TEXT NOT NULL,
11421
+ run_id TEXT NOT NULL,
11422
+ side TEXT NOT NULL,
11423
+ node_id TEXT NOT NULL,
11424
+ node_kind TEXT NOT NULL,
11425
+ depth INTEGER NOT NULL,
11426
+ predecessor_id TEXT,
11427
+ predecessor_kind TEXT,
11428
+ meeting_depth INTEGER,
11429
+ PRIMARY KEY (graph_id, run_id, side, node_kind, node_id)
11430
+ )
11431
+ `;
11432
+ }
11433
+ async function seedWorkingSide(context, nodeId, side) {
11434
+ const { operation, workingTable, graphId, runId } = context;
11435
+ return context.backend.execute(
11436
+ asCompiledRowsSql(sql`
11437
+ INSERT INTO ${workingTable}
11438
+ (graph_id, run_id, side, node_id, node_kind, depth,
11439
+ predecessor_id, predecessor_kind)
11440
+ SELECT ${graphId}, ${runId}, ${side}, n.id, n.kind, 0, NULL, NULL
11441
+ FROM ${operation.schema.nodesTable} n
11442
+ WHERE n.graph_id = ${graphId}
11443
+ AND n.id = ${nodeId}
11444
+ AND ${operation.nodeTemporalFilter}
11445
+ ON CONFLICT (graph_id, run_id, side, node_kind, node_id) DO NOTHING
11446
+ RETURNING node_id, node_kind
11447
+ `)
11448
+ );
11449
+ }
11450
+ function findSeedMeeting(forwardSeeds, reverseSeeds) {
11451
+ const reverseKeys = new Set(
11452
+ reverseSeeds.map(
11453
+ (row) => nodeIdentityKey({ id: row.node_id, kind: row.node_kind })
11454
+ )
11455
+ );
11456
+ let meeting;
11457
+ for (const row of forwardSeeds) {
11458
+ const candidate = { id: row.node_id, kind: row.node_kind, depth: 0 };
11459
+ if (!reverseKeys.has(nodeIdentityKey(candidate))) continue;
11460
+ if (meeting === void 0 || compareNodeIdentity(candidate, meeting) < 0) {
11461
+ meeting = candidate;
11462
+ }
11463
+ }
11464
+ return meeting;
11465
+ }
11466
+ async function expandWorkingTableRound(context, side, currentDepth, nextDepth, direction, maxHops) {
11467
+ const oppositeSide = side === "forward" ? "reverse" : "forward";
11468
+ let insertedCount = 0;
11469
+ let meeting;
11470
+ for (const edgeKinds of context.operation.edgeKindChunks) {
11471
+ const sourceFilter = sql`
11472
+ w.graph_id = ${context.graphId}
11473
+ AND w.run_id = ${context.runId}
11474
+ AND w.side = ${side}
11475
+ AND w.depth = ${currentDepth}
11476
+ `;
11477
+ const expansion = compileWorkingTableExpansion(
11478
+ context.operation,
11479
+ context.workingTable,
11480
+ sourceFilter,
11481
+ direction,
11482
+ edgeKinds
11483
+ );
11484
+ const rows = await context.backend.execute(
11485
+ asCompiledRowsSql(sql`
11486
+ INSERT INTO ${context.workingTable}
11487
+ (graph_id, run_id, side, node_id, node_kind, depth,
11488
+ predecessor_id, predecessor_kind, meeting_depth)
11489
+ SELECT
11490
+ ${context.graphId}, ${context.runId}, ${side},
11491
+ ranked.target_id, ranked.target_kind, ${nextDepth},
11492
+ ranked.source_id, ranked.source_kind,
11493
+ (
11494
+ SELECT opposite.depth
11495
+ FROM ${context.workingTable} opposite
11496
+ WHERE opposite.graph_id = ${context.graphId}
11497
+ AND opposite.run_id = ${context.runId}
11498
+ AND opposite.side = ${oppositeSide}
11499
+ AND opposite.node_id = ranked.target_id
11500
+ AND opposite.node_kind = ranked.target_kind
11501
+ )
11502
+ FROM (
11503
+ SELECT expanded.*,
11504
+ ROW_NUMBER() OVER (
11505
+ PARTITION BY expanded.target_kind, expanded.target_id
11506
+ ORDER BY expanded.source_id, expanded.source_kind
11507
+ ) AS candidate_rank
11508
+ FROM (${expansion}) expanded
11509
+ ) ranked
11510
+ WHERE ranked.candidate_rank = 1
11511
+ AND NOT EXISTS (
11512
+ SELECT 1
11513
+ FROM ${context.workingTable} visited
11514
+ WHERE visited.graph_id = ${context.graphId}
11515
+ AND visited.run_id = ${context.runId}
11516
+ AND visited.side = ${side}
11517
+ AND visited.node_id = ranked.target_id
11518
+ AND visited.node_kind = ranked.target_kind
11519
+ )
11520
+ ON CONFLICT (graph_id, run_id, side, node_kind, node_id) DO NOTHING
11521
+ RETURNING node_id, node_kind, meeting_depth
11522
+ `)
11523
+ );
11524
+ insertedCount += rows.length;
11525
+ meeting = selectRoundMeeting(meeting, rows, nextDepth, maxHops);
11526
+ }
11527
+ return { insertedCount, meeting };
11528
+ }
11529
+ function selectRoundMeeting(currentMeeting, rows, nextDepth, maxHops) {
11530
+ let meeting = currentMeeting;
11531
+ for (const row of rows) {
11532
+ if (row.meeting_depth === null || row.meeting_depth === void 0) {
11533
+ continue;
11534
+ }
11535
+ const totalDepth = nextDepth + Number(row.meeting_depth);
11536
+ if (totalDepth > maxHops) continue;
11537
+ const candidate = {
11538
+ id: row.node_id,
11539
+ kind: row.node_kind,
11540
+ depth: totalDepth
11541
+ };
11542
+ if (meeting === void 0 || candidate.depth < meeting.depth || candidate.depth === meeting.depth && compareNodeIdentity(candidate, meeting) < 0) {
11543
+ meeting = candidate;
11544
+ }
11545
+ }
11546
+ return meeting;
11547
+ }
11548
+ async function expandReachableWorkingTableRound(context, side, currentDepth, nextDepth, direction) {
11549
+ let insertedCount = 0;
11550
+ for (const edgeKinds of context.operation.edgeKindChunks) {
11551
+ const sourceFilter = sql`
11552
+ w.graph_id = ${context.graphId}
11553
+ AND w.run_id = ${context.runId}
11554
+ AND w.side = ${side}
11555
+ AND w.depth = ${currentDepth}
11556
+ `;
11557
+ const edgeExpansion = compileWorkingTableEdgeExpansion(
11558
+ context.operation,
11559
+ context.workingTable,
11560
+ sourceFilter,
11561
+ direction,
11562
+ edgeKinds
11563
+ );
11564
+ const rows = await context.backend.execute(
11565
+ asCompiledRowsSql(sql`
11566
+ INSERT INTO ${context.workingTable}
11567
+ (graph_id, run_id, side, node_id, node_kind, depth,
11568
+ predecessor_id, predecessor_kind)
11569
+ SELECT
11570
+ ${context.graphId}, ${context.runId}, ${side},
11571
+ candidates.target_id, candidates.target_kind, ${nextDepth},
11572
+ NULL, NULL
11573
+ FROM (
11574
+ SELECT DISTINCT expanded.target_id, expanded.target_kind
11575
+ FROM (${edgeExpansion}) expanded
11576
+ ) candidates
11577
+ JOIN ${context.operation.schema.nodesTable} n
11578
+ ON n.graph_id = ${context.graphId}
11579
+ AND n.id = candidates.target_id
11580
+ AND n.kind = candidates.target_kind
11581
+ WHERE ${context.operation.nodeTemporalFilter}
11582
+ AND NOT EXISTS (
11583
+ SELECT 1
11584
+ FROM ${context.workingTable} visited
11585
+ WHERE visited.graph_id = ${context.graphId}
11586
+ AND visited.run_id = ${context.runId}
11587
+ AND visited.side = ${side}
11588
+ AND visited.node_id = candidates.target_id
11589
+ AND visited.node_kind = candidates.target_kind
11590
+ )
11591
+ ON CONFLICT (graph_id, run_id, side, node_kind, node_id) DO NOTHING
11592
+ RETURNING node_id
11593
+ `)
11594
+ );
11595
+ insertedCount += rows.length;
11596
+ }
11597
+ return insertedCount;
11598
+ }
11599
+ async function readWorkingRows(context, side) {
11600
+ const sideFilter = side === void 0 ? sql`TRUE` : sql`side = ${side}`;
11601
+ return context.backend.execute(
11602
+ asCompiledRowsSql(sql`
11603
+ SELECT side, node_id, node_kind, depth,
11604
+ predecessor_id, predecessor_kind
11605
+ FROM ${context.workingTable}
11606
+ WHERE graph_id = ${context.graphId}
11607
+ AND run_id = ${context.runId}
11608
+ AND ${sideFilter}
11609
+ ORDER BY side, depth, node_id, node_kind
11610
+ `)
11611
+ );
11612
+ }
11613
+ async function findReachableNodesInline(ctx, sourceId, maxHops, options) {
11614
+ return withInlineIterativeGraphOperation(ctx, options, async (operation) => {
11615
+ const sources = await fetchVisibleWorkingNodes(operation, [sourceId]);
11616
+ if (sources.length === 0) return [];
11617
+ const reached = new Map(
11618
+ sources.map((source) => [
11619
+ nodeIdentityKey(source),
11620
+ { id: source.id, kind: source.kind, depth: 0 }
11621
+ ])
11622
+ );
11623
+ let workingSet = sources;
11624
+ for (let depth = 1; depth <= maxHops && workingSet.length > 0; depth++) {
11625
+ const candidates = await expandInlineWorkingSet(
11626
+ operation,
11627
+ workingSet,
11628
+ operation.direction,
11629
+ reached
11630
+ );
11631
+ const nextWorkingSet = sortCandidates(candidates);
11632
+ for (const candidate of nextWorkingSet) {
11633
+ reached.set(nodeIdentityKey(candidate), {
11634
+ id: candidate.id,
11635
+ kind: candidate.kind,
11636
+ depth
11637
+ });
11638
+ }
11639
+ workingSet = nextWorkingSet;
11640
+ }
11641
+ return [...reached.values()].toSorted(
11642
+ (left, right) => left.depth - right.depth || compareNodeIdentity(left, right)
11643
+ );
11644
+ });
11645
+ }
11646
+ async function findShortestPathInline(ctx, sourceId, targetId, maxHops, options) {
11647
+ return withInlineIterativeGraphOperation(ctx, options, async (operation) => {
11648
+ const visibleNodes = await fetchVisibleWorkingNodes(operation, [
11649
+ sourceId,
11650
+ targetId
11651
+ ]);
11652
+ const sources = visibleNodes.filter((node) => node.id === sourceId);
11653
+ const targets = visibleNodes.filter((node) => node.id === targetId);
11654
+ if (sources.length === 0 || targets.length === 0) return;
11655
+ if (sourceId === targetId) return { nodes: [sources[0]], depth: 0 };
11656
+ const forwardVisited = createVisitedMap(sources);
11657
+ const reverseVisited = createVisitedMap(targets);
11658
+ let forwardWorkingSet = sources;
11659
+ let reverseWorkingSet = targets;
11660
+ let forwardDepth = 0;
11661
+ let reverseDepth = 0;
11662
+ while (forwardWorkingSet.length > 0 && reverseWorkingSet.length > 0 && forwardDepth + reverseDepth < maxHops) {
11663
+ const expandForward = forwardWorkingSet.length <= reverseWorkingSet.length;
11664
+ const activeVisited = expandForward ? forwardVisited : reverseVisited;
11665
+ const oppositeVisited = expandForward ? reverseVisited : forwardVisited;
11666
+ const currentWorkingSet = expandForward ? forwardWorkingSet : reverseWorkingSet;
11667
+ const direction = expandForward ? operation.direction : reverseDirection(operation.direction);
11668
+ const candidates = await expandInlineWorkingSet(
11669
+ operation,
11670
+ currentWorkingSet,
11671
+ direction,
11672
+ activeVisited
11673
+ );
11674
+ const nextDepth = (expandForward ? forwardDepth : reverseDepth) + 1;
11675
+ const nextWorkingSet = sortCandidates(candidates);
11676
+ for (const candidate of nextWorkingSet) {
11677
+ activeVisited.set(nodeIdentityKey(candidate), {
11678
+ id: candidate.id,
11679
+ kind: candidate.kind,
11680
+ depth: nextDepth,
11681
+ parentKey: nodeIdentityKey({
11682
+ id: candidate.parentId,
11683
+ kind: candidate.parentKind
11684
+ })
11685
+ });
11686
+ }
11687
+ const meetingKey = findMeetingNodeKey(
11688
+ nextWorkingSet,
11689
+ activeVisited,
11690
+ oppositeVisited
11691
+ );
11692
+ if (meetingKey !== void 0) {
11693
+ return buildShortestPath(meetingKey, forwardVisited, reverseVisited);
11694
+ }
11695
+ if (expandForward) {
11696
+ forwardWorkingSet = nextWorkingSet;
11697
+ forwardDepth = nextDepth;
11698
+ } else {
11699
+ reverseWorkingSet = nextWorkingSet;
11700
+ reverseDepth = nextDepth;
11701
+ }
11702
+ }
11703
+ return;
11704
+ });
11705
+ }
11706
+ async function expandInlineWorkingSet(operation, workingSet, direction, visited) {
11707
+ return reduceExpandedWorkingSet(
11708
+ operation,
11709
+ workingSet,
11710
+ direction,
11711
+ (existing, expansion) => selectPredecessor(existing, expansion, visited)
11712
+ );
11713
+ }
11714
+ function selectPredecessor(existing, expansion, visited) {
11715
+ if (visited.has(nodeIdentityKey(expansion.target))) return existing;
11716
+ if (existing !== void 0 && compareNodeIdentity(expansion.source, {
11717
+ id: existing.parentId,
11718
+ kind: existing.parentKind
11719
+ }) >= 0) {
11720
+ return existing;
11721
+ }
11722
+ return {
11723
+ id: expansion.target.id,
11724
+ kind: expansion.target.kind,
11725
+ parentId: expansion.source.id,
11726
+ parentKind: expansion.source.kind
11727
+ };
11728
+ }
11729
+ function sortCandidates(candidates) {
11730
+ return [...candidates.values()].toSorted(
11731
+ (left, right) => compareNodeIdentity(left, right)
11732
+ );
11733
+ }
11734
+ function findMeetingNodeKey(candidates, activeVisited, oppositeVisited) {
11735
+ let best;
11736
+ for (const candidate of candidates) {
11737
+ const key = nodeIdentityKey(candidate);
11738
+ const active = activeVisited.get(key);
11739
+ const opposite = oppositeVisited.get(key);
11740
+ if (active === void 0 || opposite === void 0) continue;
11741
+ const meeting = { key, depth: active.depth + opposite.depth };
11742
+ if (best === void 0 || meeting.depth < best.depth || meeting.depth === best.depth && compareStrings(meeting.key, best.key) < 0) {
11743
+ best = meeting;
11744
+ }
11745
+ }
11746
+ return best?.key;
11747
+ }
11748
+ function buildShortestPath(meetingKey, forwardVisited, reverseVisited) {
11749
+ const forwardKeys = [];
11750
+ let cursor = meetingKey;
11751
+ while (cursor !== void 0) {
11752
+ forwardKeys.push(cursor);
11753
+ cursor = forwardVisited.get(cursor)?.parentKey;
11754
+ }
11755
+ forwardKeys.reverse();
11756
+ const pathKeys = [...forwardKeys];
11757
+ cursor = reverseVisited.get(meetingKey)?.parentKey;
11758
+ while (cursor !== void 0) {
11759
+ pathKeys.push(cursor);
11760
+ cursor = reverseVisited.get(cursor)?.parentKey;
11761
+ }
11762
+ const nodes = pathKeys.map((key) => {
11763
+ const node = forwardVisited.get(key) ?? reverseVisited.get(key);
11764
+ return { id: node?.id ?? "", kind: node?.kind ?? "" };
11765
+ });
11766
+ return { nodes, depth: nodes.length - 1 };
11767
+ }
11768
+ function createVisitedMap(nodes) {
11769
+ return new Map(
11770
+ nodes.map((node) => [
11771
+ nodeIdentityKey(node),
11772
+ { id: node.id, kind: node.kind, depth: 0, parentKey: void 0 }
11773
+ ])
11774
+ );
11775
+ }
11776
+ function createVisitedMapFromRows(rows, side) {
11777
+ return new Map(
11778
+ rows.filter((row) => row.side === side).map((row) => {
11779
+ const parentKey = nodeIdentityKeyFromRow(
11780
+ row.predecessor_id,
11781
+ row.predecessor_kind
11782
+ );
11783
+ const node = {
11784
+ id: row.node_id,
11785
+ kind: row.node_kind,
11786
+ depth: Number(row.depth),
11787
+ parentKey
11788
+ };
11789
+ return [nodeIdentityKey(node), node];
11790
+ })
11791
+ );
11792
+ }
11793
+ function reverseDirection(direction) {
11794
+ switch (direction) {
11795
+ case "out": {
11796
+ return "in";
11797
+ }
11798
+ case "in": {
11799
+ return "out";
11800
+ }
11801
+ case "both": {
11802
+ return "both";
11803
+ }
11804
+ }
11805
+ }
11806
+
11807
+ // src/store/algorithms/reachable.ts
11808
+ async function executeReachable(ctx, sourceId, options) {
11809
+ assertEdgeKinds(options.edges);
11810
+ const maxHops = resolveMaxHops(
11811
+ options.maxHops,
11812
+ DEFAULT_ALGORITHM_MAX_HOPS,
11813
+ "maxHops"
11814
+ );
11815
+ const reached = await findReachableNodes(ctx, sourceId, maxHops, options);
11816
+ return options.excludeSource === true ? reached.filter((node) => node.id !== sourceId) : reached;
11817
+ }
11818
+ async function executeCanReach(ctx, sourceId, targetId, options) {
11819
+ assertEdgeKinds(options.edges);
11820
+ const maxHops = resolveMaxHops(
11821
+ options.maxHops,
11822
+ DEFAULT_ALGORITHM_MAX_HOPS,
11823
+ "maxHops"
11824
+ );
11825
+ return await findShortestPath(ctx, sourceId, targetId, maxHops, options) !== void 0;
11826
+ }
11827
+ async function executeNeighbors(ctx, sourceId, options) {
11828
+ const depth = resolveMaxHops(options.depth, DEFAULT_NEIGHBOR_DEPTH, "depth");
10623
11829
  return executeReachable(ctx, sourceId, {
10624
11830
  edges: options.edges,
10625
11831
  maxHops: depth,
@@ -10628,70 +11834,715 @@ async function executeNeighbors(ctx, sourceId, options) {
10628
11834
  ...options.temporalMode !== void 0 && {
10629
11835
  temporalMode: options.temporalMode
10630
11836
  },
10631
- ...options.asOf !== void 0 && { asOf: options.asOf },
10632
- ...options.recordedAsOf !== void 0 && {
10633
- recordedAsOf: options.recordedAsOf
11837
+ ...options.asOf !== void 0 && { asOf: options.asOf },
11838
+ ...options.recordedAsOf !== void 0 && {
11839
+ recordedAsOf: options.recordedAsOf
11840
+ },
11841
+ ...options.workingMemory !== void 0 && {
11842
+ workingMemory: options.workingMemory
11843
+ },
11844
+ excludeSource: true
11845
+ });
11846
+ }
11847
+
11848
+ // src/store/algorithms/shortest-path.ts
11849
+ async function executeShortestPath(ctx, sourceId, targetId, options) {
11850
+ assertEdgeKinds(options.edges);
11851
+ const maxHops = resolveMaxHops(
11852
+ options.maxHops,
11853
+ DEFAULT_ALGORITHM_MAX_HOPS,
11854
+ "maxHops"
11855
+ );
11856
+ return findShortestPath(ctx, sourceId, targetId, maxHops, options);
11857
+ }
11858
+ var DEFAULT_WCC_MAX_ITERATIONS = 1e3;
11859
+ async function executeWeaklyConnectedComponents(ctx, options) {
11860
+ assertEdgeKinds(options.edges);
11861
+ assertWeaklyConnectedComponentsSupported(ctx);
11862
+ const maxIterations = resolveMaxIterations(
11863
+ options.maxIterations,
11864
+ DEFAULT_WCC_MAX_ITERATIONS,
11865
+ "weaklyConnectedComponents"
11866
+ );
11867
+ const nodeKinds = options.nodeKinds === void 0 ? void 0 : [...new Set(options.nodeKinds)].toSorted(
11868
+ (left, right) => compareCodePoints(left, right)
11869
+ );
11870
+ const traversalOptions = {
11871
+ edges: options.edges,
11872
+ direction: "both",
11873
+ ...options.temporalMode === void 0 ? {} : { temporalMode: options.temporalMode },
11874
+ ...options.asOf === void 0 ? {} : { asOf: options.asOf },
11875
+ ...options.recordedAsOf === void 0 ? {} : { recordedAsOf: options.recordedAsOf },
11876
+ ...options.workingMemory === void 0 ? {} : { workingMemory: options.workingMemory }
11877
+ };
11878
+ return runIterativeGraphOperation(ctx, traversalOptions, {
11879
+ algorithm: "weaklyConnectedComponents",
11880
+ maxIterations,
11881
+ createWorkingTable: createWorkingTable2,
11882
+ initialize: (context) => initializeWorkingTable(context, nodeKinds),
11883
+ runRound: runLabelPropagationRound,
11884
+ hasConverged(state) {
11885
+ return state.changedCount === 0;
11886
+ },
11887
+ extractResult: extractMemberships
11888
+ });
11889
+ }
11890
+ function assertWeaklyConnectedComponentsSupported(ctx) {
11891
+ if (ctx.backend.capabilities.graphAnalytics?.supported === true && ctx.backend.capabilities.windowFunctions) {
11892
+ return;
11893
+ }
11894
+ throw new UnsupportedBackendCapabilityError(
11895
+ "weaklyConnectedComponents",
11896
+ "graphAnalytics",
11897
+ {
11898
+ dialect: ctx.backend.dialect,
11899
+ supported: ctx.backend.capabilities.graphAnalytics?.supported === true,
11900
+ windowFunctions: ctx.backend.capabilities.windowFunctions
11901
+ },
11902
+ "Use a built-in transactional SQLite/PostgreSQL backend, or declare graphAnalytics support on a compatible custom backend."
11903
+ );
11904
+ }
11905
+ function createWorkingTable2(context) {
11906
+ return sql`
11907
+ CREATE TEMP TABLE ${context.workingTable} (
11908
+ graph_id TEXT NOT NULL,
11909
+ run_id TEXT NOT NULL,
11910
+ node_id TEXT NOT NULL,
11911
+ node_kind TEXT NOT NULL,
11912
+ label_id TEXT NOT NULL,
11913
+ label_kind TEXT NOT NULL,
11914
+ next_label_id TEXT NOT NULL,
11915
+ next_label_kind TEXT NOT NULL,
11916
+ PRIMARY KEY (graph_id, run_id, node_kind, node_id)
11917
+ )
11918
+ `;
11919
+ }
11920
+ async function initializeWorkingTable(context, nodeKinds) {
11921
+ const { operation, workingTable, graphId, runId } = context;
11922
+ const nodeKindFilter = nodeKinds === void 0 ? sql`TRUE` : compileKindFilter(sql.raw("n.kind"), nodeKinds);
11923
+ await context.executeTemporary(sql`
11924
+ INSERT INTO ${workingTable}
11925
+ (graph_id, run_id, node_id, node_kind, label_id, label_kind,
11926
+ next_label_id, next_label_kind)
11927
+ SELECT
11928
+ ${graphId}, ${runId}, n.id, n.kind, n.id, n.kind, n.id, n.kind
11929
+ FROM ${operation.schema.nodesTable} n
11930
+ WHERE n.graph_id = ${graphId}
11931
+ AND ${nodeKindFilter}
11932
+ AND ${operation.nodeTemporalFilter}
11933
+ `);
11934
+ const rows = await context.backend.execute(
11935
+ asCompiledRowsSql(sql`
11936
+ SELECT COUNT(*) AS count
11937
+ FROM ${workingTable}
11938
+ WHERE graph_id = ${graphId} AND run_id = ${runId}
11939
+ `)
11940
+ );
11941
+ const workingTableSize = Number(rows[0]?.count ?? 0);
11942
+ return { changedCount: workingTableSize, workingTableSize };
11943
+ }
11944
+ async function runLabelPropagationRound(context, state) {
11945
+ await resetNextLabels(context);
11946
+ for (const edgeKinds of context.operation.edgeKindChunks) {
11947
+ await propagateChunkLabels(context, edgeKinds);
11948
+ }
11949
+ const changedRows = await applyNextLabels(context);
11950
+ return {
11951
+ changedCount: changedRows.length,
11952
+ workingTableSize: state.workingTableSize
11953
+ };
11954
+ }
11955
+ async function resetNextLabels(context) {
11956
+ await context.executeTemporary(sql`
11957
+ UPDATE ${context.workingTable}
11958
+ SET next_label_id = label_id, next_label_kind = label_kind
11959
+ WHERE graph_id = ${context.graphId} AND run_id = ${context.runId}
11960
+ `);
11961
+ }
11962
+ async function propagateChunkLabels(context, edgeKinds) {
11963
+ const { operation, workingTable, graphId, runId } = context;
11964
+ const expansion = compileWorkingTableEdgeExpansion(
11965
+ operation,
11966
+ workingTable,
11967
+ sql`w.graph_id = ${graphId} AND w.run_id = ${runId}`,
11968
+ "both",
11969
+ edgeKinds
11970
+ );
11971
+ const candidateId = operation.ctx.dialect.binaryText(sql`ranked.label_id`);
11972
+ const candidateKind = operation.ctx.dialect.binaryText(
11973
+ sql`ranked.label_kind`
11974
+ );
11975
+ const currentId = operation.ctx.dialect.binaryText(sql`target.next_label_id`);
11976
+ const currentKind = operation.ctx.dialect.binaryText(
11977
+ sql`target.next_label_kind`
11978
+ );
11979
+ const sourceId = operation.ctx.dialect.binaryText(sql`source.label_id`);
11980
+ const sourceKind = operation.ctx.dialect.binaryText(sql`source.label_kind`);
11981
+ const targetId = operation.ctx.dialect.binaryText(sql`source.target_id`);
11982
+ const targetKind = operation.ctx.dialect.binaryText(sql`source.target_kind`);
11983
+ await context.executeTemporary(sql`
11984
+ WITH candidates AS (
11985
+ SELECT
11986
+ expanded.target_id,
11987
+ expanded.target_kind,
11988
+ source.label_id,
11989
+ source.label_kind
11990
+ FROM (${expansion}) expanded
11991
+ JOIN ${workingTable} source
11992
+ ON source.graph_id = ${graphId}
11993
+ AND source.run_id = ${runId}
11994
+ AND source.node_id = expanded.source_id
11995
+ AND source.node_kind = expanded.source_kind
11996
+ JOIN ${workingTable} scoped_target
11997
+ ON scoped_target.graph_id = ${graphId}
11998
+ AND scoped_target.run_id = ${runId}
11999
+ AND scoped_target.node_id = expanded.target_id
12000
+ AND scoped_target.node_kind = expanded.target_kind
12001
+ ), ranked AS (
12002
+ SELECT
12003
+ target_id,
12004
+ target_kind,
12005
+ label_id,
12006
+ label_kind,
12007
+ ROW_NUMBER() OVER (
12008
+ PARTITION BY ${targetKind}, ${targetId}
12009
+ ORDER BY ${sourceId}, ${sourceKind}
12010
+ ) AS candidate_rank
12011
+ FROM candidates source
12012
+ )
12013
+ UPDATE ${workingTable} AS target
12014
+ SET next_label_id = ranked.label_id,
12015
+ next_label_kind = ranked.label_kind
12016
+ FROM ranked
12017
+ WHERE target.graph_id = ${graphId}
12018
+ AND target.run_id = ${runId}
12019
+ AND target.node_id = ranked.target_id
12020
+ AND target.node_kind = ranked.target_kind
12021
+ AND ranked.candidate_rank = 1
12022
+ AND (
12023
+ ${candidateId} < ${currentId}
12024
+ OR (
12025
+ ${candidateId} = ${currentId}
12026
+ AND ${candidateKind} < ${currentKind}
12027
+ )
12028
+ )
12029
+ `);
12030
+ }
12031
+ function applyNextLabels(context) {
12032
+ return context.backend.execute(
12033
+ asCompiledRowsSql(sql`
12034
+ UPDATE ${context.workingTable}
12035
+ SET label_id = next_label_id, label_kind = next_label_kind
12036
+ WHERE graph_id = ${context.graphId}
12037
+ AND run_id = ${context.runId}
12038
+ AND (
12039
+ label_id <> next_label_id OR label_kind <> next_label_kind
12040
+ )
12041
+ RETURNING node_id
12042
+ `)
12043
+ );
12044
+ }
12045
+ async function extractMemberships(context) {
12046
+ const { operation } = context;
12047
+ const componentId = operation.ctx.dialect.binaryText(sql`label_id`);
12048
+ const componentKind = operation.ctx.dialect.binaryText(sql`label_kind`);
12049
+ const nodeId = operation.ctx.dialect.binaryText(sql`node_id`);
12050
+ const nodeKind = operation.ctx.dialect.binaryText(sql`node_kind`);
12051
+ const rows = await context.backend.execute(
12052
+ asCompiledRowsSql(sql`
12053
+ SELECT
12054
+ node_id,
12055
+ node_kind,
12056
+ label_id AS component_id,
12057
+ label_kind AS component_kind,
12058
+ COUNT(*) OVER (
12059
+ PARTITION BY ${componentKind}, ${componentId}
12060
+ ) AS component_size
12061
+ FROM ${context.workingTable}
12062
+ WHERE graph_id = ${context.graphId} AND run_id = ${context.runId}
12063
+ ORDER BY ${componentId}, ${componentKind}, ${nodeId}, ${nodeKind}
12064
+ `)
12065
+ );
12066
+ return rows.map((row) => ({
12067
+ id: row.node_id,
12068
+ kind: row.node_kind,
12069
+ componentId: row.component_id,
12070
+ componentKind: row.component_kind,
12071
+ size: Number(row.component_size)
12072
+ })).toSorted(compareMemberships);
12073
+ }
12074
+ function compareMemberships(left, right) {
12075
+ return compareCodePoints(left.componentId, right.componentId) || compareCodePoints(left.componentKind, right.componentKind) || compareCodePoints(left.id, right.id) || compareCodePoints(left.kind, right.kind);
12076
+ }
12077
+ var DEFAULT_WEIGHTED_SHORTEST_PATH_MAX_ITERATIONS = 1e3;
12078
+ var WEIGHTED_ALGORITHM_NAME = "weightedShortestPath";
12079
+ var MAX_EDGE_WEIGHT = Number.MAX_VALUE / 2 ** 64;
12080
+ var MIN_EDGE_WEIGHT = Number.MIN_VALUE;
12081
+ async function executeWeightedShortestPath(ctx, sourceId, targetId, options) {
12082
+ assertEdgeKinds(options.edges);
12083
+ assertWeightOptions(options.weightProperty, options.defaultWeight);
12084
+ const maxIterations = resolveMaxIterations(
12085
+ options.maxIterations,
12086
+ DEFAULT_WEIGHTED_SHORTEST_PATH_MAX_ITERATIONS,
12087
+ WEIGHTED_ALGORITHM_NAME
12088
+ );
12089
+ const traversalOptions = options;
12090
+ if (sourceId === targetId) {
12091
+ return findWeightedSelfPath(ctx, sourceId, traversalOptions);
12092
+ }
12093
+ if (supportsTemporaryIteration(ctx)) {
12094
+ return findWeightedShortestPathInWorkingTable(
12095
+ ctx,
12096
+ sourceId,
12097
+ targetId,
12098
+ maxIterations,
12099
+ traversalOptions
12100
+ );
12101
+ }
12102
+ return findWeightedShortestPathInline(
12103
+ ctx,
12104
+ sourceId,
12105
+ targetId,
12106
+ maxIterations,
12107
+ traversalOptions
12108
+ );
12109
+ }
12110
+ function assertWeightOptions(weightProperty, defaultWeight) {
12111
+ if (typeof weightProperty !== "string" || weightProperty.length === 0) {
12112
+ throw new ConfigurationError(
12113
+ `${WEIGHTED_ALGORITHM_NAME} weightProperty must be a non-empty string.`,
12114
+ { weightProperty }
12115
+ );
12116
+ }
12117
+ if (defaultWeight !== void 0 && (!Number.isFinite(defaultWeight) || defaultWeight < 0 || defaultWeight > MAX_EDGE_WEIGHT)) {
12118
+ throw new ConfigurationError(
12119
+ `${WEIGHTED_ALGORITHM_NAME} defaultWeight must be a non-negative number no greater than ${MAX_EDGE_WEIGHT}, got ${String(defaultWeight)}.`,
12120
+ { defaultWeight, maximum: MAX_EDGE_WEIGHT }
12121
+ );
12122
+ }
12123
+ }
12124
+ async function assertValidEdgeWeights(operation, weightProperty, defaultWeight) {
12125
+ const { dialect } = operation.ctx;
12126
+ const pointer = jsonPointer([weightProperty]);
12127
+ const propsColumn = sql.raw("e.props");
12128
+ const isNumber = dialect.jsonPathIsNumber(propsColumn, pointer);
12129
+ const isMissing = dialect.jsonPathIsNull(propsColumn, pointer);
12130
+ const weightText = dialect.jsonExtractText(propsColumn, pointer);
12131
+ const weightNumber = sql`CASE WHEN ${isNumber} THEN ${dialect.jsonExtractNumber(propsColumn, pointer)} END`;
12132
+ const missingViolation = defaultWeight === void 0 ? sql`audited.is_missing = 1` : dialect.booleanLiteral(false);
12133
+ const negativeViolation = sql`audited.is_number = 1 AND audited.weight_number < 0`;
12134
+ const outOfRangeViolation = sql`audited.is_number = 1 AND (ABS(audited.weight_number) > CAST(${MAX_EDGE_WEIGHT} AS NUMERIC) OR (audited.weight_number <> 0 AND ABS(audited.weight_number) < CAST(${MIN_EDGE_WEIGHT} AS NUMERIC)))`;
12135
+ for (const edgeKinds of operation.edgeKindChunks) {
12136
+ const edgeKindFilter = compileKindFilter(sql.raw("e.kind"), edgeKinds);
12137
+ const rows = await operation.backend.execute(
12138
+ asCompiledRowsSql(sql`
12139
+ SELECT audited.edge_id, audited.edge_kind, audited.weight_text,
12140
+ audited.is_number, audited.is_missing
12141
+ FROM (
12142
+ SELECT e.id AS edge_id, e.kind AS edge_kind,
12143
+ ${weightText} AS weight_text,
12144
+ ${weightNumber} AS weight_number,
12145
+ CASE WHEN ${isNumber} THEN 1 ELSE 0 END AS is_number,
12146
+ CASE WHEN ${isMissing} THEN 1 ELSE 0 END AS is_missing
12147
+ FROM ${operation.schema.edgesTable} e
12148
+ WHERE e.graph_id = ${operation.ctx.graphId}
12149
+ AND ${edgeKindFilter}
12150
+ AND ${operation.edgeTemporalFilter}
12151
+ ) audited
12152
+ WHERE (${missingViolation})
12153
+ OR (audited.is_missing = 0 AND audited.is_number = 0)
12154
+ OR (${negativeViolation})
12155
+ OR (${outOfRangeViolation})
12156
+ ORDER BY ${dialect.binaryText(sql`audited.edge_id`)}
12157
+ LIMIT 1
12158
+ `)
12159
+ );
12160
+ const row = rows[0];
12161
+ if (row !== void 0) {
12162
+ throw createInvalidEdgeWeightError(row, weightProperty);
12163
+ }
12164
+ }
12165
+ }
12166
+ function createInvalidEdgeWeightError(row, weightProperty) {
12167
+ const reason = resolveInvalidWeightReason(row);
12168
+ const value = reason === "missing" || row.weight_text === null ? void 0 : formatWeightValue(row.weight_text);
12169
+ return new InvalidEdgeWeightError({
12170
+ edgeId: row.edge_id,
12171
+ edgeKind: row.edge_kind,
12172
+ property: weightProperty,
12173
+ reason,
12174
+ ...value === void 0 ? {} : { value }
12175
+ });
12176
+ }
12177
+ function resolveInvalidWeightReason(row) {
12178
+ if (flagIsSet(row.is_missing)) return "missing";
12179
+ if (!flagIsSet(row.is_number)) return "non_numeric";
12180
+ return Number(row.weight_text) < 0 ? "negative" : "out_of_range";
12181
+ }
12182
+ function flagIsSet(value) {
12183
+ return Number(value) !== 0;
12184
+ }
12185
+ function formatWeightValue(value) {
12186
+ if (typeof value === "string") return value;
12187
+ if (typeof value === "number" || typeof value === "bigint" || typeof value === "boolean") {
12188
+ return String(value);
12189
+ }
12190
+ return JSON.stringify(value);
12191
+ }
12192
+ function compareNodeIdentityCodePoints(left, right) {
12193
+ return compareCodePoints(left.id, right.id) || compareCodePoints(left.kind, right.kind);
12194
+ }
12195
+ async function findWeightedShortestPathInWorkingTable(ctx, sourceId, targetId, maxIterations, options) {
12196
+ return runIterativeGraphOperation(ctx, options, {
12197
+ algorithm: WEIGHTED_ALGORITHM_NAME,
12198
+ maxIterations,
12199
+ createWorkingTable: createWorkingTable3,
12200
+ async initialize(context) {
12201
+ await assertValidEdgeWeights(
12202
+ context.operation,
12203
+ options.weightProperty,
12204
+ options.defaultWeight
12205
+ );
12206
+ if (!await hasVisibleNode(context.operation, targetId)) {
12207
+ return {
12208
+ frontierCount: 0,
12209
+ workingTableSize: 0,
12210
+ bestTargetDistance: void 0
12211
+ };
12212
+ }
12213
+ await context.executeTemporary(sql`
12214
+ CREATE INDEX ${frontierIndexIdentifier(context)}
12215
+ ON ${context.workingTable} (graph_id, run_id, improved_round)
12216
+ `);
12217
+ const seeded = await seedDistances(context, sourceId);
12218
+ return {
12219
+ frontierCount: seeded.length,
12220
+ workingTableSize: seeded.length,
12221
+ // Seeds carry the source id, never the target's — the self-path
12222
+ // case is short-circuited before this plan runs.
12223
+ bestTargetDistance: void 0
12224
+ };
12225
+ },
12226
+ async runRound(context, state, iteration) {
12227
+ const round = await relaxFrontierRound(
12228
+ context,
12229
+ targetId,
12230
+ iteration,
12231
+ state.bestTargetDistance
12232
+ );
12233
+ return {
12234
+ frontierCount: round.frontierCount,
12235
+ // Overcounts by re-improved existing rows (RETURNING includes
12236
+ // updates); acceptable because the size only paces the substrate's
12237
+ // ANALYZE refresh, where too-early merely refreshes stats sooner.
12238
+ workingTableSize: state.workingTableSize + round.frontierCount,
12239
+ bestTargetDistance: round.bestTargetDistance
12240
+ };
10634
12241
  },
10635
- excludeSource: true
12242
+ hasConverged(state) {
12243
+ return state.frontierCount === 0;
12244
+ },
12245
+ async extractResult(context) {
12246
+ return extractPathFromWorkingTable(context, targetId, maxIterations);
12247
+ }
10636
12248
  });
10637
12249
  }
10638
- async function executeShortestPath(ctx, sourceId, targetId, options) {
10639
- assertEdgeKinds(options.edges);
10640
- const maxHops = resolveMaxHops(
10641
- options.maxHops,
10642
- DEFAULT_ALGORITHM_MAX_HOPS,
10643
- "maxHops"
12250
+ function frontierIndexIdentifier(context) {
12251
+ return sql.identifier(
12252
+ `typegraph_iterative_${context.runId.replaceAll("-", "_")}_frontier`
10644
12253
  );
10645
- if (sourceId === targetId) {
10646
- const source = await fetchNodeKind(ctx, sourceId, options);
10647
- if (source === void 0) return void 0;
10648
- return { nodes: [source], depth: 0 };
12254
+ }
12255
+ async function hasVisibleNode(operation, nodeId) {
12256
+ const rows = await operation.backend.execute(
12257
+ asCompiledRowsSql(sql`
12258
+ SELECT n.id FROM ${operation.schema.nodesTable} n
12259
+ WHERE n.graph_id = ${operation.ctx.graphId}
12260
+ AND n.id = ${nodeId}
12261
+ AND ${operation.nodeTemporalFilter}
12262
+ LIMIT 1
12263
+ `)
12264
+ );
12265
+ return rows.length > 0;
12266
+ }
12267
+ function createWorkingTable3(context) {
12268
+ return sql`
12269
+ CREATE TEMP TABLE ${context.workingTable} (
12270
+ graph_id TEXT NOT NULL,
12271
+ run_id TEXT NOT NULL,
12272
+ node_id TEXT NOT NULL,
12273
+ node_kind TEXT NOT NULL,
12274
+ distance DOUBLE PRECISION NOT NULL,
12275
+ hops INTEGER NOT NULL,
12276
+ predecessor_id TEXT,
12277
+ predecessor_kind TEXT,
12278
+ improved_round INTEGER NOT NULL,
12279
+ PRIMARY KEY (graph_id, run_id, node_kind, node_id)
12280
+ )
12281
+ `;
12282
+ }
12283
+ async function seedDistances(context, sourceId) {
12284
+ const { operation, workingTable, graphId, runId } = context;
12285
+ return context.backend.execute(
12286
+ asCompiledRowsSql(sql`
12287
+ INSERT INTO ${workingTable}
12288
+ (graph_id, run_id, node_id, node_kind, distance, hops,
12289
+ predecessor_id, predecessor_kind, improved_round)
12290
+ SELECT ${graphId}, ${runId}, n.id, n.kind, 0, 0, NULL, NULL, 0
12291
+ FROM ${operation.schema.nodesTable} n
12292
+ WHERE n.graph_id = ${graphId}
12293
+ AND n.id = ${sourceId}
12294
+ AND ${operation.nodeTemporalFilter}
12295
+ ON CONFLICT (graph_id, run_id, node_kind, node_id) DO NOTHING
12296
+ RETURNING node_id
12297
+ `)
12298
+ );
12299
+ }
12300
+ async function relaxFrontierRound(context, targetId, iteration, roundBound) {
12301
+ const { operation, workingTable, graphId, runId } = context;
12302
+ const { dialect } = operation.ctx;
12303
+ let frontierCount = 0;
12304
+ let bestTargetDistance = roundBound;
12305
+ for (const edgeKinds of operation.edgeKindChunks) {
12306
+ const sourceFilter = sql`
12307
+ w.graph_id = ${graphId}
12308
+ AND w.run_id = ${runId}
12309
+ AND w.improved_round = ${iteration - 1}
12310
+ `;
12311
+ const expansion = compileWorkingTableExpansion(
12312
+ operation,
12313
+ workingTable,
12314
+ sourceFilter,
12315
+ operation.direction,
12316
+ edgeKinds
12317
+ );
12318
+ const rows = await context.backend.execute(
12319
+ asCompiledRowsSql(sql`
12320
+ INSERT INTO ${workingTable}
12321
+ (graph_id, run_id, node_id, node_kind, distance, hops,
12322
+ predecessor_id, predecessor_kind, improved_round)
12323
+ SELECT
12324
+ ${graphId}, ${runId}, ranked.target_id, ranked.target_kind,
12325
+ ranked.candidate_distance, ranked.candidate_hops,
12326
+ ranked.source_id, ranked.source_kind, ${iteration}
12327
+ FROM (
12328
+ SELECT candidates.*,
12329
+ ROW_NUMBER() OVER (
12330
+ PARTITION BY ${dialect.binaryText(sql`candidates.target_kind`)},
12331
+ ${dialect.binaryText(sql`candidates.target_id`)}
12332
+ ORDER BY candidates.candidate_distance,
12333
+ ${dialect.binaryText(sql`candidates.source_id`)},
12334
+ ${dialect.binaryText(sql`candidates.source_kind`)}
12335
+ ) AS candidate_rank
12336
+ FROM (
12337
+ SELECT expanded.source_id, expanded.source_kind,
12338
+ expanded.target_id, expanded.target_kind,
12339
+ frontier.distance + expanded.weight AS candidate_distance,
12340
+ frontier.hops + 1 AS candidate_hops
12341
+ FROM (${expansion}) expanded
12342
+ JOIN ${workingTable} frontier
12343
+ ON frontier.graph_id = ${graphId}
12344
+ AND frontier.run_id = ${runId}
12345
+ AND frontier.node_id = expanded.source_id
12346
+ AND frontier.node_kind = expanded.source_kind
12347
+ ) candidates
12348
+ WHERE ${roundBound === void 0 ? sql`TRUE` : sql`candidates.candidate_distance <= ${roundBound}`}
12349
+ ) ranked
12350
+ WHERE ranked.candidate_rank = 1
12351
+ ON CONFLICT (graph_id, run_id, node_kind, node_id) DO UPDATE SET
12352
+ distance = excluded.distance,
12353
+ hops = excluded.hops,
12354
+ predecessor_id = excluded.predecessor_id,
12355
+ predecessor_kind = excluded.predecessor_kind,
12356
+ improved_round = excluded.improved_round
12357
+ WHERE excluded.distance < ${workingTable}.distance
12358
+ RETURNING node_id, distance
12359
+ `)
12360
+ );
12361
+ frontierCount += rows.length;
12362
+ for (const row of rows) {
12363
+ if (row.node_id !== targetId) continue;
12364
+ const distance = Number(row.distance);
12365
+ if (bestTargetDistance === void 0 || distance < bestTargetDistance) {
12366
+ bestTargetDistance = distance;
12367
+ }
12368
+ }
10649
12369
  }
10650
- const cte = buildReachableCte({
10651
- graphId: ctx.graphId,
10652
- sourceId,
10653
- edgeKinds: options.edges,
10654
- maxHops,
10655
- direction: options.direction ?? "out",
10656
- cyclePolicy: options.cyclePolicy ?? "prevent",
10657
- includePath: true,
10658
- ...resolveTemporalOptions(ctx, options),
10659
- dialect: ctx.dialect,
10660
- schema: ctx.schema,
10661
- ...ctx.recordedReadBinding === void 0 ? {} : { recordedReadBinding: ctx.recordedReadBinding }
10662
- });
10663
- const pathContainsCheck = sql`NOT (${ctx.dialect.cycleCheck(sql.raw("r.id"), sql.raw("h.path"))})`;
10664
- const query = sql`${cte}, hit AS (SELECT id, kind, depth, path FROM reachable WHERE id = ${targetId} ORDER BY depth ASC LIMIT 1) SELECT h.id AS hit_id, h.kind AS hit_kind, h.depth AS hit_depth, h.path AS hit_path, r.id AS node_id, r.kind AS node_kind FROM hit h LEFT JOIN reachable r ON ${pathContainsCheck}`;
10665
- const rows = await ctx.backend.execute(
10666
- asCompiledRowsSql(query)
12370
+ return { frontierCount, bestTargetDistance };
12371
+ }
12372
+ async function extractPathFromWorkingTable(context, targetId, maxIterations) {
12373
+ const { operation, workingTable, graphId, runId } = context;
12374
+ const { dialect } = operation.ctx;
12375
+ const rows = await context.backend.execute(
12376
+ asCompiledRowsSql(sql`
12377
+ WITH RECURSIVE chain AS (
12378
+ SELECT best.node_id, best.node_kind, best.distance, best.hops,
12379
+ best.predecessor_id, best.predecessor_kind, 0 AS position
12380
+ FROM (
12381
+ SELECT node_id, node_kind, distance, hops,
12382
+ predecessor_id, predecessor_kind
12383
+ FROM ${workingTable}
12384
+ WHERE graph_id = ${graphId}
12385
+ AND run_id = ${runId}
12386
+ AND node_id = ${targetId}
12387
+ ORDER BY distance ASC, ${dialect.binaryText(sql`node_kind`)}
12388
+ LIMIT 1
12389
+ ) best
12390
+ UNION ALL
12391
+ SELECT w.node_id, w.node_kind, w.distance, w.hops,
12392
+ w.predecessor_id, w.predecessor_kind, c.position + 1
12393
+ FROM chain c
12394
+ JOIN ${workingTable} w
12395
+ ON w.graph_id = ${graphId}
12396
+ AND w.run_id = ${runId}
12397
+ AND w.node_id = c.predecessor_id
12398
+ AND w.node_kind = c.predecessor_kind
12399
+ WHERE c.position <= ${maxIterations}
12400
+ )
12401
+ SELECT node_id, node_kind, distance, hops
12402
+ FROM chain
12403
+ ORDER BY position DESC
12404
+ `)
10667
12405
  );
10668
- const first = rows[0];
10669
- if (first === void 0) return void 0;
10670
- const pathIds = normalizePath(first.hit_path);
10671
- const kindById = buildKindIndex(rows);
10672
- const pathNodes = pathIds.map((id) => ({
10673
- id,
10674
- kind: kindById.get(id) ?? ""
10675
- }));
10676
- return { nodes: pathNodes, depth: Number(first.hit_depth) };
12406
+ const target = rows.at(-1);
12407
+ if (target === void 0) return void 0;
12408
+ return {
12409
+ nodes: rows.map((row) => ({ id: row.node_id, kind: row.node_kind })),
12410
+ depth: Number(target.hops),
12411
+ totalWeight: Number(target.distance)
12412
+ };
10677
12413
  }
10678
- function buildKindIndex(rows) {
10679
- const map = /* @__PURE__ */ new Map();
10680
- for (const row of rows) {
10681
- if (row.node_id !== void 0 && row.node_kind !== void 0) {
10682
- map.set(row.node_id, row.node_kind);
12414
+ async function findWeightedSelfPath(ctx, nodeId, options) {
12415
+ return withInlineIterativeGraphOperation(ctx, options, async (operation) => {
12416
+ await assertValidEdgeWeights(
12417
+ operation,
12418
+ options.weightProperty,
12419
+ options.defaultWeight
12420
+ );
12421
+ const nodes = await fetchVisibleWorkingNodes(operation, [nodeId]);
12422
+ const node = nodes.toSorted(
12423
+ (left, right) => compareNodeIdentityCodePoints(left, right)
12424
+ )[0];
12425
+ if (node === void 0) return;
12426
+ return { nodes: [node], depth: 0, totalWeight: 0 };
12427
+ });
12428
+ }
12429
+ async function findWeightedShortestPathInline(ctx, sourceId, targetId, maxIterations, options) {
12430
+ return withInlineIterativeGraphOperation(ctx, options, async (operation) => {
12431
+ await assertValidEdgeWeights(
12432
+ operation,
12433
+ options.weightProperty,
12434
+ options.defaultWeight
12435
+ );
12436
+ const endpoints = await fetchVisibleWorkingNodes(operation, [
12437
+ sourceId,
12438
+ targetId
12439
+ ]);
12440
+ const sources = endpoints.filter((node) => node.id === sourceId);
12441
+ if (sources.length === 0 || !endpoints.some((node) => node.id === targetId)) {
12442
+ return;
10683
12443
  }
12444
+ const settled = new Map(
12445
+ sources.map((source) => [
12446
+ nodeIdentityKey(source),
12447
+ {
12448
+ id: source.id,
12449
+ kind: source.kind,
12450
+ distance: 0,
12451
+ hops: 0,
12452
+ parentKey: void 0
12453
+ }
12454
+ ])
12455
+ );
12456
+ let bestTargetDistance;
12457
+ let frontier = sources;
12458
+ let rounds = 0;
12459
+ while (frontier.length > 0) {
12460
+ if (rounds >= maxIterations) {
12461
+ throw new GraphAlgorithmConvergenceError(
12462
+ WEIGHTED_ALGORITHM_NAME,
12463
+ maxIterations
12464
+ );
12465
+ }
12466
+ rounds++;
12467
+ const candidates = await reduceExpandedWorkingSet(
12468
+ operation,
12469
+ frontier,
12470
+ operation.direction,
12471
+ (existing, expansion) => reduceWeightedCandidate(existing, expansion, settled)
12472
+ );
12473
+ const roundBound = bestTargetDistance;
12474
+ const improved = [];
12475
+ for (const [candidateKey, candidate] of candidates) {
12476
+ const existing = settled.get(candidateKey);
12477
+ if (existing !== void 0 && existing.distance <= candidate.distance) {
12478
+ continue;
12479
+ }
12480
+ if (roundBound !== void 0 && candidate.distance > roundBound) {
12481
+ continue;
12482
+ }
12483
+ settled.set(candidateKey, {
12484
+ id: candidate.id,
12485
+ kind: candidate.kind,
12486
+ distance: candidate.distance,
12487
+ hops: candidate.hops,
12488
+ parentKey: nodeIdentityKey({
12489
+ id: candidate.parentId,
12490
+ kind: candidate.parentKind
12491
+ })
12492
+ });
12493
+ improved.push({ id: candidate.id, kind: candidate.kind });
12494
+ if (candidate.id === targetId && (bestTargetDistance === void 0 || candidate.distance < bestTargetDistance)) {
12495
+ bestTargetDistance = candidate.distance;
12496
+ }
12497
+ }
12498
+ frontier = improved.toSorted(
12499
+ (left, right) => compareNodeIdentityCodePoints(left, right)
12500
+ );
12501
+ }
12502
+ return buildWeightedPathResult(settled, targetId);
12503
+ });
12504
+ }
12505
+ function reduceWeightedCandidate(existing, expansion, settled) {
12506
+ const weight = expansion.weight;
12507
+ if (weight === void 0) return existing;
12508
+ const sourceEntry = settled.get(nodeIdentityKey(expansion.source));
12509
+ if (sourceEntry === void 0) return existing;
12510
+ const candidateDistance = sourceEntry.distance + weight;
12511
+ if (existing !== void 0 && (existing.distance < candidateDistance || existing.distance === candidateDistance && compareNodeIdentityCodePoints(
12512
+ { id: existing.parentId, kind: existing.parentKind },
12513
+ expansion.source
12514
+ ) <= 0)) {
12515
+ return existing;
10684
12516
  }
10685
- return map;
12517
+ return {
12518
+ id: expansion.target.id,
12519
+ kind: expansion.target.kind,
12520
+ distance: candidateDistance,
12521
+ hops: sourceEntry.hops + 1,
12522
+ parentId: expansion.source.id,
12523
+ parentKind: expansion.source.kind
12524
+ };
10686
12525
  }
10687
- async function fetchNodeKind(ctx, nodeId, options) {
10688
- const temporalFilter = resolveTemporalFilter(ctx, options);
10689
- const schema = resolveReadSchema(ctx, options);
10690
- const query = sql`SELECT id, kind FROM ${schema.nodesTable} WHERE graph_id = ${ctx.graphId} AND id = ${nodeId} AND ${temporalFilter} LIMIT 1`;
10691
- const rows = await ctx.backend.execute(asCompiledRowsSql(query));
10692
- const row = rows[0];
10693
- if (row === void 0) return void 0;
10694
- return { id: row.id, kind: row.kind };
12526
+ function buildWeightedPathResult(visited, targetId) {
12527
+ let target;
12528
+ for (const node of visited.values()) {
12529
+ if (node.id !== targetId) continue;
12530
+ if (target === void 0 || node.distance < target.distance || node.distance === target.distance && compareNodeIdentityCodePoints(node, target) < 0) {
12531
+ target = node;
12532
+ }
12533
+ }
12534
+ if (target === void 0) return void 0;
12535
+ const reversedNodes = [];
12536
+ let cursor = target;
12537
+ while (cursor !== void 0) {
12538
+ reversedNodes.push({ id: cursor.id, kind: cursor.kind });
12539
+ cursor = cursor.parentKey === void 0 ? void 0 : visited.get(cursor.parentKey);
12540
+ }
12541
+ return {
12542
+ nodes: reversedNodes.toReversed(),
12543
+ depth: target.hops,
12544
+ totalWeight: target.distance
12545
+ };
10695
12546
  }
10696
12547
 
10697
12548
  // src/store/algorithms/index.ts
@@ -10704,7 +12555,7 @@ function assertRecordedAsOfInternalOnly(options, method, allowRecordedAsOf) {
10704
12555
  code: "ALGORITHM_RECORDED_ASOF_INTERNAL_ONLY",
10705
12556
  message: `recordedAsOf is only available through store.asOfRecorded(...).${method}(...).`,
10706
12557
  context: { method },
10707
- suggestion: "Use store.asOfRecorded(recordedAt).reachable/shortestPath/canReach/neighbors/degree(...) instead of passing recordedAsOf directly."
12558
+ suggestion: "Use store.asOfRecorded(recordedAt) or a recorded StoreView instead of passing recordedAsOf directly."
10708
12559
  });
10709
12560
  }
10710
12561
  function createGraphAlgorithms(params) {
@@ -10733,6 +12584,19 @@ function createGraphAlgorithms(params) {
10733
12584
  options
10734
12585
  );
10735
12586
  },
12587
+ weightedShortestPath(from, to, options) {
12588
+ assertRecordedAsOfInternalOnly(
12589
+ options,
12590
+ "weightedShortestPath",
12591
+ allowRecordedAsOf
12592
+ );
12593
+ return executeWeightedShortestPath(
12594
+ ctx,
12595
+ resolveNodeId(from),
12596
+ resolveNodeId(to),
12597
+ options
12598
+ );
12599
+ },
10736
12600
  reachable(from, options) {
10737
12601
  assertRecordedAsOfInternalOnly(options, "reachable", allowRecordedAsOf);
10738
12602
  return executeReachable(ctx, resolveNodeId(from), options);
@@ -10753,6 +12617,14 @@ function createGraphAlgorithms(params) {
10753
12617
  degree(node, options) {
10754
12618
  assertRecordedAsOfInternalOnly(options, "degree", allowRecordedAsOf);
10755
12619
  return executeDegree(ctx, resolveNodeId(node), options ?? {});
12620
+ },
12621
+ weaklyConnectedComponents(options) {
12622
+ assertRecordedAsOfInternalOnly(
12623
+ options,
12624
+ "weaklyConnectedComponents",
12625
+ allowRecordedAsOf
12626
+ );
12627
+ return executeWeaklyConnectedComponents(ctx, options);
10756
12628
  }
10757
12629
  };
10758
12630
  }
@@ -11614,6 +13486,33 @@ async function ensureFocusedStatusTable(backend, ensureFocused) {
11614
13486
  }
11615
13487
  await backend.bootstrapTables?.();
11616
13488
  }
13489
+ async function runBucketedMaterialization(items, options, bucketKey, runOne) {
13490
+ if (options.stopOnError === true) {
13491
+ const results2 = [];
13492
+ for (const item of items) {
13493
+ const entry2 = await runOne(item);
13494
+ results2.push(entry2);
13495
+ if (entry2.status === "failed") break;
13496
+ }
13497
+ return results2;
13498
+ }
13499
+ const buckets = /* @__PURE__ */ new Map();
13500
+ for (const [index, item] of items.entries()) {
13501
+ const key = bucketKey(item);
13502
+ const bucket = buckets.get(key);
13503
+ if (bucket === void 0) buckets.set(key, [[index, item]]);
13504
+ else bucket.push([index, item]);
13505
+ }
13506
+ const results = Array.from({ length: items.length });
13507
+ await Promise.all(
13508
+ [...buckets.values()].map(async (group) => {
13509
+ for (const [index, item] of group) {
13510
+ results[index] = await runOne(item);
13511
+ }
13512
+ })
13513
+ );
13514
+ return results;
13515
+ }
11617
13516
  async function runMaterialization(items, options, runOne) {
11618
13517
  if (options.stopOnError === true) {
11619
13518
  const results = [];
@@ -11641,18 +13540,10 @@ function delay(ms) {
11641
13540
  }
11642
13541
  async function materializeIndexes(context, options = {}) {
11643
13542
  const { graph, graphId, backend, schemaVersion } = context;
11644
- if (backend.executeDdl === void 0) {
11645
- throw new ConfigurationError(
11646
- "store.materializeIndexes() requires a backend with `executeDdl` support. The bundled SQLite and Postgres backends provide it; a custom backend without `executeDdl` cannot run index DDL.",
11647
- { code: "MATERIALIZE_BACKEND_UNSUPPORTED" }
11648
- );
11649
- }
11650
- if (backend.getIndexMaterialization === void 0 || backend.recordIndexMaterialization === void 0) {
11651
- throw new ConfigurationError(
11652
- "store.materializeIndexes() requires a backend with index materialization status primitives. The bundled SQLite and Postgres backends provide them; a custom backend must implement `getIndexMaterialization` and `recordIndexMaterialization`.",
11653
- { code: "MATERIALIZE_BACKEND_UNSUPPORTED" }
11654
- );
11655
- }
13543
+ assertBackendSupportsIndexMaterialization(
13544
+ backend,
13545
+ "store.materializeIndexes()"
13546
+ );
11656
13547
  const declared = graph.indexes ?? [];
11657
13548
  const kindFilter = options.kinds === void 0 ? void 0 : new Set(options.kinds);
11658
13549
  if (kindFilter !== void 0) {
@@ -11699,65 +13590,42 @@ async function materializeIndexes(context, options = {}) {
11699
13590
  backend,
11700
13591
  relationalPhysicalNames
11701
13592
  );
11702
- const materializeEntry = (declaration) => declaration.entity === "vector" ? materializeVectorIndex(
11703
- declaration,
11704
- backend,
11705
- graphId,
11706
- schemaVersion,
11707
- existingByStatusKey,
11708
- invalidLeftovers
11709
- ) : materializeRelationalIndex(
11710
- declaration,
11711
- backend,
11712
- dialect,
11713
- ddlOptions,
11714
- graphId,
11715
- schemaVersion,
11716
- existingByStatusKey,
11717
- invalidLeftovers
11718
- );
11719
- if (options.stopOnError === true) {
11720
- const results2 = [];
11721
- for (const declaration of candidates) {
11722
- const entry2 = await materializeEntry(declaration);
11723
- results2.push(entry2);
11724
- if (entry2.status === "failed") break;
13593
+ const reservedSystemNames = resolveSystemIndexNames(backend.tableNames);
13594
+ const materializeEntry = (declaration) => {
13595
+ if (declaration.entity !== "vector" && reservedSystemNames.has(declaration.name)) {
13596
+ return Promise.resolve(
13597
+ entry(
13598
+ declaration,
13599
+ "failed",
13600
+ new Error(
13601
+ `Index name "${declaration.name}" collides with a TypeGraph system index. Rename the declaration \u2014 a same-named CREATE INDEX IF NOT EXISTS would silently no-op against the system index instead of creating the declared one.`
13602
+ )
13603
+ )
13604
+ );
11725
13605
  }
11726
- await refreshStatisticsAfterCreation(
13606
+ return declaration.entity === "vector" ? materializeVectorIndex(
13607
+ declaration,
11727
13608
  backend,
11728
- results2,
11729
- options,
11730
- ddlOptions.concurrent
13609
+ graphId,
13610
+ schemaVersion,
13611
+ existingByStatusKey,
13612
+ invalidLeftovers
13613
+ ) : materializeRelationalIndex(
13614
+ declaration,
13615
+ backend,
13616
+ dialect,
13617
+ ddlOptions,
13618
+ graphId,
13619
+ schemaVersion,
13620
+ existingByStatusKey,
13621
+ invalidLeftovers
11731
13622
  );
11732
- return { results: results2 };
11733
- }
11734
- const buckets = /* @__PURE__ */ new Map();
11735
- for (const declaration of candidates) {
11736
- const key = parallelBucketKey(declaration);
11737
- const list = buckets.get(key);
11738
- if (list === void 0) buckets.set(key, [declaration]);
11739
- else list.push(declaration);
11740
- }
11741
- const bucketResults = await Promise.all(
11742
- [...buckets.values()].map(async (group) => {
11743
- const out = [];
11744
- for (const declaration of group) {
11745
- out.push(await materializeEntry(declaration));
11746
- }
11747
- return out;
11748
- })
11749
- );
11750
- const byDeclaration = /* @__PURE__ */ new Map();
11751
- let bucketIndex = 0;
11752
- for (const group of buckets.values()) {
11753
- const entries = bucketResults[bucketIndex];
11754
- for (const [declarationIndex, declaration] of group.entries()) {
11755
- byDeclaration.set(declaration, entries[declarationIndex]);
11756
- }
11757
- bucketIndex += 1;
11758
- }
11759
- const results = candidates.map(
11760
- (declaration) => byDeclaration.get(declaration)
13623
+ };
13624
+ const results = await runBucketedMaterialization(
13625
+ candidates,
13626
+ options,
13627
+ (declaration) => parallelBucketKey(declaration),
13628
+ (declaration) => materializeEntry(declaration)
11761
13629
  );
11762
13630
  await refreshStatisticsAfterCreation(
11763
13631
  backend,
@@ -11814,7 +13682,7 @@ async function materializeRelationalIndex(declaration, backend, dialect, ddlOpti
11814
13682
  await backend.executeDdl(ddl);
11815
13683
  },
11816
13684
  existingByStatusKey,
11817
- invalidLeftovers
13685
+ physicalRebuildPreload: invalidLeftovers
11818
13686
  });
11819
13687
  }
11820
13688
  async function materializeVectorIndex(declaration, backend, graphId, schemaVersion, existingByStatusKey, invalidLeftovers) {
@@ -11872,7 +13740,7 @@ async function materializeVectorIndex(declaration, backend, graphId, schemaVersi
11872
13740
  driftLabel: "Vector index",
11873
13741
  run: () => backend.createVectorIndex(params),
11874
13742
  existingByStatusKey,
11875
- invalidLeftovers
13743
+ physicalRebuildPreload: invalidLeftovers
11876
13744
  });
11877
13745
  }
11878
13746
  async function materializeOne(declaration, backend, graphId, schemaVersion, action) {
@@ -11889,7 +13757,8 @@ async function materializeOne(declaration, backend, graphId, schemaVersion, acti
11889
13757
  statusKey,
11890
13758
  signature,
11891
13759
  driftLabel,
11892
- isInvalidLeftover: (physicalIndexName) => action.invalidLeftovers.has(physicalIndexName)
13760
+ needsPhysicalRebuild: (physicalIndexName) => action.physicalRebuildPreload.has(physicalIndexName),
13761
+ ...action.rebuildWithoutClaim === void 0 ? {} : { rebuildWithoutClaim: action.rebuildWithoutClaim }
11893
13762
  }
11894
13763
  );
11895
13764
  if (settled !== void 0) return settled;
@@ -11898,7 +13767,9 @@ async function materializeOne(declaration, backend, graphId, schemaVersion, acti
11898
13767
  statusKey,
11899
13768
  signature,
11900
13769
  driftLabel,
11901
- run
13770
+ run,
13771
+ ...action.freshNeedsPhysicalRebuild === void 0 ? {} : { freshNeedsPhysicalRebuild: action.freshNeedsPhysicalRebuild },
13772
+ ...action.rebuildWithoutClaim === void 0 ? {} : { rebuildWithoutClaim: action.rebuildWithoutClaim }
11902
13773
  });
11903
13774
  }
11904
13775
  try {
@@ -11935,9 +13806,9 @@ async function materializeOne(declaration, backend, graphId, schemaVersion, acti
11935
13806
  }
11936
13807
  async function settleAgainstExisting(existing, declaration, backend, graphId, schemaVersion, action) {
11937
13808
  if (existing?.materializedAt === void 0) return void 0;
11938
- const { statusKey, signature, driftLabel, isInvalidLeftover } = action;
13809
+ const { statusKey, signature, driftLabel, needsPhysicalRebuild } = action;
11939
13810
  if (existing.signature === signature) {
11940
- if (declaration.entity !== "vector" && hasIndexBuildClaimProtocol(backend) && await isInvalidLeftover(declaration.name)) {
13811
+ if (declaration.entity !== "vector" && (hasIndexBuildClaimProtocol(backend) || action.rebuildWithoutClaim === true) && await needsPhysicalRebuild(declaration.name)) {
11941
13812
  return void 0;
11942
13813
  }
11943
13814
  return entry(declaration, "alreadyMaterialized");
@@ -12002,7 +13873,8 @@ async function materializeWithClaim(declaration, backend, graphId, schemaVersion
12002
13873
  driftLabel,
12003
13874
  // Post-claim the bulk preload is stale (we may have waited on
12004
13875
  // another builder's claim), so re-check this index fresh.
12005
- isInvalidLeftover: (physicalIndexName) => hasInvalidIndexLeftover(backend, physicalIndexName)
13876
+ needsPhysicalRebuild: action.freshNeedsPhysicalRebuild ?? ((physicalIndexName) => hasInvalidIndexLeftover(backend, physicalIndexName)),
13877
+ ...action.rebuildWithoutClaim === void 0 ? {} : { rebuildWithoutClaim: action.rebuildWithoutClaim }
12006
13878
  }
12007
13879
  );
12008
13880
  if (settled !== void 0) return settled;
@@ -12063,6 +13935,7 @@ async function hasInvalidIndexLeftover(backend, physicalIndexName) {
12063
13935
  FROM pg_class c
12064
13936
  JOIN pg_index i ON i.indexrelid = c.oid
12065
13937
  WHERE c.relname = ${physicalIndexName}
13938
+ AND pg_catalog.pg_table_is_visible(c.oid)
12066
13939
  `)
12067
13940
  );
12068
13941
  return rows[0]?.invalid === true;
@@ -12071,17 +13944,64 @@ async function preloadInvalidIndexLeftovers(backend, physicalIndexNames) {
12071
13944
  if (backend.dialect !== "postgres" || physicalIndexNames.length === 0) {
12072
13945
  return /* @__PURE__ */ new Set();
12073
13946
  }
13947
+ const { invalid } = await preloadPhysicalIndexStates(
13948
+ backend,
13949
+ physicalIndexNames
13950
+ );
13951
+ return invalid;
13952
+ }
13953
+ async function preloadExistingTables(backend, tableNames) {
13954
+ if (tableNames.length === 0) return /* @__PURE__ */ new Set();
13955
+ if (backend.dialect === "postgres") {
13956
+ const rows2 = await backend.execute(
13957
+ asCompiledRowsSql(sql`
13958
+ SELECT c.relname AS name
13959
+ FROM pg_class c
13960
+ WHERE c.relname IN (${sqlValueList(tableNames)})
13961
+ AND c.relkind IN ('r', 'p')
13962
+ AND pg_catalog.pg_table_is_visible(c.oid)
13963
+ `)
13964
+ );
13965
+ return new Set(rows2.map((row) => row.name));
13966
+ }
12074
13967
  const rows = await backend.execute(
12075
13968
  asCompiledRowsSql(sql`
12076
- SELECT c.relname AS name
12077
- FROM pg_class c
12078
- JOIN pg_index i ON i.indexrelid = c.oid
12079
- WHERE c.relname IN (${sqlValueList(physicalIndexNames)})
12080
- AND NOT i.indisvalid
13969
+ SELECT name
13970
+ FROM sqlite_master
13971
+ WHERE type = 'table'
13972
+ AND name IN (${sqlValueList(tableNames)})
12081
13973
  `)
12082
13974
  );
12083
13975
  return new Set(rows.map((row) => row.name));
12084
13976
  }
13977
+ async function preloadPhysicalIndexStates(backend, physicalIndexNames) {
13978
+ const valid = /* @__PURE__ */ new Set();
13979
+ const invalid = /* @__PURE__ */ new Set();
13980
+ if (physicalIndexNames.length === 0) return { valid, invalid };
13981
+ if (backend.dialect === "postgres") {
13982
+ const rows2 = await backend.execute(
13983
+ asCompiledRowsSql(sql`
13984
+ SELECT c.relname AS name, i.indisvalid AS valid
13985
+ FROM pg_class c
13986
+ JOIN pg_index i ON i.indexrelid = c.oid
13987
+ WHERE c.relname IN (${sqlValueList(physicalIndexNames)})
13988
+ AND pg_catalog.pg_table_is_visible(c.oid)
13989
+ `)
13990
+ );
13991
+ for (const row of rows2) (row.valid ? valid : invalid).add(row.name);
13992
+ return { valid, invalid };
13993
+ }
13994
+ const rows = await backend.execute(
13995
+ asCompiledRowsSql(sql`
13996
+ SELECT name
13997
+ FROM sqlite_master
13998
+ WHERE type = 'index'
13999
+ AND name IN (${sqlValueList(physicalIndexNames)})
14000
+ `)
14001
+ );
14002
+ for (const row of rows) valid.add(row.name);
14003
+ return { valid, invalid };
14004
+ }
12085
14005
  async function dropInvalidIndexLeftover(backend, physicalIndexName) {
12086
14006
  if (backend.executeDdl === void 0) return;
12087
14007
  if (!await hasInvalidIndexLeftover(backend, physicalIndexName)) return;
@@ -12140,6 +14060,121 @@ function buildAttempt(args) {
12140
14060
  error: args.error?.message
12141
14061
  };
12142
14062
  }
14063
+ function backendSupportsIndexMaterialization(backend) {
14064
+ return backend.executeDdl !== void 0 && backend.getIndexMaterialization !== void 0 && backend.recordIndexMaterialization !== void 0;
14065
+ }
14066
+ function assertBackendSupportsIndexMaterialization(backend, surface) {
14067
+ if (backendSupportsIndexMaterialization(backend)) return;
14068
+ throw new ConfigurationError(
14069
+ `${surface} requires a backend with \`executeDdl\` and the index materialization status primitives (\`getIndexMaterialization\`, \`recordIndexMaterialization\`). The bundled SQLite and Postgres backends provide them; a custom backend must implement all three.`,
14070
+ { code: "MATERIALIZE_BACKEND_UNSUPPORTED" }
14071
+ );
14072
+ }
14073
+ async function materializeSystemIndexes(context, options = {}) {
14074
+ const { backend, graphId, schemaVersion } = context;
14075
+ assertBackendSupportsIndexMaterialization(
14076
+ backend,
14077
+ "store.materializeSystemIndexes()"
14078
+ );
14079
+ await ensureFocusedStatusTable(
14080
+ backend,
14081
+ backend.ensureIndexMaterializationsTable
14082
+ );
14083
+ const concurrent = backend.dialect === "postgres";
14084
+ const candidates = SYSTEM_INDEX_DECLARATIONS.map((declaration) => {
14085
+ const physicalTable = resolveSystemIndexTableName(
14086
+ declaration.table,
14087
+ backend.tableNames
14088
+ );
14089
+ return {
14090
+ declaration,
14091
+ physicalTable,
14092
+ name: systemIndexName(physicalTable, declaration.suffix)
14093
+ };
14094
+ });
14095
+ const statusKeys = candidates.map((candidate) => candidate.name);
14096
+ const targetTables = [
14097
+ ...new Set(candidates.map((candidate) => candidate.physicalTable))
14098
+ ];
14099
+ const [existingByStatusKey, physicalStates, existingTables] = await Promise.all([
14100
+ preloadMaterializations(backend, statusKeys),
14101
+ preloadPhysicalIndexStates(backend, statusKeys),
14102
+ preloadExistingTables(backend, targetTables)
14103
+ ]);
14104
+ const { valid: physicallyPresent } = physicalStates;
14105
+ const physicalRebuildPreload = new Set(
14106
+ statusKeys.filter((name) => !physicallyPresent.has(name))
14107
+ );
14108
+ const materializeEntry = async (candidate) => {
14109
+ const identity = {
14110
+ name: candidate.name,
14111
+ entity: "system",
14112
+ kind: candidate.declaration.table
14113
+ };
14114
+ if (!existingTables.has(candidate.physicalTable)) {
14115
+ return {
14116
+ indexName: candidate.name,
14117
+ entity: "system",
14118
+ kind: candidate.declaration.table,
14119
+ status: "skipped",
14120
+ reason: `Relation "${candidate.physicalTable}" does not exist in this database`
14121
+ };
14122
+ }
14123
+ const existing = existingByStatusKey.get(candidate.name);
14124
+ if (existing !== void 0 && existing.entity !== "system") {
14125
+ return entry(
14126
+ identity,
14127
+ "failed",
14128
+ new Error(
14129
+ `System index name "${candidate.name}" is already recorded by a ${existing.entity} index declaration (graph "${existing.graphId}"). Rename the graph-declared index \u2014 "typegraph_"-prefixed names belong to TypeGraph's system indexes.`
14130
+ )
14131
+ );
14132
+ }
14133
+ if (existing === void 0 && physicallyPresent.has(candidate.name)) {
14134
+ return entry(identity, "alreadyMaterialized");
14135
+ }
14136
+ const signature = await computeSystemIndexSignature(
14137
+ backend.dialect,
14138
+ candidate.physicalTable,
14139
+ candidate.declaration
14140
+ );
14141
+ const ddl = generateSystemIndexDDL(
14142
+ candidate.declaration,
14143
+ candidate.physicalTable,
14144
+ { concurrent }
14145
+ );
14146
+ return materializeOne(identity, backend, graphId, schemaVersion, {
14147
+ statusKey: candidate.name,
14148
+ signature,
14149
+ driftLabel: "System index",
14150
+ run: () => backend.executeDdl(ddl),
14151
+ existingByStatusKey,
14152
+ physicalRebuildPreload,
14153
+ freshNeedsPhysicalRebuild: async (physicalIndexName) => {
14154
+ const fresh = await preloadPhysicalIndexStates(backend, [
14155
+ physicalIndexName
14156
+ ]);
14157
+ return !fresh.valid.has(physicalIndexName);
14158
+ },
14159
+ rebuildWithoutClaim: true
14160
+ });
14161
+ };
14162
+ const results = await runBucketedMaterialization(
14163
+ candidates,
14164
+ options,
14165
+ (candidate) => candidate.physicalTable,
14166
+ (candidate) => materializeEntry(candidate)
14167
+ );
14168
+ await refreshStatisticsAfterCreation(backend, results, options, concurrent);
14169
+ return { results };
14170
+ }
14171
+ async function computeSystemIndexSignature(dialect, targetTableName, declaration) {
14172
+ const json = JSON.stringify(
14173
+ { dialect, targetTableName, declaration },
14174
+ sortedReplacer
14175
+ );
14176
+ return sha256Hex(json, 16);
14177
+ }
12143
14178
  async function computeIndexSignature(dialect, targetTableName, declaration) {
12144
14179
  const hashable = {
12145
14180
  dialect,
@@ -12368,13 +14403,13 @@ function buildEmbeddingTableCleanup(ctx, row) {
12368
14403
  const slots = await resolveRemovedKindEmbeddingSlots(ctx.backend, row);
12369
14404
  await Promise.all(
12370
14405
  slots.map(
12371
- (slot) => dropVectorSlotStorage(vectorStrategy, executeDdl, slot)
14406
+ (slot) => dropVectorSlotStorage(ctx.backend, vectorStrategy, executeDdl, slot)
12372
14407
  )
12373
14408
  );
12374
14409
  })();
12375
14410
  return [cleanup];
12376
14411
  }
12377
- async function dropVectorSlotStorage(strategy, executeDdl, slot) {
14412
+ async function dropVectorSlotStorage(backend, strategy, executeDdl, slot) {
12378
14413
  try {
12379
14414
  for (const ddl of strategy.buildDropStorage(slot)) {
12380
14415
  await executeDdl(ddl);
@@ -12382,6 +14417,7 @@ async function dropVectorSlotStorage(strategy, executeDdl, slot) {
12382
14417
  } catch (error) {
12383
14418
  if (!isMissingTableError(error)) throw error;
12384
14419
  }
14420
+ await backend.deleteVectorSlotContribution?.(slot);
12385
14421
  }
12386
14422
  async function resolveRemovedKindEmbeddingSlots(backend, row) {
12387
14423
  const priorVersion = row.schemaVersion - 1;
@@ -12457,7 +14493,12 @@ async function reclaimRemovedVectorFieldTables(context) {
12457
14493
  const results = [];
12458
14494
  for (const field2 of orphans) {
12459
14495
  try {
12460
- await dropVectorSlotStorage(vectorStrategy, executeDdl, field2.slot);
14496
+ await dropVectorSlotStorage(
14497
+ backend,
14498
+ vectorStrategy,
14499
+ executeDdl,
14500
+ field2.slot
14501
+ );
12461
14502
  results.push({
12462
14503
  kind: field2.kind,
12463
14504
  fieldPath: field2.fieldPath,
@@ -13841,9 +15882,11 @@ var Store = class _Store {
13841
15882
  * Tier 1 graph algorithms: shortest path, reachability, neighborhoods, and
13842
15883
  * degree centrality.
13843
15884
  *
13844
- * Each call compiles to a single recursive-CTE query against the backend.
13845
- * The facade is built lazily on first access and cached for the lifetime
13846
- * of the store.
15885
+ * Traversal calls use the iterative graph-operation substrate: a set-based
15886
+ * breadth-first frontier backed by a temporary working table, or a chunked
15887
+ * inline relation when the backend cannot pin a transactional connection.
15888
+ * `degree` uses a single count query. The facade is built lazily on first
15889
+ * access and cached for the lifetime of the store.
13847
15890
  *
13848
15891
  * @example
13849
15892
  * ```typescript
@@ -14607,10 +16650,10 @@ var Store = class _Store {
14607
16650
  }) : backendOptions;
14608
16651
  try {
14609
16652
  const result = await this.#backend.transaction(
14610
- async (txBackend, sql17) => invoke(
16653
+ async (txBackend, sql19) => invoke(
14611
16654
  this.#buildTransactionContext(
14612
16655
  txBackend,
14613
- sql17,
16656
+ sql19,
14614
16657
  runHooks,
14615
16658
  receiptRecorder
14616
16659
  )
@@ -14728,7 +16771,7 @@ var Store = class _Store {
14728
16771
  * (#140); when history capture is enabled it is replaced with a fail-loud
14729
16772
  * guard because raw graph writes would bypass recorded-time capture.
14730
16773
  */
14731
- #buildTransactionContext(txBackend, sql17, runHooks = this.#immediateHookRunner(), receiptRecorder) {
16774
+ #buildTransactionContext(txBackend, sql19, runHooks = this.#immediateHookRunner(), receiptRecorder) {
14732
16775
  const txNodeOperations = {
14733
16776
  ...this.#buildNodeOperations(this.#createNodeOperationContext(runHooks)),
14734
16777
  createQuery: () => this.#createQueryForBackend(txBackend),
@@ -14762,7 +16805,7 @@ var Store = class _Store {
14762
16805
  ));
14763
16806
  }
14764
16807
  const getNodeCollection = (kind) => this.#resolveDynamicNodeCollection(nodes, kind);
14765
- const sqlAvailability = this.#captureEnabled ? "history" : this.#revisionTrackingEnabled ? "revisionTracking" : sql17 === void 0 ? "unavailable" : "available";
16808
+ const sqlAvailability = this.#captureEnabled ? "history" : this.#revisionTrackingEnabled ? "revisionTracking" : sql19 === void 0 ? "unavailable" : "available";
14766
16809
  const base = {
14767
16810
  nodes,
14768
16811
  edges,
@@ -14771,9 +16814,9 @@ var Store = class _Store {
14771
16814
  getNodeCollection,
14772
16815
  runNodeOperationHooks
14773
16816
  };
14774
- const withSql = sql17 === void 0 ? base : {
16817
+ const withSql = sql19 === void 0 ? base : {
14775
16818
  ...base,
14776
- sql: this.#captureEnabled ? createHistoryUnsafeSqlRef() : this.#revisionTrackingEnabled ? createRevisionTrackingUnsafeSqlRef() : sql17
16819
+ sql: this.#captureEnabled ? createHistoryUnsafeSqlRef() : this.#revisionTrackingEnabled ? createRevisionTrackingUnsafeSqlRef() : sql19
14777
16820
  };
14778
16821
  return receiptRecorder === void 0 ? withSql : this.#attachMeasure(withSql);
14779
16822
  }
@@ -14964,6 +17007,7 @@ var Store = class _Store {
14964
17007
  merged,
14965
17008
  activeRow.version
14966
17009
  );
17010
+ await materializeVectorContributions(this.#backend, merged);
14967
17011
  const evolved = this.#cloneWithGraph(
14968
17012
  merged,
14969
17013
  options?.ref,
@@ -15066,6 +17110,29 @@ var Store = class _Store {
15066
17110
  options ?? {}
15067
17111
  );
15068
17112
  }
17113
+ /**
17114
+ * Materializes TypeGraph's own base-relation indexes
17115
+ * (`SYSTEM_INDEX_DECLARATIONS`) against the live database.
17116
+ *
17117
+ * Bootstrap DDL runs only on first boot, so a system index shipped in a
17118
+ * newer library version never reaches an already-initialized database on
17119
+ * its own. `createStoreWithSchema` runs this automatically; deployments
17120
+ * that boot with plain `createStore` / `createVerifiedStore` (zero-DDL by
17121
+ * contract) adopt new system indexes by calling this once under a role
17122
+ * that may run DDL. Same status-table, drift-signature, and Postgres
17123
+ * `CREATE INDEX CONCURRENTLY` claim semantics as `materializeIndexes`.
17124
+ */
17125
+ async materializeSystemIndexes(options) {
17126
+ const { activeRow } = await this.#loadCaughtUp("materialize");
17127
+ return materializeSystemIndexes(
17128
+ {
17129
+ backend: this.#baseBackend,
17130
+ graphId: this.graphId,
17131
+ schemaVersion: activeRow.version
17132
+ },
17133
+ options ?? {}
17134
+ );
17135
+ }
15069
17136
  /**
15070
17137
  * Recreate a vector field's per-field storage at its current declared
15071
17138
  * dimension, then optionally re-embed existing rows.
@@ -15122,10 +17189,14 @@ var Store = class _Store {
15122
17189
  for (const ddl of strategy.buildDropStorage(slot)) {
15123
17190
  await backend.executeDdl(ddl);
15124
17191
  }
15125
- for (const contribution of strategy.ownedTables(slot)) {
15126
- for (const ddl of contribution.createDdl) {
15127
- await backend.executeDdl(ddl);
17192
+ if (backend.ensureVectorSlotContribution === void 0) {
17193
+ for (const contribution of strategy.ownedTables(slot)) {
17194
+ for (const ddl of contribution.createDdl) {
17195
+ await backend.executeDdl(ddl);
17196
+ }
15128
17197
  }
17198
+ } else {
17199
+ await backend.ensureVectorSlotContribution(slot, { force: true });
15129
17200
  }
15130
17201
  const declaredIndexMaterializes = declaration.indexType !== "none" && backend.capabilities.vector?.indexTypes.includes(
15131
17202
  declaration.indexType
@@ -15607,6 +17678,20 @@ async function materializeRuntimeContributions(backend, graphId) {
15607
17678
  }
15608
17679
  await backend.ensureFulltextTable?.(graphId);
15609
17680
  }
17681
+ async function materializeVectorContributions(backend, graph) {
17682
+ if (backend.capabilities.vector?.supported !== true) return;
17683
+ const slots = resolveGraphVectorSlots(graph);
17684
+ const ensureVectorSlotContributions = backend.ensureVectorSlotContributions;
17685
+ if (ensureVectorSlotContributions !== void 0) {
17686
+ await ensureVectorSlotContributions(slots, { onDrift: "skip" });
17687
+ return;
17688
+ }
17689
+ const ensureVectorSlotContribution = backend.ensureVectorSlotContribution;
17690
+ if (ensureVectorSlotContribution === void 0) return;
17691
+ for (const slot of slots) {
17692
+ await ensureVectorSlotContribution(slot, { onDrift: "skip" });
17693
+ }
17694
+ }
15610
17695
  async function createStoreWithSchema(graph, backend, options) {
15611
17696
  const {
15612
17697
  graph: merged,
@@ -15618,6 +17703,10 @@ async function createStoreWithSchema(graph, backend, options) {
15618
17703
  preloaded: { activeRow, storedSchema }
15619
17704
  });
15620
17705
  await materializeRuntimeContributions(backend, merged.id);
17706
+ await materializeVectorContributions(backend, merged);
17707
+ if (options?.systemIndexes !== "skip") {
17708
+ await materializeSystemIndexesOnBoot(backend, merged.id, result);
17709
+ }
15621
17710
  const store = new Store(
15622
17711
  merged,
15623
17712
  backend,
@@ -15626,6 +17715,30 @@ async function createStoreWithSchema(graph, backend, options) {
15626
17715
  );
15627
17716
  return [store, result];
15628
17717
  }
17718
+ async function materializeSystemIndexesOnBoot(backend, graphId, result) {
17719
+ if (result.status === "breaking") return;
17720
+ if (!backendSupportsIndexMaterialization(backend)) return;
17721
+ const schemaVersion = result.status === "migrated" ? result.toVersion : result.version;
17722
+ try {
17723
+ const { results } = await materializeSystemIndexes(
17724
+ { backend: asRawBackend(backend), graphId, schemaVersion },
17725
+ {}
17726
+ );
17727
+ const failed = results.filter(
17728
+ (entryResult) => entryResult.status === "failed"
17729
+ );
17730
+ if (failed.length === 0) return;
17731
+ console.warn(
17732
+ `[typegraph] ${String(failed.length)} system index(es) failed to materialize at boot (${failed.map((entryResult) => entryResult.indexName).join(", ")}); the store is usable but the affected access paths fall back to scans. Retry via store.materializeSystemIndexes().`,
17733
+ failed[0]?.error
17734
+ );
17735
+ } catch (error) {
17736
+ console.warn(
17737
+ "[typegraph] system-index materialization failed at boot; the store is usable but new system indexes were not adopted. Retry via store.materializeSystemIndexes().",
17738
+ error
17739
+ );
17740
+ }
17741
+ }
15629
17742
  async function createVerifiedStore(graph, backend, options) {
15630
17743
  const {
15631
17744
  graph: merged,
@@ -15642,5 +17755,5 @@ async function createVerifiedStore(graph, backend, options) {
15642
17755
  }
15643
17756
 
15644
17757
  export { ExecutableAggregateQuery, ExecutableQuery, MAX_EXPLICIT_RECURSIVE_DEPTH, MAX_RECURSIVE_DEPTH, PreparedQuery, QueryBuilder, RecordedStoreView, StoreSearch, StoreView, UnionableQuery, avg, composeFragments, count, countDistinct, countDistinctEdges, countEdges, createFragment, createQueryBuilder, createStore, createStoreWithSchema, createVerifiedStore, defineSubgraphProject, field, having, havingEq, havingGt, havingGte, havingLt, havingLte, limitFragment, max, min, offsetFragment, orderByFragment, sum };
15645
- //# sourceMappingURL=chunk-5DIPR3L3.js.map
15646
- //# sourceMappingURL=chunk-5DIPR3L3.js.map
17758
+ //# sourceMappingURL=chunk-552KNYQJ.js.map
17759
+ //# sourceMappingURL=chunk-552KNYQJ.js.map