@nicia-ai/typegraph 0.45.0 → 0.46.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 -42
- 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 -38
- 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 -43
- 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 -39
- 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-S3ZRKJHY.js → chunk-2LG2XB2M.js} +3 -3
- package/dist/{chunk-S3ZRKJHY.js.map → chunk-2LG2XB2M.js.map} +1 -1
- package/dist/{chunk-7PF2L3H6.cjs → chunk-2NA5DYLO.cjs} +4 -4
- package/dist/{chunk-7PF2L3H6.cjs.map → chunk-2NA5DYLO.cjs.map} +1 -1
- package/dist/chunk-2Z2Z3ZD6.js +58 -0
- package/dist/chunk-2Z2Z3ZD6.js.map +1 -0
- package/dist/{chunk-VVQM4CZD.cjs → chunk-3DHZCOT7.cjs} +20 -20
- package/dist/{chunk-VVQM4CZD.cjs.map → chunk-3DHZCOT7.cjs.map} +1 -1
- package/dist/{chunk-JXUTCSXX.cjs → chunk-4UGOKMI5.cjs} +250 -158
- package/dist/chunk-4UGOKMI5.cjs.map +1 -0
- package/dist/{chunk-RFRM3Q5K.js → chunk-5262ZMTR.js} +14 -5
- package/dist/chunk-5262ZMTR.js.map +1 -0
- package/dist/{chunk-QRNYDV5Z.js → chunk-5763DULU.js} +214 -69
- package/dist/chunk-5763DULU.js.map +1 -0
- package/dist/{chunk-BGYJW37L.js → chunk-5UVRGXD3.js} +277 -7
- package/dist/chunk-5UVRGXD3.js.map +1 -0
- package/dist/{chunk-QR24DJDF.cjs → chunk-62ZP23AW.cjs} +15 -15
- package/dist/{chunk-QR24DJDF.cjs.map → chunk-62ZP23AW.cjs.map} +1 -1
- package/dist/{chunk-ZI6VQSZA.cjs → chunk-65E3XWGF.cjs} +724 -577
- package/dist/chunk-65E3XWGF.cjs.map +1 -0
- package/dist/{chunk-S4KQ5LV4.js → chunk-775A4ZMN.js} +40 -6
- package/dist/chunk-775A4ZMN.js.map +1 -0
- package/dist/{chunk-J2ICGNHO.cjs → chunk-7BQXPCGN.cjs} +830 -169
- 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-DXEEHSGN.js → chunk-7VQXLT5O.js} +436 -43
- package/dist/chunk-7VQXLT5O.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-55ITBQJI.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-WUFASQUO.js → chunk-ASTI57H3.js} +3 -3
- package/dist/{chunk-WUFASQUO.js.map → chunk-ASTI57H3.js.map} +1 -1
- package/dist/{chunk-4QIKLIGG.js → chunk-BKCUOV7A.js} +15 -5
- package/dist/chunk-BKCUOV7A.js.map +1 -0
- package/dist/{chunk-JQXSZHF2.cjs → chunk-BSFJMMV3.cjs} +134 -7
- package/dist/chunk-BSFJMMV3.cjs.map +1 -0
- package/dist/{chunk-4GUTQPVO.js → chunk-C23WSN5E.js} +185 -24
- package/dist/chunk-C23WSN5E.js.map +1 -0
- package/dist/{chunk-QLUXRWKL.js → chunk-CBTIUB76.js} +3 -3
- package/dist/{chunk-QLUXRWKL.js.map → chunk-CBTIUB76.js.map} +1 -1
- package/dist/chunk-CMWHLBVN.cjs +2082 -0
- package/dist/chunk-CMWHLBVN.cjs.map +1 -0
- package/dist/{chunk-QB6VMOJ2.cjs → chunk-CXBHOOIQ.cjs} +45 -10
- package/dist/chunk-CXBHOOIQ.cjs.map +1 -0
- package/dist/{chunk-FL5UQRES.js → chunk-DCQVHAYQ.js} +5 -5
- package/dist/{chunk-FL5UQRES.js.map → chunk-DCQVHAYQ.js.map} +1 -1
- package/dist/chunk-DTVWX77L.cjs +3432 -0
- package/dist/chunk-DTVWX77L.cjs.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-GF3KXUTW.js +3393 -0
- package/dist/chunk-GF3KXUTW.js.map +1 -0
- package/dist/{chunk-VTN3C3CH.js → chunk-HFHTMECK.js} +7 -7
- package/dist/{chunk-VTN3C3CH.js.map → chunk-HFHTMECK.js.map} +1 -1
- package/dist/{chunk-FLEQ2HBK.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-TKP5T34P.js → chunk-JXA2ZP2Q.js} +6 -16
- 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-42PYUGLS.js → chunk-K5ELX2GT.js} +108 -8
- package/dist/chunk-K5ELX2GT.js.map +1 -0
- package/dist/{chunk-Q6EDCUXV.js → chunk-L65OU2U3.js} +556 -204
- package/dist/chunk-L65OU2U3.js.map +1 -0
- package/dist/{chunk-D54WH24V.cjs → chunk-LBJKZXDE.cjs} +115 -13
- package/dist/chunk-LBJKZXDE.cjs.map +1 -0
- package/dist/{chunk-FQ7ZPOTZ.js → chunk-LGHCOKTT.js} +637 -493
- package/dist/chunk-LGHCOKTT.js.map +1 -0
- package/dist/chunk-M3WP7RQZ.js +736 -0
- package/dist/chunk-M3WP7RQZ.js.map +1 -0
- package/dist/{chunk-4MEWSUVM.cjs → chunk-M4BDU5RX.cjs} +12 -12
- package/dist/chunk-M4BDU5RX.cjs.map +1 -0
- package/dist/chunk-MCLYU6BN.cjs +332 -0
- package/dist/chunk-MCLYU6BN.cjs.map +1 -0
- package/dist/{chunk-MIJKEVV7.js → chunk-MSY7FSX4.js} +37 -4
- package/dist/chunk-MSY7FSX4.js.map +1 -0
- package/dist/{chunk-H7VE3FL4.cjs → chunk-MUK4BU7K.cjs} +519 -125
- package/dist/chunk-MUK4BU7K.cjs.map +1 -0
- package/dist/{chunk-D56XG4EI.cjs → chunk-MXJDTH5B.cjs} +56 -45
- package/dist/chunk-MXJDTH5B.cjs.map +1 -0
- package/dist/{chunk-5EKPX6QU.js → chunk-MZLPX2R7.js} +8 -9
- package/dist/chunk-MZLPX2R7.js.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-BNS75YNF.cjs → chunk-NK5Q7UNE.cjs} +10 -20
- package/dist/chunk-NK5Q7UNE.cjs.map +1 -0
- package/dist/{chunk-TKORQCG4.js → chunk-NLI2AVEF.js} +386 -862
- package/dist/chunk-NLI2AVEF.js.map +1 -0
- package/dist/{chunk-YMTXMPV6.js → chunk-NMWDXFPH.js} +4 -4
- package/dist/{chunk-YMTXMPV6.js.map → chunk-NMWDXFPH.js.map} +1 -1
- package/dist/{chunk-J7LXTUTP.cjs → chunk-PF6F5NLJ.cjs} +604 -1094
- package/dist/chunk-PF6F5NLJ.cjs.map +1 -0
- package/dist/{chunk-NAAMUYMS.cjs → chunk-PNN7RHVR.cjs} +58 -48
- package/dist/chunk-PNN7RHVR.cjs.map +1 -0
- package/dist/{chunk-YOALGKRE.cjs → chunk-PNTCATDE.cjs} +3528 -1763
- package/dist/chunk-PNTCATDE.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-UCQ3BFSY.cjs → chunk-PT7RIRIZ.cjs} +684 -332
- package/dist/chunk-PT7RIRIZ.cjs.map +1 -0
- package/dist/{chunk-ACSVZPK7.cjs → chunk-PWLJ77UV.cjs} +5 -3
- package/dist/chunk-PWLJ77UV.cjs.map +1 -0
- package/dist/chunk-Q3G6A4I3.cjs +755 -0
- package/dist/chunk-Q3G6A4I3.cjs.map +1 -0
- package/dist/{chunk-Z3WO6MND.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-ID4WXTKL.js → chunk-S6PW7VV5.js} +2382 -617
- package/dist/chunk-S6PW7VV5.js.map +1 -0
- package/dist/{chunk-4GUBTVPV.js → chunk-T4XNKYXB.js} +780 -126
- package/dist/chunk-T4XNKYXB.js.map +1 -0
- package/dist/{chunk-YDWLUT5V.cjs → chunk-TQR4KBY2.cjs} +323 -178
- package/dist/chunk-TQR4KBY2.cjs.map +1 -0
- package/dist/{chunk-OOA273IJ.cjs → chunk-U5SON7R2.cjs} +27 -27
- package/dist/{chunk-OOA273IJ.cjs.map → chunk-U5SON7R2.cjs.map} +1 -1
- package/dist/{chunk-IX64T5OL.cjs → chunk-U63GDNRC.cjs} +32 -26
- package/dist/chunk-U63GDNRC.cjs.map +1 -0
- package/dist/{chunk-WDBXJOU5.cjs → chunk-UDISNXDL.cjs} +56 -47
- package/dist/chunk-UDISNXDL.cjs.map +1 -0
- package/dist/{chunk-EFEOOCJ3.js → chunk-UV3SN74I.js} +5 -5
- package/dist/{chunk-EFEOOCJ3.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-ISLPNELU.js → chunk-VGTJPTIC.js} +16 -5
- package/dist/chunk-VGTJPTIC.js.map +1 -0
- package/dist/chunk-VLT6PEWR.cjs +60 -0
- package/dist/chunk-VLT6PEWR.cjs.map +1 -0
- package/dist/chunk-VQH4VAWD.cjs +12 -0
- package/dist/{chunk-B3F2D3SP.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-WQZLUGVR.js +2057 -0
- package/dist/chunk-WQZLUGVR.js.map +1 -0
- package/dist/{chunk-EKFUG4G7.cjs → chunk-XBYVV3MP.cjs} +60 -5
- package/dist/chunk-XBYVV3MP.cjs.map +1 -0
- package/dist/{chunk-5GAONX5G.cjs → chunk-XCAGZAKP.cjs} +7 -7
- package/dist/{chunk-5GAONX5G.cjs.map → chunk-XCAGZAKP.cjs.map} +1 -1
- package/dist/chunk-XL7SLCXS.js +309 -0
- package/dist/chunk-XL7SLCXS.js.map +1 -0
- package/dist/{chunk-SVJ5GEXW.cjs → chunk-XUO3KQFO.cjs} +2 -2
- package/dist/chunk-XUO3KQFO.cjs.map +1 -0
- package/dist/{chunk-F4NHQHEZ.cjs → chunk-YAAM3M2S.cjs} +10 -10
- package/dist/{chunk-F4NHQHEZ.cjs.map → chunk-YAAM3M2S.cjs.map} +1 -1
- package/dist/{chunk-RUQS6M7G.js → chunk-YBFHZUXX.js} +6 -6
- package/dist/chunk-YBFHZUXX.js.map +1 -0
- package/dist/{chunk-KMXWZLQX.cjs → chunk-YBQOHKHN.cjs} +99 -100
- package/dist/chunk-YBQOHKHN.cjs.map +1 -0
- package/dist/{chunk-PVUI6KGR.js → chunk-YDFDBE6D.js} +93 -6
- package/dist/chunk-YDFDBE6D.js.map +1 -0
- package/dist/{chunk-P7GTYOLB.cjs → chunk-YVWGEQJE.cjs} +297 -135
- 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 -40
- package/dist/core/index.d.cts +4 -5
- package/dist/core/index.d.ts +4 -5
- package/dist/core/index.js +8 -8
- package/dist/{ddl-BG4J805L.d.cts → ddl-BJcOp4WZ.d.cts} +3 -3
- package/dist/{ddl-CgiS9QLj.d.ts → ddl-BXfGHbPN.d.ts} +3 -3
- package/dist/graph-extension/index.cjs +26 -22
- 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 -7
- package/dist/graph-merge/index.cjs +2459 -654
- 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 -467
- package/dist/graph-merge/index.js.map +1 -1
- package/dist/{index-C3y_98iN.d.cts → index-BSHvKfok.d.ts} +221 -10
- package/dist/{index-BBxZCUdu.d.ts → index-DxaYCv8Y.d.cts} +221 -10
- package/dist/index.cjs +330 -268
- 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 -49
- 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 -51
- 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 -25
- package/dist/interchange/index.js.map +1 -1
- package/dist/{manager-YoLEfcc1.d.ts → manager-CfZAimti.d.ts} +36 -5
- package/dist/{manager-KS7CGQQT.d.cts → manager-HGk6E_HW.d.cts} +36 -5
- package/dist/{materialize-indexes-Bhhyb1B3.d.ts → materialize-indexes-6A7dWGtb.d.cts} +10 -2
- package/dist/{materialize-indexes-OHRWypW6.d.cts → materialize-indexes-BMJP6Tms.d.ts} +10 -2
- package/dist/{postgres-DpkW3R5Y.d.cts → postgres-DFYWENdR.d.cts} +1961 -439
- package/dist/{postgres-DwqyIniy.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 -48
- package/dist/schema/index.d.cts +9 -16
- package/dist/schema/index.d.ts +9 -16
- package/dist/schema/index.js +23 -21
- package/dist/{searchable-DLWEfC4a.d.ts → searchable-9OQoPuRs.d.ts} +1 -2
- package/dist/{searchable-CIZL8Hyw.d.cts → searchable-CIje5XYD.d.cts} +1 -2
- package/dist/{sqlite-DY0WODUK.d.cts → sqlite-DerU5tc7.d.ts} +2029 -347
- package/dist/{sqlite-L-VIRzqA.d.ts → sqlite-DitnISa9.d.cts} +2029 -347
- package/dist/{store-view-A85FFRnk.d.cts → store-B0h6x7wo.d.cts} +1289 -767
- package/dist/{store-view-66p92IPi.d.ts → store-ClQ-MZQS.d.ts} +1289 -767
- package/dist/{temporal-BNbjXqer.d.cts → temporal-Bn-Fi8FJ.d.cts} +1 -1
- package/dist/{temporal-Bob6Nfbp.d.ts → temporal-BtM4UALv.d.ts} +1 -1
- package/dist/{types-DeORZhxn.d.cts → types-CoIx8HX5.d.cts} +1 -1
- package/dist/{types-Ch9a0Dx6.d.ts → types-HkveCCx0.d.cts} +726 -5
- package/dist/{types-Ch9a0Dx6.d.cts → types-HkveCCx0.d.ts} +726 -5
- package/dist/{types-ClCzT-dT.d.ts → types-KFfk-8Ah.d.ts} +1 -1
- package/dist/{where-DPw52ksb.d.cts → where--fk7_0kv.d.cts} +1 -1
- package/dist/{where-kWUZPvEA.d.ts → where-ws5hf-Ov.d.ts} +1 -1
- package/package.json +3 -3
- package/dist/chunk-2L54C2C2.js +0 -144
- package/dist/chunk-2L54C2C2.js.map +0 -1
- package/dist/chunk-35S6P5ZU.js +0 -27
- package/dist/chunk-35S6P5ZU.js.map +0 -1
- package/dist/chunk-42PYUGLS.js.map +0 -1
- package/dist/chunk-4GUBTVPV.js.map +0 -1
- package/dist/chunk-4GUTQPVO.js.map +0 -1
- package/dist/chunk-4MB3AQAQ.js.map +0 -1
- package/dist/chunk-4MEWSUVM.cjs.map +0 -1
- package/dist/chunk-4QIKLIGG.js.map +0 -1
- package/dist/chunk-4SZ6XPTG.cjs +0 -12
- package/dist/chunk-55ITBQJI.js.map +0 -1
- package/dist/chunk-5EKPX6QU.js.map +0 -1
- package/dist/chunk-6RYPD3NJ.cjs +0 -1282
- package/dist/chunk-6RYPD3NJ.cjs.map +0 -1
- package/dist/chunk-764JHTLT.cjs +0 -15
- package/dist/chunk-764JHTLT.cjs.map +0 -1
- package/dist/chunk-ACSVZPK7.cjs.map +0 -1
- package/dist/chunk-B3F2D3SP.cjs +0 -12
- package/dist/chunk-BGYJW37L.js.map +0 -1
- package/dist/chunk-BNS75YNF.cjs.map +0 -1
- package/dist/chunk-C4F7VJ5D.cjs.map +0 -1
- package/dist/chunk-CBKG6HEP.js +0 -1263
- package/dist/chunk-CBKG6HEP.js.map +0 -1
- package/dist/chunk-CPEL3YIR.cjs.map +0 -1
- package/dist/chunk-D54WH24V.cjs.map +0 -1
- package/dist/chunk-D56XG4EI.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-DXEEHSGN.js.map +0 -1
- package/dist/chunk-EKFUG4G7.cjs.map +0 -1
- 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-FLEQ2HBK.js.map +0 -1
- package/dist/chunk-FQ7ZPOTZ.js.map +0 -1
- package/dist/chunk-G6Q5UM7U.js.map +0 -1
- package/dist/chunk-GYA4JRP5.js.map +0 -1
- package/dist/chunk-H7VE3FL4.cjs.map +0 -1
- package/dist/chunk-ID4WXTKL.js.map +0 -1
- package/dist/chunk-ISLPNELU.js.map +0 -1
- package/dist/chunk-ITE4IXAK.js +0 -25
- package/dist/chunk-ITE4IXAK.js.map +0 -1
- package/dist/chunk-IX64T5OL.cjs.map +0 -1
- package/dist/chunk-J2ICGNHO.cjs.map +0 -1
- package/dist/chunk-J7LXTUTP.cjs.map +0 -1
- package/dist/chunk-JQXSZHF2.cjs.map +0 -1
- package/dist/chunk-JXUTCSXX.cjs.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-KMXWZLQX.cjs.map +0 -1
- package/dist/chunk-MIJKEVV7.js.map +0 -1
- package/dist/chunk-NAAMUYMS.cjs.map +0 -1
- package/dist/chunk-P7GTYOLB.cjs.map +0 -1
- package/dist/chunk-PVUI6KGR.js.map +0 -1
- package/dist/chunk-Q6EDCUXV.js.map +0 -1
- package/dist/chunk-QB6VMOJ2.cjs.map +0 -1
- package/dist/chunk-QC4MCOFV.cjs +0 -83
- package/dist/chunk-QC4MCOFV.cjs.map +0 -1
- package/dist/chunk-QMQLUA66.js +0 -12
- package/dist/chunk-QMQLUA66.js.map +0 -1
- package/dist/chunk-QRNYDV5Z.js.map +0 -1
- package/dist/chunk-RFRM3Q5K.js.map +0 -1
- package/dist/chunk-RUQS6M7G.js.map +0 -1
- package/dist/chunk-S4KQ5LV4.js.map +0 -1
- package/dist/chunk-SJDMVITW.cjs.map +0 -1
- package/dist/chunk-SNXVDZAJ.js.map +0 -1
- package/dist/chunk-SVJ5GEXW.cjs.map +0 -1
- package/dist/chunk-TDXIBCGY.cjs.map +0 -1
- package/dist/chunk-TKORQCG4.js.map +0 -1
- package/dist/chunk-TKP5T34P.js.map +0 -1
- package/dist/chunk-TRF4EBJT.cjs +0 -30
- package/dist/chunk-TRF4EBJT.cjs.map +0 -1
- package/dist/chunk-UCQ3BFSY.cjs.map +0 -1
- package/dist/chunk-UR7W5YQK.js.map +0 -1
- package/dist/chunk-V34A6R5V.js +0 -134
- package/dist/chunk-V34A6R5V.js.map +0 -1
- package/dist/chunk-WDAVPX46.js +0 -81
- package/dist/chunk-WDAVPX46.js.map +0 -1
- package/dist/chunk-WDBXJOU5.cjs.map +0 -1
- package/dist/chunk-YDWLUT5V.cjs.map +0 -1
- package/dist/chunk-YOALGKRE.cjs.map +0 -1
- package/dist/chunk-Z3WO6MND.cjs.map +0 -1
- package/dist/chunk-ZDH3DX3C.cjs +0 -30
- package/dist/chunk-ZDH3DX3C.cjs.map +0 -1
- package/dist/chunk-ZI6VQSZA.cjs.map +0 -1
- package/dist/chunk-ZTAJL4DT.cjs +0 -85
- package/dist/chunk-ZTAJL4DT.cjs.map +0 -1
- package/dist/define-graph-BGrJahvp.d.ts +0 -173
- package/dist/define-graph-alREY2wj.d.cts +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, executeNodeUpdateWhere, 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-FLEQ2HBK.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-L65OU2U3.js';
|
|
3
|
+
import { storeRuntime, STORE_RUNTIME, TRANSACTION_RUNTIME, AUTO_REFRESH_STATISTICS_ROW_THRESHOLD, getEmbeddingFields, computeFulltextContent } from './chunk-M3WP7RQZ.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-LGHCOKTT.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-GF3KXUTW.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-NLI2AVEF.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, preservesImmutableLowerBound, isCanonicalIsoDate, statedBoundMatchesStored } from './chunk-XL7SLCXS.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 = [
|
|
@@ -411,6 +411,10 @@ var CoordinatePinnedView = class {
|
|
|
411
411
|
constructor(store, coordinate) {
|
|
412
412
|
this.store = store;
|
|
413
413
|
this.coordinate = coordinate;
|
|
414
|
+
Object.defineProperty(this, "identity", {
|
|
415
|
+
enumerable: false,
|
|
416
|
+
get: () => storeRuntime(this.store).identityAtCoordinate(this.coordinate)
|
|
417
|
+
});
|
|
414
418
|
}
|
|
415
419
|
/** The temporal mode this view reads in. */
|
|
416
420
|
get mode() {
|
|
@@ -532,7 +536,7 @@ var CoordinatePinnedView = class {
|
|
|
532
536
|
});
|
|
533
537
|
}
|
|
534
538
|
};
|
|
535
|
-
var
|
|
539
|
+
var StoreViewImplementation = class extends CoordinatePinnedView {
|
|
536
540
|
#nodes;
|
|
537
541
|
#edges;
|
|
538
542
|
#search;
|
|
@@ -591,7 +595,8 @@ var StoreView = class extends CoordinatePinnedView {
|
|
|
591
595
|
return this.#search;
|
|
592
596
|
}
|
|
593
597
|
};
|
|
594
|
-
var
|
|
598
|
+
var StoreView = StoreViewImplementation;
|
|
599
|
+
var RecordedStoreViewImplementation = class extends CoordinatePinnedView {
|
|
595
600
|
#nodes;
|
|
596
601
|
#edges;
|
|
597
602
|
constructor(store, coordinate) {
|
|
@@ -650,6 +655,48 @@ var RecordedStoreView = class extends CoordinatePinnedView {
|
|
|
650
655
|
return this.#edges;
|
|
651
656
|
}
|
|
652
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
|
+
}
|
|
653
700
|
|
|
654
701
|
// src/query/compiler/emitter/plan-inspector.ts
|
|
655
702
|
function collectPlanOperations(node, ops) {
|
|
@@ -865,9 +912,13 @@ function assertRecursiveEmitterClauseAlignment(logicalPlan, input) {
|
|
|
865
912
|
}
|
|
866
913
|
function emitRecursiveQuerySql(input) {
|
|
867
914
|
assertRecursiveEmitterClauseAlignment(input.logicalPlan, input);
|
|
915
|
+
const cteList = sql.join(
|
|
916
|
+
[...input.precedingCtes ?? [], input.recursiveCte],
|
|
917
|
+
sql`, `
|
|
918
|
+
);
|
|
868
919
|
const parts = [
|
|
869
920
|
sql`WITH RECURSIVE`,
|
|
870
|
-
|
|
921
|
+
cteList,
|
|
871
922
|
sql`SELECT ${input.projection}`,
|
|
872
923
|
sql`FROM recursive_cte`,
|
|
873
924
|
input.depthFilter
|
|
@@ -1023,6 +1074,118 @@ function mergeEdgeKinds(traversal) {
|
|
|
1023
1074
|
];
|
|
1024
1075
|
}
|
|
1025
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
|
+
|
|
1026
1189
|
// src/query/compiler/typed-json-extract.ts
|
|
1027
1190
|
function compileTypedJsonExtract(input) {
|
|
1028
1191
|
const { column, dialect, pointer, valueType } = input;
|
|
@@ -1337,24 +1500,71 @@ function buildStandardTraversalCte(input) {
|
|
|
1337
1500
|
})
|
|
1338
1501
|
];
|
|
1339
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
|
+
}
|
|
1340
1528
|
function compileTraversalBranch(branch) {
|
|
1341
1529
|
const whereClauses = [
|
|
1342
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 ?? [],
|
|
1343
1535
|
compileKindFilter(sql.raw("e.kind"), branch.edgeKinds),
|
|
1344
|
-
compileKindFilter(
|
|
1345
|
-
sql.raw(`e.${branch.joinKindField}`),
|
|
1346
|
-
previousNodeKinds
|
|
1347
|
-
),
|
|
1348
1536
|
compileKindFilter(sql.raw(`e.${branch.targetKindField}`), nodeKinds)
|
|
1349
1537
|
];
|
|
1538
|
+
if (traversal.includeIdentityMembers !== true) {
|
|
1539
|
+
whereClauses.push(
|
|
1540
|
+
compileKindFilter(
|
|
1541
|
+
sql.raw(`e.${branch.joinKindField}`),
|
|
1542
|
+
previousNodeKinds
|
|
1543
|
+
)
|
|
1544
|
+
);
|
|
1545
|
+
}
|
|
1350
1546
|
if (branch.duplicateGuard !== void 0) {
|
|
1351
1547
|
whereClauses.push(branch.duplicateGuard);
|
|
1352
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
|
+
}
|
|
1353
1563
|
return sql`
|
|
1354
1564
|
SELECT ${sql.join(selectColumns, sql`, `)}
|
|
1355
1565
|
FROM cte_${sql.raw(previousAlias)}
|
|
1356
|
-
|
|
1357
|
-
|
|
1566
|
+
${frontierJoin}
|
|
1567
|
+
JOIN ${ctx.schema.edgesTable} e ON ${sourceJoin}
|
|
1358
1568
|
JOIN ${ctx.schema.nodesTable} n ON n.graph_id = e.graph_id
|
|
1359
1569
|
AND n.id = e.${sql.raw(branch.targetField)}
|
|
1360
1570
|
AND n.kind = e.${sql.raw(branch.targetKindField)}
|
|
@@ -1393,13 +1603,11 @@ function buildStandardTraversalCte(input) {
|
|
|
1393
1603
|
const inverseTargetField = traversal.direction === "out" ? "from_id" : "to_id";
|
|
1394
1604
|
const inverseJoinKindField = traversal.direction === "out" ? "to_kind" : "from_kind";
|
|
1395
1605
|
const inverseTargetKindField = traversal.direction === "out" ? "from_kind" : "to_kind";
|
|
1396
|
-
const
|
|
1397
|
-
|
|
1606
|
+
const duplicateGuard = compileInverseTraversalDuplicateGuard(
|
|
1607
|
+
directEdgeKinds,
|
|
1608
|
+
inverseEdgeKinds,
|
|
1609
|
+
(overlappingKinds) => compileKindFilter(sql.raw("e.kind"), overlappingKinds)
|
|
1398
1610
|
);
|
|
1399
|
-
const duplicateGuard = overlappingKinds.length > 0 ? sql`NOT (e.from_id = e.to_id AND ${compileKindFilter(
|
|
1400
|
-
sql.raw("e.kind"),
|
|
1401
|
-
overlappingKinds
|
|
1402
|
-
)})` : void 0;
|
|
1403
1611
|
const inverseBranch = compileTraversalBranch({
|
|
1404
1612
|
duplicateGuard,
|
|
1405
1613
|
edgeKinds: inverseEdgeKinds,
|
|
@@ -1803,6 +2011,16 @@ function buildStandardEmbeddingsCte(input) {
|
|
|
1803
2011
|
const tableName = vectorStrategy.tableName(graphId, kind, fieldPath);
|
|
1804
2012
|
const slotDescriptor = vectorSlots?.get(vectorSlotKey(kind, fieldPath));
|
|
1805
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
|
+
}
|
|
1806
2024
|
const engineFormEligible = vectorPredicate.approximate === true || vectorStrategy.searchIsExact === true;
|
|
1807
2025
|
const annSlot = engineFormEligible && branchMetric === slotDescriptor?.metric ? slotDescriptor : void 0;
|
|
1808
2026
|
if (annSlot !== void 0) {
|
|
@@ -2152,7 +2370,8 @@ function createTemporalFilterPass(ast, readInstant = "literal") {
|
|
|
2152
2370
|
...extractTemporalOptions(ast, tableAlias),
|
|
2153
2371
|
currentTimestamp
|
|
2154
2372
|
});
|
|
2155
|
-
}
|
|
2373
|
+
},
|
|
2374
|
+
currentInstant: currentTimestamp
|
|
2156
2375
|
};
|
|
2157
2376
|
}
|
|
2158
2377
|
|
|
@@ -2600,6 +2819,12 @@ function throwRecordedCurrentIndexError(recordedAsOf, usage) {
|
|
|
2600
2819
|
var MAX_RECURSIVE_DEPTH = 10;
|
|
2601
2820
|
var MAX_EXPLICIT_RECURSIVE_DEPTH = 1e3;
|
|
2602
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
|
+
}
|
|
2603
2828
|
function runRecursiveQueryPassPipeline(ast, graphId, ctx) {
|
|
2604
2829
|
let state = {
|
|
2605
2830
|
ast,
|
|
@@ -2720,6 +2945,12 @@ function compileVariableLengthQueryWithRecursiveCteStrategy(ast, graphId, ctx) {
|
|
|
2720
2945
|
requiredColumnsByAlias,
|
|
2721
2946
|
temporalFilterPass
|
|
2722
2947
|
);
|
|
2948
|
+
const identityClassCte = compileIdentityClassCte({
|
|
2949
|
+
ast,
|
|
2950
|
+
ctx,
|
|
2951
|
+
graphId,
|
|
2952
|
+
temporalFilterPass
|
|
2953
|
+
});
|
|
2723
2954
|
const projection = compileRecursiveProjection(ast, vlTraversal, dialect);
|
|
2724
2955
|
const minDepth = vlTraversal.variableLength.minDepth;
|
|
2725
2956
|
const depthFilter = minDepth > 0 ? sql`WHERE depth >= ${minDepth}` : sql.raw("");
|
|
@@ -2730,6 +2961,7 @@ function compileVariableLengthQueryWithRecursiveCteStrategy(ast, graphId, ctx) {
|
|
|
2730
2961
|
...limitOffset === void 0 ? {} : { limitOffset },
|
|
2731
2962
|
logicalPlan,
|
|
2732
2963
|
...orderBy === void 0 ? {} : { orderBy },
|
|
2964
|
+
...identityClassCte === void 0 ? {} : { precedingCtes: [identityClassCte] },
|
|
2733
2965
|
projection,
|
|
2734
2966
|
recursiveCte
|
|
2735
2967
|
});
|
|
@@ -2752,7 +2984,7 @@ function compileRecursiveCte(ast, traversal, graphId, ctx, requiredColumnsByAlia
|
|
|
2752
2984
|
const shouldEnforceCycleCheck = vl.cyclePolicy !== "allow";
|
|
2753
2985
|
const shouldTrackPath = shouldEnforceCycleCheck || vl.pathAlias !== void 0;
|
|
2754
2986
|
const recursiveJoinRequiredColumns = /* @__PURE__ */ new Set(["id"]);
|
|
2755
|
-
if (previousNodeKinds.length > 1) {
|
|
2987
|
+
if (previousNodeKinds.length > 1 || traversal.includeIdentityMembers === true) {
|
|
2756
2988
|
recursiveJoinRequiredColumns.add("kind");
|
|
2757
2989
|
}
|
|
2758
2990
|
const requiredStartColumns = requiredColumnsByAlias ? requiredColumnsByAlias.get(startAlias) ?? EMPTY_REQUIRED_COLUMNS : void 0;
|
|
@@ -2806,33 +3038,74 @@ function compileRecursiveCte(ast, traversal, graphId, ctx, requiredColumnsByAlia
|
|
|
2806
3038
|
}
|
|
2807
3039
|
const effectiveMaxDepth = vl.maxDepth > 0 ? vl.maxDepth : MAX_RECURSIVE_DEPTH;
|
|
2808
3040
|
const maxDepthCondition = sql`r.depth < ${effectiveMaxDepth}`;
|
|
2809
|
-
const
|
|
2810
|
-
|
|
2811
|
-
|
|
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;
|
|
2812
3052
|
const baseWhereClauses = [
|
|
2813
3053
|
sql`n0.graph_id = ${graphId}`,
|
|
2814
3054
|
startKindFilter,
|
|
2815
3055
|
startTemporalFilter,
|
|
2816
3056
|
...startPredicates
|
|
2817
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;
|
|
2818
3068
|
const recursiveBaseWhereClauses = [
|
|
2819
3069
|
sql`e.graph_id = ${graphId}`,
|
|
2820
3070
|
nodeKindFilter,
|
|
2821
3071
|
edgeTemporalFilter,
|
|
2822
3072
|
nodeTemporalFilter,
|
|
2823
|
-
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 ?? []
|
|
2824
3078
|
];
|
|
2825
3079
|
if (cycleCheck !== void 0) {
|
|
2826
3080
|
recursiveBaseWhereClauses.push(cycleCheck);
|
|
2827
3081
|
}
|
|
2828
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
|
+
}
|
|
2829
3098
|
function compileRecursiveBranch2(branch) {
|
|
2830
3099
|
const recursiveFilterClauses = [
|
|
2831
3100
|
...recursiveBaseWhereClauses,
|
|
2832
3101
|
compileKindFilter2(branch.edgeKinds, "e.kind"),
|
|
2833
|
-
compileKindFilter2(previousNodeKinds, `e.${branch.joinKindField}`),
|
|
2834
3102
|
compileKindFilter2(nodeKinds, `e.${branch.targetKindField}`)
|
|
2835
3103
|
];
|
|
3104
|
+
if (traversal.includeIdentityMembers !== true) {
|
|
3105
|
+
recursiveFilterClauses.push(
|
|
3106
|
+
compileKindFilter2(previousNodeKinds, `e.${branch.joinKindField}`)
|
|
3107
|
+
);
|
|
3108
|
+
}
|
|
2836
3109
|
if (branch.duplicateGuard !== void 0) {
|
|
2837
3110
|
recursiveFilterClauses.push(branch.duplicateGuard);
|
|
2838
3111
|
}
|
|
@@ -2844,14 +3117,8 @@ function compileRecursiveCte(ast, traversal, graphId, ctx, requiredColumnsByAlia
|
|
|
2844
3117
|
if (pathExtension !== void 0) {
|
|
2845
3118
|
recursiveSelectColumns.push(sql`${pathExtension} AS path`);
|
|
2846
3119
|
}
|
|
2847
|
-
const recursiveJoinClauses =
|
|
2848
|
-
|
|
2849
|
-
];
|
|
2850
|
-
if (previousNodeKinds.length > 1) {
|
|
2851
|
-
recursiveJoinClauses.push(
|
|
2852
|
-
sql`e.${sql.raw(branch.joinKindField)} = r.${sql.raw(nodeAlias)}_kind`
|
|
2853
|
-
);
|
|
2854
|
-
}
|
|
3120
|
+
const recursiveJoinClauses = compileWorktableJoinClauses(branch);
|
|
3121
|
+
const frontierJoin = identityFrontierExpansion?.frontierJoin ?? sql``;
|
|
2855
3122
|
if (forceWorktableOuterJoinOrder) {
|
|
2856
3123
|
const recursiveWhereClauses = [
|
|
2857
3124
|
...recursiveJoinClauses,
|
|
@@ -2860,6 +3127,7 @@ function compileRecursiveCte(ast, traversal, graphId, ctx, requiredColumnsByAlia
|
|
|
2860
3127
|
return sql`
|
|
2861
3128
|
SELECT ${sql.join(recursiveSelectColumns, sql`, `)}
|
|
2862
3129
|
FROM recursive_cte r
|
|
3130
|
+
${frontierJoin}
|
|
2863
3131
|
CROSS JOIN ${ctx.schema.edgesTable} e
|
|
2864
3132
|
JOIN ${ctx.schema.nodesTable} n ON n.graph_id = e.graph_id
|
|
2865
3133
|
AND n.id = e.${sql.raw(branch.targetField)}
|
|
@@ -2870,6 +3138,7 @@ function compileRecursiveCte(ast, traversal, graphId, ctx, requiredColumnsByAlia
|
|
|
2870
3138
|
return sql`
|
|
2871
3139
|
SELECT ${sql.join(recursiveSelectColumns, sql`, `)}
|
|
2872
3140
|
FROM recursive_cte r
|
|
3141
|
+
${frontierJoin}
|
|
2873
3142
|
JOIN ${ctx.schema.edgesTable} e ON ${sql.join(recursiveJoinClauses, sql` AND `)}
|
|
2874
3143
|
JOIN ${ctx.schema.nodesTable} n ON n.graph_id = e.graph_id
|
|
2875
3144
|
AND n.id = e.${sql.raw(branch.targetField)}
|
|
@@ -2893,10 +3162,11 @@ function compileRecursiveCte(ast, traversal, graphId, ctx, requiredColumnsByAlia
|
|
|
2893
3162
|
const inverseTargetField = direction === "out" ? "from_id" : "to_id";
|
|
2894
3163
|
const inverseJoinKindField = direction === "out" ? "to_kind" : "from_kind";
|
|
2895
3164
|
const inverseTargetKindField = direction === "out" ? "from_kind" : "to_kind";
|
|
2896
|
-
const
|
|
2897
|
-
|
|
3165
|
+
const duplicateGuard = compileInverseTraversalDuplicateGuard(
|
|
3166
|
+
directEdgeKinds,
|
|
3167
|
+
inverseEdgeKinds,
|
|
3168
|
+
(overlappingKinds) => compileKindFilter2(overlappingKinds, "e.kind")
|
|
2898
3169
|
);
|
|
2899
|
-
const duplicateGuard = overlappingKinds.length > 0 ? sql`NOT (e.from_id = e.to_id AND ${compileKindFilter2(overlappingKinds, "e.kind")})` : void 0;
|
|
2900
3170
|
const inverseBranch = compileRecursiveBranch2({
|
|
2901
3171
|
joinField: inverseJoinField,
|
|
2902
3172
|
targetField: inverseTargetField,
|
|
@@ -3419,7 +3689,7 @@ function buildProjectionPlan(kinds, projectionMap, resolveFieldType, entityPrefi
|
|
|
3419
3689
|
const projectedKinds = /* @__PURE__ */ new Map();
|
|
3420
3690
|
const fullKinds = [];
|
|
3421
3691
|
for (const kind of kinds) {
|
|
3422
|
-
const selection = projectionMap
|
|
3692
|
+
const selection = projectionMap !== void 0 && hasOwnKey(projectionMap, kind) ? projectionMap[kind] : void 0;
|
|
3423
3693
|
if (selection === void 0) {
|
|
3424
3694
|
fullKinds.push(kind);
|
|
3425
3695
|
continue;
|
|
@@ -4241,6 +4511,7 @@ function compileQuery(ast, graphId, options = "sqlite") {
|
|
|
4241
4511
|
schema,
|
|
4242
4512
|
annIndexTypes,
|
|
4243
4513
|
readInstant: options_.readInstant ?? "literal",
|
|
4514
|
+
identitySameIdAcrossKinds: options_.identitySameIdAcrossKinds ?? "fold",
|
|
4244
4515
|
compileQuery: (subAst, subGraphId) => compileQuery(
|
|
4245
4516
|
inheritRecordedAsOf(subAst, ast.recordedAsOf),
|
|
4246
4517
|
subGraphId,
|
|
@@ -4350,7 +4621,10 @@ function propagateOptions(options_) {
|
|
|
4350
4621
|
...options_.vectorSlots === void 0 ? {} : { vectorSlots: options_.vectorSlots },
|
|
4351
4622
|
...options_.fulltextLanguages === void 0 ? {} : { fulltextLanguages: options_.fulltextLanguages },
|
|
4352
4623
|
...options_.recordedReadBinding === void 0 ? {} : { recordedReadBinding: options_.recordedReadBinding },
|
|
4353
|
-
...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
|
+
}
|
|
4354
4628
|
};
|
|
4355
4629
|
}
|
|
4356
4630
|
function resolveCountAggregateFastPath(ast) {
|
|
@@ -4418,6 +4692,11 @@ function resolveCountAggregateFastPath(ast) {
|
|
|
4418
4692
|
};
|
|
4419
4693
|
}
|
|
4420
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
|
+
}
|
|
4421
4700
|
const plan = resolveCountAggregateFastPath(ast);
|
|
4422
4701
|
if (!plan) {
|
|
4423
4702
|
return void 0;
|
|
@@ -4609,7 +4888,14 @@ function buildStandardStartAndTraversalCtes(input) {
|
|
|
4609
4888
|
temporalFilterPass,
|
|
4610
4889
|
traversalLimit
|
|
4611
4890
|
} = input;
|
|
4891
|
+
const identityClassCte = compileIdentityClassCte({
|
|
4892
|
+
ast,
|
|
4893
|
+
ctx,
|
|
4894
|
+
graphId,
|
|
4895
|
+
temporalFilterPass
|
|
4896
|
+
});
|
|
4612
4897
|
const ctes = [
|
|
4898
|
+
...identityClassCte === void 0 ? [] : [identityClassCte],
|
|
4613
4899
|
buildStandardStartCte({
|
|
4614
4900
|
ast,
|
|
4615
4901
|
ctx,
|
|
@@ -4946,57 +5232,35 @@ function compileStandardQueryWithCteStrategy(ast, graphId, ctx) {
|
|
|
4946
5232
|
});
|
|
4947
5233
|
}
|
|
4948
5234
|
|
|
4949
|
-
// src/utils/path.ts
|
|
4950
|
-
function parseSqlitePath(path) {
|
|
4951
|
-
const emptyPath = `${SQLITE_PATH_DELIMITER}${SQLITE_PATH_DELIMITER}`;
|
|
4952
|
-
if (!path || path === emptyPath) return [];
|
|
4953
|
-
const trimmed = path.slice(1, -1);
|
|
4954
|
-
if (trimmed === "") return [];
|
|
4955
|
-
return trimmed.split(SQLITE_PATH_DELIMITER);
|
|
4956
|
-
}
|
|
4957
|
-
function isSqlitePath(value) {
|
|
4958
|
-
return typeof value === "string" && value.startsWith(SQLITE_PATH_DELIMITER) && value.endsWith(SQLITE_PATH_DELIMITER);
|
|
4959
|
-
}
|
|
4960
|
-
function normalizePath(value) {
|
|
4961
|
-
if (Array.isArray(value)) {
|
|
4962
|
-
return value;
|
|
4963
|
-
}
|
|
4964
|
-
if (isSqlitePath(value)) {
|
|
4965
|
-
return parseSqlitePath(value);
|
|
4966
|
-
}
|
|
4967
|
-
if (isPostgresTextArray(value)) {
|
|
4968
|
-
return parsePostgresTextArray(value);
|
|
4969
|
-
}
|
|
4970
|
-
return [];
|
|
4971
|
-
}
|
|
4972
|
-
function isPostgresTextArray(value) {
|
|
4973
|
-
return typeof value === "string" && value.startsWith(PG_ARRAY_START) && value.endsWith(PG_ARRAY_END);
|
|
4974
|
-
}
|
|
4975
|
-
function parsePostgresTextArray(value) {
|
|
4976
|
-
const inner = value.slice(1, -1);
|
|
4977
|
-
if (inner === "") return [];
|
|
4978
|
-
return inner.split(PG_PATH_ELEMENT_SEPARATOR);
|
|
4979
|
-
}
|
|
4980
|
-
|
|
4981
5235
|
// src/query/execution/result-mapper.ts
|
|
4982
|
-
function
|
|
4983
|
-
const
|
|
5236
|
+
function collectPathColumns(state) {
|
|
5237
|
+
const columns = [];
|
|
4984
5238
|
for (const traversal of state.traversals) {
|
|
4985
|
-
|
|
4986
|
-
|
|
5239
|
+
const pathAlias = traversal.variableLength?.pathAlias;
|
|
5240
|
+
if (pathAlias !== void 0) {
|
|
5241
|
+
columns.push({
|
|
5242
|
+
alias: pathAlias,
|
|
5243
|
+
identityExpanded: traversal.includeIdentityMembers === true
|
|
5244
|
+
});
|
|
4987
5245
|
}
|
|
4988
5246
|
}
|
|
4989
|
-
|
|
5247
|
+
return columns;
|
|
5248
|
+
}
|
|
5249
|
+
function transformPathColumns(rows, state, _dialect) {
|
|
5250
|
+
const pathColumns = collectPathColumns(state);
|
|
5251
|
+
if (pathColumns.length === 0) return rows;
|
|
4990
5252
|
const result = [];
|
|
4991
5253
|
let changed = false;
|
|
4992
5254
|
for (const row of rows) {
|
|
4993
5255
|
let transformed;
|
|
4994
|
-
for (const alias of
|
|
5256
|
+
for (const { alias, identityExpanded } of pathColumns) {
|
|
4995
5257
|
const value = row[alias];
|
|
4996
|
-
if (value
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
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;
|
|
5000
5264
|
}
|
|
5001
5265
|
if (transformed === void 0) {
|
|
5002
5266
|
result.push(row);
|
|
@@ -5008,16 +5272,24 @@ function transformPathColumns(rows, state, _dialect) {
|
|
|
5008
5272
|
return changed ? result : rows;
|
|
5009
5273
|
}
|
|
5010
5274
|
var RESERVED_NODE_KEYS = /* @__PURE__ */ new Set(["id", "kind", "meta"]);
|
|
5011
|
-
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
|
+
]);
|
|
5012
5282
|
function nullToUndefined2(value) {
|
|
5013
5283
|
return value === null ? void 0 : value;
|
|
5014
5284
|
}
|
|
5015
|
-
function
|
|
5285
|
+
function propsExcludingReserved(props, reservedKeys) {
|
|
5286
|
+
const kept = createDataKeyedBag();
|
|
5016
5287
|
for (const [key, value] of Object.entries(props)) {
|
|
5017
5288
|
if (!reservedKeys.has(key)) {
|
|
5018
|
-
|
|
5289
|
+
kept[key] = value;
|
|
5019
5290
|
}
|
|
5020
5291
|
}
|
|
5292
|
+
return kept;
|
|
5021
5293
|
}
|
|
5022
5294
|
function buildSelectableNode(row, alias) {
|
|
5023
5295
|
const id = row[`${alias}_id`];
|
|
@@ -5044,9 +5316,9 @@ function buildSelectableNode(row, alias) {
|
|
|
5044
5316
|
createdAt,
|
|
5045
5317
|
updatedAt,
|
|
5046
5318
|
deletedAt
|
|
5047
|
-
}
|
|
5319
|
+
},
|
|
5320
|
+
...propsExcludingReserved(rawProps, RESERVED_NODE_KEYS)
|
|
5048
5321
|
};
|
|
5049
|
-
assignPropsExcludingReserved(result, rawProps, RESERVED_NODE_KEYS);
|
|
5050
5322
|
return result;
|
|
5051
5323
|
}
|
|
5052
5324
|
function buildSelectableNodeOrUndefined(row, alias) {
|
|
@@ -5086,15 +5358,14 @@ function buildSelectableEdge(row, alias) {
|
|
|
5086
5358
|
createdAt,
|
|
5087
5359
|
updatedAt,
|
|
5088
5360
|
deletedAt
|
|
5089
|
-
}
|
|
5361
|
+
},
|
|
5362
|
+
...propsExcludingReserved(rawProps, RESERVED_EDGE_KEYS)
|
|
5090
5363
|
};
|
|
5091
|
-
assignPropsExcludingReserved(result, rawProps, RESERVED_EDGE_KEYS);
|
|
5092
5364
|
return result;
|
|
5093
5365
|
}
|
|
5094
5366
|
function buildSelectContext(row, startAlias, traversals) {
|
|
5095
|
-
const context =
|
|
5096
|
-
|
|
5097
|
-
};
|
|
5367
|
+
const context = createDataKeyedBag();
|
|
5368
|
+
context[startAlias] = buildSelectableNode(row, startAlias);
|
|
5098
5369
|
for (const traversal of traversals) {
|
|
5099
5370
|
const nodeAlias = traversal.nodeAlias;
|
|
5100
5371
|
const edgeAlias = traversal.edgeAlias;
|
|
@@ -5110,7 +5381,7 @@ function buildSelectContext(row, startAlias, traversals) {
|
|
|
5110
5381
|
}
|
|
5111
5382
|
}
|
|
5112
5383
|
}
|
|
5113
|
-
return context;
|
|
5384
|
+
return { ...context };
|
|
5114
5385
|
}
|
|
5115
5386
|
function mapResults(rows, startAlias, traversals, selectFunction) {
|
|
5116
5387
|
return rows.map((row) => {
|
|
@@ -5408,14 +5679,13 @@ var FieldAccessTracker = class {
|
|
|
5408
5679
|
}
|
|
5409
5680
|
};
|
|
5410
5681
|
function createTrackingContext(state, tracker, options) {
|
|
5411
|
-
const context =
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
};
|
|
5682
|
+
const context = createDataKeyedBag();
|
|
5683
|
+
context[state.startAlias] = createNodeTrackingProxy(
|
|
5684
|
+
state.startAlias,
|
|
5685
|
+
state.startKinds,
|
|
5686
|
+
tracker,
|
|
5687
|
+
options
|
|
5688
|
+
);
|
|
5419
5689
|
for (const traversal of state.traversals) {
|
|
5420
5690
|
const edgeKindNames = mergeEdgeKinds(traversal);
|
|
5421
5691
|
const optionalAbsent = options.optionalTraversalAliases === "absent" && traversal.optional;
|
|
@@ -5441,7 +5711,7 @@ function createTrackingContext(state, tracker, options) {
|
|
|
5441
5711
|
}
|
|
5442
5712
|
}
|
|
5443
5713
|
}
|
|
5444
|
-
return context;
|
|
5714
|
+
return { ...context };
|
|
5445
5715
|
}
|
|
5446
5716
|
function createNodeTrackingProxy(alias, kindNames, tracker, options) {
|
|
5447
5717
|
return new Proxy(
|
|
@@ -5449,9 +5719,10 @@ function createNodeTrackingProxy(alias, kindNames, tracker, options) {
|
|
|
5449
5719
|
{
|
|
5450
5720
|
get: (_, property) => {
|
|
5451
5721
|
if (typeof property === "symbol") return;
|
|
5452
|
-
if (property
|
|
5453
|
-
|
|
5454
|
-
|
|
5722
|
+
if (isInteropProbeKey(property) && !options.schemaIntrospector.hasDeclaredField(kindNames, property)) {
|
|
5723
|
+
return;
|
|
5724
|
+
}
|
|
5725
|
+
if (OBJECT_PROTOTYPE_PROPERTIES.has(property) && !options.schemaIntrospector.hasDeclaredField(kindNames, property)) {
|
|
5455
5726
|
if (property === "constructor") return Object;
|
|
5456
5727
|
if (property === "__proto__") return Object.prototype;
|
|
5457
5728
|
return Reflect.get(Object.prototype, property);
|
|
@@ -5482,9 +5753,16 @@ function createEdgeTrackingProxy(alias, edgeKindNames, tracker, options) {
|
|
|
5482
5753
|
{
|
|
5483
5754
|
get: (_, property) => {
|
|
5484
5755
|
if (typeof property === "symbol") return;
|
|
5485
|
-
if (property
|
|
5486
|
-
|
|
5487
|
-
|
|
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
|
+
)) {
|
|
5488
5766
|
if (property === "constructor") return Object;
|
|
5489
5767
|
if (property === "__proto__") return Object.prototype;
|
|
5490
5768
|
return Reflect.get(Object.prototype, property);
|
|
@@ -5762,7 +6040,7 @@ function buildAliasPlans(state, selectiveFields, schemaIntrospector) {
|
|
|
5762
6040
|
return plans.toSorted((a, b) => compareStrings(a.alias, b.alias));
|
|
5763
6041
|
}
|
|
5764
6042
|
function buildSelectiveContext(row, plans, traversals) {
|
|
5765
|
-
const context =
|
|
6043
|
+
const context = createDataKeyedBag();
|
|
5766
6044
|
for (const plan of plans) {
|
|
5767
6045
|
const value = plan.optional && plan.idOutputName !== void 0 ? buildOptionalAliasValue(row, plan) : buildRequiredAliasValue(row, plan);
|
|
5768
6046
|
context[plan.alias] = value;
|
|
@@ -5778,7 +6056,7 @@ function buildSelectiveContext(row, plans, traversals) {
|
|
|
5778
6056
|
}
|
|
5779
6057
|
}
|
|
5780
6058
|
}
|
|
5781
|
-
return context;
|
|
6059
|
+
return { ...context };
|
|
5782
6060
|
}
|
|
5783
6061
|
function buildOptionalAliasValue(row, plan) {
|
|
5784
6062
|
if (plan.idOutputName === void 0) {
|
|
@@ -5791,17 +6069,16 @@ function buildOptionalAliasValue(row, plan) {
|
|
|
5791
6069
|
return buildRequiredAliasValue(row, plan);
|
|
5792
6070
|
}
|
|
5793
6071
|
function buildRequiredAliasValue(row, plan) {
|
|
5794
|
-
const base =
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
}
|
|
6072
|
+
const base = createDataKeyedBag();
|
|
6073
|
+
base[SELECTABLE_ALIAS_MARKER] = {
|
|
6074
|
+
alias: plan.alias,
|
|
6075
|
+
kind: plan.kind
|
|
5799
6076
|
};
|
|
5800
6077
|
for (const field2 of plan.systemFields) {
|
|
5801
6078
|
base[field2.field] = nullToUndefined(row[field2.outputName]);
|
|
5802
6079
|
}
|
|
5803
6080
|
if (plan.metaFields.length > 0) {
|
|
5804
|
-
const meta =
|
|
6081
|
+
const meta = createDataKeyedBag();
|
|
5805
6082
|
for (const field2 of plan.metaFields) {
|
|
5806
6083
|
meta[field2.metaKey] = nullToUndefined(row[field2.outputName]);
|
|
5807
6084
|
}
|
|
@@ -5814,23 +6091,26 @@ function buildRequiredAliasValue(row, plan) {
|
|
|
5814
6091
|
return createGuardedProxy(base, plan.alias);
|
|
5815
6092
|
}
|
|
5816
6093
|
function createGuardedProxy(target, debugPath) {
|
|
5817
|
-
return new Proxy(
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
|
|
5829
|
-
|
|
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);
|
|
5830
6111
|
}
|
|
5831
|
-
throw new MissingSelectiveFieldError(debugPath, property);
|
|
5832
6112
|
}
|
|
5833
|
-
|
|
6113
|
+
);
|
|
5834
6114
|
}
|
|
5835
6115
|
|
|
5836
6116
|
// src/query/builder/read-instant-template.ts
|
|
@@ -5879,7 +6159,7 @@ function fillTemplateParams(params, bindings, dialect, listParameters = EMPTY_LI
|
|
|
5879
6159
|
readInstant ??= nowIso();
|
|
5880
6160
|
return readInstant;
|
|
5881
6161
|
}
|
|
5882
|
-
const value = bindings
|
|
6162
|
+
const value = readOwnProperty(bindings, name);
|
|
5883
6163
|
if (value === void 0) {
|
|
5884
6164
|
throw new ConfigurationError(`Missing binding for parameter "${name}"`, {
|
|
5885
6165
|
parameterName: name
|
|
@@ -5946,7 +6226,7 @@ function substitutePredicateExpression(expr, bindings) {
|
|
|
5946
6226
|
}
|
|
5947
6227
|
case "string_op": {
|
|
5948
6228
|
if (isParameterRef(expr.pattern)) {
|
|
5949
|
-
const value = bindings
|
|
6229
|
+
const value = readOwnProperty(bindings, expr.pattern.name);
|
|
5950
6230
|
if (value === void 0) {
|
|
5951
6231
|
throw new ConfigurationError(
|
|
5952
6232
|
`Missing binding for parameter "${expr.pattern.name}"`,
|
|
@@ -6020,7 +6300,7 @@ function substitutePredicateExpression(expr, bindings) {
|
|
|
6020
6300
|
}
|
|
6021
6301
|
}
|
|
6022
6302
|
function resolveBinding(bindings, name) {
|
|
6023
|
-
const value = bindings
|
|
6303
|
+
const value = readOwnProperty(bindings, name);
|
|
6024
6304
|
if (value === void 0) {
|
|
6025
6305
|
throw new ConfigurationError(`Missing binding for parameter "${name}"`, {
|
|
6026
6306
|
parameterName: name
|
|
@@ -6300,7 +6580,7 @@ function validateBindings(bindings, metadata) {
|
|
|
6300
6580
|
const { names: expectedNames, stringOpParameters, listParameters } = metadata;
|
|
6301
6581
|
const missing = [];
|
|
6302
6582
|
for (const name of expectedNames) {
|
|
6303
|
-
if (bindings
|
|
6583
|
+
if (readOwnProperty(bindings, name) === void 0) {
|
|
6304
6584
|
missing.push(name);
|
|
6305
6585
|
}
|
|
6306
6586
|
}
|
|
@@ -6320,7 +6600,7 @@ function validateBindings(bindings, metadata) {
|
|
|
6320
6600
|
);
|
|
6321
6601
|
}
|
|
6322
6602
|
for (const name of expectedNames) {
|
|
6323
|
-
const value = bindings
|
|
6603
|
+
const value = readOwnProperty(bindings, name);
|
|
6324
6604
|
if (listParameters.has(name)) {
|
|
6325
6605
|
validateListBinding(name, value, listParameters.get(name));
|
|
6326
6606
|
continue;
|
|
@@ -6711,6 +6991,7 @@ function buildCompileOptions(config) {
|
|
|
6711
6991
|
dialect: config.dialect ?? "sqlite",
|
|
6712
6992
|
schema: config.schema,
|
|
6713
6993
|
windowFunctions: config.backend?.capabilities.windowFunctions ?? true,
|
|
6994
|
+
identitySameIdAcrossKinds: config.identitySameIdAcrossKinds,
|
|
6714
6995
|
...fulltextStrategy === void 0 ? {} : {
|
|
6715
6996
|
fulltextStrategy,
|
|
6716
6997
|
fulltextLanguages: buildFulltextLanguages(config.registry)
|
|
@@ -7460,7 +7741,9 @@ var ExecutableQuery = class _ExecutableQuery {
|
|
|
7460
7741
|
optionalNodeAliases.add(traversal.nodeAlias);
|
|
7461
7742
|
}
|
|
7462
7743
|
}
|
|
7463
|
-
const cursorContext =
|
|
7744
|
+
const cursorContext = /* @__PURE__ */ Object.create(
|
|
7745
|
+
null
|
|
7746
|
+
);
|
|
7464
7747
|
for (const spec of this.#paginationOrderBy()) {
|
|
7465
7748
|
const alias = spec.field.alias;
|
|
7466
7749
|
const jsonPointer2 = spec.field.jsonPointer;
|
|
@@ -7518,7 +7801,9 @@ var ExecutableQuery = class _ExecutableQuery {
|
|
|
7518
7801
|
if (typeof existing_ === "object" && existing_ !== null) {
|
|
7519
7802
|
current = existing_;
|
|
7520
7803
|
} else {
|
|
7521
|
-
const created =
|
|
7804
|
+
const created = /* @__PURE__ */ Object.create(
|
|
7805
|
+
null
|
|
7806
|
+
);
|
|
7522
7807
|
current[segment] = created;
|
|
7523
7808
|
current = created;
|
|
7524
7809
|
}
|
|
@@ -7532,7 +7817,7 @@ var ExecutableQuery = class _ExecutableQuery {
|
|
|
7532
7817
|
if (typeof existing === "object" && existing !== null) {
|
|
7533
7818
|
return existing;
|
|
7534
7819
|
}
|
|
7535
|
-
const created =
|
|
7820
|
+
const created = /* @__PURE__ */ Object.create(null);
|
|
7536
7821
|
cursorContext[alias] = created;
|
|
7537
7822
|
return created;
|
|
7538
7823
|
}
|
|
@@ -7880,7 +8165,7 @@ var ExecutableAggregateQuery = class _ExecutableAggregateQuery {
|
|
|
7880
8165
|
*/
|
|
7881
8166
|
#mapResults(rows) {
|
|
7882
8167
|
return rows.map((row) => {
|
|
7883
|
-
const result =
|
|
8168
|
+
const result = createDataKeyedBag();
|
|
7884
8169
|
for (const key of Object.keys(this.#fields)) {
|
|
7885
8170
|
const field2 = this.#fields[key];
|
|
7886
8171
|
if (!field2) continue;
|
|
@@ -7891,7 +8176,7 @@ var ExecutableAggregateQuery = class _ExecutableAggregateQuery {
|
|
|
7891
8176
|
}
|
|
7892
8177
|
result[key] = normalizeFieldValue(field2, value);
|
|
7893
8178
|
}
|
|
7894
|
-
return result;
|
|
8179
|
+
return { ...result };
|
|
7895
8180
|
});
|
|
7896
8181
|
}
|
|
7897
8182
|
};
|
|
@@ -8004,9 +8289,10 @@ var TraversalBuilder = class _TraversalBuilder {
|
|
|
8004
8289
|
#direction;
|
|
8005
8290
|
#fromAlias;
|
|
8006
8291
|
#optional;
|
|
8292
|
+
#includeIdentityMembers;
|
|
8007
8293
|
#variableLength;
|
|
8008
8294
|
#pendingEdgePredicates;
|
|
8009
|
-
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) {
|
|
8010
8296
|
this.#config = config;
|
|
8011
8297
|
this.#state = state;
|
|
8012
8298
|
this.#edgeKinds = edgeKinds;
|
|
@@ -8015,6 +8301,7 @@ var TraversalBuilder = class _TraversalBuilder {
|
|
|
8015
8301
|
this.#direction = direction;
|
|
8016
8302
|
this.#fromAlias = fromAlias;
|
|
8017
8303
|
this.#optional = optional;
|
|
8304
|
+
this.#includeIdentityMembers = includeIdentityMembers;
|
|
8018
8305
|
this.#variableLength = variableLength;
|
|
8019
8306
|
this.#pendingEdgePredicates = pendingEdgePredicates;
|
|
8020
8307
|
}
|
|
@@ -8062,7 +8349,8 @@ var TraversalBuilder = class _TraversalBuilder {
|
|
|
8062
8349
|
...depthAlias !== void 0 && { depthAlias }
|
|
8063
8350
|
}
|
|
8064
8351
|
},
|
|
8065
|
-
this.#pendingEdgePredicates
|
|
8352
|
+
this.#pendingEdgePredicates,
|
|
8353
|
+
this.#includeIdentityMembers
|
|
8066
8354
|
);
|
|
8067
8355
|
}
|
|
8068
8356
|
/**
|
|
@@ -8091,7 +8379,8 @@ var TraversalBuilder = class _TraversalBuilder {
|
|
|
8091
8379
|
this.#inverseEdgeKinds,
|
|
8092
8380
|
this.#optional,
|
|
8093
8381
|
this.#variableLength,
|
|
8094
|
-
[...this.#pendingEdgePredicates, newPredicate]
|
|
8382
|
+
[...this.#pendingEdgePredicates, newPredicate],
|
|
8383
|
+
this.#includeIdentityMembers
|
|
8095
8384
|
);
|
|
8096
8385
|
}
|
|
8097
8386
|
/**
|
|
@@ -8172,12 +8461,16 @@ var TraversalBuilder = class _TraversalBuilder {
|
|
|
8172
8461
|
return new Proxy({}, {
|
|
8173
8462
|
get: (_, property) => {
|
|
8174
8463
|
if (typeof property === "symbol") return;
|
|
8175
|
-
if (property === "then") return;
|
|
8176
|
-
if (property === "toJSON") return;
|
|
8177
8464
|
if (property === "id") return idAccessor;
|
|
8178
8465
|
if (property === "kind") return kindAccessor;
|
|
8179
8466
|
if (property === "fromId") return fromIdAccessor;
|
|
8180
8467
|
if (property === "toId") return toIdAccessor;
|
|
8468
|
+
if (isInteropProbeKey(property) && !this.#config.schemaIntrospector.hasDeclaredEdgeField(
|
|
8469
|
+
allEdgeKinds,
|
|
8470
|
+
property
|
|
8471
|
+
)) {
|
|
8472
|
+
return;
|
|
8473
|
+
}
|
|
8181
8474
|
return buildFieldAccessor(property);
|
|
8182
8475
|
}
|
|
8183
8476
|
});
|
|
@@ -8223,7 +8516,8 @@ var TraversalBuilder = class _TraversalBuilder {
|
|
|
8223
8516
|
nodeKinds: kinds,
|
|
8224
8517
|
joinFromAlias: this.#fromAlias,
|
|
8225
8518
|
joinEdgeField: this.#direction === "out" ? "from_id" : "to_id",
|
|
8226
|
-
optional: this.#optional
|
|
8519
|
+
optional: this.#optional,
|
|
8520
|
+
...this.#includeIdentityMembers ? { includeIdentityMembers: true } : {}
|
|
8227
8521
|
};
|
|
8228
8522
|
const baseTraversal = this.#inverseEdgeKinds.length > 0 ? { ...traversalBase, inverseEdgeKinds: this.#inverseEdgeKinds } : traversalBase;
|
|
8229
8523
|
const traversal = this.#variableLength.enabled ? {
|
|
@@ -8514,6 +8808,7 @@ var QueryBuilder = class _QueryBuilder {
|
|
|
8514
8808
|
traverse(edgeKind, edgeAlias, options) {
|
|
8515
8809
|
validateSqlIdentifier(edgeAlias);
|
|
8516
8810
|
const direction = options?.direction ?? "out";
|
|
8811
|
+
this.#assertIdentityTraversalAllowed(options);
|
|
8517
8812
|
const expansion = options?.expand ?? this.#config.defaultTraversalExpansion;
|
|
8518
8813
|
const includeImplyingEdges = expansion === "implying" || expansion === "all";
|
|
8519
8814
|
const includeInverseEdges = expansion === "inverse" || expansion === "all";
|
|
@@ -8531,7 +8826,12 @@ var QueryBuilder = class _QueryBuilder {
|
|
|
8531
8826
|
direction,
|
|
8532
8827
|
fromAlias,
|
|
8533
8828
|
inverseEdgeKinds,
|
|
8534
|
-
false
|
|
8829
|
+
false,
|
|
8830
|
+
void 0,
|
|
8831
|
+
// variableLength — default
|
|
8832
|
+
void 0,
|
|
8833
|
+
// pendingEdgePredicates — default
|
|
8834
|
+
options?.includeIdentityMembers ?? false
|
|
8535
8835
|
);
|
|
8536
8836
|
}
|
|
8537
8837
|
/**
|
|
@@ -8544,6 +8844,7 @@ var QueryBuilder = class _QueryBuilder {
|
|
|
8544
8844
|
optionalTraverse(edgeKind, edgeAlias, options) {
|
|
8545
8845
|
validateSqlIdentifier(edgeAlias);
|
|
8546
8846
|
const direction = options?.direction ?? "out";
|
|
8847
|
+
this.#assertIdentityTraversalAllowed(options);
|
|
8547
8848
|
const expansion = options?.expand ?? this.#config.defaultTraversalExpansion;
|
|
8548
8849
|
const includeImplyingEdges = expansion === "implying" || expansion === "all";
|
|
8549
8850
|
const includeInverseEdges = expansion === "inverse" || expansion === "all";
|
|
@@ -8561,7 +8862,12 @@ var QueryBuilder = class _QueryBuilder {
|
|
|
8561
8862
|
direction,
|
|
8562
8863
|
fromAlias,
|
|
8563
8864
|
inverseEdgeKinds,
|
|
8564
|
-
true
|
|
8865
|
+
true,
|
|
8866
|
+
void 0,
|
|
8867
|
+
// variableLength — default
|
|
8868
|
+
void 0,
|
|
8869
|
+
// pendingEdgePredicates — default
|
|
8870
|
+
options?.includeIdentityMembers ?? false
|
|
8565
8871
|
);
|
|
8566
8872
|
}
|
|
8567
8873
|
/**
|
|
@@ -8586,6 +8892,7 @@ var QueryBuilder = class _QueryBuilder {
|
|
|
8586
8892
|
});
|
|
8587
8893
|
}
|
|
8588
8894
|
const direction = options?.direction ?? "out";
|
|
8895
|
+
this.#assertIdentityTraversalAllowed(options);
|
|
8589
8896
|
const expansion = options?.expand ?? this.#config.defaultTraversalExpansion;
|
|
8590
8897
|
const includeImplyingEdges = expansion === "implying" || expansion === "all";
|
|
8591
8898
|
const includeInverseEdges = expansion === "inverse" || expansion === "all";
|
|
@@ -8610,7 +8917,12 @@ var QueryBuilder = class _QueryBuilder {
|
|
|
8610
8917
|
direction,
|
|
8611
8918
|
fromAlias,
|
|
8612
8919
|
inverseEdgeKinds,
|
|
8613
|
-
optional
|
|
8920
|
+
optional,
|
|
8921
|
+
void 0,
|
|
8922
|
+
// variableLength — default
|
|
8923
|
+
void 0,
|
|
8924
|
+
// pendingEdgePredicates — default
|
|
8925
|
+
options?.includeIdentityMembers ?? false
|
|
8614
8926
|
);
|
|
8615
8927
|
}
|
|
8616
8928
|
/**
|
|
@@ -8912,11 +9224,15 @@ var QueryBuilder = class _QueryBuilder {
|
|
|
8912
9224
|
return new Proxy({}, {
|
|
8913
9225
|
get: (_, property) => {
|
|
8914
9226
|
if (typeof property === "symbol") return;
|
|
8915
|
-
if (property === "then") return;
|
|
8916
|
-
if (property === "toJSON") return;
|
|
8917
9227
|
if (property === "id") return idAccessor;
|
|
8918
9228
|
if (property === "kind") return kindAccessor;
|
|
8919
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
|
+
}
|
|
8920
9236
|
return this.#getFieldBuilderForProperty(kindNames, property, alias);
|
|
8921
9237
|
}
|
|
8922
9238
|
});
|
|
@@ -8925,6 +9241,21 @@ var QueryBuilder = class _QueryBuilder {
|
|
|
8925
9241
|
if (!kindNames) return false;
|
|
8926
9242
|
return this.#config.schemaIntrospector.hasSearchableField(kindNames);
|
|
8927
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
|
+
}
|
|
8928
9259
|
#expandTraversalEdgeKinds(edgeKind, includeImplyingEdges) {
|
|
8929
9260
|
return includeImplyingEdges ? this.#config.registry.expandImplyingEdges(edgeKind) : [edgeKind];
|
|
8930
9261
|
}
|
|
@@ -9005,12 +9336,16 @@ var QueryBuilder = class _QueryBuilder {
|
|
|
9005
9336
|
return new Proxy({}, {
|
|
9006
9337
|
get: (_, property) => {
|
|
9007
9338
|
if (typeof property === "symbol") return;
|
|
9008
|
-
if (property === "then") return;
|
|
9009
|
-
if (property === "toJSON") return;
|
|
9010
9339
|
if (property === "id") return idAccessor;
|
|
9011
9340
|
if (property === "kind") return kindAccessor;
|
|
9012
9341
|
if (property === "fromId") return fromIdAccessor;
|
|
9013
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
|
+
}
|
|
9014
9349
|
return this.#getFieldBuilderForEdgeProperty(
|
|
9015
9350
|
edgeKindNames,
|
|
9016
9351
|
property,
|
|
@@ -9464,11 +9799,31 @@ function createQueryBuilderWithContext(graphId, registry, options) {
|
|
|
9464
9799
|
registry.nodeKinds,
|
|
9465
9800
|
registry.edgeKinds
|
|
9466
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
|
+
}
|
|
9467
9820
|
const config = {
|
|
9468
9821
|
graphId,
|
|
9469
9822
|
registry,
|
|
9470
9823
|
schemaIntrospector,
|
|
9471
9824
|
defaultTraversalExpansion: options?.defaultTraversalExpansion ?? "inverse",
|
|
9825
|
+
identityEnabled: options?.identityEnabled ?? registry.identity !== void 0,
|
|
9826
|
+
identitySameIdAcrossKinds: options?.identitySameIdAcrossKinds ?? registry.identity?.sameIdAcrossKinds ?? "fold",
|
|
9472
9827
|
...options?.backend !== void 0 && { backend: options.backend },
|
|
9473
9828
|
...options?.dialect !== void 0 && { dialect: options.dialect },
|
|
9474
9829
|
...options?.schema !== void 0 && { schema: options.schema }
|
|
@@ -9543,11 +9898,32 @@ function isPropsObject(value) {
|
|
|
9543
9898
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9544
9899
|
}
|
|
9545
9900
|
async function rebuildFulltextIndex(ctx, nodeKind, options) {
|
|
9546
|
-
const { backend
|
|
9547
|
-
|
|
9548
|
-
|
|
9549
|
-
|
|
9550
|
-
|
|
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) {
|
|
9924
|
+
throw new ConfigurationError(
|
|
9925
|
+
"Backend does not support fulltext; cannot rebuild index",
|
|
9926
|
+
{ backend: backend.dialect, capability: "fulltext" }
|
|
9551
9927
|
);
|
|
9552
9928
|
}
|
|
9553
9929
|
const pageSize = validatePageSize(options.pageSize);
|
|
@@ -9623,7 +9999,7 @@ async function rebuildFulltextIndex(ctx, nodeKind, options) {
|
|
|
9623
9999
|
}
|
|
9624
10000
|
}
|
|
9625
10001
|
};
|
|
9626
|
-
await
|
|
10002
|
+
await driver.runPage(writePage);
|
|
9627
10003
|
processed += rows.length;
|
|
9628
10004
|
upserted += pageResult.toUpsert.length;
|
|
9629
10005
|
cleared += pageResult.toDelete.length;
|
|
@@ -10465,7 +10841,8 @@ function classifyModifications(existing, next) {
|
|
|
10465
10841
|
}
|
|
10466
10842
|
};
|
|
10467
10843
|
for (const [name, nextNode] of Object.entries(next.nodes ?? {})) {
|
|
10468
|
-
const
|
|
10844
|
+
const existingNodes = existing.nodes;
|
|
10845
|
+
const existingNode = existingNodes !== void 0 && hasOwnKey(existingNodes, name) ? existingNodes[name] : void 0;
|
|
10469
10846
|
if (existingNode === void 0) continue;
|
|
10470
10847
|
classifyNode(name, existingNode, nextNode, {
|
|
10471
10848
|
recordIncompatible,
|
|
@@ -10475,7 +10852,8 @@ function classifyModifications(existing, next) {
|
|
|
10475
10852
|
});
|
|
10476
10853
|
}
|
|
10477
10854
|
for (const [name, nextEdge] of Object.entries(next.edges ?? {})) {
|
|
10478
|
-
const
|
|
10855
|
+
const existingEdges = existing.edges;
|
|
10856
|
+
const existingEdge = existingEdges !== void 0 && hasOwnKey(existingEdges, name) ? existingEdges[name] : void 0;
|
|
10479
10857
|
if (existingEdge === void 0) continue;
|
|
10480
10858
|
classifyEdge(name, existingEdge, nextEdge, {
|
|
10481
10859
|
recordIncompatible,
|
|
@@ -10869,13 +11247,17 @@ function resolveMaxHops(rawMaxHops, fallback, optionName) {
|
|
|
10869
11247
|
}
|
|
10870
11248
|
function resolveMaxIterations(value, fallback, algorithm) {
|
|
10871
11249
|
const maxIterations = value ?? fallback;
|
|
10872
|
-
|
|
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) {
|
|
10873
11256
|
throw new ConfigurationError(
|
|
10874
|
-
`${algorithm}
|
|
10875
|
-
{
|
|
11257
|
+
`${algorithm} ${optionName} must be a positive safe integer, got ${String(value)}.`,
|
|
11258
|
+
{ [optionName]: value }
|
|
10876
11259
|
);
|
|
10877
11260
|
}
|
|
10878
|
-
return maxIterations;
|
|
10879
11261
|
}
|
|
10880
11262
|
function assertEdgeKinds(edges) {
|
|
10881
11263
|
if (edges.length === 0) {
|
|
@@ -10885,16 +11267,6 @@ function assertEdgeKinds(edges) {
|
|
|
10885
11267
|
);
|
|
10886
11268
|
}
|
|
10887
11269
|
}
|
|
10888
|
-
function compileNodeKindSeedFilter(nodeKinds) {
|
|
10889
|
-
if (nodeKinds === void 0) return sql`TRUE`;
|
|
10890
|
-
return compileKindFilter(sql.raw("n.kind"), nodeKinds);
|
|
10891
|
-
}
|
|
10892
|
-
function normalizeNodeKinds(nodeKinds) {
|
|
10893
|
-
if (nodeKinds === void 0) return void 0;
|
|
10894
|
-
return [...new Set(nodeKinds)].toSorted(
|
|
10895
|
-
(left, right) => compareCodePoints(left, right)
|
|
10896
|
-
);
|
|
10897
|
-
}
|
|
10898
11270
|
function assertGraphAnalyticsSupported(ctx, algorithm, options = {}) {
|
|
10899
11271
|
const graphAnalytics = ctx.backend.capabilities.graphAnalytics?.supported === true;
|
|
10900
11272
|
const windowFunctions = ctx.backend.capabilities.windowFunctions;
|
|
@@ -10970,6 +11342,56 @@ async function countWorkingTableRows(context) {
|
|
|
10970
11342
|
);
|
|
10971
11343
|
return Number(rows[0]?.count ?? 0);
|
|
10972
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
|
+
}
|
|
10973
11395
|
var DEFAULT_MAX_BIND_PARAMETERS = 999;
|
|
10974
11396
|
var RESERVED_TEMPORAL_BIND_PARAMETERS_PER_BRANCH = 12;
|
|
10975
11397
|
var RESERVED_WEIGHT_BIND_PARAMETERS_PER_BRANCH = 4;
|
|
@@ -11025,64 +11447,101 @@ async function runIterativeGraphOperation(ctx, options, plan) {
|
|
|
11025
11447
|
accessMode: "read_only",
|
|
11026
11448
|
temporaryWrites: INTERNAL_TEMPORARY_WRITES
|
|
11027
11449
|
};
|
|
11028
|
-
|
|
11029
|
-
|
|
11030
|
-
|
|
11031
|
-
|
|
11032
|
-
|
|
11033
|
-
);
|
|
11034
|
-
|
|
11035
|
-
|
|
11036
|
-
|
|
11037
|
-
|
|
11038
|
-
|
|
11039
|
-
|
|
11040
|
-
|
|
11041
|
-
|
|
11042
|
-
|
|
11043
|
-
|
|
11044
|
-
|
|
11045
|
-
|
|
11046
|
-
|
|
11047
|
-
|
|
11048
|
-
|
|
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)
|
|
11049
11497
|
);
|
|
11050
|
-
if (workingMemoryStatement !== void 0) {
|
|
11051
|
-
await context.executeTemporary(workingMemoryStatement);
|
|
11052
|
-
}
|
|
11053
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 {
|
|
11054
11509
|
await context.executeTemporary(plan.createWorkingTable(context));
|
|
11055
|
-
|
|
11056
|
-
|
|
11057
|
-
|
|
11058
|
-
|
|
11059
|
-
|
|
11060
|
-
|
|
11061
|
-
|
|
11062
|
-
|
|
11063
|
-
|
|
11064
|
-
|
|
11065
|
-
|
|
11066
|
-
|
|
11067
|
-
|
|
11068
|
-
|
|
11069
|
-
|
|
11070
|
-
|
|
11071
|
-
|
|
11072
|
-
|
|
11073
|
-
|
|
11074
|
-
plan.algorithm,
|
|
11075
|
-
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
|
|
11076
11529
|
);
|
|
11077
11530
|
}
|
|
11078
|
-
return await plan.extractResult(context, state);
|
|
11079
|
-
} catch (error) {
|
|
11080
|
-
operationFailure = { error };
|
|
11081
|
-
throw error;
|
|
11082
|
-
} finally {
|
|
11083
|
-
await cleanupWorkingTables(context, plan.cleanup, operationFailure);
|
|
11084
11531
|
}
|
|
11085
|
-
|
|
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
|
+
}
|
|
11086
11545
|
}
|
|
11087
11546
|
function shouldRefreshWorkingTableStatistics(workingTableSize, analyzedRowCount) {
|
|
11088
11547
|
if (workingTableSize < WORKING_TABLE_ANALYZE_MINIMUM_ROWS) return false;
|
|
@@ -11385,7 +11844,6 @@ async function executeLabelPropagation(ctx, options) {
|
|
|
11385
11844
|
DEFAULT_LABEL_PROPAGATION_MAX_ITERATIONS,
|
|
11386
11845
|
"labelPropagation"
|
|
11387
11846
|
);
|
|
11388
|
-
const nodeKinds = normalizeNodeKinds(options.nodeKinds);
|
|
11389
11847
|
const policy = {
|
|
11390
11848
|
detector: createOscillationDetector(),
|
|
11391
11849
|
maxIterations,
|
|
@@ -11401,7 +11859,7 @@ async function executeLabelPropagation(ctx, options) {
|
|
|
11401
11859
|
algorithm: "labelPropagation",
|
|
11402
11860
|
maxIterations,
|
|
11403
11861
|
createWorkingTable,
|
|
11404
|
-
initialize: (context) => initializeWorkingTables(context, nodeKinds),
|
|
11862
|
+
initialize: (context) => initializeWorkingTables(context, options.nodeKinds),
|
|
11405
11863
|
runRound: (context, state, iteration) => runLabelPropagationRound(context, state, iteration, policy),
|
|
11406
11864
|
hasConverged(state) {
|
|
11407
11865
|
return state.changedCount === 0 || state.completed;
|
|
@@ -11466,26 +11924,21 @@ function activeIndexIdentifier(context) {
|
|
|
11466
11924
|
);
|
|
11467
11925
|
}
|
|
11468
11926
|
async function initializeWorkingTables(context, nodeKinds) {
|
|
11469
|
-
|
|
11470
|
-
|
|
11471
|
-
|
|
11472
|
-
|
|
11473
|
-
|
|
11474
|
-
|
|
11475
|
-
|
|
11476
|
-
|
|
11477
|
-
FROM ${operation.schema.nodesTable} n
|
|
11478
|
-
WHERE n.graph_id = ${graphId}
|
|
11479
|
-
AND ${nodeKindFilter}
|
|
11480
|
-
AND ${operation.nodeTemporalFilter}
|
|
11481
|
-
`);
|
|
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
|
+
]);
|
|
11482
11935
|
await context.executeTemporary(sql`
|
|
11483
11936
|
CREATE INDEX ${frontierIndexIdentifier(context)}
|
|
11484
|
-
ON ${workingTable} (graph_id, run_id, changed_round)
|
|
11937
|
+
ON ${context.workingTable} (graph_id, run_id, changed_round)
|
|
11485
11938
|
`);
|
|
11486
11939
|
await context.executeTemporary(sql`
|
|
11487
11940
|
CREATE INDEX ${activeIndexIdentifier(context)}
|
|
11488
|
-
ON ${workingTable} (graph_id, run_id, active_round)
|
|
11941
|
+
ON ${context.workingTable} (graph_id, run_id, active_round)
|
|
11489
11942
|
`);
|
|
11490
11943
|
const neighborTable = neighborTableIdentifier(context);
|
|
11491
11944
|
await context.executeTemporary(sql`
|
|
@@ -11497,10 +11950,10 @@ async function initializeWorkingTables(context, nodeKinds) {
|
|
|
11497
11950
|
PRIMARY KEY (target_kind, target_id, neighbor_kind, neighbor_id)
|
|
11498
11951
|
)
|
|
11499
11952
|
`);
|
|
11500
|
-
for (const edgeKinds of operation.edgeKindChunks) {
|
|
11953
|
+
for (const edgeKinds of context.operation.edgeKindChunks) {
|
|
11501
11954
|
await materializeChunkNeighbors(context, edgeKinds);
|
|
11502
11955
|
}
|
|
11503
|
-
const analyzeNeighbors = operation.ctx.dialect.analyzeTemporaryTable(neighborTable);
|
|
11956
|
+
const analyzeNeighbors = context.operation.ctx.dialect.analyzeTemporaryTable(neighborTable);
|
|
11504
11957
|
if (analyzeNeighbors !== void 0) {
|
|
11505
11958
|
await context.executeTemporary(analyzeNeighbors);
|
|
11506
11959
|
}
|
|
@@ -11742,7 +12195,7 @@ function executePageRankOperation(ctx, options, seeds) {
|
|
|
11742
12195
|
hasConverged(state) {
|
|
11743
12196
|
return state.maximumChange <= resolved.tolerance;
|
|
11744
12197
|
},
|
|
11745
|
-
extractResult: extractScores
|
|
12198
|
+
extractResult: (context, state) => extractScores(context, state, resolved.topK)
|
|
11746
12199
|
});
|
|
11747
12200
|
}
|
|
11748
12201
|
function resolvePageRankOptions(options, algorithm) {
|
|
@@ -11760,6 +12213,7 @@ function resolvePageRankOptions(options, algorithm) {
|
|
|
11760
12213
|
{ tolerance }
|
|
11761
12214
|
);
|
|
11762
12215
|
}
|
|
12216
|
+
assertPositiveSafeIntegerOption(options.topK, algorithm, "topK");
|
|
11763
12217
|
return {
|
|
11764
12218
|
dampingFactor,
|
|
11765
12219
|
tolerance,
|
|
@@ -11768,7 +12222,8 @@ function resolvePageRankOptions(options, algorithm) {
|
|
|
11768
12222
|
DEFAULT_MAX_ITERATIONS,
|
|
11769
12223
|
algorithm
|
|
11770
12224
|
),
|
|
11771
|
-
nodeKinds:
|
|
12225
|
+
nodeKinds: options.nodeKinds,
|
|
12226
|
+
topK: options.topK
|
|
11772
12227
|
};
|
|
11773
12228
|
}
|
|
11774
12229
|
function normalizeSeeds(seeds) {
|
|
@@ -11841,17 +12296,12 @@ function createWorkingTable2(context) {
|
|
|
11841
12296
|
}
|
|
11842
12297
|
async function initializeWorkingTable(context, nodeKinds, seeds) {
|
|
11843
12298
|
const { operation, workingTable, graphId, runId } = context;
|
|
11844
|
-
|
|
11845
|
-
|
|
11846
|
-
|
|
11847
|
-
|
|
11848
|
-
|
|
11849
|
-
|
|
11850
|
-
FROM ${operation.schema.nodesTable} n
|
|
11851
|
-
WHERE n.graph_id = ${graphId}
|
|
11852
|
-
AND ${nodeKindFilter}
|
|
11853
|
-
AND ${operation.nodeTemporalFilter}
|
|
11854
|
-
`);
|
|
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
|
+
]);
|
|
11855
12305
|
const workingTableSize = await countWorkingTableRows(context);
|
|
11856
12306
|
if (workingTableSize === 0) {
|
|
11857
12307
|
if (seeds !== void 0) {
|
|
@@ -12087,16 +12537,18 @@ async function readMaximumChange(context, sourceScoreColumn, targetScoreColumn)
|
|
|
12087
12537
|
);
|
|
12088
12538
|
return Number(rows[0]?.value ?? 0);
|
|
12089
12539
|
}
|
|
12090
|
-
async function extractScores(context, state) {
|
|
12540
|
+
async function extractScores(context, state, topK) {
|
|
12091
12541
|
const score = sql.identifier(state.activeScoreColumn);
|
|
12092
12542
|
const nodeId = context.operation.ctx.dialect.binaryText(sql`node_id`);
|
|
12093
12543
|
const nodeKind = context.operation.ctx.dialect.binaryText(sql`node_kind`);
|
|
12544
|
+
const limitClause = topK === void 0 ? sql.empty() : sql`LIMIT ${topK}`;
|
|
12094
12545
|
const rows = await context.backend.execute(
|
|
12095
12546
|
asCompiledRowsSql(sql`
|
|
12096
12547
|
SELECT node_id, node_kind, ${score} AS score
|
|
12097
12548
|
FROM ${context.workingTable}
|
|
12098
12549
|
WHERE graph_id = ${context.graphId} AND run_id = ${context.runId}
|
|
12099
12550
|
ORDER BY ${score} DESC, ${nodeId}, ${nodeKind}
|
|
12551
|
+
${limitClause}
|
|
12100
12552
|
`)
|
|
12101
12553
|
);
|
|
12102
12554
|
return rows.map((row) => ({
|
|
@@ -12679,7 +13131,11 @@ async function executeWeaklyConnectedComponents(ctx, options) {
|
|
|
12679
13131
|
DEFAULT_WCC_MAX_ITERATIONS,
|
|
12680
13132
|
"weaklyConnectedComponents"
|
|
12681
13133
|
);
|
|
12682
|
-
|
|
13134
|
+
assertPositiveSafeIntegerOption(
|
|
13135
|
+
options.minComponentSize,
|
|
13136
|
+
"weaklyConnectedComponents",
|
|
13137
|
+
"minComponentSize"
|
|
13138
|
+
);
|
|
12683
13139
|
const traversalOptions = {
|
|
12684
13140
|
edges: options.edges,
|
|
12685
13141
|
direction: "both",
|
|
@@ -12690,12 +13146,12 @@ async function executeWeaklyConnectedComponents(ctx, options) {
|
|
|
12690
13146
|
algorithm: "weaklyConnectedComponents",
|
|
12691
13147
|
maxIterations,
|
|
12692
13148
|
createWorkingTable: createWorkingTable4,
|
|
12693
|
-
initialize: (context) => initializeWorkingTable2(context, nodeKinds),
|
|
13149
|
+
initialize: (context) => initializeWorkingTable2(context, options.nodeKinds),
|
|
12694
13150
|
runRound: runLabelPropagationRound2,
|
|
12695
13151
|
hasConverged(state) {
|
|
12696
13152
|
return state.changedCount === 0;
|
|
12697
13153
|
},
|
|
12698
|
-
extractResult: extractMemberships2
|
|
13154
|
+
extractResult: (context) => extractMemberships2(context, options.minComponentSize)
|
|
12699
13155
|
});
|
|
12700
13156
|
}
|
|
12701
13157
|
function createWorkingTable4(context) {
|
|
@@ -12715,22 +13171,16 @@ function createWorkingTable4(context) {
|
|
|
12715
13171
|
`;
|
|
12716
13172
|
}
|
|
12717
13173
|
async function initializeWorkingTable2(context, nodeKinds) {
|
|
12718
|
-
|
|
12719
|
-
|
|
12720
|
-
|
|
12721
|
-
|
|
12722
|
-
|
|
12723
|
-
|
|
12724
|
-
|
|
12725
|
-
${graphId}, ${runId}, n.id, n.kind, n.id, n.kind, n.id, n.kind, 0
|
|
12726
|
-
FROM ${operation.schema.nodesTable} n
|
|
12727
|
-
WHERE n.graph_id = ${graphId}
|
|
12728
|
-
AND ${nodeKindFilter}
|
|
12729
|
-
AND ${operation.nodeTemporalFilter}
|
|
12730
|
-
`);
|
|
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
|
+
]);
|
|
12731
13181
|
await context.executeTemporary(sql`
|
|
12732
13182
|
CREATE INDEX ${frontierIndexIdentifier(context)}
|
|
12733
|
-
ON ${workingTable} (graph_id, run_id, improved_round)
|
|
13183
|
+
ON ${context.workingTable} (graph_id, run_id, improved_round)
|
|
12734
13184
|
`);
|
|
12735
13185
|
const workingTableSize = await countWorkingTableRows(context);
|
|
12736
13186
|
return { changedCount: workingTableSize, workingTableSize };
|
|
@@ -12830,24 +13280,32 @@ function applyNextLabels2(context, iteration) {
|
|
|
12830
13280
|
`)
|
|
12831
13281
|
);
|
|
12832
13282
|
}
|
|
12833
|
-
async function extractMemberships2(context) {
|
|
13283
|
+
async function extractMemberships2(context, minComponentSize) {
|
|
12834
13284
|
const { operation } = context;
|
|
12835
|
-
const
|
|
12836
|
-
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`);
|
|
12837
13289
|
const nodeId = operation.ctx.dialect.binaryText(sql`node_id`);
|
|
12838
13290
|
const nodeKind = operation.ctx.dialect.binaryText(sql`node_kind`);
|
|
13291
|
+
const componentSizeFilter = minComponentSize === void 0 ? sql.empty() : sql`WHERE component_size >= ${minComponentSize}`;
|
|
12839
13292
|
const rows = await context.backend.execute(
|
|
12840
13293
|
asCompiledRowsSql(sql`
|
|
12841
|
-
|
|
12842
|
-
|
|
12843
|
-
|
|
12844
|
-
|
|
12845
|
-
|
|
12846
|
-
|
|
12847
|
-
|
|
12848
|
-
|
|
12849
|
-
|
|
12850
|
-
|
|
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}
|
|
12851
13309
|
ORDER BY ${componentId}, ${componentKind}, ${nodeId}, ${nodeKind}
|
|
12852
13310
|
`)
|
|
12853
13311
|
);
|
|
@@ -13428,6 +13886,94 @@ function createGraphAlgorithms(params) {
|
|
|
13428
13886
|
};
|
|
13429
13887
|
}
|
|
13430
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 lowerBoundRequiresWrite(requested, stored) {
|
|
13947
|
+
const requestedValidFrom = requested?.validFrom;
|
|
13948
|
+
if (!preservesImmutableLowerBound(requested?.onImmutableLowerBound)) {
|
|
13949
|
+
return windowFieldChanges(requestedValidFrom, stored);
|
|
13950
|
+
}
|
|
13951
|
+
return requestedValidFrom !== void 0 && !isCanonicalIsoDate(requestedValidFrom);
|
|
13952
|
+
}
|
|
13953
|
+
function upsertWindowChanges(requested, current) {
|
|
13954
|
+
return lowerBoundRequiresWrite(requested, current.valid_from) || windowFieldChanges(requested?.validTo, current.valid_to);
|
|
13955
|
+
}
|
|
13956
|
+
function windowAfterUpsertCreate(requested) {
|
|
13957
|
+
return { valid_from: requested.validFrom, valid_to: requested.validTo };
|
|
13958
|
+
}
|
|
13959
|
+
function windowAfterUpsertUpdate(current, requested) {
|
|
13960
|
+
return {
|
|
13961
|
+
valid_from: current.valid_from,
|
|
13962
|
+
valid_to: requested.validTo ?? current.valid_to
|
|
13963
|
+
};
|
|
13964
|
+
}
|
|
13965
|
+
function shouldCoalesceUpsert(existing, options, runDirtyCheck) {
|
|
13966
|
+
const windowChanges = upsertWindowChanges(options, existing);
|
|
13967
|
+
if (runDirtyCheck === void 0 || existing.deleted_at !== void 0 || windowChanges) {
|
|
13968
|
+
return false;
|
|
13969
|
+
}
|
|
13970
|
+
try {
|
|
13971
|
+
return runDirtyCheck().unchanged;
|
|
13972
|
+
} catch {
|
|
13973
|
+
return false;
|
|
13974
|
+
}
|
|
13975
|
+
}
|
|
13976
|
+
|
|
13431
13977
|
// src/store/collections/edge-collection.ts
|
|
13432
13978
|
function narrowEdge(edge) {
|
|
13433
13979
|
return edge;
|
|
@@ -13449,13 +13995,29 @@ function buildCreateEdgeInput(kind, from, to, props, options) {
|
|
|
13449
13995
|
if (options?.validTo !== void 0) input.validTo = options.validTo;
|
|
13450
13996
|
return input;
|
|
13451
13997
|
}
|
|
13452
|
-
function buildUpdateEdgeInput(id, props, options) {
|
|
13453
|
-
const input = { id, props };
|
|
13998
|
+
function buildUpdateEdgeInput(kind, id, props, options) {
|
|
13999
|
+
const input = { id, identity: { kind }, props };
|
|
14000
|
+
if (options?.validTo !== void 0) input.validTo = options.validTo;
|
|
14001
|
+
return input;
|
|
14002
|
+
}
|
|
14003
|
+
function buildUpsertUpdateEdgeInput(kind, id, from, to, props, options) {
|
|
14004
|
+
const input = {
|
|
14005
|
+
id,
|
|
14006
|
+
identity: {
|
|
14007
|
+
kind,
|
|
14008
|
+
fromKind: from.kind,
|
|
14009
|
+
fromId: from.id,
|
|
14010
|
+
toKind: to.kind,
|
|
14011
|
+
toId: to.id
|
|
14012
|
+
},
|
|
14013
|
+
props
|
|
14014
|
+
};
|
|
14015
|
+
if (options?.validFrom !== void 0) input.validFrom = options.validFrom;
|
|
13454
14016
|
if (options?.validTo !== void 0) input.validTo = options.validTo;
|
|
13455
14017
|
return input;
|
|
13456
14018
|
}
|
|
13457
14019
|
function endpointKey(endpointKind, id) {
|
|
13458
|
-
return
|
|
14020
|
+
return encodeTupleKey([endpointKind, id]);
|
|
13459
14021
|
}
|
|
13460
14022
|
function groupEndpointIdsByKind(references) {
|
|
13461
14023
|
const referencesByKind = groupBy(references, (ref) => ref.kind);
|
|
@@ -13502,6 +14064,7 @@ function createEdgeCollection(config) {
|
|
|
13502
14064
|
executeUpdate: executeEdgeUpdate2,
|
|
13503
14065
|
executeUpsertUpdate: executeEdgeUpsertUpdate2,
|
|
13504
14066
|
executeDelete: executeEdgeDelete2,
|
|
14067
|
+
executeDeleteBatch: executeEdgeDeleteBatch2,
|
|
13505
14068
|
executeHardDelete: executeEdgeHardDelete2,
|
|
13506
14069
|
temporalRowMatcher
|
|
13507
14070
|
} = config;
|
|
@@ -13610,7 +14173,7 @@ function createEdgeCollection(config) {
|
|
|
13610
14173
|
},
|
|
13611
14174
|
async update(id, props, options) {
|
|
13612
14175
|
const result = await executeEdgeUpdate2(
|
|
13613
|
-
buildUpdateEdgeInput(id, props, options),
|
|
14176
|
+
buildUpdateEdgeInput(kind, id, props, options),
|
|
13614
14177
|
backend
|
|
13615
14178
|
);
|
|
13616
14179
|
return narrowEdge(result);
|
|
@@ -13650,10 +14213,10 @@ function createEdgeCollection(config) {
|
|
|
13650
14213
|
};
|
|
13651
14214
|
},
|
|
13652
14215
|
async delete(id) {
|
|
13653
|
-
await executeEdgeDelete2(id, backend);
|
|
14216
|
+
await executeEdgeDelete2(kind, id, backend);
|
|
13654
14217
|
},
|
|
13655
14218
|
async hardDelete(id) {
|
|
13656
|
-
await executeEdgeHardDelete2(id, backend);
|
|
14219
|
+
await executeEdgeHardDelete2(kind, id, backend);
|
|
13657
14220
|
},
|
|
13658
14221
|
async find(filter, temporal) {
|
|
13659
14222
|
const untypedFilter = filter;
|
|
@@ -13712,6 +14275,20 @@ function createEdgeCollection(config) {
|
|
|
13712
14275
|
const toUpdate = [];
|
|
13713
14276
|
const pending = /* @__PURE__ */ new Map();
|
|
13714
14277
|
const deferred = [];
|
|
14278
|
+
const repeatedIds = config.upsertDirtyCheck === void 0 ? /* @__PURE__ */ new Set() : findRepeatedUpsertIds(items);
|
|
14279
|
+
function runDirtyCheck(edgeKind, id, currentProps, inputProps) {
|
|
14280
|
+
if (config.upsertDirtyCheck === void 0) return void 0;
|
|
14281
|
+
try {
|
|
14282
|
+
return config.upsertDirtyCheck(
|
|
14283
|
+
edgeKind,
|
|
14284
|
+
id,
|
|
14285
|
+
currentProps,
|
|
14286
|
+
inputProps
|
|
14287
|
+
);
|
|
14288
|
+
} catch {
|
|
14289
|
+
return void 0;
|
|
14290
|
+
}
|
|
14291
|
+
}
|
|
13715
14292
|
let itemIndex = 0;
|
|
13716
14293
|
for (const item of items) {
|
|
13717
14294
|
const pendingEntry = pending.get(item.id);
|
|
@@ -13728,25 +14305,48 @@ function createEdgeCollection(config) {
|
|
|
13728
14305
|
item
|
|
13729
14306
|
)
|
|
13730
14307
|
});
|
|
14308
|
+
pending.set(item.id, {
|
|
14309
|
+
identity: {
|
|
14310
|
+
kind,
|
|
14311
|
+
fromKind: item.from.kind,
|
|
14312
|
+
fromId: item.from.id,
|
|
14313
|
+
toKind: item.to.kind,
|
|
14314
|
+
toId: item.to.id
|
|
14315
|
+
},
|
|
14316
|
+
props: repeatedIds.has(item.id) ? runDirtyCheck(kind, item.id, {}, inputProps)?.validatedProps : void 0,
|
|
14317
|
+
window: windowAfterUpsertCreate(item),
|
|
14318
|
+
sourceIndex: itemIndex
|
|
14319
|
+
});
|
|
13731
14320
|
itemIndex++;
|
|
13732
14321
|
continue;
|
|
13733
14322
|
}
|
|
13734
14323
|
const deletedAt = pendingEntry === void 0 ? requireDefined(original).deleted_at : void 0;
|
|
13735
|
-
|
|
13736
|
-
|
|
13737
|
-
|
|
13738
|
-
|
|
13739
|
-
|
|
13740
|
-
|
|
13741
|
-
|
|
13742
|
-
|
|
13743
|
-
|
|
13744
|
-
|
|
13745
|
-
|
|
13746
|
-
|
|
13747
|
-
|
|
13748
|
-
|
|
13749
|
-
const
|
|
14324
|
+
const expectedIdentity = {
|
|
14325
|
+
kind,
|
|
14326
|
+
fromKind: item.from.kind,
|
|
14327
|
+
fromId: item.from.id,
|
|
14328
|
+
toKind: item.to.kind,
|
|
14329
|
+
toId: item.to.id
|
|
14330
|
+
};
|
|
14331
|
+
const actualIdentity = pendingEntry === void 0 ? edgeIdentityFromRow(requireDefined(original)) : pendingEntry.identity;
|
|
14332
|
+
assertEdgeIdentityMatches(
|
|
14333
|
+
item.id,
|
|
14334
|
+
expectedIdentity,
|
|
14335
|
+
actualIdentity,
|
|
14336
|
+
"update"
|
|
14337
|
+
);
|
|
14338
|
+
const currentProps = pendingEntry === void 0 ? rowPropsToObject(requireDefined(original).props) : pendingEntry.props;
|
|
14339
|
+
const dirty = currentProps === void 0 ? void 0 : runDirtyCheck(
|
|
14340
|
+
original?.kind ?? kind,
|
|
14341
|
+
item.id,
|
|
14342
|
+
currentProps,
|
|
14343
|
+
inputProps
|
|
14344
|
+
);
|
|
14345
|
+
const currentWindow = pendingEntry === void 0 ? {
|
|
14346
|
+
valid_from: requireDefined(original).valid_from,
|
|
14347
|
+
valid_to: requireDefined(original).valid_to
|
|
14348
|
+
} : pendingEntry.window;
|
|
14349
|
+
const coalesce = dirty?.unchanged === true && deletedAt === void 0 && !upsertWindowChanges(item, currentWindow);
|
|
13750
14350
|
if (coalesce) {
|
|
13751
14351
|
if (pendingEntry === void 0) {
|
|
13752
14352
|
results2[itemIndex] = narrowEdge(
|
|
@@ -13761,15 +14361,25 @@ function createEdgeCollection(config) {
|
|
|
13761
14361
|
} else {
|
|
13762
14362
|
toUpdate.push({
|
|
13763
14363
|
index: itemIndex,
|
|
13764
|
-
input:
|
|
14364
|
+
input: buildUpsertUpdateEdgeInput(
|
|
14365
|
+
kind,
|
|
14366
|
+
item.id,
|
|
14367
|
+
item.from,
|
|
14368
|
+
item.to,
|
|
14369
|
+
inputProps,
|
|
14370
|
+
item
|
|
14371
|
+
),
|
|
13765
14372
|
clearDeleted: deletedAt !== void 0
|
|
13766
14373
|
});
|
|
13767
|
-
|
|
13768
|
-
|
|
13769
|
-
|
|
13770
|
-
|
|
13771
|
-
|
|
13772
|
-
|
|
14374
|
+
pending.set(item.id, {
|
|
14375
|
+
identity: actualIdentity,
|
|
14376
|
+
props: dirty?.validatedProps,
|
|
14377
|
+
// A resurrection rewrites both bounds only when it NAMES the lower
|
|
14378
|
+
// one (buildUpdateEdge's clearDeleted leg); omitting `validFrom`
|
|
14379
|
+
// keeps the stored window, exactly like a live-row update.
|
|
14380
|
+
window: deletedAt !== void 0 && item.validFrom !== void 0 ? windowAfterUpsertCreate(item) : windowAfterUpsertUpdate(currentWindow, item),
|
|
14381
|
+
sourceIndex: itemIndex
|
|
14382
|
+
});
|
|
13773
14383
|
}
|
|
13774
14384
|
itemIndex++;
|
|
13775
14385
|
}
|
|
@@ -13793,7 +14403,10 @@ function createEdgeCollection(config) {
|
|
|
13793
14403
|
}
|
|
13794
14404
|
return { results: results2, mutations: toCreate.length + toUpdate.length };
|
|
13795
14405
|
};
|
|
13796
|
-
const { results, mutations } =
|
|
14406
|
+
const { results, mutations } = await runOptionallyInTransaction(
|
|
14407
|
+
backend,
|
|
14408
|
+
(target) => upsertAll(target)
|
|
14409
|
+
);
|
|
13797
14410
|
await config.maybeRefreshStatisticsAfterBulk?.(mutations);
|
|
13798
14411
|
return results;
|
|
13799
14412
|
},
|
|
@@ -13811,16 +14424,7 @@ function createEdgeCollection(config) {
|
|
|
13811
14424
|
},
|
|
13812
14425
|
async bulkDelete(ids) {
|
|
13813
14426
|
if (ids.length === 0) return;
|
|
13814
|
-
|
|
13815
|
-
for (const id of ids) {
|
|
13816
|
-
await executeEdgeDelete2(id, target);
|
|
13817
|
-
}
|
|
13818
|
-
};
|
|
13819
|
-
if (backend.capabilities.transactions && "transaction" in backend) {
|
|
13820
|
-
await backend.transaction(async (txBackend) => deleteAll(txBackend));
|
|
13821
|
-
return;
|
|
13822
|
-
}
|
|
13823
|
-
await deleteAll(backend);
|
|
14427
|
+
await executeEdgeDeleteBatch2(kind, ids, backend);
|
|
13824
14428
|
},
|
|
13825
14429
|
async findByEndpoints(from, to, options, temporal) {
|
|
13826
14430
|
const result = await config.executeFindByEndpoints(
|
|
@@ -13900,18 +14504,6 @@ function createEdgeCollection(config) {
|
|
|
13900
14504
|
};
|
|
13901
14505
|
}
|
|
13902
14506
|
|
|
13903
|
-
// src/store/collections/coalesce.ts
|
|
13904
|
-
function shouldCoalesceUpsert(existing, options, runDirtyCheck) {
|
|
13905
|
-
if (runDirtyCheck === void 0 || existing.deleted_at !== void 0 || options?.validFrom !== void 0 || options?.validTo !== void 0) {
|
|
13906
|
-
return false;
|
|
13907
|
-
}
|
|
13908
|
-
try {
|
|
13909
|
-
return runDirtyCheck().unchanged;
|
|
13910
|
-
} catch {
|
|
13911
|
-
return false;
|
|
13912
|
-
}
|
|
13913
|
-
}
|
|
13914
|
-
|
|
13915
14507
|
// src/store/collections/node-collection.ts
|
|
13916
14508
|
function narrowNode(node) {
|
|
13917
14509
|
return node;
|
|
@@ -13937,7 +14529,6 @@ function evaluateNodePredicate(accessor, predicate) {
|
|
|
13937
14529
|
{
|
|
13938
14530
|
get(_target, property) {
|
|
13939
14531
|
if (typeof property === "symbol") return;
|
|
13940
|
-
if (property === "then" || property === "toJSON") return;
|
|
13941
14532
|
if (property === "id" || property === "kind" || property === "$fulltext") {
|
|
13942
14533
|
return accessor[property];
|
|
13943
14534
|
}
|
|
@@ -13960,6 +14551,15 @@ function buildUpdateInput(kind, id, props, options) {
|
|
|
13960
14551
|
if (options?.validTo !== void 0) input.validTo = options.validTo;
|
|
13961
14552
|
return input;
|
|
13962
14553
|
}
|
|
14554
|
+
function buildUpsertUpdateInput(kind, id, props, options) {
|
|
14555
|
+
const input = { kind, id, props };
|
|
14556
|
+
if (options?.validFrom !== void 0) input.validFrom = options.validFrom;
|
|
14557
|
+
if (options?.validTo !== void 0) input.validTo = options.validTo;
|
|
14558
|
+
if (options?.onImmutableLowerBound !== void 0) {
|
|
14559
|
+
input.onImmutableLowerBound = options.onImmutableLowerBound;
|
|
14560
|
+
}
|
|
14561
|
+
return input;
|
|
14562
|
+
}
|
|
13963
14563
|
function mapBulkNodeInputs(kind, items) {
|
|
13964
14564
|
return items.map((item) => buildCreateInput(kind, item.props, item));
|
|
13965
14565
|
}
|
|
@@ -13977,6 +14577,7 @@ function createNodeCollection(config) {
|
|
|
13977
14577
|
executeUpdateWhere: executeNodeUpdateWhere2,
|
|
13978
14578
|
executeUpsertUpdate: executeNodeUpsertUpdate2,
|
|
13979
14579
|
executeDelete: executeNodeDelete2,
|
|
14580
|
+
executeDeleteBatch: executeNodeDeleteBatch2,
|
|
13980
14581
|
executeHardDelete: executeNodeHardDelete2,
|
|
13981
14582
|
temporalRowMatcher,
|
|
13982
14583
|
createQuery,
|
|
@@ -14147,20 +14748,30 @@ function createNodeCollection(config) {
|
|
|
14147
14748
|
},
|
|
14148
14749
|
async upsertByIdFromRecord(id, data, options) {
|
|
14149
14750
|
const existing = await backend.getNode(graphId, kind, id);
|
|
14150
|
-
|
|
14751
|
+
const coalesces = (row) => {
|
|
14151
14752
|
const runDirtyCheck = config.upsertDirtyCheck && (() => requireDefined(config.upsertDirtyCheck)(
|
|
14152
14753
|
kind,
|
|
14153
14754
|
id,
|
|
14154
|
-
rowPropsToObject(
|
|
14755
|
+
rowPropsToObject(row.props),
|
|
14155
14756
|
data
|
|
14156
14757
|
));
|
|
14157
|
-
|
|
14158
|
-
|
|
14758
|
+
return shouldCoalesceUpsert(row, options, runDirtyCheck);
|
|
14759
|
+
};
|
|
14760
|
+
const decision = existing !== void 0 && coalesces(existing) ? await runOptionallyInTransaction(
|
|
14761
|
+
backend,
|
|
14762
|
+
async (target) => {
|
|
14763
|
+
const confirmed = await target.getNode(graphId, kind, id);
|
|
14764
|
+
return confirmed !== void 0 && coalesces(confirmed) ? { verdict: "skip", row: confirmed } : { verdict: "write", row: confirmed };
|
|
14159
14765
|
}
|
|
14766
|
+
) : { verdict: "write", row: existing };
|
|
14767
|
+
if (decision.verdict === "skip") {
|
|
14768
|
+
return narrowNode(rowToNode2(decision.row));
|
|
14769
|
+
}
|
|
14770
|
+
if (decision.row !== void 0) {
|
|
14160
14771
|
const result2 = await executeNodeUpdate2(
|
|
14161
|
-
|
|
14772
|
+
buildUpsertUpdateInput(kind, id, data, options),
|
|
14162
14773
|
backend,
|
|
14163
|
-
{ clearDeleted:
|
|
14774
|
+
{ clearDeleted: decision.row.deleted_at !== void 0 }
|
|
14164
14775
|
);
|
|
14165
14776
|
return narrowNode(result2);
|
|
14166
14777
|
}
|
|
@@ -14206,6 +14817,15 @@ function createNodeCollection(config) {
|
|
|
14206
14817
|
const toUpdate = [];
|
|
14207
14818
|
const pending = /* @__PURE__ */ new Map();
|
|
14208
14819
|
const deferred = [];
|
|
14820
|
+
const repeatedIds = config.upsertDirtyCheck === void 0 ? /* @__PURE__ */ new Set() : findRepeatedUpsertIds(items);
|
|
14821
|
+
function runDirtyCheck(id, currentProps, inputProps) {
|
|
14822
|
+
if (config.upsertDirtyCheck === void 0) return void 0;
|
|
14823
|
+
try {
|
|
14824
|
+
return config.upsertDirtyCheck(kind, id, currentProps, inputProps);
|
|
14825
|
+
} catch {
|
|
14826
|
+
return void 0;
|
|
14827
|
+
}
|
|
14828
|
+
}
|
|
14209
14829
|
let itemIndex = 0;
|
|
14210
14830
|
for (const item of items) {
|
|
14211
14831
|
const pendingEntry = pending.get(item.id);
|
|
@@ -14215,25 +14835,22 @@ function createNodeCollection(config) {
|
|
|
14215
14835
|
index: itemIndex,
|
|
14216
14836
|
input: buildCreateInput(kind, item.props, item)
|
|
14217
14837
|
});
|
|
14838
|
+
pending.set(item.id, {
|
|
14839
|
+
props: repeatedIds.has(item.id) ? runDirtyCheck(item.id, {}, item.props)?.validatedProps : void 0,
|
|
14840
|
+
window: windowAfterUpsertCreate(item),
|
|
14841
|
+
sourceIndex: itemIndex
|
|
14842
|
+
});
|
|
14218
14843
|
itemIndex++;
|
|
14219
14844
|
continue;
|
|
14220
14845
|
}
|
|
14221
14846
|
const deletedAt = pendingEntry === void 0 ? requireDefined(original).deleted_at : void 0;
|
|
14222
|
-
|
|
14223
|
-
|
|
14224
|
-
|
|
14225
|
-
|
|
14226
|
-
|
|
14227
|
-
|
|
14228
|
-
|
|
14229
|
-
currentProps,
|
|
14230
|
-
item.props
|
|
14231
|
-
);
|
|
14232
|
-
} catch {
|
|
14233
|
-
dirty = void 0;
|
|
14234
|
-
}
|
|
14235
|
-
}
|
|
14236
|
-
const coalesce = dirty?.unchanged === true && deletedAt === void 0 && item.validFrom === void 0 && item.validTo === void 0;
|
|
14847
|
+
const currentProps = pendingEntry === void 0 ? rowPropsToObject(requireDefined(original).props) : pendingEntry.props;
|
|
14848
|
+
const dirty = currentProps === void 0 ? void 0 : runDirtyCheck(item.id, currentProps, item.props);
|
|
14849
|
+
const currentWindow = pendingEntry === void 0 ? {
|
|
14850
|
+
valid_from: requireDefined(original).valid_from,
|
|
14851
|
+
valid_to: requireDefined(original).valid_to
|
|
14852
|
+
} : pendingEntry.window;
|
|
14853
|
+
const coalesce = dirty?.unchanged === true && deletedAt === void 0 && !upsertWindowChanges(item, currentWindow);
|
|
14237
14854
|
if (coalesce) {
|
|
14238
14855
|
if (pendingEntry === void 0) {
|
|
14239
14856
|
results2[itemIndex] = narrowNode(
|
|
@@ -14248,15 +14865,17 @@ function createNodeCollection(config) {
|
|
|
14248
14865
|
} else {
|
|
14249
14866
|
toUpdate.push({
|
|
14250
14867
|
index: itemIndex,
|
|
14251
|
-
input:
|
|
14868
|
+
input: buildUpsertUpdateInput(kind, item.id, item.props, item),
|
|
14252
14869
|
clearDeleted: deletedAt !== void 0
|
|
14253
14870
|
});
|
|
14254
|
-
|
|
14255
|
-
|
|
14256
|
-
|
|
14257
|
-
|
|
14258
|
-
|
|
14259
|
-
|
|
14871
|
+
pending.set(item.id, {
|
|
14872
|
+
props: dirty?.validatedProps,
|
|
14873
|
+
// A resurrecting update rewrites BOTH bounds (buildUpdateNode's
|
|
14874
|
+
// clearDeleted leg stamps `valid_from` and reopens `valid_to`);
|
|
14875
|
+
// a live-row update moves only `valid_to`.
|
|
14876
|
+
window: deletedAt === void 0 ? windowAfterUpsertUpdate(currentWindow, item) : windowAfterUpsertCreate(item),
|
|
14877
|
+
sourceIndex: itemIndex
|
|
14878
|
+
});
|
|
14260
14879
|
}
|
|
14261
14880
|
itemIndex++;
|
|
14262
14881
|
}
|
|
@@ -14280,7 +14899,10 @@ function createNodeCollection(config) {
|
|
|
14280
14899
|
}
|
|
14281
14900
|
return { results: results2, mutations: toCreate.length + toUpdate.length };
|
|
14282
14901
|
};
|
|
14283
|
-
const { results, mutations } =
|
|
14902
|
+
const { results, mutations } = await runOptionallyInTransaction(
|
|
14903
|
+
backend,
|
|
14904
|
+
(target) => upsertAll(target)
|
|
14905
|
+
);
|
|
14284
14906
|
await config.maybeRefreshStatisticsAfterBulk?.(mutations);
|
|
14285
14907
|
return results;
|
|
14286
14908
|
},
|
|
@@ -14298,16 +14920,7 @@ function createNodeCollection(config) {
|
|
|
14298
14920
|
},
|
|
14299
14921
|
async bulkDelete(ids) {
|
|
14300
14922
|
if (ids.length === 0) return;
|
|
14301
|
-
|
|
14302
|
-
for (const id of ids) {
|
|
14303
|
-
await executeNodeDelete2(kind, id, target);
|
|
14304
|
-
}
|
|
14305
|
-
};
|
|
14306
|
-
if (backend.capabilities.transactions && "transaction" in backend) {
|
|
14307
|
-
await backend.transaction(async (txBackend) => deleteAll(txBackend));
|
|
14308
|
-
return;
|
|
14309
|
-
}
|
|
14310
|
-
await deleteAll(backend);
|
|
14923
|
+
await executeNodeDeleteBatch2(kind, ids, backend);
|
|
14311
14924
|
},
|
|
14312
14925
|
async findByConstraint(constraintName, props) {
|
|
14313
14926
|
const result = await executeFindByConstraint(
|
|
@@ -14505,6 +15118,7 @@ var HISTORY_STORE_BACKEND_KEYS = [
|
|
|
14505
15118
|
"insertNodesBatchReturning",
|
|
14506
15119
|
"insertUnique",
|
|
14507
15120
|
"insertUniqueBatch",
|
|
15121
|
+
"probeContributions",
|
|
14508
15122
|
"recordContributionMaterialization",
|
|
14509
15123
|
"recordIndexMaterialization",
|
|
14510
15124
|
"recordKindRemoval",
|
|
@@ -15415,7 +16029,7 @@ async function reconcilePendingRemovals(context) {
|
|
|
15415
16029
|
const priorEntries = priorSchema[entity === "node" ? "nodes" : "edges"];
|
|
15416
16030
|
const currentEntries = currentSchema[entity === "node" ? "nodes" : "edges"];
|
|
15417
16031
|
for (const kindName of Object.keys(priorEntries)) {
|
|
15418
|
-
if (kindName
|
|
16032
|
+
if (hasOwnKey(currentEntries, kindName)) continue;
|
|
15419
16033
|
if (recorded.has(kindRemovalKey(entity, kindName, currentRow.version))) {
|
|
15420
16034
|
continue;
|
|
15421
16035
|
}
|
|
@@ -15736,22 +16350,29 @@ function literal(value) {
|
|
|
15736
16350
|
|
|
15737
16351
|
// src/store/operations/edge-operations.ts
|
|
15738
16352
|
function getEdgeRegistration(graph, kind) {
|
|
16353
|
+
if (!hasOwnKey(graph.edges, kind)) throw new KindNotFoundError(kind, "edge");
|
|
15739
16354
|
const registration = graph.edges[kind];
|
|
15740
16355
|
if (registration === void 0) throw new KindNotFoundError(kind, "edge");
|
|
15741
16356
|
return registration;
|
|
15742
16357
|
}
|
|
15743
16358
|
function buildEdgeEndpointCacheKey(graphId, kind, id) {
|
|
15744
|
-
return
|
|
16359
|
+
return encodeTupleKey([graphId, kind, id]);
|
|
15745
16360
|
}
|
|
15746
16361
|
function buildEdgeFromCacheKey(graphId, edgeKind, fromKind, fromId) {
|
|
15747
|
-
return
|
|
16362
|
+
return encodeTupleKey([graphId, edgeKind, fromKind, fromId]);
|
|
15748
16363
|
}
|
|
15749
16364
|
function buildEdgeBetweenCacheKey(graphId, edgeKind, fromKind, fromId, toKind, toId) {
|
|
15750
|
-
return
|
|
16365
|
+
return encodeTupleKey([graphId, edgeKind, fromKind, fromId, toKind, toId]);
|
|
15751
16366
|
}
|
|
15752
16367
|
function buildCountEdgesFromCacheKey(params) {
|
|
15753
16368
|
const activeOnly = params.activeOnly === true ? "1" : "0";
|
|
15754
|
-
return
|
|
16369
|
+
return encodeTupleKey([
|
|
16370
|
+
params.graphId,
|
|
16371
|
+
params.edgeKind,
|
|
16372
|
+
params.fromKind,
|
|
16373
|
+
params.fromId,
|
|
16374
|
+
activeOnly
|
|
16375
|
+
]);
|
|
15755
16376
|
}
|
|
15756
16377
|
function buildInsertEdgeParams(graphId, id, kind, fromKind, fromId, toKind, toId, props, validFrom, validTo) {
|
|
15757
16378
|
const insertParams = {
|
|
@@ -15909,6 +16530,7 @@ async function validateAndPrepareEdgeCreate(ctx, input, id, backend) {
|
|
|
15909
16530
|
"validFrom"
|
|
15910
16531
|
);
|
|
15911
16532
|
const validTo = validateOptionalCanonicalIsoDate(input.validTo, "validTo");
|
|
16533
|
+
assertOrderedValidityWindow(`edge "${id}"`, validFrom, validTo);
|
|
15912
16534
|
const cardinality = registration.cardinality ?? "many";
|
|
15913
16535
|
const constraintContext = {
|
|
15914
16536
|
graphId: ctx.graphId,
|
|
@@ -15941,32 +16563,87 @@ async function validateAndPrepareEdgeCreate(ctx, input, id, backend) {
|
|
|
15941
16563
|
)
|
|
15942
16564
|
};
|
|
15943
16565
|
}
|
|
16566
|
+
function edgeCardinality(ctx, kind) {
|
|
16567
|
+
if (!hasOwnKey(ctx.graph.edges, kind)) return "many";
|
|
16568
|
+
return getEdgeRegistration(ctx.graph, kind).cardinality ?? "many";
|
|
16569
|
+
}
|
|
16570
|
+
var EdgeConvergenceRaced = class extends Error {
|
|
16571
|
+
constructor(kind) {
|
|
16572
|
+
super(
|
|
16573
|
+
`A competing writer claimed the ${kind} match key; re-resolving it. This is internal to getOrCreateByEndpoints and is never returned to a caller.`
|
|
16574
|
+
);
|
|
16575
|
+
this.name = "EdgeConvergenceRaced";
|
|
16576
|
+
}
|
|
16577
|
+
};
|
|
15944
16578
|
async function executeEdgeCreateInternal(ctx, input, backend, options) {
|
|
15945
16579
|
const kind = input.kind;
|
|
15946
16580
|
const id = input.id ?? generateId();
|
|
15947
16581
|
const opContext = ctx.createOperationContext("create", "edge", kind, id);
|
|
15948
|
-
|
|
15949
|
-
|
|
15950
|
-
|
|
15951
|
-
|
|
15952
|
-
|
|
15953
|
-
|
|
15954
|
-
|
|
15955
|
-
|
|
15956
|
-
|
|
15957
|
-
|
|
15958
|
-
|
|
15959
|
-
|
|
15960
|
-
|
|
15961
|
-
|
|
15962
|
-
|
|
15963
|
-
|
|
15964
|
-
|
|
15965
|
-
|
|
15966
|
-
|
|
15967
|
-
|
|
15968
|
-
|
|
15969
|
-
|
|
16582
|
+
const shouldReturnRow = options?.returnRow ?? true;
|
|
16583
|
+
const convergeOn = options?.convergeOn;
|
|
16584
|
+
return runHookedWriteOperation(
|
|
16585
|
+
ctx,
|
|
16586
|
+
opContext,
|
|
16587
|
+
backend,
|
|
16588
|
+
async (target) => {
|
|
16589
|
+
if (convergeOn !== void 0) {
|
|
16590
|
+
const candidateRows = await target.findEdgesByKind({
|
|
16591
|
+
graphId: ctx.graphId,
|
|
16592
|
+
kind,
|
|
16593
|
+
fromKind: input.fromKind,
|
|
16594
|
+
fromId: input.fromId,
|
|
16595
|
+
toKind: input.toKind,
|
|
16596
|
+
toId: input.toId,
|
|
16597
|
+
excludeDeleted: false,
|
|
16598
|
+
temporalMode: "includeTombstones"
|
|
16599
|
+
});
|
|
16600
|
+
const { liveRow, deletedRow } = findMatchingEdge(
|
|
16601
|
+
candidateRows,
|
|
16602
|
+
convergeOn.matchOn,
|
|
16603
|
+
convergeOn.props
|
|
16604
|
+
);
|
|
16605
|
+
if (liveRow !== void 0 || deletedRow !== void 0) {
|
|
16606
|
+
throw new EdgeConvergenceRaced(kind);
|
|
16607
|
+
}
|
|
16608
|
+
}
|
|
16609
|
+
const prepared = await validateAndPrepareEdgeCreate(
|
|
16610
|
+
ctx,
|
|
16611
|
+
input,
|
|
16612
|
+
id,
|
|
16613
|
+
target
|
|
16614
|
+
);
|
|
16615
|
+
const row = await withAlreadyExistsTranslation("edge", async () => {
|
|
16616
|
+
if (shouldReturnRow) return target.insertEdge(prepared.insertParams);
|
|
16617
|
+
await runInsertNoReturn(
|
|
16618
|
+
edgeInsertDispatch(target),
|
|
16619
|
+
prepared.insertParams
|
|
16620
|
+
);
|
|
16621
|
+
return;
|
|
16622
|
+
});
|
|
16623
|
+
return row === void 0 ? void 0 : rowToEdge(row);
|
|
16624
|
+
},
|
|
16625
|
+
{
|
|
16626
|
+
// A create that runs a cardinality probe, or one converging on a match
|
|
16627
|
+
// key, decides something the write must not have invalidated. A plain
|
|
16628
|
+
// `many` create decides nothing and takes no lock.
|
|
16629
|
+
fencesConstraintProbe: convergeOn === void 0 ? edgeWriteNeedsConstraintFence(edgeCardinality(ctx, kind)) : "edgeMatchKeyConvergence"
|
|
16630
|
+
}
|
|
16631
|
+
);
|
|
16632
|
+
}
|
|
16633
|
+
async function executeEdgeCreate(ctx, input, backend) {
|
|
16634
|
+
const edge = await executeEdgeCreateInternal(ctx, input, backend, {
|
|
16635
|
+
returnRow: true
|
|
16636
|
+
});
|
|
16637
|
+
if (!edge) {
|
|
16638
|
+
throw new DatabaseOperationError(
|
|
16639
|
+
"Edge create failed: expected created edge row",
|
|
16640
|
+
{ operation: "insert", entity: "edge" }
|
|
16641
|
+
);
|
|
16642
|
+
}
|
|
16643
|
+
return edge;
|
|
16644
|
+
}
|
|
16645
|
+
async function primeEdgeBatchValidationCache(ctx, inputs, backend, seedEndpointRow) {
|
|
16646
|
+
if (backend.getNodes === void 0) return;
|
|
15970
16647
|
const idsByKind = /* @__PURE__ */ new Map();
|
|
15971
16648
|
const addEndpoint = (kind, id) => {
|
|
15972
16649
|
const ids = idsByKind.get(kind) ?? /* @__PURE__ */ new Set();
|
|
@@ -16017,31 +16694,53 @@ async function executeEdgeCreateNoReturnBatch(ctx, inputs, backend) {
|
|
|
16017
16694
|
if (inputs.length === 0) {
|
|
16018
16695
|
return;
|
|
16019
16696
|
}
|
|
16020
|
-
await runInWriteTransaction(
|
|
16021
|
-
|
|
16022
|
-
|
|
16023
|
-
|
|
16024
|
-
|
|
16025
|
-
|
|
16026
|
-
|
|
16027
|
-
|
|
16697
|
+
await runInWriteTransaction(
|
|
16698
|
+
ctx,
|
|
16699
|
+
backend,
|
|
16700
|
+
async (target) => {
|
|
16701
|
+
const { batchInsertParams } = await prepareEdgeBatchCreates(
|
|
16702
|
+
ctx,
|
|
16703
|
+
inputs,
|
|
16704
|
+
target
|
|
16705
|
+
);
|
|
16706
|
+
await withAlreadyExistsTranslation(
|
|
16707
|
+
"edge",
|
|
16708
|
+
() => runInsertBatch(edgeInsertDispatch(target), batchInsertParams)
|
|
16709
|
+
);
|
|
16710
|
+
},
|
|
16711
|
+
{ fencesConstraintProbe: batchFencesConstraintProbe(ctx, inputs) }
|
|
16712
|
+
);
|
|
16028
16713
|
}
|
|
16029
16714
|
async function executeEdgeCreateBatch(ctx, inputs, backend) {
|
|
16030
16715
|
if (inputs.length === 0) {
|
|
16031
16716
|
return [];
|
|
16032
16717
|
}
|
|
16033
|
-
return runInWriteTransaction(
|
|
16034
|
-
|
|
16035
|
-
|
|
16036
|
-
|
|
16037
|
-
|
|
16038
|
-
|
|
16039
|
-
|
|
16040
|
-
|
|
16041
|
-
|
|
16718
|
+
return runInWriteTransaction(
|
|
16719
|
+
ctx,
|
|
16720
|
+
backend,
|
|
16721
|
+
async (target) => {
|
|
16722
|
+
const { batchInsertParams } = await prepareEdgeBatchCreates(
|
|
16723
|
+
ctx,
|
|
16724
|
+
inputs,
|
|
16725
|
+
target
|
|
16726
|
+
);
|
|
16727
|
+
const rows = await withAlreadyExistsTranslation(
|
|
16728
|
+
"edge",
|
|
16729
|
+
() => runInsertBatchReturning(edgeInsertDispatch(target), batchInsertParams)
|
|
16730
|
+
);
|
|
16731
|
+
return rows.map((row) => rowToEdge(row));
|
|
16732
|
+
},
|
|
16733
|
+
{ fencesConstraintProbe: batchFencesConstraintProbe(ctx, inputs) }
|
|
16734
|
+
);
|
|
16735
|
+
}
|
|
16736
|
+
function batchFencesConstraintProbe(ctx, inputs) {
|
|
16737
|
+
for (const input of inputs) {
|
|
16738
|
+
const reason = edgeWriteNeedsConstraintFence(
|
|
16739
|
+
edgeCardinality(ctx, input.kind)
|
|
16042
16740
|
);
|
|
16043
|
-
|
|
16044
|
-
}
|
|
16741
|
+
if (reason !== void 0) return reason;
|
|
16742
|
+
}
|
|
16743
|
+
return void 0;
|
|
16045
16744
|
}
|
|
16046
16745
|
function computeEdgeUpdate(ctx, kind, id, existingProps, inputProps) {
|
|
16047
16746
|
const registration = getEdgeRegistration(ctx.graph, kind);
|
|
@@ -16079,65 +16778,234 @@ async function performEdgeUpdate(ctx, input, target, options) {
|
|
|
16079
16778
|
const id = input.id;
|
|
16080
16779
|
const existing = await target.getEdge(ctx.graphId, id);
|
|
16081
16780
|
if (!existing || !options?.clearDeleted && existing.deleted_at) {
|
|
16082
|
-
throw new EdgeNotFoundError(
|
|
16781
|
+
throw new EdgeNotFoundError(input.identity.kind, id);
|
|
16083
16782
|
}
|
|
16783
|
+
assertEdgeIdentityMatches(
|
|
16784
|
+
id,
|
|
16785
|
+
input.identity,
|
|
16786
|
+
edgeIdentityFromRow(existing),
|
|
16787
|
+
"update"
|
|
16788
|
+
);
|
|
16084
16789
|
const { validatedProps } = resolveEdgeUpdateProps(ctx, existing, input.props);
|
|
16790
|
+
const validFrom = validateOptionalCanonicalIsoDate(
|
|
16791
|
+
input.validFrom,
|
|
16792
|
+
"validFrom"
|
|
16793
|
+
);
|
|
16085
16794
|
const validTo = validateOptionalCanonicalIsoDate(input.validTo, "validTo");
|
|
16795
|
+
const windowVerdict = assertWritableValidityWindow(
|
|
16796
|
+
`edge "${id}"`,
|
|
16797
|
+
validFrom,
|
|
16798
|
+
{
|
|
16799
|
+
effectiveValidFrom: existing.valid_from,
|
|
16800
|
+
appliesStatedValidFrom: options?.clearDeleted === true,
|
|
16801
|
+
// Unlike the node side, an edge RETAINS `valid_from` through a
|
|
16802
|
+
// resurrection that does not name a new one, so the effective bound is the
|
|
16803
|
+
// row's stored one on BOTH legs and there is no carve-out to make.
|
|
16804
|
+
effectiveBoundIsStored: true
|
|
16805
|
+
},
|
|
16806
|
+
validTo
|
|
16807
|
+
);
|
|
16086
16808
|
const updateParams = {
|
|
16087
16809
|
graphId: ctx.graphId,
|
|
16088
16810
|
id,
|
|
16089
|
-
|
|
16811
|
+
kind: input.identity.kind,
|
|
16812
|
+
props: validatedProps,
|
|
16813
|
+
// The bound the window verdict above READ, carried into the same `WHERE` as
|
|
16814
|
+
// the identity components and on exactly the same terms: present only when
|
|
16815
|
+
// the verdict consulted it, because a component the caller made no claim
|
|
16816
|
+
// about must not become a predicate that refuses legitimate writes.
|
|
16817
|
+
...windowVerdict.storedLowerBoundFence
|
|
16090
16818
|
};
|
|
16819
|
+
if (input.identity.fromKind !== void 0) {
|
|
16820
|
+
updateParams.fromKind = input.identity.fromKind;
|
|
16821
|
+
}
|
|
16822
|
+
if (input.identity.fromId !== void 0) {
|
|
16823
|
+
updateParams.fromId = input.identity.fromId;
|
|
16824
|
+
}
|
|
16825
|
+
if (input.identity.toKind !== void 0) {
|
|
16826
|
+
updateParams.toKind = input.identity.toKind;
|
|
16827
|
+
}
|
|
16828
|
+
if (input.identity.toId !== void 0) {
|
|
16829
|
+
updateParams.toId = input.identity.toId;
|
|
16830
|
+
}
|
|
16831
|
+
if (validFrom !== void 0) updateParams.validFrom = validFrom;
|
|
16091
16832
|
if (validTo !== void 0) updateParams.validTo = validTo;
|
|
16092
16833
|
if (options?.clearDeleted) updateParams.clearDeleted = true;
|
|
16093
|
-
const row = await
|
|
16834
|
+
const row = await withUnmatchedEdgeUpdateRefusal(
|
|
16835
|
+
ctx,
|
|
16836
|
+
target,
|
|
16837
|
+
id,
|
|
16838
|
+
input.identity,
|
|
16839
|
+
updateParams.expectedValidFrom !== void 0,
|
|
16840
|
+
() => target.updateEdge(updateParams)
|
|
16841
|
+
);
|
|
16094
16842
|
return rowToEdge(row);
|
|
16095
16843
|
}
|
|
16844
|
+
function isEdgeUpdateNoRowError(error) {
|
|
16845
|
+
return error instanceof DatabaseOperationError && error.details.operation === "update" && error.details.entity === "edge" && error.details.reason === "no_row_returned";
|
|
16846
|
+
}
|
|
16847
|
+
async function withUnmatchedEdgeUpdateRefusal(ctx, target, id, expected, assertedValidFrom, write) {
|
|
16848
|
+
try {
|
|
16849
|
+
return await write();
|
|
16850
|
+
} catch (error) {
|
|
16851
|
+
if (!isEdgeUpdateNoRowError(error)) throw error;
|
|
16852
|
+
const current = await target.getEdge(ctx.graphId, id);
|
|
16853
|
+
if (current !== void 0) {
|
|
16854
|
+
assertEdgeIdentityMatches(
|
|
16855
|
+
id,
|
|
16856
|
+
expected,
|
|
16857
|
+
edgeIdentityFromRow(current),
|
|
16858
|
+
"update"
|
|
16859
|
+
);
|
|
16860
|
+
if (assertedValidFrom && current.deleted_at === void 0) {
|
|
16861
|
+
throw new EdgeUpdateTargetMoved(expected.kind, id);
|
|
16862
|
+
}
|
|
16863
|
+
}
|
|
16864
|
+
throw new EdgeNotFoundError(expected.kind, id);
|
|
16865
|
+
}
|
|
16866
|
+
}
|
|
16867
|
+
var EdgeUpdateTargetMoved = class extends Error {
|
|
16868
|
+
constructor(kind, id) {
|
|
16869
|
+
super(
|
|
16870
|
+
`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.`
|
|
16871
|
+
);
|
|
16872
|
+
this.name = "EdgeUpdateTargetMoved";
|
|
16873
|
+
}
|
|
16874
|
+
};
|
|
16875
|
+
var EDGE_UPDATE_ATTEMPTS = 2;
|
|
16876
|
+
async function performEdgeUpdateConverging(ctx, input, target, options) {
|
|
16877
|
+
for (let attempt = 1; attempt <= EDGE_UPDATE_ATTEMPTS; attempt += 1) {
|
|
16878
|
+
try {
|
|
16879
|
+
return await performEdgeUpdate(ctx, input, target, options);
|
|
16880
|
+
} catch (error) {
|
|
16881
|
+
if (!(error instanceof EdgeUpdateTargetMoved)) throw error;
|
|
16882
|
+
if (attempt === EDGE_UPDATE_ATTEMPTS) {
|
|
16883
|
+
throw new DatabaseOperationError(
|
|
16884
|
+
`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.`,
|
|
16885
|
+
{ operation: "update", entity: "edge" },
|
|
16886
|
+
{ cause: error }
|
|
16887
|
+
);
|
|
16888
|
+
}
|
|
16889
|
+
}
|
|
16890
|
+
}
|
|
16891
|
+
throw new EdgeNotFoundError(input.identity.kind, input.id);
|
|
16892
|
+
}
|
|
16096
16893
|
async function executeEdgeUpdate(ctx, input, backend) {
|
|
16097
16894
|
const id = input.id;
|
|
16098
16895
|
const gate = await backend.getEdge(ctx.graphId, id);
|
|
16099
16896
|
if (!gate || gate.deleted_at) {
|
|
16100
|
-
throw new EdgeNotFoundError(
|
|
16897
|
+
throw new EdgeNotFoundError(input.identity.kind, id);
|
|
16101
16898
|
}
|
|
16899
|
+
assertEdgeIdentityMatches(
|
|
16900
|
+
id,
|
|
16901
|
+
input.identity,
|
|
16902
|
+
edgeIdentityFromRow(gate),
|
|
16903
|
+
"update"
|
|
16904
|
+
);
|
|
16102
16905
|
const opContext = ctx.createOperationContext("update", "edge", gate.kind, id);
|
|
16103
16906
|
return runHookedWriteOperation(
|
|
16104
16907
|
ctx,
|
|
16105
16908
|
opContext,
|
|
16106
16909
|
backend,
|
|
16107
|
-
(target) =>
|
|
16910
|
+
(target) => performEdgeUpdateConverging(ctx, input, target)
|
|
16108
16911
|
);
|
|
16109
16912
|
}
|
|
16110
16913
|
async function executeEdgeUpsertUpdate(ctx, input, backend, options) {
|
|
16914
|
+
const resurrectCardinality = options?.resurrectCardinality;
|
|
16111
16915
|
return runInWriteTransaction(
|
|
16112
16916
|
ctx,
|
|
16113
16917
|
backend,
|
|
16114
|
-
(target) =>
|
|
16918
|
+
async (target) => {
|
|
16919
|
+
if (resurrectCardinality !== void 0) {
|
|
16920
|
+
await checkCardinalityConstraint(
|
|
16921
|
+
{
|
|
16922
|
+
graphId: ctx.graphId,
|
|
16923
|
+
registry: ctx.registry,
|
|
16924
|
+
backend: target
|
|
16925
|
+
},
|
|
16926
|
+
input.identity.kind,
|
|
16927
|
+
resurrectCardinality.cardinality,
|
|
16928
|
+
resurrectCardinality.fromKind,
|
|
16929
|
+
resurrectCardinality.fromId,
|
|
16930
|
+
resurrectCardinality.toKind,
|
|
16931
|
+
resurrectCardinality.toId,
|
|
16932
|
+
resurrectCardinality.effectiveValidTo
|
|
16933
|
+
);
|
|
16934
|
+
}
|
|
16935
|
+
return performEdgeUpdateConverging(ctx, input, target, options);
|
|
16936
|
+
},
|
|
16937
|
+
{
|
|
16938
|
+
// An in-place props update re-derives no constraint verdict: endpoints
|
|
16939
|
+
// are immutable, so cardinality cannot change under it.
|
|
16940
|
+
fencesConstraintProbe: resurrectCardinality === void 0 ? void 0 : edgeWriteNeedsConstraintFence(resurrectCardinality.cardinality)
|
|
16941
|
+
}
|
|
16115
16942
|
);
|
|
16116
16943
|
}
|
|
16117
|
-
async function executeEdgeDelete(ctx, id, backend) {
|
|
16944
|
+
async function executeEdgeDelete(ctx, expectedKind, id, backend) {
|
|
16118
16945
|
const gate = await backend.getEdge(ctx.graphId, id);
|
|
16119
|
-
if (!gate
|
|
16946
|
+
if (!gate) return;
|
|
16947
|
+
assertEdgeIdentityMatches(
|
|
16948
|
+
id,
|
|
16949
|
+
{ kind: expectedKind },
|
|
16950
|
+
edgeIdentityFromRow(gate),
|
|
16951
|
+
"delete"
|
|
16952
|
+
);
|
|
16953
|
+
if (gate.deleted_at) return;
|
|
16120
16954
|
const opContext = ctx.createOperationContext("delete", "edge", gate.kind, id);
|
|
16121
16955
|
return runHookedWriteOperation(ctx, opContext, backend, async (target) => {
|
|
16122
16956
|
await target.deleteEdge({
|
|
16123
16957
|
graphId: ctx.graphId,
|
|
16124
|
-
id
|
|
16958
|
+
id,
|
|
16959
|
+
kind: expectedKind
|
|
16125
16960
|
});
|
|
16126
16961
|
});
|
|
16127
16962
|
}
|
|
16128
|
-
async function
|
|
16963
|
+
async function executeEdgeDeleteBatch(ctx, expectedKind, ids, backend) {
|
|
16964
|
+
await runInWriteTransaction(
|
|
16965
|
+
ctx,
|
|
16966
|
+
backend,
|
|
16967
|
+
async (target) => {
|
|
16968
|
+
let affectedCount = 0;
|
|
16969
|
+
for (const id of ids) {
|
|
16970
|
+
const current = await target.getEdge(ctx.graphId, id);
|
|
16971
|
+
if (!current) continue;
|
|
16972
|
+
assertEdgeIdentityMatches(
|
|
16973
|
+
id,
|
|
16974
|
+
{ kind: expectedKind },
|
|
16975
|
+
edgeIdentityFromRow(current),
|
|
16976
|
+
"delete"
|
|
16977
|
+
);
|
|
16978
|
+
if (current.deleted_at) continue;
|
|
16979
|
+
await target.deleteEdge({
|
|
16980
|
+
graphId: ctx.graphId,
|
|
16981
|
+
id,
|
|
16982
|
+
kind: expectedKind
|
|
16983
|
+
});
|
|
16984
|
+
affectedCount += 1;
|
|
16985
|
+
}
|
|
16986
|
+
return affectedCount;
|
|
16987
|
+
},
|
|
16988
|
+
{ didWrite: (affectedCount) => affectedCount > 0 }
|
|
16989
|
+
);
|
|
16990
|
+
}
|
|
16991
|
+
async function executeEdgeHardDelete(ctx, expectedKind, id, backend) {
|
|
16129
16992
|
const gate = await backend.getEdge(ctx.graphId, id);
|
|
16130
16993
|
if (!gate) return;
|
|
16994
|
+
assertEdgeIdentityMatches(
|
|
16995
|
+
id,
|
|
16996
|
+
{ kind: expectedKind },
|
|
16997
|
+
edgeIdentityFromRow(gate),
|
|
16998
|
+
"hardDelete"
|
|
16999
|
+
);
|
|
16131
17000
|
const opContext = ctx.createOperationContext("delete", "edge", gate.kind, id);
|
|
16132
17001
|
return runHookedWriteOperation(ctx, opContext, backend, async (target) => {
|
|
16133
17002
|
await target.hardDeleteEdge({
|
|
16134
17003
|
graphId: ctx.graphId,
|
|
16135
|
-
id
|
|
17004
|
+
id,
|
|
17005
|
+
kind: expectedKind
|
|
16136
17006
|
});
|
|
16137
17007
|
});
|
|
16138
17008
|
}
|
|
16139
|
-
var RECORD_SEP = "";
|
|
16140
|
-
var UNIT_SEP = "";
|
|
16141
17009
|
var UNDEFINED_SENTINEL = "";
|
|
16142
17010
|
function validateMatchOnFields(schema, matchOn, edgeKind) {
|
|
16143
17011
|
if (matchOn.length === 0) return;
|
|
@@ -16155,7 +17023,7 @@ function validateMatchOnFields(schema, matchOn, edgeKind) {
|
|
|
16155
17023
|
}
|
|
16156
17024
|
);
|
|
16157
17025
|
}
|
|
16158
|
-
const invalidFields = matchOn.filter((field2) => !(field2
|
|
17026
|
+
const invalidFields = matchOn.filter((field2) => !hasOwnKey(shape, field2));
|
|
16159
17027
|
if (invalidFields.length > 0) {
|
|
16160
17028
|
throw new ValidationError(
|
|
16161
17029
|
`Invalid matchOn fields for edge kind "${edgeKind}": ${invalidFields.join(", ")}`,
|
|
@@ -16183,16 +17051,20 @@ function stableStringify(value) {
|
|
|
16183
17051
|
return `{${entries.join(",")}}`;
|
|
16184
17052
|
}
|
|
16185
17053
|
function buildEdgeCompositeKey(fromKind, fromId, toKind, toId, props, matchOn) {
|
|
16186
|
-
const endpointPart = `${fromKind}${RECORD_SEP}${fromId}${RECORD_SEP}${toKind}${RECORD_SEP}${toId}`;
|
|
16187
|
-
if (matchOn.length === 0) return endpointPart;
|
|
16188
17054
|
const sortedFields = [...matchOn].toSorted();
|
|
16189
|
-
|
|
16190
|
-
|
|
16191
|
-
|
|
16192
|
-
|
|
17055
|
+
return encodeTupleKey([
|
|
17056
|
+
fromKind,
|
|
17057
|
+
fromId,
|
|
17058
|
+
toKind,
|
|
17059
|
+
toId,
|
|
17060
|
+
...sortedFields.flatMap((field2) => [
|
|
17061
|
+
field2,
|
|
17062
|
+
stableStringify(readOwnProperty(props, field2))
|
|
17063
|
+
])
|
|
17064
|
+
]);
|
|
16193
17065
|
}
|
|
16194
17066
|
function buildEndpointPairKey(fromKind, fromId, toKind, toId) {
|
|
16195
|
-
return
|
|
17067
|
+
return encodeTupleKey([fromKind, fromId, toKind, toId]);
|
|
16196
17068
|
}
|
|
16197
17069
|
function findMatchingEdge(rows, matchOn, inputProps) {
|
|
16198
17070
|
let liveRow;
|
|
@@ -16201,7 +17073,7 @@ function findMatchingEdge(rows, matchOn, inputProps) {
|
|
|
16201
17073
|
if (matchOn.length > 0) {
|
|
16202
17074
|
const rowProps = rowPropsToObject(row.props);
|
|
16203
17075
|
const matches = matchOn.every(
|
|
16204
|
-
(field2) => stableStringify(rowProps
|
|
17076
|
+
(field2) => stableStringify(readOwnProperty(rowProps, field2)) === stableStringify(readOwnProperty(inputProps, field2))
|
|
16205
17077
|
);
|
|
16206
17078
|
if (!matches) continue;
|
|
16207
17079
|
}
|
|
@@ -16255,6 +17127,11 @@ async function executeEdgeGetOrCreateByEndpoints(ctx, kind, fromKind, fromId, to
|
|
|
16255
17127
|
"validFrom"
|
|
16256
17128
|
);
|
|
16257
17129
|
const validTo = validateOptionalCanonicalIsoDate(options?.validTo, "validTo");
|
|
17130
|
+
assertOrderedValidityWindow(
|
|
17131
|
+
`${kind} edge between ${fromKind} "${fromId}" and ${toKind} "${toId}"`,
|
|
17132
|
+
validFrom,
|
|
17133
|
+
validTo
|
|
17134
|
+
);
|
|
16258
17135
|
if (ifExists === "return") {
|
|
16259
17136
|
const probeRows = await backend.findEdgesByKind({
|
|
16260
17137
|
graphId: ctx.graphId,
|
|
@@ -16302,8 +17179,17 @@ async function executeEdgeGetOrCreateByEndpoints(ctx, kind, fromKind, fromId, to
|
|
|
16302
17179
|
...validFrom !== void 0 && { validFrom },
|
|
16303
17180
|
...validTo !== void 0 && { validTo }
|
|
16304
17181
|
};
|
|
16305
|
-
const
|
|
16306
|
-
|
|
17182
|
+
const created = await executeEdgeCreateInternal(ctx, input, backend, {
|
|
17183
|
+
returnRow: true,
|
|
17184
|
+
convergeOn: { matchOn, props: validatedProps }
|
|
17185
|
+
});
|
|
17186
|
+
if (created === void 0) {
|
|
17187
|
+
throw new DatabaseOperationError(
|
|
17188
|
+
"Edge create failed: expected created edge row",
|
|
17189
|
+
{ operation: "insert", entity: "edge" }
|
|
17190
|
+
);
|
|
17191
|
+
}
|
|
17192
|
+
return { edge: created, action: "created" };
|
|
16307
17193
|
}
|
|
16308
17194
|
if (liveRow !== void 0) {
|
|
16309
17195
|
if (ifExists === "return") {
|
|
@@ -16313,7 +17199,9 @@ async function executeEdgeGetOrCreateByEndpoints(ctx, kind, fromKind, fromId, to
|
|
|
16313
17199
|
ctx,
|
|
16314
17200
|
{
|
|
16315
17201
|
id: liveRow.id,
|
|
17202
|
+
identity: { kind, fromKind, fromId, toKind, toId },
|
|
16316
17203
|
props: validatedProps,
|
|
17204
|
+
...validFrom !== void 0 && { validFrom },
|
|
16317
17205
|
...validTo !== void 0 && { validTo }
|
|
16318
17206
|
},
|
|
16319
17207
|
backend
|
|
@@ -16326,39 +17214,50 @@ async function executeEdgeGetOrCreateByEndpoints(ctx, kind, fromKind, fromId, to
|
|
|
16326
17214
|
}
|
|
16327
17215
|
const matchedDeletedRow = deletedRow;
|
|
16328
17216
|
const effectiveValidTo = validTo ?? matchedDeletedRow.valid_to;
|
|
16329
|
-
const constraintContext = {
|
|
16330
|
-
graphId: ctx.graphId,
|
|
16331
|
-
registry: ctx.registry,
|
|
16332
|
-
backend
|
|
16333
|
-
};
|
|
16334
|
-
await checkCardinalityConstraint(
|
|
16335
|
-
constraintContext,
|
|
16336
|
-
kind,
|
|
16337
|
-
cardinality,
|
|
16338
|
-
fromKind,
|
|
16339
|
-
fromId,
|
|
16340
|
-
toKind,
|
|
16341
|
-
toId,
|
|
16342
|
-
effectiveValidTo
|
|
16343
|
-
);
|
|
16344
17217
|
const edge = await executeEdgeUpsertUpdate(
|
|
16345
17218
|
ctx,
|
|
16346
17219
|
{
|
|
16347
17220
|
id: matchedDeletedRow.id,
|
|
17221
|
+
identity: { kind, fromKind, fromId, toKind, toId },
|
|
16348
17222
|
props: validatedProps,
|
|
17223
|
+
...validFrom !== void 0 && { validFrom },
|
|
16349
17224
|
...validTo !== void 0 && { validTo }
|
|
16350
17225
|
},
|
|
16351
17226
|
backend,
|
|
16352
|
-
{
|
|
17227
|
+
{
|
|
17228
|
+
clearDeleted: true,
|
|
17229
|
+
resurrectCardinality: {
|
|
17230
|
+
cardinality,
|
|
17231
|
+
fromKind,
|
|
17232
|
+
fromId,
|
|
17233
|
+
toKind,
|
|
17234
|
+
toId,
|
|
17235
|
+
effectiveValidTo
|
|
17236
|
+
}
|
|
17237
|
+
}
|
|
16353
17238
|
);
|
|
16354
17239
|
return { edge, action: "resurrected" };
|
|
16355
17240
|
}
|
|
16356
|
-
|
|
16357
|
-
|
|
16358
|
-
|
|
16359
|
-
|
|
16360
|
-
|
|
17241
|
+
const ATTEMPT_LIMIT = 3;
|
|
17242
|
+
for (let remaining = ATTEMPT_LIMIT; remaining > 0; remaining -= 1) {
|
|
17243
|
+
try {
|
|
17244
|
+
return await attempt();
|
|
17245
|
+
} catch (error) {
|
|
17246
|
+
const retryable = error instanceof CardinalityError || error instanceof EdgeConvergenceRaced;
|
|
17247
|
+
if (!retryable || remaining === 1) {
|
|
17248
|
+
if (!(error instanceof EdgeConvergenceRaced)) throw error;
|
|
17249
|
+
throw new DatabaseOperationError(
|
|
17250
|
+
`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.`,
|
|
17251
|
+
{ operation: "insert", entity: "edge" },
|
|
17252
|
+
{ cause: error }
|
|
17253
|
+
);
|
|
17254
|
+
}
|
|
17255
|
+
}
|
|
16361
17256
|
}
|
|
17257
|
+
throw new DatabaseOperationError(
|
|
17258
|
+
"getOrCreateByEndpoints convergence loop exited without a verdict",
|
|
17259
|
+
{ operation: "insert", entity: "edge" }
|
|
17260
|
+
);
|
|
16362
17261
|
}
|
|
16363
17262
|
async function executeEdgeBulkGetOrCreateByEndpoints(ctx, kind, items, backend, options) {
|
|
16364
17263
|
if (items.length === 0) return [];
|
|
@@ -16379,6 +17278,11 @@ async function executeEdgeBulkGetOrCreateByEndpoints(ctx, kind, items, backend,
|
|
|
16379
17278
|
"validFrom"
|
|
16380
17279
|
);
|
|
16381
17280
|
const validTo = validateOptionalCanonicalIsoDate(item.validTo, "validTo");
|
|
17281
|
+
assertOrderedValidityWindow(
|
|
17282
|
+
`${kind} edge between ${item.fromKind} "${item.fromId}" and ${item.toKind} "${item.toId}"`,
|
|
17283
|
+
validFrom,
|
|
17284
|
+
validTo
|
|
17285
|
+
);
|
|
16382
17286
|
const compositeKey = buildEdgeCompositeKey(
|
|
16383
17287
|
item.fromKind,
|
|
16384
17288
|
item.fromId,
|
|
@@ -16481,6 +17385,9 @@ async function executeEdgeBulkGetOrCreateByEndpoints(ctx, kind, items, backend,
|
|
|
16481
17385
|
fromId: entry2.fromId,
|
|
16482
17386
|
toKind: entry2.toKind,
|
|
16483
17387
|
toId: entry2.toId,
|
|
17388
|
+
...entry2.validFrom !== void 0 && {
|
|
17389
|
+
validFrom: entry2.validFrom
|
|
17390
|
+
},
|
|
16484
17391
|
...entry2.validTo !== void 0 && { validTo: entry2.validTo }
|
|
16485
17392
|
});
|
|
16486
17393
|
}
|
|
@@ -16504,75 +17411,108 @@ async function executeEdgeBulkGetOrCreateByEndpoints(ctx, kind, items, backend,
|
|
|
16504
17411
|
return found;
|
|
16505
17412
|
}
|
|
16506
17413
|
}
|
|
16507
|
-
|
|
16508
|
-
|
|
16509
|
-
|
|
16510
|
-
|
|
16511
|
-
|
|
16512
|
-
|
|
16513
|
-
|
|
16514
|
-
const createdEdges = await executeEdgeCreateBatch(
|
|
16515
|
-
ctx,
|
|
16516
|
-
createInputs,
|
|
16517
|
-
target
|
|
16518
|
-
);
|
|
16519
|
-
for (const [batchIndex, entry2] of toCreate.entries()) {
|
|
16520
|
-
results[entry2.index] = {
|
|
16521
|
-
edge: requireDefined(createdEdges[batchIndex]),
|
|
16522
|
-
action: "created"
|
|
16523
|
-
};
|
|
16524
|
-
}
|
|
16525
|
-
}
|
|
16526
|
-
for (const entry2 of toFetch) {
|
|
16527
|
-
if (entry2.isDeleted) {
|
|
16528
|
-
const effectiveValidTo = entry2.validTo ?? entry2.row.valid_to;
|
|
16529
|
-
const constraintContext = {
|
|
16530
|
-
graphId: ctx.graphId,
|
|
16531
|
-
registry: ctx.registry,
|
|
16532
|
-
backend: target
|
|
16533
|
-
};
|
|
16534
|
-
await checkCardinalityConstraint(
|
|
16535
|
-
constraintContext,
|
|
16536
|
-
kind,
|
|
16537
|
-
cardinality,
|
|
16538
|
-
entry2.fromKind,
|
|
16539
|
-
entry2.fromId,
|
|
16540
|
-
entry2.toKind,
|
|
16541
|
-
entry2.toId,
|
|
16542
|
-
effectiveValidTo
|
|
16543
|
-
);
|
|
16544
|
-
const edge = await executeEdgeUpsertUpdate(
|
|
16545
|
-
ctx,
|
|
16546
|
-
{
|
|
16547
|
-
id: entry2.row.id,
|
|
16548
|
-
props: entry2.validatedProps,
|
|
16549
|
-
...entry2.validTo !== void 0 && { validTo: entry2.validTo }
|
|
16550
|
-
},
|
|
16551
|
-
target,
|
|
16552
|
-
{ clearDeleted: true }
|
|
16553
|
-
);
|
|
16554
|
-
results[entry2.index] = { edge, action: "resurrected" };
|
|
16555
|
-
} else if (ifExists === "update") {
|
|
16556
|
-
const edge = await executeEdgeUpsertUpdate(
|
|
16557
|
-
ctx,
|
|
16558
|
-
{
|
|
16559
|
-
id: entry2.row.id,
|
|
16560
|
-
props: entry2.validatedProps,
|
|
16561
|
-
...entry2.validTo !== void 0 && { validTo: entry2.validTo }
|
|
16562
|
-
},
|
|
16563
|
-
target
|
|
17414
|
+
function runBatch() {
|
|
17415
|
+
return runInWriteTransaction(
|
|
17416
|
+
ctx,
|
|
17417
|
+
backend,
|
|
17418
|
+
async (target) => {
|
|
17419
|
+
const { toCreate, toFetch, duplicateOf } = partitionEntries(
|
|
17420
|
+
await fetchRowsByEndpoint(target)
|
|
16564
17421
|
);
|
|
16565
|
-
results
|
|
16566
|
-
|
|
16567
|
-
|
|
16568
|
-
|
|
16569
|
-
|
|
16570
|
-
|
|
16571
|
-
|
|
16572
|
-
|
|
16573
|
-
|
|
16574
|
-
|
|
16575
|
-
|
|
17422
|
+
const results = Array.from({ length: items.length });
|
|
17423
|
+
if (toCreate.length > 0) {
|
|
17424
|
+
const createInputs = toCreate.map((entry2) => entry2.input);
|
|
17425
|
+
const createdEdges = await executeEdgeCreateBatch(
|
|
17426
|
+
ctx,
|
|
17427
|
+
createInputs,
|
|
17428
|
+
target
|
|
17429
|
+
);
|
|
17430
|
+
for (const [batchIndex, entry2] of toCreate.entries()) {
|
|
17431
|
+
results[entry2.index] = {
|
|
17432
|
+
edge: requireDefined(createdEdges[batchIndex]),
|
|
17433
|
+
action: "created"
|
|
17434
|
+
};
|
|
17435
|
+
}
|
|
17436
|
+
}
|
|
17437
|
+
for (const entry2 of toFetch) {
|
|
17438
|
+
if (entry2.isDeleted) {
|
|
17439
|
+
const edge = await executeEdgeUpsertUpdate(
|
|
17440
|
+
ctx,
|
|
17441
|
+
{
|
|
17442
|
+
id: entry2.row.id,
|
|
17443
|
+
identity: {
|
|
17444
|
+
kind,
|
|
17445
|
+
fromKind: entry2.fromKind,
|
|
17446
|
+
fromId: entry2.fromId,
|
|
17447
|
+
toKind: entry2.toKind,
|
|
17448
|
+
toId: entry2.toId
|
|
17449
|
+
},
|
|
17450
|
+
props: entry2.validatedProps,
|
|
17451
|
+
...entry2.validFrom !== void 0 && {
|
|
17452
|
+
validFrom: entry2.validFrom
|
|
17453
|
+
},
|
|
17454
|
+
...entry2.validTo !== void 0 && { validTo: entry2.validTo }
|
|
17455
|
+
},
|
|
17456
|
+
target,
|
|
17457
|
+
{
|
|
17458
|
+
clearDeleted: true,
|
|
17459
|
+
resurrectCardinality: {
|
|
17460
|
+
cardinality,
|
|
17461
|
+
fromKind: entry2.fromKind,
|
|
17462
|
+
fromId: entry2.fromId,
|
|
17463
|
+
toKind: entry2.toKind,
|
|
17464
|
+
toId: entry2.toId,
|
|
17465
|
+
effectiveValidTo: entry2.validTo ?? entry2.row.valid_to
|
|
17466
|
+
}
|
|
17467
|
+
}
|
|
17468
|
+
);
|
|
17469
|
+
results[entry2.index] = { edge, action: "resurrected" };
|
|
17470
|
+
} else if (ifExists === "update") {
|
|
17471
|
+
const edge = await executeEdgeUpsertUpdate(
|
|
17472
|
+
ctx,
|
|
17473
|
+
{
|
|
17474
|
+
id: entry2.row.id,
|
|
17475
|
+
identity: {
|
|
17476
|
+
kind,
|
|
17477
|
+
fromKind: entry2.fromKind,
|
|
17478
|
+
fromId: entry2.fromId,
|
|
17479
|
+
toKind: entry2.toKind,
|
|
17480
|
+
toId: entry2.toId
|
|
17481
|
+
},
|
|
17482
|
+
props: entry2.validatedProps,
|
|
17483
|
+
...entry2.validFrom !== void 0 && {
|
|
17484
|
+
validFrom: entry2.validFrom
|
|
17485
|
+
},
|
|
17486
|
+
...entry2.validTo !== void 0 && { validTo: entry2.validTo }
|
|
17487
|
+
},
|
|
17488
|
+
target
|
|
17489
|
+
);
|
|
17490
|
+
results[entry2.index] = { edge, action: "updated" };
|
|
17491
|
+
} else {
|
|
17492
|
+
results[entry2.index] = {
|
|
17493
|
+
edge: rowToEdge(entry2.row),
|
|
17494
|
+
action: "found"
|
|
17495
|
+
};
|
|
17496
|
+
}
|
|
17497
|
+
}
|
|
17498
|
+
for (const { index, sourceIndex } of duplicateOf) {
|
|
17499
|
+
const sourceResult = requireDefined(results[sourceIndex]);
|
|
17500
|
+
results[index] = { edge: sourceResult.edge, action: "found" };
|
|
17501
|
+
}
|
|
17502
|
+
return results;
|
|
17503
|
+
},
|
|
17504
|
+
// A bulk getOrCreate always converges on a match key no database key
|
|
17505
|
+
// backs, whatever its cardinality — the same reason the single-item path
|
|
17506
|
+
// fences unconditionally.
|
|
17507
|
+
{ fencesConstraintProbe: "edgeMatchKeyConvergence" }
|
|
17508
|
+
);
|
|
17509
|
+
}
|
|
17510
|
+
try {
|
|
17511
|
+
return await runBatch();
|
|
17512
|
+
} catch (error) {
|
|
17513
|
+
if (!(error instanceof CardinalityError)) throw error;
|
|
17514
|
+
return runBatch();
|
|
17515
|
+
}
|
|
16576
17516
|
}
|
|
16577
17517
|
|
|
16578
17518
|
// src/store/recorded-read-service.ts
|
|
@@ -16906,6 +17846,12 @@ function createTransactionReceiptRecorder() {
|
|
|
16906
17846
|
const counters = {
|
|
16907
17847
|
nodes: createCountBucket(),
|
|
16908
17848
|
edges: createCountBucket(),
|
|
17849
|
+
identity: {
|
|
17850
|
+
sameAssertions: 0,
|
|
17851
|
+
differentAssertions: 0,
|
|
17852
|
+
retractions: 0,
|
|
17853
|
+
total: 0
|
|
17854
|
+
},
|
|
16909
17855
|
total: 0
|
|
16910
17856
|
};
|
|
16911
17857
|
let sealed = false;
|
|
@@ -16918,15 +17864,17 @@ function createTransactionReceiptRecorder() {
|
|
|
16918
17864
|
increment(counters.edges, kind, count2);
|
|
16919
17865
|
counters.total += count2;
|
|
16920
17866
|
},
|
|
17867
|
+
recordIdentity(kind, count2) {
|
|
17868
|
+
counters.identity[kind] += count2;
|
|
17869
|
+
counters.identity.total += count2;
|
|
17870
|
+
counters.total += count2;
|
|
17871
|
+
},
|
|
16921
17872
|
snapshot(recorded) {
|
|
16922
17873
|
return Object.freeze({
|
|
16923
17874
|
writes: Object.freeze({
|
|
16924
|
-
nodes: Object.freeze(
|
|
16925
|
-
|
|
16926
|
-
),
|
|
16927
|
-
edges: Object.freeze(
|
|
16928
|
-
Object.assign(createCountBucket(), counters.edges)
|
|
16929
|
-
),
|
|
17875
|
+
nodes: Object.freeze({ ...counters.nodes }),
|
|
17876
|
+
edges: Object.freeze({ ...counters.edges }),
|
|
17877
|
+
identity: Object.freeze({ ...counters.identity }),
|
|
16930
17878
|
total: counters.total
|
|
16931
17879
|
}),
|
|
16932
17880
|
...recorded === void 0 ? {} : { recorded }
|
|
@@ -16948,6 +17896,67 @@ function createTransactionReceiptRecorder() {
|
|
|
16948
17896
|
}
|
|
16949
17897
|
};
|
|
16950
17898
|
}
|
|
17899
|
+
function wrapTransactionIdentity(identity, recorder) {
|
|
17900
|
+
return {
|
|
17901
|
+
representativeOf: (ref) => identity.representativeOf(ref),
|
|
17902
|
+
membersOf: (ref) => identity.membersOf(ref),
|
|
17903
|
+
nodesOf: (ref) => identity.nodesOf(ref),
|
|
17904
|
+
areSame: (a, b) => identity.areSame(a, b),
|
|
17905
|
+
areDifferent: (a, b) => identity.areDifferent(a, b),
|
|
17906
|
+
assertionsOf: (ref) => identity.assertionsOf(ref),
|
|
17907
|
+
async assertSame(a, b) {
|
|
17908
|
+
recorder.assertWritable();
|
|
17909
|
+
const result = await identity.assertSame(a, b);
|
|
17910
|
+
recorder.recordIdentity("sameAssertions", 1);
|
|
17911
|
+
return result;
|
|
17912
|
+
},
|
|
17913
|
+
async assertDifferent(a, b) {
|
|
17914
|
+
recorder.assertWritable();
|
|
17915
|
+
const result = await identity.assertDifferent(a, b);
|
|
17916
|
+
recorder.recordIdentity("differentAssertions", 1);
|
|
17917
|
+
return result;
|
|
17918
|
+
},
|
|
17919
|
+
async bulkAssertSame(pairs) {
|
|
17920
|
+
recorder.assertWritable();
|
|
17921
|
+
const count2 = pairs.length;
|
|
17922
|
+
const result = await identity.bulkAssertSame(pairs);
|
|
17923
|
+
recorder.recordIdentity("sameAssertions", count2);
|
|
17924
|
+
return result;
|
|
17925
|
+
},
|
|
17926
|
+
async bulkAssertDifferent(pairs) {
|
|
17927
|
+
recorder.assertWritable();
|
|
17928
|
+
const count2 = pairs.length;
|
|
17929
|
+
const result = await identity.bulkAssertDifferent(pairs);
|
|
17930
|
+
recorder.recordIdentity("differentAssertions", count2);
|
|
17931
|
+
return result;
|
|
17932
|
+
},
|
|
17933
|
+
async retractAssertion(id) {
|
|
17934
|
+
recorder.assertWritable();
|
|
17935
|
+
const result = await identity.retractAssertion(id);
|
|
17936
|
+
recorder.recordIdentity("retractions", 1);
|
|
17937
|
+
return result;
|
|
17938
|
+
},
|
|
17939
|
+
async retractSameAssertion(a, b) {
|
|
17940
|
+
recorder.assertWritable();
|
|
17941
|
+
const result = await identity.retractSameAssertion(a, b);
|
|
17942
|
+
recorder.recordIdentity("retractions", 1);
|
|
17943
|
+
return result;
|
|
17944
|
+
},
|
|
17945
|
+
async retractDifferentAssertion(a, b) {
|
|
17946
|
+
recorder.assertWritable();
|
|
17947
|
+
const result = await identity.retractDifferentAssertion(a, b);
|
|
17948
|
+
recorder.recordIdentity("retractions", 1);
|
|
17949
|
+
return result;
|
|
17950
|
+
},
|
|
17951
|
+
async bulkRetractAssertions(ids) {
|
|
17952
|
+
recorder.assertWritable();
|
|
17953
|
+
const count2 = ids.length;
|
|
17954
|
+
const result = await identity.bulkRetractAssertions(ids);
|
|
17955
|
+
recorder.recordIdentity("retractions", count2);
|
|
17956
|
+
return result;
|
|
17957
|
+
}
|
|
17958
|
+
};
|
|
17959
|
+
}
|
|
16951
17960
|
function isWrappedMethod(value) {
|
|
16952
17961
|
return typeof value === "function";
|
|
16953
17962
|
}
|
|
@@ -17056,6 +18065,10 @@ var CAUGHT_UP_VERB_DETAILS = {
|
|
|
17056
18065
|
repair: {
|
|
17057
18066
|
phrase: "repair contributions for",
|
|
17058
18067
|
code: "REPAIR_CONTRIBUTIONS_BEFORE_INITIALIZE"
|
|
18068
|
+
},
|
|
18069
|
+
rebuild: {
|
|
18070
|
+
phrase: "rebuild a contribution for",
|
|
18071
|
+
code: "REBUILD_CONTRIBUTION_BEFORE_INITIALIZE"
|
|
17059
18072
|
}
|
|
17060
18073
|
};
|
|
17061
18074
|
var ROW_MAPPER_CONFIGS = {
|
|
@@ -17128,6 +18141,20 @@ async function commitEvolvedSchemaWhenRequiredKindsAreEmpty(backend, graph, curr
|
|
|
17128
18141
|
}
|
|
17129
18142
|
);
|
|
17130
18143
|
}
|
|
18144
|
+
async function assertEvolvedSchemaRequiredKindsEmpty(backend, graphId, classification) {
|
|
18145
|
+
const counts = await Promise.all(
|
|
18146
|
+
classification.requireEmpty.map(async (entry2) => ({
|
|
18147
|
+
entry: entry2,
|
|
18148
|
+
count: entry2.entity === "node" ? await backend.countNodesByKind({ graphId, kind: entry2.kindName }) : await backend.countEdgesByKind({ graphId, kind: entry2.kindName })
|
|
18149
|
+
}))
|
|
18150
|
+
);
|
|
18151
|
+
const nonEmpty = new Set(
|
|
18152
|
+
counts.filter(({ count: count2 }) => count2 > 0).map(({ entry: entry2 }) => entry2)
|
|
18153
|
+
);
|
|
18154
|
+
const error = buildIncompatibleChangeError(classification, nonEmpty, graphId);
|
|
18155
|
+
if (error !== void 0) throw error;
|
|
18156
|
+
}
|
|
18157
|
+
var IDENTITY_FACADES = /* @__PURE__ */ new WeakMap();
|
|
17131
18158
|
var StoreImplementation = class _StoreImplementation {
|
|
17132
18159
|
[STORE_RUNTIME];
|
|
17133
18160
|
#graph;
|
|
@@ -17159,6 +18186,19 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
17159
18186
|
#search;
|
|
17160
18187
|
constructor(graph, backend, options, schemaMetadata, adapterBackend) {
|
|
17161
18188
|
this.#graph = graph;
|
|
18189
|
+
if (graph.identity !== void 0 && (!backend.capabilities.transactions || backend.executeStatement === void 0)) {
|
|
18190
|
+
throw new ConfigurationError(
|
|
18191
|
+
"Operational Identity requires an atomic transactional backend with statement execution support.",
|
|
18192
|
+
{
|
|
18193
|
+
code: "IDENTITY_REQUIRES_ATOMIC_BACKEND",
|
|
18194
|
+
transactions: backend.capabilities.transactions,
|
|
18195
|
+
statementExecution: backend.executeStatement !== void 0
|
|
18196
|
+
},
|
|
18197
|
+
{
|
|
18198
|
+
suggestion: "Use a transactional SQLite or PostgreSQL driver; Cloudflare D1 and neon-http cannot host identity closure maintenance."
|
|
18199
|
+
}
|
|
18200
|
+
);
|
|
18201
|
+
}
|
|
17162
18202
|
this.#baseBackend = asRawBackend(backend);
|
|
17163
18203
|
this.#adapterBackend = adapterBackend;
|
|
17164
18204
|
this.#captureEnabled = options?.history === true;
|
|
@@ -17207,7 +18247,56 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
17207
18247
|
recordedEdgeGetByIds: (kind, ids, coordinate) => this.recordedEdgeGetByIds(kind, ids, coordinate),
|
|
17208
18248
|
recordedEdgeScan: (kind, coordinate, options2) => this.recordedEdgeScan(kind, coordinate, options2),
|
|
17209
18249
|
subgraphAtCoordinate: (rootId, subgraphOptions) => this.subgraphAtCoordinate(rootId, subgraphOptions),
|
|
17210
|
-
algorithmsAtCoordinate: (coordinate) => this.algorithmsAtCoordinate(coordinate)
|
|
18250
|
+
algorithmsAtCoordinate: (coordinate) => this.algorithmsAtCoordinate(coordinate),
|
|
18251
|
+
identityAtCoordinate: (coordinate) => this.identityAtCoordinate(coordinate),
|
|
18252
|
+
rebuildIdentityClosure: () => this.rebuildIdentityClosure(),
|
|
18253
|
+
validateIdentity: () => this.validateIdentity(),
|
|
18254
|
+
liveNodesSharingIds: async (ids, target) => {
|
|
18255
|
+
const backend2 = target ?? this.#baseBackend;
|
|
18256
|
+
const liveKindsById = await liveNodeKindsSharingIds(
|
|
18257
|
+
this.#identityContext(backend2),
|
|
18258
|
+
backend2,
|
|
18259
|
+
ids
|
|
18260
|
+
);
|
|
18261
|
+
const peers = [];
|
|
18262
|
+
for (const [id, kinds] of liveKindsById) {
|
|
18263
|
+
for (const kind of kinds) peers.push({ kind, id });
|
|
18264
|
+
}
|
|
18265
|
+
return peers;
|
|
18266
|
+
},
|
|
18267
|
+
identityAssertionRowsByIds: async (ids, target) => {
|
|
18268
|
+
if (this.#graph.identity === void 0) return /* @__PURE__ */ new Map();
|
|
18269
|
+
const backend2 = target ?? this.#baseBackend;
|
|
18270
|
+
const ctx = this.#identityContext(backend2);
|
|
18271
|
+
const rows = await loadAssertionsByIds(
|
|
18272
|
+
backend2,
|
|
18273
|
+
ctx.schema,
|
|
18274
|
+
this.graphId,
|
|
18275
|
+
ids
|
|
18276
|
+
);
|
|
18277
|
+
return new Map(
|
|
18278
|
+
[...rows].map(([id, row]) => [id, toTransferAssertion(row)])
|
|
18279
|
+
);
|
|
18280
|
+
},
|
|
18281
|
+
structuralIdentityClasses: async (references, target) => {
|
|
18282
|
+
if (this.#graph.identity === void 0) return /* @__PURE__ */ new Map();
|
|
18283
|
+
const backend2 = target ?? this.#baseBackend;
|
|
18284
|
+
const ctx = this.#identityContext(backend2);
|
|
18285
|
+
return loadCurrentStructuralClasses(
|
|
18286
|
+
backend2,
|
|
18287
|
+
ctx.schema,
|
|
18288
|
+
this.graphId,
|
|
18289
|
+
references
|
|
18290
|
+
);
|
|
18291
|
+
},
|
|
18292
|
+
readCurrentIdentityAssertions: (mode, options2) => this.readCurrentIdentityAssertions(mode, options2),
|
|
18293
|
+
identityAssertionsAtTarget: (target, mode) => this.identityAssertionsAtTarget(target, mode),
|
|
18294
|
+
readIdentityAssertionPageAtTarget: (target, mode, options2) => this.readIdentityAssertionPageAtTarget(target, mode, options2),
|
|
18295
|
+
lockIdentityImportTarget: (target) => this.lockIdentityImportTarget(target),
|
|
18296
|
+
foldImportedIdentityNodes: (target, references) => this.foldImportedIdentityNodes(target, references),
|
|
18297
|
+
importIdentityAssertionsAtTarget: (target, assertions, mode) => this.importIdentityAssertionsAtTarget(target, assertions, mode),
|
|
18298
|
+
applyIdentityMergeAtTarget: (target, retractionIds, assertions) => this.applyIdentityMergeAtTarget(target, retractionIds, assertions),
|
|
18299
|
+
assertIdentityClassesConsistentAtTarget: (target, seeds) => this.assertIdentityClassesConsistentAtTarget(target, seeds)
|
|
17211
18300
|
};
|
|
17212
18301
|
Object.defineProperty(this, STORE_RUNTIME, {
|
|
17213
18302
|
configurable: false,
|
|
@@ -17232,6 +18321,215 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
17232
18321
|
get registry() {
|
|
17233
18322
|
return this.#registry;
|
|
17234
18323
|
}
|
|
18324
|
+
/**
|
|
18325
|
+
* The TypeGraph Identity Profile facade. Reachable through the public
|
|
18326
|
+
* {@link Store} surface only on graphs that declared `identity: { ... }`;
|
|
18327
|
+
* the runtime guard below catches widened or JavaScript callers.
|
|
18328
|
+
*
|
|
18329
|
+
* The facade is memoized per store, so it must not close over any state a
|
|
18330
|
+
* lifecycle operation can move — see `#identityContext`.
|
|
18331
|
+
*/
|
|
18332
|
+
get identity() {
|
|
18333
|
+
this.#requireIdentityEnabled(
|
|
18334
|
+
'Add identity: { sameIdAcrossKinds: "fold" } to defineGraph(...).'
|
|
18335
|
+
);
|
|
18336
|
+
const existing = IDENTITY_FACADES.get(this);
|
|
18337
|
+
if (existing !== void 0) return existing;
|
|
18338
|
+
const facade = createIdentityFacade(this.#identityContext(this.#backend));
|
|
18339
|
+
IDENTITY_FACADES.set(this, facade);
|
|
18340
|
+
return facade;
|
|
18341
|
+
}
|
|
18342
|
+
/**
|
|
18343
|
+
* Refuses an identity operation on a graph that never declared
|
|
18344
|
+
* `identity: { ... }`, where none of the identity tables exist.
|
|
18345
|
+
*/
|
|
18346
|
+
#requireIdentityEnabled(suggestion) {
|
|
18347
|
+
if (this.#graph.identity !== void 0) return;
|
|
18348
|
+
throw new ConfigurationError(
|
|
18349
|
+
"Identity is not enabled for this graph.",
|
|
18350
|
+
{ code: "IDENTITY_NOT_ENABLED", graphId: this.graphId },
|
|
18351
|
+
suggestion === void 0 ? void 0 : { suggestion }
|
|
18352
|
+
);
|
|
18353
|
+
}
|
|
18354
|
+
/** @internal Builds the identity read facade for a pinned StoreView. */
|
|
18355
|
+
identityAtCoordinate(coordinate) {
|
|
18356
|
+
this.#requireIdentityEnabled();
|
|
18357
|
+
const recordedAsOf = coordinate.recorded?.asOf;
|
|
18358
|
+
const backend = recordedAsOf === void 0 ? this.#backend : this.#recordedReads.backendForCoordinate(
|
|
18359
|
+
coordinate,
|
|
18360
|
+
"recorded-identity"
|
|
18361
|
+
);
|
|
18362
|
+
const schema = recordedReadSchemaFor(
|
|
18363
|
+
this.#sqlSchema(),
|
|
18364
|
+
recordedAsOf,
|
|
18365
|
+
this.#recordedReadBinding,
|
|
18366
|
+
"recorded-identity"
|
|
18367
|
+
);
|
|
18368
|
+
return createIdentityReadFacade({
|
|
18369
|
+
...this.#identityContext(backend),
|
|
18370
|
+
schema,
|
|
18371
|
+
coordinate
|
|
18372
|
+
});
|
|
18373
|
+
}
|
|
18374
|
+
/** Rebuilds derived current identity closure without advancing revision. */
|
|
18375
|
+
async rebuildIdentityClosure() {
|
|
18376
|
+
this.#requireIdentityEnabled();
|
|
18377
|
+
await runInWriteTransaction(
|
|
18378
|
+
{
|
|
18379
|
+
graphId: this.graphId,
|
|
18380
|
+
schemaVersion: this.#schemaMetadata.schemaVersion,
|
|
18381
|
+
historyEnabled: this.#captureEnabled,
|
|
18382
|
+
revisionTrackingEnabled: this.#revisionTrackingEnabled,
|
|
18383
|
+
revisionSchema: this.#sqlSchema()
|
|
18384
|
+
},
|
|
18385
|
+
this.#baseBackend,
|
|
18386
|
+
async (target) => rebuildIdentityClosureForContext(this.#identityContext(target)),
|
|
18387
|
+
{ didWrite: () => false }
|
|
18388
|
+
);
|
|
18389
|
+
}
|
|
18390
|
+
/**
|
|
18391
|
+
* @internal Validates/rebuilds identity under schema-transition locks.
|
|
18392
|
+
*
|
|
18393
|
+
* `provisionDerivedRelations` carries the DDL `ensureIdentitySchemaStorage`
|
|
18394
|
+
* deliberately did not run outside this transaction, so an evolution that
|
|
18395
|
+
* fails leaves no half-published derived relation behind.
|
|
18396
|
+
*/
|
|
18397
|
+
async identitySchemaPreflight(target, provisionDerivedRelations) {
|
|
18398
|
+
await identitySchemaCommitPreflight(this.#identityContext(this.#backend), {
|
|
18399
|
+
enablement: false,
|
|
18400
|
+
provisionDerivedRelations
|
|
18401
|
+
})(target);
|
|
18402
|
+
}
|
|
18403
|
+
/** @internal Read-only startup integrity verification. */
|
|
18404
|
+
async validateIdentity() {
|
|
18405
|
+
if (this.#graph.identity === void 0) return;
|
|
18406
|
+
await validateIdentityForContext(this.#identityContext(this.#baseBackend));
|
|
18407
|
+
}
|
|
18408
|
+
/** @internal Cascades removed kinds in the schema-commit transaction. */
|
|
18409
|
+
async removeIdentityKindsInSchemaPreflight(target, kinds, options) {
|
|
18410
|
+
if (!this.#captureEnabled) {
|
|
18411
|
+
await removeIdentityKindsForContext(
|
|
18412
|
+
this.#identityContext(target),
|
|
18413
|
+
kinds,
|
|
18414
|
+
options
|
|
18415
|
+
);
|
|
18416
|
+
return;
|
|
18417
|
+
}
|
|
18418
|
+
const scope = createRecordedTransactionScope(
|
|
18419
|
+
target,
|
|
18420
|
+
this.#sqlSchema(),
|
|
18421
|
+
target.dialect === "sqlite"
|
|
18422
|
+
);
|
|
18423
|
+
await removeIdentityKindsForContext(
|
|
18424
|
+
this.#identityContext(scope.backend),
|
|
18425
|
+
kinds,
|
|
18426
|
+
options
|
|
18427
|
+
);
|
|
18428
|
+
await scope.flush();
|
|
18429
|
+
}
|
|
18430
|
+
/**
|
|
18431
|
+
* @internal Reads the graph's identity assertions in transfer shape, honoring
|
|
18432
|
+
* this store's SQL binding. Used by interchange export, base-version
|
|
18433
|
+
* fingerprinting, and merge staging/diff.
|
|
18434
|
+
*/
|
|
18435
|
+
readCurrentIdentityAssertions(mode, options) {
|
|
18436
|
+
if (this.#graph.identity === void 0) return Promise.resolve([]);
|
|
18437
|
+
return readIdentityAssertionsForInterchange(
|
|
18438
|
+
this.#identityContext(this.#baseBackend),
|
|
18439
|
+
mode,
|
|
18440
|
+
options
|
|
18441
|
+
);
|
|
18442
|
+
}
|
|
18443
|
+
/** @internal Reads identity truth through an already-bound transaction. */
|
|
18444
|
+
identityAssertionsAtTarget(target, mode = "state") {
|
|
18445
|
+
if (this.#graph.identity === void 0) return Promise.resolve([]);
|
|
18446
|
+
return readIdentityAssertionsForInterchange(
|
|
18447
|
+
this.#identityContext(target),
|
|
18448
|
+
mode
|
|
18449
|
+
);
|
|
18450
|
+
}
|
|
18451
|
+
/** @internal Reads one bounded identity-assertion page at a bound target. */
|
|
18452
|
+
readIdentityAssertionPageAtTarget(target, mode, options) {
|
|
18453
|
+
if (this.#graph.identity === void 0) {
|
|
18454
|
+
return Promise.resolve({ assertions: [], done: true });
|
|
18455
|
+
}
|
|
18456
|
+
return readIdentityAssertionPageAtTarget(
|
|
18457
|
+
this.#identityContext(target),
|
|
18458
|
+
target,
|
|
18459
|
+
mode,
|
|
18460
|
+
options
|
|
18461
|
+
);
|
|
18462
|
+
}
|
|
18463
|
+
/** @internal Acquires the enabled graph's identity lock for an import. */
|
|
18464
|
+
lockIdentityImportTarget(target) {
|
|
18465
|
+
return this.#graph.identity === void 0 ? Promise.resolve() : lockIdentityGraph(target, this.graphId);
|
|
18466
|
+
}
|
|
18467
|
+
/** @internal Restores same-id folding after the ops-layer import bypass. */
|
|
18468
|
+
foldImportedIdentityNodes(target, references) {
|
|
18469
|
+
if (this.#graph.identity === void 0 || references.length === 0) {
|
|
18470
|
+
return Promise.resolve();
|
|
18471
|
+
}
|
|
18472
|
+
return foldIdentityForCreatedNodes(
|
|
18473
|
+
{
|
|
18474
|
+
graphId: this.graphId,
|
|
18475
|
+
registry: this.#registry,
|
|
18476
|
+
sameIdAcrossKinds: this.#graph.identity.sameIdAcrossKinds,
|
|
18477
|
+
schema: this.#sqlSchema()
|
|
18478
|
+
},
|
|
18479
|
+
target,
|
|
18480
|
+
references
|
|
18481
|
+
);
|
|
18482
|
+
}
|
|
18483
|
+
/** @internal Applies identity interchange rows inside an import transaction. */
|
|
18484
|
+
importIdentityAssertionsAtTarget(target, assertions, mode) {
|
|
18485
|
+
if (assertions.length === 0) {
|
|
18486
|
+
return Promise.resolve({ created: 0, skipped: 0 });
|
|
18487
|
+
}
|
|
18488
|
+
if (this.#graph.identity === void 0) {
|
|
18489
|
+
throw new ConfigurationError(
|
|
18490
|
+
"Cannot import identity assertions into an identity-disabled graph.",
|
|
18491
|
+
{ code: "IDENTITY_IMPORT_REQUIRES_PROFILE", graphId: this.graphId }
|
|
18492
|
+
);
|
|
18493
|
+
}
|
|
18494
|
+
return importIdentityAssertionsIntoTarget(
|
|
18495
|
+
this.#identityContext(target),
|
|
18496
|
+
target,
|
|
18497
|
+
assertions,
|
|
18498
|
+
mode
|
|
18499
|
+
);
|
|
18500
|
+
}
|
|
18501
|
+
/**
|
|
18502
|
+
* @internal Post-write identity validation for a graph-merge commit: proves
|
|
18503
|
+
* the identity classes the merge touched carry no contradiction in the state
|
|
18504
|
+
* the merge just wrote, inside the merge's own transaction.
|
|
18505
|
+
*/
|
|
18506
|
+
assertIdentityClassesConsistentAtTarget(target, seeds) {
|
|
18507
|
+
if (this.#graph.identity === void 0 || seeds.length === 0) {
|
|
18508
|
+
return Promise.resolve();
|
|
18509
|
+
}
|
|
18510
|
+
return assertAffectedIdentityClassesConsistent(
|
|
18511
|
+
this.#identityContext(target),
|
|
18512
|
+
target,
|
|
18513
|
+
seeds
|
|
18514
|
+
);
|
|
18515
|
+
}
|
|
18516
|
+
/** @internal Mechanical graph-merge apply through the mutation coordinator. */
|
|
18517
|
+
applyIdentityMergeAtTarget(target, retractionIds, assertions) {
|
|
18518
|
+
if (retractionIds.length === 0 && assertions.length === 0) {
|
|
18519
|
+
return Promise.resolve({ created: 0, retracted: 0 });
|
|
18520
|
+
}
|
|
18521
|
+
if (this.#graph.identity === void 0) {
|
|
18522
|
+
throw new ConfigurationError(
|
|
18523
|
+
"Cannot apply identity merge changes to an identity-disabled graph.",
|
|
18524
|
+
{ code: "IDENTITY_MERGE_REQUIRES_PROFILE", graphId: this.graphId }
|
|
18525
|
+
);
|
|
18526
|
+
}
|
|
18527
|
+
return applyIdentityChangesForContext(
|
|
18528
|
+
this.#identityContext(target),
|
|
18529
|
+
retractionIds,
|
|
18530
|
+
assertions
|
|
18531
|
+
);
|
|
18532
|
+
}
|
|
17235
18533
|
/**
|
|
17236
18534
|
* An opaque, in-memory snapshot of this store's reconciled schema — the
|
|
17237
18535
|
* merged compile-time + runtime-committed graph plus the committed version
|
|
@@ -17604,6 +18902,7 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
17604
18902
|
upsertDirtyCheck: (kind, id, existingProps, inputProps) => nodeUpsertDirtyCheck(ctx, kind, id, existingProps, inputProps)
|
|
17605
18903
|
},
|
|
17606
18904
|
executeDelete: (kind, id, backend) => executeNodeDelete(ctx, kind, id, backend),
|
|
18905
|
+
executeDeleteBatch: (kind, ids, backend) => executeNodeDeleteBatch(ctx, kind, ids, backend),
|
|
17607
18906
|
executeHardDelete: (kind, id, backend) => executeNodeHardDelete(ctx, kind, id, backend),
|
|
17608
18907
|
temporalRowMatcher: (options) => this.#temporalRowMatcher(options),
|
|
17609
18908
|
createQuery: () => this.query(),
|
|
@@ -17661,8 +18960,9 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
17661
18960
|
...this.#options?.coalesceUnchangedUpserts === true && {
|
|
17662
18961
|
upsertDirtyCheck: (kind, id, existingProps, inputProps) => edgeUpsertDirtyCheck(ctx, kind, id, existingProps, inputProps)
|
|
17663
18962
|
},
|
|
17664
|
-
executeDelete: (id, backend) => executeEdgeDelete(ctx, id, backend),
|
|
17665
|
-
|
|
18963
|
+
executeDelete: (kind, id, backend) => executeEdgeDelete(ctx, kind, id, backend),
|
|
18964
|
+
executeDeleteBatch: (kind, ids, backend) => executeEdgeDeleteBatch(ctx, kind, ids, backend),
|
|
18965
|
+
executeHardDelete: (kind, id, backend) => executeEdgeHardDelete(ctx, kind, id, backend),
|
|
17666
18966
|
temporalRowMatcher: (options) => this.#temporalRowMatcher(options),
|
|
17667
18967
|
createQuery: () => this.query(),
|
|
17668
18968
|
executeGetOrCreateByEndpoints: (kind, fromKind, fromId, toKind, toId, props, backend, options) => executeEdgeGetOrCreateByEndpoints(
|
|
@@ -18267,9 +19567,12 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
18267
19567
|
receiptRecorder
|
|
18268
19568
|
));
|
|
18269
19569
|
}
|
|
19570
|
+
const identity = this.#graph.identity === void 0 ? void 0 : createIdentityFacade(this.#identityContext(this.#backend));
|
|
19571
|
+
const receiptIdentity = identity === void 0 || receiptRecorder === void 0 ? identity : wrapTransactionIdentity(identity, receiptRecorder);
|
|
18270
19572
|
const fallbackContext = {
|
|
18271
19573
|
nodes,
|
|
18272
19574
|
edges,
|
|
19575
|
+
...receiptIdentity === void 0 ? {} : { identity: receiptIdentity },
|
|
18273
19576
|
// No real transaction: `tx.sql` is absent and there is no atomicity.
|
|
18274
19577
|
sqlAvailability: "unavailable",
|
|
18275
19578
|
backend: createTransactionReadBackend(this.#backend),
|
|
@@ -18300,15 +19603,31 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
18300
19603
|
recordedByGraph = instants;
|
|
18301
19604
|
}) : backendOptions;
|
|
18302
19605
|
try {
|
|
18303
|
-
const run = async (txBackend, nativeTransaction) =>
|
|
18304
|
-
|
|
19606
|
+
const run = async (txBackend, nativeTransaction) => {
|
|
19607
|
+
const invokeTransaction = () => invoke(
|
|
19608
|
+
this.#buildTransactionContext(
|
|
19609
|
+
txBackend,
|
|
19610
|
+
nativeTransaction,
|
|
19611
|
+
runHooks,
|
|
19612
|
+
receiptRecorder,
|
|
19613
|
+
runBulkHooks
|
|
19614
|
+
)
|
|
19615
|
+
);
|
|
19616
|
+
if (!this.#captureEnabled && !this.#revisionTrackingEnabled) {
|
|
19617
|
+
return invokeTransaction();
|
|
19618
|
+
}
|
|
19619
|
+
return withWriteTransactionSession(
|
|
18305
19620
|
txBackend,
|
|
18306
|
-
|
|
18307
|
-
|
|
18308
|
-
|
|
18309
|
-
|
|
18310
|
-
|
|
18311
|
-
|
|
19621
|
+
{
|
|
19622
|
+
graphId: this.graphId,
|
|
19623
|
+
schemaVersion: this.#schemaMetadata.schemaVersion,
|
|
19624
|
+
historyEnabled: this.#captureEnabled,
|
|
19625
|
+
revisionTrackingEnabled: this.#revisionTrackingEnabled,
|
|
19626
|
+
revisionSchema: this.#sqlSchema()
|
|
19627
|
+
},
|
|
19628
|
+
invokeTransaction
|
|
19629
|
+
);
|
|
19630
|
+
};
|
|
18312
19631
|
const result = this.#captureEnabled || this.#adapterBackend === void 0 ? await this.#backend.transaction(
|
|
18313
19632
|
(txBackend) => run(txBackend, void 0),
|
|
18314
19633
|
transactionOptions
|
|
@@ -18494,10 +19813,15 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
18494
19813
|
context.edges,
|
|
18495
19814
|
scopeRecorder
|
|
18496
19815
|
);
|
|
19816
|
+
const identity = this.#graph.identity === void 0 ? void 0 : wrapTransactionIdentity(
|
|
19817
|
+
context.identity,
|
|
19818
|
+
scopeRecorder
|
|
19819
|
+
);
|
|
18497
19820
|
const scoped = this.#attachMeasure(
|
|
18498
19821
|
overlayPropertyDescriptors(context, {
|
|
18499
19822
|
nodes,
|
|
18500
19823
|
edges,
|
|
19824
|
+
...identity === void 0 ? {} : { identity },
|
|
18501
19825
|
getNodeCollection: (kind) => this.#resolveDynamicNodeCollection(nodes, kind)
|
|
18502
19826
|
})
|
|
18503
19827
|
);
|
|
@@ -18551,10 +19875,13 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
18551
19875
|
receiptRecorder
|
|
18552
19876
|
));
|
|
18553
19877
|
}
|
|
19878
|
+
const identity = this.#graph.identity === void 0 ? void 0 : createIdentityFacade(this.#identityContext(txBackend));
|
|
19879
|
+
const receiptIdentity = identity === void 0 || receiptRecorder === void 0 ? identity : wrapTransactionIdentity(identity, receiptRecorder);
|
|
18554
19880
|
const getNodeCollection = (kind) => this.#resolveDynamicNodeCollection(nodes, kind);
|
|
18555
19881
|
const base = {
|
|
18556
19882
|
nodes,
|
|
18557
19883
|
edges,
|
|
19884
|
+
...receiptIdentity === void 0 ? {} : { identity: receiptIdentity },
|
|
18558
19885
|
backend: createTransactionReadBackend(txBackend),
|
|
18559
19886
|
[TRANSACTION_RUNTIME]: { backend: txBackend, runNodeOperationHooks },
|
|
18560
19887
|
getNodeCollection
|
|
@@ -18587,6 +19914,67 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
18587
19914
|
});
|
|
18588
19915
|
return receiptRecorder === void 0 ? withSql : this.#attachMeasure(withSql);
|
|
18589
19916
|
}
|
|
19917
|
+
#identityContext(backend) {
|
|
19918
|
+
const currentSchemaVersion = () => this.#schemaMetadata.schemaVersion;
|
|
19919
|
+
return {
|
|
19920
|
+
graph: this.#graph,
|
|
19921
|
+
graphId: this.graphId,
|
|
19922
|
+
get schemaVersion() {
|
|
19923
|
+
return currentSchemaVersion();
|
|
19924
|
+
},
|
|
19925
|
+
registry: this.#registry,
|
|
19926
|
+
backend,
|
|
19927
|
+
schema: this.#sqlSchema(),
|
|
19928
|
+
historyEnabled: this.#captureEnabled,
|
|
19929
|
+
revisionTrackingEnabled: this.#revisionTrackingEnabled,
|
|
19930
|
+
sameIdAcrossKinds: this.#graph.identity?.sameIdAcrossKinds ?? "ignore",
|
|
19931
|
+
loadNodes: async (references, coordinate) => {
|
|
19932
|
+
const idsByKind = /* @__PURE__ */ new Map();
|
|
19933
|
+
for (const reference of references) {
|
|
19934
|
+
const ids = idsByKind.get(reference.kind) ?? /* @__PURE__ */ new Set();
|
|
19935
|
+
ids.add(reference.id);
|
|
19936
|
+
idsByKind.set(reference.kind, ids);
|
|
19937
|
+
}
|
|
19938
|
+
const loadedByReference = /* @__PURE__ */ new Map();
|
|
19939
|
+
await Promise.all(
|
|
19940
|
+
[...idsByKind].map(async ([kind, idSet]) => {
|
|
19941
|
+
const ids = [...idSet];
|
|
19942
|
+
if (coordinate?.recorded !== void 0) {
|
|
19943
|
+
const nodes = await this.recordedNodeGetByIds(
|
|
19944
|
+
kind,
|
|
19945
|
+
ids.map((id) => id),
|
|
19946
|
+
coordinate
|
|
19947
|
+
);
|
|
19948
|
+
for (const [index, node] of nodes.entries()) {
|
|
19949
|
+
if (node !== void 0) {
|
|
19950
|
+
loadedByReference.set(
|
|
19951
|
+
refKey({ kind, id: requireDefined(ids[index]) }),
|
|
19952
|
+
node
|
|
19953
|
+
);
|
|
19954
|
+
}
|
|
19955
|
+
}
|
|
19956
|
+
return;
|
|
19957
|
+
}
|
|
19958
|
+
const rows = backend.getNodes === void 0 ? await Promise.all(
|
|
19959
|
+
ids.map((id) => backend.getNode(this.graphId, kind, id))
|
|
19960
|
+
) : await backend.getNodes(this.graphId, kind, ids);
|
|
19961
|
+
const includeTombstoned = coordinate?.valid.mode === "includeTombstones";
|
|
19962
|
+
for (const row of rows) {
|
|
19963
|
+
if (row === void 0) continue;
|
|
19964
|
+
if (!includeTombstoned && row.deleted_at !== void 0) continue;
|
|
19965
|
+
loadedByReference.set(
|
|
19966
|
+
refKey({ kind, id: row.id }),
|
|
19967
|
+
rowToNode(row)
|
|
19968
|
+
);
|
|
19969
|
+
}
|
|
19970
|
+
})
|
|
19971
|
+
);
|
|
19972
|
+
return references.map(
|
|
19973
|
+
(reference) => loadedByReference.get(refKey(reference))
|
|
19974
|
+
);
|
|
19975
|
+
}
|
|
19976
|
+
};
|
|
19977
|
+
}
|
|
18590
19978
|
// === Graph Lifecycle ===
|
|
18591
19979
|
/**
|
|
18592
19980
|
* Hard-deletes all data for this graph from the database.
|
|
@@ -18602,6 +19990,9 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
18602
19990
|
if (this.#revisionTrackingEnabled) {
|
|
18603
19991
|
await lockRecordedGraphWrite(target, this.graphId);
|
|
18604
19992
|
}
|
|
19993
|
+
if (this.#graph.identity !== void 0) {
|
|
19994
|
+
await lockIdentityGraph(target, this.graphId);
|
|
19995
|
+
}
|
|
18605
19996
|
const previousRevision = this.#revisionTrackingEnabled && !this.#captureEnabled ? await readRecordedClock(target, this.#sqlSchema(), this.graphId) : void 0;
|
|
18606
19997
|
await target.clearGraph(this.graphId);
|
|
18607
19998
|
if (this.#revisionTrackingEnabled && !this.#captureEnabled) {
|
|
@@ -18764,12 +20155,40 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
18764
20155
|
this.graphId
|
|
18765
20156
|
);
|
|
18766
20157
|
}
|
|
18767
|
-
const
|
|
20158
|
+
const identityCandidate = merged.identity === void 0 ? void 0 : this.#cloneWithGraph(merged, void 0);
|
|
20159
|
+
const identityProvisioning = identityCandidate === void 0 ? void 0 : await ensureIdentitySchemaStorage(
|
|
20160
|
+
this.#baseBackend,
|
|
20161
|
+
this.#sqlSchema(),
|
|
20162
|
+
{
|
|
20163
|
+
graphId: this.graphId,
|
|
20164
|
+
enablement: false,
|
|
20165
|
+
// The evolved graph's registry — the one the candidate's own
|
|
20166
|
+
// preflight rebuilds through, so the predicate and the fill agree
|
|
20167
|
+
// on which assertions count.
|
|
20168
|
+
registry: buildKindRegistry(merged)
|
|
20169
|
+
}
|
|
20170
|
+
);
|
|
20171
|
+
const committed = identityCandidate === void 0 ? classification.requireEmpty.length > 0 ? await commitEvolvedSchemaWhenRequiredKindsAreEmpty(
|
|
18768
20172
|
this.#backend,
|
|
18769
20173
|
merged,
|
|
18770
20174
|
activeRow.version,
|
|
18771
20175
|
classification
|
|
18772
|
-
) : await commitNewSchemaVersion(this.#backend, merged, activeRow.version)
|
|
20176
|
+
) : await commitNewSchemaVersion(this.#backend, merged, activeRow.version) : await commitNewSchemaVersionWithPreflight(
|
|
20177
|
+
this.#backend,
|
|
20178
|
+
merged,
|
|
20179
|
+
activeRow.version,
|
|
20180
|
+
async (target) => {
|
|
20181
|
+
await assertEvolvedSchemaRequiredKindsEmpty(
|
|
20182
|
+
target,
|
|
20183
|
+
this.graphId,
|
|
20184
|
+
classification
|
|
20185
|
+
);
|
|
20186
|
+
await identityCandidate.identitySchemaPreflight(
|
|
20187
|
+
target,
|
|
20188
|
+
identityProvisioning?.provisionInCommit ?? []
|
|
20189
|
+
);
|
|
20190
|
+
}
|
|
20191
|
+
);
|
|
18773
20192
|
await materializeVectorContributions(this.#backend, merged);
|
|
18774
20193
|
const evolved = this.#cloneWithGraph(
|
|
18775
20194
|
merged,
|
|
@@ -19136,6 +20555,169 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
19136
20555
|
const vectorSlots = backend.capabilities.vector?.supported === true ? resolveGraphVectorSlots(baseline) : [];
|
|
19137
20556
|
return repair(this.graphId, vectorSlots);
|
|
19138
20557
|
}
|
|
20558
|
+
/**
|
|
20559
|
+
* Read-only readiness check for the search projections: is search
|
|
20560
|
+
* coherent with the graph right now, or is a query about to hit a
|
|
20561
|
+
* degraded index?
|
|
20562
|
+
*
|
|
20563
|
+
* The read-only half of {@link Store.repairContributions}, and the
|
|
20564
|
+
* bottom rung of the contribution health ladder — probe (read) →
|
|
20565
|
+
* `repairContributions()` (incremental, non-destructive) →
|
|
20566
|
+
* {@link Store.rebuildContribution} (destructive full reset). It shares
|
|
20567
|
+
* the detection logic of the other two exactly: a second implementation
|
|
20568
|
+
* would be free to disagree with the one the hot-path gate consults,
|
|
20569
|
+
* and a health check that disagrees with the gate is worse than none.
|
|
20570
|
+
*
|
|
20571
|
+
* Writes nothing — no DDL, no marker writes, no effect on the
|
|
20572
|
+
* per-instance caches the hot path relies on — so it is safe on a read
|
|
20573
|
+
* path, on a replica, and under a least-privilege role. That is the
|
|
20574
|
+
* whole reason it exists: the alternative for a caller who wants to
|
|
20575
|
+
* know whether search is ready was to run `repairContributions()`, a
|
|
20576
|
+
* write with repair side effects, and hope.
|
|
20577
|
+
*
|
|
20578
|
+
* One entry per search projection that this graph actually declares
|
|
20579
|
+
* contributions for. A projection with none is omitted rather than
|
|
20580
|
+
* reported `ready`, so an empty `entries` array means "nothing to
|
|
20581
|
+
* assess" — never "assessed and healthy". Use
|
|
20582
|
+
* {@link Store.verifyContributions} for the structured per-table
|
|
20583
|
+
* diagnostics behind a `degraded` entry.
|
|
20584
|
+
*
|
|
20585
|
+
* @throws {ConfigurationError} when the backend provisions
|
|
20586
|
+
* contributions but cannot probe its own catalog. Answering `ready`
|
|
20587
|
+
* without having looked is the one answer this method must never
|
|
20588
|
+
* give. A backend with no contribution machinery at all has nothing
|
|
20589
|
+
* to assess and resolves to empty entries instead.
|
|
20590
|
+
*/
|
|
20591
|
+
async probeContributions() {
|
|
20592
|
+
const backend = this.#baseBackend;
|
|
20593
|
+
const probe = backend.probeContributions;
|
|
20594
|
+
if (probe === void 0) {
|
|
20595
|
+
if (backend.capabilities.contributions?.supported !== true) {
|
|
20596
|
+
return { entries: [] };
|
|
20597
|
+
}
|
|
20598
|
+
throw new ConfigurationError(
|
|
20599
|
+
"probeContributions requires a backend that can probe its catalog for contribution tables.",
|
|
20600
|
+
{
|
|
20601
|
+
backend: backend.dialect,
|
|
20602
|
+
capability: "contributions",
|
|
20603
|
+
operation: "probe"
|
|
20604
|
+
}
|
|
20605
|
+
);
|
|
20606
|
+
}
|
|
20607
|
+
const vectorSlots = backend.capabilities.vector?.supported === true ? resolveGraphVectorSlots(this.#graph) : [];
|
|
20608
|
+
const entries = await probe(this.graphId, vectorSlots);
|
|
20609
|
+
const revision = await this.revisionNow();
|
|
20610
|
+
return {
|
|
20611
|
+
...revision === void 0 ? {} : { graphRevision: revision },
|
|
20612
|
+
entries
|
|
20613
|
+
};
|
|
20614
|
+
}
|
|
20615
|
+
/**
|
|
20616
|
+
* Destructively rebuilds one search projection for THIS graph: clears the
|
|
20617
|
+
* graph's rows from the projection's storage, recreates that storage from
|
|
20618
|
+
* the current DDL when recreating it is safe, reconstructs the content
|
|
20619
|
+
* from the node rows, and stamps the durable marker at the current
|
|
20620
|
+
* signature.
|
|
20621
|
+
*
|
|
20622
|
+
* **Scoped to this graph.** The fulltext table is one physical table
|
|
20623
|
+
* holding every graph's rows, keyed by `graph_id`, and this method is
|
|
20624
|
+
* fenced per graph — so it removes only this graph's rows
|
|
20625
|
+
* (`DELETE ... WHERE graph_id`, the same statement `clear()` uses) and
|
|
20626
|
+
* escalates to dropping and recreating the table only when no other graph
|
|
20627
|
+
* has rows in it. That drop is the one repair for storage provisioned at a
|
|
20628
|
+
* shape the current DDL no longer produces; when another graph's content
|
|
20629
|
+
* is in the way it is refused
|
|
20630
|
+
* ({@link ContributionRebuildUnsupportedError}, reason
|
|
20631
|
+
* `shared-storage-in-use`) rather than taken, because fulltext content is
|
|
20632
|
+
* reconstructed from a graph's own nodes through its own schema and no
|
|
20633
|
+
* other process can put back what the drop would destroy.
|
|
20634
|
+
*
|
|
20635
|
+
* The top rung of the health ladder, and the repair
|
|
20636
|
+
* {@link Store.repairContributions} deliberately refuses to perform.
|
|
20637
|
+
* Never reachable from that method — a `stale` contribution's table
|
|
20638
|
+
* exists at the *old* shape, so the ordinary ensure path's idempotent
|
|
20639
|
+
* `CREATE ... IF NOT EXISTS` no-ops and re-stamping the marker there
|
|
20640
|
+
* would leave it blessing a table whose shape is wrong, which is
|
|
20641
|
+
* exactly what the drift guard exists to prevent. Only a recreate makes
|
|
20642
|
+
* the ensure meaningful, and destroying content is a decision stated at
|
|
20643
|
+
* the call site rather than reached through a flag named `force`.
|
|
20644
|
+
*
|
|
20645
|
+
* **This destroys this graph's index content.** For `"fulltext"` nothing
|
|
20646
|
+
* is permanently lost: the searchable content is derived from node
|
|
20647
|
+
* properties TypeGraph already stores, so the refill reconstructs it in
|
|
20648
|
+
* the same transaction. Nodes whose stored `props` cannot be read as an
|
|
20649
|
+
* object are counted in `skipped` and are absent from the rebuilt index —
|
|
20650
|
+
* `store.search.rebuildFulltext()` reports their ids individually.
|
|
20651
|
+
*
|
|
20652
|
+
* Atomic: the clear (or drop), recreate, refill, and stamp all run inside
|
|
20653
|
+
* one transaction under the same per-graph fence as a schema commit, plus
|
|
20654
|
+
* a database-scoped advisory lock serializing this contribution's DDL
|
|
20655
|
+
* across graphs, so an interrupted rebuild leaves the contribution exactly
|
|
20656
|
+
* as it was rather than attested-but-empty.
|
|
20657
|
+
*
|
|
20658
|
+
* The cost differs by path, and only one of them is a maintenance-window
|
|
20659
|
+
* operation. A rebuild that drops and recreates the shared table takes
|
|
20660
|
+
* `ACCESS EXCLUSIVE` on it before deciding to — the verdict "no other
|
|
20661
|
+
* graph has rows here" is only sound under the exclusion the drop needs —
|
|
20662
|
+
* so on PostgreSQL every graph's fulltext writers wait for the whole
|
|
20663
|
+
* refill. That was already true of the drop itself, which holds the same
|
|
20664
|
+
* lock to commit; the exclusion now merely starts a few statements
|
|
20665
|
+
* earlier. The graph-scoped path takes no table lock at all: its
|
|
20666
|
+
* `DELETE ... WHERE graph_id` is transactional and touches only this
|
|
20667
|
+
* graph's rows, so it never makes another graph's writers wait.
|
|
20668
|
+
* `store.search.rebuildFulltext()` remains the incremental, resumable way
|
|
20669
|
+
* to refresh content when the storage shape is fine.
|
|
20670
|
+
*
|
|
20671
|
+
* @throws {ContributionRebuildUnsupportedError} for `"vector"`, always:
|
|
20672
|
+
* TypeGraph stores the vectors callers supply and never the inputs
|
|
20673
|
+
* that produced them, so embeddings exist only in the storage a
|
|
20674
|
+
* rebuild would drop and there is nothing to reconstruct them from.
|
|
20675
|
+
* `store.reembedVectorField(kind, fieldPath, { embed })` is the
|
|
20676
|
+
* sanctioned destructive path — it takes the callback that can
|
|
20677
|
+
* regenerate what the drop destroys. Also thrown when the active
|
|
20678
|
+
* strategy declares no teardown DDL, when the backend has no
|
|
20679
|
+
* transactional schema fence to make the sequence atomic, and when the
|
|
20680
|
+
* recorded shape is stale but the storage that would have to be
|
|
20681
|
+
* recreated holds another graph's rows (`shared-storage-in-use`). Every
|
|
20682
|
+
* one of these refuses before anything is dropped or deleted.
|
|
20683
|
+
* @throws {ConfigurationError} when the backend cannot rebuild
|
|
20684
|
+
* strategy-owned contributions at all.
|
|
20685
|
+
*/
|
|
20686
|
+
async rebuildContribution(scope, options) {
|
|
20687
|
+
const backend = this.#baseBackend;
|
|
20688
|
+
const rebuild = backend.rebuildContribution;
|
|
20689
|
+
if (rebuild === void 0) {
|
|
20690
|
+
throw new ConfigurationError(
|
|
20691
|
+
"rebuildContribution requires a backend that can drop, recreate, and re-stamp strategy-owned contribution tables.",
|
|
20692
|
+
{
|
|
20693
|
+
backend: backend.dialect,
|
|
20694
|
+
capability: "contributions",
|
|
20695
|
+
operation: "rebuild"
|
|
20696
|
+
}
|
|
20697
|
+
);
|
|
20698
|
+
}
|
|
20699
|
+
await this.#loadCaughtUp("rebuild");
|
|
20700
|
+
const registry = this.#registry;
|
|
20701
|
+
const graphId = this.graphId;
|
|
20702
|
+
return rebuild(graphId, scope, async (target) => {
|
|
20703
|
+
if (scope !== "fulltext") {
|
|
20704
|
+
throw new ContributionRebuildUnsupportedError(
|
|
20705
|
+
"vector-source-unavailable",
|
|
20706
|
+
{ graphId, contribution: scope, backend: backend.dialect }
|
|
20707
|
+
);
|
|
20708
|
+
}
|
|
20709
|
+
const result = await repopulateFulltextInTransaction(
|
|
20710
|
+
{ graphId, registry },
|
|
20711
|
+
target,
|
|
20712
|
+
options?.pageSize === void 0 ? {} : { pageSize: options.pageSize }
|
|
20713
|
+
);
|
|
20714
|
+
return {
|
|
20715
|
+
processed: result.processed,
|
|
20716
|
+
repopulated: result.upserted,
|
|
20717
|
+
skipped: result.skipped
|
|
20718
|
+
};
|
|
20719
|
+
});
|
|
20720
|
+
}
|
|
19139
20721
|
/**
|
|
19140
20722
|
* Removes extension kinds from the schema with cascading edge and
|
|
19141
20723
|
* ontology cleanup. Two-phase by design:
|
|
@@ -19183,10 +20765,19 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
19183
20765
|
const compileTimeGraph = stripGraphExtension(this.#graph);
|
|
19184
20766
|
const merged = plan.document === void 0 ? compileTimeGraph : mergeGraphExtension(compileTimeGraph, plan.document);
|
|
19185
20767
|
const finalGraph = applyDeprecatedKinds(merged, baseline.deprecatedKinds);
|
|
19186
|
-
const
|
|
20768
|
+
const identityCascade = await this.#identityRemovalPreflight(
|
|
20769
|
+
finalGraph,
|
|
20770
|
+
plan.removedNodeKinds
|
|
20771
|
+
);
|
|
20772
|
+
const committedRow = identityCascade === void 0 ? await commitNewSchemaVersion(
|
|
19187
20773
|
this.#backend,
|
|
19188
20774
|
finalGraph,
|
|
19189
20775
|
activeRow.version
|
|
20776
|
+
) : await commitNewSchemaVersionWithPreflight(
|
|
20777
|
+
this.#backend,
|
|
20778
|
+
finalGraph,
|
|
20779
|
+
activeRow.version,
|
|
20780
|
+
identityCascade
|
|
19190
20781
|
);
|
|
19191
20782
|
const recordKindRemoval = this.#backend.recordKindRemoval;
|
|
19192
20783
|
if (recordKindRemoval !== void 0) {
|
|
@@ -19224,6 +20815,35 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
19224
20815
|
}
|
|
19225
20816
|
return evolved;
|
|
19226
20817
|
}
|
|
20818
|
+
/**
|
|
20819
|
+
* The identity cascade `removeKinds()` commits with, or `undefined` when the
|
|
20820
|
+
* removal cannot touch the assertion ledger.
|
|
20821
|
+
*
|
|
20822
|
+
* Identity being switched OFF is not that case: disabling identity retains
|
|
20823
|
+
* the assertion rows, so a graph with no profile can still hold assertions
|
|
20824
|
+
* naming the kinds being removed. Those rows are cascaded too — otherwise
|
|
20825
|
+
* they survive as current orphans that a later re-enablement filters out of
|
|
20826
|
+
* the closure while raw ledger reads and merge staging still see them. What
|
|
20827
|
+
* the profile's absence does remove is the closure contract, so the repair
|
|
20828
|
+
* pass is skipped and the ledger cleanup runs alone — and a graph that has no
|
|
20829
|
+
* such rows keeps committing through the plain schema-commit primitive.
|
|
20830
|
+
*/
|
|
20831
|
+
async #identityRemovalPreflight(finalGraph, removedNodeKinds) {
|
|
20832
|
+
if (removedNodeKinds.length === 0) return void 0;
|
|
20833
|
+
const repairClosure = finalGraph.identity !== void 0;
|
|
20834
|
+
if (!repairClosure && !await identityKindCascadeNeeded(
|
|
20835
|
+
this.#backend,
|
|
20836
|
+
this.#sqlSchema(),
|
|
20837
|
+
this.graphId,
|
|
20838
|
+
removedNodeKinds
|
|
20839
|
+
)) {
|
|
20840
|
+
return void 0;
|
|
20841
|
+
}
|
|
20842
|
+
const candidate = this.#cloneWithGraph(finalGraph, void 0);
|
|
20843
|
+
return async (target) => candidate.removeIdentityKindsInSchemaPreflight(target, removedNodeKinds, {
|
|
20844
|
+
repairClosure
|
|
20845
|
+
});
|
|
20846
|
+
}
|
|
19227
20847
|
/**
|
|
19228
20848
|
* Runs the data-cleanup phase for any kinds removed via
|
|
19229
20849
|
* `removeKinds()` whose data has not yet been deleted on this
|
|
@@ -19326,10 +20946,10 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
19326
20946
|
const getPendingKindRemovals = this.#backend.getPendingKindRemovals;
|
|
19327
20947
|
if (getPendingKindRemovals === void 0) return;
|
|
19328
20948
|
const addedNodes = Object.keys(merged.nodes).filter(
|
|
19329
|
-
(kind) => !(
|
|
20949
|
+
(kind) => !hasOwnKey(baseline.nodes, kind)
|
|
19330
20950
|
);
|
|
19331
20951
|
const addedEdges = Object.keys(merged.edges).filter(
|
|
19332
|
-
(kind) => !(
|
|
20952
|
+
(kind) => !hasOwnKey(baseline.edges, kind)
|
|
19333
20953
|
);
|
|
19334
20954
|
if (addedNodes.length === 0 && addedEdges.length === 0) return;
|
|
19335
20955
|
await ensureFocusedStatusTable(
|
|
@@ -19386,6 +21006,7 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
19386
21006
|
return (ctx, fn) => this.#withBulkOperationHooks(ctx, fn);
|
|
19387
21007
|
}
|
|
19388
21008
|
#createNodeOperationContext(runHooks = this.#immediateHookRunner(), runBulkHooks = this.#immediateBulkHookRunner()) {
|
|
21009
|
+
const identityConfig = this.#graph.identity;
|
|
19389
21010
|
return {
|
|
19390
21011
|
graph: this.#graph,
|
|
19391
21012
|
graphId: this.graphId,
|
|
@@ -19394,6 +21015,31 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
19394
21015
|
revisionTrackingEnabled: this.#revisionTrackingEnabled,
|
|
19395
21016
|
revisionSchema: this.#sqlSchema(),
|
|
19396
21017
|
registry: this.#registry,
|
|
21018
|
+
...identityConfig === void 0 ? {} : {
|
|
21019
|
+
identity: {
|
|
21020
|
+
lock: (target) => lockIdentityGraph(target, this.graphId),
|
|
21021
|
+
foldCreated: (target, references) => foldIdentityForCreatedNodes(
|
|
21022
|
+
{
|
|
21023
|
+
graphId: this.graphId,
|
|
21024
|
+
registry: this.#registry,
|
|
21025
|
+
sameIdAcrossKinds: identityConfig.sameIdAcrossKinds,
|
|
21026
|
+
schema: this.#sqlSchema()
|
|
21027
|
+
},
|
|
21028
|
+
target,
|
|
21029
|
+
references
|
|
21030
|
+
),
|
|
21031
|
+
detachDeleted: (target, ref, mode) => detachIdentityForNode(
|
|
21032
|
+
{
|
|
21033
|
+
graphId: this.graphId,
|
|
21034
|
+
sameIdAcrossKinds: identityConfig.sameIdAcrossKinds,
|
|
21035
|
+
schema: this.#sqlSchema()
|
|
21036
|
+
},
|
|
21037
|
+
target,
|
|
21038
|
+
ref,
|
|
21039
|
+
mode
|
|
21040
|
+
)
|
|
21041
|
+
}
|
|
21042
|
+
},
|
|
19397
21043
|
createOperationContext: (operation, entity, kind, id) => this.#createOperationContext(operation, entity, kind, id),
|
|
19398
21044
|
withOperationHooks: runHooks,
|
|
19399
21045
|
createBulkOperationContext: (operation, kind) => ({
|
|
@@ -19423,6 +21069,11 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
19423
21069
|
* Decorates the query execution port so hooks observe each statement the
|
|
19424
21070
|
* query builder submits to the backend. A selective-projection retry
|
|
19425
21071
|
* therefore emits two independent start/end pairs with their actual SQL.
|
|
21072
|
+
*
|
|
21073
|
+
* Decoration goes through {@link createBackendOverlay}, which carries the
|
|
21074
|
+
* projection's serialized-resource ownership: the hooked backend still
|
|
21075
|
+
* executes on the source's connection, so it must answer the same "shares one
|
|
21076
|
+
* serialized connection" question as its source.
|
|
19426
21077
|
*/
|
|
19427
21078
|
#createHookedQueryBackend(backend) {
|
|
19428
21079
|
if (this.#hooks.onQueryStart === void 0 && this.#hooks.onQueryEnd === void 0 && this.#hooks.onError === void 0) {
|
|
@@ -19431,8 +21082,7 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
19431
21082
|
const executeRaw = backend.executeRaw;
|
|
19432
21083
|
const compileSql = backend.compileSql;
|
|
19433
21084
|
const projected = createGraphBackendProjection(backend);
|
|
19434
|
-
return {
|
|
19435
|
-
...projected,
|
|
21085
|
+
return createBackendOverlay(projected, {
|
|
19436
21086
|
execute: (query) => {
|
|
19437
21087
|
const compiled = compileSql === void 0 ? renderSql(query, backend.dialect) : compileSql(query);
|
|
19438
21088
|
return this.#withQueryHooks(
|
|
@@ -19448,7 +21098,7 @@ var StoreImplementation = class _StoreImplementation {
|
|
|
19448
21098
|
() => executeRaw(sqlText, params)
|
|
19449
21099
|
)
|
|
19450
21100
|
}
|
|
19451
|
-
};
|
|
21101
|
+
});
|
|
19452
21102
|
}
|
|
19453
21103
|
async #withQueryHooks(sql2, params, execute) {
|
|
19454
21104
|
const ctx = {
|
|
@@ -19804,10 +21454,70 @@ async function createStoreWithSchema(graph, backend, options) {
|
|
|
19804
21454
|
prepared.result
|
|
19805
21455
|
];
|
|
19806
21456
|
}
|
|
19807
|
-
async function assertHistorySchemaOnOpen(backend, options) {
|
|
21457
|
+
async function assertHistorySchemaOnOpen(backend, graph, options) {
|
|
19808
21458
|
if (options?.history !== true) return;
|
|
19809
21459
|
const schema = options.schema === void 0 ? createSqlSchema(backend.tableNames) : requireSqlSchema(options.schema, "store schema");
|
|
19810
|
-
await assertCurrentRecordedSchema(
|
|
21460
|
+
await assertCurrentRecordedSchema(
|
|
21461
|
+
backend,
|
|
21462
|
+
schema,
|
|
21463
|
+
graph.identity !== void 0
|
|
21464
|
+
);
|
|
21465
|
+
}
|
|
21466
|
+
var IDENTITY_ONTOLOGY_META_EDGES = /* @__PURE__ */ new Set([
|
|
21467
|
+
META_EDGE_DISJOINT_WITH,
|
|
21468
|
+
META_EDGE_EQUIVALENT_TO,
|
|
21469
|
+
META_EDGE_SAME_AS,
|
|
21470
|
+
META_EDGE_SUB_CLASS_OF
|
|
21471
|
+
]);
|
|
21472
|
+
function identityOntologyChanged(before, after) {
|
|
21473
|
+
function relevantRelations(schema) {
|
|
21474
|
+
return schema.ontology.relations.filter((relation) => IDENTITY_ONTOLOGY_META_EDGES.has(relation.metaEdge)).map(
|
|
21475
|
+
(relation) => JSON.stringify([relation.metaEdge, relation.from, relation.to])
|
|
21476
|
+
).toSorted();
|
|
21477
|
+
}
|
|
21478
|
+
return JSON.stringify(relevantRelations(before)) !== JSON.stringify(relevantRelations(after));
|
|
21479
|
+
}
|
|
21480
|
+
var IDENTITY_GATE_CODES = {
|
|
21481
|
+
enablement: "IDENTITY_ENABLEMENT_PENDING",
|
|
21482
|
+
profile: "IDENTITY_PROFILE_MIGRATION_PENDING",
|
|
21483
|
+
ontology: "IDENTITY_SCHEMA_MIGRATION_PENDING"
|
|
21484
|
+
};
|
|
21485
|
+
async function ensureSchemaWithIdentityPrecedence(backend, graph, options, gate) {
|
|
21486
|
+
try {
|
|
21487
|
+
return await ensureSchema(backend, graph, options);
|
|
21488
|
+
} catch (error) {
|
|
21489
|
+
if (gate === void 0 || !isIdentityOnlyBreakingChange(error)) throw error;
|
|
21490
|
+
throw identityMigrationRequiredError(graph.id, gate, {
|
|
21491
|
+
blockedBy: "breaking-change",
|
|
21492
|
+
cause: error
|
|
21493
|
+
});
|
|
21494
|
+
}
|
|
21495
|
+
}
|
|
21496
|
+
function isIdentityOnlyBreakingChange(error) {
|
|
21497
|
+
if (!(error instanceof MigrationError)) return false;
|
|
21498
|
+
const diff = "diff" in error.details ? error.details.diff : void 0;
|
|
21499
|
+
if (diff?.identity?.severity !== "breaking") return false;
|
|
21500
|
+
return ![
|
|
21501
|
+
...diff.nodes,
|
|
21502
|
+
...diff.edges,
|
|
21503
|
+
...diff.ontology,
|
|
21504
|
+
...diff.indexes
|
|
21505
|
+
].some((change) => change.severity === "breaking");
|
|
21506
|
+
}
|
|
21507
|
+
function identityMigrationRequiredError(graphId, gate, context) {
|
|
21508
|
+
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.";
|
|
21509
|
+
return new ConfigurationError(
|
|
21510
|
+
`Changing Operational Identity semantics requires the schema migration to commit, ${blocker}`,
|
|
21511
|
+
{
|
|
21512
|
+
code: IDENTITY_GATE_CODES[gate],
|
|
21513
|
+
graphId,
|
|
21514
|
+
...context.pendingVersion === void 0 ? {} : { pendingVersion: context.pendingVersion }
|
|
21515
|
+
},
|
|
21516
|
+
{
|
|
21517
|
+
cause: context.cause,
|
|
21518
|
+
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."
|
|
21519
|
+
}
|
|
21520
|
+
);
|
|
19811
21521
|
}
|
|
19812
21522
|
async function prepareStoreWithSchema(graph, backend, options) {
|
|
19813
21523
|
const {
|
|
@@ -19815,11 +21525,70 @@ async function prepareStoreWithSchema(graph, backend, options) {
|
|
|
19815
21525
|
activeRow,
|
|
19816
21526
|
storedSchema
|
|
19817
21527
|
} = await loadAndMergeGraphExtensionDocument(backend, graph);
|
|
19818
|
-
const
|
|
19819
|
-
|
|
21528
|
+
const identityProfileChanged = activeRow !== void 0 && storedSchema?.identity?.sameIdAcrossKinds !== merged.identity?.sameIdAcrossKinds;
|
|
21529
|
+
const identityInitialization = activeRow === void 0 && merged.identity !== void 0;
|
|
21530
|
+
const identityEnablement = identityInitialization || identityProfileChanged && storedSchema?.identity === void 0 && merged.identity !== void 0;
|
|
21531
|
+
const identitySemanticsChanged = identityProfileChanged || activeRow !== void 0 && storedSchema !== void 0 && merged.identity !== void 0 && identityOntologyChanged(
|
|
21532
|
+
storedSchema,
|
|
21533
|
+
serializeSchema(merged, activeRow.version + 1)
|
|
21534
|
+
);
|
|
21535
|
+
const identityGate = identitySemanticsChanged ? identityEnablement ? "enablement" : identityProfileChanged ? "profile" : "ontology" : void 0;
|
|
21536
|
+
const identityProfile = merged.identity;
|
|
21537
|
+
if (identityProfile !== void 0) {
|
|
21538
|
+
const resolvedIdentitySchema = options?.schema === void 0 ? createSqlSchema(backend.tableNames) : requireSqlSchema(options.schema, "store schema");
|
|
21539
|
+
const identityRegistry = buildKindRegistry(merged);
|
|
21540
|
+
await ensureIdentitySchemaStorage(backend, resolvedIdentitySchema, {
|
|
21541
|
+
graphId: merged.id,
|
|
21542
|
+
enablement: identityEnablement,
|
|
21543
|
+
registry: identityRegistry,
|
|
21544
|
+
// A derived relation this library version added, absent from (or left
|
|
21545
|
+
// empty in) a database that predates it, is created and FILLED as one
|
|
21546
|
+
// unit here — never created now and filled at the end of boot. Between
|
|
21547
|
+
// those two points sit the schema commit, the history assertion, three
|
|
21548
|
+
// materialization steps and an index build, and for that whole window an
|
|
21549
|
+
// empty separation relation would answer "not separated" to every
|
|
21550
|
+
// concurrent `assertSame`. See `ensureIdentitySchemaStorage`.
|
|
21551
|
+
//
|
|
21552
|
+
// Withheld when a schema commit is gated on an identity SEMANTICS
|
|
21553
|
+
// change: that commit's own preflight creates AND rebuilds inside the
|
|
21554
|
+
// commit transaction, under the semantics being committed. Filling here
|
|
21555
|
+
// would instead derive classes from semantics this database has not
|
|
21556
|
+
// accepted yet — and would keep them if the commit were then refused.
|
|
21557
|
+
// Nothing is provisioned on that path either, so a refused commit leaves
|
|
21558
|
+
// the relation absent rather than readable-empty, and the next open of a
|
|
21559
|
+
// graph whose semantics ARE committed heals it here.
|
|
21560
|
+
...identityGate === void 0 ? {
|
|
21561
|
+
recomputeDerivedRelations: (target) => rebuildIdentityClosureForContext({
|
|
21562
|
+
backend: target,
|
|
21563
|
+
graphId: merged.id,
|
|
21564
|
+
registry: identityRegistry,
|
|
21565
|
+
schema: resolvedIdentitySchema,
|
|
21566
|
+
sameIdAcrossKinds: identityProfile.sameIdAcrossKinds
|
|
21567
|
+
})
|
|
21568
|
+
} : {}
|
|
21569
|
+
});
|
|
21570
|
+
}
|
|
21571
|
+
const ensureOptions = {
|
|
21572
|
+
// The fallback is not useless: spreading `options` while it is possibly
|
|
21573
|
+
// undefined makes TypeScript distribute the StoreOptions union through
|
|
21574
|
+
// the literal and reject the call below.
|
|
21575
|
+
// eslint-disable-next-line unicorn/no-useless-fallback-in-spread
|
|
21576
|
+
...options ?? {},
|
|
19820
21577
|
preloaded: { activeRow, storedSchema }
|
|
19821
|
-
}
|
|
19822
|
-
await
|
|
21578
|
+
};
|
|
21579
|
+
const result = await ensureSchemaWithIdentityPrecedence(
|
|
21580
|
+
backend,
|
|
21581
|
+
merged,
|
|
21582
|
+
ensureOptions,
|
|
21583
|
+
identityGate
|
|
21584
|
+
);
|
|
21585
|
+
if (identityGate !== void 0 && result.status === "pending") {
|
|
21586
|
+
throw identityMigrationRequiredError(merged.id, identityGate, {
|
|
21587
|
+
blockedBy: "auto-migrate-disabled",
|
|
21588
|
+
pendingVersion: result.version
|
|
21589
|
+
});
|
|
21590
|
+
}
|
|
21591
|
+
await assertHistorySchemaOnOpen(backend, merged, options);
|
|
19823
21592
|
await materializeRuntimeContributions(backend, merged.id);
|
|
19824
21593
|
await materializeVectorContributions(backend, merged);
|
|
19825
21594
|
if (options?.systemIndexes !== "skip") {
|
|
@@ -19871,15 +21640,14 @@ async function materializeSystemIndexesOnBoot(backend, graphId, result) {
|
|
|
19871
21640
|
}
|
|
19872
21641
|
async function createVerifiedStore(graph, backend, options) {
|
|
19873
21642
|
const prepared = await prepareVerifiedStore(graph, backend, options);
|
|
19874
|
-
|
|
19875
|
-
|
|
19876
|
-
|
|
19877
|
-
|
|
19878
|
-
|
|
19879
|
-
|
|
19880
|
-
|
|
19881
|
-
|
|
19882
|
-
];
|
|
21643
|
+
const store = new StoreImplementation(
|
|
21644
|
+
prepared.graph,
|
|
21645
|
+
backend,
|
|
21646
|
+
options,
|
|
21647
|
+
prepared.schemaMetadata
|
|
21648
|
+
);
|
|
21649
|
+
await store.validateIdentity();
|
|
21650
|
+
return [store, prepared.result];
|
|
19883
21651
|
}
|
|
19884
21652
|
async function prepareVerifiedStore(graph, backend, options) {
|
|
19885
21653
|
const {
|
|
@@ -19887,7 +21655,7 @@ async function prepareVerifiedStore(graph, backend, options) {
|
|
|
19887
21655
|
activeRow,
|
|
19888
21656
|
result
|
|
19889
21657
|
} = await loadAndVerifyGraph(backend, graph);
|
|
19890
|
-
await assertHistorySchemaOnOpen(backend, options);
|
|
21658
|
+
await assertHistorySchemaOnOpen(backend, merged, options);
|
|
19891
21659
|
return {
|
|
19892
21660
|
graph: merged,
|
|
19893
21661
|
result,
|
|
@@ -19896,19 +21664,16 @@ async function prepareVerifiedStore(graph, backend, options) {
|
|
|
19896
21664
|
}
|
|
19897
21665
|
async function createVerifiedAdapterStore(graph, backend, options) {
|
|
19898
21666
|
const prepared = await prepareVerifiedStore(graph, backend, options);
|
|
19899
|
-
|
|
19900
|
-
|
|
19901
|
-
|
|
19902
|
-
|
|
19903
|
-
|
|
19904
|
-
|
|
19905
|
-
|
|
19906
|
-
|
|
19907
|
-
),
|
|
19908
|
-
prepared.result
|
|
19909
|
-
];
|
|
21667
|
+
const store = new AdapterStoreImplementation(
|
|
21668
|
+
prepared.graph,
|
|
21669
|
+
backend,
|
|
21670
|
+
options,
|
|
21671
|
+
prepared.schemaMetadata
|
|
21672
|
+
);
|
|
21673
|
+
await store.validateIdentity();
|
|
21674
|
+
return [asAdapterStoreSurface(store), prepared.result];
|
|
19910
21675
|
}
|
|
19911
21676
|
|
|
19912
21677
|
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 };
|
|
19913
|
-
//# sourceMappingURL=chunk-
|
|
19914
|
-
//# sourceMappingURL=chunk-
|
|
21678
|
+
//# sourceMappingURL=chunk-S6PW7VV5.js.map
|
|
21679
|
+
//# sourceMappingURL=chunk-S6PW7VV5.js.map
|