@nicia-ai/typegraph 0.44.0 → 0.46.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 (392) hide show
  1. package/dist/backend/drizzle/indexes.cjs +11 -11
  2. package/dist/backend/drizzle/indexes.d.cts +1 -1
  3. package/dist/backend/drizzle/indexes.d.ts +1 -1
  4. package/dist/backend/drizzle/indexes.js +7 -7
  5. package/dist/backend/index.cjs +53 -54
  6. package/dist/backend/index.d.cts +3 -3
  7. package/dist/backend/index.d.ts +3 -3
  8. package/dist/backend/index.js +13 -14
  9. package/dist/backend/postgres/index.cjs +29 -30
  10. package/dist/backend/postgres/index.d.cts +7 -7
  11. package/dist/backend/postgres/index.d.ts +7 -7
  12. package/dist/backend/postgres/index.js +16 -17
  13. package/dist/backend/postgres/pglite-store.cjs +48 -43
  14. package/dist/backend/postgres/pglite-store.cjs.map +1 -1
  15. package/dist/backend/postgres/pglite-store.d.cts +15 -9
  16. package/dist/backend/postgres/pglite-store.d.ts +15 -9
  17. package/dist/backend/postgres/pglite-store.js +44 -39
  18. package/dist/backend/postgres/pglite-store.js.map +1 -1
  19. package/dist/backend/postgres/pglite.cjs +18 -19
  20. package/dist/backend/postgres/pglite.d.cts +4 -4
  21. package/dist/backend/postgres/pglite.d.ts +4 -4
  22. package/dist/backend/postgres/pglite.js +17 -18
  23. package/dist/backend/sqlite/index.cjs +26 -27
  24. package/dist/backend/sqlite/index.d.cts +6 -6
  25. package/dist/backend/sqlite/index.d.ts +6 -6
  26. package/dist/backend/sqlite/index.js +14 -15
  27. package/dist/backend/sqlite/libsql.cjs +21 -22
  28. package/dist/backend/sqlite/libsql.cjs.map +1 -1
  29. package/dist/backend/sqlite/libsql.d.cts +15 -8
  30. package/dist/backend/sqlite/libsql.d.ts +15 -8
  31. package/dist/backend/sqlite/libsql.js +17 -18
  32. package/dist/backend/sqlite/libsql.js.map +1 -1
  33. package/dist/backend/sqlite/local-store.cjs +49 -44
  34. package/dist/backend/sqlite/local-store.cjs.map +1 -1
  35. package/dist/backend/sqlite/local-store.d.cts +15 -10
  36. package/dist/backend/sqlite/local-store.d.ts +15 -10
  37. package/dist/backend/sqlite/local-store.js +45 -40
  38. package/dist/backend/sqlite/local-store.js.map +1 -1
  39. package/dist/backend/sqlite/local.cjs +19 -20
  40. package/dist/backend/sqlite/local.d.cts +3 -3
  41. package/dist/backend/sqlite/local.d.ts +3 -3
  42. package/dist/backend/sqlite/local.js +17 -18
  43. package/dist/{chunk-XMYJY7NI.js → chunk-2LG2XB2M.js} +3 -3
  44. package/dist/{chunk-XMYJY7NI.js.map → chunk-2LG2XB2M.js.map} +1 -1
  45. package/dist/{chunk-QGKILWGZ.cjs → chunk-2NA5DYLO.cjs} +4 -4
  46. package/dist/{chunk-QGKILWGZ.cjs.map → chunk-2NA5DYLO.cjs.map} +1 -1
  47. package/dist/{chunk-5MUSU4ZP.cjs → chunk-3DHZCOT7.cjs} +20 -20
  48. package/dist/chunk-3DHZCOT7.cjs.map +1 -0
  49. package/dist/{chunk-OVUUTCPJ.cjs → chunk-4UGOKMI5.cjs} +268 -150
  50. package/dist/chunk-4UGOKMI5.cjs.map +1 -0
  51. package/dist/{chunk-FRHHDHKH.js → chunk-5262ZMTR.js} +26 -5
  52. package/dist/chunk-5262ZMTR.js.map +1 -0
  53. package/dist/{chunk-HOIFFY4M.js → chunk-5763DULU.js} +248 -76
  54. package/dist/chunk-5763DULU.js.map +1 -0
  55. package/dist/{chunk-7G32LIQM.js → chunk-5UVRGXD3.js} +277 -7
  56. package/dist/chunk-5UVRGXD3.js.map +1 -0
  57. package/dist/{chunk-IIT5K7SK.cjs → chunk-62ZP23AW.cjs} +15 -15
  58. package/dist/{chunk-IIT5K7SK.cjs.map → chunk-62ZP23AW.cjs.map} +1 -1
  59. package/dist/{chunk-S4KQ5LV4.js → chunk-775A4ZMN.js} +40 -6
  60. package/dist/chunk-775A4ZMN.js.map +1 -0
  61. package/dist/{chunk-QYWINYEA.cjs → chunk-7BQXPCGN.cjs} +830 -170
  62. package/dist/chunk-7BQXPCGN.cjs.map +1 -0
  63. package/dist/{chunk-FJMPR3OH.cjs → chunk-7EO3TCQB.cjs} +33 -7
  64. package/dist/chunk-7EO3TCQB.cjs.map +1 -0
  65. package/dist/chunk-7GT72YHH.cjs +1979 -0
  66. package/dist/chunk-7GT72YHH.cjs.map +1 -0
  67. package/dist/{chunk-SQRD2CYF.js → chunk-7VQXLT5O.js} +717 -76
  68. package/dist/chunk-7VQXLT5O.js.map +1 -0
  69. package/dist/{chunk-BCP7LLOY.js → chunk-7Z5OR7KO.js} +2706 -625
  70. package/dist/chunk-7Z5OR7KO.js.map +1 -0
  71. package/dist/chunk-A4OC4DVN.cjs +12 -0
  72. package/dist/{chunk-4SZ6XPTG.cjs.map → chunk-A4OC4DVN.cjs.map} +1 -1
  73. package/dist/{chunk-TDXIBCGY.cjs → chunk-A7J2YDDI.cjs} +5 -3
  74. package/dist/chunk-A7J2YDDI.cjs.map +1 -0
  75. package/dist/{chunk-FMMIX6N4.js → chunk-AKLS3BYZ.js} +56 -5
  76. package/dist/chunk-AKLS3BYZ.js.map +1 -0
  77. package/dist/{chunk-D7PG5D5K.js → chunk-AMKPPFUO.js} +32 -6
  78. package/dist/chunk-AMKPPFUO.js.map +1 -0
  79. package/dist/{chunk-PAW2XUZN.js → chunk-ASTI57H3.js} +3 -3
  80. package/dist/{chunk-PAW2XUZN.js.map → chunk-ASTI57H3.js.map} +1 -1
  81. package/dist/{chunk-FMGEZBAQ.js → chunk-BKCUOV7A.js} +27 -5
  82. package/dist/chunk-BKCUOV7A.js.map +1 -0
  83. package/dist/chunk-BSFJMMV3.cjs +268 -0
  84. package/dist/chunk-BSFJMMV3.cjs.map +1 -0
  85. package/dist/{chunk-63KCKSSH.js → chunk-C23WSN5E.js} +209 -24
  86. package/dist/chunk-C23WSN5E.js.map +1 -0
  87. package/dist/{chunk-JRP3VGHS.js → chunk-CBTIUB76.js} +3 -3
  88. package/dist/{chunk-JRP3VGHS.js.map → chunk-CBTIUB76.js.map} +1 -1
  89. package/dist/chunk-CNV4TXJG.js +58 -0
  90. package/dist/chunk-CNV4TXJG.js.map +1 -0
  91. package/dist/{chunk-QB6VMOJ2.cjs → chunk-CXBHOOIQ.cjs} +45 -10
  92. package/dist/chunk-CXBHOOIQ.cjs.map +1 -0
  93. package/dist/{chunk-46GDII45.js → chunk-DCQVHAYQ.js} +5 -5
  94. package/dist/{chunk-46GDII45.js.map → chunk-DCQVHAYQ.js.map} +1 -1
  95. package/dist/chunk-DLNROMQ7.js +306 -0
  96. package/dist/chunk-DLNROMQ7.js.map +1 -0
  97. package/dist/chunk-DYATYRY4.js +47 -0
  98. package/dist/chunk-DYATYRY4.js.map +1 -0
  99. package/dist/chunk-E4WNQBEF.cjs +217 -0
  100. package/dist/chunk-E4WNQBEF.cjs.map +1 -0
  101. package/dist/{chunk-G6Q5UM7U.js → chunk-FWJKWH6U.js} +5 -3
  102. package/dist/chunk-FWJKWH6U.js.map +1 -0
  103. package/dist/{chunk-BNUFUFAU.js → chunk-HFHTMECK.js} +7 -7
  104. package/dist/chunk-HFHTMECK.js.map +1 -0
  105. package/dist/{chunk-TFMVL4QH.js → chunk-I5MOXA4H.js} +20 -14
  106. package/dist/chunk-I5MOXA4H.js.map +1 -0
  107. package/dist/{chunk-4MB3AQAQ.js → chunk-J3WZ3FA2.js} +77 -3
  108. package/dist/chunk-J3WZ3FA2.js.map +1 -0
  109. package/dist/{chunk-UR7W5YQK.js → chunk-JBQQ7THH.js} +5 -3
  110. package/dist/chunk-JBQQ7THH.js.map +1 -0
  111. package/dist/chunk-JIVMTW7W.js +3393 -0
  112. package/dist/chunk-JIVMTW7W.js.map +1 -0
  113. package/dist/{chunk-2Y4IFKW7.js → chunk-JXA2ZP2Q.js} +6 -17
  114. package/dist/chunk-JXA2ZP2Q.js.map +1 -0
  115. package/dist/{chunk-GYA4JRP5.js → chunk-JXXDZTMN.js} +2 -2
  116. package/dist/chunk-JXXDZTMN.js.map +1 -0
  117. package/dist/chunk-JZUR7ZWE.js +254 -0
  118. package/dist/chunk-JZUR7ZWE.js.map +1 -0
  119. package/dist/chunk-K5ELX2GT.js +248 -0
  120. package/dist/chunk-K5ELX2GT.js.map +1 -0
  121. package/dist/chunk-LBJKZXDE.cjs +257 -0
  122. package/dist/chunk-LBJKZXDE.cjs.map +1 -0
  123. package/dist/{chunk-FGO5JFVJ.cjs → chunk-M4BDU5RX.cjs} +12 -12
  124. package/dist/{chunk-FGO5JFVJ.cjs.map → chunk-M4BDU5RX.cjs.map} +1 -1
  125. package/dist/{chunk-MIJKEVV7.js → chunk-MSY7FSX4.js} +37 -4
  126. package/dist/chunk-MSY7FSX4.js.map +1 -0
  127. package/dist/{chunk-ST3LPB2V.cjs → chunk-MUK4BU7K.cjs} +788 -146
  128. package/dist/chunk-MUK4BU7K.cjs.map +1 -0
  129. package/dist/{chunk-MCHP7RHZ.cjs → chunk-MXJDTH5B.cjs} +64 -41
  130. package/dist/chunk-MXJDTH5B.cjs.map +1 -0
  131. package/dist/{chunk-2VLBGQBY.cjs → chunk-N3257XE6.cjs} +724 -577
  132. package/dist/chunk-N3257XE6.cjs.map +1 -0
  133. package/dist/chunk-N66HCGCY.cjs +57 -0
  134. package/dist/chunk-N66HCGCY.cjs.map +1 -0
  135. package/dist/{chunk-CPEL3YIR.cjs → chunk-NFCVHQ5P.cjs} +55 -22
  136. package/dist/chunk-NFCVHQ5P.cjs.map +1 -0
  137. package/dist/{chunk-C4F7VJ5D.cjs → chunk-NI72YOJB.cjs} +84 -2
  138. package/dist/chunk-NI72YOJB.cjs.map +1 -0
  139. package/dist/{chunk-EE7N5XXQ.cjs → chunk-NK5Q7UNE.cjs} +10 -21
  140. package/dist/chunk-NK5Q7UNE.cjs.map +1 -0
  141. package/dist/{chunk-PHXITZE4.js → chunk-NMWDXFPH.js} +4 -4
  142. package/dist/{chunk-PHXITZE4.js.map → chunk-NMWDXFPH.js.map} +1 -1
  143. package/dist/{chunk-FSXTPBRN.js → chunk-NQ6BRJBE.js} +416 -850
  144. package/dist/chunk-NQ6BRJBE.js.map +1 -0
  145. package/dist/chunk-OH5E5KIO.js +2057 -0
  146. package/dist/chunk-OH5E5KIO.js.map +1 -0
  147. package/dist/chunk-ONU3HIED.cjs +2082 -0
  148. package/dist/chunk-ONU3HIED.cjs.map +1 -0
  149. package/dist/{chunk-STQ3BSED.cjs → chunk-PNN7RHVR.cjs} +66 -44
  150. package/dist/chunk-PNN7RHVR.cjs.map +1 -0
  151. package/dist/{chunk-6DCB664D.js → chunk-PQBQG7IM.js} +3 -3
  152. package/dist/{chunk-6DCB664D.js.map → chunk-PQBQG7IM.js.map} +1 -1
  153. package/dist/{chunk-ACSVZPK7.cjs → chunk-PWLJ77UV.cjs} +5 -3
  154. package/dist/chunk-PWLJ77UV.cjs.map +1 -0
  155. package/dist/chunk-Q7Q4WSLJ.cjs +328 -0
  156. package/dist/chunk-Q7Q4WSLJ.cjs.map +1 -0
  157. package/dist/{chunk-BEFM74L2.js → chunk-QLRESYPU.js} +637 -493
  158. package/dist/chunk-QLRESYPU.js.map +1 -0
  159. package/dist/{chunk-SBPC5IP3.js → chunk-QWX5YA3K.js} +784 -202
  160. package/dist/chunk-QWX5YA3K.js.map +1 -0
  161. package/dist/{chunk-TCRIQMBU.js → chunk-RJS7DECT.js} +8 -9
  162. package/dist/chunk-RJS7DECT.js.map +1 -0
  163. package/dist/{chunk-6JZUSDY2.cjs → chunk-RMQLSPA2.cjs} +297 -27
  164. package/dist/chunk-RMQLSPA2.cjs.map +1 -0
  165. package/dist/{chunk-SJDMVITW.cjs → chunk-RT5ZY4XA.cjs} +6 -5
  166. package/dist/chunk-RT5ZY4XA.cjs.map +1 -0
  167. package/dist/{chunk-IJTLXNGJ.js → chunk-T4XNKYXB.js} +780 -127
  168. package/dist/chunk-T4XNKYXB.js.map +1 -0
  169. package/dist/{chunk-PDGEXGS6.cjs → chunk-TQR4KBY2.cjs} +354 -182
  170. package/dist/chunk-TQR4KBY2.cjs.map +1 -0
  171. package/dist/chunk-TTAITEPJ.cjs +755 -0
  172. package/dist/chunk-TTAITEPJ.cjs.map +1 -0
  173. package/dist/{chunk-NN3QSOVS.cjs → chunk-U5SON7R2.cjs} +27 -27
  174. package/dist/{chunk-NN3QSOVS.cjs.map → chunk-U5SON7R2.cjs.map} +1 -1
  175. package/dist/{chunk-A2I2XO52.cjs → chunk-U63GDNRC.cjs} +32 -26
  176. package/dist/chunk-U63GDNRC.cjs.map +1 -0
  177. package/dist/{chunk-YNULOIR5.cjs → chunk-UDISNXDL.cjs} +64 -43
  178. package/dist/chunk-UDISNXDL.cjs.map +1 -0
  179. package/dist/{chunk-GWEDD3DH.js → chunk-UV3SN74I.js} +5 -5
  180. package/dist/{chunk-GWEDD3DH.js.map → chunk-UV3SN74I.js.map} +1 -1
  181. package/dist/{chunk-U5W4LKZV.cjs → chunk-VCRRKBLN.cjs} +9 -9
  182. package/dist/{chunk-U5W4LKZV.cjs.map → chunk-VCRRKBLN.cjs.map} +1 -1
  183. package/dist/{chunk-EFELJVLA.js → chunk-VGTJPTIC.js} +28 -5
  184. package/dist/chunk-VGTJPTIC.js.map +1 -0
  185. package/dist/chunk-VQH4VAWD.cjs +12 -0
  186. package/dist/{chunk-EPTSMFH7.cjs.map → chunk-VQH4VAWD.cjs.map} +1 -1
  187. package/dist/{chunk-SNXVDZAJ.js → chunk-W6OG3HDG.js} +5 -4
  188. package/dist/chunk-W6OG3HDG.js.map +1 -0
  189. package/dist/chunk-WDGA7IYB.js +207 -0
  190. package/dist/chunk-WDGA7IYB.js.map +1 -0
  191. package/dist/chunk-WEVQJ5LH.js +736 -0
  192. package/dist/chunk-WEVQJ5LH.js.map +1 -0
  193. package/dist/{chunk-3PRKZW6D.cjs → chunk-XBYVV3MP.cjs} +60 -5
  194. package/dist/chunk-XBYVV3MP.cjs.map +1 -0
  195. package/dist/{chunk-GFRGG2US.cjs → chunk-XCAGZAKP.cjs} +7 -7
  196. package/dist/{chunk-GFRGG2US.cjs.map → chunk-XCAGZAKP.cjs.map} +1 -1
  197. package/dist/{chunk-XR4YZFKW.cjs → chunk-XDF2UPEQ.cjs} +3864 -1783
  198. package/dist/chunk-XDF2UPEQ.cjs.map +1 -0
  199. package/dist/{chunk-7V2FGGI2.cjs → chunk-XMQSEJCF.cjs} +632 -1080
  200. package/dist/chunk-XMQSEJCF.cjs.map +1 -0
  201. package/dist/chunk-XNLIZYRZ.cjs +3432 -0
  202. package/dist/chunk-XNLIZYRZ.cjs.map +1 -0
  203. package/dist/{chunk-66UVLHQA.cjs → chunk-XNSI2FJO.cjs} +99 -100
  204. package/dist/chunk-XNSI2FJO.cjs.map +1 -0
  205. package/dist/chunk-XQSCL77R.cjs +60 -0
  206. package/dist/chunk-XQSCL77R.cjs.map +1 -0
  207. package/dist/{chunk-SVJ5GEXW.cjs → chunk-XUO3KQFO.cjs} +2 -2
  208. package/dist/chunk-XUO3KQFO.cjs.map +1 -0
  209. package/dist/{chunk-HJH3RMXG.cjs → chunk-YAAM3M2S.cjs} +10 -10
  210. package/dist/{chunk-HJH3RMXG.cjs.map → chunk-YAAM3M2S.cjs.map} +1 -1
  211. package/dist/{chunk-ATLN63JA.js → chunk-YBFHZUXX.js} +6 -6
  212. package/dist/{chunk-ATLN63JA.js.map → chunk-YBFHZUXX.js.map} +1 -1
  213. package/dist/{chunk-K3BUWSFR.js → chunk-YDFDBE6D.js} +119 -6
  214. package/dist/chunk-YDFDBE6D.js.map +1 -0
  215. package/dist/{chunk-OSZPLZIV.cjs → chunk-YVWGEQJE.cjs} +318 -132
  216. package/dist/chunk-YVWGEQJE.cjs.map +1 -0
  217. package/dist/{chunk-QT6V3YRH.js → chunk-YWGVVALJ.js} +3 -3
  218. package/dist/{chunk-QT6V3YRH.js.map → chunk-YWGVVALJ.js.map} +1 -1
  219. package/dist/core/index.cjs +40 -41
  220. package/dist/core/index.d.cts +4 -5
  221. package/dist/core/index.d.ts +4 -5
  222. package/dist/core/index.js +8 -9
  223. package/dist/{ddl-eWg7hx6R.d.cts → ddl-BJcOp4WZ.d.cts} +3 -3
  224. package/dist/{ddl-D5369kOY.d.ts → ddl-BXfGHbPN.d.ts} +3 -3
  225. package/dist/graph-extension/index.cjs +26 -23
  226. package/dist/graph-extension/index.d.cts +16 -6
  227. package/dist/graph-extension/index.d.ts +16 -6
  228. package/dist/graph-extension/index.js +7 -8
  229. package/dist/graph-merge/index.cjs +2459 -655
  230. package/dist/graph-merge/index.cjs.map +1 -1
  231. package/dist/graph-merge/index.d.cts +170 -33
  232. package/dist/graph-merge/index.d.ts +170 -33
  233. package/dist/graph-merge/index.js +2269 -468
  234. package/dist/graph-merge/index.js.map +1 -1
  235. package/dist/{index-By89Sezh.d.ts → index-BSHvKfok.d.ts} +221 -10
  236. package/dist/{index-DcQ3Sxs-.d.cts → index-DxaYCv8Y.d.cts} +221 -10
  237. package/dist/index.cjs +330 -269
  238. package/dist/index.cjs.map +1 -1
  239. package/dist/index.d.cts +590 -576
  240. package/dist/index.d.ts +590 -576
  241. package/dist/index.js +73 -50
  242. package/dist/index.js.map +1 -1
  243. package/dist/indexes/index.cjs +17 -17
  244. package/dist/indexes/index.d.cts +4 -4
  245. package/dist/indexes/index.d.ts +4 -4
  246. package/dist/indexes/index.js +10 -10
  247. package/dist/interchange/index.cjs +165 -50
  248. package/dist/interchange/index.cjs.map +1 -1
  249. package/dist/interchange/index.d.cts +261 -16
  250. package/dist/interchange/index.d.ts +261 -16
  251. package/dist/interchange/index.js +128 -24
  252. package/dist/interchange/index.js.map +1 -1
  253. package/dist/{manager-B0KjKE93.d.ts → manager-CfZAimti.d.ts} +36 -5
  254. package/dist/{manager-Cn0NFIhN.d.cts → manager-HGk6E_HW.d.cts} +36 -5
  255. package/dist/{materialize-indexes-BD4V3svD.d.ts → materialize-indexes-6A7dWGtb.d.cts} +10 -2
  256. package/dist/{materialize-indexes-DVRhd5h4.d.cts → materialize-indexes-BMJP6Tms.d.ts} +10 -2
  257. package/dist/{postgres-DaEWvN4x.d.cts → postgres-DFYWENdR.d.cts} +1961 -439
  258. package/dist/{postgres-u0a0HN2D.d.ts → postgres-e7a9TdvR.d.ts} +1961 -439
  259. package/dist/profiler/index.cjs +15 -15
  260. package/dist/profiler/index.d.cts +9 -10
  261. package/dist/profiler/index.d.ts +9 -10
  262. package/dist/profiler/index.js +4 -4
  263. package/dist/provenance/index.cjs +46 -48
  264. package/dist/provenance/index.cjs.map +1 -1
  265. package/dist/provenance/index.d.cts +7 -7
  266. package/dist/provenance/index.d.ts +7 -7
  267. package/dist/provenance/index.js +12 -14
  268. package/dist/provenance/index.js.map +1 -1
  269. package/dist/schema/index.cjs +50 -49
  270. package/dist/schema/index.d.cts +9 -16
  271. package/dist/schema/index.d.ts +9 -16
  272. package/dist/schema/index.js +23 -22
  273. package/dist/{searchable-CnYEancP.d.ts → searchable-9OQoPuRs.d.ts} +1 -2
  274. package/dist/{searchable-BT7j9_IK.d.cts → searchable-CIje5XYD.d.cts} +1 -2
  275. package/dist/{sqlite-BrMoSsI7.d.cts → sqlite-DerU5tc7.d.ts} +2029 -347
  276. package/dist/{sqlite-CHhZUyO5.d.ts → sqlite-DitnISa9.d.cts} +2029 -347
  277. package/dist/{store-view-Bvdku0Ht.d.ts → store-DawaWWjJ.d.ts} +3145 -2556
  278. package/dist/{store-view-w-md0nQ4.d.cts → store-RwTJx8LX.d.cts} +3145 -2556
  279. package/dist/{temporal-BCORwUau.d.cts → temporal-Bn-Fi8FJ.d.cts} +1 -1
  280. package/dist/{temporal--x9T8K4S.d.ts → temporal-BtM4UALv.d.ts} +1 -1
  281. package/dist/{types-6-WJmSD4.d.cts → types-CoIx8HX5.d.cts} +1 -1
  282. package/dist/{types-Cjvkj0jA.d.ts → types-HkveCCx0.d.cts} +866 -21
  283. package/dist/{types-Cjvkj0jA.d.cts → types-HkveCCx0.d.ts} +866 -21
  284. package/dist/{types-COODtTJU.d.ts → types-KFfk-8Ah.d.ts} +1 -1
  285. package/dist/{where-CUQmCR6d.d.cts → where--fk7_0kv.d.cts} +1 -1
  286. package/dist/{where-5xVawAwL.d.ts → where-ws5hf-Ov.d.ts} +1 -1
  287. package/package.json +1 -1
  288. package/dist/chunk-2BIG7U6E.cjs +0 -83
  289. package/dist/chunk-2BIG7U6E.cjs.map +0 -1
  290. package/dist/chunk-2L54C2C2.js +0 -144
  291. package/dist/chunk-2L54C2C2.js.map +0 -1
  292. package/dist/chunk-2VLBGQBY.cjs.map +0 -1
  293. package/dist/chunk-2WDD2NO3.js +0 -59
  294. package/dist/chunk-2WDD2NO3.js.map +0 -1
  295. package/dist/chunk-2Y4IFKW7.js.map +0 -1
  296. package/dist/chunk-35S6P5ZU.js +0 -27
  297. package/dist/chunk-35S6P5ZU.js.map +0 -1
  298. package/dist/chunk-3PRKZW6D.cjs.map +0 -1
  299. package/dist/chunk-4MB3AQAQ.js.map +0 -1
  300. package/dist/chunk-4SZ6XPTG.cjs +0 -12
  301. package/dist/chunk-5MUSU4ZP.cjs.map +0 -1
  302. package/dist/chunk-63KCKSSH.js.map +0 -1
  303. package/dist/chunk-66UVLHQA.cjs.map +0 -1
  304. package/dist/chunk-6JZUSDY2.cjs.map +0 -1
  305. package/dist/chunk-764JHTLT.cjs +0 -15
  306. package/dist/chunk-764JHTLT.cjs.map +0 -1
  307. package/dist/chunk-7G32LIQM.js.map +0 -1
  308. package/dist/chunk-7V2FGGI2.cjs.map +0 -1
  309. package/dist/chunk-A2I2XO52.cjs.map +0 -1
  310. package/dist/chunk-A4PBRX4C.cjs +0 -100
  311. package/dist/chunk-A4PBRX4C.cjs.map +0 -1
  312. package/dist/chunk-ACSVZPK7.cjs.map +0 -1
  313. package/dist/chunk-ALFUGRYB.cjs +0 -1282
  314. package/dist/chunk-ALFUGRYB.cjs.map +0 -1
  315. package/dist/chunk-B75XRV3O.js +0 -125
  316. package/dist/chunk-B75XRV3O.js.map +0 -1
  317. package/dist/chunk-BCP7LLOY.js.map +0 -1
  318. package/dist/chunk-BEFM74L2.js.map +0 -1
  319. package/dist/chunk-BNUFUFAU.js.map +0 -1
  320. package/dist/chunk-C4F7VJ5D.cjs.map +0 -1
  321. package/dist/chunk-CPEL3YIR.cjs.map +0 -1
  322. package/dist/chunk-D7PG5D5K.js.map +0 -1
  323. package/dist/chunk-DI74SMXS.cjs +0 -29
  324. package/dist/chunk-DI74SMXS.cjs.map +0 -1
  325. package/dist/chunk-DK2S42XB.js +0 -77
  326. package/dist/chunk-DK2S42XB.js.map +0 -1
  327. package/dist/chunk-DRACN6XG.js +0 -27
  328. package/dist/chunk-DRACN6XG.js.map +0 -1
  329. package/dist/chunk-EE7N5XXQ.cjs.map +0 -1
  330. package/dist/chunk-EFELJVLA.js.map +0 -1
  331. package/dist/chunk-EPTSMFH7.cjs +0 -12
  332. package/dist/chunk-EQ7JKLZO.cjs +0 -157
  333. package/dist/chunk-EQ7JKLZO.cjs.map +0 -1
  334. package/dist/chunk-FJMPR3OH.cjs.map +0 -1
  335. package/dist/chunk-FMGEZBAQ.js.map +0 -1
  336. package/dist/chunk-FMMIX6N4.js.map +0 -1
  337. package/dist/chunk-FRHHDHKH.js.map +0 -1
  338. package/dist/chunk-FSXTPBRN.js.map +0 -1
  339. package/dist/chunk-G6Q5UM7U.js.map +0 -1
  340. package/dist/chunk-GYA4JRP5.js.map +0 -1
  341. package/dist/chunk-HOHAJ2NR.cjs +0 -61
  342. package/dist/chunk-HOHAJ2NR.cjs.map +0 -1
  343. package/dist/chunk-HOIFFY4M.js.map +0 -1
  344. package/dist/chunk-IIMQJYAM.js +0 -81
  345. package/dist/chunk-IIMQJYAM.js.map +0 -1
  346. package/dist/chunk-IJTLXNGJ.js.map +0 -1
  347. package/dist/chunk-ITE4IXAK.js +0 -25
  348. package/dist/chunk-ITE4IXAK.js.map +0 -1
  349. package/dist/chunk-K3BUWSFR.js.map +0 -1
  350. package/dist/chunk-KCO5EFJU.js +0 -79
  351. package/dist/chunk-KCO5EFJU.js.map +0 -1
  352. package/dist/chunk-KKYELPZ2.cjs +0 -83
  353. package/dist/chunk-KKYELPZ2.cjs.map +0 -1
  354. package/dist/chunk-MCHP7RHZ.cjs.map +0 -1
  355. package/dist/chunk-MIJKEVV7.js.map +0 -1
  356. package/dist/chunk-NCX65HBY.cjs +0 -1396
  357. package/dist/chunk-NCX65HBY.cjs.map +0 -1
  358. package/dist/chunk-OSZPLZIV.cjs.map +0 -1
  359. package/dist/chunk-OVUUTCPJ.cjs.map +0 -1
  360. package/dist/chunk-PDGEXGS6.cjs.map +0 -1
  361. package/dist/chunk-QB6VMOJ2.cjs.map +0 -1
  362. package/dist/chunk-QMQLUA66.js +0 -12
  363. package/dist/chunk-QMQLUA66.js.map +0 -1
  364. package/dist/chunk-QYWINYEA.cjs.map +0 -1
  365. package/dist/chunk-S4KQ5LV4.js.map +0 -1
  366. package/dist/chunk-SBPC5IP3.js.map +0 -1
  367. package/dist/chunk-SJDMVITW.cjs.map +0 -1
  368. package/dist/chunk-SNXVDZAJ.js.map +0 -1
  369. package/dist/chunk-SQRD2CYF.js.map +0 -1
  370. package/dist/chunk-ST3LPB2V.cjs.map +0 -1
  371. package/dist/chunk-STQ3BSED.cjs.map +0 -1
  372. package/dist/chunk-SVJ5GEXW.cjs.map +0 -1
  373. package/dist/chunk-TCRIQMBU.js.map +0 -1
  374. package/dist/chunk-TDXIBCGY.cjs.map +0 -1
  375. package/dist/chunk-TFMVL4QH.js.map +0 -1
  376. package/dist/chunk-TRF4EBJT.cjs +0 -30
  377. package/dist/chunk-TRF4EBJT.cjs.map +0 -1
  378. package/dist/chunk-TVLKA4AY.js +0 -94
  379. package/dist/chunk-TVLKA4AY.js.map +0 -1
  380. package/dist/chunk-UR7W5YQK.js.map +0 -1
  381. package/dist/chunk-VITTKBWF.js +0 -1263
  382. package/dist/chunk-VITTKBWF.js.map +0 -1
  383. package/dist/chunk-WBUFJ7BS.cjs +0 -131
  384. package/dist/chunk-WBUFJ7BS.cjs.map +0 -1
  385. package/dist/chunk-XR4YZFKW.cjs.map +0 -1
  386. package/dist/chunk-YNULOIR5.cjs.map +0 -1
  387. package/dist/chunk-ZDH3DX3C.cjs +0 -30
  388. package/dist/chunk-ZDH3DX3C.cjs.map +0 -1
  389. package/dist/chunk-ZTAJL4DT.cjs +0 -85
  390. package/dist/chunk-ZTAJL4DT.cjs.map +0 -1
  391. package/dist/define-graph-DVF-oN1i.d.cts +0 -173
  392. package/dist/define-graph-SvYXOl78.d.ts +0 -173
@@ -0,0 +1,3393 @@
1
+ import { advanceRevisionClock, lockRecordedGraphWrite, uncapturedGraphWriteLock, identityAssertionSnapshotSource, identitySqlCoordinate, IDENTITY_ASSERTION_COLUMNS, compareCodePoints, withRecordedIdentityMutationTarget, recordedBindParamBudget, identityNodeVisibilitySql, historicalIdentityReconstructionCtes, generateId, identityNodeSnapshotSource } from './chunk-NQ6BRJBE.js';
2
+ import { chunk, isSqliteStaleSnapshotError, isMissingTableError } from './chunk-JZUR7ZWE.js';
3
+ import { runOptionallyInTransaction } from './chunk-AKLS3BYZ.js';
4
+ import { nowIso, canonicalizeDatabaseTimestamp } from './chunk-DLNROMQ7.js';
5
+ import { getDialect } from './chunk-YDFDBE6D.js';
6
+ import { asCompiledRowsSql, sql, asCompiledStatementSql } from './chunk-PQBQG7IM.js';
7
+ import { ValidationError, ConfigurationError, NodeNotFoundError, KindNotFoundError, IdentityContradictionError, IdentitySeparationViolationError } from './chunk-J3WZ3FA2.js';
8
+ import { requireDefined } from './chunk-DYATYRY4.js';
9
+
10
+ // src/errors/validation.ts
11
+ function zodIssuesToValidationIssues(error) {
12
+ return error.issues.map((issue) => ({
13
+ path: issue.path.join("."),
14
+ message: issue.message,
15
+ code: issue.code
16
+ }));
17
+ }
18
+ function buildLocationString(context) {
19
+ if (context.id) {
20
+ return `${context.kind}/${context.id}`;
21
+ }
22
+ return `new ${context.kind}`;
23
+ }
24
+ function validateProps(schema, props, context) {
25
+ const result = schema.safeParse(props);
26
+ if (result.success) {
27
+ return result.data;
28
+ }
29
+ const issues = zodIssuesToValidationIssues(result.error);
30
+ const location = buildLocationString(context);
31
+ throw new ValidationError(
32
+ `Invalid ${context.entityType} props for ${location}: ${result.error.message}`,
33
+ {
34
+ entityType: context.entityType,
35
+ kind: context.kind,
36
+ operation: context.operation,
37
+ ...context.id !== void 0 && { id: context.id },
38
+ issues
39
+ },
40
+ { cause: result.error }
41
+ );
42
+ }
43
+ function validateNodeProps(schema, props, context) {
44
+ return validateProps(schema, props, {
45
+ entityType: "node",
46
+ ...context
47
+ });
48
+ }
49
+ function validateEdgeProps(schema, props, context) {
50
+ return validateProps(schema, props, {
51
+ entityType: "edge",
52
+ ...context
53
+ });
54
+ }
55
+ function wrapZodError(error, context) {
56
+ const issues = zodIssuesToValidationIssues(error);
57
+ const location = buildLocationString(context);
58
+ return new ValidationError(
59
+ `Validation failed for ${context.entityType} ${location}: ${error.message}`,
60
+ {
61
+ entityType: context.entityType,
62
+ kind: context.kind,
63
+ operation: context.operation,
64
+ ...context.id !== void 0 && { id: context.id },
65
+ issues
66
+ },
67
+ { cause: error }
68
+ );
69
+ }
70
+ function createValidationError(message, issues, context) {
71
+ return new ValidationError(message, {
72
+ ...context?.entityType !== void 0 && {
73
+ entityType: context.entityType
74
+ },
75
+ ...context?.kind !== void 0 && { kind: context.kind },
76
+ ...context?.operation !== void 0 && { operation: context.operation },
77
+ ...context?.id !== void 0 && { id: context.id },
78
+ issues
79
+ });
80
+ }
81
+
82
+ // src/utils/tuple-key.ts
83
+ function encodeTupleKey(values) {
84
+ return JSON.stringify(values);
85
+ }
86
+
87
+ // src/identity/row-codec.ts
88
+ function toCanonicalIdentityTimestamp(value) {
89
+ const canonical = canonicalizeDatabaseTimestamp(value);
90
+ if (canonical === void 0) {
91
+ throw new ConfigurationError(
92
+ "Identity relation returned a timestamp that is not a representable instant.",
93
+ { value },
94
+ {
95
+ suggestion: "Inspect the identity assertion rows for a corrupt timestamp column."
96
+ }
97
+ );
98
+ }
99
+ return canonical;
100
+ }
101
+ function optionalIdentityTimestamp(value) {
102
+ return value === void 0 || value === null ? void 0 : toCanonicalIdentityTimestamp(value);
103
+ }
104
+ function normalizeIdentityAssertionRow(row) {
105
+ return {
106
+ graph_id: row.graph_id,
107
+ id: row.id,
108
+ rel: row.rel,
109
+ a_kind: row.a_kind,
110
+ a_id: row.a_id,
111
+ b_kind: row.b_kind,
112
+ b_id: row.b_id,
113
+ valid_from: toCanonicalIdentityTimestamp(row.valid_from),
114
+ valid_to: optionalIdentityTimestamp(row.valid_to),
115
+ created_at: toCanonicalIdentityTimestamp(row.created_at),
116
+ updated_at: toCanonicalIdentityTimestamp(row.updated_at),
117
+ deleted_at: optionalIdentityTimestamp(row.deleted_at),
118
+ ended_by_kind: row.ended_by_kind ?? void 0,
119
+ ended_by_id: row.ended_by_id ?? void 0
120
+ };
121
+ }
122
+ function toTransferAssertion(row) {
123
+ return {
124
+ id: row.id,
125
+ relation: row.rel,
126
+ a: { kind: row.a_kind, id: row.a_id },
127
+ b: { kind: row.b_kind, id: row.b_id },
128
+ validFrom: row.valid_from,
129
+ ...row.valid_to === void 0 ? {} : { validTo: row.valid_to },
130
+ ...row.ended_by_kind === void 0 || row.ended_by_id === void 0 ? {} : { endedBy: { kind: row.ended_by_kind, id: row.ended_by_id } }
131
+ };
132
+ }
133
+
134
+ // src/identity/reference.ts
135
+ function identityReferenceKey(ref) {
136
+ return encodeTupleKey([ref.kind, ref.id]);
137
+ }
138
+ function compareIdentityReferences(left, right) {
139
+ const kindOrder = compareCodePoints(left.kind, right.kind);
140
+ return kindOrder === 0 ? compareCodePoints(left.id, right.id) : kindOrder;
141
+ }
142
+ function normalizeIdentityPair(first, second) {
143
+ return compareIdentityReferences(first, second) <= 0 ? [first, second] : [second, first];
144
+ }
145
+ function identityReferencesContain(members, ref) {
146
+ const key = identityReferenceKey(ref);
147
+ return members.some((member) => identityReferenceKey(member) === key);
148
+ }
149
+
150
+ // src/identity/different-assertion.ts
151
+ function spanningDifferentAssertion(assertions, first, second) {
152
+ const firstKeys = new Set(
153
+ first.map((reference) => identityReferenceKey(reference))
154
+ );
155
+ const secondKeys = new Set(
156
+ second.map((reference) => identityReferenceKey(reference))
157
+ );
158
+ return assertions.find((assertion) => {
159
+ if (assertion.rel !== "different") return false;
160
+ const firstEndpoint = identityReferenceKey({
161
+ kind: assertion.a_kind,
162
+ id: assertion.a_id
163
+ });
164
+ const secondEndpoint = identityReferenceKey({
165
+ kind: assertion.b_kind,
166
+ id: assertion.b_id
167
+ });
168
+ return firstKeys.has(firstEndpoint) && secondKeys.has(secondEndpoint) || firstKeys.has(secondEndpoint) && secondKeys.has(firstEndpoint);
169
+ });
170
+ }
171
+
172
+ // src/identity/sql-target.ts
173
+ var MAX_REFERENCE_CHUNK_SIZE = 200;
174
+ function identityChunkSize(target, input) {
175
+ const parameterLimit = recordedBindParamBudget(target);
176
+ const chunkSize = Math.floor(
177
+ (parameterLimit - input.fixedParameters) / input.parametersPerItem
178
+ );
179
+ if (chunkSize < 1) {
180
+ throw new ConfigurationError(
181
+ "Operational Identity cannot fit this statement within the backend bind-parameter limit.",
182
+ {
183
+ code: "IDENTITY_BIND_BUDGET_TOO_SMALL",
184
+ parameterLimit,
185
+ fixedParameters: input.fixedParameters,
186
+ parametersPerItem: input.parametersPerItem
187
+ }
188
+ );
189
+ }
190
+ return Math.min(input.maxItems, chunkSize);
191
+ }
192
+ function requireStatementTarget(target) {
193
+ if (target.executeStatement === void 0) {
194
+ throw new ConfigurationError(
195
+ "Operational Identity requires statement execution support.",
196
+ { code: "IDENTITY_REQUIRES_STATEMENT_EXECUTION" },
197
+ {
198
+ suggestion: "Use a built-in transactional SQLite or PostgreSQL backend."
199
+ }
200
+ );
201
+ }
202
+ }
203
+ function identityWriterSlotError(cause) {
204
+ return new ConfigurationError(
205
+ "Operational Identity could not take the SQLite writer slot: this transaction was begun DEFERRED and another connection committed before the identity write, so its read snapshot is stale.",
206
+ {
207
+ code: "IDENTITY_TRANSACTION_NOT_WRITE_FENCED",
208
+ sqliteCode: "SQLITE_BUSY_SNAPSHOT"
209
+ },
210
+ {
211
+ cause,
212
+ suggestion: "Roll back and re-run the transaction (SQLite cannot upgrade a stale snapshot in place). Identity mutations serialize on the writer slot, so an adopted transaction must be opened with BEGIN IMMEDIATE \u2014 or run the writes through store.transaction(), which already does."
213
+ }
214
+ );
215
+ }
216
+ async function executeIdentityStatement(target, statement) {
217
+ requireStatementTarget(target);
218
+ try {
219
+ await target.executeStatement(asCompiledStatementSql(statement));
220
+ } catch (error) {
221
+ if (!isSqliteStaleSnapshotError(error)) throw error;
222
+ throw identityWriterSlotError(error);
223
+ }
224
+ }
225
+
226
+ // src/identity/service-read.ts
227
+ var MAX_CLOSURE_INSERT_CHUNK_SIZE = 100;
228
+ var MAX_ASSERTION_INSERT_CHUNK_SIZE = 50;
229
+ function plainRef(ref) {
230
+ return { kind: ref.kind, id: ref.id };
231
+ }
232
+ function registeredPlainRef(ctx, ref) {
233
+ const result = plainRef(ref);
234
+ if (!ctx.registry.nodeKinds.has(result.kind)) {
235
+ throw new KindNotFoundError(result.kind, "node", {
236
+ graphId: ctx.graphId
237
+ });
238
+ }
239
+ return result;
240
+ }
241
+ function refKey(ref) {
242
+ return identityReferenceKey(ref);
243
+ }
244
+ function containsRef(members, ref) {
245
+ return identityReferencesContain(members, ref);
246
+ }
247
+ function compareReferences(left, right) {
248
+ return compareIdentityReferences(left, right);
249
+ }
250
+ function normalizePair(first, second) {
251
+ return normalizeIdentityPair(first, second);
252
+ }
253
+ function clampValidTo(timestamp, validFrom) {
254
+ return compareCodePoints(timestamp, validFrom) < 0 ? validFrom : timestamp;
255
+ }
256
+ function publicAssertion(row) {
257
+ return {
258
+ id: row.id,
259
+ relation: row.rel,
260
+ a: publicNodeRef({ kind: row.a_kind, id: row.a_id }),
261
+ b: publicNodeRef({ kind: row.b_kind, id: row.b_id }),
262
+ validFrom: row.valid_from,
263
+ ...row.valid_to === void 0 ? {} : { validTo: row.valid_to }
264
+ };
265
+ }
266
+ function assertionResult(assertion, action) {
267
+ return { assertion, action };
268
+ }
269
+ function publicNodeRef(ref) {
270
+ return ref;
271
+ }
272
+ async function lockIdentityGraph(target, graphId) {
273
+ if (target.dialect !== "postgres") return;
274
+ await target.execute(
275
+ asCompiledRowsSql(sql`
276
+ SELECT pg_advisory_xact_lock(
277
+ hashtext('typegraph:identity'),
278
+ hashtext(${graphId})
279
+ )
280
+ `)
281
+ );
282
+ }
283
+ async function lockIdentityEnablementNodes(target, schema) {
284
+ if (target.dialect !== "postgres") return;
285
+ await executeIdentityStatement(
286
+ target,
287
+ sql`LOCK TABLE ${schema.nodesTable} IN SHARE MODE`
288
+ );
289
+ }
290
+ async function loadNodeSnapshot(target, schema, graphId, coordinate) {
291
+ const sqlCoordinate = identitySqlCoordinate(coordinate, nowIso());
292
+ const rows = await target.execute(
293
+ asCompiledRowsSql(
294
+ identityNodeSnapshotSource(schema, graphId, sqlCoordinate)
295
+ )
296
+ );
297
+ return rows.map((row) => ({
298
+ ref: { kind: row.kind, id: row.id },
299
+ validFrom: optionalIdentityTimestamp(row.valid_from),
300
+ validTo: optionalIdentityTimestamp(row.valid_to),
301
+ createdAt: toCanonicalIdentityTimestamp(row.created_at),
302
+ deletedAt: optionalIdentityTimestamp(row.deleted_at)
303
+ }));
304
+ }
305
+ function isCurrentClosureCoordinate(coordinate) {
306
+ return coordinate?.recorded === void 0 && (coordinate?.valid.mode ?? "current") === "current";
307
+ }
308
+ async function loadCurrentStructuralClasses(target, schema, graphId, references) {
309
+ const uniqueByKey = /* @__PURE__ */ new Map();
310
+ for (const ref of references) uniqueByKey.set(refKey(ref), ref);
311
+ const uniqueReferences = [...uniqueByKey.values()];
312
+ if (uniqueReferences.length === 0) return /* @__PURE__ */ new Map();
313
+ const chunkSize = identityChunkSize(target, {
314
+ fixedParameters: 2,
315
+ maxItems: MAX_REFERENCE_CHUNK_SIZE,
316
+ parametersPerItem: 2
317
+ });
318
+ if (uniqueReferences.length > chunkSize) {
319
+ const combined = /* @__PURE__ */ new Map();
320
+ for (const refChunk of chunk(uniqueReferences, chunkSize)) {
321
+ const classes2 = await loadCurrentStructuralClasses(
322
+ target,
323
+ schema,
324
+ graphId,
325
+ refChunk
326
+ );
327
+ for (const [key, members] of classes2) combined.set(key, members);
328
+ }
329
+ return combined;
330
+ }
331
+ const seedRows = sql.join(
332
+ uniqueReferences.map((ref) => sql`(${ref.kind}, ${ref.id})`),
333
+ sql`, `
334
+ );
335
+ const rows = await target.execute(
336
+ asCompiledRowsSql(sql`
337
+ WITH seeds(seed_kind, seed_id) AS (
338
+ VALUES ${seedRows}
339
+ ), anchors AS (
340
+ SELECT seeds.seed_kind, seeds.seed_id,
341
+ COALESCE(anchor.class_kind, seeds.seed_kind) AS class_kind,
342
+ COALESCE(anchor.class_id, seeds.seed_id) AS class_id
343
+ FROM seeds
344
+ LEFT JOIN ${schema.identityClosureTable} anchor
345
+ ON anchor.graph_id = ${graphId}
346
+ AND anchor.member_kind = seeds.seed_kind
347
+ AND anchor.member_id = seeds.seed_id
348
+ )
349
+ SELECT anchors.seed_kind, anchors.seed_id,
350
+ COALESCE(member.member_kind, anchors.seed_kind) AS member_kind,
351
+ COALESCE(member.member_id, anchors.seed_id) AS member_id
352
+ FROM anchors
353
+ LEFT JOIN ${schema.identityClosureTable} member
354
+ ON member.graph_id = ${graphId}
355
+ AND member.class_kind = anchors.class_kind
356
+ AND member.class_id = anchors.class_id
357
+ `)
358
+ );
359
+ const classes = /* @__PURE__ */ new Map();
360
+ for (const row of rows) {
361
+ const seedKey = refKey({ kind: row.seed_kind, id: row.seed_id });
362
+ const members = classes.get(seedKey) ?? [];
363
+ members.push({ kind: row.member_kind, id: row.member_id });
364
+ classes.set(seedKey, members);
365
+ }
366
+ return new Map(
367
+ [...classes].map(([seedKey, members]) => [
368
+ seedKey,
369
+ members.toSorted((left, right) => compareReferences(left, right))
370
+ ])
371
+ );
372
+ }
373
+ async function loadCurrentVisibleMembers(target, schema, graphId, ref) {
374
+ const now = nowIso();
375
+ const coordinate = identitySqlCoordinate(void 0, now);
376
+ const rows = await target.execute(
377
+ asCompiledRowsSql(sql`
378
+ WITH anchor AS (
379
+ SELECT class_kind, class_id
380
+ FROM ${schema.identityClosureTable}
381
+ WHERE graph_id = ${graphId}
382
+ AND member_kind = ${ref.kind}
383
+ AND member_id = ${ref.id}
384
+ ), members(kind, id) AS (
385
+ SELECT closure.member_kind, closure.member_id
386
+ FROM anchor
387
+ JOIN ${schema.identityClosureTable} closure
388
+ ON closure.graph_id = ${graphId}
389
+ AND closure.class_kind = anchor.class_kind
390
+ AND closure.class_id = anchor.class_id
391
+ UNION ALL
392
+ SELECT ${ref.kind}, ${ref.id}
393
+ WHERE NOT EXISTS (SELECT 1 FROM anchor)
394
+ )
395
+ SELECT n.kind, n.id
396
+ FROM members m
397
+ CROSS JOIN ${schema.nodesTable} n
398
+ WHERE n.graph_id = ${graphId}
399
+ AND n.kind = m.kind
400
+ AND n.id = m.id
401
+ AND ${identityNodeVisibilitySql(coordinate, "n")}
402
+ `)
403
+ );
404
+ const members = rows.map((row) => ({ kind: row.kind, id: row.id })).toSorted((left, right) => compareReferences(left, right));
405
+ return containsRef(members, ref) ? members : [];
406
+ }
407
+ async function loadHistoricalVisibleMembers(target, schema, graphId, ref, coordinate, sameIdAcrossKinds) {
408
+ const classes = await loadHistoricalClasses(
409
+ target,
410
+ schema,
411
+ graphId,
412
+ [ref],
413
+ coordinate,
414
+ sameIdAcrossKinds
415
+ );
416
+ return requireDefined(classes.get(refKey(ref))).visible;
417
+ }
418
+ async function loadHistoricalClasses(target, schema, graphId, references, coordinate, sameIdAcrossKinds) {
419
+ const uniqueByKey = /* @__PURE__ */ new Map();
420
+ for (const ref of references) uniqueByKey.set(refKey(ref), ref);
421
+ const uniqueReferences = [...uniqueByKey.values()];
422
+ const emptyClasses = new Map(
423
+ uniqueReferences.map((ref) => [
424
+ refKey(ref),
425
+ { structural: [], visible: [] }
426
+ ])
427
+ );
428
+ if (uniqueReferences.length === 0) return emptyClasses;
429
+ const chunkSize = identityChunkSize(target, {
430
+ fixedParameters: 24,
431
+ maxItems: MAX_REFERENCE_CHUNK_SIZE,
432
+ parametersPerItem: 2
433
+ });
434
+ if (uniqueReferences.length > chunkSize) {
435
+ const combined = /* @__PURE__ */ new Map();
436
+ for (const refChunk of chunk(uniqueReferences, chunkSize)) {
437
+ const classes = await loadHistoricalClasses(
438
+ target,
439
+ schema,
440
+ graphId,
441
+ refChunk,
442
+ coordinate,
443
+ sameIdAcrossKinds
444
+ );
445
+ for (const [key, value] of classes) combined.set(key, value);
446
+ }
447
+ return combined;
448
+ }
449
+ const currentInstant = nowIso();
450
+ const sqlCoordinate = identitySqlCoordinate(coordinate, currentInstant);
451
+ const seeds = sql.join(
452
+ uniqueReferences.map((ref) => sql`(${ref.kind}, ${ref.id})`),
453
+ sql`, `
454
+ );
455
+ const reconstruction = historicalIdentityReconstructionCtes({
456
+ schema,
457
+ graphId,
458
+ coordinate: sqlCoordinate,
459
+ seedSource: sql`VALUES ${seeds}`,
460
+ sameIdAcrossKinds
461
+ });
462
+ const rows = await target.execute(
463
+ asCompiledRowsSql(sql`
464
+ WITH RECURSIVE
465
+ ${reconstruction}
466
+ SELECT member.seed_kind, member.seed_id,
467
+ member.kind AS member_kind, member.id AS member_id,
468
+ CASE WHEN ${identityNodeVisibilitySql(sqlCoordinate, "n")}
469
+ THEN 1 ELSE 0 END AS is_visible
470
+ FROM identity_members member
471
+ JOIN node_snapshot n ON n.kind = member.kind AND n.id = member.id
472
+ `)
473
+ );
474
+ const structuralBySeed = /* @__PURE__ */ new Map();
475
+ const visibleBySeed = /* @__PURE__ */ new Map();
476
+ for (const row of rows) {
477
+ const seedKey = refKey({ kind: row.seed_kind, id: row.seed_id });
478
+ const member = { kind: row.member_kind, id: row.member_id };
479
+ const structural = structuralBySeed.get(seedKey) ?? [];
480
+ structural.push(member);
481
+ structuralBySeed.set(seedKey, structural);
482
+ if (!row.is_visible) continue;
483
+ const visible = visibleBySeed.get(seedKey) ?? [];
484
+ visible.push(member);
485
+ visibleBySeed.set(seedKey, visible);
486
+ }
487
+ return new Map(
488
+ uniqueReferences.map((ref) => {
489
+ const key = refKey(ref);
490
+ return [
491
+ key,
492
+ {
493
+ structural: (structuralBySeed.get(key) ?? []).toSorted(
494
+ (left, right) => compareReferences(left, right)
495
+ ),
496
+ visible: (visibleBySeed.get(key) ?? []).toSorted(
497
+ (left, right) => compareReferences(left, right)
498
+ )
499
+ }
500
+ ];
501
+ })
502
+ );
503
+ }
504
+ function visibleMembersAtCoordinate(ctx, ref) {
505
+ const { coordinate } = ctx;
506
+ if (coordinate === void 0 || isCurrentClosureCoordinate(coordinate)) {
507
+ return loadCurrentVisibleMembers(ctx.backend, ctx.schema, ctx.graphId, ref);
508
+ }
509
+ return loadHistoricalVisibleMembers(
510
+ ctx.backend,
511
+ ctx.schema,
512
+ ctx.graphId,
513
+ ref,
514
+ coordinate,
515
+ ctx.sameIdAcrossKinds
516
+ );
517
+ }
518
+ async function loadAssertions(target, schema, graphId, coordinate, currentInstant) {
519
+ const source = identityAssertionSnapshotSource(
520
+ schema,
521
+ graphId,
522
+ identitySqlCoordinate(coordinate, currentInstant),
523
+ void 0
524
+ );
525
+ const rows = await target.execute(
526
+ asCompiledRowsSql(source)
527
+ );
528
+ return rows.map((row) => normalizeIdentityAssertionRow(row));
529
+ }
530
+ function referenceCondition(kindColumn, idColumn, references) {
531
+ if (references.length === 0) return sql`1 = 0`;
532
+ return sql`(${sql.join(
533
+ references.map(
534
+ (ref) => sql`(${kindColumn} = ${ref.kind} AND ${idColumn} = ${ref.id})`
535
+ ),
536
+ sql` OR `
537
+ )})`;
538
+ }
539
+ async function loadAssertionsTouching(target, schema, graphId, references, coordinate, relation) {
540
+ if (references.length === 0) return [];
541
+ const chunkSize = identityChunkSize(target, {
542
+ fixedParameters: 16,
543
+ maxItems: MAX_REFERENCE_CHUNK_SIZE,
544
+ parametersPerItem: 4
545
+ });
546
+ if (references.length > chunkSize) {
547
+ const byId = /* @__PURE__ */ new Map();
548
+ for (const refChunk of chunk(references, chunkSize)) {
549
+ const assertions = await loadAssertionsTouching(
550
+ target,
551
+ schema,
552
+ graphId,
553
+ refChunk,
554
+ coordinate,
555
+ relation
556
+ );
557
+ for (const assertion of assertions) byId.set(assertion.id, assertion);
558
+ }
559
+ return [...byId.values()];
560
+ }
561
+ const source = identityAssertionSnapshotSource(
562
+ schema,
563
+ graphId,
564
+ identitySqlCoordinate(coordinate, nowIso()),
565
+ relation
566
+ );
567
+ const aMatches = referenceCondition(
568
+ sql`identity_assertions.a_kind`,
569
+ sql`identity_assertions.a_id`,
570
+ references
571
+ );
572
+ const bMatches = referenceCondition(
573
+ sql`identity_assertions.b_kind`,
574
+ sql`identity_assertions.b_id`,
575
+ references
576
+ );
577
+ const rows = await target.execute(
578
+ asCompiledRowsSql(sql`
579
+ SELECT ${IDENTITY_ASSERTION_COLUMNS}
580
+ FROM (${source}) identity_assertions
581
+ WHERE ${aMatches} OR ${bMatches}
582
+ `)
583
+ );
584
+ return rows.map((row) => normalizeIdentityAssertionRow(row));
585
+ }
586
+ async function loadSpanningDifferentAssertion(target, schema, graphId, firstClass, secondClass, coordinate) {
587
+ const assertions = await loadAssertionsTouching(
588
+ target,
589
+ schema,
590
+ graphId,
591
+ firstClass,
592
+ coordinate,
593
+ "different"
594
+ );
595
+ return spanningDifferentAssertion(assertions, firstClass, secondClass);
596
+ }
597
+
598
+ // src/identity/interchange-read.ts
599
+ function codePointOrderedAssertionId(target) {
600
+ return getDialect(target.dialect).binaryText(sql`identity_assertions.id`);
601
+ }
602
+ async function readIdentityAssertionPageAtTarget(ctx, target, mode, options) {
603
+ if (!Number.isSafeInteger(options.limit) || options.limit <= 0) {
604
+ throw new ConfigurationError(
605
+ "Identity assertion page limit must be a positive safe integer.",
606
+ { limit: options.limit }
607
+ );
608
+ }
609
+ const nodeKinds = options.nodeKinds === void 0 ? void 0 : [...new Set(options.nodeKinds)];
610
+ const kindFilterChunkSize = nodeKinds === void 0 || nodeKinds.length === 0 ? MAX_REFERENCE_CHUNK_SIZE : identityChunkSize(target, {
611
+ fixedParameters: 16,
612
+ maxItems: MAX_REFERENCE_CHUNK_SIZE,
613
+ parametersPerItem: 2
614
+ });
615
+ const filterKindsInMemory = nodeKinds !== void 0 && nodeKinds.length > kindFilterChunkSize;
616
+ const kindFilter = nodeKinds === void 0 ? sql`` : nodeKinds.length === 0 ? sql`AND 1 = 0` : filterKindsInMemory ? sql`` : sql`
617
+ AND identity_assertions.a_kind IN (${sql.join(
618
+ nodeKinds.map((kind) => sql`${kind}`),
619
+ sql`, `
620
+ )})
621
+ AND identity_assertions.b_kind IN (${sql.join(
622
+ nodeKinds.map((kind) => sql`${kind}`),
623
+ sql`, `
624
+ )})
625
+ `;
626
+ const liveEndpointJoins = options.includeDeleted === false ? sql`
627
+ JOIN ${ctx.schema.nodesTable} identity_a_node
628
+ ON identity_a_node.graph_id = identity_assertions.graph_id
629
+ AND identity_a_node.kind = identity_assertions.a_kind
630
+ AND identity_a_node.id = identity_assertions.a_id
631
+ AND identity_a_node.deleted_at IS NULL
632
+ JOIN ${ctx.schema.nodesTable} identity_b_node
633
+ ON identity_b_node.graph_id = identity_assertions.graph_id
634
+ AND identity_b_node.kind = identity_assertions.b_kind
635
+ AND identity_b_node.id = identity_assertions.b_id
636
+ AND identity_b_node.deleted_at IS NULL
637
+ ` : sql``;
638
+ const assertionIdKey = codePointOrderedAssertionId(target);
639
+ const rows = await target.execute(
640
+ asCompiledRowsSql(sql`
641
+ SELECT identity_assertions.graph_id AS graph_id,
642
+ identity_assertions.id AS id,
643
+ identity_assertions.rel AS rel,
644
+ identity_assertions.a_kind AS a_kind,
645
+ identity_assertions.a_id AS a_id,
646
+ identity_assertions.b_kind AS b_kind,
647
+ identity_assertions.b_id AS b_id,
648
+ identity_assertions.valid_from AS valid_from,
649
+ identity_assertions.valid_to AS valid_to,
650
+ identity_assertions.created_at AS created_at,
651
+ identity_assertions.updated_at AS updated_at,
652
+ identity_assertions.deleted_at AS deleted_at,
653
+ identity_assertions.ended_by_kind AS ended_by_kind,
654
+ identity_assertions.ended_by_id AS ended_by_id
655
+ FROM ${ctx.schema.identityAssertionsTable} identity_assertions
656
+ ${liveEndpointJoins}
657
+ WHERE identity_assertions.graph_id = ${ctx.graphId}
658
+ AND identity_assertions.deleted_at IS NULL
659
+ ${options.after === void 0 ? sql`` : sql`AND ${assertionIdKey} > ${options.after}`}
660
+ ${mode === "state" ? sql`AND identity_assertions.valid_to IS NULL` : sql``}
661
+ ${kindFilter}
662
+ ORDER BY ${assertionIdKey} ASC
663
+ LIMIT ${options.limit}
664
+ `)
665
+ );
666
+ const allowedKinds = filterKindsInMemory ? new Set(nodeKinds) : void 0;
667
+ const assertions = rows.filter(
668
+ (row) => allowedKinds === void 0 || allowedKinds.has(row.a_kind) && allowedKinds.has(row.b_kind)
669
+ ).map((row) => toTransferAssertion(normalizeIdentityAssertionRow(row)));
670
+ const nextAfter = rows.at(-1)?.id;
671
+ return {
672
+ assertions,
673
+ ...nextAfter === void 0 ? {} : { nextAfter },
674
+ done: rows.length < options.limit
675
+ };
676
+ }
677
+ async function readIdentityAssertionsForInterchange(ctx, mode, options) {
678
+ const page = await readIdentityAssertionPageAtTarget(ctx, ctx.backend, mode, {
679
+ ...options,
680
+ limit: 2147483647
681
+ });
682
+ return page.assertions;
683
+ }
684
+
685
+ // src/store/operations/write-transaction.ts
686
+ var writeTransactionSessions = /* @__PURE__ */ new WeakMap();
687
+ var heldGraphWriteLocks = /* @__PURE__ */ new WeakMap();
688
+ async function withWriteTransactionSession(target, ctx, fn) {
689
+ const session = {
690
+ lock: void 0,
691
+ wrote: false
692
+ };
693
+ writeTransactionSessions.set(target, session);
694
+ try {
695
+ const result = await fn();
696
+ if (session.wrote && ctx.revisionTrackingEnabled && !ctx.historyEnabled) {
697
+ await advanceRevisionClock(target, ctx.revisionSchema, ctx.graphId, true);
698
+ }
699
+ return result;
700
+ } finally {
701
+ writeTransactionSessions.delete(target);
702
+ }
703
+ }
704
+ async function lockSchemaVersionForStoreWrite(ctx, backend) {
705
+ const expectedVersion = ctx.schemaVersion;
706
+ if (expectedVersion === void 0) return;
707
+ if ("transaction" in backend && !backend.capabilities.transactions) {
708
+ throw new ConfigurationError(
709
+ "Schema-managed Store writes require a transactional backend so schema changes and entity writes can share one fence.",
710
+ {
711
+ code: "SCHEMA_WRITE_FENCE_UNSUPPORTED",
712
+ graphId: ctx.graphId
713
+ }
714
+ );
715
+ }
716
+ const lockSchemaVersionForWrite = backend.lockSchemaVersionForWrite;
717
+ if (lockSchemaVersionForWrite === void 0) {
718
+ throw new ConfigurationError(
719
+ "This backend cannot fence schema-managed Store writes against a concurrent schema change.",
720
+ {
721
+ code: "SCHEMA_WRITE_FENCE_UNSUPPORTED",
722
+ graphId: ctx.graphId
723
+ }
724
+ );
725
+ }
726
+ await lockSchemaVersionForWrite({
727
+ graphId: ctx.graphId,
728
+ expectedVersion
729
+ });
730
+ }
731
+ var CONSTRAINT_FENCE_ADVICE = {
732
+ edgeCardinality: 'declare the edge `cardinality: "many"` and enforce the limit in application code',
733
+ edgeMatchKeyConvergence: "use `create` with a caller-chosen id, whose uniqueness the edges primary key enforces, instead of `getOrCreateByEndpoints`",
734
+ nodeDisjointness: "drop the `disjointWith` axiom and keep ids distinct across those kinds yourself",
735
+ nodeUniquenessScope: 'scope the unique constraint to `"kind"`, which the uniques primary key enforces on its own'
736
+ };
737
+ function graphWriteFenceRefusal(ctx, backend, reason) {
738
+ if (!("transaction" in backend) || backend.capabilities.transactions) {
739
+ return void 0;
740
+ }
741
+ return new ConfigurationError(
742
+ "This backend cannot fence a constrained write: enforcing a declared constraint requires a transaction to scope the per-graph write lock to, and this backend has no transactions.",
743
+ {
744
+ code: "CONSTRAINT_WRITE_FENCE_UNSUPPORTED",
745
+ graphId: ctx.graphId,
746
+ constraint: reason
747
+ },
748
+ {
749
+ suggestion: `Use a transactional backend, or ${CONSTRAINT_FENCE_ADVICE[reason]}. Unconstrained writes need no fence and keep working on this backend.`
750
+ }
751
+ );
752
+ }
753
+ function runInWriteTransaction(ctx, backend, fn, options) {
754
+ const ownsWriteLock = "transaction" in backend && backend.capabilities.transactions;
755
+ const fenceReason = options?.fencesConstraintProbe;
756
+ const refusal = fenceReason === void 0 ? void 0 : graphWriteFenceRefusal(ctx, backend, fenceReason);
757
+ if (refusal !== void 0) return Promise.reject(refusal);
758
+ const needsGraphWriteLock = ctx.historyEnabled || ctx.revisionTrackingEnabled || fenceReason !== void 0;
759
+ return runOptionallyInTransaction(backend, async (target) => {
760
+ await lockSchemaVersionForStoreWrite(ctx, target);
761
+ const session = needsGraphWriteLock ? writeTransactionSessions.get(target) : void 0;
762
+ const held = heldGraphWriteLocks.get(target) ?? /* @__PURE__ */ new Set();
763
+ heldGraphWriteLocks.set(target, held);
764
+ const acquiresLock = needsGraphWriteLock && session?.lock === void 0 && !held.has(ctx.graphId);
765
+ if (acquiresLock) held.add(ctx.graphId);
766
+ const lock = acquiresLock ? await lockRecordedGraphWrite(target, ctx.graphId).catch(
767
+ (error) => {
768
+ held.delete(ctx.graphId);
769
+ throw error;
770
+ }
771
+ ) : session?.lock ?? uncapturedGraphWriteLock();
772
+ if (session !== void 0) session.lock = lock;
773
+ const result = await (acquiresLock ? fn(target, lock).finally(() => held.delete(ctx.graphId)) : fn(target, lock));
774
+ if (session !== void 0) {
775
+ session.wrote ||= options?.didWrite?.(result) ?? true;
776
+ return result;
777
+ }
778
+ if (ctx.revisionTrackingEnabled && !ctx.historyEnabled && (options?.didWrite?.(result) ?? true)) {
779
+ await advanceRevisionClock(
780
+ target,
781
+ ctx.revisionSchema,
782
+ ctx.graphId,
783
+ ownsWriteLock
784
+ );
785
+ }
786
+ return result;
787
+ });
788
+ }
789
+ function runHookedWriteOperation(ctx, opContext, backend, body, options) {
790
+ return ctx.withOperationHooks(
791
+ opContext,
792
+ () => runInWriteTransaction(ctx, backend, body, options)
793
+ );
794
+ }
795
+
796
+ // src/identity/service-components.ts
797
+ var UnionFind = class {
798
+ #parents = /* @__PURE__ */ new Map();
799
+ #sizes = /* @__PURE__ */ new Map();
800
+ #refs = /* @__PURE__ */ new Map();
801
+ add(ref) {
802
+ const key = refKey(ref);
803
+ if (this.#parents.has(key)) return;
804
+ this.#parents.set(key, key);
805
+ this.#sizes.set(key, 1);
806
+ this.#refs.set(key, ref);
807
+ }
808
+ // Iterative walk-then-compress: an adversarially ordered chain of unions can
809
+ // build O(N) depth, which a recursive find would blow the stack on.
810
+ #find(key) {
811
+ let root = key;
812
+ for (; ; ) {
813
+ const parent = this.#parents.get(root);
814
+ if (parent === void 0) {
815
+ throw new Error(`Unknown identity member ${root}`);
816
+ }
817
+ if (parent === root) break;
818
+ root = parent;
819
+ }
820
+ let cursor = key;
821
+ while (cursor !== root) {
822
+ const next = requireDefined(
823
+ this.#parents.get(cursor),
824
+ `Unknown identity member ${cursor}`
825
+ );
826
+ this.#parents.set(cursor, root);
827
+ cursor = next;
828
+ }
829
+ return root;
830
+ }
831
+ // Union by size keeps trees shallow. Canonical member selection is
832
+ // independent of root identity — components() sorts each group and takes the
833
+ // code-point-least member — so linking by size never changes the closure.
834
+ union(first, second) {
835
+ this.add(first);
836
+ this.add(second);
837
+ const firstRoot = this.#find(refKey(first));
838
+ const secondRoot = this.#find(refKey(second));
839
+ if (firstRoot === secondRoot) return;
840
+ const firstSize = requireDefined(this.#sizes.get(firstRoot));
841
+ const secondSize = requireDefined(this.#sizes.get(secondRoot));
842
+ const [root, child] = firstSize >= secondSize ? [firstRoot, secondRoot] : [secondRoot, firstRoot];
843
+ this.#parents.set(child, root);
844
+ this.#sizes.set(root, firstSize + secondSize);
845
+ }
846
+ // Public root accessor for callers that maintain their own member index
847
+ // incrementally (bulkAssertPairs) rather than re-deriving components().
848
+ root(ref) {
849
+ return this.#find(refKey(ref));
850
+ }
851
+ components() {
852
+ const groups = /* @__PURE__ */ new Map();
853
+ for (const [key, ref] of this.#refs) {
854
+ const root = this.#find(key);
855
+ const group = groups.get(root) ?? [];
856
+ group.push(ref);
857
+ groups.set(root, group);
858
+ }
859
+ const byMember = /* @__PURE__ */ new Map();
860
+ for (const group of groups.values()) {
861
+ const sorted = group.toSorted(
862
+ (left, right) => compareReferences(left, right)
863
+ );
864
+ for (const member of sorted) byMember.set(refKey(member), sorted);
865
+ }
866
+ return byMember;
867
+ }
868
+ };
869
+ function buildComponents(structuralNodes, assertions, sameIdAcrossKinds) {
870
+ const unionFind = new UnionFind();
871
+ const byId = /* @__PURE__ */ new Map();
872
+ for (const ref of structuralNodes) {
873
+ unionFind.add(ref);
874
+ const group = byId.get(ref.id) ?? [];
875
+ group.push(ref);
876
+ byId.set(ref.id, group);
877
+ }
878
+ if (sameIdAcrossKinds === "fold") {
879
+ for (const group of byId.values()) {
880
+ const first = group[0];
881
+ if (first === void 0) continue;
882
+ for (const member of group.slice(1)) unionFind.union(first, member);
883
+ }
884
+ }
885
+ for (const assertion of assertions) {
886
+ if (assertion.rel !== "same") continue;
887
+ unionFind.union(
888
+ { kind: assertion.a_kind, id: assertion.a_id },
889
+ { kind: assertion.b_kind, id: assertion.b_id }
890
+ );
891
+ }
892
+ return unionFind.components();
893
+ }
894
+ function identityActiveKinds(registry) {
895
+ return new Set(registry.nodeKinds.keys());
896
+ }
897
+ async function loadSnapshot(target, schema, graphId, coordinate, allowedKinds, sameIdAcrossKinds = "fold") {
898
+ const currentInstant = nowIso();
899
+ const [nodes, assertions] = await Promise.all([
900
+ loadNodeSnapshot(target, schema, graphId, coordinate),
901
+ loadAssertions(target, schema, graphId, coordinate, currentInstant)
902
+ ]);
903
+ const scopedNodes = allowedKinds === void 0 ? nodes : nodes.filter((node) => allowedKinds.has(node.ref.kind));
904
+ const scopedAssertions = allowedKinds === void 0 ? assertions : assertions.filter(
905
+ (assertion) => allowedKinds.has(assertion.a_kind) && allowedKinds.has(assertion.b_kind)
906
+ );
907
+ const structuralNodes = scopedNodes.filter((node) => node.deletedAt === void 0).map((node) => node.ref);
908
+ return {
909
+ nodes: scopedNodes,
910
+ structuralNodes,
911
+ assertions: scopedAssertions,
912
+ components: buildComponents(
913
+ structuralNodes,
914
+ scopedAssertions,
915
+ sameIdAcrossKinds
916
+ )
917
+ };
918
+ }
919
+ function componentFor(snapshot, ref) {
920
+ return snapshot.components.get(refKey(ref)) ?? [ref];
921
+ }
922
+ function sameComponent(snapshot, first, second) {
923
+ return containsRef(componentFor(snapshot, first), second);
924
+ }
925
+ function kindsOf(members) {
926
+ return new Set(members.map((member) => member.kind));
927
+ }
928
+ function classHasDisjointKinds(registry, first, second) {
929
+ return kindSetsHaveDisjointKinds(registry, kindsOf(first), kindsOf(second));
930
+ }
931
+ function kindSetsHaveDisjointKinds(registry, first, second) {
932
+ for (const left of first) {
933
+ for (const right of second) {
934
+ if (registry.areDisjoint(left, right)) return [left, right];
935
+ }
936
+ }
937
+ return void 0;
938
+ }
939
+ function indexDifferentAssertion(index, firstRoot, secondRoot, assertion) {
940
+ const firstNeighbors = index.get(firstRoot) ?? /* @__PURE__ */ new Map();
941
+ const secondNeighbors = index.get(secondRoot) ?? /* @__PURE__ */ new Map();
942
+ if (!firstNeighbors.has(secondRoot)) {
943
+ firstNeighbors.set(secondRoot, assertion);
944
+ }
945
+ if (!secondNeighbors.has(firstRoot)) {
946
+ secondNeighbors.set(firstRoot, assertion);
947
+ }
948
+ index.set(firstRoot, firstNeighbors);
949
+ index.set(secondRoot, secondNeighbors);
950
+ }
951
+ function mergeDifferentAssertionRoots(index, survivingRoot, retiredRoot) {
952
+ const survivingNeighbors = index.get(survivingRoot) ?? /* @__PURE__ */ new Map();
953
+ const retiredNeighbors = index.get(retiredRoot);
954
+ survivingNeighbors.delete(retiredRoot);
955
+ if (retiredNeighbors !== void 0) {
956
+ for (const [neighborRoot, assertion] of retiredNeighbors) {
957
+ if (neighborRoot === survivingRoot) continue;
958
+ const canonicalAssertion = survivingNeighbors.get(neighborRoot) ?? assertion;
959
+ survivingNeighbors.set(neighborRoot, canonicalAssertion);
960
+ const neighborMap = index.get(neighborRoot);
961
+ if (neighborMap !== void 0) {
962
+ neighborMap.delete(retiredRoot);
963
+ neighborMap.set(survivingRoot, canonicalAssertion);
964
+ }
965
+ }
966
+ }
967
+ index.delete(retiredRoot);
968
+ if (survivingNeighbors.size === 0) {
969
+ index.delete(survivingRoot);
970
+ } else {
971
+ index.set(survivingRoot, survivingNeighbors);
972
+ }
973
+ }
974
+ function validateSnapshotIntegrity(snapshot, registry, graphId) {
975
+ const structuralKeys = new Set(
976
+ snapshot.structuralNodes.map((ref) => refKey(ref))
977
+ );
978
+ for (const assertion of snapshot.assertions) {
979
+ const a = { kind: assertion.a_kind, id: assertion.a_id };
980
+ const b = { kind: assertion.b_kind, id: assertion.b_id };
981
+ if (!structuralKeys.has(refKey(a)) || !structuralKeys.has(refKey(b))) {
982
+ throw new ConfigurationError(
983
+ "Operational Identity contains a current assertion with a missing or deleted endpoint.",
984
+ {
985
+ code: "IDENTITY_SCHEMA_CONTRADICTION",
986
+ graphId,
987
+ assertionId: assertion.id,
988
+ a,
989
+ b
990
+ }
991
+ );
992
+ }
993
+ if (assertion.rel === "different" && sameComponent(snapshot, a, b)) {
994
+ throw new ConfigurationError(
995
+ "Operational Identity contains a different assertion within one identity class.",
996
+ {
997
+ code: "IDENTITY_SCHEMA_CONTRADICTION",
998
+ graphId,
999
+ assertionId: assertion.id,
1000
+ a,
1001
+ b
1002
+ }
1003
+ );
1004
+ }
1005
+ }
1006
+ const visited = /* @__PURE__ */ new Set();
1007
+ for (const [memberKey, component] of snapshot.components) {
1008
+ if (visited.has(memberKey)) continue;
1009
+ for (const member of component) visited.add(refKey(member));
1010
+ const conflictingKinds = classHasDisjointKinds(
1011
+ registry,
1012
+ component,
1013
+ component
1014
+ );
1015
+ if (conflictingKinds !== void 0) {
1016
+ throw new ConfigurationError(
1017
+ "Operational Identity class conflicts with ontology disjointness.",
1018
+ {
1019
+ code: "IDENTITY_SCHEMA_CONTRADICTION",
1020
+ graphId,
1021
+ classMembers: component,
1022
+ conflictingKinds
1023
+ }
1024
+ );
1025
+ }
1026
+ }
1027
+ }
1028
+ function closureMismatchError(graphId, detail) {
1029
+ return new ConfigurationError(
1030
+ "Operational Identity materialized closure does not match computed identity components.",
1031
+ { code: "IDENTITY_SCHEMA_CONTRADICTION", graphId, ...detail },
1032
+ {
1033
+ suggestion: "Run rebuildIdentityClosure(store) to rebuild the materialized identity closure."
1034
+ }
1035
+ );
1036
+ }
1037
+ async function assertClosureMatchesComponents(target, schema, graphId, components) {
1038
+ const expected = /* @__PURE__ */ new Map();
1039
+ const emitted = /* @__PURE__ */ new Set();
1040
+ for (const [memberKey, component] of components) {
1041
+ if (emitted.has(memberKey) || component.length < 2) continue;
1042
+ const canonical = requireDefined(component[0]);
1043
+ for (const member of component) {
1044
+ const key = refKey(member);
1045
+ emitted.add(key);
1046
+ expected.set(key, { member, classRef: canonical });
1047
+ }
1048
+ }
1049
+ const rows = await target.execute(
1050
+ asCompiledRowsSql(sql`
1051
+ SELECT member_kind, member_id, class_kind, class_id
1052
+ FROM ${schema.identityClosureTable}
1053
+ WHERE graph_id = ${graphId}
1054
+ `)
1055
+ );
1056
+ const seen = /* @__PURE__ */ new Set();
1057
+ for (const row of rows) {
1058
+ const member = { kind: row.member_kind, id: row.member_id };
1059
+ const memberKey = refKey(member);
1060
+ const match = expected.get(memberKey);
1061
+ if (match?.classRef.kind !== row.class_kind || match.classRef.id !== row.class_id) {
1062
+ throw closureMismatchError(graphId, {
1063
+ member,
1064
+ class: { kind: row.class_kind, id: row.class_id },
1065
+ expectedClass: match?.classRef
1066
+ });
1067
+ }
1068
+ seen.add(memberKey);
1069
+ }
1070
+ for (const [memberKey, { member, classRef }] of expected) {
1071
+ if (seen.has(memberKey)) continue;
1072
+ throw closureMismatchError(graphId, {
1073
+ member,
1074
+ expectedClass: classRef,
1075
+ reason: "missing-closure-row"
1076
+ });
1077
+ }
1078
+ }
1079
+ function selfAssertionError(relation) {
1080
+ return new ValidationError(
1081
+ `Identity ${relation} assertions require two distinct node references.`,
1082
+ {
1083
+ issues: [
1084
+ {
1085
+ path: "pair",
1086
+ message: "Identity self-assertions are not allowed",
1087
+ code: "IDENTITY_SELF_ASSERTION"
1088
+ }
1089
+ ]
1090
+ },
1091
+ {
1092
+ suggestion: "Filter reflexive pairs before calling an identity assertion method."
1093
+ }
1094
+ );
1095
+ }
1096
+ async function requireLiveEndpoint(target, graphId, ref) {
1097
+ const row = await target.getNode(graphId, ref.kind, ref.id);
1098
+ if (row === void 0 || row.deleted_at !== void 0) {
1099
+ throw new NodeNotFoundError(ref.kind, ref.id);
1100
+ }
1101
+ }
1102
+ async function loadLiveReferences(target, schema, graphId, references) {
1103
+ if (references.length === 0) return [];
1104
+ const chunkSize = identityChunkSize(target, {
1105
+ fixedParameters: 1,
1106
+ maxItems: MAX_REFERENCE_CHUNK_SIZE,
1107
+ parametersPerItem: 2
1108
+ });
1109
+ if (references.length > chunkSize) {
1110
+ const byKey = /* @__PURE__ */ new Map();
1111
+ for (const refChunk of chunk(references, chunkSize)) {
1112
+ const live = await loadLiveReferences(target, schema, graphId, refChunk);
1113
+ for (const ref of live) byKey.set(refKey(ref), ref);
1114
+ }
1115
+ return [...byKey.values()];
1116
+ }
1117
+ const matches = referenceCondition(sql`kind`, sql`id`, references);
1118
+ const rows = await target.execute(
1119
+ asCompiledRowsSql(sql`
1120
+ SELECT kind, id
1121
+ FROM ${schema.nodesTable}
1122
+ WHERE graph_id = ${graphId}
1123
+ AND deleted_at IS NULL
1124
+ AND ${matches}
1125
+ `)
1126
+ );
1127
+ return rows.map((row) => ({ kind: row.kind, id: row.id }));
1128
+ }
1129
+ async function requireLiveEndpoints(target, schema, graphId, references) {
1130
+ const uniqueByKey = /* @__PURE__ */ new Map();
1131
+ for (const ref of references) uniqueByKey.set(refKey(ref), ref);
1132
+ const live = await loadLiveReferences(target, schema, graphId, references);
1133
+ const liveKeys = new Set(live.map((ref) => refKey(ref)));
1134
+ for (const [key, ref] of uniqueByKey) {
1135
+ if (!liveKeys.has(key)) throw new NodeNotFoundError(ref.kind, ref.id);
1136
+ }
1137
+ }
1138
+ async function requireStructuralEndpoints(target, schema, graphId, references) {
1139
+ if (references.length === 0) return;
1140
+ const uniqueByKey = /* @__PURE__ */ new Map();
1141
+ for (const ref of references) uniqueByKey.set(refKey(ref), ref);
1142
+ const unique = [...uniqueByKey.values()];
1143
+ const chunkSize = identityChunkSize(target, {
1144
+ fixedParameters: 1,
1145
+ maxItems: MAX_REFERENCE_CHUNK_SIZE,
1146
+ parametersPerItem: 2
1147
+ });
1148
+ const presentKeys = /* @__PURE__ */ new Set();
1149
+ for (const refChunk of chunk(unique, chunkSize)) {
1150
+ const matches = referenceCondition(sql`kind`, sql`id`, refChunk);
1151
+ const rows = await target.execute(
1152
+ asCompiledRowsSql(sql`
1153
+ SELECT kind, id
1154
+ FROM ${schema.nodesTable}
1155
+ WHERE graph_id = ${graphId}
1156
+ AND ${matches}
1157
+ `)
1158
+ );
1159
+ for (const row of rows) {
1160
+ presentKeys.add(refKey({ kind: row.kind, id: row.id }));
1161
+ }
1162
+ }
1163
+ for (const [key, ref] of uniqueByKey) {
1164
+ if (!presentKeys.has(key)) throw new NodeNotFoundError(ref.kind, ref.id);
1165
+ }
1166
+ }
1167
+
1168
+ // src/identity/separation.ts
1169
+ var MAX_SEPARATION_INSERT_CHUNK_SIZE = 100;
1170
+ function identityClassKey(ref) {
1171
+ return encodeTupleKey([ref.kind, ref.id]);
1172
+ }
1173
+ var PAIR_KEY_SEPARATOR = "\0";
1174
+ function pairKey(pair) {
1175
+ return `${pair.low}${PAIR_KEY_SEPARATOR}${pair.high}`;
1176
+ }
1177
+ function orderedPair(first, second) {
1178
+ return compareCodePoints(first, second) <= 0 ? { low: first, high: second } : { low: second, high: first };
1179
+ }
1180
+ function buildSeparationProjection(assertions, classKeyOf) {
1181
+ const pairs = /* @__PURE__ */ new Map();
1182
+ const collapsed = [];
1183
+ for (const assertion of assertions) {
1184
+ if (assertion.rel !== "different") continue;
1185
+ const a = { kind: assertion.a_kind, id: assertion.a_id };
1186
+ const b = { kind: assertion.b_kind, id: assertion.b_id };
1187
+ const pair = orderedPair(classKeyOf(a), classKeyOf(b));
1188
+ pairs.set(pairKey(pair), pair);
1189
+ if (pair.low === pair.high) {
1190
+ collapsed.push({
1191
+ classKey: pair.low,
1192
+ assertionId: assertion.id,
1193
+ a,
1194
+ b
1195
+ });
1196
+ }
1197
+ }
1198
+ return { pairs: [...pairs.values()], collapsed };
1199
+ }
1200
+ async function deleteSeparationForGraph(target, schema, graphId) {
1201
+ await executeIdentityStatement(
1202
+ target,
1203
+ sql`DELETE FROM ${schema.identitySeparationTable} WHERE graph_id = ${graphId}`
1204
+ );
1205
+ }
1206
+ async function deleteSeparationForClassKeys(target, schema, graphId, classKeys) {
1207
+ const unique = [...new Set(classKeys)];
1208
+ if (unique.length === 0) return;
1209
+ const chunkSize = identityChunkSize(target, {
1210
+ fixedParameters: 1,
1211
+ maxItems: MAX_REFERENCE_CHUNK_SIZE,
1212
+ parametersPerItem: 2
1213
+ });
1214
+ for (const keyChunk of chunk(unique, chunkSize)) {
1215
+ const keyList = sql.join(
1216
+ keyChunk.map((key) => sql`${key}`),
1217
+ sql`, `
1218
+ );
1219
+ await executeIdentityStatement(
1220
+ target,
1221
+ sql`
1222
+ DELETE FROM ${schema.identitySeparationTable}
1223
+ WHERE graph_id = ${graphId}
1224
+ AND (class_key_low IN (${keyList}) OR class_key_high IN (${keyList}))
1225
+ `
1226
+ );
1227
+ }
1228
+ }
1229
+ async function isSeparated(target, schema, graphId, firstClassKey, secondClassKey, registry) {
1230
+ if (firstClassKey === secondClassKey) return false;
1231
+ const probe = await probeSeparationPair(
1232
+ target,
1233
+ schema,
1234
+ graphId,
1235
+ orderedPair(firstClassKey, secondClassKey)
1236
+ );
1237
+ if (probe.pairSeparated) return true;
1238
+ if (probe.graphHasRows) return false;
1239
+ if (separationReadinessProven(registry, graphId)) return false;
1240
+ if (await hasLiveDifferentAssertions(target, schema, graphId, registry)) {
1241
+ throw separationUnfilledError(graphId, schema);
1242
+ }
1243
+ proveSeparationReadiness(registry, graphId);
1244
+ return false;
1245
+ }
1246
+ var provenSeparationReadiness = /* @__PURE__ */ new WeakMap();
1247
+ function separationReadinessProven(registry, graphId) {
1248
+ return provenSeparationReadiness.get(registry)?.has(graphId) === true;
1249
+ }
1250
+ function proveSeparationReadiness(registry, graphId) {
1251
+ const proven = provenSeparationReadiness.get(registry) ?? /* @__PURE__ */ new Set();
1252
+ proven.add(graphId);
1253
+ provenSeparationReadiness.set(registry, proven);
1254
+ }
1255
+ async function probeSeparationPair(target, schema, graphId, pair) {
1256
+ try {
1257
+ const rows = await target.execute(
1258
+ asCompiledRowsSql(sql`
1259
+ SELECT
1260
+ (
1261
+ SELECT COUNT(*) FROM ${schema.identitySeparationTable}
1262
+ WHERE graph_id = ${graphId}
1263
+ AND class_key_low = ${pair.low}
1264
+ AND class_key_high = ${pair.high}
1265
+ ) AS pair_rows,
1266
+ (
1267
+ SELECT COUNT(*) FROM (
1268
+ SELECT 1 AS present FROM ${schema.identitySeparationTable}
1269
+ WHERE graph_id = ${graphId}
1270
+ LIMIT 1
1271
+ ) graph_probe
1272
+ ) AS graph_rows
1273
+ `)
1274
+ );
1275
+ const row = rows[0];
1276
+ return {
1277
+ graphHasRows: countOf(row, "graph_rows") > 0,
1278
+ pairSeparated: countOf(row, "pair_rows") > 0
1279
+ };
1280
+ } catch (error) {
1281
+ if (!isMissingTableError(error)) throw error;
1282
+ throw separationUnreadableError(graphId, schema, error);
1283
+ }
1284
+ }
1285
+ function countOf(row, column) {
1286
+ return Number(row?.[column] ?? 0);
1287
+ }
1288
+ function separationUnreadableError(graphId, schema, cause) {
1289
+ return new ConfigurationError(
1290
+ "Operational Identity could not read the materialized separation relation.",
1291
+ {
1292
+ code: "IDENTITY_STORAGE_MISSING",
1293
+ graphId,
1294
+ tables: [schema.tables.identitySeparation]
1295
+ },
1296
+ {
1297
+ cause,
1298
+ suggestion: "Recreate the identity separation relation with the standard TypeGraph DDL, open the Store, and run rebuildIdentityClosure(store) before serving traffic."
1299
+ }
1300
+ );
1301
+ }
1302
+ function separationUnfilledError(graphId, schema) {
1303
+ return new ConfigurationError(
1304
+ "Operational Identity found the separation relation present but never filled for this graph.",
1305
+ {
1306
+ code: "IDENTITY_STORAGE_MISSING",
1307
+ graphId,
1308
+ reason: "unfilled",
1309
+ tables: [schema.tables.identitySeparation]
1310
+ },
1311
+ {
1312
+ suggestion: "Reopen the Store (createStoreWithSchema rebuilds the derived identity relations when they are missing rows), or run rebuildIdentityClosure(store) before serving traffic. A Store handle opened while the relation did not exist keeps failing until it is reopened."
1313
+ }
1314
+ );
1315
+ }
1316
+ function unexpectedSeparationError(graphId, firstClassKey, secondClassKey) {
1317
+ return separationMismatchError(graphId, {
1318
+ unexpected: orderedPair(firstClassKey, secondClassKey)
1319
+ });
1320
+ }
1321
+ async function separationRebuildRequired(target, schema, graphId, options) {
1322
+ if (options.relationExists && await hasSeparationRows(target, schema, graphId)) {
1323
+ return false;
1324
+ }
1325
+ return hasLiveDifferentAssertions(target, schema, graphId, options.registry);
1326
+ }
1327
+ async function hasSeparationRows(target, schema, graphId) {
1328
+ const rows = await target.execute(
1329
+ asCompiledRowsSql(sql`
1330
+ SELECT 1 AS present
1331
+ FROM ${schema.identitySeparationTable}
1332
+ WHERE graph_id = ${graphId}
1333
+ LIMIT 1
1334
+ `)
1335
+ );
1336
+ return rows.length > 0;
1337
+ }
1338
+ async function hasLiveDifferentAssertions(target, schema, graphId, registry) {
1339
+ if (!await probeAnyLiveDifferent(target, schema, graphId)) return false;
1340
+ const activeKinds = [...identityActiveKinds(registry)];
1341
+ if (activeKinds.length === 0) return false;
1342
+ const kindChunks = chunk(activeKinds, owedProbeChunkSize(target));
1343
+ for (const first of kindChunks) {
1344
+ for (const second of kindChunks) {
1345
+ if (await probeOwedSeparation(target, schema, graphId, [first, second])) {
1346
+ return true;
1347
+ }
1348
+ }
1349
+ }
1350
+ return false;
1351
+ }
1352
+ var IDENTITY_SNAPSHOT_PARAMETERS = 4;
1353
+ var OWED_SEPARATION_PROBE_PARAMETERS = IDENTITY_SNAPSHOT_PARAMETERS + 2;
1354
+ async function probeAnyLiveDifferent(target, schema, graphId) {
1355
+ const rows = await target.execute(
1356
+ asCompiledRowsSql(sql`
1357
+ SELECT 1 AS present
1358
+ FROM (${liveDifferentSource(schema, graphId)}) live_different
1359
+ LIMIT 1
1360
+ `)
1361
+ );
1362
+ return rows.length > 0;
1363
+ }
1364
+ async function probeOwedSeparation(target, schema, graphId, kinds) {
1365
+ const rows = await target.execute(
1366
+ asCompiledRowsSql(sql`
1367
+ SELECT 1 AS present
1368
+ FROM (${liveDifferentSource(schema, graphId)}) live_different
1369
+ LEFT JOIN ${schema.identityClosureTable} class_a
1370
+ ON class_a.graph_id = ${graphId}
1371
+ AND class_a.member_kind = live_different.a_kind
1372
+ AND class_a.member_id = live_different.a_id
1373
+ LEFT JOIN ${schema.identityClosureTable} class_b
1374
+ ON class_b.graph_id = ${graphId}
1375
+ AND class_b.member_kind = live_different.b_kind
1376
+ AND class_b.member_id = live_different.b_id
1377
+ WHERE live_different.a_kind IN (${kindList(kinds[0])})
1378
+ AND live_different.b_kind IN (${kindList(kinds[1])})
1379
+ AND (
1380
+ COALESCE(class_a.class_kind, live_different.a_kind)
1381
+ <> COALESCE(class_b.class_kind, live_different.b_kind)
1382
+ OR COALESCE(class_a.class_id, live_different.a_id)
1383
+ <> COALESCE(class_b.class_id, live_different.b_id)
1384
+ )
1385
+ LIMIT 1
1386
+ `)
1387
+ );
1388
+ return rows.length > 0;
1389
+ }
1390
+ function owedProbeChunkSize(target) {
1391
+ return identityChunkSize(target, {
1392
+ fixedParameters: OWED_SEPARATION_PROBE_PARAMETERS,
1393
+ maxItems: MAX_REFERENCE_CHUNK_SIZE,
1394
+ parametersPerItem: 2
1395
+ });
1396
+ }
1397
+ function liveDifferentSource(schema, graphId) {
1398
+ return identityAssertionSnapshotSource(
1399
+ schema,
1400
+ graphId,
1401
+ identitySqlCoordinate(void 0, nowIso()),
1402
+ "different"
1403
+ );
1404
+ }
1405
+ function kindList(kinds) {
1406
+ return sql.join(
1407
+ kinds.map((kind) => sql`${kind}`),
1408
+ sql`, `
1409
+ );
1410
+ }
1411
+ async function readSeparationForGraph(target, schema, graphId) {
1412
+ const rows = await target.execute(
1413
+ asCompiledRowsSql(sql`
1414
+ SELECT class_key_low, class_key_high
1415
+ FROM ${schema.identitySeparationTable}
1416
+ WHERE graph_id = ${graphId}
1417
+ `)
1418
+ );
1419
+ return rows.map((row) => ({
1420
+ low: row.class_key_low,
1421
+ high: row.class_key_high
1422
+ }));
1423
+ }
1424
+ async function insertSeparationRows(target, schema, graphId, projection) {
1425
+ const chunkSize = identityChunkSize(target, {
1426
+ fixedParameters: 0,
1427
+ maxItems: MAX_SEPARATION_INSERT_CHUNK_SIZE,
1428
+ parametersPerItem: 3
1429
+ });
1430
+ for (const pairChunk of chunk(projection.pairs, chunkSize)) {
1431
+ const values = pairChunk.map(
1432
+ (pair) => sql`(${graphId}, ${pair.low}, ${pair.high})`
1433
+ );
1434
+ try {
1435
+ await executeIdentityStatement(
1436
+ target,
1437
+ sql`
1438
+ INSERT INTO ${schema.identitySeparationTable} (
1439
+ graph_id, class_key_low, class_key_high
1440
+ ) VALUES ${sql.join(values, sql`, `)}
1441
+ `
1442
+ );
1443
+ } catch (error) {
1444
+ const collapsed = collapsedIn(projection, pairChunk);
1445
+ if (collapsed === void 0) throw error;
1446
+ throw new IdentitySeparationViolationError(
1447
+ { graphId, enforcedBy: "database", ...collapsed },
1448
+ { cause: error }
1449
+ );
1450
+ }
1451
+ }
1452
+ const accepted = projection.collapsed[0];
1453
+ if (accepted === void 0) return;
1454
+ throw new IdentitySeparationViolationError({
1455
+ graphId,
1456
+ enforcedBy: "writer",
1457
+ ...accepted
1458
+ });
1459
+ }
1460
+ function collapsedIn(projection, pairChunk) {
1461
+ const keys = new Set(pairChunk.map((pair) => pairKey(pair)));
1462
+ return projection.collapsed.find(
1463
+ (collapsed) => keys.has(pairKey({ low: collapsed.classKey, high: collapsed.classKey }))
1464
+ );
1465
+ }
1466
+ function assertSeparationMatchesProjection(graphId, persisted, projection) {
1467
+ const expected = new Map(
1468
+ projection.pairs.map((pair) => [pairKey(pair), pair])
1469
+ );
1470
+ const seen = /* @__PURE__ */ new Set();
1471
+ for (const pair of persisted) {
1472
+ const key = pairKey(pair);
1473
+ if (!expected.has(key)) {
1474
+ throw separationMismatchError(graphId, { unexpected: pair });
1475
+ }
1476
+ seen.add(key);
1477
+ }
1478
+ for (const [key, pair] of expected) {
1479
+ if (seen.has(key)) continue;
1480
+ throw separationMismatchError(graphId, { missing: pair });
1481
+ }
1482
+ }
1483
+ function separationMismatchError(graphId, detail) {
1484
+ return new ConfigurationError(
1485
+ "Operational Identity materialized separation relation does not match the assertion ledger.",
1486
+ { code: "IDENTITY_SCHEMA_CONTRADICTION", graphId, ...detail },
1487
+ {
1488
+ suggestion: "Run rebuildIdentityClosure(store) to rebuild the materialized identity relations."
1489
+ }
1490
+ );
1491
+ }
1492
+
1493
+ // src/identity/service-mutation.ts
1494
+ async function validateCurrentRelation(ctx, target, relation, operation, a, b) {
1495
+ const classes = await loadCurrentStructuralClasses(
1496
+ target,
1497
+ ctx.schema,
1498
+ ctx.graphId,
1499
+ [a, b]
1500
+ );
1501
+ const aClass = requireDefined(classes.get(refKey(a)));
1502
+ const bClass = requireDefined(classes.get(refKey(b)));
1503
+ if (relation === "different") {
1504
+ if (!containsRef(aClass, b)) return;
1505
+ throw new IdentityContradictionError({
1506
+ operation,
1507
+ a,
1508
+ b,
1509
+ reason: "same-class"
1510
+ });
1511
+ }
1512
+ const aKey = currentClassKey(aClass);
1513
+ const bKey = currentClassKey(bClass);
1514
+ if (await isSeparated(target, ctx.schema, ctx.graphId, aKey, bKey, ctx.registry)) {
1515
+ const different = await loadSpanningDifferentAssertion(
1516
+ target,
1517
+ ctx.schema,
1518
+ ctx.graphId,
1519
+ aClass,
1520
+ bClass
1521
+ );
1522
+ if (different === void 0)
1523
+ throw unexpectedSeparationError(ctx.graphId, aKey, bKey);
1524
+ throw new IdentityContradictionError({
1525
+ operation,
1526
+ a,
1527
+ b,
1528
+ reason: "different-assertion",
1529
+ conflictingAssertionId: different.id
1530
+ });
1531
+ }
1532
+ const disjointKinds = classHasDisjointKinds(ctx.registry, aClass, bClass);
1533
+ if (disjointKinds === void 0) return;
1534
+ throw new IdentityContradictionError({
1535
+ operation,
1536
+ a,
1537
+ b,
1538
+ reason: "disjoint-kinds",
1539
+ conflictingKinds: disjointKinds
1540
+ });
1541
+ }
1542
+ async function currentAssertionForPair(target, schema, graphId, relation, a, b) {
1543
+ const rows = await target.execute(
1544
+ asCompiledRowsSql(sql`
1545
+ SELECT ${IDENTITY_ASSERTION_COLUMNS}
1546
+ FROM ${schema.identityAssertionsTable}
1547
+ WHERE graph_id = ${graphId}
1548
+ AND rel = ${relation}
1549
+ AND a_kind = ${a.kind}
1550
+ AND a_id = ${a.id}
1551
+ AND b_kind = ${b.kind}
1552
+ AND b_id = ${b.id}
1553
+ AND valid_to IS NULL
1554
+ AND deleted_at IS NULL
1555
+ LIMIT 1
1556
+ `)
1557
+ );
1558
+ return rows[0] === void 0 ? void 0 : normalizeIdentityAssertionRow(rows[0]);
1559
+ }
1560
+ async function insertAssertion(target, schema, graphId, relation, a, b, timestamp, touch, preserved) {
1561
+ const row = buildAssertionRow(graphId, relation, a, b, timestamp, preserved);
1562
+ await insertAssertionRows(target, schema, [row]);
1563
+ touch(graphId, row.id, row);
1564
+ return row;
1565
+ }
1566
+ function buildAssertionRow(graphId, relation, a, b, timestamp, preserved) {
1567
+ return {
1568
+ graph_id: graphId,
1569
+ id: preserved?.id ?? generateId(),
1570
+ rel: relation,
1571
+ a_kind: a.kind,
1572
+ a_id: a.id,
1573
+ b_kind: b.kind,
1574
+ b_id: b.id,
1575
+ valid_from: preserved?.validFrom ?? timestamp,
1576
+ valid_to: void 0,
1577
+ created_at: timestamp,
1578
+ updated_at: timestamp,
1579
+ deleted_at: void 0,
1580
+ ended_by_kind: void 0,
1581
+ ended_by_id: void 0
1582
+ };
1583
+ }
1584
+ function nullableAssertionValue(value) {
1585
+ return value === void 0 ? sql`NULL` : sql`${value}`;
1586
+ }
1587
+ async function insertAssertionRows(target, schema, rows) {
1588
+ if (rows.length === 0) return;
1589
+ const chunkSize = identityChunkSize(target, {
1590
+ fixedParameters: 0,
1591
+ maxItems: MAX_ASSERTION_INSERT_CHUNK_SIZE,
1592
+ parametersPerItem: 14
1593
+ });
1594
+ for (const rowChunk of chunk(rows, chunkSize)) {
1595
+ const values = rowChunk.map(
1596
+ (row) => sql`
1597
+ (
1598
+ ${row.graph_id}, ${row.id}, ${row.rel}, ${row.a_kind}, ${row.a_id},
1599
+ ${row.b_kind}, ${row.b_id}, ${row.valid_from},
1600
+ ${nullableAssertionValue(row.valid_to)},
1601
+ ${row.created_at}, ${row.updated_at},
1602
+ ${nullableAssertionValue(row.deleted_at)},
1603
+ ${nullableAssertionValue(row.ended_by_kind)},
1604
+ ${nullableAssertionValue(row.ended_by_id)}
1605
+ )
1606
+ `
1607
+ );
1608
+ await executeIdentityStatement(
1609
+ target,
1610
+ sql`
1611
+ INSERT INTO ${schema.identityAssertionsTable} (${IDENTITY_ASSERTION_COLUMNS}) VALUES ${sql.join(values, sql`, `)}
1612
+ `
1613
+ );
1614
+ }
1615
+ }
1616
+ async function loadAssertionsByIds(target, schema, graphId, ids) {
1617
+ const uniqueIds = [...new Set(ids)];
1618
+ const byId = /* @__PURE__ */ new Map();
1619
+ if (uniqueIds.length === 0) return byId;
1620
+ const chunkSize = identityChunkSize(target, {
1621
+ fixedParameters: 1,
1622
+ maxItems: MAX_REFERENCE_CHUNK_SIZE,
1623
+ parametersPerItem: 1
1624
+ });
1625
+ for (const idChunk of chunk(uniqueIds, chunkSize)) {
1626
+ const idList = sql.join(
1627
+ idChunk.map((id) => sql`${id}`),
1628
+ sql`, `
1629
+ );
1630
+ const rows = await target.execute(
1631
+ asCompiledRowsSql(sql`
1632
+ SELECT ${IDENTITY_ASSERTION_COLUMNS}
1633
+ FROM ${schema.identityAssertionsTable}
1634
+ WHERE graph_id = ${graphId} AND id IN (${idList})
1635
+ `)
1636
+ );
1637
+ for (const row of rows) {
1638
+ byId.set(row.id, normalizeIdentityAssertionRow(row));
1639
+ }
1640
+ }
1641
+ return byId;
1642
+ }
1643
+ async function loadCurrentClassAnchors(target, schema, graphId, references) {
1644
+ const uniqueByKey = /* @__PURE__ */ new Map();
1645
+ for (const ref of references) uniqueByKey.set(refKey(ref), ref);
1646
+ const uniqueReferences = [...uniqueByKey.values()];
1647
+ const anchors = /* @__PURE__ */ new Map();
1648
+ if (uniqueReferences.length === 0) return anchors;
1649
+ const chunkSize = identityChunkSize(target, {
1650
+ fixedParameters: 1,
1651
+ maxItems: MAX_REFERENCE_CHUNK_SIZE,
1652
+ parametersPerItem: 2
1653
+ });
1654
+ for (const refChunk of chunk(uniqueReferences, chunkSize)) {
1655
+ const seedRows = sql.join(
1656
+ refChunk.map((ref) => sql`(${ref.kind}, ${ref.id})`),
1657
+ sql`, `
1658
+ );
1659
+ const rows = await target.execute(
1660
+ asCompiledRowsSql(sql`
1661
+ WITH seeds(seed_kind, seed_id) AS (
1662
+ VALUES ${seedRows}
1663
+ )
1664
+ SELECT seeds.seed_kind, seeds.seed_id,
1665
+ COALESCE(anchor.class_kind, seeds.seed_kind) AS class_kind,
1666
+ COALESCE(anchor.class_id, seeds.seed_id) AS class_id
1667
+ FROM seeds
1668
+ LEFT JOIN ${schema.identityClosureTable} anchor
1669
+ ON anchor.graph_id = ${graphId}
1670
+ AND anchor.member_kind = seeds.seed_kind
1671
+ AND anchor.member_id = seeds.seed_id
1672
+ `)
1673
+ );
1674
+ for (const row of rows) {
1675
+ anchors.set(refKey({ kind: row.seed_kind, id: row.seed_id }), {
1676
+ kind: row.class_kind,
1677
+ id: row.class_id
1678
+ });
1679
+ }
1680
+ }
1681
+ return anchors;
1682
+ }
1683
+ function currentClassKey(members) {
1684
+ return identityClassKey(requireDefined(members[0]));
1685
+ }
1686
+ function snapshotClassKey(snapshot, ref) {
1687
+ return identityClassKey(requireDefined(componentFor(snapshot, ref)[0]));
1688
+ }
1689
+ async function replaceSeparationForMembers(target, schema, graphId, members) {
1690
+ const uniqueByKey = /* @__PURE__ */ new Map();
1691
+ for (const member of members) uniqueByKey.set(refKey(member), member);
1692
+ const unique = [...uniqueByKey.values()];
1693
+ if (unique.length === 0) return;
1694
+ await deleteSeparationForClassKeys(
1695
+ target,
1696
+ schema,
1697
+ graphId,
1698
+ unique.map((member) => identityClassKey(member))
1699
+ );
1700
+ const assertions = await loadAssertionsTouching(
1701
+ target,
1702
+ schema,
1703
+ graphId,
1704
+ unique,
1705
+ void 0,
1706
+ "different"
1707
+ );
1708
+ if (assertions.length === 0) return;
1709
+ const endpoints = assertions.flatMap((assertion) => [
1710
+ { kind: assertion.a_kind, id: assertion.a_id },
1711
+ { kind: assertion.b_kind, id: assertion.b_id }
1712
+ ]);
1713
+ const anchors = await loadCurrentClassAnchors(
1714
+ target,
1715
+ schema,
1716
+ graphId,
1717
+ endpoints
1718
+ );
1719
+ await insertSeparationRows(
1720
+ target,
1721
+ schema,
1722
+ graphId,
1723
+ buildSeparationProjection(
1724
+ assertions,
1725
+ (ref) => identityClassKey(requireDefined(anchors.get(refKey(ref))))
1726
+ )
1727
+ );
1728
+ }
1729
+ async function replaceSeparationForReferences(target, schema, graphId, references) {
1730
+ if (references.length === 0) return;
1731
+ const classes = await loadCurrentStructuralClasses(
1732
+ target,
1733
+ schema,
1734
+ graphId,
1735
+ references
1736
+ );
1737
+ await replaceSeparationForMembers(
1738
+ target,
1739
+ schema,
1740
+ graphId,
1741
+ [...classes.values()].flat()
1742
+ );
1743
+ }
1744
+ async function replaceClosure(target, schema, graphId, allowedKinds, sameIdAcrossKinds = "fold") {
1745
+ const snapshot = await loadSnapshot(
1746
+ target,
1747
+ schema,
1748
+ graphId,
1749
+ void 0,
1750
+ allowedKinds,
1751
+ sameIdAcrossKinds
1752
+ );
1753
+ await executeIdentityStatement(
1754
+ target,
1755
+ sql`DELETE FROM ${schema.identityClosureTable} WHERE graph_id = ${graphId}`
1756
+ );
1757
+ await insertClosureComponents(target, schema, graphId, snapshot.components);
1758
+ await deleteSeparationForGraph(target, schema, graphId);
1759
+ await insertSeparationRows(
1760
+ target,
1761
+ schema,
1762
+ graphId,
1763
+ buildSeparationProjection(
1764
+ snapshot.assertions,
1765
+ (ref) => snapshotClassKey(snapshot, ref)
1766
+ )
1767
+ );
1768
+ }
1769
+ async function insertClosureComponents(target, schema, graphId, components) {
1770
+ const emitted = /* @__PURE__ */ new Set();
1771
+ const values = [];
1772
+ for (const [memberKey, component] of components) {
1773
+ if (emitted.has(memberKey) || component.length < 2) continue;
1774
+ const canonical = requireDefined(component[0]);
1775
+ for (const member of component) {
1776
+ emitted.add(refKey(member));
1777
+ values.push(
1778
+ sql`(${graphId}, ${member.kind}, ${member.id}, ${canonical.kind}, ${canonical.id})`
1779
+ );
1780
+ }
1781
+ }
1782
+ if (values.length === 0) return;
1783
+ const chunkSize = identityChunkSize(target, {
1784
+ fixedParameters: 0,
1785
+ maxItems: MAX_CLOSURE_INSERT_CHUNK_SIZE,
1786
+ parametersPerItem: 5
1787
+ });
1788
+ for (const valueChunk of chunk(values, chunkSize)) {
1789
+ await executeIdentityStatement(
1790
+ target,
1791
+ sql`
1792
+ INSERT INTO ${schema.identityClosureTable} (
1793
+ graph_id, member_kind, member_id, class_kind, class_id
1794
+ ) VALUES ${sql.join(valueChunk, sql`, `)}
1795
+ `
1796
+ );
1797
+ }
1798
+ }
1799
+ async function replaceAffectedClosure(target, schema, graphId, references, sameIdAcrossKinds = "fold") {
1800
+ if (references.length === 0) return;
1801
+ const affectedByKey = /* @__PURE__ */ new Map();
1802
+ const classes = await loadCurrentStructuralClasses(
1803
+ target,
1804
+ schema,
1805
+ graphId,
1806
+ references
1807
+ );
1808
+ for (const ref of references) {
1809
+ for (const member of requireDefined(classes.get(refKey(ref)))) {
1810
+ affectedByKey.set(refKey(member), member);
1811
+ }
1812
+ }
1813
+ const affected = [...affectedByKey.values()];
1814
+ const structuralNodes = await loadLiveReferences(
1815
+ target,
1816
+ schema,
1817
+ graphId,
1818
+ affected
1819
+ );
1820
+ const assertions = await loadAssertionsTouching(
1821
+ target,
1822
+ schema,
1823
+ graphId,
1824
+ affected,
1825
+ void 0,
1826
+ "same"
1827
+ );
1828
+ const chunkSize = identityChunkSize(target, {
1829
+ fixedParameters: 1,
1830
+ maxItems: MAX_REFERENCE_CHUNK_SIZE,
1831
+ parametersPerItem: 2
1832
+ });
1833
+ for (const affectedChunk of chunk(affected, chunkSize)) {
1834
+ const matches = referenceCondition(
1835
+ sql`member_kind`,
1836
+ sql`member_id`,
1837
+ affectedChunk
1838
+ );
1839
+ await executeIdentityStatement(
1840
+ target,
1841
+ sql`
1842
+ DELETE FROM ${schema.identityClosureTable}
1843
+ WHERE graph_id = ${graphId} AND ${matches}
1844
+ `
1845
+ );
1846
+ }
1847
+ const components = buildComponents(
1848
+ structuralNodes,
1849
+ assertions,
1850
+ sameIdAcrossKinds
1851
+ );
1852
+ await insertClosureComponents(target, schema, graphId, components);
1853
+ const separationMembers = [...affected, ...[...components.values()].flat()];
1854
+ await replaceSeparationForMembers(target, schema, graphId, separationMembers);
1855
+ }
1856
+ async function mergeCurrentClasses(target, schema, graphId, a, b) {
1857
+ const classes = await loadCurrentStructuralClasses(target, schema, graphId, [
1858
+ a,
1859
+ b
1860
+ ]);
1861
+ const aClass = requireDefined(classes.get(refKey(a)));
1862
+ const bClass = requireDefined(classes.get(refKey(b)));
1863
+ if (containsRef(aClass, b)) return;
1864
+ const fusedMembers = [...aClass, ...bClass];
1865
+ const [smaller, larger] = aClass.length <= bClass.length ? [aClass, bClass] : [bClass, aClass];
1866
+ const canonical = requireDefined(
1867
+ [...aClass, ...bClass].toSorted(
1868
+ (left, right) => compareReferences(left, right)
1869
+ )[0]
1870
+ );
1871
+ async function relabelExistingClass(members) {
1872
+ if (members.length < 2) return;
1873
+ const previousCanonical = requireDefined(members[0]);
1874
+ if (refKey(previousCanonical) === refKey(canonical)) return;
1875
+ await executeIdentityStatement(
1876
+ target,
1877
+ sql`
1878
+ UPDATE ${schema.identityClosureTable}
1879
+ SET class_kind = ${canonical.kind}, class_id = ${canonical.id}
1880
+ WHERE graph_id = ${graphId}
1881
+ AND class_kind = ${previousCanonical.kind}
1882
+ AND class_id = ${previousCanonical.id}
1883
+ `
1884
+ );
1885
+ }
1886
+ await relabelExistingClass(smaller);
1887
+ await relabelExistingClass(larger);
1888
+ const singletonMembers = [smaller, larger].flatMap(
1889
+ (members) => members.length === 1 ? members : []
1890
+ );
1891
+ if (singletonMembers.length > 0) {
1892
+ const values = singletonMembers.map(
1893
+ (member) => sql`(${graphId}, ${member.kind}, ${member.id}, ${canonical.kind}, ${canonical.id})`
1894
+ );
1895
+ await executeIdentityStatement(
1896
+ target,
1897
+ sql`
1898
+ INSERT INTO ${schema.identityClosureTable} (
1899
+ graph_id, member_kind, member_id, class_kind, class_id
1900
+ ) VALUES ${sql.join(values, sql`, `)}
1901
+ `
1902
+ );
1903
+ }
1904
+ await replaceSeparationForMembers(target, schema, graphId, fusedMembers);
1905
+ }
1906
+ async function assertPair(ctx, target, relation, firstInput, secondInput, touch) {
1907
+ const first = registeredPlainRef(ctx, firstInput);
1908
+ const second = registeredPlainRef(ctx, secondInput);
1909
+ if (refKey(first) === refKey(second)) throw selfAssertionError(relation);
1910
+ const [a, b] = normalizePair(first, second);
1911
+ await Promise.all([
1912
+ requireLiveEndpoint(target, ctx.graphId, a),
1913
+ requireLiveEndpoint(target, ctx.graphId, b)
1914
+ ]);
1915
+ const existing = await currentAssertionForPair(
1916
+ target,
1917
+ ctx.schema,
1918
+ ctx.graphId,
1919
+ relation,
1920
+ a,
1921
+ b
1922
+ );
1923
+ if (existing !== void 0) {
1924
+ return assertionResult(publicAssertion(existing), "existing");
1925
+ }
1926
+ await validateCurrentRelation(
1927
+ ctx,
1928
+ target,
1929
+ relation,
1930
+ relation === "same" ? "assertSame" : "assertDifferent",
1931
+ a,
1932
+ b
1933
+ );
1934
+ const row = await insertAssertion(
1935
+ target,
1936
+ ctx.schema,
1937
+ ctx.graphId,
1938
+ relation,
1939
+ a,
1940
+ b,
1941
+ nowIso(),
1942
+ touch
1943
+ );
1944
+ if (relation === "same") {
1945
+ await mergeCurrentClasses(target, ctx.schema, ctx.graphId, a, b);
1946
+ } else {
1947
+ await replaceSeparationForReferences(target, ctx.schema, ctx.graphId, [
1948
+ a,
1949
+ b
1950
+ ]);
1951
+ }
1952
+ return assertionResult(publicAssertion(row), "created");
1953
+ }
1954
+
1955
+ // src/identity/assertion-key.ts
1956
+ function identityAssertionSemanticKey(relation, a, b) {
1957
+ return encodeTupleKey([relation, a.kind, a.id, b.kind, b.id]);
1958
+ }
1959
+
1960
+ // src/identity/service-facade.ts
1961
+ function assertionSemanticKey(relation, a, b) {
1962
+ return identityAssertionSemanticKey(relation, a, b);
1963
+ }
1964
+ async function bulkAssertPairs(ctx, target, relation, pairs, touch) {
1965
+ if (pairs.length === 0) return [];
1966
+ const normalizedPairs = pairs.map((pair) => {
1967
+ const first = registeredPlainRef(ctx, pair.a);
1968
+ const second = registeredPlainRef(ctx, pair.b);
1969
+ if (refKey(first) === refKey(second)) throw selfAssertionError(relation);
1970
+ return normalizePair(first, second);
1971
+ });
1972
+ const endpoints = normalizedPairs.flatMap(([a, b]) => [a, b]);
1973
+ await requireLiveEndpoints(target, ctx.schema, ctx.graphId, endpoints);
1974
+ const classes = await loadCurrentStructuralClasses(
1975
+ target,
1976
+ ctx.schema,
1977
+ ctx.graphId,
1978
+ endpoints
1979
+ );
1980
+ const structuralByKey = /* @__PURE__ */ new Map();
1981
+ for (const members of classes.values()) {
1982
+ for (const member of members) structuralByKey.set(refKey(member), member);
1983
+ }
1984
+ const structuralNodes = [...structuralByKey.values()];
1985
+ const persistedAssertions = await loadAssertionsTouching(
1986
+ target,
1987
+ ctx.schema,
1988
+ ctx.graphId,
1989
+ structuralNodes,
1990
+ void 0
1991
+ );
1992
+ const bySemanticKey = new Map(
1993
+ persistedAssertions.map((assertion) => [
1994
+ assertionSemanticKey(
1995
+ assertion.rel,
1996
+ { kind: assertion.a_kind, id: assertion.a_id },
1997
+ { kind: assertion.b_kind, id: assertion.b_id }
1998
+ ),
1999
+ assertion
2000
+ ])
2001
+ );
2002
+ const unionFind = new UnionFind();
2003
+ const allReferences = /* @__PURE__ */ new Map();
2004
+ const byId = /* @__PURE__ */ new Map();
2005
+ for (const ref of structuralNodes) {
2006
+ unionFind.add(ref);
2007
+ allReferences.set(refKey(ref), ref);
2008
+ const group = byId.get(ref.id) ?? [];
2009
+ group.push(ref);
2010
+ byId.set(ref.id, group);
2011
+ }
2012
+ if (ctx.sameIdAcrossKinds === "fold") {
2013
+ for (const group of byId.values()) {
2014
+ const first = group[0];
2015
+ if (first === void 0) continue;
2016
+ for (const member of group.slice(1)) unionFind.union(first, member);
2017
+ }
2018
+ }
2019
+ for (const assertion of persistedAssertions) {
2020
+ const endpointA = { kind: assertion.a_kind, id: assertion.a_id };
2021
+ const endpointB = { kind: assertion.b_kind, id: assertion.b_id };
2022
+ if (assertion.rel === "same") {
2023
+ unionFind.union(endpointA, endpointB);
2024
+ } else {
2025
+ unionFind.add(endpointA);
2026
+ unionFind.add(endpointB);
2027
+ }
2028
+ allReferences.set(refKey(endpointA), endpointA);
2029
+ allReferences.set(refKey(endpointB), endpointB);
2030
+ }
2031
+ const kindsByRoot = /* @__PURE__ */ new Map();
2032
+ for (const ref of allReferences.values()) {
2033
+ const root = unionFind.root(ref);
2034
+ const kinds = kindsByRoot.get(root) ?? /* @__PURE__ */ new Set();
2035
+ kinds.add(ref.kind);
2036
+ kindsByRoot.set(root, kinds);
2037
+ }
2038
+ const differentByRoot = /* @__PURE__ */ new Map();
2039
+ for (const assertion of persistedAssertions) {
2040
+ if (assertion.rel !== "different") continue;
2041
+ const rootA = unionFind.root({
2042
+ kind: assertion.a_kind,
2043
+ id: assertion.a_id
2044
+ });
2045
+ const rootB = unionFind.root({
2046
+ kind: assertion.b_kind,
2047
+ id: assertion.b_id
2048
+ });
2049
+ indexDifferentAssertion(differentByRoot, rootA, rootB, assertion);
2050
+ }
2051
+ const createdRows = [];
2052
+ const results = [];
2053
+ const closureReferences = [];
2054
+ const timestamp = nowIso();
2055
+ const operation = relation === "same" ? "assertSame" : "assertDifferent";
2056
+ for (const [a, b] of normalizedPairs) {
2057
+ const semanticKey = assertionSemanticKey(relation, a, b);
2058
+ const existing = bySemanticKey.get(semanticKey);
2059
+ if (existing !== void 0) {
2060
+ results.push(assertionResult(publicAssertion(existing), "existing"));
2061
+ continue;
2062
+ }
2063
+ const rootA = unionFind.root(a);
2064
+ const rootB = unionFind.root(b);
2065
+ if (relation === "different") {
2066
+ if (rootA === rootB) {
2067
+ throw new IdentityContradictionError({
2068
+ operation,
2069
+ a,
2070
+ b,
2071
+ reason: "same-class"
2072
+ });
2073
+ }
2074
+ } else {
2075
+ const spanning = differentByRoot.get(rootA)?.get(rootB);
2076
+ if (spanning !== void 0) {
2077
+ throw new IdentityContradictionError({
2078
+ operation,
2079
+ a,
2080
+ b,
2081
+ reason: "different-assertion",
2082
+ conflictingAssertionId: spanning.id
2083
+ });
2084
+ }
2085
+ const disjointKinds = kindSetsHaveDisjointKinds(
2086
+ ctx.registry,
2087
+ kindsByRoot.get(rootA) ?? /* @__PURE__ */ new Set([a.kind]),
2088
+ kindsByRoot.get(rootB) ?? /* @__PURE__ */ new Set([b.kind])
2089
+ );
2090
+ if (disjointKinds !== void 0) {
2091
+ throw new IdentityContradictionError({
2092
+ operation,
2093
+ a,
2094
+ b,
2095
+ reason: "disjoint-kinds",
2096
+ conflictingKinds: disjointKinds
2097
+ });
2098
+ }
2099
+ }
2100
+ const row = buildAssertionRow(ctx.graphId, relation, a, b, timestamp);
2101
+ createdRows.push(row);
2102
+ bySemanticKey.set(semanticKey, row);
2103
+ results.push(assertionResult(publicAssertion(row), "created"));
2104
+ if (relation === "same") {
2105
+ closureReferences.push(a, b);
2106
+ if (rootA !== rootB) {
2107
+ unionFind.union(a, b);
2108
+ const survivingRoot = unionFind.root(a);
2109
+ const retiredRoot = survivingRoot === rootA ? rootB : rootA;
2110
+ const survivingKinds = kindsByRoot.get(survivingRoot) ?? /* @__PURE__ */ new Set();
2111
+ const retiredKinds = kindsByRoot.get(retiredRoot);
2112
+ if (retiredKinds !== void 0) {
2113
+ for (const kind of retiredKinds) survivingKinds.add(kind);
2114
+ }
2115
+ kindsByRoot.delete(retiredRoot);
2116
+ kindsByRoot.set(survivingRoot, survivingKinds);
2117
+ mergeDifferentAssertionRoots(
2118
+ differentByRoot,
2119
+ survivingRoot,
2120
+ retiredRoot
2121
+ );
2122
+ }
2123
+ }
2124
+ }
2125
+ await insertAssertionRows(target, ctx.schema, createdRows);
2126
+ for (const row of createdRows) touch(ctx.graphId, row.id, row);
2127
+ if (closureReferences.length > 0) {
2128
+ await replaceAffectedClosure(
2129
+ target,
2130
+ ctx.schema,
2131
+ ctx.graphId,
2132
+ closureReferences,
2133
+ ctx.sameIdAcrossKinds
2134
+ );
2135
+ } else {
2136
+ await replaceSeparationForReferences(
2137
+ target,
2138
+ ctx.schema,
2139
+ ctx.graphId,
2140
+ createdRows.flatMap((row) => [
2141
+ { kind: row.a_kind, id: row.a_id },
2142
+ { kind: row.b_kind, id: row.b_id }
2143
+ ])
2144
+ );
2145
+ }
2146
+ return results;
2147
+ }
2148
+ async function findCurrentAssertionById(target, schema, graphId, id) {
2149
+ const rows = await target.execute(
2150
+ asCompiledRowsSql(sql`
2151
+ SELECT ${IDENTITY_ASSERTION_COLUMNS}
2152
+ FROM ${schema.identityAssertionsTable}
2153
+ WHERE graph_id = ${graphId}
2154
+ AND id = ${id}
2155
+ AND valid_to IS NULL
2156
+ AND deleted_at IS NULL
2157
+ LIMIT 1
2158
+ `)
2159
+ );
2160
+ return rows[0] === void 0 ? void 0 : normalizeIdentityAssertionRow(rows[0]);
2161
+ }
2162
+ async function retractById(ctx, target, id, touch) {
2163
+ const existing = await findCurrentAssertionById(
2164
+ target,
2165
+ ctx.schema,
2166
+ ctx.graphId,
2167
+ id
2168
+ );
2169
+ if (existing === void 0) return void 0;
2170
+ const now = nowIso();
2171
+ const validTo = clampValidTo(now, existing.valid_from);
2172
+ const ended = { ...existing, valid_to: validTo, updated_at: now };
2173
+ await executeIdentityStatement(
2174
+ target,
2175
+ sql`
2176
+ UPDATE ${ctx.schema.identityAssertionsTable}
2177
+ SET valid_to = ${validTo}, updated_at = ${now}
2178
+ WHERE graph_id = ${ctx.graphId}
2179
+ AND id = ${id}
2180
+ AND valid_to IS NULL
2181
+ `
2182
+ );
2183
+ touch(ctx.graphId, id, ended);
2184
+ return ended;
2185
+ }
2186
+ async function retractByIds(ctx, target, ids, touch) {
2187
+ const uniqueIds = [...new Set(ids)];
2188
+ if (uniqueIds.length === 0) return [];
2189
+ const current = [];
2190
+ const readChunkSize = identityChunkSize(target, {
2191
+ fixedParameters: 1,
2192
+ maxItems: MAX_REFERENCE_CHUNK_SIZE,
2193
+ parametersPerItem: 1
2194
+ });
2195
+ for (const idChunk of chunk(uniqueIds, readChunkSize)) {
2196
+ const placeholders = sql.join(
2197
+ idChunk.map((id) => sql`${id}`),
2198
+ sql`, `
2199
+ );
2200
+ const rows = await target.execute(
2201
+ asCompiledRowsSql(sql`
2202
+ SELECT ${IDENTITY_ASSERTION_COLUMNS}
2203
+ FROM ${ctx.schema.identityAssertionsTable}
2204
+ WHERE graph_id = ${ctx.graphId}
2205
+ AND id IN (${placeholders})
2206
+ AND valid_to IS NULL
2207
+ AND deleted_at IS NULL
2208
+ `)
2209
+ );
2210
+ current.push(...rows.map((row) => normalizeIdentityAssertionRow(row)));
2211
+ }
2212
+ if (current.length === 0) return [];
2213
+ const now = nowIso();
2214
+ const byValidTo = /* @__PURE__ */ new Map();
2215
+ const endedById = /* @__PURE__ */ new Map();
2216
+ for (const row of current) {
2217
+ const validTo = clampValidTo(now, row.valid_from);
2218
+ endedById.set(row.id, validTo);
2219
+ const group = byValidTo.get(validTo) ?? [];
2220
+ group.push(row.id);
2221
+ byValidTo.set(validTo, group);
2222
+ }
2223
+ const updateChunkSize = identityChunkSize(target, {
2224
+ fixedParameters: 3,
2225
+ maxItems: MAX_REFERENCE_CHUNK_SIZE,
2226
+ parametersPerItem: 1
2227
+ });
2228
+ for (const [validTo, ids2] of byValidTo) {
2229
+ for (const idChunk of chunk(ids2, updateChunkSize)) {
2230
+ const placeholders = sql.join(
2231
+ idChunk.map((id) => sql`${id}`),
2232
+ sql`, `
2233
+ );
2234
+ await executeIdentityStatement(
2235
+ target,
2236
+ sql`
2237
+ UPDATE ${ctx.schema.identityAssertionsTable}
2238
+ SET valid_to = ${validTo}, updated_at = ${now}
2239
+ WHERE graph_id = ${ctx.graphId}
2240
+ AND id IN (${placeholders})
2241
+ AND valid_to IS NULL
2242
+ `
2243
+ );
2244
+ }
2245
+ }
2246
+ const currentById = new Map(current.map((row) => [row.id, row]));
2247
+ const ended = uniqueIds.flatMap((id) => {
2248
+ const row = currentById.get(id);
2249
+ if (row === void 0) return [];
2250
+ return [
2251
+ {
2252
+ ...row,
2253
+ valid_to: requireDefined(endedById.get(row.id)),
2254
+ updated_at: now
2255
+ }
2256
+ ];
2257
+ });
2258
+ for (const row of ended) {
2259
+ touch(ctx.graphId, row.id, { ...row });
2260
+ }
2261
+ return ended;
2262
+ }
2263
+ async function runIdentityMutation(ctx, fn) {
2264
+ let touched = false;
2265
+ return runInWriteTransaction(
2266
+ {
2267
+ graphId: ctx.graphId,
2268
+ schemaVersion: ctx.schemaVersion,
2269
+ historyEnabled: ctx.historyEnabled,
2270
+ revisionTrackingEnabled: ctx.revisionTrackingEnabled,
2271
+ revisionSchema: ctx.schema
2272
+ },
2273
+ ctx.backend,
2274
+ async (target) => {
2275
+ await lockIdentityGraph(target, ctx.graphId);
2276
+ return withRecordedIdentityMutationTarget(
2277
+ target,
2278
+ (rawTarget, touch) => fn(
2279
+ rawTarget,
2280
+ (graphId, id, afterImage) => {
2281
+ touched = true;
2282
+ touch(graphId, id, afterImage);
2283
+ },
2284
+ () => {
2285
+ touched = true;
2286
+ }
2287
+ )
2288
+ );
2289
+ },
2290
+ { didWrite: () => touched }
2291
+ );
2292
+ }
2293
+ function createIdentityReadFacade(ctx) {
2294
+ return {
2295
+ async representativeOf(input) {
2296
+ const members = await visibleMembersAtCoordinate(
2297
+ ctx,
2298
+ registeredPlainRef(ctx, input)
2299
+ );
2300
+ return members[0] === void 0 ? void 0 : publicNodeRef(members[0]);
2301
+ },
2302
+ async membersOf(input) {
2303
+ const members = await visibleMembersAtCoordinate(
2304
+ ctx,
2305
+ registeredPlainRef(ctx, input)
2306
+ );
2307
+ return members.map((member) => publicNodeRef(member));
2308
+ },
2309
+ async nodesOf(input) {
2310
+ const members = await visibleMembersAtCoordinate(
2311
+ ctx,
2312
+ registeredPlainRef(ctx, input)
2313
+ );
2314
+ const nodes = await ctx.loadNodes(members, ctx.coordinate);
2315
+ return nodes.filter((node) => node !== void 0);
2316
+ },
2317
+ async areSame(firstInput, secondInput) {
2318
+ const first = registeredPlainRef(ctx, firstInput);
2319
+ const second = registeredPlainRef(ctx, secondInput);
2320
+ const members = await visibleMembersAtCoordinate(ctx, first);
2321
+ return containsRef(members, second);
2322
+ },
2323
+ async areDifferent(firstInput, secondInput) {
2324
+ const first = registeredPlainRef(ctx, firstInput);
2325
+ const second = registeredPlainRef(ctx, secondInput);
2326
+ const { coordinate } = ctx;
2327
+ if (coordinate === void 0 || isCurrentClosureCoordinate(coordinate)) {
2328
+ const [firstVisible, secondVisible] = await Promise.all([
2329
+ loadCurrentVisibleMembers(
2330
+ ctx.backend,
2331
+ ctx.schema,
2332
+ ctx.graphId,
2333
+ first
2334
+ ),
2335
+ loadCurrentVisibleMembers(
2336
+ ctx.backend,
2337
+ ctx.schema,
2338
+ ctx.graphId,
2339
+ second
2340
+ )
2341
+ ]);
2342
+ if (firstVisible.length === 0 || secondVisible.length === 0)
2343
+ return false;
2344
+ const classes2 = await loadCurrentStructuralClasses(
2345
+ ctx.backend,
2346
+ ctx.schema,
2347
+ ctx.graphId,
2348
+ [first, second]
2349
+ );
2350
+ const firstClass2 = requireDefined(classes2.get(refKey(first)));
2351
+ const secondClass2 = requireDefined(classes2.get(refKey(second)));
2352
+ const separated = await isSeparated(
2353
+ ctx.backend,
2354
+ ctx.schema,
2355
+ ctx.graphId,
2356
+ currentClassKey(firstClass2),
2357
+ currentClassKey(secondClass2),
2358
+ ctx.registry
2359
+ );
2360
+ return separated || classHasDisjointKinds(ctx.registry, firstClass2, secondClass2) !== void 0;
2361
+ }
2362
+ const classes = await loadHistoricalClasses(
2363
+ ctx.backend,
2364
+ ctx.schema,
2365
+ ctx.graphId,
2366
+ [first, second],
2367
+ coordinate,
2368
+ ctx.sameIdAcrossKinds
2369
+ );
2370
+ const firstClass = requireDefined(classes.get(refKey(first)));
2371
+ const secondClass = requireDefined(classes.get(refKey(second)));
2372
+ if (firstClass.visible.length === 0 || secondClass.visible.length === 0)
2373
+ return false;
2374
+ const different = await loadSpanningDifferentAssertion(
2375
+ ctx.backend,
2376
+ ctx.schema,
2377
+ ctx.graphId,
2378
+ firstClass.structural,
2379
+ secondClass.structural,
2380
+ ctx.coordinate
2381
+ );
2382
+ return different !== void 0 || classHasDisjointKinds(
2383
+ ctx.registry,
2384
+ firstClass.structural,
2385
+ secondClass.structural
2386
+ ) !== void 0;
2387
+ },
2388
+ async assertionsOf(input) {
2389
+ const ref = registeredPlainRef(ctx, input);
2390
+ const members = await visibleMembersAtCoordinate(ctx, ref);
2391
+ if (members.length === 0) return [];
2392
+ const assertions = await loadAssertionsTouching(
2393
+ ctx.backend,
2394
+ ctx.schema,
2395
+ ctx.graphId,
2396
+ [ref],
2397
+ ctx.coordinate
2398
+ );
2399
+ return assertions.filter(
2400
+ (assertion) => assertion.a_kind === ref.kind && assertion.a_id === ref.id || assertion.b_kind === ref.kind && assertion.b_id === ref.id
2401
+ ).toSorted((left, right) => compareCodePoints(left.id, right.id)).map((assertion) => publicAssertion(assertion));
2402
+ }
2403
+ };
2404
+ }
2405
+ function partitionRetractedEndpoints(retracted) {
2406
+ const closureReferences = [];
2407
+ const separationReferences = [];
2408
+ for (const ended of retracted) {
2409
+ const endpoints = [
2410
+ { kind: ended.a_kind, id: ended.a_id },
2411
+ { kind: ended.b_kind, id: ended.b_id }
2412
+ ];
2413
+ if (ended.rel === "same") {
2414
+ closureReferences.push(...endpoints);
2415
+ } else {
2416
+ separationReferences.push(...endpoints);
2417
+ }
2418
+ }
2419
+ return { closureReferences, separationReferences };
2420
+ }
2421
+ function createIdentityFacade(ctx) {
2422
+ return {
2423
+ ...createIdentityReadFacade(ctx),
2424
+ assertSame(a, b) {
2425
+ return runIdentityMutation(
2426
+ ctx,
2427
+ (target, touch) => assertPair(ctx, target, "same", a, b, touch)
2428
+ );
2429
+ },
2430
+ assertDifferent(a, b) {
2431
+ return runIdentityMutation(
2432
+ ctx,
2433
+ (target, touch) => assertPair(ctx, target, "different", a, b, touch)
2434
+ );
2435
+ },
2436
+ bulkAssertSame(pairs) {
2437
+ return runIdentityMutation(
2438
+ ctx,
2439
+ (target, touch) => bulkAssertPairs(ctx, target, "same", pairs, touch)
2440
+ );
2441
+ },
2442
+ bulkAssertDifferent(pairs) {
2443
+ return runIdentityMutation(
2444
+ ctx,
2445
+ (target, touch) => bulkAssertPairs(ctx, target, "different", pairs, touch)
2446
+ );
2447
+ },
2448
+ retractAssertion(id) {
2449
+ return runIdentityMutation(ctx, async (target, touch) => {
2450
+ const ended = await retractById(ctx, target, id, touch);
2451
+ if (ended !== void 0) {
2452
+ const endpoints = [
2453
+ { kind: ended.a_kind, id: ended.a_id },
2454
+ { kind: ended.b_kind, id: ended.b_id }
2455
+ ];
2456
+ if (ended.rel === "same") {
2457
+ await replaceAffectedClosure(
2458
+ target,
2459
+ ctx.schema,
2460
+ ctx.graphId,
2461
+ endpoints,
2462
+ ctx.sameIdAcrossKinds
2463
+ );
2464
+ } else {
2465
+ await replaceSeparationForReferences(
2466
+ target,
2467
+ ctx.schema,
2468
+ ctx.graphId,
2469
+ endpoints
2470
+ );
2471
+ }
2472
+ }
2473
+ return ended === void 0 ? void 0 : publicAssertion(ended);
2474
+ });
2475
+ },
2476
+ retractSameAssertion(firstInput, secondInput) {
2477
+ return runIdentityMutation(ctx, async (target, touch) => {
2478
+ const [a, b] = normalizePair(
2479
+ registeredPlainRef(ctx, firstInput),
2480
+ registeredPlainRef(ctx, secondInput)
2481
+ );
2482
+ const existing = await currentAssertionForPair(
2483
+ target,
2484
+ ctx.schema,
2485
+ ctx.graphId,
2486
+ "same",
2487
+ a,
2488
+ b
2489
+ );
2490
+ if (existing === void 0) return;
2491
+ const ended = await retractById(ctx, target, existing.id, touch);
2492
+ await replaceAffectedClosure(
2493
+ target,
2494
+ ctx.schema,
2495
+ ctx.graphId,
2496
+ [a, b],
2497
+ ctx.sameIdAcrossKinds
2498
+ );
2499
+ return ended === void 0 ? void 0 : publicAssertion(ended);
2500
+ });
2501
+ },
2502
+ retractDifferentAssertion(firstInput, secondInput) {
2503
+ return runIdentityMutation(ctx, async (target, touch) => {
2504
+ const [a, b] = normalizePair(
2505
+ registeredPlainRef(ctx, firstInput),
2506
+ registeredPlainRef(ctx, secondInput)
2507
+ );
2508
+ const existing = await currentAssertionForPair(
2509
+ target,
2510
+ ctx.schema,
2511
+ ctx.graphId,
2512
+ "different",
2513
+ a,
2514
+ b
2515
+ );
2516
+ if (existing === void 0) return;
2517
+ const ended = await retractById(ctx, target, existing.id, touch);
2518
+ await replaceSeparationForReferences(target, ctx.schema, ctx.graphId, [
2519
+ a,
2520
+ b
2521
+ ]);
2522
+ return ended === void 0 ? void 0 : publicAssertion(ended);
2523
+ });
2524
+ },
2525
+ bulkRetractAssertions(ids) {
2526
+ return runIdentityMutation(ctx, async (target, touch) => {
2527
+ const retracted = await retractByIds(ctx, target, ids, touch);
2528
+ const { closureReferences, separationReferences } = partitionRetractedEndpoints(retracted);
2529
+ if (closureReferences.length > 0) {
2530
+ await replaceAffectedClosure(
2531
+ target,
2532
+ ctx.schema,
2533
+ ctx.graphId,
2534
+ closureReferences,
2535
+ ctx.sameIdAcrossKinds
2536
+ );
2537
+ }
2538
+ await replaceSeparationForReferences(
2539
+ target,
2540
+ ctx.schema,
2541
+ ctx.graphId,
2542
+ separationReferences
2543
+ );
2544
+ return retracted.map((assertion) => publicAssertion(assertion));
2545
+ });
2546
+ }
2547
+ };
2548
+ }
2549
+
2550
+ // src/identity/service-interchange-write.ts
2551
+ function transferShapeError(assertion, message, issue) {
2552
+ return new ValidationError(message, {
2553
+ issues: [
2554
+ {
2555
+ path: "identity.assertions",
2556
+ assertionId: assertion.id,
2557
+ message: issue.message,
2558
+ code: issue.code
2559
+ }
2560
+ ]
2561
+ });
2562
+ }
2563
+ function validateTransferShape(ctx, assertion, mode) {
2564
+ if (!ctx.registry.nodeKinds.has(assertion.a.kind) || !ctx.registry.nodeKinds.has(assertion.b.kind)) {
2565
+ throw transferShapeError(
2566
+ assertion,
2567
+ "Identity import references an unknown node kind.",
2568
+ {
2569
+ message: `Unknown identity endpoint kind in assertion ${assertion.id}`,
2570
+ code: "IDENTITY_IMPORT_UNKNOWN_KIND"
2571
+ }
2572
+ );
2573
+ }
2574
+ if (refKey(assertion.a) === refKey(assertion.b)) {
2575
+ throw transferShapeError(
2576
+ assertion,
2577
+ `Identity ${assertion.relation} assertions require two distinct node references.`,
2578
+ {
2579
+ message: `Assertion ${assertion.id} relates a node to itself`,
2580
+ code: "IDENTITY_SELF_ASSERTION"
2581
+ }
2582
+ );
2583
+ }
2584
+ const normalized = normalizePair(assertion.a, assertion.b);
2585
+ if (refKey(normalized[0]) !== refKey(assertion.a) || refKey(normalized[1]) !== refKey(assertion.b)) {
2586
+ throw transferShapeError(
2587
+ assertion,
2588
+ "Identity import pairs must be normalized.",
2589
+ {
2590
+ message: `Assertion ${assertion.id} endpoints are not in code-point order`,
2591
+ code: "IDENTITY_IMPORT_PAIR_NOT_NORMALIZED"
2592
+ }
2593
+ );
2594
+ }
2595
+ if (mode === "state" && assertion.validTo !== void 0) {
2596
+ throw transferShapeError(
2597
+ assertion,
2598
+ "State identity import cannot contain ended assertions.",
2599
+ {
2600
+ message: `Assertion ${assertion.id} is ended`,
2601
+ code: "IDENTITY_STATE_IMPORT_ENDED_ASSERTION"
2602
+ }
2603
+ );
2604
+ }
2605
+ const now = nowIso();
2606
+ if (assertion.validTo === void 0 && compareCodePoints(assertion.validFrom, now) > 0) {
2607
+ throw transferShapeError(
2608
+ assertion,
2609
+ "Identity import cannot contain future-dated open assertions.",
2610
+ {
2611
+ message: `Assertion ${assertion.id} validFrom is in the future`,
2612
+ code: "IDENTITY_IMPORT_FUTURE_VALID_FROM"
2613
+ }
2614
+ );
2615
+ }
2616
+ if (assertion.validTo !== void 0 && compareCodePoints(assertion.validTo, assertion.validFrom) < 0) {
2617
+ throw transferShapeError(
2618
+ assertion,
2619
+ "Identity assertion validity window is empty.",
2620
+ {
2621
+ message: `Assertion ${assertion.id} validTo must not precede validFrom`,
2622
+ code: "IDENTITY_IMPORT_INVALID_WINDOW"
2623
+ }
2624
+ );
2625
+ }
2626
+ if (assertion.endedBy !== void 0) {
2627
+ if (assertion.validTo === void 0) {
2628
+ throw transferShapeError(
2629
+ assertion,
2630
+ "Identity import cannot name an ending cause on an open assertion.",
2631
+ {
2632
+ message: `Assertion ${assertion.id} carries endedBy without validTo`,
2633
+ code: "IDENTITY_IMPORT_ENDED_BY_WITHOUT_END"
2634
+ }
2635
+ );
2636
+ }
2637
+ const endedByKey = refKey(assertion.endedBy);
2638
+ if (endedByKey !== refKey(assertion.a) && endedByKey !== refKey(assertion.b)) {
2639
+ throw transferShapeError(
2640
+ assertion,
2641
+ "Identity import ending cause must name one of the assertion's endpoints.",
2642
+ {
2643
+ message: `Assertion ${assertion.id} endedBy is not an endpoint of the assertion`,
2644
+ code: "IDENTITY_IMPORT_ENDED_BY_NOT_ENDPOINT"
2645
+ }
2646
+ );
2647
+ }
2648
+ }
2649
+ if (assertion.validTo !== void 0 && compareCodePoints(assertion.validTo, now) > 0) {
2650
+ throw transferShapeError(
2651
+ assertion,
2652
+ "Identity import cannot contain assertions that end in the future.",
2653
+ {
2654
+ message: `Assertion ${assertion.id} validTo is in the future`,
2655
+ code: "IDENTITY_IMPORT_FUTURE_VALID_TO"
2656
+ }
2657
+ );
2658
+ }
2659
+ return normalized;
2660
+ }
2661
+ var IDENTITY_IMPORT_FAILED_ASSERTION = /* @__PURE__ */ Symbol(
2662
+ "typegraph.identity.failedAssertionId"
2663
+ );
2664
+ var IDENTITY_IMPORT_PROGRESS = /* @__PURE__ */ Symbol(
2665
+ "typegraph.identity.importProgress"
2666
+ );
2667
+ function rethrowTaggedWithAssertion(error, assertionId, progress) {
2668
+ if (typeof error === "object" && error !== null) {
2669
+ Object.defineProperty(error, IDENTITY_IMPORT_FAILED_ASSERTION, {
2670
+ value: assertionId,
2671
+ enumerable: false,
2672
+ configurable: true
2673
+ });
2674
+ Object.defineProperty(error, IDENTITY_IMPORT_PROGRESS, {
2675
+ value: progress,
2676
+ enumerable: false,
2677
+ configurable: true
2678
+ });
2679
+ }
2680
+ throw error;
2681
+ }
2682
+ async function importIdentityAssertionsIntoTarget(ctx, target, assertions, mode) {
2683
+ let created = 0;
2684
+ let skipped = 0;
2685
+ await withRecordedIdentityMutationTarget(target, async (rawTarget, touch) => {
2686
+ const normalized = assertions.map((assertion) => ({
2687
+ assertion,
2688
+ endpoints: validateTransferShape(ctx, assertion, mode)
2689
+ }));
2690
+ const existingById = await loadAssertionsByIds(
2691
+ rawTarget,
2692
+ ctx.schema,
2693
+ ctx.graphId,
2694
+ assertions.map((assertion) => assertion.id)
2695
+ );
2696
+ const currentEndpoints = [];
2697
+ const endedEndpoints = [];
2698
+ for (const { assertion, endpoints } of normalized) {
2699
+ const [a, b] = endpoints;
2700
+ if (assertion.validTo === void 0) {
2701
+ currentEndpoints.push(a, b);
2702
+ } else {
2703
+ endedEndpoints.push(a, b);
2704
+ }
2705
+ }
2706
+ const attributeMissingEndpoint = (error, ended) => {
2707
+ if (error instanceof NodeNotFoundError) {
2708
+ const missing = { kind: error.details.kind, id: error.details.id };
2709
+ const failing = normalized.find(
2710
+ ({ assertion, endpoints }) => assertion.validTo !== void 0 === ended && endpoints.some(
2711
+ (endpoint) => endpoint.kind === missing.kind && endpoint.id === missing.id
2712
+ )
2713
+ );
2714
+ if (failing !== void 0) {
2715
+ rethrowTaggedWithAssertion(error, failing.assertion.id, {
2716
+ created,
2717
+ skipped
2718
+ });
2719
+ }
2720
+ }
2721
+ throw error;
2722
+ };
2723
+ try {
2724
+ await requireLiveEndpoints(
2725
+ rawTarget,
2726
+ ctx.schema,
2727
+ ctx.graphId,
2728
+ currentEndpoints
2729
+ );
2730
+ } catch (error) {
2731
+ attributeMissingEndpoint(error, false);
2732
+ }
2733
+ try {
2734
+ await requireStructuralEndpoints(
2735
+ rawTarget,
2736
+ ctx.schema,
2737
+ ctx.graphId,
2738
+ endedEndpoints
2739
+ );
2740
+ } catch (error) {
2741
+ attributeMissingEndpoint(error, true);
2742
+ }
2743
+ for (const { assertion, endpoints } of normalized) {
2744
+ const [a, b] = endpoints;
2745
+ try {
2746
+ const sameId = existingById.get(assertion.id);
2747
+ if (sameId !== void 0) {
2748
+ const exact = sameId.rel === assertion.relation && sameId.a_kind === a.kind && sameId.a_id === a.id && sameId.b_kind === b.kind && sameId.b_id === b.id && sameId.valid_from === assertion.validFrom && sameId.valid_to === assertion.validTo && sameId.ended_by_kind === assertion.endedBy?.kind && sameId.ended_by_id === assertion.endedBy?.id;
2749
+ if (exact) {
2750
+ skipped += 1;
2751
+ continue;
2752
+ }
2753
+ throw new ConfigurationError(
2754
+ `Identity assertion id ${assertion.id} already identifies different truth.`,
2755
+ {
2756
+ code: "IDENTITY_IMPORT_ID_CONFLICT",
2757
+ graphId: ctx.graphId,
2758
+ assertionId: assertion.id
2759
+ }
2760
+ );
2761
+ }
2762
+ if (assertion.validTo !== void 0) {
2763
+ const timestamp = assertion.validFrom;
2764
+ const row = {
2765
+ graph_id: ctx.graphId,
2766
+ id: assertion.id,
2767
+ rel: assertion.relation,
2768
+ a_kind: a.kind,
2769
+ a_id: a.id,
2770
+ b_kind: b.kind,
2771
+ b_id: b.id,
2772
+ valid_from: assertion.validFrom,
2773
+ valid_to: assertion.validTo,
2774
+ created_at: timestamp,
2775
+ updated_at: assertion.validTo,
2776
+ deleted_at: void 0,
2777
+ ended_by_kind: assertion.endedBy?.kind,
2778
+ ended_by_id: assertion.endedBy?.id
2779
+ };
2780
+ await insertAssertionRows(rawTarget, ctx.schema, [row]);
2781
+ touch(ctx.graphId, row.id, row);
2782
+ existingById.set(row.id, row);
2783
+ created += 1;
2784
+ continue;
2785
+ }
2786
+ const existing = await currentAssertionForPair(
2787
+ rawTarget,
2788
+ ctx.schema,
2789
+ ctx.graphId,
2790
+ assertion.relation,
2791
+ a,
2792
+ b
2793
+ );
2794
+ if (existing !== void 0) {
2795
+ skipped += 1;
2796
+ continue;
2797
+ }
2798
+ await validateCurrentRelation(
2799
+ ctx,
2800
+ rawTarget,
2801
+ assertion.relation,
2802
+ "import",
2803
+ a,
2804
+ b
2805
+ );
2806
+ const inserted = await insertAssertion(
2807
+ rawTarget,
2808
+ ctx.schema,
2809
+ ctx.graphId,
2810
+ assertion.relation,
2811
+ a,
2812
+ b,
2813
+ nowIso(),
2814
+ touch,
2815
+ { id: assertion.id, validFrom: assertion.validFrom }
2816
+ );
2817
+ existingById.set(inserted.id, inserted);
2818
+ created += 1;
2819
+ if (assertion.relation === "same") {
2820
+ await mergeCurrentClasses(rawTarget, ctx.schema, ctx.graphId, a, b);
2821
+ } else {
2822
+ await replaceSeparationForReferences(
2823
+ rawTarget,
2824
+ ctx.schema,
2825
+ ctx.graphId,
2826
+ [a, b]
2827
+ );
2828
+ }
2829
+ } catch (error) {
2830
+ rethrowTaggedWithAssertion(error, assertion.id, { created, skipped });
2831
+ }
2832
+ }
2833
+ });
2834
+ return { created, skipped };
2835
+ }
2836
+ async function applyIdentityChangesForContext(ctx, retractionIds, assertions) {
2837
+ if (retractionIds.length === 0 && assertions.length === 0) {
2838
+ return { created: 0, retracted: 0 };
2839
+ }
2840
+ return runIdentityMutation(ctx, async (target, touch, markWritten) => {
2841
+ const retracted = await retractByIds(ctx, target, retractionIds, touch);
2842
+ const { closureReferences, separationReferences } = partitionRetractedEndpoints(retracted);
2843
+ if (closureReferences.length > 0) {
2844
+ await replaceAffectedClosure(
2845
+ target,
2846
+ ctx.schema,
2847
+ ctx.graphId,
2848
+ closureReferences,
2849
+ ctx.sameIdAcrossKinds
2850
+ );
2851
+ }
2852
+ await replaceSeparationForReferences(
2853
+ target,
2854
+ ctx.schema,
2855
+ ctx.graphId,
2856
+ separationReferences
2857
+ );
2858
+ const summary = await importIdentityAssertionsIntoTarget(
2859
+ ctx,
2860
+ target,
2861
+ assertions,
2862
+ "state"
2863
+ );
2864
+ if (summary.created > 0) markWritten();
2865
+ return { created: summary.created, retracted: retracted.length };
2866
+ });
2867
+ }
2868
+
2869
+ // src/identity/service-maintenance.ts
2870
+ function requireAtomicIdentityBackend(backend, graphId) {
2871
+ if (!backend.capabilities.transactions) {
2872
+ throw new ConfigurationError(
2873
+ "Operational Identity requires atomic transaction support.",
2874
+ { code: "IDENTITY_REQUIRES_ATOMIC_BACKEND", graphId }
2875
+ );
2876
+ }
2877
+ }
2878
+ async function runOnTransactionIfSupported(backend, fn) {
2879
+ if ("transaction" in backend) {
2880
+ await backend.transaction(async (target) => fn(target));
2881
+ return;
2882
+ }
2883
+ await fn(backend);
2884
+ }
2885
+ async function rebuildIdentityClosureForContext(ctx) {
2886
+ requireAtomicIdentityBackend(ctx.backend, ctx.graphId);
2887
+ async function rebuildAtTarget(target) {
2888
+ await lockIdentityGraph(target, ctx.graphId);
2889
+ await withRecordedIdentityMutationTarget(target, async (rawTarget) => {
2890
+ const activeKinds = identityActiveKinds(ctx.registry);
2891
+ const snapshot = await loadSnapshot(
2892
+ rawTarget,
2893
+ ctx.schema,
2894
+ ctx.graphId,
2895
+ void 0,
2896
+ activeKinds,
2897
+ ctx.sameIdAcrossKinds
2898
+ );
2899
+ validateSnapshotIntegrity(snapshot, ctx.registry, ctx.graphId);
2900
+ await replaceClosure(
2901
+ rawTarget,
2902
+ ctx.schema,
2903
+ ctx.graphId,
2904
+ activeKinds,
2905
+ ctx.sameIdAcrossKinds
2906
+ );
2907
+ });
2908
+ }
2909
+ await runOnTransactionIfSupported(
2910
+ ctx.backend,
2911
+ async (target) => rebuildAtTarget(target)
2912
+ );
2913
+ }
2914
+ async function validateIdentityForContext(ctx) {
2915
+ requireAtomicIdentityBackend(ctx.backend, ctx.graphId);
2916
+ async function validateAtTarget(target) {
2917
+ await lockIdentityGraph(target, ctx.graphId);
2918
+ const activeKinds = identityActiveKinds(ctx.registry);
2919
+ const snapshot = await loadSnapshot(
2920
+ target,
2921
+ ctx.schema,
2922
+ ctx.graphId,
2923
+ void 0,
2924
+ activeKinds,
2925
+ ctx.sameIdAcrossKinds
2926
+ );
2927
+ validateSnapshotIntegrity(snapshot, ctx.registry, ctx.graphId);
2928
+ await assertClosureMatchesComponents(
2929
+ target,
2930
+ ctx.schema,
2931
+ ctx.graphId,
2932
+ snapshot.components
2933
+ );
2934
+ assertSeparationMatchesProjection(
2935
+ ctx.graphId,
2936
+ await readSeparationForGraph(target, ctx.schema, ctx.graphId),
2937
+ buildSeparationProjection(
2938
+ snapshot.assertions,
2939
+ (ref) => snapshotClassKey(snapshot, ref)
2940
+ )
2941
+ );
2942
+ }
2943
+ await runOnTransactionIfSupported(
2944
+ ctx.backend,
2945
+ async (target) => validateAtTarget(target)
2946
+ );
2947
+ }
2948
+ async function loadAffectedIdentityClasses(ctx, target, seeds) {
2949
+ const classes = await loadCurrentStructuralClasses(
2950
+ target,
2951
+ ctx.schema,
2952
+ ctx.graphId,
2953
+ seeds
2954
+ );
2955
+ const byClassKey = /* @__PURE__ */ new Map();
2956
+ const classKeyByMember = /* @__PURE__ */ new Map();
2957
+ const members = /* @__PURE__ */ new Map();
2958
+ for (const classMembers of classes.values()) {
2959
+ const classKey = refKey(requireDefined(classMembers[0]));
2960
+ byClassKey.set(classKey, classMembers);
2961
+ for (const member of classMembers) {
2962
+ const memberKey = refKey(member);
2963
+ classKeyByMember.set(memberKey, classKey);
2964
+ members.set(memberKey, member);
2965
+ }
2966
+ }
2967
+ return { byClassKey, classKeyByMember, members: [...members.values()] };
2968
+ }
2969
+ function firstMemberOfKind(members, kind) {
2970
+ return requireDefined(
2971
+ members.find((member) => member.kind === kind),
2972
+ `Identity class carries no ${kind} member`
2973
+ );
2974
+ }
2975
+ async function findAffectedClassInconsistency(ctx, target, seeds) {
2976
+ const classes = await loadAffectedIdentityClasses(ctx, target, seeds);
2977
+ for (const members of classes.byClassKey.values()) {
2978
+ const conflictingKinds = classHasDisjointKinds(
2979
+ ctx.registry,
2980
+ members,
2981
+ members
2982
+ );
2983
+ if (conflictingKinds === void 0) continue;
2984
+ const [leftKind, rightKind] = conflictingKinds;
2985
+ return {
2986
+ kind: "contradiction",
2987
+ error: new IdentityContradictionError({
2988
+ operation: "merge",
2989
+ a: firstMemberOfKind(members, leftKind),
2990
+ b: firstMemberOfKind(members, rightKind),
2991
+ reason: "disjoint-kinds",
2992
+ conflictingKinds
2993
+ })
2994
+ };
2995
+ }
2996
+ const assertions = await loadAssertionsTouching(
2997
+ target,
2998
+ ctx.schema,
2999
+ ctx.graphId,
3000
+ classes.members,
3001
+ void 0
3002
+ );
3003
+ const unmerged = [];
3004
+ for (const assertion of assertions) {
3005
+ const a = { kind: assertion.a_kind, id: assertion.a_id };
3006
+ const b = { kind: assertion.b_kind, id: assertion.b_id };
3007
+ const aClassKey = classes.classKeyByMember.get(refKey(a));
3008
+ const bClassKey = classes.classKeyByMember.get(refKey(b));
3009
+ if (assertion.rel === "different") {
3010
+ if (aClassKey === void 0 || aClassKey !== bClassKey) continue;
3011
+ return {
3012
+ kind: "contradiction",
3013
+ error: new IdentityContradictionError({
3014
+ operation: "merge",
3015
+ a,
3016
+ b,
3017
+ reason: "same-class",
3018
+ conflictingAssertionId: assertion.id
3019
+ })
3020
+ };
3021
+ }
3022
+ if (aClassKey !== void 0 && bClassKey !== void 0) {
3023
+ if (aClassKey === bClassKey) continue;
3024
+ return {
3025
+ kind: "stale-closure",
3026
+ detail: { assertionId: assertion.id, a, b }
3027
+ };
3028
+ }
3029
+ unmerged.push({
3030
+ assertionId: assertion.id,
3031
+ a,
3032
+ b,
3033
+ outside: aClassKey === void 0 ? a : b
3034
+ });
3035
+ }
3036
+ if (unmerged.length > 0) {
3037
+ const liveOutside = await loadLiveReferences(
3038
+ target,
3039
+ ctx.schema,
3040
+ ctx.graphId,
3041
+ unmerged.map((row) => row.outside)
3042
+ );
3043
+ const liveKeys = new Set(liveOutside.map((ref) => refKey(ref)));
3044
+ const lagging = unmerged.find((row) => liveKeys.has(refKey(row.outside)));
3045
+ if (lagging !== void 0) {
3046
+ return {
3047
+ kind: "stale-closure",
3048
+ detail: {
3049
+ assertionId: lagging.assertionId,
3050
+ a: lagging.a,
3051
+ b: lagging.b
3052
+ }
3053
+ };
3054
+ }
3055
+ }
3056
+ if (ctx.sameIdAcrossKinds !== "fold") return void 0;
3057
+ const liveKindsById = await liveNodeKindsSharingIds(
3058
+ ctx,
3059
+ target,
3060
+ classes.members.map((member) => member.id)
3061
+ );
3062
+ for (const [id, kinds] of liveKindsById) {
3063
+ const classKeys = /* @__PURE__ */ new Set();
3064
+ for (const kind of kinds) {
3065
+ classKeys.add(classes.classKeyByMember.get(refKey({ kind, id })));
3066
+ }
3067
+ if (classKeys.size > 1) {
3068
+ return {
3069
+ kind: "stale-closure",
3070
+ detail: { sharedId: id, kinds: [...kinds] }
3071
+ };
3072
+ }
3073
+ }
3074
+ return void 0;
3075
+ }
3076
+ async function assertAffectedIdentityClassesConsistent(ctx, target, seeds) {
3077
+ if (seeds.length === 0) return;
3078
+ await lockIdentityGraph(target, ctx.graphId);
3079
+ const observed = await findAffectedClassInconsistency(ctx, target, seeds);
3080
+ if (observed === void 0) return;
3081
+ await withRecordedIdentityMutationTarget(target, async (rawTarget) => {
3082
+ await replaceClosure(
3083
+ rawTarget,
3084
+ ctx.schema,
3085
+ ctx.graphId,
3086
+ identityActiveKinds(ctx.registry),
3087
+ ctx.sameIdAcrossKinds
3088
+ );
3089
+ });
3090
+ const rebuilt = await findAffectedClassInconsistency(ctx, target, seeds);
3091
+ if (rebuilt === void 0) return;
3092
+ if (rebuilt.kind === "contradiction") throw rebuilt.error;
3093
+ throw closureMismatchError(ctx.graphId, rebuilt.detail);
3094
+ }
3095
+ async function deleteAssertionsTouchingKinds(target, schema, graphId, kinds, touch) {
3096
+ const removedKinds = [...new Set(kinds)];
3097
+ if (removedKinds.length === 0) return;
3098
+ const matched = /* @__PURE__ */ new Map();
3099
+ const kindChunkSize = identityChunkSize(target, {
3100
+ fixedParameters: 1,
3101
+ maxItems: MAX_REFERENCE_CHUNK_SIZE,
3102
+ parametersPerItem: 2
3103
+ });
3104
+ for (const kindChunk of chunk(removedKinds, kindChunkSize)) {
3105
+ const kindList2 = sql.join(
3106
+ kindChunk.map((kind) => sql`${kind}`),
3107
+ sql`, `
3108
+ );
3109
+ const rows = await target.execute(
3110
+ asCompiledRowsSql(sql`
3111
+ SELECT ${IDENTITY_ASSERTION_COLUMNS}
3112
+ FROM ${schema.identityAssertionsTable}
3113
+ WHERE graph_id = ${graphId}
3114
+ AND (a_kind IN (${kindList2}) OR b_kind IN (${kindList2}))
3115
+ `)
3116
+ );
3117
+ for (const rawRow of rows) {
3118
+ matched.set(rawRow.id, normalizeIdentityAssertionRow(rawRow));
3119
+ }
3120
+ }
3121
+ const ids = [...matched.keys()];
3122
+ if (ids.length > 0) {
3123
+ const idChunkSize = identityChunkSize(target, {
3124
+ fixedParameters: 1,
3125
+ maxItems: MAX_REFERENCE_CHUNK_SIZE,
3126
+ parametersPerItem: 1
3127
+ });
3128
+ for (const idChunk of chunk(ids, idChunkSize)) {
3129
+ const idList = sql.join(
3130
+ idChunk.map((id) => sql`${id}`),
3131
+ sql`, `
3132
+ );
3133
+ await executeIdentityStatement(
3134
+ target,
3135
+ sql`
3136
+ DELETE FROM ${schema.identityAssertionsTable}
3137
+ WHERE graph_id = ${graphId} AND id IN (${idList})
3138
+ `
3139
+ );
3140
+ }
3141
+ }
3142
+ for (const row of matched.values()) touch(graphId, row.id);
3143
+ }
3144
+ async function hasAssertionsTouchingKinds(target, schema, graphId, kinds) {
3145
+ const probedKinds = [...new Set(kinds)];
3146
+ if (probedKinds.length === 0) return false;
3147
+ const kindChunkSize = identityChunkSize(target, {
3148
+ fixedParameters: 1,
3149
+ maxItems: MAX_REFERENCE_CHUNK_SIZE,
3150
+ parametersPerItem: 2
3151
+ });
3152
+ for (const kindChunk of chunk(probedKinds, kindChunkSize)) {
3153
+ const kindList2 = sql.join(
3154
+ kindChunk.map((kind) => sql`${kind}`),
3155
+ sql`, `
3156
+ );
3157
+ const rows = await target.execute(
3158
+ asCompiledRowsSql(sql`
3159
+ SELECT id
3160
+ FROM ${schema.identityAssertionsTable}
3161
+ WHERE graph_id = ${graphId}
3162
+ AND (a_kind IN (${kindList2}) OR b_kind IN (${kindList2}))
3163
+ LIMIT 1
3164
+ `)
3165
+ );
3166
+ if (rows.length > 0) return true;
3167
+ }
3168
+ return false;
3169
+ }
3170
+ async function purgeAssertionsWithUnregisteredKinds(target, schema, graphId, registeredKinds, touch) {
3171
+ const pairs = await target.execute(
3172
+ asCompiledRowsSql(sql`
3173
+ SELECT DISTINCT a_kind, b_kind
3174
+ FROM ${schema.identityAssertionsTable}
3175
+ WHERE graph_id = ${graphId}
3176
+ `)
3177
+ );
3178
+ const unregistered = /* @__PURE__ */ new Set();
3179
+ for (const pair of pairs) {
3180
+ if (!registeredKinds.has(pair.a_kind)) unregistered.add(pair.a_kind);
3181
+ if (!registeredKinds.has(pair.b_kind)) unregistered.add(pair.b_kind);
3182
+ }
3183
+ if (unregistered.size === 0) return;
3184
+ await deleteAssertionsTouchingKinds(
3185
+ target,
3186
+ schema,
3187
+ graphId,
3188
+ [...unregistered],
3189
+ touch
3190
+ );
3191
+ }
3192
+ async function removeIdentityKindsForContext(ctx, kinds, options) {
3193
+ if (kinds.length === 0) return;
3194
+ const removedKinds = [...new Set(kinds)];
3195
+ await runIdentityMutation(ctx, async (target, touch) => {
3196
+ await deleteAssertionsTouchingKinds(
3197
+ target,
3198
+ ctx.schema,
3199
+ ctx.graphId,
3200
+ removedKinds,
3201
+ touch
3202
+ );
3203
+ if (options?.repairClosure === false) return;
3204
+ await replaceClosure(
3205
+ target,
3206
+ ctx.schema,
3207
+ ctx.graphId,
3208
+ identityActiveKinds(ctx.registry),
3209
+ ctx.sameIdAcrossKinds
3210
+ );
3211
+ });
3212
+ }
3213
+ async function liveNodeKindsSharingIds(ctx, target, ids) {
3214
+ const uniqueIds = [...new Set(ids)];
3215
+ const liveKindsById = /* @__PURE__ */ new Map();
3216
+ if (uniqueIds.length === 0) return liveKindsById;
3217
+ const chunkSize = identityChunkSize(target, {
3218
+ fixedParameters: 1,
3219
+ maxItems: MAX_REFERENCE_CHUNK_SIZE,
3220
+ parametersPerItem: 1
3221
+ });
3222
+ for (const idChunk of chunk(uniqueIds, chunkSize)) {
3223
+ const idList = sql.join(
3224
+ idChunk.map((id) => sql`${id}`),
3225
+ sql`, `
3226
+ );
3227
+ const rows = await target.execute(
3228
+ asCompiledRowsSql(sql`
3229
+ SELECT kind, id
3230
+ FROM ${ctx.schema.nodesTable}
3231
+ WHERE graph_id = ${ctx.graphId}
3232
+ AND id IN (${idList})
3233
+ AND deleted_at IS NULL
3234
+ `)
3235
+ );
3236
+ for (const row of rows) {
3237
+ if (!ctx.registry.nodeKinds.has(row.kind)) continue;
3238
+ const kinds = liveKindsById.get(row.id) ?? /* @__PURE__ */ new Set();
3239
+ kinds.add(row.kind);
3240
+ liveKindsById.set(row.id, kinds);
3241
+ }
3242
+ }
3243
+ return liveKindsById;
3244
+ }
3245
+ async function foldIdentityForCreatedNodes(ctx, target, references) {
3246
+ if (references.length === 0 || ctx.sameIdAcrossKinds === "ignore") return;
3247
+ await lockIdentityGraph(target, ctx.graphId);
3248
+ await withRecordedIdentityMutationTarget(target, async (rawTarget) => {
3249
+ const liveKindsById = await liveNodeKindsSharingIds(
3250
+ ctx,
3251
+ rawTarget,
3252
+ references.map((ref) => ref.id)
3253
+ );
3254
+ const closureReferences = [];
3255
+ for (const ref of references) {
3256
+ const liveKinds = liveKindsById.get(ref.id);
3257
+ const peers = [];
3258
+ if (liveKinds !== void 0) {
3259
+ for (const kind of ctx.registry.nodeKinds.keys()) {
3260
+ if (kind === ref.kind || !liveKinds.has(kind)) continue;
3261
+ peers.push({ kind, id: ref.id });
3262
+ }
3263
+ }
3264
+ if (peers.length === 0) continue;
3265
+ for (const peer of peers) {
3266
+ await validateCurrentRelation(
3267
+ ctx,
3268
+ rawTarget,
3269
+ "same",
3270
+ "fold",
3271
+ ref,
3272
+ peer
3273
+ );
3274
+ }
3275
+ closureReferences.push(ref, ...peers);
3276
+ }
3277
+ await replaceAffectedClosure(
3278
+ rawTarget,
3279
+ ctx.schema,
3280
+ ctx.graphId,
3281
+ closureReferences,
3282
+ ctx.sameIdAcrossKinds
3283
+ );
3284
+ });
3285
+ }
3286
+ async function hasMaterializedIdentityClass(target, schema, graphId, ref) {
3287
+ const rows = await target.execute(
3288
+ asCompiledRowsSql(sql`
3289
+ SELECT member_kind, member_id
3290
+ FROM ${schema.identityClosureTable}
3291
+ WHERE graph_id = ${graphId}
3292
+ AND member_kind = ${ref.kind}
3293
+ AND member_id = ${ref.id}
3294
+ LIMIT 1
3295
+ `)
3296
+ );
3297
+ return rows.length > 0;
3298
+ }
3299
+ async function readNodeDeletionInstant(target, schema, graphId, ref) {
3300
+ const rows = await target.execute(
3301
+ asCompiledRowsSql(sql`
3302
+ SELECT deleted_at
3303
+ FROM ${schema.nodesTable}
3304
+ WHERE graph_id = ${graphId}
3305
+ AND kind = ${ref.kind}
3306
+ AND id = ${ref.id}
3307
+ LIMIT 1
3308
+ `)
3309
+ );
3310
+ const row = rows.at(0);
3311
+ return row === void 0 ? void 0 : optionalIdentityTimestamp(row.deleted_at);
3312
+ }
3313
+ async function detachIdentityForNode(ctx, target, ref, mode) {
3314
+ await lockIdentityGraph(target, ctx.graphId);
3315
+ await withRecordedIdentityMutationTarget(target, async (rawTarget, touch) => {
3316
+ const touchesNode = sql`
3317
+ (
3318
+ (a_kind = ${ref.kind} AND a_id = ${ref.id})
3319
+ OR (b_kind = ${ref.kind} AND b_id = ${ref.id})
3320
+ )
3321
+ `;
3322
+ const scope = mode === "hard" ? sql`` : sql`AND valid_to IS NULL AND deleted_at IS NULL`;
3323
+ const rows = await rawTarget.execute(
3324
+ asCompiledRowsSql(sql`
3325
+ SELECT ${IDENTITY_ASSERTION_COLUMNS}
3326
+ FROM ${ctx.schema.identityAssertionsTable}
3327
+ WHERE graph_id = ${ctx.graphId}
3328
+ ${scope}
3329
+ AND ${touchesNode}
3330
+ `)
3331
+ );
3332
+ if (rows.length === 0 && !await hasMaterializedIdentityClass(
3333
+ rawTarget,
3334
+ ctx.schema,
3335
+ ctx.graphId,
3336
+ ref
3337
+ )) {
3338
+ return;
3339
+ }
3340
+ const now = nowIso();
3341
+ const cascadeInstant = mode === "hard" || rows.length === 0 ? now : await readNodeDeletionInstant(
3342
+ rawTarget,
3343
+ ctx.schema,
3344
+ ctx.graphId,
3345
+ ref
3346
+ ) ?? now;
3347
+ for (const rawRow of rows) {
3348
+ const row = normalizeIdentityAssertionRow(rawRow);
3349
+ if (mode === "hard") {
3350
+ await executeIdentityStatement(
3351
+ rawTarget,
3352
+ sql`
3353
+ DELETE FROM ${ctx.schema.identityAssertionsTable}
3354
+ WHERE graph_id = ${ctx.graphId} AND id = ${row.id}
3355
+ `
3356
+ );
3357
+ touch(ctx.graphId, row.id);
3358
+ } else {
3359
+ const validTo = clampValidTo(cascadeInstant, row.valid_from);
3360
+ const ended = {
3361
+ ...row,
3362
+ valid_to: validTo,
3363
+ updated_at: now,
3364
+ ended_by_kind: ref.kind,
3365
+ ended_by_id: ref.id
3366
+ };
3367
+ await executeIdentityStatement(
3368
+ rawTarget,
3369
+ sql`
3370
+ UPDATE ${ctx.schema.identityAssertionsTable}
3371
+ SET valid_to = ${validTo},
3372
+ updated_at = ${now},
3373
+ ended_by_kind = ${ref.kind},
3374
+ ended_by_id = ${ref.id}
3375
+ WHERE graph_id = ${ctx.graphId} AND id = ${row.id}
3376
+ `
3377
+ );
3378
+ touch(ctx.graphId, row.id, ended);
3379
+ }
3380
+ }
3381
+ await replaceAffectedClosure(
3382
+ rawTarget,
3383
+ ctx.schema,
3384
+ ctx.graphId,
3385
+ [ref],
3386
+ ctx.sameIdAcrossKinds
3387
+ );
3388
+ });
3389
+ }
3390
+
3391
+ export { IDENTITY_IMPORT_FAILED_ASSERTION, IDENTITY_IMPORT_PROGRESS, applyIdentityChangesForContext, assertAffectedIdentityClassesConsistent, createIdentityFacade, createIdentityReadFacade, createValidationError, deleteAssertionsTouchingKinds, detachIdentityForNode, encodeTupleKey, foldIdentityForCreatedNodes, hasAssertionsTouchingKinds, identityAssertionSemanticKey, identityReferenceKey, importIdentityAssertionsIntoTarget, liveNodeKindsSharingIds, loadAssertionsByIds, loadCurrentStructuralClasses, lockIdentityEnablementNodes, lockIdentityGraph, lockSchemaVersionForStoreWrite, normalizeIdentityPair, purgeAssertionsWithUnregisteredKinds, readIdentityAssertionPageAtTarget, readIdentityAssertionsForInterchange, rebuildIdentityClosureForContext, refKey, removeIdentityKindsForContext, runHookedWriteOperation, runInWriteTransaction, separationRebuildRequired, toTransferAssertion, validateEdgeProps, validateIdentityForContext, validateNodeProps, validateProps, withWriteTransactionSession, wrapZodError };
3392
+ //# sourceMappingURL=chunk-JIVMTW7W.js.map
3393
+ //# sourceMappingURL=chunk-JIVMTW7W.js.map