@nicia-ai/typegraph 0.33.0 → 0.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (244) hide show
  1. package/README.md +20 -0
  2. package/dist/backend/postgres/index.cjs +26 -22
  3. package/dist/backend/postgres/index.d.cts +5 -5
  4. package/dist/backend/postgres/index.d.ts +5 -5
  5. package/dist/backend/postgres/index.js +13 -9
  6. package/dist/backend/postgres/pglite.cjs +17 -13
  7. package/dist/backend/postgres/pglite.cjs.map +1 -1
  8. package/dist/backend/postgres/pglite.d.cts +2 -2
  9. package/dist/backend/postgres/pglite.d.ts +2 -2
  10. package/dist/backend/postgres/pglite.js +13 -9
  11. package/dist/backend/postgres/pglite.js.map +1 -1
  12. package/dist/backend/sqlite/index.cjs +24 -20
  13. package/dist/backend/sqlite/index.d.cts +6 -6
  14. package/dist/backend/sqlite/index.d.ts +6 -6
  15. package/dist/backend/sqlite/index.js +12 -8
  16. package/dist/backend/sqlite/libsql.cjs +18 -14
  17. package/dist/backend/sqlite/libsql.cjs.map +1 -1
  18. package/dist/backend/sqlite/libsql.d.cts +11 -4
  19. package/dist/backend/sqlite/libsql.d.ts +11 -4
  20. package/dist/backend/sqlite/libsql.js +14 -10
  21. package/dist/backend/sqlite/libsql.js.map +1 -1
  22. package/dist/backend/sqlite/local.cjs +18 -14
  23. package/dist/backend/sqlite/local.cjs.map +1 -1
  24. package/dist/backend/sqlite/local.d.cts +2 -2
  25. package/dist/backend/sqlite/local.d.ts +2 -2
  26. package/dist/backend/sqlite/local.js +13 -9
  27. package/dist/backend/sqlite/local.js.map +1 -1
  28. package/dist/chunk-2EBMPJBO.js +16 -0
  29. package/dist/chunk-2EBMPJBO.js.map +1 -0
  30. package/dist/{chunk-CFNT3ITG.cjs → chunk-2JXB3NYJ.cjs} +15 -35
  31. package/dist/chunk-2JXB3NYJ.cjs.map +1 -0
  32. package/dist/{chunk-GYVBEZAO.js → chunk-2MRAX3UE.js} +3 -18
  33. package/dist/chunk-2MRAX3UE.js.map +1 -0
  34. package/dist/{chunk-D3EWZ67S.cjs → chunk-3BRVBQQD.cjs} +999 -2675
  35. package/dist/chunk-3BRVBQQD.cjs.map +1 -0
  36. package/dist/{chunk-FDN43KXV.cjs → chunk-3Z6TW3CT.cjs} +20 -19
  37. package/dist/chunk-3Z6TW3CT.cjs.map +1 -0
  38. package/dist/{chunk-6NRFUHMS.js → chunk-4DIBPQNJ.js} +5 -4
  39. package/dist/chunk-4DIBPQNJ.js.map +1 -0
  40. package/dist/chunk-4S2FXGBJ.js +9 -0
  41. package/dist/chunk-4S2FXGBJ.js.map +1 -0
  42. package/dist/chunk-5IQIX7LU.cjs +2070 -0
  43. package/dist/chunk-5IQIX7LU.cjs.map +1 -0
  44. package/dist/{chunk-CWFPJ3RC.js → chunk-6GNSY7AV.js} +3 -3
  45. package/dist/{chunk-CWFPJ3RC.js.map → chunk-6GNSY7AV.js.map} +1 -1
  46. package/dist/{chunk-NG5N7R45.cjs → chunk-6HVOHZHS.cjs} +184 -117
  47. package/dist/chunk-6HVOHZHS.cjs.map +1 -0
  48. package/dist/chunk-6SCVW4AS.js +20 -0
  49. package/dist/chunk-6SCVW4AS.js.map +1 -0
  50. package/dist/chunk-A7SZZFAG.js +2010 -0
  51. package/dist/chunk-A7SZZFAG.js.map +1 -0
  52. package/dist/chunk-ACSVZPK7.cjs +21 -0
  53. package/dist/chunk-ACSVZPK7.cjs.map +1 -0
  54. package/dist/{chunk-IZ4PCL47.js → chunk-BVTWD66D.js} +4 -66
  55. package/dist/chunk-BVTWD66D.js.map +1 -0
  56. package/dist/{chunk-QBYTJSAY.js → chunk-CHXTTSC6.js} +5 -39
  57. package/dist/chunk-CHXTTSC6.js.map +1 -0
  58. package/dist/{chunk-DJYRRDHA.js → chunk-CYX2JTDG.js} +3 -3
  59. package/dist/{chunk-DJYRRDHA.js.map → chunk-CYX2JTDG.js.map} +1 -1
  60. package/dist/{chunk-PRMKPFN5.js → chunk-DEQFL35G.js} +4 -3
  61. package/dist/chunk-DEQFL35G.js.map +1 -0
  62. package/dist/chunk-DI74SMXS.cjs +29 -0
  63. package/dist/chunk-DI74SMXS.cjs.map +1 -0
  64. package/dist/{chunk-WWVV44DY.cjs → chunk-G6SOJEDI.cjs} +11 -11
  65. package/dist/{chunk-WWVV44DY.cjs.map → chunk-G6SOJEDI.cjs.map} +1 -1
  66. package/dist/{chunk-LM2IC4ZJ.cjs → chunk-GQANNXXT.cjs} +15 -14
  67. package/dist/chunk-GQANNXXT.cjs.map +1 -0
  68. package/dist/chunk-GZCNZ7QW.js +24 -0
  69. package/dist/chunk-GZCNZ7QW.js.map +1 -0
  70. package/dist/{chunk-KTIEB7VH.cjs → chunk-HK5SATYM.cjs} +35 -58
  71. package/dist/chunk-HK5SATYM.cjs.map +1 -0
  72. package/dist/chunk-HMFK4BXR.cjs +12 -0
  73. package/dist/{chunk-6ZR4QC3O.cjs.map → chunk-HMFK4BXR.cjs.map} +1 -1
  74. package/dist/chunk-ITE4IXAK.js +25 -0
  75. package/dist/chunk-ITE4IXAK.js.map +1 -0
  76. package/dist/chunk-JJRCLAUN.cjs +155 -0
  77. package/dist/chunk-JJRCLAUN.cjs.map +1 -0
  78. package/dist/{chunk-ZFWNO3GE.cjs → chunk-JQGIPGVL.cjs} +66 -2
  79. package/dist/chunk-JQGIPGVL.cjs.map +1 -0
  80. package/dist/{chunk-XVUJY72M.js → chunk-K2TGS3EN.js} +105 -38
  81. package/dist/chunk-K2TGS3EN.js.map +1 -0
  82. package/dist/chunk-K34OPSJK.js +29 -0
  83. package/dist/chunk-K34OPSJK.js.map +1 -0
  84. package/dist/{chunk-YW7GHMF4.cjs → chunk-KGC4LY5T.cjs} +18 -17
  85. package/dist/chunk-KGC4LY5T.cjs.map +1 -0
  86. package/dist/{chunk-SST7XDY6.cjs → chunk-KNHYNQQ3.cjs} +111 -72
  87. package/dist/chunk-KNHYNQQ3.cjs.map +1 -0
  88. package/dist/{chunk-C4C4Q7A6.js → chunk-KUTNI6SI.js} +6 -21
  89. package/dist/chunk-KUTNI6SI.js.map +1 -0
  90. package/dist/{chunk-OS6KS3HV.js → chunk-LANHCOXB.js} +8 -7
  91. package/dist/chunk-LANHCOXB.js.map +1 -0
  92. package/dist/chunk-N6WTUSBV.cjs +23 -0
  93. package/dist/chunk-N6WTUSBV.cjs.map +1 -0
  94. package/dist/{chunk-73KASJGO.cjs → chunk-NAUA4PW5.cjs} +4 -28
  95. package/dist/chunk-NAUA4PW5.cjs.map +1 -0
  96. package/dist/chunk-QV245VMN.js +145 -0
  97. package/dist/chunk-QV245VMN.js.map +1 -0
  98. package/dist/{chunk-CEF2QOTO.js → chunk-RBV7YCQD.js} +4 -3
  99. package/dist/chunk-RBV7YCQD.js.map +1 -0
  100. package/dist/{chunk-OIE2UPVG.cjs → chunk-RHZWOJVA.cjs} +2 -19
  101. package/dist/chunk-RHZWOJVA.cjs.map +1 -0
  102. package/dist/{chunk-3IR2GEMJ.cjs → chunk-RRKQAKD6.cjs} +10 -9
  103. package/dist/chunk-RRKQAKD6.cjs.map +1 -0
  104. package/dist/{chunk-UPDMJEW6.js → chunk-RY2VIWAM.js} +11 -8
  105. package/dist/chunk-RY2VIWAM.js.map +1 -0
  106. package/dist/{chunk-PIHXFF73.js → chunk-SHLY4MCM.js} +3 -3
  107. package/dist/{chunk-PIHXFF73.js.map → chunk-SHLY4MCM.js.map} +1 -1
  108. package/dist/{chunk-SPT5JMME.js → chunk-T2DWPQNE.js} +103 -64
  109. package/dist/chunk-T2DWPQNE.js.map +1 -0
  110. package/dist/{chunk-WYTUF3QO.js → chunk-T46GR2VY.js} +3 -3
  111. package/dist/{chunk-WYTUF3QO.js.map → chunk-T46GR2VY.js.map} +1 -1
  112. package/dist/{chunk-MBLXCV2Y.cjs → chunk-T6SPUYLX.cjs} +5 -43
  113. package/dist/chunk-T6SPUYLX.cjs.map +1 -0
  114. package/dist/{chunk-3QRBXYT5.cjs → chunk-TGJE22RM.cjs} +6 -6
  115. package/dist/{chunk-3QRBXYT5.cjs.map → chunk-TGJE22RM.cjs.map} +1 -1
  116. package/dist/{chunk-BF3ZDEUE.cjs → chunk-TOTPVGOG.cjs} +70 -69
  117. package/dist/chunk-TOTPVGOG.cjs.map +1 -0
  118. package/dist/{chunk-KY5SDVA7.cjs → chunk-UJLOZYKT.cjs} +90 -87
  119. package/dist/chunk-UJLOZYKT.cjs.map +1 -0
  120. package/dist/{chunk-I4W7MKVG.cjs → chunk-UNNPKQUG.cjs} +19 -155
  121. package/dist/chunk-UNNPKQUG.cjs.map +1 -0
  122. package/dist/chunk-UR7W5YQK.js +18 -0
  123. package/dist/chunk-UR7W5YQK.js.map +1 -0
  124. package/dist/{chunk-PYD3UCDU.js → chunk-V3QNTB7Z.js} +19 -148
  125. package/dist/chunk-V3QNTB7Z.js.map +1 -0
  126. package/dist/{chunk-XKNAK27R.js → chunk-VCGO4V26.js} +4 -3
  127. package/dist/chunk-VCGO4V26.js.map +1 -0
  128. package/dist/{chunk-EF3Z645U.cjs → chunk-WL2SG7ZV.cjs} +6 -72
  129. package/dist/chunk-WL2SG7ZV.cjs.map +1 -0
  130. package/dist/{chunk-WLP6TKFB.js → chunk-WLRIWYHQ.js} +640 -2307
  131. package/dist/chunk-WLRIWYHQ.js.map +1 -0
  132. package/dist/{chunk-4RBAJVEC.cjs → chunk-WVC2Q2FD.cjs} +5 -5
  133. package/dist/{chunk-4RBAJVEC.cjs.map → chunk-WVC2Q2FD.cjs.map} +1 -1
  134. package/dist/chunk-XXVEULWX.js +39 -0
  135. package/dist/chunk-XXVEULWX.js.map +1 -0
  136. package/dist/chunk-YAZE7QZR.cjs +32 -0
  137. package/dist/chunk-YAZE7QZR.cjs.map +1 -0
  138. package/dist/chunk-YC6PWTHY.cjs +44 -0
  139. package/dist/chunk-YC6PWTHY.cjs.map +1 -0
  140. package/dist/chunk-Z5HBYM7U.cjs +11 -0
  141. package/dist/chunk-Z5HBYM7U.cjs.map +1 -0
  142. package/dist/{chunk-454I6GDF.js → chunk-Z5XBMDXU.js} +63 -3
  143. package/dist/chunk-Z5XBMDXU.js.map +1 -0
  144. package/dist/{chunk-3642VBVC.cjs → chunk-ZP5WHI4C.cjs} +106 -80
  145. package/dist/chunk-ZP5WHI4C.cjs.map +1 -0
  146. package/dist/{chunk-YCV72E6L.js → chunk-ZURTTSCM.js} +4 -25
  147. package/dist/chunk-ZURTTSCM.js.map +1 -0
  148. package/dist/{chunk-JHOBXD7O.js → chunk-ZV5N4DBG.js} +46 -20
  149. package/dist/chunk-ZV5N4DBG.js.map +1 -0
  150. package/dist/chunk-ZY4GXIVN.cjs +32 -0
  151. package/dist/chunk-ZY4GXIVN.cjs.map +1 -0
  152. package/dist/{ddl-CVnHJcar.d.ts → ddl-BrmPg61z.d.ts} +3 -3
  153. package/dist/{ddl-Dxt_fBHr.d.cts → ddl-j1-sH6lE.d.cts} +3 -3
  154. package/dist/graph-extension/index.cjs +18 -17
  155. package/dist/graph-extension/index.js +3 -2
  156. package/dist/graph-merge/index.cjs +294 -146
  157. package/dist/graph-merge/index.cjs.map +1 -1
  158. package/dist/graph-merge/index.d.cts +3 -3
  159. package/dist/graph-merge/index.d.ts +3 -3
  160. package/dist/graph-merge/index.js +172 -24
  161. package/dist/graph-merge/index.js.map +1 -1
  162. package/dist/index.cjs +181 -172
  163. package/dist/index.cjs.map +1 -1
  164. package/dist/index.d.cts +7 -7
  165. package/dist/index.d.ts +7 -7
  166. package/dist/index.js +30 -22
  167. package/dist/index.js.map +1 -1
  168. package/dist/indexes/index.cjs +27 -25
  169. package/dist/indexes/index.d.cts +4 -4
  170. package/dist/indexes/index.d.ts +4 -4
  171. package/dist/indexes/index.js +9 -7
  172. package/dist/interchange/index.cjs +25 -16
  173. package/dist/interchange/index.d.cts +3 -3
  174. package/dist/interchange/index.d.ts +3 -3
  175. package/dist/interchange/index.js +12 -3
  176. package/dist/{manager-C0_r0ALf.d.ts → manager-BZUeN_BU.d.ts} +1 -1
  177. package/dist/{manager-BYl_zyZR.d.cts → manager-ms9cKdc6.d.cts} +1 -1
  178. package/dist/{postgres-MzBPkYzq.d.ts → postgres-BhpxGMC5.d.cts} +1 -1
  179. package/dist/{postgres-_9psmVGm.d.cts → postgres-CWHLwNl1.d.ts} +1 -1
  180. package/dist/profiler/index.cjs +3 -3
  181. package/dist/profiler/index.d.cts +5 -5
  182. package/dist/profiler/index.d.ts +5 -5
  183. package/dist/profiler/index.js +2 -2
  184. package/dist/provenance/index.cjs +591 -0
  185. package/dist/provenance/index.cjs.map +1 -0
  186. package/dist/provenance/index.d.cts +73 -0
  187. package/dist/provenance/index.d.ts +73 -0
  188. package/dist/provenance/index.js +589 -0
  189. package/dist/provenance/index.js.map +1 -0
  190. package/dist/schema/index.cjs +36 -32
  191. package/dist/schema/index.cjs.map +1 -1
  192. package/dist/schema/index.d.cts +4 -4
  193. package/dist/schema/index.d.ts +4 -4
  194. package/dist/schema/index.js +13 -9
  195. package/dist/schema/index.js.map +1 -1
  196. package/dist/{sqlite-gm44dShb.d.ts → sqlite-CGu5SLsw.d.ts} +1 -1
  197. package/dist/{sqlite-BfksSb45.d.cts → sqlite-XrDxNaa7.d.cts} +1 -1
  198. package/dist/{store-view-FfE5_7eo.d.ts → store-view-BlgnhEYf.d.ts} +33 -5
  199. package/dist/{store-view-9jQ2rPum.d.cts → store-view-D09po4H6.d.cts} +33 -5
  200. package/dist/{types-CdWxK-nD.d.ts → types-B1TbZJYS.d.ts} +1 -1
  201. package/dist/{types-DGylBhWo.d.ts → types-D1Sqeyxy.d.ts} +7 -3
  202. package/dist/{types-B6mhrcLv.d.cts → types-DiE1AO7p.d.cts} +1 -1
  203. package/dist/{types-C_UnQOQp.d.cts → types-DkNnF-7K.d.cts} +7 -3
  204. package/dist/{where-Bpp2ul_z.d.cts → where-DKjuofPr.d.cts} +1 -1
  205. package/dist/{where-BsEXraKW.d.ts → where-DrUIIWHb.d.ts} +1 -1
  206. package/package.json +8 -2
  207. package/dist/chunk-2YMGNYXY.js +0 -40
  208. package/dist/chunk-2YMGNYXY.js.map +0 -1
  209. package/dist/chunk-3642VBVC.cjs.map +0 -1
  210. package/dist/chunk-3IR2GEMJ.cjs.map +0 -1
  211. package/dist/chunk-454I6GDF.js.map +0 -1
  212. package/dist/chunk-6NRFUHMS.js.map +0 -1
  213. package/dist/chunk-6ZR4QC3O.cjs +0 -12
  214. package/dist/chunk-73KASJGO.cjs.map +0 -1
  215. package/dist/chunk-BF3ZDEUE.cjs.map +0 -1
  216. package/dist/chunk-C4C4Q7A6.js.map +0 -1
  217. package/dist/chunk-CEF2QOTO.js.map +0 -1
  218. package/dist/chunk-CFNT3ITG.cjs.map +0 -1
  219. package/dist/chunk-D3EWZ67S.cjs.map +0 -1
  220. package/dist/chunk-EF3Z645U.cjs.map +0 -1
  221. package/dist/chunk-FDN43KXV.cjs.map +0 -1
  222. package/dist/chunk-GYVBEZAO.js.map +0 -1
  223. package/dist/chunk-I4W7MKVG.cjs.map +0 -1
  224. package/dist/chunk-IZ4PCL47.js.map +0 -1
  225. package/dist/chunk-JHOBXD7O.js.map +0 -1
  226. package/dist/chunk-KTIEB7VH.cjs.map +0 -1
  227. package/dist/chunk-KY5SDVA7.cjs.map +0 -1
  228. package/dist/chunk-LM2IC4ZJ.cjs.map +0 -1
  229. package/dist/chunk-MBLXCV2Y.cjs.map +0 -1
  230. package/dist/chunk-NG5N7R45.cjs.map +0 -1
  231. package/dist/chunk-OIE2UPVG.cjs.map +0 -1
  232. package/dist/chunk-OS6KS3HV.js.map +0 -1
  233. package/dist/chunk-PRMKPFN5.js.map +0 -1
  234. package/dist/chunk-PYD3UCDU.js.map +0 -1
  235. package/dist/chunk-QBYTJSAY.js.map +0 -1
  236. package/dist/chunk-SPT5JMME.js.map +0 -1
  237. package/dist/chunk-SST7XDY6.cjs.map +0 -1
  238. package/dist/chunk-UPDMJEW6.js.map +0 -1
  239. package/dist/chunk-WLP6TKFB.js.map +0 -1
  240. package/dist/chunk-XKNAK27R.js.map +0 -1
  241. package/dist/chunk-XVUJY72M.js.map +0 -1
  242. package/dist/chunk-YCV72E6L.js.map +0 -1
  243. package/dist/chunk-YW7GHMF4.cjs.map +0 -1
  244. package/dist/chunk-ZFWNO3GE.cjs.map +0 -1
@@ -1,307 +1,25 @@
1
1
  import { quoteIdentifier, EMPTY_REQUIRED_COLUMNS, addRequiredColumn, markSelectiveFieldAsRequired, markFieldRefAsRequired, NODE_COLUMNS, shouldProjectColumn, mapSelectiveSystemFieldToColumn, isIdFieldRef, isAggregateExpr, EDGE_COLUMNS } from './chunk-3EUKJEQ3.js';
2
- import { loadAndMergeGraphExtensionDocument, ensureSchema, loadAndVerifyGraph, mergeGraphExtension, commitNewSchemaVersion, applyDeprecatedKinds, loadActiveSchemaWithBootstrap, parseSerializedSchema, computeClosuresFromOntology, createEmptyClosures, KindRegistry, extensionKindNames, serializeSchemaProperties, buildGraphExtensionOntologyKeySet, compileTimeOntologyKey, getTypeName, validateNodeProps, validateEdgeProps } from './chunk-XVUJY72M.js';
3
- import { validateCanonicalIsoDate, nowIso, isKnownKind, validateOptionalCanonicalIsoDate, getNodeKinds, getEdgeKinds } from './chunk-QBYTJSAY.js';
4
- import { generateIndexDDL } from './chunk-PIHXFF73.js';
5
- import { NODE_META_KEYS, EDGE_META_KEYS } from './chunk-6NRFUHMS.js';
6
- import { createSchemaIntrospector, getSearchableMetadata, DEFAULT_SEARCHABLE_LANGUAGE } from './chunk-IZ4PCL47.js';
7
- import { resolveEmbeddingFields } from './chunk-454I6GDF.js';
8
- import { IncompatibleChangeError, filterReservedKeys, RESERVED_NODE_KEYS, RESERVED_EDGE_KEYS, RemoveCompileTimeKindError, KindHasReferentsError, validateProjectionField } from './chunk-YCV72E6L.js';
9
- import { isParameterRef, fieldRef, buildFieldBuilderForTypeInfo, stringField, createFulltextAccessor, runOptionallyInTransaction, createEdgeRowMapper, createNodeRowMapper, baseField, objectField, arrayField, dateField, numberField, createBackendOverlay, POSTGRES_ROW_MAPPER_CONFIG, SQLITE_ROW_MAPPER_CONFIG, compileKindFilter, chunk, sqlValueList, isPresent, groupBy, buildPredicateIndex, compileFieldValue, extractVectorSimilarityPredicates, extractFulltextMatchPredicates, compilePredicateExpression, getPredicatesForAlias, getNodeKindsForAlias, getHybridTargetAlias, compilePredicateClauses } from './chunk-PYD3UCDU.js';
10
- import { withRecordedRelationsPrecondition, sortedReplacer, canonicalEqual, isMissingTableError } from './chunk-GYVBEZAO.js';
11
- import { getDialect, assertVectorMinScore, compileNodeIndexFieldKeys, compileIndexWhere, vectorScoreExpression, vectorMinScoreCondition, sqlNull } from './chunk-C4C4Q7A6.js';
12
- import { MAX_PG_IDENTIFIER_LENGTH, DEFAULT_PAGINATION_LIMIT, DEFAULT_STREAM_BATCH_SIZE, sha256Hex, SQLITE_PATH_DELIMITER, PG_ARRAY_START, PG_ARRAY_END, PG_PATH_ELEMENT_SEPARATOR, fnv1aBase36 } from './chunk-2YMGNYXY.js';
13
- import { ConfigurationError, UnsupportedPredicateError, ValidationError, KindNotFoundError, EndpointError, EagerMaterializationError, CompilerInvariantError, DatabaseOperationError, EdgeNotFoundError, NodeIndexNotFoundError, NodeConstraintNotFoundError, EndpointNotFoundError, RestrictedDeleteError, CardinalityError, UniquenessError, DisjointError, NodeNotFoundError } from './chunk-TQOYM4LN.js';
2
+ import { createEdgeRowMapper, createNodeRowMapper, POSTGRES_ROW_MAPPER_CONFIG, SQLITE_ROW_MAPPER_CONFIG } from './chunk-QV245VMN.js';
3
+ import { runHookedWriteOperation, runInWriteTransaction } from './chunk-6SCVW4AS.js';
4
+ import { loadAndMergeGraphExtensionDocument, ensureSchema, loadAndVerifyGraph, mergeGraphExtension, commitNewSchemaVersion, applyDeprecatedKinds, loadActiveSchemaWithBootstrap, parseSerializedSchema, computeClosuresFromOntology, createEmptyClosures, KindRegistry, extensionKindNames, serializeSchemaProperties, buildGraphExtensionOntologyKeySet, compileTimeOntologyKey, getTypeName, validateNodeProps, validateEdgeProps } from './chunk-K2TGS3EN.js';
5
+ import { sortedReplacer, canonicalEqual } from './chunk-UR7W5YQK.js';
6
+ import { isKnownKind, getNodeKinds, getEdgeKinds } from './chunk-CHXTTSC6.js';
7
+ import { generateIndexDDL } from './chunk-SHLY4MCM.js';
8
+ import { getDialect, assertVectorMinScore, compileNodeIndexFieldKeys, compileIndexWhere, vectorScoreExpression, vectorMinScoreCondition } from './chunk-KUTNI6SI.js';
9
+ import { NODE_META_KEYS, EDGE_META_KEYS } from './chunk-4DIBPQNJ.js';
10
+ import { sha256Hex, fnv1aBase36 } from './chunk-K34OPSJK.js';
11
+ import { createSchemaIntrospector } from './chunk-BVTWD66D.js';
12
+ import { IncompatibleChangeError, filterReservedKeys, RESERVED_NODE_KEYS, RESERVED_EDGE_KEYS, RemoveCompileTimeKindError, KindHasReferentsError, validateProjectionField } from './chunk-ZURTTSCM.js';
13
+ import { compareStrings } from './chunk-4S2FXGBJ.js';
14
+ import { resolveReadCoordinate, withRecordedCoordinate, describeCoordinate, coordinateContext, DEFAULT_SQL_SCHEMA, requireSqlSchema, recordedReadSchemaFor, asCompiledSelectSql, getSearchableFields, createSqlSchema, requireExternalRecordedReadSource, createRecordedReadBinding, createRecordedBackend, readRecordedClock, asRecordedInstant, recordedCaptureRequiresCallbackTransactionError, assertRecordedCaptureTransactionIsolation, createRecordedTransactionScope, lockRecordedGraphWrite, createHistoryUnsafeSqlRef, generateId, vectorSlotKey, getEmbeddingFields, computeFulltextContent, recordedReadSqlSchema, requireRecordedReadBinding, asCompiledRowsSql, getKindsForUniquenessCheck, checkWherePredicate, computeUniqueKey, applyNodeHardDelete, createNodeWriteContext, applyNodeSoftDelete, runInsertBatch, nodeInsertDispatch, runInsertBatchReturning, edgeInsertDispatch, getNodeRowsByIds, getEdgeRowsByIds, applyNodeUpdate, applyNodeInsertSideEffects, checkCardinality, checkUniqueEdge, validateEdgeEndpoints, assertValidRecordedInstant, checkUniquenessConstraints, createUniquenessContext, recordedBindParamBudget, checkDisjointness, closeRecordedHardDeletedKind, asCompiledStatementSql, EMBEDDINGS_CTE_ALIAS, FULLTEXT_CTE_ALIAS, HYBRID_CANDIDATES_CTE_ALIAS, ALIAS_CTE_PREFIX } from './chunk-A7SZZFAG.js';
15
+ import { isParameterRef, fieldRef, buildFieldBuilderForTypeInfo, stringField, createFulltextAccessor, runOptionallyInTransaction, baseField, objectField, arrayField, dateField, numberField, isLiveNodeRow, createBackendOverlay, compileKindFilter, chunk, sqlValueList, buildPredicateIndex, compileFieldValue, extractVectorSimilarityPredicates, extractFulltextMatchPredicates, compilePredicateExpression, getPredicatesForAlias, getNodeKindsForAlias, getHybridTargetAlias, compilePredicateClauses } from './chunk-V3QNTB7Z.js';
16
+ import { nowIso, validateOptionalCanonicalIsoDate } from './chunk-XXVEULWX.js';
17
+ import { withRecordedRelationsPrecondition, isMissingTableError } from './chunk-2MRAX3UE.js';
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-2EBMPJBO.js';
19
+ import { resolveEmbeddingFields } from './chunk-Z5XBMDXU.js';
20
+ import { ConfigurationError, UnsupportedPredicateError, ValidationError, KindNotFoundError, EndpointError, EagerMaterializationError, CompilerInvariantError, UniquenessError, DatabaseOperationError, CardinalityError, EdgeNotFoundError, NodeIndexNotFoundError, NodeConstraintNotFoundError, NodeNotFoundError, EndpointNotFoundError } from './chunk-TQOYM4LN.js';
14
21
  import { jsonPointer, parseJsonPointer, resolveJsonPointer } from './chunk-K5P5OZXS.js';
15
- import { sql, Placeholder } from 'drizzle-orm';
16
- import { nanoid } from 'nanoid';
17
-
18
- // src/core/temporal.ts
19
- var RECORDED_MAX = "9999-12-31T23:59:59.999Z";
20
- function assertValidRecordedInstant(value, path) {
21
- validateCanonicalIsoDate(value, path);
22
- if (value >= RECORDED_MAX) {
23
- throw new ValidationError(
24
- `${path} must be before the recorded-time open sentinel ${RECORDED_MAX}.`,
25
- {
26
- issues: [
27
- {
28
- path,
29
- message: `Expected a timestamp before ${RECORDED_MAX}, got "${value}"`
30
- }
31
- ]
32
- },
33
- {
34
- suggestion: "Use a real recorded-time commit instant, not the open-interval sentinel."
35
- }
36
- );
37
- }
38
- }
39
- function asRecordedInstant(value) {
40
- assertValidRecordedInstant(value, "asRecordedInstant");
41
- return value;
42
- }
43
- function resolveReadCoordinate(mode, asOf, suggestion) {
44
- if (mode !== "asOf" && asOf !== void 0) {
45
- throw new ValidationError(
46
- `Temporal mode "${mode}" does not take an asOf timestamp.`,
47
- {
48
- issues: [
49
- {
50
- path: "asOf",
51
- message: `asOf is only valid with mode "asOf", not "${mode}"`
52
- }
53
- ]
54
- },
55
- {
56
- suggestion: 'Pin an instant with store.asOf(timestamp) or store.view({ mode: "asOf", asOf }); drop asOf for other modes.'
57
- }
58
- );
59
- }
60
- assertValidAsOf(
61
- mode,
62
- asOf,
63
- suggestion === void 0 ? void 0 : { suggestion }
64
- );
65
- return { valid: asOf === void 0 ? { mode } : { mode, asOf } };
66
- }
67
- function withRecordedCoordinate(coordinate, recordedAsOf) {
68
- assertValidRecordedInstant(recordedAsOf, "asOfRecorded");
69
- return {
70
- ...coordinate,
71
- recorded: { asOf: recordedAsOf }
72
- };
73
- }
74
- function assertValidAsOf(mode, asOf, options) {
75
- if (mode === "asOf" && asOf === void 0) {
76
- throw new ValidationError(
77
- 'Temporal mode "asOf" requires a timestamp',
78
- {
79
- issues: [
80
- { path: "asOf", message: "Timestamp is required for asOf mode" }
81
- ]
82
- },
83
- {
84
- suggestion: options?.suggestion ?? 'Provide an ISO-8601 timestamp for asOf mode, e.g. "2026-01-01T00:00:00.000Z".'
85
- }
86
- );
87
- }
88
- if (asOf !== void 0) {
89
- validateCanonicalIsoDate(asOf, "asOf");
90
- }
91
- }
92
- function describeCoordinate(coordinate) {
93
- const { mode, asOf } = coordinate.valid;
94
- const valid = asOf === void 0 ? `mode "${mode}"` : `mode "${mode}" asOf ${asOf}`;
95
- return coordinate.recorded === void 0 ? valid : `${valid}, recorded asOf ${coordinate.recorded.asOf}`;
96
- }
97
- function coordinateContext(coordinate) {
98
- const { mode, asOf } = coordinate.valid;
99
- return {
100
- temporalMode: mode,
101
- ...asOf !== void 0 && { asOf },
102
- ...coordinate.recorded === void 0 ? {} : { recordedAsOf: coordinate.recorded.asOf }
103
- };
104
- }
105
-
106
- // src/query/sql-intent.ts
107
- function asCompiledRowsSql(query) {
108
- return query;
109
- }
110
- function asCompiledSelectSql(query) {
111
- return asCompiledRowsSql(query);
112
- }
113
- function asCompiledStatementSql(query) {
114
- return query;
115
- }
116
- var SqlSchemaDescriptor = class {
117
- // Private field gives SqlSchema nominal identity; runtime checks use instanceof.
118
- #brand = true;
119
- tables;
120
- nodesTable;
121
- edgesTable;
122
- recordedNodesTable;
123
- recordedEdgesTable;
124
- recordedClockTable;
125
- fulltextTable;
126
- constructor(fields) {
127
- this.tables = fields.tables;
128
- this.nodesTable = fields.nodesTable;
129
- this.edgesTable = fields.edgesTable;
130
- this.recordedNodesTable = fields.recordedNodesTable;
131
- this.recordedEdgesTable = fields.recordedEdgesTable;
132
- this.recordedClockTable = fields.recordedClockTable;
133
- this.fulltextTable = fields.fulltextTable;
134
- void this.#brand;
135
- Object.freeze(this);
136
- }
137
- };
138
- var DEFAULT_TABLE_NAMES = {
139
- nodes: "typegraph_nodes",
140
- edges: "typegraph_edges",
141
- recordedNodes: "typegraph_recorded_nodes",
142
- recordedEdges: "typegraph_recorded_edges",
143
- recordedClock: "typegraph_recorded_clock",
144
- fulltext: "typegraph_node_fulltext",
145
- uniques: "typegraph_node_uniques"
146
- };
147
- function resolveTableNames(names) {
148
- return {
149
- nodes: names.nodes ?? DEFAULT_TABLE_NAMES.nodes,
150
- edges: names.edges ?? DEFAULT_TABLE_NAMES.edges,
151
- recordedNodes: names.recordedNodes ?? DEFAULT_TABLE_NAMES.recordedNodes,
152
- recordedEdges: names.recordedEdges ?? DEFAULT_TABLE_NAMES.recordedEdges,
153
- recordedClock: names.recordedClock ?? DEFAULT_TABLE_NAMES.recordedClock,
154
- fulltext: names.fulltext ?? DEFAULT_TABLE_NAMES.fulltext,
155
- uniques: names.uniques ?? DEFAULT_TABLE_NAMES.uniques
156
- };
157
- }
158
- var VALID_IDENTIFIER_PATTERN = /^[a-z_][a-z0-9_$]*$/i;
159
- function validateTableName(name, label) {
160
- if (!name || name.length === 0) {
161
- throw new ConfigurationError(`${label} table name cannot be empty`);
162
- }
163
- if (name.length > MAX_PG_IDENTIFIER_LENGTH) {
164
- throw new ConfigurationError(
165
- `${label} table name exceeds maximum length of ${MAX_PG_IDENTIFIER_LENGTH} characters`
166
- );
167
- }
168
- if (!VALID_IDENTIFIER_PATTERN.test(name)) {
169
- throw new ConfigurationError(
170
- `${label} table name "${name}" is not a valid SQL identifier. Table names must start with a letter or underscore and contain only letters, digits, underscores, or dollar signs.`
171
- );
172
- }
173
- }
174
- function quoteIdentifier2(name) {
175
- return `"${name.replaceAll('"', '""')}"`;
176
- }
177
- function freezeSqlSchema(fields) {
178
- return new SqlSchemaDescriptor(fields);
179
- }
180
- function isSqlSchema(schema) {
181
- return schema instanceof SqlSchemaDescriptor && Object.isFrozen(schema);
182
- }
183
- function requireSqlSchema(schema, surface = "SqlSchema") {
184
- if (isSqlSchema(schema)) return schema;
185
- throw new ConfigurationError(
186
- `${surface} must be created with createSqlSchema(...).`,
187
- { code: "INVALID_SQL_SCHEMA", surface },
188
- {
189
- suggestion: "Pass the result of createSqlSchema(...) instead of a plain schema-shaped object."
190
- }
191
- );
192
- }
193
- function createSqlSchema(names = {}) {
194
- const tables = resolveTableNames(names);
195
- validateTableName(tables.nodes, "nodes");
196
- validateTableName(tables.edges, "edges");
197
- validateTableName(tables.recordedNodes, "recordedNodes");
198
- validateTableName(tables.recordedEdges, "recordedEdges");
199
- validateTableName(tables.recordedClock, "recordedClock");
200
- validateTableName(tables.fulltext, "fulltext");
201
- validateTableName(tables.uniques, "uniques");
202
- return freezeSqlSchema({
203
- tables: Object.freeze(tables),
204
- nodesTable: sql.raw(quoteIdentifier2(tables.nodes)),
205
- edgesTable: sql.raw(quoteIdentifier2(tables.edges)),
206
- recordedNodesTable: sql.raw(quoteIdentifier2(tables.recordedNodes)),
207
- recordedEdgesTable: sql.raw(quoteIdentifier2(tables.recordedEdges)),
208
- recordedClockTable: sql.raw(quoteIdentifier2(tables.recordedClock)),
209
- fulltextTable: sql.raw(quoteIdentifier2(tables.fulltext))
210
- });
211
- }
212
- var EXTERNAL_RECORDED_READ_SOURCE = /* @__PURE__ */ Symbol(
213
- "ExternalRecordedReadSource"
214
- );
215
- var TYPEGRAPH_RECORDED_READ_SOURCE = /* @__PURE__ */ Symbol(
216
- "TypeGraphRecordedReadSource"
217
- );
218
- function recordedRelation(options) {
219
- const schema = requireSqlSchema(options.schema, "recordedRelation schema");
220
- return Object.freeze({
221
- source: "external",
222
- schema,
223
- [EXTERNAL_RECORDED_READ_SOURCE]: true
224
- });
225
- }
226
- function requireExternalRecordedReadSource(source) {
227
- if (source === void 0) return void 0;
228
- if (isExternalRecordedReadSource(source)) return source;
229
- throw new ConfigurationError(
230
- "recordedRead must be created with recordedRelation({ schema }).",
231
- { code: "INVALID_RECORDED_READ_SOURCE" },
232
- {
233
- suggestion: "Pass { recordedRead: recordedRelation({ schema }) } for an externally populated recorded relation. Use { history: true } when TypeGraph should capture writes."
234
- }
235
- );
236
- }
237
- function isExternalRecordedReadSource(source) {
238
- if (typeof source !== "object" || source === null) return false;
239
- const candidate = source;
240
- return candidate.source === "external" && candidate[EXTERNAL_RECORDED_READ_SOURCE] === true && isSqlSchema(candidate.schema) && Object.isFrozen(candidate);
241
- }
242
- function createRecordedReadBinding(schema) {
243
- const readSchema = requireSqlSchema(schema, "recorded read binding schema");
244
- return Object.freeze({
245
- source: "typegraph-capture",
246
- schema: readSchema,
247
- [TYPEGRAPH_RECORDED_READ_SOURCE]: true
248
- });
249
- }
250
- function requireRecordedReadBinding(binding, surface) {
251
- if (isRecordedReadBinding(binding)) return binding;
252
- if (binding !== void 0) {
253
- throw new ConfigurationError(
254
- "Recorded-time reads require a recorded read relation created by TypeGraph.",
255
- { code: "INVALID_RECORDED_READ_BINDING", surface },
256
- {
257
- suggestion: "Use createStore(graph, backend, { history: true }) or { recordedRead: recordedRelation({ schema }) } instead of passing a plain object."
258
- }
259
- );
260
- }
261
- throw new ConfigurationError(
262
- "Recorded-time reads require a recorded read relation.",
263
- { code: "RECORDED_READ_REQUIRES_BINDING", surface },
264
- {
265
- suggestion: "Create the store with createStore(graph, backend, { history: true }) to bind TypeGraph's built-in captured relation, or pass { recordedRead: recordedRelation({ schema }) } for an externally populated recorded relation."
266
- }
267
- );
268
- }
269
- function isTypeGraphRecordedReadSource(source) {
270
- if (typeof source !== "object" || source === null) return false;
271
- const candidate = source;
272
- return candidate.source === "typegraph-capture" && candidate[TYPEGRAPH_RECORDED_READ_SOURCE] === true && isSqlSchema(candidate.schema) && Object.isFrozen(candidate);
273
- }
274
- function isRecordedReadBinding(binding) {
275
- return isExternalRecordedReadSource(binding) || isTypeGraphRecordedReadSource(binding);
276
- }
277
- function recordedReadSqlSchema(binding) {
278
- const { schema } = requireRecordedReadBinding(
279
- binding,
280
- "recorded-read-schema"
281
- );
282
- return freezeSqlSchema({
283
- tables: schema.tables,
284
- nodesTable: schema.recordedNodesTable,
285
- edgesTable: schema.recordedEdgesTable,
286
- recordedNodesTable: schema.recordedNodesTable,
287
- recordedEdgesTable: schema.recordedEdgesTable,
288
- recordedClockTable: schema.recordedClockTable,
289
- fulltextTable: schema.fulltextTable
290
- });
291
- }
292
- function recordedReadSchemaFor(schema, recordedAsOf, binding, surface) {
293
- const baseSchema = requireSqlSchema(schema, `${surface} schema`);
294
- if (recordedAsOf === void 0) return baseSchema;
295
- return recordedReadSqlSchema(requireRecordedReadBinding(binding, surface));
296
- }
297
- var DEFAULT_SQL_SCHEMA = createSqlSchema();
298
- function vectorSlotKey(nodeKind, fieldPath) {
299
- return `${nodeKind}\0${fieldPath}`;
300
- }
301
- var ALIAS_CTE_PREFIX = "cte_";
302
- var EMBEDDINGS_CTE_ALIAS = "cte_embeddings";
303
- var FULLTEXT_CTE_ALIAS = "cte_fulltext";
304
- var HYBRID_CANDIDATES_CTE_ALIAS = "cte_relevance_candidates";
22
+ import { Placeholder, sql } from 'drizzle-orm';
305
23
 
306
24
  // src/store/collection-surface.ts
307
25
  var CURRENT_ONLY_READ_NAMES = [
@@ -4632,7 +4350,7 @@ function compileStandardQueryWithCteStrategy(ast, graphId, ctx) {
4632
4350
  ...vectorPredicate === void 0 ? {} : { vectorPredicate },
4633
4351
  ...fulltextPredicate === void 0 ? {} : { fulltextPredicate }
4634
4352
  });
4635
- const groupBy2 = buildStandardGroupBy({ ast, dialect });
4353
+ const groupBy = buildStandardGroupBy({ ast, dialect });
4636
4354
  const having2 = buildStandardHaving({ ast, ctx });
4637
4355
  const orderBy = selectStandardOrderBy({
4638
4356
  ast,
@@ -4649,7 +4367,7 @@ function compileStandardQueryWithCteStrategy(ast, graphId, ctx) {
4649
4367
  return emitStandardQuerySql({
4650
4368
  ctes,
4651
4369
  fromClause,
4652
- ...groupBy2 === void 0 ? {} : { groupBy: groupBy2 },
4370
+ ...groupBy === void 0 ? {} : { groupBy },
4653
4371
  ...having2 === void 0 ? {} : { having: having2 },
4654
4372
  ...orderBy === void 0 ? {} : { orderBy },
4655
4373
  ...limitOffset === void 0 ? {} : { limitOffset },
@@ -4657,9 +4375,6 @@ function compileStandardQueryWithCteStrategy(ast, graphId, ctx) {
4657
4375
  projection
4658
4376
  });
4659
4377
  }
4660
- function generateId() {
4661
- return nanoid();
4662
- }
4663
4378
 
4664
4379
  // src/utils/path.ts
4665
4380
  function parseSqlitePath(path) {
@@ -5249,9 +4964,9 @@ function buildSelectiveFields(accessedFields, options) {
5249
4964
  valueType: typeInfo?.valueType
5250
4965
  };
5251
4966
  }).toSorted((a, b) => {
5252
- const aliasCompare = a.alias.localeCompare(b.alias);
4967
+ const aliasCompare = compareStrings(a.alias, b.alias);
5253
4968
  if (aliasCompare !== 0) return aliasCompare;
5254
- return a.field.localeCompare(b.field);
4969
+ return compareStrings(a.field, b.field);
5255
4970
  });
5256
4971
  }
5257
4972
  function buildAliasKindMap(state) {
@@ -5471,7 +5186,7 @@ function buildAliasPlans(state, selectiveFields, schemaIntrospector) {
5471
5186
  systemOutputNames: new Set(systemFields.map((f) => f.outputName))
5472
5187
  });
5473
5188
  }
5474
- return plans.toSorted((a, b) => a.alias.localeCompare(b.alias));
5189
+ return plans.toSorted((a, b) => compareStrings(a.alias, b.alias));
5475
5190
  }
5476
5191
  function buildSelectiveContext(row, plans, traversals) {
5477
5192
  const context = {};
@@ -6896,9 +6611,9 @@ var ExecutableQuery = class _ExecutableQuery {
6896
6611
  add(traversal.edgeAlias);
6897
6612
  }
6898
6613
  return result.toSorted((a, b) => {
6899
- const aliasCompare = a.alias.localeCompare(b.alias);
6614
+ const aliasCompare = compareStrings(a.alias, b.alias);
6900
6615
  if (aliasCompare !== 0) return aliasCompare;
6901
- return a.field.localeCompare(b.field);
6616
+ return compareStrings(a.field, b.field);
6902
6617
  });
6903
6618
  }
6904
6619
  async #tryOptimizedPaginate(cursorData, direction, pageLimit, fetchLimit, cursor, isBackward) {
@@ -6979,7 +6694,7 @@ var ExecutableQuery = class _ExecutableQuery {
6979
6694
  };
6980
6695
  }
6981
6696
  #recordOrderByFieldsForPagination(tracker) {
6982
- for (const spec of this.#state.orderBy) {
6697
+ for (const spec of this.#paginationOrderBy()) {
6983
6698
  const field2 = spec.field;
6984
6699
  if (field2.path.length === 1 && field2.path[0] !== "props" && field2.jsonPointer === void 0) {
6985
6700
  tracker.record(field2.alias, field2.path[0], true);
@@ -7004,7 +6719,7 @@ var ExecutableQuery = class _ExecutableQuery {
7004
6719
  row,
7005
6720
  selectiveFields
7006
6721
  );
7007
- return buildCursorFromRow(contextRow, this.#state.orderBy, direction);
6722
+ return buildCursorFromRow(contextRow, this.#paginationOrderBy(), direction);
7008
6723
  }
7009
6724
  #buildCursorContextFromSelectiveRow(row, selectiveFields) {
7010
6725
  const outputNameByAliasField = /* @__PURE__ */ new Map();
@@ -7021,7 +6736,7 @@ var ExecutableQuery = class _ExecutableQuery {
7021
6736
  }
7022
6737
  }
7023
6738
  const cursorContext = {};
7024
- for (const spec of this.#state.orderBy) {
6739
+ for (const spec of this.#paginationOrderBy()) {
7025
6740
  const alias = spec.field.alias;
7026
6741
  const jsonPointer2 = spec.field.jsonPointer;
7027
6742
  if (jsonPointer2 === void 0) {
@@ -7102,6 +6817,32 @@ var ExecutableQuery = class _ExecutableQuery {
7102
6817
  }
7103
6818
  return this.#state.traversals.find((t) => t.nodeAlias === alias)?.nodeKinds;
7104
6819
  }
6820
+ /**
6821
+ * The ORDER BY used for keyset pagination: the caller's ORDER BY plus a final
6822
+ * unique tiebreaker on the start alias's `id`. Without a unique final key, a
6823
+ * non-unique sort (e.g. `orderBy("p", "age")` with many equal ages) makes the
6824
+ * keyset predicate `age > lastAge` skip every not-yet-returned equal-age row,
6825
+ * silently losing data across pages. The start node's `id` is unique and
6826
+ * always projected, so appending it gives a total order that page boundaries
6827
+ * can split cleanly. Skipped when the caller already orders by the start
6828
+ * alias's `id` (the sort is already total). Every pagination read of ORDER BY
6829
+ * — the emitted sort, the cursor predicate, cursor encoding, and cursor
6830
+ * validation — routes through here so the extra column stays consistent across
6831
+ * the standard and selective-field-optimized paths.
6832
+ */
6833
+ #paginationOrderBy() {
6834
+ const orderBy = this.#state.orderBy;
6835
+ const startAlias = this.#state.startAlias;
6836
+ const alreadyTotal = orderBy.some(
6837
+ (spec) => spec.field.alias === startAlias && spec.field.path.length === 1 && spec.field.path[0] === "id" && spec.field.jsonPointer === void 0
6838
+ );
6839
+ if (alreadyTotal) return orderBy;
6840
+ const tiebreaker = {
6841
+ field: { __type: "field_ref", alias: startAlias, path: ["id"] },
6842
+ direction: "asc"
6843
+ };
6844
+ return [...orderBy, tiebreaker];
6845
+ }
7105
6846
  /**
7106
6847
  * Executes a paginated query using cursor-based keyset pagination.
7107
6848
  *
@@ -7140,7 +6881,7 @@ var ExecutableQuery = class _ExecutableQuery {
7140
6881
  let cursorData;
7141
6882
  if (cursor) {
7142
6883
  cursorData = decodeCursor(cursor);
7143
- validateCursorColumns(cursorData, this.#state.orderBy);
6884
+ validateCursorColumns(cursorData, this.#paginationOrderBy());
7144
6885
  }
7145
6886
  const fetchLimit = limit + 1;
7146
6887
  const direction = isBackward ? "backward" : "forward";
@@ -7172,7 +6913,7 @@ var ExecutableQuery = class _ExecutableQuery {
7172
6913
  return buildPaginatedResult(
7173
6914
  data,
7174
6915
  orderedRows,
7175
- this.#state.orderBy,
6916
+ this.#paginationOrderBy(),
7176
6917
  limit,
7177
6918
  hasMore,
7178
6919
  isBackward,
@@ -7218,7 +6959,7 @@ var ExecutableQuery = class _ExecutableQuery {
7218
6959
  */
7219
6960
  async #executeWithCursor(cursorData, direction, limit, options) {
7220
6961
  const ast = this.toAst();
7221
- let orderBy = this.#state.orderBy;
6962
+ let orderBy = this.#paginationOrderBy();
7222
6963
  if (direction === "backward") {
7223
6964
  orderBy = orderBy.map((spec) => ({
7224
6965
  ...spec,
@@ -7229,7 +6970,7 @@ var ExecutableQuery = class _ExecutableQuery {
7229
6970
  if (cursorData) {
7230
6971
  const cursorPredicate = buildCursorPredicate(
7231
6972
  cursorData,
7232
- this.#state.orderBy,
6973
+ this.#paginationOrderBy(),
7233
6974
  direction,
7234
6975
  this.#state.startAlias
7235
6976
  );
@@ -8947,101 +8688,6 @@ function createQueryBuilderWithContext(graphId, registry, options) {
8947
8688
  return new QueryBuilder(config, initialState);
8948
8689
  }
8949
8690
 
8950
- // src/store/fulltext-sync.ts
8951
- var searchableFieldsCache = /* @__PURE__ */ new WeakMap();
8952
- function getSearchableFields(schema) {
8953
- const cached = searchableFieldsCache.get(schema);
8954
- if (cached) return cached;
8955
- const fields = computeSearchableFields(schema);
8956
- searchableFieldsCache.set(schema, fields);
8957
- return fields;
8958
- }
8959
- function computeSearchableFields(schema) {
8960
- if (schema.type !== "object") return [];
8961
- const def = schema.def;
8962
- const shape = def.shape;
8963
- if (!shape) return [];
8964
- const fields = [];
8965
- for (const [fieldPath, fieldSchema] of Object.entries(shape)) {
8966
- const metadata = getSearchableMetadata(fieldSchema);
8967
- if (metadata !== void 0) {
8968
- fields.push({ fieldPath, metadata });
8969
- }
8970
- }
8971
- warnIfConflictingLanguages(fields);
8972
- return fields;
8973
- }
8974
- function warnIfConflictingLanguages(fields) {
8975
- if (fields.length < 2) return;
8976
- const languages = new Set(fields.map((field2) => field2.metadata.language));
8977
- if (languages.size < 2) return;
8978
- if (typeof console === "undefined" || typeof console.warn !== "function") {
8979
- return;
8980
- }
8981
- const fieldSummary = fields.map((field2) => `${field2.fieldPath}=${field2.metadata.language}`).join(", ");
8982
- const winning = fields[0]?.metadata.language ?? DEFAULT_SEARCHABLE_LANGUAGE;
8983
- console.warn(
8984
- `[typegraph] searchable() fields declare conflicting languages (${fieldSummary}). The first field's language ("${winning}") is recorded on the fulltext row; true multilingual indexing requires a dedicated node kind per language.`
8985
- );
8986
- }
8987
- var FIELD_SEPARATOR = "\n";
8988
- function resolveCombinedLanguage(fields) {
8989
- return fields[0]?.metadata.language ?? DEFAULT_SEARCHABLE_LANGUAGE;
8990
- }
8991
- function computeFulltextContent(schema, props) {
8992
- const searchableFields = getSearchableFields(schema);
8993
- if (searchableFields.length === 0) return void 0;
8994
- const parts = [];
8995
- for (const field2 of searchableFields) {
8996
- const value = props[field2.fieldPath];
8997
- if (typeof value === "string" && value.length > 0) {
8998
- parts.push(value);
8999
- }
9000
- }
9001
- if (parts.length === 0) return void 0;
9002
- return {
9003
- content: parts.join(FIELD_SEPARATOR),
9004
- language: resolveCombinedLanguage(searchableFields)
9005
- };
9006
- }
9007
- async function syncFulltext(ctx, schema, props) {
9008
- const { backend } = ctx;
9009
- if (!backend.upsertFulltext || !backend.deleteFulltext) {
9010
- return;
9011
- }
9012
- const computed = computeFulltextContent(schema, props);
9013
- if (computed !== void 0) {
9014
- await backend.upsertFulltext({
9015
- graphId: ctx.graphId,
9016
- nodeKind: ctx.nodeKind,
9017
- nodeId: ctx.nodeId,
9018
- content: computed.content,
9019
- language: computed.language
9020
- });
9021
- return;
9022
- }
9023
- if (getSearchableFields(schema).length === 0) return;
9024
- await backend.deleteFulltext({
9025
- graphId: ctx.graphId,
9026
- nodeKind: ctx.nodeKind,
9027
- nodeId: ctx.nodeId
9028
- });
9029
- }
9030
- async function deleteNodeFulltext(ctx, schema) {
9031
- const { backend } = ctx;
9032
- if (!backend.deleteFulltext) {
9033
- return;
9034
- }
9035
- if (getSearchableFields(schema).length === 0) {
9036
- return;
9037
- }
9038
- await backend.deleteFulltext({
9039
- graphId: ctx.graphId,
9040
- nodeKind: ctx.nodeKind,
9041
- nodeId: ctx.nodeId
9042
- });
9043
- }
9044
-
9045
8691
  // src/store/fulltext-rebuild.ts
9046
8692
  var DEFAULT_PAGE_SIZE = 500;
9047
8693
  var DEFAULT_MAX_SKIPPED_IDS = 1e4;
@@ -9215,78 +8861,6 @@ function processPage(schema, rows) {
9215
8861
  return { toUpsert, toDelete, skipped, skippedIds };
9216
8862
  }
9217
8863
 
9218
- // src/store/embedding-sync.ts
9219
- var embeddingFieldsCache = /* @__PURE__ */ new WeakMap();
9220
- function getEmbeddingFields(schema) {
9221
- const cached = embeddingFieldsCache.get(schema);
9222
- if (cached) return cached;
9223
- const fields = resolveEmbeddingFields(schema);
9224
- embeddingFieldsCache.set(schema, fields);
9225
- return fields;
9226
- }
9227
- async function syncEmbeddings(ctx, schema, props) {
9228
- const { backend } = ctx;
9229
- if (!backend.upsertEmbedding || !backend.deleteEmbedding) {
9230
- return;
9231
- }
9232
- const embeddingFields = getEmbeddingFields(schema);
9233
- if (embeddingFields.length === 0) {
9234
- return;
9235
- }
9236
- for (const field2 of embeddingFields) {
9237
- const value = props[field2.fieldPath];
9238
- if (isValidEmbeddingValue(value)) {
9239
- await backend.upsertEmbedding({
9240
- graphId: ctx.graphId,
9241
- nodeKind: ctx.nodeKind,
9242
- nodeId: ctx.nodeId,
9243
- fieldPath: field2.fieldPath,
9244
- embedding: value,
9245
- dimensions: field2.dimensions,
9246
- metric: field2.metric,
9247
- indexType: field2.indexType
9248
- });
9249
- } else if (value === void 0) {
9250
- await backend.deleteEmbedding({
9251
- graphId: ctx.graphId,
9252
- nodeKind: ctx.nodeKind,
9253
- nodeId: ctx.nodeId,
9254
- fieldPath: field2.fieldPath,
9255
- dimensions: field2.dimensions,
9256
- metric: field2.metric,
9257
- indexType: field2.indexType
9258
- });
9259
- }
9260
- }
9261
- }
9262
- async function deleteNodeEmbeddings(ctx, schema) {
9263
- const { backend } = ctx;
9264
- if (!backend.deleteEmbedding) {
9265
- return;
9266
- }
9267
- const embeddingFields = getEmbeddingFields(schema);
9268
- if (embeddingFields.length === 0) {
9269
- return;
9270
- }
9271
- for (const field2 of embeddingFields) {
9272
- await backend.deleteEmbedding({
9273
- graphId: ctx.graphId,
9274
- nodeKind: ctx.nodeKind,
9275
- nodeId: ctx.nodeId,
9276
- fieldPath: field2.fieldPath,
9277
- dimensions: field2.dimensions,
9278
- metric: field2.metric,
9279
- indexType: field2.indexType
9280
- });
9281
- }
9282
- }
9283
- function isValidEmbeddingValue(value) {
9284
- if (!Array.isArray(value)) {
9285
- return false;
9286
- }
9287
- return value.every((n) => typeof n === "number" && Number.isFinite(n));
9288
- }
9289
-
9290
8864
  // src/store/search.ts
9291
8865
  function resolveSearchSlot(ctx, nodeKind, fieldPath) {
9292
8866
  const nodeType = ctx.registry.getNodeType(nodeKind);
@@ -9512,7 +9086,7 @@ async function executeHybridSearch(ctx, nodeKind, options) {
9512
9086
  fused.set(row.nodeId, entry2);
9513
9087
  }
9514
9088
  const ranked = [...fused.values()].toSorted(
9515
- (a, b) => b.fusedScore - a.fusedScore || a.nodeId.localeCompare(b.nodeId)
9089
+ (a, b) => b.fusedScore - a.fusedScore || compareStrings(a.nodeId, b.nodeId)
9516
9090
  ).slice(0, options.limit);
9517
9091
  const ids = ranked.map((entry2) => entry2.nodeId);
9518
9092
  const nodeMap = await fetchNodesByIds(backend, graphId, nodeKind, ids);
@@ -10631,33 +10205,6 @@ function createGraphAlgorithms(params) {
10631
10205
  };
10632
10206
  }
10633
10207
 
10634
- // src/store/row-fetch.ts
10635
- async function getRowsByIds(ids, policy) {
10636
- const rowsById = /* @__PURE__ */ new Map();
10637
- if (ids.length === 0) return rowsById;
10638
- if (policy.batch !== void 0) {
10639
- const rows2 = await policy.batch(ids);
10640
- for (const row of rows2) {
10641
- rowsById.set(row.id, row);
10642
- }
10643
- return rowsById;
10644
- }
10645
- const uniqueIds = [...new Set(ids)];
10646
- const rows = await Promise.all(uniqueIds.map((id) => policy.one(id)));
10647
- for (const row of rows) {
10648
- if (row !== void 0) rowsById.set(row.id, row);
10649
- }
10650
- return rowsById;
10651
- }
10652
-
10653
- // src/store/edge-fetch.ts
10654
- async function getEdgeRowsByIds(backend, graphId, ids) {
10655
- return getRowsByIds(ids, {
10656
- batch: backend.getEdges === void 0 ? void 0 : (batchIds) => backend.getEdges(graphId, batchIds),
10657
- one: (id) => backend.getEdge(graphId, id)
10658
- });
10659
- }
10660
-
10661
10208
  // src/store/collections/edge-collection.ts
10662
10209
  function narrowEdge(edge) {
10663
10210
  return edge;
@@ -10989,14 +10536,6 @@ function createEdgeCollection(config) {
10989
10536
  };
10990
10537
  }
10991
10538
 
10992
- // src/store/node-fetch.ts
10993
- async function getNodeRowsByIds(backend, graphId, kind, ids) {
10994
- return getRowsByIds(ids, {
10995
- batch: backend.getNodes === void 0 ? void 0 : (batchIds) => backend.getNodes(graphId, kind, batchIds),
10996
- one: (id) => backend.getNode(graphId, kind, id)
10997
- });
10998
- }
10999
-
11000
10539
  // src/store/collections/node-collection.ts
11001
10540
  function narrowNode(node) {
11002
10541
  return node;
@@ -11269,20 +10808,14 @@ function createNodeCollection(config) {
11269
10808
  return results.map((bucket) => narrowNodes(bucket));
11270
10809
  },
11271
10810
  async getOrCreateByConstraint(constraintName, props, options) {
11272
- const execute = async (target) => {
11273
- const result = await executeGetOrCreateByConstraint(
11274
- kind,
11275
- constraintName,
11276
- props,
11277
- target,
11278
- options
11279
- );
11280
- return result;
11281
- };
11282
- if (backend.capabilities.transactions && "transaction" in backend) {
11283
- return backend.transaction(async (txBackend) => execute(txBackend));
11284
- }
11285
- return execute(backend);
10811
+ const result = await executeGetOrCreateByConstraint(
10812
+ kind,
10813
+ constraintName,
10814
+ props,
10815
+ backend,
10816
+ options
10817
+ );
10818
+ return result;
11286
10819
  },
11287
10820
  async bulkGetOrCreateByConstraint(constraintName, items, options) {
11288
10821
  if (items.length === 0) return [];
@@ -11734,990 +11267,25 @@ async function computeIndexSignature(dialect, targetTableName, declaration) {
11734
11267
  const json = JSON.stringify(hashable, sortedReplacer);
11735
11268
  return sha256Hex(json, 16);
11736
11269
  }
11737
-
11738
- // src/store/insert-dispatch.ts
11739
- function nodeInsertDispatch(backend) {
11740
- return {
11741
- one: (params) => backend.insertNode(params),
11742
- oneNoReturn: backend.insertNodeNoReturn === void 0 ? void 0 : (params) => backend.insertNodeNoReturn(params),
11743
- batch: backend.insertNodesBatch === void 0 ? void 0 : (params) => backend.insertNodesBatch(params),
11744
- batchReturning: backend.insertNodesBatchReturning === void 0 ? void 0 : (params) => backend.insertNodesBatchReturning(params)
11745
- };
11746
- }
11747
- function edgeInsertDispatch(backend) {
11748
- return {
11749
- one: (params) => backend.insertEdge(params),
11750
- oneNoReturn: backend.insertEdgeNoReturn === void 0 ? void 0 : (params) => backend.insertEdgeNoReturn(params),
11751
- batch: backend.insertEdgesBatch === void 0 ? void 0 : (params) => backend.insertEdgesBatch(params),
11752
- batchReturning: backend.insertEdgesBatchReturning === void 0 ? void 0 : (params) => backend.insertEdgesBatchReturning(params)
11753
- };
11754
- }
11755
- async function runInsertNoReturn(dispatch, params) {
11756
- if (dispatch.oneNoReturn !== void 0) {
11757
- await dispatch.oneNoReturn(params);
11758
- return;
11759
- }
11760
- await dispatch.one(params);
11761
- }
11762
- async function runInsertBatch(dispatch, params) {
11763
- if (params.length === 0) return;
11764
- if (dispatch.batch !== void 0) {
11765
- await dispatch.batch(params);
11766
- return;
11767
- }
11768
- for (const insertParams of params) {
11769
- await runInsertNoReturn(dispatch, insertParams);
11770
- }
11771
- }
11772
- async function runInsertBatchReturning(dispatch, params) {
11773
- if (dispatch.batchReturning !== void 0) {
11774
- return dispatch.batchReturning(params);
11775
- }
11776
- const rows = [];
11777
- for (const insertParams of params) {
11778
- rows.push(await dispatch.one(insertParams));
11779
- }
11780
- return rows;
11781
- }
11782
- function requireRecordedSchema(target) {
11783
- if (target.tableNames === void 0) {
11784
- throw new ConfigurationError(
11785
- "Recorded-time capture requires the backend to expose tableNames.",
11786
- { dialect: target.dialect },
11787
- {
11788
- suggestion: "Use a built-in SQLite/PostgreSQL backend, or set tableNames on the custom backend so recorded-time capture targets the configured relations."
11789
- }
11790
- );
11791
- }
11792
- return createSqlSchema(target.tableNames);
11270
+ var ENTITY_KINDS = ["node", "edge"];
11271
+ function buildPendingKindRemoval(args) {
11272
+ return { ...args, removedAt: void 0, error: void 0 };
11793
11273
  }
11794
- function requireCaptureStatements(target) {
11795
- if (target.executeStatement === void 0) {
11274
+ async function materializeRemovals(context, options = {}) {
11275
+ const { backend, graphId } = context;
11276
+ if (backend.executeDdl === void 0 || backend.recordKindRemoval === void 0 || backend.getPendingKindRemovals === void 0) {
11796
11277
  throw new ConfigurationError(
11797
- "Recorded-time capture requires backend.executeStatement support on the transaction backend.",
11798
- { dialect: target.dialect },
11799
- {
11800
- suggestion: "Use the built-in SQLite/PostgreSQL backends or provide executeStatement on the custom backend's transaction target."
11801
- }
11278
+ "store.materializeRemovals() requires a backend with kind-removal primitives (executeDdl, recordKindRemoval, getPendingKindRemovals). The bundled SQLite and Postgres backends provide them.",
11279
+ { code: "MATERIALIZE_REMOVALS_BACKEND_UNSUPPORTED" }
11802
11280
  );
11803
11281
  }
11804
- }
11805
- async function executeStatement(target, query) {
11806
- requireCaptureStatements(target);
11807
- await target.executeStatement(asCompiledStatementSql(query));
11808
- }
11809
- function unsupportedPostgresIsolationError(isolationLevel) {
11810
- return new ConfigurationError(
11811
- "Recorded-time capture on PostgreSQL requires read_committed isolation.",
11812
- { isolationLevel: isolationLevel ?? "unknown" },
11813
- {
11814
- suggestion: "Omit the transaction isolation option or use read_committed. PostgreSQL repeatable_read/serializable snapshots cannot safely allocate the per-graph recorded clock inside the captured transaction."
11815
- }
11816
- );
11817
- }
11818
- function isSupportedRecordedIsolationLevel(isolationLevel) {
11819
- return isolationLevel === void 0 || isolationLevel === "read_committed" || isolationLevel === "read_uncommitted";
11820
- }
11821
- function assertRequestedRecordedIsolation(backend, options) {
11822
- if (backend.dialect !== "postgres") return;
11823
- const isolationLevel = options?.isolationLevel;
11824
- if (isSupportedRecordedIsolationLevel(isolationLevel)) return;
11825
- throw unsupportedPostgresIsolationError(isolationLevel);
11826
- }
11827
- function normalizeIsolationLevel(value) {
11828
- if (typeof value !== "string") return void 0;
11829
- return value.replaceAll(" ", "_").toLowerCase();
11830
- }
11831
- async function assertRecordedCaptureTransactionIsolation(target) {
11832
- if (target.dialect !== "postgres") return;
11833
- const rows = await target.execute(
11834
- asCompiledRowsSql(sql`
11835
- SELECT current_setting('transaction_isolation') AS transaction_isolation
11836
- `)
11837
- );
11838
- const isolationLevel = normalizeIsolationLevel(
11839
- rows[0]?.transaction_isolation
11840
- );
11841
- if (isSupportedRecordedIsolationLevel(isolationLevel)) return;
11842
- throw unsupportedPostgresIsolationError(isolationLevel);
11843
- }
11844
- function recordedCaptureRequiresCallbackTransactionError(details) {
11845
- return new ConfigurationError(
11846
- "withTransaction() has no recorded-time capture flush point when history is enabled.",
11847
- details,
11848
- {
11849
- suggestion: "Use store.withRecordedTransaction(externalTx, async (tx) => ...) so TypeGraph can flush capture before the caller commits."
11850
- }
11851
- );
11852
- }
11853
- function historyUnsafeRawWriteError(surface) {
11854
- return new ConfigurationError(
11855
- `${surface} is not available when history capture is enabled because raw SQL writes bypass recorded-time capture.`,
11856
- { code: "RECORDED_CAPTURE_RAW_SQL_DISABLED" },
11857
- {
11858
- suggestion: "Run graph writes through store.nodes/store.edges (or tx.nodes/tx.edges inside a transaction), or perform raw relational writes outside a history-enabled TypeGraph store."
11859
- }
11860
- );
11861
- }
11862
- function failHistoryUnsafeSqlRef() {
11863
- throw historyUnsafeRawWriteError("tx.sql");
11864
- }
11865
- function createHistoryUnsafeSqlRef() {
11866
- return new Proxy(
11867
- function historyUnsafeSqlRef() {
11868
- return failHistoryUnsafeSqlRef();
11869
- },
11870
- {
11871
- get() {
11872
- return failHistoryUnsafeSqlRef();
11873
- },
11874
- apply() {
11875
- return failHistoryUnsafeSqlRef();
11876
- },
11877
- set() {
11878
- return failHistoryUnsafeSqlRef();
11879
- },
11880
- defineProperty() {
11881
- return failHistoryUnsafeSqlRef();
11882
- },
11883
- deleteProperty() {
11884
- return failHistoryUnsafeSqlRef();
11885
- },
11886
- // Enumeration / inspection traps too, so spreading (`{...tx.sql}`),
11887
- // `Object.keys`, `in`, and prototype reads also fail loudly rather than
11888
- // silently yielding an empty object that reads as "no raw handle".
11889
- has() {
11890
- return failHistoryUnsafeSqlRef();
11891
- },
11892
- ownKeys() {
11893
- return failHistoryUnsafeSqlRef();
11894
- },
11895
- getOwnPropertyDescriptor() {
11896
- return failHistoryUnsafeSqlRef();
11897
- },
11898
- getPrototypeOf() {
11899
- return failHistoryUnsafeSqlRef();
11900
- }
11901
- }
11902
- );
11903
- }
11904
- function rawWriteGuards(target, prefix) {
11905
- return {
11906
- ...target.executeStatement === void 0 ? {} : {
11907
- executeStatement(_query) {
11908
- return Promise.reject(
11909
- historyUnsafeRawWriteError(`${prefix}.executeStatement`)
11910
- );
11911
- }
11912
- },
11913
- ...target.executeDdl === void 0 ? {} : {
11914
- executeDdl(_ddl) {
11915
- return Promise.reject(
11916
- historyUnsafeRawWriteError(`${prefix}.executeDdl`)
11917
- );
11918
- }
11919
- }
11920
- };
11921
- }
11922
- function assertCapturableBackend(backend) {
11923
- if (!backend.capabilities.transactions) {
11924
- throw new ConfigurationError(
11925
- "history: true requires a backend with transaction support.",
11926
- { dialect: backend.dialect },
11927
- {
11928
- suggestion: "Use a transactional SQLite/PostgreSQL backend or disable recorded-time capture for this store."
11929
- }
11930
- );
11931
- }
11932
- if (backend.executeStatement === void 0) {
11933
- throw new ConfigurationError(
11934
- "history: true requires a backend that supports executeStatement.",
11935
- { dialect: backend.dialect },
11936
- {
11937
- suggestion: "Use a built-in SQLite/PostgreSQL backend, or implement executeStatement on the custom backend so recorded-time capture can write history rows."
11938
- }
11939
- );
11940
- }
11941
- if (backend.capabilities.returning === false) {
11942
- throw new ConfigurationError(
11943
- "history: true requires a backend that supports UPDATE \u2026 RETURNING.",
11944
- { dialect: backend.dialect },
11945
- {
11946
- suggestion: "Use a built-in SQLite/PostgreSQL backend, or run recorded-time capture on a backend whose engine supports UPDATE \u2026 RETURNING \u2014 capture closes recorded intervals with a RETURNING statement on its hot path."
11947
- }
11948
- );
11949
- }
11950
- if (backend.tableNames === void 0) {
11951
- throw new ConfigurationError(
11952
- "history: true requires a backend that exposes tableNames.",
11953
- { dialect: backend.dialect },
11954
- {
11955
- suggestion: "Use a built-in SQLite/PostgreSQL backend, or set tableNames on the custom backend so recorded-time capture can resolve the recorded relations instead of silently falling back to defaults."
11956
- }
11957
- );
11958
- }
11959
- }
11960
-
11961
- // src/store/recorded-capture/clock.ts
11962
- var RECORDED_MIN = "1970-01-01T00:00:00.000Z";
11963
- var RECORDED_MAX_TIME = new Date(RECORDED_MAX).getTime();
11964
- var RECORDED_CLOCK_ADVISORY_LOCK_NAMESPACE = "typegraph:recorded-clock";
11965
- function recordedClockAdvisoryLockSql(graphId) {
11966
- return sql`
11967
- SELECT pg_advisory_xact_lock(
11968
- hashtext(${RECORDED_CLOCK_ADVISORY_LOCK_NAMESPACE}),
11969
- hashtext(${graphId})
11970
- )
11971
- `;
11972
- }
11973
- function failInvalidClockTimestamp(value) {
11974
- throw new ConfigurationError(
11975
- "Recorded clock row contained an invalid timestamp",
11976
- { value }
11977
- );
11978
- }
11979
- var ZONELESS_DATETIME_PATTERN = /^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?$/;
11980
- var OFFSET_DATETIME_PATTERN = /^(\d{4}-\d{2}-\d{2})[ T](\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?)(Z|[+-]\d{2}(?::?\d{2})?)$/i;
11981
- function normalizeOffsetDesignator(offset) {
11982
- if (offset.toUpperCase() === "Z") return "Z";
11983
- if (/^[+-]\d{2}$/.test(offset)) return `${offset}:00`;
11984
- if (/^[+-]\d{4}$/.test(offset)) {
11985
- return `${offset.slice(0, 3)}:${offset.slice(3)}`;
11986
- }
11987
- return offset;
11988
- }
11989
- function clockTimestampMs(value) {
11990
- if (ZONELESS_DATETIME_PATTERN.test(value)) {
11991
- return Date.parse(`${value.replace(" ", "T")}Z`);
11992
- }
11993
- const offsetMatch = OFFSET_DATETIME_PATTERN.exec(value);
11994
- if (offsetMatch) {
11995
- const [, date, time, offset] = offsetMatch;
11996
- return Date.parse(`${date}T${time}${normalizeOffsetDesignator(offset)}`);
11997
- }
11998
- return Date.parse(value);
11999
- }
12000
- function toCanonicalIso(value) {
12001
- if (value instanceof Date) {
12002
- if (Number.isNaN(value.getTime())) return failInvalidClockTimestamp(value);
12003
- return value.toISOString();
12004
- }
12005
- if (typeof value === "string") {
12006
- const timestamp = clockTimestampMs(value);
12007
- if (Number.isNaN(timestamp)) return failInvalidClockTimestamp(value);
12008
- return new Date(timestamp).toISOString();
12009
- }
12010
- return failInvalidClockTimestamp(value);
12011
- }
12012
- function nextRecordedCommitTime(previous) {
12013
- const wallTime = Date.parse(nowIso());
12014
- return previous === void 0 ? wallTime : Math.max(wallTime, Date.parse(previous) + 1);
12015
- }
12016
- async function readRecordedClock(target, schema, graphId) {
12017
- const rows = await target.execute(
12018
- asCompiledRowsSql(sql`
12019
- SELECT recorded_at
12020
- FROM ${schema.recordedClockTable}
12021
- WHERE graph_id = ${graphId}
12022
- `)
12023
- );
12024
- const first = rows[0];
12025
- return first === void 0 ? void 0 : toCanonicalIso(first.recorded_at);
12026
- }
12027
- async function writeRecordedClock(target, schema, graphId, recordedAt) {
12028
- await executeStatement(
12029
- target,
12030
- sql`
12031
- INSERT INTO ${schema.recordedClockTable} (graph_id, recorded_at)
12032
- VALUES (${graphId}, ${recordedAt})
12033
- ON CONFLICT (graph_id) DO UPDATE SET recorded_at = ${recordedAt}
12034
- `
12035
- );
12036
- }
12037
- async function lockRecordedClock(target, schema, graphId, ownsWriteLock) {
12038
- if (target.dialect === "postgres") {
12039
- await target.execute(
12040
- asCompiledRowsSql(recordedClockAdvisoryLockSql(graphId))
12041
- );
12042
- return;
12043
- }
12044
- if (ownsWriteLock) return;
12045
- await executeStatement(
12046
- target,
12047
- sql`
12048
- INSERT INTO ${schema.recordedClockTable} (graph_id, recorded_at)
12049
- VALUES (${graphId}, ${RECORDED_MIN})
12050
- ON CONFLICT (graph_id) DO UPDATE SET recorded_at = recorded_at
12051
- `
12052
- );
12053
- }
12054
- async function allocateRecordedCommit(target, schema, graphId, ownsWriteLock) {
12055
- await lockRecordedClock(target, schema, graphId, ownsWriteLock);
12056
- const previous = await readRecordedClock(target, schema, graphId);
12057
- const recordedCommitTime = nextRecordedCommitTime(previous);
12058
- if (!Number.isFinite(recordedCommitTime) || recordedCommitTime >= RECORDED_MAX_TIME) {
12059
- throw new ConfigurationError(
12060
- `Recorded commit clock reached the open sentinel ${RECORDED_MAX}`,
12061
- { graphId, recordedCommitTime },
12062
- {
12063
- suggestion: "Use a graph with a normal recorded-time clock; the open sentinel is reserved for relation intervals."
12064
- }
12065
- );
12066
- }
12067
- const recordedCommit = new Date(recordedCommitTime).toISOString();
12068
- await writeRecordedClock(target, schema, graphId, recordedCommit);
12069
- return recordedCommit;
12070
- }
12071
- var RECORDED_HISTORY_META = "{}";
12072
- var RECORDED_NODE_COLUMNS = [
12073
- "history_id",
12074
- "graph_id",
12075
- "kind",
12076
- "id",
12077
- "props",
12078
- "version",
12079
- "valid_from",
12080
- "valid_to",
12081
- "created_at",
12082
- "updated_at",
12083
- "deleted_at",
12084
- "recorded_from",
12085
- "recorded_to",
12086
- "op",
12087
- "schema_version",
12088
- "tx_id",
12089
- "meta"
12090
- ];
12091
- var RECORDED_EDGE_COLUMNS = [
12092
- "history_id",
12093
- "graph_id",
12094
- "id",
12095
- "kind",
12096
- "from_kind",
12097
- "from_id",
12098
- "to_kind",
12099
- "to_id",
12100
- "props",
12101
- "valid_from",
12102
- "valid_to",
12103
- "created_at",
12104
- "updated_at",
12105
- "deleted_at",
12106
- "recorded_from",
12107
- "recorded_to",
12108
- "op",
12109
- "schema_version",
12110
- "tx_id",
12111
- "meta"
12112
- ];
12113
- var RECORDED_NODE_COLUMN_LIST = sql.raw(RECORDED_NODE_COLUMNS.join(", "));
12114
- var RECORDED_EDGE_COLUMN_LIST = sql.raw(RECORDED_EDGE_COLUMNS.join(", "));
12115
- var RECORDED_BIND_PARAM_BUDGET = 900;
12116
- function recordedBindParamBudget(target) {
12117
- const budget = target.capabilities.maxBindParameters ?? RECORDED_BIND_PARAM_BUDGET;
12118
- if (!Number.isSafeInteger(budget) || budget < 1) {
12119
- throw new ConfigurationError(
12120
- `backend capabilities.maxBindParameters must be a positive integer, got: ${String(budget)}`,
12121
- {
12122
- code: "INVALID_BACKEND_CAPABILITY",
12123
- capability: "maxBindParameters",
12124
- value: budget
12125
- }
12126
- );
12127
- }
12128
- return budget;
12129
- }
12130
- function recordedChunkSize(target, columnCount) {
12131
- const budget = recordedBindParamBudget(target);
12132
- return Math.max(1, Math.floor(budget / columnCount));
12133
- }
12134
- function recordedCommonCells() {
12135
- return {
12136
- history_id: () => sql`${generateId()}`,
12137
- graph_id: (row) => sql`${row.graph_id}`,
12138
- id: (row) => sql`${row.id}`,
12139
- kind: (row) => sql`${row.kind}`,
12140
- props: (row) => sql`${row.props}`,
12141
- valid_from: (row) => sql`${sqlNull(row.valid_from)}`,
12142
- valid_to: (row) => sql`${sqlNull(row.valid_to)}`,
12143
- created_at: (row) => sql`${row.created_at}`,
12144
- updated_at: (row) => sql`${row.updated_at}`,
12145
- deleted_at: (row) => sql`${sqlNull(row.deleted_at)}`,
12146
- recorded_from: (_row, recordedCommit) => sql`${recordedCommit}`,
12147
- recorded_to: () => sql`${RECORDED_MAX}`,
12148
- op: (_row, _recordedCommit, operation) => sql`${operation}`,
12149
- schema_version: () => sql`NULL`,
12150
- tx_id: () => sql`NULL`,
12151
- meta: () => sql`${RECORDED_HISTORY_META}`
12152
- };
12153
- }
12154
- var recordedNodeCells = {
12155
- ...recordedCommonCells(),
12156
- version: (row) => sql`${row.version}`
12157
- };
12158
- var recordedEdgeCells = {
12159
- ...recordedCommonCells(),
12160
- from_kind: (row) => sql`${row.from_kind}`,
12161
- from_id: (row) => sql`${row.from_id}`,
12162
- to_kind: (row) => sql`${row.to_kind}`,
12163
- to_id: (row) => sql`${row.to_id}`
12164
- };
12165
- function recordedValuesTuple(columns, cells, row, recordedCommit, operation) {
12166
- const tuple = columns.map(
12167
- (column) => cells[column](row, recordedCommit, operation)
12168
- );
12169
- return sql`(${sql.join(tuple, sql`, `)})`;
12170
- }
12171
- async function insertRecordedRows(target, table, columnList, columns, cells, inserts, recordedCommit) {
12172
- if (inserts.length === 0) return;
12173
- const values = inserts.map(
12174
- (insert) => recordedValuesTuple(
12175
- columns,
12176
- cells,
12177
- insert.row,
12178
- recordedCommit,
12179
- insert.operation
12180
- )
12181
- );
12182
- await executeStatement(
12183
- target,
12184
- sql`
12185
- INSERT INTO ${table} (${columnList})
12186
- VALUES ${sql.join(values, sql`, `)}
12187
- `
12188
- );
12189
- }
12190
- function recordedNodeChunkSize(target) {
12191
- return recordedChunkSize(target, RECORDED_NODE_COLUMNS.length);
12192
- }
12193
- function recordedEdgeChunkSize(target) {
12194
- return recordedChunkSize(target, RECORDED_EDGE_COLUMNS.length);
12195
- }
12196
- function insertRecordedNodeRows(target, table, inserts, recordedCommit) {
12197
- return insertRecordedRows(
12198
- target,
12199
- table,
12200
- RECORDED_NODE_COLUMN_LIST,
12201
- RECORDED_NODE_COLUMNS,
12202
- recordedNodeCells,
12203
- inserts,
12204
- recordedCommit
12205
- );
12206
- }
12207
- function insertRecordedEdgeRows(target, table, inserts, recordedCommit) {
12208
- return insertRecordedRows(
12209
- target,
12210
- table,
12211
- RECORDED_EDGE_COLUMN_LIST,
12212
- RECORDED_EDGE_COLUMNS,
12213
- recordedEdgeCells,
12214
- inserts,
12215
- recordedCommit
12216
- );
12217
- }
12218
-
12219
- // src/store/recorded-capture/flush.ts
12220
- function entityKey(entity) {
12221
- switch (entity.entity) {
12222
- case "node": {
12223
- return `node\0${entity.graphId}\0${entity.kind}\0${entity.id}`;
12224
- }
12225
- case "edge": {
12226
- return `edge\0${entity.graphId}\0${entity.id}`;
12227
- }
12228
- }
12229
- }
12230
- async function closeOpenReturning(target, table, graphId, ids, recordedCommit, kind) {
12231
- const kindFilter = kind === void 0 ? sql`` : sql`AND kind = ${kind}`;
12232
- const rows = await target.execute(
12233
- asCompiledRowsSql(sql`
12234
- UPDATE ${table}
12235
- SET recorded_to = ${recordedCommit}
12236
- WHERE graph_id = ${graphId}
12237
- ${kindFilter}
12238
- AND recorded_to = ${RECORDED_MAX}
12239
- AND id IN (${sqlValueList(ids)})
12240
- RETURNING id, deleted_at
12241
- `)
12242
- );
12243
- return new Map(rows.map((row) => [row.id, isPresent(row.deleted_at)]));
12244
- }
12245
- function recordedOp(hadOpenRow, priorRowWasTombstone, after) {
12246
- if (isPresent(after.deleted_at)) return "delete";
12247
- if (!hadOpenRow || priorRowWasTombstone) return "create";
12248
- return "update";
12249
- }
12250
- function groupNodesByKind(entities) {
12251
- const byKind = /* @__PURE__ */ new Map();
12252
- for (const entity of entities) {
12253
- const group = byKind.get(entity.kind) ?? [];
12254
- group.push(entity);
12255
- byKind.set(entity.kind, group);
12256
- }
12257
- return byKind;
12258
- }
12259
- async function resolveAfterImages(entities, readMissing) {
12260
- const afterById = /* @__PURE__ */ new Map();
12261
- const needRead = [];
12262
- for (const entity of entities) {
12263
- if (entity.afterImage === void 0) {
12264
- needRead.push(entity.id);
12265
- } else {
12266
- afterById.set(entity.id, entity.afterImage);
12267
- }
12268
- }
12269
- if (needRead.length > 0) {
12270
- for (const [id, row] of await readMissing(needRead)) afterById.set(id, row);
12271
- }
12272
- return afterById;
12273
- }
12274
- function recordedInsertsFor(afterById, closed) {
12275
- return [...afterById.values()].map((row) => ({
12276
- row,
12277
- operation: recordedOp(closed.has(row.id), closed.get(row.id) ?? false, row)
12278
- }));
12279
- }
12280
- async function flushNodes(target, schema, graphId, entities, recordedCommit) {
12281
- const chunkSize = recordedNodeChunkSize(target);
12282
- for (const [kind, group] of groupNodesByKind(entities)) {
12283
- for (const entityChunk of chunk(group, chunkSize)) {
12284
- const ids = entityChunk.map((entity) => entity.id);
12285
- const closed = await closeOpenReturning(
12286
- target,
12287
- schema.recordedNodesTable,
12288
- graphId,
12289
- ids,
12290
- recordedCommit,
12291
- kind
12292
- );
12293
- const afterById = await resolveAfterImages(
12294
- entityChunk,
12295
- (missing) => getNodeRowsByIds(target, graphId, kind, missing)
12296
- );
12297
- await insertRecordedNodeRows(
12298
- target,
12299
- schema.recordedNodesTable,
12300
- recordedInsertsFor(afterById, closed),
12301
- recordedCommit
12302
- );
12303
- }
12304
- }
12305
- }
12306
- async function flushEdges(target, schema, graphId, entities, recordedCommit) {
12307
- const chunkSize = recordedEdgeChunkSize(target);
12308
- for (const entityChunk of chunk(entities, chunkSize)) {
12309
- const ids = entityChunk.map((entity) => entity.id);
12310
- const closed = await closeOpenReturning(
12311
- target,
12312
- schema.recordedEdgesTable,
12313
- graphId,
12314
- ids,
12315
- recordedCommit
12316
- );
12317
- const afterById = await resolveAfterImages(
12318
- entityChunk,
12319
- (missing) => getEdgeRowsByIds(target, graphId, missing)
12320
- );
12321
- await insertRecordedEdgeRows(
12322
- target,
12323
- schema.recordedEdgesTable,
12324
- recordedInsertsFor(afterById, closed),
12325
- recordedCommit
12326
- );
12327
- }
12328
- }
12329
- async function closeOpenByKind(target, table, graphId, kind, recordedCommit) {
12330
- await executeStatement(
12331
- target,
12332
- sql`
12333
- UPDATE ${table}
12334
- SET recorded_to = ${recordedCommit}
12335
- WHERE graph_id = ${graphId}
12336
- AND kind = ${kind}
12337
- AND recorded_to = ${RECORDED_MAX}
12338
- `
12339
- );
12340
- }
12341
- async function closeOpenEdgesByNodeKind(target, schema, graphId, nodeKind, recordedCommit) {
12342
- await executeStatement(
12343
- target,
12344
- sql`
12345
- UPDATE ${schema.recordedEdgesTable}
12346
- SET recorded_to = ${recordedCommit}
12347
- WHERE graph_id = ${graphId}
12348
- AND (from_kind = ${nodeKind} OR to_kind = ${nodeKind})
12349
- AND recorded_to = ${RECORDED_MAX}
12350
- `
12351
- );
12352
- }
12353
- async function closeRecordedHardDeletedKind(target, schema, graphId, removal, ownsWriteLock) {
12354
- const recordedCommit = await allocateRecordedCommit(
12355
- target,
12356
- schema,
12357
- graphId,
12358
- ownsWriteLock
12359
- );
12360
- if (removal.entity === "node") {
12361
- await closeOpenByKind(
12362
- target,
12363
- schema.recordedNodesTable,
12364
- graphId,
12365
- removal.kind,
12366
- recordedCommit
12367
- );
12368
- await closeOpenEdgesByNodeKind(
12369
- target,
12370
- schema,
12371
- graphId,
12372
- removal.kind,
12373
- recordedCommit
12374
- );
12375
- return;
12376
- }
12377
- await closeOpenByKind(
12378
- target,
12379
- schema.recordedEdgesTable,
12380
- graphId,
12381
- removal.kind,
12382
- recordedCommit
12383
- );
12384
- }
12385
- async function queryConnectedEdgeIds(target, schema, params) {
12386
- const rows = await target.execute(
12387
- asCompiledRowsSql(sql`
12388
- SELECT id
12389
- FROM ${schema.edgesTable}
12390
- WHERE graph_id = ${params.graphId}
12391
- AND (
12392
- (from_kind = ${params.kind} AND from_id = ${params.id})
12393
- OR (to_kind = ${params.kind} AND to_id = ${params.id})
12394
- )
12395
- `)
12396
- );
12397
- return rows.map((row) => row.id);
12398
- }
12399
-
12400
- // src/store/recorded-capture.ts
12401
- function nodeIdentityKey(graphId, kind, id) {
12402
- return `${graphId}\0${kind}\0${id}`;
12403
- }
12404
- function nodeParamsIdentityKey(params) {
12405
- return nodeIdentityKey(params.graphId, params.kind, params.id);
12406
- }
12407
- function nodeRowIdentityKey(row) {
12408
- return nodeIdentityKey(row.graph_id, row.kind, row.id);
12409
- }
12410
- function edgeIdentityKey(graphId, id) {
12411
- return `${graphId}\0${id}`;
12412
- }
12413
- function edgeParamsIdentityKey(params) {
12414
- return edgeIdentityKey(params.graphId, params.id);
12415
- }
12416
- function edgeRowIdentityKey(row) {
12417
- return edgeIdentityKey(row.graph_id, row.id);
12418
- }
12419
- function createRecordedCaptureSession() {
12420
- const touched = /* @__PURE__ */ new Map();
12421
- let sealed = false;
12422
- function touch(entity) {
12423
- if (sealed) {
12424
- throw new ConfigurationError(
12425
- "Recorded-time capture session is sealed: a graph write happened after the transaction's capture was flushed.",
12426
- { entity: entity.entity, graphId: entity.graphId, id: entity.id },
12427
- {
12428
- suggestion: "Perform all writes inside the withRecordedTransaction callback; do not reuse the transaction context after it returns."
12429
- }
12430
- );
12431
- }
12432
- touched.set(entityKey(entity), entity);
12433
- }
12434
- return {
12435
- touchNode(graphId, kind, id, afterImage) {
12436
- touch({ entity: "node", graphId, kind, id, afterImage });
12437
- },
12438
- touchEdge(graphId, id, afterImage) {
12439
- touch({ entity: "edge", graphId, id, afterImage });
12440
- },
12441
- async flush(target, schema, ownsWriteLock) {
12442
- if (sealed) {
12443
- throw new ConfigurationError(
12444
- "Recorded-time capture session was already flushed.",
12445
- {},
12446
- {
12447
- suggestion: "A capture scope flushes once at its terminal point; do not flush it twice."
12448
- }
12449
- );
12450
- }
12451
- sealed = true;
12452
- if (touched.size === 0) return;
12453
- const byGraph = groupBy(touched.values(), (entity) => entity.graphId);
12454
- for (const [graphId, entities] of byGraph) {
12455
- const recordedCommit = await allocateRecordedCommit(
12456
- target,
12457
- schema,
12458
- graphId,
12459
- ownsWriteLock
12460
- );
12461
- const nodes = entities.filter(
12462
- (entity) => entity.entity === "node"
12463
- );
12464
- const edges = entities.filter(
12465
- (entity) => entity.entity === "edge"
12466
- );
12467
- await flushNodes(target, schema, graphId, nodes, recordedCommit);
12468
- await flushEdges(target, schema, graphId, edges, recordedCommit);
12469
- }
12470
- touched.clear();
12471
- }
12472
- };
12473
- }
12474
- function createRecordedTransactionBackend(target, session, schema) {
12475
- const nodeDispatch = nodeInsertDispatch(target);
12476
- const edgeDispatch = edgeInsertDispatch(target);
12477
- return createBackendOverlay(target, {
12478
- ...rawWriteGuards(target, "tx.backend"),
12479
- async insertNode(params) {
12480
- const row = await target.insertNode(params);
12481
- session.touchNode(params.graphId, params.kind, params.id, row);
12482
- return row;
12483
- },
12484
- ...target.insertNodeNoReturn === void 0 ? {} : {
12485
- async insertNodeNoReturn(params) {
12486
- await runInsertNoReturn(nodeDispatch, params);
12487
- session.touchNode(params.graphId, params.kind, params.id);
12488
- }
12489
- },
12490
- ...target.insertNodesBatch === void 0 ? {} : {
12491
- async insertNodesBatch(params) {
12492
- await runInsertBatch(nodeDispatch, params);
12493
- for (const node of params) {
12494
- session.touchNode(node.graphId, node.kind, node.id);
12495
- }
12496
- }
12497
- },
12498
- ...target.insertNodesBatchReturning === void 0 ? {} : {
12499
- async insertNodesBatchReturning(params) {
12500
- const rows = await runInsertBatchReturning(nodeDispatch, params);
12501
- const rowsByIdentity = new Map(
12502
- rows.map((row) => [nodeRowIdentityKey(row), row])
12503
- );
12504
- for (const node of params) {
12505
- session.touchNode(
12506
- node.graphId,
12507
- node.kind,
12508
- node.id,
12509
- rowsByIdentity.get(nodeParamsIdentityKey(node))
12510
- );
12511
- }
12512
- return rows;
12513
- }
12514
- },
12515
- async updateNode(params) {
12516
- const row = await target.updateNode(params);
12517
- session.touchNode(params.graphId, params.kind, params.id, row);
12518
- return row;
12519
- },
12520
- async deleteNode(params) {
12521
- await target.deleteNode(params);
12522
- session.touchNode(params.graphId, params.kind, params.id);
12523
- },
12524
- async hardDeleteNode(params) {
12525
- const connectedEdgeIds = await queryConnectedEdgeIds(
12526
- target,
12527
- schema,
12528
- params
12529
- );
12530
- await target.hardDeleteNode(params);
12531
- session.touchNode(params.graphId, params.kind, params.id);
12532
- for (const edgeId of connectedEdgeIds) {
12533
- session.touchEdge(params.graphId, edgeId);
12534
- }
12535
- },
12536
- async insertEdge(params) {
12537
- const row = await target.insertEdge(params);
12538
- session.touchEdge(params.graphId, params.id, row);
12539
- return row;
12540
- },
12541
- ...target.insertEdgeNoReturn === void 0 ? {} : {
12542
- async insertEdgeNoReturn(params) {
12543
- await runInsertNoReturn(edgeDispatch, params);
12544
- session.touchEdge(params.graphId, params.id);
12545
- }
12546
- },
12547
- ...target.insertEdgesBatch === void 0 ? {} : {
12548
- async insertEdgesBatch(params) {
12549
- await runInsertBatch(edgeDispatch, params);
12550
- for (const edge of params) {
12551
- session.touchEdge(edge.graphId, edge.id);
12552
- }
12553
- }
12554
- },
12555
- ...target.insertEdgesBatchReturning === void 0 ? {} : {
12556
- async insertEdgesBatchReturning(params) {
12557
- const rows = await runInsertBatchReturning(edgeDispatch, params);
12558
- const rowsByIdentity = new Map(
12559
- rows.map((row) => [edgeRowIdentityKey(row), row])
12560
- );
12561
- for (const edge of params) {
12562
- session.touchEdge(
12563
- edge.graphId,
12564
- edge.id,
12565
- rowsByIdentity.get(edgeParamsIdentityKey(edge))
12566
- );
12567
- }
12568
- return rows;
12569
- }
12570
- },
12571
- async updateEdge(params) {
12572
- const row = await target.updateEdge(params);
12573
- session.touchEdge(params.graphId, params.id, row);
12574
- return row;
12575
- },
12576
- async deleteEdge(params) {
12577
- await target.deleteEdge(params);
12578
- session.touchEdge(params.graphId, params.id);
12579
- },
12580
- async hardDeleteEdge(params) {
12581
- await target.hardDeleteEdge(params);
12582
- session.touchEdge(params.graphId, params.id);
12583
- }
12584
- });
12585
- }
12586
- function createRecordedTransactionScope(target, schema, ownsWriteLock = false) {
12587
- requireCaptureStatements(target);
12588
- const session = createRecordedCaptureSession();
12589
- const resolvedSchema = schema ?? requireRecordedSchema(target);
12590
- return {
12591
- backend: createRecordedTransactionBackend(target, session, resolvedSchema),
12592
- async flush() {
12593
- await withRecordedRelationsPrecondition(
12594
- session.flush(target, resolvedSchema, ownsWriteLock),
12595
- { dialect: target.dialect, surface: "capture-flush" }
12596
- );
12597
- }
12598
- };
12599
- }
12600
- async function runCapturedAutocommit(backend, schema, fn, options) {
12601
- assertRequestedRecordedIsolation(backend, options);
12602
- return backend.transaction(async (target) => {
12603
- await assertRecordedCaptureTransactionIsolation(target);
12604
- const scope = createRecordedTransactionScope(target, schema, true);
12605
- const result = await fn(scope.backend);
12606
- await scope.flush();
12607
- return result;
12608
- }, options);
12609
- }
12610
- function createRecordedBackend(backend, schema) {
12611
- assertCapturableBackend(backend);
12612
- const capture = (fn) => runCapturedAutocommit(backend, schema, fn);
12613
- return createBackendOverlay(backend, {
12614
- ...rawWriteGuards(backend, "backend"),
12615
- async insertNode(params) {
12616
- return capture((target) => target.insertNode(params));
12617
- },
12618
- ...backend.insertNodeNoReturn === void 0 ? {} : {
12619
- async insertNodeNoReturn(params) {
12620
- await capture(
12621
- (target) => runInsertNoReturn(nodeInsertDispatch(target), params)
12622
- );
12623
- }
12624
- },
12625
- ...backend.insertNodesBatch === void 0 ? {} : {
12626
- async insertNodesBatch(params) {
12627
- await capture(
12628
- (target) => runInsertBatch(nodeInsertDispatch(target), params)
12629
- );
12630
- }
12631
- },
12632
- ...backend.insertNodesBatchReturning === void 0 ? {} : {
12633
- async insertNodesBatchReturning(params) {
12634
- return capture(
12635
- (target) => runInsertBatchReturning(nodeInsertDispatch(target), params)
12636
- );
12637
- }
12638
- },
12639
- async updateNode(params) {
12640
- return capture((target) => target.updateNode(params));
12641
- },
12642
- async deleteNode(params) {
12643
- await capture((target) => target.deleteNode(params));
12644
- },
12645
- async hardDeleteNode(params) {
12646
- await capture((target) => target.hardDeleteNode(params));
12647
- },
12648
- async insertEdge(params) {
12649
- return capture((target) => target.insertEdge(params));
12650
- },
12651
- ...backend.insertEdgeNoReturn === void 0 ? {} : {
12652
- async insertEdgeNoReturn(params) {
12653
- await capture(
12654
- (target) => runInsertNoReturn(edgeInsertDispatch(target), params)
12655
- );
12656
- }
12657
- },
12658
- ...backend.insertEdgesBatch === void 0 ? {} : {
12659
- async insertEdgesBatch(params) {
12660
- await capture(
12661
- (target) => runInsertBatch(edgeInsertDispatch(target), params)
12662
- );
12663
- }
12664
- },
12665
- ...backend.insertEdgesBatchReturning === void 0 ? {} : {
12666
- async insertEdgesBatchReturning(params) {
12667
- return capture(
12668
- (target) => runInsertBatchReturning(edgeInsertDispatch(target), params)
12669
- );
12670
- }
12671
- },
12672
- async updateEdge(params) {
12673
- return capture((target) => target.updateEdge(params));
12674
- },
12675
- async deleteEdge(params) {
12676
- await capture((target) => target.deleteEdge(params));
12677
- },
12678
- async hardDeleteEdge(params) {
12679
- await capture((target) => target.hardDeleteEdge(params));
12680
- },
12681
- async transaction(fn, options) {
12682
- assertRequestedRecordedIsolation(backend, options);
12683
- return backend.transaction(async (target) => {
12684
- await assertRecordedCaptureTransactionIsolation(target);
12685
- const scope = createRecordedTransactionScope(target, schema, true);
12686
- const result = await fn(scope.backend, createHistoryUnsafeSqlRef());
12687
- await scope.flush();
12688
- return result;
12689
- }, options);
12690
- },
12691
- ...backend.adoptTransaction === void 0 ? {} : {
12692
- adoptTransaction(externalTx) {
12693
- throw recordedCaptureRequiresCallbackTransactionError({
12694
- externalTxType: typeof externalTx
12695
- });
12696
- }
12697
- }
12698
- });
12699
- }
12700
-
12701
- // src/store/materialize-removals.ts
12702
- var ENTITY_KINDS = ["node", "edge"];
12703
- function buildPendingKindRemoval(args) {
12704
- return { ...args, removedAt: void 0, error: void 0 };
12705
- }
12706
- async function materializeRemovals(context, options = {}) {
12707
- const { backend, graphId } = context;
12708
- if (backend.executeDdl === void 0 || backend.recordKindRemoval === void 0 || backend.getPendingKindRemovals === void 0) {
12709
- throw new ConfigurationError(
12710
- "store.materializeRemovals() requires a backend with kind-removal primitives (executeDdl, recordKindRemoval, getPendingKindRemovals). The bundled SQLite and Postgres backends provide them.",
12711
- { code: "MATERIALIZE_REMOVALS_BACKEND_UNSUPPORTED" }
12712
- );
12713
- }
12714
- await ensureFocusedStatusTable(backend, backend.ensureKindRemovalsTable);
12715
- await reconcilePendingRemovals(context);
12716
- const reclaimedVectorFields = await reclaimRemovedVectorFieldTables(context);
12717
- const pending = await backend.getPendingKindRemovals(graphId);
12718
- const kindFilter = options.kinds === void 0 ? void 0 : new Set(options.kinds);
12719
- const candidates = pending.filter(
12720
- (row) => kindFilter === void 0 ? true : kindFilter.has(row.kindName)
11282
+ await ensureFocusedStatusTable(backend, backend.ensureKindRemovalsTable);
11283
+ await reconcilePendingRemovals(context);
11284
+ const reclaimedVectorFields = await reclaimRemovedVectorFieldTables(context);
11285
+ const pending = await backend.getPendingKindRemovals(graphId);
11286
+ const kindFilter = options.kinds === void 0 ? void 0 : new Set(options.kinds);
11287
+ const candidates = pending.filter(
11288
+ (row) => kindFilter === void 0 ? true : kindFilter.has(row.kindName)
12721
11289
  );
12722
11290
  if (candidates.length === 0) return { results: [], reclaimedVectorFields };
12723
11291
  const tableNames = backend.tableNames;
@@ -13023,180 +11591,20 @@ async function reclaimRemovedVectorFieldTables(context) {
13023
11591
  }
13024
11592
  }
13025
11593
  if (results.every((entry2) => entry2.status === "reclaimed")) {
13026
- if (perBackend === void 0) {
13027
- perBackend = /* @__PURE__ */ new Map();
13028
- reclaimCache.set(backend, perBackend);
13029
- }
13030
- perBackend.set(cacheKey, results);
13031
- }
13032
- return results;
13033
- }
13034
- var reclaimCache = /* @__PURE__ */ new WeakMap();
13035
- function quote(identifier) {
13036
- return `"${identifier.replaceAll('"', '""')}"`;
13037
- }
13038
- function literal(value) {
13039
- return `'${value.replaceAll("'", "''")}'`;
13040
- }
13041
-
13042
- // src/constraints/index.ts
13043
- var UNIQUE_KEY_SEPARATOR = "";
13044
- var UNIQUE_KEY_NULL_MARKER = "";
13045
- function computeUniqueKey(props, fields, collation) {
13046
- const values = fields.map((field2) => {
13047
- const value = props[field2];
13048
- if (value === void 0 || value === null) {
13049
- return UNIQUE_KEY_NULL_MARKER;
13050
- }
13051
- const stringValue = typeof value === "string" ? value : typeof value === "number" || typeof value === "boolean" ? value.toString() : JSON.stringify(value);
13052
- return collation === "caseInsensitive" ? stringValue.toLowerCase() : stringValue;
13053
- });
13054
- return values.join(UNIQUE_KEY_SEPARATOR);
13055
- }
13056
- function checkWherePredicate(constraint, props) {
13057
- if (!constraint.where) {
13058
- return true;
13059
- }
13060
- const predicateBuilder = buildPredicateContext(props);
13061
- const predicate = constraint.where(predicateBuilder);
13062
- return evaluatePredicate(predicate, props);
13063
- }
13064
- function buildPredicateContext(props) {
13065
- const context = {};
13066
- for (const key of Object.keys(props)) {
13067
- context[key] = {
13068
- isNull: () => ({
13069
- __type: "unique_predicate",
13070
- field: key,
13071
- op: "isNull"
13072
- }),
13073
- isNotNull: () => ({
13074
- __type: "unique_predicate",
13075
- field: key,
13076
- op: "isNotNull"
13077
- })
13078
- };
13079
- }
13080
- return context;
13081
- }
13082
- function evaluatePredicate(predicate, props) {
13083
- if (typeof predicate !== "object" || predicate === null || !("__type" in predicate)) {
13084
- return true;
13085
- }
13086
- const pred = predicate;
13087
- if (pred.__type !== "unique_predicate") {
13088
- return true;
13089
- }
13090
- const value = props[pred.field];
13091
- if (pred.op === "isNull") {
13092
- return value === null || value === void 0;
13093
- }
13094
- return isPresent(value);
13095
- }
13096
- function getKindsForUniquenessCheck(baseKind, scope, registry) {
13097
- if (scope === "kind") {
13098
- return [baseKind];
13099
- }
13100
- const root = findRootAncestor(baseKind, registry);
13101
- return registry.expandSubClasses(root);
13102
- }
13103
- function findRootAncestor(kind, registry) {
13104
- const ancestors = registry.getAncestors(kind);
13105
- if (ancestors.size === 0) {
13106
- return kind;
13107
- }
13108
- for (const ancestor of ancestors) {
13109
- if (registry.getAncestors(ancestor).size === 0) {
13110
- return ancestor;
13111
- }
13112
- }
13113
- const firstAncestor = [...ancestors][0];
13114
- return firstAncestor ? findRootAncestor(firstAncestor, registry) : kind;
13115
- }
13116
- function checkCardinality(edgeKind, fromKind, fromId, cardinality, existingEdgeCount, hasActiveEdge) {
13117
- switch (cardinality) {
13118
- case "many": {
13119
- return void 0;
13120
- }
13121
- case "one": {
13122
- if (existingEdgeCount > 0) {
13123
- return new CardinalityError({
13124
- edgeKind,
13125
- fromKind,
13126
- fromId,
13127
- cardinality: "one",
13128
- existingCount: existingEdgeCount
13129
- });
13130
- }
13131
- return void 0;
13132
- }
13133
- case "unique": {
13134
- return void 0;
13135
- }
13136
- case "oneActive": {
13137
- if (hasActiveEdge) {
13138
- return new CardinalityError({
13139
- edgeKind,
13140
- fromKind,
13141
- fromId,
13142
- cardinality: "oneActive",
13143
- existingCount: 1
13144
- });
13145
- }
13146
- return void 0;
13147
- }
13148
- }
13149
- }
13150
- function checkUniqueEdge(edgeKind, fromKind, fromId, _toKind, _toId, existingCount) {
13151
- if (existingCount > 0) {
13152
- return new CardinalityError({
13153
- edgeKind,
13154
- fromKind,
13155
- fromId,
13156
- cardinality: "unique",
13157
- existingCount
13158
- });
13159
- }
13160
- return void 0;
13161
- }
13162
- function validateEdgeEndpoints(edgeKind, fromKind, toKind, registration, registry) {
13163
- const validFromKinds = registration.from.map((node) => node.kind);
13164
- const fromValid = validFromKinds.some(
13165
- (validKind) => registry.isAssignableTo(fromKind, validKind)
13166
- );
13167
- if (!fromValid) {
13168
- return new EndpointError({
13169
- edgeKind,
13170
- endpoint: "from",
13171
- actualKind: fromKind,
13172
- expectedKinds: validFromKinds
13173
- });
13174
- }
13175
- const validToKinds = registration.to.map((node) => node.kind);
13176
- const toValid = validToKinds.some(
13177
- (validKind) => registry.isAssignableTo(toKind, validKind)
13178
- );
13179
- if (!toValid) {
13180
- return new EndpointError({
13181
- edgeKind,
13182
- endpoint: "to",
13183
- actualKind: toKind,
13184
- expectedKinds: validToKinds
13185
- });
13186
- }
13187
- return void 0;
13188
- }
13189
- function checkDisjointness(nodeId, nodeKind, existingKinds, registry) {
13190
- for (const existingKind of existingKinds) {
13191
- if (registry.areDisjoint(nodeKind, existingKind)) {
13192
- return new DisjointError({
13193
- nodeId,
13194
- attemptedKind: nodeKind,
13195
- conflictingKind: existingKind
13196
- });
11594
+ if (perBackend === void 0) {
11595
+ perBackend = /* @__PURE__ */ new Map();
11596
+ reclaimCache.set(backend, perBackend);
13197
11597
  }
11598
+ perBackend.set(cacheKey, results);
13198
11599
  }
13199
- return void 0;
11600
+ return results;
11601
+ }
11602
+ var reclaimCache = /* @__PURE__ */ new WeakMap();
11603
+ function quote(identifier) {
11604
+ return `"${identifier.replaceAll('"', '""')}"`;
11605
+ }
11606
+ function literal(value) {
11607
+ return `'${value.replaceAll("'", "''")}'`;
13200
11608
  }
13201
11609
 
13202
11610
  // src/store/constraints.ts
@@ -13483,16 +11891,11 @@ async function executeEdgeCreateInternal(ctx, input, backend, options) {
13483
11891
  const kind = input.kind;
13484
11892
  const id = input.id ?? generateId();
13485
11893
  const opContext = ctx.createOperationContext("create", "edge", kind, id);
13486
- return ctx.withOperationHooks(opContext, async () => {
13487
- const prepared = await validateAndPrepareEdgeCreate(
13488
- ctx,
13489
- input,
13490
- id,
13491
- backend
13492
- );
11894
+ return runHookedWriteOperation(ctx, opContext, backend, async (target) => {
11895
+ const prepared = await validateAndPrepareEdgeCreate(ctx, input, id, target);
13493
11896
  let row;
13494
11897
  {
13495
- row = await backend.insertEdge(prepared.insertParams);
11898
+ row = await target.insertEdge(prepared.insertParams);
13496
11899
  }
13497
11900
  if (row === void 0) return;
13498
11901
  return rowToEdge(row);
@@ -13512,93 +11915,64 @@ async function executeEdgeCreateNoReturnBatch(ctx, inputs, backend) {
13512
11915
  if (inputs.length === 0) {
13513
11916
  return;
13514
11917
  }
13515
- const { backend: validationBackend, registerPendingEdgeForCardinality } = createEdgeBatchValidationBackend(backend);
13516
- const preparedCreates = [];
13517
- for (const input of inputs) {
13518
- const id = input.id ?? generateId();
13519
- const prepared = await validateAndPrepareEdgeCreate(
13520
- ctx,
13521
- input,
13522
- id,
13523
- validationBackend
13524
- );
13525
- preparedCreates.push(prepared);
13526
- registerPendingEdgeForCardinality(
13527
- prepared.insertParams,
13528
- prepared.cardinality
11918
+ await runInWriteTransaction(ctx, backend, async (target) => {
11919
+ const { backend: validationBackend, registerPendingEdgeForCardinality } = createEdgeBatchValidationBackend(target);
11920
+ const preparedCreates = [];
11921
+ for (const input of inputs) {
11922
+ const id = input.id ?? generateId();
11923
+ const prepared = await validateAndPrepareEdgeCreate(
11924
+ ctx,
11925
+ input,
11926
+ id,
11927
+ validationBackend
11928
+ );
11929
+ preparedCreates.push(prepared);
11930
+ registerPendingEdgeForCardinality(
11931
+ prepared.insertParams,
11932
+ prepared.cardinality
11933
+ );
11934
+ }
11935
+ const batchInsertParams = preparedCreates.map(
11936
+ (prepared) => prepared.insertParams
13529
11937
  );
13530
- }
13531
- const batchInsertParams = preparedCreates.map(
13532
- (prepared) => prepared.insertParams
13533
- );
13534
- await runInsertBatch(edgeInsertDispatch(backend), batchInsertParams);
11938
+ await runInsertBatch(edgeInsertDispatch(target), batchInsertParams);
11939
+ });
13535
11940
  }
13536
11941
  async function executeEdgeCreateBatch(ctx, inputs, backend) {
13537
11942
  if (inputs.length === 0) {
13538
11943
  return [];
13539
11944
  }
13540
- const { backend: validationBackend, registerPendingEdgeForCardinality } = createEdgeBatchValidationBackend(backend);
13541
- const preparedCreates = [];
13542
- for (const input of inputs) {
13543
- const id = input.id ?? generateId();
13544
- const prepared = await validateAndPrepareEdgeCreate(
13545
- ctx,
13546
- input,
13547
- id,
13548
- validationBackend
11945
+ return runInWriteTransaction(ctx, backend, async (target) => {
11946
+ const { backend: validationBackend, registerPendingEdgeForCardinality } = createEdgeBatchValidationBackend(target);
11947
+ const preparedCreates = [];
11948
+ for (const input of inputs) {
11949
+ const id = input.id ?? generateId();
11950
+ const prepared = await validateAndPrepareEdgeCreate(
11951
+ ctx,
11952
+ input,
11953
+ id,
11954
+ validationBackend
11955
+ );
11956
+ preparedCreates.push(prepared);
11957
+ registerPendingEdgeForCardinality(
11958
+ prepared.insertParams,
11959
+ prepared.cardinality
11960
+ );
11961
+ }
11962
+ const batchInsertParams = preparedCreates.map(
11963
+ (prepared) => prepared.insertParams
13549
11964
  );
13550
- preparedCreates.push(prepared);
13551
- registerPendingEdgeForCardinality(
13552
- prepared.insertParams,
13553
- prepared.cardinality
11965
+ const rows = await runInsertBatchReturning(
11966
+ edgeInsertDispatch(target),
11967
+ batchInsertParams
13554
11968
  );
13555
- }
13556
- const batchInsertParams = preparedCreates.map(
13557
- (prepared) => prepared.insertParams
13558
- );
13559
- const rows = await runInsertBatchReturning(
13560
- edgeInsertDispatch(backend),
13561
- batchInsertParams
13562
- );
13563
- return rows.map((row) => rowToEdge(row));
13564
- }
13565
- async function executeEdgeUpdate(ctx, input, backend) {
13566
- const id = input.id;
13567
- const existing = await backend.getEdge(ctx.graphId, id);
13568
- if (!existing || existing.deleted_at) {
13569
- throw new EdgeNotFoundError("unknown", id);
13570
- }
13571
- const opContext = ctx.createOperationContext(
13572
- "update",
13573
- "edge",
13574
- existing.kind,
13575
- id
13576
- );
13577
- return ctx.withOperationHooks(opContext, async () => {
13578
- const registration = getEdgeRegistration(ctx.graph, existing.kind);
13579
- const edgeKind = registration.type;
13580
- const existingProps = JSON.parse(existing.props);
13581
- const mergedProps = { ...existingProps, ...input.props };
13582
- const validatedProps = validateEdgeProps(edgeKind.schema, mergedProps, {
13583
- kind: existing.kind,
13584
- operation: "update",
13585
- id
13586
- });
13587
- const validTo = validateOptionalCanonicalIsoDate(input.validTo, "validTo");
13588
- const updateParams = {
13589
- graphId: ctx.graphId,
13590
- id,
13591
- props: validatedProps
13592
- };
13593
- if (validTo !== void 0) updateParams.validTo = validTo;
13594
- const row = await backend.updateEdge(updateParams);
13595
- return rowToEdge(row);
11969
+ return rows.map((row) => rowToEdge(row));
13596
11970
  });
13597
11971
  }
13598
- async function executeEdgeUpsertUpdate(ctx, input, backend, options) {
11972
+ async function performEdgeUpdate(ctx, input, target, options) {
13599
11973
  const id = input.id;
13600
- const existing = await backend.getEdge(ctx.graphId, id);
13601
- if (!existing) {
11974
+ const existing = await target.getEdge(ctx.graphId, id);
11975
+ if (!existing || !options?.clearDeleted && existing.deleted_at) {
13602
11976
  throw new EdgeNotFoundError("unknown", id);
13603
11977
  }
13604
11978
  const registration = getEdgeRegistration(ctx.graph, existing.kind);
@@ -13618,40 +11992,55 @@ async function executeEdgeUpsertUpdate(ctx, input, backend, options) {
13618
11992
  };
13619
11993
  if (validTo !== void 0) updateParams.validTo = validTo;
13620
11994
  if (options?.clearDeleted) updateParams.clearDeleted = true;
13621
- const row = await backend.updateEdge(updateParams);
11995
+ const row = await target.updateEdge(updateParams);
13622
11996
  return rowToEdge(row);
13623
11997
  }
13624
- async function executeEdgeDelete(ctx, id, backend) {
13625
- const existing = await backend.getEdge(ctx.graphId, id);
13626
- if (!existing || existing.deleted_at) {
13627
- return;
11998
+ async function executeEdgeUpdate(ctx, input, backend) {
11999
+ const id = input.id;
12000
+ const gate = await backend.getEdge(ctx.graphId, id);
12001
+ if (!gate || gate.deleted_at) {
12002
+ throw new EdgeNotFoundError("unknown", id);
13628
12003
  }
13629
- const opContext = ctx.createOperationContext(
13630
- "delete",
13631
- "edge",
13632
- existing.kind,
13633
- id
12004
+ const opContext = ctx.createOperationContext("update", "edge", gate.kind, id);
12005
+ return runHookedWriteOperation(
12006
+ ctx,
12007
+ opContext,
12008
+ backend,
12009
+ (target) => performEdgeUpdate(ctx, input, target)
12010
+ );
12011
+ }
12012
+ async function executeEdgeUpsertUpdate(ctx, input, backend, options) {
12013
+ return runInWriteTransaction(
12014
+ ctx,
12015
+ backend,
12016
+ (target) => performEdgeUpdate(ctx, input, target, options)
13634
12017
  );
13635
- return ctx.withOperationHooks(opContext, async () => {
13636
- await backend.deleteEdge({
12018
+ }
12019
+ async function executeEdgeDelete(ctx, id, backend) {
12020
+ const gate = await backend.getEdge(ctx.graphId, id);
12021
+ if (!gate || gate.deleted_at) return;
12022
+ const opContext = ctx.createOperationContext("delete", "edge", gate.kind, id);
12023
+ return runHookedWriteOperation(ctx, opContext, backend, async (target) => {
12024
+ const existing = await target.getEdge(ctx.graphId, id);
12025
+ if (!existing || existing.deleted_at) {
12026
+ return;
12027
+ }
12028
+ await target.deleteEdge({
13637
12029
  graphId: ctx.graphId,
13638
12030
  id
13639
12031
  });
13640
12032
  });
13641
12033
  }
13642
12034
  async function executeEdgeHardDelete(ctx, id, backend) {
13643
- const existing = await backend.getEdge(ctx.graphId, id);
13644
- if (!existing) {
13645
- return;
13646
- }
13647
- const opContext = ctx.createOperationContext(
13648
- "delete",
13649
- "edge",
13650
- existing.kind,
13651
- id
13652
- );
13653
- return ctx.withOperationHooks(opContext, async () => {
13654
- await backend.hardDeleteEdge({
12035
+ const gate = await backend.getEdge(ctx.graphId, id);
12036
+ if (!gate) return;
12037
+ const opContext = ctx.createOperationContext("delete", "edge", gate.kind, id);
12038
+ return runHookedWriteOperation(ctx, opContext, backend, async (target) => {
12039
+ const existing = await target.getEdge(ctx.graphId, id);
12040
+ if (!existing) {
12041
+ return;
12042
+ }
12043
+ await target.hardDeleteEdge({
13655
12044
  graphId: ctx.graphId,
13656
12045
  id
13657
12046
  });
@@ -13771,72 +12160,100 @@ async function executeEdgeGetOrCreateByEndpoints(ctx, kind, fromKind, fromId, to
13771
12160
  operation: "create"
13772
12161
  });
13773
12162
  validateMatchOnFields(edgeKind.schema, matchOn, kind);
13774
- const candidateRows = await backend.findEdgesByKind({
13775
- graphId: ctx.graphId,
13776
- kind,
13777
- fromKind,
13778
- fromId,
13779
- toKind,
13780
- toId,
13781
- excludeDeleted: false,
13782
- temporalMode: "includeTombstones"
13783
- });
13784
- const { liveRow, deletedRow } = findMatchingEdge(
13785
- candidateRows,
13786
- matchOn,
13787
- validatedProps
13788
- );
13789
- if (liveRow === void 0 && deletedRow === void 0) {
13790
- const input = {
12163
+ if (ifExists === "return") {
12164
+ const probeRows = await backend.findEdgesByKind({
12165
+ graphId: ctx.graphId,
13791
12166
  kind,
13792
12167
  fromKind,
13793
12168
  fromId,
13794
12169
  toKind,
13795
12170
  toId,
13796
- props: validatedProps
13797
- };
13798
- const edge2 = await executeEdgeCreate(ctx, input, backend);
13799
- return { edge: edge2, action: "created" };
12171
+ excludeDeleted: false,
12172
+ temporalMode: "includeTombstones"
12173
+ });
12174
+ const { liveRow: probedLiveRow } = findMatchingEdge(
12175
+ probeRows,
12176
+ matchOn,
12177
+ validatedProps
12178
+ );
12179
+ if (probedLiveRow !== void 0) {
12180
+ return { edge: rowToEdge(probedLiveRow), action: "found" };
12181
+ }
13800
12182
  }
13801
- if (liveRow !== void 0) {
13802
- if (ifExists === "return") {
13803
- return { edge: rowToEdge(liveRow), action: "found" };
12183
+ async function attempt() {
12184
+ const candidateRows = await backend.findEdgesByKind({
12185
+ graphId: ctx.graphId,
12186
+ kind,
12187
+ fromKind,
12188
+ fromId,
12189
+ toKind,
12190
+ toId,
12191
+ excludeDeleted: false,
12192
+ temporalMode: "includeTombstones"
12193
+ });
12194
+ const { liveRow, deletedRow } = findMatchingEdge(
12195
+ candidateRows,
12196
+ matchOn,
12197
+ validatedProps
12198
+ );
12199
+ if (liveRow === void 0 && deletedRow === void 0) {
12200
+ const input = {
12201
+ kind,
12202
+ fromKind,
12203
+ fromId,
12204
+ toKind,
12205
+ toId,
12206
+ props: validatedProps
12207
+ };
12208
+ const edge2 = await executeEdgeCreate(ctx, input, backend);
12209
+ return { edge: edge2, action: "created" };
13804
12210
  }
13805
- const edge2 = await executeEdgeUpsertUpdate(
13806
- ctx,
13807
- { id: liveRow.id, props: validatedProps },
12211
+ if (liveRow !== void 0) {
12212
+ if (ifExists === "return") {
12213
+ return { edge: rowToEdge(liveRow), action: "found" };
12214
+ }
12215
+ const edge2 = await executeEdgeUpsertUpdate(
12216
+ ctx,
12217
+ { id: liveRow.id, props: validatedProps },
12218
+ backend
12219
+ );
12220
+ return { edge: edge2, action: "updated" };
12221
+ }
12222
+ const cardinality = registration.cardinality ?? "many";
12223
+ if (deletedRow === void 0) {
12224
+ throw new Error("Expected deletedRow to be defined");
12225
+ }
12226
+ const matchedDeletedRow = deletedRow;
12227
+ const effectiveValidTo = matchedDeletedRow.valid_to;
12228
+ const constraintContext = {
12229
+ graphId: ctx.graphId,
12230
+ registry: ctx.registry,
13808
12231
  backend
12232
+ };
12233
+ await checkCardinalityConstraint(
12234
+ constraintContext,
12235
+ kind,
12236
+ cardinality,
12237
+ fromKind,
12238
+ fromId,
12239
+ toKind,
12240
+ toId,
12241
+ effectiveValidTo
13809
12242
  );
13810
- return { edge: edge2, action: "updated" };
12243
+ const edge = await executeEdgeUpsertUpdate(
12244
+ ctx,
12245
+ { id: matchedDeletedRow.id, props: validatedProps },
12246
+ backend,
12247
+ { clearDeleted: true }
12248
+ );
12249
+ return { edge, action: "resurrected" };
13811
12250
  }
13812
- const cardinality = registration.cardinality ?? "many";
13813
- if (deletedRow === void 0) {
13814
- throw new Error("Expected deletedRow to be defined");
12251
+ try {
12252
+ return await attempt();
12253
+ } catch (error) {
12254
+ if (!(error instanceof CardinalityError)) throw error;
12255
+ return attempt();
13815
12256
  }
13816
- const matchedDeletedRow = deletedRow;
13817
- const effectiveValidTo = matchedDeletedRow.valid_to;
13818
- const constraintContext = {
13819
- graphId: ctx.graphId,
13820
- registry: ctx.registry,
13821
- backend
13822
- };
13823
- await checkCardinalityConstraint(
13824
- constraintContext,
13825
- kind,
13826
- cardinality,
13827
- fromKind,
13828
- fromId,
13829
- toKind,
13830
- toId,
13831
- effectiveValidTo
13832
- );
13833
- const edge = await executeEdgeUpsertUpdate(
13834
- ctx,
13835
- { id: matchedDeletedRow.id, props: validatedProps },
13836
- backend,
13837
- { clearDeleted: true }
13838
- );
13839
- return { edge, action: "resurrected" };
13840
12257
  }
13841
12258
  async function executeEdgeBulkGetOrCreateByEndpoints(ctx, kind, items, backend, options) {
13842
12259
  if (items.length === 0) return [];
@@ -13882,258 +12299,153 @@ async function executeEdgeBulkGetOrCreateByEndpoints(ctx, kind, items, backend,
13882
12299
  uniqueEndpoints.set(entry2.endpointKey, {
13883
12300
  fromKind: entry2.fromKind,
13884
12301
  fromId: entry2.fromId,
13885
- toKind: entry2.toKind,
13886
- toId: entry2.toId
13887
- });
13888
- }
13889
- }
13890
- const rowsByEndpoint = /* @__PURE__ */ new Map();
13891
- for (const [endpointKey, endpoint] of uniqueEndpoints) {
13892
- const rows = await backend.findEdgesByKind({
13893
- graphId: ctx.graphId,
13894
- kind,
13895
- fromKind: endpoint.fromKind,
13896
- fromId: endpoint.fromId,
13897
- toKind: endpoint.toKind,
13898
- toId: endpoint.toId,
13899
- excludeDeleted: false,
13900
- temporalMode: "includeTombstones"
13901
- });
13902
- rowsByEndpoint.set(endpointKey, rows);
13903
- }
13904
- const toCreate = [];
13905
- const toFetch = [];
13906
- const duplicateOf = [];
13907
- const seenKeys = /* @__PURE__ */ new Map();
13908
- for (const [index, entry2] of validated.entries()) {
13909
- const previousIndex = seenKeys.get(entry2.compositeKey);
13910
- if (previousIndex !== void 0) {
13911
- duplicateOf.push({ index, sourceIndex: previousIndex });
13912
- continue;
13913
- }
13914
- seenKeys.set(entry2.compositeKey, index);
13915
- const candidateRows = rowsByEndpoint.get(entry2.endpointKey) ?? [];
13916
- const { liveRow, deletedRow } = findMatchingEdge(
13917
- candidateRows,
13918
- matchOn,
13919
- entry2.validatedProps
13920
- );
13921
- if (liveRow === void 0 && deletedRow === void 0) {
13922
- toCreate.push({
13923
- index,
13924
- input: {
13925
- kind,
13926
- fromKind: entry2.fromKind,
13927
- fromId: entry2.fromId,
13928
- toKind: entry2.toKind,
13929
- toId: entry2.toId,
13930
- props: entry2.validatedProps
13931
- }
13932
- });
13933
- } else {
13934
- const bestRow = liveRow ?? deletedRow;
13935
- if (bestRow === void 0) {
13936
- throw new Error("Expected at least one of liveRow or deletedRow");
13937
- }
13938
- toFetch.push({
13939
- index,
13940
- row: bestRow,
13941
- isDeleted: liveRow === void 0,
13942
- validatedProps: entry2.validatedProps,
13943
- fromKind: entry2.fromKind,
13944
- fromId: entry2.fromId,
13945
- toKind: entry2.toKind,
13946
- toId: entry2.toId
13947
- });
13948
- }
13949
- }
13950
- const results = Array.from({ length: items.length });
13951
- if (toCreate.length > 0) {
13952
- const createInputs = toCreate.map((entry2) => entry2.input);
13953
- const createdEdges = await executeEdgeCreateBatch(
13954
- ctx,
13955
- createInputs,
13956
- backend
13957
- );
13958
- for (const [batchIndex, entry2] of toCreate.entries()) {
13959
- results[entry2.index] = {
13960
- edge: createdEdges[batchIndex],
13961
- action: "created"
13962
- };
13963
- }
13964
- }
13965
- for (const entry2 of toFetch) {
13966
- if (entry2.isDeleted) {
13967
- const effectiveValidTo = entry2.row.valid_to;
13968
- const constraintContext = {
13969
- graphId: ctx.graphId,
13970
- registry: ctx.registry,
13971
- backend
13972
- };
13973
- await checkCardinalityConstraint(
13974
- constraintContext,
13975
- kind,
13976
- cardinality,
13977
- entry2.fromKind,
13978
- entry2.fromId,
13979
- entry2.toKind,
13980
- entry2.toId,
13981
- effectiveValidTo
13982
- );
13983
- const edge = await executeEdgeUpsertUpdate(
13984
- ctx,
13985
- { id: entry2.row.id, props: entry2.validatedProps },
13986
- backend,
13987
- { clearDeleted: true }
13988
- );
13989
- results[entry2.index] = { edge, action: "resurrected" };
13990
- } else if (ifExists === "update") {
13991
- const edge = await executeEdgeUpsertUpdate(
13992
- ctx,
13993
- { id: entry2.row.id, props: entry2.validatedProps },
13994
- backend
13995
- );
13996
- results[entry2.index] = { edge, action: "updated" };
13997
- } else {
13998
- results[entry2.index] = { edge: rowToEdge(entry2.row), action: "found" };
13999
- }
14000
- }
14001
- for (const { index, sourceIndex } of duplicateOf) {
14002
- const sourceResult = results[sourceIndex];
14003
- results[index] = { edge: sourceResult.edge, action: "found" };
14004
- }
14005
- return results;
14006
- }
14007
-
14008
- // src/store/uniqueness.ts
14009
- async function checkUniquenessConstraints(ctx, kind, id, props, constraints) {
14010
- for (const constraint of constraints) {
14011
- if (!checkWherePredicate(constraint, props)) {
14012
- continue;
12302
+ toKind: entry2.toKind,
12303
+ toId: entry2.toId
12304
+ });
14013
12305
  }
14014
- const key = computeUniqueKey(
14015
- props,
14016
- constraint.fields,
14017
- constraint.collation
14018
- );
14019
- const kindsToCheck = getKindsForUniquenessCheck(
14020
- kind,
14021
- constraint.scope,
14022
- ctx.registry
14023
- );
14024
- for (const kindToCheck of kindsToCheck) {
14025
- const existing = await ctx.backend.checkUnique({
12306
+ }
12307
+ async function fetchRowsByEndpoint(reader) {
12308
+ const rowsByEndpoint = /* @__PURE__ */ new Map();
12309
+ for (const [endpointKey, endpoint] of uniqueEndpoints) {
12310
+ const rows = await reader.findEdgesByKind({
14026
12311
  graphId: ctx.graphId,
14027
- nodeKind: kindToCheck,
14028
- constraintName: constraint.name,
14029
- key
12312
+ kind,
12313
+ fromKind: endpoint.fromKind,
12314
+ fromId: endpoint.fromId,
12315
+ toKind: endpoint.toKind,
12316
+ toId: endpoint.toId,
12317
+ excludeDeleted: false,
12318
+ temporalMode: "includeTombstones"
14030
12319
  });
14031
- if (existing && existing.node_id !== id) {
14032
- throw new UniquenessError({
14033
- constraintName: constraint.name,
14034
- kind: kindToCheck,
14035
- existingId: existing.node_id,
14036
- newId: id,
14037
- fields: constraint.fields
12320
+ rowsByEndpoint.set(endpointKey, rows);
12321
+ }
12322
+ return rowsByEndpoint;
12323
+ }
12324
+ function partitionEntries(rowsByEndpoint) {
12325
+ const toCreate = [];
12326
+ const toFetch = [];
12327
+ const duplicateOf = [];
12328
+ const seenKeys = /* @__PURE__ */ new Map();
12329
+ for (const [index, entry2] of validated.entries()) {
12330
+ const previousIndex = seenKeys.get(entry2.compositeKey);
12331
+ if (previousIndex !== void 0) {
12332
+ duplicateOf.push({ index, sourceIndex: previousIndex });
12333
+ continue;
12334
+ }
12335
+ seenKeys.set(entry2.compositeKey, index);
12336
+ const candidateRows = rowsByEndpoint.get(entry2.endpointKey) ?? [];
12337
+ const { liveRow, deletedRow } = findMatchingEdge(
12338
+ candidateRows,
12339
+ matchOn,
12340
+ entry2.validatedProps
12341
+ );
12342
+ if (liveRow === void 0 && deletedRow === void 0) {
12343
+ toCreate.push({
12344
+ index,
12345
+ input: {
12346
+ kind,
12347
+ fromKind: entry2.fromKind,
12348
+ fromId: entry2.fromId,
12349
+ toKind: entry2.toKind,
12350
+ toId: entry2.toId,
12351
+ props: entry2.validatedProps
12352
+ }
12353
+ });
12354
+ } else {
12355
+ const bestRow = liveRow ?? deletedRow;
12356
+ if (bestRow === void 0) {
12357
+ throw new Error("Expected at least one of liveRow or deletedRow");
12358
+ }
12359
+ toFetch.push({
12360
+ index,
12361
+ row: bestRow,
12362
+ isDeleted: liveRow === void 0,
12363
+ validatedProps: entry2.validatedProps,
12364
+ fromKind: entry2.fromKind,
12365
+ fromId: entry2.fromId,
12366
+ toKind: entry2.toKind,
12367
+ toId: entry2.toId
14038
12368
  });
14039
12369
  }
14040
12370
  }
12371
+ return { toCreate, toFetch, duplicateOf };
14041
12372
  }
14042
- }
14043
- async function insertUniquenessEntries(ctx, kind, id, props, constraints) {
14044
- for (const constraint of constraints) {
14045
- if (!checkWherePredicate(constraint, props)) {
14046
- continue;
12373
+ if (ifExists === "return") {
12374
+ const probe = partitionEntries(await fetchRowsByEndpoint(backend));
12375
+ const allFoundLive = probe.toCreate.length === 0 && probe.toFetch.every((entry2) => !entry2.isDeleted);
12376
+ if (allFoundLive) {
12377
+ const found = Array.from({ length: items.length });
12378
+ for (const entry2 of probe.toFetch) {
12379
+ found[entry2.index] = { edge: rowToEdge(entry2.row), action: "found" };
12380
+ }
12381
+ for (const { index, sourceIndex } of probe.duplicateOf) {
12382
+ found[index] = { edge: found[sourceIndex].edge, action: "found" };
12383
+ }
12384
+ return found;
14047
12385
  }
14048
- const key = computeUniqueKey(
14049
- props,
14050
- constraint.fields,
14051
- constraint.collation
14052
- );
14053
- await ctx.backend.insertUnique({
14054
- graphId: ctx.graphId,
14055
- nodeKind: kind,
14056
- constraintName: constraint.name,
14057
- key,
14058
- nodeId: id,
14059
- concreteKind: kind
14060
- });
14061
12386
  }
14062
- }
14063
- async function deleteUniquenessEntries(ctx, kind, props, constraints) {
14064
- for (const constraint of constraints) {
14065
- if (!checkWherePredicate(constraint, props)) {
14066
- continue;
14067
- }
14068
- const key = computeUniqueKey(
14069
- props,
14070
- constraint.fields,
14071
- constraint.collation
12387
+ return runInWriteTransaction(ctx, backend, async (target) => {
12388
+ const { toCreate, toFetch, duplicateOf } = partitionEntries(
12389
+ await fetchRowsByEndpoint(target)
14072
12390
  );
14073
- await ctx.backend.deleteUnique({
14074
- graphId: ctx.graphId,
14075
- nodeKind: kind,
14076
- constraintName: constraint.name,
14077
- key
14078
- });
14079
- }
14080
- }
14081
- async function updateUniquenessEntries(ctx, kind, id, oldProps, newProps, constraints) {
14082
- for (const constraint of constraints) {
14083
- const oldApplies = checkWherePredicate(constraint, oldProps);
14084
- const newApplies = checkWherePredicate(constraint, newProps);
14085
- const oldKey = oldApplies ? computeUniqueKey(oldProps, constraint.fields, constraint.collation) : void 0;
14086
- const newKey = newApplies ? computeUniqueKey(newProps, constraint.fields, constraint.collation) : void 0;
14087
- if (!oldApplies && !newApplies) {
14088
- continue;
14089
- }
14090
- if (oldApplies && newApplies && oldKey === newKey) {
14091
- continue;
14092
- }
14093
- if (oldApplies && oldKey !== void 0) {
14094
- await ctx.backend.deleteUnique({
14095
- graphId: ctx.graphId,
14096
- nodeKind: kind,
14097
- constraintName: constraint.name,
14098
- key: oldKey
14099
- });
14100
- }
14101
- if (newApplies && newKey !== void 0) {
14102
- const kindsToCheck = getKindsForUniquenessCheck(
14103
- kind,
14104
- constraint.scope,
14105
- ctx.registry
12391
+ const results = Array.from({ length: items.length });
12392
+ if (toCreate.length > 0) {
12393
+ const createInputs = toCreate.map((entry2) => entry2.input);
12394
+ const createdEdges = await executeEdgeCreateBatch(
12395
+ ctx,
12396
+ createInputs,
12397
+ target
14106
12398
  );
14107
- for (const kindToCheck of kindsToCheck) {
14108
- const existing = await ctx.backend.checkUnique({
12399
+ for (const [batchIndex, entry2] of toCreate.entries()) {
12400
+ results[entry2.index] = {
12401
+ edge: createdEdges[batchIndex],
12402
+ action: "created"
12403
+ };
12404
+ }
12405
+ }
12406
+ for (const entry2 of toFetch) {
12407
+ if (entry2.isDeleted) {
12408
+ const effectiveValidTo = entry2.row.valid_to;
12409
+ const constraintContext = {
14109
12410
  graphId: ctx.graphId,
14110
- nodeKind: kindToCheck,
14111
- constraintName: constraint.name,
14112
- key: newKey
14113
- });
14114
- if (existing && existing.node_id !== id) {
14115
- throw new UniquenessError({
14116
- constraintName: constraint.name,
14117
- kind: kindToCheck,
14118
- existingId: existing.node_id,
14119
- newId: id,
14120
- fields: constraint.fields
14121
- });
14122
- }
12411
+ registry: ctx.registry,
12412
+ backend: target
12413
+ };
12414
+ await checkCardinalityConstraint(
12415
+ constraintContext,
12416
+ kind,
12417
+ cardinality,
12418
+ entry2.fromKind,
12419
+ entry2.fromId,
12420
+ entry2.toKind,
12421
+ entry2.toId,
12422
+ effectiveValidTo
12423
+ );
12424
+ const edge = await executeEdgeUpsertUpdate(
12425
+ ctx,
12426
+ { id: entry2.row.id, props: entry2.validatedProps },
12427
+ target,
12428
+ { clearDeleted: true }
12429
+ );
12430
+ results[entry2.index] = { edge, action: "resurrected" };
12431
+ } else if (ifExists === "update") {
12432
+ const edge = await executeEdgeUpsertUpdate(
12433
+ ctx,
12434
+ { id: entry2.row.id, props: entry2.validatedProps },
12435
+ target
12436
+ );
12437
+ results[entry2.index] = { edge, action: "updated" };
12438
+ } else {
12439
+ results[entry2.index] = { edge: rowToEdge(entry2.row), action: "found" };
14123
12440
  }
14124
- await ctx.backend.insertUnique({
14125
- graphId: ctx.graphId,
14126
- nodeKind: kind,
14127
- constraintName: constraint.name,
14128
- key: newKey,
14129
- nodeId: id,
14130
- concreteKind: kind
14131
- });
14132
12441
  }
14133
- }
12442
+ for (const { index, sourceIndex } of duplicateOf) {
12443
+ const sourceResult = results[sourceIndex];
12444
+ results[index] = { edge: sourceResult.edge, action: "found" };
12445
+ }
12446
+ return results;
12447
+ });
14134
12448
  }
14135
-
14136
- // src/store/operations/node-operations.ts
14137
12449
  function getNodeRegistration(graph, kind) {
14138
12450
  const registration = graph.nodes[kind];
14139
12451
  if (registration === void 0) throw new KindNotFoundError(kind, "node");
@@ -14143,9 +12455,6 @@ var CACHE_KEY_SEPARATOR = "\0";
14143
12455
  function buildNodeCacheKey(graphId, kind, id) {
14144
12456
  return `${graphId}${CACHE_KEY_SEPARATOR}${kind}${CACHE_KEY_SEPARATOR}${id}`;
14145
12457
  }
14146
- function runInTopLevelTransactionForHistory(ctx, backend, fn) {
14147
- return ctx.historyEnabled ? runOptionallyInTransaction(backend, fn) : fn(backend);
14148
- }
14149
12458
  function buildUniqueCacheKey(graphId, nodeKind, constraintName, key) {
14150
12459
  return `${graphId}${CACHE_KEY_SEPARATOR}${nodeKind}${CACHE_KEY_SEPARATOR}${constraintName}${CACHE_KEY_SEPARATOR}${key}`;
14151
12460
  }
@@ -14195,15 +12504,6 @@ function resolveConstraint(graph, kind, constraintName) {
14195
12504
  }
14196
12505
  return constraint;
14197
12506
  }
14198
- function createUniquenessContext(graphId, registry, backend) {
14199
- return { graphId, registry, backend };
14200
- }
14201
- function createEmbeddingSyncContext(graphId, nodeKind, nodeId, backend) {
14202
- return { graphId, nodeKind, nodeId, backend };
14203
- }
14204
- function createFulltextSyncContext(graphId, nodeKind, nodeId, backend) {
14205
- return { graphId, nodeKind, nodeId, backend };
14206
- }
14207
12507
  function createNodeBatchValidationBackend(graphId, registry, backend) {
14208
12508
  const nodeCache = /* @__PURE__ */ new Map();
14209
12509
  const pendingNodes = /* @__PURE__ */ new Map();
@@ -14345,37 +12645,24 @@ async function validateAndPrepareNodeCreate(ctx, input, id, backend) {
14345
12645
  )
14346
12646
  };
14347
12647
  }
14348
- async function finalizeNodeCreate(ctx, prepared, backend) {
14349
- await insertUniquenessEntries(
14350
- createUniquenessContext(ctx.graphId, ctx.registry, backend),
14351
- prepared.kind,
14352
- prepared.id,
14353
- prepared.validatedProps,
14354
- prepared.uniqueConstraints
14355
- );
14356
- await syncEmbeddings(
14357
- createEmbeddingSyncContext(
14358
- ctx.graphId,
14359
- prepared.kind,
14360
- prepared.id,
14361
- backend
14362
- ),
14363
- prepared.nodeKind.schema,
14364
- prepared.validatedProps
14365
- );
14366
- await syncFulltext(
14367
- createFulltextSyncContext(ctx.graphId, prepared.kind, prepared.id, backend),
14368
- prepared.nodeKind.schema,
14369
- prepared.validatedProps
12648
+ async function finalizeNodeCreate(ctx, prepared, backend, lock) {
12649
+ await applyNodeInsertSideEffects(
12650
+ createNodeWriteContext(ctx.graphId, ctx.registry, lock),
12651
+ {
12652
+ kind: prepared.kind,
12653
+ id: prepared.id,
12654
+ schema: prepared.nodeKind.schema,
12655
+ props: prepared.validatedProps,
12656
+ uniqueConstraints: prepared.uniqueConstraints
12657
+ },
12658
+ backend
14370
12659
  );
14371
12660
  }
14372
- async function performNodeUpdate(ctx, input, backend, options) {
12661
+ async function performNodeUpdate(ctx, input, backend, lock, options) {
14373
12662
  const { kind, id } = input;
14374
12663
  const registration = getNodeRegistration(ctx.graph, kind);
14375
12664
  const existing = await backend.getNode(ctx.graphId, kind, id);
14376
- if (!existing || existing.deleted_at && !options?.clearDeleted) {
14377
- throw new NodeNotFoundError(kind, id);
14378
- }
12665
+ if (!existing) throw new NodeNotFoundError(kind, id);
14379
12666
  const existingProps = JSON.parse(existing.props);
14380
12667
  const mergedProps = { ...existingProps, ...input.props };
14381
12668
  const nodeKind = registration.type;
@@ -14385,69 +12672,29 @@ async function performNodeUpdate(ctx, input, backend, options) {
14385
12672
  id
14386
12673
  });
14387
12674
  const validTo = validateOptionalCanonicalIsoDate(input.validTo, "validTo");
14388
- await updateUniquenessEntries(
14389
- createUniquenessContext(ctx.graphId, ctx.registry, backend),
14390
- kind,
14391
- id,
14392
- existingProps,
12675
+ const writeContext = createNodeWriteContext(ctx.graphId, ctx.registry, lock);
12676
+ const shared = {
12677
+ schema: nodeKind.schema,
14393
12678
  validatedProps,
14394
- registration.unique ?? []
14395
- );
14396
- const updateParams = {
14397
- graphId: ctx.graphId,
14398
- kind,
14399
- id,
14400
- props: validatedProps,
14401
- incrementVersion: true
12679
+ uniqueConstraints: registration.unique ?? [],
12680
+ ...validTo !== void 0 && { validTo }
14402
12681
  };
14403
- if (validTo !== void 0) updateParams.validTo = validTo;
14404
- if (options?.clearDeleted) updateParams.clearDeleted = true;
14405
- const row = await backend.updateNode(updateParams);
14406
- await syncEmbeddings(
14407
- createEmbeddingSyncContext(ctx.graphId, kind, id, backend),
14408
- nodeKind.schema,
14409
- validatedProps
14410
- );
14411
- await syncFulltext(
14412
- createFulltextSyncContext(ctx.graphId, kind, id, backend),
14413
- nodeKind.schema,
14414
- validatedProps
12682
+ if (options?.clearDeleted) {
12683
+ const row2 = await applyNodeUpdate(
12684
+ writeContext,
12685
+ { ...shared, existing, clearDeleted: true },
12686
+ backend
12687
+ );
12688
+ return rowToNode(row2);
12689
+ }
12690
+ if (!isLiveNodeRow(existing)) throw new NodeNotFoundError(kind, id);
12691
+ const row = await applyNodeUpdate(
12692
+ writeContext,
12693
+ { ...shared, existing },
12694
+ backend
14415
12695
  );
14416
12696
  return rowToNode(row);
14417
12697
  }
14418
- async function enforceDeleteBehavior(ctx, kind, id, mode, backend, registration) {
14419
- const deleteBehavior = registration.onDelete ?? "restrict";
14420
- const connectedEdges = await backend.findEdgesConnectedTo({
14421
- graphId: ctx.graphId,
14422
- nodeKind: kind,
14423
- nodeId: id
14424
- });
14425
- if (connectedEdges.length === 0) return;
14426
- switch (deleteBehavior) {
14427
- case "restrict": {
14428
- const edgeKinds = [...new Set(connectedEdges.map((edge) => edge.kind))];
14429
- throw new RestrictedDeleteError({
14430
- nodeKind: kind,
14431
- nodeId: id,
14432
- edgeCount: connectedEdges.length,
14433
- edgeKinds
14434
- });
14435
- }
14436
- case "cascade":
14437
- case "disconnect": {
14438
- for (const edge of connectedEdges) {
14439
- await (mode === "hard" ? backend.hardDeleteEdge({
14440
- graphId: ctx.graphId,
14441
- id: edge.id
14442
- }) : backend.deleteEdge({
14443
- graphId: ctx.graphId,
14444
- id: edge.id
14445
- }));
14446
- }
14447
- break;
14448
- }
14449
- }
14450
- }
14451
12698
  async function prepareBatchCreates(ctx, inputs, backend) {
14452
12699
  const {
14453
12700
  backend: validationBackend,
@@ -14528,9 +12775,11 @@ async function executeNodeCreateInternal(ctx, input, backend, options) {
14528
12775
  const kind = input.kind;
14529
12776
  const id = input.id ?? generateId();
14530
12777
  const opContext = ctx.createOperationContext("create", "node", kind, id);
14531
- return ctx.withOperationHooks(
12778
+ return runHookedWriteOperation(
12779
+ ctx,
14532
12780
  opContext,
14533
- () => runInTopLevelTransactionForHistory(ctx, backend, async (target) => {
12781
+ backend,
12782
+ async (target, lock) => {
14534
12783
  const prepared = await validateAndPrepareNodeCreate(
14535
12784
  ctx,
14536
12785
  input,
@@ -14541,10 +12790,10 @@ async function executeNodeCreateInternal(ctx, input, backend, options) {
14541
12790
  {
14542
12791
  row = await target.insertNode(prepared.insertParams);
14543
12792
  }
14544
- await finalizeNodeCreate(ctx, prepared, target);
12793
+ await finalizeNodeCreate(ctx, prepared, target, lock);
14545
12794
  if (row === void 0) return;
14546
12795
  return rowToNode(row);
14547
- })
12796
+ }
14548
12797
  );
14549
12798
  }
14550
12799
  async function executeNodeCreate(ctx, input, backend) {
@@ -14559,7 +12808,7 @@ async function executeNodeCreate(ctx, input, backend) {
14559
12808
  }
14560
12809
  async function executeNodeCreateNoReturnBatch(ctx, inputs, backend) {
14561
12810
  if (inputs.length === 0) return;
14562
- await runInTopLevelTransactionForHistory(ctx, backend, async (target) => {
12811
+ await runInWriteTransaction(ctx, backend, async (target, lock) => {
14563
12812
  const { preparedCreates, batchInsertParams } = await prepareBatchCreates(
14564
12813
  ctx,
14565
12814
  inputs,
@@ -14567,13 +12816,13 @@ async function executeNodeCreateNoReturnBatch(ctx, inputs, backend) {
14567
12816
  );
14568
12817
  await runInsertBatch(nodeInsertDispatch(target), batchInsertParams);
14569
12818
  for (const prepared of preparedCreates) {
14570
- await finalizeNodeCreate(ctx, prepared, target);
12819
+ await finalizeNodeCreate(ctx, prepared, target, lock);
14571
12820
  }
14572
12821
  });
14573
12822
  }
14574
12823
  async function executeNodeCreateBatch(ctx, inputs, backend) {
14575
12824
  if (inputs.length === 0) return [];
14576
- return runInTopLevelTransactionForHistory(ctx, backend, async (target) => {
12825
+ return runInWriteTransaction(ctx, backend, async (target, lock) => {
14577
12826
  const { preparedCreates, batchInsertParams } = await prepareBatchCreates(
14578
12827
  ctx,
14579
12828
  inputs,
@@ -14584,7 +12833,7 @@ async function executeNodeCreateBatch(ctx, inputs, backend) {
14584
12833
  batchInsertParams
14585
12834
  );
14586
12835
  for (const prepared of preparedCreates) {
14587
- await finalizeNodeCreate(ctx, prepared, target);
12836
+ await finalizeNodeCreate(ctx, prepared, target, lock);
14588
12837
  }
14589
12838
  return rows.map((row) => rowToNode(row));
14590
12839
  });
@@ -14596,71 +12845,69 @@ async function executeNodeUpdate(ctx, input, backend, options) {
14596
12845
  input.kind,
14597
12846
  input.id
14598
12847
  );
14599
- return ctx.withOperationHooks(
12848
+ return runHookedWriteOperation(
12849
+ ctx,
14600
12850
  opContext,
14601
- () => runInTopLevelTransactionForHistory(
14602
- ctx,
14603
- backend,
14604
- (target) => performNodeUpdate(ctx, input, target, options)
14605
- )
12851
+ backend,
12852
+ (target, lock) => performNodeUpdate(ctx, input, target, lock, options)
14606
12853
  );
14607
12854
  }
14608
12855
  async function executeNodeUpsertUpdate(ctx, input, backend, options) {
14609
- return runInTopLevelTransactionForHistory(
12856
+ return runInWriteTransaction(
14610
12857
  ctx,
14611
12858
  backend,
14612
- (target) => performNodeUpdate(ctx, input, target, options)
12859
+ (target, lock) => performNodeUpdate(ctx, input, target, lock, options)
14613
12860
  );
14614
12861
  }
14615
12862
  async function executeNodeDelete(ctx, kind, id, backend) {
12863
+ const gate = await backend.getNode(ctx.graphId, kind, id);
12864
+ if (!gate || gate.deleted_at) return;
14616
12865
  const opContext = ctx.createOperationContext("delete", "node", kind, id);
14617
- return ctx.withOperationHooks(opContext, async () => {
14618
- const registration = getNodeRegistration(ctx.graph, kind);
14619
- const preflight = await backend.getNode(ctx.graphId, kind, id);
14620
- if (!preflight || preflight.deleted_at) return;
14621
- const softDelete = async (target) => {
14622
- const existing = await target.getNode(ctx.graphId, kind, id);
14623
- if (!existing || existing.deleted_at) return;
14624
- const existingProps = JSON.parse(existing.props);
14625
- await enforceDeleteBehavior(ctx, kind, id, "soft", target, registration);
14626
- await target.deleteNode({ graphId: ctx.graphId, kind, id });
14627
- await deleteUniquenessEntries(
14628
- createUniquenessContext(ctx.graphId, ctx.registry, target),
14629
- kind,
14630
- existingProps,
14631
- registration.unique ?? []
14632
- );
14633
- const nodeKind = registration.type;
14634
- await deleteNodeEmbeddings(
14635
- createEmbeddingSyncContext(ctx.graphId, kind, id, target),
14636
- nodeKind.schema
14637
- );
14638
- await deleteNodeFulltext(
14639
- createFulltextSyncContext(ctx.graphId, kind, id, target),
14640
- nodeKind.schema
12866
+ return runHookedWriteOperation(
12867
+ ctx,
12868
+ opContext,
12869
+ backend,
12870
+ async (target, lock) => {
12871
+ const registration = getNodeRegistration(ctx.graph, kind);
12872
+ const preflight = await target.getNode(ctx.graphId, kind, id);
12873
+ if (!preflight || !isLiveNodeRow(preflight)) return;
12874
+ await applyNodeSoftDelete(
12875
+ createNodeWriteContext(ctx.graphId, ctx.registry, lock),
12876
+ {
12877
+ existing: preflight,
12878
+ schema: registration.type.schema,
12879
+ uniqueConstraints: registration.unique ?? [],
12880
+ onDelete: registration.onDelete
12881
+ },
12882
+ target
14641
12883
  );
14642
- };
14643
- await runOptionallyInTransaction(backend, (target) => softDelete(target));
14644
- });
12884
+ }
12885
+ );
14645
12886
  }
14646
12887
  async function executeNodeHardDelete(ctx, kind, id, backend) {
12888
+ const gate = await backend.getNode(ctx.graphId, kind, id);
12889
+ if (!gate) return;
14647
12890
  const opContext = ctx.createOperationContext("delete", "node", kind, id);
14648
- return ctx.withOperationHooks(opContext, async () => {
14649
- const registration = getNodeRegistration(ctx.graph, kind);
14650
- const preflight = await backend.getNode(ctx.graphId, kind, id);
14651
- if (!preflight) return;
14652
- const hardDelete = async (target) => {
14653
- const existing = await target.getNode(ctx.graphId, kind, id);
14654
- if (!existing) return;
14655
- await enforceDeleteBehavior(ctx, kind, id, "hard", target, registration);
14656
- await target.hardDeleteNode({ graphId: ctx.graphId, kind, id });
14657
- await deleteNodeEmbeddings(
14658
- createEmbeddingSyncContext(ctx.graphId, kind, id, target),
14659
- registration.type.schema
12891
+ return runHookedWriteOperation(
12892
+ ctx,
12893
+ opContext,
12894
+ backend,
12895
+ async (target, lock) => {
12896
+ const registration = getNodeRegistration(ctx.graph, kind);
12897
+ const preflight = await target.getNode(ctx.graphId, kind, id);
12898
+ if (!preflight) return;
12899
+ await applyNodeHardDelete(
12900
+ createNodeWriteContext(ctx.graphId, ctx.registry, lock),
12901
+ {
12902
+ kind,
12903
+ id,
12904
+ schema: registration.type.schema,
12905
+ onDelete: registration.onDelete
12906
+ },
12907
+ target
14660
12908
  );
14661
- };
14662
- await runOptionallyInTransaction(backend, (target) => hardDelete(target));
14663
- });
12909
+ }
12910
+ );
14664
12911
  }
14665
12912
  async function executeNodeGetOrCreateByConstraint(ctx, kind, constraintName, props, backend, options) {
14666
12913
  const ifExists = options?.ifExists ?? "return";
@@ -14689,51 +12936,59 @@ async function executeNodeGetOrCreateByConstraint(ctx, kind, constraintName, pro
14689
12936
  constraint.scope,
14690
12937
  ctx.registry
14691
12938
  );
14692
- const existingUniqueRow = await findUniqueRowAcrossKinds(
14693
- backend,
14694
- ctx.graphId,
14695
- constraint.name,
14696
- key,
14697
- kindsToCheck,
14698
- true
14699
- );
14700
- if (existingUniqueRow === void 0) {
14701
- const node = await executeNodeCreate(
14702
- ctx,
14703
- { kind, props: validatedProps },
14704
- backend
12939
+ async function attempt() {
12940
+ const existingUniqueRow = await findUniqueRowAcrossKinds(
12941
+ backend,
12942
+ ctx.graphId,
12943
+ constraint.name,
12944
+ key,
12945
+ kindsToCheck,
12946
+ true
14705
12947
  );
14706
- return { node, action: "created" };
14707
- }
14708
- const existingRow = await backend.getNode(
14709
- ctx.graphId,
14710
- existingUniqueRow.concrete_kind,
14711
- existingUniqueRow.node_id
14712
- );
14713
- if (existingRow === void 0) {
14714
- const node = await executeNodeCreate(
14715
- ctx,
14716
- { kind, props: validatedProps },
14717
- backend
12948
+ if (existingUniqueRow === void 0) {
12949
+ const node = await executeNodeCreate(
12950
+ ctx,
12951
+ { kind, props: validatedProps },
12952
+ backend
12953
+ );
12954
+ return { node, action: "created" };
12955
+ }
12956
+ const existingRow = await backend.getNode(
12957
+ ctx.graphId,
12958
+ existingUniqueRow.concrete_kind,
12959
+ existingUniqueRow.node_id
14718
12960
  );
14719
- return { node, action: "created" };
12961
+ if (existingRow === void 0) {
12962
+ const node = await executeNodeCreate(
12963
+ ctx,
12964
+ { kind, props: validatedProps },
12965
+ backend
12966
+ );
12967
+ return { node, action: "created" };
12968
+ }
12969
+ const isSoftDeleted = existingRow.deleted_at !== void 0;
12970
+ if (isSoftDeleted || ifExists === "update") {
12971
+ const concreteKind = existingUniqueRow.concrete_kind;
12972
+ const node = await executeNodeUpsertUpdate(
12973
+ ctx,
12974
+ {
12975
+ kind: concreteKind,
12976
+ id: existingRow.id,
12977
+ props: validatedProps
12978
+ },
12979
+ backend,
12980
+ { clearDeleted: isSoftDeleted }
12981
+ );
12982
+ return { node, action: isSoftDeleted ? "resurrected" : "updated" };
12983
+ }
12984
+ return { node: rowToNode(existingRow), action: "found" };
14720
12985
  }
14721
- const isSoftDeleted = existingRow.deleted_at !== void 0;
14722
- if (isSoftDeleted || ifExists === "update") {
14723
- const concreteKind = existingUniqueRow.concrete_kind;
14724
- const node = await executeNodeUpsertUpdate(
14725
- ctx,
14726
- {
14727
- kind: concreteKind,
14728
- id: existingRow.id,
14729
- props: validatedProps
14730
- },
14731
- backend,
14732
- { clearDeleted: isSoftDeleted }
14733
- );
14734
- return { node, action: isSoftDeleted ? "resurrected" : "updated" };
12986
+ try {
12987
+ return await attempt();
12988
+ } catch (error) {
12989
+ if (!(error instanceof UniquenessError)) throw error;
12990
+ return attempt();
14735
12991
  }
14736
- return { node: rowToNode(existingRow), action: "found" };
14737
12992
  }
14738
12993
  async function executeNodeFindByConstraint(ctx, kind, constraintName, props, backend) {
14739
12994
  const registration = getNodeRegistration(ctx.graph, kind);
@@ -15347,6 +13602,9 @@ var CAUGHT_UP_VERB_DETAILS = {
15347
13602
  function rowMapperConfigFor(backend) {
15348
13603
  return backend.dialect === "postgres" ? POSTGRES_ROW_MAPPER_CONFIG : SQLITE_ROW_MAPPER_CONFIG;
15349
13604
  }
13605
+ function asError(error) {
13606
+ return error instanceof Error ? error : new Error(String(error));
13607
+ }
15350
13608
  var Store = class _Store {
15351
13609
  #graph;
15352
13610
  /**
@@ -15660,7 +13918,9 @@ var Store = class _Store {
15660
13918
  * Node operations bound to this store instance.
15661
13919
  */
15662
13920
  get #nodeOperations() {
15663
- const ctx = this.#createNodeOperationContext();
13921
+ return this.#buildNodeOperations(this.#createNodeOperationContext());
13922
+ }
13923
+ #buildNodeOperations(ctx) {
15664
13924
  return {
15665
13925
  defaultTemporalMode: this.#graph.defaults.temporalMode,
15666
13926
  rowToNode: (row) => rowToNode(row),
@@ -15716,7 +13976,9 @@ var Store = class _Store {
15716
13976
  * Edge operations bound to this store instance.
15717
13977
  */
15718
13978
  get #edgeOperations() {
15719
- const ctx = this.#createEdgeOperationContext();
13979
+ return this.#buildEdgeOperations(this.#createEdgeOperationContext());
13980
+ }
13981
+ #buildEdgeOperations(ctx) {
15720
13982
  return {
15721
13983
  defaultTemporalMode: this.#graph.defaults.temporalMode,
15722
13984
  rowToEdge: (row) => rowToEdge(row),
@@ -16128,13 +14390,36 @@ var Store = class _Store {
16128
14390
  return invoke({
16129
14391
  nodes: this.nodes,
16130
14392
  edges: this.edges,
16131
- backend: this.#backend
14393
+ backend: this.#backend,
14394
+ getNodeCollection: (kind) => this.getNodeCollection(kind),
14395
+ // No transaction, no deferral: operations apply as they happen, so
14396
+ // their hooks fire immediately.
14397
+ runNodeOperationHooks: (operation, kind, id, hookedFunction) => this.#withOperationHooks(
14398
+ this.#createOperationContext(operation, "node", kind, id),
14399
+ hookedFunction
14400
+ )
16132
14401
  });
16133
14402
  }
16134
- return this.#backend.transaction(
16135
- async (txBackend, sql22) => invoke(this.#buildTransactionContext(txBackend, sql22)),
16136
- options
16137
- );
14403
+ const pending = [];
14404
+ const runHooks = this.#createBufferedHookRunner(pending);
14405
+ try {
14406
+ const result = await this.#backend.transaction(
14407
+ async (txBackend, sql17) => invoke(this.#buildTransactionContext(txBackend, sql17, runHooks)),
14408
+ options
14409
+ );
14410
+ for (const outcome of pending) {
14411
+ this.#hooks.onOperationEnd?.(outcome.ctx, {
14412
+ durationMs: outcome.durationMs
14413
+ });
14414
+ }
14415
+ return result;
14416
+ } catch (error) {
14417
+ const failure = asError(error);
14418
+ for (const outcome of pending) {
14419
+ this.#hooks.onError?.(outcome.ctx, failure);
14420
+ }
14421
+ throw error;
14422
+ }
16138
14423
  }
16139
14424
  /**
16140
14425
  * Adopts a caller-owned, already-open Drizzle transaction so the graph
@@ -16234,6 +14519,9 @@ var Store = class _Store {
16234
14519
  backend: txBackend,
16235
14520
  flush: () => Promise.resolve()
16236
14521
  };
14522
+ if (this.#captureEnabled) {
14523
+ await lockRecordedGraphWrite(scope.backend, this.graphId);
14524
+ }
16237
14525
  const invoke = fn;
16238
14526
  const result = await invoke(
16239
14527
  this.#buildTransactionContext(scope.backend, externalTx)
@@ -16251,15 +14539,16 @@ var Store = class _Store {
16251
14539
  * (#140); when history capture is enabled it is replaced with a fail-loud
16252
14540
  * guard because raw graph writes would bypass recorded-time capture.
16253
14541
  */
16254
- #buildTransactionContext(txBackend, sql22) {
14542
+ #buildTransactionContext(txBackend, sql17, runHooks = this.#immediateHookRunner()) {
16255
14543
  const txNodeOperations = {
16256
- ...this.#nodeOperations,
14544
+ ...this.#buildNodeOperations(this.#createNodeOperationContext(runHooks)),
16257
14545
  createQuery: () => this.#createQueryForBackend(txBackend)
16258
14546
  };
16259
14547
  const txEdgeOperations = {
16260
- ...this.#edgeOperations,
14548
+ ...this.#buildEdgeOperations(this.#createEdgeOperationContext(runHooks)),
16261
14549
  createQuery: () => this.#createQueryForBackend(txBackend)
16262
14550
  };
14551
+ const runNodeOperationHooks = (operation, kind, id, fn) => runHooks(this.#createOperationContext(operation, "node", kind, id), fn);
16263
14552
  const nodes = createNodeCollectionsProxy(
16264
14553
  this.#graph,
16265
14554
  this.graphId,
@@ -16274,9 +14563,28 @@ var Store = class _Store {
16274
14563
  txBackend,
16275
14564
  txEdgeOperations
16276
14565
  );
16277
- if (sql22 === void 0) return { nodes, edges, backend: txBackend };
16278
- const exposedSql = this.#captureEnabled ? createHistoryUnsafeSqlRef() : sql22;
16279
- return { nodes, edges, sql: exposedSql, backend: txBackend };
14566
+ const getNodeCollection = (kind) => {
14567
+ if (!Object.hasOwn(this.#graph.nodes, kind)) return void 0;
14568
+ return nodes[kind];
14569
+ };
14570
+ if (sql17 === void 0) {
14571
+ return {
14572
+ nodes,
14573
+ edges,
14574
+ backend: txBackend,
14575
+ getNodeCollection,
14576
+ runNodeOperationHooks
14577
+ };
14578
+ }
14579
+ const exposedSql = this.#captureEnabled ? createHistoryUnsafeSqlRef() : sql17;
14580
+ return {
14581
+ nodes,
14582
+ edges,
14583
+ sql: exposedSql,
14584
+ backend: txBackend,
14585
+ getNodeCollection,
14586
+ runNodeOperationHooks
14587
+ };
16280
14588
  }
16281
14589
  // === Graph Lifecycle ===
16282
14590
  /**
@@ -16937,23 +15245,27 @@ var Store = class _Store {
16937
15245
  await this.#backend.close();
16938
15246
  }
16939
15247
  // === Internal: Operation Contexts ===
16940
- #createNodeOperationContext() {
15248
+ #immediateHookRunner() {
15249
+ return (ctx, fn) => this.#withOperationHooks(ctx, fn);
15250
+ }
15251
+ #createNodeOperationContext(runHooks = this.#immediateHookRunner()) {
16941
15252
  return {
16942
15253
  graph: this.#graph,
16943
15254
  graphId: this.graphId,
16944
15255
  historyEnabled: this.#captureEnabled,
16945
15256
  registry: this.#registry,
16946
15257
  createOperationContext: (operation, entity, kind, id) => this.#createOperationContext(operation, entity, kind, id),
16947
- withOperationHooks: (ctx, fn) => this.#withOperationHooks(ctx, fn)
15258
+ withOperationHooks: runHooks
16948
15259
  };
16949
15260
  }
16950
- #createEdgeOperationContext() {
15261
+ #createEdgeOperationContext(runHooks = this.#immediateHookRunner()) {
16951
15262
  return {
16952
15263
  graph: this.#graph,
16953
15264
  graphId: this.graphId,
15265
+ historyEnabled: this.#captureEnabled,
16954
15266
  registry: this.#registry,
16955
15267
  createOperationContext: (operation, entity, kind, id) => this.#createOperationContext(operation, entity, kind, id),
16956
- withOperationHooks: (ctx, fn) => this.#withOperationHooks(ctx, fn)
15268
+ withOperationHooks: runHooks
16957
15269
  };
16958
15270
  }
16959
15271
  // === Internal: Hook Helpers ===
@@ -16983,13 +15295,34 @@ var Store = class _Store {
16983
15295
  });
16984
15296
  return result;
16985
15297
  } catch (error) {
16986
- this.#hooks.onError?.(
16987
- ctx,
16988
- error instanceof Error ? error : new Error(String(error))
16989
- );
15298
+ this.#hooks.onError?.(ctx, asError(error));
16990
15299
  throw error;
16991
15300
  }
16992
15301
  }
15302
+ /**
15303
+ * A hook runner for operations that execute INSIDE an explicit
15304
+ * `store.transaction`: `onOperationStart` (and a failed operation's
15305
+ * `onError`) fire immediately — the attempt and the failure are real when
15306
+ * they happen — but a successful operation's `onOperationEnd` is only
15307
+ * BUFFERED. `transaction()` flushes the buffer after the backend COMMIT
15308
+ * succeeds, or converts every buffered success into `onError` when the
15309
+ * transaction fails, so `onOperationEnd` always means durably committed
15310
+ * even for operations nested in a caller-controlled transaction.
15311
+ */
15312
+ #createBufferedHookRunner(pending) {
15313
+ return async (ctx, fn) => {
15314
+ this.#hooks.onOperationStart?.(ctx);
15315
+ const startTime = Date.now();
15316
+ try {
15317
+ const result = await fn();
15318
+ pending.push({ ctx, durationMs: Date.now() - startTime });
15319
+ return result;
15320
+ } catch (error) {
15321
+ this.#hooks.onError?.(ctx, asError(error));
15322
+ throw error;
15323
+ }
15324
+ };
15325
+ }
16993
15326
  // === Internal: Temporal Filtering ===
16994
15327
  #temporalRowMatcher(options) {
16995
15328
  const { temporalMode, asOf } = resolveTemporalReadParams(
@@ -17100,6 +15433,6 @@ async function createVerifiedStore(graph, backend, options) {
17100
15433
  return [store, result];
17101
15434
  }
17102
15435
 
17103
- export { DEFAULT_SQL_SCHEMA, ExecutableAggregateQuery, ExecutableQuery, MAX_EXPLICIT_RECURSIVE_DEPTH, MAX_RECURSIVE_DEPTH, PreparedQuery, QueryBuilder, RecordedStoreView, StoreSearch, StoreView, UnionableQuery, asCompiledRowsSql, asCompiledSelectSql, asCompiledStatementSql, asRecordedInstant, avg, composeFragments, computeUniqueKey, count, countDistinct, countDistinctEdges, countEdges, createFragment, createQueryBuilder, createSqlSchema, createStore, createStoreWithSchema, createVerifiedStore, defineSubgraphProject, field, generateId, having, havingEq, havingGt, havingGte, havingLt, havingLte, limitFragment, max, min, offsetFragment, orderByFragment, recordedRelation, sum };
17104
- //# sourceMappingURL=chunk-WLP6TKFB.js.map
17105
- //# sourceMappingURL=chunk-WLP6TKFB.js.map
15436
+ 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 };
15437
+ //# sourceMappingURL=chunk-WLRIWYHQ.js.map
15438
+ //# sourceMappingURL=chunk-WLRIWYHQ.js.map