@nicia-ai/typegraph 0.46.1 → 0.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -1
- package/dist/backend/drizzle/indexes.cjs +9 -9
- package/dist/backend/drizzle/indexes.d.cts +1 -1
- package/dist/backend/drizzle/indexes.d.ts +1 -1
- package/dist/backend/drizzle/indexes.js +5 -5
- package/dist/backend/index.cjs +50 -49
- package/dist/backend/index.d.cts +3 -3
- package/dist/backend/index.d.ts +3 -3
- package/dist/backend/index.js +11 -10
- package/dist/backend/postgres/index.cjs +27 -26
- package/dist/backend/postgres/index.d.cts +6 -6
- package/dist/backend/postgres/index.d.ts +6 -6
- package/dist/backend/postgres/index.js +14 -13
- package/dist/backend/postgres/pglite-store.cjs +34 -33
- package/dist/backend/postgres/pglite-store.cjs.map +1 -1
- package/dist/backend/postgres/pglite-store.d.cts +8 -8
- package/dist/backend/postgres/pglite-store.d.ts +8 -8
- package/dist/backend/postgres/pglite-store.js +30 -29
- package/dist/backend/postgres/pglite-store.js.map +1 -1
- package/dist/backend/postgres/pglite.cjs +16 -15
- package/dist/backend/postgres/pglite.d.cts +3 -3
- package/dist/backend/postgres/pglite.d.ts +3 -3
- package/dist/backend/postgres/pglite.js +15 -14
- package/dist/backend/sqlite/index.cjs +24 -23
- package/dist/backend/sqlite/index.d.cts +6 -6
- package/dist/backend/sqlite/index.d.ts +6 -6
- package/dist/backend/sqlite/index.js +12 -11
- package/dist/backend/sqlite/libsql.cjs +19 -18
- package/dist/backend/sqlite/libsql.cjs.map +1 -1
- package/dist/backend/sqlite/libsql.d.cts +3 -3
- package/dist/backend/sqlite/libsql.d.ts +3 -3
- package/dist/backend/sqlite/libsql.js +14 -13
- package/dist/backend/sqlite/libsql.js.map +1 -1
- package/dist/backend/sqlite/local-store.cjs +35 -34
- package/dist/backend/sqlite/local-store.cjs.map +1 -1
- package/dist/backend/sqlite/local-store.d.cts +8 -8
- package/dist/backend/sqlite/local-store.d.ts +8 -8
- package/dist/backend/sqlite/local-store.js +31 -30
- package/dist/backend/sqlite/local-store.js.map +1 -1
- package/dist/backend/sqlite/local.cjs +17 -16
- package/dist/backend/sqlite/local.d.cts +3 -3
- package/dist/backend/sqlite/local.d.ts +3 -3
- package/dist/backend/sqlite/local.js +15 -14
- package/dist/{chunk-NK5Q7UNE.cjs → chunk-26YKDNUD.cjs} +10 -10
- package/dist/{chunk-NK5Q7UNE.cjs.map → chunk-26YKDNUD.cjs.map} +1 -1
- package/dist/{chunk-PQBQG7IM.js → chunk-3DMMGT6S.js} +3 -3
- package/dist/{chunk-PQBQG7IM.js.map → chunk-3DMMGT6S.js.map} +1 -1
- package/dist/{chunk-XBYVV3MP.cjs → chunk-3HGRTCDU.cjs} +6 -4
- package/dist/chunk-3HGRTCDU.cjs.map +1 -0
- package/dist/{chunk-NFCVHQ5P.cjs → chunk-3U5ABFDW.cjs} +26 -26
- package/dist/{chunk-NFCVHQ5P.cjs.map → chunk-3U5ABFDW.cjs.map} +1 -1
- package/dist/{chunk-MCLYU6BN.cjs → chunk-425IZOQ5.cjs} +11 -164
- package/dist/chunk-425IZOQ5.cjs.map +1 -0
- package/dist/{chunk-M4BDU5RX.cjs → chunk-5FBRVORR.cjs} +12 -12
- package/dist/{chunk-M4BDU5RX.cjs.map → chunk-5FBRVORR.cjs.map} +1 -1
- package/dist/{chunk-C23WSN5E.js → chunk-5FT3TLPK.js} +20 -14
- package/dist/chunk-5FT3TLPK.js.map +1 -0
- package/dist/{chunk-YBFHZUXX.js → chunk-5UMTUOM5.js} +6 -6
- package/dist/{chunk-YBFHZUXX.js.map → chunk-5UMTUOM5.js.map} +1 -1
- package/dist/{chunk-HFHTMECK.js → chunk-6FWSNSRE.js} +7 -7
- package/dist/{chunk-HFHTMECK.js.map → chunk-6FWSNSRE.js.map} +1 -1
- package/dist/{chunk-65E3XWGF.cjs → chunk-6JVZSOHQ.cjs} +91 -91
- package/dist/{chunk-65E3XWGF.cjs.map → chunk-6JVZSOHQ.cjs.map} +1 -1
- package/dist/chunk-6KFLYYOC.cjs +161 -0
- package/dist/chunk-6KFLYYOC.cjs.map +1 -0
- package/dist/{chunk-LBJKZXDE.cjs → chunk-6NGW27EO.cjs} +10 -10
- package/dist/{chunk-LBJKZXDE.cjs.map → chunk-6NGW27EO.cjs.map} +1 -1
- package/dist/{chunk-5UVRGXD3.js → chunk-6SWPJEYN.js} +5 -5
- package/dist/{chunk-5UVRGXD3.js.map → chunk-6SWPJEYN.js.map} +1 -1
- package/dist/{chunk-BKCUOV7A.js → chunk-7SQSZN2J.js} +5 -5
- package/dist/{chunk-BKCUOV7A.js.map → chunk-7SQSZN2J.js.map} +1 -1
- package/dist/{chunk-YAAM3M2S.cjs → chunk-AMMWA76F.cjs} +51 -59
- package/dist/chunk-AMMWA76F.cjs.map +1 -0
- package/dist/{chunk-62ZP23AW.cjs → chunk-AOSSQDVO.cjs} +13 -13
- package/dist/{chunk-62ZP23AW.cjs.map → chunk-AOSSQDVO.cjs.map} +1 -1
- package/dist/{chunk-NLI2AVEF.js → chunk-AVGUJWVX.js} +30 -10
- package/dist/chunk-AVGUJWVX.js.map +1 -0
- package/dist/{chunk-TQR4KBY2.cjs → chunk-BCQ76AXA.cjs} +262 -155
- package/dist/chunk-BCQ76AXA.cjs.map +1 -0
- package/dist/{chunk-CXBHOOIQ.cjs → chunk-BFUOME67.cjs} +11 -11
- package/dist/{chunk-CXBHOOIQ.cjs.map → chunk-BFUOME67.cjs.map} +1 -1
- package/dist/{chunk-PNTCATDE.cjs → chunk-BR7SHKGM.cjs} +1672 -1400
- package/dist/chunk-BR7SHKGM.cjs.map +1 -0
- package/dist/{chunk-ASTI57H3.js → chunk-CHAOFCHV.js} +3 -3
- package/dist/{chunk-ASTI57H3.js.map → chunk-CHAOFCHV.js.map} +1 -1
- package/dist/chunk-CICO5GXD.cjs +12 -0
- package/dist/{chunk-A4OC4DVN.cjs.map → chunk-CICO5GXD.cjs.map} +1 -1
- package/dist/{chunk-T4XNKYXB.js → chunk-D5R42W5D.js} +4 -4
- package/dist/{chunk-T4XNKYXB.js.map → chunk-D5R42W5D.js.map} +1 -1
- package/dist/{chunk-S6PW7VV5.js → chunk-DJZTXBWY.js} +410 -138
- package/dist/chunk-DJZTXBWY.js.map +1 -0
- package/dist/{chunk-UDISNXDL.cjs → chunk-DO7SAMEK.cjs} +47 -47
- package/dist/{chunk-UDISNXDL.cjs.map → chunk-DO7SAMEK.cjs.map} +1 -1
- package/dist/{chunk-VCRRKBLN.cjs → chunk-EDN4Y4ZG.cjs} +9 -9
- package/dist/{chunk-VCRRKBLN.cjs.map → chunk-EDN4Y4ZG.cjs.map} +1 -1
- package/dist/{chunk-YVWGEQJE.cjs → chunk-EMIB2RXD.cjs} +139 -133
- package/dist/chunk-EMIB2RXD.cjs.map +1 -0
- package/dist/{chunk-2Z2Z3ZD6.js → chunk-EP4RUYD6.js} +3 -3
- package/dist/{chunk-2Z2Z3ZD6.js.map → chunk-EP4RUYD6.js.map} +1 -1
- package/dist/{chunk-M3WP7RQZ.js → chunk-FNOMT74T.js} +10 -8
- package/dist/chunk-FNOMT74T.js.map +1 -0
- package/dist/{chunk-E4WNQBEF.cjs → chunk-FQPEMEWX.cjs} +12 -12
- package/dist/{chunk-E4WNQBEF.cjs.map → chunk-FQPEMEWX.cjs.map} +1 -1
- package/dist/{chunk-WDGA7IYB.js → chunk-GLHXM7C4.js} +3 -3
- package/dist/{chunk-WDGA7IYB.js.map → chunk-GLHXM7C4.js.map} +1 -1
- package/dist/{chunk-7BQXPCGN.cjs → chunk-GYEG3ZLZ.cjs} +10 -10
- package/dist/{chunk-7BQXPCGN.cjs.map → chunk-GYEG3ZLZ.cjs.map} +1 -1
- package/dist/{chunk-VLT6PEWR.cjs → chunk-GYUJZARH.cjs} +4 -4
- package/dist/{chunk-VLT6PEWR.cjs.map → chunk-GYUJZARH.cjs.map} +1 -1
- package/dist/{chunk-RMQLSPA2.cjs → chunk-HDL2ARHX.cjs} +27 -27
- package/dist/{chunk-RMQLSPA2.cjs.map → chunk-HDL2ARHX.cjs.map} +1 -1
- package/dist/{chunk-775A4ZMN.js → chunk-HEV35TGT.js} +4 -4
- package/dist/{chunk-775A4ZMN.js.map → chunk-HEV35TGT.js.map} +1 -1
- package/dist/{chunk-3DHZCOT7.cjs → chunk-I4G3SQPF.cjs} +20 -20
- package/dist/{chunk-3DHZCOT7.cjs.map → chunk-I4G3SQPF.cjs.map} +1 -1
- package/dist/{chunk-VGTJPTIC.js → chunk-JONXFAPO.js} +5 -5
- package/dist/{chunk-VGTJPTIC.js.map → chunk-JONXFAPO.js.map} +1 -1
- package/dist/{chunk-BSFJMMV3.cjs → chunk-JR6SX5RH.cjs} +4 -4
- package/dist/{chunk-BSFJMMV3.cjs.map → chunk-JR6SX5RH.cjs.map} +1 -1
- package/dist/{chunk-YBQOHKHN.cjs → chunk-KABFHAL7.cjs} +100 -99
- package/dist/chunk-KABFHAL7.cjs.map +1 -0
- package/dist/{chunk-7VQXLT5O.js → chunk-KH7XW7VV.js} +111 -27
- package/dist/chunk-KH7XW7VV.js.map +1 -0
- package/dist/{chunk-DCQVHAYQ.js → chunk-KKBDXCLQ.js} +4 -4
- package/dist/{chunk-DCQVHAYQ.js.map → chunk-KKBDXCLQ.js.map} +1 -1
- package/dist/{chunk-4UGOKMI5.cjs → chunk-L3LQNR34.cjs} +161 -161
- package/dist/{chunk-4UGOKMI5.cjs.map → chunk-L3LQNR34.cjs.map} +1 -1
- package/dist/{chunk-NMWDXFPH.js → chunk-LEWHMOUY.js} +46 -56
- package/dist/chunk-LEWHMOUY.js.map +1 -0
- package/dist/{chunk-U63GDNRC.cjs → chunk-LRQKXRXO.cjs} +4 -4
- package/dist/{chunk-U63GDNRC.cjs.map → chunk-LRQKXRXO.cjs.map} +1 -1
- package/dist/{chunk-5763DULU.js → chunk-M35DF33T.js} +147 -40
- package/dist/chunk-M35DF33T.js.map +1 -0
- package/dist/{chunk-Q3G6A4I3.cjs → chunk-MFZR4QKM.cjs} +30 -28
- package/dist/chunk-MFZR4QKM.cjs.map +1 -0
- package/dist/chunk-MKJ4KXL5.cjs +12 -0
- package/dist/{chunk-VQH4VAWD.cjs.map → chunk-MKJ4KXL5.cjs.map} +1 -1
- package/dist/{chunk-NI72YOJB.cjs → chunk-MRDBBHIZ.cjs} +70 -2
- package/dist/chunk-MRDBBHIZ.cjs.map +1 -0
- package/dist/{chunk-DTVWX77L.cjs → chunk-MX4DHSQP.cjs} +1023 -360
- package/dist/chunk-MX4DHSQP.cjs.map +1 -0
- package/dist/{chunk-2LG2XB2M.js → chunk-MYKHJI2J.js} +3 -3
- package/dist/{chunk-2LG2XB2M.js.map → chunk-MYKHJI2J.js.map} +1 -1
- package/dist/{chunk-JZUR7ZWE.js → chunk-NAZ2RA4L.js} +3 -3
- package/dist/{chunk-JZUR7ZWE.js.map → chunk-NAZ2RA4L.js.map} +1 -1
- package/dist/{chunk-XL7SLCXS.js → chunk-NG2RB5WL.js} +4 -145
- package/dist/chunk-NG2RB5WL.js.map +1 -0
- package/dist/{chunk-GF3KXUTW.js → chunk-NNQ6A3RM.js} +803 -143
- package/dist/chunk-NNQ6A3RM.js.map +1 -0
- package/dist/{chunk-MSY7FSX4.js → chunk-NVY5CJFP.js} +4 -4
- package/dist/{chunk-MSY7FSX4.js.map → chunk-NVY5CJFP.js.map} +1 -1
- package/dist/{chunk-XCAGZAKP.cjs → chunk-OI2GQ25X.cjs} +7 -7
- package/dist/{chunk-XCAGZAKP.cjs.map → chunk-OI2GQ25X.cjs.map} +1 -1
- package/dist/{chunk-PF6F5NLJ.cjs → chunk-OIFSOPSR.cjs} +259 -238
- package/dist/chunk-OIFSOPSR.cjs.map +1 -0
- package/dist/{chunk-UV3SN74I.js → chunk-OMZ37BM2.js} +5 -5
- package/dist/{chunk-UV3SN74I.js.map → chunk-OMZ37BM2.js.map} +1 -1
- package/dist/{chunk-PNN7RHVR.cjs → chunk-OORXGFL5.cjs} +48 -48
- package/dist/{chunk-PNN7RHVR.cjs.map → chunk-OORXGFL5.cjs.map} +1 -1
- package/dist/{chunk-YWGVVALJ.js → chunk-OVI7IXYX.js} +3 -3
- package/dist/{chunk-YWGVVALJ.js.map → chunk-OVI7IXYX.js.map} +1 -1
- package/dist/{chunk-I5MOXA4H.js → chunk-P6YLZTRE.js} +3 -3
- package/dist/{chunk-I5MOXA4H.js.map → chunk-P6YLZTRE.js.map} +1 -1
- package/dist/{chunk-WQZLUGVR.js → chunk-QS3NQZVC.js} +82 -16
- package/dist/chunk-QS3NQZVC.js.map +1 -0
- package/dist/{chunk-5262ZMTR.js → chunk-QTD6HX4O.js} +5 -5
- package/dist/{chunk-5262ZMTR.js.map → chunk-QTD6HX4O.js.map} +1 -1
- package/dist/{chunk-MXJDTH5B.cjs → chunk-ROR5ZR6A.cjs} +45 -45
- package/dist/{chunk-MXJDTH5B.cjs.map → chunk-ROR5ZR6A.cjs.map} +1 -1
- package/dist/{chunk-CBTIUB76.js → chunk-SDBBMD4N.js} +3 -3
- package/dist/{chunk-CBTIUB76.js.map → chunk-SDBBMD4N.js.map} +1 -1
- package/dist/{chunk-L65OU2U3.js → chunk-SE6EGQSD.js} +78 -16
- package/dist/chunk-SE6EGQSD.js.map +1 -0
- package/dist/{chunk-JXA2ZP2Q.js → chunk-SHFBB23Q.js} +4 -4
- package/dist/{chunk-JXA2ZP2Q.js.map → chunk-SHFBB23Q.js.map} +1 -1
- package/dist/{chunk-AKLS3BYZ.js → chunk-SHSSRCFR.js} +5 -3
- package/dist/chunk-SHSSRCFR.js.map +1 -0
- package/dist/{chunk-2NA5DYLO.cjs → chunk-TZEGUG6D.cjs} +4 -4
- package/dist/{chunk-2NA5DYLO.cjs.map → chunk-TZEGUG6D.cjs.map} +1 -1
- package/dist/{chunk-CMWHLBVN.cjs → chunk-U5EDAVOJ.cjs} +140 -74
- package/dist/chunk-U5EDAVOJ.cjs.map +1 -0
- package/dist/{chunk-MUK4BU7K.cjs → chunk-UPAJHMW3.cjs} +210 -126
- package/dist/chunk-UPAJHMW3.cjs.map +1 -0
- package/dist/{chunk-PT7RIRIZ.cjs → chunk-WGX7JH7E.cjs} +235 -170
- package/dist/chunk-WGX7JH7E.cjs.map +1 -0
- package/dist/{chunk-U5SON7R2.cjs → chunk-WODMN3Q2.cjs} +27 -27
- package/dist/{chunk-U5SON7R2.cjs.map → chunk-WODMN3Q2.cjs.map} +1 -1
- package/dist/{chunk-K5ELX2GT.js → chunk-XVWOYKIK.js} +3 -3
- package/dist/{chunk-K5ELX2GT.js.map → chunk-XVWOYKIK.js.map} +1 -1
- package/dist/chunk-XY5OMOJD.js +147 -0
- package/dist/chunk-XY5OMOJD.js.map +1 -0
- package/dist/{chunk-LGHCOKTT.js → chunk-XYBQI6LE.js} +14 -14
- package/dist/{chunk-LGHCOKTT.js.map → chunk-XYBQI6LE.js.map} +1 -1
- package/dist/{chunk-YDFDBE6D.js → chunk-ZDKTPBGV.js} +4 -4
- package/dist/{chunk-YDFDBE6D.js.map → chunk-ZDKTPBGV.js.map} +1 -1
- package/dist/{chunk-MZLPX2R7.js → chunk-ZHNXLS3C.js} +9 -8
- package/dist/chunk-ZHNXLS3C.js.map +1 -0
- package/dist/{chunk-J3WZ3FA2.js → chunk-ZLONH2UD.js} +67 -3
- package/dist/chunk-ZLONH2UD.js.map +1 -0
- package/dist/core/index.cjs +28 -27
- package/dist/core/index.d.cts +4 -4
- package/dist/core/index.d.ts +4 -4
- package/dist/core/index.js +8 -7
- package/dist/{ddl-BXfGHbPN.d.ts → ddl-CprnMXJ-.d.ts} +3 -3
- package/dist/{ddl-BJcOp4WZ.d.cts → ddl-DJA7Lci5.d.cts} +3 -3
- package/dist/graph-extension/index.cjs +20 -20
- package/dist/graph-extension/index.d.cts +4 -4
- package/dist/graph-extension/index.d.ts +4 -4
- package/dist/graph-extension/index.js +4 -4
- package/dist/graph-merge/index.cjs +3204 -635
- package/dist/graph-merge/index.cjs.map +1 -1
- package/dist/graph-merge/index.d.cts +502 -60
- package/dist/graph-merge/index.d.ts +502 -60
- package/dist/graph-merge/index.js +2979 -427
- package/dist/graph-merge/index.js.map +1 -1
- package/dist/{index-BSHvKfok.d.ts → index-CHTv4XMZ.d.cts} +64 -3
- package/dist/{index-DxaYCv8Y.d.cts → index-CzoJ3MUw.d.ts} +64 -3
- package/dist/index.cjs +232 -215
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -11
- package/dist/index.d.ts +11 -11
- package/dist/index.js +35 -34
- package/dist/index.js.map +1 -1
- package/dist/indexes/index.cjs +13 -13
- package/dist/indexes/index.d.cts +4 -4
- package/dist/indexes/index.d.ts +4 -4
- package/dist/indexes/index.js +6 -6
- package/dist/interchange/index.cjs +53 -52
- package/dist/interchange/index.cjs.map +1 -1
- package/dist/interchange/index.d.cts +17 -13
- package/dist/interchange/index.d.ts +17 -13
- package/dist/interchange/index.js +17 -16
- package/dist/interchange/index.js.map +1 -1
- package/dist/{manager-CfZAimti.d.ts → manager-B6sOQzcQ.d.ts} +2 -2
- package/dist/{manager-HGk6E_HW.d.cts → manager-DlY1vlla.d.cts} +2 -2
- package/dist/{materialize-indexes-BMJP6Tms.d.ts → materialize-indexes-DQL4xcpf.d.ts} +1 -1
- package/dist/{materialize-indexes-6A7dWGtb.d.cts → materialize-indexes-sskhp-c_.d.cts} +1 -1
- package/dist/{postgres-DFYWENdR.d.cts → postgres-B6YOUcaK.d.ts} +1 -1
- package/dist/{postgres-e7a9TdvR.d.ts → postgres-BXy_nyMs.d.cts} +1 -1
- package/dist/profiler/index.d.cts +9 -9
- package/dist/profiler/index.d.ts +9 -9
- package/dist/provenance/index.cjs +43 -42
- 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 +10 -9
- package/dist/provenance/index.js.map +1 -1
- package/dist/schema/index.cjs +45 -44
- package/dist/schema/index.d.cts +6 -6
- package/dist/schema/index.d.ts +6 -6
- package/dist/schema/index.js +18 -17
- package/dist/{searchable-CIje5XYD.d.cts → searchable-0Cn_lY8U.d.cts} +1 -1
- package/dist/{searchable-9OQoPuRs.d.ts → searchable-D9gMiwcS.d.ts} +1 -1
- package/dist/{sqlite-DerU5tc7.d.ts → sqlite-ClXuBYAj.d.ts} +1 -1
- package/dist/{sqlite-DitnISa9.d.cts → sqlite-DioSxaPe.d.cts} +1 -1
- package/dist/{store-ClQ-MZQS.d.ts → store-5j-w5onJ.d.ts} +128 -69
- package/dist/{store-B0h6x7wo.d.cts → store-CFedKGU5.d.cts} +128 -69
- package/dist/{temporal-Bn-Fi8FJ.d.cts → temporal-Ce1pG0S1.d.cts} +1 -1
- package/dist/{temporal-BtM4UALv.d.ts → temporal-CjO9FH1l.d.ts} +1 -1
- package/dist/{types-KFfk-8Ah.d.ts → types-BrdOFu48.d.ts} +1 -1
- package/dist/{types-HkveCCx0.d.ts → types-DKkMJZjR.d.cts} +39 -12
- package/dist/{types-HkveCCx0.d.cts → types-DKkMJZjR.d.ts} +39 -12
- package/dist/{types-CoIx8HX5.d.cts → types-oj_OGAaZ.d.cts} +1 -1
- package/dist/{where-ws5hf-Ov.d.ts → where-C8w2627G.d.ts} +1 -1
- package/dist/{where--fk7_0kv.d.cts → where-D1wKCJ4Q.d.cts} +1 -1
- package/package.json +1 -1
- package/dist/chunk-5763DULU.js.map +0 -1
- package/dist/chunk-7VQXLT5O.js.map +0 -1
- package/dist/chunk-A4OC4DVN.cjs +0 -12
- package/dist/chunk-AKLS3BYZ.js.map +0 -1
- package/dist/chunk-C23WSN5E.js.map +0 -1
- package/dist/chunk-CMWHLBVN.cjs.map +0 -1
- package/dist/chunk-DTVWX77L.cjs.map +0 -1
- package/dist/chunk-GF3KXUTW.js.map +0 -1
- package/dist/chunk-J3WZ3FA2.js.map +0 -1
- package/dist/chunk-L65OU2U3.js.map +0 -1
- package/dist/chunk-M3WP7RQZ.js.map +0 -1
- package/dist/chunk-MCLYU6BN.cjs.map +0 -1
- package/dist/chunk-MUK4BU7K.cjs.map +0 -1
- package/dist/chunk-MZLPX2R7.js.map +0 -1
- package/dist/chunk-NI72YOJB.cjs.map +0 -1
- package/dist/chunk-NLI2AVEF.js.map +0 -1
- package/dist/chunk-NMWDXFPH.js.map +0 -1
- package/dist/chunk-PF6F5NLJ.cjs.map +0 -1
- package/dist/chunk-PNTCATDE.cjs.map +0 -1
- package/dist/chunk-PT7RIRIZ.cjs.map +0 -1
- package/dist/chunk-Q3G6A4I3.cjs.map +0 -1
- package/dist/chunk-S6PW7VV5.js.map +0 -1
- package/dist/chunk-TQR4KBY2.cjs.map +0 -1
- package/dist/chunk-VQH4VAWD.cjs +0 -12
- package/dist/chunk-WQZLUGVR.js.map +0 -1
- package/dist/chunk-XBYVV3MP.cjs.map +0 -1
- package/dist/chunk-XL7SLCXS.js.map +0 -1
- package/dist/chunk-YAAM3M2S.cjs.map +0 -1
- package/dist/chunk-YBQOHKHN.cjs.map +0 -1
- package/dist/chunk-YVWGEQJE.cjs.map +0 -1
|
@@ -1,34 +1,35 @@
|
|
|
1
|
-
import { createStoreWithSchema } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
3
|
-
import { importGraphStream, exportGraphStream, importGraph, exportGraph } from '../chunk-
|
|
4
|
-
import '../chunk-
|
|
5
|
-
import { storeBackend, storeRuntime, transactionBackend } from '../chunk-
|
|
6
|
-
import { computeSchemaHash, serializeSchema } from '../chunk-
|
|
7
|
-
import { defineNode } from '../chunk-
|
|
8
|
-
import { encodeTupleKey, normalizeIdentityPair, identityReferenceKey, identityAssertionSemanticKey } from '../chunk-
|
|
9
|
-
import {
|
|
1
|
+
import { createStoreWithSchema } from '../chunk-DJZTXBWY.js';
|
|
2
|
+
import '../chunk-LEWHMOUY.js';
|
|
3
|
+
import { importGraphStream, exportGraphStream, importGraph, exportGraph } from '../chunk-QS3NQZVC.js';
|
|
4
|
+
import '../chunk-SE6EGQSD.js';
|
|
5
|
+
import { storeBackend, storeRuntime, transactionBackend } from '../chunk-FNOMT74T.js';
|
|
6
|
+
import { computeSchemaHash, serializeSchema } from '../chunk-XYBQI6LE.js';
|
|
7
|
+
import { defineNode } from '../chunk-SHFBB23Q.js';
|
|
8
|
+
import { encodeTupleKey, forceWriteTransactionRevision, normalizeIdentityPair, identityReferenceKey, identityValidityWindowsOverlap, identityAssertionSemanticKey } from '../chunk-NNQ6A3RM.js';
|
|
9
|
+
import { compareStrings, generateId, lockRecordedGraphWrite, readRevisionOrigin, readRecordedClock, forceRecordedGraphRevision, advanceRevisionClock, compareCodePoints } from '../chunk-AVGUJWVX.js';
|
|
10
10
|
import { sortedReplacer } from '../chunk-JBQQ7THH.js';
|
|
11
|
-
import '../chunk-
|
|
12
|
-
import { createSqlSchema } from '../chunk-
|
|
13
|
-
import { snapshotExportContention } from '../chunk-
|
|
14
|
-
import { getNodeKinds, getEdgeKinds, defineInternalGraph } from '../chunk-
|
|
15
|
-
import { computeUniqueKey } from '../chunk-
|
|
16
|
-
import
|
|
17
|
-
import '../chunk-
|
|
18
|
-
import '../chunk-
|
|
19
|
-
import '../chunk-
|
|
11
|
+
import '../chunk-NAZ2RA4L.js';
|
|
12
|
+
import { createSqlSchema } from '../chunk-NVY5CJFP.js';
|
|
13
|
+
import { snapshotExportContention } from '../chunk-SHSSRCFR.js';
|
|
14
|
+
import { getNodeKinds, getEdgeKinds, defineInternalGraph } from '../chunk-HEV35TGT.js';
|
|
15
|
+
import { validateEdgeEndpoints, computeUniqueKey } from '../chunk-GLHXM7C4.js';
|
|
16
|
+
import '../chunk-XY5OMOJD.js';
|
|
17
|
+
import { canonicalizeDatabaseTimestamp } from '../chunk-NG2RB5WL.js';
|
|
18
|
+
import '../chunk-KKBDXCLQ.js';
|
|
19
|
+
import '../chunk-SDBBMD4N.js';
|
|
20
|
+
import '../chunk-P6YLZTRE.js';
|
|
20
21
|
import { sha256Hex } from '../chunk-K34OPSJK.js';
|
|
21
22
|
import '../chunk-2EBMPJBO.js';
|
|
22
|
-
import '../chunk-
|
|
23
|
-
import { asCompiledStatementSql, sql, asCompiledRowsSql } from '../chunk-
|
|
23
|
+
import '../chunk-ZDKTPBGV.js';
|
|
24
|
+
import { asCompiledStatementSql, sql, asCompiledRowsSql } from '../chunk-3DMMGT6S.js';
|
|
24
25
|
import '../chunk-AMKPPFUO.js';
|
|
25
26
|
import '../chunk-W6OG3HDG.js';
|
|
26
27
|
import '../chunk-PC2UBAUB.js';
|
|
27
|
-
import { ok, err, isErr, computeTransitiveClosure, isReachable } from '../chunk-
|
|
28
|
-
export { isErr, isOk, unwrap } from '../chunk-
|
|
28
|
+
import { ok, err, isErr, computeTransitiveClosure, isReachable } from '../chunk-D5R42W5D.js';
|
|
29
|
+
export { isErr, isOk, unwrap } from '../chunk-D5R42W5D.js';
|
|
29
30
|
import { META_EDGE_EQUIVALENT_TO, META_EDGE_SAME_AS } from '../chunk-JXXDZTMN.js';
|
|
30
|
-
import '../chunk-
|
|
31
|
-
import { TypeGraphError, ConfigurationError, IdentityContradictionError, NodeNotFoundError } from '../chunk-
|
|
31
|
+
import '../chunk-XVWOYKIK.js';
|
|
32
|
+
import { TypeGraphError, ConfigurationError, IdentityContradictionError, NodeNotFoundError, IdentityEndpointValidityError } from '../chunk-ZLONH2UD.js';
|
|
32
33
|
import '../chunk-FWJKWH6U.js';
|
|
33
34
|
import { createDataKeyedBag, hasOwnKey, requireDefined, readOwnProperty } from '../chunk-DYATYRY4.js';
|
|
34
35
|
import { z } from 'zod';
|
|
@@ -106,8 +107,20 @@ var MERGE_ERROR_CODES = {
|
|
|
106
107
|
branch: "GRAPH_MERGE_BRANCH_ERROR",
|
|
107
108
|
similarityUnavailable: "GRAPH_MERGE_SIMILARITY_UNAVAILABLE",
|
|
108
109
|
conflict: "GRAPH_MERGE_CONFLICT",
|
|
110
|
+
constraintConflict: "GRAPH_MERGE_CONSTRAINT_CONFLICT",
|
|
109
111
|
identityConflict: "GRAPH_MERGE_IDENTITY_CONFLICT",
|
|
110
|
-
baseVersionMismatch: "GRAPH_MERGE_BASE_VERSION_MISMATCH"
|
|
112
|
+
baseVersionMismatch: "GRAPH_MERGE_BASE_VERSION_MISMATCH",
|
|
113
|
+
planCapability: "GRAPH_MERGE_PLAN_CAPABILITY",
|
|
114
|
+
planInvalid: "GRAPH_MERGE_PLAN_INVALID",
|
|
115
|
+
planVersionUnsupported: "GRAPH_MERGE_PLAN_VERSION_UNSUPPORTED",
|
|
116
|
+
planDigestMismatch: "GRAPH_MERGE_PLAN_DIGEST_MISMATCH",
|
|
117
|
+
planTargetMismatch: "GRAPH_MERGE_PLAN_TARGET_MISMATCH",
|
|
118
|
+
planSchemaMismatch: "GRAPH_MERGE_PLAN_SCHEMA_MISMATCH",
|
|
119
|
+
planOriginMismatch: "GRAPH_MERGE_PLAN_ORIGIN_MISMATCH",
|
|
120
|
+
planStale: "GRAPH_MERGE_PLAN_STALE",
|
|
121
|
+
planningStale: "GRAPH_MERGE_PLANNING_STALE",
|
|
122
|
+
candidateSource: "GRAPH_MERGE_CANDIDATE_SOURCE",
|
|
123
|
+
evidence: "GRAPH_MERGE_EVIDENCE"
|
|
111
124
|
};
|
|
112
125
|
function toTypeGraphErrorOptions(category, options) {
|
|
113
126
|
return {
|
|
@@ -164,6 +177,31 @@ var MergeConflictError = class extends MergeError {
|
|
|
164
177
|
this.name = "MergeConflictError";
|
|
165
178
|
}
|
|
166
179
|
};
|
|
180
|
+
var MergeConstraintConflictError = class extends MergeError {
|
|
181
|
+
static errorCategory = "constraint";
|
|
182
|
+
code = MERGE_ERROR_CODES.constraintConflict;
|
|
183
|
+
constructor(cause) {
|
|
184
|
+
super(`The resolved merge would violate ${cause.name}: ${cause.message}`, {
|
|
185
|
+
cause,
|
|
186
|
+
details: {
|
|
187
|
+
...cause.details,
|
|
188
|
+
constraintCode: cause.code,
|
|
189
|
+
constraintErrorName: cause.name,
|
|
190
|
+
constraintDetails: cause.details
|
|
191
|
+
},
|
|
192
|
+
suggestion: cause.suggestion ?? "Change the branch data or target state so the resolved graph satisfies its constraints, then retry the merge."
|
|
193
|
+
});
|
|
194
|
+
this.name = "MergeConstraintConflictError";
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
function translateMergeCommitError(error) {
|
|
198
|
+
if (error instanceof MergeError) return error;
|
|
199
|
+
if (!(error instanceof TypeGraphError)) return error;
|
|
200
|
+
if (error.category !== "constraint" || error.code.startsWith("IDENTITY_")) {
|
|
201
|
+
return error;
|
|
202
|
+
}
|
|
203
|
+
return new MergeConstraintConflictError(error);
|
|
204
|
+
}
|
|
167
205
|
var IdentityMergeConflictError = class extends MergeError {
|
|
168
206
|
code = MERGE_ERROR_CODES.identityConflict;
|
|
169
207
|
constructor(message, options = {}) {
|
|
@@ -181,56 +219,496 @@ var BaseVersionMismatchError = class extends MergeError {
|
|
|
181
219
|
this.name = "BaseVersionMismatchError";
|
|
182
220
|
}
|
|
183
221
|
};
|
|
222
|
+
var MergePlanCapabilityError = class extends MergeError {
|
|
223
|
+
static errorCategory = "user";
|
|
224
|
+
code = MERGE_ERROR_CODES.planCapability;
|
|
225
|
+
constructor(message, options = {}) {
|
|
226
|
+
super(message, options);
|
|
227
|
+
this.name = "MergePlanCapabilityError";
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
var InvalidMergePlanError = class extends MergeError {
|
|
231
|
+
static errorCategory = "user";
|
|
232
|
+
code = MERGE_ERROR_CODES.planInvalid;
|
|
233
|
+
constructor(message, options = {}) {
|
|
234
|
+
super(message, options);
|
|
235
|
+
this.name = "InvalidMergePlanError";
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
var UnsupportedMergePlanVersionError = class extends InvalidMergePlanError {
|
|
239
|
+
code = MERGE_ERROR_CODES.planVersionUnsupported;
|
|
240
|
+
constructor(message, options = {}) {
|
|
241
|
+
super(message, options);
|
|
242
|
+
this.name = "UnsupportedMergePlanVersionError";
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
var MergePlanDigestMismatchError = class extends InvalidMergePlanError {
|
|
246
|
+
code = MERGE_ERROR_CODES.planDigestMismatch;
|
|
247
|
+
constructor(message, options = {}) {
|
|
248
|
+
super(message, options);
|
|
249
|
+
this.name = "MergePlanDigestMismatchError";
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
var MergePlanTargetMismatchError = class extends InvalidMergePlanError {
|
|
253
|
+
code = MERGE_ERROR_CODES.planTargetMismatch;
|
|
254
|
+
constructor(message, options = {}) {
|
|
255
|
+
super(message, options);
|
|
256
|
+
this.name = "MergePlanTargetMismatchError";
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
var MergePlanSchemaMismatchError = class extends InvalidMergePlanError {
|
|
260
|
+
code = MERGE_ERROR_CODES.planSchemaMismatch;
|
|
261
|
+
constructor(message, options = {}) {
|
|
262
|
+
super(message, options);
|
|
263
|
+
this.name = "MergePlanSchemaMismatchError";
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
var MergePlanOriginMismatchError = class extends InvalidMergePlanError {
|
|
267
|
+
code = MERGE_ERROR_CODES.planOriginMismatch;
|
|
268
|
+
constructor(message, options = {}) {
|
|
269
|
+
super(message, options);
|
|
270
|
+
this.name = "MergePlanOriginMismatchError";
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
var StaleMergePlanError = class extends MergeError {
|
|
274
|
+
code = MERGE_ERROR_CODES.planStale;
|
|
275
|
+
constructor(message, options = {}) {
|
|
276
|
+
super(message, {
|
|
277
|
+
...options,
|
|
278
|
+
suggestion: options.suggestion ?? "Create a new merge plan against the target's current revision, review it, and apply that plan instead."
|
|
279
|
+
});
|
|
280
|
+
this.name = "StaleMergePlanError";
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
var MergePlanningStaleError = class extends StaleMergePlanError {
|
|
284
|
+
code = MERGE_ERROR_CODES.planningStale;
|
|
285
|
+
constructor(message, options = {}) {
|
|
286
|
+
super(message, options);
|
|
287
|
+
this.name = "MergePlanningStaleError";
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
var CandidateSourceError = class extends MergeError {
|
|
291
|
+
code = MERGE_ERROR_CODES.candidateSource;
|
|
292
|
+
constructor(message, options = {}) {
|
|
293
|
+
super(message, options);
|
|
294
|
+
this.name = "CandidateSourceError";
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
var MatchEvidenceError = class extends MergeError {
|
|
298
|
+
code = MERGE_ERROR_CODES.evidence;
|
|
299
|
+
constructor(message, options = {}) {
|
|
300
|
+
super(message, options);
|
|
301
|
+
this.name = "MatchEvidenceError";
|
|
302
|
+
}
|
|
303
|
+
};
|
|
184
304
|
function describeCause(cause) {
|
|
185
305
|
return cause instanceof Error ? cause.message : String(cause);
|
|
186
306
|
}
|
|
187
307
|
|
|
188
|
-
// src/graph-merge/
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
308
|
+
// src/graph-merge/temporal-identity-closure.ts
|
|
309
|
+
function disjointCacheKey(left, right) {
|
|
310
|
+
return left <= right ? `${left}\0${right}` : `${right}\0${left}`;
|
|
311
|
+
}
|
|
312
|
+
function areDisjoint(state, left, right) {
|
|
313
|
+
const key = disjointCacheKey(left, right);
|
|
314
|
+
const cached = state.disjointCache.get(key);
|
|
315
|
+
if (cached !== void 0) return cached;
|
|
316
|
+
const result = state.context.areDisjoint(left, right);
|
|
317
|
+
state.disjointCache.set(key, result);
|
|
318
|
+
return result;
|
|
319
|
+
}
|
|
320
|
+
function findRoot(components, index) {
|
|
321
|
+
let root = index;
|
|
322
|
+
while (components.parent[root] !== root) {
|
|
323
|
+
root = requireDefined(components.parent[root]);
|
|
192
324
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
325
|
+
return root;
|
|
326
|
+
}
|
|
327
|
+
function componentKindsConflict(state, leftKinds, rightKinds) {
|
|
328
|
+
for (const left of leftKinds.keys()) {
|
|
329
|
+
for (const right of rightKinds.keys()) {
|
|
330
|
+
if (left !== right && areDisjoint(state, left, right)) return true;
|
|
199
331
|
}
|
|
200
332
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
333
|
+
return false;
|
|
334
|
+
}
|
|
335
|
+
function unionReferences(state, leftIndex, rightIndex) {
|
|
336
|
+
const { components } = state;
|
|
337
|
+
const leftRoot = findRoot(components, leftIndex);
|
|
338
|
+
const rightRoot = findRoot(components, rightIndex);
|
|
339
|
+
if (leftRoot === rightRoot) return;
|
|
340
|
+
const [parentRoot, childRoot] = requireDefined(components.sizes[leftRoot]) >= requireDefined(components.sizes[rightRoot]) ? [leftRoot, rightRoot] : [rightRoot, leftRoot];
|
|
341
|
+
const parentKinds = requireDefined(components.kindFirstReference[parentRoot]);
|
|
342
|
+
const childKinds = requireDefined(components.kindFirstReference[childRoot]);
|
|
343
|
+
const parentDifferent = requireDefined(
|
|
344
|
+
components.differentAssertions[parentRoot]
|
|
345
|
+
);
|
|
346
|
+
const childDifferent = requireDefined(
|
|
347
|
+
components.differentAssertions[childRoot]
|
|
348
|
+
);
|
|
349
|
+
const previousParentSize = requireDefined(components.sizes[parentRoot]);
|
|
350
|
+
const previousViolation = requireDefined(
|
|
351
|
+
components.violatesOntology[parentRoot]
|
|
352
|
+
);
|
|
353
|
+
const changedKinds = [...childKinds].flatMap(
|
|
354
|
+
([kind, childFirstReference]) => {
|
|
355
|
+
const previousFirstReference = parentKinds.get(kind);
|
|
356
|
+
return previousFirstReference === void 0 || childFirstReference < previousFirstReference ? [
|
|
357
|
+
{
|
|
358
|
+
kind,
|
|
359
|
+
previousFirstReference,
|
|
360
|
+
nextFirstReference: childFirstReference
|
|
361
|
+
}
|
|
362
|
+
] : [];
|
|
207
363
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
364
|
+
);
|
|
365
|
+
const addedDifferent = [...childDifferent].filter(
|
|
366
|
+
(assertionIndex) => !parentDifferent.has(assertionIndex)
|
|
367
|
+
);
|
|
368
|
+
const addedContradictions = [];
|
|
369
|
+
const crossesDisjointKinds = componentKindsConflict(
|
|
370
|
+
state,
|
|
371
|
+
parentKinds,
|
|
372
|
+
childKinds
|
|
373
|
+
);
|
|
374
|
+
components.parent[childRoot] = parentRoot;
|
|
375
|
+
components.sizes[parentRoot] = previousParentSize + requireDefined(components.sizes[childRoot]);
|
|
376
|
+
for (const { kind, nextFirstReference } of changedKinds) {
|
|
377
|
+
parentKinds.set(kind, nextFirstReference);
|
|
378
|
+
}
|
|
379
|
+
for (const assertionIndex of addedDifferent) {
|
|
380
|
+
parentDifferent.add(assertionIndex);
|
|
381
|
+
}
|
|
382
|
+
components.violatesOntology[parentRoot] = previousViolation || requireDefined(components.violatesOntology[childRoot]) || crossesDisjointKinds;
|
|
383
|
+
for (const assertionIndex of childDifferent) {
|
|
384
|
+
const assertion = requireDefined(state.assertions[assertionIndex]);
|
|
385
|
+
const aIndex = requireDefined(
|
|
386
|
+
state.referenceIndexByKey.get(mergeKeyOf(assertion.a))
|
|
387
|
+
);
|
|
388
|
+
const bIndex = requireDefined(
|
|
389
|
+
state.referenceIndexByKey.get(mergeKeyOf(assertion.b))
|
|
390
|
+
);
|
|
391
|
+
if (findRoot(components, aIndex) === findRoot(components, bIndex) && !state.contradictoryAssertions.has(assertionIndex)) {
|
|
392
|
+
state.contradictoryAssertions.add(assertionIndex);
|
|
393
|
+
addedContradictions.push(assertionIndex);
|
|
213
394
|
}
|
|
214
|
-
return root;
|
|
215
395
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const rightRoot = this.find(right);
|
|
220
|
-
if (leftRoot === rightRoot) {
|
|
221
|
-
return;
|
|
396
|
+
state.undo.push(() => {
|
|
397
|
+
for (const assertionIndex of addedContradictions) {
|
|
398
|
+
state.contradictoryAssertions.delete(assertionIndex);
|
|
222
399
|
}
|
|
223
|
-
|
|
224
|
-
|
|
400
|
+
components.violatesOntology[parentRoot] = previousViolation;
|
|
401
|
+
for (const assertionIndex of addedDifferent) {
|
|
402
|
+
parentDifferent.delete(assertionIndex);
|
|
403
|
+
}
|
|
404
|
+
for (const { kind, previousFirstReference } of changedKinds) {
|
|
405
|
+
if (previousFirstReference === void 0) parentKinds.delete(kind);
|
|
406
|
+
else parentKinds.set(kind, previousFirstReference);
|
|
407
|
+
}
|
|
408
|
+
components.sizes[parentRoot] = previousParentSize;
|
|
409
|
+
components.parent[childRoot] = childRoot;
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
function activateReference(state, referenceIndex) {
|
|
413
|
+
if (state.activeReferences[referenceIndex] === true) return;
|
|
414
|
+
const reference = requireDefined(state.references[referenceIndex]);
|
|
415
|
+
const peers = [...state.activeReferencesById.get(reference.id) ?? []];
|
|
416
|
+
let addedSharedIdViolations = 0;
|
|
417
|
+
for (const peerIndex of peers) {
|
|
418
|
+
const peer = requireDefined(state.references[peerIndex]);
|
|
419
|
+
if (areDisjoint(state, reference.kind, peer.kind)) {
|
|
420
|
+
addedSharedIdViolations += 1;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
state.sharedIdViolationCount.value += addedSharedIdViolations;
|
|
424
|
+
state.activeReferences[referenceIndex] = true;
|
|
425
|
+
const activeForId = state.activeReferencesById.get(reference.id) ?? /* @__PURE__ */ new Set();
|
|
426
|
+
activeForId.add(referenceIndex);
|
|
427
|
+
state.activeReferencesById.set(reference.id, activeForId);
|
|
428
|
+
state.undo.push(() => {
|
|
429
|
+
activeForId.delete(referenceIndex);
|
|
430
|
+
if (activeForId.size === 0) {
|
|
431
|
+
state.activeReferencesById.delete(reference.id);
|
|
432
|
+
}
|
|
433
|
+
state.activeReferences[referenceIndex] = false;
|
|
434
|
+
state.sharedIdViolationCount.value -= addedSharedIdViolations;
|
|
435
|
+
});
|
|
436
|
+
if (state.context.sameIdAcrossKinds === "fold") {
|
|
437
|
+
for (const peerIndex of peers) {
|
|
438
|
+
unionReferences(state, referenceIndex, peerIndex);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
function addDifferentAssertion(state, assertionIndex) {
|
|
443
|
+
const assertion = requireDefined(state.assertions[assertionIndex]);
|
|
444
|
+
const aIndex = requireDefined(
|
|
445
|
+
state.referenceIndexByKey.get(mergeKeyOf(assertion.a))
|
|
446
|
+
);
|
|
447
|
+
const bIndex = requireDefined(
|
|
448
|
+
state.referenceIndexByKey.get(mergeKeyOf(assertion.b))
|
|
449
|
+
);
|
|
450
|
+
const aRoot = findRoot(state.components, aIndex);
|
|
451
|
+
const bRoot = findRoot(state.components, bIndex);
|
|
452
|
+
if (aRoot === bRoot) {
|
|
453
|
+
state.contradictoryAssertions.add(assertionIndex);
|
|
454
|
+
state.undo.push(() => {
|
|
455
|
+
state.contradictoryAssertions.delete(assertionIndex);
|
|
456
|
+
});
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
const aDifferent = requireDefined(
|
|
460
|
+
state.components.differentAssertions[aRoot]
|
|
461
|
+
);
|
|
462
|
+
const bDifferent = requireDefined(
|
|
463
|
+
state.components.differentAssertions[bRoot]
|
|
464
|
+
);
|
|
465
|
+
aDifferent.add(assertionIndex);
|
|
466
|
+
bDifferent.add(assertionIndex);
|
|
467
|
+
state.undo.push(() => {
|
|
468
|
+
aDifferent.delete(assertionIndex);
|
|
469
|
+
bDifferent.delete(assertionIndex);
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
function rollback(state, snapshot) {
|
|
473
|
+
while (state.undo.length > snapshot) {
|
|
474
|
+
requireDefined(state.undo.pop())();
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
function firstDisjointPair(state, kinds) {
|
|
478
|
+
for (const [index, left] of kinds.entries()) {
|
|
479
|
+
for (const right of kinds.slice(index + 1)) {
|
|
480
|
+
if (areDisjoint(state, left, right)) return [left, right];
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
return void 0;
|
|
484
|
+
}
|
|
485
|
+
function firstAssertionIndex(assertionIndexes) {
|
|
486
|
+
let first = Number.POSITIVE_INFINITY;
|
|
487
|
+
for (const assertionIndex of assertionIndexes) {
|
|
488
|
+
first = Math.min(first, assertionIndex);
|
|
489
|
+
}
|
|
490
|
+
return first;
|
|
491
|
+
}
|
|
492
|
+
function activeClassMembers(state, root) {
|
|
493
|
+
return state.references.filter(
|
|
494
|
+
(_reference, index) => state.activeReferences[index] === true && findRoot(state.components, index) === root
|
|
495
|
+
).toSorted(
|
|
496
|
+
(left, right) => compareMergeKeys(
|
|
497
|
+
mergeKey(left.kind, left.id),
|
|
498
|
+
mergeKey(right.kind, right.id)
|
|
499
|
+
)
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
function componentKindsInReferenceOrder(state, root) {
|
|
503
|
+
return [...requireDefined(state.components.kindFirstReference[root])].toSorted((left, right) => left[1] - right[1]).map(([kind]) => kind);
|
|
504
|
+
}
|
|
505
|
+
function assertCoordinateConsistent(state) {
|
|
506
|
+
if (state.sharedIdViolationCount.value > 0) {
|
|
507
|
+
const activeById = /* @__PURE__ */ new Map();
|
|
508
|
+
for (const [index, reference] of state.references.entries()) {
|
|
509
|
+
if (state.activeReferences[index] !== true) continue;
|
|
510
|
+
const references = activeById.get(reference.id) ?? [];
|
|
511
|
+
references.push(reference);
|
|
512
|
+
activeById.set(reference.id, references);
|
|
513
|
+
}
|
|
514
|
+
for (const [sharedId, references] of activeById) {
|
|
515
|
+
const disjointKinds = firstDisjointPair(state, [
|
|
516
|
+
...new Set(references.map((reference) => reference.kind))
|
|
517
|
+
]);
|
|
518
|
+
if (disjointKinds === void 0) continue;
|
|
519
|
+
throw new IdentityMergeConflictError(
|
|
520
|
+
"The merged graph would give one id to two ontology-disjoint kinds.",
|
|
521
|
+
{ details: { disjointKinds, sharedId } }
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
if (state.contradictoryAssertions.size > 0) {
|
|
526
|
+
const assertionIndex = firstAssertionIndex(state.contradictoryAssertions);
|
|
527
|
+
const assertion = requireDefined(state.assertions[assertionIndex]);
|
|
528
|
+
const root = findRoot(
|
|
529
|
+
state.components,
|
|
530
|
+
requireDefined(state.referenceIndexByKey.get(mergeKeyOf(assertion.a)))
|
|
531
|
+
);
|
|
532
|
+
throw new IdentityMergeConflictError(
|
|
533
|
+
"The merged identity ledger would assert one pair of nodes is both the same and different.",
|
|
534
|
+
{
|
|
535
|
+
details: {
|
|
536
|
+
assertion,
|
|
537
|
+
sameClass: activeClassMembers(state, root)
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
const checkedRoots = /* @__PURE__ */ new Set();
|
|
543
|
+
for (const [referenceIndex, active] of state.activeReferences.entries()) {
|
|
544
|
+
if (!active) continue;
|
|
545
|
+
const root = findRoot(state.components, referenceIndex);
|
|
546
|
+
if (checkedRoots.has(root)) continue;
|
|
547
|
+
checkedRoots.add(root);
|
|
548
|
+
if (!requireDefined(state.components.violatesOntology[root])) continue;
|
|
549
|
+
const sameClass = activeClassMembers(state, root);
|
|
550
|
+
const disjointKinds = firstDisjointPair(
|
|
551
|
+
state,
|
|
552
|
+
componentKindsInReferenceOrder(state, root)
|
|
553
|
+
);
|
|
554
|
+
if (disjointKinds === void 0) continue;
|
|
555
|
+
throw new IdentityMergeConflictError(
|
|
556
|
+
"The merged identity ledger would join two ontology-disjoint kinds into one class.",
|
|
557
|
+
{ details: { disjointKinds, sameClass } }
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
function addRangeEvent(events, node, left, right, start, end, add) {
|
|
562
|
+
if (start <= left && right <= end) {
|
|
563
|
+
add(requireDefined(events[node]));
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
const middle = Math.floor((left + right) / 2);
|
|
567
|
+
if (start < middle) {
|
|
568
|
+
addRangeEvent(events, node * 2, left, middle, start, end, add);
|
|
569
|
+
}
|
|
570
|
+
if (middle < end) {
|
|
571
|
+
addRangeEvent(events, node * 2 + 1, middle, right, start, end, add);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
function visitTemporalEvents(events, state, node, left, right) {
|
|
575
|
+
const snapshot = state.undo.length;
|
|
576
|
+
const event = requireDefined(events[node]);
|
|
577
|
+
for (const referenceIndex of event.references) {
|
|
578
|
+
activateReference(state, referenceIndex);
|
|
579
|
+
}
|
|
580
|
+
for (const assertionIndex of event.sameAssertions) {
|
|
581
|
+
const assertion = requireDefined(state.assertions[assertionIndex]);
|
|
582
|
+
unionReferences(
|
|
583
|
+
state,
|
|
584
|
+
requireDefined(state.referenceIndexByKey.get(mergeKeyOf(assertion.a))),
|
|
585
|
+
requireDefined(state.referenceIndexByKey.get(mergeKeyOf(assertion.b)))
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
for (const assertionIndex of event.differentAssertions) {
|
|
589
|
+
addDifferentAssertion(state, assertionIndex);
|
|
590
|
+
}
|
|
591
|
+
if (right - left === 1) {
|
|
592
|
+
assertCoordinateConsistent(state);
|
|
593
|
+
} else {
|
|
594
|
+
const middle = Math.floor((left + right) / 2);
|
|
595
|
+
visitTemporalEvents(events, state, node * 2, left, middle);
|
|
596
|
+
visitTemporalEvents(events, state, node * 2 + 1, middle, right);
|
|
597
|
+
}
|
|
598
|
+
rollback(state, snapshot);
|
|
599
|
+
}
|
|
600
|
+
function mergeRanges(ranges) {
|
|
601
|
+
const merged = [];
|
|
602
|
+
for (const range of ranges.toSorted(
|
|
603
|
+
(left, right) => left.start - right.start
|
|
604
|
+
)) {
|
|
605
|
+
const previous = merged.at(-1);
|
|
606
|
+
if (previous === void 0 || previous.end < range.start) {
|
|
607
|
+
merged.push({ ...range });
|
|
225
608
|
} else {
|
|
226
|
-
|
|
609
|
+
previous.end = Math.max(previous.end, range.end);
|
|
227
610
|
}
|
|
228
611
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
612
|
+
return merged;
|
|
613
|
+
}
|
|
614
|
+
function assertTemporalIdentityClosureConsistent(assertions, identityContext, nodeUniverse) {
|
|
615
|
+
const boundaries = /* @__PURE__ */ new Set();
|
|
616
|
+
for (const assertion of assertions) {
|
|
617
|
+
boundaries.add(assertion.validFrom);
|
|
618
|
+
if (assertion.validTo !== void 0) boundaries.add(assertion.validTo);
|
|
232
619
|
}
|
|
233
|
-
|
|
620
|
+
const sortedBoundaries = [...boundaries].toSorted();
|
|
621
|
+
const leafCount = Math.max(1, sortedBoundaries.length);
|
|
622
|
+
const boundaryIndex = new Map(
|
|
623
|
+
sortedBoundaries.map((boundary, index) => [boundary, index])
|
|
624
|
+
);
|
|
625
|
+
const assertionRanges = assertions.map(
|
|
626
|
+
(assertion, assertionIndex) => ({
|
|
627
|
+
assertionIndex,
|
|
628
|
+
start: requireDefined(boundaryIndex.get(assertion.validFrom)),
|
|
629
|
+
end: assertion.validTo === void 0 ? leafCount : requireDefined(boundaryIndex.get(assertion.validTo))
|
|
630
|
+
})
|
|
631
|
+
);
|
|
632
|
+
const references = [];
|
|
633
|
+
const referenceIndexByKey = /* @__PURE__ */ new Map();
|
|
634
|
+
const addReference = (reference) => {
|
|
635
|
+
const key = mergeKey(reference.kind, reference.id);
|
|
636
|
+
const existing = referenceIndexByKey.get(key);
|
|
637
|
+
if (existing !== void 0) return existing;
|
|
638
|
+
const index = references.length;
|
|
639
|
+
references.push(reference);
|
|
640
|
+
referenceIndexByKey.set(key, index);
|
|
641
|
+
return index;
|
|
642
|
+
};
|
|
643
|
+
const alwaysActive = new Set(nodeUniverse.map((node) => addReference(node)));
|
|
644
|
+
const rangesByReference = /* @__PURE__ */ new Map();
|
|
645
|
+
for (const range of assertionRanges) {
|
|
646
|
+
if (range.start >= range.end) continue;
|
|
647
|
+
const assertion = requireDefined(assertions[range.assertionIndex]);
|
|
648
|
+
for (const endpoint of [assertion.a, assertion.b]) {
|
|
649
|
+
const referenceIndex = addReference(endpoint);
|
|
650
|
+
const ranges = rangesByReference.get(referenceIndex) ?? [];
|
|
651
|
+
ranges.push({ start: range.start, end: range.end });
|
|
652
|
+
rangesByReference.set(referenceIndex, ranges);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
const events = Array.from(
|
|
656
|
+
{ length: leafCount * 4 },
|
|
657
|
+
() => ({ references: [], sameAssertions: [], differentAssertions: [] })
|
|
658
|
+
);
|
|
659
|
+
for (const [referenceIndex, ranges] of rangesByReference) {
|
|
660
|
+
if (alwaysActive.has(referenceIndex)) continue;
|
|
661
|
+
for (const range of mergeRanges(ranges)) {
|
|
662
|
+
addRangeEvent(
|
|
663
|
+
events,
|
|
664
|
+
1,
|
|
665
|
+
0,
|
|
666
|
+
leafCount,
|
|
667
|
+
range.start,
|
|
668
|
+
range.end,
|
|
669
|
+
(event) => event.references.push(referenceIndex)
|
|
670
|
+
);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
for (const range of assertionRanges) {
|
|
674
|
+
if (range.start >= range.end) continue;
|
|
675
|
+
const assertion = requireDefined(assertions[range.assertionIndex]);
|
|
676
|
+
addRangeEvent(
|
|
677
|
+
events,
|
|
678
|
+
1,
|
|
679
|
+
0,
|
|
680
|
+
leafCount,
|
|
681
|
+
range.start,
|
|
682
|
+
range.end,
|
|
683
|
+
(event) => assertion.relation === "same" ? event.sameAssertions.push(range.assertionIndex) : event.differentAssertions.push(range.assertionIndex)
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
const state = {
|
|
687
|
+
assertions,
|
|
688
|
+
context: identityContext,
|
|
689
|
+
references,
|
|
690
|
+
referenceIndexByKey,
|
|
691
|
+
activeReferences: references.map(() => false),
|
|
692
|
+
activeReferencesById: /* @__PURE__ */ new Map(),
|
|
693
|
+
components: {
|
|
694
|
+
parent: references.map((_reference, index) => index),
|
|
695
|
+
sizes: references.map(() => 1),
|
|
696
|
+
kindFirstReference: references.map(
|
|
697
|
+
(reference, index) => /* @__PURE__ */ new Map([[reference.kind, index]])
|
|
698
|
+
),
|
|
699
|
+
differentAssertions: references.map(() => /* @__PURE__ */ new Set()),
|
|
700
|
+
violatesOntology: references.map(() => false)
|
|
701
|
+
},
|
|
702
|
+
contradictoryAssertions: /* @__PURE__ */ new Set(),
|
|
703
|
+
undo: [],
|
|
704
|
+
disjointCache: /* @__PURE__ */ new Map(),
|
|
705
|
+
sharedIdViolationCount: { value: 0 }
|
|
706
|
+
};
|
|
707
|
+
for (const referenceIndex of alwaysActive)
|
|
708
|
+
activateReference(state, referenceIndex);
|
|
709
|
+
state.undo.length = 0;
|
|
710
|
+
visitTemporalEvents(events, state, 1, 0, leafCount);
|
|
711
|
+
}
|
|
234
712
|
|
|
235
713
|
// src/graph-merge/merge-identity.ts
|
|
236
714
|
function endpointTuple(assertion) {
|
|
@@ -246,12 +724,18 @@ function identitySemanticKey(assertion) {
|
|
|
246
724
|
assertion.b
|
|
247
725
|
);
|
|
248
726
|
}
|
|
727
|
+
function identityDedupeKey(assertion) {
|
|
728
|
+
const semantic = identitySemanticKey(assertion);
|
|
729
|
+
if (assertion.validTo === void 0) return semantic;
|
|
730
|
+
return encodeTupleKey([semantic, assertion.validFrom, assertion.validTo]);
|
|
731
|
+
}
|
|
249
732
|
function compareIdentitySurvivors(left, right) {
|
|
250
733
|
const byValidity = compareCodePoints(left.validFrom, right.validFrom);
|
|
251
734
|
return byValidity === 0 ? compareCodePoints(left.id, right.id) : byValidity;
|
|
252
735
|
}
|
|
253
736
|
var DUPLICATE_IDENTITY_ASSERTION_DROP_REASON = "identity:duplicate-assertion";
|
|
254
737
|
var REDUNDANT_IDENTITY_ASSERTION_DROP_REASON = "identity:endpoints-collapsed";
|
|
738
|
+
var EMPTY_REMAPPED_IDENTITY_WINDOW_DROP_REASON = "identity:empty-remapped-window";
|
|
255
739
|
function droppedIdentityAssertion(assertion, reason) {
|
|
256
740
|
return { kind: "identity", id: assertion.id, reason };
|
|
257
741
|
}
|
|
@@ -259,7 +743,7 @@ function dedupeIdentityAssertions(assertions, committedIds) {
|
|
|
259
743
|
const survivorBySemantic = /* @__PURE__ */ new Map();
|
|
260
744
|
const dropped = [];
|
|
261
745
|
for (const assertion of assertions) {
|
|
262
|
-
const key =
|
|
746
|
+
const key = identityDedupeKey(assertion);
|
|
263
747
|
const previous = survivorBySemantic.get(key);
|
|
264
748
|
if (previous === void 0) {
|
|
265
749
|
survivorBySemantic.set(key, assertion);
|
|
@@ -278,7 +762,7 @@ function dedupeIdentityAssertions(assertions, committedIds) {
|
|
|
278
762
|
}
|
|
279
763
|
return {
|
|
280
764
|
survivors: [...survivorBySemantic.values()].toSorted(
|
|
281
|
-
(left, right) => compareCodePoints(
|
|
765
|
+
(left, right) => compareCodePoints(identityDedupeKey(left), identityDedupeKey(right))
|
|
282
766
|
),
|
|
283
767
|
dropped
|
|
284
768
|
};
|
|
@@ -292,13 +776,26 @@ function assertNoOpposingIdentityRelations(assertions) {
|
|
|
292
776
|
byEndpoint.set(key, group);
|
|
293
777
|
}
|
|
294
778
|
for (const [endpoint, group] of byEndpoint) {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
throw new IdentityMergeConflictError(
|
|
299
|
-
"Branches asserted opposing identity relations for one endpoint pair.",
|
|
300
|
-
{ details: { endpoint, assertions: group } }
|
|
779
|
+
const same = group.filter((assertion) => assertion.relation === "same");
|
|
780
|
+
const different = group.filter(
|
|
781
|
+
(assertion) => assertion.relation === "different"
|
|
301
782
|
);
|
|
783
|
+
for (const sameAssertion of same) {
|
|
784
|
+
for (const differentAssertion of different) {
|
|
785
|
+
if (!identityValidityWindowsOverlap(sameAssertion, differentAssertion)) {
|
|
786
|
+
continue;
|
|
787
|
+
}
|
|
788
|
+
throw new IdentityMergeConflictError(
|
|
789
|
+
"Branches asserted opposing identity relations for one endpoint pair.",
|
|
790
|
+
{
|
|
791
|
+
details: {
|
|
792
|
+
endpoint,
|
|
793
|
+
assertions: [sameAssertion, differentAssertion]
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
302
799
|
}
|
|
303
800
|
}
|
|
304
801
|
function assertNoRetractReassertRace(staging) {
|
|
@@ -415,9 +912,10 @@ function resolveIdentityEndpoint(endpoint, canonicalOf, retypeMap) {
|
|
|
415
912
|
id: idOf(canonical)
|
|
416
913
|
};
|
|
417
914
|
}
|
|
418
|
-
function remapIdentityAssertionEndpoints(assertions, canonicalOf, retypeMap, storedRowsById) {
|
|
915
|
+
function remapIdentityAssertionEndpoints(assertions, canonicalOf, retypeMap, storedRowsById, endpointWindows = /* @__PURE__ */ new Map()) {
|
|
419
916
|
const remapped = [];
|
|
420
917
|
const dropped = [];
|
|
918
|
+
const narrowingWarningById = /* @__PURE__ */ new Map();
|
|
421
919
|
for (const assertion of assertions) {
|
|
422
920
|
const remappedA = resolveIdentityEndpoint(
|
|
423
921
|
assertion.a,
|
|
@@ -445,7 +943,47 @@ function remapIdentityAssertionEndpoints(assertions, canonicalOf, retypeMap, sto
|
|
|
445
943
|
continue;
|
|
446
944
|
}
|
|
447
945
|
const [a, b] = normalizeIdentityPair(remappedA, remappedB);
|
|
448
|
-
const
|
|
946
|
+
const endpointWindowA = endpointWindows.get(mergeKeyOf(a));
|
|
947
|
+
const endpointWindowB = endpointWindows.get(mergeKeyOf(b));
|
|
948
|
+
const validFrom = [
|
|
949
|
+
assertion.validFrom,
|
|
950
|
+
endpointWindowA?.validFrom,
|
|
951
|
+
endpointWindowB?.validFrom
|
|
952
|
+
].filter((bound) => bound !== void 0).reduce(
|
|
953
|
+
(latest, bound) => compareCodePoints(latest, bound) < 0 ? bound : latest
|
|
954
|
+
);
|
|
955
|
+
const validTo = [
|
|
956
|
+
assertion.validTo,
|
|
957
|
+
endpointWindowA?.validTo,
|
|
958
|
+
endpointWindowB?.validTo
|
|
959
|
+
].filter((bound) => bound !== void 0).reduce(
|
|
960
|
+
(earliest, bound) => earliest === void 0 || compareCodePoints(bound, earliest) < 0 ? bound : earliest,
|
|
961
|
+
void 0
|
|
962
|
+
);
|
|
963
|
+
if (validTo !== void 0 && compareCodePoints(validFrom, validTo) >= 0) {
|
|
964
|
+
dropped.push(
|
|
965
|
+
droppedIdentityAssertion(
|
|
966
|
+
assertion,
|
|
967
|
+
EMPTY_REMAPPED_IDENTITY_WINDOW_DROP_REASON
|
|
968
|
+
)
|
|
969
|
+
);
|
|
970
|
+
continue;
|
|
971
|
+
}
|
|
972
|
+
if (validFrom !== assertion.validFrom || validTo !== assertion.validTo) {
|
|
973
|
+
const originalUpper = assertion.validTo ?? "open";
|
|
974
|
+
const remappedUpper = validTo ?? "open";
|
|
975
|
+
narrowingWarningById.set(
|
|
976
|
+
assertion.id,
|
|
977
|
+
`Identity assertion ${JSON.stringify(assertion.id)} was narrowed from [${assertion.validFrom}, ${originalUpper}) to [${validFrom}, ${remappedUpper}) to fit its remapped endpoint windows.`
|
|
978
|
+
);
|
|
979
|
+
}
|
|
980
|
+
const result = {
|
|
981
|
+
...assertion,
|
|
982
|
+
a,
|
|
983
|
+
b,
|
|
984
|
+
validFrom,
|
|
985
|
+
...validTo === void 0 ? {} : { validTo }
|
|
986
|
+
};
|
|
449
987
|
const stored = storedRowsById.get(assertion.id);
|
|
450
988
|
if (stored !== void 0 && assertionTruthKey(stored) === assertionTruthKey(assertion) && assertionTruthKey(result) !== assertionTruthKey(assertion)) {
|
|
451
989
|
throw new IdentityMergeConflictError(
|
|
@@ -468,7 +1006,11 @@ function remapIdentityAssertionEndpoints(assertions, canonicalOf, retypeMap, sto
|
|
|
468
1006
|
assertNoOpposingIdentityRelations(deduped.survivors);
|
|
469
1007
|
return {
|
|
470
1008
|
assertions: deduped.survivors,
|
|
471
|
-
dropped: [...dropped, ...deduped.dropped]
|
|
1009
|
+
dropped: [...dropped, ...deduped.dropped],
|
|
1010
|
+
warnings: deduped.survivors.flatMap((assertion) => {
|
|
1011
|
+
const warning = narrowingWarningById.get(assertion.id);
|
|
1012
|
+
return warning === void 0 ? [] : [warning];
|
|
1013
|
+
})
|
|
472
1014
|
};
|
|
473
1015
|
}
|
|
474
1016
|
function assertIdentityEndpointsNotDeleted(assertions, nodeDeletions, retypeMap) {
|
|
@@ -505,89 +1047,11 @@ function assertNoContradictoryIdentityClosure(plannedAssertions, retractions, ba
|
|
|
505
1047
|
b: resolveIdentityEndpoint(assertion.b, canonicalOf, retypeMap)
|
|
506
1048
|
}))
|
|
507
1049
|
];
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
sameClasses.add(mergeKey(node.kind, node.id));
|
|
513
|
-
}
|
|
514
|
-
for (const assertion of mergedLedger) {
|
|
515
|
-
if (assertion.relation === "same") {
|
|
516
|
-
sameClasses.union(mergeKeyOf(assertion.a), mergeKeyOf(assertion.b));
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
const membersOfClass = (root) => sameClasses.members().filter((member) => sameClasses.find(member) === root).map((member) => ({ kind: kindOf(member), id: idOf(member) })).toSorted(
|
|
520
|
-
(left, right) => compareMergeKeys(mergeKeyOf(left), mergeKeyOf(right))
|
|
1050
|
+
assertTemporalIdentityClosureConsistent(
|
|
1051
|
+
mergedLedger,
|
|
1052
|
+
identityContext,
|
|
1053
|
+
nodeUniverse
|
|
521
1054
|
);
|
|
522
|
-
const firstDisjointPair = (kinds) => {
|
|
523
|
-
for (const [index, left] of kinds.entries()) {
|
|
524
|
-
for (const right of kinds.slice(index + 1)) {
|
|
525
|
-
if (identityContext.areDisjoint(left, right)) return [left, right];
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
return void 0;
|
|
529
|
-
};
|
|
530
|
-
const refsById = /* @__PURE__ */ new Map();
|
|
531
|
-
const addRef = (kind, id) => {
|
|
532
|
-
const keys = refsById.get(id) ?? [];
|
|
533
|
-
keys.push(mergeKey(kind, id));
|
|
534
|
-
refsById.set(id, keys);
|
|
535
|
-
};
|
|
536
|
-
for (const node of nodeUniverse) addRef(node.kind, node.id);
|
|
537
|
-
for (const assertion of mergedLedger) {
|
|
538
|
-
addRef(assertion.a.kind, assertion.a.id);
|
|
539
|
-
addRef(assertion.b.kind, assertion.b.id);
|
|
540
|
-
}
|
|
541
|
-
for (const keys of refsById.values()) {
|
|
542
|
-
const disjointKinds = firstDisjointPair([
|
|
543
|
-
...new Set(keys.map((key) => kindOf(key)))
|
|
544
|
-
]);
|
|
545
|
-
if (disjointKinds === void 0) continue;
|
|
546
|
-
throw new IdentityMergeConflictError(
|
|
547
|
-
"The merged graph would give one id to two ontology-disjoint kinds.",
|
|
548
|
-
{
|
|
549
|
-
details: {
|
|
550
|
-
disjointKinds,
|
|
551
|
-
sharedId: idOf(requireDefined(keys[0]))
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
);
|
|
555
|
-
}
|
|
556
|
-
if (identityContext.sameIdAcrossKinds === "fold") {
|
|
557
|
-
for (const keys of refsById.values()) {
|
|
558
|
-
const [first, ...rest] = keys;
|
|
559
|
-
if (first === void 0) continue;
|
|
560
|
-
for (const other of rest) sameClasses.union(first, other);
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
for (const assertion of mergedLedger) {
|
|
564
|
-
if (assertion.relation !== "different") {
|
|
565
|
-
continue;
|
|
566
|
-
}
|
|
567
|
-
const root = sameClasses.find(mergeKeyOf(assertion.a));
|
|
568
|
-
if (root !== sameClasses.find(mergeKeyOf(assertion.b))) {
|
|
569
|
-
continue;
|
|
570
|
-
}
|
|
571
|
-
throw new IdentityMergeConflictError(
|
|
572
|
-
"The merged identity ledger would assert one pair of nodes is both the same and different.",
|
|
573
|
-
{ details: { assertion, sameClass: membersOfClass(root) } }
|
|
574
|
-
);
|
|
575
|
-
}
|
|
576
|
-
const kindsByRoot = /* @__PURE__ */ new Map();
|
|
577
|
-
for (const member of sameClasses.members()) {
|
|
578
|
-
const root = sameClasses.find(member);
|
|
579
|
-
const kinds = kindsByRoot.get(root) ?? /* @__PURE__ */ new Set();
|
|
580
|
-
kinds.add(kindOf(member));
|
|
581
|
-
kindsByRoot.set(root, kinds);
|
|
582
|
-
}
|
|
583
|
-
for (const [root, kinds] of kindsByRoot) {
|
|
584
|
-
const disjointKinds = firstDisjointPair([...kinds]);
|
|
585
|
-
if (disjointKinds === void 0) continue;
|
|
586
|
-
throw new IdentityMergeConflictError(
|
|
587
|
-
"The merged identity ledger would join two ontology-disjoint kinds into one class.",
|
|
588
|
-
{ details: { disjointKinds, sameClass: membersOfClass(root) } }
|
|
589
|
-
);
|
|
590
|
-
}
|
|
591
1055
|
}
|
|
592
1056
|
async function buildIdentityPeerProbe(target, probedIds, targetPeers, plan, profile) {
|
|
593
1057
|
const finalIds = /* @__PURE__ */ new Set([
|
|
@@ -608,9 +1072,9 @@ async function buildIdentityPeerProbe(target, probedIds, targetPeers, plan, prof
|
|
|
608
1072
|
seedsByKey.set(mergeKey(kind, id), { kind, id });
|
|
609
1073
|
};
|
|
610
1074
|
for (const entity of plan.canonicalEntities) {
|
|
611
|
-
const
|
|
612
|
-
if (plan.nodeDeletions.has(
|
|
613
|
-
addSeed(plan.retypeMap.get(
|
|
1075
|
+
const sourceKey2 = mergeKey(entity.kind, entity.canonicalId);
|
|
1076
|
+
if (plan.nodeDeletions.has(sourceKey2)) continue;
|
|
1077
|
+
addSeed(plan.retypeMap.get(sourceKey2) ?? entity.kind, entity.canonicalId);
|
|
614
1078
|
}
|
|
615
1079
|
for (const assertion of plan.identityAssertions) {
|
|
616
1080
|
addSeed(assertion.a.kind, assertion.a.id);
|
|
@@ -789,9 +1253,9 @@ function affectedIdentityClassSeeds(plan, profile) {
|
|
|
789
1253
|
}
|
|
790
1254
|
if (profile === "fold") {
|
|
791
1255
|
for (const entity of plan.canonicalEntities) {
|
|
792
|
-
const
|
|
793
|
-
if (plan.nodeDeletions.has(
|
|
794
|
-
addSeed(plan.retypeMap.get(
|
|
1256
|
+
const sourceKey2 = mergeKey(entity.kind, entity.canonicalId);
|
|
1257
|
+
if (plan.nodeDeletions.has(sourceKey2)) continue;
|
|
1258
|
+
addSeed(plan.retypeMap.get(sourceKey2) ?? entity.kind, entity.canonicalId);
|
|
795
1259
|
}
|
|
796
1260
|
}
|
|
797
1261
|
return [...seedsByKey.values()];
|
|
@@ -1126,30 +1590,22 @@ function classifyRetraction(assertion, deletedByKey, forkAssertion) {
|
|
|
1126
1590
|
}
|
|
1127
1591
|
return EXPLICIT_RETRACTION;
|
|
1128
1592
|
}
|
|
1129
|
-
|
|
1130
|
-
if (retracted.length === 0
|
|
1131
|
-
return retracted.map((assertion) => ({
|
|
1132
|
-
assertion,
|
|
1133
|
-
cause: EXPLICIT_RETRACTION
|
|
1134
|
-
}));
|
|
1135
|
-
}
|
|
1593
|
+
function classifyRetractions(retracted, deletedNodes, forkRowsById) {
|
|
1594
|
+
if (retracted.length === 0) return [];
|
|
1136
1595
|
const deletedByKey = new Map(
|
|
1137
1596
|
deletedNodes.map((deletion) => [
|
|
1138
1597
|
mergeKey(deletion.kind, deletion.id),
|
|
1139
1598
|
deletion
|
|
1140
1599
|
])
|
|
1141
1600
|
);
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
forkRowsById.get(assertion.id)
|
|
1151
|
-
)
|
|
1152
|
-
}));
|
|
1601
|
+
return retracted.map((assertion) => {
|
|
1602
|
+
const forkAssertion = forkRowsById.get(assertion.id);
|
|
1603
|
+
const endedAssertion = forkAssertion?.validTo !== void 0 && assertionIdentityKey(forkAssertion) === assertionIdentityKey(assertion) ? forkAssertion : assertion;
|
|
1604
|
+
return {
|
|
1605
|
+
assertion: endedAssertion,
|
|
1606
|
+
cause: classifyRetraction(assertion, deletedByKey, forkAssertion)
|
|
1607
|
+
};
|
|
1608
|
+
});
|
|
1153
1609
|
}
|
|
1154
1610
|
function byId(left, right) {
|
|
1155
1611
|
return compareStrings(left.id, right.id);
|
|
@@ -1159,8 +1615,8 @@ async function diffAgainstBase(baseStore, forkStore, captureForkState = true) {
|
|
|
1159
1615
|
const nodeKinds = getNodeKinds(graph);
|
|
1160
1616
|
const edgeKinds = getEdgeKinds(graph);
|
|
1161
1617
|
const [baseIdentity, forkIdentity] = await Promise.all([
|
|
1162
|
-
storeRuntime(baseStore).readCurrentIdentityAssertions("
|
|
1163
|
-
storeRuntime(forkStore).readCurrentIdentityAssertions("
|
|
1618
|
+
storeRuntime(baseStore).readCurrentIdentityAssertions("archival"),
|
|
1619
|
+
storeRuntime(forkStore).readCurrentIdentityAssertions("archival")
|
|
1164
1620
|
]);
|
|
1165
1621
|
const baseIdentityById = new Map(
|
|
1166
1622
|
baseIdentity.map((assertion) => [assertion.id, assertion])
|
|
@@ -1252,10 +1708,10 @@ async function diffAgainstBase(baseStore, forkStore, captureForkState = true) {
|
|
|
1252
1708
|
const fork = forkIdentityById.get(assertion.id);
|
|
1253
1709
|
return fork === void 0 || assertionTruthKey(fork) !== assertionTruthKey(assertion);
|
|
1254
1710
|
}).toSorted((left, right) => compareStrings(left.id, right.id));
|
|
1255
|
-
const classifiedRetractions =
|
|
1256
|
-
forkStore,
|
|
1711
|
+
const classifiedRetractions = classifyRetractions(
|
|
1257
1712
|
retractedAssertions,
|
|
1258
|
-
deletedNodes
|
|
1713
|
+
deletedNodes,
|
|
1714
|
+
forkIdentityById
|
|
1259
1715
|
);
|
|
1260
1716
|
return {
|
|
1261
1717
|
nodes: {
|
|
@@ -1281,7 +1737,7 @@ async function diffAgainstBase(baseStore, forkStore, captureForkState = true) {
|
|
|
1281
1737
|
// unsupported id reuse as a typed conflict.
|
|
1282
1738
|
new: forkIdentity.filter((assertion) => {
|
|
1283
1739
|
const base = baseIdentityById.get(assertion.id);
|
|
1284
|
-
return base === void 0 || assertionTruthKey(base) !== assertionTruthKey(assertion);
|
|
1740
|
+
return base === void 0 || assertionIdentityKey(base) !== assertionIdentityKey(assertion) && assertionTruthKey(base) !== assertionTruthKey(assertion);
|
|
1285
1741
|
}).toSorted((left, right) => compareStrings(left.id, right.id)),
|
|
1286
1742
|
retracted: classifiedRetractions
|
|
1287
1743
|
},
|
|
@@ -1527,6 +1983,14 @@ var UNIQUE_KEY_PREFIX = "u";
|
|
|
1527
1983
|
function isUniqueBucketKey(key) {
|
|
1528
1984
|
return key.startsWith(`${UNIQUE_KEY_PREFIX}${KEY_PART_SEPARATOR}`);
|
|
1529
1985
|
}
|
|
1986
|
+
function uniqueConstraintNameForBucket(key) {
|
|
1987
|
+
if (!isUniqueBucketKey(key)) {
|
|
1988
|
+
return void 0;
|
|
1989
|
+
}
|
|
1990
|
+
const constraintStart = UNIQUE_KEY_PREFIX.length + KEY_PART_SEPARATOR.length;
|
|
1991
|
+
const constraintEnd = key.indexOf(KEY_PART_SEPARATOR, constraintStart);
|
|
1992
|
+
return constraintEnd === -1 ? void 0 : key.slice(constraintStart, constraintEnd);
|
|
1993
|
+
}
|
|
1530
1994
|
function constraintSignature(node, constraint) {
|
|
1531
1995
|
const props = node;
|
|
1532
1996
|
for (const field of constraint.fields) {
|
|
@@ -1874,7 +2338,10 @@ function canonicalizeCluster(cluster, members, policy, branchRank, weights, cano
|
|
|
1874
2338
|
canonicalId,
|
|
1875
2339
|
memberIds,
|
|
1876
2340
|
kind,
|
|
1877
|
-
branchOrigins
|
|
2341
|
+
branchOrigins,
|
|
2342
|
+
// The merge orchestrator replaces this with the witness selected from the
|
|
2343
|
+
// post-guard surviving edge set. Canonicalization never re-derives evidence.
|
|
2344
|
+
decisiveEdges: []
|
|
1878
2345
|
};
|
|
1879
2346
|
const windowSource = members.find(
|
|
1880
2347
|
(member) => member.branchId === COMMITTED_TARGET_BRANCH && member.kind === kind && member.id === canonicalId
|
|
@@ -1886,10 +2353,59 @@ function canonicalizeCluster(cluster, members, policy, branchRank, weights, cano
|
|
|
1886
2353
|
resolution,
|
|
1887
2354
|
conflicts,
|
|
1888
2355
|
...windowSource.validFrom === void 0 ? {} : { validFrom: windowSource.validFrom },
|
|
1889
|
-
...windowSource.validTo === void 0 ? {} : { validTo: windowSource.validTo }
|
|
2356
|
+
...windowSource.validTo === void 0 ? {} : { validTo: windowSource.validTo },
|
|
2357
|
+
...windowSource.validFrom === void 0 ? {} : { endpointValidFrom: windowSource.validFrom },
|
|
2358
|
+
...windowSource.validTo === void 0 ? {} : { endpointValidTo: windowSource.validTo }
|
|
1890
2359
|
};
|
|
1891
2360
|
}
|
|
1892
2361
|
|
|
2362
|
+
// src/graph-merge/union-find.ts
|
|
2363
|
+
var UnionFind = class {
|
|
2364
|
+
constructor(compare) {
|
|
2365
|
+
this.compare = compare;
|
|
2366
|
+
}
|
|
2367
|
+
compare;
|
|
2368
|
+
parent = /* @__PURE__ */ new Map();
|
|
2369
|
+
/** Adds `value` as its own singleton set if it is not already present. */
|
|
2370
|
+
add(value) {
|
|
2371
|
+
if (!this.parent.has(value)) {
|
|
2372
|
+
this.parent.set(value, value);
|
|
2373
|
+
}
|
|
2374
|
+
}
|
|
2375
|
+
/** Returns the representative of `value`'s set, compressing the path to it. */
|
|
2376
|
+
find(value) {
|
|
2377
|
+
this.add(value);
|
|
2378
|
+
let root = value;
|
|
2379
|
+
while (this.parent.get(root) !== root) {
|
|
2380
|
+
root = requireDefined(this.parent.get(root));
|
|
2381
|
+
}
|
|
2382
|
+
let cursor = value;
|
|
2383
|
+
while (cursor !== root) {
|
|
2384
|
+
const next = requireDefined(this.parent.get(cursor));
|
|
2385
|
+
this.parent.set(cursor, root);
|
|
2386
|
+
cursor = next;
|
|
2387
|
+
}
|
|
2388
|
+
return root;
|
|
2389
|
+
}
|
|
2390
|
+
/** Merges the sets of `left` and `right`; the `compare`-minimal root wins. */
|
|
2391
|
+
union(left, right) {
|
|
2392
|
+
const leftRoot = this.find(left);
|
|
2393
|
+
const rightRoot = this.find(right);
|
|
2394
|
+
if (leftRoot === rightRoot) {
|
|
2395
|
+
return;
|
|
2396
|
+
}
|
|
2397
|
+
if (this.compare(leftRoot, rightRoot) <= 0) {
|
|
2398
|
+
this.parent.set(rightRoot, leftRoot);
|
|
2399
|
+
} else {
|
|
2400
|
+
this.parent.set(leftRoot, rightRoot);
|
|
2401
|
+
}
|
|
2402
|
+
}
|
|
2403
|
+
/** Every value seen by {@link add}/{@link find}/{@link union}, insertion order. */
|
|
2404
|
+
members() {
|
|
2405
|
+
return [...this.parent.keys()];
|
|
2406
|
+
}
|
|
2407
|
+
};
|
|
2408
|
+
|
|
1893
2409
|
// src/graph-merge/closures.ts
|
|
1894
2410
|
var SUB_CLASS_OF_META_EDGE = "subClassOf";
|
|
1895
2411
|
var EQUIVALENCE_META_EDGES = /* @__PURE__ */ new Set([
|
|
@@ -1944,6 +2460,101 @@ function isReachable2(closure, from, to) {
|
|
|
1944
2460
|
return isReachable(closure.closure, fromRep, toRep);
|
|
1945
2461
|
}
|
|
1946
2462
|
|
|
2463
|
+
// src/graph-merge/evidence.ts
|
|
2464
|
+
function entityRef(key) {
|
|
2465
|
+
return { kind: kindOf(key), id: idOf(key) };
|
|
2466
|
+
}
|
|
2467
|
+
var SOURCE_KIND_ORDER = {
|
|
2468
|
+
block: 0,
|
|
2469
|
+
unique: 1,
|
|
2470
|
+
baseUnique: 2,
|
|
2471
|
+
baseIndex: 3,
|
|
2472
|
+
keyless: 4,
|
|
2473
|
+
retype: 5,
|
|
2474
|
+
custom: 6
|
|
2475
|
+
};
|
|
2476
|
+
function sourceKey(source) {
|
|
2477
|
+
switch (source.kind) {
|
|
2478
|
+
case "block":
|
|
2479
|
+
case "keyless":
|
|
2480
|
+
case "retype": {
|
|
2481
|
+
return JSON.stringify([SOURCE_KIND_ORDER[source.kind], source.sourceId]);
|
|
2482
|
+
}
|
|
2483
|
+
case "unique":
|
|
2484
|
+
case "baseUnique": {
|
|
2485
|
+
return JSON.stringify([
|
|
2486
|
+
SOURCE_KIND_ORDER[source.kind],
|
|
2487
|
+
source.sourceId,
|
|
2488
|
+
source.constraintName
|
|
2489
|
+
]);
|
|
2490
|
+
}
|
|
2491
|
+
case "baseIndex": {
|
|
2492
|
+
return JSON.stringify([
|
|
2493
|
+
SOURCE_KIND_ORDER[source.kind],
|
|
2494
|
+
source.sourceId,
|
|
2495
|
+
source.indexName
|
|
2496
|
+
]);
|
|
2497
|
+
}
|
|
2498
|
+
case "custom": {
|
|
2499
|
+
return JSON.stringify([
|
|
2500
|
+
SOURCE_KIND_ORDER.custom,
|
|
2501
|
+
source.sourceId,
|
|
2502
|
+
source.metadata === void 0 ? "" : canonicalValueKey(source.metadata)
|
|
2503
|
+
]);
|
|
2504
|
+
}
|
|
2505
|
+
default: {
|
|
2506
|
+
const exhaustive = source;
|
|
2507
|
+
return exhaustive;
|
|
2508
|
+
}
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
function compareMatchSources(left, right) {
|
|
2512
|
+
return compareStrings(sourceKey(left), sourceKey(right));
|
|
2513
|
+
}
|
|
2514
|
+
function compareEntityRefs(left, right) {
|
|
2515
|
+
return compareMergeKeys(
|
|
2516
|
+
mergeKey(left.kind, left.id),
|
|
2517
|
+
mergeKey(right.kind, right.id)
|
|
2518
|
+
);
|
|
2519
|
+
}
|
|
2520
|
+
function normalizeMatchSources(sources) {
|
|
2521
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
2522
|
+
for (const source of sources) {
|
|
2523
|
+
byKey.set(sourceKey(source), source);
|
|
2524
|
+
}
|
|
2525
|
+
return [...byKey].sort(([, left], [, right]) => compareMatchSources(left, right)).map(([, source]) => source);
|
|
2526
|
+
}
|
|
2527
|
+
var DEFAULT_HYBRID_WEIGHT = 0.5;
|
|
2528
|
+
function describeMatchStrategy(strategy) {
|
|
2529
|
+
switch (strategy.kind) {
|
|
2530
|
+
case "fulltext": {
|
|
2531
|
+
return { kind: "fulltext", fields: [...strategy.fields] };
|
|
2532
|
+
}
|
|
2533
|
+
case "vector": {
|
|
2534
|
+
return { kind: "vector", fields: [strategy.field] };
|
|
2535
|
+
}
|
|
2536
|
+
case "hybrid": {
|
|
2537
|
+
const vector = strategy.weights?.vector ?? DEFAULT_HYBRID_WEIGHT;
|
|
2538
|
+
const fulltext = strategy.weights?.fulltext ?? DEFAULT_HYBRID_WEIGHT;
|
|
2539
|
+
const total = vector + fulltext;
|
|
2540
|
+
const weights = total === 0 ? { vector: DEFAULT_HYBRID_WEIGHT, fulltext: DEFAULT_HYBRID_WEIGHT } : { vector: vector / total, fulltext: fulltext / total };
|
|
2541
|
+
return { kind: "hybrid", fields: [...strategy.fields], weights };
|
|
2542
|
+
}
|
|
2543
|
+
case "custom": {
|
|
2544
|
+
return { kind: "custom" };
|
|
2545
|
+
}
|
|
2546
|
+
default: {
|
|
2547
|
+
const exhaustive = strategy;
|
|
2548
|
+
return exhaustive;
|
|
2549
|
+
}
|
|
2550
|
+
}
|
|
2551
|
+
}
|
|
2552
|
+
function compareMatchEvidence(left, right) {
|
|
2553
|
+
const byA = compareEntityRefs(left.a, right.a);
|
|
2554
|
+
if (byA !== 0) return byA;
|
|
2555
|
+
return compareEntityRefs(left.b, right.b);
|
|
2556
|
+
}
|
|
2557
|
+
|
|
1947
2558
|
// src/graph-merge/similarity.ts
|
|
1948
2559
|
var MIN_SCORE = 0;
|
|
1949
2560
|
var MAX_SCORE = 1;
|
|
@@ -2073,18 +2684,18 @@ function cosineFromLookup(embeddings, leftText, rightText) {
|
|
|
2073
2684
|
}
|
|
2074
2685
|
return cosineSimilarity(leftVector, rightVector);
|
|
2075
2686
|
}
|
|
2076
|
-
var
|
|
2687
|
+
var DEFAULT_HYBRID_WEIGHT2 = 0.5;
|
|
2077
2688
|
function nonNegativeFinite(value) {
|
|
2078
2689
|
return Number.isFinite(value) ? Math.max(0, value) : 0;
|
|
2079
2690
|
}
|
|
2080
2691
|
function normalizeHybridWeights(weights) {
|
|
2081
|
-
const vector = nonNegativeFinite(weights?.vector ??
|
|
2692
|
+
const vector = nonNegativeFinite(weights?.vector ?? DEFAULT_HYBRID_WEIGHT2);
|
|
2082
2693
|
const fulltext = nonNegativeFinite(
|
|
2083
|
-
weights?.fulltext ??
|
|
2694
|
+
weights?.fulltext ?? DEFAULT_HYBRID_WEIGHT2
|
|
2084
2695
|
);
|
|
2085
2696
|
const total = vector + fulltext;
|
|
2086
2697
|
if (total === 0) {
|
|
2087
|
-
return { vector:
|
|
2698
|
+
return { vector: DEFAULT_HYBRID_WEIGHT2, fulltext: DEFAULT_HYBRID_WEIGHT2 };
|
|
2088
2699
|
}
|
|
2089
2700
|
return { vector: vector / total, fulltext: fulltext / total };
|
|
2090
2701
|
}
|
|
@@ -2123,7 +2734,36 @@ function prepareNode(node, strategy) {
|
|
|
2123
2734
|
function scorePrepared(left, right, strategy, ctx) {
|
|
2124
2735
|
switch (strategy.kind) {
|
|
2125
2736
|
case "custom": {
|
|
2126
|
-
|
|
2737
|
+
let raw;
|
|
2738
|
+
try {
|
|
2739
|
+
raw = strategy.score(left.node, right.node);
|
|
2740
|
+
} catch (error) {
|
|
2741
|
+
return err(
|
|
2742
|
+
new MatchEvidenceError("Custom similarity scorer failed.", {
|
|
2743
|
+
details: {
|
|
2744
|
+
kind: left.node.kind,
|
|
2745
|
+
sourceId: "similarity:custom",
|
|
2746
|
+
operation: "score"
|
|
2747
|
+
},
|
|
2748
|
+
cause: error
|
|
2749
|
+
})
|
|
2750
|
+
);
|
|
2751
|
+
}
|
|
2752
|
+
if (!Number.isFinite(raw)) {
|
|
2753
|
+
return err(
|
|
2754
|
+
new MatchEvidenceError(
|
|
2755
|
+
"Custom similarity scorer returned a non-finite score.",
|
|
2756
|
+
{
|
|
2757
|
+
details: {
|
|
2758
|
+
kind: left.node.kind,
|
|
2759
|
+
sourceId: "similarity:custom",
|
|
2760
|
+
operation: "score",
|
|
2761
|
+
score: String(raw)
|
|
2762
|
+
}
|
|
2763
|
+
}
|
|
2764
|
+
)
|
|
2765
|
+
);
|
|
2766
|
+
}
|
|
2127
2767
|
return ok(clampScore(raw));
|
|
2128
2768
|
}
|
|
2129
2769
|
case "fulltext": {
|
|
@@ -2191,25 +2831,51 @@ function compareCandidateEdges(left, right) {
|
|
|
2191
2831
|
function endpointKey(a, b) {
|
|
2192
2832
|
return JSON.stringify([a, b]);
|
|
2193
2833
|
}
|
|
2194
|
-
function
|
|
2834
|
+
function withSources(edge, sources) {
|
|
2835
|
+
return {
|
|
2836
|
+
...edge,
|
|
2837
|
+
evidence: {
|
|
2838
|
+
...edge.evidence,
|
|
2839
|
+
sources: normalizeMatchSources([...edge.evidence.sources, ...sources])
|
|
2840
|
+
}
|
|
2841
|
+
};
|
|
2842
|
+
}
|
|
2843
|
+
function scoreCandidates(input, resolveConfig, ctx, ceilingPolicy, maxComparisonsPerKind, diagnosticLimit = 0) {
|
|
2195
2844
|
const { similarity, threshold } = resolveConfig;
|
|
2196
|
-
const
|
|
2197
|
-
const forced = [];
|
|
2845
|
+
const forcedByKey = /* @__PURE__ */ new Map();
|
|
2198
2846
|
for (const edge of input.forcedEdges) {
|
|
2199
2847
|
const key = endpointKey(edge.a, edge.b);
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2848
|
+
const previous = forcedByKey.get(key);
|
|
2849
|
+
forcedByKey.set(
|
|
2850
|
+
key,
|
|
2851
|
+
previous === void 0 ? edge : withSources(previous, edge.evidence.sources)
|
|
2852
|
+
);
|
|
2204
2853
|
}
|
|
2205
|
-
const
|
|
2854
|
+
const fuzzyByKey = /* @__PURE__ */ new Map();
|
|
2206
2855
|
for (const pair of input.pairs) {
|
|
2207
2856
|
const key = endpointKey(pair.a, pair.b);
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2857
|
+
const forced2 = forcedByKey.get(key);
|
|
2858
|
+
if (forced2 !== void 0) {
|
|
2859
|
+
forcedByKey.set(key, withSources(forced2, pair.sources));
|
|
2860
|
+
continue;
|
|
2211
2861
|
}
|
|
2862
|
+
const previous = fuzzyByKey.get(key);
|
|
2863
|
+
fuzzyByKey.set(
|
|
2864
|
+
key,
|
|
2865
|
+
previous === void 0 ? pair : {
|
|
2866
|
+
...previous,
|
|
2867
|
+
sources: normalizeMatchSources([
|
|
2868
|
+
...previous.sources,
|
|
2869
|
+
...pair.sources
|
|
2870
|
+
])
|
|
2871
|
+
}
|
|
2872
|
+
);
|
|
2212
2873
|
}
|
|
2874
|
+
const forced = [...forcedByKey.values()];
|
|
2875
|
+
const fuzzy = [...fuzzyByKey.values()].sort((left, right) => {
|
|
2876
|
+
const byA = compareMergeKeys(left.a, right.a);
|
|
2877
|
+
return byA === 0 ? compareMergeKeys(left.b, right.b) : byA;
|
|
2878
|
+
});
|
|
2213
2879
|
if (maxComparisonsPerKind !== void 0 && fuzzy.length > maxComparisonsPerKind) {
|
|
2214
2880
|
if (ceilingPolicy === "error") {
|
|
2215
2881
|
return err(
|
|
@@ -2236,23 +2902,81 @@ function scoreCandidates(input, resolveConfig, ctx, ceilingPolicy, maxComparison
|
|
|
2236
2902
|
limit: maxComparisonsPerKind,
|
|
2237
2903
|
message: `Skipped similarity for this kind: ${fuzzy.length} candidate pairs exceeded maxComparisonsPerKind ${maxComparisonsPerKind}; nodes will merge by id and exact unique match only.`
|
|
2238
2904
|
}
|
|
2239
|
-
]
|
|
2905
|
+
],
|
|
2906
|
+
diagnostics: [],
|
|
2907
|
+
diagnosticsTotal: 0
|
|
2240
2908
|
});
|
|
2241
2909
|
}
|
|
2242
2910
|
const scorer = createPairScorer(similarity, ctx);
|
|
2243
2911
|
const edges = [...forced];
|
|
2244
|
-
|
|
2912
|
+
const diagnostics = [];
|
|
2913
|
+
let diagnosticsTotal = 0;
|
|
2914
|
+
for (const { left, right, a, b, sources } of fuzzy) {
|
|
2245
2915
|
const scored = scorer(a, left, b, right);
|
|
2246
2916
|
if (isErr(scored)) {
|
|
2917
|
+
if (similarity.kind === "custom" && scored.error instanceof MatchEvidenceError) {
|
|
2918
|
+
return err(
|
|
2919
|
+
new MatchEvidenceError(scored.error.message, {
|
|
2920
|
+
cause: scored.error,
|
|
2921
|
+
details: {
|
|
2922
|
+
kind: left.kind,
|
|
2923
|
+
operation: "score",
|
|
2924
|
+
sourceIds: normalizeMatchSources(sources).map(
|
|
2925
|
+
(source) => source.sourceId
|
|
2926
|
+
),
|
|
2927
|
+
endpoints: [entityRef(a), entityRef(b)]
|
|
2928
|
+
}
|
|
2929
|
+
})
|
|
2930
|
+
);
|
|
2931
|
+
}
|
|
2247
2932
|
return err(scored.error);
|
|
2248
2933
|
}
|
|
2249
|
-
if (scored.data
|
|
2250
|
-
|
|
2934
|
+
if (!Number.isFinite(scored.data)) {
|
|
2935
|
+
return err(
|
|
2936
|
+
new MatchEvidenceError(
|
|
2937
|
+
"Similarity scorer returned a non-finite score.",
|
|
2938
|
+
{
|
|
2939
|
+
details: {
|
|
2940
|
+
kind: left.kind,
|
|
2941
|
+
operation: "score",
|
|
2942
|
+
sourceIds: normalizeMatchSources(sources).map(
|
|
2943
|
+
(source) => source.sourceId
|
|
2944
|
+
),
|
|
2945
|
+
score: String(scored.data)
|
|
2946
|
+
}
|
|
2947
|
+
}
|
|
2948
|
+
)
|
|
2949
|
+
);
|
|
2950
|
+
}
|
|
2951
|
+
const noComparableValues = scored.data < 0;
|
|
2952
|
+
const publicScore = noComparableValues ? 0 : scored.data;
|
|
2953
|
+
const accepted = !noComparableValues && scored.data >= threshold;
|
|
2954
|
+
diagnosticsTotal += 1;
|
|
2955
|
+
const retainsDiagnostic = diagnostics.length < diagnosticLimit;
|
|
2956
|
+
if (accepted || retainsDiagnostic) {
|
|
2957
|
+
const evidence = {
|
|
2958
|
+
a: entityRef(a),
|
|
2959
|
+
b: entityRef(b),
|
|
2960
|
+
sources: normalizeMatchSources(sources),
|
|
2961
|
+
decision: "scored",
|
|
2962
|
+
strategy: describeMatchStrategy(similarity),
|
|
2963
|
+
score: publicScore,
|
|
2964
|
+
threshold
|
|
2965
|
+
};
|
|
2966
|
+
if (accepted) edges.push({ a, b, score: scored.data, evidence });
|
|
2967
|
+
if (!retainsDiagnostic) continue;
|
|
2968
|
+
diagnostics.push({
|
|
2969
|
+
evidence,
|
|
2970
|
+
scoreDecision: accepted ? "accepted" : "rejected",
|
|
2971
|
+
...noComparableValues ? { reason: "noComparableValues" } : {}
|
|
2972
|
+
});
|
|
2251
2973
|
}
|
|
2252
2974
|
}
|
|
2253
2975
|
return ok({
|
|
2254
2976
|
edges: edges.sort((left, right) => compareCandidateEdges(left, right)),
|
|
2255
|
-
warnings: []
|
|
2977
|
+
warnings: [],
|
|
2978
|
+
diagnostics,
|
|
2979
|
+
diagnosticsTotal
|
|
2256
2980
|
});
|
|
2257
2981
|
}
|
|
2258
2982
|
|
|
@@ -2354,13 +3078,14 @@ function weakestEdge(edges) {
|
|
|
2354
3078
|
}
|
|
2355
3079
|
function splitUntil(members, edges, isOffending) {
|
|
2356
3080
|
let surviving = internalEdges(members, edges);
|
|
3081
|
+
const removedEdges = [];
|
|
2357
3082
|
for (; ; ) {
|
|
2358
3083
|
const components = buildComponents(surviving, members, true);
|
|
2359
3084
|
const offending = components.filter(
|
|
2360
3085
|
(component) => isOffending(component.members, surviving)
|
|
2361
3086
|
);
|
|
2362
3087
|
if (offending.length === 0) {
|
|
2363
|
-
return components;
|
|
3088
|
+
return { clusters: components, survivingEdges: surviving, removedEdges };
|
|
2364
3089
|
}
|
|
2365
3090
|
let splittable;
|
|
2366
3091
|
for (const component of offending) {
|
|
@@ -2372,11 +3097,16 @@ function splitUntil(members, edges, isOffending) {
|
|
|
2372
3097
|
}
|
|
2373
3098
|
if (splittable === void 0) {
|
|
2374
3099
|
const offendingSet = new Set(offending);
|
|
2375
|
-
return
|
|
2376
|
-
|
|
2377
|
-
|
|
3100
|
+
return {
|
|
3101
|
+
clusters: components.flatMap(
|
|
3102
|
+
(component) => offendingSet.has(component) ? component.members.map((id) => ({ members: [id] })) : [component]
|
|
3103
|
+
),
|
|
3104
|
+
survivingEdges: surviving,
|
|
3105
|
+
removedEdges
|
|
3106
|
+
};
|
|
2378
3107
|
}
|
|
2379
3108
|
const weakest = weakestEdge(splittable);
|
|
3109
|
+
removedEdges.push(weakest);
|
|
2380
3110
|
surviving = surviving.filter((edge) => edge !== weakest);
|
|
2381
3111
|
}
|
|
2382
3112
|
}
|
|
@@ -2390,16 +3120,30 @@ function splitByDropWeakest(members, edges, maxDiameter) {
|
|
|
2390
3120
|
function connectedComponents(candidateEdges, nodeIds) {
|
|
2391
3121
|
return buildComponents(candidateEdges, nodeIds);
|
|
2392
3122
|
}
|
|
2393
|
-
function
|
|
3123
|
+
function enforceDiameterWithEdges(clusters, candidateEdges, clusterMaxDiameter) {
|
|
2394
3124
|
const guarded = [];
|
|
3125
|
+
let survivingEdges = [...candidateEdges];
|
|
3126
|
+
const excludedEdges = [];
|
|
2395
3127
|
for (const component of clusters) {
|
|
2396
3128
|
if (exceedsDiameter(component.members, candidateEdges, clusterMaxDiameter)) {
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
3129
|
+
const split = splitByDropWeakest(
|
|
3130
|
+
component.members,
|
|
3131
|
+
survivingEdges,
|
|
3132
|
+
clusterMaxDiameter
|
|
3133
|
+
);
|
|
3134
|
+
guarded.push(...split.clusters);
|
|
3135
|
+
const memberSet = new Set(component.members);
|
|
3136
|
+
survivingEdges = [
|
|
3137
|
+
...survivingEdges.filter(
|
|
3138
|
+
(edge) => !(memberSet.has(edge.a) && memberSet.has(edge.b))
|
|
3139
|
+
),
|
|
3140
|
+
...split.survivingEdges
|
|
3141
|
+
].sort((left, right) => compareCandidateEdges(left, right));
|
|
3142
|
+
excludedEdges.push(
|
|
3143
|
+
...split.removedEdges.map((edge) => ({
|
|
3144
|
+
edge,
|
|
3145
|
+
reason: "diameter"
|
|
3146
|
+
}))
|
|
2403
3147
|
);
|
|
2404
3148
|
} else {
|
|
2405
3149
|
guarded.push(component);
|
|
@@ -2411,7 +3155,7 @@ function enforceDiameter(clusters, candidateEdges, clusterMaxDiameter) {
|
|
|
2411
3155
|
requireDefined(right.members[0])
|
|
2412
3156
|
)
|
|
2413
3157
|
);
|
|
2414
|
-
return guarded;
|
|
3158
|
+
return { clusters: guarded, survivingEdges, excludedEdges };
|
|
2415
3159
|
}
|
|
2416
3160
|
function countBaseMembers(members, baseIds) {
|
|
2417
3161
|
let count = 0;
|
|
@@ -2431,10 +3175,19 @@ function splitByBaseMultiplicity(members, edges, baseIds) {
|
|
|
2431
3175
|
}
|
|
2432
3176
|
function enforceBaseGuard(clusters, candidateEdges, baseIds) {
|
|
2433
3177
|
if (baseIds.size === 0) {
|
|
2434
|
-
return {
|
|
3178
|
+
return {
|
|
3179
|
+
clusters,
|
|
3180
|
+
events: [],
|
|
3181
|
+
survivingEdges: [...candidateEdges].sort(
|
|
3182
|
+
(left, right) => compareCandidateEdges(left, right)
|
|
3183
|
+
),
|
|
3184
|
+
excludedEdges: []
|
|
3185
|
+
};
|
|
2435
3186
|
}
|
|
2436
3187
|
const result = [];
|
|
2437
3188
|
const events = [];
|
|
3189
|
+
let survivingEdges = [...candidateEdges];
|
|
3190
|
+
const excludedEdges = [];
|
|
2438
3191
|
for (const cluster of clusters) {
|
|
2439
3192
|
const clusterBaseIds = cluster.members.filter((id) => baseIds.has(id));
|
|
2440
3193
|
if (clusterBaseIds.length < 2) {
|
|
@@ -2449,8 +3202,24 @@ function enforceBaseGuard(clusters, candidateEdges, baseIds) {
|
|
|
2449
3202
|
(left, right) => compareMergeKeys(left, right)
|
|
2450
3203
|
)
|
|
2451
3204
|
});
|
|
2452
|
-
|
|
2453
|
-
|
|
3205
|
+
const split = splitByBaseMultiplicity(
|
|
3206
|
+
cluster.members,
|
|
3207
|
+
survivingEdges,
|
|
3208
|
+
baseIds
|
|
3209
|
+
);
|
|
3210
|
+
result.push(...split.clusters);
|
|
3211
|
+
const memberSet = new Set(cluster.members);
|
|
3212
|
+
survivingEdges = [
|
|
3213
|
+
...survivingEdges.filter(
|
|
3214
|
+
(edge) => !(memberSet.has(edge.a) && memberSet.has(edge.b))
|
|
3215
|
+
),
|
|
3216
|
+
...split.survivingEdges
|
|
3217
|
+
].sort((left, right) => compareCandidateEdges(left, right));
|
|
3218
|
+
excludedEdges.push(
|
|
3219
|
+
...split.removedEdges.map((edge) => ({
|
|
3220
|
+
edge,
|
|
3221
|
+
reason: "baseAmbiguity"
|
|
3222
|
+
}))
|
|
2454
3223
|
);
|
|
2455
3224
|
}
|
|
2456
3225
|
result.sort(
|
|
@@ -2459,7 +3228,20 @@ function enforceBaseGuard(clusters, candidateEdges, baseIds) {
|
|
|
2459
3228
|
requireDefined(right.members[0])
|
|
2460
3229
|
)
|
|
2461
3230
|
);
|
|
2462
|
-
return { clusters: result, events };
|
|
3231
|
+
return { clusters: result, events, survivingEdges, excludedEdges };
|
|
3232
|
+
}
|
|
3233
|
+
function decisiveEdgesForCluster(cluster, survivingEdges) {
|
|
3234
|
+
const members = new Set(cluster.members);
|
|
3235
|
+
const forest = new UnionFind(compareMergeKeys);
|
|
3236
|
+
for (const member of cluster.members) forest.add(member);
|
|
3237
|
+
const decisive = [];
|
|
3238
|
+
for (const edge of internalEdges(cluster.members, survivingEdges)) {
|
|
3239
|
+
if (!members.has(edge.a) || !members.has(edge.b)) continue;
|
|
3240
|
+
if (forest.find(edge.a) === forest.find(edge.b)) continue;
|
|
3241
|
+
forest.union(edge.a, edge.b);
|
|
3242
|
+
decisive.push(edge.evidence);
|
|
3243
|
+
}
|
|
3244
|
+
return decisive;
|
|
2463
3245
|
}
|
|
2464
3246
|
|
|
2465
3247
|
// src/graph-merge/delete-modify.ts
|
|
@@ -2756,8 +3538,9 @@ var WINDOW_NOT_APPLICABLE_DROP_REASON = "window-not-applicable";
|
|
|
2756
3538
|
function earliestEnd(claims) {
|
|
2757
3539
|
let earliest;
|
|
2758
3540
|
for (const claim of claims) {
|
|
2759
|
-
if (
|
|
2760
|
-
|
|
3541
|
+
if (claim.change.kind !== "set") continue;
|
|
3542
|
+
if (earliest === void 0 || compareStrings(claim.change.validTo, earliest) < 0) {
|
|
3543
|
+
earliest = claim.change.validTo;
|
|
2761
3544
|
}
|
|
2762
3545
|
}
|
|
2763
3546
|
return earliest;
|
|
@@ -2766,18 +3549,26 @@ function resolveEndClaims(claims, preferredBranchId) {
|
|
|
2766
3549
|
const preferred = claims.filter(
|
|
2767
3550
|
(claim) => claim.branchId === preferredBranchId
|
|
2768
3551
|
);
|
|
2769
|
-
|
|
3552
|
+
const candidates = preferred.length > 0 ? preferred : claims;
|
|
3553
|
+
const earliest = earliestEnd(candidates);
|
|
3554
|
+
return earliest === void 0 ? candidates.some((claim) => claim.change.kind === "clear") ? { kind: "clear" } : void 0 : { kind: "set", validTo: earliest };
|
|
2770
3555
|
}
|
|
2771
3556
|
function claimingBranches(claims) {
|
|
2772
3557
|
return [...new Set(claims.map((claim) => claim.branchId))].sort(
|
|
2773
3558
|
(left, right) => compareStrings(left, right)
|
|
2774
3559
|
);
|
|
2775
3560
|
}
|
|
3561
|
+
function sameValidToChange(left, right) {
|
|
3562
|
+
if (left.kind !== right.kind) return false;
|
|
3563
|
+
if (left.kind === "clear") return true;
|
|
3564
|
+
return right.kind === "set" && left.validTo === right.validTo;
|
|
3565
|
+
}
|
|
2776
3566
|
function classifyDelta(base, fork) {
|
|
3567
|
+
const lowerBoundMoved = fork.validFrom !== base.validFrom;
|
|
2777
3568
|
const endCleared = fork.validTo === void 0 && base.validTo !== void 0;
|
|
2778
3569
|
return {
|
|
2779
|
-
applicableEnd: fork.validTo !== void 0 && fork.validTo !== base.validTo ? fork.validTo : void 0,
|
|
2780
|
-
unapplicable:
|
|
3570
|
+
applicableEnd: fork.validTo !== void 0 && fork.validTo !== base.validTo ? { kind: "set", validTo: fork.validTo } : endCleared && !lowerBoundMoved ? { kind: "clear" } : void 0,
|
|
3571
|
+
unapplicable: lowerBoundMoved
|
|
2781
3572
|
};
|
|
2782
3573
|
}
|
|
2783
3574
|
function resolvePopulation(entity, deltas, deletions, preferredBranchId, dropItem) {
|
|
@@ -2812,7 +3603,7 @@ function resolvePopulation(entity, deltas, deletions, preferredBranchId, dropIte
|
|
|
2812
3603
|
continue;
|
|
2813
3604
|
}
|
|
2814
3605
|
if (applicableEnd !== void 0) {
|
|
2815
|
-
claims.push({ branchId: delta.branchId,
|
|
3606
|
+
claims.push({ branchId: delta.branchId, change: applicableEnd });
|
|
2816
3607
|
}
|
|
2817
3608
|
if (unapplicable && !reportedUnapplicable) {
|
|
2818
3609
|
reportedUnapplicable = true;
|
|
@@ -2826,27 +3617,29 @@ function resolvePopulation(entity, deltas, deletions, preferredBranchId, dropIte
|
|
|
2826
3617
|
entity,
|
|
2827
3618
|
kind: first.kind,
|
|
2828
3619
|
id: first.id,
|
|
2829
|
-
validTo: targetEnd,
|
|
3620
|
+
...targetEnd.kind === "set" ? { validTo: targetEnd.validTo } : { clearValidTo: true },
|
|
2830
3621
|
claimedBy: claimingBranches(claims),
|
|
2831
3622
|
precedence: VALIDITY_END_TARGET_PRECEDENCE
|
|
2832
3623
|
});
|
|
2833
3624
|
}
|
|
2834
3625
|
continue;
|
|
2835
3626
|
}
|
|
2836
|
-
const resolved =
|
|
3627
|
+
const resolved = resolveEndClaims(claims, void 0);
|
|
2837
3628
|
if (resolved === void 0) {
|
|
2838
3629
|
continue;
|
|
2839
3630
|
}
|
|
2840
3631
|
ends.set(identity, resolved);
|
|
2841
3632
|
credits.set(
|
|
2842
3633
|
identity,
|
|
2843
|
-
claimingBranches(
|
|
3634
|
+
claimingBranches(
|
|
3635
|
+
claims.filter((claim) => sameValidToChange(claim.change, resolved))
|
|
3636
|
+
)
|
|
2844
3637
|
);
|
|
2845
3638
|
resolutions.push({
|
|
2846
3639
|
entity,
|
|
2847
3640
|
kind: first.kind,
|
|
2848
3641
|
id: first.id,
|
|
2849
|
-
validTo: resolved,
|
|
3642
|
+
...resolved.kind === "set" ? { validTo: resolved.validTo } : { clearValidTo: true },
|
|
2850
3643
|
claimedBy: claimingBranches(claims)
|
|
2851
3644
|
});
|
|
2852
3645
|
}
|
|
@@ -3070,17 +3863,19 @@ function foldEdgeSet(foldSet, context, branchRank, preferredBranchId) {
|
|
|
3070
3863
|
const fromKind = kindOf(survivor.fromKey);
|
|
3071
3864
|
const toId = idOf(survivor.toKey);
|
|
3072
3865
|
const toKind = kindOf(survivor.toKey);
|
|
3073
|
-
const preferredSurvivorEnd = survivor.staged.branchId === preferredBranchId ? survivor.staged.validTo : void 0;
|
|
3866
|
+
const preferredSurvivorEnd = survivor.staged.branchId === preferredBranchId ? typeof survivor.staged.validTo === "string" ? { kind: "set", validTo: survivor.staged.validTo } : survivor.staged.clearValidTo === true ? { kind: "clear" } : void 0 : void 0;
|
|
3074
3867
|
const foldedEnd = preferredSurvivorEnd ?? resolveEndClaims(
|
|
3075
|
-
foldSet.filter(
|
|
3868
|
+
foldSet.filter(
|
|
3869
|
+
(edge) => edge.staged.validTo !== void 0 || edge.staged.clearValidTo === true
|
|
3870
|
+
).map((edge) => ({
|
|
3076
3871
|
branchId: edge.staged.branchId,
|
|
3077
|
-
validTo:
|
|
3872
|
+
change: typeof edge.staged.validTo === "string" ? { kind: "set", validTo: edge.staged.validTo } : { kind: "clear" }
|
|
3078
3873
|
})),
|
|
3079
3874
|
preferredBranchId
|
|
3080
3875
|
);
|
|
3081
3876
|
const window = {
|
|
3082
3877
|
...survivor.staged.validFrom === void 0 ? {} : { validFrom: survivor.staged.validFrom },
|
|
3083
|
-
...foldedEnd ===
|
|
3878
|
+
...foldedEnd?.kind === "set" ? { validTo: foldedEnd.validTo } : foldedEnd?.kind === "clear" ? { clearValidTo: true } : {}
|
|
3084
3879
|
};
|
|
3085
3880
|
const contentKeys = new Set(foldSet.map((edge) => edge.dedupeKey));
|
|
3086
3881
|
if (contentKeys.size === 1) {
|
|
@@ -3225,7 +4020,10 @@ var mergeOptionsScalarSchema = z.object({
|
|
|
3225
4020
|
provenance: z.boolean().default(MERGE_OPTION_DEFAULTS.provenance),
|
|
3226
4021
|
persistProvenance: z.boolean().default(MERGE_OPTION_DEFAULTS.persistProvenance),
|
|
3227
4022
|
maxComparisonsPerKind: z.number().int({ message: "maxComparisonsPerKind must be an integer" }).min(0, { message: "maxComparisonsPerKind must be >= 0" }).optional(),
|
|
3228
|
-
clusterMaxDiameter: z.number().positive({ message: "clusterMaxDiameter must be positive" }).optional()
|
|
4023
|
+
clusterMaxDiameter: z.number().positive({ message: "clusterMaxDiameter must be positive" }).optional(),
|
|
4024
|
+
candidateDiagnostics: z.object({
|
|
4025
|
+
limit: z.number().int({ message: "candidateDiagnostics.limit must be an integer" }).min(0, { message: "candidateDiagnostics.limit must be >= 0" })
|
|
4026
|
+
}).strict().optional()
|
|
3229
4027
|
});
|
|
3230
4028
|
function validatePropertyConflictPolicy(policy, label) {
|
|
3231
4029
|
if (typeof policy === "string" && !propertyConflictPolicySchema.safeParse(policy).success) {
|
|
@@ -3288,7 +4086,8 @@ function normalizeMergeOptions(options = {}) {
|
|
|
3288
4086
|
provenance: options.provenance,
|
|
3289
4087
|
persistProvenance: options.persistProvenance,
|
|
3290
4088
|
...options.maxComparisonsPerKind === void 0 ? {} : { maxComparisonsPerKind: options.maxComparisonsPerKind },
|
|
3291
|
-
...options.clusterMaxDiameter === void 0 ? {} : { clusterMaxDiameter: options.clusterMaxDiameter }
|
|
4089
|
+
...options.clusterMaxDiameter === void 0 ? {} : { clusterMaxDiameter: options.clusterMaxDiameter },
|
|
4090
|
+
...options.candidateDiagnostics === void 0 ? {} : { candidateDiagnostics: options.candidateDiagnostics }
|
|
3292
4091
|
});
|
|
3293
4092
|
const onPropertyConflict = validatePropertyConflictPolicy(
|
|
3294
4093
|
options.onPropertyConflict ?? MERGE_OPTION_DEFAULTS.onPropertyConflict,
|
|
@@ -3319,10 +4118,625 @@ function normalizeMergeOptions(options = {}) {
|
|
|
3319
4118
|
...options.target === void 0 ? {} : { target: options.target },
|
|
3320
4119
|
...scalar.maxComparisonsPerKind === void 0 ? {} : { maxComparisonsPerKind: scalar.maxComparisonsPerKind },
|
|
3321
4120
|
...scalar.clusterMaxDiameter === void 0 ? {} : { clusterMaxDiameter: scalar.clusterMaxDiameter },
|
|
4121
|
+
...scalar.candidateDiagnostics === void 0 ? {} : { candidateDiagnostics: scalar.candidateDiagnostics },
|
|
3322
4122
|
...options.branchOrder === void 0 ? {} : { branchOrder: options.branchOrder },
|
|
3323
4123
|
...provenanceWeights === void 0 ? {} : { provenanceWeights }
|
|
3324
4124
|
};
|
|
3325
4125
|
}
|
|
4126
|
+
var MERGE_PLAN_FORMAT_VERSION = 1;
|
|
4127
|
+
var MERGE_PLAN_DIGEST_ALGORITHM = "sha256";
|
|
4128
|
+
var nonEmptyStringSchema = z.string().min(1);
|
|
4129
|
+
var finiteNumberSchema = z.number();
|
|
4130
|
+
var nonNegativeIntegerSchema = finiteNumberSchema.int().nonnegative();
|
|
4131
|
+
var jsonObjectSchema = z.record(z.string(), z.json());
|
|
4132
|
+
var mergePlanEntityRefSchema = z.object({ kind: nonEmptyStringSchema, id: nonEmptyStringSchema }).strict();
|
|
4133
|
+
var mergePlanSchemaFenceSchema = z.object({
|
|
4134
|
+
managed: z.boolean(),
|
|
4135
|
+
version: nonNegativeIntegerSchema,
|
|
4136
|
+
hash: nonEmptyStringSchema
|
|
4137
|
+
}).strict();
|
|
4138
|
+
var mergePlanRevisionFenceSchema = z.object({
|
|
4139
|
+
origin: nonEmptyStringSchema,
|
|
4140
|
+
revision: nonEmptyStringSchema.nullable()
|
|
4141
|
+
}).strict();
|
|
4142
|
+
var mergePlanTargetFenceSchema = z.object({
|
|
4143
|
+
graphId: nonEmptyStringSchema,
|
|
4144
|
+
schema: mergePlanSchemaFenceSchema,
|
|
4145
|
+
revision: mergePlanRevisionFenceSchema
|
|
4146
|
+
}).strict();
|
|
4147
|
+
var branchAnchorSchema = z.object({
|
|
4148
|
+
branchId: nonEmptyStringSchema,
|
|
4149
|
+
baseVersion: nonEmptyStringSchema
|
|
4150
|
+
}).strict();
|
|
4151
|
+
var snapshotAnchorsSchema = z.object({
|
|
4152
|
+
kind: z.literal("snapshot"),
|
|
4153
|
+
base: z.object({
|
|
4154
|
+
graphId: nonEmptyStringSchema,
|
|
4155
|
+
baseVersion: nonEmptyStringSchema
|
|
4156
|
+
}).strict(),
|
|
4157
|
+
branches: z.array(branchAnchorSchema)
|
|
4158
|
+
}).strict();
|
|
4159
|
+
var incrementalAnchorsSchema = z.object({
|
|
4160
|
+
kind: z.literal("incremental"),
|
|
4161
|
+
forkPoint: z.object({
|
|
4162
|
+
graphId: nonEmptyStringSchema,
|
|
4163
|
+
baseVersion: nonEmptyStringSchema,
|
|
4164
|
+
schema: mergePlanSchemaFenceSchema
|
|
4165
|
+
}).strict(),
|
|
4166
|
+
branches: z.array(branchAnchorSchema)
|
|
4167
|
+
}).strict();
|
|
4168
|
+
var mergePlanAnchorsSchema = z.discriminatedUnion("kind", [
|
|
4169
|
+
snapshotAnchorsSchema,
|
|
4170
|
+
incrementalAnchorsSchema
|
|
4171
|
+
]);
|
|
4172
|
+
var nodeUpsertSchema = z.object({
|
|
4173
|
+
kind: nonEmptyStringSchema,
|
|
4174
|
+
id: nonEmptyStringSchema,
|
|
4175
|
+
setProps: jsonObjectSchema,
|
|
4176
|
+
unsetProps: z.array(nonEmptyStringSchema),
|
|
4177
|
+
validFrom: nonEmptyStringSchema.optional(),
|
|
4178
|
+
validTo: nonEmptyStringSchema.optional()
|
|
4179
|
+
}).strict();
|
|
4180
|
+
var edgeUpsertSchema = z.object({
|
|
4181
|
+
kind: nonEmptyStringSchema,
|
|
4182
|
+
id: nonEmptyStringSchema,
|
|
4183
|
+
from: mergePlanEntityRefSchema,
|
|
4184
|
+
to: mergePlanEntityRefSchema,
|
|
4185
|
+
setProps: jsonObjectSchema,
|
|
4186
|
+
unsetProps: z.array(nonEmptyStringSchema),
|
|
4187
|
+
validFrom: nonEmptyStringSchema.optional(),
|
|
4188
|
+
validTo: nonEmptyStringSchema.optional()
|
|
4189
|
+
}).strict();
|
|
4190
|
+
var mergePlanIdentityAssertionSchema = z.object({
|
|
4191
|
+
id: nonEmptyStringSchema,
|
|
4192
|
+
relation: z.enum(["same", "different"]),
|
|
4193
|
+
a: mergePlanEntityRefSchema,
|
|
4194
|
+
b: mergePlanEntityRefSchema,
|
|
4195
|
+
validFrom: nonEmptyStringSchema,
|
|
4196
|
+
validTo: nonEmptyStringSchema.optional(),
|
|
4197
|
+
endedBy: mergePlanEntityRefSchema.optional()
|
|
4198
|
+
}).strict();
|
|
4199
|
+
var mergePlanWritesSchema = z.object({
|
|
4200
|
+
nodeDeletes: z.array(mergePlanEntityRefSchema),
|
|
4201
|
+
nodeUpserts: z.array(nodeUpsertSchema),
|
|
4202
|
+
edgeDeletes: z.array(mergePlanEntityRefSchema),
|
|
4203
|
+
edgeUpserts: z.array(edgeUpsertSchema),
|
|
4204
|
+
identityAssertions: z.array(mergePlanIdentityAssertionSchema),
|
|
4205
|
+
identityRetractions: z.array(mergePlanIdentityAssertionSchema)
|
|
4206
|
+
}).strict();
|
|
4207
|
+
var proposedRoleSchema = z.object({
|
|
4208
|
+
upserts: nonNegativeIntegerSchema,
|
|
4209
|
+
deletions: nonNegativeIntegerSchema
|
|
4210
|
+
}).strict();
|
|
4211
|
+
var mergePlanProposedSummarySchema = z.object({
|
|
4212
|
+
nodes: proposedRoleSchema,
|
|
4213
|
+
edges: proposedRoleSchema,
|
|
4214
|
+
identity: z.object({
|
|
4215
|
+
assertions: nonNegativeIntegerSchema,
|
|
4216
|
+
retractions: nonNegativeIntegerSchema
|
|
4217
|
+
}).strict()
|
|
4218
|
+
}).strict();
|
|
4219
|
+
var mergePlanGuardsSchema = z.object({
|
|
4220
|
+
canonicalMappings: z.array(
|
|
4221
|
+
z.object({
|
|
4222
|
+
member: mergePlanEntityRefSchema,
|
|
4223
|
+
canonical: mergePlanEntityRefSchema
|
|
4224
|
+
}).strict()
|
|
4225
|
+
),
|
|
4226
|
+
retypes: z.array(
|
|
4227
|
+
z.object({
|
|
4228
|
+
entity: mergePlanEntityRefSchema,
|
|
4229
|
+
toKind: nonEmptyStringSchema
|
|
4230
|
+
}).strict()
|
|
4231
|
+
),
|
|
4232
|
+
deletedNodes: z.array(mergePlanEntityRefSchema),
|
|
4233
|
+
incremental: z.object({
|
|
4234
|
+
tombstoneResurrection: z.literal("refuse"),
|
|
4235
|
+
lossyUpdates: z.literal("refuse"),
|
|
4236
|
+
edgeIdentity: z.literal("preserve")
|
|
4237
|
+
}).strict().optional()
|
|
4238
|
+
}).strict();
|
|
4239
|
+
var matchSourceSchema = z.discriminatedUnion("kind", [
|
|
4240
|
+
z.object({ kind: z.literal("block"), sourceId: nonEmptyStringSchema }).strict(),
|
|
4241
|
+
z.object({
|
|
4242
|
+
kind: z.literal("unique"),
|
|
4243
|
+
sourceId: nonEmptyStringSchema,
|
|
4244
|
+
constraintName: nonEmptyStringSchema
|
|
4245
|
+
}).strict(),
|
|
4246
|
+
z.object({
|
|
4247
|
+
kind: z.literal("baseUnique"),
|
|
4248
|
+
sourceId: nonEmptyStringSchema,
|
|
4249
|
+
constraintName: nonEmptyStringSchema
|
|
4250
|
+
}).strict(),
|
|
4251
|
+
z.object({
|
|
4252
|
+
kind: z.literal("baseIndex"),
|
|
4253
|
+
sourceId: nonEmptyStringSchema,
|
|
4254
|
+
indexName: nonEmptyStringSchema
|
|
4255
|
+
}).strict(),
|
|
4256
|
+
z.object({ kind: z.literal("keyless"), sourceId: nonEmptyStringSchema }).strict(),
|
|
4257
|
+
z.object({ kind: z.literal("retype"), sourceId: nonEmptyStringSchema }).strict(),
|
|
4258
|
+
z.object({
|
|
4259
|
+
kind: z.literal("custom"),
|
|
4260
|
+
sourceId: nonEmptyStringSchema,
|
|
4261
|
+
metadata: z.json().optional()
|
|
4262
|
+
}).strict()
|
|
4263
|
+
]);
|
|
4264
|
+
var similarityStrategySchema = z.discriminatedUnion("kind", [
|
|
4265
|
+
z.object({
|
|
4266
|
+
kind: z.literal("fulltext"),
|
|
4267
|
+
fields: z.array(nonEmptyStringSchema)
|
|
4268
|
+
}).strict(),
|
|
4269
|
+
z.object({
|
|
4270
|
+
kind: z.literal("vector"),
|
|
4271
|
+
fields: z.array(nonEmptyStringSchema)
|
|
4272
|
+
}).strict(),
|
|
4273
|
+
z.object({
|
|
4274
|
+
kind: z.literal("hybrid"),
|
|
4275
|
+
fields: z.array(nonEmptyStringSchema),
|
|
4276
|
+
weights: z.object({ vector: finiteNumberSchema, fulltext: finiteNumberSchema }).strict()
|
|
4277
|
+
}).strict(),
|
|
4278
|
+
z.object({ kind: z.literal("custom") }).strict()
|
|
4279
|
+
]);
|
|
4280
|
+
var definitionalEvidenceSchema = z.object({
|
|
4281
|
+
a: mergePlanEntityRefSchema,
|
|
4282
|
+
b: mergePlanEntityRefSchema,
|
|
4283
|
+
sources: z.array(matchSourceSchema),
|
|
4284
|
+
decision: z.literal("definitional")
|
|
4285
|
+
}).strict();
|
|
4286
|
+
var scoredEvidenceSchema = z.object({
|
|
4287
|
+
a: mergePlanEntityRefSchema,
|
|
4288
|
+
b: mergePlanEntityRefSchema,
|
|
4289
|
+
sources: z.array(matchSourceSchema),
|
|
4290
|
+
decision: z.literal("scored"),
|
|
4291
|
+
strategy: similarityStrategySchema,
|
|
4292
|
+
score: finiteNumberSchema,
|
|
4293
|
+
threshold: finiteNumberSchema
|
|
4294
|
+
}).strict();
|
|
4295
|
+
var mergePlanMatchEvidenceSchema = z.discriminatedUnion("decision", [
|
|
4296
|
+
definitionalEvidenceSchema,
|
|
4297
|
+
scoredEvidenceSchema
|
|
4298
|
+
]);
|
|
4299
|
+
var entityResolutionSchema = z.object({
|
|
4300
|
+
canonicalId: nonEmptyStringSchema,
|
|
4301
|
+
memberIds: z.array(nonEmptyStringSchema),
|
|
4302
|
+
kind: nonEmptyStringSchema,
|
|
4303
|
+
branchOrigins: z.array(nonEmptyStringSchema),
|
|
4304
|
+
decisiveEdges: z.array(mergePlanMatchEvidenceSchema)
|
|
4305
|
+
}).strict();
|
|
4306
|
+
var diagnosticsSchema = z.object({
|
|
4307
|
+
entries: z.array(
|
|
4308
|
+
z.object({
|
|
4309
|
+
evidence: mergePlanMatchEvidenceSchema,
|
|
4310
|
+
scoreDecision: z.enum(["accepted", "rejected"]),
|
|
4311
|
+
reason: z.literal("noComparableValues").optional(),
|
|
4312
|
+
clusterDisposition: z.union([
|
|
4313
|
+
z.literal("retained"),
|
|
4314
|
+
z.object({
|
|
4315
|
+
kind: z.literal("excluded"),
|
|
4316
|
+
reason: z.enum(["diameter", "baseAmbiguity"])
|
|
4317
|
+
}).strict()
|
|
4318
|
+
]).optional()
|
|
4319
|
+
}).strict()
|
|
4320
|
+
),
|
|
4321
|
+
total: nonNegativeIntegerSchema,
|
|
4322
|
+
limit: nonNegativeIntegerSchema,
|
|
4323
|
+
truncated: z.boolean()
|
|
4324
|
+
}).strict();
|
|
4325
|
+
var mergePlanReviewSchema = z.object({
|
|
4326
|
+
resolutions: z.array(entityResolutionSchema),
|
|
4327
|
+
conflicts: z.array(
|
|
4328
|
+
z.object({
|
|
4329
|
+
entityId: nonEmptyStringSchema,
|
|
4330
|
+
kind: nonEmptyStringSchema,
|
|
4331
|
+
property: nonEmptyStringSchema,
|
|
4332
|
+
values: z.array(
|
|
4333
|
+
z.object({ branchId: nonEmptyStringSchema, value: z.json() }).strict()
|
|
4334
|
+
),
|
|
4335
|
+
resolution: z.json()
|
|
4336
|
+
}).strict()
|
|
4337
|
+
),
|
|
4338
|
+
deleteModifyConflicts: z.array(
|
|
4339
|
+
z.object({
|
|
4340
|
+
entityId: nonEmptyStringSchema,
|
|
4341
|
+
kind: nonEmptyStringSchema,
|
|
4342
|
+
deletedBy: nonEmptyStringSchema,
|
|
4343
|
+
modifiedBy: nonEmptyStringSchema,
|
|
4344
|
+
resolution: z.enum(["deleteWins", "modifyWins", "flag"])
|
|
4345
|
+
}).strict()
|
|
4346
|
+
),
|
|
4347
|
+
typeReconciliations: z.array(
|
|
4348
|
+
z.object({
|
|
4349
|
+
entityId: nonEmptyStringSchema,
|
|
4350
|
+
fromTypes: z.array(nonEmptyStringSchema),
|
|
4351
|
+
toType: nonEmptyStringSchema,
|
|
4352
|
+
decisiveEdges: z.array(mergePlanMatchEvidenceSchema).optional()
|
|
4353
|
+
}).strict()
|
|
4354
|
+
),
|
|
4355
|
+
dropped: z.array(
|
|
4356
|
+
z.discriminatedUnion("kind", [
|
|
4357
|
+
z.object({
|
|
4358
|
+
kind: z.literal("node"),
|
|
4359
|
+
id: nonEmptyStringSchema,
|
|
4360
|
+
reason: nonEmptyStringSchema
|
|
4361
|
+
}).strict(),
|
|
4362
|
+
z.object({
|
|
4363
|
+
kind: z.literal("edge"),
|
|
4364
|
+
id: nonEmptyStringSchema,
|
|
4365
|
+
reason: nonEmptyStringSchema
|
|
4366
|
+
}).strict(),
|
|
4367
|
+
z.object({
|
|
4368
|
+
kind: z.literal("identity"),
|
|
4369
|
+
id: nonEmptyStringSchema,
|
|
4370
|
+
reason: nonEmptyStringSchema
|
|
4371
|
+
}).strict()
|
|
4372
|
+
])
|
|
4373
|
+
),
|
|
4374
|
+
validityEnds: z.array(
|
|
4375
|
+
z.object({
|
|
4376
|
+
entity: z.enum(["node", "edge"]),
|
|
4377
|
+
kind: nonEmptyStringSchema,
|
|
4378
|
+
id: nonEmptyStringSchema,
|
|
4379
|
+
validTo: nonEmptyStringSchema,
|
|
4380
|
+
claimedBy: z.array(nonEmptyStringSchema),
|
|
4381
|
+
precedence: z.literal("target").optional()
|
|
4382
|
+
}).strict()
|
|
4383
|
+
),
|
|
4384
|
+
baseAmbiguities: z.array(
|
|
4385
|
+
z.object({
|
|
4386
|
+
baseIds: z.array(mergePlanEntityRefSchema),
|
|
4387
|
+
memberIds: z.array(mergePlanEntityRefSchema)
|
|
4388
|
+
}).strict()
|
|
4389
|
+
),
|
|
4390
|
+
provenanceRecords: z.array(
|
|
4391
|
+
z.object({
|
|
4392
|
+
role: z.enum(["node", "edge"]),
|
|
4393
|
+
canonicalId: nonEmptyStringSchema,
|
|
4394
|
+
canonicalKind: nonEmptyStringSchema,
|
|
4395
|
+
branchId: nonEmptyStringSchema,
|
|
4396
|
+
sourceId: nonEmptyStringSchema
|
|
4397
|
+
}).strict()
|
|
4398
|
+
),
|
|
4399
|
+
warnings: z.array(z.string()),
|
|
4400
|
+
diagnostics: diagnosticsSchema.optional()
|
|
4401
|
+
}).strict();
|
|
4402
|
+
var mergePlanDigestSchema = z.object({
|
|
4403
|
+
algorithm: z.literal(MERGE_PLAN_DIGEST_ALGORITHM),
|
|
4404
|
+
value: z.string().regex(/^[\da-f]{64}$/)
|
|
4405
|
+
}).strict();
|
|
4406
|
+
var mergePlanArtifactV1BaseSchema = z.object({
|
|
4407
|
+
formatVersion: z.literal(MERGE_PLAN_FORMAT_VERSION),
|
|
4408
|
+
digest: mergePlanDigestSchema,
|
|
4409
|
+
mode: z.enum(["snapshot", "incremental"]),
|
|
4410
|
+
target: mergePlanTargetFenceSchema,
|
|
4411
|
+
anchors: mergePlanAnchorsSchema,
|
|
4412
|
+
proposed: mergePlanProposedSummarySchema,
|
|
4413
|
+
writes: mergePlanWritesSchema,
|
|
4414
|
+
guards: mergePlanGuardsSchema,
|
|
4415
|
+
review: mergePlanReviewSchema,
|
|
4416
|
+
provenance: z.object({ includeInReport: z.boolean(), persist: z.boolean() }).strict()
|
|
4417
|
+
}).strict();
|
|
4418
|
+
var mergePlanArtifactV1InputBaseSchema = mergePlanArtifactV1BaseSchema.omit({
|
|
4419
|
+
digest: true
|
|
4420
|
+
});
|
|
4421
|
+
function addSemanticIssues(artifact, ctx) {
|
|
4422
|
+
if (artifact.mode !== artifact.anchors.kind) {
|
|
4423
|
+
ctx.addIssue({
|
|
4424
|
+
code: "custom",
|
|
4425
|
+
path: ["anchors", "kind"],
|
|
4426
|
+
message: `Expected ${artifact.mode} anchors for a ${artifact.mode} plan.`
|
|
4427
|
+
});
|
|
4428
|
+
}
|
|
4429
|
+
if (artifact.mode === "incremental" !== (artifact.guards.incremental !== void 0)) {
|
|
4430
|
+
ctx.addIssue({
|
|
4431
|
+
code: "custom",
|
|
4432
|
+
path: ["guards", "incremental"],
|
|
4433
|
+
message: "Incremental write guards must be present exactly for incremental plans."
|
|
4434
|
+
});
|
|
4435
|
+
}
|
|
4436
|
+
const expectedCounts = {
|
|
4437
|
+
nodeUpserts: artifact.writes.nodeUpserts.length,
|
|
4438
|
+
nodeDeletions: artifact.writes.nodeDeletes.length,
|
|
4439
|
+
edgeUpserts: artifact.writes.edgeUpserts.length,
|
|
4440
|
+
edgeDeletions: artifact.writes.edgeDeletes.length,
|
|
4441
|
+
assertions: artifact.writes.identityAssertions.length,
|
|
4442
|
+
retractions: artifact.writes.identityRetractions.length
|
|
4443
|
+
};
|
|
4444
|
+
const receivedCounts = {
|
|
4445
|
+
nodeUpserts: artifact.proposed.nodes.upserts,
|
|
4446
|
+
nodeDeletions: artifact.proposed.nodes.deletions,
|
|
4447
|
+
edgeUpserts: artifact.proposed.edges.upserts,
|
|
4448
|
+
edgeDeletions: artifact.proposed.edges.deletions,
|
|
4449
|
+
assertions: artifact.proposed.identity.assertions,
|
|
4450
|
+
retractions: artifact.proposed.identity.retractions
|
|
4451
|
+
};
|
|
4452
|
+
if (JSON.stringify(expectedCounts) !== JSON.stringify(receivedCounts)) {
|
|
4453
|
+
ctx.addIssue({
|
|
4454
|
+
code: "custom",
|
|
4455
|
+
path: ["proposed"],
|
|
4456
|
+
message: "Proposed counts must equal the resolved write-set counts."
|
|
4457
|
+
});
|
|
4458
|
+
}
|
|
4459
|
+
addOperationIssues(
|
|
4460
|
+
artifact.writes.nodeDeletes,
|
|
4461
|
+
artifact.writes.nodeUpserts,
|
|
4462
|
+
"node",
|
|
4463
|
+
ctx
|
|
4464
|
+
);
|
|
4465
|
+
addOperationIssues(
|
|
4466
|
+
artifact.writes.edgeDeletes,
|
|
4467
|
+
artifact.writes.edgeUpserts,
|
|
4468
|
+
"edge",
|
|
4469
|
+
ctx
|
|
4470
|
+
);
|
|
4471
|
+
for (const [index, upsert] of artifact.writes.nodeUpserts.entries()) {
|
|
4472
|
+
addPropertyPatchIssues(upsert, ["writes", "nodeUpserts", index], ctx);
|
|
4473
|
+
}
|
|
4474
|
+
for (const [index, upsert] of artifact.writes.edgeUpserts.entries()) {
|
|
4475
|
+
addPropertyPatchIssues(upsert, ["writes", "edgeUpserts", index], ctx);
|
|
4476
|
+
}
|
|
4477
|
+
const canonicalByMember = /* @__PURE__ */ new Map();
|
|
4478
|
+
let invalidCanonicalMappings = false;
|
|
4479
|
+
for (const mapping of artifact.guards.canonicalMappings) {
|
|
4480
|
+
const member = entityKey(mapping.member);
|
|
4481
|
+
const canonical = entityKey(mapping.canonical);
|
|
4482
|
+
if (canonicalByMember.has(member)) invalidCanonicalMappings = true;
|
|
4483
|
+
canonicalByMember.set(member, canonical);
|
|
4484
|
+
}
|
|
4485
|
+
for (const canonical of canonicalByMember.values()) {
|
|
4486
|
+
if (canonicalByMember.get(canonical) !== canonical) {
|
|
4487
|
+
invalidCanonicalMappings = true;
|
|
4488
|
+
}
|
|
4489
|
+
}
|
|
4490
|
+
if (invalidCanonicalMappings) {
|
|
4491
|
+
ctx.addIssue({
|
|
4492
|
+
code: "custom",
|
|
4493
|
+
path: ["guards", "canonicalMappings"],
|
|
4494
|
+
message: "Canonical mappings must name each member once and include a self-mapped canonical root."
|
|
4495
|
+
});
|
|
4496
|
+
}
|
|
4497
|
+
const diagnostics = artifact.review.diagnostics;
|
|
4498
|
+
if (diagnostics !== void 0) {
|
|
4499
|
+
const expectedRetained = Math.min(diagnostics.total, diagnostics.limit);
|
|
4500
|
+
if (diagnostics.entries.length !== expectedRetained || diagnostics.truncated !== diagnostics.total > diagnostics.entries.length) {
|
|
4501
|
+
ctx.addIssue({
|
|
4502
|
+
code: "custom",
|
|
4503
|
+
path: ["review", "diagnostics"],
|
|
4504
|
+
message: "Diagnostic retention and truncation metadata must exactly match total and limit."
|
|
4505
|
+
});
|
|
4506
|
+
}
|
|
4507
|
+
for (const [index, diagnostic] of diagnostics.entries.entries()) {
|
|
4508
|
+
addEvidenceIssues(
|
|
4509
|
+
diagnostic.evidence,
|
|
4510
|
+
["review", "diagnostics", "entries", index, "evidence"],
|
|
4511
|
+
ctx
|
|
4512
|
+
);
|
|
4513
|
+
const accepted = diagnostic.evidence.decision === "definitional" || diagnostic.evidence.score >= diagnostic.evidence.threshold && diagnostic.reason === void 0;
|
|
4514
|
+
if (diagnostic.scoreDecision !== (accepted ? "accepted" : "rejected") || diagnostic.evidence.decision === "scored" && diagnostic.reason === "noComparableValues" && diagnostic.evidence.score !== 0 || diagnostic.evidence.decision === "definitional" && (diagnostic.reason !== void 0 || typeof diagnostic.clusterDisposition !== "object") || diagnostic.scoreDecision === "rejected" && diagnostic.clusterDisposition !== void 0) {
|
|
4515
|
+
ctx.addIssue({
|
|
4516
|
+
code: "custom",
|
|
4517
|
+
path: ["review", "diagnostics", "entries", index],
|
|
4518
|
+
message: "Diagnostic decision, reason, and cluster disposition must agree with its score evidence."
|
|
4519
|
+
});
|
|
4520
|
+
}
|
|
4521
|
+
}
|
|
4522
|
+
}
|
|
4523
|
+
for (const [index, resolution] of artifact.review.resolutions.entries()) {
|
|
4524
|
+
addResolutionEvidenceIssues(artifact, resolution, index, ctx);
|
|
4525
|
+
}
|
|
4526
|
+
for (const [
|
|
4527
|
+
reconciliationIndex,
|
|
4528
|
+
reconciliation
|
|
4529
|
+
] of artifact.review.typeReconciliations.entries()) {
|
|
4530
|
+
for (const [edgeIndex, evidence] of (reconciliation.decisiveEdges ?? []).entries()) {
|
|
4531
|
+
addEvidenceIssues(
|
|
4532
|
+
evidence,
|
|
4533
|
+
[
|
|
4534
|
+
"review",
|
|
4535
|
+
"typeReconciliations",
|
|
4536
|
+
reconciliationIndex,
|
|
4537
|
+
"decisiveEdges",
|
|
4538
|
+
edgeIndex
|
|
4539
|
+
],
|
|
4540
|
+
ctx
|
|
4541
|
+
);
|
|
4542
|
+
}
|
|
4543
|
+
}
|
|
4544
|
+
}
|
|
4545
|
+
function addResolutionEvidenceIssues(artifact, resolution, resolutionIndex, ctx) {
|
|
4546
|
+
const retypedKinds = new Map(
|
|
4547
|
+
artifact.guards.retypes.map((retype) => [
|
|
4548
|
+
entityKey(retype.entity),
|
|
4549
|
+
retype.toKind
|
|
4550
|
+
])
|
|
4551
|
+
);
|
|
4552
|
+
const clusterMembers = artifact.guards.canonicalMappings.filter((mapping) => {
|
|
4553
|
+
const canonicalKind = retypedKinds.get(entityKey(mapping.canonical)) ?? mapping.canonical.kind;
|
|
4554
|
+
return mapping.canonical.id === resolution.canonicalId && canonicalKind === resolution.kind;
|
|
4555
|
+
}).map((mapping) => mapping.member);
|
|
4556
|
+
const uniqueMembers = new Map(
|
|
4557
|
+
clusterMembers.map((member) => [entityKey(member), member])
|
|
4558
|
+
);
|
|
4559
|
+
const memberIds = new Set(
|
|
4560
|
+
[...uniqueMembers.values()].map((member) => member.id)
|
|
4561
|
+
);
|
|
4562
|
+
const declaredMemberIds = new Set(resolution.memberIds);
|
|
4563
|
+
if (uniqueMembers.size < 2 || declaredMemberIds.size !== resolution.memberIds.length || memberIds.size !== declaredMemberIds.size || [...memberIds].some((id) => !declaredMemberIds.has(id)) || resolution.decisiveEdges.length !== uniqueMembers.size - 1) {
|
|
4564
|
+
ctx.addIssue({
|
|
4565
|
+
code: "custom",
|
|
4566
|
+
path: ["review", "resolutions", resolutionIndex],
|
|
4567
|
+
message: "A resolution must name its complete guarded cluster and carry exactly N-1 decisive edges."
|
|
4568
|
+
});
|
|
4569
|
+
return;
|
|
4570
|
+
}
|
|
4571
|
+
const parent = new Map([...uniqueMembers.keys()].map((key) => [key, key]));
|
|
4572
|
+
const find = (key) => {
|
|
4573
|
+
const next = parent.get(key);
|
|
4574
|
+
if (next === void 0 || next === key) return key;
|
|
4575
|
+
const root = find(next);
|
|
4576
|
+
parent.set(key, root);
|
|
4577
|
+
return root;
|
|
4578
|
+
};
|
|
4579
|
+
let invalidTree = false;
|
|
4580
|
+
for (const [edgeIndex, evidence] of resolution.decisiveEdges.entries()) {
|
|
4581
|
+
addEvidenceIssues(
|
|
4582
|
+
evidence,
|
|
4583
|
+
["review", "resolutions", resolutionIndex, "decisiveEdges", edgeIndex],
|
|
4584
|
+
ctx
|
|
4585
|
+
);
|
|
4586
|
+
const a = entityKey(evidence.a);
|
|
4587
|
+
const b = entityKey(evidence.b);
|
|
4588
|
+
if (!uniqueMembers.has(a) || !uniqueMembers.has(b)) {
|
|
4589
|
+
invalidTree = true;
|
|
4590
|
+
continue;
|
|
4591
|
+
}
|
|
4592
|
+
const rootA = find(a);
|
|
4593
|
+
const rootB = find(b);
|
|
4594
|
+
if (rootA === rootB) invalidTree = true;
|
|
4595
|
+
else parent.set(rootB, rootA);
|
|
4596
|
+
}
|
|
4597
|
+
if (invalidTree || new Set([...uniqueMembers.keys()].map((key) => find(key))).size !== 1) {
|
|
4598
|
+
ctx.addIssue({
|
|
4599
|
+
code: "custom",
|
|
4600
|
+
path: ["review", "resolutions", resolutionIndex, "decisiveEdges"],
|
|
4601
|
+
message: "Decisive edges must be an acyclic connected witness over the guarded cluster."
|
|
4602
|
+
});
|
|
4603
|
+
}
|
|
4604
|
+
}
|
|
4605
|
+
function addEvidenceIssues(evidence, path, ctx) {
|
|
4606
|
+
const sourcesCanonical = evidence.sources.every((source, index) => {
|
|
4607
|
+
const previous = evidence.sources[index - 1];
|
|
4608
|
+
return previous === void 0 || compareMatchSources(previous, source) < 0;
|
|
4609
|
+
});
|
|
4610
|
+
const invalidStrategy = evidence.decision === "scored" && (evidence.strategy.kind === "vector" && evidence.strategy.fields.length !== 1 || evidence.strategy.kind === "hybrid" && (evidence.strategy.weights.vector < 0 || evidence.strategy.weights.vector > 1 || evidence.strategy.weights.fulltext < 0 || evidence.strategy.weights.fulltext > 1 || Math.abs(
|
|
4611
|
+
evidence.strategy.weights.vector + evidence.strategy.weights.fulltext - 1
|
|
4612
|
+
) > Number.EPSILON));
|
|
4613
|
+
if (compareEntityRefs(evidence.a, evidence.b) >= 0 || evidence.sources.length === 0 || !sourcesCanonical || evidence.decision === "scored" && (evidence.score < 0 || evidence.score > 1 || evidence.threshold < 0 || evidence.threshold > 1) || invalidStrategy) {
|
|
4614
|
+
ctx.addIssue({
|
|
4615
|
+
code: "custom",
|
|
4616
|
+
path: [...path],
|
|
4617
|
+
message: "Evidence endpoints and sources must be canonical, and scored values must be within [0, 1]."
|
|
4618
|
+
});
|
|
4619
|
+
}
|
|
4620
|
+
}
|
|
4621
|
+
function addOperationIssues(deletions, upserts, role, ctx) {
|
|
4622
|
+
const deletionKeys = deletions.map((entry) => entityKey(entry));
|
|
4623
|
+
const upsertKeys = upserts.map((entry) => entityKey(entry));
|
|
4624
|
+
if (new Set(deletionKeys).size !== deletionKeys.length || new Set(upsertKeys).size !== upsertKeys.length) {
|
|
4625
|
+
ctx.addIssue({
|
|
4626
|
+
code: "custom",
|
|
4627
|
+
path: ["writes"],
|
|
4628
|
+
message: `The ${role} write set must not contain duplicate identities.`
|
|
4629
|
+
});
|
|
4630
|
+
}
|
|
4631
|
+
const deleted = new Set(deletionKeys);
|
|
4632
|
+
if (upsertKeys.some((key) => deleted.has(key))) {
|
|
4633
|
+
ctx.addIssue({
|
|
4634
|
+
code: "custom",
|
|
4635
|
+
path: ["writes"],
|
|
4636
|
+
message: `The same ${role} identity cannot be both deleted and upserted.`
|
|
4637
|
+
});
|
|
4638
|
+
}
|
|
4639
|
+
}
|
|
4640
|
+
function addPropertyPatchIssues(patch, path, ctx) {
|
|
4641
|
+
const unset = new Set(patch.unsetProps);
|
|
4642
|
+
if (unset.size !== patch.unsetProps.length || Object.keys(patch.setProps).some((key) => unset.has(key))) {
|
|
4643
|
+
ctx.addIssue({
|
|
4644
|
+
code: "custom",
|
|
4645
|
+
path: [...path],
|
|
4646
|
+
message: "unsetProps must be duplicate-free and disjoint from setProps keys."
|
|
4647
|
+
});
|
|
4648
|
+
}
|
|
4649
|
+
}
|
|
4650
|
+
function entityKey(entity) {
|
|
4651
|
+
return JSON.stringify([entity.kind, entity.id]);
|
|
4652
|
+
}
|
|
4653
|
+
var mergePlanArtifactV1InputSchema = mergePlanArtifactV1InputBaseSchema.superRefine(
|
|
4654
|
+
(artifact, ctx) => addSemanticIssues(artifact, ctx)
|
|
4655
|
+
);
|
|
4656
|
+
var mergePlanArtifactV1Schema = mergePlanArtifactV1BaseSchema.superRefine(
|
|
4657
|
+
(artifact, ctx) => addSemanticIssues(artifact, ctx)
|
|
4658
|
+
);
|
|
4659
|
+
|
|
4660
|
+
// src/graph-merge/plan-canonical.ts
|
|
4661
|
+
function canonicalMergePlanJson(artifact) {
|
|
4662
|
+
const digestless = "digest" in artifact ? omitDigest(artifact) : artifact;
|
|
4663
|
+
return JSON.stringify(digestless, sortedReplacer);
|
|
4664
|
+
}
|
|
4665
|
+
async function computeMergePlanDigest(artifact) {
|
|
4666
|
+
return sha256Hex(canonicalMergePlanJson(artifact), 32);
|
|
4667
|
+
}
|
|
4668
|
+
async function finalizeMergePlanArtifact(artifact) {
|
|
4669
|
+
const finalized = {
|
|
4670
|
+
...artifact,
|
|
4671
|
+
digest: {
|
|
4672
|
+
algorithm: MERGE_PLAN_DIGEST_ALGORITHM,
|
|
4673
|
+
value: await computeMergePlanDigest(artifact)
|
|
4674
|
+
}
|
|
4675
|
+
};
|
|
4676
|
+
const canonical = JSON.parse(
|
|
4677
|
+
JSON.stringify(finalized, sortedReplacer)
|
|
4678
|
+
);
|
|
4679
|
+
return canonical;
|
|
4680
|
+
}
|
|
4681
|
+
function omitDigest(artifact) {
|
|
4682
|
+
const { digest: _digest, ...digestless } = artifact;
|
|
4683
|
+
return digestless;
|
|
4684
|
+
}
|
|
4685
|
+
|
|
4686
|
+
// src/graph-merge/plan-wire.ts
|
|
4687
|
+
function parseMergePlanArtifact(input) {
|
|
4688
|
+
const version = readFormatVersion(input);
|
|
4689
|
+
if (version !== MERGE_PLAN_FORMAT_VERSION) {
|
|
4690
|
+
return {
|
|
4691
|
+
success: false,
|
|
4692
|
+
error: { kind: "unsupported-version", received: version }
|
|
4693
|
+
};
|
|
4694
|
+
}
|
|
4695
|
+
const parsed = mergePlanArtifactV1Schema.safeParse(input);
|
|
4696
|
+
if (!parsed.success) {
|
|
4697
|
+
return {
|
|
4698
|
+
success: false,
|
|
4699
|
+
error: { kind: "malformed", issues: parsed.error.issues }
|
|
4700
|
+
};
|
|
4701
|
+
}
|
|
4702
|
+
return {
|
|
4703
|
+
success: true,
|
|
4704
|
+
artifact: parsed.data
|
|
4705
|
+
};
|
|
4706
|
+
}
|
|
4707
|
+
async function verifyMergePlanDigest(artifact) {
|
|
4708
|
+
const expected = await computeMergePlanDigest(artifact);
|
|
4709
|
+
const received = artifact.digest.value;
|
|
4710
|
+
return expected === received ? { valid: true } : { valid: false, expected, received };
|
|
4711
|
+
}
|
|
4712
|
+
async function validateMergePlanArtifact(input) {
|
|
4713
|
+
const parsed = parseMergePlanArtifact(input);
|
|
4714
|
+
if (!parsed.success) return parsed;
|
|
4715
|
+
const digest = await verifyMergePlanDigest(parsed.artifact);
|
|
4716
|
+
if (!digest.valid) {
|
|
4717
|
+
return {
|
|
4718
|
+
success: false,
|
|
4719
|
+
error: {
|
|
4720
|
+
kind: "digest-mismatch",
|
|
4721
|
+
expected: digest.expected,
|
|
4722
|
+
received: digest.received
|
|
4723
|
+
}
|
|
4724
|
+
};
|
|
4725
|
+
}
|
|
4726
|
+
return parsed;
|
|
4727
|
+
}
|
|
4728
|
+
async function constructMergePlanArtifact(input) {
|
|
4729
|
+
const validatedInput = mergePlanArtifactV1InputSchema.parse(input);
|
|
4730
|
+
return finalizeMergePlanArtifact(
|
|
4731
|
+
validatedInput
|
|
4732
|
+
);
|
|
4733
|
+
}
|
|
4734
|
+
function readFormatVersion(input) {
|
|
4735
|
+
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
4736
|
+
return void 0;
|
|
4737
|
+
}
|
|
4738
|
+
return Reflect.get(input, "formatVersion");
|
|
4739
|
+
}
|
|
3326
4740
|
var PROVENANCE_KIND = "Provenance";
|
|
3327
4741
|
var PROVENANCE_OWNER_KIND = "ProvenanceOwner";
|
|
3328
4742
|
var Provenance = defineNode(PROVENANCE_KIND, {
|
|
@@ -3673,10 +5087,10 @@ async function readProvenance(store, query = {}) {
|
|
|
3673
5087
|
}
|
|
3674
5088
|
|
|
3675
5089
|
// src/graph-merge/sources.ts
|
|
3676
|
-
function orderEndpoints(left, right) {
|
|
5090
|
+
function orderEndpoints(left, right, sources) {
|
|
3677
5091
|
const leftKey = mergeKeyOf(left);
|
|
3678
5092
|
const rightKey = mergeKeyOf(right);
|
|
3679
|
-
return compareMergeKeys(leftKey, rightKey) <= 0 ? { a: leftKey, b: rightKey, left, right } : { a: rightKey, b: leftKey, left: right, right: left };
|
|
5093
|
+
return compareMergeKeys(leftKey, rightKey) <= 0 ? { a: leftKey, b: rightKey, left, right, sources } : { a: rightKey, b: leftKey, left: right, right: left, sources };
|
|
3680
5094
|
}
|
|
3681
5095
|
function sortMembersById(members) {
|
|
3682
5096
|
return [...members].sort((left, right) => compareStrings(left.id, right.id));
|
|
@@ -3719,7 +5133,8 @@ function windowedPairs(members, config) {
|
|
|
3719
5133
|
pairs.push(
|
|
3720
5134
|
orderEndpoints(
|
|
3721
5135
|
requireDefined(keyed[index]).node,
|
|
3722
|
-
requireDefined(keyed[index_]).node
|
|
5136
|
+
requireDefined(keyed[index_]).node,
|
|
5137
|
+
[{ kind: "keyless", sourceId: "keyless" }]
|
|
3723
5138
|
)
|
|
3724
5139
|
);
|
|
3725
5140
|
}
|
|
@@ -3733,8 +5148,22 @@ function pairsFromBlocks(blocks, keyless) {
|
|
|
3733
5148
|
continue;
|
|
3734
5149
|
}
|
|
3735
5150
|
const members = sortMembersById(blocks.get(bucketKey) ?? []);
|
|
3736
|
-
if (
|
|
3737
|
-
|
|
5151
|
+
if (bucketKey === UNBLOCKED_BUCKET_KEY) {
|
|
5152
|
+
if (keyless !== void 0) {
|
|
5153
|
+
pairs.push(...windowedPairs(members, keyless));
|
|
5154
|
+
continue;
|
|
5155
|
+
}
|
|
5156
|
+
for (let index = 0; index < members.length; index += 1) {
|
|
5157
|
+
for (let index_ = index + 1; index_ < members.length; index_ += 1) {
|
|
5158
|
+
pairs.push(
|
|
5159
|
+
orderEndpoints(
|
|
5160
|
+
requireDefined(members[index]),
|
|
5161
|
+
requireDefined(members[index_]),
|
|
5162
|
+
[{ kind: "keyless", sourceId: "keyless" }]
|
|
5163
|
+
)
|
|
5164
|
+
);
|
|
5165
|
+
}
|
|
5166
|
+
}
|
|
3738
5167
|
continue;
|
|
3739
5168
|
}
|
|
3740
5169
|
for (let index = 0; index < members.length; index += 1) {
|
|
@@ -3742,7 +5171,8 @@ function pairsFromBlocks(blocks, keyless) {
|
|
|
3742
5171
|
pairs.push(
|
|
3743
5172
|
orderEndpoints(
|
|
3744
5173
|
requireDefined(members[index]),
|
|
3745
|
-
requireDefined(members[index_])
|
|
5174
|
+
requireDefined(members[index_]),
|
|
5175
|
+
[{ kind: "block", sourceId: "exactKey" }]
|
|
3746
5176
|
)
|
|
3747
5177
|
);
|
|
3748
5178
|
}
|
|
@@ -3757,13 +5187,37 @@ function forcedEdgesFromBlocks(blocks) {
|
|
|
3757
5187
|
continue;
|
|
3758
5188
|
}
|
|
3759
5189
|
const members = sortMembersById(blocks.get(bucketKey) ?? []);
|
|
5190
|
+
const constraintName = uniqueConstraintNameForBucket(bucketKey);
|
|
5191
|
+
if (constraintName === void 0) {
|
|
5192
|
+
throw new CandidateSourceError(
|
|
5193
|
+
"Unique candidate bucket has no constraint name.",
|
|
5194
|
+
{
|
|
5195
|
+
details: {
|
|
5196
|
+
operation: "candidateEvidence",
|
|
5197
|
+
sourceId: "unique",
|
|
5198
|
+
kind: members[0]?.kind ?? "unknown"
|
|
5199
|
+
}
|
|
5200
|
+
}
|
|
5201
|
+
);
|
|
5202
|
+
}
|
|
3760
5203
|
for (let index = 0; index < members.length; index += 1) {
|
|
3761
5204
|
for (let index_ = index + 1; index_ < members.length; index_ += 1) {
|
|
3762
5205
|
const { a, b } = orderEndpoints(
|
|
3763
5206
|
requireDefined(members[index]),
|
|
3764
|
-
requireDefined(members[index_])
|
|
5207
|
+
requireDefined(members[index_]),
|
|
5208
|
+
[]
|
|
3765
5209
|
);
|
|
3766
|
-
edges.push({
|
|
5210
|
+
edges.push({
|
|
5211
|
+
a,
|
|
5212
|
+
b,
|
|
5213
|
+
score: FORCED_MATCH_SCORE,
|
|
5214
|
+
evidence: {
|
|
5215
|
+
a: entityRef(a),
|
|
5216
|
+
b: entityRef(b),
|
|
5217
|
+
sources: [{ kind: "unique", sourceId: "unique", constraintName }],
|
|
5218
|
+
decision: "definitional"
|
|
5219
|
+
}
|
|
5220
|
+
});
|
|
3767
5221
|
}
|
|
3768
5222
|
}
|
|
3769
5223
|
}
|
|
@@ -3797,7 +5251,13 @@ function ontologyRetypeEdges(stagedIdentities, isRetypeCompatible) {
|
|
|
3797
5251
|
edges.push({
|
|
3798
5252
|
a: requireDefined(members[0]),
|
|
3799
5253
|
b: requireDefined(members[index]),
|
|
3800
|
-
score: FORCED_MATCH_SCORE
|
|
5254
|
+
score: FORCED_MATCH_SCORE,
|
|
5255
|
+
evidence: {
|
|
5256
|
+
a: entityRef(requireDefined(members[0])),
|
|
5257
|
+
b: entityRef(requireDefined(members[index])),
|
|
5258
|
+
sources: [{ kind: "retype", sourceId: "retype" }],
|
|
5259
|
+
decision: "definitional"
|
|
5260
|
+
}
|
|
3801
5261
|
});
|
|
3802
5262
|
}
|
|
3803
5263
|
}
|
|
@@ -3864,9 +5324,16 @@ var baseUniqueSource = {
|
|
|
3864
5324
|
async generate(scope) {
|
|
3865
5325
|
const { kind, nodes, uniqueConstraints, store } = scope;
|
|
3866
5326
|
if (nodes === void 0 || uniqueConstraints === void 0 || store === void 0) {
|
|
3867
|
-
throw new
|
|
5327
|
+
throw new CandidateSourceError(
|
|
3868
5328
|
"baseUniqueSource requires nodes, uniqueConstraints, and store in the source scope.",
|
|
3869
|
-
{
|
|
5329
|
+
{
|
|
5330
|
+
details: {
|
|
5331
|
+
kind,
|
|
5332
|
+
source: "baseUnique",
|
|
5333
|
+
sourceId: "baseUnique",
|
|
5334
|
+
operation: "generate"
|
|
5335
|
+
}
|
|
5336
|
+
}
|
|
3870
5337
|
);
|
|
3871
5338
|
}
|
|
3872
5339
|
const collection = store.nodes[kind];
|
|
@@ -3884,14 +5351,23 @@ var baseUniqueSource = {
|
|
|
3884
5351
|
try {
|
|
3885
5352
|
return await collection.bulkFindByConstraint(constraint.name, items);
|
|
3886
5353
|
} catch (error) {
|
|
3887
|
-
throw new
|
|
5354
|
+
throw new CandidateSourceError(
|
|
3888
5355
|
`baseUniqueSource: new-vs-base lookup for constraint "${constraint.name}" on kind "${kind}" failed \u2014 a staged node's props could not be validated against the kind's schema for constraint-key computation.`,
|
|
3889
|
-
{
|
|
5356
|
+
{
|
|
5357
|
+
details: {
|
|
5358
|
+
kind,
|
|
5359
|
+
sourceId: "baseUnique",
|
|
5360
|
+
operation: "lookupConstraint",
|
|
5361
|
+
constraint: constraint.name
|
|
5362
|
+
},
|
|
5363
|
+
cause: error
|
|
5364
|
+
}
|
|
3890
5365
|
);
|
|
3891
5366
|
}
|
|
3892
5367
|
})
|
|
3893
5368
|
);
|
|
3894
|
-
for (const matches of matchesByConstraint) {
|
|
5369
|
+
for (const [constraintIndex, matches] of matchesByConstraint.entries()) {
|
|
5370
|
+
const constraint = requireDefined(constraints[constraintIndex]);
|
|
3895
5371
|
for (const [index, node] of nodes.entries()) {
|
|
3896
5372
|
const base = matches[index];
|
|
3897
5373
|
if (base === void 0) {
|
|
@@ -3905,14 +5381,32 @@ var baseUniqueSource = {
|
|
|
3905
5381
|
origin: "base",
|
|
3906
5382
|
id: base.id,
|
|
3907
5383
|
kind: base.kind,
|
|
3908
|
-
props: nodeProps(base)
|
|
5384
|
+
props: nodeProps(base),
|
|
5385
|
+
...base.meta.validFrom === void 0 ? {} : { validFrom: base.meta.validFrom },
|
|
5386
|
+
...base.meta.validTo === void 0 ? {} : { validTo: base.meta.validTo }
|
|
3909
5387
|
});
|
|
3910
5388
|
}
|
|
3911
5389
|
if (stagedKey === baseKey) {
|
|
3912
5390
|
continue;
|
|
3913
5391
|
}
|
|
3914
5392
|
const { a, b } = compareMergeKeys(stagedKey, baseKey) <= 0 ? { a: stagedKey, b: baseKey } : { a: baseKey, b: stagedKey };
|
|
3915
|
-
forcedEdges.push({
|
|
5393
|
+
forcedEdges.push({
|
|
5394
|
+
a,
|
|
5395
|
+
b,
|
|
5396
|
+
score: FORCED_MATCH_SCORE,
|
|
5397
|
+
evidence: {
|
|
5398
|
+
a: entityRef(a),
|
|
5399
|
+
b: entityRef(b),
|
|
5400
|
+
sources: [
|
|
5401
|
+
{
|
|
5402
|
+
kind: "baseUnique",
|
|
5403
|
+
sourceId: "baseUnique",
|
|
5404
|
+
constraintName: constraint.name
|
|
5405
|
+
}
|
|
5406
|
+
],
|
|
5407
|
+
decision: "definitional"
|
|
5408
|
+
}
|
|
5409
|
+
});
|
|
3916
5410
|
}
|
|
3917
5411
|
}
|
|
3918
5412
|
return {
|
|
@@ -3932,9 +5426,16 @@ var baseKeySource = {
|
|
|
3932
5426
|
return { pairs: [], forcedEdges: [], baseMembers: [] };
|
|
3933
5427
|
}
|
|
3934
5428
|
if (nodes === void 0 || store === void 0) {
|
|
3935
|
-
throw new
|
|
5429
|
+
throw new CandidateSourceError(
|
|
3936
5430
|
"baseKeySource requires nodes and store in the source scope.",
|
|
3937
|
-
{
|
|
5431
|
+
{
|
|
5432
|
+
details: {
|
|
5433
|
+
kind,
|
|
5434
|
+
source: "baseKey",
|
|
5435
|
+
sourceId: "baseKey",
|
|
5436
|
+
operation: "generate"
|
|
5437
|
+
}
|
|
5438
|
+
}
|
|
3938
5439
|
);
|
|
3939
5440
|
}
|
|
3940
5441
|
const collection = store.nodes[kind];
|
|
@@ -3946,9 +5447,17 @@ var baseKeySource = {
|
|
|
3946
5447
|
try {
|
|
3947
5448
|
matchesByNode = await collection.bulkFindByIndex(blockIndex, items);
|
|
3948
5449
|
} catch (error) {
|
|
3949
|
-
throw new
|
|
5450
|
+
throw new CandidateSourceError(
|
|
3950
5451
|
`baseKeySource: new-vs-base lookup on index "${blockIndex}" for kind "${kind}" failed \u2014 the index is undeclared or a staged node's props are incompatible with its indexed fields.`,
|
|
3951
|
-
{
|
|
5452
|
+
{
|
|
5453
|
+
details: {
|
|
5454
|
+
kind,
|
|
5455
|
+
sourceId: "baseKey",
|
|
5456
|
+
operation: "lookupIndex",
|
|
5457
|
+
index: blockIndex
|
|
5458
|
+
},
|
|
5459
|
+
cause: error
|
|
5460
|
+
}
|
|
3952
5461
|
);
|
|
3953
5462
|
}
|
|
3954
5463
|
const pairs = [];
|
|
@@ -3963,13 +5472,19 @@ var baseKeySource = {
|
|
|
3963
5472
|
origin: "base",
|
|
3964
5473
|
id: base.id,
|
|
3965
5474
|
kind: base.kind,
|
|
3966
|
-
props: nodeProps(base)
|
|
5475
|
+
props: nodeProps(base),
|
|
5476
|
+
...base.meta.validFrom === void 0 ? {} : { validFrom: base.meta.validFrom },
|
|
5477
|
+
...base.meta.validTo === void 0 ? {} : { validTo: base.meta.validTo }
|
|
3967
5478
|
});
|
|
3968
5479
|
}
|
|
3969
5480
|
if (stagedKey === baseKey) {
|
|
3970
5481
|
continue;
|
|
3971
5482
|
}
|
|
3972
|
-
pairs.push(
|
|
5483
|
+
pairs.push(
|
|
5484
|
+
orderEndpoints(stagedNode, base, [
|
|
5485
|
+
{ kind: "baseIndex", sourceId: "baseKey", indexName: blockIndex }
|
|
5486
|
+
])
|
|
5487
|
+
);
|
|
3973
5488
|
}
|
|
3974
5489
|
}
|
|
3975
5490
|
return {
|
|
@@ -4321,6 +5836,8 @@ async function generateAllCandidates(target, staging, options, introspectionKind
|
|
|
4321
5836
|
const allEdges = [];
|
|
4322
5837
|
const warnings = [];
|
|
4323
5838
|
const baseMembers = [];
|
|
5839
|
+
const diagnostics = [];
|
|
5840
|
+
let diagnosticsTotal = 0;
|
|
4324
5841
|
const byKind = newNodesByKind(staging);
|
|
4325
5842
|
const sources = useBaseSources ? [...CANDIDATE_SOURCES, baseUniqueSource, baseKeySource] : CANDIDATE_SOURCES;
|
|
4326
5843
|
const baseStore = target;
|
|
@@ -4329,7 +5846,13 @@ async function generateAllCandidates(target, staging, options, introspectionKind
|
|
|
4329
5846
|
async ([kind, items]) => {
|
|
4330
5847
|
const resolveConfig = options.resolve[kind];
|
|
4331
5848
|
if (resolveConfig === void 0) {
|
|
4332
|
-
return ok({
|
|
5849
|
+
return ok({
|
|
5850
|
+
edges: [],
|
|
5851
|
+
warnings: [],
|
|
5852
|
+
baseMembers: [],
|
|
5853
|
+
diagnostics: [],
|
|
5854
|
+
diagnosticsTotal: 0
|
|
5855
|
+
});
|
|
4333
5856
|
}
|
|
4334
5857
|
const nodes = items.map((staged) => asNode(staged));
|
|
4335
5858
|
const uniqueConstraints = uniqueConstraintsFor(
|
|
@@ -4370,7 +5893,8 @@ async function generateAllCandidates(target, staging, options, introspectionKind
|
|
|
4370
5893
|
resolveConfig,
|
|
4371
5894
|
kindCtx,
|
|
4372
5895
|
options.onComparisonCeiling,
|
|
4373
|
-
options.maxComparisonsPerKind
|
|
5896
|
+
options.maxComparisonsPerKind,
|
|
5897
|
+
options.candidateDiagnostics?.limit ?? 0
|
|
4374
5898
|
);
|
|
4375
5899
|
if (isErr(scored)) {
|
|
4376
5900
|
return err(scored.error);
|
|
@@ -4380,7 +5904,9 @@ async function generateAllCandidates(target, staging, options, introspectionKind
|
|
|
4380
5904
|
warnings: scored.data.warnings.map(
|
|
4381
5905
|
(warning) => `[${kind}] ${warning.message}`
|
|
4382
5906
|
),
|
|
4383
|
-
baseMembers: kindBaseMembers
|
|
5907
|
+
baseMembers: kindBaseMembers,
|
|
5908
|
+
diagnostics: scored.data.diagnostics,
|
|
5909
|
+
diagnosticsTotal: scored.data.diagnosticsTotal
|
|
4384
5910
|
});
|
|
4385
5911
|
}
|
|
4386
5912
|
)
|
|
@@ -4392,6 +5918,14 @@ async function generateAllCandidates(target, staging, options, introspectionKind
|
|
|
4392
5918
|
allEdges.push(...result.data.edges);
|
|
4393
5919
|
warnings.push(...result.data.warnings);
|
|
4394
5920
|
baseMembers.push(...result.data.baseMembers);
|
|
5921
|
+
if (options.candidateDiagnostics !== void 0) {
|
|
5922
|
+
diagnostics.push(...result.data.diagnostics);
|
|
5923
|
+
diagnostics.sort(
|
|
5924
|
+
(left, right) => compareMatchEvidence(left.evidence, right.evidence)
|
|
5925
|
+
);
|
|
5926
|
+
diagnostics.splice(options.candidateDiagnostics.limit);
|
|
5927
|
+
}
|
|
5928
|
+
diagnosticsTotal += result.data.diagnosticsTotal;
|
|
4395
5929
|
}
|
|
4396
5930
|
return ok({
|
|
4397
5931
|
// The ONE shared `(a, b)` edge comparator (id-first `(kind, id)` order), so this
|
|
@@ -4402,7 +5936,9 @@ async function generateAllCandidates(target, staging, options, introspectionKind
|
|
|
4402
5936
|
// base members would tie under a bare-id compare).
|
|
4403
5937
|
baseMembers: baseMembers.sort(
|
|
4404
5938
|
(left, right) => compareMergeKeys(mergeKeyOf(left), mergeKeyOf(right))
|
|
4405
|
-
)
|
|
5939
|
+
),
|
|
5940
|
+
diagnostics,
|
|
5941
|
+
diagnosticsTotal
|
|
4406
5942
|
});
|
|
4407
5943
|
}
|
|
4408
5944
|
function clusterMembersFor(cluster, newNodesById, baseMembersById) {
|
|
@@ -4426,7 +5962,9 @@ function clusterMembersFor(cluster, newNodesById, baseMembersById) {
|
|
|
4426
5962
|
id: base.id,
|
|
4427
5963
|
kind: base.kind,
|
|
4428
5964
|
branchId: BASE_PROVENANCE_BRANCH,
|
|
4429
|
-
props: base.props
|
|
5965
|
+
props: base.props,
|
|
5966
|
+
...base.validFrom === void 0 ? {} : { validFrom: base.validFrom },
|
|
5967
|
+
...base.validTo === void 0 ? {} : { validTo: base.validTo }
|
|
4430
5968
|
});
|
|
4431
5969
|
}
|
|
4432
5970
|
}
|
|
@@ -4459,7 +5997,7 @@ function buildStagedEdges(staging, modifiedEdges, edgeValidityEnds, edgeDeletion
|
|
|
4459
5997
|
for (const item of modifiedEdges) {
|
|
4460
5998
|
const identity = mergeKeyOf(item.edge);
|
|
4461
5999
|
stagedIdentities.add(identity);
|
|
4462
|
-
const
|
|
6000
|
+
const validToChange = edgeValidityEnds.get(identity);
|
|
4463
6001
|
staged.push({
|
|
4464
6002
|
id: item.edge.id,
|
|
4465
6003
|
kind: item.edge.kind,
|
|
@@ -4471,13 +6009,13 @@ function buildStagedEdges(staging, modifiedEdges, edgeValidityEnds, edgeDeletion
|
|
|
4471
6009
|
props: item.edge.forkProps,
|
|
4472
6010
|
baseProps: item.edge.baseProps,
|
|
4473
6011
|
branchId: item.branchId,
|
|
4474
|
-
...
|
|
6012
|
+
...validToChange?.kind === "set" ? { validTo: validToChange.validTo } : validToChange?.kind === "clear" ? { clearValidTo: true } : {}
|
|
4475
6013
|
});
|
|
4476
6014
|
}
|
|
4477
6015
|
for (const item of staging.windowedEdges) {
|
|
4478
6016
|
const identity = mergeKeyOf(item.edge);
|
|
4479
|
-
const
|
|
4480
|
-
if (
|
|
6017
|
+
const validToChange = edgeValidityEnds.get(identity);
|
|
6018
|
+
if (validToChange === void 0 || stagedIdentities.has(identity) || edgeDeletions.has(identity)) {
|
|
4481
6019
|
continue;
|
|
4482
6020
|
}
|
|
4483
6021
|
stagedIdentities.add(identity);
|
|
@@ -4493,7 +6031,7 @@ function buildStagedEdges(staging, modifiedEdges, edgeValidityEnds, edgeDeletion
|
|
|
4493
6031
|
props: item.edge.props,
|
|
4494
6032
|
baseProps: item.edge.baseProps,
|
|
4495
6033
|
branchId: item.branchId,
|
|
4496
|
-
validTo
|
|
6034
|
+
...validToChange.kind === "set" ? { validTo: validToChange.validTo } : { clearValidTo: true }
|
|
4497
6035
|
});
|
|
4498
6036
|
}
|
|
4499
6037
|
return { edges: staged, windowOnlyCarried };
|
|
@@ -4541,7 +6079,7 @@ function buildProvenanceIndex(records) {
|
|
|
4541
6079
|
};
|
|
4542
6080
|
}
|
|
4543
6081
|
var EMPTY_WEIGHTS = /* @__PURE__ */ new Map();
|
|
4544
|
-
function
|
|
6082
|
+
function buildInternalMergePlan(staging, candidateEdges, candidateWarnings, candidateDiagnostics, candidateDiagnosticsTotal, baseMembers, options, branchRank, subClassClosure, identityContext, storedIdentityRowsById, targetPeers, preferredBranchId) {
|
|
4545
6083
|
const identity = planIdentityChanges(staging, storedIdentityRowsById);
|
|
4546
6084
|
const provenanceRecords = [];
|
|
4547
6085
|
const recordedContributions = /* @__PURE__ */ new Set();
|
|
@@ -4593,7 +6131,56 @@ function planMerge(staging, candidateEdges, candidateWarnings, baseMembers, opti
|
|
|
4593
6131
|
clusterEdges,
|
|
4594
6132
|
baseIds
|
|
4595
6133
|
);
|
|
4596
|
-
const
|
|
6134
|
+
const diameterGuard = options.clusterMaxDiameter === void 0 ? {
|
|
6135
|
+
clusters: guard.clusters,
|
|
6136
|
+
survivingEdges: guard.survivingEdges,
|
|
6137
|
+
excludedEdges: []
|
|
6138
|
+
} : enforceDiameterWithEdges(
|
|
6139
|
+
guard.clusters,
|
|
6140
|
+
clusterEdges,
|
|
6141
|
+
options.clusterMaxDiameter
|
|
6142
|
+
);
|
|
6143
|
+
const clusters = diameterGuard.clusters;
|
|
6144
|
+
const baseSurvivingEdges = new Set(guard.survivingEdges);
|
|
6145
|
+
const survivingEdges = diameterGuard.survivingEdges.filter(
|
|
6146
|
+
(edge) => baseSurvivingEdges.has(edge)
|
|
6147
|
+
);
|
|
6148
|
+
const excludedByEndpoints = /* @__PURE__ */ new Map();
|
|
6149
|
+
for (const excluded of [
|
|
6150
|
+
...guard.excludedEdges,
|
|
6151
|
+
...diameterGuard.excludedEdges
|
|
6152
|
+
]) {
|
|
6153
|
+
excludedByEndpoints.set(
|
|
6154
|
+
JSON.stringify([excluded.edge.a, excluded.edge.b]),
|
|
6155
|
+
excluded.reason
|
|
6156
|
+
);
|
|
6157
|
+
}
|
|
6158
|
+
const diagnosticsWithDisposition = candidateDiagnostics.map((diagnostic) => {
|
|
6159
|
+
if (diagnostic.evidence.decision === "definitional") return diagnostic;
|
|
6160
|
+
if (diagnostic.scoreDecision === "rejected") return diagnostic;
|
|
6161
|
+
const a = mergeKey(diagnostic.evidence.a.kind, diagnostic.evidence.a.id);
|
|
6162
|
+
const b = mergeKey(diagnostic.evidence.b.kind, diagnostic.evidence.b.id);
|
|
6163
|
+
const reason = excludedByEndpoints.get(JSON.stringify([a, b]));
|
|
6164
|
+
return {
|
|
6165
|
+
...diagnostic,
|
|
6166
|
+
clusterDisposition: reason === void 0 ? "retained" : { kind: "excluded", reason }
|
|
6167
|
+
};
|
|
6168
|
+
});
|
|
6169
|
+
const definitionalExclusions = [
|
|
6170
|
+
...guard.excludedEdges,
|
|
6171
|
+
...diameterGuard.excludedEdges
|
|
6172
|
+
].filter((excluded) => excluded.edge.evidence.decision === "definitional").map((excluded) => ({
|
|
6173
|
+
evidence: excluded.edge.evidence,
|
|
6174
|
+
scoreDecision: "accepted",
|
|
6175
|
+
clusterDisposition: {
|
|
6176
|
+
kind: "excluded",
|
|
6177
|
+
reason: excluded.reason
|
|
6178
|
+
}
|
|
6179
|
+
}));
|
|
6180
|
+
const retainedDiagnostics = [
|
|
6181
|
+
...diagnosticsWithDisposition,
|
|
6182
|
+
...definitionalExclusions
|
|
6183
|
+
].sort((left, right) => compareMatchEvidence(left.evidence, right.evidence));
|
|
4597
6184
|
const baseAmbiguities = guard.events.map((event) => ({
|
|
4598
6185
|
baseIds: event.baseIds.map((key) => ({ kind: kindOf(key), id: idOf(key) })),
|
|
4599
6186
|
memberIds: event.memberIds.map((key) => ({
|
|
@@ -4603,6 +6190,7 @@ function planMerge(staging, candidateEdges, candidateWarnings, baseMembers, opti
|
|
|
4603
6190
|
}));
|
|
4604
6191
|
const canonicalEntities = [];
|
|
4605
6192
|
const resolutions = [];
|
|
6193
|
+
const clusterByCanonicalIdentity = /* @__PURE__ */ new Map();
|
|
4606
6194
|
const propertyConflicts = [];
|
|
4607
6195
|
const reconcileInputs = [];
|
|
4608
6196
|
for (const cluster of clusters) {
|
|
@@ -4622,6 +6210,10 @@ function planMerge(staging, candidateEdges, candidateWarnings, baseMembers, opti
|
|
|
4622
6210
|
preferredBranchId
|
|
4623
6211
|
);
|
|
4624
6212
|
canonicalEntities.push(entity);
|
|
6213
|
+
clusterByCanonicalIdentity.set(
|
|
6214
|
+
mergeKey(entity.kind, entity.canonicalId),
|
|
6215
|
+
cluster
|
|
6216
|
+
);
|
|
4625
6217
|
reconcileInputs.push({
|
|
4626
6218
|
canonicalId: mergeKey(entity.kind, entity.canonicalId),
|
|
4627
6219
|
memberKinds: members.map((member) => member.kind)
|
|
@@ -4630,7 +6222,10 @@ function planMerge(staging, candidateEdges, candidateWarnings, baseMembers, opti
|
|
|
4630
6222
|
cluster.members.map((member) => idOf(member))
|
|
4631
6223
|
);
|
|
4632
6224
|
if (distinctMergedIds.size > 1) {
|
|
4633
|
-
resolutions.push(
|
|
6225
|
+
resolutions.push({
|
|
6226
|
+
...entity.resolution,
|
|
6227
|
+
decisiveEdges: decisiveEdgesForCluster(cluster, survivingEdges)
|
|
6228
|
+
});
|
|
4634
6229
|
}
|
|
4635
6230
|
for (const conflict of entity.conflicts) {
|
|
4636
6231
|
propertyConflicts.push(conflict);
|
|
@@ -4761,6 +6356,21 @@ function planMerge(staging, candidateEdges, candidateWarnings, baseMembers, opti
|
|
|
4761
6356
|
subClassClosure,
|
|
4762
6357
|
options.reconcileTypes
|
|
4763
6358
|
);
|
|
6359
|
+
const reconciledClusterByEntityId = /* @__PURE__ */ new Map();
|
|
6360
|
+
for (const identity2 of reconciliation.retypeMap.keys()) {
|
|
6361
|
+
const entityId = idOf(identity2);
|
|
6362
|
+
const cluster = clusterByCanonicalIdentity.get(identity2);
|
|
6363
|
+
if (cluster !== void 0 && !reconciledClusterByEntityId.has(entityId)) {
|
|
6364
|
+
reconciledClusterByEntityId.set(entityId, cluster);
|
|
6365
|
+
}
|
|
6366
|
+
}
|
|
6367
|
+
const typeReconciliations = reconciliation.reconciliations.map((item) => {
|
|
6368
|
+
const cluster = reconciledClusterByEntityId.get(item.entityId);
|
|
6369
|
+
return cluster === void 0 ? item : {
|
|
6370
|
+
...item,
|
|
6371
|
+
decisiveEdges: decisiveEdgesForCluster(cluster, survivingEdges)
|
|
6372
|
+
};
|
|
6373
|
+
});
|
|
4764
6374
|
const entityByIdentity = /* @__PURE__ */ new Map();
|
|
4765
6375
|
for (const entity of canonicalEntities) {
|
|
4766
6376
|
entityByIdentity.set(mergeKey(entity.kind, entity.canonicalId), entity);
|
|
@@ -4769,11 +6379,24 @@ function planMerge(staging, candidateEdges, candidateWarnings, baseMembers, opti
|
|
|
4769
6379
|
clusters,
|
|
4770
6380
|
(cluster) => pickClusterCanonical(cluster, entityByIdentity)
|
|
4771
6381
|
);
|
|
6382
|
+
const canonicalEndpointWindows = /* @__PURE__ */ new Map();
|
|
6383
|
+
for (const entity of canonicalEntities) {
|
|
6384
|
+
if (entity.endpointValidFrom === void 0 && entity.endpointValidTo === void 0) {
|
|
6385
|
+
continue;
|
|
6386
|
+
}
|
|
6387
|
+
const sourceKey2 = mergeKey(entity.kind, entity.canonicalId);
|
|
6388
|
+
const finalKind = reconciliation.retypeMap.get(sourceKey2) ?? entity.kind;
|
|
6389
|
+
canonicalEndpointWindows.set(mergeKey(finalKind, entity.canonicalId), {
|
|
6390
|
+
...entity.endpointValidFrom === void 0 ? {} : { validFrom: entity.endpointValidFrom },
|
|
6391
|
+
...entity.endpointValidTo === void 0 ? {} : { validTo: entity.endpointValidTo }
|
|
6392
|
+
});
|
|
6393
|
+
}
|
|
4772
6394
|
const identityRemap = remapIdentityAssertionEndpoints(
|
|
4773
6395
|
identity.assertions,
|
|
4774
6396
|
canonicalOf,
|
|
4775
6397
|
reconciliation.retypeMap,
|
|
4776
|
-
storedIdentityRowsById
|
|
6398
|
+
storedIdentityRowsById,
|
|
6399
|
+
canonicalEndpointWindows
|
|
4777
6400
|
);
|
|
4778
6401
|
assertIdentityEndpointsNotDeleted(
|
|
4779
6402
|
identityRemap.assertions,
|
|
@@ -4897,7 +6520,7 @@ function planMerge(staging, candidateEdges, candidateWarnings, baseMembers, opti
|
|
|
4897
6520
|
mergeKey(right.kind, right.entityId)
|
|
4898
6521
|
)
|
|
4899
6522
|
),
|
|
4900
|
-
typeReconciliations
|
|
6523
|
+
typeReconciliations,
|
|
4901
6524
|
dropped,
|
|
4902
6525
|
baseAmbiguities: baseAmbiguities.sort(
|
|
4903
6526
|
(left, right) => compareMergeKeys(
|
|
@@ -4912,7 +6535,18 @@ function planMerge(staging, candidateEdges, candidateWarnings, baseMembers, opti
|
|
|
4912
6535
|
)
|
|
4913
6536
|
),
|
|
4914
6537
|
provenanceRecords,
|
|
4915
|
-
warnings: candidateWarnings,
|
|
6538
|
+
warnings: [...candidateWarnings, ...identityRemap.warnings],
|
|
6539
|
+
...options.candidateDiagnostics === void 0 ? {} : {
|
|
6540
|
+
candidateDiagnostics: {
|
|
6541
|
+
entries: retainedDiagnostics.slice(
|
|
6542
|
+
0,
|
|
6543
|
+
options.candidateDiagnostics.limit
|
|
6544
|
+
),
|
|
6545
|
+
total: candidateDiagnosticsTotal + definitionalExclusions.length,
|
|
6546
|
+
limit: options.candidateDiagnostics.limit,
|
|
6547
|
+
truncated: candidateDiagnosticsTotal + definitionalExclusions.length > options.candidateDiagnostics.limit
|
|
6548
|
+
}
|
|
6549
|
+
},
|
|
4916
6550
|
identityAssertions: identityRemap.assertions,
|
|
4917
6551
|
identityRetractions: survivingRetractions
|
|
4918
6552
|
};
|
|
@@ -4963,13 +6597,13 @@ function plannedNodeWrites(plan) {
|
|
|
4963
6597
|
continue;
|
|
4964
6598
|
}
|
|
4965
6599
|
written.add(identity);
|
|
4966
|
-
const
|
|
6600
|
+
const validToChange = plan.nodeValidityEnds.get(identity);
|
|
4967
6601
|
writes.push({
|
|
4968
6602
|
identity,
|
|
4969
6603
|
kind: modification.node.kind,
|
|
4970
6604
|
id: modification.node.id,
|
|
4971
6605
|
modification: modification.node,
|
|
4972
|
-
...
|
|
6606
|
+
...validToChange?.kind === "set" ? { validTo: validToChange.validTo } : validToChange?.kind === "clear" ? { clearValidTo: true } : {}
|
|
4973
6607
|
});
|
|
4974
6608
|
}
|
|
4975
6609
|
for (const entity of plan.canonicalEntities) {
|
|
@@ -4980,7 +6614,7 @@ function plannedNodeWrites(plan) {
|
|
|
4980
6614
|
const kind = plan.retypeMap.get(sourceIdentity) ?? entity.kind;
|
|
4981
6615
|
const modification = modificationsByIdentity.get(sourceIdentity);
|
|
4982
6616
|
written.add(sourceIdentity);
|
|
4983
|
-
const
|
|
6617
|
+
const validToChange = plan.nodeValidityEnds.get(sourceIdentity);
|
|
4984
6618
|
writes.push({
|
|
4985
6619
|
identity: mergeKey(kind, entity.canonicalId),
|
|
4986
6620
|
kind,
|
|
@@ -4988,7 +6622,7 @@ function plannedNodeWrites(plan) {
|
|
|
4988
6622
|
entity,
|
|
4989
6623
|
...modification === void 0 ? {} : { modification },
|
|
4990
6624
|
...entity.validFrom === void 0 ? {} : { validFrom: entity.validFrom },
|
|
4991
|
-
...validTo ===
|
|
6625
|
+
...typeof entity.validTo === "string" ? { validTo: entity.validTo } : validToChange?.kind === "set" ? { validTo: validToChange.validTo } : validToChange?.kind === "clear" ? { clearValidTo: true } : {}
|
|
4992
6626
|
});
|
|
4993
6627
|
}
|
|
4994
6628
|
for (const identity of [...plan.nodeValidityEnds.keys()].sort(
|
|
@@ -4998,11 +6632,12 @@ function plannedNodeWrites(plan) {
|
|
|
4998
6632
|
continue;
|
|
4999
6633
|
}
|
|
5000
6634
|
const kind = plan.retypeMap.get(identity) ?? kindOf(identity);
|
|
6635
|
+
const validToChange = requireDefined(plan.nodeValidityEnds.get(identity));
|
|
5001
6636
|
writes.push({
|
|
5002
6637
|
identity: mergeKey(kind, idOf(identity)),
|
|
5003
6638
|
kind,
|
|
5004
6639
|
id: idOf(identity),
|
|
5005
|
-
validTo:
|
|
6640
|
+
...validToChange.kind === "set" ? { validTo: validToChange.validTo } : { clearValidTo: true }
|
|
5006
6641
|
});
|
|
5007
6642
|
}
|
|
5008
6643
|
return writes;
|
|
@@ -5013,67 +6648,128 @@ function nodeWriteProps(write, modificationProps) {
|
|
|
5013
6648
|
...write.modification === void 0 ? void 0 : modificationProps(write.modification)
|
|
5014
6649
|
};
|
|
5015
6650
|
}
|
|
5016
|
-
|
|
5017
|
-
const
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
await collection.delete(idOf(identity));
|
|
6651
|
+
function nodeWriteWindowOptions(write) {
|
|
6652
|
+
const validFrom = write.validFrom === void 0 ? {} : { validFrom: write.validFrom };
|
|
6653
|
+
if (write.clearValidTo === true) {
|
|
6654
|
+
return { ...validFrom, clearValidTo: true };
|
|
5021
6655
|
}
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
6656
|
+
return {
|
|
6657
|
+
...validFrom,
|
|
6658
|
+
...write.validTo === void 0 ? {} : { validTo: write.validTo }
|
|
6659
|
+
};
|
|
6660
|
+
}
|
|
6661
|
+
async function applyNodeRows(nodesApi, deletions, upserts) {
|
|
6662
|
+
for (const deletion of deletions) {
|
|
6663
|
+
await nodeCollection(nodesApi, deletion.kind).delete(deletion.id);
|
|
6664
|
+
}
|
|
6665
|
+
const committed = /* @__PURE__ */ new Set();
|
|
6666
|
+
for (const upsert of upserts) {
|
|
6667
|
+
await nodeCollection(nodesApi, upsert.kind).upsertByIdFromRecord(
|
|
6668
|
+
upsert.id,
|
|
6669
|
+
upsert.props,
|
|
6670
|
+
nodeWriteWindowOptions(upsert)
|
|
5034
6671
|
);
|
|
5035
|
-
|
|
6672
|
+
committed.add(mergeKey(upsert.kind, upsert.id));
|
|
5036
6673
|
}
|
|
5037
|
-
|
|
5038
|
-
|
|
6674
|
+
return committed.size;
|
|
6675
|
+
}
|
|
6676
|
+
async function applyEdgeRows(edgesApi, deletions, upserts) {
|
|
6677
|
+
for (const deletion of deletions) {
|
|
6678
|
+
await edgeCollection(edgesApi, deletion.kind).delete(deletion.id);
|
|
5039
6679
|
}
|
|
5040
|
-
const
|
|
5041
|
-
for (const
|
|
5042
|
-
const
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
id: edge.id,
|
|
5046
|
-
from: finalEdgeEndpoint(plan, edge.fromKind, edge.fromId),
|
|
5047
|
-
to: finalEdgeEndpoint(plan, edge.toKind, edge.toId),
|
|
5048
|
-
props,
|
|
5049
|
-
...edge.validFrom === void 0 ? {} : { validFrom: edge.validFrom },
|
|
5050
|
-
...edge.validTo === void 0 ? {} : { validTo: edge.validTo }
|
|
5051
|
-
};
|
|
5052
|
-
const existing = edgesByKind.get(edge.kind);
|
|
5053
|
-
if (existing === void 0) {
|
|
5054
|
-
edgesByKind.set(edge.kind, [item]);
|
|
5055
|
-
} else {
|
|
5056
|
-
existing.push(item);
|
|
5057
|
-
}
|
|
6680
|
+
const byKind = /* @__PURE__ */ new Map();
|
|
6681
|
+
for (const upsert of upserts) {
|
|
6682
|
+
const items = byKind.get(upsert.kind);
|
|
6683
|
+
if (items === void 0) byKind.set(upsert.kind, [upsert.item]);
|
|
6684
|
+
else items.push(upsert.item);
|
|
5058
6685
|
}
|
|
5059
|
-
let
|
|
5060
|
-
for (const [kind, items] of
|
|
6686
|
+
let committed = 0;
|
|
6687
|
+
for (const [kind, items] of byKind) {
|
|
5061
6688
|
await edgeCollection(edgesApi, kind).bulkUpsertById(items);
|
|
5062
|
-
|
|
6689
|
+
committed += items.length;
|
|
5063
6690
|
}
|
|
5064
|
-
|
|
6691
|
+
return committed;
|
|
6692
|
+
}
|
|
6693
|
+
async function applyIdentityRows(target, txBackend, assertions, retractions, assertConsistent) {
|
|
5065
6694
|
try {
|
|
5066
|
-
|
|
6695
|
+
const applied = await storeRuntime(target).applyIdentityMergeAtTarget(
|
|
5067
6696
|
txBackend,
|
|
5068
|
-
|
|
5069
|
-
|
|
6697
|
+
retractions,
|
|
6698
|
+
assertions
|
|
5070
6699
|
);
|
|
5071
|
-
|
|
6700
|
+
if (assertConsistent !== void 0) await assertConsistent();
|
|
6701
|
+
return { asserted: applied.created, retracted: applied.retracted };
|
|
5072
6702
|
} catch (error) {
|
|
5073
6703
|
throw translateIdentityCommitError(error);
|
|
5074
6704
|
}
|
|
6705
|
+
}
|
|
6706
|
+
async function applyInternalMergePlan(plan, nodesApi, edgesApi, target, txBackend) {
|
|
6707
|
+
const nodeDeletions = [...plan.nodeDeletions].map(([identity, kind]) => ({
|
|
6708
|
+
kind,
|
|
6709
|
+
id: idOf(identity)
|
|
6710
|
+
}));
|
|
6711
|
+
const nodeUpserts = plannedNodeWrites(plan).map((write) => ({
|
|
6712
|
+
kind: write.kind,
|
|
6713
|
+
id: write.id,
|
|
6714
|
+
props: nodeWriteProps(
|
|
6715
|
+
write,
|
|
6716
|
+
(modification) => commitModificationProps(modification.baseProps, modification.forkProps)
|
|
6717
|
+
),
|
|
6718
|
+
...write.validFrom === void 0 ? {} : { validFrom: write.validFrom },
|
|
6719
|
+
...write.clearValidTo === true ? { clearValidTo: true } : write.validTo === void 0 ? {} : { validTo: write.validTo }
|
|
6720
|
+
}));
|
|
6721
|
+
const validityEndedNodes = new Set(
|
|
6722
|
+
nodeUpserts.filter((write) => "validTo" in write).map((write) => mergeKey(write.kind, write.id))
|
|
6723
|
+
);
|
|
6724
|
+
const earlyIdentityRetractions = plan.identityRetractions.filter(
|
|
6725
|
+
(retraction) => validityEndedNodes.has(mergeKeyOf(retraction.a)) || validityEndedNodes.has(mergeKeyOf(retraction.b))
|
|
6726
|
+
);
|
|
6727
|
+
const earlyIdentity = await applyIdentityRows(
|
|
6728
|
+
target,
|
|
6729
|
+
txBackend,
|
|
6730
|
+
[],
|
|
6731
|
+
earlyIdentityRetractions
|
|
6732
|
+
);
|
|
6733
|
+
let committedNodes;
|
|
6734
|
+
try {
|
|
6735
|
+
committedNodes = await applyNodeRows(nodesApi, nodeDeletions, nodeUpserts);
|
|
6736
|
+
} catch (error) {
|
|
6737
|
+
throw error instanceof IdentityEndpointValidityError ? translateIdentityCommitError(error) : error;
|
|
6738
|
+
}
|
|
6739
|
+
const edgeDeletions = [...plan.edgeDeletions].map(([identity, kind]) => ({
|
|
6740
|
+
kind,
|
|
6741
|
+
id: idOf(identity)
|
|
6742
|
+
}));
|
|
6743
|
+
const edgeUpserts = [];
|
|
6744
|
+
for (const edge of plan.mergedEdges) {
|
|
6745
|
+
const edgeBaseProps = plan.inheritedEdgeBaseProps.get(edge.id);
|
|
6746
|
+
const props = edgeBaseProps === void 0 ? edge.props : commitModificationProps(edgeBaseProps, edge.props);
|
|
6747
|
+
edgeUpserts.push({
|
|
6748
|
+
kind: edge.kind,
|
|
6749
|
+
item: {
|
|
6750
|
+
id: edge.id,
|
|
6751
|
+
from: finalEdgeEndpoint(plan, edge.fromKind, edge.fromId),
|
|
6752
|
+
to: finalEdgeEndpoint(plan, edge.toKind, edge.toId),
|
|
6753
|
+
props,
|
|
6754
|
+
...edge.validFrom === void 0 ? {} : { validFrom: edge.validFrom },
|
|
6755
|
+
...edge.clearValidTo === true ? { clearValidTo: true } : edge.validTo === void 0 ? {} : { validTo: edge.validTo }
|
|
6756
|
+
}
|
|
6757
|
+
});
|
|
6758
|
+
}
|
|
6759
|
+
const committedEdges = await applyEdgeRows(
|
|
6760
|
+
edgesApi,
|
|
6761
|
+
edgeDeletions,
|
|
6762
|
+
edgeUpserts
|
|
6763
|
+
);
|
|
6764
|
+
const appliedIdentity = await applyIdentityRows(
|
|
6765
|
+
target,
|
|
6766
|
+
txBackend,
|
|
6767
|
+
plan.identityAssertions,
|
|
6768
|
+
plan.identityRetractions,
|
|
6769
|
+
() => assertMergedIdentityClassesConsistent(target, txBackend, plan)
|
|
6770
|
+
);
|
|
5075
6771
|
return {
|
|
5076
|
-
nodes:
|
|
6772
|
+
nodes: committedNodes,
|
|
5077
6773
|
edges: committedEdges,
|
|
5078
6774
|
// ACTUAL ledger effects from the applier: rows created (idempotent
|
|
5079
6775
|
// exact/pair matches the target already held are excluded — the normal
|
|
@@ -5082,8 +6778,8 @@ async function applyMergePlan(plan, nodesApi, edgesApi, target, txBackend) {
|
|
|
5082
6778
|
// failure aborts the whole merge, so the counts never describe a
|
|
5083
6779
|
// partial commit.
|
|
5084
6780
|
identity: {
|
|
5085
|
-
asserted: appliedIdentity.
|
|
5086
|
-
retracted: appliedIdentity.retracted
|
|
6781
|
+
asserted: appliedIdentity.asserted,
|
|
6782
|
+
retracted: earlyIdentity.retracted + appliedIdentity.retracted
|
|
5087
6783
|
}
|
|
5088
6784
|
};
|
|
5089
6785
|
}
|
|
@@ -5094,26 +6790,39 @@ async function commitPlan(target, plan, expectedBaseVersion) {
|
|
|
5094
6790
|
{ details: { capability: "transactions" } }
|
|
5095
6791
|
);
|
|
5096
6792
|
}
|
|
5097
|
-
return
|
|
5098
|
-
() =>
|
|
5099
|
-
|
|
5100
|
-
|
|
6793
|
+
return runMergeCommit(
|
|
6794
|
+
() => withTxConflictRetry(
|
|
6795
|
+
() => target.transaction(async (tx) => {
|
|
6796
|
+
if (expectedBaseVersion !== void 0) {
|
|
6797
|
+
await assertTargetUnchanged(
|
|
6798
|
+
transactionBackend(tx),
|
|
6799
|
+
target,
|
|
6800
|
+
expectedBaseVersion
|
|
6801
|
+
);
|
|
6802
|
+
}
|
|
6803
|
+
await assertPlannedIdentityIdsFresh(
|
|
6804
|
+
target,
|
|
5101
6805
|
transactionBackend(tx),
|
|
6806
|
+
plan
|
|
6807
|
+
);
|
|
6808
|
+
return applyInternalMergePlan(
|
|
6809
|
+
plan,
|
|
6810
|
+
tx.nodes,
|
|
6811
|
+
tx.edges,
|
|
5102
6812
|
target,
|
|
5103
|
-
|
|
6813
|
+
transactionBackend(tx)
|
|
5104
6814
|
);
|
|
5105
|
-
}
|
|
5106
|
-
|
|
5107
|
-
return applyMergePlan(
|
|
5108
|
-
plan,
|
|
5109
|
-
tx.nodes,
|
|
5110
|
-
tx.edges,
|
|
5111
|
-
target,
|
|
5112
|
-
transactionBackend(tx)
|
|
5113
|
-
);
|
|
5114
|
-
}, mergeCommitTransactionOptions(target))
|
|
6815
|
+
}, mergeCommitTransactionOptions(target))
|
|
6816
|
+
)
|
|
5115
6817
|
);
|
|
5116
6818
|
}
|
|
6819
|
+
async function runMergeCommit(commit) {
|
|
6820
|
+
try {
|
|
6821
|
+
return await commit();
|
|
6822
|
+
} catch (error) {
|
|
6823
|
+
throw translateMergeCommitError(error);
|
|
6824
|
+
}
|
|
6825
|
+
}
|
|
5117
6826
|
var MERGE_COMMIT_TX_OPTIONS = {
|
|
5118
6827
|
isolationLevel: "serializable"
|
|
5119
6828
|
};
|
|
@@ -5250,7 +6959,169 @@ async function tryOpenProvenanceStore(target) {
|
|
|
5250
6959
|
);
|
|
5251
6960
|
}
|
|
5252
6961
|
}
|
|
5253
|
-
|
|
6962
|
+
function assertPublicPlanCapability(target) {
|
|
6963
|
+
if (!storeBackend(target).capabilities.transactions) {
|
|
6964
|
+
throw new MergePlanCapabilityError(
|
|
6965
|
+
"Public merge plans require a transaction-capable target backend.",
|
|
6966
|
+
{ details: { capability: "transactions" } }
|
|
6967
|
+
);
|
|
6968
|
+
}
|
|
6969
|
+
if (!target.revisionTrackingEnabled) {
|
|
6970
|
+
throw new MergePlanCapabilityError(
|
|
6971
|
+
"Public merge plans require durable target revision tracking.",
|
|
6972
|
+
{
|
|
6973
|
+
details: { capability: "revisionTracking" },
|
|
6974
|
+
suggestion: "Create the target Store with { revisionTracking: true } (or history enabled), then create a new plan."
|
|
6975
|
+
}
|
|
6976
|
+
);
|
|
6977
|
+
}
|
|
6978
|
+
}
|
|
6979
|
+
async function captureMergePlanTargetFence(target) {
|
|
6980
|
+
assertPublicPlanCapability(target);
|
|
6981
|
+
const [activeSchema, schemaHash, origin, revision] = await Promise.all([
|
|
6982
|
+
storeBackend(target).getActiveSchema(target.graphId),
|
|
6983
|
+
computeSchemaComponent(target),
|
|
6984
|
+
target.revisionOriginNow(),
|
|
6985
|
+
target.revisionNow()
|
|
6986
|
+
]);
|
|
6987
|
+
return {
|
|
6988
|
+
graphId: target.graphId,
|
|
6989
|
+
schema: {
|
|
6990
|
+
managed: activeSchema !== void 0,
|
|
6991
|
+
version: activeSchema?.version ?? 1,
|
|
6992
|
+
hash: activeSchema?.schema_hash ?? schemaHash
|
|
6993
|
+
},
|
|
6994
|
+
revision: { origin, revision: revision ?? null }
|
|
6995
|
+
};
|
|
6996
|
+
}
|
|
6997
|
+
function sameMergePlanTargetFence(left, right) {
|
|
6998
|
+
return left.graphId === right.graphId && left.schema.managed === right.schema.managed && left.schema.version === right.schema.version && left.schema.hash === right.schema.hash && left.revision.origin === right.revision.origin && left.revision.revision === right.revision.revision;
|
|
6999
|
+
}
|
|
7000
|
+
async function assertPlanningFenceUnchanged(target, startingFence) {
|
|
7001
|
+
const endingFence = await captureMergePlanTargetFence(target);
|
|
7002
|
+
if (sameMergePlanTargetFence(startingFence, endingFence)) return;
|
|
7003
|
+
throw new MergePlanningStaleError(
|
|
7004
|
+
"The merge target changed while the plan was being computed; no reviewable plan was produced.",
|
|
7005
|
+
{ details: { startingFence, endingFence } }
|
|
7006
|
+
);
|
|
7007
|
+
}
|
|
7008
|
+
function splitWireProps(props) {
|
|
7009
|
+
const setProps = createDataKeyedBag();
|
|
7010
|
+
const unsetProps = [];
|
|
7011
|
+
for (const key of Object.keys(props).sort(compareStrings)) {
|
|
7012
|
+
const value = props[key];
|
|
7013
|
+
if (value === void 0) {
|
|
7014
|
+
unsetProps.push(key);
|
|
7015
|
+
} else {
|
|
7016
|
+
setProps[key] = value;
|
|
7017
|
+
}
|
|
7018
|
+
}
|
|
7019
|
+
return { setProps, unsetProps };
|
|
7020
|
+
}
|
|
7021
|
+
function wireEntityRef(kind, id) {
|
|
7022
|
+
return { kind, id };
|
|
7023
|
+
}
|
|
7024
|
+
function resolvedNodeUpserts(plan) {
|
|
7025
|
+
return plannedNodeWrites(plan).map((write) => ({
|
|
7026
|
+
kind: write.kind,
|
|
7027
|
+
id: write.id,
|
|
7028
|
+
...splitWireProps(
|
|
7029
|
+
nodeWriteProps(
|
|
7030
|
+
write,
|
|
7031
|
+
(modification) => commitModificationProps(modification.baseProps, modification.forkProps)
|
|
7032
|
+
)
|
|
7033
|
+
),
|
|
7034
|
+
...write.validFrom === void 0 ? {} : { validFrom: write.validFrom },
|
|
7035
|
+
...write.validTo === void 0 ? {} : { validTo: write.validTo }
|
|
7036
|
+
}));
|
|
7037
|
+
}
|
|
7038
|
+
async function resolvedMergeArtifact(resolved, mode, targetFence, anchors) {
|
|
7039
|
+
const { plan, options } = resolved;
|
|
7040
|
+
const nodeUpserts = resolvedNodeUpserts(plan);
|
|
7041
|
+
const edgeUpserts = plan.mergedEdges.map((edge) => {
|
|
7042
|
+
const baseProps = plan.inheritedEdgeBaseProps.get(edge.id);
|
|
7043
|
+
const props = baseProps === void 0 ? edge.props : commitModificationProps(baseProps, edge.props);
|
|
7044
|
+
return {
|
|
7045
|
+
kind: edge.kind,
|
|
7046
|
+
id: edge.id,
|
|
7047
|
+
from: finalEdgeEndpoint(plan, edge.fromKind, edge.fromId),
|
|
7048
|
+
to: finalEdgeEndpoint(plan, edge.toKind, edge.toId),
|
|
7049
|
+
...splitWireProps(props),
|
|
7050
|
+
...edge.validFrom === void 0 ? {} : { validFrom: edge.validFrom },
|
|
7051
|
+
...edge.validTo === void 0 ? {} : { validTo: edge.validTo }
|
|
7052
|
+
};
|
|
7053
|
+
});
|
|
7054
|
+
const input = {
|
|
7055
|
+
formatVersion: 1,
|
|
7056
|
+
mode,
|
|
7057
|
+
target: targetFence,
|
|
7058
|
+
anchors,
|
|
7059
|
+
proposed: {
|
|
7060
|
+
nodes: {
|
|
7061
|
+
upserts: nodeUpserts.length,
|
|
7062
|
+
deletions: plan.nodeDeletions.size
|
|
7063
|
+
},
|
|
7064
|
+
edges: {
|
|
7065
|
+
upserts: edgeUpserts.length,
|
|
7066
|
+
deletions: plan.edgeDeletions.size
|
|
7067
|
+
},
|
|
7068
|
+
identity: {
|
|
7069
|
+
assertions: plan.identityAssertions.length,
|
|
7070
|
+
retractions: plan.identityRetractions.length
|
|
7071
|
+
}
|
|
7072
|
+
},
|
|
7073
|
+
writes: {
|
|
7074
|
+
nodeDeletes: [...plan.nodeDeletions].map(
|
|
7075
|
+
([identity, kind]) => wireEntityRef(kind, idOf(identity))
|
|
7076
|
+
),
|
|
7077
|
+
nodeUpserts,
|
|
7078
|
+
edgeDeletes: [...plan.edgeDeletions].map(
|
|
7079
|
+
([identity, kind]) => wireEntityRef(kind, idOf(identity))
|
|
7080
|
+
),
|
|
7081
|
+
edgeUpserts,
|
|
7082
|
+
identityAssertions: plan.identityAssertions,
|
|
7083
|
+
identityRetractions: plan.identityRetractions
|
|
7084
|
+
},
|
|
7085
|
+
guards: {
|
|
7086
|
+
canonicalMappings: [...plan.canonicalOf].sort(([left], [right]) => compareMergeKeys(left, right)).map(([member, canonical]) => ({
|
|
7087
|
+
member: wireEntityRef(kindOf(member), idOf(member)),
|
|
7088
|
+
canonical: wireEntityRef(kindOf(canonical), idOf(canonical))
|
|
7089
|
+
})),
|
|
7090
|
+
retypes: [...plan.retypeMap].sort(([left], [right]) => compareMergeKeys(left, right)).map(([entity, toKind]) => ({
|
|
7091
|
+
entity: wireEntityRef(kindOf(entity), idOf(entity)),
|
|
7092
|
+
toKind
|
|
7093
|
+
})),
|
|
7094
|
+
deletedNodes: [...plan.nodeDeletions].map(
|
|
7095
|
+
([identity, kind]) => wireEntityRef(kind, idOf(identity))
|
|
7096
|
+
),
|
|
7097
|
+
...resolved.incrementalGuard === void 0 ? {} : {
|
|
7098
|
+
incremental: {
|
|
7099
|
+
tombstoneResurrection: "refuse",
|
|
7100
|
+
lossyUpdates: "refuse",
|
|
7101
|
+
edgeIdentity: "preserve"
|
|
7102
|
+
}
|
|
7103
|
+
}
|
|
7104
|
+
},
|
|
7105
|
+
review: {
|
|
7106
|
+
resolutions: plan.resolutions,
|
|
7107
|
+
conflicts: plan.propertyConflicts,
|
|
7108
|
+
deleteModifyConflicts: plan.deleteModifyConflicts,
|
|
7109
|
+
typeReconciliations: plan.typeReconciliations,
|
|
7110
|
+
dropped: plan.dropped,
|
|
7111
|
+
validityEnds: plan.validityEnds,
|
|
7112
|
+
baseAmbiguities: plan.baseAmbiguities,
|
|
7113
|
+
provenanceRecords: plan.provenanceRecords,
|
|
7114
|
+
warnings: plan.warnings,
|
|
7115
|
+
...plan.candidateDiagnostics === void 0 ? {} : { diagnostics: plan.candidateDiagnostics }
|
|
7116
|
+
},
|
|
7117
|
+
provenance: {
|
|
7118
|
+
includeInReport: options.provenance,
|
|
7119
|
+
persist: options.persistProvenance
|
|
7120
|
+
}
|
|
7121
|
+
};
|
|
7122
|
+
return constructMergePlanArtifact(input);
|
|
7123
|
+
}
|
|
7124
|
+
async function resolveMerge(store, target, branches, options, useBaseSources, incremental, expectedBaseVersion, complete) {
|
|
5254
7125
|
let targetBranchSeen = false;
|
|
5255
7126
|
for (const branch2 of branches) {
|
|
5256
7127
|
if (branch2.id === BASE_PROVENANCE_BRANCH) {
|
|
@@ -5303,10 +7174,6 @@ async function resolveMerge(store, target, branches, options, useBaseSources, in
|
|
|
5303
7174
|
);
|
|
5304
7175
|
}
|
|
5305
7176
|
}
|
|
5306
|
-
const provenanceStore = options.persistProvenance ? await tryOpenProvenanceStore(target) : void 0;
|
|
5307
|
-
if (provenanceStore !== void 0 && isErr(provenanceStore)) {
|
|
5308
|
-
return provenanceStore;
|
|
5309
|
-
}
|
|
5310
7177
|
try {
|
|
5311
7178
|
const preferredBranchId = incremental?.targetBranchId;
|
|
5312
7179
|
const staging = await stageBranches(store, branches, preferredBranchId);
|
|
@@ -5375,10 +7242,12 @@ async function resolveMerge(store, target, branches, options, useBaseSources, in
|
|
|
5375
7242
|
sameIdAcrossKinds: target.graph.identity?.sameIdAcrossKinds,
|
|
5376
7243
|
areDisjoint: (left, right) => target.registry.areDisjoint(left, right)
|
|
5377
7244
|
};
|
|
5378
|
-
const plan =
|
|
7245
|
+
const plan = buildInternalMergePlan(
|
|
5379
7246
|
staging,
|
|
5380
7247
|
candidates.data.edges,
|
|
5381
7248
|
candidates.data.warnings,
|
|
7249
|
+
candidates.data.diagnostics,
|
|
7250
|
+
candidates.data.diagnosticsTotal,
|
|
5382
7251
|
candidates.data.baseMembers,
|
|
5383
7252
|
options,
|
|
5384
7253
|
branchRank,
|
|
@@ -5434,29 +7303,727 @@ async function resolveMerge(store, target, branches, options, useBaseSources, in
|
|
|
5434
7303
|
);
|
|
5435
7304
|
identityGuard = built.probe;
|
|
5436
7305
|
}
|
|
5437
|
-
const
|
|
7306
|
+
const incrementalGuard = incremental === void 0 ? void 0 : {
|
|
5438
7307
|
stagedNewByKind,
|
|
5439
7308
|
options,
|
|
5440
7309
|
introspectionKinds,
|
|
5441
7310
|
forkPoint: incremental.forkPoint,
|
|
5442
7311
|
...identityGuard === void 0 ? {} : { identityPeerProbe: identityGuard },
|
|
5443
|
-
// The committed (kind, id) keys the base sources matched at PLAN time;
|
|
5444
|
-
// anything NEW the in-tx re-probe surfaces is a window write.
|
|
5445
7312
|
plannedBaseMatchKeys: new Set(
|
|
5446
7313
|
candidates.data.baseMembers.map((member) => mergeKeyOf(member))
|
|
5447
7314
|
),
|
|
5448
7315
|
targetNodeVersions: staging.targetNodeVersions,
|
|
5449
7316
|
targetEdgeSignatures: staging.targetEdgeSignatures
|
|
5450
|
-
}
|
|
5451
|
-
|
|
5452
|
-
|
|
7317
|
+
};
|
|
7318
|
+
return ok(
|
|
7319
|
+
await complete({
|
|
7320
|
+
target,
|
|
7321
|
+
plan,
|
|
7322
|
+
options,
|
|
7323
|
+
...expectedBaseVersion === void 0 ? {} : { expectedBaseVersion },
|
|
7324
|
+
...incrementalGuard === void 0 ? {} : { incrementalGuard }
|
|
7325
|
+
})
|
|
7326
|
+
);
|
|
7327
|
+
} catch (error) {
|
|
7328
|
+
if (error instanceof MergeError) {
|
|
7329
|
+
return err(error);
|
|
7330
|
+
}
|
|
7331
|
+
return err(
|
|
7332
|
+
new MergeError(
|
|
7333
|
+
`Merge failed while staging or committing: ${describeCause(error)}`,
|
|
7334
|
+
{ cause: error }
|
|
7335
|
+
)
|
|
7336
|
+
);
|
|
7337
|
+
}
|
|
7338
|
+
}
|
|
7339
|
+
async function commitResolvedMerge(resolved) {
|
|
7340
|
+
const { target, plan, options } = resolved;
|
|
7341
|
+
const provenanceStore = options.persistProvenance ? await tryOpenProvenanceStore(target) : void 0;
|
|
7342
|
+
if (provenanceStore !== void 0 && isErr(provenanceStore)) {
|
|
7343
|
+
throw provenanceStore.error;
|
|
7344
|
+
}
|
|
7345
|
+
const merged = resolved.incrementalGuard === void 0 ? await commitPlan(target, plan, resolved.expectedBaseVersion) : await commitIncrementalPlan(target, plan, resolved.incrementalGuard);
|
|
7346
|
+
const provenance = options.provenance ? buildProvenanceIndex(plan.provenanceRecords) : { byBranch: () => ({ nodeIds: [], edgeIds: [] }) };
|
|
7347
|
+
const warnings = [...plan.warnings];
|
|
7348
|
+
let provenancePersisted;
|
|
7349
|
+
if (provenanceStore !== void 0 && !isErr(provenanceStore)) {
|
|
7350
|
+
try {
|
|
7351
|
+
const count = await persistProvenanceRecords(
|
|
7352
|
+
provenanceStore.data,
|
|
7353
|
+
target.graphId,
|
|
7354
|
+
plan.provenanceRecords
|
|
7355
|
+
);
|
|
7356
|
+
provenancePersisted = {
|
|
7357
|
+
graphId: provenanceGraphId(target.graphId),
|
|
7358
|
+
count
|
|
7359
|
+
};
|
|
7360
|
+
} catch (error) {
|
|
7361
|
+
warnings.push(
|
|
7362
|
+
"provenance persistence failed (graph committed; provenance not persisted): " + (error instanceof Error ? error.message : String(error))
|
|
7363
|
+
);
|
|
7364
|
+
}
|
|
7365
|
+
}
|
|
7366
|
+
return {
|
|
7367
|
+
merged,
|
|
7368
|
+
resolutions: plan.resolutions,
|
|
7369
|
+
conflicts: plan.propertyConflicts,
|
|
7370
|
+
deleteModifyConflicts: plan.deleteModifyConflicts,
|
|
7371
|
+
typeReconciliations: plan.typeReconciliations,
|
|
7372
|
+
dropped: plan.dropped,
|
|
7373
|
+
validityEnds: plan.validityEnds,
|
|
7374
|
+
baseAmbiguities: plan.baseAmbiguities,
|
|
7375
|
+
provenance,
|
|
7376
|
+
warnings,
|
|
7377
|
+
...plan.candidateDiagnostics === void 0 ? {} : { candidateDiagnostics: plan.candidateDiagnostics },
|
|
7378
|
+
...provenancePersisted === void 0 ? {} : { provenancePersisted }
|
|
7379
|
+
};
|
|
7380
|
+
}
|
|
7381
|
+
function incrementalBaseConflictPolicyError(options) {
|
|
7382
|
+
return new InvalidMergeOptionsError(
|
|
7383
|
+
'mergeIncremental() requires onBasePropertyConflict: "flag" (keep-base); a non-keep-base policy could overwrite a newer committed base value with a stale branch value.',
|
|
7384
|
+
{
|
|
7385
|
+
details: {
|
|
7386
|
+
option: "onBasePropertyConflict",
|
|
7387
|
+
accepted: "flag",
|
|
7388
|
+
received: options.onBasePropertyConflict
|
|
7389
|
+
}
|
|
7390
|
+
}
|
|
7391
|
+
);
|
|
7392
|
+
}
|
|
7393
|
+
function incrementalSchemaError() {
|
|
7394
|
+
return new MergeError(
|
|
7395
|
+
"mergeIncremental() requires target and forkPoint to share a schema; schema drift is not supported (the target content may differ \u2014 only the schema must match).",
|
|
7396
|
+
{ details: {} }
|
|
7397
|
+
);
|
|
7398
|
+
}
|
|
7399
|
+
async function planMerge(store, branches, optionsInput = {}) {
|
|
7400
|
+
const normalized = tryNormalize(optionsInput);
|
|
7401
|
+
if (isErr(normalized)) return err(normalized.error);
|
|
7402
|
+
const options = normalized.data;
|
|
7403
|
+
const target = options.target ?? store;
|
|
7404
|
+
let targetFence;
|
|
7405
|
+
try {
|
|
7406
|
+
targetFence = await captureMergePlanTargetFence(target);
|
|
7407
|
+
} catch (error) {
|
|
7408
|
+
return err(
|
|
7409
|
+
error instanceof MergeError ? error : new MergePlanCapabilityError(
|
|
7410
|
+
`Unable to capture the target's durable plan fence: ${describeCause(error)}`,
|
|
7411
|
+
{ cause: error }
|
|
7412
|
+
)
|
|
7413
|
+
);
|
|
7414
|
+
}
|
|
7415
|
+
const precondition = await validateBaseVersions(target, branches);
|
|
7416
|
+
if (isErr(precondition)) return err(precondition.error);
|
|
7417
|
+
const anchors = {
|
|
7418
|
+
kind: "snapshot",
|
|
7419
|
+
base: { graphId: store.graphId, baseVersion: precondition.data },
|
|
7420
|
+
branches: [...branches].sort((left, right) => compareStrings(left.id, right.id)).map((branch2) => ({
|
|
7421
|
+
branchId: branch2.id,
|
|
7422
|
+
baseVersion: branch2.base
|
|
7423
|
+
}))
|
|
7424
|
+
};
|
|
7425
|
+
return resolveMerge(
|
|
7426
|
+
store,
|
|
7427
|
+
target,
|
|
7428
|
+
branches,
|
|
7429
|
+
options,
|
|
7430
|
+
false,
|
|
7431
|
+
void 0,
|
|
7432
|
+
precondition.data,
|
|
7433
|
+
async (resolved) => {
|
|
7434
|
+
await assertPlanningFenceUnchanged(target, targetFence);
|
|
7435
|
+
return resolvedMergeArtifact(resolved, "snapshot", targetFence, anchors);
|
|
7436
|
+
}
|
|
7437
|
+
);
|
|
7438
|
+
}
|
|
7439
|
+
async function planMergeIncremental(args) {
|
|
7440
|
+
const { forkPoint, target, branches } = args;
|
|
7441
|
+
const normalized = tryNormalize(args.options ?? {}, ["target"]);
|
|
7442
|
+
if (isErr(normalized)) return err(normalized.error);
|
|
7443
|
+
const options = normalized.data;
|
|
7444
|
+
if (options.onBasePropertyConflict !== "flag") {
|
|
7445
|
+
return err(incrementalBaseConflictPolicyError(options));
|
|
7446
|
+
}
|
|
7447
|
+
let targetFence;
|
|
7448
|
+
try {
|
|
7449
|
+
targetFence = await captureMergePlanTargetFence(target);
|
|
7450
|
+
} catch (error) {
|
|
7451
|
+
return err(
|
|
7452
|
+
error instanceof MergeError ? error : new MergePlanCapabilityError(
|
|
7453
|
+
`Unable to capture the target's durable plan fence: ${describeCause(error)}`,
|
|
7454
|
+
{ cause: error }
|
|
7455
|
+
)
|
|
7456
|
+
);
|
|
7457
|
+
}
|
|
7458
|
+
const forkPrecondition = await validateForkPointVersions(forkPoint, branches);
|
|
7459
|
+
if (isErr(forkPrecondition)) return err(forkPrecondition.error);
|
|
7460
|
+
const forkVersion = forkPrecondition.data;
|
|
7461
|
+
const [forkSchema, targetSchema] = await Promise.all([
|
|
7462
|
+
computeSchemaComponent(forkPoint),
|
|
7463
|
+
computeSchemaComponent(target)
|
|
7464
|
+
]);
|
|
7465
|
+
if (forkSchema !== targetSchema) return err(incrementalSchemaError());
|
|
7466
|
+
const forkActiveSchema = await storeBackend(forkPoint).getActiveSchema(
|
|
7467
|
+
forkPoint.graphId
|
|
7468
|
+
);
|
|
7469
|
+
const targetBranch = {
|
|
7470
|
+
id: COMMITTED_TARGET_BRANCH,
|
|
7471
|
+
base: forkVersion,
|
|
7472
|
+
store: target
|
|
7473
|
+
};
|
|
7474
|
+
const anchors = {
|
|
7475
|
+
kind: "incremental",
|
|
7476
|
+
forkPoint: {
|
|
7477
|
+
graphId: forkPoint.graphId,
|
|
7478
|
+
baseVersion: forkVersion,
|
|
7479
|
+
schema: {
|
|
7480
|
+
managed: forkActiveSchema !== void 0,
|
|
7481
|
+
version: forkActiveSchema?.version ?? 1,
|
|
7482
|
+
hash: forkActiveSchema?.schema_hash ?? forkSchema
|
|
7483
|
+
}
|
|
7484
|
+
},
|
|
7485
|
+
branches: [...branches].sort((left, right) => compareStrings(left.id, right.id)).map((branch2) => ({
|
|
7486
|
+
branchId: branch2.id,
|
|
7487
|
+
baseVersion: branch2.base
|
|
7488
|
+
}))
|
|
7489
|
+
};
|
|
7490
|
+
return resolveMerge(
|
|
7491
|
+
forkPoint,
|
|
7492
|
+
target,
|
|
7493
|
+
[targetBranch, ...branches],
|
|
7494
|
+
options,
|
|
7495
|
+
true,
|
|
7496
|
+
{
|
|
7497
|
+
targetBranchId: COMMITTED_TARGET_BRANCH,
|
|
7498
|
+
forkPoint: { store: forkPoint, version: forkVersion }
|
|
7499
|
+
},
|
|
7500
|
+
void 0,
|
|
7501
|
+
async (resolved) => {
|
|
7502
|
+
await assertPlanningFenceUnchanged(target, targetFence);
|
|
7503
|
+
await assertForkPointUnchanged({
|
|
7504
|
+
store: forkPoint,
|
|
7505
|
+
version: forkVersion
|
|
7506
|
+
});
|
|
7507
|
+
return resolvedMergeArtifact(
|
|
7508
|
+
resolved,
|
|
7509
|
+
"incremental",
|
|
7510
|
+
targetFence,
|
|
7511
|
+
anchors
|
|
7512
|
+
);
|
|
7513
|
+
}
|
|
7514
|
+
);
|
|
7515
|
+
}
|
|
7516
|
+
function mergePlanValidationError(failure) {
|
|
7517
|
+
switch (failure.kind) {
|
|
7518
|
+
case "unsupported-version": {
|
|
7519
|
+
return new UnsupportedMergePlanVersionError(
|
|
7520
|
+
`Unsupported merge plan format version ${String(failure.received)}.`,
|
|
7521
|
+
{ details: { received: failure.received } }
|
|
7522
|
+
);
|
|
7523
|
+
}
|
|
7524
|
+
case "digest-mismatch": {
|
|
7525
|
+
return new MergePlanDigestMismatchError(
|
|
7526
|
+
"The merge plan digest does not match its canonical content.",
|
|
7527
|
+
{ details: { expected: failure.expected, received: failure.received } }
|
|
7528
|
+
);
|
|
7529
|
+
}
|
|
7530
|
+
case "malformed": {
|
|
7531
|
+
return new InvalidMergePlanError("The merge plan is malformed.", {
|
|
7532
|
+
details: { issues: failure.issues }
|
|
7533
|
+
});
|
|
7534
|
+
}
|
|
7535
|
+
default: {
|
|
7536
|
+
const exhaustive = failure;
|
|
7537
|
+
return exhaustive;
|
|
7538
|
+
}
|
|
7539
|
+
}
|
|
7540
|
+
}
|
|
7541
|
+
function wireWriteProps(setProps, unsetProps) {
|
|
7542
|
+
const props = createDataKeyedBag();
|
|
7543
|
+
for (const [key, value] of Object.entries(setProps)) props[key] = value;
|
|
7544
|
+
for (const key of unsetProps) props[key] = void 0;
|
|
7545
|
+
return props;
|
|
7546
|
+
}
|
|
7547
|
+
function resolvedWireProps(current, setProps, unsetProps) {
|
|
7548
|
+
const props = createDataKeyedBag();
|
|
7549
|
+
const unset = new Set(unsetProps);
|
|
7550
|
+
for (const [key, value] of Object.entries(current)) {
|
|
7551
|
+
if (!unset.has(key)) props[key] = value;
|
|
7552
|
+
}
|
|
7553
|
+
for (const [key, value] of Object.entries(setProps)) props[key] = value;
|
|
7554
|
+
return props;
|
|
7555
|
+
}
|
|
7556
|
+
function invalidPlanForTarget(message, details) {
|
|
7557
|
+
return new InvalidMergePlanError(message, { details });
|
|
7558
|
+
}
|
|
7559
|
+
async function preflightWireMergeWrites(target, nodesApi, edgesApi, artifact) {
|
|
7560
|
+
const graphNodes = target.graph.nodes;
|
|
7561
|
+
const graphEdges = target.graph.edges;
|
|
7562
|
+
const assertNodeKind = (kind) => {
|
|
7563
|
+
if (!hasOwnKey(graphNodes, kind)) {
|
|
7564
|
+
throw invalidPlanForTarget(
|
|
7565
|
+
`The merge plan references unknown node kind "${kind}".`,
|
|
7566
|
+
{ kind, role: "node" }
|
|
7567
|
+
);
|
|
7568
|
+
}
|
|
7569
|
+
};
|
|
7570
|
+
const assertEdgeKind = (kind) => {
|
|
7571
|
+
if (!hasOwnKey(graphEdges, kind) || graphEdges[kind] === void 0) {
|
|
7572
|
+
throw invalidPlanForTarget(
|
|
7573
|
+
`The merge plan references unknown edge kind "${kind}".`,
|
|
7574
|
+
{ kind, role: "edge" }
|
|
7575
|
+
);
|
|
7576
|
+
}
|
|
7577
|
+
};
|
|
7578
|
+
for (const item of [
|
|
7579
|
+
...artifact.writes.nodeDeletes,
|
|
7580
|
+
...artifact.writes.nodeUpserts,
|
|
7581
|
+
...artifact.guards.deletedNodes,
|
|
7582
|
+
...artifact.guards.canonicalMappings.flatMap((mapping) => [
|
|
7583
|
+
mapping.member,
|
|
7584
|
+
mapping.canonical
|
|
7585
|
+
]),
|
|
7586
|
+
...artifact.guards.retypes.map((retype) => retype.entity),
|
|
7587
|
+
...artifact.writes.identityAssertions.flatMap((assertion) => [
|
|
7588
|
+
assertion.a,
|
|
7589
|
+
assertion.b
|
|
7590
|
+
]),
|
|
7591
|
+
...artifact.writes.identityRetractions.flatMap((assertion) => [
|
|
7592
|
+
assertion.a,
|
|
7593
|
+
assertion.b
|
|
7594
|
+
])
|
|
7595
|
+
]) {
|
|
7596
|
+
assertNodeKind(item.kind);
|
|
7597
|
+
}
|
|
7598
|
+
for (const retype of artifact.guards.retypes) assertNodeKind(retype.toKind);
|
|
7599
|
+
for (const item of [
|
|
7600
|
+
...artifact.writes.edgeDeletes,
|
|
7601
|
+
...artifact.writes.edgeUpserts
|
|
7602
|
+
]) {
|
|
7603
|
+
assertEdgeKind(item.kind);
|
|
7604
|
+
}
|
|
7605
|
+
const plannedNodes = new Set(
|
|
7606
|
+
artifact.writes.nodeUpserts.map(
|
|
7607
|
+
(upsert) => mergeKey(upsert.kind, upsert.id)
|
|
7608
|
+
)
|
|
7609
|
+
);
|
|
7610
|
+
const deletedNodes = new Set(
|
|
7611
|
+
artifact.writes.nodeDeletes.map(
|
|
7612
|
+
(deletion) => mergeKey(deletion.kind, deletion.id)
|
|
7613
|
+
)
|
|
7614
|
+
);
|
|
7615
|
+
const endpointRows = /* @__PURE__ */ new Map();
|
|
7616
|
+
const readEndpoint = async (endpoint) => {
|
|
7617
|
+
const key = mergeKey(endpoint.kind, endpoint.id);
|
|
7618
|
+
if (endpointRows.has(key)) return endpointRows.get(key);
|
|
7619
|
+
const row = (await nodeCollection(nodesApi, endpoint.kind).getByIds(
|
|
7620
|
+
[endpoint.id],
|
|
7621
|
+
INCLUDE_TOMBSTONES
|
|
7622
|
+
))[0];
|
|
7623
|
+
endpointRows.set(key, row);
|
|
7624
|
+
return row;
|
|
7625
|
+
};
|
|
7626
|
+
const nodeUpsertsByKind = /* @__PURE__ */ new Map();
|
|
7627
|
+
for (const upsert of artifact.writes.nodeUpserts) {
|
|
7628
|
+
const upserts = nodeUpsertsByKind.get(upsert.kind) ?? [];
|
|
7629
|
+
upserts.push(upsert);
|
|
7630
|
+
nodeUpsertsByKind.set(upsert.kind, upserts);
|
|
7631
|
+
}
|
|
7632
|
+
const currentNodesByIdentity = /* @__PURE__ */ new Map();
|
|
7633
|
+
for (const [kind, upserts] of nodeUpsertsByKind) {
|
|
7634
|
+
const rows = await nodeCollection(nodesApi, kind).getByIds(
|
|
7635
|
+
upserts.map((upsert) => upsert.id),
|
|
7636
|
+
INCLUDE_TOMBSTONES
|
|
7637
|
+
);
|
|
7638
|
+
for (const [index, upsert] of upserts.entries()) {
|
|
7639
|
+
const row = rows[index];
|
|
7640
|
+
const identity = mergeKey(kind, upsert.id);
|
|
7641
|
+
currentNodesByIdentity.set(identity, row);
|
|
7642
|
+
endpointRows.set(identity, row);
|
|
7643
|
+
}
|
|
7644
|
+
}
|
|
7645
|
+
for (const upsert of artifact.writes.nodeUpserts) {
|
|
7646
|
+
const current = currentNodesByIdentity.get(
|
|
7647
|
+
mergeKey(upsert.kind, upsert.id)
|
|
7648
|
+
);
|
|
7649
|
+
if (artifact.guards.incremental !== void 0 && current?.meta.deletedAt !== void 0) {
|
|
7650
|
+
throw invalidPlanForTarget(
|
|
7651
|
+
`The incremental merge plan would resurrect soft-deleted committed node "${upsert.id}".`,
|
|
7652
|
+
{ kind: upsert.kind, id: upsert.id, role: "node" }
|
|
7653
|
+
);
|
|
7654
|
+
}
|
|
7655
|
+
const props = resolvedWireProps(
|
|
7656
|
+
current === void 0 ? {} : nodeProps2(current),
|
|
7657
|
+
upsert.setProps,
|
|
7658
|
+
upsert.unsetProps
|
|
7659
|
+
);
|
|
7660
|
+
const schema = nodeSchemaFor(target, upsert.kind);
|
|
7661
|
+
if (schema !== void 0 && !schema.safeParse(props).success) {
|
|
7662
|
+
throw invalidPlanForTarget(
|
|
7663
|
+
`The merge plan's node write for "${upsert.id}" does not validate against the active schema.`,
|
|
7664
|
+
{ kind: upsert.kind, id: upsert.id, role: "node" }
|
|
7665
|
+
);
|
|
7666
|
+
}
|
|
7667
|
+
if (artifact.guards.incremental !== void 0 && current !== void 0) {
|
|
7668
|
+
const analysis = analyzeRevalidatingWrite(
|
|
7669
|
+
schema,
|
|
7670
|
+
nodeProps2(current),
|
|
7671
|
+
wireWriteProps(upsert.setProps, upsert.unsetProps)
|
|
7672
|
+
);
|
|
7673
|
+
assertValidRevalidatingWrite(
|
|
7674
|
+
analysis,
|
|
7675
|
+
`The incremental merge plan found committed node "${upsert.id}" with props that do not validate against the active schema.`,
|
|
7676
|
+
{ kind: upsert.kind, id: upsert.id, role: "node" }
|
|
7677
|
+
);
|
|
7678
|
+
if (analysis.stripsCurrentProps && analysis.schemaWouldChange) {
|
|
7679
|
+
throw invalidPlanForTarget(
|
|
7680
|
+
`The incremental merge plan would strip existing props while updating committed node "${upsert.id}".`,
|
|
7681
|
+
{ kind: upsert.kind, id: upsert.id, role: "node" }
|
|
7682
|
+
);
|
|
7683
|
+
}
|
|
7684
|
+
}
|
|
7685
|
+
}
|
|
7686
|
+
const currentEdgeIds = /* @__PURE__ */ new Set();
|
|
7687
|
+
const edgeUpsertsByKind = /* @__PURE__ */ new Map();
|
|
7688
|
+
for (const upsert of artifact.writes.edgeUpserts) {
|
|
7689
|
+
const upserts = edgeUpsertsByKind.get(upsert.kind) ?? [];
|
|
7690
|
+
upserts.push(upsert);
|
|
7691
|
+
edgeUpsertsByKind.set(upsert.kind, upserts);
|
|
7692
|
+
}
|
|
7693
|
+
const currentEdgesByIdentity = /* @__PURE__ */ new Map();
|
|
7694
|
+
for (const [kind, upserts] of edgeUpsertsByKind) {
|
|
7695
|
+
const rows = await edgeCollection(edgesApi, kind).getByIds(
|
|
7696
|
+
upserts.map((upsert) => upsert.id),
|
|
7697
|
+
INCLUDE_TOMBSTONES
|
|
7698
|
+
);
|
|
7699
|
+
for (const [index, upsert] of upserts.entries()) {
|
|
7700
|
+
const row = rows[index];
|
|
7701
|
+
currentEdgesByIdentity.set(mergeKey(kind, upsert.id), row);
|
|
7702
|
+
if (row !== void 0) currentEdgeIds.add(upsert.id);
|
|
7703
|
+
}
|
|
7704
|
+
}
|
|
7705
|
+
for (const upsert of artifact.writes.edgeUpserts) {
|
|
7706
|
+
const registration = requireDefined(graphEdges[upsert.kind]);
|
|
7707
|
+
const endpointError = validateEdgeEndpoints(
|
|
7708
|
+
upsert.kind,
|
|
7709
|
+
upsert.from.kind,
|
|
7710
|
+
upsert.to.kind,
|
|
7711
|
+
registration,
|
|
7712
|
+
target.registry
|
|
7713
|
+
);
|
|
7714
|
+
if (endpointError !== void 0) {
|
|
7715
|
+
throw invalidPlanForTarget(endpointError.message, {
|
|
7716
|
+
kind: upsert.kind,
|
|
7717
|
+
id: upsert.id,
|
|
7718
|
+
role: "edge"
|
|
7719
|
+
});
|
|
7720
|
+
}
|
|
7721
|
+
for (const endpoint of [upsert.from, upsert.to]) {
|
|
7722
|
+
assertNodeKind(endpoint.kind);
|
|
7723
|
+
const key = mergeKey(endpoint.kind, endpoint.id);
|
|
7724
|
+
if (deletedNodes.has(key)) {
|
|
7725
|
+
throw invalidPlanForTarget(
|
|
7726
|
+
`The merge plan deletes an endpoint required by edge "${upsert.id}".`,
|
|
7727
|
+
{ edgeKind: upsert.kind, edgeId: upsert.id, endpoint }
|
|
7728
|
+
);
|
|
7729
|
+
}
|
|
7730
|
+
if (plannedNodes.has(key)) continue;
|
|
7731
|
+
const row = await readEndpoint(endpoint);
|
|
7732
|
+
if (row === void 0 || row.meta.deletedAt !== void 0) {
|
|
7733
|
+
throw invalidPlanForTarget(
|
|
7734
|
+
`The merge plan references a missing edge endpoint "${endpoint.id}".`,
|
|
7735
|
+
{ edgeKind: upsert.kind, edgeId: upsert.id, endpoint }
|
|
7736
|
+
);
|
|
7737
|
+
}
|
|
7738
|
+
}
|
|
7739
|
+
const current = currentEdgesByIdentity.get(
|
|
7740
|
+
mergeKey(upsert.kind, upsert.id)
|
|
7741
|
+
);
|
|
7742
|
+
if (artifact.guards.incremental !== void 0 && current?.meta.deletedAt !== void 0) {
|
|
7743
|
+
throw invalidPlanForTarget(
|
|
7744
|
+
`The incremental merge plan would resurrect soft-deleted committed edge "${upsert.id}".`,
|
|
7745
|
+
{ kind: upsert.kind, id: upsert.id, role: "edge" }
|
|
7746
|
+
);
|
|
7747
|
+
}
|
|
7748
|
+
const props = resolvedWireProps(
|
|
7749
|
+
current === void 0 ? {} : edgeProps(current),
|
|
7750
|
+
upsert.setProps,
|
|
7751
|
+
upsert.unsetProps
|
|
7752
|
+
);
|
|
7753
|
+
const schema = edgeSchemaFor(target, upsert.kind);
|
|
7754
|
+
if (schema !== void 0 && !schema.safeParse(props).success) {
|
|
7755
|
+
throw invalidPlanForTarget(
|
|
7756
|
+
`The merge plan's edge write for "${upsert.id}" does not validate against the active schema.`,
|
|
7757
|
+
{ kind: upsert.kind, id: upsert.id, role: "edge" }
|
|
7758
|
+
);
|
|
7759
|
+
}
|
|
7760
|
+
if (artifact.guards.incremental !== void 0 && current !== void 0) {
|
|
7761
|
+
const analysis = analyzeRevalidatingWrite(
|
|
7762
|
+
schema,
|
|
7763
|
+
edgeProps(current),
|
|
7764
|
+
wireWriteProps(upsert.setProps, upsert.unsetProps)
|
|
7765
|
+
);
|
|
7766
|
+
assertValidRevalidatingWrite(
|
|
7767
|
+
analysis,
|
|
7768
|
+
`The incremental merge plan found committed edge "${upsert.id}" with props that do not validate against the active schema.`,
|
|
7769
|
+
{ kind: upsert.kind, id: upsert.id, role: "edge" }
|
|
7770
|
+
);
|
|
7771
|
+
if (analysis.stripsCurrentProps && analysis.schemaWouldChange) {
|
|
7772
|
+
throw invalidPlanForTarget(
|
|
7773
|
+
`The incremental merge plan would strip existing props while updating committed edge "${upsert.id}".`,
|
|
7774
|
+
{ kind: upsert.kind, id: upsert.id, role: "edge" }
|
|
7775
|
+
);
|
|
7776
|
+
}
|
|
7777
|
+
}
|
|
7778
|
+
if (current !== void 0 && (current.fromKind !== upsert.from.kind || current.fromId !== upsert.from.id || current.toKind !== upsert.to.kind || current.toId !== upsert.to.id)) {
|
|
7779
|
+
throw invalidPlanForTarget(
|
|
7780
|
+
`The merge plan would change the immutable endpoints of edge "${upsert.id}".`,
|
|
7781
|
+
{ kind: upsert.kind, id: upsert.id, role: "edge" }
|
|
7782
|
+
);
|
|
7783
|
+
}
|
|
7784
|
+
}
|
|
7785
|
+
const missingEdgeIds = artifact.writes.edgeUpserts.map((upsert) => upsert.id).filter((id) => !currentEdgeIds.has(id));
|
|
7786
|
+
if (missingEdgeIds.length > 0) {
|
|
7787
|
+
const plannedEdgeById = new Map(
|
|
7788
|
+
artifact.writes.edgeUpserts.map((upsert) => [upsert.id, upsert])
|
|
7789
|
+
);
|
|
7790
|
+
for (const kind of Object.keys(target.graph.edges)) {
|
|
7791
|
+
const rows = await edgeCollection(edgesApi, kind).getByIds(
|
|
7792
|
+
missingEdgeIds,
|
|
7793
|
+
INCLUDE_TOMBSTONES
|
|
7794
|
+
);
|
|
7795
|
+
for (const [index, row] of rows.entries()) {
|
|
7796
|
+
if (row === void 0) continue;
|
|
7797
|
+
const id = requireDefined(missingEdgeIds[index]);
|
|
7798
|
+
const planned = plannedEdgeById.get(id);
|
|
7799
|
+
if (planned !== void 0 && planned.kind !== row.kind) {
|
|
7800
|
+
throw invalidPlanForTarget(
|
|
7801
|
+
`The merge plan reuses edge id "${id}" across kinds.`,
|
|
7802
|
+
{ id, committedKind: row.kind, plannedKind: planned.kind }
|
|
7803
|
+
);
|
|
7804
|
+
}
|
|
7805
|
+
}
|
|
7806
|
+
}
|
|
7807
|
+
}
|
|
7808
|
+
}
|
|
7809
|
+
async function assertMergePlanFenceInsideTransaction(target, txBackend, artifact) {
|
|
7810
|
+
await lockRecordedGraphWrite(txBackend, target.graphId);
|
|
7811
|
+
const activeSchema = await txBackend.getActiveSchema(target.graphId);
|
|
7812
|
+
const liveSchema = {
|
|
7813
|
+
managed: activeSchema !== void 0,
|
|
7814
|
+
version: activeSchema?.version ?? 1,
|
|
7815
|
+
hash: activeSchema?.schema_hash ?? await computeSchemaComponent(target)
|
|
7816
|
+
};
|
|
7817
|
+
if (liveSchema.managed !== artifact.target.schema.managed || liveSchema.version !== artifact.target.schema.version || liveSchema.hash !== artifact.target.schema.hash) {
|
|
7818
|
+
throw new MergePlanSchemaMismatchError(
|
|
7819
|
+
"The target's active schema no longer matches the merge plan.",
|
|
7820
|
+
{ details: { expected: artifact.target.schema, live: liveSchema } }
|
|
7821
|
+
);
|
|
7822
|
+
}
|
|
7823
|
+
const liveOrigin = await readRevisionOrigin(
|
|
7824
|
+
txBackend,
|
|
7825
|
+
target.revisionSchema,
|
|
7826
|
+
target.graphId
|
|
7827
|
+
);
|
|
7828
|
+
if (liveOrigin !== artifact.target.revision.origin) {
|
|
7829
|
+
throw new MergePlanOriginMismatchError(
|
|
7830
|
+
"The merge plan belongs to a different target revision origin.",
|
|
7831
|
+
{
|
|
7832
|
+
details: {
|
|
7833
|
+
expectedOrigin: artifact.target.revision.origin,
|
|
7834
|
+
liveOrigin
|
|
7835
|
+
}
|
|
7836
|
+
}
|
|
7837
|
+
);
|
|
7838
|
+
}
|
|
7839
|
+
const liveRevision = await readRecordedClock(
|
|
7840
|
+
txBackend,
|
|
7841
|
+
target.revisionSchema,
|
|
7842
|
+
target.graphId
|
|
7843
|
+
) ?? null;
|
|
7844
|
+
if (liveRevision !== artifact.target.revision.revision) {
|
|
7845
|
+
throw new StaleMergePlanError(
|
|
7846
|
+
"The target revision changed after this merge plan was created; the plan was not applied.",
|
|
7847
|
+
{
|
|
7848
|
+
details: {
|
|
7849
|
+
expectedRevision: artifact.target.revision.revision,
|
|
7850
|
+
liveRevision
|
|
7851
|
+
}
|
|
7852
|
+
}
|
|
7853
|
+
);
|
|
7854
|
+
}
|
|
7855
|
+
}
|
|
7856
|
+
function identityConsistencySeeds(artifact, profile) {
|
|
7857
|
+
const deleted = new Set(
|
|
7858
|
+
artifact.writes.nodeDeletes.map(
|
|
7859
|
+
(entity) => mergeKey(entity.kind, entity.id)
|
|
7860
|
+
)
|
|
7861
|
+
);
|
|
7862
|
+
const byIdentity = /* @__PURE__ */ new Map();
|
|
7863
|
+
const add = (entity) => {
|
|
7864
|
+
const identity = mergeKey(entity.kind, entity.id);
|
|
7865
|
+
if (!deleted.has(identity)) byIdentity.set(identity, entity);
|
|
7866
|
+
};
|
|
7867
|
+
for (const assertion of [
|
|
7868
|
+
...artifact.writes.identityAssertions,
|
|
7869
|
+
...artifact.writes.identityRetractions
|
|
7870
|
+
]) {
|
|
7871
|
+
add(assertion.a);
|
|
7872
|
+
add(assertion.b);
|
|
7873
|
+
}
|
|
7874
|
+
if (profile === "fold") {
|
|
7875
|
+
for (const upsert of artifact.writes.nodeUpserts) add(upsert);
|
|
7876
|
+
}
|
|
7877
|
+
return [...byIdentity].sort(([left], [right]) => compareMergeKeys(left, right)).map(([, entity]) => entity);
|
|
7878
|
+
}
|
|
7879
|
+
async function applyWireMergeWrites(target, nodesApi, edgesApi, txBackend, artifact) {
|
|
7880
|
+
const committedNodes = await applyNodeRows(
|
|
7881
|
+
nodesApi,
|
|
7882
|
+
artifact.writes.nodeDeletes,
|
|
7883
|
+
artifact.writes.nodeUpserts.map((upsert) => ({
|
|
7884
|
+
kind: upsert.kind,
|
|
7885
|
+
id: upsert.id,
|
|
7886
|
+
props: wireWriteProps(upsert.setProps, upsert.unsetProps),
|
|
7887
|
+
...upsert.validFrom === void 0 ? {} : { validFrom: upsert.validFrom },
|
|
7888
|
+
...upsert.validTo === void 0 ? {} : { validTo: upsert.validTo }
|
|
7889
|
+
}))
|
|
7890
|
+
);
|
|
7891
|
+
const committedEdges = await applyEdgeRows(
|
|
7892
|
+
edgesApi,
|
|
7893
|
+
artifact.writes.edgeDeletes,
|
|
7894
|
+
artifact.writes.edgeUpserts.map((upsert) => ({
|
|
7895
|
+
kind: upsert.kind,
|
|
7896
|
+
item: {
|
|
7897
|
+
id: upsert.id,
|
|
7898
|
+
from: upsert.from,
|
|
7899
|
+
to: upsert.to,
|
|
7900
|
+
props: wireWriteProps(upsert.setProps, upsert.unsetProps),
|
|
7901
|
+
...upsert.validFrom === void 0 ? {} : { validFrom: upsert.validFrom },
|
|
7902
|
+
...upsert.validTo === void 0 ? {} : { validTo: upsert.validTo }
|
|
7903
|
+
}
|
|
7904
|
+
}))
|
|
7905
|
+
);
|
|
7906
|
+
const identityAssertions = artifact.writes.identityAssertions;
|
|
7907
|
+
const identityRetractions = artifact.writes.identityRetractions;
|
|
7908
|
+
const appliedIdentity = await applyIdentityRows(
|
|
7909
|
+
target,
|
|
7910
|
+
txBackend,
|
|
7911
|
+
identityAssertions,
|
|
7912
|
+
identityRetractions,
|
|
7913
|
+
() => storeRuntime(target).assertIdentityClassesConsistentAtTarget(
|
|
7914
|
+
txBackend,
|
|
7915
|
+
identityConsistencySeeds(
|
|
7916
|
+
artifact,
|
|
7917
|
+
target.graph.identity?.sameIdAcrossKinds
|
|
7918
|
+
)
|
|
7919
|
+
)
|
|
7920
|
+
);
|
|
7921
|
+
if (!forceRecordedGraphRevision(txBackend, target.graphId) && !forceWriteTransactionRevision(txBackend)) {
|
|
7922
|
+
await advanceRevisionClock(
|
|
7923
|
+
txBackend,
|
|
7924
|
+
target.revisionSchema,
|
|
7925
|
+
target.graphId,
|
|
7926
|
+
true
|
|
7927
|
+
);
|
|
7928
|
+
}
|
|
7929
|
+
return {
|
|
7930
|
+
nodes: committedNodes,
|
|
7931
|
+
edges: committedEdges,
|
|
7932
|
+
identity: {
|
|
7933
|
+
asserted: appliedIdentity.asserted,
|
|
7934
|
+
retracted: appliedIdentity.retracted
|
|
7935
|
+
}
|
|
7936
|
+
};
|
|
7937
|
+
}
|
|
7938
|
+
function reportFromArtifact(artifact, merged, warnings, provenancePersisted) {
|
|
7939
|
+
const provenanceRecords = artifact.review.provenanceRecords;
|
|
7940
|
+
return {
|
|
7941
|
+
merged,
|
|
7942
|
+
resolutions: artifact.review.resolutions,
|
|
7943
|
+
conflicts: artifact.review.conflicts,
|
|
7944
|
+
deleteModifyConflicts: artifact.review.deleteModifyConflicts,
|
|
7945
|
+
typeReconciliations: artifact.review.typeReconciliations,
|
|
7946
|
+
dropped: artifact.review.dropped,
|
|
7947
|
+
validityEnds: artifact.review.validityEnds,
|
|
7948
|
+
baseAmbiguities: artifact.review.baseAmbiguities,
|
|
7949
|
+
provenance: artifact.provenance.includeInReport ? buildProvenanceIndex(provenanceRecords) : { byBranch: () => ({ nodeIds: [], edgeIds: [] }) },
|
|
7950
|
+
warnings,
|
|
7951
|
+
...artifact.review.diagnostics === void 0 ? {} : {
|
|
7952
|
+
candidateDiagnostics: artifact.review.diagnostics
|
|
7953
|
+
},
|
|
7954
|
+
...provenancePersisted === void 0 ? {} : { provenancePersisted }
|
|
7955
|
+
};
|
|
7956
|
+
}
|
|
7957
|
+
async function applyMergePlan(target, input) {
|
|
7958
|
+
let validation;
|
|
7959
|
+
try {
|
|
7960
|
+
validation = await validateMergePlanArtifact(input);
|
|
7961
|
+
} catch (error) {
|
|
7962
|
+
return err(
|
|
7963
|
+
new InvalidMergePlanError(
|
|
7964
|
+
`Merge plan validation failed: ${describeCause(error)}`,
|
|
7965
|
+
{ cause: error }
|
|
7966
|
+
)
|
|
7967
|
+
);
|
|
7968
|
+
}
|
|
7969
|
+
if (!validation.success)
|
|
7970
|
+
return err(mergePlanValidationError(validation.error));
|
|
7971
|
+
const artifact = validation.artifact;
|
|
7972
|
+
if (artifact.target.graphId !== target.graphId) {
|
|
7973
|
+
return err(
|
|
7974
|
+
new MergePlanTargetMismatchError(
|
|
7975
|
+
"The merge plan names a different target graph.",
|
|
7976
|
+
{
|
|
7977
|
+
details: {
|
|
7978
|
+
expectedGraphId: artifact.target.graphId,
|
|
7979
|
+
receivedGraphId: target.graphId
|
|
7980
|
+
}
|
|
7981
|
+
}
|
|
7982
|
+
)
|
|
7983
|
+
);
|
|
7984
|
+
}
|
|
7985
|
+
try {
|
|
7986
|
+
assertPublicPlanCapability(target);
|
|
7987
|
+
const provenanceStore = artifact.provenance.persist ? await tryOpenProvenanceStore(target) : void 0;
|
|
7988
|
+
if (provenanceStore !== void 0 && isErr(provenanceStore)) {
|
|
7989
|
+
return err(provenanceStore.error);
|
|
7990
|
+
}
|
|
7991
|
+
const merged = await withTxConflictRetry(
|
|
7992
|
+
() => target.transaction(async (tx) => {
|
|
7993
|
+
const txBackend = transactionBackend(tx);
|
|
7994
|
+
await assertMergePlanFenceInsideTransaction(
|
|
7995
|
+
target,
|
|
7996
|
+
txBackend,
|
|
7997
|
+
artifact
|
|
7998
|
+
);
|
|
7999
|
+
await preflightWireMergeWrites(
|
|
8000
|
+
target,
|
|
8001
|
+
tx.nodes,
|
|
8002
|
+
tx.edges,
|
|
8003
|
+
artifact
|
|
8004
|
+
);
|
|
8005
|
+
await assertPlannedIdentityIdsFresh(target, txBackend, {
|
|
8006
|
+
identityAssertions: artifact.writes.identityAssertions,
|
|
8007
|
+
identityRetractions: artifact.writes.identityRetractions
|
|
8008
|
+
});
|
|
8009
|
+
return applyWireMergeWrites(
|
|
8010
|
+
target,
|
|
8011
|
+
tx.nodes,
|
|
8012
|
+
tx.edges,
|
|
8013
|
+
txBackend,
|
|
8014
|
+
artifact
|
|
8015
|
+
);
|
|
8016
|
+
}, mergeCommitTransactionOptions(target))
|
|
8017
|
+
);
|
|
8018
|
+
const warnings = [...artifact.review.warnings];
|
|
5453
8019
|
let provenancePersisted;
|
|
5454
8020
|
if (provenanceStore !== void 0 && !isErr(provenanceStore)) {
|
|
5455
8021
|
try {
|
|
8022
|
+
const records = artifact.review.provenanceRecords;
|
|
5456
8023
|
const count = await persistProvenanceRecords(
|
|
5457
8024
|
provenanceStore.data,
|
|
5458
8025
|
target.graphId,
|
|
5459
|
-
|
|
8026
|
+
records
|
|
5460
8027
|
);
|
|
5461
8028
|
provenancePersisted = {
|
|
5462
8029
|
graphId: provenanceGraphId(target.graphId),
|
|
@@ -5464,32 +8031,21 @@ async function resolveMerge(store, target, branches, options, useBaseSources, in
|
|
|
5464
8031
|
};
|
|
5465
8032
|
} catch (error) {
|
|
5466
8033
|
warnings.push(
|
|
5467
|
-
"provenance persistence failed (graph committed; provenance not persisted): " + (error
|
|
8034
|
+
"provenance persistence failed (graph committed; provenance not persisted): " + describeCause(error)
|
|
5468
8035
|
);
|
|
5469
8036
|
}
|
|
5470
8037
|
}
|
|
5471
|
-
|
|
5472
|
-
merged,
|
|
5473
|
-
|
|
5474
|
-
conflicts: plan.propertyConflicts,
|
|
5475
|
-
deleteModifyConflicts: plan.deleteModifyConflicts,
|
|
5476
|
-
typeReconciliations: plan.typeReconciliations,
|
|
5477
|
-
dropped: plan.dropped,
|
|
5478
|
-
validityEnds: plan.validityEnds,
|
|
5479
|
-
baseAmbiguities: plan.baseAmbiguities,
|
|
5480
|
-
provenance,
|
|
5481
|
-
warnings,
|
|
5482
|
-
...provenancePersisted === void 0 ? {} : { provenancePersisted }
|
|
5483
|
-
};
|
|
5484
|
-
return ok(report);
|
|
8038
|
+
return ok(
|
|
8039
|
+
reportFromArtifact(artifact, merged, warnings, provenancePersisted)
|
|
8040
|
+
);
|
|
5485
8041
|
} catch (error) {
|
|
5486
|
-
|
|
5487
|
-
return err(error);
|
|
5488
|
-
}
|
|
8042
|
+
const translated = translateMergeCommitError(error);
|
|
5489
8043
|
return err(
|
|
5490
|
-
new MergeError(
|
|
5491
|
-
`Merge
|
|
5492
|
-
{
|
|
8044
|
+
translated instanceof MergeError ? translated : new MergeError(
|
|
8045
|
+
`Merge plan apply failed: ${describeCause(translated)}`,
|
|
8046
|
+
{
|
|
8047
|
+
cause: translated
|
|
8048
|
+
}
|
|
5493
8049
|
)
|
|
5494
8050
|
);
|
|
5495
8051
|
}
|
|
@@ -5512,7 +8068,8 @@ async function merge(store, branches, optionsInput = {}) {
|
|
|
5512
8068
|
options,
|
|
5513
8069
|
false,
|
|
5514
8070
|
void 0,
|
|
5515
|
-
precondition.data
|
|
8071
|
+
precondition.data,
|
|
8072
|
+
commitResolvedMerge
|
|
5516
8073
|
);
|
|
5517
8074
|
}
|
|
5518
8075
|
async function validateForkPointVersions(forkPoint, branches) {
|
|
@@ -5931,33 +8488,42 @@ async function commitIncrementalPlan(target, plan, guard) {
|
|
|
5931
8488
|
{ details: { capability: "transactions" } }
|
|
5932
8489
|
);
|
|
5933
8490
|
}
|
|
5934
|
-
return
|
|
5935
|
-
() =>
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
|
|
5951
|
-
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
5960
|
-
|
|
8491
|
+
return runMergeCommit(
|
|
8492
|
+
() => withTxConflictRetry(
|
|
8493
|
+
() => target.transaction(async (tx) => {
|
|
8494
|
+
if (target.revisionTrackingEnabled) {
|
|
8495
|
+
await lockRecordedGraphWrite(transactionBackend(tx), target.graphId);
|
|
8496
|
+
}
|
|
8497
|
+
await assertForkPointUnchanged(guard.forkPoint);
|
|
8498
|
+
const nodesApi = tx.nodes;
|
|
8499
|
+
const edgesApi = tx.edges;
|
|
8500
|
+
await assertBaseResolutionStable(
|
|
8501
|
+
tx,
|
|
8502
|
+
guard
|
|
8503
|
+
);
|
|
8504
|
+
await assertPlannedIdentityIdsFresh(
|
|
8505
|
+
target,
|
|
8506
|
+
transactionBackend(tx),
|
|
8507
|
+
plan
|
|
8508
|
+
);
|
|
8509
|
+
await assertIdentityPeersStable(
|
|
8510
|
+
target,
|
|
8511
|
+
transactionBackend(tx),
|
|
8512
|
+
guard.identityPeerProbe,
|
|
8513
|
+
plan
|
|
8514
|
+
);
|
|
8515
|
+
await assertInheritedTargetUnchanged(nodesApi, edgesApi, guard, plan);
|
|
8516
|
+
await validateIncrementalNodeWrites(target, nodesApi, plan);
|
|
8517
|
+
await validateIncrementalEdgeWrites(target, edgesApi, plan);
|
|
8518
|
+
return applyInternalMergePlan(
|
|
8519
|
+
plan,
|
|
8520
|
+
nodesApi,
|
|
8521
|
+
edgesApi,
|
|
8522
|
+
target,
|
|
8523
|
+
transactionBackend(tx)
|
|
8524
|
+
);
|
|
8525
|
+
}, mergeCommitTransactionOptions(target))
|
|
8526
|
+
)
|
|
5961
8527
|
);
|
|
5962
8528
|
}
|
|
5963
8529
|
async function mergeIncremental(args) {
|
|
@@ -5968,18 +8534,7 @@ async function mergeIncremental(args) {
|
|
|
5968
8534
|
}
|
|
5969
8535
|
const options = normalized.data;
|
|
5970
8536
|
if (options.onBasePropertyConflict !== "flag") {
|
|
5971
|
-
return err(
|
|
5972
|
-
new InvalidMergeOptionsError(
|
|
5973
|
-
'mergeIncremental() requires onBasePropertyConflict: "flag" (keep-base); a non-keep-base policy could overwrite a newer committed base value with a stale branch value.',
|
|
5974
|
-
{
|
|
5975
|
-
details: {
|
|
5976
|
-
option: "onBasePropertyConflict",
|
|
5977
|
-
accepted: "flag",
|
|
5978
|
-
received: options.onBasePropertyConflict
|
|
5979
|
-
}
|
|
5980
|
-
}
|
|
5981
|
-
)
|
|
5982
|
-
);
|
|
8537
|
+
return err(incrementalBaseConflictPolicyError(options));
|
|
5983
8538
|
}
|
|
5984
8539
|
const forkPrecondition = await validateForkPointVersions(forkPoint, branches);
|
|
5985
8540
|
if (isErr(forkPrecondition)) {
|
|
@@ -5991,12 +8546,7 @@ async function mergeIncremental(args) {
|
|
|
5991
8546
|
computeSchemaComponent(target)
|
|
5992
8547
|
]);
|
|
5993
8548
|
if (forkSchema !== targetSchema) {
|
|
5994
|
-
return err(
|
|
5995
|
-
new MergeError(
|
|
5996
|
-
"mergeIncremental() requires target and forkPoint to share a schema; schema drift is not supported (the target content may differ \u2014 only the schema must match).",
|
|
5997
|
-
{ details: {} }
|
|
5998
|
-
)
|
|
5999
|
-
);
|
|
8549
|
+
return err(incrementalSchemaError());
|
|
6000
8550
|
}
|
|
6001
8551
|
const targetBranch = {
|
|
6002
8552
|
id: COMMITTED_TARGET_BRANCH,
|
|
@@ -6015,10 +8565,12 @@ async function mergeIncremental(args) {
|
|
|
6015
8565
|
// it is re-established there rather than assumed to have held for the
|
|
6016
8566
|
// whole of planning (see `assertForkPointUnchanged`).
|
|
6017
8567
|
forkPoint: { store: forkPoint, version: forkVersion }
|
|
6018
|
-
}
|
|
8568
|
+
},
|
|
8569
|
+
void 0,
|
|
8570
|
+
commitResolvedMerge
|
|
6019
8571
|
);
|
|
6020
8572
|
}
|
|
6021
8573
|
|
|
6022
|
-
export { BaseVersionMismatchError, BranchError, IdentityMergeConflictError, InvalidMergeOptionsError, MERGE_ERROR_CODES, MERGE_OPTION_DEFAULTS, MergeConflictError, MergeError, SimilarityUnavailableError, VALIDITY_END_TARGET_PRECEDENCE, asBaseVersion, asBranchId, branch, cloneWorkingCopyStrategy, computeBaseVersion, merge, mergeIncremental, normalizeMergeOptions, openProvenanceStore, persistProvenanceRecords, provenanceGraphId, readProvenance };
|
|
8574
|
+
export { BaseVersionMismatchError, BranchError, CandidateSourceError, IdentityMergeConflictError, InvalidMergeOptionsError, InvalidMergePlanError, MERGE_ERROR_CODES, MERGE_OPTION_DEFAULTS, MERGE_PLAN_DIGEST_ALGORITHM, MERGE_PLAN_FORMAT_VERSION, MatchEvidenceError, MergeConflictError, MergeConstraintConflictError, MergeError, MergePlanCapabilityError, MergePlanDigestMismatchError, MergePlanOriginMismatchError, MergePlanSchemaMismatchError, MergePlanTargetMismatchError, MergePlanningStaleError, SimilarityUnavailableError, StaleMergePlanError, UnsupportedMergePlanVersionError, VALIDITY_END_TARGET_PRECEDENCE, applyMergePlan, asBaseVersion, asBranchId, branch, cloneWorkingCopyStrategy, computeBaseVersion, merge, mergeIncremental, normalizeMergeOptions, openProvenanceStore, persistProvenanceRecords, planMerge, planMergeIncremental, provenanceGraphId, readProvenance };
|
|
6023
8575
|
//# sourceMappingURL=index.js.map
|
|
6024
8576
|
//# sourceMappingURL=index.js.map
|