@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.
- package/dist/backend/drizzle/indexes.cjs +11 -11
- package/dist/backend/drizzle/indexes.d.cts +1 -1
- package/dist/backend/drizzle/indexes.d.ts +1 -1
- package/dist/backend/drizzle/indexes.js +7 -7
- package/dist/backend/index.cjs +53 -54
- package/dist/backend/index.d.cts +3 -3
- package/dist/backend/index.d.ts +3 -3
- package/dist/backend/index.js +13 -14
- package/dist/backend/postgres/index.cjs +29 -30
- package/dist/backend/postgres/index.d.cts +7 -7
- package/dist/backend/postgres/index.d.ts +7 -7
- package/dist/backend/postgres/index.js +16 -17
- package/dist/backend/postgres/pglite-store.cjs +48 -43
- package/dist/backend/postgres/pglite-store.cjs.map +1 -1
- package/dist/backend/postgres/pglite-store.d.cts +15 -9
- package/dist/backend/postgres/pglite-store.d.ts +15 -9
- package/dist/backend/postgres/pglite-store.js +44 -39
- package/dist/backend/postgres/pglite-store.js.map +1 -1
- package/dist/backend/postgres/pglite.cjs +18 -19
- package/dist/backend/postgres/pglite.d.cts +4 -4
- package/dist/backend/postgres/pglite.d.ts +4 -4
- package/dist/backend/postgres/pglite.js +17 -18
- package/dist/backend/sqlite/index.cjs +26 -27
- package/dist/backend/sqlite/index.d.cts +6 -6
- package/dist/backend/sqlite/index.d.ts +6 -6
- package/dist/backend/sqlite/index.js +14 -15
- package/dist/backend/sqlite/libsql.cjs +21 -22
- package/dist/backend/sqlite/libsql.cjs.map +1 -1
- package/dist/backend/sqlite/libsql.d.cts +15 -8
- package/dist/backend/sqlite/libsql.d.ts +15 -8
- package/dist/backend/sqlite/libsql.js +17 -18
- package/dist/backend/sqlite/libsql.js.map +1 -1
- package/dist/backend/sqlite/local-store.cjs +49 -44
- package/dist/backend/sqlite/local-store.cjs.map +1 -1
- package/dist/backend/sqlite/local-store.d.cts +15 -10
- package/dist/backend/sqlite/local-store.d.ts +15 -10
- package/dist/backend/sqlite/local-store.js +45 -40
- package/dist/backend/sqlite/local-store.js.map +1 -1
- package/dist/backend/sqlite/local.cjs +19 -20
- package/dist/backend/sqlite/local.d.cts +3 -3
- package/dist/backend/sqlite/local.d.ts +3 -3
- package/dist/backend/sqlite/local.js +17 -18
- package/dist/{chunk-XMYJY7NI.js → chunk-2LG2XB2M.js} +3 -3
- package/dist/{chunk-XMYJY7NI.js.map → chunk-2LG2XB2M.js.map} +1 -1
- package/dist/{chunk-QGKILWGZ.cjs → chunk-2NA5DYLO.cjs} +4 -4
- package/dist/{chunk-QGKILWGZ.cjs.map → chunk-2NA5DYLO.cjs.map} +1 -1
- package/dist/{chunk-5MUSU4ZP.cjs → chunk-3DHZCOT7.cjs} +20 -20
- package/dist/chunk-3DHZCOT7.cjs.map +1 -0
- package/dist/{chunk-OVUUTCPJ.cjs → chunk-4UGOKMI5.cjs} +268 -150
- package/dist/chunk-4UGOKMI5.cjs.map +1 -0
- package/dist/{chunk-FRHHDHKH.js → chunk-5262ZMTR.js} +26 -5
- package/dist/chunk-5262ZMTR.js.map +1 -0
- package/dist/{chunk-HOIFFY4M.js → chunk-5763DULU.js} +248 -76
- package/dist/chunk-5763DULU.js.map +1 -0
- package/dist/{chunk-7G32LIQM.js → chunk-5UVRGXD3.js} +277 -7
- package/dist/chunk-5UVRGXD3.js.map +1 -0
- package/dist/{chunk-IIT5K7SK.cjs → chunk-62ZP23AW.cjs} +15 -15
- package/dist/{chunk-IIT5K7SK.cjs.map → chunk-62ZP23AW.cjs.map} +1 -1
- package/dist/{chunk-S4KQ5LV4.js → chunk-775A4ZMN.js} +40 -6
- package/dist/chunk-775A4ZMN.js.map +1 -0
- package/dist/{chunk-QYWINYEA.cjs → chunk-7BQXPCGN.cjs} +830 -170
- package/dist/chunk-7BQXPCGN.cjs.map +1 -0
- package/dist/{chunk-FJMPR3OH.cjs → chunk-7EO3TCQB.cjs} +33 -7
- package/dist/chunk-7EO3TCQB.cjs.map +1 -0
- package/dist/chunk-7GT72YHH.cjs +1979 -0
- package/dist/chunk-7GT72YHH.cjs.map +1 -0
- package/dist/{chunk-SQRD2CYF.js → chunk-7VQXLT5O.js} +717 -76
- package/dist/chunk-7VQXLT5O.js.map +1 -0
- package/dist/{chunk-BCP7LLOY.js → chunk-7Z5OR7KO.js} +2706 -625
- package/dist/chunk-7Z5OR7KO.js.map +1 -0
- package/dist/chunk-A4OC4DVN.cjs +12 -0
- package/dist/{chunk-4SZ6XPTG.cjs.map → chunk-A4OC4DVN.cjs.map} +1 -1
- package/dist/{chunk-TDXIBCGY.cjs → chunk-A7J2YDDI.cjs} +5 -3
- package/dist/chunk-A7J2YDDI.cjs.map +1 -0
- package/dist/{chunk-FMMIX6N4.js → chunk-AKLS3BYZ.js} +56 -5
- package/dist/chunk-AKLS3BYZ.js.map +1 -0
- package/dist/{chunk-D7PG5D5K.js → chunk-AMKPPFUO.js} +32 -6
- package/dist/chunk-AMKPPFUO.js.map +1 -0
- package/dist/{chunk-PAW2XUZN.js → chunk-ASTI57H3.js} +3 -3
- package/dist/{chunk-PAW2XUZN.js.map → chunk-ASTI57H3.js.map} +1 -1
- package/dist/{chunk-FMGEZBAQ.js → chunk-BKCUOV7A.js} +27 -5
- package/dist/chunk-BKCUOV7A.js.map +1 -0
- package/dist/chunk-BSFJMMV3.cjs +268 -0
- package/dist/chunk-BSFJMMV3.cjs.map +1 -0
- package/dist/{chunk-63KCKSSH.js → chunk-C23WSN5E.js} +209 -24
- package/dist/chunk-C23WSN5E.js.map +1 -0
- package/dist/{chunk-JRP3VGHS.js → chunk-CBTIUB76.js} +3 -3
- package/dist/{chunk-JRP3VGHS.js.map → chunk-CBTIUB76.js.map} +1 -1
- package/dist/chunk-CNV4TXJG.js +58 -0
- package/dist/chunk-CNV4TXJG.js.map +1 -0
- package/dist/{chunk-QB6VMOJ2.cjs → chunk-CXBHOOIQ.cjs} +45 -10
- package/dist/chunk-CXBHOOIQ.cjs.map +1 -0
- package/dist/{chunk-46GDII45.js → chunk-DCQVHAYQ.js} +5 -5
- package/dist/{chunk-46GDII45.js.map → chunk-DCQVHAYQ.js.map} +1 -1
- package/dist/chunk-DLNROMQ7.js +306 -0
- package/dist/chunk-DLNROMQ7.js.map +1 -0
- package/dist/chunk-DYATYRY4.js +47 -0
- package/dist/chunk-DYATYRY4.js.map +1 -0
- package/dist/chunk-E4WNQBEF.cjs +217 -0
- package/dist/chunk-E4WNQBEF.cjs.map +1 -0
- package/dist/{chunk-G6Q5UM7U.js → chunk-FWJKWH6U.js} +5 -3
- package/dist/chunk-FWJKWH6U.js.map +1 -0
- package/dist/{chunk-BNUFUFAU.js → chunk-HFHTMECK.js} +7 -7
- package/dist/chunk-HFHTMECK.js.map +1 -0
- package/dist/{chunk-TFMVL4QH.js → chunk-I5MOXA4H.js} +20 -14
- package/dist/chunk-I5MOXA4H.js.map +1 -0
- package/dist/{chunk-4MB3AQAQ.js → chunk-J3WZ3FA2.js} +77 -3
- package/dist/chunk-J3WZ3FA2.js.map +1 -0
- package/dist/{chunk-UR7W5YQK.js → chunk-JBQQ7THH.js} +5 -3
- package/dist/chunk-JBQQ7THH.js.map +1 -0
- package/dist/chunk-JIVMTW7W.js +3393 -0
- package/dist/chunk-JIVMTW7W.js.map +1 -0
- package/dist/{chunk-2Y4IFKW7.js → chunk-JXA2ZP2Q.js} +6 -17
- package/dist/chunk-JXA2ZP2Q.js.map +1 -0
- package/dist/{chunk-GYA4JRP5.js → chunk-JXXDZTMN.js} +2 -2
- package/dist/chunk-JXXDZTMN.js.map +1 -0
- package/dist/chunk-JZUR7ZWE.js +254 -0
- package/dist/chunk-JZUR7ZWE.js.map +1 -0
- package/dist/chunk-K5ELX2GT.js +248 -0
- package/dist/chunk-K5ELX2GT.js.map +1 -0
- package/dist/chunk-LBJKZXDE.cjs +257 -0
- package/dist/chunk-LBJKZXDE.cjs.map +1 -0
- package/dist/{chunk-FGO5JFVJ.cjs → chunk-M4BDU5RX.cjs} +12 -12
- package/dist/{chunk-FGO5JFVJ.cjs.map → chunk-M4BDU5RX.cjs.map} +1 -1
- package/dist/{chunk-MIJKEVV7.js → chunk-MSY7FSX4.js} +37 -4
- package/dist/chunk-MSY7FSX4.js.map +1 -0
- package/dist/{chunk-ST3LPB2V.cjs → chunk-MUK4BU7K.cjs} +788 -146
- package/dist/chunk-MUK4BU7K.cjs.map +1 -0
- package/dist/{chunk-MCHP7RHZ.cjs → chunk-MXJDTH5B.cjs} +64 -41
- package/dist/chunk-MXJDTH5B.cjs.map +1 -0
- package/dist/{chunk-2VLBGQBY.cjs → chunk-N3257XE6.cjs} +724 -577
- package/dist/chunk-N3257XE6.cjs.map +1 -0
- package/dist/chunk-N66HCGCY.cjs +57 -0
- package/dist/chunk-N66HCGCY.cjs.map +1 -0
- package/dist/{chunk-CPEL3YIR.cjs → chunk-NFCVHQ5P.cjs} +55 -22
- package/dist/chunk-NFCVHQ5P.cjs.map +1 -0
- package/dist/{chunk-C4F7VJ5D.cjs → chunk-NI72YOJB.cjs} +84 -2
- package/dist/chunk-NI72YOJB.cjs.map +1 -0
- package/dist/{chunk-EE7N5XXQ.cjs → chunk-NK5Q7UNE.cjs} +10 -21
- package/dist/chunk-NK5Q7UNE.cjs.map +1 -0
- package/dist/{chunk-PHXITZE4.js → chunk-NMWDXFPH.js} +4 -4
- package/dist/{chunk-PHXITZE4.js.map → chunk-NMWDXFPH.js.map} +1 -1
- package/dist/{chunk-FSXTPBRN.js → chunk-NQ6BRJBE.js} +416 -850
- package/dist/chunk-NQ6BRJBE.js.map +1 -0
- package/dist/chunk-OH5E5KIO.js +2057 -0
- package/dist/chunk-OH5E5KIO.js.map +1 -0
- package/dist/chunk-ONU3HIED.cjs +2082 -0
- package/dist/chunk-ONU3HIED.cjs.map +1 -0
- package/dist/{chunk-STQ3BSED.cjs → chunk-PNN7RHVR.cjs} +66 -44
- package/dist/chunk-PNN7RHVR.cjs.map +1 -0
- package/dist/{chunk-6DCB664D.js → chunk-PQBQG7IM.js} +3 -3
- package/dist/{chunk-6DCB664D.js.map → chunk-PQBQG7IM.js.map} +1 -1
- package/dist/{chunk-ACSVZPK7.cjs → chunk-PWLJ77UV.cjs} +5 -3
- package/dist/chunk-PWLJ77UV.cjs.map +1 -0
- package/dist/chunk-Q7Q4WSLJ.cjs +328 -0
- package/dist/chunk-Q7Q4WSLJ.cjs.map +1 -0
- package/dist/{chunk-BEFM74L2.js → chunk-QLRESYPU.js} +637 -493
- package/dist/chunk-QLRESYPU.js.map +1 -0
- package/dist/{chunk-SBPC5IP3.js → chunk-QWX5YA3K.js} +784 -202
- package/dist/chunk-QWX5YA3K.js.map +1 -0
- package/dist/{chunk-TCRIQMBU.js → chunk-RJS7DECT.js} +8 -9
- package/dist/chunk-RJS7DECT.js.map +1 -0
- package/dist/{chunk-6JZUSDY2.cjs → chunk-RMQLSPA2.cjs} +297 -27
- package/dist/chunk-RMQLSPA2.cjs.map +1 -0
- package/dist/{chunk-SJDMVITW.cjs → chunk-RT5ZY4XA.cjs} +6 -5
- package/dist/chunk-RT5ZY4XA.cjs.map +1 -0
- package/dist/{chunk-IJTLXNGJ.js → chunk-T4XNKYXB.js} +780 -127
- package/dist/chunk-T4XNKYXB.js.map +1 -0
- package/dist/{chunk-PDGEXGS6.cjs → chunk-TQR4KBY2.cjs} +354 -182
- package/dist/chunk-TQR4KBY2.cjs.map +1 -0
- package/dist/chunk-TTAITEPJ.cjs +755 -0
- package/dist/chunk-TTAITEPJ.cjs.map +1 -0
- package/dist/{chunk-NN3QSOVS.cjs → chunk-U5SON7R2.cjs} +27 -27
- package/dist/{chunk-NN3QSOVS.cjs.map → chunk-U5SON7R2.cjs.map} +1 -1
- package/dist/{chunk-A2I2XO52.cjs → chunk-U63GDNRC.cjs} +32 -26
- package/dist/chunk-U63GDNRC.cjs.map +1 -0
- package/dist/{chunk-YNULOIR5.cjs → chunk-UDISNXDL.cjs} +64 -43
- package/dist/chunk-UDISNXDL.cjs.map +1 -0
- package/dist/{chunk-GWEDD3DH.js → chunk-UV3SN74I.js} +5 -5
- package/dist/{chunk-GWEDD3DH.js.map → chunk-UV3SN74I.js.map} +1 -1
- package/dist/{chunk-U5W4LKZV.cjs → chunk-VCRRKBLN.cjs} +9 -9
- package/dist/{chunk-U5W4LKZV.cjs.map → chunk-VCRRKBLN.cjs.map} +1 -1
- package/dist/{chunk-EFELJVLA.js → chunk-VGTJPTIC.js} +28 -5
- package/dist/chunk-VGTJPTIC.js.map +1 -0
- package/dist/chunk-VQH4VAWD.cjs +12 -0
- package/dist/{chunk-EPTSMFH7.cjs.map → chunk-VQH4VAWD.cjs.map} +1 -1
- package/dist/{chunk-SNXVDZAJ.js → chunk-W6OG3HDG.js} +5 -4
- package/dist/chunk-W6OG3HDG.js.map +1 -0
- package/dist/chunk-WDGA7IYB.js +207 -0
- package/dist/chunk-WDGA7IYB.js.map +1 -0
- package/dist/chunk-WEVQJ5LH.js +736 -0
- package/dist/chunk-WEVQJ5LH.js.map +1 -0
- package/dist/{chunk-3PRKZW6D.cjs → chunk-XBYVV3MP.cjs} +60 -5
- package/dist/chunk-XBYVV3MP.cjs.map +1 -0
- package/dist/{chunk-GFRGG2US.cjs → chunk-XCAGZAKP.cjs} +7 -7
- package/dist/{chunk-GFRGG2US.cjs.map → chunk-XCAGZAKP.cjs.map} +1 -1
- package/dist/{chunk-XR4YZFKW.cjs → chunk-XDF2UPEQ.cjs} +3864 -1783
- package/dist/chunk-XDF2UPEQ.cjs.map +1 -0
- package/dist/{chunk-7V2FGGI2.cjs → chunk-XMQSEJCF.cjs} +632 -1080
- package/dist/chunk-XMQSEJCF.cjs.map +1 -0
- package/dist/chunk-XNLIZYRZ.cjs +3432 -0
- package/dist/chunk-XNLIZYRZ.cjs.map +1 -0
- package/dist/{chunk-66UVLHQA.cjs → chunk-XNSI2FJO.cjs} +99 -100
- package/dist/chunk-XNSI2FJO.cjs.map +1 -0
- package/dist/chunk-XQSCL77R.cjs +60 -0
- package/dist/chunk-XQSCL77R.cjs.map +1 -0
- package/dist/{chunk-SVJ5GEXW.cjs → chunk-XUO3KQFO.cjs} +2 -2
- package/dist/chunk-XUO3KQFO.cjs.map +1 -0
- package/dist/{chunk-HJH3RMXG.cjs → chunk-YAAM3M2S.cjs} +10 -10
- package/dist/{chunk-HJH3RMXG.cjs.map → chunk-YAAM3M2S.cjs.map} +1 -1
- package/dist/{chunk-ATLN63JA.js → chunk-YBFHZUXX.js} +6 -6
- package/dist/{chunk-ATLN63JA.js.map → chunk-YBFHZUXX.js.map} +1 -1
- package/dist/{chunk-K3BUWSFR.js → chunk-YDFDBE6D.js} +119 -6
- package/dist/chunk-YDFDBE6D.js.map +1 -0
- package/dist/{chunk-OSZPLZIV.cjs → chunk-YVWGEQJE.cjs} +318 -132
- package/dist/chunk-YVWGEQJE.cjs.map +1 -0
- package/dist/{chunk-QT6V3YRH.js → chunk-YWGVVALJ.js} +3 -3
- package/dist/{chunk-QT6V3YRH.js.map → chunk-YWGVVALJ.js.map} +1 -1
- package/dist/core/index.cjs +40 -41
- package/dist/core/index.d.cts +4 -5
- package/dist/core/index.d.ts +4 -5
- package/dist/core/index.js +8 -9
- package/dist/{ddl-eWg7hx6R.d.cts → ddl-BJcOp4WZ.d.cts} +3 -3
- package/dist/{ddl-D5369kOY.d.ts → ddl-BXfGHbPN.d.ts} +3 -3
- package/dist/graph-extension/index.cjs +26 -23
- package/dist/graph-extension/index.d.cts +16 -6
- package/dist/graph-extension/index.d.ts +16 -6
- package/dist/graph-extension/index.js +7 -8
- package/dist/graph-merge/index.cjs +2459 -655
- package/dist/graph-merge/index.cjs.map +1 -1
- package/dist/graph-merge/index.d.cts +170 -33
- package/dist/graph-merge/index.d.ts +170 -33
- package/dist/graph-merge/index.js +2269 -468
- package/dist/graph-merge/index.js.map +1 -1
- package/dist/{index-By89Sezh.d.ts → index-BSHvKfok.d.ts} +221 -10
- package/dist/{index-DcQ3Sxs-.d.cts → index-DxaYCv8Y.d.cts} +221 -10
- package/dist/index.cjs +330 -269
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +590 -576
- package/dist/index.d.ts +590 -576
- package/dist/index.js +73 -50
- package/dist/index.js.map +1 -1
- package/dist/indexes/index.cjs +17 -17
- package/dist/indexes/index.d.cts +4 -4
- package/dist/indexes/index.d.ts +4 -4
- package/dist/indexes/index.js +10 -10
- package/dist/interchange/index.cjs +165 -50
- package/dist/interchange/index.cjs.map +1 -1
- package/dist/interchange/index.d.cts +261 -16
- package/dist/interchange/index.d.ts +261 -16
- package/dist/interchange/index.js +128 -24
- package/dist/interchange/index.js.map +1 -1
- package/dist/{manager-B0KjKE93.d.ts → manager-CfZAimti.d.ts} +36 -5
- package/dist/{manager-Cn0NFIhN.d.cts → manager-HGk6E_HW.d.cts} +36 -5
- package/dist/{materialize-indexes-BD4V3svD.d.ts → materialize-indexes-6A7dWGtb.d.cts} +10 -2
- package/dist/{materialize-indexes-DVRhd5h4.d.cts → materialize-indexes-BMJP6Tms.d.ts} +10 -2
- package/dist/{postgres-DaEWvN4x.d.cts → postgres-DFYWENdR.d.cts} +1961 -439
- package/dist/{postgres-u0a0HN2D.d.ts → postgres-e7a9TdvR.d.ts} +1961 -439
- package/dist/profiler/index.cjs +15 -15
- package/dist/profiler/index.d.cts +9 -10
- package/dist/profiler/index.d.ts +9 -10
- package/dist/profiler/index.js +4 -4
- package/dist/provenance/index.cjs +46 -48
- package/dist/provenance/index.cjs.map +1 -1
- package/dist/provenance/index.d.cts +7 -7
- package/dist/provenance/index.d.ts +7 -7
- package/dist/provenance/index.js +12 -14
- package/dist/provenance/index.js.map +1 -1
- package/dist/schema/index.cjs +50 -49
- package/dist/schema/index.d.cts +9 -16
- package/dist/schema/index.d.ts +9 -16
- package/dist/schema/index.js +23 -22
- package/dist/{searchable-CnYEancP.d.ts → searchable-9OQoPuRs.d.ts} +1 -2
- package/dist/{searchable-BT7j9_IK.d.cts → searchable-CIje5XYD.d.cts} +1 -2
- package/dist/{sqlite-BrMoSsI7.d.cts → sqlite-DerU5tc7.d.ts} +2029 -347
- package/dist/{sqlite-CHhZUyO5.d.ts → sqlite-DitnISa9.d.cts} +2029 -347
- package/dist/{store-view-Bvdku0Ht.d.ts → store-DawaWWjJ.d.ts} +3145 -2556
- package/dist/{store-view-w-md0nQ4.d.cts → store-RwTJx8LX.d.cts} +3145 -2556
- package/dist/{temporal-BCORwUau.d.cts → temporal-Bn-Fi8FJ.d.cts} +1 -1
- package/dist/{temporal--x9T8K4S.d.ts → temporal-BtM4UALv.d.ts} +1 -1
- package/dist/{types-6-WJmSD4.d.cts → types-CoIx8HX5.d.cts} +1 -1
- package/dist/{types-Cjvkj0jA.d.ts → types-HkveCCx0.d.cts} +866 -21
- package/dist/{types-Cjvkj0jA.d.cts → types-HkveCCx0.d.ts} +866 -21
- package/dist/{types-COODtTJU.d.ts → types-KFfk-8Ah.d.ts} +1 -1
- package/dist/{where-CUQmCR6d.d.cts → where--fk7_0kv.d.cts} +1 -1
- package/dist/{where-5xVawAwL.d.ts → where-ws5hf-Ov.d.ts} +1 -1
- package/package.json +1 -1
- package/dist/chunk-2BIG7U6E.cjs +0 -83
- package/dist/chunk-2BIG7U6E.cjs.map +0 -1
- package/dist/chunk-2L54C2C2.js +0 -144
- package/dist/chunk-2L54C2C2.js.map +0 -1
- package/dist/chunk-2VLBGQBY.cjs.map +0 -1
- package/dist/chunk-2WDD2NO3.js +0 -59
- package/dist/chunk-2WDD2NO3.js.map +0 -1
- package/dist/chunk-2Y4IFKW7.js.map +0 -1
- package/dist/chunk-35S6P5ZU.js +0 -27
- package/dist/chunk-35S6P5ZU.js.map +0 -1
- package/dist/chunk-3PRKZW6D.cjs.map +0 -1
- package/dist/chunk-4MB3AQAQ.js.map +0 -1
- package/dist/chunk-4SZ6XPTG.cjs +0 -12
- package/dist/chunk-5MUSU4ZP.cjs.map +0 -1
- package/dist/chunk-63KCKSSH.js.map +0 -1
- package/dist/chunk-66UVLHQA.cjs.map +0 -1
- package/dist/chunk-6JZUSDY2.cjs.map +0 -1
- package/dist/chunk-764JHTLT.cjs +0 -15
- package/dist/chunk-764JHTLT.cjs.map +0 -1
- package/dist/chunk-7G32LIQM.js.map +0 -1
- package/dist/chunk-7V2FGGI2.cjs.map +0 -1
- package/dist/chunk-A2I2XO52.cjs.map +0 -1
- package/dist/chunk-A4PBRX4C.cjs +0 -100
- package/dist/chunk-A4PBRX4C.cjs.map +0 -1
- package/dist/chunk-ACSVZPK7.cjs.map +0 -1
- package/dist/chunk-ALFUGRYB.cjs +0 -1282
- package/dist/chunk-ALFUGRYB.cjs.map +0 -1
- package/dist/chunk-B75XRV3O.js +0 -125
- package/dist/chunk-B75XRV3O.js.map +0 -1
- package/dist/chunk-BCP7LLOY.js.map +0 -1
- package/dist/chunk-BEFM74L2.js.map +0 -1
- package/dist/chunk-BNUFUFAU.js.map +0 -1
- package/dist/chunk-C4F7VJ5D.cjs.map +0 -1
- package/dist/chunk-CPEL3YIR.cjs.map +0 -1
- package/dist/chunk-D7PG5D5K.js.map +0 -1
- package/dist/chunk-DI74SMXS.cjs +0 -29
- package/dist/chunk-DI74SMXS.cjs.map +0 -1
- package/dist/chunk-DK2S42XB.js +0 -77
- package/dist/chunk-DK2S42XB.js.map +0 -1
- package/dist/chunk-DRACN6XG.js +0 -27
- package/dist/chunk-DRACN6XG.js.map +0 -1
- package/dist/chunk-EE7N5XXQ.cjs.map +0 -1
- package/dist/chunk-EFELJVLA.js.map +0 -1
- package/dist/chunk-EPTSMFH7.cjs +0 -12
- package/dist/chunk-EQ7JKLZO.cjs +0 -157
- package/dist/chunk-EQ7JKLZO.cjs.map +0 -1
- package/dist/chunk-FJMPR3OH.cjs.map +0 -1
- package/dist/chunk-FMGEZBAQ.js.map +0 -1
- package/dist/chunk-FMMIX6N4.js.map +0 -1
- package/dist/chunk-FRHHDHKH.js.map +0 -1
- package/dist/chunk-FSXTPBRN.js.map +0 -1
- package/dist/chunk-G6Q5UM7U.js.map +0 -1
- package/dist/chunk-GYA4JRP5.js.map +0 -1
- package/dist/chunk-HOHAJ2NR.cjs +0 -61
- package/dist/chunk-HOHAJ2NR.cjs.map +0 -1
- package/dist/chunk-HOIFFY4M.js.map +0 -1
- package/dist/chunk-IIMQJYAM.js +0 -81
- package/dist/chunk-IIMQJYAM.js.map +0 -1
- package/dist/chunk-IJTLXNGJ.js.map +0 -1
- package/dist/chunk-ITE4IXAK.js +0 -25
- package/dist/chunk-ITE4IXAK.js.map +0 -1
- package/dist/chunk-K3BUWSFR.js.map +0 -1
- package/dist/chunk-KCO5EFJU.js +0 -79
- package/dist/chunk-KCO5EFJU.js.map +0 -1
- package/dist/chunk-KKYELPZ2.cjs +0 -83
- package/dist/chunk-KKYELPZ2.cjs.map +0 -1
- package/dist/chunk-MCHP7RHZ.cjs.map +0 -1
- package/dist/chunk-MIJKEVV7.js.map +0 -1
- package/dist/chunk-NCX65HBY.cjs +0 -1396
- package/dist/chunk-NCX65HBY.cjs.map +0 -1
- package/dist/chunk-OSZPLZIV.cjs.map +0 -1
- package/dist/chunk-OVUUTCPJ.cjs.map +0 -1
- package/dist/chunk-PDGEXGS6.cjs.map +0 -1
- package/dist/chunk-QB6VMOJ2.cjs.map +0 -1
- package/dist/chunk-QMQLUA66.js +0 -12
- package/dist/chunk-QMQLUA66.js.map +0 -1
- package/dist/chunk-QYWINYEA.cjs.map +0 -1
- package/dist/chunk-S4KQ5LV4.js.map +0 -1
- package/dist/chunk-SBPC5IP3.js.map +0 -1
- package/dist/chunk-SJDMVITW.cjs.map +0 -1
- package/dist/chunk-SNXVDZAJ.js.map +0 -1
- package/dist/chunk-SQRD2CYF.js.map +0 -1
- package/dist/chunk-ST3LPB2V.cjs.map +0 -1
- package/dist/chunk-STQ3BSED.cjs.map +0 -1
- package/dist/chunk-SVJ5GEXW.cjs.map +0 -1
- package/dist/chunk-TCRIQMBU.js.map +0 -1
- package/dist/chunk-TDXIBCGY.cjs.map +0 -1
- package/dist/chunk-TFMVL4QH.js.map +0 -1
- package/dist/chunk-TRF4EBJT.cjs +0 -30
- package/dist/chunk-TRF4EBJT.cjs.map +0 -1
- package/dist/chunk-TVLKA4AY.js +0 -94
- package/dist/chunk-TVLKA4AY.js.map +0 -1
- package/dist/chunk-UR7W5YQK.js.map +0 -1
- package/dist/chunk-VITTKBWF.js +0 -1263
- package/dist/chunk-VITTKBWF.js.map +0 -1
- package/dist/chunk-WBUFJ7BS.cjs +0 -131
- package/dist/chunk-WBUFJ7BS.cjs.map +0 -1
- package/dist/chunk-XR4YZFKW.cjs.map +0 -1
- package/dist/chunk-YNULOIR5.cjs.map +0 -1
- package/dist/chunk-ZDH3DX3C.cjs +0 -30
- package/dist/chunk-ZDH3DX3C.cjs.map +0 -1
- package/dist/chunk-ZTAJL4DT.cjs +0 -85
- package/dist/chunk-ZTAJL4DT.cjs.map +0 -1
- package/dist/define-graph-DVF-oN1i.d.cts +0 -173
- package/dist/define-graph-SvYXOl78.d.ts +0 -173
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import { createEdgeRowMapper, createNodeRowMapper, SQLITE_ROW_MAPPER_CONFIG, POSTGRES_ROW_MAPPER_CONFIG } from './chunk-
|
|
2
|
-
import { rowToNode, executeNodeBulkFindByIndex, executeNodeBulkFindByConstraint, executeNodeFindByConstraint, executeNodeBulkGetOrCreateByConstraint, executeNodeGetOrCreateByConstraint, executeNodeHardDelete, executeNodeDelete, nodeUpsertDirtyCheck, executeNodeUpsertUpdate, executeNodeUpdate, executeNodeCreateNoReturnBatch, executeNodeCreateBatch, executeNodeCreate, rowToEdge,
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import { NODE_META_KEYS, EDGE_META_KEYS } from './chunk-TFMVL4QH.js';
|
|
1
|
+
import { createEdgeRowMapper, createNodeRowMapper, SQLITE_ROW_MAPPER_CONFIG, POSTGRES_ROW_MAPPER_CONFIG } from './chunk-NMWDXFPH.js';
|
|
2
|
+
import { rowToNode, executeNodeBulkFindByIndex, executeNodeBulkFindByConstraint, executeNodeFindByConstraint, executeNodeBulkGetOrCreateByConstraint, executeNodeGetOrCreateByConstraint, executeNodeHardDelete, executeNodeDeleteBatch, executeNodeDelete, nodeUpsertDirtyCheck, executeNodeUpsertUpdate, executeNodeUpdateWhere, executeNodeUpdate, executeNodeCreateNoReturnBatch, executeNodeCreateBatch, executeNodeCreate, rowToEdge, checkCardinalityConstraint, edgeWriteNeedsConstraintFence, withAlreadyExistsTranslation, rowToNodeMeta, rowToEdgeMeta } from './chunk-QWX5YA3K.js';
|
|
3
|
+
import { storeRuntime, STORE_RUNTIME, TRANSACTION_RUNTIME, AUTO_REFRESH_STATISTICS_ROW_THRESHOLD, getEmbeddingFields, computeFulltextContent } from './chunk-WEVQJ5LH.js';
|
|
4
|
+
import { loadAndMergeGraphExtensionDocument, serializeSchema, buildKindRegistry, ensureIdentitySchemaStorage, loadAndVerifyGraph, identitySchemaCommitPreflight, getSchemaChanges, requiresMigration, mergeGraphExtension, commitNewSchemaVersion, commitNewSchemaVersionWithPreflight, planRemovals, stripGraphExtension, applyDeprecatedKinds, identityKindCascadeNeeded, ensureSchema, loadActiveSchemaWithBootstrap, parseSerializedSchema, extensionKindNames, serializeSchemaProperties, buildGraphExtensionOntologyKeySet, compileTimeOntologyKey, commitNewSchemaVersionIfKindsEmpty, serializeIndexDeclaration } from './chunk-QLRESYPU.js';
|
|
5
|
+
import { getTypeName } from './chunk-JXA2ZP2Q.js';
|
|
6
|
+
import { rebuildIdentityClosureForContext, loadCurrentStructuralClasses, loadAssertionsByIds, toTransferAssertion, liveNodeKindsSharingIds, createIdentityFacade, createIdentityReadFacade, runInWriteTransaction, validateIdentityForContext, removeIdentityKindsForContext, readIdentityAssertionsForInterchange, readIdentityAssertionPageAtTarget, lockIdentityGraph, foldIdentityForCreatedNodes, importIdentityAssertionsIntoTarget, assertAffectedIdentityClassesConsistent, applyIdentityChangesForContext, withWriteTransactionSession, lockSchemaVersionForStoreWrite, refKey, detachIdentityForNode, validateEdgeProps, runHookedWriteOperation, encodeTupleKey } from './chunk-JIVMTW7W.js';
|
|
7
|
+
import { isParameterRef, resolveParameterValueType, fieldRef, compareStrings, buildFieldBuilderForTypeInfo, stringField, createFulltextAccessor, CURRENT_READ_INSTANT_PLACEHOLDER, withPinnedReadInstant, compareCodePoints, assertRevisionTrackableBackend, createRecordedBackend, createRecordedTransactionScope, readRecordedClock, ensureRevisionOrigin, withRecordedFlushObserver, recordedCaptureRequiresCallbackTransactionError, assertRecordedCaptureTransactionIsolation, lockRecordedGraphWrite, createGraphBackendProjection, generateId, assertCurrentRecordedSchema, baseField, objectField, arrayField, dateField, numberField, advanceRevisionClock, runInsertBatch, edgeInsertDispatch, runInsertBatchReturning, projectBackendMembers, sqlValueList, getNodeRowsByIds, getEdgeRowsByIds, compileKindFilter, runInsertNoReturn, currentReadInstant, compileTemporalFilter, closeRecordedHardDeletedKind, buildPredicateIndex, historicalIdentityPeerClassQuery, IDENTITY_PEER_CLASS_COLUMNS, compileFieldValue, recordedBindParamBudget, extractVectorSimilarityPredicates, extractFulltextMatchPredicates, currentReadInstantFor, extractTemporalOptions, compilePredicateExpression, getPredicatesForAlias, getFieldPointer, getNodeKindsForAlias, getHybridTargetAlias, throwHistoryUnsafeSqlAccess, throwRevisionTrackingUnsafeSqlAccess, compilePredicateClauses } from './chunk-NQ6BRJBE.js';
|
|
8
|
+
import { canonicalEqual, sortedReplacer } from './chunk-JBQQ7THH.js';
|
|
9
|
+
import { withRecordedRelationsPrecondition, chunk, postgresReadWriteRefusedSqlState, isMissingTableError, groupBy, postgresTemporaryTableUnavailableSqlState } from './chunk-JZUR7ZWE.js';
|
|
10
|
+
import { createSqlSchema, requireSqlSchema, DEFAULT_SQL_SCHEMA, recordedReadSchemaFor, requireExternalRecordedReadSource, createRecordedReadBinding, vectorSlotKey, recordedReadSqlSchema, requireRecordedReadBinding, ALIAS_CTE_PREFIX, EMBEDDINGS_CTE_ALIAS, FULLTEXT_CTE_ALIAS, HYBRID_CANDIDATES_CTE_ALIAS } from './chunk-MSY7FSX4.js';
|
|
11
|
+
import { runOptionallyInTransaction, createTransactionReadBackend, createBackendOverlay, rowPropsToObject, INTERNAL_TEMPORARY_WRITES } from './chunk-AKLS3BYZ.js';
|
|
12
|
+
import { isKnownKind } from './chunk-775A4ZMN.js';
|
|
13
|
+
import { validateEdgeEndpoints } from './chunk-WDGA7IYB.js';
|
|
14
|
+
import { resolveReadCoordinate, withRecordedCoordinate, describeCoordinate, coordinateContext, nowIso, recordedInstantWallTime, asRecordedInstant, validateOptionalCanonicalIsoDate, assertOrderedValidityWindow, assertWritableValidityWindow, assertValidRecordedInstant, optionalRecordedInstantParts, isCanonicalIsoDate, statedBoundMatchesStored } from './chunk-DLNROMQ7.js';
|
|
15
|
+
import { generateIndexDDL } from './chunk-DCQVHAYQ.js';
|
|
16
|
+
import { SYSTEM_INDEX_DECLARATIONS, resolveSystemIndexTableName, systemIndexName, resolveSystemIndexNames, generateSystemIndexDDL } from './chunk-CBTIUB76.js';
|
|
17
|
+
import { NODE_META_KEYS, EDGE_META_KEYS } from './chunk-I5MOXA4H.js';
|
|
19
18
|
import { sha256Hex, fnv1aBase36 } from './chunk-K34OPSJK.js';
|
|
20
19
|
import { DEFAULT_PAGINATION_LIMIT, DEFAULT_STREAM_BATCH_SIZE, SQLITE_PATH_DELIMITER, PG_ARRAY_START, PG_ARRAY_END, PG_PATH_ELEMENT_SEPARATOR, MAX_PG_IDENTIFIER_LENGTH } from './chunk-2EBMPJBO.js';
|
|
21
|
-
import { getDialect, assertVectorMinScore, vectorScoreExpression, vectorMinScoreCondition } from './chunk-
|
|
22
|
-
import { isSqlPlaceholder, bindSqlValue, markAnnIndexScan, asCompiledSelectSql, isRawExecutable, renderSql, annIndexScanTypes, sql, asCompiledRowsSql, markForceCustomPlan,
|
|
23
|
-
import { createSchemaIntrospector } from './chunk-
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import { validateProjectionField } from './chunk-
|
|
28
|
-
import { ConfigurationError, UnsupportedPredicateError, ValidationError, KindNotFoundError, EndpointError, EagerMaterializationError, CompilerInvariantError, CardinalityError, EdgeNotFoundError,
|
|
29
|
-
import { jsonPointer, parseJsonPointer, resolveJsonPointer } from './chunk-
|
|
20
|
+
import { getDialect, assertVectorMinScore, assertApproximateMetricSupported, vectorScoreExpression, vectorMinScoreCondition } from './chunk-YDFDBE6D.js';
|
|
21
|
+
import { isSqlPlaceholder, bindSqlValue, markAnnIndexScan, asCompiledSelectSql, isRawExecutable, renderSql, annIndexScanTypes, sql, asCompiledRowsSql, markForceCustomPlan, asCompiledStatementSql, asCompiledTemporaryStatementSql } from './chunk-PQBQG7IM.js';
|
|
22
|
+
import { createSchemaIntrospector } from './chunk-AMKPPFUO.js';
|
|
23
|
+
import { resolveEmbeddingFields, resolveGraphVectorSlots, resolveDeclaredFulltextLanguage, getSearchableFields } from './chunk-PC2UBAUB.js';
|
|
24
|
+
import { IncompatibleChangeError } from './chunk-T4XNKYXB.js';
|
|
25
|
+
import { META_EDGE_DISJOINT_WITH, META_EDGE_EQUIVALENT_TO, META_EDGE_SAME_AS, META_EDGE_SUB_CLASS_OF } from './chunk-JXXDZTMN.js';
|
|
26
|
+
import { validateProjectionField } from './chunk-K5ELX2GT.js';
|
|
27
|
+
import { ConfigurationError, UnsupportedPredicateError, ValidationError, KindNotFoundError, EndpointError, EagerMaterializationError, ContributionRebuildUnsupportedError, CompilerInvariantError, CardinalityError, DatabaseOperationError, EdgeNotFoundError, MigrationError, EDGE_IDENTITY_MISMATCH_CODE, UnsupportedBackendCapabilityError, EndpointNotFoundError, GraphAlgorithmConvergenceError, InvalidEdgeWeightError } from './chunk-J3WZ3FA2.js';
|
|
28
|
+
import { jsonPointer, parseJsonPointer, resolveJsonPointer } from './chunk-FWJKWH6U.js';
|
|
29
|
+
import { readOwnProperty, requireDefined, createDataKeyedBag, isInteropProbeKey, hasOwnKey, isPresent } from './chunk-DYATYRY4.js';
|
|
30
30
|
|
|
31
31
|
// src/store/collection-surface.ts
|
|
32
32
|
var CURRENT_ONLY_READ_NAMES = [
|
|
@@ -38,6 +38,7 @@ var NODE_WRITE_NAMES = [
|
|
|
38
38
|
"create",
|
|
39
39
|
"createFromRecord",
|
|
40
40
|
"update",
|
|
41
|
+
"updateWhere",
|
|
41
42
|
"delete",
|
|
42
43
|
"hardDelete",
|
|
43
44
|
"upsertById",
|
|
@@ -410,6 +411,10 @@ var CoordinatePinnedView = class {
|
|
|
410
411
|
constructor(store, coordinate) {
|
|
411
412
|
this.store = store;
|
|
412
413
|
this.coordinate = coordinate;
|
|
414
|
+
Object.defineProperty(this, "identity", {
|
|
415
|
+
enumerable: false,
|
|
416
|
+
get: () => storeRuntime(this.store).identityAtCoordinate(this.coordinate)
|
|
417
|
+
});
|
|
413
418
|
}
|
|
414
419
|
/** The temporal mode this view reads in. */
|
|
415
420
|
get mode() {
|
|
@@ -531,7 +536,7 @@ var CoordinatePinnedView = class {
|
|
|
531
536
|
});
|
|
532
537
|
}
|
|
533
538
|
};
|
|
534
|
-
var
|
|
539
|
+
var StoreViewImplementation = class extends CoordinatePinnedView {
|
|
535
540
|
#nodes;
|
|
536
541
|
#edges;
|
|
537
542
|
#search;
|
|
@@ -570,6 +575,13 @@ var StoreView = class extends CoordinatePinnedView {
|
|
|
570
575
|
);
|
|
571
576
|
return this.#edges;
|
|
572
577
|
}
|
|
578
|
+
/** Heterogeneous multi-kind edge read pinned to this view's coordinate. */
|
|
579
|
+
bulkFindEdgesFrom(params, options) {
|
|
580
|
+
return this.store.bulkFindEdgesFrom(params, {
|
|
581
|
+
...options,
|
|
582
|
+
...withValidCoordinate(this.coordinate)
|
|
583
|
+
});
|
|
584
|
+
}
|
|
573
585
|
/**
|
|
574
586
|
* Read-only search facade. On a `current` view the read methods
|
|
575
587
|
* (`fulltext` / `vector` / `hybrid`) delegate to the live `store.search`,
|
|
@@ -583,7 +595,8 @@ var StoreView = class extends CoordinatePinnedView {
|
|
|
583
595
|
return this.#search;
|
|
584
596
|
}
|
|
585
597
|
};
|
|
586
|
-
var
|
|
598
|
+
var StoreView = StoreViewImplementation;
|
|
599
|
+
var RecordedStoreViewImplementation = class extends CoordinatePinnedView {
|
|
587
600
|
#nodes;
|
|
588
601
|
#edges;
|
|
589
602
|
constructor(store, coordinate) {
|
|
@@ -642,6 +655,48 @@ var RecordedStoreView = class extends CoordinatePinnedView {
|
|
|
642
655
|
return this.#edges;
|
|
643
656
|
}
|
|
644
657
|
};
|
|
658
|
+
var RecordedStoreView = RecordedStoreViewImplementation;
|
|
659
|
+
|
|
660
|
+
// src/utils/path.ts
|
|
661
|
+
function parseSqlitePath(path) {
|
|
662
|
+
const emptyPath = `${SQLITE_PATH_DELIMITER}${SQLITE_PATH_DELIMITER}`;
|
|
663
|
+
if (!path || path === emptyPath) return [];
|
|
664
|
+
const trimmed = path.slice(1, -1);
|
|
665
|
+
if (trimmed === "") return [];
|
|
666
|
+
return trimmed.split(SQLITE_PATH_DELIMITER);
|
|
667
|
+
}
|
|
668
|
+
function isSqlitePath(value) {
|
|
669
|
+
return typeof value === "string" && value.startsWith(SQLITE_PATH_DELIMITER) && value.endsWith(SQLITE_PATH_DELIMITER);
|
|
670
|
+
}
|
|
671
|
+
function normalizePath(value) {
|
|
672
|
+
if (Array.isArray(value)) {
|
|
673
|
+
return value;
|
|
674
|
+
}
|
|
675
|
+
if (isSqlitePath(value)) {
|
|
676
|
+
return parseSqlitePath(value);
|
|
677
|
+
}
|
|
678
|
+
if (isPostgresTextArray(value)) {
|
|
679
|
+
return parsePostgresTextArray(value);
|
|
680
|
+
}
|
|
681
|
+
return [];
|
|
682
|
+
}
|
|
683
|
+
var IDENTITY_PATH_TOKEN_SEPARATOR = "";
|
|
684
|
+
function stripIdentityPathTokens(path) {
|
|
685
|
+
return path.map((token) => stripIdentityPathToken(token));
|
|
686
|
+
}
|
|
687
|
+
function stripIdentityPathToken(token) {
|
|
688
|
+
const separatorIndex = token.indexOf(IDENTITY_PATH_TOKEN_SEPARATOR);
|
|
689
|
+
if (separatorIndex === -1) return token;
|
|
690
|
+
return token.slice(separatorIndex + IDENTITY_PATH_TOKEN_SEPARATOR.length);
|
|
691
|
+
}
|
|
692
|
+
function isPostgresTextArray(value) {
|
|
693
|
+
return typeof value === "string" && value.startsWith(PG_ARRAY_START) && value.endsWith(PG_ARRAY_END);
|
|
694
|
+
}
|
|
695
|
+
function parsePostgresTextArray(value) {
|
|
696
|
+
const inner = value.slice(1, -1);
|
|
697
|
+
if (inner === "") return [];
|
|
698
|
+
return inner.split(PG_PATH_ELEMENT_SEPARATOR);
|
|
699
|
+
}
|
|
645
700
|
|
|
646
701
|
// src/query/compiler/emitter/plan-inspector.ts
|
|
647
702
|
function collectPlanOperations(node, ops) {
|
|
@@ -857,9 +912,13 @@ function assertRecursiveEmitterClauseAlignment(logicalPlan, input) {
|
|
|
857
912
|
}
|
|
858
913
|
function emitRecursiveQuerySql(input) {
|
|
859
914
|
assertRecursiveEmitterClauseAlignment(input.logicalPlan, input);
|
|
915
|
+
const cteList = sql.join(
|
|
916
|
+
[...input.precedingCtes ?? [], input.recursiveCte],
|
|
917
|
+
sql`, `
|
|
918
|
+
);
|
|
860
919
|
const parts = [
|
|
861
920
|
sql`WITH RECURSIVE`,
|
|
862
|
-
|
|
921
|
+
cteList,
|
|
863
922
|
sql`SELECT ${input.projection}`,
|
|
864
923
|
sql`FROM recursive_cte`,
|
|
865
924
|
input.depthFilter
|
|
@@ -1015,6 +1074,118 @@ function mergeEdgeKinds(traversal) {
|
|
|
1015
1074
|
];
|
|
1016
1075
|
}
|
|
1017
1076
|
|
|
1077
|
+
// src/query/compiler/identity-traversal.ts
|
|
1078
|
+
var IDENTITY_CLASS_CTE_ALIAS = "identity_peer_class";
|
|
1079
|
+
var PEER_CLASS_ALIAS = "identity_peer";
|
|
1080
|
+
var CURRENT_SEED_CLASS_ALIAS = "identity_seed_class";
|
|
1081
|
+
var CURRENT_MEMBER_NODE_ALIAS = "identity_peer_node";
|
|
1082
|
+
function historicalCoordinate(input) {
|
|
1083
|
+
const { ast, temporalFilterPass } = input;
|
|
1084
|
+
const recorded = optionalRecordedInstantParts(
|
|
1085
|
+
ast.recordedAsOf,
|
|
1086
|
+
"recordedAsOf"
|
|
1087
|
+
);
|
|
1088
|
+
if (recorded === void 0 && ast.temporalMode.mode === "current") {
|
|
1089
|
+
return void 0;
|
|
1090
|
+
}
|
|
1091
|
+
return {
|
|
1092
|
+
validMode: ast.temporalMode.mode,
|
|
1093
|
+
validAsOf: ast.temporalMode.asOf,
|
|
1094
|
+
recorded,
|
|
1095
|
+
currentInstant: temporalFilterPass.currentInstant
|
|
1096
|
+
};
|
|
1097
|
+
}
|
|
1098
|
+
function compileIdentityClassCte(input) {
|
|
1099
|
+
const { ast, ctx, graphId, temporalFilterPass } = input;
|
|
1100
|
+
const expandsIdentity = ast.traversals.some(
|
|
1101
|
+
(traversal) => traversal.includeIdentityMembers === true
|
|
1102
|
+
);
|
|
1103
|
+
if (!expandsIdentity) return void 0;
|
|
1104
|
+
const coordinate = historicalCoordinate({ ast, temporalFilterPass });
|
|
1105
|
+
if (coordinate === void 0) return void 0;
|
|
1106
|
+
const peerClasses = historicalIdentityPeerClassQuery({
|
|
1107
|
+
schema: ctx.schema,
|
|
1108
|
+
graphId,
|
|
1109
|
+
coordinate,
|
|
1110
|
+
sameIdAcrossKinds: ctx.identitySameIdAcrossKinds ?? "fold"
|
|
1111
|
+
});
|
|
1112
|
+
return sql`
|
|
1113
|
+
${sql.identifier(IDENTITY_CLASS_CTE_ALIAS)}(${IDENTITY_PEER_CLASS_COLUMNS}) AS MATERIALIZED (
|
|
1114
|
+
${peerClasses}
|
|
1115
|
+
)
|
|
1116
|
+
`;
|
|
1117
|
+
}
|
|
1118
|
+
function planCurrentIdentityFrontierExpansion(input) {
|
|
1119
|
+
const { ctx, graphId, previousId, previousKind, temporalFilterPass } = input;
|
|
1120
|
+
const seedClass = sql.raw(CURRENT_SEED_CLASS_ALIAS);
|
|
1121
|
+
const peer = sql.raw(PEER_CLASS_ALIAS);
|
|
1122
|
+
const memberNode = sql.raw(CURRENT_MEMBER_NODE_ALIAS);
|
|
1123
|
+
return {
|
|
1124
|
+
frontierJoin: sql`
|
|
1125
|
+
LEFT JOIN ${ctx.schema.identityClosureTable} ${seedClass}
|
|
1126
|
+
ON ${seedClass}.graph_id = ${graphId}
|
|
1127
|
+
AND ${seedClass}.member_kind = ${previousKind}
|
|
1128
|
+
AND ${seedClass}.member_id = ${previousId}
|
|
1129
|
+
LEFT JOIN ${ctx.schema.identityClosureTable} ${peer}
|
|
1130
|
+
ON ${peer}.graph_id = ${seedClass}.graph_id
|
|
1131
|
+
AND ${peer}.class_kind = ${seedClass}.class_kind
|
|
1132
|
+
AND ${peer}.class_id = ${seedClass}.class_id
|
|
1133
|
+
LEFT JOIN ${ctx.schema.nodesTable} ${memberNode}
|
|
1134
|
+
ON ${memberNode}.graph_id = ${peer}.graph_id
|
|
1135
|
+
AND ${memberNode}.kind = ${peer}.member_kind
|
|
1136
|
+
AND ${memberNode}.id = ${peer}.member_id
|
|
1137
|
+
AND ${temporalFilterPass.forAlias(CURRENT_MEMBER_NODE_ALIAS)}
|
|
1138
|
+
`,
|
|
1139
|
+
memberId: sql`COALESCE(${peer}.member_id, ${previousId})`,
|
|
1140
|
+
memberKind: sql`COALESCE(${peer}.member_kind, ${previousKind})`,
|
|
1141
|
+
whereClauses: [
|
|
1142
|
+
sql`(${peer}.member_id IS NULL OR ${memberNode}.id IS NOT NULL)`
|
|
1143
|
+
]
|
|
1144
|
+
};
|
|
1145
|
+
}
|
|
1146
|
+
function planHistoricalIdentityFrontierExpansion(input) {
|
|
1147
|
+
const { previousId, previousKind } = input;
|
|
1148
|
+
const peer = sql.identifier(PEER_CLASS_ALIAS);
|
|
1149
|
+
return {
|
|
1150
|
+
frontierJoin: sql`
|
|
1151
|
+
LEFT JOIN ${sql.identifier(IDENTITY_CLASS_CTE_ALIAS)} ${peer}
|
|
1152
|
+
ON ${peer}.seed_kind = ${previousKind}
|
|
1153
|
+
AND ${peer}.seed_id = ${previousId}
|
|
1154
|
+
`,
|
|
1155
|
+
memberId: sql`COALESCE(${peer}.id, ${previousId})`,
|
|
1156
|
+
memberKind: sql`COALESCE(${peer}.kind, ${previousKind})`,
|
|
1157
|
+
whereClauses: []
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
function planIdentityFrontierExpansion(input) {
|
|
1161
|
+
const { ast, ctx, graphId, previousId, previousKind, temporalFilterPass } = input;
|
|
1162
|
+
const coordinate = historicalCoordinate({ ast, temporalFilterPass });
|
|
1163
|
+
if (coordinate !== void 0) {
|
|
1164
|
+
return planHistoricalIdentityFrontierExpansion({
|
|
1165
|
+
previousId,
|
|
1166
|
+
previousKind
|
|
1167
|
+
});
|
|
1168
|
+
}
|
|
1169
|
+
return planCurrentIdentityFrontierExpansion({
|
|
1170
|
+
ctx,
|
|
1171
|
+
graphId,
|
|
1172
|
+
previousId,
|
|
1173
|
+
previousKind,
|
|
1174
|
+
temporalFilterPass
|
|
1175
|
+
});
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
// src/query/compiler/inverse-traversal-guard.ts
|
|
1179
|
+
function compileInverseTraversalDuplicateGuard(directEdgeKinds, inverseEdgeKinds, compileKindFilter3) {
|
|
1180
|
+
const overlappingKinds = inverseEdgeKinds.filter(
|
|
1181
|
+
(kind) => directEdgeKinds.includes(kind)
|
|
1182
|
+
);
|
|
1183
|
+
if (overlappingKinds.length === 0) {
|
|
1184
|
+
return void 0;
|
|
1185
|
+
}
|
|
1186
|
+
return sql`NOT (e.from_id = e.to_id AND e.from_kind = e.to_kind AND ${compileKindFilter3(overlappingKinds)})`;
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1018
1189
|
// src/query/compiler/typed-json-extract.ts
|
|
1019
1190
|
function compileTypedJsonExtract(input) {
|
|
1020
1191
|
const { column, dialect, pointer, valueType } = input;
|
|
@@ -1329,24 +1500,71 @@ function buildStandardTraversalCte(input) {
|
|
|
1329
1500
|
})
|
|
1330
1501
|
];
|
|
1331
1502
|
const cteMaterialization = materializeCte ? sql`MATERIALIZED ` : ctx.dialect.capabilities.emitNotMaterializedHint ? sql`NOT MATERIALIZED ` : sql``;
|
|
1503
|
+
const previousIdColumn = sql`cte_${sql.raw(previousAlias)}.${sql.raw(`${previousAlias}_id`)}`;
|
|
1504
|
+
const previousKindColumn = sql`cte_${sql.raw(previousAlias)}.${sql.raw(`${previousAlias}_kind`)}`;
|
|
1505
|
+
const identityFrontierExpansion = traversal.includeIdentityMembers === true ? planIdentityFrontierExpansion({
|
|
1506
|
+
ast,
|
|
1507
|
+
ctx,
|
|
1508
|
+
graphId,
|
|
1509
|
+
previousId: previousIdColumn,
|
|
1510
|
+
previousKind: previousKindColumn,
|
|
1511
|
+
temporalFilterPass
|
|
1512
|
+
}) : void 0;
|
|
1513
|
+
const pinFrontierAheadOfEdges = identityFrontierExpansion !== void 0 && ctx.dialect.capabilities.forceRecursiveWorktableOuterJoinOrder;
|
|
1514
|
+
function compileSourceJoin(branch) {
|
|
1515
|
+
const edgeId = sql`e.${sql.raw(branch.joinField)}`;
|
|
1516
|
+
const edgeKind = sql`e.${sql.raw(branch.joinKindField)}`;
|
|
1517
|
+
if (identityFrontierExpansion !== void 0) {
|
|
1518
|
+
return sql`
|
|
1519
|
+
${identityFrontierExpansion.memberId} = ${edgeId}
|
|
1520
|
+
AND ${identityFrontierExpansion.memberKind} = ${edgeKind}
|
|
1521
|
+
`;
|
|
1522
|
+
}
|
|
1523
|
+
return sql`
|
|
1524
|
+
${previousIdColumn} = ${edgeId}
|
|
1525
|
+
AND ${previousKindColumn} = ${edgeKind}
|
|
1526
|
+
`;
|
|
1527
|
+
}
|
|
1332
1528
|
function compileTraversalBranch(branch) {
|
|
1333
1529
|
const whereClauses = [
|
|
1334
1530
|
...baseWhereClauses,
|
|
1531
|
+
// Conditions the frontier widening cannot state in a join condition —
|
|
1532
|
+
// currently the member-visibility guard. Every branch of the step carries
|
|
1533
|
+
// them, because every branch reads the widened frontier.
|
|
1534
|
+
...identityFrontierExpansion?.whereClauses ?? [],
|
|
1335
1535
|
compileKindFilter(sql.raw("e.kind"), branch.edgeKinds),
|
|
1336
|
-
compileKindFilter(
|
|
1337
|
-
sql.raw(`e.${branch.joinKindField}`),
|
|
1338
|
-
previousNodeKinds
|
|
1339
|
-
),
|
|
1340
1536
|
compileKindFilter(sql.raw(`e.${branch.targetKindField}`), nodeKinds)
|
|
1341
1537
|
];
|
|
1538
|
+
if (traversal.includeIdentityMembers !== true) {
|
|
1539
|
+
whereClauses.push(
|
|
1540
|
+
compileKindFilter(
|
|
1541
|
+
sql.raw(`e.${branch.joinKindField}`),
|
|
1542
|
+
previousNodeKinds
|
|
1543
|
+
)
|
|
1544
|
+
);
|
|
1545
|
+
}
|
|
1342
1546
|
if (branch.duplicateGuard !== void 0) {
|
|
1343
1547
|
whereClauses.push(branch.duplicateGuard);
|
|
1344
1548
|
}
|
|
1549
|
+
const sourceJoin = compileSourceJoin(branch);
|
|
1550
|
+
const frontierJoin = identityFrontierExpansion?.frontierJoin ?? sql``;
|
|
1551
|
+
if (pinFrontierAheadOfEdges) {
|
|
1552
|
+
return sql`
|
|
1553
|
+
SELECT ${sql.join(selectColumns, sql`, `)}
|
|
1554
|
+
FROM cte_${sql.raw(previousAlias)}
|
|
1555
|
+
${frontierJoin}
|
|
1556
|
+
CROSS JOIN ${ctx.schema.edgesTable} e
|
|
1557
|
+
JOIN ${ctx.schema.nodesTable} n ON n.graph_id = e.graph_id
|
|
1558
|
+
AND n.id = e.${sql.raw(branch.targetField)}
|
|
1559
|
+
AND n.kind = e.${sql.raw(branch.targetKindField)}
|
|
1560
|
+
WHERE ${sql.join([sourceJoin, ...whereClauses], sql` AND `)}
|
|
1561
|
+
`;
|
|
1562
|
+
}
|
|
1345
1563
|
return sql`
|
|
1346
1564
|
SELECT ${sql.join(selectColumns, sql`, `)}
|
|
1347
1565
|
FROM cte_${sql.raw(previousAlias)}
|
|
1348
|
-
|
|
1349
|
-
|
|
1566
|
+
${frontierJoin}
|
|
1567
|
+
JOIN ${ctx.schema.edgesTable} e ON ${sourceJoin}
|
|
1350
1568
|
JOIN ${ctx.schema.nodesTable} n ON n.graph_id = e.graph_id
|
|
1351
1569
|
AND n.id = e.${sql.raw(branch.targetField)}
|
|
1352
1570
|
AND n.kind = e.${sql.raw(branch.targetKindField)}
|
|
@@ -1385,13 +1603,11 @@ function buildStandardTraversalCte(input) {
|
|
|
1385
1603
|
const inverseTargetField = traversal.direction === "out" ? "from_id" : "to_id";
|
|
1386
1604
|
const inverseJoinKindField = traversal.direction === "out" ? "to_kind" : "from_kind";
|
|
1387
1605
|
const inverseTargetKindField = traversal.direction === "out" ? "from_kind" : "to_kind";
|
|
1388
|
-
const
|
|
1389
|
-
|
|
1606
|
+
const duplicateGuard = compileInverseTraversalDuplicateGuard(
|
|
1607
|
+
directEdgeKinds,
|
|
1608
|
+
inverseEdgeKinds,
|
|
1609
|
+
(overlappingKinds) => compileKindFilter(sql.raw("e.kind"), overlappingKinds)
|
|
1390
1610
|
);
|
|
1391
|
-
const duplicateGuard = overlappingKinds.length > 0 ? sql`NOT (e.from_id = e.to_id AND ${compileKindFilter(
|
|
1392
|
-
sql.raw("e.kind"),
|
|
1393
|
-
overlappingKinds
|
|
1394
|
-
)})` : void 0;
|
|
1395
1611
|
const inverseBranch = compileTraversalBranch({
|
|
1396
1612
|
duplicateGuard,
|
|
1397
1613
|
edgeKinds: inverseEdgeKinds,
|
|
@@ -1795,6 +2011,16 @@ function buildStandardEmbeddingsCte(input) {
|
|
|
1795
2011
|
const tableName = vectorStrategy.tableName(graphId, kind, fieldPath);
|
|
1796
2012
|
const slotDescriptor = vectorSlots?.get(vectorSlotKey(kind, fieldPath));
|
|
1797
2013
|
const branchMetric = metric ?? slotDescriptor?.metric ?? "cosine";
|
|
2014
|
+
if (slotDescriptor !== void 0) {
|
|
2015
|
+
assertApproximateMetricSupported({
|
|
2016
|
+
approximate: vectorPredicate.approximate,
|
|
2017
|
+
requestedMetric: metric,
|
|
2018
|
+
declaredMetric: slotDescriptor.metric,
|
|
2019
|
+
indexType: slotDescriptor.indexType,
|
|
2020
|
+
nodeKind: kind,
|
|
2021
|
+
fieldPath
|
|
2022
|
+
});
|
|
2023
|
+
}
|
|
1798
2024
|
const engineFormEligible = vectorPredicate.approximate === true || vectorStrategy.searchIsExact === true;
|
|
1799
2025
|
const annSlot = engineFormEligible && branchMetric === slotDescriptor?.metric ? slotDescriptor : void 0;
|
|
1800
2026
|
if (annSlot !== void 0) {
|
|
@@ -2144,7 +2370,8 @@ function createTemporalFilterPass(ast, readInstant = "literal") {
|
|
|
2144
2370
|
...extractTemporalOptions(ast, tableAlias),
|
|
2145
2371
|
currentTimestamp
|
|
2146
2372
|
});
|
|
2147
|
-
}
|
|
2373
|
+
},
|
|
2374
|
+
currentInstant: currentTimestamp
|
|
2148
2375
|
};
|
|
2149
2376
|
}
|
|
2150
2377
|
|
|
@@ -2592,6 +2819,12 @@ function throwRecordedCurrentIndexError(recordedAsOf, usage) {
|
|
|
2592
2819
|
var MAX_RECURSIVE_DEPTH = 10;
|
|
2593
2820
|
var MAX_EXPLICIT_RECURSIVE_DEPTH = 1e3;
|
|
2594
2821
|
var NO_ALWAYS_REQUIRED_COLUMNS = /* @__PURE__ */ new Set();
|
|
2822
|
+
function compilePathToken(tableAlias, composite) {
|
|
2823
|
+
if (!composite) {
|
|
2824
|
+
return sql.raw(`${tableAlias}.id`);
|
|
2825
|
+
}
|
|
2826
|
+
return sql`(${sql.raw(`${tableAlias}.kind`)} || ${IDENTITY_PATH_TOKEN_SEPARATOR} || ${sql.raw(`${tableAlias}.id`)})`;
|
|
2827
|
+
}
|
|
2595
2828
|
function runRecursiveQueryPassPipeline(ast, graphId, ctx) {
|
|
2596
2829
|
let state = {
|
|
2597
2830
|
ast,
|
|
@@ -2712,6 +2945,12 @@ function compileVariableLengthQueryWithRecursiveCteStrategy(ast, graphId, ctx) {
|
|
|
2712
2945
|
requiredColumnsByAlias,
|
|
2713
2946
|
temporalFilterPass
|
|
2714
2947
|
);
|
|
2948
|
+
const identityClassCte = compileIdentityClassCte({
|
|
2949
|
+
ast,
|
|
2950
|
+
ctx,
|
|
2951
|
+
graphId,
|
|
2952
|
+
temporalFilterPass
|
|
2953
|
+
});
|
|
2715
2954
|
const projection = compileRecursiveProjection(ast, vlTraversal, dialect);
|
|
2716
2955
|
const minDepth = vlTraversal.variableLength.minDepth;
|
|
2717
2956
|
const depthFilter = minDepth > 0 ? sql`WHERE depth >= ${minDepth}` : sql.raw("");
|
|
@@ -2722,6 +2961,7 @@ function compileVariableLengthQueryWithRecursiveCteStrategy(ast, graphId, ctx) {
|
|
|
2722
2961
|
...limitOffset === void 0 ? {} : { limitOffset },
|
|
2723
2962
|
logicalPlan,
|
|
2724
2963
|
...orderBy === void 0 ? {} : { orderBy },
|
|
2964
|
+
...identityClassCte === void 0 ? {} : { precedingCtes: [identityClassCte] },
|
|
2725
2965
|
projection,
|
|
2726
2966
|
recursiveCte
|
|
2727
2967
|
});
|
|
@@ -2744,7 +2984,7 @@ function compileRecursiveCte(ast, traversal, graphId, ctx, requiredColumnsByAlia
|
|
|
2744
2984
|
const shouldEnforceCycleCheck = vl.cyclePolicy !== "allow";
|
|
2745
2985
|
const shouldTrackPath = shouldEnforceCycleCheck || vl.pathAlias !== void 0;
|
|
2746
2986
|
const recursiveJoinRequiredColumns = /* @__PURE__ */ new Set(["id"]);
|
|
2747
|
-
if (previousNodeKinds.length > 1) {
|
|
2987
|
+
if (previousNodeKinds.length > 1 || traversal.includeIdentityMembers === true) {
|
|
2748
2988
|
recursiveJoinRequiredColumns.add("kind");
|
|
2749
2989
|
}
|
|
2750
2990
|
const requiredStartColumns = requiredColumnsByAlias ? requiredColumnsByAlias.get(startAlias) ?? EMPTY_REQUIRED_COLUMNS : void 0;
|
|
@@ -2798,33 +3038,74 @@ function compileRecursiveCte(ast, traversal, graphId, ctx, requiredColumnsByAlia
|
|
|
2798
3038
|
}
|
|
2799
3039
|
const effectiveMaxDepth = vl.maxDepth > 0 ? vl.maxDepth : MAX_RECURSIVE_DEPTH;
|
|
2800
3040
|
const maxDepthCondition = sql`r.depth < ${effectiveMaxDepth}`;
|
|
2801
|
-
const
|
|
2802
|
-
|
|
2803
|
-
|
|
3041
|
+
const baseToken = compilePathToken(
|
|
3042
|
+
"n0",
|
|
3043
|
+
traversal.includeIdentityMembers === true
|
|
3044
|
+
);
|
|
3045
|
+
const stepToken = compilePathToken(
|
|
3046
|
+
"n",
|
|
3047
|
+
traversal.includeIdentityMembers === true
|
|
3048
|
+
);
|
|
3049
|
+
const cycleCheck = shouldEnforceCycleCheck ? dialect.cycleCheck(stepToken, sql.raw("r.path")) : void 0;
|
|
3050
|
+
const initialPath = shouldTrackPath ? dialect.initializePath(baseToken) : void 0;
|
|
3051
|
+
const pathExtension = shouldTrackPath ? dialect.extendPath(sql.raw("r.path"), stepToken) : void 0;
|
|
2804
3052
|
const baseWhereClauses = [
|
|
2805
3053
|
sql`n0.graph_id = ${graphId}`,
|
|
2806
3054
|
startKindFilter,
|
|
2807
3055
|
startTemporalFilter,
|
|
2808
3056
|
...startPredicates
|
|
2809
3057
|
];
|
|
3058
|
+
const previousIdColumn = sql`r.${sql.raw(nodeAlias)}_id`;
|
|
3059
|
+
const previousKindColumn = sql`r.${sql.raw(nodeAlias)}_kind`;
|
|
3060
|
+
const identityFrontierExpansion = traversal.includeIdentityMembers === true ? planIdentityFrontierExpansion({
|
|
3061
|
+
ast,
|
|
3062
|
+
ctx,
|
|
3063
|
+
graphId,
|
|
3064
|
+
previousId: previousIdColumn,
|
|
3065
|
+
previousKind: previousKindColumn,
|
|
3066
|
+
temporalFilterPass
|
|
3067
|
+
}) : void 0;
|
|
2810
3068
|
const recursiveBaseWhereClauses = [
|
|
2811
3069
|
sql`e.graph_id = ${graphId}`,
|
|
2812
3070
|
nodeKindFilter,
|
|
2813
3071
|
edgeTemporalFilter,
|
|
2814
3072
|
nodeTemporalFilter,
|
|
2815
|
-
maxDepthCondition
|
|
3073
|
+
maxDepthCondition,
|
|
3074
|
+
// Conditions the frontier widening cannot state in a join condition —
|
|
3075
|
+
// currently the member-visibility guard. Every branch of the recursive term
|
|
3076
|
+
// carries them, because every branch reads the widened frontier.
|
|
3077
|
+
...identityFrontierExpansion?.whereClauses ?? []
|
|
2816
3078
|
];
|
|
2817
3079
|
if (cycleCheck !== void 0) {
|
|
2818
3080
|
recursiveBaseWhereClauses.push(cycleCheck);
|
|
2819
3081
|
}
|
|
2820
3082
|
recursiveBaseWhereClauses.push(...edgePredicates, ...targetNodePredicates);
|
|
3083
|
+
function compileWorktableJoinClauses(branch) {
|
|
3084
|
+
const edgeId = sql`e.${sql.raw(branch.joinField)}`;
|
|
3085
|
+
const edgeKind = sql`e.${sql.raw(branch.joinKindField)}`;
|
|
3086
|
+
if (identityFrontierExpansion !== void 0) {
|
|
3087
|
+
return [
|
|
3088
|
+
sql`${edgeId} = ${identityFrontierExpansion.memberId}`,
|
|
3089
|
+
sql`${edgeKind} = ${identityFrontierExpansion.memberKind}`
|
|
3090
|
+
];
|
|
3091
|
+
}
|
|
3092
|
+
const clauses = [sql`${edgeId} = ${previousIdColumn}`];
|
|
3093
|
+
if (previousNodeKinds.length > 1) {
|
|
3094
|
+
clauses.push(sql`${edgeKind} = ${previousKindColumn}`);
|
|
3095
|
+
}
|
|
3096
|
+
return clauses;
|
|
3097
|
+
}
|
|
2821
3098
|
function compileRecursiveBranch2(branch) {
|
|
2822
3099
|
const recursiveFilterClauses = [
|
|
2823
3100
|
...recursiveBaseWhereClauses,
|
|
2824
3101
|
compileKindFilter2(branch.edgeKinds, "e.kind"),
|
|
2825
|
-
compileKindFilter2(previousNodeKinds, `e.${branch.joinKindField}`),
|
|
2826
3102
|
compileKindFilter2(nodeKinds, `e.${branch.targetKindField}`)
|
|
2827
3103
|
];
|
|
3104
|
+
if (traversal.includeIdentityMembers !== true) {
|
|
3105
|
+
recursiveFilterClauses.push(
|
|
3106
|
+
compileKindFilter2(previousNodeKinds, `e.${branch.joinKindField}`)
|
|
3107
|
+
);
|
|
3108
|
+
}
|
|
2828
3109
|
if (branch.duplicateGuard !== void 0) {
|
|
2829
3110
|
recursiveFilterClauses.push(branch.duplicateGuard);
|
|
2830
3111
|
}
|
|
@@ -2836,14 +3117,8 @@ function compileRecursiveCte(ast, traversal, graphId, ctx, requiredColumnsByAlia
|
|
|
2836
3117
|
if (pathExtension !== void 0) {
|
|
2837
3118
|
recursiveSelectColumns.push(sql`${pathExtension} AS path`);
|
|
2838
3119
|
}
|
|
2839
|
-
const recursiveJoinClauses =
|
|
2840
|
-
|
|
2841
|
-
];
|
|
2842
|
-
if (previousNodeKinds.length > 1) {
|
|
2843
|
-
recursiveJoinClauses.push(
|
|
2844
|
-
sql`e.${sql.raw(branch.joinKindField)} = r.${sql.raw(nodeAlias)}_kind`
|
|
2845
|
-
);
|
|
2846
|
-
}
|
|
3120
|
+
const recursiveJoinClauses = compileWorktableJoinClauses(branch);
|
|
3121
|
+
const frontierJoin = identityFrontierExpansion?.frontierJoin ?? sql``;
|
|
2847
3122
|
if (forceWorktableOuterJoinOrder) {
|
|
2848
3123
|
const recursiveWhereClauses = [
|
|
2849
3124
|
...recursiveJoinClauses,
|
|
@@ -2852,6 +3127,7 @@ function compileRecursiveCte(ast, traversal, graphId, ctx, requiredColumnsByAlia
|
|
|
2852
3127
|
return sql`
|
|
2853
3128
|
SELECT ${sql.join(recursiveSelectColumns, sql`, `)}
|
|
2854
3129
|
FROM recursive_cte r
|
|
3130
|
+
${frontierJoin}
|
|
2855
3131
|
CROSS JOIN ${ctx.schema.edgesTable} e
|
|
2856
3132
|
JOIN ${ctx.schema.nodesTable} n ON n.graph_id = e.graph_id
|
|
2857
3133
|
AND n.id = e.${sql.raw(branch.targetField)}
|
|
@@ -2862,6 +3138,7 @@ function compileRecursiveCte(ast, traversal, graphId, ctx, requiredColumnsByAlia
|
|
|
2862
3138
|
return sql`
|
|
2863
3139
|
SELECT ${sql.join(recursiveSelectColumns, sql`, `)}
|
|
2864
3140
|
FROM recursive_cte r
|
|
3141
|
+
${frontierJoin}
|
|
2865
3142
|
JOIN ${ctx.schema.edgesTable} e ON ${sql.join(recursiveJoinClauses, sql` AND `)}
|
|
2866
3143
|
JOIN ${ctx.schema.nodesTable} n ON n.graph_id = e.graph_id
|
|
2867
3144
|
AND n.id = e.${sql.raw(branch.targetField)}
|
|
@@ -2885,10 +3162,11 @@ function compileRecursiveCte(ast, traversal, graphId, ctx, requiredColumnsByAlia
|
|
|
2885
3162
|
const inverseTargetField = direction === "out" ? "from_id" : "to_id";
|
|
2886
3163
|
const inverseJoinKindField = direction === "out" ? "to_kind" : "from_kind";
|
|
2887
3164
|
const inverseTargetKindField = direction === "out" ? "from_kind" : "to_kind";
|
|
2888
|
-
const
|
|
2889
|
-
|
|
3165
|
+
const duplicateGuard = compileInverseTraversalDuplicateGuard(
|
|
3166
|
+
directEdgeKinds,
|
|
3167
|
+
inverseEdgeKinds,
|
|
3168
|
+
(overlappingKinds) => compileKindFilter2(overlappingKinds, "e.kind")
|
|
2890
3169
|
);
|
|
2891
|
-
const duplicateGuard = overlappingKinds.length > 0 ? sql`NOT (e.from_id = e.to_id AND ${compileKindFilter2(overlappingKinds, "e.kind")})` : void 0;
|
|
2892
3170
|
const inverseBranch = compileRecursiveBranch2({
|
|
2893
3171
|
joinField: inverseJoinField,
|
|
2894
3172
|
targetField: inverseTargetField,
|
|
@@ -3411,7 +3689,7 @@ function buildProjectionPlan(kinds, projectionMap, resolveFieldType, entityPrefi
|
|
|
3411
3689
|
const projectedKinds = /* @__PURE__ */ new Map();
|
|
3412
3690
|
const fullKinds = [];
|
|
3413
3691
|
for (const kind of kinds) {
|
|
3414
|
-
const selection = projectionMap
|
|
3692
|
+
const selection = projectionMap !== void 0 && hasOwnKey(projectionMap, kind) ? projectionMap[kind] : void 0;
|
|
3415
3693
|
if (selection === void 0) {
|
|
3416
3694
|
fullKinds.push(kind);
|
|
3417
3695
|
continue;
|
|
@@ -4233,6 +4511,7 @@ function compileQuery(ast, graphId, options = "sqlite") {
|
|
|
4233
4511
|
schema,
|
|
4234
4512
|
annIndexTypes,
|
|
4235
4513
|
readInstant: options_.readInstant ?? "literal",
|
|
4514
|
+
identitySameIdAcrossKinds: options_.identitySameIdAcrossKinds ?? "fold",
|
|
4236
4515
|
compileQuery: (subAst, subGraphId) => compileQuery(
|
|
4237
4516
|
inheritRecordedAsOf(subAst, ast.recordedAsOf),
|
|
4238
4517
|
subGraphId,
|
|
@@ -4342,7 +4621,10 @@ function propagateOptions(options_) {
|
|
|
4342
4621
|
...options_.vectorSlots === void 0 ? {} : { vectorSlots: options_.vectorSlots },
|
|
4343
4622
|
...options_.fulltextLanguages === void 0 ? {} : { fulltextLanguages: options_.fulltextLanguages },
|
|
4344
4623
|
...options_.recordedReadBinding === void 0 ? {} : { recordedReadBinding: options_.recordedReadBinding },
|
|
4345
|
-
...options_.readInstant === void 0 ? {} : { readInstant: options_.readInstant }
|
|
4624
|
+
...options_.readInstant === void 0 ? {} : { readInstant: options_.readInstant },
|
|
4625
|
+
...options_.identitySameIdAcrossKinds === void 0 ? {} : {
|
|
4626
|
+
identitySameIdAcrossKinds: options_.identitySameIdAcrossKinds
|
|
4627
|
+
}
|
|
4346
4628
|
};
|
|
4347
4629
|
}
|
|
4348
4630
|
function resolveCountAggregateFastPath(ast) {
|
|
@@ -4410,6 +4692,11 @@ function resolveCountAggregateFastPath(ast) {
|
|
|
4410
4692
|
};
|
|
4411
4693
|
}
|
|
4412
4694
|
function compileCountAggregateFastPath(ast, graphId, ctx, logicalPlan, requiredColumnsByAlias, predicateIndex, temporalFilterPass) {
|
|
4695
|
+
if (ast.traversals.some(
|
|
4696
|
+
(traversal2) => traversal2.includeIdentityMembers === true
|
|
4697
|
+
)) {
|
|
4698
|
+
return void 0;
|
|
4699
|
+
}
|
|
4413
4700
|
const plan = resolveCountAggregateFastPath(ast);
|
|
4414
4701
|
if (!plan) {
|
|
4415
4702
|
return void 0;
|
|
@@ -4601,7 +4888,14 @@ function buildStandardStartAndTraversalCtes(input) {
|
|
|
4601
4888
|
temporalFilterPass,
|
|
4602
4889
|
traversalLimit
|
|
4603
4890
|
} = input;
|
|
4891
|
+
const identityClassCte = compileIdentityClassCte({
|
|
4892
|
+
ast,
|
|
4893
|
+
ctx,
|
|
4894
|
+
graphId,
|
|
4895
|
+
temporalFilterPass
|
|
4896
|
+
});
|
|
4604
4897
|
const ctes = [
|
|
4898
|
+
...identityClassCte === void 0 ? [] : [identityClassCte],
|
|
4605
4899
|
buildStandardStartCte({
|
|
4606
4900
|
ast,
|
|
4607
4901
|
ctx,
|
|
@@ -4938,57 +5232,35 @@ function compileStandardQueryWithCteStrategy(ast, graphId, ctx) {
|
|
|
4938
5232
|
});
|
|
4939
5233
|
}
|
|
4940
5234
|
|
|
4941
|
-
// src/utils/path.ts
|
|
4942
|
-
function parseSqlitePath(path) {
|
|
4943
|
-
const emptyPath = `${SQLITE_PATH_DELIMITER}${SQLITE_PATH_DELIMITER}`;
|
|
4944
|
-
if (!path || path === emptyPath) return [];
|
|
4945
|
-
const trimmed = path.slice(1, -1);
|
|
4946
|
-
if (trimmed === "") return [];
|
|
4947
|
-
return trimmed.split(SQLITE_PATH_DELIMITER);
|
|
4948
|
-
}
|
|
4949
|
-
function isSqlitePath(value) {
|
|
4950
|
-
return typeof value === "string" && value.startsWith(SQLITE_PATH_DELIMITER) && value.endsWith(SQLITE_PATH_DELIMITER);
|
|
4951
|
-
}
|
|
4952
|
-
function normalizePath(value) {
|
|
4953
|
-
if (Array.isArray(value)) {
|
|
4954
|
-
return value;
|
|
4955
|
-
}
|
|
4956
|
-
if (isSqlitePath(value)) {
|
|
4957
|
-
return parseSqlitePath(value);
|
|
4958
|
-
}
|
|
4959
|
-
if (isPostgresTextArray(value)) {
|
|
4960
|
-
return parsePostgresTextArray(value);
|
|
4961
|
-
}
|
|
4962
|
-
return [];
|
|
4963
|
-
}
|
|
4964
|
-
function isPostgresTextArray(value) {
|
|
4965
|
-
return typeof value === "string" && value.startsWith(PG_ARRAY_START) && value.endsWith(PG_ARRAY_END);
|
|
4966
|
-
}
|
|
4967
|
-
function parsePostgresTextArray(value) {
|
|
4968
|
-
const inner = value.slice(1, -1);
|
|
4969
|
-
if (inner === "") return [];
|
|
4970
|
-
return inner.split(PG_PATH_ELEMENT_SEPARATOR);
|
|
4971
|
-
}
|
|
4972
|
-
|
|
4973
5235
|
// src/query/execution/result-mapper.ts
|
|
4974
|
-
function
|
|
4975
|
-
const
|
|
5236
|
+
function collectPathColumns(state) {
|
|
5237
|
+
const columns = [];
|
|
4976
5238
|
for (const traversal of state.traversals) {
|
|
4977
|
-
|
|
4978
|
-
|
|
5239
|
+
const pathAlias = traversal.variableLength?.pathAlias;
|
|
5240
|
+
if (pathAlias !== void 0) {
|
|
5241
|
+
columns.push({
|
|
5242
|
+
alias: pathAlias,
|
|
5243
|
+
identityExpanded: traversal.includeIdentityMembers === true
|
|
5244
|
+
});
|
|
4979
5245
|
}
|
|
4980
5246
|
}
|
|
4981
|
-
|
|
5247
|
+
return columns;
|
|
5248
|
+
}
|
|
5249
|
+
function transformPathColumns(rows, state, _dialect) {
|
|
5250
|
+
const pathColumns = collectPathColumns(state);
|
|
5251
|
+
if (pathColumns.length === 0) return rows;
|
|
4982
5252
|
const result = [];
|
|
4983
5253
|
let changed = false;
|
|
4984
5254
|
for (const row of rows) {
|
|
4985
5255
|
let transformed;
|
|
4986
|
-
for (const alias of
|
|
5256
|
+
for (const { alias, identityExpanded } of pathColumns) {
|
|
4987
5257
|
const value = row[alias];
|
|
4988
|
-
if (value
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
5258
|
+
if (value === void 0) continue;
|
|
5259
|
+
const normalized = normalizePath(value);
|
|
5260
|
+
const path = identityExpanded ? stripIdentityPathTokens(normalized) : normalized;
|
|
5261
|
+
if (path === value) continue;
|
|
5262
|
+
transformed ??= { ...row };
|
|
5263
|
+
transformed[alias] = path;
|
|
4992
5264
|
}
|
|
4993
5265
|
if (transformed === void 0) {
|
|
4994
5266
|
result.push(row);
|
|
@@ -5000,16 +5272,24 @@ function transformPathColumns(rows, state, _dialect) {
|
|
|
5000
5272
|
return changed ? result : rows;
|
|
5001
5273
|
}
|
|
5002
5274
|
var RESERVED_NODE_KEYS = /* @__PURE__ */ new Set(["id", "kind", "meta"]);
|
|
5003
|
-
var RESERVED_EDGE_KEYS = /* @__PURE__ */ new Set([
|
|
5275
|
+
var RESERVED_EDGE_KEYS = /* @__PURE__ */ new Set([
|
|
5276
|
+
"id",
|
|
5277
|
+
"kind",
|
|
5278
|
+
"fromId",
|
|
5279
|
+
"toId",
|
|
5280
|
+
"meta"
|
|
5281
|
+
]);
|
|
5004
5282
|
function nullToUndefined2(value) {
|
|
5005
5283
|
return value === null ? void 0 : value;
|
|
5006
5284
|
}
|
|
5007
|
-
function
|
|
5285
|
+
function propsExcludingReserved(props, reservedKeys) {
|
|
5286
|
+
const kept = createDataKeyedBag();
|
|
5008
5287
|
for (const [key, value] of Object.entries(props)) {
|
|
5009
5288
|
if (!reservedKeys.has(key)) {
|
|
5010
|
-
|
|
5289
|
+
kept[key] = value;
|
|
5011
5290
|
}
|
|
5012
5291
|
}
|
|
5292
|
+
return kept;
|
|
5013
5293
|
}
|
|
5014
5294
|
function buildSelectableNode(row, alias) {
|
|
5015
5295
|
const id = row[`${alias}_id`];
|
|
@@ -5036,9 +5316,9 @@ function buildSelectableNode(row, alias) {
|
|
|
5036
5316
|
createdAt,
|
|
5037
5317
|
updatedAt,
|
|
5038
5318
|
deletedAt
|
|
5039
|
-
}
|
|
5319
|
+
},
|
|
5320
|
+
...propsExcludingReserved(rawProps, RESERVED_NODE_KEYS)
|
|
5040
5321
|
};
|
|
5041
|
-
assignPropsExcludingReserved(result, rawProps, RESERVED_NODE_KEYS);
|
|
5042
5322
|
return result;
|
|
5043
5323
|
}
|
|
5044
5324
|
function buildSelectableNodeOrUndefined(row, alias) {
|
|
@@ -5078,15 +5358,14 @@ function buildSelectableEdge(row, alias) {
|
|
|
5078
5358
|
createdAt,
|
|
5079
5359
|
updatedAt,
|
|
5080
5360
|
deletedAt
|
|
5081
|
-
}
|
|
5361
|
+
},
|
|
5362
|
+
...propsExcludingReserved(rawProps, RESERVED_EDGE_KEYS)
|
|
5082
5363
|
};
|
|
5083
|
-
assignPropsExcludingReserved(result, rawProps, RESERVED_EDGE_KEYS);
|
|
5084
5364
|
return result;
|
|
5085
5365
|
}
|
|
5086
5366
|
function buildSelectContext(row, startAlias, traversals) {
|
|
5087
|
-
const context =
|
|
5088
|
-
|
|
5089
|
-
};
|
|
5367
|
+
const context = createDataKeyedBag();
|
|
5368
|
+
context[startAlias] = buildSelectableNode(row, startAlias);
|
|
5090
5369
|
for (const traversal of traversals) {
|
|
5091
5370
|
const nodeAlias = traversal.nodeAlias;
|
|
5092
5371
|
const edgeAlias = traversal.edgeAlias;
|
|
@@ -5102,7 +5381,7 @@ function buildSelectContext(row, startAlias, traversals) {
|
|
|
5102
5381
|
}
|
|
5103
5382
|
}
|
|
5104
5383
|
}
|
|
5105
|
-
return context;
|
|
5384
|
+
return { ...context };
|
|
5106
5385
|
}
|
|
5107
5386
|
function mapResults(rows, startAlias, traversals, selectFunction) {
|
|
5108
5387
|
return rows.map((row) => {
|
|
@@ -5400,14 +5679,13 @@ var FieldAccessTracker = class {
|
|
|
5400
5679
|
}
|
|
5401
5680
|
};
|
|
5402
5681
|
function createTrackingContext(state, tracker, options) {
|
|
5403
|
-
const context =
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
};
|
|
5682
|
+
const context = createDataKeyedBag();
|
|
5683
|
+
context[state.startAlias] = createNodeTrackingProxy(
|
|
5684
|
+
state.startAlias,
|
|
5685
|
+
state.startKinds,
|
|
5686
|
+
tracker,
|
|
5687
|
+
options
|
|
5688
|
+
);
|
|
5411
5689
|
for (const traversal of state.traversals) {
|
|
5412
5690
|
const edgeKindNames = mergeEdgeKinds(traversal);
|
|
5413
5691
|
const optionalAbsent = options.optionalTraversalAliases === "absent" && traversal.optional;
|
|
@@ -5433,7 +5711,7 @@ function createTrackingContext(state, tracker, options) {
|
|
|
5433
5711
|
}
|
|
5434
5712
|
}
|
|
5435
5713
|
}
|
|
5436
|
-
return context;
|
|
5714
|
+
return { ...context };
|
|
5437
5715
|
}
|
|
5438
5716
|
function createNodeTrackingProxy(alias, kindNames, tracker, options) {
|
|
5439
5717
|
return new Proxy(
|
|
@@ -5441,9 +5719,10 @@ function createNodeTrackingProxy(alias, kindNames, tracker, options) {
|
|
|
5441
5719
|
{
|
|
5442
5720
|
get: (_, property) => {
|
|
5443
5721
|
if (typeof property === "symbol") return;
|
|
5444
|
-
if (property
|
|
5445
|
-
|
|
5446
|
-
|
|
5722
|
+
if (isInteropProbeKey(property) && !options.schemaIntrospector.hasDeclaredField(kindNames, property)) {
|
|
5723
|
+
return;
|
|
5724
|
+
}
|
|
5725
|
+
if (OBJECT_PROTOTYPE_PROPERTIES.has(property) && !options.schemaIntrospector.hasDeclaredField(kindNames, property)) {
|
|
5447
5726
|
if (property === "constructor") return Object;
|
|
5448
5727
|
if (property === "__proto__") return Object.prototype;
|
|
5449
5728
|
return Reflect.get(Object.prototype, property);
|
|
@@ -5474,9 +5753,16 @@ function createEdgeTrackingProxy(alias, edgeKindNames, tracker, options) {
|
|
|
5474
5753
|
{
|
|
5475
5754
|
get: (_, property) => {
|
|
5476
5755
|
if (typeof property === "symbol") return;
|
|
5477
|
-
if (property
|
|
5478
|
-
|
|
5479
|
-
|
|
5756
|
+
if (isInteropProbeKey(property) && !options.schemaIntrospector.hasDeclaredEdgeField(
|
|
5757
|
+
edgeKindNames,
|
|
5758
|
+
property
|
|
5759
|
+
)) {
|
|
5760
|
+
return;
|
|
5761
|
+
}
|
|
5762
|
+
if (OBJECT_PROTOTYPE_PROPERTIES.has(property) && !options.schemaIntrospector.hasDeclaredEdgeField(
|
|
5763
|
+
edgeKindNames,
|
|
5764
|
+
property
|
|
5765
|
+
)) {
|
|
5480
5766
|
if (property === "constructor") return Object;
|
|
5481
5767
|
if (property === "__proto__") return Object.prototype;
|
|
5482
5768
|
return Reflect.get(Object.prototype, property);
|
|
@@ -5754,7 +6040,7 @@ function buildAliasPlans(state, selectiveFields, schemaIntrospector) {
|
|
|
5754
6040
|
return plans.toSorted((a, b) => compareStrings(a.alias, b.alias));
|
|
5755
6041
|
}
|
|
5756
6042
|
function buildSelectiveContext(row, plans, traversals) {
|
|
5757
|
-
const context =
|
|
6043
|
+
const context = createDataKeyedBag();
|
|
5758
6044
|
for (const plan of plans) {
|
|
5759
6045
|
const value = plan.optional && plan.idOutputName !== void 0 ? buildOptionalAliasValue(row, plan) : buildRequiredAliasValue(row, plan);
|
|
5760
6046
|
context[plan.alias] = value;
|
|
@@ -5770,7 +6056,7 @@ function buildSelectiveContext(row, plans, traversals) {
|
|
|
5770
6056
|
}
|
|
5771
6057
|
}
|
|
5772
6058
|
}
|
|
5773
|
-
return context;
|
|
6059
|
+
return { ...context };
|
|
5774
6060
|
}
|
|
5775
6061
|
function buildOptionalAliasValue(row, plan) {
|
|
5776
6062
|
if (plan.idOutputName === void 0) {
|
|
@@ -5783,17 +6069,16 @@ function buildOptionalAliasValue(row, plan) {
|
|
|
5783
6069
|
return buildRequiredAliasValue(row, plan);
|
|
5784
6070
|
}
|
|
5785
6071
|
function buildRequiredAliasValue(row, plan) {
|
|
5786
|
-
const base =
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
}
|
|
6072
|
+
const base = createDataKeyedBag();
|
|
6073
|
+
base[SELECTABLE_ALIAS_MARKER] = {
|
|
6074
|
+
alias: plan.alias,
|
|
6075
|
+
kind: plan.kind
|
|
5791
6076
|
};
|
|
5792
6077
|
for (const field2 of plan.systemFields) {
|
|
5793
6078
|
base[field2.field] = nullToUndefined(row[field2.outputName]);
|
|
5794
6079
|
}
|
|
5795
6080
|
if (plan.metaFields.length > 0) {
|
|
5796
|
-
const meta =
|
|
6081
|
+
const meta = createDataKeyedBag();
|
|
5797
6082
|
for (const field2 of plan.metaFields) {
|
|
5798
6083
|
meta[field2.metaKey] = nullToUndefined(row[field2.outputName]);
|
|
5799
6084
|
}
|
|
@@ -5806,23 +6091,26 @@ function buildRequiredAliasValue(row, plan) {
|
|
|
5806
6091
|
return createGuardedProxy(base, plan.alias);
|
|
5807
6092
|
}
|
|
5808
6093
|
function createGuardedProxy(target, debugPath) {
|
|
5809
|
-
return new Proxy(
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
|
|
5815
|
-
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
6094
|
+
return new Proxy(
|
|
6095
|
+
{ ...target },
|
|
6096
|
+
{
|
|
6097
|
+
get: (object, property, receiver) => {
|
|
6098
|
+
if (typeof property === "symbol") {
|
|
6099
|
+
return Reflect.get(object, property, receiver);
|
|
6100
|
+
}
|
|
6101
|
+
if (hasOwnKey(object, property)) {
|
|
6102
|
+
return Reflect.get(object, property, receiver);
|
|
6103
|
+
}
|
|
6104
|
+
if (isInteropProbeKey(property)) {
|
|
6105
|
+
return;
|
|
6106
|
+
}
|
|
6107
|
+
if (property in Object.prototype) {
|
|
6108
|
+
return Reflect.get(Object.prototype, property, receiver);
|
|
6109
|
+
}
|
|
6110
|
+
throw new MissingSelectiveFieldError(debugPath, property);
|
|
5822
6111
|
}
|
|
5823
|
-
throw new MissingSelectiveFieldError(debugPath, property);
|
|
5824
6112
|
}
|
|
5825
|
-
|
|
6113
|
+
);
|
|
5826
6114
|
}
|
|
5827
6115
|
|
|
5828
6116
|
// src/query/builder/read-instant-template.ts
|
|
@@ -5871,7 +6159,7 @@ function fillTemplateParams(params, bindings, dialect, listParameters = EMPTY_LI
|
|
|
5871
6159
|
readInstant ??= nowIso();
|
|
5872
6160
|
return readInstant;
|
|
5873
6161
|
}
|
|
5874
|
-
const value = bindings
|
|
6162
|
+
const value = readOwnProperty(bindings, name);
|
|
5875
6163
|
if (value === void 0) {
|
|
5876
6164
|
throw new ConfigurationError(`Missing binding for parameter "${name}"`, {
|
|
5877
6165
|
parameterName: name
|
|
@@ -5938,7 +6226,7 @@ function substitutePredicateExpression(expr, bindings) {
|
|
|
5938
6226
|
}
|
|
5939
6227
|
case "string_op": {
|
|
5940
6228
|
if (isParameterRef(expr.pattern)) {
|
|
5941
|
-
const value = bindings
|
|
6229
|
+
const value = readOwnProperty(bindings, expr.pattern.name);
|
|
5942
6230
|
if (value === void 0) {
|
|
5943
6231
|
throw new ConfigurationError(
|
|
5944
6232
|
`Missing binding for parameter "${expr.pattern.name}"`,
|
|
@@ -6012,7 +6300,7 @@ function substitutePredicateExpression(expr, bindings) {
|
|
|
6012
6300
|
}
|
|
6013
6301
|
}
|
|
6014
6302
|
function resolveBinding(bindings, name) {
|
|
6015
|
-
const value = bindings
|
|
6303
|
+
const value = readOwnProperty(bindings, name);
|
|
6016
6304
|
if (value === void 0) {
|
|
6017
6305
|
throw new ConfigurationError(`Missing binding for parameter "${name}"`, {
|
|
6018
6306
|
parameterName: name
|
|
@@ -6292,7 +6580,7 @@ function validateBindings(bindings, metadata) {
|
|
|
6292
6580
|
const { names: expectedNames, stringOpParameters, listParameters } = metadata;
|
|
6293
6581
|
const missing = [];
|
|
6294
6582
|
for (const name of expectedNames) {
|
|
6295
|
-
if (bindings
|
|
6583
|
+
if (readOwnProperty(bindings, name) === void 0) {
|
|
6296
6584
|
missing.push(name);
|
|
6297
6585
|
}
|
|
6298
6586
|
}
|
|
@@ -6312,7 +6600,7 @@ function validateBindings(bindings, metadata) {
|
|
|
6312
6600
|
);
|
|
6313
6601
|
}
|
|
6314
6602
|
for (const name of expectedNames) {
|
|
6315
|
-
const value = bindings
|
|
6603
|
+
const value = readOwnProperty(bindings, name);
|
|
6316
6604
|
if (listParameters.has(name)) {
|
|
6317
6605
|
validateListBinding(name, value, listParameters.get(name));
|
|
6318
6606
|
continue;
|
|
@@ -6703,6 +6991,7 @@ function buildCompileOptions(config) {
|
|
|
6703
6991
|
dialect: config.dialect ?? "sqlite",
|
|
6704
6992
|
schema: config.schema,
|
|
6705
6993
|
windowFunctions: config.backend?.capabilities.windowFunctions ?? true,
|
|
6994
|
+
identitySameIdAcrossKinds: config.identitySameIdAcrossKinds,
|
|
6706
6995
|
...fulltextStrategy === void 0 ? {} : {
|
|
6707
6996
|
fulltextStrategy,
|
|
6708
6997
|
fulltextLanguages: buildFulltextLanguages(config.registry)
|
|
@@ -7452,7 +7741,9 @@ var ExecutableQuery = class _ExecutableQuery {
|
|
|
7452
7741
|
optionalNodeAliases.add(traversal.nodeAlias);
|
|
7453
7742
|
}
|
|
7454
7743
|
}
|
|
7455
|
-
const cursorContext =
|
|
7744
|
+
const cursorContext = /* @__PURE__ */ Object.create(
|
|
7745
|
+
null
|
|
7746
|
+
);
|
|
7456
7747
|
for (const spec of this.#paginationOrderBy()) {
|
|
7457
7748
|
const alias = spec.field.alias;
|
|
7458
7749
|
const jsonPointer2 = spec.field.jsonPointer;
|
|
@@ -7510,7 +7801,9 @@ var ExecutableQuery = class _ExecutableQuery {
|
|
|
7510
7801
|
if (typeof existing_ === "object" && existing_ !== null) {
|
|
7511
7802
|
current = existing_;
|
|
7512
7803
|
} else {
|
|
7513
|
-
const created =
|
|
7804
|
+
const created = /* @__PURE__ */ Object.create(
|
|
7805
|
+
null
|
|
7806
|
+
);
|
|
7514
7807
|
current[segment] = created;
|
|
7515
7808
|
current = created;
|
|
7516
7809
|
}
|
|
@@ -7524,7 +7817,7 @@ var ExecutableQuery = class _ExecutableQuery {
|
|
|
7524
7817
|
if (typeof existing === "object" && existing !== null) {
|
|
7525
7818
|
return existing;
|
|
7526
7819
|
}
|
|
7527
|
-
const created =
|
|
7820
|
+
const created = /* @__PURE__ */ Object.create(null);
|
|
7528
7821
|
cursorContext[alias] = created;
|
|
7529
7822
|
return created;
|
|
7530
7823
|
}
|
|
@@ -7872,7 +8165,7 @@ var ExecutableAggregateQuery = class _ExecutableAggregateQuery {
|
|
|
7872
8165
|
*/
|
|
7873
8166
|
#mapResults(rows) {
|
|
7874
8167
|
return rows.map((row) => {
|
|
7875
|
-
const result =
|
|
8168
|
+
const result = createDataKeyedBag();
|
|
7876
8169
|
for (const key of Object.keys(this.#fields)) {
|
|
7877
8170
|
const field2 = this.#fields[key];
|
|
7878
8171
|
if (!field2) continue;
|
|
@@ -7883,7 +8176,7 @@ var ExecutableAggregateQuery = class _ExecutableAggregateQuery {
|
|
|
7883
8176
|
}
|
|
7884
8177
|
result[key] = normalizeFieldValue(field2, value);
|
|
7885
8178
|
}
|
|
7886
|
-
return result;
|
|
8179
|
+
return { ...result };
|
|
7887
8180
|
});
|
|
7888
8181
|
}
|
|
7889
8182
|
};
|
|
@@ -7996,9 +8289,10 @@ var TraversalBuilder = class _TraversalBuilder {
|
|
|
7996
8289
|
#direction;
|
|
7997
8290
|
#fromAlias;
|
|
7998
8291
|
#optional;
|
|
8292
|
+
#includeIdentityMembers;
|
|
7999
8293
|
#variableLength;
|
|
8000
8294
|
#pendingEdgePredicates;
|
|
8001
|
-
constructor(config, state, edgeKinds, edgeAlias, direction, fromAlias, inverseEdgeKinds = [], optional = false, variableLength = DEFAULT_VARIABLE_LENGTH_STATE, pendingEdgePredicates = []) {
|
|
8295
|
+
constructor(config, state, edgeKinds, edgeAlias, direction, fromAlias, inverseEdgeKinds = [], optional = false, variableLength = DEFAULT_VARIABLE_LENGTH_STATE, pendingEdgePredicates = [], includeIdentityMembers = false) {
|
|
8002
8296
|
this.#config = config;
|
|
8003
8297
|
this.#state = state;
|
|
8004
8298
|
this.#edgeKinds = edgeKinds;
|
|
@@ -8007,6 +8301,7 @@ var TraversalBuilder = class _TraversalBuilder {
|
|
|
8007
8301
|
this.#direction = direction;
|
|
8008
8302
|
this.#fromAlias = fromAlias;
|
|
8009
8303
|
this.#optional = optional;
|
|
8304
|
+
this.#includeIdentityMembers = includeIdentityMembers;
|
|
8010
8305
|
this.#variableLength = variableLength;
|
|
8011
8306
|
this.#pendingEdgePredicates = pendingEdgePredicates;
|
|
8012
8307
|
}
|
|
@@ -8054,7 +8349,8 @@ var TraversalBuilder = class _TraversalBuilder {
|
|
|
8054
8349
|
...depthAlias !== void 0 && { depthAlias }
|
|
8055
8350
|
}
|
|
8056
8351
|
},
|
|
8057
|
-
this.#pendingEdgePredicates
|
|
8352
|
+
this.#pendingEdgePredicates,
|
|
8353
|
+
this.#includeIdentityMembers
|
|
8058
8354
|
);
|
|
8059
8355
|
}
|
|
8060
8356
|
/**
|
|
@@ -8083,7 +8379,8 @@ var TraversalBuilder = class _TraversalBuilder {
|
|
|
8083
8379
|
this.#inverseEdgeKinds,
|
|
8084
8380
|
this.#optional,
|
|
8085
8381
|
this.#variableLength,
|
|
8086
|
-
[...this.#pendingEdgePredicates, newPredicate]
|
|
8382
|
+
[...this.#pendingEdgePredicates, newPredicate],
|
|
8383
|
+
this.#includeIdentityMembers
|
|
8087
8384
|
);
|
|
8088
8385
|
}
|
|
8089
8386
|
/**
|
|
@@ -8164,12 +8461,16 @@ var TraversalBuilder = class _TraversalBuilder {
|
|
|
8164
8461
|
return new Proxy({}, {
|
|
8165
8462
|
get: (_, property) => {
|
|
8166
8463
|
if (typeof property === "symbol") return;
|
|
8167
|
-
if (property === "then") return;
|
|
8168
|
-
if (property === "toJSON") return;
|
|
8169
8464
|
if (property === "id") return idAccessor;
|
|
8170
8465
|
if (property === "kind") return kindAccessor;
|
|
8171
8466
|
if (property === "fromId") return fromIdAccessor;
|
|
8172
8467
|
if (property === "toId") return toIdAccessor;
|
|
8468
|
+
if (isInteropProbeKey(property) && !this.#config.schemaIntrospector.hasDeclaredEdgeField(
|
|
8469
|
+
allEdgeKinds,
|
|
8470
|
+
property
|
|
8471
|
+
)) {
|
|
8472
|
+
return;
|
|
8473
|
+
}
|
|
8173
8474
|
return buildFieldAccessor(property);
|
|
8174
8475
|
}
|
|
8175
8476
|
});
|
|
@@ -8215,7 +8516,8 @@ var TraversalBuilder = class _TraversalBuilder {
|
|
|
8215
8516
|
nodeKinds: kinds,
|
|
8216
8517
|
joinFromAlias: this.#fromAlias,
|
|
8217
8518
|
joinEdgeField: this.#direction === "out" ? "from_id" : "to_id",
|
|
8218
|
-
optional: this.#optional
|
|
8519
|
+
optional: this.#optional,
|
|
8520
|
+
...this.#includeIdentityMembers ? { includeIdentityMembers: true } : {}
|
|
8219
8521
|
};
|
|
8220
8522
|
const baseTraversal = this.#inverseEdgeKinds.length > 0 ? { ...traversalBase, inverseEdgeKinds: this.#inverseEdgeKinds } : traversalBase;
|
|
8221
8523
|
const traversal = this.#variableLength.enabled ? {
|
|
@@ -8506,6 +8808,7 @@ var QueryBuilder = class _QueryBuilder {
|
|
|
8506
8808
|
traverse(edgeKind, edgeAlias, options) {
|
|
8507
8809
|
validateSqlIdentifier(edgeAlias);
|
|
8508
8810
|
const direction = options?.direction ?? "out";
|
|
8811
|
+
this.#assertIdentityTraversalAllowed(options);
|
|
8509
8812
|
const expansion = options?.expand ?? this.#config.defaultTraversalExpansion;
|
|
8510
8813
|
const includeImplyingEdges = expansion === "implying" || expansion === "all";
|
|
8511
8814
|
const includeInverseEdges = expansion === "inverse" || expansion === "all";
|
|
@@ -8523,7 +8826,12 @@ var QueryBuilder = class _QueryBuilder {
|
|
|
8523
8826
|
direction,
|
|
8524
8827
|
fromAlias,
|
|
8525
8828
|
inverseEdgeKinds,
|
|
8526
|
-
false
|
|
8829
|
+
false,
|
|
8830
|
+
void 0,
|
|
8831
|
+
// variableLength — default
|
|
8832
|
+
void 0,
|
|
8833
|
+
// pendingEdgePredicates — default
|
|
8834
|
+
options?.includeIdentityMembers ?? false
|
|
8527
8835
|
);
|
|
8528
8836
|
}
|
|
8529
8837
|
/**
|
|
@@ -8536,6 +8844,7 @@ var QueryBuilder = class _QueryBuilder {
|
|
|
8536
8844
|
optionalTraverse(edgeKind, edgeAlias, options) {
|
|
8537
8845
|
validateSqlIdentifier(edgeAlias);
|
|
8538
8846
|
const direction = options?.direction ?? "out";
|
|
8847
|
+
this.#assertIdentityTraversalAllowed(options);
|
|
8539
8848
|
const expansion = options?.expand ?? this.#config.defaultTraversalExpansion;
|
|
8540
8849
|
const includeImplyingEdges = expansion === "implying" || expansion === "all";
|
|
8541
8850
|
const includeInverseEdges = expansion === "inverse" || expansion === "all";
|
|
@@ -8553,7 +8862,12 @@ var QueryBuilder = class _QueryBuilder {
|
|
|
8553
8862
|
direction,
|
|
8554
8863
|
fromAlias,
|
|
8555
8864
|
inverseEdgeKinds,
|
|
8556
|
-
true
|
|
8865
|
+
true,
|
|
8866
|
+
void 0,
|
|
8867
|
+
// variableLength — default
|
|
8868
|
+
void 0,
|
|
8869
|
+
// pendingEdgePredicates — default
|
|
8870
|
+
options?.includeIdentityMembers ?? false
|
|
8557
8871
|
);
|
|
8558
8872
|
}
|
|
8559
8873
|
/**
|
|
@@ -8578,6 +8892,7 @@ var QueryBuilder = class _QueryBuilder {
|
|
|
8578
8892
|
});
|
|
8579
8893
|
}
|
|
8580
8894
|
const direction = options?.direction ?? "out";
|
|
8895
|
+
this.#assertIdentityTraversalAllowed(options);
|
|
8581
8896
|
const expansion = options?.expand ?? this.#config.defaultTraversalExpansion;
|
|
8582
8897
|
const includeImplyingEdges = expansion === "implying" || expansion === "all";
|
|
8583
8898
|
const includeInverseEdges = expansion === "inverse" || expansion === "all";
|
|
@@ -8602,7 +8917,12 @@ var QueryBuilder = class _QueryBuilder {
|
|
|
8602
8917
|
direction,
|
|
8603
8918
|
fromAlias,
|
|
8604
8919
|
inverseEdgeKinds,
|
|
8605
|
-
optional
|
|
8920
|
+
optional,
|
|
8921
|
+
void 0,
|
|
8922
|
+
// variableLength — default
|
|
8923
|
+
void 0,
|
|
8924
|
+
// pendingEdgePredicates — default
|
|
8925
|
+
options?.includeIdentityMembers ?? false
|
|
8606
8926
|
);
|
|
8607
8927
|
}
|
|
8608
8928
|
/**
|
|
@@ -8904,11 +9224,15 @@ var QueryBuilder = class _QueryBuilder {
|
|
|
8904
9224
|
return new Proxy({}, {
|
|
8905
9225
|
get: (_, property) => {
|
|
8906
9226
|
if (typeof property === "symbol") return;
|
|
8907
|
-
if (property === "then") return;
|
|
8908
|
-
if (property === "toJSON") return;
|
|
8909
9227
|
if (property === "id") return idAccessor;
|
|
8910
9228
|
if (property === "kind") return kindAccessor;
|
|
8911
9229
|
if (property === "$fulltext") return fulltextAccessor;
|
|
9230
|
+
if (isInteropProbeKey(property) && !(kindNames !== void 0 && this.#config.schemaIntrospector.hasDeclaredField(
|
|
9231
|
+
kindNames,
|
|
9232
|
+
property
|
|
9233
|
+
))) {
|
|
9234
|
+
return;
|
|
9235
|
+
}
|
|
8912
9236
|
return this.#getFieldBuilderForProperty(kindNames, property, alias);
|
|
8913
9237
|
}
|
|
8914
9238
|
});
|
|
@@ -8917,6 +9241,21 @@ var QueryBuilder = class _QueryBuilder {
|
|
|
8917
9241
|
if (!kindNames) return false;
|
|
8918
9242
|
return this.#config.schemaIntrospector.hasSearchableField(kindNames);
|
|
8919
9243
|
}
|
|
9244
|
+
/**
|
|
9245
|
+
* Guards `includeIdentityMembers` against a non-identity-enabled builder.
|
|
9246
|
+
*/
|
|
9247
|
+
#assertIdentityTraversalAllowed(options) {
|
|
9248
|
+
if (!options?.includeIdentityMembers || this.#config.identityEnabled) {
|
|
9249
|
+
return;
|
|
9250
|
+
}
|
|
9251
|
+
throw new ConfigurationError(
|
|
9252
|
+
"includeIdentityMembers requires an identity-enabled graph registry.",
|
|
9253
|
+
{ code: "IDENTITY_NOT_ENABLED", graphId: this.#config.graphId },
|
|
9254
|
+
{
|
|
9255
|
+
suggestion: "Enable defineGraph(...).identity and build the registry from that graph."
|
|
9256
|
+
}
|
|
9257
|
+
);
|
|
9258
|
+
}
|
|
8920
9259
|
#expandTraversalEdgeKinds(edgeKind, includeImplyingEdges) {
|
|
8921
9260
|
return includeImplyingEdges ? this.#config.registry.expandImplyingEdges(edgeKind) : [edgeKind];
|
|
8922
9261
|
}
|
|
@@ -8997,12 +9336,16 @@ var QueryBuilder = class _QueryBuilder {
|
|
|
8997
9336
|
return new Proxy({}, {
|
|
8998
9337
|
get: (_, property) => {
|
|
8999
9338
|
if (typeof property === "symbol") return;
|
|
9000
|
-
if (property === "then") return;
|
|
9001
|
-
if (property === "toJSON") return;
|
|
9002
9339
|
if (property === "id") return idAccessor;
|
|
9003
9340
|
if (property === "kind") return kindAccessor;
|
|
9004
9341
|
if (property === "fromId") return fromIdAccessor;
|
|
9005
9342
|
if (property === "toId") return toIdAccessor;
|
|
9343
|
+
if (isInteropProbeKey(property) && !(edgeKindNames !== void 0 && this.#config.schemaIntrospector.hasDeclaredEdgeField(
|
|
9344
|
+
edgeKindNames,
|
|
9345
|
+
property
|
|
9346
|
+
))) {
|
|
9347
|
+
return;
|
|
9348
|
+
}
|
|
9006
9349
|
return this.#getFieldBuilderForEdgeProperty(
|
|
9007
9350
|
edgeKindNames,
|
|
9008
9351
|
property,
|
|
@@ -9456,11 +9799,31 @@ function createQueryBuilderWithContext(graphId, registry, options) {
|
|
|
9456
9799
|
registry.nodeKinds,
|
|
9457
9800
|
registry.edgeKinds
|
|
9458
9801
|
);
|
|
9802
|
+
if (options?.identityEnabled === true && registry.identity === void 0) {
|
|
9803
|
+
throw new ConfigurationError(
|
|
9804
|
+
"Identity-aware query compilation requires an identity-enabled graph registry.",
|
|
9805
|
+
{ code: "IDENTITY_NOT_ENABLED", graphId },
|
|
9806
|
+
{
|
|
9807
|
+
suggestion: "Build the registry from a graph whose defineGraph(...) config includes identity."
|
|
9808
|
+
}
|
|
9809
|
+
);
|
|
9810
|
+
}
|
|
9811
|
+
if (options?.identitySameIdAcrossKinds !== void 0 && registry.identity === void 0) {
|
|
9812
|
+
throw new ConfigurationError(
|
|
9813
|
+
"identitySameIdAcrossKinds requires an identity-enabled graph registry.",
|
|
9814
|
+
{ code: "IDENTITY_NOT_ENABLED", graphId },
|
|
9815
|
+
{
|
|
9816
|
+
suggestion: "Configure sameIdAcrossKinds on defineGraph(...).identity instead."
|
|
9817
|
+
}
|
|
9818
|
+
);
|
|
9819
|
+
}
|
|
9459
9820
|
const config = {
|
|
9460
9821
|
graphId,
|
|
9461
9822
|
registry,
|
|
9462
9823
|
schemaIntrospector,
|
|
9463
9824
|
defaultTraversalExpansion: options?.defaultTraversalExpansion ?? "inverse",
|
|
9825
|
+
identityEnabled: options?.identityEnabled ?? registry.identity !== void 0,
|
|
9826
|
+
identitySameIdAcrossKinds: options?.identitySameIdAcrossKinds ?? registry.identity?.sameIdAcrossKinds ?? "fold",
|
|
9464
9827
|
...options?.backend !== void 0 && { backend: options.backend },
|
|
9465
9828
|
...options?.dialect !== void 0 && { dialect: options.dialect },
|
|
9466
9829
|
...options?.schema !== void 0 && { schema: options.schema }
|
|
@@ -9535,8 +9898,29 @@ function isPropsObject(value) {
|
|
|
9535
9898
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9536
9899
|
}
|
|
9537
9900
|
async function rebuildFulltextIndex(ctx, nodeKind, options) {
|
|
9538
|
-
const { backend
|
|
9539
|
-
|
|
9901
|
+
const { backend } = ctx;
|
|
9902
|
+
return runFulltextRebuild(
|
|
9903
|
+
{
|
|
9904
|
+
read: backend,
|
|
9905
|
+
runPage: (write) => runOptionallyInTransaction(backend, write)
|
|
9906
|
+
},
|
|
9907
|
+
ctx,
|
|
9908
|
+
nodeKind,
|
|
9909
|
+
options
|
|
9910
|
+
);
|
|
9911
|
+
}
|
|
9912
|
+
async function repopulateFulltextInTransaction(ctx, target, options = {}) {
|
|
9913
|
+
return runFulltextRebuild(
|
|
9914
|
+
{ read: target, runPage: (write) => write(target) },
|
|
9915
|
+
ctx,
|
|
9916
|
+
void 0,
|
|
9917
|
+
options
|
|
9918
|
+
);
|
|
9919
|
+
}
|
|
9920
|
+
async function runFulltextRebuild(driver, ctx, nodeKind, options) {
|
|
9921
|
+
const { registry } = ctx;
|
|
9922
|
+
const backend = driver.read;
|
|
9923
|
+
if (!backend.upsertFulltext || !backend.deleteFulltext) {
|
|
9540
9924
|
throw new ConfigurationError(
|
|
9541
9925
|
"Backend does not support fulltext; cannot rebuild index",
|
|
9542
9926
|
{ backend: backend.dialect, capability: "fulltext" }
|
|
@@ -9615,7 +9999,7 @@ async function rebuildFulltextIndex(ctx, nodeKind, options) {
|
|
|
9615
9999
|
}
|
|
9616
10000
|
}
|
|
9617
10001
|
};
|
|
9618
|
-
await
|
|
10002
|
+
await driver.runPage(writePage);
|
|
9619
10003
|
processed += rows.length;
|
|
9620
10004
|
upserted += pageResult.toUpsert.length;
|
|
9621
10005
|
cleared += pageResult.toDelete.length;
|
|
@@ -10457,7 +10841,8 @@ function classifyModifications(existing, next) {
|
|
|
10457
10841
|
}
|
|
10458
10842
|
};
|
|
10459
10843
|
for (const [name, nextNode] of Object.entries(next.nodes ?? {})) {
|
|
10460
|
-
const
|
|
10844
|
+
const existingNodes = existing.nodes;
|
|
10845
|
+
const existingNode = existingNodes !== void 0 && hasOwnKey(existingNodes, name) ? existingNodes[name] : void 0;
|
|
10461
10846
|
if (existingNode === void 0) continue;
|
|
10462
10847
|
classifyNode(name, existingNode, nextNode, {
|
|
10463
10848
|
recordIncompatible,
|
|
@@ -10467,7 +10852,8 @@ function classifyModifications(existing, next) {
|
|
|
10467
10852
|
});
|
|
10468
10853
|
}
|
|
10469
10854
|
for (const [name, nextEdge] of Object.entries(next.edges ?? {})) {
|
|
10470
|
-
const
|
|
10855
|
+
const existingEdges = existing.edges;
|
|
10856
|
+
const existingEdge = existingEdges !== void 0 && hasOwnKey(existingEdges, name) ? existingEdges[name] : void 0;
|
|
10471
10857
|
if (existingEdge === void 0) continue;
|
|
10472
10858
|
classifyEdge(name, existingEdge, nextEdge, {
|
|
10473
10859
|
recordIncompatible,
|
|
@@ -10861,13 +11247,17 @@ function resolveMaxHops(rawMaxHops, fallback, optionName) {
|
|
|
10861
11247
|
}
|
|
10862
11248
|
function resolveMaxIterations(value, fallback, algorithm) {
|
|
10863
11249
|
const maxIterations = value ?? fallback;
|
|
10864
|
-
|
|
11250
|
+
assertPositiveSafeIntegerOption(maxIterations, algorithm, "maxIterations");
|
|
11251
|
+
return maxIterations;
|
|
11252
|
+
}
|
|
11253
|
+
function assertPositiveSafeIntegerOption(value, algorithm, optionName) {
|
|
11254
|
+
if (value === void 0) return;
|
|
11255
|
+
if (!Number.isSafeInteger(value) || value < 1) {
|
|
10865
11256
|
throw new ConfigurationError(
|
|
10866
|
-
`${algorithm}
|
|
10867
|
-
{
|
|
11257
|
+
`${algorithm} ${optionName} must be a positive safe integer, got ${String(value)}.`,
|
|
11258
|
+
{ [optionName]: value }
|
|
10868
11259
|
);
|
|
10869
11260
|
}
|
|
10870
|
-
return maxIterations;
|
|
10871
11261
|
}
|
|
10872
11262
|
function assertEdgeKinds(edges) {
|
|
10873
11263
|
if (edges.length === 0) {
|
|
@@ -10877,16 +11267,6 @@ function assertEdgeKinds(edges) {
|
|
|
10877
11267
|
);
|
|
10878
11268
|
}
|
|
10879
11269
|
}
|
|
10880
|
-
function compileNodeKindSeedFilter(nodeKinds) {
|
|
10881
|
-
if (nodeKinds === void 0) return sql`TRUE`;
|
|
10882
|
-
return compileKindFilter(sql.raw("n.kind"), nodeKinds);
|
|
10883
|
-
}
|
|
10884
|
-
function normalizeNodeKinds(nodeKinds) {
|
|
10885
|
-
if (nodeKinds === void 0) return void 0;
|
|
10886
|
-
return [...new Set(nodeKinds)].toSorted(
|
|
10887
|
-
(left, right) => compareCodePoints(left, right)
|
|
10888
|
-
);
|
|
10889
|
-
}
|
|
10890
11270
|
function assertGraphAnalyticsSupported(ctx, algorithm, options = {}) {
|
|
10891
11271
|
const graphAnalytics = ctx.backend.capabilities.graphAnalytics?.supported === true;
|
|
10892
11272
|
const windowFunctions = ctx.backend.capabilities.windowFunctions;
|
|
@@ -10962,6 +11342,56 @@ async function countWorkingTableRows(context) {
|
|
|
10962
11342
|
);
|
|
10963
11343
|
return Number(rows[0]?.count ?? 0);
|
|
10964
11344
|
}
|
|
11345
|
+
async function seedWorkingTableFromNodes(context, nodeKinds, seedColumns) {
|
|
11346
|
+
if (nodeKinds?.length === 0) return;
|
|
11347
|
+
const additionalColumns = seedColumns.length === 0 ? sql.empty() : sql`, ${sql.join(
|
|
11348
|
+
seedColumns.map((column) => sql.identifier(column.name)),
|
|
11349
|
+
sql`, `
|
|
11350
|
+
)}`;
|
|
11351
|
+
const additionalValues = seedColumns.length === 0 ? sql.empty() : sql`, ${sql.join(
|
|
11352
|
+
seedColumns.map((column) => column.value),
|
|
11353
|
+
sql`, `
|
|
11354
|
+
)}`;
|
|
11355
|
+
const compileSeedStatement = (nodeKindFilter) => {
|
|
11356
|
+
return sql`
|
|
11357
|
+
INSERT INTO ${context.workingTable}
|
|
11358
|
+
(graph_id, run_id, node_id, node_kind${additionalColumns})
|
|
11359
|
+
SELECT ${context.graphId}, ${context.runId}, n.id, n.kind${additionalValues}
|
|
11360
|
+
FROM ${context.operation.schema.nodesTable} n
|
|
11361
|
+
WHERE n.graph_id = ${context.graphId}
|
|
11362
|
+
AND ${nodeKindFilter}
|
|
11363
|
+
AND ${context.operation.nodeTemporalFilter}
|
|
11364
|
+
`;
|
|
11365
|
+
};
|
|
11366
|
+
const fixedStatement = compileSeedStatement(sql`TRUE`);
|
|
11367
|
+
const parameterLimit = context.operation.backend.capabilities.maxBindParameters ?? DEFAULT_MAX_BIND_PARAMETERS;
|
|
11368
|
+
const fixedParameterCount = countBindParameters(fixedStatement);
|
|
11369
|
+
const kindBindBudget = parameterLimit - fixedParameterCount;
|
|
11370
|
+
if (kindBindBudget < (nodeKinds === void 0 ? 0 : 1)) {
|
|
11371
|
+
throw new ConfigurationError(
|
|
11372
|
+
"An iterative graph operation cannot fit its node-kind initialization within the backend bind-parameter limit.",
|
|
11373
|
+
{ fixedParameterCount, parameterLimit }
|
|
11374
|
+
);
|
|
11375
|
+
}
|
|
11376
|
+
if (nodeKinds === void 0) {
|
|
11377
|
+
await context.executeTemporary(fixedStatement);
|
|
11378
|
+
return;
|
|
11379
|
+
}
|
|
11380
|
+
const normalizedKinds = [...new Set(nodeKinds)].toSorted(
|
|
11381
|
+
(left, right) => compareCodePoints(left, right)
|
|
11382
|
+
);
|
|
11383
|
+
for (const kindChunk of chunkValues(normalizedKinds, kindBindBudget)) {
|
|
11384
|
+
await context.executeTemporary(
|
|
11385
|
+
compileSeedStatement(compileKindFilter(sql.raw("n.kind"), kindChunk))
|
|
11386
|
+
);
|
|
11387
|
+
}
|
|
11388
|
+
}
|
|
11389
|
+
function countBindParameters(fragment) {
|
|
11390
|
+
return fragment.chunks.reduce(
|
|
11391
|
+
(count2, chunk2) => count2 + (chunk2.kind === "parameter" || chunk2.kind === "placeholder" ? 1 : 0),
|
|
11392
|
+
0
|
|
11393
|
+
);
|
|
11394
|
+
}
|
|
10965
11395
|
var DEFAULT_MAX_BIND_PARAMETERS = 999;
|
|
10966
11396
|
var RESERVED_TEMPORAL_BIND_PARAMETERS_PER_BRANCH = 12;
|
|
10967
11397
|
var RESERVED_WEIGHT_BIND_PARAMETERS_PER_BRANCH = 4;
|
|
@@ -11017,64 +11447,101 @@ async function runIterativeGraphOperation(ctx, options, plan) {
|
|
|
11017
11447
|
accessMode: "read_only",
|
|
11018
11448
|
temporaryWrites: INTERNAL_TEMPORARY_WRITES
|
|
11019
11449
|
};
|
|
11020
|
-
|
|
11021
|
-
|
|
11022
|
-
|
|
11023
|
-
|
|
11024
|
-
|
|
11025
|
-
);
|
|
11026
|
-
|
|
11027
|
-
|
|
11028
|
-
|
|
11029
|
-
|
|
11030
|
-
|
|
11031
|
-
|
|
11032
|
-
|
|
11033
|
-
|
|
11034
|
-
|
|
11035
|
-
|
|
11036
|
-
|
|
11037
|
-
|
|
11038
|
-
|
|
11039
|
-
|
|
11040
|
-
|
|
11450
|
+
const rounds = { started: false };
|
|
11451
|
+
try {
|
|
11452
|
+
return await ctx.backend.transaction(async (backend) => {
|
|
11453
|
+
rounds.started = true;
|
|
11454
|
+
return runWorkingTableRounds(ctx, options, plan, backend);
|
|
11455
|
+
}, transactionOptions);
|
|
11456
|
+
} catch (error) {
|
|
11457
|
+
if (rounds.started || ctx.backend.dialect !== "postgres") throw error;
|
|
11458
|
+
const sqlState = postgresReadWriteRefusedSqlState(error);
|
|
11459
|
+
if (sqlState === void 0) throw error;
|
|
11460
|
+
throw temporaryTableCapabilityError(ctx, plan.algorithm, sqlState, error);
|
|
11461
|
+
}
|
|
11462
|
+
}
|
|
11463
|
+
function temporaryTableCapabilityError(ctx, algorithm, sqlState, cause) {
|
|
11464
|
+
const error = new UnsupportedBackendCapabilityError(
|
|
11465
|
+
algorithm,
|
|
11466
|
+
"graphAnalytics",
|
|
11467
|
+
{
|
|
11468
|
+
dialect: ctx.backend.dialect,
|
|
11469
|
+
supported: false,
|
|
11470
|
+
requirement: "transaction-local temporary tables",
|
|
11471
|
+
sqlState
|
|
11472
|
+
},
|
|
11473
|
+
"Run graph analytics on a writable PostgreSQL primary with a role that has the TEMP privilege."
|
|
11474
|
+
);
|
|
11475
|
+
Object.defineProperty(error, "cause", {
|
|
11476
|
+
configurable: true,
|
|
11477
|
+
value: cause,
|
|
11478
|
+
writable: true
|
|
11479
|
+
});
|
|
11480
|
+
return error;
|
|
11481
|
+
}
|
|
11482
|
+
async function runWorkingTableRounds(ctx, options, plan, backend) {
|
|
11483
|
+
const temporaryBackend = requireTemporaryStatements(backend);
|
|
11484
|
+
const runId = generateId();
|
|
11485
|
+
const workingTable = sql.identifier(
|
|
11486
|
+
`typegraph_iterative_${runId.replaceAll("-", "_")}`
|
|
11487
|
+
);
|
|
11488
|
+
const context = {
|
|
11489
|
+
operation: createOperation(ctx, options, temporaryBackend),
|
|
11490
|
+
backend: temporaryBackend,
|
|
11491
|
+
workingTable,
|
|
11492
|
+
graphId: ctx.graphId,
|
|
11493
|
+
runId,
|
|
11494
|
+
executeTemporary: async (query) => {
|
|
11495
|
+
await temporaryBackend.executeTemporaryStatement(
|
|
11496
|
+
asCompiledTemporaryStatementSql(query)
|
|
11041
11497
|
);
|
|
11042
|
-
if (workingMemoryStatement !== void 0) {
|
|
11043
|
-
await context.executeTemporary(workingMemoryStatement);
|
|
11044
|
-
}
|
|
11045
11498
|
}
|
|
11499
|
+
};
|
|
11500
|
+
if (context.operation.workingMemory !== void 0) {
|
|
11501
|
+
const workingMemoryStatement = ctx.dialect.setTransactionWorkingMemory(
|
|
11502
|
+
context.operation.workingMemory
|
|
11503
|
+
);
|
|
11504
|
+
if (workingMemoryStatement !== void 0) {
|
|
11505
|
+
await context.executeTemporary(workingMemoryStatement);
|
|
11506
|
+
}
|
|
11507
|
+
}
|
|
11508
|
+
try {
|
|
11046
11509
|
await context.executeTemporary(plan.createWorkingTable(context));
|
|
11047
|
-
|
|
11048
|
-
|
|
11049
|
-
|
|
11050
|
-
|
|
11051
|
-
|
|
11052
|
-
|
|
11053
|
-
|
|
11054
|
-
|
|
11055
|
-
|
|
11056
|
-
|
|
11057
|
-
|
|
11058
|
-
|
|
11059
|
-
|
|
11060
|
-
|
|
11061
|
-
|
|
11062
|
-
|
|
11063
|
-
|
|
11064
|
-
|
|
11065
|
-
|
|
11066
|
-
plan.algorithm,
|
|
11067
|
-
plan.maxIterations
|
|
11510
|
+
} catch (error) {
|
|
11511
|
+
const sqlState = ctx.backend.dialect === "postgres" ? postgresTemporaryTableUnavailableSqlState(error) : void 0;
|
|
11512
|
+
if (sqlState === void 0) throw error;
|
|
11513
|
+
throw temporaryTableCapabilityError(ctx, plan.algorithm, sqlState, error);
|
|
11514
|
+
}
|
|
11515
|
+
let operationFailure;
|
|
11516
|
+
try {
|
|
11517
|
+
let state = await plan.initialize(context);
|
|
11518
|
+
let analyzedRowCount = await refreshWorkingTableStatistics(
|
|
11519
|
+
context,
|
|
11520
|
+
state.workingTableSize
|
|
11521
|
+
);
|
|
11522
|
+
for (let iteration = 1; iteration <= plan.maxIterations && !plan.hasConverged(state); iteration++) {
|
|
11523
|
+
state = await plan.runRound(context, state, iteration);
|
|
11524
|
+
if (iteration < plan.maxIterations && !plan.hasConverged(state)) {
|
|
11525
|
+
analyzedRowCount = await refreshWorkingTableStatistics(
|
|
11526
|
+
context,
|
|
11527
|
+
state.workingTableSize,
|
|
11528
|
+
analyzedRowCount
|
|
11068
11529
|
);
|
|
11069
11530
|
}
|
|
11070
|
-
return await plan.extractResult(context, state);
|
|
11071
|
-
} catch (error) {
|
|
11072
|
-
operationFailure = { error };
|
|
11073
|
-
throw error;
|
|
11074
|
-
} finally {
|
|
11075
|
-
await cleanupWorkingTables(context, plan.cleanup, operationFailure);
|
|
11076
11531
|
}
|
|
11077
|
-
|
|
11532
|
+
if (!plan.hasConverged(state)) {
|
|
11533
|
+
throw new GraphAlgorithmConvergenceError(
|
|
11534
|
+
plan.algorithm,
|
|
11535
|
+
plan.maxIterations
|
|
11536
|
+
);
|
|
11537
|
+
}
|
|
11538
|
+
return await plan.extractResult(context, state);
|
|
11539
|
+
} catch (error) {
|
|
11540
|
+
operationFailure = { error };
|
|
11541
|
+
throw error;
|
|
11542
|
+
} finally {
|
|
11543
|
+
await cleanupWorkingTables(context, plan.cleanup, operationFailure);
|
|
11544
|
+
}
|
|
11078
11545
|
}
|
|
11079
11546
|
function shouldRefreshWorkingTableStatistics(workingTableSize, analyzedRowCount) {
|
|
11080
11547
|
if (workingTableSize < WORKING_TABLE_ANALYZE_MINIMUM_ROWS) return false;
|
|
@@ -11377,7 +11844,6 @@ async function executeLabelPropagation(ctx, options) {
|
|
|
11377
11844
|
DEFAULT_LABEL_PROPAGATION_MAX_ITERATIONS,
|
|
11378
11845
|
"labelPropagation"
|
|
11379
11846
|
);
|
|
11380
|
-
const nodeKinds = normalizeNodeKinds(options.nodeKinds);
|
|
11381
11847
|
const policy = {
|
|
11382
11848
|
detector: createOscillationDetector(),
|
|
11383
11849
|
maxIterations,
|
|
@@ -11393,7 +11859,7 @@ async function executeLabelPropagation(ctx, options) {
|
|
|
11393
11859
|
algorithm: "labelPropagation",
|
|
11394
11860
|
maxIterations,
|
|
11395
11861
|
createWorkingTable,
|
|
11396
|
-
initialize: (context) => initializeWorkingTables(context, nodeKinds),
|
|
11862
|
+
initialize: (context) => initializeWorkingTables(context, options.nodeKinds),
|
|
11397
11863
|
runRound: (context, state, iteration) => runLabelPropagationRound(context, state, iteration, policy),
|
|
11398
11864
|
hasConverged(state) {
|
|
11399
11865
|
return state.changedCount === 0 || state.completed;
|
|
@@ -11458,26 +11924,21 @@ function activeIndexIdentifier(context) {
|
|
|
11458
11924
|
);
|
|
11459
11925
|
}
|
|
11460
11926
|
async function initializeWorkingTables(context, nodeKinds) {
|
|
11461
|
-
|
|
11462
|
-
|
|
11463
|
-
|
|
11464
|
-
|
|
11465
|
-
|
|
11466
|
-
|
|
11467
|
-
|
|
11468
|
-
|
|
11469
|
-
FROM ${operation.schema.nodesTable} n
|
|
11470
|
-
WHERE n.graph_id = ${graphId}
|
|
11471
|
-
AND ${nodeKindFilter}
|
|
11472
|
-
AND ${operation.nodeTemporalFilter}
|
|
11473
|
-
`);
|
|
11927
|
+
await seedWorkingTableFromNodes(context, nodeKinds, [
|
|
11928
|
+
{ name: "label_id", value: sql.raw("n.id") },
|
|
11929
|
+
{ name: "label_kind", value: sql.raw("n.kind") },
|
|
11930
|
+
{ name: "next_label_id", value: sql.raw("n.id") },
|
|
11931
|
+
{ name: "next_label_kind", value: sql.raw("n.kind") },
|
|
11932
|
+
{ name: "changed_round", value: sql.raw("0") },
|
|
11933
|
+
{ name: "active_round", value: sql.raw("1") }
|
|
11934
|
+
]);
|
|
11474
11935
|
await context.executeTemporary(sql`
|
|
11475
11936
|
CREATE INDEX ${frontierIndexIdentifier(context)}
|
|
11476
|
-
ON ${workingTable} (graph_id, run_id, changed_round)
|
|
11937
|
+
ON ${context.workingTable} (graph_id, run_id, changed_round)
|
|
11477
11938
|
`);
|
|
11478
11939
|
await context.executeTemporary(sql`
|
|
11479
11940
|
CREATE INDEX ${activeIndexIdentifier(context)}
|
|
11480
|
-
ON ${workingTable} (graph_id, run_id, active_round)
|
|
11941
|
+
ON ${context.workingTable} (graph_id, run_id, active_round)
|
|
11481
11942
|
`);
|
|
11482
11943
|
const neighborTable = neighborTableIdentifier(context);
|
|
11483
11944
|
await context.executeTemporary(sql`
|
|
@@ -11489,10 +11950,10 @@ async function initializeWorkingTables(context, nodeKinds) {
|
|
|
11489
11950
|
PRIMARY KEY (target_kind, target_id, neighbor_kind, neighbor_id)
|
|
11490
11951
|
)
|
|
11491
11952
|
`);
|
|
11492
|
-
for (const edgeKinds of operation.edgeKindChunks) {
|
|
11953
|
+
for (const edgeKinds of context.operation.edgeKindChunks) {
|
|
11493
11954
|
await materializeChunkNeighbors(context, edgeKinds);
|
|
11494
11955
|
}
|
|
11495
|
-
const analyzeNeighbors = operation.ctx.dialect.analyzeTemporaryTable(neighborTable);
|
|
11956
|
+
const analyzeNeighbors = context.operation.ctx.dialect.analyzeTemporaryTable(neighborTable);
|
|
11496
11957
|
if (analyzeNeighbors !== void 0) {
|
|
11497
11958
|
await context.executeTemporary(analyzeNeighbors);
|
|
11498
11959
|
}
|
|
@@ -11734,7 +12195,7 @@ function executePageRankOperation(ctx, options, seeds) {
|
|
|
11734
12195
|
hasConverged(state) {
|
|
11735
12196
|
return state.maximumChange <= resolved.tolerance;
|
|
11736
12197
|
},
|
|
11737
|
-
extractResult: extractScores
|
|
12198
|
+
extractResult: (context, state) => extractScores(context, state, resolved.topK)
|
|
11738
12199
|
});
|
|
11739
12200
|
}
|
|
11740
12201
|
function resolvePageRankOptions(options, algorithm) {
|
|
@@ -11752,6 +12213,7 @@ function resolvePageRankOptions(options, algorithm) {
|
|
|
11752
12213
|
{ tolerance }
|
|
11753
12214
|
);
|
|
11754
12215
|
}
|
|
12216
|
+
assertPositiveSafeIntegerOption(options.topK, algorithm, "topK");
|
|
11755
12217
|
return {
|
|
11756
12218
|
dampingFactor,
|
|
11757
12219
|
tolerance,
|
|
@@ -11760,7 +12222,8 @@ function resolvePageRankOptions(options, algorithm) {
|
|
|
11760
12222
|
DEFAULT_MAX_ITERATIONS,
|
|
11761
12223
|
algorithm
|
|
11762
12224
|
),
|
|
11763
|
-
nodeKinds:
|
|
12225
|
+
nodeKinds: options.nodeKinds,
|
|
12226
|
+
topK: options.topK
|
|
11764
12227
|
};
|
|
11765
12228
|
}
|
|
11766
12229
|
function normalizeSeeds(seeds) {
|
|
@@ -11833,17 +12296,12 @@ function createWorkingTable2(context) {
|
|
|
11833
12296
|
}
|
|
11834
12297
|
async function initializeWorkingTable(context, nodeKinds, seeds) {
|
|
11835
12298
|
const { operation, workingTable, graphId, runId } = context;
|
|
11836
|
-
|
|
11837
|
-
|
|
11838
|
-
|
|
11839
|
-
|
|
11840
|
-
|
|
11841
|
-
|
|
11842
|
-
FROM ${operation.schema.nodesTable} n
|
|
11843
|
-
WHERE n.graph_id = ${graphId}
|
|
11844
|
-
AND ${nodeKindFilter}
|
|
11845
|
-
AND ${operation.nodeTemporalFilter}
|
|
11846
|
-
`);
|
|
12299
|
+
await seedWorkingTableFromNodes(context, nodeKinds, [
|
|
12300
|
+
{ name: "score", value: sql.raw("0.0") },
|
|
12301
|
+
{ name: "next_score", value: sql.raw("0.0") },
|
|
12302
|
+
{ name: "personalization", value: sql.raw("0.0") },
|
|
12303
|
+
{ name: "out_weight", value: sql.raw("0.0") }
|
|
12304
|
+
]);
|
|
11847
12305
|
const workingTableSize = await countWorkingTableRows(context);
|
|
11848
12306
|
if (workingTableSize === 0) {
|
|
11849
12307
|
if (seeds !== void 0) {
|
|
@@ -12079,16 +12537,18 @@ async function readMaximumChange(context, sourceScoreColumn, targetScoreColumn)
|
|
|
12079
12537
|
);
|
|
12080
12538
|
return Number(rows[0]?.value ?? 0);
|
|
12081
12539
|
}
|
|
12082
|
-
async function extractScores(context, state) {
|
|
12540
|
+
async function extractScores(context, state, topK) {
|
|
12083
12541
|
const score = sql.identifier(state.activeScoreColumn);
|
|
12084
12542
|
const nodeId = context.operation.ctx.dialect.binaryText(sql`node_id`);
|
|
12085
12543
|
const nodeKind = context.operation.ctx.dialect.binaryText(sql`node_kind`);
|
|
12544
|
+
const limitClause = topK === void 0 ? sql.empty() : sql`LIMIT ${topK}`;
|
|
12086
12545
|
const rows = await context.backend.execute(
|
|
12087
12546
|
asCompiledRowsSql(sql`
|
|
12088
12547
|
SELECT node_id, node_kind, ${score} AS score
|
|
12089
12548
|
FROM ${context.workingTable}
|
|
12090
12549
|
WHERE graph_id = ${context.graphId} AND run_id = ${context.runId}
|
|
12091
12550
|
ORDER BY ${score} DESC, ${nodeId}, ${nodeKind}
|
|
12551
|
+
${limitClause}
|
|
12092
12552
|
`)
|
|
12093
12553
|
);
|
|
12094
12554
|
return rows.map((row) => ({
|
|
@@ -12671,7 +13131,11 @@ async function executeWeaklyConnectedComponents(ctx, options) {
|
|
|
12671
13131
|
DEFAULT_WCC_MAX_ITERATIONS,
|
|
12672
13132
|
"weaklyConnectedComponents"
|
|
12673
13133
|
);
|
|
12674
|
-
|
|
13134
|
+
assertPositiveSafeIntegerOption(
|
|
13135
|
+
options.minComponentSize,
|
|
13136
|
+
"weaklyConnectedComponents",
|
|
13137
|
+
"minComponentSize"
|
|
13138
|
+
);
|
|
12675
13139
|
const traversalOptions = {
|
|
12676
13140
|
edges: options.edges,
|
|
12677
13141
|
direction: "both",
|
|
@@ -12682,12 +13146,12 @@ async function executeWeaklyConnectedComponents(ctx, options) {
|
|
|
12682
13146
|
algorithm: "weaklyConnectedComponents",
|
|
12683
13147
|
maxIterations,
|
|
12684
13148
|
createWorkingTable: createWorkingTable4,
|
|
12685
|
-
initialize: (context) => initializeWorkingTable2(context, nodeKinds),
|
|
13149
|
+
initialize: (context) => initializeWorkingTable2(context, options.nodeKinds),
|
|
12686
13150
|
runRound: runLabelPropagationRound2,
|
|
12687
13151
|
hasConverged(state) {
|
|
12688
13152
|
return state.changedCount === 0;
|
|
12689
13153
|
},
|
|
12690
|
-
extractResult: extractMemberships2
|
|
13154
|
+
extractResult: (context) => extractMemberships2(context, options.minComponentSize)
|
|
12691
13155
|
});
|
|
12692
13156
|
}
|
|
12693
13157
|
function createWorkingTable4(context) {
|
|
@@ -12707,22 +13171,16 @@ function createWorkingTable4(context) {
|
|
|
12707
13171
|
`;
|
|
12708
13172
|
}
|
|
12709
13173
|
async function initializeWorkingTable2(context, nodeKinds) {
|
|
12710
|
-
|
|
12711
|
-
|
|
12712
|
-
|
|
12713
|
-
|
|
12714
|
-
|
|
12715
|
-
|
|
12716
|
-
|
|
12717
|
-
${graphId}, ${runId}, n.id, n.kind, n.id, n.kind, n.id, n.kind, 0
|
|
12718
|
-
FROM ${operation.schema.nodesTable} n
|
|
12719
|
-
WHERE n.graph_id = ${graphId}
|
|
12720
|
-
AND ${nodeKindFilter}
|
|
12721
|
-
AND ${operation.nodeTemporalFilter}
|
|
12722
|
-
`);
|
|
13174
|
+
await seedWorkingTableFromNodes(context, nodeKinds, [
|
|
13175
|
+
{ name: "label_id", value: sql.raw("n.id") },
|
|
13176
|
+
{ name: "label_kind", value: sql.raw("n.kind") },
|
|
13177
|
+
{ name: "next_label_id", value: sql.raw("n.id") },
|
|
13178
|
+
{ name: "next_label_kind", value: sql.raw("n.kind") },
|
|
13179
|
+
{ name: "improved_round", value: sql.raw("0") }
|
|
13180
|
+
]);
|
|
12723
13181
|
await context.executeTemporary(sql`
|
|
12724
13182
|
CREATE INDEX ${frontierIndexIdentifier(context)}
|
|
12725
|
-
ON ${workingTable} (graph_id, run_id, improved_round)
|
|
13183
|
+
ON ${context.workingTable} (graph_id, run_id, improved_round)
|
|
12726
13184
|
`);
|
|
12727
13185
|
const workingTableSize = await countWorkingTableRows(context);
|
|
12728
13186
|
return { changedCount: workingTableSize, workingTableSize };
|
|
@@ -12822,24 +13280,32 @@ function applyNextLabels2(context, iteration) {
|
|
|
12822
13280
|
`)
|
|
12823
13281
|
);
|
|
12824
13282
|
}
|
|
12825
|
-
async function extractMemberships2(context) {
|
|
13283
|
+
async function extractMemberships2(context, minComponentSize) {
|
|
12826
13284
|
const { operation } = context;
|
|
12827
|
-
const
|
|
12828
|
-
const
|
|
13285
|
+
const labelId = operation.ctx.dialect.binaryText(sql`label_id`);
|
|
13286
|
+
const labelKind = operation.ctx.dialect.binaryText(sql`label_kind`);
|
|
13287
|
+
const componentId = operation.ctx.dialect.binaryText(sql`component_id`);
|
|
13288
|
+
const componentKind = operation.ctx.dialect.binaryText(sql`component_kind`);
|
|
12829
13289
|
const nodeId = operation.ctx.dialect.binaryText(sql`node_id`);
|
|
12830
13290
|
const nodeKind = operation.ctx.dialect.binaryText(sql`node_kind`);
|
|
13291
|
+
const componentSizeFilter = minComponentSize === void 0 ? sql.empty() : sql`WHERE component_size >= ${minComponentSize}`;
|
|
12831
13292
|
const rows = await context.backend.execute(
|
|
12832
13293
|
asCompiledRowsSql(sql`
|
|
12833
|
-
|
|
12834
|
-
|
|
12835
|
-
|
|
12836
|
-
|
|
12837
|
-
|
|
12838
|
-
|
|
12839
|
-
|
|
12840
|
-
|
|
12841
|
-
|
|
12842
|
-
|
|
13294
|
+
WITH memberships AS (
|
|
13295
|
+
SELECT
|
|
13296
|
+
node_id,
|
|
13297
|
+
node_kind,
|
|
13298
|
+
label_id AS component_id,
|
|
13299
|
+
label_kind AS component_kind,
|
|
13300
|
+
COUNT(*) OVER (
|
|
13301
|
+
PARTITION BY ${labelKind}, ${labelId}
|
|
13302
|
+
) AS component_size
|
|
13303
|
+
FROM ${context.workingTable}
|
|
13304
|
+
WHERE graph_id = ${context.graphId} AND run_id = ${context.runId}
|
|
13305
|
+
)
|
|
13306
|
+
SELECT node_id, node_kind, component_id, component_kind, component_size
|
|
13307
|
+
FROM memberships
|
|
13308
|
+
${componentSizeFilter}
|
|
12843
13309
|
ORDER BY ${componentId}, ${componentKind}, ${nodeId}, ${nodeKind}
|
|
12844
13310
|
`)
|
|
12845
13311
|
);
|
|
@@ -13420,6 +13886,87 @@ function createGraphAlgorithms(params) {
|
|
|
13420
13886
|
};
|
|
13421
13887
|
}
|
|
13422
13888
|
|
|
13889
|
+
// src/store/operations/edge-identity.ts
|
|
13890
|
+
function edgeIdentityFromRow(row) {
|
|
13891
|
+
return {
|
|
13892
|
+
kind: row.kind,
|
|
13893
|
+
fromKind: row.from_kind,
|
|
13894
|
+
fromId: row.from_id,
|
|
13895
|
+
toKind: row.to_kind,
|
|
13896
|
+
toId: row.to_id
|
|
13897
|
+
};
|
|
13898
|
+
}
|
|
13899
|
+
function assertEdgeIdentityMatches(id, expected, actual, operation) {
|
|
13900
|
+
const mismatches = [
|
|
13901
|
+
expected.kind === actual.kind ? void 0 : "kind",
|
|
13902
|
+
expected.fromKind === void 0 || expected.fromKind === actual.fromKind ? void 0 : "from.kind",
|
|
13903
|
+
expected.fromId === void 0 || expected.fromId === actual.fromId ? void 0 : "from.id",
|
|
13904
|
+
expected.toKind === void 0 || expected.toKind === actual.toKind ? void 0 : "to.kind",
|
|
13905
|
+
expected.toId === void 0 || expected.toId === actual.toId ? void 0 : "to.id"
|
|
13906
|
+
].filter((path) => path !== void 0);
|
|
13907
|
+
if (mismatches.length === 0) return;
|
|
13908
|
+
throw new ValidationError(
|
|
13909
|
+
`Edge "${id}" belongs to ${actual.kind} (${actual.fromKind}/${actual.fromId} -> ${actual.toKind}/${actual.toId}), not ${expected.kind}` + (expected.fromKind === void 0 ? "." : ` (${expected.fromKind}/${expected.fromId} -> ${expected.toKind}/${expected.toId}).`),
|
|
13910
|
+
{
|
|
13911
|
+
entityType: "edge",
|
|
13912
|
+
kind: expected.kind,
|
|
13913
|
+
operation,
|
|
13914
|
+
id,
|
|
13915
|
+
issues: mismatches.map((path) => ({
|
|
13916
|
+
path,
|
|
13917
|
+
message: "The edge id resolves to a different immutable identity",
|
|
13918
|
+
code: EDGE_IDENTITY_MISMATCH_CODE
|
|
13919
|
+
}))
|
|
13920
|
+
},
|
|
13921
|
+
{
|
|
13922
|
+
suggestion: "Use the edge collection and endpoints that created this id, or choose a new id."
|
|
13923
|
+
}
|
|
13924
|
+
);
|
|
13925
|
+
}
|
|
13926
|
+
|
|
13927
|
+
// src/store/collections/coalesce.ts
|
|
13928
|
+
function findRepeatedUpsertIds(items) {
|
|
13929
|
+
const seen = /* @__PURE__ */ new Set();
|
|
13930
|
+
const repeated = /* @__PURE__ */ new Set();
|
|
13931
|
+
for (const item of items) {
|
|
13932
|
+
if (seen.has(item.id)) repeated.add(item.id);
|
|
13933
|
+
seen.add(item.id);
|
|
13934
|
+
}
|
|
13935
|
+
return repeated;
|
|
13936
|
+
}
|
|
13937
|
+
function windowFieldChanges(requested, stored) {
|
|
13938
|
+
if (requested === void 0) {
|
|
13939
|
+
return false;
|
|
13940
|
+
}
|
|
13941
|
+
if (!isCanonicalIsoDate(requested)) {
|
|
13942
|
+
return true;
|
|
13943
|
+
}
|
|
13944
|
+
return !statedBoundMatchesStored(requested, stored);
|
|
13945
|
+
}
|
|
13946
|
+
function upsertWindowChanges(requested, current) {
|
|
13947
|
+
return windowFieldChanges(requested?.validFrom, current.valid_from) || windowFieldChanges(requested?.validTo, current.valid_to);
|
|
13948
|
+
}
|
|
13949
|
+
function windowAfterUpsertCreate(requested) {
|
|
13950
|
+
return { valid_from: requested.validFrom, valid_to: requested.validTo };
|
|
13951
|
+
}
|
|
13952
|
+
function windowAfterUpsertUpdate(current, requested) {
|
|
13953
|
+
return {
|
|
13954
|
+
valid_from: current.valid_from,
|
|
13955
|
+
valid_to: requested.validTo ?? current.valid_to
|
|
13956
|
+
};
|
|
13957
|
+
}
|
|
13958
|
+
function shouldCoalesceUpsert(existing, options, runDirtyCheck) {
|
|
13959
|
+
const windowChanges = upsertWindowChanges(options, existing);
|
|
13960
|
+
if (runDirtyCheck === void 0 || existing.deleted_at !== void 0 || windowChanges) {
|
|
13961
|
+
return false;
|
|
13962
|
+
}
|
|
13963
|
+
try {
|
|
13964
|
+
return runDirtyCheck().unchanged;
|
|
13965
|
+
} catch {
|
|
13966
|
+
return false;
|
|
13967
|
+
}
|
|
13968
|
+
}
|
|
13969
|
+
|
|
13423
13970
|
// src/store/collections/edge-collection.ts
|
|
13424
13971
|
function narrowEdge(edge) {
|
|
13425
13972
|
return edge;
|
|
@@ -13441,13 +13988,29 @@ function buildCreateEdgeInput(kind, from, to, props, options) {
|
|
|
13441
13988
|
if (options?.validTo !== void 0) input.validTo = options.validTo;
|
|
13442
13989
|
return input;
|
|
13443
13990
|
}
|
|
13444
|
-
function buildUpdateEdgeInput(id, props, options) {
|
|
13445
|
-
const input = { id, props };
|
|
13991
|
+
function buildUpdateEdgeInput(kind, id, props, options) {
|
|
13992
|
+
const input = { id, identity: { kind }, props };
|
|
13993
|
+
if (options?.validTo !== void 0) input.validTo = options.validTo;
|
|
13994
|
+
return input;
|
|
13995
|
+
}
|
|
13996
|
+
function buildUpsertUpdateEdgeInput(kind, id, from, to, props, options) {
|
|
13997
|
+
const input = {
|
|
13998
|
+
id,
|
|
13999
|
+
identity: {
|
|
14000
|
+
kind,
|
|
14001
|
+
fromKind: from.kind,
|
|
14002
|
+
fromId: from.id,
|
|
14003
|
+
toKind: to.kind,
|
|
14004
|
+
toId: to.id
|
|
14005
|
+
},
|
|
14006
|
+
props
|
|
14007
|
+
};
|
|
14008
|
+
if (options?.validFrom !== void 0) input.validFrom = options.validFrom;
|
|
13446
14009
|
if (options?.validTo !== void 0) input.validTo = options.validTo;
|
|
13447
14010
|
return input;
|
|
13448
14011
|
}
|
|
13449
14012
|
function endpointKey(endpointKind, id) {
|
|
13450
|
-
return
|
|
14013
|
+
return encodeTupleKey([endpointKind, id]);
|
|
13451
14014
|
}
|
|
13452
14015
|
function groupEndpointIdsByKind(references) {
|
|
13453
14016
|
const referencesByKind = groupBy(references, (ref) => ref.kind);
|
|
@@ -13494,6 +14057,7 @@ function createEdgeCollection(config) {
|
|
|
13494
14057
|
executeUpdate: executeEdgeUpdate2,
|
|
13495
14058
|
executeUpsertUpdate: executeEdgeUpsertUpdate2,
|
|
13496
14059
|
executeDelete: executeEdgeDelete2,
|
|
14060
|
+
executeDeleteBatch: executeEdgeDeleteBatch2,
|
|
13497
14061
|
executeHardDelete: executeEdgeHardDelete2,
|
|
13498
14062
|
temporalRowMatcher
|
|
13499
14063
|
} = config;
|
|
@@ -13602,7 +14166,7 @@ function createEdgeCollection(config) {
|
|
|
13602
14166
|
},
|
|
13603
14167
|
async update(id, props, options) {
|
|
13604
14168
|
const result = await executeEdgeUpdate2(
|
|
13605
|
-
buildUpdateEdgeInput(id, props, options),
|
|
14169
|
+
buildUpdateEdgeInput(kind, id, props, options),
|
|
13606
14170
|
backend
|
|
13607
14171
|
);
|
|
13608
14172
|
return narrowEdge(result);
|
|
@@ -13642,10 +14206,10 @@ function createEdgeCollection(config) {
|
|
|
13642
14206
|
};
|
|
13643
14207
|
},
|
|
13644
14208
|
async delete(id) {
|
|
13645
|
-
await executeEdgeDelete2(id, backend);
|
|
14209
|
+
await executeEdgeDelete2(kind, id, backend);
|
|
13646
14210
|
},
|
|
13647
14211
|
async hardDelete(id) {
|
|
13648
|
-
await executeEdgeHardDelete2(id, backend);
|
|
14212
|
+
await executeEdgeHardDelete2(kind, id, backend);
|
|
13649
14213
|
},
|
|
13650
14214
|
async find(filter, temporal) {
|
|
13651
14215
|
const untypedFilter = filter;
|
|
@@ -13704,6 +14268,20 @@ function createEdgeCollection(config) {
|
|
|
13704
14268
|
const toUpdate = [];
|
|
13705
14269
|
const pending = /* @__PURE__ */ new Map();
|
|
13706
14270
|
const deferred = [];
|
|
14271
|
+
const repeatedIds = config.upsertDirtyCheck === void 0 ? /* @__PURE__ */ new Set() : findRepeatedUpsertIds(items);
|
|
14272
|
+
function runDirtyCheck(edgeKind, id, currentProps, inputProps) {
|
|
14273
|
+
if (config.upsertDirtyCheck === void 0) return void 0;
|
|
14274
|
+
try {
|
|
14275
|
+
return config.upsertDirtyCheck(
|
|
14276
|
+
edgeKind,
|
|
14277
|
+
id,
|
|
14278
|
+
currentProps,
|
|
14279
|
+
inputProps
|
|
14280
|
+
);
|
|
14281
|
+
} catch {
|
|
14282
|
+
return void 0;
|
|
14283
|
+
}
|
|
14284
|
+
}
|
|
13707
14285
|
let itemIndex = 0;
|
|
13708
14286
|
for (const item of items) {
|
|
13709
14287
|
const pendingEntry = pending.get(item.id);
|
|
@@ -13720,25 +14298,48 @@ function createEdgeCollection(config) {
|
|
|
13720
14298
|
item
|
|
13721
14299
|
)
|
|
13722
14300
|
});
|
|
14301
|
+
pending.set(item.id, {
|
|
14302
|
+
identity: {
|
|
14303
|
+
kind,
|
|
14304
|
+
fromKind: item.from.kind,
|
|
14305
|
+
fromId: item.from.id,
|
|
14306
|
+
toKind: item.to.kind,
|
|
14307
|
+
toId: item.to.id
|
|
14308
|
+
},
|
|
14309
|
+
props: repeatedIds.has(item.id) ? runDirtyCheck(kind, item.id, {}, inputProps)?.validatedProps : void 0,
|
|
14310
|
+
window: windowAfterUpsertCreate(item),
|
|
14311
|
+
sourceIndex: itemIndex
|
|
14312
|
+
});
|
|
13723
14313
|
itemIndex++;
|
|
13724
14314
|
continue;
|
|
13725
14315
|
}
|
|
13726
14316
|
const deletedAt = pendingEntry === void 0 ? requireDefined(original).deleted_at : void 0;
|
|
13727
|
-
|
|
13728
|
-
|
|
13729
|
-
|
|
13730
|
-
|
|
13731
|
-
|
|
13732
|
-
|
|
13733
|
-
|
|
13734
|
-
|
|
13735
|
-
|
|
13736
|
-
|
|
13737
|
-
|
|
13738
|
-
|
|
13739
|
-
|
|
13740
|
-
|
|
13741
|
-
const
|
|
14317
|
+
const expectedIdentity = {
|
|
14318
|
+
kind,
|
|
14319
|
+
fromKind: item.from.kind,
|
|
14320
|
+
fromId: item.from.id,
|
|
14321
|
+
toKind: item.to.kind,
|
|
14322
|
+
toId: item.to.id
|
|
14323
|
+
};
|
|
14324
|
+
const actualIdentity = pendingEntry === void 0 ? edgeIdentityFromRow(requireDefined(original)) : pendingEntry.identity;
|
|
14325
|
+
assertEdgeIdentityMatches(
|
|
14326
|
+
item.id,
|
|
14327
|
+
expectedIdentity,
|
|
14328
|
+
actualIdentity,
|
|
14329
|
+
"update"
|
|
14330
|
+
);
|
|
14331
|
+
const currentProps = pendingEntry === void 0 ? rowPropsToObject(requireDefined(original).props) : pendingEntry.props;
|
|
14332
|
+
const dirty = currentProps === void 0 ? void 0 : runDirtyCheck(
|
|
14333
|
+
original?.kind ?? kind,
|
|
14334
|
+
item.id,
|
|
14335
|
+
currentProps,
|
|
14336
|
+
inputProps
|
|
14337
|
+
);
|
|
14338
|
+
const currentWindow = pendingEntry === void 0 ? {
|
|
14339
|
+
valid_from: requireDefined(original).valid_from,
|
|
14340
|
+
valid_to: requireDefined(original).valid_to
|
|
14341
|
+
} : pendingEntry.window;
|
|
14342
|
+
const coalesce = dirty?.unchanged === true && deletedAt === void 0 && !upsertWindowChanges(item, currentWindow);
|
|
13742
14343
|
if (coalesce) {
|
|
13743
14344
|
if (pendingEntry === void 0) {
|
|
13744
14345
|
results2[itemIndex] = narrowEdge(
|
|
@@ -13753,15 +14354,25 @@ function createEdgeCollection(config) {
|
|
|
13753
14354
|
} else {
|
|
13754
14355
|
toUpdate.push({
|
|
13755
14356
|
index: itemIndex,
|
|
13756
|
-
input:
|
|
14357
|
+
input: buildUpsertUpdateEdgeInput(
|
|
14358
|
+
kind,
|
|
14359
|
+
item.id,
|
|
14360
|
+
item.from,
|
|
14361
|
+
item.to,
|
|
14362
|
+
inputProps,
|
|
14363
|
+
item
|
|
14364
|
+
),
|
|
13757
14365
|
clearDeleted: deletedAt !== void 0
|
|
13758
14366
|
});
|
|
13759
|
-
|
|
13760
|
-
|
|
13761
|
-
|
|
13762
|
-
|
|
13763
|
-
|
|
13764
|
-
|
|
14367
|
+
pending.set(item.id, {
|
|
14368
|
+
identity: actualIdentity,
|
|
14369
|
+
props: dirty?.validatedProps,
|
|
14370
|
+
// A resurrection rewrites both bounds only when it NAMES the lower
|
|
14371
|
+
// one (buildUpdateEdge's clearDeleted leg); omitting `validFrom`
|
|
14372
|
+
// keeps the stored window, exactly like a live-row update.
|
|
14373
|
+
window: deletedAt !== void 0 && item.validFrom !== void 0 ? windowAfterUpsertCreate(item) : windowAfterUpsertUpdate(currentWindow, item),
|
|
14374
|
+
sourceIndex: itemIndex
|
|
14375
|
+
});
|
|
13765
14376
|
}
|
|
13766
14377
|
itemIndex++;
|
|
13767
14378
|
}
|
|
@@ -13785,7 +14396,10 @@ function createEdgeCollection(config) {
|
|
|
13785
14396
|
}
|
|
13786
14397
|
return { results: results2, mutations: toCreate.length + toUpdate.length };
|
|
13787
14398
|
};
|
|
13788
|
-
const { results, mutations } =
|
|
14399
|
+
const { results, mutations } = await runOptionallyInTransaction(
|
|
14400
|
+
backend,
|
|
14401
|
+
(target) => upsertAll(target)
|
|
14402
|
+
);
|
|
13789
14403
|
await config.maybeRefreshStatisticsAfterBulk?.(mutations);
|
|
13790
14404
|
return results;
|
|
13791
14405
|
},
|
|
@@ -13803,16 +14417,7 @@ function createEdgeCollection(config) {
|
|
|
13803
14417
|
},
|
|
13804
14418
|
async bulkDelete(ids) {
|
|
13805
14419
|
if (ids.length === 0) return;
|
|
13806
|
-
|
|
13807
|
-
for (const id of ids) {
|
|
13808
|
-
await executeEdgeDelete2(id, target);
|
|
13809
|
-
}
|
|
13810
|
-
};
|
|
13811
|
-
if (backend.capabilities.transactions && "transaction" in backend) {
|
|
13812
|
-
await backend.transaction(async (txBackend) => deleteAll(txBackend));
|
|
13813
|
-
return;
|
|
13814
|
-
}
|
|
13815
|
-
await deleteAll(backend);
|
|
14420
|
+
await executeEdgeDeleteBatch2(kind, ids, backend);
|
|
13816
14421
|
},
|
|
13817
14422
|
async findByEndpoints(from, to, options, temporal) {
|
|
13818
14423
|
const result = await config.executeFindByEndpoints(
|
|
@@ -13892,18 +14497,6 @@ function createEdgeCollection(config) {
|
|
|
13892
14497
|
};
|
|
13893
14498
|
}
|
|
13894
14499
|
|
|
13895
|
-
// src/store/collections/coalesce.ts
|
|
13896
|
-
function shouldCoalesceUpsert(existing, options, runDirtyCheck) {
|
|
13897
|
-
if (runDirtyCheck === void 0 || existing.deleted_at !== void 0 || options?.validFrom !== void 0 || options?.validTo !== void 0) {
|
|
13898
|
-
return false;
|
|
13899
|
-
}
|
|
13900
|
-
try {
|
|
13901
|
-
return runDirtyCheck().unchanged;
|
|
13902
|
-
} catch {
|
|
13903
|
-
return false;
|
|
13904
|
-
}
|
|
13905
|
-
}
|
|
13906
|
-
|
|
13907
14500
|
// src/store/collections/node-collection.ts
|
|
13908
14501
|
function narrowNode(node) {
|
|
13909
14502
|
return node;
|
|
@@ -13911,6 +14504,34 @@ function narrowNode(node) {
|
|
|
13911
14504
|
function narrowNodes(nodes) {
|
|
13912
14505
|
return nodes;
|
|
13913
14506
|
}
|
|
14507
|
+
function createFieldMember(accessor) {
|
|
14508
|
+
const selectField = (name) => accessor.field(name);
|
|
14509
|
+
return new Proxy(selectField, {
|
|
14510
|
+
get(target, property, receiver) {
|
|
14511
|
+
if (Reflect.has(target, property)) {
|
|
14512
|
+
const ownProperty = Reflect.get(target, property, receiver);
|
|
14513
|
+
return ownProperty;
|
|
14514
|
+
}
|
|
14515
|
+
return accessor.field("field")[property];
|
|
14516
|
+
}
|
|
14517
|
+
});
|
|
14518
|
+
}
|
|
14519
|
+
function evaluateNodePredicate(accessor, predicate) {
|
|
14520
|
+
const collectionAccessor = new Proxy(
|
|
14521
|
+
{},
|
|
14522
|
+
{
|
|
14523
|
+
get(_target, property) {
|
|
14524
|
+
if (typeof property === "symbol") return;
|
|
14525
|
+
if (property === "id" || property === "kind" || property === "$fulltext") {
|
|
14526
|
+
return accessor[property];
|
|
14527
|
+
}
|
|
14528
|
+
if (property === "field") return createFieldMember(accessor);
|
|
14529
|
+
return accessor.field(property);
|
|
14530
|
+
}
|
|
14531
|
+
}
|
|
14532
|
+
);
|
|
14533
|
+
return predicate(collectionAccessor);
|
|
14534
|
+
}
|
|
13914
14535
|
function buildCreateInput(kind, props, options) {
|
|
13915
14536
|
const input = { kind, props };
|
|
13916
14537
|
if (options?.id !== void 0) input.id = options.id;
|
|
@@ -13923,6 +14544,12 @@ function buildUpdateInput(kind, id, props, options) {
|
|
|
13923
14544
|
if (options?.validTo !== void 0) input.validTo = options.validTo;
|
|
13924
14545
|
return input;
|
|
13925
14546
|
}
|
|
14547
|
+
function buildUpsertUpdateInput(kind, id, props, options) {
|
|
14548
|
+
const input = { kind, id, props };
|
|
14549
|
+
if (options?.validFrom !== void 0) input.validFrom = options.validFrom;
|
|
14550
|
+
if (options?.validTo !== void 0) input.validTo = options.validTo;
|
|
14551
|
+
return input;
|
|
14552
|
+
}
|
|
13926
14553
|
function mapBulkNodeInputs(kind, items) {
|
|
13927
14554
|
return items.map((item) => buildCreateInput(kind, item.props, item));
|
|
13928
14555
|
}
|
|
@@ -13937,8 +14564,10 @@ function createNodeCollection(config) {
|
|
|
13937
14564
|
executeCreateNoReturnBatch: executeNodeCreateNoReturnBatch2,
|
|
13938
14565
|
executeCreateBatch: executeNodeCreateBatch2,
|
|
13939
14566
|
executeUpdate: executeNodeUpdate2,
|
|
14567
|
+
executeUpdateWhere: executeNodeUpdateWhere2,
|
|
13940
14568
|
executeUpsertUpdate: executeNodeUpsertUpdate2,
|
|
13941
14569
|
executeDelete: executeNodeDelete2,
|
|
14570
|
+
executeDeleteBatch: executeNodeDeleteBatch2,
|
|
13942
14571
|
executeHardDelete: executeNodeHardDelete2,
|
|
13943
14572
|
temporalRowMatcher,
|
|
13944
14573
|
createQuery,
|
|
@@ -13983,6 +14612,85 @@ function createNodeCollection(config) {
|
|
|
13983
14612
|
);
|
|
13984
14613
|
return narrowNode(result);
|
|
13985
14614
|
},
|
|
14615
|
+
async updateWhere(params) {
|
|
14616
|
+
if (createQuery === void 0) {
|
|
14617
|
+
throw new ConfigurationError(
|
|
14618
|
+
`store.nodes.${kind}.updateWhere() requires a query-capable store`,
|
|
14619
|
+
{ kind, operation: "updateWhere" }
|
|
14620
|
+
);
|
|
14621
|
+
}
|
|
14622
|
+
const exists = params.exists ?? [];
|
|
14623
|
+
if (params.where === void 0 && exists.length === 0 && !params.all) {
|
|
14624
|
+
throw new ConfigurationError(
|
|
14625
|
+
`store.nodes.${kind}.updateWhere() requires where, exists, or explicit all: true`,
|
|
14626
|
+
{ kind, operation: "updateWhere" }
|
|
14627
|
+
);
|
|
14628
|
+
}
|
|
14629
|
+
const rootAlias = "update_candidate";
|
|
14630
|
+
const readInstant = nowIso();
|
|
14631
|
+
let base = createQuery().fromDynamic(kind, rootAlias).temporal("asOf", readInstant);
|
|
14632
|
+
const where = params.where;
|
|
14633
|
+
if (where !== void 0) {
|
|
14634
|
+
base = base.whereNode(
|
|
14635
|
+
rootAlias,
|
|
14636
|
+
(accessor) => evaluateNodePredicate(accessor, where)
|
|
14637
|
+
);
|
|
14638
|
+
}
|
|
14639
|
+
const candidateIdColumn = `${rootAlias}_id`;
|
|
14640
|
+
const compiledBranches = [];
|
|
14641
|
+
for (const [index, relation] of exists.entries()) {
|
|
14642
|
+
const edgeAlias = `update_edge_${index}`;
|
|
14643
|
+
const relatedAlias = `update_related_${index}`;
|
|
14644
|
+
const relationRoot = createQuery().fromDynamic(kind, rootAlias).temporal("asOf", readInstant);
|
|
14645
|
+
let traversal = relationRoot.traverseDynamic(
|
|
14646
|
+
relation.edgeKind,
|
|
14647
|
+
edgeAlias,
|
|
14648
|
+
{
|
|
14649
|
+
direction: relation.direction,
|
|
14650
|
+
expand: "none"
|
|
14651
|
+
}
|
|
14652
|
+
);
|
|
14653
|
+
if (relation.whereEdge !== void 0) {
|
|
14654
|
+
traversal = traversal.whereEdge(edgeAlias, relation.whereEdge);
|
|
14655
|
+
}
|
|
14656
|
+
let related = traversal.toDynamic(relation.relatedKind, relatedAlias);
|
|
14657
|
+
if (relation.whereRelated !== void 0) {
|
|
14658
|
+
related = related.whereNode(relatedAlias, relation.whereRelated);
|
|
14659
|
+
}
|
|
14660
|
+
compiledBranches.push(
|
|
14661
|
+
related.select(
|
|
14662
|
+
(ctx) => ctx[rootAlias]?.id
|
|
14663
|
+
).compile()
|
|
14664
|
+
);
|
|
14665
|
+
}
|
|
14666
|
+
compiledBranches.unshift(
|
|
14667
|
+
base.select((ctx) => ctx[rootAlias]?.id).compile()
|
|
14668
|
+
);
|
|
14669
|
+
const projectCandidateId = (branch, index) => sql`
|
|
14670
|
+
SELECT ${sql.identifier(candidateIdColumn)}
|
|
14671
|
+
FROM (${branch}) AS ${sql.identifier(`update_branch_${index}`)}
|
|
14672
|
+
`;
|
|
14673
|
+
let combinedCandidates = projectCandidateId(
|
|
14674
|
+
requireDefined(compiledBranches[0]),
|
|
14675
|
+
0
|
|
14676
|
+
);
|
|
14677
|
+
for (let index = 1; index < compiledBranches.length; index++) {
|
|
14678
|
+
combinedCandidates = sql`${combinedCandidates} INTERSECT ${projectCandidateId(
|
|
14679
|
+
requireDefined(compiledBranches[index]),
|
|
14680
|
+
index
|
|
14681
|
+
)}`;
|
|
14682
|
+
}
|
|
14683
|
+
const compiledCandidates = asCompiledSelectSql(combinedCandidates);
|
|
14684
|
+
const result = await executeNodeUpdateWhere2(
|
|
14685
|
+
kind,
|
|
14686
|
+
params.patch,
|
|
14687
|
+
compiledCandidates,
|
|
14688
|
+
candidateIdColumn,
|
|
14689
|
+
backend
|
|
14690
|
+
);
|
|
14691
|
+
await config.maybeRefreshStatisticsAfterBulk?.(result.affectedCount);
|
|
14692
|
+
return result;
|
|
14693
|
+
},
|
|
13986
14694
|
async delete(id) {
|
|
13987
14695
|
await executeNodeDelete2(kind, id, backend);
|
|
13988
14696
|
},
|
|
@@ -14030,20 +14738,30 @@ function createNodeCollection(config) {
|
|
|
14030
14738
|
},
|
|
14031
14739
|
async upsertByIdFromRecord(id, data, options) {
|
|
14032
14740
|
const existing = await backend.getNode(graphId, kind, id);
|
|
14033
|
-
|
|
14741
|
+
const coalesces = (row) => {
|
|
14034
14742
|
const runDirtyCheck = config.upsertDirtyCheck && (() => requireDefined(config.upsertDirtyCheck)(
|
|
14035
14743
|
kind,
|
|
14036
14744
|
id,
|
|
14037
|
-
rowPropsToObject(
|
|
14745
|
+
rowPropsToObject(row.props),
|
|
14038
14746
|
data
|
|
14039
14747
|
));
|
|
14040
|
-
|
|
14041
|
-
|
|
14748
|
+
return shouldCoalesceUpsert(row, options, runDirtyCheck);
|
|
14749
|
+
};
|
|
14750
|
+
const decision = existing !== void 0 && coalesces(existing) ? await runOptionallyInTransaction(
|
|
14751
|
+
backend,
|
|
14752
|
+
async (target) => {
|
|
14753
|
+
const confirmed = await target.getNode(graphId, kind, id);
|
|
14754
|
+
return confirmed !== void 0 && coalesces(confirmed) ? { verdict: "skip", row: confirmed } : { verdict: "write", row: confirmed };
|
|
14042
14755
|
}
|
|
14756
|
+
) : { verdict: "write", row: existing };
|
|
14757
|
+
if (decision.verdict === "skip") {
|
|
14758
|
+
return narrowNode(rowToNode2(decision.row));
|
|
14759
|
+
}
|
|
14760
|
+
if (decision.row !== void 0) {
|
|
14043
14761
|
const result2 = await executeNodeUpdate2(
|
|
14044
|
-
|
|
14762
|
+
buildUpsertUpdateInput(kind, id, data, options),
|
|
14045
14763
|
backend,
|
|
14046
|
-
{ clearDeleted:
|
|
14764
|
+
{ clearDeleted: decision.row.deleted_at !== void 0 }
|
|
14047
14765
|
);
|
|
14048
14766
|
return narrowNode(result2);
|
|
14049
14767
|
}
|
|
@@ -14089,6 +14807,15 @@ function createNodeCollection(config) {
|
|
|
14089
14807
|
const toUpdate = [];
|
|
14090
14808
|
const pending = /* @__PURE__ */ new Map();
|
|
14091
14809
|
const deferred = [];
|
|
14810
|
+
const repeatedIds = config.upsertDirtyCheck === void 0 ? /* @__PURE__ */ new Set() : findRepeatedUpsertIds(items);
|
|
14811
|
+
function runDirtyCheck(id, currentProps, inputProps) {
|
|
14812
|
+
if (config.upsertDirtyCheck === void 0) return void 0;
|
|
14813
|
+
try {
|
|
14814
|
+
return config.upsertDirtyCheck(kind, id, currentProps, inputProps);
|
|
14815
|
+
} catch {
|
|
14816
|
+
return void 0;
|
|
14817
|
+
}
|
|
14818
|
+
}
|
|
14092
14819
|
let itemIndex = 0;
|
|
14093
14820
|
for (const item of items) {
|
|
14094
14821
|
const pendingEntry = pending.get(item.id);
|
|
@@ -14098,25 +14825,22 @@ function createNodeCollection(config) {
|
|
|
14098
14825
|
index: itemIndex,
|
|
14099
14826
|
input: buildCreateInput(kind, item.props, item)
|
|
14100
14827
|
});
|
|
14828
|
+
pending.set(item.id, {
|
|
14829
|
+
props: repeatedIds.has(item.id) ? runDirtyCheck(item.id, {}, item.props)?.validatedProps : void 0,
|
|
14830
|
+
window: windowAfterUpsertCreate(item),
|
|
14831
|
+
sourceIndex: itemIndex
|
|
14832
|
+
});
|
|
14101
14833
|
itemIndex++;
|
|
14102
14834
|
continue;
|
|
14103
14835
|
}
|
|
14104
14836
|
const deletedAt = pendingEntry === void 0 ? requireDefined(original).deleted_at : void 0;
|
|
14105
|
-
|
|
14106
|
-
|
|
14107
|
-
|
|
14108
|
-
|
|
14109
|
-
|
|
14110
|
-
|
|
14111
|
-
|
|
14112
|
-
currentProps,
|
|
14113
|
-
item.props
|
|
14114
|
-
);
|
|
14115
|
-
} catch {
|
|
14116
|
-
dirty = void 0;
|
|
14117
|
-
}
|
|
14118
|
-
}
|
|
14119
|
-
const coalesce = dirty?.unchanged === true && deletedAt === void 0 && item.validFrom === void 0 && item.validTo === void 0;
|
|
14837
|
+
const currentProps = pendingEntry === void 0 ? rowPropsToObject(requireDefined(original).props) : pendingEntry.props;
|
|
14838
|
+
const dirty = currentProps === void 0 ? void 0 : runDirtyCheck(item.id, currentProps, item.props);
|
|
14839
|
+
const currentWindow = pendingEntry === void 0 ? {
|
|
14840
|
+
valid_from: requireDefined(original).valid_from,
|
|
14841
|
+
valid_to: requireDefined(original).valid_to
|
|
14842
|
+
} : pendingEntry.window;
|
|
14843
|
+
const coalesce = dirty?.unchanged === true && deletedAt === void 0 && !upsertWindowChanges(item, currentWindow);
|
|
14120
14844
|
if (coalesce) {
|
|
14121
14845
|
if (pendingEntry === void 0) {
|
|
14122
14846
|
results2[itemIndex] = narrowNode(
|
|
@@ -14131,15 +14855,17 @@ function createNodeCollection(config) {
|
|
|
14131
14855
|
} else {
|
|
14132
14856
|
toUpdate.push({
|
|
14133
14857
|
index: itemIndex,
|
|
14134
|
-
input:
|
|
14858
|
+
input: buildUpsertUpdateInput(kind, item.id, item.props, item),
|
|
14135
14859
|
clearDeleted: deletedAt !== void 0
|
|
14136
14860
|
});
|
|
14137
|
-
|
|
14138
|
-
|
|
14139
|
-
|
|
14140
|
-
|
|
14141
|
-
|
|
14142
|
-
|
|
14861
|
+
pending.set(item.id, {
|
|
14862
|
+
props: dirty?.validatedProps,
|
|
14863
|
+
// A resurrecting update rewrites BOTH bounds (buildUpdateNode's
|
|
14864
|
+
// clearDeleted leg stamps `valid_from` and reopens `valid_to`);
|
|
14865
|
+
// a live-row update moves only `valid_to`.
|
|
14866
|
+
window: deletedAt === void 0 ? windowAfterUpsertUpdate(currentWindow, item) : windowAfterUpsertCreate(item),
|
|
14867
|
+
sourceIndex: itemIndex
|
|
14868
|
+
});
|
|
14143
14869
|
}
|
|
14144
14870
|
itemIndex++;
|
|
14145
14871
|
}
|
|
@@ -14163,7 +14889,10 @@ function createNodeCollection(config) {
|
|
|
14163
14889
|
}
|
|
14164
14890
|
return { results: results2, mutations: toCreate.length + toUpdate.length };
|
|
14165
14891
|
};
|
|
14166
|
-
const { results, mutations } =
|
|
14892
|
+
const { results, mutations } = await runOptionallyInTransaction(
|
|
14893
|
+
backend,
|
|
14894
|
+
(target) => upsertAll(target)
|
|
14895
|
+
);
|
|
14167
14896
|
await config.maybeRefreshStatisticsAfterBulk?.(mutations);
|
|
14168
14897
|
return results;
|
|
14169
14898
|
},
|
|
@@ -14181,16 +14910,7 @@ function createNodeCollection(config) {
|
|
|
14181
14910
|
},
|
|
14182
14911
|
async bulkDelete(ids) {
|
|
14183
14912
|
if (ids.length === 0) return;
|
|
14184
|
-
|
|
14185
|
-
for (const id of ids) {
|
|
14186
|
-
await executeNodeDelete2(kind, id, target);
|
|
14187
|
-
}
|
|
14188
|
-
};
|
|
14189
|
-
if (backend.capabilities.transactions && "transaction" in backend) {
|
|
14190
|
-
await backend.transaction(async (txBackend) => deleteAll(txBackend));
|
|
14191
|
-
return;
|
|
14192
|
-
}
|
|
14193
|
-
await deleteAll(backend);
|
|
14913
|
+
await executeNodeDeleteBatch2(kind, ids, backend);
|
|
14194
14914
|
},
|
|
14195
14915
|
async findByConstraint(constraintName, props) {
|
|
14196
14916
|
const result = await executeFindByConstraint(
|
|
@@ -14333,10 +15053,12 @@ var HISTORY_STORE_BACKEND_KEYS = [
|
|
|
14333
15053
|
"deleteEdge",
|
|
14334
15054
|
"deleteEdgesBatch",
|
|
14335
15055
|
"deleteEmbedding",
|
|
15056
|
+
"deleteEmbeddingBatch",
|
|
14336
15057
|
"deleteFulltext",
|
|
14337
15058
|
"deleteFulltextBatch",
|
|
14338
15059
|
"deleteNode",
|
|
14339
15060
|
"deleteUnique",
|
|
15061
|
+
"hardDeleteUniquesByNodeIds",
|
|
14340
15062
|
"deleteVectorSlotContribution",
|
|
14341
15063
|
"dialect",
|
|
14342
15064
|
"dropVectorIndex",
|
|
@@ -14354,6 +15076,7 @@ var HISTORY_STORE_BACKEND_KEYS = [
|
|
|
14354
15076
|
"executeTemporaryStatement",
|
|
14355
15077
|
"findEdgesByKind",
|
|
14356
15078
|
"findEdgesByEndpointSet",
|
|
15079
|
+
"findEdgesByHeterogeneousEndpointSet",
|
|
14357
15080
|
"findEdgesConnectedTo",
|
|
14358
15081
|
"findNodesByKind",
|
|
14359
15082
|
"fulltextSearch",
|
|
@@ -14373,6 +15096,7 @@ var HISTORY_STORE_BACKEND_KEYS = [
|
|
|
14373
15096
|
"hardDeleteEdge",
|
|
14374
15097
|
"hardDeleteEdgesBatch",
|
|
14375
15098
|
"hardDeleteNode",
|
|
15099
|
+
"hardDeleteUniquesByNodeIds",
|
|
14376
15100
|
"hybridSearch",
|
|
14377
15101
|
"insertEdge",
|
|
14378
15102
|
"insertEdgeNoReturn",
|
|
@@ -14384,6 +15108,7 @@ var HISTORY_STORE_BACKEND_KEYS = [
|
|
|
14384
15108
|
"insertNodesBatchReturning",
|
|
14385
15109
|
"insertUnique",
|
|
14386
15110
|
"insertUniqueBatch",
|
|
15111
|
+
"probeContributions",
|
|
14387
15112
|
"recordContributionMaterialization",
|
|
14388
15113
|
"recordIndexMaterialization",
|
|
14389
15114
|
"recordKindRemoval",
|
|
@@ -14394,6 +15119,7 @@ var HISTORY_STORE_BACKEND_KEYS = [
|
|
|
14394
15119
|
"tableNames",
|
|
14395
15120
|
"updateEdge",
|
|
14396
15121
|
"updateNode",
|
|
15122
|
+
"updateNodeSet",
|
|
14397
15123
|
"upsertEmbedding",
|
|
14398
15124
|
"upsertEmbeddingBatch",
|
|
14399
15125
|
"upsertFulltext",
|
|
@@ -15293,7 +16019,7 @@ async function reconcilePendingRemovals(context) {
|
|
|
15293
16019
|
const priorEntries = priorSchema[entity === "node" ? "nodes" : "edges"];
|
|
15294
16020
|
const currentEntries = currentSchema[entity === "node" ? "nodes" : "edges"];
|
|
15295
16021
|
for (const kindName of Object.keys(priorEntries)) {
|
|
15296
|
-
if (kindName
|
|
16022
|
+
if (hasOwnKey(currentEntries, kindName)) continue;
|
|
15297
16023
|
if (recorded.has(kindRemovalKey(entity, kindName, currentRow.version))) {
|
|
15298
16024
|
continue;
|
|
15299
16025
|
}
|
|
@@ -15614,22 +16340,29 @@ function literal(value) {
|
|
|
15614
16340
|
|
|
15615
16341
|
// src/store/operations/edge-operations.ts
|
|
15616
16342
|
function getEdgeRegistration(graph, kind) {
|
|
16343
|
+
if (!hasOwnKey(graph.edges, kind)) throw new KindNotFoundError(kind, "edge");
|
|
15617
16344
|
const registration = graph.edges[kind];
|
|
15618
16345
|
if (registration === void 0) throw new KindNotFoundError(kind, "edge");
|
|
15619
16346
|
return registration;
|
|
15620
16347
|
}
|
|
15621
16348
|
function buildEdgeEndpointCacheKey(graphId, kind, id) {
|
|
15622
|
-
return
|
|
16349
|
+
return encodeTupleKey([graphId, kind, id]);
|
|
15623
16350
|
}
|
|
15624
16351
|
function buildEdgeFromCacheKey(graphId, edgeKind, fromKind, fromId) {
|
|
15625
|
-
return
|
|
16352
|
+
return encodeTupleKey([graphId, edgeKind, fromKind, fromId]);
|
|
15626
16353
|
}
|
|
15627
16354
|
function buildEdgeBetweenCacheKey(graphId, edgeKind, fromKind, fromId, toKind, toId) {
|
|
15628
|
-
return
|
|
16355
|
+
return encodeTupleKey([graphId, edgeKind, fromKind, fromId, toKind, toId]);
|
|
15629
16356
|
}
|
|
15630
16357
|
function buildCountEdgesFromCacheKey(params) {
|
|
15631
16358
|
const activeOnly = params.activeOnly === true ? "1" : "0";
|
|
15632
|
-
return
|
|
16359
|
+
return encodeTupleKey([
|
|
16360
|
+
params.graphId,
|
|
16361
|
+
params.edgeKind,
|
|
16362
|
+
params.fromKind,
|
|
16363
|
+
params.fromId,
|
|
16364
|
+
activeOnly
|
|
16365
|
+
]);
|
|
15633
16366
|
}
|
|
15634
16367
|
function buildInsertEdgeParams(graphId, id, kind, fromKind, fromId, toKind, toId, props, validFrom, validTo) {
|
|
15635
16368
|
const insertParams = {
|
|
@@ -15787,6 +16520,7 @@ async function validateAndPrepareEdgeCreate(ctx, input, id, backend) {
|
|
|
15787
16520
|
"validFrom"
|
|
15788
16521
|
);
|
|
15789
16522
|
const validTo = validateOptionalCanonicalIsoDate(input.validTo, "validTo");
|
|
16523
|
+
assertOrderedValidityWindow(`edge "${id}"`, validFrom, validTo);
|
|
15790
16524
|
const cardinality = registration.cardinality ?? "many";
|
|
15791
16525
|
const constraintContext = {
|
|
15792
16526
|
graphId: ctx.graphId,
|
|
@@ -15819,29 +16553,84 @@ async function validateAndPrepareEdgeCreate(ctx, input, id, backend) {
|
|
|
15819
16553
|
)
|
|
15820
16554
|
};
|
|
15821
16555
|
}
|
|
16556
|
+
function edgeCardinality(ctx, kind) {
|
|
16557
|
+
if (!hasOwnKey(ctx.graph.edges, kind)) return "many";
|
|
16558
|
+
return getEdgeRegistration(ctx.graph, kind).cardinality ?? "many";
|
|
16559
|
+
}
|
|
16560
|
+
var EdgeConvergenceRaced = class extends Error {
|
|
16561
|
+
constructor(kind) {
|
|
16562
|
+
super(
|
|
16563
|
+
`A competing writer claimed the ${kind} match key; re-resolving it. This is internal to getOrCreateByEndpoints and is never returned to a caller.`
|
|
16564
|
+
);
|
|
16565
|
+
this.name = "EdgeConvergenceRaced";
|
|
16566
|
+
}
|
|
16567
|
+
};
|
|
15822
16568
|
async function executeEdgeCreateInternal(ctx, input, backend, options) {
|
|
15823
16569
|
const kind = input.kind;
|
|
15824
16570
|
const id = input.id ?? generateId();
|
|
15825
16571
|
const opContext = ctx.createOperationContext("create", "edge", kind, id);
|
|
15826
|
-
|
|
15827
|
-
|
|
15828
|
-
|
|
16572
|
+
const shouldReturnRow = options?.returnRow ?? true;
|
|
16573
|
+
const convergeOn = options?.convergeOn;
|
|
16574
|
+
return runHookedWriteOperation(
|
|
16575
|
+
ctx,
|
|
16576
|
+
opContext,
|
|
16577
|
+
backend,
|
|
16578
|
+
async (target) => {
|
|
16579
|
+
if (convergeOn !== void 0) {
|
|
16580
|
+
const candidateRows = await target.findEdgesByKind({
|
|
16581
|
+
graphId: ctx.graphId,
|
|
16582
|
+
kind,
|
|
16583
|
+
fromKind: input.fromKind,
|
|
16584
|
+
fromId: input.fromId,
|
|
16585
|
+
toKind: input.toKind,
|
|
16586
|
+
toId: input.toId,
|
|
16587
|
+
excludeDeleted: false,
|
|
16588
|
+
temporalMode: "includeTombstones"
|
|
16589
|
+
});
|
|
16590
|
+
const { liveRow, deletedRow } = findMatchingEdge(
|
|
16591
|
+
candidateRows,
|
|
16592
|
+
convergeOn.matchOn,
|
|
16593
|
+
convergeOn.props
|
|
16594
|
+
);
|
|
16595
|
+
if (liveRow !== void 0 || deletedRow !== void 0) {
|
|
16596
|
+
throw new EdgeConvergenceRaced(kind);
|
|
16597
|
+
}
|
|
16598
|
+
}
|
|
16599
|
+
const prepared = await validateAndPrepareEdgeCreate(
|
|
16600
|
+
ctx,
|
|
16601
|
+
input,
|
|
16602
|
+
id,
|
|
16603
|
+
target
|
|
16604
|
+
);
|
|
16605
|
+
const row = await withAlreadyExistsTranslation("edge", async () => {
|
|
16606
|
+
if (shouldReturnRow) return target.insertEdge(prepared.insertParams);
|
|
16607
|
+
await runInsertNoReturn(
|
|
16608
|
+
edgeInsertDispatch(target),
|
|
16609
|
+
prepared.insertParams
|
|
16610
|
+
);
|
|
16611
|
+
return;
|
|
16612
|
+
});
|
|
16613
|
+
return row === void 0 ? void 0 : rowToEdge(row);
|
|
16614
|
+
},
|
|
15829
16615
|
{
|
|
15830
|
-
|
|
16616
|
+
// A create that runs a cardinality probe, or one converging on a match
|
|
16617
|
+
// key, decides something the write must not have invalidated. A plain
|
|
16618
|
+
// `many` create decides nothing and takes no lock.
|
|
16619
|
+
fencesConstraintProbe: convergeOn === void 0 ? edgeWriteNeedsConstraintFence(edgeCardinality(ctx, kind)) : "edgeMatchKeyConvergence"
|
|
15831
16620
|
}
|
|
15832
|
-
|
|
15833
|
-
return rowToEdge(row);
|
|
15834
|
-
});
|
|
16621
|
+
);
|
|
15835
16622
|
}
|
|
15836
16623
|
async function executeEdgeCreate(ctx, input, backend) {
|
|
15837
|
-
const
|
|
15838
|
-
|
|
16624
|
+
const edge = await executeEdgeCreateInternal(ctx, input, backend, {
|
|
16625
|
+
returnRow: true
|
|
16626
|
+
});
|
|
16627
|
+
if (!edge) {
|
|
15839
16628
|
throw new DatabaseOperationError(
|
|
15840
16629
|
"Edge create failed: expected created edge row",
|
|
15841
16630
|
{ operation: "insert", entity: "edge" }
|
|
15842
16631
|
);
|
|
15843
16632
|
}
|
|
15844
|
-
return
|
|
16633
|
+
return edge;
|
|
15845
16634
|
}
|
|
15846
16635
|
async function primeEdgeBatchValidationCache(ctx, inputs, backend, seedEndpointRow) {
|
|
15847
16636
|
if (backend.getNodes === void 0) return;
|
|
@@ -15895,31 +16684,53 @@ async function executeEdgeCreateNoReturnBatch(ctx, inputs, backend) {
|
|
|
15895
16684
|
if (inputs.length === 0) {
|
|
15896
16685
|
return;
|
|
15897
16686
|
}
|
|
15898
|
-
await runInWriteTransaction(
|
|
15899
|
-
|
|
15900
|
-
|
|
15901
|
-
|
|
15902
|
-
|
|
15903
|
-
|
|
15904
|
-
|
|
15905
|
-
|
|
16687
|
+
await runInWriteTransaction(
|
|
16688
|
+
ctx,
|
|
16689
|
+
backend,
|
|
16690
|
+
async (target) => {
|
|
16691
|
+
const { batchInsertParams } = await prepareEdgeBatchCreates(
|
|
16692
|
+
ctx,
|
|
16693
|
+
inputs,
|
|
16694
|
+
target
|
|
16695
|
+
);
|
|
16696
|
+
await withAlreadyExistsTranslation(
|
|
16697
|
+
"edge",
|
|
16698
|
+
() => runInsertBatch(edgeInsertDispatch(target), batchInsertParams)
|
|
16699
|
+
);
|
|
16700
|
+
},
|
|
16701
|
+
{ fencesConstraintProbe: batchFencesConstraintProbe(ctx, inputs) }
|
|
16702
|
+
);
|
|
15906
16703
|
}
|
|
15907
16704
|
async function executeEdgeCreateBatch(ctx, inputs, backend) {
|
|
15908
16705
|
if (inputs.length === 0) {
|
|
15909
16706
|
return [];
|
|
15910
16707
|
}
|
|
15911
|
-
return runInWriteTransaction(
|
|
15912
|
-
|
|
15913
|
-
|
|
15914
|
-
|
|
15915
|
-
|
|
15916
|
-
|
|
15917
|
-
|
|
15918
|
-
|
|
15919
|
-
|
|
16708
|
+
return runInWriteTransaction(
|
|
16709
|
+
ctx,
|
|
16710
|
+
backend,
|
|
16711
|
+
async (target) => {
|
|
16712
|
+
const { batchInsertParams } = await prepareEdgeBatchCreates(
|
|
16713
|
+
ctx,
|
|
16714
|
+
inputs,
|
|
16715
|
+
target
|
|
16716
|
+
);
|
|
16717
|
+
const rows = await withAlreadyExistsTranslation(
|
|
16718
|
+
"edge",
|
|
16719
|
+
() => runInsertBatchReturning(edgeInsertDispatch(target), batchInsertParams)
|
|
16720
|
+
);
|
|
16721
|
+
return rows.map((row) => rowToEdge(row));
|
|
16722
|
+
},
|
|
16723
|
+
{ fencesConstraintProbe: batchFencesConstraintProbe(ctx, inputs) }
|
|
16724
|
+
);
|
|
16725
|
+
}
|
|
16726
|
+
function batchFencesConstraintProbe(ctx, inputs) {
|
|
16727
|
+
for (const input of inputs) {
|
|
16728
|
+
const reason = edgeWriteNeedsConstraintFence(
|
|
16729
|
+
edgeCardinality(ctx, input.kind)
|
|
15920
16730
|
);
|
|
15921
|
-
|
|
15922
|
-
}
|
|
16731
|
+
if (reason !== void 0) return reason;
|
|
16732
|
+
}
|
|
16733
|
+
return void 0;
|
|
15923
16734
|
}
|
|
15924
16735
|
function computeEdgeUpdate(ctx, kind, id, existingProps, inputProps) {
|
|
15925
16736
|
const registration = getEdgeRegistration(ctx.graph, kind);
|
|
@@ -15957,65 +16768,234 @@ async function performEdgeUpdate(ctx, input, target, options) {
|
|
|
15957
16768
|
const id = input.id;
|
|
15958
16769
|
const existing = await target.getEdge(ctx.graphId, id);
|
|
15959
16770
|
if (!existing || !options?.clearDeleted && existing.deleted_at) {
|
|
15960
|
-
throw new EdgeNotFoundError(
|
|
16771
|
+
throw new EdgeNotFoundError(input.identity.kind, id);
|
|
15961
16772
|
}
|
|
16773
|
+
assertEdgeIdentityMatches(
|
|
16774
|
+
id,
|
|
16775
|
+
input.identity,
|
|
16776
|
+
edgeIdentityFromRow(existing),
|
|
16777
|
+
"update"
|
|
16778
|
+
);
|
|
15962
16779
|
const { validatedProps } = resolveEdgeUpdateProps(ctx, existing, input.props);
|
|
16780
|
+
const validFrom = validateOptionalCanonicalIsoDate(
|
|
16781
|
+
input.validFrom,
|
|
16782
|
+
"validFrom"
|
|
16783
|
+
);
|
|
15963
16784
|
const validTo = validateOptionalCanonicalIsoDate(input.validTo, "validTo");
|
|
16785
|
+
const windowVerdict = assertWritableValidityWindow(
|
|
16786
|
+
`edge "${id}"`,
|
|
16787
|
+
validFrom,
|
|
16788
|
+
{
|
|
16789
|
+
effectiveValidFrom: existing.valid_from,
|
|
16790
|
+
appliesStatedValidFrom: options?.clearDeleted === true,
|
|
16791
|
+
// Unlike the node side, an edge RETAINS `valid_from` through a
|
|
16792
|
+
// resurrection that does not name a new one, so the effective bound is the
|
|
16793
|
+
// row's stored one on BOTH legs and there is no carve-out to make.
|
|
16794
|
+
effectiveBoundIsStored: true
|
|
16795
|
+
},
|
|
16796
|
+
validTo
|
|
16797
|
+
);
|
|
15964
16798
|
const updateParams = {
|
|
15965
16799
|
graphId: ctx.graphId,
|
|
15966
16800
|
id,
|
|
15967
|
-
|
|
16801
|
+
kind: input.identity.kind,
|
|
16802
|
+
props: validatedProps,
|
|
16803
|
+
// The bound the window verdict above READ, carried into the same `WHERE` as
|
|
16804
|
+
// the identity components and on exactly the same terms: present only when
|
|
16805
|
+
// the verdict consulted it, because a component the caller made no claim
|
|
16806
|
+
// about must not become a predicate that refuses legitimate writes.
|
|
16807
|
+
...windowVerdict.storedLowerBoundFence
|
|
15968
16808
|
};
|
|
16809
|
+
if (input.identity.fromKind !== void 0) {
|
|
16810
|
+
updateParams.fromKind = input.identity.fromKind;
|
|
16811
|
+
}
|
|
16812
|
+
if (input.identity.fromId !== void 0) {
|
|
16813
|
+
updateParams.fromId = input.identity.fromId;
|
|
16814
|
+
}
|
|
16815
|
+
if (input.identity.toKind !== void 0) {
|
|
16816
|
+
updateParams.toKind = input.identity.toKind;
|
|
16817
|
+
}
|
|
16818
|
+
if (input.identity.toId !== void 0) {
|
|
16819
|
+
updateParams.toId = input.identity.toId;
|
|
16820
|
+
}
|
|
16821
|
+
if (validFrom !== void 0) updateParams.validFrom = validFrom;
|
|
15969
16822
|
if (validTo !== void 0) updateParams.validTo = validTo;
|
|
15970
16823
|
if (options?.clearDeleted) updateParams.clearDeleted = true;
|
|
15971
|
-
const row = await
|
|
16824
|
+
const row = await withUnmatchedEdgeUpdateRefusal(
|
|
16825
|
+
ctx,
|
|
16826
|
+
target,
|
|
16827
|
+
id,
|
|
16828
|
+
input.identity,
|
|
16829
|
+
updateParams.expectedValidFrom !== void 0,
|
|
16830
|
+
() => target.updateEdge(updateParams)
|
|
16831
|
+
);
|
|
15972
16832
|
return rowToEdge(row);
|
|
15973
16833
|
}
|
|
16834
|
+
function isEdgeUpdateNoRowError(error) {
|
|
16835
|
+
return error instanceof DatabaseOperationError && error.details.operation === "update" && error.details.entity === "edge" && error.details.reason === "no_row_returned";
|
|
16836
|
+
}
|
|
16837
|
+
async function withUnmatchedEdgeUpdateRefusal(ctx, target, id, expected, assertedValidFrom, write) {
|
|
16838
|
+
try {
|
|
16839
|
+
return await write();
|
|
16840
|
+
} catch (error) {
|
|
16841
|
+
if (!isEdgeUpdateNoRowError(error)) throw error;
|
|
16842
|
+
const current = await target.getEdge(ctx.graphId, id);
|
|
16843
|
+
if (current !== void 0) {
|
|
16844
|
+
assertEdgeIdentityMatches(
|
|
16845
|
+
id,
|
|
16846
|
+
expected,
|
|
16847
|
+
edgeIdentityFromRow(current),
|
|
16848
|
+
"update"
|
|
16849
|
+
);
|
|
16850
|
+
if (assertedValidFrom && current.deleted_at === void 0) {
|
|
16851
|
+
throw new EdgeUpdateTargetMoved(expected.kind, id);
|
|
16852
|
+
}
|
|
16853
|
+
}
|
|
16854
|
+
throw new EdgeNotFoundError(expected.kind, id);
|
|
16855
|
+
}
|
|
16856
|
+
}
|
|
16857
|
+
var EdgeUpdateTargetMoved = class extends Error {
|
|
16858
|
+
constructor(kind, id) {
|
|
16859
|
+
super(
|
|
16860
|
+
`The ${kind} edge "${id}" was replaced between this update's read and its write; re-resolving it. This is internal and is never returned to a caller.`
|
|
16861
|
+
);
|
|
16862
|
+
this.name = "EdgeUpdateTargetMoved";
|
|
16863
|
+
}
|
|
16864
|
+
};
|
|
16865
|
+
var EDGE_UPDATE_ATTEMPTS = 2;
|
|
16866
|
+
async function performEdgeUpdateConverging(ctx, input, target, options) {
|
|
16867
|
+
for (let attempt = 1; attempt <= EDGE_UPDATE_ATTEMPTS; attempt += 1) {
|
|
16868
|
+
try {
|
|
16869
|
+
return await performEdgeUpdate(ctx, input, target, options);
|
|
16870
|
+
} catch (error) {
|
|
16871
|
+
if (!(error instanceof EdgeUpdateTargetMoved)) throw error;
|
|
16872
|
+
if (attempt === EDGE_UPDATE_ATTEMPTS) {
|
|
16873
|
+
throw new DatabaseOperationError(
|
|
16874
|
+
`Edge update for "${input.id}" could not be applied to a stable row after ${EDGE_UPDATE_ATTEMPTS} attempts: the row was replaced between each read and its write. A concurrent writer is replacing this edge faster than it can be read; serialize the writers, or retry.`,
|
|
16875
|
+
{ operation: "update", entity: "edge" },
|
|
16876
|
+
{ cause: error }
|
|
16877
|
+
);
|
|
16878
|
+
}
|
|
16879
|
+
}
|
|
16880
|
+
}
|
|
16881
|
+
throw new EdgeNotFoundError(input.identity.kind, input.id);
|
|
16882
|
+
}
|
|
15974
16883
|
async function executeEdgeUpdate(ctx, input, backend) {
|
|
15975
16884
|
const id = input.id;
|
|
15976
16885
|
const gate = await backend.getEdge(ctx.graphId, id);
|
|
15977
16886
|
if (!gate || gate.deleted_at) {
|
|
15978
|
-
throw new EdgeNotFoundError(
|
|
16887
|
+
throw new EdgeNotFoundError(input.identity.kind, id);
|
|
15979
16888
|
}
|
|
16889
|
+
assertEdgeIdentityMatches(
|
|
16890
|
+
id,
|
|
16891
|
+
input.identity,
|
|
16892
|
+
edgeIdentityFromRow(gate),
|
|
16893
|
+
"update"
|
|
16894
|
+
);
|
|
15980
16895
|
const opContext = ctx.createOperationContext("update", "edge", gate.kind, id);
|
|
15981
16896
|
return runHookedWriteOperation(
|
|
15982
16897
|
ctx,
|
|
15983
16898
|
opContext,
|
|
15984
16899
|
backend,
|
|
15985
|
-
(target) =>
|
|
16900
|
+
(target) => performEdgeUpdateConverging(ctx, input, target)
|
|
15986
16901
|
);
|
|
15987
16902
|
}
|
|
15988
16903
|
async function executeEdgeUpsertUpdate(ctx, input, backend, options) {
|
|
16904
|
+
const resurrectCardinality = options?.resurrectCardinality;
|
|
15989
16905
|
return runInWriteTransaction(
|
|
15990
16906
|
ctx,
|
|
15991
16907
|
backend,
|
|
15992
|
-
(target) =>
|
|
16908
|
+
async (target) => {
|
|
16909
|
+
if (resurrectCardinality !== void 0) {
|
|
16910
|
+
await checkCardinalityConstraint(
|
|
16911
|
+
{
|
|
16912
|
+
graphId: ctx.graphId,
|
|
16913
|
+
registry: ctx.registry,
|
|
16914
|
+
backend: target
|
|
16915
|
+
},
|
|
16916
|
+
input.identity.kind,
|
|
16917
|
+
resurrectCardinality.cardinality,
|
|
16918
|
+
resurrectCardinality.fromKind,
|
|
16919
|
+
resurrectCardinality.fromId,
|
|
16920
|
+
resurrectCardinality.toKind,
|
|
16921
|
+
resurrectCardinality.toId,
|
|
16922
|
+
resurrectCardinality.effectiveValidTo
|
|
16923
|
+
);
|
|
16924
|
+
}
|
|
16925
|
+
return performEdgeUpdateConverging(ctx, input, target, options);
|
|
16926
|
+
},
|
|
16927
|
+
{
|
|
16928
|
+
// An in-place props update re-derives no constraint verdict: endpoints
|
|
16929
|
+
// are immutable, so cardinality cannot change under it.
|
|
16930
|
+
fencesConstraintProbe: resurrectCardinality === void 0 ? void 0 : edgeWriteNeedsConstraintFence(resurrectCardinality.cardinality)
|
|
16931
|
+
}
|
|
15993
16932
|
);
|
|
15994
16933
|
}
|
|
15995
|
-
async function executeEdgeDelete(ctx, id, backend) {
|
|
16934
|
+
async function executeEdgeDelete(ctx, expectedKind, id, backend) {
|
|
15996
16935
|
const gate = await backend.getEdge(ctx.graphId, id);
|
|
15997
|
-
if (!gate
|
|
16936
|
+
if (!gate) return;
|
|
16937
|
+
assertEdgeIdentityMatches(
|
|
16938
|
+
id,
|
|
16939
|
+
{ kind: expectedKind },
|
|
16940
|
+
edgeIdentityFromRow(gate),
|
|
16941
|
+
"delete"
|
|
16942
|
+
);
|
|
16943
|
+
if (gate.deleted_at) return;
|
|
15998
16944
|
const opContext = ctx.createOperationContext("delete", "edge", gate.kind, id);
|
|
15999
16945
|
return runHookedWriteOperation(ctx, opContext, backend, async (target) => {
|
|
16000
16946
|
await target.deleteEdge({
|
|
16001
16947
|
graphId: ctx.graphId,
|
|
16002
|
-
id
|
|
16948
|
+
id,
|
|
16949
|
+
kind: expectedKind
|
|
16003
16950
|
});
|
|
16004
16951
|
});
|
|
16005
16952
|
}
|
|
16006
|
-
async function
|
|
16953
|
+
async function executeEdgeDeleteBatch(ctx, expectedKind, ids, backend) {
|
|
16954
|
+
await runInWriteTransaction(
|
|
16955
|
+
ctx,
|
|
16956
|
+
backend,
|
|
16957
|
+
async (target) => {
|
|
16958
|
+
let affectedCount = 0;
|
|
16959
|
+
for (const id of ids) {
|
|
16960
|
+
const current = await target.getEdge(ctx.graphId, id);
|
|
16961
|
+
if (!current) continue;
|
|
16962
|
+
assertEdgeIdentityMatches(
|
|
16963
|
+
id,
|
|
16964
|
+
{ kind: expectedKind },
|
|
16965
|
+
edgeIdentityFromRow(current),
|
|
16966
|
+
"delete"
|
|
16967
|
+
);
|
|
16968
|
+
if (current.deleted_at) continue;
|
|
16969
|
+
await target.deleteEdge({
|
|
16970
|
+
graphId: ctx.graphId,
|
|
16971
|
+
id,
|
|
16972
|
+
kind: expectedKind
|
|
16973
|
+
});
|
|
16974
|
+
affectedCount += 1;
|
|
16975
|
+
}
|
|
16976
|
+
return affectedCount;
|
|
16977
|
+
},
|
|
16978
|
+
{ didWrite: (affectedCount) => affectedCount > 0 }
|
|
16979
|
+
);
|
|
16980
|
+
}
|
|
16981
|
+
async function executeEdgeHardDelete(ctx, expectedKind, id, backend) {
|
|
16007
16982
|
const gate = await backend.getEdge(ctx.graphId, id);
|
|
16008
16983
|
if (!gate) return;
|
|
16984
|
+
assertEdgeIdentityMatches(
|
|
16985
|
+
id,
|
|
16986
|
+
{ kind: expectedKind },
|
|
16987
|
+
edgeIdentityFromRow(gate),
|
|
16988
|
+
"hardDelete"
|
|
16989
|
+
);
|
|
16009
16990
|
const opContext = ctx.createOperationContext("delete", "edge", gate.kind, id);
|
|
16010
16991
|
return runHookedWriteOperation(ctx, opContext, backend, async (target) => {
|
|
16011
16992
|
await target.hardDeleteEdge({
|
|
16012
16993
|
graphId: ctx.graphId,
|
|
16013
|
-
id
|
|
16994
|
+
id,
|
|
16995
|
+
kind: expectedKind
|
|
16014
16996
|
});
|
|
16015
16997
|
});
|
|
16016
16998
|
}
|
|
16017
|
-
var RECORD_SEP = "";
|
|
16018
|
-
var UNIT_SEP = "";
|
|
16019
16999
|
var UNDEFINED_SENTINEL = "";
|
|
16020
17000
|
function validateMatchOnFields(schema, matchOn, edgeKind) {
|
|
16021
17001
|
if (matchOn.length === 0) return;
|
|
@@ -16033,7 +17013,7 @@ function validateMatchOnFields(schema, matchOn, edgeKind) {
|
|
|
16033
17013
|
}
|
|
16034
17014
|
);
|
|
16035
17015
|
}
|
|
16036
|
-
const invalidFields = matchOn.filter((field2) => !(field2
|
|
17016
|
+
const invalidFields = matchOn.filter((field2) => !hasOwnKey(shape, field2));
|
|
16037
17017
|
if (invalidFields.length > 0) {
|
|
16038
17018
|
throw new ValidationError(
|
|
16039
17019
|
`Invalid matchOn fields for edge kind "${edgeKind}": ${invalidFields.join(", ")}`,
|
|
@@ -16061,16 +17041,20 @@ function stableStringify(value) {
|
|
|
16061
17041
|
return `{${entries.join(",")}}`;
|
|
16062
17042
|
}
|
|
16063
17043
|
function buildEdgeCompositeKey(fromKind, fromId, toKind, toId, props, matchOn) {
|
|
16064
|
-
const endpointPart = `${fromKind}${RECORD_SEP}${fromId}${RECORD_SEP}${toKind}${RECORD_SEP}${toId}`;
|
|
16065
|
-
if (matchOn.length === 0) return endpointPart;
|
|
16066
17044
|
const sortedFields = [...matchOn].toSorted();
|
|
16067
|
-
|
|
16068
|
-
|
|
16069
|
-
|
|
16070
|
-
|
|
17045
|
+
return encodeTupleKey([
|
|
17046
|
+
fromKind,
|
|
17047
|
+
fromId,
|
|
17048
|
+
toKind,
|
|
17049
|
+
toId,
|
|
17050
|
+
...sortedFields.flatMap((field2) => [
|
|
17051
|
+
field2,
|
|
17052
|
+
stableStringify(readOwnProperty(props, field2))
|
|
17053
|
+
])
|
|
17054
|
+
]);
|
|
16071
17055
|
}
|
|
16072
17056
|
function buildEndpointPairKey(fromKind, fromId, toKind, toId) {
|
|
16073
|
-
return
|
|
17057
|
+
return encodeTupleKey([fromKind, fromId, toKind, toId]);
|
|
16074
17058
|
}
|
|
16075
17059
|
function findMatchingEdge(rows, matchOn, inputProps) {
|
|
16076
17060
|
let liveRow;
|
|
@@ -16079,7 +17063,7 @@ function findMatchingEdge(rows, matchOn, inputProps) {
|
|
|
16079
17063
|
if (matchOn.length > 0) {
|
|
16080
17064
|
const rowProps = rowPropsToObject(row.props);
|
|
16081
17065
|
const matches = matchOn.every(
|
|
16082
|
-
(field2) => stableStringify(rowProps
|
|
17066
|
+
(field2) => stableStringify(readOwnProperty(rowProps, field2)) === stableStringify(readOwnProperty(inputProps, field2))
|
|
16083
17067
|
);
|
|
16084
17068
|
if (!matches) continue;
|
|
16085
17069
|
}
|
|
@@ -16133,6 +17117,11 @@ async function executeEdgeGetOrCreateByEndpoints(ctx, kind, fromKind, fromId, to
|
|
|
16133
17117
|
"validFrom"
|
|
16134
17118
|
);
|
|
16135
17119
|
const validTo = validateOptionalCanonicalIsoDate(options?.validTo, "validTo");
|
|
17120
|
+
assertOrderedValidityWindow(
|
|
17121
|
+
`${kind} edge between ${fromKind} "${fromId}" and ${toKind} "${toId}"`,
|
|
17122
|
+
validFrom,
|
|
17123
|
+
validTo
|
|
17124
|
+
);
|
|
16136
17125
|
if (ifExists === "return") {
|
|
16137
17126
|
const probeRows = await backend.findEdgesByKind({
|
|
16138
17127
|
graphId: ctx.graphId,
|
|
@@ -16180,8 +17169,17 @@ async function executeEdgeGetOrCreateByEndpoints(ctx, kind, fromKind, fromId, to
|
|
|
16180
17169
|
...validFrom !== void 0 && { validFrom },
|
|
16181
17170
|
...validTo !== void 0 && { validTo }
|
|
16182
17171
|
};
|
|
16183
|
-
const
|
|
16184
|
-
|
|
17172
|
+
const created = await executeEdgeCreateInternal(ctx, input, backend, {
|
|
17173
|
+
returnRow: true,
|
|
17174
|
+
convergeOn: { matchOn, props: validatedProps }
|
|
17175
|
+
});
|
|
17176
|
+
if (created === void 0) {
|
|
17177
|
+
throw new DatabaseOperationError(
|
|
17178
|
+
"Edge create failed: expected created edge row",
|
|
17179
|
+
{ operation: "insert", entity: "edge" }
|
|
17180
|
+
);
|
|
17181
|
+
}
|
|
17182
|
+
return { edge: created, action: "created" };
|
|
16185
17183
|
}
|
|
16186
17184
|
if (liveRow !== void 0) {
|
|
16187
17185
|
if (ifExists === "return") {
|
|
@@ -16191,7 +17189,9 @@ async function executeEdgeGetOrCreateByEndpoints(ctx, kind, fromKind, fromId, to
|
|
|
16191
17189
|
ctx,
|
|
16192
17190
|
{
|
|
16193
17191
|
id: liveRow.id,
|
|
17192
|
+
identity: { kind, fromKind, fromId, toKind, toId },
|
|
16194
17193
|
props: validatedProps,
|
|
17194
|
+
...validFrom !== void 0 && { validFrom },
|
|
16195
17195
|
...validTo !== void 0 && { validTo }
|
|
16196
17196
|
},
|
|
16197
17197
|
backend
|
|
@@ -16204,39 +17204,50 @@ async function executeEdgeGetOrCreateByEndpoints(ctx, kind, fromKind, fromId, to
|
|
|
16204
17204
|
}
|
|
16205
17205
|
const matchedDeletedRow = deletedRow;
|
|
16206
17206
|
const effectiveValidTo = validTo ?? matchedDeletedRow.valid_to;
|
|
16207
|
-
const constraintContext = {
|
|
16208
|
-
graphId: ctx.graphId,
|
|
16209
|
-
registry: ctx.registry,
|
|
16210
|
-
backend
|
|
16211
|
-
};
|
|
16212
|
-
await checkCardinalityConstraint(
|
|
16213
|
-
constraintContext,
|
|
16214
|
-
kind,
|
|
16215
|
-
cardinality,
|
|
16216
|
-
fromKind,
|
|
16217
|
-
fromId,
|
|
16218
|
-
toKind,
|
|
16219
|
-
toId,
|
|
16220
|
-
effectiveValidTo
|
|
16221
|
-
);
|
|
16222
17207
|
const edge = await executeEdgeUpsertUpdate(
|
|
16223
17208
|
ctx,
|
|
16224
17209
|
{
|
|
16225
17210
|
id: matchedDeletedRow.id,
|
|
17211
|
+
identity: { kind, fromKind, fromId, toKind, toId },
|
|
16226
17212
|
props: validatedProps,
|
|
17213
|
+
...validFrom !== void 0 && { validFrom },
|
|
16227
17214
|
...validTo !== void 0 && { validTo }
|
|
16228
17215
|
},
|
|
16229
17216
|
backend,
|
|
16230
|
-
{
|
|
17217
|
+
{
|
|
17218
|
+
clearDeleted: true,
|
|
17219
|
+
resurrectCardinality: {
|
|
17220
|
+
cardinality,
|
|
17221
|
+
fromKind,
|
|
17222
|
+
fromId,
|
|
17223
|
+
toKind,
|
|
17224
|
+
toId,
|
|
17225
|
+
effectiveValidTo
|
|
17226
|
+
}
|
|
17227
|
+
}
|
|
16231
17228
|
);
|
|
16232
17229
|
return { edge, action: "resurrected" };
|
|
16233
17230
|
}
|
|
16234
|
-
|
|
16235
|
-
|
|
16236
|
-
|
|
16237
|
-
|
|
16238
|
-
|
|
17231
|
+
const ATTEMPT_LIMIT = 3;
|
|
17232
|
+
for (let remaining = ATTEMPT_LIMIT; remaining > 0; remaining -= 1) {
|
|
17233
|
+
try {
|
|
17234
|
+
return await attempt();
|
|
17235
|
+
} catch (error) {
|
|
17236
|
+
const retryable = error instanceof CardinalityError || error instanceof EdgeConvergenceRaced;
|
|
17237
|
+
if (!retryable || remaining === 1) {
|
|
17238
|
+
if (!(error instanceof EdgeConvergenceRaced)) throw error;
|
|
17239
|
+
throw new DatabaseOperationError(
|
|
17240
|
+
`getOrCreateByEndpoints for ${kind} between ${fromKind} "${fromId}" and ${toKind} "${toId}" lost its match key to a competing writer ${String(ATTEMPT_LIMIT)} times without converging. A concurrent writer is repeatedly creating and removing this edge; serialize those callers or retry the operation.`,
|
|
17241
|
+
{ operation: "insert", entity: "edge" },
|
|
17242
|
+
{ cause: error }
|
|
17243
|
+
);
|
|
17244
|
+
}
|
|
17245
|
+
}
|
|
16239
17246
|
}
|
|
17247
|
+
throw new DatabaseOperationError(
|
|
17248
|
+
"getOrCreateByEndpoints convergence loop exited without a verdict",
|
|
17249
|
+
{ operation: "insert", entity: "edge" }
|
|
17250
|
+
);
|
|
16240
17251
|
}
|
|
16241
17252
|
async function executeEdgeBulkGetOrCreateByEndpoints(ctx, kind, items, backend, options) {
|
|
16242
17253
|
if (items.length === 0) return [];
|
|
@@ -16257,6 +17268,11 @@ async function executeEdgeBulkGetOrCreateByEndpoints(ctx, kind, items, backend,
|
|
|
16257
17268
|
"validFrom"
|
|
16258
17269
|
);
|
|
16259
17270
|
const validTo = validateOptionalCanonicalIsoDate(item.validTo, "validTo");
|
|
17271
|
+
assertOrderedValidityWindow(
|
|
17272
|
+
`${kind} edge between ${item.fromKind} "${item.fromId}" and ${item.toKind} "${item.toId}"`,
|
|
17273
|
+
validFrom,
|
|
17274
|
+
validTo
|
|
17275
|
+
);
|
|
16260
17276
|
const compositeKey = buildEdgeCompositeKey(
|
|
16261
17277
|
item.fromKind,
|
|
16262
17278
|
item.fromId,
|
|
@@ -16359,6 +17375,9 @@ async function executeEdgeBulkGetOrCreateByEndpoints(ctx, kind, items, backend,
|
|
|
16359
17375
|
fromId: entry2.fromId,
|
|
16360
17376
|
toKind: entry2.toKind,
|
|
16361
17377
|
toId: entry2.toId,
|
|
17378
|
+
...entry2.validFrom !== void 0 && {
|
|
17379
|
+
validFrom: entry2.validFrom
|
|
17380
|
+
},
|
|
16362
17381
|
...entry2.validTo !== void 0 && { validTo: entry2.validTo }
|
|
16363
17382
|
});
|
|
16364
17383
|
}
|
|
@@ -16382,75 +17401,108 @@ async function executeEdgeBulkGetOrCreateByEndpoints(ctx, kind, items, backend,
|
|
|
16382
17401
|
return found;
|
|
16383
17402
|
}
|
|
16384
17403
|
}
|
|
16385
|
-
|
|
16386
|
-
|
|
16387
|
-
|
|
16388
|
-
|
|
16389
|
-
|
|
16390
|
-
|
|
16391
|
-
|
|
16392
|
-
const createdEdges = await executeEdgeCreateBatch(
|
|
16393
|
-
ctx,
|
|
16394
|
-
createInputs,
|
|
16395
|
-
target
|
|
16396
|
-
);
|
|
16397
|
-
for (const [batchIndex, entry2] of toCreate.entries()) {
|
|
16398
|
-
results[entry2.index] = {
|
|
16399
|
-
edge: requireDefined(createdEdges[batchIndex]),
|
|
16400
|
-
action: "created"
|
|
16401
|
-
};
|
|
16402
|
-
}
|
|
16403
|
-
}
|
|
16404
|
-
for (const entry2 of toFetch) {
|
|
16405
|
-
if (entry2.isDeleted) {
|
|
16406
|
-
const effectiveValidTo = entry2.validTo ?? entry2.row.valid_to;
|
|
16407
|
-
const constraintContext = {
|
|
16408
|
-
graphId: ctx.graphId,
|
|
16409
|
-
registry: ctx.registry,
|
|
16410
|
-
backend: target
|
|
16411
|
-
};
|
|
16412
|
-
await checkCardinalityConstraint(
|
|
16413
|
-
constraintContext,
|
|
16414
|
-
kind,
|
|
16415
|
-
cardinality,
|
|
16416
|
-
entry2.fromKind,
|
|
16417
|
-
entry2.fromId,
|
|
16418
|
-
entry2.toKind,
|
|
16419
|
-
entry2.toId,
|
|
16420
|
-
effectiveValidTo
|
|
16421
|
-
);
|
|
16422
|
-
const edge = await executeEdgeUpsertUpdate(
|
|
16423
|
-
ctx,
|
|
16424
|
-
{
|
|
16425
|
-
id: entry2.row.id,
|
|
16426
|
-
props: entry2.validatedProps,
|
|
16427
|
-
...entry2.validTo !== void 0 && { validTo: entry2.validTo }
|
|
16428
|
-
},
|
|
16429
|
-
target,
|
|
16430
|
-
{ clearDeleted: true }
|
|
16431
|
-
);
|
|
16432
|
-
results[entry2.index] = { edge, action: "resurrected" };
|
|
16433
|
-
} else if (ifExists === "update") {
|
|
16434
|
-
const edge = await executeEdgeUpsertUpdate(
|
|
16435
|
-
ctx,
|
|
16436
|
-
{
|
|
16437
|
-
id: entry2.row.id,
|
|
16438
|
-
props: entry2.validatedProps,
|
|
16439
|
-
...entry2.validTo !== void 0 && { validTo: entry2.validTo }
|
|
16440
|
-
},
|
|
16441
|
-
target
|
|
17404
|
+
function runBatch() {
|
|
17405
|
+
return runInWriteTransaction(
|
|
17406
|
+
ctx,
|
|
17407
|
+
backend,
|
|
17408
|
+
async (target) => {
|
|
17409
|
+
const { toCreate, toFetch, duplicateOf } = partitionEntries(
|
|
17410
|
+
await fetchRowsByEndpoint(target)
|
|
16442
17411
|
);
|
|
16443
|
-
results
|
|
16444
|
-
|
|
16445
|
-
|
|
16446
|
-
|
|
16447
|
-
|
|
16448
|
-
|
|
16449
|
-
|
|
16450
|
-
|
|
16451
|
-
|
|
16452
|
-
|
|
16453
|
-
|
|
17412
|
+
const results = Array.from({ length: items.length });
|
|
17413
|
+
if (toCreate.length > 0) {
|
|
17414
|
+
const createInputs = toCreate.map((entry2) => entry2.input);
|
|
17415
|
+
const createdEdges = await executeEdgeCreateBatch(
|
|
17416
|
+
ctx,
|
|
17417
|
+
createInputs,
|
|
17418
|
+
target
|
|
17419
|
+
);
|
|
17420
|
+
for (const [batchIndex, entry2] of toCreate.entries()) {
|
|
17421
|
+
results[entry2.index] = {
|
|
17422
|
+
edge: requireDefined(createdEdges[batchIndex]),
|
|
17423
|
+
action: "created"
|
|
17424
|
+
};
|
|
17425
|
+
}
|
|
17426
|
+
}
|
|
17427
|
+
for (const entry2 of toFetch) {
|
|
17428
|
+
if (entry2.isDeleted) {
|
|
17429
|
+
const edge = await executeEdgeUpsertUpdate(
|
|
17430
|
+
ctx,
|
|
17431
|
+
{
|
|
17432
|
+
id: entry2.row.id,
|
|
17433
|
+
identity: {
|
|
17434
|
+
kind,
|
|
17435
|
+
fromKind: entry2.fromKind,
|
|
17436
|
+
fromId: entry2.fromId,
|
|
17437
|
+
toKind: entry2.toKind,
|
|
17438
|
+
toId: entry2.toId
|
|
17439
|
+
},
|
|
17440
|
+
props: entry2.validatedProps,
|
|
17441
|
+
...entry2.validFrom !== void 0 && {
|
|
17442
|
+
validFrom: entry2.validFrom
|
|
17443
|
+
},
|
|
17444
|
+
...entry2.validTo !== void 0 && { validTo: entry2.validTo }
|
|
17445
|
+
},
|
|
17446
|
+
target,
|
|
17447
|
+
{
|
|
17448
|
+
clearDeleted: true,
|
|
17449
|
+
resurrectCardinality: {
|
|
17450
|
+
cardinality,
|
|
17451
|
+
fromKind: entry2.fromKind,
|
|
17452
|
+
fromId: entry2.fromId,
|
|
17453
|
+
toKind: entry2.toKind,
|
|
17454
|
+
toId: entry2.toId,
|
|
17455
|
+
effectiveValidTo: entry2.validTo ?? entry2.row.valid_to
|
|
17456
|
+
}
|
|
17457
|
+
}
|
|
17458
|
+
);
|
|
17459
|
+
results[entry2.index] = { edge, action: "resurrected" };
|
|
17460
|
+
} else if (ifExists === "update") {
|
|
17461
|
+
const edge = await executeEdgeUpsertUpdate(
|
|
17462
|
+
ctx,
|
|
17463
|
+
{
|
|
17464
|
+
id: entry2.row.id,
|
|
17465
|
+
identity: {
|
|
17466
|
+
kind,
|
|
17467
|
+
fromKind: entry2.fromKind,
|
|
17468
|
+
fromId: entry2.fromId,
|
|
17469
|
+
toKind: entry2.toKind,
|
|
17470
|
+
toId: entry2.toId
|
|
17471
|
+
},
|
|
17472
|
+
props: entry2.validatedProps,
|
|
17473
|
+
...entry2.validFrom !== void 0 && {
|
|
17474
|
+
validFrom: entry2.validFrom
|
|
17475
|
+
},
|
|
17476
|
+
...entry2.validTo !== void 0 && { validTo: entry2.validTo }
|
|
17477
|
+
},
|
|
17478
|
+
target
|
|
17479
|
+
);
|
|
17480
|
+
results[entry2.index] = { edge, action: "updated" };
|
|
17481
|
+
} else {
|
|
17482
|
+
results[entry2.index] = {
|
|
17483
|
+
edge: rowToEdge(entry2.row),
|
|
17484
|
+
action: "found"
|
|
17485
|
+
};
|
|
17486
|
+
}
|
|
17487
|
+
}
|
|
17488
|
+
for (const { index, sourceIndex } of duplicateOf) {
|
|
17489
|
+
const sourceResult = requireDefined(results[sourceIndex]);
|
|
17490
|
+
results[index] = { edge: sourceResult.edge, action: "found" };
|
|
17491
|
+
}
|
|
17492
|
+
return results;
|
|
17493
|
+
},
|
|
17494
|
+
// A bulk getOrCreate always converges on a match key no database key
|
|
17495
|
+
// backs, whatever its cardinality — the same reason the single-item path
|
|
17496
|
+
// fences unconditionally.
|
|
17497
|
+
{ fencesConstraintProbe: "edgeMatchKeyConvergence" }
|
|
17498
|
+
);
|
|
17499
|
+
}
|
|
17500
|
+
try {
|
|
17501
|
+
return await runBatch();
|
|
17502
|
+
} catch (error) {
|
|
17503
|
+
if (!(error instanceof CardinalityError)) throw error;
|
|
17504
|
+
return runBatch();
|
|
17505
|
+
}
|
|
16454
17506
|
}
|
|
16455
17507
|
|
|
16456
17508
|
// src/store/recorded-read-service.ts
|
|
@@ -16749,6 +17801,7 @@ var NODE_WRITE_INTENT_COUNTERS = {
|
|
|
16749
17801
|
create: countSingleWrite,
|
|
16750
17802
|
createFromRecord: countSingleWrite,
|
|
16751
17803
|
update: countSingleWrite,
|
|
17804
|
+
updateWhere: countSingleWrite,
|
|
16752
17805
|
delete: countSingleWrite,
|
|
16753
17806
|
hardDelete: countSingleWrite,
|
|
16754
17807
|
upsertById: countSingleWrite,
|
|
@@ -16783,6 +17836,12 @@ function createTransactionReceiptRecorder() {
|
|
|
16783
17836
|
const counters = {
|
|
16784
17837
|
nodes: createCountBucket(),
|
|
16785
17838
|
edges: createCountBucket(),
|
|
17839
|
+
identity: {
|
|
17840
|
+
sameAssertions: 0,
|
|
17841
|
+
differentAssertions: 0,
|
|
17842
|
+
retractions: 0,
|
|
17843
|
+
total: 0
|
|
17844
|
+
},
|
|
16786
17845
|
total: 0
|
|
16787
17846
|
};
|
|
16788
17847
|
let sealed = false;
|
|
@@ -16795,15 +17854,17 @@ function createTransactionReceiptRecorder() {
|
|
|
16795
17854
|
increment(counters.edges, kind, count2);
|
|
16796
17855
|
counters.total += count2;
|
|
16797
17856
|
},
|
|
17857
|
+
recordIdentity(kind, count2) {
|
|
17858
|
+
counters.identity[kind] += count2;
|
|
17859
|
+
counters.identity.total += count2;
|
|
17860
|
+
counters.total += count2;
|
|
17861
|
+
},
|
|
16798
17862
|
snapshot(recorded) {
|
|
16799
17863
|
return Object.freeze({
|
|
16800
17864
|
writes: Object.freeze({
|
|
16801
|
-
nodes: Object.freeze(
|
|
16802
|
-
|
|
16803
|
-
),
|
|
16804
|
-
edges: Object.freeze(
|
|
16805
|
-
Object.assign(createCountBucket(), counters.edges)
|
|
16806
|
-
),
|
|
17865
|
+
nodes: Object.freeze({ ...counters.nodes }),
|
|
17866
|
+
edges: Object.freeze({ ...counters.edges }),
|
|
17867
|
+
identity: Object.freeze({ ...counters.identity }),
|
|
16807
17868
|
total: counters.total
|
|
16808
17869
|
}),
|
|
16809
17870
|
...recorded === void 0 ? {} : { recorded }
|
|
@@ -16825,6 +17886,67 @@ function createTransactionReceiptRecorder() {
|
|
|
16825
17886
|
}
|
|
16826
17887
|
};
|
|
16827
17888
|
}
|
|
17889
|
+
function wrapTransactionIdentity(identity, recorder) {
|
|
17890
|
+
return {
|
|
17891
|
+
representativeOf: (ref) => identity.representativeOf(ref),
|
|
17892
|
+
membersOf: (ref) => identity.membersOf(ref),
|
|
17893
|
+
nodesOf: (ref) => identity.nodesOf(ref),
|
|
17894
|
+
areSame: (a, b) => identity.areSame(a, b),
|
|
17895
|
+
areDifferent: (a, b) => identity.areDifferent(a, b),
|
|
17896
|
+
assertionsOf: (ref) => identity.assertionsOf(ref),
|
|
17897
|
+
async assertSame(a, b) {
|
|
17898
|
+
recorder.assertWritable();
|
|
17899
|
+
const result = await identity.assertSame(a, b);
|
|
17900
|
+
recorder.recordIdentity("sameAssertions", 1);
|
|
17901
|
+
return result;
|
|
17902
|
+
},
|
|
17903
|
+
async assertDifferent(a, b) {
|
|
17904
|
+
recorder.assertWritable();
|
|
17905
|
+
const result = await identity.assertDifferent(a, b);
|
|
17906
|
+
recorder.recordIdentity("differentAssertions", 1);
|
|
17907
|
+
return result;
|
|
17908
|
+
},
|
|
17909
|
+
async bulkAssertSame(pairs) {
|
|
17910
|
+
recorder.assertWritable();
|
|
17911
|
+
const count2 = pairs.length;
|
|
17912
|
+
const result = await identity.bulkAssertSame(pairs);
|
|
17913
|
+
recorder.recordIdentity("sameAssertions", count2);
|
|
17914
|
+
return result;
|
|
17915
|
+
},
|
|
17916
|
+
async bulkAssertDifferent(pairs) {
|
|
17917
|
+
recorder.assertWritable();
|
|
17918
|
+
const count2 = pairs.length;
|
|
17919
|
+
const result = await identity.bulkAssertDifferent(pairs);
|
|
17920
|
+
recorder.recordIdentity("differentAssertions", count2);
|
|
17921
|
+
return result;
|
|
17922
|
+
},
|
|
17923
|
+
async retractAssertion(id) {
|
|
17924
|
+
recorder.assertWritable();
|
|
17925
|
+
const result = await identity.retractAssertion(id);
|
|
17926
|
+
recorder.recordIdentity("retractions", 1);
|
|
17927
|
+
return result;
|
|
17928
|
+
},
|
|
17929
|
+
async retractSameAssertion(a, b) {
|
|
17930
|
+
recorder.assertWritable();
|
|
17931
|
+
const result = await identity.retractSameAssertion(a, b);
|
|
17932
|
+
recorder.recordIdentity("retractions", 1);
|
|
17933
|
+
return result;
|
|
17934
|
+
},
|
|
17935
|
+
async retractDifferentAssertion(a, b) {
|
|
17936
|
+
recorder.assertWritable();
|
|
17937
|
+
const result = await identity.retractDifferentAssertion(a, b);
|
|
17938
|
+
recorder.recordIdentity("retractions", 1);
|
|
17939
|
+
return result;
|
|
17940
|
+
},
|
|
17941
|
+
async bulkRetractAssertions(ids) {
|
|
17942
|
+
recorder.assertWritable();
|
|
17943
|
+
const count2 = ids.length;
|
|
17944
|
+
const result = await identity.bulkRetractAssertions(ids);
|
|
17945
|
+
recorder.recordIdentity("retractions", count2);
|
|
17946
|
+
return result;
|
|
17947
|
+
}
|
|
17948
|
+
};
|
|
17949
|
+
}
|
|
16828
17950
|
function isWrappedMethod(value) {
|
|
16829
17951
|
return typeof value === "function";
|
|
16830
17952
|
}
|
|
@@ -16929,6 +18051,14 @@ var CAUGHT_UP_VERB_DETAILS = {
|
|
|
16929
18051
|
reembed: {
|
|
16930
18052
|
phrase: "re-embed a vector field on",
|
|
16931
18053
|
code: "REEMBED_BEFORE_INITIALIZE"
|
|
18054
|
+
},
|
|
18055
|
+
repair: {
|
|
18056
|
+
phrase: "repair contributions for",
|
|
18057
|
+
code: "REPAIR_CONTRIBUTIONS_BEFORE_INITIALIZE"
|
|
18058
|
+
},
|
|
18059
|
+
rebuild: {
|
|
18060
|
+
phrase: "rebuild a contribution for",
|
|
18061
|
+
code: "REBUILD_CONTRIBUTION_BEFORE_INITIALIZE"
|
|
16932
18062
|
}
|
|
16933
18063
|
};
|
|
16934
18064
|
var ROW_MAPPER_CONFIGS = {
|
|
@@ -17001,6 +18131,20 @@ async function commitEvolvedSchemaWhenRequiredKindsAreEmpty(backend, graph, curr
|
|
|
17001
18131
|
}
|
|
17002
18132
|
);
|
|
17003
18133
|
}
|
|
18134
|
+
async function assertEvolvedSchemaRequiredKindsEmpty(backend, graphId, classification) {
|
|
18135
|
+
const counts = await Promise.all(
|
|
18136
|
+
classification.requireEmpty.map(async (entry2) => ({
|
|
18137
|
+
entry: entry2,
|
|
18138
|
+
count: entry2.entity === "node" ? await backend.countNodesByKind({ graphId, kind: entry2.kindName }) : await backend.countEdgesByKind({ graphId, kind: entry2.kindName })
|
|
18139
|
+
}))
|
|
18140
|
+
);
|
|
18141
|
+
const nonEmpty = new Set(
|
|
18142
|
+
counts.filter(({ count: count2 }) => count2 > 0).map(({ entry: entry2 }) => entry2)
|
|
18143
|
+
);
|
|
18144
|
+
const error = buildIncompatibleChangeError(classification, nonEmpty, graphId);
|
|
18145
|
+
if (error !== void 0) throw error;
|
|
18146
|
+
}
|
|
18147
|
+
var IDENTITY_FACADES = /* @__PURE__ */ new WeakMap();
|
|
17004
18148
|
var StoreImplementation = class _StoreImplementation {
|
|
17005
18149
|
[STORE_RUNTIME];
|
|
17006
18150
|
#graph;
|
|
@@ -17032,6 +18176,19 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
17032
18176
|
#search;
|
|
17033
18177
|
constructor(graph, backend, options, schemaMetadata, adapterBackend) {
|
|
17034
18178
|
this.#graph = graph;
|
|
18179
|
+
if (graph.identity !== void 0 && (!backend.capabilities.transactions || backend.executeStatement === void 0)) {
|
|
18180
|
+
throw new ConfigurationError(
|
|
18181
|
+
"Operational Identity requires an atomic transactional backend with statement execution support.",
|
|
18182
|
+
{
|
|
18183
|
+
code: "IDENTITY_REQUIRES_ATOMIC_BACKEND",
|
|
18184
|
+
transactions: backend.capabilities.transactions,
|
|
18185
|
+
statementExecution: backend.executeStatement !== void 0
|
|
18186
|
+
},
|
|
18187
|
+
{
|
|
18188
|
+
suggestion: "Use a transactional SQLite or PostgreSQL driver; Cloudflare D1 and neon-http cannot host identity closure maintenance."
|
|
18189
|
+
}
|
|
18190
|
+
);
|
|
18191
|
+
}
|
|
17035
18192
|
this.#baseBackend = asRawBackend(backend);
|
|
17036
18193
|
this.#adapterBackend = adapterBackend;
|
|
17037
18194
|
this.#captureEnabled = options?.history === true;
|
|
@@ -17080,7 +18237,56 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
17080
18237
|
recordedEdgeGetByIds: (kind, ids, coordinate) => this.recordedEdgeGetByIds(kind, ids, coordinate),
|
|
17081
18238
|
recordedEdgeScan: (kind, coordinate, options2) => this.recordedEdgeScan(kind, coordinate, options2),
|
|
17082
18239
|
subgraphAtCoordinate: (rootId, subgraphOptions) => this.subgraphAtCoordinate(rootId, subgraphOptions),
|
|
17083
|
-
algorithmsAtCoordinate: (coordinate) => this.algorithmsAtCoordinate(coordinate)
|
|
18240
|
+
algorithmsAtCoordinate: (coordinate) => this.algorithmsAtCoordinate(coordinate),
|
|
18241
|
+
identityAtCoordinate: (coordinate) => this.identityAtCoordinate(coordinate),
|
|
18242
|
+
rebuildIdentityClosure: () => this.rebuildIdentityClosure(),
|
|
18243
|
+
validateIdentity: () => this.validateIdentity(),
|
|
18244
|
+
liveNodesSharingIds: async (ids, target) => {
|
|
18245
|
+
const backend2 = target ?? this.#baseBackend;
|
|
18246
|
+
const liveKindsById = await liveNodeKindsSharingIds(
|
|
18247
|
+
this.#identityContext(backend2),
|
|
18248
|
+
backend2,
|
|
18249
|
+
ids
|
|
18250
|
+
);
|
|
18251
|
+
const peers = [];
|
|
18252
|
+
for (const [id, kinds] of liveKindsById) {
|
|
18253
|
+
for (const kind of kinds) peers.push({ kind, id });
|
|
18254
|
+
}
|
|
18255
|
+
return peers;
|
|
18256
|
+
},
|
|
18257
|
+
identityAssertionRowsByIds: async (ids, target) => {
|
|
18258
|
+
if (this.#graph.identity === void 0) return /* @__PURE__ */ new Map();
|
|
18259
|
+
const backend2 = target ?? this.#baseBackend;
|
|
18260
|
+
const ctx = this.#identityContext(backend2);
|
|
18261
|
+
const rows = await loadAssertionsByIds(
|
|
18262
|
+
backend2,
|
|
18263
|
+
ctx.schema,
|
|
18264
|
+
this.graphId,
|
|
18265
|
+
ids
|
|
18266
|
+
);
|
|
18267
|
+
return new Map(
|
|
18268
|
+
[...rows].map(([id, row]) => [id, toTransferAssertion(row)])
|
|
18269
|
+
);
|
|
18270
|
+
},
|
|
18271
|
+
structuralIdentityClasses: async (references, target) => {
|
|
18272
|
+
if (this.#graph.identity === void 0) return /* @__PURE__ */ new Map();
|
|
18273
|
+
const backend2 = target ?? this.#baseBackend;
|
|
18274
|
+
const ctx = this.#identityContext(backend2);
|
|
18275
|
+
return loadCurrentStructuralClasses(
|
|
18276
|
+
backend2,
|
|
18277
|
+
ctx.schema,
|
|
18278
|
+
this.graphId,
|
|
18279
|
+
references
|
|
18280
|
+
);
|
|
18281
|
+
},
|
|
18282
|
+
readCurrentIdentityAssertions: (mode, options2) => this.readCurrentIdentityAssertions(mode, options2),
|
|
18283
|
+
identityAssertionsAtTarget: (target, mode) => this.identityAssertionsAtTarget(target, mode),
|
|
18284
|
+
readIdentityAssertionPageAtTarget: (target, mode, options2) => this.readIdentityAssertionPageAtTarget(target, mode, options2),
|
|
18285
|
+
lockIdentityImportTarget: (target) => this.lockIdentityImportTarget(target),
|
|
18286
|
+
foldImportedIdentityNodes: (target, references) => this.foldImportedIdentityNodes(target, references),
|
|
18287
|
+
importIdentityAssertionsAtTarget: (target, assertions, mode) => this.importIdentityAssertionsAtTarget(target, assertions, mode),
|
|
18288
|
+
applyIdentityMergeAtTarget: (target, retractionIds, assertions) => this.applyIdentityMergeAtTarget(target, retractionIds, assertions),
|
|
18289
|
+
assertIdentityClassesConsistentAtTarget: (target, seeds) => this.assertIdentityClassesConsistentAtTarget(target, seeds)
|
|
17084
18290
|
};
|
|
17085
18291
|
Object.defineProperty(this, STORE_RUNTIME, {
|
|
17086
18292
|
configurable: false,
|
|
@@ -17105,6 +18311,215 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
17105
18311
|
get registry() {
|
|
17106
18312
|
return this.#registry;
|
|
17107
18313
|
}
|
|
18314
|
+
/**
|
|
18315
|
+
* The TypeGraph Identity Profile facade. Reachable through the public
|
|
18316
|
+
* {@link Store} surface only on graphs that declared `identity: { ... }`;
|
|
18317
|
+
* the runtime guard below catches widened or JavaScript callers.
|
|
18318
|
+
*
|
|
18319
|
+
* The facade is memoized per store, so it must not close over any state a
|
|
18320
|
+
* lifecycle operation can move — see `#identityContext`.
|
|
18321
|
+
*/
|
|
18322
|
+
get identity() {
|
|
18323
|
+
this.#requireIdentityEnabled(
|
|
18324
|
+
'Add identity: { sameIdAcrossKinds: "fold" } to defineGraph(...).'
|
|
18325
|
+
);
|
|
18326
|
+
const existing = IDENTITY_FACADES.get(this);
|
|
18327
|
+
if (existing !== void 0) return existing;
|
|
18328
|
+
const facade = createIdentityFacade(this.#identityContext(this.#backend));
|
|
18329
|
+
IDENTITY_FACADES.set(this, facade);
|
|
18330
|
+
return facade;
|
|
18331
|
+
}
|
|
18332
|
+
/**
|
|
18333
|
+
* Refuses an identity operation on a graph that never declared
|
|
18334
|
+
* `identity: { ... }`, where none of the identity tables exist.
|
|
18335
|
+
*/
|
|
18336
|
+
#requireIdentityEnabled(suggestion) {
|
|
18337
|
+
if (this.#graph.identity !== void 0) return;
|
|
18338
|
+
throw new ConfigurationError(
|
|
18339
|
+
"Identity is not enabled for this graph.",
|
|
18340
|
+
{ code: "IDENTITY_NOT_ENABLED", graphId: this.graphId },
|
|
18341
|
+
suggestion === void 0 ? void 0 : { suggestion }
|
|
18342
|
+
);
|
|
18343
|
+
}
|
|
18344
|
+
/** @internal Builds the identity read facade for a pinned StoreView. */
|
|
18345
|
+
identityAtCoordinate(coordinate) {
|
|
18346
|
+
this.#requireIdentityEnabled();
|
|
18347
|
+
const recordedAsOf = coordinate.recorded?.asOf;
|
|
18348
|
+
const backend = recordedAsOf === void 0 ? this.#backend : this.#recordedReads.backendForCoordinate(
|
|
18349
|
+
coordinate,
|
|
18350
|
+
"recorded-identity"
|
|
18351
|
+
);
|
|
18352
|
+
const schema = recordedReadSchemaFor(
|
|
18353
|
+
this.#sqlSchema(),
|
|
18354
|
+
recordedAsOf,
|
|
18355
|
+
this.#recordedReadBinding,
|
|
18356
|
+
"recorded-identity"
|
|
18357
|
+
);
|
|
18358
|
+
return createIdentityReadFacade({
|
|
18359
|
+
...this.#identityContext(backend),
|
|
18360
|
+
schema,
|
|
18361
|
+
coordinate
|
|
18362
|
+
});
|
|
18363
|
+
}
|
|
18364
|
+
/** Rebuilds derived current identity closure without advancing revision. */
|
|
18365
|
+
async rebuildIdentityClosure() {
|
|
18366
|
+
this.#requireIdentityEnabled();
|
|
18367
|
+
await runInWriteTransaction(
|
|
18368
|
+
{
|
|
18369
|
+
graphId: this.graphId,
|
|
18370
|
+
schemaVersion: this.#schemaMetadata.schemaVersion,
|
|
18371
|
+
historyEnabled: this.#captureEnabled,
|
|
18372
|
+
revisionTrackingEnabled: this.#revisionTrackingEnabled,
|
|
18373
|
+
revisionSchema: this.#sqlSchema()
|
|
18374
|
+
},
|
|
18375
|
+
this.#baseBackend,
|
|
18376
|
+
async (target) => rebuildIdentityClosureForContext(this.#identityContext(target)),
|
|
18377
|
+
{ didWrite: () => false }
|
|
18378
|
+
);
|
|
18379
|
+
}
|
|
18380
|
+
/**
|
|
18381
|
+
* @internal Validates/rebuilds identity under schema-transition locks.
|
|
18382
|
+
*
|
|
18383
|
+
* `provisionDerivedRelations` carries the DDL `ensureIdentitySchemaStorage`
|
|
18384
|
+
* deliberately did not run outside this transaction, so an evolution that
|
|
18385
|
+
* fails leaves no half-published derived relation behind.
|
|
18386
|
+
*/
|
|
18387
|
+
async identitySchemaPreflight(target, provisionDerivedRelations) {
|
|
18388
|
+
await identitySchemaCommitPreflight(this.#identityContext(this.#backend), {
|
|
18389
|
+
enablement: false,
|
|
18390
|
+
provisionDerivedRelations
|
|
18391
|
+
})(target);
|
|
18392
|
+
}
|
|
18393
|
+
/** @internal Read-only startup integrity verification. */
|
|
18394
|
+
async validateIdentity() {
|
|
18395
|
+
if (this.#graph.identity === void 0) return;
|
|
18396
|
+
await validateIdentityForContext(this.#identityContext(this.#baseBackend));
|
|
18397
|
+
}
|
|
18398
|
+
/** @internal Cascades removed kinds in the schema-commit transaction. */
|
|
18399
|
+
async removeIdentityKindsInSchemaPreflight(target, kinds, options) {
|
|
18400
|
+
if (!this.#captureEnabled) {
|
|
18401
|
+
await removeIdentityKindsForContext(
|
|
18402
|
+
this.#identityContext(target),
|
|
18403
|
+
kinds,
|
|
18404
|
+
options
|
|
18405
|
+
);
|
|
18406
|
+
return;
|
|
18407
|
+
}
|
|
18408
|
+
const scope = createRecordedTransactionScope(
|
|
18409
|
+
target,
|
|
18410
|
+
this.#sqlSchema(),
|
|
18411
|
+
target.dialect === "sqlite"
|
|
18412
|
+
);
|
|
18413
|
+
await removeIdentityKindsForContext(
|
|
18414
|
+
this.#identityContext(scope.backend),
|
|
18415
|
+
kinds,
|
|
18416
|
+
options
|
|
18417
|
+
);
|
|
18418
|
+
await scope.flush();
|
|
18419
|
+
}
|
|
18420
|
+
/**
|
|
18421
|
+
* @internal Reads the graph's identity assertions in transfer shape, honoring
|
|
18422
|
+
* this store's SQL binding. Used by interchange export, base-version
|
|
18423
|
+
* fingerprinting, and merge staging/diff.
|
|
18424
|
+
*/
|
|
18425
|
+
readCurrentIdentityAssertions(mode, options) {
|
|
18426
|
+
if (this.#graph.identity === void 0) return Promise.resolve([]);
|
|
18427
|
+
return readIdentityAssertionsForInterchange(
|
|
18428
|
+
this.#identityContext(this.#baseBackend),
|
|
18429
|
+
mode,
|
|
18430
|
+
options
|
|
18431
|
+
);
|
|
18432
|
+
}
|
|
18433
|
+
/** @internal Reads identity truth through an already-bound transaction. */
|
|
18434
|
+
identityAssertionsAtTarget(target, mode = "state") {
|
|
18435
|
+
if (this.#graph.identity === void 0) return Promise.resolve([]);
|
|
18436
|
+
return readIdentityAssertionsForInterchange(
|
|
18437
|
+
this.#identityContext(target),
|
|
18438
|
+
mode
|
|
18439
|
+
);
|
|
18440
|
+
}
|
|
18441
|
+
/** @internal Reads one bounded identity-assertion page at a bound target. */
|
|
18442
|
+
readIdentityAssertionPageAtTarget(target, mode, options) {
|
|
18443
|
+
if (this.#graph.identity === void 0) {
|
|
18444
|
+
return Promise.resolve({ assertions: [], done: true });
|
|
18445
|
+
}
|
|
18446
|
+
return readIdentityAssertionPageAtTarget(
|
|
18447
|
+
this.#identityContext(target),
|
|
18448
|
+
target,
|
|
18449
|
+
mode,
|
|
18450
|
+
options
|
|
18451
|
+
);
|
|
18452
|
+
}
|
|
18453
|
+
/** @internal Acquires the enabled graph's identity lock for an import. */
|
|
18454
|
+
lockIdentityImportTarget(target) {
|
|
18455
|
+
return this.#graph.identity === void 0 ? Promise.resolve() : lockIdentityGraph(target, this.graphId);
|
|
18456
|
+
}
|
|
18457
|
+
/** @internal Restores same-id folding after the ops-layer import bypass. */
|
|
18458
|
+
foldImportedIdentityNodes(target, references) {
|
|
18459
|
+
if (this.#graph.identity === void 0 || references.length === 0) {
|
|
18460
|
+
return Promise.resolve();
|
|
18461
|
+
}
|
|
18462
|
+
return foldIdentityForCreatedNodes(
|
|
18463
|
+
{
|
|
18464
|
+
graphId: this.graphId,
|
|
18465
|
+
registry: this.#registry,
|
|
18466
|
+
sameIdAcrossKinds: this.#graph.identity.sameIdAcrossKinds,
|
|
18467
|
+
schema: this.#sqlSchema()
|
|
18468
|
+
},
|
|
18469
|
+
target,
|
|
18470
|
+
references
|
|
18471
|
+
);
|
|
18472
|
+
}
|
|
18473
|
+
/** @internal Applies identity interchange rows inside an import transaction. */
|
|
18474
|
+
importIdentityAssertionsAtTarget(target, assertions, mode) {
|
|
18475
|
+
if (assertions.length === 0) {
|
|
18476
|
+
return Promise.resolve({ created: 0, skipped: 0 });
|
|
18477
|
+
}
|
|
18478
|
+
if (this.#graph.identity === void 0) {
|
|
18479
|
+
throw new ConfigurationError(
|
|
18480
|
+
"Cannot import identity assertions into an identity-disabled graph.",
|
|
18481
|
+
{ code: "IDENTITY_IMPORT_REQUIRES_PROFILE", graphId: this.graphId }
|
|
18482
|
+
);
|
|
18483
|
+
}
|
|
18484
|
+
return importIdentityAssertionsIntoTarget(
|
|
18485
|
+
this.#identityContext(target),
|
|
18486
|
+
target,
|
|
18487
|
+
assertions,
|
|
18488
|
+
mode
|
|
18489
|
+
);
|
|
18490
|
+
}
|
|
18491
|
+
/**
|
|
18492
|
+
* @internal Post-write identity validation for a graph-merge commit: proves
|
|
18493
|
+
* the identity classes the merge touched carry no contradiction in the state
|
|
18494
|
+
* the merge just wrote, inside the merge's own transaction.
|
|
18495
|
+
*/
|
|
18496
|
+
assertIdentityClassesConsistentAtTarget(target, seeds) {
|
|
18497
|
+
if (this.#graph.identity === void 0 || seeds.length === 0) {
|
|
18498
|
+
return Promise.resolve();
|
|
18499
|
+
}
|
|
18500
|
+
return assertAffectedIdentityClassesConsistent(
|
|
18501
|
+
this.#identityContext(target),
|
|
18502
|
+
target,
|
|
18503
|
+
seeds
|
|
18504
|
+
);
|
|
18505
|
+
}
|
|
18506
|
+
/** @internal Mechanical graph-merge apply through the mutation coordinator. */
|
|
18507
|
+
applyIdentityMergeAtTarget(target, retractionIds, assertions) {
|
|
18508
|
+
if (retractionIds.length === 0 && assertions.length === 0) {
|
|
18509
|
+
return Promise.resolve({ created: 0, retracted: 0 });
|
|
18510
|
+
}
|
|
18511
|
+
if (this.#graph.identity === void 0) {
|
|
18512
|
+
throw new ConfigurationError(
|
|
18513
|
+
"Cannot apply identity merge changes to an identity-disabled graph.",
|
|
18514
|
+
{ code: "IDENTITY_MERGE_REQUIRES_PROFILE", graphId: this.graphId }
|
|
18515
|
+
);
|
|
18516
|
+
}
|
|
18517
|
+
return applyIdentityChangesForContext(
|
|
18518
|
+
this.#identityContext(target),
|
|
18519
|
+
retractionIds,
|
|
18520
|
+
assertions
|
|
18521
|
+
);
|
|
18522
|
+
}
|
|
17108
18523
|
/**
|
|
17109
18524
|
* An opaque, in-memory snapshot of this store's reconciled schema — the
|
|
17110
18525
|
* merged compile-time + runtime-committed graph plus the committed version
|
|
@@ -17293,8 +18708,9 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
17293
18708
|
* `KindNotFoundError` when the kind is not registered.
|
|
17294
18709
|
*
|
|
17295
18710
|
* The dominant graph-extension-kind access pattern: `await store.evolve(...)`
|
|
17296
|
-
* returns
|
|
17297
|
-
* kind, and the null-check the optional variant requires
|
|
18711
|
+
* returns the Store for the resulting schema, the caller immediately
|
|
18712
|
+
* operates on the new kind, and the null-check the optional variant requires
|
|
18713
|
+
* is busywork.
|
|
17298
18714
|
*/
|
|
17299
18715
|
getNodeCollectionOrThrow(kind) {
|
|
17300
18716
|
const collection = this.getNodeCollection(kind);
|
|
@@ -17457,6 +18873,14 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
17457
18873
|
executeCreateBatch: (inputs, backend) => executeNodeCreateBatch(ctx, inputs, backend),
|
|
17458
18874
|
executeCreateNoReturnBatch: (inputs, backend) => executeNodeCreateNoReturnBatch(ctx, inputs, backend),
|
|
17459
18875
|
executeUpdate: (input, backend, options) => executeNodeUpdate(ctx, { ...input, id: input.id }, backend, options),
|
|
18876
|
+
executeUpdateWhere: (kind, patch, candidateIds, candidateIdColumn, backend) => executeNodeUpdateWhere(
|
|
18877
|
+
ctx,
|
|
18878
|
+
kind,
|
|
18879
|
+
patch,
|
|
18880
|
+
candidateIds,
|
|
18881
|
+
candidateIdColumn,
|
|
18882
|
+
backend
|
|
18883
|
+
),
|
|
17460
18884
|
executeUpsertUpdate: (input, backend, options) => executeNodeUpsertUpdate(
|
|
17461
18885
|
ctx,
|
|
17462
18886
|
{ ...input, id: input.id },
|
|
@@ -17468,6 +18892,7 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
17468
18892
|
upsertDirtyCheck: (kind, id, existingProps, inputProps) => nodeUpsertDirtyCheck(ctx, kind, id, existingProps, inputProps)
|
|
17469
18893
|
},
|
|
17470
18894
|
executeDelete: (kind, id, backend) => executeNodeDelete(ctx, kind, id, backend),
|
|
18895
|
+
executeDeleteBatch: (kind, ids, backend) => executeNodeDeleteBatch(ctx, kind, ids, backend),
|
|
17471
18896
|
executeHardDelete: (kind, id, backend) => executeNodeHardDelete(ctx, kind, id, backend),
|
|
17472
18897
|
temporalRowMatcher: (options) => this.#temporalRowMatcher(options),
|
|
17473
18898
|
createQuery: () => this.query(),
|
|
@@ -17525,8 +18950,9 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
17525
18950
|
...this.#options?.coalesceUnchangedUpserts === true && {
|
|
17526
18951
|
upsertDirtyCheck: (kind, id, existingProps, inputProps) => edgeUpsertDirtyCheck(ctx, kind, id, existingProps, inputProps)
|
|
17527
18952
|
},
|
|
17528
|
-
executeDelete: (id, backend) => executeEdgeDelete(ctx, id, backend),
|
|
17529
|
-
|
|
18953
|
+
executeDelete: (kind, id, backend) => executeEdgeDelete(ctx, kind, id, backend),
|
|
18954
|
+
executeDeleteBatch: (kind, ids, backend) => executeEdgeDeleteBatch(ctx, kind, ids, backend),
|
|
18955
|
+
executeHardDelete: (kind, id, backend) => executeEdgeHardDelete(ctx, kind, id, backend),
|
|
17530
18956
|
temporalRowMatcher: (options) => this.#temporalRowMatcher(options),
|
|
17531
18957
|
createQuery: () => this.query(),
|
|
17532
18958
|
executeGetOrCreateByEndpoints: (kind, fromKind, fromId, toKind, toId, props, backend, options) => executeEdgeGetOrCreateByEndpoints(
|
|
@@ -17927,6 +19353,96 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
17927
19353
|
return results;
|
|
17928
19354
|
});
|
|
17929
19355
|
}
|
|
19356
|
+
/**
|
|
19357
|
+
* Reads several edge kinds from heterogeneous source nodes with round trips
|
|
19358
|
+
* independent of the number of licensed edge/source-kind combinations.
|
|
19359
|
+
*
|
|
19360
|
+
* Results are flattened in source-group/id order. Repeated sources receive
|
|
19361
|
+
* independent edge arrays, and sources with no matching edges remain present
|
|
19362
|
+
* with an empty array. Inputs that exceed the backend bind budget are split
|
|
19363
|
+
* into source chunks without splitting one source across statements.
|
|
19364
|
+
*/
|
|
19365
|
+
async bulkFindEdgesFrom(params, options) {
|
|
19366
|
+
const sources = params.sources.flatMap(
|
|
19367
|
+
(group) => {
|
|
19368
|
+
if (!Object.hasOwn(this.#graph.nodes, group.kind)) {
|
|
19369
|
+
throw new KindNotFoundError(group.kind, "node", {
|
|
19370
|
+
graphId: this.graphId
|
|
19371
|
+
});
|
|
19372
|
+
}
|
|
19373
|
+
return group.ids.map((id) => ({ kind: group.kind, id }));
|
|
19374
|
+
}
|
|
19375
|
+
);
|
|
19376
|
+
for (const edgeKind of params.edgeKinds) {
|
|
19377
|
+
if (!Object.hasOwn(this.#graph.edges, edgeKind)) {
|
|
19378
|
+
throw new KindNotFoundError(edgeKind, "edge", {
|
|
19379
|
+
graphId: this.graphId
|
|
19380
|
+
});
|
|
19381
|
+
}
|
|
19382
|
+
}
|
|
19383
|
+
if (sources.length === 0 || params.edgeKinds.length === 0) {
|
|
19384
|
+
return sources.map((source) => ({ source, edges: [] }));
|
|
19385
|
+
}
|
|
19386
|
+
const readEndpointSet = this.#baseBackend.findEdgesByHeterogeneousEndpointSet;
|
|
19387
|
+
if (readEndpointSet === void 0) {
|
|
19388
|
+
throw new ConfigurationError(
|
|
19389
|
+
"store.bulkFindEdgesFrom() requires a backend that can read heterogeneous endpoint and edge-kind sets in set-oriented statements.",
|
|
19390
|
+
{
|
|
19391
|
+
backend: this.#baseBackend.dialect,
|
|
19392
|
+
capability: "findEdgesByHeterogeneousEndpointSet",
|
|
19393
|
+
operation: "bulkFindEdgesFrom"
|
|
19394
|
+
},
|
|
19395
|
+
{
|
|
19396
|
+
suggestion: "Use per-edge-kind bulkFindFrom calls explicitly if that round-trip tradeoff is acceptable."
|
|
19397
|
+
}
|
|
19398
|
+
);
|
|
19399
|
+
}
|
|
19400
|
+
const limitPerInput = options?.limitPerInput;
|
|
19401
|
+
if (limitPerInput !== void 0 && (!Number.isInteger(limitPerInput) || limitPerInput <= 0)) {
|
|
19402
|
+
throw new ValidationError(
|
|
19403
|
+
"bulk endpoint reads require limitPerInput to be a positive integer",
|
|
19404
|
+
{
|
|
19405
|
+
entityType: "edge",
|
|
19406
|
+
kind: params.edgeKinds.join(","),
|
|
19407
|
+
issues: [
|
|
19408
|
+
{
|
|
19409
|
+
path: "limitPerInput",
|
|
19410
|
+
message: `Expected a positive integer, received ${String(limitPerInput)}`,
|
|
19411
|
+
code: "invalid_value"
|
|
19412
|
+
}
|
|
19413
|
+
]
|
|
19414
|
+
}
|
|
19415
|
+
);
|
|
19416
|
+
}
|
|
19417
|
+
const temporal = resolveTemporalReadParams(
|
|
19418
|
+
options,
|
|
19419
|
+
this.#graph.defaults.temporalMode
|
|
19420
|
+
);
|
|
19421
|
+
const endpoints = sources;
|
|
19422
|
+
const rows = await readEndpointSet({
|
|
19423
|
+
graphId: this.graphId,
|
|
19424
|
+
side: "from",
|
|
19425
|
+
endpoints,
|
|
19426
|
+
edgeKinds: params.edgeKinds,
|
|
19427
|
+
...limitPerInput !== void 0 && this.#baseBackend.capabilities.windowFunctions && {
|
|
19428
|
+
limitPerEndpoint: limitPerInput
|
|
19429
|
+
},
|
|
19430
|
+
...temporal
|
|
19431
|
+
});
|
|
19432
|
+
const edgesBySource = /* @__PURE__ */ new Map();
|
|
19433
|
+
for (const row of rows) {
|
|
19434
|
+
const edge = rowToEdge(row);
|
|
19435
|
+
const key = `${edge.fromKind}\0${edge.fromId}`;
|
|
19436
|
+
const bucket = edgesBySource.get(key);
|
|
19437
|
+
if (bucket === void 0) edgesBySource.set(key, [edge]);
|
|
19438
|
+
else bucket.push(edge);
|
|
19439
|
+
}
|
|
19440
|
+
return sources.map((source) => {
|
|
19441
|
+
const bucket = edgesBySource.get(`${source.kind}\0${source.id}`) ?? [];
|
|
19442
|
+
const edges = limitPerInput === void 0 ? [...bucket] : bucket.slice(0, limitPerInput);
|
|
19443
|
+
return { source, edges };
|
|
19444
|
+
});
|
|
19445
|
+
}
|
|
17930
19446
|
// === Subgraph Extraction ===
|
|
17931
19447
|
/**
|
|
17932
19448
|
* Extracts a typed subgraph by traversing from a root node.
|
|
@@ -18041,9 +19557,12 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
18041
19557
|
receiptRecorder
|
|
18042
19558
|
));
|
|
18043
19559
|
}
|
|
19560
|
+
const identity = this.#graph.identity === void 0 ? void 0 : createIdentityFacade(this.#identityContext(this.#backend));
|
|
19561
|
+
const receiptIdentity = identity === void 0 || receiptRecorder === void 0 ? identity : wrapTransactionIdentity(identity, receiptRecorder);
|
|
18044
19562
|
const fallbackContext = {
|
|
18045
19563
|
nodes,
|
|
18046
19564
|
edges,
|
|
19565
|
+
...receiptIdentity === void 0 ? {} : { identity: receiptIdentity },
|
|
18047
19566
|
// No real transaction: `tx.sql` is absent and there is no atomicity.
|
|
18048
19567
|
sqlAvailability: "unavailable",
|
|
18049
19568
|
backend: createTransactionReadBackend(this.#backend),
|
|
@@ -18068,19 +19587,37 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
18068
19587
|
}
|
|
18069
19588
|
const pending = [];
|
|
18070
19589
|
const runHooks = this.#createBufferedHookRunner(pending);
|
|
19590
|
+
const runBulkHooks = this.#createBufferedBulkHookRunner(pending);
|
|
18071
19591
|
let recordedByGraph;
|
|
18072
19592
|
const transactionOptions = receiptRecorder !== void 0 && this.#captureEnabled ? withRecordedFlushObserver(backendOptions, (instants) => {
|
|
18073
19593
|
recordedByGraph = instants;
|
|
18074
19594
|
}) : backendOptions;
|
|
18075
19595
|
try {
|
|
18076
|
-
const run = async (txBackend, nativeTransaction) =>
|
|
18077
|
-
|
|
19596
|
+
const run = async (txBackend, nativeTransaction) => {
|
|
19597
|
+
const invokeTransaction = () => invoke(
|
|
19598
|
+
this.#buildTransactionContext(
|
|
19599
|
+
txBackend,
|
|
19600
|
+
nativeTransaction,
|
|
19601
|
+
runHooks,
|
|
19602
|
+
receiptRecorder,
|
|
19603
|
+
runBulkHooks
|
|
19604
|
+
)
|
|
19605
|
+
);
|
|
19606
|
+
if (!this.#captureEnabled && !this.#revisionTrackingEnabled) {
|
|
19607
|
+
return invokeTransaction();
|
|
19608
|
+
}
|
|
19609
|
+
return withWriteTransactionSession(
|
|
18078
19610
|
txBackend,
|
|
18079
|
-
|
|
18080
|
-
|
|
18081
|
-
|
|
18082
|
-
|
|
18083
|
-
|
|
19611
|
+
{
|
|
19612
|
+
graphId: this.graphId,
|
|
19613
|
+
schemaVersion: this.#schemaMetadata.schemaVersion,
|
|
19614
|
+
historyEnabled: this.#captureEnabled,
|
|
19615
|
+
revisionTrackingEnabled: this.#revisionTrackingEnabled,
|
|
19616
|
+
revisionSchema: this.#sqlSchema()
|
|
19617
|
+
},
|
|
19618
|
+
invokeTransaction
|
|
19619
|
+
);
|
|
19620
|
+
};
|
|
18084
19621
|
const result = this.#captureEnabled || this.#adapterBackend === void 0 ? await this.#backend.transaction(
|
|
18085
19622
|
(txBackend) => run(txBackend, void 0),
|
|
18086
19623
|
transactionOptions
|
|
@@ -18089,9 +19626,16 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
18089
19626
|
transactionOptions
|
|
18090
19627
|
);
|
|
18091
19628
|
for (const outcome of pending) {
|
|
18092
|
-
|
|
18093
|
-
|
|
18094
|
-
|
|
19629
|
+
if (outcome.type === "bulkOperation") {
|
|
19630
|
+
this.#hooks.onBulkOperationEnd?.(outcome.ctx, {
|
|
19631
|
+
affectedCount: outcome.affectedCount,
|
|
19632
|
+
durationMs: outcome.durationMs
|
|
19633
|
+
});
|
|
19634
|
+
} else {
|
|
19635
|
+
this.#hooks.onOperationEnd?.(outcome.ctx, {
|
|
19636
|
+
durationMs: outcome.durationMs
|
|
19637
|
+
});
|
|
19638
|
+
}
|
|
18095
19639
|
}
|
|
18096
19640
|
return { result, recordedByGraph };
|
|
18097
19641
|
} catch (error) {
|
|
@@ -18259,10 +19803,15 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
18259
19803
|
context.edges,
|
|
18260
19804
|
scopeRecorder
|
|
18261
19805
|
);
|
|
19806
|
+
const identity = this.#graph.identity === void 0 ? void 0 : wrapTransactionIdentity(
|
|
19807
|
+
context.identity,
|
|
19808
|
+
scopeRecorder
|
|
19809
|
+
);
|
|
18262
19810
|
const scoped = this.#attachMeasure(
|
|
18263
19811
|
overlayPropertyDescriptors(context, {
|
|
18264
19812
|
nodes,
|
|
18265
19813
|
edges,
|
|
19814
|
+
...identity === void 0 ? {} : { identity },
|
|
18266
19815
|
getNodeCollection: (kind) => this.#resolveDynamicNodeCollection(nodes, kind)
|
|
18267
19816
|
})
|
|
18268
19817
|
);
|
|
@@ -18281,9 +19830,11 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
18281
19830
|
* (#140); when history capture is enabled it is replaced with a fail-loud
|
|
18282
19831
|
* guard because raw graph writes would bypass recorded-time capture.
|
|
18283
19832
|
*/
|
|
18284
|
-
#buildTransactionContext(txBackend, sql2, runHooks = this.#immediateHookRunner(), receiptRecorder) {
|
|
19833
|
+
#buildTransactionContext(txBackend, sql2, runHooks = this.#immediateHookRunner(), receiptRecorder, runBulkHooks = this.#immediateBulkHookRunner()) {
|
|
18285
19834
|
const txNodeOperations = {
|
|
18286
|
-
...this.#buildNodeOperations(
|
|
19835
|
+
...this.#buildNodeOperations(
|
|
19836
|
+
this.#createNodeOperationContext(runHooks, runBulkHooks)
|
|
19837
|
+
),
|
|
18287
19838
|
createQuery: () => this.#createQueryForBackend(txBackend),
|
|
18288
19839
|
maybeRefreshStatisticsAfterBulk: void 0
|
|
18289
19840
|
};
|
|
@@ -18314,10 +19865,13 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
18314
19865
|
receiptRecorder
|
|
18315
19866
|
));
|
|
18316
19867
|
}
|
|
19868
|
+
const identity = this.#graph.identity === void 0 ? void 0 : createIdentityFacade(this.#identityContext(txBackend));
|
|
19869
|
+
const receiptIdentity = identity === void 0 || receiptRecorder === void 0 ? identity : wrapTransactionIdentity(identity, receiptRecorder);
|
|
18317
19870
|
const getNodeCollection = (kind) => this.#resolveDynamicNodeCollection(nodes, kind);
|
|
18318
19871
|
const base = {
|
|
18319
19872
|
nodes,
|
|
18320
19873
|
edges,
|
|
19874
|
+
...receiptIdentity === void 0 ? {} : { identity: receiptIdentity },
|
|
18321
19875
|
backend: createTransactionReadBackend(txBackend),
|
|
18322
19876
|
[TRANSACTION_RUNTIME]: { backend: txBackend, runNodeOperationHooks },
|
|
18323
19877
|
getNodeCollection
|
|
@@ -18350,6 +19904,67 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
18350
19904
|
});
|
|
18351
19905
|
return receiptRecorder === void 0 ? withSql : this.#attachMeasure(withSql);
|
|
18352
19906
|
}
|
|
19907
|
+
#identityContext(backend) {
|
|
19908
|
+
const currentSchemaVersion = () => this.#schemaMetadata.schemaVersion;
|
|
19909
|
+
return {
|
|
19910
|
+
graph: this.#graph,
|
|
19911
|
+
graphId: this.graphId,
|
|
19912
|
+
get schemaVersion() {
|
|
19913
|
+
return currentSchemaVersion();
|
|
19914
|
+
},
|
|
19915
|
+
registry: this.#registry,
|
|
19916
|
+
backend,
|
|
19917
|
+
schema: this.#sqlSchema(),
|
|
19918
|
+
historyEnabled: this.#captureEnabled,
|
|
19919
|
+
revisionTrackingEnabled: this.#revisionTrackingEnabled,
|
|
19920
|
+
sameIdAcrossKinds: this.#graph.identity?.sameIdAcrossKinds ?? "ignore",
|
|
19921
|
+
loadNodes: async (references, coordinate) => {
|
|
19922
|
+
const idsByKind = /* @__PURE__ */ new Map();
|
|
19923
|
+
for (const reference of references) {
|
|
19924
|
+
const ids = idsByKind.get(reference.kind) ?? /* @__PURE__ */ new Set();
|
|
19925
|
+
ids.add(reference.id);
|
|
19926
|
+
idsByKind.set(reference.kind, ids);
|
|
19927
|
+
}
|
|
19928
|
+
const loadedByReference = /* @__PURE__ */ new Map();
|
|
19929
|
+
await Promise.all(
|
|
19930
|
+
[...idsByKind].map(async ([kind, idSet]) => {
|
|
19931
|
+
const ids = [...idSet];
|
|
19932
|
+
if (coordinate?.recorded !== void 0) {
|
|
19933
|
+
const nodes = await this.recordedNodeGetByIds(
|
|
19934
|
+
kind,
|
|
19935
|
+
ids.map((id) => id),
|
|
19936
|
+
coordinate
|
|
19937
|
+
);
|
|
19938
|
+
for (const [index, node] of nodes.entries()) {
|
|
19939
|
+
if (node !== void 0) {
|
|
19940
|
+
loadedByReference.set(
|
|
19941
|
+
refKey({ kind, id: requireDefined(ids[index]) }),
|
|
19942
|
+
node
|
|
19943
|
+
);
|
|
19944
|
+
}
|
|
19945
|
+
}
|
|
19946
|
+
return;
|
|
19947
|
+
}
|
|
19948
|
+
const rows = backend.getNodes === void 0 ? await Promise.all(
|
|
19949
|
+
ids.map((id) => backend.getNode(this.graphId, kind, id))
|
|
19950
|
+
) : await backend.getNodes(this.graphId, kind, ids);
|
|
19951
|
+
const includeTombstoned = coordinate?.valid.mode === "includeTombstones";
|
|
19952
|
+
for (const row of rows) {
|
|
19953
|
+
if (row === void 0) continue;
|
|
19954
|
+
if (!includeTombstoned && row.deleted_at !== void 0) continue;
|
|
19955
|
+
loadedByReference.set(
|
|
19956
|
+
refKey({ kind, id: row.id }),
|
|
19957
|
+
rowToNode(row)
|
|
19958
|
+
);
|
|
19959
|
+
}
|
|
19960
|
+
})
|
|
19961
|
+
);
|
|
19962
|
+
return references.map(
|
|
19963
|
+
(reference) => loadedByReference.get(refKey(reference))
|
|
19964
|
+
);
|
|
19965
|
+
}
|
|
19966
|
+
};
|
|
19967
|
+
}
|
|
18353
19968
|
// === Graph Lifecycle ===
|
|
18354
19969
|
/**
|
|
18355
19970
|
* Hard-deletes all data for this graph from the database.
|
|
@@ -18365,6 +19980,9 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
18365
19980
|
if (this.#revisionTrackingEnabled) {
|
|
18366
19981
|
await lockRecordedGraphWrite(target, this.graphId);
|
|
18367
19982
|
}
|
|
19983
|
+
if (this.#graph.identity !== void 0) {
|
|
19984
|
+
await lockIdentityGraph(target, this.graphId);
|
|
19985
|
+
}
|
|
18368
19986
|
const previousRevision = this.#revisionTrackingEnabled && !this.#captureEnabled ? await readRecordedClock(target, this.#sqlSchema(), this.graphId) : void 0;
|
|
18369
19987
|
await target.clearGraph(this.graphId);
|
|
18370
19988
|
if (this.#revisionTrackingEnabled && !this.#captureEnabled) {
|
|
@@ -18451,12 +20069,14 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
18451
20069
|
/**
|
|
18452
20070
|
* Evolves the graph at runtime by merging a graph extension into the
|
|
18453
20071
|
* current schema, atomically committing a new schema version, and
|
|
18454
|
-
* returning
|
|
20072
|
+
* returning the `Store` for the merged graph.
|
|
18455
20073
|
*
|
|
18456
20074
|
* The `Store` is immutable — its registry, collections, and operation
|
|
18457
|
-
* contexts close over the graph at construction time
|
|
18458
|
-
* must use the returned
|
|
18459
|
-
*
|
|
20075
|
+
* contexts close over the graph at construction time. After `evolve()`
|
|
20076
|
+
* resolves, callers must use the returned Store (or the re-pointed
|
|
20077
|
+
* `ref.current`) for every subsequent operation in the same request. A
|
|
20078
|
+
* Store captured before the schema commit remains pinned to the old version,
|
|
20079
|
+
* and its next managed write is rejected by the schema fence.
|
|
18460
20080
|
*
|
|
18461
20081
|
* **Cost for purely additive extensions is proportional to schema
|
|
18462
20082
|
* document size, not row count.** The commit is a single CAS write.
|
|
@@ -18474,10 +20094,10 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
18474
20094
|
*
|
|
18475
20095
|
* @param extension - Graph extension produced by
|
|
18476
20096
|
* `defineGraphExtension(...)`.
|
|
18477
|
-
* @param options.ref - Optional handle whose `current` is overwritten
|
|
18478
|
-
*
|
|
18479
|
-
*
|
|
18480
|
-
*
|
|
20097
|
+
* @param options.ref - Optional handle whose `current` is overwritten with
|
|
20098
|
+
* the replacement before a successful call resolves. Code awaiting this
|
|
20099
|
+
* call can use the returned Store or `ref.current` immediately. A
|
|
20100
|
+
* previously captured Store is not mutated.
|
|
18481
20101
|
*
|
|
18482
20102
|
* @throws {GraphExtensionValidationError} when the extension is
|
|
18483
20103
|
* structurally invalid.
|
|
@@ -18525,12 +20145,40 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
18525
20145
|
this.graphId
|
|
18526
20146
|
);
|
|
18527
20147
|
}
|
|
18528
|
-
const
|
|
20148
|
+
const identityCandidate = merged.identity === void 0 ? void 0 : this.#cloneWithGraph(merged, void 0);
|
|
20149
|
+
const identityProvisioning = identityCandidate === void 0 ? void 0 : await ensureIdentitySchemaStorage(
|
|
20150
|
+
this.#baseBackend,
|
|
20151
|
+
this.#sqlSchema(),
|
|
20152
|
+
{
|
|
20153
|
+
graphId: this.graphId,
|
|
20154
|
+
enablement: false,
|
|
20155
|
+
// The evolved graph's registry — the one the candidate's own
|
|
20156
|
+
// preflight rebuilds through, so the predicate and the fill agree
|
|
20157
|
+
// on which assertions count.
|
|
20158
|
+
registry: buildKindRegistry(merged)
|
|
20159
|
+
}
|
|
20160
|
+
);
|
|
20161
|
+
const committed = identityCandidate === void 0 ? classification.requireEmpty.length > 0 ? await commitEvolvedSchemaWhenRequiredKindsAreEmpty(
|
|
18529
20162
|
this.#backend,
|
|
18530
20163
|
merged,
|
|
18531
20164
|
activeRow.version,
|
|
18532
20165
|
classification
|
|
18533
|
-
) : await commitNewSchemaVersion(this.#backend, merged, activeRow.version)
|
|
20166
|
+
) : await commitNewSchemaVersion(this.#backend, merged, activeRow.version) : await commitNewSchemaVersionWithPreflight(
|
|
20167
|
+
this.#backend,
|
|
20168
|
+
merged,
|
|
20169
|
+
activeRow.version,
|
|
20170
|
+
async (target) => {
|
|
20171
|
+
await assertEvolvedSchemaRequiredKindsEmpty(
|
|
20172
|
+
target,
|
|
20173
|
+
this.graphId,
|
|
20174
|
+
classification
|
|
20175
|
+
);
|
|
20176
|
+
await identityCandidate.identitySchemaPreflight(
|
|
20177
|
+
target,
|
|
20178
|
+
identityProvisioning?.provisionInCommit ?? []
|
|
20179
|
+
);
|
|
20180
|
+
}
|
|
20181
|
+
);
|
|
18534
20182
|
await materializeVectorContributions(this.#backend, merged);
|
|
18535
20183
|
const evolved = this.#cloneWithGraph(
|
|
18536
20184
|
merged,
|
|
@@ -18561,8 +20209,8 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
18561
20209
|
* kind that's already marked is a no-op (no version bump).
|
|
18562
20210
|
*
|
|
18563
20211
|
* @param names - Node or edge kind names to mark as deprecated.
|
|
18564
|
-
* @param options.ref - Optional handle to be re-pointed
|
|
18565
|
-
*
|
|
20212
|
+
* @param options.ref - Optional handle to be re-pointed to the replacement
|
|
20213
|
+
* Store before a successful call resolves, mirroring `evolve()`.
|
|
18566
20214
|
*
|
|
18567
20215
|
* @throws {ConfigurationError} on `DEPRECATE_BEFORE_INITIALIZE` (no
|
|
18568
20216
|
* schema yet) or `DEPRECATE_UNKNOWN_KIND` (name not on the graph).
|
|
@@ -18583,8 +20231,8 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
18583
20231
|
*
|
|
18584
20232
|
* @param names - Node or edge kind names to remove from the
|
|
18585
20233
|
* deprecated set.
|
|
18586
|
-
* @param options.ref - Optional handle to be re-pointed
|
|
18587
|
-
*
|
|
20234
|
+
* @param options.ref - Optional handle to be re-pointed to the replacement
|
|
20235
|
+
* Store before a successful call resolves.
|
|
18588
20236
|
*
|
|
18589
20237
|
* @throws {ConfigurationError} on `DEPRECATE_BEFORE_INITIALIZE`.
|
|
18590
20238
|
* @throws {StaleVersionError} on a CAS race with another writer.
|
|
@@ -18864,6 +20512,202 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
18864
20512
|
const vectorSlots = backend.capabilities.vector?.supported === true ? resolveGraphVectorSlots(this.#graph) : [];
|
|
18865
20513
|
return verify(this.graphId, vectorSlots);
|
|
18866
20514
|
}
|
|
20515
|
+
/**
|
|
20516
|
+
* Non-destructively repairs contribution states whose physical storage can
|
|
20517
|
+
* be preserved: `missing-marker` and `failed-materialization`.
|
|
20518
|
+
* `orphaned-marker` and `stale` findings are returned as
|
|
20519
|
+
* `requires-rebuild`; this method never drops contribution tables or
|
|
20520
|
+
* fabricates the data needed to rebuild them.
|
|
20521
|
+
*
|
|
20522
|
+
* The repair re-audits inside the backend and resolves every declaration
|
|
20523
|
+
* from the committed schema. Callers do not supply diagnostics, physical
|
|
20524
|
+
* names, or DDL. A stale Store is caught up to the active persisted graph
|
|
20525
|
+
* before its vector slots are enumerated, so repair does not use declarations
|
|
20526
|
+
* from the Store's stale in-memory graph snapshot.
|
|
20527
|
+
*
|
|
20528
|
+
* @throws {ConfigurationError} when no schema has been initialized or the
|
|
20529
|
+
* backend cannot probe and repair strategy-owned contributions.
|
|
20530
|
+
*/
|
|
20531
|
+
async repairContributions() {
|
|
20532
|
+
const backend = this.#baseBackend;
|
|
20533
|
+
const repair = backend.repairContributions;
|
|
20534
|
+
if (repair === void 0) {
|
|
20535
|
+
throw new ConfigurationError(
|
|
20536
|
+
"repairContributions requires a backend that can probe and repair strategy-owned contribution tables.",
|
|
20537
|
+
{
|
|
20538
|
+
backend: backend.dialect,
|
|
20539
|
+
capability: "contributions",
|
|
20540
|
+
operation: "repair"
|
|
20541
|
+
}
|
|
20542
|
+
);
|
|
20543
|
+
}
|
|
20544
|
+
const { baseline } = await this.#loadCaughtUp("repair");
|
|
20545
|
+
const vectorSlots = backend.capabilities.vector?.supported === true ? resolveGraphVectorSlots(baseline) : [];
|
|
20546
|
+
return repair(this.graphId, vectorSlots);
|
|
20547
|
+
}
|
|
20548
|
+
/**
|
|
20549
|
+
* Read-only readiness check for the search projections: is search
|
|
20550
|
+
* coherent with the graph right now, or is a query about to hit a
|
|
20551
|
+
* degraded index?
|
|
20552
|
+
*
|
|
20553
|
+
* The read-only half of {@link Store.repairContributions}, and the
|
|
20554
|
+
* bottom rung of the contribution health ladder — probe (read) →
|
|
20555
|
+
* `repairContributions()` (incremental, non-destructive) →
|
|
20556
|
+
* {@link Store.rebuildContribution} (destructive full reset). It shares
|
|
20557
|
+
* the detection logic of the other two exactly: a second implementation
|
|
20558
|
+
* would be free to disagree with the one the hot-path gate consults,
|
|
20559
|
+
* and a health check that disagrees with the gate is worse than none.
|
|
20560
|
+
*
|
|
20561
|
+
* Writes nothing — no DDL, no marker writes, no effect on the
|
|
20562
|
+
* per-instance caches the hot path relies on — so it is safe on a read
|
|
20563
|
+
* path, on a replica, and under a least-privilege role. That is the
|
|
20564
|
+
* whole reason it exists: the alternative for a caller who wants to
|
|
20565
|
+
* know whether search is ready was to run `repairContributions()`, a
|
|
20566
|
+
* write with repair side effects, and hope.
|
|
20567
|
+
*
|
|
20568
|
+
* One entry per search projection that this graph actually declares
|
|
20569
|
+
* contributions for. A projection with none is omitted rather than
|
|
20570
|
+
* reported `ready`, so an empty `entries` array means "nothing to
|
|
20571
|
+
* assess" — never "assessed and healthy". Use
|
|
20572
|
+
* {@link Store.verifyContributions} for the structured per-table
|
|
20573
|
+
* diagnostics behind a `degraded` entry.
|
|
20574
|
+
*
|
|
20575
|
+
* @throws {ConfigurationError} when the backend provisions
|
|
20576
|
+
* contributions but cannot probe its own catalog. Answering `ready`
|
|
20577
|
+
* without having looked is the one answer this method must never
|
|
20578
|
+
* give. A backend with no contribution machinery at all has nothing
|
|
20579
|
+
* to assess and resolves to empty entries instead.
|
|
20580
|
+
*/
|
|
20581
|
+
async probeContributions() {
|
|
20582
|
+
const backend = this.#baseBackend;
|
|
20583
|
+
const probe = backend.probeContributions;
|
|
20584
|
+
if (probe === void 0) {
|
|
20585
|
+
if (backend.capabilities.contributions?.supported !== true) {
|
|
20586
|
+
return { entries: [] };
|
|
20587
|
+
}
|
|
20588
|
+
throw new ConfigurationError(
|
|
20589
|
+
"probeContributions requires a backend that can probe its catalog for contribution tables.",
|
|
20590
|
+
{
|
|
20591
|
+
backend: backend.dialect,
|
|
20592
|
+
capability: "contributions",
|
|
20593
|
+
operation: "probe"
|
|
20594
|
+
}
|
|
20595
|
+
);
|
|
20596
|
+
}
|
|
20597
|
+
const vectorSlots = backend.capabilities.vector?.supported === true ? resolveGraphVectorSlots(this.#graph) : [];
|
|
20598
|
+
const entries = await probe(this.graphId, vectorSlots);
|
|
20599
|
+
const revision = await this.revisionNow();
|
|
20600
|
+
return {
|
|
20601
|
+
...revision === void 0 ? {} : { graphRevision: revision },
|
|
20602
|
+
entries
|
|
20603
|
+
};
|
|
20604
|
+
}
|
|
20605
|
+
/**
|
|
20606
|
+
* Destructively rebuilds one search projection for THIS graph: clears the
|
|
20607
|
+
* graph's rows from the projection's storage, recreates that storage from
|
|
20608
|
+
* the current DDL when recreating it is safe, reconstructs the content
|
|
20609
|
+
* from the node rows, and stamps the durable marker at the current
|
|
20610
|
+
* signature.
|
|
20611
|
+
*
|
|
20612
|
+
* **Scoped to this graph.** The fulltext table is one physical table
|
|
20613
|
+
* holding every graph's rows, keyed by `graph_id`, and this method is
|
|
20614
|
+
* fenced per graph — so it removes only this graph's rows
|
|
20615
|
+
* (`DELETE ... WHERE graph_id`, the same statement `clear()` uses) and
|
|
20616
|
+
* escalates to dropping and recreating the table only when no other graph
|
|
20617
|
+
* has rows in it. That drop is the one repair for storage provisioned at a
|
|
20618
|
+
* shape the current DDL no longer produces; when another graph's content
|
|
20619
|
+
* is in the way it is refused
|
|
20620
|
+
* ({@link ContributionRebuildUnsupportedError}, reason
|
|
20621
|
+
* `shared-storage-in-use`) rather than taken, because fulltext content is
|
|
20622
|
+
* reconstructed from a graph's own nodes through its own schema and no
|
|
20623
|
+
* other process can put back what the drop would destroy.
|
|
20624
|
+
*
|
|
20625
|
+
* The top rung of the health ladder, and the repair
|
|
20626
|
+
* {@link Store.repairContributions} deliberately refuses to perform.
|
|
20627
|
+
* Never reachable from that method — a `stale` contribution's table
|
|
20628
|
+
* exists at the *old* shape, so the ordinary ensure path's idempotent
|
|
20629
|
+
* `CREATE ... IF NOT EXISTS` no-ops and re-stamping the marker there
|
|
20630
|
+
* would leave it blessing a table whose shape is wrong, which is
|
|
20631
|
+
* exactly what the drift guard exists to prevent. Only a recreate makes
|
|
20632
|
+
* the ensure meaningful, and destroying content is a decision stated at
|
|
20633
|
+
* the call site rather than reached through a flag named `force`.
|
|
20634
|
+
*
|
|
20635
|
+
* **This destroys this graph's index content.** For `"fulltext"` nothing
|
|
20636
|
+
* is permanently lost: the searchable content is derived from node
|
|
20637
|
+
* properties TypeGraph already stores, so the refill reconstructs it in
|
|
20638
|
+
* the same transaction. Nodes whose stored `props` cannot be read as an
|
|
20639
|
+
* object are counted in `skipped` and are absent from the rebuilt index —
|
|
20640
|
+
* `store.search.rebuildFulltext()` reports their ids individually.
|
|
20641
|
+
*
|
|
20642
|
+
* Atomic: the clear (or drop), recreate, refill, and stamp all run inside
|
|
20643
|
+
* one transaction under the same per-graph fence as a schema commit, plus
|
|
20644
|
+
* a database-scoped advisory lock serializing this contribution's DDL
|
|
20645
|
+
* across graphs, so an interrupted rebuild leaves the contribution exactly
|
|
20646
|
+
* as it was rather than attested-but-empty.
|
|
20647
|
+
*
|
|
20648
|
+
* The cost differs by path, and only one of them is a maintenance-window
|
|
20649
|
+
* operation. A rebuild that drops and recreates the shared table takes
|
|
20650
|
+
* `ACCESS EXCLUSIVE` on it before deciding to — the verdict "no other
|
|
20651
|
+
* graph has rows here" is only sound under the exclusion the drop needs —
|
|
20652
|
+
* so on PostgreSQL every graph's fulltext writers wait for the whole
|
|
20653
|
+
* refill. That was already true of the drop itself, which holds the same
|
|
20654
|
+
* lock to commit; the exclusion now merely starts a few statements
|
|
20655
|
+
* earlier. The graph-scoped path takes no table lock at all: its
|
|
20656
|
+
* `DELETE ... WHERE graph_id` is transactional and touches only this
|
|
20657
|
+
* graph's rows, so it never makes another graph's writers wait.
|
|
20658
|
+
* `store.search.rebuildFulltext()` remains the incremental, resumable way
|
|
20659
|
+
* to refresh content when the storage shape is fine.
|
|
20660
|
+
*
|
|
20661
|
+
* @throws {ContributionRebuildUnsupportedError} for `"vector"`, always:
|
|
20662
|
+
* TypeGraph stores the vectors callers supply and never the inputs
|
|
20663
|
+
* that produced them, so embeddings exist only in the storage a
|
|
20664
|
+
* rebuild would drop and there is nothing to reconstruct them from.
|
|
20665
|
+
* `store.reembedVectorField(kind, fieldPath, { embed })` is the
|
|
20666
|
+
* sanctioned destructive path — it takes the callback that can
|
|
20667
|
+
* regenerate what the drop destroys. Also thrown when the active
|
|
20668
|
+
* strategy declares no teardown DDL, when the backend has no
|
|
20669
|
+
* transactional schema fence to make the sequence atomic, and when the
|
|
20670
|
+
* recorded shape is stale but the storage that would have to be
|
|
20671
|
+
* recreated holds another graph's rows (`shared-storage-in-use`). Every
|
|
20672
|
+
* one of these refuses before anything is dropped or deleted.
|
|
20673
|
+
* @throws {ConfigurationError} when the backend cannot rebuild
|
|
20674
|
+
* strategy-owned contributions at all.
|
|
20675
|
+
*/
|
|
20676
|
+
async rebuildContribution(scope, options) {
|
|
20677
|
+
const backend = this.#baseBackend;
|
|
20678
|
+
const rebuild = backend.rebuildContribution;
|
|
20679
|
+
if (rebuild === void 0) {
|
|
20680
|
+
throw new ConfigurationError(
|
|
20681
|
+
"rebuildContribution requires a backend that can drop, recreate, and re-stamp strategy-owned contribution tables.",
|
|
20682
|
+
{
|
|
20683
|
+
backend: backend.dialect,
|
|
20684
|
+
capability: "contributions",
|
|
20685
|
+
operation: "rebuild"
|
|
20686
|
+
}
|
|
20687
|
+
);
|
|
20688
|
+
}
|
|
20689
|
+
await this.#loadCaughtUp("rebuild");
|
|
20690
|
+
const registry = this.#registry;
|
|
20691
|
+
const graphId = this.graphId;
|
|
20692
|
+
return rebuild(graphId, scope, async (target) => {
|
|
20693
|
+
if (scope !== "fulltext") {
|
|
20694
|
+
throw new ContributionRebuildUnsupportedError(
|
|
20695
|
+
"vector-source-unavailable",
|
|
20696
|
+
{ graphId, contribution: scope, backend: backend.dialect }
|
|
20697
|
+
);
|
|
20698
|
+
}
|
|
20699
|
+
const result = await repopulateFulltextInTransaction(
|
|
20700
|
+
{ graphId, registry },
|
|
20701
|
+
target,
|
|
20702
|
+
options?.pageSize === void 0 ? {} : { pageSize: options.pageSize }
|
|
20703
|
+
);
|
|
20704
|
+
return {
|
|
20705
|
+
processed: result.processed,
|
|
20706
|
+
repopulated: result.upserted,
|
|
20707
|
+
skipped: result.skipped
|
|
20708
|
+
};
|
|
20709
|
+
});
|
|
20710
|
+
}
|
|
18867
20711
|
/**
|
|
18868
20712
|
* Removes extension kinds from the schema with cascading edge and
|
|
18869
20713
|
* ontology cleanup. Two-phase by design:
|
|
@@ -18911,16 +20755,26 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
18911
20755
|
const compileTimeGraph = stripGraphExtension(this.#graph);
|
|
18912
20756
|
const merged = plan.document === void 0 ? compileTimeGraph : mergeGraphExtension(compileTimeGraph, plan.document);
|
|
18913
20757
|
const finalGraph = applyDeprecatedKinds(merged, baseline.deprecatedKinds);
|
|
18914
|
-
const
|
|
20758
|
+
const identityCascade = await this.#identityRemovalPreflight(
|
|
20759
|
+
finalGraph,
|
|
20760
|
+
plan.removedNodeKinds
|
|
20761
|
+
);
|
|
20762
|
+
const committedRow = identityCascade === void 0 ? await commitNewSchemaVersion(
|
|
18915
20763
|
this.#backend,
|
|
18916
20764
|
finalGraph,
|
|
18917
20765
|
activeRow.version
|
|
20766
|
+
) : await commitNewSchemaVersionWithPreflight(
|
|
20767
|
+
this.#backend,
|
|
20768
|
+
finalGraph,
|
|
20769
|
+
activeRow.version,
|
|
20770
|
+
identityCascade
|
|
18918
20771
|
);
|
|
18919
20772
|
const recordKindRemoval = this.#backend.recordKindRemoval;
|
|
18920
20773
|
if (recordKindRemoval !== void 0) {
|
|
18921
|
-
|
|
18922
|
-
|
|
18923
|
-
|
|
20774
|
+
await ensureFocusedStatusTable(
|
|
20775
|
+
this.#backend,
|
|
20776
|
+
this.#backend.ensureKindRemovalsTable
|
|
20777
|
+
);
|
|
18924
20778
|
const attemptedAt = nowIso();
|
|
18925
20779
|
const newSchemaVersion = committedRow.version;
|
|
18926
20780
|
const queue = (kindName, entity) => recordKindRemoval(
|
|
@@ -18951,6 +20805,35 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
18951
20805
|
}
|
|
18952
20806
|
return evolved;
|
|
18953
20807
|
}
|
|
20808
|
+
/**
|
|
20809
|
+
* The identity cascade `removeKinds()` commits with, or `undefined` when the
|
|
20810
|
+
* removal cannot touch the assertion ledger.
|
|
20811
|
+
*
|
|
20812
|
+
* Identity being switched OFF is not that case: disabling identity retains
|
|
20813
|
+
* the assertion rows, so a graph with no profile can still hold assertions
|
|
20814
|
+
* naming the kinds being removed. Those rows are cascaded too — otherwise
|
|
20815
|
+
* they survive as current orphans that a later re-enablement filters out of
|
|
20816
|
+
* the closure while raw ledger reads and merge staging still see them. What
|
|
20817
|
+
* the profile's absence does remove is the closure contract, so the repair
|
|
20818
|
+
* pass is skipped and the ledger cleanup runs alone — and a graph that has no
|
|
20819
|
+
* such rows keeps committing through the plain schema-commit primitive.
|
|
20820
|
+
*/
|
|
20821
|
+
async #identityRemovalPreflight(finalGraph, removedNodeKinds) {
|
|
20822
|
+
if (removedNodeKinds.length === 0) return void 0;
|
|
20823
|
+
const repairClosure = finalGraph.identity !== void 0;
|
|
20824
|
+
if (!repairClosure && !await identityKindCascadeNeeded(
|
|
20825
|
+
this.#backend,
|
|
20826
|
+
this.#sqlSchema(),
|
|
20827
|
+
this.graphId,
|
|
20828
|
+
removedNodeKinds
|
|
20829
|
+
)) {
|
|
20830
|
+
return void 0;
|
|
20831
|
+
}
|
|
20832
|
+
const candidate = this.#cloneWithGraph(finalGraph, void 0);
|
|
20833
|
+
return async (target) => candidate.removeIdentityKindsInSchemaPreflight(target, removedNodeKinds, {
|
|
20834
|
+
repairClosure
|
|
20835
|
+
});
|
|
20836
|
+
}
|
|
18954
20837
|
/**
|
|
18955
20838
|
* Runs the data-cleanup phase for any kinds removed via
|
|
18956
20839
|
* `removeKinds()` whose data has not yet been deleted on this
|
|
@@ -19053,12 +20936,16 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
19053
20936
|
const getPendingKindRemovals = this.#backend.getPendingKindRemovals;
|
|
19054
20937
|
if (getPendingKindRemovals === void 0) return;
|
|
19055
20938
|
const addedNodes = Object.keys(merged.nodes).filter(
|
|
19056
|
-
(kind) => !(
|
|
20939
|
+
(kind) => !hasOwnKey(baseline.nodes, kind)
|
|
19057
20940
|
);
|
|
19058
20941
|
const addedEdges = Object.keys(merged.edges).filter(
|
|
19059
|
-
(kind) => !(
|
|
20942
|
+
(kind) => !hasOwnKey(baseline.edges, kind)
|
|
19060
20943
|
);
|
|
19061
20944
|
if (addedNodes.length === 0 && addedEdges.length === 0) return;
|
|
20945
|
+
await ensureFocusedStatusTable(
|
|
20946
|
+
this.#backend,
|
|
20947
|
+
this.#backend.ensureKindRemovalsTable
|
|
20948
|
+
);
|
|
19062
20949
|
const pending = await getPendingKindRemovals(this.graphId);
|
|
19063
20950
|
if (pending.length === 0) return;
|
|
19064
20951
|
const blocked = pending.filter(
|
|
@@ -19105,7 +20992,11 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
19105
20992
|
#immediateHookRunner() {
|
|
19106
20993
|
return (ctx, fn) => this.#withOperationHooks(ctx, fn);
|
|
19107
20994
|
}
|
|
19108
|
-
#
|
|
20995
|
+
#immediateBulkHookRunner() {
|
|
20996
|
+
return (ctx, fn) => this.#withBulkOperationHooks(ctx, fn);
|
|
20997
|
+
}
|
|
20998
|
+
#createNodeOperationContext(runHooks = this.#immediateHookRunner(), runBulkHooks = this.#immediateBulkHookRunner()) {
|
|
20999
|
+
const identityConfig = this.#graph.identity;
|
|
19109
21000
|
return {
|
|
19110
21001
|
graph: this.#graph,
|
|
19111
21002
|
graphId: this.graphId,
|
|
@@ -19114,8 +21005,40 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
19114
21005
|
revisionTrackingEnabled: this.#revisionTrackingEnabled,
|
|
19115
21006
|
revisionSchema: this.#sqlSchema(),
|
|
19116
21007
|
registry: this.#registry,
|
|
21008
|
+
...identityConfig === void 0 ? {} : {
|
|
21009
|
+
identity: {
|
|
21010
|
+
lock: (target) => lockIdentityGraph(target, this.graphId),
|
|
21011
|
+
foldCreated: (target, references) => foldIdentityForCreatedNodes(
|
|
21012
|
+
{
|
|
21013
|
+
graphId: this.graphId,
|
|
21014
|
+
registry: this.#registry,
|
|
21015
|
+
sameIdAcrossKinds: identityConfig.sameIdAcrossKinds,
|
|
21016
|
+
schema: this.#sqlSchema()
|
|
21017
|
+
},
|
|
21018
|
+
target,
|
|
21019
|
+
references
|
|
21020
|
+
),
|
|
21021
|
+
detachDeleted: (target, ref, mode) => detachIdentityForNode(
|
|
21022
|
+
{
|
|
21023
|
+
graphId: this.graphId,
|
|
21024
|
+
sameIdAcrossKinds: identityConfig.sameIdAcrossKinds,
|
|
21025
|
+
schema: this.#sqlSchema()
|
|
21026
|
+
},
|
|
21027
|
+
target,
|
|
21028
|
+
ref,
|
|
21029
|
+
mode
|
|
21030
|
+
)
|
|
21031
|
+
}
|
|
21032
|
+
},
|
|
19117
21033
|
createOperationContext: (operation, entity, kind, id) => this.#createOperationContext(operation, entity, kind, id),
|
|
19118
|
-
withOperationHooks: runHooks
|
|
21034
|
+
withOperationHooks: runHooks,
|
|
21035
|
+
createBulkOperationContext: (operation, kind) => ({
|
|
21036
|
+
...this.#createHookContext(),
|
|
21037
|
+
operation,
|
|
21038
|
+
entity: "node",
|
|
21039
|
+
kind
|
|
21040
|
+
}),
|
|
21041
|
+
withBulkOperationHooks: runBulkHooks
|
|
19119
21042
|
};
|
|
19120
21043
|
}
|
|
19121
21044
|
#createEdgeOperationContext(runHooks = this.#immediateHookRunner()) {
|
|
@@ -19136,6 +21059,11 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
19136
21059
|
* Decorates the query execution port so hooks observe each statement the
|
|
19137
21060
|
* query builder submits to the backend. A selective-projection retry
|
|
19138
21061
|
* therefore emits two independent start/end pairs with their actual SQL.
|
|
21062
|
+
*
|
|
21063
|
+
* Decoration goes through {@link createBackendOverlay}, which carries the
|
|
21064
|
+
* projection's serialized-resource ownership: the hooked backend still
|
|
21065
|
+
* executes on the source's connection, so it must answer the same "shares one
|
|
21066
|
+
* serialized connection" question as its source.
|
|
19139
21067
|
*/
|
|
19140
21068
|
#createHookedQueryBackend(backend) {
|
|
19141
21069
|
if (this.#hooks.onQueryStart === void 0 && this.#hooks.onQueryEnd === void 0 && this.#hooks.onError === void 0) {
|
|
@@ -19144,8 +21072,7 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
19144
21072
|
const executeRaw = backend.executeRaw;
|
|
19145
21073
|
const compileSql = backend.compileSql;
|
|
19146
21074
|
const projected = createGraphBackendProjection(backend);
|
|
19147
|
-
return {
|
|
19148
|
-
...projected,
|
|
21075
|
+
return createBackendOverlay(projected, {
|
|
19149
21076
|
execute: (query) => {
|
|
19150
21077
|
const compiled = compileSql === void 0 ? renderSql(query, backend.dialect) : compileSql(query);
|
|
19151
21078
|
return this.#withQueryHooks(
|
|
@@ -19161,7 +21088,7 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
19161
21088
|
() => executeRaw(sqlText, params)
|
|
19162
21089
|
)
|
|
19163
21090
|
}
|
|
19164
|
-
};
|
|
21091
|
+
});
|
|
19165
21092
|
}
|
|
19166
21093
|
async #withQueryHooks(sql2, params, execute) {
|
|
19167
21094
|
const ctx = {
|
|
@@ -19213,6 +21140,21 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
19213
21140
|
throw error;
|
|
19214
21141
|
}
|
|
19215
21142
|
}
|
|
21143
|
+
async #withBulkOperationHooks(ctx, fn) {
|
|
21144
|
+
this.#hooks.onBulkOperationStart?.(ctx);
|
|
21145
|
+
const startTime = Date.now();
|
|
21146
|
+
try {
|
|
21147
|
+
const result = await fn();
|
|
21148
|
+
this.#hooks.onBulkOperationEnd?.(ctx, {
|
|
21149
|
+
affectedCount: result.affectedCount,
|
|
21150
|
+
durationMs: Date.now() - startTime
|
|
21151
|
+
});
|
|
21152
|
+
return result;
|
|
21153
|
+
} catch (error) {
|
|
21154
|
+
this.#hooks.onError?.(ctx, asError(error));
|
|
21155
|
+
throw error;
|
|
21156
|
+
}
|
|
21157
|
+
}
|
|
19216
21158
|
/**
|
|
19217
21159
|
* A hook runner for operations that execute INSIDE an explicit
|
|
19218
21160
|
* `store.transaction`: `onOperationStart` (and a failed operation's
|
|
@@ -19229,7 +21171,31 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
19229
21171
|
const startTime = Date.now();
|
|
19230
21172
|
try {
|
|
19231
21173
|
const result = await fn();
|
|
19232
|
-
pending.push({
|
|
21174
|
+
pending.push({
|
|
21175
|
+
type: "operation",
|
|
21176
|
+
ctx,
|
|
21177
|
+
durationMs: Date.now() - startTime
|
|
21178
|
+
});
|
|
21179
|
+
return result;
|
|
21180
|
+
} catch (error) {
|
|
21181
|
+
this.#hooks.onError?.(ctx, asError(error));
|
|
21182
|
+
throw error;
|
|
21183
|
+
}
|
|
21184
|
+
};
|
|
21185
|
+
}
|
|
21186
|
+
/** Buffered counterpart for set-based operations inside `transaction()`. */
|
|
21187
|
+
#createBufferedBulkHookRunner(pending) {
|
|
21188
|
+
return async (ctx, fn) => {
|
|
21189
|
+
this.#hooks.onBulkOperationStart?.(ctx);
|
|
21190
|
+
const startTime = Date.now();
|
|
21191
|
+
try {
|
|
21192
|
+
const result = await fn();
|
|
21193
|
+
pending.push({
|
|
21194
|
+
type: "bulkOperation",
|
|
21195
|
+
ctx,
|
|
21196
|
+
affectedCount: result.affectedCount,
|
|
21197
|
+
durationMs: Date.now() - startTime
|
|
21198
|
+
});
|
|
19233
21199
|
return result;
|
|
19234
21200
|
} catch (error) {
|
|
19235
21201
|
this.#hooks.onError?.(ctx, asError(error));
|
|
@@ -19478,10 +21444,70 @@ async function createStoreWithSchema(graph, backend, options) {
|
|
|
19478
21444
|
prepared.result
|
|
19479
21445
|
];
|
|
19480
21446
|
}
|
|
19481
|
-
async function assertHistorySchemaOnOpen(backend, options) {
|
|
21447
|
+
async function assertHistorySchemaOnOpen(backend, graph, options) {
|
|
19482
21448
|
if (options?.history !== true) return;
|
|
19483
21449
|
const schema = options.schema === void 0 ? createSqlSchema(backend.tableNames) : requireSqlSchema(options.schema, "store schema");
|
|
19484
|
-
await assertCurrentRecordedSchema(
|
|
21450
|
+
await assertCurrentRecordedSchema(
|
|
21451
|
+
backend,
|
|
21452
|
+
schema,
|
|
21453
|
+
graph.identity !== void 0
|
|
21454
|
+
);
|
|
21455
|
+
}
|
|
21456
|
+
var IDENTITY_ONTOLOGY_META_EDGES = /* @__PURE__ */ new Set([
|
|
21457
|
+
META_EDGE_DISJOINT_WITH,
|
|
21458
|
+
META_EDGE_EQUIVALENT_TO,
|
|
21459
|
+
META_EDGE_SAME_AS,
|
|
21460
|
+
META_EDGE_SUB_CLASS_OF
|
|
21461
|
+
]);
|
|
21462
|
+
function identityOntologyChanged(before, after) {
|
|
21463
|
+
function relevantRelations(schema) {
|
|
21464
|
+
return schema.ontology.relations.filter((relation) => IDENTITY_ONTOLOGY_META_EDGES.has(relation.metaEdge)).map(
|
|
21465
|
+
(relation) => JSON.stringify([relation.metaEdge, relation.from, relation.to])
|
|
21466
|
+
).toSorted();
|
|
21467
|
+
}
|
|
21468
|
+
return JSON.stringify(relevantRelations(before)) !== JSON.stringify(relevantRelations(after));
|
|
21469
|
+
}
|
|
21470
|
+
var IDENTITY_GATE_CODES = {
|
|
21471
|
+
enablement: "IDENTITY_ENABLEMENT_PENDING",
|
|
21472
|
+
profile: "IDENTITY_PROFILE_MIGRATION_PENDING",
|
|
21473
|
+
ontology: "IDENTITY_SCHEMA_MIGRATION_PENDING"
|
|
21474
|
+
};
|
|
21475
|
+
async function ensureSchemaWithIdentityPrecedence(backend, graph, options, gate) {
|
|
21476
|
+
try {
|
|
21477
|
+
return await ensureSchema(backend, graph, options);
|
|
21478
|
+
} catch (error) {
|
|
21479
|
+
if (gate === void 0 || !isIdentityOnlyBreakingChange(error)) throw error;
|
|
21480
|
+
throw identityMigrationRequiredError(graph.id, gate, {
|
|
21481
|
+
blockedBy: "breaking-change",
|
|
21482
|
+
cause: error
|
|
21483
|
+
});
|
|
21484
|
+
}
|
|
21485
|
+
}
|
|
21486
|
+
function isIdentityOnlyBreakingChange(error) {
|
|
21487
|
+
if (!(error instanceof MigrationError)) return false;
|
|
21488
|
+
const diff = "diff" in error.details ? error.details.diff : void 0;
|
|
21489
|
+
if (diff?.identity?.severity !== "breaking") return false;
|
|
21490
|
+
return ![
|
|
21491
|
+
...diff.nodes,
|
|
21492
|
+
...diff.edges,
|
|
21493
|
+
...diff.ontology,
|
|
21494
|
+
...diff.indexes
|
|
21495
|
+
].some((change) => change.severity === "breaking");
|
|
21496
|
+
}
|
|
21497
|
+
function identityMigrationRequiredError(graphId, gate, context) {
|
|
21498
|
+
const blocker = context.blockedBy === "breaking-change" ? "but the change is breaking and has not been applied." : "but autoMigrate is disabled and the change is pending.";
|
|
21499
|
+
return new ConfigurationError(
|
|
21500
|
+
`Changing Operational Identity semantics requires the schema migration to commit, ${blocker}`,
|
|
21501
|
+
{
|
|
21502
|
+
code: IDENTITY_GATE_CODES[gate],
|
|
21503
|
+
graphId,
|
|
21504
|
+
...context.pendingVersion === void 0 ? {} : { pendingVersion: context.pendingVersion }
|
|
21505
|
+
},
|
|
21506
|
+
{
|
|
21507
|
+
cause: context.cause,
|
|
21508
|
+
suggestion: "Apply the migration explicitly with migrateSchema(backend, graph, activeVersion) \u2014 it commits the new version and rebuilds the identity closure in the same transaction \u2014 or, for a change that is not breaking, enable autoMigrate."
|
|
21509
|
+
}
|
|
21510
|
+
);
|
|
19485
21511
|
}
|
|
19486
21512
|
async function prepareStoreWithSchema(graph, backend, options) {
|
|
19487
21513
|
const {
|
|
@@ -19489,11 +21515,70 @@ async function prepareStoreWithSchema(graph, backend, options) {
|
|
|
19489
21515
|
activeRow,
|
|
19490
21516
|
storedSchema
|
|
19491
21517
|
} = await loadAndMergeGraphExtensionDocument(backend, graph);
|
|
19492
|
-
const
|
|
19493
|
-
|
|
21518
|
+
const identityProfileChanged = activeRow !== void 0 && storedSchema?.identity?.sameIdAcrossKinds !== merged.identity?.sameIdAcrossKinds;
|
|
21519
|
+
const identityInitialization = activeRow === void 0 && merged.identity !== void 0;
|
|
21520
|
+
const identityEnablement = identityInitialization || identityProfileChanged && storedSchema?.identity === void 0 && merged.identity !== void 0;
|
|
21521
|
+
const identitySemanticsChanged = identityProfileChanged || activeRow !== void 0 && storedSchema !== void 0 && merged.identity !== void 0 && identityOntologyChanged(
|
|
21522
|
+
storedSchema,
|
|
21523
|
+
serializeSchema(merged, activeRow.version + 1)
|
|
21524
|
+
);
|
|
21525
|
+
const identityGate = identitySemanticsChanged ? identityEnablement ? "enablement" : identityProfileChanged ? "profile" : "ontology" : void 0;
|
|
21526
|
+
const identityProfile = merged.identity;
|
|
21527
|
+
if (identityProfile !== void 0) {
|
|
21528
|
+
const resolvedIdentitySchema = options?.schema === void 0 ? createSqlSchema(backend.tableNames) : requireSqlSchema(options.schema, "store schema");
|
|
21529
|
+
const identityRegistry = buildKindRegistry(merged);
|
|
21530
|
+
await ensureIdentitySchemaStorage(backend, resolvedIdentitySchema, {
|
|
21531
|
+
graphId: merged.id,
|
|
21532
|
+
enablement: identityEnablement,
|
|
21533
|
+
registry: identityRegistry,
|
|
21534
|
+
// A derived relation this library version added, absent from (or left
|
|
21535
|
+
// empty in) a database that predates it, is created and FILLED as one
|
|
21536
|
+
// unit here — never created now and filled at the end of boot. Between
|
|
21537
|
+
// those two points sit the schema commit, the history assertion, three
|
|
21538
|
+
// materialization steps and an index build, and for that whole window an
|
|
21539
|
+
// empty separation relation would answer "not separated" to every
|
|
21540
|
+
// concurrent `assertSame`. See `ensureIdentitySchemaStorage`.
|
|
21541
|
+
//
|
|
21542
|
+
// Withheld when a schema commit is gated on an identity SEMANTICS
|
|
21543
|
+
// change: that commit's own preflight creates AND rebuilds inside the
|
|
21544
|
+
// commit transaction, under the semantics being committed. Filling here
|
|
21545
|
+
// would instead derive classes from semantics this database has not
|
|
21546
|
+
// accepted yet — and would keep them if the commit were then refused.
|
|
21547
|
+
// Nothing is provisioned on that path either, so a refused commit leaves
|
|
21548
|
+
// the relation absent rather than readable-empty, and the next open of a
|
|
21549
|
+
// graph whose semantics ARE committed heals it here.
|
|
21550
|
+
...identityGate === void 0 ? {
|
|
21551
|
+
recomputeDerivedRelations: (target) => rebuildIdentityClosureForContext({
|
|
21552
|
+
backend: target,
|
|
21553
|
+
graphId: merged.id,
|
|
21554
|
+
registry: identityRegistry,
|
|
21555
|
+
schema: resolvedIdentitySchema,
|
|
21556
|
+
sameIdAcrossKinds: identityProfile.sameIdAcrossKinds
|
|
21557
|
+
})
|
|
21558
|
+
} : {}
|
|
21559
|
+
});
|
|
21560
|
+
}
|
|
21561
|
+
const ensureOptions = {
|
|
21562
|
+
// The fallback is not useless: spreading `options` while it is possibly
|
|
21563
|
+
// undefined makes TypeScript distribute the StoreOptions union through
|
|
21564
|
+
// the literal and reject the call below.
|
|
21565
|
+
// eslint-disable-next-line unicorn/no-useless-fallback-in-spread
|
|
21566
|
+
...options ?? {},
|
|
19494
21567
|
preloaded: { activeRow, storedSchema }
|
|
19495
|
-
}
|
|
19496
|
-
await
|
|
21568
|
+
};
|
|
21569
|
+
const result = await ensureSchemaWithIdentityPrecedence(
|
|
21570
|
+
backend,
|
|
21571
|
+
merged,
|
|
21572
|
+
ensureOptions,
|
|
21573
|
+
identityGate
|
|
21574
|
+
);
|
|
21575
|
+
if (identityGate !== void 0 && result.status === "pending") {
|
|
21576
|
+
throw identityMigrationRequiredError(merged.id, identityGate, {
|
|
21577
|
+
blockedBy: "auto-migrate-disabled",
|
|
21578
|
+
pendingVersion: result.version
|
|
21579
|
+
});
|
|
21580
|
+
}
|
|
21581
|
+
await assertHistorySchemaOnOpen(backend, merged, options);
|
|
19497
21582
|
await materializeRuntimeContributions(backend, merged.id);
|
|
19498
21583
|
await materializeVectorContributions(backend, merged);
|
|
19499
21584
|
if (options?.systemIndexes !== "skip") {
|
|
@@ -19545,15 +21630,14 @@ async function materializeSystemIndexesOnBoot(backend, graphId, result) {
|
|
|
19545
21630
|
}
|
|
19546
21631
|
async function createVerifiedStore(graph, backend, options) {
|
|
19547
21632
|
const prepared = await prepareVerifiedStore(graph, backend, options);
|
|
19548
|
-
|
|
19549
|
-
|
|
19550
|
-
|
|
19551
|
-
|
|
19552
|
-
|
|
19553
|
-
|
|
19554
|
-
|
|
19555
|
-
|
|
19556
|
-
];
|
|
21633
|
+
const store = new StoreImplementation(
|
|
21634
|
+
prepared.graph,
|
|
21635
|
+
backend,
|
|
21636
|
+
options,
|
|
21637
|
+
prepared.schemaMetadata
|
|
21638
|
+
);
|
|
21639
|
+
await store.validateIdentity();
|
|
21640
|
+
return [store, prepared.result];
|
|
19557
21641
|
}
|
|
19558
21642
|
async function prepareVerifiedStore(graph, backend, options) {
|
|
19559
21643
|
const {
|
|
@@ -19561,7 +21645,7 @@ async function prepareVerifiedStore(graph, backend, options) {
|
|
|
19561
21645
|
activeRow,
|
|
19562
21646
|
result
|
|
19563
21647
|
} = await loadAndVerifyGraph(backend, graph);
|
|
19564
|
-
await assertHistorySchemaOnOpen(backend, options);
|
|
21648
|
+
await assertHistorySchemaOnOpen(backend, merged, options);
|
|
19565
21649
|
return {
|
|
19566
21650
|
graph: merged,
|
|
19567
21651
|
result,
|
|
@@ -19570,19 +21654,16 @@ async function prepareVerifiedStore(graph, backend, options) {
|
|
|
19570
21654
|
}
|
|
19571
21655
|
async function createVerifiedAdapterStore(graph, backend, options) {
|
|
19572
21656
|
const prepared = await prepareVerifiedStore(graph, backend, options);
|
|
19573
|
-
|
|
19574
|
-
|
|
19575
|
-
|
|
19576
|
-
|
|
19577
|
-
|
|
19578
|
-
|
|
19579
|
-
|
|
19580
|
-
|
|
19581
|
-
),
|
|
19582
|
-
prepared.result
|
|
19583
|
-
];
|
|
21657
|
+
const store = new AdapterStoreImplementation(
|
|
21658
|
+
prepared.graph,
|
|
21659
|
+
backend,
|
|
21660
|
+
options,
|
|
21661
|
+
prepared.schemaMetadata
|
|
21662
|
+
);
|
|
21663
|
+
await store.validateIdentity();
|
|
21664
|
+
return [asAdapterStoreSurface(store), prepared.result];
|
|
19584
21665
|
}
|
|
19585
21666
|
|
|
19586
21667
|
export { ExecutableAggregateQuery, ExecutableQuery, MAX_EXPLICIT_RECURSIVE_DEPTH, MAX_RECURSIVE_DEPTH, PreparedQuery, QueryBuilder, RecordedStoreView, StoreSearch, StoreView, UnionableQuery, avg, composeFragments, count, countDistinct, countDistinctEdges, countEdges, createAdapterStore, createAdapterStoreWithSchema, createFragment, createQueryBuilder, createStore, createStoreWithSchema, createVerifiedAdapterStore, createVerifiedStore, defineSubgraphProject, field, having, havingEq, havingGt, havingGte, havingLt, havingLte, limitFragment, max, min, offsetFragment, orderByFragment, sum };
|
|
19587
|
-
//# sourceMappingURL=chunk-
|
|
19588
|
-
//# sourceMappingURL=chunk-
|
|
21668
|
+
//# sourceMappingURL=chunk-7Z5OR7KO.js.map
|
|
21669
|
+
//# sourceMappingURL=chunk-7Z5OR7KO.js.map
|