@nicia-ai/typegraph 0.36.0 → 0.37.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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-7BQLPXK6.cjs → chunk-5AVRFNP4.cjs} +2955 -898
  44. package/dist/chunk-5AVRFNP4.cjs.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-JFMEONLG.cjs → chunk-EJYMTO3K.cjs} +332 -188
  64. package/dist/chunk-EJYMTO3K.cjs.map +1 -0
  65. package/dist/{chunk-FPIZFYHE.js → chunk-EXU3RASM.js} +74 -15
  66. package/dist/chunk-EXU3RASM.js.map +1 -0
  67. package/dist/{chunk-5DIPR3L3.js → chunk-GA4TT3DH.js} +2338 -281
  68. package/dist/chunk-GA4TT3DH.js.map +1 -0
  69. package/dist/{chunk-H7YNSMA7.cjs → chunk-HAHNRASW.cjs} +20 -2
  70. package/dist/chunk-HAHNRASW.cjs.map +1 -0
  71. package/dist/{chunk-J3LEGNQB.cjs → chunk-IP3CGGPA.cjs} +8 -8
  72. package/dist/{chunk-J3LEGNQB.cjs.map → chunk-IP3CGGPA.cjs.map} +1 -1
  73. package/dist/{chunk-X4JBT642.cjs → chunk-IPSVVTWT.cjs} +94 -35
  74. package/dist/chunk-IPSVVTWT.cjs.map +1 -0
  75. package/dist/{chunk-VQEXF5D4.js → chunk-IXGYRP2P.js} +184 -92
  76. package/dist/chunk-IXGYRP2P.js.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-PWGX5DF5.cjs → chunk-NUIG3BCV.cjs} +100 -85
  88. package/dist/chunk-NUIG3BCV.cjs.map +1 -0
  89. package/dist/{chunk-ZAMFOJ5P.cjs → chunk-NXQFATMF.cjs} +269 -177
  90. package/dist/chunk-NXQFATMF.cjs.map +1 -0
  91. package/dist/{chunk-KBGQIPRQ.js → chunk-PC2UBAUB.js} +20 -3
  92. package/dist/chunk-PC2UBAUB.js.map +1 -0
  93. package/dist/chunk-QARBLBXT.cjs +219 -0
  94. package/dist/chunk-QARBLBXT.cjs.map +1 -0
  95. package/dist/{chunk-6FR2MMS6.js → chunk-QERAIDRJ.js} +45 -4
  96. package/dist/chunk-QERAIDRJ.js.map +1 -0
  97. package/dist/chunk-QSXTWU4U.cjs +12 -0
  98. package/dist/{chunk-4BMPGT35.cjs.map → chunk-QSXTWU4U.cjs.map} +1 -1
  99. package/dist/{chunk-DN5VP4JC.js → chunk-RCVLFTCT.js} +9 -10
  100. package/dist/chunk-RCVLFTCT.js.map +1 -0
  101. package/dist/{chunk-ZIGXMKQQ.js → chunk-RRSNR3TP.js} +3 -3
  102. package/dist/{chunk-ZIGXMKQQ.js.map → chunk-RRSNR3TP.js.map} +1 -1
  103. package/dist/{chunk-4CAXEB2F.cjs → chunk-TGOMW4MB.cjs} +91 -92
  104. package/dist/chunk-TGOMW4MB.cjs.map +1 -0
  105. package/dist/{chunk-PZKNKG5M.js → chunk-TKT2XHED.js} +8 -8
  106. package/dist/{chunk-PZKNKG5M.js.map → chunk-TKT2XHED.js.map} +1 -1
  107. package/dist/{chunk-S5F7ZLP2.js → chunk-TPF25IG6.js} +3 -3
  108. package/dist/{chunk-S5F7ZLP2.js.map → chunk-TPF25IG6.js.map} +1 -1
  109. package/dist/{chunk-5SEGFTZX.js → chunk-UGZUAYAR.js} +3 -3
  110. package/dist/{chunk-5SEGFTZX.js.map → chunk-UGZUAYAR.js.map} +1 -1
  111. package/dist/{chunk-BJVEU3ZL.js → chunk-UV3WP5WZ.js} +265 -121
  112. package/dist/chunk-UV3WP5WZ.js.map +1 -0
  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(
@@ -10457,6 +10736,13 @@ function buildCompileTimeReferentIndex(graph, runtimeEdgeNames) {
10457
10736
  // src/store/algorithms/context.ts
10458
10737
  var DEFAULT_ALGORITHM_MAX_HOPS = 10;
10459
10738
  var DEFAULT_NEIGHBOR_DEPTH = 1;
10739
+ function pickTemporalOptions(options) {
10740
+ return {
10741
+ ...options.temporalMode === void 0 ? {} : { temporalMode: options.temporalMode },
10742
+ ...options.asOf === void 0 ? {} : { asOf: options.asOf },
10743
+ ...options.recordedAsOf === void 0 ? {} : { recordedAsOf: options.recordedAsOf }
10744
+ };
10745
+ }
10460
10746
  function resolveTemporalOptions(ctx, options) {
10461
10747
  const { valid } = resolveReadCoordinate(
10462
10748
  options.temporalMode ?? ctx.defaultTemporalMode,
@@ -10512,6 +10798,16 @@ function resolveMaxHops(rawMaxHops, fallback, optionName) {
10512
10798
  }
10513
10799
  return value;
10514
10800
  }
10801
+ function resolveMaxIterations(value, fallback, algorithm) {
10802
+ const maxIterations = value ?? fallback;
10803
+ if (!Number.isSafeInteger(maxIterations) || maxIterations < 1) {
10804
+ throw new ConfigurationError(
10805
+ `${algorithm} maxIterations must be a positive safe integer, got ${String(maxIterations)}.`,
10806
+ { maxIterations }
10807
+ );
10808
+ }
10809
+ return maxIterations;
10810
+ }
10515
10811
  function assertEdgeKinds(edges) {
10516
10812
  if (edges.length === 0) {
10517
10813
  throw new ConfigurationError(
@@ -10561,137 +10857,1636 @@ function compileDirectionFilter(ctx, direction, nodeId, nodesTable) {
10561
10857
  }
10562
10858
  }
10563
10859
  }
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)
10860
+ function frontierIndexIdentifier(context) {
10861
+ return sql.identifier(
10862
+ `typegraph_iterative_${context.runId.replaceAll("-", "_")}_frontier`
10588
10863
  );
10589
- return rows.map((row) => ({
10590
- id: row.id,
10591
- kind: row.kind,
10592
- depth: Number(row.depth)
10593
- }));
10594
10864
  }
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;
10865
+ var DEFAULT_MAX_BIND_PARAMETERS = 999;
10866
+ var RESERVED_TEMPORAL_BIND_PARAMETERS_PER_BRANCH = 12;
10867
+ var RESERVED_WEIGHT_BIND_PARAMETERS_PER_BRANCH = 4;
10868
+ var WORKING_TABLE_ANALYZE_MINIMUM_ROWS = 16;
10869
+ var WORKING_TABLE_ANALYZE_GROWTH_FACTOR = 4;
10870
+ var ITERATIVE_WORKING_MEMORY_PATTERN = /^(\d+)(kB|MB|GB)$/;
10871
+ var WORKING_MEMORY_KILOBYTES_PER_UNIT = {
10872
+ kB: 1,
10873
+ MB: 1024,
10874
+ GB: 1024 * 1024
10875
+ };
10876
+ var MIN_WORKING_MEMORY_KILOBYTES = 64;
10877
+ var MAX_WORKING_MEMORY_KILOBYTES = 2147483647;
10878
+ function resolveWorkingMemory(workingMemory) {
10879
+ if (workingMemory === void 0) return void 0;
10880
+ const match = ITERATIVE_WORKING_MEMORY_PATTERN.exec(workingMemory);
10881
+ if (match === null) {
10882
+ throw new ConfigurationError(
10883
+ `Iterative graph operation workingMemory must be digits followed by kB, MB, or GB (for example "64MB"), got ${JSON.stringify(workingMemory)}.`,
10884
+ { workingMemory }
10885
+ );
10886
+ }
10887
+ const kilobytes = Number(match[1]) * (WORKING_MEMORY_KILOBYTES_PER_UNIT[match[2] ?? ""] ?? 0);
10888
+ if (kilobytes < MIN_WORKING_MEMORY_KILOBYTES || kilobytes > MAX_WORKING_MEMORY_KILOBYTES) {
10889
+ throw new ConfigurationError(
10890
+ `Iterative graph operation workingMemory must be between ${MIN_WORKING_MEMORY_KILOBYTES}kB and ${MAX_WORKING_MEMORY_KILOBYTES}kB, got ${JSON.stringify(workingMemory)}.`,
10891
+ { workingMemory, kilobytes }
10892
+ );
10893
+ }
10894
+ return workingMemory;
10620
10895
  }
10621
- async function executeNeighbors(ctx, sourceId, options) {
10622
- const depth = resolveMaxHops(options.depth, DEFAULT_NEIGHBOR_DEPTH, "depth");
10623
- return executeReachable(ctx, sourceId, {
10896
+ async function withInlineIterativeGraphOperation(ctx, options, run) {
10897
+ if (ctx.backend.capabilities.transactions) {
10898
+ return ctx.backend.transaction(
10899
+ async (backend) => run(createOperation(ctx, options, backend)),
10900
+ {
10901
+ isolationLevel: "repeatable_read",
10902
+ accessMode: "read_only"
10903
+ }
10904
+ );
10905
+ }
10906
+ return run(createOperation(ctx, options, ctx.backend));
10907
+ }
10908
+ async function runIterativeGraphOperation(ctx, options, plan) {
10909
+ if (!ctx.backend.capabilities.transactions) {
10910
+ throw new ConfigurationError(
10911
+ "Temporary-table graph iteration requires a transactional backend.",
10912
+ { dialect: ctx.backend.dialect }
10913
+ );
10914
+ }
10915
+ return ctx.backend.transaction(
10916
+ async (backend) => {
10917
+ const temporaryBackend = requireTemporaryStatements(backend);
10918
+ const runId = generateId();
10919
+ const workingTable = sql.identifier(
10920
+ `typegraph_iterative_${runId.replaceAll("-", "_")}`
10921
+ );
10922
+ const context = {
10923
+ operation: createOperation(ctx, options, temporaryBackend),
10924
+ backend: temporaryBackend,
10925
+ workingTable,
10926
+ graphId: ctx.graphId,
10927
+ runId,
10928
+ executeTemporary: async (query) => {
10929
+ await temporaryBackend.executeTemporaryStatement(
10930
+ asCompiledTemporaryStatementSql(query)
10931
+ );
10932
+ }
10933
+ };
10934
+ if (context.operation.workingMemory !== void 0) {
10935
+ const workingMemoryStatement = ctx.dialect.setTransactionWorkingMemory(
10936
+ context.operation.workingMemory
10937
+ );
10938
+ if (workingMemoryStatement !== void 0) {
10939
+ await context.executeTemporary(workingMemoryStatement);
10940
+ }
10941
+ }
10942
+ await context.executeTemporary(plan.createWorkingTable(context));
10943
+ let operationError;
10944
+ try {
10945
+ let state = await plan.initialize(context);
10946
+ let analyzedRowCount = await refreshWorkingTableStatistics(
10947
+ context,
10948
+ state.workingTableSize
10949
+ );
10950
+ for (let iteration = 1; iteration <= plan.maxIterations && !plan.hasConverged(state); iteration++) {
10951
+ state = await plan.runRound(context, state, iteration);
10952
+ if (iteration < plan.maxIterations && !plan.hasConverged(state)) {
10953
+ analyzedRowCount = await refreshWorkingTableStatistics(
10954
+ context,
10955
+ state.workingTableSize,
10956
+ analyzedRowCount
10957
+ );
10958
+ }
10959
+ }
10960
+ if (!plan.hasConverged(state)) {
10961
+ throw new GraphAlgorithmConvergenceError(
10962
+ plan.algorithm,
10963
+ plan.maxIterations
10964
+ );
10965
+ }
10966
+ return await plan.extractResult(context, state);
10967
+ } catch (error) {
10968
+ operationError = error;
10969
+ throw error;
10970
+ } finally {
10971
+ await dropWorkingTable(context, operationError);
10972
+ }
10973
+ },
10974
+ {
10975
+ isolationLevel: "repeatable_read",
10976
+ accessMode: "read_only",
10977
+ temporaryWrites: INTERNAL_TEMPORARY_WRITES
10978
+ }
10979
+ );
10980
+ }
10981
+ function shouldRefreshWorkingTableStatistics(workingTableSize, analyzedRowCount) {
10982
+ if (workingTableSize < WORKING_TABLE_ANALYZE_MINIMUM_ROWS) return false;
10983
+ if (analyzedRowCount === void 0) return true;
10984
+ return workingTableSize >= analyzedRowCount * WORKING_TABLE_ANALYZE_GROWTH_FACTOR;
10985
+ }
10986
+ async function refreshWorkingTableStatistics(context, workingTableSize, analyzedRowCount) {
10987
+ if (!shouldRefreshWorkingTableStatistics(workingTableSize, analyzedRowCount)) {
10988
+ return analyzedRowCount;
10989
+ }
10990
+ const statement = context.operation.ctx.dialect.analyzeTemporaryTable(
10991
+ context.workingTable
10992
+ );
10993
+ if (statement === void 0) return analyzedRowCount;
10994
+ await context.executeTemporary(statement);
10995
+ return workingTableSize;
10996
+ }
10997
+ async function dropWorkingTable(context, operationError) {
10998
+ try {
10999
+ await context.executeTemporary(
11000
+ sql`DROP TABLE IF EXISTS ${context.workingTable}`
11001
+ );
11002
+ } catch (cleanupError) {
11003
+ if (operationError === void 0) throw cleanupError;
11004
+ }
11005
+ }
11006
+ async function fetchVisibleWorkingNodes(operation, nodeIds) {
11007
+ const nodes = /* @__PURE__ */ new Map();
11008
+ for (const chunk2 of chunkValues(nodeIds, operation.maxWorkingSetSize)) {
11009
+ 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}`;
11010
+ const rows = await operation.backend.execute(
11011
+ asCompiledRowsSql(query)
11012
+ );
11013
+ for (const row of rows) nodes.set(nodeIdentityKey(row), row);
11014
+ }
11015
+ return [...nodes.values()].toSorted(
11016
+ (left, right) => compareNodeIdentity(left, right)
11017
+ );
11018
+ }
11019
+ async function reduceExpandedWorkingSet(operation, workingSet, direction, reduce) {
11020
+ const reduced = /* @__PURE__ */ new Map();
11021
+ for (const edgeKinds of operation.edgeKindChunks) {
11022
+ for (const chunk2 of chunkValues(workingSet, operation.maxWorkingSetSize)) {
11023
+ const rows = await operation.backend.execute(
11024
+ asCompiledRowsSql(
11025
+ compileExpansionQuery(operation, chunk2, direction, edgeKinds)
11026
+ )
11027
+ );
11028
+ for (const row of rows) {
11029
+ if (operation.weightExpression !== void 0 && !isPresent(row.weight)) {
11030
+ throw new CompilerInvariantError(
11031
+ "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.",
11032
+ { source: row.source_id, target: row.target_id }
11033
+ );
11034
+ }
11035
+ const expansion = {
11036
+ source: { id: row.source_id, kind: row.source_kind },
11037
+ target: { id: row.target_id, kind: row.target_kind },
11038
+ ...isPresent(row.weight) ? { weight: Number(row.weight) } : {}
11039
+ };
11040
+ const targetKey = nodeIdentityKey(expansion.target);
11041
+ const selected = reduce(reduced.get(targetKey), expansion);
11042
+ if (selected !== void 0) reduced.set(targetKey, selected);
11043
+ }
11044
+ }
11045
+ }
11046
+ return reduced;
11047
+ }
11048
+ function nodeIdentityKey(node) {
11049
+ return `${node.kind}\0${node.id}`;
11050
+ }
11051
+ function nodeIdentityKeyFromRow(id, kind) {
11052
+ if (typeof id !== "string" || typeof kind !== "string") return void 0;
11053
+ return nodeIdentityKey({ id, kind });
11054
+ }
11055
+ function compareNodeIdentity(left, right) {
11056
+ return compareCodePoints(left.id, right.id) || compareCodePoints(left.kind, right.kind);
11057
+ }
11058
+ var OUTGOING_DIRECTION_FIELDS = {
11059
+ joinField: "from_id",
11060
+ joinKindField: "from_kind",
11061
+ targetField: "to_id",
11062
+ targetKindField: "to_kind"
11063
+ };
11064
+ var INCOMING_DIRECTION_FIELDS = {
11065
+ joinField: "to_id",
11066
+ joinKindField: "to_kind",
11067
+ targetField: "from_id",
11068
+ targetKindField: "from_kind"
11069
+ };
11070
+ function fieldsForDirection(direction) {
11071
+ switch (direction) {
11072
+ case "out": {
11073
+ return [OUTGOING_DIRECTION_FIELDS];
11074
+ }
11075
+ case "in": {
11076
+ return [INCOMING_DIRECTION_FIELDS];
11077
+ }
11078
+ case "both": {
11079
+ return [OUTGOING_DIRECTION_FIELDS, INCOMING_DIRECTION_FIELDS];
11080
+ }
11081
+ }
11082
+ }
11083
+ function compileDirectionUnion(direction, compileDirection) {
11084
+ return sql.join(
11085
+ fieldsForDirection(direction).map((fields) => compileDirection(fields)),
11086
+ sql` UNION ALL `
11087
+ );
11088
+ }
11089
+ function compileExpansionQuery(operation, workingSet, direction, edgeKinds) {
11090
+ const workingValues = sql.join(
11091
+ workingSet.map((node) => sql`(${node.id}, ${node.kind})`),
11092
+ sql`, `
11093
+ );
11094
+ const workingCte = sql`WITH working_set(node_id, node_kind) AS (VALUES ${workingValues})`;
11095
+ const expansion = compileDirectionUnion(
11096
+ direction,
11097
+ (fields) => compileDirectionalExpansion(
11098
+ operation,
11099
+ sql`working_set`,
11100
+ sql`TRUE`,
11101
+ edgeKinds,
11102
+ fields.joinField,
11103
+ fields.joinKindField,
11104
+ fields.targetField,
11105
+ fields.targetKindField
11106
+ )
11107
+ );
11108
+ return sql`${workingCte} ${expansion}`;
11109
+ }
11110
+ function compileWorkingTableExpansion(operation, workingTable, sourceFilter, direction, edgeKinds) {
11111
+ return compileDirectionUnion(
11112
+ direction,
11113
+ (fields) => compileDirectionalExpansion(
11114
+ operation,
11115
+ workingTable,
11116
+ sourceFilter,
11117
+ edgeKinds,
11118
+ fields.joinField,
11119
+ fields.joinKindField,
11120
+ fields.targetField,
11121
+ fields.targetKindField
11122
+ )
11123
+ );
11124
+ }
11125
+ function compileWorkingTableEdgeExpansion(operation, workingTable, sourceFilter, direction, edgeKinds, sourceProjection) {
11126
+ return compileDirectionUnion(
11127
+ direction,
11128
+ (fields) => compileDirectionalEdgeExpansion(
11129
+ operation,
11130
+ workingTable,
11131
+ sourceFilter,
11132
+ edgeKinds,
11133
+ fields.joinField,
11134
+ fields.joinKindField,
11135
+ fields.targetField,
11136
+ fields.targetKindField,
11137
+ sourceProjection
11138
+ )
11139
+ );
11140
+ }
11141
+ function compileDirectionalExpansion(operation, workingRelation, sourceFilter, edgeKinds, joinField, joinKindField, targetField, targetKindField) {
11142
+ const edgeExpansion = compileDirectionalEdgeExpansion(
11143
+ operation,
11144
+ workingRelation,
11145
+ sourceFilter,
11146
+ edgeKinds,
11147
+ joinField,
11148
+ joinKindField,
11149
+ targetField,
11150
+ targetKindField
11151
+ );
11152
+ const weightColumn = operation.weightExpression === void 0 ? sql`` : sql`, expanded.weight`;
11153
+ 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}`;
11154
+ }
11155
+ function compileDirectionalEdgeExpansion(operation, workingRelation, sourceFilter, edgeKinds, joinField, joinKindField, targetField, targetKindField, sourceProjection) {
11156
+ const edgeKindFilter = compileKindFilter(sql.raw("e.kind"), edgeKinds);
11157
+ const weightColumn = operation.weightExpression === void 0 ? sql`` : sql`, ${operation.weightExpression} AS weight`;
11158
+ const sourceColumns = sourceProjection === void 0 ? sql`` : sql`, ${sourceProjection}`;
11159
+ return sql`SELECT w.node_id AS source_id, w.node_kind AS source_kind${sourceColumns}, 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}`;
11160
+ }
11161
+ function chunkValues(values, chunkSize) {
11162
+ const chunks = [];
11163
+ for (let offset = 0; offset < values.length; offset += chunkSize) {
11164
+ chunks.push(values.slice(offset, offset + chunkSize));
11165
+ }
11166
+ return chunks;
11167
+ }
11168
+ function createOperation(ctx, options, backend) {
11169
+ const temporal = resolveTemporalOptions(ctx, options);
11170
+ const currentTimestamp = currentReadInstant();
11171
+ const nodeTemporalFilter = compileTemporalFilter({
11172
+ mode: temporal.temporalMode,
11173
+ ...temporal.asOf === void 0 ? {} : { asOf: temporal.asOf },
11174
+ ...temporal.recordedAsOf === void 0 ? {} : { recordedAsOf: temporal.recordedAsOf },
11175
+ tableAlias: "n",
11176
+ currentTimestamp
11177
+ });
11178
+ const edgeTemporalFilter = compileTemporalFilter({
11179
+ mode: temporal.temporalMode,
11180
+ ...temporal.asOf === void 0 ? {} : { asOf: temporal.asOf },
11181
+ ...temporal.recordedAsOf === void 0 ? {} : { recordedAsOf: temporal.recordedAsOf },
11182
+ tableAlias: "e",
11183
+ currentTimestamp
11184
+ });
11185
+ const weightExpression = options.weightProperty === void 0 ? void 0 : compileWeightExpression(
11186
+ ctx.dialect,
11187
+ options.weightProperty,
11188
+ options.defaultWeight
11189
+ );
11190
+ const direction = options.direction ?? "out";
11191
+ const branchCount = direction === "both" ? 2 : 1;
11192
+ const parameterLimit = backend.capabilities.maxBindParameters ?? DEFAULT_MAX_BIND_PARAMETERS;
11193
+ const reservedPerBranch = RESERVED_TEMPORAL_BIND_PARAMETERS_PER_BRANCH + (weightExpression === void 0 ? 0 : RESERVED_WEIGHT_BIND_PARAMETERS_PER_BRANCH);
11194
+ const fixedParameters = branchCount * reservedPerBranch;
11195
+ const sharedBudget = parameterLimit - fixedParameters;
11196
+ const maxEdgeKindsPerQuery = Math.floor(sharedBudget / (branchCount + 2));
11197
+ if (maxEdgeKindsPerQuery < 1) {
11198
+ throw new ConfigurationError(
11199
+ "An iterative graph operation cannot fit its fixed filters within the backend bind-parameter limit.",
11200
+ { parameterLimit, direction }
11201
+ );
11202
+ }
11203
+ const edgeKindChunks = chunkValues(
11204
+ [...new Set(options.edges)].toSorted(
11205
+ (left, right) => compareStrings(left, right)
11206
+ ),
11207
+ maxEdgeKindsPerQuery
11208
+ );
11209
+ const largestEdgeKindChunk = Math.max(
11210
+ ...edgeKindChunks.map((chunk2) => chunk2.length)
11211
+ );
11212
+ const maxWorkingSetSize = Math.floor(
11213
+ (parameterLimit - branchCount * (largestEdgeKindChunk + reservedPerBranch)) / 2
11214
+ );
11215
+ if (maxWorkingSetSize < 1) {
11216
+ throw new ConfigurationError(
11217
+ "An iterative graph operation cannot fit one working-set node within the backend bind-parameter limit.",
11218
+ { parameterLimit, direction }
11219
+ );
11220
+ }
11221
+ return {
11222
+ backend,
11223
+ ctx,
11224
+ edgeKindChunks,
11225
+ direction,
11226
+ maxWorkingSetSize,
11227
+ nodeTemporalFilter,
11228
+ edgeTemporalFilter,
11229
+ schema: resolveReadSchema(ctx, options),
11230
+ workingMemory: resolveWorkingMemory(options.workingMemory),
11231
+ weightExpression
11232
+ };
11233
+ }
11234
+ function compileWeightExpression(dialect, weightProperty, defaultWeight) {
11235
+ const extracted = dialect.jsonExtractDouble(
11236
+ sql.raw("e.props"),
11237
+ jsonPointer([weightProperty])
11238
+ );
11239
+ return defaultWeight === void 0 ? extracted : sql`COALESCE(${extracted}, ${defaultWeight})`;
11240
+ }
11241
+ function supportsTemporaryIteration(ctx) {
11242
+ return ctx.backend.capabilities.transactions && ctx.backend.capabilities.graphAnalytics?.supported !== false && ctx.backend.capabilities.returning !== false && ctx.backend.executeTemporaryStatement !== void 0;
11243
+ }
11244
+ function requireTemporaryStatements(backend) {
11245
+ if (backend.executeTemporaryStatement === void 0) {
11246
+ throw new ConfigurationError(
11247
+ "Iterative graph operations require temporary-statement support on the transaction backend.",
11248
+ { dialect: backend.dialect },
11249
+ {
11250
+ suggestion: "Use a built-in SQLite/PostgreSQL backend or implement executeTemporaryStatement on the custom backend."
11251
+ }
11252
+ );
11253
+ }
11254
+ return backend;
11255
+ }
11256
+
11257
+ // src/store/algorithms/breadth-first.ts
11258
+ async function findReachableNodes(ctx, sourceId, maxHops, options) {
11259
+ if (supportsTemporaryIteration(ctx)) {
11260
+ return findReachableNodesInWorkingTable(ctx, sourceId, maxHops, options);
11261
+ }
11262
+ return findReachableNodesInline(ctx, sourceId, maxHops, options);
11263
+ }
11264
+ async function findShortestPath(ctx, sourceId, targetId, maxHops, options) {
11265
+ if (supportsTemporaryIteration(ctx)) {
11266
+ return findShortestPathInWorkingTable(
11267
+ ctx,
11268
+ sourceId,
11269
+ targetId,
11270
+ maxHops,
11271
+ options
11272
+ );
11273
+ }
11274
+ return findShortestPathInline(ctx, sourceId, targetId, maxHops, options);
11275
+ }
11276
+ async function findReachableNodesInWorkingTable(ctx, sourceId, maxHops, options) {
11277
+ return runIterativeGraphOperation(ctx, options, {
11278
+ algorithm: "reachable",
11279
+ maxIterations: maxHops,
11280
+ createWorkingTable,
11281
+ async initialize(context) {
11282
+ const seeded = await seedWorkingSide(context, sourceId, "forward");
11283
+ return {
11284
+ depth: 0,
11285
+ frontierCount: seeded.length,
11286
+ workingTableSize: seeded.length
11287
+ };
11288
+ },
11289
+ async runRound(context, state) {
11290
+ const nextDepth = state.depth + 1;
11291
+ const frontierCount = await expandReachableWorkingTableRound(
11292
+ context,
11293
+ "forward",
11294
+ state.depth,
11295
+ nextDepth,
11296
+ context.operation.direction
11297
+ );
11298
+ return {
11299
+ depth: nextDepth,
11300
+ frontierCount,
11301
+ workingTableSize: state.workingTableSize + frontierCount
11302
+ };
11303
+ },
11304
+ hasConverged(state) {
11305
+ return state.frontierCount === 0 || state.depth >= maxHops;
11306
+ },
11307
+ async extractResult(context) {
11308
+ const rows = await readWorkingRows(context, "forward");
11309
+ return rows.map((row) => ({
11310
+ id: row.node_id,
11311
+ kind: row.node_kind,
11312
+ depth: Number(row.depth)
11313
+ })).toSorted(
11314
+ (left, right) => left.depth - right.depth || compareNodeIdentity(left, right)
11315
+ );
11316
+ }
11317
+ });
11318
+ }
11319
+ async function findShortestPathInWorkingTable(ctx, sourceId, targetId, maxHops, options) {
11320
+ return runIterativeGraphOperation(ctx, options, {
11321
+ algorithm: "shortestPath",
11322
+ maxIterations: maxHops,
11323
+ createWorkingTable,
11324
+ async initialize(context) {
11325
+ const forwardSeeds = await seedWorkingSide(context, sourceId, "forward");
11326
+ const reverseSeeds = await seedWorkingSide(context, targetId, "reverse");
11327
+ return {
11328
+ forwardDepth: 0,
11329
+ reverseDepth: 0,
11330
+ forwardFrontierCount: forwardSeeds.length,
11331
+ reverseFrontierCount: reverseSeeds.length,
11332
+ meeting: findSeedMeeting(forwardSeeds, reverseSeeds),
11333
+ workingTableSize: forwardSeeds.length + reverseSeeds.length
11334
+ };
11335
+ },
11336
+ async runRound(context, state) {
11337
+ const expandForward = state.forwardFrontierCount <= state.reverseFrontierCount;
11338
+ const side = expandForward ? "forward" : "reverse";
11339
+ const currentDepth = expandForward ? state.forwardDepth : state.reverseDepth;
11340
+ const nextDepth = currentDepth + 1;
11341
+ const direction = expandForward ? context.operation.direction : reverseDirection(context.operation.direction);
11342
+ const round = await expandWorkingTableRound(
11343
+ context,
11344
+ side,
11345
+ currentDepth,
11346
+ nextDepth,
11347
+ direction,
11348
+ maxHops
11349
+ );
11350
+ return {
11351
+ forwardDepth: expandForward ? nextDepth : state.forwardDepth,
11352
+ reverseDepth: expandForward ? state.reverseDepth : nextDepth,
11353
+ forwardFrontierCount: expandForward ? round.insertedCount : state.forwardFrontierCount,
11354
+ reverseFrontierCount: expandForward ? state.reverseFrontierCount : round.insertedCount,
11355
+ meeting: round.meeting,
11356
+ workingTableSize: state.workingTableSize + round.insertedCount
11357
+ };
11358
+ },
11359
+ hasConverged(state) {
11360
+ return state.meeting !== void 0 || state.forwardFrontierCount === 0 || state.reverseFrontierCount === 0 || state.forwardDepth + state.reverseDepth >= maxHops;
11361
+ },
11362
+ async extractResult(context, state) {
11363
+ if (state.meeting === void 0) return;
11364
+ const rows = await readWorkingRows(context);
11365
+ return buildShortestPath(
11366
+ nodeIdentityKey(state.meeting),
11367
+ createVisitedMapFromRows(rows, "forward"),
11368
+ createVisitedMapFromRows(rows, "reverse")
11369
+ );
11370
+ }
11371
+ });
11372
+ }
11373
+ function createWorkingTable(context) {
11374
+ return sql`
11375
+ CREATE TEMP TABLE ${context.workingTable} (
11376
+ graph_id TEXT NOT NULL,
11377
+ run_id TEXT NOT NULL,
11378
+ side TEXT NOT NULL,
11379
+ node_id TEXT NOT NULL,
11380
+ node_kind TEXT NOT NULL,
11381
+ depth INTEGER NOT NULL,
11382
+ predecessor_id TEXT,
11383
+ predecessor_kind TEXT,
11384
+ meeting_depth INTEGER,
11385
+ PRIMARY KEY (graph_id, run_id, side, node_kind, node_id)
11386
+ )
11387
+ `;
11388
+ }
11389
+ async function seedWorkingSide(context, nodeId, side) {
11390
+ const { operation, workingTable, graphId, runId } = context;
11391
+ return context.backend.execute(
11392
+ asCompiledRowsSql(sql`
11393
+ INSERT INTO ${workingTable}
11394
+ (graph_id, run_id, side, node_id, node_kind, depth,
11395
+ predecessor_id, predecessor_kind)
11396
+ SELECT ${graphId}, ${runId}, ${side}, n.id, n.kind, 0, NULL, NULL
11397
+ FROM ${operation.schema.nodesTable} n
11398
+ WHERE n.graph_id = ${graphId}
11399
+ AND n.id = ${nodeId}
11400
+ AND ${operation.nodeTemporalFilter}
11401
+ ON CONFLICT (graph_id, run_id, side, node_kind, node_id) DO NOTHING
11402
+ RETURNING node_id, node_kind
11403
+ `)
11404
+ );
11405
+ }
11406
+ function findSeedMeeting(forwardSeeds, reverseSeeds) {
11407
+ const reverseKeys = new Set(
11408
+ reverseSeeds.map(
11409
+ (row) => nodeIdentityKey({ id: row.node_id, kind: row.node_kind })
11410
+ )
11411
+ );
11412
+ let meeting;
11413
+ for (const row of forwardSeeds) {
11414
+ const candidate = { id: row.node_id, kind: row.node_kind, depth: 0 };
11415
+ if (!reverseKeys.has(nodeIdentityKey(candidate))) continue;
11416
+ if (meeting === void 0 || compareNodeIdentity(candidate, meeting) < 0) {
11417
+ meeting = candidate;
11418
+ }
11419
+ }
11420
+ return meeting;
11421
+ }
11422
+ async function expandWorkingTableRound(context, side, currentDepth, nextDepth, direction, maxHops) {
11423
+ const oppositeSide = side === "forward" ? "reverse" : "forward";
11424
+ let insertedCount = 0;
11425
+ let meeting;
11426
+ const { dialect } = context.operation.ctx;
11427
+ const targetKind = dialect.binaryText(sql`expanded.target_kind`);
11428
+ const targetId = dialect.binaryText(sql`expanded.target_id`);
11429
+ const sourceId = dialect.binaryText(sql`expanded.source_id`);
11430
+ const sourceKind = dialect.binaryText(sql`expanded.source_kind`);
11431
+ for (const edgeKinds of context.operation.edgeKindChunks) {
11432
+ const sourceFilter = sql`
11433
+ w.graph_id = ${context.graphId}
11434
+ AND w.run_id = ${context.runId}
11435
+ AND w.side = ${side}
11436
+ AND w.depth = ${currentDepth}
11437
+ `;
11438
+ const expansion = compileWorkingTableExpansion(
11439
+ context.operation,
11440
+ context.workingTable,
11441
+ sourceFilter,
11442
+ direction,
11443
+ edgeKinds
11444
+ );
11445
+ const rows = await context.backend.execute(
11446
+ asCompiledRowsSql(sql`
11447
+ INSERT INTO ${context.workingTable}
11448
+ (graph_id, run_id, side, node_id, node_kind, depth,
11449
+ predecessor_id, predecessor_kind, meeting_depth)
11450
+ SELECT
11451
+ ${context.graphId}, ${context.runId}, ${side},
11452
+ ranked.target_id, ranked.target_kind, ${nextDepth},
11453
+ ranked.source_id, ranked.source_kind,
11454
+ (
11455
+ SELECT opposite.depth
11456
+ FROM ${context.workingTable} opposite
11457
+ WHERE opposite.graph_id = ${context.graphId}
11458
+ AND opposite.run_id = ${context.runId}
11459
+ AND opposite.side = ${oppositeSide}
11460
+ AND opposite.node_id = ranked.target_id
11461
+ AND opposite.node_kind = ranked.target_kind
11462
+ )
11463
+ FROM (
11464
+ SELECT expanded.*,
11465
+ ROW_NUMBER() OVER (
11466
+ PARTITION BY ${targetKind}, ${targetId}
11467
+ ORDER BY ${sourceId}, ${sourceKind}
11468
+ ) AS candidate_rank
11469
+ FROM (${expansion}) expanded
11470
+ ) ranked
11471
+ WHERE ranked.candidate_rank = 1
11472
+ AND NOT EXISTS (
11473
+ SELECT 1
11474
+ FROM ${context.workingTable} visited
11475
+ WHERE visited.graph_id = ${context.graphId}
11476
+ AND visited.run_id = ${context.runId}
11477
+ AND visited.side = ${side}
11478
+ AND visited.node_id = ranked.target_id
11479
+ AND visited.node_kind = ranked.target_kind
11480
+ )
11481
+ ON CONFLICT (graph_id, run_id, side, node_kind, node_id) DO NOTHING
11482
+ RETURNING node_id, node_kind, meeting_depth
11483
+ `)
11484
+ );
11485
+ insertedCount += rows.length;
11486
+ meeting = selectRoundMeeting(meeting, rows, nextDepth, maxHops);
11487
+ }
11488
+ return { insertedCount, meeting };
11489
+ }
11490
+ function selectRoundMeeting(currentMeeting, rows, nextDepth, maxHops) {
11491
+ let meeting = currentMeeting;
11492
+ for (const row of rows) {
11493
+ if (row.meeting_depth === null || row.meeting_depth === void 0) {
11494
+ continue;
11495
+ }
11496
+ const totalDepth = nextDepth + Number(row.meeting_depth);
11497
+ if (totalDepth > maxHops) continue;
11498
+ const candidate = {
11499
+ id: row.node_id,
11500
+ kind: row.node_kind,
11501
+ depth: totalDepth
11502
+ };
11503
+ if (meeting === void 0 || candidate.depth < meeting.depth || candidate.depth === meeting.depth && compareNodeIdentity(candidate, meeting) < 0) {
11504
+ meeting = candidate;
11505
+ }
11506
+ }
11507
+ return meeting;
11508
+ }
11509
+ async function expandReachableWorkingTableRound(context, side, currentDepth, nextDepth, direction) {
11510
+ let insertedCount = 0;
11511
+ for (const edgeKinds of context.operation.edgeKindChunks) {
11512
+ const sourceFilter = sql`
11513
+ w.graph_id = ${context.graphId}
11514
+ AND w.run_id = ${context.runId}
11515
+ AND w.side = ${side}
11516
+ AND w.depth = ${currentDepth}
11517
+ `;
11518
+ const edgeExpansion = compileWorkingTableEdgeExpansion(
11519
+ context.operation,
11520
+ context.workingTable,
11521
+ sourceFilter,
11522
+ direction,
11523
+ edgeKinds
11524
+ );
11525
+ const rows = await context.backend.execute(
11526
+ asCompiledRowsSql(sql`
11527
+ INSERT INTO ${context.workingTable}
11528
+ (graph_id, run_id, side, node_id, node_kind, depth,
11529
+ predecessor_id, predecessor_kind)
11530
+ SELECT
11531
+ ${context.graphId}, ${context.runId}, ${side},
11532
+ candidates.target_id, candidates.target_kind, ${nextDepth},
11533
+ NULL, NULL
11534
+ FROM (
11535
+ SELECT DISTINCT expanded.target_id, expanded.target_kind
11536
+ FROM (${edgeExpansion}) expanded
11537
+ ) candidates
11538
+ JOIN ${context.operation.schema.nodesTable} n
11539
+ ON n.graph_id = ${context.graphId}
11540
+ AND n.id = candidates.target_id
11541
+ AND n.kind = candidates.target_kind
11542
+ WHERE ${context.operation.nodeTemporalFilter}
11543
+ AND NOT EXISTS (
11544
+ SELECT 1
11545
+ FROM ${context.workingTable} visited
11546
+ WHERE visited.graph_id = ${context.graphId}
11547
+ AND visited.run_id = ${context.runId}
11548
+ AND visited.side = ${side}
11549
+ AND visited.node_id = candidates.target_id
11550
+ AND visited.node_kind = candidates.target_kind
11551
+ )
11552
+ ON CONFLICT (graph_id, run_id, side, node_kind, node_id) DO NOTHING
11553
+ RETURNING node_id
11554
+ `)
11555
+ );
11556
+ insertedCount += rows.length;
11557
+ }
11558
+ return insertedCount;
11559
+ }
11560
+ async function readWorkingRows(context, side) {
11561
+ const sideFilter = side === void 0 ? sql`TRUE` : sql`side = ${side}`;
11562
+ return context.backend.execute(
11563
+ asCompiledRowsSql(sql`
11564
+ SELECT side, node_id, node_kind, depth,
11565
+ predecessor_id, predecessor_kind
11566
+ FROM ${context.workingTable}
11567
+ WHERE graph_id = ${context.graphId}
11568
+ AND run_id = ${context.runId}
11569
+ AND ${sideFilter}
11570
+ `)
11571
+ );
11572
+ }
11573
+ async function findReachableNodesInline(ctx, sourceId, maxHops, options) {
11574
+ return withInlineIterativeGraphOperation(ctx, options, async (operation) => {
11575
+ const sources = await fetchVisibleWorkingNodes(operation, [sourceId]);
11576
+ if (sources.length === 0) return [];
11577
+ const reached = new Map(
11578
+ sources.map((source) => [
11579
+ nodeIdentityKey(source),
11580
+ { id: source.id, kind: source.kind, depth: 0 }
11581
+ ])
11582
+ );
11583
+ let workingSet = sources;
11584
+ for (let depth = 1; depth <= maxHops && workingSet.length > 0; depth++) {
11585
+ const candidates = await expandInlineWorkingSet(
11586
+ operation,
11587
+ workingSet,
11588
+ operation.direction,
11589
+ reached
11590
+ );
11591
+ const nextWorkingSet = sortCandidates(candidates);
11592
+ for (const candidate of nextWorkingSet) {
11593
+ reached.set(nodeIdentityKey(candidate), {
11594
+ id: candidate.id,
11595
+ kind: candidate.kind,
11596
+ depth
11597
+ });
11598
+ }
11599
+ workingSet = nextWorkingSet;
11600
+ }
11601
+ return [...reached.values()].toSorted(
11602
+ (left, right) => left.depth - right.depth || compareNodeIdentity(left, right)
11603
+ );
11604
+ });
11605
+ }
11606
+ async function findShortestPathInline(ctx, sourceId, targetId, maxHops, options) {
11607
+ return withInlineIterativeGraphOperation(ctx, options, async (operation) => {
11608
+ const visibleNodes = await fetchVisibleWorkingNodes(operation, [
11609
+ sourceId,
11610
+ targetId
11611
+ ]);
11612
+ const sources = visibleNodes.filter((node) => node.id === sourceId);
11613
+ const targets = visibleNodes.filter((node) => node.id === targetId);
11614
+ if (sources.length === 0 || targets.length === 0) return;
11615
+ if (sourceId === targetId) return { nodes: [sources[0]], depth: 0 };
11616
+ const forwardVisited = createVisitedMap(sources);
11617
+ const reverseVisited = createVisitedMap(targets);
11618
+ let forwardWorkingSet = sources;
11619
+ let reverseWorkingSet = targets;
11620
+ let forwardDepth = 0;
11621
+ let reverseDepth = 0;
11622
+ while (forwardWorkingSet.length > 0 && reverseWorkingSet.length > 0 && forwardDepth + reverseDepth < maxHops) {
11623
+ const expandForward = forwardWorkingSet.length <= reverseWorkingSet.length;
11624
+ const activeVisited = expandForward ? forwardVisited : reverseVisited;
11625
+ const oppositeVisited = expandForward ? reverseVisited : forwardVisited;
11626
+ const currentWorkingSet = expandForward ? forwardWorkingSet : reverseWorkingSet;
11627
+ const direction = expandForward ? operation.direction : reverseDirection(operation.direction);
11628
+ const candidates = await expandInlineWorkingSet(
11629
+ operation,
11630
+ currentWorkingSet,
11631
+ direction,
11632
+ activeVisited
11633
+ );
11634
+ const nextDepth = (expandForward ? forwardDepth : reverseDepth) + 1;
11635
+ const nextWorkingSet = sortCandidates(candidates);
11636
+ for (const candidate of nextWorkingSet) {
11637
+ activeVisited.set(nodeIdentityKey(candidate), {
11638
+ id: candidate.id,
11639
+ kind: candidate.kind,
11640
+ depth: nextDepth,
11641
+ parentKey: nodeIdentityKey({
11642
+ id: candidate.parentId,
11643
+ kind: candidate.parentKind
11644
+ })
11645
+ });
11646
+ }
11647
+ const meetingKey = findMeetingNodeKey(
11648
+ nextWorkingSet,
11649
+ activeVisited,
11650
+ oppositeVisited
11651
+ );
11652
+ if (meetingKey !== void 0) {
11653
+ return buildShortestPath(meetingKey, forwardVisited, reverseVisited);
11654
+ }
11655
+ if (expandForward) {
11656
+ forwardWorkingSet = nextWorkingSet;
11657
+ forwardDepth = nextDepth;
11658
+ } else {
11659
+ reverseWorkingSet = nextWorkingSet;
11660
+ reverseDepth = nextDepth;
11661
+ }
11662
+ }
11663
+ return;
11664
+ });
11665
+ }
11666
+ async function expandInlineWorkingSet(operation, workingSet, direction, visited) {
11667
+ return reduceExpandedWorkingSet(
11668
+ operation,
11669
+ workingSet,
11670
+ direction,
11671
+ (existing, expansion) => selectPredecessor(existing, expansion, visited)
11672
+ );
11673
+ }
11674
+ function selectPredecessor(existing, expansion, visited) {
11675
+ if (visited.has(nodeIdentityKey(expansion.target))) return existing;
11676
+ if (existing !== void 0 && compareNodeIdentity(expansion.source, {
11677
+ id: existing.parentId,
11678
+ kind: existing.parentKind
11679
+ }) >= 0) {
11680
+ return existing;
11681
+ }
11682
+ return {
11683
+ id: expansion.target.id,
11684
+ kind: expansion.target.kind,
11685
+ parentId: expansion.source.id,
11686
+ parentKind: expansion.source.kind
11687
+ };
11688
+ }
11689
+ function sortCandidates(candidates) {
11690
+ return [...candidates.values()].toSorted(
11691
+ (left, right) => compareNodeIdentity(left, right)
11692
+ );
11693
+ }
11694
+ function findMeetingNodeKey(candidates, activeVisited, oppositeVisited) {
11695
+ let best;
11696
+ for (const candidate of candidates) {
11697
+ const key = nodeIdentityKey(candidate);
11698
+ const active = activeVisited.get(key);
11699
+ const opposite = oppositeVisited.get(key);
11700
+ if (active === void 0 || opposite === void 0) continue;
11701
+ const meeting = {
11702
+ key,
11703
+ node: { id: candidate.id, kind: candidate.kind },
11704
+ depth: active.depth + opposite.depth
11705
+ };
11706
+ if (best === void 0 || meeting.depth < best.depth || meeting.depth === best.depth && compareNodeIdentity(meeting.node, best.node) < 0) {
11707
+ best = meeting;
11708
+ }
11709
+ }
11710
+ return best?.key;
11711
+ }
11712
+ function buildShortestPath(meetingKey, forwardVisited, reverseVisited) {
11713
+ const forwardKeys = [];
11714
+ let cursor = meetingKey;
11715
+ while (cursor !== void 0) {
11716
+ forwardKeys.push(cursor);
11717
+ cursor = forwardVisited.get(cursor)?.parentKey;
11718
+ }
11719
+ forwardKeys.reverse();
11720
+ const pathKeys = [...forwardKeys];
11721
+ cursor = reverseVisited.get(meetingKey)?.parentKey;
11722
+ while (cursor !== void 0) {
11723
+ pathKeys.push(cursor);
11724
+ cursor = reverseVisited.get(cursor)?.parentKey;
11725
+ }
11726
+ const nodes = pathKeys.map((key) => {
11727
+ const node = forwardVisited.get(key) ?? reverseVisited.get(key);
11728
+ return { id: node?.id ?? "", kind: node?.kind ?? "" };
11729
+ });
11730
+ return { nodes, depth: nodes.length - 1 };
11731
+ }
11732
+ function createVisitedMap(nodes) {
11733
+ return new Map(
11734
+ nodes.map((node) => [
11735
+ nodeIdentityKey(node),
11736
+ { id: node.id, kind: node.kind, depth: 0, parentKey: void 0 }
11737
+ ])
11738
+ );
11739
+ }
11740
+ function createVisitedMapFromRows(rows, side) {
11741
+ return new Map(
11742
+ rows.filter((row) => row.side === side).map((row) => {
11743
+ const parentKey = nodeIdentityKeyFromRow(
11744
+ row.predecessor_id,
11745
+ row.predecessor_kind
11746
+ );
11747
+ const node = {
11748
+ id: row.node_id,
11749
+ kind: row.node_kind,
11750
+ depth: Number(row.depth),
11751
+ parentKey
11752
+ };
11753
+ return [nodeIdentityKey(node), node];
11754
+ })
11755
+ );
11756
+ }
11757
+ function reverseDirection(direction) {
11758
+ switch (direction) {
11759
+ case "out": {
11760
+ return "in";
11761
+ }
11762
+ case "in": {
11763
+ return "out";
11764
+ }
11765
+ case "both": {
11766
+ return "both";
11767
+ }
11768
+ }
11769
+ }
11770
+
11771
+ // src/store/algorithms/reachable.ts
11772
+ async function executeReachable(ctx, sourceId, options) {
11773
+ assertEdgeKinds(options.edges);
11774
+ const maxHops = resolveMaxHops(
11775
+ options.maxHops,
11776
+ DEFAULT_ALGORITHM_MAX_HOPS,
11777
+ "maxHops"
11778
+ );
11779
+ const reached = await findReachableNodes(ctx, sourceId, maxHops, options);
11780
+ return options.excludeSource === true ? reached.filter((node) => node.id !== sourceId) : reached;
11781
+ }
11782
+ async function executeCanReach(ctx, sourceId, targetId, options) {
11783
+ assertEdgeKinds(options.edges);
11784
+ const maxHops = resolveMaxHops(
11785
+ options.maxHops,
11786
+ DEFAULT_ALGORITHM_MAX_HOPS,
11787
+ "maxHops"
11788
+ );
11789
+ return await findShortestPath(ctx, sourceId, targetId, maxHops, options) !== void 0;
11790
+ }
11791
+ async function executeNeighbors(ctx, sourceId, options) {
11792
+ const depth = resolveMaxHops(options.depth, DEFAULT_NEIGHBOR_DEPTH, "depth");
11793
+ return executeReachable(ctx, sourceId, {
10624
11794
  edges: options.edges,
10625
11795
  maxHops: depth,
10626
11796
  direction: options.direction ?? "out",
10627
11797
  cyclePolicy: options.cyclePolicy ?? "prevent",
10628
- ...options.temporalMode !== void 0 && {
10629
- temporalMode: options.temporalMode
11798
+ ...pickTemporalOptions(options),
11799
+ ...options.workingMemory !== void 0 && {
11800
+ workingMemory: options.workingMemory
11801
+ },
11802
+ excludeSource: true
11803
+ });
11804
+ }
11805
+
11806
+ // src/store/algorithms/shortest-path.ts
11807
+ async function executeShortestPath(ctx, sourceId, targetId, options) {
11808
+ assertEdgeKinds(options.edges);
11809
+ const maxHops = resolveMaxHops(
11810
+ options.maxHops,
11811
+ DEFAULT_ALGORITHM_MAX_HOPS,
11812
+ "maxHops"
11813
+ );
11814
+ return findShortestPath(ctx, sourceId, targetId, maxHops, options);
11815
+ }
11816
+ var DEFAULT_WCC_MAX_ITERATIONS = 1e3;
11817
+ async function executeWeaklyConnectedComponents(ctx, options) {
11818
+ assertEdgeKinds(options.edges);
11819
+ assertWeaklyConnectedComponentsSupported(ctx);
11820
+ const maxIterations = resolveMaxIterations(
11821
+ options.maxIterations,
11822
+ DEFAULT_WCC_MAX_ITERATIONS,
11823
+ "weaklyConnectedComponents"
11824
+ );
11825
+ const nodeKinds = options.nodeKinds === void 0 ? void 0 : [...new Set(options.nodeKinds)].toSorted(
11826
+ (left, right) => compareCodePoints(left, right)
11827
+ );
11828
+ const traversalOptions = {
11829
+ edges: options.edges,
11830
+ direction: "both",
11831
+ ...pickTemporalOptions(options),
11832
+ ...options.workingMemory === void 0 ? {} : { workingMemory: options.workingMemory }
11833
+ };
11834
+ return runIterativeGraphOperation(ctx, traversalOptions, {
11835
+ algorithm: "weaklyConnectedComponents",
11836
+ maxIterations,
11837
+ createWorkingTable: createWorkingTable2,
11838
+ initialize: (context) => initializeWorkingTable(context, nodeKinds),
11839
+ runRound: runLabelPropagationRound,
11840
+ hasConverged(state) {
11841
+ return state.changedCount === 0;
11842
+ },
11843
+ extractResult: extractMemberships
11844
+ });
11845
+ }
11846
+ function assertWeaklyConnectedComponentsSupported(ctx) {
11847
+ if (ctx.backend.capabilities.graphAnalytics?.supported === true && ctx.backend.capabilities.windowFunctions) {
11848
+ return;
11849
+ }
11850
+ throw new UnsupportedBackendCapabilityError(
11851
+ "weaklyConnectedComponents",
11852
+ "graphAnalytics",
11853
+ {
11854
+ dialect: ctx.backend.dialect,
11855
+ supported: ctx.backend.capabilities.graphAnalytics?.supported === true,
11856
+ windowFunctions: ctx.backend.capabilities.windowFunctions
11857
+ },
11858
+ "Use a built-in transactional SQLite/PostgreSQL backend, or declare graphAnalytics support on a compatible custom backend."
11859
+ );
11860
+ }
11861
+ function createWorkingTable2(context) {
11862
+ return sql`
11863
+ CREATE TEMP TABLE ${context.workingTable} (
11864
+ graph_id TEXT NOT NULL,
11865
+ run_id TEXT NOT NULL,
11866
+ node_id TEXT NOT NULL,
11867
+ node_kind TEXT NOT NULL,
11868
+ label_id TEXT NOT NULL,
11869
+ label_kind TEXT NOT NULL,
11870
+ next_label_id TEXT NOT NULL,
11871
+ next_label_kind TEXT NOT NULL,
11872
+ improved_round INTEGER NOT NULL,
11873
+ PRIMARY KEY (graph_id, run_id, node_kind, node_id)
11874
+ )
11875
+ `;
11876
+ }
11877
+ async function initializeWorkingTable(context, nodeKinds) {
11878
+ const { operation, workingTable, graphId, runId } = context;
11879
+ const nodeKindFilter = nodeKinds === void 0 ? sql`TRUE` : compileKindFilter(sql.raw("n.kind"), nodeKinds);
11880
+ await context.executeTemporary(sql`
11881
+ INSERT INTO ${workingTable}
11882
+ (graph_id, run_id, node_id, node_kind, label_id, label_kind,
11883
+ next_label_id, next_label_kind, improved_round)
11884
+ SELECT
11885
+ ${graphId}, ${runId}, n.id, n.kind, n.id, n.kind, n.id, n.kind, 0
11886
+ FROM ${operation.schema.nodesTable} n
11887
+ WHERE n.graph_id = ${graphId}
11888
+ AND ${nodeKindFilter}
11889
+ AND ${operation.nodeTemporalFilter}
11890
+ `);
11891
+ await context.executeTemporary(sql`
11892
+ CREATE INDEX ${frontierIndexIdentifier(context)}
11893
+ ON ${workingTable} (graph_id, run_id, improved_round)
11894
+ `);
11895
+ const rows = await context.backend.execute(
11896
+ asCompiledRowsSql(sql`
11897
+ SELECT COUNT(*) AS count
11898
+ FROM ${workingTable}
11899
+ WHERE graph_id = ${graphId} AND run_id = ${runId}
11900
+ `)
11901
+ );
11902
+ const workingTableSize = Number(rows[0]?.count ?? 0);
11903
+ return { changedCount: workingTableSize, workingTableSize };
11904
+ }
11905
+ async function runLabelPropagationRound(context, state, iteration) {
11906
+ for (const edgeKinds of context.operation.edgeKindChunks) {
11907
+ await propagateChunkLabels(context, edgeKinds, iteration);
11908
+ }
11909
+ const changedRows = await applyNextLabels(context, iteration);
11910
+ return {
11911
+ changedCount: changedRows.length,
11912
+ workingTableSize: state.workingTableSize
11913
+ };
11914
+ }
11915
+ async function propagateChunkLabels(context, edgeKinds, iteration) {
11916
+ const { operation, workingTable, graphId, runId } = context;
11917
+ const expansion = compileWorkingTableEdgeExpansion(
11918
+ operation,
11919
+ workingTable,
11920
+ sql`
11921
+ w.graph_id = ${graphId}
11922
+ AND w.run_id = ${runId}
11923
+ AND w.improved_round = ${iteration - 1}
11924
+ `,
11925
+ "both",
11926
+ edgeKinds,
11927
+ sql`w.label_id AS source_label_id, w.label_kind AS source_label_kind`
11928
+ );
11929
+ const candidateId = operation.ctx.dialect.binaryText(sql`ranked.label_id`);
11930
+ const candidateKind = operation.ctx.dialect.binaryText(
11931
+ sql`ranked.label_kind`
11932
+ );
11933
+ const currentId = operation.ctx.dialect.binaryText(sql`target.next_label_id`);
11934
+ const currentKind = operation.ctx.dialect.binaryText(
11935
+ sql`target.next_label_kind`
11936
+ );
11937
+ const sourceId = operation.ctx.dialect.binaryText(sql`source.label_id`);
11938
+ const sourceKind = operation.ctx.dialect.binaryText(sql`source.label_kind`);
11939
+ const targetId = operation.ctx.dialect.binaryText(sql`source.target_id`);
11940
+ const targetKind = operation.ctx.dialect.binaryText(sql`source.target_kind`);
11941
+ await context.executeTemporary(sql`
11942
+ WITH candidates AS (
11943
+ SELECT
11944
+ expanded.target_id,
11945
+ expanded.target_kind,
11946
+ expanded.source_label_id AS label_id,
11947
+ expanded.source_label_kind AS label_kind
11948
+ FROM (${expansion}) expanded
11949
+ JOIN ${workingTable} scoped_target
11950
+ ON scoped_target.graph_id = ${graphId}
11951
+ AND scoped_target.run_id = ${runId}
11952
+ AND scoped_target.node_id = expanded.target_id
11953
+ AND scoped_target.node_kind = expanded.target_kind
11954
+ ), ranked AS (
11955
+ SELECT
11956
+ target_id,
11957
+ target_kind,
11958
+ label_id,
11959
+ label_kind,
11960
+ ROW_NUMBER() OVER (
11961
+ PARTITION BY ${targetKind}, ${targetId}
11962
+ ORDER BY ${sourceId}, ${sourceKind}
11963
+ ) AS candidate_rank
11964
+ FROM candidates source
11965
+ )
11966
+ UPDATE ${workingTable} AS target
11967
+ SET next_label_id = ranked.label_id,
11968
+ next_label_kind = ranked.label_kind
11969
+ FROM ranked
11970
+ WHERE target.graph_id = ${graphId}
11971
+ AND target.run_id = ${runId}
11972
+ AND target.node_id = ranked.target_id
11973
+ AND target.node_kind = ranked.target_kind
11974
+ AND ranked.candidate_rank = 1
11975
+ AND (
11976
+ ${candidateId} < ${currentId}
11977
+ OR (
11978
+ ${candidateId} = ${currentId}
11979
+ AND ${candidateKind} < ${currentKind}
11980
+ )
11981
+ )
11982
+ `);
11983
+ }
11984
+ function applyNextLabels(context, iteration) {
11985
+ return context.backend.execute(
11986
+ asCompiledRowsSql(sql`
11987
+ UPDATE ${context.workingTable}
11988
+ SET label_id = next_label_id,
11989
+ label_kind = next_label_kind,
11990
+ improved_round = ${iteration}
11991
+ WHERE graph_id = ${context.graphId}
11992
+ AND run_id = ${context.runId}
11993
+ AND (
11994
+ label_id <> next_label_id OR label_kind <> next_label_kind
11995
+ )
11996
+ RETURNING node_id
11997
+ `)
11998
+ );
11999
+ }
12000
+ async function extractMemberships(context) {
12001
+ const { operation } = context;
12002
+ const componentId = operation.ctx.dialect.binaryText(sql`label_id`);
12003
+ const componentKind = operation.ctx.dialect.binaryText(sql`label_kind`);
12004
+ const nodeId = operation.ctx.dialect.binaryText(sql`node_id`);
12005
+ const nodeKind = operation.ctx.dialect.binaryText(sql`node_kind`);
12006
+ const rows = await context.backend.execute(
12007
+ asCompiledRowsSql(sql`
12008
+ SELECT
12009
+ node_id,
12010
+ node_kind,
12011
+ label_id AS component_id,
12012
+ label_kind AS component_kind,
12013
+ COUNT(*) OVER (
12014
+ PARTITION BY ${componentKind}, ${componentId}
12015
+ ) AS component_size
12016
+ FROM ${context.workingTable}
12017
+ WHERE graph_id = ${context.graphId} AND run_id = ${context.runId}
12018
+ ORDER BY ${componentId}, ${componentKind}, ${nodeId}, ${nodeKind}
12019
+ `)
12020
+ );
12021
+ return rows.map((row) => ({
12022
+ id: row.node_id,
12023
+ kind: row.node_kind,
12024
+ componentId: row.component_id,
12025
+ componentKind: row.component_kind,
12026
+ size: Number(row.component_size)
12027
+ }));
12028
+ }
12029
+ var DEFAULT_WEIGHTED_SHORTEST_PATH_MAX_ITERATIONS = 1e3;
12030
+ var WEIGHTED_ALGORITHM_NAME = "weightedShortestPath";
12031
+ var MAX_EDGE_WEIGHT = Number.MAX_VALUE / 2 ** 64;
12032
+ var MIN_EDGE_WEIGHT = Number.MIN_VALUE;
12033
+ async function executeWeightedShortestPath(ctx, sourceId, targetId, options) {
12034
+ assertEdgeKinds(options.edges);
12035
+ assertWeightOptions(options.weightProperty, options.defaultWeight);
12036
+ const maxIterations = resolveMaxIterations(
12037
+ options.maxIterations,
12038
+ DEFAULT_WEIGHTED_SHORTEST_PATH_MAX_ITERATIONS,
12039
+ WEIGHTED_ALGORITHM_NAME
12040
+ );
12041
+ const traversalOptions = options;
12042
+ if (sourceId === targetId) {
12043
+ return findWeightedSelfPath(ctx, sourceId, traversalOptions);
12044
+ }
12045
+ if (supportsTemporaryIteration(ctx)) {
12046
+ return findWeightedShortestPathInWorkingTable(
12047
+ ctx,
12048
+ sourceId,
12049
+ targetId,
12050
+ maxIterations,
12051
+ traversalOptions
12052
+ );
12053
+ }
12054
+ return findWeightedShortestPathInline(
12055
+ ctx,
12056
+ sourceId,
12057
+ targetId,
12058
+ maxIterations,
12059
+ traversalOptions
12060
+ );
12061
+ }
12062
+ function assertWeightOptions(weightProperty, defaultWeight) {
12063
+ if (typeof weightProperty !== "string" || weightProperty.length === 0) {
12064
+ throw new ConfigurationError(
12065
+ `${WEIGHTED_ALGORITHM_NAME} weightProperty must be a non-empty string.`,
12066
+ { weightProperty }
12067
+ );
12068
+ }
12069
+ if (defaultWeight !== void 0 && (!Number.isFinite(defaultWeight) || defaultWeight < 0 || defaultWeight > MAX_EDGE_WEIGHT)) {
12070
+ throw new ConfigurationError(
12071
+ `${WEIGHTED_ALGORITHM_NAME} defaultWeight must be a non-negative number no greater than ${MAX_EDGE_WEIGHT}, got ${String(defaultWeight)}.`,
12072
+ { defaultWeight, maximum: MAX_EDGE_WEIGHT }
12073
+ );
12074
+ }
12075
+ }
12076
+ async function assertValidEdgeWeights(operation, weightProperty, defaultWeight) {
12077
+ const { dialect } = operation.ctx;
12078
+ const pointer = jsonPointer([weightProperty]);
12079
+ const propsColumn = sql.raw("e.props");
12080
+ const isNumber = dialect.jsonPathIsNumber(propsColumn, pointer);
12081
+ const isMissing = dialect.jsonPathIsNull(propsColumn, pointer);
12082
+ const weightText = dialect.jsonExtractText(propsColumn, pointer);
12083
+ const weightNumber = sql`CASE WHEN ${isNumber} THEN ${dialect.jsonExtractNumber(propsColumn, pointer)} END`;
12084
+ const missingViolation = defaultWeight === void 0 ? sql`audited.is_missing = 1` : dialect.booleanLiteral(false);
12085
+ const negativeViolation = sql`audited.is_number = 1 AND audited.weight_number < 0`;
12086
+ 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)))`;
12087
+ for (const edgeKinds of operation.edgeKindChunks) {
12088
+ const edgeKindFilter = compileKindFilter(sql.raw("e.kind"), edgeKinds);
12089
+ const rows = await operation.backend.execute(
12090
+ asCompiledRowsSql(sql`
12091
+ SELECT audited.edge_id, audited.edge_kind, audited.weight_text,
12092
+ audited.is_number, audited.is_missing
12093
+ FROM (
12094
+ SELECT e.id AS edge_id, e.kind AS edge_kind,
12095
+ ${weightText} AS weight_text,
12096
+ ${weightNumber} AS weight_number,
12097
+ CASE WHEN ${isNumber} THEN 1 ELSE 0 END AS is_number,
12098
+ CASE WHEN ${isMissing} THEN 1 ELSE 0 END AS is_missing
12099
+ FROM ${operation.schema.edgesTable} e
12100
+ WHERE e.graph_id = ${operation.ctx.graphId}
12101
+ AND ${edgeKindFilter}
12102
+ AND ${operation.edgeTemporalFilter}
12103
+ ) audited
12104
+ WHERE (${missingViolation})
12105
+ OR (audited.is_missing = 0 AND audited.is_number = 0)
12106
+ OR (${negativeViolation})
12107
+ OR (${outOfRangeViolation})
12108
+ ORDER BY ${dialect.binaryText(sql`audited.edge_id`)}
12109
+ LIMIT 1
12110
+ `)
12111
+ );
12112
+ const row = rows[0];
12113
+ if (row !== void 0) {
12114
+ throw createInvalidEdgeWeightError(row, weightProperty);
12115
+ }
12116
+ }
12117
+ }
12118
+ function createInvalidEdgeWeightError(row, weightProperty) {
12119
+ const reason = resolveInvalidWeightReason(row);
12120
+ const value = reason === "missing" || row.weight_text === null ? void 0 : formatWeightValue(row.weight_text);
12121
+ return new InvalidEdgeWeightError({
12122
+ edgeId: row.edge_id,
12123
+ edgeKind: row.edge_kind,
12124
+ property: weightProperty,
12125
+ reason,
12126
+ ...value === void 0 ? {} : { value }
12127
+ });
12128
+ }
12129
+ function resolveInvalidWeightReason(row) {
12130
+ if (flagIsSet(row.is_missing)) return "missing";
12131
+ if (!flagIsSet(row.is_number)) return "non_numeric";
12132
+ return Number(row.weight_text) < 0 ? "negative" : "out_of_range";
12133
+ }
12134
+ function flagIsSet(value) {
12135
+ return Number(value) !== 0;
12136
+ }
12137
+ function formatWeightValue(value) {
12138
+ if (typeof value === "string") return value;
12139
+ if (typeof value === "number" || typeof value === "bigint" || typeof value === "boolean") {
12140
+ return String(value);
12141
+ }
12142
+ return JSON.stringify(value);
12143
+ }
12144
+ async function findWeightedShortestPathInWorkingTable(ctx, sourceId, targetId, maxIterations, options) {
12145
+ return runIterativeGraphOperation(ctx, options, {
12146
+ algorithm: WEIGHTED_ALGORITHM_NAME,
12147
+ maxIterations,
12148
+ createWorkingTable: createWorkingTable3,
12149
+ async initialize(context) {
12150
+ await assertValidEdgeWeights(
12151
+ context.operation,
12152
+ options.weightProperty,
12153
+ options.defaultWeight
12154
+ );
12155
+ if (!await hasVisibleNode(context.operation, targetId)) {
12156
+ return {
12157
+ frontierCount: 0,
12158
+ workingTableSize: 0,
12159
+ bestTargetDistance: void 0
12160
+ };
12161
+ }
12162
+ await context.executeTemporary(sql`
12163
+ CREATE INDEX ${frontierIndexIdentifier(context)}
12164
+ ON ${context.workingTable} (graph_id, run_id, improved_round)
12165
+ `);
12166
+ const seeded = await seedDistances(context, sourceId);
12167
+ return {
12168
+ frontierCount: seeded.length,
12169
+ workingTableSize: seeded.length,
12170
+ // Seeds carry the source id, never the target's — the self-path
12171
+ // case is short-circuited before this plan runs.
12172
+ bestTargetDistance: void 0
12173
+ };
12174
+ },
12175
+ async runRound(context, state, iteration) {
12176
+ const round = await relaxFrontierRound(
12177
+ context,
12178
+ targetId,
12179
+ iteration,
12180
+ state.bestTargetDistance
12181
+ );
12182
+ return {
12183
+ frontierCount: round.frontierCount,
12184
+ // Overcounts by re-improved existing rows (RETURNING includes
12185
+ // updates); acceptable because the size only paces the substrate's
12186
+ // ANALYZE refresh, where too-early merely refreshes stats sooner.
12187
+ workingTableSize: state.workingTableSize + round.frontierCount,
12188
+ bestTargetDistance: round.bestTargetDistance
12189
+ };
10630
12190
  },
10631
- ...options.asOf !== void 0 && { asOf: options.asOf },
10632
- ...options.recordedAsOf !== void 0 && {
10633
- recordedAsOf: options.recordedAsOf
12191
+ hasConverged(state) {
12192
+ return state.frontierCount === 0;
10634
12193
  },
10635
- excludeSource: true
12194
+ async extractResult(context) {
12195
+ return extractPathFromWorkingTable(context, targetId, maxIterations);
12196
+ }
10636
12197
  });
10637
12198
  }
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"
12199
+ async function hasVisibleNode(operation, nodeId) {
12200
+ const rows = await operation.backend.execute(
12201
+ asCompiledRowsSql(sql`
12202
+ SELECT n.id FROM ${operation.schema.nodesTable} n
12203
+ WHERE n.graph_id = ${operation.ctx.graphId}
12204
+ AND n.id = ${nodeId}
12205
+ AND ${operation.nodeTemporalFilter}
12206
+ LIMIT 1
12207
+ `)
10644
12208
  );
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 };
12209
+ return rows.length > 0;
12210
+ }
12211
+ function createWorkingTable3(context) {
12212
+ return sql`
12213
+ CREATE TEMP TABLE ${context.workingTable} (
12214
+ graph_id TEXT NOT NULL,
12215
+ run_id TEXT NOT NULL,
12216
+ node_id TEXT NOT NULL,
12217
+ node_kind TEXT NOT NULL,
12218
+ distance DOUBLE PRECISION NOT NULL,
12219
+ hops INTEGER NOT NULL,
12220
+ predecessor_id TEXT,
12221
+ predecessor_kind TEXT,
12222
+ improved_round INTEGER NOT NULL,
12223
+ PRIMARY KEY (graph_id, run_id, node_kind, node_id)
12224
+ )
12225
+ `;
12226
+ }
12227
+ async function seedDistances(context, sourceId) {
12228
+ const { operation, workingTable, graphId, runId } = context;
12229
+ return context.backend.execute(
12230
+ asCompiledRowsSql(sql`
12231
+ INSERT INTO ${workingTable}
12232
+ (graph_id, run_id, node_id, node_kind, distance, hops,
12233
+ predecessor_id, predecessor_kind, improved_round)
12234
+ SELECT ${graphId}, ${runId}, n.id, n.kind, 0, 0, NULL, NULL, 0
12235
+ FROM ${operation.schema.nodesTable} n
12236
+ WHERE n.graph_id = ${graphId}
12237
+ AND n.id = ${sourceId}
12238
+ AND ${operation.nodeTemporalFilter}
12239
+ ON CONFLICT (graph_id, run_id, node_kind, node_id) DO NOTHING
12240
+ RETURNING node_id
12241
+ `)
12242
+ );
12243
+ }
12244
+ async function relaxFrontierRound(context, targetId, iteration, roundBound) {
12245
+ const { operation, workingTable, graphId, runId } = context;
12246
+ const { dialect } = operation.ctx;
12247
+ let frontierCount = 0;
12248
+ let bestTargetDistance = roundBound;
12249
+ for (const edgeKinds of operation.edgeKindChunks) {
12250
+ const sourceFilter = sql`
12251
+ w.graph_id = ${graphId}
12252
+ AND w.run_id = ${runId}
12253
+ AND w.improved_round = ${iteration - 1}
12254
+ `;
12255
+ const expansion = compileWorkingTableExpansion(
12256
+ operation,
12257
+ workingTable,
12258
+ sourceFilter,
12259
+ operation.direction,
12260
+ edgeKinds
12261
+ );
12262
+ const rows = await context.backend.execute(
12263
+ asCompiledRowsSql(sql`
12264
+ INSERT INTO ${workingTable}
12265
+ (graph_id, run_id, node_id, node_kind, distance, hops,
12266
+ predecessor_id, predecessor_kind, improved_round)
12267
+ SELECT
12268
+ ${graphId}, ${runId}, ranked.target_id, ranked.target_kind,
12269
+ ranked.candidate_distance, ranked.candidate_hops,
12270
+ ranked.source_id, ranked.source_kind, ${iteration}
12271
+ FROM (
12272
+ SELECT candidates.*,
12273
+ ROW_NUMBER() OVER (
12274
+ PARTITION BY ${dialect.binaryText(sql`candidates.target_kind`)},
12275
+ ${dialect.binaryText(sql`candidates.target_id`)}
12276
+ ORDER BY candidates.candidate_distance,
12277
+ ${dialect.binaryText(sql`candidates.source_id`)},
12278
+ ${dialect.binaryText(sql`candidates.source_kind`)}
12279
+ ) AS candidate_rank
12280
+ FROM (
12281
+ SELECT expanded.source_id, expanded.source_kind,
12282
+ expanded.target_id, expanded.target_kind,
12283
+ frontier.distance + expanded.weight AS candidate_distance,
12284
+ frontier.hops + 1 AS candidate_hops
12285
+ FROM (${expansion}) expanded
12286
+ JOIN ${workingTable} frontier
12287
+ ON frontier.graph_id = ${graphId}
12288
+ AND frontier.run_id = ${runId}
12289
+ AND frontier.node_id = expanded.source_id
12290
+ AND frontier.node_kind = expanded.source_kind
12291
+ ) candidates
12292
+ WHERE ${roundBound === void 0 ? sql`TRUE` : sql`candidates.candidate_distance <= ${roundBound}`}
12293
+ ) ranked
12294
+ WHERE ranked.candidate_rank = 1
12295
+ ON CONFLICT (graph_id, run_id, node_kind, node_id) DO UPDATE SET
12296
+ distance = excluded.distance,
12297
+ hops = excluded.hops,
12298
+ predecessor_id = excluded.predecessor_id,
12299
+ predecessor_kind = excluded.predecessor_kind,
12300
+ improved_round = excluded.improved_round
12301
+ WHERE excluded.distance < ${workingTable}.distance
12302
+ RETURNING node_id, distance
12303
+ `)
12304
+ );
12305
+ frontierCount += rows.length;
12306
+ for (const row of rows) {
12307
+ if (row.node_id !== targetId) continue;
12308
+ const distance = Number(row.distance);
12309
+ if (bestTargetDistance === void 0 || distance < bestTargetDistance) {
12310
+ bestTargetDistance = distance;
12311
+ }
12312
+ }
10649
12313
  }
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)
12314
+ return { frontierCount, bestTargetDistance };
12315
+ }
12316
+ async function extractPathFromWorkingTable(context, targetId, maxIterations) {
12317
+ const { operation, workingTable, graphId, runId } = context;
12318
+ const { dialect } = operation.ctx;
12319
+ const rows = await context.backend.execute(
12320
+ asCompiledRowsSql(sql`
12321
+ WITH RECURSIVE chain AS (
12322
+ SELECT best.node_id, best.node_kind, best.distance, best.hops,
12323
+ best.predecessor_id, best.predecessor_kind, 0 AS position
12324
+ FROM (
12325
+ SELECT node_id, node_kind, distance, hops,
12326
+ predecessor_id, predecessor_kind
12327
+ FROM ${workingTable}
12328
+ WHERE graph_id = ${graphId}
12329
+ AND run_id = ${runId}
12330
+ AND node_id = ${targetId}
12331
+ ORDER BY distance ASC, ${dialect.binaryText(sql`node_kind`)}
12332
+ LIMIT 1
12333
+ ) best
12334
+ UNION ALL
12335
+ SELECT w.node_id, w.node_kind, w.distance, w.hops,
12336
+ w.predecessor_id, w.predecessor_kind, c.position + 1
12337
+ FROM chain c
12338
+ JOIN ${workingTable} w
12339
+ ON w.graph_id = ${graphId}
12340
+ AND w.run_id = ${runId}
12341
+ AND w.node_id = c.predecessor_id
12342
+ AND w.node_kind = c.predecessor_kind
12343
+ WHERE c.position <= ${maxIterations}
12344
+ )
12345
+ SELECT node_id, node_kind, distance, hops
12346
+ FROM chain
12347
+ ORDER BY position DESC
12348
+ `)
10667
12349
  );
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) };
12350
+ const target = rows.at(-1);
12351
+ if (target === void 0) return void 0;
12352
+ return {
12353
+ nodes: rows.map((row) => ({ id: row.node_id, kind: row.node_kind })),
12354
+ depth: Number(target.hops),
12355
+ totalWeight: Number(target.distance)
12356
+ };
10677
12357
  }
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);
12358
+ async function findWeightedSelfPath(ctx, nodeId, options) {
12359
+ return withInlineIterativeGraphOperation(ctx, options, async (operation) => {
12360
+ await assertValidEdgeWeights(
12361
+ operation,
12362
+ options.weightProperty,
12363
+ options.defaultWeight
12364
+ );
12365
+ const nodes = await fetchVisibleWorkingNodes(operation, [nodeId]);
12366
+ const node = nodes.toSorted(
12367
+ (left, right) => compareNodeIdentity(left, right)
12368
+ )[0];
12369
+ if (node === void 0) return;
12370
+ return { nodes: [node], depth: 0, totalWeight: 0 };
12371
+ });
12372
+ }
12373
+ async function findWeightedShortestPathInline(ctx, sourceId, targetId, maxIterations, options) {
12374
+ return withInlineIterativeGraphOperation(ctx, options, async (operation) => {
12375
+ await assertValidEdgeWeights(
12376
+ operation,
12377
+ options.weightProperty,
12378
+ options.defaultWeight
12379
+ );
12380
+ const endpoints = await fetchVisibleWorkingNodes(operation, [
12381
+ sourceId,
12382
+ targetId
12383
+ ]);
12384
+ const sources = endpoints.filter((node) => node.id === sourceId);
12385
+ if (sources.length === 0 || !endpoints.some((node) => node.id === targetId)) {
12386
+ return;
10683
12387
  }
12388
+ const settled = new Map(
12389
+ sources.map((source) => [
12390
+ nodeIdentityKey(source),
12391
+ {
12392
+ id: source.id,
12393
+ kind: source.kind,
12394
+ distance: 0,
12395
+ hops: 0,
12396
+ parentKey: void 0
12397
+ }
12398
+ ])
12399
+ );
12400
+ let bestTargetDistance;
12401
+ let frontier = sources;
12402
+ let rounds = 0;
12403
+ while (frontier.length > 0) {
12404
+ if (rounds >= maxIterations) {
12405
+ throw new GraphAlgorithmConvergenceError(
12406
+ WEIGHTED_ALGORITHM_NAME,
12407
+ maxIterations
12408
+ );
12409
+ }
12410
+ rounds++;
12411
+ const candidates = await reduceExpandedWorkingSet(
12412
+ operation,
12413
+ frontier,
12414
+ operation.direction,
12415
+ (existing, expansion) => reduceWeightedCandidate(existing, expansion, settled)
12416
+ );
12417
+ const roundBound = bestTargetDistance;
12418
+ const improved = [];
12419
+ for (const [candidateKey, candidate] of candidates) {
12420
+ const existing = settled.get(candidateKey);
12421
+ if (existing !== void 0 && existing.distance <= candidate.distance) {
12422
+ continue;
12423
+ }
12424
+ if (roundBound !== void 0 && candidate.distance > roundBound) {
12425
+ continue;
12426
+ }
12427
+ settled.set(candidateKey, {
12428
+ id: candidate.id,
12429
+ kind: candidate.kind,
12430
+ distance: candidate.distance,
12431
+ hops: candidate.hops,
12432
+ parentKey: nodeIdentityKey({
12433
+ id: candidate.parentId,
12434
+ kind: candidate.parentKind
12435
+ })
12436
+ });
12437
+ improved.push({ id: candidate.id, kind: candidate.kind });
12438
+ if (candidate.id === targetId && (bestTargetDistance === void 0 || candidate.distance < bestTargetDistance)) {
12439
+ bestTargetDistance = candidate.distance;
12440
+ }
12441
+ }
12442
+ frontier = improved.toSorted(
12443
+ (left, right) => compareNodeIdentity(left, right)
12444
+ );
12445
+ }
12446
+ return buildWeightedPathResult(settled, targetId);
12447
+ });
12448
+ }
12449
+ function reduceWeightedCandidate(existing, expansion, settled) {
12450
+ const weight = expansion.weight;
12451
+ if (weight === void 0) return existing;
12452
+ const sourceEntry = settled.get(nodeIdentityKey(expansion.source));
12453
+ if (sourceEntry === void 0) return existing;
12454
+ const candidateDistance = sourceEntry.distance + weight;
12455
+ if (existing !== void 0 && (existing.distance < candidateDistance || existing.distance === candidateDistance && compareNodeIdentity(
12456
+ { id: existing.parentId, kind: existing.parentKind },
12457
+ expansion.source
12458
+ ) <= 0)) {
12459
+ return existing;
10684
12460
  }
10685
- return map;
12461
+ return {
12462
+ id: expansion.target.id,
12463
+ kind: expansion.target.kind,
12464
+ distance: candidateDistance,
12465
+ hops: sourceEntry.hops + 1,
12466
+ parentId: expansion.source.id,
12467
+ parentKind: expansion.source.kind
12468
+ };
10686
12469
  }
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 };
12470
+ function buildWeightedPathResult(visited, targetId) {
12471
+ let target;
12472
+ for (const node of visited.values()) {
12473
+ if (node.id !== targetId) continue;
12474
+ if (target === void 0 || node.distance < target.distance || node.distance === target.distance && compareNodeIdentity(node, target) < 0) {
12475
+ target = node;
12476
+ }
12477
+ }
12478
+ if (target === void 0) return void 0;
12479
+ const reversedNodes = [];
12480
+ let cursor = target;
12481
+ while (cursor !== void 0) {
12482
+ reversedNodes.push({ id: cursor.id, kind: cursor.kind });
12483
+ cursor = cursor.parentKey === void 0 ? void 0 : visited.get(cursor.parentKey);
12484
+ }
12485
+ return {
12486
+ nodes: reversedNodes.toReversed(),
12487
+ depth: target.hops,
12488
+ totalWeight: target.distance
12489
+ };
10695
12490
  }
10696
12491
 
10697
12492
  // src/store/algorithms/index.ts
@@ -10704,7 +12499,7 @@ function assertRecordedAsOfInternalOnly(options, method, allowRecordedAsOf) {
10704
12499
  code: "ALGORITHM_RECORDED_ASOF_INTERNAL_ONLY",
10705
12500
  message: `recordedAsOf is only available through store.asOfRecorded(...).${method}(...).`,
10706
12501
  context: { method },
10707
- suggestion: "Use store.asOfRecorded(recordedAt).reachable/shortestPath/canReach/neighbors/degree(...) instead of passing recordedAsOf directly."
12502
+ suggestion: "Use store.asOfRecorded(recordedAt) or a recorded StoreView instead of passing recordedAsOf directly."
10708
12503
  });
10709
12504
  }
10710
12505
  function createGraphAlgorithms(params) {
@@ -10733,6 +12528,19 @@ function createGraphAlgorithms(params) {
10733
12528
  options
10734
12529
  );
10735
12530
  },
12531
+ weightedShortestPath(from, to, options) {
12532
+ assertRecordedAsOfInternalOnly(
12533
+ options,
12534
+ "weightedShortestPath",
12535
+ allowRecordedAsOf
12536
+ );
12537
+ return executeWeightedShortestPath(
12538
+ ctx,
12539
+ resolveNodeId(from),
12540
+ resolveNodeId(to),
12541
+ options
12542
+ );
12543
+ },
10736
12544
  reachable(from, options) {
10737
12545
  assertRecordedAsOfInternalOnly(options, "reachable", allowRecordedAsOf);
10738
12546
  return executeReachable(ctx, resolveNodeId(from), options);
@@ -10753,6 +12561,14 @@ function createGraphAlgorithms(params) {
10753
12561
  degree(node, options) {
10754
12562
  assertRecordedAsOfInternalOnly(options, "degree", allowRecordedAsOf);
10755
12563
  return executeDegree(ctx, resolveNodeId(node), options ?? {});
12564
+ },
12565
+ weaklyConnectedComponents(options) {
12566
+ assertRecordedAsOfInternalOnly(
12567
+ options,
12568
+ "weaklyConnectedComponents",
12569
+ allowRecordedAsOf
12570
+ );
12571
+ return executeWeaklyConnectedComponents(ctx, options);
10756
12572
  }
10757
12573
  };
10758
12574
  }
@@ -11614,6 +13430,33 @@ async function ensureFocusedStatusTable(backend, ensureFocused) {
11614
13430
  }
11615
13431
  await backend.bootstrapTables?.();
11616
13432
  }
13433
+ async function runBucketedMaterialization(items, options, bucketKey, runOne) {
13434
+ if (options.stopOnError === true) {
13435
+ const results2 = [];
13436
+ for (const item of items) {
13437
+ const entry2 = await runOne(item);
13438
+ results2.push(entry2);
13439
+ if (entry2.status === "failed") break;
13440
+ }
13441
+ return results2;
13442
+ }
13443
+ const buckets = /* @__PURE__ */ new Map();
13444
+ for (const [index, item] of items.entries()) {
13445
+ const key = bucketKey(item);
13446
+ const bucket = buckets.get(key);
13447
+ if (bucket === void 0) buckets.set(key, [[index, item]]);
13448
+ else bucket.push([index, item]);
13449
+ }
13450
+ const results = Array.from({ length: items.length });
13451
+ await Promise.all(
13452
+ [...buckets.values()].map(async (group) => {
13453
+ for (const [index, item] of group) {
13454
+ results[index] = await runOne(item);
13455
+ }
13456
+ })
13457
+ );
13458
+ return results;
13459
+ }
11617
13460
  async function runMaterialization(items, options, runOne) {
11618
13461
  if (options.stopOnError === true) {
11619
13462
  const results = [];
@@ -11641,18 +13484,10 @@ function delay(ms) {
11641
13484
  }
11642
13485
  async function materializeIndexes(context, options = {}) {
11643
13486
  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
- }
13487
+ assertBackendSupportsIndexMaterialization(
13488
+ backend,
13489
+ "store.materializeIndexes()"
13490
+ );
11656
13491
  const declared = graph.indexes ?? [];
11657
13492
  const kindFilter = options.kinds === void 0 ? void 0 : new Set(options.kinds);
11658
13493
  if (kindFilter !== void 0) {
@@ -11699,65 +13534,42 @@ async function materializeIndexes(context, options = {}) {
11699
13534
  backend,
11700
13535
  relationalPhysicalNames
11701
13536
  );
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;
13537
+ const reservedSystemNames = resolveSystemIndexNames(backend.tableNames);
13538
+ const materializeEntry = (declaration) => {
13539
+ if (declaration.entity !== "vector" && reservedSystemNames.has(declaration.name)) {
13540
+ return Promise.resolve(
13541
+ entry(
13542
+ declaration,
13543
+ "failed",
13544
+ new Error(
13545
+ `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.`
13546
+ )
13547
+ )
13548
+ );
11725
13549
  }
11726
- await refreshStatisticsAfterCreation(
13550
+ return declaration.entity === "vector" ? materializeVectorIndex(
13551
+ declaration,
11727
13552
  backend,
11728
- results2,
11729
- options,
11730
- ddlOptions.concurrent
13553
+ graphId,
13554
+ schemaVersion,
13555
+ existingByStatusKey,
13556
+ invalidLeftovers
13557
+ ) : materializeRelationalIndex(
13558
+ declaration,
13559
+ backend,
13560
+ dialect,
13561
+ ddlOptions,
13562
+ graphId,
13563
+ schemaVersion,
13564
+ existingByStatusKey,
13565
+ invalidLeftovers
11731
13566
  );
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)
13567
+ };
13568
+ const results = await runBucketedMaterialization(
13569
+ candidates,
13570
+ options,
13571
+ (declaration) => parallelBucketKey(declaration),
13572
+ (declaration) => materializeEntry(declaration)
11761
13573
  );
11762
13574
  await refreshStatisticsAfterCreation(
11763
13575
  backend,
@@ -11814,7 +13626,7 @@ async function materializeRelationalIndex(declaration, backend, dialect, ddlOpti
11814
13626
  await backend.executeDdl(ddl);
11815
13627
  },
11816
13628
  existingByStatusKey,
11817
- invalidLeftovers
13629
+ physicalRebuildPreload: invalidLeftovers
11818
13630
  });
11819
13631
  }
11820
13632
  async function materializeVectorIndex(declaration, backend, graphId, schemaVersion, existingByStatusKey, invalidLeftovers) {
@@ -11872,7 +13684,7 @@ async function materializeVectorIndex(declaration, backend, graphId, schemaVersi
11872
13684
  driftLabel: "Vector index",
11873
13685
  run: () => backend.createVectorIndex(params),
11874
13686
  existingByStatusKey,
11875
- invalidLeftovers
13687
+ physicalRebuildPreload: invalidLeftovers
11876
13688
  });
11877
13689
  }
11878
13690
  async function materializeOne(declaration, backend, graphId, schemaVersion, action) {
@@ -11889,7 +13701,8 @@ async function materializeOne(declaration, backend, graphId, schemaVersion, acti
11889
13701
  statusKey,
11890
13702
  signature,
11891
13703
  driftLabel,
11892
- isInvalidLeftover: (physicalIndexName) => action.invalidLeftovers.has(physicalIndexName)
13704
+ needsPhysicalRebuild: (physicalIndexName) => action.physicalRebuildPreload.has(physicalIndexName),
13705
+ ...action.rebuildWithoutClaim === void 0 ? {} : { rebuildWithoutClaim: action.rebuildWithoutClaim }
11893
13706
  }
11894
13707
  );
11895
13708
  if (settled !== void 0) return settled;
@@ -11898,7 +13711,9 @@ async function materializeOne(declaration, backend, graphId, schemaVersion, acti
11898
13711
  statusKey,
11899
13712
  signature,
11900
13713
  driftLabel,
11901
- run
13714
+ run,
13715
+ ...action.freshNeedsPhysicalRebuild === void 0 ? {} : { freshNeedsPhysicalRebuild: action.freshNeedsPhysicalRebuild },
13716
+ ...action.rebuildWithoutClaim === void 0 ? {} : { rebuildWithoutClaim: action.rebuildWithoutClaim }
11902
13717
  });
11903
13718
  }
11904
13719
  try {
@@ -11935,9 +13750,9 @@ async function materializeOne(declaration, backend, graphId, schemaVersion, acti
11935
13750
  }
11936
13751
  async function settleAgainstExisting(existing, declaration, backend, graphId, schemaVersion, action) {
11937
13752
  if (existing?.materializedAt === void 0) return void 0;
11938
- const { statusKey, signature, driftLabel, isInvalidLeftover } = action;
13753
+ const { statusKey, signature, driftLabel, needsPhysicalRebuild } = action;
11939
13754
  if (existing.signature === signature) {
11940
- if (declaration.entity !== "vector" && hasIndexBuildClaimProtocol(backend) && await isInvalidLeftover(declaration.name)) {
13755
+ if (declaration.entity !== "vector" && (hasIndexBuildClaimProtocol(backend) || action.rebuildWithoutClaim === true) && await needsPhysicalRebuild(declaration.name)) {
11941
13756
  return void 0;
11942
13757
  }
11943
13758
  return entry(declaration, "alreadyMaterialized");
@@ -12002,7 +13817,8 @@ async function materializeWithClaim(declaration, backend, graphId, schemaVersion
12002
13817
  driftLabel,
12003
13818
  // Post-claim the bulk preload is stale (we may have waited on
12004
13819
  // another builder's claim), so re-check this index fresh.
12005
- isInvalidLeftover: (physicalIndexName) => hasInvalidIndexLeftover(backend, physicalIndexName)
13820
+ needsPhysicalRebuild: action.freshNeedsPhysicalRebuild ?? ((physicalIndexName) => hasInvalidIndexLeftover(backend, physicalIndexName)),
13821
+ ...action.rebuildWithoutClaim === void 0 ? {} : { rebuildWithoutClaim: action.rebuildWithoutClaim }
12006
13822
  }
12007
13823
  );
12008
13824
  if (settled !== void 0) return settled;
@@ -12063,6 +13879,7 @@ async function hasInvalidIndexLeftover(backend, physicalIndexName) {
12063
13879
  FROM pg_class c
12064
13880
  JOIN pg_index i ON i.indexrelid = c.oid
12065
13881
  WHERE c.relname = ${physicalIndexName}
13882
+ AND pg_catalog.pg_table_is_visible(c.oid)
12066
13883
  `)
12067
13884
  );
12068
13885
  return rows[0]?.invalid === true;
@@ -12071,17 +13888,64 @@ async function preloadInvalidIndexLeftovers(backend, physicalIndexNames) {
12071
13888
  if (backend.dialect !== "postgres" || physicalIndexNames.length === 0) {
12072
13889
  return /* @__PURE__ */ new Set();
12073
13890
  }
13891
+ const { invalid } = await preloadPhysicalIndexStates(
13892
+ backend,
13893
+ physicalIndexNames
13894
+ );
13895
+ return invalid;
13896
+ }
13897
+ async function preloadExistingTables(backend, tableNames) {
13898
+ if (tableNames.length === 0) return /* @__PURE__ */ new Set();
13899
+ if (backend.dialect === "postgres") {
13900
+ const rows2 = await backend.execute(
13901
+ asCompiledRowsSql(sql`
13902
+ SELECT c.relname AS name
13903
+ FROM pg_class c
13904
+ WHERE c.relname IN (${sqlValueList(tableNames)})
13905
+ AND c.relkind IN ('r', 'p')
13906
+ AND pg_catalog.pg_table_is_visible(c.oid)
13907
+ `)
13908
+ );
13909
+ return new Set(rows2.map((row) => row.name));
13910
+ }
12074
13911
  const rows = await backend.execute(
12075
13912
  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
13913
+ SELECT name
13914
+ FROM sqlite_master
13915
+ WHERE type = 'table'
13916
+ AND name IN (${sqlValueList(tableNames)})
12081
13917
  `)
12082
13918
  );
12083
13919
  return new Set(rows.map((row) => row.name));
12084
13920
  }
13921
+ async function preloadPhysicalIndexStates(backend, physicalIndexNames) {
13922
+ const valid = /* @__PURE__ */ new Set();
13923
+ const invalid = /* @__PURE__ */ new Set();
13924
+ if (physicalIndexNames.length === 0) return { valid, invalid };
13925
+ if (backend.dialect === "postgres") {
13926
+ const rows2 = await backend.execute(
13927
+ asCompiledRowsSql(sql`
13928
+ SELECT c.relname AS name, i.indisvalid AS valid
13929
+ FROM pg_class c
13930
+ JOIN pg_index i ON i.indexrelid = c.oid
13931
+ WHERE c.relname IN (${sqlValueList(physicalIndexNames)})
13932
+ AND pg_catalog.pg_table_is_visible(c.oid)
13933
+ `)
13934
+ );
13935
+ for (const row of rows2) (row.valid ? valid : invalid).add(row.name);
13936
+ return { valid, invalid };
13937
+ }
13938
+ const rows = await backend.execute(
13939
+ asCompiledRowsSql(sql`
13940
+ SELECT name
13941
+ FROM sqlite_master
13942
+ WHERE type = 'index'
13943
+ AND name IN (${sqlValueList(physicalIndexNames)})
13944
+ `)
13945
+ );
13946
+ for (const row of rows) valid.add(row.name);
13947
+ return { valid, invalid };
13948
+ }
12085
13949
  async function dropInvalidIndexLeftover(backend, physicalIndexName) {
12086
13950
  if (backend.executeDdl === void 0) return;
12087
13951
  if (!await hasInvalidIndexLeftover(backend, physicalIndexName)) return;
@@ -12140,6 +14004,121 @@ function buildAttempt(args) {
12140
14004
  error: args.error?.message
12141
14005
  };
12142
14006
  }
14007
+ function backendSupportsIndexMaterialization(backend) {
14008
+ return backend.executeDdl !== void 0 && backend.getIndexMaterialization !== void 0 && backend.recordIndexMaterialization !== void 0;
14009
+ }
14010
+ function assertBackendSupportsIndexMaterialization(backend, surface) {
14011
+ if (backendSupportsIndexMaterialization(backend)) return;
14012
+ throw new ConfigurationError(
14013
+ `${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.`,
14014
+ { code: "MATERIALIZE_BACKEND_UNSUPPORTED" }
14015
+ );
14016
+ }
14017
+ async function materializeSystemIndexes(context, options = {}) {
14018
+ const { backend, graphId, schemaVersion } = context;
14019
+ assertBackendSupportsIndexMaterialization(
14020
+ backend,
14021
+ "store.materializeSystemIndexes()"
14022
+ );
14023
+ await ensureFocusedStatusTable(
14024
+ backend,
14025
+ backend.ensureIndexMaterializationsTable
14026
+ );
14027
+ const concurrent = backend.dialect === "postgres";
14028
+ const candidates = SYSTEM_INDEX_DECLARATIONS.map((declaration) => {
14029
+ const physicalTable = resolveSystemIndexTableName(
14030
+ declaration.table,
14031
+ backend.tableNames
14032
+ );
14033
+ return {
14034
+ declaration,
14035
+ physicalTable,
14036
+ name: systemIndexName(physicalTable, declaration.suffix)
14037
+ };
14038
+ });
14039
+ const statusKeys = candidates.map((candidate) => candidate.name);
14040
+ const targetTables = [
14041
+ ...new Set(candidates.map((candidate) => candidate.physicalTable))
14042
+ ];
14043
+ const [existingByStatusKey, physicalStates, existingTables] = await Promise.all([
14044
+ preloadMaterializations(backend, statusKeys),
14045
+ preloadPhysicalIndexStates(backend, statusKeys),
14046
+ preloadExistingTables(backend, targetTables)
14047
+ ]);
14048
+ const { valid: physicallyPresent } = physicalStates;
14049
+ const physicalRebuildPreload = new Set(
14050
+ statusKeys.filter((name) => !physicallyPresent.has(name))
14051
+ );
14052
+ const materializeEntry = async (candidate) => {
14053
+ const identity = {
14054
+ name: candidate.name,
14055
+ entity: "system",
14056
+ kind: candidate.declaration.table
14057
+ };
14058
+ if (!existingTables.has(candidate.physicalTable)) {
14059
+ return {
14060
+ indexName: candidate.name,
14061
+ entity: "system",
14062
+ kind: candidate.declaration.table,
14063
+ status: "skipped",
14064
+ reason: `Relation "${candidate.physicalTable}" does not exist in this database`
14065
+ };
14066
+ }
14067
+ const existing = existingByStatusKey.get(candidate.name);
14068
+ if (existing !== void 0 && existing.entity !== "system") {
14069
+ return entry(
14070
+ identity,
14071
+ "failed",
14072
+ new Error(
14073
+ `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.`
14074
+ )
14075
+ );
14076
+ }
14077
+ if (existing === void 0 && physicallyPresent.has(candidate.name)) {
14078
+ return entry(identity, "alreadyMaterialized");
14079
+ }
14080
+ const signature = await computeSystemIndexSignature(
14081
+ backend.dialect,
14082
+ candidate.physicalTable,
14083
+ candidate.declaration
14084
+ );
14085
+ const ddl = generateSystemIndexDDL(
14086
+ candidate.declaration,
14087
+ candidate.physicalTable,
14088
+ { concurrent }
14089
+ );
14090
+ return materializeOne(identity, backend, graphId, schemaVersion, {
14091
+ statusKey: candidate.name,
14092
+ signature,
14093
+ driftLabel: "System index",
14094
+ run: () => backend.executeDdl(ddl),
14095
+ existingByStatusKey,
14096
+ physicalRebuildPreload,
14097
+ freshNeedsPhysicalRebuild: async (physicalIndexName) => {
14098
+ const fresh = await preloadPhysicalIndexStates(backend, [
14099
+ physicalIndexName
14100
+ ]);
14101
+ return !fresh.valid.has(physicalIndexName);
14102
+ },
14103
+ rebuildWithoutClaim: true
14104
+ });
14105
+ };
14106
+ const results = await runBucketedMaterialization(
14107
+ candidates,
14108
+ options,
14109
+ (candidate) => candidate.physicalTable,
14110
+ (candidate) => materializeEntry(candidate)
14111
+ );
14112
+ await refreshStatisticsAfterCreation(backend, results, options, concurrent);
14113
+ return { results };
14114
+ }
14115
+ async function computeSystemIndexSignature(dialect, targetTableName, declaration) {
14116
+ const json = JSON.stringify(
14117
+ { dialect, targetTableName, declaration },
14118
+ sortedReplacer
14119
+ );
14120
+ return sha256Hex(json, 16);
14121
+ }
12143
14122
  async function computeIndexSignature(dialect, targetTableName, declaration) {
12144
14123
  const hashable = {
12145
14124
  dialect,
@@ -12368,13 +14347,13 @@ function buildEmbeddingTableCleanup(ctx, row) {
12368
14347
  const slots = await resolveRemovedKindEmbeddingSlots(ctx.backend, row);
12369
14348
  await Promise.all(
12370
14349
  slots.map(
12371
- (slot) => dropVectorSlotStorage(vectorStrategy, executeDdl, slot)
14350
+ (slot) => dropVectorSlotStorage(ctx.backend, vectorStrategy, executeDdl, slot)
12372
14351
  )
12373
14352
  );
12374
14353
  })();
12375
14354
  return [cleanup];
12376
14355
  }
12377
- async function dropVectorSlotStorage(strategy, executeDdl, slot) {
14356
+ async function dropVectorSlotStorage(backend, strategy, executeDdl, slot) {
12378
14357
  try {
12379
14358
  for (const ddl of strategy.buildDropStorage(slot)) {
12380
14359
  await executeDdl(ddl);
@@ -12382,6 +14361,7 @@ async function dropVectorSlotStorage(strategy, executeDdl, slot) {
12382
14361
  } catch (error) {
12383
14362
  if (!isMissingTableError(error)) throw error;
12384
14363
  }
14364
+ await backend.deleteVectorSlotContribution?.(slot);
12385
14365
  }
12386
14366
  async function resolveRemovedKindEmbeddingSlots(backend, row) {
12387
14367
  const priorVersion = row.schemaVersion - 1;
@@ -12457,7 +14437,12 @@ async function reclaimRemovedVectorFieldTables(context) {
12457
14437
  const results = [];
12458
14438
  for (const field2 of orphans) {
12459
14439
  try {
12460
- await dropVectorSlotStorage(vectorStrategy, executeDdl, field2.slot);
14440
+ await dropVectorSlotStorage(
14441
+ backend,
14442
+ vectorStrategy,
14443
+ executeDdl,
14444
+ field2.slot
14445
+ );
12461
14446
  results.push({
12462
14447
  kind: field2.kind,
12463
14448
  fieldPath: field2.fieldPath,
@@ -13841,9 +15826,11 @@ var Store = class _Store {
13841
15826
  * Tier 1 graph algorithms: shortest path, reachability, neighborhoods, and
13842
15827
  * degree centrality.
13843
15828
  *
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.
15829
+ * Traversal calls use the iterative graph-operation substrate: a set-based
15830
+ * breadth-first frontier backed by a temporary working table, or a chunked
15831
+ * inline relation when the backend cannot pin a transactional connection.
15832
+ * `degree` uses a single count query. The facade is built lazily on first
15833
+ * access and cached for the lifetime of the store.
13847
15834
  *
13848
15835
  * @example
13849
15836
  * ```typescript
@@ -14607,10 +16594,10 @@ var Store = class _Store {
14607
16594
  }) : backendOptions;
14608
16595
  try {
14609
16596
  const result = await this.#backend.transaction(
14610
- async (txBackend, sql17) => invoke(
16597
+ async (txBackend, sql19) => invoke(
14611
16598
  this.#buildTransactionContext(
14612
16599
  txBackend,
14613
- sql17,
16600
+ sql19,
14614
16601
  runHooks,
14615
16602
  receiptRecorder
14616
16603
  )
@@ -14728,7 +16715,7 @@ var Store = class _Store {
14728
16715
  * (#140); when history capture is enabled it is replaced with a fail-loud
14729
16716
  * guard because raw graph writes would bypass recorded-time capture.
14730
16717
  */
14731
- #buildTransactionContext(txBackend, sql17, runHooks = this.#immediateHookRunner(), receiptRecorder) {
16718
+ #buildTransactionContext(txBackend, sql19, runHooks = this.#immediateHookRunner(), receiptRecorder) {
14732
16719
  const txNodeOperations = {
14733
16720
  ...this.#buildNodeOperations(this.#createNodeOperationContext(runHooks)),
14734
16721
  createQuery: () => this.#createQueryForBackend(txBackend),
@@ -14762,7 +16749,7 @@ var Store = class _Store {
14762
16749
  ));
14763
16750
  }
14764
16751
  const getNodeCollection = (kind) => this.#resolveDynamicNodeCollection(nodes, kind);
14765
- const sqlAvailability = this.#captureEnabled ? "history" : this.#revisionTrackingEnabled ? "revisionTracking" : sql17 === void 0 ? "unavailable" : "available";
16752
+ const sqlAvailability = this.#captureEnabled ? "history" : this.#revisionTrackingEnabled ? "revisionTracking" : sql19 === void 0 ? "unavailable" : "available";
14766
16753
  const base = {
14767
16754
  nodes,
14768
16755
  edges,
@@ -14771,9 +16758,9 @@ var Store = class _Store {
14771
16758
  getNodeCollection,
14772
16759
  runNodeOperationHooks
14773
16760
  };
14774
- const withSql = sql17 === void 0 ? base : {
16761
+ const withSql = sql19 === void 0 ? base : {
14775
16762
  ...base,
14776
- sql: this.#captureEnabled ? createHistoryUnsafeSqlRef() : this.#revisionTrackingEnabled ? createRevisionTrackingUnsafeSqlRef() : sql17
16763
+ sql: this.#captureEnabled ? createHistoryUnsafeSqlRef() : this.#revisionTrackingEnabled ? createRevisionTrackingUnsafeSqlRef() : sql19
14777
16764
  };
14778
16765
  return receiptRecorder === void 0 ? withSql : this.#attachMeasure(withSql);
14779
16766
  }
@@ -14964,6 +16951,7 @@ var Store = class _Store {
14964
16951
  merged,
14965
16952
  activeRow.version
14966
16953
  );
16954
+ await materializeVectorContributions(this.#backend, merged);
14967
16955
  const evolved = this.#cloneWithGraph(
14968
16956
  merged,
14969
16957
  options?.ref,
@@ -15066,6 +17054,29 @@ var Store = class _Store {
15066
17054
  options ?? {}
15067
17055
  );
15068
17056
  }
17057
+ /**
17058
+ * Materializes TypeGraph's own base-relation indexes
17059
+ * (`SYSTEM_INDEX_DECLARATIONS`) against the live database.
17060
+ *
17061
+ * Bootstrap DDL runs only on first boot, so a system index shipped in a
17062
+ * newer library version never reaches an already-initialized database on
17063
+ * its own. `createStoreWithSchema` runs this automatically; deployments
17064
+ * that boot with plain `createStore` / `createVerifiedStore` (zero-DDL by
17065
+ * contract) adopt new system indexes by calling this once under a role
17066
+ * that may run DDL. Same status-table, drift-signature, and Postgres
17067
+ * `CREATE INDEX CONCURRENTLY` claim semantics as `materializeIndexes`.
17068
+ */
17069
+ async materializeSystemIndexes(options) {
17070
+ const { activeRow } = await this.#loadCaughtUp("materialize");
17071
+ return materializeSystemIndexes(
17072
+ {
17073
+ backend: this.#baseBackend,
17074
+ graphId: this.graphId,
17075
+ schemaVersion: activeRow.version
17076
+ },
17077
+ options ?? {}
17078
+ );
17079
+ }
15069
17080
  /**
15070
17081
  * Recreate a vector field's per-field storage at its current declared
15071
17082
  * dimension, then optionally re-embed existing rows.
@@ -15122,10 +17133,14 @@ var Store = class _Store {
15122
17133
  for (const ddl of strategy.buildDropStorage(slot)) {
15123
17134
  await backend.executeDdl(ddl);
15124
17135
  }
15125
- for (const contribution of strategy.ownedTables(slot)) {
15126
- for (const ddl of contribution.createDdl) {
15127
- await backend.executeDdl(ddl);
17136
+ if (backend.ensureVectorSlotContribution === void 0) {
17137
+ for (const contribution of strategy.ownedTables(slot)) {
17138
+ for (const ddl of contribution.createDdl) {
17139
+ await backend.executeDdl(ddl);
17140
+ }
15128
17141
  }
17142
+ } else {
17143
+ await backend.ensureVectorSlotContribution(slot, { force: true });
15129
17144
  }
15130
17145
  const declaredIndexMaterializes = declaration.indexType !== "none" && backend.capabilities.vector?.indexTypes.includes(
15131
17146
  declaration.indexType
@@ -15607,6 +17622,20 @@ async function materializeRuntimeContributions(backend, graphId) {
15607
17622
  }
15608
17623
  await backend.ensureFulltextTable?.(graphId);
15609
17624
  }
17625
+ async function materializeVectorContributions(backend, graph) {
17626
+ if (backend.capabilities.vector?.supported !== true) return;
17627
+ const slots = resolveGraphVectorSlots(graph);
17628
+ const ensureVectorSlotContributions = backend.ensureVectorSlotContributions;
17629
+ if (ensureVectorSlotContributions !== void 0) {
17630
+ await ensureVectorSlotContributions(slots, { onDrift: "skip" });
17631
+ return;
17632
+ }
17633
+ const ensureVectorSlotContribution = backend.ensureVectorSlotContribution;
17634
+ if (ensureVectorSlotContribution === void 0) return;
17635
+ for (const slot of slots) {
17636
+ await ensureVectorSlotContribution(slot, { onDrift: "skip" });
17637
+ }
17638
+ }
15610
17639
  async function createStoreWithSchema(graph, backend, options) {
15611
17640
  const {
15612
17641
  graph: merged,
@@ -15618,6 +17647,10 @@ async function createStoreWithSchema(graph, backend, options) {
15618
17647
  preloaded: { activeRow, storedSchema }
15619
17648
  });
15620
17649
  await materializeRuntimeContributions(backend, merged.id);
17650
+ await materializeVectorContributions(backend, merged);
17651
+ if (options?.systemIndexes !== "skip") {
17652
+ await materializeSystemIndexesOnBoot(backend, merged.id, result);
17653
+ }
15621
17654
  const store = new Store(
15622
17655
  merged,
15623
17656
  backend,
@@ -15626,6 +17659,30 @@ async function createStoreWithSchema(graph, backend, options) {
15626
17659
  );
15627
17660
  return [store, result];
15628
17661
  }
17662
+ async function materializeSystemIndexesOnBoot(backend, graphId, result) {
17663
+ if (result.status === "breaking") return;
17664
+ if (!backendSupportsIndexMaterialization(backend)) return;
17665
+ const schemaVersion = result.status === "migrated" ? result.toVersion : result.version;
17666
+ try {
17667
+ const { results } = await materializeSystemIndexes(
17668
+ { backend: asRawBackend(backend), graphId, schemaVersion },
17669
+ {}
17670
+ );
17671
+ const failed = results.filter(
17672
+ (entryResult) => entryResult.status === "failed"
17673
+ );
17674
+ if (failed.length === 0) return;
17675
+ console.warn(
17676
+ `[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().`,
17677
+ failed[0]?.error
17678
+ );
17679
+ } catch (error) {
17680
+ console.warn(
17681
+ "[typegraph] system-index materialization failed at boot; the store is usable but new system indexes were not adopted. Retry via store.materializeSystemIndexes().",
17682
+ error
17683
+ );
17684
+ }
17685
+ }
15629
17686
  async function createVerifiedStore(graph, backend, options) {
15630
17687
  const {
15631
17688
  graph: merged,
@@ -15642,5 +17699,5 @@ async function createVerifiedStore(graph, backend, options) {
15642
17699
  }
15643
17700
 
15644
17701
  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
17702
+ //# sourceMappingURL=chunk-GA4TT3DH.js.map
17703
+ //# sourceMappingURL=chunk-GA4TT3DH.js.map